Align idle-noise vocabulary across DEM, engines and neo, and emit the requested Pauli channel - #420
Draft
ciaranra wants to merge 48 commits into
Draft
Align idle-noise vocabulary across DEM, engines and neo, and emit the requested Pauli channel#420ciaranra wants to merge 48 commits into
ciaranra wants to merge 48 commits into
Conversation
…pipeline, with user-guide coverage
…/_model, p_idle_quadratic, p_idle_coherent)
…clobbers; correct coherent-branch docs
…n of coherent idle noise in the standard DEM builder
This was referenced Aug 3, 2026
…twirl handoff test off deprecated aliases
… rates with idle noise
…tead of the surface-code generator
…r and split the workflow guide into stages
ciaranra
commented
Aug 4, 2026
ciaranra
commented
Aug 4, 2026
ciaranra
commented
Aug 4, 2026
ciaranra
commented
Aug 4, 2026
|
|
||
| pymatching = PyMatchingDecoder.from_dem(terminal_graphlike_text) | ||
| tesseract = TesseractDecoder.from_dem(source_graphlike_text, preset="fast") | ||
| bp_osd = DemAwareDecoder.from_dem(raw_text, decoder_type="bp_osd") |
Member
Author
There was a problem hiding this comment.
We should have a BpOsdDecoder or a LdpcDecoder...
ciaranra
commented
Aug 4, 2026
|
|
||
| pymatching_errors += pymatching.decode(syndrome).correction[0] != actual | ||
| tesseract_errors += (tesseract.decode_syndrome(syndrome).observables_mask & 1) != actual | ||
| bp_osd_errors += (bp_osd.decode_syndrome(syndrome).observables_mask & 1) != actual |
Member
Author
There was a problem hiding this comment.
We should think about having a nice convenience function for each of these decoders that looks nice/same name...
ciaranra
commented
Aug 4, 2026
ciaranra
commented
Aug 4, 2026
ciaranra
commented
Aug 4, 2026
# Conflicts: # examples/surface_code_experiments.ipynb # python/quantum-pecos/tests/qec/test_from_guppy_dem.py
…setters to field names
…herent dephasing twirl
…ne-law unit conversion
…r the legacy preset
…e mode-switch bool
ciaranra
commented
Aug 5, 2026
| .with_p_idle_linear_model({"X": 0.25, "Y": 0.25, "Z": 0.5}) | ||
| .with_p_idle_coherent(False) | ||
| .with_p_idle_quadratic_rate(0.03 / math.pi) | ||
| .with_idle_after_2q(1.0) |
Member
Author
There was a problem hiding this comment.
do the general_noise().with_idle_after_2q(...) ignore other idles by default like the DEM builder does... should we have and option to not ignore other idles?
ciaranra
commented
Aug 5, 2026
| ) | ||
|
|
||
| dem_build = ( | ||
| DetectorErrorModel.builder() |
Member
Author
There was a problem hiding this comment.
does the builder have .runtime(...) that accepts Selene-shaped runtime plugins?
ciaranra
commented
Aug 5, 2026
| .with_p_prep(0.001) | ||
| .with_p_meas(0.001) | ||
| .with_single_qubit_probability(0.0005) | ||
| .with_two_qubit_probability(0.002) |
Member
Author
There was a problem hiding this comment.
this should be using with_p1(...), with_p2(...) etc...
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.
Summary
Started as idle-gate handling for the Guppy-to-DEM pipeline and grew into an alignment of the
idle-noise vocabulary across
NoiseParameters(DEM),general_noise()(engines) andexp/pecos-neo, plus a correction to what the DEM actually emits.The two halves:
every surface, with no unit conversion and no mode flags.
channel; a DEM is a set of independent mechanisms. Those are two unravelings of the same
map, and the builder was not converting between them.
The workflow example is the short version — its DEM block and its simulator block are now
character-identical apart from the constructor name:
Behaviour changes reviewers should look at first
These change results or break calls. Nothing here is a silent no-op fix; each one moves numbers
or requires a call-site edit.
general_noise()is noiseless by defaultp1,p2,p_prep,p_meas_*, idle, emission/seepage ratios)..auto()restores that preset verbatim. Anything relying on implicit noise now gets silence.6.666667e-04→6.671117e-04; 2q1.333333e-03→1.345946e-03. The 2q case was ~0.93% of the two-qubit error weight.sin²(θ), nowsin²(θ/2)— the exact Pauli twirl of its own coherentRZ(θ). Changes results for anyone using incoherent quadratic idle in neo.with_p1_probability→with_p1,with_meas_probability→with_p_meas, etc. Hard rename, no aliases.with_p2_idle(probability)→with_idle_after_2q(duration)with_p2_idle(0.01)with no linear rate now produces nothing; the equivalent iswith_p_idle_linear_rate(0.01).with_idle_after_2q(1.0).with_p_idle_coherent(bool)→with_p_idle_quadratic_coherent(bool)with_p_idle_coherentis now a family setter taking(rate, model)over RX/RY/RZ.simulation.rsdiscarded the configuredNoiseResourcebefore it reached the runner, so importance sampling through the high-level API applied no noise model at all. Results were near-zero failures with meaningless weights._ => {}(#436); non-Clifford gates were skipped without a rotation executor (#437). Now: executed where supported, rejected at configuration time with an actionable message, or panic as a broken invariant.with_p_idle_linear_rate,with_p_idle_linear_model,with_p_idle_quadratic_rate,with_p_idle_quadratic_coherent,with_p_idle_coherent_to_incoherent_factor, and bothwith_average_p_idle_*are gone (#424 ramp steps 2-3). The families replace them. The quadratic conversion is not 1:1:with_p_idle_quadratic_rate(r)becomeswith_p_idle_sin_squared(r * PI, {"Z": 1.0}), because the legacy rate was cycles-per-time and was scaled byfactor/2 * 2*PIinternally. Copying the number across without thePIgives a channel wrong by that factor.from_t1_t2/with_idle_t1_t2now use the first-order Pauli twirl withT2as total transverse coherence:rX = rY = 1/(4*T1),rZ = 1/(2*T2) - 1/(4*T1), quadratic zero.T2 > 2*T1is now rejected rather than silently clamped, and short times are no longer clamped to 1. This moves numbers for every T1/T2 user; a documented example was silently producing maximal noise before (#438).NoiseConfig's nine per-axisf64fields collapse into oneIdleNoiseFamily { rate, weights }instantiated three times, mirroringpecos-engines' sampler shape. An unset map means symmetric X/Y/Z — a property nine flat fields could not have. Emitted DEM text is byte-identical; this is a representation change, not a physics change.with_p_idle_{,x_,y_,z_}{linear,quadratic,quadratic_sine}_ratesetters are gone; the three families are the only public idle API.NoiseParameters(p_idle_z_linear_rate=...)now raisesTypeError. Two of the removed names collided with livegeneral_noise()setters meaning different physics. Replacement iswith_p_idle_linear(r, {"Z": 1.0})etc.; DEM fixtures pin that this reproduces the old output byte-for-byte.with_p_idle_linear(rate)→(rate, model)with_p_idle_linear_weightsremoved.exp/crate, deliberate.Idle-noise vocabulary
Three families on every surface, each named for its own law, each a rate plus a model:
with_p_idle_linearp = rate·m·twith_p_idle_sin_squaredp = sin²(rate·m·t)with_p_idle_coherentangle = rate·m·tRates are radians per time unit throughout — engines'
2πandcoherent_to_incoherent_factor/2conversions apply only to the legacy
with_p_idle_quadratic_ratepath, which is unchanged.Mixing a legacy spelling with its family equivalent is a loud build-time error; those two
differ by
1.5πat the old default factor, so silent precedence between them was the bug worthpreventing.
with_idle_after_2q(duration)places idle sites on each two-qubit gate operand, matching theDEM pass, on both engines and neo.
DEM channel conversion
Independent mechanisms compose — two firing together produce a third error — so writing the
channel's per-Pauli probabilities straight in understates every Pauli. The conversion is exact
and closed-form via the Pauli-channel characters, and runs on propagated flip-signatures
rather than Pauli labels, since a DEM mechanism is
(probability, flip-set)with no Pauliattached. At that layer undetectable Paulis drop and equal signatures merge by addition, which
collapses most apparent infeasibilities: a 2-qubit gate's 15 Paulis typically become 3
mechanisms.
Exact representability holds iff
pY ≥ pX·pZand cyclically. Realistic noise clears this byorders of magnitude; only an exactly-Y-free channel takes a residual, bounded by
pX·pZ,reported as a total-variation distance on
dem.idle_noise_residualsand warned on at build.Two pre-existing defects surfaced and are fixed: equal signatures were merged with
combine_probabilities(the independent rule) rather than summed, undercounting mutuallyexclusive Paulis by
2·p₁·p₂; and after-2q idle in neo fired only when the gate's Pauli erroralso fired.
Prep and measurement are single Bernoulli events and are deliberately not converted — a
test pins that they stay exact.
Controlling the approximation warning
When a channel is not exactly representable, the builder emits the closest non-negative fit and
warns.
.with_residual_warning_threshold(fraction)sets the tolerance below which that warningis not worth raising, as a fraction of the channel's total error weight — so one value means
the same thing across a sweep, which an absolute probability cannot. Default
0.0warns on anyresidual; values outside
[0, 1]are rejected, and a value above1.0says explicitly that thesetter is relative, since that is the likely misreading.
The threshold gates the warning only.
dem.idle_noise_residualsand the audit entry stay fullypopulated whatever it is set to, so a quieted approximation remains provable after the fact.
Residuals now also carry
channel_weightandrelative_magnitude.Suppressing warnings wholesale remains
warnings.filterwarnings' job; this setter encodes aphysics tolerance, not log-noise management.
Docs
docs/workflows/guppy-dem-decoding.md— five-stage worked example, hand-written repetitioncode, DEM sampling and
sim()sampling, Tesseract / BP+OSD / PyMatching.docs/user-guide/dem-from-guppy.md— idle-noise families, stim export, validity domain.pecos-docs/design/neo-noise-event-emission.md, including the review panelthat rejected adding event emission to
NoiseResponse.Issues resolved
Closes #436 — importance runner silently dropped every injected noise gate except X/Y/Z.
Closes #437 — non-Clifford gates silently skipped when no rotation executor is configured.
Closes #433 —
NoiseModelrenamed toNoiseParameters, lifted topecos, fluent setters.Closes #428 — DEM construction reshaped into the house builder pattern.
Closes #430 —
DemAwareDecoderno longer wraps observable bits past 64.Closes #438 — the T1/T2 idle derivation is dimensionally consistent and no longer clamps.
Closes #424 — ramp steps 2 and 3; the legacy idle spellings are removed.
Still open
dev, not a regression from this PR: the stalling ops live incrates/pecos-hugr, which this branch does not touch, andqec-guppy.mdis byte-identical todev. Still unisolated; needs its own bisect.translating to the family form at the Rust boundary. Unreachable from the public API, so this
is an implementation detail rather than an omission.