cqlib_core/compile/knowledge/
rule.rs1use crate::circuit::{Instruction, MCGate, Parameter, ParameterValue, StandardGate};
20use crate::compile::PARAMETER_EQ_TOLERANCE;
21use smallvec::SmallVec;
22use std::collections::{BTreeSet, HashSet};
23
24#[derive(Debug, Clone)]
26pub struct RuleItem {
27 pub instruction: Instruction,
29 pub qubits: SmallVec<[u32; 3]>,
31 pub params: Option<SmallVec<[ParameterValue; 1]>>,
33}
34
35impl RuleItem {
36 pub fn standard(gate: StandardGate, qubits: &[u32], params: Vec<ParameterValue>) -> Self {
38 Self {
39 instruction: Instruction::Standard(gate),
40 qubits: SmallVec::from_slice(qubits),
41 params: if params.is_empty() {
42 None
43 } else {
44 Some(SmallVec::from_vec(params))
45 },
46 }
47 }
48
49 pub fn mc_gate(gate: MCGate, qubits: &[u32], params: Vec<ParameterValue>) -> Self {
51 Self {
52 instruction: Instruction::McGate(Box::new(gate)),
53 qubits: SmallVec::from_slice(qubits),
54 params: if params.is_empty() {
55 None
56 } else {
57 Some(SmallVec::from_vec(params))
58 },
59 }
60 }
61
62 pub fn symbols(&self) -> HashSet<String> {
64 let mut symbols = HashSet::new();
65 if let Some(params) = &self.params {
66 for param in params {
67 if let ParameterValue::Param(param) = param {
68 symbols.extend(param.get_symbols());
69 }
70 }
71 }
72
73 symbols
74 }
75
76 pub fn validate(&self) -> Result<(), RuleValidationError> {
78 let (instruction, expected_qubits, expected_params) = match &self.instruction {
79 Instruction::Standard(gate) => (gate.to_string(), gate.num_qubits(), gate.num_params()),
80 Instruction::McGate(gate) => (
81 format!(
82 "MC{}[{}]",
83 gate.base_gate(),
84 gate.num_qubits() - gate.base_gate().num_qubits()
85 ),
86 gate.num_qubits(),
87 gate.num_params(),
88 ),
89 other => {
90 return Err(RuleValidationError::UnsupportedInstruction {
91 instruction: other.to_string(),
92 });
93 }
94 };
95
96 if self.qubits.len() != expected_qubits {
97 return Err(RuleValidationError::WrongQubitCount {
98 instruction: instruction.clone(),
99 expected: expected_qubits,
100 got: self.qubits.len(),
101 });
102 }
103
104 let got_params = self.params.as_ref().map_or(0, SmallVec::len);
105 if got_params != expected_params {
106 return Err(RuleValidationError::WrongParamCount {
107 instruction: instruction.clone(),
108 expected: expected_params,
109 got: got_params,
110 });
111 }
112
113 let mut seen = SmallVec::<[u32; 3]>::new();
114 for &qubit in &self.qubits {
115 if seen.contains(&qubit) {
116 return Err(RuleValidationError::DuplicateQubit {
117 instruction: instruction.clone(),
118 qubit,
119 });
120 }
121 seen.push(qubit);
122 }
123
124 Ok(())
125 }
126
127 pub fn equivalent_to(&self, other: &Self) -> bool {
130 let lhs_params = self.params.as_deref().unwrap_or(&[]);
131 let rhs_params = other.params.as_deref().unwrap_or(&[]);
132 self.instruction == other.instruction
133 && self.qubits == other.qubits
134 && lhs_params.len() == rhs_params.len()
135 && lhs_params.iter().zip(rhs_params).all(|(lhs, rhs)| {
136 let lhs: Parameter = lhs.into();
137 lhs.provably_equal(&rhs.into(), PARAMETER_EQ_TOLERANCE)
138 })
139 }
140}
141
142#[derive(Debug, Clone)]
143pub enum Condition {
145 Eq(Parameter, Parameter),
147 EqMod(Parameter, Parameter, Parameter),
149}
150
151impl Condition {
152 pub fn symbols(&self) -> HashSet<String> {
154 let mut symbols = HashSet::new();
155 match self {
156 Condition::Eq(lhs, rhs) => {
157 symbols.extend(lhs.get_symbols());
158 symbols.extend(rhs.get_symbols());
159 }
160 Condition::EqMod(lhs, rhs, modulus) => {
161 symbols.extend(lhs.get_symbols());
162 symbols.extend(rhs.get_symbols());
163 symbols.extend(modulus.get_symbols());
164 }
165 }
166
167 symbols
168 }
169}
170
171#[derive(Debug, Clone)]
172pub struct Rule {
174 pub name: String,
176 pub operations: SmallVec<[RuleItem; 4]>,
178 pub conditions: Option<SmallVec<[Condition; 2]>>,
180 pub target: SmallVec<[RuleItem; 4]>,
182}
183
184#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
185pub enum RuleValidationError {
186 #[error("rule match block is empty")]
188 EmptyMatch,
189 #[error("unsupported instruction: {instruction}")]
191 UnsupportedInstruction { instruction: String },
192 #[error("wrong qubit count for {instruction}: expected {expected}, got {got}")]
194 WrongQubitCount {
195 instruction: String,
196 expected: usize,
197 got: usize,
198 },
199 #[error("wrong parameter count for {instruction}: expected {expected}, got {got}")]
201 WrongParamCount {
202 instruction: String,
203 expected: usize,
204 got: usize,
205 },
206 #[error("duplicate qubit {qubit} in {instruction}")]
208 DuplicateQubit { instruction: String, qubit: u32 },
209 #[error("rewrite qubit {qubit} is not bound by the match block")]
211 UnboundRewriteQubit { qubit: u32 },
212 #[error("rewrite symbol {symbol} is not bound by the match block")]
214 UnboundRewriteSymbol { symbol: String },
215 #[error("condition symbol {symbol} is not bound by the match block")]
217 UnboundConditionSymbol { symbol: String },
218 #[error("qubit labels must be dense from 0, found {labels:?}")]
220 NonDenseQubitLabels { labels: Vec<u32> },
221}
222
223impl Rule {
224 pub fn new(name: &str, ops: Vec<RuleItem>, target: Vec<RuleItem>) -> Rule {
226 Rule {
227 name: name.to_string(),
228 operations: SmallVec::from_vec(ops),
229 conditions: None,
230 target: SmallVec::from_vec(target),
231 }
232 }
233
234 pub fn validate(&self) -> Result<(), RuleValidationError> {
241 if self.operations.is_empty() {
242 return Err(RuleValidationError::EmptyMatch);
243 }
244
245 for item in &self.operations {
247 item.validate()?;
248 }
249 for item in &self.target {
250 item.validate()?;
251 }
252
253 let match_qubits = self.operation_qubits();
254 validate_dense_qubit_labels(&match_qubits)?;
256
257 for item in &self.target {
259 for &qubit in &item.qubits {
260 if !match_qubits.contains(&qubit) {
261 return Err(RuleValidationError::UnboundRewriteQubit { qubit });
262 }
263 }
264 }
265
266 let bound_symbols = self.operation_symbols();
267 for symbol in self.target_symbols() {
269 if !bound_symbols.contains(&symbol) {
270 return Err(RuleValidationError::UnboundRewriteSymbol { symbol });
271 }
272 }
273
274 for symbol in self.condition_symbols() {
276 if !bound_symbols.contains(&symbol) {
277 return Err(RuleValidationError::UnboundConditionSymbol { symbol });
278 }
279 }
280
281 Ok(())
282 }
283
284 pub fn num_qubits(&self) -> usize {
286 let mut op_qubits = self.operation_qubits();
287 op_qubits.extend(self.target_qubits());
288 op_qubits.len().max(1)
290 }
291
292 pub fn collect_free_symbols(&self) -> HashSet<String> {
294 let mut symbols = self.operation_symbols();
295 symbols.extend(self.target_symbols());
296 symbols.extend(self.condition_symbols());
297 symbols
298 }
299
300 pub fn target_qubits(&self) -> BTreeSet<u32> {
302 self.target
303 .iter()
304 .flat_map(|qs| qs.qubits.iter().cloned())
305 .collect()
306 }
307
308 pub fn operation_qubits(&self) -> BTreeSet<u32> {
310 self.operations
311 .iter()
312 .flat_map(|qs| qs.qubits.iter().cloned())
313 .collect()
314 }
315
316 pub fn operation_symbols(&self) -> HashSet<String> {
318 self.operations.iter().flat_map(RuleItem::symbols).collect()
319 }
320
321 pub fn target_symbols(&self) -> HashSet<String> {
323 self.target.iter().flat_map(RuleItem::symbols).collect()
324 }
325
326 pub fn condition_symbols(&self) -> HashSet<String> {
328 self.conditions
329 .iter()
330 .flatten()
331 .flat_map(Condition::symbols)
332 .collect()
333 }
334}
335
336fn validate_dense_qubit_labels(labels: &BTreeSet<u32>) -> Result<(), RuleValidationError> {
337 for (expected, actual) in labels.iter().copied().enumerate() {
338 if expected as u32 != actual {
339 return Err(RuleValidationError::NonDenseQubitLabels {
340 labels: labels.iter().copied().collect(),
341 });
342 }
343 }
344 Ok(())
345}
346
347#[cfg(test)]
348#[path = "./rule_test.rs"]
349mod rule_test;