use crate::circuit::ClassicalControlOp;
use crate::circuit::ClassicalType;
use crate::circuit::circuit_param::{CircuitParam, ParameterValue};
use crate::circuit::classical::{ClassicalValue, ClassicalVar};
use crate::circuit::classical_expr::ClassicalExpr;
use crate::circuit::control_flow::{ControlBody, validate_for_types, validate_switch};
use crate::circuit::error::CircuitError;
use crate::circuit::gate::circuit_gate::CircuitGate;
use crate::circuit::gate::directive::Directive;
use crate::circuit::gate::instruction::Instruction;
use crate::circuit::gate::standard_gate::StandardGate;
use crate::circuit::gate::{ClassicalDataOp, MCGate, UnitaryGate};
use crate::circuit::operation::{Operation, ValueOperation};
use alloc::collections::BTreeSet;
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub struct ValueControlBody {
operations: Vec<ValueOperation>,
}
impl ValueControlBody {
pub fn new(operations: Vec<ValueOperation>) -> Self {
Self { operations }
}
pub fn operations(&self) -> &[ValueOperation] {
self.operations.as_slice()
}
pub fn used_qubits(&self) -> BTreeSet<crate::circuit::Qubit> {
let mut qubits = BTreeSet::new();
for operation in &self.operations {
qubits.extend(operation.qubits.iter().copied());
if let ValueInstruction::ClassicalControl(control) = &operation.instruction {
qubits.extend(control.used_qubits());
}
}
qubits
}
pub fn has_measurement(&self) -> bool {
self.operations
.iter()
.any(|operation| match &operation.instruction {
ValueInstruction::Instruction(instruction) => instruction.has_measurement(),
ValueInstruction::ClassicalControl(control) => control.has_measurement(),
})
}
pub fn reads_value(&self, value: ClassicalValue) -> bool {
self.operations
.iter()
.any(|operation| match &operation.instruction {
ValueInstruction::Instruction(instruction) => instruction.reads_value(value),
ValueInstruction::ClassicalControl(control) => control.reads_value(value),
})
}
}
impl From<Vec<ValueOperation>> for ValueControlBody {
fn from(operations: Vec<ValueOperation>) -> Self {
Self::new(operations)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ValueSwitchCase {
pub value: u128,
pub body: ValueControlBody,
}
impl ValueSwitchCase {
pub fn new(value: u128, body: ValueControlBody) -> Self {
Self { value, body }
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ValueClassicalControlOp {
If {
condition: ClassicalExpr,
then_body: ValueControlBody,
else_body: Option<ValueControlBody>,
},
While {
condition: ClassicalExpr,
body: ValueControlBody,
},
For {
var: ClassicalVar,
start: ClassicalExpr,
stop: ClassicalExpr,
step: ClassicalExpr,
body: ValueControlBody,
},
Switch {
target: ClassicalExpr,
cases: Vec<ValueSwitchCase>,
default: Option<ValueControlBody>,
},
Break,
Continue,
}
impl ValueClassicalControlOp {
pub fn new_if(
condition: ClassicalExpr,
then_body: ValueControlBody,
else_body: Option<ValueControlBody>,
) -> Result<Self, CircuitError> {
if condition.ty() != ClassicalType::Bool {
return Err(CircuitError::InvalidOperation(format!(
"if condition must be Bool, got {:?}",
condition.ty()
)));
}
Ok(Self::If {
condition,
then_body,
else_body,
})
}
pub fn new_while(
condition: ClassicalExpr,
body: ValueControlBody,
) -> Result<Self, CircuitError> {
if condition.ty() != ClassicalType::Bool {
return Err(CircuitError::InvalidOperation(format!(
"while condition must be Bool, got {:?}",
condition.ty()
)));
}
Ok(Self::While { condition, body })
}
pub fn new_for(
var: ClassicalVar,
start: ClassicalExpr,
stop: ClassicalExpr,
step: ClassicalExpr,
body: ValueControlBody,
) -> Result<Self, CircuitError> {
validate_for_types(var, &start, &stop, &step)?;
Ok(Self::For {
var,
start,
stop,
step,
body,
})
}
pub fn new_switch(
target: ClassicalExpr,
cases: Vec<ValueSwitchCase>,
default: Option<ValueControlBody>,
) -> Result<Self, CircuitError> {
validate_switch(&target, cases.iter().map(|case| case.value))?;
Ok(Self::Switch {
target,
cases,
default,
})
}
pub fn used_qubits(&self) -> BTreeSet<crate::circuit::Qubit> {
let mut qubits = BTreeSet::new();
match self {
Self::If {
then_body,
else_body,
..
} => {
qubits.extend(then_body.used_qubits());
if let Some(else_body) = else_body {
qubits.extend(else_body.used_qubits());
}
}
Self::While { body, .. } | Self::For { body, .. } => {
qubits.extend(body.used_qubits());
}
Self::Switch { cases, default, .. } => {
for case in cases {
qubits.extend(case.body.used_qubits());
}
if let Some(default) = default {
qubits.extend(default.used_qubits());
}
}
Self::Break | Self::Continue => {}
}
qubits
}
pub fn classical_var_reads(&self) -> Vec<ClassicalVar> {
match self {
Self::If { condition, .. } | Self::While { condition, .. } => {
condition.vars().into_iter().collect()
}
Self::For {
start, stop, step, ..
} => {
let mut vars: Vec<_> = start.vars().into_iter().collect();
vars.extend(stop.vars());
vars.extend(step.vars());
vars
}
Self::Switch { target, .. } => target.vars().into_iter().collect(),
Self::Break | Self::Continue => Vec::new(),
}
}
pub fn classical_value_reads(&self) -> Vec<crate::circuit::ClassicalValue> {
match self {
Self::If { condition, .. } | Self::While { condition, .. } => {
condition.values().into_iter().collect()
}
Self::For {
start, stop, step, ..
} => {
let mut vals: Vec<_> = start.values().into_iter().collect();
vals.extend(stop.values());
vals.extend(step.values());
vals
}
Self::Switch { target, .. } => target.values().into_iter().collect(),
Self::Break | Self::Continue => Vec::new(),
}
}
pub fn classical_writes(&self) -> Vec<ClassicalVar> {
match self {
Self::For { var, .. } => vec![*var],
_ => Vec::new(),
}
}
pub fn has_measurement(&self) -> bool {
match self {
Self::If {
then_body,
else_body,
..
} => {
then_body.has_measurement()
|| else_body
.as_ref()
.is_some_and(|body| body.has_measurement())
}
Self::While { body, .. } | Self::For { body, .. } => body.has_measurement(),
Self::Switch { cases, default, .. } => {
cases.iter().any(|case| case.body.has_measurement())
|| default.as_ref().is_some_and(|body| body.has_measurement())
}
Self::Break | Self::Continue => false,
}
}
pub fn reads_value(&self, value: ClassicalValue) -> bool {
if self.classical_value_reads().contains(&value) {
return true;
}
match self {
Self::If {
then_body,
else_body,
..
} => {
then_body.reads_value(value)
|| else_body
.as_ref()
.is_some_and(|body| body.reads_value(value))
}
Self::While { body, .. } | Self::For { body, .. } => body.reads_value(value),
Self::Switch { cases, default, .. } => {
cases.iter().any(|case| case.body.reads_value(value))
|| default.as_ref().is_some_and(|body| body.reads_value(value))
}
Self::Break | Self::Continue => false,
}
}
}
impl fmt::Display for ValueClassicalControlOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::If { .. } => write!(f, "if"),
Self::While { .. } => write!(f, "while"),
Self::For { .. } => write!(f, "for"),
Self::Switch { .. } => write!(f, "switch"),
Self::Break => write!(f, "break"),
Self::Continue => write!(f, "continue"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ValueInstruction {
Instruction(Instruction),
ClassicalControl(ValueClassicalControlOp),
}
impl ValueInstruction {
pub fn from_instruction(inst: Instruction) -> Self {
debug_assert!(
!matches!(inst, Instruction::ClassicalControl(_)),
"Instruction::ClassicalControl must use ValueInstruction::ClassicalControl variant"
);
Self::Instruction(inst)
}
pub fn is_classical_control(&self) -> bool {
matches!(self, Self::ClassicalControl(_))
}
pub fn is_instruction(&self) -> bool {
matches!(self, Self::Instruction(_))
}
pub fn name(&self) -> String {
match self {
Self::Instruction(instruction) => instruction.name(),
Self::ClassicalControl(control) => control.to_string(),
}
}
pub fn instruction_type(&self) -> &'static str {
match self {
Self::Instruction(instruction) => instruction.instruction_type(),
Self::ClassicalControl(_) => "classical_control",
}
}
pub fn is_standard(&self) -> bool {
matches!(self, Self::Instruction(instruction) if instruction.is_standard())
}
pub fn is_mcgate(&self) -> bool {
matches!(self, Self::Instruction(instruction) if instruction.is_mcgate())
}
pub fn is_unitary(&self) -> bool {
matches!(self, Self::Instruction(instruction) if instruction.is_unitary())
}
pub fn is_circuit_gate(&self) -> bool {
matches!(self, Self::Instruction(instruction) if instruction.is_circuit_gate())
}
pub fn is_directive(&self) -> bool {
matches!(self, Self::Instruction(instruction) if instruction.is_directive())
}
pub fn is_classical_data(&self) -> bool {
matches!(self, Self::Instruction(instruction) if instruction.is_classical_data())
}
pub fn is_delay(&self) -> bool {
matches!(self, Self::Instruction(instruction) if instruction.is_delay())
}
pub fn standard_gate(&self) -> Option<StandardGate> {
self.as_instruction().and_then(Instruction::standard_gate)
}
pub fn directive(&self) -> Option<Directive> {
self.as_instruction().and_then(Instruction::directive)
}
pub fn mc_gate(&self) -> Option<&MCGate> {
self.as_instruction().and_then(Instruction::mc_gate)
}
pub fn unitary_gate(&self) -> Option<&UnitaryGate> {
self.as_instruction().and_then(Instruction::unitary_gate)
}
pub fn circuit_gate(&self) -> Option<&CircuitGate> {
self.as_instruction().and_then(Instruction::circuit_gate)
}
pub fn classical_data(&self) -> Option<&ClassicalDataOp> {
self.as_instruction().and_then(Instruction::classical_data)
}
pub fn classical_control(&self) -> Option<&ValueClassicalControlOp> {
match self {
Self::Instruction(_) => None,
Self::ClassicalControl(op) => Some(op),
}
}
pub fn as_instruction(&self) -> Option<&Instruction> {
match self {
Self::Instruction(inst) => Some(inst),
Self::ClassicalControl(_) => None,
}
}
pub fn into_instruction(self) -> Option<Instruction> {
match self {
Self::Instruction(inst) => Some(inst),
Self::ClassicalControl(_) => None,
}
}
}
impl From<Instruction> for ValueInstruction {
fn from(inst: Instruction) -> Self {
Self::from_instruction(inst)
}
}
impl From<ValueClassicalControlOp> for ValueInstruction {
fn from(op: ValueClassicalControlOp) -> Self {
Self::ClassicalControl(op)
}
}
impl fmt::Display for ValueInstruction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Instruction(inst) => write!(f, "{}", inst),
Self::ClassicalControl(vcc) => write!(f, "{}", vcc),
}
}
}
pub fn storage_operation_to_value<F>(
operation: Operation,
resolve_parameter: &F,
) -> Result<ValueOperation, CircuitError>
where
F: Fn(&CircuitParam) -> Result<ParameterValue, CircuitError>,
{
fn convert_operation<F>(
operation: Operation,
resolve_parameter: &F,
) -> Result<ValueOperation, CircuitError>
where
F: Fn(&CircuitParam) -> Result<ParameterValue, CircuitError>,
{
let params = operation
.params
.iter()
.map(resolve_parameter)
.collect::<Result<_, _>>()?;
Ok(ValueOperation {
instruction: convert_instruction(operation.instruction, resolve_parameter)?,
qubits: operation.qubits,
params,
label: operation.label,
})
}
fn convert_body<F>(
body: &ControlBody,
resolve_parameter: &F,
) -> Result<ValueControlBody, CircuitError>
where
F: Fn(&CircuitParam) -> Result<ParameterValue, CircuitError>,
{
body.operations()
.iter()
.cloned()
.map(|operation| convert_operation(operation, resolve_parameter))
.collect::<Result<Vec<_>, _>>()
.map(ValueControlBody::new)
}
fn convert_instruction<F>(
instruction: Instruction,
resolve_parameter: &F,
) -> Result<ValueInstruction, CircuitError>
where
F: Fn(&CircuitParam) -> Result<ParameterValue, CircuitError>,
{
let Instruction::ClassicalControl(op) = instruction else {
return Ok(ValueInstruction::Instruction(instruction));
};
let op = match op {
ClassicalControlOp::If(op) => ValueClassicalControlOp::If {
condition: op.condition().clone(),
then_body: convert_body(op.then_body(), resolve_parameter)?,
else_body: op
.else_body()
.map(|body| convert_body(body, resolve_parameter))
.transpose()?,
},
ClassicalControlOp::While(op) => ValueClassicalControlOp::While {
condition: op.condition().clone(),
body: convert_body(op.body(), resolve_parameter)?,
},
ClassicalControlOp::For(op) => ValueClassicalControlOp::For {
var: op.var(),
start: op.start().clone(),
stop: op.stop().clone(),
step: op.step().clone(),
body: convert_body(op.body(), resolve_parameter)?,
},
ClassicalControlOp::Switch(op) => ValueClassicalControlOp::Switch {
target: op.target().clone(),
cases: op
.cases()
.iter()
.map(|case| {
Ok(ValueSwitchCase::new(
case.value(),
convert_body(case.body(), resolve_parameter)?,
))
})
.collect::<Result<_, CircuitError>>()?,
default: op
.default()
.map(|body| convert_body(body, resolve_parameter))
.transpose()?,
},
ClassicalControlOp::Break => ValueClassicalControlOp::Break,
ClassicalControlOp::Continue => ValueClassicalControlOp::Continue,
};
Ok(ValueInstruction::ClassicalControl(op))
}
convert_operation(operation, resolve_parameter)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::circuit::ClassicalType;
use crate::circuit::bit::Qubit;
use crate::circuit::gate::{ClassicalDataOp, Directive, StandardGate};
use std::num::NonZeroU32;
#[test]
fn value_control_body_stores_value_operations() {
let ops = vec![ValueOperation::from_standard(
StandardGate::H,
[Qubit::new(0)],
[],
)];
let body = ValueControlBody::new(ops);
assert_eq!(body.operations().len(), 1);
}
#[test]
fn value_control_body_from_vec() {
let ops = vec![
ValueOperation::from_standard(StandardGate::X, [Qubit::new(0)], []),
ValueOperation::from_standard(StandardGate::Y, [Qubit::new(1)], []),
];
let body: ValueControlBody = ops.into();
assert_eq!(body.operations().len(), 2);
}
#[test]
fn value_instruction_from_gate_instruction() {
let vi = ValueInstruction::from_instruction(Instruction::Standard(StandardGate::H));
assert!(vi.is_instruction());
assert!(!vi.is_classical_control());
}
#[test]
fn value_instruction_from_classical_control() {
let vcc = ValueClassicalControlOp::Break;
let vi = ValueInstruction::from(vcc);
assert!(vi.is_classical_control());
assert!(!vi.is_instruction());
}
#[test]
fn value_instruction_exposes_instruction_queries() {
let standard = ValueInstruction::from_instruction(Instruction::Standard(StandardGate::H));
assert_eq!(standard.name(), "H");
assert_eq!(standard.instruction_type(), "standard");
assert!(standard.is_standard());
assert_eq!(standard.standard_gate(), Some(StandardGate::H));
assert_eq!(standard.directive(), None);
let directive =
ValueInstruction::from_instruction(Instruction::Directive(Directive::Barrier));
assert_eq!(directive.name(), "barrier");
assert_eq!(directive.to_string(), "barrier");
assert_eq!(directive.instruction_type(), "directive");
assert!(directive.is_directive());
assert_eq!(directive.directive(), Some(Directive::Barrier));
let value =
ClassicalValue::new(crate::circuit::CircuitId::default(), 0, ClassicalType::Bit);
let classical_data = ValueInstruction::from_instruction(Instruction::ClassicalData(
ClassicalDataOp::MeasureBit { result: value },
));
assert_eq!(classical_data.name(), "measure_bit");
assert_eq!(classical_data.instruction_type(), "classical_data");
assert!(classical_data.is_classical_data());
let control = ValueInstruction::from(ValueClassicalControlOp::Break);
assert_eq!(control.name(), "break");
assert_eq!(control.instruction_type(), "classical_control");
assert!(control.is_classical_control());
let delay = ValueInstruction::from_instruction(Instruction::Delay);
assert_eq!(delay.name(), "delay");
assert_eq!(delay.instruction_type(), "delay");
assert!(delay.is_delay());
}
#[test]
fn value_instruction_delegates_payload_accessors() {
let mc = ValueInstruction::from_instruction(Instruction::McGate(Box::new(MCGate::new(
2,
StandardGate::X,
))));
assert!(mc.is_mcgate());
let mc_gate = mc.mc_gate().unwrap();
assert_eq!(mc_gate.num_ctrl_qubits(), 2);
assert_eq!(mc_gate.base_gate(), &StandardGate::X);
assert!(mc.unitary_gate().is_none());
assert!(mc.circuit_gate().is_none());
assert!(mc.classical_data().is_none());
let unitary = ValueInstruction::from_instruction(Instruction::UnitaryGate(Box::new(
UnitaryGate::new("oracle", 1, 0),
)));
assert!(unitary.is_unitary());
assert_eq!(
unitary.unitary_gate().map(|gate| gate.label()),
Some("oracle")
);
assert!(unitary.mc_gate().is_none());
let circuit_gate = CircuitGate::new(
"composite",
crate::circuit::gate::circuit_gate::FrozenCircuit::new(crate::circuit::Circuit::new(1)),
)
.unwrap();
let circuit =
ValueInstruction::from_instruction(Instruction::CircuitGate(Box::new(circuit_gate)));
assert_eq!(
circuit.circuit_gate().map(|gate| gate.name()),
Some("composite")
);
let value =
ClassicalValue::new(crate::circuit::CircuitId::default(), 0, ClassicalType::Bit);
let data = ValueInstruction::from_instruction(Instruction::ClassicalData(
ClassicalDataOp::MeasureBit { result: value },
));
assert_eq!(
data.classical_data().and_then(|op| op.result()),
Some(value)
);
let control = ValueInstruction::from(ValueClassicalControlOp::Break);
assert!(control.mc_gate().is_none());
assert!(control.unitary_gate().is_none());
assert!(control.circuit_gate().is_none());
assert!(control.classical_data().is_none());
assert_eq!(
control.classical_control(),
Some(&ValueClassicalControlOp::Break)
);
assert!(data.classical_control().is_none());
}
#[test]
fn value_classical_control_reads_and_writes() {
let var = ClassicalVar::new(
crate::circuit::CircuitId::default(),
0,
ClassicalType::UInt(NonZeroU32::new(8).unwrap()),
);
let start = ClassicalExpr::var(var);
let stop = ClassicalExpr::uint_literal(8, 10).unwrap();
let step = ClassicalExpr::uint_literal(8, 1).unwrap();
let body = ValueControlBody::new(vec![]);
let vcc = ValueClassicalControlOp::For {
var,
start,
stop,
step,
body,
};
assert_eq!(vcc.classical_var_reads().len(), 1);
assert_eq!(vcc.classical_writes().len(), 1);
assert_eq!(vcc.classical_writes()[0], var);
}
#[test]
fn value_body_reports_nested_measurements() {
let value =
ClassicalValue::new(crate::circuit::CircuitId::default(), 0, ClassicalType::Bit);
let nested = ValueControlBody::new(vec![ValueOperation {
instruction: ValueInstruction::from_instruction(Instruction::ClassicalData(
ClassicalDataOp::MeasureBit { result: value },
)),
qubits: smallvec::smallvec![Qubit::new(0)],
params: smallvec::smallvec![],
label: None,
}]);
let body = ValueControlBody::new(vec![ValueOperation {
instruction: ValueInstruction::ClassicalControl(ValueClassicalControlOp::If {
condition: ClassicalExpr::bool_literal(true),
then_body: nested,
else_body: None,
}),
qubits: smallvec::smallvec![Qubit::new(0)],
params: smallvec::smallvec![],
label: None,
}]);
assert!(body.has_measurement());
}
#[test]
fn value_body_reports_nested_value_reads() {
let circuit_id = crate::circuit::CircuitId::default();
let value = ClassicalValue::new(circuit_id, 0, ClassicalType::Bit);
let target = ClassicalVar::new(circuit_id, 1, ClassicalType::Bit);
let nested = ValueControlBody::new(vec![ValueOperation {
instruction: ValueInstruction::from_instruction(Instruction::ClassicalData(
ClassicalDataOp::Store {
target,
value: value.expr(),
},
)),
qubits: smallvec::smallvec![],
params: smallvec::smallvec![],
label: None,
}]);
let control = ValueClassicalControlOp::While {
condition: ClassicalExpr::bool_literal(true),
body: nested,
};
assert!(control.reads_value(value));
}
#[test]
fn value_switch_case_construction() {
let body = ValueControlBody::new(vec![]);
let case = ValueSwitchCase::new(1, body);
assert_eq!(case.value, 1);
assert_eq!(case.body.operations().len(), 0);
}
#[test]
fn value_instructions_compare_nested_control_flow_structurally() {
let make_if = || {
ValueInstruction::ClassicalControl(ValueClassicalControlOp::If {
condition: ClassicalExpr::bool_literal(true),
then_body: ValueControlBody::new(vec![ValueOperation::from_standard(
StandardGate::H,
[Qubit::new(0)],
[],
)]),
else_body: None,
})
};
assert_eq!(make_if(), make_if());
let different_body = ValueInstruction::ClassicalControl(ValueClassicalControlOp::If {
condition: ClassicalExpr::bool_literal(true),
then_body: ValueControlBody::new(vec![ValueOperation::from_standard(
StandardGate::X,
[Qubit::new(0)],
[],
)]),
else_body: None,
});
assert_ne!(make_if(), different_body);
let with_else = ValueInstruction::ClassicalControl(ValueClassicalControlOp::If {
condition: ClassicalExpr::bool_literal(true),
then_body: ValueControlBody::new(vec![ValueOperation::from_standard(
StandardGate::H,
[Qubit::new(0)],
[],
)]),
else_body: Some(ValueControlBody::new(vec![])),
});
assert_ne!(make_if(), with_else);
assert_eq!(
ValueInstruction::from_instruction(Instruction::Standard(StandardGate::H)),
ValueInstruction::from_instruction(Instruction::Standard(StandardGate::H))
);
assert_ne!(
ValueInstruction::from_instruction(Instruction::Standard(StandardGate::H)),
ValueInstruction::from_instruction(Instruction::Standard(StandardGate::X))
);
}
}