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