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},
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: CompiledGate =
400 bincode::serde::decode_from_reader(reader, bincode::config::standard())?;
401
402 Ok(Some(compiled))
403 }
404
405 fn write_to_disk(&self, compiled: &CompiledGate) -> QuantRS2Result<()> {
407 let file_path = self.cache_file_path(&compiled.gate_id);
408
409 if let Some(parent) = file_path.parent() {
411 fs::create_dir_all(parent)?;
412 }
413
414 let file = File::create(&file_path)?;
415 let mut writer = BufWriter::new(file);
416 bincode::serde::encode_into_std_write(compiled, &mut writer, bincode::config::standard())?;
417
418 Ok(())
419 }
420
421 fn queue_for_write(&self, compiled: CompiledGate) -> QuantRS2Result<()> {
423 let mut queue = self
424 .write_queue
425 .write()
426 .map_err(|_| QuantRS2Error::RuntimeError("Write queue lock poisoned".to_string()))?;
427 queue.push_back(compiled);
428 Ok(())
429 }
430
431 fn start_background_writer(
433 cache_dir: PathBuf,
434 write_queue: Arc<RwLock<VecDeque<CompiledGate>>>,
435 ) -> std::thread::JoinHandle<()> {
436 std::thread::spawn(move || loop {
437 std::thread::sleep(Duration::from_millis(100));
438
439 let gates_to_write: Vec<CompiledGate> = {
440 match write_queue.write() {
441 Ok(mut queue) => queue.drain(..).collect(),
442 Err(_) => continue, }
444 };
445
446 for compiled in gates_to_write {
447 let filename = format!(
448 "{}.cache",
449 &compiled.gate_id[..16.min(compiled.gate_id.len())]
450 );
451 let file_path = cache_dir.join(filename);
452
453 if let Err(e) = Self::write_gate_to_file(&file_path, &compiled, 3) {
454 eprintln!("Failed to write gate to cache: {e}");
455 }
456 }
457 })
458 }
459
460 fn write_gate_to_file(
462 file_path: &Path,
463 compiled: &CompiledGate,
464 _compression_level: i32,
465 ) -> QuantRS2Result<()> {
466 if let Some(parent) = file_path.parent() {
467 fs::create_dir_all(parent)?;
468 }
469
470 let file = File::create(file_path)?;
471 let mut writer = BufWriter::new(file);
472 bincode::serde::encode_into_std_write(compiled, &mut writer, bincode::config::standard())?;
473
474 Ok(())
475 }
476
477 fn cache_file_path(&self, gate_id: &str) -> PathBuf {
479 let filename = format!("{}.cache", &gate_id[..16.min(gate_id.len())]);
481 self.config.cache_dir.join(filename)
482 }
483
484 fn estimate_size(&self, compiled: &CompiledGate) -> usize {
486 std::mem::size_of::<CompiledGate>() +
487 compiled.matrix.len() * std::mem::size_of::<Complex64>() +
488 compiled.gate_id.len() +
489 1024
491 }
492
493 fn record_hit(&self, gate_id: &str) -> QuantRS2Result<()> {
495 let mut stats = self
496 .statistics
497 .write()
498 .map_err(|_| QuantRS2Error::RuntimeError("Statistics lock poisoned".to_string()))?;
499 stats.total_hits += 1;
500
501 if let Ok(cache) = self.memory_cache.read() {
503 if let Some(compiled) = cache.gates.get(gate_id) {
504 stats.time_saved_us += compiled.metadata.compilation_time_us;
505 }
506 }
507
508 Ok(())
509 }
510
511 fn record_miss(&self) -> QuantRS2Result<()> {
513 let mut stats = self
514 .statistics
515 .write()
516 .map_err(|_| QuantRS2Error::RuntimeError("Statistics lock poisoned".to_string()))?;
517 stats.total_misses += 1;
518 Ok(())
519 }
520
521 pub fn clear(&self) -> QuantRS2Result<()> {
523 let mut cache = self
525 .memory_cache
526 .write()
527 .map_err(|_| QuantRS2Error::RuntimeError("Memory cache lock poisoned".to_string()))?;
528 cache.gates.clear();
529 cache.lru_queue.clear();
530 cache.current_size = 0;
531
532 if self.config.enable_persistence && self.config.cache_dir.exists() {
534 for entry in fs::read_dir(&self.config.cache_dir)? {
535 let entry = entry?;
536 if entry.path().extension().and_then(|s| s.to_str()) == Some("cache") {
537 fs::remove_file(entry.path())?;
538 }
539 }
540 }
541
542 let mut stats = self
544 .statistics
545 .write()
546 .map_err(|_| QuantRS2Error::RuntimeError("Statistics lock poisoned".to_string()))?;
547 *stats = CacheStatistics {
548 total_hits: 0,
549 total_misses: 0,
550 time_saved_us: 0,
551 num_entries: 0,
552 total_size_bytes: 0,
553 created_at: current_timestamp(),
554 };
555
556 Ok(())
557 }
558
559 pub fn statistics(&self) -> CacheStatistics {
561 self.statistics
562 .read()
563 .map(|s| s.clone())
564 .unwrap_or_else(|_| CacheStatistics {
565 total_hits: 0,
566 total_misses: 0,
567 time_saved_us: 0,
568 num_entries: 0,
569 total_size_bytes: 0,
570 created_at: current_timestamp(),
571 })
572 }
573
574 pub fn optimize(&self) -> QuantRS2Result<()> {
576 if !self.config.enable_persistence {
577 return Ok(());
578 }
579
580 let mut removed_count = 0;
581
582 for entry in fs::read_dir(&self.config.cache_dir)? {
583 let entry = entry?;
584 let path = entry.path();
585
586 if path.extension().and_then(|s| s.to_str()) == Some("cache") {
587 let metadata = fs::metadata(&path)?;
588 let modified = metadata.modified()?;
589 let age = SystemTime::now()
590 .duration_since(modified)
591 .unwrap_or_default();
592
593 if age > self.config.expiration_time {
594 fs::remove_file(&path)?;
595 removed_count += 1;
596 }
597 }
598 }
599
600 println!("Cache optimization: removed {removed_count} expired entries");
601 Ok(())
602 }
603
604 pub fn export_statistics(&self, path: &Path) -> QuantRS2Result<()> {
606 let stats = self.statistics();
607 let json = serde_json::to_string_pretty(&stats)?;
608 fs::write(path, json)?;
609 Ok(())
610 }
611
612 pub fn precompile_common_gates(&self) -> QuantRS2Result<()> {
614 use crate::gate::{multi::*, single::*};
615
616 let single_qubit_gates: Vec<Box<dyn GateOp>> = vec![
618 Box::new(Hadamard {
619 target: crate::qubit::QubitId(0),
620 }),
621 Box::new(PauliX {
622 target: crate::qubit::QubitId(0),
623 }),
624 Box::new(PauliY {
625 target: crate::qubit::QubitId(0),
626 }),
627 Box::new(PauliZ {
628 target: crate::qubit::QubitId(0),
629 }),
630 Box::new(Phase {
631 target: crate::qubit::QubitId(0),
632 }),
633 Box::new(RotationZ {
634 target: crate::qubit::QubitId(0),
635 theta: std::f64::consts::PI / 4.0,
636 }),
637 ];
638
639 for gate in single_qubit_gates {
640 let _ = self.get_or_compile(gate.as_ref(), |g| compile_single_qubit_gate(g))?;
641 }
642
643 let two_qubit_gates: Vec<Box<dyn GateOp>> = vec![
645 Box::new(CNOT {
646 control: crate::qubit::QubitId(0),
647 target: crate::qubit::QubitId(1),
648 }),
649 Box::new(CZ {
650 control: crate::qubit::QubitId(0),
651 target: crate::qubit::QubitId(1),
652 }),
653 Box::new(SWAP {
654 qubit1: crate::qubit::QubitId(0),
655 qubit2: crate::qubit::QubitId(1),
656 }),
657 ];
658
659 for gate in two_qubit_gates {
660 let _ = self.get_or_compile(gate.as_ref(), |g| compile_two_qubit_gate(g))?;
661 }
662
663 Ok(())
664 }
665}
666
667fn current_timestamp() -> u64 {
669 SystemTime::now()
670 .duration_since(UNIX_EPOCH)
671 .unwrap_or_default()
672 .as_secs()
673}
674
675fn compile_single_qubit_gate(gate: &dyn GateOp) -> QuantRS2Result<CompiledGate> {
677 let matrix = gate.matrix()?;
678 let gate_id = String::new(); let is_diagonal = matrix[1].norm() < 1e-10 && matrix[2].norm() < 1e-10;
682 let diagonal = if is_diagonal {
683 Some(vec![matrix[0], matrix[3]])
684 } else {
685 None
686 };
687
688 let simd_layout = if false {
690 Some(SimdLayout {
692 layout_type: "avx2".to_string(),
693 data: matrix.clone(),
694 stride: 2,
695 alignment: 32,
696 })
697 } else {
698 None
699 };
700
701 Ok(CompiledGate {
702 gate_id,
703 matrix,
704 num_qubits: 1,
705 optimizations: GateOptimizations {
706 diagonal,
707 decomposition: None,
708 simd_layout,
709 gpu_kernel_id: None,
710 tensor_network: None,
711 },
712 metadata: CompilationMetadata {
713 compiled_at: current_timestamp(),
714 compilation_time_us: 0, compiler_version: env!("CARGO_PKG_VERSION").to_string(),
716 target_hardware: "generic".to_string(),
717 optimization_level: 2,
718 cache_hits: 0,
719 last_accessed: current_timestamp(),
720 },
721 })
722}
723
724fn compile_two_qubit_gate(gate: &dyn GateOp) -> QuantRS2Result<CompiledGate> {
725 let matrix = gate.matrix()?;
726 let gate_id = String::new(); let decomposition = if gate.name() == "CNOT" {
730 Some(GateDecomposition {
731 gates: vec!["H".to_string(), "CZ".to_string(), "H".to_string()],
732 parameters: vec![vec![], vec![], vec![]],
733 targets: vec![vec![1], vec![0, 1], vec![1]],
734 gate_count: 3,
735 error: 1e-15,
736 })
737 } else {
738 None
739 };
740
741 Ok(CompiledGate {
742 gate_id,
743 matrix,
744 num_qubits: 2,
745 optimizations: GateOptimizations {
746 diagonal: None,
747 decomposition,
748 simd_layout: None,
749 gpu_kernel_id: Some(format!("{}_kernel", gate.name().to_lowercase())),
750 tensor_network: None,
751 },
752 metadata: CompilationMetadata {
753 compiled_at: current_timestamp(),
754 compilation_time_us: 0,
755 compiler_version: env!("CARGO_PKG_VERSION").to_string(),
756 target_hardware: "generic".to_string(),
757 optimization_level: 2,
758 cache_hits: 0,
759 last_accessed: current_timestamp(),
760 },
761 })
762}
763
764static GLOBAL_CACHE: OnceLock<Arc<CompilationCache>> = OnceLock::new();
766
767pub fn initialize_compilation_cache(config: CacheConfig) -> QuantRS2Result<()> {
769 let cache = CompilationCache::new(config)?;
770
771 GLOBAL_CACHE.set(Arc::new(cache)).map_err(|_| {
772 QuantRS2Error::RuntimeError("Compilation cache already initialized".to_string())
773 })?;
774
775 Ok(())
776}
777
778pub fn get_compilation_cache() -> QuantRS2Result<Arc<CompilationCache>> {
780 GLOBAL_CACHE
781 .get()
782 .map(Arc::clone)
783 .ok_or_else(|| QuantRS2Error::RuntimeError("Compilation cache not initialized".to_string()))
784}
785
786#[cfg(test)]
787mod tests {
788 use super::*;
789 use crate::gate::single::{Hadamard, PauliX};
790 use crate::qubit::QubitId;
791 use std::fs;
792 #[test]
795 fn test_cache_creation() {
796 let temp_dir = std::env::temp_dir().join("quantrs_test_cache");
797 let config = CacheConfig {
798 cache_dir: temp_dir,
799 enable_persistence: false, ..Default::default()
801 };
802
803 let cache = CompilationCache::new(config).expect("Failed to create cache");
804 let stats = cache.statistics();
805
806 assert_eq!(stats.total_hits, 0);
807 assert_eq!(stats.total_misses, 0);
808 assert_eq!(stats.num_entries, 0);
809 }
810
811 #[test]
812 fn test_gate_compilation_and_caching() {
813 let temp_dir = std::env::temp_dir().join(format!(
814 "quantrs_test_caching_{}_{}",
815 std::process::id(),
816 std::time::SystemTime::now()
817 .duration_since(std::time::UNIX_EPOCH)
818 .unwrap_or_default()
819 .as_nanos()
820 ));
821 let config = CacheConfig {
822 cache_dir: temp_dir,
823 enable_persistence: false, async_writes: false,
825 ..Default::default()
826 };
827
828 let cache = CompilationCache::new(config).expect("Failed to create cache");
829 cache.clear().expect("Failed to clear cache");
831 let gate = Hadamard { target: QubitId(0) };
832
833 let compiled1 = cache
835 .get_or_compile(&gate, compile_single_qubit_gate)
836 .expect("Failed to compile gate");
837 let stats1 = cache.statistics();
838 assert_eq!(stats1.total_misses, 1);
839 assert_eq!(stats1.total_hits, 0);
840
841 let compiled2 = cache
843 .get_or_compile(&gate, compile_single_qubit_gate)
844 .expect("Failed to get cached gate");
845 let stats2 = cache.statistics();
846 assert_eq!(stats2.total_misses, 1);
847 assert_eq!(stats2.total_hits, 1);
848
849 assert_eq!(compiled1.gate_id, compiled2.gate_id);
851 assert_eq!(compiled1.matrix, compiled2.matrix);
852 }
853
854 #[test]
855 fn test_cache_eviction() {
856 let temp_dir = std::env::temp_dir().join(format!("quantrs_test_{}", std::process::id()));
857 let config = CacheConfig {
858 cache_dir: temp_dir,
859 max_memory_entries: 2,
860 enable_persistence: false,
861 ..Default::default()
862 };
863
864 let cache = CompilationCache::new(config).expect("Failed to create cache");
865
866 for i in 0..3 {
868 let gate = PauliX { target: QubitId(i) };
869 let _ = cache
870 .get_or_compile(&gate, compile_single_qubit_gate)
871 .expect("Failed to compile gate");
872 }
873
874 let stats = cache.statistics();
875 assert_eq!(stats.num_entries, 2); }
877
878 #[test]
879 fn test_persistent_cache() {
880 let temp_dir = std::env::temp_dir().join(format!("quantrs_test_{}", std::process::id()));
881 let config = CacheConfig {
882 cache_dir: temp_dir,
883 enable_persistence: true,
884 async_writes: false,
885 ..Default::default()
886 };
887
888 let gate = Hadamard { target: QubitId(0) };
889 let gate_id;
890
891 {
893 let cache = CompilationCache::new(config.clone()).expect("Failed to create cache");
894 let compiled = cache
895 .get_or_compile(&gate, compile_single_qubit_gate)
896 .expect("Failed to compile gate");
897 gate_id = compiled.gate_id.clone();
898 }
899
900 {
902 let cache = CompilationCache::new(config).expect("Failed to create cache");
903 let compiled = cache
904 .get_or_compile(&gate, compile_single_qubit_gate)
905 .expect("Failed to get cached gate");
906
907 assert_eq!(compiled.gate_id, gate_id);
908
909 let stats = cache.statistics();
910 assert_eq!(stats.total_hits, 1); assert_eq!(stats.total_misses, 0);
912 }
913 }
914
915 #[test]
916 fn test_cache_optimization() {
917 let temp_dir = std::env::temp_dir().join(format!("quantrs_test_{}", std::process::id()));
918 let config = CacheConfig {
919 cache_dir: temp_dir,
920 enable_persistence: true,
921 expiration_time: Duration::from_secs(0), async_writes: false,
923 ..Default::default()
924 };
925
926 let cache = CompilationCache::new(config).expect("Failed to create cache");
927 let gate = Hadamard { target: QubitId(0) };
928
929 let _ = cache
931 .get_or_compile(&gate, compile_single_qubit_gate)
932 .expect("Failed to compile gate");
933
934 std::thread::sleep(Duration::from_millis(100));
936 cache.optimize().expect("Failed to optimize cache");
937
938 cache.clear().expect("Failed to clear cache"); let _ = cache
941 .get_or_compile(&gate, compile_single_qubit_gate)
942 .expect("Failed to recompile gate");
943
944 let stats = cache.statistics();
945 assert_eq!(stats.total_misses, 1); }
947
948 #[test]
949 fn test_precompile_common_gates() {
950 let temp_dir = std::env::temp_dir().join(format!("quantrs_test_{}", std::process::id()));
951 let config = CacheConfig {
952 cache_dir: temp_dir,
953 enable_persistence: false, async_writes: false,
955 ..Default::default()
956 };
957
958 let cache = CompilationCache::new(config).expect("Failed to create cache");
959 cache.clear().expect("Failed to clear cache");
961 cache
962 .precompile_common_gates()
963 .expect("Failed to precompile gates");
964
965 let stats = cache.statistics();
966 assert!(stats.num_entries > 0);
967 println!("Precompiled {} gates", stats.num_entries);
968 }
969
970 #[test]
971 fn test_statistics_export() {
972 let temp_dir = std::env::temp_dir().join(format!(
973 "quantrs_test_stats_{}_{}",
974 std::process::id(),
975 std::time::SystemTime::now()
976 .duration_since(std::time::UNIX_EPOCH)
977 .unwrap_or_default()
978 .as_nanos()
979 ));
980 let config = CacheConfig {
981 cache_dir: temp_dir.clone(),
982 enable_persistence: false, ..Default::default()
984 };
985
986 let cache = CompilationCache::new(config).expect("Failed to create cache");
987 cache.clear().expect("Failed to clear cache");
989
990 let gate = Hadamard { target: QubitId(0) };
992 let _ = cache
993 .get_or_compile(&gate, compile_single_qubit_gate)
994 .expect("Failed to compile gate");
995 let _ = cache
996 .get_or_compile(&gate, compile_single_qubit_gate)
997 .expect("Failed to get cached gate");
998
999 std::fs::create_dir_all(&temp_dir).expect("Failed to create temp dir");
1001 let stats_path = temp_dir.join("stats.json");
1002 cache
1003 .export_statistics(&stats_path)
1004 .expect("Failed to export statistics");
1005
1006 assert!(stats_path.exists());
1008 let contents = fs::read_to_string(&stats_path).expect("Failed to read stats file");
1009 let parsed: CacheStatistics =
1010 serde_json::from_str(&contents).expect("Failed to parse JSON");
1011
1012 assert_eq!(parsed.total_hits, 1);
1013 assert_eq!(parsed.total_misses, 1);
1014 }
1015}