Conversation
Introduce a mechanism to measure numeric test tolerances instead of asserting them, so they can be tightened automatically. - config: ICON4PY_RECORD_TOLERANCES (record measured differences to a file) and ICON4PY_TOLERANCE_DRIFT_WARN (non-failing warning when a tolerance is much looser than the measured difference). - tolerances: ToleranceRecorder plus helpers to aggregate measurements (max over deterministic CPU backends) and propose tightened tolerances. - test_utils: dallclose/assert_dallclose record or drift-warn via shared helpers and take a 'key' label identifying the compared field. - pytest_hooks: activate the recorder, attach per-test context (test id, backend, experiment), dump measurements as JSON lines, and print a drift summary. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the inline RBF_TOLERANCES dict with a JSON file loaded at import time, and label the comparisons with the dimension name so they can be recorded. This is the pilot for the automatic tolerance-tightening tooling; the loaded table keeps the same shape so consumers are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- update_tolerances.py: tighten a JSON tolerance store from recorded
measurements (tighten-only, deterministic CPU backends, max over
backends scaled by a safety factor).
- noxfile: 'update_tolerances' session that records tolerances on the
deterministic CPU backends and runs the updater.
- ci: a job in the weekly 'all' pipeline that runs the session and
publishes the proposed tolerance tightenings as an artifact
('tolerance-updates.patch'); it never commits, so a human reviews and
applies the change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
951502c to
2f32a3b
Compare
|
Mandatory Tests Before merging, run the When developing, you can test your changes on CSCS CI before merge with the You can pass options to override pipeline variables, for example:
Available options are:
For each option, See The Optional Tests To run benchmarks you can use:
For more detailed information please look at CI in the EXCLAIM universe. |
|
may benefit from rel delta computation from #1333 as well |
nfarabullini
left a comment
There was a problem hiding this comment.
some initial comments
| # Absolute tolerances for the RBF interpolation coefficients, keyed by dimension and experiment | ||
| # name. Maintained via the tolerance-tightening tooling (see 'scripts/python/update_tolerances.py'); | ||
| # edit 'rbf_tolerances.json' rather than hardcoding values here. |
There was a problem hiding this comment.
| # Absolute tolerances for the RBF interpolation coefficients, keyed by dimension and experiment | |
| # name. Maintained via the tolerance-tightening tooling (see 'scripts/python/update_tolerances.py'); | |
| # edit 'rbf_tolerances.json' rather than hardcoding values here. | |
| # Absolute tolerances for the RBF interpolation coefficients, maintained via the tolerance-tightening tooling. |
| # When set to a file path, 'assert_dallclose' records the measured max absolute/relative | ||
| # differences (instead of asserting) so that tolerances can be measured and tightened automatically. |
There was a problem hiding this comment.
| # When set to a file path, 'assert_dallclose' records the measured max absolute/relative | |
| # differences (instead of asserting) so that tolerances can be measured and tightened automatically. |
You don't need this
| # When enabled, 'assert_dallclose' emits a non-failing warning whenever a passed tolerance is much | ||
| # larger than the measured difference, flagging tolerances that have become too loose. |
There was a problem hiding this comment.
| # When enabled, 'assert_dallclose' emits a non-failing warning whenever a passed tolerance is much | |
| # larger than the measured difference, flagging tolerances that have become too loose. |
Neither this
| config.option.markexpr = " and ".join(["not datatest", *m_option]) | ||
|
|
||
| # Activate tolerance recording/drift detection when the corresponding options are enabled. | ||
| if testing_config.RECORD_TOLERANCES_PATH is not None or testing_config.TOLERANCE_DRIFT_WARN: |
There was a problem hiding this comment.
| if testing_config.RECORD_TOLERANCES_PATH is not None or testing_config.TOLERANCE_DRIFT_WARN: | |
| if testing_config.RECORD_TOLERANCES_PATH or testing_config.TOLERANCE_DRIFT_WARN: |
I think you can also just say this
| # A stored tolerance is flagged as drifted (too loose) when it exceeds the measured difference by | ||
| # more than this factor. | ||
| DRIFT_FACTOR = 100.0 | ||
| # Proposed tolerances are the measured difference scaled by this factor to leave headroom. | ||
| SAFETY_FACTOR = 4.0 | ||
| # Backends whose results are deterministic enough that measured differences may be used to tighten | ||
| # tolerances automatically. GPU and dace backends are excluded (documented as non-deterministic). | ||
| DETERMINISTIC_CPU_BACKENDS = frozenset({"gtfn_cpu", "embedded"}) |
There was a problem hiding this comment.
can you summarize comments?
| actual_array = np.asarray(actual, dtype=float) | ||
| desired_array = np.asarray(desired, dtype=float) | ||
| absolute = np.abs(actual_array - desired_array) |
There was a problem hiding this comment.
| actual_array = np.asarray(actual, dtype=float) | |
| desired_array = np.asarray(desired, dtype=float) | |
| absolute = np.abs(actual_array - desired_array) | |
| absolute = np.abs(actual_array - desired_array) |
Maybe you can dimply do this?
| return _RecorderState.active | ||
|
|
||
|
|
||
| def activate_recorder() -> ToleranceRecorder: |
There was a problem hiding this comment.
| def activate_recorder() -> ToleranceRecorder: | |
| def activate_recorder() -> ToleranceRecorder | None: |
| exponent = math.floor(math.log10(scaled)) | ||
| fraction = scaled / 10.0**exponent | ||
| return math.ceil(fraction) * 10.0**exponent |
There was a problem hiding this comment.
why all of this is effectively this is the same as:
return math.ceil(scaled)
Related issue: #967
from a friend:
Automatic test-tolerance measurement and tightening (RBF pilot)
Numeric test tolerances are currently chosen by hand: run with
ICON4PY_DALLCLOSE_PRINT_INSTEAD_OF_FAIL=true, read the printed max diffs, and paste values intothe tests. Nothing revisits a tolerance once a fix makes it too loose, so stale tolerances can hide
regressions.
This PR adds machinery to measure tolerances automatically and tighten them, and pilots it
on the RBF interpolation tolerances. The bulk of the ~300 inline
atol/rtolliterals elsewhereare intentionally left for a follow-up.
How it works
ICON4PY_RECORD_TOLERANCES=<file>):dallclose/assert_dallclosemeasure themax absolute/relative difference instead of asserting, tagged with
{backend, experiment, field}via the pytest plugin, and dump to JSON lines.
ICON4PY_TOLERANCE_DRIFT_WARN): a non-failing terminal summary flagstolerances that are much looser than the measured difference.
scripts/python/update_tolerances.py,nox -s update_tolerances): aggregatesmeasurements (max over the deterministic CPU backends, scaled by a safety factor, rounded up)
and only tightens a stored tolerance — never loosens it.
allpipeline runs the nox session ongtfn_cpu/embeddedand publishes the proposed tightenings as atolerance-updates.patchartifact. Itnever commits or pushes — a human reviews and applies the patch. (No GitHub token needed in CSCS
CI; the
allpipeline already has the serialized data, the backends and the weekly cadence.)Design notes
auto-managed backends, matching current practice. GPU/dace backends are excluded because they are
documented as non-deterministic; their per-backend tolerances stay hand-maintained. The recorder
keeps per-backend data in the JSONL, so a future per-backend store schema is a compatible
extension.
regression.
{dimension: {experiment: atol}}shape, so consumers are unchanged.Validation (locally, real serialized data)
embeddedandgtfn_cpu.loosened it tightened them from the measured diffs (e.g.
Vertex/gauss3d 1e-6 -> 9e-15); thedace_gpusample was ignored and MCH was never loosened.Follow-ups
standalone_driverprognostic-field tolerances onceAdd more experiments to standalone driver tests #1333 lands).
deliverable; a webhook secret would let CI ping instead of relying on manual monitoring.
Draft: opening for review of the approach before wiring more test suites into the store.