quantrs2_sim/
diagnostics.rs

1//! Comprehensive error handling and diagnostics for quantum simulation
2//!
3//! This module provides advanced error handling, performance diagnostics,
4//! and system health monitoring for the quantum simulation framework.
5
6use scirs2_core::Complex64;
7use quantrs2_core::error::{QuantRS2Error, QuantRS2Result};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::sync::{Arc, Mutex};
11use std::time::{Duration, Instant};
12
13/// Comprehensive diagnostics system for quantum simulation
14#[derive(Debug, Clone)]
15pub struct SimulationDiagnostics {
16    /// Error tracking and categorization
17    error_tracker: Arc<Mutex<ErrorTracker>>,
18    /// Performance monitoring
19    performance_monitor: Arc<Mutex<PerformanceMonitor>>,
20    /// Memory usage tracking
21    memory_tracker: Arc<Mutex<MemoryTracker>>,
22    /// Circuit analysis results
23    circuit_analyzer: Arc<Mutex<CircuitAnalyzer>>,
24}
25
26/// Error tracking and categorization system
27#[derive(Debug, Default)]
28struct ErrorTracker {
29    /// Total error count by category
30    error_counts: HashMap<ErrorCategory, usize>,
31    /// Recent errors with timestamps
32    recent_errors: Vec<(Instant, ErrorInfo)>,
33    /// Error patterns and frequencies
34    error_patterns: HashMap<String, usize>,
35    /// Critical error threshold
36    critical_threshold: usize,
37}
38
39/// Performance monitoring system
40#[derive(Debug, Default)]
41struct PerformanceMonitor {
42    /// Operation timing statistics
43    operation_times: HashMap<String, OperationStats>,
44    /// Gate application performance
45    gate_performance: HashMap<String, GateStats>,
46    /// Memory allocation patterns
47    allocation_patterns: Vec<(Instant, usize, String)>,
48    /// Simulation throughput metrics
49    throughput_metrics: ThroughputMetrics,
50}
51
52/// Memory usage tracking
53#[derive(Debug, Default)]
54struct MemoryTracker {
55    /// Peak memory usage per operation
56    peak_memory: HashMap<String, usize>,
57    /// Memory efficiency metrics
58    efficiency_metrics: MemoryEfficiencyMetrics,
59    /// Buffer pool statistics
60    buffer_pool_stats: BufferPoolStats,
61    /// Memory leak detection
62    leak_detection: LeakDetectionStats,
63    /// Memory allocation patterns
64    allocation_patterns: Vec<(Instant, usize, String)>,
65}
66
67/// Circuit analysis and optimization recommendations
68#[derive(Debug, Default)]
69struct CircuitAnalyzer {
70    /// Circuit complexity metrics
71    complexity_metrics: ComplexityMetrics,
72    /// Gate count statistics
73    gate_statistics: HashMap<String, usize>,
74    /// Optimization opportunities
75    optimization_opportunities: Vec<OptimizationRecommendation>,
76    /// Circuit health score
77    health_score: f64,
78}
79
80/// Error categorization
81#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
82pub enum ErrorCategory {
83    /// Memory allocation errors
84    Memory,
85    /// Invalid circuit structure
86    Circuit,
87    /// Qubit index out of bounds
88    QubitIndex,
89    /// Mathematical computation errors
90    Computation,
91    /// Hardware/GPU related errors
92    Hardware,
93    /// Configuration errors
94    Configuration,
95    /// Threading/concurrency errors
96    Concurrency,
97    /// Unknown error category
98    Unknown,
99}
100
101/// Detailed error information
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct ErrorInfo {
104    pub category: ErrorCategory,
105    pub message: String,
106    pub context: HashMap<String, String>,
107    pub severity: ErrorSeverity,
108    pub suggested_fix: Option<String>,
109}
110
111/// Error severity levels
112#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
113pub enum ErrorSeverity {
114    Low,
115    Medium,
116    High,
117    Critical,
118}
119
120/// Operation timing statistics
121#[derive(Debug, Default, Clone)]
122struct OperationStats {
123    total_time: Duration,
124    call_count: usize,
125    min_time: Option<Duration>,
126    max_time: Option<Duration>,
127    recent_times: Vec<Duration>,
128}
129
130/// Gate-specific performance statistics
131#[derive(Debug, Default, Clone)]
132struct GateStats {
133    total_applications: usize,
134    total_time: Duration,
135    average_time: Duration,
136    qubits_affected: Vec<usize>,
137    efficiency_score: f64,
138}
139
140/// Throughput metrics
141#[derive(Debug, Default, Clone)]
142struct ThroughputMetrics {
143    gates_per_second: f64,
144    qubits_simulated_per_second: f64,
145    circuits_completed: usize,
146    average_circuit_time: Duration,
147}
148
149/// Memory efficiency metrics
150#[derive(Debug, Default, Clone)]
151struct MemoryEfficiencyMetrics {
152    buffer_reuse_rate: f64,
153    allocation_efficiency: f64,
154    peak_to_average_ratio: f64,
155    fragmentation_score: f64,
156}
157
158/// Buffer pool statistics
159#[derive(Debug, Default, Clone)]
160struct BufferPoolStats {
161    total_allocations: usize,
162    total_reuses: usize,
163    cache_hit_rate: f64,
164    average_buffer_lifetime: Duration,
165}
166
167/// Memory leak detection statistics
168#[derive(Debug, Default, Clone)]
169struct LeakDetectionStats {
170    suspicious_allocations: usize,
171    memory_growth_rate: f64,
172    long_lived_allocations: usize,
173}
174
175/// Circuit complexity metrics
176#[derive(Debug, Default, Clone)]
177struct ComplexityMetrics {
178    total_gates: usize,
179    depth: usize,
180    width: usize,
181    entanglement_measure: f64,
182    parallelization_potential: f64,
183}
184
185/// Optimization recommendations
186#[derive(Debug, Clone, Serialize, Deserialize)]
187pub struct OptimizationRecommendation {
188    pub category: OptimizationCategory,
189    pub description: String,
190    pub expected_improvement: f64,
191    pub implementation_difficulty: Difficulty,
192    pub priority: Priority,
193}
194
195/// Optimization categories
196#[derive(Debug, Clone, Serialize, Deserialize)]
197pub enum OptimizationCategory {
198    GateFusion,
199    CircuitReordering,
200    MemoryOptimization,
201    ParallelizationOpportunity,
202    AlgorithmicImprovement,
203}
204
205/// Implementation difficulty levels
206#[derive(Debug, Clone, Serialize, Deserialize)]
207pub enum Difficulty {
208    Easy,
209    Medium,
210    Hard,
211}
212
213/// Priority levels
214#[derive(Debug, Clone, Serialize, Deserialize)]
215pub enum Priority {
216    Low,
217    Medium,
218    High,
219    Critical,
220}
221
222/// Comprehensive diagnostic report
223#[derive(Debug, Serialize, Deserialize)]
224pub struct DiagnosticReport {
225    pub timestamp: String,
226    pub error_summary: ErrorSummary,
227    pub performance_summary: PerformanceSummary,
228    pub memory_summary: MemorySummary,
229    pub circuit_analysis: CircuitAnalysisSummary,
230    pub recommendations: Vec<OptimizationRecommendation>,
231    pub overall_health_score: f64,
232}
233
234#[derive(Debug, Serialize, Deserialize)]
235pub struct ErrorSummary {
236    pub total_errors: usize,
237    pub errors_by_category: HashMap<ErrorCategory, usize>,
238    pub critical_errors: usize,
239    pub error_rate: f64,
240}
241
242#[derive(Debug, Serialize, Deserialize)]
243pub struct PerformanceSummary {
244    pub average_gate_time: f64,
245    pub gates_per_second: f64,
246    pub memory_efficiency: f64,
247    pub parallelization_efficiency: f64,
248}
249
250#[derive(Debug, Serialize, Deserialize)]
251pub struct MemorySummary {
252    pub peak_memory_usage: usize,
253    pub buffer_pool_efficiency: f64,
254    pub memory_leak_risk: f64,
255    pub allocation_efficiency: f64,
256}
257
258#[derive(Debug, Serialize, Deserialize)]
259pub struct CircuitAnalysisSummary {
260    pub complexity_score: f64,
261    pub optimization_potential: f64,
262    pub gate_distribution: HashMap<String, usize>,
263    pub depth_analysis: DepthAnalysis,
264}
265
266#[derive(Debug, Serialize, Deserialize)]
267pub struct DepthAnalysis {
268    pub total_depth: usize,
269    pub critical_path_length: usize,
270    pub parallelization_opportunities: usize,
271}
272
273impl SimulationDiagnostics {
274    /// Create a new diagnostics system
275    pub fn new() -> Self {
276        Self {
277            error_tracker: Arc::new(Mutex::new(ErrorTracker::default())),
278            performance_monitor: Arc::new(Mutex::new(PerformanceMonitor::default())),
279            memory_tracker: Arc::new(Mutex::new(MemoryTracker::default())),
280            circuit_analyzer: Arc::new(Mutex::new(CircuitAnalyzer::default())),
281        }
282    }
283
284    /// Record an error with detailed context
285    pub fn record_error(&self, error: &QuantRS2Error, context: HashMap<String, String>) {
286        let error_info = self.categorize_error(error, context);
287
288        if let Ok(mut tracker) = self.error_tracker.lock() {
289            tracker.record_error(error_info);
290        }
291    }
292
293    /// Record operation timing
294    pub fn record_operation_time(&self, operation: &str, duration: Duration) {
295        if let Ok(mut monitor) = self.performance_monitor.lock() {
296            monitor.record_operation(operation.to_string(), duration);
297        }
298    }
299
300    /// Record gate application performance
301    pub fn record_gate_performance(&self, gate_name: &str, qubits: &[usize], duration: Duration) {
302        if let Ok(mut monitor) = self.performance_monitor.lock() {
303            monitor.record_gate_performance(gate_name.to_string(), qubits.to_vec(), duration);
304        }
305    }
306
307    /// Record memory allocation
308    pub fn record_memory_allocation(&self, size: usize, operation: &str) {
309        if let Ok(mut tracker) = self.memory_tracker.lock() {
310            tracker.record_allocation(size, operation.to_string());
311        }
312    }
313
314    /// Analyze circuit complexity and optimization opportunities
315    pub fn analyze_circuit<const N: usize>(&self, circuit: &quantrs2_circuit::builder::Circuit<N>) {
316        if let Ok(mut analyzer) = self.circuit_analyzer.lock() {
317            analyzer.analyze_circuit(circuit);
318        }
319    }
320
321    /// Generate comprehensive diagnostic report
322    pub fn generate_report(&self) -> DiagnosticReport {
323        let timestamp = chrono::Utc::now().to_rfc3339();
324
325        let error_summary = self
326            .error_tracker
327            .lock()
328            .map(|tracker| tracker.generate_summary())
329            .unwrap_or_default();
330
331        let performance_summary = self
332            .performance_monitor
333            .lock()
334            .map(|monitor| monitor.generate_summary())
335            .unwrap_or_default();
336
337        let memory_summary = self
338            .memory_tracker
339            .lock()
340            .map(|tracker| tracker.generate_summary())
341            .unwrap_or_default();
342
343        let circuit_analysis = self
344            .circuit_analyzer
345            .lock()
346            .map(|analyzer| analyzer.generate_summary())
347            .unwrap_or_default();
348
349        let recommendations = self.generate_recommendations();
350        let overall_health_score =
351            self.calculate_health_score(&error_summary, &performance_summary, &memory_summary);
352
353        DiagnosticReport {
354            timestamp,
355            error_summary,
356            performance_summary,
357            memory_summary,
358            circuit_analysis,
359            recommendations,
360            overall_health_score,
361        }
362    }
363
364    /// Categorize error for better tracking
365    fn categorize_error(
366        &self,
367        error: &QuantRS2Error,
368        context: HashMap<String, String>,
369    ) -> ErrorInfo {
370        let (category, severity, suggested_fix) = match error {
371            QuantRS2Error::InvalidQubitId(_) => (
372                ErrorCategory::QubitIndex,
373                ErrorSeverity::High,
374                Some("Check qubit indices are within circuit bounds".to_string()),
375            ),
376            QuantRS2Error::CircuitValidationFailed(_) => (
377                ErrorCategory::Circuit,
378                ErrorSeverity::Medium,
379                Some("Validate circuit structure before simulation".to_string()),
380            ),
381            QuantRS2Error::LinalgError(_) => (
382                ErrorCategory::Computation,
383                ErrorSeverity::High,
384                Some("Check matrix dimensions and numerical stability".to_string()),
385            ),
386            QuantRS2Error::UnsupportedOperation(_) => (
387                ErrorCategory::Configuration,
388                ErrorSeverity::Medium,
389                Some("Use supported gate types for this simulator".to_string()),
390            ),
391            QuantRS2Error::InvalidInput(_) => (
392                ErrorCategory::Configuration,
393                ErrorSeverity::Medium,
394                Some("Validate input parameters before operation".to_string()),
395            ),
396            _ => (ErrorCategory::Unknown, ErrorSeverity::Low, None),
397        };
398
399        ErrorInfo {
400            category,
401            message: error.to_string(),
402            context,
403            severity,
404            suggested_fix,
405        }
406    }
407
408    /// Generate optimization recommendations
409    fn generate_recommendations(&self) -> Vec<OptimizationRecommendation> {
410        let mut recommendations = Vec::new();
411
412        // Analyze performance patterns
413        if let Ok(monitor) = self.performance_monitor.lock() {
414            if monitor.gate_performance.len() > 0 {
415                let avg_gate_time: Duration = monitor
416                    .gate_performance
417                    .values()
418                    .map(|stats| stats.average_time)
419                    .sum::<Duration>()
420                    / monitor.gate_performance.len() as u32;
421
422                if avg_gate_time > Duration::from_millis(1) {
423                    recommendations.push(OptimizationRecommendation {
424                        category: OptimizationCategory::GateFusion,
425                        description: "Consider gate fusion to reduce operation overhead"
426                            .to_string(),
427                        expected_improvement: 0.3,
428                        implementation_difficulty: Difficulty::Medium,
429                        priority: Priority::High,
430                    });
431                }
432            }
433        }
434
435        // Analyze memory patterns
436        if let Ok(tracker) = self.memory_tracker.lock() {
437            if tracker.efficiency_metrics.buffer_reuse_rate < 0.7 {
438                recommendations.push(OptimizationRecommendation {
439                    category: OptimizationCategory::MemoryOptimization,
440                    description: "Improve buffer pool utilization for better memory efficiency"
441                        .to_string(),
442                    expected_improvement: 0.25,
443                    implementation_difficulty: Difficulty::Easy,
444                    priority: Priority::Medium,
445                });
446            }
447        }
448
449        // Analyze circuit structure
450        if let Ok(analyzer) = self.circuit_analyzer.lock() {
451            if analyzer.complexity_metrics.parallelization_potential > 0.5 {
452                recommendations.push(OptimizationRecommendation {
453                    category: OptimizationCategory::ParallelizationOpportunity,
454                    description:
455                        "Circuit has high parallelization potential - consider parallel execution"
456                            .to_string(),
457                    expected_improvement: 0.4,
458                    implementation_difficulty: Difficulty::Hard,
459                    priority: Priority::High,
460                });
461            }
462        }
463
464        recommendations
465    }
466
467    /// Calculate overall system health score
468    fn calculate_health_score(
469        &self,
470        error_summary: &ErrorSummary,
471        performance_summary: &PerformanceSummary,
472        memory_summary: &MemorySummary,
473    ) -> f64 {
474        let error_score = if error_summary.total_errors == 0 {
475            1.0
476        } else {
477            1.0 - (error_summary.error_rate.min(0.5) * 2.0)
478        };
479
480        let performance_score = (performance_summary.gates_per_second / 1000.0).min(1.0);
481        let memory_score = memory_summary.buffer_pool_efficiency;
482
483        (error_score * 0.4 + performance_score * 0.3 + memory_score * 0.3) * 100.0
484    }
485}
486
487impl ErrorTracker {
488    fn record_error(&mut self, error_info: ErrorInfo) {
489        *self.error_counts.entry(error_info.category).or_insert(0) += 1;
490        self.recent_errors
491            .push((Instant::now(), error_info.clone()));
492
493        // Track error patterns
494        let pattern = format!(
495            "{:?}:{}",
496            error_info.category,
497            error_info
498                .message
499                .split_whitespace()
500                .take(3)
501                .collect::<Vec<_>>()
502                .join(" ")
503        );
504        *self.error_patterns.entry(pattern).or_insert(0) += 1;
505
506        // Keep only recent errors (last 100)
507        if self.recent_errors.len() > 100 {
508            self.recent_errors.remove(0);
509        }
510    }
511
512    fn generate_summary(&self) -> ErrorSummary {
513        let total_errors = self.recent_errors.len();
514        let critical_errors = self
515            .recent_errors
516            .iter()
517            .filter(|(_, error)| matches!(error.severity, ErrorSeverity::Critical))
518            .count();
519
520        let error_rate = if total_errors > 0 {
521            critical_errors as f64 / total_errors as f64
522        } else {
523            0.0
524        };
525
526        ErrorSummary {
527            total_errors,
528            errors_by_category: self.error_counts.clone(),
529            critical_errors,
530            error_rate,
531        }
532    }
533}
534
535impl PerformanceMonitor {
536    fn record_operation(&mut self, operation: String, duration: Duration) {
537        let stats = self.operation_times.entry(operation).or_default();
538        stats.total_time += duration;
539        stats.call_count += 1;
540
541        stats.min_time = Some(stats.min_time.map_or(duration, |min| min.min(duration)));
542        stats.max_time = Some(stats.max_time.map_or(duration, |max| max.max(duration)));
543
544        stats.recent_times.push(duration);
545        if stats.recent_times.len() > 50 {
546            stats.recent_times.remove(0);
547        }
548    }
549
550    fn record_gate_performance(
551        &mut self,
552        gate_name: String,
553        qubits: Vec<usize>,
554        duration: Duration,
555    ) {
556        let stats = self.gate_performance.entry(gate_name).or_default();
557        stats.total_applications += 1;
558        stats.total_time += duration;
559        stats.average_time = stats.total_time / stats.total_applications as u32;
560        stats.qubits_affected.extend(qubits);
561
562        // Calculate efficiency score based on time and qubit count
563        stats.efficiency_score =
564            1000.0 / (duration.as_nanos() as f64 / stats.qubits_affected.len() as f64);
565    }
566
567    fn generate_summary(&self) -> PerformanceSummary {
568        let average_gate_time = if !self.gate_performance.is_empty() {
569            self.gate_performance
570                .values()
571                .map(|stats| stats.average_time.as_nanos() as f64)
572                .sum::<f64>()
573                / self.gate_performance.len() as f64
574        } else {
575            0.0
576        };
577
578        let gates_per_second = if average_gate_time > 0.0 {
579            1_000_000_000.0 / average_gate_time
580        } else {
581            0.0
582        };
583
584        PerformanceSummary {
585            average_gate_time,
586            gates_per_second,
587            memory_efficiency: 0.85, // Would be calculated from actual metrics
588            parallelization_efficiency: 0.75, // Would be calculated from actual metrics
589        }
590    }
591}
592
593impl MemoryTracker {
594    fn record_allocation(&mut self, size: usize, operation: String) {
595        self.allocation_patterns
596            .push((Instant::now(), size, operation.clone()));
597
598        // Update peak memory for operation
599        let current_peak = self.peak_memory.entry(operation).or_insert(0);
600        *current_peak = (*current_peak).max(size);
601
602        // Keep only recent allocations
603        if self.allocation_patterns.len() > 1000 {
604            self.allocation_patterns.remove(0);
605        }
606    }
607
608    fn generate_summary(&self) -> MemorySummary {
609        let peak_memory_usage = self.peak_memory.values().max().copied().unwrap_or(0);
610
611        MemorySummary {
612            peak_memory_usage,
613            buffer_pool_efficiency: 0.85, // Would be calculated from actual pool statistics
614            memory_leak_risk: 0.1,        // Would be calculated from growth patterns
615            allocation_efficiency: 0.9,   // Would be calculated from reuse patterns
616        }
617    }
618}
619
620impl CircuitAnalyzer {
621    fn analyze_circuit<const N: usize>(&mut self, circuit: &quantrs2_circuit::builder::Circuit<N>) {
622        self.complexity_metrics.width = N;
623        self.complexity_metrics.total_gates = circuit.gates().len();
624
625        // Analyze gate distribution
626        for gate in circuit.gates() {
627            *self
628                .gate_statistics
629                .entry(gate.name().to_string())
630                .or_insert(0) += 1;
631        }
632
633        // Calculate complexity score
634        self.complexity_metrics.entanglement_measure = self.calculate_entanglement_measure();
635        self.complexity_metrics.parallelization_potential =
636            self.calculate_parallelization_potential();
637
638        // Calculate overall health score
639        self.health_score = self.calculate_circuit_health();
640    }
641
642    fn calculate_entanglement_measure(&self) -> f64 {
643        // Simplified entanglement measure based on two-qubit gates
644        let two_qubit_gates = self
645            .gate_statistics
646            .iter()
647            .filter(|(name, _)| matches!(name.as_str(), "CNOT" | "CZ" | "SWAP" | "CY" | "CH"))
648            .map(|(_, count)| *count)
649            .sum::<usize>();
650
651        two_qubit_gates as f64 / self.complexity_metrics.total_gates.max(1) as f64
652    }
653
654    fn calculate_parallelization_potential(&self) -> f64 {
655        // Simplified calculation based on gate dependencies
656        // In a real implementation, this would analyze the circuit DAG
657        let single_qubit_gates = self
658            .gate_statistics
659            .iter()
660            .filter(|(name, _)| {
661                matches!(
662                    name.as_str(),
663                    "H" | "X" | "Y" | "Z" | "S" | "T" | "RX" | "RY" | "RZ"
664                )
665            })
666            .map(|(_, count)| *count)
667            .sum::<usize>();
668
669        single_qubit_gates as f64 / self.complexity_metrics.total_gates.max(1) as f64
670    }
671
672    fn calculate_circuit_health(&self) -> f64 {
673        // Health score based on various factors
674        let depth_score = if self.complexity_metrics.depth > 0 {
675            1.0 / (1.0 + (self.complexity_metrics.depth as f64 / 100.0))
676        } else {
677            1.0
678        };
679
680        let complexity_score = 1.0 - self.complexity_metrics.entanglement_measure.min(1.0);
681        let parallelization_score = self.complexity_metrics.parallelization_potential;
682
683        (depth_score + complexity_score + parallelization_score) / 3.0 * 100.0
684    }
685
686    fn generate_summary(&self) -> CircuitAnalysisSummary {
687        CircuitAnalysisSummary {
688            complexity_score: self.complexity_metrics.entanglement_measure * 100.0,
689            optimization_potential: self.complexity_metrics.parallelization_potential * 100.0,
690            gate_distribution: self.gate_statistics.clone(),
691            depth_analysis: DepthAnalysis {
692                total_depth: self.complexity_metrics.depth,
693                critical_path_length: self.complexity_metrics.depth, // Simplified
694                parallelization_opportunities: (self.complexity_metrics.parallelization_potential
695                    * 10.0) as usize,
696            },
697        }
698    }
699}
700
701impl Default for ErrorSummary {
702    fn default() -> Self {
703        Self {
704            total_errors: 0,
705            errors_by_category: HashMap::new(),
706            critical_errors: 0,
707            error_rate: 0.0,
708        }
709    }
710}
711
712impl Default for PerformanceSummary {
713    fn default() -> Self {
714        Self {
715            average_gate_time: 0.0,
716            gates_per_second: 0.0,
717            memory_efficiency: 0.0,
718            parallelization_efficiency: 0.0,
719        }
720    }
721}
722
723impl Default for MemorySummary {
724    fn default() -> Self {
725        Self {
726            peak_memory_usage: 0,
727            buffer_pool_efficiency: 0.0,
728            memory_leak_risk: 0.0,
729            allocation_efficiency: 0.0,
730        }
731    }
732}
733
734impl Default for CircuitAnalysisSummary {
735    fn default() -> Self {
736        Self {
737            complexity_score: 0.0,
738            optimization_potential: 0.0,
739            gate_distribution: HashMap::new(),
740            depth_analysis: DepthAnalysis {
741                total_depth: 0,
742                critical_path_length: 0,
743                parallelization_opportunities: 0,
744            },
745        }
746    }
747}
748
749impl Default for SimulationDiagnostics {
750    fn default() -> Self {
751        Self::new()
752    }
753}
754
755#[cfg(test)]
756mod tests {
757    use super::*;
758
759    #[test]
760    fn test_diagnostics_creation() {
761        let diagnostics = SimulationDiagnostics::new();
762        let report = diagnostics.generate_report();
763
764        assert_eq!(report.error_summary.total_errors, 0);
765        assert!(report.overall_health_score >= 0.0);
766    }
767
768    #[test]
769    fn test_error_recording() {
770        let diagnostics = SimulationDiagnostics::new();
771        let error = QuantRS2Error::InvalidQubitId(5);
772        let mut context = HashMap::new();
773        context.insert("operation".to_string(), "gate_application".to_string());
774
775        diagnostics.record_error(&error, context);
776
777        let report = diagnostics.generate_report();
778        assert_eq!(report.error_summary.total_errors, 1);
779        assert!(report
780            .error_summary
781            .errors_by_category
782            .contains_key(&ErrorCategory::QubitIndex));
783    }
784
785    #[test]
786    fn test_performance_recording() {
787        let diagnostics = SimulationDiagnostics::new();
788
789        diagnostics.record_operation_time("gate_application", Duration::from_millis(10));
790        diagnostics.record_gate_performance("H", &[0], Duration::from_micros(500));
791
792        let report = diagnostics.generate_report();
793        assert!(report.performance_summary.average_gate_time > 0.0);
794    }
795}