lexora / apps /api /tests /test_retrieve.py
Abdr007's picture
Lexora β€” deployed tree
547ce6e
Raw
History Blame Contribute Delete
2.66 kB
"""Reciprocal Rank Fusion β€” the maths the whole retrieval story rests on."""
from __future__ import annotations
import pytest
from app.rag.retrieve import reciprocal_rank_fusion
class TestReciprocalRankFusion:
def test_single_list_is_monotonically_decreasing(self):
fused = reciprocal_rank_fusion({"dense": ["a", "b", "c"]}, k=60)
assert fused["a"] > fused["b"] > fused["c"]
def test_exact_values_match_the_formula(self):
"""RRF(d) = sum over retrievers of 1 / (k + rank), rank 1-based."""
fused = reciprocal_rank_fusion({"dense": ["a", "b"]}, k=60)
assert fused["a"] == pytest.approx(1 / 61)
assert fused["b"] == pytest.approx(1 / 62)
def test_agreement_beats_a_single_strong_vote(self):
"""A document both retrievers rank first must outrank one retriever's favourite.
This is the property that makes fusion worth doing: it rewards agreement rather
than any single retriever's confidence.
"""
fused = reciprocal_rank_fusion(
{"dense": ["both", "dense_only"], "sparse": ["both", "sparse_only"]}, k=60
)
assert fused["both"] == pytest.approx(2 / 61)
assert fused["both"] > fused["dense_only"]
assert fused["both"] > fused["sparse_only"]
def test_scores_are_never_compared_across_retrievers(self):
"""Only ranks matter β€” a retriever's score scale cannot influence the result."""
a = reciprocal_rank_fusion({"dense": ["x", "y"], "sparse": ["y", "x"]}, k=60)
b = reciprocal_rank_fusion({"sparse": ["y", "x"], "dense": ["x", "y"]}, k=60)
assert a == b
def test_k_damps_the_head_of_each_list(self):
"""Larger k flattens the gap between rank 1 and rank 2."""
small = reciprocal_rank_fusion({"d": ["a", "b"]}, k=1)
large = reciprocal_rank_fusion({"d": ["a", "b"]}, k=1000)
assert (small["a"] - small["b"]) > (large["a"] - large["b"])
def test_empty_input_yields_empty_output(self):
assert reciprocal_rank_fusion({}, k=60) == {}
assert reciprocal_rank_fusion({"dense": []}, k=60) == {}
@pytest.mark.parametrize("k", [0, -1, -60])
def test_rejects_k_below_one(self, k: int):
"""k=-1 divides by zero at rank 1 and small k inverts the ordering."""
with pytest.raises(ValueError, match="RRF k must be"):
reciprocal_rank_fusion({"dense": ["a"]}, k=k)
def test_missing_from_one_list_still_scores(self):
fused = reciprocal_rank_fusion({"dense": ["a"], "sparse": ["b"]}, k=60)
assert fused["a"] == pytest.approx(1 / 61)
assert fused["b"] == pytest.approx(1 / 61)