Noise Model

graphqomb.noise_model module

Noise model interface for Stim circuit compilation.

This module provides:

See also

stim_compile

The main compilation function that accepts a NoiseModel.

Notes

  • Placement control: Each NoiseOp has a placement attribute. AUTO defers to default_noise_placement(), while BEFORE/AFTER force insertion side.

  • Record delta: Heralded instructions (HeraldedPauliChannel1, HeraldedErase) add measurement records. The compiler automatically tracks these to compute correct detector indices.

  • Coordinate access: Events provide NodeInfo objects with optional coordinates, useful for position-dependent noise models.

Examples

Create a simple depolarizing noise model:

>>> from graphqomb.noise_model import (
...     NoiseModel,
...     PrepareEvent,
...     EntangleEvent,
...     PauliChannel1,
...     PauliChannel2,
...     depolarize1_probs,
...     depolarize2_probs,
... )
>>>
>>> class DepolarizingNoise(NoiseModel):
...     def __init__(self, p1: float, p2: float) -> None:
...         self.p1 = p1  # Single-qubit depolarizing probability
...         self.p2 = p2  # Two-qubit depolarizing probability
...
...     def on_prepare(self, event: PrepareEvent) -> list[PauliChannel1]:
...         return [PauliChannel1(**depolarize1_probs(self.p1), targets=[event.node.id])]
...
...     def on_entangle(self, event: EntangleEvent) -> list[PauliChannel2]:
...         return [
...             PauliChannel2.from_mapping(
...                 probabilities=depolarize2_probs(self.p2),
...                 targets=[(event.node0.id, event.node1.id)],
...             )
...         ]

Use with stim_compile:

>>> from graphqomb.stim_compiler import stim_compile
>>> # pattern = ...  # your compiled pattern
>>> # stim_str = stim_compile(pattern, noise_models=[DepolarizingNoise(0.001, 0.01)])

Use heralded noise that adds measurement records:

>>> from graphqomb.noise_model import NoiseModel, MeasureEvent, HeraldedPauliChannel1
>>>
>>> class HeraldedMeasurementNoise(NoiseModel):
...     def on_measure(self, event: MeasureEvent) -> list[HeraldedPauliChannel1]:
...         # Heralded erasure with 10% probability
...         return [HeraldedPauliChannel1(pi=0.1, px=0.0, py=0.0, pz=0.0, targets=[event.node.id])]
graphqomb.noise_model.depolarize1_probs(p)[source]

Create probability dict for single-qubit depolarizing channel.

Parameters:

p (float) – Total depolarizing probability.

Returns:

Mapping with keys px, py, pz each set to p/3.

Return type:

dict[str, float]

Examples

>>> probs = depolarize1_probs(0.03)
>>> probs["px"]
0.01
>>> probs["py"]
0.01
graphqomb.noise_model.depolarize2_probs(p)[source]

Create probability dict for 2-qubit depolarizing channel.

Parameters:

p (float) – Total depolarizing probability.

Returns:

Mapping from Pauli pair to probability p/15.

Return type:

dict[str, float]

Examples

>>> probs = depolarize2_probs(0.15)
>>> probs["ZZ"]
0.01
>>> len(probs)
15
class graphqomb.noise_model.NoisePlacement[source]

Where to insert noise relative to the main operation.

AUTO = 1
BEFORE = 2
AFTER = 3
class graphqomb.noise_model.Coordinate[source]

N-dimensional coordinate for a node.

Parameters:

values (tuple[float, …]) – The coordinate values as a tuple of floats.

Examples

>>> coord = Coordinate((1.0, 2.0, 3.0))
>>> coord.xy
(1.0, 2.0)
>>> coord.xyz
(1.0, 2.0, 3.0)
values: tuple[float, ...]
property xy: tuple[float, float] | None

Return the first two dimensions as (x, y), or None if fewer than 2 dimensions.

property xyz: tuple[float, float, float] | None

Return the first three dimensions as (x, y, z), or None if fewer than 3 dimensions.

__init__(values)
class graphqomb.noise_model.NodeInfo[source]

Node identifier with optional coordinate.

Parameters:
  • id (int) – The unique node index in the pattern.

  • coord (Coordinate | None) – The spatial coordinate of the node, if available.

id: int
coord: Coordinate | None
__init__(id, coord)
class graphqomb.noise_model.PrepareEvent[source]

Event emitted when a qubit is prepared (N command).

Parameters:
  • time (int) – The current tick (time step) in the pattern execution.

  • node (NodeInfo) – Information about the node being prepared.

  • is_input (bool) – Whether this node is an input node of the pattern. Input nodes may require different noise treatment.

time: int
node: NodeInfo
is_input: bool
__init__(time, node, is_input)
class graphqomb.noise_model.EntangleEvent[source]

Event emitted when two qubits are entangled (E command / CZ gate).

Parameters:
  • time (int) – The current tick (time step) in the pattern execution.

  • node0 (NodeInfo) – Information about the first node in the entanglement.

  • node1 (NodeInfo) – Information about the second node in the entanglement.

  • edge (tuple[int, int]) – The edge as (min_node_id, max_node_id).

time: int
node0: NodeInfo
node1: NodeInfo
edge: tuple[int, int]
__init__(time, node0, node1, edge)
class graphqomb.noise_model.MeasureEvent[source]

Event emitted when a qubit is measured (M command).

Parameters:
  • time (int) – The current tick (time step) in the pattern execution.

  • node (NodeInfo) – Information about the node being measured.

  • axis (Axis) – The measurement axis (X, Y, or Z).

time: int
node: NodeInfo
axis: Axis
__init__(time, node, axis)
class graphqomb.noise_model.IdleEvent[source]

Event emitted for qubits that are idle during a TICK.

Parameters:
  • time (int) – The current tick (time step) in the pattern execution.

  • nodes (collections.abc.Sequence[NodeInfo]) – Information about all nodes that are idle during this tick.

  • duration (float) – The duration of the idle period (from tick_duration parameter).

time: int
nodes: Sequence[NodeInfo]
duration: float
__init__(time, nodes, duration)
graphqomb.noise_model.NoiseEvent = graphqomb.noise_model.PrepareEvent | graphqomb.noise_model.EntangleEvent | graphqomb.noise_model.MeasureEvent | graphqomb.noise_model.IdleEvent

Union type of all noise event types.

graphqomb.noise_model.default_noise_placement(event)[source]

Return the global default placement for AUTO noise operations.

Measurement noise is inserted before measurement operations. Noise for all other events is inserted after the corresponding operation.

Parameters:

event (NoiseEvent) – The event for which to determine the default placement.

Returns:

BEFORE for measurement events, AFTER for all others.

Return type:

NoisePlacement

class graphqomb.noise_model.PauliChannel1[source]

Single-qubit Pauli channel noise operation.

Applies independent X, Y, Z errors with given probabilities. Corresponds to Stim’s PAULI_CHANNEL_1 instruction.

Parameters:

Examples

>>> op = PauliChannel1(px=0.01, py=0.01, pz=0.01, targets=[0, 1])
>>> noise_op_to_stim(op)
('PAULI_CHANNEL_1(0.01,0.01,0.01) 0 1', 0)
px: float
py: float
pz: float
targets: Sequence[int]
placement: NoisePlacement = 1
__init__(px, py, pz, targets, placement=NoisePlacement.AUTO)
class graphqomb.noise_model.PauliChannel2[source]

Two-qubit Pauli channel noise operation.

Applies correlated two-qubit Pauli errors. Corresponds to Stim’s PAULI_CHANNEL_2 instruction.

Parameters:

Examples

Using a mapping (recommended for sparse errors):

>>> op = PauliChannel2.from_mapping(probabilities={"ZZ": 0.01}, targets=[(0, 1)])
>>> text, delta = noise_op_to_stim(op)
>>> "PAULI_CHANNEL_2" in text
True

Using a full probability sequence:

>>> probs = [0.0] * 14 + [0.01]  # Only ZZ error
>>> op = PauliChannel2.from_sequence(probabilities=probs, targets=[(2, 3)])
probabilities: tuple[float, ...]
targets: tuple[tuple[int, int], ...]
placement: NoisePlacement = 1
classmethod from_mapping(probabilities, targets, placement=NoisePlacement.AUTO)[source]

Build a Pauli channel from sparse Pauli-pair probabilities.

Returns:

A normalized two-qubit Pauli channel.

Return type:

PauliChannel2

classmethod from_sequence(probabilities, targets, placement=NoisePlacement.AUTO)[source]

Build a Pauli channel from probabilities in Stim’s required order.

Returns:

A normalized two-qubit Pauli channel.

Return type:

PauliChannel2

__init__(probabilities, targets, placement=NoisePlacement.AUTO)
class graphqomb.noise_model.HeraldedPauliChannel1[source]

Heralded single-qubit Pauli channel noise operation.

Similar to PauliChannel1 but produces a herald measurement record indicating whether an error occurred. The herald outcome is 1 if any error occurred (including identity with probability pi). Corresponds to Stim’s HERALDED_PAULI_CHANNEL_1 instruction.

Parameters:

Notes

This instruction adds one measurement record per target qubit. The compiler automatically tracks this when computing detector indices.

Examples

>>> op = HeraldedPauliChannel1(pi=0.0, px=0.01, py=0.0, pz=0.0, targets=[5])
>>> text, delta = noise_op_to_stim(op)
>>> text
'HERALDED_PAULI_CHANNEL_1(0.0,0.01,0.0,0.0) 5'
>>> delta  # One record added per target
1
pi: float
px: float
py: float
pz: float
targets: Sequence[int]
placement: NoisePlacement = 1
__init__(pi, px, py, pz, targets, placement=NoisePlacement.AUTO)
class graphqomb.noise_model.HeraldedErase[source]

Heralded erasure noise operation.

Models photon loss or erasure errors with a herald signal. Corresponds to Stim’s HERALDED_ERASE instruction.

Parameters:

Notes

This instruction adds one measurement record per target qubit. The compiler automatically tracks this when computing detector indices.

Examples

>>> op = HeraldedErase(p=0.05, targets=[0, 1, 2])
>>> text, delta = noise_op_to_stim(op)
>>> text
'HERALDED_ERASE(0.05) 0 1 2'
>>> delta  # One record added per target
3
p: float
targets: Sequence[int]
placement: NoisePlacement = 1
__init__(p, targets, placement=NoisePlacement.AUTO)
class graphqomb.noise_model.RawStimOp[source]

Raw Stim instruction for advanced use cases.

Use this when the typed noise operations don’t cover your use case. The text is inserted directly into the Stim circuit.

Parameters:
  • text (str) – A single Stim instruction line (without trailing newline).

  • record_delta (int) – The number of measurement records added by this instruction. Most noise instructions do not add records (default 0).

  • placement (NoisePlacement) – Whether to insert before or after the main operation. AUTO defers to default_noise_placement().

Examples

>>> op = RawStimOp("X_ERROR(0.001) 0 1 2")
>>> noise_op_to_stim(op)
('X_ERROR(0.001) 0 1 2', 0)

With custom record delta for measurement-like instructions:

>>> op = RawStimOp("MR 5", record_delta=1)
>>> noise_op_to_stim(op)
('MR 5', 1)
text: str
record_delta: int = 0
placement: NoisePlacement = 1
__init__(text, record_delta=0, placement=NoisePlacement.AUTO)
class graphqomb.noise_model.MeasurementFlip[source]

Measurement flip error applied to measurement instruction.

Unlike other NoiseOp types that insert separate instructions, this modifies the measurement instruction itself to use Stim’s built-in measurement error probability: MX(p) instead of MX.

Parameters:
  • p (float) – Probability of measurement result flip.

  • target (int) – Target qubit index (must match the measurement target).

  • placement (NoisePlacement) – Placement attribute for compatibility (ignored, as this modifies the measurement instruction itself).

p: float
target: int
placement: NoisePlacement = 1
__init__(p, target, placement=NoisePlacement.AUTO)
graphqomb.noise_model.NoiseOp = graphqomb.noise_model.PauliChannel1 | graphqomb.noise_model.PauliChannel2 | graphqomb.noise_model.HeraldedPauliChannel1 | graphqomb.noise_model.HeraldedErase | graphqomb.noise_model.RawStimOp | graphqomb.noise_model.MeasurementFlip

Union type of all noise operation types.

class graphqomb.noise_model.NoiseModel[source]

Base class for custom noise injection during Stim compilation.

Subclass this to define custom noise behavior by overriding one or more of the event handler methods. Each method receives an event object with context about the current operation and returns noise operations to inject.

Examples

>>> class SimpleNoise(NoiseModel):
...     def on_prepare(self, event: PrepareEvent) -> list[PauliChannel1]:
...         # Add depolarizing noise after preparation
...         p = 0.001 / 3
...         return [PauliChannel1(px=p, py=p, pz=p, targets=[event.node.id])]
...
...     def on_measure(self, event: MeasureEvent) -> list[PauliChannel1]:
...         # Add bit-flip noise before measurement
...         return [
...             PauliChannel1(px=0.01, py=0.0, pz=0.0, targets=[event.node.id], placement=NoisePlacement.BEFORE)
...         ]
on_prepare(event)[source]

Return noise operations to inject at qubit preparation.

Parameters:

event (PrepareEvent) – Context about the preparation operation.

Returns:

Zero or more noise operations to inject.

Return type:

collections.abc.Sequence[NoiseOp]

on_entangle(event)[source]

Return noise operations to inject at entanglement.

Parameters:

event (EntangleEvent) – Context about the entanglement operation.

Returns:

Zero or more noise operations to inject.

Return type:

collections.abc.Sequence[NoiseOp]

on_measure(event)[source]

Return noise operations to inject at measurement.

Parameters:

event (MeasureEvent) – Context about the measurement operation.

Returns:

Zero or more noise operations to inject.

Return type:

collections.abc.Sequence[NoiseOp]

on_idle(event)[source]

Return noise operations to inject during idle periods.

Parameters:

event (IdleEvent) – Context about the idle period.

Returns:

Zero or more noise operations to inject.

Return type:

collections.abc.Sequence[NoiseOp]

graphqomb.noise_model.noise_op_to_stim(op)[source]

Convert a NoiseOp into a Stim instruction line and record delta.

Parameters:

op (NoiseOp) – The noise operation to convert.

Returns:

A tuple of (stim_instruction, record_delta) where stim_instruction is a single line of Stim code and record_delta is the number of measurement records added.

Return type:

tuple[str, int]

Raises:

TypeError – If op is not a recognized NoiseOp type.

Examples

>>> op = PauliChannel1(px=0.01, py=0.02, pz=0.03, targets=[0])
>>> noise_op_to_stim(op)
('PAULI_CHANNEL_1(0.01,0.02,0.03) 0', 0)
class graphqomb.noise_model.DepolarizingNoiseModel[source]

Depolarizing noise after single and two-qubit gates.

This model adds depolarizing noise after qubit preparation (RX) and entanglement (CZ) operations.

Parameters:
  • p1 (float) – Single-qubit depolarizing probability (after RX preparation).

  • p2 (float | None) – Two-qubit depolarizing probability (after CZ). If None, defaults to p1.

Examples

>>> from graphqomb.noise_model import DepolarizingNoiseModel
>>> model = DepolarizingNoiseModel(p1=0.001, p2=0.01)
>>> # Use with stim_compile:
>>> # stim_compile(pattern, noise_models=[model])
__init__(p1, p2=None)[source]
on_prepare(event)[source]

Add single-qubit depolarizing noise after preparation.

Returns:

A tuple containing DEPOLARIZE1 instruction, or empty if p1 <= 0.

Return type:

collections.abc.Sequence[NoiseOp]

on_entangle(event)[source]

Add two-qubit depolarizing noise after entanglement.

Returns:

A tuple containing DEPOLARIZE2 instruction, or empty if p2 <= 0.

Return type:

collections.abc.Sequence[NoiseOp]

class graphqomb.noise_model.MeasurementFlipNoiseModel[source]

Measurement bit-flip noise using Stim’s built-in measurement error.

This model produces MX(p), MY(p), MZ(p) instead of MX, MY, MZ, which adds measurement flip error with probability p.

Parameters:

p (float) – Probability of measurement result flip.

Examples

>>> from graphqomb.noise_model import MeasurementFlipNoiseModel
>>> model = MeasurementFlipNoiseModel(p=0.001)
>>> # Use with stim_compile:
>>> # stim_compile(pattern, noise_models=[model])
__init__(p)[source]
on_measure(event)[source]

Add measurement flip error.

Returns:

A tuple containing MeasurementFlip operation, or empty if p <= 0.

Return type:

collections.abc.Sequence[NoiseOp]