Skip to content

Latest commit

Β 

History

44 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

ANRF AISEHack 2.0 β€” Polymer Property Prediction

Team Cross Linkers Β· πŸ₯‰ 3rd out of 145 teams

Physics-constrained multi-task ensembles for predicting seven polymer properties from PSMILES

Placement Public LB Private LB OOF Transfer gap Python License


Predict seven physical properties of a polymer β€” thermal, electronic, optical and dielectric β€” from its repeat unit written as a PSMILES string. Scored on the unweighted mean RΒ² across all seven, which means a property with 221 training labels counts exactly as much as one with 4,143.

Our final system is a five-model ensemble combined by a per-property cross-fitted Ridge and then corrected by an explicit, affine-fitted physics stage. The central modelling idea is treating the dielectric constant not as a fitted correlation but as an exact decomposition, Ξ΅ = nΒ² + Ξ΅_ionic, licensed by how the reference DFPT calculations are defined.

πŸ“– Read the full journey β€” how we got here, what broke, and the two discoveries that decided it.


Results

Private leaderboard 0.917 β†’ πŸ₯‰ 3rd out of 145 teams
Public leaderboard 0.924
Mean out-of-fold RΒ² 0.9234
CV β†’ LB transfer gap +0.0006
Training cost 380 fitted models Β· 3 h 01 m on two Tesla T4
Inference seconds, for all 4,940 test rows

Per-target out-of-fold RΒ², final run

target LightGBM XGBoost CatBoost Multi-task NN Periodic GNN Ridge stack + physics
tg 0.9176 0.9166 0.9092 0.9025 0.9162 0.9274 0.9274
egc 0.9185 0.9197 0.9157 0.8849 0.8999 0.9307 0.9307
egb 0.9310 0.9337 0.9270 0.9324 0.9387 0.9551 0.9551
eps 0.8247 0.8266 0.8369 0.8049 0.8200 0.8514 0.8758
nc 0.8766 0.8775 0.8798 0.8942 0.9055 0.9164 0.9331
ei 0.8367 0.8250 0.8353 0.8532 0.8777 0.8725 0.8978
eea 0.8758 0.8775 0.8922 0.8940 0.9264 0.9367 0.9438
MEAN 0.8830 0.8824 0.8852 0.8809 0.8978 0.9129 0.9234
  • The stack beats its best member by +0.0151 β€” that is what decorrelation buys.
  • Physics adds a further +0.0105, concentrated exactly where the relations reach: ei +0.0250, eps +0.0244, nc +0.0168, eea +0.0070.

Leaderboard progression

# Milestone Public LB
1 Inherited baseline (fingerprints β†’ trees) 0.883 β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
2 Leak fixes + honest cross-fitting 0.898 β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
3 Periodic GNN + Ξ΅ = nΒ² + Ξ΅_ionic 0.916 β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
4 Round-2 archive integrated 0.919 β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
5 Final submission (ppp-v3-g.ipynb) 0.924 β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ

For scale: at our mid-competition leaderboard snapshot, first place on the public board stood at 0.917 β€” we finished the finale above it, at 0.924.

Why the validation number held

That +0.0006 gap is the number we are proudest of, and it is not luck. We started Round 3 with an inherited pipeline whose OOF read 0.903 and whose leaderboard read 0.883 β€” a βˆ’0.020 lie. Closing it took a deliberate leak hunt (four separate leaks, all of one shape) and the removal of early stopping everywhere: a model that picks its iteration count by watching the rows it is then scored on reports a number it cannot reproduce out of sample.


The hackathon

ANRF AISEHack 2.0 β€” a national AI-for-Science hackathon hosted on Kaggle, with the Grand Finale at IIT Madras. Three elimination rounds, each a private community competition scored on mean RΒ² across polymer properties.

flowchart LR
    R1["<b>ROUND 1</b><br/>2 properties<br/>145 teams<br/><br/><b>30th</b>"]
    R2["<b>ROUND 2</b><br/>7 properties<br/>76 teams<br/><br/><b>6th</b>"]
    R3["<b>ROUND 3 Β· FINALE</b><br/>7 properties<br/>+ 3 judged themes<br/>10 finalists<br/><br/><b>3rd of 145</b> πŸ₯‰"]

    R1 -->|qualified| R2 -->|qualified| R3

    style R1 fill:#f5f5f5,stroke:#999,color:#000
    style R2 fill:#e0f0ef,stroke:#00807F,color:#000
    style R3 fill:#fdf0d9,stroke:#B8790B,stroke-width:3px,color:#000
Loading
Round Task Field Our result
Round 1 Predict two polymer properties from SMILES 145 teams 30th
Round 2 Expanded to all seven properties 76 teams 6th
Round 3 β€” Finale Seven properties + robustness, explainability and polymer invariance 10 finalists 3rd πŸ₯‰

145 teams entered the hackathon; 10 survived to the finale, where we placed 3rd.

Round 3 deliberately moved the goal past raw accuracy. From the competition overview: "the challenge moves beyond raw accuracy to robustness and trust β€” two qualities that separate a research prototype from a model that materials scientists can actually rely on." It also added a 5.97 M-molecule auxiliary SMILES corpus and a hard rule set β€” one notebook execution does everything, no external data, no pretrained weights, no uploaded artifacts, seeds fixed and printed.

This repository contains our Round 3 work. Rounds 1 and 2 are kept locally but are not published here.


The task

Property Symbol Domain Train rows Test rows Range
Glass transition temperature tg thermal 4,143 2,763 βˆ’109.8 – 495.0
Chain bandgap egc electronic 2,028 1,352 0.02 – 9.86
Bulk bandgap egb electronic 337 224 0.51 – 10.11
Dielectric constant eps dielectric 229 153 2.61 – 9.09
Refractive index nc optical 229 153 1.56 – 2.76
Ionisation energy ei electronic 222 148 4.03 – 9.84
Electron affinity eea electronic 221 147 0.39 – 5.14
Score = mean( RΒ²_tg , RΒ²_egc , RΒ²_egb , RΒ²_eps , RΒ²_nc , RΒ²_ei , RΒ²_eea )     ← UNWEIGHTED

The one fact that drove every design decision

tg has 4,143 training rows and eea has 221 β€” and they count exactly the same. The five smallest properties are 5/7 of the score sitting on under 4% of the rows.

So: model per target, or standardise per target before any shared loss. Never optimise one pooled loss on raw targets β€” tg reaches 495 and nc reaches 2.76, so tg would swallow the gradient and every other property would quietly collapse to its mean.

Two data traps that cost us time β€” worth knowing before you start

The published test row count is wrong. The Kaggle data page says "4,497 data points". test.csv has 4,940 rows β€” 4,497 counts unique molecules. A 4,497-row submission is rejected. One inherited notebook carried a len(test_df) != 4497 warning that fired on every correct run.

target_type values are lowercase (tg egc egb eps nc ei eea). Matching "Tg" does not raise β€” it silently matches zero rows and scores nan. There is an assertion at import time in src/metric.py for exactly this reason.

There are no free lookups, but there is a shortcut. Only 2 of 4,940 test rows have their exact (canonical SMILES, property) key in training. But 1,063 of 4,133 test polymers appear in training under some other property β€” which is what makes co-observed partner features legitimate, and what dictates the fold design.


Architecture

flowchart TD
    IN["<b>12,345 PSMILES</b><br/>train 7,405 Β· test 4,940<br/>every repeat unit has exactly two * points"]
    CANON["<b>CANONICALISE AT INGEST</b> (RDKit)<br/>10,605 raw strings β†’ 8,990 distinct molecules<br/><i>makes every later stage a function of the MOLECULE</i>"]
    FEAT["<b>FEATURISE β†’ 3,037 columns</b><br/>Morgan r2/r3 Β· atom-pair Β· torsion Β· MACCS<br/>RDKit descriptors Β· SMARTS groups Β· intensive twins<br/>+ leak-guarded co-observed partner block"]
    ARCH["<b>ROUND-2 ARCHIVE</b><br/><i>host-sanctioned</i><br/>6,165 tg/egc labels<br/>β‘  +2,446 training rows<br/>β‘‘ partner labels 15.3%β†’38.1%<br/>β‘’ measured override"]

    GBDT["LightGBM Β· XGBoost Β· CatBoost<br/>70 models each"]
    NN["Multi-task NN<br/>6 seeds Γ— 10 folds"]
    GNN["<b>Periodic GNN</b><br/>4 seeds Γ— 110 epochs<br/><i>best single model 0.8978</i>"]

    STACK["<b>PER-PROPERTY CROSS-FITTED RIDGE STACK</b><br/>five columns β†’ one Ridge per target<br/>OOF 0.9129"]
    PHYS["<b>TWO-PASS PHYSICS BLEND</b> β€” every relation AFFINE-FITTED<br/>ei = egc + eea Β· eea = ei βˆ’ egc Β· egb β‰ˆ egc<br/><b>eps = ncΒ² + Ξ΅_ionic</b> ← a DFPT identity, not a regression<br/>OOF 0.9233"]
    POST["Partner regression β†’ clip to observed range"]
    OUT["<b>ARCHIVE OVERRIDE β†’ submission.csv</b><br/>measured truth on 2,450 of 4,940 test rows<br/>applied AFTER clipping<br/><b>Public LB 0.924</b>"]

    IN --> CANON --> FEAT
    ARCH -.-> FEAT
    FEAT --> GBDT & NN & GNN
    GBDT & NN & GNN --> STACK --> PHYS --> POST --> OUT
    ARCH -.->|measured values never perturbed| OUT

    style IN fill:#ffffff,stroke:#999,color:#000
    style CANON fill:#e0f0ef,stroke:#00807F,color:#000
    style FEAT fill:#ffffff,stroke:#999,color:#000
    style ARCH fill:#fdf0d9,stroke:#B8790B,stroke-width:2px,color:#000
    style GBDT fill:#f5f5f5,stroke:#999,color:#000
    style NN fill:#f5f5f5,stroke:#999,color:#000
    style GNN fill:#e0f0ef,stroke:#00807F,stroke-width:2px,color:#000
    style STACK fill:#e0f0ef,stroke:#00807F,color:#000
    style PHYS fill:#fdf0d9,stroke:#B8790B,stroke-width:2px,color:#000
    style POST fill:#ffffff,stroke:#999,color:#000
    style OUT fill:#e0f0ef,stroke:#00807F,stroke-width:3px,color:#000
Loading

The full annotated pipeline, with every number taken from the saved outputs of the submitted run:

Architecture

Design choices that mattered

Five base models, fold-averaged, with no early stopping anywhere. Each model first averages its own fold replicas into one vector; those five vectors enter a per-property Ridge as five columns. There is no plain averaging across models anywhere, and the combiner is cross-fitted so its weights never see the rows they are scored on.

Per-property KFold, not polymer-grouped folds. This is the opposite of the usual advice, and it is measured:

scheme partner availability for validation rows
polymer-grouped folds 0%
per-property KFold ~96%
the actual test set 88–99%

Holding out a whole polymer removes the other properties measured on it β€” but at test time those properties are in train.csv for nearly every DFT-block row. Grouped CV would have measured a harder problem than the one being scored. (Within a single property there are essentially zero duplicate canonical polymers, so plain KFold is already leakage-safe.)

Physics applied after stacking, not as a feature. A tree splits one axis at a time and cannot cheaply represent a sum of two columns, so ei = egc + eea is wasted as one feature among 3,037. As an explicit post-stack blend it is worth +0.0250 on ei.


The three ideas that decided it

1 Β· Ξ΅ = nΒ² + Ξ΅_ionic β€” reading the physics, not tuning the model

Three of our five relations were nearly exact when applied raw. Two were not:

target expression raw RΒ² affine-fitted RΒ²
ei egc + eea 0.963 0.965
eea ei βˆ’ egc 0.971 0.973
egb egc 0.892 0.928
eps ncΒ² 0.336 0.855
nc sqrt(eps) 0.171 0.837

That collapse to 0.336 is not noise β€” it is physics. Maxwell's relation holds at optical frequency, but the dataset tabulates the static dielectric constant. A DFPT calculation accounts for exactly two contributions and nothing else β€” an electronic part and an ionic part β€” and those are stored as separate fields in the dataset of record. Since the electronic part is nΒ², the shortfall is exactly the ionic term:

$$\varepsilon_{ionic} = \varepsilon - n^2$$

An identity, not a regression β€” so eps βˆ’ ncΒ² is not a residual to be absorbed by an intercept. It is a physical quantity with its own structure–property relationship, and it can be modelled.

Two independent checks that this was real rather than convenient:

  • eps βˆ’ ncΒ² is positive on all 134 co-observed molecules (mean +0.767, min +0.024). A generic empirical residual would cross zero; an ionic contribution cannot be negative.
  • Our own blind affine fit had already converged on a = 1.040, b = 0.615 β€” a slope of one and an intercept equal to the mean ionic term. The regression was rediscovering the decomposition on its own, with the ionic part pinned to a single constant.

Naming it correctly makes the constant predictable:

approach RΒ² on covered eps rows
direct eps model 0.7129
ncΒ² + constant ionic term (what a global affine gives) 0.8514
ncΒ² + structure-predicted ionic term 0.9573

The lesson: reading where the data came from paid more than any amount of further tuning. The same fit, correctly named, went from a constant to a model.

2 Β· The Round-2 archive β€” reading the host's own baseline

The official baseline notebook shipped with the competition fetches two Google Drive files. Their -O names are swapped: the file being written to test.csv is in fact a labelled table of 6,171 tg/egc values from the Round-2 release.

We asked the hosts directly whether it was in scope, and they confirmed it β€” their own baseline downloads it. The loader (Round 3/src/archive.py) then validates the file rather than trusting the filename, declares itself in the compliance audit, and falls back to running archive-free if it is unavailable.

Measured against Round 3: 3,719 of its labels were already in train.csv and 2,450 correspond to test rows, with values agreeing on 3,717 of 3,719 overlaps β€” the same measurements, not a second dataset. Used three ways:

  1. +2,446 training rows β€” 7,405 β†’ 9,851 (+33%). The boosters are per-property so they only see more tg/egc, but the NN and GNN share one trunk across seven heads, so a larger abundant-target block improves the representation the five scarce targets ride on.
  2. Partner labels β€” true_egc test coverage 15.3% β†’ 38.1%.
  3. A measured override on 2,450 of 4,940 test rows (49.6%), applied after clipping so a real measurement is never perturbed downstream.
The prediction we got wrong, and kept in the log

We wrote: "this cannot move cross-validation β€” CV rows gain no label they did not already have." That was wrong: CV moved +0.0149 (3-fold LightGBM, 0.8514 β†’ 0.8663).

The archive supplies true_egc for polymers whose own egc row sits in the held-out fold β€” a value no training fold could otherwise see. Not a leak of the scored label, and the numbers prove it: tg moved +0.0004 and egc +0.0000, exactly as drop_leaky() guarantees, while the entire gain landed on the five targets that consume egc as a source (egb +0.0202, eps +0.0508, nc +0.0209, ei +0.0110).

And it is conservative, not optimistic: with the archive present, true_egc coverage on test rows (73.9–77.7%) exceeds coverage on CV rows (65.9–73.0%) for every one of those five targets.

3 Β· The periodic graph network β€” the repeat unit is not a molecule

A repeat unit is not a molecule with two dangling stubs: the two * points are the same bond to the neighbouring unit. So we drop both dummies, bond their neighbours, and flag the wrap-around edge β€” the network then encodes an infinite chain.

It became our best single model (0.8978) and the most complementary one: worse than the boosters on egc, far better on eea (0.9264 vs 0.8758) and ei (0.8777 vs 0.8367). It is the only member not built on the shared 3,037-column matrix, so its errors are structurally decorrelated. Adding it took the stack from 0.8713 to 0.9129.

And the readout is an invariance decision, not a hyperparameter. A sum readout scales with repeat-unit count; a mean does not. Measured on an untrained network β€” a property of the architecture, not of any fit:

readout rewriting median |Ξ”| max |Ξ”|
mean+max (shipped) permutational 0.00 0.00
mean+max (shipped) repetition (dimer) 1.11eβˆ’4 1.06eβˆ’3
sum (conventional) repetition (dimer) 4.79eβˆ’1 2.49

Four orders of magnitude β€” which is why we never use a sum readout.


The three judged themes

Round 3 scored robustness, explainability and invariance alongside accuracy. We treated all three as machine-checked deliverables the notebook proves at runtime, not claims on a slide.

Polymer invariance β€” three separate claims, reported separately

claim status evidence
Re-representation (atom ordering) βœ… EXACT 2,000 rewritings Γ— 400 molecules β†’ 0 string mismatches; 3,037 feature columns Γ— 150 molecules β†’ max |Ξ”| = 0.000e+00; end-to-end predictions β†’ max |Ξ”| = 0.000e+00
Repetition (monomer vs dimer) ⚠️ measured near-invariance ~97% of polymers give structurally identical node environments; served by intensive twins, mean+max readout and chain-extension augmentation
Translational (different cut point) ❌ not guaranteed diagnosed openly: the wrap-around edge is forced to a generic single bond and carries a positional flag, both cut-point dependent by definition

Canonicalising at ingest is the mechanism: it makes every downstream stage a deterministic function of the molecule, and canonical(rewrite(s)) == canonical(s) for any valid rewriting. It was never hypothetical β€” 67.6% of the supplied SMILES strings are non-canonical.

Explainability β€” exact TreeSHAP, and it recovered physics we never encoded

Computed inside LightGBM via pred_contrib=True β€” the same algorithm the shap package implements, deliberately with no external dependency that could fail to install mid-run.

  • tg β€” intensive twins carry 25.4% of attribution, ring counts lead. Chain stiffness dominates, exactly as free-volume theory predicts. (The invariance design choice turned out to be the biggest accuracy lever on the biggest property.)
  • egc β€” rd_FractionCSP3 tops the list: an spΒ³-rich backbone localises electrons and widens the gap; conjugation narrows it.
  • egb β€” the fitted partner combination is 26.0% and true_egc 10.3%. The relation egb β‰ˆ egc appears in the attribution rather than being asserted by us.
  • nc / eps β€” molar-refractivity surface descriptors appear for both, reproducing refractive index as electronic polarisability from an independent direction.

One molecule is explained end to end, with additivity verified against the model's own output to 3.98eβˆ’13.

Robustness β€” a calibrated applicability domain

Every prediction ships a flag saying whether the model has seen chemistry like it. 22.2% of molecules are out of domain, and the flag predicts error:

target RΒ² in-domain RΒ² out-of-domain MAE in MAE out
tg 0.938 0.884 17.77 25.31
egc 0.941 0.900 0.248 0.371
eps 0.914 0.816 0.197 0.298
ei 0.918 0.861 0.191 0.268
eea 0.953 0.917 0.157 0.244

Two details separate a useful flag from a constant: use unfolded Morgan substructure hashes (folded 2,048-bit fingerprints are degenerate at this corpus size β€” every bit is occupied), and cap the * points as methyl first (no corpus molecule carries an attachment point, so comparing raw makes every polymer novel by definition).

An honest negative result we chose to report: we tested the 5.97 M auxiliary corpus as a model input and it was worth +0.0012 β€” noise. An SVD embedding of it is a linear re-expression of the Morgan fingerprint the trees already see in full. We repurposed it as a trust signal rather than discarding it.


Repository map

AISE2.0/
β”œβ”€β”€ README.md                    this file
β”œβ”€β”€ LICENSE                      MIT (code only β€” the data is not ours to license)
β”œβ”€β”€ CLAUDE.md                    repo-wide orientation
└── Round 3/                     the finale β€” everything below is Round 3
    β”œβ”€β”€ CLAUDE.md                the operational contract: ground truth, rules, workflow
    β”œβ”€β”€ src/                     β˜… source of truth for all modelling
    β”œβ”€β”€ submissions/             exported Kaggle notebooks + saved OOF/test vectors
    β”œβ”€β”€ experiments/LOG.md       β˜… every scored run, dead ends included
    β”œβ”€β”€ context/                 the Kaggle pages (overview, rules, data, leaderboard)
    β”œβ”€β”€ documents/               reports, slides, the journey write-up
    β”œβ”€β”€ scripts/                 notebook linting, execution, diagram generation
    β”œβ”€β”€ .claude/                 skills, agents and hooks used during the sprint
    └── data/                    ⚠️ NOT IN GIT β€” see "Getting the data"
src/ β€” the harness
file role
data.py Β· paths.py loading, canonical dedupe, Kaggle/local path resolution
smiles_utils.py canonicalisation, and the three polymer rewritings (permute / translate / oligomerise)
features.py 3,037-column featuriser, keyed by canonical SMILES
partners.py co-observed partner features + the leakage guard
archive.py the host-sanctioned Round-2 label table, validated on load
physics.py the five relations, affine-fitted β€” never applied raw
splits.py Β· oof.py fold design; base models β†’ cross-fitted stack β†’ two-pass physics
models/ trees.py, mtnn.py, gnn.py, cnn.py, simple.py
configs/ lgbm.py, lgbm_physics.py, ridge_baseline.py
cv.py Β· predict.py the two entry points every other tool derives from
invariance.py Β· explain.py Β· report.py the judged deliverables
check_submission.py Β· export_notebook.py submission gate; notebook generator

A config is exactly two functions β€” fit(train_df, seed, targets=None) and predict(state, df) β€” and CV, inference, the invariance audit and the explainability report all derive from that pair. What CV measured is literally what gets submitted.


Reproduce

1 Β· Get the data

The competition data is deliberately not in this repository. Rule Β§4.b of the competition rules: "You agree not to transmit, duplicate, publish, redistribute or otherwise provide or make available the Competition Data to any party not participating in the Competition." (smile_r3.csv is also 314 MB, which exceeds GitHub's 100 MB per-file limit outright.)

Download from the competition data page into Round 3/data/:

Round 3/data/
β”œβ”€β”€ train.csv               7,409 rows  (smiles, target, target_type)   long format
β”œβ”€β”€ test.csv                4,940 rows  (id, smiles, target_type)
β”œβ”€β”€ sample_submission.csv   a format illustration, not a template
β”œβ”€β”€ PI1M.csv                995,800 polymer SMILES with * wildcards
β”œβ”€β”€ smile_r3.csv            5,973,370 small-molecule SMILES (no wildcards)
└── base_line_model.ipynb   the host's official baseline

2 Β· Build the environment

There is no system pandas, rdkit or lightgbm β€” use the project venv:

cd "Round 3"
uv venv --python 3.12 .venv
uv pip install --python .venv/bin/python -r requirements-dev.txt

3 Β· Run it

Every command below is run from Round 3/.

# Score a config β€” the gate every idea passes before it gets a submission slot
./.venv/bin/python -m src.cv --config lgbm            # ~377 s, the local reference
./.venv/bin/python -m src.cv --config lgbm_physics    # +0.0288, 4x the noise floor
./.venv/bin/python -m src.cv --config ridge_baseline  # host baseline equivalent, 4 s

# Full fit -> submission.csv  (~55 s for lgbm)
./.venv/bin/python -m src.predict --config lgbm --out submission.csv

# Validate before uploading β€” always
./.venv/bin/python -m src.check_submission submission.csv

# The judged deliverables
./.venv/bin/python -m src.invariance --config lgbm    # invariance certificate
./.venv/bin/python -m src.explain --config lgbm       # per-target TreeSHAP report

# The full five-model ensemble (long)
./.venv/bin/python -m src.run_ensemble --models lgbm,xgb,cb,mtnn,gnn --folds 10

# Regenerate the Kaggle notebook from src/
./.venv/bin/python -m src.export_notebook \
    --models lgbm,xgb,cb,mtnn,gnn --out submissions/final.ipynb

Runs are appended automatically to experiments/LOG.md. The harness prints a noise floor β€” a delta smaller than 2Γ— it is not an improvement, and we said so out loud rather than claiming a win.

4 Β· On Kaggle

src/paths.py resolves the data at runtime (/kaggle/input/aisehack-2-0, then /kaggle/input, then local data/), so the exported notebook runs unmodified. Attach the Round-2 archive as a Kaggle Dataset if internet is off β€” otherwise the pipeline warns and runs archive-free rather than failing.

⚠️ Two platform traps that each cost hours

LightGBM must be imported before PyTorch. They bundle separate OpenMP runtimes; on macOS, torch-first makes LightGBM segfault inside __init_from_np2d with no traceback. The same duplicate-runtime trap also deadlocks β€” one local run sat at 0% CPU and 63 MB RSS for 44 minutes. Linux (and therefore Kaggle) is unaffected, but the import order costs nothing and makes the notebook run anywhere.

For local runs: OMP_NUM_THREADS=4 KMP_DUPLICATE_LIB_OK=TRUE, and do not run two model jobs at once.

Notebook print()s are block-buffered when stdout is a file. A cell can look stalled for minutes while its output sits in an 8 KB buffer. Run verifiers with PYTHONUNBUFFERED=1 before concluding anything is stuck.


Notebook ledger

Round 3/submissions/ holds the full experimental record, not just the winner.

notebook public LB what it is
ppp-v3-g.ipynb 0.924 ⭐ THE SUBMISSION. Private LB 0.917, 3rd place. 5 models, archive as training rows + override, 3 h 01 m
ppp-v3-g-fixed.ipynb β€” the same, with two crash points repaired
ppp-v4-g.ipynb 0.919 2 h 53 m variant, higher seed counts, archive not in training
round3_v2.ipynb 0.922 an independently built sibling pipeline β€” our second final pick
round3_final.ipynb β€” sibling pipeline; records the host confirmation on the archive
final.ipynb β€” generated by src/export_notebook.py from src/
aisehack3-1.ipynb 0.883 the incumbent we started Round 3 with (OOF ~0.903)
aisehack3-2/5/6.ipynb β€” intermediate iterations
stack_only.ipynb β€” stack-only variant, built for CV-to-LB calibration
round-3-aisehack.ipynb β€” "v2" β€” never scored; documented risks, not a safe default
pie-net-v3-checkpoint-run-2.ipynb β€” β›” rejected β€” rule violation. Imports checkpoints from a previous Kaggle run (Β§6.2.2/Β§6.2.4). Kept for the record; never submitted

submissions/oof_and_preds/ holds saved OOF and test vectors for every base model, so a blending experiment costs seconds instead of a full rerun.


Deliverables

document
πŸ“– The journey The full narrative β€” what led to what, every failure, and the lessons. Start here.
πŸ“„ Technical report (PDF) The formal write-up Β· LaTeX source
πŸ“Š Finale presentation (PDF) The slides presented at IIT Madras
πŸ“‹ Mid-point check-in The interim status report
πŸ§ͺ Experiment log Every scored run and every dead end, with noise floors

What we learned

The full list is in journey.md. The five that generalise beyond this competition:

  1. Fix your ruler before you build. Our first instinct was to improve a 0.903. The right move was to ask why 0.903 became 0.883. Every improvement measured with a broken validation is a coin flip.
  2. Be suspicious of your biggest wins. The size of a measured gain correlated almost perfectly with the probability it was a leak: +0.069 was a leak, +0.042 was a leak, +0.008 was a leak. The real gains were +0.003 to +0.010 and arrived slowly.
  3. Leaks have one shape β€” a value that is itself a model output, used as an input, closing a cycle back to the row's own label. Once we named the shape we found the rest quickly. And prove the leak exists before proving the guard removes it: a guard that passes without a demonstrable leak proves nothing.
  4. Read where your data came from. The largest physics gain came from a 2020 DFPT paper and a 2016 dataset description; the second largest came from reading the host's own baseline notebook line by line. Neither was a modelling idea.
  5. Only new information ever paid. Every purely derived feature failed. What worked: partner labels, a new model class, more data, truer structure, and an exact physical identity. If a feature is a deterministic function of columns the model already has, a good model already has it.

References

The physics and architecture choices are grounded in published work, principally from the Ramprasad group, whose datasets these targets originate from.

  1. Chen, Kim, Batra et al. & Ramprasad, Frequency-dependent dielectric constant prediction of polymers using machine learning, npj Comput. Mater. 6, 61 (2020) β€” source of the ionic decomposition.
  2. Huan, Mannodi-Kanakkithodi, Kim, Sharma, Pilania & Ramprasad, A polymer dataset for accelerated property prediction and design, Sci. Data 3, 160012 (2016) β€” the dataset of record; stores Ξ΅_electronic and Ξ΅_ionic as separate fields.
  3. Gurnani, Kuenneth, Toland & Ramprasad, Polymer informatics at scale with multitask graph neural networks, Chem. Mater. 35, 1560 (2023) β€” polyGNN; source of the periodic graph.
  4. Kuenneth, Rajan, Tran, Chen, Kim & Ramprasad, Polymer informatics with multi-task learning, Patterns 2, 100238 (2021) β€” the shared-trunk design, and the origin of this property set.
  5. Lightstone, Chen, Kim, Batra & Ramprasad, Refractive index prediction models for polymers using machine learning, J. Appl. Phys. 127, 215105 (2020).
  6. Gilmer, Schoenholz, Riley, Vinyals & Dahl, Neural message passing for quantum chemistry, ICML (2017).
  7. Lundberg & Lee, A unified approach to interpreting model predictions, NeurIPS (2017); Lundberg et al., Nat. Mach. Intell. 2, 56 (2020) β€” SHAP / TreeSHAP.
  8. Venkatram, Batra et al. & Ramprasad, Predicting crystallization tendency of polymers using multi-fidelity information fusion, J. Phys. Chem. B (2020) β€” ancestor of the partner features.
  9. Liu et al., Open Polymer Challenge: Post-Competition Report, arXiv:2512.08896 (2025) β€” independently corroborates chain extension as a top-solution strategy.
  10. Tran, Kim, Chen, Chandrasekaran, Batra et al., Machine-learning predictions of polymer properties with Polymer Genome, J. Appl. Phys. 128, 171104 (2020) β€” our external benchmark.

Team Cross Linkers

ANRF AISEHack 2026 Β· Grand Finale, IIT Madras Β· πŸ₯‰ 3rd out of 145 teams

Public LB 0.924 Β· Private LB 0.917 Β· mean OOF RΒ² 0.9234 Β· 380 models Β· 3 h 01 m on one Tesla T4

Invariance Ξ” = 0 exactly, certified at three levels Β· TreeSHAP per property, additivity verified to 4eβˆ’13 A single reproducible Kaggle notebook: all seeds fixed, no wall-clock branching, no artifact imported that the run did not itself create.

Code released under the MIT License. Competition data is not included and is not ours to redistribute.

About

πŸ₯‰ 3rd of 145 teams at ANRF AISEHack 2.0 β€” seven polymer properties from PSMILES via a physics-constrained multi-task ensemble. Public LB 0.924.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages