Source code for graphix.noise_models.depolarising
"""Depolarising noise model."""
from __future__ import annotations
from typing import TYPE_CHECKING
import typing_extensions
from graphix.channels import KrausChannel, depolarising_channel, two_qubit_depolarising_channel
from graphix.command import BaseM, CommandKind
from graphix.measurements import toggle_outcome
from graphix.noise_models.noise_model import ApplyNoise, CommandOrNoise, Noise, NoiseModel
from graphix.rng import ensure_rng
from graphix.utils import Probability
if TYPE_CHECKING:
from collections.abc import Iterable
from numpy.random import Generator
from graphix.measurements import Outcome
[docs]
class DepolarisingNoise(Noise):
"""One-qubit depolarising noise with probabibity ``prob``."""
prob = Probability()
[docs]
def __init__(self, prob: float) -> None:
"""Initialize one-qubit depolarizing noise.
Parameters
----------
prob : float
Probability parameter of the noise, between 0 and 1.
"""
self.prob = prob
@property
@typing_extensions.override
def nqubits(self) -> int:
"""Return the number of qubits targetted by the noise element."""
return 1
[docs]
@typing_extensions.override
def to_kraus_channel(self) -> KrausChannel:
"""Return the Kraus channel describing the noise element."""
return depolarising_channel(self.prob)
[docs]
class TwoQubitDepolarisingNoise(Noise):
"""Two-qubits depolarising noise with probabibity ``prob``."""
prob = Probability()
[docs]
def __init__(self, prob: float) -> None:
"""Initialize two-qubit depolarizing noise.
Parameters
----------
prob : float
Probability parameter of the noise, between 0 and 1.
"""
self.prob = prob
@property
@typing_extensions.override
def nqubits(self) -> int:
"""Return the number of qubits targetted by the noise element."""
return 2
[docs]
@typing_extensions.override
def to_kraus_channel(self) -> KrausChannel:
"""Return the Kraus channel describing the noise element."""
return two_qubit_depolarising_channel(self.prob)
[docs]
class DepolarisingNoiseModel(NoiseModel):
"""Depolarising noise model.
:param NoiseModel: Parent abstract class class:`NoiseModel`
:type NoiseModel: class
"""
[docs]
def __init__(
self,
prepare_error_prob: float = 0.0,
x_error_prob: float = 0.0,
z_error_prob: float = 0.0,
entanglement_error_prob: float = 0.0,
measure_channel_prob: float = 0.0,
measure_error_prob: float = 0.0,
rng: Generator | None = None,
) -> None:
self.prepare_error_prob = prepare_error_prob
self.x_error_prob = x_error_prob
self.z_error_prob = z_error_prob
self.entanglement_error_prob = entanglement_error_prob
self.measure_error_prob = measure_error_prob
self.measure_channel_prob = measure_channel_prob
self.rng = ensure_rng(rng)
[docs]
@typing_extensions.override
def command(self, cmd: CommandOrNoise, rng: Generator | None = None) -> list[CommandOrNoise]:
"""Return the noise to apply to the command ``cmd``."""
if cmd.kind == CommandKind.N:
return [cmd, ApplyNoise(noise=DepolarisingNoise(self.prepare_error_prob), nodes=[cmd.node])]
if cmd.kind == CommandKind.E:
return [
cmd,
ApplyNoise(noise=TwoQubitDepolarisingNoise(self.entanglement_error_prob), nodes=list(cmd.nodes)),
]
if cmd.kind == CommandKind.M:
return [ApplyNoise(noise=DepolarisingNoise(self.measure_channel_prob), nodes=[cmd.node]), cmd]
if cmd.kind == CommandKind.X:
return [cmd, ApplyNoise(noise=DepolarisingNoise(self.x_error_prob), nodes=[cmd.node])]
if cmd.kind == CommandKind.Z:
return [cmd, ApplyNoise(noise=DepolarisingNoise(self.z_error_prob), nodes=[cmd.node])]
# Use of `==` here for mypy
if cmd.kind == CommandKind.C or cmd.kind == CommandKind.T or cmd.kind == CommandKind.ApplyNoise: # noqa: PLR1714
return [cmd]
if cmd.kind == CommandKind.S:
raise ValueError("Unexpected signal!")
typing_extensions.assert_never(cmd.kind)
[docs]
@typing_extensions.override
def confuse_result(self, cmd: BaseM, result: Outcome, rng: Generator | None = None) -> Outcome:
"""Assign wrong measurement result cmd = "M"."""
if self.rng.uniform() < self.measure_error_prob:
return toggle_outcome(result)
return result