Skip to main content

cqlib_core/circuit/control_flow/
switch_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, ClassicalValue, ClassicalVar, Qubit};
16use std::collections::BTreeSet;
17
18/// Exact-value switch case.
19#[derive(Debug, Clone, PartialEq)]
20pub struct SwitchCase {
21    value: u128,
22    body: ControlBody,
23}
24
25impl SwitchCase {
26    /// Creates an exact-value case with its body.
27    pub fn new(value: u128, body: ControlBody) -> Self {
28        Self { value, body }
29    }
30
31    /// Returns the exact value matched by this case.
32    pub fn value(&self) -> u128 {
33        self.value
34    }
35
36    /// Returns the body executed when this case matches.
37    pub fn body(&self) -> &ControlBody {
38        &self.body
39    }
40}
41
42/// Exact-value switch over an unsigned classical expression.
43#[derive(Debug, Clone, PartialEq)]
44pub struct SwitchOp {
45    target: ClassicalExpr,
46    cases: Box<[SwitchCase]>,
47    default: Option<ControlBody>,
48}
49
50impl SwitchOp {
51    /// Creates a switch without fallthrough.
52    pub fn new(
53        target: ClassicalExpr,
54        cases: Vec<SwitchCase>,
55        default: Option<ControlBody>,
56    ) -> Result<Self, CircuitError> {
57        super::validate_switch(&target, cases.iter().map(|case| case.value))?;
58
59        Ok(Self {
60            target,
61            cases: cases.into_boxed_slice(),
62            default,
63        })
64    }
65
66    /// Returns the unsigned expression being matched.
67    pub fn target(&self) -> &ClassicalExpr {
68        &self.target
69    }
70
71    /// Returns exact-value cases in source order.
72    pub fn cases(&self) -> &[SwitchCase] {
73        &self.cases
74    }
75
76    /// Returns the optional default body.
77    pub fn default(&self) -> Option<&ControlBody> {
78        self.default.as_ref()
79    }
80
81    /// Returns mutable variables read by the target expression.
82    pub fn classical_var_reads(&self) -> BTreeSet<ClassicalVar> {
83        self.target.vars()
84    }
85
86    /// Returns immutable values read by the target expression.
87    pub fn classical_value_reads(&self) -> BTreeSet<ClassicalValue> {
88        self.target.values()
89    }
90
91    /// Returns qubits used by all case and default bodies.
92    pub fn used_qubits(&self) -> BTreeSet<Qubit> {
93        let mut qubits = BTreeSet::new();
94        for case in self.cases.iter() {
95            qubits.extend(case.body.used_qubits());
96        }
97        if let Some(default) = &self.default {
98            qubits.extend(default.used_qubits());
99        }
100        qubits
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::{SwitchCase, SwitchOp};
107    use crate::circuit::{CircuitId, ClassicalExpr, ClassicalType, ClassicalValue, ControlBody};
108
109    #[test]
110    fn switch_requires_uint_target_and_valid_unique_cases() {
111        assert!(
112            SwitchOp::new(
113                ClassicalExpr::uint_literal(3, 0).unwrap(),
114                vec![
115                    SwitchCase::new(1, ControlBody::new(vec![])),
116                    SwitchCase::new(7, ControlBody::new(vec![])),
117                ],
118                None,
119            )
120            .is_ok()
121        );
122
123        assert!(
124            SwitchOp::new(ClassicalExpr::bit_vec_literal(3, 0).unwrap(), vec![], None,).is_err()
125        );
126        assert!(
127            SwitchOp::new(
128                ClassicalExpr::uint_literal(3, 0).unwrap(),
129                vec![SwitchCase::new(8, ControlBody::new(vec![]))],
130                None,
131            )
132            .is_err()
133        );
134        assert!(
135            SwitchOp::new(
136                ClassicalExpr::uint_literal(3, 0).unwrap(),
137                vec![
138                    SwitchCase::new(1, ControlBody::new(vec![])),
139                    SwitchCase::new(1, ControlBody::new(vec![])),
140                ],
141                None,
142            )
143            .is_err()
144        );
145    }
146
147    #[test]
148    fn switch_reports_target_value_reads() {
149        let value = ClassicalValue::new(CircuitId::new(), 5, ClassicalType::uint(3).unwrap());
150        let op = SwitchOp::new(value.expr(), vec![], None).unwrap();
151
152        assert!(op.classical_var_reads().is_empty());
153        assert!(op.classical_value_reads().contains(&value));
154    }
155}