OpenQASM 2 (qiskit.qasm2
)#
Qiskit has support for interoperation with OpenQASM 2.0 programs, both parsing into Qiskit formats
and exporting back to OpenQASM 2. The parsing components live in this module, while currently the
export capabilities are limited to being the QuantumCircuit.qasm()
method.
Nota
OpenQASM 2 is a simple language, and not suitable for general serialisation of Qiskit objects. See some discussion of alternatives below, if that is what you are looking for.
Parsing API#
This module contains two public functions, both of which create a QuantumCircuit
from an
OpenQASM 2 program. load()
takes a filename, while loads()
takes the program itself as a
string. Their internals are very similar, so both offer almost the same API.
- qiskit.qasm2.load(filename, *, include_path=('.',), include_input_directory='append', custom_instructions=(), custom_classical=(), strict=False)[fuente]#
Parse an OpenQASM 2 program from a file into a
QuantumCircuit
. The given path should be ASCII or UTF-8 encoded, and contain the OpenQASM 2 program.- Parámetros:
filename (str | PathLike) – The OpenQASM 2 program in a string.
include_path (Iterable[str | PathLike]) – order of directories to search when evaluating
include
statements.include_input_directory (Literal['append', 'prepend'] | None) – Whether to add the directory of the input file to the
include_path
, and if so, whether to append it to search last, or prepend it to search first. PassNone
to suppress adding this directory entirely.custom_instructions (Iterable[CustomInstruction]) – any custom constructors that should be used for specific gates or opaque instructions during circuit construction. See Specifying custom instructions for more.
custom_classical (Iterable[CustomClassical]) – any custom classical functions that should be used during the parsing of classical expressions. See Specifying custom classical functions for more.
strict (bool) – whether to run in strict mode.
- Devuelve:
A circuit object representing the same OpenQASM 2 program.
- Tipo del valor devuelto:
- qiskit.qasm2.loads(string, *, include_path=('.',), custom_instructions=(), custom_classical=(), strict=False)[fuente]#
Parse an OpenQASM 2 program from a string into a
QuantumCircuit
.- Parámetros:
string (str) – The OpenQASM 2 program in a string.
include_path (Iterable[str | PathLike]) – order of directories to search when evaluating
include
statements.custom_instructions (Iterable[CustomInstruction]) – any custom constructors that should be used for specific gates or opaque instructions during circuit construction. See Specifying custom instructions for more.
custom_classical (Iterable[CustomClassical]) – any custom classical functions that should be used during the parsing of classical expressions. See Specifying custom classical functions for more.
strict (bool) – whether to run in strict mode.
- Devuelve:
A circuit object representing the same OpenQASM 2 program.
- Tipo del valor devuelto:
Both of these loading functions also take an argument include_path
, which is an iterable of
directory names to use when searching for files in include
statements. The directories are
tried from index 0 onwards, and the first match is used. The import qelib1.inc
is treated
specially; it is always found before looking in the include path, and contains exactly the content
of the paper describing the OpenQASM 2 language. The gates
in this include file are mapped to circuit-library gate objects defined by Qiskit.
Specifying custom instructions#
You can extend the quantum components of the OpenQASM 2 language by passing an iterable of
information on custom instructions as the argument custom_instructions
. In files that have
compatible definitions for these instructions, the given constructor
will be used in place of
whatever other handling qiskit.qasm2
would have done. These instructions may optionally be
marked as builtin
, which causes them to not require an opaque
or gate
declaration, but
they will silently ignore a compatible declaration. Either way, it is an error to provide a custom
instruction that has a different number of parameters or qubits as a defined instruction in a parsed
program. Each element of the argument iterable should be a particular data class:
- class qiskit.qasm2.CustomInstruction(name, num_params, num_qubits, constructor, builtin=False)[fuente]#
Information about a custom instruction that should be defined during the parse.
The
name
,num_params
andnum_qubits
fields are self-explanatory. Theconstructor
field should be a callable object with signature*args -> Instruction
, where each of thenum_params
args
is a floating-point value. Most of the built-in Qiskit gate classes have this form.There is a final
builtin
field. This is optional, and if set true will cause the instruction to be defined and available within the parsing, even if there is no definition in any included OpenQASM 2 file.
This can be particularly useful when trying to resolve ambiguities in the global-phase conventions of an OpenQASM 2 program. See OpenQASM 2 Phase Conventions for more details.
Specifying custom classical functions#
Similar to the quantum extensions above, you can also extend the processing done to classical
expressions (arguments to gates) by passing an iterable to the argument custom_classical
to either
loader. This needs the name
(a valid OpenQASM 2 identifier), the number num_params
of
parameters it takes, and a Python callable that implements the function. The Python callable must
be able to accept num_params
positional floating-point arguments, and must return a float or
integer (which will be converted to a float). Builtin functions cannot be overridden.
- class qiskit.qasm2.CustomClassical#
Information about a custom classical function that should be defined in mathematical expressions.
The given callable must be a Python function that takes num_params floats, and returns a float. The name is the identifier that refers to it in the OpenQASM 2 program. This cannot clash with any defined gates.
Strict mode#
Both of the loader functions have an optional «strict» mode. By default, this parser is a little
bit more relaxed than the official specification: it allows trailing commas in parameter lists;
unnecessary (empty-statement) semicolons; the OPENQASM 2.0;
version statement to be omitted; and
a couple of other quality-of-life improvements without emitting any errors. You can use the
letter-of-the-spec mode with strict=True
.
Errors#
This module defines a generic error type that derives from QiskitError
that can be used as a
catch when you care about failures emitted by the interoperation layer specifically.
- exception qiskit.qasm2.QASM2Error(*message)[fuente]#
A general error raised by the OpenQASM 2 interoperation layer.
Set the error message.
In cases where the lexer or parser fails due to an invalid OpenQASM 2 file, the conversion functions will raise a more specific error with a message explaining what the failure is, and where in the file it occurred.
- exception qiskit.qasm2.QASM2ParseError(*message)[fuente]#
An error raised because of a failure to parse an OpenQASM 2 file.
Set the error message.
Similarly, a failure during the export of an OpenQASM 2 program will raise its own subclass of
QASM2Error
:
Examples#
Use loads()
to import an OpenQASM 2 program in a string into a QuantumCircuit
:
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()
┌───┐ ┌─┐
q_0: ┤ H ├──■──┤M├───
└───┘┌─┴─┐└╥┘┌─┐
q_1: ─────┤ X ├─╫─┤M├
└───┘ ║ └╥┘
c: 2/═══════════╩══╩═
0 1
You can achieve the same thing if the program is stored in a file by using load()
instead,
passing the filename as an argument:
import qiskit.qasm2
circuit = qiskit.qasm2.load("myfile.qasm")
OpenQASM 2 files can include other OpenQASM 2 files via the include
statement. You can
influence the search path used for finding these files with the include_path
argument to both
load()
and loads()
. By default, only the current working directory is searched.
import qiskit.qasm2
program = """
include "other.qasm";
// ... and so on
"""
circuit = qiskit.qasm2.loads(program, include_path=("/path/to/a", "/path/to/b", "."))
For load()
only, there is an extra argument include_input_directory
, which can be used to
either 'append'
, 'prepend'
or ignore (None
) the directory of the loaded file in the
include path. By default, this directory is appended to the search path, so it is tried last, but
you can change this.
import qiskit.qasm2
filenames = ["./subdirectory/a.qasm", "/path/to/b.qasm", "~/my.qasm"]
# Search the directory of each file before other parts of the include path.
circuits = [
qiskit.qasm2.load(filename, include_input_directory="prepend") for filename in filenames
]
# Override the include path, and don't search the directory of each file unless it's in the
# absolute path list.
circuits = [
qiskit.qasm2.load(
filename,
include_path=("/usr/include/qasm", "~/qasm/include"),
include_input_directory=None,
)
for filename in filenames
]
Sometimes you may want to influence the Gate
objects that the importer emits for given
named instructions. Gates that are defined by the statement include "qelib1.inc";
will
automatically be associated with a suitable Qiskit circuit-library gate, but you can extend this:
from qiskit.circuit import Gate
from qiskit.qasm2 import loads, CustomInstruction
class MyGate(Gate):
def __init__(self, theta):
super().__init__("my", 2, [theta])
class Builtin(Gate):
def __init__(self):
super().__init__("builtin", 1, [])
program = """
opaque my(theta) q1, q2;
qreg q[2];
my(0.5) q[0], q[1];
builtin q[0];
"""
customs = [
CustomInstruction(name="my", num_params=1, num_qubits=2, constructor=MyGate),
# Setting 'builtin=True' means the instruction doesn't require a declaration to be usable.
CustomInstruction("builtin", 0, 1, Builtin, builtin=True),
]
circuit = loads(program, custom_instructions=customs)
Similarly, you can add new classical functions used during the description of arguments to gates,
both in the main body of the program (which come out constant-folded) and within the bodies of
defined gates (which are computed on demand). Here we provide a Python version of atan2(y, x)
,
which mathematically is \(\arctan(y/x)\) but correctly handling angle quadrants and infinities,
and a custom add_one
function:
import math
from qiskit.qasm2 import loads, CustomClassical
program = """
include "qelib1.inc";
qreg q[2];
rx(atan2(pi, 3 + add_one(0.2))) q[0];
cx q[0], q[1];
"""
def add_one(x):
return x + 1
customs = [
# `atan2` takes two parameters, and `math.atan2` implements it.
CustomClassical("atan2", 2, math.atan2),
# Our `add_one` takes only one parameter.
CustomClassical("add_one", 1, add_one),
]
circuit = loads(program, custom_classical=customs)
OpenQASM 2 Phase Conventions#
As a language, OpenQASM 2 does not have a way to specify the global phase of a complete program, nor
of particular gate definitions. This means that parsers of the language may interpret particular
gates with a different global phase than what you might expect. For example, the de facto
standard library of OpenQASM 2 qelib1.inc
contains definitions of u1
and rz
as follows:
gate u1(lambda) q {
U(0, 0, lambda) q;
}
gate rz(phi) a {
u1(phi) a;
}
In other words, rz
appears to be a direct alias for u1
. However, the interpretation of
u1
is specified in equation (3) of the paper describing the language as
where the \(\sim\) symbol denotes equivalence only up to a global phase. When parsing OpenQASM
2, we need to choose how to handle a distinction between such gates; u1
is defined in the prose
to be different by a phase to rz
, but the language is not designed to represent this.
Qiskit’s default position is to interpret a usage of the standard-library rz
using
RZGate
, and a usage of u1
as using the phase-distinct U1Gate
. If you wish
to use the phase conventions more implied by a direct interpretation of the gate
statements in
the header file, you can use CustomInstruction
to override how Qiskit builds the circuit.
For the standard qelib1.inc
include there is only one point of difference, and so the override
needed to switch its phase convention is:
from qiskit import qasm2
from qiskit.circuit.library import PhaseGate
from qiskit.quantum_info import Operator
program = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[1];
rz(pi / 2) q[0];
"""
custom = [
qasm2.CustomInstruction("rz", 1, 1, PhaseGate),
]
This will use Qiskit’s PhaseGate
class to represent the rz
instruction, which is
equal (including the phase) to U1Gate
:
Operator(qasm2.loads(program, custom_instructions=custom))
Operator([[1.000000e+00+0.j, 0.000000e+00+0.j],
[0.000000e+00+0.j, 6.123234e-17+1.j]],
input_dims=(2,), output_dims=(2,))
Legacy Compatibility#
QuantumCircuit.from_qasm_str()
and from_qasm_file()
used to make a few
additions on top of the raw specification. Qiskit originally tried to use OpenQASM 2 as a sort of
serialisation format, and expanded its behaviour as Qiskit expanded. The new parser under all its
defaults implements the specification more strictly.
The complete legacy code-paths are
from qiskit.converters import ast_to_dag, dag_to_circuit
from qiskit.qasm import Qasm
def from_qasm_file(path: str):
dag_to_circuit(ast_to_dag(Qasm(filename=path).parse()))
def from_qasm_str(qasm_str: str):
dag_to_circuit(ast_to_dag(Qasm(data=qasm_str).parse()))
In particular, in the legacy importers:
- the include_path is effectively:
<qiskit>/qasm/libs
, where<qiskit>
is the root of the installedqiskit
package;the current working directory.
- there are additional instructions defined in
qelib1.inc
: csx a, b
Controlled \(\sqrt X\) gate, corresponding to
CSXGate
.cu(theta, phi, lambda, gamma) c, t
The four-parameter version of a controlled-\(U\), corresponding to
CUGate
.rxx(theta) a, b
Two-qubit rotation around the \(XX\) axis, corresponding to
RXXGate
.rzz(theta) a, b
Two-qubit rotation around the \(ZZ\) axis, corresponding to
RZZGate
.rccx a, b, c
The double-controlled \(X\) gate, but with relative phase differences over the standard Toffoli gate. This should correspond to the Qiskit gate
RCCXGate
, but the legacy converter wouldn’t actually output this type.rc3x a, b, c, d
The triple-controlled \(X\) gate, but with relative phase differences over the standard definition. Corresponds to
RC3XGate
.c3x a, b, c, d
The triple-controlled \(X\) gate, corresponding to
C3XGate
.c3sqrtx a, b, c, d
The triple-controlled \(\sqrt X\) gate, corresponding to
C3SXGate
.c4x a, b, c, d, e
The quadruple-controlled \(X\) gate., corresponding to
C4XGate
.
- there are additional instructions defined in
if any
opaque
orgate
definition was given for the namedelay
, they attempt to output aDelay
instruction at each call. To function, this expects a definition compatible withopaque delay(t) q;
, where the timet
is given in units ofdt
. The importer will raise errors on construction if there was not exactly one parameter and one qubit, or if the parameter is not integer-valued.the additional scientific-calculator functions
asin
,acos
andatan
are available.the parsed grammar is effectively the same as the strict mode of the new importers.
You can emulate this behaviour in load()
and loads()
by setting include_path
appropriately (try inspecting the variable qiskit.__file__
to find the installed location), and
by passing a list of CustomInstruction
instances for each of the custom gates you care
about. To make things easier we make three tuples available, which each contain one component of
a configuration that is equivalent to Qiskit’s legacy converter behaviour.
- qiskit.qasm2.LEGACY_CUSTOM_INSTRUCTIONS#
A tuple containing the extra custom_instructions that Qiskit’s legacy built-in converters used if
qelib1.inc
is included, and there is any definition of adelay
instruction. The gates in the paper version ofqelib1.inc
anddelay
all require a compatible declaration statement to be present within the OpenQASM 2 program, but Qiskit’s legacy additions are all marked as builtins since they are not actually present in any include file this parser sees.
- qiskit.qasm2.LEGACY_CUSTOM_CLASSICAL#
A tuple containing the extra custom_classical functions that Qiskit’s legacy built-in converters use beyond those specified by the paper. This is the three basic inverse trigonometric functions: \(\asin\), \(\acos\) and \(\atan\).
- qiskit.qasm2.LEGACY_INCLUDE_PATH#
A tuple containing the exact include_path used by the legacy Qiskit converter.
On all the gates defined in Qiskit’s legacy version of qelib1.inc
and the delay
instruction, it does not matter how the gates are actually defined and used, the legacy importer
will always attempt to output its custom objects for them. This can result in errors during the
circuit construction, even after a successful parse. There is no way to emulate this buggy
behaviour with qiskit.qasm2
; only an include "qelib1.inc";
statement or the
custom_instructions argument can cause built-in Qiskit instructions to be used, and the signatures
of these match each other.
Nota
Circuits imported with load()
and loads()
with the above legacy-compatibility settings
should compare equal to those created by Qiskit’s legacy importer, provided no non-qelib1.inc
user gates are defined. User-defined gates are handled slightly differently in the new importer,
and while they should have equivalent definition
fields on inspection, this
module uses a custom class to lazily load the definition when it is requested (like most Qiskit
objects), rather than eagerly creating it during the parse. Qiskit’s comparison rules for gates
will see these two objects as unequal, although any pass through transpile()
for a
particular backend should produce the same output circuits.
Alternatives#
The parser components of this module started off as a separate PyPI package: qiskit-qasm2. This package at version 0.5.3 was vendored into Qiskit Terra 0.24. Any subsequent changes between the two packages may not necessarily be kept in sync.
There is a newer version of the OpenQASM specification, version 3.0, which is described at
https://openqasm.com. This includes far more facilities for high-level classical programming.
Qiskit has some rudimentary support for OpenQASM 3 already; see qiskit.qasm3
for that.
OpenQASM 2 is not a suitable serialization language for Qiskit’s QuantumCircuit
. This
module is provided for interoperability purposes, not as a general serialization format. If that is
what you need, consider using qiskit.qpy
instead.