Skip to main content

cqlib_core/circuit/control_flow/
while_op.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 super::ControlBody;
14use crate::circuit::classical_expr::ClassicalExpr;
15use crate::circuit::{CircuitError, ClassicalType, ClassicalValue, ClassicalVar, Qubit};
16use std::collections::BTreeSet;
17
18/// Loop controlled by a boolean classical expression.
19#[derive(Debug, Clone, PartialEq)]
20pub struct WhileOp {
21    condition: ClassicalExpr,
22    body: ControlBody,
23}
24
25impl WhileOp {
26    /// Creates an expression-based `while` operation.
27    pub fn new(condition: ClassicalExpr, body: ControlBody) -> Result<Self, CircuitError> {
28        if condition.ty() != ClassicalType::Bool {
29            return Err(CircuitError::InvalidOperation(format!(
30                "while condition must be Bool, got {:?}",
31                condition.ty()
32            )));
33        }
34        Ok(Self { condition, body })
35    }
36
37    /// Returns the boolean loop condition.
38    pub fn condition(&self) -> &ClassicalExpr {
39        &self.condition
40    }
41
42    /// Returns the loop body.
43    pub fn body(&self) -> &ControlBody {
44        &self.body
45    }
46
47    /// Returns mutable variables read by the condition.
48    pub fn classical_var_reads(&self) -> BTreeSet<ClassicalVar> {
49        self.condition.vars()
50    }
51
52    /// Returns immutable values read by the condition.
53    pub fn classical_value_reads(&self) -> BTreeSet<ClassicalValue> {
54        self.condition.values()
55    }
56
57    /// Returns qubits used by the loop body.
58    pub fn used_qubits(&self) -> BTreeSet<Qubit> {
59        self.body.used_qubits()
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::WhileOp;
66    use crate::circuit::{
67        CircuitId, ClassicalExpr, ClassicalType, ClassicalValue, ClassicalVar, ControlBody,
68    };
69
70    #[test]
71    fn while_requires_bool_condition() {
72        assert!(WhileOp::new(ClassicalExpr::bool_literal(true), ControlBody::new(vec![])).is_ok());
73        assert!(WhileOp::new(ClassicalExpr::bit_literal(true), ControlBody::new(vec![])).is_err());
74    }
75
76    #[test]
77    fn while_reports_condition_reads() {
78        let circuit_id = CircuitId::new();
79        let bit = ClassicalVar::new(circuit_id, 3, ClassicalType::Bit);
80        let value = ClassicalValue::new(circuit_id, 4, ClassicalType::Bit);
81        let condition = ClassicalExpr::try_and(
82            ClassicalExpr::bit_to_bool(bit.expr()).unwrap(),
83            ClassicalExpr::bit_to_bool(value.expr()).unwrap(),
84        )
85        .unwrap();
86        let op = WhileOp::new(condition, ControlBody::new(vec![])).unwrap();
87
88        assert!(op.classical_var_reads().contains(&bit));
89        assert!(op.classical_value_reads().contains(&value));
90    }
91}