Skip to main content

rankweave

Language-agnostic hybrid-retrieval score fusion — pure-Python, store-agnostic.

rankweave decides how to combine the scores from a lexical channel (character-trigram / BM25 / learned-sparse) and a semantic channel (dense embeddings) into one ranking. It ships two research-grounded fusion strategies and the query-side Unicode normalization that makes character-level lexical matching language-agnostic. It has no dependencies (stdlib only) and no opinion about your store — bring your own channels; rankweave fuses their scores.

It is extracted, unchanged in behavior, from the Context Search engine of naruon, following the lab's ONE SOURCE MULTI USE convention: standalone product and submodule-importable.

Why

A convex combination of theoretically min-max normalized scores (TM2C2) beats Reciprocal Rank Fusion in- and out-of-domain, is robust for alpha ∈ [0.6, 0.8] with no training data, and — unlike rank fusion — preserves the score distribution (Bruch, Gai & Ingber 2023). RRF remains available for channels that expose only ranks. See docs/research/ for the grounding.

Install

pip install rankweave

Quickstart

from rankweave import FusionSettings, fuse_channel_scores, normalize_search_text

# 1) Normalize the query the same way you normalize indexed documents
#    (NFC compose; do accent-folding + lowercasing on the store side too).
query = normalize_search_text("  Trần Hưng Đạo 회의  ")   # -> "Trần Hưng Đạo 회의"

# 2) Run your own lexical + dense channels, then fuse per candidate.
settings = FusionSettings()                 # TM2C2, semantic weight alpha = 0.7
score = fuse_channel_scores(
    word_similarity_score=0.62,             # lexical channel score in [0, 1]
    cosine_distance=0.30,                   # dense channel distance in [0, 2]
    channel_ranks={"lexical": 1, "dense": 1},
    settings=settings,
)                                            # -> bounded [0, 1] fused score

A channel that did not return a candidate contributes its theoretical minimum (absent evidence is the infimum, not an imputed value). Pass FusionSettings(strategy_name="reciprocal_rank_fusion") to fuse by rank instead — then only channel_ranks matters.

API

Symbol Purpose
FusionSettings Immutable strategy + parameters (strategy_name, semantic_weight_alpha, rank_constant_eta).
fuse_channel_scores(...) Fuse one candidate's channel evidence into a single score under the chosen strategy.
convex_combination_score(...) TM2C2 over already-normalized [0, 1] scores.
reciprocal_rank_fusion_score(ranks, eta=60) RRF over 1-based per-channel ranks.
theoretical_min_max_normalize(score, bounds) Scale a score to [0, 1] using a scoring function's theoretical bounds.
normalize_search_text(text) NFC-compose + whitespace-collapse + length-cap a query.
WORD_SIMILARITY_THEORETICAL_BOUNDS, COSINE_DISTANCE_THEORETICAL_BOUNDS (lower, upper) tuples for the common lexical/dense pairing.

The normalization contract

Character-trigram lexical retrieval is language-agnostic only if query and documents fold identically. normalize_search_text owns the query side (NFC). Do accent-folding + lowercasing on the store side, in one place, and call it from both — e.g. a PostgreSQL IMMUTABLE wrapper lower(unaccent(normalize(text, NFC))) used in a pg_trgm GiST expression index. rankweave stays out of your store so the two sides cannot silently diverge.

Research grounding

  • Bruch, Gai & Ingber (2023). An Analysis of Fusion Functions for Hybrid Retrieval. ACM TOIS 42(1). arXiv:2210.11934. — TM2C2 > RRF; theoretical-normalization stability; the fusion desiderata (monotonicity, homogeneity, boundedness, Lipschitz continuity, sample efficiency) this library's defaults satisfy.
  • Cormack, Clarke & Büttcher (2009). Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods. SIGIR 2009. — RRF definition, η = 60.
  • UAX #15, Unicode Normalization Forms — NFC composition.

PDFs and a citation manifest live in docs/research/.

Development

pip install -e ".[dev]"
python -m pytest -q          # 27 tests, no external services
python -m ruff check .

License

Apache-2.0 — see LICENSE.

Metadata

Release files for rankweave 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for rankweave 0.1.0
File Size Uploaded
rankweave-0.1.0.tar.gz 12.4 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for rankweave 0.1.0
File Interpreter ABI Platform
rankweave-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 12.4 MB

Release files / rankweave-0.1.0.tar.gz

Download URL rankweave-0.1.0.tar.gz
Size 12.4 MB
Tags Source
SHA-256 checksum
How to use checksums
841d4cabd4f96bb67575f9500f9af08030efc04c065c0c2de9ac7b45535b20c8
BLAKE2b-256 checksum
How to use checksums
aa919710d0f2230278441dc9a9834921494f2afb92e12caa4b6f236fd8b25b6b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Release files / rankweave-0.1.0-py3-none-any.whl

Download URL rankweave-0.1.0-py3-none-any.whl
Size 12.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bbc9a1da237bfdfc376150ec55a94628e808499778a03f3beb1ab8c238c0bd75
BLAKE2b-256 checksum
How to use checksums
dc21949661a1ab5e8034b66f40cd72764a0ce9694b2b48b66af02e9db61ea6d8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page