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[, ...])

Add noise chosen by user-supplied functions.

ExactOperationInstanceQubitNoise(operation, ...)

Add noise to an operation acting on exactly the given qubits.

ExactQubitReadoutNoise(qubits, noise)

Add a readout error after measurements on exactly the given qubits.

GlobalReadoutNoise(noise)

Add a readout error after every measurement.

IdleNoise(relation)

Replace every Delay with a noise operation.

NoiseModel([rules, name])

Collection of prioritized noise rules.

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

Add noise to every instance of an operation.

SetIdleQubitNoise(relation, qubits)

Replace every Delay on qubits in a set with a noise operation.

SetOperationInstanceQubitNoise(operation, ...)

Add noise to an operation whose qubits are all in a set.

SetQubitReadoutNoise(qubits, noise)

Add a readout error after measurements whose qubits are all in a set.

class mimiqcircuits.noisemodel.AbstractNoiseRule[source]

Bases: AbstractCircuitRule

Abstract base class for all noise rules.

class mimiqcircuits.noisemodel.GlobalReadoutNoise(noise)[source]

Bases: AbstractNoiseRule

Add a readout error after every measurement.

The readout error acts on the classical bits the measurement writes to, so it is placed right after the measurement and does not replace it.

Parameters:

noise (ReadoutErr) – The ReadoutErr to apply.

Example

>>> import mimiqcircuits as mc
>>> mc.GlobalReadoutNoise(mc.ReadoutErr(0.01, 0.02))
GlobalReadoutNoise(noise=RErr(0.01,0.02))
noise: ReadoutErr
priority()[source]
matches(inst)[source]
apply_rule(inst)[source]
__init__(noise)
class mimiqcircuits.noisemodel.ExactQubitReadoutNoise(qubits, noise)[source]

Bases: AbstractNoiseRule

Add a readout error after measurements on exactly the given qubits.

A measurement matches only when its qubits are qubits, in the same order.

Parameters:
  • qubits (Sequence[int]) – Qubits the measurement must act on, in order.

  • noise (ReadoutErr) – The ReadoutErr to apply.

Raises:

ValueError – If qubits is empty or has repetitions.

Example

>>> import mimiqcircuits as mc
>>> mc.ExactQubitReadoutNoise([0, 1], mc.ReadoutErr(0.01, 0.02))
ExactQubitReadoutNoise(qubits=(0, 1), noise=RErr(0.01,0.02))
qubits: Sequence[int]
noise: ReadoutErr
priority()[source]
matches(inst)[source]
apply_rule(inst)[source]
__init__(qubits, noise)
class mimiqcircuits.noisemodel.SetQubitReadoutNoise(qubits, noise)[source]

Bases: AbstractNoiseRule

Add a readout error after measurements whose qubits are all in a set.

Parameters:
  • qubits (Iterable[int]) – Qubits where the readout error applies.

  • noise (ReadoutErr) – The ReadoutErr to apply.

Raises:

ValueError – If qubits is empty or has repetitions.

Example

>>> import mimiqcircuits as mc
>>> mc.SetQubitReadoutNoise([0, 2, 4], mc.ReadoutErr(0.01, 0.02))
SetQubitReadoutNoise(qubits=[0, 2, 4], noise=RErr(0.01,0.02))
qubits: Iterable[int]
noise: ReadoutErr
priority()[source]
matches(inst)[source]
apply_rule(inst)[source]
__init__(qubits, noise)
class mimiqcircuits.noisemodel.OperationInstanceNoise(operation, noise, before=False, replace=False)[source]

Bases: AbstractNoiseRule

Add noise to every instance of an operation.

An instruction matches when its operation has the same type as operation. If operation has symbolic parameters, such as GateRX(a), any parameter value matches and the symbols in noise are replaced by the values of the matched instance. Otherwise the operation must be equal to operation.

Parameters:
  • operation (Operation) – Operation to match: a gate, measurement, reset, Block, Repeat, IfStatement or WhileStatement. Symbolic parameters must be plain symbols.

  • noise (krauschannel | Gate) – Kraus channel or gate to add, on the same number of qubits as operation.

  • before (bool) – Add the noise before the operation instead of after it.

  • replace (bool) – Replace the operation with the noise.

Raises:

ValueError – If operation is of an unsupported kind, if noise acts on a different number of qubits, or if both before and replace are set.

Example

>>> import mimiqcircuits as mc
>>> from symengine import symbols, pi
>>> a = symbols("a")
>>> rule = mc.OperationInstanceNoise(mc.GateRX(a), mc.Depolarizing1(a / pi))
>>> c = mc.Circuit().push(mc.GateRX(pi / 2), 0)
>>> mc.NoiseModel([rule]).apply_noise_model(c)
1-qubit circuit with 2 instructions:
├── RX((1/2)*pi) @ q[0]
└── Depolarizing(0.5) @ q[0]
operation: Operation
noise: krauschannel | Gate
before: bool = False
replace: bool = False
priority()[source]
replaces()[source]
matches(inst)[source]
apply_rule(inst)[source]
__init__(operation, noise, before=False, replace=False)
class mimiqcircuits.noisemodel.ExactOperationInstanceQubitNoise(operation, qubits, noise, before=False, replace=False)[source]

Bases: AbstractNoiseRule

Add noise to an operation acting on exactly the given qubits.

Matches like OperationInstanceNoise, and also requires the instruction’s qubits to be qubits, in the same order.

Parameters:
  • operation (Operation) – Operation to match (see OperationInstanceNoise).

  • qubits (Sequence[int]) – Qubits the operation must act on, in order.

  • noise (krauschannel | Gate) – Kraus channel or gate to add, on the same number of qubits as operation.

  • before (bool) – Add the noise before the operation instead of after it.

  • replace (bool) – Replace the operation with the noise.

Raises:

ValueError – If operation is of an unsupported kind, if qubits has repetitions or a length other than the number of qubits of operation, if noise acts on a different number of qubits, or if both before and replace are set.

Example

>>> import mimiqcircuits as mc
>>> rule = mc.ExactOperationInstanceQubitNoise(mc.GateCX(), [0, 1], mc.Depolarizing2(0.01))
>>> c = mc.Circuit().push(mc.GateCX(), 0, 1).push(mc.GateCX(), 1, 0)
>>> mc.NoiseModel([rule]).apply_noise_model(c)
2-qubit circuit with 3 instructions:
├── CX @ q[0], q[1]
├── Depolarizing(0.01) @ q[0:1]
└── CX @ q[1], q[0]
operation: Operation
qubits: Sequence[int]
noise: krauschannel | Gate
before: bool = False
replace: bool = False
priority()[source]
replaces()[source]
matches(inst)[source]
apply_rule(inst)[source]
__init__(operation, qubits, noise, before=False, replace=False)
class mimiqcircuits.noisemodel.SetOperationInstanceQubitNoise(operation, qubits, noise, before=False, replace=False)[source]

Bases: AbstractNoiseRule

Add noise to an operation whose qubits are all in a set.

Matches like OperationInstanceNoise, and also requires every qubit of the instruction to be in qubits.

Parameters:
  • operation (Operation) – Operation to match (see OperationInstanceNoise).

  • qubits (Iterable[int]) – Qubits where the noise applies.

  • noise (krauschannel | Gate) – Kraus channel or gate to add, on the same number of qubits as operation.

  • before (bool) – Add the noise before the operation instead of after it.

  • replace (bool) – Replace the operation with the noise.

Raises:

ValueError – If operation is of an unsupported kind, if qubits has repetitions or fewer qubits than operation, if noise acts on a different number of qubits, or if both before and replace are set.

Example

>>> import mimiqcircuits as mc
>>> rule = mc.SetOperationInstanceQubitNoise(mc.GateH(), [0, 1], mc.AmplitudeDamping(0.001))
>>> c = mc.Circuit().push(mc.GateH(), 0).push(mc.GateH(), 2)
>>> mc.NoiseModel([rule]).apply_noise_model(c)
3-qubit circuit with 3 instructions:
├── H @ q[0]
├── AmplitudeDamping(0.001) @ q[0]
└── H @ q[2]
operation: Operation
qubits: Iterable[int]
noise: krauschannel | Gate
before: bool = False
replace: bool = False
priority()[source]
replaces()[source]
matches(inst)[source]
apply_rule(inst)[source]
__init__(operation, qubits, noise, before=False, replace=False)
class mimiqcircuits.noisemodel.IdleNoise(relation)[source]

Bases: AbstractNoiseRule

Replace every Delay with a noise operation.

Idle time is marked explicitly with Delay(t) instructions; qubits that are simply not acted upon get no noise. Each matching Delay is replaced by the noise on the same qubits.

Parameters:

relation (tuple | Operation) – Either a constant noise operation, or a tuple (t, noise(t)) where t is a symbol. In the second form t is replaced by the duration of each Delay.

Raises:

ValueError – If relation is neither an operation nor a (symbol, operation) tuple.

Example

>>> import mimiqcircuits as mc
>>> from symengine import symbols
>>> t = symbols("t")
>>> rule = mc.IdleNoise((t, mc.AmplitudeDamping(t / 1000)))
>>> c = mc.Circuit().push(mc.GateH(), 0).push(mc.Delay(10), 1)
>>> mc.NoiseModel([rule]).apply_noise_model(c)
2-qubit circuit with 2 instructions:
├── H @ q[0]
└── AmplitudeDamping(0.01) @ q[1]
relation: tuple | Operation
priority()[source]
replaces()[source]
matches(inst)[source]
apply_rule(inst)[source]
__init__(relation)
class mimiqcircuits.noisemodel.SetIdleQubitNoise(relation, qubits)[source]

Bases: AbstractNoiseRule

Replace every Delay on qubits in a set with a noise operation.

Like IdleNoise, restricted to Delay instructions whose qubits are all in qubits.

Parameters:
  • relation (tuple | Operation) – Constant noise operation or (t, noise(t)) tuple (see IdleNoise).

  • qubits (Iterable[int]) – Qubits where the noise applies.

Raises:

ValueError – If qubits is empty, or relation is neither an operation nor a (symbol, operation) tuple.

Example

>>> import mimiqcircuits as mc
>>> rule = mc.SetIdleQubitNoise(mc.AmplitudeDamping(1e-4), [0])
>>> c = mc.Circuit().push(mc.Delay(10), 0).push(mc.Delay(10), 1)
>>> mc.NoiseModel([rule]).apply_noise_model(c)
2-qubit circuit with 2 instructions:
├── AmplitudeDamping(0.0001) @ q[0]
└── Delay(10) @ q[1]
relation: tuple | Operation
qubits: Iterable[int]
priority()[source]
replaces()[source]
matches(inst)[source]
apply_rule(inst)[source]
__init__(relation, qubits)
class mimiqcircuits.noisemodel.CustomNoiseRule(matcher, generator, priority_val=0, before=False, replace=False)[source]

Bases: AbstractNoiseRule

Add noise chosen by user-supplied functions.

Parameters:
  • matcher (Callable[[Instruction], bool]) – Function inst -> bool that selects the instructions.

  • generator (Callable[[Instruction], Instruction]) – Function inst -> Instruction that builds the noise instruction for a matched instruction.

  • priority_val (int) – Priority of the rule; lower runs first. Defaults to PRIORITY_USER_OVERRIDE, ahead of every built-in rule.

  • before (bool) – Add the noise before the instruction instead of after it.

  • replace (bool) – Replace the instruction with the noise.

A noise model containing a CustomNoiseRule cannot be saved with saveproto: the matcher and generator are Python callables with no serialized form.

Example

>>> import mimiqcircuits as mc
>>> rule = mc.CustomNoiseRule(
...     lambda inst: inst.get_operation().num_qubits == 2,
...     lambda inst: mc.Instruction(mc.Depolarizing2(0.01), inst.get_qubits()),
... )
>>> c = mc.Circuit().push(mc.GateCX(), 0, 1)
>>> mc.NoiseModel([rule]).apply_noise_model(c)
2-qubit circuit with 2 instructions:
├── CX @ q[0], q[1]
└── Depolarizing(0.01) @ q[0:1]
matcher: Callable[[Instruction], bool]
generator: Callable[[Instruction], Instruction]
priority_val: int = 0
before: bool = False
replace: bool = False
priority()[source]
matches(inst)[source]
apply_rule(inst)[source]
replaces()[source]
__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_step()
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_step()
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_step()
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_step()
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_step()
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_step().decompose_step()
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_step()
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_step().decompose_step()
2-qubit circuit with 4 instructions:
├── H @ q[0]
├── AmplitudeDamping(0.01) @ q[0]
├── H @ q[1]
└── AmplitudeDamping(0.01) @ q[1]