1use std::collections::HashMap;
4
5use glam::IVec3;
6use thiserror::Error;
7
8use super::graph::filled_node_kind;
9use super::output_correction::{OutputCorrectionRow, pivot_output_correction_rows};
10use super::stabilizer::{
11 CoeffVec, DeadlineProjection, SelectiveConstraint, StabilizerBasis,
12 collect_selective_constraints, deadline_projection_with_initial_coeffs,
13 normalize_selective_support, reduce_to_basis, solve_coeff_combination,
14};
15use super::{
16 NodeKind, Stabilizer, StabilizerError, StabilizerGenerator, StabilizerGenerators,
17 StabilizerRowKind, ZXError, ZXGraph,
18};
19use crate::{BlockGraph, ModuleCertificationLimits, Pauli, PauliBasis, PauliString, SelectiveKind};
20
21#[derive(Debug, Clone, Error)]
23#[non_exhaustive]
24pub enum RuntimeBasisError {
25 #[error("selective node at {0} is not part of the live basis")]
27 UnknownSelective(IVec3),
28 #[error("selective node at {0} appears more than once in one assignment")]
30 DuplicateSelective(IVec3),
31 #[error("basis {chosen} is not allowed at selective {pos} of kind {kind}")]
33 InvalidSelectiveBasis {
34 pos: IVec3,
36 kind: SelectiveKind,
38 chosen: PauliBasis,
40 },
41 #[error("measurement variable {0:?} is not present in the live ZX graph")]
43 UnknownMeasurement(String),
44 #[error("selective fill is only supported for Clifford-only computation")]
46 NonCliffordFillUnsupported,
47 #[error("output port {0} is not present in the live ZX graph")]
49 UnknownOutput(IVec3),
50 #[error("no closed {pauli} frame surface for output {output}")]
52 OutputSurfaceUnavailable {
53 output: IVec3,
55 pauli: PauliBasis,
57 },
58 #[error("{0}")]
60 ZX(#[from] ZXError),
61 #[error("{0}")]
63 Stabilizer(#[from] StabilizerError),
64}
65
66#[derive(Debug, Clone)]
72pub struct RuntimeStabilizerBasis {
73 zx_graph: ZXGraph,
74 input_columns: Vec<usize>,
77 prepared_columns: Vec<usize>,
78 basis: StabilizerBasis,
79 readout_coeffs: Vec<CoeffVec>,
84 measurement_ordinals: HashMap<String, usize>,
85 selective_kinds: HashMap<IVec3, SelectiveKind>,
86}
87
88#[derive(Debug, Clone, PartialEq)]
90pub struct DerivedSurface {
91 pub readout_ordinals: Vec<usize>,
97 pub stabilizer: Stabilizer,
99}
100
101impl RuntimeStabilizerBasis {
102 pub fn zx_graph(&self) -> &ZXGraph {
104 &self.zx_graph
105 }
106
107 pub fn generators(&self) -> &[PauliString] {
109 &self.basis.rows
110 }
111
112 pub(crate) fn output_free_readout_failure(
113 &self,
114 name: &str,
115 ordinal: usize,
116 readout_width: usize,
117 outputs: &[IVec3],
118 ) -> Result<Option<IVec3>, RuntimeBasisError> {
119 let mut components = Vec::new();
120 for &output in outputs {
121 let node = self
122 .zx_graph
123 .node_at(output)
124 .filter(|node| node.is_output_port(&self.zx_graph))
125 .ok_or(RuntimeBasisError::UnknownOutput(output))?;
126 components.push((output, node.id, Pauli::X));
127 if node.role != crate::PortRole::Multiplex {
128 components.push((output, node.id, Pauli::Z));
129 }
130 }
131
132 let width = readout_width + components.len();
133 let vectors = self
134 .basis
135 .rows
136 .iter()
137 .zip(&self.readout_coeffs)
138 .map(|(row, readout)| {
139 let mut projected = CoeffVec::zeros(width);
140 for bit in readout.iter_ones() {
141 projected.set_bit(bit, true);
142 }
143 for (offset, (_, col, axis)) in components.iter().enumerate() {
144 projected.set_bit(readout_width + offset, row.get(*col) & *axis);
145 }
146 projected
147 })
148 .collect::<Vec<_>>();
149 if solve_coeff_combination(&vectors, &CoeffVec::singleton(ordinal, width)).is_some() {
150 return Ok(None);
151 }
152
153 let witness = solve_coeff_combination(
154 &self.readout_coeffs,
155 &CoeffVec::singleton(ordinal, readout_width),
156 )
157 .ok_or_else(|| RuntimeBasisError::UnknownMeasurement(name.into()))?;
158 let mut row = PauliString::new(self.zx_graph.total_ids());
159 for index in witness.iter_ones() {
160 row ^= &self.basis.rows[index];
161 }
162 for (output, col, axis) in components {
163 if row.get(col) & axis {
164 return Ok(Some(output));
165 }
166 }
167 unreachable!("an output-free witness would satisfy the projected solve")
168 }
169
170 pub fn with_t_nodes_as_ports(mut self) -> Self {
173 for node in &mut self.zx_graph.nodes {
176 if node.kind == NodeKind::T {
177 node.kind = NodeKind::Port;
178 }
179 }
180 self
181 }
182
183 fn selective_port_map(
184 &self,
185 fills: &[(IVec3, PauliBasis)],
186 ) -> Result<HashMap<IVec3, NodeKind>, RuntimeBasisError> {
187 let mut port_map = HashMap::with_capacity(fills.len());
188 for &(pos, chosen) in fills {
189 let kind = self
190 .selective_kinds
191 .get(&pos)
192 .copied()
193 .ok_or(RuntimeBasisError::UnknownSelective(pos))?;
194 if ![kind.pauli_if_true(), kind.pauli_if_false()].contains(&chosen) {
195 return Err(RuntimeBasisError::InvalidSelectiveBasis { pos, kind, chosen });
196 }
197 if port_map.insert(pos, filled_node_kind(chosen)).is_some() {
198 return Err(RuntimeBasisError::DuplicateSelective(pos));
199 }
200 }
201 Ok(port_map)
202 }
203
204 pub fn into_generators(self) -> StabilizerGenerators {
207 let Self {
208 zx_graph,
209 mut basis,
210 ..
211 } = self;
212 zx_graph.reconstruct_cross_center(&mut basis.rows);
215 let auxiliary_rows = basis.rows.split_off(basis.public_len);
216 basis.kinds.truncate(basis.public_len);
217 let row_phases = zx_graph.stabilizer_row_phases(&basis.rows);
218 let generators = basis
219 .rows
220 .into_iter()
221 .zip(basis.kinds)
222 .zip(row_phases)
223 .map(|((row, kind), row_phase)| {
224 StabilizerGenerator::new(
225 zx_graph.pauli_string_to_stabilizer_with_row_phase(row, row_phase),
226 kind,
227 )
228 })
229 .collect();
230 StabilizerGenerators::from_parts(zx_graph, generators, auxiliary_rows)
231 }
232
233 pub fn from_block_graph(graph: &BlockGraph) -> Result<Self, RuntimeBasisError> {
239 let zx_graph = ZXGraph::try_from(graph)?;
240 Self::from_zx_graph(zx_graph)
241 }
242
243 pub fn from_zx_graph(zx_graph: ZXGraph) -> Result<Self, RuntimeBasisError> {
249 Self::from_zx_graph_with_limits(zx_graph, ModuleCertificationLimits::DEFAULT)
250 }
251
252 pub fn from_zx_graph_with_limits(
258 zx_graph: ZXGraph,
259 limits: ModuleCertificationLimits,
260 ) -> Result<Self, RuntimeBasisError> {
261 match zx_graph.stabilizers_with_limits(limits) {
262 Ok(stabilizers) => return Ok(Self::from_generators(&stabilizers)),
263 Err(
264 StabilizerError::SelectiveSupportUnsatisfiable { .. }
265 | StabilizerError::MeasurementSurfaceUnavailable { .. },
266 ) => {}
267 Err(error) => return Err(error.into()),
268 }
269
270 let raw_external = zx_graph.to_external_generator_table();
273 let constraints = collect_selective_constraints(&zx_graph);
274 let raw_basis = reduce_to_basis(&raw_external, zx_graph.total_ids);
275 let generators = reduce_to_basis(
276 &normalize_selective_support(&raw_basis, &constraints)?,
277 zx_graph.total_ids,
278 );
279 let basis = StabilizerBasis::all_logical(generators);
280 let selective_kinds = selective_kinds_of(&zx_graph);
281 let readout_coeffs = initial_readout_coeffs(&basis.kinds, basis.public_len);
282 let (input_columns, prepared_columns) = authored_boundary_columns(&zx_graph);
283 Ok(Self {
284 input_columns,
285 prepared_columns,
286 zx_graph,
287 basis,
288 readout_coeffs,
289 measurement_ordinals: HashMap::new(),
290 selective_kinds,
291 })
292 }
293
294 pub fn from_generators(stabilizers: &StabilizerGenerators) -> Self {
304 let zx_graph = stabilizers.zx_graph.clone();
305 let rows = stabilizers
306 .generators
307 .iter()
308 .map(|generator| generator.stabilizer.paulis.clone())
309 .chain(stabilizers.auxiliary_rows().iter().cloned())
310 .collect();
311 let kinds: Vec<_> = stabilizers
312 .generators
313 .iter()
314 .map(|generator| generator.kind.clone())
315 .chain(
316 stabilizers
317 .auxiliary_rows()
318 .iter()
319 .map(|_| StabilizerRowKind::Logical),
320 )
321 .collect();
322 let selective_kinds = selective_kinds_of(&zx_graph);
323 let measurement_ordinals = stabilizers
324 .generators
325 .iter()
326 .enumerate()
327 .filter_map(|(ordinal, generator)| {
328 generator
329 .measurement_name()
330 .map(|name| (name.to_owned(), ordinal))
331 })
332 .collect();
333 let public_len = stabilizers.generators.len();
334 let readout_coeffs = initial_readout_coeffs(&kinds, public_len);
335 let (input_columns, prepared_columns) = authored_boundary_columns(&zx_graph);
336 Self {
337 input_columns,
338 prepared_columns,
339 zx_graph,
340 basis: StabilizerBasis::with_public_len(rows, kinds, public_len),
341 readout_coeffs,
342 measurement_ordinals,
343 selective_kinds,
344 }
345 }
346
347 pub fn for_source_readouts(
358 stabilizers: &StabilizerGenerators,
359 ) -> Result<Self, RuntimeBasisError> {
360 let mut basis = Self::from_generators(stabilizers).with_t_nodes_as_ports();
361 let width = stabilizers.generators.len();
362 basis.readout_coeffs.fill(CoeffVec::zeros(width));
363 for node in basis.zx_graph.action_graph().ordered_nodes() {
364 let crate::Action::Measure { name, target } = &node.action else {
365 continue;
366 };
367 let axis = match node.measurement {
368 Some(crate::MeasurementObservable::Concrete(PauliBasis::X)) => Pauli::X,
369 Some(crate::MeasurementObservable::Concrete(PauliBasis::Z)) => Pauli::Z,
370 _ => {
371 return Err(StabilizerError::MeasurementSurfaceUnavailable {
372 mvar: name.clone(),
373 }
374 .into());
375 }
376 };
377 let col = basis
378 .zx_graph
379 .measurement_column(target)
380 .ok_or_else(|| RuntimeBasisError::UnknownMeasurement(name.clone()))?;
381 if basis.zx_graph.nodes().get(col).is_some_and(|node| {
382 matches!(node.kind, NodeKind::X | NodeKind::Z) && node.kind.cross_pauli() == axis
383 }) {
384 return Err(
385 StabilizerError::MeasurementSurfaceUnavailable { mvar: name.clone() }.into(),
386 );
387 }
388 let ordinal = basis.measurement_ordinals[name];
389 for (row, coeff) in basis.basis.rows.iter().zip(&mut basis.readout_coeffs) {
390 coeff.set_bit(ordinal, row.get(col) & axis);
391 }
392 }
393 Ok(basis)
394 }
395
396 #[cfg(test)]
404 pub(super) fn for_output_frames(
405 stabilizers: &StabilizerGenerators,
406 ) -> Result<Self, RuntimeBasisError> {
407 let mut basis = Self::for_source_readouts(stabilizers)?;
408 let constraints = basis
409 .zx_graph
410 .action_graph()
411 .ordered_nodes()
412 .filter_map(|node| {
413 let crate::Action::Measure { target, .. } = &node.action else {
414 return None;
415 };
416 let Some(crate::MeasurementObservable::Concrete(axis)) = node.measurement else {
417 unreachable!("source readout measurements have concrete observables")
418 };
419 Some((
420 basis
421 .zx_graph
422 .measurement_column(target)
423 .expect("named coordinate"),
424 Pauli::from(axis),
425 ))
426 })
427 .collect::<Vec<_>>();
428 basis.restrict_to_zero(&constraints);
429 Ok(basis)
430 }
431
432 #[cfg(test)]
433 fn restrict_to_zero(&mut self, constraints: &[(usize, Pauli)]) {
434 let rank = super::stabilizer::gaussian_elimination_with_tracking(
435 &mut self.basis.rows,
436 &mut self.readout_coeffs,
437 constraints.iter().copied(),
438 |row, &(col, axis)| row.get(col) & axis,
439 None,
440 );
441 self.basis.rows.drain(..rank);
442 self.readout_coeffs.drain(..rank);
443 self.basis = StabilizerBasis::all_logical(std::mem::take(&mut self.basis.rows));
444 }
445
446 pub fn apply_selective_fill(
454 &self,
455 pos: IVec3,
456 chosen: PauliBasis,
457 ) -> Result<RuntimeStabilizerBasis, RuntimeBasisError> {
458 self.apply_selective_fills(&[(pos, chosen)])
459 }
460
461 pub fn apply_selective_fills(
472 &self,
473 fills: &[(IVec3, PauliBasis)],
474 ) -> Result<RuntimeStabilizerBasis, RuntimeBasisError> {
475 if fills.is_empty() {
476 return Ok(self.clone());
477 }
478 if !self.zx_graph.is_clifford_computation() {
479 return Err(RuntimeBasisError::NonCliffordFillUnsupported);
480 }
481
482 let port_map = self.selective_port_map(fills)?;
483
484 let mut basis = self.basis.clone();
485 let mut readout_coeffs = self.readout_coeffs.clone();
486 let mut remaining_constraints = collect_selective_constraints(&self.zx_graph);
487 for &(pos, chosen) in fills {
488 let kind = self.selective_kinds[&pos];
489 let col = self
490 .zx_graph
491 .node_at(pos)
492 .expect("validated selective has a ZX node")
493 .id;
494 remaining_constraints.retain(|constraint| constraint.pos != pos);
495 let constraints = [(col, Pauli::from(chosen))];
496 (basis, readout_coeffs) = evolve_selective_basis(
497 &basis,
498 &readout_coeffs,
499 &constraints,
500 &remaining_constraints,
501 pos,
502 kind,
503 )?;
504 }
505
506 let filled_zx = self.zx_graph.fill_ports(&port_map)?;
507 Ok(RuntimeStabilizerBasis {
508 input_columns: self.input_columns.clone(),
509 prepared_columns: self.prepared_columns.clone(),
510 zx_graph: filled_zx,
511 basis,
512 readout_coeffs,
513 measurement_ordinals: self.measurement_ordinals.clone(),
514 selective_kinds: remaining_constraints
515 .into_iter()
516 .map(|constraint| (constraint.pos, constraint.kind))
517 .collect(),
518 })
519 }
520
521 pub fn apply_selective_fill_assignments(
532 &self,
533 assignments: &[Vec<(IVec3, PauliBasis)>],
534 ) -> Result<Vec<RuntimeStabilizerBasis>, RuntimeBasisError> {
535 let independently = || {
536 assignments
537 .iter()
538 .map(|fills| self.apply_selective_fills(fills))
539 .collect()
540 };
541 let Some(first) = assignments.first() else {
542 return Ok(Vec::new());
543 };
544 if assignments.iter().any(|assignment| {
545 assignment.len() != first.len()
546 || assignment
547 .iter()
548 .zip(first)
549 .any(|((pos, _), (first, _))| pos != first)
550 }) {
551 return independently();
552 }
553 if first.is_empty() {
554 return Ok(vec![self.clone(); assignments.len()]);
555 }
556 if !self.zx_graph.is_clifford_computation() {
557 return Err(RuntimeBasisError::NonCliffordFillUnsupported);
558 }
559 let Ok(port_maps) = assignments
560 .iter()
561 .map(|fills| self.selective_port_map(fills))
562 .collect::<Result<Vec<_>, _>>()
563 else {
564 return independently();
565 };
566 let mut states = vec![None; assignments.len()];
567 let remaining = collect_selective_constraints(&self.zx_graph);
568 if fill_prefix_states(self, assignments, remaining.clone(), &mut states).is_none() {
569 return independently();
570 }
571 let remaining = first.iter().fold(remaining, |mut remaining, (pos, _)| {
572 remaining.retain(|constraint| constraint.pos != *pos);
573 remaining
574 });
575 states
576 .into_iter()
577 .zip(port_maps)
578 .map(|(state, ports)| {
579 let (basis, readout_coeffs) = state.expect("every assignment reached a trie leaf");
580 Ok(RuntimeStabilizerBasis {
581 input_columns: self.input_columns.clone(),
582 prepared_columns: self.prepared_columns.clone(),
583 zx_graph: self.zx_graph.fill_ports(&ports)?,
584 basis,
585 readout_coeffs,
586 measurement_ordinals: self.measurement_ordinals.clone(),
587 selective_kinds: remaining
588 .iter()
589 .map(|constraint| (constraint.pos, constraint.kind))
590 .collect(),
591 })
592 })
593 .collect()
594 }
595
596 pub fn derive_measurement_surface(
606 &self,
607 mvar: &str,
608 deadline: i64,
609 ) -> Result<DerivedSurface, RuntimeBasisError> {
610 self.derive_measurement_surface_with_constraints(mvar, deadline, &[], &[], false, &[])
611 }
612
613 pub fn derive_causal_measurement_surface(
619 &self,
620 mvar: &str,
621 deadline: i64,
622 ) -> Result<DerivedSurface, RuntimeBasisError> {
623 self.derive_readout_from_known(mvar, deadline, &[], &[])
624 }
625
626 pub(crate) fn derive_readout_from_known(
627 &self,
628 mvar: &str,
629 deadline: i64,
630 unavailable: &[Vec<(usize, Pauli)>],
631 known: &[usize],
632 ) -> Result<DerivedSurface, RuntimeBasisError> {
633 let absent = self
634 .selective_kinds
635 .keys()
636 .flat_map(|&pos| {
637 let col = self.zx_graph.node_at(pos).expect("live selective node").id;
638 [(col, Pauli::X), (col, Pauli::Z)]
639 })
640 .collect::<Vec<_>>();
641 self.derive_measurement_surface_with_constraints(
642 mvar,
643 deadline,
644 &absent,
645 unavailable,
646 true,
647 known,
648 )
649 }
650
651 fn derive_measurement_surface_with_constraints(
652 &self,
653 mvar: &str,
654 deadline: i64,
655 absent: &[(usize, Pauli)],
656 unavailable_feedback: &[Vec<(usize, Pauli)>],
657 require_output_free: bool,
658 known: &[usize],
659 ) -> Result<DerivedSurface, RuntimeBasisError> {
660 let (derivation_rows, derivation_coeffs) = if require_output_free {
661 (self.basis.rows.clone(), self.readout_coeffs.clone())
662 } else {
663 self.derivation_rows_and_coeffs()
664 };
665 let DeadlineProjection {
666 consumed,
667 rows: projected_rows,
668 coeffs: projected_coeffs,
669 } = deadline_projection_with_initial_coeffs(
670 &derivation_rows,
671 &derivation_coeffs,
672 &self.zx_graph,
673 deadline,
674 );
675 let ordinal = *self
676 .measurement_ordinals
677 .get(mvar)
678 .ok_or_else(|| RuntimeBasisError::UnknownMeasurement(mvar.to_owned()))?;
679 let readout_width = self.readout_coeffs.first().map_or(0, CoeffVec::len);
680 let rows = &projected_rows[consumed..];
681 let outputs = self.zx_graph.output_ports();
682 let output_components = outputs
683 .iter()
684 .flat_map(|&pos| {
685 let node = self.zx_graph.node_at(pos).expect("output port is a node");
686 [Pauli::X, Pauli::Z]
687 .into_iter()
688 .filter(move |&axis| {
689 node.role != crate::PortRole::Multiplex || axis == Pauli::X
690 })
691 .map(move |axis| (node.id, axis))
692 })
693 .collect::<Vec<_>>();
694 for output_free in [true, false] {
698 if !output_free && require_output_free {
699 break;
700 }
701 let components = absent
702 .iter()
703 .chain(output_components.iter().filter(|_| output_free))
704 .copied()
705 .collect::<Vec<_>>();
706 let width = readout_width + components.len() + unavailable_feedback.len();
707 let constraints = rows
708 .iter()
709 .zip(&projected_coeffs[consumed..])
710 .map(|(row, coeff)| {
711 let mut value = CoeffVec::zeros(width);
712 for bit in coeff.iter_ones().filter(|bit| !known.contains(bit)) {
713 value.set_bit(bit, true);
714 }
715 for (offset, &(col, axis)) in components.iter().enumerate() {
716 value.set_bit(readout_width + offset, row.get(col) & axis);
717 }
718 for (offset, targets) in unavailable_feedback.iter().enumerate() {
719 let odd = targets.iter().fold(false, |odd, &(column, pauli)| {
720 odd ^ row.get(column).anticommutes(pauli)
721 });
722 value.set_bit(readout_width + components.len() + offset, odd);
723 }
724 value
725 })
726 .collect::<Vec<_>>();
727 let target = CoeffVec::singleton(ordinal, width);
728 if let Some(combination) = solve_coeff_combination(&constraints, &target) {
729 let mut row = PauliString::new(self.zx_graph.total_ids());
730 let mut coeff = CoeffVec::zeros(readout_width);
731 for index in combination.iter_ones() {
732 row ^= &rows[index];
733 coeff.xor_assign(&projected_coeffs[consumed + index]);
734 }
735 return Ok(materialize_derived_surface(&self.zx_graph, row, coeff));
736 }
737 }
738 Err(StabilizerError::UnavailableControlParity {
739 mvar: mvar.to_owned(),
740 deadline,
741 }
742 .into())
743 }
744
745 pub fn derive_output_correction_rows(
753 &self,
754 outputs: &[IVec3],
755 ) -> Result<Vec<OutputCorrectionRow>, RuntimeBasisError> {
756 Ok(self
757 .pivoted_output_correction_rows(outputs)?
758 .into_iter()
759 .map(|(correction, _)| correction)
760 .collect())
761 }
762
763 pub fn derive_output_correction_surfaces(
772 &self,
773 outputs: &[IVec3],
774 ) -> Result<Vec<(OutputCorrectionRow, Stabilizer)>, RuntimeBasisError> {
775 Ok(self
776 .pivoted_output_correction_rows(outputs)?
777 .into_iter()
778 .map(|(correction, row)| (correction, self.zx_graph.materialize_stabilizer(row)))
779 .collect())
780 }
781
782 #[cfg(feature = "verify")]
785 pub(crate) fn closed_stabilizer_surfaces(&self) -> Vec<Stabilizer> {
786 let mut rows = self.derivation_rows_and_coeffs().0;
787 let constraints = self
788 .zx_graph
789 .nodes()
790 .iter()
791 .filter(|node| matches!(node.kind, NodeKind::Port | NodeKind::T))
792 .flat_map(|node| [(node.id, Pauli::X), (node.id, Pauli::Z)]);
793 let consumed = super::stabilizer::gaussian_elimination_with_tracking(
794 &mut rows,
795 &mut [],
796 constraints,
797 |row, &(column, axis)| row.get(column) & axis,
798 None,
799 );
800 rows.into_iter()
801 .skip(consumed)
802 .map(|row| self.zx_graph.materialize_stabilizer(row))
803 .collect()
804 }
805
806 #[cfg(feature = "verify")]
808 pub(crate) fn resolved_surfaces(&self) -> Vec<Stabilizer> {
809 self.derivation_rows_and_coeffs()
810 .0
811 .into_iter()
812 .map(|row| self.zx_graph.materialize_stabilizer(row))
813 .collect()
814 }
815
816 pub fn into_output_correction_surfaces(
823 self,
824 outputs: &[IVec3],
825 ) -> Result<Vec<(OutputCorrectionRow, Stabilizer)>, RuntimeBasisError> {
826 let Self {
827 zx_graph,
828 input_columns,
829 prepared_columns,
830 basis,
831 readout_coeffs,
832 ..
833 } = self;
834 let (rows, coeffs) = basis
835 .rows
836 .into_iter()
837 .zip(basis.kinds)
838 .zip(readout_coeffs)
839 .filter(|((_, kind), _)| !kind.is_selective_fixing())
840 .map(|((row, _), coeff)| (row, coeff))
841 .unzip();
842 let output_cols = requested_output_columns(&zx_graph, outputs)?;
843 let protected = protected_boundary_columns(&zx_graph, &prepared_columns, &output_cols);
844 Ok(pivot_output_correction_rows(
845 rows,
846 coeffs,
847 outputs,
848 &output_cols,
849 &input_columns,
850 &protected,
851 )
852 .into_iter()
853 .map(|(correction, row)| (correction, zx_graph.materialize_stabilizer(row)))
854 .collect())
855 }
856
857 fn pivoted_output_correction_rows(
858 &self,
859 outputs: &[IVec3],
860 ) -> Result<Vec<(OutputCorrectionRow, PauliString)>, RuntimeBasisError> {
861 let projected = self.projected_rows_for_outputs(outputs)?;
862 let protected = protected_boundary_columns(
863 &self.zx_graph,
864 &self.prepared_columns,
865 &projected.output_cols,
866 );
867 Ok(pivot_output_correction_rows(
868 projected.rows,
869 projected.coeffs,
870 outputs,
871 &projected.output_cols,
872 &self.input_columns,
873 &protected,
874 ))
875 }
876
877 fn projected_rows_for_outputs(
880 &self,
881 outputs: &[IVec3],
882 ) -> Result<ProjectedOutputRows, RuntimeBasisError> {
883 let (projected_rows, projected_coeffs) = self.derivation_rows_and_coeffs();
884 let output_cols = requested_output_columns(&self.zx_graph, outputs)?;
885 Ok(ProjectedOutputRows {
886 rows: projected_rows,
887 coeffs: projected_coeffs,
888 output_cols,
889 })
890 }
891 fn derivation_rows_and_coeffs(&self) -> (Vec<PauliString>, Vec<CoeffVec>) {
892 self.basis
893 .rows
894 .iter()
895 .zip(&self.basis.kinds)
896 .zip(&self.readout_coeffs)
897 .filter(|((_, kind), _)| !kind.is_selective_fixing())
898 .map(|((row, _), coeff)| (row.clone(), coeff.clone()))
899 .unzip()
900 }
901}
902
903pub(super) fn authored_boundary_columns(zx: &ZXGraph) -> (Vec<usize>, Vec<usize>) {
905 let mut nodes = zx
906 .nodes()
907 .iter()
908 .filter(|node| node.kind == NodeKind::T || node.is_input_port(zx))
909 .collect::<Vec<_>>();
910 nodes.sort_unstable_by_key(|node| node.pos.to_array());
911 let inputs = nodes
912 .iter()
913 .filter(|node| node.is_input_port(zx))
914 .map(|node| node.id)
915 .collect();
916 let prepared = nodes
917 .iter()
918 .filter(|node| node.kind == NodeKind::T)
919 .map(|node| node.id)
920 .collect();
921 (inputs, prepared)
922}
923
924fn protected_boundary_columns(zx: &ZXGraph, prepared: &[usize], outputs: &[usize]) -> Vec<usize> {
926 let mut columns = prepared
927 .iter()
928 .copied()
929 .chain(
930 zx.nodes()
931 .iter()
932 .filter(|node| node.is_output_port(zx) && !outputs.contains(&node.id))
933 .map(|node| node.id),
934 )
935 .collect::<Vec<_>>();
936 columns.sort_unstable_by_key(|&column| zx.nodes()[column].pos.to_array());
937 columns.dedup();
938 columns
939}
940
941struct FillWork {
942 indices: Vec<usize>,
943 depth: usize,
944 basis: StabilizerBasis,
945 readout_coeffs: Vec<CoeffVec>,
946 remaining: Vec<SelectiveConstraint>,
947}
948
949fn fill_prefix_states(
950 source: &RuntimeStabilizerBasis,
951 assignments: &[Vec<(IVec3, PauliBasis)>],
952 remaining: Vec<SelectiveConstraint>,
953 states: &mut [Option<(StabilizerBasis, Vec<CoeffVec>)>],
954) -> Option<()> {
955 let mut stack = vec![FillWork {
956 indices: (0..assignments.len()).collect(),
957 depth: 0,
958 basis: source.basis.clone(),
959 readout_coeffs: source.readout_coeffs.clone(),
960 remaining,
961 }];
962 while let Some(mut work) = stack.pop() {
963 loop {
964 if work.depth == assignments[work.indices[0]].len() {
965 let last = work.indices.pop()?;
966 for &index in &work.indices {
967 states[index] = Some((work.basis.clone(), work.readout_coeffs.clone()));
968 }
969 states[last] = Some((work.basis, work.readout_coeffs));
970 break;
971 }
972 let pos = assignments[work.indices[0]][work.depth].0;
973 let kind = source.selective_kinds[&pos];
974 let col = source
975 .zx_graph
976 .node_at(pos)
977 .expect("validated selective has a ZX node")
978 .id;
979 work.remaining.retain(|constraint| constraint.pos != pos);
980 let mut groups: Vec<(PauliBasis, Vec<usize>)> = Vec::new();
981 for &index in &work.indices {
982 let chosen = assignments[index][work.depth].1;
983 if let Some((_, group)) = groups.iter_mut().find(|(value, _)| *value == chosen) {
984 group.push(index);
985 } else {
986 groups.push((chosen, vec![index]));
987 }
988 }
989 if groups.len() == 1 {
990 let (chosen, indices) = groups.pop()?;
991 (work.basis, work.readout_coeffs) = evolve_selective_basis(
992 &work.basis,
993 &work.readout_coeffs,
994 &[(col, Pauli::from(chosen))],
995 &work.remaining,
996 pos,
997 kind,
998 )
999 .ok()?;
1000 work.indices = indices;
1001 work.depth += 1;
1002 continue;
1003 }
1004 for (chosen, indices) in groups {
1005 let (basis, readout_coeffs) = evolve_selective_basis(
1006 &work.basis,
1007 &work.readout_coeffs,
1008 &[(col, Pauli::from(chosen))],
1009 &work.remaining,
1010 pos,
1011 kind,
1012 )
1013 .ok()?;
1014 stack.push(FillWork {
1015 indices,
1016 depth: work.depth + 1,
1017 basis,
1018 readout_coeffs,
1019 remaining: work.remaining.clone(),
1020 });
1021 }
1022 break;
1023 }
1024 }
1025 Some(())
1026}
1027
1028fn evolve_selective_basis(
1029 basis: &StabilizerBasis,
1030 readout_coeffs: &[CoeffVec],
1031 constraints: &[(usize, Pauli)],
1032 remaining_constraints: &[SelectiveConstraint],
1033 pos: IVec3,
1034 kind: SelectiveKind,
1035) -> Result<(StabilizerBasis, Vec<CoeffVec>), RuntimeBasisError> {
1036 let (basis, coeffs) = if basis
1037 .kinds
1038 .iter()
1039 .any(|row_kind| row_kind.fixes_selective(pos))
1040 {
1041 match basis.apply_tagged_selective_fill(constraints, pos, kind) {
1042 Ok(transition) => transition,
1043 Err(StabilizerError::SelectiveSupportUnsatisfiable { .. }) => {
1044 basis.apply_generic_selective_fill(constraints, remaining_constraints)?
1045 }
1046 Err(err) => return Err(err.into()),
1047 }
1048 } else {
1049 basis.apply_generic_selective_fill(constraints, remaining_constraints)?
1050 };
1051 Ok((basis, compose_coeffs(&coeffs, readout_coeffs)))
1052}
1053
1054fn initial_readout_coeffs(kinds: &[StabilizerRowKind], public_len: usize) -> Vec<CoeffVec> {
1055 let width = kinds.len();
1056 kinds
1057 .iter()
1058 .enumerate()
1059 .map(|(index, kind)| {
1060 if index < public_len && kind.is_readout() {
1061 CoeffVec::singleton(index, width)
1062 } else {
1063 CoeffVec::zeros(width)
1064 }
1065 })
1066 .collect()
1067}
1068
1069fn compose_coeffs(transform: &[CoeffVec], source: &[CoeffVec]) -> Vec<CoeffVec> {
1070 let width = source.first().map_or(0, CoeffVec::len);
1071 transform
1072 .iter()
1073 .map(|combination| {
1074 let mut composed = CoeffVec::zeros(width);
1075 for source_index in combination.iter_ones() {
1076 composed.xor_assign(&source[source_index]);
1077 }
1078 composed
1079 })
1080 .collect()
1081}
1082
1083fn selective_kinds_of(zx_graph: &ZXGraph) -> HashMap<IVec3, SelectiveKind> {
1084 collect_selective_constraints(zx_graph)
1085 .into_iter()
1086 .map(|constraint| (constraint.pos, constraint.kind))
1087 .collect()
1088}
1089
1090struct ProjectedOutputRows {
1093 rows: Vec<PauliString>,
1094 coeffs: Vec<CoeffVec>,
1095 output_cols: Vec<usize>,
1096}
1097
1098fn materialize_derived_surface(
1099 zx_graph: &ZXGraph,
1100 row: PauliString,
1101 coeff: CoeffVec,
1102) -> DerivedSurface {
1103 DerivedSurface {
1104 readout_ordinals: coeff.to_indices(),
1105 stabilizer: zx_graph.materialize_stabilizer(row),
1106 }
1107}
1108
1109fn requested_output_columns(
1110 zx_graph: &ZXGraph,
1111 outputs: &[IVec3],
1112) -> Result<Vec<usize>, RuntimeBasisError> {
1113 outputs
1114 .iter()
1115 .copied()
1116 .map(|output| {
1117 let node_id = zx_graph
1118 .node_at(output)
1119 .filter(|node| node.is_output_port(zx_graph))
1120 .map(|node| node.id)
1121 .ok_or(RuntimeBasisError::UnknownOutput(output))?;
1122 Ok(node_id)
1123 })
1124 .collect()
1125}
1126
1127#[cfg(test)]
1128mod tests {
1129 use glam::ivec3;
1130
1131 use crate::MeasurementObservable;
1132 use crate::{BlockGraph, GalleryItem, Pauli, PauliBasis, PauliString};
1133
1134 use super::{CoeffVec, RuntimeBasisError, RuntimeStabilizerBasis};
1135
1136 #[test]
1137 fn source_readouts_preserve_the_input_instrument_under_a_public_rebase() {
1138 let mut generators = GalleryItem::CCZInjectedAnd
1139 .build()
1140 .materialize_root_graph()
1141 .expect("gallery flat projection")
1142 .stabilizers()
1143 .unwrap();
1144 let source = RuntimeStabilizerBasis::for_source_readouts(&generators).unwrap();
1145 let expected = source
1146 .derive_causal_measurement_surface("m1", i64::MAX)
1147 .unwrap();
1148 let named = generators
1149 .generators
1150 .iter()
1151 .position(|g| g.measurement_name() == Some("m1"))
1152 .unwrap();
1153 let logical = generators
1154 .generators
1155 .iter()
1156 .position(|g| matches!(g.kind, crate::StabilizerRowKind::Logical))
1157 .unwrap();
1158 let mixed = &generators.generators[named].stabilizer.paulis
1159 ^ &generators.generators[logical].stabilizer.paulis;
1160 generators.generators[named].stabilizer = generators.zx_graph.materialize_stabilizer(mixed);
1161 let rebased = RuntimeStabilizerBasis::for_source_readouts(&generators)
1162 .unwrap()
1163 .derive_causal_measurement_surface("m1", i64::MAX)
1164 .unwrap();
1165 assert_eq!(
1166 rebased.stabilizer.port_stabilizer,
1167 expected.stabilizer.port_stabilizer
1168 );
1169 assert_eq!(rebased.stabilizer.sign, expected.stabilizer.sign);
1170 }
1171
1172 #[test]
1173 fn joint_fill_provenance_never_names_internal_rows() {
1174 let graph = GalleryItem::ToffoliFromAndDelayedCZ
1175 .build()
1176 .materialize_root_graph()
1177 .expect("gallery flat projection");
1178 let stabilizers = graph.stabilizers().unwrap();
1179 let public_rows = stabilizers.generators.len();
1180 assert!(!stabilizers.auxiliary_rows().is_empty());
1181
1182 let mut basis =
1183 RuntimeStabilizerBasis::from_generators(&stabilizers).with_t_nodes_as_ports();
1184 assert!(
1185 basis.readout_coeffs[public_rows..]
1186 .iter()
1187 .all(CoeffVec::is_zero)
1188 );
1189
1190 let resolves = graph
1191 .actions()
1192 .iter()
1193 .filter_map(|action| match action {
1194 crate::Action::Resolve { target, .. } => Some(*target),
1195 _ => None,
1196 })
1197 .collect::<Vec<_>>();
1198 for pos in resolves {
1199 let kind = basis.selective_kinds[&pos];
1200 basis = basis
1201 .apply_selective_fill(pos, kind.pauli_if_false())
1202 .unwrap();
1203 }
1204
1205 let outputs = basis.zx_graph.output_ports();
1206 for correction in basis.derive_output_correction_rows(&outputs).unwrap() {
1207 assert!(
1208 correction
1209 .readout_ordinals
1210 .iter()
1211 .all(|&ordinal| ordinal < public_rows),
1212 "derived readout provenance exposed an internal row"
1213 );
1214 }
1215 }
1216
1217 #[test]
1218 fn batched_fills_match_sequential_output_surfaces() {
1219 for entry in [
1220 GalleryItem::CCZInjectedAnd,
1221 GalleryItem::CCZInjectedMaj,
1222 GalleryItem::ToffoliFromAndDelayedCZ,
1223 GalleryItem::T,
1224 ] {
1225 let graph = entry
1226 .build()
1227 .materialize_root_graph()
1228 .expect("gallery flat projection");
1229 let stabilizers = graph.stabilizers().unwrap();
1230 let base =
1231 RuntimeStabilizerBasis::from_generators(&stabilizers).with_t_nodes_as_ports();
1232 assert!(base.input_columns.iter().all(|&column| {
1233 stabilizers.zx_graph.nodes()[column].is_input_port(&stabilizers.zx_graph)
1234 }));
1235 assert!(base.prepared_columns.iter().all(|&column| {
1236 stabilizers.zx_graph.nodes()[column].kind == crate::NodeKind::T
1237 && base.zx_graph.nodes()[column].kind == crate::NodeKind::Port
1238 && !base.input_columns.contains(&column)
1239 }));
1240 let sites = graph
1241 .actions()
1242 .iter()
1243 .filter_map(|action| match action {
1244 crate::Action::Resolve { target, .. } => Some(*target),
1245 _ => None,
1246 })
1247 .collect::<Vec<_>>();
1248 let outputs = stabilizers.zx_graph.output_ports();
1249
1250 let assignments = stabilizers
1251 .zx_graph
1252 .action_graph()
1253 .resolve_value_domain(&sites)
1254 .unwrap()
1255 .values()
1256 .iter()
1257 .map(|bits| {
1258 sites
1259 .iter()
1260 .zip(bits)
1261 .map(|(&pos, &bit)| {
1262 let kind = base.selective_kinds[&pos];
1263 (
1264 pos,
1265 if bit {
1266 kind.pauli_if_true()
1267 } else {
1268 kind.pauli_if_false()
1269 },
1270 )
1271 })
1272 .collect::<Vec<_>>()
1273 })
1274 .collect::<Vec<_>>();
1275 let forked = base.apply_selective_fill_assignments(&assignments).unwrap();
1276 for (fills, forked) in assignments.iter().zip(forked) {
1277 let batched = base.apply_selective_fills(fills).unwrap();
1278 let sequential = fills.iter().try_fold(base.clone(), |basis, &(pos, axis)| {
1279 basis.apply_selective_fill(pos, axis)
1280 });
1281
1282 let sequential = sequential
1283 .unwrap()
1284 .derive_output_correction_surfaces(&outputs)
1285 .unwrap();
1286 assert_eq!(
1287 batched.derive_output_correction_surfaces(&outputs).unwrap(),
1288 sequential,
1289 "borrowed {entry:?} assignment {fills:?}"
1290 );
1291 assert_eq!(
1292 batched.into_output_correction_surfaces(&outputs).unwrap(),
1293 sequential,
1294 "consuming {entry:?} assignment {fills:?}"
1295 );
1296 assert_eq!(
1297 forked.into_output_correction_surfaces(&outputs).unwrap(),
1298 sequential,
1299 "forked {entry:?} assignment {fills:?}"
1300 );
1301 }
1302
1303 let mut reordered = assignments[..2].to_vec();
1304 reordered[0].reverse();
1305 let fallback = base.apply_selective_fill_assignments(&reordered).unwrap();
1306 for (fills, fallback) in reordered.iter().zip(fallback) {
1307 let expected = base
1308 .apply_selective_fills(fills)
1309 .unwrap()
1310 .into_output_correction_surfaces(&outputs)
1311 .unwrap();
1312 assert_eq!(
1313 fallback.into_output_correction_surfaces(&outputs).unwrap(),
1314 expected,
1315 "fallback {entry:?} assignment {fills:?}"
1316 );
1317 }
1318
1319 let repeated = vec![assignments[0].clone(); 3];
1320 let expected = base
1321 .apply_selective_fills(&repeated[0])
1322 .unwrap()
1323 .into_output_correction_surfaces(&outputs)
1324 .unwrap();
1325 for filled in base.apply_selective_fill_assignments(&repeated).unwrap() {
1326 assert_eq!(
1327 filled.into_output_correction_surfaces(&outputs).unwrap(),
1328 expected
1329 );
1330 }
1331 }
1332 }
1333
1334 #[test]
1335 fn uma_readout_prefers_output_free_surface_after_z_fills() {
1336 let stabilizers = GalleryItem::UMA
1337 .build()
1338 .materialize_root_graph()
1339 .expect("gallery flat projection")
1340 .stabilizers()
1341 .unwrap();
1342 stabilizers
1343 .validate_measurements_close_before_outputs()
1344 .unwrap();
1345 let basis = RuntimeStabilizerBasis::from_generators(&stabilizers)
1346 .apply_selective_fills(&[
1347 (ivec3(2, 1, 2), PauliBasis::Z),
1348 (ivec3(2, 2, 2), PauliBasis::Z),
1349 ])
1350 .unwrap();
1351 let derived = basis.derive_measurement_surface("m_ikprime", 2).unwrap();
1352 assert!(
1353 !derived
1354 .stabilizer
1355 .port_stabilizer
1356 .contains_key(&ivec3(2, 0, 1))
1357 );
1358 let ordinal = stabilizers
1359 .generators
1360 .iter()
1361 .position(|row| row.measurement_name() == Some("m_ikprime"))
1362 .unwrap();
1363 assert_eq!(derived.readout_ordinals, vec![ordinal]);
1364 }
1365
1366 #[test]
1367 fn measurement_derivation_preserves_observable_and_readout_identity() {
1368 for (kind, expected) in [("XZX", Pauli::X), ("ZXZ", Pauli::Z)] {
1369 let graph = BlockGraph::from_blog_text(&format!(
1370 "BLOG 1.0\n0: Port [0,0,-1]\n1: {kind} [0,0,0]\n2: Port [0,0,1]\n0 -> +Z\n1 -> +Z\nm = measure 1\n"
1371 )).unwrap();
1372 let stabilizers = graph.stabilizers().unwrap();
1373 let ordinal = stabilizers
1374 .generators
1375 .iter()
1376 .position(|generator| generator.measurement_name() == Some("m"))
1377 .unwrap();
1378 let basis = RuntimeStabilizerBasis::from_generators(&stabilizers);
1379 let derived = basis.derive_measurement_surface("m", 2).unwrap();
1380 assert_eq!(derived.stabilizer.interior_nodes[&ivec3(0, 0, 0)], expected);
1381 assert_eq!(derived.readout_ordinals, vec![ordinal]);
1382 }
1383 }
1384
1385 #[test]
1386 fn runtime_basis_uses_measurement_actions() {
1387 let graph = GalleryItem::T
1388 .build()
1389 .materialize_root_graph()
1390 .expect("gallery flat projection");
1391 let zx = crate::ZXGraph::try_from(&graph).unwrap();
1392
1393 let basis = RuntimeStabilizerBasis::from_zx_graph(zx).unwrap();
1394 let derived = basis.derive_measurement_surface("mzz", 2).unwrap();
1395
1396 assert!(!derived.readout_ordinals.is_empty());
1397 }
1398
1399 #[test]
1400 fn apply_selective_fill_refreshes_filled_selective_measurement_metadata() {
1401 let mut graph = BlockGraph::from_blog_text(
1402 "BLOG 1.0\n\n 0: Port [0,0,0]\n 1: XZ [0,0,1]\n 2: ZXZ [1,0,0]\n [0,0,0] -> +Z\n",
1403 )
1404 .unwrap();
1405 graph
1406 .set_actions_lenient(vec![
1407 crate::Action::Measure {
1408 target: crate::MeasureTarget::Node(ivec3(0, 0, 1)),
1409 name: "m".into(),
1410 },
1411 crate::Action::Measure {
1412 target: crate::MeasureTarget::Node(ivec3(1, 0, 0)),
1413 name: "m2".into(),
1414 },
1415 crate::Action::Resolve {
1416 target: ivec3(0, 0, 1),
1417 condition: crate::Expr::Var("m2".into()),
1418 },
1419 ])
1420 .unwrap();
1421 let zx = crate::ZXGraph::from_block_graph_for_analysis(&graph).unwrap();
1422 let basis = RuntimeStabilizerBasis::from_zx_graph(zx).unwrap();
1423
1424 let filled = basis
1425 .apply_selective_fill(ivec3(0, 0, 1), PauliBasis::Z)
1426 .unwrap();
1427 let zx = &filled.zx_graph;
1428
1429 assert_eq!(
1430 zx.action_graph().node_by_ordinal(0).unwrap().measurement,
1431 Some(MeasurementObservable::Concrete(PauliBasis::Z))
1432 );
1433 let ordinal = basis.measurement_ordinals["m"];
1434 let derived = filled.derive_measurement_surface("m", 2).unwrap();
1435 assert_eq!(derived.stabilizer.interior_nodes[&ivec3(0, 0, 1)], Pauli::Z);
1436 assert_eq!(derived.readout_ordinals, vec![ordinal]);
1437 }
1438
1439 #[test]
1440 fn apply_selective_fill_rejects_non_clifford_graphs() {
1441 let x_basis_graph = GalleryItem::T
1442 .build()
1443 .materialize_root_graph()
1444 .expect("gallery flat projection");
1445 let x_basis = RuntimeStabilizerBasis::from_block_graph(&x_basis_graph).unwrap();
1446 let x_err = x_basis
1447 .apply_selective_fill(ivec3(1, 0, 2), crate::PauliBasis::X)
1448 .unwrap_err();
1449 assert!(matches!(
1450 x_err,
1451 RuntimeBasisError::NonCliffordFillUnsupported
1452 ));
1453
1454 let z_basis_graph = GalleryItem::TWithPreparedY
1455 .build()
1456 .materialize_root_graph()
1457 .expect("gallery flat projection");
1458 let z_basis = RuntimeStabilizerBasis::from_block_graph(&z_basis_graph).unwrap();
1459 let z_err = z_basis
1460 .apply_selective_fill(ivec3(2, 1, 2), crate::PauliBasis::Z)
1461 .unwrap_err();
1462 assert!(matches!(
1463 z_err,
1464 RuntimeBasisError::NonCliffordFillUnsupported
1465 ));
1466 }
1467
1468 #[test]
1469 fn selective_future_pipe_topology_is_rejected() {
1470 let graph = BlockGraph::from_blog_text(
1471 "BLOG 1.0\n\n 0: XZ [0,0,1]\n 1: ZXZ [0,0,2]\n 2: Port [0,0,3]\n [0,0,1] -> +Z\n [0,0,2] -> +Z\n",
1472 )
1473 .unwrap();
1474 RuntimeStabilizerBasis::from_block_graph(&graph).unwrap_err();
1475 }
1476
1477 #[test]
1478 fn tagged_selective_fill_preserves_remaining_selective_fixing_rows() {
1479 let filled_pos = ivec3(0, 0, 0);
1480 let remaining_pos = ivec3(1, 0, 0);
1481 let remaining_col = 1;
1482 let remaining_forbidden = Pauli::Z;
1483 let generators = vec![
1484 PauliString::try_from("Z_").unwrap(),
1485 PauliString::try_from("_Z").unwrap(),
1486 PauliString::try_from("Y_").unwrap(),
1487 PauliString::try_from("_X").unwrap(),
1488 ];
1489 let row_kinds = vec![
1490 super::super::stabilizer::StabilizerRowKind::SelectiveFixing {
1491 targets: vec![super::super::stabilizer::SelectiveFixingTarget {
1492 pos: filled_pos,
1493 forbidden: Pauli::Z,
1494 }],
1495 },
1496 super::super::stabilizer::StabilizerRowKind::SelectiveFixing {
1497 targets: vec![super::super::stabilizer::SelectiveFixingTarget {
1498 pos: remaining_pos,
1499 forbidden: remaining_forbidden,
1500 }],
1501 },
1502 super::super::stabilizer::StabilizerRowKind::Logical,
1503 super::super::stabilizer::StabilizerRowKind::Logical,
1504 ];
1505
1506 let basis = super::super::stabilizer::StabilizerBasis::new(generators, row_kinds);
1507 let (result, _) = basis
1508 .apply_tagged_selective_fill(&[(0, Pauli::X)], filled_pos, crate::SelectiveKind::XY)
1509 .unwrap();
1510
1511 assert!(
1512 result
1513 .kinds
1514 .iter()
1515 .all(|kind| !kind.fixes_selective(filled_pos))
1516 );
1517 assert!(result.rows.iter().zip(&result.kinds).any(|(row, kind)| {
1518 kind.fixes_selective(remaining_pos) && row.get(remaining_col) == remaining_forbidden
1519 }));
1520 }
1521
1522 #[test]
1523 fn output_correction_rows_cover_requested_outputs() {
1524 let graph = GalleryItem::CNOT
1525 .build()
1526 .materialize_root_graph()
1527 .expect("gallery flat projection");
1528 let basis = RuntimeStabilizerBasis::from_block_graph(&graph).unwrap();
1529 let outputs = [ivec3(0, 0, 3), ivec3(1, 1, 3)];
1530 let joints = basis.derive_output_correction_rows(&outputs).unwrap();
1531
1532 assert!(!joints.is_empty());
1533 for output in outputs {
1534 assert!(joints.iter().any(|joint| {
1535 joint
1536 .output_support
1537 .iter()
1538 .any(|&(supported, _)| supported == output)
1539 }));
1540 }
1541 assert!(
1542 joints
1543 .iter()
1544 .all(|joint| !joint.readout_ordinals.is_empty())
1545 );
1546 }
1547
1548 #[test]
1549 fn output_projection_past_i32_max_layer_is_representable() {
1550 let graph = GalleryItem::CNOT
1551 .build()
1552 .materialize_root_graph()
1553 .expect("gallery flat projection");
1554 let max_z = *graph.spans().unwrap().2.end();
1555 let shifted = graph
1556 .shift_positions(ivec3(0, 0, i32::MAX - max_z))
1557 .unwrap();
1558 let zx = crate::ZXGraph::try_from(&shifted).unwrap();
1559 assert_eq!(
1560 zx.nodes().iter().map(|node| node.pos.z).max(),
1561 Some(i32::MAX)
1562 );
1563 let outputs = zx.output_ports();
1564 let basis = RuntimeStabilizerBasis::from_zx_graph(zx).unwrap();
1565
1566 assert!(
1567 !basis
1568 .derive_output_correction_rows(&outputs)
1569 .unwrap()
1570 .is_empty()
1571 );
1572 }
1573
1574 #[test]
1575 fn output_correction_rows_report_unknown_output() {
1576 let graph = GalleryItem::T
1577 .build()
1578 .materialize_root_graph()
1579 .expect("gallery flat projection");
1580 let basis = RuntimeStabilizerBasis::from_block_graph(&graph).unwrap();
1581 let err = basis
1582 .derive_output_correction_rows(&[ivec3(99, 99, 99)])
1583 .expect_err("missing output should error");
1584
1585 assert!(matches!(err, RuntimeBasisError::UnknownOutput(pos) if pos == ivec3(99, 99, 99)));
1586 }
1587
1588 #[test]
1589 fn ccz_factory_runtime_basis_derives_mx1357_surface() {
1590 let graph = GalleryItem::CCZFactoryWithTels
1591 .build()
1592 .materialize_root_graph()
1593 .expect("gallery flat projection");
1594 let basis = RuntimeStabilizerBasis::from_block_graph(&graph).unwrap();
1595 let derived = basis.derive_measurement_surface("mx1357", 7).unwrap();
1596
1597 assert!(!derived.readout_ordinals.is_empty());
1598 assert!(
1599 derived
1600 .stabilizer
1601 .interior_nodes
1602 .contains_key(&ivec3(2, 0, 6))
1603 );
1604 }
1605}