quantrs2_sim/
dynamic.rs

1use crate::simulator::Simulator; // Local simulator trait
2#[cfg(feature = "python")]
3use pyo3::exceptions::PyValueError;
4#[cfg(feature = "python")]
5use pyo3::PyResult;
6use quantrs2_circuit::builder::Circuit;
7use quantrs2_circuit::builder::Simulator as CircuitSimulator; // Circuit simulator trait
8use quantrs2_core::{
9    error::{QuantRS2Error, QuantRS2Result},
10    gate::GateOp,
11};
12use scirs2_core::Complex64;
13
14// Unused imports
15#[allow(unused_imports)]
16use crate::simulator::SimulatorResult;
17use crate::statevector::StateVectorSimulator;
18#[allow(unused_imports)]
19use quantrs2_core::qubit::QubitId;
20#[allow(unused_imports)]
21use std::collections::HashMap;
22
23#[cfg(all(feature = "gpu", not(target_os = "macos")))]
24use crate::gpu::GpuStateVectorSimulator;
25
26/// A dynamic circuit that encapsulates circuits of different qubit counts
27pub enum DynamicCircuit {
28    /// 2-qubit circuit
29    Q2(Circuit<2>),
30    /// 3-qubit circuit
31    Q3(Circuit<3>),
32    /// 4-qubit circuit
33    Q4(Circuit<4>),
34    /// 5-qubit circuit
35    Q5(Circuit<5>),
36    /// 6-qubit circuit
37    Q6(Circuit<6>),
38    /// 7-qubit circuit
39    Q7(Circuit<7>),
40    /// 8-qubit circuit
41    Q8(Circuit<8>),
42    /// 9-qubit circuit
43    Q9(Circuit<9>),
44    /// 10-qubit circuit
45    Q10(Circuit<10>),
46    /// 12-qubit circuit
47    Q12(Circuit<12>),
48    /// 16-qubit circuit
49    Q16(Circuit<16>),
50    /// 20-qubit circuit
51    Q20(Circuit<20>),
52    /// 24-qubit circuit
53    Q24(Circuit<24>),
54    /// 32-qubit circuit
55    Q32(Circuit<32>),
56}
57
58impl DynamicCircuit {
59    /// Create a new dynamic circuit with the specified number of qubits
60    pub fn new(n_qubits: usize) -> QuantRS2Result<Self> {
61        match n_qubits {
62            2 => Ok(Self::Q2(Circuit::<2>::new())),
63            3 => Ok(Self::Q3(Circuit::<3>::new())),
64            4 => Ok(Self::Q4(Circuit::<4>::new())),
65            5 => Ok(Self::Q5(Circuit::<5>::new())),
66            6 => Ok(Self::Q6(Circuit::<6>::new())),
67            7 => Ok(Self::Q7(Circuit::<7>::new())),
68            8 => Ok(Self::Q8(Circuit::<8>::new())),
69            9 => Ok(Self::Q9(Circuit::<9>::new())),
70            10 => Ok(Self::Q10(Circuit::<10>::new())),
71            12 => Ok(Self::Q12(Circuit::<12>::new())),
72            16 => Ok(Self::Q16(Circuit::<16>::new())),
73            20 => Ok(Self::Q20(Circuit::<20>::new())),
74            24 => Ok(Self::Q24(Circuit::<24>::new())),
75            32 => Ok(Self::Q32(Circuit::<32>::new())),
76            _ => Err(QuantRS2Error::UnsupportedQubits(
77                n_qubits,
78                "Supported qubit counts are 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 16, 20, 24, and 32."
79                    .to_string(),
80            )),
81        }
82    }
83
84    /// Get the list of gate names in the circuit
85    #[must_use]
86    pub fn gates(&self) -> Vec<String> {
87        self.get_gate_names()
88    }
89
90    // This method is duplicated later in the file, removing it here
91
92    // This method is duplicated later in the file, removing it here
93
94    // This method is duplicated later in the file, removing it here
95
96    // This method is duplicated later in the file, removing it here
97
98    // This method is duplicated later in the file, removing it here
99
100    /// Get the number of qubits in the circuit
101    #[must_use]
102    pub const fn num_qubits(&self) -> usize {
103        match self {
104            Self::Q2(_) => 2,
105            Self::Q3(_) => 3,
106            Self::Q4(_) => 4,
107            Self::Q5(_) => 5,
108            Self::Q6(_) => 6,
109            Self::Q7(_) => 7,
110            Self::Q8(_) => 8,
111            Self::Q9(_) => 9,
112            Self::Q10(_) => 10,
113            Self::Q12(_) => 12,
114            Self::Q16(_) => 16,
115            Self::Q20(_) => 20,
116            Self::Q24(_) => 24,
117            Self::Q32(_) => 32,
118        }
119    }
120
121    /// Get the gate names in the circuit
122    #[must_use]
123    pub fn get_gate_names(&self) -> Vec<String> {
124        match self {
125            Self::Q2(c) => c
126                .gates()
127                .iter()
128                .map(|gate| gate.name().to_string())
129                .collect(),
130            Self::Q3(c) => c
131                .gates()
132                .iter()
133                .map(|gate| gate.name().to_string())
134                .collect(),
135            Self::Q4(c) => c
136                .gates()
137                .iter()
138                .map(|gate| gate.name().to_string())
139                .collect(),
140            Self::Q5(c) => c
141                .gates()
142                .iter()
143                .map(|gate| gate.name().to_string())
144                .collect(),
145            Self::Q6(c) => c
146                .gates()
147                .iter()
148                .map(|gate| gate.name().to_string())
149                .collect(),
150            Self::Q7(c) => c
151                .gates()
152                .iter()
153                .map(|gate| gate.name().to_string())
154                .collect(),
155            Self::Q8(c) => c
156                .gates()
157                .iter()
158                .map(|gate| gate.name().to_string())
159                .collect(),
160            Self::Q9(c) => c
161                .gates()
162                .iter()
163                .map(|gate| gate.name().to_string())
164                .collect(),
165            Self::Q10(c) => c
166                .gates()
167                .iter()
168                .map(|gate| gate.name().to_string())
169                .collect(),
170            Self::Q12(c) => c
171                .gates()
172                .iter()
173                .map(|gate| gate.name().to_string())
174                .collect(),
175            Self::Q16(c) => c
176                .gates()
177                .iter()
178                .map(|gate| gate.name().to_string())
179                .collect(),
180            Self::Q20(c) => c
181                .gates()
182                .iter()
183                .map(|gate| gate.name().to_string())
184                .collect(),
185            Self::Q24(c) => c
186                .gates()
187                .iter()
188                .map(|gate| gate.name().to_string())
189                .collect(),
190            Self::Q32(c) => c
191                .gates()
192                .iter()
193                .map(|gate| gate.name().to_string())
194                .collect(),
195        }
196    }
197
198    /// Get the qubit for single-qubit gate
199    #[cfg(feature = "python")]
200    pub fn get_single_qubit_for_gate(&self, gate_type: &str, index: usize) -> PyResult<u32> {
201        // Placeholder for visualization - in a real implementation, we would track this information
202        let gate_name = gate_type.to_string();
203        let gates = self.get_gate_names();
204
205        // Find the Nth occurrence of this gate type
206        let mut count = 0;
207        for (i, name) in gates.iter().enumerate() {
208            if name == &gate_name {
209                if count == index {
210                    // Return a placeholder qubit ID - in a real implementation this would be accurate
211                    match self {
212                        Self::Q2(c) => {
213                            if let Some(gate) = c.gates().get(i) {
214                                if gate.qubits().len() == 1 {
215                                    return Ok(gate.qubits()[0].id());
216                                }
217                            }
218                        }
219                        // Repeat for all other qubit counts
220                        _ => return Ok(0),
221                    }
222                }
223                count += 1;
224            }
225        }
226
227        Err(PyValueError::new_err(format!(
228            "Gate {gate_type} at index {index} not found"
229        )))
230    }
231
232    /// Get the parameters for a rotation gate
233    #[cfg(feature = "python")]
234    pub fn get_rotation_params_for_gate(
235        &self,
236        gate_type: &str,
237        index: usize,
238    ) -> PyResult<(u32, f64)> {
239        // Placeholder for visualization - in a real implementation, we would track this information
240        let gate_name = gate_type.to_string();
241        let gates = self.get_gate_names();
242
243        // Find the Nth occurrence of this gate type
244        let mut count = 0;
245        for name in &gates {
246            if name == &gate_name {
247                if count == index {
248                    // Return placeholder values - in a real implementation these would be accurate
249                    return Ok((0, 0.0));
250                }
251                count += 1;
252            }
253        }
254
255        Err(PyValueError::new_err(format!(
256            "Gate {gate_type} at index {index} not found"
257        )))
258    }
259
260    /// Get the parameters for a two-qubit gate
261    #[cfg(feature = "python")]
262    pub fn get_two_qubit_params_for_gate(
263        &self,
264        gate_type: &str,
265        index: usize,
266    ) -> PyResult<(u32, u32)> {
267        // Placeholder for visualization - in a real implementation, we would track this information
268        let gate_name = gate_type.to_string();
269        let gates = self.get_gate_names();
270
271        // Find the Nth occurrence of this gate type
272        let mut count = 0;
273        for name in &gates {
274            if name == &gate_name {
275                if count == index {
276                    // Return placeholder values - in a real implementation these would be accurate
277                    return Ok((0, 1));
278                }
279                count += 1;
280            }
281        }
282
283        Err(PyValueError::new_err(format!(
284            "Gate {gate_type} at index {index} not found"
285        )))
286    }
287
288    /// Get the parameters for a controlled rotation gate
289    #[cfg(feature = "python")]
290    pub fn get_controlled_rotation_params_for_gate(
291        &self,
292        gate_type: &str,
293        index: usize,
294    ) -> PyResult<(u32, u32, f64)> {
295        // Placeholder for visualization - in a real implementation, we would track this information
296        let gate_name = gate_type.to_string();
297        let gates = self.get_gate_names();
298
299        // Find the Nth occurrence of this gate type
300        let mut count = 0;
301        for name in &gates {
302            if name == &gate_name {
303                if count == index {
304                    // Return placeholder values - in a real implementation these would be accurate
305                    return Ok((0, 1, 0.0));
306                }
307                count += 1;
308            }
309        }
310
311        Err(PyValueError::new_err(format!(
312            "Gate {gate_type} at index {index} not found"
313        )))
314    }
315
316    /// Get the parameters for a three-qubit gate
317    #[cfg(feature = "python")]
318    pub fn get_three_qubit_params_for_gate(
319        &self,
320        gate_type: &str,
321        index: usize,
322    ) -> PyResult<(u32, u32, u32)> {
323        // Placeholder for visualization - in a real implementation, we would track this information
324        let gate_name = gate_type.to_string();
325        let gates = self.get_gate_names();
326
327        // Find the Nth occurrence of this gate type
328        let mut count = 0;
329        for name in &gates {
330            if name == &gate_name {
331                if count == index {
332                    // Return placeholder values - in a real implementation these would be accurate
333                    return Ok((0, 1, 2));
334                }
335                count += 1;
336            }
337        }
338
339        Err(PyValueError::new_err(format!(
340            "Gate {gate_type} at index {index} not found"
341        )))
342    }
343
344    /// Apply a gate to the circuit
345    pub fn apply_gate<G: GateOp + Clone + Send + Sync + 'static>(
346        &mut self,
347        gate: G,
348    ) -> QuantRS2Result<()> {
349        match self {
350            Self::Q2(c) => c.add_gate(gate).map(|_| ()),
351            Self::Q3(c) => c.add_gate(gate).map(|_| ()),
352            Self::Q4(c) => c.add_gate(gate).map(|_| ()),
353            Self::Q5(c) => c.add_gate(gate).map(|_| ()),
354            Self::Q6(c) => c.add_gate(gate).map(|_| ()),
355            Self::Q7(c) => c.add_gate(gate).map(|_| ()),
356            Self::Q8(c) => c.add_gate(gate).map(|_| ()),
357            Self::Q9(c) => c.add_gate(gate).map(|_| ()),
358            Self::Q10(c) => c.add_gate(gate).map(|_| ()),
359            Self::Q12(c) => c.add_gate(gate).map(|_| ()),
360            Self::Q16(c) => c.add_gate(gate).map(|_| ()),
361            Self::Q20(c) => c.add_gate(gate).map(|_| ()),
362            Self::Q24(c) => c.add_gate(gate).map(|_| ()),
363            Self::Q32(c) => c.add_gate(gate).map(|_| ()),
364        }
365    }
366
367    /// Run the circuit on a CPU simulator
368    pub fn run(&self, simulator: &StateVectorSimulator) -> QuantRS2Result<DynamicResult> {
369        match self {
370            Self::Q2(c) => {
371                let result = simulator.run(c)?;
372                Ok(DynamicResult {
373                    amplitudes: result.amplitudes().to_vec(),
374                    num_qubits: 2,
375                })
376            }
377            Self::Q3(c) => {
378                let result = simulator.run(c)?;
379                Ok(DynamicResult {
380                    amplitudes: result.amplitudes().to_vec(),
381                    num_qubits: 3,
382                })
383            }
384            Self::Q4(c) => {
385                let result = simulator.run(c)?;
386                Ok(DynamicResult {
387                    amplitudes: result.amplitudes().to_vec(),
388                    num_qubits: 4,
389                })
390            }
391            Self::Q5(c) => {
392                let result = simulator.run(c)?;
393                Ok(DynamicResult {
394                    amplitudes: result.amplitudes().to_vec(),
395                    num_qubits: 5,
396                })
397            }
398            Self::Q6(c) => {
399                let result = simulator.run(c)?;
400                Ok(DynamicResult {
401                    amplitudes: result.amplitudes().to_vec(),
402                    num_qubits: 6,
403                })
404            }
405            Self::Q7(c) => {
406                let result = simulator.run(c)?;
407                Ok(DynamicResult {
408                    amplitudes: result.amplitudes().to_vec(),
409                    num_qubits: 7,
410                })
411            }
412            Self::Q8(c) => {
413                let result = simulator.run(c)?;
414                Ok(DynamicResult {
415                    amplitudes: result.amplitudes().to_vec(),
416                    num_qubits: 8,
417                })
418            }
419            Self::Q9(c) => {
420                let result = simulator.run(c)?;
421                Ok(DynamicResult {
422                    amplitudes: result.amplitudes().to_vec(),
423                    num_qubits: 9,
424                })
425            }
426            Self::Q10(c) => {
427                let result = simulator.run(c)?;
428                Ok(DynamicResult {
429                    amplitudes: result.amplitudes().to_vec(),
430                    num_qubits: 10,
431                })
432            }
433            Self::Q12(c) => {
434                let result = simulator.run(c)?;
435                Ok(DynamicResult {
436                    amplitudes: result.amplitudes().to_vec(),
437                    num_qubits: 12,
438                })
439            }
440            Self::Q16(c) => {
441                let result = simulator.run(c)?;
442                Ok(DynamicResult {
443                    amplitudes: result.amplitudes().to_vec(),
444                    num_qubits: 16,
445                })
446            }
447            Self::Q20(c) => {
448                let result = simulator.run(c)?;
449                Ok(DynamicResult {
450                    amplitudes: result.amplitudes().to_vec(),
451                    num_qubits: 20,
452                })
453            }
454            Self::Q24(c) => {
455                let result = simulator.run(c)?;
456                Ok(DynamicResult {
457                    amplitudes: result.amplitudes().to_vec(),
458                    num_qubits: 24,
459                })
460            }
461            Self::Q32(c) => {
462                let result = simulator.run(c)?;
463                Ok(DynamicResult {
464                    amplitudes: result.amplitudes().to_vec(),
465                    num_qubits: 32,
466                })
467            }
468        }
469    }
470
471    /// Check if GPU acceleration is available
472    #[cfg(all(feature = "gpu", not(target_os = "macos")))]
473    pub fn is_gpu_available() -> bool {
474        GpuStateVectorSimulator::is_available()
475    }
476
477    /// Run the circuit on a GPU simulator
478    #[cfg(all(feature = "gpu", not(target_os = "macos")))]
479    pub fn run_gpu(&self) -> QuantRS2Result<DynamicResult> {
480        // Try to create the GPU simulator
481        let mut gpu_simulator = match GpuStateVectorSimulator::new_blocking() {
482            Ok(sim) => sim,
483            Err(e) => {
484                return Err(QuantRS2Error::BackendExecutionFailed(format!(
485                    "Failed to create GPU simulator: {}",
486                    e
487                )))
488            }
489        };
490
491        // Run the circuit on the GPU
492        match self {
493            DynamicCircuit::Q2(c) => {
494                let result = gpu_simulator.run(c).map_err(|e| {
495                    QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
496                })?;
497                Ok(DynamicResult {
498                    amplitudes: result.amplitudes.clone(),
499                    num_qubits: 2,
500                })
501            }
502            DynamicCircuit::Q3(c) => {
503                let result = gpu_simulator.run(c).map_err(|e| {
504                    QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
505                })?;
506                Ok(DynamicResult {
507                    amplitudes: result.amplitudes.clone(),
508                    num_qubits: 3,
509                })
510            }
511            DynamicCircuit::Q4(c) => {
512                let result = gpu_simulator.run(c).map_err(|e| {
513                    QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
514                })?;
515                Ok(DynamicResult {
516                    amplitudes: result.amplitudes.clone(),
517                    num_qubits: 4,
518                })
519            }
520            DynamicCircuit::Q5(c) => {
521                let result = gpu_simulator.run(c).map_err(|e| {
522                    QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
523                })?;
524                Ok(DynamicResult {
525                    amplitudes: result.amplitudes.clone(),
526                    num_qubits: 5,
527                })
528            }
529            DynamicCircuit::Q6(c) => {
530                let result = gpu_simulator.run(c).map_err(|e| {
531                    QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
532                })?;
533                Ok(DynamicResult {
534                    amplitudes: result.amplitudes.clone(),
535                    num_qubits: 6,
536                })
537            }
538            DynamicCircuit::Q7(c) => {
539                let result = gpu_simulator.run(c).map_err(|e| {
540                    QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
541                })?;
542                Ok(DynamicResult {
543                    amplitudes: result.amplitudes.clone(),
544                    num_qubits: 7,
545                })
546            }
547            DynamicCircuit::Q8(c) => {
548                let result = gpu_simulator.run(c).map_err(|e| {
549                    QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
550                })?;
551                Ok(DynamicResult {
552                    amplitudes: result.amplitudes.clone(),
553                    num_qubits: 8,
554                })
555            }
556            DynamicCircuit::Q9(c) => {
557                let result = gpu_simulator.run(c).map_err(|e| {
558                    QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
559                })?;
560                Ok(DynamicResult {
561                    amplitudes: result.amplitudes.clone(),
562                    num_qubits: 9,
563                })
564            }
565            DynamicCircuit::Q10(c) => {
566                let result = gpu_simulator.run(c).map_err(|e| {
567                    QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
568                })?;
569                Ok(DynamicResult {
570                    amplitudes: result.amplitudes.clone(),
571                    num_qubits: 10,
572                })
573            }
574            DynamicCircuit::Q12(c) => {
575                let result = gpu_simulator.run(c).map_err(|e| {
576                    QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
577                })?;
578                Ok(DynamicResult {
579                    amplitudes: result.amplitudes.clone(),
580                    num_qubits: 12,
581                })
582            }
583            DynamicCircuit::Q16(c) => {
584                let result = gpu_simulator.run(c).map_err(|e| {
585                    QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
586                })?;
587                Ok(DynamicResult {
588                    amplitudes: result.amplitudes.clone(),
589                    num_qubits: 16,
590                })
591            }
592            DynamicCircuit::Q20(c) => {
593                let result = gpu_simulator.run(c).map_err(|e| {
594                    QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
595                })?;
596                Ok(DynamicResult {
597                    amplitudes: result.amplitudes.clone(),
598                    num_qubits: 20,
599                })
600            }
601            DynamicCircuit::Q24(c) => {
602                let result = gpu_simulator.run(c).map_err(|e| {
603                    QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
604                })?;
605                Ok(DynamicResult {
606                    amplitudes: result.amplitudes.clone(),
607                    num_qubits: 24,
608                })
609            }
610            DynamicCircuit::Q32(c) => {
611                let result = gpu_simulator.run(c).map_err(|e| {
612                    QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
613                })?;
614                Ok(DynamicResult {
615                    amplitudes: result.amplitudes.clone(),
616                    num_qubits: 32,
617                })
618            }
619        }
620    }
621
622    /// Check if GPU acceleration is available (stub for macOS)
623    #[cfg(not(all(feature = "gpu", not(target_os = "macos"))))]
624    #[must_use]
625    pub const fn is_gpu_available() -> bool {
626        false
627    }
628
629    /// Run the circuit on a GPU simulator (stub for macOS)
630    #[cfg(not(all(feature = "gpu", not(target_os = "macos"))))]
631    pub fn run_gpu(&self) -> QuantRS2Result<DynamicResult> {
632        Err(QuantRS2Error::BackendExecutionFailed(
633            "GPU acceleration is not available on this platform".to_string(),
634        ))
635    }
636
637    /// Run the circuit on the best available simulator (GPU if available, CPU otherwise)
638    #[cfg(all(feature = "gpu", not(target_os = "macos")))]
639    pub fn run_best(&self) -> QuantRS2Result<DynamicResult> {
640        if Self::is_gpu_available() && self.num_qubits() >= 4 {
641            self.run_gpu()
642        } else {
643            let simulator = StateVectorSimulator::new();
644            self.run(&simulator)
645        }
646    }
647
648    /// Run the circuit on the best available simulator (CPU only on macOS with GPU feature)
649    #[cfg(all(feature = "gpu", target_os = "macos"))]
650    pub fn run_best(&self) -> QuantRS2Result<DynamicResult> {
651        let simulator = StateVectorSimulator::new();
652        self.run(&simulator)
653    }
654
655    /// Run the circuit on the best available simulator (CPU only if GPU feature is disabled)
656    #[cfg(not(feature = "gpu"))]
657    pub fn run_best(&self) -> QuantRS2Result<DynamicResult> {
658        let simulator = StateVectorSimulator::new();
659        self.run(&simulator)
660    }
661}
662
663/// Dynamic simulation result that can handle any qubit count
664pub struct DynamicResult {
665    /// State vector amplitudes
666    pub amplitudes: Vec<Complex64>,
667    /// Number of qubits
668    pub num_qubits: usize,
669}
670
671impl DynamicResult {
672    /// Get the state vector amplitudes
673    #[must_use]
674    pub fn amplitudes(&self) -> &[Complex64] {
675        &self.amplitudes
676    }
677
678    /// Get the probabilities for each basis state
679    #[must_use]
680    pub fn probabilities(&self) -> Vec<f64> {
681        self.amplitudes
682            .iter()
683            .map(scirs2_core::Complex::norm_sqr)
684            .collect()
685    }
686
687    /// Get the number of qubits
688    #[must_use]
689    pub const fn num_qubits(&self) -> usize {
690        self.num_qubits
691    }
692}