OpenQASM 3

Overview

QuantumRingsLib can compose a QuantumCircuit from OpenQASM 3.x content.

There are two supported entry points:

  • QuantumCircuit.from_qasm_file(path) — compose a circuit from an OpenQASM 3 file.

  • QuantumCircuit.from_qasm_str(qasm_str) — compose a circuit from an OpenQASM 3 string.

For the recommended end-to-end workflow (provider → backend → circuit → run), see Circuits.

QuantumRingsLib also provides a OpenQASM helper class for importing qasm3-compliant files/strings into a quantum circuit. The OpenQASM.load(...) and OpenQASM.loads(...) methods accept additional parameters (for example, an include path and a strict flag). See the API reference for details.

Existing OpenQASM 3 files can also be submitted directly to BackendV2.run without first composing a QuantumCircuit. This is required for circuits that contain control flow referencing mid-circuit measurements, or that use input / output directives.

Note

OpenQASM 3 files use OPENQASM 3; or OPENQASM 3.0; as their version header and include "stdgates.inc"; (double quotes) for the standard gate library.

Import from a file

Use QuantumCircuit.from_qasm_file(...) to compose a circuit from an OpenQASM 3 file. An optional global_phase argument is supported:

import math
from QuantumRingsLib import QuantumCircuit

circuit_name = "my_openQASM_file.qasm"
global_phase = math.pi / 8

qc = QuantumCircuit.from_qasm_file(circuit_name, global_phase)

Or using the OpenQASM helper class:

from QuantumRingsLib import OpenQASM

circuit_name = "my_openQASM_file.qasm"
oq = OpenQASM()
qc = oq.load(circuit_name)

This example creates a 10-qubit GHZ state with a for loop. Because the measurement occurs at the end and no classical bits are referenced inside the loop, this can be imported using OpenQASM.load or QuantumCircuit.from_qasm_file:

/*
 Creates a 10-qubit GHZ state
 File name: ghz.qasm
*/
OPENQASM 3;
include "stdgates.inc";

qubit[10] qreg1;
output bit[10] creg1;

h qreg1[0];
for uint[64] i in [0:sizeof(qreg1)-2]
{
   cx qreg1[i], qreg1[i+1];
}
measure qreg1 -> creg1;

This is a full end-to-end version of the GHZ example, including provider setup and Windows CUDA path configuration:

import os
import sys
import platform

#
# Assumes that you have set up the OS environmental variables QR_TOKEN and
# QR_ACCOUNT with your Quantum Rings account information.
#

my_token = os.environ["QR_TOKEN"]
my_name = os.environ["QR_ACCOUNT"]

my_backend = "amber_quantum_rings"
precision = "single"

if platform.system() == "Windows":
   cuda_path = os.getenv("CUDA_PATH", "")
   if "" == cuda_path:
      cuda_path = "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v13.0\\bin\\x64"
   else:
      if "13" in cuda_path:
         cuda_path += "\\bin\\x64"
      else:
         cuda_path += "\\bin"
   os.add_dll_directory(cuda_path)

import QuantumRingsLib
from QuantumRingsLib import QuantumRegister, AncillaRegister, ClassicalRegister
from QuantumRingsLib import QuantumCircuit
from QuantumRingsLib import QuantumRingsProvider
from QuantumRingsLib import job_monitor
from QuantumRingsLib import JobStatus
from QuantumRingsLib import JobV1, Result

provider = QuantumRingsProvider(token=my_token, name=my_name)
backend = provider.get_backend(backend=my_backend)

shots = 1000
program_file = "C:\\OpenQASM\\ghz.qasm"

job = backend.run(program_file, shots=shots, mode="sync")
result = job.result()
counts = result.get_counts()
print(counts)

Import from a string

Use QuantumCircuit.from_qasm_str(...) to compose a circuit from an OpenQASM 3 string:

from QuantumRingsLib import QuantumCircuit

qasm_str = open("circuit.qasm", "r", encoding="utf-8").read()
qc = QuantumCircuit.from_qasm_str(qasm_str)

Or using the OpenQASM helper class:

from QuantumRingsLib import OpenQASM

qasm_str = open("circuit.qasm", "r", encoding="utf-8").read()
oq = OpenQASM()
qc = oq.loads(qasm_str)

Direct execution

Pass the file path as the first argument:

from QuantumRingsLib import QuantumRingsProvider, job_monitor

provider = QuantumRingsProvider(token=my_token, name=my_name)
backend = provider.get_backend(backend="amber_quantum_rings")

circuit_name = r"C:\\path\\to\\circuit.qasm"
job = backend.run(circuit_name, mode="sync", shots=1000)
job_monitor(job, quiet=True)

result = job.result()
counts = result.get_counts()
print(counts)

Note

BackendV2.run does not return a QuantumCircuit object. If you need to inspect or modify the circuit before execution, use the import path instead.

The following example circuit references a measured classical bit inside a conditional block, so it must be submitted directly to BackendV2.run:

OPENQASM 3.0;
include "stdgates.inc";

extern double my_random();

qubit q;
bit c;

h q;
c = measure q;
if (c == 1) {
   double r = my_random();
}

Using input and output directives

OpenQASM 3 supports input and output keywords to pass parameters into a program and control which registers are returned. Only the bit data type can be marked as output. Parameters are passed to BackendV2.run via the inputs argument as a list of dictionaries.

The following example defines a typed input parameter and an output register:

OPENQASM 3.0;
include "stdgates.inc";
qubit[10] qreg1;
output bit[10] creg1;
input angle my_angle;

rz(my_angle) qreg1[0];
measure qreg1 -> creg1;

The parameters are passed via the inputs argument to BackendV2.run:

import math

data = [
    {"param": "my_angle", "values": [math.pi / 2]}
]

job = backend.run("my_circuit.qasm", shots=1000, inputs=data)

This is a longer example with multiple parameters of different types, including arrays and complex numbers:

OPENQASM 3.0;
include "stdgates.inc";

qubit[10] qreg1;
output bit[10] creg1;
bit[5] creg2;

input array[int[64], 2, 3]          my_int_array;
input array[uint[64], 2, 3]         my_uint_array;
input array[float[64], 2, 2]        my_float_array;
input const array[complex[float[64]], 3] my_complex_array;
input angle my_test_angle;
input bool  my_bool;

print("Integer Array:", my_int_array, "\n");
print("Unsigned integer Array:", my_uint_array, "\n");
print("Float Array:", my_float_array, "\n");
print("Complex Array:", my_complex_array, "\n");
print("Angle:", my_test_angle, "\n");
print("bool:", my_bool, "\n");

x qreg1[0];
x qreg1[2];
x qreg1[4];
x qreg1[6];
x qreg1[8];

measure qreg1 -> creg1;
import math

data = [
   {"param": "my_int_array",     "values": [[-1, -2, -3], [1, 2, 3]]},
   {"param": "my_uint_array",    "values": [[1, 2, 3], [4, 5, 6]]},
   {"param": "my_float_array",   "values": [[1.1, 2.2], [4.4, 5.5]]},
   {"param": "my_complex_array", "values": [(3+4j), (5+6j), (7+8j)]},
   {"param": "my_bool",          "values": [True]},
   {"param": "my_test_angle",    "values": [math.pi / 2]},
]

job = backend.run("C:\\OpenQASM\\testinputoutput.qasm", shots=1, inputs=data)

Important

Always mark the bit registers you want returned with the output keyword. If no output directive is present, mid-circuit measurement results may not be what you expect. See below.

The output directive and mid-circuit measurements

If a circuit performs mid-circuit measurements into multiple bit registers, only registers marked output are returned. Without the output keyword, all bit registers are concatenated in the result, which may produce an unexpected bitstring.

Without output (incorrect result):

OPENQASM 3;
include "stdgates.inc";
qubit[10] input_1;
bit[10] output_1;        -- not marked output

x input_1[0];
x input_1[1];
output_1[4:5] = measure input_1[0:1];
output_1[0:1] = measure input_1[2:3];

-- Produces: '000011000011'  (unexpected)

With output (correct result):

OPENQASM 3;
include "stdgates.inc";
qubit[10] input_1;
output bit[10] output_1;  -- marked output

x input_1[0];
x input_1[1];
output_1[4:5] = measure input_1[0:1];
output_1[0:1] = measure input_1[2:3];

-- Produces: '0000111111'   (expected)

Exporting OpenQASM 3

You can dump a QuantumCircuit to OpenQASM 3 format using qc.qasm3():

qasm3_txt = qc.qasm3()
print(qasm3_txt)

To save directly to a file, pass a filename argument. See the API reference for details.

Note

The exported circuit is always flattened. Any loops, subroutines, or user-defined gates in the original file will not be preserved. The flattened circuit will only represent the executed path.

Common errors

  • E32 — The file does not comply with the OpenQASM 3.0 standard. Check version header, quote style on include, and register declaration syntax.

  • E83 — Circuit must be run using BackendV2.run. Occurs when a circuit with control flow referencing mid-circuit classical bits is passed to OpenQASM.load or QuantumCircuit.from_qasm_file.

  • E101input parameters declared but not provided. Pass all required parameters via the inputs argument to BackendV2.run.

  • RuntimeError — Can be raised if there is a problem with the instructions in the OpenQASM 3 file or string.

See also