1use crate::{
8 error::{QuantRS2Error, QuantRS2Result},
9 gate::GateOp,
10};
11use scirs2_core::Complex64;
12use serde::{Deserialize, Serialize};
13use std::{
14 collections::{HashMap, VecDeque},
15 fs::{self, File},
16 io::{BufReader, BufWriter, Write},
17 path::{Path, PathBuf},
18 sync::{Arc, OnceLock, RwLock},
19 time::{Duration, SystemTime, UNIX_EPOCH},
20};
21use std::collections::hash_map::DefaultHasher;
23use std::hash::{Hash, Hasher};
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct CompiledGate {
28 pub gate_id: String,
30 pub matrix: Vec<Complex64>,
32 pub num_qubits: usize,
34 pub optimizations: GateOptimizations,
36 pub metadata: CompilationMetadata,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct GateOptimizations {
43 pub diagonal: Option<Vec<Complex64>>,
45 pub decomposition: Option<GateDecomposition>,
47 pub simd_layout: Option<SimdLayout>,
49 pub gpu_kernel_id: Option<String>,
51 pub tensor_network: Option<TensorNetworkRep>,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct GateDecomposition {
58 pub gates: Vec<String>,
60 pub parameters: Vec<Vec<f64>>,
62 pub targets: Vec<Vec<usize>>,
64 pub gate_count: usize,
66 pub error: f64,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct SimdLayout {
73 pub layout_type: String,
75 pub data: Vec<Complex64>,
77 pub stride: usize,
79 pub alignment: usize,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct TensorNetworkRep {
86 pub tensors: Vec<TensorNode>,
88 pub contraction_order: Vec<(usize, usize)>,
90 pub bond_dims: Vec<usize>,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct TensorNode {
96 pub id: usize,
97 pub shape: Vec<usize>,
98 pub data: Vec<Complex64>,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct CompilationMetadata {
104 pub compiled_at: u64,
106 pub compilation_time_us: u64,
108 pub compiler_version: String,
110 pub target_hardware: String,
112 pub optimization_level: u32,
114 pub cache_hits: u64,
116 pub last_accessed: u64,
118}
119
120#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct CacheStatistics {
123 pub total_hits: u64,
125 pub total_misses: u64,
127 pub time_saved_us: u64,
129 pub num_entries: usize,
131 pub total_size_bytes: usize,
133 pub created_at: u64,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct CacheConfig {
140 pub max_memory_entries: usize,
142 pub max_size_bytes: usize,
144 pub cache_dir: PathBuf,
146 pub enable_persistence: bool,
148 pub expiration_time: Duration,
150 pub compression_level: u32,
152 pub async_writes: bool,
154}
155
156impl Default for CacheConfig {
157 fn default() -> Self {
158 Self {
159 max_memory_entries: 10000,
160 max_size_bytes: 1024 * 1024 * 1024, cache_dir: PathBuf::from(".quantrs_cache"),
162 enable_persistence: true,
163 expiration_time: Duration::from_secs(30 * 24 * 60 * 60), compression_level: 3,
165 async_writes: true,
166 }
167 }
168}
169
170pub struct CompilationCache {
172 memory_cache: Arc<RwLock<MemoryCache>>,
174 config: CacheConfig,
176 statistics: Arc<RwLock<CacheStatistics>>,
178 writer_handle: Option<std::thread::JoinHandle<()>>,
180 write_queue: Arc<RwLock<VecDeque<CompiledGate>>>,
182}
183
184struct MemoryCache {
186 gates: HashMap<String, CompiledGate>,
188 lru_queue: VecDeque<String>,
190 current_size: usize,
192}
193
194impl CompilationCache {
195 pub fn new(config: CacheConfig) -> QuantRS2Result<Self> {
197 if config.enable_persistence {
199 fs::create_dir_all(&config.cache_dir)?;
200 }
201
202 let memory_cache = Arc::new(RwLock::new(MemoryCache {
203 gates: HashMap::new(),
204 lru_queue: VecDeque::new(),
205 current_size: 0,
206 }));
207
208 let statistics = Arc::new(RwLock::new(CacheStatistics {
209 total_hits: 0,
210 total_misses: 0,
211 time_saved_us: 0,
212 num_entries: 0,
213 total_size_bytes: 0,
214 created_at: current_timestamp(),
215 }));
216
217 let write_queue = Arc::new(RwLock::new(VecDeque::new()));
218
219 let writer_handle = if config.async_writes && config.enable_persistence {
221 Some(Self::start_background_writer(
222 config.cache_dir.clone(),
223 Arc::clone(&write_queue),
224 ))
225 } else {
226 None
227 };
228
229 Ok(Self {
230 memory_cache,
231 config,
232 statistics,
233 writer_handle,
234 write_queue,
235 })
236 }
237
238 pub fn get_or_compile<F>(
240 &self,
241 gate: &dyn GateOp,
242 compile_fn: F,
243 ) -> QuantRS2Result<CompiledGate>
244 where
245 F: FnOnce(&dyn GateOp) -> QuantRS2Result<CompiledGate>,
246 {
247 let gate_id = self.compute_gate_id(gate)?;
248
249 if let Some(compiled) = self.get_from_memory(&gate_id)? {
251 self.record_hit(&gate_id)?;
252 return Ok(compiled);
253 }
254
255 if self.config.enable_persistence {
257 if let Some(compiled) = self.get_from_disk(&gate_id)? {
258 self.add_to_memory(compiled.clone())?;
259 self.record_hit(&gate_id)?;
260 return Ok(compiled);
261 }
262 }
263
264 self.record_miss()?;
266 let start_time = std::time::Instant::now();
267
268 let mut compiled = compile_fn(gate)?;
269
270 let compilation_time = start_time.elapsed().as_micros() as u64;
271 compiled.metadata.compilation_time_us = compilation_time;
272 compiled.gate_id = gate_id;
273
274 self.add_to_memory(compiled.clone())?;
276
277 if self.config.enable_persistence {
278 if self.config.async_writes {
279 self.queue_for_write(compiled.clone())?;
280 } else {
281 self.write_to_disk(&compiled)?;
282 }
283 }
284
285 Ok(compiled)
286 }
287
288 fn compute_gate_id(&self, gate: &dyn GateOp) -> QuantRS2Result<String> {
290 let mut hasher = DefaultHasher::new();
291
292 gate.name().hash(&mut hasher);
294
295 let matrix = gate.matrix()?;
297 for elem in &matrix {
298 elem.re.to_bits().hash(&mut hasher);
299 elem.im.to_bits().hash(&mut hasher);
300 }
301
302 for qubit in gate.qubits() {
304 qubit.0.hash(&mut hasher);
305 }
306
307 let result = hasher.finish();
308 Ok(format!("{result:x}"))
309 }
310
311 fn get_from_memory(&self, gate_id: &str) -> QuantRS2Result<Option<CompiledGate>> {
313 let mut cache = self
314 .memory_cache
315 .write()
316 .map_err(|_| QuantRS2Error::RuntimeError("Memory cache lock poisoned".to_string()))?;
317
318 if let Some(compiled) = cache.gates.get(gate_id).cloned() {
319 cache.lru_queue.retain(|id| id != gate_id);
321 cache.lru_queue.push_front(gate_id.to_string());
322
323 let mut updated_compiled = compiled;
325 updated_compiled.metadata.last_accessed = current_timestamp();
326 cache
327 .gates
328 .insert(gate_id.to_string(), updated_compiled.clone());
329
330 Ok(Some(updated_compiled))
331 } else {
332 Ok(None)
333 }
334 }
335
336 fn add_to_memory(&self, compiled: CompiledGate) -> QuantRS2Result<()> {
338 let mut cache = self
339 .memory_cache
340 .write()
341 .map_err(|_| QuantRS2Error::RuntimeError("Memory cache lock poisoned".to_string()))?;
342 let gate_size = self.estimate_size(&compiled);
343
344 while cache.gates.len() >= self.config.max_memory_entries
346 || cache.current_size + gate_size > self.config.max_size_bytes
347 {
348 if let Some(evict_id) = cache.lru_queue.pop_back() {
349 if let Some(evicted) = cache.gates.remove(&evict_id) {
350 cache.current_size -= self.estimate_size(&evicted);
351 }
352 } else {
353 break;
354 }
355 }
356
357 cache
359 .gates
360 .insert(compiled.gate_id.clone(), compiled.clone());
361 cache.lru_queue.push_front(compiled.gate_id);
362 cache.current_size += gate_size;
363
364 if let Ok(mut stats) = self.statistics.write() {
366 stats.num_entries = cache.gates.len();
367 stats.total_size_bytes = cache.current_size;
368 }
369
370 Ok(())
371 }
372
373 fn get_from_disk(&self, gate_id: &str) -> QuantRS2Result<Option<CompiledGate>> {
375 let file_path = self.cache_file_path(gate_id);
376
377 if !file_path.exists() {
378 return Ok(None);
379 }
380
381 let metadata = fs::metadata(&file_path)?;
383 let modified = metadata.modified()?;
384 let age = SystemTime::now()
385 .duration_since(modified)
386 .unwrap_or_default();
387
388 if age > self.config.expiration_time {
389 fs::remove_file(&file_path)?;
391 return Ok(None);
392 }
393
394 let file = File::open(&file_path)?;
396 let reader = BufReader::new(file);
397
398 let (compiled, _bytes_read): (CompiledGate, usize) =
401 oxicode::serde::decode_from_std_read(reader, oxicode::config::standard())?;
402
403 Ok(Some(compiled))
404 }
405
406 fn write_to_disk(&self, compiled: &CompiledGate) -> QuantRS2Result<()> {
408 let file_path = self.cache_file_path(&compiled.gate_id);
409
410 if let Some(parent) = file_path.parent() {
412 fs::create_dir_all(parent)?;
413 }
414
415 let file = File::create(&file_path)?;
416 let mut writer = BufWriter::new(file);
417 let bytes = oxicode::serde::encode_to_vec(compiled, oxicode::config::standard())?;
418 writer.write_all(&bytes)?;
419
420 Ok(())
421 }
422
423 fn queue_for_write(&self, compiled: CompiledGate) -> QuantRS2Result<()> {
425 let mut queue = self
426 .write_queue
427 .write()
428 .map_err(|_| QuantRS2Error::RuntimeError("Write queue lock poisoned".to_string()))?;
429 queue.push_back(compiled);
430 Ok(())
431 }
432
433 fn start_background_writer(
435 cache_dir: PathBuf,
436 write_queue: Arc<RwLock<VecDeque<CompiledGate>>>,
437 ) -> std::thread::JoinHandle<()> {
438 std::thread::spawn(move || loop {
439 std::thread::sleep(Duration::from_millis(100));
440
441 let gates_to_write: Vec<CompiledGate> = {
442 match write_queue.write() {
443 Ok(mut queue) => queue.drain(..).collect(),
444 Err(_) => continue, }
446 };
447
448 for compiled in gates_to_write {
449 let filename = format!(
450 "{}.cache",
451 &compiled.gate_id[..16.min(compiled.gate_id.len())]
452 );
453 let file_path = cache_dir.join(filename);
454
455 if let Err(e) = Self::write_gate_to_file(&file_path, &compiled, 3) {
456 eprintln!("Failed to write gate to cache: {e}");
457 }
458 }
459 })
460 }
461
462 fn write_gate_to_file(
464 file_path: &Path,
465 compiled: &CompiledGate,
466 _compression_level: i32,
467 ) -> QuantRS2Result<()> {
468 if let Some(parent) = file_path.parent() {
469 fs::create_dir_all(parent)?;
470 }
471
472 let file = File::create(file_path)?;
473 let mut writer = BufWriter::new(file);
474 let bytes = oxicode::serde::encode_to_vec(compiled, oxicode::config::standard())?;
475 writer.write_all(&bytes)?;
476
477 Ok(())
478 }
479
480 fn cache_file_path(&self, gate_id: &str) -> PathBuf {
482 let filename = format!("{}.cache", &gate_id[..16.min(gate_id.len())]);
484 self.config.cache_dir.join(filename)
485 }
486
487 fn estimate_size(&self, compiled: &CompiledGate) -> usize {
489 std::mem::size_of::<CompiledGate>() +
490 compiled.matrix.len() * std::mem::size_of::<Complex64>() +
491 compiled.gate_id.len() +
492 1024
494 }
495
496 fn record_hit(&self, gate_id: &str) -> QuantRS2Result<()> {
498 let mut stats = self
499 .statistics
500 .write()
501 .map_err(|_| QuantRS2Error::RuntimeError("Statistics lock poisoned".to_string()))?;
502 stats.total_hits += 1;
503
504 if let Ok(cache) = self.memory_cache.read() {
506 if let Some(compiled) = cache.gates.get(gate_id) {
507 stats.time_saved_us += compiled.metadata.compilation_time_us;
508 }
509 }
510
511 Ok(())
512 }
513
514 fn record_miss(&self) -> QuantRS2Result<()> {
516 let mut stats = self
517 .statistics
518 .write()
519 .map_err(|_| QuantRS2Error::RuntimeError("Statistics lock poisoned".to_string()))?;
520 stats.total_misses += 1;
521 Ok(())
522 }
523
524 pub fn clear(&self) -> QuantRS2Result<()> {
526 let mut cache = self
528 .memory_cache
529 .write()
530 .map_err(|_| QuantRS2Error::RuntimeError("Memory cache lock poisoned".to_string()))?;
531 cache.gates.clear();
532 cache.lru_queue.clear();
533 cache.current_size = 0;
534
535 if self.config.enable_persistence && self.config.cache_dir.exists() {
537 for entry in fs::read_dir(&self.config.cache_dir)? {
538 let entry = entry?;
539 if entry.path().extension().and_then(|s| s.to_str()) == Some("cache") {
540 fs::remove_file(entry.path())?;
541 }
542 }
543 }
544
545 let mut stats = self
547 .statistics
548 .write()
549 .map_err(|_| QuantRS2Error::RuntimeError("Statistics lock poisoned".to_string()))?;
550 *stats = CacheStatistics {
551 total_hits: 0,
552 total_misses: 0,
553 time_saved_us: 0,
554 num_entries: 0,
555 total_size_bytes: 0,
556 created_at: current_timestamp(),
557 };
558
559 Ok(())
560 }
561
562 pub fn statistics(&self) -> CacheStatistics {
564 self.statistics
565 .read()
566 .map(|s| s.clone())
567 .unwrap_or_else(|_| CacheStatistics {
568 total_hits: 0,
569 total_misses: 0,
570 time_saved_us: 0,
571 num_entries: 0,
572 total_size_bytes: 0,
573 created_at: current_timestamp(),
574 })
575 }
576
577 pub fn optimize(&self) -> QuantRS2Result<()> {
579 if !self.config.enable_persistence {
580 return Ok(());
581 }
582
583 let mut removed_count = 0;
584
585 for entry in fs::read_dir(&self.config.cache_dir)? {
586 let entry = entry?;
587 let path = entry.path();
588
589 if path.extension().and_then(|s| s.to_str()) == Some("cache") {
590 let metadata = fs::metadata(&path)?;
591 let modified = metadata.modified()?;
592 let age = SystemTime::now()
593 .duration_since(modified)
594 .unwrap_or_default();
595
596 if age > self.config.expiration_time {
597 fs::remove_file(&path)?;
598 removed_count += 1;
599 }
600 }
601 }
602
603 println!("Cache optimization: removed {removed_count} expired entries");
604 Ok(())
605 }
606
607 pub fn export_statistics(&self, path: &Path) -> QuantRS2Result<()> {
609 let stats = self.statistics();
610 let json = serde_json::to_string_pretty(&stats)?;
611 fs::write(path, json)?;
612 Ok(())
613 }
614
615 pub fn precompile_common_gates(&self) -> QuantRS2Result<()> {
617 use crate::gate::{multi::*, single::*};
618
619 let single_qubit_gates: Vec<Box<dyn GateOp>> = vec![
621 Box::new(Hadamard {
622 target: crate::qubit::QubitId(0),
623 }),
624 Box::new(PauliX {
625 target: crate::qubit::QubitId(0),
626 }),
627 Box::new(PauliY {
628 target: crate::qubit::QubitId(0),
629 }),
630 Box::new(PauliZ {
631 target: crate::qubit::QubitId(0),
632 }),
633 Box::new(Phase {
634 target: crate::qubit::QubitId(0),
635 }),
636 Box::new(RotationZ {
637 target: crate::qubit::QubitId(0),
638 theta: std::f64::consts::PI / 4.0,
639 }),
640 ];
641
642 for gate in single_qubit_gates {
643 let _ = self.get_or_compile(gate.as_ref(), |g| compile_single_qubit_gate(g))?;
644 }
645
646 let two_qubit_gates: Vec<Box<dyn GateOp>> = vec![
648 Box::new(CNOT {
649 control: crate::qubit::QubitId(0),
650 target: crate::qubit::QubitId(1),
651 }),
652 Box::new(CZ {
653 control: crate::qubit::QubitId(0),
654 target: crate::qubit::QubitId(1),
655 }),
656 Box::new(SWAP {
657 qubit1: crate::qubit::QubitId(0),
658 qubit2: crate::qubit::QubitId(1),
659 }),
660 ];
661
662 for gate in two_qubit_gates {
663 let _ = self.get_or_compile(gate.as_ref(), |g| compile_two_qubit_gate(g))?;
664 }
665
666 Ok(())
667 }
668}
669
670fn current_timestamp() -> u64 {
672 SystemTime::now()
673 .duration_since(UNIX_EPOCH)
674 .unwrap_or_default()
675 .as_secs()
676}
677
678fn compile_single_qubit_gate(gate: &dyn GateOp) -> QuantRS2Result<CompiledGate> {
680 let matrix = gate.matrix()?;
681 let gate_id = String::new(); let is_diagonal = matrix[1].norm() < 1e-10 && matrix[2].norm() < 1e-10;
685 let diagonal = if is_diagonal {
686 Some(vec![matrix[0], matrix[3]])
687 } else {
688 None
689 };
690
691 let simd_layout = if false {
693 Some(SimdLayout {
695 layout_type: "avx2".to_string(),
696 data: matrix.clone(),
697 stride: 2,
698 alignment: 32,
699 })
700 } else {
701 None
702 };
703
704 Ok(CompiledGate {
705 gate_id,
706 matrix,
707 num_qubits: 1,
708 optimizations: GateOptimizations {
709 diagonal,
710 decomposition: None,
711 simd_layout,
712 gpu_kernel_id: None,
713 tensor_network: None,
714 },
715 metadata: CompilationMetadata {
716 compiled_at: current_timestamp(),
717 compilation_time_us: 0, compiler_version: env!("CARGO_PKG_VERSION").to_string(),
719 target_hardware: "generic".to_string(),
720 optimization_level: 2,
721 cache_hits: 0,
722 last_accessed: current_timestamp(),
723 },
724 })
725}
726
727fn compile_two_qubit_gate(gate: &dyn GateOp) -> QuantRS2Result<CompiledGate> {
728 let matrix = gate.matrix()?;
729 let gate_id = String::new(); let decomposition = if gate.name() == "CNOT" {
733 Some(GateDecomposition {
734 gates: vec!["H".to_string(), "CZ".to_string(), "H".to_string()],
735 parameters: vec![vec![], vec![], vec![]],
736 targets: vec![vec![1], vec![0, 1], vec![1]],
737 gate_count: 3,
738 error: 1e-15,
739 })
740 } else {
741 None
742 };
743
744 Ok(CompiledGate {
745 gate_id,
746 matrix,
747 num_qubits: 2,
748 optimizations: GateOptimizations {
749 diagonal: None,
750 decomposition,
751 simd_layout: None,
752 gpu_kernel_id: Some(format!("{}_kernel", gate.name().to_lowercase())),
753 tensor_network: None,
754 },
755 metadata: CompilationMetadata {
756 compiled_at: current_timestamp(),
757 compilation_time_us: 0,
758 compiler_version: env!("CARGO_PKG_VERSION").to_string(),
759 target_hardware: "generic".to_string(),
760 optimization_level: 2,
761 cache_hits: 0,
762 last_accessed: current_timestamp(),
763 },
764 })
765}
766
767static GLOBAL_CACHE: OnceLock<Arc<CompilationCache>> = OnceLock::new();
769
770pub fn initialize_compilation_cache(config: CacheConfig) -> QuantRS2Result<()> {
772 let cache = CompilationCache::new(config)?;
773
774 GLOBAL_CACHE.set(Arc::new(cache)).map_err(|_| {
775 QuantRS2Error::RuntimeError("Compilation cache already initialized".to_string())
776 })?;
777
778 Ok(())
779}
780
781pub fn get_compilation_cache() -> QuantRS2Result<Arc<CompilationCache>> {
783 GLOBAL_CACHE
784 .get()
785 .map(Arc::clone)
786 .ok_or_else(|| QuantRS2Error::RuntimeError("Compilation cache not initialized".to_string()))
787}
788
789#[cfg(test)]
790mod tests {
791 use super::*;
792 use crate::gate::single::{Hadamard, PauliX};
793 use crate::qubit::QubitId;
794 use std::fs;
795 #[test]
798 fn test_cache_creation() {
799 let temp_dir = std::env::temp_dir().join("quantrs_test_cache");
800 let config = CacheConfig {
801 cache_dir: temp_dir,
802 enable_persistence: false, ..Default::default()
804 };
805
806 let cache = CompilationCache::new(config).expect("Failed to create cache");
807 let stats = cache.statistics();
808
809 assert_eq!(stats.total_hits, 0);
810 assert_eq!(stats.total_misses, 0);
811 assert_eq!(stats.num_entries, 0);
812 }
813
814 #[test]
815 fn test_gate_compilation_and_caching() {
816 let temp_dir = std::env::temp_dir().join(format!(
817 "quantrs_test_caching_{}_{}",
818 std::process::id(),
819 std::time::SystemTime::now()
820 .duration_since(std::time::UNIX_EPOCH)
821 .unwrap_or_default()
822 .as_nanos()
823 ));
824 let config = CacheConfig {
825 cache_dir: temp_dir,
826 enable_persistence: false, async_writes: false,
828 ..Default::default()
829 };
830
831 let cache = CompilationCache::new(config).expect("Failed to create cache");
832 cache.clear().expect("Failed to clear cache");
834 let gate = Hadamard { target: QubitId(0) };
835
836 let compiled1 = cache
838 .get_or_compile(&gate, compile_single_qubit_gate)
839 .expect("Failed to compile gate");
840 let stats1 = cache.statistics();
841 assert_eq!(stats1.total_misses, 1);
842 assert_eq!(stats1.total_hits, 0);
843
844 let compiled2 = cache
846 .get_or_compile(&gate, compile_single_qubit_gate)
847 .expect("Failed to get cached gate");
848 let stats2 = cache.statistics();
849 assert_eq!(stats2.total_misses, 1);
850 assert_eq!(stats2.total_hits, 1);
851
852 assert_eq!(compiled1.gate_id, compiled2.gate_id);
854 assert_eq!(compiled1.matrix, compiled2.matrix);
855 }
856
857 #[test]
858 fn test_cache_eviction() {
859 let temp_dir = std::env::temp_dir().join(format!("quantrs_test_{}", std::process::id()));
860 let config = CacheConfig {
861 cache_dir: temp_dir,
862 max_memory_entries: 2,
863 enable_persistence: false,
864 ..Default::default()
865 };
866
867 let cache = CompilationCache::new(config).expect("Failed to create cache");
868
869 for i in 0..3 {
871 let gate = PauliX { target: QubitId(i) };
872 let _ = cache
873 .get_or_compile(&gate, compile_single_qubit_gate)
874 .expect("Failed to compile gate");
875 }
876
877 let stats = cache.statistics();
878 assert_eq!(stats.num_entries, 2); }
880
881 #[test]
882 fn test_persistent_cache() {
883 let temp_dir = std::env::temp_dir().join(format!("quantrs_test_{}", std::process::id()));
884 let config = CacheConfig {
885 cache_dir: temp_dir,
886 enable_persistence: true,
887 async_writes: false,
888 ..Default::default()
889 };
890
891 let gate = Hadamard { target: QubitId(0) };
892 let gate_id;
893
894 {
896 let cache = CompilationCache::new(config.clone()).expect("Failed to create cache");
897 let compiled = cache
898 .get_or_compile(&gate, compile_single_qubit_gate)
899 .expect("Failed to compile gate");
900 gate_id = compiled.gate_id.clone();
901 }
902
903 {
905 let cache = CompilationCache::new(config).expect("Failed to create cache");
906 let compiled = cache
907 .get_or_compile(&gate, compile_single_qubit_gate)
908 .expect("Failed to get cached gate");
909
910 assert_eq!(compiled.gate_id, gate_id);
911
912 let stats = cache.statistics();
913 assert_eq!(stats.total_hits, 1); assert_eq!(stats.total_misses, 0);
915 }
916 }
917
918 #[test]
919 fn test_cache_optimization() {
920 let temp_dir = std::env::temp_dir().join(format!("quantrs_test_{}", std::process::id()));
921 let config = CacheConfig {
922 cache_dir: temp_dir,
923 enable_persistence: true,
924 expiration_time: Duration::from_secs(0), async_writes: false,
926 ..Default::default()
927 };
928
929 let cache = CompilationCache::new(config).expect("Failed to create cache");
930 let gate = Hadamard { target: QubitId(0) };
931
932 let _ = cache
934 .get_or_compile(&gate, compile_single_qubit_gate)
935 .expect("Failed to compile gate");
936
937 std::thread::sleep(Duration::from_millis(100));
939 cache.optimize().expect("Failed to optimize cache");
940
941 cache.clear().expect("Failed to clear cache"); let _ = cache
944 .get_or_compile(&gate, compile_single_qubit_gate)
945 .expect("Failed to recompile gate");
946
947 let stats = cache.statistics();
948 assert_eq!(stats.total_misses, 1); }
950
951 #[test]
952 fn test_precompile_common_gates() {
953 let temp_dir = std::env::temp_dir().join(format!("quantrs_test_{}", std::process::id()));
954 let config = CacheConfig {
955 cache_dir: temp_dir,
956 enable_persistence: false, async_writes: false,
958 ..Default::default()
959 };
960
961 let cache = CompilationCache::new(config).expect("Failed to create cache");
962 cache.clear().expect("Failed to clear cache");
964 cache
965 .precompile_common_gates()
966 .expect("Failed to precompile gates");
967
968 let stats = cache.statistics();
969 assert!(stats.num_entries > 0);
970 println!("Precompiled {} gates", stats.num_entries);
971 }
972
973 #[test]
974 fn test_statistics_export() {
975 let temp_dir = std::env::temp_dir().join(format!(
976 "quantrs_test_stats_{}_{}",
977 std::process::id(),
978 std::time::SystemTime::now()
979 .duration_since(std::time::UNIX_EPOCH)
980 .unwrap_or_default()
981 .as_nanos()
982 ));
983 let config = CacheConfig {
984 cache_dir: temp_dir.clone(),
985 enable_persistence: false, ..Default::default()
987 };
988
989 let cache = CompilationCache::new(config).expect("Failed to create cache");
990 cache.clear().expect("Failed to clear cache");
992
993 let gate = Hadamard { target: QubitId(0) };
995 let _ = cache
996 .get_or_compile(&gate, compile_single_qubit_gate)
997 .expect("Failed to compile gate");
998 let _ = cache
999 .get_or_compile(&gate, compile_single_qubit_gate)
1000 .expect("Failed to get cached gate");
1001
1002 std::fs::create_dir_all(&temp_dir).expect("Failed to create temp dir");
1004 let stats_path = temp_dir.join("stats.json");
1005 cache
1006 .export_statistics(&stats_path)
1007 .expect("Failed to export statistics");
1008
1009 assert!(stats_path.exists());
1011 let contents = fs::read_to_string(&stats_path).expect("Failed to read stats file");
1012 let parsed: CacheStatistics =
1013 serde_json::from_str(&contents).expect("Failed to parse JSON");
1014
1015 assert_eq!(parsed.total_hits, 1);
1016 assert_eq!(parsed.total_misses, 1);
1017 }
1018}