sf.utils.extract_channel¶
-
extract_channel
(prog, cutoff_dim, representation='choi', vectorize_modes=False)[source]¶ Numerical array representation of the channel corresponding to a quantum circuit.
The representation choices include the Choi state representation, the Liouville representation, and the Kraus representation.
Note
Channel extraction can currently only be performed using the
'fock'
backend.Tensor shapes
If
vectorize_modes=True
:representation='choi'
andrepresentation='liouville'
return an array with 4 indicesrepresentation='kraus'
returns an array of Kraus operators in matrix form
If
vectorize_modes=False
:representation='choi'
andrepresentation='liouville'
return an array with \(4N\) indicesrepresentation='kraus'
returns an array of Kraus operators with \(2N\) indices each, where \(N\) is the number of modes that the Program is created with
Note that the Kraus representation automatically returns only the non-zero Kraus operators. One can reduce the number of operators by discarding Kraus operators with small norm (thus approximating the channel).
Choi representation
Mathematically, the Choi representation of a channel is a bipartite state \(\Lambda_{AB}\) which contains a complete description of the channel. The way we use it to compute the action of the channel \(\mathcal{C}\) on an input state \(\mathcal{\rho}\) is as follows:
\[\mathcal{C}(\rho) = \mathrm{Tr}[(\rho_A^T\otimes\mathbb{1}_B)\Lambda_{AB}]\]The indices of the non-vectorized Choi operator match exactly those of the state, so that the action of the channel can be computed as (e.g., for one mode or for
vectorize_modes=True
):>>> rho_out = np.einsum('ab,abcd', rho_in, choi)
Notice that this respects the transpose operation.
For two modes:
>>> rho_out = np.einsum('abcd,abcdefgh', rho_in, choi)
Combining consecutive channels (in the order \(1,2,3,\dots\)) is also straightforward with the Choi operator:
>>> choi_combined = np.einsum('abcd,cdef,efgh', choi_1, choi_2, choi_3)
Liouville operator
The Liouville operator is a partial transpose of the Choi operator, such that the first half of consecutive index pairs are the output-input right modes (i.e., acting on the “bra” part of the state) and the second half are the output-input left modes (i.e., acting on the “ket” part of the state).
Therefore, the action of the Liouville operator (e.g., for one mode or for
vectorize_modes=True
) is\[\mathcal{C}(\rho) = \mathrm{unvec}[\mathcal{L}\mathrm{vec}(\rho)]\]where vec() and unvec() are the operations that stack the columns of a matrix to form a vector and vice versa. In code:
>>> rho_out = np.einsum('abcd,bd->ca', liouville, rho_in)
Notice that the state contracts with the second index of each pair and that we output the ket on the left (
c
) and the bra on the right (a
).For two modes we have:
>>> rho_out = np.einsum('abcdefgh,fbhd->eagc', liouville, rho_in)
The Liouville representation has the property that if the channel is unitary, the operator is separable. On the other hand, even if the channel were the identity, the Choi operator would correspond to a maximally entangled state.
The choi and liouville operators in matrix form (i.e., with two indices) can be found as follows, where
D
is the dimension of each vectorized index (i.e., for \(N\) modes,D=cutoff_dim**N
):>>> choi_matrix = liouville.reshape(D**2, D**2).T >>> liouville_matrix = choi.reshape(D**2, D**2).T
Kraus representation
The Kraus representation is perhaps the most well known:
\[\mathcal{C}(\rho) = \sum_k A_k\rho A_k^\dagger\]So to define a channel in the Kraus representation one needs to supply a list of Kraus operators \(\{A_k\}\). In fact, the result of
extract_channel
in the Kraus representation is a rank-3 tensor, where the first index is the one indexing the list of operators.Adjacent indices of each Kraus operator correspond to output-input pairs of the same mode, so the action of the channel can be written as (here for one mode or for
vectorize_modes=True
):>>> rho_out = np.einsum('abc,cd,aed->be', kraus, rho_in, np.conj(kraus))
Notice the transpose on the third index string (
aed
rather thanade
), as the last operator should be the conjugate transpose of the first, and we cannot just donp.conj(kraus).T
becausekraus
has 3 indices and we just need to transpose the last two.Example
Here we show that the Choi operator of the identity channel is proportional to a maximally entangled Bell \(\ket{\phi^+}\) state:
>>> prog = sf.Program(num_subsystems=1) >>> C = extract_channel(prog, cutoff_dim=2, representation='choi') >>> print(abs(C).reshape((4,4))) [[1. 0. 0. 1.] [0. 0. 0. 0.] [0. 0. 0. 0.] [1. 0. 0. 1.]]
- Parameters
prog (Program) – program containing the circuit
cutoff_dim (int) – dimension of each index
representation (str) – choice between
'choi'
,'liouville'
or'kraus'
vectorize_modes (bool) – if True, reshapes the result into rank-4 tensor, otherwise it returns a rank-4N tensor, where N is the number of modes
- Returns
channel, according to the specified options
- Return type
array
- Raises
TypeError – if the gates used to construct the circuit are not all unitary or channels