Skip to content

Noise Channels ​

qudit.noise provides building blocks for quantum noise models: Channel holds a Kraus decomposition, Multiplex sequences multiple channels, and the Process factory constructs standard multi-qudit noise models.

Channel ​

A Channel represents a completely positive map in Kraus form:

Φ(⋅)=∑kEk(⋅)Ek†

Each Kraus operator Ek is stored as a list of local Gate objects; an operator "word" that is applied left-to-right via Gate.forwardd.

Property/MethodDescription
opsList of Kraus words (list[list[Gate]])
dFull Hilbert space dimension
run(rho)Apply Φ(ρ)=∑kEkρEk†
correctable()Return Kraus words listed in correctables
isTP∑kEk†Ek=I?
isCPChoi matrix ⪰0?
isCPTPboth CP and TP?
toChoi()Choi–Jamiołkowski matrix J(Φ)
toSuperop()Superoperator S s.t. vec(Φ(ρ))=Svec(ρ)
toStinespring()Stinespring isometry V s.t. Φ(ρ)=Trenv(VρV†)

Running a channel ​

python
# Amplitude damping on 2 qubits
channel = Process.AD(d=2, n=2, Y=0.1)

# Apply to |00><00|
rho = np.zeros((4, 4), dtype=complex)
rho[0, 0] = 1.0

rho_out = channel.run(rho)
print(rho_out.shape)   # (4, 4)
python
from qudit.noise import Process
import numpy as np

Channel analysis ​

python
channel = Process.AD(d=2, n=2, Y=0.1)

print(channel.isTP)    # True
print(channel.isCP)    # True
print(channel.isCPTP)  # True

J = channel.toChoi()   # shape (d^2, d^2)
S = channel.toSuperop()
V = channel.toStinespring()  # isometry: V†V ≈ I
python
from qudit.noise import Process

NOTE

isTP, isCP, isCPTP are cached_property values; they are computed once on first access and cached.

Multiplex ​

Multiplex sequences multiple channels, applying each in turn:

ρ↦Φn∘⋯∘Φ2∘Φ1(ρ)

The main use case is independent identically distributed (IID) noise, where each wire gets its own channel applied sequentially.

python
# IID amplitude damping on 3 qubits
multi = IID.AD(n=3, y=0.05)

rho = np.zeros((8, 8), dtype=complex)
rho[0, 0] = 1.0

rho_out = multi.run(rho)
python
from qudit.noise import IID
import numpy as np

Multiplex flattens nested Multiplex inputs on construction, so Multiplex([multi1, multi2]) gives a single flat list of channels.

Process ​

Process constructs standard multi-qudit noise channels with correctable subsets pre-labeled.

Amplitude damping (AD) ​

Pure amplitude damping uses only lowering operators Ak (p=0 case of GAD):

Ak:|r⟩↦(rk)1/2(1−Y)(r−k)/2Yk/2|r−k⟩
python
channel = Process.AD(d=2, n=4, Y=0.01, order=1)
ArgumentDescription
dLocal dimension per site (any d ≥ 2)
nNumber of physical sites
YDamping parameter Y∈[0,1]
orderMax correctable error order (default 1)
groupIf True, keep correctable sets grouped by order
iidIf True, return Multiplex of single-wire channels

Generalized amplitude damping (GAD) ​

GAD adds raising operators Rk parameterized by environment excitation probability p:

python
channel = Process.GAD(d=2, n=4, Y=0.01, p=0.001, order=1)

Same arguments as AD, plus p (environment excitation probability).

Pauli channel ​

python
px, py, pz = 0.01, 0.005, 0.01
channel = Process.Pauli(n=3, paulis=["X", "Y", "Z"], p=[px, py, pz], order=1)
python
from qudit.noise import Process

Kraus operators are tensor products of {I,pXX,pYY,pZZ} across all n sites. Correctable subsets are labeled by Hamming weight.

Depolarising channel ​

Applies all d2 Weyl-Heisenberg operators as Kraus terms. Works for any d≥2:

Φ(ρ)=(1−p)ρ+pd2∑j,kWjkρWjk†
python
channel = Process.Depolarising(d=2, n=2, p=0.05)
channel = Process.Depolarising(d=3, n=1, p=0.02)  # qutrit

Phase damping ​

Kills off-diagonal coherences without energy exchange. For d=2: K0=diag(1,1−p), K1=diag(0,p). Works for any d:

python
channel = Process.PhaseDamp(d=2, n=2, p=0.1)

Bit-flip and phase-flip channels ​

Qudit generalizations using the cyclic shift Xd and clock Zd operators:

python
channel = Process.BitFlip(d=2, n=2, p=0.05)   # applies X_d with prob p
channel = Process.PhaseFlip(d=2, n=2, p=0.05)  # applies Z_d with prob p

Both reduce to the standard qubit channels when d=2.

Reset channel ​

Collapses each qudit to |0⟩ with probability p:

Φ(ρ)=(1−p)ρ+p|0⟩⟨0|Tr(ρ)
python
channel = Process.Reset(d=2, n=2, p=0.1)

Thermal relaxation ​

Combined T1 energy decay and T2 dephasing for qubits (d=2 only). Requires T2≤2T1:

python
channel = Process.ThermalRelax(n=2, T1=100e-6, T2=80e-6, t=50e-9)

All Process channels accept an iid=True flag to return a Multiplex of independent single-site channels instead of a joint n-site channel.

Weyl-Heisenberg channel (NoisyGate) ​

For circuit-level noise in Mode.NOISY, NoisyGate("weyl", param, ...) implements the Heisenberg-Weyl displacement channel for any local dimension d:

Φ(ρ)=(1−∑(m,n)≠(0,0)pmn)ρ+∑(m,n)≠(0,0)pmnWmnρWmn†

where Wmn=XdmZdn, Xd is the cyclic shift, and Zd is the clock operator. param is a length-(d2−1) tensor of probabilities in row-major order (0,1),(0,2),…,(d−1,d−1) excluding (0,0).

python
from qudit.circuit.gates import NoisyGate
import torch

# Qubit Weyl (d=2): 3 parameters for W_01=Z, W_10=X, W_11=XZ
ng2 = NoisyGate("weyl", torch.tensor([0.02, 0.01, 0.01]), index=0, wires=1, dims=2)

# Qutrit Weyl (d=3): 8 parameters for all (m,n) ≠ (0,0)
ng3 = NoisyGate("weyl", torch.tensor([0.005]*8), index=0, wires=1, dims=3)

Correctable subsets ​

After building a channel, channel.correctables holds a flat list (or grouped list if group=True) of Kraus-word indices that are considered correctable up to the given order.

python
channel = Process.GAD(2, 4, Y=0.01, p=0.001)
Ek = channel.correctable()  # list of Kraus operator lists

These are passed directly to Recovery.leung for constructing recovery maps (see Error Correction).

IID noise ​

IID builds single-wire channels and multiplexes them for independent identically distributed noise. Both factories work for any local dimension d ≥ 2:

FactorySignatureDescription
IID.AD(n, d, y)Amplitude damping on each of n qudits independently
IID.GAD(n, d, y, p)Generalized amplitude damping on each qudit independently
IID.Depolarising(n, d, p)Depolarising channel on each qudit independently
IID.PhaseDamp(n, d, p)Phase damping on each qudit independently
IID.BitFlip(n, d, p)Bit-flip (Xd) on each qudit independently
IID.PhaseFlip(n, d, p)Phase-flip (Zd) on each qudit independently
IID.Reset(n, d, p)Reset to $
python
qubit = IID.AD(n=3, d=2, y=0.05)

qutrit = IID.AD(n=2, d=3, y=0.1)
python
from qudit.noise import IID

TIP

Use Process.AD(d=..., n=..., iid=True) as a shortcut; it delegates to IID.AD and returns a Multiplex.