quantrs2_circuit/optimization/
analysis.rs1use crate::builder::Circuit;
6use crate::optimization::gate_properties::get_gate_properties;
7use quantrs2_core::error::QuantRS2Result;
8use quantrs2_core::gate::GateOp;
9use quantrs2_core::qubit::QubitId;
10use std::collections::{HashMap, HashSet};
11
12#[derive(Debug, Clone)]
14pub struct CircuitMetrics {
15 pub gate_count: usize,
17 pub gate_types: HashMap<String, usize>,
19 pub depth: usize,
21 pub two_qubit_gates: usize,
23 pub num_qubits: usize,
25 pub critical_path: usize,
27 pub execution_time: f64,
29 pub total_error: f64,
31 pub gate_density: f64,
33 pub parallelism: f64,
35}
36
37impl CircuitMetrics {
38 pub fn improvement_from(&self, other: &CircuitMetrics) -> MetricImprovement {
40 MetricImprovement {
41 gate_count: Self::percent_change(other.gate_count as f64, self.gate_count as f64),
42 depth: Self::percent_change(other.depth as f64, self.depth as f64),
43 two_qubit_gates: Self::percent_change(
44 other.two_qubit_gates as f64,
45 self.two_qubit_gates as f64,
46 ),
47 execution_time: Self::percent_change(other.execution_time, self.execution_time),
48 total_error: Self::percent_change(other.total_error, self.total_error),
49 }
50 }
51
52 fn percent_change(old_val: f64, new_val: f64) -> f64 {
53 if old_val == 0.0 {
54 0.0
55 } else {
56 ((old_val - new_val) / old_val) * 100.0
57 }
58 }
59}
60
61#[derive(Debug, Clone)]
63pub struct MetricImprovement {
64 pub gate_count: f64,
65 pub depth: f64,
66 pub two_qubit_gates: f64,
67 pub execution_time: f64,
68 pub total_error: f64,
69}
70
71pub struct CircuitAnalyzer {
73 analyze_parallelism: bool,
74 analyze_critical_path: bool,
75}
76
77impl CircuitAnalyzer {
78 pub fn new() -> Self {
80 Self {
81 analyze_parallelism: true,
82 analyze_critical_path: true,
83 }
84 }
85
86 pub fn analyze<const N: usize>(&self, circuit: &Circuit<N>) -> QuantRS2Result<CircuitMetrics> {
88 let stats = circuit.get_stats();
89
90 let mut execution_time = 0.0;
92 for gate in circuit.gates() {
93 execution_time += self.estimate_gate_time(gate.as_ref());
94 }
95
96 let total_error = self.estimate_total_error(circuit);
98
99 let parallelism = if stats.depth > 0 {
101 stats.total_gates as f64 / stats.depth as f64
102 } else {
103 0.0
104 };
105
106 Ok(CircuitMetrics {
107 gate_count: stats.total_gates,
108 gate_types: stats.gate_counts,
109 depth: stats.depth,
110 two_qubit_gates: stats.two_qubit_gates,
111 num_qubits: stats.used_qubits,
112 critical_path: stats.depth, execution_time,
114 total_error,
115 gate_density: stats.gate_density,
116 parallelism,
117 })
118 }
119
120 fn estimate_gate_time(&self, gate: &dyn GateOp) -> f64 {
122 match gate.name() {
123 "H" | "X" | "Y" | "Z" | "S" | "T" | "RX" | "RY" | "RZ" => 50.0, "CNOT" | "CX" | "CZ" | "CY" | "SWAP" | "CRX" | "CRY" | "CRZ" => 200.0,
127 "Toffoli" | "Fredkin" | "CSWAP" => 500.0,
129 "measure" => 1000.0,
131 _ => 100.0,
133 }
134 }
135
136 fn estimate_total_error<const N: usize>(&self, circuit: &Circuit<N>) -> f64 {
138 let mut total_error = 0.0;
139
140 for gate in circuit.gates() {
141 total_error += self.estimate_gate_error(gate.as_ref());
142 }
143
144 let stats = circuit.get_stats();
146 let coherence_error = stats.depth as f64 * 0.001; total_error + coherence_error
149 }
150
151 fn estimate_gate_error(&self, gate: &dyn GateOp) -> f64 {
153 match gate.name() {
154 "H" | "X" | "Y" | "Z" | "S" | "T" => 0.0001,
156 "RX" | "RY" | "RZ" => 0.0005,
158 "CNOT" | "CX" | "CZ" | "CY" | "SWAP" => 0.01,
160 "CRX" | "CRY" | "CRZ" => 0.015,
162 "Toffoli" | "Fredkin" | "CSWAP" => 0.05,
164 "measure" => 0.02,
166 _ => 0.01,
168 }
169 }
170
171 pub fn analyze_gates(&self, gates: &[Box<dyn GateOp>], num_qubits: usize) -> CircuitMetrics {
173 let mut gate_types = HashMap::new();
174 let mut two_qubit_gates = 0;
175 let mut execution_time = 0.0;
176 let mut total_error = 0.0;
177
178 for gate in gates {
180 let gate_name = gate.name().to_string();
181 *gate_types.entry(gate_name).or_insert(0) += 1;
182
183 if gate.num_qubits() == 2 {
184 two_qubit_gates += 1;
185 }
186
187 let props = get_gate_properties(gate.as_ref());
188 execution_time += props.cost.duration_ns;
189 total_error += props.error.total_error();
190 }
191
192 let depth = if self.analyze_critical_path {
193 self.calculate_depth(gates)
194 } else {
195 gates.len()
196 };
197
198 let critical_path = if self.analyze_critical_path {
199 self.calculate_critical_path(gates)
200 } else {
201 depth
202 };
203
204 let parallelism = if self.analyze_parallelism && depth > 0 {
205 gates.len() as f64 / depth as f64
206 } else {
207 1.0
208 };
209
210 CircuitMetrics {
211 gate_count: gates.len(),
212 gate_types,
213 depth,
214 two_qubit_gates,
215 num_qubits,
216 critical_path,
217 execution_time,
218 total_error,
219 gate_density: gates.len() as f64 / num_qubits as f64,
220 parallelism,
221 }
222 }
223
224 fn calculate_depth(&self, gates: &[Box<dyn GateOp>]) -> usize {
226 let mut qubit_depths: HashMap<u32, usize> = HashMap::new();
227 let mut max_depth = 0;
228
229 for gate in gates {
230 let gate_qubits = gate.qubits();
231
232 let current_depth = gate_qubits
234 .iter()
235 .map(|q| qubit_depths.get(&q.id()).copied().unwrap_or(0))
236 .max()
237 .unwrap_or(0);
238
239 let new_depth = current_depth + 1;
241 for qubit in gate_qubits {
242 qubit_depths.insert(qubit.id(), new_depth);
243 }
244
245 max_depth = max_depth.max(new_depth);
246 }
247
248 max_depth
249 }
250
251 fn calculate_critical_path(&self, gates: &[Box<dyn GateOp>]) -> usize {
253 let mut dependencies: Vec<HashSet<usize>> = vec![HashSet::new(); gates.len()];
255 let mut qubit_last_gate: HashMap<u32, usize> = HashMap::new();
256
257 for (i, gate) in gates.iter().enumerate() {
258 for qubit in gate.qubits() {
259 if let Some(&prev_gate) = qubit_last_gate.get(&qubit.id()) {
260 dependencies[i].insert(prev_gate);
261 }
262 qubit_last_gate.insert(qubit.id(), i);
263 }
264 }
265
266 let mut path_lengths = vec![0; gates.len()];
268 let mut max_path = 0;
269
270 for i in 0..gates.len() {
271 let max_dep_length = dependencies[i]
272 .iter()
273 .map(|&j| path_lengths[j])
274 .max()
275 .unwrap_or(0);
276
277 path_lengths[i] = max_dep_length + 1;
278 max_path = max_path.max(path_lengths[i]);
279 }
280
281 max_path
282 }
283}
284
285impl Default for CircuitAnalyzer {
286 fn default() -> Self {
287 Self::new()
288 }
289}
290
291#[derive(Debug)]
293pub struct OptimizationReport {
294 pub initial_metrics: CircuitMetrics,
296 pub final_metrics: CircuitMetrics,
298 pub applied_passes: Vec<String>,
300}
301
302impl OptimizationReport {
303 pub fn improvement(&self) -> MetricImprovement {
305 self.final_metrics.improvement_from(&self.initial_metrics)
306 }
307
308 pub fn print_summary(&self) {
310 println!("=== Circuit Optimization Report ===");
311 println!();
312 println!("Initial Metrics:");
313 println!(" Gate count: {}", self.initial_metrics.gate_count);
314 println!(" Depth: {}", self.initial_metrics.depth);
315 println!(
316 " Two-qubit gates: {}",
317 self.initial_metrics.two_qubit_gates
318 );
319 println!(
320 " Execution time: {:.2} ns",
321 self.initial_metrics.execution_time
322 );
323 println!(" Total error: {:.6}", self.initial_metrics.total_error);
324 println!();
325 println!("Final Metrics:");
326 println!(" Gate count: {}", self.final_metrics.gate_count);
327 println!(" Depth: {}", self.final_metrics.depth);
328 println!(" Two-qubit gates: {}", self.final_metrics.two_qubit_gates);
329 println!(
330 " Execution time: {:.2} ns",
331 self.final_metrics.execution_time
332 );
333 println!(" Total error: {:.6}", self.final_metrics.total_error);
334 println!();
335 println!("Improvements:");
336 let improvement = self.improvement();
337 println!(" Gate count: {:.1}%", improvement.gate_count);
338 println!(" Depth: {:.1}%", improvement.depth);
339 println!(" Two-qubit gates: {:.1}%", improvement.two_qubit_gates);
340 println!(" Execution time: {:.1}%", improvement.execution_time);
341 println!(" Total error: {:.1}%", improvement.total_error);
342 println!();
343 println!("Applied Passes:");
344 for pass in &self.applied_passes {
345 println!(" - {}", pass);
346 }
347 }
348
349 pub fn detailed_report(&self) -> String {
351 let mut report = String::new();
352
353 report.push_str("=== Detailed Circuit Optimization Report ===\n\n");
354
355 report.push_str("Gate Type Breakdown:\n");
357 report.push_str("Initial:\n");
358 for (gate_type, count) in &self.initial_metrics.gate_types {
359 report.push_str(&format!(" {}: {}\n", gate_type, count));
360 }
361 report.push_str("Final:\n");
362 for (gate_type, count) in &self.final_metrics.gate_types {
363 report.push_str(&format!(" {}: {}\n", gate_type, count));
364 }
365
366 report.push_str(&format!("\nGate Density:\n"));
368 report.push_str(&format!(
369 " Initial: {:.2} gates/qubit\n",
370 self.initial_metrics.gate_density
371 ));
372 report.push_str(&format!(
373 " Final: {:.2} gates/qubit\n",
374 self.final_metrics.gate_density
375 ));
376
377 report.push_str(&format!("\nParallelism Factor:\n"));
378 report.push_str(&format!(
379 " Initial: {:.2}\n",
380 self.initial_metrics.parallelism
381 ));
382 report.push_str(&format!(" Final: {:.2}\n", self.final_metrics.parallelism));
383
384 report
385 }
386}