Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions quil-rs/python/quil/instructions.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -1200,6 +1200,11 @@ class MeasureCalibrationDefinition:
@instructions.setter
def instructions(self, value: builtins.list[Instruction]) -> None: ...
@property
def name(self) -> typing.Optional[builtins.str]:
r"""
The Quil-T name of the measurement that this measure calibration definition is for, if any.
"""
@property
def qubit(self) -> Qubit:
r"""
The qubit that this measure calibration definition is for.
Expand All @@ -1222,6 +1227,16 @@ class MeasureCalibrationIdentifier:
A unique identifier for a measurement calibration definition within a program
"""
@property
def name(self) -> typing.Optional[builtins.str]:
r"""
The Quil-T name of the measurement, if any.
"""
@name.setter
def name(self, value: typing.Optional[builtins.str]) -> None:
r"""
The Quil-T name of the measurement, if any.
"""
@property
def qubit(self) -> Qubit:
r"""
The qubit which is being measured.
Expand All @@ -1248,21 +1263,23 @@ class MeasureCalibrationIdentifier:
If this is missing, this is a calibration for a measurement for effect.
"""
def __eq__(self, other:builtins.object) -> builtins.bool: ...
def __getnewargs__(self) -> tuple[Qubit, typing.Optional[builtins.str]]: ...
def __new__(cls, qubit:Qubit, target:typing.Optional[builtins.str]) -> MeasureCalibrationIdentifier: ...
def __getnewargs_ex__(self) -> tuple[tuple[Qubit, str | None], dict[str, str | None]]: ...
def __new__(cls, qubit:Qubit, target:typing.Optional[builtins.str], *, name:typing.Optional[builtins.str]=None) -> MeasureCalibrationIdentifier: ...
def __repr__(self) -> builtins.str: ...
def to_quil(self) -> builtins.str: ...
def to_quil_or_debug(self) -> builtins.str: ...

class Measurement:
@property
def name(self) -> typing.Optional[builtins.str]: ...
@property
def qubit(self) -> Qubit: ...
@property
def target(self) -> typing.Optional[MemoryReference]: ...
def __eq__(self, other:builtins.object) -> builtins.bool: ...
def __getnewargs__(self) -> tuple[Qubit, typing.Optional[MemoryReference]]: ...
def __getnewargs_ex__(self) -> tuple[tuple[Qubit, MemoryReference | None], dict[str, str | None]]: ...
def __hash__(self) -> builtins.int: ...
def __new__(cls, qubit:Qubit, target:typing.Optional[MemoryReference]) -> Measurement: ...
def __new__(cls, qubit:Qubit, target:typing.Optional[MemoryReference], *, name:typing.Optional[builtins.str]=None) -> Measurement: ...
def __repr__(self) -> builtins.str: ...
def to_quil(self) -> builtins.str: ...
def to_quil_or_debug(self) -> builtins.str: ...
Expand Down
2 changes: 1 addition & 1 deletion quil-rs/scripts/pyo3_linter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ def _cfg(regex: str, cond: str = r"[^,]+") -> str:
)
STUB_GEN_RE = re.compile(
_cfg(
r"gen_stub_py(class(?:(?:_complex)?_enum)?|methods|function)(?:\((\s*?[^)]+)\))?"
r"(?:(?:pyo3_stub_gen::)?derive::)?gen_stub_py(class(?:(?:_complex)?_enum)?|methods|function)(?:\((\s*?[^)]+)\))?"
)
)

Expand Down
102 changes: 94 additions & 8 deletions quil-rs/src/instruction/calibration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ impl Quil for MeasureCalibrationDefinition {
pyo3::pyclass(module = "quil.instructions", eq, get_all, set_all, subclass)
)]
pub struct MeasureCalibrationIdentifier {
/// The Quil-T name of the measurement, if any.
pub name: Option<String>,

/// The qubit which is being measured.
pub qubit: Qubit,
Comment on lines +268 to +269
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the Qubit is no longer optional, I don't think there's a way for this to not be a breaking change for PyQuil, as I see no obvious "default" qubit to supply.

And based on what I've read of your proposed changes to the Quil spec, I'm guessing it probably should always have been required? I don't see a way to make sense of "measure this qubit" without knowing which one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is just a bug – somebody had a thinko, and instead of "required qubit and optional memory location" implemented "optional qubit and required memory location", which as you say makes no sense – and isn't even compatible with MEASURE, which correctly takes a required qubit and an optional memory location. And if you look at the parser, you'll see some very strange code which could for instance result in a DEFCAL MEASURE that (a) measures no qubits, but (b) puts the result into the memory location named "0".

/// Parse the contents of a `DEFCAL MEASURE` instruction, following the `MEASURE` token.
pub(crate) fn parse_defcal_measure<'a>(
    input: ParserInput<'a>,
) -> InternalParserResult<'a, Instruction> {
    let (input, params) = pair(parse_qubit, opt(token!(Identifier(v))))(input)?;
    let (qubit, destination) = match params {
        (qubit, Some(destination)) => (Some(qubit), destination),
        (destination, None) => (None, destination.to_quil_or_debug()),
    };
    // ...
}

I wonder if we should move this fix back with the CalibrationDefinition name change, if you think that'll help things go more smoothly? My thinking was that MeasureCalibrationDefinition had to change anyway, to accommodate the new !name, but if you think it would make for a softer landing I could pull this fix out.

Copy link
Contributor

@asaites asaites Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should move this fix back with the CalibrationDefinition name change, if you think that'll help things go more smoothly?

I'm inclined to say "yes" specifically to the change for making qubit required and parameter optional. That part has to be a breaking change for pyQuil, but the rest we could work-around; I've detailed some of that below and am interested in your thoughts with respect to how well quil-rs, quil, and pyQuil should match with respect to constructor signatures.

My thought is about the effect on pyQuil users specifically, avoiding 2 major version bumps back-to-back. We should be able to prevent that by simply not releasing a new pyQuil right away, though I think we need to change the version specifier for quil in its dependency list to prevent it from trying to grab the newer release before we're ready (given that this project is currently pre-1.0, that probably should have always been the case).


pyQuil doesn't actually have its own notion of a MeasureCalibrationIdentifier -- it just has DefMeasureCalibration (corresponding to MeasureCalibrationDefinition), with (roughly) this constructor:

class DefMeasureCalibration(quil_rs.MeasureCalibrationDefinition, AbstractInstruction):
    def __new__(
        cls,
        qubit: Qubit | FormalArgument | None,
        memory_reference: MemoryReference,
        instrs: list[AbstractInstruction],
    ) -> Self:
        """Initialize a new measure calibration definition."""
        rs_qubit = None if not qubit else _convert_to_rs_qubit(qubit)
        ident = quil_rs.MeasureCalibrationIdentifier(rs_qubit, str(memory_reference))
        return super().__new__(cls, ident, _convert_to_rs_instructions(instrs))

So the breaking change is here:

class DefMeasureCalibration(quil_rs.MeasureCalibrationDefinition, AbstractInstruction):
    def __new__(
        cls,
-       qubit: Qubit | FormalArgument | None,
+       qubit: Qubit | FormalArgument,
        memory_reference: MemoryReference,
        instrs: list[AbstractInstruction],
    ) -> Self:
        """Initialize a new measure calibration definition."""
-       rs_qubit = None if not qubit else _convert_to_rs_qubit(qubit)
+       rs_qubit = _convert_to_rs_qubit(qubit)
        ident = quil_rs.MeasureCalibrationIdentifier(rs_qubit, str(memory_reference))
        return super().__new__(cls, ident, _convert_to_rs_instructions(instrs))

And then to add support for named measurements, we could make this non-breaking change (assuming the constructor signature for MeasureCalibrationIdentifier you've proposed in this PR):

class DefMeasureCalibration(quil_rs.MeasureCalibrationDefinition, AbstractInstruction):
    def __new__(
        cls,
        qubit: Qubit | FormalArgument,
        memory_reference: MemoryReference,
        instrs: list[AbstractInstruction],
+       name: str | None = None,
    ) -> Self:
        """Initialize a new measure calibration definition."""
        rs_qubit = _convert_to_rs_qubit(qubit)
-       ident = quil_rs.MeasureCalibrationIdentifier(rs_qubit, str(memory_reference))
+       ident = quil_rs.MeasureCalibrationIdentifier(name, rs_qubit, str(memory_reference))
        return super().__new__(cls, ident, _convert_to_rs_instructions(instrs))

As a point of interest, we could essentially make the same sort of transition within quil to avoid two breaking changes. First we'd make this breaking change:

pickleable_new! {
    impl MeasureCalibrationIdentifier {
-      pub fn new(qubit: Option<Qubit>, parameter: String);
+      pub fn new(qubit: Qubit, target: Option<String>);
    }
}

With that, this wouldn't be a breaking change:

pickleable_new! {
    impl MeasureCalibrationIdentifier {
-      pub fn new(qubit: Qubit, target: Option<String>);
+      #[pyo3(signature = (qubit, target, name=None))]
+      pub new(qubit: Qubit, target: Option<String>, name: Option<String>);
    }
}

Of course, this requires we add the name parameter to the end in order to give it a default value.
We could have a separate constructor specifically for quil, if you really wanted the Rust constructor's parameter order to match their order within the Quil language construct. It would also be possible (though more complex) to write out an overloaded signature and sort out the details of which version you meant ourselves, but that wouldn't be my recommendation in the face of the above options.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two separate thoughts:

  1. Re the existing DEFCAL MEASURE bug: sounds like, due to the way Python works, the breaking change if any will actually be pretty small. I'll move it back along with the other change regardless, though, it'll be nice to be uniform.

  2. I thought about this issue with defaults, and I'm torn. On the Rust side, I think putting the name first is a win – you have to write None anyway, so you might as well put it in the logical order. On the Python side… I don't know! I think there's also a good argument for

def __init__(self, qubit: Qubit, target: Optional[String], *, name: Optional[String] = None):
    ...

for this and for MEASURE, since that lets you create the simple case simply. Perhaps the move is to leave this a breaking change in quil-py, and have the pyQuil wrapper do the default-parameter dance?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I included the DEFCAL MEASURE fix in #482. The discussion about point 2 can continue though!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps the move is to leave this a breaking change in quil-py, and have the pyQuil wrapper do the default-parameter dance?

In this particular case, it's moot: pyQuil doesn't have its own MeasureCalibrationIdentifier, and its signature for DefMeasureCalibration already diverges from MeasureCalibrationDefinition (to say nothing of the name difference). Handling the parameter ordering is no burden for this instance, and making qubit required is a breaking change no matter what.

In the bigger picture, we want Python users to have an ergonomic experience working with quil-rs. It's been fuzzy whether that responsibility falls on quil or should be handled in a higher-level wrapper like pyQuil. Right now, pyQuil's subclasses mostly exist to support things like "accept a Python number where an Expression.Number is expected". That's the kind of ergonomics we want, but having all those extra classes has proven itself a significant maintenance burden. Moving forward, our goal should be getting rid of those subclasses and having pyQuil manipulate quil's constructs directly when appropriate.

Doing that initially will, of course, be a major breaking change. Then, without the layer in between, quil will be more responsible for avoiding breaking changes. It might sound like that gets a lot harder without the extra layer, but the reality is that quil-py is precisely the compatibility layer between quil and quil-rs! Then the question becomes how far we want to let the Python version of things diverge from their Rust counterparts, as for example having a constructor with different parameter ordering. I don't know offhand how heavy a burden divergence there will create, but my instinct is that we should minimize differences whenever possible.

So with respect to adding the new constructor parameter (necessarily a breaking change for quil-rs), the options are:

  1. Share the constructor with quil, with the new name parameter at the beginning of the list, making it a breaking change for quil (and potentially pyQuil, depending on when it lands).
  2. Share the constructor with quil, but add the new name parameter to the end of the list, potentially at the cost of making it less ergonomic for Rust users or those very familiar with Quil. Use the pyo3 signature to avoid a breaking change for quil.
  3. Add a new constructor specifically for quil, using the pyo3 signature to avoid a breaking change, and deal with the consequences the divergence may entail.

Putting it in these terms makes me think (2) is the right way forward. Do you think I'm on the right track?

Copy link
Contributor Author

@antalsz antalsz Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that’s convincing, yeah! I’d amend it very slightly: make the gate name parameter last but named-only, as in my snippet above. This gives us multiple virtues:

  • There are no Python calls that look identical to Rust calls but have different meanings;
  • If you want to write things in the same order, you can, as long as you name every parameter.

I can make that change to MEASURE and DEFCAL MEASURE.


Expand All @@ -272,18 +275,27 @@ pub struct MeasureCalibrationIdentifier {
pub target: Option<String>,
}

pickleable_new! {
impl MeasureCalibrationIdentifier {
pub fn new(qubit: Qubit, target: Option<String>);
impl MeasureCalibrationIdentifier {
pub const fn new(name: Option<String>, qubit: Qubit, target: Option<String>) -> Self {
Self {
name,
qubit,
target,
}
}
}

impl CalibrationSignature for MeasureCalibrationIdentifier {
type Signature<'a> = (&'a Qubit, Option<&'a str>);
type Signature<'a> = (Option<&'a str>, &'a Qubit, Option<&'a str>);

fn signature(&self) -> Self::Signature<'_> {
let Self { qubit, target } = self;
(qubit, target.as_deref())
let Self {
name,
qubit,
target,
} = self;

(name.as_deref(), qubit, target.as_deref())
}

fn has_signature(&self, signature: &Self::Signature<'_>) -> bool {
Expand All @@ -297,9 +309,17 @@ impl Quil for MeasureCalibrationIdentifier {
f: &mut impl std::fmt::Write,
fall_back_to_debug: bool,
) -> crate::quil::ToQuilResult<()> {
let Self { qubit, target } = self;
let Self {
name,
qubit,
target,
} = self;

write!(f, "DEFCAL MEASURE ")?;
write!(f, "DEFCAL MEASURE")?;
if let Some(name) = name {
write!(f, "!{name}")?;
}
write!(f, " ")?;
qubit.write(f, fall_back_to_debug)?;
if let Some(target) = target {
write!(f, " {target}")?;
Expand All @@ -324,6 +344,23 @@ mod test_measure_calibration_definition {
"With Fixed Qubit",
MeasureCalibrationDefinition {
identifier: MeasureCalibrationIdentifier {
name: None,
qubit: Qubit::Fixed(0),
target: Some("theta".to_string()),
},
instructions: vec![Instruction::Gate(Gate {
name: "X".to_string(),
parameters: vec![Expression::Variable("theta".to_string())],
qubits: vec![Qubit::Fixed(0)],
modifiers: vec![],

})]},
)]
#[case(
"Named With Fixed Qubit",
MeasureCalibrationDefinition {
identifier: MeasureCalibrationIdentifier {
name: Some("midcircuit".to_string()),
qubit: Qubit::Fixed(0),
target: Some("theta".to_string()),
},
Expand All @@ -339,6 +376,23 @@ mod test_measure_calibration_definition {
"Effect With Fixed Qubit",
MeasureCalibrationDefinition {
identifier: MeasureCalibrationIdentifier {
name: None,
qubit: Qubit::Fixed(0),
target: None,
},
instructions: vec![Instruction::Gate(Gate {
name: "X".to_string(),
parameters: vec![Expression::PiConstant()],
qubits: vec![Qubit::Fixed(0)],
modifiers: vec![],

})]},
)]
#[case(
"Named Effect With Fixed Qubit",
MeasureCalibrationDefinition {
identifier: MeasureCalibrationIdentifier {
name: Some("midcircuit".to_string()),
qubit: Qubit::Fixed(0),
target: None,
},
Expand All @@ -354,6 +408,22 @@ mod test_measure_calibration_definition {
"With Variable Qubit",
MeasureCalibrationDefinition {
identifier: MeasureCalibrationIdentifier {
name: None,
qubit: Qubit::Variable("q".to_string()),
target: Some("theta".to_string()),
},
instructions: vec![Instruction::Gate(Gate {
name: "X".to_string(),
parameters: vec![Expression::Variable("theta".to_string())],
qubits: vec![Qubit::Variable("q".to_string())],
modifiers: vec![],
})]},
)]
#[case(
"Named With Variable Qubit",
MeasureCalibrationDefinition {
identifier: MeasureCalibrationIdentifier {
name: Some("midcircuit".to_string()),
qubit: Qubit::Variable("q".to_string()),
target: Some("theta".to_string()),
},
Expand All @@ -368,6 +438,22 @@ mod test_measure_calibration_definition {
"Effect Variable Qubit",
MeasureCalibrationDefinition {
identifier: MeasureCalibrationIdentifier {
name: None,
qubit: Qubit::Variable("q".to_string()),
target: None,
},
instructions: vec![Instruction::Gate(Gate {
name: "X".to_string(),
parameters: vec![Expression::PiConstant()],
qubits: vec![Qubit::Variable("q".to_string())],
modifiers: vec![],
})]},
)]
#[case(
"Named Effect Variable Qubit",
MeasureCalibrationDefinition {
identifier: MeasureCalibrationIdentifier {
name: Some("midcircuit".to_string()),
qubit: Qubit::Variable("q".to_string()),
target: None,
},
Expand Down
29 changes: 22 additions & 7 deletions quil-rs/src/instruction/measurement.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#[cfg(feature = "stubs")]
use pyo3_stub_gen::derive::gen_stub_pyclass;

use crate::{pickleable_new, quil::Quil};
use crate::quil::Quil;

use super::{MemoryReference, Qubit};

Expand All @@ -12,13 +12,18 @@ use super::{MemoryReference, Qubit};
pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all, subclass)
)]
pub struct Measurement {
pub name: Option<String>,
pub qubit: Qubit,
pub target: Option<MemoryReference>,
}

pickleable_new! {
impl Measurement {
pub fn new(qubit: Qubit, target: Option<MemoryReference>);
impl Measurement {
pub const fn new(name: Option<String>, qubit: Qubit, target: Option<MemoryReference>) -> Self {
Self {
name,
qubit,
target,
}
}
}

Expand All @@ -28,9 +33,19 @@ impl Quil for Measurement {
writer: &mut impl std::fmt::Write,
fall_back_to_debug: bool,
) -> Result<(), crate::quil::ToQuilError> {
write!(writer, "MEASURE ")?;
self.qubit.write(writer, fall_back_to_debug)?;
if let Some(target) = &self.target {
let Self {
name,
qubit,
target,
} = self;

write!(writer, "MEASURE")?;
if let Some(name) = name {
write!(writer, "!{name}")?;
}
write!(writer, " ")?;
qubit.write(writer, fall_back_to_debug)?;
if let Some(target) = target {
write!(writer, " ")?;
target.write(writer, fall_back_to_debug)?;
}
Expand Down
71 changes: 70 additions & 1 deletion quil-rs/src/instruction/quilpy.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use num_complex::Complex64;
use numpy::{PyArray2, ToPyArray};
use paste::paste;
use pyo3::{prelude::*, types::PyTuple};
use pyo3::{
prelude::*,
types::{IntoPyDict as _, PyDict, PyTuple},
};

use super::*;
use crate::{
Expand Down Expand Up @@ -512,6 +515,12 @@ impl GateSpecification {
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl MeasureCalibrationDefinition {
/// The Quil-T name of the measurement that this measure calibration definition is for, if any.
#[getter]
fn name(&self) -> Option<&str> {
self.identifier.name.as_deref()
}

/// The qubit that this measure calibration definition is for.
#[getter]
fn qubit(&self) -> Qubit {
Expand All @@ -526,6 +535,66 @@ impl MeasureCalibrationDefinition {
}
}

// We don't use [`pickleable_new!`] here because we're separating Rust's
// [`MeasureCalibrationIdentifier::new`] and Python's `MeasureCalibrationIdentifier.new`.
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl MeasureCalibrationIdentifier {
// Note that the Python argument order is not the same as the Rust argument order for
// [`Self::new`], and that this function requires keywords on the Python side! Make sure
// `__getnewargs_ex__` is consistent with `__new__`!
#[pyo3(signature = (qubit, target, *, name = None))]
#[new]
fn __new__(qubit: Qubit, target: Option<String>, name: Option<String>) -> Self {
Self::new(name, qubit, target)
}

#[gen_stub(override_return_type(
type_repr = "tuple[tuple[Qubit, str | None], dict[str, str | None]]"
))]
fn __getnewargs_ex__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
let Self {
name,
qubit,
target,
} = self;
let positional: Bound<'py, PyTuple> = (qubit.clone(), target.clone()).into_pyobject(py)?;
let keyword: Bound<'py, PyDict> = [("name", name)].into_py_dict(py)?;
(positional, keyword).into_pyobject(py)
}
}

// We don't use [`pickleable_new!`] here because we're separating Rust's [`Measurement::new`] and
// Python's `Measurement.new`.
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl Measurement {
// Note that the Python argument order is not the same as the Rust argument order for
// [`Self::new`], and that this function requires keywords on the Python side! Make sure
// `__getnewargs_ex__` is consistent with `__new__`!
#[pyo3(signature = (qubit, target, *, name = None))]
#[new]
fn __new__(qubit: Qubit, target: Option<MemoryReference>, name: Option<String>) -> Self {
Self::new(name, qubit, target)
}

#[gen_stub(override_return_type(
type_repr = "tuple[tuple[Qubit, MemoryReference | None], dict[str, str | None]]"
))]
fn __getnewargs_ex__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
let Self {
name,
qubit,
target,
} = self;
let positional: Bound<'py, PyTuple> = (qubit.clone(), target.clone()).into_pyobject(py)?;
let keyword: Bound<'py, PyDict> = [("name", name)].into_py_dict(py)?;
(positional, keyword).into_pyobject(py)
}
}

#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl Sharing {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
source: quil-rs/src/instruction/calibration.rs
expression: measure_cal_def.to_quil_or_debug()
---
DEFCAL MEASURE!midcircuit q:
X(pi) q
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
source: quil-rs/src/instruction/calibration.rs
expression: measure_cal_def.to_quil_or_debug()
---
DEFCAL MEASURE!midcircuit 0:
X(pi) 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
source: quil-rs/src/instruction/calibration.rs
expression: measure_cal_def.to_quil_or_debug()
---
DEFCAL MEASURE!midcircuit 0 theta:
X(%theta) 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
source: quil-rs/src/instruction/calibration.rs
expression: measure_cal_def.to_quil_or_debug()
---
DEFCAL MEASURE!midcircuit q theta:
X(%theta) q
Loading
Loading