Repository navigation
Description
Summary
GeneralizedTableauSum with sum_cutoff=0.0 panics when you build a sampler, for any channel that produces three or more branches. The branch weights sum to 0.9999999999999999 in f64, and the normalization debug_assert uses a strict >= against 1 - sum_cutoff, which at sum_cutoff == 0 leaves no room for rounding.
Reachable from Python, and it surfaces as a PanicException rather than a Python-level error.
Reproduction
from ppvm.generalized_tableau_sum import GeneralizedTableauSum
t = GeneralizedTableauSum(n_qubits=2, sum_cutoff=0.0, seed=1)
t.depolarize1(0, 0.3)
t.sampler()thread '<unnamed>' panicked at crates/ppvm-tableau-sum/src/data.rs:136:9:
Normalization error in sum
PanicException: Normalization error in sum
Scope
Any channel whose branch weights don't sum to exactly 1.0 in binary floating point. Two-branch splits are fine because the halves are exact:
| operation | sum_cutoff=0.0 |
|---|---|
depolarize1(0, 0.3) |
panics (p/3 is not exact) |
pauli_error(0, [0.1, 0.2, 0.3]) |
panics |
correlated_loss_channel(0, 1, [0.1, 0.2, 0.0]) |
panics |
loss_channel(0, 0.1) |
ok |
x_error(0, 0.1) |
ok |
Cause
crates/ppvm-tableau-sum/src/data.rs:136:
debug_assert!(
*p_cum.last().unwrap_or(&T::Coeff::zero()) >= T::Coeff::one() - self.sum_cutoff.clone(),
"Normalization error in sum"
);sum_cutoff doubles as both the truncation threshold and the normalization tolerance. That coupling is fine while sum_cutoff is comfortably larger than accumulated f64 error, but at 0.0 the assert demands the cumulative mass be exactly >= 1.0, which a sum of three or more f64 weights generally is not.
Notes
- The
debug_assertmeans release wheels don't hit this, so it affects development andmaturin developinstalls. The invariant it's checking is still worth checking — the issue is the tolerance, not the assert. - Convention-independent: I verified it reproduces with weights from both the old and new correlated-loss conventions, and with channels that have nothing to do with loss.
- A plausible fix is to separate the normalization tolerance from
sum_cutoff— compare against1.0 - max(sum_cutoff, k * f64::EPSILON)for a smallk, or scale the slack with the branch count. - Found while building a cross-backend verification harness for fix(tableau): correlated-loss convention, duplicate batch targets, CZ-block overlap #205; unrelated to that PR's changes.
🤖 Generated with Claude Code
Activity