{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# OpenQASM import and export Tequila circuits" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Open Quantum Assembly Language" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**OpenQASM** is an intermediate representation for quantum instructions. The language was first described in a [paper](https://arxiv.org/pdf/1707.03429v2.pdf) published in July 2017, and a reference source code implementation was released as part of IBM's Quantum Information Software Kit (Qiskit) for use with their IBM Q Experience cloud quantum computing platform.\n", "\n", "OpenQASM defines its version at the head of a source file as a real number, as in the declaration:\n", "\n", "```\n", "OPENQASM 2.0;\n", "````\n", "\n", "The level of OpenQASM's original published implementations (e.g., Qiskit, infra) is OpenQASM 2.0. The 3.0 level of the specification is currently work in progress and can be viewed at the [OpenQASM](https://github.com/qiskit/openqasm) repository on GitHub.\n", "\n", "Tequila functions `export_open_qasm`, `import_open_qasm`, and `import_open_qasm_from_file` work with current OpenQASM version = 2.0\n", "\n", "\n", "### Example\n", "\n", "The following is an example of OpenQASM source code from the official library. The [program](https://github.com/QISKit/openqasm/blob/master/examples/generic/adder.qasm) adds two four-bit numbers.\n", "\n", "```\n", "// quantum ripple-carry adder from Cuccaro et al, quant-ph/0410184\n", "OPENQASM 2.0;\n", "include \"qelib1.inc\";\n", "gate majority a,b,c \n", "{ \n", " cx c,b; \n", " cx c,a; \n", " ccx a,b,c; \n", "}\n", "gate unmaj a,b,c \n", "{ \n", " ccx a,b,c; \n", " cx c,a; \n", " cx a,b; \n", "}\n", "qreg cin[1];\n", "qreg a[4];\n", "qreg b[4];\n", "qreg cout[1];\n", "creg ans[5];\n", "// set input states\n", "x a[0]; // a = 0001\n", "x b; // b = 1111\n", "// add a to b, storing result in b\n", "majority cin[0],b[0],a[0];\n", "majority a[0],b[1],a[1];\n", "majority a[1],b[2],a[2];\n", "majority a[2],b[3],a[3];\n", "cx a[3],cout[0];\n", "unmaj a[2],b[3],a[3];\n", "unmaj a[1],b[2],a[2];\n", "unmaj a[0],b[1],a[1];\n", "unmaj cin[0],b[0],a[0];\n", "measure b[0] -> ans[0];\n", "measure b[1] -> ans[1];\n", "measure b[2] -> ans[2];\n", "measure b[3] -> ans[3];\n", "measure cout[0] -> ans[4];\n", "````" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Export to OpenQASM" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once you have a circuit in Tequila, it is possible to generate its equivalent in OpenQASM code, using the `export_open_qasm` function, for example:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import tequila as tq\n", "from numpy import pi" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0: ───H───Y───────@───\n", " │\n", "1: ───H───────────┼───\n", " │\n", "2: ───Z───Ry(π)───┼───\n", " │\n", "3: ───────────────X───\n" ] } ], "source": [ "circuit = tq.gates.H(target=[0,1]) + \\\n", " tq.gates.Y(target=0) + \\\n", " tq.gates.Z(target=2) + \\\n", " tq.gates.CX(target=3, control=0) + \\\n", " tq.gates.Ry(target=2, angle=pi)\n", "\n", "tq.draw(circuit)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "OPENQASM 2.0;\n", "include \"qelib1.inc\";\n", "qreg q[4];\n", "creg c[4];\n", "h q[0];\n", "h q[1];\n", "y q[0];\n", "z q[2];\n", "cx q[0],q[3];\n", "ry(3.141592653589793) q[2];\n", "\n" ] } ], "source": [ "openqasmcode = tq.export_open_qasm(circuit)\n", "\n", "print(openqasmcode)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is possible to generate the OpenQASM code for [ZX-Calculus](https://en.wikipedia.org/wiki/ZX-calculus), that is, without `Y` gates (`Y`, `Ry`, `Cy`, `CRy`), if you want to activate this option you need to use the `zx_calculus` flag. If enabled, the OpenQASM code will be generated without `Y` gates and will instead place their equivalents for each gate." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "OPENQASM 2.0;\n", "include \"qelib1.inc\";\n", "qreg q[4];\n", "creg c[4];\n", "h q[0];\n", "h q[1];\n", "rz(-1.5707963267948966) q[0];\n", "x q[0];\n", "rz(1.5707963267948966) q[0];\n", "z q[2];\n", "cx q[0],q[3];\n", "rz(-1.5707963267948966) q[2];\n", "rx(3.141592653589793) q[2];\n", "rz(1.5707963267948966) q[2];\n", "\n" ] } ], "source": [ "openqasmcode_no_y = tq.export_open_qasm(circuit, zx_calculus=True)\n", "\n", "print(openqasmcode_no_y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the Tequila circuit is created with variables, the corresponding values must be indicated when exporting it to OpenQASM code:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0: ───H───Y───────────────────────────────────────@───Rx(0.318309886183791*pi*f((var2,))_1)───\n", " │\n", "1: ───H───────────────────────────────────────────┼───────────────────────────────────────────\n", " │\n", "2: ───Z───Ry(0.318309886183791*pi*f((var1,))_0)───┼───────────────────────────────────────────\n", " │\n", "3: ───────────────────────────────────────────────X───────────────────────────────────────────\n" ] } ], "source": [ "circuit_var = tq.gates.H(target=[0,1]) + \\\n", " tq.gates.Y(target=0) + \\\n", " tq.gates.Z(target=2) + \\\n", " tq.gates.CX(target=3, control=0) + \\\n", " tq.gates.Ry(target=2, angle=\"var1\") + \\\n", " tq.gates.Rx(target=0, angle=\"var2\")\n", "\n", "tq.draw(circuit_var)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "OPENQASM 2.0;\n", "include \"qelib1.inc\";\n", "qreg q[4];\n", "creg c[4];\n", "h q[0];\n", "h q[1];\n", "y q[0];\n", "z q[2];\n", "cx q[0],q[3];\n", "ry(2.8) q[2];\n", "rx(0.4487989505128276) q[0];\n", "\n" ] } ], "source": [ "variables = {\"var1\":2.8, \"var2\": pi/7}\n", "\n", "openqasmcode_var = tq.export_open_qasm(circuit_var, variables=variables)\n", "\n", "print(openqasmcode_var)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For convenience, it is possible to generate the code in OpenQASM and send the result to a file for external use. The name of the file must be indicated in the `filename` parameter:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "OPENQASM 2.0;\n", "include \"qelib1.inc\";\n", "qreg q[4];\n", "creg c[4];\n", "h q[0];\n", "h q[1];\n", "y q[0];\n", "z q[2];\n", "cx q[0],q[3];\n", "ry(3.141592653589793) q[2];\n", "\n" ] } ], "source": [ "openqasmcode = tq.export_open_qasm(circuit, filename=\"MyOpenQASMCode.qasm\")\n", "\n", "print(openqasmcode)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now the `MyOpenQASMCode.qasm` file has been created" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Import from OpenQASM" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is possible to take a code in OpenQASM and use it to generate a circuit in Tequila, using the `import_open_qasm` function, for example:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " ┌──┐\n", "0: ────X─────@───@───Rz(0.143π)───\n", " │ │\n", "1: ────Y─────┼───@───Rx(π)────────\n", " │ │\n", "2: ──────────┼───┼───@────────────\n", " │ │ │\n", "3: ─────@────┼───X───Ry(1.6π)─────\n", " │ │\n", "4: ────H┼────Z────────────────────\n", " │\n", "5: ─────H─────────────────────────\n", " └──┘\n" ] } ], "source": [ "openqasmcode = \"OPENQASM 2.0;\\n\" \\\n", " \"include \\\"qelib1.inc\\\";\\n\" \\\n", " \"qreg q1[3];\\n\" \\\n", " \"qreg q2[4];\\n\" \\\n", " \"creg c[3];\\n\" \\\n", " \"x q1[0];\\n\" \\\n", " \"y q1[1];\\n\" \\\n", " \"h q2[2];\\n\" \\\n", " \"cz q1[0],q2[2];\\n\" \\\n", " \"ch q2[1],q2[3];\\n\" \\\n", " \"ccx q1[0],q1[1],q2[1];\\n\" \\\n", " \"rx(pi) q1[1];\\n\" \\\n", " \"rz(pi/7) q1[0];\\n\" \\\n", " \"cry(1.6*pi) q2[0],q2[1];\\n\" \\\n", "\n", "circuit = tq.import_open_qasm(openqasmcode)\n", "\n", "tq.draw(circuit)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can import an OpenQASM code that is not written strictly, but only has the instructions of the gates without containing the definition lines, for this the `rigorous` flag is used, for example:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0: ───X───@───@───Rz(0.143π)───\n", " │ │\n", "1: ───Y───┼───@────────────────\n", " │ │\n", "2: ───────┼───┼───@────────────\n", " │ │ │\n", "3: ───────┼───X───Ry(1.6π)─────\n", " │\n", "4: ───────Z────────────────────\n" ] } ], "source": [ "openqasmcode_not_rigorous = \"qreg q1[3];\\n\" \\\n", " \"qreg q2[4];\\n\" \\\n", " \"creg c[3];\\n\" \\\n", " \"x q1[0];\\n\" \\\n", " \"y q1[1];\\n\" \\\n", " \"cz q1[0],q2[2];\\n\" \\\n", " \"ccx q1[0],q1[1],q2[1];\\n\" \\\n", " \"rz(pi/7) q1[0];\\n\" \\\n", " \"cry(1.6*pi) q2[0],q2[1];\\n\" \\\n", "\n", "circuit_nr = tq.import_open_qasm(openqasmcode_not_rigorous, rigorous=False)\n", "\n", "tq.draw(circuit_nr)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the case of having the OpenQASM code in a file, it is possible to load that file to generate the Tequila circuit from there, for this the `import_open_qasm_from_file` function is used:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0: ───H───Y───────@───\n", " │\n", "1: ───H───────────┼───\n", " │\n", "2: ───Z───Ry(π)───┼───\n", " │\n", "3: ───────────────X───\n" ] } ], "source": [ "circuit_from_file = tq.import_open_qasm_from_file(filename=\"MyOpenQASMCode.qasm\")\n", "\n", "tq.draw(circuit_from_file)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the OpenQASM code contains *custom gates*, importing into a Tequila circuit will generate the equivalent with the gates in the proper place with their corresponding parameters, for example:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " ┌──┐\n", "0: ────────X─────────\n", " │\n", "1: ───Y────┼X────────\n", " ││\n", "2: ───@────@┼────────\n", " │ │\n", "3: ───X─────┼────────\n", " │\n", "4: ───H────Y┼────────\n", " │\n", "5: ───Z─────@────@───\n", " │\n", "6: ──────────────X───\n", " └──┘\n" ] } ], "source": [ "openqasmcode_custom = \"OPENQASM 2.0;\\n\" \\\n", " \"include \\\"qelib1.inc\\\";\\n\" \\\n", " \"gate mycustom a,b,c\\n\" \\\n", " \"{\\n\" \\\n", " \"cx c,b;\\n\" \\\n", " \"cx c,a;\\n\" \\\n", " \"}\\n\" \\\n", " \"qreg q1[3];\\n\" \\\n", " \"qreg q2[4];\\n\" \\\n", " \"creg c[3];\\n\" \\\n", " \"y q1[1];\\n\" \\\n", " \"z q2[2];\\n\" \\\n", " \"mycustom q1[0],q2[0],q1[2];\\n\" \\\n", " \"h q2[1];\\n\" \\\n", " \"mycustom q2[3],q1[1],q2[2];\\n\" \\\n", " \"y q2[1];\\n\"\n", "\n", "circuit_cg = tq.import_open_qasm(openqasmcode_custom)\n", "\n", "tq.draw(circuit_cg)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "@webio": { "lastCommId": null, "lastKernelId": null }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.9" } }, "nbformat": 4, "nbformat_minor": 4 }