1use std::cmp::Ordering;
2
3use glam::IVec2;
4use smallvec::SmallVec;
5
6use crate::{CoordinateOverflowError, GateType, PauliBasis, PauliMap};
7
8pub fn checked_translate_coordinate(
15 coordinate: IVec2,
16 offset: IVec2,
17) -> Result<IVec2, CoordinateOverflowError> {
18 coordinate
19 .x
20 .checked_add(offset.x)
21 .zip(coordinate.y.checked_add(offset.y))
22 .map(|(x, y)| IVec2::new(x, y))
23 .ok_or(CoordinateOverflowError { coordinate, offset })
24}
25
26#[derive(
28 Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
29)]
30pub struct BodyId(pub u32);
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
39pub struct ConditionalCorrection {
40 pub pauli: PauliBasis,
42 pub control: u32,
44 pub target: IVec2,
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
50pub struct MeasRecord {
51 pub id: u32,
53 pub qubit: IVec2,
55}
56
57#[derive(Debug, Clone, Default, PartialEq, Eq)]
64pub struct MeasRegistry {
65 records: Vec<MeasRecord>,
66 next_id: u32,
67}
68
69impl serde::Serialize for MeasRegistry {
76 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
77 self.records.serialize(serializer)
78 }
79}
80
81impl<'de> serde::Deserialize<'de> for MeasRegistry {
82 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
83 let records = Vec::<MeasRecord>::deserialize(deserializer)?;
84 if !records.windows(2).all(|pair| pair[0].id < pair[1].id) {
85 return Err(serde::de::Error::custom(
86 "measurement records must be strictly sorted by id",
87 ));
88 }
89 let next_id = match records.last() {
90 None => 0,
91 Some(record) => record
92 .id
93 .checked_add(1)
94 .ok_or_else(|| serde::de::Error::custom("measurement id space exhausted"))?,
95 };
96 Ok(Self { records, next_id })
97 }
98}
99
100impl MeasRegistry {
101 pub fn new() -> Self {
103 Self::default()
104 }
105
106 pub fn allocate(&mut self, qubits: impl IntoIterator<Item = IVec2>) -> Vec<u32> {
109 qubits
110 .into_iter()
111 .map(|qubit| self.allocate_one(qubit))
112 .collect()
113 }
114
115 pub fn allocate_one(&mut self, qubit: IVec2) -> u32 {
123 let id = self.next_id;
124 self.next_id = id_after(id);
125 self.records.push(MeasRecord { id, qubit });
126 id
127 }
128
129 pub fn reserve(&mut self, id: u32, qubit: IVec2) {
137 let slot = match self.records.last() {
140 Some(record) if record.id < id => Err(self.records.len()),
141 _ => self.records.binary_search_by_key(&id, |record| record.id),
142 };
143 match slot {
144 Ok(index) => assert_eq!(
145 self.records[index].qubit, qubit,
146 "measurement id reserved for a different qubit"
147 ),
148 Err(index) => {
149 let next_id = id_after(id);
150 self.records.insert(index, MeasRecord { id, qubit });
151 self.next_id = self.next_id.max(next_id);
152 }
153 }
154 }
155
156 pub(crate) fn reserve_many(&mut self, records: &mut [MeasRecord]) {
162 records.sort_unstable_by_key(|record| record.id);
163 for record in records {
164 self.reserve(record.id, record.qubit);
165 }
166 }
167
168 pub(crate) fn remap_ids(&mut self, remap: &rustc_hash::FxHashMap<u32, u32>) {
175 if remap.is_empty() {
176 return;
177 }
178
179 let next_id = self
180 .records
181 .iter()
182 .map(|record| remap.get(&record.id).copied().unwrap_or(record.id))
183 .max()
184 .map_or(0, id_after);
185
186 for record in &mut self.records {
187 let id = remap.get(&record.id).copied().unwrap_or(record.id);
188 record.id = id;
189 }
190 self.records.sort_by_key(|record| record.id);
191 self.records.dedup_by_key(|record| record.id);
192 self.next_id = next_id;
193 }
194
195 pub fn record(&self, id: u32) -> Option<&MeasRecord> {
197 if let Some(record) = self.records.get(id as usize)
198 && record.id == id
199 {
200 return Some(record);
201 }
202 self.records
203 .binary_search_by_key(&id, |record| record.id)
204 .ok()
205 .map(|index| &self.records[index])
206 }
207
208 pub fn records(&self) -> &[MeasRecord] {
210 &self.records
211 }
212
213 pub(crate) fn next_id(&self) -> u32 {
215 self.next_id
216 }
217}
218
219fn id_after(id: u32) -> u32 {
221 id.checked_add(1)
222 .expect("id space is u32; programs stay far below 2^32 measurements")
223}
224
225#[derive(
228 Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
229)]
230pub struct LoopStateId(pub u32);
231
232#[derive(
238 Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
239)]
240pub enum DetectorTerm<M = u32> {
241 Measurement(M),
243 LoopState(LoopStateId),
245}
246
247type DetectorTerms<M> = SmallVec<[DetectorTerm<M>; 2]>;
248
249#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
255pub struct DetectorParity<M = u32> {
256 terms: DetectorTerms<M>,
257 sign: bool,
258}
259
260impl<M> Default for DetectorParity<M> {
261 fn default() -> Self {
262 Self {
263 terms: SmallVec::new(),
264 sign: false,
265 }
266 }
267}
268
269impl<M: Copy + Ord> DetectorParity<M> {
270 pub fn from_measurements(measurements: impl IntoIterator<Item = M>) -> Self {
272 Self::from_terms(measurements.into_iter().map(DetectorTerm::Measurement))
273 }
274
275 pub fn from_terms(terms: impl IntoIterator<Item = DetectorTerm<M>>) -> Self {
277 let mut parity = Self {
278 terms: terms.into_iter().collect(),
279 sign: false,
280 };
281 parity.canonicalize();
282 parity
283 }
284
285 pub fn from_term_buf(terms: SmallVec<[DetectorTerm<M>; 2]>) -> Self {
289 let mut parity = Self { terms, sign: false };
290 parity.canonicalize();
291 parity
292 }
293
294 #[must_use]
298 pub fn with_sign(mut self, sign: bool) -> Self {
299 self.sign = sign;
300 self
301 }
302
303 #[must_use]
305 pub fn sign(&self) -> bool {
306 self.sign
307 }
308
309 pub fn terms(&self) -> &[DetectorTerm<M>] {
311 &self.terms
312 }
313
314 pub fn measurements(&self) -> impl Iterator<Item = M> + '_ {
316 self.terms.iter().filter_map(|term| match term {
317 DetectorTerm::Measurement(measurement) => Some(*measurement),
318 DetectorTerm::LoopState(_) => None,
319 })
320 }
321
322 pub fn is_empty(&self) -> bool {
324 self.terms.is_empty() && !self.sign
325 }
326
327 pub fn xor_assign(&mut self, other: &Self) {
329 self.sign ^= other.sign;
330 self.terms.extend_from_slice(&other.terms);
331 self.canonicalize();
332 }
333
334 #[must_use]
336 pub fn map_measurements(mut self, mut f: impl FnMut(M) -> M) -> Self {
337 for term in &mut self.terms {
338 if let DetectorTerm::Measurement(measurement) = term {
339 *measurement = f(*measurement);
340 }
341 }
342 self.canonicalize();
343 self
344 }
345
346 pub fn try_map_measurements<N: Copy + Ord, E>(
353 &self,
354 mut f: impl FnMut(M) -> Result<N, E>,
355 ) -> Result<DetectorParity<N>, E> {
356 let mut terms = DetectorTerms::with_capacity(self.terms.len());
357 for &term in &self.terms {
358 terms.push(match term {
359 DetectorTerm::Measurement(measurement) => {
360 DetectorTerm::Measurement(f(measurement)?)
361 }
362 DetectorTerm::LoopState(state) => DetectorTerm::LoopState(state),
363 });
364 }
365 Ok(DetectorParity::from_terms(terms).with_sign(self.sign))
366 }
367
368 fn canonicalize(&mut self) {
369 match self.terms.as_mut_slice() {
373 [] | [_] => return,
374 [a, b] => {
375 match (*a).cmp(&*b) {
376 Ordering::Equal => self.terms.clear(),
377 Ordering::Greater => self.terms.swap(0, 1),
378 Ordering::Less => {}
379 }
380 return;
381 }
382 _ => {}
383 }
384
385 self.terms.sort_unstable();
386 let mut write = 0;
387 let mut read = 0;
388 while read < self.terms.len() {
389 let term = self.terms[read];
390 let mut count = 1;
391 read += 1;
392 while read < self.terms.len() && self.terms[read] == term {
393 count += 1;
394 read += 1;
395 }
396 if count % 2 == 1 {
397 self.terms[write] = term;
398 write += 1;
399 }
400 }
401 self.terms.truncate(write);
402 }
403}
404
405impl<M: std::fmt::Display> std::fmt::Display for DetectorParity<M> {
406 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
407 if self.sign {
408 write!(f, "-")?;
409 }
410 if self.terms.is_empty() {
411 return write!(f, "0");
412 }
413 for (index, term) in self.terms.iter().enumerate() {
414 if index > 0 {
415 write!(f, "*")?;
416 }
417 match term {
418 DetectorTerm::Measurement(measurement) => write!(f, "{measurement}")?,
419 DetectorTerm::LoopState(state) => write!(f, "loop_state({})", state.0)?,
420 }
421 }
422 Ok(())
423 }
424}
425
426#[derive(Debug, Clone, PartialEq, Eq)]
431pub struct LoopCarriedDetectorState<M = u32> {
432 pub state: LoopStateId,
434 pub initial: DetectorParity<M>,
436 pub next: DetectorParity<M>,
438}
439
440#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
443pub struct CircuitBody {
444 ops: Vec<Op>,
445}
446
447impl CircuitBody {
448 pub fn new() -> Self {
450 Self::default()
451 }
452
453 pub fn from_ops(ops: Vec<Op>) -> Self {
455 Self { ops }
456 }
457
458 pub fn ops(&self) -> &[Op] {
460 &self.ops
461 }
462
463 pub fn ops_mut(&mut self) -> &mut Vec<Op> {
465 &mut self.ops
466 }
467}
468
469pub type DetectorCoords = SmallVec<[f64; 2]>;
473
474#[must_use]
477pub fn translate_detector_coords(coords: &DetectorCoords, offset: IVec2) -> DetectorCoords {
478 let mut coords = coords.clone();
479 if let Some(x) = coords.get_mut(0) {
480 *x += offset.x as f64;
481 }
482 if let Some(y) = coords.get_mut(1) {
483 *y += offset.y as f64;
484 }
485 coords
486}
487
488#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
490pub enum Op {
491 Gate {
493 gate: GateType,
495 qubits: Vec<IVec2>,
497 },
498 Measure {
501 basis: PauliBasis,
503 qubits: Vec<IVec2>,
505 measurements: Vec<u32>,
507 flip_probability: f64,
509 },
510 MPP {
516 products: Vec<PauliMap>,
518 measurements: Vec<u32>,
520 },
521 Depolarize1 {
523 probability: f64,
525 qubits: Vec<IVec2>,
527 },
528 Depolarize2 {
530 probability: f64,
532 qubits: Vec<IVec2>,
534 },
535 PauliError {
537 probability: f64,
539 pauli: PauliBasis,
541 qubits: Vec<IVec2>,
543 },
544 Tick,
546 Repeat {
548 body: BodyId,
550 repetitions: u32,
552 },
553 ConditionalPauli(Vec<ConditionalCorrection>),
563}
564
565#[cfg(test)]
566mod tests {
567 use super::*;
568 use glam::ivec2;
569
570 #[test]
571 fn registry_overflow_panics_leave_records_unchanged() {
572 for remap in [false, true] {
573 let mut registry = MeasRegistry::new();
574 registry.reserve(3, ivec2(0, 0));
575 let before = registry.clone();
576 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
577 if remap {
578 registry.remap_ids(&[(3, u32::MAX)].into_iter().collect());
579 } else {
580 registry.reserve(u32::MAX, ivec2(1, 0));
581 }
582 }));
583 assert!(result.is_err(), "exhausted measurement ids must panic");
584 assert_eq!(
585 registry,
586 before,
587 "{} mutated the registry after overflow",
588 if remap { "remap_ids" } else { "reserve" },
589 );
590 }
591 }
592
593 #[test]
594 fn detector_parity_xors_signs_and_terms() {
595 let mut parity = DetectorParity::from_measurements([7]).with_sign(true);
596 parity.xor_assign(&DetectorParity::from_measurements([7]));
597
598 assert!(parity.terms().is_empty());
599 assert!(parity.sign());
600 assert!(
601 !parity.is_empty(),
602 "negative identity is not the zero parity"
603 );
604
605 parity.xor_assign(&DetectorParity::default().with_sign(true));
606 assert!(parity.is_empty());
607 }
608
609 #[test]
610 fn detector_parity_map_recanonicalizes_the_reused_buffer() {
611 let parity = DetectorParity::from_measurements([1, 2, 3])
612 .with_sign(true)
613 .map_measurements(|measurement| if measurement == 1 { 3 } else { measurement });
614
615 assert_eq!(parity.measurements().collect::<Vec<_>>(), vec![2]);
616 assert!(parity.sign());
617 }
618
619 #[test]
624 fn deserialize_recomputes_next_id_and_rejects_unsorted_records() {
625 let mut registry = MeasRegistry::new();
626 registry.reserve(3, ivec2(0, 0));
627 registry.reserve(7, ivec2(1, 0));
628 let bytes = postcard::to_allocvec(®istry).expect("registry encodes into a Vec");
629 let mut restored: MeasRegistry = postcard::from_bytes(&bytes).expect("registry decodes");
630 assert_eq!(restored, registry);
631 assert_eq!(restored.record(7).unwrap().qubit, ivec2(1, 0));
632 assert!(restored.record(1).is_none());
633 assert_eq!(restored.allocate([ivec2(2, 0)]), vec![8]);
634
635 let unsorted = vec![
636 MeasRecord {
637 id: 7,
638 qubit: ivec2(1, 0),
639 },
640 MeasRecord {
641 id: 3,
642 qubit: ivec2(0, 0),
643 },
644 ];
645 let bytes = postcard::to_allocvec(&unsorted).expect("test wire encodes into a Vec");
646 postcard::from_bytes::<MeasRegistry>(&bytes).unwrap_err();
647 }
648
649 #[test]
650 fn meas_registry_reserves_many_in_sorted_order() {
651 let mut registry = MeasRegistry::new();
652 let mut records = vec![
653 MeasRecord {
654 id: 2,
655 qubit: ivec2(2, 0),
656 },
657 MeasRecord {
658 id: 0,
659 qubit: ivec2(0, 0),
660 },
661 MeasRecord {
662 id: 1,
663 qubit: ivec2(1, 0),
664 },
665 ];
666
667 registry.reserve_many(&mut records);
668
669 assert_eq!(
670 registry.records(),
671 &[
672 MeasRecord {
673 id: 0,
674 qubit: ivec2(0, 0)
675 },
676 MeasRecord {
677 id: 1,
678 qubit: ivec2(1, 0)
679 },
680 MeasRecord {
681 id: 2,
682 qubit: ivec2(2, 0)
683 },
684 ]
685 );
686 }
687}