CX gate
Contents
7.1. CX gate#
CX (Controlled-X), known also as CNOT
, is one of the most essential gates for quantum computation. It is known that any controlled gate can be realized by a combination of CX gates and one-qubit gates including X.
7.1.1. Definition#
Operational Definition
When gate CX\(_{q_0}^{q_1}\) acts on \(|q_1\, q_0\rangle\), X is applied to \(q_1\) if \(q_0=1\) and nothing is done otherwise.1 Qubit \(q_0\) serves as source and \(q_1\) as target. Mathematically, it is expressed as
Switching source and target qubits,
CX\(_{q_1}^{q_0}|q_1\, q_0\rangle\) means “Apply X to \(q_0\) if \(q_1=1\) and do nothing otherwise.” Mathematically, it is expressed as
Transformation
CX transforms computational basis as follows:
Matrix representation
which is default CX in Qiskit. In many literature, CX
corresponds to:
Don’t get confused. Our notation avoids the confusion.
The Qiskit circuit symbol is cx
and it appears in circuit as:
from qiskit import QuantumCircuit
qc=QuantumCircuit(2)
# CX_{q0,q1}
qc.cx(0,1)
# CX_{q1,q0}
qc.cx(1,0)
print(qc)
┌───┐
q_0: ──■──┤ X ├
┌─┴─┐└─┬─┘
q_1: ┤ X ├──■──
└───┘
7.1.2. Acting on superposition state#
CX\(_{q_0}^{q_1}\) swaps the coefficients of \(|01\rangle\) and \(|11\rangle\).
Similarly, CX\(_{q_1}^{q_0}\) swaps the coefficients of \(|10\rangle\) and \(|11\rangle\).
The following Qiskit example demonstrates it.
from qiskit import *
qr=QuantumRegister(2,'q')
qc=QuantumCircuit(qr)
qc.x([0,1])
qc.h([0,1])
qc.draw()
┌───┐┌───┐ q_0: ┤ X ├┤ H ├ ├───┤├───┤ q_1: ┤ X ├┤ H ├ └───┘└───┘
from qiskit.quantum_info import Statevector
# state before applying CX
print("State before applying CX.")
Statevector(qc).draw('latex')
State before applying CX.
qc.cx(0,1)
qc.draw()
┌───┐┌───┐ q_0: ┤ X ├┤ H ├──■── ├───┤├───┤┌─┴─┐ q_1: ┤ X ├┤ H ├┤ X ├ └───┘└───┘└───┘
# state after applyiong CX
print("State after applying CX.")
Statevector(qc).draw('latex')
State after applying CX.
Compare the states before and after applying CX
and find how the coefficients changed.
Source qubit not necessarily preserved by control gates
The definition of CX seems indicating that only the state of the target qubit is modified and the state of the source qubit remains the same. Surprisingly, that is not true. In some cases, the state of the source qubit also changes.
Let us look at the above example more carefully. The state before applying CX\(_{q_0}^{q_1}\) is
and after applying CX
\(_{q_0}^{q_1}\) ,
Notice that the source qubit is transformed from \(|-\rangle\) to \(|+\rangle\). This kind of transformation is called phase kickback since the phase of the source qubit changed. The phase kickback is a ubiquitous strategy in quantum algorithms. See the next subsection.
7.1.3. Phase kickback#
Let us look at the action of CX on z-basis \(|\pm\pm\rangle\). We assume that \(q_0\) is the control qubit and \(q_1\) is the target.
This transformation can be described as “Apply Z to \(q_0\) if \(q_1=-\) and do nothing otherwise.” Interestingly, now \(q_1\) is the control qubit and \(q_0\) is the target. The gate has not change and this operation is still “Apply X to \(q_1\) if \(q_0=1\) and do nothing otherwise.” The two operations are equivalent. The reversal of control-target relation is known as signature of phase kickback. Other controlled gates also show phase kickback.
Remembering that the Hadamard gate changes basis set from z-basis to x-basis and vice versa. Applying H gate before CX we can realize the phase kickback shown above. After the kickback, we can go back to z-basis by another H gate. Then, we have reversed CX.
In the following example, we start with an initial state \(|10\rangle\). In the first example, the basis is switched to x-basis by H gate and apply CX\(_{q_0}^{q_1}\). In the second computation, CX\(_{q_1}^{q_0}\) is directly applied. Both get the same result. Try other initial conditions and confirm that the two circuits do the same transformation.
from qiskit import *
qr = QuantumRegister(2,'q')
qc = QuantumCircuit(qr)
qc.x(1)
qc.barrier()
qc.h([0,1])
qc.cx(0,1)
qc.h([0,1])
qc.draw()
░ ┌───┐ ┌───┐ q_0: ──────░─┤ H ├──■──┤ H ├ ┌───┐ ░ ├───┤┌─┴─┐├───┤ q_1: ┤ X ├─░─┤ H ├┤ X ├┤ H ├ └───┘ ░ └───┘└───┘└───┘
from qiskit.quantum_info import Statevector
Statevector(qc).draw('latex')
However, this must be equivalent to
qc = QuantumCircuit(qr)
qc.x(1)
qc.barrier()
qc.cx(1,0)
qc.draw()
░ ┌───┐ q_0: ──────░─┤ X ├ ┌───┐ ░ └─┬─┘ q_1: ┤ X ├─░───■── └───┘ ░
Statevector(qc).draw('latex')
7.1.4. Change of basis#
Suppose that we want to flip \(q_1\) if \(q_0\) is \(|-\rangle\) and do nothing otherwise. We change the basis of \(q_0\) using H gate, apply CX\(_{q_0}^{q_1}\), and apply H again to \(q_0\). Note that the third step bring \(q_0\) back to the original state. We can generate superposition of Bell states using the circuit.
If we want to flip the target qubit also in x-basis, H gate is applied on \(q_1\) before and after CX. as well. However, this circuit is identical to just a single CX since it does the same transformation simply in a different basis.
The following example generates \(\frac{1}{\sqrt{2}} \left( \Phi^{-} + \Psi^{+} \right)\).
from qiskit import *
qr=QuantumRegister(2,'q')
qc=QuantumCircuit(qr)
qc.h(0)
qc.cx(0,1)
qc.h(0)
qc.draw()
┌───┐ ┌───┐ q_0: ┤ H ├──■──┤ H ├ └───┘┌─┴─┐└───┘ q_1: ─────┤ X ├───── └───┘
from qiskit.quantum_info import Statevector
Statevector(qc).draw('latex')
7.1.5. Simple applications#
As the above example shows, CX gate is used to create Bell states. In Section 8, many quantum strategies that use CX extensively will be discussed. Here are a few small applications.
Generating Bell states#
The No one-qubit gate can generate an entangled state. The CX gate is commonly used to generate entangled states from product states. As discussed in Section 6.1, Bell states play important roles in quantum computation. We can generate all Bell states by by applying CX_{q_0}^{q_1} \cdot H_{q_0}$ to the computational basis vectors.
The last one has unwanted phase “-”. We can get rid of it by applying Z on both qubits. In the following Qiskit code, we generate the singlet state \(|\Psi^{-}\).
Exercise 7.1.1 Generate \(|\Psi^{\pm}\rangle\) and visualize the results using Qiskit. (HINT: You can flip one of qubits by X gate.
from qiskit import *
from qiskit.quantum_info import Statevector
qr=QuantumRegister(2,'q')
qc=QuantumCircuit(qr)
# generate |11>
qc.x([0,1])
# apply CX*H
qc.h(0)
qc.cx(0,1)
# adjust phase
qc.z([0,1])
psi=Statevector(qc)
psi.draw('latex')
from qiskit.visualization import plot_state_qsphere
# it's an entangled state. Use qsphere.
plot_state_qsphere(psi,figsize=(4,4))

Exercise 7.3.1 Generate \(|\Phi^{\pm}\rangle\) and visualize the results using Qiskit.
Bell state measurement#
We can expand any two qubit states in the Bell basis as
Now, we want find the probabilities to find the Bell states. The process is called Bell measurement. After the measurement, the state collapses to a Bell state depending on the outcome of the measurement. The Bell measurement is commonly used in quantum information processes, such as quantum teleportation. However, the Bell measurement is not trivial. Any local measurement fails and thus the standard measurement based on the computational basis does not help.
In the example above, we transformed the computational basis vectors to the Bell states using CX\(_{q_0}^{q_1} \cdot\) H\(_{q_0}\). By inverting the transformation, we can convert the Bell states to the computational basis. That is applying H\(_{q_0} \cdot\) CX\(_{q_0}^{q_1}\) to the Bell states
Applying \((I \otimes H)\cdot CX\) to the superposition (7.2), we obtain
The resulting state is a superposition in computational basis but the expansion coefficients are the same as before the transformation. Now, the standard measurement in the computational basis determine the probabilities of finding the corresponding Bell state. The actual measurement collapses the state to one of computational basis vectors but the Bell measurement should result in one of the Bell state. The computational basis vector obtained from the measurement can be transformed back to the corresponding Bell state.
Addition modulo 2#
We want to compute modulo-2 addition of two bits \(x\) and \(y\). We write it \(x \otimes y\). Its truth table is
\(x\) |
\(y\) |
\(x\oplus y\) |
---|---|---|
0 |
0 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
1 |
1 |
0 |
In classical computer, XOR gate calculate it. Unlike classical XOR, quantum computation must be reversible. Thus we need to retain the values of \(x\) and \(y\). The out needs one qubit. In total, we need three qubits \(|z\,y\,x\rangle = |z\rangle \otimes |y\rangle \otimes |x\rangle\) where \(z\) contains \(x \oplus y\) at the end. We assume that \(z=0\) initially. We want to construct a quantum circuit which transforms \(|0\,y,x\rangle\) to \(|(x \oplus y)\, y\, x\rangle\). Writing it explicitly.
The second transformation can be done by CX\(_{q_0}^{q_1}\) and the third transformation can be done by a CX\(_{q_1}^{q_2}\). These two gates also works for the first transformation. However, it does not work for the last transformation. It takes more than one step. It would be nice if \(z\) is directly correlated to \(y\) or \(x\). Let us try CX\(_{q_0}^{q_1}\).
Now , the output \(z\) and \(y\) are perfectly correlated. We can now apply CX\(_{q_1}^{q_2}\) to find \(z\).
The value of \(z\) is correct but \(x\) and \(y\) are not preserved. That is not good. Next,we try to recover them by X\(_{q_0}^{q_1}\).
Now we have the desired output. Three CX gates calculate \(x \oplus y\).
from qiskit import *
from qiskit.quantum_info import Statevector
import numpy as np
# loop over all inputs
for x in range(2):
for y in range(2):
qr=QuantumRegister(3,'q')
qc=QuantumCircuit(qr)
# generate input state
if x==1:
qc.x(0)
if y==1:
qc.x(1)
# compute x \oplus y
qc.cx(0,1)
qc.cx(1,2)
qc.cx(0,1)
# extract output
psi=Statevector(qc).to_dict()
psi=list(psi.keys())[0]
print("x={0:d}, y={1:d}, x+y={2:s}".format(x,y,psi[0]))
x=0, y=0, x+y=0
x=0, y=1, x+y=1
x=1, y=0, x+y=1
x=1, y=1, x+y=0
Long distance CX#
Constructing devices with controlled gates is not trivial at all. Controlled gates can be directly applied on two qubits if they are close to each other. Let us consider a linear chain of qubits and assume that CX can be applied only on the nearest neighbor pair. For example, a device allows CX on pairs \((q_0, q_1)\) and \((q_1, q_2)\) but not on \((q_0, q_2)\). What can we do if we want to use CX on \((q_0,q_2)\)? Fortunately, we can create a circuit that acts like CX over a distance. Let us create a Bell state between \(q_0\) and \(q_2\) using CX\(_{q_0}^{q_2} \cdot\) H\(_{q_0}\). In the following Qiskit example, CX\(_{q_0}^{q_2}\) is realized by four CX on the nearest neighbors.
from qiskit import *
qr=QuantumRegister(3,'q')
qc=QuantumCircuit(qr)
qc.h(0)
qc.cx(0,1)
qc.cx(1,2)
qc.cx(0,1)
qc.cx(1,2)
qc.draw()
┌───┐ q_0: ┤ H ├──■─────────■─────── └───┘┌─┴─┐ ┌─┴─┐ q_1: ─────┤ X ├──■──┤ X ├──■── └───┘┌─┴─┐└───┘┌─┴─┐ q_2: ──────────┤ X ├─────┤ X ├ └───┘ └───┘
from qiskit.quantum_info import Statevector, partial_trace
fullpsi=Statevector(qc)
fullpsi.draw('latex')
Note that \(q_1\) is still 0 but \(q_0\) and \(q_2\) are entangled. Removing the middle qubit, we should have Bell state \(|\Phi^{+}\rangle\).
import numpy as np
rho = partial_trace(fullpsi, [1])
psi = Statevector(np.sqrt(np.diagonal(rho)))
psi.draw('latex')
The above circuit is equivalent to the following with a long distance CX.
qr=QuantumRegister(3,'q')
qc=QuantumCircuit(qr)
qc.h(0)
qc.cx(0,2)
qc.draw()
┌───┐ q_0: ┤ H ├──■── └───┘ │ q_1: ───────┼── ┌─┴─┐ q_2: ─────┤ X ├ └───┘
One-way information transfer#
Qubit \(q_0\) is in an arbitrary state \(|\psi\rangle\) and another qubit \(q_1\) in a reset state \(|0\rangle\). We can transfer the state of \(q_0\) to \(q_1\) using CX gates. However, the state of \(q_0\) is necessarily destroyed due to the no-cloning theorem. In the following Qiskit example, \(q_0\) is initially in a superposition state \(\cos(\theta) | 0\rangle + \sin(\theta)|1\rangle\) and \(q_1\) in \(|0\rangle\). Thus the total state vector is initially
After transformation, \(q_0\) is \(|0\rangle\) and \(q_1\) is now in the superposition state, The outcome should be
This method works only when \(q_1\) is initially in \(|0\rangle\).
from qiskit import *
from qiskit.quantum_info import Statevector
qr=QuantumRegister(2,'q')
qc=QuantumCircuit(qr)
# Generate a state to be transfer
theta=np.pi/3
phi=0.0
qc.u(theta,phi,0,0)
psi0=Statevector(qc)
qc.barrier()
# transfer the state
qc.cx(0,1)
qc.cx(1,0)
psi1=Statevector(qc)
print("Initial state")
psi0.draw('latex')
Initial state
The above equation is a product state \(|0\rangle \otimes \left(\frac{\sqrt{3}}{2}|0\rangle + \frac{1}{2}|1\rangle\right)\).
print("Final state")
psi1.draw('latex')
Final state
Writing the above state in a product form, \(\left(\frac{\sqrt{3}}{2}|0\rangle + \frac{1}{2}|1\rangle\right) \otimes |0\rangle\). Notice that the superposition state moved from a quibit to the other.
Swapping qubits#
Inthe previous example, the information is trasferred in one direction from \(q_0\) to \(q_1\). The SWAP operation transfers information in both direction. Swap gate SWAP is defined by SWAP\(|q_1\, q_0\rangle = |q_0\, q_1\rangle\). When it acts on a superposition state, the coefficients of \(|01\rangle\) and \(|10\rangle\) is swapped.
Recall that CX\(_{q_0}^{q_1}\) swaps the coefficients of \(|01\rangle\) and \(|11\rangle\), and CX\(_{q_1}^{q_0}\) swaps the coefficients of \(|10\rangle\) and \(|11\rangle\). Using these gates in series, we can create SWAP.
which indicates SWAP=\(\text{CX}_{q_0}^{q_1} \cdot \text{CX}_{q_1}^{q_0} \cdot \text{CX}_{q_0}^{q_1}\). (See the following circuit.)
from qiskit import *
qr=QuantumRegister(2,'q')
qc=QuantumCircuit(qr)
qc.cx(0,1)
qc.cx(1,0)
qc.cx(0,1)
print("sawpping qubits")
qc.draw()
sawpping qubits
┌───┐ q_0: ──■──┤ X ├──■── ┌─┴─┐└─┬─┘┌─┴─┐ q_1: ┤ X ├──■──┤ X ├ └───┘ └───┘
from qiskit import *
from qiskit.quantum_info import Statevector
import numpy as np
qr=QuantumRegister(2,'q')
qc=QuantumCircuit(qr)
# randomly oriented qubits
a=np.pi*np.random.rand()
b=np.pi*np.random.rand()
c=np.pi*np.random.rand()
qc.u(a,b,c,0)
a=np.pi*np.random.rand()
b=np.pi*np.random.rand()
c=np.pi*np.random.rand()
qc.u(a,b,c,1)
# get the statevector in dict format
psi=Statevector(qc).to_dict()
# evaluate of probabilities
p0 = dict()
for k in psi:
p0[k]=abs(psi[k])**2
from qiskit.visualization import plot_histogram
print("probability distribution before swap")
plot_histogram(p0)
probability distribution before swap

qc.cx(0,1)
qc.cx(1,0)
qc.cx(0,1)
psi=Statevector(qc).to_dict()
p1 = dict()
for k in psi:
p1[k]=abs(psi[k])**2
print("probability distribution after swapping.")
plot_histogram(p1)
probability distribution after swapping.

Last Modified on 08/22/2022.
- 1
The notation
CX
\(_{q_0}^{q_1}\) is not commonly used. In many literature, simplyCX
is used without specifying which qubit is source, causing confusion. If you are confused, see the corresponding circuit diagram.