307 lines
12 KiB
Plaintext
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>
|