quil_rs/instruction/
calibration.rs

1use crate::{
2    instruction::{
3        write_expression_parameter_string, write_instruction_block, Expression, GateModifier,
4        Instruction, Qubit,
5    },
6    quil::{Quil, INDENT},
7    validation::identifier::{validate_identifier, IdentifierValidationError},
8};
9
10use super::{write_qubit_parameters, Gate};
11
12pub trait CalibrationSignature {
13    type Signature<'a>
14    where
15        Self: 'a;
16
17    fn signature(&self) -> Self::Signature<'_>;
18    fn has_signature(&self, signature: &Self::Signature<'_>) -> bool;
19}
20
21#[derive(Clone, Debug, Default, PartialEq)]
22pub struct Calibration {
23    pub identifier: CalibrationIdentifier,
24    pub instructions: Vec<Instruction>,
25}
26
27impl Calibration {
28    /// Builds a new calibration definition.
29    pub fn new(
30        identifier: CalibrationIdentifier,
31        instructions: Vec<Instruction>,
32    ) -> Result<Self, IdentifierValidationError> {
33        Ok(Self {
34            identifier,
35            instructions,
36        })
37    }
38}
39
40impl CalibrationSignature for Calibration {
41    type Signature<'a> = (&'a str, &'a [Expression], &'a [Qubit]);
42
43    fn signature(&self) -> Self::Signature<'_> {
44        self.identifier.signature()
45    }
46
47    fn has_signature(&self, signature: &Self::Signature<'_>) -> bool {
48        self.identifier.has_signature(signature)
49    }
50}
51
52impl Quil for Calibration {
53    fn write(
54        &self,
55        f: &mut impl std::fmt::Write,
56        fall_back_to_debug: bool,
57    ) -> crate::quil::ToQuilResult<()> {
58        self.identifier.write(f, fall_back_to_debug)?;
59        write!(f, ":")?;
60        for instruction in &self.instructions {
61            write!(f, "\n{INDENT}")?;
62            instruction.write(f, fall_back_to_debug)?;
63        }
64        Ok(())
65    }
66}
67
68/// Unique identifier for a calibration definition within a program
69#[derive(Clone, Debug, Default, PartialEq)]
70pub struct CalibrationIdentifier {
71    /// The modifiers applied to the gate
72    pub modifiers: Vec<GateModifier>,
73
74    /// The name of the gate
75    pub name: String,
76
77    /// The parameters of the gate - these are the variables in the calibration definition
78    pub parameters: Vec<Expression>,
79
80    /// The qubits on which the gate is applied
81    pub qubits: Vec<Qubit>,
82}
83
84impl CalibrationIdentifier {
85    /// Builds a new calibration identifier.
86    ///
87    /// # Errors
88    ///
89    /// Returns an error if the given name isn't a valid Quil identifier.
90    pub fn new(
91        name: String,
92        modifiers: Vec<GateModifier>,
93        parameters: Vec<Expression>,
94        qubits: Vec<Qubit>,
95    ) -> Result<Self, IdentifierValidationError> {
96        validate_identifier(name.as_str())?;
97        Ok(Self {
98            modifiers,
99            name,
100            parameters,
101            qubits,
102        })
103    }
104
105    pub fn matches(&self, gate: &Gate) -> bool {
106        // Filter out non-matching calibrations: check rules 1-4
107        if self.name != gate.name
108            || self.modifiers != gate.modifiers
109            || self.parameters.len() != gate.parameters.len()
110            || self.qubits.len() != gate.qubits.len()
111        {
112            return false;
113        }
114
115        let fixed_qubits_match = self
116            .qubits
117            .iter()
118            .enumerate()
119            .all(|(calibration_index, _)| {
120                match (
121                    &self.qubits[calibration_index],
122                    &gate.qubits[calibration_index],
123                ) {
124                    // Placeholders never match
125                    (Qubit::Placeholder(_), _) | (_, Qubit::Placeholder(_)) => false,
126                    // If they're both fixed, test if they're fixed to the same qubit
127                    (Qubit::Fixed(calibration_fixed_qubit), Qubit::Fixed(gate_fixed_qubit)) => {
128                        calibration_fixed_qubit == gate_fixed_qubit
129                    }
130                    // If the calibration is variable, it matches any fixed qubit
131                    (Qubit::Variable(_), _) => true,
132                    // If the calibration is fixed, but the gate's qubit is variable, it's not a match
133                    (Qubit::Fixed(_), _) => false,
134                }
135            });
136        if !fixed_qubits_match {
137            return false;
138        }
139
140        let fixed_parameters_match =
141            self.parameters
142                .iter()
143                .enumerate()
144                .all(|(calibration_index, _)| {
145                    let calibration_parameters =
146                        self.parameters[calibration_index].clone().into_simplified();
147                    let gate_parameters =
148                        gate.parameters[calibration_index].clone().into_simplified();
149                    match (calibration_parameters, gate_parameters) {
150                        // If the calibration is variable, it matches any fixed qubit
151                        (Expression::Variable(_), _) => true,
152                        // If the calibration is fixed, but the gate's qubit is variable, it's not a match
153                        (calib, gate) => calib == gate,
154                    }
155                });
156        fixed_parameters_match
157    }
158}
159
160impl CalibrationSignature for CalibrationIdentifier {
161    type Signature<'a> = (&'a str, &'a [Expression], &'a [Qubit]);
162
163    fn signature(&self) -> Self::Signature<'_> {
164        (
165            self.name.as_str(),
166            self.parameters.as_slice(),
167            self.qubits.as_slice(),
168        )
169    }
170
171    fn has_signature(&self, signature: &Self::Signature<'_>) -> bool {
172        let (name, parameters, qubits) = signature;
173        self.name == *name && self.parameters == *parameters && self.qubits == *qubits
174    }
175}
176
177impl Quil for CalibrationIdentifier {
178    fn write(
179        &self,
180        f: &mut impl std::fmt::Write,
181        fall_back_to_debug: bool,
182    ) -> crate::quil::ToQuilResult<()> {
183        write!(f, "DEFCAL {}", self.name)?;
184        write_expression_parameter_string(f, fall_back_to_debug, &self.parameters)?;
185        write_qubit_parameters(f, fall_back_to_debug, &self.qubits)?;
186        Ok(())
187    }
188}
189
190#[derive(Clone, Debug, PartialEq)]
191pub struct MeasureCalibrationDefinition {
192    pub identifier: MeasureCalibrationIdentifier,
193    pub instructions: Vec<Instruction>,
194}
195
196impl MeasureCalibrationDefinition {
197    pub fn new(identifier: MeasureCalibrationIdentifier, instructions: Vec<Instruction>) -> Self {
198        Self {
199            identifier,
200            instructions,
201        }
202    }
203}
204
205impl CalibrationSignature for MeasureCalibrationDefinition {
206    type Signature<'a> = (Option<&'a Qubit>, &'a str);
207
208    fn signature(&self) -> Self::Signature<'_> {
209        self.identifier.signature()
210    }
211
212    fn has_signature(&self, signature: &Self::Signature<'_>) -> bool {
213        self.identifier.has_signature(signature)
214    }
215}
216
217impl Quil for MeasureCalibrationDefinition {
218    fn write(
219        &self,
220        f: &mut impl std::fmt::Write,
221        fall_back_to_debug: bool,
222    ) -> crate::quil::ToQuilResult<()> {
223        self.identifier.write(f, fall_back_to_debug)?;
224        writeln!(f, ":")?;
225
226        write_instruction_block(f, fall_back_to_debug, &self.instructions)?;
227        writeln!(f)?;
228        Ok(())
229    }
230}
231
232/// A unique identifier for a measurement calibration definition within a program
233#[derive(Clone, Debug, Default, PartialEq)]
234pub struct MeasureCalibrationIdentifier {
235    /// The qubit which is the target of measurement, if any
236    pub qubit: Option<Qubit>,
237
238    /// The memory region name to which the measurement result is written
239    pub parameter: String,
240}
241
242impl MeasureCalibrationIdentifier {
243    pub fn new(qubit: Option<Qubit>, parameter: String) -> Self {
244        Self { qubit, parameter }
245    }
246}
247
248impl CalibrationSignature for MeasureCalibrationIdentifier {
249    type Signature<'a> = (Option<&'a Qubit>, &'a str);
250
251    fn signature(&self) -> Self::Signature<'_> {
252        (self.qubit.as_ref(), self.parameter.as_str())
253    }
254
255    fn has_signature(&self, signature: &Self::Signature<'_>) -> bool {
256        let (qubit, parameter) = signature;
257        self.qubit.as_ref() == *qubit && self.parameter == *parameter
258    }
259}
260
261impl Quil for MeasureCalibrationIdentifier {
262    fn write(
263        &self,
264        f: &mut impl std::fmt::Write,
265        fall_back_to_debug: bool,
266    ) -> crate::quil::ToQuilResult<()> {
267        write!(f, "DEFCAL MEASURE")?;
268        if let Some(qubit) = &self.qubit {
269            write!(f, " ")?;
270            qubit.write(f, fall_back_to_debug)?;
271        }
272        write!(f, " {}", self.parameter,)?;
273        Ok(())
274    }
275}
276
277#[cfg(test)]
278mod test_measure_calibration_definition {
279    use super::MeasureCalibrationDefinition;
280    use crate::expression::Expression;
281    use crate::instruction::calibration::MeasureCalibrationIdentifier;
282    use crate::instruction::{Gate, Instruction, Qubit};
283    use crate::quil::Quil;
284    use insta::assert_snapshot;
285    use rstest::rstest;
286
287    #[rstest]
288    #[case(
289        "With Fixed Qubit",
290        MeasureCalibrationDefinition {
291            identifier: MeasureCalibrationIdentifier {
292                qubit: Some(Qubit::Fixed(0)),
293                parameter: "theta".to_string(),
294            },
295            instructions: vec![Instruction::Gate(Gate {
296                name: "X".to_string(),
297                parameters: vec![Expression::Variable("theta".to_string())],
298                qubits: vec![Qubit::Fixed(0)],
299                modifiers: vec![],
300
301            })]},
302    )]
303    #[case(
304        "With Variable Qubit",
305        MeasureCalibrationDefinition {
306            identifier: MeasureCalibrationIdentifier {
307                qubit: Some(Qubit::Variable("q".to_string())),
308                parameter: "theta".to_string(),
309            },
310            instructions: vec![Instruction::Gate(Gate {
311                name: "X".to_string(),
312                parameters: vec![Expression::Variable("theta".to_string())],
313                qubits: vec![Qubit::Variable("q".to_string())],
314                modifiers: vec![],
315            })]},
316    )]
317    fn test_display(
318        #[case] description: &str,
319        #[case] measure_cal_def: MeasureCalibrationDefinition,
320    ) {
321        insta::with_settings!({
322            snapshot_suffix => description,
323        }, {
324            assert_snapshot!(measure_cal_def.to_quil_or_debug())
325        })
326    }
327}