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 quantrs2_core::error::{QuantRS2Error, QuantRS2Result};
7use scirs2_core::Complex64;
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    #[must_use]
276    pub fn new() -> Self {
277        Self {
278            error_tracker: Arc::new(Mutex::new(ErrorTracker::default())),
279            performance_monitor: Arc::new(Mutex::new(PerformanceMonitor::default())),
280            memory_tracker: Arc::new(Mutex::new(MemoryTracker::default())),
281            circuit_analyzer: Arc::new(Mutex::new(CircuitAnalyzer::default())),
282        }
283    }
284
285    /// Record an error with detailed context
286    pub fn record_error(&self, error: &QuantRS2Error, context: HashMap<String, String>) {
287        let error_info = self.categorize_error(error, context);
288
289        if let Ok(mut tracker) = self.error_tracker.lock() {
290            tracker.record_error(error_info);
291        }
292    }
293
294    /// Record operation timing
295    pub fn record_operation_time(&self, operation: &str, duration: Duration) {
296        if let Ok(mut monitor) = self.performance_monitor.lock() {
297            monitor.record_operation(operation.to_string(), duration);
298        }
299    }
300
301    /// Record gate application performance
302    pub fn record_gate_performance(&self, gate_name: &str, qubits: &[usize], duration: Duration) {
303        if let Ok(mut monitor) = self.performance_monitor.lock() {
304            monitor.record_gate_performance(gate_name.to_string(), qubits.to_vec(), duration);
305        }
306    }
307
308    /// Record memory allocation
309    pub fn record_memory_allocation(&self, size: usize, operation: &str) {
310        if let Ok(mut tracker) = self.memory_tracker.lock() {
311            tracker.record_allocation(size, operation.to_string());
312        }
313    }
314
315    /// Analyze circuit complexity and optimization opportunities
316    pub fn analyze_circuit<const N: usize>(&self, circuit: &quantrs2_circuit::builder::Circuit<N>) {
317        if let Ok(mut analyzer) = self.circuit_analyzer.lock() {
318            analyzer.analyze_circuit(circuit);
319        }
320    }
321
322    /// Generate comprehensive diagnostic report
323    #[must_use]
324    pub fn generate_report(&self) -> DiagnosticReport {
325        let timestamp = chrono::Utc::now().to_rfc3339();
326
327        let error_summary = self
328            .error_tracker
329            .lock()
330            .map(|tracker| tracker.generate_summary())
331            .unwrap_or_default();
332
333        let performance_summary = self
334            .performance_monitor
335            .lock()
336            .map(|monitor| monitor.generate_summary())
337            .unwrap_or_default();
338
339        let memory_summary = self
340            .memory_tracker
341            .lock()
342            .map(|tracker| tracker.generate_summary())
343            .unwrap_or_default();
344
345        let circuit_analysis = self
346            .circuit_analyzer
347            .lock()
348            .map(|analyzer| analyzer.generate_summary())
349            .unwrap_or_default();
350
351        let recommendations = self.generate_recommendations();
352        let overall_health_score =
353            self.calculate_health_score(&error_summary, &performance_summary, &memory_summary);
354
355        DiagnosticReport {
356            timestamp,
357            error_summary,
358            performance_summary,
359            memory_summary,
360            circuit_analysis,
361            recommendations,
362            overall_health_score,
363        }
364    }
365
366    /// Categorize error for better tracking
367    fn categorize_error(
368        &self,
369        error: &QuantRS2Error,
370        context: HashMap<String, String>,
371    ) -> ErrorInfo {
372        let (category, severity, suggested_fix) = match error {
373            QuantRS2Error::InvalidQubitId(_) => (
374                ErrorCategory::QubitIndex,
375                ErrorSeverity::High,
376                Some("Check qubit indices are within circuit bounds".to_string()),
377            ),
378            QuantRS2Error::CircuitValidationFailed(_) => (
379                ErrorCategory::Circuit,
380                ErrorSeverity::Medium,
381                Some("Validate circuit structure before simulation".to_string()),
382            ),
383            QuantRS2Error::LinalgError(_) => (
384                ErrorCategory::Computation,
385                ErrorSeverity::High,
386                Some("Check matrix dimensions and numerical stability".to_string()),
387            ),
388            QuantRS2Error::UnsupportedOperation(_) => (
389                ErrorCategory::Configuration,
390                ErrorSeverity::Medium,
391                Some("Use supported gate types for this simulator".to_string()),
392            ),
393            QuantRS2Error::InvalidInput(_) => (
394                ErrorCategory::Configuration,
395                ErrorSeverity::Medium,
396                Some("Validate input parameters before operation".to_string()),
397            ),
398            _ => (ErrorCategory::Unknown, ErrorSeverity::Low, None),
399        };
400
401        ErrorInfo {
402            category,
403            message: error.to_string(),
404            context,
405            severity,
406            suggested_fix,
407        }
408    }
409
410    /// Generate optimization recommendations
411    fn generate_recommendations(&self) -> Vec<OptimizationRecommendation> {
412        let mut recommendations = Vec::new();
413
414        // Analyze performance patterns
415        if let Ok(monitor) = self.performance_monitor.lock() {
416            if !monitor.gate_performance.is_empty() {
417                let avg_gate_time: Duration = monitor
418                    .gate_performance
419                    .values()
420                    .map(|stats| stats.average_time)
421                    .sum::<Duration>()
422                    / monitor.gate_performance.len() as u32;
423
424                if avg_gate_time > Duration::from_millis(1) {
425                    recommendations.push(OptimizationRecommendation {
426                        category: OptimizationCategory::GateFusion,
427                        description: "Consider gate fusion to reduce operation overhead"
428                            .to_string(),
429                        expected_improvement: 0.3,
430                        implementation_difficulty: Difficulty::Medium,
431                        priority: Priority::High,
432                    });
433                }
434            }
435        }
436
437        // Analyze memory patterns
438        if let Ok(tracker) = self.memory_tracker.lock() {
439            if tracker.efficiency_metrics.buffer_reuse_rate < 0.7 {
440                recommendations.push(OptimizationRecommendation {
441                    category: OptimizationCategory::MemoryOptimization,
442                    description: "Improve buffer pool utilization for better memory efficiency"
443                        .to_string(),
444                    expected_improvement: 0.25,
445                    implementation_difficulty: Difficulty::Easy,
446                    priority: Priority::Medium,
447                });
448            }
449        }
450
451        // Analyze circuit structure
452        if let Ok(analyzer) = self.circuit_analyzer.lock() {
453            if analyzer.complexity_metrics.parallelization_potential > 0.5 {
454                recommendations.push(OptimizationRecommendation {
455                    category: OptimizationCategory::ParallelizationOpportunity,
456                    description:
457                        "Circuit has high parallelization potential - consider parallel execution"
458                            .to_string(),
459                    expected_improvement: 0.4,
460                    implementation_difficulty: Difficulty::Hard,
461                    priority: Priority::High,
462                });
463            }
464        }
465
466        recommendations
467    }
468
469    /// Calculate overall system health score
470    fn calculate_health_score(
471        &self,
472        error_summary: &ErrorSummary,
473        performance_summary: &PerformanceSummary,
474        memory_summary: &MemorySummary,
475    ) -> f64 {
476        let error_score = if error_summary.total_errors == 0 {
477            1.0
478        } else {
479            error_summary.error_rate.min(0.5).mul_add(-2.0, 1.0)
480        };
481
482        let performance_score = (performance_summary.gates_per_second / 1000.0).min(1.0);
483        let memory_score = memory_summary.buffer_pool_efficiency;
484
485        memory_score.mul_add(0.3, error_score * 0.4 + performance_score * 0.3) * 100.0
486    }
487}
488
489impl ErrorTracker {
490    fn record_error(&mut self, error_info: ErrorInfo) {
491        *self.error_counts.entry(error_info.category).or_insert(0) += 1;
492        self.recent_errors
493            .push((Instant::now(), error_info.clone()));
494
495        // Track error patterns
496        let pattern = format!(
497            "{:?}:{}",
498            error_info.category,
499            error_info
500                .message
501                .split_whitespace()
502                .take(3)
503                .collect::<Vec<_>>()
504                .join(" ")
505        );
506        *self.error_patterns.entry(pattern).or_insert(0) += 1;
507
508        // Keep only recent errors (last 100)
509        if self.recent_errors.len() > 100 {
510            self.recent_errors.remove(0);
511        }
512    }
513
514    fn generate_summary(&self) -> ErrorSummary {
515        let total_errors = self.recent_errors.len();
516        let critical_errors = self
517            .recent_errors
518            .iter()
519            .filter(|(_, error)| matches!(error.severity, ErrorSeverity::Critical))
520            .count();
521
522        let error_rate = if total_errors > 0 {
523            critical_errors as f64 / total_errors as f64
524        } else {
525            0.0
526        };
527
528        ErrorSummary {
529            total_errors,
530            errors_by_category: self.error_counts.clone(),
531            critical_errors,
532            error_rate,
533        }
534    }
535}
536
537impl PerformanceMonitor {
538    fn record_operation(&mut self, operation: String, duration: Duration) {
539        let stats = self.operation_times.entry(operation).or_default();
540        stats.total_time += duration;
541        stats.call_count += 1;
542
543        stats.min_time = Some(stats.min_time.map_or(duration, |min| min.min(duration)));
544        stats.max_time = Some(stats.max_time.map_or(duration, |max| max.max(duration)));
545
546        stats.recent_times.push(duration);
547        if stats.recent_times.len() > 50 {
548            stats.recent_times.remove(0);
549        }
550    }
551
552    fn record_gate_performance(
553        &mut self,
554        gate_name: String,
555        qubits: Vec<usize>,
556        duration: Duration,
557    ) {
558        let stats = self.gate_performance.entry(gate_name).or_default();
559        stats.total_applications += 1;
560        stats.total_time += duration;
561        stats.average_time = stats.total_time / stats.total_applications as u32;
562        stats.qubits_affected.extend(qubits);
563
564        // Calculate efficiency score based on time and qubit count
565        stats.efficiency_score =
566            1000.0 / (duration.as_nanos() as f64 / stats.qubits_affected.len() as f64);
567    }
568
569    fn generate_summary(&self) -> PerformanceSummary {
570        let average_gate_time = if self.gate_performance.is_empty() {
571            0.0
572        } else {
573            self.gate_performance
574                .values()
575                .map(|stats| stats.average_time.as_nanos() as f64)
576                .sum::<f64>()
577                / self.gate_performance.len() as f64
578        };
579
580        let gates_per_second = if average_gate_time > 0.0 {
581            1_000_000_000.0 / average_gate_time
582        } else {
583            0.0
584        };
585
586        PerformanceSummary {
587            average_gate_time,
588            gates_per_second,
589            memory_efficiency: 0.85, // Would be calculated from actual metrics
590            parallelization_efficiency: 0.75, // Would be calculated from actual metrics
591        }
592    }
593}
594
595impl MemoryTracker {
596    fn record_allocation(&mut self, size: usize, operation: String) {
597        self.allocation_patterns
598            .push((Instant::now(), size, operation.clone()));
599
600        // Update peak memory for operation
601        let current_peak = self.peak_memory.entry(operation).or_insert(0);
602        *current_peak = (*current_peak).max(size);
603
604        // Keep only recent allocations
605        if self.allocation_patterns.len() > 1000 {
606            self.allocation_patterns.remove(0);
607        }
608    }
609
610    fn generate_summary(&self) -> MemorySummary {
611        let peak_memory_usage = self.peak_memory.values().max().copied().unwrap_or(0);
612
613        MemorySummary {
614            peak_memory_usage,
615            buffer_pool_efficiency: 0.85, // Would be calculated from actual pool statistics
616            memory_leak_risk: 0.1,        // Would be calculated from growth patterns
617            allocation_efficiency: 0.9,   // Would be calculated from reuse patterns
618        }
619    }
620}
621
622impl CircuitAnalyzer {
623    fn analyze_circuit<const N: usize>(&mut self, circuit: &quantrs2_circuit::builder::Circuit<N>) {
624        self.complexity_metrics.width = N;
625        self.complexity_metrics.total_gates = circuit.gates().len();
626
627        // Analyze gate distribution
628        for gate in circuit.gates() {
629            *self
630                .gate_statistics
631                .entry(gate.name().to_string())
632                .or_insert(0) += 1;
633        }
634
635        // Calculate complexity score
636        self.complexity_metrics.entanglement_measure = self.calculate_entanglement_measure();
637        self.complexity_metrics.parallelization_potential =
638            self.calculate_parallelization_potential();
639
640        // Calculate overall health score
641        self.health_score = self.calculate_circuit_health();
642    }
643
644    fn calculate_entanglement_measure(&self) -> f64 {
645        // Simplified entanglement measure based on two-qubit gates
646        let two_qubit_gates = self
647            .gate_statistics
648            .iter()
649            .filter(|(name, _)| matches!(name.as_str(), "CNOT" | "CZ" | "SWAP" | "CY" | "CH"))
650            .map(|(_, count)| *count)
651            .sum::<usize>();
652
653        two_qubit_gates as f64 / self.complexity_metrics.total_gates.max(1) as f64
654    }
655
656    fn calculate_parallelization_potential(&self) -> f64 {
657        // Simplified calculation based on gate dependencies
658        // In a real implementation, this would analyze the circuit DAG
659        let single_qubit_gates = self
660            .gate_statistics
661            .iter()
662            .filter(|(name, _)| {
663                matches!(
664                    name.as_str(),
665                    "H" | "X" | "Y" | "Z" | "S" | "T" | "RX" | "RY" | "RZ"
666                )
667            })
668            .map(|(_, count)| *count)
669            .sum::<usize>();
670
671        single_qubit_gates as f64 / self.complexity_metrics.total_gates.max(1) as f64
672    }
673
674    fn calculate_circuit_health(&self) -> f64 {
675        // Health score based on various factors
676        let depth_score = if self.complexity_metrics.depth > 0 {
677            1.0 / (1.0 + (self.complexity_metrics.depth as f64 / 100.0))
678        } else {
679            1.0
680        };
681
682        let complexity_score = 1.0 - self.complexity_metrics.entanglement_measure.min(1.0);
683        let parallelization_score = self.complexity_metrics.parallelization_potential;
684
685        (depth_score + complexity_score + parallelization_score) / 3.0 * 100.0
686    }
687
688    fn generate_summary(&self) -> CircuitAnalysisSummary {
689        CircuitAnalysisSummary {
690            complexity_score: self.complexity_metrics.entanglement_measure * 100.0,
691            optimization_potential: self.complexity_metrics.parallelization_potential * 100.0,
692            gate_distribution: self.gate_statistics.clone(),
693            depth_analysis: DepthAnalysis {
694                total_depth: self.complexity_metrics.depth,
695                critical_path_length: self.complexity_metrics.depth, // Simplified
696                parallelization_opportunities: (self.complexity_metrics.parallelization_potential
697                    * 10.0) as usize,
698            },
699        }
700    }
701}
702
703impl Default for ErrorSummary {
704    fn default() -> Self {
705        Self {
706            total_errors: 0,
707            errors_by_category: HashMap::new(),
708            critical_errors: 0,
709            error_rate: 0.0,
710        }
711    }
712}
713
714impl Default for PerformanceSummary {
715    fn default() -> Self {
716        Self {
717            average_gate_time: 0.0,
718            gates_per_second: 0.0,
719            memory_efficiency: 0.0,
720            parallelization_efficiency: 0.0,
721        }
722    }
723}
724
725impl Default for MemorySummary {
726    fn default() -> Self {
727        Self {
728            peak_memory_usage: 0,
729            buffer_pool_efficiency: 0.0,
730            memory_leak_risk: 0.0,
731            allocation_efficiency: 0.0,
732        }
733    }
734}
735
736impl Default for CircuitAnalysisSummary {
737    fn default() -> Self {
738        Self {
739            complexity_score: 0.0,
740            optimization_potential: 0.0,
741            gate_distribution: HashMap::new(),
742            depth_analysis: DepthAnalysis {
743                total_depth: 0,
744                critical_path_length: 0,
745                parallelization_opportunities: 0,
746            },
747        }
748    }
749}
750
751impl Default for SimulationDiagnostics {
752    fn default() -> Self {
753        Self::new()
754    }
755}
756
757#[cfg(test)]
758mod tests {
759    use super::*;
760
761    #[test]
762    fn test_diagnostics_creation() {
763        let diagnostics = SimulationDiagnostics::new();
764        let report = diagnostics.generate_report();
765
766        assert_eq!(report.error_summary.total_errors, 0);
767        assert!(report.overall_health_score >= 0.0);
768    }
769
770    #[test]
771    fn test_error_recording() {
772        let diagnostics = SimulationDiagnostics::new();
773        let error = QuantRS2Error::InvalidQubitId(5);
774        let mut context = HashMap::new();
775        context.insert("operation".to_string(), "gate_application".to_string());
776
777        diagnostics.record_error(&error, context);
778
779        let report = diagnostics.generate_report();
780        assert_eq!(report.error_summary.total_errors, 1);
781        assert!(report
782            .error_summary
783            .errors_by_category
784            .contains_key(&ErrorCategory::QubitIndex));
785    }
786
787    #[test]
788    fn test_performance_recording() {
789        let diagnostics = SimulationDiagnostics::new();
790
791        diagnostics.record_operation_time("gate_application", Duration::from_millis(10));
792        diagnostics.record_gate_performance("H", &[0], Duration::from_micros(500));
793
794        let report = diagnostics.generate_report();
795        assert!(report.performance_summary.average_gate_time > 0.0);
796    }
797}