From 586f21a66f76adee46fbbf15717a2e82f14056bc Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 1 Oct 2026 12:18:58 +0000 Subject: [PATCH] [qec] Share Stim QUBIT_COORDS emission in the emitter module Both builders call stim_emit::emit_qubit_coords. Lattice surgery still walks blocks in workload order. Measure lines and closing detectors stay in the builders. Checked-in Stim gold stays byte-identical. Refs #473 Co-authored-by: Arnab Ghosh --- quon_qec/src/experiment.rs | 8 ++---- quon_qec/src/experiment/stim_emit.rs | 37 +++++++++++++++++++++++----- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/quon_qec/src/experiment.rs b/quon_qec/src/experiment.rs index c9e93cd..255e6e0 100644 --- a/quon_qec/src/experiment.rs +++ b/quon_qec/src/experiment.rs @@ -486,9 +486,7 @@ fn emit_stim_single_block_memory(expanded: &ExpandedWorkload) -> Result Result = expanded diff --git a/quon_qec/src/experiment/stim_emit.rs b/quon_qec/src/experiment/stim_emit.rs index 2d8166a..b3e69f0 100644 --- a/quon_qec/src/experiment/stim_emit.rs +++ b/quon_qec/src/experiment/stim_emit.rs @@ -10,16 +10,17 @@ //! kind is a new impl plus one arm in [`lattice_round_emitter`]. //! //! Both builders share [`emit_reset_tick`] for the prepare-`R` / `TICK` line, -//! [`emit_single_block_header`] / [`emit_lattice_surgery_header`] for the -//! comment preamble, and [`emit_observable_include`] for `OBSERVABLE_INCLUDE` -//! record offsets. `QUBIT_COORDS` stay in the builders. Lattice surgery still -//! appends frame-byproduct records after the shared observable line. +//! [`emit_qubit_coords`] for `QUBIT_COORDS`, [`emit_single_block_header`] / +//! [`emit_lattice_surgery_header`] for the comment preamble, and +//! [`emit_observable_include`] for `OBSERVABLE_INCLUDE` record offsets. +//! Measure lines and closing detectors stay in the builders. Lattice surgery +//! still appends frame-byproduct records after the shared observable line. use std::collections::HashMap; use crate::expand::{ - ExpandedBlock, ExpandedWorkload, MergeBoundary, PauliFrameUpdate, PhysicalRound, RoundKind, - RoundTerminal, + ExpandedBlock, ExpandedWorkload, MergeBoundary, PauliFrameUpdate, PhysicalAtomId, + PhysicalRound, RoundKind, RoundTerminal, }; use crate::workload::LogicalBasis; @@ -85,6 +86,16 @@ pub(crate) fn emit_single_block_header( )); } +/// Write `QUBIT_COORDS(x, y) id` in caller order. +/// +/// Single-block memory passes one block. Lattice surgery calls this once per +/// block, in workload order. Pairs stop at the shorter slice, matching `zip`. +pub(crate) fn emit_qubit_coords(out: &mut String, atoms: &[PhysicalAtomId], coords: &[(i32, i32)]) { + for (atom, &(x, y)) in atoms.iter().zip(coords.iter()) { + out.push_str(&format!("QUBIT_COORDS({x}, {y}) {}\n", atom.0)); + } +} + /// Write the lattice-surgery CX comment preamble. pub(crate) fn emit_lattice_surgery_header(out: &mut String, distance: u32, blocks: usize) { out.push_str(&format!( @@ -794,6 +805,20 @@ mod tests { ); } + #[test] + fn qubit_coords_keep_caller_order() { + let mut out = String::new(); + emit_qubit_coords( + &mut out, + &[PhysicalAtomId(4), PhysicalAtomId(0)], + &[(1, 2), (3, 4)], + ); + assert_eq!(out, "QUBIT_COORDS(1, 2) 4\nQUBIT_COORDS(3, 4) 0\n"); + let mut short = String::new(); + emit_qubit_coords(&mut short, &[PhysicalAtomId(1)], &[(0, 0), (9, 9)]); + assert_eq!(short, "QUBIT_COORDS(0, 0) 1\n"); + } + #[test] fn lattice_surgery_header_matches_gold_preamble() { let mut out = String::new();