Repository navigation
fix(exp): make results.json byte-identical on every toolchain - #1
Merged
Merged
Conversation
CI's "Exports are current" step regenerates site/data and fails on any diff. results.json differed from the committed file only in the last binary digit of a few floats: json.dumps writes up to 17 significant digits, and that digit depends on the toolchain, not on the study. - CPython 3.12 made sum() of floats compensated (Neumaier), so Long.profile_mean gave results one ulp apart on 3.11 (where the file was exported) and on 3.12 (the runner). This is the diff CI reported. - pandas 3.1 made read_csv's float parser correctly rounded; 2.x and 3.0 are one ulp off on ~6% of the values in results/. requirements.txt is unpinned, so the next runner would resolve 3.1 and fail on ~100 more lines. Fix, in experiments/export_web_results.py: - write_json rounds every float to JSON_SIGNIFICANT_DIGITS = 10 significant digits (canonical_floats). That is ~1e6 times coarser than the noise and finer than anything the dashboard prints (at most 4 decimals). Rankings and comparisons in build() keep full precision. - profile_mean uses math.fsum, which is correctly rounded on every Python version, so the known source of drift is gone at its root. site/data/results.json is regenerated. It equals the previous file with each float rounded to 10 significant digits (worst relative change 4.4e-10), and the dashboard renders identical tables. The export is now byte-identical under Python 3.11 and 3.13 with pandas 2.3.3, 3.0.6 and 3.1.0; the closest value sits ~2,400 ulps from a rounding boundary. config.json and the traces were already deterministic and are unchanged. Co-Authored-By: Claude Opus 5.5 <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.
Root cause
The browser engine matches Python job regenerates
site/dataand runsgit diff --exit-code -- site/data. All four CI runs onmainso far (e.g. run 36266471164) failed there, with the same diff insite/data/results.json: only the last digit of a few floats changes.json.dumpswrites the shortest round-trip repr, which can be up to 17 significant digits. That last binary digit comes from the toolchain, not from the study. Two separate upstream changes make it drift:sum()over floats to compensated (Neumaier) summation.Long.profile_meanused the built-insum(). The committed file was exported on Python 3.11 and the runner uses 3.12, so the Table IV (right) averages came out one ulp apart. These are exactly the 4 lines CI reported. I reproduced it locally: Python 3.13 gives CI's values and 3.11 gives the committed ones.read_csv's float parser correctly rounded (and deprecatedfloat_precision). pandas 2.x and 3.0 are one ulp off on 49,544 of the 795,356 float values inresults/(about 6%). The committed file carries the pandas 2.x parse.requirements.txtis unpinned, so the next runner would resolve pandas 3.1 and fail on about 100 more lines even with (1) fixed.The research numbers are fine. The drift is about 1e-16 relative.
Fix (
experiments/export_web_results.py)write_jsonnow passes the payload throughcanonical_floats, which rounds every float toJSON_SIGNIFICANT_DIGITS = 10significant digits. It uses Python's correctly rounded float formatting, which doesn't depend on the platform C library.rankingsand every comparison inbuild()still see full-precision values.Long.profile_meannow usesmath.fsum, which is correctly rounded on every Python version.site/data/results.json.The CI check itself is unchanged.
Verification
results.jsonis exactly the old file with each float rounded to 10 significant digits. The worst relative change is 4.4e-10, and keys, ints, booleans and strings are untouched.export_web_config+export_web_traces+export_web_results+git diff --exit-code -- site/data) passes in each.site/js/research.jsunder Node on the old and new file. The headline cards, the honest block and all six tables render to identical HTML.python -m pytest tests_web -qpasses (7, including the JS/Python parity test), and so doespython -m pytest -q(69).Deliberately not changed
results.jsonwithtoFixed; it doesn't emit or compare it.config.jsonand the traces. They were already byte-identical across these toolchains. The traces also have to stay at full precision:test_shipped_traces_are_the_python_scenariosrequires exact array equality, and the dashboard's parity readout compares against theirreference_metrics.🤖 Generated with Claude Code