galata. GitHub ↗

ADR-0004: Determinism policy, and what it does not cover

Context

Charter rule 6 says determinism is a tested property, not an aspiration. A tool whose output changes between runs cannot support a regression suite, cannot support a Monte Carlo study whose worst case is meant to be re-examinable, and cannot support the claim that a number is reproducible from the CLI.

"Deterministic" is also a word that gets over-claimed. It is worth stating precisely what is guaranteed, what is not, and why — because a badge that implies more than it delivers is worse than no badge.

Decision

The guarantee, in two tiers

Tier 1 — same binary, same platform: bit-identical. Running the same computation twice with the same inputs on the same machine and binary produces identical bits. This is gated two ways, both on Linux, macOS and Windows. tests/determinism/ re-runs each battery in process and compares the resulting doubles with EXPECT_EQ — an exact comparison, not a tolerance. scripts/check-determinism.sh runs tools/determinism twice and diff -us the two files, which is the byte comparison; the tool prints at %.17g, which round-trips a double exactly, so byte-identical output means bit-identical values rather than values that merely print the same.

There is no test that runs a pipeline twice and diffs its output files. That would be the stronger check, because it would cover the report writers as well as the numerics, and nothing currently gates those.

Tier 2 — same source, different platform: agreement to a published bound. Output produced on Linux, macOS and Windows agrees to a documented tolerance — 1e-9 relative, between every pair of platforms — with one carve-out, which this record failed to state until now.

Values downstream of the central-difference Jacobian are excluded from the cross-platform comparison altogether: scripts/compare-determinism.sh drops every fingerprint key prefixed tier1. before it compares anything. Dividing by the perturbation h amplifies a libm disagreement by 1/h, which on a small matrix entry reaches about 1e-8 relative through nobody's error — past this gate. Those values are held byte-identical within a platform by tier 1 instead, which is the stronger claim; the rest is what the 1e-9 bound covers. The split is 47 excluded of 145 fingerprinted values as this is written, and docs/VERIFICATION.md reports both counts from the run rather than from here.

The observed deviation is printed by every comparison rather than merely bounded, and it is read from the workflow log. It is deliberately not restated in docs/VERIFICATION.md: determinism.yml holds contents: read and writes nothing back to the repository, so a figure copied in by hand would be a figure that drifts. That is the report's own reasoning and this record follows it; an earlier version of this record claimed the opposite.

Tier 2 is not bit-identity, and the reason is worth being blunt about.

Why Tier 2 is not bit-identity

sqrt is required by IEEE 754 to be correctly rounded, so it produces identical bits everywhere. Nothing else transcendental does. sin, cos, tan, asin, atan2, exp, log and pow come from the platform's math library — glibc on Linux, Apple's libm on macOS, the UCRT on Windows — and those implementations are not correctly rounded, do not agree with each other in the final bits, and change between versions of the same library.

galata cannot avoid these functions. Angle of attack is an atan2. The atmosphere's pressure profile is a pow. Every rotation is a sin and a cos. Any claim of cross-platform bit-identity would therefore be either false or would require shipping a correctly-rounded libm, which is a serious project in its own right and is not one this tool is undertaking in 1.x.

So the honest claim is the two-tier one, and the badge says "determinism" linked to the document that defines it.

What produces Tier 1

This rests on a convention rather than on a library, which is why it needed a gate more than any other item here: two runs on a German machine would be equally wrong and equally identical, so tier 1 alone would pass. scripts/check-determinism.sh therefore runs the fingerprint a third time with a comma-decimal locale in the environment and requires the bytes not to move. It passes because galata ignores the environment, which is exactly the claim; a setlocale(LC_ALL, "") introduced anywhere in the library turns every decimal point into a comma and fails it.

An earlier version of this record credited fmt with delivering this. It never did — it was declared in vcpkg.json and linked by nothing — and the dependency has since been dropped rather than wired, because putting an unpinned library's undocumented float formatting underneath every committed artefact is the opposite of what pinning Eigen is for.

What breaks the guarantee, stated so nobody is surprised

Alternatives considered

Claim bit-identity across platforms and enforce it. This is what the reader wants to be true. It would require a correctly-rounded implementation of every transcendental galata uses — the crlibm / RLIBM line of work shows it is possible — and would make the tool substantially slower. Rejected for 1.x on cost, and recorded here so the option is not lost. If it is ever done, Tier 2 collapses into Tier 1 and this ADR is superseded rather than amended.

Gate cross-platform agreement on a hash of rounded output — round every number to, say, 9 significant figures and hash that. Attractive because it looks like bit-identity and is easy to implement. Rejected: it converts a smooth tolerance into a cliff, so a result sitting near a rounding boundary flaps between pass and fail with no physical change. A tolerance with the observed deviation published is honest; a hash of rounded values only looks stricter.

Use /fp:strict on MSVC. It additionally preserves exception semantics and rounding-mode changes. galata never manipulates the floating-point environment, so it buys nothing over /fp:precise and costs measurable performance.

Consequences

Revisit when

A correctly-rounded math library becomes a practical dependency, or the project introduces parallelism into any gated numerical path.