Skip to main content

Language-agnostic hybrid-retrieval score fusion (TM2C2 + RRF) and Unicode query normalization — pure-Python, store-agnostic.

Project description

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rankweave-0.1.0.tar.gz (12.4 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rankweave-0.1.0-py3-none-any.whl (12.5 kB view details)

Uploaded Python 3

File details

Details for the file rankweave-0.1.0.tar.gz.

File metadata

  • Download URL: rankweave-0.1.0.tar.gz
  • Upload date:
  • Size: 12.4 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for rankweave-0.1.0.tar.gz
Algorithm Hash digest
SHA256 841d4cabd4f96bb67575f9500f9af08030efc04c065c0c2de9ac7b45535b20c8
MD5 d05695c2103b19f043bb1bff28977f9e
BLAKE2b-256 aa919710d0f2230278441dc9a9834921494f2afb92e12caa4b6f236fd8b25b6b

See more details on using hashes here.

File details

Details for the file rankweave-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: rankweave-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 12.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for rankweave-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bbc9a1da237bfdfc376150ec55a94628e808499778a03f3beb1ab8c238c0bd75
MD5 da9df2c7e7823933b54f4f2c15c28615
BLAKE2b-256 dc21949661a1ab5e8034b66f40cd72764a0ce9694b2b48b66af02e9db61ea6d8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page