You inherit a naive symbolic-regression model family: a Python function func(x, params) that
currently predicts a plain linear combination of the inputs. Your task is to evolve it into a richer
family that can express the kind of structure real scientific laws carry — so that, after its
parameters are fit to data, it recovers the underlying law and extrapolates correctly.
You do not submit a formula for one equation. A sealed verifier takes several hidden
scientific-style equations (the kinds of laws that turn up across physics, chemistry and
engineering), fits your
func's parameters to each one's training points, and scores how well the fitted family predicts on
held-out points those equations never showed you. The hidden equations differ from anything in
this workspace, so a formula hardcoded for a specific equation earns nothing — only a family that
generalises across unseen laws scores.
Hard Constraints
- Submit a model family (the
methods/main/directory), not an answer. The verifier re-importsmethods/main/sr_program.py, fitsparamsfrom scratch, and scores it. - Keep the exact contract in
methods/main/sr_program.py: func(x, params) -> np.ndarray:xis(n_samples, n_features)withn_featuresin1..3;paramsis 1-D of lengthNUM_PARAMS; return shape(n_samples,).NUM_PARAMS: int — the verifier optimises a params vector of this length by multi-restart BFGS, sofuncmay be non-linear inparams: an entry ofparamsmay appear anywhere in the expression, not only as a linear coefficient, and it is still fitted for you — e.g. a parameter sitting inside a non-linear call,params[k] * g(params[j] * x[:, 0]), is optimised just as a plain coefficient is.- Numerical safety: never return NaN or Inf — clip exponent arguments, keep denominators away
from zero, guard
sqrt/log. Any sample that yields a non-finite value is scored as a miss. - Budgets (integrity):
methods/maintotal size ≤ 262,144 bytes andNUM_PARAMS ≤ 200, so a lookup table or precomputed constants cannot be smuggled in as "code". No network at grade time.
What You Have
/app/(/app/README.mdorients you):methods/main/sr_program.py— the graded file: the naive linearfunc+NUM_PARAMS.DATA_CARD.md— the contract, the public practice equations, and the budgets.- Your self-check surface (free):
python /app/selfcheck.pyruns yourfuncthrough the exact fit-then-extrapolate pipeline on five public practice equations (different from the graded ones) and prints the raw mean extrapolation accuracy. Treat it as a proxy only — the sealed equations differ, so keep margin and stay general.
What You Submit
Edit /app/methods/main/sr_program.py so that func is a family expressive enough for the laws the
verifier will fit it to, while remaining numerically safe, and raise NUM_PARAMS to match the
parameters your family needs. There is no
submit step and no per-attempt feedback — self-check as long as your window allows, then leave your
best methods/main in place; it is fit and graded once at the end.
How It Is Judged
For each hidden equation the verifier fits your params (multi-restart BFGS on that equation's
training points), then predicts on a wider-range held-out set and measures the extrapolation fit:
per hidden equation: score = max(0, R^2) on the held-out extrapolation points (1.0 = perfect)
metric = mean of that score across all hidden equations (higher is better)
The verifier owns the fitting and the scoring and re-runs your func on data it never revealed, so
neither the fit nor the score can be gamed.
The grading budget, in full — size your family against it. You get no per-attempt feedback, so these numbers are published rather than left for you to guess:
| grading run | free selfcheck.py |
|
|---|---|---|
BFGS fits of your func |
500 (several hidden equations × several data draws × 10 restarts) | 40 (5 practice equations × 8 restarts) |
| iteration cap per restart | 100 | 100 (identical) |
| wall-clock budget | 3600 s total | none |
| resources | 2 CPU cores, 1024 MB | 4 CPU cores, 2048 MB |
So grading costs about 12× a self-check run (500 fits vs 40, same per-fit cap): time one
python /app/selfcheck.py, multiply by ~12, and compare against 3600 s. Two things drive that cost:
- BFGS differentiates your
funcnumerically, so one iteration costs aboutNUM_PARAMS + 1calls. Total calls per grade ≈50,000 × (NUM_PARAMS + 1): about 1.8 million atNUM_PARAMS = 35and 11 million at the maximumNUM_PARAMS = 200. - So the budget is really a per-call budget:
3600 s ÷ 11e6 ≈ 330 µs per callatNUM_PARAMS = 200(and ~2 ms per call atNUM_PARAMS = 35). A lean vectorisedfuncis a few tens of µs; one that rebuilds a large(n_samples, NUM_PARAMS)basis matrix on every call is several hundred µs and is tight at the top of the range. Time your ownfuncand do the arithmetic —NUM_PARAMSis only free if the extra parameters do not also make each call slower.
If the budget does run out, you are not zeroed: the equations whose predictions are already written are scored normally and the unfinished ones count as misses, so a slow family degrades gradually instead of falling off a cliff. It is still lost score — finishing is strictly better.