cqlib_core/compile/transform/rewrite/
config.rs1use crate::circuit::{Instruction, MCGate, StandardGate};
16use crate::compile::error::CompilerError;
17use crate::compile::knowledge::library::RuleKind;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21pub enum RewriteMode {
22 Optimize,
24 Lowering,
26}
27
28#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct RewriteConfig {
31 max_rounds: u8,
32 max_window_ops: usize,
33 max_pattern_len: usize,
34 recurse_control_flow: bool,
35 skip_labeled_ops: bool,
36 preserve_two_qubit_connectivity: bool,
37 enabled_kinds: Vec<RuleKind>,
38 mode: RewriteMode,
39 target_instructions: Option<Vec<TargetInstruction>>,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Hash)]
44pub(super) enum TargetInstruction {
45 Standard(StandardGate),
46 McGate(MCGate),
47}
48
49impl Default for RewriteConfig {
50 fn default() -> Self {
51 Self::production()
52 }
53}
54
55impl RewriteConfig {
56 pub fn production() -> Self {
58 Self {
59 max_rounds: 8,
60 max_window_ops: 16,
61 max_pattern_len: 8,
62 recurse_control_flow: true,
63 skip_labeled_ops: true,
64 preserve_two_qubit_connectivity: false,
65 enabled_kinds: vec![
66 RuleKind::Simplify,
67 RuleKind::Cancel,
68 RuleKind::Merge,
69 RuleKind::Canonicalize,
70 ],
71 mode: RewriteMode::Optimize,
72 target_instructions: None,
73 }
74 }
75
76 pub fn lowering() -> Self {
78 Self {
79 enabled_kinds: vec![
80 RuleKind::Simplify,
81 RuleKind::Cancel,
82 RuleKind::Merge,
83 RuleKind::Canonicalize,
84 RuleKind::Decompose,
85 RuleKind::HardwareNative,
86 ],
87 mode: RewriteMode::Lowering,
88 ..Self::production()
89 }
90 }
91
92 pub fn new() -> Self {
94 Self::production()
95 }
96
97 pub fn with_max_rounds(mut self, max_rounds: u8) -> Self {
98 self.max_rounds = max_rounds;
99 self
100 }
101
102 pub fn with_max_window_ops(mut self, max_window_ops: usize) -> Self {
103 self.max_window_ops = max_window_ops;
104 self
105 }
106
107 pub fn with_max_pattern_len(mut self, max_pattern_len: usize) -> Self {
108 self.max_pattern_len = max_pattern_len;
109 self
110 }
111
112 pub fn recurse_control_flow(mut self, enabled: bool) -> Self {
113 self.recurse_control_flow = enabled;
114 self
115 }
116
117 pub fn skip_labeled_ops(mut self, enabled: bool) -> Self {
118 self.skip_labeled_ops = enabled;
119 self
120 }
121
122 pub fn with_preserve_two_qubit_connectivity(mut self, enabled: bool) -> Self {
138 self.preserve_two_qubit_connectivity = enabled;
139 self
140 }
141
142 pub fn with_enabled_kinds(mut self, kinds: Vec<RuleKind>) -> Self {
143 self.enabled_kinds = kinds;
144 self
145 }
146
147 pub fn with_mode(mut self, mode: RewriteMode) -> Self {
148 self.mode = mode;
149 self
150 }
151
152 pub fn with_target_instructions(
154 self,
155 target_instructions: Vec<Instruction>,
156 ) -> Result<Self, CompilerError> {
157 self.try_with_target_instructions(target_instructions)
158 }
159
160 pub fn try_with_target_instructions(
162 mut self,
163 target_instructions: Vec<Instruction>,
164 ) -> Result<Self, CompilerError> {
165 if target_instructions.is_empty() {
166 return Err(CompilerError::InvalidInput(
167 "rewrite target instruction basis must not be empty".to_string(),
168 ));
169 }
170
171 let mut deduped = Vec::with_capacity(target_instructions.len());
172 for instruction in target_instructions {
173 let instruction = match instruction {
174 Instruction::Standard(gate) => TargetInstruction::Standard(gate),
175 Instruction::McGate(gate) => TargetInstruction::McGate(*gate),
176 other => {
177 return Err(CompilerError::InvalidInput(format!(
178 "unsupported rewrite target instruction {other:?}"
179 )));
180 }
181 };
182
183 if !deduped.contains(&instruction) {
184 deduped.push(instruction);
185 }
186 }
187
188 self.target_instructions = Some(deduped);
189 Ok(self)
190 }
191
192 pub const fn max_rounds(&self) -> u8 {
193 self.max_rounds
194 }
195
196 pub const fn max_window_ops(&self) -> usize {
197 self.max_window_ops
198 }
199
200 pub const fn max_pattern_len(&self) -> usize {
201 self.max_pattern_len
202 }
203
204 pub const fn recurses_control_flow(&self) -> bool {
205 self.recurse_control_flow
206 }
207
208 pub const fn skips_labeled_ops(&self) -> bool {
209 self.skip_labeled_ops
210 }
211
212 pub const fn preserve_two_qubit_connectivity(&self) -> bool {
215 self.preserve_two_qubit_connectivity
216 }
217
218 pub const fn mode(&self) -> RewriteMode {
219 self.mode
220 }
221
222 pub fn enabled_kinds(&self) -> &[RuleKind] {
224 &self.enabled_kinds
225 }
226
227 pub fn target_instruction_basis(&self) -> Option<Vec<Instruction>> {
229 self.target_instructions.as_ref().map(|instructions| {
230 instructions
231 .iter()
232 .map(|instruction| match instruction {
233 TargetInstruction::Standard(gate) => Instruction::Standard(*gate),
234 TargetInstruction::McGate(gate) => Instruction::McGate(Box::new(gate.clone())),
235 })
236 .collect()
237 })
238 }
239
240 pub(super) fn target_instructions(&self) -> Option<&[TargetInstruction]> {
241 self.target_instructions.as_deref()
242 }
243
244 pub(super) fn enables_kind(&self, kind: RuleKind) -> bool {
245 self.enabled_kinds.contains(&kind)
246 }
247
248 pub(super) fn allows_kind(&self, kind: RuleKind) -> bool {
249 if !self.enables_kind(kind) {
250 return false;
251 }
252
253 !matches!(kind, RuleKind::Decompose | RuleKind::HardwareNative)
254 || self.mode == RewriteMode::Lowering
255 }
256
257 pub(super) fn allows_rewrite(
258 &self,
259 kind: RuleKind,
260 before: LocalRewriteCost,
261 after: LocalRewriteCost,
262 ) -> bool {
263 if after.unsupported_ops > before.unsupported_ops {
264 return false;
265 }
266 if after < before {
267 return true;
268 }
269 if self.target_instructions.is_some() {
270 return false;
271 }
272
273 self.mode == RewriteMode::Lowering
274 && matches!(kind, RuleKind::Decompose | RuleKind::HardwareNative)
275 }
276}
277
278#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
280pub(super) struct LocalRewriteCost {
281 pub(super) unsupported_ops: usize,
282 pub(super) lowering_distance: usize,
283 pub(super) two_qubit_ops: usize,
284 pub(super) multi_qubit_ops: usize,
285 pub(super) depth_estimate: usize,
286 pub(super) total_ops: usize,
287 pub(super) parameterized_ops: usize,
288 pub(super) parameter_count: usize,
291}
292
293#[derive(Debug, Clone, Copy, PartialEq, Eq)]
295pub(super) enum GPhaseCost {
296 ExplicitOperation,
297 ImplicitReplacement,
298}
299
300impl LocalRewriteCost {
301 pub(super) fn add_gate_like(
302 &mut self,
303 standard_gate: Option<StandardGate>,
304 target_supported: bool,
305 qubit_count: usize,
306 param_count: usize,
307 gphase_cost: GPhaseCost,
308 ) -> bool {
309 if standard_gate == Some(StandardGate::GPhase)
310 && gphase_cost == GPhaseCost::ImplicitReplacement
311 {
312 return false;
313 }
314
315 self.total_ops += 1;
316 if !target_supported {
317 self.unsupported_ops += 1;
318 }
319 match qubit_count {
320 0 | 1 => {}
321 2 => self.two_qubit_ops += 1,
322 _ => self.multi_qubit_ops += 1,
323 }
324 if param_count > 0 {
325 self.parameterized_ops += 1;
326 }
327 self.parameter_count += param_count;
328 true
329 }
330}