use super::ControlBody;
use crate::circuit::classical_expr::ClassicalExpr;
use crate::circuit::{CircuitError, ClassicalValue, ClassicalVar, Qubit};
use std::collections::BTreeSet;
#[derive(Debug, Clone, PartialEq)]
pub struct SwitchCase {
value: u128,
body: ControlBody,
}
impl SwitchCase {
pub fn new(value: u128, body: ControlBody) -> Self {
Self { value, body }
}
pub fn value(&self) -> u128 {
self.value
}
pub fn body(&self) -> &ControlBody {
&self.body
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SwitchOp {
target: ClassicalExpr,
cases: Box<[SwitchCase]>,
default: Option<ControlBody>,
}
impl SwitchOp {
pub fn new(
target: ClassicalExpr,
cases: Vec<SwitchCase>,
default: Option<ControlBody>,
) -> Result<Self, CircuitError> {
super::validate_switch(&target, cases.iter().map(|case| case.value))?;
Ok(Self {
target,
cases: cases.into_boxed_slice(),
default,
})
}
pub fn target(&self) -> &ClassicalExpr {
&self.target
}
pub fn cases(&self) -> &[SwitchCase] {
&self.cases
}
pub fn default(&self) -> Option<&ControlBody> {
self.default.as_ref()
}
pub fn classical_var_reads(&self) -> BTreeSet<ClassicalVar> {
self.target.vars()
}
pub fn classical_value_reads(&self) -> BTreeSet<ClassicalValue> {
self.target.values()
}
pub fn used_qubits(&self) -> BTreeSet<Qubit> {
let mut qubits = BTreeSet::new();
for case in self.cases.iter() {
qubits.extend(case.body.used_qubits());
}
if let Some(default) = &self.default {
qubits.extend(default.used_qubits());
}
qubits
}
}
#[cfg(test)]
mod tests {
use super::{SwitchCase, SwitchOp};
use crate::circuit::{CircuitId, ClassicalExpr, ClassicalType, ClassicalValue, ControlBody};
#[test]
fn switch_requires_uint_target_and_valid_unique_cases() {
assert!(
SwitchOp::new(
ClassicalExpr::uint_literal(3, 0).unwrap(),
vec![
SwitchCase::new(1, ControlBody::new(vec![])),
SwitchCase::new(7, ControlBody::new(vec![])),
],
None,
)
.is_ok()
);
assert!(
SwitchOp::new(ClassicalExpr::bit_vec_literal(3, 0).unwrap(), vec![], None,).is_err()
);
assert!(
SwitchOp::new(
ClassicalExpr::uint_literal(3, 0).unwrap(),
vec![SwitchCase::new(8, ControlBody::new(vec![]))],
None,
)
.is_err()
);
assert!(
SwitchOp::new(
ClassicalExpr::uint_literal(3, 0).unwrap(),
vec![
SwitchCase::new(1, ControlBody::new(vec![])),
SwitchCase::new(1, ControlBody::new(vec![])),
],
None,
)
.is_err()
);
}
#[test]
fn switch_reports_target_value_reads() {
let value = ClassicalValue::new(CircuitId::new(), 5, ClassicalType::uint(3).unwrap());
let op = SwitchOp::new(value.expr(), vec![], None).unwrap();
assert!(op.classical_var_reads().is_empty());
assert!(op.classical_value_reads().contains(&value));
}
}