Skip to content

Commit

Permalink
[CQT-127] Allow Qubit to accept QubitLike (#325)
Browse files Browse the repository at this point in the history
* [CQT 126] Verify dataclasses using __post_init__ (#319) (#321)

* Verify dataclasses using __post_init__

* Restructure tests and remove try-exceptions

* Fix typos

---------

Co-authored-by: Juan Boschero <[email protected]>

* [CQT-127] Allow Qubit to accept QubitLike

* Accept QubitLike throughout opensquirrel

* Happify ruff

* [CQT 126] Verify dataclasses using __post_init__ (#319) (#321)

* Verify dataclasses using __post_init__

* Restructure tests and remove try-exceptions

* Fix typos

---------

Co-authored-by: Juan Boschero <[email protected]>

* [CQT-127] Allow Qubit to accept QubitLike

* Accept QubitLike throughout opensquirrel

* Happify ruff

* Implement QubitLike

* Happify ruff

* Happify mypy

* Docstrings

* Happify ruff

* Fix for python39

* typo

* Remove redundend Qubit from OS

* Removed redundant Qubit statements from tutorial

* Removed redundant Qubit statements from example

* Fix named measurements and reset

* Removed redundant Qubit statements from tests

* Resolve initial conflicts

* Fix tests

* Happify mypy

* Fix py3.9

* Fix CRk test errors

* Remove excessive Qubit wrappers

* Happify ruff

* Restrict QubitLike internally

* Happify ruff

* Replace annotation finding with method

---------

Co-authored-by: Juan Boschero <[email protected]>
Co-authored-by: Stan van der Linde <[email protected]>
  • Loading branch information
3 people authored Oct 8, 2024
1 parent a6ca70c commit 4d76942
Show file tree
Hide file tree
Showing 37 changed files with 703 additions and 816 deletions.
48 changes: 17 additions & 31 deletions docs/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ For creation of a circuit through Python, the `CircuitBuilder` can be used accor

```python
from opensquirrel import CircuitBuilder
from opensquirrel.ir import Qubit, Float
from opensquirrel.ir import Float

builder = CircuitBuilder(qubit_register_size=2)
builder.Ry(Qubit(0), Float(0.23)).CNOT(Qubit(0),Qubit(1))
builder.Ry(0, Float(0.23)).CNOT(0, 1)
qc = builder.to_circuit()

print(qc)
Expand All @@ -87,7 +87,7 @@ You can naturally use the functionalities available in Python to create your cir
```python
builder = CircuitBuilder(qubit_register_size=10)
for i in range(0, 10, 2):
builder.H(Qubit(i))
builder.H(i)
qc = builder.to_circuit()

print(qc)
Expand All @@ -110,9 +110,9 @@ For instance, you can generate a quantum fourier transform (QFT) circuit as foll
qubit_register_size = 5
builder = CircuitBuilder(qubit_register_size)
for i in range(qubit_register_size):
builder.H(Qubit(i))
builder.H(i)
for c in range(i + 1, qubit_register_size):
builder.CRk(Qubit(c), Qubit(i), c-i+1)
builder.CRk(c, i, c-i+1)
qft = builder.to_circuit()

print(qft)
Expand Down Expand Up @@ -161,18 +161,6 @@ _Output_:
Parsing error: failed to resolve overload for cnot with argument pack (qubit, int)

The issue is that the CNOT expects a qubit as second input argument where an integer has been provided.
The same holds for the `CircuitBuilder`, _i.e._,
it also throws an error if arguments are passed of an unexpected type:

```python
try:
CircuitBuilder(qubit_register_size=2).CNOT(Qubit(0), 3)
except Exception as e:
print(e)
```
_Output_:

TypeError: wrong argument type for instruction `CNOT`, got <class 'int'> but expected Qubit

## Modifying a circuit

Expand All @@ -198,7 +186,7 @@ import math

builder = CircuitBuilder(1)
for _ in range(4):
builder.Rx(Qubit(0), Float(math.pi / 4))
builder.Rx(0, Float(math.pi / 4))
qc = builder.to_circuit()

qc.merge_single_qubit_gates()
Expand Down Expand Up @@ -235,7 +223,7 @@ Below is shown how the X-gate is defined in the default gate set of OpenSquirrel

```python
@named_gate
def x(q: Qubit) -> Gate:
def x(q: QubitLike) -> Gate:
return BlochSphereRotation(qubit=q, axis=(1, 0, 0), angle=math.pi, phase=math.pi / 2)
```

Expand All @@ -248,24 +236,22 @@ For instance, the CNOT gate is defined in the default gate set of OpenSquirrel a

```python
@named_gate
def cnot(control: Qubit, target: Qubit) -> Gate:
def cnot(control: QubitLike, target: QubitLike) -> Gate:
return ControlledGate(control, x(target))
```

- The `MatrixGate` class may be used to define a gate in the generic form of a matrix:

```python
@named_gate
def swap(q1: Qubit, q2: Qubit) -> Gate:
def swap(q1: QubitLike, q2: QubitLike) -> Gate:
return MatrixGate(
np.array(
[
[1, 0, 0, 0],
[0, 0, 1, 0],
[0, 1, 0, 0],
[0, 0, 0, 1],
]
),
[
[1, 0, 0, 0],
[0, 0, 1, 0],
[0, 1, 0, 0],
[0, 0, 0, 1],
],
[q1, q2],
)
```
Expand Down Expand Up @@ -377,7 +363,7 @@ an example can be seen below where a Hadamard, Z, Y and Rx gate are all decompos
from opensquirrel.decomposer.aba_decomposer import ZYZDecomposer

builder = CircuitBuilder(qubit_register_size=1)
builder.H(Qubit(0)).Z(Qubit(0)).Y(Qubit(0)).Rx(Qubit(0), Float(math.pi / 3))
builder.H(0).Z(0).Y(0).Rx(0, Float(math.pi / 3))
qc = builder.to_circuit()

qc.decompose(decomposer=ZYZDecomposer())
Expand All @@ -404,7 +390,7 @@ Similarly, the decomposer can be used on individual gates.
from opensquirrel.decomposer.aba_decomposer import XZXDecomposer
from opensquirrel.default_gates import H

print(ZYZDecomposer().decompose(H(Qubit(0))))
print(ZYZDecomposer().decompose(H(0)))
```
_Output_:

Expand Down
98 changes: 67 additions & 31 deletions example/decompositions.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,14 @@
"import math\n",
"\n",
"from opensquirrel.circuit_builder import CircuitBuilder\n",
"from opensquirrel.ir import Float, Qubit\n",
"from opensquirrel.ir import Float\n",
"\n",
"# Build the circuit structure using the CircuitBuilder\n",
"builder = CircuitBuilder(qubit_register_size=1)\n",
"builder.H(Qubit(0))\n",
"builder.Z(Qubit(0))\n",
"builder.Y(Qubit(0))\n",
"builder.Rx(Qubit(0), Float(math.pi / 3))\n",
"builder.H(0)\n",
"builder.Z(0)\n",
"builder.Y(0)\n",
"builder.Rx(0, Float(math.pi / 3))\n",
"\n",
"# Create a new circuit from the constructed structure\n",
"circuit = builder.to_circuit()\n",
Expand All @@ -81,7 +81,9 @@
"cell_type": "markdown",
"id": "afe111839ab98b22",
"metadata": {},
"source": "Above we can see the current gates in our circuit. Having created a circuit, we can now use an ABA decomposition in `opensquirrel.decomposer.aba_decomposer` to decompose the gates in the circuit. For this example, we will apply the Z-Y-Z decomposition using the `ZYZDecomposer`. "
"source": [
"Above we can see the current gates in our circuit. Having created a circuit, we can now use an ABA decomposition in `opensquirrel.decomposer.aba_decomposer` to decompose the gates in the circuit. For this example, we will apply the Z-Y-Z decomposition using the `ZYZDecomposer`. "
]
},
{
"cell_type": "code",
Expand Down Expand Up @@ -172,7 +174,7 @@
"from opensquirrel.decomposer.aba_decomposer import XZXDecomposer\n",
"from opensquirrel.default_gates import H\n",
"\n",
"XZXDecomposer().decompose(H(Qubit(0)))"
"XZXDecomposer().decompose(H(0))"
]
},
{
Expand Down Expand Up @@ -226,10 +228,10 @@
"source": [
"# Build the circuit structure using the CircuitBuilder\n",
"builder = CircuitBuilder(qubit_register_size=1)\n",
"builder.H(Qubit(0))\n",
"builder.Z(Qubit(0))\n",
"builder.X(Qubit(0))\n",
"builder.Rx(Qubit(0), Float(math.pi / 3))\n",
"builder.H(0)\n",
"builder.Z(0)\n",
"builder.X(0)\n",
"builder.Rx(0, Float(math.pi / 3))\n",
"\n",
"# Create a new circuit from the constructed structure\n",
"circuit = builder.to_circuit()\n",
Expand All @@ -240,7 +242,9 @@
"cell_type": "markdown",
"id": "69517d8287c1777d",
"metadata": {},
"source": "The `McKayDecomposer` is called from `opensquirrel.decomposer.mckay_decomposer` and used in a similar method to the `ABADecomposer`."
"source": [
"The `McKayDecomposer` is called from `opensquirrel.decomposer.mckay_decomposer` and used in a similar method to the `ABADecomposer`."
]
},
{
"cell_type": "code",
Expand Down Expand Up @@ -358,9 +362,9 @@
"source": [
"# Build the circuit structure using the CircuitBuilder\n",
"builder = CircuitBuilder(qubit_register_size=2)\n",
"builder.CZ(Qubit(0), Qubit(1))\n",
"builder.CR(Qubit(0), Qubit(1), Float(math.pi / 3))\n",
"builder.CR(Qubit(1), Qubit(0), Float(math.pi / 2))\n",
"builder.CZ(0, 1)\n",
"builder.CR(0, 1, Float(math.pi / 3))\n",
"builder.CR(1, 0, Float(math.pi / 2))\n",
"\n",
"# Create a new circuit from the constructed structure\n",
"circuit = builder.to_circuit()\n",
Expand All @@ -371,7 +375,9 @@
"cell_type": "markdown",
"id": "8c945ab4572e9f77",
"metadata": {},
"source": "We can import `CNOTDecomposer` from `opensquirrel.decomposer.cnot_decomposer`. The above circuit can then be decomposed using `CNOTDecomposer` as seen below."
"source": [
"We can import `CNOTDecomposer` from `opensquirrel.decomposer.cnot_decomposer`. The above circuit can then be decomposed using `CNOTDecomposer` as seen below."
]
},
{
"cell_type": "code",
Expand Down Expand Up @@ -475,7 +481,9 @@
"outputs": [
{
"data": {
"text/latex": "$\\displaystyle \\cos{\\left(\\frac{\\theta_{2}}{2} \\right)} \\cos{\\left(\\frac{\\theta_{1} + \\theta_{3}}{2} \\right)} + - \\sin{\\left(\\frac{\\theta_{2}}{2} \\right)} \\sin{\\left(\\frac{\\theta_{1} - \\theta_{3}}{2} \\right)} i + \\sin{\\left(\\frac{\\theta_{2}}{2} \\right)} \\cos{\\left(\\frac{\\theta_{1} - \\theta_{3}}{2} \\right)} j + \\sin{\\left(\\frac{\\theta_{1} + \\theta_{3}}{2} \\right)} \\cos{\\left(\\frac{\\theta_{2}}{2} \\right)} k$",
"text/latex": [
"$\\displaystyle \\cos{\\left(\\frac{\\theta_{2}}{2} \\right)} \\cos{\\left(\\frac{\\theta_{1} + \\theta_{3}}{2} \\right)} + - \\sin{\\left(\\frac{\\theta_{2}}{2} \\right)} \\sin{\\left(\\frac{\\theta_{1} - \\theta_{3}}{2} \\right)} i + \\sin{\\left(\\frac{\\theta_{2}}{2} \\right)} \\cos{\\left(\\frac{\\theta_{1} - \\theta_{3}}{2} \\right)} j + \\sin{\\left(\\frac{\\theta_{1} + \\theta_{3}}{2} \\right)} \\cos{\\left(\\frac{\\theta_{2}}{2} \\right)} k$"
],
"text/plain": [
"cos(theta_2/2)*cos((theta_1 + theta_3)/2) + (-sin(theta_2/2)*sin((theta_1 - theta_3)/2))*i + sin(theta_2/2)*cos((theta_1 - theta_3)/2)*j + sin((theta_1 + theta_3)/2)*cos(theta_2/2)*k"
]
Expand All @@ -502,7 +510,9 @@
"cell_type": "markdown",
"id": "6e776f2cb7cae465",
"metadata": {},
"source": "Let us change variables and define $p\\equiv\\theta_1 + \\theta_3$ and $m\\equiv\\theta_1 - \\theta_3$."
"source": [
"Let us change variables and define $p\\equiv\\theta_1 + \\theta_3$ and $m\\equiv\\theta_1 - \\theta_3$."
]
},
{
"cell_type": "code",
Expand All @@ -517,7 +527,9 @@
"outputs": [
{
"data": {
"text/latex": "$\\displaystyle \\cos{\\left(\\frac{p}{2} \\right)} \\cos{\\left(\\frac{\\theta_{2}}{2} \\right)} + - \\sin{\\left(\\frac{m}{2} \\right)} \\sin{\\left(\\frac{\\theta_{2}}{2} \\right)} i + \\sin{\\left(\\frac{\\theta_{2}}{2} \\right)} \\cos{\\left(\\frac{m}{2} \\right)} j + \\sin{\\left(\\frac{p}{2} \\right)} \\cos{\\left(\\frac{\\theta_{2}}{2} \\right)} k$",
"text/latex": [
"$\\displaystyle \\cos{\\left(\\frac{p}{2} \\right)} \\cos{\\left(\\frac{\\theta_{2}}{2} \\right)} + - \\sin{\\left(\\frac{m}{2} \\right)} \\sin{\\left(\\frac{\\theta_{2}}{2} \\right)} i + \\sin{\\left(\\frac{\\theta_{2}}{2} \\right)} \\cos{\\left(\\frac{m}{2} \\right)} j + \\sin{\\left(\\frac{p}{2} \\right)} \\cos{\\left(\\frac{\\theta_{2}}{2} \\right)} k$"
],
"text/plain": [
"cos(p/2)*cos(theta_2/2) + (-sin(m/2)*sin(theta_2/2))*i + sin(theta_2/2)*cos(m/2)*j + sin(p/2)*cos(theta_2/2)*k"
]
Expand All @@ -539,7 +551,9 @@
"cell_type": "markdown",
"id": "1ece3d36eb99512b",
"metadata": {},
"source": "The original rotation's quaternion $q$ can be defined in Sympy accordingly:"
"source": [
"The original rotation's quaternion $q$ can be defined in Sympy accordingly:"
]
},
{
"cell_type": "code",
Expand All @@ -554,7 +568,9 @@
"outputs": [
{
"data": {
"text/latex": "$\\displaystyle \\cos{\\left(\\frac{\\alpha}{2} \\right)} + n_{x} \\sin{\\left(\\frac{\\alpha}{2} \\right)} i + n_{y} \\sin{\\left(\\frac{\\alpha}{2} \\right)} j + n_{z} \\sin{\\left(\\frac{\\alpha}{2} \\right)} k$",
"text/latex": [
"$\\displaystyle \\cos{\\left(\\frac{\\alpha}{2} \\right)} + n_{x} \\sin{\\left(\\frac{\\alpha}{2} \\right)} i + n_{y} \\sin{\\left(\\frac{\\alpha}{2} \\right)} j + n_{z} \\sin{\\left(\\frac{\\alpha}{2} \\right)} k$"
],
"text/plain": [
"cos(alpha/2) + n_x*sin(alpha/2)*i + n_y*sin(alpha/2)*j + n_z*sin(alpha/2)*k"
]
Expand All @@ -577,7 +593,9 @@
"cell_type": "markdown",
"id": "7cb50842c8fa111a",
"metadata": {},
"source": "We get the following system of equations, where the unknowns are $p$, $m$, and $\\theta_2$:\n"
"source": [
"We get the following system of equations, where the unknowns are $p$, $m$, and $\\theta_2$:\n"
]
},
{
"cell_type": "code",
Expand All @@ -592,7 +610,9 @@
"outputs": [
{
"data": {
"text/latex": "$\\displaystyle \\cos{\\left(\\frac{p}{2} \\right)} \\cos{\\left(\\frac{\\theta_{2}}{2} \\right)} = \\cos{\\left(\\frac{\\alpha}{2} \\right)}$",
"text/latex": [
"$\\displaystyle \\cos{\\left(\\frac{p}{2} \\right)} \\cos{\\left(\\frac{\\theta_{2}}{2} \\right)} = \\cos{\\left(\\frac{\\alpha}{2} \\right)}$"
],
"text/plain": [
"Eq(cos(p/2)*cos(theta_2/2), cos(alpha/2))"
]
Expand All @@ -602,7 +622,9 @@
},
{
"data": {
"text/latex": "$\\displaystyle - \\sin{\\left(\\frac{m}{2} \\right)} \\sin{\\left(\\frac{\\theta_{2}}{2} \\right)} = n_{x} \\sin{\\left(\\frac{\\alpha}{2} \\right)}$",
"text/latex": [
"$\\displaystyle - \\sin{\\left(\\frac{m}{2} \\right)} \\sin{\\left(\\frac{\\theta_{2}}{2} \\right)} = n_{x} \\sin{\\left(\\frac{\\alpha}{2} \\right)}$"
],
"text/plain": [
"Eq(-sin(m/2)*sin(theta_2/2), n_x*sin(alpha/2))"
]
Expand All @@ -612,7 +634,9 @@
},
{
"data": {
"text/latex": "$\\displaystyle \\sin{\\left(\\frac{\\theta_{2}}{2} \\right)} \\cos{\\left(\\frac{m}{2} \\right)} = n_{y} \\sin{\\left(\\frac{\\alpha}{2} \\right)}$",
"text/latex": [
"$\\displaystyle \\sin{\\left(\\frac{\\theta_{2}}{2} \\right)} \\cos{\\left(\\frac{m}{2} \\right)} = n_{y} \\sin{\\left(\\frac{\\alpha}{2} \\right)}$"
],
"text/plain": [
"Eq(sin(theta_2/2)*cos(m/2), n_y*sin(alpha/2))"
]
Expand All @@ -622,7 +646,9 @@
},
{
"data": {
"text/latex": "$\\displaystyle \\sin{\\left(\\frac{p}{2} \\right)} \\cos{\\left(\\frac{\\theta_{2}}{2} \\right)} = n_{z} \\sin{\\left(\\frac{\\alpha}{2} \\right)}$",
"text/latex": [
"$\\displaystyle \\sin{\\left(\\frac{p}{2} \\right)} \\cos{\\left(\\frac{\\theta_{2}}{2} \\right)} = n_{z} \\sin{\\left(\\frac{\\alpha}{2} \\right)}$"
],
"text/plain": [
"Eq(sin(p/2)*cos(theta_2/2), n_z*sin(alpha/2))"
]
Expand All @@ -646,7 +672,9 @@
"cell_type": "markdown",
"id": "e6b34ef9cb46f2e4",
"metadata": {},
"source": "Instead, assume $\\sin(p/2) \\neq 0$, then we can substitute in the first equation $\\cos\\left(\\theta_2/2\\right)$ with its value computed from the last equation. We get:"
"source": [
"Instead, assume $\\sin(p/2) \\neq 0$, then we can substitute in the first equation $\\cos\\left(\\theta_2/2\\right)$ with its value computed from the last equation. We get:"
]
},
{
"cell_type": "code",
Expand All @@ -661,7 +689,9 @@
"outputs": [
{
"data": {
"text/latex": "$\\displaystyle \\frac{n_{z} \\sin{\\left(\\frac{\\alpha}{2} \\right)}}{\\tan{\\left(\\frac{p}{2} \\right)}} = \\cos{\\left(\\frac{\\alpha}{2} \\right)}$",
"text/latex": [
"$\\displaystyle \\frac{n_{z} \\sin{\\left(\\frac{\\alpha}{2} \\right)}}{\\tan{\\left(\\frac{p}{2} \\right)}} = \\cos{\\left(\\frac{\\alpha}{2} \\right)}$"
],
"text/plain": [
"Eq(n_z*sin(alpha/2)/tan(p/2), cos(alpha/2))"
]
Expand Down Expand Up @@ -718,7 +748,9 @@
"cell_type": "markdown",
"id": "e3fc4cdb6d15dd1",
"metadata": {},
"source": "The terms are similar to the other decompositions, XZX, YXY, ZXZ, XYX and YZY. However, for ZXZ, XYX and YZY, the $i$ term in the quaternion is positive as seen below in the YZY decomposition."
"source": [
"The terms are similar to the other decompositions, XZX, YXY, ZXZ, XYX and YZY. However, for ZXZ, XYX and YZY, the $i$ term in the quaternion is positive as seen below in the YZY decomposition."
]
},
{
"cell_type": "code",
Expand All @@ -733,7 +765,9 @@
"outputs": [
{
"data": {
"text/latex": "$\\displaystyle \\cos{\\left(\\frac{\\theta_{2}}{2} \\right)} \\cos{\\left(\\frac{\\theta_{1} + \\theta_{3}}{2} \\right)} + \\sin{\\left(\\frac{\\theta_{2}}{2} \\right)} \\sin{\\left(\\frac{\\theta_{1} - \\theta_{3}}{2} \\right)} i + \\sin{\\left(\\frac{\\theta_{1} + \\theta_{3}}{2} \\right)} \\cos{\\left(\\frac{\\theta_{2}}{2} \\right)} j + \\sin{\\left(\\frac{\\theta_{2}}{2} \\right)} \\cos{\\left(\\frac{\\theta_{1} - \\theta_{3}}{2} \\right)} k$",
"text/latex": [
"$\\displaystyle \\cos{\\left(\\frac{\\theta_{2}}{2} \\right)} \\cos{\\left(\\frac{\\theta_{1} + \\theta_{3}}{2} \\right)} + \\sin{\\left(\\frac{\\theta_{2}}{2} \\right)} \\sin{\\left(\\frac{\\theta_{1} - \\theta_{3}}{2} \\right)} i + \\sin{\\left(\\frac{\\theta_{1} + \\theta_{3}}{2} \\right)} \\cos{\\left(\\frac{\\theta_{2}}{2} \\right)} j + \\sin{\\left(\\frac{\\theta_{2}}{2} \\right)} \\cos{\\left(\\frac{\\theta_{1} - \\theta_{3}}{2} \\right)} k$"
],
"text/plain": [
"cos(theta_2/2)*cos((theta_1 + theta_3)/2) + sin(theta_2/2)*sin((theta_1 - theta_3)/2)*i + sin((theta_1 + theta_3)/2)*cos(theta_2/2)*j + sin(theta_2/2)*cos((theta_1 - theta_3)/2)*k"
]
Expand All @@ -758,7 +792,9 @@
"cell_type": "markdown",
"id": "a41b84ed5f3365b2",
"metadata": {},
"source": "Thus, in order to correct for the orientation of the rotation, $\\theta_1$ and $\\theta_3$ are swapped. "
"source": [
"Thus, in order to correct for the orientation of the rotation, $\\theta_1$ and $\\theta_3$ are swapped. "
]
},
{
"cell_type": "markdown",
Expand Down
Loading

0 comments on commit 4d76942

Please sign in to comment.