bloq_ir/ir/frame.rs
1use std::collections::BTreeMap;
2
3use bloq_circuit::{Basis, PauliMap};
4use glam::IVec3;
5
6use super::{Bloq, BloqNodeId, NodeProvenance, TemplateInstanceId};
7
8/// Logical Pauli-frame sign nodes for one terminal output.
9///
10/// These are the `Compute` nodes carrying the X- and Z-frame corrections a frame consumer
11/// applies at the output port. Produced by the symbolic GF(2) solve of the
12/// joint output-correction system.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct FramePair {
15 /// The source output port this frame corrects.
16 pub port: IVec3,
17 /// The `Compute` node whose bit is the X-frame sign.
18 pub x: BloqNodeId,
19 /// The `Compute` node whose bit is the Z-frame sign.
20 pub z: BloqNodeId,
21}
22
23/// Terminal logical Pauli operators for one program output.
24///
25/// The compiler records these in layout-global coordinates so an executor can
26/// inspect or teleport an output using the saved IR alone. The owning instance
27/// also identifies the output cut gated by shot-scoped discards.
28#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
29pub struct LogicalOutput {
30 /// The source output port.
31 pub port: IVec3,
32 /// Instance owning this output cut; coordinates may be reused by later instances.
33 /// Its membership and enclosing region selections must be unconditional (WF-7).
34 pub instance: TemplateInstanceId,
35 /// Logical X operator in layout-global coordinates.
36 pub x: PauliMap,
37 /// Logical Z operator in layout-global coordinates.
38 pub z: PauliMap,
39}
40
41/// Input-port metadata for physical preparation hooks.
42#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
43pub struct LogicalInput {
44 /// The source input port.
45 pub port: IVec3,
46 /// Port instance whose template owns the patch stabilizers.
47 /// Its membership and enclosing region selections must be unconditional (WF-7).
48 pub instance: TemplateInstanceId,
49 /// Logical X operator in layout-global coordinates.
50 pub x: PauliMap,
51 /// Logical Z operator in layout-global coordinates.
52 pub z: PauliMap,
53}
54
55impl Bloq {
56 /// Replace the logical-input metadata.
57 pub fn set_logical_inputs(&mut self, inputs: Vec<LogicalInput>) {
58 self.logical_inputs = inputs;
59 }
60
61 /// Logical input patches in deterministic source-port order.
62 pub fn logical_inputs(&self) -> &[LogicalInput] {
63 &self.logical_inputs
64 }
65
66 /// The per-output terminal Pauli-frame signs: one [`FramePair`] per
67 /// source output port, in `(x, y, z)` order. Derived from the
68 /// [`NodeProvenance::OutputFrame`] stamps on frame-sign `Compute` nodes —
69 /// the stamps are the single source of truth, so this table cannot drift
70 /// from the graph. Validation guarantees every stamped output carries
71 /// exactly one X and one Z stamp; on an unvalidated program an incomplete
72 /// pair is skipped.
73 // Spec rule WF-15.
74 pub fn output_frames(&self) -> Vec<FramePair> {
75 let mut stamps = BTreeMap::new();
76 for (id, node) in self.nodes() {
77 if let NodeProvenance::OutputFrame { port, basis } = &node.provenance {
78 let entry: &mut (Option<BloqNodeId>, Option<BloqNodeId>) =
79 stamps.entry((port.x, port.y, port.z)).or_default();
80 match basis {
81 Basis::X => entry.0 = Some(id),
82 Basis::Z => entry.1 = Some(id),
83 }
84 }
85 }
86 stamps
87 .into_iter()
88 .filter_map(|((x, y, z), (frame_x, frame_z))| {
89 Some(FramePair {
90 port: IVec3::new(x, y, z),
91 x: frame_x?,
92 z: frame_z?,
93 })
94 })
95 .collect()
96 }
97
98 /// Replace the terminal logical-output metadata.
99 pub fn set_logical_outputs(&mut self, outputs: Vec<LogicalOutput>) {
100 self.logical_outputs = outputs;
101 }
102
103 /// Terminal logical operators in deterministic output order.
104 pub fn logical_outputs(&self) -> &[LogicalOutput] {
105 &self.logical_outputs
106 }
107}