OpenQASM 3 Importer for Qiskit

This project is a temporary importer for parsing simple OpenQASM 3 programmes and converting them into Qiskit QuantumCircuit objects.

We fully expect Qiskit’s capability to grow in this field, and for a similar importer to be merged into mainline Qiskit. This project is a stop-gap measure, while various technical details are decided on the Qiskit side, as switching parsing frameworks and so on is far more complex for code brought into Qiskit, as the stability guarantees are much stronger.

API

This package providers two public methods: parse() and convert(). The complete path of taking a string representation of an OpenQASM 3 programme to QuantumCircuit is parse(), while convert() takes an AST Program node from the reference OpenQASM 3 Python package, and converts that to a QuantumCircuit.

qiskit_qasm3_import.parse(string: str, /) QuantumCircuit

Wrapper around convert(), which first parses the OpenQASM 3 program into AST form, and then converts the output to Qiskit format.

qiskit_qasm3_import.convert(node: Program) QuantumCircuit

Convert a parsed OpenQASM 3 program in AST form, into a Qiskit QuantumCircuit.

The converter in this module is quite limited, since Qiskit does not yet support all the features of OpenQASM 3. When something unsupported is encountered during an attempted import, a ConversionError will be raised.

exception qiskit_qasm3_import.ConversionError(message, node: QASMNode | None = None)

Raised when an error occurs converting from the AST representation into a QuantumCircuit. This is often due to OpenQASM 3 constructs that have no equivalent in Qiskit.

Internals

Conversion mechanism

The internals of the convert() function use a tree visitor, which subclasses openqasm3.visitor.QASMVisitor. This is principally an internal implementation detail.

class qiskit_qasm3_import.converter.ConvertVisitor

Internal visitor of an OpenQASM 3 AST to convert it to a QuantumCircuit. The complete conversion is done by calling convert() on a openqasm3.ast.Program node.

The other methods on this class are internal only, and generally not part of the public interface.

convert(node: Program, *, source: str | None = None) QuantumCircuit

Convert a program node into a QuantumCircuit.

If given, source is a string containing the OpenQASM 3 source code that was parsed into node. This is used to generated improved error messages. A State containing information about the conversion is returned. The QuantumCircuit is stored in property thereof named circuit.

In addition, there is a subvisitor to handle constant-folding and the resolution of symbols and expressions into equivalent Qiskit types.

class qiskit_qasm3_import.expression.ValueResolver(context: State, strict: bool = True)

A resolver for value-like objects that have exact Qiskit representations. Everything handled here should resolve into a single Qiskit object (or occasionally a compound of such objects, like lists of bits).

These resolvers are intended to be short-lived. Their instance state is the instance of State created in ConvertVisitor.convert().

resolve(node: Expression) Tuple[Any, Type]

The entry point to the resolver, resolving the AST node into a 2-tuple of a relevant Qiskit type, and the Type that it is an instance of.

Conditions are handled slightly differently; to avoid needing to support the entire type system of OpenQASM 3 before Terra does, a special-case function handles the allowable components of the expression tree in conditions before delegating the rest of the calculation to ExpressionResolver.resolve() above.

qiskit_qasm3_import.expression.resolve_condition(node: Expression, context: State) Tuple[Clbit, bool] | Tuple[Iterable[Clbit], int]

A resolver for conditions that can be converted into Qiskit’s very basic equality form of either Clbit == bool or ClassicalRegister == int.

This effectively just handles very special outer cases, then delegates the rest of the work to a ValueResolver.

Type system

Internally we use a reduced and modified type system to represent the intermediate values we are working over. Everything is a subclass of the abstract base class.

class qiskit_qasm3_import.types.Type

An internal representation of the OpenQASM 3 type system values, or at least that parts of it that Terra has some sort of support for. The reference AST does not have a nice unified object to use here; there is ClassicalType but no quantum equivalent (since it’s implicit).

This class is just a typing base, and should never be instantiated. Its subclasses will be, though.

abstract pretty() str

A pretty string representation of the type, useful for debugging.

There are several different types within this. Some of these represent the types of real values that have corresponding Qiskit or just standard Python objects, and some are types used during inference or to represent errors.

final class qiskit_qasm3_import.types.Bit

A single bit. This corresponds to Terra’s Clbit.

final class qiskit_qasm3_import.types.BitArray(size: int)

An array of bits. This roughly corresponds to Terra’s ClassicalRegister.

final class qiskit_qasm3_import.types.Qubit

A single qubit. This corresponds to Terra’s Qubit.

final class qiskit_qasm3_import.types.QubitArray(size: int)

An array of qubits. This roughly corresponds to Terra’s QuantumRegister.

final class qiskit_qasm3_import.types.HardwareQubit

A hardware qubit. This corresponds a hardware qubits referenced in Terra’s TranspilerLayout.

final class qiskit_qasm3_import.types.Bool(const: bool)

A Boolean value. This is only used by Terra in for single-bit conditions.

final class qiskit_qasm3_import.types.Int(const: bool = False, size: int | None = None)

An integer value. This is generally only encountered as a constant and so is represented by a Python integer, but can also be the type of the Qiskit Parameter used to represent for-loop variables.

final class qiskit_qasm3_import.types.Uint(const: bool = False, size: int | None = None)

An unsigned integer value.

final class qiskit_qasm3_import.types.Float(const: bool = False, size: int | None = None)

A floating-point type. Terra can use this either in a constant form as a Python float, or as a Parameter.

final class qiskit_qasm3_import.types.Angle(const: bool = False, size: int | None = None)

An angle type. OpenQASM 3 makes a large distinction between angle and float (the OpenQASM angle is integer-like), but Terra currently treats them as interchangeable. This might change in the future.

final class qiskit_qasm3_import.types.Duration(const: bool)

A duration. Right now, this is only recognised in constant form, which is represented within ValueResolver as a 2-tuple of a float and its unit.

final class qiskit_qasm3_import.types.Range(base: Type)

A range selector. The inner type is the join of the types of the start and end values.

final class qiskit_qasm3_import.types.Sequence(base: Type)

A general sequence of values. This is represented internally as a list or tuple of the contained type.

final class qiskit_qasm3_import.types.Gate(n_classical: int, n_quantum: int)

The type of a gate. Since the classical parameters of gates have a fixed type in OpenQASM 3, this just stores the counts of the classical and quantum arguments.

final class qiskit_qasm3_import.types.Error

A zero type that represents an error during type checking.

final class qiskit_qasm3_import.types.Never

The bottom type. There are no valid values of this type, as it can never be instantiated. This is used during inference in cases where multiple types must combined into their join, but some of the elements have missing values, such as a range that has a start but no stop value.