use quantrs2_core::{
error::{QuantRS2Error, QuantRS2Result},
gate::GateOp,
qubit::QubitId,
};
use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ClassicalRegister {
pub name: String,
pub size: usize,
}
impl ClassicalRegister {
pub fn new(name: impl Into<String>, size: usize) -> Self {
Self {
name: name.into(),
size,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ClassicalBit {
pub register: String,
pub index: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ClassicalValue {
Bit(bool),
Integer(u64),
Register(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ComparisonOp {
Equal,
NotEqual,
Less,
LessEqual,
Greater,
GreaterEqual,
}
#[derive(Debug, Clone)]
pub struct ClassicalCondition {
pub lhs: ClassicalValue,
pub op: ComparisonOp,
pub rhs: ClassicalValue,
}
impl ClassicalCondition {
#[must_use]
pub const fn equals(lhs: ClassicalValue, rhs: ClassicalValue) -> Self {
Self {
lhs,
op: ComparisonOp::Equal,
rhs,
}
}
#[must_use]
pub fn register_equals(register: &str, value: u64) -> Self {
Self {
lhs: ClassicalValue::Register(register.to_string()),
op: ComparisonOp::Equal,
rhs: ClassicalValue::Integer(value),
}
}
}
#[derive(Debug, Clone)]
pub struct MeasureOp {
pub qubit: QubitId,
pub cbit: ClassicalBit,
}
impl MeasureOp {
#[must_use]
pub fn new(qubit: QubitId, register: &str, bit_index: usize) -> Self {
Self {
qubit,
cbit: ClassicalBit {
register: register.to_string(),
index: bit_index,
},
}
}
}
pub struct ConditionalGate {
pub condition: ClassicalCondition,
pub gate: Box<dyn GateOp>,
}
impl fmt::Debug for ConditionalGate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ConditionalGate")
.field("condition", &self.condition)
.field("gate", &self.gate.name())
.finish()
}
}
#[derive(Debug)]
pub enum ClassicalOp {
Measure(MeasureOp),
Reset(String),
Conditional(ConditionalGate),
Compute {
output: String,
op: String,
inputs: Vec<String>,
},
}
pub struct ClassicalCircuit<const N: usize> {
pub classical_registers: HashMap<String, ClassicalRegister>,
pub operations: Vec<CircuitOp>,
}
pub enum CircuitOp {
Quantum(Box<dyn GateOp>),
Classical(ClassicalOp),
}
impl<const N: usize> Default for ClassicalCircuit<N> {
fn default() -> Self {
Self::new()
}
}
impl<const N: usize> ClassicalCircuit<N> {
#[must_use]
pub fn new() -> Self {
Self {
classical_registers: HashMap::new(),
operations: Vec::new(),
}
}
pub fn add_classical_register(&mut self, name: &str, size: usize) -> QuantRS2Result<()> {
if self.classical_registers.contains_key(name) {
return Err(QuantRS2Error::InvalidInput(format!(
"Classical register '{name}' already exists"
)));
}
self.classical_registers
.insert(name.to_string(), ClassicalRegister::new(name, size));
Ok(())
}
pub fn add_gate<G: GateOp + 'static>(&mut self, gate: G) -> QuantRS2Result<()> {
for qubit in gate.qubits() {
if qubit.id() as usize >= N {
return Err(QuantRS2Error::InvalidQubitId(qubit.id()));
}
}
self.operations.push(CircuitOp::Quantum(Box::new(gate)));
Ok(())
}
pub fn measure(&mut self, qubit: QubitId, register: &str, bit: usize) -> QuantRS2Result<()> {
if qubit.id() as usize >= N {
return Err(QuantRS2Error::InvalidQubitId(qubit.id()));
}
let creg = self.classical_registers.get(register).ok_or_else(|| {
QuantRS2Error::InvalidInput(format!("Classical register '{register}' not found"))
})?;
if bit >= creg.size {
return Err(QuantRS2Error::InvalidInput(format!(
"Bit index {} out of range for register '{}' (size: {})",
bit, register, creg.size
)));
}
self.operations
.push(CircuitOp::Classical(ClassicalOp::Measure(MeasureOp::new(
qubit, register, bit,
))));
Ok(())
}
pub fn add_conditional<G: GateOp + 'static>(
&mut self,
condition: ClassicalCondition,
gate: G,
) -> QuantRS2Result<()> {
for qubit in gate.qubits() {
if qubit.id() as usize >= N {
return Err(QuantRS2Error::InvalidQubitId(qubit.id()));
}
}
self.validate_classical_value(&condition.lhs)?;
self.validate_classical_value(&condition.rhs)?;
self.operations
.push(CircuitOp::Classical(ClassicalOp::Conditional(
ConditionalGate {
condition,
gate: Box::new(gate),
},
)));
Ok(())
}
fn validate_classical_value(&self, value: &ClassicalValue) -> QuantRS2Result<()> {
match value {
ClassicalValue::Register(register_name) => {
if !self.classical_registers.contains_key(register_name) {
return Err(QuantRS2Error::InvalidInput(format!(
"Classical register '{register_name}' not found. Available registers: {:?}",
self.classical_registers.keys().collect::<Vec<_>>()
)));
}
}
ClassicalValue::Bit(_) | ClassicalValue::Integer(_) => {
}
}
Ok(())
}
pub fn reset_classical(&mut self, register: &str) -> QuantRS2Result<()> {
if !self.classical_registers.contains_key(register) {
return Err(QuantRS2Error::InvalidInput(format!(
"Classical register '{register}' not found"
)));
}
self.operations
.push(CircuitOp::Classical(ClassicalOp::Reset(
register.to_string(),
)));
Ok(())
}
#[must_use]
pub fn num_operations(&self) -> usize {
self.operations.len()
}
}
pub struct ClassicalCircuitBuilder<const N: usize> {
circuit: ClassicalCircuit<N>,
}
impl<const N: usize> Default for ClassicalCircuitBuilder<N> {
fn default() -> Self {
Self::new()
}
}
impl<const N: usize> ClassicalCircuitBuilder<N> {
#[must_use]
pub fn new() -> Self {
Self {
circuit: ClassicalCircuit::new(),
}
}
pub fn classical_register(mut self, name: &str, size: usize) -> QuantRS2Result<Self> {
self.circuit.add_classical_register(name, size)?;
Ok(self)
}
pub fn gate<G: GateOp + 'static>(mut self, gate: G) -> QuantRS2Result<Self> {
self.circuit.add_gate(gate)?;
Ok(self)
}
pub fn measure(mut self, qubit: QubitId, register: &str, bit: usize) -> QuantRS2Result<Self> {
self.circuit.measure(qubit, register, bit)?;
Ok(self)
}
pub fn conditional<G: GateOp + 'static>(
mut self,
condition: ClassicalCondition,
gate: G,
) -> QuantRS2Result<Self> {
self.circuit.add_conditional(condition, gate)?;
Ok(self)
}
#[must_use]
pub fn build(self) -> ClassicalCircuit<N> {
self.circuit
}
}
#[cfg(test)]
mod tests {
use super::*;
use quantrs2_core::gate::single::PauliX;
#[test]
fn test_classical_register() {
let reg = ClassicalRegister::new("c", 3);
assert_eq!(reg.name, "c");
assert_eq!(reg.size, 3);
}
#[test]
fn test_classical_condition() {
let cond = ClassicalCondition::register_equals("c", 1);
assert_eq!(cond.op, ComparisonOp::Equal);
match &cond.lhs {
ClassicalValue::Register(name) => assert_eq!(name, "c"),
_ => panic!("Expected Register variant"),
}
match &cond.rhs {
ClassicalValue::Integer(val) => assert_eq!(*val, 1),
_ => panic!("Expected Integer variant"),
}
}
#[test]
fn test_classical_circuit_builder() {
let circuit = ClassicalCircuitBuilder::<2>::new()
.classical_register("c", 2)
.expect("Failed to add classical register")
.gate(PauliX { target: QubitId(0) })
.expect("Failed to add PauliX gate")
.measure(QubitId(0), "c", 0)
.expect("Failed to add measurement")
.conditional(
ClassicalCondition::register_equals("c", 1),
PauliX { target: QubitId(1) },
)
.expect("Failed to add conditional gate")
.build();
assert_eq!(circuit.classical_registers.len(), 1);
assert_eq!(circuit.num_operations(), 3);
}
#[test]
fn test_conditional_validation_invalid_register() {
let mut circuit = ClassicalCircuit::<2>::new();
circuit
.add_classical_register("measurement", 1)
.expect("Failed to add classical register");
let condition = ClassicalCondition::register_equals("nonexistent", 1);
let result = circuit.add_conditional(condition, PauliX { target: QubitId(0) });
assert!(result.is_err());
match result {
Err(QuantRS2Error::InvalidInput(msg)) => {
assert!(msg.contains("nonexistent"));
assert!(msg.contains("not found"));
}
_ => panic!("Expected InvalidInput error"),
}
}
#[test]
fn test_conditional_validation_valid_register() {
let mut circuit = ClassicalCircuit::<2>::new();
circuit
.add_classical_register("measurement", 1)
.expect("Failed to add classical register");
circuit
.measure(QubitId(0), "measurement", 0)
.expect("Failed to add measurement");
let condition = ClassicalCondition::register_equals("measurement", 1);
let result = circuit.add_conditional(condition, PauliX { target: QubitId(1) });
assert!(result.is_ok());
assert_eq!(circuit.num_operations(), 2); }
}