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