Source code for toqito.channel_ops.partial_channel

"""Apply channel a subsystem of an operator."""
from __future__ import annotations
import numpy as np

from toqito.channel_ops import apply_channel
from toqito.states import max_entangled
from toqito.perms import permute_systems


[docs] def partial_channel( rho: np.ndarray, phi_map: np.ndarray | list[list[np.ndarray]], sys: int = 2, dim: list[int] | np.ndarray = None, ) -> np.ndarray: r"""Apply channel to a subsystem of an operator [WatPMap18]_. Applies the operator .. math:: \left(\mathbb{I} \otimes \Phi \right) \left(\rho \right). In other words, it is the result of applying the channel :math:`\Phi` to the second subsystem of :math:`\rho`, which is assumed to act on two subsystems of equal dimension. The input :code:`phi_map` should be provided as a Choi matrix. This function is adapted from the QETLAB package. Examples ========== The following applies the completely depolarizing channel to the second subsystem of a random density matrix. >>> from toqito.channel_ops import partial_channel >>> from toqito.channels import depolarizing >>> rho = np.array([[0.3101, -0.0220-0.0219*1j, -0.0671-0.0030*1j, -0.0170-0.0694*1j], >>> [-0.0220+0.0219*1j, 0.1008, -0.0775+0.0492*1j, -0.0613+0.0529*1j], >>> [-0.0671+0.0030*1j, -0.0775-0.0492*1j, 0.1361, 0.0602 + 0.0062*1j], >>> [-0.0170+0.0694*1j, -0.0613-0.0529*1j, 0.0602-0.0062*1j, 0.4530]]) >>> phi_x = partial_channel(rho, depolarizing(2)) [[ 0.20545+0.j 0. +0.j -0.0642 +0.02495j 0. +0.j ] [ 0. +0.j 0.20545+0.j 0. +0.j -0.0642 +0.02495j] [-0.0642 -0.02495j 0. +0.j 0.29455+0.j 0. +0.j ] [ 0. +0.j -0.0642 -0.02495j 0. +0.j 0.29455+0.j ]] The following applies the completely depolarizing channel to the first subsystem. >>> from toqito.channel_ops import partial_channel >>> from toqito.channels import depolarizing >>> rho = np.array([[0.3101, -0.0220-0.0219*1j, -0.0671-0.0030*1j, -0.0170-0.0694*1j], >>> [-0.0220+0.0219*1j, 0.1008, -0.0775+0.0492*1j, -0.0613+0.0529*1j], >>> [-0.0671+0.0030*1j, -0.0775-0.0492*1j, 0.1361, 0.0602 + 0.0062*1j], >>> [-0.0170+0.0694*1j, -0.0613-0.0529*1j, 0.0602-0.0062*1j, 0.4530]]) >>> phi_x = partial_channel(rho, depolarizing(2), 1) [[0.2231+0.j 0.0191-0.00785j 0. +0.j 0. +0.j ] [0.0191+0.00785j 0.2769+0.j 0. +0.j 0. +0.j ] [0. +0.j 0. +0.j 0.2231+0.j 0.0191-0.00785j] [0. +0.j 0. +0.j 0.0191+0.00785j 0.2769+0.j ]] References ========== .. [WatPMap18] Watrous, John. The theory of quantum information. Cambridge University Press, 2018. :raises ValueError: If Phi map is not provided as a Choi matrix or Kraus operators. :param rho: A matrix. :param phi_map: The map to partially apply. :param sys: Scalar or vector specifying the size of the subsystems. :param dim: Dimension of the subsystems. If :code:`None`, all dimensions are assumed to be equal. :return: The partial map :code:`phi_map` applied to matrix :code:`rho`. """ if dim is None: dim = np.round(np.sqrt(list(rho.shape))).conj().T * np.ones(2) if isinstance(dim, list): dim = np.array(dim) # Force dim to be a row vector. if dim.ndim == 1: dim = dim.T.flatten() dim = np.array([dim, dim]) prod_dim_r1 = int(np.prod(dim[0, : sys - 1])) prod_dim_c1 = int(np.prod(dim[1, : sys - 1])) prod_dim_r2 = int(np.prod(dim[0, sys:])) prod_dim_c2 = int(np.prod(dim[1, sys:])) if isinstance(phi_map, list): # Compute the Kraus operators on the full system. s_phi_1, s_phi_2 = len(phi_map), len(phi_map[0]) # Map is completely positive. if s_phi_2 == 1 or s_phi_1 == 1 and s_phi_2 > 2: phi = [] for m in phi_map: phi.append( np.kron( np.kron(np.identity(prod_dim_r1), m), np.identity(prod_dim_r2), ) ) phi_x = apply_channel(rho, phi) else: phi_1 = [] for m in phi_map: phi_1.append( np.kron( np.kron(np.identity(prod_dim_r1), m[0]), np.identity(prod_dim_r2), ) ) phi_2 = [] for m in phi_map: phi_2.append( np.kron( np.kron(np.identity(prod_dim_c1), m[1]), np.identity(prod_dim_c2), ) ) phi_x = [list(l) for l in zip(phi_1, phi_2)] phi_x = apply_channel(rho, phi_x) return phi_x # The `phi_map` variable is provided as a Choi matrix. if isinstance(phi_map, np.ndarray): dim_phi = phi_map.shape dim = np.array( [ [ prod_dim_r2, prod_dim_r2, int(dim[0, sys - 1]), int(dim_phi[0] / dim[0, sys - 1]), prod_dim_r1, prod_dim_r1, ], [ prod_dim_c2, prod_dim_c2, int(dim[1, sys - 1]), int(dim_phi[1] / dim[1, sys - 1]), prod_dim_c1, prod_dim_c1, ], ] ) psi_r1 = max_entangled(prod_dim_r2, False, False) psi_c1 = max_entangled(prod_dim_c2, False, False) psi_r2 = max_entangled(prod_dim_r1, False, False) psi_c2 = max_entangled(prod_dim_c1, False, False) phi_map = permute_systems( np.kron(np.kron(psi_r1 * psi_c1.conj().T, phi_map), psi_r2 * psi_c2.conj().T), [1, 3, 5, 2, 4, 6], dim, ) phi_x = apply_channel(rho, phi_map) return phi_x raise ValueError( "The `phi_map` variable is assumed to be provided as " "either a Choi matrix or a list of Kraus operators." )