1#[cfg(not(feature = "python"))]
2use optipy::strip_pyo3;
3#[cfg(feature = "stubs")]
4use pyo3_stub_gen::derive::gen_stub_pyclass;
5
6use crate::{
7 instruction::{
8 write_expression_parameter_string, write_instruction_block, Expression, GateModifier,
9 Instruction, Qubit,
10 },
11 pickleable_new,
12 quil::{Quil, INDENT},
13 validation::identifier::{validate_identifier, IdentifierValidationError},
14};
15
16use super::{write_qubit_parameters, Gate};
17
18pub trait CalibrationSignature {
19 type Signature<'a>
20 where
21 Self: 'a;
22
23 fn signature(&self) -> Self::Signature<'_>;
24 fn has_signature(&self, signature: &Self::Signature<'_>) -> bool;
25}
26
27#[derive(Clone, Debug, PartialEq)]
28#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
29#[cfg_attr(
30 feature = "python",
31 pyo3::pyclass(module = "quil.instructions", eq, get_all, set_all, subclass)
32)]
33#[cfg_attr(not(feature = "python"), strip_pyo3)]
34pub struct CalibrationDefinition {
35 #[pyo3(name = "identifier")]
36 pub identifier: CalibrationIdentifier,
37 pub instructions: Vec<Instruction>,
38}
39
40pickleable_new! {
41 impl CalibrationDefinition {
42 pub fn new(
44 identifier: CalibrationIdentifier,
45 instructions: Vec<Instruction>,
46 );
47 }
48}
49
50impl CalibrationSignature for CalibrationDefinition {
51 type Signature<'a> = <CalibrationIdentifier as CalibrationSignature>::Signature<'a>;
52
53 fn signature(&self) -> Self::Signature<'_> {
54 self.identifier.signature()
55 }
56
57 fn has_signature(&self, signature: &Self::Signature<'_>) -> bool {
58 self.identifier.has_signature(signature)
59 }
60}
61
62impl Quil for CalibrationDefinition {
63 fn write(
64 &self,
65 f: &mut impl std::fmt::Write,
66 fall_back_to_debug: bool,
67 ) -> crate::quil::ToQuilResult<()> {
68 self.identifier.write(f, fall_back_to_debug)?;
69 write!(f, ":")?;
70 for instruction in &self.instructions {
71 write!(f, "\n{INDENT}")?;
72 instruction.write(f, fall_back_to_debug)?;
73 }
74 Ok(())
75 }
76}
77
78#[derive(Clone, Debug, PartialEq)]
80#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
81#[cfg_attr(
82 feature = "python",
83 pyo3::pyclass(module = "quil.instructions", eq, get_all, set_all, subclass)
84)]
85pub struct CalibrationIdentifier {
86 pub modifiers: Vec<GateModifier>,
88
89 pub name: String,
91
92 pub parameters: Vec<Expression>,
94
95 pub qubits: Vec<Qubit>,
97}
98
99impl CalibrationIdentifier {
100 pub fn new(
106 name: String,
107 modifiers: Vec<GateModifier>,
108 parameters: Vec<Expression>,
109 qubits: Vec<Qubit>,
110 ) -> Result<Self, IdentifierValidationError> {
111 validate_identifier(name.as_str())?;
112 Ok(Self {
113 modifiers,
114 name,
115 parameters,
116 qubits,
117 })
118 }
119}
120
121impl CalibrationIdentifier {
122 pub fn matches(&self, gate: &Gate) -> bool {
123 if self.name != gate.name
125 || self.modifiers != gate.modifiers
126 || self.parameters.len() != gate.parameters.len()
127 || self.qubits.len() != gate.qubits.len()
128 {
129 return false;
130 }
131
132 let fixed_qubits_match = self
133 .qubits
134 .iter()
135 .enumerate()
136 .all(|(calibration_index, _)| {
137 match (
138 &self.qubits[calibration_index],
139 &gate.qubits[calibration_index],
140 ) {
141 (Qubit::Placeholder(_), _) | (_, Qubit::Placeholder(_)) => false,
143 (Qubit::Fixed(calibration_fixed_qubit), Qubit::Fixed(gate_fixed_qubit)) => {
145 calibration_fixed_qubit == gate_fixed_qubit
146 }
147 (Qubit::Variable(_), _) => true,
149 (Qubit::Fixed(_), _) => false,
151 }
152 });
153 if !fixed_qubits_match {
154 return false;
155 }
156
157 let fixed_parameters_match =
158 self.parameters
159 .iter()
160 .enumerate()
161 .all(|(calibration_index, _)| {
162 let calibration_parameters =
163 self.parameters[calibration_index].clone().into_simplified();
164 let gate_parameters =
165 gate.parameters[calibration_index].clone().into_simplified();
166 match (calibration_parameters, gate_parameters) {
167 (Expression::Variable(_), _) => true,
169 (calib, gate) => calib == gate,
171 }
172 });
173 fixed_parameters_match
174 }
175}
176
177impl CalibrationSignature for CalibrationIdentifier {
178 type Signature<'a> = (&'a [GateModifier], &'a str, &'a [Expression], &'a [Qubit]);
179
180 fn signature(&self) -> Self::Signature<'_> {
181 let Self {
182 modifiers,
183 name,
184 parameters,
185 qubits,
186 } = self;
187 (
188 modifiers.as_slice(),
189 name.as_str(),
190 parameters.as_slice(),
191 qubits.as_slice(),
192 )
193 }
194
195 fn has_signature(&self, signature: &Self::Signature<'_>) -> bool {
196 &self.signature() == signature
197 }
198}
199
200impl Quil for CalibrationIdentifier {
201 fn write(
202 &self,
203 f: &mut impl std::fmt::Write,
204 fall_back_to_debug: bool,
205 ) -> crate::quil::ToQuilResult<()> {
206 write!(f, "DEFCAL {}", self.name)?;
207 write_expression_parameter_string(f, fall_back_to_debug, &self.parameters)?;
208 write_qubit_parameters(f, fall_back_to_debug, &self.qubits)?;
209 Ok(())
210 }
211}
212
213#[derive(Clone, Debug, PartialEq)]
214#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
215#[cfg_attr(
216 feature = "python",
217 pyo3::pyclass(module = "quil.instructions", eq, get_all, set_all, subclass)
218)]
219pub struct MeasureCalibrationDefinition {
220 pub identifier: MeasureCalibrationIdentifier,
221 pub instructions: Vec<Instruction>,
222}
223
224pickleable_new! {
225 impl MeasureCalibrationDefinition {
226 pub fn new(identifier: MeasureCalibrationIdentifier, instructions: Vec<Instruction>);
227 }
228}
229
230impl CalibrationSignature for MeasureCalibrationDefinition {
231 type Signature<'a> = <MeasureCalibrationIdentifier as CalibrationSignature>::Signature<'a>;
232
233 fn signature(&self) -> Self::Signature<'_> {
234 self.identifier.signature()
235 }
236
237 fn has_signature(&self, signature: &Self::Signature<'_>) -> bool {
238 self.identifier.has_signature(signature)
239 }
240}
241
242impl Quil for MeasureCalibrationDefinition {
243 fn write(
244 &self,
245 f: &mut impl std::fmt::Write,
246 fall_back_to_debug: bool,
247 ) -> crate::quil::ToQuilResult<()> {
248 self.identifier.write(f, fall_back_to_debug)?;
249 writeln!(f, ":")?;
250
251 write_instruction_block(f, fall_back_to_debug, &self.instructions)?;
252 writeln!(f)?;
253 Ok(())
254 }
255}
256
257#[derive(Clone, Debug, PartialEq)]
259#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
260#[cfg_attr(
261 feature = "python",
262 pyo3::pyclass(module = "quil.instructions", eq, get_all, set_all, subclass)
263)]
264pub struct MeasureCalibrationIdentifier {
265 pub name: Option<String>,
267
268 pub qubit: Qubit,
270
271 pub target: Option<String>,
276}
277
278impl MeasureCalibrationIdentifier {
279 pub const fn new(name: Option<String>, qubit: Qubit, target: Option<String>) -> Self {
280 Self {
281 name,
282 qubit,
283 target,
284 }
285 }
286}
287
288impl CalibrationSignature for MeasureCalibrationIdentifier {
289 type Signature<'a> = (Option<&'a str>, &'a Qubit, Option<&'a str>);
290
291 fn signature(&self) -> Self::Signature<'_> {
292 let Self {
293 name,
294 qubit,
295 target,
296 } = self;
297
298 (name.as_deref(), qubit, target.as_deref())
299 }
300
301 fn has_signature(&self, signature: &Self::Signature<'_>) -> bool {
302 &self.signature() == signature
303 }
304}
305
306impl Quil for MeasureCalibrationIdentifier {
307 fn write(
308 &self,
309 f: &mut impl std::fmt::Write,
310 fall_back_to_debug: bool,
311 ) -> crate::quil::ToQuilResult<()> {
312 let Self {
313 name,
314 qubit,
315 target,
316 } = self;
317
318 write!(f, "DEFCAL MEASURE")?;
319 if let Some(name) = name {
320 write!(f, "!{name}")?;
321 }
322 write!(f, " ")?;
323 qubit.write(f, fall_back_to_debug)?;
324 if let Some(target) = target {
325 write!(f, " {target}")?;
326 }
327
328 Ok(())
329 }
330}
331
332#[cfg(test)]
333mod test_measure_calibration_definition {
334 use super::MeasureCalibrationDefinition;
335 use crate::expression::Expression;
336 use crate::instruction::calibration::MeasureCalibrationIdentifier;
337 use crate::instruction::{Gate, Instruction, Qubit};
338 use crate::quil::Quil;
339 use insta::assert_snapshot;
340 use rstest::rstest;
341
342 #[rstest]
343 #[case(
344 "With Fixed Qubit",
345 MeasureCalibrationDefinition {
346 identifier: MeasureCalibrationIdentifier {
347 name: None,
348 qubit: Qubit::Fixed(0),
349 target: Some("theta".to_string()),
350 },
351 instructions: vec![Instruction::Gate(Gate {
352 name: "X".to_string(),
353 parameters: vec![Expression::Variable("theta".to_string())],
354 qubits: vec![Qubit::Fixed(0)],
355 modifiers: vec![],
356
357 })]},
358 )]
359 #[case(
360 "Named With Fixed Qubit",
361 MeasureCalibrationDefinition {
362 identifier: MeasureCalibrationIdentifier {
363 name: Some("midcircuit".to_string()),
364 qubit: Qubit::Fixed(0),
365 target: Some("theta".to_string()),
366 },
367 instructions: vec![Instruction::Gate(Gate {
368 name: "X".to_string(),
369 parameters: vec![Expression::Variable("theta".to_string())],
370 qubits: vec![Qubit::Fixed(0)],
371 modifiers: vec![],
372
373 })]},
374 )]
375 #[case(
376 "Effect With Fixed Qubit",
377 MeasureCalibrationDefinition {
378 identifier: MeasureCalibrationIdentifier {
379 name: None,
380 qubit: Qubit::Fixed(0),
381 target: None,
382 },
383 instructions: vec![Instruction::Gate(Gate {
384 name: "X".to_string(),
385 parameters: vec![Expression::PiConstant()],
386 qubits: vec![Qubit::Fixed(0)],
387 modifiers: vec![],
388
389 })]},
390 )]
391 #[case(
392 "Named Effect With Fixed Qubit",
393 MeasureCalibrationDefinition {
394 identifier: MeasureCalibrationIdentifier {
395 name: Some("midcircuit".to_string()),
396 qubit: Qubit::Fixed(0),
397 target: None,
398 },
399 instructions: vec![Instruction::Gate(Gate {
400 name: "X".to_string(),
401 parameters: vec![Expression::PiConstant()],
402 qubits: vec![Qubit::Fixed(0)],
403 modifiers: vec![],
404
405 })]},
406 )]
407 #[case(
408 "With Variable Qubit",
409 MeasureCalibrationDefinition {
410 identifier: MeasureCalibrationIdentifier {
411 name: None,
412 qubit: Qubit::Variable("q".to_string()),
413 target: Some("theta".to_string()),
414 },
415 instructions: vec![Instruction::Gate(Gate {
416 name: "X".to_string(),
417 parameters: vec![Expression::Variable("theta".to_string())],
418 qubits: vec![Qubit::Variable("q".to_string())],
419 modifiers: vec![],
420 })]},
421 )]
422 #[case(
423 "Named With Variable Qubit",
424 MeasureCalibrationDefinition {
425 identifier: MeasureCalibrationIdentifier {
426 name: Some("midcircuit".to_string()),
427 qubit: Qubit::Variable("q".to_string()),
428 target: Some("theta".to_string()),
429 },
430 instructions: vec![Instruction::Gate(Gate {
431 name: "X".to_string(),
432 parameters: vec![Expression::Variable("theta".to_string())],
433 qubits: vec![Qubit::Variable("q".to_string())],
434 modifiers: vec![],
435 })]},
436 )]
437 #[case(
438 "Effect Variable Qubit",
439 MeasureCalibrationDefinition {
440 identifier: MeasureCalibrationIdentifier {
441 name: None,
442 qubit: Qubit::Variable("q".to_string()),
443 target: None,
444 },
445 instructions: vec![Instruction::Gate(Gate {
446 name: "X".to_string(),
447 parameters: vec![Expression::PiConstant()],
448 qubits: vec![Qubit::Variable("q".to_string())],
449 modifiers: vec![],
450 })]},
451 )]
452 #[case(
453 "Named Effect Variable Qubit",
454 MeasureCalibrationDefinition {
455 identifier: MeasureCalibrationIdentifier {
456 name: Some("midcircuit".to_string()),
457 qubit: Qubit::Variable("q".to_string()),
458 target: None,
459 },
460 instructions: vec![Instruction::Gate(Gate {
461 name: "X".to_string(),
462 parameters: vec![Expression::PiConstant()],
463 qubits: vec![Qubit::Variable("q".to_string())],
464 modifiers: vec![],
465 })]},
466 )]
467 fn test_display(
468 #[case] description: &str,
469 #[case] measure_cal_def: MeasureCalibrationDefinition,
470 ) {
471 insta::with_settings!({
472 snapshot_suffix => description,
473 }, {
474 assert_snapshot!(measure_cal_def.to_quil_or_debug())
475 })
476 }
477}