OpenQASM 2 and the Qiskit SDK
The Qiskit SDK provides some tools for converting between OpenQASM representations of quantum programs, and the QuantumCircuit class.
Import an OpenQASM 2 program into Qiskit
Two functions import OpenQASM 2 programs into Qiskit.
These are qasm2.load()
, which takes a filename, and qasm2.loads()
, which takes the OpenQASM 2 program as a string.
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 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:
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()
Example: import an OpenQASM 2 program from a file
Use load()
to import an OpenQASM 2 program from a file into a QuantumCircuit:
import qiskit.qasm2
circuit = qiskit.qasm2.load("myfile.qasm")
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(opens in a new tab).
Qiskit will use the built-in gates in the 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.
You can tell the importer to use specific 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.
As of Qiskit SDK 1.0, Qiskit's OpenQASM 2 exporter (see Export a Qiskit circuit to OpenQASM 2) 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 to resolve this problem.
This discrepancy is legacy behavior of Qiskit, and it will be resolved in a later release of Qiskit(opens in a new tab).
To pass information about a custom instruction to the OpenQASM 2 importer, use the qasm2.CustomInstruction
class.
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
, 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
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.
This is qasm2.LEGACY_CUSTOM_INSTRUCTIONS
, which can be given as the custom_instructions
argument to qasm2.load()
and qasm2.loads()
.
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
to easily tell the importer to use the same set of gates that Qiskit's OpenQASM 2 exporter previously used.
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.
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.
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,
)
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()
and qasm2.loads()
, with the 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 in a quadrant-aware manner.
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)
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
.
Export a Qiskit circuit to OpenQASM 2
Qiskit can also export a QuantumCircuit
to OpenQASM 2.
You use the function qasm2.dump()
to write to a file, and 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()
, a location to write the output to.
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(opens in a new tab), 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.
Example: export a circuit to OpenQASM 2
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
- Learn how to generate OpenQASM code in the Explore gates and circuits with the Quantum Composer(opens in a new tab) tutorial.
- See the OpenQASM 2 Qiskit API reference.
- Review the Verify your program topic.
- Visit the OpenQASM Live Specification(opens in a new tab).