Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added TritFlip #5784

Merged
merged 24 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
74a4e39
Added TritFlip
Gabriel-Bottrill Jun 1, 2024
bae24ea
Added changelog entry
Gabriel-Bottrill Jun 1, 2024
0e4b268
Fixed tests and added output of Kraus matrices
Gabriel-Bottrill Jun 1, 2024
a863acf
Reformatted init
Gabriel-Bottrill Jun 1, 2024
a638d05
Fixed the expected_jac_fn for computing Jacobians
Gabriel-Bottrill Jun 1, 2024
082b455
Fixed documentation and added example link to qutrit misclassification
Gabriel-Bottrill Jun 1, 2024
4b8bb7f
Fixed tests, interface tests for multiple parameters and arbitrary in…
Gabriel-Bottrill Jun 1, 2024
1933b76
Fixed JAX test
Gabriel-Bottrill Jun 1, 2024
d4ed816
Merge branch 'master' into qutrit_channel_tritflip
glassnotes Jun 3, 2024
899e425
Merge branch 'master' into qutrit_channel_tritflip
Gabriel-Bottrill Jun 3, 2024
493993a
Changed amplitude damping gamma_x to gamma_xy for clarity
Gabriel-Bottrill Jun 3, 2024
27a198e
Added suggested doc changes.
Gabriel-Bottrill Jun 3, 2024
54cc695
Apply suggestions from code review
Gabriel-Bottrill Jun 4, 2024
e5ec16a
Fixed pylint error
Gabriel-Bottrill Jun 4, 2024
e160944
Removed changes to gamma
Gabriel-Bottrill Jun 4, 2024
7b197ba
Merge branch 'master' into qutrit_channel_tritflip
Gabriel-Bottrill Jun 4, 2024
2ee59c0
Merge branch 'master' into qutrit_channel_tritflip
glassnotes Jun 5, 2024
c6dd33d
Merge branch 'master' into qutrit_channel_tritflip
Gabriel-Bottrill Jun 5, 2024
3c3e5f6
Merge branch 'master' into qutrit_channel_tritflip
Gabriel-Bottrill Jun 5, 2024
77c83ec
Merge branch 'master' into qutrit_channel_tritflip
Gabriel-Bottrill Jun 6, 2024
0e390f6
Merge branch 'master' into qutrit_channel_tritflip
glassnotes Jun 6, 2024
6b3ee25
Merge branch 'master' into qutrit_channel_tritflip
Gabriel-Bottrill Jun 6, 2024
f73f345
Merge branch 'master' into qutrit_channel_tritflip
Gabriel-Bottrill Jun 6, 2024
3c3894f
Merge branch 'master' into qutrit_channel_tritflip
Gabriel-Bottrill Jun 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/introduction/operations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,7 @@ Qutrit noisy channels

~pennylane.QutritDepolarizingChannel
~pennylane.QutritAmplitudeDamping
~pennylane.TritFlip

:html:`</div>`

Expand Down
7 changes: 6 additions & 1 deletion doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,17 @@
* Implemented kwargs (`check_interface`, `check_trainability`, `rtol` and `atol`) support in `qml.equal` for the operators `Pow`, `Adjoint`, `Exp`, and `SProd`.
[(#5668)](https://github.com/PennyLaneAI/pennylane/issues/5668)

* ``qml.QutritDepolarizingChannel`` has been added, allowing for depolarizing noise to be simulated on the `default.qutrit.mixed` device.
* `qml.QutritDepolarizingChannel` has been added, allowing for depolarizing noise to be simulated on the `default.qutrit.mixed` device.
[(#5502)](https://github.com/PennyLaneAI/pennylane/pull/5502)

* `qml.QutritAmplitudeDamping` channel has been added, allowing for noise processes modelled by amplitude damping to be simulated on the `default.qutrit.mixed` device.
[(#5503)](https://github.com/PennyLaneAI/pennylane/pull/5503)
[(#5757)](https://github.com/PennyLaneAI/pennylane/pull/5757)

* `qml.TritFlip` has been added, allowing for trit flip errors, such as misclassification,
to be simulated on the `default.qutrit.mixed` device.
[(#5784)](https://github.com/PennyLaneAI/pennylane/pull/5784)


<h3>Breaking changes 💔</h3>

Expand Down
5 changes: 1 addition & 4 deletions pennylane/ops/qutrit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@
"THermitian",
"GellMann",
}
__channels__ = {
"QutritDepolarizingChannel",
"QutritAmplitudeDamping",
}
__channels__ = {"QutritDepolarizingChannel", "QutritAmplitudeDamping", "TritFlip"}

__all__ = list(__ops__ | __obs__ | __channels__)
179 changes: 148 additions & 31 deletions pennylane/ops/qutrit/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ class QutritDepolarizingChannel(Channel):

Args:
p (float): Each qutrit Pauli operator is applied with probability :math:`\frac{p}{8}`
wires (Sequence[int] or int): the wire the channel acts on
wires (Sequence[int] or int): The wire the channel acts on
id (str or None): String representing the operation (optional)
"""

Expand All @@ -142,7 +142,7 @@ def compute_kraus_matrices(p): # pylint:disable=arguments-differ
r"""Kraus matrices representing the qutrit depolarizing channel.

Args:
p (float): each qutrit Pauli gate is applied with probability :math:`\frac{p}{8}`
p (float): Each qutrit Pauli gate is applied with probability :math:`\frac{p}{8}`

Returns:
list (array): list of Kraus matrices
Expand Down Expand Up @@ -234,39 +234,39 @@ class QutritAmplitudeDamping(Channel):
.. math::
K_0 = \begin{bmatrix}
1 & 0 & 0\\
0 & \sqrt{1-\gamma_1} & 0 \\
0 & 0 & \sqrt{1-(\gamma_2+\gamma_3)}
0 & \sqrt{1-\gamma_{01}} & 0 \\
Gabriel-Bottrill marked this conversation as resolved.
Show resolved Hide resolved
0 & 0 & \sqrt{1-(\gamma_{02}+\gamma_{12})}
\end{bmatrix}

.. math::
K_1 = \begin{bmatrix}
0 & \sqrt{\gamma_1} & 0 \\
0 & \sqrt{\gamma_{01}} & 0 \\
0 & 0 & 0 \\
0 & 0 & 0
\end{bmatrix}, \quad
K_2 = \begin{bmatrix}
0 & 0 & \sqrt{\gamma_2} \\
0 & 0 & \sqrt{\gamma_{02}} \\
0 & 0 & 0 \\
0 & 0 & 0
\end{bmatrix}, \quad
K_3 = \begin{bmatrix}
0 & 0 & 0 \\
0 & 0 & \sqrt{\gamma_3} \\
0 & 0 & \sqrt{\gamma_{12}} \\
0 & 0 & 0
\end{bmatrix}

where :math:`\gamma_1, \gamma_2, \gamma_3 \in [0, 1]` are the amplitude damping
where :math:`\gamma_{01}, \gamma_{02}, \gamma_{12} \in [0, 1]` are the amplitude damping
probabilities for subspaces (0,1), (0,2), and (1,2) respectively.

.. note::

When :math:`\gamma_3=0` then Kraus operators :math:`\{K_0, K_1, K_2\}` are adapted from
When :math:`\gamma_{12}=0` then Kraus operators :math:`\{K_0, K_1, K_2\}` are adapted from
[`1 <https://doi.org/10.48550/arXiv.1905.10481>`_] (Eq. 8).

The Kraus operator :math:`K_3` represents the :math:`|2 \rangle \rightarrow |1 \rangle` transition which is more
likely on some devices [`2 <https://arxiv.org/abs/2003.03307>`_] (Sec II.A).

To maintain normalization :math:`\gamma_2 + \gamma_3 \leq 1`.
To maintain normalization :math:`\gamma_{02} + \gamma_{12} \leq 1`.


**Details:**
Expand All @@ -275,36 +275,36 @@ class QutritAmplitudeDamping(Channel):
* Number of parameters: 3

Args:
gamma_1 (float): :math:`|1 \rangle \rightarrow |0 \rangle` amplitude damping probability.
gamma_2 (float): :math:`|2 \rangle \rightarrow |0 \rangle` amplitude damping probability.
gamma_3 (float): :math:`|2 \rangle \rightarrow |1 \rangle` amplitude damping probability.
wires (Sequence[int] or int): the wire the channel acts on
gamma_01 (float): :math:`|1 \rangle \rightarrow |0 \rangle` amplitude damping probability.
gamma_02 (float): :math:`|2 \rangle \rightarrow |0 \rangle` amplitude damping probability.
gamma_12 (float): :math:`|2 \rangle \rightarrow |1 \rangle` amplitude damping probability.
wires (Sequence[int] or int): The wire the channel acts on
id (str or None): String representing the operation (optional)
"""

num_params = 3
num_wires = 1
grad_method = "F"

def __init__(self, gamma_1, gamma_2, gamma_3, wires, id=None):
def __init__(self, gamma_01, gamma_02, gamma_12, wires, id=None):
# Verify input
for gamma in (gamma_1, gamma_2, gamma_3):
for gamma in (gamma_01, gamma_02, gamma_12):
if not math.is_abstract(gamma):
if not 0.0 <= gamma <= 1.0:
raise ValueError("Each probability must be in the interval [0,1]")
if not (math.is_abstract(gamma_2) or math.is_abstract(gamma_3)):
if not 0.0 <= gamma_2 + gamma_3 <= 1.0:
raise ValueError(r"\gamma_2+\gamma_3 must be in the interval [0,1]")
super().__init__(gamma_1, gamma_2, gamma_3, wires=wires, id=id)
if not (math.is_abstract(gamma_02) or math.is_abstract(gamma_12)):
if not 0.0 <= gamma_02 + gamma_12 <= 1.0:
raise ValueError(r"\gamma_{02}+\gamma_{12} must be in the interval [0,1]")
super().__init__(gamma_01, gamma_02, gamma_12, wires=wires, id=id)

@staticmethod
def compute_kraus_matrices(gamma_1, gamma_2, gamma_3): # pylint:disable=arguments-differ
def compute_kraus_matrices(gamma_01, gamma_02, gamma_12): # pylint:disable=arguments-differ
r"""Kraus matrices representing the ``QutritAmplitudeDamping`` channel.

Args:
gamma_1 (float): :math:`|1\rangle \rightarrow |0\rangle` amplitude damping probability.
gamma_2 (float): :math:`|2\rangle \rightarrow |0\rangle` amplitude damping probability.
gamma_3 (float): :math:`|2\rangle \rightarrow |1\rangle` amplitude damping probability.
gamma_01 (float): :math:`|1\rangle \rightarrow |0\rangle` amplitude damping probability.
gamma_02 (float): :math:`|2\rangle \rightarrow |0\rangle` amplitude damping probability.
gamma_12 (float): :math:`|2\rangle \rightarrow |1\rangle` amplitude damping probability.

Returns:
list(array): list of Kraus matrices
Expand All @@ -328,15 +328,132 @@ def compute_kraus_matrices(gamma_1, gamma_2, gamma_3): # pylint:disable=argumen
]
"""
K0 = math.diag(
[1, math.sqrt(1 - gamma_1 + math.eps), math.sqrt(1 - gamma_2 - gamma_3 + math.eps)]
[1, math.sqrt(1 - gamma_01 + math.eps), math.sqrt(1 - gamma_02 - gamma_12 + math.eps)]
)
K1 = math.sqrt(gamma_1 + math.eps) * math.convert_like(
math.cast_like(math.array([[0, 1, 0], [0, 0, 0], [0, 0, 0]]), gamma_1), gamma_1
K1 = math.sqrt(gamma_01 + math.eps) * math.convert_like(
math.cast_like(math.array([[0, 1, 0], [0, 0, 0], [0, 0, 0]]), gamma_01), gamma_01
)
K2 = math.sqrt(gamma_2 + math.eps) * math.convert_like(
math.cast_like(math.array([[0, 0, 1], [0, 0, 0], [0, 0, 0]]), gamma_2), gamma_2
K2 = math.sqrt(gamma_02 + math.eps) * math.convert_like(
math.cast_like(math.array([[0, 0, 1], [0, 0, 0], [0, 0, 0]]), gamma_02), gamma_02
)
K3 = math.sqrt(gamma_3 + math.eps) * math.convert_like(
math.cast_like(math.array([[0, 0, 0], [0, 0, 1], [0, 0, 0]]), gamma_3), gamma_3
K3 = math.sqrt(gamma_12 + math.eps) * math.convert_like(
math.cast_like(math.array([[0, 0, 0], [0, 0, 1], [0, 0, 0]]), gamma_12), gamma_12
)
return [K0, K1, K2, K3]


class TritFlip(Channel):
Gabriel-Bottrill marked this conversation as resolved.
Show resolved Hide resolved
r"""
Single-qutrit trit flip error channel. Applying "bit flips" on each qutrit subspace.
Gabriel-Bottrill marked this conversation as resolved.
Show resolved Hide resolved

This channel is modelled by the following Kraus matrices:

.. math::
K_0 = \sqrt{1-(p_{01} + p_{02} + p_{12})} \begin{bmatrix}
1 & 0 & 0 \\
0 & 1 & 0 \\
0 & 0 & 1
\end{bmatrix}

.. math::
K_1 = \sqrt{p_{01}}\begin{bmatrix}
0 & 1 & 0 \\
1 & 0 & 0 \\
0 & 0 & 1
\end{bmatrix}, \quad
K_2 = \sqrt{p_{02}}\begin{bmatrix}
0 & 0 & 1 \\
0 & 1 & 0 \\
1 & 0 & 0
\end{bmatrix}, \quad
K_3 = \sqrt{p_{12}}\begin{bmatrix}
1 & 0 & 0 \\
0 & 0 & 1 \\
0 & 1 & 0
\end{bmatrix}

where :math:`p_{01}, p_{02}, p_{12} \in [0, 1]` is the probability of two qutrit state-coefficients
flipping for subspaces (0,1), (0,2), and (1,2) respectively.
Gabriel-Bottrill marked this conversation as resolved.
Show resolved Hide resolved

.. note::
The Kraus operators :math:`\{K_0, K_1, K_2, K_3\}` are adapted from the
`BitFlip <https://docs.pennylane.ai/en/stable/code/api/pennylane.BitFlip.html>`_ channel's Kraus operators.

This channel is primarily meant to simulate the misclassification inherent to measurements on some platforms.
An example of a measurement with misclassification can be seen in [`1 <https://arxiv.org/abs/2309.11303>`_] (Fig 1a).

To maintain normalization :math:`p_{01} + p_{02} + p_{12} \leq 1`.


**Details:**

* Number of wires: 1
* Number of parameters: 3

Args:
p_01 (float): The probability that a :math:`|0 \rangle \leftrightarrow |1 \rangle` trit flip error occurs.
p_02 (float): The probability that a :math:`|0 \rangle \leftrightarrow |2 \rangle` trit flip error occurs.
p_12 (float): The probability that a :math:`|1 \rangle \leftrightarrow |2 \rangle` trit flip error occurs.
wires (Sequence[int] or int): The wire the channel acts on
id (str or None): String representing the operation (optional)
"""

num_params = 3
num_wires = 1
grad_method = "F"
Gabriel-Bottrill marked this conversation as resolved.
Show resolved Hide resolved

def __init__(self, p_01, p_02, p_12, wires, id=None):
Gabriel-Bottrill marked this conversation as resolved.
Show resolved Hide resolved
# Verify input
Gabriel-Bottrill marked this conversation as resolved.
Show resolved Hide resolved
ps = (p_01, p_02, p_12)
for p in ps:
if not math.is_abstract(p) and not 0.0 <= p <= 1.0:
raise ValueError("All probabilities must be in the interval [0,1]")
if not any(math.is_abstract(p) for p in ps):
if not 0.0 <= sum(ps) <= 1.0:
raise ValueError("The sum of probabilities must be in the interval [0,1]")

super().__init__(p_01, p_02, p_12, wires=wires, id=id)

@staticmethod
def compute_kraus_matrices(p_01, p_02, p_12): # pylint:disable=arguments-differ
r"""Kraus matrices representing the TritFlip channel.

Args:
p_01 (float): The probability that a :math:`|0 \rangle \leftrightarrow |1 \rangle` trit flip error occurs.
p_02 (float): The probability that a :math:`|0 \rangle \leftrightarrow |2 \rangle` trit flip error occurs.
p_12 (float): The probability that a :math:`|1 \rangle \leftrightarrow |2 \rangle` trit flip error occurs.

Returns:
list (array): list of Kraus matrices

**Example**

>>> qml.TritFlip.compute_kraus_matrices(0.05, 0.01, 0.10)
[
array([ [0.91651514, 0. , 0. ],
[0. , 0.91651514, 0. ],
[0. , 0. , 0.91651514]]),
array([ [0. , 0.2236068 , 0. ],
[0.2236068 , 0. , 0. ],
[0. , 0. , 0.2236068]]),
array([ [0. , 0. , 0.1 ],
[0. , 0.1 , 0. ],
[0.1 , 0. , 0. ]]),
array([ [0.31622777, 0. , 0. ],
[0. , 0. , 0.31622777],
[0. , 0.31622777, 0. ]])
]
"""
K0 = math.sqrt(1 - (p_01 + p_02 + p_12) + math.eps) * math.convert_like(
math.cast_like(np.eye(3), p_01), p_01
)
K1 = math.sqrt(p_01 + math.eps) * math.convert_like(
math.cast_like(math.array([[0, 1, 0], [1, 0, 0], [0, 0, 1]]), p_01), p_01
)
K2 = math.sqrt(p_02 + math.eps) * math.convert_like(
math.cast_like(math.array([[0, 0, 1], [0, 1, 0], [1, 0, 0]]), p_02), p_02
)
K3 = math.sqrt(p_12 + math.eps) * math.convert_like(
math.cast_like(math.array([[1, 0, 0], [0, 0, 1], [0, 1, 0]]), p_12), p_12
)
return [K0, K1, K2, K3]
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ def test_measurement_is_swapped_out(self, mp_fn, mp_cls, shots):
(qml.TRX(1.1, 0), True),
(qml.QutritDepolarizingChannel(0.4, 0), True),
(qml.QutritAmplitudeDamping(0.1, 0.2, 0.12, 0), True),
Gabriel-Bottrill marked this conversation as resolved.
Show resolved Hide resolved
(qml.TritFlip(0.4, 0.1, 0.02, 0), True),
],
)
def test_accepted_operator(self, op, expected):
Expand Down
Loading
Loading