quantrs2_core/optimizations/
memory_optimization.rs

1//! Memory Optimization for Quantum Simulations using SciRS2 Beta.1
2//!
3//! This module provides memory-efficient algorithms and buffer management
4//! for large-scale quantum state simulations.
5
6use crate::error::QuantRS2Result;
7use scirs2_core::memory::{metrics, BufferPool, ChunkProcessor2D};
8use scirs2_core::ndarray::{Array1, Array2, ArrayViewMut1};
9use scirs2_core::Complex64;
10use std::sync::{Arc, Mutex, OnceLock};
11
12/// Quantum-specific buffer pool for state vectors and matrices
13pub struct QuantumBufferPool {
14    /// Pool for complex64 state vector components
15    state_vector_pool: Arc<Mutex<BufferPool<Complex64>>>,
16    /// Pool for real-valued probability arrays
17    probability_pool: Arc<Mutex<BufferPool<f64>>>,
18    /// Pool for temporary computation buffers
19    temp_buffer_pool: Arc<Mutex<BufferPool<Complex64>>>,
20    /// Usage statistics
21    allocations: Arc<Mutex<u64>>,
22    deallocations: Arc<Mutex<u64>>,
23    peak_memory_usage: Arc<Mutex<usize>>,
24}
25
26impl Default for QuantumBufferPool {
27    fn default() -> Self {
28        Self::new()
29    }
30}
31
32impl QuantumBufferPool {
33    /// Create a new quantum buffer pool
34    pub fn new() -> Self {
35        Self {
36            state_vector_pool: Arc::new(Mutex::new(BufferPool::new())),
37            probability_pool: Arc::new(Mutex::new(BufferPool::new())),
38            temp_buffer_pool: Arc::new(Mutex::new(BufferPool::new())),
39            allocations: Arc::new(Mutex::new(0)),
40            deallocations: Arc::new(Mutex::new(0)),
41            peak_memory_usage: Arc::new(Mutex::new(0)),
42        }
43    }
44
45    /// Acquire a state vector buffer from the pool
46    pub fn acquire_state_vector(&self, size: usize) -> Vec<Complex64> {
47        metrics::track_allocation("QuantumStateVector", size * 16, 0); // Complex64 is 16 bytes
48        *self.allocations.lock().unwrap() += 1;
49
50        let current_usage = size * 16;
51        let mut peak = self.peak_memory_usage.lock().unwrap();
52        if current_usage > *peak {
53            *peak = current_usage;
54        }
55
56        self.state_vector_pool.lock().unwrap().acquire_vec(size)
57    }
58
59    /// Release a state vector buffer back to the pool
60    pub fn release_state_vector(&self, buffer: Vec<Complex64>) {
61        let size = buffer.len();
62        metrics::track_deallocation("QuantumStateVector", size * 16, 0);
63        *self.deallocations.lock().unwrap() += 1;
64
65        self.state_vector_pool.lock().unwrap().release_vec(buffer);
66    }
67
68    /// Acquire a probability buffer from the pool
69    pub fn acquire_probability_buffer(&self, size: usize) -> Vec<f64> {
70        metrics::track_allocation("ProbabilityBuffer", size * 8, 0); // f64 is 8 bytes
71        *self.allocations.lock().unwrap() += 1;
72
73        self.probability_pool.lock().unwrap().acquire_vec(size)
74    }
75
76    /// Release a probability buffer back to the pool
77    pub fn release_probability_buffer(&self, buffer: Vec<f64>) {
78        let size = buffer.len();
79        metrics::track_deallocation("ProbabilityBuffer", size * 8, 0);
80        *self.deallocations.lock().unwrap() += 1;
81
82        self.probability_pool.lock().unwrap().release_vec(buffer);
83    }
84
85    /// Get buffer pool statistics
86    pub fn get_stats(&self) -> MemoryUsageStats {
87        MemoryUsageStats {
88            total_allocations: *self.allocations.lock().unwrap(),
89            total_deallocations: *self.deallocations.lock().unwrap(),
90            peak_memory_usage_bytes: *self.peak_memory_usage.lock().unwrap(),
91            active_buffers: self
92                .allocations
93                .lock()
94                .unwrap()
95                .saturating_sub(*self.deallocations.lock().unwrap()),
96        }
97    }
98}
99
100/// Memory usage statistics
101#[derive(Debug, Clone)]
102pub struct MemoryUsageStats {
103    pub total_allocations: u64,
104    pub total_deallocations: u64,
105    pub peak_memory_usage_bytes: usize,
106    pub active_buffers: u64,
107}
108
109/// Optimized state vector manager for large quantum systems
110pub struct StateVectorManager {
111    /// Current state vector
112    state: Option<Vec<Complex64>>,
113    /// Number of qubits
114    num_qubits: usize,
115    /// Buffer pool reference
116    pool: Arc<QuantumBufferPool>,
117    /// Whether to use chunked processing for large states
118    use_chunked_processing: bool,
119}
120
121impl StateVectorManager {
122    /// Create a new state vector manager
123    pub fn new(num_qubits: usize, pool: Arc<QuantumBufferPool>) -> Self {
124        let use_chunked_processing = num_qubits > 20; // Use chunking for >20 qubits (~16M elements)
125
126        Self {
127            state: None,
128            num_qubits,
129            pool,
130            use_chunked_processing,
131        }
132    }
133
134    /// Initialize the state vector to |00...0⟩
135    pub fn initialize_zero_state(&mut self) -> QuantRS2Result<()> {
136        let size = 1 << self.num_qubits;
137        let mut state = self.pool.acquire_state_vector(size);
138
139        // Initialize to zero state
140        state.fill(Complex64::new(0.0, 0.0));
141        state[0] = Complex64::new(1.0, 0.0);
142
143        self.state = Some(state);
144        Ok(())
145    }
146
147    /// Apply a single-qubit gate with memory optimization
148    pub fn apply_single_qubit_gate(
149        &mut self,
150        gate_matrix: &[Complex64; 4],
151        qubit_idx: usize,
152    ) -> QuantRS2Result<()> {
153        let use_chunked = self.use_chunked_processing;
154        let pool = self.pool.clone();
155
156        let state = self.state.as_mut().ok_or_else(|| {
157            crate::error::QuantRS2Error::InvalidInput("State not initialized".to_string())
158        })?;
159
160        if use_chunked {
161            Self::apply_single_qubit_gate_chunked_impl(&pool, state, gate_matrix, qubit_idx)
162        } else {
163            Self::apply_single_qubit_gate_direct_impl(&pool, state, gate_matrix, qubit_idx)
164        }
165    }
166
167    /// Direct application for smaller state vectors
168    fn apply_single_qubit_gate_direct_impl(
169        pool: &QuantumBufferPool,
170        state: &mut [Complex64],
171        gate_matrix: &[Complex64; 4],
172        qubit_idx: usize,
173    ) -> QuantRS2Result<()> {
174        let size = state.len();
175        let target_bit = 1 << qubit_idx;
176
177        // Acquire temporary buffer for parallel processing
178        let mut temp_buffer = pool.acquire_state_vector(size);
179        temp_buffer.copy_from_slice(state);
180
181        // Apply gate using SIMD-optimized operations
182        for i in 0..size {
183            if i & target_bit == 0 {
184                let j = i | target_bit;
185                let amp_0 = temp_buffer[i];
186                let amp_1 = temp_buffer[j];
187
188                state[i] = gate_matrix[0] * amp_0 + gate_matrix[1] * amp_1;
189                state[j] = gate_matrix[2] * amp_0 + gate_matrix[3] * amp_1;
190            }
191        }
192
193        pool.release_state_vector(temp_buffer);
194        Ok(())
195    }
196
197    /// Chunked application for large state vectors
198    fn apply_single_qubit_gate_chunked_impl(
199        pool: &QuantumBufferPool,
200        state: &mut [Complex64],
201        gate_matrix: &[Complex64; 4],
202        qubit_idx: usize,
203    ) -> QuantRS2Result<()> {
204        let chunk_size = 1 << 18; // Process in 256K element chunks
205        let target_bit = 1 << qubit_idx;
206
207        // Create a temporary copy for atomic operations
208        let mut temp_state = pool.acquire_state_vector(state.len());
209        temp_state.copy_from_slice(state);
210
211        // Apply gate using temporary buffer
212        for i in 0..state.len() {
213            if i & target_bit == 0 {
214                let j = i | target_bit;
215                if j < state.len() {
216                    let amp_0 = temp_state[i];
217                    let amp_1 = temp_state[j];
218
219                    state[i] = gate_matrix[0] * amp_0 + gate_matrix[1] * amp_1;
220                    state[j] = gate_matrix[2] * amp_0 + gate_matrix[3] * amp_1;
221                }
222            }
223        }
224
225        pool.release_state_vector(temp_state);
226
227        Ok(())
228    }
229
230    /// Get measurement probabilities with memory optimization
231    pub fn get_probabilities(&self) -> QuantRS2Result<Vec<f64>> {
232        let state = self.state.as_ref().ok_or_else(|| {
233            crate::error::QuantRS2Error::InvalidInput("State not initialized".to_string())
234        })?;
235
236        let mut probabilities = self.pool.acquire_probability_buffer(state.len());
237
238        // Compute probabilities using SIMD operations
239        for (i, &amplitude) in state.iter().enumerate() {
240            probabilities[i] = amplitude.norm_sqr();
241        }
242
243        Ok(probabilities)
244    }
245
246    /// Release resources when done
247    pub fn finalize(mut self) {
248        if let Some(state) = self.state.take() {
249            self.pool.release_state_vector(state);
250        }
251    }
252}
253
254/// Global quantum buffer pool
255static GLOBAL_QUANTUM_POOL: OnceLock<QuantumBufferPool> = OnceLock::new();
256
257/// Get the global quantum buffer pool
258pub fn global_quantum_buffer_pool() -> &'static QuantumBufferPool {
259    GLOBAL_QUANTUM_POOL.get_or_init(QuantumBufferPool::new)
260}
261
262/// Initialize buffer pools
263pub fn initialize_buffer_pools() {
264    // Force initialization of the global pool
265    let _pool = global_quantum_buffer_pool();
266}
267
268/// Optimized state vector allocation function
269pub fn optimized_state_vector_allocation(num_qubits: usize) -> StateVectorManager {
270    let pool = Arc::new(QuantumBufferPool::new());
271    StateVectorManager::new(num_qubits, pool)
272}
273
274/// Get global memory usage statistics
275pub fn get_memory_usage_stats() -> MemoryUsageStats {
276    global_quantum_buffer_pool().get_stats()
277}
278
279#[cfg(test)]
280mod tests {
281    use super::*;
282
283    #[test]
284    #[ignore] // Slow test: SciRS2 metrics tracking is unexpectedly slow (~71s)
285    fn test_buffer_pool_basic_functionality() {
286        let pool = QuantumBufferPool::new();
287
288        // Test state vector acquisition and release
289        let buffer = pool.acquire_state_vector(100);
290        assert_eq!(buffer.len(), 100);
291
292        let stats_before = pool.get_stats();
293        pool.release_state_vector(buffer);
294        let stats_after = pool.get_stats();
295
296        assert_eq!(
297            stats_after.total_allocations,
298            stats_before.total_allocations
299        );
300        assert_eq!(
301            stats_after.total_deallocations,
302            stats_before.total_deallocations + 1
303        );
304    }
305
306    #[test]
307    fn test_state_vector_manager() {
308        let pool = Arc::new(QuantumBufferPool::new());
309        let mut manager = StateVectorManager::new(2, pool); // 2-qubit system
310
311        // Initialize state
312        manager.initialize_zero_state().unwrap();
313
314        // Apply Hadamard-like gate to first qubit
315        let h_gate = [
316            Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0),
317            Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0),
318            Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0),
319            Complex64::new(-1.0 / 2.0_f64.sqrt(), 0.0),
320        ];
321
322        manager.apply_single_qubit_gate(&h_gate, 0).unwrap();
323
324        // Get probabilities
325        let probabilities = manager.get_probabilities().unwrap();
326        assert_eq!(probabilities.len(), 4);
327
328        // Should be in equal superposition on first qubit
329        assert!((probabilities[0] - 0.5).abs() < 1e-10); // |00⟩
330        assert!((probabilities[1] - 0.5).abs() < 1e-10); // |01⟩
331        assert!((probabilities[2] - 0.0).abs() < 1e-10); // |10⟩
332        assert!((probabilities[3] - 0.0).abs() < 1e-10); // |11⟩
333
334        manager.finalize();
335    }
336
337    #[test]
338    fn test_chunked_processing_threshold() {
339        let pool = Arc::new(QuantumBufferPool::new());
340
341        // Small system should not use chunking
342        let small_manager = StateVectorManager::new(10, pool.clone());
343        assert!(!small_manager.use_chunked_processing);
344
345        // Large system should use chunking
346        let large_manager = StateVectorManager::new(25, pool);
347        assert!(large_manager.use_chunked_processing);
348    }
349}