Skip to content

channel_dim

Computes the input, output, and environment dimensions of a channel.

channel_dim

channel_dim(
    phi: ndarray | list[ndarray] | list[list[ndarray]],
    allow_rect: bool = True,
    dim: int | list[int] | ndarray | None = None,
    compute_env_dim: bool = True,
) -> tuple[ndarray | int, ndarray | int, int | None]

Compute the input, output, and environment dimensions of a channel.

This function returns the dimensions of the input, output, and environment spaces of the input channel, in that order. Input and output dimensions are both \(1 \times 2\) vectors containing the row and column dimensions of their spaces. The environment dimension is always a scalar, and it is equal to the number of Kraus operators of phi (if phi is provided as a Choi matrix then the environment dimension is the minimal number of Kraus operators of any representation of phi).

The dim argument should be provided if and only if phi is a Choi matrix with unequal input and output dimensions (since it is impossible to determine the input and output dimensions from the Choi matrix alone). If allow_rect is false and phi acts on non-square matrix spaces, an error will be produced. If phi maps \(M_{r,c}\) to \(M_{x,y}\) then dim should be the \(2 \times 2\) matrix [[r,x], [c,y]]. If phi maps \(M_m\) to \(M_n\), then dim can simply be the vector [m,n]. If allow_rect is false then the returned input and output dimensions will be scalars instead of vectors. If compute_env_dim is false and phi is a Choi matrix we avoid computing the rank of the Choi matrix.

This function was adapted from QETLAB 1.

Parameters:

  • phi (ndarray | list[ndarray] | list[list[ndarray]]) –

    A superoperator. It should be provided either as a Choi matrix, or as a (1d or 2d) list of numpy arrays whose entries are its Kraus operators.

  • allow_rect (bool, default: True ) –

    A flag indicating that the input and output spaces of phi can be non-square (default True).

  • dim (int | list[int] | ndarray | None, default: None ) –

    A scalar, vector or matrix containing the input and output dimensions of phi.

  • compute_env_dim (bool, default: True ) –

    A flag indicating whether we compute the environment dimension.

Returns:

  • tuple[ndarray | int, ndarray | int, int | None]

    The input, output, and environment dimensions of a channel.

Examples:

The dimensions of a channel can be computed from a list of Kraus operators. For example, the following Kraus operators describe a qubit bit-flip channel:

import numpy as np
from toqito.channel_props.channel_dim import channel_dim

kraus_ops = [
    np.sqrt(0.5) * np.eye(2),
    np.sqrt(0.5) * np.array([[0, 1], [1, 0]]),
]
dim_in, dim_out, dim_env = channel_dim(kraus_ops)

print(dim_in, dim_out, dim_env)
[2 2] [2 2] 2

References

1 Johnston, Nathaniel. {{QETLAB}: {A MATLAB} toolbox for quantum entanglement}. doi:10.5281/zenodo.44637.

Source code in toqito/channel_props/channel_dim.py
def channel_dim(
    phi: np.ndarray | list[np.ndarray] | list[list[np.ndarray]],
    allow_rect: bool = True,
    dim: int | list[int] | np.ndarray | None = None,
    compute_env_dim: bool = True,
) -> tuple[np.ndarray | int, np.ndarray | int, int | None]:
    r"""Compute the input, output, and environment dimensions of a channel.

    This function returns the dimensions of the input, output, and environment spaces of the
    input channel, in that order. Input and output dimensions are both \(1 \times 2\) vectors
    containing the row and column dimensions of their spaces. The environment dimension is
    always a scalar, and it is equal to the number of Kraus operators of `phi` (if `phi` is
    provided as a Choi matrix then the environment dimension is the minimal number of Kraus
    operators of any representation of `phi`).

    The `dim` argument should be provided if and only if `phi` is a Choi matrix with unequal
    input and output dimensions (since it is impossible to determine the input and output
    dimensions from the Choi matrix alone). If `allow_rect` is false and `phi` acts on
    non-square matrix spaces, an error will be produced. If `phi` maps \(M_{r,c}\) to
    \(M_{x,y}\) then `dim` should be the \(2 \times 2\) matrix `[[r,x], [c,y]]`. If `phi` maps
    \(M_m\) to \(M_n\), then `dim` can simply be the vector `[m,n]`. If `allow_rect` is false
    then the returned input and output dimensions will be scalars instead of vectors. If
    `compute_env_dim` is false and `phi` is a Choi matrix we avoid computing the rank of the
    Choi matrix.

    This function was adapted from QETLAB [@qetlablink].

    Args:
        phi: A superoperator. It should be provided either as a Choi matrix, or as a (1d or 2d) list of numpy arrays
            whose entries are its Kraus operators.
        allow_rect: A flag indicating that the input and output spaces of `phi` can be non-square (default True).
        dim: A scalar, vector or matrix containing the input and output dimensions of `phi`.
        compute_env_dim: A flag indicating whether we compute the environment dimension.

    Returns:
        The input, output, and environment dimensions of a channel.

    Examples:
        The dimensions of a channel can be computed from a list of Kraus operators.
        For example, the following Kraus operators describe a qubit bit-flip
        channel:

        ```python exec="1" source="above" result="text"
        import numpy as np
        from toqito.channel_props.channel_dim import channel_dim

        kraus_ops = [
            np.sqrt(0.5) * np.eye(2),
            np.sqrt(0.5) * np.array([[0, 1], [1, 0]]),
        ]
        dim_in, dim_out, dim_env = channel_dim(kraus_ops)

        print(dim_in, dim_out, dim_env)
        ```

    """
    dim_in = np.zeros(2, dtype=int)
    dim_out = np.zeros(2, dtype=int)

    if isinstance(phi, list):
        left_ops, right_ops, is_cpt = normalize_kraus(phi)

        dim_e = len(left_ops)
        dim_out[0], dim_in[0] = left_ops[0].shape
        if is_cpt:
            # input and output are squares.
            dim_in[1] = dim_in[0]
            dim_out[1] = dim_out[0]
        else:
            dim_out[1], dim_in[1] = right_ops[0].shape

        if dim is None:
            dim = np.vstack([dim_in, dim_out]).T
        dim = _expand_dim(dim)

        # Now do some error checking.
        if (dim_in[0] != dim_in[1] or dim_out[0] != dim_out[1]) and not allow_rect:
            raise ValueError("The input and output spaces of PHI must be square.")

        if np.any(dim != np.vstack([dim_in, dim_out]).T):
            raise ValueError("The dimensions of PHI do not match those provided in the DIM argument.")

        if (is_cpt and any(k_mat.shape != (dim[0, 1], dim[0, 0]) for k_mat in left_ops)) or (
            not is_cpt
            and any(
                left.shape != (dim[0, 1], dim[0, 0]) or right.shape != (dim[1, 1], dim[1, 0])
                for left, right in zip(left_ops, right_ops)
            )
        ):
            raise ValueError("The Kraus operators of PHI do not all have the same size.")

    # If Phi is a Choi matrix, the dimensions are a bit more of a pain: we have
    # to guess a bit if the input and output dimensions are different.
    else:
        # Try to guess input and output dims.
        rows, cols = phi.shape
        dim_in = np.array([int(np.round(np.sqrt(rows))), int(np.round(np.sqrt(cols)))])
        dim_out = dim_in

        if dim is None:
            dim = np.vstack([dim_in, dim_out]).T
        dim = _expand_dim(dim)

        if dim[0, 0] * dim[0, 1] != rows or dim[1, 0] * dim[1, 1] != cols:
            raise ValueError(
                "If the input and output dimensions are unequal and PHI is provided "
                "as a Choi matrix, the optional argument DIM must be specified "
                "(and its dimensions must agree with PHI)."
            )

        if (dim[0, 0] != dim[1, 0] or dim[0, 1] != dim[1, 1]) and not allow_rect:
            raise ValueError("The input and output spaces of PHI must be square.")

        # environment dimension is the rank of the Choi matrix
        dim_e = None
        if compute_env_dim:
            dim_e = np.linalg.matrix_rank(phi)

    # Finally, put `dim` back into `dim_in` and `dim_out`.
    if allow_rect:
        dim_in = np.array([dim[0, 0], dim[1, 0]])
        dim_out = np.array([dim[0, 1], dim[1, 1]])
    else:
        dim_in = dim[0, 0]
        dim_out = dim[0, 1]

    return (dim_in, dim_out, dim_e)