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