Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,7 @@ test/na/Output/

# Release packaging output (issue #234)
/dist/

# insta pending-snapshot scratch files: never intended to be committed
# (review via `cargo insta review`, then the .snap itself is the artifact)
*.snap.new
5 changes: 1 addition & 4 deletions quon_na/src/movement/duals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use std::collections::BTreeSet;

use crate::geometry::euclidean_um;
use crate::layout::{AtomId, Position, SiteId};
use crate::movement::bank::{OrderedF64, layout_from_sites, site_position_map};
use crate::movement::bank::{OrderedF64, layout_from_sites};
use crate::movement::geometry::legs_conflict;
use crate::movement::types::{CandidateLeg, InteractionPair, MovementParams, MovementPlanError};

Expand Down Expand Up @@ -198,6 +198,3 @@ pub(crate) fn pack_legs_greedy(
}
Ok(chosen)
}

// Re-export for site_position_map callers in plan.rs.
pub(crate) use std::collections::BTreeMap;
6 changes: 2 additions & 4 deletions quon_na/src/movement/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@
use std::collections::{BTreeMap, BTreeSet};

use crate::geometry::{euclidean_um, movement_duration_us, ordered_pair};
use crate::layout::{AodTrapRef, AtomBinding, AtomId, Position, SiteId, TrapBinding};
use crate::layout::{AodTrapRef, AtomId, Position, SiteId};
use crate::movement::bank::{pair_occupants, site_position_map};
use crate::movement::duals::{AodGrid, DualCandidate, pack_legs_greedy};
use crate::movement::geometry::{
POS_EPS_UM, check_entangling_geometry, legs_conflict, verify_aod_legality,
};
use crate::movement::geometry::{POS_EPS_UM, check_entangling_geometry, verify_aod_legality};
use crate::movement::types::{
CandidateLeg, InteractionPair, MoveSpec, MovementParams, MovementPlanError,
};
Expand Down
2 changes: 1 addition & 1 deletion quon_na/src/movement/geometry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::collections::{BTreeMap, BTreeSet};

use crate::geometry::{euclidean_um, ordered_pair};
use crate::layout::{AtomId, Position, SiteId};
use crate::movement::types::{CandidateLeg, MoveSpec, MovementPlanError};
use crate::movement::types::{MoveSpec, MovementPlanError};

/// R1–R3 check with dialect-identical `≤` predicates over **all occupied** atoms (B11).
pub fn check_entangling_geometry(
Expand Down
6 changes: 2 additions & 4 deletions quon_na/src/movement/plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,15 @@
use std::collections::{BTreeMap, BTreeSet};

use crate::geometry::ordered_pair;
use crate::layout::{AtomBinding, AtomId, NeutralAtomLayout, Position, SiteId, TrapBinding};
use crate::layout::{AtomBinding, AtomId, SiteId, TrapBinding};
use crate::movement::bank::{ensure_interaction_pairs, site_position_map};
use crate::movement::duals::{AodGrid, generate_duals, select_duals_sortis};
use crate::movement::emit::{
EmitCtx, can_skip_geometry_gated, collect_entangle2, gate_atom_set, layer_has_entangle,
layer_has_entangle_n, layers_have_entangle, max_entangle2_width,
};
use crate::movement::geometry::check_entangling_geometry;
use crate::movement::types::{
InteractionPair, MovementParams, MovementPlanError, MovementPlanResult,
};
use crate::movement::types::{MovementParams, MovementPlanError, MovementPlanResult};
use crate::schedule::{NeutralAtomAction, ScheduleLayer};
use crate::schedule_entry::GraphScheduleRequest;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ expression: json
"code_family": "repetition_code_toy",
"distance": 3,
"memory_rounds": 2,
"t_count": 0,
"tdag_count": 0,
"ccz_count": 0,
"magic_state_demand": 0,
"estimated_cycles": 37,
"bottleneck": "rearrangement",
"error_budget": {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ expression: json
"code_family": "surface_code_like",
"distance": 3,
"memory_rounds": 2,
"t_count": 0,
"tdag_count": 0,
"ccz_count": 0,
"magic_state_demand": 0,
"estimated_cycles": 165,
"bottleneck": "rearrangement",
"error_budget": {
Expand Down

This file was deleted.

41 changes: 30 additions & 11 deletions quon_qec/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1459,17 +1459,6 @@ mod tests {
);
}

let mz_anc = expanded
.rounds
.iter()
.find(|r| r.kind == RoundKind::MeasureAncilla)
.expect("ancilla mz");
assert_eq!(
mz_anc.terminal.len(),
3,
"ancilla logical Z is top-row (d atoms), not all data"
);

// L-shaped: control left of ancilla (L/R rough seam); target below ancilla
// (top/bottom smooth seam).
let c = &expanded.blocks[0];
Expand All @@ -1479,6 +1468,36 @@ mod tests {
.iter()
.find(|b| b.logical_id == LogicalQubitId(2))
.expect("ancilla");

let mz_anc = expanded
.rounds
.iter()
.find(|r| r.kind == RoundKind::MeasureAncilla)
.expect("ancilla mz");
let measure_events = mz_anc
.terminal
.iter()
.filter(|t| matches!(t, RoundTerminal::Measure { .. }))
.count();
let reset_events = mz_anc
.terminal
.iter()
.filter(|t| matches!(t, RoundTerminal::Reset { .. }))
.count();
assert_eq!(
measure_events, 3,
"ancilla logical Z is top-row (d atoms), not all data"
);
// The ancilla's own footprint (2d²−1 for a distance-d surface patch),
// not `a.check_atoms.len()` — that field is read post-expansion and
// by then also holds the rough/smooth seam atoms attached afterward
// for check-graph accounting (see below), which the MeasureAncilla
// reset (emitted earlier, before that attachment) never touches.
let ancilla_footprint = 2 * (a.distance * a.distance) as usize - 1;
assert_eq!(
reset_events, ancilla_footprint,
"ancilla is fully retired (all data+check atoms reset) after its logical Z readout"
);
let c_max_x = c.coords.iter().map(|(x, _)| *x).max().unwrap();
let a_min_x = a
.coords
Expand Down
30 changes: 22 additions & 8 deletions quon_qec/src/lattice_surgery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,27 @@ pub fn expand_logical_cx(
Some(target),
)?);

// 6. Measure ancilla logical Z (top-row product — not all data)
// 6. Measure ancilla logical Z (top-row product — not all data), then
// reset the whole ancilla footprint. The gadget leaves the ancilla fully
// disentangled from control/target at this point (byproducts corrected
// via the frame updates below), so every ancilla atom — not just the
// measured top row — is safe to reset; without this the un-reset
// bulk/check atoms stay physically "in the way" and trip the dialect
// verifier the first time a later op has to move them.
let mut ancilla_mz_terminal: Vec<RoundTerminal> = ancilla_logical_z
.iter()
.map(|&atom| RoundTerminal::Measure {
atom,
basis: LogicalBasis::Z,
})
.collect();
ancilla_mz_terminal.extend(
ancilla
.data_atoms
.iter()
.chain(ancilla.check_atoms.iter())
.map(|&atom| RoundTerminal::Reset { atom }),
);
rounds.push(PhysicalRound {
kind: RoundKind::MeasureAncilla,
logical_id: ancilla_id,
Expand All @@ -166,13 +186,7 @@ pub fn expand_logical_cx(
z_cnot_count: 0,
local_mid: Vec::new(),
local_after: Vec::new(),
terminal: ancilla_logical_z
.iter()
.map(|&atom| RoundTerminal::Measure {
atom,
basis: LogicalBasis::Z,
})
.collect(),
terminal: ancilla_mz_terminal,
partner_logical_id: None,
frame_updates: Vec::new(),
});
Expand Down
32 changes: 25 additions & 7 deletions quon_qec/src/patch_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,30 @@ pub fn lower_patch_plan(
let layout = find_layout(layouts, *logical_id)?;
let top_row = top_row_data(layout)?;
ancilla_mz_atoms = top_row.clone();
// The ancilla is fully retired here: by the Horsman gadget's
// design, the rough/smooth seam measurements plus this
// logical-Z readout leave it disentangled from control/target
// (byproducts are corrected via the frame updates below), so
// every ancilla atom — not just the measured top row — is
// safe to reset. Without this, the un-reset bulk/check atoms
// stay physically "in the way" and trip the dialect verifier
// (measured-then-reused-without-reset) the first time a later
// op's movement/geometry pass has to touch them, e.g. a
// second logical_cx allocating the next ancilla nearby.
let mut terminal: Vec<RoundTerminal> = top_row
.iter()
.map(|&atom| RoundTerminal::Measure {
atom,
basis: LogicalBasis::Z,
})
.collect();
terminal.extend(
layout
.data_atoms
.iter()
.chain(layout.check_atoms.iter())
.map(|&atom| RoundTerminal::Reset { atom }),
);
rounds.push(PhysicalRound {
kind: RoundKind::MeasureAncilla,
logical_id: *logical_id,
Expand All @@ -815,13 +839,7 @@ pub fn lower_patch_plan(
z_cnot_count: 0,
local_mid: Vec::new(),
local_after: Vec::new(),
terminal: top_row
.iter()
.map(|&atom| RoundTerminal::Measure {
atom,
basis: LogicalBasis::Z,
})
.collect(),
terminal,
partner_logical_id: None,
frame_updates: Vec::new(),
});
Expand Down
20 changes: 18 additions & 2 deletions quon_qec/tests/patch_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,11 +274,27 @@ fn cx_plan_ancilla_measurement_is_top_row() {
.find(|r| r.kind == quon_qec::expand::RoundKind::MeasureAncilla)
.expect("ancilla mz");
// d=3 → top row has 3 data atoms
let measure_events = mz_anc
.terminal
.iter()
.filter(|t| matches!(t, quon_qec::expand::RoundTerminal::Measure { .. }))
.count();
assert_eq!(
mz_anc.terminal.len(),
3,
measure_events, 3,
"ancilla logical Z is top-row (d atoms), not all data"
);
// The ancilla is fully retired here: 9 data + 8 check atoms reset (d=3
// intrinsic footprint, before the seam atoms get attached afterward for
// check-graph accounting — see cx_plan_seam_atoms_attached_to_ancilla_block).
let reset_events = mz_anc
.terminal
.iter()
.filter(|t| matches!(t, quon_qec::expand::RoundTerminal::Reset { .. }))
.count();
assert_eq!(
reset_events, 17,
"ancilla is fully retired (all data+check atoms reset) after its logical Z readout"
);
}

#[test]
Expand Down
Loading