2.2. Unitary gates#

In the following section, all unitary gates that are implemented in geqo are introduced along with a simple example. Several of these gates support parameters, e.g. rotation angles. Note that the parameters are specified by names only, e.g. Phase("φ") denotes a phase gate with an angle, which has the name φ.

The following unitary gates are currently defined in geqo. They are basic gates, which means that they are internally not decomposed into a sequence of other gates.

  • BasicGate and InverseBasicGate

  • CNOT

  • Hadamard

  • PauliX, PauliY and PauliZ

  • PermuteQubits

  • Phase and InversePhase

  • Rx and InverseRx

  • Ry and InverseRy

  • Rz and InverseRz

  • Rzz and InverseRzz

  • SGate and InverseSGate

  • SwapQubits

2.2.1. BasicGate and InverseBasicGate#

The BasicGate and its inverse InverseBasicGate are useful for defining custom unitary matrices. The arguments are a name for the corresponding unitary matrix and the number of qubits, which the gates are acting on.

For most simulators, the method setValue must be used to assign a value to the name of the unitary matrix. In the following example, the SymPy based simulator simulatorUnitarySymPy is used and it supports symbolic values for the entries of a matrix.

import sympy as sym
from geqo.core.basic import BasicGate, InverseBasicGate
from geqo.simulators.sympy import simulatorUnitarySymPy

gate = BasicGate("a", 1)  # Create a unitary gate on one qubit with the name a.

sim = simulatorUnitarySymPy(1)
sim.setValue(
    "a", sym.Matrix([[1, 0], [0, sym.I]])
)  # Assign a matrix to the name of the unitary matrix.

sim.apply(gate, [0])
display(sim.u)
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 0\\0 & i\end{matrix}\right]\end{split}\]

The application of InverseBasicGate after BasicGate leads to the identity matrix. Since both gates have the same name for the unitary matrix, it is not necessary to use the setValue a second time.

gate2 = InverseBasicGate("a", 1)  # Create the inverse gate.

sim.apply(
    gate2, [0]
)  # Setting the value before calling this method is not necessary because it is already set.
display(sim.u)
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 0\\0 & 1\end{matrix}\right]\end{split}\]

2.2.2. CNOT#

The CNOT gate is a two-qubit gate and it follows the standard definition of CNOT, also called CX in some cases.

from geqo.gates.fundamental_gates import CNOT
from geqo.simulators.sympy import simulatorUnitarySymPy

gate = CNOT()  # Create a CNOT gate.

sim = simulatorUnitarySymPy(2)  # The gate operates on two qubits.

sim.apply(gate, [0, 1])  # Apply CNOT to two qubits.
display(sim.u)  # Show the resulting unitary matrix
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 0 & 0 & 0\\0 & 1 & 0 & 0\\0 & 0 & 0 & 1\\0 & 0 & 1 & 0\end{matrix}\right]\end{split}\]

2.2.3. Hadamard#

The Hadamard operation acts on a single qubit and it generates equal superpositions of the computation basis states \(|0\rangle\) and \(|1\rangle\) when applied to one of these. The Hadamard operation is its own inverse and effectively performs the quantum Fourier transform on a single qubit.

from geqo.gates.fundamental_gates import Hadamard
from geqo.simulators.sympy import simulatorUnitarySymPy

gate = Hadamard()  # Create a Hadamard gate.

sim = simulatorUnitarySymPy(1)  # The gate operates on one qubit.

sim.apply(gate, [0])
display(sim.u)  # Show the resulting unitary matrix
\[\begin{split}\displaystyle \left[\begin{matrix}\frac{\sqrt{2}}{2} & \frac{\sqrt{2}}{2}\\\frac{\sqrt{2}}{2} & - \frac{\sqrt{2}}{2}\end{matrix}\right]\end{split}\]

2.2.4. PauliX, PauliY and PauliZ#

These operators correspond to the Pauli matrices \(\sigma_x\), \(\sigma_y\) and \(\sigma_z\). They operate on a single qubit.

from geqo.gates.fundamental_gates import PauliX
from geqo.simulators.sympy import simulatorUnitarySymPy

gate = PauliX()  # Create a PauliX gate.
sim = simulatorUnitarySymPy(1)  # The gate operates on one qubit.
sim.apply(gate, [0])
display(sim.u)  # Show the resulting unitary matrix
\[\begin{split}\displaystyle \left[\begin{matrix}0 & 1\\1 & 0\end{matrix}\right]\end{split}\]
from geqo.gates.fundamental_gates import PauliY
from geqo.simulators.sympy import simulatorUnitarySymPy

gate = PauliY()  # Create a PauliY gate.
sim = simulatorUnitarySymPy(1)  # The gate operates on one qubit.
sim.apply(gate, [0])
display(sim.u)  # Show the resulting unitary matrix
\[\begin{split}\displaystyle \left[\begin{matrix}0 & - i\\i & 0\end{matrix}\right]\end{split}\]
from geqo.gates.fundamental_gates import PauliZ
from geqo.simulators.sympy import simulatorUnitarySymPy

gate = PauliZ()  # Create a PauliZ gate.
sim = simulatorUnitarySymPy(1)  # The gate operates on one qubit.
sim.apply(gate, [0])
display(sim.u)  # Show the resulting unitary matrix
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 0\\0 & -1\end{matrix}\right]\end{split}\]

2.2.5. PermuteQubits#

This operation allows to define a unitary operation that corresponds to a permutation of qubits. The argument is the target order of the permuted qubits, which are denoted by 0, …, n-1 for n qubits. For instance, the notation [1, 2, 0] means that the first qubit is mapped to the third position and the second qubit mapped to the first position, e.g. it is a cyclic permutation. The number of qubits is derived from the length of the list.

import sympy as sym
from geqo.algorithms.algorithms import PermuteQubits
from geqo.simulators.sympy import simulatorUnitarySymPy

gate = PermuteQubits(
    [1, 2, 0]
)  # Create a permutation corresponding to a cyclic left shift of qubits.
sim = simulatorUnitarySymPy(3)  # The gate is defined on three qubits.

sim.apply(gate, [0, 1, 2])
display(sim.u)
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\0 & 0 & 0 & 0 & 0 & 0 & 0 & 1\end{matrix}\right]\end{split}\]

2.2.6. Phase and InversePhase#

The phase gate applies a phase to the state \(|1\rangle\) of a qubit. Its argument is a name for the phase.

For most simulators, the method setValue must be used to assign a value to the name of the phase. In the following example, the SymPy based simulator simulatorUnitarySymPy is used and it supports symbolic values for the phase.

import sympy as sym
from geqo.gates.fundamental_gates import Phase
from geqo.simulators.sympy import simulatorUnitarySymPy

gate = Phase("φ")  # Create a phase gate with a phase with the name φ.

sim = simulatorUnitarySymPy(1)
sim.setValue(
    "φ", sym.sqrt(2)
)  # Set the value of the phase with the name φ to a symbolic value.

sim.apply(
    gate, [0]
)  # Setting the value before calling this method is not necessary because it is already set.
display(sim.u)
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 0\\0 & e^{\sqrt{2} i}\end{matrix}\right]\end{split}\]

2.2.7. Rx and InverseRx#

The Rx gate and its inverse are rotations around the x axis in the Bloch sphere representation of a qubit. The argument of these gates is a name for the rotation angle.

For most simulators, the method setValue must be used to assign a value to the name of the rotation. In the following example, the SymPy based simulator simulatorUnitarySymPy is used and it supports symbolic values for the phase.

import sympy as sym
from geqo.gates.rotation_gates import Rx, InverseRx
from geqo.simulators.sympy import simulatorUnitarySymPy

gate = Rx("φ")  # Create an Rx gate with an angle with the name φ.

sim = simulatorUnitarySymPy(1)
sim.setValue(
    "φ", sym.sqrt(2)
)  # Set the value of the rotation angle with the name φ to a symbolic value.

sim.apply(gate, [0])
display(sim.u)
\[\begin{split}\displaystyle \left[\begin{matrix}\cos{\left(\frac{\sqrt{2}}{2} \right)} & - i \sin{\left(\frac{\sqrt{2}}{2} \right)}\\- i \sin{\left(\frac{\sqrt{2}}{2} \right)} & \cos{\left(\frac{\sqrt{2}}{2} \right)}\end{matrix}\right]\end{split}\]

Applying the InverseRx after the Rx gate with the same angle leads to the identity.

gate2 = InverseRx("φ")  # Create an inverse Rx gate with an angle with the name φ.

sim.apply(
    gate2, [0]
)  # Setting the value before calling this method is not necessary because it is already set.
sim.u.simplify()  # Simplify the expressions in the result matrix.
display(sim.u)
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 0\\0 & 1\end{matrix}\right]\end{split}\]

2.2.8. Ry and InverseRy#

The Ry gate and its inverse are rotations around the y axis in the Bloch sphere representation of a qubit. The argument of these gates is a name for the rotation angle.

For most simulators, the method setValue must be used to assign a value to the name of the rotation. In the following example, the SymPy based simulator simulatorUnitarySymPy is used and it supports symbolic values for the phase.

import sympy as sym
from geqo.gates.rotation_gates import Ry, InverseRy
from geqo.simulators.sympy import simulatorUnitarySymPy

gate = Ry("φ")  # Create an Ry gate with an angle with the name φ.

sim = simulatorUnitarySymPy(1)
sim.setValue(
    "φ", sym.sqrt(2)
)  # Set the value of the rotation angle with the name φ to a symbolic value.

sim.apply(gate, [0])
display(sim.u)
\[\begin{split}\displaystyle \left[\begin{matrix}\cos{\left(\frac{\sqrt{2}}{2} \right)} & - \sin{\left(\frac{\sqrt{2}}{2} \right)}\\\sin{\left(\frac{\sqrt{2}}{2} \right)} & \cos{\left(\frac{\sqrt{2}}{2} \right)}\end{matrix}\right]\end{split}\]

Applying the InverseRy after the Ry gate with the same angle leads to the identity.

gate2 = InverseRy("φ")  # Create an inverse Ry gate with an angle with the name φ.

sim.apply(
    gate2, [0]
)  # Setting the value before calling this method is not necessary because it is already set.
sim.u.simplify()  # Simplify the expressions in the result matrix.
display(sim.u)
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 0\\0 & 1\end{matrix}\right]\end{split}\]

2.2.9. Rz and InverseRz#

The Rz gate and its inverse are rotations around the z axis in the Bloch sphere representation of a qubit. The argument of these gates is a name for the rotation angle.

For most simulators, the method setValue must be used to assign a value to the name of the rotation. In the following example, the SymPy based simulator simulatorUnitarySymPy is used and it supports symbolic values for the phase.

import sympy as sym
from geqo.gates.rotation_gates import Rz, InverseRz
from geqo.simulators.sympy import simulatorUnitarySymPy

gate = Rz("φ")  # Create an Rz gate with an angle with the name φ.

sim = simulatorUnitarySymPy(1)
sim.setValue(
    "φ", sym.sqrt(2)
)  # Set the value of the rotation angle with the name φ to a symbolic value.

sim.apply(gate, [0])
display(sim.u)
\[\begin{split}\displaystyle \left[\begin{matrix}e^{- \frac{\sqrt{2} i}{2}} & 0\\0 & e^{\frac{\sqrt{2} i}{2}}\end{matrix}\right]\end{split}\]

Applying the InverseRz after the Rz gate with the same angle leads to the identity.

gate2 = InverseRz("φ")  # Create an inverse Rz gate with an angle with the name φ.

sim.apply(
    gate2, [0]
)  # Setting the value before calling this method is not necessary because it is already set.
display(sim.u)
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 0\\0 & 1\end{matrix}\right]\end{split}\]

2.2.10. Rzz and InverseRzz#

The Rzz gate and its inverse are entangling gates on two qubits. The argument of the gates is the name of a rotation angle.

import sympy as sym
from geqo.gates.rotation_gates import Rzz, InverseRzz
from geqo.simulators.sympy import simulatorUnitarySymPy

gate = Rzz("φ")  # Create an Rzz gate with an angle with the name φ.

sim = simulatorUnitarySymPy(2)
sim.setValue(
    "φ", sym.sqrt(2)
)  # Set the value of the rotation angle with the name φ to a symbolic value.

sim.apply(gate, [0, 1])
display(sim.u)
\[\begin{split}\displaystyle \left[\begin{matrix}e^{- \frac{\sqrt{2} i}{2}} & 0 & 0 & 0\\0 & e^{\frac{\sqrt{2} i}{2}} & 0 & 0\\0 & 0 & e^{\frac{\sqrt{2} i}{2}} & 0\\0 & 0 & 0 & e^{- \frac{\sqrt{2} i}{2}}\end{matrix}\right]\end{split}\]

Applying the InverseRzz after the Rzz gate with the same angle leads to the identity.

gate2 = InverseRzz("φ")  # Create an inverse Rz gate with an angle with the name φ.
sim.apply(
    gate2, [0, 1]
)  # Setting the value before calling this method is not necessary because it is already set.
display(sim.u)
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 0 & 0 & 0\\0 & 1 & 0 & 0\\0 & 0 & 1 & 0\\0 & 0 & 0 & 1\end{matrix}\right]\end{split}\]

2.2.11. SGate and InverseSGate#

The S gate is the square root of the PauliZ gate.

import sympy as sym
from geqo.gates.fundamental_gates import SGate, InverseSGate
from geqo.simulators.sympy import simulatorUnitarySymPy

gate = SGate()  # Create an SGate.

sim = simulatorUnitarySymPy(1)

sim.apply(gate, [0])
display(sim.u)
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 0\\0 & i\end{matrix}\right]\end{split}\]

Applying the InverseSGate after the SGate gate leads to the identity.

gate2 = InverseSGate()  # Create an InverseSGate.
sim.apply(gate2, [0])
display(sim.u)
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 0\\0 & 1\end{matrix}\right]\end{split}\]

2.2.12. SwapQubits#

This operation acts on two qubits and it corresponds to exchanging two qubits.

import sympy as sym
from geqo.gates.fundamental_gates import SwapQubits
from geqo.simulators.sympy import simulatorUnitarySymPy

gate = SwapQubits()  # Create the SwapQubits gate

sim = simulatorUnitarySymPy(2)  # The operation acts on two qubits.
sim.apply(gate, [0, 1])
display(sim.u)  # Show the corresponding unitary operation.
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 0 & 0 & 0\\0 & 0 & 1 & 0\\0 & 1 & 0 & 0\\0 & 0 & 0 & 1\end{matrix}\right]\end{split}\]