Parsing

This package currently 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 the same API.

qiskit_qasm2.load(filename: str | PathLike, include_path: Iterable[str | PathLike] = ('.',), include_input_directory: Literal['append', 'prepend'] | None = 'append', custom_instructions: Iterable[CustomInstruction] = (), custom_classical: Iterable[CustomClassical] = (), strict: bool = False)

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.

Parameters:
  • filename (str) – A filename for a file that contains an OpenQASM 2 program.

  • include_file_directory (None, "append" or "prepend".) – 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. Pass None to suppress adding this directory entirely.

Returns:

A circuit object representing the same OpenQASM 2 program.

Return type:

QuantumCircuit

qiskit_qasm2.loads(string: str, include_path: Iterable[str | PathLike] = ('.',), custom_instructions: Iterable[CustomInstruction] = (), custom_classical: Iterable[CustomClassical] = (), strict: bool = False)

Parse an OpenQASM 2 program from a string into a QuantumCircuit.

Parameters:

string (str) – The OpenQASM 2 program in a string.

Returns:

A circuit object representing the same OpenQASM 2 program.

Return type:

QuantumCircuit

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 standard gates provided by Qiskit.

By default, the two loaders operate in a permissive mode. This allows some syntactical niceties that do not change the meaning of OpenQASM 2 programs, but are technically against the specification. This includes not requiring the version statement, allowing empty statements (often seen as extraneous semicolons), and allowing trailing commas in various lists. You can swap to a precise implementation of the spec by making the strict parameter to both true.

You can extend the OpenQASM 2 language by supplying constructors for instructions whose definitions cannot be expressed in OpenQASM 2, and by supplying extra functions that should be available to the “scientific calculator” functionality.

Extra instructions are supplied 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: str, n_params: int, n_qubits: int, constructor: Callable[[Unpack], Instruction], builtin: bool = False)

Information about a custom instruction that should be defined during the parse.

The name, n_params and n_qubits fields are self-explanatory. The constructor field should be a callable object with signature *args -> Instruction, where each of the n_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.

Further scientific-calculator functions are supplied by providing an iterable of information on the desired function, and a Python implementation of it. You must supply the name, number of floating-point parameters, and the function itself. The function should take in that many floating-point values as separate arguments, and return a float. The information is supplied as a particular data class:

class qiskit_qasm2.CustomClassical(name, n_params, callable, /)

Information about a custom classical function that should be defined in mathematical expressions.

The given callable must be a Python function that takes n_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.

In cases where the lexer or parser fails due to an invalid OpenQASM 2 file, the conversion functions will raise an error with a message explaining what the failure is, and where in the file it occurred.

exception qiskit_qasm2.QASM2ParseError

An error raised during parsing of OpenQASM 2 programs.

Qiskit Compatibility

Qiskit’s QuantumCircuit.from_qasm_str() and from_qasm_file() have a few additions on top of the raw specification, as Qiskit originally tried to use OpenQASM 2 as a sort of serialisation format, and expanded their behaviour as Qiskit expanded. This parser under all its defaults implements the specification more precisely.

In particular, in the Qiskit importers:

  • the allowed grammar is effectively our strict mode; Qiskit is quite inflexible.

  • the include_path is:
    1. <qiskit>/qasm/libs, where <qiskit> is the root of the installed qiskit package;

    2. the current working directory.

  • there are additional instructions defined in qelib1.inc:
    u0(gamma) a

    This is ambiguous. Its provided definition suggests it is a no-op and that the gamma parameter is ignored, but a comment above it describes it as a delay for gamma times the length of the shortest single-qubit gate. In compatibility mode, we require gamma to be an integer, and set its definition to that many Qiskit IGates.

    u(theta, phi, lambda) a

    A synonym for the OpenQASM 2 builtin gate U. Corresponds to UGate.

    p(lambda) a

    A synonym for the paper’s u1. Corresponds to PhaseGate, which is a synonym of U1Gate.

    sx a

    \(\sqrt X\) gate, corresponding to SXGate.

    sxdg a

    \(\sqrt X^\dagger\) gate, corresponding to SXdgGate.

    swap a, b

    The swap gate, corresponding to SwapGate.

    cswap a, b, c

    The controlled swap gate, corresponding to CSwapGate.

    crx(theta) a, b

    Controlled rotation around the \(X\) axis. Corresponds to CRXGate.

    cry(theta) a, b

    Controlled rotation around the \(Y\) axis. Corresponds to CRYGate.

    cp(lambda) a, b

    Controlled phase gate, which is a synonym for the paper gate cu1. Corresponds to CPhaseGate.

    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 arond the \(XX\) axis, corresponding to RXXGate.

    rzz(theta) a, b

    Two-qubit rotation arond 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 Qiskit converter won’t actually output this type.

    rc3x a, b, c, d

    The triple-controlled \(X\) gate, but with relative phase differences over the standard definition. Should correspond 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. Should correspond to C3SXGate.

    c4x a, b, c, d, e

    The quadruple-controlled \(X\) gate. Should correspond to C4XGate.

  • if any opaque or gate definition is given for the name delay, they will attempt to output a Delay instruction at each call. To function, this expects a definition compatible with opaque delay(t) q;, where the time t is given in units of dt. The importer will raise an error on calls to the instruction if there are actually not exactly one parameter and one qubit, or if the parameter is not integer-valued.

  • the additional scientific-calculator functions asin, acos and atan are available.

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 available one tuple containing all the above instructions (using the correspondences that Qiskit forgets as well) that you can supply to custom_instructions, and one containing the additional scientific-calculator functions.

qiskit_qasm2.QISKIT_CUSTOM_INSTRUCTIONS

A tuple containing the extra custom_instructions that Qiskit’s built-in converters use if qelib1.inc is included, and there is any definition of a delay instruction. The gates in the paper version of qelib1.inc and delay all require a compatible declaration statement to be present within the OpenQASM 2 program, but Qiskit’s additions are all marked as builtins since they are not actually present in any include file this parser sees.

qiskit_qasm2.QISKIT_CUSTOM_CLASSICAL

A tuple containing the extra custom_classical functions that Qiskit’s built-in converters use beyond those specified by the paper. This is the three basic inverse trigonometric functions.

On all the gates defined in Qiskit’s version of qelib1.inc and the delay instruction, it does not matter how the gates are actually defined and used, Qiskit 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 in 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.

Note

Circuits imported with load() and loads() with the above Qiskit-compability settings should compare equal to those created by Qiskit’s importers, provided no non-qelib1.inc user gates are defined. User-defined gates are handled slightly differently between this package and Qiskit, and while they should have equivalent definition fields on inspection, this package 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 qiskit.transpile() for a particular backend should produce the same output circuits.