Skip to content

remove_identity() conflates idle stripping with unitary identity removal, silently dropping p1 fault sites #445

Description

@ciaranra

Splitting remove_identity() into a unitary-optimization pass and an idle-stripping pass. They are different concerns with different consequences, and the DEM currently uses the combined one to mean only the second.

What it removes today

is_identity_gate (crates/pecos-quantum/src/pass.rs:299):

match gate.gate_type {
    GateType::I | GateType::Idle => true,
    gt if is_rotation(gt) => gate.angles.len() == 1 && gate.angles[0].is_zero(),
    _ => false,
}

So it strips I, Idle, and zero-angle RX/RY/RZ/RXX/RYY/RZZ/CRZ — everything that is identity by effect.

Why conflating them matters

These are not the same kind of thing:

removed what it is consequence
Idle a duration-carrying scheduling marker that drives idle noise removing it removes idle noise — the intended effect
I, zero-angle rotations no-op unitaries removing them removes their gate noise — an unintended side effect

RZ is a p1 fault site in the DEM builder (crates/pecos-qec/src/fault_tolerance/dem_builder/mem_builder.rs:130), so a zero-angle RZ still carries single-qubit gate noise. Deleting it deletes that fault mechanism.

The DEM builder calls remove_identity() to implement strip_traced_idles, whose documented purpose is only to avoid double-counting idle noise (python/quantum-pecos/src/pecos/qec/dem.py:311). So a noise-modelling decision is silently also performing a gate-noise-removing optimization.

Frequency: latent, not currently biting

Measured on traced circuits:

program gates remove_identity dropped
plain Clifford (H, CX, measure) 11 0
repetition-code memory 30 0
explicit rz(angle(0.0)) 12 1

Lowering does not manufacture zero-angle rotations — traced circuits contain RZ, R1XY, RZZ with nonzero angles. A zero-angle rotation appears only if the program contains one.

That makes this latent today, but reachable: a parameterised program whose angle evaluates to zero for some setting is an ordinary thing to write, and a sweep is exactly where a silently-missing fault site would be hardest to notice.

Proposal

Split the pass:

  • remove_identity() — unitary identity gates only (I, zero-angle rotations). A genuine optimization, semantically neutral in an ideal circuit; callers opting into it accept losing those gates' noise sites.
  • strip_idles()Idle gates only. A noise-modelling decision.

Then have the DEM builder's strip_traced_idles call strip_idles(), which is what it actually means.

Acceptance

  • A traced circuit containing a zero-angle rotation keeps its p1 fault mechanism after strip_idles(), and loses it after remove_identity().
  • strip_traced_idles=True no longer changes the emitted DEM for a circuit whose only identity-like gates are zero-angle rotations.
  • Existing behaviour for circuits with no zero-angle rotations is byte-identical — which per the table above is every traced circuit measured so far.

Found while documenting the workflow example in PR #420. Not fixed there: it changes a pass's semantics and would move DEM numbers for affected circuits, and that PR already carries three number-moving changes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingrustPull requests that update rust codeseverity:mediumDegraded or awkward behavior with a workaround

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions