GaterAid Documentation

“I fear not the man who has applied two gates to two thousand qubits. The man who has applied two thousand gates to two qubits, I fear.”

—S. Tzu, Ph.D

The GaterAid package provides a simple and intuitive interface for applying common and customisable two-qubit gates to quantum states.

See our example tutorial notebook for a quickstart (or download it).

The repository for GaterAid can be found on GitHub and the package contains the following modules:

  • controlled_gates: provides a selection of common controlled gates, as well as simple cutomisation for controlled gates.

  • gate_base: provides the base class for all two-qubit gates.

  • other_gates: provides a selection of common two-qubit gates, as well as simple customisation for local and general gates.

  • quantum_state: provides the base class for all two-qubit quantum states.

  • utilities: provides utility functions for the package.

For detailed documentation and examples, navigate the contents below. In particular, find examples of how to use GaterAid detailed in the Examples section below, or alternatively download the Jupyter notebook example.ipynb from the repository.

Installation

GaterAid is hosted on the PyPi package index. Use pip to install GaterAid:

pip install GaterAid

Examples

To use GaterAid, import it in your Python code:

>>> from gateraid import *
>>> import numpy as np

Quantum States

GaterAid represents quantum states as numpy arrays in the QuantumState class. For example, the state \(q:=\frac{1}{\sqrt{2}}(|00\rangle + |11\rangle)\) is represented as:

>>> q = QuantumState(np.array([1, 0, 0, 1])/np.sqrt(2))
>>> print(q)
State:
0.7071+0j|00> +
0+0j|01> +
0+0j|10> +
0.7071+0j|11>

Common Two-Qubit Gates

In the other_gates module, GaterAid provides a selection of common two-qubit gates. For example, we can define the controlled-NOT gate (CNOT 1->2) as follows,

>>> CX = make_CX()
>>> print(CX)
CX1->2 gate with matrix:
[[1+0j 0+0j 0+0j 0+0j]
 [0+0j 1+0j 0+0j 0+0j]
 [0+0j 0+0j 0+0j 1+0j]
 [0+0j 0+0j 1+0j 0+0j]]

We can apply this gate on our state \(q\) as follows,

>>> CX(q)
>>> print(q)
State:
0.7071+0j|00> +
0+0j|01> +
0+0j|10> +
0.7071+0j|11>

giving us the expected outcome: \(\frac{1}{\sqrt{2}}(|00\rangle + |11\rangle) \mapsto \frac{1}{\sqrt{2}}(|00\rangle + |10\rangle)\). Similarly we can define the reversed anti-controlled phase gate (anti-CZ 2->1) and the SWAP gate (SWAP).

>>> CZ = make_CZ(first_qubit_controlled=False, anti-control=True)
>>> SWAP = make_SWAP()

Each of these can be applied to our state in succesion:

>>> CZ(q)
>>> print(q)
State:
0.7071+0j|00> +
0+0j|01> +
-0.7071+0j|10> +
0+0j|11>
>>> SWAP(q)
>>> print(q)
State:
0.7071+0j|00> +
-0.7071+0j|01> +
0+0j|10> +
0+0j|11>

More common gates can be found in the other_gates module.

General and Customisable Two-Qubit Gates

GaterAid also provides classes for general two-qubit gates and allows simple customisation for controlled gates and local gates. For example, we can define a NOT gate acting on only the first qubit (XI) as follows,

>>> X = make_X()
>>> XI = LocalUnitary(X, 'X', 0)
>>> print(XI)
X_I gate with matrix:
[[0+0j 0+0j 1+0j 0+0j]
 [0+0j 0+0j 0+0j 1+0j]
 [1+0j 0+0j 0+0j 0+0j]
 [0+0j 1+0j 0+0j 0+0j]]

Then after re-initialising our state, we can apply this gate,

>>> q = QuantumState(np.array([1, 1, 0, 0])/np.sqrt(2))
>>> XI(q)
>>> print(q)
State:
0+0j|00> +
0+0j|01> +
0.7071+0j|10> +
0.7071+0j|11>

Alternatively, we can define a controlled-Hadamard gate,

>>> q = QuantumState(np.array([0, 0, 1, 1])/np.sqrt(2))
>>> H = make_H()
>>> CH = ControlledUnitary(H, 'H')
>>> CH(q)
>>> print(q)
State:
0+0j|00> +
0+0j|01> +
1+0j|10> +
0+0j|11>

Finally, an arbitrary two-qubit gate can be defined in terms of the two-qubit Pauli operators \(P_i\) and real coefficients \(c_i\), as

\[U = \exp(i\sum_i c_i P_i)\]

Then for example, we can generate a random unitary gate as follows,

>>> coef = np.random.rand(16)
>>> pauli_dict = {'II': coef[0], 'IX': coef[1], 'IY': coef[2], 'IZ': coef[3], 'XI': coef[4], 'XX': coef[5], 'XY': coef[6], 'XZ': coef[7], 'YI': coef[8], 'YX': coef[9], 'YY': coef[10], 'YZ': coef[11], 'ZI': coef[12], 'ZX': coef[13], 'ZY': coef[14], 'ZZ': coef[15]}
>>> U = GeneralUnitary(pauli_dict)
>>> print(U(q))
State:
-0.4403+0.4553j|00> +
0.0794-0.1427j|01> +
0.5642-0.1816j|10> +
0.03915-0.4683j|11>

giving us a random final state. More detail of general and customisable gates can be found in other_gates and controlled_gates modules. Enjoy!