mimiqcircuits.lossmodel¶
Loss model definitions and qubit-loss sampling.
This module provides the rule system used by lower_losses() (and hence
resolve_losses()) when a circuit instruction touches both lost and
surviving qubits. A LossModel lets users decide whether such an
instruction should be dropped, replaced, decorated, or handled by custom logic.
Examples
>>> from mimiqcircuits import *
>>> circuit = Circuit()
>>> _ = circuit.push(Loss(), 1)
>>> _ = circuit.push(GateCX(), 0, 1)
>>> model = LossModel().add_replace(GateCX(), Depolarizing1(0.2))
>>> resolve_losses(circuit, lossmodel=model)
2-qubit circuit with 2 instructions:
├── Lost @ q[1]
└── Depolarizing(0.2) @ q[0]
Functions
|
Decide how a single instruction touching both lost and present qubits is rewritten under a |
|
Lower a circuit with loss operations into one using only primitives. |
|
Fully resolve loss into a runnable, loss-free circuit. |
|
Build a deterministic "what-if" loss scenario from a circuit. |
|
Resolve the random qubit-loss events in a circuit. |
Classes
|
User-defined loss rule. |
|
Add a decoration before or after a matched instruction. |
|
Drop matched instructions that touch lost qubits. |
|
Collection of prioritized loss rules. |
|
Replace a matched instruction with new instructions. |
- class mimiqcircuits.lossmodel.AbstractCircuitRule[source]¶
Bases:
objectShared base class for circuit transformation rules.
- class mimiqcircuits.lossmodel.DropRule(operation=None)[source]¶
Bases:
AbstractCircuitRuleDrop matched instructions that touch lost qubits.
A
DropRuleis useful when a partially affected operation should not be salvaged. Ifoperationis omitted, the rule matches any operation that reaches the loss model.- Parameters:
operation (optional) – Operation pattern to drop. If omitted, the rule is a catch-all rule.
Examples
>>> from mimiqcircuits import * >>> model = LossModel().add_drop(GateCX()) >>> model LossModel (unnamed, 1 rules) └── DropRule(CX)
>>> circuit = Circuit() >>> _ = circuit.push(Loss(), 1) >>> _ = circuit.push(GateCX(), 0, 1) >>> circuit.resolve_losses(lossmodel=model) 2-qubit circuit with 1 instruction: └── Lost @ q[1]
- class mimiqcircuits.lossmodel.DecorateRule(operation, decoration=None, *, before=False)[source]¶
Bases:
AbstractCircuitRuleAdd a decoration before or after a matched instruction.
During loss sampling, any generated instruction that still touches a lost qubit is filtered out. This makes
DecorateRuleuseful for modeling a side effect on surviving qubits when an operation was attempted but one of its qubits was missing.- Parameters:
operation – Operation pattern to match.
decoration – Operation or instruction sequence to add.
before (bool) – If
True, add the decoration before the matched instruction. Otherwise, add it after.
Examples
>>> from mimiqcircuits import * >>> model = LossModel().add_decorate(GateCZ(), Depolarizing1(0.01), before=True) >>> model LossModel (unnamed, 1 rules) └── DecorateRule(CZ, Depolarizing(0.01), before)
>>> circuit = Circuit() >>> _ = circuit.push(Loss(), 1) >>> _ = circuit.push(GateCZ(), 0, 1) >>> circuit.resolve_losses(lossmodel=model) 2-qubit circuit with 2 instructions: ├── Lost @ q[1] └── Depolarizing(0.01) @ q[0]
- class mimiqcircuits.lossmodel.ReplaceRule(operation, replacement=None)[source]¶
Bases:
AbstractCircuitRuleReplace a matched instruction with new instructions.
Use
ReplaceRulewhen a partially affected operation should be removed and replaced by another operation on surviving qubits. A one-qubit replacement is broadcast to each target of the matched instruction, and copies on lost qubits are filtered out.- Parameters:
operation – Operation pattern to match.
replacement – Replacement operation or instruction sequence.
Examples
>>> from mimiqcircuits import * >>> model = LossModel().add_replace(GateCX(), Depolarizing1(0.2)) >>> model LossModel (unnamed, 1 rules) └── ReplaceRule(CX => Depolarizing(0.2))
>>> circuit = Circuit() >>> _ = circuit.push(Loss(), 1) >>> _ = circuit.push(GateCX(), 0, 1) >>> circuit.resolve_losses(lossmodel=model) 2-qubit circuit with 2 instructions: ├── Lost @ q[1] └── Depolarizing(0.2) @ q[0]
- class mimiqcircuits.lossmodel.CustomRule(matcher, generator)[source]¶
Bases:
AbstractCircuitRuleUser-defined loss rule.
CustomRuleis the escape hatch for loss policies that cannot be expressed withDropRule,ReplaceRule, orDecorateRule. The matcher decides whether the rule applies. The generator returnsNoneto drop the instruction, oneInstruction, or a sequence of instructions.The generator may accept
(inst),(inst, lost), or(inst, lost, rng). It may also acceptrngas a keyword argument.- Parameters:
matcher – Callable that receives an instruction and returns
Trueif the rule should apply.generator – Callable that generates replacement instructions.
Examples
Define a custom fallback for a partially lost
CX. If the control qubit survives, replace the failedCXbyXon the control. If the control is lost, returnNoneto drop the instruction.>>> from mimiqcircuits import * >>> def cx_control_fallback(inst, lost): ... control = inst.get_qubits()[0] ... if lost.get(control, False): ... return None ... return Instruction(GateX(), (control,)) ... >>> model = LossModel([ ... CustomRule( ... lambda inst: isinstance(inst.get_operation(), GateCX), ... cx_control_fallback, ... ) ... ]) >>> model LossModel (unnamed, 1 rules) └── CustomRule(<callable>)
>>> circuit = Circuit() >>> _ = circuit.push(Loss(), 1) >>> _ = circuit.push(GateCX(), 0, 1) >>> circuit.resolve_losses(lossmodel=model) 2-qubit circuit with 2 instructions: ├── Lost @ q[1] └── X @ q[0]
Another custom rule can generate one instruction for each surviving qubit. Here a partially lost
CXbecomesZon every qubit that is still present:>>> model = LossModel([ ... CustomRule( ... lambda inst: isinstance(inst.get_operation(), GateCX), ... lambda inst, lost: [ ... Instruction(GateZ(), (q,)) ... for q in inst.get_qubits() ... if not lost.get(q, False) ... ], ... ) ... ]) >>> circuit.resolve_losses(lossmodel=model) 2-qubit circuit with 2 instructions: ├── Lost @ q[1] └── Z @ q[0]
- class mimiqcircuits.lossmodel.LossModel(rules=None, name='')[source]¶
Bases:
objectCollection of prioritized loss rules.
A
LossModeltellssample_losses()what to do when an instruction touches both lost and surviving qubits. With no rules, the conservative behavior is used: instructions touching lost qubits are dropped.- Parameters:
rules (optional) – Iterable of loss rules.
name (str) – Optional model name used in display output.
Examples
>>> from mimiqcircuits import * >>> model = LossModel(name="My Loss Model") >>> model LossModel (My Loss Model, 0 rules)
Rules can be added incrementally:
>>> model.add_replace(GateCX(), Depolarizing1(0.2)) LossModel (My Loss Model, 1 rules) └── ReplaceRule(CX => Depolarizing(0.2))
The model can then be passed to
sample_losses:>>> circuit = Circuit() >>> _ = circuit.push(Loss(), 1) >>> _ = circuit.push(GateCX(), 0, 1) >>> circuit.resolve_losses(lossmodel=model) 2-qubit circuit with 2 instructions: ├── Lost @ q[1] └── Depolarizing(0.2) @ q[0]
- mimiqcircuits.lossmodel.sample_losses(circuit, rng=None)[source]¶
Resolve the random qubit-loss events in a circuit.
Each
mimiqcircuits.Loss(p)is drawn: with probabilitypit becomes a certain lossLoss(1.0), otherwise it is removed. Every other operation, including the deterministic loss bookkeeping (Reload,Check,MeasureCheck), is kept unchanged. The result is deterministic but still expressed with loss operations; uselower_losses()orresolve_losses()to turn it into a runnable, loss-free circuit.- Parameters:
circuit (Circuit) – Circuit to sample.
rng (optional) – Random number generator providing a
random()method.
- Returns:
A circuit whose random loss events have been resolved.
- Return type:
mc.Circuit
- mimiqcircuits.lossmodel.lower_losses(circuit, rng=None, lossmodel=None)[source]¶
Lower a circuit with loss operations into one using only primitives.
This is the deterministic half of loss resolution. It expects the random
Loss(p)events to be resolved already (seesample_losses()) and treats any remainingLossas a certain loss. It rewrites every loss operation intoReset/Measure/SetBit0/SetBit1plus passiveLost/Reloadedmarkers, and drops or replaces gates on lost qubits according tolossmodel. The returned circuit contains no loss operations.- Parameters:
- Returns:
A loss-free circuit that runs on any backend.
- Return type:
mc.Circuit
- mimiqcircuits.lossmodel.resolve_losses(circuit, rng=None, lossmodel=None)[source]¶
Fully resolve loss into a runnable, loss-free circuit.
The user-facing entry point: it runs
sample_losses()to draw the randomLoss(p)events, thenlower_losses()to rewrite the result into primitives. The returned circuit contains no loss operations.
- mimiqcircuits.lossmodel.sample_loss_scenario(circuit, loss_indices, *, p=1.0, rng=None, lossmodel=None)[source]¶
Build a deterministic “what-if” loss scenario from a circuit.
The selected
mimiqcircuits.Lossinstructions, counted in circuit order (1-based), are forced toLoss(p)while every otherLossis forced toLoss(0.0). The result is then resolved withresolve_losses(), so the returned circuit shows the effect of losing exactly those sites.- Parameters:
circuit (Circuit) – Circuit containing
Lossinstructions.loss_indices – A 1-based index or iterable of indices selecting which
Lossinstructions to force.p (optional) – Probability assigned to the selected sites (default 1.0).
rng (optional) – Random number generator.
lossmodel (optional) – A
LossModelfor gates touching lost qubits.
- Returns:
A loss-free circuit for the chosen scenario.
- Return type:
mc.Circuit
Examples
>>> from mimiqcircuits import * >>> circuit = Circuit() >>> _ = circuit.push(Loss(0.2), 0) >>> _ = circuit.push(GateCX(), 0, 1) >>> _ = circuit.push(Loss(0.4), 1) >>> sample_loss_scenario(circuit, 2) 2-qubit circuit with 2 instructions: ├── CX @ q[0], q[1] └── Lost @ q[1]