Skip to main content

quantrs2_circuit/builder/
mod.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::{
18            Fredkin,
19            ISwap,
20            Toffoli,
21            CH,
22            CNOT,
23            CRX,
24            CRY,
25            CRZ,
26            CS,
27            CY,
28            CZ,
29            // Qiskit-compatible gates
30            DCX,
31            ECR,
32            RXX,
33            RYY,
34            RZX,
35            RZZ,
36            SWAP,
37        },
38        single::{
39            Hadamard,
40            // Qiskit-compatible gates
41            Identity,
42            PGate,
43            PauliX,
44            PauliY,
45            PauliZ,
46            Phase,
47            PhaseDagger,
48            RotationX,
49            RotationY,
50            RotationZ,
51            SqrtX,
52            SqrtXDagger,
53            TDagger,
54            UGate,
55            T,
56        },
57        GateOp,
58    },
59    qubit::QubitId,
60    register::Register,
61};
62
63use scirs2_core::Complex64;
64use std::any::Any;
65use std::collections::HashSet;
66
67/// Circuit statistics for introspection and optimization
68#[derive(Debug, Clone)]
69pub struct CircuitStats {
70    /// Total number of gates
71    pub total_gates: usize,
72    /// Gate counts by type
73    pub gate_counts: HashMap<String, usize>,
74    /// Circuit depth (sequential length)
75    pub depth: usize,
76    /// Number of two-qubit gates
77    pub two_qubit_gates: usize,
78    /// Number of multi-qubit gates (3+)
79    pub multi_qubit_gates: usize,
80    /// Gate density (gates per qubit)
81    pub gate_density: f64,
82    /// Number of qubits actually used
83    pub used_qubits: usize,
84    /// Total qubits available
85    pub total_qubits: usize,
86}
87
88/// Gate pool for reusing common gates to reduce memory allocations
89#[derive(Debug, Clone)]
90pub struct GatePool {
91    /// Common single-qubit gates that can be shared
92    gates: HashMap<String, Arc<dyn GateOp + Send + Sync>>,
93}
94
95impl GatePool {
96    /// Create a new gate pool with common gates pre-allocated
97    #[must_use]
98    pub fn new() -> Self {
99        let mut gates = HashMap::with_capacity(16);
100
101        // Pre-allocate common gates for different qubits
102        for qubit_id in 0..32 {
103            let qubit = QubitId::new(qubit_id);
104
105            // Common single-qubit gates
106            gates.insert(
107                format!("H_{qubit_id}"),
108                Arc::new(Hadamard { target: qubit }) as Arc<dyn GateOp + Send + Sync>,
109            );
110            gates.insert(
111                format!("X_{qubit_id}"),
112                Arc::new(PauliX { target: qubit }) as Arc<dyn GateOp + Send + Sync>,
113            );
114            gates.insert(
115                format!("Y_{qubit_id}"),
116                Arc::new(PauliY { target: qubit }) as Arc<dyn GateOp + Send + Sync>,
117            );
118            gates.insert(
119                format!("Z_{qubit_id}"),
120                Arc::new(PauliZ { target: qubit }) as Arc<dyn GateOp + Send + Sync>,
121            );
122            gates.insert(
123                format!("S_{qubit_id}"),
124                Arc::new(Phase { target: qubit }) as Arc<dyn GateOp + Send + Sync>,
125            );
126            gates.insert(
127                format!("T_{qubit_id}"),
128                Arc::new(T { target: qubit }) as Arc<dyn GateOp + Send + Sync>,
129            );
130        }
131
132        Self { gates }
133    }
134
135    /// Get a gate from the pool if available, otherwise create new.
136    ///
137    /// Parameterized gates (RX, RY, RZ, rotation angles, etc.) are NEVER cached
138    /// because two gates with the same name and target qubit can have different
139    /// rotation angles.  Caching them by (name, qubits) alone would incorrectly
140    /// return a stale angle on repeated calls — which breaks variational circuits.
141    pub fn get_gate<G: GateOp + Clone + Send + Sync + 'static>(
142        &mut self,
143        gate: G,
144    ) -> Arc<dyn GateOp + Send + Sync> {
145        // Parameterized gates must not be pooled: always allocate fresh.
146        if gate.is_parameterized() {
147            return Arc::new(gate) as Arc<dyn GateOp + Send + Sync>;
148        }
149
150        let key = format!("{}_{:?}", gate.name(), gate.qubits());
151
152        if let Some(cached_gate) = self.gates.get(&key) {
153            cached_gate.clone()
154        } else {
155            let arc_gate = Arc::new(gate) as Arc<dyn GateOp + Send + Sync>;
156            self.gates.insert(key, arc_gate.clone());
157            arc_gate
158        }
159    }
160}
161
162impl Default for GatePool {
163    fn default() -> Self {
164        Self::new()
165    }
166}
167
168/// A placeholder measurement gate for QASM export
169#[derive(Debug, Clone)]
170pub struct Measure {
171    pub target: QubitId,
172}
173
174impl GateOp for Measure {
175    fn name(&self) -> &'static str {
176        "measure"
177    }
178
179    fn qubits(&self) -> Vec<QubitId> {
180        vec![self.target]
181    }
182
183    fn is_parameterized(&self) -> bool {
184        false
185    }
186
187    fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
188        // Measurement is a non-unitary, irreversible operation (wavefunction
189        // collapse followed by classical-bit assignment) and therefore has no
190        // unitary matrix representation. Callers that build a circuit-wide
191        // unitary (e.g. via repeated `gate.matrix()` composition) must fail
192        // loudly on circuits containing measurements rather than silently
193        // treating them as an identity no-op.
194        Err(quantrs2_core::error::QuantRS2Error::UnsupportedOperation(
195            "Measure has no unitary matrix representation".to_string(),
196        ))
197    }
198
199    fn as_any(&self) -> &dyn Any {
200        self
201    }
202
203    fn clone_gate(&self) -> Box<dyn GateOp> {
204        Box::new(self.clone())
205    }
206}
207
208/// Wrapper that lets a `Box<dyn GateOp>` live inside an `Arc<dyn GateOp + Send + Sync>`.
209///
210/// `GateOp` is already `Send + Sync` (see the trait definition), so this is safe.
211struct BoxGateWrapper(Box<dyn GateOp>);
212
213impl std::fmt::Debug for BoxGateWrapper {
214    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215        self.0.fmt(f)
216    }
217}
218
219// SAFETY: GateOp: Send + Sync, so Box<dyn GateOp> is Send + Sync.
220unsafe impl Send for BoxGateWrapper {}
221unsafe impl Sync for BoxGateWrapper {}
222
223impl GateOp for BoxGateWrapper {
224    fn name(&self) -> &'static str {
225        self.0.name()
226    }
227    fn qubits(&self) -> Vec<QubitId> {
228        self.0.qubits()
229    }
230    fn num_qubits(&self) -> usize {
231        self.0.num_qubits()
232    }
233    fn is_parameterized(&self) -> bool {
234        self.0.is_parameterized()
235    }
236    fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
237        self.0.matrix()
238    }
239    fn as_any(&self) -> &dyn std::any::Any {
240        self.0.as_any()
241    }
242    fn clone_gate(&self) -> Box<dyn GateOp> {
243        self.0.clone_gate()
244    }
245}
246
247/// Barrier metadata: records which gate-index a barrier was inserted after
248/// and which qubits it spans.  Optimization passes inspect this list and
249/// refuse to move any gate across a barrier boundary that includes its qubit.
250#[derive(Debug, Clone)]
251pub struct BarrierInfo {
252    /// Index into `gates` *after* which this barrier is logically placed.
253    /// A value of `0` means "before all gates".
254    pub after_gate_index: usize,
255    /// Qubits covered by this barrier.
256    pub qubits: Vec<QubitId>,
257}
258
259/// A quantum circuit with a fixed number of qubits.
260///
261/// `Circuit<N>` stores a sequence of quantum gate operations over `N` qubits.
262/// Gates can be appended with the builder methods (e.g. [`Circuit::h`],
263/// [`Circuit::cnot`]) and the circuit can be simulated by passing it to any
264/// type that implements [`Simulator`].
265///
266/// # Examples
267///
268/// ```rust
269/// use quantrs2_circuit::builder::Circuit;
270///
271/// // Build a 2-qubit Bell state preparation circuit
272/// let mut circ: Circuit<2> = Circuit::new();
273/// circ.h(0).expect("h failed").cnot(0, 1).expect("cnot failed");
274/// assert_eq!(circ.num_gates(), 2);
275/// ```
276pub struct Circuit<const N: usize> {
277    /// Vector of gates to be applied in sequence using Arc for shared ownership
278    gates: Vec<Arc<dyn GateOp + Send + Sync>>,
279    /// Gate pool for reusing common gates
280    gate_pool: GatePool,
281    /// Barrier metadata stored for use by optimization passes.
282    /// Barriers are *not* real gates — they carry no unitary — but they
283    /// partition the gate list and must be respected by any reordering pass.
284    pub barriers: Vec<BarrierInfo>,
285}
286
287impl<const N: usize> Clone for Circuit<N> {
288    fn clone(&self) -> Self {
289        // With Arc, cloning is much more efficient - just clone the references
290        Self {
291            gates: self.gates.clone(),
292            gate_pool: self.gate_pool.clone(),
293            barriers: self.barriers.clone(),
294        }
295    }
296}
297
298impl<const N: usize> fmt::Debug for Circuit<N> {
299    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300        f.debug_struct("Circuit")
301            .field("num_qubits", &N)
302            .field("num_gates", &self.gates.len())
303            .finish()
304    }
305}
306
307impl<const N: usize> Circuit<N> {
308    /// Create a new empty circuit with N qubits.
309    ///
310    /// # Examples
311    ///
312    /// ```rust
313    /// use quantrs2_circuit::builder::Circuit;
314    /// let circ: Circuit<3> = Circuit::new();
315    /// assert_eq!(circ.num_gates(), 0);
316    /// ```
317    #[must_use]
318    pub fn new() -> Self {
319        Self {
320            gates: Vec::with_capacity(64), // Pre-allocate capacity for better performance
321            gate_pool: GatePool::new(),
322            barriers: Vec::new(),
323        }
324    }
325
326    /// Create a new circuit with estimated capacity
327    #[must_use]
328    pub fn with_capacity(capacity: usize) -> Self {
329        Self {
330            gates: Vec::with_capacity(capacity),
331            gate_pool: GatePool::new(),
332            barriers: Vec::new(),
333        }
334    }
335
336    /// Add a gate to the circuit
337    pub fn add_gate<G: GateOp + Clone + Send + Sync + 'static>(
338        &mut self,
339        gate: G,
340    ) -> QuantRS2Result<&mut Self> {
341        // Validate that all qubits are within range
342        for qubit in gate.qubits() {
343            if qubit.id() as usize >= N {
344                return Err(quantrs2_core::error::QuantRS2Error::InvalidInput(format!(
345                    "Gate '{}' targets qubit {} which is out of range for {}-qubit circuit (valid range: 0-{})",
346                    gate.name(),
347                    qubit.id(),
348                    N,
349                    N - 1
350                )));
351            }
352        }
353
354        // Use gate pool for common gates to reduce memory allocations
355        let gate_arc = self.gate_pool.get_gate(gate);
356        self.gates.push(gate_arc);
357        Ok(self)
358    }
359
360    /// Create a circuit from a list of boxed gate operations.
361    ///
362    /// Gates are added sequentially; any gate targeting a qubit ≥ N is silently
363    /// dropped (rather than causing a hard error) so that optimization passes
364    /// that may insert temporary placeholders still produce a valid circuit.
365    pub fn from_gates(gates: Vec<Box<dyn GateOp>>) -> QuantRS2Result<Self> {
366        let mut circuit = Self::with_capacity(gates.len());
367        for gate in gates {
368            // Validate qubit bounds; skip rather than abort on out-of-range gates.
369            let in_range = gate.qubits().iter().all(|q| (q.id() as usize) < N);
370            if in_range {
371                // Clone the gate via the trait method to get a concrete Arc.
372                // `clone_gate` returns `Box<dyn GateOp>` which is Send+Sync;
373                // We use the BoxGateWrapper to convert to Arc<dyn GateOp+Send+Sync>.
374                let arc: Arc<dyn GateOp + Send + Sync> = Arc::new(BoxGateWrapper(gate));
375                circuit.gates.push(arc);
376            }
377        }
378        Ok(circuit)
379    }
380
381    /// Add a gate from an Arc (for copying gates between circuits)
382    pub fn add_gate_arc(
383        &mut self,
384        gate: Arc<dyn GateOp + Send + Sync>,
385    ) -> QuantRS2Result<&mut Self> {
386        // Validate that all qubits are within range
387        for qubit in gate.qubits() {
388            if qubit.id() as usize >= N {
389                return Err(quantrs2_core::error::QuantRS2Error::InvalidInput(format!(
390                    "Gate '{}' targets qubit {} which is out of range for {}-qubit circuit (valid range: 0-{})",
391                    gate.name(),
392                    qubit.id(),
393                    N,
394                    N - 1
395                )));
396            }
397        }
398
399        self.gates.push(gate);
400        Ok(self)
401    }
402
403    /// Get all gates in the circuit
404    #[must_use]
405    pub fn gates(&self) -> &[Arc<dyn GateOp + Send + Sync>] {
406        &self.gates
407    }
408
409    /// Get gates as Vec for compatibility with existing optimization code
410    #[must_use]
411    pub fn gates_as_boxes(&self) -> Vec<Box<dyn GateOp>> {
412        self.gates
413            .iter()
414            .map(|arc_gate| arc_gate.clone_gate())
415            .collect()
416    }
417
418    /// Circuit introspection methods for optimization
419
420    /// Count gates by type
421    #[must_use]
422    pub fn count_gates_by_type(&self) -> HashMap<String, usize> {
423        let mut counts = HashMap::new();
424        for gate in &self.gates {
425            *counts.entry(gate.name().to_string()).or_insert(0) += 1;
426        }
427        counts
428    }
429
430    /// Calculate circuit depth (longest sequential path)
431    #[must_use]
432    pub fn calculate_depth(&self) -> usize {
433        if self.gates.is_empty() {
434            return 0;
435        }
436
437        // Track the last time each qubit was used
438        let mut qubit_last_used = vec![0; N];
439        let mut max_depth = 0;
440
441        for (gate_idx, gate) in self.gates.iter().enumerate() {
442            let gate_qubits = gate.qubits();
443
444            // Find the maximum depth among all qubits this gate uses
445            let gate_start_depth = gate_qubits
446                .iter()
447                .map(|q| qubit_last_used[q.id() as usize])
448                .max()
449                .unwrap_or(0);
450
451            let gate_end_depth = gate_start_depth + 1;
452
453            // Update the depth for all qubits this gate touches
454            for qubit in gate_qubits {
455                qubit_last_used[qubit.id() as usize] = gate_end_depth;
456            }
457
458            max_depth = max_depth.max(gate_end_depth);
459        }
460
461        max_depth
462    }
463
464    /// Count two-qubit gates
465    #[must_use]
466    pub fn count_two_qubit_gates(&self) -> usize {
467        self.gates
468            .iter()
469            .filter(|gate| gate.qubits().len() == 2)
470            .count()
471    }
472
473    /// Count multi-qubit gates (3 or more qubits)
474    #[must_use]
475    pub fn count_multi_qubit_gates(&self) -> usize {
476        self.gates
477            .iter()
478            .filter(|gate| gate.qubits().len() >= 3)
479            .count()
480    }
481
482    /// Calculate the critical path length (same as depth for now, but could be enhanced)
483    #[must_use]
484    pub fn calculate_critical_path(&self) -> usize {
485        self.calculate_depth()
486    }
487
488    /// Calculate gate density (gates per qubit)
489    #[must_use]
490    pub fn calculate_gate_density(&self) -> f64 {
491        if N == 0 {
492            0.0
493        } else {
494            self.gates.len() as f64 / N as f64
495        }
496    }
497
498    /// Get all unique qubits used in the circuit
499    #[must_use]
500    pub fn get_used_qubits(&self) -> HashSet<QubitId> {
501        let mut used_qubits = HashSet::new();
502        for gate in &self.gates {
503            for qubit in gate.qubits() {
504                used_qubits.insert(qubit);
505            }
506        }
507        used_qubits
508    }
509
510    /// Check if the circuit uses all available qubits
511    #[must_use]
512    pub fn uses_all_qubits(&self) -> bool {
513        self.get_used_qubits().len() == N
514    }
515
516    /// Get gates that operate on a specific qubit
517    #[must_use]
518    pub fn gates_on_qubit(&self, target_qubit: QubitId) -> Vec<&Arc<dyn GateOp + Send + Sync>> {
519        self.gates
520            .iter()
521            .filter(|gate| gate.qubits().contains(&target_qubit))
522            .collect()
523    }
524
525    /// Get gates between two indices (inclusive)
526    #[must_use]
527    pub fn gates_in_range(&self, start: usize, end: usize) -> &[Arc<dyn GateOp + Send + Sync>] {
528        let end = end.min(self.gates.len().saturating_sub(1));
529        let start = start.min(end);
530        &self.gates[start..=end]
531    }
532
533    /// Check if circuit is empty
534    #[must_use]
535    pub fn is_empty(&self) -> bool {
536        self.gates.is_empty()
537    }
538
539    /// Get circuit statistics summary
540    #[must_use]
541    pub fn get_stats(&self) -> CircuitStats {
542        let gate_counts = self.count_gates_by_type();
543        let depth = self.calculate_depth();
544        let two_qubit_gates = self.count_two_qubit_gates();
545        let multi_qubit_gates = self.count_multi_qubit_gates();
546        let gate_density = self.calculate_gate_density();
547        let used_qubits = self.get_used_qubits().len();
548
549        CircuitStats {
550            total_gates: self.gates.len(),
551            gate_counts,
552            depth,
553            two_qubit_gates,
554            multi_qubit_gates,
555            gate_density,
556            used_qubits,
557            total_qubits: N,
558        }
559    }
560
561    /// Get the number of qubits in the circuit
562    #[must_use]
563    pub const fn num_qubits(&self) -> usize {
564        N
565    }
566
567    /// Get the number of gates in the circuit
568    #[must_use]
569    pub fn num_gates(&self) -> usize {
570        self.gates.len()
571    }
572
573    /// Get the names of all gates in the circuit
574    #[must_use]
575    pub fn get_gate_names(&self) -> Vec<String> {
576        self.gates
577            .iter()
578            .map(|gate| gate.name().to_string())
579            .collect()
580    }
581
582    /// Helper method to find a gate by type and index
583    pub(crate) fn find_gate_by_type_and_index(
584        &self,
585        gate_type: &str,
586        index: usize,
587    ) -> Option<&dyn GateOp> {
588        let mut count = 0;
589        for gate in &self.gates {
590            if gate.name() == gate_type {
591                if count == index {
592                    return Some(gate.as_ref());
593                }
594                count += 1;
595            }
596        }
597        None
598    }
599
600    /// Run the circuit on a simulator
601    pub fn run<S: Simulator<N>>(&self, simulator: S) -> QuantRS2Result<Register<N>> {
602        simulator.run(self)
603    }
604
605    /// Decompose the circuit into a sequence of standard gates
606    ///
607    /// This method will return a new circuit with complex gates decomposed
608    /// into sequences of simpler gates.
609    pub fn decompose(&self) -> QuantRS2Result<Self> {
610        let mut decomposed = Self::new();
611
612        // Convert Arc gates to Box gates for compatibility with decomposition utilities
613        let boxed_gates = self.gates_as_boxes();
614
615        // Decompose all gates
616        let simple_gates = decomp_utils::decompose_circuit(&boxed_gates)?;
617
618        // Add each decomposed gate to the new circuit
619        for gate in simple_gates {
620            decomposed.add_gate_box(gate)?;
621        }
622
623        Ok(decomposed)
624    }
625
626    /// Build the circuit (for compatibility - returns self)
627    #[must_use]
628    pub const fn build(self) -> Self {
629        self
630    }
631
632    /// Optimize the circuit by combining or removing gates
633    ///
634    /// This method will return a new circuit with simplified gates
635    /// by removing unnecessary gates or combining adjacent gates.
636    /// Barrier metadata is preserved: each barrier is re-anchored to the
637    /// closest gate index in the optimized circuit.
638    pub fn optimize(&self) -> QuantRS2Result<Self> {
639        let mut optimized = Self::new();
640
641        // Convert Arc gates to Box gates for compatibility with optimization utilities
642        let boxed_gates = self.gates_as_boxes();
643
644        // Optimize the gate sequence
645        let simplified_gates_result = decomp_utils::optimize_gate_sequence(&boxed_gates);
646
647        // Add each optimized gate to the new circuit
648        if let Ok(simplified_gates) = simplified_gates_result {
649            for g in simplified_gates {
650                optimized.add_gate_box(g)?;
651            }
652        }
653
654        // Re-anchor barriers: clamp after_gate_index to the new gate count so
655        // that barriers are not lost even if the gate list shrinks.
656        let new_gate_count = optimized.gates.len();
657        optimized.barriers = self
658            .barriers
659            .iter()
660            .map(|b| BarrierInfo {
661                after_gate_index: b.after_gate_index.min(new_gate_count),
662                qubits: b.qubits.clone(),
663            })
664            .collect();
665
666        Ok(optimized)
667    }
668
669    /// Add a raw boxed gate to the circuit
670    /// Exposed as `pub(crate)` so that routing and transpiler passes can use it.
671    pub(crate) fn add_gate_box(&mut self, gate: Box<dyn GateOp>) -> QuantRS2Result<&mut Self> {
672        // Validate that all qubits are within range
673        for qubit in gate.qubits() {
674            if qubit.id() as usize >= N {
675                return Err(quantrs2_core::error::QuantRS2Error::InvalidInput(format!(
676                    "Gate '{}' targets qubit {} which is out of range for {}-qubit circuit (valid range: 0-{})",
677                    gate.name(),
678                    qubit.id(),
679                    N,
680                    N - 1
681                )));
682            }
683        }
684
685        // For now, convert via cloning until we can update all callers to use Arc directly
686        // This maintains safety but has some performance cost
687        let cloned_gate = gate.clone_gate();
688
689        // Attempt a zero-copy fast-path for every concrete gate type that is
690        // Copy/Clone.  For any type not listed here the BoxGateWrapper fallback
691        // is used instead, which avoids the UnsupportedOperation error that
692        // previously blocked callers like `decompose()` and `add_composite()`.
693        if let Some(g) = cloned_gate.as_any().downcast_ref::<Hadamard>() {
694            self.gates
695                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
696        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<PauliX>() {
697            self.gates
698                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
699        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<PauliY>() {
700            self.gates
701                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
702        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<PauliZ>() {
703            self.gates
704                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
705        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<CNOT>() {
706            self.gates
707                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
708        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<CZ>() {
709            self.gates
710                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
711        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<SWAP>() {
712            self.gates
713                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
714        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<CY>() {
715            self.gates
716                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
717        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<CH>() {
718            self.gates
719                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
720        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<CS>() {
721            self.gates
722                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
723        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<Toffoli>() {
724            self.gates
725                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
726        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<Fredkin>() {
727            self.gates
728                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
729        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<CRX>() {
730            self.gates
731                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
732        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<CRY>() {
733            self.gates
734                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
735        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<CRZ>() {
736            self.gates
737                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
738        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<ISwap>() {
739            self.gates
740                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
741        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<ECR>() {
742            self.gates
743                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
744        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<RXX>() {
745            self.gates
746                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
747        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<RYY>() {
748            self.gates
749                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
750        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<RZZ>() {
751            self.gates
752                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
753        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<RZX>() {
754            self.gates
755                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
756        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<DCX>() {
757            self.gates
758                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
759        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<RotationX>() {
760            self.gates
761                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
762        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<RotationY>() {
763            self.gates
764                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
765        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<RotationZ>() {
766            self.gates
767                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
768        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<Phase>() {
769            self.gates
770                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
771        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<PhaseDagger>() {
772            self.gates
773                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
774        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<T>() {
775            self.gates
776                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
777        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<TDagger>() {
778            self.gates
779                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
780        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<SqrtX>() {
781            self.gates
782                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
783        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<SqrtXDagger>() {
784            self.gates
785                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
786        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<UGate>() {
787            self.gates
788                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
789        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<PGate>() {
790            self.gates
791                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
792        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<Identity>() {
793            self.gates
794                .push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
795        } else if let Some(g) = cloned_gate.as_any().downcast_ref::<Measure>() {
796            self.gates
797                .push(Arc::new(g.clone()) as Arc<dyn GateOp + Send + Sync>);
798        } else {
799            // Generic fallback for any gate type not listed above.
800            // BoxGateWrapper is Send + Sync (enforced by SAFETY comment on the
801            // struct), so we can safely wrap any Box<dyn GateOp> in an Arc.
802            // This avoids an UnsupportedOperation error and keeps callers like
803            // `decompose()`, `optimize()`, and `add_composite()` working for
804            // third-party gate types defined outside this crate.
805            self.gates
806                .push(Arc::new(BoxGateWrapper(cloned_gate)) as Arc<dyn GateOp + Send + Sync>);
807        }
808
809        Ok(self)
810    }
811
812    /// Create a composite gate from a subsequence of this circuit
813    ///
814    /// This method allows creating a custom gate that combines several
815    /// other gates, which can be applied as a single unit to a circuit.
816    pub fn create_composite(
817        &self,
818        start_idx: usize,
819        end_idx: usize,
820        name: &str,
821    ) -> QuantRS2Result<CompositeGate> {
822        if start_idx >= self.gates.len() || end_idx > self.gates.len() || start_idx >= end_idx {
823            return Err(quantrs2_core::error::QuantRS2Error::InvalidInput(format!(
824                "Invalid start/end indices ({}/{}) for circuit with {} gates",
825                start_idx,
826                end_idx,
827                self.gates.len()
828            )));
829        }
830
831        // Get the gates in the specified range
832        // We need to create box clones of each gate
833        let mut gates: Vec<Box<dyn GateOp>> = Vec::new();
834        for gate in &self.gates[start_idx..end_idx] {
835            gates.push(decomp_utils::clone_gate(gate.as_ref())?);
836        }
837
838        // Collect all unique qubits these gates act on
839        let mut qubits = Vec::new();
840        for gate in &gates {
841            for qubit in gate.qubits() {
842                if !qubits.contains(&qubit) {
843                    qubits.push(qubit);
844                }
845            }
846        }
847
848        Ok(CompositeGate {
849            gates,
850            qubits,
851            name: name.to_string(),
852        })
853    }
854
855    /// Add all gates from a composite gate to this circuit
856    pub fn add_composite(&mut self, composite: &CompositeGate) -> QuantRS2Result<&mut Self> {
857        // Clone each gate from the composite and add to this circuit
858        for gate in &composite.gates {
859            // We can't directly clone a Box<dyn GateOp>, so we need a different approach
860            // We need to create a new gate by using the type information
861            // This is a simplified version - in a real implementation,
862            // we would have a more robust way to clone gates
863            let gate_clone = decomp_utils::clone_gate(gate.as_ref())?;
864            self.add_gate_box(gate_clone)?;
865        }
866
867        Ok(self)
868    }
869
870    /// Convert this circuit to a `ClassicalCircuit` with classical control support
871    #[must_use]
872    pub fn with_classical_control(self) -> crate::classical::ClassicalCircuit<N> {
873        let mut classical_circuit = crate::classical::ClassicalCircuit::new();
874
875        // Add a default classical register for measurements
876        let _ = classical_circuit.add_classical_register("c", N);
877
878        // Transfer all gates, converting Arc to Box for compatibility
879        for gate in self.gates {
880            let boxed_gate = gate.clone_gate();
881            classical_circuit
882                .operations
883                .push(crate::classical::CircuitOp::Quantum(boxed_gate));
884        }
885
886        classical_circuit
887    }
888
889    // Common quantum state preparation patterns
890
891    /// Prepare a Bell state |Φ+⟩ = (|00⟩ + |11⟩)/√2 on two qubits
892    ///
893    /// # Example
894    /// ```ignore
895    /// let mut circuit = Circuit::<2>::new();
896    /// circuit.bell_state(0, 1)?; // Prepare Bell state on qubits 0 and 1
897    /// ```
898    pub fn bell_state(&mut self, qubit1: u32, qubit2: u32) -> QuantRS2Result<&mut Self> {
899        self.h(QubitId::new(qubit1))?;
900        self.cnot(QubitId::new(qubit1), QubitId::new(qubit2))?;
901        Ok(self)
902    }
903
904    /// Prepare a GHZ state (|000...⟩ + |111...⟩)/√2 on specified qubits
905    ///
906    /// # Example
907    /// ```ignore
908    /// let mut circuit = Circuit::<3>::new();
909    /// circuit.ghz_state(&[0, 1, 2])?; // Prepare GHZ state on qubits 0, 1, and 2
910    /// ```
911    pub fn ghz_state(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
912        if qubits.is_empty() {
913            return Ok(self);
914        }
915
916        // Apply Hadamard to first qubit
917        self.h(QubitId::new(qubits[0]))?;
918
919        // Apply CNOT gates to entangle all qubits
920        for i in 1..qubits.len() {
921            self.cnot(QubitId::new(qubits[0]), QubitId::new(qubits[i]))?;
922        }
923
924        Ok(self)
925    }
926
927    /// Prepare a W state on specified qubits
928    ///
929    /// W state: (|100...⟩ + |010...⟩ + |001...⟩ + ...)/√n
930    ///
931    /// This is an approximation using rotation gates.
932    pub fn w_state(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
933        if qubits.is_empty() {
934            return Ok(self);
935        }
936
937        let n = qubits.len() as f64;
938
939        // For n qubits, prepare W state using controlled rotations
940        // This is a simplified implementation
941        self.ry(QubitId::new(qubits[0]), 2.0 * (1.0 / n.sqrt()).acos())?;
942
943        for i in 1..qubits.len() {
944            let angle = 2.0 * (1.0 / (n - i as f64).sqrt()).acos();
945            self.cry(QubitId::new(qubits[i - 1]), QubitId::new(qubits[i]), angle)?;
946        }
947
948        // Apply X gates to ensure proper state preparation
949        for i in 0..qubits.len() - 1 {
950            self.cnot(QubitId::new(qubits[i + 1]), QubitId::new(qubits[i]))?;
951        }
952
953        Ok(self)
954    }
955
956    /// Prepare a product state |++++...⟩ by applying Hadamard to all qubits
957    ///
958    /// # Example
959    /// ```ignore
960    /// let mut circuit = Circuit::<4>::new();
961    /// circuit.plus_state_all()?; // Prepare |+⟩ on all 4 qubits
962    /// ```
963    pub fn plus_state_all(&mut self) -> QuantRS2Result<&mut Self> {
964        for i in 0..N {
965            self.h(QubitId::new(i as u32))?;
966        }
967        Ok(self)
968    }
969
970    /// Create a ladder of CNOT gates connecting adjacent qubits
971    ///
972    /// # Example
973    /// ```ignore
974    /// let mut circuit = Circuit::<4>::new();
975    /// circuit.cnot_ladder(&[0, 1, 2, 3])?; // Creates: CNOT(0,1), CNOT(1,2), CNOT(2,3)
976    /// ```
977    pub fn cnot_ladder(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
978        if qubits.len() < 2 {
979            return Ok(self);
980        }
981
982        for i in 0..qubits.len() - 1 {
983            self.cnot(QubitId::new(qubits[i]), QubitId::new(qubits[i + 1]))?;
984        }
985
986        Ok(self)
987    }
988
989    /// Create a ring of CNOT gates connecting qubits in a cycle
990    ///
991    /// Like CNOT ladder but also connects last to first qubit.
992    pub fn cnot_ring(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
993        if qubits.len() < 2 {
994            return Ok(self);
995        }
996
997        // Add ladder
998        self.cnot_ladder(qubits)?;
999
1000        // Close the ring by connecting last to first
1001        let last_idx = qubits.len() - 1;
1002        self.cnot(QubitId::new(qubits[last_idx]), QubitId::new(qubits[0]))?;
1003
1004        Ok(self)
1005    }
1006
1007    /// Create a ladder of SWAP gates connecting adjacent qubits
1008    ///
1009    /// # Example
1010    /// ```ignore
1011    /// let mut circuit = Circuit::<4>::new();
1012    /// circuit.swap_ladder(&[0, 1, 2, 3])?; // Creates: SWAP(0,1), SWAP(1,2), SWAP(2,3)
1013    /// ```
1014    pub fn swap_ladder(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
1015        if qubits.len() < 2 {
1016            return Ok(self);
1017        }
1018
1019        for i in 0..qubits.len() - 1 {
1020            self.swap(QubitId::new(qubits[i]), QubitId::new(qubits[i + 1]))?;
1021        }
1022
1023        Ok(self)
1024    }
1025
1026    /// Create a ladder of CZ gates connecting adjacent qubits
1027    ///
1028    /// # Example
1029    /// ```ignore
1030    /// let mut circuit = Circuit::<4>::new();
1031    /// circuit.cz_ladder(&[0, 1, 2, 3])?; // Creates: CZ(0,1), CZ(1,2), CZ(2,3)
1032    /// ```
1033    pub fn cz_ladder(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
1034        if qubits.len() < 2 {
1035            return Ok(self);
1036        }
1037
1038        for i in 0..qubits.len() - 1 {
1039            self.cz(QubitId::new(qubits[i]), QubitId::new(qubits[i + 1]))?;
1040        }
1041
1042        Ok(self)
1043    }
1044}
1045
1046impl<const N: usize> Default for Circuit<N> {
1047    fn default() -> Self {
1048        Self::new()
1049    }
1050}
1051
1052/// Trait for quantum circuit simulators
1053pub trait Simulator<const N: usize> {
1054    /// Run a quantum circuit and return the final register state
1055    fn run(&self, circuit: &Circuit<N>) -> QuantRS2Result<Register<N>>;
1056}
1057
1058#[cfg(test)]
1059mod tests;