Skip to main content

cqlib_core/circuit/control_flow/
body.rs

1// This code is part of Cqlib.
2//
3// (C) Copyright China Telecom Quantum Group 2026
4//
5// This code is licensed under the Apache License, Version 2.0. You may
6// obtain a copy of this license in the LICENSE.txt file in the root directory
7// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8//
9// Any modifications or derivative works of this code must retain this
10// copyright notice, and modified files need to carry a notice indicating
11// that they have been altered from the originals.
12
13use crate::circuit::{ClassicalValue, Operation, Qubit};
14use std::collections::BTreeSet;
15use std::sync::Arc;
16
17/// Structured control-flow body.
18#[derive(Debug, Clone, PartialEq)]
19pub struct ControlBody {
20    operations: Arc<Vec<Operation>>,
21}
22
23impl ControlBody {
24    /// Creates a body from a sequence of operations.
25    pub fn new(operations: Vec<Operation>) -> Self {
26        Self {
27            operations: Arc::new(operations),
28        }
29    }
30
31    /// Returns the body operations.
32    pub fn operations(&self) -> &[Operation] {
33        self.operations.as_slice()
34    }
35
36    /// Returns all qubits referenced directly by this body.
37    pub fn used_qubits(&self) -> BTreeSet<Qubit> {
38        let mut qubits = BTreeSet::new();
39        for operation in self.operations() {
40            qubits.extend(operation.qubits.iter().copied());
41        }
42        qubits
43    }
44
45    /// Returns true when this body contains a measurement operation.
46    pub fn has_measurement(&self) -> bool {
47        self.operations()
48            .iter()
49            .any(|operation| operation.instruction.has_measurement())
50    }
51
52    /// Returns true when this body reads `value`.
53    pub fn reads_value(&self, value: ClassicalValue) -> bool {
54        self.operations()
55            .iter()
56            .any(|operation| operation.instruction.reads_value(value))
57    }
58}
59
60impl From<Vec<Operation>> for ControlBody {
61    fn from(operations: Vec<Operation>) -> Self {
62        Self::new(operations)
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::ControlBody;
69    use crate::circuit::{
70        CircuitId, ClassicalDataOp, ClassicalExpr, ClassicalType, ClassicalValue,
71        ControlBody as Body, IfOp, Instruction, Operation, Qubit, StandardGate,
72    };
73    use smallvec::smallvec;
74
75    #[test]
76    fn body_reports_directly_used_qubits() {
77        let body = ControlBody::new(vec![
78            Operation {
79                instruction: Instruction::Standard(StandardGate::CX),
80                qubits: smallvec![Qubit::new(0), Qubit::new(2)],
81                params: smallvec![],
82                label: None,
83            },
84            Operation {
85                instruction: Instruction::Standard(StandardGate::H),
86                qubits: smallvec![Qubit::new(1)],
87                params: smallvec![],
88                label: None,
89            },
90        ]);
91
92        let qubits = body.used_qubits();
93        assert_eq!(qubits.len(), 3);
94        assert!(qubits.contains(&Qubit::new(0)));
95        assert!(qubits.contains(&Qubit::new(1)));
96        assert!(qubits.contains(&Qubit::new(2)));
97    }
98
99    #[test]
100    fn body_reports_nested_measurements() {
101        let value = ClassicalValue::new(CircuitId::new(), 0, ClassicalType::Bit);
102        let inner = Body::new(vec![Operation {
103            instruction: Instruction::ClassicalData(ClassicalDataOp::MeasureBit { result: value }),
104            qubits: smallvec![Qubit::new(0)],
105            params: smallvec![],
106            label: None,
107        }]);
108        let op = IfOp::new(ClassicalExpr::bool_literal(true), inner, None).unwrap();
109        let body = ControlBody::new(vec![Operation {
110            instruction: Instruction::ClassicalControl(super::super::ClassicalControlOp::If(op)),
111            qubits: smallvec![],
112            params: smallvec![],
113            label: None,
114        }]);
115
116        assert!(body.has_measurement());
117    }
118
119    #[test]
120    fn body_reports_nested_value_reads() {
121        let circuit_id = CircuitId::new();
122        let value = ClassicalValue::new(circuit_id, 0, ClassicalType::Bit);
123        let target = crate::circuit::ClassicalVar::new(circuit_id, 1, ClassicalType::Bit);
124        let inner = Body::new(vec![Operation {
125            instruction: Instruction::ClassicalData(ClassicalDataOp::Store {
126                target,
127                value: value.expr(),
128            }),
129            qubits: smallvec![],
130            params: smallvec![],
131            label: None,
132        }]);
133        let op = IfOp::new(ClassicalExpr::bool_literal(true), inner, None).unwrap();
134        let body = ControlBody::new(vec![Operation {
135            instruction: Instruction::ClassicalControl(super::super::ClassicalControlOp::If(op)),
136            qubits: smallvec![],
137            params: smallvec![],
138            label: None,
139        }]);
140
141        assert!(body.reads_value(value));
142    }
143}