Source code for qmetro.param_channel.creators

from __future__ import annotations

from itertools import product
from typing import Any

import numpy as np

from ..qtools import ket_bra

from ..qtools import (
    depolarization_krauses, par_dephasing_krauses, per_dephasing_krauses,
    par_amp_damping_krauses, per_amp_damping_krauses
)

from . import ParamChannel




[docs] def par_dephasing(p: float, noise_first: bool = True, eps: float | None = None, rot_like: bool = False, c: float | None = None, **kwargs: Any) -> ParamChannel: """ Returnes paramtrised channel of a qubit channel where: - the signal is rotating Bloch sphere around the z-axis, - noise shrinks the xy-plane preserving the z-axis. See more details in :ref:`the documentation <par-deph>`. Parameters ---------- p : float Probability that the input state will remain unchanged. noise_first : bool, optional Whether noise is before signal, by default True. eps : float | None, optional Alternative way of determining the noise strength: p = cos(eps/2)**2, that when provided is used instead of p (p argument is ignored). rot_like : bool, optional If True then Kraus operators of noise are: K+ = exp(-1j/2 * eps * sigma_z) / sqrt(2), K- = exp(+1j/2 * eps * sigma_z) / sqrt(2), where p = cos(eps/2)**2, by default False. c : float | None, optional Correlation parameter from the interval [-1, 1]. When set it will put rot_like=True and create a channel with environment space such that K+ and K- will be correlated (see also :func:`cmarkov_channel` ). They are fully correlated for c=1, no correlated for c=0 and anticorrelated for c=-1. If None creates ParamChannel without environment space. By default None. **kwargs : dict, optional Arguments that will be passed to ParamChannel constructor. Returns ------- channel : ParamChannel Parametrised channel. """ rot_like = rot_like or c is not None if eps is None: krauses, dkrauses = par_dephasing_krauses( p, noise_first, rot_like=rot_like ) else: krauses, dkrauses = par_dephasing_krauses( noise_first=noise_first, eps=eps, rot_like=rot_like ) if c is None: return ParamChannel(krauses=krauses, dkrauses=dkrauses, **kwargs) trans = np.array([ [(1+c)/2, (1-c)/2], [(1-c)/2, (1+c)/2] ]) Kp, Km = krauses dKp, dKm = dkrauses sqrt2 = np.sqrt(2) rot_p = ParamChannel(krauses=[sqrt2*Kp], dkrauses=[sqrt2*dKp]) rot_m = ParamChannel(krauses=[sqrt2*Km], dkrauses=[sqrt2*dKm]) return cmarkov_channel([rot_p, rot_m], trans)
[docs] def per_dephasing(p: float, noise_first: bool = True, **kwargs ) -> ParamChannel: """ Returnes paramtrised channel of a qubit channel where: - the signal is rotating Bloch sphere around the z-axis, - noise shrinks the yz-plane preserving the x-axis. See more details in :ref:`the documentation <per-deph>`. Parameters ---------- p : float Probability that the input state will remain unchanged. noise_first : bool, optional Whether noise is before signal, by default True. **kwargs : dict, optional Arguments that will be passed to ParamChannel constructor. Returns ------- channel : ParamChannel Parametrised channel. """ krauses, dkrauses = per_dephasing_krauses(p, noise_first) return ParamChannel(krauses=krauses, dkrauses=dkrauses, **kwargs)
[docs] def per_amp_damping(p: float, noise_first: bool = True, **kwargs: Any ) -> ParamChannel: """ Returnes paramtrised channel of a qubit channel where: - the signal is rotating Bloch sphere around the z-axis, - noise models decay from state ``|+⟩`` to ``|-⟩``. See more details in :ref:`the documentation <per-amp>`. Parameters ---------- p : float Noise parametrization. For p = 1 there is no noise for p = 0 the noise is maximal. noise_first : bool, optional Whether noise is before signal, by default True. **kwargs : dict, optional Arguments that will be passed to ParamChannel constructor. Returns ------- channel : ParamChannel Parametrised channel. """ krauses, dkrauses = per_amp_damping_krauses(p, noise_first) return ParamChannel(krauses=krauses, dkrauses=dkrauses, **kwargs)
[docs] def par_amp_damping(p: float, noise_first: bool = True, **kwargs ) -> ParamChannel: """ Returnes parametrised channel of a qubit channel where: - the signal is rotating Bloch sphere around the z-axis, - noise models decay from state ``|1⟩`` to ``|0⟩``. See more details in :ref:`the documentation <par-amp>`. Parameters ---------- p : float Noise strength. For p = 1 there is no noise for p = 0 the noise is maximal. noise_first : bool, optional Whether noise is before signal, by default True. **kwargs : dict, optional Arguments that will be passed to ParamChannel constructor. Returns ------- channel : ParamChannel Parametrised channel. """ krauses, dkrauses = par_amp_damping_krauses(p, noise_first) return ParamChannel(krauses=krauses, dkrauses=dkrauses, **kwargs)
[docs] def depolarization(p: float, noise_first: bool = True, eta: float | None = None, **kwargs: Any) -> ParamChannel: """ Returnes paramtrised channel of a qubit channel where: - the signal is rotating Bloch sphere around the z-axis, - noise shrinks uniformly the whole Bloch ball. See more details in :ref:`the documentation <depolarization>`. Parameters ---------- p : float Probability that the input state will remain unchanged. noise_first : bool, optional Whether noise is before signal, by default True. eta : float | None, optional Alternative method of determining the noise strength that when provided is used instead of p (either p or eta argument has to be provided). In this parametrisation eta is the factor by which Bloch sphere gets shrunken. **kwargs : dict, optional Arguments that will be passed to ParamChannel constructor. Returns ------- channel : ParamChannel Parametrised channel. """ if eta is None: krauses, dkrauses = depolarization_krauses(p, noise_first) else: krauses, dkrauses = depolarization_krauses( noise_first=noise_first, eta=eta ) return ParamChannel(krauses=krauses, dkrauses=dkrauses, **kwargs)
[docs] def cmarkov_channel(channels: list[ParamChannel], trans_mat: np.ndarray ) -> ParamChannel: """ Creates a channel encoding classical Markovian correlations between a list of channels with transition probabilities given by the transition matrix. It is a channel G such that `G.link_env(G, ..., G)` is a comb representing a Markov chain of channels from the input list. Formally, for a list of channels F0, F1, ..., Fn-1 of the form L(H) -> L(K) and a transition matrix T, the function constructs a channel G: L(H x E) -> L(K x E), where E is a newly created environment space of dimension n. When the environment is in the i-th state G applies Fi and changes the state of the environment to j with probability T[i, j]. In this way, the state of the environment encodes which channel from the list will be applied next. Parameters ---------- channels : list[ParamChannel] List of channels F0, ..., Fn-1to be connected in a chain. All must have the same input and output space dimensions, be single (not comb) and have trivial environments. trans_mat : np.ndarray Transition matrix defining the transition probabilities, that is T[i, j] is the probability of transiting from channel i to channel j. Returns ------- channel : ParamChannel Channel G encoding the Markovian correlations. """ n = len(channels) d_in = channels[0].input_dim d_out = channels[0].output_dim for ch in channels: if ch.input_dim != d_in or ch.output_dim != d_out: raise ValueError( 'All channels must have the same input and output ' 'dimensions.' ) if not ch.trivial_env: raise ValueError( 'All channels must have trivial environment spaces.' ) if ch.is_comb: raise ValueError( 'All channels must be single channels, not combs.' ) # Krauses of channel G. As = [] dAs = [] id = np.identity(n) for i, j in product(range(n), repeat=2): prob = trans_mat[i, j] if prob == 0: continue ei = id[i] ej = id[j] for k, dk in zip(*channels[i].dkrauses()): A = np.sqrt(prob) * np.kron(ket_bra(ej, ei), k) dA = np.sqrt(prob) * np.kron(ket_bra(ej, ei), dk) As.append(A) dAs.append(dA) return ParamChannel(krauses=As, dkrauses=dAs, env_dim=n)
def _corr_dephasing(p: float, c: float, angle: float = 0, noise_first: bool = True, c_in: float | None = None) -> ParamChannel: """ Returns rotation-like parametrised dephasing channel with correlation c between subsequent U+ and U- Kraus operators. Creates a channel G encoding classical Markov correlation with transition matrix: T = [ (1+c)/2 , (1-c)/2 ] [ (1-c)/2 , (1+c)/2 ] The information about correlations is stored in an environment space such that G.link_env(G, ..., G) is a comb representing a Markov chain of dephasing channels with correlation c. Parameters ---------- p : float Probability that the input state will remain unchanged. No dephasing for p=1, maximal dephasing for p=0.5. c : float Correlation parameter. When first qubits of subsequent channels (environments) are connected, then dephasing angles are fully correlated for c=1, no correlated for c=0 and anticorrelated for c=-1. angle : float, optional Angle between signal and dephasing axis, by default 0. noise_first : bool, optional Whether noise is before signal, by default True. c_in : float | None, optional Correlation of noise map acting on input environment. If None, then c_in = sqrt(c), then noise is equally distributed between input and output environments. Returns ------- channel : ParamChannel Correlated dephasing channel. """ raise NotImplementedError() # TODO: Make it into general dephasing function. if angle or c_in is not None: raise NotImplementedError( 'Currently only angle=0 and c_in=None are supported.' ) T = np.array([ [1+c, 1-c], [1-c, 1+c] ]) / 2 deph = par_dephasing(p, rot_like=True, noise_first=noise_first) (Up, Um), (dUp, dUm) = deph.dkrauses() sqrt2 = np.sqrt(2) rot_p = ParamChannel(krauses=[sqrt2*Up], dkrauses=[sqrt2*dUp]) rot_m = ParamChannel(krauses=[sqrt2*Um], dkrauses=[sqrt2*dUm]) return cmarkov_channel([rot_p, rot_m], T)