mimiqcircuits.noisemodel

Noise model definitions and application.

This module implements rule-based noise injection for mimiqcircuits circuits. Rules are evaluated by priority (lower value means higher priority), and the first matching rule is applied to each instruction.

Functions

apply_noise_model(circuit, model)

Apply a noise model to a circuit and return a new circuit.

Classes

AbstractNoiseRule()

Abstract base class for all noise rules.

CustomNoiseRule(matcher, generator[, ...])

ExactOperationInstanceQubitNoise(operation, ...)

ExactQubitReadoutNoise(qubits, noise)

GlobalReadoutNoise(noise)

IdleNoise(relation)

NoiseModel([rules, name])

Collection of prioritized noise rules.

OperationInstanceNoise(operation, noise[, ...])

SetIdleQubitNoise(relation, qubits)

SetOperationInstanceQubitNoise(operation, ...)

SetQubitReadoutNoise(qubits, noise)

class mimiqcircuits.noisemodel.AbstractNoiseRule[source]

Bases: object

Abstract base class for all noise rules.

priority()[source]

Lower = higher priority.

before()[source]

If True, apply noise before the operation.

replaces()[source]

If True, noise instruction replaces the original instruction.

matches(inst)[source]
apply_rule(inst)[source]
class mimiqcircuits.noisemodel.GlobalReadoutNoise(noise: 'mc.ReadoutErr')[source]

Bases: AbstractNoiseRule

noise: ReadoutErr
priority()[source]

Lower = higher priority.

matches(inst)[source]
apply_rule(inst)[source]
__init__(noise)
class mimiqcircuits.noisemodel.ExactQubitReadoutNoise(qubits: 'Sequence[int]', noise: 'mc.ReadoutErr')[source]

Bases: AbstractNoiseRule

qubits: Sequence[int]
noise: ReadoutErr
priority()[source]

Lower = higher priority.

matches(inst)[source]
apply_rule(inst)[source]
__init__(qubits, noise)
class mimiqcircuits.noisemodel.SetQubitReadoutNoise(qubits: 'Iterable[int]', noise: 'mc.ReadoutErr')[source]

Bases: AbstractNoiseRule

qubits: Iterable[int]
noise: ReadoutErr
priority()[source]

Lower = higher priority.

matches(inst)[source]
apply_rule(inst)[source]
__init__(qubits, noise)
class mimiqcircuits.noisemodel.OperationInstanceNoise(operation: 'mc.Operation', noise: 'Union[mc.krauschannel, mc.Gate]', before: 'bool' = False, replace: 'bool' = False)[source]

Bases: AbstractNoiseRule

operation: Operation
noise: krauschannel | Gate
before: bool = False
replace: bool = False
priority()[source]

Lower = higher priority.

replaces()[source]

If True, noise instruction replaces the original instruction.

matches(inst)[source]
apply_rule(inst)[source]
__init__(operation, noise, before=False, replace=False)
class mimiqcircuits.noisemodel.ExactOperationInstanceQubitNoise(operation: 'mc.Operation', qubits: 'Sequence[int]', noise: 'Union[mc.krauschannel, mc.Gate]', before: 'bool' = False, replace: 'bool' = False)[source]

Bases: AbstractNoiseRule

operation: Operation
qubits: Sequence[int]
noise: krauschannel | Gate
before: bool = False
replace: bool = False
priority()[source]

Lower = higher priority.

replaces()[source]

If True, noise instruction replaces the original instruction.

matches(inst)[source]
apply_rule(inst)[source]
__init__(operation, qubits, noise, before=False, replace=False)
class mimiqcircuits.noisemodel.SetOperationInstanceQubitNoise(operation: 'mc.Operation', qubits: 'Iterable[int]', noise: 'Union[mc.krauschannel, mc.Gate]', before: 'bool' = False, replace: 'bool' = False)[source]

Bases: AbstractNoiseRule

operation: Operation
qubits: Iterable[int]
noise: krauschannel | Gate
before: bool = False
replace: bool = False
priority()[source]

Lower = higher priority.

replaces()[source]

If True, noise instruction replaces the original instruction.

matches(inst)[source]
apply_rule(inst)[source]
__init__(operation, qubits, noise, before=False, replace=False)
class mimiqcircuits.noisemodel.IdleNoise(relation: 'Union[tuple, mc.Operation]')[source]

Bases: AbstractNoiseRule

relation: tuple | Operation
priority()[source]

Lower = higher priority.

replaces()[source]

If True, noise instruction replaces the original instruction.

matches(inst)[source]
apply_rule(inst)[source]
__init__(relation)
class mimiqcircuits.noisemodel.SetIdleQubitNoise(relation: 'Union[tuple, mc.Operation]', qubits: 'Iterable[int]')[source]

Bases: AbstractNoiseRule

relation: tuple | Operation
qubits: Iterable[int]
priority()[source]

Lower = higher priority.

replaces()[source]

If True, noise instruction replaces the original instruction.

matches(inst)[source]
apply_rule(inst)[source]
__init__(relation, qubits)
class mimiqcircuits.noisemodel.CustomNoiseRule(matcher: 'Callable[[mc.Instruction], bool]', generator: 'Callable[[mc.Instruction], mc.Instruction]', priority_val: 'int' = 0, before: 'bool' = False, replace: 'bool' = False)[source]

Bases: AbstractNoiseRule

matcher: Callable[[Instruction], bool]
generator: Callable[[Instruction], Instruction]
priority_val: int = 0
before: bool = False
replace: bool = False
priority()[source]

Lower = higher priority.

matches(inst)[source]
apply_rule(inst)[source]
replaces()[source]

If True, noise instruction replaces the original instruction.

__init__(matcher, generator, priority_val=0, before=False, replace=False)
class mimiqcircuits.noisemodel.NoiseModel(rules=<factory>, name='')[source]

Bases: object

Collection of prioritized noise rules.

Rules are always kept sorted by priority() so more specific rules win over generic fallbacks.

Priority order (lower number = higher priority): - CustomNoiseRule (default PRIORITY_USER_OVERRIDE) - ExactOperationInstanceQubitNoise (PRIORITY_EXACT_OPERATION) - ExactQubitReadoutNoise (PRIORITY_EXACT_READOUT) - SetOperationInstanceQubitNoise (PRIORITY_SET_OPERATION) - SetQubitReadoutNoise (PRIORITY_SET_READOUT) - OperationInstanceNoise (PRIORITY_GLOBAL_OPERATION) - GlobalReadoutNoise (PRIORITY_GLOBAL_READOUT) - SetIdleQubitNoise (PRIORITY_SET_IDLE) - IdleNoise (PRIORITY_IDLE)

Parameters:

Example

>>> import mimiqcircuits as mc
>>> from symengine import symbols, pi
>>> theta = symbols("theta")
>>> model = mc.NoiseModel(
...     [
...         mc.OperationInstanceNoise(mc.GateRX(theta), mc.Depolarizing(1, theta / pi)),
...         mc.ExactOperationInstanceQubitNoise(mc.GateCX(), [0, 1], mc.Depolarizing(2, 0.01)),
...         mc.GlobalReadoutNoise(mc.ReadoutErr(0.01, 0.02)),
...         mc.IdleNoise(mc.AmplitudeDamping(1e-4)),
...     ],
...     name="angle-dependent",
... )
rules: List[AbstractNoiseRule]
name: str = ''
add_rule(rule)[source]

Add a rule and keep model rules sorted by priority.

add_readout_noise(noise, *, qubits=None, exact=False)[source]

Add a readout-noise rule.

Behavior: - qubits is None: add GlobalReadoutNoise. - qubits with exact=False: add SetQubitReadoutNoise. - qubits with exact=True: add ExactQubitReadoutNoise.

Parameters:
  • noise (ReadoutErr) – Readout error channel.

  • qubits – Optional qubit targets.

  • exact – When True, qubit order must match exactly.

Returns:

self (for chaining).

Example

>>> import mimiqcircuits as mc
>>> model = mc.NoiseModel()
>>> model.add_readout_noise(mc.ReadoutErr(0.01, 0.02))
NoiseModel(rules=[GlobalReadoutNoise(noise=RErr(0.01,0.02))], name='')
>>> model.add_readout_noise(mc.ReadoutErr(0.03, 0.04), qubits=[0, 2])
NoiseModel(rules=[SetQubitReadoutNoise(qubits=(0, 2), noise=RErr(0.03,0.04)), GlobalReadoutNoise(noise=RErr(0.01,0.02))], name='')
>>> model.add_readout_noise(mc.ReadoutErr(0.05, 0.06), qubits=[2, 0], exact=True)
NoiseModel(rules=[ExactQubitReadoutNoise(qubits=(2, 0), noise=RErr(0.05,0.06)), SetQubitReadoutNoise(qubits=(0, 2), noise=RErr(0.03,0.04)), GlobalReadoutNoise(noise=RErr(0.01,0.02))], name='')
add_operation_noise(operation, noise, *, qubits=None, exact=False, before=False, replace=False)[source]

Add operation-instance noise.

operation can be concrete (exact parameter match) or symbolic (match by operation type and substitute parameters into noise).

Parameters:
  • operation – Operation pattern to match.

  • noise – Noise operation/channel to inject.

  • qubits – Optional qubit restriction.

  • exact – If True, qubits must match the exact ordered tuple.

  • before – If True, insert noise before the matched instruction.

  • replace – If True, replace the matched instruction with noise.

Returns:

self (for chaining).

Raises:

ValueError – If operation target is unsupported or arguments are invalid.

Examples

>>> import mimiqcircuits as mc
>>> from symengine import symbols, pi
>>> theta, alpha, beta = symbols("theta alpha beta")
>>> model = mc.NoiseModel()
>>> _ = model.add_operation_noise(mc.GateRX(pi / 2), mc.AmplitudeDamping(0.001))
>>> _ = model.add_operation_noise(mc.GateRX(theta), mc.Depolarizing(1, theta / pi))
>>> _ = model.add_operation_noise(
...     mc.GateU(alpha, beta, 0),
...     mc.Depolarizing(1, (alpha**2 + beta**2) / (2 * pi**2)),
... )
>>> _ = model.add_operation_noise(
...     mc.GateRX(theta),
...     mc.Depolarizing(1, theta / pi),
...     qubits=[0, 1, 2],
... )
>>> _ = model.add_operation_noise(
...     mc.GateRX(theta),
...     mc.Depolarizing(1, theta / pi),
...     qubits=[0],
...     exact=True,
... )
>>> _ = model.add_operation_noise(mc.Measure(), mc.PauliX(0.02), before=True)
>>> _ = model.add_operation_noise(mc.Reset(), mc.Depolarizing(1, 0.01))
>>> _ = model.add_operation_noise(mc.GateH(), mc.AmplitudeDamping(0.001), replace=True)
add_idle_noise(noise, qubits=None)[source]

Add idle-noise rule(s) for Delay instructions.

noise can be a constant operation or a relation tuple (time_symbol, target_operation_expr).

Parameters:
  • noise – Idle noise operation or symbolic relation.

  • qubits – Optional qubit subset where idle noise is allowed.

Returns:

self (for chaining).

Example

>>> import mimiqcircuits as mc
>>> from symengine import symbols
>>> t = symbols("t")
>>> model = mc.NoiseModel()
>>> model.add_idle_noise(mc.AmplitudeDamping(1e-4))
NoiseModel(rules=[IdleNoise(relation=AmplitudeDamping(0.0001))], name='')
>>> model.add_idle_noise((t, mc.AmplitudeDamping(t / 1000)), qubits=[0, 1, 2])
NoiseModel(rules=[SetIdleQubitNoise(relation=(t, AmplitudeDamping((1/1000)*t)), qubits=frozenset({0, 1, 2})), IdleNoise(relation=AmplitudeDamping(0.0001))], name='')
apply_noise_model(circuit)[source]

Apply this noise model and return a new noisy circuit.

This is a convenience wrapper around module-level apply_noise_model.

describe()[source]
saveproto(file)[source]
static loadproto(file)[source]
__init__(rules=<factory>, name='')
mimiqcircuits.noisemodel.apply_noise_model(circuit, model)[source]

Apply a noise model to a circuit and return a new circuit.

Rules are evaluated in priority order. For each instruction, only the first matching rule is applied.

Wrapper operations are traversed recursively: Block, IfStatement, Parallel, Repeat, and GateCall.

Parameters:
Returns:

A new circuit with injected noise.

Examples

>>> import mimiqcircuits as mc
>>> from symengine import symbols, pi
>>> theta = symbols("theta")
>>> c = mc.Circuit()
>>> c.push(mc.GateRX(0.4), 0)
1-qubit circuit with 1 instruction:
└── RX(0.4) @ q[0]

>>> c.push(mc.GateRX(0.8), 1)
2-qubit circuit with 2 instructions:
├── RX(0.4) @ q[0]
└── RX(0.8) @ q[1]

>>> c.push(mc.Measure(), 0, 0)
2-qubit, 1-bit circuit with 3 instructions:
├── RX(0.4) @ q[0]
├── RX(0.8) @ q[1]
└── M @ q[0], c[0]

>>> c.push(mc.Measure(), 1, 1)
2-qubit, 2-bit circuit with 4 instructions:
├── RX(0.4) @ q[0]
├── RX(0.8) @ q[1]
├── M @ q[0], c[0]
└── M @ q[1], c[1]

>>> model = mc.NoiseModel([mc.OperationInstanceNoise(mc.GateRX(theta), mc.Depolarizing(1, theta / pi)),
...         mc.GlobalReadoutNoise(mc.ReadoutErr(0.01, 0.02)),])
>>> noisy = mc.apply_noise_model(c, model)
Recursive wrapper examples:
>>> model = mc.NoiseModel([mc.OperationInstanceNoise(mc.GateH(), mc.AmplitudeDamping(0.01))])
>>> inner = mc.Circuit().push(mc.GateH(), 0)
>>> c_block = mc.Circuit().push(mc.Block(inner), 0)
>>> noisy_block= mc.apply_noise_model(c_block, model)
>>> noisy_block.decompose()
1-qubit circuit with 2 instructions:
├── H @ q[0]
└── AmplitudeDamping(0.01) @ q[0]

>>> decl = mc.GateDecl("local_h", (), inner)
>>> c_call = mc.Circuit().push(mc.GateCall(decl, ()), 0)
>>> noisy_call = mc.apply_noise_model(c_call, model)
>>> noisy_call.decompose()
1-qubit circuit with 2 instructions:
├── H @ q[0]
└── AmplitudeDamping(0.01) @ q[0]

>>> c_parallel = mc.Circuit().push(mc.Parallel(2, mc.GateH()), 0, 1)
>>> noisy_parallel = mc.apply_noise_model(c_parallel, model)
>>> noisy_parallel.decompose()
2-qubit circuit with 4 instructions:
├── H @ q[0]
├── AmplitudeDamping(0.01) @ q[0]
├── H @ q[1]
└── AmplitudeDamping(0.01) @ q[1]

>>> c_repeat = mc.Circuit().push(mc.Repeat(2, mc.GateH()), 0)
>>> noisy_repeat = mc.apply_noise_model(c_repeat, model)
>>> noisy_repeat.decompose()
1-qubit circuit with 4 instructions:
├── H @ q[0]
├── AmplitudeDamping(0.01) @ q[0]
├── H @ q[0]
└── AmplitudeDamping(0.01) @ q[0]

>>> c_if = mc.Circuit().push(mc.IfStatement(mc.GateH(), mc.BitString("1")), 0, 0)
>>> noisy_if = mc.apply_noise_model(c_if, model)
>>> noisy_if.decompose()
1-qubit, 1-bit circuit with 2 instructions:
├── IF(c==1) H @ q[0], condition[0]
└── IF(c==1) AmplitudeDamping(0.01) @ q[0], condition[0]
>>> decl_inner = mc.GateDecl("inner_h", (), inner)
>>> middle = mc.Circuit().push(mc.GateCall(decl_inner, ()), 0)
>>> decl_outer = mc.GateDecl("outer_call", (), middle)
>>> c_nested = mc.Circuit().push(mc.GateCall(decl_outer, ()), 0)
>>> noisy_nested = mc.apply_noise_model(c_nested, model)
>>> noisy_nested.decompose().decompose()
1-qubit circuit with 2 instructions:
├── H @ q[0]
└── AmplitudeDamping(0.01) @ q[0]
Recursive wrapper examples deeply nested:
>>> c_nested2 = mc.Circuit().push(mc.GateCall(decl_outer, ()), 0).push(mc.GateCall(decl_outer, ()), 1)
>>> c_nested2.decompose()
2-qubit circuit with 2 instructions:
├── inner_h() @ q[0]
└── inner_h() @ q[1]

>>> noisy_nested2 = mc.apply_noise_model(c_nested2, model)
>>> noisy_nested2.decompose().decompose()
2-qubit circuit with 4 instructions:
├── H @ q[0]
├── AmplitudeDamping(0.01) @ q[0]
├── H @ q[1]
└── AmplitudeDamping(0.01) @ q[1]