Skip to main contentIBM Quantum Documentation Preview

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()
output

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")

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.

Warning

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 arctan(y/x)\arctan(y/x) 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.

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(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

Recommendations