448 lines
18 KiB
Plaintext
448 lines
18 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "35492576-b15e-43e6-8b9b-293631c6b194",
|
|
"metadata": {},
|
|
"source": [
|
|
"# OpenQASM 2 and the Qiskit SDK"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "f1330cdd-036e-4a99-b2d1-0348a4d70cb3",
|
|
"metadata": {
|
|
"tags": [
|
|
"version-info"
|
|
]
|
|
},
|
|
"source": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "328f6a16-5ec4-4d80-8507-f5d97aa02f26",
|
|
"metadata": {},
|
|
"source": [
|
|
"The Qiskit SDK provides some tools for converting between OpenQASM representations of quantum programs, and the [QuantumCircuit](/docs/api/qiskit/qiskit.circuit.QuantumCircuit) class.\n",
|
|
"\n",
|
|
"<span id=\"qasm2-import\"></span>\n",
|
|
"## Import an OpenQASM 2 program into Qiskit\n",
|
|
"\n",
|
|
"Two functions import OpenQASM 2 programs into Qiskit.\n",
|
|
"These are [`qasm2.load()`](../api/qiskit/qasm2#load), which takes a filename, and [`qasm2.loads()`](../api/qiskit/qasm2#loads), which takes the OpenQASM 2 program as a string.\n",
|
|
"\n",
|
|
"```python\n",
|
|
"import qiskit.qasm2\n",
|
|
"\n",
|
|
"qiskit.qasm2.load(filename, include_path=('.',), include_input_directory='append', custom_instructions=(), custom_classical=(), strict=False)\n",
|
|
"qiskit.qasm2.loads(program, include_path=('.',), custom_instructions=(), custom_classical=(), strict=False)\n",
|
|
"```"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "52d909cd-6332-46a3-9a1a-330e1c4a66cf",
|
|
"metadata": {},
|
|
"source": [
|
|
"See the [OpenQASM 2 Qiskit API](/docs/api/qiskit/qasm2) for more information.\n",
|
|
"\n",
|
|
"### Import simple programs\n",
|
|
"\n",
|
|
"For most OpenQASM 2 programs, you can simply use `qasm2.load` and `qasm2.loads` with a single argument.\n",
|
|
"\n",
|
|
"#### Example: import an OpenQASM 2 program as a string\n",
|
|
"\n",
|
|
"Use `qasm2.loads()` to import an OpenQASM 2 program as a string into a QuantumCircuit:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"id": "f7d62945-1899-4b39-acee-6aaf6b37db09",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
" ┌───┐ ┌─┐ \n",
|
|
"q_0: ┤ H ├──■──┤M├───\n",
|
|
" └───┘┌─┴─┐└╥┘┌─┐\n",
|
|
"q_1: ─────┤ X ├─╫─┤M├\n",
|
|
" └───┘ ║ └╥┘\n",
|
|
"c: 2/═══════════╩══╩═\n",
|
|
" 0 1 "
|
|
]
|
|
},
|
|
"execution_count": 1,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"import qiskit.qasm2\n",
|
|
"\n",
|
|
"program = \"\"\"\n",
|
|
" OPENQASM 2.0;\n",
|
|
" include \"qelib1.inc\";\n",
|
|
" qreg q[2];\n",
|
|
" creg c[2];\n",
|
|
"\n",
|
|
" h q[0];\n",
|
|
" cx q[0], q[1];\n",
|
|
"\n",
|
|
" measure q -> c;\n",
|
|
"\"\"\"\n",
|
|
"circuit = qiskit.qasm2.loads(program)\n",
|
|
"circuit.draw()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "6556b10e-7d15-4c51-a917-a969a23babff",
|
|
"metadata": {},
|
|
"source": [
|
|
"#### Example: import an OpenQASM 2 program from a file\n",
|
|
"\n",
|
|
"Use `load()` to import an OpenQASM 2 program from a file into a QuantumCircuit:\n",
|
|
"\n",
|
|
"```python\n",
|
|
"import qiskit.qasm2\n",
|
|
"circuit = qiskit.qasm2.load(\"myfile.qasm\")\n",
|
|
"```\n",
|
|
"\n",
|
|
"\n",
|
|
"<span id=\"custom-instructions\"></span>\n",
|
|
"### Link OpenQASM 2 gates with Qiskit gates\n",
|
|
"\n",
|
|
"By default, Qiskit's OpenQASM 2 importer treats the include file `\"qelib1.inc\"` as a *de facto* standard library.\n",
|
|
"The importer treats this file as containing precisely the gates it is described to contain in [the original paper defining OpenQASM 2](https://arxiv.org/abs/1707.03429).\n",
|
|
"Qiskit will use the built-in gates in [the circuit library](../api/qiskit/circuit_library) to represent the gates in `\"qelib1.inc\"`.\n",
|
|
"Gates defined in the program by manual OpenQASM 2 `gate` statements will, by default, be constructed as custom [Qiskit `Gate` subclasses](../api/qiskit/qiskit.circuit.Gate).\n",
|
|
"\n",
|
|
"You can tell the importer to use specific [`Gate`](../api/qiskit/qiskit.circuit.Gate) classes for the given `gate` statements it encounters.\n",
|
|
"You can also use this mechanism to treat additional gate names as \"built-in\", that is, not requiring an explicit definition.\n",
|
|
"If you specify which gate classes to use for `gate` statements outside of `\"qelib1.inc\"`, the resulting circuit will typically be more efficient to work with.\n",
|
|
"\n",
|
|
"<Admonition type=\"warning\">\n",
|
|
"As of Qiskit SDK 1.0, Qiskit's OpenQASM 2 *exporter* (see [Export a Qiskit circuit to OpenQASM 2](#qasm2-export)) still behaves as if `\"qelib1.inc\"` has more gates than it really does.\n",
|
|
"This means that the default settings of the importer might not be able to import a program exported by our importer.\n",
|
|
"See [the specific example on working with the legacy exporter](#qasm2-import-legacy) to resolve this problem.\n",
|
|
"\n",
|
|
"This discrepancy is legacy behavior of Qiskit, and [it will be resolved in a later release of Qiskit](https://github.com/Qiskit/qiskit/issues/10737).\n",
|
|
"</Admonition>\n",
|
|
"\n",
|
|
"To pass information about a custom instruction to the OpenQASM 2 importer, use [the `qasm2.CustomInstruction` class](../api/qiskit/qasm2#qiskit.qasm2.CustomInstruction).\n",
|
|
"This has four required pieces of information, in order:\n",
|
|
"\n",
|
|
"* The **name** of the gate, used in the OpenQASM 2 program\n",
|
|
"* The **number of angle parameters** that the gate takes\n",
|
|
"* The **number of qubits** that the gate acts on\n",
|
|
"* The Python **constructor** class or function for the gate, which takes the gate parameters (but not qubits) as individual arguments\n",
|
|
"\n",
|
|
"If the importer encounters a `gate` definition that matches a given custom instruction, it will use that custom information to reconstruct the gate object.\n",
|
|
"If a `gate` statement is encountered that matches the `name` of a custom instruction, but does not match both the number of parameters and the number of qubits, the importer will raise a [`QASM2ParseError`](../api/qiskit/qasm2#qasm2parseerror), to indicate the mismatch between the supplied information and program.\n",
|
|
"\n",
|
|
"In addition, a fifth argument `builtin` can be optionally set to `True` to make the gate automatically available within the OpenQASM 2 program, even if it is not explicitly defined.\n",
|
|
"If the importer does encounter an explicit `gate` definition for a built-in custom instruction, it will accept it silently.\n",
|
|
"As before, if an explicit definition of the same name is not compatible with the provided custom instruction, a [`QASM2ParseError`](../api/qiskit/qasm2#qasm2parseerror) will be raised.\n",
|
|
"This is useful for compatibility with older OpenQASM 2 exporters, and with certain other quantum platforms that treat the \"basis gates\" of their hardware as built-in instructions.\n",
|
|
"\n",
|
|
"Qiskit provides a data attribute for working with OpenQASM 2 programs produced by legacy versions of [Qiskit's OpenQASM 2 exporting capabilities](#qasm2-export).\n",
|
|
"This is [`qasm2.LEGACY_CUSTOM_INSTRUCTIONS`](../api/qiskit/qasm2#legacy-compatibility), which can be given as the `custom_instructions` argument to [`qasm2.load()`](../api/qiskit/qasm2#load) and [`qasm2.loads()`](../api/qiskit/qasm2#loads).\n",
|
|
"\n",
|
|
"\n",
|
|
"<span id=\"qasm2-import-legacy\"></span>\n",
|
|
"#### Example: import a program created by Qiskit's legacy exporter\n",
|
|
"\n",
|
|
"This OpenQASM 2 program uses gates that are not in the original version of `\"qelib1.inc\"` without declaring them, but are standard gates in Qiskit's library.\n",
|
|
"You can use [`qasm2.LEGACY_CUSTOM_INSTRUCTIONS`](../api/qiskit/qasm2#legacy-compatibility) to easily tell the importer to use the same set of gates that Qiskit's OpenQASM 2 exporter previously used."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"id": "a4b52b96-1b2d-48bb-8aa4-bd413aa3acea",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from qiskit import qasm2\n",
|
|
"\n",
|
|
"program = \"\"\"\n",
|
|
" OPENQASM 2.0;\n",
|
|
" include \"qelib1.inc\";\n",
|
|
"\n",
|
|
" qreg q[4];\n",
|
|
" creg c[4];\n",
|
|
"\n",
|
|
" h q[0];\n",
|
|
" cx q[0], q[1];\n",
|
|
"\n",
|
|
" // 'rxx' is not actually in `qelib1.inc`,\n",
|
|
" // but Qiskit used to behave as if it were.\n",
|
|
" rxx(0.75) q[2], q[3];\n",
|
|
"\n",
|
|
" measure q -> c;\n",
|
|
"\"\"\"\n",
|
|
"circuit = qasm2.loads(\n",
|
|
" program,\n",
|
|
" custom_instructions=qasm2.LEGACY_CUSTOM_INSTRUCTIONS,\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "0de0c75d-3890-4138-a994-e8dcf728d7f3",
|
|
"metadata": {},
|
|
"source": [
|
|
"#### Example: use a particular gate class when importing an OpenQASM 2 program\n",
|
|
"\n",
|
|
"Qiskit cannot, in general, verify if the definition in an OpenQASM 2 `gate` statement corresponds exactly to a Qiskit standard-library gate.\n",
|
|
"Instead, Qiskit chooses a custom gate using the precise definition supplied.\n",
|
|
"This can be less efficient that using one of the built-in standard gates, or a user-defined custom gate.\n",
|
|
"You can manually define `gate` statements with particular classes."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"id": "73432f4a-c5c1-4cec-ba5d-85ad4787ef52",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from qiskit import qasm2\n",
|
|
"from qiskit.circuit import Gate\n",
|
|
"from qiskit.circuit.library import RZXGate\n",
|
|
"\n",
|
|
"\n",
|
|
"# Define a custom gate that takes one qubit and two angles.\n",
|
|
"class MyGate(Gate):\n",
|
|
" def __init__(self, theta, phi):\n",
|
|
" super().__init__(\"my\", 1, [theta, phi])\n",
|
|
"\n",
|
|
"\n",
|
|
"custom_instructions = [\n",
|
|
" # Link the OpenQASM 2 name 'my' with our custom gate.\n",
|
|
" qasm2.CustomInstruction(\"my\", 2, 1, MyGate),\n",
|
|
" # Link the OpenQASM 2 name 'rzx' with Qiskit's\n",
|
|
" # built-in RZXGate.\n",
|
|
" qasm2.CustomInstruction(\"rzx\", 1, 2, RZXGate),\n",
|
|
"]\n",
|
|
"\n",
|
|
"program = \"\"\"\n",
|
|
" OPENQASM 2.0;\n",
|
|
"\n",
|
|
" gate my(theta, phi) q {\n",
|
|
" U(theta / 2, phi, -theta / 2) q;\n",
|
|
" }\n",
|
|
" gate rzx(theta) a, b {\n",
|
|
" // It doesn't matter what definition is\n",
|
|
" // supplied, if the parameters match;\n",
|
|
" // Qiskit will still use `RZXGate`.\n",
|
|
" }\n",
|
|
"\n",
|
|
" qreg q[2];\n",
|
|
" my(0.25, 0.125) q[0];\n",
|
|
" rzx(pi) q[0], q[1];\n",
|
|
"\"\"\"\n",
|
|
"\n",
|
|
"circuit = qasm2.loads(\n",
|
|
" program,\n",
|
|
" custom_instructions=custom_instructions,\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "eb78c9cc-e600-4be0-8616-b9bc5ddad511",
|
|
"metadata": {},
|
|
"source": [
|
|
"#### Example: define a new built-in gate in an OpenQASM 2 program\n",
|
|
"\n",
|
|
"If the argument `builtin=True` is set, a custom gate does not need to have an associated definition."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"id": "c37eb527-bed0-4c62-8404-f79e49d42318",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from qiskit import qasm2\n",
|
|
"from qiskit.circuit import Gate\n",
|
|
"\n",
|
|
"\n",
|
|
"# Define a custom gate that takes one qubit and two angles.\n",
|
|
"class MyGate(Gate):\n",
|
|
" def __init__(self, theta, phi):\n",
|
|
" super().__init__(\"my\", 1, [theta, phi])\n",
|
|
"\n",
|
|
"\n",
|
|
"custom_instructions = [\n",
|
|
" qasm2.CustomInstruction(\"my\", 2, 1, MyGate, builtin=True),\n",
|
|
"]\n",
|
|
"\n",
|
|
"program = \"\"\"\n",
|
|
" OPENQASM 2.0;\n",
|
|
" qreg q[1];\n",
|
|
"\n",
|
|
" my(0.25, 0.125) q[0];\n",
|
|
"\"\"\"\n",
|
|
"\n",
|
|
"circuit = qasm2.loads(\n",
|
|
" program,\n",
|
|
" custom_instructions=custom_instructions,\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "4145ce48-0001-42e2-be5e-83c2e31f0fd9",
|
|
"metadata": {},
|
|
"source": [
|
|
"<span id=\"custom-classical\"></span>\n",
|
|
"### Define custom classical functions\n",
|
|
"\n",
|
|
"OpenQASM 2 includes some built-in classical functions to use in gate arguments.\n",
|
|
"You can extend the language with more functions by using the `custom_classical` argument to [`qasm2.load()`](../api/qiskit/qasm2#load) and [`qasm2.loads()`](../api/qiskit/qasm3#loads), with the [`qasm2.CustomClassical`](../api/qiskit/qasm2#qiskit.qasm2.CustomClassical) class.\n",
|
|
"\n",
|
|
"To define a custom classical function, you must supply:\n",
|
|
"\n",
|
|
"* The *name* of the function as it appears in the OpenQASM 2 program\n",
|
|
"* The number of floating-point arguments it accepts\n",
|
|
"* A callable Python object that evaluates the function\n",
|
|
"\n",
|
|
"All defined custom classical functions are treated as built-in to the OpenQASM 2 language by the importer.\n",
|
|
"There is no official way within the OpenQASM 2 language to define new functions; this is a Qiskit extension.\n",
|
|
"\n",
|
|
"\n",
|
|
"#### Example: use custom classical instructions\n",
|
|
"\n",
|
|
"Here we provide two custom classical functions.\n",
|
|
"The first is simple, and just adds one to its input.\n",
|
|
"The second is the function ``math.atan2``, which represents the mathematical operation $\\arctan(y/x)$ in a quadrant-aware manner."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"id": "b135de19-4b54-4820-8f9d-84a42494debe",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import math\n",
|
|
"from qiskit import qasm2\n",
|
|
"\n",
|
|
"program = \"\"\"\n",
|
|
" include \"qelib1.inc\";\n",
|
|
" qreg q[2];\n",
|
|
" rx(arctan(pi, 3 + add_one(0.2))) q[0];\n",
|
|
" cx q[0], q[1];\n",
|
|
"\"\"\"\n",
|
|
"\n",
|
|
"\n",
|
|
"def add_one(x):\n",
|
|
" return x + 1\n",
|
|
"\n",
|
|
"\n",
|
|
"customs = [\n",
|
|
" # Our `add_one` takes only one parameter.\n",
|
|
" qasm2.CustomClassical(\"add_one\", 1, add_one),\n",
|
|
" # `arctan` takes two parameters, and `math.atan2` implements it.\n",
|
|
" qasm2.CustomClassical(\"arctan\", 2, math.atan2),\n",
|
|
"]\n",
|
|
"circuit = qasm2.loads(program, custom_classical=customs)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "4373999e-9df1-4078-ab83-d1c88a577aaf",
|
|
"metadata": {},
|
|
"source": [
|
|
"<span id=\"strict\"></span>\n",
|
|
"### Strict mode\n",
|
|
"\n",
|
|
"By default, this parser is more relaxed than the official specification.\n",
|
|
"It allows trailing commas in parameter lists; unnecessary (empty-statement) semicolons; omission of the `OPENQASM 2.0;` version statement; and several other quality-of-life improvements without emitting any errors.\n",
|
|
"However, you can use the \"letter-of-the-spec\" mode with `strict=True`.\n",
|
|
"\n",
|
|
"\n",
|
|
"<span id=\"qasm2-export\"></span>\n",
|
|
"## Export a Qiskit circuit to OpenQASM 2\n",
|
|
"\n",
|
|
"Qiskit can also export a [`QuantumCircuit`](../api/qiskit/qiskit.circuit.QuantumCircuit) to OpenQASM 2.\n",
|
|
"You use the function [`qasm2.dump()`](../api/qiskit/qasm2#dump) to write to a file, and [`qasm2.dumps()`](../api/qiskit/qasm2#dumps) to write to a string.\n",
|
|
"These functions currently have a very simple interface: they accept a circuit and, only in the case of [`qasm2.dump()`](../api/qiskit/qasm2#dump), a location to write the output to.\n",
|
|
"\n",
|
|
"<Admonition type=\"warning\">\n",
|
|
"Qiskit's OpenQASM 2 exporter still assumes a legacy, non-standard version of the `\"qelib1.inc\"` include file.\n",
|
|
"[This will be resolved in a later release of Qiskit](https://github.com/Qiskit/qiskit/issues/10737), but in the meantime, if you need to re-import an OpenQASM 2 program created with Qiskit, use [the example above for how to tell the importer about the legacy gates](#qasm2-import-legacy).\n",
|
|
"</Admonition>\n",
|
|
"\n",
|
|
"\n",
|
|
"### Example: export a circuit to OpenQASM 2"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"id": "70f4d657-8be3-400d-a793-d36962a855ef",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from qiskit import QuantumCircuit, qasm2\n",
|
|
"\n",
|
|
"# Define any circuit.\n",
|
|
"circuit = QuantumCircuit(2, 2)\n",
|
|
"circuit.h(0)\n",
|
|
"circuit.cx(0, 1)\n",
|
|
"circuit.measure([0, 1], [0, 1])\n",
|
|
"\n",
|
|
"# Export to a string.\n",
|
|
"program = qasm2.dumps(circuit)\n",
|
|
"\n",
|
|
"# Export to a file.\n",
|
|
"qasm2.dump(circuit, \"my_file.qasm\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "1c0fd601-51ec-4d82-8821-20e0554a3662",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Next steps\n",
|
|
"\n",
|
|
"<Admonition type=\"tip\" title=\"Recommendations\">\n",
|
|
" - Learn how to generate OpenQASM code in the [Explore gates and circuits with the Quantum Composer](https://learning.quantum.ibm.com/tutorial/explore-gates-and-circuits-with-the-quantum-composer) tutorial.\n",
|
|
" - See the [OpenQASM 2 Qiskit API](/docs/api/qiskit/qasm2) reference.\n",
|
|
" - Review the [Verify your program](./debugging-tools) topic.\n",
|
|
" - Visit the [OpenQASM Live Specification](https://openqasm.com/).\n",
|
|
"</Admonition>"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"description": "How to convert code between OpenQASM 2 and the Qiskit SDK.",
|
|
"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"
|
|
},
|
|
"title": "OpenQASM 2 and the Qiskit SDK"
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
}
|