Skip to main content

quantrs2_circuit/
measurement.rs

1//! Mid-circuit measurement and feed-forward support
2//!
3//! This module provides functionality for performing measurements during circuit
4//! execution and using the results to control subsequent quantum operations.
5
6use crate::builder::Circuit;
7use crate::classical::{ClassicalCondition, ClassicalRegister};
8use quantrs2_core::{
9    error::{QuantRS2Error, QuantRS2Result},
10    gate::GateOp,
11    qubit::QubitId,
12};
13use std::collections::HashMap;
14use std::sync::Arc;
15
16/// Measurement operation that can be performed mid-circuit
17#[derive(Debug, Clone)]
18pub struct Measurement {
19    /// Qubit to measure
20    pub qubit: QubitId,
21    /// Classical register to store result
22    pub target_bit: usize,
23    /// Optional label for the measurement
24    pub label: Option<String>,
25}
26
27impl Measurement {
28    /// Create a new measurement operation
29    #[must_use]
30    pub const fn new(qubit: QubitId, target_bit: usize) -> Self {
31        Self {
32            qubit,
33            target_bit,
34            label: None,
35        }
36    }
37
38    /// Add a label to the measurement
39    #[must_use]
40    pub fn with_label(mut self, label: String) -> Self {
41        self.label = Some(label);
42        self
43    }
44}
45
46/// Feed-forward operation based on measurement results
47#[derive(Debug, Clone)]
48pub struct FeedForward {
49    /// Condition for applying the operation
50    pub condition: ClassicalCondition,
51    /// Gate to apply if condition is met
52    pub gate: Box<dyn GateOp>,
53    /// Optional else gate
54    pub else_gate: Option<Box<dyn GateOp>>,
55}
56
57impl FeedForward {
58    /// Create a new feed-forward operation
59    #[must_use]
60    pub fn new(condition: ClassicalCondition, gate: Box<dyn GateOp>) -> Self {
61        Self {
62            condition,
63            gate,
64            else_gate: None,
65        }
66    }
67
68    /// Add an else gate to apply if condition is not met
69    #[must_use]
70    pub fn with_else(mut self, else_gate: Box<dyn GateOp>) -> Self {
71        self.else_gate = Some(else_gate);
72        self
73    }
74}
75
76/// Circuit operation that can include measurements and feed-forward
77#[derive(Debug, Clone)]
78pub enum CircuitOp {
79    /// Standard quantum gate
80    Gate(Box<dyn GateOp>),
81    /// Mid-circuit measurement
82    Measure(Measurement),
83    /// Feed-forward operation
84    FeedForward(FeedForward),
85    /// Barrier for synchronization
86    Barrier(Vec<QubitId>),
87    /// Reset qubit to |0⟩
88    Reset(QubitId),
89}
90
91/// Enhanced circuit builder with measurement support
92pub struct MeasurementCircuit<const N: usize> {
93    /// Operations in the circuit
94    operations: Vec<CircuitOp>,
95    /// Classical registers for storing measurement results
96    classical_registers: HashMap<String, ClassicalRegister>,
97    /// Measurement count for tracking
98    measurement_count: usize,
99    /// Current classical bit allocation
100    current_bit: usize,
101}
102
103impl<const N: usize> Default for MeasurementCircuit<N> {
104    fn default() -> Self {
105        Self::new()
106    }
107}
108
109impl<const N: usize> MeasurementCircuit<N> {
110    /// Create a new measurement-enabled circuit
111    #[must_use]
112    pub fn new() -> Self {
113        let mut classical_registers = HashMap::new();
114        classical_registers.insert(
115            "default".to_string(),
116            ClassicalRegister::new("default".to_string(), N),
117        );
118
119        Self {
120            operations: Vec::new(),
121            classical_registers,
122            measurement_count: 0,
123            current_bit: 0,
124        }
125    }
126
127    /// Add a quantum gate
128    pub fn add_gate(&mut self, gate: Box<dyn GateOp>) -> QuantRS2Result<()> {
129        // Validate qubit indices
130        for qubit in gate.qubits() {
131            if qubit.id() >= N as u32 {
132                return Err(QuantRS2Error::InvalidQubitId(qubit.id()));
133            }
134        }
135
136        self.operations.push(CircuitOp::Gate(gate));
137        Ok(())
138    }
139
140    /// Add a mid-circuit measurement
141    pub fn measure(&mut self, qubit: QubitId) -> QuantRS2Result<usize> {
142        if qubit.id() >= N as u32 {
143            return Err(QuantRS2Error::InvalidQubitId(qubit.id()));
144        }
145
146        if self.current_bit >= N {
147            return Err(QuantRS2Error::InvalidInput(
148                "Not enough classical bits for measurement".to_string(),
149            ));
150        }
151
152        let target_bit = self.current_bit;
153        self.current_bit += 1;
154
155        let measurement =
156            Measurement::new(qubit, target_bit).with_label(format!("m{}", self.measurement_count));
157
158        self.operations.push(CircuitOp::Measure(measurement));
159        self.measurement_count += 1;
160
161        Ok(target_bit)
162    }
163
164    /// Add a conditional gate based on measurement result
165    pub fn add_conditional(
166        &mut self,
167        condition: ClassicalCondition,
168        gate: Box<dyn GateOp>,
169    ) -> QuantRS2Result<()> {
170        // Validate condition - simplified validation
171        // In a full implementation, this would validate the register references
172
173        // Validate gate qubits
174        for qubit in gate.qubits() {
175            if qubit.id() >= N as u32 {
176                return Err(QuantRS2Error::InvalidQubitId(qubit.id()));
177            }
178        }
179
180        let feed_forward = FeedForward::new(condition, gate);
181        self.operations.push(CircuitOp::FeedForward(feed_forward));
182        Ok(())
183    }
184
185    /// Add a conditional gate with else branch
186    pub fn add_if_else(
187        &mut self,
188        condition: ClassicalCondition,
189        if_gate: Box<dyn GateOp>,
190        else_gate: Box<dyn GateOp>,
191    ) -> QuantRS2Result<()> {
192        // Validate gates
193        for qubit in if_gate.qubits().iter().chain(else_gate.qubits().iter()) {
194            if qubit.id() >= N as u32 {
195                return Err(QuantRS2Error::InvalidQubitId(qubit.id()));
196            }
197        }
198
199        let feed_forward = FeedForward::new(condition, if_gate).with_else(else_gate);
200        self.operations.push(CircuitOp::FeedForward(feed_forward));
201        Ok(())
202    }
203
204    /// Add a barrier for synchronization
205    pub fn barrier(&mut self, qubits: Vec<QubitId>) -> QuantRS2Result<()> {
206        for qubit in &qubits {
207            if qubit.id() >= N as u32 {
208                return Err(QuantRS2Error::InvalidQubitId(qubit.id()));
209            }
210        }
211
212        self.operations.push(CircuitOp::Barrier(qubits));
213        Ok(())
214    }
215
216    /// Reset a qubit to |0⟩
217    pub fn reset(&mut self, qubit: QubitId) -> QuantRS2Result<()> {
218        if qubit.id() >= N as u32 {
219            return Err(QuantRS2Error::InvalidQubitId(qubit.id()));
220        }
221
222        self.operations.push(CircuitOp::Reset(qubit));
223        Ok(())
224    }
225
226    /// Get the number of operations
227    #[must_use]
228    pub fn num_operations(&self) -> usize {
229        self.operations.len()
230    }
231
232    /// Get the number of measurements
233    #[must_use]
234    pub const fn num_measurements(&self) -> usize {
235        self.measurement_count
236    }
237
238    /// Get all operations
239    #[must_use]
240    pub fn operations(&self) -> &[CircuitOp] {
241        &self.operations
242    }
243
244    /// Convert to a standard circuit (without measurements)
245    pub fn to_circuit(&self) -> QuantRS2Result<Circuit<N>> {
246        let mut circuit = Circuit::<N>::new();
247
248        for op in &self.operations {
249            match op {
250                CircuitOp::Gate(_)
251                | CircuitOp::Measure(_)
252                | CircuitOp::FeedForward(_)
253                | CircuitOp::Barrier(_)
254                | CircuitOp::Reset(_) => {
255                    // Skip: gates can't be easily converted, measurements/barriers/resets not in standard circuit
256                }
257            }
258        }
259
260        Ok(circuit)
261    }
262
263    /// Analyze the circuit for measurement dependencies
264    #[must_use]
265    pub fn analyze_dependencies(&self) -> MeasurementDependencies {
266        let mut deps = MeasurementDependencies::new();
267        let mut measurement_map = HashMap::new();
268
269        // First pass: collect all measurements
270        for (i, op) in self.operations.iter().enumerate() {
271            if let CircuitOp::Measure(m) = op {
272                measurement_map.insert(m.target_bit, i);
273                deps.measurements.push((i, m.clone()));
274            }
275        }
276
277        // Second pass: find feed-forward dependencies
278        for (i, op) in self.operations.iter().enumerate() {
279            if let CircuitOp::FeedForward(_ff) = op {
280                // In a full implementation, this would properly track classical dependencies
281                // For now, assume all feed-forward depends on previous measurements
282                if !measurement_map.is_empty() {
283                    let last_measurement = measurement_map.len() - 1;
284                    deps.feed_forward_deps.push((last_measurement, i));
285                }
286            }
287        }
288
289        deps
290    }
291}
292
293/// Analysis result for measurement dependencies
294#[derive(Debug)]
295pub struct MeasurementDependencies {
296    /// List of (index, measurement) pairs
297    pub measurements: Vec<(usize, Measurement)>,
298    /// List of (`measurement_index`, `feedforward_index`) dependencies
299    pub feed_forward_deps: Vec<(usize, usize)>,
300}
301
302impl MeasurementDependencies {
303    const fn new() -> Self {
304        Self {
305            measurements: Vec::new(),
306            feed_forward_deps: Vec::new(),
307        }
308    }
309
310    /// Check if there are any feed-forward operations
311    #[must_use]
312    pub fn has_feed_forward(&self) -> bool {
313        !self.feed_forward_deps.is_empty()
314    }
315
316    /// Get the number of measurements
317    #[must_use]
318    pub fn num_measurements(&self) -> usize {
319        self.measurements.len()
320    }
321}
322
323/// Builder pattern for measurement circuits
324pub struct MeasurementCircuitBuilder<const N: usize> {
325    circuit: MeasurementCircuit<N>,
326}
327
328impl<const N: usize> Default for MeasurementCircuitBuilder<N> {
329    fn default() -> Self {
330        Self::new()
331    }
332}
333
334impl<const N: usize> MeasurementCircuitBuilder<N> {
335    /// Create a new builder
336    #[must_use]
337    pub fn new() -> Self {
338        Self {
339            circuit: MeasurementCircuit::new(),
340        }
341    }
342
343    /// Add a gate
344    pub fn gate(mut self, gate: Box<dyn GateOp>) -> QuantRS2Result<Self> {
345        self.circuit.add_gate(gate)?;
346        Ok(self)
347    }
348
349    /// Add a measurement
350    pub fn measure(mut self, qubit: QubitId) -> QuantRS2Result<(Self, usize)> {
351        let bit = self.circuit.measure(qubit)?;
352        Ok((self, bit))
353    }
354
355    /// Add a conditional gate
356    pub fn when(
357        mut self,
358        condition: ClassicalCondition,
359        gate: Box<dyn GateOp>,
360    ) -> QuantRS2Result<Self> {
361        self.circuit.add_conditional(condition, gate)?;
362        Ok(self)
363    }
364
365    /// Add a conditional gate with else
366    pub fn if_else(
367        mut self,
368        condition: ClassicalCondition,
369        if_gate: Box<dyn GateOp>,
370        else_gate: Box<dyn GateOp>,
371    ) -> QuantRS2Result<Self> {
372        self.circuit.add_if_else(condition, if_gate, else_gate)?;
373        Ok(self)
374    }
375
376    /// Add a barrier
377    pub fn barrier(mut self, qubits: Vec<QubitId>) -> QuantRS2Result<Self> {
378        self.circuit.barrier(qubits)?;
379        Ok(self)
380    }
381
382    /// Reset a qubit
383    pub fn reset(mut self, qubit: QubitId) -> QuantRS2Result<Self> {
384        self.circuit.reset(qubit)?;
385        Ok(self)
386    }
387
388    /// Build the circuit
389    #[must_use]
390    pub fn build(self) -> MeasurementCircuit<N> {
391        self.circuit
392    }
393}
394
395#[cfg(test)]
396mod tests {
397    use super::*;
398    use quantrs2_core::gate::single::{Hadamard, PauliX};
399
400    #[test]
401    fn test_measurement_circuit() {
402        let mut circuit = MeasurementCircuit::<3>::new();
403
404        // Add Hadamard gate
405        circuit
406            .add_gate(Box::new(Hadamard { target: QubitId(0) }))
407            .expect("Failed to add Hadamard gate");
408
409        // Measure qubit 0
410        let bit0 = circuit
411            .measure(QubitId(0))
412            .expect("Failed to measure qubit 0");
413        assert_eq!(bit0, 0);
414
415        // Add conditional X gate
416        let condition = ClassicalCondition::equals(
417            crate::classical::ClassicalValue::Integer(bit0 as u64),
418            crate::classical::ClassicalValue::Integer(1),
419        );
420        circuit
421            .add_conditional(condition, Box::new(PauliX { target: QubitId(1) }))
422            .expect("Failed to add conditional X gate");
423
424        assert_eq!(circuit.num_operations(), 3);
425        assert_eq!(circuit.num_measurements(), 1);
426    }
427
428    #[test]
429    fn test_feed_forward() {
430        let mut circuit = MeasurementCircuit::<2>::new();
431
432        // Bell state preparation with measurement
433        circuit
434            .add_gate(Box::new(Hadamard { target: QubitId(0) }))
435            .expect("Failed to add Hadamard gate");
436        circuit
437            .add_gate(Box::new(quantrs2_core::gate::multi::CNOT {
438                control: QubitId(0),
439                target: QubitId(1),
440            }))
441            .expect("Failed to add CNOT gate");
442
443        // Measure first qubit
444        let bit = circuit
445            .measure(QubitId(0))
446            .expect("Failed to measure qubit 0");
447
448        // Apply X to second qubit if first measured as 1
449        let condition = ClassicalCondition::equals(
450            crate::classical::ClassicalValue::Integer(bit as u64),
451            crate::classical::ClassicalValue::Integer(1),
452        );
453        circuit
454            .add_conditional(condition, Box::new(PauliX { target: QubitId(1) }))
455            .expect("Failed to add conditional X gate");
456
457        // Analyze dependencies
458        let deps = circuit.analyze_dependencies();
459        assert_eq!(deps.num_measurements(), 1);
460        assert!(deps.has_feed_forward());
461    }
462
463    #[test]
464    fn test_builder_pattern() {
465        let (builder, bit) = MeasurementCircuitBuilder::<2>::new()
466            .gate(Box::new(Hadamard { target: QubitId(0) }))
467            .expect("Failed to add gate")
468            .measure(QubitId(0))
469            .expect("Failed to measure qubit");
470
471        let circuit = builder
472            .when(
473                ClassicalCondition::equals(
474                    crate::classical::ClassicalValue::Integer(bit as u64),
475                    crate::classical::ClassicalValue::Integer(1),
476                ),
477                Box::new(PauliX { target: QubitId(1) }),
478            )
479            .expect("Failed to add conditional gate")
480            .build();
481
482        assert_eq!(circuit.num_operations(), 3);
483    }
484}