Christopher Granade

Institute for Quantum Computing
$\newcommand{\ket}[1]{\left|#1\right\rangle}$
$\newcommand{\bra}[1]{\left\langle#1\right|}$
$\newcommand{\cnot}{{\scriptstyle \rm CNOT}}$
$\newcommand{\Tr}{\operatorname{Tr}}$

In this notebook, we will demonstrate the `tensor_contract`

function, which contracts one or more pairs of indices of a Qobj. This functionality can be used to find rectangular superoperators that implement the partial trace channel $S(\rho) = \Tr_2(\rho)$, for instance. Using this functionality, we can quickly turn a system-environment representation of an open quantum process into a superoperator representation.

We enable a few features such that this notebook runs in both Python 2 and 3.

In [1]:

```
from __future__ import division, print_function
```

In [2]:

```
import numpy as np
import qutip as qt
from qutip.ipynbtools import version_table
```

In [3]:

```
%matplotlib inline
```

In [4]:

```
qt.settings.colorblind_safe = True
```

We start off by first demonstrating plotting of superoperators, as this will be useful to us in visualizing the results of a contracted channel.

In particular, we will use Hinton diagrams as implemented by `qutip.visualization.hinton`

, which
show the real parts of matrix elements as squares whose size and color both correspond to the magnitude of each element. To illustrate, we first plot a few density operators.

In [5]:

```
qt.visualization.hinton(qt.identity([2, 3]).unit());
```

In [6]:

```
qt.visualization.hinton(qt.Qobj([
[1, 0.5],
[0.5, 1]
]).unit());
```

We show superoperators as matrices in the *Pauli basis*, such that any Hermicity-preserving map is represented by a real-valued matrix. This is especially convienent for use with Hinton diagrams, as the plot thus carries complete information about the channel.

As an example, conjugation by $\sigma_z$ leaves $\mathbb{1}$ and $\sigma_z$ invariant, but flips the sign of $\sigma_x$ and $\sigma_y$. This is indicated in Hinton diagrams by a negative-valued square for the sign change and a positive-valued square for a +1 sign.

In [7]:

```
qt.visualization.hinton(qt.to_super(qt.sigmaz()));
```

As a couple more examples, we also consider the supermatrix for a Hadamard transform and for $\sigma_z \otimes H$.

In [8]:

```
qt.visualization.hinton(qt.to_super(qt.hadamard_transform()));
```

In [9]:

```
qt.visualization.hinton(qt.to_super(qt.tensor(qt.sigmaz(), qt.hadamard_transform())));
```

As an example of tensor contraction, we now consider the map $S(\rho) = \Tr_2[\cnot (\rho \otimes \ket{0}\bra{0}) \cnot^\dagger]$.
We can think of the $\cnot$ here as a system-environment representation of an open quantum process, in which an environment register is prepared in a state $\rho_{\text{anc}}$, then a unitary acts jointly on the system of interest and environment. Finally, the environment is traced out, leaving a *channel* on the system alone. In terms of Wood diagrams, this can be represented as the composition of a preparation map, evolution under the system-environment unitary, and then a measurement map.

The two tensor wires on the left indicate where we must take a tensor contraction to obtain the measurement map. Numbering the tensor wires from 0 to 3, this corresponds to a `tensor_contract`

argument of `(1, 3)`

.

In [10]:

```
s_meas = qt.tensor_contract(qt.to_super(qt.identity([2, 2])), (1, 3))
s_meas
```

Out[10]:

Meanwhile, the `super_tensor`

function implements the swap on the right, such that we can quickly find the preparation map.

In [11]:

```
q = qt.tensor(qt.identity(2), qt.basis(2))
s_prep = qt.sprepost(q, q.dag())
s_prep
```

Out[11]:

For a $\cnot$ system-environment model, the composition of these maps should give us a completely dephasing channel. The channel on both qubits is just the superunitary $\cnot$ channel:

In [12]:

```
qt.visualization.hinton(qt.to_super(qt.cnot()))
```

Out[12]:

We now complete by multiplying the superunitary $\cnot$ by the preparation channel above, then applying the partial trace channel by contracting the second and fourth index indices. As expected, this gives us a dephasing map.

In [13]:

```
qt.tensor_contract(qt.to_super(qt.cnot()), (1, 3)) * s_prep
```

Out[13]:

In [14]:

```
qt.visualization.hinton(qt.tensor_contract(qt.to_super(qt.cnot()), (1, 3)) * s_prep);
```

In [15]:

```
version_table()
```

Out[15]: