quantrs2_core/optimization/
mod.rs

1//! Gate optimization passes for quantum circuits
2//!
3//! This module provides various optimization techniques for quantum circuits,
4//! including gate fusion, commutation, and peephole optimizations.
5
6pub mod compression;
7pub mod fusion;
8pub mod lazy_evaluation;
9pub mod peephole;
10pub mod zx_optimizer;
11
12use crate::error::QuantRS2Result;
13use crate::gate::GateOp;
14use crate::qubit::QubitId;
15
16/// Trait for optimization passes
17pub trait OptimizationPass {
18    /// Apply the optimization pass to a sequence of gates
19    fn optimize(&self, gates: Vec<Box<dyn GateOp>>) -> QuantRS2Result<Vec<Box<dyn GateOp>>>;
20
21    /// Get the name of this optimization pass
22    fn name(&self) -> &str;
23
24    /// Check if this pass is applicable to the given gates
25    fn is_applicable(&self, gates: &[Box<dyn GateOp>]) -> bool {
26        !gates.is_empty()
27    }
28}
29
30/// Chain multiple optimization passes together
31pub struct OptimizationChain {
32    passes: Vec<Box<dyn OptimizationPass>>,
33}
34
35impl OptimizationChain {
36    /// Create a new optimization chain
37    pub fn new() -> Self {
38        Self { passes: Vec::new() }
39    }
40
41    /// Add an optimization pass to the chain
42    #[must_use]
43    pub fn add_pass(mut self, pass: Box<dyn OptimizationPass>) -> Self {
44        self.passes.push(pass);
45        self
46    }
47
48    /// Apply all optimization passes in sequence
49    pub fn optimize(
50        &self,
51        mut gates: Vec<Box<dyn GateOp>>,
52    ) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
53        for pass in &self.passes {
54            if pass.is_applicable(&gates) {
55                gates = pass.optimize(gates)?;
56            }
57        }
58        Ok(gates)
59    }
60}
61
62/// Information about gate connectivity
63#[derive(Debug, Clone)]
64pub struct GateInfo {
65    /// The gate being analyzed
66    pub gate: Box<dyn GateOp>,
67    /// Index in the gate sequence
68    pub index: usize,
69    /// Qubits this gate acts on
70    pub qubits: Vec<QubitId>,
71    /// Whether this gate is parameterized
72    pub is_parameterized: bool,
73}
74
75/// Check if two gates act on disjoint qubits
76pub fn gates_are_disjoint(gate1: &dyn GateOp, gate2: &dyn GateOp) -> bool {
77    let qubits1 = gate1.qubits();
78    let qubits2 = gate2.qubits();
79
80    for q1 in &qubits1 {
81        for q2 in &qubits2 {
82            if q1 == q2 {
83                return false;
84            }
85        }
86    }
87    true
88}
89
90/// Check if two gates can be commuted past each other
91pub fn gates_can_commute(gate1: &dyn GateOp, gate2: &dyn GateOp) -> bool {
92    // Disjoint gates always commute
93    if gates_are_disjoint(gate1, gate2) {
94        return true;
95    }
96
97    // Same single-qubit gates on same qubit might commute
98    if gate1.qubits().len() == 1 && gate2.qubits().len() == 1 && gate1.qubits() == gate2.qubits() {
99        match (gate1.name(), gate2.name()) {
100            // Same-basis gates commute with each other
101            ("Z" | "S" | "S†" | "T" | "T†" | "RZ", "Z" | "S" | "T")
102            | ("Z" | "S" | "S†" | "T" | "T†", "S†" | "T†")
103            | ("RZ", "RZ")
104            | ("X" | "RX", "X" | "RX") // X-basis
105            | ("Y" | "RY", "Y" | "RY") => true, // Y-basis
106
107            _ => false,
108        }
109    } else {
110        false
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117    use crate::gate::single::{Hadamard, PauliX, PauliZ};
118    use crate::qubit::QubitId;
119
120    #[test]
121    fn test_gates_are_disjoint() {
122        let gate1 = Box::new(PauliX { target: QubitId(0) }) as Box<dyn GateOp>;
123        let gate2 = Box::new(PauliZ { target: QubitId(1) }) as Box<dyn GateOp>;
124        let gate3 = Box::new(Hadamard { target: QubitId(0) }) as Box<dyn GateOp>;
125
126        assert!(gates_are_disjoint(gate1.as_ref(), gate2.as_ref()));
127        assert!(!gates_are_disjoint(gate1.as_ref(), gate3.as_ref()));
128    }
129
130    #[test]
131    fn test_gates_can_commute() {
132        let z1 = Box::new(PauliZ { target: QubitId(0) }) as Box<dyn GateOp>;
133        let z2 = Box::new(PauliZ { target: QubitId(0) }) as Box<dyn GateOp>;
134        let x1 = Box::new(PauliX { target: QubitId(0) }) as Box<dyn GateOp>;
135
136        assert!(gates_can_commute(z1.as_ref(), z2.as_ref()));
137        assert!(!gates_can_commute(z1.as_ref(), x1.as_ref()));
138    }
139}