Quil Parser & Program Builder
This library is the implementation of the Quil spec in Rust, with bindings for Python.
It serves three purposes:
- Parse Quil programs from strings, and output programs to strings
- Manipulate Quil programs within Rust and Python
- Construct a dependency graph among program instructions
It should be considered unstable until the release of v1.0.
Note: For Rigetti's Python library for quantum programming with Quil, you likely want PyQuil.
This code serves as the foundation of that library,
but PyQuil offers higher-level abstractions and greater stability guarantees.
Crate Features
quil-rs has several optional features:
| Feature | Description |
|---|---|
graphviz-dot |
Enable plotting ScheduledPrograms in Graphviz dotfile format. |
wasm-bindgen |
Enable compilation to wasm32-unknown-unknown with wasm-bindgen |
python |
Enable Python bindings via PyO3 |
stubs |
Enable type stub generation via pyo3_stub_gen (implies python) |
The python feature enables code to build the quil Python package,
which allows Python programs to use the features of quil-rs.
The code related to the Python package is sometimes referred to as quil-py,
both for historical reasons, and to distinguish it from other parts of quil-rs.
Note that maturin commands (maturin develop, maturin release, etc.)
automatically enable the python feature, as well as the pyo3/extension-module feature,
which disables linking libpython, and is needed for manylinux builds.
Development
For more information on building, testing, or contributing to quil-rs and quil-py,
please see the Contribution Guide
Migration to quil v0.32.0
Prior to v0.32.0, this repository included a separate crate quil-py
which exposed the Python bindings to quil.
Starting at that version, we've merged that crate into quil-rs directly,
supported by the python feature flag.
With this change, we've united the version numbers for quil-rs and quil,
resulting in a large jump since the last version.
The new version comes with breaking changes, particularly for Python users,
which are summarized below and detailed in the CHANGELOG.
For the trouble, we now support the latest versions of PyO3 and Python.
This comes with improvements to security, stability, and performance
and makes our codebase much easier to support moving forward.
Rust (quil-rs) Breaking Changes
For Rust consumers of quil-rs, there are a few breaking changes:
- Unit enum variants are now empty tuple variants
instruction::Calibrationhas been renamedinstruction::CalibrationDefinitionMeasureCalibrationDefinitionhas new struct and constructorExpression::evaluatemay require explicit types- The unused-but-public
MemoryAccesstype has been removed
Unit variants
The previously Unit-variants of
Instruction (Halt, Nop, and Wait) and Expression (PiConstant)
are now empty tuple variants to make them compatible with PyO3's "Complex enums".
That means the following variants must have () appended to their usages:
// Previously you'd use this:
match expression
// Now you must use this:
match expression
instruction::CalibrationDefinition
The Calibration type in in the instruction module has been renamed CalibrationDefinition
to be consistent with other definition instructions, particularly MeasureCalibrationDefinition,
and to match its variant name within the Instructions enumeration,
as is the naming convention for all other Instruction variants.
instruction::MeasureCalibrationDefinition
Contrary to the Quil specification, MeasureCalibrationDefinition
made its qubit optional and its parameter name required.
This is now fixed, making qubit required and the name optional;
in addition, it renames parameter to target for clarity,
in anticipation of Named Measurements support.
Note that it was always a bug to use None for the qubit parameter.
Expression::evaluate
The signature of Expression::evaluate
was generalized to make it easier to share with Python,
and this will cause an error if you were relying on type inference for these parameters, e.g.:
some_expression.evaluate;
You can fix it by specifying the key type, e.g.:
some_expression.evaluate;
instruction::MemoryAccess
The MemoryAccess type has been removed.
This struct was not in use within quil-rs, but it was part of the public API.
Python (quil/quil-py) Breaking Changes
If you're directly using the quil Python package that exposes bindings to quil-rs,
please be aware of the following changes required to upgrade to the newest quil:
- We are dropping support for Python 3.9 and adding support through Python 3.13.
- We no longer wrap
quil-rstypes with an additional layer for Python interop, but instead directly expose them as#[pyclass]es; in particular, thefrom_*,as_*,to_*,is_*, andinnermethods have been removed, You should replace their usage with more typical Python operations (see below for examples). Note thatto_quilandparseare still there for converting to/fromstrs. - The following functions are now
@propertyattributes (so you no longer call them), making them consistent with othergetters already inquil:program.BasicBlock.labelprogram.BasicBlock.instructionsprogram.BasicBlock.terminatorprogram.CalibrationExpansion.calibration_usedprogram.CalibrationExpansion.rangeprogram.CalibrationExpansion.expansionsprogram.ScheduleSeconds.itemsprogram.ScheduleSeconds.durationprogram.ProgramCalibrationExpansion.programprogram.ProgramCalibrationExpansion.source_map
- Classes which implement
__hash__are now immutable from Python, and conversely, classes which are mutable from Python no longer implement__hash__methods. In Python, a class should not be both hashable and mutable[^note-hash]. We now enforce this, usually by making the type immutable so we can implement__hash__, though in some cases we removed__hash__to allow the type to be mutable. - The manner in which classes support the
pickleandcopymodules now works so that__getnewargs__returns the appropriate arguments for the__new__constructor. This should reduce many edge cases around subclassingquiltypes, but may constitute a breaking change if you were relying on__setstate__support. To be clear, we do not recommend usingpickleas a serialization format, but understand it has its value for its relationship tocopyandmultiprocessing. - Finally, some type stubs had incorrect parameter names, but now have been updated.
They look like breaking changes when comparing the APIs,
but in reality these were already required if you were using them as keyword arguments:
instructions.SetScale.__new__:phaseshould bescaleinstructions.MemoryReference.parse:inputshould bestringinstructions.TargetPlaceholder.__new__:base_targetshould bebase_labelprogram.CalibrationSet.__new__:measure_calibration_definitionsshould bemeasure_calibrations
[^note-hash]: If it implements __hash__, it should implement __eq__,
but if it implements __eq__ and is mutable, it should not implement __hash__.
Thus, a class that implements __hash__ should not be mutable.
Put another way, a Python object's hash is required to be stable throughout its lifetime.
This is actually the same rule as in Rust, but in safe Rust you can only violate it
through a logic bug involving interior mutability.
Specific Examples
Instead of using from_*, just use the target class's constructor directly.
For example:
from quil.instructions import Instruction, Gate, Qubit
some_gate = Gate(
"X",
parameters=(),
- qubits=[Qubit.from_fixed(0)],
+ qubits=[Qubit.Fixed(0)],
modifiers=()
)
- instr = Instruction.from_gate(some_gate)
+ instr = Instruction.Gate(some_gate)
- expr = Expression.from_number(1.0+0.5j)
+ expr = Expression.Number(1.0+0.5j)
Replace is_*, to_*, as_*, and inner with match.
Here's an example of extracting inner elements:
:
:
return
:
return
:
return * 2
:
return
If needed, you can replace inner with _0, usually paired with an isinstance check.
Keep in mind that enumerated subclasses are often named after the class they take as a parameter.
The following asserts are all valid:
=
=
assert
assert
assert
assert not
assert not
assert ==