Noise¶
MaestroBackend reads the noise from the circuit, so you don’t build a
maestro.NoiseModel. Maestro runs the circuit’s own Stim noise, and
noise_type and the noise keywords say what to add to it or replace.
examples/example_noise_modes.py shows each step
on a Deltakit circuit and a Stim-generated one.
The circuit’s own noise¶
Maestro executes Stim’s Pauli noise channels (DEPOLARIZE1/2, X/Y/Z_ERROR,
PAULI_CHANNEL_1/2) and measurement flips (M(p)) natively in the circuit. So
with the default noise_type='pauli' and no overrides, a noisy circuit (SI1000,
for example) runs with exactly its own noise.
Noise types¶
noise_type controls how errors are realised:
|
Gate & circuit errors |
|---|---|
|
exact in-circuit Stim Pauli & readout noise (or replacement gate noise when |
|
gate depolarizing replaced by isotropic coherent over-rotation, ε = 2·arcsin√p about a body diagonal after every gate; idle, reset and readout noise kept |
|
circuit noise and coherent over-rotation both |
|
none (noiseless) |
Readout error applies in every mode except 'none'. Coherent
noise describes the over-rotation in more detail.
Overriding the rates¶
The noise keywords override or extend the rates read from the circuit:
Keyword |
Meaning |
|---|---|
|
depolarizing probability after single-qubit gates |
|
two-qubit depolarizing probability after two-qubit gates |
|
symmetric measurement flip probability |
|
per-gate |
|
infidelity of the coherent over-rotation; defaults to |
backend.simulate(noisy, 1000, noise_type='full', coherent=0.002, t1=1e-4)
Unknown keywords raise TypeError, and keywords the noise_type never reads
raise ValueError, before anything is sampled: 'pauli' doesn’t read
coherent, 'coherent' doesn’t read p_1q, and 'none' reads none of them.
coherent sets the strength of the over-rotation that 'coherent' puts in
place of the gate noise and 'full' adds to it. t1 adds T1 decay to
whatever runs, after every gate: DensityMatrix and MatrixProductOperator
apply amplitude damping γ, and every other simulation type resets the qubit to
|0⟩ with probability γ.
When the gate noise is replaced¶
When noise_type is 'coherent', or a Pauli rate (p_1q, p_2q,
readout_error) is overridden, the circuit’s gate noise is removed with
strip_gate_noise() and the maestro.NoiseModel
supplies its replacement: the coherent over-rotation for 'coherent', and
otherwise depolarizing at the overridden rates:
Stim (from Deltakit) |
|
|---|---|
|
|
|
|
|
|
Gate noise is the depolarizing whose qubit’s latest operation was a gate of the
same arity. Deltakit places it after the next TICK, merged with the idle noise
of other qubits, so one instruction’s targets are split: gate noise goes, idle
noise stays. Idle, reset (X_ERROR) and measurement (M(p)) noise always stay
in the circuit, and the rates come from the stripped instructions themselves. A
warning is raised if they differ across gates, since one rate replaces them
all. Only DEPOLARIZE1/2 is treated as gate noise; other channels after gates
(X_ERROR, for example) stay in the circuit.
Working with the noise directly¶
The functions MaestroBackend uses are public, for looking at a circuit’s noise
yourself:
from deltakit_maestro import build_noise_model, noise_params_from_stim, strip_gate_noise, to_stim
stim_circuit = to_stim(noisy) # a Deltakit SI1000(p=0.01) circuit
rates = noise_params_from_stim(stim_circuit) # {'p_1q': 0.001, 'p_2q': 0.01, 'readout_error': 0.05}
clean_gates = strip_gate_noise(stim_circuit) # the circuit without its gate noise
model = build_noise_model(clean_gates, 'coherent', p_2q=rates['p_2q'])
The API reference lists their arguments, and
Custom Maestro noise model shows how to
customize per-qubit and per-coupler channels on the resulting maestro.NoiseModel
and sample it with MaestroCompiledSampler and MaestroSinterSampler.