1use crate::{
8 automatic_parallelization::{AutoParallelConfig, AutoParallelEngine},
9 circuit_optimization::{CircuitOptimizer, OptimizationConfig},
10 distributed_simulator::{DistributedQuantumSimulator, DistributedSimulatorConfig},
11 error::{Result, SimulatorError},
12 large_scale_simulator::{LargeScaleQuantumSimulator, LargeScaleSimulatorConfig},
13 simulator::SimulatorResult,
14 statevector::StateVectorSimulator,
15};
16use quantrs2_circuit::builder::{Circuit, Simulator};
17use quantrs2_core::{
18 error::{QuantRS2Error, QuantRS2Result},
19 gate::GateOp,
20 qubit::QubitId,
21 register::Register,
22};
23use std::fmt::Write;
24
25#[cfg(all(feature = "gpu", not(target_os = "macos")))]
26use crate::gpu::SciRS2GpuStateVectorSimulator;
27use scirs2_core::parallel_ops::current_num_threads; use scirs2_core::Complex64;
29use serde::{Deserialize, Serialize};
30use std::collections::HashMap;
31use std::sync::Arc;
32use std::time::{Duration, Instant};
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct AutoOptimizerConfig {
37 pub enable_profiling: bool,
39 pub memory_budget: usize,
41 pub cpu_utilization_threshold: f64,
43 pub gpu_check_timeout: Duration,
45 pub enable_distributed: bool,
47 pub scirs2_optimization_level: OptimizationLevel,
49 pub fallback_strategy: FallbackStrategy,
51 pub analysis_depth: AnalysisDepth,
53 pub performance_cache_size: usize,
55 pub backend_preferences: Vec<BackendType>,
57}
58
59impl Default for AutoOptimizerConfig {
60 fn default() -> Self {
61 Self {
62 enable_profiling: true,
63 memory_budget: 8 * 1024 * 1024 * 1024, cpu_utilization_threshold: 0.8,
65 gpu_check_timeout: Duration::from_millis(1000),
66 enable_distributed: true,
67 scirs2_optimization_level: OptimizationLevel::Aggressive,
68 fallback_strategy: FallbackStrategy::Conservative,
69 analysis_depth: AnalysisDepth::Deep,
70 performance_cache_size: 1000,
71 backend_preferences: vec![
72 BackendType::SciRS2Gpu,
73 BackendType::LargeScale,
74 BackendType::Distributed,
75 BackendType::StateVector,
76 ],
77 }
78 }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
83pub enum BackendType {
84 StateVector,
86 SciRS2Gpu,
88 LargeScale,
90 Distributed,
92 Auto,
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
98pub enum OptimizationLevel {
99 None,
101 Basic,
103 Advanced,
105 Aggressive,
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
111pub enum FallbackStrategy {
112 Conservative,
114 Aggressive,
116 Fail,
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
122pub enum AnalysisDepth {
123 Quick,
125 Standard,
127 Deep,
129}
130
131#[derive(Debug, Clone)]
133pub struct CircuitCharacteristics {
134 pub num_qubits: usize,
136 pub num_gates: usize,
138 pub circuit_depth: usize,
140 pub gate_distribution: HashMap<String, usize>,
142 pub parallelism_potential: f64,
144 pub memory_requirement: usize,
146 pub complexity_score: f64,
148 pub two_qubit_density: f64,
150 pub connectivity_properties: ConnectivityProperties,
152 pub entanglement_depth: usize,
154 pub noise_susceptibility: f64,
156}
157
158#[derive(Debug, Clone)]
160pub struct ConnectivityProperties {
161 pub max_degree: usize,
163 pub avg_degree: f64,
165 pub connected_components: usize,
167 pub diameter: usize,
169 pub clustering_coefficient: f64,
171}
172
173#[derive(Debug, Clone)]
175pub struct BackendRecommendation {
176 pub backend_type: BackendType,
178 pub confidence: f64,
180 pub expected_improvement: f64,
182 pub estimated_execution_time: Duration,
184 pub estimated_memory_usage: usize,
186 pub reasoning: String,
188 pub alternatives: Vec<(BackendType, f64)>,
190 pub prediction_model: String,
192}
193
194#[derive(Debug, Clone)]
196pub struct PerformanceMetrics {
197 pub execution_time: Duration,
199 pub memory_usage: usize,
204 pub cpu_utilization: Option<f64>,
208 pub gpu_utilization: Option<f64>,
210 pub throughput: f64,
212 pub error_rate: Option<f64>,
215}
216
217#[derive(Debug, Clone)]
219pub struct PerformanceHistory {
220 pub circuit_hash: u64,
222 pub backend_type: BackendType,
224 pub metrics: PerformanceMetrics,
226 pub timestamp: Instant,
228}
229
230pub struct AutoOptimizer {
232 config: AutoOptimizerConfig,
234 circuit_optimizer: CircuitOptimizer,
236 parallel_engine: AutoParallelEngine,
238 performance_cache: Vec<PerformanceHistory>,
240 backend_availability: HashMap<BackendType, bool>,
242 scirs2_analyzer: SciRS2CircuitAnalyzer,
244}
245
246struct SciRS2CircuitAnalyzer {
248 enable_advanced_features: bool,
250}
251
252impl AutoOptimizer {
253 #[must_use]
255 pub fn new() -> Self {
256 Self::with_config(AutoOptimizerConfig::default())
257 }
258
259 #[must_use]
261 pub fn with_config(config: AutoOptimizerConfig) -> Self {
262 let optimization_config = OptimizationConfig {
263 enable_gate_fusion: true,
264 enable_redundant_elimination: true,
265 enable_commutation_reordering: true,
266 enable_single_qubit_optimization: true,
267 enable_two_qubit_optimization: true,
268 max_passes: 3,
269 enable_depth_reduction: true,
270 };
271
272 let parallel_config = AutoParallelConfig {
273 max_threads: current_num_threads(), min_gates_for_parallel: 20,
275 strategy: crate::automatic_parallelization::ParallelizationStrategy::Hybrid,
276 ..Default::default()
277 };
278
279 Self {
280 config,
281 circuit_optimizer: CircuitOptimizer::with_config(optimization_config),
282 parallel_engine: AutoParallelEngine::new(parallel_config),
283 performance_cache: Vec::new(),
284 backend_availability: HashMap::new(),
285 scirs2_analyzer: SciRS2CircuitAnalyzer {
286 enable_advanced_features: true,
287 },
288 }
289 }
290
291 pub fn analyze_circuit<const N: usize>(
293 &self,
294 circuit: &Circuit<N>,
295 ) -> QuantRS2Result<CircuitCharacteristics> {
296 let start_time = Instant::now();
297
298 let num_qubits = circuit.num_qubits();
300 let num_gates = circuit.num_gates();
301 let circuit_depth = self.calculate_circuit_depth(circuit);
302
303 let gate_distribution = self.analyze_gate_distribution(circuit);
305
306 let parallelism_potential = self.analyze_parallelism_potential(circuit)?;
308
309 let memory_requirement = self.estimate_memory_requirement(num_qubits, num_gates);
311
312 let complexity_score = self.calculate_complexity_score(circuit)?;
314
315 let two_qubit_density = self.calculate_two_qubit_density(circuit);
317
318 let connectivity_properties = self.analyze_connectivity(circuit)?;
320
321 let entanglement_depth = self.estimate_entanglement_depth(circuit)?;
323
324 let noise_susceptibility = self.analyze_noise_susceptibility(circuit);
326
327 let analysis_time = start_time.elapsed();
328 if self.config.enable_profiling {
329 println!("Circuit analysis completed in {analysis_time:?}");
330 }
331
332 Ok(CircuitCharacteristics {
333 num_qubits,
334 num_gates,
335 circuit_depth,
336 gate_distribution,
337 parallelism_potential,
338 memory_requirement,
339 complexity_score,
340 two_qubit_density,
341 connectivity_properties,
342 entanglement_depth,
343 noise_susceptibility,
344 })
345 }
346
347 pub fn recommend_backend<const N: usize>(
349 &mut self,
350 circuit: &Circuit<N>,
351 ) -> QuantRS2Result<BackendRecommendation> {
352 let characteristics = self.analyze_circuit(circuit)?;
354
355 self.update_backend_availability()?;
357
358 if let Some(cached_result) = self.check_performance_cache(&characteristics) {
360 return Ok(self.build_recommendation_from_cache(cached_result));
361 }
362
363 let recommendation = self.generate_backend_recommendation(&characteristics)?;
365
366 Ok(recommendation)
367 }
368
369 pub fn execute_optimized<const N: usize>(
371 &mut self,
372 circuit: &Circuit<N>,
373 ) -> Result<SimulatorResult<N>> {
374 let recommendation = self
376 .recommend_backend(circuit)
377 .map_err(|e| SimulatorError::ComputationError(e.to_string()))?;
378
379 if self.config.enable_profiling {
380 println!(
381 "Using {} backend (confidence: {:.2})",
382 self.backend_type_name(recommendation.backend_type),
383 recommendation.confidence
384 );
385 println!("Reasoning: {}", recommendation.reasoning);
386 }
387
388 let start_time = Instant::now();
390 let register = self.execute_with_backend(circuit, recommendation.backend_type)?;
391 let execution_time = start_time.elapsed();
392
393 let result = self.register_to_simulator_result(register);
395
396 if self.config.enable_profiling {
398 self.record_performance_metrics(circuit, recommendation.backend_type, execution_time);
399 println!("Execution completed in {execution_time:?}");
400 }
401
402 Ok(result)
403 }
404
405 fn calculate_circuit_depth<const N: usize>(&self, circuit: &Circuit<N>) -> usize {
407 let mut qubit_depths = HashMap::new();
408 let mut max_depth = 0;
409
410 for gate in circuit.gates() {
411 let qubits = gate.qubits();
412
413 let input_depth = qubits
415 .iter()
416 .map(|&q| qubit_depths.get(&q).copied().unwrap_or(0))
417 .max()
418 .unwrap_or(0);
419
420 let new_depth = input_depth + 1;
421
422 for &qubit in &qubits {
424 qubit_depths.insert(qubit, new_depth);
425 }
426
427 max_depth = max_depth.max(new_depth);
428 }
429
430 max_depth
431 }
432
433 fn analyze_gate_distribution<const N: usize>(
435 &self,
436 circuit: &Circuit<N>,
437 ) -> HashMap<String, usize> {
438 let mut distribution = HashMap::new();
439
440 for gate in circuit.gates() {
441 let gate_name = gate.name().to_string();
442 *distribution.entry(gate_name).or_insert(0) += 1;
443 }
444
445 distribution
446 }
447
448 fn analyze_parallelism_potential<const N: usize>(
450 &self,
451 circuit: &Circuit<N>,
452 ) -> QuantRS2Result<f64> {
453 let analysis = self.parallel_engine.analyze_circuit(circuit)?;
455 Ok(analysis.efficiency)
456 }
457
458 const fn estimate_memory_requirement(&self, num_qubits: usize, num_gates: usize) -> usize {
460 let state_vector_size = (1 << num_qubits) * std::mem::size_of::<Complex64>();
462
463 let overhead = num_gates * 64; state_vector_size + overhead
467 }
468
469 fn calculate_complexity_score<const N: usize>(
471 &self,
472 circuit: &Circuit<N>,
473 ) -> QuantRS2Result<f64> {
474 let num_qubits = circuit.num_qubits() as f64;
475 let num_gates = circuit.num_gates() as f64;
476 let depth = self.calculate_circuit_depth(circuit) as f64;
477
478 let gate_complexity = num_gates * (num_qubits.log2() + 1.0);
480 let depth_complexity = depth * num_qubits;
481 let entanglement_complexity = self.estimate_entanglement_complexity(circuit)?;
482
483 let structural_richness = self.scirs2_analyzer.analyze_circuit_with_scirs2(circuit)?;
486 let richness_factor = 1.0 + structural_richness;
487
488 Ok(
489 (gate_complexity + depth_complexity + entanglement_complexity) * richness_factor
490 / 1000.0,
491 )
492 }
493
494 fn estimate_entanglement_complexity<const N: usize>(
496 &self,
497 circuit: &Circuit<N>,
498 ) -> QuantRS2Result<f64> {
499 let mut entanglement_score = 0.0;
500
501 for gate in circuit.gates() {
502 let qubits = gate.qubits();
503 if qubits.len() >= 2 {
504 entanglement_score += qubits.len() as f64 * qubits.len() as f64;
506 }
507 }
508
509 Ok(entanglement_score)
510 }
511
512 fn calculate_two_qubit_density<const N: usize>(&self, circuit: &Circuit<N>) -> f64 {
514 let total_gates = circuit.num_gates();
515 if total_gates == 0 {
516 return 0.0;
517 }
518
519 let two_qubit_gates = circuit
520 .gates()
521 .iter()
522 .filter(|gate| gate.qubits().len() >= 2)
523 .count();
524
525 two_qubit_gates as f64 / total_gates as f64
526 }
527
528 fn analyze_connectivity<const N: usize>(
530 &self,
531 circuit: &Circuit<N>,
532 ) -> QuantRS2Result<ConnectivityProperties> {
533 let mut qubit_connections: HashMap<QubitId, Vec<QubitId>> = HashMap::new();
534
535 for gate in circuit.gates() {
537 let qubits = gate.qubits();
538 if qubits.len() >= 2 {
539 for i in 0..qubits.len() {
540 for j in (i + 1)..qubits.len() {
541 qubit_connections
542 .entry(qubits[i])
543 .or_default()
544 .push(qubits[j]);
545 qubit_connections
546 .entry(qubits[j])
547 .or_default()
548 .push(qubits[i]);
549 }
550 }
551 }
552 }
553
554 let adjacency: HashMap<QubitId, std::collections::HashSet<QubitId>> = qubit_connections
557 .iter()
558 .map(|(&node, neighbors)| {
559 let set: std::collections::HashSet<QubitId> =
560 neighbors.iter().copied().filter(|&n| n != node).collect();
561 (node, set)
562 })
563 .collect();
564
565 let max_degree = adjacency
567 .values()
568 .map(std::collections::HashSet::len)
569 .max()
570 .unwrap_or(0);
571
572 let avg_degree = if adjacency.is_empty() {
573 0.0
574 } else {
575 adjacency
576 .values()
577 .map(std::collections::HashSet::len)
578 .sum::<usize>() as f64
579 / adjacency.len() as f64
580 };
581
582 let connected_components =
586 Self::count_connected_components(&adjacency, circuit.num_qubits());
587
588 let diameter = Self::graph_diameter(&adjacency);
592
593 let clustering_coefficient = Self::clustering_coefficient(&adjacency);
597
598 Ok(ConnectivityProperties {
599 max_degree,
600 avg_degree,
601 connected_components,
602 diameter,
603 clustering_coefficient,
604 })
605 }
606
607 fn count_connected_components(
612 adjacency: &HashMap<QubitId, std::collections::HashSet<QubitId>>,
613 total_qubits: usize,
614 ) -> usize {
615 use std::collections::{HashSet, VecDeque};
616
617 let mut visited: HashSet<QubitId> = HashSet::new();
618 let mut components = 0;
619
620 for &start in adjacency.keys() {
621 if visited.contains(&start) {
622 continue;
623 }
624 components += 1;
625 let mut queue = VecDeque::new();
626 queue.push_back(start);
627 visited.insert(start);
628 while let Some(node) = queue.pop_front() {
629 if let Some(neighbors) = adjacency.get(&node) {
630 for &next in neighbors {
631 if visited.insert(next) {
632 queue.push_back(next);
633 }
634 }
635 }
636 }
637 }
638
639 let connected_qubits = visited.len();
641 let isolated = total_qubits.saturating_sub(connected_qubits);
642 components + isolated
643 }
644
645 fn graph_diameter(adjacency: &HashMap<QubitId, std::collections::HashSet<QubitId>>) -> usize {
650 use std::collections::hash_map::Entry;
651 use std::collections::{HashMap as Map, VecDeque};
652
653 let mut diameter = 0;
654 for &source in adjacency.keys() {
655 let mut distances: Map<QubitId, usize> = Map::new();
656 distances.insert(source, 0);
657 let mut queue = VecDeque::new();
658 queue.push_back(source);
659 while let Some(node) = queue.pop_front() {
660 let current_dist = distances.get(&node).copied().unwrap_or(0);
661 if let Some(neighbors) = adjacency.get(&node) {
662 for &next in neighbors {
663 if let Entry::Vacant(slot) = distances.entry(next) {
664 slot.insert(current_dist + 1);
665 queue.push_back(next);
666 }
667 }
668 }
669 }
670 if let Some(&max_dist) = distances.values().max() {
671 diameter = diameter.max(max_dist);
672 }
673 }
674 diameter
675 }
676
677 fn clustering_coefficient(
684 adjacency: &HashMap<QubitId, std::collections::HashSet<QubitId>>,
685 ) -> f64 {
686 let mut sum = 0.0;
687 let mut counted = 0usize;
688
689 for neighbors in adjacency.values() {
690 let degree = neighbors.len();
691 if degree < 2 {
692 continue;
693 }
694 let neighbor_list: Vec<QubitId> = neighbors.iter().copied().collect();
695 let mut links = 0usize;
696 for i in 0..neighbor_list.len() {
697 for j in (i + 1)..neighbor_list.len() {
698 if let Some(set) = adjacency.get(&neighbor_list[i]) {
699 if set.contains(&neighbor_list[j]) {
700 links += 1;
701 }
702 }
703 }
704 }
705 let possible = degree * (degree - 1) / 2;
706 sum += links as f64 / possible as f64;
707 counted += 1;
708 }
709
710 if counted == 0 {
711 0.0
712 } else {
713 sum / counted as f64
714 }
715 }
716
717 fn estimate_entanglement_depth<const N: usize>(
719 &self,
720 circuit: &Circuit<N>,
721 ) -> QuantRS2Result<usize> {
722 let two_qubit_gates = circuit
724 .gates()
725 .iter()
726 .filter(|gate| gate.qubits().len() >= 2)
727 .count();
728
729 let depth_estimate = (two_qubit_gates as f64).sqrt().ceil() as usize;
731 Ok(depth_estimate.min(circuit.num_qubits()))
732 }
733
734 fn analyze_noise_susceptibility<const N: usize>(&self, circuit: &Circuit<N>) -> f64 {
736 let depth = self.calculate_circuit_depth(circuit) as f64;
737 let two_qubit_density = self.calculate_two_qubit_density(circuit);
738
739 (depth / 100.0 + two_qubit_density).min(1.0)
741 }
742
743 fn update_backend_availability(&mut self) -> QuantRS2Result<()> {
745 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
747 let gpu_available = SciRS2GpuStateVectorSimulator::is_available();
748 #[cfg(any(not(feature = "gpu"), target_os = "macos"))]
749 let gpu_available = false;
750
751 self.backend_availability
752 .insert(BackendType::SciRS2Gpu, gpu_available);
753
754 self.backend_availability
756 .insert(BackendType::StateVector, true);
757 self.backend_availability
758 .insert(BackendType::LargeScale, true);
759
760 self.backend_availability
762 .insert(BackendType::Distributed, false);
763
764 Ok(())
765 }
766
767 fn check_performance_cache(
769 &self,
770 characteristics: &CircuitCharacteristics,
771 ) -> Option<&PerformanceHistory> {
772 self.performance_cache
775 .iter()
776 .find(|&entry| self.are_characteristics_similar(characteristics, entry))
777 .map(|v| v as _)
778 }
779
780 fn are_characteristics_similar(
788 &self,
789 characteristics: &CircuitCharacteristics,
790 entry: &PerformanceHistory,
791 ) -> bool {
792 self.characteristics_bucket_hash(characteristics) == entry.circuit_hash
793 }
794
795 fn characteristics_bucket_hash(&self, characteristics: &CircuitCharacteristics) -> u64 {
802 use std::collections::hash_map::DefaultHasher;
803 use std::hash::{Hash, Hasher};
804
805 let mut hasher = DefaultHasher::new();
806 characteristics.num_qubits.hash(&mut hasher);
807 let gate_magnitude = (characteristics.num_gates as f64).max(1.0).log10().floor() as i64;
809 gate_magnitude.hash(&mut hasher);
810 let density_decile = (characteristics.two_qubit_density * 10.0).round() as i64;
812 density_decile.hash(&mut hasher);
813 hasher.finish()
814 }
815
816 fn build_recommendation_from_cache(
818 &self,
819 cache_entry: &PerformanceHistory,
820 ) -> BackendRecommendation {
821 BackendRecommendation {
822 backend_type: cache_entry.backend_type,
823 confidence: 0.9, expected_improvement: 0.0,
825 estimated_execution_time: cache_entry.metrics.execution_time,
826 estimated_memory_usage: cache_entry.metrics.memory_usage,
827 reasoning: "Based on cached performance data for similar circuits".to_string(),
828 alternatives: Vec::new(),
829 prediction_model: "Cache-based".to_string(),
830 }
831 }
832
833 fn generate_backend_recommendation(
835 &self,
836 characteristics: &CircuitCharacteristics,
837 ) -> QuantRS2Result<BackendRecommendation> {
838 let mut scores: HashMap<BackendType, f64> = HashMap::new();
839 let mut reasoning = String::new();
840
841 for &backend_type in &self.config.backend_preferences {
843 if !self
844 .backend_availability
845 .get(&backend_type)
846 .unwrap_or(&false)
847 {
848 continue;
849 }
850
851 let score = self.score_backend_for_characteristics(backend_type, characteristics);
852 scores.insert(backend_type, score);
853 }
854
855 let (best_backend, best_score) = scores
857 .into_iter()
858 .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
859 .unwrap_or((BackendType::StateVector, 0.5));
860
861 reasoning = self.generate_recommendation_reasoning(best_backend, characteristics);
863
864 let estimated_execution_time = self.estimate_execution_time(best_backend, characteristics);
866 let estimated_memory_usage = characteristics.memory_requirement;
867
868 Ok(BackendRecommendation {
869 backend_type: best_backend,
870 confidence: best_score,
871 expected_improvement: (best_score - 0.5).max(0.0) * 2.0, estimated_execution_time,
873 estimated_memory_usage,
874 reasoning,
875 alternatives: Vec::new(),
876 prediction_model: "SciRS2-guided heuristic".to_string(),
877 })
878 }
879
880 fn score_backend_for_characteristics(
882 &self,
883 backend_type: BackendType,
884 characteristics: &CircuitCharacteristics,
885 ) -> f64 {
886 let mut score: f64 = 0.5; match backend_type {
889 BackendType::StateVector => {
890 if characteristics.num_qubits <= 20 {
892 score += 0.3;
893 }
894 if characteristics.num_gates <= 1000 {
895 score += 0.2;
896 }
897 }
898 BackendType::SciRS2Gpu => {
899 if characteristics.num_qubits >= 10 && characteristics.num_qubits <= 30 {
901 score += 0.4;
902 }
903 if characteristics.parallelism_potential > 0.5 {
904 score += 0.3;
905 }
906 if characteristics.two_qubit_density > 0.3 {
907 score += 0.2;
908 }
909 }
910 BackendType::LargeScale => {
911 if characteristics.num_qubits >= 20 {
913 score += 0.4;
914 }
915 if characteristics.complexity_score > 0.5 {
916 score += 0.3;
917 }
918 }
919 BackendType::Distributed => {
920 if characteristics.num_qubits >= 30 {
922 score += 0.5;
923 }
924 if characteristics.memory_requirement > self.config.memory_budget / 2 {
925 score += 0.3;
926 }
927 }
928 BackendType::Auto => {
929 score = 0.1;
931 }
932 }
933
934 score.min(1.0)
935 }
936
937 fn generate_recommendation_reasoning(
939 &self,
940 backend_type: BackendType,
941 characteristics: &CircuitCharacteristics,
942 ) -> String {
943 match backend_type {
944 BackendType::StateVector => {
945 format!("CPU state vector simulator recommended for {} qubits, {} gates. Suitable for small circuits with straightforward execution.",
946 characteristics.num_qubits, characteristics.num_gates)
947 }
948 BackendType::SciRS2Gpu => {
949 format!("SciRS2 GPU simulator recommended for {} qubits, {} gates. High parallelism potential ({:.2}) and two-qubit gate density ({:.2}) make GPU acceleration beneficial.",
950 characteristics.num_qubits, characteristics.num_gates, characteristics.parallelism_potential, characteristics.two_qubit_density)
951 }
952 BackendType::LargeScale => {
953 format!("Large-scale simulator recommended for {} qubits, {} gates. Circuit complexity ({:.2}) and depth ({}) require optimized memory management.",
954 characteristics.num_qubits, characteristics.num_gates, characteristics.complexity_score, characteristics.circuit_depth)
955 }
956 BackendType::Distributed => {
957 format!("Distributed simulator recommended for {} qubits, {} gates. Memory requirement ({:.1} MB) exceeds single-node capacity.",
958 characteristics.num_qubits, characteristics.num_gates, characteristics.memory_requirement as f64 / (1024.0 * 1024.0))
959 }
960 BackendType::Auto => "Automatic backend selection".to_string(),
961 }
962 }
963
964 fn estimate_execution_time(
966 &self,
967 backend_type: BackendType,
968 characteristics: &CircuitCharacteristics,
969 ) -> Duration {
970 let base_time_ms = match backend_type {
971 BackendType::StateVector => characteristics.num_gates as u64 * 10,
972 BackendType::SciRS2Gpu => characteristics.num_gates as u64 * 2,
973 BackendType::LargeScale => characteristics.num_gates as u64 * 5,
974 BackendType::Distributed => characteristics.num_gates as u64 * 15,
975 BackendType::Auto => characteristics.num_gates as u64 * 10,
976 };
977
978 let complexity_factor = characteristics.complexity_score.mul_add(2.0, 1.0) as u64;
980 Duration::from_millis(base_time_ms * complexity_factor)
981 }
982
983 fn execute_with_backend<const N: usize>(
985 &self,
986 circuit: &Circuit<N>,
987 backend_type: BackendType,
988 ) -> Result<Register<N>> {
989 match backend_type {
990 BackendType::StateVector => {
991 let simulator = StateVectorSimulator::new();
992 simulator
993 .run(circuit)
994 .map_err(|e| SimulatorError::ComputationError(e.to_string()))
995 .and_then(|result| {
996 Register::with_amplitudes(result.amplitudes().to_vec())
997 .map_err(|e| SimulatorError::ComputationError(e.to_string()))
998 })
999 }
1000 BackendType::SciRS2Gpu => {
1001 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
1002 {
1003 let mut simulator = SciRS2GpuStateVectorSimulator::new()
1004 .map_err(|e| SimulatorError::ComputationError(e.to_string()))?;
1005 use crate::simulator::Simulator;
1006 simulator
1007 .run(circuit)
1008 .map_err(|e| SimulatorError::ComputationError(e.to_string()))
1009 .and_then(|result| {
1010 Register::with_amplitudes(result.amplitudes().to_vec())
1011 .map_err(|e| SimulatorError::ComputationError(e.to_string()))
1012 })
1013 }
1014 #[cfg(any(not(feature = "gpu"), target_os = "macos"))]
1015 {
1016 let simulator = StateVectorSimulator::new();
1018 simulator
1019 .run(circuit)
1020 .map_err(|e| SimulatorError::ComputationError(e.to_string()))
1021 .and_then(|result| {
1022 Register::with_amplitudes(result.amplitudes().to_vec())
1023 .map_err(|e| SimulatorError::ComputationError(e.to_string()))
1024 })
1025 }
1026 }
1027 BackendType::LargeScale => {
1028 let config = LargeScaleSimulatorConfig::default();
1030 let simulator = LargeScaleQuantumSimulator::new(config)
1031 .map_err(|e| SimulatorError::ComputationError(e.to_string()))?;
1032 simulator
1033 .run(circuit)
1034 .map_err(|e| SimulatorError::ComputationError(e.to_string()))
1035 }
1036 BackendType::Distributed => {
1037 let config = LargeScaleSimulatorConfig::default();
1039 let simulator = LargeScaleQuantumSimulator::new(config)
1040 .map_err(|e| SimulatorError::ComputationError(e.to_string()))?;
1041 simulator
1042 .run(circuit)
1043 .map_err(|e| SimulatorError::ComputationError(e.to_string()))
1044 }
1045 BackendType::Auto => {
1046 let simulator = StateVectorSimulator::new();
1048 simulator
1049 .run(circuit)
1050 .map_err(|e| SimulatorError::ComputationError(e.to_string()))
1051 }
1052 }
1053 }
1054
1055 fn register_to_simulator_result<const N: usize>(
1057 &self,
1058 register: Register<N>,
1059 ) -> SimulatorResult<N> {
1060 let amplitudes = register.amplitudes().to_vec();
1062
1063 SimulatorResult {
1064 amplitudes,
1065 num_qubits: N,
1066 }
1067 }
1068
1069 fn record_performance_metrics<const N: usize>(
1071 &mut self,
1072 circuit: &Circuit<N>,
1073 backend_type: BackendType,
1074 execution_time: Duration,
1075 ) {
1076 let state_vector_bytes = (1usize << N) * std::mem::size_of::<Complex64>();
1080
1081 let elapsed_secs = execution_time.as_secs_f64();
1082 let throughput = if elapsed_secs > 0.0 {
1083 circuit.num_gates() as f64 / elapsed_secs
1084 } else {
1085 0.0
1086 };
1087
1088 let metrics = PerformanceMetrics {
1089 execution_time,
1090 memory_usage: state_vector_bytes,
1091 cpu_utilization: None,
1093 gpu_utilization: None,
1094 throughput,
1095 error_rate: None,
1097 };
1098
1099 let circuit_hash = match self.analyze_circuit(circuit) {
1103 Ok(characteristics) => self.characteristics_bucket_hash(&characteristics),
1104 Err(_) => self.compute_circuit_hash(circuit),
1105 };
1106
1107 let history_entry = PerformanceHistory {
1108 circuit_hash,
1109 backend_type,
1110 metrics,
1111 timestamp: Instant::now(),
1112 };
1113
1114 self.performance_cache.push(history_entry);
1115
1116 if self.performance_cache.len() > self.config.performance_cache_size {
1118 self.performance_cache.remove(0);
1119 }
1120 }
1121
1122 fn compute_circuit_hash<const N: usize>(&self, circuit: &Circuit<N>) -> u64 {
1124 use std::collections::hash_map::DefaultHasher;
1125 use std::hash::{Hash, Hasher};
1126
1127 let mut hasher = DefaultHasher::new();
1128 circuit.num_gates().hash(&mut hasher);
1129 circuit.num_qubits().hash(&mut hasher);
1130
1131 for gate in circuit.gates() {
1132 gate.name().hash(&mut hasher);
1133 gate.qubits().len().hash(&mut hasher);
1134 }
1135
1136 hasher.finish()
1137 }
1138
1139 const fn backend_type_name(&self, backend_type: BackendType) -> &'static str {
1141 match backend_type {
1142 BackendType::StateVector => "CPU StateVector",
1143 BackendType::SciRS2Gpu => "SciRS2 GPU",
1144 BackendType::LargeScale => "Large-Scale",
1145 BackendType::Distributed => "Distributed",
1146 BackendType::Auto => "Auto",
1147 }
1148 }
1149
1150 #[must_use]
1152 pub fn get_performance_summary(&self) -> String {
1153 let total_circuits = self.performance_cache.len();
1154 if total_circuits == 0 {
1155 return "No performance data available".to_string();
1156 }
1157
1158 let avg_execution_time = self
1159 .performance_cache
1160 .iter()
1161 .map(|entry| entry.metrics.execution_time.as_millis())
1162 .sum::<u128>()
1163 / total_circuits as u128;
1164
1165 let backend_usage: HashMap<BackendType, usize> =
1166 self.performance_cache
1167 .iter()
1168 .fold(HashMap::new(), |mut acc, entry| {
1169 *acc.entry(entry.backend_type).or_insert(0) += 1;
1170 acc
1171 });
1172
1173 let mut summary = "AutoOptimizer Performance Summary\n".to_string();
1174 writeln!(summary, "Total circuits processed: {total_circuits}")
1175 .expect("Writing to String should never fail");
1176 writeln!(summary, "Average execution time: {avg_execution_time}ms")
1177 .expect("Writing to String should never fail");
1178 summary.push_str("Backend usage:\n");
1179
1180 for (backend, count) in backend_usage {
1181 let percentage = (count as f64 / total_circuits as f64) * 100.0;
1182 writeln!(
1183 summary,
1184 " {}: {} ({:.1}%)",
1185 self.backend_type_name(backend),
1186 count,
1187 percentage
1188 )
1189 .expect("Writing to String should never fail");
1190 }
1191
1192 summary
1193 }
1194}
1195
1196impl Default for AutoOptimizer {
1197 fn default() -> Self {
1198 Self::new()
1199 }
1200}
1201
1202impl SciRS2CircuitAnalyzer {
1203 fn analyze_circuit_with_scirs2<const N: usize>(
1212 &self,
1213 circuit: &Circuit<N>,
1214 ) -> QuantRS2Result<f64> {
1215 if !self.enable_advanced_features {
1216 return Ok(0.0);
1217 }
1218
1219 let num_gates = circuit.num_gates();
1220 if num_gates == 0 || N == 0 {
1221 return Ok(0.0);
1222 }
1223
1224 let gates_per_qubit = num_gates as f64 / N as f64;
1227 let utilization = gates_per_qubit / (1.0 + gates_per_qubit);
1228
1229 let two_qubit_gates = circuit
1231 .gates()
1232 .iter()
1233 .filter(|gate| gate.qubits().len() >= 2)
1234 .count();
1235 let entangling_fraction = two_qubit_gates as f64 / num_gates as f64;
1236
1237 let total_arity: usize = circuit.gates().iter().map(|gate| gate.qubits().len()).sum();
1239 let mean_arity = total_arity as f64 / num_gates as f64;
1240 let arity_score = (mean_arity / N as f64).min(1.0);
1241
1242 Ok(((utilization + entangling_fraction + arity_score) / 3.0).clamp(0.0, 1.0))
1244 }
1245}
1246
1247pub fn execute_with_auto_optimization<const N: usize>(
1249 circuit: &Circuit<N>,
1250) -> Result<SimulatorResult<N>> {
1251 let mut optimizer = AutoOptimizer::new();
1252 optimizer.execute_optimized(circuit)
1253}
1254
1255pub fn recommend_backend_for_circuit<const N: usize>(
1257 circuit: &Circuit<N>,
1258) -> QuantRS2Result<BackendRecommendation> {
1259 let mut optimizer = AutoOptimizer::new();
1260 optimizer.recommend_backend(circuit)
1261}
1262
1263#[cfg(test)]
1264mod tests {
1265 use super::*;
1266 use quantrs2_circuit::builder::CircuitBuilder;
1267
1268 #[test]
1269 fn test_auto_optimizer_creation() {
1270 let optimizer = AutoOptimizer::new();
1271 assert!(optimizer.config.enable_profiling);
1272 }
1273
1274 #[test]
1275 fn test_circuit_characteristics_analysis() {
1276 let optimizer = AutoOptimizer::new();
1277
1278 let mut builder = CircuitBuilder::<4>::new();
1280 let _ = builder.h(0);
1281 let _ = builder.cnot(0, 1);
1282 let _ = builder.h(2);
1283 let _ = builder.cnot(2, 3);
1284 let circuit = builder.build();
1285
1286 let characteristics = optimizer
1287 .analyze_circuit(&circuit)
1288 .expect("Failed to analyze circuit characteristics");
1289
1290 assert_eq!(characteristics.num_qubits, 4);
1291 assert_eq!(characteristics.num_gates, 4);
1292 assert!(characteristics.circuit_depth > 0);
1293 assert!(characteristics.two_qubit_density > 0.0);
1294 }
1295
1296 #[test]
1297 fn test_backend_recommendation() {
1298 let mut optimizer = AutoOptimizer::new();
1299
1300 let mut builder = CircuitBuilder::<2>::new();
1302 let _ = builder.h(0);
1303 let _ = builder.cnot(0, 1);
1304 let circuit = builder.build();
1305
1306 let recommendation = optimizer
1307 .recommend_backend(&circuit)
1308 .expect("Failed to get backend recommendation");
1309
1310 assert!(recommendation.confidence > 0.0);
1311 assert!(!recommendation.reasoning.is_empty());
1312 }
1313
1314 #[test]
1315 fn test_execute_with_optimization() {
1316 let mut optimizer = AutoOptimizer::new();
1317
1318 let mut builder = CircuitBuilder::<2>::new();
1320 let _ = builder.h(0);
1321 let _ = builder.cnot(0, 1);
1322 let circuit = builder.build();
1323
1324 let result = optimizer.execute_optimized(&circuit);
1325 assert!(result.is_ok());
1326
1327 if let Ok(sim_result) = result {
1328 assert_eq!(sim_result.num_qubits, 2);
1329 assert_eq!(sim_result.amplitudes.len(), 4);
1330 }
1331 }
1332
1333 #[test]
1334 fn test_convenience_functions() {
1335 let mut builder = CircuitBuilder::<2>::new();
1337 let _ = builder.h(0);
1338 let _ = builder.cnot(0, 1);
1339 let circuit = builder.build();
1340
1341 let recommendation = recommend_backend_for_circuit(&circuit);
1343 assert!(recommendation.is_ok());
1344
1345 let result = execute_with_auto_optimization(&circuit);
1347 assert!(result.is_ok());
1348 }
1349}