1use quantrs2_circuit::builder::Circuit;
7use quantrs2_core::{
8 error::{QuantRS2Error, QuantRS2Result},
9 gate::{multi::*, single::*, GateOp},
10 qubit::QubitId,
11};
12use scirs2_core::Complex64;
13use std::collections::{HashMap, HashSet, VecDeque};
14
15#[derive(Debug, Clone)]
17pub struct OptimizationConfig {
18 pub enable_gate_fusion: bool,
20 pub enable_redundant_elimination: bool,
22 pub enable_commutation_reordering: bool,
24 pub enable_single_qubit_optimization: bool,
26 pub enable_two_qubit_optimization: bool,
28 pub max_passes: usize,
30 pub enable_depth_reduction: bool,
32}
33
34impl Default for OptimizationConfig {
35 fn default() -> Self {
36 Self {
37 enable_gate_fusion: true,
38 enable_redundant_elimination: true,
39 enable_commutation_reordering: true,
40 enable_single_qubit_optimization: true,
41 enable_two_qubit_optimization: true,
42 max_passes: 3,
43 enable_depth_reduction: true,
44 }
45 }
46}
47
48#[derive(Debug)]
50pub struct CircuitOptimizer {
51 config: OptimizationConfig,
52 statistics: OptimizationStatistics,
53}
54
55#[derive(Debug, Default, Clone)]
57pub struct OptimizationStatistics {
58 pub original_gate_count: usize,
60 pub optimized_gate_count: usize,
62 pub original_depth: usize,
64 pub optimized_depth: usize,
66 pub redundant_gates_eliminated: usize,
68 pub gates_fused: usize,
70 pub gates_reordered: usize,
72 pub passes_performed: usize,
74 pub optimization_time_ns: u128,
76}
77
78#[derive(Debug, Clone)]
80struct DependencyGraph {
81 dependencies: HashMap<usize, Vec<usize>>,
83 gate_info: Vec<GateInfo>,
85 qubit_usage: HashMap<QubitId, Vec<usize>>,
87}
88
89#[derive(Debug, Clone)]
91struct GateInfo {
92 position: usize,
94 gate_type: String,
96 qubits: Vec<QubitId>,
98 optimized_away: bool,
100 fused_with: Vec<usize>,
102}
103
104#[derive(Debug, Clone)]
106struct SingleQubitFusion {
107 gates: Vec<usize>,
109 qubit: QubitId,
111 fused_matrix: [[Complex64; 2]; 2],
113}
114
115#[derive(Debug, Clone)]
117pub struct OptimizationResult {
118 pub success: bool,
120 pub gates_eliminated: usize,
122 pub gates_modified: usize,
124 pub depth_improvement: i32,
126 pub description: String,
128}
129
130impl CircuitOptimizer {
131 #[must_use]
133 pub fn new() -> Self {
134 Self {
135 config: OptimizationConfig::default(),
136 statistics: OptimizationStatistics::default(),
137 }
138 }
139
140 #[must_use]
142 pub fn with_config(config: OptimizationConfig) -> Self {
143 Self {
144 config,
145 statistics: OptimizationStatistics::default(),
146 }
147 }
148
149 pub fn optimize<const N: usize>(&mut self, circuit: &Circuit<N>) -> QuantRS2Result<Circuit<N>> {
151 let start_time = std::time::Instant::now();
152
153 self.statistics.original_gate_count = circuit.gates().len();
155 self.statistics.original_depth = self.calculate_circuit_depth(circuit);
156
157 let mut dependency_graph = self.build_dependency_graph(circuit)?;
159
160 let mut optimized_circuit = circuit.clone();
162
163 for pass in 0..self.config.max_passes {
164 let mut pass_improved = false;
165
166 if self.config.enable_redundant_elimination {
168 let result = self.eliminate_redundant_gates(&optimized_circuit)?;
169 if result.success {
170 pass_improved = true;
171 self.statistics.redundant_gates_eliminated += result.gates_eliminated;
172 }
173 }
174
175 if self.config.enable_single_qubit_optimization {
177 let result = self.fuse_single_qubit_gates(&optimized_circuit)?;
178 if result.success {
179 pass_improved = true;
180 self.statistics.gates_fused += result.gates_modified;
181 }
182 }
183
184 if self.config.enable_commutation_reordering {
186 let result = self.reorder_commuting_gates(&optimized_circuit)?;
187 if result.success {
188 pass_improved = true;
189 self.statistics.gates_reordered += result.gates_modified;
190 }
191 }
192
193 if self.config.enable_two_qubit_optimization {
195 let result = self.optimize_two_qubit_gates(&optimized_circuit)?;
196 if result.success {
197 pass_improved = true;
198 }
199 }
200
201 if self.config.enable_depth_reduction {
203 let result = self.reduce_circuit_depth(&optimized_circuit)?;
204 if result.success {
205 pass_improved = true;
206 }
207 }
208
209 self.statistics.passes_performed = pass + 1;
210
211 if !pass_improved {
213 break;
214 }
215 }
216
217 self.statistics.optimized_gate_count = optimized_circuit.gates().len();
219 self.statistics.optimized_depth = self.calculate_circuit_depth(&optimized_circuit);
220 self.statistics.optimization_time_ns = start_time.elapsed().as_nanos();
221
222 Ok(optimized_circuit)
223 }
224
225 #[must_use]
227 pub const fn get_statistics(&self) -> &OptimizationStatistics {
228 &self.statistics
229 }
230
231 pub fn reset_statistics(&mut self) {
233 self.statistics = OptimizationStatistics::default();
234 }
235
236 fn build_dependency_graph<const N: usize>(
238 &self,
239 circuit: &Circuit<N>,
240 ) -> QuantRS2Result<DependencyGraph> {
241 let mut graph = DependencyGraph {
242 dependencies: HashMap::new(),
243 gate_info: Vec::new(),
244 qubit_usage: HashMap::new(),
245 };
246
247 for (pos, gate) in circuit.gates().iter().enumerate() {
249 let qubits = gate.qubits();
250 let gate_info = GateInfo {
251 position: pos,
252 gate_type: gate.name().to_string(),
253 qubits: qubits.clone(),
254 optimized_away: false,
255 fused_with: Vec::new(),
256 };
257
258 graph.gate_info.push(gate_info);
259
260 for &qubit in &qubits {
262 graph.qubit_usage.entry(qubit).or_default().push(pos);
263 }
264
265 let mut deps = Vec::new();
267 for &qubit in &qubits {
268 if let Some(previous_uses) = graph.qubit_usage.get(&qubit) {
269 for &prev_pos in previous_uses {
270 if prev_pos < pos {
271 deps.push(prev_pos);
272 }
273 }
274 }
275 }
276
277 graph.dependencies.insert(pos, deps);
278 }
279
280 Ok(graph)
281 }
282
283 fn calculate_circuit_depth<const N: usize>(&self, circuit: &Circuit<N>) -> usize {
285 let mut qubit_depths = HashMap::new();
286 let mut max_depth = 0;
287
288 for gate in circuit.gates() {
289 let qubits = gate.qubits();
290
291 let input_depth = qubits
293 .iter()
294 .map(|&q| qubit_depths.get(&q).copied().unwrap_or(0))
295 .max()
296 .unwrap_or(0);
297
298 let new_depth = input_depth + 1;
299
300 for &qubit in &qubits {
302 qubit_depths.insert(qubit, new_depth);
303 }
304
305 max_depth = max_depth.max(new_depth);
306 }
307
308 max_depth
309 }
310
311 fn eliminate_redundant_gates<const N: usize>(
313 &self,
314 circuit: &Circuit<N>,
315 ) -> QuantRS2Result<OptimizationResult> {
316 let gates = circuit.gates();
318 let mut redundant_pairs = Vec::new();
319
320 for i in 0..gates.len().saturating_sub(1) {
322 let gate1 = &gates[i];
323 let gate2 = &gates[i + 1];
324
325 if gate1.name() == gate2.name() && gate1.qubits() == gate2.qubits() {
327 match gate1.name() {
329 "H" | "X" | "Y" | "Z" | "CNOT" | "SWAP" => {
330 redundant_pairs.push((i, i + 1));
331 }
332 _ => {}
333 }
334 }
335 }
336
337 let gates_eliminated = redundant_pairs.len() * 2; Ok(OptimizationResult {
342 success: gates_eliminated > 0,
343 gates_eliminated,
344 gates_modified: redundant_pairs.len(),
345 depth_improvement: redundant_pairs.len() as i32, description: format!(
347 "Found {} redundant gate pairs for elimination",
348 redundant_pairs.len()
349 ),
350 })
351 }
352
353 fn fuse_single_qubit_gates<const N: usize>(
355 &self,
356 circuit: &Circuit<N>,
357 ) -> QuantRS2Result<OptimizationResult> {
358 let fusion_candidates = self.find_single_qubit_fusion_candidates(circuit)?;
360
361 let mut gates_fused = 0;
363 let candidates_count = fusion_candidates.len();
364 for candidate in &fusion_candidates {
365 if candidate.gates.len() > 1 {
366 gates_fused += candidate.gates.len() - 1; }
368 }
369
370 Ok(OptimizationResult {
371 success: gates_fused > 0,
372 gates_eliminated: gates_fused,
373 gates_modified: candidates_count,
374 depth_improvement: 0,
375 description: format!("Fused {candidates_count} single-qubit gate sequences"),
376 })
377 }
378
379 fn find_single_qubit_fusion_candidates<const N: usize>(
381 &self,
382 circuit: &Circuit<N>,
383 ) -> QuantRS2Result<Vec<SingleQubitFusion>> {
384 let mut candidates = Vec::new();
385 let mut qubit_gate_sequences: HashMap<QubitId, Vec<usize>> = HashMap::new();
386
387 for (pos, gate) in circuit.gates().iter().enumerate() {
389 let qubits = gate.qubits();
390 if qubits.len() == 1 {
391 let qubit = qubits[0];
392 qubit_gate_sequences.entry(qubit).or_default().push(pos);
393 } else {
394 for &qubit in &qubits {
396 if let Some(sequence) = qubit_gate_sequences.get(&qubit) {
397 if sequence.len() > 1 {
398 candidates
399 .push(self.create_fusion_candidate(circuit, sequence, qubit)?);
400 }
401 }
402 qubit_gate_sequences.insert(qubit, Vec::new());
403 }
404 }
405 }
406
407 for (qubit, sequence) in qubit_gate_sequences {
409 if sequence.len() > 1 {
410 candidates.push(self.create_fusion_candidate(circuit, &sequence, qubit)?);
411 }
412 }
413
414 Ok(candidates)
415 }
416
417 fn create_fusion_candidate<const N: usize>(
419 &self,
420 circuit: &Circuit<N>,
421 gate_positions: &[usize],
422 qubit: QubitId,
423 ) -> QuantRS2Result<SingleQubitFusion> {
424 let identity_matrix = [
427 [Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)],
428 [Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)],
429 ];
430
431 Ok(SingleQubitFusion {
432 gates: gate_positions.to_vec(),
433 qubit,
434 fused_matrix: identity_matrix,
435 })
436 }
437
438 fn reorder_commuting_gates<const N: usize>(
440 &self,
441 circuit: &Circuit<N>,
442 ) -> QuantRS2Result<OptimizationResult> {
443 let gates = circuit.gates();
445 let mut reordering_opportunities = 0;
446
447 for i in 0..gates.len().saturating_sub(1) {
449 let gate1 = &gates[i];
450 let gate2 = &gates[i + 1];
451
452 let qubits1: std::collections::HashSet<_> = gate1.qubits().into_iter().collect();
454 let qubits2: std::collections::HashSet<_> = gate2.qubits().into_iter().collect();
455
456 if qubits1.is_disjoint(&qubits2) {
457 reordering_opportunities += 1;
458 }
459
460 match (gate1.name(), gate2.name()) {
462 (
464 "H" | "X" | "Y" | "Z" | "S" | "T" | "RX" | "RY" | "RZ",
465 "H" | "X" | "Y" | "Z" | "S" | "T" | "RX" | "RY" | "RZ",
466 ) if qubits1.is_disjoint(&qubits2) => {
467 reordering_opportunities += 1;
468 }
469 ("CNOT", "CNOT") if qubits1.is_disjoint(&qubits2) => {
471 reordering_opportunities += 1;
472 }
473 _ => {}
474 }
475 }
476
477 Ok(OptimizationResult {
478 success: reordering_opportunities > 0,
479 gates_eliminated: 0,
480 gates_modified: reordering_opportunities,
481 depth_improvement: (reordering_opportunities / 2) as i32, description: format!(
483 "Found {reordering_opportunities} gate reordering opportunities for parallelization"
484 ),
485 })
486 }
487
488 fn optimize_two_qubit_gates<const N: usize>(
490 &self,
491 circuit: &Circuit<N>,
492 ) -> QuantRS2Result<OptimizationResult> {
493 let gates = circuit.gates();
495 let mut optimization_count = 0;
496
497 for i in 0..gates.len().saturating_sub(2) {
499 if gates[i].name() == "CNOT"
500 && gates[i + 1].name() == "CNOT"
501 && gates[i + 2].name() == "CNOT"
502 {
503 let qubits1 = gates[i].qubits();
504 let qubits2 = gates[i + 1].qubits();
505 let qubits3 = gates[i + 2].qubits();
506
507 if qubits1.len() == 2
509 && qubits2.len() == 2
510 && qubits3.len() == 2
511 && qubits1 == qubits3
512 && qubits1[1] == qubits2[0]
513 {
514 optimization_count += 1;
516 }
517 }
518 }
519
520 for i in 0..gates.len().saturating_sub(2) {
522 if gates[i].name() == "CNOT"
523 && gates[i + 1].name() == "CNOT"
524 && gates[i + 2].name() == "CNOT"
525 {
526 let qubits1 = gates[i].qubits();
527 let qubits2 = gates[i + 1].qubits();
528 let qubits3 = gates[i + 2].qubits();
529
530 if qubits1.len() == 2
532 && qubits2.len() == 2
533 && qubits3.len() == 2
534 && qubits1[0] == qubits3[0]
535 && qubits1[1] == qubits3[1]
536 && qubits1[0] == qubits2[1]
537 && qubits1[1] == qubits2[0]
538 {
539 optimization_count += 1;
540 }
541 }
542 }
543
544 Ok(OptimizationResult {
545 success: optimization_count > 0,
546 gates_eliminated: optimization_count, gates_modified: optimization_count,
548 depth_improvement: optimization_count as i32,
549 description: format!(
550 "Found {optimization_count} two-qubit gate optimization opportunities"
551 ),
552 })
553 }
554
555 fn reduce_circuit_depth<const N: usize>(
557 &self,
558 circuit: &Circuit<N>,
559 ) -> QuantRS2Result<OptimizationResult> {
560 let original_depth = self.calculate_circuit_depth(circuit);
562
563 let new_depth = original_depth; Ok(OptimizationResult {
567 success: false,
568 gates_eliminated: 0,
569 gates_modified: 0,
570 depth_improvement: (original_depth as i32) - (new_depth as i32),
571 description: "Circuit depth reduction".to_string(),
572 })
573 }
574}
575
576impl Default for CircuitOptimizer {
577 fn default() -> Self {
578 Self::new()
579 }
580}
581
582impl OptimizationStatistics {
583 #[must_use]
585 pub fn gate_count_reduction(&self) -> f64 {
586 if self.original_gate_count == 0 {
587 0.0
588 } else {
589 (self.original_gate_count as f64 - self.optimized_gate_count as f64)
590 / self.original_gate_count as f64
591 * 100.0
592 }
593 }
594
595 #[must_use]
597 pub fn depth_reduction(&self) -> f64 {
598 if self.original_depth == 0 {
599 0.0
600 } else {
601 (self.original_depth as f64 - self.optimized_depth as f64) / self.original_depth as f64
602 * 100.0
603 }
604 }
605
606 #[must_use]
608 pub fn generate_report(&self) -> String {
609 format!(
610 r"
611📊 Circuit Optimization Report
612==============================
613
614📈 Gate Count Optimization
615 • Original Gates: {}
616 • Optimized Gates: {}
617 • Reduction: {:.1}%
618
619🔍 Circuit Depth Optimization
620 • Original Depth: {}
621 • Optimized Depth: {}
622 • Reduction: {:.1}%
623
624⚡ Optimization Details
625 • Redundant Gates Eliminated: {}
626 • Gates Fused: {}
627 • Gates Reordered: {}
628 • Optimization Passes: {}
629 • Optimization Time: {:.2}ms
630
631✅ Summary
632Circuit optimization {} with {:.1}% gate reduction and {:.1}% depth reduction.
633",
634 self.original_gate_count,
635 self.optimized_gate_count,
636 self.gate_count_reduction(),
637 self.original_depth,
638 self.optimized_depth,
639 self.depth_reduction(),
640 self.redundant_gates_eliminated,
641 self.gates_fused,
642 self.gates_reordered,
643 self.passes_performed,
644 self.optimization_time_ns as f64 / 1_000_000.0,
645 if self.gate_count_reduction() > 0.0 || self.depth_reduction() > 0.0 {
646 "successful"
647 } else {
648 "completed"
649 },
650 self.gate_count_reduction(),
651 self.depth_reduction()
652 )
653 }
654}
655
656pub fn optimize_circuit<const N: usize>(circuit: &Circuit<N>) -> QuantRS2Result<Circuit<N>> {
658 let mut optimizer = CircuitOptimizer::new();
659 optimizer.optimize(circuit)
660}
661
662pub fn optimize_circuit_with_config<const N: usize>(
664 circuit: &Circuit<N>,
665 config: OptimizationConfig,
666) -> QuantRS2Result<(Circuit<N>, OptimizationStatistics)> {
667 let mut optimizer = CircuitOptimizer::with_config(config);
668 let optimized_circuit = optimizer.optimize(circuit)?;
669 Ok((optimized_circuit, optimizer.statistics.clone()))
670}
671
672#[cfg(test)]
673mod tests {
674 use super::*;
675
676 #[test]
677 fn test_optimizer_creation() {
678 let optimizer = CircuitOptimizer::new();
679 assert!(optimizer.config.enable_gate_fusion);
680 assert!(optimizer.config.enable_redundant_elimination);
681 }
682
683 #[test]
684 fn test_optimization_config() {
685 let mut config = OptimizationConfig::default();
686 config.enable_gate_fusion = false;
687 config.max_passes = 5;
688
689 let optimizer = CircuitOptimizer::with_config(config);
690 assert!(!optimizer.config.enable_gate_fusion);
691 assert_eq!(optimizer.config.max_passes, 5);
692 }
693
694 #[test]
695 fn test_statistics_calculations() {
696 let stats = OptimizationStatistics {
697 original_gate_count: 100,
698 optimized_gate_count: 80,
699 original_depth: 50,
700 optimized_depth: 40,
701 ..Default::default()
702 };
703
704 assert_eq!(stats.gate_count_reduction(), 20.0);
705 assert_eq!(stats.depth_reduction(), 20.0);
706 }
707
708 #[test]
709 fn test_report_generation() {
710 let stats = OptimizationStatistics {
711 original_gate_count: 100,
712 optimized_gate_count: 80,
713 original_depth: 50,
714 optimized_depth: 40,
715 redundant_gates_eliminated: 10,
716 gates_fused: 5,
717 gates_reordered: 3,
718 passes_performed: 2,
719 optimization_time_ns: 1_000_000,
720 };
721
722 let report = stats.generate_report();
723 assert!(report.contains("20.0%"));
724 assert!(report.contains("100"));
725 assert!(report.contains("80"));
726 }
727}