Channels & Noise API Reference¶
Noise here is just a set of Kraus operators (a Channel), and a NoiseModel is
the bag of channels you hand to a simulator. Anything built from Kraus operators
needs the DensityMatrixSimulator; classical readout error is the one exception,
and works on either backend.
from qutritium.channels import (
Channel, NoiseModel, SPAMNoiseModel, ReadoutError,
depolarizing_channel, dephasing_channel,
amplitude_damping_channel, pauli_channel,
)
Channel¶
A Channel holds a Completely Positive Trace-Preserving (CPTP) map as its Kraus operators \(\{K_i\}\), which have to
satisfy completeness, \(\sum_i K_i^\dagger K_i = I\) — the constructor checks that
for you:
Channel(kraus_operators, num_qutrits=1)
Build one from a list of Kraus operators.
Channel.kraus
The Kraus list (you get a copy).
Channel.apply_kraus_op(rho)
Apply the map to a density matrix.
Preset channels¶
These are the ones you'll reach for most. All single-qutrit (\(d = 3\)).
depolarizing_channel(p)
With probability p, throw the state away and replace it with the maximally
mixed state: \((1-p)\rho + p\,I/3\).
dephasing_channel(p)
Kill the off-diagonal coherences but leave the populations alone: \((1-p)\rho + p\,\mathrm{diag}(\rho)\).
amplitude_damping_channel(gamma_10, gamma_21=None)
Relaxation down the ladder, \(|1\rangle\to|0\rangle\) at rate \(\gamma_{10}\) and
\(|2\rangle\to|1\rangle\) at \(\gamma_{21}\) (one rung per application; gamma_21
defaults to gamma_10). Apply it enough times and any state ends up in
\(|0\rangle\).
pauli_channel(probabilities)
A random mix of Weyl unitaries, \(\sum_g p_g\,U_g\rho U_g^\dagger\). Pass a dict
from operator name ("identity", "x_plus", "z", "x01", …) to its
probability; they have to be non-negative and add up to 1.
ReadoutError¶
Measurement error is classical, so we model it as a confusion matrix \(A\) instead of a quantum channel: \(A_{j,i}\) is the chance of reading \(j\) when the true outcome was \(i\). We just apply it to the probability vector, \(p_\text{meas} = A\,p_\text{ideal}\), which is why it works on both simulators.
ReadoutError(confusion_matrix)
A has to be square, \(3^n\)-dimensional, non-negative, and column-stochastic.
ReadoutError.from_single_qutrit(a, n_qutrit)
Tensor a per-qutrit \(3\times3\) matrix up to the full register.
NoiseModel¶
The NoiseModel is the container you attach to a simulator with
set_noise_model. It carries three kinds of error:
add_quantum_error(channel, gate_label)
Add a Kraus channel after every gate with that label. Density-matrix sim only.
add_prep_error(channel, qutrit=None)
Add a Kraus channel once, before any gate. Density-matrix sim only.
add_readout_error(readout)
Add a readout confusion matrix. Applied in run() on both simulators.
NoiseModel.has_kraus_errors
Whether any gate or prep channel is set — that's how the StatevectorSimulator
knows to turn a model down.
SPAMNoiseModel¶
If all you want is a bit of state-prep and measurement error, SPAMNoiseModel
builds both halves for you.
SPAMNoiseModel(p_prep, p_meas, n_qutrit=1)
Gives every qutrit a symmetric prep pauli_channel (weight p_prep/2 each on
x_plus/x_minus) plus a symmetric readout matrix with off-diagonal mass
p_meas. It carries a Kraus prep channel, so it's density-matrix only.
Examples¶
Applying a channel directly¶
import numpy as np
from qutritium.channels import amplitude_damping_channel, depolarizing_channel
rho = np.diag([0, 0, 1]).astype(complex) # |2><2|
amplitude_damping_channel(1.0).apply_kraus_op(rho) # -> |1><1|
depolarizing_channel(1.0).apply_kraus_op(rho) # -> I/3
Attaching a NoiseModel to a simulator¶
from qutritium import QutritCircuit, DensityMatrixSimulator
from qutritium.channels import NoiseModel, depolarizing_channel
from qutritium.gates import X01
qc = QutritCircuit(1, None)
qc.append(X01(), first_qutrit=0)
qc.measure_all()
nm = NoiseModel()
nm.add_quantum_error(depolarizing_channel(0.3), "X01")
dm = DensityMatrixSimulator(qc)
dm.set_noise_model(nm)
print(dm.probabilities()) # [0.1, 0.8, 0.1]
SPAM noise in one call¶
from qutritium.channels import SPAMNoiseModel
dm2 = DensityMatrixSimulator(qc) # fresh simulator -- set_noise_model must run before run()/probabilities()
dm2.set_noise_model(SPAMNoiseModel(p_prep=0.02, p_meas=0.03))
Notes¶
- Everything here is single-qutrit for now; a model just applies its channel to each target qutrit one at a time.
- We use Kraus operators, not Lindblad, and the noise sits on the simulator rather than the circuit. So you can take one circuit and run it noiseless or noisy just by giving fresh simulators different models.
References¶
- Gottesman, D. (1998). arXiv:quant-ph/9802007 — Weyl operators \(W_{ab} = X^a Z^b\) used by
pauli_channel - Schwinger, J. (1960). PNAS 46, 570 — unitary operator bases