"""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,
) -> 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
[docs]
@typing_extensions.override
def command(
self, cmd: CommandOrNoise, rng: Generator | None = None, *, stacklevel: int = 1
) -> list[CommandOrNoise]:
"""Return the noise to apply to the command ``cmd``."""
match cmd.kind:
case CommandKind.N:
return [cmd, ApplyNoise(noise=DepolarisingNoise(self.prepare_error_prob), nodes=[cmd.node])]
case CommandKind.E:
return [
cmd,
ApplyNoise(noise=TwoQubitDepolarisingNoise(self.entanglement_error_prob), nodes=list(cmd.nodes)),
]
case CommandKind.M:
return [ApplyNoise(noise=DepolarisingNoise(self.measure_channel_prob), nodes=[cmd.node]), cmd]
case CommandKind.X:
return [
cmd,
ApplyNoise(noise=DepolarisingNoise(self.x_error_prob), nodes=[cmd.node], domain=cmd.domain),
]
case CommandKind.Z:
return [
cmd,
ApplyNoise(noise=DepolarisingNoise(self.z_error_prob), nodes=[cmd.node], domain=cmd.domain),
]
case CommandKind.C | CommandKind.T | CommandKind.ApplyNoise:
return [cmd]
case CommandKind.S:
raise ValueError("Unexpected signal!")
case _:
typing_extensions.assert_never(cmd.kind)
[docs]
@typing_extensions.override
def confuse_result(
self, cmd: BaseM, result: Outcome, rng: Generator | None = None, *, stacklevel: int = 1
) -> Outcome:
"""Assign wrong measurement result cmd = "M"."""
rng = ensure_rng(rng, stacklevel=stacklevel + 1)
if rng.uniform() < self.measure_error_prob:
return toggle_outcome(result)
return result