{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Quantum Software (with PyZX!): Part 2\n", "\n", "_Aleks Kissinger | 2023_\n", "\n", "Welcome to the second in a series of practicals which will build up from basics to using the ZX calculus to do some interesting and potentially useful stuff on a real quantum computer.\n", "\n", "This problem sheet is designed to go with the [Quantum Software](https://www.cs.ox.ac.uk/teaching/courses/2022-2023/qsoft/) course taught in Oxford, but if you came across it another way, you are more than welcome to give it a go! In part 1, we saw how to:\n", "1. construct quantum circuits and ZX-diagrams programmatically\n", "2. evaluate/simulate them numerically\n", "3. do basic diagrammatic reasoning with software\n", "\n", "In this part, we will use some of the techniques from part 1 to do _automated_ diagrammatic reasoning and circuit optimisation with the ZX-calculus. We will also be translating the circuits that come out into a format that is useable by IBM's [qiskit](https://qiskit.org/) library, and running those circuits on a real quantum computer!\n", "\n", "_These problem sheets, like PyZX itself are released under the [Apache 2](https://github.com/Quantomatic/pyzx/blob/master/LICENSE) open-source license. Feel free to use, copy, and modify them at will, e.g. to use your own course._" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "%pip install \"pyzx @ git+https://github.com/Quantomatic/pyzx.git\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import sys, os, math, random\n", "from fractions import Fraction\n", "\n", "import pyzx as zx\n", "from pyzx import print_matrix\n", "from pyzx.basicrules import *\n", "\n", "from qiskit.providers.fake_provider import FakeAthens\n", "from qiskit import QuantumCircuit, Aer, IBMQ, execute\n", "from qiskit.compiler import assemble\n", "from qiskit.tools.monitor import job_monitor\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Z = zx.VertexType.Z\n", "X = zx.VertexType.X\n", "B = zx.VertexType.BOUNDARY\n", "SE = zx.EdgeType.SIMPLE\n", "HE = zx.EdgeType.HADAMARD" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the next few questions, we will use to rules from part 1 to do automatic optimisation of quantum circuits. To provide a baseline for comparison, we will start by writing a naive circuit optimiser, which performs basic gate cancellations.\n", "\n", "Here's a bit of a template with some comments to get us started:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def circuit_optimizer(circ):\n", " c = circ.copy()\n", " \n", " # keep looping until we return a circuit\n", " while True:\n", " new_gates = []\n", " \n", " # search through the gates in circuit c in order\n", " for g in c.gates:\n", " \n", " # if current and last gate are both Z-phase gates...\n", " if (len(new_gates) > 0 and\n", " isinstance(new_gates[-1], zx.gates.ZPhase) and\n", " isinstance(g, zx.gates.ZPhase) and\n", " new_gates[-1].target == g.target):\n", " \n", " # ...combine the phases into a single phase gate\n", " g1 = zx.gates.ZPhase(g.target, new_gates[-1].phase + g.phase)\n", " new_gates[-1] = g1\n", " \n", " # otherwise just save the current gate\n", " else:\n", " new_gates.append(g)\n", " \n", " # if we actually got a smaller circuit...\n", " if len(new_gates) < len(c.gates):\n", " c.gates = new_gates # ...save it and continue\n", " else:\n", " return c # ...otherwise return the circuit" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This code will combine the phases in adjacent `ZPhase` gates (i.e. gates produced by `s`, `t` or `rz(.)` in QASM).\n", "\n", "Lets see it in action:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "demo_circ = zx.qasm(\"\"\"\n", "qreg q[3];\n", "\n", "t q[0];\n", "t q[0];\n", "t q[0];\n", "cx q[0], q[1];\n", "cx q[1], q[2];\n", "cx q[1], q[2];\n", "cx q[0], q[1];\n", "t q[2];\n", "\"\"\")\n", "demo_opt = circuit_optimizer(demo_circ)\n", "\n", "print(\"original:\")\n", "zx.draw(demo_circ)\n", "print(\"optimised:\")\n", "zx.draw(demo_opt)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This works okay, but it could be much better." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
Although it is obviously more fun to run things on a real quantum computer, if you can't get it to work (or are pressed for time), you can use noisy_simulator
as the backend for this question and the next one. It should have approximately the same behaviour as the \"ibmq_athens\" device.
Note that performance varies a great deal from device to device (and even between runs), so your results with a real quantum computer may vary.
\n", "\n", "