1use scirs2_core::ndarray::{Array1, Array2};
7use scirs2_core::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 Self::SingleQubitMatrix(_) => 4 * std::mem::size_of::<Complex64>(),
85 Self::TwoQubitMatrix(_) => 16 * std::mem::size_of::<Complex64>(),
86 Self::Matrix(m) => m.len() * std::mem::size_of::<Complex64>(),
87 Self::StateVector(v) => v.len() * std::mem::size_of::<Complex64>(),
88 Self::ExpectationValue(_) => std::mem::size_of::<Complex64>(),
89 Self::Probabilities(p) => p.len() * std::mem::size_of::<f64>(),
90 Self::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 = self
184 .average_access_time_ns
185 .mul_add((self.total_requests - 1) as f64, access_time_ns as f64);
186 self.average_access_time_ns = total_time / self.total_requests as f64;
187
188 let type_stats = self
190 .efficiency_by_type
191 .entry(operation_type.to_string())
192 .or_insert(0.0);
193 if hit {
195 *type_stats = f64::midpoint(*type_stats, 1.0);
196 } else {
197 *type_stats /= 2.0;
198 }
199 }
200}
201
202#[derive(Debug)]
204pub struct QuantumOperationCache {
205 cache: RwLock<HashMap<OperationKey, CachedOperation>>,
207 access_order: Mutex<VecDeque<OperationKey>>,
209 config: CacheConfig,
211 stats: Arc<Mutex<CacheStats>>,
213 last_cleanup: Mutex<Instant>,
215}
216
217impl QuantumOperationCache {
218 pub fn new(config: CacheConfig) -> Self {
220 Self {
221 cache: RwLock::new(HashMap::new()),
222 access_order: Mutex::new(VecDeque::new()),
223 config,
224 stats: Arc::new(Mutex::new(CacheStats::default())),
225 last_cleanup: Mutex::new(Instant::now()),
226 }
227 }
228
229 pub fn get(&self, key: &OperationKey) -> Option<CachedData> {
231 let start_time = Instant::now();
232
233 let result = {
234 let entry_data = {
235 let cache = self.cache.read().unwrap();
236 cache.get(key).map(|entry| entry.data.clone())
237 };
238
239 if entry_data.is_some() {
240 self.update_access_stats(key);
241 }
242
243 entry_data
244 };
245
246 let access_time = start_time.elapsed().as_nanos() as u64;
247
248 if self.config.enable_stats {
250 if let Ok(mut stats) = self.stats.lock() {
251 stats.record_access(&key.operation_type, result.is_some(), access_time);
252 }
253 }
254
255 result
256 }
257
258 pub fn put(&self, key: OperationKey, data: CachedData) {
260 let size = data.estimate_size();
261 let entry = CachedOperation {
262 data,
263 created_at: Instant::now(),
264 access_count: 0,
265 last_accessed: Instant::now(),
266 size_bytes: size,
267 };
268
269 self.maybe_evict(&key, size);
271
272 {
274 let mut cache = self.cache.write().unwrap();
275 cache.insert(key.clone(), entry);
276 }
277
278 {
280 let mut access_order = self.access_order.lock().unwrap();
281 access_order.push_back(key);
282 }
283
284 if self.config.enable_stats {
286 if let Ok(mut stats) = self.stats.lock() {
287 stats.current_entries = self.cache.read().unwrap().len();
288 stats.current_memory_bytes += size;
289 if stats.current_memory_bytes > stats.peak_memory_bytes {
290 stats.peak_memory_bytes = stats.current_memory_bytes;
291 }
292 }
293 }
294
295 self.maybe_cleanup();
297 }
298
299 fn update_access_stats(&self, key: &OperationKey) {
301 let mut cache = self.cache.write().unwrap();
302 if let Some(entry) = cache.get_mut(key) {
303 entry.access_count += 1;
304 entry.last_accessed = Instant::now();
305 }
306
307 if self.config.eviction_policy == EvictionPolicy::LRU {
309 let mut access_order = self.access_order.lock().unwrap();
310 if let Some(pos) = access_order.iter().position(|k| k == key) {
312 access_order.remove(pos);
313 }
314 access_order.push_back(key.clone());
315 }
316 }
317
318 fn maybe_evict(&self, _new_key: &OperationKey, new_size: usize) {
320 let (current_entries, current_memory) = {
321 let cache = self.cache.read().unwrap();
322 (cache.len(), self.get_current_memory_usage())
323 };
324
325 let needs_eviction = current_entries >= self.config.max_entries
326 || current_memory + new_size > self.config.max_memory_bytes;
327
328 if needs_eviction {
329 self.evict_entries();
330 }
331 }
332
333 fn evict_entries(&self) {
335 match self.config.eviction_policy {
336 EvictionPolicy::LRU => self.evict_lru(),
337 EvictionPolicy::LFU => self.evict_lfu(),
338 EvictionPolicy::TTL => self.evict_expired(),
339 EvictionPolicy::FIFO => self.evict_fifo(),
340 EvictionPolicy::Hybrid => self.evict_hybrid(),
341 }
342 }
343
344 fn evict_lru(&self) {
346 let keys_to_evict: Vec<OperationKey> = {
347 let mut access_order = self.access_order.lock().unwrap();
348 let mut keys = Vec::new();
349
350 let max_evictions = (self.config.max_entries / 4).max(1);
352
353 for _ in 0..max_evictions {
354 if let Some(key) = access_order.pop_front() {
355 keys.push(key);
356 } else {
357 break;
358 }
359 }
360 keys
361 };
362
363 self.remove_entries(keys_to_evict);
364 }
365
366 fn evict_lfu(&self) {
368 let keys_to_evict: Vec<OperationKey> = {
369 let cache = self.cache.read().unwrap();
370 let mut entries: Vec<_> = cache.iter().collect();
371
372 entries.sort_by_key(|(_, entry)| entry.access_count);
374
375 let max_evictions = (cache.len() / 4).max(1);
377 entries
378 .into_iter()
379 .take(max_evictions)
380 .map(|(key, _)| key.clone())
381 .collect()
382 };
383
384 self.remove_entries(keys_to_evict);
385 }
386
387 fn evict_expired(&self) {
389 let now = Instant::now();
390 let keys_to_evict: Vec<OperationKey> = {
391 let cache = self.cache.read().unwrap();
392 cache
393 .iter()
394 .filter(|(_, entry)| now.duration_since(entry.created_at) > self.config.ttl)
395 .map(|(key, _)| key.clone())
396 .collect()
397 };
398
399 self.remove_entries(keys_to_evict);
400 }
401
402 fn evict_fifo(&self) {
404 let keys_to_evict: Vec<OperationKey> = {
406 let cache = self.cache.read().unwrap();
407 let mut entries: Vec<_> = cache.iter().collect();
408
409 entries.sort_by_key(|(_, entry)| entry.created_at);
411
412 let max_evictions = (cache.len() / 4).max(1);
414 entries
415 .into_iter()
416 .take(max_evictions)
417 .map(|(key, _)| key.clone())
418 .collect()
419 };
420
421 self.remove_entries(keys_to_evict);
422 }
423
424 fn evict_hybrid(&self) {
426 let keys_to_evict: Vec<OperationKey> = {
427 let cache = self.cache.read().unwrap();
428 let mut entries: Vec<_> = cache.iter().collect();
429
430 entries.sort_by_key(|(_, entry)| {
432 let recency_score = entry.last_accessed.elapsed().as_secs();
433 let frequency_score = 1000 / (entry.access_count + 1); recency_score + frequency_score
435 });
436
437 let max_evictions = (cache.len() / 4).max(1);
439 entries
440 .into_iter()
441 .take(max_evictions)
442 .map(|(key, _)| key.clone())
443 .collect()
444 };
445
446 self.remove_entries(keys_to_evict);
447 }
448
449 fn remove_entries(&self, keys: Vec<OperationKey>) {
451 let mut total_freed_memory = 0usize;
452
453 {
454 let mut cache = self.cache.write().unwrap();
455 for key in &keys {
456 if let Some(entry) = cache.remove(key) {
457 total_freed_memory += entry.size_bytes;
458 }
459 }
460 }
461
462 {
464 let mut access_order = self.access_order.lock().unwrap();
465 access_order.retain(|key| !keys.contains(key));
466 }
467
468 if self.config.enable_stats {
470 if let Ok(mut stats) = self.stats.lock() {
471 stats.evictions += keys.len() as u64;
472 stats.current_entries = self.cache.read().unwrap().len();
473 stats.current_memory_bytes = stats
474 .current_memory_bytes
475 .saturating_sub(total_freed_memory);
476 }
477 }
478 }
479
480 fn get_current_memory_usage(&self) -> usize {
482 let cache = self.cache.read().unwrap();
483 cache.values().map(|entry| entry.size_bytes).sum()
484 }
485
486 fn maybe_cleanup(&self) {
488 if let Ok(mut last_cleanup) = self.last_cleanup.try_lock() {
489 if last_cleanup.elapsed() > self.config.cleanup_interval {
490 self.evict_expired();
491 *last_cleanup = Instant::now();
492 }
493 }
494 }
495
496 pub fn clear(&self) {
498 let mut cache = self.cache.write().unwrap();
499 cache.clear();
500
501 let mut access_order = self.access_order.lock().unwrap();
502 access_order.clear();
503
504 if self.config.enable_stats {
505 if let Ok(mut stats) = self.stats.lock() {
506 stats.current_entries = 0;
507 stats.current_memory_bytes = 0;
508 }
509 }
510 }
511
512 pub fn get_stats(&self) -> CacheStats {
514 self.stats.lock().unwrap().clone()
515 }
516
517 pub const fn get_config(&self) -> &CacheConfig {
519 &self.config
520 }
521}
522
523pub struct GateMatrixCache {
525 cache: QuantumOperationCache,
527}
528
529impl Default for GateMatrixCache {
530 fn default() -> Self {
531 Self::new()
532 }
533}
534
535impl GateMatrixCache {
536 pub fn new() -> Self {
538 let config = CacheConfig {
539 max_entries: 5000,
540 max_memory_bytes: 64 * 1024 * 1024, eviction_policy: EvictionPolicy::LRU,
542 ttl: Duration::from_secs(1800), enable_stats: true,
544 cleanup_interval: Duration::from_secs(120), };
546
547 Self {
548 cache: QuantumOperationCache::new(config),
549 }
550 }
551
552 pub fn get_single_qubit_gate(
554 &self,
555 gate_name: &str,
556 parameters: &[f64],
557 ) -> Option<Array2<Complex64>> {
558 let key = OperationKey {
559 operation_type: format!("single_qubit_{gate_name}"),
560 parameters: parameters.to_vec(),
561 qubits: vec![],
562 metadata: HashMap::new(),
563 };
564
565 match self.cache.get(&key) {
566 Some(CachedData::SingleQubitMatrix(matrix)) => Some(matrix),
567 Some(CachedData::Matrix(matrix)) if matrix.shape() == [2, 2] => Some(matrix),
568 _ => None,
569 }
570 }
571
572 pub fn put_single_qubit_gate(
574 &self,
575 gate_name: &str,
576 parameters: &[f64],
577 matrix: Array2<Complex64>,
578 ) {
579 let key = OperationKey {
580 operation_type: format!("single_qubit_{gate_name}"),
581 parameters: parameters.to_vec(),
582 qubits: vec![],
583 metadata: HashMap::new(),
584 };
585
586 self.cache.put(key, CachedData::SingleQubitMatrix(matrix));
587 }
588
589 pub fn get_two_qubit_gate(
591 &self,
592 gate_name: &str,
593 parameters: &[f64],
594 ) -> Option<Array2<Complex64>> {
595 let key = OperationKey {
596 operation_type: format!("two_qubit_{gate_name}"),
597 parameters: parameters.to_vec(),
598 qubits: vec![],
599 metadata: HashMap::new(),
600 };
601
602 match self.cache.get(&key) {
603 Some(CachedData::TwoQubitMatrix(matrix)) => Some(matrix),
604 Some(CachedData::Matrix(matrix)) if matrix.shape() == [4, 4] => Some(matrix),
605 _ => None,
606 }
607 }
608
609 pub fn put_two_qubit_gate(
611 &self,
612 gate_name: &str,
613 parameters: &[f64],
614 matrix: Array2<Complex64>,
615 ) {
616 let key = OperationKey {
617 operation_type: format!("two_qubit_{gate_name}"),
618 parameters: parameters.to_vec(),
619 qubits: vec![],
620 metadata: HashMap::new(),
621 };
622
623 self.cache.put(key, CachedData::TwoQubitMatrix(matrix));
624 }
625
626 pub fn get_stats(&self) -> CacheStats {
628 self.cache.get_stats()
629 }
630}
631
632#[cfg(test)]
633mod tests {
634 use super::*;
635 use scirs2_core::ndarray::array;
636
637 #[test]
638 fn test_operation_cache() {
639 let config = CacheConfig::default();
640 let cache = QuantumOperationCache::new(config);
641
642 let key = OperationKey {
643 operation_type: "test_op".to_string(),
644 parameters: vec![0.5, 1.0],
645 qubits: vec![0, 1],
646 metadata: HashMap::new(),
647 };
648
649 let matrix = array![
650 [Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)],
651 [Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)]
652 ];
653
654 assert!(cache.get(&key).is_none());
656
657 cache.put(key.clone(), CachedData::Matrix(matrix.clone()));
659
660 match cache.get(&key) {
661 Some(CachedData::Matrix(cached_matrix)) => {
662 assert_eq!(cached_matrix, matrix);
663 }
664 _ => panic!("Expected cached matrix"),
665 }
666
667 let stats = cache.get_stats();
668 assert_eq!(stats.total_requests, 2);
669 assert_eq!(stats.hits, 1);
670 assert_eq!(stats.misses, 1);
671 }
672
673 #[test]
674 fn test_gate_matrix_cache() {
675 let cache = GateMatrixCache::new();
676
677 let pauli_x = array![
678 [Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)],
679 [Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)]
680 ];
681
682 assert!(cache.get_single_qubit_gate("X", &[]).is_none());
684
685 cache.put_single_qubit_gate("X", &[], pauli_x.clone());
687
688 let cached_matrix = cache.get_single_qubit_gate("X", &[]).unwrap();
689 assert_eq!(cached_matrix, pauli_x);
690 }
691
692 #[test]
693 fn test_eviction_policies() {
694 let mut config = CacheConfig::default();
695 config.max_entries = 2;
696
697 let cache = QuantumOperationCache::new(config);
698
699 for i in 0..3 {
701 let key = OperationKey {
702 operation_type: format!("test_{i}"),
703 parameters: vec![i as f64],
704 qubits: vec![i],
705 metadata: HashMap::new(),
706 };
707 cache.put(
708 key,
709 CachedData::ExpectationValue(Complex64::new(i as f64, 0.0)),
710 );
711 }
712
713 let stats = cache.get_stats();
715 assert_eq!(stats.current_entries, 2);
716 assert!(stats.evictions > 0);
717 }
718}