#![allow(rustdoc::private_doc_tests)]
use crate::circuit::bit::Qubit;
use crate::circuit::circuit_impl::Circuit;
use crate::circuit::classical_expr::ClassicalExpr;
use crate::circuit::control_flow::{
ClassicalControlOp, ControlBody, ForOp, IfOp, SwitchCase, SwitchOp, WhileOp,
};
use crate::circuit::error::CircuitError;
use crate::circuit::gate::instruction::Instruction;
use crate::circuit::{
CircuitParam, ClassicalDataOp, ClassicalType, ClassicalValue, ClassicalVar, Measurement,
};
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum ControlScopeKind {
Loop,
Switch,
}
#[doc(hidden)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExternalControlScope {
Branch,
Loop,
Switch,
}
pub struct SwitchBuilder<'a> {
circuit: &'a mut Circuit,
cases: Vec<SwitchCase>,
default: Option<ControlBody>,
}
impl SwitchBuilder<'_> {
pub fn value<F>(&mut self, value: u128, body: F) -> Result<(), CircuitError>
where
F: FnOnce(&mut Circuit) -> Result<(), CircuitError>,
{
let body = self
.circuit
.build_control_body(Some(ControlScopeKind::Switch), body)?;
self.cases.push(SwitchCase::new(value, body));
Ok(())
}
pub fn default<F>(&mut self, body: F) -> Result<(), CircuitError>
where
F: FnOnce(&mut Circuit) -> Result<(), CircuitError>,
{
if self.default.is_some() {
return Err(CircuitError::InvalidOperation(
"switch default case is already defined".to_string(),
));
}
self.default = Some(
self.circuit
.build_control_body(Some(ControlScopeKind::Switch), body)?,
);
Ok(())
}
}
#[derive(Debug, Clone)]
pub(super) struct CircuitCheckpoint {
data_len: usize,
qubit_len: usize,
parameter_len: usize,
symbol_len: usize,
classical_var_len: usize,
classical_value_len: usize,
control_scope_len: usize,
global_phase: CircuitParam,
}
#[doc(hidden)]
pub struct ControlBodyTransaction {
checkpoint: CircuitCheckpoint,
}
#[derive(Debug, Clone, Copy)]
struct ControlValidationContext {
break_allowed: bool,
continue_allowed: bool,
}
impl ControlValidationContext {
fn from_scope_stack(scopes: &[ControlScopeKind]) -> Self {
Self {
break_allowed: scopes
.iter()
.rev()
.any(|scope| matches!(scope, ControlScopeKind::Loop | ControlScopeKind::Switch)),
continue_allowed: scopes
.iter()
.rev()
.any(|scope| matches!(scope, ControlScopeKind::Loop)),
}
}
fn enter_loop(self) -> Self {
Self {
break_allowed: true,
continue_allowed: true,
}
}
fn enter_switch(self) -> Self {
Self {
break_allowed: true,
continue_allowed: self.continue_allowed,
}
}
}
impl Circuit {
pub fn var(&mut self, ty: ClassicalType) -> ClassicalVar {
self.allocate_classical_var(ty)
}
fn allocate_classical_var(&mut self, ty: ClassicalType) -> ClassicalVar {
let id = self.classical_vars.len() as u32;
self.classical_vars.push(ty);
ClassicalVar::new(self.circuit_id, id, ty)
}
fn allocate_classical_value(&mut self, ty: ClassicalType) -> ClassicalValue {
let id = self.classical_values.len() as u32;
self.classical_values.push(ty);
ClassicalValue::new(self.circuit_id, id, ty)
}
pub(super) fn checkpoint(&self) -> CircuitCheckpoint {
CircuitCheckpoint {
data_len: self.data.len(),
qubit_len: self.qubits.len(),
parameter_len: self.parameters.len(),
symbol_len: self.symbols.len(),
classical_var_len: self.classical_vars.len(),
classical_value_len: self.classical_values.len(),
control_scope_len: self.control_scope_stack.len(),
global_phase: self.global_phase.clone(),
}
}
pub(super) fn rollback_to(&mut self, checkpoint: CircuitCheckpoint) {
Arc::make_mut(&mut self.data).truncate(checkpoint.data_len);
self.qubits.truncate(checkpoint.qubit_len);
self.parameters.truncate(checkpoint.parameter_len);
self.global_phase = checkpoint.global_phase;
self.symbols.truncate(checkpoint.symbol_len);
self.classical_vars.truncate(checkpoint.classical_var_len);
self.classical_values
.truncate(checkpoint.classical_value_len);
self.control_scope_stack
.truncate(checkpoint.control_scope_len);
}
#[doc(hidden)]
pub fn validate_classical_var(&self, var: ClassicalVar) -> Result<(), CircuitError> {
if var.circuit_id() != self.circuit_id {
return Err(CircuitError::ForeignClassicalHandle {
kind: "classical variable",
index: var.index(),
});
}
match self.classical_vars.get(var.id() as usize) {
Some(ty) if *ty == var.ty() => Ok(()),
Some(ty) => Err(CircuitError::InvalidOperation(format!(
"classical variable {} has type {:?}, got {:?}",
var.id(),
ty,
var.ty()
))),
None => Err(CircuitError::InvalidOperation(format!(
"classical variable {} is not allocated by this circuit",
var.id()
))),
}
}
pub(super) fn validate_classical_value(
&self,
value: ClassicalValue,
) -> Result<(), CircuitError> {
if value.circuit_id() != self.circuit_id {
return Err(CircuitError::ForeignClassicalHandle {
kind: "classical value",
index: value.index(),
});
}
match self.classical_values.get(value.index() as usize) {
Some(ty) if *ty == value.ty() => Ok(()),
Some(ty) => Err(CircuitError::InvalidOperation(format!(
"classical value {} has type {:?}, got {:?}",
value.index(),
ty,
value.ty()
))),
None => Err(CircuitError::InvalidOperation(format!(
"classical value {} is not produced by this circuit",
value.index()
))),
}
}
#[doc(hidden)]
pub fn validate_classical_expr(&self, expr: &ClassicalExpr) -> Result<(), CircuitError> {
for var in expr.vars() {
self.validate_classical_var(var)?;
}
for value in expr.values() {
self.validate_classical_value(value)?;
}
Ok(())
}
fn validate_control_body_in(
&self,
body: &ControlBody,
context: ControlValidationContext,
) -> Result<(), CircuitError> {
for operation in body.operations() {
for qubit in &operation.qubits {
if !self.qubits.contains(qubit) {
return Err(CircuitError::QubitNotFound(qubit.id()));
}
}
if let Instruction::ClassicalControl(op) = &operation.instruction {
self.validate_control_op_in(op, context)?;
}
if let Instruction::ClassicalData(op) = &operation.instruction {
self.validate_classical_data_op(op, operation.qubits.len())?;
}
}
Ok(())
}
pub(super) fn validate_classical_data_op(
&self,
op: &ClassicalDataOp,
qubit_count: usize,
) -> Result<(), CircuitError> {
match op {
ClassicalDataOp::Store { target, value } => {
self.validate_classical_var(*target)?;
if qubit_count != 0 {
return Err(CircuitError::QubitCountMismatch {
expected: 0,
actual: qubit_count,
});
}
self.validate_classical_expr(value)?;
if value.ty() != target.ty() {
return Err(CircuitError::InvalidOperation(format!(
"store target type {:?} does not match value type {:?}",
target.ty(),
value.ty()
)));
}
}
ClassicalDataOp::MeasureBit { result } => {
self.validate_classical_value(*result)?;
if qubit_count != 1 {
return Err(CircuitError::QubitCountMismatch {
expected: 1,
actual: qubit_count,
});
}
if result.ty() != ClassicalType::Bit {
return Err(CircuitError::InvalidOperation(format!(
"single-qubit measurement result must be Bit, got {:?}",
result.ty()
)));
}
}
ClassicalDataOp::MeasureBits { result } => {
self.validate_classical_value(*result)?;
let expected = match result.ty() {
ClassicalType::BitVec(width) => width.get() as usize,
ty => {
return Err(CircuitError::InvalidOperation(format!(
"multi-qubit measurement result must be BitVec, got {ty:?}"
)));
}
};
if qubit_count != expected {
return Err(CircuitError::QubitCountMismatch {
expected,
actual: qubit_count,
});
}
}
}
Ok(())
}
pub(super) fn validate_control_op(&self, op: &ClassicalControlOp) -> Result<(), CircuitError> {
self.validate_control_op_in(
op,
ControlValidationContext::from_scope_stack(&self.control_scope_stack),
)
}
fn validate_control_op_in(
&self,
op: &ClassicalControlOp,
context: ControlValidationContext,
) -> Result<(), CircuitError> {
match op {
ClassicalControlOp::If(op) => {
self.validate_classical_expr(op.condition())?;
self.validate_control_body_in(op.then_body(), context)?;
if let Some(body) = op.else_body() {
self.validate_control_body_in(body, context)?;
}
}
ClassicalControlOp::While(op) => {
self.validate_classical_expr(op.condition())?;
self.validate_control_body_in(op.body(), context.enter_loop())?;
}
ClassicalControlOp::For(op) => {
self.validate_classical_var(op.var())?;
self.validate_classical_expr(op.start())?;
self.validate_classical_expr(op.stop())?;
self.validate_classical_expr(op.step())?;
self.validate_control_body_in(op.body(), context.enter_loop())?;
}
ClassicalControlOp::Switch(op) => {
self.validate_classical_expr(op.target())?;
let body_context = context.enter_switch();
for case in op.cases() {
self.validate_control_body_in(case.body(), body_context)?;
}
if let Some(body) = op.default() {
self.validate_control_body_in(body, body_context)?;
}
}
ClassicalControlOp::Break => {
if !context.break_allowed {
return Err(CircuitError::InvalidOperation(
"break can only be used inside a loop or switch body".to_string(),
));
}
}
ClassicalControlOp::Continue => {
if !context.continue_allowed {
return Err(CircuitError::InvalidOperation(
"continue can only be used inside a loop body".to_string(),
));
}
}
}
Ok(())
}
fn build_control_body<F>(
&mut self,
scope: Option<ControlScopeKind>,
body: F,
) -> Result<ControlBody, CircuitError>
where
F: FnOnce(&mut Circuit) -> Result<(), CircuitError>,
{
let checkpoint = self.checkpoint();
if let Some(scope) = scope {
self.control_scope_stack.push(scope);
}
let result = body(self);
self.control_scope_stack
.truncate(checkpoint.control_scope_len);
match result {
Ok(()) => Ok(ControlBody::new(
Arc::make_mut(&mut self.data).split_off(checkpoint.data_len),
)),
Err(error) => {
self.rollback_to(checkpoint);
Err(error)
}
}
}
#[doc(hidden)]
pub fn begin_control_body_transaction(&self) -> ControlBodyTransaction {
ControlBodyTransaction {
checkpoint: self.checkpoint(),
}
}
#[doc(hidden)]
pub fn enter_external_control_body(
&mut self,
transaction: &ControlBodyTransaction,
scope: ExternalControlScope,
) {
self.control_scope_stack
.truncate(transaction.checkpoint.control_scope_len);
match scope {
ExternalControlScope::Branch => {}
ExternalControlScope::Loop => self.control_scope_stack.push(ControlScopeKind::Loop),
ExternalControlScope::Switch => self.control_scope_stack.push(ControlScopeKind::Switch),
}
}
#[doc(hidden)]
pub fn finish_external_control_body(
&mut self,
transaction: &ControlBodyTransaction,
) -> ControlBody {
self.control_scope_stack
.truncate(transaction.checkpoint.control_scope_len);
ControlBody::new(Arc::make_mut(&mut self.data).split_off(transaction.checkpoint.data_len))
}
#[doc(hidden)]
pub fn commit_control_body_transaction(&mut self, transaction: ControlBodyTransaction) {
self.control_scope_stack
.truncate(transaction.checkpoint.control_scope_len);
}
#[doc(hidden)]
pub fn rollback_control_body_transaction(&mut self, transaction: ControlBodyTransaction) {
self.rollback_to(transaction.checkpoint);
}
pub fn store(
&mut self,
target: ClassicalVar,
value: ClassicalExpr,
) -> Result<(), CircuitError> {
self.validate_classical_var(target)?;
self.validate_classical_expr(&value)?;
self.append(
Instruction::ClassicalData(ClassicalDataOp::Store { target, value }),
std::iter::empty::<Qubit>(),
std::iter::empty(),
None,
)
}
pub fn measure(&mut self, qubit: Qubit) -> Result<Measurement, CircuitError> {
let checkpoint = self.checkpoint();
let result = self.allocate_classical_value(ClassicalType::Bit);
if let Err(error) = self.append(
Instruction::ClassicalData(ClassicalDataOp::MeasureBit { result }),
[qubit],
std::iter::empty(),
None,
) {
self.rollback_to(checkpoint);
return Err(error);
}
Ok(Measurement::new(result, smallvec::smallvec![qubit]))
}
pub fn measure_bits<I>(&mut self, qubits: I) -> Result<Measurement, CircuitError>
where
I: IntoIterator<Item = Qubit>,
{
let qubits: smallvec::SmallVec<[Qubit; 3]> = qubits.into_iter().collect();
let ty = ClassicalType::bit_vec(qubits.len() as u32).ok_or_else(|| {
CircuitError::InvalidOperation(
"multi-qubit measurement requires at least one qubit".to_string(),
)
})?;
let checkpoint = self.checkpoint();
let result = self.allocate_classical_value(ty);
if let Err(error) = self.append(
Instruction::ClassicalData(ClassicalDataOp::MeasureBits { result }),
qubits.iter().copied(),
std::iter::empty(),
None,
) {
self.rollback_to(checkpoint);
return Err(error);
}
Ok(Measurement::new(result, qubits))
}
pub fn measure_into(
&mut self,
qubit: Qubit,
target: ClassicalVar,
) -> Result<Measurement, CircuitError> {
self.validate_classical_var(target)?;
let checkpoint = self.checkpoint();
let result = self.measure(qubit)?;
if let Err(error) = self.store(target, result.expr()) {
self.rollback_to(checkpoint);
return Err(error);
}
Ok(result)
}
pub fn measure_bits_into<I>(
&mut self,
qubits: I,
target: ClassicalVar,
) -> Result<Measurement, CircuitError>
where
I: IntoIterator<Item = Qubit>,
{
self.validate_classical_var(target)?;
let qubits: smallvec::SmallVec<[Qubit; 3]> = qubits.into_iter().collect();
let checkpoint = self.checkpoint();
let result = self.measure_bits(qubits)?;
if let Err(error) = self.store(target, result.expr()) {
self.rollback_to(checkpoint);
return Err(error);
}
Ok(result)
}
pub fn if_<F>(&mut self, condition: ClassicalExpr, then_body: F) -> Result<(), CircuitError>
where
F: FnOnce(&mut Circuit) -> Result<(), CircuitError>,
{
self.validate_classical_expr(&condition)?;
let checkpoint = self.checkpoint();
let result = (|| {
let then_body = self.build_control_body(None, then_body)?;
let op = IfOp::new(condition, then_body, None)?;
self.append_control(ClassicalControlOp::If(op))
})();
if result.is_err() {
self.rollback_to(checkpoint);
}
result
}
pub fn if_else<T, E>(
&mut self,
condition: ClassicalExpr,
then_body: T,
else_body: E,
) -> Result<(), CircuitError>
where
T: FnOnce(&mut Circuit) -> Result<(), CircuitError>,
E: FnOnce(&mut Circuit) -> Result<(), CircuitError>,
{
self.validate_classical_expr(&condition)?;
let checkpoint = self.checkpoint();
let result = (|| {
let then_body = self.build_control_body(None, then_body)?;
let else_body = self.build_control_body(None, else_body)?;
let op = IfOp::new(condition, then_body, Some(else_body))?;
self.append_control(ClassicalControlOp::If(op))
})();
if result.is_err() {
self.rollback_to(checkpoint);
}
result
}
pub fn while_<F>(&mut self, condition: ClassicalExpr, body: F) -> Result<(), CircuitError>
where
F: FnOnce(&mut Circuit) -> Result<(), CircuitError>,
{
self.validate_classical_expr(&condition)?;
let checkpoint = self.checkpoint();
let result = (|| {
let body = self.build_control_body(Some(ControlScopeKind::Loop), body)?;
let op = WhileOp::new(condition, body)?;
self.append_control(ClassicalControlOp::While(op))
})();
if result.is_err() {
self.rollback_to(checkpoint);
}
result
}
pub fn for_uint<F>(
&mut self,
var: ClassicalVar,
start: ClassicalExpr,
stop: ClassicalExpr,
step: ClassicalExpr,
body: F,
) -> Result<(), CircuitError>
where
F: FnOnce(&mut Circuit, ClassicalExpr) -> Result<(), CircuitError>,
{
self.validate_classical_var(var)?;
self.validate_classical_expr(&start)?;
self.validate_classical_expr(&stop)?;
self.validate_classical_expr(&step)?;
let checkpoint = self.checkpoint();
let loop_expr = var.expr();
let result = (|| {
let body = self
.build_control_body(Some(ControlScopeKind::Loop), |scope| body(scope, loop_expr))?;
let op = ForOp::new(var, start, stop, step, body)?;
self.append_control(ClassicalControlOp::For(op))
})();
if result.is_err() {
self.rollback_to(checkpoint);
}
result
}
pub fn switch<F>(&mut self, target: ClassicalExpr, build: F) -> Result<(), CircuitError>
where
F: FnOnce(&mut SwitchBuilder<'_>) -> Result<(), CircuitError>,
{
self.validate_classical_expr(&target)?;
let checkpoint = self.checkpoint();
let result = (|| {
let mut builder = SwitchBuilder {
circuit: self,
cases: Vec::new(),
default: None,
};
build(&mut builder)?;
let op = SwitchOp::new(target, builder.cases, builder.default)?;
builder
.circuit
.append_control(ClassicalControlOp::Switch(op))
})();
if result.is_err() {
self.rollback_to(checkpoint);
}
result
}
pub fn append_control(&mut self, op: ClassicalControlOp) -> Result<(), CircuitError> {
let qubits: Vec<Qubit> = op.used_qubits().into_iter().collect();
self.append(
Instruction::ClassicalControl(op),
qubits,
std::iter::empty(),
None,
)
}
pub fn break_loop(&mut self) -> Result<(), CircuitError> {
self.append_control(ClassicalControlOp::Break)
}
pub fn continue_loop(&mut self) -> Result<(), CircuitError> {
self.append_control(ClassicalControlOp::Continue)
}
}