quantrs2_core/
memory_efficient.rs

1//! Memory-efficient quantum state storage using SciRS2
2//!
3//! This module provides memory-efficient storage for quantum states by leveraging
4//! SciRS2's memory management utilities.
5
6use crate::error::{QuantRS2Error, QuantRS2Result};
7use num_complex::Complex64;
8
9/// A memory-efficient storage for large quantum state vectors
10///
11/// This provides memory-efficient storage and operations for quantum states,
12/// with support for chunk-based processing of large state vectors.
13pub struct EfficientStateVector {
14    /// Number of qubits
15    num_qubits: usize,
16    /// The actual state data
17    data: Vec<Complex64>,
18}
19
20impl EfficientStateVector {
21    /// Create a new efficient state vector for the given number of qubits
22    pub fn new(num_qubits: usize) -> QuantRS2Result<Self> {
23        let size = 1 << num_qubits;
24        if size > 1 << 30 {
25            // For very large states (>30 qubits), create large vector directly
26            // Note: BufferPool would need different design for thread-safe access
27            let mut data = vec![Complex64::new(0.0, 0.0); size];
28            data[0] = Complex64::new(1.0, 0.0); // Initialize to |00...0⟩
29            Ok(Self { num_qubits, data })
30        } else {
31            // For smaller states, use regular allocation
32            let mut data = vec![Complex64::new(0.0, 0.0); size];
33            data[0] = Complex64::new(1.0, 0.0); // Initialize to |00...0⟩
34            Ok(Self { num_qubits, data })
35        }
36    }
37
38    /// Get the number of qubits
39    pub fn num_qubits(&self) -> usize {
40        self.num_qubits
41    }
42
43    /// Get the size of the state vector
44    pub fn size(&self) -> usize {
45        self.data.len()
46    }
47
48    /// Get a reference to the state data
49    pub fn data(&self) -> &[Complex64] {
50        &self.data
51    }
52
53    /// Get a mutable reference to the state data
54    pub fn data_mut(&mut self) -> &mut [Complex64] {
55        &mut self.data
56    }
57
58    /// Normalize the state vector
59    pub fn normalize(&mut self) -> QuantRS2Result<()> {
60        let norm_sqr: f64 = self.data.iter().map(|c| c.norm_sqr()).sum();
61        if norm_sqr == 0.0 {
62            return Err(QuantRS2Error::InvalidInput(
63                "Cannot normalize zero vector".to_string(),
64            ));
65        }
66        let norm = norm_sqr.sqrt();
67        for amplitude in &mut self.data {
68            *amplitude /= norm;
69        }
70        Ok(())
71    }
72
73    /// Calculate the probability of measuring a specific basis state
74    pub fn get_probability(&self, basis_state: usize) -> QuantRS2Result<f64> {
75        if basis_state >= self.data.len() {
76            return Err(QuantRS2Error::InvalidInput(format!(
77                "Basis state {} out of range for {} qubits",
78                basis_state, self.num_qubits
79            )));
80        }
81        Ok(self.data[basis_state].norm_sqr())
82    }
83
84    /// Apply a function to chunks of the state vector
85    ///
86    /// This is useful for operations that can be parallelized or when
87    /// working with states too large to fit in cache.
88    pub fn process_chunks<F>(&mut self, chunk_size: usize, mut f: F) -> QuantRS2Result<()>
89    where
90        F: FnMut(&mut [Complex64], usize),
91    {
92        if chunk_size == 0 || chunk_size > self.data.len() {
93            return Err(QuantRS2Error::InvalidInput(
94                "Invalid chunk size".to_string(),
95            ));
96        }
97
98        for (chunk_idx, chunk) in self.data.chunks_mut(chunk_size).enumerate() {
99            f(chunk, chunk_idx * chunk_size);
100        }
101        Ok(())
102    }
103}
104
105/// Memory usage statistics for quantum states
106pub struct StateMemoryStats {
107    /// Number of complex numbers stored
108    pub num_amplitudes: usize,
109    /// Memory used in bytes
110    pub memory_bytes: usize,
111}
112
113impl EfficientStateVector {
114    /// Get memory usage statistics
115    pub fn memory_stats(&self) -> StateMemoryStats {
116        StateMemoryStats {
117            num_amplitudes: self.data.len(),
118            memory_bytes: self.data.len() * std::mem::size_of::<Complex64>(),
119        }
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126
127    #[test]
128    fn test_efficient_state_vector() {
129        let state = EfficientStateVector::new(3).unwrap();
130        assert_eq!(state.num_qubits(), 3);
131        assert_eq!(state.size(), 8);
132
133        // Check initial state is |000⟩
134        assert_eq!(state.data()[0], Complex64::new(1.0, 0.0));
135        for i in 1..8 {
136            assert_eq!(state.data()[i], Complex64::new(0.0, 0.0));
137        }
138    }
139
140    #[test]
141    fn test_normalization() {
142        let mut state = EfficientStateVector::new(2).unwrap();
143        state.data_mut()[0] = Complex64::new(1.0, 0.0);
144        state.data_mut()[1] = Complex64::new(0.0, 1.0);
145        state.data_mut()[2] = Complex64::new(1.0, 0.0);
146        state.data_mut()[3] = Complex64::new(0.0, -1.0);
147
148        state.normalize().unwrap();
149
150        let norm_sqr: f64 = state.data().iter().map(|c| c.norm_sqr()).sum();
151        assert!((norm_sqr - 1.0).abs() < 1e-10);
152    }
153
154    #[test]
155    fn test_chunk_processing() {
156        let mut state = EfficientStateVector::new(3).unwrap();
157
158        // Process in chunks of 2
159        state
160            .process_chunks(2, |chunk, start_idx| {
161                for (i, amp) in chunk.iter_mut().enumerate() {
162                    *amp = Complex64::new((start_idx + i) as f64, 0.0);
163                }
164            })
165            .unwrap();
166
167        // Verify the result
168        for i in 0..8 {
169            assert_eq!(state.data()[i], Complex64::new(i as f64, 0.0));
170        }
171    }
172}