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 if original_t > 0 {
69 ((original_t - optimized_t) * 100) / original_t
70 } else {
71 0
72 }
73 );
74 }
75
76 Ok(optimized)
77 }
78
79 fn name(&self) -> &str {
80 &self.name
81 }
82
83 fn is_applicable(&self, gates: &[Box<dyn GateOp>]) -> bool {
84 if gates.is_empty() {
90 return false;
91 }
92
93 let has_supported_gates = gates.iter().any(|g| {
95 matches!(
96 g.name(),
97 "H" | "X" | "Y" | "Z" | "S" | "T" | "RX" | "RY" | "RZ" | "CNOT" | "CZ"
98 )
99 });
100
101 let rotation_count = gates
103 .iter()
104 .filter(|g| matches!(g.name(), "RX" | "RY" | "RZ" | "T"))
105 .count();
106
107 let cnot_count = gates
108 .iter()
109 .filter(|g| matches!(g.name(), "CNOT" | "CZ"))
110 .count();
111
112 has_supported_gates && (rotation_count > 1 || cnot_count > 1)
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120 use crate::gate::{multi::*, single::*};
121 use crate::qubit::QubitId;
122
123 #[test]
124 fn test_zx_optimization_pass() {
125 let gates: Vec<Box<dyn GateOp>> = vec![
126 Box::new(Hadamard { target: QubitId(0) }),
127 Box::new(RotationZ {
128 target: QubitId(0),
129 theta: std::f64::consts::PI / 4.0,
130 }),
131 Box::new(RotationZ {
132 target: QubitId(0),
133 theta: std::f64::consts::PI / 4.0,
134 }),
135 Box::new(Hadamard { target: QubitId(0) }),
136 ];
137
138 let pass = ZXOptimizationPass::new();
139 assert!(pass.is_applicable(&gates));
140
141 let optimized = pass
142 .optimize(gates.clone())
143 .expect("Failed to optimize circuit with ZX pass");
144
145 assert!(optimized.len() <= gates.len());
147 }
148
149 #[test]
150 fn test_zx_not_applicable() {
151 let gates: Vec<Box<dyn GateOp>> = vec![];
153
154 let pass = ZXOptimizationPass::new();
155 assert!(!pass.is_applicable(&gates));
156 }
157
158 #[test]
159 fn test_zx_with_optimization_chain() {
160 use crate::optimization::OptimizationChain;
161
162 let gates: Vec<Box<dyn GateOp>> = vec![
163 Box::new(RotationZ {
164 target: QubitId(0),
165 theta: std::f64::consts::PI / 4.0,
166 }),
167 Box::new(RotationZ {
168 target: QubitId(0),
169 theta: std::f64::consts::PI / 4.0,
170 }),
171 Box::new(CNOT {
172 control: QubitId(0),
173 target: QubitId(1),
174 }),
175 Box::new(CNOT {
176 control: QubitId(0),
177 target: QubitId(1),
178 }),
179 ];
180
181 let chain = OptimizationChain::new().add_pass(Box::new(ZXOptimizationPass::new()));
182
183 let optimized = chain
184 .optimize(gates.clone())
185 .expect("Failed to optimize circuit with ZX chain");
186
187 assert!(optimized.len() < gates.len());
189 }
190}