quantrs2_circuit/
builder.rs

1//! Builder types for quantum circuits.
2//!
3//! This module contains the Circuit type for building and
4//! executing quantum circuits.
5
6use std::collections::HashMap;
7use std::fmt;
8use std::sync::Arc;
9
10/// Type alias for backwards compatibility
11pub type CircuitBuilder<const N: usize> = Circuit<N>;
12
13use quantrs2_core::{
14    decomposition::{utils as decomp_utils, CompositeGate},
15    error::QuantRS2Result,
16    gate::{
17        multi::{
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/// A quantum circuit with a fixed number of qubits
197pub struct Circuit<const N: usize> {
198    /// Vector of gates to be applied in sequence using Arc for shared ownership
199    gates: Vec<Arc<dyn GateOp + Send + Sync>>,
200    /// Gate pool for reusing common gates
201    gate_pool: GatePool,
202}
203
204impl<const N: usize> Clone for Circuit<N> {
205    fn clone(&self) -> Self {
206        // With Arc, cloning is much more efficient - just clone the references
207        Self {
208            gates: self.gates.clone(),
209            gate_pool: self.gate_pool.clone(),
210        }
211    }
212}
213
214impl<const N: usize> fmt::Debug for Circuit<N> {
215    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216        f.debug_struct("Circuit")
217            .field("num_qubits", &N)
218            .field("num_gates", &self.gates.len())
219            .finish()
220    }
221}
222
223impl<const N: usize> Circuit<N> {
224    /// Create a new empty circuit with N qubits
225    #[must_use]
226    pub fn new() -> Self {
227        Self {
228            gates: Vec::with_capacity(64), // Pre-allocate capacity for better performance
229            gate_pool: GatePool::new(),
230        }
231    }
232
233    /// Create a new circuit with estimated capacity
234    #[must_use]
235    pub fn with_capacity(capacity: usize) -> Self {
236        Self {
237            gates: Vec::with_capacity(capacity),
238            gate_pool: GatePool::new(),
239        }
240    }
241
242    /// Add a gate to the circuit
243    pub fn add_gate<G: GateOp + Clone + Send + Sync + 'static>(
244        &mut self,
245        gate: G,
246    ) -> QuantRS2Result<&mut Self> {
247        // Validate that all qubits are within range
248        for qubit in gate.qubits() {
249            if qubit.id() as usize >= N {
250                return Err(quantrs2_core::error::QuantRS2Error::InvalidInput(format!(
251                    "Gate '{}' targets qubit {} which is out of range for {}-qubit circuit (valid range: 0-{})",
252                    gate.name(),
253                    qubit.id(),
254                    N,
255                    N - 1
256                )));
257            }
258        }
259
260        // Use gate pool for common gates to reduce memory allocations
261        let gate_arc = self.gate_pool.get_gate(gate);
262        self.gates.push(gate_arc);
263        Ok(self)
264    }
265
266    /// Add a gate from an Arc (for copying gates between circuits)
267    pub fn add_gate_arc(
268        &mut self,
269        gate: Arc<dyn GateOp + Send + Sync>,
270    ) -> QuantRS2Result<&mut Self> {
271        // Validate that all qubits are within range
272        for qubit in gate.qubits() {
273            if qubit.id() as usize >= N {
274                return Err(quantrs2_core::error::QuantRS2Error::InvalidInput(format!(
275                    "Gate '{}' targets qubit {} which is out of range for {}-qubit circuit (valid range: 0-{})",
276                    gate.name(),
277                    qubit.id(),
278                    N,
279                    N - 1
280                )));
281            }
282        }
283
284        self.gates.push(gate);
285        Ok(self)
286    }
287
288    /// Get all gates in the circuit
289    #[must_use]
290    pub fn gates(&self) -> &[Arc<dyn GateOp + Send + Sync>] {
291        &self.gates
292    }
293
294    /// Get gates as Vec for compatibility with existing optimization code
295    #[must_use]
296    pub fn gates_as_boxes(&self) -> Vec<Box<dyn GateOp>> {
297        self.gates
298            .iter()
299            .map(|arc_gate| arc_gate.clone_gate())
300            .collect()
301    }
302
303    /// Circuit introspection methods for optimization
304
305    /// Count gates by type
306    #[must_use]
307    pub fn count_gates_by_type(&self) -> HashMap<String, usize> {
308        let mut counts = HashMap::new();
309        for gate in &self.gates {
310            *counts.entry(gate.name().to_string()).or_insert(0) += 1;
311        }
312        counts
313    }
314
315    /// Calculate circuit depth (longest sequential path)
316    #[must_use]
317    pub fn calculate_depth(&self) -> usize {
318        if self.gates.is_empty() {
319            return 0;
320        }
321
322        // Track the last time each qubit was used
323        let mut qubit_last_used = vec![0; N];
324        let mut max_depth = 0;
325
326        for (gate_idx, gate) in self.gates.iter().enumerate() {
327            let gate_qubits = gate.qubits();
328
329            // Find the maximum depth among all qubits this gate uses
330            let gate_start_depth = gate_qubits
331                .iter()
332                .map(|q| qubit_last_used[q.id() as usize])
333                .max()
334                .unwrap_or(0);
335
336            let gate_end_depth = gate_start_depth + 1;
337
338            // Update the depth for all qubits this gate touches
339            for qubit in gate_qubits {
340                qubit_last_used[qubit.id() as usize] = gate_end_depth;
341            }
342
343            max_depth = max_depth.max(gate_end_depth);
344        }
345
346        max_depth
347    }
348
349    /// Count two-qubit gates
350    #[must_use]
351    pub fn count_two_qubit_gates(&self) -> usize {
352        self.gates
353            .iter()
354            .filter(|gate| gate.qubits().len() == 2)
355            .count()
356    }
357
358    /// Count multi-qubit gates (3 or more qubits)
359    #[must_use]
360    pub fn count_multi_qubit_gates(&self) -> usize {
361        self.gates
362            .iter()
363            .filter(|gate| gate.qubits().len() >= 3)
364            .count()
365    }
366
367    /// Calculate the critical path length (same as depth for now, but could be enhanced)
368    #[must_use]
369    pub fn calculate_critical_path(&self) -> usize {
370        self.calculate_depth()
371    }
372
373    /// Calculate gate density (gates per qubit)
374    #[must_use]
375    pub fn calculate_gate_density(&self) -> f64 {
376        if N == 0 {
377            0.0
378        } else {
379            self.gates.len() as f64 / N as f64
380        }
381    }
382
383    /// Get all unique qubits used in the circuit
384    #[must_use]
385    pub fn get_used_qubits(&self) -> HashSet<QubitId> {
386        let mut used_qubits = HashSet::new();
387        for gate in &self.gates {
388            for qubit in gate.qubits() {
389                used_qubits.insert(qubit);
390            }
391        }
392        used_qubits
393    }
394
395    /// Check if the circuit uses all available qubits
396    #[must_use]
397    pub fn uses_all_qubits(&self) -> bool {
398        self.get_used_qubits().len() == N
399    }
400
401    /// Get gates that operate on a specific qubit
402    #[must_use]
403    pub fn gates_on_qubit(&self, target_qubit: QubitId) -> Vec<&Arc<dyn GateOp + Send + Sync>> {
404        self.gates
405            .iter()
406            .filter(|gate| gate.qubits().contains(&target_qubit))
407            .collect()
408    }
409
410    /// Get gates between two indices (inclusive)
411    #[must_use]
412    pub fn gates_in_range(&self, start: usize, end: usize) -> &[Arc<dyn GateOp + Send + Sync>] {
413        let end = end.min(self.gates.len().saturating_sub(1));
414        let start = start.min(end);
415        &self.gates[start..=end]
416    }
417
418    /// Check if circuit is empty
419    #[must_use]
420    pub fn is_empty(&self) -> bool {
421        self.gates.is_empty()
422    }
423
424    /// Get circuit statistics summary
425    #[must_use]
426    pub fn get_stats(&self) -> CircuitStats {
427        let gate_counts = self.count_gates_by_type();
428        let depth = self.calculate_depth();
429        let two_qubit_gates = self.count_two_qubit_gates();
430        let multi_qubit_gates = self.count_multi_qubit_gates();
431        let gate_density = self.calculate_gate_density();
432        let used_qubits = self.get_used_qubits().len();
433
434        CircuitStats {
435            total_gates: self.gates.len(),
436            gate_counts,
437            depth,
438            two_qubit_gates,
439            multi_qubit_gates,
440            gate_density,
441            used_qubits,
442            total_qubits: N,
443        }
444    }
445
446    /// Get the number of qubits in the circuit
447    #[must_use]
448    pub const fn num_qubits(&self) -> usize {
449        N
450    }
451
452    /// Get the number of gates in the circuit
453    #[must_use]
454    pub fn num_gates(&self) -> usize {
455        self.gates.len()
456    }
457
458    /// Get the names of all gates in the circuit
459    #[must_use]
460    pub fn get_gate_names(&self) -> Vec<String> {
461        self.gates
462            .iter()
463            .map(|gate| gate.name().to_string())
464            .collect()
465    }
466
467    /// Get a qubit for a specific single-qubit gate by gate type and index
468    #[cfg(feature = "python")]
469    pub fn get_single_qubit_for_gate(&self, gate_type: &str, index: usize) -> pyo3::PyResult<u32> {
470        self.find_gate_by_type_and_index(gate_type, index)
471            .and_then(|gate| {
472                if gate.qubits().len() == 1 {
473                    Some(gate.qubits()[0].id())
474                } else {
475                    None
476                }
477            })
478            .ok_or_else(|| {
479                pyo3::exceptions::PyValueError::new_err(format!(
480                    "Gate {gate_type} at index {index} not found or is not a single-qubit gate"
481                ))
482            })
483    }
484
485    /// Get rotation parameters (qubit, angle) for a specific gate by gate type and index
486    #[cfg(feature = "python")]
487    pub fn get_rotation_params_for_gate(
488        &self,
489        gate_type: &str,
490        index: usize,
491    ) -> pyo3::PyResult<(u32, f64)> {
492        // Note: This is a simplified implementation, actual implementation would check
493        // gate type and extract the rotation parameter
494        self.find_gate_by_type_and_index(gate_type, index)
495            .and_then(|gate| {
496                if gate.qubits().len() == 1 {
497                    // Default angle (in a real implementation, we would extract this from the gate)
498                    Some((gate.qubits()[0].id(), 0.0))
499                } else {
500                    None
501                }
502            })
503            .ok_or_else(|| {
504                pyo3::exceptions::PyValueError::new_err(format!(
505                    "Gate {gate_type} at index {index} not found or is not a rotation gate"
506                ))
507            })
508    }
509
510    /// Get two-qubit parameters (control, target) for a specific gate by gate type and index
511    #[cfg(feature = "python")]
512    pub fn get_two_qubit_params_for_gate(
513        &self,
514        gate_type: &str,
515        index: usize,
516    ) -> pyo3::PyResult<(u32, u32)> {
517        self.find_gate_by_type_and_index(gate_type, index)
518            .and_then(|gate| {
519                if gate.qubits().len() == 2 {
520                    Some((gate.qubits()[0].id(), gate.qubits()[1].id()))
521                } else {
522                    None
523                }
524            })
525            .ok_or_else(|| {
526                pyo3::exceptions::PyValueError::new_err(format!(
527                    "Gate {gate_type} at index {index} not found or is not a two-qubit gate"
528                ))
529            })
530    }
531
532    /// Get controlled rotation parameters (control, target, angle) for a specific gate
533    #[cfg(feature = "python")]
534    pub fn get_controlled_rotation_params_for_gate(
535        &self,
536        gate_type: &str,
537        index: usize,
538    ) -> pyo3::PyResult<(u32, u32, f64)> {
539        // Note: This is a simplified implementation, actual implementation would check
540        // gate type and extract the rotation parameter
541        self.find_gate_by_type_and_index(gate_type, index)
542            .and_then(|gate| {
543                if gate.qubits().len() == 2 {
544                    // Default angle (in a real implementation, we would extract this from the gate)
545                    Some((gate.qubits()[0].id(), gate.qubits()[1].id(), 0.0))
546                } else {
547                    None
548                }
549            })
550            .ok_or_else(|| {
551                pyo3::exceptions::PyValueError::new_err(format!(
552                    "Gate {gate_type} at index {index} not found or is not a controlled rotation gate"
553                ))
554            })
555    }
556
557    /// Get three-qubit parameters for gates like Toffoli or Fredkin
558    #[cfg(feature = "python")]
559    pub fn get_three_qubit_params_for_gate(
560        &self,
561        gate_type: &str,
562        index: usize,
563    ) -> pyo3::PyResult<(u32, u32, u32)> {
564        self.find_gate_by_type_and_index(gate_type, index)
565            .and_then(|gate| {
566                if gate.qubits().len() == 3 {
567                    Some((
568                        gate.qubits()[0].id(),
569                        gate.qubits()[1].id(),
570                        gate.qubits()[2].id(),
571                    ))
572                } else {
573                    None
574                }
575            })
576            .ok_or_else(|| {
577                pyo3::exceptions::PyValueError::new_err(format!(
578                    "Gate {gate_type} at index {index} not found or is not a three-qubit gate"
579                ))
580            })
581    }
582
583    /// Helper method to find a gate by type and index
584    fn find_gate_by_type_and_index(&self, gate_type: &str, index: usize) -> Option<&dyn GateOp> {
585        let mut count = 0;
586        for gate in &self.gates {
587            if gate.name() == gate_type {
588                if count == index {
589                    return Some(gate.as_ref());
590                }
591                count += 1;
592            }
593        }
594        None
595    }
596
597    /// Apply a Hadamard gate to a qubit
598    pub fn h(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
599        self.add_gate(Hadamard {
600            target: target.into(),
601        })
602    }
603
604    /// Apply a Pauli-X gate to a qubit
605    pub fn x(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
606        self.add_gate(PauliX {
607            target: target.into(),
608        })
609    }
610
611    /// Apply a Pauli-Y gate to a qubit
612    pub fn y(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
613        self.add_gate(PauliY {
614            target: target.into(),
615        })
616    }
617
618    /// Apply a Pauli-Z gate to a qubit
619    pub fn z(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
620        self.add_gate(PauliZ {
621            target: target.into(),
622        })
623    }
624
625    /// Apply a rotation around X-axis
626    pub fn rx(&mut self, target: impl Into<QubitId>, theta: f64) -> QuantRS2Result<&mut Self> {
627        self.add_gate(RotationX {
628            target: target.into(),
629            theta,
630        })
631    }
632
633    /// Apply a rotation around Y-axis
634    pub fn ry(&mut self, target: impl Into<QubitId>, theta: f64) -> QuantRS2Result<&mut Self> {
635        self.add_gate(RotationY {
636            target: target.into(),
637            theta,
638        })
639    }
640
641    /// Apply a rotation around Z-axis
642    pub fn rz(&mut self, target: impl Into<QubitId>, theta: f64) -> QuantRS2Result<&mut Self> {
643        self.add_gate(RotationZ {
644            target: target.into(),
645            theta,
646        })
647    }
648
649    /// Apply a Phase gate (S gate)
650    pub fn s(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
651        self.add_gate(Phase {
652            target: target.into(),
653        })
654    }
655
656    /// Apply a Phase-dagger gate (S† gate)
657    pub fn sdg(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
658        self.add_gate(PhaseDagger {
659            target: target.into(),
660        })
661    }
662
663    /// Apply a T gate
664    pub fn t(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
665        self.add_gate(T {
666            target: target.into(),
667        })
668    }
669
670    /// Apply a T-dagger gate (T† gate)
671    pub fn tdg(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
672        self.add_gate(TDagger {
673            target: target.into(),
674        })
675    }
676
677    /// Apply a Square Root of X gate (√X)
678    pub fn sx(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
679        self.add_gate(SqrtX {
680            target: target.into(),
681        })
682    }
683
684    /// Apply a Square Root of X Dagger gate (√X†)
685    pub fn sxdg(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
686        self.add_gate(SqrtXDagger {
687            target: target.into(),
688        })
689    }
690
691    /// Apply a CNOT gate
692    pub fn cnot(
693        &mut self,
694        control: impl Into<QubitId>,
695        target: impl Into<QubitId>,
696    ) -> QuantRS2Result<&mut Self> {
697        self.add_gate(CNOT {
698            control: control.into(),
699            target: target.into(),
700        })
701    }
702
703    /// Apply a CNOT gate (alias for cnot)
704    pub fn cx(
705        &mut self,
706        control: impl Into<QubitId>,
707        target: impl Into<QubitId>,
708    ) -> QuantRS2Result<&mut Self> {
709        self.cnot(control, target)
710    }
711
712    /// Apply a CY gate (Controlled-Y)
713    pub fn cy(
714        &mut self,
715        control: impl Into<QubitId>,
716        target: impl Into<QubitId>,
717    ) -> QuantRS2Result<&mut Self> {
718        self.add_gate(CY {
719            control: control.into(),
720            target: target.into(),
721        })
722    }
723
724    /// Apply a CZ gate (Controlled-Z)
725    pub fn cz(
726        &mut self,
727        control: impl Into<QubitId>,
728        target: impl Into<QubitId>,
729    ) -> QuantRS2Result<&mut Self> {
730        self.add_gate(CZ {
731            control: control.into(),
732            target: target.into(),
733        })
734    }
735
736    /// Apply a CH gate (Controlled-Hadamard)
737    pub fn ch(
738        &mut self,
739        control: impl Into<QubitId>,
740        target: impl Into<QubitId>,
741    ) -> QuantRS2Result<&mut Self> {
742        self.add_gate(CH {
743            control: control.into(),
744            target: target.into(),
745        })
746    }
747
748    /// Apply a CS gate (Controlled-Phase/S)
749    pub fn cs(
750        &mut self,
751        control: impl Into<QubitId>,
752        target: impl Into<QubitId>,
753    ) -> QuantRS2Result<&mut Self> {
754        self.add_gate(CS {
755            control: control.into(),
756            target: target.into(),
757        })
758    }
759
760    /// Apply a controlled rotation around X-axis (CRX)
761    pub fn crx(
762        &mut self,
763        control: impl Into<QubitId>,
764        target: impl Into<QubitId>,
765        theta: f64,
766    ) -> QuantRS2Result<&mut Self> {
767        self.add_gate(CRX {
768            control: control.into(),
769            target: target.into(),
770            theta,
771        })
772    }
773
774    /// Apply a controlled rotation around Y-axis (CRY)
775    pub fn cry(
776        &mut self,
777        control: impl Into<QubitId>,
778        target: impl Into<QubitId>,
779        theta: f64,
780    ) -> QuantRS2Result<&mut Self> {
781        self.add_gate(CRY {
782            control: control.into(),
783            target: target.into(),
784            theta,
785        })
786    }
787
788    /// Apply a controlled rotation around Z-axis (CRZ)
789    pub fn crz(
790        &mut self,
791        control: impl Into<QubitId>,
792        target: impl Into<QubitId>,
793        theta: f64,
794    ) -> QuantRS2Result<&mut Self> {
795        self.add_gate(CRZ {
796            control: control.into(),
797            target: target.into(),
798            theta,
799        })
800    }
801
802    /// Apply a controlled phase gate
803    pub fn cp(
804        &mut self,
805        control: impl Into<QubitId>,
806        target: impl Into<QubitId>,
807        lambda: f64,
808    ) -> QuantRS2Result<&mut Self> {
809        // CRZ(lambda) is equivalent to CP(lambda) up to a global phase
810        self.crz(control, target, lambda)
811    }
812
813    /// Apply a SWAP gate
814    pub fn swap(
815        &mut self,
816        qubit1: impl Into<QubitId>,
817        qubit2: impl Into<QubitId>,
818    ) -> QuantRS2Result<&mut Self> {
819        self.add_gate(SWAP {
820            qubit1: qubit1.into(),
821            qubit2: qubit2.into(),
822        })
823    }
824
825    /// Apply a Toffoli (CCNOT) gate
826    pub fn toffoli(
827        &mut self,
828        control1: impl Into<QubitId>,
829        control2: impl Into<QubitId>,
830        target: impl Into<QubitId>,
831    ) -> QuantRS2Result<&mut Self> {
832        self.add_gate(Toffoli {
833            control1: control1.into(),
834            control2: control2.into(),
835            target: target.into(),
836        })
837    }
838
839    /// Apply a Fredkin (CSWAP) gate
840    pub fn cswap(
841        &mut self,
842        control: impl Into<QubitId>,
843        target1: impl Into<QubitId>,
844        target2: impl Into<QubitId>,
845    ) -> QuantRS2Result<&mut Self> {
846        self.add_gate(Fredkin {
847            control: control.into(),
848            target1: target1.into(),
849            target2: target2.into(),
850        })
851    }
852
853    // ============ Qiskit-Compatible Gates ============
854
855    /// Apply a U gate (general single-qubit rotation)
856    ///
857    /// U(θ, φ, λ) = [[cos(θ/2), -e^(iλ)·sin(θ/2)],
858    ///              [e^(iφ)·sin(θ/2), e^(i(φ+λ))·cos(θ/2)]]
859    pub fn u(
860        &mut self,
861        target: impl Into<QubitId>,
862        theta: f64,
863        phi: f64,
864        lambda: f64,
865    ) -> QuantRS2Result<&mut Self> {
866        self.add_gate(UGate {
867            target: target.into(),
868            theta,
869            phi,
870            lambda,
871        })
872    }
873
874    /// Apply a P gate (phase gate with parameter)
875    ///
876    /// P(λ) = [[1, 0], [0, e^(iλ)]]
877    pub fn p(&mut self, target: impl Into<QubitId>, lambda: f64) -> QuantRS2Result<&mut Self> {
878        self.add_gate(PGate {
879            target: target.into(),
880            lambda,
881        })
882    }
883
884    /// Apply an Identity gate
885    pub fn id(&mut self, target: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
886        self.add_gate(Identity {
887            target: target.into(),
888        })
889    }
890
891    /// Apply an iSWAP gate
892    ///
893    /// iSWAP swaps two qubits and phases |01⟩ and |10⟩ by i
894    pub fn iswap(
895        &mut self,
896        qubit1: impl Into<QubitId>,
897        qubit2: impl Into<QubitId>,
898    ) -> QuantRS2Result<&mut Self> {
899        self.add_gate(ISwap {
900            qubit1: qubit1.into(),
901            qubit2: qubit2.into(),
902        })
903    }
904
905    /// Apply an ECR gate (IBM native echoed cross-resonance gate)
906    pub fn ecr(
907        &mut self,
908        control: impl Into<QubitId>,
909        target: impl Into<QubitId>,
910    ) -> QuantRS2Result<&mut Self> {
911        self.add_gate(ECR {
912            control: control.into(),
913            target: target.into(),
914        })
915    }
916
917    /// Apply an RXX gate (two-qubit XX rotation)
918    ///
919    /// RXX(θ) = exp(-i * θ/2 * X⊗X)
920    pub fn rxx(
921        &mut self,
922        qubit1: impl Into<QubitId>,
923        qubit2: impl Into<QubitId>,
924        theta: f64,
925    ) -> QuantRS2Result<&mut Self> {
926        self.add_gate(RXX {
927            qubit1: qubit1.into(),
928            qubit2: qubit2.into(),
929            theta,
930        })
931    }
932
933    /// Apply an RYY gate (two-qubit YY rotation)
934    ///
935    /// RYY(θ) = exp(-i * θ/2 * Y⊗Y)
936    pub fn ryy(
937        &mut self,
938        qubit1: impl Into<QubitId>,
939        qubit2: impl Into<QubitId>,
940        theta: f64,
941    ) -> QuantRS2Result<&mut Self> {
942        self.add_gate(RYY {
943            qubit1: qubit1.into(),
944            qubit2: qubit2.into(),
945            theta,
946        })
947    }
948
949    /// Apply an RZZ gate (two-qubit ZZ rotation)
950    ///
951    /// RZZ(θ) = exp(-i * θ/2 * Z⊗Z)
952    pub fn rzz(
953        &mut self,
954        qubit1: impl Into<QubitId>,
955        qubit2: impl Into<QubitId>,
956        theta: f64,
957    ) -> QuantRS2Result<&mut Self> {
958        self.add_gate(RZZ {
959            qubit1: qubit1.into(),
960            qubit2: qubit2.into(),
961            theta,
962        })
963    }
964
965    /// Apply an RZX gate (two-qubit ZX rotation / cross-resonance)
966    ///
967    /// RZX(θ) = exp(-i * θ/2 * Z⊗X)
968    pub fn rzx(
969        &mut self,
970        control: impl Into<QubitId>,
971        target: impl Into<QubitId>,
972        theta: f64,
973    ) -> QuantRS2Result<&mut Self> {
974        self.add_gate(RZX {
975            control: control.into(),
976            target: target.into(),
977            theta,
978        })
979    }
980
981    /// Apply a DCX gate (double CNOT gate)
982    ///
983    /// DCX = CNOT(0,1) @ CNOT(1,0)
984    pub fn dcx(
985        &mut self,
986        qubit1: impl Into<QubitId>,
987        qubit2: impl Into<QubitId>,
988    ) -> QuantRS2Result<&mut Self> {
989        self.add_gate(DCX {
990            qubit1: qubit1.into(),
991            qubit2: qubit2.into(),
992        })
993    }
994
995    /// Apply a CCX gate (alias for Toffoli)
996    pub fn ccx(
997        &mut self,
998        control1: impl Into<QubitId>,
999        control2: impl Into<QubitId>,
1000        target: impl Into<QubitId>,
1001    ) -> QuantRS2Result<&mut Self> {
1002        self.toffoli(control1, control2, target)
1003    }
1004
1005    /// Apply a Fredkin gate (alias for cswap)
1006    pub fn fredkin(
1007        &mut self,
1008        control: impl Into<QubitId>,
1009        target1: impl Into<QubitId>,
1010        target2: impl Into<QubitId>,
1011    ) -> QuantRS2Result<&mut Self> {
1012        self.cswap(control, target1, target2)
1013    }
1014
1015    /// Measure a qubit (currently adds a placeholder measure gate)
1016    ///
1017    /// Note: This is currently a placeholder implementation for QASM export compatibility.
1018    /// For actual quantum measurements, use the measurement module functionality.
1019    pub fn measure(&mut self, qubit: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
1020        let qubit_id = qubit.into();
1021        self.add_gate(Measure { target: qubit_id })?;
1022        Ok(self)
1023    }
1024
1025    /// Reset a qubit to |0⟩ state
1026    ///
1027    /// Note: This operation is not yet fully implemented.
1028    /// Reset operations are complex and require special handling in quantum circuits.
1029    pub fn reset(&mut self, _qubit: impl Into<QubitId>) -> QuantRS2Result<&mut Self> {
1030        Err(quantrs2_core::error::QuantRS2Error::UnsupportedOperation(
1031            "Reset operation is not yet implemented. Reset requires special quantum state manipulation.".to_string()
1032        ))
1033    }
1034
1035    /// Add a barrier to prevent optimization across this point
1036    ///
1037    /// Barriers are used to prevent gate optimization algorithms from reordering gates
1038    /// across specific points in the circuit. This is useful for maintaining timing
1039    /// constraints or preserving specific circuit structure.
1040    pub fn barrier(&mut self, qubits: &[QubitId]) -> QuantRS2Result<&mut Self> {
1041        // Validate all qubits are within range
1042        for &qubit in qubits {
1043            if qubit.id() as usize >= N {
1044                return Err(quantrs2_core::error::QuantRS2Error::InvalidQubitId(
1045                    qubit.id(),
1046                ));
1047            }
1048        }
1049
1050        // For now, barriers are implicit - they don't add gates but could be used
1051        // by optimization passes. In a full implementation, we'd store barrier information
1052        // for use by the optimization framework.
1053
1054        // TODO: Implement barrier storage for optimization passes
1055        Ok(self)
1056    }
1057
1058    /// Run the circuit on a simulator
1059    pub fn run<S: Simulator<N>>(&self, simulator: S) -> QuantRS2Result<Register<N>> {
1060        simulator.run(self)
1061    }
1062
1063    /// Decompose the circuit into a sequence of standard gates
1064    ///
1065    /// This method will return a new circuit with complex gates decomposed
1066    /// into sequences of simpler gates.
1067    pub fn decompose(&self) -> QuantRS2Result<Self> {
1068        let mut decomposed = Self::new();
1069
1070        // Convert Arc gates to Box gates for compatibility with decomposition utilities
1071        let boxed_gates = self.gates_as_boxes();
1072
1073        // Decompose all gates
1074        let simple_gates = decomp_utils::decompose_circuit(&boxed_gates)?;
1075
1076        // Add each decomposed gate to the new circuit
1077        for gate in simple_gates {
1078            decomposed.add_gate_box(gate)?;
1079        }
1080
1081        Ok(decomposed)
1082    }
1083
1084    /// Build the circuit (for compatibility - returns self)
1085    #[must_use]
1086    pub const fn build(self) -> Self {
1087        self
1088    }
1089
1090    /// Optimize the circuit by combining or removing gates
1091    ///
1092    /// This method will return a new circuit with simplified gates
1093    /// by removing unnecessary gates or combining adjacent gates.
1094    pub fn optimize(&self) -> QuantRS2Result<Self> {
1095        let mut optimized = Self::new();
1096
1097        // Convert Arc gates to Box gates for compatibility with optimization utilities
1098        let boxed_gates = self.gates_as_boxes();
1099
1100        // Optimize the gate sequence
1101        let simplified_gates_result = decomp_utils::optimize_gate_sequence(&boxed_gates);
1102
1103        // Add each optimized gate to the new circuit
1104        if let Ok(simplified_gates) = simplified_gates_result {
1105            // We need to handle each gate individually
1106            for g in simplified_gates {
1107                optimized.add_gate_box(g)?;
1108            }
1109        }
1110
1111        Ok(optimized)
1112    }
1113
1114    /// Add a raw boxed gate to the circuit
1115    /// This is an internal utility and not part of the public API
1116    fn add_gate_box(&mut self, gate: Box<dyn GateOp>) -> QuantRS2Result<&mut Self> {
1117        // Validate that all qubits are within range
1118        for qubit in gate.qubits() {
1119            if qubit.id() as usize >= N {
1120                return Err(quantrs2_core::error::QuantRS2Error::InvalidInput(format!(
1121                    "Gate '{}' targets qubit {} which is out of range for {}-qubit circuit (valid range: 0-{})",
1122                    gate.name(),
1123                    qubit.id(),
1124                    N,
1125                    N - 1
1126                )));
1127            }
1128        }
1129
1130        // For now, convert via cloning until we can update all callers to use Arc directly
1131        // This maintains safety but has some performance cost
1132        let cloned_gate = gate.clone_gate();
1133
1134        // Convert the specific gate types to Arc using match
1135        if let Some(h_gate) = cloned_gate.as_any().downcast_ref::<Hadamard>() {
1136            self.gates
1137                .push(Arc::new(*h_gate) as Arc<dyn GateOp + Send + Sync>);
1138        } else if let Some(x_gate) = cloned_gate.as_any().downcast_ref::<PauliX>() {
1139            self.gates
1140                .push(Arc::new(*x_gate) as Arc<dyn GateOp + Send + Sync>);
1141        } else if let Some(y_gate) = cloned_gate.as_any().downcast_ref::<PauliY>() {
1142            self.gates
1143                .push(Arc::new(*y_gate) as Arc<dyn GateOp + Send + Sync>);
1144        } else if let Some(z_gate) = cloned_gate.as_any().downcast_ref::<PauliZ>() {
1145            self.gates
1146                .push(Arc::new(*z_gate) as Arc<dyn GateOp + Send + Sync>);
1147        } else if let Some(cnot_gate) = cloned_gate.as_any().downcast_ref::<CNOT>() {
1148            self.gates
1149                .push(Arc::new(*cnot_gate) as Arc<dyn GateOp + Send + Sync>);
1150        } else if let Some(measure_gate) = cloned_gate.as_any().downcast_ref::<Measure>() {
1151            self.gates
1152                .push(Arc::new(measure_gate.clone()) as Arc<dyn GateOp + Send + Sync>);
1153        } else {
1154            // For unknown gate types, we'll use a less efficient fallback
1155            // TODO: Extend this to cover all gate types or implement a better conversion mechanism
1156            return Err(quantrs2_core::error::QuantRS2Error::UnsupportedOperation(
1157                format!(
1158                    "Gate type '{}' not yet supported in Arc conversion",
1159                    gate.name()
1160                ),
1161            ));
1162        }
1163
1164        Ok(self)
1165    }
1166
1167    /// Create a composite gate from a subsequence of this circuit
1168    ///
1169    /// This method allows creating a custom gate that combines several
1170    /// other gates, which can be applied as a single unit to a circuit.
1171    pub fn create_composite(
1172        &self,
1173        start_idx: usize,
1174        end_idx: usize,
1175        name: &str,
1176    ) -> QuantRS2Result<CompositeGate> {
1177        if start_idx >= self.gates.len() || end_idx > self.gates.len() || start_idx >= end_idx {
1178            return Err(quantrs2_core::error::QuantRS2Error::InvalidInput(format!(
1179                "Invalid start/end indices ({}/{}) for circuit with {} gates",
1180                start_idx,
1181                end_idx,
1182                self.gates.len()
1183            )));
1184        }
1185
1186        // Get the gates in the specified range
1187        // We need to create box clones of each gate
1188        let mut gates: Vec<Box<dyn GateOp>> = Vec::new();
1189        for gate in &self.gates[start_idx..end_idx] {
1190            gates.push(decomp_utils::clone_gate(gate.as_ref())?);
1191        }
1192
1193        // Collect all unique qubits these gates act on
1194        let mut qubits = Vec::new();
1195        for gate in &gates {
1196            for qubit in gate.qubits() {
1197                if !qubits.contains(&qubit) {
1198                    qubits.push(qubit);
1199                }
1200            }
1201        }
1202
1203        Ok(CompositeGate {
1204            gates,
1205            qubits,
1206            name: name.to_string(),
1207        })
1208    }
1209
1210    /// Add all gates from a composite gate to this circuit
1211    pub fn add_composite(&mut self, composite: &CompositeGate) -> QuantRS2Result<&mut Self> {
1212        // Clone each gate from the composite and add to this circuit
1213        for gate in &composite.gates {
1214            // We can't directly clone a Box<dyn GateOp>, so we need a different approach
1215            // We need to create a new gate by using the type information
1216            // This is a simplified version - in a real implementation,
1217            // we would have a more robust way to clone gates
1218            let gate_clone = decomp_utils::clone_gate(gate.as_ref())?;
1219            self.add_gate_box(gate_clone)?;
1220        }
1221
1222        Ok(self)
1223    }
1224
1225    // Classical control flow extensions
1226
1227    /// Measure all qubits in the circuit
1228    pub fn measure_all(&mut self) -> QuantRS2Result<&mut Self> {
1229        for i in 0..N {
1230            self.measure(QubitId(i as u32))?;
1231        }
1232        Ok(self)
1233    }
1234
1235    /// Convert this circuit to a `ClassicalCircuit` with classical control support
1236    #[must_use]
1237    pub fn with_classical_control(self) -> crate::classical::ClassicalCircuit<N> {
1238        let mut classical_circuit = crate::classical::ClassicalCircuit::new();
1239
1240        // Add a default classical register for measurements
1241        let _ = classical_circuit.add_classical_register("c", N);
1242
1243        // Transfer all gates, converting Arc to Box for compatibility
1244        for gate in self.gates {
1245            let boxed_gate = gate.clone_gate();
1246            classical_circuit
1247                .operations
1248                .push(crate::classical::CircuitOp::Quantum(boxed_gate));
1249        }
1250
1251        classical_circuit
1252    }
1253
1254    // Batch operations for improved ergonomics
1255
1256    /// Apply Hadamard gates to multiple qubits at once
1257    ///
1258    /// # Example
1259    /// ```ignore
1260    /// let mut circuit = Circuit::<5>::new();
1261    /// circuit.h_all(&[0, 1, 2])?; // Apply H to qubits 0, 1, and 2
1262    /// ```
1263    pub fn h_all(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
1264        for &qubit in qubits {
1265            self.h(QubitId::new(qubit))?;
1266        }
1267        Ok(self)
1268    }
1269
1270    /// Apply Pauli-X gates to multiple qubits at once
1271    ///
1272    /// # Example
1273    /// ```ignore
1274    /// let mut circuit = Circuit::<5>::new();
1275    /// circuit.x_all(&[0, 2, 4])?; // Apply X to qubits 0, 2, and 4
1276    /// ```
1277    pub fn x_all(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
1278        for &qubit in qubits {
1279            self.x(QubitId::new(qubit))?;
1280        }
1281        Ok(self)
1282    }
1283
1284    /// Apply Pauli-Y gates to multiple qubits at once
1285    pub fn y_all(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
1286        for &qubit in qubits {
1287            self.y(QubitId::new(qubit))?;
1288        }
1289        Ok(self)
1290    }
1291
1292    /// Apply Pauli-Z gates to multiple qubits at once
1293    pub fn z_all(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
1294        for &qubit in qubits {
1295            self.z(QubitId::new(qubit))?;
1296        }
1297        Ok(self)
1298    }
1299
1300    /// Apply Hadamard gates to a range of qubits
1301    ///
1302    /// # Example
1303    /// ```ignore
1304    /// let mut circuit = Circuit::<5>::new();
1305    /// circuit.h_range(0..3)?; // Apply H to qubits 0, 1, and 2
1306    /// ```
1307    pub fn h_range(&mut self, range: std::ops::Range<u32>) -> QuantRS2Result<&mut Self> {
1308        for qubit in range {
1309            self.h(QubitId::new(qubit))?;
1310        }
1311        Ok(self)
1312    }
1313
1314    /// Apply Pauli-X gates to a range of qubits
1315    pub fn x_range(&mut self, range: std::ops::Range<u32>) -> QuantRS2Result<&mut Self> {
1316        for qubit in range {
1317            self.x(QubitId::new(qubit))?;
1318        }
1319        Ok(self)
1320    }
1321
1322    // Common quantum state preparation patterns
1323
1324    /// Prepare a Bell state |Φ+⟩ = (|00⟩ + |11⟩)/√2 on two qubits
1325    ///
1326    /// # Example
1327    /// ```ignore
1328    /// let mut circuit = Circuit::<2>::new();
1329    /// circuit.bell_state(0, 1)?; // Prepare Bell state on qubits 0 and 1
1330    /// ```
1331    pub fn bell_state(&mut self, qubit1: u32, qubit2: u32) -> QuantRS2Result<&mut Self> {
1332        self.h(QubitId::new(qubit1))?;
1333        self.cnot(QubitId::new(qubit1), QubitId::new(qubit2))?;
1334        Ok(self)
1335    }
1336
1337    /// Prepare a GHZ state (|000...⟩ + |111...⟩)/√2 on specified qubits
1338    ///
1339    /// # Example
1340    /// ```ignore
1341    /// let mut circuit = Circuit::<3>::new();
1342    /// circuit.ghz_state(&[0, 1, 2])?; // Prepare GHZ state on qubits 0, 1, and 2
1343    /// ```
1344    pub fn ghz_state(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
1345        if qubits.is_empty() {
1346            return Ok(self);
1347        }
1348
1349        // Apply Hadamard to first qubit
1350        self.h(QubitId::new(qubits[0]))?;
1351
1352        // Apply CNOT gates to entangle all qubits
1353        for i in 1..qubits.len() {
1354            self.cnot(QubitId::new(qubits[0]), QubitId::new(qubits[i]))?;
1355        }
1356
1357        Ok(self)
1358    }
1359
1360    /// Prepare a W state on specified qubits
1361    ///
1362    /// W state: (|100...⟩ + |010...⟩ + |001...⟩ + ...)/√n
1363    ///
1364    /// This is an approximation using rotation gates.
1365    pub fn w_state(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
1366        if qubits.is_empty() {
1367            return Ok(self);
1368        }
1369
1370        let n = qubits.len() as f64;
1371
1372        // For n qubits, prepare W state using controlled rotations
1373        // This is a simplified implementation
1374        self.ry(QubitId::new(qubits[0]), 2.0 * (1.0 / n.sqrt()).acos())?;
1375
1376        for i in 1..qubits.len() {
1377            let angle = 2.0 * (1.0 / (n - i as f64).sqrt()).acos();
1378            self.cry(QubitId::new(qubits[i - 1]), QubitId::new(qubits[i]), angle)?;
1379        }
1380
1381        // Apply X gates to ensure proper state preparation
1382        for i in 0..qubits.len() - 1 {
1383            self.cnot(QubitId::new(qubits[i + 1]), QubitId::new(qubits[i]))?;
1384        }
1385
1386        Ok(self)
1387    }
1388
1389    /// Prepare a product state |++++...⟩ by applying Hadamard to all qubits
1390    ///
1391    /// # Example
1392    /// ```ignore
1393    /// let mut circuit = Circuit::<4>::new();
1394    /// circuit.plus_state_all()?; // Prepare |+⟩ on all 4 qubits
1395    /// ```
1396    pub fn plus_state_all(&mut self) -> QuantRS2Result<&mut Self> {
1397        for i in 0..N {
1398            self.h(QubitId::new(i as u32))?;
1399        }
1400        Ok(self)
1401    }
1402
1403    /// Apply a rotation gate to multiple qubits with the same angle
1404    pub fn rx_all(&mut self, qubits: &[u32], theta: f64) -> QuantRS2Result<&mut Self> {
1405        for &qubit in qubits {
1406            self.rx(QubitId::new(qubit), theta)?;
1407        }
1408        Ok(self)
1409    }
1410
1411    /// Apply RY rotation to multiple qubits
1412    pub fn ry_all(&mut self, qubits: &[u32], theta: f64) -> QuantRS2Result<&mut Self> {
1413        for &qubit in qubits {
1414            self.ry(QubitId::new(qubit), theta)?;
1415        }
1416        Ok(self)
1417    }
1418
1419    /// Apply RZ rotation to multiple qubits
1420    pub fn rz_all(&mut self, qubits: &[u32], theta: f64) -> QuantRS2Result<&mut Self> {
1421        for &qubit in qubits {
1422            self.rz(QubitId::new(qubit), theta)?;
1423        }
1424        Ok(self)
1425    }
1426
1427    /// Create a ladder of CNOT gates connecting adjacent qubits
1428    ///
1429    /// # Example
1430    /// ```ignore
1431    /// let mut circuit = Circuit::<4>::new();
1432    /// circuit.cnot_ladder(&[0, 1, 2, 3])?; // Creates: CNOT(0,1), CNOT(1,2), CNOT(2,3)
1433    /// ```
1434    pub fn cnot_ladder(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
1435        if qubits.len() < 2 {
1436            return Ok(self);
1437        }
1438
1439        for i in 0..qubits.len() - 1 {
1440            self.cnot(QubitId::new(qubits[i]), QubitId::new(qubits[i + 1]))?;
1441        }
1442
1443        Ok(self)
1444    }
1445
1446    /// Create a ring of CNOT gates connecting qubits in a cycle
1447    ///
1448    /// Like CNOT ladder but also connects last to first qubit.
1449    pub fn cnot_ring(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
1450        if qubits.len() < 2 {
1451            return Ok(self);
1452        }
1453
1454        // Add ladder
1455        self.cnot_ladder(qubits)?;
1456
1457        // Close the ring by connecting last to first
1458        let last_idx = qubits.len() - 1;
1459        self.cnot(QubitId::new(qubits[last_idx]), QubitId::new(qubits[0]))?;
1460
1461        Ok(self)
1462    }
1463
1464    /// Create a ladder of SWAP gates connecting adjacent qubits
1465    ///
1466    /// # Example
1467    /// ```ignore
1468    /// let mut circuit = Circuit::<4>::new();
1469    /// circuit.swap_ladder(&[0, 1, 2, 3])?; // Creates: SWAP(0,1), SWAP(1,2), SWAP(2,3)
1470    /// ```
1471    pub fn swap_ladder(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
1472        if qubits.len() < 2 {
1473            return Ok(self);
1474        }
1475
1476        for i in 0..qubits.len() - 1 {
1477            self.swap(QubitId::new(qubits[i]), QubitId::new(qubits[i + 1]))?;
1478        }
1479
1480        Ok(self)
1481    }
1482
1483    /// Create a ladder of CZ gates connecting adjacent qubits
1484    ///
1485    /// # Example
1486    /// ```ignore
1487    /// let mut circuit = Circuit::<4>::new();
1488    /// circuit.cz_ladder(&[0, 1, 2, 3])?; // Creates: CZ(0,1), CZ(1,2), CZ(2,3)
1489    /// ```
1490    pub fn cz_ladder(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
1491        if qubits.len() < 2 {
1492            return Ok(self);
1493        }
1494
1495        for i in 0..qubits.len() - 1 {
1496            self.cz(QubitId::new(qubits[i]), QubitId::new(qubits[i + 1]))?;
1497        }
1498
1499        Ok(self)
1500    }
1501
1502    /// Apply SWAP gates to multiple qubit pairs
1503    ///
1504    /// # Arguments
1505    /// * `pairs` - Slice of (control, target) qubit pairs
1506    ///
1507    /// # Example
1508    /// ```ignore
1509    /// let mut circuit = Circuit::<6>::new();
1510    /// circuit.swap_all(&[(0, 1), (2, 3), (4, 5)])?; // Swap three pairs simultaneously
1511    /// ```
1512    pub fn swap_all(&mut self, pairs: &[(u32, u32)]) -> QuantRS2Result<&mut Self> {
1513        for &(q1, q2) in pairs {
1514            self.swap(QubitId::new(q1), QubitId::new(q2))?;
1515        }
1516        Ok(self)
1517    }
1518
1519    /// Apply CZ gates to multiple qubit pairs
1520    ///
1521    /// # Arguments
1522    /// * `pairs` - Slice of (control, target) qubit pairs
1523    ///
1524    /// # Example
1525    /// ```ignore
1526    /// let mut circuit = Circuit::<6>::new();
1527    /// circuit.cz_all(&[(0, 1), (2, 3), (4, 5)])?; // Apply CZ to three pairs
1528    /// ```
1529    pub fn cz_all(&mut self, pairs: &[(u32, u32)]) -> QuantRS2Result<&mut Self> {
1530        for &(q1, q2) in pairs {
1531            self.cz(QubitId::new(q1), QubitId::new(q2))?;
1532        }
1533        Ok(self)
1534    }
1535
1536    /// Apply CNOT gates to multiple qubit pairs
1537    ///
1538    /// # Arguments
1539    /// * `pairs` - Slice of (control, target) qubit pairs
1540    ///
1541    /// # Example
1542    /// ```ignore
1543    /// let mut circuit = Circuit::<6>::new();
1544    /// circuit.cnot_all(&[(0, 1), (2, 3), (4, 5)])?; // Apply CNOT to three pairs
1545    /// ```
1546    pub fn cnot_all(&mut self, pairs: &[(u32, u32)]) -> QuantRS2Result<&mut Self> {
1547        for &(control, target) in pairs {
1548            self.cnot(QubitId::new(control), QubitId::new(target))?;
1549        }
1550        Ok(self)
1551    }
1552
1553    /// Add barriers to multiple qubits
1554    ///
1555    /// Barriers prevent optimization across them and can be used to
1556    /// visualize circuit structure.
1557    ///
1558    /// # Example
1559    /// ```ignore
1560    /// let mut circuit = Circuit::<5>::new();
1561    /// circuit.h_all(&[0, 1, 2])?;
1562    /// circuit.barrier_all(&[0, 1, 2])?; // Prevent optimization across this point
1563    /// circuit.cnot_ladder(&[0, 1, 2])?;
1564    /// ```
1565    pub fn barrier_all(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
1566        let qubit_ids: Vec<QubitId> = qubits.iter().map(|&q| QubitId::new(q)).collect();
1567        self.barrier(&qubit_ids)?;
1568        Ok(self)
1569    }
1570}
1571
1572impl<const N: usize> Default for Circuit<N> {
1573    fn default() -> Self {
1574        Self::new()
1575    }
1576}
1577
1578/// Trait for quantum circuit simulators
1579pub trait Simulator<const N: usize> {
1580    /// Run a quantum circuit and return the final register state
1581    fn run(&self, circuit: &Circuit<N>) -> QuantRS2Result<Register<N>>;
1582}
1583
1584#[cfg(test)]
1585mod tests {
1586    use super::*;
1587
1588    #[test]
1589    fn test_h_all() -> QuantRS2Result<()> {
1590        let mut circuit = Circuit::<5>::new();
1591        circuit.h_all(&[0, 1, 2])?;
1592
1593        assert_eq!(circuit.gates().len(), 3);
1594        for gate in circuit.gates() {
1595            assert_eq!(gate.name(), "H");
1596        }
1597        Ok(())
1598    }
1599
1600    #[test]
1601    fn test_x_all() -> QuantRS2Result<()> {
1602        let mut circuit = Circuit::<5>::new();
1603        circuit.x_all(&[0, 2, 4])?;
1604
1605        assert_eq!(circuit.gates().len(), 3);
1606        for gate in circuit.gates() {
1607            assert_eq!(gate.name(), "X");
1608        }
1609        Ok(())
1610    }
1611
1612    #[test]
1613    fn test_y_all() -> QuantRS2Result<()> {
1614        let mut circuit = Circuit::<3>::new();
1615        circuit.y_all(&[0, 1, 2])?;
1616
1617        assert_eq!(circuit.gates().len(), 3);
1618        for gate in circuit.gates() {
1619            assert_eq!(gate.name(), "Y");
1620        }
1621        Ok(())
1622    }
1623
1624    #[test]
1625    fn test_z_all() -> QuantRS2Result<()> {
1626        let mut circuit = Circuit::<4>::new();
1627        circuit.z_all(&[1, 3])?;
1628
1629        assert_eq!(circuit.gates().len(), 2);
1630        for gate in circuit.gates() {
1631            assert_eq!(gate.name(), "Z");
1632        }
1633        Ok(())
1634    }
1635
1636    #[test]
1637    fn test_h_range() -> QuantRS2Result<()> {
1638        let mut circuit = Circuit::<5>::new();
1639        circuit.h_range(0..3)?;
1640
1641        assert_eq!(circuit.gates().len(), 3);
1642        for gate in circuit.gates() {
1643            assert_eq!(gate.name(), "H");
1644        }
1645        Ok(())
1646    }
1647
1648    #[test]
1649    fn test_x_range() -> QuantRS2Result<()> {
1650        let mut circuit = Circuit::<5>::new();
1651        circuit.x_range(1..4)?;
1652
1653        assert_eq!(circuit.gates().len(), 3);
1654        for gate in circuit.gates() {
1655            assert_eq!(gate.name(), "X");
1656        }
1657        Ok(())
1658    }
1659
1660    #[test]
1661    fn test_bell_state() -> QuantRS2Result<()> {
1662        let mut circuit = Circuit::<2>::new();
1663        circuit.bell_state(0, 1)?;
1664
1665        assert_eq!(circuit.gates().len(), 2);
1666        assert_eq!(circuit.gates()[0].name(), "H");
1667        assert_eq!(circuit.gates()[1].name(), "CNOT");
1668        Ok(())
1669    }
1670
1671    #[test]
1672    fn test_ghz_state() -> QuantRS2Result<()> {
1673        let mut circuit = Circuit::<4>::new();
1674        circuit.ghz_state(&[0, 1, 2, 3])?;
1675
1676        // Should have 1 H + 3 CNOTs
1677        assert_eq!(circuit.gates().len(), 4);
1678        assert_eq!(circuit.gates()[0].name(), "H");
1679        for i in 1..4 {
1680            assert_eq!(circuit.gates()[i].name(), "CNOT");
1681        }
1682        Ok(())
1683    }
1684
1685    #[test]
1686    fn test_ghz_state_empty() -> QuantRS2Result<()> {
1687        let mut circuit = Circuit::<4>::new();
1688        circuit.ghz_state(&[])?;
1689
1690        assert_eq!(circuit.gates().len(), 0);
1691        Ok(())
1692    }
1693
1694    #[test]
1695    fn test_w_state() -> QuantRS2Result<()> {
1696        let mut circuit = Circuit::<3>::new();
1697        circuit.w_state(&[0, 1, 2])?;
1698
1699        // W state requires RY + CRY + CNOT gates
1700        assert!(!circuit.gates().is_empty());
1701        // At least one rotation gate
1702        assert!(circuit
1703            .gates()
1704            .iter()
1705            .any(|g| g.name() == "RY" || g.name() == "CRY"));
1706        Ok(())
1707    }
1708
1709    #[test]
1710    fn test_w_state_empty() -> QuantRS2Result<()> {
1711        let mut circuit = Circuit::<3>::new();
1712        circuit.w_state(&[])?;
1713
1714        assert_eq!(circuit.gates().len(), 0);
1715        Ok(())
1716    }
1717
1718    #[test]
1719    fn test_plus_state_all() -> QuantRS2Result<()> {
1720        let mut circuit = Circuit::<4>::new();
1721        circuit.plus_state_all()?;
1722
1723        assert_eq!(circuit.gates().len(), 4);
1724        for gate in circuit.gates() {
1725            assert_eq!(gate.name(), "H");
1726        }
1727        Ok(())
1728    }
1729
1730    #[test]
1731    fn test_rx_all() -> QuantRS2Result<()> {
1732        let mut circuit = Circuit::<4>::new();
1733        let theta = std::f64::consts::PI / 4.0;
1734        circuit.rx_all(&[0, 1, 2], theta)?;
1735
1736        assert_eq!(circuit.gates().len(), 3);
1737        for gate in circuit.gates() {
1738            assert_eq!(gate.name(), "RX");
1739        }
1740        Ok(())
1741    }
1742
1743    #[test]
1744    fn test_ry_all() -> QuantRS2Result<()> {
1745        let mut circuit = Circuit::<4>::new();
1746        let theta = std::f64::consts::PI / 3.0;
1747        circuit.ry_all(&[0, 2], theta)?;
1748
1749        assert_eq!(circuit.gates().len(), 2);
1750        for gate in circuit.gates() {
1751            assert_eq!(gate.name(), "RY");
1752        }
1753        Ok(())
1754    }
1755
1756    #[test]
1757    fn test_rz_all() -> QuantRS2Result<()> {
1758        let mut circuit = Circuit::<5>::new();
1759        let theta = std::f64::consts::PI / 2.0;
1760        circuit.rz_all(&[1, 2, 3], theta)?;
1761
1762        assert_eq!(circuit.gates().len(), 3);
1763        for gate in circuit.gates() {
1764            assert_eq!(gate.name(), "RZ");
1765        }
1766        Ok(())
1767    }
1768
1769    #[test]
1770    fn test_cnot_ladder() -> QuantRS2Result<()> {
1771        let mut circuit = Circuit::<5>::new();
1772        circuit.cnot_ladder(&[0, 1, 2, 3])?;
1773
1774        assert_eq!(circuit.gates().len(), 3);
1775        for gate in circuit.gates() {
1776            assert_eq!(gate.name(), "CNOT");
1777        }
1778        Ok(())
1779    }
1780
1781    #[test]
1782    fn test_cnot_ladder_too_small() -> QuantRS2Result<()> {
1783        let mut circuit = Circuit::<5>::new();
1784        circuit.cnot_ladder(&[0])?;
1785
1786        assert_eq!(circuit.gates().len(), 0);
1787        Ok(())
1788    }
1789
1790    #[test]
1791    fn test_cnot_ring() -> QuantRS2Result<()> {
1792        let mut circuit = Circuit::<4>::new();
1793        circuit.cnot_ring(&[0, 1, 2, 3])?;
1794
1795        // Should have 4 CNOTs (3 for ladder + 1 to close ring)
1796        assert_eq!(circuit.gates().len(), 4);
1797        for gate in circuit.gates() {
1798            assert_eq!(gate.name(), "CNOT");
1799        }
1800        Ok(())
1801    }
1802
1803    #[test]
1804    fn test_cnot_ring_too_small() -> QuantRS2Result<()> {
1805        let mut circuit = Circuit::<4>::new();
1806        circuit.cnot_ring(&[0])?;
1807
1808        assert_eq!(circuit.gates().len(), 0);
1809        Ok(())
1810    }
1811
1812    #[test]
1813    fn test_combined_patterns() -> QuantRS2Result<()> {
1814        let mut circuit = Circuit::<5>::new();
1815
1816        // Initialize all qubits to |+⟩
1817        circuit.plus_state_all()?;
1818
1819        // Create entanglement with CNOT ladder
1820        circuit.cnot_ladder(&[0, 1, 2, 3, 4])?;
1821
1822        // Apply phase to some qubits
1823        circuit.z_all(&[0, 2, 4])?;
1824
1825        let stats = circuit.get_stats();
1826        assert_eq!(stats.total_gates, 5 + 4 + 3); // 5 H + 4 CNOT + 3 Z
1827        assert_eq!(stats.total_qubits, 5);
1828        Ok(())
1829    }
1830
1831    #[test]
1832    fn test_swap_ladder() -> QuantRS2Result<()> {
1833        let mut circuit = Circuit::<5>::new();
1834        circuit.swap_ladder(&[0, 1, 2, 3])?;
1835
1836        assert_eq!(circuit.gates().len(), 3); // SWAP(0,1), SWAP(1,2), SWAP(2,3)
1837        for gate in circuit.gates() {
1838            assert_eq!(gate.name(), "SWAP");
1839        }
1840        Ok(())
1841    }
1842
1843    #[test]
1844    fn test_swap_ladder_empty() -> QuantRS2Result<()> {
1845        let mut circuit = Circuit::<5>::new();
1846        circuit.swap_ladder(&[])?;
1847
1848        assert_eq!(circuit.gates().len(), 0);
1849        Ok(())
1850    }
1851
1852    #[test]
1853    fn test_swap_ladder_single() -> QuantRS2Result<()> {
1854        let mut circuit = Circuit::<5>::new();
1855        circuit.swap_ladder(&[0])?;
1856
1857        assert_eq!(circuit.gates().len(), 0);
1858        Ok(())
1859    }
1860
1861    #[test]
1862    fn test_cz_ladder() -> QuantRS2Result<()> {
1863        let mut circuit = Circuit::<4>::new();
1864        circuit.cz_ladder(&[0, 1, 2, 3])?;
1865
1866        assert_eq!(circuit.gates().len(), 3); // CZ(0,1), CZ(1,2), CZ(2,3)
1867        for gate in circuit.gates() {
1868            assert_eq!(gate.name(), "CZ");
1869        }
1870        Ok(())
1871    }
1872
1873    #[test]
1874    fn test_cz_ladder_empty() -> QuantRS2Result<()> {
1875        let mut circuit = Circuit::<4>::new();
1876        circuit.cz_ladder(&[])?;
1877
1878        assert_eq!(circuit.gates().len(), 0);
1879        Ok(())
1880    }
1881
1882    #[test]
1883    fn test_swap_all() -> QuantRS2Result<()> {
1884        let mut circuit = Circuit::<6>::new();
1885        circuit.swap_all(&[(0, 1), (2, 3), (4, 5)])?;
1886
1887        assert_eq!(circuit.gates().len(), 3);
1888        for gate in circuit.gates() {
1889            assert_eq!(gate.name(), "SWAP");
1890        }
1891        Ok(())
1892    }
1893
1894    #[test]
1895    fn test_swap_all_empty() -> QuantRS2Result<()> {
1896        let mut circuit = Circuit::<6>::new();
1897        circuit.swap_all(&[])?;
1898
1899        assert_eq!(circuit.gates().len(), 0);
1900        Ok(())
1901    }
1902
1903    #[test]
1904    fn test_cz_all() -> QuantRS2Result<()> {
1905        let mut circuit = Circuit::<6>::new();
1906        circuit.cz_all(&[(0, 1), (2, 3), (4, 5)])?;
1907
1908        assert_eq!(circuit.gates().len(), 3);
1909        for gate in circuit.gates() {
1910            assert_eq!(gate.name(), "CZ");
1911        }
1912        Ok(())
1913    }
1914
1915    #[test]
1916    fn test_cz_all_empty() -> QuantRS2Result<()> {
1917        let mut circuit = Circuit::<6>::new();
1918        circuit.cz_all(&[])?;
1919
1920        assert_eq!(circuit.gates().len(), 0);
1921        Ok(())
1922    }
1923
1924    #[test]
1925    fn test_cnot_all() -> QuantRS2Result<()> {
1926        let mut circuit = Circuit::<6>::new();
1927        circuit.cnot_all(&[(0, 1), (2, 3), (4, 5)])?;
1928
1929        assert_eq!(circuit.gates().len(), 3);
1930        for gate in circuit.gates() {
1931            assert_eq!(gate.name(), "CNOT");
1932        }
1933        Ok(())
1934    }
1935
1936    #[test]
1937    fn test_cnot_all_empty() -> QuantRS2Result<()> {
1938        let mut circuit = Circuit::<6>::new();
1939        circuit.cnot_all(&[])?;
1940
1941        assert_eq!(circuit.gates().len(), 0);
1942        Ok(())
1943    }
1944
1945    #[test]
1946    fn test_barrier_all() -> QuantRS2Result<()> {
1947        let mut circuit = Circuit::<5>::new();
1948        circuit.h_all(&[0, 1, 2])?;
1949        circuit.barrier_all(&[0, 1, 2])?;
1950        circuit.cnot_ladder(&[0, 1, 2])?;
1951
1952        // Barriers don't currently add gates (they're implicit in the optimization framework)
1953        // Should have 3 H + 2 CNOT
1954        assert_eq!(circuit.gates().len(), 5);
1955        Ok(())
1956    }
1957
1958    #[test]
1959    fn test_barrier_all_empty() -> QuantRS2Result<()> {
1960        let mut circuit = Circuit::<5>::new();
1961        circuit.barrier_all(&[])?;
1962
1963        assert_eq!(circuit.gates().len(), 0);
1964        Ok(())
1965    }
1966
1967    #[test]
1968    fn test_advanced_entanglement_patterns() -> QuantRS2Result<()> {
1969        let mut circuit = Circuit::<6>::new();
1970
1971        // Create superposition
1972        circuit.h_all(&[0, 1, 2, 3, 4, 5])?;
1973
1974        // Add barrier to prevent optimization (implicit, doesn't add gates)
1975        circuit.barrier_all(&[0, 1, 2, 3, 4, 5])?;
1976
1977        // Create entanglement with CZ ladder
1978        circuit.cz_ladder(&[0, 1, 2, 3, 4, 5])?;
1979
1980        // Add more entanglement with CNOT pairs
1981        circuit.cnot_all(&[(0, 3), (1, 4), (2, 5)])?;
1982
1983        let stats = circuit.get_stats();
1984        // 6 H + 5 CZ + 3 CNOT = 14 gates (barriers are implicit)
1985        assert_eq!(stats.total_gates, 14);
1986        assert_eq!(stats.total_qubits, 6);
1987        Ok(())
1988    }
1989}