Skip to content

Fix reset-window and final-round data-qubit priors in precompute_dem#87

Merged
ivanbasov merged 4 commits into
mainfrom
fix/reset-window-and-final-round-priors
Jul 10, 2026
Merged

Fix reset-window and final-round data-qubit priors in precompute_dem#87
ivanbasov merged 4 commits into
mainfrom
fix/reset-window-and-final-round-priors

Conversation

@ivanbasov

Copy link
Copy Markdown
Member

Summary

Follow-up to #85 (see the review discussion there). Auditing build_single_p_marginal with the same isolated-channel Stim methodology used to validate #85 surfaced two more circuit-vs-sampler mismatches in the explicit 25-parameter noise-model path. Both predate #85 and were invisible to its validation only because the affected channels were zeroed in that isolation.

1. Reset-window data idles were overcounted with p_idle_cnot_*

In NoiseModel mode the circuit applies no idle noise to data qubits during the ancilla prep/reset window — that idle is deliberately folded into the measurement-window SPAM idle (MemoryCircuit: "IGNORE data-idle during ancilla prep/reset"; the config calibration p_idle_spam = 2p/3 − 4p²/9 reflects the combined window). But the marginal builder let intermediate-round data locations at tt == 0 fall through to the bulk-idle branch and assigned p_idle_cnot_*.

Isolating p_idle_cnot_X/Y/Z = 0.002 (d=3, r=4, X basis, 400k shots), per-round mean syndrome densities:

Round Sampler (before) Stim Sampler (after)
0 0.01087 0.01079 0.01087
1 0.03291 0.02165 0.02171
2 0.03294 0.02177 0.02168
3 0.01092 0.01102 0.01095

With the full public 25-parameter values (r13 config), intermediate-round syndrome densities were ~10% hotter than Stim (0.1020 vs 0.0924).

Fix: intermediate-round data locations at tt == 0 get probability 0. Boundary rounds are unaffected (their tt == 0 data locations are consumed by is_data_prep).

2. Final-round data injection used the preparation rates

The circuit represents noisy data readout as a fake data-measurement flip injected at the start of the final perfect round, at the measurement rates: Z_ERROR(p_meas_X) for X-basis readout, X_ERROR(p_meas_Z) for Z-basis. The sampler's is_data_prep branch assigned p_prep_X/Z instead (its is_final_round split had identical arms — this change makes the split carry the intended distinction).

All shipped configs use p_prep == p_meas, so this cancels numerically today, but it breaks as soon as the two families are calibrated separately. Isolating the channels makes the mirror image visible in the final round (d=3, r=4, 400k shots): with only p_meas_* = 0.004, Stim shows 0.0099 vs the sampler's 0.0040; with only p_prep_* = 0.004, the sampler showed 0.0099 vs Stim's 0.0040. After the fix both isolations agree with Stim.

Tests

Extends test_precompute_dem_probabilities.py (added in #85):

  • Unit tests asserting the exact prior at both locations (reset-window zeros, final-round p_meas_* with the basis-correct Pauli), plus a guard that round-0 data preparation keeps p_prep_*. The two new location tests fail on main with the expected signatures.
  • A seeded, deterministic end-to-end guard (TestSamplerMatchesStimSyndromeDensities): per-round syndrome densities from Bernoulli(p) errors pushed through the precomputed H must match direct Stim sampling of the same MemoryCircuit within 5 standard errors, for cnot-idle-only, measurement-only, and full-25-parameter noise. Runs in ~1 s at d=3/r=4 (CPU, no cuStabilizer needed), so it sits in the short tier and will catch any future circuit-vs-sampler prior drift.

Impact

Only the explicit-NoiseModel path of build_single_p_marginal is touched; the legacy scalar path is untouched (its circuit emits a genuine reset-window idle, which it already accounts for). Training data generated after this change has intermediate-round syndrome densities ~10% lower than before, now matching the Stim reference; models trained on previously generated bundles are unaffected functionally but were trained against slightly hotter priors.

Two circuit-vs-sampler mismatches in build_single_p_marginal, both in the
explicit 25-parameter noise model path:

1. Data qubits idling during the ancilla prep/reset window (tt == 0,
   intermediate rounds) were assigned p_idle_cnot_*, but the circuit emits
   no noise there: that idle is folded into the measurement-window SPAM
   idle. Assign 0 instead.

2. The final-round data injection was assigned p_prep_*, but the circuit
   represents noisy data readout as a fake data-measurement flip at
   p_meas_* (Z_ERROR(p_meas_X) for X-basis readout, X_ERROR(p_meas_Z) for
   Z-basis). Use the measurement rates.

Adds unit tests for both locations plus a seeded end-to-end guard that
compares per-round syndrome densities from Bernoulli(p) errors pushed
through H against direct Stim sampling of the same MemoryCircuit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ivanbasov ivanbasov requested a review from jolle-ag July 9, 2026 20:22
@ivanbasov

Copy link
Copy Markdown
Member Author

@jolle-ag one focused question for review, since this pins down the intended noise-model semantics: is the Stim circuit the reference on both counts — (1) the data-qubit reset-window idle carries no separate noise location because it is folded into the measurement-window SPAM idle (consistent with the p_idle_spam = 2p/3 − 4p²/9 calibration in the configs), and (2) the final-round data injection is a fake data-measurement flip at p_meas_X/p_meas_Z? This PR makes the precomputed priors match the circuit on both; if either circuit behavior itself doesn't reflect the intended physical model, the fix belongs on the circuit side instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ivanbasov ivanbasov requested a review from bmhowe23 July 9, 2026 21:16

@bmhowe23 bmhowe23 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. It would be good to get Jan's answer to your question before merging.

@jolle-ag

Copy link
Copy Markdown
Collaborator

Thanks for catching this. What you spotted and how you fixed it is the intended behavior.

@ivanbasov ivanbasov merged commit 9668bf0 into main Jul 10, 2026
17 checks passed
@ivanbasov ivanbasov deleted the fix/reset-window-and-final-round-priors branch July 10, 2026 15:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants