1use ndarray::{Array1, Array2};
7use num_complex::Complex64;
8use serde::{Deserialize, Serialize};
9use std::collections::{HashMap, VecDeque};
10use std::hash::{Hash, Hasher};
11use std::sync::{Arc, Mutex, RwLock};
12use std::time::{Duration, Instant};
13
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
16pub struct OperationKey {
17 pub operation_type: String,
19 pub parameters: Vec<f64>,
21 pub qubits: Vec<usize>,
23 pub metadata: HashMap<String, String>,
25}
26
27impl Hash for OperationKey {
28 fn hash<H: Hasher>(&self, state: &mut H) {
29 self.operation_type.hash(state);
30 for param in &self.parameters {
32 let rounded = (param * 1e12).round() as i64;
34 rounded.hash(state);
35 }
36 self.qubits.hash(state);
37 for (k, v) in &self.metadata {
38 k.hash(state);
39 v.hash(state);
40 }
41 }
42}
43
44impl Eq for OperationKey {}
45
46#[derive(Debug, Clone)]
48pub struct CachedOperation {
49 pub data: CachedData,
51 pub created_at: Instant,
53 pub access_count: u64,
55 pub last_accessed: Instant,
57 pub size_bytes: usize,
59}
60
61#[derive(Debug, Clone)]
63pub enum CachedData {
64 SingleQubitMatrix(Array2<Complex64>),
66 TwoQubitMatrix(Array2<Complex64>),
68 Matrix(Array2<Complex64>),
70 StateVector(Array1<Complex64>),
72 ExpectationValue(Complex64),
74 Probabilities(Vec<f64>),
76 Custom(Vec<u8>),
78}
79
80impl CachedData {
81 pub fn estimate_size(&self) -> usize {
83 match self {
84 CachedData::SingleQubitMatrix(_) => 4 * std::mem::size_of::<Complex64>(),
85 CachedData::TwoQubitMatrix(_) => 16 * std::mem::size_of::<Complex64>(),
86 CachedData::Matrix(m) => m.len() * std::mem::size_of::<Complex64>(),
87 CachedData::StateVector(v) => v.len() * std::mem::size_of::<Complex64>(),
88 CachedData::ExpectationValue(_) => std::mem::size_of::<Complex64>(),
89 CachedData::Probabilities(p) => p.len() * std::mem::size_of::<f64>(),
90 CachedData::Custom(data) => data.len(),
91 }
92 }
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub enum EvictionPolicy {
98 LRU,
100 LFU,
102 TTL,
104 FIFO,
106 Hybrid,
108}
109
110#[derive(Debug, Clone)]
112pub struct CacheConfig {
113 pub max_entries: usize,
115 pub max_memory_bytes: usize,
117 pub eviction_policy: EvictionPolicy,
119 pub ttl: Duration,
121 pub enable_stats: bool,
123 pub cleanup_interval: Duration,
125}
126
127impl Default for CacheConfig {
128 fn default() -> Self {
129 Self {
130 max_entries: 10000,
131 max_memory_bytes: 256 * 1024 * 1024, eviction_policy: EvictionPolicy::LRU,
133 ttl: Duration::from_secs(3600), enable_stats: true,
135 cleanup_interval: Duration::from_secs(60), }
137 }
138}
139
140#[derive(Debug, Clone, Default)]
142pub struct CacheStats {
143 pub total_requests: u64,
145 pub hits: u64,
147 pub misses: u64,
149 pub evictions: u64,
151 pub current_entries: usize,
153 pub current_memory_bytes: usize,
155 pub peak_memory_bytes: usize,
157 pub average_access_time_ns: f64,
159 pub efficiency_by_type: HashMap<String, f64>,
161}
162
163impl CacheStats {
164 pub fn hit_ratio(&self) -> f64 {
166 if self.total_requests == 0 {
167 0.0
168 } else {
169 self.hits as f64 / self.total_requests as f64
170 }
171 }
172
173 pub fn record_access(&mut self, operation_type: &str, hit: bool, access_time_ns: u64) {
175 self.total_requests += 1;
176 if hit {
177 self.hits += 1;
178 } else {
179 self.misses += 1;
180 }
181
182 let total_time =
184 self.average_access_time_ns * (self.total_requests - 1) as f64 + access_time_ns as f64;
185 self.average_access_time_ns = total_time / self.total_requests as f64;
186
187 let type_stats = self
189 .efficiency_by_type
190 .entry(operation_type.to_string())
191 .or_insert(0.0);
192 if hit {
194 *type_stats = (*type_stats + 1.0) / 2.0;
195 } else {
196 *type_stats = *type_stats / 2.0;
197 }
198 }
199}
200
201#[derive(Debug)]
203pub struct QuantumOperationCache {
204 cache: RwLock<HashMap<OperationKey, CachedOperation>>,
206 access_order: Mutex<VecDeque<OperationKey>>,
208 config: CacheConfig,
210 stats: Arc<Mutex<CacheStats>>,
212 last_cleanup: Mutex<Instant>,
214}
215
216impl QuantumOperationCache {
217 pub fn new(config: CacheConfig) -> Self {
219 Self {
220 cache: RwLock::new(HashMap::new()),
221 access_order: Mutex::new(VecDeque::new()),
222 config,
223 stats: Arc::new(Mutex::new(CacheStats::default())),
224 last_cleanup: Mutex::new(Instant::now()),
225 }
226 }
227
228 pub fn get(&self, key: &OperationKey) -> Option<CachedData> {
230 let start_time = Instant::now();
231
232 let result = {
233 let entry_data = {
234 let cache = self.cache.read().unwrap();
235 cache.get(key).map(|entry| entry.data.clone())
236 };
237
238 if entry_data.is_some() {
239 self.update_access_stats(key);
240 }
241
242 entry_data
243 };
244
245 let access_time = start_time.elapsed().as_nanos() as u64;
246
247 if self.config.enable_stats {
249 if let Ok(mut stats) = self.stats.lock() {
250 stats.record_access(&key.operation_type, result.is_some(), access_time);
251 }
252 }
253
254 result
255 }
256
257 pub fn put(&self, key: OperationKey, data: CachedData) {
259 let size = data.estimate_size();
260 let entry = CachedOperation {
261 data,
262 created_at: Instant::now(),
263 access_count: 0,
264 last_accessed: Instant::now(),
265 size_bytes: size,
266 };
267
268 self.maybe_evict(&key, size);
270
271 {
273 let mut cache = self.cache.write().unwrap();
274 cache.insert(key.clone(), entry);
275 }
276
277 {
279 let mut access_order = self.access_order.lock().unwrap();
280 access_order.push_back(key);
281 }
282
283 if self.config.enable_stats {
285 if let Ok(mut stats) = self.stats.lock() {
286 stats.current_entries = self.cache.read().unwrap().len();
287 stats.current_memory_bytes += size;
288 if stats.current_memory_bytes > stats.peak_memory_bytes {
289 stats.peak_memory_bytes = stats.current_memory_bytes;
290 }
291 }
292 }
293
294 self.maybe_cleanup();
296 }
297
298 fn update_access_stats(&self, key: &OperationKey) {
300 let mut cache = self.cache.write().unwrap();
301 if let Some(entry) = cache.get_mut(key) {
302 entry.access_count += 1;
303 entry.last_accessed = Instant::now();
304 }
305
306 if self.config.eviction_policy == EvictionPolicy::LRU {
308 let mut access_order = self.access_order.lock().unwrap();
309 if let Some(pos) = access_order.iter().position(|k| k == key) {
311 access_order.remove(pos);
312 }
313 access_order.push_back(key.clone());
314 }
315 }
316
317 fn maybe_evict(&self, _new_key: &OperationKey, new_size: usize) {
319 let (current_entries, current_memory) = {
320 let cache = self.cache.read().unwrap();
321 (cache.len(), self.get_current_memory_usage())
322 };
323
324 let needs_eviction = current_entries >= self.config.max_entries
325 || current_memory + new_size > self.config.max_memory_bytes;
326
327 if needs_eviction {
328 self.evict_entries();
329 }
330 }
331
332 fn evict_entries(&self) {
334 match self.config.eviction_policy {
335 EvictionPolicy::LRU => self.evict_lru(),
336 EvictionPolicy::LFU => self.evict_lfu(),
337 EvictionPolicy::TTL => self.evict_expired(),
338 EvictionPolicy::FIFO => self.evict_fifo(),
339 EvictionPolicy::Hybrid => self.evict_hybrid(),
340 }
341 }
342
343 fn evict_lru(&self) {
345 let keys_to_evict: Vec<OperationKey> = {
346 let mut access_order = self.access_order.lock().unwrap();
347 let mut keys = Vec::new();
348
349 let max_evictions = (self.config.max_entries / 4).max(1);
351
352 for _ in 0..max_evictions {
353 if let Some(key) = access_order.pop_front() {
354 keys.push(key);
355 } else {
356 break;
357 }
358 }
359 keys
360 };
361
362 self.remove_entries(keys_to_evict);
363 }
364
365 fn evict_lfu(&self) {
367 let keys_to_evict: Vec<OperationKey> = {
368 let cache = self.cache.read().unwrap();
369 let mut entries: Vec<_> = cache.iter().collect();
370
371 entries.sort_by_key(|(_, entry)| entry.access_count);
373
374 let max_evictions = (cache.len() / 4).max(1);
376 entries
377 .into_iter()
378 .take(max_evictions)
379 .map(|(key, _)| key.clone())
380 .collect()
381 };
382
383 self.remove_entries(keys_to_evict);
384 }
385
386 fn evict_expired(&self) {
388 let now = Instant::now();
389 let keys_to_evict: Vec<OperationKey> = {
390 let cache = self.cache.read().unwrap();
391 cache
392 .iter()
393 .filter(|(_, entry)| now.duration_since(entry.created_at) > self.config.ttl)
394 .map(|(key, _)| key.clone())
395 .collect()
396 };
397
398 self.remove_entries(keys_to_evict);
399 }
400
401 fn evict_fifo(&self) {
403 let keys_to_evict: Vec<OperationKey> = {
405 let cache = self.cache.read().unwrap();
406 let mut entries: Vec<_> = cache.iter().collect();
407
408 entries.sort_by_key(|(_, entry)| entry.created_at);
410
411 let max_evictions = (cache.len() / 4).max(1);
413 entries
414 .into_iter()
415 .take(max_evictions)
416 .map(|(key, _)| key.clone())
417 .collect()
418 };
419
420 self.remove_entries(keys_to_evict);
421 }
422
423 fn evict_hybrid(&self) {
425 let keys_to_evict: Vec<OperationKey> = {
426 let cache = self.cache.read().unwrap();
427 let mut entries: Vec<_> = cache.iter().collect();
428
429 entries.sort_by_key(|(_, entry)| {
431 let recency_score = entry.last_accessed.elapsed().as_secs();
432 let frequency_score = 1000 / (entry.access_count + 1); recency_score + frequency_score
434 });
435
436 let max_evictions = (cache.len() / 4).max(1);
438 entries
439 .into_iter()
440 .take(max_evictions)
441 .map(|(key, _)| key.clone())
442 .collect()
443 };
444
445 self.remove_entries(keys_to_evict);
446 }
447
448 fn remove_entries(&self, keys: Vec<OperationKey>) {
450 let mut total_freed_memory = 0usize;
451
452 {
453 let mut cache = self.cache.write().unwrap();
454 for key in &keys {
455 if let Some(entry) = cache.remove(key) {
456 total_freed_memory += entry.size_bytes;
457 }
458 }
459 }
460
461 {
463 let mut access_order = self.access_order.lock().unwrap();
464 access_order.retain(|key| !keys.contains(key));
465 }
466
467 if self.config.enable_stats {
469 if let Ok(mut stats) = self.stats.lock() {
470 stats.evictions += keys.len() as u64;
471 stats.current_entries = self.cache.read().unwrap().len();
472 stats.current_memory_bytes = stats
473 .current_memory_bytes
474 .saturating_sub(total_freed_memory);
475 }
476 }
477 }
478
479 fn get_current_memory_usage(&self) -> usize {
481 let cache = self.cache.read().unwrap();
482 cache.values().map(|entry| entry.size_bytes).sum()
483 }
484
485 fn maybe_cleanup(&self) {
487 if let Ok(mut last_cleanup) = self.last_cleanup.try_lock() {
488 if last_cleanup.elapsed() > self.config.cleanup_interval {
489 self.evict_expired();
490 *last_cleanup = Instant::now();
491 }
492 }
493 }
494
495 pub fn clear(&self) {
497 let mut cache = self.cache.write().unwrap();
498 cache.clear();
499
500 let mut access_order = self.access_order.lock().unwrap();
501 access_order.clear();
502
503 if self.config.enable_stats {
504 if let Ok(mut stats) = self.stats.lock() {
505 stats.current_entries = 0;
506 stats.current_memory_bytes = 0;
507 }
508 }
509 }
510
511 pub fn get_stats(&self) -> CacheStats {
513 self.stats.lock().unwrap().clone()
514 }
515
516 pub fn get_config(&self) -> &CacheConfig {
518 &self.config
519 }
520}
521
522pub struct GateMatrixCache {
524 cache: QuantumOperationCache,
526}
527
528impl GateMatrixCache {
529 pub fn new() -> Self {
531 let config = CacheConfig {
532 max_entries: 5000,
533 max_memory_bytes: 64 * 1024 * 1024, eviction_policy: EvictionPolicy::LRU,
535 ttl: Duration::from_secs(1800), enable_stats: true,
537 cleanup_interval: Duration::from_secs(120), };
539
540 Self {
541 cache: QuantumOperationCache::new(config),
542 }
543 }
544
545 pub fn get_single_qubit_gate(
547 &self,
548 gate_name: &str,
549 parameters: &[f64],
550 ) -> Option<Array2<Complex64>> {
551 let key = OperationKey {
552 operation_type: format!("single_qubit_{}", gate_name),
553 parameters: parameters.to_vec(),
554 qubits: vec![],
555 metadata: HashMap::new(),
556 };
557
558 match self.cache.get(&key) {
559 Some(CachedData::SingleQubitMatrix(matrix)) => Some(matrix),
560 Some(CachedData::Matrix(matrix)) if matrix.shape() == [2, 2] => Some(matrix),
561 _ => None,
562 }
563 }
564
565 pub fn put_single_qubit_gate(
567 &self,
568 gate_name: &str,
569 parameters: &[f64],
570 matrix: Array2<Complex64>,
571 ) {
572 let key = OperationKey {
573 operation_type: format!("single_qubit_{}", gate_name),
574 parameters: parameters.to_vec(),
575 qubits: vec![],
576 metadata: HashMap::new(),
577 };
578
579 self.cache.put(key, CachedData::SingleQubitMatrix(matrix));
580 }
581
582 pub fn get_two_qubit_gate(
584 &self,
585 gate_name: &str,
586 parameters: &[f64],
587 ) -> Option<Array2<Complex64>> {
588 let key = OperationKey {
589 operation_type: format!("two_qubit_{}", gate_name),
590 parameters: parameters.to_vec(),
591 qubits: vec![],
592 metadata: HashMap::new(),
593 };
594
595 match self.cache.get(&key) {
596 Some(CachedData::TwoQubitMatrix(matrix)) => Some(matrix),
597 Some(CachedData::Matrix(matrix)) if matrix.shape() == [4, 4] => Some(matrix),
598 _ => None,
599 }
600 }
601
602 pub fn put_two_qubit_gate(
604 &self,
605 gate_name: &str,
606 parameters: &[f64],
607 matrix: Array2<Complex64>,
608 ) {
609 let key = OperationKey {
610 operation_type: format!("two_qubit_{}", gate_name),
611 parameters: parameters.to_vec(),
612 qubits: vec![],
613 metadata: HashMap::new(),
614 };
615
616 self.cache.put(key, CachedData::TwoQubitMatrix(matrix));
617 }
618
619 pub fn get_stats(&self) -> CacheStats {
621 self.cache.get_stats()
622 }
623}
624
625#[cfg(test)]
626mod tests {
627 use super::*;
628 use ndarray::array;
629
630 #[test]
631 fn test_operation_cache() {
632 let config = CacheConfig::default();
633 let cache = QuantumOperationCache::new(config);
634
635 let key = OperationKey {
636 operation_type: "test_op".to_string(),
637 parameters: vec![0.5, 1.0],
638 qubits: vec![0, 1],
639 metadata: HashMap::new(),
640 };
641
642 let matrix = array![
643 [Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)],
644 [Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)]
645 ];
646
647 assert!(cache.get(&key).is_none());
649
650 cache.put(key.clone(), CachedData::Matrix(matrix.clone()));
652
653 match cache.get(&key) {
654 Some(CachedData::Matrix(cached_matrix)) => {
655 assert_eq!(cached_matrix, matrix);
656 }
657 _ => panic!("Expected cached matrix"),
658 }
659
660 let stats = cache.get_stats();
661 assert_eq!(stats.total_requests, 2);
662 assert_eq!(stats.hits, 1);
663 assert_eq!(stats.misses, 1);
664 }
665
666 #[test]
667 fn test_gate_matrix_cache() {
668 let cache = GateMatrixCache::new();
669
670 let pauli_x = array![
671 [Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)],
672 [Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)]
673 ];
674
675 assert!(cache.get_single_qubit_gate("X", &[]).is_none());
677
678 cache.put_single_qubit_gate("X", &[], pauli_x.clone());
680
681 let cached_matrix = cache.get_single_qubit_gate("X", &[]).unwrap();
682 assert_eq!(cached_matrix, pauli_x);
683 }
684
685 #[test]
686 fn test_eviction_policies() {
687 let mut config = CacheConfig::default();
688 config.max_entries = 2;
689
690 let cache = QuantumOperationCache::new(config);
691
692 for i in 0..3 {
694 let key = OperationKey {
695 operation_type: format!("test_{}", i),
696 parameters: vec![i as f64],
697 qubits: vec![i],
698 metadata: HashMap::new(),
699 };
700 cache.put(
701 key,
702 CachedData::ExpectationValue(Complex64::new(i as f64, 0.0)),
703 );
704 }
705
706 let stats = cache.get_stats();
708 assert_eq!(stats.current_entries, 2);
709 assert!(stats.evictions > 0);
710 }
711}