Quanfire Statute Embedding — embed-statute-en (Central Acts / bare statutory text)
⚠️ Measurement correction — 2026-08-21
The headline this card used to carry (+48 % Recall@1) was measured on a contaminated evaluation split. The corrected figure is +28.1 %.
The bug: the training filter dropped a pair only when its positive was in the held-out set, and pairs drawn from a held-out Act were never excluded at all. Adjacent-section and heading↔section pairs from the same Act therefore sat on both sides of the split — the model was partly trained on the documents it was scored on.
The adapter was retrained and rescored on a split that isolates at the source-Act level (591 documents held out, 32,200 pairs dropped from the pool). Every number in Results below is the clean measurement. The gain is smaller and it is still statistically significant (disjoint 95 % CIs).
Two things a reader deserves to know:
- The clean figures are for a retrain (
statute-en-e2c), not for the weight file currently in this repo. The published weights saw the evaluation documents during training, so no honest score for them exists. Publishing the clean adapter as a new revision is pending.- This was the harshest of the three corrections, and the reason is volume. Document isolation cut training from 32,432 pairs to 3,052 — 90.6 % of the pool — because a single Act yields hundreds of same-document pairs. The two runs are not remotely volume-matched, so the drop from +48 % to +28.1 % is not a measure of "what the leak was worth". The honest finding is that this corpus does not contain enough distinct Acts to support both a clean split and a high-volume train, and that a bigger statute corpus — not a better recipe — is what would move this model next.
Fix:
without_held_out()in quanfire-multilingual-embedding (commits66470fe,6fe7e6b,3deaf8d).
A retrieval adapter for English-language Indian central statutory text — the
sections, sub-sections and marginal-note headings of Acts enacted by Parliament. It
is a LoRA adaptation over a frozen
intfloat/multilingual-e5-small
(MIT) base — a ~2.4 MB adapter, 384-dimensional normalized vectors, max_length 256.
This is a sibling to embed-legal-en,
built to cover the register that model measured itself flat on. embed-legal-en is a
judgment specialist; on statutory / bare-act text its transfer was honestly zero.
embed-statute-en is the purpose-built statute retriever for exactly that text — and,
like its sibling, this card shows you where it helps and where it does not, measured.
- Framework & code: github.com/quanfire-ai/quanfire-multilingual-embedding (Apache-2.0)
- PyPI:
pip install quanfire-multilingual-embedding - Weights licence: Apache-2.0 (see Licence & provenance — attribution to the source dataset is required)
- Internal run:
statute-en-e2· base e5-small · rank 32 / alpha 64, LoRA onquery,value· mean pooling · 2 epochs, lr 1e-4 (bf16, CUDA) · adapter-mined hard negatives (4/pair, positive-margin 0.05) · the clean re-measure reported below isstatute-en-e2c: identical configuration, retrained on a document-isolated split
What it is for
Retrieving and ranking passages of English central statutory text — statute search, section-to-section similarity, "find the provision that says X" over an Act corpus. It embeds a query and a passage into the same 384-d space; cosine similarity ranks.
Scope — read this before you use it
| Validated? | |
|---|---|
| English central statutory (bare-Act) retrieval | ✅ Yes — +28.1 % Recall@1 overall, +79 % on the un-gameable low-overlap slice, on a clean document-isolated split; see Results |
| Judgment / case-law text | ❌ No — that is embed-legal-en's register, not this one |
| State legislation, rules, notifications, contracts | ❌ Not measured — bring your own evaluation |
| Non-English statutory text (Hindi, Tamil, …) | ❌ No — the model and its training data are English-only |
The gain this adapter provides is statute-specific. For judgments use the sibling model; for other registers, measure before you trust it.
Results (held-out, scored on CUDA)
In-distribution — 1,978 held-out central-statute pairs (2,000 sampled, 22 duplicate-positive queries dropped). The published base is the only honest baseline; the adapter is scored on the same held-out pairs, which were never seen in training. All figures are single-run, measured on CUDA (never MPS).
| Metric | base e5-small | clean retrain (statute-en-e2c) |
change | withdrawn figure |
|---|---|---|---|---|
| Recall@1 | 0.1815 | 0.2326 | +28.1 % | |
| Recall@5 | 0.3458 | 0.4085 | +18.1 % | |
| Recall@10 | 0.4110 | 0.4767 | +16.0 % | |
| MRR | 0.2623 | 0.3173 | +21.0 % | |
| nDCG@10 | 0.2900 | 0.3476 | +19.9 % |
The base column is unchanged — the contamination only ever inflated the adapter, so the correction is confined to one column.
The Recall@1 gain clears sampling noise with disjoint 95 % confidence intervals: base [0.1651, 0.1991] vs clean retrain [0.2145, 0.2517] — the intervals do not touch.
The un-gameable slice. Statute marginal-note headings often restate the section body, so
a string-matcher can win on high-overlap pairs without learning meaning. The honest readout is
the low-lexical-overlap bucket (<0.3 token overlap — pure semantics), and it is where the
adapter helps most:
| Lexical-overlap slice | base Recall@1 | clean retrain | change | withdrawn figure |
|---|---|---|---|---|
low <0.3 (n = 874, un-gameable) |
0.0767 | 0.1373 | +79 % | |
mid 0.3–0.7 (n = 1,104) |
0.2645 | 0.3080 | +16 % |
On the pairs a string-matcher cannot solve, the adapter still improves Recall@1 by 79 % (and Recall@10 rises 0.245 → 0.334) — the relative gain is largest exactly where surface overlap cannot help, which is the evidence that it learned statutory semantics rather than string matching. It no longer "more than doubles"; that phrasing belonged to the withdrawn figure. By pair kind, the gain holds across both dominant types: adjacent-section 0.185 → 0.242 (n = 1,488) and heading↔section 0.172 → 0.205 (n = 489).
Which weights should I use?
Two revisions are published. They share the recipe and differ in what can be said about them:
| Revision | Trained on | Has a valid score? |
|---|---|---|
main (default) |
32,432 pairs — the full mined pool | ❌ No. It trained on the evaluation documents, so no clean held-out set exists for it within this corpus |
clean-2026-08-21 |
3,052 pairs — document-isolated split | ✅ Yes — every figure in Results above is this adapter |
hf download quanfire-ai/embed-statute-en --revision clean-2026-08-21 --local-dir embed-statute-en-clean
Which one to take. If you need a number you can cite or audit, take
clean-2026-08-21 — it is the one the Results section describes. main saw
10x the training data and may well be the stronger retriever in practice, but "may
well be" is precisely the kind of claim this card no longer makes.
We have not swapped the default, and the reason is worth stating: doing so would trade a plausibly-stronger model for a measurable one with no evidence that the trade is good. Settling it properly needs a head-to-head of the two adapters on a corpus neither of them trained on. That is planned, and until it runs, both revisions stay up and this section stays honest about which is which.
Usage
Pull the adapter and run it through the Quanfire framework, which applies the LoRA over the frozen base and produces normalized embeddings:
pip install 'quanfire-multilingual-embedding[neural]'
hf download quanfire-ai/embed-statute-en --local-dir embed-statute-en
As an HTTP embeddings service (OpenAI-compatible POST /v1/embeddings):
qfme serve --adapter embed-statute-en --port 8000
curl -s localhost:8000/v1/embeddings \
-H 'content-type: application/json' \
-d '{"input": ["What is the punishment for criminal breach of trust by a public servant?"]}'
In-process, as a search pipeline:
from multilingual_embedding.pipelines.search import SemanticSearchPipeline
pipe = SemanticSearchPipeline.from_adapter("embed-statute-en")
pipe.index([
"Whoever, being in any manner entrusted with property, dishonestly misappropriates it, commits criminal breach of trust.",
"Every Act shall come into operation on the day it receives the assent of the President, unless otherwise provided.",
"This Act extends to the whole of India.",
])
for hit in pipe.search("when does a statute take effect", top_k=3):
print(hit.rank, round(hit.score, 3), hit.text)
Vectors are L2-normalized float32 (dimension 384). The model is symmetric (empty
prefixes), so input_type is not required. Exact (brute-force cosine) search is the
intended regime up to ~10⁵–10⁶ vectors; add an ANN index beyond that.
Training corpus
Built from 858 Central Acts enacted by the Indian Parliament, mined into contrastive section-level pairs. Two mining fixes distinguish this corpus from a naive Wikipedia-style pass, and both were proven by an oracle-diff at build time (naive vs tuned mine), not assumed:
- Short-provision recovery. Statute provisions (extent, commencement, short-title) are far terser than encyclopedic prose. The default 100-character positive floor dropped 2,039 of them; lowering it to 40 recovers ~2,015 genuine short provisions (only 24 remain rejected).
- Lexical-leakage removal. Marginal-note headings frequently restate the section body verbatim (overlap ≈ 1.0 — pure string leakage a model can "solve" without learning meaning). Capping pair overlap at 0.5 removes 34,294 such leaky pairs, dropping the heading↔section mean overlap 0.68 → 0.26.
Result: 35,252 clean pairs (adjacent 26,214 + heading↔section 9,026 + title-lead 12), every pair kind under 0.4 mean overlap.
Hard negatives (the e2 step). On top of in-batch negatives, four hard negatives per pair were mined against the first-run (e1) adapter — its own confusions — then filtered at positive-margin 0.05. This filter matters: an unfiltered mine was 78.8 % false negatives, because generic statutory headings ("Short title", "Definitions") legitimately match sections across every Act; the margin drops any candidate scoring within 0.05 of the pair's own positive, keeping only genuinely harder negatives. Training for 2 epochs on this set is what produced the numbers above.
Licence & provenance
Weights: Apache-2.0 — usable and redistributable commercially, with attribution to the source dataset (below). There is no share-alike obligation: the source dataset is CC-BY-4.0 (attribution, not copyleft), and — critically — this is a non-reconstructive model. It emits 384-d vectors; it does not store or reproduce statutory text, so training on bare-Act text is sound and the weights carry no text.
| Source | Role | Licence / status |
|---|---|---|
An annotated dataset of Central Acts enacted by the Indian Parliament (Zenodo 5088102) |
training corpus (section text) | CC-BY-4.0 — attribution required |
intfloat/multilingual-e5-small |
frozen base checkpoint | MIT |
On the bare-Act text itself. Under §52(1)(q)(ii) of the Copyright Act 1957, the text of an Act may be reproduced only together with commentary. That condition governs republishing the text; it does not constrain a non-reconstructive embedder, which learns a vector geometry and cannot emit the source text. Accordingly Quanfire ships the weights, not the corpus — the bare-Act training corpus is not redistributed. Please retain the Zenodo CC-BY-4.0 attribution when you use these weights.
The framework source code is Apache-2.0 (separate from these weights).
Limitations
- A LoRA adapter over a published checkpoint — not an independently pretrained model.
- English only. It does not embed Hindi, Tamil or other Indic statutory text.
- Central Acts only. Trained on central parliamentary Acts; state legislation, rules, regulations, notifications and contracts are out of distribution and unmeasured.
- Statute-specific. For judgment/case-law text use
embed-legal-en; transfer between the two registers is not assumed in either direction. - Not legal advice, and not a substitute for authoritative sources — retrieval surfaces candidate provisions; verify against the official text (India Code / the Gazette).
- Exact cosine search is the intended regime up to ~10⁵–10⁶ vectors.
Citation
Quanfire Statute Embedding — embed-statute-en (internal run statute-en-e2).
Quanfire, 2026. https://github.com/quanfire-ai/quanfire-multilingual-embedding
Training corpus: "An annotated dataset of Central Acts enacted by the Indian
Parliament", Zenodo 5088102 (CC-BY-4.0).
Model tree for quanfire-ai/embed-statute-en
Base model
intfloat/multilingual-e5-small