quantrs2_circuit/
builder.rs

1//! Builder types for quantum circuits.
2//!
3//! This module contains the Circuit type for building and
4//! executing quantum circuits.
5
6use std::collections::HashMap;
7use std::fmt;
8use std::sync::Arc;
9
10/// Type alias for backwards compatibility
11pub type CircuitBuilder<const N: usize> = Circuit<N>;
12
13use quantrs2_core::{
14    decomposition::{utils as decomp_utils, CompositeGate},
15    error::QuantRS2Result,
16    gate::{
17        multi::{Fredkin, Toffoli, CH, CNOT, CRX, CRY, CRZ, CS, CY, CZ, SWAP},
18        single::{
19            Hadamard, PauliX, PauliY, PauliZ, Phase, PhaseDagger, RotationX, RotationY, RotationZ,
20            SqrtX, SqrtXDagger, TDagger, T,
21        },
22        GateOp,
23    },
24    qubit::QubitId,
25    register::Register,
26};
27
28use scirs2_core::Complex64;
29use std::any::Any;
30use std::collections::HashSet;
31
32/// Circuit statistics for introspection and optimization
33#[derive(Debug, Clone)]
34pub struct CircuitStats {
35    /// Total number of gates
36    pub total_gates: usize,
37    /// Gate counts by type
38    pub gate_counts: HashMap<String, usize>,
39    /// Circuit depth (sequential length)
40    pub depth: usize,
41    /// Number of two-qubit gates
42    pub two_qubit_gates: usize,
43    /// Number of multi-qubit gates (3+)
44    pub multi_qubit_gates: usize,
45    /// Gate density (gates per qubit)
46    pub gate_density: f64,
47    /// Number of qubits actually used
48    pub used_qubits: usize,
49    /// Total qubits available
50    pub total_qubits: usize,
51}
52
53/// Gate pool for reusing common gates to reduce memory allocations
54#[derive(Debug, Clone)]
55pub struct GatePool {
56    /// Common single-qubit gates that can be shared
57    gates: HashMap<String, Arc<dyn GateOp + Send + Sync>>,
58}
59
60impl GatePool {
61    /// Create a new gate pool with common gates pre-allocated
62    pub fn new() -> Self {
63        let mut gates = HashMap::with_capacity(16);
64
65        // Pre-allocate common gates for different qubits
66        for qubit_id in 0..32 {
67            let qubit = QubitId::new(qubit_id);
68
69            // Common single-qubit gates
70            gates.insert(
71                format!("H_{}", qubit_id),
72                Arc::new(Hadamard { target: qubit }) as Arc<dyn GateOp + Send + Sync>,
73            );
74            gates.insert(
75                format!("X_{}", qubit_id),
76                Arc::new(PauliX { target: qubit }) as Arc<dyn GateOp + Send + Sync>,
77            );
78            gates.insert(
79                format!("Y_{}", qubit_id),
80                Arc::new(PauliY { target: qubit }) as Arc<dyn GateOp + Send + Sync>,
81            );
82            gates.insert(
83                format!("Z_{}", qubit_id),
84                Arc::new(PauliZ { target: qubit }) as Arc<dyn GateOp + Send + Sync>,
85            );
86            gates.insert(
87                format!("S_{}", qubit_id),
88                Arc::new(Phase { target: qubit }) as Arc<dyn GateOp + Send + Sync>,
89            );
90            gates.insert(
91                format!("T_{}", qubit_id),
92                Arc::new(T { target: qubit }) as Arc<dyn GateOp + Send + Sync>,
93            );
94        }
95
96        Self { gates }
97    }
98
99    /// Get a gate from the pool if available, otherwise create new
100    pub fn get_gate<G: GateOp + Clone + Send + Sync + 'static>(
101        &mut self,
102        gate: G,
103    ) -> Arc<dyn GateOp + Send + Sync> {
104        let key = format!("{}_{:?}", gate.name(), gate.qubits());
105
106        if let Some(cached_gate) = self.gates.get(&key) {
107            cached_gate.clone()
108        } else {
109            let arc_gate = Arc::new(gate) as Arc<dyn GateOp + Send + Sync>;
110            self.gates.insert(key, arc_gate.clone());
111            arc_gate
112        }
113    }
114}
115
116impl Default for GatePool {
117    fn default() -> Self {
118        Self::new()
119    }
120}
121
122/// A placeholder measurement gate for QASM export
123#[derive(Debug, Clone)]
124pub struct Measure {
125    pub target: QubitId,
126}
127
128impl GateOp for Measure {
129    fn name(&self) -> &'static str {
130        "measure"
131    }
132
133    fn qubits(&self) -> Vec<QubitId> {
134        vec![self.target]
135    }
136
137    fn is_parameterized(&self) -> bool {
138        false
139    }
140
141    fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
142        // Measurement doesn't have a unitary matrix representation
143        Ok(vec![
144            Complex64::new(1.0, 0.0),
145            Complex64::new(0.0, 0.0),
146            Complex64::new(0.0, 0.0),
147            Complex64::new(1.0, 0.0),
148        ])
149    }
150
151    fn as_any(&self) -> &dyn Any {
152        self
153    }
154
155    fn clone_gate(&self) -> Box<dyn GateOp> {
156        Box::new(self.clone())
157    }
158}
159
160/// A quantum circuit with a fixed number of qubits
161pub struct Circuit<const N: usize> {
162    /// Vector of gates to be applied in sequence using Arc for shared ownership
163    gates: Vec<Arc<dyn GateOp + Send + Sync>>,
164    /// Gate pool for reusing common gates
165    gate_pool: GatePool,
166}
167
168impl<const N: usize> Clone for Circuit<N> {
169    fn clone(&self) -> Self {
170        // With Arc, cloning is much more efficient - just clone the references
171        Self {
172            gates: self.gates.clone(),
173            gate_pool: self.gate_pool.clone(),
174        }
175    }
176}
177
178impl<const N: usize> fmt::Debug for Circuit<N> {
179    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180        f.debug_struct("Circuit")
181            .field("num_qubits", &N)
182            .field("num_gates", &self.gates.len())
183            .finish()
184    }
185}
186
187impl<const N: usize> Circuit<N> {
188    /// Create a new empty circuit with N qubits
189    pub fn new() -> Self {
190        Self {
191            gates: Vec::with_capacity(64), // Pre-allocate capacity for better performance
192            gate_pool: GatePool::new(),
193        }
194    }
195
196    /// Create a new circuit with estimated capacity
197    pub fn with_capacity(capacity: usize) -> Self {
198        Self {
199            gates: Vec::with_capacity(capacity),
200            gate_pool: GatePool::new(),
201        }
202    }
203
204    /// Add a gate to the circuit
205    pub fn add_gate<G: GateOp + Clone + Send + Sync + 'static>(
206        &mut self,
207        gate: G,
208    ) -> QuantRS2Result<&mut Self> {
209        // Validate that all qubits are within range
210        for qubit in gate.qubits() {
211            if qubit.id() as usize >= N {
212                return Err(quantrs2_core::error::QuantRS2Error::InvalidInput(format!(
213                    "Gate '{}' targets qubit {} which is out of range for {}-qubit circuit (valid range: 0-{})",
214                    gate.name(),
215                    qubit.id(),
216                    N,
217                    N - 1
218                )));
219            }
220        }
221
222        // Use gate pool for common gates to reduce memory allocations
223        let gate_arc = self.gate_pool.get_gate(gate);
224        self.gates.push(gate_arc);
225        Ok(self)
226    }
227
228    /// Add a gate from an Arc (for copying gates between circuits)
229    pub fn add_gate_arc(
230        &mut self,
231        gate: Arc<dyn GateOp + Send + Sync>,
232    ) -> QuantRS2Result<&mut Self> {
233        // Validate that all qubits are within range
234        for qubit in gate.qubits() {
235            if qubit.id() as usize >= N {
236                return Err(quantrs2_core::error::QuantRS2Error::InvalidInput(format!(
237                    "Gate '{}' targets qubit {} which is out of range for {}-qubit circuit (valid range: 0-{})",
238                    gate.name(),
239                    qubit.id(),
240                    N,
241                    N - 1
242                )));
243            }
244        }
245
246        self.gates.push(gate);
247        Ok(self)
248    }
249
250    /// Get all gates in the circuit
251    pub fn gates(&self) -> &[Arc<dyn GateOp + Send + Sync>] {
252        &self.gates
253    }
254
255    /// Get gates as Vec for compatibility with existing optimization code
256    pub fn gates_as_boxes(&self) -> Vec<Box<dyn GateOp>> {
257        self.gates
258            .iter()
259            .map(|arc_gate| arc_gate.clone_gate())
260            .collect()
261    }
262
263    /// Circuit introspection methods for optimization
264
265    /// Count gates by type
266    pub fn count_gates_by_type(&self) -> HashMap<String, usize> {
267        let mut counts = HashMap::new();
268        for gate in &self.gates {
269            *counts.entry(gate.name().to_string()).or_insert(0) += 1;
270        }
271        counts
272    }
273
274    /// Calculate circuit depth (longest sequential path)
275    pub fn calculate_depth(&self) -> usize {
276        if self.gates.is_empty() {
277            return 0;
278        }
279
280        // Track the last time each qubit was used
281        let mut qubit_last_used = vec![0; N];
282        let mut max_depth = 0;
283
284        for (gate_idx, gate) in self.gates.iter().enumerate() {
285            let gate_qubits = gate.qubits();
286
287            // Find the maximum depth among all qubits this gate uses
288            let gate_start_depth = gate_qubits
289                .iter()
290                .map(|q| qubit_last_used[q.id() as usize])
291                .max()
292                .unwrap_or(0);
293
294            let gate_end_depth = gate_start_depth + 1;
295
296            // Update the depth for all qubits this gate touches
297            for qubit in gate_qubits {
298                qubit_last_used[qubit.id() as usize] = gate_end_depth;
299            }
300
301            max_depth = max_depth.max(gate_end_depth);
302        }
303
304        max_depth
305    }
306
307    /// Count two-qubit gates
308    pub fn count_two_qubit_gates(&self) -> usize {
309        self.gates
310            .iter()
311            .filter(|gate| gate.qubits().len() == 2)
312            .count()
313    }
314
315    /// Count multi-qubit gates (3 or more qubits)
316    pub fn count_multi_qubit_gates(&self) -> usize {
317        self.gates
318            .iter()
319            .filter(|gate| gate.qubits().len() >= 3)
320            .count()
321    }
322
323    /// Calculate the critical path length (same as depth for now, but could be enhanced)
324    pub fn calculate_critical_path(&self) -> usize {
325        self.calculate_depth()
326    }
327
328    /// Calculate gate density (gates per qubit)
329    pub fn calculate_gate_density(&self) -> f64 {
330        if N == 0 {
331            0.0
332        } else {
333            self.gates.len() as f64 / N as f64
334        }
335    }
336
337    /// Get all unique qubits used in the circuit
338    pub fn get_used_qubits(&self) -> HashSet<QubitId> {
339        let mut used_qubits = HashSet::new();
340        for gate in &self.gates {
341            for qubit in gate.qubits() {
342                used_qubits.insert(qubit);
343            }
344        }
345        used_qubits
346    }
347
348    /// Check if the circuit uses all available qubits
349    pub fn uses_all_qubits(&self) -> bool {
350        self.get_used_qubits().len() == N
351    }
352
353    /// Get gates that operate on a specific qubit
354    pub fn gates_on_qubit(&self, target_qubit: QubitId) -> Vec<&Arc<dyn GateOp + Send + Sync>> {
355        self.gates
356            .iter()
357            .filter(|gate| gate.qubits().contains(&target_qubit))
358            .collect()
359    }
360
361    /// Get gates between two indices (inclusive)
362    pub fn gates_in_range(&self, start: usize, end: usize) -> &[Arc<dyn GateOp + Send + Sync>] {
363        let end = end.min(self.gates.len().saturating_sub(1));
364        let start = start.min(end);
365        &self.gates[start..=end]
366    }
367
368    /// Check if circuit is empty
369    pub fn is_empty(&self) -> bool {
370        self.gates.is_empty()
371    }
372
373    /// Get circuit statistics summary
374    pub fn get_stats(&self) -> CircuitStats {
375        let gate_counts = self.count_gates_by_type();
376        let depth = self.calculate_depth();
377        let two_qubit_gates = self.count_two_qubit_gates();
378        let multi_qubit_gates = self.count_multi_qubit_gates();
379        let gate_density = self.calculate_gate_density();
380        let used_qubits = self.get_used_qubits().len();
381
382        CircuitStats {
383            total_gates: self.gates.len(),
384            gate_counts,
385            depth,
386            two_qubit_gates,
387            multi_qubit_gates,
388            gate_density,
389            used_qubits,
390            total_qubits: N,
391        }
392    }
393
394    /// Get the number of qubits in the circuit
395    pub fn num_qubits(&self) -> usize {
396        N
397    }
398
399    /// Get the number of gates in the circuit
400    pub fn num_gates(&self) -> usize {
401        self.gates.len()
402    }
403
404    /// Get the names of all gates in the circuit
405    pub fn get_gate_names(&self) -> Vec<String> {
406        self.gates
407            .iter()
408            .map(|gate| gate.name().to_string())
409            .collect()
410    }
411
412    /// Get a qubit for a specific single-qubit gate by gate type and index
413    pub fn get_single_qubit_for_gate(&self, gate_type: &str, index: usize) -> pyo3::PyResult<u32> {
414        self.find_gate_by_type_and_index(gate_type, index)
415            .and_then(|gate| {
416                if gate.qubits().len() == 1 {
417                    Some(gate.qubits()[0].id())
418                } else {
419                    None
420                }
421            })
422            .ok_or_else(|| {
423                pyo3::exceptions::PyValueError::new_err(format!(
424                    "Gate {} at index {} not found or is not a single-qubit gate",
425                    gate_type, index
426                ))
427            })
428    }
429
430    /// Get rotation parameters (qubit, angle) for a specific gate by gate type and index
431    pub fn get_rotation_params_for_gate(
432        &self,
433        gate_type: &str,
434        index: usize,
435    ) -> pyo3::PyResult<(u32, f64)> {
436        // Note: This is a simplified implementation, actual implementation would check
437        // gate type and extract the rotation parameter
438        self.find_gate_by_type_and_index(gate_type, index)
439            .and_then(|gate| {
440                if gate.qubits().len() == 1 {
441                    // Default angle (in a real implementation, we would extract this from the gate)
442                    Some((gate.qubits()[0].id(), 0.0))
443                } else {
444                    None
445                }
446            })
447            .ok_or_else(|| {
448                pyo3::exceptions::PyValueError::new_err(format!(
449                    "Gate {} at index {} not found or is not a rotation gate",
450                    gate_type, index
451                ))
452            })
453    }
454
455    /// Get two-qubit parameters (control, target) for a specific gate by gate type and index
456    pub fn get_two_qubit_params_for_gate(
457        &self,
458        gate_type: &str,
459        index: usize,
460    ) -> pyo3::PyResult<(u32, u32)> {
461        self.find_gate_by_type_and_index(gate_type, index)
462            .and_then(|gate| {
463                if gate.qubits().len() == 2 {
464                    Some((gate.qubits()[0].id(), gate.qubits()[1].id()))
465                } else {
466                    None
467                }
468            })
469            .ok_or_else(|| {
470                pyo3::exceptions::PyValueError::new_err(format!(
471                    "Gate {} at index {} not found or is not a two-qubit gate",
472                    gate_type, index
473                ))
474            })
475    }
476
477    /// Get controlled rotation parameters (control, target, angle) for a specific gate
478    pub fn get_controlled_rotation_params_for_gate(
479        &self,
480        gate_type: &str,
481        index: usize,
482    ) -> pyo3::PyResult<(u32, u32, f64)> {
483        // Note: This is a simplified implementation, actual implementation would check
484        // gate type and extract the rotation parameter
485        self.find_gate_by_type_and_index(gate_type, index)
486            .and_then(|gate| {
487                if gate.qubits().len() == 2 {
488                    // Default angle (in a real implementation, we would extract this from the gate)
489                    Some((gate.qubits()[0].id(), gate.qubits()[1].id(), 0.0))
490                } else {
491                    None
492                }
493            })
494            .ok_or_else(|| {
495                pyo3::exceptions::PyValueError::new_err(format!(
496                    "Gate {} at index {} not found or is not a controlled rotation gate",
497                    gate_type, index
498                ))
499            })
500    }
501
502    /// Get three-qubit parameters for gates like Toffoli or Fredkin
503    pub fn get_three_qubit_params_for_gate(
504        &self,
505        gate_type: &str,
506        index: usize,
507    ) -> pyo3::PyResult<(u32, u32, u32)> {
508        self.find_gate_by_type_and_index(gate_type, index)
509            .and_then(|gate| {
510                if gate.qubits().len() == 3 {
511                    Some((
512                        gate.qubits()[0].id(),
513                        gate.qubits()[1].id(),
514                        gate.qubits()[2].id(),
515                    ))
516                } else {
517                    None
518                }
519            })
520            .ok_or_else(|| {
521                pyo3::exceptions::PyValueError::new_err(format!(
522                    "Gate {} at index {} not found or is not a three-qubit gate",
523                    gate_type, index
524                ))
525            })
526    }
527
528    /// Helper method to find a gate by type and index
529    fn find_gate_by_type_and_index(&self, gate_type: &str, index: usize) -> Option<&dyn GateOp> {
530        let mut count = 0;
531        for gate in &self.gates {
532            if gate.name() == gate_type {
533                if count == index {
534                    return Some(gate.as_ref());
535                }
536                count += 1;
537            }
538        }
539        None
540    }
541
542    /// Apply a Hadamard gate to a qubit
543    pub fn h(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
544        self.add_gate(Hadamard {
545            target: target.into(),
546        })
547    }
548
549    /// Apply a Pauli-X gate to a qubit
550    pub fn x(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
551        self.add_gate(PauliX {
552            target: target.into(),
553        })
554    }
555
556    /// Apply a Pauli-Y gate to a qubit
557    pub fn y(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
558        self.add_gate(PauliY {
559            target: target.into(),
560        })
561    }
562
563    /// Apply a Pauli-Z gate to a qubit
564    pub fn z(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
565        self.add_gate(PauliZ {
566            target: target.into(),
567        })
568    }
569
570    /// Apply a rotation around X-axis
571    pub fn rx(&mut self, target: impl Into<QubitId>, theta: f64) -> QuantRS2Result<&mut Self> {
572        self.add_gate(RotationX {
573            target: target.into(),
574            theta,
575        })
576    }
577
578    /// Apply a rotation around Y-axis
579    pub fn ry(&mut self, target: impl Into<QubitId>, theta: f64) -> QuantRS2Result<&mut Self> {
580        self.add_gate(RotationY {
581            target: target.into(),
582            theta,
583        })
584    }
585
586    /// Apply a rotation around Z-axis
587    pub fn rz(&mut self, target: impl Into<QubitId>, theta: f64) -> QuantRS2Result<&mut Self> {
588        self.add_gate(RotationZ {
589            target: target.into(),
590            theta,
591        })
592    }
593
594    /// Apply a Phase gate (S gate)
595    pub fn s(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
596        self.add_gate(Phase {
597            target: target.into(),
598        })
599    }
600
601    /// Apply a Phase-dagger gate (S† gate)
602    pub fn sdg(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
603        self.add_gate(PhaseDagger {
604            target: target.into(),
605        })
606    }
607
608    /// Apply a T gate
609    pub fn t(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
610        self.add_gate(T {
611            target: target.into(),
612        })
613    }
614
615    /// Apply a T-dagger gate (T† gate)
616    pub fn tdg(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
617        self.add_gate(TDagger {
618            target: target.into(),
619        })
620    }
621
622    /// Apply a Square Root of X gate (√X)
623    pub fn sx(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
624        self.add_gate(SqrtX {
625            target: target.into(),
626        })
627    }
628
629    /// Apply a Square Root of X Dagger gate (√X†)
630    pub fn sxdg(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
631        self.add_gate(SqrtXDagger {
632            target: target.into(),
633        })
634    }
635
636    /// Apply a CNOT gate
637    pub fn cnot(
638        &mut self,
639        control: impl Into<QubitId>,
640        target: impl Into<QubitId>,
641    ) -> QuantRS2Result<&mut Self> {
642        self.add_gate(CNOT {
643            control: control.into(),
644            target: target.into(),
645        })
646    }
647
648    /// Apply a CNOT gate (alias for cnot)
649    pub fn cx(
650        &mut self,
651        control: impl Into<QubitId>,
652        target: impl Into<QubitId>,
653    ) -> QuantRS2Result<&mut Self> {
654        self.cnot(control, target)
655    }
656
657    /// Apply a CY gate (Controlled-Y)
658    pub fn cy(
659        &mut self,
660        control: impl Into<QubitId>,
661        target: impl Into<QubitId>,
662    ) -> QuantRS2Result<&mut Self> {
663        self.add_gate(CY {
664            control: control.into(),
665            target: target.into(),
666        })
667    }
668
669    /// Apply a CZ gate (Controlled-Z)
670    pub fn cz(
671        &mut self,
672        control: impl Into<QubitId>,
673        target: impl Into<QubitId>,
674    ) -> QuantRS2Result<&mut Self> {
675        self.add_gate(CZ {
676            control: control.into(),
677            target: target.into(),
678        })
679    }
680
681    /// Apply a CH gate (Controlled-Hadamard)
682    pub fn ch(
683        &mut self,
684        control: impl Into<QubitId>,
685        target: impl Into<QubitId>,
686    ) -> QuantRS2Result<&mut Self> {
687        self.add_gate(CH {
688            control: control.into(),
689            target: target.into(),
690        })
691    }
692
693    /// Apply a CS gate (Controlled-Phase/S)
694    pub fn cs(
695        &mut self,
696        control: impl Into<QubitId>,
697        target: impl Into<QubitId>,
698    ) -> QuantRS2Result<&mut Self> {
699        self.add_gate(CS {
700            control: control.into(),
701            target: target.into(),
702        })
703    }
704
705    /// Apply a controlled rotation around X-axis (CRX)
706    pub fn crx(
707        &mut self,
708        control: impl Into<QubitId>,
709        target: impl Into<QubitId>,
710        theta: f64,
711    ) -> QuantRS2Result<&mut Self> {
712        self.add_gate(CRX {
713            control: control.into(),
714            target: target.into(),
715            theta,
716        })
717    }
718
719    /// Apply a controlled rotation around Y-axis (CRY)
720    pub fn cry(
721        &mut self,
722        control: impl Into<QubitId>,
723        target: impl Into<QubitId>,
724        theta: f64,
725    ) -> QuantRS2Result<&mut Self> {
726        self.add_gate(CRY {
727            control: control.into(),
728            target: target.into(),
729            theta,
730        })
731    }
732
733    /// Apply a controlled rotation around Z-axis (CRZ)
734    pub fn crz(
735        &mut self,
736        control: impl Into<QubitId>,
737        target: impl Into<QubitId>,
738        theta: f64,
739    ) -> QuantRS2Result<&mut Self> {
740        self.add_gate(CRZ {
741            control: control.into(),
742            target: target.into(),
743            theta,
744        })
745    }
746
747    /// Apply a controlled phase gate
748    pub fn cp(
749        &mut self,
750        control: impl Into<QubitId>,
751        target: impl Into<QubitId>,
752        lambda: f64,
753    ) -> QuantRS2Result<&mut Self> {
754        // CRZ(lambda) is equivalent to CP(lambda) up to a global phase
755        self.crz(control, target, lambda)
756    }
757
758    /// Apply a SWAP gate
759    pub fn swap(
760        &mut self,
761        qubit1: impl Into<QubitId>,
762        qubit2: impl Into<QubitId>,
763    ) -> QuantRS2Result<&mut Self> {
764        self.add_gate(SWAP {
765            qubit1: qubit1.into(),
766            qubit2: qubit2.into(),
767        })
768    }
769
770    /// Apply a Toffoli (CCNOT) gate
771    pub fn toffoli(
772        &mut self,
773        control1: impl Into<QubitId>,
774        control2: impl Into<QubitId>,
775        target: impl Into<QubitId>,
776    ) -> QuantRS2Result<&mut Self> {
777        self.add_gate(Toffoli {
778            control1: control1.into(),
779            control2: control2.into(),
780            target: target.into(),
781        })
782    }
783
784    /// Apply a Fredkin (CSWAP) gate
785    pub fn cswap(
786        &mut self,
787        control: impl Into<QubitId>,
788        target1: impl Into<QubitId>,
789        target2: impl Into<QubitId>,
790    ) -> QuantRS2Result<&mut Self> {
791        self.add_gate(Fredkin {
792            control: control.into(),
793            target1: target1.into(),
794            target2: target2.into(),
795        })
796    }
797
798    /// Measure a qubit (currently adds a placeholder measure gate)
799    ///
800    /// Note: This is currently a placeholder implementation for QASM export compatibility.
801    /// For actual quantum measurements, use the measurement module functionality.
802    pub fn measure(&mut self, qubit: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
803        let qubit_id = qubit.into();
804        self.add_gate(Measure { target: qubit_id })?;
805        Ok(self)
806    }
807
808    /// Reset a qubit to |0⟩ state
809    ///
810    /// Note: This operation is not yet fully implemented.
811    /// Reset operations are complex and require special handling in quantum circuits.
812    pub fn reset(&mut self, _qubit: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
813        Err(quantrs2_core::error::QuantRS2Error::UnsupportedOperation(
814            "Reset operation is not yet implemented. Reset requires special quantum state manipulation.".to_string()
815        ))
816    }
817
818    /// Add a barrier to prevent optimization across this point
819    ///
820    /// Barriers are used to prevent gate optimization algorithms from reordering gates
821    /// across specific points in the circuit. This is useful for maintaining timing
822    /// constraints or preserving specific circuit structure.
823    pub fn barrier(&mut self, qubits: &[QubitId]) -> QuantRS2Result<&mut Self> {
824        // Validate all qubits are within range
825        for &qubit in qubits {
826            if qubit.id() as usize >= N {
827                return Err(quantrs2_core::error::QuantRS2Error::InvalidQubitId(
828                    qubit.id(),
829                ));
830            }
831        }
832
833        // For now, barriers are implicit - they don't add gates but could be used
834        // by optimization passes. In a full implementation, we'd store barrier information
835        // for use by the optimization framework.
836
837        // TODO: Implement barrier storage for optimization passes
838        Ok(self)
839    }
840
841    /// Run the circuit on a simulator
842    pub fn run<S: Simulator<N>>(&self, simulator: S) -> QuantRS2Result<Register<N>> {
843        simulator.run(self)
844    }
845
846    /// Decompose the circuit into a sequence of standard gates
847    ///
848    /// This method will return a new circuit with complex gates decomposed
849    /// into sequences of simpler gates.
850    pub fn decompose(&self) -> QuantRS2Result<Self> {
851        let mut decomposed = Self::new();
852
853        // Convert Arc gates to Box gates for compatibility with decomposition utilities
854        let boxed_gates = self.gates_as_boxes();
855
856        // Decompose all gates
857        let simple_gates = decomp_utils::decompose_circuit(&boxed_gates)?;
858
859        // Add each decomposed gate to the new circuit
860        for gate in simple_gates {
861            decomposed.add_gate_box(gate)?;
862        }
863
864        Ok(decomposed)
865    }
866
867    /// Build the circuit (for compatibility - returns self)
868    pub fn build(self) -> Self {
869        self
870    }
871
872    /// Optimize the circuit by combining or removing gates
873    ///
874    /// This method will return a new circuit with simplified gates
875    /// by removing unnecessary gates or combining adjacent gates.
876    pub fn optimize(&self) -> QuantRS2Result<Self> {
877        let mut optimized = Self::new();
878
879        // Convert Arc gates to Box gates for compatibility with optimization utilities
880        let boxed_gates = self.gates_as_boxes();
881
882        // Optimize the gate sequence
883        let simplified_gates_result = decomp_utils::optimize_gate_sequence(&boxed_gates);
884
885        // Add each optimized gate to the new circuit
886        if let Ok(simplified_gates) = simplified_gates_result {
887            // We need to handle each gate individually
888            for g in simplified_gates {
889                optimized.add_gate_box(g)?;
890            }
891        }
892
893        Ok(optimized)
894    }
895
896    /// Add a raw boxed gate to the circuit
897    /// This is an internal utility and not part of the public API
898    fn add_gate_box(&mut self, gate: Box<dyn GateOp>) -> QuantRS2Result<&mut Self> {
899        // Validate that all qubits are within range
900        for qubit in gate.qubits() {
901            if qubit.id() as usize >= N {
902                return Err(quantrs2_core::error::QuantRS2Error::InvalidInput(format!(
903                    "Gate '{}' targets qubit {} which is out of range for {}-qubit circuit (valid range: 0-{})",
904                    gate.name(),
905                    qubit.id(),
906                    N,
907                    N - 1
908                )));
909            }
910        }
911
912        // For now, convert via cloning until we can update all callers to use Arc directly
913        // This maintains safety but has some performance cost
914        let cloned_gate = gate.clone_gate();
915
916        // Convert the specific gate types to Arc using match
917        if let Some(h_gate) = cloned_gate.as_any().downcast_ref::<Hadamard>() {
918            self.gates
919                .push(Arc::new(h_gate.clone()) as Arc<dyn GateOp + Send + Sync>);
920        } else if let Some(x_gate) = cloned_gate.as_any().downcast_ref::<PauliX>() {
921            self.gates
922                .push(Arc::new(x_gate.clone()) as Arc<dyn GateOp + Send + Sync>);
923        } else if let Some(y_gate) = cloned_gate.as_any().downcast_ref::<PauliY>() {
924            self.gates
925                .push(Arc::new(y_gate.clone()) as Arc<dyn GateOp + Send + Sync>);
926        } else if let Some(z_gate) = cloned_gate.as_any().downcast_ref::<PauliZ>() {
927            self.gates
928                .push(Arc::new(z_gate.clone()) as Arc<dyn GateOp + Send + Sync>);
929        } else if let Some(cnot_gate) = cloned_gate.as_any().downcast_ref::<CNOT>() {
930            self.gates
931                .push(Arc::new(cnot_gate.clone()) as Arc<dyn GateOp + Send + Sync>);
932        } else if let Some(measure_gate) = cloned_gate.as_any().downcast_ref::<Measure>() {
933            self.gates
934                .push(Arc::new(measure_gate.clone()) as Arc<dyn GateOp + Send + Sync>);
935        } else {
936            // For unknown gate types, we'll use a less efficient fallback
937            // TODO: Extend this to cover all gate types or implement a better conversion mechanism
938            return Err(quantrs2_core::error::QuantRS2Error::UnsupportedOperation(
939                format!(
940                    "Gate type '{}' not yet supported in Arc conversion",
941                    gate.name()
942                ),
943            ));
944        }
945
946        Ok(self)
947    }
948
949    /// Create a composite gate from a subsequence of this circuit
950    ///
951    /// This method allows creating a custom gate that combines several
952    /// other gates, which can be applied as a single unit to a circuit.
953    pub fn create_composite(
954        &self,
955        start_idx: usize,
956        end_idx: usize,
957        name: &str,
958    ) -> QuantRS2Result<CompositeGate> {
959        if start_idx >= self.gates.len() || end_idx > self.gates.len() || start_idx >= end_idx {
960            return Err(quantrs2_core::error::QuantRS2Error::InvalidInput(format!(
961                "Invalid start/end indices ({}/{}) for circuit with {} gates",
962                start_idx,
963                end_idx,
964                self.gates.len()
965            )));
966        }
967
968        // Get the gates in the specified range
969        // We need to create box clones of each gate
970        let mut gates: Vec<Box<dyn GateOp>> = Vec::new();
971        for gate in &self.gates[start_idx..end_idx] {
972            gates.push(decomp_utils::clone_gate(gate.as_ref())?);
973        }
974
975        // Collect all unique qubits these gates act on
976        let mut qubits = Vec::new();
977        for gate in &gates {
978            for qubit in gate.qubits() {
979                if !qubits.contains(&qubit) {
980                    qubits.push(qubit);
981                }
982            }
983        }
984
985        Ok(CompositeGate {
986            gates,
987            qubits,
988            name: name.to_string(),
989        })
990    }
991
992    /// Add all gates from a composite gate to this circuit
993    pub fn add_composite(&mut self, composite: &CompositeGate) -> QuantRS2Result<&mut Self> {
994        // Clone each gate from the composite and add to this circuit
995        for gate in &composite.gates {
996            // We can't directly clone a Box<dyn GateOp>, so we need a different approach
997            // We need to create a new gate by using the type information
998            // This is a simplified version - in a real implementation,
999            // we would have a more robust way to clone gates
1000            let gate_clone = decomp_utils::clone_gate(gate.as_ref())?;
1001            self.add_gate_box(gate_clone)?;
1002        }
1003
1004        Ok(self)
1005    }
1006
1007    // Classical control flow extensions
1008
1009    /// Measure all qubits in the circuit
1010    pub fn measure_all(&mut self) -> QuantRS2Result<&mut Self> {
1011        for i in 0..N {
1012            self.measure(QubitId(i as u32))?;
1013        }
1014        Ok(self)
1015    }
1016
1017    /// Convert this circuit to a ClassicalCircuit with classical control support
1018    pub fn with_classical_control(self) -> crate::classical::ClassicalCircuit<N> {
1019        let mut classical_circuit = crate::classical::ClassicalCircuit::new();
1020
1021        // Add a default classical register for measurements
1022        let _ = classical_circuit.add_classical_register("c", N);
1023
1024        // Transfer all gates, converting Arc to Box for compatibility
1025        for gate in self.gates {
1026            let boxed_gate = gate.clone_gate();
1027            classical_circuit
1028                .operations
1029                .push(crate::classical::CircuitOp::Quantum(boxed_gate));
1030        }
1031
1032        classical_circuit
1033    }
1034}
1035
1036impl<const N: usize> Default for Circuit<N> {
1037    fn default() -> Self {
1038        Self::new()
1039    }
1040}
1041
1042/// Trait for quantum circuit simulators
1043pub trait Simulator<const N: usize> {
1044    /// Run a quantum circuit and return the final register state
1045    fn run(&self, circuit: &Circuit<N>) -> QuantRS2Result<Register<N>>;
1046}