Skip to content

complementary_channel

Computes the complementary channel/map of a superoperator.

complementary_channel

complementary_channel(
    kraus_ops: list[ndarray],
) -> list[ndarray]

Compute the Kraus operators for the complementary map of a quantum channel.

(Section: Representations and Characterizations of Channels from 1).

The complementary map is derived from the given quantum channel's Kraus operators by rearranging the rows of the input Kraus operators into the Kraus operators of the complementary map.

Specifically, for each Kraus operator \(K_i\) in the input channel \(\Phi\), we define the complementary Kraus operators \(K_i^C\) by stacking the rows of \(K_i\) from all Kraus operators vertically.

Parameters:

  • kraus_ops (list[ndarray]) –

    A list of numpy arrays representing the Kraus operators of a quantum channel. Each Kraus operator is assumed to be a square matrix.

Returns:

  • list[ndarray]

    A list of numpy arrays representing the Kraus operators of the complementary map.

Raises:

  • ValueError

    If the input is not a valid list of Kraus operators.

Examples:

Suppose the following Kraus operators define a quantum channel:

\[ K_1 = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 0 \\ 0 & 0 \end{pmatrix}, K_2 = \frac{1}{\sqrt{2}} \begin{pmatrix} 0 & 1 \\ 0 & 0 \end{pmatrix}, K_3 = \frac{1}{\sqrt{2}} \begin{pmatrix} 0 & 0 \\ 1 & 0 \end{pmatrix}, K_4 = \frac{1}{\sqrt{2}} \begin{pmatrix} 0 & 0 \\ 0 & 1 \end{pmatrix} \]

To compute the Kraus operators for the complementary map, we rearrange the rows of these Kraus operators as follows:

import numpy as np
from toqito.channel_ops import complementary_channel
kraus_ops_Phi = [
np.sqrt(0.5) * np.array([[1, 0], [0, 0]]),
np.sqrt(0.5) * np.array([[0, 1], [0, 0]]),
np.sqrt(0.5) * np.array([[0, 0], [1, 0]]),
np.sqrt(0.5) * np.array([[0, 0], [0, 1]])
]
comp_kraus_ops = complementary_channel(kraus_ops_Phi)
for i, op in enumerate(comp_kraus_ops):
    print(f"Kraus operator {i + 1}:")
    print(op)
Kraus operator 1:
[[0.70710678 0.        ]
 [0.         0.70710678]
 [0.         0.        ]
 [0.         0.        ]]
Kraus operator 2:
[[0.         0.        ]
 [0.         0.        ]
 [0.70710678 0.        ]
 [0.         0.70710678]]

References

1 Watrous, John. The Theory of Quantum Information. (2018). doi:10.1017/9781316848142.

Source code in toqito/channel_ops/complementary_channel.py
def complementary_channel(kraus_ops: list[np.ndarray]) -> list[np.ndarray]:
    r"""Compute the Kraus operators for the complementary map of a quantum channel.

    (Section: Representations and Characterizations of Channels from [@watrous2018theory]).

    The complementary map is derived from the given quantum channel's Kraus operators by
    rearranging the rows of the input Kraus operators into the Kraus operators of the
    complementary map.

    Specifically, for each Kraus operator \(K_i\) in the input channel \(\Phi\),
    we define the complementary Kraus operators \(K_i^C\) by stacking the rows of
    \(K_i\) from all Kraus operators vertically.

    Args:
        kraus_ops: A list of numpy arrays representing the Kraus operators of a quantum channel. Each Kraus operator is
            assumed to be a square matrix.

    Returns:
        A list of numpy arrays representing the Kraus operators of the complementary map.

    Raises:
        ValueError: If the input is not a valid list of Kraus operators.

    Examples:
        Suppose the following Kraus operators define a quantum channel:

        \[
            K_1 = \frac{1}{\sqrt{2}} \begin{pmatrix}
                1 & 0 \\
                0 & 0
            \end{pmatrix},
            K_2 = \frac{1}{\sqrt{2}} \begin{pmatrix}
                0 & 1 \\
                0 & 0
            \end{pmatrix},
            K_3 = \frac{1}{\sqrt{2}} \begin{pmatrix}
                0 & 0 \\
                1 & 0
            \end{pmatrix},
            K_4 = \frac{1}{\sqrt{2}} \begin{pmatrix}
                0 & 0 \\
                0 & 1
            \end{pmatrix}
        \]

        To compute the Kraus operators for the complementary map, we rearrange the rows of these
        Kraus operators as follows:

        ```python exec="1" source="above" result="text"
        import numpy as np
        from toqito.channel_ops import complementary_channel
        kraus_ops_Phi = [
        np.sqrt(0.5) * np.array([[1, 0], [0, 0]]),
        np.sqrt(0.5) * np.array([[0, 1], [0, 0]]),
        np.sqrt(0.5) * np.array([[0, 0], [1, 0]]),
        np.sqrt(0.5) * np.array([[0, 0], [0, 1]])
        ]
        comp_kraus_ops = complementary_channel(kraus_ops_Phi)
        for i, op in enumerate(comp_kraus_ops):
            print(f"Kraus operator {i + 1}:")
            print(op)
        ```

    """
    num_kraus = len(kraus_ops)
    if num_kraus == 0:
        raise ValueError("All Kraus operators must be non-empty matrices.")

    op_dim = kraus_ops[0].shape[0]

    if any(k.shape[0] != k.shape[1] for k in kraus_ops):
        raise ValueError("All Kraus operators must be square matrices.")

    if any(k.shape[0] != op_dim for k in kraus_ops):
        raise ValueError("All Kraus operators must be equal size matrices.")

    # Check the Kraus completeness relation: ∑ K_i† K_i = I
    identity = np.eye(op_dim, dtype=kraus_ops[0].dtype)
    sum_k_dagger_k = sum(k.T.conj() @ k for k in kraus_ops)

    if not np.allclose(sum_k_dagger_k, identity):
        raise ValueError("The Kraus operators do not satisfy the completeness relation ∑ K_i† K_i = I.")

    comp_kraus_ops = []

    for row in range(op_dim):
        comp_kraus_op = np.vstack([kraus_ops[i][row, :] for i in range(num_kraus)])
        comp_kraus_ops.append(comp_kraus_op)

    return comp_kraus_ops