1use crate::error::{QuantRS2Error, QuantRS2Result};
7use crate::platform::PlatformCapabilities;
8use std::collections::HashMap;
9use std::time::{Duration, Instant};
10
11pub struct QuantumProfiler {
13 capabilities: PlatformCapabilities,
15 sessions: HashMap<String, ProfilingSession>,
17 global_stats: GlobalStatistics,
19}
20
21impl Default for QuantumProfiler {
22 fn default() -> Self {
23 Self::new()
24 }
25}
26
27impl QuantumProfiler {
28 pub fn new() -> Self {
30 Self {
31 capabilities: PlatformCapabilities::detect(),
32 sessions: HashMap::new(),
33 global_stats: GlobalStatistics::default(),
34 }
35 }
36
37 pub fn start_session(&mut self, name: impl Into<String>) -> ProfilingSessionHandle {
39 let name = name.into();
40 let session = ProfilingSession::new(name.clone());
41 let handle = ProfilingSessionHandle {
42 name: name.clone(),
43 start_time: session.start_time,
44 };
45 self.sessions.insert(name, session);
46 handle
47 }
48
49 pub fn end_session(
51 &mut self,
52 handle: ProfilingSessionHandle,
53 ) -> QuantRS2Result<ProfilingReport> {
54 let session = self
55 .sessions
56 .remove(&handle.name)
57 .ok_or_else(|| QuantRS2Error::InvalidInput("Session not found".to_string()))?;
58
59 let duration = handle.start_time.elapsed();
60
61 self.global_stats.total_sessions += 1;
63 self.global_stats.total_duration += duration;
64
65 let recommendations = self.generate_recommendations(&session);
67
68 Ok(ProfilingReport {
69 session_name: handle.name,
70 duration,
71 metrics: session.metrics,
72 gate_counts: session.gate_counts,
73 memory_usage: session.memory_usage,
74 recommendations,
75 })
76 }
77
78 pub fn record_gate(&mut self, session_name: &str, gate_type: &str) -> QuantRS2Result<()> {
80 let session = self
81 .sessions
82 .get_mut(session_name)
83 .ok_or_else(|| QuantRS2Error::InvalidInput("Session not found".to_string()))?;
84
85 *session
86 .gate_counts
87 .entry(gate_type.to_string())
88 .or_insert(0) += 1;
89 session.metrics.total_gates += 1;
90
91 Ok(())
92 }
93
94 pub fn record_measurement(
96 &mut self,
97 session_name: &str,
98 num_qubits: usize,
99 ) -> QuantRS2Result<()> {
100 let session = self
101 .sessions
102 .get_mut(session_name)
103 .ok_or_else(|| QuantRS2Error::InvalidInput("Session not found".to_string()))?;
104
105 session.metrics.measurements += 1;
106 session.metrics.qubits_measured = session.metrics.qubits_measured.max(num_qubits);
107
108 Ok(())
109 }
110
111 pub fn record_memory(
113 &mut self,
114 session_name: &str,
115 bytes: usize,
116 phase: &str,
117 ) -> QuantRS2Result<()> {
118 let session = self
119 .sessions
120 .get_mut(session_name)
121 .ok_or_else(|| QuantRS2Error::InvalidInput("Session not found".to_string()))?;
122
123 session.memory_usage.insert(phase.to_string(), bytes);
124 session.metrics.peak_memory = session.metrics.peak_memory.max(bytes);
125
126 Ok(())
127 }
128
129 pub fn record_depth(&mut self, session_name: &str, depth: usize) -> QuantRS2Result<()> {
131 let session = self
132 .sessions
133 .get_mut(session_name)
134 .ok_or_else(|| QuantRS2Error::InvalidInput("Session not found".to_string()))?;
135
136 session.metrics.circuit_depth = session.metrics.circuit_depth.max(depth);
137
138 Ok(())
139 }
140
141 pub fn global_statistics(&self) -> &GlobalStatistics {
143 &self.global_stats
144 }
145
146 fn generate_recommendations(&self, session: &ProfilingSession) -> Vec<String> {
148 let mut recommendations = Vec::new();
149
150 let two_qubit_gates = session
152 .gate_counts
153 .iter()
154 .filter(|(name, _)| {
155 name.contains("CNOT") || name.contains("CZ") || name.contains("SWAP")
156 })
157 .map(|(_, count)| count)
158 .sum::<usize>();
159
160 if two_qubit_gates > session.metrics.total_gates / 2 {
161 recommendations.push(
162 "High two-qubit gate count detected. Consider circuit optimization.".to_string(),
163 );
164 }
165
166 if session.metrics.circuit_depth > 100 {
168 recommendations.push(
169 "Deep circuit detected. Consider gate fusion or compilation optimization."
170 .to_string(),
171 );
172 }
173
174 if session.metrics.peak_memory > 1024 * 1024 * 1024 {
176 recommendations.push(
178 "High memory usage detected. Consider tensor network or state vector compression."
179 .to_string(),
180 );
181 }
182
183 if !self.capabilities.has_simd() {
185 recommendations.push(
186 "SIMD not available. Performance may be limited for large state vectors."
187 .to_string(),
188 );
189 }
190
191 if !self.capabilities.has_gpu() && session.metrics.total_gates > 1000 {
192 recommendations.push(
193 "GPU not available. Consider GPU acceleration for large circuits.".to_string(),
194 );
195 }
196
197 if recommendations.is_empty() {
198 recommendations.push("Circuit is well-optimized for current platform.".to_string());
199 }
200
201 recommendations
202 }
203}
204
205#[derive(Debug, Clone)]
207pub struct ProfilingSessionHandle {
208 name: String,
209 start_time: Instant,
210}
211
212#[derive(Debug, Clone)]
214struct ProfilingSession {
215 name: String,
216 start_time: Instant,
217 metrics: SessionMetrics,
218 gate_counts: HashMap<String, usize>,
219 memory_usage: HashMap<String, usize>,
220}
221
222impl ProfilingSession {
223 fn new(name: String) -> Self {
224 Self {
225 name,
226 start_time: Instant::now(),
227 metrics: SessionMetrics::default(),
228 gate_counts: HashMap::new(),
229 memory_usage: HashMap::new(),
230 }
231 }
232}
233
234#[derive(Debug, Clone, Default)]
236struct SessionMetrics {
237 total_gates: usize,
238 circuit_depth: usize,
239 measurements: usize,
240 qubits_measured: usize,
241 peak_memory: usize,
242}
243
244#[derive(Debug, Clone, Default)]
246pub struct GlobalStatistics {
247 pub total_sessions: usize,
249 pub total_duration: Duration,
251}
252
253#[derive(Debug, Clone)]
255pub struct ProfilingReport {
256 pub session_name: String,
258 pub duration: Duration,
260 pub metrics: SessionMetrics,
262 pub gate_counts: HashMap<String, usize>,
264 pub memory_usage: HashMap<String, usize>,
266 pub recommendations: Vec<String>,
268}
269
270impl ProfilingReport {
271 pub fn print_detailed(&self) {
273 println!("╔══════════════════════════════════════════════════════════════╗");
274 println!("║ Quantum Algorithm Profiling Report ║");
275 println!("╚══════════════════════════════════════════════════════════════╝");
276 println!();
277 println!("Session: {}", self.session_name);
278 println!("Duration: {:?}", self.duration);
279 println!();
280 println!("═══ Gate Statistics ═══");
281 println!(" Total gates: {}", self.metrics.total_gates);
282 println!(" Circuit depth: {}", self.metrics.circuit_depth);
283 println!(
284 " Gates per layer: {:.2}",
285 self.metrics.total_gates as f64 / self.metrics.circuit_depth.max(1) as f64
286 );
287 println!();
288
289 if !self.gate_counts.is_empty() {
290 println!(" Gate breakdown:");
291 let mut sorted_gates: Vec<_> = self.gate_counts.iter().collect();
292 sorted_gates.sort_by_key(|(_, count)| std::cmp::Reverse(**count));
293 for (gate, count) in sorted_gates.iter().take(10) {
294 let percentage = (**count as f64 / self.metrics.total_gates as f64) * 100.0;
295 println!(" {:<10} {:>6} ({:>5.1}%)", gate, count, percentage);
296 }
297 println!();
298 }
299
300 println!("═══ Measurement Statistics ═══");
301 println!(" Total measurements: {}", self.metrics.measurements);
302 println!(" Qubits measured: {}", self.metrics.qubits_measured);
303 println!();
304
305 println!("═══ Memory Statistics ═══");
306 println!(
307 " Peak memory: {} MB",
308 self.metrics.peak_memory / (1024 * 1024)
309 );
310
311 if !self.memory_usage.is_empty() {
312 println!(" Memory by phase:");
313 for (phase, bytes) in &self.memory_usage {
314 println!(" {:<20} {:>8} MB", phase, bytes / (1024 * 1024));
315 }
316 println!();
317 }
318
319 println!("═══ Performance Metrics ═══");
320 let gates_per_sec = self.metrics.total_gates as f64 / self.duration.as_secs_f64();
321 println!(" Gates per second: {:.2}", gates_per_sec);
322 println!(
323 " Average gate time: {:.2} µs",
324 self.duration.as_micros() as f64 / self.metrics.total_gates.max(1) as f64
325 );
326 println!();
327
328 println!("═══ Recommendations ═══");
329 for (i, rec) in self.recommendations.iter().enumerate() {
330 println!(" {}. {}", i + 1, rec);
331 }
332 println!("╚══════════════════════════════════════════════════════════════╝");
333 }
334
335 pub fn to_json(&self) -> String {
337 format!(
338 r#"{{
339 "session_name": "{}",
340 "duration_ms": {},
341 "metrics": {{
342 "total_gates": {},
343 "circuit_depth": {},
344 "measurements": {},
345 "qubits_measured": {},
346 "peak_memory": {}
347 }},
348 "gate_counts": {:?},
349 "memory_usage": {:?},
350 "recommendations": {:?}
351}}"#,
352 self.session_name,
353 self.duration.as_millis(),
354 self.metrics.total_gates,
355 self.metrics.circuit_depth,
356 self.metrics.measurements,
357 self.metrics.qubits_measured,
358 self.metrics.peak_memory,
359 self.gate_counts,
360 self.memory_usage,
361 self.recommendations
362 )
363 }
364}
365
366#[cfg(test)]
367mod tests {
368 use super::*;
369
370 #[test]
371 fn test_profiler_creation() {
372 let profiler = QuantumProfiler::new();
373 assert_eq!(profiler.global_stats.total_sessions, 0);
374 }
375
376 #[test]
377 fn test_session_lifecycle() {
378 let mut profiler = QuantumProfiler::new();
379
380 let handle = profiler.start_session("test_circuit");
381 assert_eq!(handle.name, "test_circuit");
382
383 let report = profiler.end_session(handle).unwrap();
384 assert_eq!(report.session_name, "test_circuit");
385 assert_eq!(profiler.global_stats.total_sessions, 1);
386 }
387
388 #[test]
389 fn test_gate_recording() {
390 let mut profiler = QuantumProfiler::new();
391 let handle = profiler.start_session("test");
392
393 profiler.record_gate("test", "H").unwrap();
394 profiler.record_gate("test", "CNOT").unwrap();
395 profiler.record_gate("test", "H").unwrap();
396
397 let report = profiler.end_session(handle).unwrap();
398 assert_eq!(report.metrics.total_gates, 3);
399 assert_eq!(*report.gate_counts.get("H").unwrap(), 2);
400 assert_eq!(*report.gate_counts.get("CNOT").unwrap(), 1);
401 }
402
403 #[test]
404 fn test_measurement_recording() {
405 let mut profiler = QuantumProfiler::new();
406 let handle = profiler.start_session("test");
407
408 profiler.record_measurement("test", 5).unwrap();
409 profiler.record_measurement("test", 3).unwrap();
410
411 let report = profiler.end_session(handle).unwrap();
412 assert_eq!(report.metrics.measurements, 2);
413 assert_eq!(report.metrics.qubits_measured, 5);
414 }
415
416 #[test]
417 fn test_memory_recording() {
418 let mut profiler = QuantumProfiler::new();
419 let handle = profiler.start_session("test");
420
421 profiler
422 .record_memory("test", 1024 * 1024, "initialization")
423 .unwrap();
424 profiler
425 .record_memory("test", 2 * 1024 * 1024, "computation")
426 .unwrap();
427
428 let report = profiler.end_session(handle).unwrap();
429 assert_eq!(report.metrics.peak_memory, 2 * 1024 * 1024);
430 assert_eq!(report.memory_usage.len(), 2);
431 }
432
433 #[test]
434 fn test_recommendations() {
435 let mut profiler = QuantumProfiler::new();
436 let handle = profiler.start_session("deep_circuit");
437
438 for _ in 0..150 {
440 profiler.record_gate("deep_circuit", "H").unwrap();
441 }
442 profiler.record_depth("deep_circuit", 150).unwrap();
443
444 let report = profiler.end_session(handle).unwrap();
445 assert!(!report.recommendations.is_empty());
446 assert!(report
447 .recommendations
448 .iter()
449 .any(|r| r.contains("deep circuit") || r.contains("Deep circuit")));
450 }
451
452 #[test]
453 fn test_report_formatting() {
454 let mut profiler = QuantumProfiler::new();
455 let handle = profiler.start_session("test");
456
457 profiler.record_gate("test", "H").unwrap();
458 profiler.record_measurement("test", 1).unwrap();
459
460 let report = profiler.end_session(handle).unwrap();
461
462 report.print_detailed();
464
465 let json = report.to_json();
467 assert!(json.contains("session_name"));
468 assert!(json.contains("test"));
469 }
470
471 #[test]
472 fn test_global_statistics() {
473 let mut profiler = QuantumProfiler::new();
474
475 for i in 0..5 {
476 let handle = profiler.start_session(format!("session_{}", i));
477 profiler.end_session(handle).unwrap();
478 }
479
480 let stats = profiler.global_statistics();
481 assert_eq!(stats.total_sessions, 5);
482 assert!(stats.total_duration.as_nanos() > 0);
483 }
484}