diff --git a/CHANGELOG.md b/CHANGELOG.md index eb129ffe..01c2496c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). -Right now this project is in Beta and does not yet follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +Right now this project is in Beta and does not yet follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Hence, occasionally changes will be backwards incompatible (although they will all be documented here). ## [0.x.x] - xxxx-xx-xx @@ -13,7 +13,8 @@ Hence, occasionally changes will be backwards incompatible (although they will a - Support for W nodes and Z boxes (courtesy of @RazinShaikh). - Support for poly phases (courtesy of @RazinShaikh) - Support for Jupyter notebooks in documentation using nbsphinx (courtesy of @dlyongemallo). -- Support for OpenQASM 3.0; added Jupyter notebook documenting supported qasm gates (courtesy of @dlyongemallo). +- Jupyter notebook documenting all supported gates (courtesy of @dlyongemallo). +- Support for OpenQASM 3.0 (courtesy of @dlyongemallo). - A function `is_well_formed` to check that a graph is a well-formed ZX-diagram (courtesy of @RazinShaikh). - A function `is_pauli` to check whether a phase is Pauli (courtesy of @y-richie-y) - A function `GraphDiff` that calculates what actions are needed to bring one graph to another (used in ZXLive). @@ -76,7 +77,7 @@ This release adds several new features: support for evaluating ZX-diagrams as te There is one small breaking change, which is that `Graph.inputs` and `Graph.outputs` are now methods that return a list, instead of being lists themselves. ### Added -- Added support for evaluating ZX-diagrams as tensor networks in [quimb](https://quimb.readthedocs.io/en/latest/index.html) (courtesy of +- Added support for evaluating ZX-diagrams as tensor networks in [quimb](https://quimb.readthedocs.io/en/latest/index.html) (courtesy of Paul Tirlisan). - Added [quizx](https://github.com/Quantomatic/quizx) backend for the `Graph` class. - `Graph` vertices can now carry a `ground` generator. This makes it possible to represent measurements and classical control in the diagrams. See the accompanying [paper](https://arxiv.org/abs/2109.06071) (courtesy of ABorgna). diff --git a/doc/index.rst b/doc/index.rst index 67a10827..8cda52b6 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -21,7 +21,7 @@ PyZX :caption: Notebooks: notebooks/gettingstarted - notebooks/qasm + notebooks/gates Indices and tables ================== diff --git a/doc/notebooks/qasm.ipynb b/doc/notebooks/gates.ipynb similarity index 56% rename from doc/notebooks/qasm.ipynb rename to doc/notebooks/gates.ipynb index b4bac0a0..5b9fff8a 100644 --- a/doc/notebooks/qasm.ipynb +++ b/doc/notebooks/gates.ipynb @@ -17,9 +17,9 @@ "id": "2b28a205", "metadata": {}, "source": [ - "# Supported OpenQASM Gates\n", + "# Supported Gates\n", "\n", - "The pyzx library supports a subset of the OpenQASM format.\n", + "The pyzx library supports a variety of gates, including all the commonly-used gates in the OpenQASM standard library. It also supports a subset of the OpenQASM file format.\n", "\n", "Here are some examples illustrating the usage:" ] @@ -32,8 +32,14 @@ "outputs": [], "source": [ "import pyzx as zx\n", - "from pyzx.circuit import Circuit\n", + "from pyzx.circuit import Circuit, CNOT\n", "\n", + "# Add pyzx gates.\n", + "c = Circuit(2)\n", + "c.add_gate(\"CNOT\", 0, 1)\n", + "c.add_gate(CNOT(1, 0))\n", + "\n", + "# Adding qasm gates.\n", "s = \"\"\"\n", "OPENQASM 2.0;\n", "include \"qelib1.inc\";\n", @@ -52,7 +58,7 @@ "id": "4fb99b07", "metadata": {}, "source": [ - "The set of supported OpenQASM gates are listed below for reference, using the following function to draw their graphs. (Set `simplify` to `True` to reduce the graphs, and set `show_matrix` to `True` to output their matrices.)" + "The set of supported gates are listed below for reference, using the following function to draw their graphs. (Set `simplify` to `True` to reduce the graphs, and set `show_matrix` to `True` to output their matrices.)" ] }, { @@ -64,15 +70,30 @@ "source": [ "from pyzx.circuit.qasmparser import QASMParser\n", "\n", - "def draw_qasm_circuit(num_qubits, qasm, simplify=False, show_matrix=False):\n", - " print(qasm)\n", - " g = QASMParser().parse(f\"qreg q[{num_qubits}];\\n\" + qasm, strict=False).to_graph()\n", + "def _print_gate_name(gate):\n", + " print(gate.name + (\" (adjoint)\" if hasattr(gate, \"adjoint\") and gate.adjoint else \"\"))\n", + "\n", + "def draw_zx_diagram(num_qubits, gate_or_qasm, simplify=False, show_matrix=False):\n", + " if isinstance(gate_or_qasm, str):\n", + " qasm = gate_or_qasm\n", + " c = QASMParser().parse(f\"qreg q[{num_qubits}];\\n\" + qasm, strict=False)\n", + " for gate in c.gates:\n", + " _print_gate_name(gate)\n", + " print(qasm)\n", + " else:\n", + " gate = gate_or_qasm\n", + " c = Circuit(num_qubits)\n", + " c.add_gate(gate)\n", + " _print_gate_name(gate)\n", + " print(\"(no simple qasm command)\")\n", + "\n", + " g = c.to_graph()\n", " if simplify:\n", - " g.auto_detect_io()\n", - " zx.simplify.full_reduce(g)\n", + " g.auto_detect_io()\n", + " zx.simplify.full_reduce(g)\n", " zx.draw(g)\n", " if show_matrix:\n", - " print(g.to_matrix())" + " print(g.to_matrix())" ] }, { @@ -92,10 +113,10 @@ "metadata": {}, "outputs": [], "source": [ - "draw_qasm_circuit(1, 'x q;')\n", - "draw_qasm_circuit(1, 'y q;')\n", - "draw_qasm_circuit(1, 'z q;')\n", - "draw_qasm_circuit(1, 'h q;')" + "draw_zx_diagram(1, 'x q;')\n", + "draw_zx_diagram(1, 'y q;')\n", + "draw_zx_diagram(1, 'z q;')\n", + "draw_zx_diagram(1, 'h q;')" ] }, { @@ -115,12 +136,12 @@ "metadata": {}, "outputs": [], "source": [ - "draw_qasm_circuit(1, 'rx(0.125*pi) q;')\n", - "draw_qasm_circuit(1, 'ry(0.125*pi) q;')\n", - "draw_qasm_circuit(1, 'rz(0.125*pi) q;') # can also use 'p' or 'u1'\n", + "draw_zx_diagram(1, 'rx(0.125*pi) q;')\n", + "draw_zx_diagram(1, 'ry(0.125*pi) q;')\n", + "draw_zx_diagram(1, 'rz(0.125*pi) q;') # can also use 'p' or 'u1'\n", "\n", - "draw_qasm_circuit(1, 'u2(0.125*pi,0.125*pi) q[0];')\n", - "draw_qasm_circuit(1, 'u3(0.125*pi,0.125*pi,0.125*pi) q[0];')" + "draw_zx_diagram(1, 'u2(0.125*pi,0.125*pi) q[0];')\n", + "draw_zx_diagram(1, 'u3(0.125*pi,0.125*pi,0.125*pi) q[0];')" ] }, { @@ -138,12 +159,12 @@ "metadata": {}, "outputs": [], "source": [ - "draw_qasm_circuit(1, 's q;')\n", - "draw_qasm_circuit(1, 'sdg q;')\n", - "draw_qasm_circuit(1, 't q;')\n", - "draw_qasm_circuit(1, 'tdg q;')\n", - "draw_qasm_circuit(1, 'sx q;')\n", - "draw_qasm_circuit(1, 'sxdg q;')" + "draw_zx_diagram(1, 's q;')\n", + "draw_zx_diagram(1, 'sdg q;')\n", + "draw_zx_diagram(1, 't q;')\n", + "draw_zx_diagram(1, 'tdg q;')\n", + "draw_zx_diagram(1, 'sx q;')\n", + "draw_zx_diagram(1, 'sxdg q;')" ] }, { @@ -169,7 +190,7 @@ "metadata": {}, "outputs": [], "source": [ - "draw_qasm_circuit(2, 'swap q[0], q[1];')" + "draw_zx_diagram(2, 'swap q[0], q[1];')" ] }, { @@ -187,8 +208,8 @@ "metadata": {}, "outputs": [], "source": [ - "draw_qasm_circuit(2, 'rxx(0.125*pi) q[0], q[1];')\n", - "draw_qasm_circuit(2, 'rzz(0.125*pi) q[0], q[1];')" + "draw_zx_diagram(2, 'rxx(0.125*pi) q[0], q[1];')\n", + "draw_zx_diagram(2, 'rzz(0.125*pi) q[0], q[1];')" ] }, { @@ -206,11 +227,13 @@ "metadata": {}, "outputs": [], "source": [ - "draw_qasm_circuit(2, 'cx q[0], q[1];')\n", - "draw_qasm_circuit(2, 'cy q[0], q[1];')\n", - "draw_qasm_circuit(2, 'cz q[0], q[1];')\n", - "draw_qasm_circuit(2, 'ch q[0], q[1];')\n", - "draw_qasm_circuit(2, 'csx q[0], q[1];')" + "from pyzx.circuit import XCX\n", + "draw_zx_diagram(2, 'cx q[0], q[1];')\n", + "draw_zx_diagram(2, 'cy q[0], q[1];')\n", + "draw_zx_diagram(2, 'cz q[0], q[1];')\n", + "draw_zx_diagram(2, 'ch q[0], q[1];')\n", + "draw_zx_diagram(2, 'csx q[0], q[1];')\n", + "draw_zx_diagram(2, XCX(0, 1))" ] }, { @@ -230,15 +253,15 @@ "metadata": {}, "outputs": [], "source": [ - "draw_qasm_circuit(2, 'crx(0.125*pi) q[0], q[1];')\n", - "draw_qasm_circuit(2, 'cry(0.125*pi) q[0], q[1];')\n", - "draw_qasm_circuit(2, 'crz(0.125*pi) q[0], q[1];')\n", + "draw_zx_diagram(2, 'crx(0.125*pi) q[0], q[1];')\n", + "draw_zx_diagram(2, 'cry(0.125*pi) q[0], q[1];')\n", + "draw_zx_diagram(2, 'crz(0.125*pi) q[0], q[1];')\n", "\n", "# Note that this differs from 'crz' by a relative phase.\n", - "draw_qasm_circuit(2, 'cp(0.125*pi) q[0], q[1];') # can also use 'cphase' or 'cu1'\n", + "draw_zx_diagram(2, 'cp(0.125*pi) q[0], q[1];') # can also use 'cphase' or 'cu1'\n", "\n", - "draw_qasm_circuit(2, 'cu3(0.125*pi,0.125*pi,0.125*pi) q[0], q[1];')\n", - "draw_qasm_circuit(2, 'cu(0.125*pi,0.125*pi,0.125*pi,0.125*pi) q[0], q[1];')" + "draw_zx_diagram(2, 'cu3(0.125*pi,0.125*pi,0.125*pi) q[0], q[1];')\n", + "draw_zx_diagram(2, 'cu(0.125*pi,0.125*pi,0.125*pi,0.125*pi) q[0], q[1];')" ] }, { @@ -264,7 +287,7 @@ "metadata": {}, "outputs": [], "source": [ - "draw_qasm_circuit(3, 'cswap q[0], q[1], q[2];') # Fredkin" + "draw_zx_diagram(3, 'cswap q[0], q[1], q[2];') # Fredkin" ] }, { @@ -282,8 +305,29 @@ "metadata": {}, "outputs": [], "source": [ - "draw_qasm_circuit(3, 'ccx q[0], q[1], q[2];') # Toffoli\n", - "draw_qasm_circuit(3, 'ccz q[0], q[1], q[2];')" + "draw_zx_diagram(3, 'ccx q[0], q[1], q[2];') # Toffoli\n", + "draw_zx_diagram(3, 'ccz q[0], q[1], q[2];')" + ] + }, + { + "cell_type": "markdown", + "id": "a79198ef-d527-4c81-bb9f-d5aa9851c266", + "metadata": {}, + "source": [ + "## Other gates" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bca3b4c3", + "metadata": {}, + "outputs": [], + "source": [ + "from pyzx.circuit import ParityPhase, FSim\n", + "\n", + "draw_zx_diagram(4, ParityPhase(0.5, 0, 1, 2, 3))\n", + "draw_zx_diagram(2, FSim(0, 1, 1/2, 1))" ] } ], diff --git a/doc/representations.rst b/doc/representations.rst index 9f12ba70..cc326135 100644 --- a/doc/representations.rst +++ b/doc/representations.rst @@ -21,7 +21,7 @@ The currently supported formats are To convert a PyZX circuit to these formats, use :meth:`~pyzx.circuit.Circuit.to_qasm`, :meth:`~pyzx.circuit.Circuit.to_quipper`, :meth:`~pyzx.circuit.Circuit.to_qc`. -PyZX also offers a convenience function to construct a circuit out of a string containing QASM code using either :meth:`~pyzx.circuit.Circuit.from_qasm` or :func:`~pyzx.circuit.qasmparser.qasm`. See the `Supported OpenQASM Gates notebook `_ for more details. +PyZX also offers a convenience function to construct a circuit out of a string containing QASM code using either :meth:`~pyzx.circuit.Circuit.from_qasm` or :func:`~pyzx.circuit.qasmparser.qasm`. See the `Supported Gates notebook `_ for more details. To convert a Circuit into a PyZX Graph (i.e. a ZX-diagram), call the method :meth:`~pyzx.circuit.Circuit.to_graph`. diff --git a/pyzx/circuit/__init__.py b/pyzx/circuit/__init__.py index f2bd2bf3..1d349bf7 100644 --- a/pyzx/circuit/__init__.py +++ b/pyzx/circuit/__init__.py @@ -19,7 +19,9 @@ import numpy as np -from .gates import Gate, gate_types, ZPhase, XPhase, CZ, XCX, CNOT, HAD, SWAP, CCZ, Tofolli, Measurement +from .gates import (Gate, gate_types, NOT, Y, Z, HAD, XPhase, YPhase, ZPhase, U2, U3, S, T, SX, SWAP, RXX, RZZ, CNOT, + CY, CZ, CHAD, CSX, XCX, CRX, CRY, CRZ, CPhase, CU3, CU, CSWAP, Tofolli, CCZ, ParityPhase, FSim, + Measurement) from ..graph.base import BaseGraph from ..utils import EdgeType diff --git a/pyzx/circuit/gates.py b/pyzx/circuit/gates.py index 00c57659..000c017c 100644 --- a/pyzx/circuit/gates.py +++ b/pyzx/circuit/gates.py @@ -824,6 +824,8 @@ class FSim(Gate): qsim_name = 'fs' print_phase = True def __init__(self, control:int, target:int, theta:FractionLike, phi:FractionLike): + # TODO: this version assumes theta is always (pi/2) + assert theta == Fraction(1, 2) self.control = control self.target = target self.theta = theta