Repository navigation
Experimental noise model & simulation module (tracking) #1863
Description
Tracking issue for the experimental noise model and simulation module, originally proposed as one large PR (#1848) and split into a stack of five reviewable PRs. This issue holds the overall design; each PR below is scoped to one piece and stacked on the previous one (review/merge top to bottom).
PRs (merge in order)
- feat: drop Python 3.9 support, add rigetti-quax (1/5) (#1852) #1858 — Drop Python 3.9, add
rigetti-quax(v4.20.0) · closes Drop Python 3.9 #1852 - feat: qudit noise model (2/5) (#1854) #1859 — Qudit noise model (v4.21.0) · closes Qudit Noise model #1854
- feat: experimental exact simulators (3/5) (#1855) #1860 — Experimental exact simulators (v4.22.0) · closes Experimental exact simulators #1855
- feat: trajectory simulator (4/5) (#1856) #1861 — Trajectory simulator (v4.23.0) · closes Trajectory simulator #1856
- feat: dynamic-shape trajectory simulator (5/5) (#1857) #1862 — Dynamic-shape trajectory simulator (v4.24.0) · closes Dynamic shape trajectory simulator #1857
Each branch is based on the previous one; the tip of #1862 reproduces the original #1848 branch exactly (modulo the per-PR version bumps).
Description
This work adds new noise modeling and simulation utilities. It is intended to replace the existing reference simulators and the existing NoiseModel. The proposed migration path is to add these utilities as experimental private modules, while marking the existing simulation and noise utilities as deprecated. In the next major version of pyquil, the deprecated utilities will be replaced by the new utilities.
Dependencies
This adds the new dependency rigetti-quax. This package provides a wide set of utilities for representing quantum objects including states, gates, measurements and noise channels. It's based on jax, which delivers high performance but adds jax as a transitive dependency to pyquil. jax is a relatively mature package but somewhat larger than existing dependencies (around 80MB compared to scipy at 40MB), so this is worth some discussion.
Noise
First, the noise.py file is promoted to a module. This is non-breaking as the same functions continue to be exported from pyquil.noise. Inside the module are several new private files. When these are promoted to the public API, they will be exported from pyquil.noise.
New Noise Model System (pyquil/noise/_noise_model.py)
We introduce a frozen-dataclass-based NoiseModel container that collects per-instruction noise channels. The role of the noise model is to store a collection of channels, which together make up a device noise model. Channels are associated with instructions in the program and we can get the channel for a particular instruction via NoiseModel.get_channel(inst).
NoiseModelLike— Protocol defining theget_channelinterface for custom implementations.NoiseModel— The canonical use case. Accepts anIterableof channels (list, tuple, set, generator), stores them as an immutableTuple. Supports+for composition andget_channel(inst)for lookup.DepolarizingNoiseModel— Convenience model returning a depolarizing channel for any gate.CompositeNoiseModel— Chains multipleNoiseModelLikeobjects, returning the first non-None channel.
Noise models can also be constructed from the instruction set architecture, giving users a straightforward path to a device-realistic noise model.
Quax-Backed Noise Channels (pyquil/noise/_channels.py)
Four frozen dataclasses representing physical noise processes, each backed by a quax operator (SuperOp, KrausMap, or QuantumInstrument). The main role of the channel is to associate a superoperator with a particular instruction. For example, the instruction CZ 0 1 indicates that qubits 0 and 1 undergo the unitary CZ operation (via the quil spec); the channel associates that instruction with a superoperator — a higher-dimensional representation of the operation that includes the effects of noise.
| Class | Purpose |
|---|---|
Channel |
Noisy gate instruction |
MeasurementChannel |
Noisy measurement (QuantumInstrument) |
ResetChannel |
Noisy reset operation |
CycleChannel |
Noise for an entire instruction cycle (multi-gate group) |
Simulation
A noise model is not very interesting on its own; we want a simulator which can simulate its effects on programs. The existing pyquil simulators are limited: the numpy reference simulators have poor performance, while the QVM has limited options for representing noise. Here we replace the reference numpy simulators with a jax-accelerated, flexible simulation framework.
Simulators (pyquil/simulation/_simulator.py)
A simulator object is constructed from a program. The hierarchy is based on capabilities and scale.
| Simulator | Use Case | JAX-Compatible | Scale | Noise | Measurements |
|---|---|---|---|---|---|
PureStateVectorSimulator |
Gate-only programs (no noise/measurements/resets) | jit + grad |
<26 qubits | No | No |
DensityMatrixSimulator |
Any program, optionally with noise | jit + grad |
<13 qubits | Yes | No |
TrajectorySimulator |
Monte Carlo trajectory simulation | jit (per-batch) |
<26 qubits | Yes | Yes |
DynamicTrajectorySimulator |
Eager, dynamic per-qudit dims (leakage-aware) | No (data-dependent shapes) | leakage-dependent | Yes | Yes |
Linearizer / DAG / Resolver / Compressor Pipeline (pyquil/simulation/_resolver.py)
The simulator is an object rather than a simple function because efficient simulation requires constructing several related closures — the linearizer, resolver, compressor, and calculator.
Linearizer— ConvertsMemoryMap→ flat JAX parameter vector.Resolver— Converts parameter vector →(operator, subsystem)pairs, consulting the noise model.Compressor— Greedy edge contraction on the program DAG, merging adjacent operators up tomax_subsystem_sizequbits.Calculator— Computes the final state from the resolved and compressed operators.
These closures are tightly coupled — each is constructed from the program structure and can be compiled with jax.jit, which is key to good performance. The simulator class combines them into a single logical construct. The architecture is documented in docs/source/simulation_architecture.rst (added in #1860).
Usage
from pyquil.gates import CNOT, H
from pyquil.quil import Program
from pyquil.simulation._simulator import PureStateVectorSimulator
program = Program(H(0), CNOT(0, 1))
sim = PureStateVectorSimulator(program)
psi = sim.compute()
Activity
feat: drop Python 3.9 support, add rigetti-quax (1/5) (#1858)