Skip to main content

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}