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:
- 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:
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:
<qiskit>/qasm/libs
, where<qiskit>
is the root of the installedqiskit
package;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
IGate
s.u(theta, phi, lambda) a
A synonym for the OpenQASM 2 builtin gate
U
. Corresponds toUGate
.p(lambda) a
A synonym for the paper’s
u1
. Corresponds toPhaseGate
, which is a synonym ofU1Gate
.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 toCPhaseGate
.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
.
- there are additional instructions defined in
if any
opaque
orgate
definition is given for the namedelay
, they will 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 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
andatan
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 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 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.