1use crate::circuit::Instruction;
20use crate::compile::knowledge::matcher::KnowledgeInstructionKey;
21use crate::compile::knowledge::rule::{Rule, RuleValidationError};
22use crate::compile::knowledge::rule_dsl::load::{
23 LoadError, load_rules_from_file, load_rules_from_str,
24};
25use smallvec::SmallVec;
26use std::collections::{BTreeSet, HashMap};
27use std::path::Path;
28use std::sync::OnceLock;
29
30static BUILTIN_RULES: OnceLock<Result<RuleLibrary, RuleLibraryError>> = OnceLock::new();
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34pub struct RuleId(usize);
35
36impl RuleId {
37 pub const fn as_usize(self) -> usize {
39 self.0
40 }
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
45pub enum RuleKind {
46 Simplify,
48 Cancel,
50 Merge,
52 Commute,
54 Decompose,
56 Canonicalize,
58 HardwareNative,
60 Other,
62}
63
64#[derive(Debug, Clone)]
66pub struct RuleMetadata {
67 pub id: RuleId,
69 pub kind: RuleKind,
71 pub pattern_len: usize,
73 pub rewrite_len: usize,
75 pub qubit_count: usize,
77 pub first_instruction: Instruction,
79 pub cost_delta: isize,
81 pub has_conditions: bool,
83}
84
85#[derive(Debug, Clone, Default)]
86struct RuleGateMetadata {
87 match_instruction_keys: SmallVec<[KnowledgeInstructionKey; 4]>,
88 rewrite_instruction_keys: SmallVec<[KnowledgeInstructionKey; 4]>,
89}
90
91#[derive(Debug, Clone, PartialEq, thiserror::Error)]
93pub enum RuleLibraryError {
94 #[error("invalid rule {name}: {source}")]
96 InvalidRule {
97 name: String,
99 source: RuleValidationError,
101 },
102 #[error("duplicate rule name: {0}")]
104 DuplicateRuleName(String),
105 #[error("unsupported instruction for rule library index: {instruction}")]
107 UnsupportedInstruction { instruction: String },
108 #[error("failed to load rules: {0}")]
110 Load(LoadError),
111}
112
113#[derive(Debug, Clone, Default)]
115pub struct RuleLibrary {
116 rules: Vec<Rule>,
117 metadata: Vec<RuleMetadata>,
118 gate_metadata: Vec<RuleGateMetadata>,
119 name_map: HashMap<String, RuleId>,
120 first_instruction_map: Vec<(KnowledgeInstructionKey, SmallVec<[RuleId; 8]>)>,
121 kind_map: HashMap<RuleKind, SmallVec<[RuleId; 8]>>,
122}
123
124impl RuleLibrary {
125 pub fn new() -> Self {
127 Self::default()
128 }
129
130 pub fn builtin_rules() -> Result<&'static RuleLibrary, RuleLibraryError> {
135 match BUILTIN_RULES.get_or_init(|| {
136 let mut library = RuleLibrary::new();
137
138 for (source, kind) in BUILTIN_RULE_SOURCES {
139 let rules = load_rules_from_str(source).map_err(RuleLibraryError::Load)?;
140 library.extend_rules_with_validation(rules, *kind, false)?;
141 }
142
143 Ok(library)
144 }) {
145 Ok(library) => Ok(library),
146 Err(err) => Err(err.clone()),
147 }
148 }
149
150 pub fn from_rules(rules: Vec<Rule>, kind: RuleKind) -> Result<Self, RuleLibraryError> {
154 let mut library = Self::new();
155 library.extend_rules(rules, kind)?;
156 Ok(library)
157 }
158
159 pub fn from_dsl_str(source: &str, kind: RuleKind) -> Result<Self, RuleLibraryError> {
161 Self::from_rules(
162 load_rules_from_str(source).map_err(RuleLibraryError::Load)?,
163 kind,
164 )
165 }
166
167 pub fn from_dsl_file(path: impl AsRef<Path>, kind: RuleKind) -> Result<Self, RuleLibraryError> {
169 Self::from_rules(
170 load_rules_from_file(path).map_err(RuleLibraryError::Load)?,
171 kind,
172 )
173 }
174
175 pub fn add_rule(
181 &mut self,
182 rule: Rule,
183 kind: RuleKind,
184 validate: bool,
185 ) -> Result<RuleId, RuleLibraryError> {
186 if validate {
187 rule.validate()
188 .map_err(|source| RuleLibraryError::InvalidRule {
189 name: rule.name.clone(),
190 source,
191 })?;
192 }
193
194 if self.name_map.contains_key(&rule.name) {
195 return Err(RuleLibraryError::DuplicateRuleName(rule.name));
196 }
197
198 let id = RuleId(self.rules.len());
199 let first_operation =
200 rule.operations
201 .first()
202 .ok_or_else(|| RuleLibraryError::InvalidRule {
203 name: rule.name.clone(),
204 source: RuleValidationError::EmptyMatch,
205 })?;
206 let first_instruction_key = KnowledgeInstructionKey::from_instruction(
207 &first_operation.instruction,
208 )
209 .ok_or_else(|| RuleLibraryError::UnsupportedInstruction {
210 instruction: first_operation.instruction.to_string(),
211 })?;
212 let pattern_len = rule.operations.len();
213 let rewrite_len = rule.target.len();
214 let metadata = RuleMetadata {
215 id,
216 kind,
217 pattern_len,
218 rewrite_len,
219 qubit_count: rule
220 .operations
221 .iter()
222 .chain(&rule.target)
223 .flat_map(|item| item.qubits.iter().copied())
224 .collect::<BTreeSet<_>>()
225 .len(),
226 first_instruction: first_operation.instruction.clone(),
227 cost_delta: rewrite_len as isize - pattern_len as isize,
228 has_conditions: rule
229 .conditions
230 .as_ref()
231 .is_some_and(|conditions| !conditions.is_empty()),
232 };
233 let gate_metadata = build_gate_metadata(&rule)?;
234
235 self.name_map.insert(rule.name.clone(), id);
236 if let Some((_, ids)) = self
237 .first_instruction_map
238 .iter_mut()
239 .find(|(key, _)| key == &first_instruction_key)
240 {
241 ids.push(id);
242 } else {
243 self.first_instruction_map
244 .push((first_instruction_key, SmallVec::from_vec(vec![id])));
245 }
246 self.kind_map.entry(kind).or_default().push(id);
247 self.metadata.push(metadata);
248 self.gate_metadata.push(gate_metadata);
249 self.rules.push(rule);
250
251 Ok(id)
252 }
253
254 pub fn extend_rules(
259 &mut self,
260 rules: Vec<Rule>,
261 kind: RuleKind,
262 ) -> Result<SmallVec<[RuleId; 8]>, RuleLibraryError> {
263 self.extend_rules_with_validation(rules, kind, true)
264 }
265
266 fn extend_rules_with_validation(
267 &mut self,
268 rules: Vec<Rule>,
269 kind: RuleKind,
270 validate: bool,
271 ) -> Result<SmallVec<[RuleId; 8]>, RuleLibraryError> {
272 let mut updated = self.clone();
273 let mut ids = SmallVec::new();
274
275 for rule in rules {
276 ids.push(updated.add_rule(rule, kind, validate)?);
277 }
278
279 *self = updated;
280 Ok(ids)
281 }
282
283 pub fn len(&self) -> usize {
285 self.rules.len()
286 }
287
288 pub fn is_empty(&self) -> bool {
290 self.rules.is_empty()
291 }
292
293 pub fn rules(&self) -> &[Rule] {
295 &self.rules
296 }
297
298 pub fn get(&self, id: RuleId) -> Option<&Rule> {
300 self.rules.get(id.0)
301 }
302
303 pub fn metadata(&self, id: RuleId) -> Option<&RuleMetadata> {
305 self.metadata.get(id.0)
306 }
307
308 pub fn id_by_name(&self, name: &str) -> Option<RuleId> {
310 self.name_map.get(name).copied()
311 }
312
313 pub fn get_by_name(&self, name: &str) -> Option<&Rule> {
315 self.id_by_name(name).and_then(|id| self.get(id))
316 }
317
318 pub fn contains(&self, name: &str) -> bool {
320 self.name_map.contains_key(name)
321 }
322
323 pub fn candidates_for_first_instruction(
325 &self,
326 instruction: &Instruction,
327 ) -> Result<&[RuleId], RuleLibraryError> {
328 let key = KnowledgeInstructionKey::from_instruction(instruction).ok_or_else(|| {
329 RuleLibraryError::UnsupportedInstruction {
330 instruction: instruction.to_string(),
331 }
332 })?;
333
334 Ok(self
335 .first_instruction_map
336 .iter()
337 .find(|(candidate, _)| candidate == &key)
338 .map(|(_, ids)| ids.as_slice())
339 .unwrap_or(&[]))
340 }
341
342 pub fn rules_by_kind(&self, kind: RuleKind) -> &[RuleId] {
344 self.kind_map
345 .get(&kind)
346 .map(SmallVec::as_slice)
347 .unwrap_or(&[])
348 }
349
350 pub fn filter_rule_ids_by_instruction_keys(
356 &self,
357 op_instructions: &[Instruction],
358 target_instructions: &[Instruction],
359 ) -> Result<SmallVec<[RuleId; 16]>, RuleLibraryError> {
360 let op_keys = op_instructions
361 .iter()
362 .map(|instruction| {
363 KnowledgeInstructionKey::from_instruction(instruction).ok_or_else(|| {
364 RuleLibraryError::UnsupportedInstruction {
365 instruction: instruction.to_string(),
366 }
367 })
368 })
369 .collect::<Result<SmallVec<[_; 8]>, _>>()?;
370 let target_keys = target_instructions
371 .iter()
372 .map(|instruction| {
373 KnowledgeInstructionKey::from_instruction(instruction).ok_or_else(|| {
374 RuleLibraryError::UnsupportedInstruction {
375 instruction: instruction.to_string(),
376 }
377 })
378 })
379 .collect::<Result<SmallVec<[_; 8]>, _>>()?;
380
381 let mut ids = SmallVec::new();
382
383 for (index, metadata) in self.gate_metadata.iter().enumerate() {
384 let match_supported = metadata
385 .match_instruction_keys
386 .iter()
387 .all(|required| op_keys.iter().any(|available| available == required));
388 let rewrite_supported = metadata
389 .rewrite_instruction_keys
390 .iter()
391 .all(|required| target_keys.iter().any(|available| available == required));
392
393 if match_supported && rewrite_supported {
394 ids.push(RuleId(index));
395 }
396 }
397
398 Ok(ids)
399 }
400}
401
402fn build_gate_metadata(rule: &Rule) -> Result<RuleGateMetadata, RuleLibraryError> {
403 let mut match_instruction_keys = SmallVec::new();
404 let mut rewrite_instruction_keys = SmallVec::new();
405
406 for item in &rule.operations {
407 match_instruction_keys.push(
408 KnowledgeInstructionKey::from_instruction(&item.instruction).ok_or_else(|| {
409 RuleLibraryError::UnsupportedInstruction {
410 instruction: item.instruction.to_string(),
411 }
412 })?,
413 );
414 }
415
416 for item in &rule.target {
417 rewrite_instruction_keys.push(
418 KnowledgeInstructionKey::from_instruction(&item.instruction).ok_or_else(|| {
419 RuleLibraryError::UnsupportedInstruction {
420 instruction: item.instruction.to_string(),
421 }
422 })?,
423 );
424 }
425
426 Ok(RuleGateMetadata {
427 match_instruction_keys,
428 rewrite_instruction_keys,
429 })
430}
431
432const BUILTIN_RULE_SOURCES: &[(&str, RuleKind)] = &[
433 (include_str!("rules/normalize.rule"), RuleKind::Canonicalize),
434 (include_str!("rules/cancel.rule"), RuleKind::Cancel),
435 (include_str!("rules/merge.rule"), RuleKind::Merge),
436 (include_str!("rules/identity.rule"), RuleKind::Simplify),
437 (include_str!("rules/specialize.rule"), RuleKind::Simplify),
438 (include_str!("rules/commutation.rule"), RuleKind::Commute),
439 (
440 include_str!("rules/decompose_ccx.rule"),
441 RuleKind::Decompose,
442 ),
443 (
444 include_str!("rules/decompose_controlled_pauli.rule"),
445 RuleKind::Decompose,
446 ),
447 (
448 include_str!("rules/decompose_controlled_rotation.rule"),
449 RuleKind::Decompose,
450 ),
451 (
452 include_str!("rules/decompose_mc_gate.rule"),
453 RuleKind::Decompose,
454 ),
455 (
456 include_str!("rules/decompose_fsim.rule"),
457 RuleKind::Decompose,
458 ),
459 (
460 include_str!("rules/decompose_ising.rule"),
461 RuleKind::Decompose,
462 ),
463 (
464 include_str!("rules/decompose_phase.rule"),
465 RuleKind::Decompose,
466 ),
467 (
468 include_str!("rules/decompose_qcis.rule"),
469 RuleKind::Decompose,
470 ),
471 (
472 include_str!("rules/decompose_single_clifford.rule"),
473 RuleKind::Decompose,
474 ),
475 (
476 include_str!("rules/decompose_single_rotation.rule"),
477 RuleKind::Decompose,
478 ),
479 (
480 include_str!("rules/decompose_swap.rule"),
481 RuleKind::Decompose,
482 ),
483];
484
485#[cfg(test)]
486#[path = "./library_test.rs"]
487mod library_test;
488
489#[cfg(test)]
490#[path = "builtin_rules_contract_test.rs"]
491mod builtin_rules_contract_test;