Blocking is a budget problem
Every blocking method spends the same currency: candidate pairs. Given a fixed pair budget B, the only question that matters is which pairs you buy. ERBOT's blocking experiments (E1, E2) test two ways of spending that budget against the standard fixed-k nearest-neighbour baseline — under a protocol designed to keep us honest: budgets aligned to the pair, kill lines registered before the runs, and a permutation test that decides whether an apparent mechanism is real or a mirage.
The protocol (v2)
- Shared pool. All arms draw from one candidate pool: the union of per-record top-200 neighbours by character-3-gram bag-of-words cosine.
- Entity-disjoint split. 70/30 train/test by gold cluster (seed 20261012); all tuning and calibration on train only.
- Exact budget alignment. Treatments are trimmed or padded to the baseline's pair count (E1 on Cora aligned to +0.00%), so a win cannot be a bigger budget in disguise.
- Pre-registered kill lines. dPC ≤ 0 kills the idea on that dataset; a mechanism claim additionally requires permutation p < 0.05.
- Permutation attribution. R = 199 permutations of the allocation profile (E2) or the community labels (E1); p = 0.005 is the floor at this R.
- Metric. PC = pair completeness (share of true duplicate pairs the candidate set contains). Datasets are described on the Datasets page.
The 3 × 2 matrix, closed
Full-protocol v2 runs, 2026-10-20/21. E1 and E2 use different test splits, hence two Cora baselines.
| Dataset | Baseline PC | E1 collective | E2 marginal-allocation | Reading |
|---|---|---|---|---|
| Cora | 0.7050 (E1) · 0.5865 (E2) | dPC +0.2835, perm p = 1.0 attribution rejected | dPC +0.3227 (both variants), perm p = 0.005 established | The effect lives here: the baseline cannot saturate. |
| D10K | 1.0000 | dPC −0.0008 kill line | dPC +0.0000 / −0.0004 kill line | Ceiling: the scope boundary, from above. |
| MusicBrainz-20K | 0.9948 (E1) · 0.9954 (E2) | dPC −0.0025 kill line | dPC −0.0010 / −0.0026 kill line A beats random, p = 0.005 | Near-ceiling: the same boundary. |
Cora · E2 marginal allocation — the effect, decomposed
E2 replaces the uniform per-record budget with a learned allocation: records whose next neighbours are still likely to be true matches receive more pairs; records that have run dry receive fewer. Two calibration variants — A (Platt-calibrated model fitted on labelled train pairs) and B (an unlabeled mixture model of the similarity profile) — land on exactly the same test result.
A gain of dPC +0.3227 on fewer pairs (−1.5%). The permutation test then splits the credit. Shuffling who gets the budget — keeping the multiset of per-record budgets fixed — still buys ≈ +0.170: that half is generic pool mechanics (any reallocation beats the rigid grid). The observed allocation adds a further ≈ +0.15 on top, and that increment is what the test certifies: it beat all 199 permutations (variant A null mean +0.1704, sd 0.0156; variant B +0.1697, sd 0.0154).
dPC axis. Teal band: permutation null, mean ± 2 sd (R = 199). Claim, precisely: who gets the budget matters, beyond the shape of the budget profile.
Cora · E1 collective blocking — a bigger headline, a rejected explanation
E1 allocates the budget by community: Leiden communities on the similarity graph receive budget shares, on the theory that dense regions hide more duplicates. At an exactly aligned budget (24,267 pairs, deviation +0.00%) the headline is even larger than E2's — and the permutation test refuses to certify it.
D10K & MusicBrainz-20K — where the effect cannot live
On the two larger datasets the fixed-k baseline is already at (D10K) or near (MusicBrainz) the ceiling, and every kill line fires as pre-registered. Reallocation can only swap away pairs the baseline already holds. These are not failures of the experiments; they are the measurements that bound the claim.
Footnote kept, claim not made: variant A reaches the same perfect PC with 1.3% fewer pairs — an efficiency whisper, recorded as dead under the pre-registered wording.
One nuance survives the ceiling. MusicBrainz E2-A is dead against fixed-k (−0.0010), yet its permutation null sits at −0.0060 (sd 0.0012): the learned allocation is significantly less bad than random reallocation (p = 0.005). The ordering learned > random holds on both Cora and MusicBrainz — what flips sign across datasets is the headroom, not the quality of the allocation. Variant B shows no such signal (p = 0.93).
MusicBrainz-20K, E2-A. Dead against the baseline, alive against chance: the allocation carries signal even where it cannot win.
Related methods under the same ruler (2026-10-22)
A claim about allocation invites an obvious question: how do the classic methods do under the same protocol — same split, same pool, same budget? We re-ran five of them: global top-B (the whole pool ranked by cosine — the degenerate allocation with no per-record structure), adaptive-k (each record keeps neighbours within a relative threshold of its own best cosine, α tuned on train), meta-blocking (token blocks with block purging; edges weighted by common-block count or Jaccard, plus weight-based neighbourhood pruning), sorted neighborhood (one lexicographic key, sliding window), and canopy (two-threshold greedy clustering). This comparison is descriptive — no permutation tests were run for these methods.
The uncomfortable result first: on Cora, plain global ranking beats our calibrated waterfill by +0.0135, and adaptive-k sits between them. We therefore withdraw the claim that calibrated marginal allocation is the best allocator. What survives is the framework — spending the budget by global score gains +0.32 to +0.34 over the fixed-k grid — plus the scope law below and E2's permutation attribution, which compared waterfill against shuffled budget profiles and is untouched by this run. (Global top-B's +0.34 is a headline number; nobody has decomposed it.)
| Method (PC at budget B) | Cora | D10K | MusicBrainz-20K |
|---|---|---|---|
| Fixed-k baseline | 0.5865 | 0.9996 | 0.9946 |
| Global top-B | 0.9227 | 0.9996 | 0.9924 |
| Adaptive-k (aligned) | 0.9173 | 0.9996 | 0.9889 |
| Marginal allocation A (Platt) | 0.9092 | 0.9996 | 0.9924 |
| Marginal allocation B (mixture) | 0.9092 | 0.9992 | 0.9920 |
| Meta-blocking CBS (aligned) | 0.5135 | 0.9935 | 0.9899 |
| Meta-blocking JS (aligned) | 0.5135 | 0.9906 | 0.9911 |
| Sorted neighborhood (aligned) | 0.5279 | 0.6851 | 0.2075 |
| Canopy (native pairs) | 0.2007 · 4,517 | 1.0000 · 5,668 | 0.9848 · 56,721 |
| Meta-blocking WNP (native pairs) | 0.1790 · 4,373 | 0.9471 · 7,772 | 0.9804 · 97,406 |
- Meta-blocking never wins at equal budget — on any of the three datasets. On Cora its token graph holds only 15,009 edges, fewer than the budget itself: sparse bibliographic tokens are a poor fit for token blocking.
- Sorted neighborhood is uniformly weak, and collapses on MusicBrainz (0.2075): one lexicographic key cannot group music-metadata duplicates.
- Canopy is the efficiency side-finding. At equal budget it changes no verdict, but on near-duplicate data it needs a fraction of the pairs: PC 1.0000 on D10K with 5,668 pairs — 14% of the budget.
- On MusicBrainz the plain fixed-k baseline beats every alternative on the table — the scope law from the other side.
The scope law
Allocation gains appear exactly where the fixed-k baseline cannot saturate — large on Cora (baseline 0.59–0.71), zero at the D10K and MusicBrainz ceilings (0.995–1.000). The deliverable of this experiment series is that scope statement plus one certified effect size (Cora E2, dPC +0.3227, permutation p = 0.005, roughly half of it allocation proper) — not a universal improvement claim. The related-methods run narrows it once more: within allocation rules, the ranking signal matters more than the allocation's shape.
Limitations, stated
- End-to-end clustering on the D10K and MusicBrainz candidate graphs collapses to singletons in both arms (ARI = 0), so end-to-end claims for those datasets are blocked until the clustering layer is addressed. The blocking comparisons are unaffected (identical in both arms).
- E1's native-only PC was not logged in v2, so its Cora gain cannot be split between native community pairs and pool padding retroactively.
- One seed, one split per dataset; permutation R = 199 bounds p at 0.005.
What runs next
- E1 v3 attribution check — log native pairs and native-only PC before alignment; pad only from within-community candidates.
- E3 exclusion blocking — recycle budget from pairs the model is sure are non-matches.
- Scale track — the same protocol on cluster-sampled ladders up to full North Carolina voter data, where saturation is not a given.
- An interactive budget-allocation demo (the Budget Lab) is live in the Studio: same protocol in miniature on the bundled datasets, computed live — a teaching demonstration, distinct from the archived runs reported here.
Reproduce these runs in R
Every number on this page comes from four R scripts run against a source checkout of the ERBOT package. The scripts are the protocol — there is no hidden step. If a re-run disagrees with the archived numbers, the disagreement is in the data files or the package state, and step 5 below is how you catch it.
1 · Environment
R ≥ 4.1 (the archived runs used R 4.3/4.4). Install the package dependencies once:
install.packages(c("pkgload", "data.table", "dplyr", "tibble", "stringr",
"stringi", "tidyr", "purrr", "readr", "readxl",
"text2vec", "Matrix", "irlba", "stringdist", "igraph",
"FNN", "cluster", "gridExtra", "scales", "magrittr",
"jsonlite"))2 · The scripts
Download the four scripts and place them in experiments/ at the package root. Each loads the package from the working tree (pkgload::load_all), so always run Rscript from the package root. The dataset is the first argument (default cora).
- exp-e1-collective-blocking-v2.R — E1 collective blocking + community-label permutation
- exp-e2-waterfill-v2.R — E2 marginal allocation (variants A/B) + allocation-profile permutation
- exp-related-baselines-v1.R — the related-methods comparison above (global top-B, adaptive-k, meta-blocking, sorted neighborhood, canopy)
- build-musicbrainz20k.R — one-time MusicBrainz-20K sample builder
3 · Data placement
inst/extdata/cora.csv inst/extdata/cora_gold.csv # bundled with the working tree data/10Kfull.csv data/10Kduplicates.csv # D10K — source on the Datasets page data/raw/musicbrainz-20000-A01.csv.zip # MusicBrainz — Leipzig download
Build the MusicBrainz-20K sample once (whole-cluster sampling, seed 20261020; writes data/musicbrainz20k.csv and its gold file; check the printed fingerprint — 20,000 records / 10,294 entities / 16,934 matched pairs):
Rscript experiments/build-musicbrainz20k.R id CID 20000
4 · Run
Rscript experiments/exp-e1-collective-blocking-v2.R cora Rscript experiments/exp-e2-waterfill-v2.R cora Rscript experiments/exp-related-baselines-v1.R cora Rscript experiments/exp-e1-collective-blocking-v2.R d10k Rscript experiments/exp-e2-waterfill-v2.R d10k Rscript experiments/exp-related-baselines-v1.R d10k Rscript experiments/exp-e1-collective-blocking-v2.R musicbrainz20k Rscript experiments/exp-e2-waterfill-v2.R musicbrainz20k Rscript experiments/exp-related-baselines-v1.R musicbrainz20k
Cora finishes in minutes. The E1/E2 permutation sections (R = 199) on D10K and MusicBrainz are hour-scale — run them in one sitting, on a machine that will not sleep. The related-baselines script has no permutation section, and it can also run without the package installed: it carries local replicas of the three package functions it needs (standalone mode; data.table, Matrix and tibble suffice).
5 · Check against the archive
The E2 Cora run must open with these lines (the same numbers the related-baselines script prints as its self-check). If your baseline differs, stop — your data or package state differs from the archived runs, and later numbers are not comparable.
baseline (fixed-k): 20,898 pairs · PC 0.5865 waterfill A / B: 20,575 pairs · PC 0.9092 permutation (R=199): null +0.170 ± 0.016 · observed +0.3227 · p = 0.005
State note: the scripts were run on 2026-10-20–22 against the ERBOT working tree; they also live in the public package repository (xinminchu/erbot · experiments/), which was unified on 2026-10-22 to the working-tree state that produced these results. The D10K and MusicBrainz data files are not redistributed here — their sources are on the Datasets page.
Provenance
Harness: ERBOT R package, experiment scripts exp-e1-collective-blocking-v2.R and exp-e2-waterfill-v2.R (v2.2, including the D10K gold shim and the TID-keyed MusicBrainz-20K builder; sample seed 20261020). Runs executed 2026-10-20 and 2026-10-21; every number on this page is taken from the archived run outputs without modification. Dataset identities, gold construction and licences: Datasets.
Related-methods section: script exp-related-baselines-v1.R, run 2026-10-22 on E2's v2 split; its in-run self-check reproduced the archived Cora baseline and waterfill numbers exactly. Two data notes: the D10K runs used a copy rebuilt from JedAI's official serialized distribution and verified against the archived fingerprint (10,000 records / 8,705 pairs / 6,000 entities) — its tuned k* is 20 rather than 50, so D10K budgets differ from the v2 runs while every verdict is unchanged; the MusicBrainz-20K sample was rebuilt with the builder's seed and matches the archived fingerprint exactly (20,000 / 10,294 / 16,934), with the baseline PC differing in the fourth decimal (0.9946 vs 0.9954).