OpenQASM 3 conversions
from qbraid_qir.qasm3 import qasm3_to_qir
program = """
OPENQASM 3;
include "stdgates.inc";
qubit[2] q;
bit[2] c;
h q[0];
cx q[0], q[1];
measure q[0] -> c[0];
measure q[1] -> c[1];
"""
module = qasm3_to_qir(program, name="bell")
ir = str(module)
; ModuleID = 'bell'
source_filename = "bell"
%Qubit = type opaque
%Result = type opaque
define void @main() #0 {
entry:
call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
ret void
}
declare void @__quantum__qis__h__body(%Qubit*)
declare void @__quantum__qis__cnot__body(%Qubit*, %Qubit*)
declare void @__quantum__qis__mz__body(%Qubit*, %Result*) #1
attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="custom" "required_num_qubits"="2" "required_num_results"="2" }
attributes #1 = { "irreversible" }
!llvm.module.flags = !{!0, !1, !2, !3}
!0 = !{i32 1, !"qir_major_version", i32 1}
!1 = !{i32 7, !"qir_minor_version", i32 0}
!2 = !{i32 1, !"dynamic_qubit_management", i1 false}
!3 = !{i32 1, !"dynamic_result_management", i1 false}Execute the QIR program using the qir-runner command line tool:
$ qir-runner -f bell.bcCurrently Supported Constructs
The qbraid_qir.qasm3.qasm3_to_qir() converter supports the following OpenQASM 3 constructs:
1. Register Declarations
OpenQASM declarations of all forms qreg, qubit, bit, and creg are supported.
OPENQASM 3;
// qubit declaration
qubit q1;
qubit[2] q2;
qreg q3[3];
qubit[1] q4;
// bit declaration
bit c1;
bit[2] c2;
creg c3[3];
bit[1] c4;2. Quantum Measurements
OpenQASM measurements are supported which involve single qubit measurement and full register measurements. Range based measurements are not supported currently.
OPENQASM 3;
qubit[2] q1;
qubit[5] q2;
qubit q3;
bit[2] c1;
bit c2;
// supported
c1 = measure q1;
measure q1 -> c1;
c2[0] = measure q3[0];
//ERROR: not supported
c1[0:2] = measure q1[0:2];3. Quantum Reset
Resets are supported on declared quantum registers in all forms.
OPENQASM 3;
include "stdgates.inc";
// qubit declarations
qubit q1;
qubit[2] q2;
qreg q3[3];
// reset operations
reset q1;
reset q2[1];
reset q3[2];
reset q3[:2];4. Quantum Gates
pyqir._nativegates are supported along with support forU3and U2 gates. TheU[x]gates are defined in terms of existingrxandrzgates according to the decomposition present on the Qiskit UGate documentation and PhaseGate documentation.- Full set of the openqasm3 “stdgates.inc” gates are supported. These gates are decomposed with the help of the
pyqir._nativegate set and subsequently converted to QIR.
5. Quantum Barriers
Barriers are supported only if they are placed on ALL the qubits in the circuit. For example:
qubit q[2];
U(0.1, 0.2, 0.3) q[0];
// Barrier on all qubits
barrier q;
// ERROR: Barrier on a subset of qubits
barrier q[0];6. Custom Quantum Gates
Gates defined by users are supported as long as they are defined in terms of pyqir._native gate set. Identifier mapping in gate parameter expressions is not supported at the moment. Example:
OPENQASM 3.0;
include "stdgates.inc";
// Supported
gate custom2(g) s {
h s;
rz(g) s;
}
// Supported
gate custom(a,b,c) p, q {
custom2(a) q;
h p;
cx p,q;
rx(a) q;
ry(0.5/0.1) q;
}
qubit[2] q;
custom(2 + 3 - 1/5, 0.1, 0.3) q[0], q[1];7. Simple Branching Statements
Since QIR supports branching on a measurement result, single bit branching statements are supported at the moment. General boolean expressions and support for branching on full registers will be added in future. For example:
OPENQASM 3;
include "stdgates.inc";
qubit[4] q;
bit[4] c;
h q;
measure q -> c;
// supported
if (c[0]) {
x q[0];
cx q[0], q[1];
}
if (c[1] == 1) {
cx q[1], q[2];
}
if (!c[2]) {
h q[2];
}
// ERROR: not supported
if (c == 8) {
x q[0];
}
// ERROR: not supported
int[4] element;
if (element > 5) {
y q[1];
}8. Expressions
- General expression evaluation involving literals and constants is supported.
- Expressions involving constants and variables are fully supported.
- Expressions with arrays are also supported (in global scopes).
OPENQASM 3;
qubit q;
// supported
rx(1.57) q;
rz(3-2*3) q;
rz(3-2*3\*(8/2)) q;
rx(-1.57) q;
rx(4%2) q;
OPENQASM 3;
// scalar value assignment and declaration
int[32] a = 5;
float[32] b;
b = 3.14;
// constant value assignment and declaration
const int[32] c_int = 5;
const float[32] c_float = 3.141592;
// arbitary expressions
int[32] x = a + 5*c_int;OPENQASM 3;
// static initialization
array[int[32], 3, 2] arr_int = { {1, 2}, {3, 4}, {5, 6} };
array[float[32], 3, 2] arr_float32 = { {1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0} };
// assignment of individual elements
int a = 2;
arr_int[1][1] = a; // arr_int = { {1, 2}, {3, 2}, {5, 6} }
// array elements in expressions
float[32] b = arr_float32[1][1] * 3.1415; // b = 4.0*3.1415 = 12.566
qubit q;
rx(b) q; // rx(12.566) is applied on qubit q9. Variables and Types
- Scalar types
int,uint,float,boolandbitwith arbitrary sizes are supported. arraytype is supported forint,uint,float,boolandbit.complexandangletypes are not supported yet.
OPENQASM 3;
// declarations
int a;
uint b;
int[2] c = 1;
uint[13] d = 2;
float[32] my_pi = 3.14;
// assignments and expressions
a = 5 \* c;
b = 6 - d;
OPENQASM 3;
// declarations
array[int[32], 3, 2] arr_int = { {1, 2}, {3, 4}, {5, 6} };
array[float[32], 3, 2] arr_float32;
// element assignment
float[32] c = 4.5;
arr_float32[1][1] = c; // arr_float32 = { {None, None}, {None, 4.5}, {None, None} }
arr_int[1][1] = 5; // arr_int = { {1, 2}, {3, 5}, {5, 6} }
// expressions
float[32] d = arr_float32[1][1] * 3.1415; // d = 4.5*3.1415 = 14.1377510. Loops
Both for and while loops are supported at the moment. For example:
OPENQASM 3.0;
include "stdgates.inc";
qubit[2] q;
int[32] i;
for int[32] i in [0:2] {
h q[i];
}
OPENQASM 3.0;
include "stdgates.inc";
qubit[4] q;
bit[4] c;
int j = 0;
h q;
for int i in [0:2]{
cx q[i], q[i+1];
h q[j];
j += 1;
}
h q[j];
measure q -> c;OPENQASM 3.0;
include "stdgates.inc";
qubit[4] q;
int i = 0;
while (i < 3) {
h q[i];
cx q[i], q[i+1];
i += 1;
}OPENQASM 3.0;
include "stdgates.inc";
qubit[2] q;
int i = 0;
while (i < 2) {
for int j in {0, 1} {
h q[j];
}
i += 1;
}11. Aliasing
- Aliasing quantum registers is supported and can be used to refer to the same quantum register with different names.
- Aliases are currently supported in global scope of the qasm3 program, with support for function scopes coming soon.
OPENQASM 3.0;
include "stdgates.inc";
qubit[5] q;
let myqreg0 = q;
let myqreg1 = q[1];
let myqreg2 = q[1:];
let myqreg3 = q[:4];
let myqreg4 = q[1:4];
let myqreg5 = q[1:2:4];
let myqreg6 = q[{0, 1}];
x myqreg0[0];
h myqreg1;
cx myqreg2[0], myqreg2[1];
cx myqreg3[2], myqreg3[3];
ccx myqreg4;
swap myqreg5[0], myqreg5[1];
cz myqreg6;
OPENQASM 3;
include "stdgates.inc";
qubit[4] q;
bit[4] c;
h q;
measure q -> c;
if (c[0]) {
let alias = q[0:2];
x alias[0];
cx alias[0], alias[1];
}
if (c[1] == 1) {
cx q[1], q[2];
}
if (!c[2]) {
h q[2];
}12. Subroutines
We support QASM3 subroutines in the QIR converter
Definitions
Subroutines are supported in QASM3 and can be defined using the def keyword and are only allowed in the global scope.
OPENQASM 3;
include "stdgates.inc";
def my_function(qubit q) { // void function
h q;
return;
}OPENQASM 3;
include "stdgates.inc";
def my_function(qubit q) -> int[32] {
h q;
return 1;
}Calling subroutines
Subroutines can be called from within the main program or from other subroutines.
OPENQASM 3.0;
include "stdgates.inc";
def my_function(qubit a, float[32] b) {
rx(b) a;
float[64] c = 2*b;
rx(c) a;
return;
}
qubit q;
bit c;
float[32] r = 3.14;
my_function(q, r);OPENQASM 3;
include "stdgates.inc";
def my_function(qubit q) -> bool{
h q;
return true;
}
qubit q;
bool b = my_function(q);OPENQASM 3;
include "stdgates.inc";
def my_function(qubit q) {
h q;
return;
}
def my_function_2(qubit[2] q) {
my_function(q[1]);
return;
}
qubit[2] q;
my_function_2(q);
Arguments and Return Types
-
Quantum arguments of type
qubitare supported in subroutines. -
Classical arguments are supported for any scalar type and can return values of any scalar type.
-
All argument validations mentioned in the OpenQASM3 specification are supported.
mixed argumentsOPENQASM 3; include "stdgates.inc"; def my_function(qubit q, int[32] a, float[32] b) { rz(a) q; rx(b) q; return; } qubit q; int[32] n = 5; float[32] b = 3.14; my_function(q, n, b);simple return typeOPENQASM 3; include "stdgates.inc"; def my_function(qubit q) -> bool { h q; return true; } qubit q; bool b = my_function(q);testing return valuesOPENQASM 3; include "stdgates.inc"; def my_function(qubit q) -> float[32] { h q; return 3.14; } def my_function_2(qubit q, float[32] r) { rx(r) q; return; } qubit[2] q; float[32] r1 = my_function(q[0]); my_function_2(q[0], r1); array[float[32], 1, 1] r2 = {{3.14}}; my_function_2(q[1], r2[0,0]);
Future Work
- Add
mutableandreadonlyarrays as classical arguments in a subroutines - Support looping statements inside a subroutine - Support switch statements inside a subroutine - Support for aliasing in a subroutine
13. Switch
Switch statements in QASM3 are supported in the QIR converter. These are defined according to the OpenQASM3 spec and support all the features mentioned in the specification.
OPENQASM 3.0;
include "stdgates.inc";
const int i = 4;
const int j = 4;
qubit q;
switch(i) {
case 6, j {
x q;
}
default {
z q;
}
}OPENQASM 3.0;
include "stdgates.inc";
const int i = 1;
qubit q;
switch(i) {
case 1,3,5,7 {
int j = 4; // definition inside scope
switch(j) {
case 1,3,5,7 {
x q;
}
case 2,4,6,8 {
y q; // this will be executed
}
default {
z q;
}
}
}
case 2,4,6,8 {
y q;
}
default {
z q;
}
}Thanks for your feedback.

