Skip to main content

quantrs2_sim/
statevector.rs

1//! State-vector quantum circuit simulator.
2//!
3//! [`StateVectorSimulator`] stores the full 2^N complex amplitude vector and
4//! applies unitary gates via direct matrix-vector products.  It is the default
5//! exact simulator in QuantRS2 and supports up to ~30 qubits on a typical
6//! workstation.
7
8use scirs2_core::parallel_ops::{
9    IndexedParallelIterator, IntoParallelRefMutIterator, ParallelIterator,
10};
11use scirs2_core::Complex64;
12use std::sync::Mutex;
13
14use quantrs2_circuit::builder::{Circuit, Simulator};
15use quantrs2_core::{
16    error::{QuantRS2Error, QuantRS2Result},
17    gate::{multi, single, GateOp},
18    qubit::QubitId,
19    register::Register,
20};
21
22use crate::diagnostics::SimulationDiagnostics;
23use crate::optimized_simd;
24use crate::scirs2_integration::{SciRS2Backend, SciRS2Matrix, SciRS2Vector, BLAS};
25use crate::utils::{flip_bit, gate_vec_to_array2};
26
27/// A state vector simulator for quantum circuits.
28///
29/// This simulator implements the state vector approach, where the full quantum
30/// state is represented as a complex vector of dimension 2^N for N qubits.
31///
32/// # Examples
33///
34/// ```rust
35/// use quantrs2_sim::statevector::StateVectorSimulator;
36/// use quantrs2_circuit::builder::Circuit;
37/// use quantrs2_circuit::builder::Simulator;
38///
39/// let mut circ: Circuit<2> = Circuit::new();
40/// circ.h(0).expect("h").cnot(0, 1).expect("cnot");
41/// let sim = StateVectorSimulator::new();
42/// let result = sim.run(&circ).expect("simulation failed");
43/// assert_eq!(result.num_qubits(), 2);
44/// ```
45#[derive(Debug)]
46pub struct StateVectorSimulator {
47    /// Use parallel execution
48    pub parallel: bool,
49
50    /// Basic noise model (if any)
51    pub noise_model: Option<crate::noise::NoiseModel>,
52
53    /// Advanced noise model (if any)
54    pub advanced_noise_model: Option<crate::noise_advanced::AdvancedNoiseModel>,
55
56    /// Optimized buffer pool for memory reuse (thread-safe)
57    buffer_pool: Mutex<BufferPool>,
58
59    /// Enable SIMD optimizations for gate operations
60    pub use_simd: bool,
61
62    /// Enable gate fusion optimization
63    pub use_gate_fusion: bool,
64
65    /// Diagnostics system for monitoring and error handling
66    pub diagnostics: Option<SimulationDiagnostics>,
67
68    /// `SciRS2` backend for optimized linear algebra operations
69    scirs2_backend: SciRS2Backend,
70
71    /// Current quantum state managed by the imperative gate API
72    /// ([`StateVectorSimulator::initialize_state`], [`StateVectorSimulator::apply_h`], ...).
73    ///
74    /// This is empty until [`StateVectorSimulator::initialize_state`] (or
75    /// [`StateVectorSimulator::apply_interface_circuit`]) is called. The
76    /// [`Simulator::run`] entry point does not use this field; it evolves a
77    /// local state vector and is independent of the imperative API.
78    current_state: Vec<Complex64>,
79}
80
81impl Clone for StateVectorSimulator {
82    fn clone(&self) -> Self {
83        Self {
84            parallel: self.parallel,
85            noise_model: self.noise_model.clone(),
86            advanced_noise_model: self.advanced_noise_model.clone(),
87            buffer_pool: Mutex::new(BufferPool::new(4, 1024)), // Create new buffer pool
88            use_simd: self.use_simd,
89            use_gate_fusion: self.use_gate_fusion,
90            diagnostics: self
91                .diagnostics
92                .as_ref()
93                .map(|_| SimulationDiagnostics::new()),
94            scirs2_backend: SciRS2Backend::new(),
95            current_state: self.current_state.clone(),
96        }
97    }
98}
99
100/// Memory pool for efficient state vector operations
101#[derive(Debug, Clone)]
102pub struct BufferPool {
103    /// Pre-allocated working buffers
104    working_buffers: std::collections::VecDeque<Vec<Complex64>>,
105    /// Maximum number of cached buffers
106    max_buffers: usize,
107    /// Target buffer size for efficient reuse
108    target_size: usize,
109}
110
111impl BufferPool {
112    /// Create new buffer pool
113    #[must_use]
114    pub fn new(max_buffers: usize, target_size: usize) -> Self {
115        Self {
116            working_buffers: std::collections::VecDeque::with_capacity(max_buffers),
117            max_buffers,
118            target_size,
119        }
120    }
121
122    /// Get a buffer from pool or allocate new one
123    pub fn get_buffer(&mut self, size: usize) -> Vec<Complex64> {
124        // Try to reuse existing buffer
125        if let Some(mut buffer) = self.working_buffers.pop_front() {
126            if buffer.capacity() >= size {
127                buffer.clear();
128                buffer.resize(size, Complex64::new(0.0, 0.0));
129                return buffer;
130            }
131        }
132
133        // Allocate new buffer with extra capacity for growth
134        let capacity = size.max(self.target_size);
135        let mut buffer = Vec::with_capacity(capacity);
136        buffer.resize(size, Complex64::new(0.0, 0.0));
137        buffer
138    }
139
140    /// Return buffer to pool
141    pub fn return_buffer(&mut self, buffer: Vec<Complex64>) {
142        if self.working_buffers.len() < self.max_buffers
143            && buffer.capacity() >= self.target_size / 2
144        {
145            self.working_buffers.push_back(buffer);
146        }
147        // Otherwise let buffer be dropped and deallocated
148    }
149
150    /// Clear all cached buffers
151    pub fn clear(&mut self) {
152        self.working_buffers.clear();
153    }
154}
155
156impl StateVectorSimulator {
157    /// Create a new state vector simulator with default settings
158    #[must_use]
159    pub fn new() -> Self {
160        Self {
161            parallel: true,
162            noise_model: None,
163            advanced_noise_model: None,
164            buffer_pool: Mutex::new(BufferPool::new(4, 1024)), // Default: 4 buffers, 1K size target
165            use_simd: true,
166            use_gate_fusion: true,
167            diagnostics: None,
168            scirs2_backend: SciRS2Backend::new(),
169            current_state: Vec::new(),
170        }
171    }
172
173    /// Create a new state vector simulator with parallel execution disabled
174    #[must_use]
175    pub fn sequential() -> Self {
176        Self {
177            parallel: false,
178            noise_model: None,
179            advanced_noise_model: None,
180            buffer_pool: Mutex::new(BufferPool::new(2, 512)), // Smaller pool for sequential
181            use_simd: true,
182            use_gate_fusion: true,
183            diagnostics: None,
184            scirs2_backend: SciRS2Backend::new(),
185            current_state: Vec::new(),
186        }
187    }
188
189    /// Create a new state vector simulator with a basic noise model
190    #[must_use]
191    pub fn with_noise(noise_model: crate::noise::NoiseModel) -> Self {
192        Self {
193            parallel: true,
194            noise_model: Some(noise_model),
195            advanced_noise_model: None,
196            buffer_pool: Mutex::new(BufferPool::new(4, 1024)),
197            use_simd: true,
198            use_gate_fusion: true,
199            diagnostics: None,
200            scirs2_backend: SciRS2Backend::new(),
201            current_state: Vec::new(),
202        }
203    }
204
205    /// Create a new state vector simulator with an advanced noise model
206    #[must_use]
207    pub fn with_advanced_noise(
208        advanced_noise_model: crate::noise_advanced::AdvancedNoiseModel,
209    ) -> Self {
210        Self {
211            parallel: true,
212            noise_model: None,
213            advanced_noise_model: Some(advanced_noise_model),
214            buffer_pool: Mutex::new(BufferPool::new(4, 1024)),
215            use_simd: true,
216            use_gate_fusion: true,
217            diagnostics: None,
218            scirs2_backend: SciRS2Backend::new(),
219            current_state: Vec::new(),
220        }
221    }
222
223    /// Create simulator with custom buffer pool configuration
224    #[must_use]
225    pub fn with_buffer_pool(parallel: bool, max_buffers: usize, target_size: usize) -> Self {
226        Self {
227            parallel,
228            noise_model: None,
229            advanced_noise_model: None,
230            buffer_pool: Mutex::new(BufferPool::new(max_buffers, target_size)),
231            use_simd: true,
232            use_gate_fusion: true,
233            diagnostics: None,
234            scirs2_backend: SciRS2Backend::new(),
235            current_state: Vec::new(),
236        }
237    }
238
239    /// Set the basic noise model
240    pub fn set_noise_model(&mut self, noise_model: crate::noise::NoiseModel) -> &mut Self {
241        self.noise_model = Some(noise_model);
242        self.advanced_noise_model = None; // Remove advanced model if it exists
243        self
244    }
245
246    /// Set the advanced noise model
247    pub fn set_advanced_noise_model(
248        &mut self,
249        advanced_noise_model: crate::noise_advanced::AdvancedNoiseModel,
250    ) -> &mut Self {
251        self.advanced_noise_model = Some(advanced_noise_model);
252        self.noise_model = None; // Remove basic model if it exists
253        self
254    }
255
256    /// Remove all noise models
257    pub fn remove_noise_model(&mut self) -> &mut Self {
258        self.noise_model = None;
259        self.advanced_noise_model = None;
260        self
261    }
262
263    /// Enable or disable SIMD optimizations
264    pub const fn set_simd_enabled(&mut self, enabled: bool) -> &mut Self {
265        self.use_simd = enabled;
266        self
267    }
268
269    /// Enable or disable gate fusion optimization
270    pub const fn set_gate_fusion_enabled(&mut self, enabled: bool) -> &mut Self {
271        self.use_gate_fusion = enabled;
272        self
273    }
274
275    /// Get access to buffer pool for testing purposes
276    pub const fn get_buffer_pool(&self) -> &Mutex<BufferPool> {
277        &self.buffer_pool
278    }
279
280    /// Enable diagnostics and monitoring
281    pub fn enable_diagnostics(&mut self) -> &mut Self {
282        self.diagnostics = Some(SimulationDiagnostics::new());
283        self
284    }
285
286    /// Disable diagnostics and monitoring
287    pub fn disable_diagnostics(&mut self) -> &mut Self {
288        self.diagnostics = None;
289        self
290    }
291
292    /// Get diagnostics report if diagnostics are enabled
293    pub fn get_diagnostics_report(&self) -> Option<crate::diagnostics::DiagnosticReport> {
294        self.diagnostics
295            .as_ref()
296            .map(super::diagnostics::SimulationDiagnostics::generate_report)
297    }
298
299    /// Create a high-performance configuration
300    #[must_use]
301    pub fn high_performance() -> Self {
302        Self {
303            parallel: true,
304            noise_model: None,
305            advanced_noise_model: None,
306            buffer_pool: Mutex::new(BufferPool::new(8, 2048)), // Larger pool for high performance
307            use_simd: true,
308            use_gate_fusion: true,
309            diagnostics: Some(SimulationDiagnostics::new()),
310            scirs2_backend: SciRS2Backend::new(),
311            current_state: Vec::new(),
312        }
313    }
314
315    /// Apply a dense matrix-vector multiplication using SciRS2 when available
316    #[cfg(feature = "advanced_math")]
317    fn apply_dense_matrix_vector(
318        &mut self,
319        matrix: &[Complex64],
320        vector: &[Complex64],
321        result: &mut [Complex64],
322    ) -> QuantRS2Result<()> {
323        if self.scirs2_backend.is_available() && matrix.len() >= 64 && vector.len() >= 8 {
324            // Use SciRS2 for larger operations where the overhead is worthwhile
325            use scirs2_core::ndarray::{Array1, Array2};
326
327            let rows = result.len();
328            let cols = vector.len();
329
330            if matrix.len() != rows * cols {
331                return Err(QuantRS2Error::InvalidInput(
332                    "Dimension mismatch in matrix application".to_string(),
333                ));
334            }
335
336            // Convert to ndarray format
337            let matrix_2d =
338                Array2::from_shape_vec((rows, cols), matrix.to_vec()).map_err(|_| {
339                    QuantRS2Error::InvalidInput("Matrix shape conversion failed".to_string())
340                })?;
341            let vector_1d = Array1::from_vec(vector.to_vec());
342
343            // Convert to SciRS2 format
344            let scirs2_matrix = SciRS2Matrix::from_array2(matrix_2d);
345            let scirs2_vector = SciRS2Vector::from_array1(vector_1d);
346
347            // Perform optimized matrix-vector multiplication
348            let scirs2_result = self
349                .scirs2_backend
350                .matrix_vector_multiply(&scirs2_matrix, &scirs2_vector)
351                .map_err(|_| {
352                    QuantRS2Error::ComputationError(
353                        "Matrix-vector multiplication failed".to_string(),
354                    )
355                })?;
356
357            // Convert back to slice
358            let result_array = scirs2_result.to_array1().map_err(|_| {
359                QuantRS2Error::ComputationError("Result conversion to array failed".to_string())
360            })?;
361            if let Some(slice) = result_array.as_slice() {
362                result.copy_from_slice(slice);
363            } else {
364                return Err(QuantRS2Error::ComputationError(
365                    "Result array is not contiguous".to_string(),
366                ));
367            }
368
369            Ok(())
370        } else {
371            // Fallback to manual implementation for smaller operations
372            for i in 0..result.len() {
373                result[i] = Complex64::new(0.0, 0.0);
374                for j in 0..vector.len() {
375                    result[i] += matrix[i * vector.len() + j] * vector[j];
376                }
377            }
378            Ok(())
379        }
380    }
381
382    /// Fallback dense matrix-vector multiplication for when `SciRS2` is not available
383    #[cfg(not(feature = "advanced_math"))]
384    fn apply_dense_matrix_vector(
385        &self,
386        matrix: &[Complex64],
387        vector: &[Complex64],
388        result: &mut [Complex64],
389    ) -> QuantRS2Result<()> {
390        // Manual implementation
391        for i in 0..result.len() {
392            result[i] = Complex64::new(0.0, 0.0);
393            for j in 0..vector.len() {
394                result[i] += matrix[i * vector.len() + j] * vector[j];
395            }
396        }
397        Ok(())
398    }
399
400    /// Apply a single-qubit gate to a state vector
401    fn apply_single_qubit_gate<const N: usize>(
402        &self,
403        state: &mut [Complex64],
404        gate_matrix: &[Complex64],
405        target: QubitId,
406    ) -> QuantRS2Result<()> {
407        let target_idx = target.id() as usize;
408        if target_idx >= N {
409            return Err(QuantRS2Error::InvalidQubitId(target.id()));
410        }
411
412        // Use SIMD optimization if enabled and beneficial
413        if self.use_simd && state.len() >= 8 {
414            return self.apply_single_qubit_gate_simd::<N>(state, gate_matrix, target_idx);
415        }
416
417        // Convert the gate matrix to flat representation for faster access
418        // Gate matrix: [m00, m01, m10, m11]
419        let m00 = gate_matrix[0];
420        let m01 = gate_matrix[1];
421        let m10 = gate_matrix[2];
422        let m11 = gate_matrix[3];
423
424        // Apply the gate to each amplitude
425        if self.parallel {
426            // Get buffer from pool for temporary storage
427            let mut state_copy = self
428                .buffer_pool
429                .lock()
430                .expect("buffer pool lock poisoned")
431                .get_buffer(state.len());
432            state_copy.copy_from_slice(state);
433
434            state.par_iter_mut().enumerate().for_each(|(idx, amp)| {
435                let bit_val = (idx >> target_idx) & 1;
436                let paired_idx = if bit_val == 0 {
437                    idx | (1 << target_idx)
438                } else {
439                    idx & !(1 << target_idx)
440                };
441
442                let idx0 = if bit_val == 0 { idx } else { paired_idx };
443                let idx1 = if bit_val == 0 { paired_idx } else { idx };
444
445                let val0 = state_copy[idx0];
446                let val1 = state_copy[idx1];
447
448                // Use direct matrix element access for better performance
449                *amp = if idx == idx0 {
450                    m00 * val0 + m01 * val1
451                } else {
452                    m10 * val0 + m11 * val1
453                };
454            });
455
456            // Return buffer to pool
457            self.buffer_pool
458                .lock()
459                .expect("buffer pool lock poisoned")
460                .return_buffer(state_copy);
461        } else {
462            // Sequential implementation using buffer pool
463            let dim = state.len();
464            let mut new_state = self
465                .buffer_pool
466                .lock()
467                .expect("buffer pool lock poisoned")
468                .get_buffer(dim);
469
470            for i in 0..dim {
471                let bit_val = (i >> target_idx) & 1;
472                let paired_idx = flip_bit(i, target_idx);
473
474                if bit_val == 0 {
475                    new_state[i] = m00 * state[i] + m01 * state[paired_idx];
476                    new_state[paired_idx] = m10 * state[i] + m11 * state[paired_idx];
477                }
478            }
479
480            state.copy_from_slice(&new_state);
481            // Return buffer to pool
482            self.buffer_pool
483                .lock()
484                .expect("buffer pool lock poisoned")
485                .return_buffer(new_state);
486        }
487
488        Ok(())
489    }
490
491    /// Apply a single-qubit gate using SIMD optimization
492    fn apply_single_qubit_gate_simd<const N: usize>(
493        &self,
494        state: &mut [Complex64],
495        gate_matrix: &[Complex64],
496        target_idx: usize,
497    ) -> QuantRS2Result<()> {
498        let dim = state.len();
499        let pairs_per_block = 1 << target_idx;
500        let total_pairs = dim / 2;
501
502        // Get buffers for SIMD processing
503        let mut pool = self.buffer_pool.lock().expect("buffer pool lock poisoned");
504        let mut in_amps0 = pool.get_buffer(total_pairs);
505        let mut in_amps1 = pool.get_buffer(total_pairs);
506        let mut out_amps0 = pool.get_buffer(total_pairs);
507        let mut out_amps1 = pool.get_buffer(total_pairs);
508
509        // Collect amplitudes for target bit = 0 and target bit = 1
510        let mut pair_idx = 0;
511        for block in 0..(dim / (pairs_per_block * 2)) {
512            let block_start = block * pairs_per_block * 2;
513
514            for offset in 0..pairs_per_block {
515                let idx0 = block_start + offset;
516                let idx1 = block_start + pairs_per_block + offset;
517
518                in_amps0[pair_idx] = state[idx0];
519                in_amps1[pair_idx] = state[idx1];
520                pair_idx += 1;
521            }
522        }
523
524        // Convert gate matrix to required format for SIMD
525        let gate_matrix_array: [Complex64; 4] = [
526            gate_matrix[0],
527            gate_matrix[1],
528            gate_matrix[2],
529            gate_matrix[3],
530        ];
531
532        // Apply SIMD gate operation
533        optimized_simd::apply_single_qubit_gate_optimized(
534            &gate_matrix_array,
535            &in_amps0,
536            &in_amps1,
537            &mut out_amps0,
538            &mut out_amps1,
539        );
540
541        // Write results back to state vector
542        pair_idx = 0;
543        for block in 0..(dim / (pairs_per_block * 2)) {
544            let block_start = block * pairs_per_block * 2;
545
546            for offset in 0..pairs_per_block {
547                let idx0 = block_start + offset;
548                let idx1 = block_start + pairs_per_block + offset;
549
550                state[idx0] = out_amps0[pair_idx];
551                state[idx1] = out_amps1[pair_idx];
552                pair_idx += 1;
553            }
554        }
555
556        // Return buffers to pool
557        pool.return_buffer(in_amps0);
558        pool.return_buffer(in_amps1);
559        pool.return_buffer(out_amps0);
560        pool.return_buffer(out_amps1);
561
562        Ok(())
563    }
564
565    /// Apply a two-qubit gate to a state vector
566    fn apply_two_qubit_gate<const N: usize>(
567        &self,
568        state: &mut [Complex64],
569        gate_matrix: &[Complex64],
570        control: QubitId,
571        target: QubitId,
572    ) -> QuantRS2Result<()> {
573        let control_idx = control.id() as usize;
574        let target_idx = target.id() as usize;
575
576        if control_idx >= N || target_idx >= N {
577            return Err(QuantRS2Error::InvalidQubitId(if control_idx >= N {
578                control.id()
579            } else {
580                target.id()
581            }));
582        }
583
584        if control_idx == target_idx {
585            return Err(QuantRS2Error::CircuitValidationFailed(
586                "Control and target qubits must be different".into(),
587            ));
588        }
589
590        // Pre-extract matrix elements for faster access (16 elements total)
591        let m = gate_matrix; // Direct slice access is faster than ndarray indexing
592
593        // Apply the gate to each amplitude
594        if self.parallel {
595            // Get buffer from pool for temporary storage
596            let mut state_copy = self
597                .buffer_pool
598                .lock()
599                .expect("buffer pool lock poisoned")
600                .get_buffer(state.len());
601            state_copy.copy_from_slice(state);
602
603            state.par_iter_mut().enumerate().for_each(|(idx, amp)| {
604                let idx00 = idx & !(1 << control_idx) & !(1 << target_idx);
605                let idx01 = idx00 | (1 << target_idx);
606                let idx10 = idx00 | (1 << control_idx);
607                let idx11 = idx00 | (1 << control_idx) | (1 << target_idx);
608
609                let val00 = state_copy[idx00];
610                let val01 = state_copy[idx01];
611                let val10 = state_copy[idx10];
612                let val11 = state_copy[idx11];
613
614                // Use direct matrix access for better performance
615                *amp = match idx {
616                    i if i == idx00 => m[0] * val00 + m[1] * val01 + m[2] * val10 + m[3] * val11,
617                    i if i == idx01 => m[4] * val00 + m[5] * val01 + m[6] * val10 + m[7] * val11,
618                    i if i == idx10 => m[8] * val00 + m[9] * val01 + m[10] * val10 + m[11] * val11,
619                    i if i == idx11 => {
620                        m[12] * val00 + m[13] * val01 + m[14] * val10 + m[15] * val11
621                    }
622                    _ => unreachable!(),
623                };
624            });
625
626            // Return buffer to pool
627            self.buffer_pool
628                .lock()
629                .expect("buffer pool lock poisoned")
630                .return_buffer(state_copy);
631        } else {
632            // Sequential implementation using buffer pool
633            let dim = state.len();
634            let mut new_state = self
635                .buffer_pool
636                .lock()
637                .expect("buffer pool lock poisoned")
638                .get_buffer(dim);
639
640            #[allow(clippy::needless_range_loop)]
641            for i in 0..dim {
642                let control_bit = (i >> control_idx) & 1;
643                let target_bit = (i >> target_idx) & 1;
644
645                // Calculate the four basis states in the 2-qubit subspace
646                let i00 = i & !(1 << control_idx) & !(1 << target_idx);
647                let i01 = i00 | (1 << target_idx);
648                let i10 = i00 | (1 << control_idx);
649                let i11 = i10 | (1 << target_idx);
650
651                let basis_idx = (control_bit << 1) | target_bit;
652
653                // Calculate the new amplitude for this state using direct access
654                let row_offset = basis_idx * 4;
655                new_state[i] = m[row_offset] * state[i00]
656                    + m[row_offset + 1] * state[i01]
657                    + m[row_offset + 2] * state[i10]
658                    + m[row_offset + 3] * state[i11];
659            }
660
661            state.copy_from_slice(&new_state);
662            // Return buffer to pool
663            self.buffer_pool
664                .lock()
665                .expect("buffer pool lock poisoned")
666                .return_buffer(new_state);
667        }
668
669        Ok(())
670    }
671
672    /// Apply CNOT gate efficiently (special case)
673    fn apply_cnot<const N: usize>(
674        &self,
675        state: &mut [Complex64],
676        control: QubitId,
677        target: QubitId,
678    ) -> QuantRS2Result<()> {
679        let control_idx = control.id() as usize;
680        let target_idx = target.id() as usize;
681
682        if control_idx >= N || target_idx >= N {
683            return Err(QuantRS2Error::InvalidQubitId(if control_idx >= N {
684                control.id()
685            } else {
686                target.id()
687            }));
688        }
689
690        if control_idx == target_idx {
691            return Err(QuantRS2Error::CircuitValidationFailed(
692                "Control and target qubits must be different".into(),
693            ));
694        }
695
696        // Apply the CNOT gate - only swap amplitudes where control is 1
697        if self.parallel {
698            let mut state_copy = self
699                .buffer_pool
700                .lock()
701                .expect("buffer pool lock poisoned")
702                .get_buffer(state.len());
703            state_copy.copy_from_slice(state);
704
705            state.par_iter_mut().enumerate().for_each(|(i, amp)| {
706                if (i >> control_idx) & 1 == 1 {
707                    let flipped = flip_bit(i, target_idx);
708                    *amp = state_copy[flipped];
709                }
710            });
711
712            self.buffer_pool
713                .lock()
714                .expect("buffer pool lock poisoned")
715                .return_buffer(state_copy);
716        } else {
717            let dim = state.len();
718            let mut new_state = self
719                .buffer_pool
720                .lock()
721                .expect("buffer pool lock poisoned")
722                .get_buffer(dim);
723
724            for i in 0..dim {
725                if (i >> control_idx) & 1 == 1 {
726                    let flipped = flip_bit(i, target_idx);
727                    new_state[flipped] = state[i];
728                    new_state[i] = state[flipped];
729                } else {
730                    new_state[i] = state[i];
731                }
732            }
733
734            state.copy_from_slice(&new_state);
735            self.buffer_pool
736                .lock()
737                .expect("buffer pool lock poisoned")
738                .return_buffer(new_state);
739        }
740
741        Ok(())
742    }
743
744    /// Apply SWAP gate efficiently (special case)
745    fn apply_swap<const N: usize>(
746        &self,
747        state: &mut [Complex64],
748        qubit1: QubitId,
749        qubit2: QubitId,
750    ) -> QuantRS2Result<()> {
751        let q1_idx = qubit1.id() as usize;
752        let q2_idx = qubit2.id() as usize;
753
754        if q1_idx >= N || q2_idx >= N {
755            return Err(QuantRS2Error::InvalidQubitId(if q1_idx >= N {
756                qubit1.id()
757            } else {
758                qubit2.id()
759            }));
760        }
761
762        if q1_idx == q2_idx {
763            return Err(QuantRS2Error::CircuitValidationFailed(
764                "Qubits must be different for SWAP gate".into(),
765            ));
766        }
767
768        // Apply the SWAP gate - swap amplitudes where qubits have different values
769        if self.parallel {
770            let mut state_copy = self
771                .buffer_pool
772                .lock()
773                .expect("buffer pool lock poisoned")
774                .get_buffer(state.len());
775            state_copy.copy_from_slice(state);
776
777            state.par_iter_mut().enumerate().for_each(|(i, amp)| {
778                let bit1 = (i >> q1_idx) & 1;
779                let bit2 = (i >> q2_idx) & 1;
780
781                if bit1 != bit2 {
782                    let swapped = flip_bit(flip_bit(i, q1_idx), q2_idx);
783                    *amp = state_copy[swapped];
784                }
785            });
786
787            self.buffer_pool
788                .lock()
789                .expect("buffer pool lock poisoned")
790                .return_buffer(state_copy);
791        } else {
792            let dim = state.len();
793            let mut new_state = self
794                .buffer_pool
795                .lock()
796                .expect("buffer pool lock poisoned")
797                .get_buffer(dim);
798
799            for i in 0..dim {
800                let bit1 = (i >> q1_idx) & 1;
801                let bit2 = (i >> q2_idx) & 1;
802
803                if bit1 == bit2 {
804                    new_state[i] = state[i];
805                } else {
806                    let swapped = flip_bit(flip_bit(i, q1_idx), q2_idx);
807                    new_state[swapped] = state[i];
808                    new_state[i] = state[swapped];
809                }
810            }
811
812            state.copy_from_slice(&new_state);
813            self.buffer_pool
814                .lock()
815                .expect("buffer pool lock poisoned")
816                .return_buffer(new_state);
817        }
818
819        Ok(())
820    }
821}
822
823impl Default for StateVectorSimulator {
824    fn default() -> Self {
825        Self::new()
826    }
827}
828
829impl<const N: usize> Simulator<N> for StateVectorSimulator {
830    fn run(&self, circuit: &Circuit<N>) -> QuantRS2Result<Register<N>> {
831        // Initialize state vector to |0...0⟩
832        let dim = 1 << N;
833        let mut state = vec![Complex64::new(0.0, 0.0); dim];
834        state[0] = Complex64::new(1.0, 0.0);
835
836        // Apply each gate in the circuit
837        for gate in circuit.gates() {
838            match gate.name() {
839                // Single-qubit gates
840                "H" => {
841                    if let Some(g) = gate.as_any().downcast_ref::<single::Hadamard>() {
842                        let matrix = g.matrix()?;
843                        self.apply_single_qubit_gate::<N>(&mut state, &matrix, g.target)?;
844                    }
845                }
846                "X" => {
847                    if let Some(g) = gate.as_any().downcast_ref::<single::PauliX>() {
848                        let matrix = g.matrix()?;
849                        self.apply_single_qubit_gate::<N>(&mut state, &matrix, g.target)?;
850                    }
851                }
852                "Y" => {
853                    if let Some(g) = gate.as_any().downcast_ref::<single::PauliY>() {
854                        let matrix = g.matrix()?;
855                        self.apply_single_qubit_gate::<N>(&mut state, &matrix, g.target)?;
856                    }
857                }
858                "Z" => {
859                    if let Some(g) = gate.as_any().downcast_ref::<single::PauliZ>() {
860                        let matrix = g.matrix()?;
861                        self.apply_single_qubit_gate::<N>(&mut state, &matrix, g.target)?;
862                    }
863                }
864                "RX" => {
865                    if let Some(g) = gate.as_any().downcast_ref::<single::RotationX>() {
866                        let matrix = g.matrix()?;
867                        self.apply_single_qubit_gate::<N>(&mut state, &matrix, g.target)?;
868                    }
869                }
870                "RY" => {
871                    if let Some(g) = gate.as_any().downcast_ref::<single::RotationY>() {
872                        let matrix = g.matrix()?;
873                        self.apply_single_qubit_gate::<N>(&mut state, &matrix, g.target)?;
874                    }
875                }
876                "RZ" => {
877                    if let Some(g) = gate.as_any().downcast_ref::<single::RotationZ>() {
878                        let matrix = g.matrix()?;
879                        self.apply_single_qubit_gate::<N>(&mut state, &matrix, g.target)?;
880                    }
881                }
882                "S" => {
883                    if let Some(g) = gate.as_any().downcast_ref::<single::Phase>() {
884                        let matrix = g.matrix()?;
885                        self.apply_single_qubit_gate::<N>(&mut state, &matrix, g.target)?;
886                    }
887                }
888                "T" => {
889                    if let Some(g) = gate.as_any().downcast_ref::<single::T>() {
890                        let matrix = g.matrix()?;
891                        self.apply_single_qubit_gate::<N>(&mut state, &matrix, g.target)?;
892                    }
893                }
894                "S†" => {
895                    if let Some(g) = gate.as_any().downcast_ref::<single::PhaseDagger>() {
896                        let matrix = g.matrix()?;
897                        self.apply_single_qubit_gate::<N>(&mut state, &matrix, g.target)?;
898                    }
899                }
900                "T†" => {
901                    if let Some(g) = gate.as_any().downcast_ref::<single::TDagger>() {
902                        let matrix = g.matrix()?;
903                        self.apply_single_qubit_gate::<N>(&mut state, &matrix, g.target)?;
904                    }
905                }
906                "√X" => {
907                    if let Some(g) = gate.as_any().downcast_ref::<single::SqrtX>() {
908                        let matrix = g.matrix()?;
909                        self.apply_single_qubit_gate::<N>(&mut state, &matrix, g.target)?;
910                    }
911                }
912                "√X†" => {
913                    if let Some(g) = gate.as_any().downcast_ref::<single::SqrtXDagger>() {
914                        let matrix = g.matrix()?;
915                        self.apply_single_qubit_gate::<N>(&mut state, &matrix, g.target)?;
916                    }
917                }
918
919                // Two-qubit gates
920                "CNOT" => {
921                    if let Some(g) = gate.as_any().downcast_ref::<multi::CNOT>() {
922                        // Use optimized implementation for CNOT
923                        self.apply_cnot::<N>(&mut state, g.control, g.target)?;
924                    }
925                }
926                "CZ" => {
927                    if let Some(g) = gate.as_any().downcast_ref::<multi::CZ>() {
928                        let matrix = g.matrix()?;
929                        self.apply_two_qubit_gate::<N>(&mut state, &matrix, g.control, g.target)?;
930                    }
931                }
932                "SWAP" => {
933                    if let Some(g) = gate.as_any().downcast_ref::<multi::SWAP>() {
934                        // Use optimized implementation for SWAP
935                        self.apply_swap::<N>(&mut state, g.qubit1, g.qubit2)?;
936                    }
937                }
938                "CY" => {
939                    if let Some(g) = gate.as_any().downcast_ref::<multi::CY>() {
940                        let matrix = g.matrix()?;
941                        self.apply_two_qubit_gate::<N>(&mut state, &matrix, g.control, g.target)?;
942                    }
943                }
944                "CH" => {
945                    if let Some(g) = gate.as_any().downcast_ref::<multi::CH>() {
946                        let matrix = g.matrix()?;
947                        self.apply_two_qubit_gate::<N>(&mut state, &matrix, g.control, g.target)?;
948                    }
949                }
950                "CS" => {
951                    if let Some(g) = gate.as_any().downcast_ref::<multi::CS>() {
952                        let matrix = g.matrix()?;
953                        self.apply_two_qubit_gate::<N>(&mut state, &matrix, g.control, g.target)?;
954                    }
955                }
956                "CRX" => {
957                    if let Some(g) = gate.as_any().downcast_ref::<multi::CRX>() {
958                        let matrix = g.matrix()?;
959                        self.apply_two_qubit_gate::<N>(&mut state, &matrix, g.control, g.target)?;
960                    }
961                }
962                "CRY" => {
963                    if let Some(g) = gate.as_any().downcast_ref::<multi::CRY>() {
964                        let matrix = g.matrix()?;
965                        self.apply_two_qubit_gate::<N>(&mut state, &matrix, g.control, g.target)?;
966                    }
967                }
968                "CRZ" => {
969                    if let Some(g) = gate.as_any().downcast_ref::<multi::CRZ>() {
970                        let matrix = g.matrix()?;
971                        self.apply_two_qubit_gate::<N>(&mut state, &matrix, g.control, g.target)?;
972                    }
973                }
974
975                // Three-qubit gates
976                "Toffoli" => {
977                    if let Some(toffoli_gate) = gate.as_any().downcast_ref::<multi::Toffoli>() {
978                        let control1 = toffoli_gate.control1;
979                        let control2 = toffoli_gate.control2;
980                        let target = toffoli_gate.target;
981                        self.apply_toffoli_to_state::<N>(&mut state, control1, control2, target)?;
982                    }
983                }
984                "Fredkin" => {
985                    if let Some(fredkin_gate) = gate.as_any().downcast_ref::<multi::Fredkin>() {
986                        let control = fredkin_gate.control;
987                        let target1 = fredkin_gate.target1;
988                        let target2 = fredkin_gate.target2;
989                        self.apply_fredkin_to_state::<N>(&mut state, control, target1, target2)?;
990                    }
991                }
992
993                _ => {
994                    return Err(QuantRS2Error::UnsupportedOperation(format!(
995                        "Gate {} not supported",
996                        gate.name()
997                    )));
998                }
999            }
1000
1001            // Apply per-gate noise if configured
1002            if let Some(ref noise_model) = self.noise_model {
1003                if noise_model.per_gate {
1004                    noise_model.apply_to_statevector(&mut state)?;
1005                }
1006            }
1007
1008            // Apply per-gate advanced noise if configured
1009            if let Some(ref advanced_noise_model) = self.advanced_noise_model {
1010                if advanced_noise_model.per_gate {
1011                    advanced_noise_model.apply_to_statevector(&mut state)?;
1012                }
1013            }
1014        }
1015
1016        // Apply final noise if not per-gate
1017        if let Some(ref noise_model) = self.noise_model {
1018            if !noise_model.per_gate {
1019                noise_model.apply_to_statevector(&mut state)?;
1020            }
1021        }
1022
1023        // Apply final advanced noise if not per-gate
1024        if let Some(ref advanced_noise_model) = self.advanced_noise_model {
1025            if !advanced_noise_model.per_gate {
1026                advanced_noise_model.apply_to_statevector(&mut state)?;
1027            }
1028        }
1029
1030        // Create register from final state
1031        Register::<N>::with_amplitudes(state)
1032    }
1033}
1034
1035impl StateVectorSimulator {
1036    /// Number of qubits currently represented by [`Self::current_state`].
1037    ///
1038    /// Returns an error if the state has not been initialised yet.
1039    fn state_num_qubits(&self) -> QuantRS2Result<usize> {
1040        let len = self.current_state.len();
1041        if len == 0 || !len.is_power_of_two() {
1042            return Err(QuantRS2Error::InvalidInput(
1043                "Quantum state has not been initialised; call initialize_state first".to_string(),
1044            ));
1045        }
1046        Ok(len.trailing_zeros() as usize)
1047    }
1048
1049    /// Bounds-check a single qubit index against the current state size.
1050    fn check_qubit(&self, qubit: usize) -> QuantRS2Result<()> {
1051        let n = self.state_num_qubits()?;
1052        if qubit >= n {
1053            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
1054        }
1055        Ok(())
1056    }
1057
1058    /// Initialize state with specified number of qubits in |0...0⟩
1059    ///
1060    /// Allocates a fresh `2^num_qubits` amplitude vector set to the
1061    /// computational-basis state |0...0⟩ and stores it in [`Self::current_state`].
1062    pub fn initialize_state(&mut self, num_qubits: usize) -> QuantRS2Result<()> {
1063        if num_qubits == 0 {
1064            return Err(QuantRS2Error::InvalidInput(
1065                "Number of qubits must be at least 1".to_string(),
1066            ));
1067        }
1068        if num_qubits >= usize::BITS as usize {
1069            return Err(QuantRS2Error::InvalidInput(format!(
1070                "Number of qubits {num_qubits} is too large to address"
1071            )));
1072        }
1073        let dim = 1usize << num_qubits;
1074        let mut state = vec![Complex64::new(0.0, 0.0); dim];
1075        state[0] = Complex64::new(1.0, 0.0);
1076        self.current_state = state;
1077        Ok(())
1078    }
1079
1080    /// Get a copy of the current quantum state amplitudes.
1081    pub fn get_state(&self) -> Vec<Complex64> {
1082        self.current_state.clone()
1083    }
1084
1085    /// Get a copy of the current quantum state amplitudes.
1086    ///
1087    /// Historically named `get_state_mut`; it returns an owned copy that the
1088    /// caller mutates and writes back via [`Self::set_state`].
1089    pub fn get_state_mut(&mut self) -> Vec<Complex64> {
1090        self.current_state.clone()
1091    }
1092
1093    /// Set the quantum state.
1094    ///
1095    /// The supplied vector must be non-empty with a power-of-two length so it
1096    /// corresponds to an integer number of qubits.
1097    pub fn set_state(&mut self, state: Vec<Complex64>) -> QuantRS2Result<()> {
1098        if state.is_empty() || !state.len().is_power_of_two() {
1099            return Err(QuantRS2Error::InvalidInput(
1100                "State vector length must be a non-zero power of two".to_string(),
1101            ));
1102        }
1103        self.current_state = state;
1104        Ok(())
1105    }
1106
1107    /// Apply an interface circuit to the current quantum state.
1108    ///
1109    /// If the state has not been initialised (or its size does not match the
1110    /// circuit), it is (re)initialised to |0...0⟩ over `circuit.num_qubits`
1111    /// qubits before the gates are applied.
1112    pub fn apply_interface_circuit(
1113        &mut self,
1114        circuit: &crate::circuit_interfaces::InterfaceCircuit,
1115    ) -> QuantRS2Result<()> {
1116        let expected_dim = 1usize << circuit.num_qubits;
1117        if self.current_state.len() != expected_dim {
1118            self.initialize_state(circuit.num_qubits)?;
1119        }
1120        let mut state = std::mem::take(&mut self.current_state);
1121        let result = (|| {
1122            for gate in &circuit.gates {
1123                apply_interface_gate_to_state(&mut state, gate, circuit.num_qubits)?;
1124            }
1125            Ok(())
1126        })();
1127        self.current_state = state;
1128        result
1129    }
1130
1131    /// Apply Hadamard gate to qubit
1132    pub fn apply_h(&mut self, qubit: usize) -> QuantRS2Result<()> {
1133        self.check_qubit(qubit)?;
1134        let inv_sqrt2 = 1.0 / std::f64::consts::SQRT_2;
1135        let matrix = [
1136            Complex64::new(inv_sqrt2, 0.0),
1137            Complex64::new(inv_sqrt2, 0.0),
1138            Complex64::new(inv_sqrt2, 0.0),
1139            Complex64::new(-inv_sqrt2, 0.0),
1140        ];
1141        apply_single_qubit_inplace(&mut self.current_state, &matrix, qubit);
1142        Ok(())
1143    }
1144
1145    /// Apply Pauli-X gate to qubit
1146    pub fn apply_x(&mut self, qubit: usize) -> QuantRS2Result<()> {
1147        self.check_qubit(qubit)?;
1148        let matrix = [
1149            Complex64::new(0.0, 0.0),
1150            Complex64::new(1.0, 0.0),
1151            Complex64::new(1.0, 0.0),
1152            Complex64::new(0.0, 0.0),
1153        ];
1154        apply_single_qubit_inplace(&mut self.current_state, &matrix, qubit);
1155        Ok(())
1156    }
1157
1158    /// Apply Pauli-Z gate to qubit
1159    pub fn apply_z_public(&mut self, qubit: usize) -> QuantRS2Result<()> {
1160        self.check_qubit(qubit)?;
1161        let matrix = [
1162            Complex64::new(1.0, 0.0),
1163            Complex64::new(0.0, 0.0),
1164            Complex64::new(0.0, 0.0),
1165            Complex64::new(-1.0, 0.0),
1166        ];
1167        apply_single_qubit_inplace(&mut self.current_state, &matrix, qubit);
1168        Ok(())
1169    }
1170
1171    /// Apply CNOT gate (public interface with usize indices)
1172    pub fn apply_cnot_public(&mut self, control: usize, target: usize) -> QuantRS2Result<()> {
1173        self.check_qubit(control)?;
1174        self.check_qubit(target)?;
1175        if control == target {
1176            return Err(QuantRS2Error::InvalidInput(
1177                "CNOT control and target must differ".to_string(),
1178            ));
1179        }
1180        let control_mask = 1usize << control;
1181        let target_mask = 1usize << target;
1182        for i in 0..self.current_state.len() {
1183            if (i & control_mask) != 0 && (i & target_mask) == 0 {
1184                self.current_state.swap(i, i | target_mask);
1185            }
1186        }
1187        Ok(())
1188    }
1189
1190    /// Apply Toffoli (CCNOT) gate to state vector
1191    fn apply_toffoli_to_state<const N: usize>(
1192        &self,
1193        state: &mut [Complex64],
1194        control1: QubitId,
1195        control2: QubitId,
1196        target: QubitId,
1197    ) -> QuantRS2Result<()> {
1198        let control1_idx = control1.id() as usize;
1199        let control2_idx = control2.id() as usize;
1200        let target_idx = target.id() as usize;
1201
1202        if control1_idx >= N || control2_idx >= N || target_idx >= N {
1203            return Err(QuantRS2Error::InvalidQubitId(if control1_idx >= N {
1204                control1.id()
1205            } else if control2_idx >= N {
1206                control2.id()
1207            } else {
1208                target.id()
1209            }));
1210        }
1211
1212        // Apply Toffoli gate by swapping amplitudes when both controls are |1⟩
1213        if self.parallel {
1214            let mut state_copy = self
1215                .buffer_pool
1216                .lock()
1217                .expect("buffer pool lock poisoned")
1218                .get_buffer(state.len());
1219            state_copy.copy_from_slice(state);
1220
1221            state.par_iter_mut().enumerate().for_each(|(i, amp)| {
1222                let control1_bit = (i >> control1_idx) & 1;
1223                let control2_bit = (i >> control2_idx) & 1;
1224
1225                if control1_bit == 1 && control2_bit == 1 {
1226                    let flipped = flip_bit(i, target_idx);
1227                    *amp = state_copy[flipped];
1228                }
1229            });
1230
1231            self.buffer_pool
1232                .lock()
1233                .expect("buffer pool lock poisoned")
1234                .return_buffer(state_copy);
1235        } else {
1236            let mut new_state = self
1237                .buffer_pool
1238                .lock()
1239                .expect("buffer pool lock poisoned")
1240                .get_buffer(state.len());
1241            new_state.copy_from_slice(state);
1242
1243            for i in 0..state.len() {
1244                let control1_bit = (i >> control1_idx) & 1;
1245                let control2_bit = (i >> control2_idx) & 1;
1246
1247                if control1_bit == 1 && control2_bit == 1 {
1248                    let flipped = flip_bit(i, target_idx);
1249                    new_state[flipped] = state[i];
1250                    new_state[i] = state[flipped];
1251                }
1252            }
1253
1254            state.copy_from_slice(&new_state);
1255            self.buffer_pool
1256                .lock()
1257                .expect("buffer pool lock poisoned")
1258                .return_buffer(new_state);
1259        }
1260
1261        Ok(())
1262    }
1263
1264    /// Apply Fredkin (CSWAP) gate to state vector
1265    fn apply_fredkin_to_state<const N: usize>(
1266        &self,
1267        state: &mut [Complex64],
1268        control: QubitId,
1269        target1: QubitId,
1270        target2: QubitId,
1271    ) -> QuantRS2Result<()> {
1272        let control_idx = control.id() as usize;
1273        let target1_idx = target1.id() as usize;
1274        let target2_idx = target2.id() as usize;
1275
1276        if control_idx >= N || target1_idx >= N || target2_idx >= N {
1277            return Err(QuantRS2Error::InvalidQubitId(if control_idx >= N {
1278                control.id()
1279            } else if target1_idx >= N {
1280                target1.id()
1281            } else {
1282                target2.id()
1283            }));
1284        }
1285
1286        // Apply Fredkin gate by swapping target qubits when control is |1⟩
1287        if self.parallel {
1288            let mut state_copy = self
1289                .buffer_pool
1290                .lock()
1291                .expect("buffer pool lock poisoned")
1292                .get_buffer(state.len());
1293            state_copy.copy_from_slice(state);
1294
1295            state.par_iter_mut().enumerate().for_each(|(i, amp)| {
1296                let control_bit = (i >> control_idx) & 1;
1297                let target1_bit = (i >> target1_idx) & 1;
1298                let target2_bit = (i >> target2_idx) & 1;
1299
1300                if control_bit == 1 && target1_bit != target2_bit {
1301                    let swapped = flip_bit(flip_bit(i, target1_idx), target2_idx);
1302                    *amp = state_copy[swapped];
1303                }
1304            });
1305
1306            self.buffer_pool
1307                .lock()
1308                .expect("buffer pool lock poisoned")
1309                .return_buffer(state_copy);
1310        } else {
1311            let mut new_state = self
1312                .buffer_pool
1313                .lock()
1314                .expect("buffer pool lock poisoned")
1315                .get_buffer(state.len());
1316            new_state.copy_from_slice(state);
1317
1318            for i in 0..state.len() {
1319                let control_bit = (i >> control_idx) & 1;
1320                let target1_bit = (i >> target1_idx) & 1;
1321                let target2_bit = (i >> target2_idx) & 1;
1322
1323                if control_bit == 1 && target1_bit != target2_bit {
1324                    let swapped = flip_bit(flip_bit(i, target1_idx), target2_idx);
1325                    new_state[swapped] = state[i];
1326                    new_state[i] = state[swapped];
1327                }
1328            }
1329
1330            state.copy_from_slice(&new_state);
1331            self.buffer_pool
1332                .lock()
1333                .expect("buffer pool lock poisoned")
1334                .return_buffer(new_state);
1335        }
1336
1337        Ok(())
1338    }
1339
1340    /// Apply Toffoli (CCNOT) gate to the current state (public interface).
1341    ///
1342    /// Flips `target` whenever both `control1` and `control2` are |1⟩.
1343    pub fn apply_toffoli(
1344        &mut self,
1345        control1: QubitId,
1346        control2: QubitId,
1347        target: QubitId,
1348    ) -> QuantRS2Result<()> {
1349        let c1 = control1.id() as usize;
1350        let c2 = control2.id() as usize;
1351        let t = target.id() as usize;
1352        self.check_qubit(c1)?;
1353        self.check_qubit(c2)?;
1354        self.check_qubit(t)?;
1355        if c1 == c2 || c1 == t || c2 == t {
1356            return Err(QuantRS2Error::InvalidInput(
1357                "Toffoli controls and target must be distinct".to_string(),
1358            ));
1359        }
1360        let c1_mask = 1usize << c1;
1361        let c2_mask = 1usize << c2;
1362        let t_mask = 1usize << t;
1363        for i in 0..self.current_state.len() {
1364            if (i & c1_mask) != 0 && (i & c2_mask) != 0 && (i & t_mask) == 0 {
1365                self.current_state.swap(i, i | t_mask);
1366            }
1367        }
1368        Ok(())
1369    }
1370
1371    /// Apply Fredkin (CSWAP) gate to the current state (public interface).
1372    ///
1373    /// Swaps `target1` and `target2` whenever `control` is |1⟩.
1374    pub fn apply_fredkin(
1375        &mut self,
1376        control: QubitId,
1377        target1: QubitId,
1378        target2: QubitId,
1379    ) -> QuantRS2Result<()> {
1380        let c = control.id() as usize;
1381        let t1 = target1.id() as usize;
1382        let t2 = target2.id() as usize;
1383        self.check_qubit(c)?;
1384        self.check_qubit(t1)?;
1385        self.check_qubit(t2)?;
1386        if c == t1 || c == t2 || t1 == t2 {
1387            return Err(QuantRS2Error::InvalidInput(
1388                "Fredkin control and targets must be distinct".to_string(),
1389            ));
1390        }
1391        let c_mask = 1usize << c;
1392        let t1_mask = 1usize << t1;
1393        let t2_mask = 1usize << t2;
1394        for i in 0..self.current_state.len() {
1395            // Swap the pair (t1=0, t2=1) with (t1=1, t2=0) once, only when the
1396            // control bit is set.
1397            if (i & c_mask) != 0 && (i & t1_mask) == 0 && (i & t2_mask) != 0 {
1398                let partner = (i | t1_mask) & !t2_mask;
1399                self.current_state.swap(i, partner);
1400            }
1401        }
1402        Ok(())
1403    }
1404}
1405
1406/// Apply a 2×2 single-qubit gate to `state` in place.
1407///
1408/// The matrix is row-major `[m00, m01, m10, m11]` acting on the amplitude
1409/// pair whose `target` bit is 0 (row 0) and 1 (row 1).
1410fn apply_single_qubit_inplace(state: &mut [Complex64], matrix: &[Complex64; 4], target: usize) {
1411    let target_mask = 1usize << target;
1412    for i in 0..state.len() {
1413        if (i & target_mask) == 0 {
1414            let j = i | target_mask;
1415            let a = state[i];
1416            let b = state[j];
1417            state[i] = matrix[0] * a + matrix[1] * b;
1418            state[j] = matrix[2] * a + matrix[3] * b;
1419        }
1420    }
1421}
1422
1423/// Apply a `k`-qubit unitary `matrix` (dimension `2^k`, row-major) to `state`
1424/// over the given `qubits`, following the convention that `qubits[0]` is the
1425/// most-significant index of the gate matrix (matching the crate's existing
1426/// two-qubit gate ordering).
1427fn apply_multi_qubit_inplace(
1428    state: &mut [Complex64],
1429    matrix: &scirs2_core::ndarray::Array2<Complex64>,
1430    qubits: &[usize],
1431) -> QuantRS2Result<()> {
1432    let k = qubits.len();
1433    let gate_dim = 1usize << k;
1434    if matrix.nrows() != gate_dim || matrix.ncols() != gate_dim {
1435        return Err(QuantRS2Error::InvalidInput(format!(
1436            "Gate matrix dimension {}x{} does not match {} qubit(s)",
1437            matrix.nrows(),
1438            matrix.ncols(),
1439            k
1440        )));
1441    }
1442    let mut combined_mask = 0usize;
1443    for &q in qubits {
1444        combined_mask |= 1usize << q;
1445    }
1446
1447    let mut indices = vec![0usize; gate_dim];
1448    let mut inputs = vec![Complex64::new(0.0, 0.0); gate_dim];
1449    for base in 0..state.len() {
1450        // Process each group of basis states (those sharing all non-target
1451        // bits) exactly once, keyed on the representative with all target
1452        // bits cleared.
1453        if base & combined_mask != 0 {
1454            continue;
1455        }
1456        for (g, (idx_slot, in_slot)) in indices.iter_mut().zip(inputs.iter_mut()).enumerate() {
1457            let mut idx = base;
1458            for (b, &q) in qubits.iter().enumerate() {
1459                if (g >> (k - 1 - b)) & 1 == 1 {
1460                    idx |= 1usize << q;
1461                }
1462            }
1463            *idx_slot = idx;
1464            *in_slot = state[idx];
1465        }
1466        for row in 0..gate_dim {
1467            let mut acc = Complex64::new(0.0, 0.0);
1468            for (col, &inp) in inputs.iter().enumerate() {
1469                acc += matrix[[row, col]] * inp;
1470            }
1471            state[indices[row]] = acc;
1472        }
1473    }
1474    Ok(())
1475}
1476
1477/// Apply a single [`InterfaceGate`](crate::circuit_interfaces::InterfaceGate)
1478/// to `state`.
1479///
1480/// Common single-qubit aliases (`X`, `H`) that the interface gate's own
1481/// `unitary_matrix` does not resolve are handled here directly; every other
1482/// unitary gate type is applied via its `unitary_matrix`. Genuinely
1483/// non-unitary operations (measurement / reset) return an honest error rather
1484/// than silently doing nothing.
1485fn apply_interface_gate_to_state(
1486    state: &mut [Complex64],
1487    gate: &crate::circuit_interfaces::InterfaceGate,
1488    num_qubits: usize,
1489) -> QuantRS2Result<()> {
1490    use crate::circuit_interfaces::InterfaceGateType;
1491
1492    for &q in &gate.qubits {
1493        if q >= num_qubits {
1494            return Err(QuantRS2Error::InvalidQubitId(q as u32));
1495        }
1496    }
1497
1498    let inv_sqrt2 = 1.0 / std::f64::consts::SQRT_2;
1499    match &gate.gate_type {
1500        InterfaceGateType::Identity => Ok(()),
1501        InterfaceGateType::PauliX | InterfaceGateType::X => {
1502            let m = [
1503                Complex64::new(0.0, 0.0),
1504                Complex64::new(1.0, 0.0),
1505                Complex64::new(1.0, 0.0),
1506                Complex64::new(0.0, 0.0),
1507            ];
1508            apply_single_qubit_inplace(state, &m, gate.qubits[0]);
1509            Ok(())
1510        }
1511        InterfaceGateType::Hadamard | InterfaceGateType::H => {
1512            let m = [
1513                Complex64::new(inv_sqrt2, 0.0),
1514                Complex64::new(inv_sqrt2, 0.0),
1515                Complex64::new(inv_sqrt2, 0.0),
1516                Complex64::new(-inv_sqrt2, 0.0),
1517            ];
1518            apply_single_qubit_inplace(state, &m, gate.qubits[0]);
1519            Ok(())
1520        }
1521        InterfaceGateType::Measure | InterfaceGateType::Reset => {
1522            Err(QuantRS2Error::UnsupportedOperation(format!(
1523                "State-vector apply_interface_circuit does not support non-unitary '{:?}'",
1524                gate.gate_type
1525            )))
1526        }
1527        _ => {
1528            let matrix = gate
1529                .unitary_matrix()
1530                .map_err(|e| QuantRS2Error::InvalidInput(e.to_string()))?;
1531            if gate.qubits.len() == 1 {
1532                let flat = [
1533                    matrix[[0, 0]],
1534                    matrix[[0, 1]],
1535                    matrix[[1, 0]],
1536                    matrix[[1, 1]],
1537                ];
1538                apply_single_qubit_inplace(state, &flat, gate.qubits[0]);
1539                Ok(())
1540            } else {
1541                apply_multi_qubit_inplace(state, &matrix, &gate.qubits)
1542            }
1543        }
1544    }
1545}