Reciprocal rank fusion

ImplementEasy12 min13 checks

rank-fusionhybrid-retrievalrankingretrieval

Problem

Reciprocal rank fusion (RRF) merges ranked lists from different retrievers into one list. It uses only the rank of each document, not the raw score. A BM25 score and a cosine similarity have different scales, but their ranks are comparable. Hybrid search uses RRF to merge keyword and vector results.

Task

Write two functions.

  1. rrf_scores(rankings, k=60, weights=None) returns the fused score of each document.
  2. fuse(rankings, k=60, weights=None) returns the documents sorted by fused score.

Each ranking is a list of document ids, best first. The first document of a list has rank 1. The fused score of document d is

\mathrm{RRF}(d) = \sum_{i} \frac{w_i}{k + \mathrm{rank}_i(d)}

The sum runs over the lists that contain d. A list that does not contain d adds nothing. When weights is None, every w_i is 1.

If weights is not None and its length is not the number of lists, raise ValueError.

Tie rule: fuse sorts from the highest score to the lowest. When two documents have the same score, the lower doc id comes first.

Input and output

NameTypeShapeMeaning
rankingslist of lists of intL listseach list holds distinct doc ids, best first
kintscalarthe RRF constant, default 60
weightslist of float or NoneL valuesthe weight of each list
  • rrf_scores returns a dict that maps each doc id in any list to its fused score as a float.
  • fuse returns a list of (doc_id, score) tuples, best first. It holds every document of rrf_scores.

Constraints

  • 0 <= L <= 10 and each list has at most 1000 ids. A list can be empty.
  • Doc ids are non negative ints. An id occurs at most once in one list.
  • k >= 1 and each weight is positive.
  • Do not change the input lists.
  • Use Python and numpy only.

Examples

pythonbm25 = [3, 1, 4]
dense = [1, 5, 3]
fuse([bm25, dense])
text[(1, 0.03252247488101534), (3, 0.032266458495966696), (5, 0.016129032258064516), (4, 0.015873015873015872)]

Document 1 has ranks 2 and 1, so its score is 1/62 + 1/61. Document 3 has ranks 1 and 3, so its score is 1/61 + 1/63. Document 1 comes first.

pythonfuse([bm25, dense], weights=[2.0, 1.0])
text[(3, 0.04865990111891751), (1, 0.048651507139079855), (4, 0.031746031746031744), (5, 0.016129032258064516)]

Hints

4 hints

    Solution and notes

    Locked

    Pass every check with Submit to open the reference solution and the notes.

    Run it on your computer

    Clone the repository, install numpy and run the same checks in a terminal:

    python3 scripts/practice/run.py reciprocal-rank-fusion my_answer.py
    python3 scripts/practice/run.py reciprocal-rank-fusion my_answer.py --examples

    Start my_answer.py from starter.py.

    Your code

    Python loads on the first run

    Your code is saved in this browser as you type. On a phone you can read and edit the code, but the checks are easier to use on a desktop.

    Keyboard shortcuts
    Ctrl Enter
    Run the examples
    Ctrl Shift Enter
    Submit
    Tab / Shift Tab
    Indent or outdent the lines
    Esc then Tab
    Move the focus out of the editor
    Alt H
    Show the next hint

    On a Mac, use Cmd in place of Ctrl.

    Select Run to check the examples, or Submit to run every check.