def rand_map_with_BCSZ_dist(D, K): """ Given a Hilbert space dimension $D$ and a Kraus rank $K$, returns a $D^2 × D^2$ Choi matrix $J(Λ)$ of a channel drawn from the BCSZ distribution with Kraus rank $K$. [RQO] Random quantum operations, Bruzda et al., Physics Letters A 373, 320 (2009). https://doi.org/10.1016/j.physleta.2008.11.043 https://arxiv.org/abs/0804.2361 :param D: Hilbert space dimension (scalar). :param K: The rank of a state (scalar). :return: D^2 × D^2 Choi matrix, drawn from the BCSZ distribution with Kraus rank K. """ # TODO: this ^^ is CPTP, might want a flag that allows for just CP quantum operations. X = ginibre_matrix_complex(D**2, K) rho = X @ X.conj().T rho_red = partial_trace(rho, [0], [D, D]) # Note that Eqn. 8 of [RQO] uses a *row* stacking convention so in that case we would write # Q = np.kron(np.eye(D), sqrtm(la.inv(rho_red))) # But as we use column stacking we need: Q = np.kron(sqrtm(la.inv(rho_red)), np.eye(D)) Z = Q @ rho @ Q return Z
def proj_choi_to_trace_preserving(choi: np.ndarray) -> np.ndarray: """ Projects the Choi representation of a process to the closest processes in the space of trace preserving maps. Equation 12 of [PGD], but without vecing the Choi matrix. See choi_is_trace_preserving for comparison. :param choi: Choi representation of a process :return: Choi representation of the projected trace preserving process """ dim = int(np.sqrt(choi.shape[0])) # trace out the output Hilbert space, keep the input space at index 0 pt = partial_trace(choi, dims=[dim, dim], keep=[0]) # isolate the part the violates the condition we want, namely pt = Id diff = pt - np.eye(dim) # we want to subtract off the violation from the larger operator, so 'invert' the partial_trace subtract = np.kron(diff / dim, np.eye(dim)) return choi - subtract
def choi_is_trace_preserving(choi: np.ndarray, rtol: float = 1e-05, atol: float = 1e-08) -> bool: """ Checks if a quantum process, specified by a Choi matrix, is trace-preserving. :param choi: A dim**2 by dim**2 Choi matrix :param rtol: The relative tolerance parameter in np.allclose :param atol: The absolute tolerance parameter in np.allclose :return: Returns True if the quantum channel is trace-preserving with the given tolerance; False otherwise. """ rows, cols = choi.shape dim = int(np.sqrt(rows)) # the choi matrix acts on the Hilbert space H_{in} \otimes H_{out}. # We want to trace out H_{out} and so keep the H_{in} space at index 0. keep = [0] id_iff_tp = partial_trace(choi, keep, [dim, dim]) # Equation 3.33 of [GRAPTN] return np.allclose(id_iff_tp, np.identity(dim), rtol=rtol, atol=atol)
def proj_to_tni(choi_vec): """ Projects the vectorized Choi matrix of a process into the space of trace non-increasing maps. Equation 33 of [PGD] :param choi_vec: vectorized Choi representation of a process :return: The vectorized Choi representation of the projected TNI process """ dim = int(np.sqrt(np.sqrt(choi_vec.size))) # trace out the output Hilbert space pt = partial_trace(unvec(choi_vec), dims=[dim, dim], keep=[0]) hermitian = (pt + pt.conj().T) / 2 # enforce Hermiticity d, v = np.linalg.eigh(hermitian) d[d > 1] = 1 # enforce trace preserving D = np.diag(d) projection = v @ D @ v.conj().T trace_increasing_part = np.kron((pt - projection) / dim, np.eye(dim)) return choi_vec - vec(trace_increasing_part)
def proj_choi_to_trace_non_increasing(choi: np.ndarray) -> np.ndarray: """ Projects the Choi matrix of a process into the space of trace non-increasing maps. Equation 33 of [PGD] :param choi: Choi representation of a process :return: Choi representation of the projected trace non-increasing process """ dim = int(np.sqrt(choi.shape[0])) # trace out the output Hilbert space pt = partial_trace(choi, dims=[dim, dim], keep=[0]) hermitian = (pt + pt.conj().T) / 2 # enforce Hermiticity d, v = np.linalg.eigh(hermitian) d[d > 1] = 1 # enforce trace preserving D = np.diag(d) projection = v @ D @ v.conj().T trace_increasing_part = np.kron((pt - projection) / dim, np.eye(dim)) return choi - trace_increasing_part
def apply_choi_matrix_2_state(choi: np.ndarray, state: np.ndarray) -> np.ndarray: r""" Apply a quantum channel, specified by a Choi matrix (using the column stacking convention), to a state. The Choi matrix is a dim**2 by dim**2 matrix and the state rho is a dim by dim matrix. The output state is rho_{out} = Tr_{A_{in}}[(rho^T \otimes Id) Choi_matrix ], where T denotes transposition and Tr_{A_{in}} is the partial trace over input Hilbert space H_{ A_{in}}; the Choi matrix representing a process mapping rho in H_{A_{in}} to rho_{out} in H_{B_{out}} is regarded as an operator on the space H_{A_{in}} \otimes H_{B_{out}}. :param choi: a dim**2 by dim**2 matrix :param state: A dim by dim ndarray which is the density matrix for the state :return: a dim by dim matrix. """ dim = int(np.sqrt(np.asarray(choi).shape[0])) dims = [dim, dim] tot_matrix = np.kron(state.transpose(), np.identity(dim)) @ choi return partial_trace(tot_matrix, [1], dims)
def test_proj_to_tni(): state = np.array([[0., 0., 0., 0.], [0., 1.01, 1.01, 0.], [0., 1., 1., 0.], [0., 0., 0., 0.]]) trace_non_increasing = unvec(proj_to_tni(vec(state))) pt = partial_trace(trace_non_increasing, dims=[2, 2], keep=[0]) assert np.allclose(pt, np.eye(2))