Conversation
A registered metric is now five fields: name, function, family, cost and direction. The first line of its docstring is the question the column answers, written so it reads without the metric's name. Direction None means a diagnostic -- read beside the other columns, never ranked on -- which is what `dpp` becomes: three monotone transforms of one determinant that collapses toward underflow before the models stop differing. Where a metric is computed and how per-design values collapse to one number are no longer properties of the metric. `Board.evaluate` takes `space` (pixel, or a PCA of the reference designs) and `aggregation` (mean or median), so every metric is written once; the spec records the aggregation in each row and the `*_median` columns are retired. The board also scores half of the reference designs against the other half, which is the only honest measurement of what real designs score on a column. Port the math layer from the workshop branch (vendi, geometric-mean DPP, median-sigma, PRDC), fix `register_metric` silently targeting the global registry when handed an empty one, and replace "brief" with "conditions" throughout, including three PR #75 docstrings. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Ten code cells, one call each: what metrics exist, score five models, read the board, rank, change the aggregation, change the space, register a metric. Two of the models are constructions with known right answers; the one that returns the correct withheld designs under permuted conditions scores a perfect MMD, which is the lesson. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Twenty metrics, each one question: the set-level ones (mmd, coverage, vendi, dpp), the per-condition ones (per_condition_distance, volume_error), feasibility, memorization (train_distance, copy_rate), cond_sens, the optimality gaps, four call-budget metrics that price a defect in optimizer calls (calls_to_settle, gap_after_calls, reaches_reference_rate, first_call_gain), and three cost columns. Names say what they measure. `novelty` split into `train_distance` (nearest training design) and `copy_rate` (the submission gate, against everything the model could have copied); `cond_err` became `volume_error`, since it was never about conditions in general; `recovery` became `gap_after_calls`, and `parity_rate` became `reaches_reference_rate`. `calls_to_parity` is gone: a copier reaches parity in zero calls. `dpp` keeps its name and becomes the n-th-root form, since the raw determinant reads 1e-20 on every real board. The context now carries each design's re-optimization trajectory, the generator's parameter count and training time, so the new metrics have what they read. Specs move to v2 with the full list and the aggregation policy; v1 stays committed so published rows can be read under the protocol that produced them, and only the current spec must name live metrics. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`Board.evaluate(space=...)` looks the space up in a registry: `pixel` and `pca` are built in, and a learned latent space registers one projection function. Metrics that need the actual designs declare `pixel_only` and are skipped elsewhere. The kernel bandwidth defaults to the median pairwise distance of the reference designs in the chosen space, so the diversity columns can see anything on a problem the spec never pinned a value for. `Board.from_generators` scores loaded models through an `Evaluator`, which is what cond_sens, the cost columns and the physics need, and `Board.load` pulls published checkpoints by name -- the workshop in one call. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The docs named `novelty`, listed the old default pass, and pointed at spec v1. They now name the columns that exist, say what --list-metrics prints, and send readers to the notebook for the rest. `Board` is exported from `engiopt.evaluation` as the front door. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`ruff format --check` covers .ipynb files and the notebook had never been through the formatter. Re-executed after formatting so the stored outputs match the source. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
CI runs `ruff check .` over notebooks too. The first cell set pandas display options before importing the registry, two cells had imports out of the repo's order, and the toy problem's helpers lacked docstrings. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
After #75 the evaluation layer on
mainregistered eight metrics and no way to score several models at once without building anEvaluationContextby hand. The full suite lived onfeat/dataset-sensitivity-fieldsas ~42 separately maintained functions with prefixed names (pca_mmd,lv_vendi,iog_median, ...). This PR brings the suite tomainon a contract simple enough to explain in one sentence, and adds the object you use it through.Nothing here touches the LVAE, the baselines, or the pool tooling. Those are separate PRs.
How you use it
example_metrics_suite.ipynbat the repo root walks all of this on a toy problem in ten code cells. It runs in seconds with no downloads.The contract
A metric is a function of an
EvaluationContextplus five declarations: name, family, cost, direction, and (rarely)pixel_only. The first line of its docstring is the question the column answers, and that line is whatexplain()and--list-metricsprint.higher_is_better=Nonemeans a diagnostic: read it beside the other columns, never rank on it.Two things that used to be baked into metric names are now arguments to
Board.evaluate:pixelandpcaare built in; a learned latent space registers one projection function withregister_space. Metrics that need the actual designs (a constraint check, a copy corpus) declarepixel_onlyand are skipped elsewhere. This replaces thepca_*/lv_*/lvoff_*/pixel_*prefixes.meanormedian, declared on the spec and recorded in every row. This replaces the*_mediancolumns.The metrics
Twenty, down from forty-two. Renamed so the name says what is measured:
mmd,coveragevendi,dppcond_sens,per_condition_distance(waspaired_distance),volume_error(wascond_err)train_distance,copy_rate(together, wasnovelty)violiog,cog,fog,calls_to_settle,gap_after_calls,reaches_reference_rate,first_call_gaingeneration_seconds,n_parameters,train_minutesdppkeeps its name but is now the n-th root of the kernel determinant, bounded in (0, 1]. The raw determinant that v1 rows hold reads around 1e-20 on every real board and cannot be compared with this column.calls_to_parityis gone: parity is measured against the reference optimum, so a copier reaches it in zero calls.The board also scores one random half of the reference designs against the other half and shows that as a row. That is what real, correct designs score on each column on this problem, and it is the only comparison point that is measured rather than assumed.
Specs
All four problems get a
v2.jsonwith the full metric list and the aggregation policy; v2 is now the default.v1.jsonstays committed so rows published under it can be read. A v1 spec still loads, but evaluating under it raisesUnknown metric 'novelty'at metric selection, because that metric no longer exists. Nothing on theIDEALLab/engiopt-leaderboarddataset references v1 (it has no rows).Also in here
OptimizationResults.trajectories(each design's gap at every optimizer call),EvaluationContext.reduce,model_params,train_minutes,train_designs.register_metricsilently registered into the global registry when handed an empty custom one, becauseMetricRegistryis aMappingand an empty mapping is falsy. Fixed.LEADERBOARD.md,README.mdandCONTRIBUTING_A_MODEL.mddescribe the suite that exists now.What I checked
pytest tests/ -m "not network": 298 passed, 0 skipped, 1 failed. The failure istest_every_generator_directory_has_an_adapter, which trips on six leftover__pycache__-only directories in my working copy from branch switching. They are not in git and cannot exist in CI.python -m engiopt.evaluate --list-metricsruns as documented and prints the table above.tests/test_metrics_suite.py(19),tests/test_board.py(8),tests/test_metric_registry.py(4). The trajectory metrics are checked against hand-built paths with known answers.What I did not check
Board.loadcallsEvaluator.for_problemandGenerator.from_pretrainedwith the signatures they have onmain, but has not been exercised end to end. The notebook is a toy problem by design.Follow-ups
evaluate(bootstrap=...)). Planned, small, not in this PR.knn_retrieval,deconv_regression, the planted constructions): separate PR.space="lv": registered by the LVAE PR once the Lipschitz fixes land.🤖 Generated with Claude Code