You inherit a spectrum-blind baseline: for every planet it predicts the training-set marginal mean and standard deviation for each atmosphere parameter, completely ignoring the 52-bin transmission spectrum it was given. Your goal is to raise the mean Gaussian-Log-Likelihood (GLL) of your predicted posterior mean/std against the true injected atmosphere parameters; a sealed verifier re-trains and re-runs your solver on a HIDDEN, disjoint batch of planets and scores it (higher is better).
Hard Constraints
- You may only edit code under
/app/methods/main/; you may add sibling.pymodules. - Keep the entrypoint signatures
train(train_records) -> Noneandpredict(test_records) -> (mean, sigma), wheremean/sigmaare each a list of lists of shape[len(test_records), 6], one row per input record in the same order astest_records(positional, not keyed by planet ID), columns in the order ofTARGETS = ["planet_temp", "log_H2O", "log_CO2", "log_CH4", "log_CO", "log_NH3"].sigmamust be> 0everywhere. numpy+pandas+scikit-learn+scipy+ the standard library only — no internet at run time, no other third-party packages. The verifier image has exactly these; any other import makes the submission score 0. Network access during your own session is restricted to the model API as well (an allowlist is enforced outside your container), so solve the task from the data in/app/datarather than trying to fetch anything.- Grading budget (the box your submission is re-run in).
train()+predict()together run once, in a fresh process, under a 7200-second wall-clock budget with 4 CPUs and 1 GB of memory — the same shape as this session's container, so a self-check that fits here fits there. Over-budget or out-of-memory means the whole submission scores 0, and no partial result is kept. The graded call is modestly larger thanselfcheck.py's: it fits on 3200 labelled planets (vs 2560 in the self-check split) and predicts 800 (vs 640) — 1.25x on both, so scale your self-check timings by about that before deciding you have room. - Your solver must be deterministic given the shipped train/test data — the verifier calls
train()thenpredict()fresh in its own process; unseeded randomness makes your local self-check unrepresentative of the graded run (seed every model you fit). /dev/shmis only 64 MiB (the container default, here and in the grader) and cannot be enlarged. A shared segment larger than that is created successfully and only faults on the first write, killing the process with SIGBUS and no traceback -- no output, so the submission scores 0. If you parallelise withmultiprocessing.shared_memory, or hand very large arrays tojoblib/lokyworkers, keep the data in ordinary heap memory or point the temp folder at a normal filesystem path.
What You Have
/app/data/train.csv: 3200 visible planets. Each row hasspec_0..spec_51(52-bin transmission spectrum,(Rp/Rs)^2, dimensionless),noise_0..noise_51(1-sigma instrument noise, same bins),star_radius_m,planet_radius_m,star_temperature(aux physical features), and the 6 true target columns./app/data/wavelength_grid.csvgives the wavelength (microns) and bin width for each of the 52 spectral bins — identical grid for every planet, shipped once./app/methods/main/solver.py: the baseline solver (spectrum-blind constant mean/std) — this directory is what gets graded. Improve it in place or rewrite the algorithm entirely. Matching the baseline earns nothing./app/gll_metric.py: the exact GLL formula the verifier uses (the formula is public; only the true target values are sealed). Read it to see precisely how you are scored./app/selfcheck.py: a free, unlimited local dry-run (python /app/selfcheck.py) that fits on an 80/20 internal split of the visible planets and prints the proxy mean GLL. It is a proxy only — the real held-out planets are a different, disjoint, sealed batch.
What You Submit
Edit /app/methods/main/solver.py, keeping the contract:
def train(train_records: list[dict]) -> None:
# Called once, on the labelled re-training set (same columns as /app/data/train.csv). Fit
# whatever state you need and stash it (module globals are fine).
...
def predict(test_records: list[dict]) -> tuple[list[list[float]], list[list[float]]]:
# Called once, on FEATURE-ONLY rows (no target columns). Return (mean, sigma), each
# [len(test_records), 6], same row order as test_records, columns in TARGETS order.
...
There is no submit step and no per-attempt feedback on the real held-out set — iterate against
selfcheck.py, then leave your best solver.py in place; it is graded once at the end.
How It Is Judged
After your run, the grader copies your methods/main/ into a sealed verifier, spawns a fresh process
that imports it and calls train() on the sealed re-training planets, then predict() on a HIDDEN,
disjoint batch of held-out planets, and scores the mean Gaussian-Log-Likelihood of your posterior
mean/std against the true injected atmosphere parameters (higher is better):
GLL(x, mu, sigma) = -0.5 * log(2*pi*sigma^2) - (mu - x)^2 / (2*sigma^2)
averaged over every (planet, target) pair. Your score rises monotonically with the mean GLL, so
pushing GLL up is always the goal. Any crash, wrong output shape, non-finite value, or
sigma <= 0 scores the whole submission 0.