quantrs2_core/optimizations/
profiling_integration.rs

1//! Quantum Computing Profiling Integration with SciRS2 Beta.1
2//!
3//! This module provides comprehensive profiling capabilities for quantum
4//! computations using the advanced profiling features in scirs2-core beta.1.
5
6use crate::error::QuantRS2Result;
7use scirs2_core::profiling::{MemoryTracker, Profiler, Timer};
8use std::collections::HashMap;
9use std::sync::{Arc, Mutex, OnceLock};
10use std::time::{Duration, Instant};
11
12use std::fmt::Write;
13/// Quantum operation profiling data
14#[derive(Debug, Clone, serde::Serialize)]
15pub struct QuantumOperationProfile {
16    pub operation_name: String,
17    pub execution_count: u64,
18    pub total_time: Duration,
19    pub average_time: Duration,
20    pub min_time: Duration,
21    pub max_time: Duration,
22    pub memory_usage: u64,
23    pub gate_count: u64,
24}
25
26/// Comprehensive quantum profiler
27pub struct QuantumProfiler {
28    /// Operation profiles
29    profiles: Arc<Mutex<HashMap<String, QuantumOperationProfile>>>,
30    /// Active timers
31    active_timers: Arc<Mutex<HashMap<String, Instant>>>,
32    /// Global profiling enabled flag
33    enabled: Arc<Mutex<bool>>,
34    /// Memory tracking
35    memory_tracker: Arc<Mutex<Option<MemoryTracker>>>,
36}
37
38impl Default for QuantumProfiler {
39    fn default() -> Self {
40        Self::new()
41    }
42}
43
44impl QuantumProfiler {
45    /// Create a new quantum profiler
46    pub fn new() -> Self {
47        Self {
48            profiles: Arc::new(Mutex::new(HashMap::new())),
49            active_timers: Arc::new(Mutex::new(HashMap::new())),
50            enabled: Arc::new(Mutex::new(false)),
51            memory_tracker: Arc::new(Mutex::new(None)),
52        }
53    }
54
55    /// Enable profiling
56    pub fn enable(&self) {
57        *self.enabled.lock().expect("Profiler enabled lock poisoned") = true;
58
59        // Start global profiler
60        if let Ok(mut profiler) = Profiler::global().lock() {
61            profiler.start();
62        }
63    }
64
65    /// Disable profiling
66    pub fn disable(&self) {
67        *self.enabled.lock().expect("Profiler enabled lock poisoned") = false;
68
69        // Stop global profiler
70        if let Ok(mut profiler) = Profiler::global().lock() {
71            profiler.stop();
72        }
73    }
74
75    /// Check if profiling is enabled
76    pub fn is_enabled(&self) -> bool {
77        *self.enabled.lock().expect("Profiler enabled lock poisoned")
78    }
79
80    /// Start profiling a quantum operation
81    pub fn start_operation(&self, operation_name: &str) {
82        if !self.is_enabled() {
83            return;
84        }
85
86        let mut timers = self
87            .active_timers
88            .lock()
89            .expect("Active timers lock poisoned");
90        timers.insert(operation_name.to_string(), Instant::now());
91
92        // Start memory tracking
93        let mut tracker = self
94            .memory_tracker
95            .lock()
96            .expect("Memory tracker lock poisoned");
97        *tracker = Some(MemoryTracker::start(operation_name));
98    }
99
100    /// End profiling a quantum operation
101    pub fn end_operation(&self, operation_name: &str, gate_count: u64) {
102        if !self.is_enabled() {
103            return;
104        }
105
106        let start_time = {
107            let mut timers = self
108                .active_timers
109                .lock()
110                .expect("Active timers lock poisoned");
111            timers.remove(operation_name)
112        };
113
114        if let Some(start) = start_time {
115            let execution_time = start.elapsed();
116
117            // Stop memory tracking
118            let memory_usage = {
119                let mut tracker = self
120                    .memory_tracker
121                    .lock()
122                    .expect("Memory tracker lock poisoned");
123                if let Some(mem_tracker) = tracker.take() {
124                    mem_tracker.stop();
125                    // In a real implementation, this would return actual memory usage
126                    0 // Placeholder
127                } else {
128                    0
129                }
130            };
131
132            // Update profile
133            let mut profiles = self.profiles.lock().expect("Profiles lock poisoned");
134            let profile = profiles
135                .entry(operation_name.to_string())
136                .or_insert_with(|| QuantumOperationProfile {
137                    operation_name: operation_name.to_string(),
138                    execution_count: 0,
139                    total_time: Duration::ZERO,
140                    average_time: Duration::ZERO,
141                    min_time: Duration::MAX,
142                    max_time: Duration::ZERO,
143                    memory_usage: 0,
144                    gate_count: 0,
145                });
146
147            profile.execution_count += 1;
148            profile.total_time += execution_time;
149            profile.average_time = profile.total_time / profile.execution_count as u32;
150            profile.min_time = profile.min_time.min(execution_time);
151            profile.max_time = profile.max_time.max(execution_time);
152            profile.memory_usage += memory_usage;
153            profile.gate_count += gate_count;
154        }
155    }
156
157    /// Profile a quantum operation with automatic timing
158    pub fn profile_operation<F, R>(&self, operation_name: &str, gate_count: u64, operation: F) -> R
159    where
160        F: FnOnce() -> R,
161    {
162        if !self.is_enabled() {
163            return operation();
164        }
165
166        self.start_operation(operation_name);
167        let result = operation();
168        self.end_operation(operation_name, gate_count);
169        result
170    }
171
172    /// Get profiling results for all operations
173    pub fn get_profiles(&self) -> HashMap<String, QuantumOperationProfile> {
174        self.profiles
175            .lock()
176            .expect("Profiles lock poisoned")
177            .clone()
178    }
179
180    /// Get profiling results for a specific operation
181    pub fn get_operation_profile(&self, operation_name: &str) -> Option<QuantumOperationProfile> {
182        self.profiles
183            .lock()
184            .expect("Profiles lock poisoned")
185            .get(operation_name)
186            .cloned()
187    }
188
189    /// Generate a comprehensive profiling report
190    pub fn generate_report(&self) -> String {
191        let profiles = self.get_profiles();
192        let mut report = String::new();
193
194        report.push_str("=== QuantRS2 Performance Profiling Report ===\n\n");
195
196        if profiles.is_empty() {
197            report.push_str("No profiling data available.\n");
198            return report;
199        }
200
201        // Sort by total execution time
202        let mut sorted_profiles: Vec<_> = profiles.values().collect();
203        sorted_profiles.sort_by(|a, b| b.total_time.cmp(&a.total_time));
204
205        writeln!(
206            report,
207            "{:<30} {:<10} {:<12} {:<12} {:<12} {:<12} {:<10}",
208            "Operation", "Count", "Total (ms)", "Avg (ms)", "Min (ms)", "Max (ms)", "Gates"
209        )
210        .expect("Writing to String cannot fail");
211        report.push_str(&"-".repeat(110));
212        report.push('\n');
213
214        for profile in &sorted_profiles {
215            writeln!(
216                report,
217                "{:<30} {:<10} {:<12.3} {:<12.3} {:<12.3} {:<12.3} {:<10}",
218                profile.operation_name,
219                profile.execution_count,
220                profile.total_time.as_secs_f64() * 1000.0,
221                profile.average_time.as_secs_f64() * 1000.0,
222                profile.min_time.as_secs_f64() * 1000.0,
223                profile.max_time.as_secs_f64() * 1000.0,
224                profile.gate_count,
225            )
226            .expect("Writing to String cannot fail");
227        }
228
229        report.push_str("\n=== Performance Insights ===\n");
230
231        // Find the most time-consuming operation
232        if let Some(slowest) = sorted_profiles.first() {
233            writeln!(
234                report,
235                "Most time-consuming operation: {} ({:.3}ms total)",
236                slowest.operation_name,
237                slowest.total_time.as_secs_f64() * 1000.0
238            )
239            .expect("Writing to String cannot fail");
240        }
241
242        // Find the most frequent operation
243        let most_frequent = sorted_profiles.iter().max_by_key(|p| p.execution_count);
244        if let Some(frequent) = most_frequent {
245            writeln!(
246                report,
247                "Most frequent operation: {} ({} executions)",
248                frequent.operation_name, frequent.execution_count
249            )
250            .expect("Writing to String cannot fail");
251        }
252
253        // Calculate total gate throughput
254        let total_gates: u64 = profiles.values().map(|p| p.gate_count).sum();
255        let total_time: Duration = profiles.values().map(|p| p.total_time).sum();
256        if total_time.as_secs_f64() > 0.0 {
257            let gate_throughput = total_gates as f64 / total_time.as_secs_f64();
258            writeln!(
259                report,
260                "Total gate throughput: {gate_throughput:.0} gates/second"
261            )
262            .expect("Writing to String cannot fail");
263        }
264
265        report
266    }
267
268    /// Clear all profiling data
269    pub fn clear(&self) {
270        self.profiles
271            .lock()
272            .expect("Profiles lock poisoned")
273            .clear();
274        self.active_timers
275            .lock()
276            .expect("Active timers lock poisoned")
277            .clear();
278    }
279
280    /// Export profiling data to JSON
281    pub fn export_json(&self) -> QuantRS2Result<String> {
282        let profiles = self.get_profiles();
283        serde_json::to_string_pretty(&profiles).map_err(|e| e.into()) // Use the existing From<serde_json::Error> implementation
284    }
285}
286
287/// Global quantum profiler instance
288static GLOBAL_QUANTUM_PROFILER: OnceLock<QuantumProfiler> = OnceLock::new();
289
290/// Get the global quantum profiler
291pub fn global_quantum_profiler() -> &'static QuantumProfiler {
292    GLOBAL_QUANTUM_PROFILER.get_or_init(QuantumProfiler::new)
293}
294
295/// Enable quantum profiling globally
296pub fn enable_quantum_profiling() {
297    global_quantum_profiler().enable();
298}
299
300/// Disable quantum profiling globally
301pub fn disable_quantum_profiling() {
302    global_quantum_profiler().disable();
303}
304
305/// Check if quantum profiling is active
306pub fn is_profiling_active() -> bool {
307    global_quantum_profiler().is_enabled()
308}
309
310/// Macro for easy profiling of quantum operations
311#[macro_export]
312macro_rules! profile_quantum_operation {
313    ($operation_name:expr, $gate_count:expr, $operation:expr) => {{
314        $crate::optimizations::profiling_integration::global_quantum_profiler().profile_operation(
315            $operation_name,
316            $gate_count,
317            || $operation,
318        )
319    }};
320}
321
322/// Macro for easy profiling with automatic gate counting
323#[macro_export]
324macro_rules! profile_gate_operation {
325    ($gate_name:expr, $operation:expr) => {{
326        $crate::optimizations::profiling_integration::global_quantum_profiler().profile_operation(
327            $gate_name,
328            1,
329            || $operation,
330        )
331    }};
332}
333
334#[cfg(test)]
335mod tests {
336    use super::*;
337    use std::thread;
338
339    #[test]
340    fn test_basic_profiling() {
341        let profiler = QuantumProfiler::new();
342        profiler.enable();
343
344        // Profile a simple operation
345        let result = profiler.profile_operation("test_gate", 1, || {
346            thread::sleep(Duration::from_millis(10));
347            42
348        });
349
350        assert_eq!(result, 42);
351
352        let profiles = profiler.get_profiles();
353        assert!(profiles.contains_key("test_gate"));
354
355        let test_profile = &profiles["test_gate"];
356        assert_eq!(test_profile.execution_count, 1);
357        assert_eq!(test_profile.gate_count, 1);
358        assert!(test_profile.total_time >= Duration::from_millis(10));
359    }
360
361    #[test]
362    fn test_multiple_operations() {
363        let profiler = QuantumProfiler::new();
364        profiler.enable();
365
366        // Profile multiple operations
367        for i in 0..5 {
368            profiler.profile_operation("hadamard", 1, || {
369                thread::sleep(Duration::from_millis(1));
370            });
371        }
372
373        for i in 0..3 {
374            profiler.profile_operation("cnot", 2, || {
375                thread::sleep(Duration::from_millis(2));
376            });
377        }
378
379        let profiles = profiler.get_profiles();
380
381        let hadamard_profile = &profiles["hadamard"];
382        assert_eq!(hadamard_profile.execution_count, 5);
383        assert_eq!(hadamard_profile.gate_count, 5);
384
385        let cnot_profile = &profiles["cnot"];
386        assert_eq!(cnot_profile.execution_count, 3);
387        assert_eq!(cnot_profile.gate_count, 6); // 2 gates * 3 executions
388    }
389
390    #[test]
391    fn test_profiling_disabled() {
392        let profiler = QuantumProfiler::new();
393        // Don't enable profiling
394
395        let result = profiler.profile_operation("test_gate", 1, || 42);
396
397        assert_eq!(result, 42);
398
399        let profiles = profiler.get_profiles();
400        assert!(profiles.is_empty()); // No profiling data should be collected
401    }
402
403    #[test]
404    fn test_report_generation() {
405        let profiler = QuantumProfiler::new();
406        profiler.enable();
407
408        profiler.profile_operation("fast_gate", 1, || {
409            thread::sleep(Duration::from_millis(1));
410        });
411
412        profiler.profile_operation("slow_gate", 1, || {
413            thread::sleep(Duration::from_millis(10));
414        });
415
416        let report = profiler.generate_report();
417        assert!(report.contains("QuantRS2 Performance Profiling Report"));
418        assert!(report.contains("fast_gate"));
419        assert!(report.contains("slow_gate"));
420        assert!(report.contains("Performance Insights"));
421    }
422
423    #[test]
424    fn test_json_export() {
425        let profiler = QuantumProfiler::new();
426        profiler.enable();
427
428        profiler.profile_operation("test_operation", 1, || {
429            thread::sleep(Duration::from_millis(1));
430        });
431
432        let json_result = profiler.export_json();
433        assert!(json_result.is_ok());
434
435        let json = json_result.expect("Failed to export JSON");
436        assert!(json.contains("test_operation"));
437        assert!(json.contains("execution_count"));
438    }
439}