Skip to content

Store an Idle command's duration as time units instead of a rotation angle - #729

Open
ciaranra wants to merge 2 commits into
devfrom
idle-duration-not-angle
Open

Store an Idle command's duration as time units instead of a rotation angle#729
ciaranra wants to merge 2 commits into
devfrom
idle-duration-not-angle

Conversation

@ciaranra

@ciaranra ciaranra commented Sep 6, 2026

Copy link
Copy Markdown
Member

Closes #727.

Problem

GateCommand in pecos-neo had only an angles field, so GateCommand::idle stored an idle gate's duration there by reinterpreting Angle64's u64 storage as a count of time units. The construction site said so outright. Every consumer that read angles therefore treated an idle duration as a rotation angle.

pecos-core already models this correctly and is unchanged here: GateType::Idle.angle_arity() is 0, and Gate::idle builds with empty angles and the duration in params. Only pecos-neo disagreed.

The visible consequence was in snap_command_queue, which snapped the duration to the nearest representable rotation angle: an idle of 23 time units became 0.

Change

pub struct GateCommand {
    pub gate_type: GateType,
    pub qubits: SmallVec<[QubitId; 4]>,
    pub payload: GatePayload,
}

pub enum GatePayload {
    Angles(SmallVec<[Angle64; 2]>),
    Duration(TimeUnits),
}

angles() returns &[] for a duration payload, so an Idle command genuinely has no angles and angle consumers need no special case for it. The duration is a TimeUnits throughout, exact across the full u64 range. new, with_angles, idle and get_idle_duration keep their signatures, so callers do not churn.

The consumers in queue_validation.rs change only from cmd.angles to cmd.angles(). Nothing there special-cases Idle, which is the point: the distinction now lives in the type rather than in each reader's memory.

pecos-core and the wire format are untouched. ByteMessage already derives angle count from gate type and writes non-angle parameters separately, and GateCommand has no serde implementation, so no encoding changes.

Incidental fix

sim_neo_bindings.rs contained a hand-rolled copy of the command-to-gate conversion that built a core Gate with params: GateParams::new() while copying the command's angles across. For an Idle command that wrote the duration into the angle slot of a gate whose duration belongs in params, so the experimental Python bridge was producing malformed Idle gates and dropping durations. It now calls the shared command_queue_to_gates.

Verification

  • Release and debug: 5,133 tests and doctests each, zero failures, six pre-existing ignored.
  • cargo clippy --locked --workspace --all-targets --all-features -- -D warnings: clean.
  • cargo fmt --all -- --check: clean.
  • Execution evidence: an idle of 23 snapped to 0 before this change and survives as 23 after. Durations of 0, 1, 23, 2^53, 2^53 + 1 and u64::MAX survive every snapping policy and report zero angles.
  • Eight mutations were introduced and each was caught, covering duration rounding through f64, exposing a duration as an angle, dropping the duration during snapping, and dropping it on import, export and dispatch.

Converting a GateCommand into a core Gate still passes through TimeUnits::as_f64(), so durations above 2^53 lose precision at that boundary. That limit is pre-existing, follows from Gate.params being f64, and is entangled with the f64-based wire format, so it is deliberately left alone here.

Relationship to #701

#701 enforces gate angle arity. On its branch, pecos-neo special-cased Idle to report an angle arity of 1 so that the duration-in-angles storage would pass its own new validation, which is what made an idle duration visible to angle consumers as a rotation angle. With the storage corrected here, that special case is unnecessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Idle stores its duration in the rotation-angle slot

1 participant