Skip to content

GeneralizedTableauSum(sum_cutoff=0.0) panics on sampler() for any multi-branch channel #206

Description

@Roger-luo

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_assert means release wheels don't hit this, so it affects development and maturin develop installs. 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 against 1.0 - max(sum_cutoff, k * f64::EPSILON) for a small k, 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: PPVMArea: Pauli Propagation VM related issues.category: bugCategory: this is a bug or something isn't working as expected.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions