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