quantrs2_circuit/
classical.rs

1//! Classical control flow support for quantum circuits
2//!
3//! This module provides support for classical registers, measurements,
4//! and conditional execution of quantum gates based on classical values.
5
6use quantrs2_core::{
7    error::{QuantRS2Error, QuantRS2Result},
8    gate::GateOp,
9    qubit::QubitId,
10};
11use std::collections::HashMap;
12use std::fmt;
13
14/// A classical register that can store measurement results
15#[derive(Debug, Clone, PartialEq, Eq, Hash)]
16pub struct ClassicalRegister {
17    /// Name of the register
18    pub name: String,
19    /// Number of bits in the register
20    pub size: usize,
21}
22
23impl ClassicalRegister {
24    /// Create a new classical register
25    pub fn new(name: impl Into<String>, size: usize) -> Self {
26        Self {
27            name: name.into(),
28            size,
29        }
30    }
31}
32
33/// A classical bit reference within a register
34#[derive(Debug, Clone, PartialEq, Eq, Hash)]
35pub struct ClassicalBit {
36    /// The register containing this bit
37    pub register: String,
38    /// Index within the register
39    pub index: usize,
40}
41
42/// Classical values that can be used in conditions
43#[derive(Debug, Clone, PartialEq, Eq)]
44pub enum ClassicalValue {
45    /// A single bit value
46    Bit(bool),
47    /// A multi-bit integer value
48    Integer(u64),
49    /// A reference to a classical register
50    Register(String),
51}
52
53/// Comparison operators for classical conditions
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum ComparisonOp {
56    /// Equal to
57    Equal,
58    /// Not equal to
59    NotEqual,
60    /// Less than
61    Less,
62    /// Less than or equal
63    LessEqual,
64    /// Greater than
65    Greater,
66    /// Greater than or equal
67    GreaterEqual,
68}
69
70/// A condition that gates execution based on classical values
71#[derive(Debug, Clone)]
72pub struct ClassicalCondition {
73    /// Left-hand side of the comparison
74    pub lhs: ClassicalValue,
75    /// Comparison operator
76    pub op: ComparisonOp,
77    /// Right-hand side of the comparison
78    pub rhs: ClassicalValue,
79}
80
81impl ClassicalCondition {
82    /// Create a new equality condition
83    #[must_use]
84    pub const fn equals(lhs: ClassicalValue, rhs: ClassicalValue) -> Self {
85        Self {
86            lhs,
87            op: ComparisonOp::Equal,
88            rhs,
89        }
90    }
91
92    /// Check if a register equals a specific value
93    #[must_use]
94    pub fn register_equals(register: &str, value: u64) -> Self {
95        Self {
96            lhs: ClassicalValue::Register(register.to_string()),
97            op: ComparisonOp::Equal,
98            rhs: ClassicalValue::Integer(value),
99        }
100    }
101}
102
103/// A measurement operation that stores the result in a classical register
104#[derive(Debug, Clone)]
105pub struct MeasureOp {
106    /// Qubit to measure
107    pub qubit: QubitId,
108    /// Classical bit to store the result
109    pub cbit: ClassicalBit,
110}
111
112impl MeasureOp {
113    /// Create a new measurement operation
114    #[must_use]
115    pub fn new(qubit: QubitId, register: &str, bit_index: usize) -> Self {
116        Self {
117            qubit,
118            cbit: ClassicalBit {
119                register: register.to_string(),
120                index: bit_index,
121            },
122        }
123    }
124}
125
126/// A gate that executes conditionally based on classical values
127pub struct ConditionalGate {
128    /// The condition to check
129    pub condition: ClassicalCondition,
130    /// The gate to execute if the condition is true
131    pub gate: Box<dyn GateOp>,
132}
133
134impl fmt::Debug for ConditionalGate {
135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136        f.debug_struct("ConditionalGate")
137            .field("condition", &self.condition)
138            .field("gate", &self.gate.name())
139            .finish()
140    }
141}
142
143/// Classical control flow operations
144#[derive(Debug)]
145pub enum ClassicalOp {
146    /// Measure a qubit into a classical bit
147    Measure(MeasureOp),
148    /// Reset a classical register
149    Reset(String),
150    /// Conditional gate execution
151    Conditional(ConditionalGate),
152    /// Classical computation (e.g., XOR, AND)
153    Compute {
154        /// Output register
155        output: String,
156        /// Operation type
157        op: String,
158        /// Input registers
159        inputs: Vec<String>,
160    },
161}
162
163/// A circuit with classical control flow support
164pub struct ClassicalCircuit<const N: usize> {
165    /// Classical registers
166    pub classical_registers: HashMap<String, ClassicalRegister>,
167    /// Operations (both quantum and classical)
168    pub operations: Vec<CircuitOp>,
169}
170
171/// Operations that can appear in a classical circuit
172pub enum CircuitOp {
173    /// A quantum gate operation
174    Quantum(Box<dyn GateOp>),
175    /// A classical operation
176    Classical(ClassicalOp),
177}
178
179impl<const N: usize> Default for ClassicalCircuit<N> {
180    fn default() -> Self {
181        Self::new()
182    }
183}
184
185impl<const N: usize> ClassicalCircuit<N> {
186    /// Create a new circuit with classical control
187    #[must_use]
188    pub fn new() -> Self {
189        Self {
190            classical_registers: HashMap::new(),
191            operations: Vec::new(),
192        }
193    }
194
195    /// Add a classical register
196    pub fn add_classical_register(&mut self, name: &str, size: usize) -> QuantRS2Result<()> {
197        if self.classical_registers.contains_key(name) {
198            return Err(QuantRS2Error::InvalidInput(format!(
199                "Classical register '{name}' already exists"
200            )));
201        }
202
203        self.classical_registers
204            .insert(name.to_string(), ClassicalRegister::new(name, size));
205        Ok(())
206    }
207
208    /// Add a quantum gate
209    pub fn add_gate<G: GateOp + 'static>(&mut self, gate: G) -> QuantRS2Result<()> {
210        // Validate qubits
211        for qubit in gate.qubits() {
212            if qubit.id() as usize >= N {
213                return Err(QuantRS2Error::InvalidQubitId(qubit.id()));
214            }
215        }
216
217        self.operations.push(CircuitOp::Quantum(Box::new(gate)));
218        Ok(())
219    }
220
221    /// Add a measurement
222    pub fn measure(&mut self, qubit: QubitId, register: &str, bit: usize) -> QuantRS2Result<()> {
223        // Validate qubit
224        if qubit.id() as usize >= N {
225            return Err(QuantRS2Error::InvalidQubitId(qubit.id()));
226        }
227
228        // Validate classical register
229        let creg = self.classical_registers.get(register).ok_or_else(|| {
230            QuantRS2Error::InvalidInput(format!("Classical register '{register}' not found"))
231        })?;
232
233        if bit >= creg.size {
234            return Err(QuantRS2Error::InvalidInput(format!(
235                "Bit index {} out of range for register '{}' (size: {})",
236                bit, register, creg.size
237            )));
238        }
239
240        self.operations
241            .push(CircuitOp::Classical(ClassicalOp::Measure(MeasureOp::new(
242                qubit, register, bit,
243            ))));
244        Ok(())
245    }
246
247    /// Add a conditional gate
248    ///
249    /// Adds a gate that will only execute if the classical condition is satisfied.
250    ///
251    /// # Arguments
252    /// * `condition` - The classical condition that must be true for the gate to execute
253    /// * `gate` - The quantum gate to execute conditionally
254    ///
255    /// # Errors
256    /// Returns an error if:
257    /// - The gate acts on invalid qubits (>= N)
258    /// - The condition references non-existent classical registers
259    ///
260    /// # Examples
261    /// ```ignore
262    /// circuit.add_classical_register("measurement", 1)?;
263    /// circuit.measure(QubitId(0), "measurement", 0)?;
264    /// let condition = ClassicalCondition::register_equals("measurement", 1);
265    /// circuit.add_conditional(condition, PauliX { target: QubitId(1) })?;
266    /// ```
267    pub fn add_conditional<G: GateOp + 'static>(
268        &mut self,
269        condition: ClassicalCondition,
270        gate: G,
271    ) -> QuantRS2Result<()> {
272        // Validate gate qubits
273        for qubit in gate.qubits() {
274            if qubit.id() as usize >= N {
275                return Err(QuantRS2Error::InvalidQubitId(qubit.id()));
276            }
277        }
278
279        // Validate condition references valid registers
280        self.validate_classical_value(&condition.lhs)?;
281        self.validate_classical_value(&condition.rhs)?;
282
283        self.operations
284            .push(CircuitOp::Classical(ClassicalOp::Conditional(
285                ConditionalGate {
286                    condition,
287                    gate: Box::new(gate),
288                },
289            )));
290        Ok(())
291    }
292
293    /// Validate that a classical value references valid registers
294    fn validate_classical_value(&self, value: &ClassicalValue) -> QuantRS2Result<()> {
295        match value {
296            ClassicalValue::Register(register_name) => {
297                if !self.classical_registers.contains_key(register_name) {
298                    return Err(QuantRS2Error::InvalidInput(format!(
299                        "Classical register '{register_name}' not found. Available registers: {:?}",
300                        self.classical_registers.keys().collect::<Vec<_>>()
301                    )));
302                }
303            }
304            ClassicalValue::Bit(_) | ClassicalValue::Integer(_) => {
305                // Literal values are always valid
306            }
307        }
308        Ok(())
309    }
310
311    /// Reset a classical register to zero
312    pub fn reset_classical(&mut self, register: &str) -> QuantRS2Result<()> {
313        if !self.classical_registers.contains_key(register) {
314            return Err(QuantRS2Error::InvalidInput(format!(
315                "Classical register '{register}' not found"
316            )));
317        }
318
319        self.operations
320            .push(CircuitOp::Classical(ClassicalOp::Reset(
321                register.to_string(),
322            )));
323        Ok(())
324    }
325
326    /// Get the number of operations
327    #[must_use]
328    pub fn num_operations(&self) -> usize {
329        self.operations.len()
330    }
331}
332
333/// Builder pattern for classical circuits
334pub struct ClassicalCircuitBuilder<const N: usize> {
335    circuit: ClassicalCircuit<N>,
336}
337
338impl<const N: usize> Default for ClassicalCircuitBuilder<N> {
339    fn default() -> Self {
340        Self::new()
341    }
342}
343
344impl<const N: usize> ClassicalCircuitBuilder<N> {
345    /// Create a new builder
346    #[must_use]
347    pub fn new() -> Self {
348        Self {
349            circuit: ClassicalCircuit::new(),
350        }
351    }
352
353    /// Add a classical register
354    pub fn classical_register(mut self, name: &str, size: usize) -> QuantRS2Result<Self> {
355        self.circuit.add_classical_register(name, size)?;
356        Ok(self)
357    }
358
359    /// Add a quantum gate
360    pub fn gate<G: GateOp + 'static>(mut self, gate: G) -> QuantRS2Result<Self> {
361        self.circuit.add_gate(gate)?;
362        Ok(self)
363    }
364
365    /// Add a measurement
366    pub fn measure(mut self, qubit: QubitId, register: &str, bit: usize) -> QuantRS2Result<Self> {
367        self.circuit.measure(qubit, register, bit)?;
368        Ok(self)
369    }
370
371    /// Add a conditional gate
372    pub fn conditional<G: GateOp + 'static>(
373        mut self,
374        condition: ClassicalCondition,
375        gate: G,
376    ) -> QuantRS2Result<Self> {
377        self.circuit.add_conditional(condition, gate)?;
378        Ok(self)
379    }
380
381    /// Build the circuit
382    #[must_use]
383    pub fn build(self) -> ClassicalCircuit<N> {
384        self.circuit
385    }
386}
387
388#[cfg(test)]
389mod tests {
390    use super::*;
391    use quantrs2_core::gate::single::PauliX;
392
393    #[test]
394    fn test_classical_register() {
395        let reg = ClassicalRegister::new("c", 3);
396        assert_eq!(reg.name, "c");
397        assert_eq!(reg.size, 3);
398    }
399
400    #[test]
401    fn test_classical_condition() {
402        let cond = ClassicalCondition::register_equals("c", 1);
403        assert_eq!(cond.op, ComparisonOp::Equal);
404
405        match &cond.lhs {
406            ClassicalValue::Register(name) => assert_eq!(name, "c"),
407            _ => panic!("Expected Register variant"),
408        }
409
410        match &cond.rhs {
411            ClassicalValue::Integer(val) => assert_eq!(*val, 1),
412            _ => panic!("Expected Integer variant"),
413        }
414    }
415
416    #[test]
417    fn test_classical_circuit_builder() {
418        let circuit = ClassicalCircuitBuilder::<2>::new()
419            .classical_register("c", 2)
420            .expect("Failed to add classical register")
421            .gate(PauliX { target: QubitId(0) })
422            .expect("Failed to add PauliX gate")
423            .measure(QubitId(0), "c", 0)
424            .expect("Failed to add measurement")
425            .conditional(
426                ClassicalCondition::register_equals("c", 1),
427                PauliX { target: QubitId(1) },
428            )
429            .expect("Failed to add conditional gate")
430            .build();
431
432        assert_eq!(circuit.classical_registers.len(), 1);
433        assert_eq!(circuit.num_operations(), 3);
434    }
435
436    #[test]
437    fn test_conditional_validation_invalid_register() {
438        // Test that using a non-existent register in a condition produces an error
439        let mut circuit = ClassicalCircuit::<2>::new();
440        circuit
441            .add_classical_register("measurement", 1)
442            .expect("Failed to add classical register");
443
444        // Try to add a conditional gate with a condition referencing a non-existent register
445        let condition = ClassicalCondition::register_equals("nonexistent", 1);
446        let result = circuit.add_conditional(condition, PauliX { target: QubitId(0) });
447
448        assert!(result.is_err());
449        match result {
450            Err(QuantRS2Error::InvalidInput(msg)) => {
451                assert!(msg.contains("nonexistent"));
452                assert!(msg.contains("not found"));
453            }
454            _ => panic!("Expected InvalidInput error"),
455        }
456    }
457
458    #[test]
459    fn test_conditional_validation_valid_register() {
460        // Test that using a valid register works correctly
461        let mut circuit = ClassicalCircuit::<2>::new();
462        circuit
463            .add_classical_register("measurement", 1)
464            .expect("Failed to add classical register");
465        circuit
466            .measure(QubitId(0), "measurement", 0)
467            .expect("Failed to add measurement");
468
469        // Add a conditional gate with a valid condition
470        let condition = ClassicalCondition::register_equals("measurement", 1);
471        let result = circuit.add_conditional(condition, PauliX { target: QubitId(1) });
472
473        assert!(result.is_ok());
474        assert_eq!(circuit.num_operations(), 2); // Measure + Conditional
475    }
476}