Skip to content

feat!: add support for named RESETs - #532

Open
mingyoungjeng wants to merge 16 commits into
mainfrom
named-resets
Open

mingyoungjeng wants to merge 16 commits into
mainfrom
named-resets

Conversation

@mingyoungjeng

@mingyoungjeng mingyoungjeng commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

Adds support for named reset calibrations, akin to named measurements (quil-rs!479)

  • Adds ResetCalibrationDefinition and Instruction::ResetCalibrationDefinition
  • Elevate RESET to a gate-level instruction, which can be expanded with calibrations
  • Rename CalibrationDefinition and CalibrationIdentifier to GateCalibrationDefinition and GateCalibrationIdentifier, respectively
  • Rename CalibrationSource to CalibrationIdentifier
  • Minor fixes to Instruction::get_qubits, Instruction::get_qubits_mut, and Program::used_qubits

TODO:

  • add more tests
  • Python stub-gen
  • Make Quil specification proposal

@erichulburd

Copy link
Copy Markdown
Collaborator

I'm a fair bit skeptical we should define a separate calibration type for reset calibrations. In contrast to measurements, where there is an (optional) "memory out" parameter, a reset calibration can be expressed in the same way as a gate calibration (granted, reset instructions currently don't accept read-only parameters, we shouldn't presume that will always be the case).

More important than some of the implementation details I'll advocate below, the general perspective I'd pitch is:

  • The distinction b/t calibration definition types is narrow and unimportant to both our translator (which translates pulse instructions) and downstream users who don't care if they're calibrating a gate, measurement, reset, or something else (like a leakage reduction unit, LRU).
  • The last point about "something else" is important - we don't want to add friction for defining arbitrary calibrations for quantum operations in the future.

Given this perspective, I would advocate an implementation along these lines:

  1. Support the RESET!calibration_name syntax, along with the optional Reset.calibration_name (or whatever) attribute.
  2. Reset calibration definitions are the same as the gate calibration definitions.
  3. There should be a single calibration hashmap for gates, resets, and "whatever else". The key name is (operation type, calibration name). This could be just be separate calibration hashmap keyed on (calibration name,) and maybe this is an implementation that doesn't matter so much, but I find the single hashmap to be simpler and more scalable (in the sense that the attribute-space won't proliferate with different types that can be calibrated).... Again, maybe an implementation detail not worth litigating extensively, but this is where my present persuasion lies.

@mingyoungjeng

Copy link
Copy Markdown
Collaborator Author

a reset calibration can be expressed in the same way as a gate calibration (granted, reset instructions currently don't accept read-only parameters, we shouldn't presume that will always be the case).

Assuming (named) reset calibrations should operate like current (named) measurement calibrations, i.e., with the ! syntax,

DEFCAL MEASURE!name q:
    ...

DEFCAL RESET!name q:
    ...

from a type-safety perspective, these should be distinct identifiers from gate calibrations. In more concrete terms, the differences between a (gate) CalibrationIdentifier and a ResetCalibrationIdentifier are meaningful in my mind.

struct CalibrationIdentifier {
    name: String,
    qubits: Vec<Qubit>,
    parameters: Vec<Expression>,
    modifiers: Vec<GateModifier>,
}

struct ResetCalibrationIdentifier {
    name: Option<String>,
    qubit: Option<Qubit>,
}
  • To support both DEFCAL RESET!name and DEFCAL RESET, reset calibrations require an optional name field, which isn't permitted for gate calibrations.
  • The qubit field of ResetCalibrationIdentifier could be represented in a Vec, but the Option<_> is more precise to match how RESET instructions are currently defined. (Side note: it might be interesting to explore expanding the specification of RESET to accept multiple qubits?)
  • Like you mentioned, RESETs don't currently accept parameters. Since they don't in the current Quil specification, I think it is important, again, from a type safety perspective, to make "reset calibrations with parameters" unrepresentable.
  • Similarly, I don't think RESETs should support gate modifiers — in particular GateModifier::dagger, since RESETs are non-unitary.

The distinction b/t calibration definition types is narrow and unimportant to both our translator (which translates pulse instructions) and downstream users who don't care if they're calibrating a gate, measurement, reset, or something else (like a leakage reduction unit, LRU). The last point about "something else" is important - we don't want to add friction for defining arbitrary calibrations for quantum operations in the future.

I think there is discussion to be had on generalizing calibration definitions (and Quil's other DEF-initions). However, I think that restructuring, in addition to adding named RESETs and reset calibrations, might be a bit bloated for a single PR (both here and the Quil specification).

There should be a single calibration hashmap for gates, resets, and "whatever else". The key name is (operation type, calibration name). This could be just be separate calibration hashmap keyed on (calibration name,) and maybe this is an implementation that doesn't matter so much, but I find the single hashmap to be simpler and more scalable (in the sense that the attribute-space won't proliferate with different types that can be calibrated).... Again, maybe an implementation detail not worth litigating extensively, but this is where my present persuasion lies.

I think I can attempt to implement calibrations with something like this:

enum CalibrationIdentifier {
    // Rename current `CalibrationIdentifier` => `GateCalibrationIdentifier`
    Gate(GateCalibrationIdentifier),
    Measure(MeasureCalibrationIdentifier),
    Reset(ResetCalibrationIdentifier),
}

struct Calibrations {
    calibrations: HashMap<CalibrationIdentifier, Vec<Instructions>>,
}

This would put calibrations in a single map without compromising on the type guarantees in the existing implementation.

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
PR Preview Action v1.8.2

QR code for preview link

🚀 View preview at
https://rigetti.github.io/quil-rs/pr-preview/pr-532/

Built to branch quil-py-docs at 2026-11-05 17:10 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@mingyoungjeng mingyoungjeng self-assigned this Oct 1, 2026
@mingyoungjeng
mingyoungjeng marked this pull request as ready for review October 2, 2026 19:41
@mingyoungjeng
mingyoungjeng requested a review from asaites October 5, 2026 20:16

@edyounis edyounis left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The separation of CalibrationIdenifiers seems like an unnecessary abstraction. There is a lot of repeated code and enum dispatch to include to the separation, when the real logic is that the different structures have different requirements on signatures. The simplest way to include that logic would just be a check during parsing, rather than internalizing it in the structure of the program. There is also the extension of the name in for quil writing purposes. A clue to this is that write_calibration_definition calls identifier.write and instruction.write

This is definitely me saying, "I would have designed it differently," and I want to avoid the usual second part of that statement: "So you should too." So, feel free to ignore this point, it won't stop me from approving this, but I wanted to bring it up. Everything you have done looks correct.

Also the renaming of CalibrationDefinition to GateCalibrationDefinition is a breaking API change. I agree with it, but it is unnecessary and worth calling out. I really do agree with it, and doubt it will effect anyone.

let Self {
identifier,
instructions,
} = self;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

NIT: feel free to ignore, but why destruct here? Why not just &self.identifier, &self.instructions?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

For me, it is mostly to illustrate which fields of the struct are being used and to automatically flag if one of those fields changes in the future.

qubit.write(writer, fall_back_to_debug)
}
None => write!(writer, "RESET").map_err(Into::into),
let Self { name, qubit } = self;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Same comment as above, why destruct? (Feel free to ignore)

input: ParserInput<'a>,
) -> InternalParserResult<'a, Instruction> {
let (input, modifiers) = many0(parse_gate_modifier)(input)?;
let (input, name) = token!(Identifier(v))(input)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is the ! a separate token that would fail this? If not and it can find its way into an Identifier, do we want to include some logic to catch and nudge the user back to the correct way, for example in a miscapitialization case like reset!blah

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

That is probably worth doing at some point, and I think there are a few areas in the parser where messages like that could be provided.

Comment thread quil-rs/src/parser/common.rs Resolved
Comment thread quil-rs/src/program/mod.rs Resolved
}
Instruction::RawCapture(raw_capture) => {
self.instructions.push(Instruction::RawCapture(raw_capture));
body_instruction => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I am missing something, where does this match case come from? I can find the body_instruction symbol

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is just a wildcard catch

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants