1# Copyright 2021 The Cirq Developers 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Tools for analyzing and manipulating quantum channels.""" 15from typing import Sequence 16 17import numpy as np 18 19from cirq import protocols 20from cirq._compat import deprecated 21 22 23def kraus_to_choi(kraus_operators: Sequence[np.ndarray]) -> np.ndarray: 24 r"""Returns the unique Choi matrix corresponding to a Kraus representation of a channel. 25 26 Quantum channel E: L(H1) -> L(H2) may be described by a collection of operators A_i, called 27 Kraus operators, such that 28 29 $$ 30 E(\rho) = \sum_i A_i \rho A_i^\dagger. 31 $$ 32 33 Kraus representation is not unique. Alternatively, E may be specified by its Choi matrix J(E) 34 defined as 35 36 $$ 37 J(E) = (E \otimes I)(|\phi\rangle\langle\phi|) 38 $$ 39 40 where $|\phi\rangle = \sum_i|i\rangle|i\rangle$ is the unnormalized maximally entangled state 41 and I: L(H1) -> L(H1) is the identity map. Choi matrix is unique for a given channel. 42 43 The computation of the Choi matrix from a Kraus representation is essentially a reconstruction 44 of a matrix from its eigendecomposition. It has the cost of O(kd**4) where k is the number of 45 Kraus operators and d is the dimension of the input and output Hilbert space. 46 47 Args: 48 kraus_operators: Sequence of Kraus operators specifying a quantum channel. 49 50 Returns: 51 Choi matrix of the channel specified by kraus_operators. 52 """ 53 d = np.prod(kraus_operators[0].shape, dtype=np.int64) 54 c = np.zeros((d, d), dtype=np.complex128) 55 for k in kraus_operators: 56 v = np.reshape(k, d) 57 c += np.outer(v, v.conj()) 58 return c 59 60 61def choi_to_kraus(choi: np.ndarray, atol: float = 1e-10) -> Sequence[np.ndarray]: 62 r"""Returns a Kraus representation of a channel with given Choi matrix. 63 64 Quantum channel E: L(H1) -> L(H2) may be described by a collection of operators A_i, called 65 Kraus operators, such that 66 67 $$ 68 E(\rho) = \sum_i A_i \rho A_i^\dagger. 69 $$ 70 71 Kraus representation is not unique. Alternatively, E may be specified by its Choi matrix J(E) 72 defined as 73 74 $$ 75 J(E) = (E \otimes I)(|\phi\rangle\langle\phi|) 76 $$ 77 78 where $|\phi\rangle = \sum_i|i\rangle|i\rangle$ is the unnormalized maximally entangled state 79 and I: L(H1) -> L(H1) is the identity map. Choi matrix is unique for a given channel. 80 81 The most expensive step in the computation of a Kraus representation from a Choi matrix is 82 the eigendecomposition of the Choi. Therefore, the cost of the conversion is O(d**6) where 83 d is the dimension of the input and output Hilbert space. 84 85 Args: 86 choi: Choi matrix of the channel. 87 atol: Tolerance used in checking if choi is positive and in deciding which Kraus 88 operators to omit. 89 90 Returns: 91 Approximate Kraus representation of the quantum channel specified via a Choi matrix. 92 Kraus operators with Frobenius norm smaller than atol are omitted. 93 94 Raises: 95 ValueError: when choi is not a positive square matrix. 96 """ 97 d = int(np.round(np.sqrt(choi.shape[0]))) 98 if choi.shape != (d * d, d * d): 99 raise ValueError(f"Invalid Choi matrix shape, expected {(d * d, d * d)}, got {choi.shape}") 100 if not np.allclose(choi, choi.T.conj(), atol=atol): 101 raise ValueError("Choi matrix must be Hermitian") 102 103 w, v = np.linalg.eigh(choi) 104 if np.any(w < -atol): 105 raise ValueError(f"Choi matrix must be positive, got one with eigenvalues {w}") 106 107 w = np.maximum(w, 0) 108 u = np.sqrt(w) * v 109 return [k.reshape(d, d) for k in u.T if np.linalg.norm(k) > atol] 110 111 112@deprecated(deadline='v0.14', fix='use cirq.kraus_to_superoperator instead') 113def kraus_to_channel_matrix(kraus_operators: Sequence[np.ndarray]) -> np.ndarray: 114 """Returns the matrix representation of the linear map with given Kraus operators.""" 115 return kraus_to_superoperator(kraus_operators) 116 117 118def kraus_to_superoperator(kraus_operators: Sequence[np.ndarray]) -> np.ndarray: 119 r"""Returns the matrix representation of the linear map with given Kraus operators. 120 121 Quantum channel E: L(H1) -> L(H2) may be described by a collection of operators A_i, called 122 Kraus operators, such that 123 124 $$ 125 E(\rho) = \sum_i A_i \rho A_i^\dagger. 126 $$ 127 128 Kraus representation is not unique. Alternatively, E may be specified by its superoperator 129 matrix K(E) defined so that 130 131 $$ 132 K(E) vec(\rho) = vec(E(\rho)) 133 $$ 134 135 where the vectorization map $vec$ rearranges d-by-d matrices into d**2-dimensional vectors. 136 Superoperator matrix is unique for a given channel. It is also called the natural 137 representation of a quantum channel. 138 139 The computation of the superoperator matrix from a Kraus representation involves the sum of 140 Kronecker products of all Kraus operators. This has the cost of O(kd**4) where k is the number 141 of Kraus operators and d is the dimension of the input and output Hilbert space. 142 143 Args: 144 kraus_operators: Sequence of Kraus operators specifying a quantum channel. 145 146 Returns: 147 Superoperator matrix of the channel specified by kraus_operators. 148 """ 149 d_out, d_in = kraus_operators[0].shape 150 m = np.zeros((d_out * d_out, d_in * d_in), dtype=np.complex128) 151 for k in kraus_operators: 152 m += np.kron(k, k.conj()) 153 return m 154 155 156def superoperator_to_kraus(superoperator: np.ndarray) -> Sequence[np.ndarray]: 157 r"""Returns a Kraus representation of a channel specified via the superoperator matrix. 158 159 Quantum channel E: L(H1) -> L(H2) may be described by a collection of operators A_i, called 160 Kraus operators, such that 161 162 $$ 163 E(\rho) = \sum_i A_i \rho A_i^\dagger. 164 $$ 165 166 Kraus representation is not unique. Alternatively, E may be specified by its superoperator 167 matrix K(E) defined so that 168 169 $$ 170 K(E) vec(\rho) = vec(E(\rho)) 171 $$ 172 173 where the vectorization map $vec$ rearranges d-by-d matrices into d**2-dimensional vectors. 174 Superoperator matrix is unique for a given channel. It is also called the natural 175 representation of a quantum channel. 176 177 The most expensive step in the computation of a Kraus representation from a superoperator 178 matrix is eigendecomposition. Therefore, the cost of the conversion is O(d**6) where d is 179 the dimension of the input and output Hilbert space. 180 181 Args: 182 superoperator: Superoperator matrix specifying a quantum channel. 183 184 Returns: 185 Sequence of Kraus operators of the channel specified by superoperator. 186 187 Raises: 188 ValueError: If superoperator is not a valid superoperator matrix. 189 """ 190 return choi_to_kraus(superoperator_to_choi(superoperator)) 191 192 193def choi_to_superoperator(choi: np.ndarray) -> np.ndarray: 194 r"""Returns the superoperator matrix of a quantum channel specified via the Choi matrix. 195 196 Quantum channel E: L(H1) -> L(H2) may be specified by its Choi matrix J(E) defined as 197 198 $$ 199 J(E) = (E \otimes I)(|\phi\rangle\langle\phi|) 200 $$ 201 202 where $|\phi\rangle = \sum_i|i\rangle|i\rangle$ is the unnormalized maximally entangled state 203 and I: L(H1) -> L(H1) is the identity map. Choi matrix is unique for a given channel. 204 Alternatively, E may be specified by its superoperator matrix K(E) defined so that 205 206 $$ 207 K(E) vec(\rho) = vec(E(\rho)) 208 $$ 209 210 where the vectorization map $vec$ rearranges d-by-d matrices into d**2-dimensional vectors. 211 Superoperator matrix is unique for a given channel. It is also called the natural 212 representation of a quantum channel. 213 214 A quantum channel can be viewed as a tensor with four indices. Different ways of grouping 215 the indices into two pairs yield different matrix representations of the channel, including 216 the superoperator and Choi representations. Hence, the conversion between the superoperator 217 and Choi matrices is a permutation of matrix elements effected by reshaping the array and 218 swapping its axes. Therefore, its cost is O(d**4) where d is the dimension of the input and 219 output Hilbert space. 220 221 Args: 222 choi: Choi matrix specifying a quantum channel. 223 224 Returns: 225 Superoperator matrix of the channel specified by choi. 226 227 Raises: 228 ValueError: If Choi is not Hermitian or is of invalid shape. 229 """ 230 d = int(np.round(np.sqrt(choi.shape[0]))) 231 if choi.shape != (d * d, d * d): 232 raise ValueError(f"Invalid Choi matrix shape, expected {(d * d, d * d)}, got {choi.shape}") 233 if not np.allclose(choi, choi.T.conj()): 234 raise ValueError("Choi matrix must be Hermitian") 235 236 c = np.reshape(choi, (d, d, d, d)) 237 s = np.swapaxes(c, 1, 2) 238 return np.reshape(s, (d * d, d * d)) 239 240 241def superoperator_to_choi(superoperator: np.ndarray) -> np.ndarray: 242 r"""Returns the Choi matrix of a quantum channel specified via the superoperator matrix. 243 244 Quantum channel E: L(H1) -> L(H2) may be specified by its Choi matrix J(E) defined as 245 246 $$ 247 J(E) = (E \otimes I)(|\phi\rangle\langle\phi|) 248 $$ 249 250 where $|\phi\rangle = \sum_i|i\rangle|i\rangle$ is the unnormalized maximally entangled state 251 and I: L(H1) -> L(H1) is the identity map. Choi matrix is unique for a given channel. 252 Alternatively, E may be specified by its superoperator matrix K(E) defined so that 253 254 $$ 255 K(E) vec(\rho) = vec(E(\rho)) 256 $$ 257 258 where the vectorization map $vec$ rearranges d-by-d matrices into d**2-dimensional vectors. 259 Superoperator matrix is unique for a given channel. It is also called the natural 260 representation of a quantum channel. 261 262 A quantum channel can be viewed as a tensor with four indices. Different ways of grouping 263 the indices into two pairs yield different matrix representations of the channel, including 264 the superoperator and Choi representations. Hence, the conversion between the superoperator 265 and Choi matrices is a permutation of matrix elements effected by reshaping the array and 266 swapping its axes. Therefore, its cost is O(d**4) where d is the dimension of the input and 267 output Hilbert space. 268 269 Args: 270 superoperator: Superoperator matrix specifying a quantum channel. 271 272 Returns: 273 Choi matrix of the channel specified by superoperator. 274 275 Raises: 276 ValueError: If superoperator has invalid shape. 277 """ 278 d = int(np.round(np.sqrt(superoperator.shape[0]))) 279 if superoperator.shape != (d * d, d * d): 280 raise ValueError( 281 f"Invalid superoperator matrix shape, expected {(d * d, d * d)}, " 282 f"got {superoperator.shape}" 283 ) 284 285 s = np.reshape(superoperator, (d, d, d, d)) 286 c = np.swapaxes(s, 1, 2) 287 return np.reshape(c, (d * d, d * d)) 288 289 290def operation_to_choi(operation: 'protocols.SupportsKraus') -> np.ndarray: 291 r"""Returns the unique Choi matrix associated with an operation . 292 293 Choi matrix J(E) of a linear map E: L(H1) -> L(H2) which takes linear operators 294 on Hilbert space H1 to linear operators on Hilbert space H2 is defined as 295 296 $$ 297 J(E) = (E \otimes I)(|\phi\rangle\langle\phi|) 298 $$ 299 300 where $|\phi\rangle = \sum_i|i\rangle|i\rangle$ is the unnormalized maximally 301 entangled state and I: L(H1) -> L(H1) is the identity map. Note that J(E) is 302 a square matrix with d1*d2 rows and columns where d1 = dim H1 and d2 = dim H2. 303 304 Args: 305 operation: Quantum channel. 306 Returns: 307 Choi matrix corresponding to operation. 308 """ 309 return kraus_to_choi(protocols.kraus(operation)) 310 311 312@deprecated(deadline='v0.14', fix='use cirq.operation_to_superoperator instead') 313def operation_to_channel_matrix(operation: 'protocols.SupportsKraus') -> np.ndarray: 314 """Returns the matrix representation of an operation in standard basis. 315 316 Let E: L(H1) -> L(H2) denote a linear map which takes linear operators on Hilbert space H1 317 to linear operators on Hilbert space H2 and let d1 = dim H1 and d2 = dim H2. Also, let Fij 318 denote an operator whose matrix has one in ith row and jth column and zeros everywhere else. 319 Note that d1-by-d1 operators Fij form a basis of L(H1). Similarly, d2-by-d2 operators Fij 320 form a basis of L(H2). This function returns the matrix of E in these bases. 321 322 Args: 323 operation: Quantum channel. 324 Returns: 325 Matrix representation of operation. 326 """ 327 return operation_to_superoperator(operation) 328 329 330def operation_to_superoperator(operation: 'protocols.SupportsKraus') -> np.ndarray: 331 """Returns the matrix representation of an operation in standard basis. 332 333 Let E: L(H1) -> L(H2) denote a linear map which takes linear operators on Hilbert space H1 334 to linear operators on Hilbert space H2 and let d1 = dim H1 and d2 = dim H2. Also, let Fij 335 denote an operator whose matrix has one in ith row and jth column and zeros everywhere else. 336 Note that d1-by-d1 operators Fij form a basis of L(H1). Similarly, d2-by-d2 operators Fij 337 form a basis of L(H2). This function returns the matrix of E in these bases. 338 339 Args: 340 operation: Quantum channel. 341 Returns: 342 Matrix representation of operation. 343 """ 344 return kraus_to_superoperator(protocols.kraus(operation)) 345