Phase Kickback

## Exploring the CNOT-Gate

In the previous section, we saw some very basic results with the CNOT gate. Here we will explore some more interesting results.

We saw that we could entangle the two qubits by placing the control qubit in the state $|+\rangle$:

$$\text{CNOT}|0{+}\rangle = \tfrac{1}{\sqrt{2}}(|00\rangle + |11\rangle)$$

But what happens if we put the second qubit in superposition?

from qiskit import *
from math import pi
import numpy as np
from qiskit.visualization import plot_bloch_multivector, plot_histogram

# For Jupyter Notebooks:
%config InlineBackend.figure_format = 'svg' # Makes the images look nice

qc = QuantumCircuit(2)
qc.h(0)
qc.h(1)
qc.cx(0,1)
qc.draw('mpl')


We see we have the CNOT acting on the state:

$$|{+}{+}\rangle = \tfrac{1}{2}(|00\rangle + |01\rangle + |10\rangle + |11\rangle)$$

Since the CNOT swaps the amplitudes of $|01\rangle$ and $|11\rangle$, we see no change:

# Let's see the result
statevector_backend = Aer.get_backend('statevector_simulator')
final_state = execute(qc,statevector_backend).result().get_statevector()

# In Jupyter Notebooks we can display this nicely using Latex.
# If not using Jupyter Notebooks you may need to remove the
# vector2latex function and use print(final_state) instead.
from qiskit_textbook.tools import vector2latex
vector2latex(final_state, pretext="\\text{Statevector} = ", precision=1)

$$\text{Statevector} = \begin{bmatrix} 0.5 \\ 0.5 \\ 0.5 \\ 0.5\end{bmatrix}$$
plot_bloch_multivector(final_state)


Let’s add a negative phase to the target qubit:

qc = QuantumCircuit(2)
qc.h(0)
qc.h(1)
qc.z(1)
qc.draw('mpl')


This is the state:

$$|{-}{+}\rangle = \tfrac{1}{2}(|00\rangle + |01\rangle - |10\rangle - |11\rangle)$$
final_state = execute(qc,statevector_backend).result().get_statevector()
vector2latex(final_state, pretext="\\text{Statevector} = ", precision=1)

$$\text{Statevector} = \begin{bmatrix} 0.5 \\ 0.5 \\ -0.5 \\ -0.5\end{bmatrix}$$
plot_bloch_multivector(final_state)


If the CNOT acts on this state, we will swap the amplitudes of $|01\rangle$ and $|11\rangle$, resulting in the state:

\begin{aligned} \text{CNOT}|{-}{+}\rangle & = \tfrac{1}{2}(|00\rangle - |01\rangle + |10\rangle - |11\rangle) \\ \text{CNOT}|{-}{+}\rangle & = |{-}{-}\rangle \end{aligned}

This is interesting, because it affects the state of the control qubit while leaving the state of the target qubit unchanged.

qc.cx(0,1)
qc.draw('mpl')

final_state = execute(qc,statevector_backend).result().get_statevector()
vector2latex(final_state, pretext="\\text{Statevector} = ", precision=1)

$$\text{Statevector} = \begin{bmatrix} 0.5 \\ -0.5 \\ -0.5 \\ 0.5\end{bmatrix}$$
plot_bloch_multivector(final_state)


If you remember the H-gate transforms $|{+}\rangle \rightarrow |0\rangle$ and $|{-}\rangle \rightarrow |1\rangle$, we can see that wrapping a CNOT in H-gates has the equivalent behaviour of a CNOT acting in the opposite direction:

We can verify this using Qiskit's unitary simulator:

qc = QuantumCircuit(2)
qc.h(0)
qc.h(1)
qc.cx(0,1)
qc.h(0)
qc.h(1)
display(qc.draw('mpl'))

unitary_backend = Aer.get_backend('unitary_simulator')
unitary = execute(qc,unitary_backend).result().get_unitary()
# In Jupyter Notebooks we can display this nicely using Latex.
# If not using Jupyter Notebooks you may need to remove the
# unitary2latex function and use print(unitary) instead.
from qiskit_textbook.tools import unitary2latex
unitary2latex(unitary, pretext="\\text{Circuit = }\n")

$$\text{Circuit = } \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ \end{bmatrix}$$
qc = QuantumCircuit(2)
qc.cx(1,0)
display(qc.draw('mpl'))

unitary_backend = Aer.get_backend('unitary_simulator')
unitary = execute(qc,unitary_backend).result().get_unitary()
unitary2latex(unitary, pretext="\\text{Circuit = }\n")

$$\text{Circuit = } \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ \end{bmatrix}$$

This identity is an example of phase kickback, which leads us neatly on to the next section...

## Phase Kickback

### Explaining the CNOT Circuit Identity

In the previous section we saw this identity:

This is an example of kickback (or, phase kickback ) which is very important and is used in almost every quantum algorithm. Kickback is where the global phase added by a gate to a qubit is ‘kicked back’ into a different qubit via a controlled operation. For example, we saw that performing an X-gate on a $|{-}\rangle$ qubit gives it the global phase $-1$:

$$X|{-}\rangle = -|{-}\rangle$$

When our control qubit is in either $|0\rangle$ or $|1\rangle$, this phase affects the whole state, however it is a global phase and is thus unobservable:

\begin{aligned} \text{CNOT}|{-}0\rangle & = |{-}\rangle \otimes |0\rangle \\ & = |{-}0\rangle \\ \quad & \\ \text{CNOT}|{-}1\rangle & = X|{-}\rangle \otimes |1\rangle \\ & = -|{-}\rangle \otimes |1\rangle \\ & = -|{-}1\rangle \\ \end{aligned}

The interesting effect is when our control qubit is in superposition, this global phase applies to the target qubit only when it is in the state $|1\rangle$, and this adds a relative phase to our control qubit:

\begin{aligned} \text{CNOT}|{-}{+}\rangle & = \tfrac{1}{\sqrt{2}}(\text{CNOT}|{-}0\rangle + \text{CNOT}|{-}1\rangle) \\ \text{CNOT}|{-}{+}\rangle & = \tfrac{1}{\sqrt{2}}(|{-}0\rangle + X|{-}1\rangle) \\ \text{CNOT}|{-}{+}\rangle & = \tfrac{1}{\sqrt{2}}(|{-}0\rangle -|{-}1\rangle) \\ \end{aligned}

This can then be written as the two separable qubit states:

\begin{aligned} \text{CNOT}|{-}{+}\rangle & = |{-}\rangle \otimes \tfrac{1}{\sqrt{2}}(|{0}\rangle - |1\rangle )\\ \text{CNOT}|{-}{+}\rangle & = |{-}{-}\rangle \\ \end{aligned}

Wrapping the CNOT in H-gates transforms the qubits from the computational basis to the $(|+\rangle, |-\rangle)$ basis, where we see this effect. This identity is very useful in hardware, since some hardwares only allow for CNOTs in one direction between two specific qubits. We can use this identity to overcome this problem and allow CNOTs in both directions.

### Kickback with the T-gate

Let’s look at another controlled operation, the controlled-T gate:

qc = QuantumCircuit(2)
qc.cu1(pi/4, 0, 1)
qc.draw('mpl')


The T-gate has the matrix:

$$\text{T} = \begin{bmatrix} 1 & 0 \\ 0 & e^{i\pi/4}\\ \end{bmatrix}$$

And the controlled-T gate has the matrix:

$$\text{Controlled-T} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & e^{i\pi/4}\\ \end{bmatrix}$$

We can verify this using Qiskit's unitary simulator:

unitary_backend = Aer.get_backend('unitary_simulator')
unitary = execute(qc,unitary_backend).result().get_unitary()
unitary2latex(unitary, pretext="\\text{Controlled-T} = \n")

$$\text{Controlled-T} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 0.70711+0.70711j \\ \end{bmatrix}$$

More generally, we can find the matrix of any controlled-U operation using the rule:

\begin{aligned} \text{U} & = \begin{bmatrix} u_{00} & u_{01} \\ u_{10} & u_{11}\\ \end{bmatrix} \\ \quad & \\ \text{Controlled-U} & = \begin{bmatrix} I & 0 \\ 0 & U\\ \end{bmatrix} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & u_{00} & u_{01} \\ 0 & 0 & u_{10} & u_{11}\\ \end{bmatrix} \end{aligned}

If we apply the T-gate to a qubit in the state $|1\rangle$, we add a phase of $e^{i\pi/4}$ to this qubit:

$$T|1\rangle = e^{i\pi/4}|1\rangle$$

This is global phase and is unobservable, but if we control this operation using another qubit in the $|{+}\rangle$ state, the phase is kicked-back and this changes the relative phase in our control qubit:

\begin{aligned} |1{+}\rangle & = |1\rangle \otimes \tfrac{1}{\sqrt{2}}(|0\rangle + |1\rangle) \\ & = \tfrac{1}{\sqrt{2}}(|10\rangle + |11\rangle) \\ & \\ \text{Controlled-T}|1{+}\rangle & = \tfrac{1}{\sqrt{2}}(|10\rangle + e^{i\pi/4}|11\rangle) \\ & \\ \text{Controlled-T}|1{+}\rangle & = |1\rangle \otimes \tfrac{1}{\sqrt{2}}(|0\rangle + e^{i\pi/4}|1\rangle) \end{aligned}

This has the effect of rotating our control qubit around the Z-axis of the Bloch sphere, while leaving the target qubit unchanged. Let's see this in Qiskit:

qc = QuantumCircuit(2)
qc.h(0)
qc.x(1)
# display() is a Jupyter Notebook command and might not
# work outside the notebooks, remove this command if you
# get an error.
display(qc.draw('mpl'))
final_state = execute(qc,statevector_backend).result().get_statevector()
plot_bloch_multivector(final_state)

qc.cu1(pi/4, 0, 1)
display(qc.draw('mpl'))
final_state = execute(qc,statevector_backend).result().get_statevector()
plot_bloch_multivector(final_state)