Skip to content

Commit

Permalink
Merge pull request #161 from georgios-ts/use-channel-dim
Browse files Browse the repository at this point in the history
Use `channel_dim`.
  • Loading branch information
vprusso committed Jun 2, 2023
2 parents 9ce0bb7 + 327dbef commit 716cd8f
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 30 deletions.
21 changes: 17 additions & 4 deletions tests/test_channel_ops/test_dual_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from toqito.channel_ops import dual_channel
from toqito.channels import choi
from toqito.perms import swap_operator


def test_dual_channel_kraus1():
Expand Down Expand Up @@ -73,10 +74,22 @@ def test_dual_channel_choi_dims():


def test_dual_channel_nonsquare_matrix():
"""If the channel is represented as a Choi matrix, it must be square."""
with np.testing.assert_raises(ValueError):
j = np.array([[1, 2, 3, 4], [4, 3, 2, 1]])
dual_channel(j)
"""Dual of a channel that transposes 3x2 matrices."""
choi = swap_operator([2, 3])
choi_dual = dual_channel(choi, dims=[[3, 2], [2, 3]])
expected_choi_dual = np.array(
[
[1, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 1, 0],
[0, 1, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 1],
]
)

bool_mat = np.isclose(choi_dual, expected_choi_dual)
np.testing.assert_equal(np.all(bool_mat), True)


def test_dual_channel_not_matrix():
Expand Down
48 changes: 48 additions & 0 deletions tests/test_channel_ops/test_kraus_to_choi.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,53 @@ def test_kraus_to_choi_depolarizing_channel():
np.testing.assert_equal(np.all(bool_mat), True)


def test_kraus_to_choi_isometry():
"""Kraus operators for an isometry."""
v_mat = np.array([[1, 0, 0], [0, 1, 0]])

choi_res = kraus_to_choi([v_mat])
expected_choi_res = np.array(
[
[1, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[1, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
]
)

bool_mat = np.isclose(choi_res, expected_choi_res)
np.testing.assert_equal(np.all(bool_mat), True)


def test_kraus_to_choi_non_square():
"""Kraus operators for non square inputs and outputs."""
kraus_1 = np.array([[1, 0, 0], [0, 1, 0]])

kraus_2 = np.array(
[
[0, 0, 1, 0],
[0, 1, 0, 0],
[1, 0, 0, 1],
]
)

choi_res = kraus_to_choi([[kraus_1, kraus_2]])
expected_choi_res = np.array(
[
[0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
]
)

bool_mat = np.isclose(choi_res, expected_choi_res)
np.testing.assert_equal(np.all(bool_mat), True)


if __name__ == "__main__":
np.testing.run_module_suite()
22 changes: 22 additions & 0 deletions tests/test_channel_props/test_is_unital.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Tests for is_unital."""
import numpy as np

from toqito.channel_ops import kraus_to_choi
from toqito.channel_props import is_unital
from toqito.channels import depolarizing
from toqito.perms import swap_operator
Expand Down Expand Up @@ -36,5 +37,26 @@ def test_is_unital_depolarizing_choi_true():
np.testing.assert_equal(is_unital(depolarizing(4)), True)


def test_is_unital_isometry_true():
"""Verify isometry channel is unital."""
v_mat = np.array([[1, 0, 0], [0, 1, 0]])
np.testing.assert_equal(is_unital([v_mat], dim=[3, 2]), True)


def test_is_unital_choi_isometry_true():
"""Verify isometry channel with Choi matrix is unital."""
v_mat = np.array([[1, 0, 0], [0, 1, 0]])
choi = kraus_to_choi([v_mat])
np.testing.assert_equal(is_unital(choi, dim=[3, 2]), True)


def test_is_unital_isometry_true_unspecified_dim():
"""Verify isometry channel with Choi matrix raises if dim is unspecified."""
v_mat = np.array([[1, 0, 0], [0, 1, 0]])
choi = kraus_to_choi([v_mat])
with np.testing.assert_raises(ValueError):
is_unital(choi)


if __name__ == "__main__":
np.testing.run_module_suite()
22 changes: 8 additions & 14 deletions toqito/channel_ops/dual_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from __future__ import annotations
import numpy as np

from toqito.helper import channel_dim
from toqito.matrix_props import is_square
from toqito.perms import swap

Expand All @@ -13,9 +14,11 @@ def dual_channel(
Compute the dual of a map (quantum channel) [WatDChan18]_.
The map can be represented as a Choi matrix, with optional specification of input
and output dimensions. In this case the Choi matrix of the dual channel is
returned, obtained by swapping input and output (see :func:`toqito.perms.swap`),
and complex conjugating all elements.
and output dimensions. If the input channel maps :math:`M_{r,c}` to :math:`M_{x,y}`
then :code:`dim` should be the list :code:`[[r,x], [c,y]]`. If it maps :math:`M_m`
to :math:`M_n`, then :code:`dim` can simply be the vector :code:`[m,n]`. In this
case the Choi matrix of the dual channel is returned, obtained by swapping input and
output (see :func:`toqito.perms.swap`), and complex conjugating all elements.
The map can also be represented as a list of Kraus operators.
A list of lists, each containing two elements, corresponds to the families
Expand Down Expand Up @@ -56,17 +59,8 @@ def dual_channel(
# If phi_op is a `ndarray`, assume it is a Choi matrix.
if isinstance(phi_op, np.ndarray):
if len(phi_op.shape) == 2:
if not is_square(phi_op):
raise ValueError("Invalid: `phi_op` is not a valid Choi matrix (not square).")
if dims is None:
sqr = np.sqrt(phi_op.shape[0])
if sqr.is_integer():
dims = [int(round(sqr))] * 2
else:
raise ValueError(
"The dimensions `dims` of the input and output should be specified."
)
return swap(phi_op.conj(), dim=dims)
d_in, d_out, _ = channel_dim(phi_op, dim=dims, compute_env_dim=False)
return swap(phi_op.conj(), dim=[[d_in[0], d_out[0]], [d_in[1], d_out[1]]])
raise ValueError(
"Invalid: The variable `phi_op` must either be a list of "
"Kraus operators or as a Choi matrix."
Expand Down
5 changes: 3 additions & 2 deletions toqito/channel_ops/kraus_to_choi.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from toqito.states import max_entangled
from toqito.channel_ops import partial_channel
from toqito.helper import channel_dim


def kraus_to_choi(kraus_ops: list[list[np.ndarray]], sys: int = 2) -> np.ndarray:
Expand Down Expand Up @@ -61,8 +62,8 @@ def kraus_to_choi(kraus_ops: list[list[np.ndarray]], sys: int = 2) -> np.ndarray
:param sys: The dimension of the system (default is 2).
:return: The corresponding Choi matrix of the provided Kraus operators.
"""
dim_op_1 = kraus_ops[0][0].shape[0]
dim_op_2 = kraus_ops[0][0].shape[1]
dim_in, _, _ = channel_dim(kraus_ops)
dim_op_1, dim_op_2 = dim_in

choi_mat = partial_channel(
max_entangled(dim_op_1, False, False) * max_entangled(dim_op_2, False, False).conj().T,
Expand Down
23 changes: 13 additions & 10 deletions toqito/channel_props/is_unital.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@

import numpy as np

from toqito.channel_ops import apply_channel, kraus_to_choi
from toqito.channel_ops import apply_channel
from toqito.matrix_props import is_identity
from toqito.helper import channel_dim


def is_unital(
phi: np.ndarray | list[list[np.ndarray]],
rtol: float = 1e-05,
atol: float = 1e-08,
dim: int | list[int] | np.ndarray = None,
) -> bool:
r"""
Determine whether the given channel is unital [WatUnital18]_.
Expand All @@ -21,6 +23,10 @@ def is_unital(
.. math::
\Phi(\mathbb{I}_{\mathcal{X}}) = \mathbb{I}_{\mathcal{Y}}.
If the input channel maps :math:`M_{r,c}` to :math:`M_{x,y}` then :code:`dim` should be the
list :code:`[[r,x], [c,y]]`. If it maps :math:`M_m` to :math:`M_n`, then :code:`dim` can simply
be the vector :code:`[m,n]`.
Examples
==========
Expand All @@ -34,15 +40,15 @@ def is_unital(
>>> is_unital(choi)
True
Alternatively, the channel whose Choi matrix is the depolarizing channel is an example of a
non-unital channel.
Additionally, the channel whose Choi matrix is the depolarizing channel is another example of
a unital channel.
>>> from toqito.channels import depolarizing
>>> from toqito.channel_props import is_unital
>>>
>>> choi = depolarizing(4)
>>> is_unital(choi)
False
True
References
==========
Expand All @@ -54,14 +60,11 @@ def is_unital(
:param phi: The channel provided as either a Choi matrix or a list of Kraus operators.
:param rtol: The relative tolerance parameter (default 1e-05).
:param atol: The absolute tolerance parameter (default 1e-08).
:param dim: A scalar, vector or matrix containing the input and output dimensions of PHI.
:return: :code:`True` if the channel is unital, and :code:`False` otherwise.
"""
# If the variable `phi` is provided as a list, we assume this is a list of Kraus operators.
if isinstance(phi, list):
phi = kraus_to_choi(phi)

dim = int(np.sqrt(phi.shape[0]))
dim_in, _, _ = channel_dim(phi, dim=dim, allow_rect=False, compute_env_dim=False)

# Channel is unital if :code:`mat` is the identity matrix.
mat = apply_channel(np.identity(dim), phi)
mat = apply_channel(np.identity(dim_in), phi)
return is_identity(mat, rtol=rtol, atol=atol)

0 comments on commit 716cd8f

Please sign in to comment.