quantrs2_core/optimization/
zx_optimizer.rs1use crate::{
7 error::QuantRS2Result, gate::GateOp, optimization::OptimizationPass, zx_extraction::ZXPipeline,
8};
9
10#[derive(Debug, Clone)]
12pub struct ZXOptimizationPass {
13 name: String,
15 verbose: bool,
17}
18
19impl Default for ZXOptimizationPass {
20 fn default() -> Self {
21 Self {
22 name: "ZX-Calculus Optimization".to_string(),
23 verbose: false,
24 }
25 }
26}
27
28impl ZXOptimizationPass {
29 #[must_use]
31 pub fn new() -> Self {
32 Self::default()
33 }
34
35 #[must_use]
37 pub const fn with_verbose(mut self, verbose: bool) -> Self {
38 self.verbose = verbose;
39 self
40 }
41}
42
43impl OptimizationPass for ZXOptimizationPass {
44 fn optimize(&self, gates: Vec<Box<dyn GateOp>>) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
45 if gates.is_empty() {
46 return Ok(gates);
47 }
48
49 let pipeline = ZXPipeline::new();
50
51 if self.verbose {
52 let (original_t, _) = pipeline.compare_t_count(&gates, &gates);
53 println!(
54 "ZX-Calculus: Processing circuit with {} gates, {} T-gates",
55 gates.len(),
56 original_t
57 );
58 }
59
60 let optimized = pipeline.optimize(&gates)?;
61
62 if self.verbose {
63 let (original_t, optimized_t) = pipeline.compare_t_count(&gates, &optimized);
64 println!(
65 "ZX-Calculus: Optimized to {} gates, {} T-gates ({}% reduction)",
66 optimized.len(),
67 optimized_t,
68 ((original_t - optimized_t) * 100)
69 .checked_div(original_t)
70 .unwrap_or(0)
71 );
72 }
73
74 Ok(optimized)
75 }
76
77 fn name(&self) -> &str {
78 &self.name
79 }
80
81 fn is_applicable(&self, gates: &[Box<dyn GateOp>]) -> bool {
82 if gates.is_empty() {
88 return false;
89 }
90
91 let has_supported_gates = gates.iter().any(|g| {
93 matches!(
94 g.name(),
95 "H" | "X" | "Y" | "Z" | "S" | "T" | "RX" | "RY" | "RZ" | "CNOT" | "CZ"
96 )
97 });
98
99 let rotation_count = gates
101 .iter()
102 .filter(|g| matches!(g.name(), "RX" | "RY" | "RZ" | "T"))
103 .count();
104
105 let cnot_count = gates
106 .iter()
107 .filter(|g| matches!(g.name(), "CNOT" | "CZ"))
108 .count();
109
110 has_supported_gates && (rotation_count > 1 || cnot_count > 1)
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use crate::gate::{multi::*, single::*};
119 use crate::qubit::QubitId;
120
121 #[test]
122 fn test_zx_optimization_pass() {
123 let gates: Vec<Box<dyn GateOp>> = vec![
124 Box::new(Hadamard { target: QubitId(0) }),
125 Box::new(RotationZ {
126 target: QubitId(0),
127 theta: std::f64::consts::PI / 4.0,
128 }),
129 Box::new(RotationZ {
130 target: QubitId(0),
131 theta: std::f64::consts::PI / 4.0,
132 }),
133 Box::new(Hadamard { target: QubitId(0) }),
134 ];
135
136 let pass = ZXOptimizationPass::new();
137 assert!(pass.is_applicable(&gates));
138
139 let optimized = pass
140 .optimize(gates.clone())
141 .expect("Failed to optimize circuit with ZX pass");
142
143 assert!(optimized.len() <= gates.len());
145 }
146
147 #[test]
148 fn test_zx_not_applicable() {
149 let gates: Vec<Box<dyn GateOp>> = vec![];
151
152 let pass = ZXOptimizationPass::new();
153 assert!(!pass.is_applicable(&gates));
154 }
155
156 #[test]
157 fn test_zx_with_optimization_chain() {
158 use crate::optimization::OptimizationChain;
159
160 let gates: Vec<Box<dyn GateOp>> = vec![
161 Box::new(RotationZ {
162 target: QubitId(0),
163 theta: std::f64::consts::PI / 4.0,
164 }),
165 Box::new(RotationZ {
166 target: QubitId(0),
167 theta: std::f64::consts::PI / 4.0,
168 }),
169 Box::new(CNOT {
170 control: QubitId(0),
171 target: QubitId(1),
172 }),
173 Box::new(CNOT {
174 control: QubitId(0),
175 target: QubitId(1),
176 }),
177 ];
178
179 let chain = OptimizationChain::new().add_pass(Box::new(ZXOptimizationPass::new()));
180
181 let optimized = chain
182 .optimize(gates.clone())
183 .expect("Failed to optimize circuit with ZX chain");
184
185 assert!(optimized.len() < gates.len());
187 }
188}