1use crate::error::{Result, SimulatorError};
29use std::collections::{HashMap, HashSet};
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
37pub enum GateType {
38 I,
40 H,
42 X,
44 Y,
46 Z,
48 S,
50 Sdg,
52 T,
54 Tdg,
56 RX,
58 RY,
60 RZ,
62 CNOT,
64 CZ,
66 SWAP,
68 Toffoli,
70}
71
72impl GateType {
73 pub fn is_self_inverse(&self) -> bool {
75 matches!(
76 self,
77 GateType::H | GateType::X | GateType::Y | GateType::Z | GateType::CNOT | GateType::SWAP
78 )
79 }
80
81 pub fn inverse(&self) -> Self {
83 match self {
84 GateType::S => GateType::Sdg,
85 GateType::Sdg => GateType::S,
86 GateType::T => GateType::Tdg,
87 GateType::Tdg => GateType::T,
88 GateType::RX | GateType::RY | GateType::RZ => *self, _ if self.is_self_inverse() => *self,
90 _ => *self, }
92 }
93
94 pub fn is_rotation(&self) -> bool {
96 matches!(self, GateType::RX | GateType::RY | GateType::RZ)
97 }
98
99 pub fn is_single_qubit(&self) -> bool {
101 !matches!(
102 self,
103 GateType::CNOT | GateType::CZ | GateType::SWAP | GateType::Toffoli
104 )
105 }
106
107 pub fn commutes_with(&self, other: &GateType) -> bool {
109 match (self, other) {
111 (GateType::Z, GateType::Z)
113 | (GateType::Z, GateType::S)
114 | (GateType::Z, GateType::Sdg)
115 | (GateType::Z, GateType::T)
116 | (GateType::Z, GateType::Tdg)
117 | (GateType::S, GateType::Z)
118 | (GateType::Sdg, GateType::Z)
119 | (GateType::T, GateType::Z)
120 | (GateType::Tdg, GateType::Z) => true,
121
122 (GateType::RX, GateType::RX)
124 | (GateType::RY, GateType::RY)
125 | (GateType::RZ, GateType::RZ) => true,
126
127 _ => false,
128 }
129 }
130}
131
132#[derive(Debug, Clone, PartialEq)]
134pub struct Gate {
135 pub gate_type: GateType,
137 pub qubits: Vec<usize>,
139 pub parameters: Vec<f64>,
141}
142
143impl Gate {
144 pub fn new(gate_type: GateType, qubits: Vec<usize>) -> Self {
146 Self {
147 gate_type,
148 qubits,
149 parameters: Vec::new(),
150 }
151 }
152
153 pub fn with_parameters(gate_type: GateType, qubits: Vec<usize>, parameters: Vec<f64>) -> Self {
155 Self {
156 gate_type,
157 qubits,
158 parameters,
159 }
160 }
161
162 pub fn inverse(&self) -> Self {
164 let inv_type = self.gate_type.inverse();
165 let inv_params = if self.gate_type.is_rotation() {
166 self.parameters.iter().map(|p| -p).collect()
167 } else {
168 self.parameters.clone()
169 };
170
171 Self {
172 gate_type: inv_type,
173 qubits: self.qubits.clone(),
174 parameters: inv_params,
175 }
176 }
177
178 pub fn is_inverse_of(&self, other: &Gate) -> bool {
180 if self.qubits != other.qubits {
181 return false;
182 }
183
184 if self.gate_type.is_self_inverse() && self.gate_type == other.gate_type {
185 return true;
186 }
187
188 if self.gate_type.inverse() == other.gate_type {
189 if self.gate_type.is_rotation() {
191 return self
192 .parameters
193 .iter()
194 .zip(other.parameters.iter())
195 .all(|(p1, p2)| (p1 + p2).abs() < 1e-10);
196 }
197 return true;
198 }
199
200 false
201 }
202}
203
204#[derive(Debug, Clone)]
206pub struct Circuit {
207 pub n_qubits: usize,
209 pub gates: Vec<Gate>,
211}
212
213impl Circuit {
214 pub fn new(n_qubits: usize) -> Self {
216 Self {
217 n_qubits,
218 gates: Vec::new(),
219 }
220 }
221
222 pub fn add_gate(&mut self, gate: Gate) -> Result<()> {
224 for &qubit in &gate.qubits {
226 if qubit >= self.n_qubits {
227 return Err(SimulatorError::InvalidInput(format!(
228 "Qubit index {} out of range (circuit has {} qubits)",
229 qubit, self.n_qubits
230 )));
231 }
232 }
233 self.gates.push(gate);
234 Ok(())
235 }
236
237 pub fn depth(&self) -> usize {
239 if self.gates.is_empty() {
240 return 0;
241 }
242
243 let mut qubit_depths = vec![0; self.n_qubits];
244 let mut max_depth = 0;
245
246 for gate in &self.gates {
247 let current_depth = gate
249 .qubits
250 .iter()
251 .map(|&q| qubit_depths[q])
252 .max()
253 .unwrap_or(0);
254
255 for &qubit in &gate.qubits {
257 qubit_depths[qubit] = current_depth + 1;
258 }
259
260 max_depth = max_depth.max(current_depth + 1);
261 }
262
263 max_depth
264 }
265
266 pub fn gate_counts(&self) -> HashMap<GateType, usize> {
268 let mut counts = HashMap::new();
269 for gate in &self.gates {
270 *counts.entry(gate.gate_type).or_insert(0) += 1;
271 }
272 counts
273 }
274
275 pub fn two_qubit_gate_count(&self) -> usize {
277 self.gates.iter().filter(|g| g.qubits.len() == 2).count()
278 }
279}
280
281#[derive(Debug, Clone, Copy, PartialEq, Eq)]
287pub enum OptimizationPass {
288 CancelInverses,
290 FuseRotations,
292 CommutativeReordering,
294 RemoveIdentities,
296 TemplateMatching,
298}
299
300pub struct CircuitOptimizer {
302 passes: Vec<OptimizationPass>,
304 max_iterations: usize,
306}
307
308impl CircuitOptimizer {
309 pub fn new() -> Self {
311 Self {
312 passes: vec![
313 OptimizationPass::CancelInverses,
314 OptimizationPass::RemoveIdentities,
315 OptimizationPass::FuseRotations,
316 ],
317 max_iterations: 10,
318 }
319 }
320
321 pub fn with_pass(mut self, pass: OptimizationPass) -> Self {
323 self.passes.push(pass);
324 self
325 }
326
327 pub fn with_max_iterations(mut self, max_iterations: usize) -> Self {
329 self.max_iterations = max_iterations;
330 self
331 }
332
333 pub fn optimize(&self, circuit: &Circuit) -> Result<Circuit> {
335 let mut optimized = circuit.clone();
336 let mut iteration = 0;
337
338 loop {
339 let initial_gate_count = optimized.gates.len();
340
341 for pass in &self.passes {
343 optimized = self.apply_pass(&optimized, *pass)?;
344 }
345
346 iteration += 1;
347 let final_gate_count = optimized.gates.len();
348
349 if final_gate_count >= initial_gate_count || iteration >= self.max_iterations {
351 break;
352 }
353 }
354
355 Ok(optimized)
356 }
357
358 fn apply_pass(&self, circuit: &Circuit, pass: OptimizationPass) -> Result<Circuit> {
360 match pass {
361 OptimizationPass::CancelInverses => self.cancel_inverses(circuit),
362 OptimizationPass::FuseRotations => self.fuse_rotations(circuit),
363 OptimizationPass::CommutativeReordering => self.commutative_reordering(circuit),
364 OptimizationPass::RemoveIdentities => self.remove_identities(circuit),
365 OptimizationPass::TemplateMatching => self.template_matching(circuit),
366 }
367 }
368
369 fn cancel_inverses(&self, circuit: &Circuit) -> Result<Circuit> {
371 let mut optimized = Circuit::new(circuit.n_qubits);
372 let gates = &circuit.gates;
373 let mut skip_next = false;
374
375 for i in 0..gates.len() {
376 if skip_next {
377 skip_next = false;
378 continue;
379 }
380
381 if i + 1 < gates.len() && gates[i].is_inverse_of(&gates[i + 1]) {
382 skip_next = true;
384 } else {
385 optimized.add_gate(gates[i].clone())?;
386 }
387 }
388
389 Ok(optimized)
390 }
391
392 fn fuse_rotations(&self, circuit: &Circuit) -> Result<Circuit> {
394 let mut optimized = Circuit::new(circuit.n_qubits);
395 let gates = &circuit.gates;
396 let mut i = 0;
397
398 while i < gates.len() {
399 let gate = &gates[i];
400
401 if gate.gate_type.is_rotation() && gate.qubits.len() == 1 {
403 let mut fused_angle = gate.parameters[0];
405 let mut j = i + 1;
406
407 while j < gates.len() {
408 let next_gate = &gates[j];
409 if next_gate.gate_type == gate.gate_type
410 && next_gate.qubits == gate.qubits
411 && !next_gate.parameters.is_empty()
412 {
413 fused_angle += next_gate.parameters[0];
414 j += 1;
415 } else {
416 break;
417 }
418 }
419
420 if fused_angle.abs() > 1e-10 {
422 optimized.add_gate(Gate::with_parameters(
423 gate.gate_type,
424 gate.qubits.clone(),
425 vec![fused_angle],
426 ))?;
427 }
428
429 i = j;
430 } else {
431 optimized.add_gate(gate.clone())?;
432 i += 1;
433 }
434 }
435
436 Ok(optimized)
437 }
438
439 fn commutative_reordering(&self, circuit: &Circuit) -> Result<Circuit> {
441 Ok(circuit.clone())
444 }
445
446 fn remove_identities(&self, circuit: &Circuit) -> Result<Circuit> {
448 let mut optimized = Circuit::new(circuit.n_qubits);
449
450 for gate in &circuit.gates {
451 if gate.gate_type == GateType::I {
453 continue;
454 }
455
456 if gate.gate_type.is_rotation()
458 && !gate.parameters.is_empty()
459 && gate.parameters[0].abs() < 1e-10
460 {
461 continue;
462 }
463
464 optimized.add_gate(gate.clone())?;
465 }
466
467 Ok(optimized)
468 }
469
470 fn template_matching(&self, circuit: &Circuit) -> Result<Circuit> {
472 Ok(circuit.clone())
474 }
475}
476
477impl Default for CircuitOptimizer {
478 fn default() -> Self {
479 Self::new()
480 }
481}
482
483#[derive(Debug, Clone)]
485pub struct OptimizationStats {
486 pub original_gates: usize,
488 pub optimized_gates: usize,
490 pub original_depth: usize,
492 pub optimized_depth: usize,
494 pub original_two_qubit_gates: usize,
496 pub optimized_two_qubit_gates: usize,
498 pub gate_reduction_percent: f64,
500 pub depth_reduction_percent: f64,
502}
503
504impl OptimizationStats {
505 pub fn from_circuits(original: &Circuit, optimized: &Circuit) -> Self {
507 let original_gates = original.gates.len();
508 let optimized_gates = optimized.gates.len();
509 let original_depth = original.depth();
510 let optimized_depth = optimized.depth();
511 let original_two_qubit = original.two_qubit_gate_count();
512 let optimized_two_qubit = optimized.two_qubit_gate_count();
513
514 let gate_reduction = if original_gates > 0 {
515 100.0 * (original_gates - optimized_gates) as f64 / original_gates as f64
516 } else {
517 0.0
518 };
519
520 let depth_reduction = if original_depth > 0 {
521 100.0 * (original_depth - optimized_depth) as f64 / original_depth as f64
522 } else {
523 0.0
524 };
525
526 Self {
527 original_gates,
528 optimized_gates,
529 original_depth,
530 optimized_depth,
531 original_two_qubit_gates: original_two_qubit,
532 optimized_two_qubit_gates: optimized_two_qubit,
533 gate_reduction_percent: gate_reduction,
534 depth_reduction_percent: depth_reduction,
535 }
536 }
537}
538
539#[cfg(test)]
540mod tests {
541 use super::*;
542
543 #[test]
544 fn test_gate_inverse() {
545 let h = Gate::new(GateType::H, vec![0]);
546 let h_inv = h.inverse();
547 assert!(h.is_inverse_of(&h_inv));
548 }
549
550 #[test]
551 fn test_cancel_inverses() {
552 let mut circuit = Circuit::new(2);
553 circuit.add_gate(Gate::new(GateType::H, vec![0])).unwrap();
554 circuit.add_gate(Gate::new(GateType::H, vec![0])).unwrap();
555 circuit.add_gate(Gate::new(GateType::X, vec![1])).unwrap();
556
557 let optimizer = CircuitOptimizer::new();
558 let optimized = optimizer.cancel_inverses(&circuit).unwrap();
559
560 assert_eq!(optimized.gates.len(), 1);
562 assert_eq!(optimized.gates[0].gate_type, GateType::X);
563 }
564
565 #[test]
566 fn test_fuse_rotations() {
567 let mut circuit = Circuit::new(1);
568 circuit
569 .add_gate(Gate::with_parameters(
570 GateType::RX,
571 vec![0],
572 vec![std::f64::consts::PI / 4.0],
573 ))
574 .unwrap();
575 circuit
576 .add_gate(Gate::with_parameters(
577 GateType::RX,
578 vec![0],
579 vec![std::f64::consts::PI / 4.0],
580 ))
581 .unwrap();
582
583 let optimizer = CircuitOptimizer::new();
584 let optimized = optimizer.fuse_rotations(&circuit).unwrap();
585
586 assert_eq!(optimized.gates.len(), 1);
588 assert!((optimized.gates[0].parameters[0] - std::f64::consts::PI / 2.0).abs() < 1e-10);
589 }
590
591 #[test]
592 fn test_remove_identities() {
593 let mut circuit = Circuit::new(2);
594 circuit.add_gate(Gate::new(GateType::I, vec![0])).unwrap();
595 circuit.add_gate(Gate::new(GateType::X, vec![1])).unwrap();
596 circuit
597 .add_gate(Gate::with_parameters(GateType::RZ, vec![0], vec![0.0]))
598 .unwrap();
599
600 let optimizer = CircuitOptimizer::new();
601 let optimized = optimizer.remove_identities(&circuit).unwrap();
602
603 assert_eq!(optimized.gates.len(), 1);
605 assert_eq!(optimized.gates[0].gate_type, GateType::X);
606 }
607
608 #[test]
609 fn test_circuit_depth() {
610 let mut circuit = Circuit::new(2);
611 circuit.add_gate(Gate::new(GateType::H, vec![0])).unwrap();
612 circuit.add_gate(Gate::new(GateType::H, vec![1])).unwrap();
613 circuit
614 .add_gate(Gate::new(GateType::CNOT, vec![0, 1]))
615 .unwrap();
616
617 assert_eq!(circuit.depth(), 2);
619 }
620
621 #[test]
622 fn test_full_optimization() {
623 let mut circuit = Circuit::new(2);
624 circuit.add_gate(Gate::new(GateType::H, vec![0])).unwrap();
626 circuit.add_gate(Gate::new(GateType::H, vec![0])).unwrap(); circuit
628 .add_gate(Gate::with_parameters(
629 GateType::RX,
630 vec![1],
631 vec![std::f64::consts::PI / 4.0],
632 ))
633 .unwrap();
634 circuit
635 .add_gate(Gate::with_parameters(
636 GateType::RX,
637 vec![1],
638 vec![std::f64::consts::PI / 4.0],
639 ))
640 .unwrap(); circuit.add_gate(Gate::new(GateType::I, vec![0])).unwrap(); let optimizer = CircuitOptimizer::new();
644 let optimized = optimizer.optimize(&circuit).unwrap();
645
646 assert_eq!(optimized.gates.len(), 1);
648 assert_eq!(optimized.gates[0].gate_type, GateType::RX);
649 }
650
651 #[test]
652 fn test_optimization_stats() {
653 let mut original = Circuit::new(2);
654 for _ in 0..10 {
655 original.add_gate(Gate::new(GateType::X, vec![0])).unwrap();
656 }
657
658 let mut optimized = Circuit::new(2);
659 optimized.add_gate(Gate::new(GateType::X, vec![0])).unwrap();
660
661 let stats = OptimizationStats::from_circuits(&original, &optimized);
662 assert_eq!(stats.original_gates, 10);
663 assert_eq!(stats.optimized_gates, 1);
664 assert!((stats.gate_reduction_percent - 90.0).abs() < 1e-6);
665 }
666}