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 pub fn new() -> Self {
31 Self::default()
32 }
33
34 pub fn with_verbose(mut self, verbose: bool) -> Self {
36 self.verbose = verbose;
37 self
38 }
39}
40
41impl OptimizationPass for ZXOptimizationPass {
42 fn optimize(&self, gates: Vec<Box<dyn GateOp>>) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
43 if gates.is_empty() {
44 return Ok(gates);
45 }
46
47 let pipeline = ZXPipeline::new();
48
49 if self.verbose {
50 let (original_t, _) = pipeline.compare_t_count(&gates, &gates);
51 println!(
52 "ZX-Calculus: Processing circuit with {} gates, {} T-gates",
53 gates.len(),
54 original_t
55 );
56 }
57
58 let optimized = pipeline.optimize(&gates)?;
59
60 if self.verbose {
61 let (original_t, optimized_t) = pipeline.compare_t_count(&gates, &optimized);
62 println!(
63 "ZX-Calculus: Optimized to {} gates, {} T-gates ({}% reduction)",
64 optimized.len(),
65 optimized_t,
66 if original_t > 0 {
67 ((original_t - optimized_t) * 100) / original_t
68 } else {
69 0
70 }
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.optimize(gates.clone()).unwrap();
140
141 assert!(optimized.len() <= gates.len());
143 }
144
145 #[test]
146 fn test_zx_not_applicable() {
147 let gates: Vec<Box<dyn GateOp>> = vec![];
149
150 let pass = ZXOptimizationPass::new();
151 assert!(!pass.is_applicable(&gates));
152 }
153
154 #[test]
155 fn test_zx_with_optimization_chain() {
156 use crate::optimization::OptimizationChain;
157
158 let gates: Vec<Box<dyn GateOp>> = vec![
159 Box::new(RotationZ {
160 target: QubitId(0),
161 theta: std::f64::consts::PI / 4.0,
162 }),
163 Box::new(RotationZ {
164 target: QubitId(0),
165 theta: std::f64::consts::PI / 4.0,
166 }),
167 Box::new(CNOT {
168 control: QubitId(0),
169 target: QubitId(1),
170 }),
171 Box::new(CNOT {
172 control: QubitId(0),
173 target: QubitId(1),
174 }),
175 ];
176
177 let chain = OptimizationChain::new().add_pass(Box::new(ZXOptimizationPass::new()));
178
179 let optimized = chain.optimize(gates.clone()).unwrap();
180
181 assert!(optimized.len() < gates.len());
183 }
184}