Skip to content

Preserve exact Pauli measurement bases via AxisMeasBasis (reduce float-based angle logic) #147

@masa10-f

Description

@masa10-f

Background / Motivation

graphqomb currently provides two measurement-basis representations:

  • PlannerMeasBasis(plane, angle: float)
  • AxisMeasBasis(axis, sign) (exact Pauli ±X/±Y/±Z)

Internally, most code treats bases as planner-like (uses plane/angle), and Pauli detection depends on floating-point closeness checks (e.g. is_close_angle, is_clifford_angle). This reduces the main advantage of AxisMeasBasis: representing Pauli measurements exactly without numerical tolerance issues.

Notable examples:

  • Float-based Pauli detection: determine_pauli_axis in graphqomb/common.py
  • Local Clifford update is numeric and always returns PlannerMeasBasis: update_lc_basis in graphqomb/euler.py
  • GraphState Clifford application uses update_lc_basis: apply_local_clifford in graphqomb/graphstate.py
  • Downstream components rely on Pauli detection (stim_compiler, pauli_simplification, PauliFrame caching)

Goal

When a measurement basis is (or becomes) a Pauli basis, represent and preserve it as AxisMeasBasis end-to-end, avoiding float-based angle comparisons in that regime.

Proposal

  1. Add an explicit “Pauli basis query” API to MeasBasis

    • e.g. pauli_basis() -> AxisMeasBasis | None
    • AxisMeasBasis.pauli_basis() returns self
    • PlannerMeasBasis.pauli_basis() returns an AxisMeasBasis only if it is Pauli (tolerance only at this boundary is OK)
  2. Prefer discrete Pauli info in consumers

    • Update determine_pauli_axis to short-circuit for AxisMeasBasis (or to use basis.pauli_basis() first)
    • Update call sites to rely on this path where possible (stim_compiler, feedforward, visualizer, PauliFrame caching)
  3. Implement an algebraic update path for Pauli bases under Local Cliffords

    • Extend update_lc_basis(lc, basis) so that if basis is Pauli (AxisMeasBasis), it returns an AxisMeasBasis computed by a discrete Clifford action table (permutation/sign on {X,Y,Z})
    • Keep existing numeric fallback for non-Pauli bases
    • (Optional follow-up) represent LocalClifford itself in a discrete form (24-element group id or angles mod π/2) to eliminate float drift entirely for Clifford-only workflows
  4. “Snap to Axis” normalization at key boundaries

    • After numeric updates that yield a Pauli basis, convert to AxisMeasBasis and store that instead of PlannerMeasBasis
    • Apply this in GraphState.apply_local_clifford and Local Clifford expansion paths (_expand_input_local_cliffords, _expand_output_local_cliffords) when created angles correspond to Pauli axes

Acceptance Criteria

  • determine_pauli_axis(AxisMeasBasis(...)) returns the correct axis without float comparisons.
  • update_lc_basis(LocalClifford, AxisMeasBasis) returns AxisMeasBasis and matches existing numeric behavior for all Pauli bases and all local Cliffords.
  • Pauli measurements do not “drift” into PlannerMeasBasis after repeated Clifford updates/expansions.
  • stim_compile does not fail due to missed Pauli detection caused by float error.
  • Tests cover:
    • Local Clifford action on Pauli bases (all 24 Cliffords × 6 Pauli eigenbases, or equivalent coverage)
    • Regression for “snap to axis” behavior across GraphState transformations

Notes / Risks

  • Clarify the semantics of AxisMeasBasis.sign vs flip() and downstream classical post-processing, to ensure normalization does not change meaning.
  • Any tolerance use should be confined to recognizing a PlannerMeasBasis as Pauli; once converted, operations should remain discrete.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions