qiskit-documentation/docs/guides/interoperate-qiskit-qasm2.i...

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
}