1use crate::error::QuantRS2Error;
8use crate::gate_translation::GateType;
9use crate::scirs2_quantum_profiler::{
10 CircuitProfilingResult, GateProfilingResult, MemoryAnalysis, OptimizationRecommendation,
11 ProfilingPrecision, QuantumGate, SimdAnalysis,
12};
13use scirs2_core::Complex64;
14use crate::parallel_ops_stubs::*;
16use crate::buffer_pool::BufferPool;
18use crate::platform::PlatformCapabilities;
19use scirs2_core::ndarray::{Array1, Array2, ArrayView1};
20use serde::{Deserialize, Serialize};
21use std::collections::{BTreeMap, HashMap, VecDeque};
22use std::io::Write;
23use std::sync::{
24 atomic::{AtomicU64, AtomicUsize, Ordering},
25 Arc, Mutex,
26};
27use std::time::{Duration, Instant};
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct EnhancedProfilingConfig {
32 pub precision: ProfilingPrecision,
34
35 pub enable_deep_analysis: bool,
37
38 pub track_memory_patterns: bool,
40
41 pub profile_simd_operations: bool,
43
44 pub track_parallel_patterns: bool,
46
47 pub enable_cache_analysis: bool,
49
50 pub track_memory_bandwidth: bool,
52
53 pub enable_instruction_profiling: bool,
55
56 pub enable_resource_estimation: bool,
58
59 pub analyze_noise_impact: bool,
61
62 pub generate_optimizations: bool,
64
65 pub bottleneck_detection_depth: usize,
67
68 pub enable_performance_prediction: bool,
70
71 pub hardware_aware_profiling: bool,
73
74 pub export_formats: Vec<ExportFormat>,
76}
77
78impl Default for EnhancedProfilingConfig {
79 fn default() -> Self {
80 Self {
81 precision: ProfilingPrecision::High,
82 enable_deep_analysis: true,
83 track_memory_patterns: true,
84 profile_simd_operations: true,
85 track_parallel_patterns: true,
86 enable_cache_analysis: true,
87 track_memory_bandwidth: true,
88 enable_instruction_profiling: false,
89 enable_resource_estimation: true,
90 analyze_noise_impact: true,
91 generate_optimizations: true,
92 bottleneck_detection_depth: 5,
93 enable_performance_prediction: true,
94 hardware_aware_profiling: true,
95 export_formats: vec![ExportFormat::JSON, ExportFormat::HTML, ExportFormat::CSV],
96 }
97 }
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
102pub enum ExportFormat {
103 JSON,
104 HTML,
105 CSV,
106 LaTeX,
107 Markdown,
108 Binary,
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
113pub enum MetricType {
114 ExecutionTime,
115 MemoryUsage,
116 CacheHitRate,
117 SimdUtilization,
118 ParallelEfficiency,
119 MemoryBandwidth,
120 InstructionCount,
121 BranchMisprediction,
122 PowerConsumption,
123 ThermalThrottle,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct PerformanceMetrics {
129 pub values: HashMap<MetricType, f64>,
131
132 pub time_series: HashMap<MetricType, Vec<(f64, f64)>>,
134
135 pub statistics: MetricStatistics,
137
138 pub correlations: HashMap<(MetricType, MetricType), f64>,
140
141 pub anomalies: Vec<AnomalyEvent>,
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct MetricStatistics {
148 pub mean: HashMap<MetricType, f64>,
149 pub std_dev: HashMap<MetricType, f64>,
150 pub min: HashMap<MetricType, f64>,
151 pub max: HashMap<MetricType, f64>,
152 pub percentiles: HashMap<MetricType, BTreeMap<u8, f64>>,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct AnomalyEvent {
158 pub timestamp: f64,
159 pub metric: MetricType,
160 pub severity: AnomalySeverity,
161 pub description: String,
162 pub impact: f64,
163}
164
165#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
166pub enum AnomalySeverity {
167 Low,
168 Medium,
169 High,
170 Critical,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct BottleneckAnalysis {
176 pub bottlenecks: Vec<Bottleneck>,
178
179 pub impact_analysis: HashMap<String, f64>,
181
182 pub opportunities: Vec<OptimizationOpportunity>,
184
185 pub resource_heatmap: Array2<f64>,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
190pub struct Bottleneck {
191 pub location: CircuitLocation,
192 pub bottleneck_type: BottleneckType,
193 pub severity: f64,
194 pub impact_percentage: f64,
195 pub suggested_fixes: Vec<String>,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
199pub enum BottleneckType {
200 MemoryBandwidth,
201 ComputeIntensive,
202 CacheMiss,
203 ParallelizationIssue,
204 SimdUnderutilization,
205 DataDependency,
206 ResourceContention,
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize)]
210pub struct CircuitLocation {
211 pub gate_index: usize,
212 pub layer: usize,
213 pub qubits: Vec<usize>,
214 pub context: String,
215}
216
217#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct OptimizationOpportunity {
219 pub opportunity_type: OpportunityType,
220 pub estimated_improvement: f64,
221 pub difficulty: Difficulty,
222 pub implementation: String,
223 pub trade_offs: Vec<String>,
224}
225
226#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
227pub enum OpportunityType {
228 GateFusion,
229 Parallelization,
230 SimdOptimization,
231 MemoryReordering,
232 CacheOptimization,
233 AlgorithmicImprovement,
234 HardwareSpecific,
235}
236
237#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
238pub enum Difficulty {
239 Trivial,
240 Easy,
241 Medium,
242 Hard,
243 Expert,
244}
245
246#[derive(Serialize, Deserialize)]
248pub struct HardwarePerformanceModel {
249 #[serde(skip, default = "PlatformCapabilities::detect")]
251 pub platform: PlatformCapabilities,
252
253 pub characteristics: HardwareCharacteristics,
255
256 pub scaling_models: HashMap<String, ScalingModel>,
258
259 pub optimization_strategies: Vec<HardwareOptimizationStrategy>,
261}
262
263#[derive(Debug, Clone, Serialize, Deserialize)]
264pub struct HardwareCharacteristics {
265 pub cpu_frequency: f64,
266 pub cache_sizes: Vec<usize>,
267 pub memory_bandwidth: f64,
268 pub simd_width: usize,
269 pub num_cores: usize,
270 pub gpu_available: bool,
271 pub gpu_memory: Option<usize>,
272 pub quantum_accelerator: Option<String>,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
276pub struct ScalingModel {
277 pub model_type: ScalingType,
278 pub parameters: HashMap<String, f64>,
279 pub confidence: f64,
280}
281
282#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
283pub enum ScalingType {
284 Linear,
285 Logarithmic,
286 Polynomial,
287 Exponential,
288 Custom,
289}
290
291#[derive(Debug, Clone, Serialize, Deserialize)]
292pub struct HardwareOptimizationStrategy {
293 pub strategy_name: String,
294 pub applicable_conditions: Vec<String>,
295 pub expected_speedup: f64,
296 pub implementation_cost: f64,
297}
298
299pub struct EnhancedQuantumProfiler {
301 config: EnhancedProfilingConfig,
302 platform_caps: PlatformCapabilities,
303 buffer_pool: Arc<BufferPool<Complex64>>,
304 metrics_collector: Arc<MetricsCollector>,
305 hardware_model: Option<HardwarePerformanceModel>,
306 profiling_state: Arc<Mutex<ProfilingState>>,
307}
308
309struct MetricsCollector {
311 execution_times: Mutex<HashMap<String, Vec<Duration>>>,
312 memory_usage: AtomicUsize,
313 simd_ops_count: AtomicU64,
314 parallel_ops_count: AtomicU64,
315 cache_hits: AtomicU64,
316 cache_misses: AtomicU64,
317 bandwidth_bytes: AtomicU64,
318 start_time: Instant,
319}
320
321impl MetricsCollector {
322 fn new() -> Self {
323 Self {
324 execution_times: Mutex::new(HashMap::new()),
325 memory_usage: AtomicUsize::new(0),
326 simd_ops_count: AtomicU64::new(0),
327 parallel_ops_count: AtomicU64::new(0),
328 cache_hits: AtomicU64::new(0),
329 cache_misses: AtomicU64::new(0),
330 bandwidth_bytes: AtomicU64::new(0),
331 start_time: Instant::now(),
332 }
333 }
334
335 fn record_execution(&self, operation: &str, duration: Duration) {
336 let mut times = self.execution_times.lock().unwrap();
337 times
338 .entry(operation.to_string())
339 .or_insert_with(Vec::new)
340 .push(duration);
341 }
342
343 fn record_memory(&self, bytes: usize) {
344 self.memory_usage.fetch_add(bytes, Ordering::Relaxed);
345 }
346
347 fn record_simd_op(&self) {
348 self.simd_ops_count.fetch_add(1, Ordering::Relaxed);
349 }
350
351 fn record_parallel_op(&self) {
352 self.parallel_ops_count.fetch_add(1, Ordering::Relaxed);
353 }
354
355 fn record_cache_access(&self, hit: bool) {
356 if hit {
357 self.cache_hits.fetch_add(1, Ordering::Relaxed);
358 } else {
359 self.cache_misses.fetch_add(1, Ordering::Relaxed);
360 }
361 }
362
363 fn record_bandwidth(&self, bytes: usize) {
364 self.bandwidth_bytes
365 .fetch_add(bytes as u64, Ordering::Relaxed);
366 }
367
368 fn get_elapsed(&self) -> Duration {
369 self.start_time.elapsed()
370 }
371}
372
373struct ProfilingState {
375 current_depth: usize,
376 call_stack: Vec<String>,
377 gate_timings: HashMap<usize, GateTimingInfo>,
378 memory_snapshots: VecDeque<MemorySnapshot>,
379 anomaly_detector: AnomalyDetector,
380}
381
382#[derive(Debug, Clone)]
383struct GateTimingInfo {
384 gate_type: GateType,
385 start_time: Instant,
386 end_time: Option<Instant>,
387 memory_before: usize,
388 memory_after: Option<usize>,
389 simd_ops: u64,
390 parallel_ops: u64,
391}
392
393#[derive(Debug, Clone)]
394struct MemorySnapshot {
395 timestamp: Instant,
396 total_memory: usize,
397 heap_memory: usize,
398 stack_memory: usize,
399 buffer_pool_memory: usize,
400}
401
402struct AnomalyDetector {
404 baseline_metrics: HashMap<MetricType, (f64, f64)>, detection_threshold: f64,
406 history_window: usize,
407 metric_history: HashMap<MetricType, VecDeque<f64>>,
408}
409
410impl AnomalyDetector {
411 fn new(detection_threshold: f64, history_window: usize) -> Self {
412 Self {
413 baseline_metrics: HashMap::new(),
414 detection_threshold,
415 history_window,
416 metric_history: HashMap::new(),
417 }
418 }
419
420 fn update_metric(&mut self, metric: MetricType, value: f64) -> Option<AnomalyEvent> {
421 let history = self
422 .metric_history
423 .entry(metric)
424 .or_insert_with(VecDeque::new);
425 history.push_back(value);
426
427 if history.len() > self.history_window {
428 history.pop_front();
429 }
430
431 if history.len() >= 10 {
433 let mean: f64 = history.iter().sum::<f64>() / history.len() as f64;
434 let variance: f64 =
435 history.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / history.len() as f64;
436 let std_dev = variance.sqrt();
437
438 self.baseline_metrics.insert(metric, (mean, std_dev));
439
440 if let Some(&(baseline_mean, baseline_std)) = self.baseline_metrics.get(&metric) {
442 let z_score = (value - baseline_mean).abs() / baseline_std;
443
444 if z_score > self.detection_threshold {
445 let severity = match z_score {
446 z if z < 3.0 => AnomalySeverity::Low,
447 z if z < 4.0 => AnomalySeverity::Medium,
448 z if z < 5.0 => AnomalySeverity::High,
449 _ => AnomalySeverity::Critical,
450 };
451
452 return Some(AnomalyEvent {
453 timestamp: history.len() as f64,
454 metric,
455 severity,
456 description: format!("Anomaly detected: z-score = {:.2}", z_score),
457 impact: z_score / 10.0, });
459 }
460 }
461 }
462
463 None
464 }
465}
466
467impl EnhancedQuantumProfiler {
468 pub fn new() -> Self {
470 Self::with_config(EnhancedProfilingConfig::default())
471 }
472
473 pub fn with_config(config: EnhancedProfilingConfig) -> Self {
475 let platform_caps = PlatformCapabilities::detect();
476 let buffer_pool = Arc::new(BufferPool::new());
477 let metrics_collector = Arc::new(MetricsCollector::new());
478
479 let hardware_model = if config.hardware_aware_profiling {
480 Some(Self::build_hardware_model(&platform_caps))
481 } else {
482 None
483 };
484
485 let profiling_state = Arc::new(Mutex::new(ProfilingState {
486 current_depth: 0,
487 call_stack: Vec::new(),
488 gate_timings: HashMap::new(),
489 memory_snapshots: VecDeque::new(),
490 anomaly_detector: AnomalyDetector::new(3.0, 100),
491 }));
492
493 Self {
494 config,
495 platform_caps,
496 buffer_pool,
497 metrics_collector,
498 hardware_model,
499 profiling_state,
500 }
501 }
502
503 fn build_hardware_model(platform_caps: &PlatformCapabilities) -> HardwarePerformanceModel {
505 let characteristics = HardwareCharacteristics {
506 cpu_frequency: 3.0e9, cache_sizes: vec![32 * 1024, 256 * 1024, 8 * 1024 * 1024], memory_bandwidth: 50.0e9, simd_width: if platform_caps.simd_available() {
510 256
511 } else {
512 128
513 },
514 num_cores: platform_caps.cpu.logical_cores,
515 gpu_available: platform_caps.gpu_available(),
516 gpu_memory: if platform_caps.gpu_available() {
517 Some(8 * 1024 * 1024 * 1024)
518 } else {
519 None
520 },
521 quantum_accelerator: None,
522 };
523
524 let mut scaling_models = HashMap::new();
525 scaling_models.insert(
526 "gate_execution".to_string(),
527 ScalingModel {
528 model_type: ScalingType::Linear,
529 parameters: vec![("slope".to_string(), 1e-6), ("intercept".to_string(), 1e-7)]
530 .into_iter()
531 .collect(),
532 confidence: 0.95,
533 },
534 );
535
536 scaling_models.insert(
537 "memory_access".to_string(),
538 ScalingModel {
539 model_type: ScalingType::Logarithmic,
540 parameters: vec![("base".to_string(), 2.0), ("coefficient".to_string(), 1e-8)]
541 .into_iter()
542 .collect(),
543 confidence: 0.90,
544 },
545 );
546
547 let optimization_strategies = vec![
548 HardwareOptimizationStrategy {
549 strategy_name: "SIMD Vectorization".to_string(),
550 applicable_conditions: vec!["vector_friendly_gates".to_string()],
551 expected_speedup: 4.0,
552 implementation_cost: 0.2,
553 },
554 HardwareOptimizationStrategy {
555 strategy_name: "Parallel Execution".to_string(),
556 applicable_conditions: vec!["independent_gates".to_string()],
557 expected_speedup: characteristics.num_cores as f64 * 0.8,
558 implementation_cost: 0.3,
559 },
560 HardwareOptimizationStrategy {
561 strategy_name: "Cache Optimization".to_string(),
562 applicable_conditions: vec!["repeated_access_patterns".to_string()],
563 expected_speedup: 2.0,
564 implementation_cost: 0.1,
565 },
566 ];
567
568 HardwarePerformanceModel {
569 platform: PlatformCapabilities::detect(),
570 characteristics,
571 scaling_models,
572 optimization_strategies,
573 }
574 }
575
576 pub fn profile_circuit(
578 &self,
579 circuit: &[QuantumGate],
580 num_qubits: usize,
581 ) -> Result<EnhancedProfilingReport, QuantRS2Error> {
582 let start_time = Instant::now();
583
584 self.initialize_profiling(num_qubits)?;
586
587 let mut gate_results = Vec::new();
589 for (idx, gate) in circuit.iter().enumerate() {
590 let gate_result = self.profile_gate(gate, idx, num_qubits)?;
591 gate_results.push(gate_result);
592 }
593
594 let performance_metrics = self.collect_performance_metrics()?;
596
597 let bottleneck_analysis = if self.config.enable_deep_analysis {
599 Some(self.analyze_bottlenecks(&gate_results, num_qubits)?)
600 } else {
601 None
602 };
603
604 let optimizations = if self.config.generate_optimizations {
606 self.generate_optimization_recommendations(&gate_results, &bottleneck_analysis)?
607 } else {
608 Vec::new()
609 };
610
611 let performance_predictions = if self.config.enable_performance_prediction {
613 Some(self.predict_performance(&gate_results, num_qubits)?)
614 } else {
615 None
616 };
617
618 let total_time = start_time.elapsed();
620
621 let export_data = self.prepare_export_data(&gate_results)?;
623
624 Ok(EnhancedProfilingReport {
625 summary: ProfilingSummary {
626 total_execution_time: total_time,
627 num_gates: circuit.len(),
628 num_qubits,
629 platform_info: PlatformCapabilities::detect(),
630 profiling_config: self.config.clone(),
631 },
632 gate_results,
633 performance_metrics,
634 bottleneck_analysis,
635 optimizations,
636 performance_predictions,
637 export_data,
638 })
639 }
640
641 fn initialize_profiling(&self, num_qubits: usize) -> Result<(), QuantRS2Error> {
643 let mut state = self.profiling_state.lock().unwrap();
644 state.current_depth = 0;
645 state.call_stack.clear();
646 state.gate_timings.clear();
647 state.memory_snapshots.clear();
648
649 let initial_snapshot = MemorySnapshot {
651 timestamp: Instant::now(),
652 total_memory: self.estimate_memory_usage(num_qubits),
653 heap_memory: 0,
654 stack_memory: 0,
655 buffer_pool_memory: 0,
656 };
657 state.memory_snapshots.push_back(initial_snapshot);
658
659 Ok(())
660 }
661
662 fn profile_gate(
664 &self,
665 gate: &QuantumGate,
666 gate_index: usize,
667 num_qubits: usize,
668 ) -> Result<EnhancedGateProfilingResult, QuantRS2Error> {
669 let start_time = Instant::now();
670 let memory_before = self.estimate_memory_usage(num_qubits);
671
672 {
674 let mut state = self.profiling_state.lock().unwrap();
675 state.gate_timings.insert(
676 gate_index,
677 GateTimingInfo {
678 gate_type: gate.gate_type().clone(),
679 start_time,
680 end_time: None,
681 memory_before,
682 memory_after: None,
683 simd_ops: 0,
684 parallel_ops: 0,
685 },
686 );
687 }
688
689 self.simulate_gate_execution(gate, num_qubits)?;
691
692 let end_time = Instant::now();
693 let memory_after = self.estimate_memory_usage(num_qubits);
694 let execution_time = end_time - start_time;
695
696 {
698 let mut state = self.profiling_state.lock().unwrap();
699 if let Some(timing_info) = state.gate_timings.get_mut(&gate_index) {
700 timing_info.end_time = Some(end_time);
701 timing_info.memory_after = Some(memory_after);
702 timing_info.simd_ops = self
703 .metrics_collector
704 .simd_ops_count
705 .load(Ordering::Relaxed);
706 timing_info.parallel_ops = self
707 .metrics_collector
708 .parallel_ops_count
709 .load(Ordering::Relaxed);
710 }
711 }
712
713 self.metrics_collector
715 .record_execution(&format!("{:?}", gate.gate_type()), execution_time);
716 self.metrics_collector
717 .record_memory(memory_after.saturating_sub(memory_before));
718
719 let mut anomalies = Vec::new();
721 {
722 let mut state = self.profiling_state.lock().unwrap();
723 if let Some(anomaly) = state
724 .anomaly_detector
725 .update_metric(MetricType::ExecutionTime, execution_time.as_secs_f64())
726 {
727 anomalies.push(anomaly);
728 }
729 }
730
731 Ok(EnhancedGateProfilingResult {
732 gate_index,
733 gate_type: gate.gate_type().clone(),
734 execution_time,
735 memory_delta: memory_after as i64 - memory_before as i64,
736 simd_operations: self
737 .metrics_collector
738 .simd_ops_count
739 .load(Ordering::Relaxed),
740 parallel_operations: self
741 .metrics_collector
742 .parallel_ops_count
743 .load(Ordering::Relaxed),
744 cache_efficiency: self.calculate_cache_efficiency(),
745 bandwidth_usage: self.calculate_bandwidth_usage(execution_time),
746 anomalies,
747 detailed_metrics: self.collect_detailed_gate_metrics(gate, execution_time)?,
748 })
749 }
750
751 fn simulate_gate_execution(
753 &self,
754 gate: &QuantumGate,
755 num_qubits: usize,
756 ) -> Result<(), QuantRS2Error> {
757 match gate.gate_type() {
759 GateType::H | GateType::X | GateType::Y | GateType::Z => {
760 if self.platform_caps.simd_available() {
762 self.metrics_collector.record_simd_op();
763 }
764 self.metrics_collector
765 .record_bandwidth(16 * (1 << num_qubits)); }
767 GateType::CNOT | GateType::CZ => {
768 if num_qubits > 10 {
770 self.metrics_collector.record_parallel_op();
771 }
772 self.metrics_collector
773 .record_bandwidth(32 * (1 << num_qubits));
774 }
775 _ => {
776 self.metrics_collector.record_parallel_op();
778 self.metrics_collector
779 .record_bandwidth(64 * (1 << num_qubits));
780 }
781 }
782
783 use scirs2_core::random::prelude::*;
785 let cache_hit = thread_rng().gen::<f64>() > 0.2; self.metrics_collector.record_cache_access(cache_hit);
787
788 Ok(())
789 }
790
791 fn estimate_memory_usage(&self, num_qubits: usize) -> usize {
793 let state_vector_size = (1 << num_qubits) * std::mem::size_of::<Complex64>();
794 let overhead = state_vector_size / 10; state_vector_size + overhead
796 }
797
798 fn calculate_cache_efficiency(&self) -> f64 {
800 let hits = self.metrics_collector.cache_hits.load(Ordering::Relaxed) as f64;
801 let misses = self.metrics_collector.cache_misses.load(Ordering::Relaxed) as f64;
802 let total = hits + misses;
803
804 if total > 0.0 {
805 hits / total
806 } else {
807 1.0 }
809 }
810
811 fn calculate_bandwidth_usage(&self, duration: Duration) -> f64 {
813 let bytes = self
814 .metrics_collector
815 .bandwidth_bytes
816 .load(Ordering::Relaxed) as f64;
817 let seconds = duration.as_secs_f64();
818
819 if seconds > 0.0 {
820 bytes / seconds
821 } else {
822 0.0
823 }
824 }
825
826 fn collect_detailed_gate_metrics(
828 &self,
829 gate: &QuantumGate,
830 execution_time: Duration,
831 ) -> Result<HashMap<String, f64>, QuantRS2Error> {
832 let mut metrics = HashMap::new();
833
834 metrics.insert(
835 "execution_time_us".to_string(),
836 execution_time.as_micros() as f64,
837 );
838 metrics.insert(
839 "cache_efficiency".to_string(),
840 self.calculate_cache_efficiency(),
841 );
842 metrics.insert(
843 "bandwidth_mbps".to_string(),
844 self.calculate_bandwidth_usage(execution_time) / 1e6,
845 );
846
847 if let Some(ref hw_model) = self.hardware_model {
848 metrics.insert(
849 "theoretical_flops".to_string(),
850 self.estimate_flops(gate, &hw_model.characteristics),
851 );
852 }
853
854 Ok(metrics)
855 }
856
857 fn estimate_flops(&self, gate: &QuantumGate, hw_chars: &HardwareCharacteristics) -> f64 {
859 let base_flops = match gate.gate_type() {
860 GateType::H => 8.0, GateType::X | GateType::Y | GateType::Z => 4.0,
862 GateType::CNOT | GateType::CZ => 16.0,
863 _ => 32.0, };
865
866 base_flops * hw_chars.cpu_frequency
867 }
868
869 fn collect_performance_metrics(&self) -> Result<PerformanceMetrics, QuantRS2Error> {
871 let mut values = HashMap::new();
872 let elapsed = self.metrics_collector.get_elapsed();
873
874 values.insert(MetricType::ExecutionTime, elapsed.as_secs_f64());
875 values.insert(
876 MetricType::MemoryUsage,
877 self.metrics_collector.memory_usage.load(Ordering::Relaxed) as f64,
878 );
879 values.insert(
880 MetricType::SimdUtilization,
881 self.metrics_collector
882 .simd_ops_count
883 .load(Ordering::Relaxed) as f64,
884 );
885 values.insert(
886 MetricType::ParallelEfficiency,
887 self.metrics_collector
888 .parallel_ops_count
889 .load(Ordering::Relaxed) as f64,
890 );
891 values.insert(MetricType::CacheHitRate, self.calculate_cache_efficiency());
892 values.insert(
893 MetricType::MemoryBandwidth,
894 self.calculate_bandwidth_usage(elapsed),
895 );
896
897 let statistics = self.calculate_metric_statistics(&values)?;
899
900 let mut time_series = HashMap::new();
902 for (metric, value) in &values {
903 time_series.insert(*metric, vec![(0.0, 0.0), (elapsed.as_secs_f64(), *value)]);
904 }
905
906 Ok(PerformanceMetrics {
907 values,
908 time_series,
909 statistics,
910 correlations: HashMap::new(), anomalies: Vec::new(), })
913 }
914
915 fn calculate_metric_statistics(
917 &self,
918 values: &HashMap<MetricType, f64>,
919 ) -> Result<MetricStatistics, QuantRS2Error> {
920 let mut mean = HashMap::new();
921 let mut std_dev = HashMap::new();
922 let mut min = HashMap::new();
923 let mut max = HashMap::new();
924 let mut percentiles = HashMap::new();
925
926 for (metric, value) in values {
927 mean.insert(*metric, *value);
928 std_dev.insert(*metric, 0.0); min.insert(*metric, *value);
930 max.insert(*metric, *value);
931
932 let mut percs = BTreeMap::new();
933 percs.insert(50, *value); percs.insert(95, *value * 1.1); percs.insert(99, *value * 1.2); percentiles.insert(*metric, percs);
937 }
938
939 Ok(MetricStatistics {
940 mean,
941 std_dev,
942 min,
943 max,
944 percentiles,
945 })
946 }
947
948 fn analyze_bottlenecks(
950 &self,
951 gate_results: &[EnhancedGateProfilingResult],
952 num_qubits: usize,
953 ) -> Result<BottleneckAnalysis, QuantRS2Error> {
954 let mut bottlenecks = Vec::new();
955 let mut impact_analysis = HashMap::new();
956 let mut opportunities = Vec::new();
957
958 let total_time: Duration = gate_results.iter().map(|r| r.execution_time).sum();
960 let avg_time = total_time / gate_results.len() as u32;
961
962 for (idx, result) in gate_results.iter().enumerate() {
963 if result.execution_time > avg_time * 2 {
964 let impact = result.execution_time.as_secs_f64() / total_time.as_secs_f64();
965
966 bottlenecks.push(Bottleneck {
967 location: CircuitLocation {
968 gate_index: idx,
969 layer: idx / num_qubits, qubits: vec![idx % num_qubits], context: format!("Gate {:?} at index {}", result.gate_type, idx),
972 },
973 bottleneck_type: BottleneckType::ComputeIntensive,
974 severity: impact * 100.0,
975 impact_percentage: impact * 100.0,
976 suggested_fixes: vec![
977 "Consider gate decomposition".to_string(),
978 "Explore parallel execution".to_string(),
979 ],
980 });
981
982 impact_analysis.insert(format!("gate_{}", idx), impact);
983 }
984
985 if result.cache_efficiency < 0.5 {
987 bottlenecks.push(Bottleneck {
988 location: CircuitLocation {
989 gate_index: idx,
990 layer: idx / num_qubits,
991 qubits: vec![idx % num_qubits],
992 context: format!("Poor cache efficiency at gate {}", idx),
993 },
994 bottleneck_type: BottleneckType::CacheMiss,
995 severity: (1.0 - result.cache_efficiency) * 50.0,
996 impact_percentage: 10.0, suggested_fixes: vec![
998 "Reorder operations for better locality".to_string(),
999 "Consider data prefetching".to_string(),
1000 ],
1001 });
1002 }
1003 }
1004
1005 if self.platform_caps.simd_available() {
1007 let simd_utilization = gate_results
1008 .iter()
1009 .filter(|r| r.simd_operations > 0)
1010 .count() as f64
1011 / gate_results.len() as f64;
1012
1013 if simd_utilization < 0.5 {
1014 opportunities.push(OptimizationOpportunity {
1015 opportunity_type: OpportunityType::SimdOptimization,
1016 estimated_improvement: (1.0 - simd_utilization) * 2.0,
1017 difficulty: Difficulty::Medium,
1018 implementation: "Vectorize gate operations using AVX2".to_string(),
1019 trade_offs: vec!["Increased code complexity".to_string()],
1020 });
1021 }
1022 }
1023
1024 let resource_heatmap = Array2::zeros((gate_results.len(), 4)); Ok(BottleneckAnalysis {
1028 bottlenecks,
1029 impact_analysis,
1030 opportunities,
1031 resource_heatmap,
1032 })
1033 }
1034
1035 fn generate_optimization_recommendations(
1037 &self,
1038 gate_results: &[EnhancedGateProfilingResult],
1039 bottleneck_analysis: &Option<BottleneckAnalysis>,
1040 ) -> Result<Vec<EnhancedOptimizationRecommendation>, QuantRS2Error> {
1041 let mut recommendations = Vec::new();
1042
1043 for window in gate_results.windows(2) {
1045 if Self::can_fuse_gates(&window[0].gate_type, &window[1].gate_type) {
1046 recommendations.push(EnhancedOptimizationRecommendation {
1047 recommendation_type: RecommendationType::GateFusion,
1048 priority: Priority::High,
1049 estimated_speedup: 1.5,
1050 implementation_difficulty: Difficulty::Easy,
1051 description: format!(
1052 "Fuse {:?} and {:?} gates",
1053 window[0].gate_type, window[1].gate_type
1054 ),
1055 code_example: Some(
1056 self.generate_fusion_code(&window[0].gate_type, &window[1].gate_type),
1057 ),
1058 prerequisites: vec!["Adjacent gates must commute".to_string()],
1059 risks: vec!["May increase numerical error".to_string()],
1060 });
1061 }
1062 }
1063
1064 if let Some(ref hw_model) = self.hardware_model {
1066 for strategy in &hw_model.optimization_strategies {
1067 if strategy.expected_speedup > 1.5 {
1068 recommendations.push(EnhancedOptimizationRecommendation {
1069 recommendation_type: RecommendationType::HardwareSpecific,
1070 priority: Priority::Medium,
1071 estimated_speedup: strategy.expected_speedup,
1072 implementation_difficulty: Difficulty::Hard,
1073 description: strategy.strategy_name.clone(),
1074 code_example: None,
1075 prerequisites: strategy.applicable_conditions.clone(),
1076 risks: vec!["Platform-specific code".to_string()],
1077 });
1078 }
1079 }
1080 }
1081
1082 if let Some(bottleneck_analysis) = bottleneck_analysis {
1084 for opportunity in &bottleneck_analysis.opportunities {
1085 recommendations.push(EnhancedOptimizationRecommendation {
1086 recommendation_type: match opportunity.opportunity_type {
1087 OpportunityType::GateFusion => RecommendationType::GateFusion,
1088 OpportunityType::Parallelization => RecommendationType::Parallelization,
1089 OpportunityType::SimdOptimization => RecommendationType::SimdVectorization,
1090 OpportunityType::MemoryReordering => RecommendationType::MemoryOptimization,
1091 OpportunityType::CacheOptimization => RecommendationType::CacheOptimization,
1092 OpportunityType::AlgorithmicImprovement => {
1093 RecommendationType::AlgorithmicChange
1094 }
1095 OpportunityType::HardwareSpecific => RecommendationType::HardwareSpecific,
1096 },
1097 priority: match opportunity.difficulty {
1098 Difficulty::Trivial | Difficulty::Easy => Priority::High,
1099 Difficulty::Medium => Priority::Medium,
1100 Difficulty::Hard | Difficulty::Expert => Priority::Low,
1101 },
1102 estimated_speedup: opportunity.estimated_improvement,
1103 implementation_difficulty: opportunity.difficulty,
1104 description: opportunity.implementation.clone(),
1105 code_example: None,
1106 prerequisites: Vec::new(),
1107 risks: opportunity.trade_offs.clone(),
1108 });
1109 }
1110 }
1111
1112 Ok(recommendations)
1113 }
1114
1115 fn can_fuse_gates(gate1: &GateType, gate2: &GateType) -> bool {
1117 use GateType::*;
1118 matches!(
1119 (gate1, gate2),
1120 (H, H) | (X, X) | (Y, Y) | (Z, Z) | (Rz(_), Rz(_)) | (Rx(_), Rx(_)) | (Ry(_), Ry(_)) )
1123 }
1124
1125 fn generate_fusion_code(&self, gate1: &GateType, gate2: &GateType) -> String {
1127 format!(
1128 "// Fused {:?} and {:?}\nlet fused_gate = FusedGate::new({:?}, {:?});\nfused_gate.apply(state);",
1129 gate1, gate2, gate1, gate2
1130 )
1131 }
1132
1133 fn predict_performance(
1135 &self,
1136 gate_results: &[EnhancedGateProfilingResult],
1137 num_qubits: usize,
1138 ) -> Result<PerformancePredictions, QuantRS2Error> {
1139 let mut predictions = HashMap::new();
1140
1141 let current_time: Duration = gate_results.iter().map(|r| r.execution_time).sum();
1143
1144 predictions.insert(
1145 "current".to_string(),
1146 PredictedPerformance {
1147 hardware_description: "Current Platform".to_string(),
1148 estimated_time: current_time,
1149 confidence: 1.0,
1150 limiting_factors: vec!["Actual measurement".to_string()],
1151 },
1152 );
1153
1154 if self.platform_caps.gpu_available() {
1156 let gpu_speedup = (num_qubits as f64).ln() * 2.0; predictions.insert(
1158 "gpu".to_string(),
1159 PredictedPerformance {
1160 hardware_description: "GPU Acceleration".to_string(),
1161 estimated_time: current_time / gpu_speedup as u32,
1162 confidence: 0.8,
1163 limiting_factors: vec!["Memory transfer overhead".to_string()],
1164 },
1165 );
1166 }
1167
1168 predictions.insert(
1170 "quantum_hw".to_string(),
1171 PredictedPerformance {
1172 hardware_description: "Quantum Hardware (NISQ)".to_string(),
1173 estimated_time: Duration::from_millis(gate_results.len() as u64 * 10), confidence: 0.5,
1175 limiting_factors: vec![
1176 "Gate fidelity".to_string(),
1177 "Connectivity constraints".to_string(),
1178 "Decoherence".to_string(),
1179 ],
1180 },
1181 );
1182
1183 predictions.insert(
1185 "cloud_qpu".to_string(),
1186 PredictedPerformance {
1187 hardware_description: "Cloud Quantum Processor".to_string(),
1188 estimated_time: Duration::from_secs(1)
1189 + Duration::from_millis(gate_results.len() as u64),
1190 confidence: 0.6,
1191 limiting_factors: vec!["Network latency".to_string(), "Queue time".to_string()],
1192 },
1193 );
1194
1195 let hardware_recommendations =
1197 self.generate_hardware_recommendations(num_qubits, &predictions);
1198
1199 Ok(PerformancePredictions {
1200 predictions,
1201 scaling_analysis: self.analyze_scaling(num_qubits)?,
1202 hardware_recommendations,
1203 })
1204 }
1205
1206 fn analyze_scaling(&self, num_qubits: usize) -> Result<ScalingAnalysis, QuantRS2Error> {
1208 Ok(ScalingAnalysis {
1209 qubit_scaling: ScalingType::Exponential,
1210 gate_scaling: ScalingType::Linear,
1211 memory_scaling: ScalingType::Exponential,
1212 predicted_limits: HashMap::from([
1213 ("max_qubits_cpu".to_string(), 30.0),
1214 ("max_qubits_gpu".to_string(), 35.0),
1215 ("max_gates_per_second".to_string(), 1e6),
1216 ]),
1217 })
1218 }
1219
1220 fn generate_hardware_recommendations(
1222 &self,
1223 num_qubits: usize,
1224 predictions: &HashMap<String, PredictedPerformance>,
1225 ) -> Vec<String> {
1226 let mut recommendations = Vec::new();
1227
1228 if num_qubits > 20 {
1229 recommendations.push("Consider GPU acceleration for large circuits".to_string());
1230 }
1231
1232 if num_qubits > 30 {
1233 recommendations.push("Tensor network methods recommended".to_string());
1234 }
1235
1236 if let Some(gpu_pred) = predictions.get("gpu") {
1237 if gpu_pred.confidence > 0.7 {
1238 recommendations.push("GPU acceleration shows promising speedup".to_string());
1239 }
1240 }
1241
1242 recommendations
1243 }
1244
1245 fn prepare_export_data(
1247 &self,
1248 gate_results: &[EnhancedGateProfilingResult],
1249 ) -> Result<HashMap<ExportFormat, Vec<u8>>, QuantRS2Error> {
1250 let mut export_data = HashMap::new();
1251
1252 for format in &self.config.export_formats {
1253 let data = match format {
1254 ExportFormat::JSON => self.export_to_json(gate_results)?,
1255 ExportFormat::CSV => self.export_to_csv(gate_results)?,
1256 ExportFormat::HTML => self.export_to_html(gate_results)?,
1257 _ => Vec::new(), };
1259 export_data.insert(*format, data);
1260 }
1261
1262 Ok(export_data)
1263 }
1264
1265 fn export_to_json(
1267 &self,
1268 gate_results: &[EnhancedGateProfilingResult],
1269 ) -> Result<Vec<u8>, QuantRS2Error> {
1270 let json = serde_json::to_vec_pretty(gate_results).map_err(|e| {
1271 QuantRS2Error::ComputationError(format!("CSV generation failed: {}", e))
1272 })?;
1273 Ok(json)
1274 }
1275
1276 fn export_to_csv(
1278 &self,
1279 gate_results: &[EnhancedGateProfilingResult],
1280 ) -> Result<Vec<u8>, QuantRS2Error> {
1281 let mut csv = Vec::new();
1282 writeln!(csv, "gate_index,gate_type,execution_time_us,memory_delta,simd_ops,parallel_ops,cache_efficiency")
1283 .map_err(|e| QuantRS2Error::ComputationError(format!("IO error: {}", e)))?;
1284
1285 for result in gate_results {
1286 writeln!(
1287 csv,
1288 "{},{:?},{},{},{},{},{:.2}",
1289 result.gate_index,
1290 result.gate_type,
1291 result.execution_time.as_micros(),
1292 result.memory_delta,
1293 result.simd_operations,
1294 result.parallel_operations,
1295 result.cache_efficiency
1296 )
1297 .map_err(|e| QuantRS2Error::ComputationError(format!("IO error: {}", e)))?;
1298 }
1299
1300 Ok(csv)
1301 }
1302
1303 fn export_to_html(
1305 &self,
1306 gate_results: &[EnhancedGateProfilingResult],
1307 ) -> Result<Vec<u8>, QuantRS2Error> {
1308 let mut html = Vec::new();
1309 writeln!(
1310 html,
1311 "<html><head><title>Quantum Circuit Profiling Report</title>"
1312 )
1313 .map_err(|e| QuantRS2Error::ComputationError(format!("IO error: {}", e)))?;
1314 writeln!(html, "<style>table {{ border-collapse: collapse; }} th, td {{ border: 1px solid black; padding: 8px; }}</style>")
1315 .map_err(|e| QuantRS2Error::ComputationError(format!("IO error: {}", e)))?;
1316 writeln!(html, "</head><body><h1>Profiling Results</h1><table>")
1317 .map_err(|e| QuantRS2Error::ComputationError(format!("IO error: {}", e)))?;
1318 writeln!(html, "<tr><th>Gate</th><th>Type</th><th>Time (μs)</th><th>Memory</th><th>SIMD</th><th>Parallel</th><th>Cache</th></tr>")
1319 .map_err(|e| QuantRS2Error::ComputationError(format!("IO error: {}", e)))?;
1320
1321 for result in gate_results {
1322 writeln!(html, "<tr><td>{}</td><td>{:?}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{:.1}%</td></tr>",
1323 result.gate_index,
1324 result.gate_type,
1325 result.execution_time.as_micros(),
1326 result.memory_delta,
1327 result.simd_operations,
1328 result.parallel_operations,
1329 result.cache_efficiency * 100.0
1330 ).map_err(|e| QuantRS2Error::ComputationError(format!("IO error: {}", e)))?;
1331 }
1332
1333 writeln!(html, "</table></body></html>")
1334 .map_err(|e| QuantRS2Error::ComputationError(format!("IO error: {}", e)))?;
1335
1336 Ok(html)
1337 }
1338}
1339
1340#[derive(Debug, Clone, Serialize, Deserialize)]
1342pub struct EnhancedGateProfilingResult {
1343 pub gate_index: usize,
1344 pub gate_type: GateType,
1345 pub execution_time: Duration,
1346 pub memory_delta: i64,
1347 pub simd_operations: u64,
1348 pub parallel_operations: u64,
1349 pub cache_efficiency: f64,
1350 pub bandwidth_usage: f64,
1351 pub anomalies: Vec<AnomalyEvent>,
1352 pub detailed_metrics: HashMap<String, f64>,
1353}
1354
1355#[derive(Debug, Clone, Serialize, Deserialize)]
1357pub struct EnhancedOptimizationRecommendation {
1358 pub recommendation_type: RecommendationType,
1359 pub priority: Priority,
1360 pub estimated_speedup: f64,
1361 pub implementation_difficulty: Difficulty,
1362 pub description: String,
1363 pub code_example: Option<String>,
1364 pub prerequisites: Vec<String>,
1365 pub risks: Vec<String>,
1366}
1367
1368#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1369pub enum RecommendationType {
1370 GateFusion,
1371 Parallelization,
1372 SimdVectorization,
1373 MemoryOptimization,
1374 CacheOptimization,
1375 AlgorithmicChange,
1376 HardwareSpecific,
1377}
1378
1379#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1380pub enum Priority {
1381 Low,
1382 Medium,
1383 High,
1384 Critical,
1385}
1386
1387#[derive(Debug, Clone, Serialize, Deserialize)]
1389pub struct PerformancePredictions {
1390 pub predictions: HashMap<String, PredictedPerformance>,
1391 pub scaling_analysis: ScalingAnalysis,
1392 pub hardware_recommendations: Vec<String>,
1393}
1394
1395#[derive(Debug, Clone, Serialize, Deserialize)]
1396pub struct PredictedPerformance {
1397 pub hardware_description: String,
1398 pub estimated_time: Duration,
1399 pub confidence: f64,
1400 pub limiting_factors: Vec<String>,
1401}
1402
1403#[derive(Debug, Clone, Serialize, Deserialize)]
1404pub struct ScalingAnalysis {
1405 pub qubit_scaling: ScalingType,
1406 pub gate_scaling: ScalingType,
1407 pub memory_scaling: ScalingType,
1408 pub predicted_limits: HashMap<String, f64>,
1409}
1410
1411#[derive(Serialize, Deserialize)]
1413pub struct EnhancedProfilingReport {
1414 pub summary: ProfilingSummary,
1415 pub gate_results: Vec<EnhancedGateProfilingResult>,
1416 pub performance_metrics: PerformanceMetrics,
1417 pub bottleneck_analysis: Option<BottleneckAnalysis>,
1418 pub optimizations: Vec<EnhancedOptimizationRecommendation>,
1419 pub performance_predictions: Option<PerformancePredictions>,
1420 pub export_data: HashMap<ExportFormat, Vec<u8>>,
1421}
1422
1423#[derive(Serialize, Deserialize)]
1424pub struct ProfilingSummary {
1425 pub total_execution_time: Duration,
1426 pub num_gates: usize,
1427 pub num_qubits: usize,
1428 #[serde(skip, default = "PlatformCapabilities::detect")]
1429 pub platform_info: PlatformCapabilities,
1430 pub profiling_config: EnhancedProfilingConfig,
1431}
1432
1433#[cfg(test)]
1434mod tests {
1435 use super::*;
1436
1437 #[test]
1438 fn test_enhanced_profiler_creation() {
1439 let profiler = EnhancedQuantumProfiler::new();
1440 assert!(profiler.platform_caps.simd_available());
1441 }
1442
1443 #[test]
1444 fn test_basic_profiling() {
1445 let profiler = EnhancedQuantumProfiler::new();
1446 let gates = vec![
1447 QuantumGate::new(GateType::H, vec![0], None),
1448 QuantumGate::new(GateType::CNOT, vec![0, 1], None),
1449 QuantumGate::new(GateType::H, vec![1], None),
1450 ];
1451
1452 let result = profiler.profile_circuit(&gates, 2).unwrap();
1453 assert_eq!(result.gate_results.len(), 3);
1454 assert!(result.summary.total_execution_time.as_nanos() > 0);
1455 }
1456
1457 #[test]
1458 fn test_bottleneck_detection() {
1459 let config = EnhancedProfilingConfig {
1460 enable_deep_analysis: true,
1461 ..Default::default()
1462 };
1463 let profiler = EnhancedQuantumProfiler::with_config(config);
1464
1465 let gates = vec![
1466 QuantumGate::new(GateType::H, vec![0], None),
1467 QuantumGate::new(GateType::T, vec![0], None),
1468 QuantumGate::new(GateType::H, vec![0], None),
1469 ];
1470
1471 let result = profiler.profile_circuit(&gates, 1).unwrap();
1472 assert!(result.bottleneck_analysis.is_some());
1473 }
1474
1475 #[test]
1476 fn test_optimization_recommendations() {
1477 let config = EnhancedProfilingConfig {
1478 generate_optimizations: true,
1479 ..Default::default()
1480 };
1481 let profiler = EnhancedQuantumProfiler::with_config(config);
1482
1483 let gates = vec![
1484 QuantumGate::new(GateType::H, vec![0], None),
1485 QuantumGate::new(GateType::H, vec![0], None), ];
1487
1488 let result = profiler.profile_circuit(&gates, 1).unwrap();
1489 assert!(!result.optimizations.is_empty());
1490 assert!(result
1491 .optimizations
1492 .iter()
1493 .any(|opt| opt.recommendation_type == RecommendationType::GateFusion));
1494 }
1495
1496 #[test]
1497 fn test_performance_prediction() {
1498 let config = EnhancedProfilingConfig {
1499 enable_performance_prediction: true,
1500 ..Default::default()
1501 };
1502 let profiler = EnhancedQuantumProfiler::with_config(config);
1503
1504 let gates = vec![
1505 QuantumGate::new(GateType::X, vec![0], None),
1506 QuantumGate::new(GateType::Y, vec![1], None),
1507 QuantumGate::new(GateType::Z, vec![2], None),
1508 ];
1509
1510 let result = profiler.profile_circuit(&gates, 3).unwrap();
1511 assert!(result.performance_predictions.is_some());
1512
1513 let predictions = result.performance_predictions.unwrap();
1514 assert!(predictions.predictions.contains_key("current"));
1515 assert!(predictions.predictions.contains_key("quantum_hw"));
1516 }
1517
1518 #[test]
1519 fn test_export_formats() {
1520 let config = EnhancedProfilingConfig {
1521 export_formats: vec![ExportFormat::JSON, ExportFormat::CSV, ExportFormat::HTML],
1522 ..Default::default()
1523 };
1524 let profiler = EnhancedQuantumProfiler::with_config(config);
1525
1526 let gates = vec![QuantumGate::new(GateType::H, vec![0], None)];
1527
1528 let result = profiler.profile_circuit(&gates, 1).unwrap();
1529 assert_eq!(result.export_data.len(), 3);
1530 assert!(result.export_data.contains_key(&ExportFormat::JSON));
1531 assert!(result.export_data.contains_key(&ExportFormat::CSV));
1532 assert!(result.export_data.contains_key(&ExportFormat::HTML));
1533 }
1534}