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 a noise model to a circuit and return a new circuit. |
Classes
Abstract base class for all noise rules. |
|
|
Add noise chosen by user-supplied functions. |
|
Add noise to an operation acting on exactly the given qubits. |
|
Add a readout error after measurements on exactly the given qubits. |
|
Add a readout error after every measurement. |
|
Replace every |
|
Collection of prioritized noise rules. |
|
Add noise to every instance of an operation. |
|
Replace every |
|
Add noise to an operation whose qubits are all in a set. |
|
Add a readout error after measurements whose qubits are all in a set. |
- class mimiqcircuits.noisemodel.AbstractNoiseRule[source]¶
Bases:
AbstractCircuitRuleAbstract base class for all noise rules.
- class mimiqcircuits.noisemodel.GlobalReadoutNoise(noise)[source]¶
Bases:
AbstractNoiseRuleAdd 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
ReadoutErrto apply.
Example
>>> import mimiqcircuits as mc >>> mc.GlobalReadoutNoise(mc.ReadoutErr(0.01, 0.02)) GlobalReadoutNoise(noise=RErr(0.01,0.02))
- noise: ReadoutErr¶
- __init__(noise)¶
- class mimiqcircuits.noisemodel.ExactQubitReadoutNoise(qubits, noise)[source]¶
Bases:
AbstractNoiseRuleAdd 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
ReadoutErrto apply.
- Raises:
ValueError – If
qubitsis 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))
- noise: ReadoutErr¶
- __init__(qubits, noise)¶
- class mimiqcircuits.noisemodel.SetQubitReadoutNoise(qubits, noise)[source]¶
Bases:
AbstractNoiseRuleAdd 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
ReadoutErrto apply.
- Raises:
ValueError – If
qubitsis 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))
- noise: ReadoutErr¶
- __init__(qubits, noise)¶
- class mimiqcircuits.noisemodel.OperationInstanceNoise(operation, noise, before=False, replace=False)[source]¶
Bases:
AbstractNoiseRuleAdd noise to every instance of an operation.
An instruction matches when its operation has the same type as
operation. Ifoperationhas symbolic parameters, such asGateRX(a), any parameter value matches and the symbols innoiseare replaced by the values of the matched instance. Otherwise the operation must be equal tooperation.- Parameters:
operation (Operation) – Operation to match: a gate, measurement, reset,
Block,Repeat,IfStatementorWhileStatement. 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
operationis of an unsupported kind, ifnoiseacts on a different number of qubits, or if bothbeforeandreplaceare 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]
- noise: krauschannel | Gate¶
- __init__(operation, noise, before=False, replace=False)¶
- class mimiqcircuits.noisemodel.ExactOperationInstanceQubitNoise(operation, qubits, noise, before=False, replace=False)[source]¶
Bases:
AbstractNoiseRuleAdd noise to an operation acting on exactly the given qubits.
Matches like
OperationInstanceNoise, and also requires the instruction’s qubits to bequbits, 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
operationis of an unsupported kind, ifqubitshas repetitions or a length other than the number of qubits ofoperation, ifnoiseacts on a different number of qubits, or if bothbeforeandreplaceare 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]
- noise: krauschannel | Gate¶
- __init__(operation, qubits, noise, before=False, replace=False)¶
- class mimiqcircuits.noisemodel.SetOperationInstanceQubitNoise(operation, qubits, noise, before=False, replace=False)[source]¶
Bases:
AbstractNoiseRuleAdd noise to an operation whose qubits are all in a set.
Matches like
OperationInstanceNoise, and also requires every qubit of the instruction to be inqubits.- Parameters:
operation (Operation) – Operation to match (see
OperationInstanceNoise).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
operationis of an unsupported kind, ifqubitshas repetitions or fewer qubits thanoperation, ifnoiseacts on a different number of qubits, or if bothbeforeandreplaceare 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]
- noise: krauschannel | Gate¶
- __init__(operation, qubits, noise, before=False, replace=False)¶
- class mimiqcircuits.noisemodel.IdleNoise(relation)[source]¶
Bases:
AbstractNoiseRuleReplace every
Delaywith a noise operation.Idle time is marked explicitly with
Delay(t)instructions; qubits that are simply not acted upon get no noise. Each matchingDelayis replaced by the noise on the same qubits.- Parameters:
relation (tuple | Operation) – Either a constant noise operation, or a tuple
(t, noise(t))wheretis a symbol. In the second formtis replaced by the duration of eachDelay.- Raises:
ValueError – If
relationis 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]
- __init__(relation)¶
- class mimiqcircuits.noisemodel.SetIdleQubitNoise(relation, qubits)[source]¶
Bases:
AbstractNoiseRuleReplace every
Delayon qubits in a set with a noise operation.Like
IdleNoise, restricted toDelayinstructions whose qubits are all inqubits.- Parameters:
- Raises:
ValueError – If
qubitsis empty, orrelationis 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]
- __init__(relation, qubits)¶
- class mimiqcircuits.noisemodel.CustomNoiseRule(matcher, generator, priority_val=0, before=False, replace=False)[source]¶
Bases:
AbstractNoiseRuleAdd noise chosen by user-supplied functions.
- Parameters:
matcher (Callable[[Instruction], bool]) – Function
inst -> boolthat selects the instructions.generator (Callable[[Instruction], Instruction]) – Function
inst -> Instructionthat 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
CustomNoiseRulecannot be saved withsaveproto: 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]¶
- __init__(matcher, generator, priority_val=0, before=False, replace=False)¶
- class mimiqcircuits.noisemodel.NoiseModel(rules=<factory>, name='')[source]¶
Bases:
objectCollection 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(defaultPRIORITY_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:
rules (List[AbstractNoiseRule]) – Initial list of rules.
name (str) – Optional model name.
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]¶
- add_readout_noise(noise, *, qubits=None, exact=False)[source]¶
Add a readout-noise rule.
Behavior: -
qubits is None: addGlobalReadoutNoise. -qubitswithexact=False: addSetQubitReadoutNoise. -qubitswithexact=True: addExactQubitReadoutNoise.- 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.
operationcan be concrete (exact parameter match) or symbolic (match by operation type and substitute parameters intonoise).- 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
Delayinstructions.noisecan 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.
- __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, andGateCall.- Parameters:
circuit (Circuit) – Input circuit.
model (NoiseModel) – Noise model to apply.
- 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]