The parity gate¶
Of the gates, gate 13 is the one unusual enough to need its own page: it asserts that the Rust and Python arms of every paired example agree to the bit.
What it asserts. Each pair listed in examples/parity.toml has a Rust arm
and a Python arm. Both print a canonical atune-parity/1 block at the end of the
run — study, seed, trial count, sampler, and the winning trial's number, value
and parameters. The gate runs both and requires the blocks to match: the header
byte-equal, the same keys in ascending order, string fields byte-equal, and float
fields equal as raw f64 bits after parsing. That is atune's determinism
contract, asserted across the binding boundary.
How to run it.
cargo dev check-parityruns both arms of every pair. It needs the wheel built, so runmaturin develop --release -m crates/atune_py/Cargo.tomlfirst.cargo dev check-parity --rust-goldenruns only the Rust arms and compares each against a committed golden file. No Python needed, which is why this half runs in the fast per-push gate.cargo dev run-examplesruns every example, paired or not.
When it reddens. The failure names the pair, the first differing key, and both values with their bit patterns. Work through these in order:
- You changed one arm of a pair. Change the other. The two programs are
idiomatic in their own language but arithmetically identical: same operation
order, and no fused multiply-add on either side, because
f64::mul_addhas no pre-3.13 Python counterpart and the fused and unfused spellings agree only for particular inputs. - You changed a sampler, scheduler or seed derivation on purpose. Then every
pair's numbers move together, and the Rust goldens are stale by design.
Re-bless them with
ATUNE_BLESS_PARITY=1 cargo dev check-parity --rust-goldenand read the diff — that file is the record of the change in behaviour. Never bless without reading it. - Only
best.valuediffers, in the last bits, and you changed neither arm. Check that both arms ran on the same machine.f64::cosandmath.cosboth defer to the platform's C library, which is not required to be correctly rounded, so a cross-machine comparison is a statement about libm rather than about atune. The goldens name the platform they were blessed on. - The trial count differs. Something is history-dependent and running in parallel. A parity example that uses a history-dependent sampler or scheduler must run with a parallelism of one: the interleaving decides the history, and the history decides the answer.
- Neither arm changed and neither of the above applies. That is a real determinism bug, and it is the outcome the gate exists for. Do not paper over it.
What you may not do. Do not add a tolerance to make a comparison pass. Do not
delete a pair to make the gate quiet: every example under examples/ must appear
in examples/parity.toml, either as a [[pair]] or as an [[unpaired]] entry
with a reason, and a file in neither is a parse error. If an objective
genuinely cannot be spelled identically in both languages, the pair downgrades to
compare = "params-only" — parameters and trial number still bit-equal, the value
to a declared tolerance — and that mode requires a reason too, which is rendered
on the example's own page.