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