Sibling-project lessons translated to CASMI26
TL;DR. Two prior structure-prediction challenges run on this machine converged on one result: the model's own confidence does not rank its own candidates (within-item Spearman ρ ≈ −0.03, worse than random selection), while agreement across ≥4 genuinely distinct engines does (+0.0380, p = 3.4 × 10⁻⁶) — provided you deduplicate, because 39 % of the "independent" opinions were copies. For CASMI26 the same shape applies: the published metabolite-annotation literature shows top-1 accuracy roughly halving when the candidate pool grows from a biological database to PubChem (CSI:FingerID 68.5 % → 34.4 %; MetFrag fragmentation-only 22 % → 6 %), and a random ranker's MRR@25 falls exactly as H₂₅/n = 3.816/n, so pool size is a first-order multiplier on the score, not a detail. The fusion rule to use is quality-weighted Reciprocal Rank Fusion, not Borda — and Cormack's own data shows CombMNZ is a live competitor, so it must be measured, not assumed.
1. Translation table — every sibling lesson → a CASMI26 rule
Also at research/q7_compute_execution/sibling_project_lessons_consensus_confidence_poolsize.json (20 rows, array-of-objects). Abbreviated here; the JSON carries the full test_here text.
| # | Sibling lesson | Evidence / number | CASMI26 translation | How we'd test it here | Expected MRR@25 effect |
|---|---|---|---|---|---|
| 1 | Model confidence does not rank candidates | Boltz-2 complex_ipde vs LDDT-PLI, within-ligand ρ = −0.033; argmax-confidence 0.5706 vs random 0.5769 (FINDING_001) |
Spectrum-model softmax / fingerprint cosine is a retrieval score, not a correctness probability | Within-pool ρ(score, correct) per molecule_id; MRR@25 of argsort-by-score vs a random permutation of the same pool |
Diagnostic. If ρ≈0 all score comes from pool construction; ceiling is H₂₅/|pool| |
| 2 | Pooled correlation is confounded by item difficulty | Pooled +0.05/+0.08 while within-item −0.033 | Never pool (candidate, score, correct) rows across molecules — easy molecules inflate everything | Stratify every ranker metric by molecule_id; report per-molecule win rate |
Prevents a false +0.02–0.05 that evaporates on the LB |
| 3 | Cross-engine consensus is the strongest ranking signal | ρ = −0.258, correct on 75.9 % of ligands, Wilcoxon p = 3.4e−06, selected 0.6164 vs random 0.5784 (+0.0380); 6× the engine's own confidence (FINDING_011) | Rank candidates by agreement across orthogonal spectrum→structure engines | Build ≥4 orthogonal scorers; fuse; compare to best single engine on a novel-scaffold holdout | Largest single expected gain; plausibly +0.02 to +0.06 (unverified) |
| 4 | Consensus saturates at 4 distinct references | depth 4 → +0.0315, depth 8 → +0.0345, Δ +0.0030 against a 95th-pct noise floor of +0.0138 (FINDING_016) | Build exactly four engines well, not eight shallow ones | Exact enumeration of all C(n,4) engine subsets; plot MRR@25 vs subset size | Budget control: halves engineering/compute at no measurable loss |
| 5 | A separate job is not a separate opinion | 12 raw entries → 7 distinct poses = 39 % duplicates; dedup improved the gain 0.6120→0.6136 (FINDING_011); Protenix pool ~70 % duplicates (FINDING_015) | Dedup candidates on tautomer-canonical InChIKey14 before fusion; dedup engines sharing a backbone/training set | Distinct-InChIKey14 count vs raw rows; pairwise engine-score ρ to detect clones | +0.001–0.002 free, and removes a submission-rejection risk |
| 6 | A weaker engine in the reference set costs signal | esmfold2 at matched depth: +0.0380 → +0.0178; RoseTTAFold-3 added: +0.0352 → +0.0314 (FINDING_011) | Orthogonality alone does not earn a seat. Gate on standalone recall@25 / MRR@25 and weight accordingly | Leave-one-engine-out ablation; admit only if LOO removal costs more than the permutation floor | Protects ~0.02 that naive "more engines" fusion discards |
| 7 | "Independent" engines were not independent | Chai-1 errors vs Boltz-2 ρ +0.45; Protenix +0.474 | Our engines are worse: library search, fingerprint prediction and in-silico fragmentation all consume the same GNPS/MassBank/MoNA spectra and the same structure DBs | Pairwise correlation of per-molecule truth-rank errors; report effective witness count | Reframes expected consensus gain downward; prevents trusting a "4-engine" fusion that is really ~1.8 |
| 8 | The selection gap is the real budget | oracle 0.6975 vs selected 0.5706; gap 0.1269 per ligand (FINDING_001) | Log recall@25 and MRR@25 on every run. BRIEF back-solves recall@25 ≈ 0.55 at LB 0.417 → ~45 % never reach top-25 | Oracle MRR@25 (truth at rank 1 whenever present) alongside achieved | Decides whether the next week is generation or ranking |
| 9 | A union pool adds oracle, not score | union oracle +0.0375, random +0.0090, selected −0.0017 (FINDING_013) | Unioning COCONUT with NPAtlas/LOTUS/PubChem raises recall@25 but can be flat-to-negative on MRR@25 | Incremental union sweep reporting Δrecall@25 and ΔMRR@25 separately | Avoids a plausible −0.01 to −0.05; matches "PubChem 90 M HURTS" |
| 10 | Consensus cannot pick the minority-right answer | "the union's value lies precisely in poses that disagree with the majority engine" (FINDING_013) | ~90 % of hidden molecules have no training spectrum; the truth may be what every library-anchored engine ranks low | Add an absolute scorer (explained MS2 intensity fraction, formula/isotope agreement, adduct consistency); measure on the novel stratum only | The only route to the unreachable ~45 % |
| 11 | Establish a permutation noise floor first | 2,000 random features: mean +0.0001, sd 0.0087, 95th +0.0138, 99th +0.0195; six "promising" features were all best-of-20-random (FINDING_007) | Draw random per-candidate scores on the real pools; publish the MRR@25 null and the number of features tried | Permutation null over the 400-molecule holdout; bar at the 99th pct adjusted for comparisons | Negative-protection: stops us shipping noise |
| 12 | Orthogonality turns two noise signals into one real one | terms +0.0108 and +0.0100 (both inside noise), within-item ρ +0.030, combined +0.0279 fitted / +0.0220 leave-one-cluster-out, p=0.0002 (FINDING_003) | Confirms the public-LB lesson that Morgan r2 + r3 + MACCS beats a better single fingerprint | Measure within-molecule rank correlation before combining; combine only |ρ| ≲ 0.1; validate leave-one-scaffold-cluster-out | Cheapest real gain available |
| 13 | A hard validity gate zeroes a good prediction | POSEBUSTERS_MAX_FAILURES = 0; 1 of 22 checks → LDDT-PLI 0.0; hit 15 % of ligands; best-PB-passing-pose rescue +0.065 |
CASMI26 gates: missing molecule_id/smiles column, any null, duplicated molecule_id, >25 guesses → rejection. Silent per-molecule zeros: unparseable SMILES, tautomer canonicalisation mismatch |
Port submission_audit.py: RDKit 2026.03.3 parse, organiser-exact tautomer canonicalisation, recomputed InChIKey14, ≤25 unique non-null, exact molecule_id set match, no duplicate InChIKey14 within a row |
Protects the whole submission from 0.000 |
| 14 | Read the organisers' scorer; its conventions outrank model quality | Scorer uses Needleman-Wunsch not resnum; resname LIG; flat zip (submission_audit.py) |
Reimplement MRR@25 exactly, including tautomer canonicalisation. A local metric that skips it mis-ranks our experiments | Pin RDKit 2026.03.3 locally and in the notebook; unit-test on keto/enol, amide/imidic-acid, 2-pyridone pairs that must collapse | Removes a systematic CV↔LB bias |
| 15 | Local validation lied | CYP3A4 validation predicted 0.54–0.58, achieved 0.4326; gap recorded as unexplained | Visible test.parquet is a decoy (verbatim enveda-180 train rows). Any CV on it measures library search |
Holdout: timsTOF only, scaffold-disjoint, NP-like, 1–16 spectra/molecule median 3, the 7 test adducts, merged multi-CE | No gain; without it no other number means anything |
| 16 | Borda LOST; z-hybrid WON | PXR z-hybrid 0.4996 → 0.5472; Borda listed among losers | Use a fusion rule that saturates with depth. Borda is linear in rank, so one engine burying a candidate at rank 400/1000 vetoes it — and with 25 output slots deep ranks carry no information | Head-to-head on one holdout: RRF(k=60), quality-weighted RRF, within-molecule z-sum (CombSUM), CombMNZ, Borda | Rule choice moved the sibling +0.048; likely worth more than any single engine's improvement |
| 17 | A targeted tail swap pays where the same method loses globally | 8 least-confident ligands swapped: 0.5472 → 0.5640 (+0.0168), while the same engine as a pool member regressed 0.5551 → 0.5241 | Route only the lowest-margin molecules to an expensive second pipeline (de-novo generation, widened window, different DB) | Rank holdout by fused margin; treat the bottom decile only; pre-register the decile size | Plausibly +0.005 to +0.02 (unverified) |
| 18 | Low confidence is reliably bad even when high confidence is not reliably good | Catastrophe-detector regime: ρ −0.763, 96.7 % correct direction where 19.2 % of poses are catastrophic, vs ρ −0.258 where 0.2 % are (FINDING_012) | Our ranker will be better at naming failures than at ordering successes. Use it for triage | Bin holdout by fused confidence; plot achieved MRR@25 per bin (a confidence–accuracy characteristic curve) | Enables row 17; no direct gain |
| 19 | Runaway compute; the guard must read billed dollars | Modal ~$1.4/GPU-h, limit hit at $37.58; three guards: decorator caps, preflight ledger, watchdog (budget.py) |
Binding constraint here is the Kaggle notebook wall clock (limit unverified). Needs a per-molecule time budget and a degrade path that still emits 25 valid SMILES | Monotonic clock budget per molecule_id; fall back to cheapest engine's top-25; dry-run 400 molecules, measure p99 latency |
Protects against 0.000 from a timeout |
| 20 | Pre-register; count your comparisons | FINDING_041 design committed at 40e0f55 before any job ran; all C(10,d) subsets enumerated to dodge a last-rung maximum-vs-average bias (FINDING_035) |
5 submissions/day × 2 final selections is a multiple-comparison engine | Dated pre-registration per experiment; select finals on holdout MRR@25, LB only as a gross-failure check | Guards against the public→private shakedown |
2. Pool-size sweet spot — what the published numbers actually say
The arithmetic first, because it bounds everything. If the truth sits at a uniformly random rank among n candidates and the ranker has no skill, then for n ≥ 25, E[MRR@25] = H₂₅/n where H₂₅ = 3.815958. Exactly:
| candidates per query n | E[MRR@25], zero-skill ranker | note |
|---|---|---|
| 10 | 0.2929 (= H₁₀/10) | truth always inside the 25 slots |
| 25 | 0.1526 | |
| 100 | 0.0382 | |
| 1,000 | 0.00382 | ≈ the CASMI 2016 mean pool |
| 10,000 | 0.000382 | |
| 100,000 | 0.0000380 |
(Derived here, not cited.) The public-LB rank-1 of ≈ 0.471 is therefore ~123× a zero-skill ranker on a 1,000-candidate pool. Because the relationship is 1/n, halving the pool doubles the no-skill component, and the marginal damage of adding candidates is largest when the pool is already small.
Published empirical anchors. No paper I opened publishes a clean accuracy-vs-candidate-count curve at 10 / 10² / 10⁴. What exists is database-to-database contrasts and one binned analysis:
| Source | Smaller pool | Larger pool | Metric |
|---|---|---|---|
| Dührkop et al. 2015, PNAS (CSI:FingerID), PMC4611636 | biodatabase: 68.5 % top-1, 92.3 % top-5 | PubChem: 34.4 % top-1, 63.5 % top-5 | correct identifications |
| Same | CSI:FingerID fractional rank at 50 % identification 2.23 (CFM-ID 13.5); at 66.7 % 6.38 (CFM-ID 50.0) | — | PubChem search |
| Ruttkies et al. 2016, J Cheminform (MetFrag2.3), PMC4732001 | ChemSpider, fragmentation only: 105 top-1 (22 %) | PubChem, fragmentation only: 30 top-1 (6 %) | 473-spectrum benchmark |
| Same, with retention-time + reference metadata | ChemSpider 420 (89 %) | PubChem 336 (71 %) | same benchmark |
| Hoffmann et al. 2022, Nat Biotechnol (COSMIC), PMC8926923 | biomolecule DB: 57 hits at FDR < 10 % of 123 CASMI 2016 queries | PubChem-trained classifiers "showed a worse performance" with "a substantial drop of correct annotations for small FDR values" | FDR-controlled annotation |
| Schymanski et al. 2017, CASMI 2016 report, PMC5368104 | mean 816 candidates (negative mode) / 1,281 (positive), ~1,114 overall | — | 208 challenges (81 neg, 127 pos) |
Three things follow. First, the halving is real: CSI:FingerID's top-1 goes 68.5 % → 34.4 % and MetFrag's goes 22 % → 6 % when the pool grows. Second, it is not purely a size effect: ChemSpider is only a few-fold smaller than PubChem yet MetFrag's fragmentation-only top-1 is 3.7× higher, so content composition (how chemically plausible the decoys are) matters as much as count — which is exactly the eyewitness "fair fillers" problem in §4. Third, and most important as a gotcha, the one study that binned by actual candidate count found the relationship is NOT monotonic. CASMI 2016 reports verbatim: "Fewer Top 1 entries and higher median/mean ranks were observed for the challenges with moderate candidate numbers (200–1000 candidates); lower median ranks and more Top 1 entries were observed for lower and higher candidate numbers", and twice states "candidate numbers are not the only driver of performance." Per-cluster means were black 611 / green 1,603 / blue 1,019 / red 380 (negative) and magenta 5,297 / cyan 1,029 / red 886 / black 1,534 / blue 978 / green 722 (positive). The plausible mechanism: very large pools arise for large, heteroatom-rich formulas whose MS2 spectra are also more informative, so formula size confounds pool size.
CASMI 2016 category top-1s on ~1,114-candidate pools: Category 2 (in-silico only) CSI:FID 34 % (70/208), CSI:IOKR_A 30 % (62/208), MS-FINDER 22 % (46/208), CFM_orig top-10 59 % (123/208); Category 3 (metadata allowed) MS-FINDER+MD 70 % top-1, CFM_retrain+DB 58 % top-1, CFM top-10 87 %. The metadata jump (34 % → 70 %) is bigger than any model improvement in the table — and MetFrag's 6 % → 71 % with retention time + references says it twice. We have no retention time, but we do have adduct, polarity, collision energy and a declared natural-product provenance; our analogue of "metadata" is any prior that reweights the pool (NP-likeness, biosynthetic plausibility, taxonomic co-occurrence).
Sweet spot. The evidence supports the smallest pool whose recall@25 you can still prove, not a fixed number. The operational rule: sweep the ppm window and the database union, and plot recall@25 against mean pool size; stop at the knee where recall@25 stops rising faster than 1/n is falling.
3. Rank aggregation — which fusion rule for MRR@25
I read the primary paper in full. Cormack, Clarke & Büttcher, Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods, SIGIR '09, pp. 758–759:
RRFscore(d ∈ D) = Σ_{r∈R} 1/(k + r(d)), where k = 60 was fixed during a pilot investigation and not altered during subsequent validation.
Their stated intuition is precisely our problem: "while highly-ranked documents are more important, the importance of lower-ranked documents does not vanish as it would were, say, an exponential function used. The constant k mitigates the impact of high rankings by outlier systems."
Reported numbers:
| Experiment | RRF | Best individual | Condorcet | CombMNZ |
|---|---|---|---|---|
| TREC Robust (MAP) | .3686 | .3586 | .3652 | .3575 |
| TREC 3 | .4350 | .4226 | .4256 | .4381 |
| TREC 5 | .3394 | .3165 | .3213 | .3237 |
| TREC 9 | .2830 | .3519 (manual) / (.2801) | .2750 | .2671 |
| LETOR 3 (583,850 doc-query pairs) | .6051 | ListNet .5846, RankSVM .5737, RankBoost .5622 | .5917 | .6107 |
Sensitivity to k (pilot, TREC topics 351–400): MAP .2072 at k=0, .2134 at k=20, .2145 at k=60, .2147 at k=80, .2098 at k=500 — flat across 10–100, so k is not a tuning knob worth spending on. Significance: RRF beat Condorcet 7/7 (p ≈ 0.008) and CombMNZ 6/7 (p ≈ 0.04); "RRF outperforms Condorcet, CombMNZ and the best system by 4 % to 5 % on average."
Recommendation: quality-weighted RRF as the default, with CombSUM-on-within-molecule-z-scores and CombMNZ as mandatory comparators. The reasoning, in order of weight:
- Scale immunity. Our scorers emit incommensurable quantities — a modified cosine in [0,1], a MetFrag/CFM-ID explained-intensity score, a Tanimoto to a predicted fingerprint, a formula-agreement count. Cormack names exactly this as CombMNZ's weakness: it "multiplies the sum of the uncalibrated scores… It is perhaps not surprising that its results have higher variance… by happenstance, some scores are more amenable than others." The sibling's z-hybrid solved the same problem by standardising within the item before summing — CombSUM on z-scores, the defensible score-level alternative.
- Objective alignment. The competition metric is reciprocal rank. RRF optimises a surrogate of the thing being scored; Borda and CombMNZ do not.
- Why Borda lost in PXR, mechanistically. Borda weights linearly in rank. On a 1,000-candidate pool with 25 output slots, one engine burying the truth at rank 400 subtracts 400 units and vetoes it, while the gap between rank 600 and rank 900 — information we will never use — carries 300 units. RRF's 1/(k+r) collapses everything past rank ~100 to near-zero, the correct shape when only the top 25 score.
- Honest caveat: RRF was not uniformly best in its own paper. CombMNZ beat it on TREC 3 (.4381 vs .4350) and on LETOR 3 (.6107 vs .6051, p ≈ .2). Cormack's claim is "consistently equaled or bettered," not "always beat." Default to weighted RRF, but run the five-way bake-off (table row 16) with a permutation CI before fixing it.
- Weighting. Sibling row 6 (esmfold2 halving the gain) says an unweighted fusion is actively dangerous. Use RRFscore = Σ_e w_e/(k + r_e(d)) with w_e from each engine's standalone holdout MRR@25, admitting an engine only if leave-one-out removal costs more than the permutation floor.
I did not open Fox & Shaw (1994), the CombSUM/CombMNZ primary; the CombMNZ definition and numbers quoted above are from the RRF paper's own formulation, CMNZscore(d) = |{r : r(d) ≤ c}| · Σ_{r: r(d)≤c} s_r(d). Treat the Fox & Shaw attribution as unverified here.
4. Forced analogy: jurisprudence of eyewitness line-ups
The analogy, precisely. A candidate pool for one molecule_id is a police line-up. The true structure is the suspect; every other candidate is a filler. The ranking model is the eyewitness; the MS/MS spectrum is the witness's memory trace of the perpetrator. MRR@25 is "how far down the witness's ordered list the true suspect appears." The sibling finding "confidence ≠ accuracy" is the eyewitness literature's Phase 1 conclusion, and Wixted & Wells (2017, Psychol Sci Public Interest, doi:10.1177/1529100616686966) overturn it: under what they call pristine conditions, confidence is highly diagnostic. Their Box 3 lists five, verbatim:
- Include Only One Suspect per Lineup — 2. The Suspect Should Not "Stand Out" in the Lineup — 3. Caution that the Offender Might Not Be in the Lineup — 4. Use Double-Blind Testing — 5. Collect a Confidence Statement at the Time of the Identification
Their numbers: in fair line-ups "high-confidence accuracy is very high (95 % to 100 % correct)"; a field study of real robbery cases yielded 72 high-confidence suspect IDs and 17 high-confidence filler IDs, implying ≈ 96 % correct, with a signal-detection fit estimating ≈ 97 %. Under unfair line-ups the same high-confidence bin falls to 88 %, 85 %, 80 %/75 %, and in one study ~66 % versus ~86 % in the matched fair condition. The Phase-1 pessimism came from the point-biserial correlation (Sporer et al. 1995 reported .41, which Wixted & Wells argue answers the wrong question — it conditions on accuracy to predict confidence, when the legal system needs the reverse) and from pooling choosers with non-choosers.
Our five pristine conditions, derived.
- One suspect per line-up. Deduplicate the pool on tautomer-canonical InChIKey14. If the truth appears three times as three SMILES and the metric collapses them, our ranker's "votes" are split across identical suspects and the pool statistics are wrong. Sibling row 5 is the same failure in pose space (39 % duplicates).
- The suspect must not stand out. This is the mock-witness test, and it is the single most valuable idea the analogy yields. Wells' test asks whether a non-witness, given only the verbal description, can pick the suspect. Our version: can a model that never sees the MS2 spectrum — using only precursor m/z, formula and adduct — pick the true structure out of the pool above chance? If yes, the pool is biased (e.g. COCONUT's representation skew, or candidate lists where the truth is the only "drug-like" entry among 900 combinatorial decoys), and our measured accuracy is partly line-up bias, not spectral evidence. This is directly testable on the holdout and costs almost nothing. The dud effect follows: Wixted & Wells cite Charman, Wells & Joy (2011) showing that adding obviously-wrong fillers increases confidence in a wrong-but-plausible pick. Padding pools with implausible structures will inflate softmax confidence without improving accuracy.
- Caution that the offender might not be in the line-up. Our pools are all-suspect line-ups with no "not present" option — the model must emit 25 guesses whether or not the truth is in the pool, and with recall@25 ≈ 0.55, ~45 % of line-ups are target-absent. Wixted & Wells' framing is exact: "a lineup that contains only suspects (no fillers) is like a multiple-choice test with no 'wrong' answer." We need an abstention head — a scored estimate of P(truth ∈ pool) — even though the submission format forbids abstaining, because it is what routes a molecule to the expensive tail pipeline (row 17).
- Double-blind administration. The administrator must not know the answer. Our violations are leakage: tuning the ppm window, the pool source or the fusion weights against the public LB; or validating on
test.parquet, whose rows are verbatim train. The visible test set is a non-blind administrator. - Confidence at the initial identification. Wixted & Wells are emphatic that confidence expressed later, after feedback, is worthless. Our analogue: record the ranker's confidence from the first, un-reranked pass. A confidence computed after consensus reranking is contaminated by the other engines' opinions — the statistical equivalent of post-identification feedback — and will look better calibrated than it is.
Is cross-engine consensus = multiple independent witnesses? Partly, and the independence assumption is violated — worse here than in the sibling. Chai-1's errors correlated with Boltz-2's at ρ = +0.45 and Protenix's at +0.474. Our engines share more: library search, fingerprint-prediction retrieval and in-silico fragmentation are all parameterised, directly or indirectly, by the same public GNPS / MassBank / MoNA spectra and by the same structure databases. In eyewitness terms these are co-witnesses who discussed the case before testifying, and the law treats that as contamination for exactly the reason that applies here: four correlated witnesses are not four witnesses. The sibling's measurement that a weaker fourth engine drove the gain from +0.0380 to +0.0178 is the quantitative version of adding a contaminated co-witness.
Where it breaks — three places, honestly.
- The base rate is inverted. Line-ups mostly contain the culprit; our pools fail to contain the truth ~45 % of the time. Wixted & Wells' high-confidence diagnosticity depends on a base rate of guilt; they show high-confidence accuracy "stays high (above 90 %) until the base rate drops below 35 %." Ours may be below that. Their asymmetric result survives though, and it is the one we should bank: "No matter how good or how bad the eyewitness identification procedure is, a low-confidence ID implies that the ID is error prone." That matches sibling row 18 exactly — our ranker will be a good failure-detector and a poor fine-ranker.
- There is no memory decay or post-event contamination. A spectrum is a fixed measurement; a witness's memory degrades and can be overwritten. The whole "collect confidence at the initial ID" machinery maps only metaphorically.
- Fillers are not adversarially chosen in our case, they are whatever the database contains. Line-up fairness is a controlled design variable; pool fairness is an accident of COCONUT's composition. That makes the mock-witness test a diagnostic for us, not a fix: if the pool is unfair we cannot simply re-select fillers without changing recall@25.
Verdict: partially holds. The pristine-conditions framework translates into five concrete, cheap, testable preconditions and one genuinely new experiment (the mock-witness probe). The multiple-witness part of the analogy holds structurally but its independence premise is more violated here than in the sibling project, so the expected consensus gain should be discounted, not extrapolated.
5. Engineering reuse — absolute paths
All under D:\Users\ashenoy00000\.windsurf\OpenADMET-cyp-structure. Read-only; port patterns, do not modify.
| Purpose | Path | What to take |
|---|---|---|
| Submission builder | scripts\submit\build_submission.py |
Deterministic artifact assembly from a scored pool |
| Submission audit (highest value) | scripts\submit\submission_audit.py |
Replicates the organisers' scorer "call for call" to find hard zeros locally; subcommands numbering, pbgate, pbrescue. The CASMI26 port is parse / canonicalise / inchikey14 / ≤25-unique / id-set-match |
| Dashboard generator | scripts\viz\build_dashboard.py, scripts\viz\structures_template.html, scripts\viz\extract_for_viewer.py |
Static HTML from Python → vercel deploy --prod --yes; deploy only on material change (free tier ~100 rolling deploys) |
| Explorer Slurm | scripts\explorer\boltz_depth.py, scripts\explorer\boltz_depth.sbatch, scripts\explorer\verify_inputs.sbatch, docs\RUNBOOK_explorer_boltz.md |
--partition=gyorilab,gpu --gres=gpu:1 --mem=128G --time=07:00:00; GPU nodes need proxy http://10.99.0.130:3128; CRLF breaks sbatch; use scp/tar-over-ssh, not rsync |
| Budget guard | src\cypstruct\budget.py + scripts\ops\watchdog.py |
Three independent guards: decorator-level timeout/retries/max_containers; a plain-JSON ledger with preflight() that refuses before spending; a watchdog that kills apps past their declared budget. CAPS dict and COST_HINTS_JOB / COST_HINTS_SAMPLE split fixed vs per-sample cost |
| Modal job patterns | scripts\cofold\modal_boltz.py, modal_chai.py, modal_af3.py, detached.py |
Detached launch + poll loop with an exit condition |
| Consensus / selection core | src\cypstruct\xengine.py, src\cypstruct\select.py, src\cypstruct\pose.py |
Parameter-free cross-engine agreement, within-item z-scoring, dedup-before-fusion |
| Cross-engine experiment harness | scripts\structure\cross_engine_agreement.py, build_xeng_feature.py, build_reference_set.py, matched_depth_analysis.py, minority_report.py |
Leak-free design: each item scored only against the other engine's outputs |
| Readiness / diagnostics | scripts\ops\readiness.py, scripts\ops\pool_diagnostics.py, scripts\ops\blind_rehearsal.py |
A full dress rehearsal of drop day before drop day |
| Process discipline | docs\PREREG_*.md (12 files), docs\FINDING_*.md (41 files), docs\DROP_DAY_PLAYBOOK.md |
Pre-register before running; one numbered finding per result, including nulls |
| Plumbing | src\cypstruct\paths.py, storage.py, chem.py |
Path discipline and hot/cold storage split |
Gotchas / what could fool us
- The visible test set is a non-blind administrator.
test.parquetrows are verbatim enveda-180 train rows. Any score computed on it is library-search performance and will be 3–5× the hidden score. - Pooled-vs-within confounding. A ranker whose within-molecule ρ is zero can show a strongly positive pooled correlation purely because easy molecules have small pools and high scores. The sibling measured exactly this (+0.05 pooled, −0.033 within).
- Pool size and formula size are confounded. CASMI 2016's non-monotonic binning (worst performance at 200–1,000 candidates, better both above and below) is the smoking gun. Any "smaller pool is better" experiment must control for molecular formula, or it will measure chemistry, not pool size.
- Tautomer canonicalisation is a silent zero. A correct structure written as the wrong tautomer, or canonicalised with a different RDKit build, scores 0. The organisers pin RDKit 2026.03.3; so must we, in both the local harness and the notebook.
- Duplicate InChIKey14 inside one row's 25 wastes slots. Twenty-five SMILES that collapse to eleven distinct InChIKey14s is an eleven-guess submission. Dedup after canonicalisation, not before.
- "Four engines" that are one engine. Fingerprint-prediction models trained on the same GNPS dump, and library search over the same library, are correlated witnesses. Measure the error correlation; report an effective witness count.
- The dud effect. Padding the pool with implausible decoys raises the model's apparent confidence without raising accuracy — documented in the eyewitness literature, and structurally identical to softmax over an easy-negatives-dominated candidate set.
- Best-of-N illusion. The sibling drew ~20 features and six of them cleared +0.010, all noise (null sd 0.0087). With 5 submissions/day for ~90 days, the public LB offers ~450 draws. Two final selections chosen by public LB rank is close to picking the best of 450 random draws.
- A metric reimplementation that is "close enough." The sibling's own scorer paired atoms by
(resnum, atom_name)while the organisers' used sequence alignment; the discrepancy created a bug class that cost whole ligands. Reimplement MRR@25 against the published definition, with unit tests on adversarial tautomers. - Consensus is a catastrophe detector, not a fine-ranker. Its measured value scaled with how bad the pool's worst members were (ρ −0.763 where 19.2 % of poses were catastrophic, ρ −0.258 where 0.2 % were). If our pools are uniformly mediocre, expect the small regime, not the large one.
- Oracle gains that selection cannot collect. Adding a candidate source raised the sibling's oracle +0.0375 and its selected score −0.0017. Always report both.
- Collision-energy merging. Hidden test spectra carry a list of collision energies (merged multi-CE). A holdout built from single-CE train spectra tests a different measurement object.
Implications for the challenge
- Instrument every run with two numbers: recall@25 and MRR@25. Attribute every change to the coverage term or the ranking term. Do this before any modelling work.
- Build the hidden-test-mimicking holdout first (timsTOF only, scaffold-disjoint, NP-like, 1–16 spectra/molecule median 3, the 7 test adducts, merged multi-CE). Nothing else is measurable without it.
- Run the mock-witness probe. Train a model on precursor m/z + formula + adduct only, no MS2, and measure its MRR@25 on our pools. Any score materially above H₂₅/n is pool bias we are accidentally harvesting — and it will not transfer to a differently-built hidden pool.
- Compute a permutation null for MRR@25 on the holdout (≥2,000 draws of random per-candidate scores on the real pools) and refuse to ship any feature below the 99th percentile adjusted for the number of features tried.
- Build exactly four orthogonal engines, gated on standalone recall@25: (i) library/entropy search, (ii) spectrum→fingerprint retrieval, (iii) in-silico fragmentation (MetFrag / CFM-ID) as an absolute scorer, (iv) formula/isotope/adduct agreement. Add de-novo generation as a fifth only for the low-margin tail.
- Fuse with quality-weighted RRF, k = 60, Σ_e w_e/(k + r_e(d)), w_e from standalone holdout MRR@25 — but run the five-way bake-off (RRF / weighted RRF / within-molecule z-sum / CombMNZ / Borda) once, with a permutation CI, before freezing it.
- Deduplicate candidates on tautomer-canonical InChIKey14 before fusion and again before writing the 25.
- Sweep the ppm window and database union against recall@25 vs mean pool size and stop at the knee. The BRIEF's "1 mDa / 10 ppm is critical" and "PubChem HURTS" are both consistent with the 1/n arithmetic and with CSI:FingerID's 68.5 % → 34.4 %.
- Port
submission_audit.pyon day one, not on drop day. Hard gates: column presence, nulls, duplicatemolecule_id, >25 guesses, RDKit-2026.03.3 parse, canonical-form match. - Add a per-molecule wall-clock budget with a degrade path that still emits 25 valid SMILES, and score the degrade path so we know what a timeout costs.
- Route the lowest-margin decile to an expensive second pipeline (tail rescue), pre-registering the decile size.
- Pre-register the two final submissions' selection criterion now, on holdout MRR@25, with the public LB used only as a gross-failure check.
Open questions
- What is the actual Kaggle notebook compute/time limit for this competition? BRIEF marks it unverified; it determines whether a four-engine fusion is feasible at inference.
- Is there any published accuracy-vs-candidate-count curve at fixed molecular formula? Nothing I opened controls for formula, which is the main confounder in the CASMI 2016 binning.
- What exactly does the organisers' tautomer canonicalisation call look like (
TautomerEnumeratordefaults? a specificCanonicalizecall?)? A one-line difference changes our local metric. - Does enveda's PRISM, or any public checkpoint derived from it, count as an orthogonal engine or as another correlated witness trained on the same public half of its 1.2 B spectra?
- Can the mock-witness probe be run against the hidden pool indirectly, e.g. by checking whether public-LB score correlates with pool size across submissions? Probably not within 5 submissions/day without burning the budget.
Sources
Local (read, not modified):
D:\Users\ashenoy00000\.devin\CASMI26_Mass_Spec\research\_meta\sibling_learnings.mdD:\Users\ashenoy00000\.devin\CASMI26_Mass_Spec\BRIEF.mdD:\Users\ashenoy00000\.windsurf\OpenADMET-cyp-structure\docs\FINDING_001_selection_is_the_whole_problem.md…\docs\FINDING_003_first_selector_that_works.md,FINDING_007_noise_floor.md,FINDING_011_cross_engine_agreement.md,FINDING_012_cross_engine_generalises.md,FINDING_013_union_pool_adds_oracle_not_score.md,FINDING_015_protenix_v2_is_deterministic.md,FINDING_016_sampler_sweep_is_a_renewable_reference.md,FINDING_041_reference_depth_value.md…\src\cypstruct\budget.py,…\scripts\submit\submission_audit.py(module docstrings)
Note: the digest records the cross-engine gain as "+0.038, p = 3.7e-6"; the primary document (FINDING_011) states +0.0380 at reference depth 7 with within-ligand ρ = −0.258, 75.9 % correct direction and Wilcoxon p = 3.4e−06. I report the primary.
Web (opened in full or in substantial part):
- Cormack, Clarke & Büttcher (2009), Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods, SIGIR '09: 758–759 — http://cormack.uwaterloo.ca/cormacksigir09-rrf.pdf (full text extracted)
- Dührkop, Shen, Meusel, Rousu & Böcker (2015), Searching molecular structure databases with tandem mass spectra using CSI:FingerID, PNAS, doi:10.1073/pnas.1509788112 — https://pmc.ncbi.nlm.nih.gov/articles/PMC4611636/
- Ruttkies, Schymanski, Wolf, Hollender & Neumann (2016), MetFrag relaunched, J Cheminform 8:3, doi:10.1186/s13321-016-0115-9 — https://pmc.ncbi.nlm.nih.gov/articles/PMC4732001/
- Schymanski et al. (2017), Critical Assessment of Small Molecule Identification 2016: automated methods, J Cheminform, doi:10.1186/s13321-017-0207-1 — https://pmc.ncbi.nlm.nih.gov/articles/PMC5368104/
- Hoffmann et al. (2022), High-confidence structural annotation of metabolites absent from spectral libraries (COSMIC), Nat Biotechnol, doi:10.1038/s41587-021-01045-9 — https://pmc.ncbi.nlm.nih.gov/articles/PMC8926923/
- Ludwig et al. (2018), Bayesian networks for mass spectrometric metabolite identification via molecular fingerprints, Bioinformatics 34:i333, doi:10.1093/bioinformatics/bty245 — https://pmc.ncbi.nlm.nih.gov/articles/PMC6022630/ (top-1 43.62 % CV / 36.61 % CASMI 2016; no candidate-count analysis — a negative result for this brief)
- Wixted & Wells (2017), The Relationship Between Eyewitness Confidence and Identification Accuracy: A New Synthesis, Psychol Sci Public Interest 18(1), doi:10.1177/1529100616686966 — https://wixtedlab.ucsd.edu/publications/wixted2017/Wixted_Wells_2017.pdf (full text extracted; Box 3, pp. 26–27 and the fair/unfair CAC comparisons)
- Metadata confirmation for the above: https://api.semanticscholar.org/graph/v1/paper/DOI:10.1177/1529100616686966 ; https://pmc.ncbi.nlm.nih.gov/tools/idconv/
Not opened, flagged: Fox & Shaw (1994) CombSUM/CombMNZ primary — the CombMNZ formula and results quoted here come from the RRF paper's own formulation. The SIRIUS 4 book-chapter preprint at bio.informatik.uni-jena.de returned HTTP 503. The Kaggle competition page itself was not fetched (JS-rendered; per BRIEF, Kaggle facts here come from the BRIEF's own GitHub-write-up intelligence).