OpenQASM is an intermediate representation for quantum instructions. The language was first described in a paper published in July 2017, and a reference source code implementation was released as part of IBM's Quantum Information Software Kit (Qiskit) for use with their IBM Q Experience cloud quantum computing platform.
OpenQASM defines its version at the head of a source file as a real number, as in the declaration:
OPENQASM 2.0;
The level of OpenQASM's original published implementations (e.g., Qiskit, infra) is OpenQASM 2.0. The 3.0 level of the specification is currently work in progress and can be viewed at the OpenQASM repository on GitHub.
Tequila functions export_open_qasm
, import_open_qasm
, and import_open_qasm_from_file
work with current OpenQASM version = 2.0
The following is an example of OpenQASM source code from the official library. The program adds two four-bit numbers.
// quantum ripple-carry adder from Cuccaro et al, quant-ph/0410184
OPENQASM 2.0;
include "qelib1.inc";
gate majority a,b,c
{
cx c,b;
cx c,a;
ccx a,b,c;
}
gate unmaj a,b,c
{
ccx a,b,c;
cx c,a;
cx a,b;
}
qreg cin[1];
qreg a[4];
qreg b[4];
qreg cout[1];
creg ans[5];
// set input states
x a[0]; // a = 0001
x b; // b = 1111
// add a to b, storing result in b
majority cin[0],b[0],a[0];
majority a[0],b[1],a[1];
majority a[1],b[2],a[2];
majority a[2],b[3],a[3];
cx a[3],cout[0];
unmaj a[2],b[3],a[3];
unmaj a[1],b[2],a[2];
unmaj a[0],b[1],a[1];
unmaj cin[0],b[0],a[0];
measure b[0] -> ans[0];
measure b[1] -> ans[1];
measure b[2] -> ans[2];
measure b[3] -> ans[3];
measure cout[0] -> ans[4];
Once you have a circuit in Tequila, it is possible to generate its equivalent in OpenQASM code, using the export_open_qasm
function, for example:
import tequila as tq
from numpy import pi
circuit = tq.gates.H(target=[0,1]) + \
tq.gates.Y(target=0) + \
tq.gates.Z(target=2) + \
tq.gates.CX(target=3, control=0) + \
tq.gates.Ry(target=2, angle=pi)
tq.draw(circuit)
0: ───H───Y───────@─── │ 1: ───H───────────┼─── │ 2: ───Z───Ry(π)───┼─── │ 3: ───────────────X───
openqasmcode = tq.export_open_qasm(circuit)
print(openqasmcode)
OPENQASM 2.0; include "qelib1.inc"; qreg q[4]; creg c[4]; h q[0]; h q[1]; y q[0]; z q[2]; cx q[0],q[3]; ry(3.141592653589793) q[2];
It is possible to generate the OpenQASM code for ZX-Calculus, that is, without Y
gates (Y
, Ry
, Cy
, CRy
), if you want to activate this option you need to use the zx_calculus
flag. If enabled, the OpenQASM code will be generated without Y
gates and will instead place their equivalents for each gate.
openqasmcode_no_y = tq.export_open_qasm(circuit, zx_calculus=True)
print(openqasmcode_no_y)
OPENQASM 2.0; include "qelib1.inc"; qreg q[4]; creg c[4]; h q[0]; h q[1]; rz(-1.5707963267948966) q[0]; x q[0]; rz(1.5707963267948966) q[0]; z q[2]; cx q[0],q[3]; rz(-1.5707963267948966) q[2]; rx(3.141592653589793) q[2]; rz(1.5707963267948966) q[2];
If the Tequila circuit is created with variables, the corresponding values must be indicated when exporting it to OpenQASM code:
circuit_var = tq.gates.H(target=[0,1]) + \
tq.gates.Y(target=0) + \
tq.gates.Z(target=2) + \
tq.gates.CX(target=3, control=0) + \
tq.gates.Ry(target=2, angle="var1") + \
tq.gates.Rx(target=0, angle="var2")
tq.draw(circuit_var)
0: ───H───Y───────────────────────────────────────@───Rx(0.318309886183791*pi*f((var2,))_1)─── │ 1: ───H───────────────────────────────────────────┼─────────────────────────────────────────── │ 2: ───Z───Ry(0.318309886183791*pi*f((var1,))_0)───┼─────────────────────────────────────────── │ 3: ───────────────────────────────────────────────X───────────────────────────────────────────
variables = {"var1":2.8, "var2": pi/7}
openqasmcode_var = tq.export_open_qasm(circuit_var, variables=variables)
print(openqasmcode_var)
OPENQASM 2.0; include "qelib1.inc"; qreg q[4]; creg c[4]; h q[0]; h q[1]; y q[0]; z q[2]; cx q[0],q[3]; ry(2.8) q[2]; rx(0.4487989505128276) q[0];
For convenience, it is possible to generate the code in OpenQASM and send the result to a file for external use. The name of the file must be indicated in the filename
parameter:
openqasmcode = tq.export_open_qasm(circuit, filename="MyOpenQASMCode.qasm")
print(openqasmcode)
OPENQASM 2.0; include "qelib1.inc"; qreg q[4]; creg c[4]; h q[0]; h q[1]; y q[0]; z q[2]; cx q[0],q[3]; ry(3.141592653589793) q[2];
Now the MyOpenQASMCode.qasm
file has been created
It is possible to take a code in OpenQASM and use it to generate a circuit in Tequila, using the import_open_qasm
function, for example:
openqasmcode = "OPENQASM 2.0;\n" \
"include \"qelib1.inc\";\n" \
"qreg q1[3];\n" \
"qreg q2[4];\n" \
"creg c[3];\n" \
"x q1[0];\n" \
"y q1[1];\n" \
"h q2[2];\n" \
"cz q1[0],q2[2];\n" \
"ch q2[1],q2[3];\n" \
"ccx q1[0],q1[1],q2[1];\n" \
"rx(pi) q1[1];\n" \
"rz(pi/7) q1[0];\n" \
"cry(1.6*pi) q2[0],q2[1];\n" \
circuit = tq.import_open_qasm(openqasmcode)
tq.draw(circuit)
┌──┐ 0: ────X─────@───@───Rz(0.143π)─── │ │ 1: ────Y─────┼───@───Rx(π)──────── │ │ 2: ──────────┼───┼───@──────────── │ │ │ 3: ─────@────┼───X───Ry(1.6π)───── │ │ 4: ────H┼────Z──────────────────── │ 5: ─────H───────────────────────── └──┘
You can import an OpenQASM code that is not written strictly, but only has the instructions of the gates without containing the definition lines, for this the rigorous
flag is used, for example:
openqasmcode_not_rigorous = "qreg q1[3];\n" \
"qreg q2[4];\n" \
"creg c[3];\n" \
"x q1[0];\n" \
"y q1[1];\n" \
"cz q1[0],q2[2];\n" \
"ccx q1[0],q1[1],q2[1];\n" \
"rz(pi/7) q1[0];\n" \
"cry(1.6*pi) q2[0],q2[1];\n" \
circuit_nr = tq.import_open_qasm(openqasmcode_not_rigorous, rigorous=False)
tq.draw(circuit_nr)
0: ───X───@───@───Rz(0.143π)─── │ │ 1: ───Y───┼───@──────────────── │ │ 2: ───────┼───┼───@──────────── │ │ │ 3: ───────┼───X───Ry(1.6π)───── │ 4: ───────Z────────────────────
In the case of having the OpenQASM code in a file, it is possible to load that file to generate the Tequila circuit from there, for this the import_open_qasm_from_file
function is used:
circuit_from_file = tq.import_open_qasm_from_file(filename="MyOpenQASMCode.qasm")
tq.draw(circuit_from_file)
0: ───H───Y───────@─── │ 1: ───H───────────┼─── │ 2: ───Z───Ry(π)───┼─── │ 3: ───────────────X───
If the OpenQASM code contains custom gates, importing into a Tequila circuit will generate the equivalent with the gates in the proper place with their corresponding parameters, for example:
openqasmcode_custom = "OPENQASM 2.0;\n" \
"include \"qelib1.inc\";\n" \
"gate mycustom a,b,c\n" \
"{\n" \
"cx c,b;\n" \
"cx c,a;\n" \
"}\n" \
"qreg q1[3];\n" \
"qreg q2[4];\n" \
"creg c[3];\n" \
"y q1[1];\n" \
"z q2[2];\n" \
"mycustom q1[0],q2[0],q1[2];\n" \
"h q2[1];\n" \
"mycustom q2[3],q1[1],q2[2];\n" \
"y q2[1];\n"
circuit_cg = tq.import_open_qasm(openqasmcode_custom)
tq.draw(circuit_cg)
┌──┐ 0: ────────X───────── │ 1: ───Y────┼X──────── ││ 2: ───@────@┼──────── │ │ 3: ───X─────┼──────── │ 4: ───H────Y┼──────── │ 5: ───Z─────@────@─── │ 6: ──────────────X─── └──┘