M.E. Irizarry-Gelpí

Physics impostor. Mathematics interloper. Husband. Father.

SWAP and CNOT Gates


In this post I would like to record a relation between SWAP and controlled-NOT gates. Make sure you import NumPy first:

import numpy as np

First, consider a single qubit. Unitary 2×2 matrices act on the state vector. The two permutation matrices that can act on this state are

I2 = np.matrix([
    [1, 0],
    [0, 1],
])

NOT2_1 = np.matrix([
    [0, 1],
    [1, 0],
])

Next, consider two qubits. Unitary 4×4 matrices act on the state vector. A priori, there are 4! = 24 distinct permutation matrices. Four of them are related to NOT and controlled-NOT gates. There are two NOT gates:

NOT4_1 = np.kron(NOT2_1, I2)
NOT4_2 = np.kron(I2, NOT2_1)

Explicitly:

print(NOT4_1)

[[0 0 1 0]
 [0 0 0 1]
 [1 0 0 0]
 [0 1 0 0]]

print(NOT4_2)

[[0 1 0 0]
 [1 0 0 0]
 [0 0 0 1]
 [0 0 1 0]]

There are also two controlled-NOT gates:

CNOT4_12 = np.matrix([
    [1, 0, 0, 0],
    [0, 1, 0, 0],
    [0, 0, 0, 1],
    [0, 0, 1, 0],
])

CNOT4_21 = np.matrix([
    [1, 0, 0, 0],
    [0, 0, 0, 1],
    [0, 0, 1, 0],
    [0, 1, 0, 0],
])

You can have one two-qubit SWAP gate:

SWAP4_12 = np.matrix([
    [1, 0, 0, 0],
    [0, 0, 1, 0],
    [0, 1, 0, 0],
    [0, 0, 0, 1],
])

However, this SWAP gate can be re-written in terms of controlled-NOT gates:

np.all(np.equal(SWAP4_12, CNOT4_12 @ CNOT4_21 @ CNOT4_12))
True

np.all(np.equal(SWAP4_12, CNOT4_21 @ CNOT4_12 @ CNOT4_21))
True

Another permutation gate is the tensor product of two NOT gates:

print(np.kron(NOT2_1, NOT2_1))

[[0 0 0 1]
 [0 0 1 0]
 [0 1 0 0]
 [1 0 0 0]]

Note that all of the permutation gates introduced so far square to the identity. This is not true for all permutation gates. Indeed, the following gate does not square to the identity

G4 = np.matrix([
    [0, 0, 1, 0],
    [1, 0, 0, 0],
    [0, 1, 0, 0],
    [0, 0, 0, 1],
])

print(G4 @ G4)

[[0 1 0 0]
 [0 0 1 0]
 [1 0 0 0]
 [0 0 0 1]]

Finally, consider three qubits. Unitary 8×8 matrices act on the state vector. There are three pair of qubits, so there can be six controlled-NOT gates. Consider two such gates:

CNOT8_12 = np.kron(CNOT4_12, I2)

CNOT8_21 = np.kron(CNOT4_21, I2)

Again, you can use these two controlled-NOT gates to produce a SWAP gate:

print(CNOT8_12 @ CNOT8_21 @ CNOT8_12)

[[1 0 0 0 0 0 0 0]
 [0 1 0 0 0 0 0 0]
 [0 0 0 0 1 0 0 0]
 [0 0 0 0 0 1 0 0]
 [0 0 1 0 0 0 0 0]
 [0 0 0 1 0 0 0 0]
 [0 0 0 0 0 0 1 0]
 [0 0 0 0 0 0 0 1]]

The resultant gate swaps the first and second qubits.

I would like to conclude with a notation proposal. The 2-dimensional NOT gate can be denoted with the symbol \(\operatorname{NOT}_{2}(1)\). Or, more simply \(\operatorname{NOT}_{2}\). The two 4-dimensional NOT gates can be denoted by \(\operatorname{NOT}_{4}(1)\) and \(\operatorname{NOT}_{4}(2)\). Here, the number inside the parenthesis denotes which of the two qubits the NOT gate is acting on. The two 4-dimensional controlled-NOT gates can be denoted by \(\operatorname{CNOT}_{4}(1, 2)\) and \(\operatorname{CNOT}_{4}(2, 1)\). Here the first number inside the parenthesis denotes the control qubit, and the second denotes the target qubit. The 4-dimensional SWAP gate can be denoted by \(\operatorname{SWAP}_{4}(1, 2)\). With this notation the 8-dimensional gates can be written in a similar way. There are three NOT gates:

\begin{align*} \operatorname{NOT}_{8}&(1), & \operatorname{NOT}_{8}&(2), & \operatorname{NOT}_{8}&(3) \end{align*}

There are six controlled-NOT gates:

\begin{align*} \operatorname{CNOT}_{8}&(1, 2), & \operatorname{CNOT}_{8}&(1, 3), & \operatorname{CNOT}_{8}&(2, 1), & \operatorname{CNOT}_{8}&(2, 3), & \operatorname{CNOT}_{8}&(3, 1), & \operatorname{CNOT}_{8}&(3, 2) \end{align*}

And there are three SWAP gates:

\begin{align*} \operatorname{SWAP}_{8}&(1, 2), & \operatorname{SWAP}_{8}&(1, 3), & \operatorname{SWAP}_{8}&(2, 3) \end{align*}

Of course, there are many more permutation gates.