Source code for mimiqcircuits.operations.noisechannel.standards.ampdamping
#
# Copyright © 2022-2023 University of Strasbourg. All Rights Reserved.
# Copyright © 2032-2024 QPerfect. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from mimiqcircuits.operations.krauschannel import krauschannel
import mimiqcircuits as mc
import numpy as np
import symengine as se
import sympy as sp
[docs]
class AmplitudeDamping(krauschannel):
r"""One-qubit amplitude damping noise channel.
The amplitude damping channel is defined by the Kraus operators
See Also:
:func:`GeneralizedAmplitudeDamping`
**Kraus Matrices representation:**
.. math::
\operatorname E_1 =
\begin{pmatrix}
1 & 0 \\
0 & \sqrt{1-\gamma}
\end{pmatrix}
,\quad
\operatorname E_2 =
\begin{pmatrix}
0 & \sqrt{\gamma} \\
0 & 0
\end{pmatrix},
Parameters:
gamma: ``gamma in [0,1]``.
Examples:
>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(AmplitudeDamping(0.1), 0)
1-qubit circuit with 1 instructions:
└── AmplitudeDamping(0.1) @ q[0]
<BLANKLINE>
"""
_num_qubits = 1
_name = "AmplitudeDamping"
_parnames = ()
def __init__(self, gamma):
if not isinstance(gamma, (sp.Basic, se.Basic)) and (gamma < 0 or gamma > 1):
raise ValueError("Value of gamma must be between 0 and 1.")
self.gamma = gamma
self._parnames = ("gamma",)
[docs]
def evaluate(self, d={}):
evaluated_gamma = float(self.gamma.subs(d).evalf()) if hasattr(self.gamma, 'subs') else self.gamma
if not (0 <= evaluated_gamma <= 1):
raise ValueError("Probability after evaluation must be between 0 and 1.")
return AmplitudeDamping(evaluated_gamma)
[docs]
def krausoperators(self):
E1 = mc.DiagonalOp(1, np.sqrt(1 - self.gamma))
E2 = mc.SigmaMinus(np.sqrt(self.gamma))
return [E1, E2]
[docs]
def krausmatrices(self):
return [operator.matrix() for operator in self.krausoperators()]
[docs]
def iswrapper(self):
pass
@property
def opname(self):
return self._name
@property
def parnames(self):
return self._parnames
def __str__(self):
return f"{self._name}({self.gamma})"
[docs]
class GeneralizedAmplitudeDamping(krauschannel):
r"""One-qubit generalized amplitude damping noise channel.
The amplitude generalized damping channel is defined by the Kraus operators.
See Also:
:func:`AmplitudeDamping`
**Kraus Matrices representation:**
.. math::
\operatorname E_1 =
\sqrt{p}
\begin{pmatrix}
1 & 0 \\
0 & \sqrt{1-\gamma}
\end{pmatrix}
,\quad
\operatorname E_2 =
\sqrt{p}
\begin{pmatrix}
0 & \sqrt{\gamma} \\
0 & 0
\end{pmatrix}
,\quad
\operatorname E_3 =
\sqrt{1-p}
\begin{pmatrix}
\sqrt{1-\gamma} & 0 \\
0 & 1
\end{pmatrix}
,\quad
\operatorname E_4 =
\sqrt{1-p}
\begin{pmatrix}
0 & 0 \\
\sqrt{\gamma} & 0
\end{pmatrix},
Parameters:
gamma: ``gamma in [0,1]``.
Examples:
>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(GeneralizedAmplitudeDamping(0.1,1), 0)
1-qubit circuit with 1 instructions:
└── GeneralizedAmplitudeDamping((0.1, 1)) @ q[0]
<BLANKLINE>
"""
_num_qubits = 1
_name = "GeneralizedAmplitudeDamping"
_parnames = ()
def __init__(self, p, gamma):
if not isinstance(p, (sp.Basic, se.Basic)) and (p < 0 or p > 1):
raise ValueError("Value of p must be between 0 and 1.")
if not isinstance(gamma, (sp.Basic, se.Basic)) and (gamma < 0 or gamma > 1):
raise ValueError("Value of gamma must be between 0 and 1.")
self.p = p
self.gamma = gamma
self._parnames = ("p", "gamma")
[docs]
def evaluate(self, d={}):
# Helper function to evaluate a parameter
def evaluate_param(param):
if hasattr(param, 'subs'):
substituted_value = param.subs(d)
return float(substituted_value.evalf()) if substituted_value.is_number else substituted_value
return param
evaluated_p = evaluate_param(self.p)
evaluated_gamma = evaluate_param(self.gamma)
if isinstance(evaluated_p, (float, int)) and not (0 <= evaluated_p <= 1):
raise ValueError("Probability p after evaluation must be between 0 and 1.")
if isinstance(evaluated_gamma, (float, int)) and not (0 <= evaluated_gamma <= 1):
raise ValueError("Probability gamma after evaluation must be between 0 and 1.")
# Return a new instance of GeneralizedAmplitudeDamping with the evaluated values
return GeneralizedAmplitudeDamping(evaluated_p, evaluated_gamma)
[docs]
def krausoperators(self):
p = self.p
gamma = self.gamma
E1 = mc.DiagonalOp(np.sqrt(p), np.sqrt(p) * np.sqrt(1 - gamma))
E2 = mc.DiagonalOp(np.sqrt(1 - p) * np.sqrt(1 - gamma), np.sqrt(1 - p))
E3 = mc.SigmaMinus(np.sqrt(p) * np.sqrt(gamma))
E4 = mc.SigmaPlus(np.sqrt(1 - p) * np.sqrt(gamma))
return [E1, E2, E3, E4]
[docs]
def krausmatrices(self):
return [operator.matrix() for operator in self.krausoperators()]
[docs]
def iswrapper(self):
pass
@property
def num_qubits(self):
return self._num_qubits
@property
def parnames(self):
return self._parnames
def __str__(self):
return f"{self._name}({self.p,self.gamma})"
__all__ = ["GeneralizedAmplitudeDamping", "AmplitudeDamping"]