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

307 lines
12 KiB
Plaintext

---
title: OpenQASM 2 and the Qiskit SDK
description: How to convert code between OpenQASM 2 and the Qiskit SDK.
---
# OpenQASM 2 and the Qiskit SDK
The Qiskit SDK provides some tools for converting between OpenQASM representations of quantum programs, and the [QuantumCircuit](/api/qiskit/qiskit.circuit.QuantumCircuit) class.
<span id="qasm2-import"></span>
## Import an OpenQASM 2 program into Qiskit
Two functions import OpenQASM 2 programs into Qiskit.
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.
```python
import qiskit.qasm2
qiskit.qasm2.load(filename, include_path=('.',), include_input_directory='append', custom_instructions=(), custom_classical=(), strict=False)
qiskit.qasm2.loads(program, include_path=('.',), custom_instructions=(), custom_classical=(), strict=False)
```
See the [OpenQASM 2 Qiskit API](/api/qiskit/qasm2) for more information.
### Import simple programs
For most OpenQASM 2 programs, you can simply use `qasm2.load` and `qasm2.loads` with a single argument.
#### Example: import an OpenQASM 2 program as a string
Use `qasm2.loads()` to import an OpenQASM 2 program as a string into a QuantumCircuit:
```python
import qiskit.qasm2
program = '''
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q[0];
cx q[0], q[1];
measure q -> c;
'''
circuit = qiskit.qasm2.loads(program)
circuit.draw()
```
![output](/images/guides/qasm2.png)
#### Example: import an OpenQASM 2 program from a file
Use `load()` to import an OpenQASM 2 program from a file into a QuantumCircuit:
```python
import qiskit.qasm2
circuit = qiskit.qasm2.load("myfile.qasm")
```
<span id="custom-instructions"></span>
### Link OpenQASM 2 gates with Qiskit gates
By default, Qiskit's OpenQASM 2 importer treats the include file `"qelib1.inc"` as a *de facto* standard library.
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).
Qiskit will use the built-in gates in [the circuit library](../api/qiskit/circuit_library) to represent the gates in `"qelib1.inc"`.
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).
You can tell the importer to use specific [`Gate`](../api/qiskit/qiskit.circuit.Gate) classes for the given `gate` statements it encounters.
You can also use this mechanism to treat additional gate names as "built-in", that is, not requiring an explicit definition.
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.
<Admonition type="warning">
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.
This means that the default settings of the importer might not be able to import a program exported by our importer.
See [the specific example on working with the legacy exporter](#qasm2-import-legacy) to resolve this problem.
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).
</Admonition>
To pass information about a custom instruction to the OpenQASM 2 importer, use [the `qasm2.CustomInstruction` class](../api/qiskit/qasm2#qiskit.qasm2.CustomInstruction).
This has four required pieces of information, in order:
* The **name** of the gate, used in the OpenQASM 2 program
* The **number of angle parameters** that the gate takes
* The **number of qubits** that the gate acts on
* The Python **constructor** class or function for the gate, which takes the gate parameters (but not qubits) as individual arguments
If the importer encounters a `gate` definition that matches a given custom instruction, it will use that custom information to reconstruct the gate object.
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.
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.
If the importer does encounter an explicit `gate` definition for a built-in custom instruction, it will accept it silently.
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.
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.
Qiskit provides a data attribute for working with OpenQASM 2 programs produced by legacy versions of [Qiskit's OpenQASM 2 exporting capabilities](#qasm2-export).
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).
<span id="qasm2-import-legacy"></span>
#### Example: import a program created by Qiskit's legacy exporter
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.
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.
```python
from qiskit import qasm2
program = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[4];
creg c[4];
h q[0];
cx q[0], q[1];
// 'rxx' is not actually in `qelib1.inc`,
// but Qiskit used to behave as if it were.
rxx(0.75) q[2], q[3];
measure q -> c;
"""
circuit = qasm2.loads(
program,
custom_instructions=qasm2.LEGACY_CUSTOM_INSTRUCTIONS,
)
```
#### Example: use a particular gate class when importing an OpenQASM 2 program
Qiskit cannot, in general, verify if the definition in an OpenQASM 2 `gate` statement corresponds exactly to a Qiskit standard-library gate.
Instead, Qiskit chooses a custom gate using the precise definition supplied.
This can be less efficient that using one of the built-in standard gates, or a user-defined custom gate.
You can manually define `gate` statements with particular classes.
```python
from qiskit import qasm2
from qiskit.circuit import Gate
from qiskit.circuit.library import RZXGate
# Define a custom gate that takes one qubit and two angles.
class MyGate(Gate):
def __init__(self, theta, phi):
super().__init__("my", 1, [theta, phi])
custom_instructions = [
# Link the OpenQASM 2 name 'my' with our custom gate.
qasm2.CustomInstruction("my", 2, 1, MyGate),
# Link the OpenQASM 2 name 'rzx' with Qiskit's
# built-in RZXGate.
qasm2.CustomInstruction("rzx", 1, 2, RZXGate),
]
program = """
OPENQASM 2.0;
gate my(theta, phi) q {
U(theta / 2, phi, -theta / 2) q;
}
gate rzx(theta) a, b {
// It doesn't matter what definition is
// supplied, if the parameters match;
// Qiskit will still use `RZXGate`.
}
qreg q[2];
my(0.25, 0.125) q[0];
rzx(pi) q[0], q[1];
"""
circuit = qasm2.loads(
program,
custom_instructions=custom_instructions,
)
```
#### Example: define a new built-in gate in an OpenQASM 2 program
If the argument `builtin=True` is set, a custom gate does not need to have an associated definition.
```python
from qiskit import qasm2
from qiskit.circuit import Gate
# Define a custom gate that takes one qubit and two angles.
class MyGate(Gate):
def __init__(self, theta, phi):
super().__init__("my", 1, [theta, phi])
custom_instructions = [
qasm2.CustomInstruction("my", 2, 1, MyGate, builtin=True),
]
program = """
OPENQASM 2.0;
qreg q[1];
my(0.25, 0.125) q[0];
"""
circuit = qasm2.loads(
program,
custom_instructions=custom_instructions,
)
```
<span id="custom-classical"></span>
### Define custom classical functions
OpenQASM 2 includes some built-in classical functions to use in gate arguments.
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.
To define a custom classical function, you must supply:
* The *name* of the function as it appears in the OpenQASM 2 program
* The number of floating-point arguments it accepts
* A callable Python object that evaluates the function
All defined custom classical functions are treated as built-in to the OpenQASM 2 language by the importer.
There is no official way within the OpenQASM 2 language to define new functions; this is a Qiskit extension.
#### Example: use custom classical instructions
Here we provide two custom classical functions.
The first is simple, and just adds one to its input.
The second is the function ``math.atan2``, which represents the mathematical operation $\arctan(y/x)$ in a quadrant-aware manner.
```python
import math
import qiskit.qasm2
program = '''
include "qelib1.inc";
qreg q[2];
rx(arctan(pi, 3 + add_one(0.2))) q[0];
cx q[0], q[1];
'''
def add_one(x):
return x + 1
customs = [
# Our `add_one` takes only one parameter.
qasm2.CustomClassical("add_one", 1, add_one),
# `arctan` takes two parameters, and `math.atan2` implements it.
qasm2.CustomClassical("atan2", 2, math.atan2),
]
circuit = qasm2.loads(program, custom_classical=customs)
```
<span id="strict"></span>
### Strict mode
By default, this parser is more relaxed than the official specification.
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.
However, you can use the "letter-of-the-spec" mode with `strict=True`.
<span id="qasm2-export"></span>
## Export a Qiskit circuit to OpenQASM 2
Qiskit can also export a [`QuantumCircuit`](../api/qiskit/qiskit.circuit.QuantumCircuit) to OpenQASM 2.
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.
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.
<Admonition type="warning">
Qiskit's OpenQASM 2 exporter still assumes a legacy, non-standard version of the `"qelib1.inc"` include file.
[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).
</Admonition>
### Example: export a circuit to OpenQASM 2
```python
from qiskit import QuantumCircuit, qasm2
# Define any circuit.
circuit = QuantumCircuit(2, 2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure([0, 1], [0, 1])
# Export to a string.
program = qasm2.dumps(circuit)
# Export to a file.
qasm2.dump(circuit, "my_file.qasm")
```
## Next steps
<Admonition type="tip" title="Recommendations">
- 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.
- See the [OpenQASM 2 Qiskit API](/api/qiskit/qasm2) reference.
- Review the [Verify your program](./debugging-tools) topic.
- Visit the [OpenQASM Live Specification](https://openqasm.com/).
</Admonition>