quantrs2_circuit/optimization/
pass_manager.rs1use crate::builder::Circuit;
6use crate::optimization::cost_model::{CircuitCostExt, CostModel};
7use crate::optimization::passes::{
8 CircuitRewriting, CostBasedOptimization, CostTarget, DecompositionOptimization,
9 GateCancellation, GateCommutation, GateMerging, OptimizationPass, OptimizationPassExt,
10 PeepholeOptimization, RotationMerging, TemplateMatching, TwoQubitOptimization,
11};
12use quantrs2_core::error::QuantRS2Result;
13use serde::{Deserialize, Serialize};
14use std::collections::HashSet;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
18pub enum OptimizationLevel {
19 None,
21 Light,
23 Medium,
25 Heavy,
27 Custom,
29}
30
31#[derive(Debug, Clone)]
33pub struct PassConfig {
34 pub max_iterations: usize,
36 pub aggressive: bool,
38 pub target_gates: HashSet<String>,
40 pub backend: Option<String>,
42 pub level: OptimizationLevel,
44 pub disabled_passes: HashSet<String>,
46}
47
48impl Default for PassConfig {
49 fn default() -> Self {
50 Self {
51 max_iterations: 10,
52 aggressive: false,
53 target_gates: HashSet::new(),
54 backend: None,
55 level: OptimizationLevel::Medium,
56 disabled_passes: HashSet::new(),
57 }
58 }
59}
60
61pub struct PassManager {
63 passes: Vec<Box<dyn OptimizationPass>>,
64 config: PassConfig,
65 applied_passes: Vec<String>,
66}
67
68impl PassManager {
69 #[must_use]
71 pub fn new() -> Self {
72 Self::with_level(OptimizationLevel::Medium)
73 }
74
75 #[must_use]
77 pub fn with_level(level: OptimizationLevel) -> Self {
78 let config = PassConfig {
79 level,
80 ..Default::default()
81 };
82
83 let passes = Self::create_passes_for_level(level, &config);
84
85 Self {
86 passes,
87 config,
88 applied_passes: Vec::new(),
89 }
90 }
91
92 #[must_use]
94 pub fn for_hardware(hardware: &str) -> Self {
95 let mut config = PassConfig {
96 level: OptimizationLevel::Medium,
97 backend: Some(hardware.to_string()),
98 ..Default::default()
99 };
100
101 config.target_gates = match hardware {
103 "ibm" => vec!["X", "Y", "Z", "H", "S", "T", "RZ", "CNOT", "CZ"]
104 .into_iter()
105 .map(std::string::ToString::to_string)
106 .collect(),
107 "google" => vec!["X", "Y", "Z", "H", "RZ", "CZ", "SQRT_X"]
108 .into_iter()
109 .map(std::string::ToString::to_string)
110 .collect(),
111 "aws" => vec!["X", "Y", "Z", "H", "RZ", "RX", "RY", "CNOT", "CZ"]
112 .into_iter()
113 .map(std::string::ToString::to_string)
114 .collect(),
115 _ => HashSet::new(),
116 };
117
118 let passes = Self::create_passes_for_hardware(hardware, &config);
119
120 Self {
121 passes,
122 config,
123 applied_passes: Vec::new(),
124 }
125 }
126
127 pub fn configure(&mut self, config: PassConfig) {
129 self.config = config;
130 self.passes = Self::create_passes_for_level(self.config.level, &self.config);
131 }
132
133 pub fn add_pass(&mut self, pass: Box<dyn OptimizationPass>) {
135 self.passes.push(pass);
136 }
137
138 pub fn remove_pass(&mut self, name: &str) {
140 self.passes.retain(|p| p.name() != name);
141 }
142
143 pub fn run<const N: usize>(
145 &mut self,
146 circuit: &Circuit<N>,
147 cost_model: &dyn CostModel,
148 ) -> QuantRS2Result<Circuit<N>> {
149 self.applied_passes.clear();
150 let mut current_circuit = circuit.clone();
151 let mut iteration = 0;
152 let mut improved = true;
153
154 while improved && iteration < self.config.max_iterations {
155 improved = false;
156 let start_cost = cost_model.circuit_cost(¤t_circuit);
157
158 for pass in &self.passes {
159 if self.config.disabled_passes.contains(pass.name()) {
160 continue;
161 }
162
163 if pass.should_apply() {
164 let optimized = pass.apply(¤t_circuit, cost_model)?;
165 let new_cost = cost_model.circuit_cost(&optimized);
166
167 if new_cost < start_cost {
168 current_circuit = optimized;
169 self.applied_passes.push(pass.name().to_string());
170 improved = true;
171 }
172 }
173 }
174
175 iteration += 1;
176 }
177
178 Ok(current_circuit)
179 }
180
181 #[must_use]
183 pub fn get_applied_passes(&self) -> Vec<String> {
184 self.applied_passes.clone()
185 }
186
187 fn create_passes_for_level(
189 level: OptimizationLevel,
190 config: &PassConfig,
191 ) -> Vec<Box<dyn OptimizationPass>> {
192 match level {
193 OptimizationLevel::None => vec![],
194
195 OptimizationLevel::Light => vec![
196 Box::new(GateCancellation::new(false)),
197 Box::new(RotationMerging::new(1e-10)),
198 ],
199
200 OptimizationLevel::Medium => vec![
201 Box::new(GateCancellation::new(false)),
202 Box::new(GateCommutation::new(5)),
203 Box::new(PeepholeOptimization::new(3)),
204 Box::new(RotationMerging::new(1e-10)),
205 Box::new(GateMerging::new(true, 1e-10)),
206 Box::new(TemplateMatching::new()),
207 ],
208
209 OptimizationLevel::Heavy => vec![
210 Box::new(GateCancellation::new(true)),
211 Box::new(GateCommutation::new(10)),
212 Box::new(PeepholeOptimization::new(4)),
213 Box::new(RotationMerging::new(1e-12)),
214 Box::new(GateMerging::new(true, 1e-12)),
215 Box::new(DecompositionOptimization::new(
216 config.target_gates.clone(),
217 true,
218 )),
219 Box::new(TwoQubitOptimization::new(true, true)),
220 Box::new(TemplateMatching::new()),
221 Box::new(CircuitRewriting::new(100)),
222 Box::new(CostBasedOptimization::new(CostTarget::Balanced, 20)),
223 ],
224
225 OptimizationLevel::Custom => vec![],
226 }
227 }
228
229 fn create_passes_for_hardware(
231 hardware: &str,
232 config: &PassConfig,
233 ) -> Vec<Box<dyn OptimizationPass>> {
234 let mut passes: Vec<Box<dyn OptimizationPass>> = vec![
235 Box::new(GateCancellation::new(false)),
236 Box::new(GateCommutation::new(5)),
237 Box::new(PeepholeOptimization::new(3)),
238 Box::new(RotationMerging::new(1e-10)),
239 ];
240
241 match hardware {
242 "ibm" => {
243 passes.push(Box::new(DecompositionOptimization::for_hardware("ibm")));
244 passes.push(Box::new(TwoQubitOptimization::new(false, true)));
245 }
246 "google" => {
247 passes.push(Box::new(DecompositionOptimization::for_hardware("google")));
248 passes.push(Box::new(TwoQubitOptimization::new(true, false)));
249 }
250 "aws" => {
251 passes.push(Box::new(DecompositionOptimization::for_hardware("aws")));
252 passes.push(Box::new(CostBasedOptimization::new(
253 CostTarget::TotalError,
254 10,
255 )));
256 }
257 _ => {
258 passes.push(Box::new(DecompositionOptimization::new(
259 config.target_gates.clone(),
260 true,
261 )));
262 }
263 }
264
265 passes.push(Box::new(TemplateMatching::new()));
266 passes
267 }
268}
269
270impl Default for PassManager {
271 fn default() -> Self {
272 Self::new()
273 }
274}