Skip to main content

quantrs2_sim/
large_scale_simulator.rs

1//! Large-Scale Quantum Simulator with Advanced Memory Optimization
2//!
3//! This module provides state-of-the-art memory optimization techniques to enable
4//! simulation of 40+ qubit quantum circuits on standard hardware through sparse
5//! representations, compression, memory mapping, and `SciRS2` high-performance computing.
6
7use quantrs2_circuit::builder::{Circuit, Simulator};
8use quantrs2_core::{
9    buffer_pool::BufferPool,
10    error::{QuantRS2Error, QuantRS2Result},
11    gate::GateOp,
12    qubit::QubitId,
13};
14use scirs2_core::parallel_ops::{IndexedParallelIterator, ParallelIterator}; // SciRS2 POLICY compliant
15                                                                            // use quantrs2_core::platform::PlatformCapabilities;
16                                                                            // use scirs2_core::memory::BufferPool as SciRS2BufferPool;
17                                                                            // use scirs2_optimize::compression::{CompressionEngine, HuffmanEncoder, LZ4Encoder};
18                                                                            // use scirs2_linalg::{Matrix, Vector, SVD, sparse::CSRMatrix};
19                                                                            // flate2 replaced by oxiarc-deflate (COOLJAPAN Pure Rust Policy)
20use memmap2::{MmapMut, MmapOptions};
21use scirs2_core::ndarray::{Array1, Array2, Array3, ArrayView1, ArrayView2};
22use scirs2_core::Complex64;
23use serde::{Deserialize, Serialize};
24use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
25use std::fmt;
26use std::fs::{File, OpenOptions};
27use std::io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write};
28use std::path::{Path, PathBuf};
29use std::sync::{Arc, Mutex, RwLock};
30use uuid::Uuid;
31
32/// Large-scale simulator configuration for 40+ qubit systems
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct LargeScaleSimulatorConfig {
35    /// Maximum number of qubits to simulate
36    pub max_qubits: usize,
37
38    /// Enable sparse state vector representation
39    pub enable_sparse_representation: bool,
40
41    /// Enable state compression
42    pub enable_compression: bool,
43
44    /// Enable memory mapping for very large states
45    pub enable_memory_mapping: bool,
46
47    /// Enable chunked processing
48    pub enable_chunked_processing: bool,
49
50    /// Chunk size for processing (in complex numbers)
51    pub chunk_size: usize,
52
53    /// Sparsity threshold (fraction of non-zero elements)
54    pub sparsity_threshold: f64,
55
56    /// Compression threshold (minimum size to compress)
57    pub compression_threshold: usize,
58
59    /// Memory mapping threshold (minimum size to use mmap)
60    pub memory_mapping_threshold: usize,
61
62    /// Working directory for temporary files
63    pub working_directory: PathBuf,
64
65    /// Enable `SciRS2` optimizations
66    pub enable_scirs2_optimizations: bool,
67
68    /// Memory budget in bytes
69    pub memory_budget: usize,
70
71    /// Enable adaptive precision
72    pub enable_adaptive_precision: bool,
73
74    /// Precision tolerance for adaptive precision
75    pub precision_tolerance: f64,
76}
77
78impl Default for LargeScaleSimulatorConfig {
79    fn default() -> Self {
80        Self {
81            max_qubits: 50,
82            enable_sparse_representation: true,
83            enable_compression: true,
84            enable_memory_mapping: true,
85            enable_chunked_processing: true,
86            chunk_size: 1024 * 1024,                    // 1M complex numbers
87            sparsity_threshold: 0.1,                    // Sparse if < 10% non-zero
88            compression_threshold: 1024 * 1024 * 8,     // 8MB
89            memory_mapping_threshold: 1024 * 1024 * 64, // 64MB
90            working_directory: std::env::temp_dir().join("quantrs_large_scale"),
91            enable_scirs2_optimizations: true,
92            memory_budget: 8 * 1024 * 1024 * 1024, // 8GB
93            enable_adaptive_precision: true,
94            precision_tolerance: 1e-12,
95        }
96    }
97}
98
99/// Simple sparse matrix representation for quantum states
100#[derive(Debug, Clone)]
101pub struct SimpleSparseMatrix {
102    /// Non-zero values
103    values: Vec<Complex64>,
104    /// Column indices for non-zero values
105    col_indices: Vec<usize>,
106    /// Row pointers (start index for each row)
107    row_ptr: Vec<usize>,
108    /// Matrix dimensions
109    rows: usize,
110    cols: usize,
111}
112
113impl SimpleSparseMatrix {
114    #[must_use]
115    pub fn from_dense(dense: &[Complex64], threshold: f64) -> Self {
116        let rows = dense.len();
117        let cols = 1; // For state vectors
118        let mut values = Vec::new();
119        let mut col_indices = Vec::new();
120        let mut row_ptr = vec![0];
121
122        for (i, &val) in dense.iter().enumerate() {
123            if val.norm() > threshold {
124                values.push(val);
125                col_indices.push(0); // State vector is column vector
126            }
127            row_ptr.push(values.len());
128        }
129
130        Self {
131            values,
132            col_indices,
133            row_ptr,
134            rows,
135            cols,
136        }
137    }
138
139    #[must_use]
140    pub fn to_dense(&self) -> Vec<Complex64> {
141        let mut dense = vec![Complex64::new(0.0, 0.0); self.rows];
142
143        // Reconstruct each basis-state amplitude from the CSR row pointers.
144        // The value for basis state `row` lives at `values[row_ptr[row]]` when
145        // that row has a stored (non-zero) entry. Iterating `values` directly
146        // and using the enumeration counter as the row index is WRONG, because
147        // `values` is compacted to non-zero entries only, so it would place the
148        // k-th non-zero amplitude at basis state k instead of its real index.
149        for row in 0..self.rows {
150            if row + 1 < self.row_ptr.len() {
151                let start = self.row_ptr[row];
152                let end = self.row_ptr[row + 1];
153                if start < end && start < self.values.len() {
154                    dense[row] = self.values[start];
155                }
156            }
157        }
158
159        dense
160    }
161
162    #[must_use]
163    pub fn nnz(&self) -> usize {
164        self.values.len()
165    }
166
167    /// Get amplitude at a specific row (basis state index)
168    #[must_use]
169    pub fn get_amplitude(&self, row: usize) -> Complex64 {
170        if row >= self.rows || row + 1 >= self.row_ptr.len() {
171            return Complex64::new(0.0, 0.0);
172        }
173        let start = self.row_ptr[row];
174        let end = self.row_ptr[row + 1];
175        if start < end && start < self.values.len() {
176            self.values[start]
177        } else {
178            Complex64::new(0.0, 0.0)
179        }
180    }
181
182    /// Build from a HashMap of non-zero amplitudes (sparse direct construction)
183    #[must_use]
184    pub fn from_sparse_map(
185        amplitudes: &HashMap<usize, Complex64>,
186        dimension: usize,
187        threshold: f64,
188    ) -> Self {
189        let rows = dimension;
190        let cols = 1;
191        let mut values = Vec::new();
192        let mut col_indices = Vec::new();
193        let mut row_ptr = vec![0usize; rows + 1];
194
195        // First pass: count non-zero entries per row
196        for (&idx, &val) in amplitudes {
197            if idx < rows && val.norm() > threshold {
198                row_ptr[idx + 1] = 1;
199            }
200        }
201
202        // Prefix sum to get actual row pointers
203        for i in 1..=rows {
204            row_ptr[i] += row_ptr[i - 1];
205        }
206
207        let nnz = row_ptr[rows];
208        values.resize(nnz, Complex64::new(0.0, 0.0));
209        col_indices.resize(nnz, 0usize);
210
211        // Second pass: fill values
212        let mut fill_pos = vec![0usize; rows];
213        fill_pos[..rows].copy_from_slice(&row_ptr[..rows]);
214
215        for (&idx, &val) in amplitudes {
216            if idx < rows && val.norm() > threshold {
217                let pos = fill_pos[idx];
218                values[pos] = val;
219                col_indices[pos] = 0;
220                fill_pos[idx] += 1;
221            }
222        }
223
224        Self {
225            values,
226            col_indices,
227            row_ptr,
228            rows,
229            cols,
230        }
231    }
232
233    /// Convert to a HashMap of non-zero amplitudes
234    #[must_use]
235    pub fn to_sparse_map(&self) -> HashMap<usize, Complex64> {
236        let mut map = HashMap::new();
237        for row in 0..self.rows {
238            if row + 1 < self.row_ptr.len() {
239                let start = self.row_ptr[row];
240                let end = self.row_ptr[row + 1];
241                if start < end && start < self.values.len() {
242                    let val = self.values[start];
243                    if val.norm() > 0.0 {
244                        map.insert(row, val);
245                    }
246                }
247            }
248        }
249        map
250    }
251}
252
253/// Sparse quantum state representation using simple sparse matrices
254#[derive(Debug)]
255pub struct SparseQuantumState {
256    /// Sparse representation of non-zero amplitudes
257    sparse_amplitudes: SimpleSparseMatrix,
258
259    /// Number of qubits
260    num_qubits: usize,
261
262    /// Total dimension (`2^num_qubits`)
263    dimension: usize,
264
265    /// Non-zero indices and their positions
266    nonzero_indices: HashMap<usize, usize>,
267
268    /// Sparsity ratio
269    sparsity_ratio: f64,
270}
271
272impl SparseQuantumState {
273    /// Create new sparse quantum state
274    pub fn new(num_qubits: usize) -> QuantRS2Result<Self> {
275        let dimension = 1usize << num_qubits;
276
277        // Initialize in |0...0⟩ state (single non-zero element)
278        let mut dense = vec![Complex64::new(0.0, 0.0); dimension];
279        dense[0] = Complex64::new(1.0, 0.0);
280
281        let sparse_amplitudes = SimpleSparseMatrix::from_dense(&dense, 1e-15);
282
283        let mut nonzero_indices = HashMap::new();
284        nonzero_indices.insert(0, 0);
285
286        Ok(Self {
287            sparse_amplitudes,
288            num_qubits,
289            dimension,
290            nonzero_indices,
291            sparsity_ratio: 1.0 / dimension as f64,
292        })
293    }
294
295    /// Convert from dense state vector
296    pub fn from_dense(amplitudes: &[Complex64], threshold: f64) -> QuantRS2Result<Self> {
297        let num_qubits = (amplitudes.len() as f64).log2() as usize;
298        let dimension = amplitudes.len();
299
300        // Find non-zero elements
301        let mut nonzero_indices = HashMap::new();
302        let mut nonzero_count = 0;
303
304        for (i, &amplitude) in amplitudes.iter().enumerate() {
305            if amplitude.norm() > threshold {
306                nonzero_indices.insert(i, nonzero_count);
307                nonzero_count += 1;
308            }
309        }
310
311        let sparse_amplitudes = SimpleSparseMatrix::from_dense(amplitudes, threshold);
312        let sparsity_ratio = nonzero_count as f64 / dimension as f64;
313
314        Ok(Self {
315            sparse_amplitudes,
316            num_qubits,
317            dimension,
318            nonzero_indices,
319            sparsity_ratio,
320        })
321    }
322
323    /// Convert to dense state vector
324    pub fn to_dense(&self) -> QuantRS2Result<Vec<Complex64>> {
325        Ok(self.sparse_amplitudes.to_dense())
326    }
327
328    /// Apply sparse gate operation using true sparse arithmetic.
329    ///
330    /// For single-qubit gates we iterate only over non-zero amplitude pairs
331    /// (basis states that differ only in the target qubit bit), computing the
332    /// two output amplitudes from the 2×2 gate matrix without ever constructing
333    /// the full dense state vector.
334    ///
335    /// For two-qubit gates we enumerate all four combinations of the two qubit
336    /// bits across non-zero amplitudes and compute the four output amplitudes.
337    ///
338    /// Amplitudes whose norm falls below 1e-12 are pruned to maintain sparsity.
339    pub fn apply_sparse_gate(&mut self, gate: &dyn GateOp) -> QuantRS2Result<()> {
340        let qubits = gate.qubits();
341        match qubits.len() {
342            1 => {
343                let target = qubits[0].id() as usize;
344                let matrix = gate.matrix()?;
345                self.apply_single_qubit_sparse(&matrix, target)?;
346            }
347            2 => {
348                let q0 = qubits[0].id() as usize;
349                let q1 = qubits[1].id() as usize;
350                let matrix = gate.matrix()?;
351                self.apply_two_qubit_sparse(&matrix, q0, q1)?;
352            }
353            _ => {
354                // Fallback to dense operation for 3+ qubit gates
355                let matrix = gate.matrix()?;
356                self.apply_dense_gate(&matrix, &qubits)?;
357            }
358        }
359
360        Ok(())
361    }
362
363    /// Apply a single-qubit gate in true sparse representation.
364    ///
365    /// For each pair of basis states (i0, i1) that differ only in `target` bit:
366    ///   new[i0] = m[0]*amp[i0] + m[1]*amp[i1]
367    ///   new[i1] = m[2]*amp[i0] + m[3]*amp[i1]
368    ///
369    /// We only process pairs where at least one amplitude is non-zero.
370    fn apply_single_qubit_sparse(
371        &mut self,
372        matrix: &[Complex64],
373        target: usize,
374    ) -> QuantRS2Result<()> {
375        if matrix.len() < 4 {
376            return Err(QuantRS2Error::InvalidInput(
377                "Single-qubit gate matrix must have 4 elements".to_string(),
378            ));
379        }
380        if target >= self.num_qubits {
381            return Err(QuantRS2Error::InvalidInput(format!(
382                "Target qubit {} out of range (num_qubits={})",
383                target, self.num_qubits
384            )));
385        }
386
387        const THRESHOLD: f64 = 1e-12;
388
389        // Collect current non-zero amplitudes into a HashMap for O(1) lookup
390        let current: HashMap<usize, Complex64> = self.sparse_amplitudes.to_sparse_map();
391
392        let target_mask = 1usize << target;
393        // We need to process each (i0, i1) pair exactly once.
394        // i0 has target bit = 0, i1 = i0 | target_mask
395        let mut visited: HashSet<usize> = HashSet::new();
396        let mut new_amplitudes: HashMap<usize, Complex64> = HashMap::new();
397
398        for &idx in current.keys() {
399            // Normalise to the i0 version (target bit = 0)
400            let i0 = idx & !target_mask;
401            if visited.contains(&i0) {
402                continue;
403            }
404            visited.insert(i0);
405            let i1 = i0 | target_mask;
406
407            let a0 = current
408                .get(&i0)
409                .copied()
410                .unwrap_or(Complex64::new(0.0, 0.0));
411            let a1 = current
412                .get(&i1)
413                .copied()
414                .unwrap_or(Complex64::new(0.0, 0.0));
415
416            let new0 = matrix[0] * a0 + matrix[1] * a1;
417            let new1 = matrix[2] * a0 + matrix[3] * a1;
418
419            if new0.norm() > THRESHOLD {
420                new_amplitudes.insert(i0, new0);
421            }
422            if new1.norm() > THRESHOLD {
423                new_amplitudes.insert(i1, new1);
424            }
425        }
426
427        // Rebuild sparse representation from new amplitudes
428        self.nonzero_indices.clear();
429        for (pos, (&idx, _)) in new_amplitudes.iter().enumerate() {
430            self.nonzero_indices.insert(idx, pos);
431        }
432        self.sparse_amplitudes =
433            SimpleSparseMatrix::from_sparse_map(&new_amplitudes, self.dimension, THRESHOLD);
434        self.sparsity_ratio = new_amplitudes.len() as f64 / self.dimension as f64;
435
436        Ok(())
437    }
438
439    /// Apply a two-qubit gate in true sparse representation.
440    ///
441    /// For each group of four basis states sharing all bits except q0/q1, we
442    /// read the four amplitudes (|00⟩, |01⟩, |10⟩, |11⟩), apply the 4×4
443    /// matrix, and write back only outputs above threshold.
444    fn apply_two_qubit_sparse(
445        &mut self,
446        matrix: &[Complex64],
447        q0: usize,
448        q1: usize,
449    ) -> QuantRS2Result<()> {
450        if matrix.len() < 16 {
451            return Err(QuantRS2Error::InvalidInput(
452                "Two-qubit gate matrix must have 16 elements".to_string(),
453            ));
454        }
455        if q0 >= self.num_qubits || q1 >= self.num_qubits {
456            return Err(QuantRS2Error::InvalidInput(format!(
457                "Qubit indices {},{} out of range (num_qubits={})",
458                q0, q1, self.num_qubits
459            )));
460        }
461
462        const THRESHOLD: f64 = 1e-12;
463
464        let current: HashMap<usize, Complex64> = self.sparse_amplitudes.to_sparse_map();
465        let mask0 = 1usize << q0;
466        let mask1 = 1usize << q1;
467        let both_mask = mask0 | mask1;
468
469        // Base index has both q0 and q1 bits = 0
470        let mut visited: HashSet<usize> = HashSet::new();
471        let mut new_amplitudes: HashMap<usize, Complex64> = HashMap::new();
472
473        for &idx in current.keys() {
474            let base = idx & !both_mask;
475            if visited.contains(&base) {
476                continue;
477            }
478            visited.insert(base);
479
480            // Four basis states: |b_q0=0, b_q1=0⟩, |01⟩, |10⟩, |11⟩
481            let i00 = base;
482            let i01 = base | mask1;
483            let i10 = base | mask0;
484            let i11 = base | both_mask;
485
486            let a00 = current
487                .get(&i00)
488                .copied()
489                .unwrap_or(Complex64::new(0.0, 0.0));
490            let a01 = current
491                .get(&i01)
492                .copied()
493                .unwrap_or(Complex64::new(0.0, 0.0));
494            let a10 = current
495                .get(&i10)
496                .copied()
497                .unwrap_or(Complex64::new(0.0, 0.0));
498            let a11 = current
499                .get(&i11)
500                .copied()
501                .unwrap_or(Complex64::new(0.0, 0.0));
502
503            let inputs = [a00, a01, a10, a11];
504            let indices = [i00, i01, i10, i11];
505
506            for (row, &out_idx) in indices.iter().enumerate() {
507                let mut new_val = Complex64::new(0.0, 0.0);
508                for (col, &inp) in inputs.iter().enumerate() {
509                    new_val += matrix[row * 4 + col] * inp;
510                }
511                if new_val.norm() > THRESHOLD {
512                    new_amplitudes.insert(out_idx, new_val);
513                }
514            }
515        }
516
517        self.nonzero_indices.clear();
518        for (pos, (&idx, _)) in new_amplitudes.iter().enumerate() {
519            self.nonzero_indices.insert(idx, pos);
520        }
521        self.sparse_amplitudes =
522            SimpleSparseMatrix::from_sparse_map(&new_amplitudes, self.dimension, THRESHOLD);
523        self.sparsity_ratio = new_amplitudes.len() as f64 / self.dimension as f64;
524
525        Ok(())
526    }
527
528    /// Apply Pauli-X gate efficiently in sparse representation
529    fn apply_pauli_x_sparse(&mut self, target: usize) -> QuantRS2Result<()> {
530        // Pauli-X just flips the target bit in the indices
531        let mut new_nonzero_indices = HashMap::new();
532        let target_mask = 1usize << target;
533
534        for (&old_idx, &pos) in &self.nonzero_indices {
535            let new_idx = old_idx ^ target_mask;
536            new_nonzero_indices.insert(new_idx, pos);
537        }
538
539        self.nonzero_indices = new_nonzero_indices;
540
541        // Update sparse matrix indices
542        self.update_sparse_matrix()?;
543
544        Ok(())
545    }
546
547    /// Apply Hadamard gate in sparse representation
548    fn apply_hadamard_sparse(&mut self, target: usize) -> QuantRS2Result<()> {
549        // Hadamard creates superposition, potentially doubling non-zero elements
550        // For true sparsity preservation, we'd need more sophisticated techniques
551        // For now, use dense conversion
552        let dense = self.to_dense()?;
553        let mut new_dense = vec![Complex64::new(0.0, 0.0); self.dimension];
554
555        let h_00 = Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0);
556        let h_01 = Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0);
557        let h_10 = Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0);
558        let h_11 = Complex64::new(-1.0 / 2.0_f64.sqrt(), 0.0);
559
560        let target_mask = 1usize << target;
561
562        for i in 0..self.dimension {
563            let paired_idx = i ^ target_mask;
564            let bit_val = (i >> target) & 1;
565
566            if bit_val == 0 {
567                new_dense[i] = h_00 * dense[i] + h_01 * dense[paired_idx];
568                new_dense[paired_idx] = h_10 * dense[i] + h_11 * dense[paired_idx];
569            }
570        }
571
572        *self = Self::from_dense(&new_dense, 1e-15)?;
573
574        Ok(())
575    }
576
577    /// Apply dense gate operation for an arbitrary-arity gate.
578    ///
579    /// The state is materialised densely, the `2^k x 2^k` (row-major) gate
580    /// `matrix` acting on `k = qubits.len()` qubits is applied over every group
581    /// of basis states that differ only in the target qubit bits, and the
582    /// result is re-sparsified. The gate-matrix index convention matches the
583    /// rest of the module: `qubits[0]` is the most-significant index of the
584    /// gate matrix.
585    fn apply_dense_gate(&mut self, matrix: &[Complex64], qubits: &[QubitId]) -> QuantRS2Result<()> {
586        // Convert to dense, apply gate, convert back if still sparse enough
587        let mut dense = self.to_dense()?;
588
589        let k = qubits.len();
590        if k == 0 {
591            return Err(QuantRS2Error::InvalidInput(
592                "Gate must act on at least one qubit".to_string(),
593            ));
594        }
595        let gate_dim = 1usize << k;
596        if matrix.len() != gate_dim * gate_dim {
597            return Err(QuantRS2Error::InvalidInput(format!(
598                "Gate matrix has {} elements, expected {} for {} qubit(s)",
599                matrix.len(),
600                gate_dim * gate_dim,
601                k
602            )));
603        }
604
605        let mut qubit_indices = Vec::with_capacity(k);
606        let mut combined_mask = 0usize;
607        for q in qubits {
608            let idx = q.id() as usize;
609            if idx >= self.num_qubits {
610                return Err(QuantRS2Error::InvalidInput(format!(
611                    "Target qubit {} out of range (num_qubits={})",
612                    idx, self.num_qubits
613                )));
614            }
615            qubit_indices.push(idx);
616            combined_mask |= 1usize << idx;
617        }
618
619        let mut indices = vec![0usize; gate_dim];
620        let mut inputs = vec![Complex64::new(0.0, 0.0); gate_dim];
621        for base in 0..self.dimension {
622            // Visit each group of amplitudes (those agreeing on all
623            // non-target bits) exactly once.
624            if base & combined_mask != 0 {
625                continue;
626            }
627            for (g, (idx_slot, in_slot)) in indices.iter_mut().zip(inputs.iter_mut()).enumerate() {
628                let mut idx = base;
629                for (b, &q) in qubit_indices.iter().enumerate() {
630                    if (g >> (k - 1 - b)) & 1 == 1 {
631                        idx |= 1usize << q;
632                    }
633                }
634                *idx_slot = idx;
635                *in_slot = dense[idx];
636            }
637            for row in 0..gate_dim {
638                let mut acc = Complex64::new(0.0, 0.0);
639                for (col, &inp) in inputs.iter().enumerate() {
640                    acc += matrix[row * gate_dim + col] * inp;
641                }
642                dense[indices[row]] = acc;
643            }
644        }
645
646        // Check if result is still sparse enough
647        let nonzero_count = dense.iter().filter(|&&x| x.norm() > 1e-15).count();
648        let new_sparsity = nonzero_count as f64 / self.dimension as f64;
649
650        if new_sparsity < 0.5 {
651            // If still reasonably sparse
652            *self = Self::from_dense(&dense, 1e-15)?;
653        } else {
654            // Convert to dense representation if no longer sparse
655            return Err(QuantRS2Error::ComputationError(
656                "State no longer sparse".to_string(),
657            ));
658        }
659
660        Ok(())
661    }
662
663    /// Update sparse matrix after index changes
664    fn update_sparse_matrix(&mut self) -> QuantRS2Result<()> {
665        // Rebuild sparse matrix from current nonzero_indices
666        let mut dense = vec![Complex64::new(0.0, 0.0); self.dimension];
667
668        for &idx in self.nonzero_indices.keys() {
669            if idx < dense.len() {
670                // Set normalized amplitude (simplified)
671                dense[idx] = Complex64::new(1.0 / (self.nonzero_indices.len() as f64).sqrt(), 0.0);
672            }
673        }
674
675        self.sparse_amplitudes = SimpleSparseMatrix::from_dense(&dense, 1e-15);
676        self.sparsity_ratio = self.nonzero_indices.len() as f64 / self.dimension as f64;
677
678        Ok(())
679    }
680
681    /// Get current sparsity ratio
682    #[must_use]
683    pub const fn sparsity_ratio(&self) -> f64 {
684        self.sparsity_ratio
685    }
686
687    /// Get memory usage in bytes
688    #[must_use]
689    pub fn memory_usage(&self) -> usize {
690        self.nonzero_indices.len()
691            * (std::mem::size_of::<usize>() + std::mem::size_of::<Complex64>())
692    }
693}
694
695/// Simple compression engine for quantum states
696#[derive(Debug)]
697pub struct SimpleCompressionEngine {
698    /// Internal buffer for compression operations
699    buffer: Vec<u8>,
700}
701
702impl Default for SimpleCompressionEngine {
703    fn default() -> Self {
704        Self::new()
705    }
706}
707
708impl SimpleCompressionEngine {
709    #[must_use]
710    pub const fn new() -> Self {
711        Self { buffer: Vec::new() }
712    }
713
714    /// Simple LZ-style compression (using oxiarc-deflate zlib)
715    pub fn compress_lz4(&self, data: &[u8]) -> Result<Vec<u8>, String> {
716        oxiarc_deflate::zlib::zlib_compress(data, 6).map_err(|e| format!("Compression failed: {e}"))
717    }
718
719    /// Simple decompression (using oxiarc-deflate zlib)
720    pub fn decompress_lz4(&self, data: &[u8]) -> Result<Vec<u8>, String> {
721        oxiarc_deflate::zlib::zlib_decompress(data)
722            .map_err(|e| format!("Decompression failed: {e}"))
723    }
724
725    /// Huffman compression placeholder
726    pub fn compress_huffman(&self, data: &[u8]) -> Result<Vec<u8>, String> {
727        // For now, just use the LZ4 method
728        self.compress_lz4(data)
729    }
730
731    /// Huffman decompression placeholder
732    pub fn decompress_huffman(&self, data: &[u8]) -> Result<Vec<u8>, String> {
733        // For now, just use the LZ4 method
734        self.decompress_lz4(data)
735    }
736}
737
738/// Compressed quantum state using simple compression
739#[derive(Debug)]
740pub struct CompressedQuantumState {
741    /// Compressed amplitude data
742    compressed_data: Vec<u8>,
743
744    /// Compression metadata
745    compression_metadata: CompressionMetadata,
746
747    /// Compression engine
748    compression_engine: SimpleCompressionEngine,
749
750    /// Number of qubits
751    num_qubits: usize,
752
753    /// Original size in bytes
754    original_size: usize,
755}
756
757/// Compression metadata
758#[derive(Debug, Clone, Serialize, Deserialize)]
759pub struct CompressionMetadata {
760    /// Compression algorithm used
761    pub algorithm: CompressionAlgorithm,
762
763    /// Compression ratio achieved
764    pub compression_ratio: f64,
765
766    /// Original data size
767    pub original_size: usize,
768
769    /// Compressed data size
770    pub compressed_size: usize,
771
772    /// Checksum for integrity verification
773    pub checksum: u64,
774}
775
776/// Supported compression algorithms
777#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
778pub enum CompressionAlgorithm {
779    /// Huffman encoding for sparse data
780    Huffman,
781    /// LZ4 for general compression
782    LZ4,
783    /// Quantum-specific amplitude compression
784    QuantumAmplitude,
785    /// No compression
786    None,
787}
788
789impl CompressedQuantumState {
790    /// Create new compressed state from dense amplitudes
791    pub fn from_dense(
792        amplitudes: &[Complex64],
793        algorithm: CompressionAlgorithm,
794    ) -> QuantRS2Result<Self> {
795        let num_qubits = (amplitudes.len() as f64).log2() as usize;
796        let original_size = std::mem::size_of_val(amplitudes);
797
798        // Convert to bytes
799        let amplitude_bytes: &[u8] =
800            unsafe { std::slice::from_raw_parts(amplitudes.as_ptr().cast::<u8>(), original_size) };
801
802        // Initialize compression engine
803        let compression_engine = SimpleCompressionEngine::new();
804
805        let (compressed_data, metadata) = match algorithm {
806            CompressionAlgorithm::Huffman => {
807                let compressed = compression_engine
808                    .compress_huffman(amplitude_bytes)
809                    .map_err(|e| {
810                        QuantRS2Error::ComputationError(format!("Huffman compression failed: {e}"))
811                    })?;
812
813                let metadata = CompressionMetadata {
814                    algorithm: CompressionAlgorithm::Huffman,
815                    compression_ratio: original_size as f64 / compressed.len() as f64,
816                    original_size,
817                    compressed_size: compressed.len(),
818                    checksum: Self::calculate_checksum(amplitude_bytes),
819                };
820
821                (compressed, metadata)
822            }
823            CompressionAlgorithm::LZ4 => {
824                let compressed = compression_engine
825                    .compress_lz4(amplitude_bytes)
826                    .map_err(|e| {
827                        QuantRS2Error::ComputationError(format!("LZ4 compression failed: {e}"))
828                    })?;
829
830                let metadata = CompressionMetadata {
831                    algorithm: CompressionAlgorithm::LZ4,
832                    compression_ratio: original_size as f64 / compressed.len() as f64,
833                    original_size,
834                    compressed_size: compressed.len(),
835                    checksum: Self::calculate_checksum(amplitude_bytes),
836                };
837
838                (compressed, metadata)
839            }
840            CompressionAlgorithm::QuantumAmplitude => {
841                // Custom quantum amplitude compression
842                let compressed = Self::compress_quantum_amplitudes(amplitudes)?;
843
844                let metadata = CompressionMetadata {
845                    algorithm: CompressionAlgorithm::QuantumAmplitude,
846                    compression_ratio: original_size as f64 / compressed.len() as f64,
847                    original_size,
848                    compressed_size: compressed.len(),
849                    checksum: Self::calculate_checksum(amplitude_bytes),
850                };
851
852                (compressed, metadata)
853            }
854            CompressionAlgorithm::None => {
855                let metadata = CompressionMetadata {
856                    algorithm: CompressionAlgorithm::None,
857                    compression_ratio: 1.0,
858                    original_size,
859                    compressed_size: original_size,
860                    checksum: Self::calculate_checksum(amplitude_bytes),
861                };
862
863                (amplitude_bytes.to_vec(), metadata)
864            }
865        };
866
867        Ok(Self {
868            compressed_data,
869            compression_metadata: metadata,
870            compression_engine,
871            num_qubits,
872            original_size,
873        })
874    }
875
876    /// Decompress to dense amplitudes
877    pub fn to_dense(&self) -> QuantRS2Result<Vec<Complex64>> {
878        let decompressed_bytes = match self.compression_metadata.algorithm {
879            CompressionAlgorithm::Huffman => self
880                .compression_engine
881                .decompress_huffman(&self.compressed_data)
882                .map_err(|e| {
883                    QuantRS2Error::ComputationError(format!("Huffman decompression failed: {e}"))
884                })?,
885            CompressionAlgorithm::LZ4 => self
886                .compression_engine
887                .decompress_lz4(&self.compressed_data)
888                .map_err(|e| {
889                    QuantRS2Error::ComputationError(format!("LZ4 decompression failed: {e}"))
890                })?,
891            CompressionAlgorithm::QuantumAmplitude => {
892                Self::decompress_quantum_amplitudes(&self.compressed_data, self.num_qubits)?
893            }
894            CompressionAlgorithm::None => self.compressed_data.clone(),
895        };
896
897        // Verify checksum
898        let checksum = Self::calculate_checksum(&decompressed_bytes);
899        if checksum != self.compression_metadata.checksum {
900            return Err(QuantRS2Error::ComputationError(
901                "Checksum verification failed".to_string(),
902            ));
903        }
904
905        // Convert bytes back to complex numbers
906        let amplitudes = unsafe {
907            std::slice::from_raw_parts(
908                decompressed_bytes.as_ptr().cast::<Complex64>(),
909                decompressed_bytes.len() / std::mem::size_of::<Complex64>(),
910            )
911        }
912        .to_vec();
913
914        Ok(amplitudes)
915    }
916
917    /// Custom quantum amplitude compression
918    fn compress_quantum_amplitudes(amplitudes: &[Complex64]) -> QuantRS2Result<Vec<u8>> {
919        // Quantum-specific compression using magnitude-phase representation
920        let mut compressed = Vec::new();
921
922        for &amplitude in amplitudes {
923            let magnitude = amplitude.norm();
924            let phase = amplitude.arg();
925
926            // Quantize magnitude and phase for better compression
927            let quantized_magnitude = (magnitude * 65_535.0) as u16;
928            let quantized_phase =
929                ((phase + std::f64::consts::PI) / (2.0 * std::f64::consts::PI) * 65_535.0) as u16;
930
931            compressed.extend_from_slice(&quantized_magnitude.to_le_bytes());
932            compressed.extend_from_slice(&quantized_phase.to_le_bytes());
933        }
934
935        Ok(compressed)
936    }
937
938    /// Custom quantum amplitude decompression
939    fn decompress_quantum_amplitudes(data: &[u8], num_qubits: usize) -> QuantRS2Result<Vec<u8>> {
940        let dimension = 1usize << num_qubits;
941        let mut amplitudes = Vec::with_capacity(dimension);
942
943        for i in 0..dimension {
944            let offset = i * 4; // 2 bytes magnitude + 2 bytes phase
945            if offset + 4 <= data.len() {
946                let magnitude_bytes = [data[offset], data[offset + 1]];
947                let phase_bytes = [data[offset + 2], data[offset + 3]];
948
949                let quantized_magnitude = u16::from_le_bytes(magnitude_bytes);
950                let quantized_phase = u16::from_le_bytes(phase_bytes);
951
952                let magnitude = f64::from(quantized_magnitude) / 65_535.0;
953                let phase = ((f64::from(quantized_phase) / 65_535.0) * 2.0)
954                    .mul_add(std::f64::consts::PI, -std::f64::consts::PI);
955
956                let amplitude = Complex64::new(magnitude * phase.cos(), magnitude * phase.sin());
957                amplitudes.push(amplitude);
958            }
959        }
960
961        // Convert back to bytes
962        let amplitude_bytes = unsafe {
963            std::slice::from_raw_parts(
964                amplitudes.as_ptr().cast::<u8>(),
965                amplitudes.len() * std::mem::size_of::<Complex64>(),
966            )
967        };
968
969        Ok(amplitude_bytes.to_vec())
970    }
971
972    /// Calculate checksum for integrity verification
973    fn calculate_checksum(data: &[u8]) -> u64 {
974        // Simple checksum implementation
975        data.iter()
976            .enumerate()
977            .map(|(i, &b)| (i as u64).wrapping_mul(u64::from(b)))
978            .sum()
979    }
980
981    /// Get compression ratio
982    #[must_use]
983    pub const fn compression_ratio(&self) -> f64 {
984        self.compression_metadata.compression_ratio
985    }
986
987    /// Get memory usage in bytes
988    #[must_use]
989    pub fn memory_usage(&self) -> usize {
990        self.compressed_data.len()
991    }
992}
993
994/// Memory-mapped quantum state for very large simulations
995#[derive(Debug)]
996pub struct MemoryMappedQuantumState {
997    /// Memory-mapped file
998    mmap: MmapMut,
999
1000    /// File path
1001    file_path: PathBuf,
1002
1003    /// Number of qubits
1004    num_qubits: usize,
1005
1006    /// State dimension
1007    dimension: usize,
1008
1009    /// Chunk size for processing
1010    chunk_size: usize,
1011}
1012
1013impl MemoryMappedQuantumState {
1014    /// Create new memory-mapped state
1015    pub fn new(num_qubits: usize, chunk_size: usize, working_dir: &Path) -> QuantRS2Result<Self> {
1016        let dimension = 1usize << num_qubits;
1017        let file_size = dimension * std::mem::size_of::<Complex64>();
1018
1019        // Create temporary file
1020        std::fs::create_dir_all(working_dir).map_err(|e| {
1021            QuantRS2Error::InvalidInput(format!("Failed to create working directory: {e}"))
1022        })?;
1023
1024        let file_path = working_dir.join(format!("quantum_state_{}.tmp", Uuid::new_v4()));
1025
1026        let file = OpenOptions::new()
1027            .read(true)
1028            .write(true)
1029            .create(true)
1030            .open(&file_path)
1031            .map_err(|e| QuantRS2Error::InvalidInput(format!("Failed to create temp file: {e}")))?;
1032
1033        file.set_len(file_size as u64)
1034            .map_err(|e| QuantRS2Error::InvalidInput(format!("Failed to set file size: {e}")))?;
1035
1036        let mmap = unsafe {
1037            MmapOptions::new().map_mut(&file).map_err(|e| {
1038                QuantRS2Error::InvalidInput(format!("Failed to create memory map: {e}"))
1039            })?
1040        };
1041
1042        let mut state = Self {
1043            mmap,
1044            file_path,
1045            num_qubits,
1046            dimension,
1047            chunk_size,
1048        };
1049
1050        // Initialize to |0...0⟩ state
1051        state.initialize_zero_state()?;
1052
1053        Ok(state)
1054    }
1055
1056    /// Initialize state to |0...0⟩
1057    fn initialize_zero_state(&mut self) -> QuantRS2Result<()> {
1058        let amplitudes = self.get_amplitudes_mut();
1059
1060        // Clear all amplitudes
1061        for amplitude in amplitudes.iter_mut() {
1062            *amplitude = Complex64::new(0.0, 0.0);
1063        }
1064
1065        // Set first amplitude to 1.0 (|0...0⟩ state)
1066        if !amplitudes.is_empty() {
1067            amplitudes[0] = Complex64::new(1.0, 0.0);
1068        }
1069
1070        Ok(())
1071    }
1072
1073    /// Get amplitudes as mutable slice
1074    fn get_amplitudes_mut(&mut self) -> &mut [Complex64] {
1075        unsafe {
1076            std::slice::from_raw_parts_mut(
1077                self.mmap.as_mut_ptr().cast::<Complex64>(),
1078                self.dimension,
1079            )
1080        }
1081    }
1082
1083    /// Get amplitudes as slice
1084    fn get_amplitudes(&self) -> &[Complex64] {
1085        unsafe {
1086            std::slice::from_raw_parts(self.mmap.as_ptr().cast::<Complex64>(), self.dimension)
1087        }
1088    }
1089
1090    /// Apply gate operation using chunked processing
1091    pub fn apply_gate_chunked(&mut self, gate: &dyn GateOp) -> QuantRS2Result<()> {
1092        let num_chunks = self.dimension.div_ceil(self.chunk_size);
1093
1094        for chunk_idx in 0..num_chunks {
1095            let start = chunk_idx * self.chunk_size;
1096            let end = (start + self.chunk_size).min(self.dimension);
1097
1098            self.apply_gate_to_chunk(gate, start, end)?;
1099        }
1100
1101        Ok(())
1102    }
1103
1104    /// Apply gate to specific chunk
1105    fn apply_gate_to_chunk(
1106        &mut self,
1107        gate: &dyn GateOp,
1108        start: usize,
1109        end: usize,
1110    ) -> QuantRS2Result<()> {
1111        // Cache dimension to avoid borrowing issues
1112        let dimension = self.dimension;
1113        let amplitudes = self.get_amplitudes_mut();
1114
1115        match gate.name() {
1116            "X" => {
1117                if let Some(target) = gate.qubits().first() {
1118                    let target_idx = target.id() as usize;
1119                    let target_mask = 1usize << target_idx;
1120
1121                    for i in start..end {
1122                        if (i & target_mask) == 0 {
1123                            let paired_idx = i | target_mask;
1124                            if paired_idx < dimension {
1125                                amplitudes.swap(i, paired_idx);
1126                            }
1127                        }
1128                    }
1129                }
1130            }
1131            "H" => {
1132                if let Some(target) = gate.qubits().first() {
1133                    let target_idx = target.id() as usize;
1134                    let target_mask = 1usize << target_idx;
1135                    let inv_sqrt2 = Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0);
1136
1137                    // `amplitudes` is the *full* state array, so the paired
1138                    // amplitude can be read directly even when it lives in a
1139                    // different chunk. Because `paired_idx = i | target_mask`
1140                    // is always >= i, and we only initiate the update from the
1141                    // index whose target bit is 0, every pair is processed
1142                    // exactly once regardless of the chunk boundary. This is
1143                    // what fixes the previous cross-chunk no-op for high qubit
1144                    // indices (target stride >= chunk_size).
1145                    for i in start..end {
1146                        if (i & target_mask) == 0 {
1147                            let paired_idx = i | target_mask;
1148                            if paired_idx < dimension {
1149                                let old_0 = amplitudes[i];
1150                                let old_1 = amplitudes[paired_idx];
1151
1152                                amplitudes[i] = inv_sqrt2 * (old_0 + old_1);
1153                                amplitudes[paired_idx] = inv_sqrt2 * (old_0 - old_1);
1154                            }
1155                        }
1156                    }
1157                }
1158            }
1159            _ => {
1160                return Err(QuantRS2Error::UnsupportedOperation(format!(
1161                    "Chunked operation not implemented for gate {}",
1162                    gate.name()
1163                )));
1164            }
1165        }
1166
1167        Ok(())
1168    }
1169
1170    /// Get memory usage (just metadata, actual state is in file)
1171    #[must_use]
1172    pub const fn memory_usage(&self) -> usize {
1173        std::mem::size_of::<Self>()
1174    }
1175
1176    /// Get file size
1177    #[must_use]
1178    pub const fn file_size(&self) -> usize {
1179        self.dimension * std::mem::size_of::<Complex64>()
1180    }
1181}
1182
1183impl Drop for MemoryMappedQuantumState {
1184    fn drop(&mut self) {
1185        // Clean up temporary file
1186        let _ = std::fs::remove_file(&self.file_path);
1187    }
1188}
1189
1190/// Large-scale quantum simulator supporting 40+ qubits
1191#[derive(Debug)]
1192pub struct LargeScaleQuantumSimulator {
1193    /// Configuration
1194    config: LargeScaleSimulatorConfig,
1195
1196    /// Current quantum state representation
1197    state: QuantumStateRepresentation,
1198
1199    /// Buffer pool for optimizations
1200    buffer_pool: Arc<Mutex<Vec<Vec<Complex64>>>>,
1201
1202    /// Memory usage statistics
1203    memory_stats: Arc<Mutex<MemoryStatistics>>,
1204}
1205
1206/// Different quantum state representations
1207#[derive(Debug)]
1208pub enum QuantumStateRepresentation {
1209    /// Dense state vector (traditional)
1210    Dense(Vec<Complex64>),
1211
1212    /// Sparse state representation
1213    Sparse(SparseQuantumState),
1214
1215    /// Compressed state representation
1216    Compressed(CompressedQuantumState),
1217
1218    /// Memory-mapped state for very large simulations
1219    MemoryMapped(MemoryMappedQuantumState),
1220}
1221
1222/// Memory usage statistics
1223#[derive(Debug, Default, Clone)]
1224pub struct MemoryStatistics {
1225    /// Current memory usage in bytes
1226    pub current_usage: usize,
1227
1228    /// Peak memory usage in bytes
1229    pub peak_usage: usize,
1230
1231    /// Number of allocations
1232    pub allocations: u64,
1233
1234    /// Number of deallocations
1235    pub deallocations: u64,
1236
1237    /// Compression ratio achieved
1238    pub compression_ratio: f64,
1239
1240    /// Sparsity ratio
1241    pub sparsity_ratio: f64,
1242
1243    /// Time spent in memory operations (microseconds)
1244    pub memory_operation_time_us: u64,
1245}
1246
1247impl LargeScaleQuantumSimulator {
1248    /// Create new large-scale simulator
1249    pub fn new(config: LargeScaleSimulatorConfig) -> QuantRS2Result<Self> {
1250        let buffer_pool = Arc::new(Mutex::new(Vec::new()));
1251        let memory_stats = Arc::new(Mutex::new(MemoryStatistics::default()));
1252
1253        // Initialize with appropriate state representation
1254        let state = QuantumStateRepresentation::Dense(vec![Complex64::new(1.0, 0.0)]);
1255
1256        Ok(Self {
1257            config,
1258            state,
1259            buffer_pool,
1260            memory_stats,
1261        })
1262    }
1263
1264    /// Initialize quantum state for given number of qubits
1265    pub fn initialize_state(&mut self, num_qubits: usize) -> QuantRS2Result<()> {
1266        if num_qubits > self.config.max_qubits {
1267            return Err(QuantRS2Error::InvalidInput(format!(
1268                "Number of qubits {} exceeds maximum {}",
1269                num_qubits, self.config.max_qubits
1270            )));
1271        }
1272
1273        let dimension = 1usize << num_qubits;
1274        let memory_required = dimension * std::mem::size_of::<Complex64>();
1275
1276        // Choose appropriate representation based on size and configuration
1277        self.state = if memory_required > self.config.memory_mapping_threshold {
1278            // Use memory mapping for very large states
1279            QuantumStateRepresentation::MemoryMapped(MemoryMappedQuantumState::new(
1280                num_qubits,
1281                self.config.chunk_size,
1282                &self.config.working_directory,
1283            )?)
1284        } else if memory_required > self.config.compression_threshold
1285            && self.config.enable_compression
1286        {
1287            // Use compression for medium-large states
1288            let amplitudes = vec![Complex64::new(0.0, 0.0); dimension];
1289            let mut amplitudes = amplitudes;
1290            amplitudes[0] = Complex64::new(1.0, 0.0); // |0...0⟩ state
1291
1292            QuantumStateRepresentation::Compressed(CompressedQuantumState::from_dense(
1293                &amplitudes,
1294                CompressionAlgorithm::LZ4,
1295            )?)
1296        } else if self.config.enable_sparse_representation {
1297            // Use sparse representation
1298            QuantumStateRepresentation::Sparse(SparseQuantumState::new(num_qubits)?)
1299        } else {
1300            // Use traditional dense representation
1301            let mut amplitudes = vec![Complex64::new(0.0, 0.0); dimension];
1302            amplitudes[0] = Complex64::new(1.0, 0.0); // |0...0⟩ state
1303            QuantumStateRepresentation::Dense(amplitudes)
1304        };
1305
1306        self.update_memory_stats()?;
1307
1308        Ok(())
1309    }
1310
1311    /// Apply quantum gate
1312    pub fn apply_gate(&mut self, gate: &dyn GateOp) -> QuantRS2Result<()> {
1313        let start_time = std::time::Instant::now();
1314
1315        // Handle different state representations
1316        let mut needs_state_change = None;
1317
1318        match &mut self.state {
1319            QuantumStateRepresentation::Dense(amplitudes) => {
1320                // Create a copy to avoid borrowing issues
1321                let mut amplitudes_copy = amplitudes.clone();
1322                Self::apply_gate_dense(&mut amplitudes_copy, gate, &self.config)?;
1323                *amplitudes = amplitudes_copy;
1324            }
1325            QuantumStateRepresentation::Sparse(sparse_state) => {
1326                sparse_state.apply_sparse_gate(gate)?;
1327
1328                // Check if still sparse enough
1329                if sparse_state.sparsity_ratio() > self.config.sparsity_threshold {
1330                    // Convert to dense if no longer sparse
1331                    let dense = sparse_state.to_dense()?;
1332                    needs_state_change = Some(QuantumStateRepresentation::Dense(dense));
1333                }
1334            }
1335            QuantumStateRepresentation::Compressed(compressed_state) => {
1336                // Decompress, apply gate, recompress
1337                let mut dense = compressed_state.to_dense()?;
1338                Self::apply_gate_dense(&mut dense, gate, &self.config)?;
1339
1340                // Recompress if beneficial
1341                let new_compressed =
1342                    CompressedQuantumState::from_dense(&dense, CompressionAlgorithm::LZ4)?;
1343                if new_compressed.compression_ratio() > 1.5 {
1344                    needs_state_change =
1345                        Some(QuantumStateRepresentation::Compressed(new_compressed));
1346                } else {
1347                    needs_state_change = Some(QuantumStateRepresentation::Dense(dense));
1348                }
1349            }
1350            QuantumStateRepresentation::MemoryMapped(mmap_state) => {
1351                mmap_state.apply_gate_chunked(gate)?;
1352            }
1353        }
1354
1355        // Apply state change if needed
1356        if let Some(new_state) = needs_state_change {
1357            self.state = new_state;
1358        }
1359
1360        let elapsed = start_time.elapsed();
1361        if let Ok(mut stats) = self.memory_stats.lock() {
1362            stats.memory_operation_time_us += elapsed.as_micros() as u64;
1363        }
1364
1365        Ok(())
1366    }
1367
1368    /// Apply gate to dense representation
1369    fn apply_gate_dense(
1370        amplitudes: &mut [Complex64],
1371        gate: &dyn GateOp,
1372        config: &LargeScaleSimulatorConfig,
1373    ) -> QuantRS2Result<()> {
1374        match gate.name() {
1375            "X" => {
1376                if let Some(target) = gate.qubits().first() {
1377                    let target_idx = target.id() as usize;
1378                    Self::apply_pauli_x_dense(amplitudes, target_idx)?;
1379                }
1380            }
1381            "H" => {
1382                if let Some(target) = gate.qubits().first() {
1383                    let target_idx = target.id() as usize;
1384                    Self::apply_hadamard_dense(amplitudes, target_idx, config)?;
1385                }
1386            }
1387            "CNOT" => {
1388                if gate.qubits().len() >= 2 {
1389                    let control_idx = gate.qubits()[0].id() as usize;
1390                    let target_idx = gate.qubits()[1].id() as usize;
1391                    Self::apply_cnot_dense(amplitudes, control_idx, target_idx)?;
1392                }
1393            }
1394            _ => {
1395                return Err(QuantRS2Error::UnsupportedOperation(format!(
1396                    "Gate {} not implemented in large-scale simulator",
1397                    gate.name()
1398                )));
1399            }
1400        }
1401
1402        Ok(())
1403    }
1404
1405    /// Apply Pauli-X gate to dense representation
1406    fn apply_pauli_x_dense(amplitudes: &mut [Complex64], target: usize) -> QuantRS2Result<()> {
1407        let target_mask = 1usize << target;
1408
1409        // Sequential operation for now (parallel optimization can be added later)
1410        for i in 0..amplitudes.len() {
1411            if (i & target_mask) == 0 {
1412                let paired_idx = i | target_mask;
1413                if paired_idx < amplitudes.len() {
1414                    amplitudes.swap(i, paired_idx);
1415                }
1416            }
1417        }
1418
1419        Ok(())
1420    }
1421
1422    /// Apply Hadamard gate to dense representation
1423    fn apply_hadamard_dense(
1424        amplitudes: &mut [Complex64],
1425        target: usize,
1426        _config: &LargeScaleSimulatorConfig,
1427    ) -> QuantRS2Result<()> {
1428        let target_mask = 1usize << target;
1429        let inv_sqrt2 = Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0);
1430
1431        // Create temporary buffer
1432        let mut temp = vec![Complex64::new(0.0, 0.0); amplitudes.len()];
1433        temp.copy_from_slice(amplitudes);
1434
1435        // Sequential operation for now (parallel optimization can be added later)
1436        for i in 0..amplitudes.len() {
1437            if (i & target_mask) == 0 {
1438                let paired_idx = i | target_mask;
1439                if paired_idx < amplitudes.len() {
1440                    let old_0 = temp[i];
1441                    let old_1 = temp[paired_idx];
1442
1443                    amplitudes[i] = inv_sqrt2 * (old_0 + old_1);
1444                    amplitudes[paired_idx] = inv_sqrt2 * (old_0 - old_1);
1445                }
1446            }
1447        }
1448
1449        Ok(())
1450    }
1451
1452    /// Apply CNOT gate to dense representation
1453    fn apply_cnot_dense(
1454        amplitudes: &mut [Complex64],
1455        control: usize,
1456        target: usize,
1457    ) -> QuantRS2Result<()> {
1458        let control_mask = 1usize << control;
1459        let target_mask = 1usize << target;
1460
1461        // Sequential operation for now (parallel optimization can be added later)
1462        for i in 0..amplitudes.len() {
1463            if (i & control_mask) != 0 && (i & target_mask) == 0 {
1464                let flipped_idx = i | target_mask;
1465                if flipped_idx < amplitudes.len() {
1466                    amplitudes.swap(i, flipped_idx);
1467                }
1468            }
1469        }
1470
1471        Ok(())
1472    }
1473
1474    /// Get current state as dense vector (for measurement)
1475    pub fn get_dense_state(&self) -> QuantRS2Result<Vec<Complex64>> {
1476        match &self.state {
1477            QuantumStateRepresentation::Dense(amplitudes) => Ok(amplitudes.clone()),
1478            QuantumStateRepresentation::Sparse(sparse_state) => sparse_state.to_dense(),
1479            QuantumStateRepresentation::Compressed(compressed_state) => compressed_state.to_dense(),
1480            QuantumStateRepresentation::MemoryMapped(mmap_state) => {
1481                Ok(mmap_state.get_amplitudes().to_vec())
1482            }
1483        }
1484    }
1485
1486    /// Update memory usage statistics
1487    fn update_memory_stats(&self) -> QuantRS2Result<()> {
1488        if let Ok(mut stats) = self.memory_stats.lock() {
1489            let current_usage = match &self.state {
1490                QuantumStateRepresentation::Dense(amplitudes) => {
1491                    amplitudes.len() * std::mem::size_of::<Complex64>()
1492                }
1493                QuantumStateRepresentation::Sparse(sparse_state) => sparse_state.memory_usage(),
1494                QuantumStateRepresentation::Compressed(compressed_state) => {
1495                    compressed_state.memory_usage()
1496                }
1497                QuantumStateRepresentation::MemoryMapped(mmap_state) => mmap_state.memory_usage(),
1498            };
1499
1500            stats.current_usage = current_usage;
1501            if current_usage > stats.peak_usage {
1502                stats.peak_usage = current_usage;
1503            }
1504
1505            // Update compression and sparsity ratios
1506            match &self.state {
1507                QuantumStateRepresentation::Compressed(compressed_state) => {
1508                    stats.compression_ratio = compressed_state.compression_ratio();
1509                }
1510                QuantumStateRepresentation::Sparse(sparse_state) => {
1511                    stats.sparsity_ratio = sparse_state.sparsity_ratio();
1512                }
1513                _ => {}
1514            }
1515        }
1516
1517        Ok(())
1518    }
1519
1520    /// Get memory statistics
1521    #[must_use]
1522    pub fn get_memory_stats(&self) -> MemoryStatistics {
1523        self.memory_stats
1524            .lock()
1525            .map(|stats| stats.clone())
1526            .unwrap_or_default()
1527    }
1528
1529    /// Get configuration
1530    #[must_use]
1531    pub const fn get_config(&self) -> &LargeScaleSimulatorConfig {
1532        &self.config
1533    }
1534
1535    /// Check if current state can simulate given number of qubits
1536    #[must_use]
1537    pub const fn can_simulate(&self, num_qubits: usize) -> bool {
1538        if num_qubits > self.config.max_qubits {
1539            return false;
1540        }
1541
1542        let dimension = 1usize << num_qubits;
1543        let memory_required = dimension * std::mem::size_of::<Complex64>();
1544
1545        memory_required <= self.config.memory_budget
1546    }
1547
1548    /// Estimate memory requirements for given number of qubits
1549    #[must_use]
1550    pub fn estimate_memory_requirements(&self, num_qubits: usize) -> usize {
1551        let dimension = 1usize << num_qubits;
1552        let base_memory = dimension * std::mem::size_of::<Complex64>();
1553
1554        // Add overhead for operations and temporary buffers
1555        let overhead_factor = 1.5;
1556        (base_memory as f64 * overhead_factor) as usize
1557    }
1558}
1559
1560impl<const N: usize> Simulator<N> for LargeScaleQuantumSimulator {
1561    fn run(&self, circuit: &Circuit<N>) -> QuantRS2Result<quantrs2_core::register::Register<N>> {
1562        let mut simulator = Self::new(self.config.clone())?;
1563        simulator.initialize_state(N)?;
1564
1565        // Apply all gates in the circuit
1566        for gate in circuit.gates() {
1567            simulator.apply_gate(gate.as_ref())?;
1568        }
1569
1570        // Get final state and create register
1571        let final_state = simulator.get_dense_state()?;
1572        quantrs2_core::register::Register::with_amplitudes(final_state)
1573    }
1574}
1575
1576#[cfg(test)]
1577mod tests {
1578    use super::*;
1579    use quantrs2_core::gate::multi::CNOT;
1580    use quantrs2_core::gate::single::{Hadamard, PauliX};
1581    use quantrs2_core::qubit::QubitId;
1582
1583    #[test]
1584    fn test_sparse_quantum_state() {
1585        let mut sparse_state =
1586            SparseQuantumState::new(3).expect("Sparse state creation should succeed in test");
1587        assert_eq!(sparse_state.num_qubits, 3);
1588        assert_eq!(sparse_state.dimension, 8);
1589        assert!(sparse_state.sparsity_ratio() < 0.2);
1590
1591        let dense = sparse_state
1592            .to_dense()
1593            .expect("Sparse to dense conversion should succeed in test");
1594        assert_eq!(dense.len(), 8);
1595        assert!((dense[0] - Complex64::new(1.0, 0.0)).norm() < 1e-10);
1596    }
1597
1598    #[test]
1599    fn test_compressed_quantum_state() {
1600        let amplitudes = vec![
1601            Complex64::new(1.0, 0.0),
1602            Complex64::new(0.0, 0.0),
1603            Complex64::new(0.0, 0.0),
1604            Complex64::new(0.0, 0.0),
1605        ];
1606
1607        let compressed = CompressedQuantumState::from_dense(&amplitudes, CompressionAlgorithm::LZ4)
1608            .expect("Compression should succeed in test");
1609        let decompressed = compressed
1610            .to_dense()
1611            .expect("Decompression should succeed in test");
1612
1613        assert_eq!(decompressed.len(), 4);
1614        assert!((decompressed[0] - Complex64::new(1.0, 0.0)).norm() < 1e-10);
1615    }
1616
1617    #[test]
1618    fn test_large_scale_simulator() {
1619        let config = LargeScaleSimulatorConfig::default();
1620        let mut simulator = LargeScaleQuantumSimulator::new(config)
1621            .expect("Simulator creation should succeed in test");
1622
1623        // Test with 10 qubits (medium scale)
1624        simulator
1625            .initialize_state(10)
1626            .expect("State initialization should succeed in test");
1627        assert!(simulator.can_simulate(10));
1628
1629        // Test basic gates
1630        let x_gate = PauliX { target: QubitId(0) };
1631        simulator
1632            .apply_gate(&x_gate)
1633            .expect("X gate application should succeed in test");
1634
1635        let h_gate = Hadamard { target: QubitId(1) };
1636        simulator
1637            .apply_gate(&h_gate)
1638            .expect("H gate application should succeed in test");
1639
1640        let final_state = simulator
1641            .get_dense_state()
1642            .expect("State retrieval should succeed in test");
1643        assert_eq!(final_state.len(), 1024); // 2^10
1644    }
1645
1646    #[test]
1647    fn test_memory_stats() {
1648        let config = LargeScaleSimulatorConfig::default();
1649        let mut simulator = LargeScaleQuantumSimulator::new(config)
1650            .expect("Simulator creation should succeed in test");
1651
1652        simulator
1653            .initialize_state(5)
1654            .expect("State initialization should succeed in test");
1655        let stats = simulator.get_memory_stats();
1656
1657        assert!(stats.current_usage > 0);
1658        assert_eq!(stats.peak_usage, stats.current_usage);
1659    }
1660
1661    /// Regression: `apply_dense_gate` previously did nothing for 3+ qubit
1662    /// gates, leaving the state unchanged while reporting success. A Toffoli
1663    /// must genuinely permute the amplitudes.
1664    #[test]
1665    fn test_apply_dense_gate_three_qubit_toffoli() {
1666        // Start in |011> (bit0=1, bit1=1, bit2=0) -> index 3, i.e. both
1667        // controls (qubit0, qubit1) set and target (qubit2) unset.
1668        let mut dense = vec![Complex64::new(0.0, 0.0); 8];
1669        dense[3] = Complex64::new(1.0, 0.0);
1670        let mut sparse = SparseQuantumState::from_dense(&dense, 1e-15)
1671            .expect("sparse state construction should succeed");
1672
1673        // Standard Toffoli 8x8 matrix (MSB-first ordering: qubits[0] is the
1674        // most significant), which swaps basis states 6 (110) and 7 (111).
1675        let mut matrix = vec![Complex64::new(0.0, 0.0); 64];
1676        for i in 0..8 {
1677            matrix[i * 8 + i] = Complex64::new(1.0, 0.0);
1678        }
1679        matrix[6 * 8 + 6] = Complex64::new(0.0, 0.0);
1680        matrix[7 * 8 + 7] = Complex64::new(0.0, 0.0);
1681        matrix[6 * 8 + 7] = Complex64::new(1.0, 0.0);
1682        matrix[7 * 8 + 6] = Complex64::new(1.0, 0.0);
1683
1684        let qubits = [QubitId(0), QubitId(1), QubitId(2)];
1685        sparse
1686            .apply_dense_gate(&matrix, &qubits)
1687            .expect("dense gate application should succeed");
1688
1689        let result = sparse.to_dense().expect("dense conversion should succeed");
1690        // Target qubit flipped: |111> -> index 7.
1691        assert!(
1692            (result[7] - Complex64::new(1.0, 0.0)).norm() < 1e-10,
1693            "Toffoli must move amplitude from index 3 to index 7, got {result:?}"
1694        );
1695        assert!(
1696            result[3].norm() < 1e-10,
1697            "index 3 must be empty after Toffoli"
1698        );
1699    }
1700
1701    /// Regression: the memory-mapped Hadamard silently skipped amplitude pairs
1702    /// that crossed a chunk boundary, making H a no-op for qubits whose stride
1703    /// (`1 << target`) is >= `chunk_size`. Here qubit 4 has stride 16 while the
1704    /// chunk size is 4, so every pair is cross-chunk.
1705    #[test]
1706    fn test_memory_mapped_hadamard_crosses_chunk_boundary() {
1707        let dir = std::env::temp_dir();
1708        let mut mmap = MemoryMappedQuantumState::new(5, 4, &dir)
1709            .expect("memory-mapped state creation should succeed");
1710
1711        let h_gate = Hadamard { target: QubitId(4) };
1712        mmap.apply_gate_chunked(&h_gate)
1713            .expect("chunked Hadamard should succeed");
1714
1715        let amps = mmap.get_amplitudes();
1716        let inv_sqrt2 = 1.0 / 2.0_f64.sqrt();
1717        // H on qubit 4 of |00000> -> (|00000> + |10000>)/sqrt2, i.e. indices
1718        // 0 and 16 each carry 1/sqrt2.
1719        assert!(
1720            (amps[0].re - inv_sqrt2).abs() < 1e-10,
1721            "amplitude at index 0 should be 1/sqrt2, got {}",
1722            amps[0].re
1723        );
1724        assert!(
1725            (amps[16].re - inv_sqrt2).abs() < 1e-10,
1726            "cross-chunk amplitude at index 16 should be 1/sqrt2, got {} (was silently skipped before the fix)",
1727            amps[16].re
1728        );
1729        let norm: f64 = amps.iter().map(|a| a.norm_sqr()).sum();
1730        assert!((norm - 1.0).abs() < 1e-9, "state must remain normalised");
1731    }
1732}