1use crate::{
8 error::{QuantRS2Error, QuantRS2Result},
9 gate::GateOp,
10};
11use num_complex::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!("{:x}", result))
309 }
310
311 fn get_from_memory(&self, gate_id: &str) -> QuantRS2Result<Option<CompiledGate>> {
313 let mut cache = self.memory_cache.write().unwrap();
314
315 if let Some(compiled) = cache.gates.get(gate_id).cloned() {
316 cache.lru_queue.retain(|id| id != gate_id);
318 cache.lru_queue.push_front(gate_id.to_string());
319
320 let mut updated_compiled = compiled.clone();
322 updated_compiled.metadata.last_accessed = current_timestamp();
323 cache
324 .gates
325 .insert(gate_id.to_string(), updated_compiled.clone());
326
327 Ok(Some(updated_compiled))
328 } else {
329 Ok(None)
330 }
331 }
332
333 fn add_to_memory(&self, compiled: CompiledGate) -> QuantRS2Result<()> {
335 let mut cache = self.memory_cache.write().unwrap();
336 let gate_size = self.estimate_size(&compiled);
337
338 while cache.gates.len() >= self.config.max_memory_entries
340 || cache.current_size + gate_size > self.config.max_size_bytes
341 {
342 if let Some(evict_id) = cache.lru_queue.pop_back() {
343 if let Some(evicted) = cache.gates.remove(&evict_id) {
344 cache.current_size -= self.estimate_size(&evicted);
345 }
346 } else {
347 break;
348 }
349 }
350
351 cache
353 .gates
354 .insert(compiled.gate_id.clone(), compiled.clone());
355 cache.lru_queue.push_front(compiled.gate_id.clone());
356 cache.current_size += gate_size;
357
358 let mut stats = self.statistics.write().unwrap();
360 stats.num_entries = cache.gates.len();
361 stats.total_size_bytes = cache.current_size;
362
363 Ok(())
364 }
365
366 fn get_from_disk(&self, gate_id: &str) -> QuantRS2Result<Option<CompiledGate>> {
368 let file_path = self.cache_file_path(gate_id);
369
370 if !file_path.exists() {
371 return Ok(None);
372 }
373
374 let metadata = fs::metadata(&file_path)?;
376 let modified = metadata.modified()?;
377 let age = SystemTime::now()
378 .duration_since(modified)
379 .unwrap_or_default();
380
381 if age > self.config.expiration_time {
382 fs::remove_file(&file_path)?;
384 return Ok(None);
385 }
386
387 let file = File::open(&file_path)?;
389 let reader = BufReader::new(file);
390
391 let compiled = bincode::deserialize_from(reader)?;
392
393 Ok(Some(compiled))
394 }
395
396 fn write_to_disk(&self, compiled: &CompiledGate) -> QuantRS2Result<()> {
398 let file_path = self.cache_file_path(&compiled.gate_id);
399
400 if let Some(parent) = file_path.parent() {
402 fs::create_dir_all(parent)?;
403 }
404
405 let file = File::create(&file_path)?;
406 let writer = BufWriter::new(file);
407
408 bincode::serialize_into(writer, compiled)?;
409
410 Ok(())
411 }
412
413 fn queue_for_write(&self, compiled: CompiledGate) -> QuantRS2Result<()> {
415 let mut queue = self.write_queue.write().unwrap();
416 queue.push_back(compiled);
417 Ok(())
418 }
419
420 fn start_background_writer(
422 cache_dir: PathBuf,
423 write_queue: Arc<RwLock<VecDeque<CompiledGate>>>,
424 ) -> std::thread::JoinHandle<()> {
425 std::thread::spawn(move || loop {
426 std::thread::sleep(Duration::from_millis(100));
427
428 let gates_to_write: Vec<CompiledGate> = {
429 let mut queue = write_queue.write().unwrap();
430 queue.drain(..).collect()
431 };
432
433 for compiled in gates_to_write {
434 let filename = format!(
435 "{}.cache",
436 &compiled.gate_id[..16.min(compiled.gate_id.len())]
437 );
438 let file_path = cache_dir.join(filename);
439
440 if let Err(e) = Self::write_gate_to_file(&file_path, &compiled, 3) {
441 eprintln!("Failed to write gate to cache: {}", e);
442 }
443 }
444 })
445 }
446
447 fn write_gate_to_file(
449 file_path: &Path,
450 compiled: &CompiledGate,
451 _compression_level: i32,
452 ) -> QuantRS2Result<()> {
453 if let Some(parent) = file_path.parent() {
454 fs::create_dir_all(parent)?;
455 }
456
457 let file = File::create(file_path)?;
458 let writer = BufWriter::new(file);
459
460 bincode::serialize_into(writer, compiled)?;
461
462 Ok(())
463 }
464
465 fn cache_file_path(&self, gate_id: &str) -> PathBuf {
467 let filename = format!("{}.cache", &gate_id[..16.min(gate_id.len())]);
469 self.config.cache_dir.join(filename)
470 }
471
472 fn estimate_size(&self, compiled: &CompiledGate) -> usize {
474 std::mem::size_of::<CompiledGate>() +
475 compiled.matrix.len() * std::mem::size_of::<Complex64>() +
476 compiled.gate_id.len() +
477 1024
479 }
480
481 fn record_hit(&self, gate_id: &str) -> QuantRS2Result<()> {
483 let mut stats = self.statistics.write().unwrap();
484 stats.total_hits += 1;
485
486 if let Ok(cache) = self.memory_cache.read() {
488 if let Some(compiled) = cache.gates.get(gate_id) {
489 stats.time_saved_us += compiled.metadata.compilation_time_us;
490 }
491 }
492
493 Ok(())
494 }
495
496 fn record_miss(&self) -> QuantRS2Result<()> {
498 let mut stats = self.statistics.write().unwrap();
499 stats.total_misses += 1;
500 Ok(())
501 }
502
503 pub fn clear(&self) -> QuantRS2Result<()> {
505 let mut cache = self.memory_cache.write().unwrap();
507 cache.gates.clear();
508 cache.lru_queue.clear();
509 cache.current_size = 0;
510
511 if self.config.enable_persistence && self.config.cache_dir.exists() {
513 for entry in fs::read_dir(&self.config.cache_dir)? {
514 let entry = entry?;
515 if entry.path().extension().and_then(|s| s.to_str()) == Some("cache") {
516 fs::remove_file(entry.path())?;
517 }
518 }
519 }
520
521 let mut stats = self.statistics.write().unwrap();
523 *stats = CacheStatistics {
524 total_hits: 0,
525 total_misses: 0,
526 time_saved_us: 0,
527 num_entries: 0,
528 total_size_bytes: 0,
529 created_at: current_timestamp(),
530 };
531
532 Ok(())
533 }
534
535 pub fn statistics(&self) -> CacheStatistics {
537 self.statistics.read().unwrap().clone()
538 }
539
540 pub fn optimize(&self) -> QuantRS2Result<()> {
542 if !self.config.enable_persistence {
543 return Ok(());
544 }
545
546 let mut removed_count = 0;
547
548 for entry in fs::read_dir(&self.config.cache_dir)? {
549 let entry = entry?;
550 let path = entry.path();
551
552 if path.extension().and_then(|s| s.to_str()) == Some("cache") {
553 let metadata = fs::metadata(&path)?;
554 let modified = metadata.modified()?;
555 let age = SystemTime::now()
556 .duration_since(modified)
557 .unwrap_or_default();
558
559 if age > self.config.expiration_time {
560 fs::remove_file(&path)?;
561 removed_count += 1;
562 }
563 }
564 }
565
566 println!(
567 "Cache optimization: removed {} expired entries",
568 removed_count
569 );
570 Ok(())
571 }
572
573 pub fn export_statistics(&self, path: &Path) -> QuantRS2Result<()> {
575 let stats = self.statistics();
576 let json = serde_json::to_string_pretty(&stats)?;
577 fs::write(path, json)?;
578 Ok(())
579 }
580
581 pub fn precompile_common_gates(&self) -> QuantRS2Result<()> {
583 use crate::gate::{multi::*, single::*};
584
585 let single_qubit_gates: Vec<Box<dyn GateOp>> = vec![
587 Box::new(Hadamard {
588 target: crate::qubit::QubitId(0),
589 }),
590 Box::new(PauliX {
591 target: crate::qubit::QubitId(0),
592 }),
593 Box::new(PauliY {
594 target: crate::qubit::QubitId(0),
595 }),
596 Box::new(PauliZ {
597 target: crate::qubit::QubitId(0),
598 }),
599 Box::new(Phase {
600 target: crate::qubit::QubitId(0),
601 }),
602 Box::new(RotationZ {
603 target: crate::qubit::QubitId(0),
604 theta: std::f64::consts::PI / 4.0,
605 }),
606 ];
607
608 for gate in single_qubit_gates {
609 let _ = self.get_or_compile(gate.as_ref(), |g| compile_single_qubit_gate(g))?;
610 }
611
612 let two_qubit_gates: Vec<Box<dyn GateOp>> = vec![
614 Box::new(CNOT {
615 control: crate::qubit::QubitId(0),
616 target: crate::qubit::QubitId(1),
617 }),
618 Box::new(CZ {
619 control: crate::qubit::QubitId(0),
620 target: crate::qubit::QubitId(1),
621 }),
622 Box::new(SWAP {
623 qubit1: crate::qubit::QubitId(0),
624 qubit2: crate::qubit::QubitId(1),
625 }),
626 ];
627
628 for gate in two_qubit_gates {
629 let _ = self.get_or_compile(gate.as_ref(), |g| compile_two_qubit_gate(g))?;
630 }
631
632 Ok(())
633 }
634}
635
636fn current_timestamp() -> u64 {
638 SystemTime::now()
639 .duration_since(UNIX_EPOCH)
640 .unwrap_or_default()
641 .as_secs()
642}
643
644fn compile_single_qubit_gate(gate: &dyn GateOp) -> QuantRS2Result<CompiledGate> {
646 let matrix = gate.matrix()?;
647 let gate_id = String::new(); let is_diagonal = matrix[1].norm() < 1e-10 && matrix[2].norm() < 1e-10;
651 let diagonal = if is_diagonal {
652 Some(vec![matrix[0], matrix[3]])
653 } else {
654 None
655 };
656
657 let simd_layout = if false {
659 Some(SimdLayout {
661 layout_type: "avx2".to_string(),
662 data: matrix.clone(),
663 stride: 2,
664 alignment: 32,
665 })
666 } else {
667 None
668 };
669
670 Ok(CompiledGate {
671 gate_id,
672 matrix,
673 num_qubits: 1,
674 optimizations: GateOptimizations {
675 diagonal,
676 decomposition: None,
677 simd_layout,
678 gpu_kernel_id: None,
679 tensor_network: None,
680 },
681 metadata: CompilationMetadata {
682 compiled_at: current_timestamp(),
683 compilation_time_us: 0, compiler_version: env!("CARGO_PKG_VERSION").to_string(),
685 target_hardware: "generic".to_string(),
686 optimization_level: 2,
687 cache_hits: 0,
688 last_accessed: current_timestamp(),
689 },
690 })
691}
692
693fn compile_two_qubit_gate(gate: &dyn GateOp) -> QuantRS2Result<CompiledGate> {
694 let matrix = gate.matrix()?;
695 let gate_id = String::new(); let decomposition = if gate.name() == "CNOT" {
699 Some(GateDecomposition {
700 gates: vec!["H".to_string(), "CZ".to_string(), "H".to_string()],
701 parameters: vec![vec![], vec![], vec![]],
702 targets: vec![vec![1], vec![0, 1], vec![1]],
703 gate_count: 3,
704 error: 1e-15,
705 })
706 } else {
707 None
708 };
709
710 Ok(CompiledGate {
711 gate_id,
712 matrix,
713 num_qubits: 2,
714 optimizations: GateOptimizations {
715 diagonal: None,
716 decomposition,
717 simd_layout: None,
718 gpu_kernel_id: Some(format!("{}_kernel", gate.name().to_lowercase())),
719 tensor_network: None,
720 },
721 metadata: CompilationMetadata {
722 compiled_at: current_timestamp(),
723 compilation_time_us: 0,
724 compiler_version: env!("CARGO_PKG_VERSION").to_string(),
725 target_hardware: "generic".to_string(),
726 optimization_level: 2,
727 cache_hits: 0,
728 last_accessed: current_timestamp(),
729 },
730 })
731}
732
733static GLOBAL_CACHE: OnceLock<Arc<CompilationCache>> = OnceLock::new();
735
736pub fn initialize_compilation_cache(config: CacheConfig) -> QuantRS2Result<()> {
738 let cache = CompilationCache::new(config)?;
739
740 GLOBAL_CACHE.set(Arc::new(cache)).map_err(|_| {
741 QuantRS2Error::RuntimeError("Compilation cache already initialized".to_string())
742 })?;
743
744 Ok(())
745}
746
747pub fn get_compilation_cache() -> QuantRS2Result<Arc<CompilationCache>> {
749 GLOBAL_CACHE
750 .get()
751 .map(Arc::clone)
752 .ok_or_else(|| QuantRS2Error::RuntimeError("Compilation cache not initialized".to_string()))
753}
754
755#[cfg(test)]
756mod tests {
757 use super::*;
758 use crate::gate::single::{Hadamard, PauliX};
759 use crate::qubit::QubitId;
760 use std::fs;
761 #[test]
764 fn test_cache_creation() {
765 let temp_dir = std::env::temp_dir().join("quantrs_test_cache");
766 let config = CacheConfig {
767 cache_dir: temp_dir,
768 enable_persistence: false, ..Default::default()
770 };
771
772 let cache = CompilationCache::new(config).unwrap();
773 let stats = cache.statistics();
774
775 assert_eq!(stats.total_hits, 0);
776 assert_eq!(stats.total_misses, 0);
777 assert_eq!(stats.num_entries, 0);
778 }
779
780 #[test]
781 fn test_gate_compilation_and_caching() {
782 let temp_dir = std::env::temp_dir().join(format!(
783 "quantrs_test_caching_{}_{}",
784 std::process::id(),
785 std::time::SystemTime::now()
786 .duration_since(std::time::UNIX_EPOCH)
787 .unwrap()
788 .as_nanos()
789 ));
790 let config = CacheConfig {
791 cache_dir: temp_dir,
792 enable_persistence: false, async_writes: false,
794 ..Default::default()
795 };
796
797 let cache = CompilationCache::new(config).unwrap();
798 cache.clear().unwrap();
800 let gate = Hadamard { target: QubitId(0) };
801
802 let compiled1 = cache
804 .get_or_compile(&gate, compile_single_qubit_gate)
805 .unwrap();
806 let stats1 = cache.statistics();
807 assert_eq!(stats1.total_misses, 1);
808 assert_eq!(stats1.total_hits, 0);
809
810 let compiled2 = cache
812 .get_or_compile(&gate, compile_single_qubit_gate)
813 .unwrap();
814 let stats2 = cache.statistics();
815 assert_eq!(stats2.total_misses, 1);
816 assert_eq!(stats2.total_hits, 1);
817
818 assert_eq!(compiled1.gate_id, compiled2.gate_id);
820 assert_eq!(compiled1.matrix, compiled2.matrix);
821 }
822
823 #[test]
824 fn test_cache_eviction() {
825 let temp_dir = std::env::temp_dir().join(format!("quantrs_test_{}", std::process::id()));
826 let config = CacheConfig {
827 cache_dir: temp_dir,
828 max_memory_entries: 2,
829 enable_persistence: false,
830 ..Default::default()
831 };
832
833 let cache = CompilationCache::new(config).unwrap();
834
835 for i in 0..3 {
837 let gate = PauliX { target: QubitId(i) };
838 let _ = cache
839 .get_or_compile(&gate, compile_single_qubit_gate)
840 .unwrap();
841 }
842
843 let stats = cache.statistics();
844 assert_eq!(stats.num_entries, 2); }
846
847 #[test]
848 fn test_persistent_cache() {
849 let temp_dir = std::env::temp_dir().join(format!("quantrs_test_{}", std::process::id()));
850 let config = CacheConfig {
851 cache_dir: temp_dir,
852 enable_persistence: true,
853 async_writes: false,
854 ..Default::default()
855 };
856
857 let gate = Hadamard { target: QubitId(0) };
858 let gate_id;
859
860 {
862 let cache = CompilationCache::new(config.clone()).unwrap();
863 let compiled = cache
864 .get_or_compile(&gate, compile_single_qubit_gate)
865 .unwrap();
866 gate_id = compiled.gate_id.clone();
867 }
868
869 {
871 let cache = CompilationCache::new(config).unwrap();
872 let compiled = cache
873 .get_or_compile(&gate, compile_single_qubit_gate)
874 .unwrap();
875
876 assert_eq!(compiled.gate_id, gate_id);
877
878 let stats = cache.statistics();
879 assert_eq!(stats.total_hits, 1); assert_eq!(stats.total_misses, 0);
881 }
882 }
883
884 #[test]
885 fn test_cache_optimization() {
886 let temp_dir = std::env::temp_dir().join(format!("quantrs_test_{}", std::process::id()));
887 let config = CacheConfig {
888 cache_dir: temp_dir,
889 enable_persistence: true,
890 expiration_time: Duration::from_secs(0), async_writes: false,
892 ..Default::default()
893 };
894
895 let cache = CompilationCache::new(config).unwrap();
896 let gate = Hadamard { target: QubitId(0) };
897
898 let _ = cache
900 .get_or_compile(&gate, compile_single_qubit_gate)
901 .unwrap();
902
903 std::thread::sleep(Duration::from_millis(100));
905 cache.optimize().unwrap();
906
907 cache.clear().unwrap(); let _ = cache
910 .get_or_compile(&gate, compile_single_qubit_gate)
911 .unwrap();
912
913 let stats = cache.statistics();
914 assert_eq!(stats.total_misses, 1); }
916
917 #[test]
918 fn test_precompile_common_gates() {
919 let temp_dir = std::env::temp_dir().join(format!("quantrs_test_{}", std::process::id()));
920 let config = CacheConfig {
921 cache_dir: temp_dir,
922 enable_persistence: false, async_writes: false,
924 ..Default::default()
925 };
926
927 let cache = CompilationCache::new(config).unwrap();
928 cache.clear().unwrap();
930 cache.precompile_common_gates().unwrap();
931
932 let stats = cache.statistics();
933 assert!(stats.num_entries > 0);
934 println!("Precompiled {} gates", stats.num_entries);
935 }
936
937 #[test]
938 fn test_statistics_export() {
939 let temp_dir = std::env::temp_dir().join(format!(
940 "quantrs_test_stats_{}_{}",
941 std::process::id(),
942 std::time::SystemTime::now()
943 .duration_since(std::time::UNIX_EPOCH)
944 .unwrap()
945 .as_nanos()
946 ));
947 let config = CacheConfig {
948 cache_dir: temp_dir.clone(),
949 enable_persistence: false, ..Default::default()
951 };
952
953 let cache = CompilationCache::new(config).unwrap();
954 cache.clear().unwrap();
956
957 let gate = Hadamard { target: QubitId(0) };
959 let _ = cache
960 .get_or_compile(&gate, compile_single_qubit_gate)
961 .unwrap();
962 let _ = cache
963 .get_or_compile(&gate, compile_single_qubit_gate)
964 .unwrap();
965
966 std::fs::create_dir_all(&temp_dir).unwrap();
968 let stats_path = temp_dir.join("stats.json");
969 cache.export_statistics(&stats_path).unwrap();
970
971 assert!(stats_path.exists());
973 let contents = fs::read_to_string(&stats_path).unwrap();
974 let parsed: CacheStatistics = serde_json::from_str(&contents).unwrap();
975
976 assert_eq!(parsed.total_hits, 1);
977 assert_eq!(parsed.total_misses, 1);
978 }
979}