1use std::collections::{BTreeMap, HashMap, VecDeque};
8use std::sync::{Arc, Mutex, RwLock};
9use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
10
11use quantrs2_circuit::prelude::*;
12use quantrs2_core::{
13 error::{QuantRS2Error, QuantRS2Result},
14 gate::GateOp,
15 qubit::QubitId,
16};
17
18use serde::{Deserialize, Serialize};
19
20#[cfg(feature = "scirs2")]
22use scirs2_graph::{
23 betweenness_centrality, closeness_centrality, dijkstra_path, minimum_spanning_tree,
24 strongly_connected_components, Graph,
25};
26#[cfg(feature = "scirs2")]
27use scirs2_linalg::{det, eig, inv, matrix_norm, prelude::*, svd, LinalgError, LinalgResult};
28#[cfg(feature = "scirs2")]
29use scirs2_optimize::{minimize, OptimizeResult};
30use scirs2_stats::ttest::Alternative;
31#[cfg(feature = "scirs2")]
32use scirs2_stats::{corrcoef, distributions, mean, pearsonr, spearmanr, std, var};
33
34#[cfg(not(feature = "scirs2"))]
36mod fallback_scirs2;
37#[cfg(not(feature = "scirs2"))]
38use fallback_scirs2::*;
39
40use scirs2_core::ndarray::{Array1, Array2, ArrayView1, ArrayView2};
41use scirs2_core::random::prelude::*;
42
43use crate::{
44 adaptive_compilation::AdaptiveCompilationConfig,
45 backend_traits::{query_backend_capabilities, BackendCapabilities},
46 calibration::{CalibrationManager, DeviceCalibration},
47 integrated_device_manager::IntegratedQuantumDeviceManager,
48 noise_model::CalibrationNoiseModel,
49 topology::HardwareTopology,
50 CircuitResult, DeviceError, DeviceResult,
51};
52
53pub mod alerting;
55pub mod config;
56pub mod data_collection;
57pub mod ml_analytics;
58pub mod optimization;
59pub mod reporting;
60pub mod visualization;
61
62pub use alerting::*;
64pub use config::*;
65pub use data_collection::*;
66pub use ml_analytics::*;
67pub use optimization::*;
68pub use reporting::*;
69pub use visualization::*;
70
71#[cfg(not(feature = "scirs2"))]
72pub use fallback_scirs2::*;
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct ExecutionMetrics {
79 pub execution_time_ms: f64,
81 pub fidelity: f64,
83 pub success: bool,
85 pub gate_count: usize,
87 pub circuit_depth: usize,
89 pub two_qubit_gate_count: usize,
91 pub error_rate: f64,
93 pub timestamp_ms: u128,
95}
96
97impl ExecutionMetrics {
98 pub fn new(
100 execution_time_ms: f64,
101 fidelity: f64,
102 success: bool,
103 gate_count: usize,
104 circuit_depth: usize,
105 two_qubit_gate_count: usize,
106 error_rate: f64,
107 ) -> Self {
108 let timestamp_ms = SystemTime::now()
109 .duration_since(UNIX_EPOCH)
110 .unwrap_or_default()
111 .as_millis();
112 Self {
113 execution_time_ms,
114 fidelity,
115 success,
116 gate_count,
117 circuit_depth,
118 two_qubit_gate_count,
119 error_rate,
120 timestamp_ms,
121 }
122 }
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct DashboardSummary {
128 pub total_executions: usize,
130 pub successful_executions: usize,
132 pub success_rate: f64,
134 pub mean_fidelity: f64,
136 pub min_fidelity: f64,
138 pub max_fidelity: f64,
140 pub mean_execution_time_ms: f64,
142 pub p95_execution_time_ms: f64,
144 pub mean_error_rate: f64,
146 pub mean_gate_count: f64,
148 pub mean_circuit_depth: f64,
150 pub distinct_circuits: usize,
152}
153
154#[derive(Debug, Clone)]
156struct CircuitStats {
157 count: usize,
158 total_execution_time_ms: f64,
159 total_fidelity: f64,
160 total_error_rate: f64,
161 total_gate_count: usize,
162 total_circuit_depth: usize,
163 successes: usize,
164 sorted_times: Vec<f64>,
166}
167
168impl CircuitStats {
169 fn new() -> Self {
170 Self {
171 count: 0,
172 total_execution_time_ms: 0.0,
173 total_fidelity: 0.0,
174 total_error_rate: 0.0,
175 total_gate_count: 0,
176 total_circuit_depth: 0,
177 successes: 0,
178 sorted_times: Vec::new(),
179 }
180 }
181
182 fn record(&mut self, m: &ExecutionMetrics) {
183 self.count += 1;
184 self.total_execution_time_ms += m.execution_time_ms;
185 self.total_fidelity += m.fidelity;
186 self.total_error_rate += m.error_rate;
187 self.total_gate_count += m.gate_count;
188 self.total_circuit_depth += m.circuit_depth;
189 if m.success {
190 self.successes += 1;
191 }
192 let pos = self
194 .sorted_times
195 .partition_point(|&t| t <= m.execution_time_ms);
196 self.sorted_times.insert(pos, m.execution_time_ms);
197 }
198
199 fn percentile_time(&self, p: f64) -> f64 {
200 if self.sorted_times.is_empty() {
201 return 0.0;
202 }
203 let idx = ((p / 100.0) * (self.sorted_times.len() as f64 - 1.0)).round() as usize;
204 self.sorted_times
205 .get(idx.min(self.sorted_times.len() - 1))
206 .copied()
207 .unwrap_or(0.0)
208 }
209}
210
211pub struct PerformanceDashboard {
224 stats: HashMap<String, CircuitStats>,
226 history: VecDeque<(String, ExecutionMetrics)>,
228 config: DashboardConfig,
230 created_at: SystemTime,
232}
233
234impl PerformanceDashboard {
235 pub fn new(config: DashboardConfig) -> Self {
237 Self {
238 stats: HashMap::new(),
239 history: VecDeque::new(),
240 config,
241 created_at: SystemTime::now(),
242 }
243 }
244
245 pub fn record_execution(&mut self, circuit_id: &str, metrics: ExecutionMetrics) {
250 self.stats
252 .entry(circuit_id.to_string())
253 .or_insert_with(CircuitStats::new)
254 .record(&metrics);
255
256 self.history.push_front((circuit_id.to_string(), metrics));
258 let buffer = self.config.data_config.buffer_size;
259 while self.history.len() > buffer {
260 self.history.pop_back();
261 }
262 }
263
264 pub fn get_summary(&self) -> DashboardSummary {
266 if self.stats.is_empty() {
267 return DashboardSummary {
268 total_executions: 0,
269 successful_executions: 0,
270 success_rate: 0.0,
271 mean_fidelity: 0.0,
272 min_fidelity: 0.0,
273 max_fidelity: 0.0,
274 mean_execution_time_ms: 0.0,
275 p95_execution_time_ms: 0.0,
276 mean_error_rate: 0.0,
277 mean_gate_count: 0.0,
278 mean_circuit_depth: 0.0,
279 distinct_circuits: 0,
280 };
281 }
282
283 let total_executions: usize = self.stats.values().map(|s| s.count).sum();
284 let successful_executions: usize = self.stats.values().map(|s| s.successes).sum();
285
286 let sum_fidelity: f64 = self.stats.values().map(|s| s.total_fidelity).sum();
287 let sum_time: f64 = self.stats.values().map(|s| s.total_execution_time_ms).sum();
288 let sum_error: f64 = self.stats.values().map(|s| s.total_error_rate).sum();
289 let sum_gates: usize = self.stats.values().map(|s| s.total_gate_count).sum();
290 let sum_depth: usize = self.stats.values().map(|s| s.total_circuit_depth).sum();
291
292 let (min_fidelity, max_fidelity) = self
294 .history
295 .iter()
296 .fold((f64::MAX, f64::MIN), |(mn, mx), (_, m)| {
297 (mn.min(m.fidelity), mx.max(m.fidelity))
298 });
299 let min_fidelity = if min_fidelity == f64::MAX {
300 0.0
301 } else {
302 min_fidelity
303 };
304 let max_fidelity = if max_fidelity == f64::MIN {
305 0.0
306 } else {
307 max_fidelity
308 };
309
310 let mut all_times: Vec<f64> = self
312 .history
313 .iter()
314 .map(|(_, m)| m.execution_time_ms)
315 .collect();
316 all_times.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
317 let p95_idx = ((0.95 * (all_times.len() as f64 - 1.0)).round() as usize)
318 .min(all_times.len().saturating_sub(1));
319 let p95_execution_time_ms = all_times.get(p95_idx).copied().unwrap_or(0.0);
320
321 let n = total_executions as f64;
322 DashboardSummary {
323 total_executions,
324 successful_executions,
325 success_rate: if total_executions > 0 {
326 successful_executions as f64 / n
327 } else {
328 0.0
329 },
330 mean_fidelity: sum_fidelity / n,
331 min_fidelity,
332 max_fidelity,
333 mean_execution_time_ms: sum_time / n,
334 p95_execution_time_ms,
335 mean_error_rate: sum_error / n,
336 mean_gate_count: sum_gates as f64 / n,
337 mean_circuit_depth: sum_depth as f64 / n,
338 distinct_circuits: self.stats.len(),
339 }
340 }
341
342 pub fn export_report(&self) -> String {
344 let summary = self.get_summary();
345 let uptime = self.created_at.elapsed().unwrap_or_default().as_secs();
346
347 let mut report = String::new();
348 report.push_str("# QuantRS2 Device Performance Dashboard\n\n");
349 report.push_str(&format!("**Dashboard uptime:** {}s\n\n", uptime));
350
351 report.push_str("## Summary\n\n");
352 report.push_str("| Metric | Value |\n");
353 report.push_str("|--------|-------|\n");
354 report.push_str(&format!(
355 "| Total Executions | {} |\n",
356 summary.total_executions
357 ));
358 report.push_str(&format!(
359 "| Successful Executions | {} |\n",
360 summary.successful_executions
361 ));
362 report.push_str(&format!(
363 "| Success Rate | {:.2}% |\n",
364 summary.success_rate * 100.0
365 ));
366 report.push_str(&format!(
367 "| Mean Fidelity | {:.4} |\n",
368 summary.mean_fidelity
369 ));
370 report.push_str(&format!("| Min Fidelity | {:.4} |\n", summary.min_fidelity));
371 report.push_str(&format!("| Max Fidelity | {:.4} |\n", summary.max_fidelity));
372 report.push_str(&format!(
373 "| Mean Execution Time | {:.2} ms |\n",
374 summary.mean_execution_time_ms
375 ));
376 report.push_str(&format!(
377 "| P95 Execution Time | {:.2} ms |\n",
378 summary.p95_execution_time_ms
379 ));
380 report.push_str(&format!(
381 "| Mean Error Rate | {:.6} |\n",
382 summary.mean_error_rate
383 ));
384 report.push_str(&format!(
385 "| Mean Gate Count | {:.1} |\n",
386 summary.mean_gate_count
387 ));
388 report.push_str(&format!(
389 "| Mean Circuit Depth | {:.1} |\n",
390 summary.mean_circuit_depth
391 ));
392 report.push_str(&format!(
393 "| Distinct Circuits Tracked | {} |\n",
394 summary.distinct_circuits
395 ));
396
397 if !self.stats.is_empty() {
399 report.push_str("\n## Per-Circuit Breakdown\n\n");
400 report.push_str(
401 "| Circuit ID | Executions | Success Rate | Mean Fidelity | Mean Time (ms) |\n",
402 );
403 report.push_str(
404 "|-----------|-----------|-------------|--------------|---------------|\n",
405 );
406
407 let mut circuit_ids: Vec<&String> = self.stats.keys().collect();
408 circuit_ids.sort();
409 for id in circuit_ids {
410 let s = match self.stats.get(id) {
411 Some(s) => s,
412 None => continue,
413 };
414 let mean_f = if s.count > 0 {
415 s.total_fidelity / s.count as f64
416 } else {
417 0.0
418 };
419 let mean_t = if s.count > 0 {
420 s.total_execution_time_ms / s.count as f64
421 } else {
422 0.0
423 };
424 let sr = if s.count > 0 {
425 s.successes as f64 / s.count as f64
426 } else {
427 0.0
428 };
429 report.push_str(&format!(
430 "| {} | {} | {:.1}% | {:.4} | {:.2} |\n",
431 id,
432 s.count,
433 sr * 100.0,
434 mean_f,
435 mean_t
436 ));
437 }
438 }
439
440 report
441 }
442
443 pub fn execution_count(&self, circuit_id: &str) -> usize {
445 self.stats.get(circuit_id).map_or(0, |s| s.count)
446 }
447
448 pub fn reset(&mut self) {
450 self.stats.clear();
451 self.history.clear();
452 }
453}
454
455impl Default for DashboardConfig {
456 fn default() -> Self {
457 use crate::performance_dashboard::{
458 alerting::{AlertingConfig, AnomalyDetectionAlgorithm, AnomalyDetectionConfig},
459 data_collection::{
460 AggregationConfig, AggregationFunction, DataCollectionConfig, MetricsConfig,
461 PerformanceMetric, QualityMetric, ResourceMetric, SamplingConfig, SamplingStrategy,
462 TimeWindow,
463 },
464 ml_analytics::{
465 EvaluationConfig, EvaluationMetric, FeatureConfig, MLAnalyticsConfig,
466 ModelSelectionCriteria, TrainingConfig,
467 },
468 optimization::{DashboardOptimizationConfig, OptimizationObjective},
469 reporting::{
470 DistributionConfig, ReportFormat, ReportFrequency, ReportSchedule, ReportingConfig,
471 },
472 visualization::{
473 ColorScheme, GridLayout, InteractiveConfig, LayoutConfig, ThemeConfig,
474 VisualizationConfig,
475 },
476 };
477 use std::collections::HashMap;
478 use std::time::Duration;
479
480 DashboardConfig {
481 enable_realtime_monitoring: false,
482 data_config: DataCollectionConfig {
483 collection_interval: 60,
484 buffer_size: 1000,
485 retention_days: 30,
486 metrics_config: MetricsConfig {
487 performance_metrics: vec![
488 PerformanceMetric::Fidelity,
489 PerformanceMetric::Latency,
490 PerformanceMetric::ErrorRate,
491 ],
492 resource_metrics: vec![ResourceMetric::CpuUtilization],
493 quality_metrics: vec![QualityMetric::GateFidelity],
494 custom_metrics: vec![],
495 },
496 aggregation_config: AggregationConfig {
497 aggregation_functions: vec![
498 AggregationFunction::Mean,
499 AggregationFunction::Percentile(95.0),
500 ],
501 time_windows: vec![TimeWindow::Minutes(5), TimeWindow::Hours(1)],
502 grouping_dimensions: vec!["circuit_id".to_string()],
503 },
504 sampling_config: SamplingConfig {
505 sampling_strategy: SamplingStrategy::Fixed,
506 sample_rate: 1.0,
507 adaptive_sampling: false,
508 quality_based_sampling: false,
509 },
510 },
511 visualization_config: VisualizationConfig {
512 refresh_rate: 60,
513 chart_types: vec![],
514 layout_config: LayoutConfig {
515 grid_layout: GridLayout {
516 rows: 4,
517 columns: 3,
518 gap_size: 8,
519 },
520 responsive_design: true,
521 panel_configuration: vec![],
522 },
523 theme_config: ThemeConfig {
524 color_scheme: ColorScheme::Default,
525 dark_mode: false,
526 custom_styling: HashMap::new(),
527 },
528 interactive_config: InteractiveConfig {
529 enable_drill_down: true,
530 enable_filtering: true,
531 enable_zooming: true,
532 enable_real_time_updates: false,
533 },
534 },
535 alerting_config: AlertingConfig {
536 enable_alerting: false,
537 alert_thresholds: HashMap::new(),
538 notification_channels: vec![],
539 escalation_rules: vec![],
540 anomaly_detection: AnomalyDetectionConfig {
541 detection_algorithms: vec![AnomalyDetectionAlgorithm::StatisticalOutlier],
542 sensitivity: 0.95,
543 baseline_window: Duration::from_secs(3600),
544 detection_window: Duration::from_secs(300),
545 },
546 },
547 ml_config: MLAnalyticsConfig {
548 enable_ml_analytics: false,
549 prediction_models: vec![],
550 feature_config: FeatureConfig {
551 feature_selection_methods: vec![],
552 feature_engineering_rules: vec![],
553 dimensionality_reduction: None,
554 },
555 training_config: TrainingConfig {
556 training_data_size: 1000,
557 validation_split: 0.2,
558 cross_validation_folds: 5,
559 hyperparameter_tuning: false,
560 model_selection_criteria: ModelSelectionCriteria::CrossValidationScore,
561 },
562 evaluation_config: EvaluationConfig {
563 evaluation_metrics: vec![EvaluationMetric::RMSE],
564 test_data_size: 200,
565 evaluation_frequency: Duration::from_secs(3600),
566 performance_tracking: true,
567 },
568 },
569 optimization_config: DashboardOptimizationConfig {
570 enable_auto_recommendations: false,
571 optimization_objectives: vec![OptimizationObjective::BalancedPerformance],
572 confidence_threshold: 0.8,
573 priority_weighting: HashMap::new(),
574 },
575 reporting_config: ReportingConfig {
576 enable_automated_reports: false,
577 report_schedule: ReportSchedule {
578 frequency: ReportFrequency::Daily,
579 time_of_day: "00:00".to_string(),
580 time_zone: "UTC".to_string(),
581 custom_schedule: None,
582 },
583 report_formats: vec![ReportFormat::HTML],
584 distribution_config: DistributionConfig {
585 email_recipients: vec![],
586 file_storage_locations: vec![],
587 api_endpoints: vec![],
588 },
589 },
590 }
591 }
592}
593
594#[cfg(test)]
595mod dashboard_tests {
596 use super::*;
597
598 fn sample_metrics(fidelity: f64, success: bool, time_ms: f64) -> ExecutionMetrics {
599 ExecutionMetrics::new(time_ms, fidelity, success, 5, 3, 2, 1.0 - fidelity)
600 }
601
602 #[test]
603 fn test_dashboard_creation() {
604 let dashboard = PerformanceDashboard::new(DashboardConfig::default());
605 let summary = dashboard.get_summary();
606 assert_eq!(summary.total_executions, 0);
607 assert_eq!(summary.distinct_circuits, 0);
608 }
609
610 #[test]
611 fn test_record_and_summary() {
612 let mut dashboard = PerformanceDashboard::new(DashboardConfig::default());
613 dashboard.record_execution("bell_state", sample_metrics(0.99, true, 10.0));
614 dashboard.record_execution("bell_state", sample_metrics(0.97, true, 12.0));
615 dashboard.record_execution("ghz", sample_metrics(0.95, false, 20.0));
616
617 let summary = dashboard.get_summary();
618 assert_eq!(summary.total_executions, 3);
619 assert_eq!(summary.successful_executions, 2);
620 assert_eq!(summary.distinct_circuits, 2);
621 assert!((summary.success_rate - 2.0 / 3.0).abs() < 1e-6);
622 assert!(summary.mean_fidelity > 0.0);
623 }
624
625 #[test]
626 fn test_execution_count() {
627 let mut dashboard = PerformanceDashboard::new(DashboardConfig::default());
628 dashboard.record_execution("circ_a", sample_metrics(0.9, true, 5.0));
629 dashboard.record_execution("circ_a", sample_metrics(0.88, true, 6.0));
630
631 assert_eq!(dashboard.execution_count("circ_a"), 2);
632 assert_eq!(dashboard.execution_count("missing"), 0);
633 }
634
635 #[test]
636 fn test_export_report_contains_sections() {
637 let mut dashboard = PerformanceDashboard::new(DashboardConfig::default());
638 dashboard.record_execution("test_circuit", sample_metrics(0.99, true, 8.0));
639
640 let report = dashboard.export_report();
641 assert!(report.contains("# QuantRS2 Device Performance Dashboard"));
642 assert!(report.contains("## Summary"));
643 assert!(report.contains("## Per-Circuit Breakdown"));
644 assert!(report.contains("test_circuit"));
645 }
646
647 #[test]
648 fn test_reset() {
649 let mut dashboard = PerformanceDashboard::new(DashboardConfig::default());
650 dashboard.record_execution("circ", sample_metrics(0.9, true, 5.0));
651 assert_eq!(dashboard.get_summary().total_executions, 1);
652 dashboard.reset();
653 assert_eq!(dashboard.get_summary().total_executions, 0);
654 }
655}