API reference

Everything below imports from deltakit_maestro. Most code needs only MaestroBackend; the Guide explains how it runs each circuit.

Backend

class MaestroBackend(simulator_type=maestro.SimulatorType.QCSim, simulation_type=None, max_bond_dimension=32, **config)

Maestro simulator backend for Deltakit circuits.

Parameters:
  • simulator_type (maestro.SimulatorType) – Maestro engine; SimulatorType.Gpu for GPU.

  • simulation_type (maestro.SimulationType | None) – Stabilizer, MatrixProductState, Statevector, TensorNetwork, … None (default) picks per run: Stabilizer unless the run has coherent noise, then MatrixProductState. Stabilizer samples Pauli noise on Pauli frames; T1 decay, which is not a Pauli channel, it runs shot by shot. Any other value runs everything with that type.

  • max_bond_dimension (int) – MPS/TTN bond dimension χ.

  • **config – Further maestro.SimulatorConfig fields, except seed: pass that to simulate or compile_sampler, since under sinter.collect every worker would draw the same shots. fixed_backend defaults to True, so the simulation type above is the one that runs; False lets Maestro replace it, e.g. sample a Clifford circuit on Pauli frames instead of an MPS.

Noise keyword arguments (p_1q, p_2q, readout_error, t1, coherent) on the methods below override or extend the rates read from the circuit; see build_noise_model.

simulate(circuit, shots, noise_type='pauli', seed=None, **noise)

Run shots and return raw measurements plus detection events.

Return type:

SimulationResult

compile_sampler(circuit, noise_type='pauli', decoder='pymatching', seed=None, **noise)

MaestroCompiledSampler for measurement or detection-event sampling.

decoder is compiled against the task’s DEM and used by .sample(); .sample_detection_events() and .sample_measurements() ignore it. Pass decoder='vacuous' (or None) for undecoded logical error statistics. Shots come back in random order, so any slice of them is a fair sample.

Return type:

MaestroCompiledSampler

as_sinter_task(circuit, noise_type='pauli', json_metadata=None, **noise)

Circuit + matching DEM as a sinter.Task.

For 'pauli' and 'full' without explicit Pauli overrides (p_1q, p_2q, readout_error), the noisy Stim circuit is kept intact so Maestro executes its in-circuit Pauli channels and measurement flips directly. Otherwise the gate noise is stripped (strip_gate_noise) and the NoiseModel replaces it; idle, reset and measurement noise stay in the circuit. The NoiseModel layers travel in json_metadata['noise'] so the sampler can rebuild them in each worker.

The DEM is that of twirled_circuit, so the decoder knows the noise that is simulated. For 'none' there is no such noise and the input circuit’s own DEM is used. The DEM is decomposed into graphlike errors for matching decoders where it can be; where it can’t, as for bivariate bicycle codes, it is left whole for decoders such as BP-OSD.

Return type:

Task

sinter_decoders(decoder='pymatching')

custom_decoders= for sinter.collect over as_sinter_task tasks.

Return type:

dict[str, Sampler]

twirled_circuit(circuit, noise_type='pauli', **noise)

Stim circuit carrying the Pauli twirl of the noise that is simulated.

Its DEM is the one as_sinter_task gives the decoder. Sampling it (simulate or compile_sampler with the default 'pauli', which runs a circuit’s own noise as it is) gives the Pauli-twirled baseline to compare a coherent run against. For 'pauli' without overrides it is the input circuit.

Return type:

Circuit

config_for(noise_type='pauli', **noise)

The maestro.SimulatorConfig a run with this noise uses.

A run is Clifford, so Stabilizer, unless it has coherent noise. For 'coherent' and 'full' the strength is coherent, else the circuit’s p_2q. Without either keyword that rate is unknown here, so it is taken to be nonzero and the answer is MatrixProductState; the sampler itself knows the circuit and decides exactly.

The type is pinned (fixed_backend=True) unless fixed_backend was passed to the backend: unpinned, Maestro runs any Clifford circuit on Pauli frames whatever the type, so a noiseless MPS check of χ would never see truncation. A pinned Stabilizer still uses frames.

Return type:

SimulatorConfig

class SimulationResult(measurements, detection_events, observable_flips, metadata=<factory>)

Output of MaestroBackend.simulate.

measurements

(shots, num_measurements) uint8, Stim record order.

Type:

numpy.ndarray

detection_events

(shots, num_detectors) bool.

Type:

numpy.ndarray

observable_flips

(shots, num_observables) bool.

Type:

numpy.ndarray

metadata

Backend, shot count and the noise parameters used.

Type:

dict[str, Any]

property num_measurements: int

Measurements in each shot’s record.

SINTER_DECODER = 'maestro'

The decoder name that MaestroBackend.as_sinter_task() gives its tasks. MaestroBackend.sinter_decoders() maps it to the Maestro sampler.

Noise translation

The steps MaestroBackend takes with a noisy circuit, for using them directly. The guide’s Noise page explains them.

to_stim(circuit)

Deltakit Circuit (or anything Stim-like) → native stim.Circuit.

Return type:

Circuit

noise_params_from_stim(noisy)

Read the gate and readout error rates out of a noisy Stim circuit.

The gate rates are those of the depolarizing that strip_gate_noise removes. Warns if the circuit has several rates for one of them, since a single rate replaces them all.

Returns:

{'p_1q', 'p_2q', 'readout_error'}, ready to splat into build_noise_model.

Return type:

dict[str, float]

strip_gate_noise(noisy, readout=False)

noisy without the depolarizing that follows its gates.

A DEPOLARIZE1 or DEPOLARIZE2 target is gate noise if its qubit’s latest operation was a gate of the same arity that no noise has followed yet. Deltakit puts it after the next TICK, merged with the idle noise of the other qubits, so the targets of one instruction are split: gate noise goes, idle noise stays. Resets, measurements and every other noise channel stay too. Returns a flattened circuit.

Parameters:
  • noisy (Circuit) – The Stim circuit.

  • readout (bool) – Also remove measurement flips M(p), for when a NoiseModel supplies the readout error instead.

Return type:

Circuit

build_noise_model(circuit, noise_type='pauli', *, p_1q=0.0, p_2q=0.0, readout_error=0.0, t1=0.0, coherent=None)

Build a maestro.NoiseModel for circuit.

Parameters:
  • circuit (Circuit) – Stim circuit; supplies the qubit count and the two-qubit gate pairs that carry two-qubit depolarizing.

  • noise_type (str) – How gate errors are realised. 'pauli': stochastic depolarizing. 'coherent': isotropic coherent over-rotations (ε = 2·arcsin√p about a body diagonal after every gate). 'full': both. 'none': no noise at all.

  • p_1q (float) – Depolarizing probability after single-qubit gates.

  • p_2q (float) – Two-qubit depolarizing probability after two-qubit gates.

  • readout_error (float) – Symmetric measurement flip probability.

  • t1 (float) – Per-gate T1 decay probability γ. DensityMatrix and MatrixProductOperator apply amplitude damping γ; every other simulation type resets the qubit to |0⟩ with probability γ.

  • coherent (float | None) – Infidelity of the coherent over-rotation, applied per qubit after every gate, one- or two-qubit. Defaults to p_2q.

Return type:

NoiseModel

pauli_twirl_circuit(circuit, noise_type='pauli', *, p_1q=0.0, p_2q=0.0, readout_error=0.0, t1=0.0, coherent=None, amplitude_damping=False)

circuit plus the Pauli twirl of build_noise_model’s layers.

Takes the same arguments as build_noise_model and places each channel where Maestro injects it: after every gate except I, which Maestro runs as an idle. The circuit’s own Stim noise is kept. The twirl is exact for the Pauli layers; the coherent over-rotation twirls to DEPOLARIZE1(coherent). T1 decay γ twirls to PAULI_CHANNEL_1(γ/4, γ/4, γ/4), the reset to |0⟩ with probability γ that Maestro’s pure-state simulation types run.

Parameters:

amplitude_damping (bool) – Twirl T1 as amplitude damping γ instead, to PAULI_CHANNEL_1(γ/4, γ/4, (1-√(1-γ))²/4), as DensityMatrix and MatrixProductOperator simulate it.

Return type:

Circuit

NOISE_TYPES = ('pauli', 'coherent', 'full', 'none')

The values noise_type takes.

NOISE_KEYS = ('p_1q', 'p_2q', 'readout_error', 't1', 'coherent')

The noise keywords that override or extend the rates read from a circuit.