1use scirs2_core::Complex64;
7use quantrs2_circuit::builder::Circuit;
8use quantrs2_core::{
9 error::{QuantRS2Error, QuantRS2Result},
10 gate::{multi::*, single::*, GateOp},
11 qubit::QubitId,
12};
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 pub fn new() -> Self {
133 Self {
134 config: OptimizationConfig::default(),
135 statistics: OptimizationStatistics::default(),
136 }
137 }
138
139 pub fn with_config(config: OptimizationConfig) -> Self {
141 Self {
142 config,
143 statistics: OptimizationStatistics::default(),
144 }
145 }
146
147 pub fn optimize<const N: usize>(&mut self, circuit: &Circuit<N>) -> QuantRS2Result<Circuit<N>> {
149 let start_time = std::time::Instant::now();
150
151 self.statistics.original_gate_count = circuit.gates().len();
153 self.statistics.original_depth = self.calculate_circuit_depth(circuit);
154
155 let mut dependency_graph = self.build_dependency_graph(circuit)?;
157
158 let mut optimized_circuit = circuit.clone();
160
161 for pass in 0..self.config.max_passes {
162 let mut pass_improved = false;
163
164 if self.config.enable_redundant_elimination {
166 let result = self.eliminate_redundant_gates(&mut optimized_circuit)?;
167 if result.success {
168 pass_improved = true;
169 self.statistics.redundant_gates_eliminated += result.gates_eliminated;
170 }
171 }
172
173 if self.config.enable_single_qubit_optimization {
175 let result = self.fuse_single_qubit_gates(&mut optimized_circuit)?;
176 if result.success {
177 pass_improved = true;
178 self.statistics.gates_fused += result.gates_modified;
179 }
180 }
181
182 if self.config.enable_commutation_reordering {
184 let result = self.reorder_commuting_gates(&mut optimized_circuit)?;
185 if result.success {
186 pass_improved = true;
187 self.statistics.gates_reordered += result.gates_modified;
188 }
189 }
190
191 if self.config.enable_two_qubit_optimization {
193 let result = self.optimize_two_qubit_gates(&mut optimized_circuit)?;
194 if result.success {
195 pass_improved = true;
196 }
197 }
198
199 if self.config.enable_depth_reduction {
201 let result = self.reduce_circuit_depth(&mut optimized_circuit)?;
202 if result.success {
203 pass_improved = true;
204 }
205 }
206
207 self.statistics.passes_performed = pass + 1;
208
209 if !pass_improved {
211 break;
212 }
213 }
214
215 self.statistics.optimized_gate_count = optimized_circuit.gates().len();
217 self.statistics.optimized_depth = self.calculate_circuit_depth(&optimized_circuit);
218 self.statistics.optimization_time_ns = start_time.elapsed().as_nanos();
219
220 Ok(optimized_circuit)
221 }
222
223 pub fn get_statistics(&self) -> &OptimizationStatistics {
225 &self.statistics
226 }
227
228 pub fn reset_statistics(&mut self) {
230 self.statistics = OptimizationStatistics::default();
231 }
232
233 fn build_dependency_graph<const N: usize>(
235 &self,
236 circuit: &Circuit<N>,
237 ) -> QuantRS2Result<DependencyGraph> {
238 let mut graph = DependencyGraph {
239 dependencies: HashMap::new(),
240 gate_info: Vec::new(),
241 qubit_usage: HashMap::new(),
242 };
243
244 for (pos, gate) in circuit.gates().iter().enumerate() {
246 let qubits = gate.qubits();
247 let gate_info = GateInfo {
248 position: pos,
249 gate_type: gate.name().to_string(),
250 qubits: qubits.clone(),
251 optimized_away: false,
252 fused_with: Vec::new(),
253 };
254
255 graph.gate_info.push(gate_info);
256
257 for &qubit in &qubits {
259 graph
260 .qubit_usage
261 .entry(qubit)
262 .or_insert_with(Vec::new)
263 .push(pos);
264 }
265
266 let mut deps = Vec::new();
268 for &qubit in &qubits {
269 if let Some(previous_uses) = graph.qubit_usage.get(&qubit) {
270 for &prev_pos in previous_uses {
271 if prev_pos < pos {
272 deps.push(prev_pos);
273 }
274 }
275 }
276 }
277
278 graph.dependencies.insert(pos, deps);
279 }
280
281 Ok(graph)
282 }
283
284 fn calculate_circuit_depth<const N: usize>(&self, circuit: &Circuit<N>) -> usize {
286 let mut qubit_depths = HashMap::new();
287 let mut max_depth = 0;
288
289 for gate in circuit.gates() {
290 let qubits = gate.qubits();
291
292 let input_depth = qubits
294 .iter()
295 .map(|&q| qubit_depths.get(&q).copied().unwrap_or(0))
296 .max()
297 .unwrap_or(0);
298
299 let new_depth = input_depth + 1;
300
301 for &qubit in &qubits {
303 qubit_depths.insert(qubit, new_depth);
304 }
305
306 max_depth = max_depth.max(new_depth);
307 }
308
309 max_depth
310 }
311
312 fn eliminate_redundant_gates<const N: usize>(
314 &self,
315 circuit: &mut Circuit<N>,
316 ) -> QuantRS2Result<OptimizationResult> {
317 let gates = circuit.gates();
319 let mut redundant_pairs = Vec::new();
320
321 for i in 0..gates.len().saturating_sub(1) {
323 let gate1 = &gates[i];
324 let gate2 = &gates[i + 1];
325
326 if gate1.name() == gate2.name() && gate1.qubits() == gate2.qubits() {
328 match gate1.name() {
330 "H" | "X" | "Y" | "Z" | "CNOT" | "SWAP" => {
331 redundant_pairs.push((i, i + 1));
332 }
333 _ => {}
334 }
335 }
336 }
337
338 let gates_eliminated = redundant_pairs.len() * 2; Ok(OptimizationResult {
343 success: gates_eliminated > 0,
344 gates_eliminated,
345 gates_modified: redundant_pairs.len(),
346 depth_improvement: redundant_pairs.len() as i32, description: format!(
348 "Found {} redundant gate pairs for elimination",
349 redundant_pairs.len()
350 ),
351 })
352 }
353
354 fn fuse_single_qubit_gates<const N: usize>(
356 &self,
357 circuit: &mut Circuit<N>,
358 ) -> QuantRS2Result<OptimizationResult> {
359 let fusion_candidates = self.find_single_qubit_fusion_candidates(circuit)?;
361
362 let mut gates_fused = 0;
364 let candidates_count = fusion_candidates.len();
365 for candidate in &fusion_candidates {
366 if candidate.gates.len() > 1 {
367 gates_fused += candidate.gates.len() - 1; }
369 }
370
371 Ok(OptimizationResult {
372 success: gates_fused > 0,
373 gates_eliminated: gates_fused,
374 gates_modified: candidates_count,
375 depth_improvement: 0,
376 description: format!("Fused {} single-qubit gate sequences", candidates_count),
377 })
378 }
379
380 fn find_single_qubit_fusion_candidates<const N: usize>(
382 &self,
383 circuit: &Circuit<N>,
384 ) -> QuantRS2Result<Vec<SingleQubitFusion>> {
385 let mut candidates = Vec::new();
386 let mut qubit_gate_sequences: HashMap<QubitId, Vec<usize>> = HashMap::new();
387
388 for (pos, gate) in circuit.gates().iter().enumerate() {
390 let qubits = gate.qubits();
391 if qubits.len() == 1 {
392 let qubit = qubits[0];
393 qubit_gate_sequences
394 .entry(qubit)
395 .or_insert_with(Vec::new)
396 .push(pos);
397 } else {
398 for &qubit in &qubits {
400 if let Some(sequence) = qubit_gate_sequences.get(&qubit) {
401 if sequence.len() > 1 {
402 candidates
403 .push(self.create_fusion_candidate(circuit, sequence, qubit)?);
404 }
405 }
406 qubit_gate_sequences.insert(qubit, Vec::new());
407 }
408 }
409 }
410
411 for (qubit, sequence) in qubit_gate_sequences {
413 if sequence.len() > 1 {
414 candidates.push(self.create_fusion_candidate(circuit, &sequence, qubit)?);
415 }
416 }
417
418 Ok(candidates)
419 }
420
421 fn create_fusion_candidate<const N: usize>(
423 &self,
424 circuit: &Circuit<N>,
425 gate_positions: &[usize],
426 qubit: QubitId,
427 ) -> QuantRS2Result<SingleQubitFusion> {
428 let identity_matrix = [
431 [Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)],
432 [Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)],
433 ];
434
435 Ok(SingleQubitFusion {
436 gates: gate_positions.to_vec(),
437 qubit,
438 fused_matrix: identity_matrix,
439 })
440 }
441
442 fn reorder_commuting_gates<const N: usize>(
444 &self,
445 circuit: &mut Circuit<N>,
446 ) -> QuantRS2Result<OptimizationResult> {
447 let gates = circuit.gates();
449 let mut reordering_opportunities = 0;
450
451 for i in 0..gates.len().saturating_sub(1) {
453 let gate1 = &gates[i];
454 let gate2 = &gates[i + 1];
455
456 let qubits1: std::collections::HashSet<_> = gate1.qubits().into_iter().collect();
458 let qubits2: std::collections::HashSet<_> = gate2.qubits().into_iter().collect();
459
460 if qubits1.is_disjoint(&qubits2) {
461 reordering_opportunities += 1;
462 }
463
464 match (gate1.name(), gate2.name()) {
466 (
468 "H" | "X" | "Y" | "Z" | "S" | "T" | "RX" | "RY" | "RZ",
469 "H" | "X" | "Y" | "Z" | "S" | "T" | "RX" | "RY" | "RZ",
470 ) if qubits1.is_disjoint(&qubits2) => {
471 reordering_opportunities += 1;
472 }
473 ("CNOT", "CNOT") if qubits1.is_disjoint(&qubits2) => {
475 reordering_opportunities += 1;
476 }
477 _ => {}
478 }
479 }
480
481 Ok(OptimizationResult {
482 success: reordering_opportunities > 0,
483 gates_eliminated: 0,
484 gates_modified: reordering_opportunities,
485 depth_improvement: (reordering_opportunities / 2) as i32, description: format!(
487 "Found {} gate reordering opportunities for parallelization",
488 reordering_opportunities
489 ),
490 })
491 }
492
493 fn optimize_two_qubit_gates<const N: usize>(
495 &self,
496 circuit: &mut Circuit<N>,
497 ) -> QuantRS2Result<OptimizationResult> {
498 let gates = circuit.gates();
500 let mut optimization_count = 0;
501
502 for i in 0..gates.len().saturating_sub(2) {
504 if gates[i].name() == "CNOT"
505 && gates[i + 1].name() == "CNOT"
506 && gates[i + 2].name() == "CNOT"
507 {
508 let qubits1 = gates[i].qubits();
509 let qubits2 = gates[i + 1].qubits();
510 let qubits3 = gates[i + 2].qubits();
511
512 if qubits1.len() == 2
514 && qubits2.len() == 2
515 && qubits3.len() == 2
516 && qubits1 == qubits3
517 && qubits1[1] == qubits2[0]
518 {
519 optimization_count += 1;
521 }
522 }
523 }
524
525 for i in 0..gates.len().saturating_sub(2) {
527 if gates[i].name() == "CNOT"
528 && gates[i + 1].name() == "CNOT"
529 && gates[i + 2].name() == "CNOT"
530 {
531 let qubits1 = gates[i].qubits();
532 let qubits2 = gates[i + 1].qubits();
533 let qubits3 = gates[i + 2].qubits();
534
535 if qubits1.len() == 2
537 && qubits2.len() == 2
538 && qubits3.len() == 2
539 && qubits1[0] == qubits3[0]
540 && qubits1[1] == qubits3[1]
541 && qubits1[0] == qubits2[1]
542 && qubits1[1] == qubits2[0]
543 {
544 optimization_count += 1;
545 }
546 }
547 }
548
549 Ok(OptimizationResult {
550 success: optimization_count > 0,
551 gates_eliminated: optimization_count, gates_modified: optimization_count,
553 depth_improvement: optimization_count as i32,
554 description: format!(
555 "Found {} two-qubit gate optimization opportunities",
556 optimization_count
557 ),
558 })
559 }
560
561 fn reduce_circuit_depth<const N: usize>(
563 &self,
564 circuit: &mut Circuit<N>,
565 ) -> QuantRS2Result<OptimizationResult> {
566 let original_depth = self.calculate_circuit_depth(circuit);
568
569 let new_depth = original_depth; Ok(OptimizationResult {
573 success: false,
574 gates_eliminated: 0,
575 gates_modified: 0,
576 depth_improvement: (original_depth as i32) - (new_depth as i32),
577 description: "Circuit depth reduction".to_string(),
578 })
579 }
580}
581
582impl Default for CircuitOptimizer {
583 fn default() -> Self {
584 Self::new()
585 }
586}
587
588impl OptimizationStatistics {
589 pub fn gate_count_reduction(&self) -> f64 {
591 if self.original_gate_count == 0 {
592 0.0
593 } else {
594 (self.original_gate_count as f64 - self.optimized_gate_count as f64)
595 / self.original_gate_count as f64
596 * 100.0
597 }
598 }
599
600 pub fn depth_reduction(&self) -> f64 {
602 if self.original_depth == 0 {
603 0.0
604 } else {
605 (self.original_depth as f64 - self.optimized_depth as f64) / self.original_depth as f64
606 * 100.0
607 }
608 }
609
610 pub fn generate_report(&self) -> String {
612 format!(
613 r#"
614📊 Circuit Optimization Report
615==============================
616
617📈 Gate Count Optimization
618 • Original Gates: {}
619 • Optimized Gates: {}
620 • Reduction: {:.1}%
621
622🔍 Circuit Depth Optimization
623 • Original Depth: {}
624 • Optimized Depth: {}
625 • Reduction: {:.1}%
626
627⚡ Optimization Details
628 • Redundant Gates Eliminated: {}
629 • Gates Fused: {}
630 • Gates Reordered: {}
631 • Optimization Passes: {}
632 • Optimization Time: {:.2}ms
633
634✅ Summary
635Circuit optimization {} with {:.1}% gate reduction and {:.1}% depth reduction.
636"#,
637 self.original_gate_count,
638 self.optimized_gate_count,
639 self.gate_count_reduction(),
640 self.original_depth,
641 self.optimized_depth,
642 self.depth_reduction(),
643 self.redundant_gates_eliminated,
644 self.gates_fused,
645 self.gates_reordered,
646 self.passes_performed,
647 self.optimization_time_ns as f64 / 1_000_000.0,
648 if self.gate_count_reduction() > 0.0 || self.depth_reduction() > 0.0 {
649 "successful"
650 } else {
651 "completed"
652 },
653 self.gate_count_reduction(),
654 self.depth_reduction()
655 )
656 }
657}
658
659pub fn optimize_circuit<const N: usize>(circuit: &Circuit<N>) -> QuantRS2Result<Circuit<N>> {
661 let mut optimizer = CircuitOptimizer::new();
662 optimizer.optimize(circuit)
663}
664
665pub fn optimize_circuit_with_config<const N: usize>(
667 circuit: &Circuit<N>,
668 config: OptimizationConfig,
669) -> QuantRS2Result<(Circuit<N>, OptimizationStatistics)> {
670 let mut optimizer = CircuitOptimizer::with_config(config);
671 let optimized_circuit = optimizer.optimize(circuit)?;
672 Ok((optimized_circuit, optimizer.statistics.clone()))
673}
674
675#[cfg(test)]
676mod tests {
677 use super::*;
678
679 #[test]
680 fn test_optimizer_creation() {
681 let optimizer = CircuitOptimizer::new();
682 assert!(optimizer.config.enable_gate_fusion);
683 assert!(optimizer.config.enable_redundant_elimination);
684 }
685
686 #[test]
687 fn test_optimization_config() {
688 let mut config = OptimizationConfig::default();
689 config.enable_gate_fusion = false;
690 config.max_passes = 5;
691
692 let optimizer = CircuitOptimizer::with_config(config);
693 assert!(!optimizer.config.enable_gate_fusion);
694 assert_eq!(optimizer.config.max_passes, 5);
695 }
696
697 #[test]
698 fn test_statistics_calculations() {
699 let stats = OptimizationStatistics {
700 original_gate_count: 100,
701 optimized_gate_count: 80,
702 original_depth: 50,
703 optimized_depth: 40,
704 ..Default::default()
705 };
706
707 assert_eq!(stats.gate_count_reduction(), 20.0);
708 assert_eq!(stats.depth_reduction(), 20.0);
709 }
710
711 #[test]
712 fn test_report_generation() {
713 let stats = OptimizationStatistics {
714 original_gate_count: 100,
715 optimized_gate_count: 80,
716 original_depth: 50,
717 optimized_depth: 40,
718 redundant_gates_eliminated: 10,
719 gates_fused: 5,
720 gates_reordered: 3,
721 passes_performed: 2,
722 optimization_time_ns: 1_000_000,
723 };
724
725 let report = stats.generate_report();
726 assert!(report.contains("20.0%"));
727 assert!(report.contains("100"));
728 assert!(report.contains("80"));
729 }
730}