Skip to content

Circuit

A Circuit is a stim-like instruction list — gates, noise, measurements, detectors, and observables — recorded once and reused. Build it with the same uppercase methods as the Simulator, then run it, sample it, or hand it to a noise or QEC sampler.

python
from qliff import Circuit

c = Circuit(2)
c.H(0).CX(0, 1).M(0, 1)

Circuit(num_qubits=0) grows its width automatically as you reference higher qubit indices, so the argument is only a hint.

Building blocks

Every instruction is a triple (name, targets, arg). The fluent methods are thin wrappers over append; use whichever reads better.

Gates and measurement

These mirror the simulator and take one or more targets (two-qubit gates take flattened (control, target) pairs).

MethodEffect
H, S, S_DAG, X, Y, Zsingle-qubit Clifford gates
CX (CNOT), CZ, SWAPtwo-qubit Clifford gates
M(*q)measure in the Z basis (appends to the record)
MR(*q)measure, then reset to |0
R(*q)reset to |0

Noise

Noise methods take the target(s) first, then the channel parameter. See Noise for the channels themselves.

MethodParameterChannel
DEPOLARIZE1(q, p)psingle-qubit depolarizing
DEPOLARIZE2(pair, p)ptwo-qubit depolarizing
X_ERROR(q, p), Z_ERROR(q, p)pbit-flip / phase-flip
PAULI_CHANNEL_1(q, (px,py,pz))tuplearbitrary 1-qubit Pauli channel
RZ(q, theta), RX(q, theta)θcoherent rotation
AMPLITUDE_DAMP(q, p)pamplitude damping (non-unitary)

Detectors and observables

A detector is a set of measurement records whose parity is deterministic in the noiseless circuit. An observable is a logical degree of freedom tracked across the run. Record indices use stim's rec[-1] convention: negative indices count back from the measurements declared so far.

MethodDescription
detector(*recs)declare a detector over the given measurement records
observable(index, *recs)declare logical observable index over records
python
c = Circuit(1)
c.M(0)
c.X_ERROR(0, 0.1)
c.M(0)
c.detector(-1, -2)
c.observable(0, -1)

Running and sampling

MethodReturnsDescription
run(seed=None)Simulatorapply a noiseless circuit and return the final state; raises if it contains noise
sample(shots, seed=None)ndarray[uint8]measurement records over shots Pauli-noise trajectories, shape (shots, measurements)
estimate(observable, shots=10000, stratify=None, seed=None)floatreweighted estimate of O
detector_sampler()DetectorSamplera sampler of detection events (see QEC)
dem()DetectorErrorModelthe detector error model (see QEC)

estimate reweights trajectories and is unbiased for any noise, including coherent and non-unitary channels. stratify chooses the variance strategy:

stratifyBehaviourUse when
Noneauto: flat for Pauli, stratified otherwisedefault
Falseflat importance samplingall channels are Pauli
Truestratified by fault count kgeneral noise, lowest variance
python
c = Circuit(1)
c.H(0).RZ(0, 0.3)
c.estimate("X", 20000)                  # ~ cos(0.3)
c.estimate("X", 20000, stratify=False)

Properties

PropertyDescription
num_qubitsregister width (grows as instructions are added)
num_measurementscount of M/MR outcomes recorded so far
instructionsthe underlying (name, targets, arg) list
detectors, observablesthe declared detectors and observables
is_pauliTrue if every noise instruction is a Pauli channel

Low-level and custom channels

append(name, targets, arg=None) adds any instruction directly, and noise(channel, *targets) drops in a custom Channel instance:

python
from qliff.noise import BitFlip

c = Circuit(1)
c.append("H", 0)
c.noise(BitFlip(0.1), 0)