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" | "S" | "S†" | "T" | "T†" | "RZ", "Z")
101            | ("Z" | "S" | "S†" | "T" | "T†" | "RZ", "S")
102            | ("Z" | "S" | "S†" | "T" | "T†", "S†")
103            | ("Z" | "S" | "S†" | "T" | "T†" | "RZ", "T")
104            | ("Z" | "S" | "S†" | "T" | "T†", "T†")
105            | ("RZ", "RZ") => true,
106
107            // X-basis gates commute with each other
108            ("X" | "RX", "X") | ("RX" | "X", "RX") => true,
109
110            // Y-basis gates commute with each other
111            ("Y" | "RY", "Y") | ("RY" | "Y", "RY") => true,
112
113            _ => false,
114        }
115    } else {
116        false
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123    use crate::gate::single::{Hadamard, PauliX, PauliZ};
124    use crate::qubit::QubitId;
125
126    #[test]
127    fn test_gates_are_disjoint() {
128        let gate1 = Box::new(PauliX { target: QubitId(0) }) as Box<dyn GateOp>;
129        let gate2 = Box::new(PauliZ { target: QubitId(1) }) as Box<dyn GateOp>;
130        let gate3 = Box::new(Hadamard { target: QubitId(0) }) as Box<dyn GateOp>;
131
132        assert!(gates_are_disjoint(gate1.as_ref(), gate2.as_ref()));
133        assert!(!gates_are_disjoint(gate1.as_ref(), gate3.as_ref()));
134    }
135
136    #[test]
137    fn test_gates_can_commute() {
138        let z1 = Box::new(PauliZ { target: QubitId(0) }) as Box<dyn GateOp>;
139        let z2 = Box::new(PauliZ { target: QubitId(0) }) as Box<dyn GateOp>;
140        let x1 = Box::new(PauliX { target: QubitId(0) }) as Box<dyn GateOp>;
141
142        assert!(gates_can_commute(z1.as_ref(), z2.as_ref()));
143        assert!(!gates_can_commute(z1.as_ref(), x1.as_ref()));
144    }
145}