Skip to main content

quantrs2_sim/
dynamic.rs

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