TL;DR. The Kaggle API confirms this is a notebooks-only ("code") competition with 5 submissions/day; mirrored rules say the re-run notebook gets at most 9 h CPU or GPU, internet off, submission.csv output, external public data and pre-trained weights allowed. RDKit is not in the Kaggle/Colab image (Python 3.13, numpy 2.1.3), so rdkit==2026.03.3 must be vendored as a Kaggle Dataset from its 37.2 MB cp313 manylinux_2_28_x86_64 wheel. TautomerEnumerator.Canonicalize() silently uses whatever partial tautomer set exists when the 1000-transform cap is hit, ties are broken by lexicographic canonical SMILES, and the stereo-aware tie-break fix (#9519) is only in the unreleased 2026.09 line, so 2026.03.3 and 2026.03.6 are the same "procedure" for connectivity; our eight-molecule canary passes on 2026.03.6 and must be re-run on 2026.03.3 inside the notebook.
1. Competition and platform constraints (what is verified, by whom)
Two kinds of facts are mixed here; the table separates competition-specific (from the Kaggle API on this box or from GitHub mirrors of the rules page) from generic-Kaggle (docs mirrored by third parties — the official kaggle.com/docs/* pages render as JS shells and returned only a <title>).
| Fact | Value | Scope | Source / confidence |
|---|---|---|---|
| Notebooks-only (code) competition | is_kernels_submissions_only = True |
competition | Kaggle Python API (KaggleApi.competitions_list(search="casmi"), CLI 2.2.4, run 2026-10-07) — high |
| Daily submission cap | max_daily_submissions = 5 |
competition | same API call — high |
| Final selections | 2 | competition | GitHub write-ups (Isaackjoshua, OfficialBhattacharya docs/COMPETITION.md) — medium (API has no field) |
| Deadlines | merger & new-entrant 2026-12-07 23:59; final 2026-12-14 23:59; enabled 2026-09-14 22:57:37 | competition | API — high |
| Metric label in API | evaluation_metric = "CASMI: Mean Average Precision" (a custom-metric label; all write-ups and the rules say MRR@25) |
competition | API — high that the label exists; the metric itself is MRR@25 |
| Reward / teams | $50,000; 2,672 teams; userHasEntered = True now (Queen's M2 said False at 403 time — rules may have been accepted since; retry the download) |
competition | API/CLI — high |
| Notebook runtime at re-run | "≤9h CPU or GPU, internet disabled, output named submission.csv" | competition | OfficialBhattacharya docs/COMPETITION.md mirror of the Overview page — medium |
| External data / weights | "Freely and publicly available external data and pre-trained models are allowed" | competition | same mirror — medium |
| Another participant's practical budget | "well under the 9h Kaggle limit"; model shipped as private Kaggle dataset isaackjoshua/casmi26-fp-model |
competition | Isaackjoshua README — medium |
| Interactive session limit | 12 h CPU/GPU, 9 h TPU | generic | Ultralytics Kaggle page quoting Kaggle docs; HF knowledge-base mirror dated 2025-11-21 — medium |
| GPUs | P100 16 GB; T4 ×2 (16 GB each); 4 vCPU, 29–32 GB RAM | generic | Ultralytics page; HF mirror — medium. L4 ×4 and TPU v5e: unverified (search budget exhausted before I could confirm) |
| Weekly accelerator quota | 30 h GPU/week; ~20 h TPU/week | generic | Ultralytics; HF mirror — medium |
| Disk | 20 GB /kaggle/working (auto-saved); extra scratch in /kaggle/temp not saved |
generic | HF mirror; Ultralytics — medium |
| CPU-only RAM | 30 GB | generic | HF mirror — medium |
| Max submission.csv size; private-dataset quota (often quoted as 200 GB) | unverified | generic | not fetched |
Practical consequence: train.parquet is 3.03 GB (Queen M1) and loads into a 29–32 GB session fine; the hidden test is ~1,500 spectra, so the 9 h re-run limit is not binding for ranking, only for in-notebook training (do that offline, ship weights as a Dataset).
2. RDKit 2026.03.3 wheel, Kaggle image, offline install
PyPI https://pypi.org/pypi/rdkit/2026.03.3/json: 20 wheels uploaded 2026-06-05, tags cp310–cp314 (no cp39), platforms manylinux_2_28_{x86_64,aarch64}, macosx_11_0_arm64, win_amd64. requires_dist = ["numpy", "Pillow"] — no numpy pin, no requires_python. The wheel's own METADATA (I downloaded it) agrees: Requires-Dist: numpy, Requires-Dist: Pillow; 1,140 files, bundled rdkit.libs/*.so.1.
| Wheel (2026.03.3) | Bytes |
|---|---|
| cp310 manylinux_2_28_x86_64 | 37,258,660 |
| cp311 manylinux_2_28_x86_64 | 37,257,559 |
| cp312 manylinux_2_28_x86_64 | 37,188,323 |
| cp313 manylinux_2_28_x86_64 (the Kaggle target) | 37,187,145 (verified by local pip download) |
| cp314 manylinux_2_28_x86_64 | 37,199,685 |
| cp313 win_amd64 | 24,618,400 |
Latest PyPI release is 2026.03.6 (uploaded 2026-09-03; adds cp315 wheels). Python 3.14 wheels for 2026.03.3 do exist, so the local 3.14 box can be pinned to 2026.03.3 (pip install rdkit==2026.03.3) — nothing forces us to stay on 2026.03.6.
Kaggle image (Kaggle/docker-python Dockerfile.tmpl, fetched 2026-10-07): base us-docker.pkg.dev/colab-images/public/{runtime,cpu-runtime}:release-colab-external-images_20260917-060051_RC00, PACKAGE_PATH=/usr/local/lib/python3.13/dist-packages → Python 3.13, packages installed with uv pip install --system. kaggle_requirements.txt (128 lines) contains no rdkit, numpy, matchms, pyteomics or torch pins (torch/tf/jax are frozen from the Colab base). Colab's published pip-freeze.txt (googlecolab/backend-info, 572 lines): numpy==2.1.3, scipy==1.16.3, pandas==2.2.3, pyarrow==23.0.1, polars==1.35.2, torch 2.11.0 (cp313), rdkit absent; tests/test_rdkit.py in docker-python is 404. Conclusion (medium-high confidence; cannot be 100 % until a notebook is run): RDKit is not pre-installed → vendor it.
numpy ABI: RDKit wheels since 2025.03.1 ("Numpy 2 Support", #7531) are built against numpy 2; Kaggle's 2.1.3 is compatible. Do not pin numpy in the dataset unless you also vendor it; --no-deps avoids pip trying to resolve against the offline index.
matchms (PyPI, fetched): latest 0.33.1 (2026-06-08), pure-python py3-none-any, requires_python <3.15,>=3.10, numpy>=2.0.0, rdkit>=2024.3.5, scipy>=1.15.3,<1.17, pandas>=2.2.3, pickydict>=0.5.0, sparsestack>=0.7.0, deprecated>=1.3.1, lxml>=6.0.2. Kaggle has scipy 1.16.3 (inside the window). pickydict, sparsestack, deprecated are not in the Colab freeze → vendor them too.
3. What Canonicalize() actually does, and what happens at the cap
Source read: Code/GraphMol/MolStandardize/Tautomer.cpp and MolStandardize.h (master, 2026-10-07); Python docs for 2026.03.6.
- Defaults (
CleanupParameters):maxTautomers = 1000,maxTransforms = 1000,tautomerRemoveSp3Stereo = true,tautomerRemoveBondStereo = true,tautomerRemoveIsotopicHs = true,tautomerReassignStereo = true. Confirmed locally on 2026.03.6 (GetMaxTautomers()=1000,GetMaxTransforms()=1000). enumerate()keeps all tautomers in astd::map<std::string, Tautomer>keyed by canonical SMILES (MolToSmiles(*product, true)), so the frontier is visited in lexicographic SMILES order; transforms are applied in the fixed catalogue order;++nTransformshappens once per transform that matches, andif (nTransforms >= d_maxTransforms) { res.d_status = MaxTransformsReached; bailOut = true; }. Products are deduplicated by canonical SMILES.canonicalize()copies the enumerator, setsreassignStereo=false, callsenumerate(), and if the result is empty logs "no tautomers found, returning input molecule"; otherwise it callspickCanonical()on whatever was found, with no check of the status. A truncated enumeration is scored exactly like a complete one, with no warning.- Scoring (Sitzmann et al., JCAMD 24:521, 2010 flavour):
scoreRings+100 per aromatic ring, +150 if all-carbon aromatic;scoreSubstructsper-pattern weights from −4 (aci-nitro) to +25 (benzoquinone);scoreHeteroHs−1 per H on P/S/Se/Te. Tie-break in released 2026.03.x: equal score → smaller canonical SMILES string wins (the[byte 0x5B sorts after letters, which is exactly issue #9518's complaint). Master now has an intermediatenStereotie-break (PR #9519, milestone 2026_09_1, i.e. not in any PyPI release as of today).
Is a cap-hit result deterministic? Within one build: yes — the traversal is a pure function of (canonical SMILES algorithm, transform catalogue, cap), which is why the Queen's 12-randomised-SMILES test gives one InChIKey14 per molecule. Across versions: only if none of those three ingredients changed. Which ones changed:
| Release | Change relevant to the "procedure" | Ref |
|---|---|---|
| 2024.03.1 | tautomer/protomer hash v2 fixes change outputs; MolStandardize Python impls removed | ReleaseNotes.md |
| 2024.09.1 | valence model rewritten; v2 tautomer hash bug fix; Python 3.13 support (#7731) | ReleaseNotes.md |
| 2024.09.5 / .6 | InChI bumped 1.07 → 1.07.2 (#8176, merged 2025-01-17) → 1.07.3 (#8300, merged 2025-02-26) | PR pages |
| 2025.03.1 | numpy 2 support (#7531); MolStandardize Pipeline; "HetAtomTautomerv2 detects tautomerism at carboxylate" | ReleaseNotes.md |
| 2026.03.1 | SMILES double-bond canonicalisation algorithm changed; Kekulize() made order-independent; "a significant number of known SMILES canonicalization problems were resolved"; v2 tautomer hash E/Z preservation (#9128) | ReleaseNotes.md |
| 2026.03.2 (2026-04-30) | #9134 "Speed-up tautomer canonicalization, no API changes" — deliberately a subset of #9046, which was closed because its tree-search state key "was affecting/changing what got selected as canonical" | PR #9134, PR #9046, release page |
| 2026.03.3 (2026-05-29) | #9297 ability to block atoms/bonds from tautomer zones (opt-in); nothing else in MolStandardize | release page |
| 2026.03.4 (2026-07-10) | #9315 fix STEREOANY loss on InChI round-trip | release page |
| 2026.03.5 (2026-08-01) | #9338 Canonicalize() precondition crash with SetRemoveBondStereo(False) on atropisomer bond (not our path: default is True) |
release page |
| 2026.03.6 (2026-08-28) | canonical-ranking performance work ("Reuse Hanoi scratch buffer", "insertion sort for canonical neighbors") — claimed no value change; "Fixes for AI-found issues" ×4 | release page |
| 2026.09.1 (unreleased) | #9519 stereo-aware tie-break (fixes #9518, reported on 2025.09.4 and 2026.03.5); #9584 macrocycle STEREOANY (#9583); new SSSR symmetrisation default | ReleaseNotes.md, issue pages |
Reading: between 2026.03.3 and 2026.03.6 no listed change touches the transform catalogue, the scoring table, or the SMILES canonicalisation semantics; the risk is limited to the unadvertised "AI-found" fixes and ranking-code refactors. Between 2025.09.x and 2026.03.x the canonical SMILES algorithm did change, so an old-RDKit pipeline can explore a different truncated tautomer subset for cap-hitting molecules. Between 2026.03.x and 2026.09.1 the tie-break changes (48 of 34,671 molecules in the reporter's set lost sp3 centres under the old rule; the fix can pick a different tautomer whenever scores tie).
Local cap-sensitivity experiment (2026.03.6, this box): raising maxTransforms from 1000 to 5000/20000 lets quercetin finish (435 tautomers vs 363 truncated), cyanidin (524 vs 326), doxycycline (420 vs 341), tetracycline (168 vs 165), rutin (162 both) — and the InChIKey14 never changed for any of the eight molecules tested (quercetin REFJWTPEDVJJIY, rutin IKGXIBQEEMLURG, tetracycline MUHBBHLZPHKTTR, luteolin-7-O-glucoside PEFNSGRTCBGNAN, cyanidin VEVZSMAEJFVWIL, doxycycline VXVNYCRICCVPTD, emodin RHMXXJGYXNZAPX; luteolin-glucoside and emodin complete under 1000). Note that tetracycline's canonical tautomer is the triketo form (...C1C(=O)C(C(N)=O)C(=O)...), different connectivity from the enol input, yet the key matched — standard InChI's mobile-H layer absorbs the move. So the practical hazard is narrower than "every cap-hit is unstable": it is the subset of molecules where (a) the cap truncates, and (b) the best-scoring found tautomer differs from the global best in a way InChI's mobile-H normalisation does not absorb (C-H tautomers outside InChI's rules, e.g. certain keto-enol or ring-chain cases). Frequency unmeasured; see open questions.
4. Submission mechanics (confirmed from write-ups and the sample file size)
- CSV
molecule_id,smiles;smilesholds up to 25 SMILES joined by;, best first (ibnmarzuk README examplem_0014ef,CC1=CC(=O)C=CC1=O;OC(=O)c1ccccc1O;...).sample_submission.csvis 43,619 bytes for 400 ids (Queen M1). - Rejected if: missing columns, empty file, nulls, duplicated
molecule_id, >25 predictions in a row (COMPETITION.md mirror). Write-ups add defensive handling for molecule_ids absent from the hidden test — fill every id intest.parquetof the re-run environment, not of the visible decoy. - Scoring: both prediction and answer → RDKit tautomer canonicalisation (pinned 2026.03.3) → InChIKey first block (14 chars); MRR@25 = mean over molecules of 1/rank of first match, 0 if absent.
- 5 submissions/day (API), 2 final selections (write-ups). Leaders are using ~2.3/day (Queen M5), so the cap is not binding.
- Notebook must write
/kaggle/working/submission.csvwith internet off (ibnmarzukHOW_TO_SUBMIT.md); it is re-executed by Kaggle against the hidden test.
5. Offline-install recipe and canary (copy-pasteable)
On a machine with internet (any OS — we are downloading Linux wheels, not installing them):
mkdir -p casmi26_wheels && cd casmi26_wheels
PY=3.13; PLAT=manylinux_2_28_x86_64
pip download rdkit==2026.03.3 --no-deps --only-binary=:all: --python-version $PY --platform $PLAT -d .
pip download matchms==0.33.1 pickydict sparsestack deprecated --no-deps --only-binary=:all: --python-version $PY --platform $PLAT -d .
# verify the exact bytes PyPI lists (cp313 manylinux x86_64 = 37,187,145)
ls -l rdkit-2026.3.3-cp313-cp313-manylinux_2_28_x86_64.whl
sha256sum *.whl > SHA256SUMS
kaggle datasets init -p . # edit dataset-metadata.json: id <user>/casmi26-wheels, title, licenses MIT
kaggle datasets create -p . --dir-mode zip # or `kaggle datasets version -p . -m "rdkit 2026.03.3"`
(pip download for the rdkit wheel was executed here on 2026-10-07 and produced exactly 37,187,145 bytes; the matchms line is untested but uses the same mechanism and pure-python wheels.) Attach the dataset to the competition notebook (Add Input → Datasets), then the first notebook cell:
import subprocess, sys, importlib
WHL = "/kaggle/input/casmi26-wheels"
subprocess.check_call([sys.executable, "-m", "pip", "install", "--no-index", "--no-deps",
"--find-links", WHL, "rdkit==2026.03.3", "matchms==0.33.1",
"pickydict", "sparsestack", "deprecated"])
importlib.invalidate_caches()
Second cell — the canary (verified on 2026.03.6 here; expected to pass on 2026.03.3, which is what the assert enforces):
import sys, rdkit
from rdkit import Chem, RDLogger
from rdkit.Chem.MolStandardize import rdMolStandardize as MS
from rdkit.Chem import inchi
RDLogger.DisableLog('rdApp.*')
EXPECT_RDKIT, EXPECT_INCHI = "2026.03.3", "1.07.3"
CANARY = { # name: (SMILES, InChIKey14 after Canonicalize())
"quercetin": ("O=c1c(O)c(-c2ccc(O)c(O)c2)oc2cc(O)cc(O)c12", "REFJWTPEDVJJIY"),
"rutin": ("C[C@H]1O[C@@H](OC[C@H]2O[C@@H](Oc3c(-c4ccc(O)c(O)c4)oc4cc(O)cc(O)c4c3=O)[C@H](O)[C@@H](O)[C@@H]2O)[C@H](O)[C@H](O)[C@H]1O", "IKGXIBQEEMLURG"),
"tetracycline":("CN(C)[C@H]1[C@@H]2C[C@H]3C(=C(O)c4c(O)cccc4[C@@]3(C)O)C(=O)[C@]2(O)C(O)=C(C(N)=O)C1=O", "MUHBBHLZPHKTTR"),
"caffeine": ("Cn1cnc2c1c(=O)n(C)c(=O)n2C", "RYYVLZVUVIJVGH"),
"curcumin": ("COc1cc(/C=C/C(=O)CC(=O)/C=C/c2ccc(O)c(OC)c2)ccc1O", "VFLDPWHFBUODDF"),
"genistein": ("O=c1c(-c2ccc(O)cc2)coc2cc(O)cc(O)c12", "TZBJGXHYKVUXJN"),
"apigenin": ("O=c1cc(-c2ccc(O)cc2)oc2cc(O)cc(O)c12", "KZNIFHPLKGYRTM"),
"kaempferol": ("O=c1c(O)c(-c2ccc(O)cc2)oc2cc(O)cc(O)c12", "IYRMWMYZSQPJKC"),
}
te = MS.TautomerEnumerator()
assert rdkit.__version__ == EXPECT_RDKIT, f"RDKit {rdkit.__version__} is not the court procedure {EXPECT_RDKIT}"
assert inchi.GetInchiVersion() == EXPECT_INCHI, inchi.GetInchiVersion()
assert (te.GetMaxTautomers(), te.GetMaxTransforms()) == (1000, 1000)
bad = []
for name, (smi, exp) in CANARY.items():
got = inchi.MolToInchiKey(te.Canonicalize(Chem.MolFromSmiles(smi)))[:14]
st = te.Enumerate(Chem.MolFromSmiles(smi)).status
print(f"{'OK ' if got == exp else 'BAD'} {name:13s} {got} enum={st}")
if got != exp: bad.append(name)
assert not bad, f"canary mismatch: {bad}"
print("CANARY PASSED", rdkit.__version__, sys.version.split()[0])
Output on this box (2026.03.6, Python 3.14.2, InChI 1.07.3): all eight OK; quercetin/rutin/tetracycline report MaxTransformsReached, the rest Completed; the version assert is what fails here, by design. A copy lives at C:\tb\tmp\...\scratchpad\canary.py (scratch, not in the repo).
Forced analogy: jurisprudence of eyewitness line-ups (administration and chain of custody)
Statement. In a line-up, the identification is only admissible if the administration matched the documented protocol: same filler set, same instructions, same administrator blindness, documented from evidence room to courtroom. Here the "witness" is a predicted SMILES, the "suspect" is the hidden structure, and the "line-up procedure" is TautomerEnumerator().Canonicalize() → InChIKey14 under RDKit 2026.03.3 on Kaggle's Python 3.13 image. Our lab runs 2026.03.6 on Python 3.14 — a different administrator following a procedure that is probably the same but is not documented to be.
Derived protocol (testable).
- Same administrator: pin
rdkit==2026.03.3locally too (cp314 wheel exists); never compute a training/candidate InChIKey14 with any other build. Recordrdkit.__version__,inchi.GetInchiVersion(), Python tag and wheel SHA-256 in every artefact (candidate-pool parquet, model card). - Documented fillers: the eight-molecule canary (plus any molecule whose status is
MaxTransformsReached) is the control line-up; it runs in cell 2 of the submission notebook and in CI locally; a mismatch aborts before any inference (assert, not a warning). - Evidence log: for the candidate pool, store
(smiles_in, canonical_smiles_out, inchikey14, enum_status); theenum_statuscolumn flags the ~cap-hitting fraction where a procedure change could move the key — exactly the molecules a reviewer should re-check on the pinned build. - Testable claim: if the protocol holds, re-keying the full COCONUT-sized pool under 2026.03.3 vs 2026.03.6 yields 0 differing InChIKey14. Any nonzero count is a concrete, countable risk to MRR@25 (each moved key is a guaranteed miss for that molecule).
Where it holds / breaks. Holds for the admissibility logic: a key computed under another version is "evidence obtained by a different procedure" and should be re-derived, not trusted. It partially holds on consequences: in court, a procedural deviation can void the identification entirely; here, standard InChI's mobile-H normalisation makes most deviations harmless (tetracycline's canonical tautomer changed connectivity but kept its key), so the penalty is sparse, not total. It breaks on intent: line-up law worries about suggestive bias toward the suspect; RDKit has no suspect — the risk is pure procedural drift, and there is no "fair" alternative procedure, only the pinned one.
Gotchas / what could fool us
- Kaggle docs are JS shells — every
kaggle.com/docs/*fetch returned only a title. The 9 h figure comes from a participant's mirror of the Overview page; treat it as the limit until the rules are accepted and read first-hand. - The API metric label says "CASMI: Mean Average Precision" while the rules say MRR@25. For a single relevant item per query MAP == MRR, so the custom-metric name is not evidence of a different scorer, but do not quote the API label.
Canonicalize()never tells you the enumeration was truncated. CallEnumerate()separately if you wantstatus; it doubles the cost (quercetin 174 ms + 174 ms).- Lexicographic tie-break depends on the SMILES canonicaliser, which changed in 2026.03.1 and is scheduled to change again (2026.09.1 SSSR symmetrisation default, #9519). Keys derived with 2025.09.x or earlier RDKit (common in pre-computed COCONUT/MoNA dumps) are "another administrator's" keys.
- The train set's
inchikey14column was produced by Enveda's pipeline, version unknown. Do not assume it equalsCanonicalize()→key onnormalized_smilesunder 2026.03.3; measure the mismatch rate once the data is downloadable (Queen's M3 403 may be lifted —userHasEnteredis now True). - The 1000-transform cap is global per molecule, not per branch; polyphenol glycosides and tetracyclines hit it in 170–440 ms. A 400-molecule test ×25 candidates is trivial (~1–4 min), but keying a 400k-molecule pool at ~50 ms median is hours — do it once, offline, under the pinned build, and ship the parquet.
- Stereo is irrelevant to the metric but not to the procedure:
tautomerRemoveSp3Stereo=Truestrips centres in tautomeric zones before scoring, and the SMILES tie-break punishes[C@H]strings; none of this can change a 14-character key except through which tautomer wins. - Vendoring hazards:
pip install --no-indexwithout--no-depswill fail trying to resolvePillowoffline;manylinux_2_28needs glibc ≥ 2.28 (Colab's Ubuntu base satisfies it; exact base OS unverified); if a future Kaggle image jumps to Python 3.14, the cp313 wheel silently becomes uninstallable — vendor both cp313 and cp314. - Decoy visible test: a notebook that reads
test.parquetand hard-codes 400 ids will be rejected at re-run if the hidden parquet has different ids; always iterate the parquet present at run time and fill missing ids with a dummy valid SMILES (C), never nulls. - Issue #8390 (chirality loss, closed as not planned) and similar reports are about stereo, not connectivity — they are noise for InChIKey14 but will appear in any search for "Canonicalize bug".
Implications for the challenge
- Pin
rdkit==2026.03.3everywhere today (local 3.14 wheel exists); re-run the Queen's M7/M8 measurements under it and record any delta. - Build the
casmi26-wheelsKaggle Dataset now (rdkit cp313 + cp314, matchms 0.33.1 + pickydict/sparsestack/deprecated; ~75 MB) and make the canary cell the first thing every notebook runs. - Re-key every candidate pool (COCONUT, enveda-180 structures, train
normalized_smiles) under the pinned build; storeenum_status; report the count of keys that differ from the shippedinchikey14column — that count is a direct ceiling on MRR@25 for library hits. - Submission writer: enforce the rejection rules locally (columns, no nulls, unique ids, ≤25, every id in the run-time
test.parquet), dedupe candidates by pinned-build InChIKey14 before truncating to 25 (duplicates waste rank slots). - Do all training off-Kaggle (Explorer/Modal); the notebook only loads weights from a Dataset and ranks ~1,500 spectra — minutes, not hours.
- Treat the 9 h and GPU-type facts as provisional until a team member accepts the rules and reads the Overview; the API already proves notebooks-only and 5/day.
Open questions
- What fraction of COCONUT-like structures hit
MaxTransformsReached, and for how many does the key move between 2025.09.x, 2026.03.3, 2026.03.6 and master (#9519)? Needs a one-off run on the pinned builds. - Does Enveda's scorer apply any cleanup before
Canonicalize()(largest fragment, uncharger,MolFromSmilesfailure → counted wrong?) — unverified; only the rules page (403 to us) can say. - Exact GPU menu on Kaggle today (L4 ×4? TPU v5e-8?) — unverified.
- Does Kaggle's GPU image, as opposed to Colab's CPU freeze, include RDKit? Verify with
import rdkitin a throwaway notebook before relying on the vendor path (the vendor path is harmless either way).
Sources
- Kaggle Python API on this box (CLI 2.2.4),
KaggleApi.competitions_list(search="casmi"), 2026-10-07:is_kernels_submissions_only,max_daily_submissions, deadlines, metric label. - https://pypi.org/pypi/rdkit/2026.03.3/json and https://pypi.org/pypi/rdkit/2026.03.6/json; local
pip download rdkit==2026.03.3 --python-version 3.13 --platform manylinux_2_28_x86_64(37,187,145 bytes). - https://pypi.org/pypi/matchms/json
- https://raw.githubusercontent.com/Kaggle/docker-python/main/Dockerfile.tmpl ; https://raw.githubusercontent.com/Kaggle/docker-python/main/kaggle_requirements.txt ; https://raw.githubusercontent.com/Kaggle/docker-python/main/tests/test_rdkit.py (404)
- https://raw.githubusercontent.com/googlecolab/backend-info/main/pip-freeze.txt
- https://raw.githubusercontent.com/rdkit/rdkit/master/ReleaseNotes.md ; https://raw.githubusercontent.com/rdkit/rdkit/master/Code/GraphMol/MolStandardize/Tautomer.cpp ; https://raw.githubusercontent.com/rdkit/rdkit/master/Code/GraphMol/MolStandardize/MolStandardize.h
- https://www.rdkit.org/docs/source/rdkit.Chem.MolStandardize.rdMolStandardize.html (2026.03.6 docs)
- RDKit release pages: https://github.com/rdkit/rdkit/releases/tag/Release_2026_03_2 , _03_3 , _03_4 , _03_5 , _03_6
- RDKit PRs/issues: https://github.com/rdkit/rdkit/pull/9134 , /pull/9046 , /pull/8300 , /pull/8176 , /issues/9518 , /issues/9583 , /issues/8390
- Competition mirrors (Kaggle pages are JS shells): https://raw.githubusercontent.com/OfficialBhattacharya/kaggle_CASMI_2026/main/docs/COMPETITION.md ; https://raw.githubusercontent.com/Isaackjoshua/casmi-2026/main/README.md ; https://raw.githubusercontent.com/ibnmarzuk/casmi26-molecule-id/main/README.md and repo tree https://github.com/ibnmarzuk/casmi26-molecule-id
- Generic Kaggle specs: https://docs.ultralytics.com/integrations/kaggle/ ; https://huggingface.co/datasets/John6666/knowledge_base_md_for_rag_1/raw/main/kaggle_20251121.md ; https://raw.githubusercontent.com/andandandand/practical-computer-vision/main/docs/kaggle-gpu-tpu-guide.md
- Attempted, returned only a title: https://www.kaggle.com/docs/competitions , https://www.kaggle.com/docs/notebooks
- Local experiments (RDKit 2026.03.6, Python 3.14.2, Windows, 2026-10-07): canary run; cap-sensitivity run (maxTransforms 1000/5000/20000 on 8 molecules).