Skip to Content
DocsUnitaryLab Simulator User ManualCircuit ToolsOpenQASM and Python Import/Export

OpenQASM and Python Import/Export

Circuits can be converted between in-memory strings, files, and Python reconstruction code. OpenQASM is suitable for exchanging circuits with other tools; Python export generates readable UnitaryLab construction code.

from unitarylab import Circuit qc = Circuit(2) qc.h(0) qc.cx(0, 1) qasm3 = qc.to_qasm() qasm2 = qc.to_qasm2() restored = Circuit.from_qasm(qasm3) qc.to_qasm_file("circuit.qasm") restored_from_file = Circuit.from_qasm_file("circuit.qasm")

to_qasm() exports OpenQASM 3.0 by default. from_qasm() parses OpenQASM 2.0 or 3.0 according to the version declaration. decompose controls block expansion; transpile=True transpiles to a basic gate basis before export.

Public interfaces

InterfacePurpose
to_qasm(...)Return an OpenQASM 3.0 string
to_qasm2(qreg_name="q", creg_name="c", decompose=False, transpile=False, gates_to_unroll=None, transpile_basis="default")Return an OpenQASM 2.0 string
from_qasm(code, gate_storage="python")Parse a version declaration and create a circuit
from_qasm_file(path, gate_storage="python")Read a circuit from a file
to_qasm_file(path, ...)Write OpenQASM 3.0 to a file
to_python(...) / to_python_file(...)Generate UnitaryLab or CUDA-Q Python code
to_cudaq()Return a CUDA-Q kernel (requires CUDA-Q)

Both to_qasm() and to_qasm2() support decomposition and transpilation options. decompose=True is equivalent to expanding one level; a positive integer specifies the number of levels. transpile=True fully expands block gates, regardless of the decompose depth. transpile_basis and gates_to_unroll control gate-basis transpilation before export.

The lower-level unitarylab.backend.qasm module provides gate_sequence_to_qasm*, qasm*_to_gate_sequence, and circuit_from_gate_sequence; use these when directly handling GateSequence objects.

Supported formats and constraints

  • Common single-qubit, parameterized, controlled, SWAP, measurement, and custom gate operations are supported.
  • Parameters must be numeric; classical control flow such as if and while is not supported.
  • OpenQASM 3.0 uses a UnitaryLab extension for unitary gates with stored matrices. UnitaryLab can restore them, but this is not a standard interchange format. OpenQASM 2.0 has no arbitrary-matrix syntax; use to_qasm2(transpile=True) to decompose them into a gate basis first.
  • Non-contiguous block gates may be inlined; not every custom gate supports a complete round trip.
  • Read OpenQASM only from trusted sources and limit input file size. Parsing does not execute embedded Python, but very large circuits can still consume substantial memory.

Python source

to_python(*, framework="unitarylab", circuit_name="Unitarylab Circuit", variable_name="circuit") generates UnitaryLab reconstruction code by default. It supports common gates, block gates, measurement, and gates with stored matrices; other gates are reconstructed using QuantumGate information. circuit_name=None reuses the original circuit name. to_python_file(filepath, ...) writes the code and returns the resolved pathlib.Path.

import numpy as np codegen_qc = Circuit(2) codegen_qc.p(np.pi, 0) codegen_qc.ry(np.pi / 2, 0) codegen_qc.cx(0, 1) print(codegen_qc.to_python())

After exporting, perform a round-trip check by comparing gate counts and measurement mappings, and compare matrices for small circuits without measurements.

CUDA-Q export

from unitarylab import Circuit bell = Circuit(2) bell.h(0) bell.cx(0, 1) cudaq_source = bell.to_python(framework="cudaq", variable_name="kernel") print(cudaq_source) # With CUDA-Q installed, construct an executable kernel directly: # kernel = bell.to_cudaq()

Generating source with to_python(framework="cudaq") does not require CUDA-Q to be installed. Executing the generated code or calling to_cudaq() requires this optional dependency. to_cudaq() returns a non-parameterized CUDA-Q kernel whose gate parameters are fixed to the numeric values stored in the current circuit.

Export expands block gates. Arbitrary unitary matrices are limited to one or two qubits. Circuits without measurement are allowed; if measurement is present, it must be terminal and map every qubit in order as 0→0, 1→1, …. Mid-circuit measurement, partial measurement, and reordered classical mappings are unsupported. Unsupported gates raise an exception.

Last updated on