QASM converter
from mpqp.qasm import *
To allow interoperability with different providers, we leverage the OpenQASM standard, broadly adopted by the community. We describe here how to convert OpenQASM from a version to another, and how to generate supported providers’ circuits from OpenQASM code.
Note
To learn more about how we generate OpenQASM code from a
QCircuit
, have a look at the
to_other_language()
.
OpenQASM2.0 and OpenQASM3.0 utility
The latest version of OpenQASM (3.0, started in 2020) has been released by a
collaborative group from IBM Quantum, AWS Quantum Computing, Zapata Computing,
Zurich Instruments, and the University of Oxford. This version extends OpenQASM
2.0, adding advanced features and modifying parts of the syntax and grammar.
Some aspects of OpenQASM 2.0 are not fully backward compatible, hence the need
to keep track of instructions requiring custom definitions in Instr
.
To aid in the transition, this module provides conversion functions for moving between OpenQASM 2.0 and 3.0, as well as managing user-defined gates and handling code transformations. Key functionalities include:
- OpenQASM 2.0 to 3.0 Conversion:
convert_instruction_2_to_3()
: Converts individual instructions from QASM 2.0 syntax to 3.0, handling specific syntax adjustments.parse_openqasm_2_file()
: Splits OpenQASM 2.0 code into individual instructions, preserving gate declarations to ensure proper handling during conversion.open_qasm_2_to_3()
: Main function for converting OpenQASM 2.0 code to 3.0. It adds necessary library includes.open_qasm_file_conversion_2_to_3()
: Reads from the specified file, and outputs the converted file in QASM 2.0 syntax.
- OpenQASM 3.0 to 2.0 Conversion:
convert_instruction_3_to_2()
: Converts individual instructions from QASM 3.0 syntax to 2.0, handling specific syntax adjustments.parse_openqasm_3_file()
: Splits OpenQASM 3.0 code into individual instructions, preserving gate declarations to ensure proper handling during conversion.open_qasm_3_to_2()
: Main function for converting OpenQASM 3.0 code to 2.0. It adds necessary library includes, tracks cumulative global phases.open_qasm_file_conversion_3_to_2()
: Reads from the specified file, and outputs the converted file in QASM 2.0 syntax.
- User-Defined Gate Handling:
UserGate Class: Represents user-defined gates in OpenQASM. Each
UserGate
instance stores the gate’s name, parameters, qubits, and instruction sequence.parse_user_gates()
: Extracts and stores user-defined gate definitions from OpenQASM code, removing them from the main code to allow separate handling. Custom gates are identified using theGATE_PATTERN
regex and stored asUserGate
instances.remove_user_gates()
: Replaces calls to user-defined gates in OpenQASM code with their expanded definitions. This function relies onparse_user_gates
to retrieve gate definitions, and it substitutes parameter and qubit values within each gate’s body instructions for accurate expansion.
- Supporting Functions:
open_qasm_hard_includes()
: Combines multiple OpenQASM files into a single file with resolved includes, simplifying code management for projects with multiple source files.
- class Instr(value)[source]
Bases:
Enum
Special instruction for which the definition needs to included in the file.
- BRAKET_CUSTOM_INCLUDE = 16
- C3SQRTX = 13
- C3X = 11
- C4X = 12
- CSX = 3
- CU3 = 5
- OQASM2_ALL_STDGATES = 14
- OQASM3_ALL_STDGATES = 15
- QE_LIB = 2
- RC3X = 10
- RCCX = 9
- RXX = 8
- RZZ = 7
- STD_LIB = 1
- SXDG = 6
- U0 = 4
- class UserGate(name, parameters, qubits, instructions)[source]
Bases:
object
Represents a custom user-defined quantum gate with specified parameters, qubits, and instructions. This class serves as a template for custom gates that can be used in a quantum circuit.
- Parameters
name (str) – The name of the user-defined gate.
parameters (list[str]) – A list of parameter names that the gate requires (e.g., angles, coefficients).
qubits (list[str]) – A list of qubit identifiers that the gate operates on.
instructions (list[str]) – A list of instructions (quantum operations) that define the gate’s behavior.
- convert_instruction_2_to_3(instr, included_instr, included_tree_current, defined_gates, path_to_main=None, translation_warning=True)[source]
Some instructions changed name from QASM 2 to QASM 3, also the way to import files changed slightly. This function operates those changes on a single instruction.
- Parameters
instr (str) – Instruction to be upgraded.
included_instr (set[mpqp.qasm.open_qasm_2_and_3.Instr]) – Some instructions need new imports, in order to keep track of which instruction are already.
scope (imported in the overall) – instructions is passed and modified along.
included (a dictionary of already) – instructions is passed and modified along.
included_tree_current (Node) – Current Node in the file inclusion tree.
defined_gates (set[str]) – Set of custom gates already defined.
path_to_main (Optional[str]) – Path to the main folder from which include paths are described.
translation_warning (bool) –
- Returns
The upgraded instruction and the potential code to add in the header as the second element.
- Return type
tuple[str, str]
- convert_instruction_3_to_2(instr, included_instr, included_tree_current, defined_gates, path_to_main=None, gphase=0.0)[source]
Some instructions changed name from QASM 2 to QASM 3, also the way to import files changed slightly. This function operates those changes on a single instruction.
- Parameters
instr (str) – Instruction to be upgraded.
included_instr (set[mpqp.qasm.open_qasm_2_and_3.Instr]) – Some instructions need new imports, in order to keep track of which instruction are already.
scope (imported in the overall) – instructions is passed and modified along.
included (a dictionary of already) – instructions is passed and modified along.
included_tree_current (Node) – Current Node in the file inclusion tree.
defined_gates (set[str]) – Set of custom gates already defined.
path_to_main (Optional[str]) – Path to the main folder from which include paths are described.
gphase (float) – The global phase of a circuit, which is not handled in OpenQASM2.
- Returns
The upgraded instruction, the potential code to add in the header as the second element and the global phase of the circuit.
- Return type
tuple[str, str, float]
Example
>>> convert_instruction_3_to_2("phase(0.3) q1[0];",set(),Node(""),set()) ('u1(0.3) q1[0];;\n', '', 0.0)
- open_qasm_2_to_3(code, included_tree_current_node=None, path_to_file=None, defined_gates=None, translation_warning=True)[source]
Converts an OpenQASM code from version 2.0 and 3.0.
This function will also recursively go through the imported files to translate them too. It is a partial conversion (the
opaque
keyword is not handled and comments are stripped) for helping building temporary bridges between different platforms that use different versions.- Parameters
code (str) – String containing the OpenQASM 2.0 code and instructions.
included_tree_current_node (Optional[Node]) – Current Node in the file inclusion tree.
path_to_file (Optional[str]) – Path to the location of the file from which the code is coming (useful for locating imports).
defined_gates (Optional[set[str]]) – Set of custom gates already defined.
translation_warning (bool) –
- Returns
Converted OpenQASM code in the 3.0 version.
- Return type
str
Example
>>> qasm2_str = '''OPENQASM 2.0; ... qreg q[2]; ... creg c[2]; ... h q[0]; ... cx q[0],q[1]; ... measure q[0] -> c[0]; ... measure q[1] -> c[1]; ... ''' >>> print(open_qasm_2_to_3(qasm2_str)) OPENQASM 3.0; include "stdgates.inc"; qubit[2] q; bit[2] c; h q[0]; cx q[0],q[1]; c[0] = measure q[0]; c[1] = measure q[1];
- open_qasm_3_to_2(code, included_tree_current_node=None, path_to_file=None, defined_gates=None, gphase=0.0)[source]
Converts an OpenQASM 3.0 code back to OpenQASM 2.0.
This function will also recursively go through the imported files to translate them too. It is a partial conversion (the
opaque
,for
,switch
, and many others keywords are not handled) for helping building temporary bridges between different platforms using different versions.- Parameters
code (str) – String containing the OpenQASM 3.0 code.
included_tree_current_node (Optional[Node]) – Current Node in the file inclusion tree.
path_to_file (Optional[str]) – Path to the location of the file from which the code is coming (useful for locating imports).
defined_gates (Optional[set[str]]) – Set of custom gates already defined.
gphase (float) – The global phase of a circuit, which is not handled in OpenQASM2.
- Returns
Converted OpenQASM code in the 2.0 version.
- Return type
tuple[str, float]
Example
>>> qasm3_str = '''OPENQASM 3.0; ... qubit[2] q; ... bit[2] c; ... h q[0]; ... cx q[0],q[1]; ... c[0] = measure q[0]; ... c[1] = measure q[1]; ... ''' >>> qasm_2, gphase = open_qasm_3_to_2(qasm3_str) >>> print(qasm_2) OPENQASM 2.0; include "qelib1.inc"; qreg q[2]; creg c[2]; h q[0]; cx q[0],q[1]; measure q[0] -> c[0]; measure q[1] -> c[1];
- open_qasm_file_conversion_2_to_3(path, translation_warning=True)[source]
Converts an OpenQASM code in a file from version 2.0 and 3.0.
This function is a shorthand to initialize
open_qasm_2_to_3()
with the correct values.- Parameters
path (str) – Path to the file containing the OpenQASM 2.0 code, and eventual imports.
translation_warning (bool) –
- Returns
Converted OpenQASM code in the 3.0 version.
- Return type
str
Examples
>>> example_dir = "examples/scripts/qasm_files/" >>> with open(example_dir + "main.qasm", "r") as f: ... print(f.read()) OPENQASM 2.0; include "include1.qasm"; include "include2.qasm"; qreg q[2]; creg c[2]; h q[0]; cx q[0],q[1]; gate2 q[0]; gate3 q[0], q[1]; measure q[0] -> c[0]; measure q[1] -> c[1]; >>> print(open_qasm_file_conversion_2_to_3(example_dir + "main.qasm")) OPENQASM 3.0; include 'include1_converted.qasm'; include 'include2_converted.qasm'; include "stdgates.inc"; qubit[2] q; bit[2] c; h q[0]; cx q[0],q[1]; gate2 q[0]; gate3 q[0], q[1]; c[0] = measure q[0]; c[1] = measure q[1]; >>> with open(example_dir + "include1_converted.qasm", "r") as f: ... print(f.read()) OPENQASM 3.0; include "stdgates.inc"; gate gate2 a { u3(pi, -pi/2, pi/2) a; } >>> with open(example_dir + "include2_converted.qasm", "r") as f: ... print(f.read()) OPENQASM 3.0; include "stdgates.inc"; gate gate3 a, b { u3(0, -pi/2, pi/3) a; cz a, b; }
- open_qasm_file_conversion_3_to_2(path)[source]
Converts an OpenQASM code in a file from version 3.0 and 2.0.
This function is a shorthand to initialize
open_qasm_3_to_2()
with the correct values.- Parameters
path (str) – Path to the file containing the OpenQASM 3.0 code, and eventual imports.
- Returns
Converted OpenQASM code in the 2.0 version.
- Return type
tuple[str, float]
Examples
>>> example_dir = "examples/scripts/qasm_files/" >>> with open(example_dir + "main_converted.qasm", "r") as f: ... print(f.read()) OPENQASM 3.0; include 'include1_converted.qasm'; include 'include2_converted.qasm'; include "stdgates.inc"; qubit[2] q; bit[2] c; h q[0]; cx q[0],q[1]; gate2 q[0]; gate3 q[0], q[1]; c[0] = measure q[0]; c[1] = measure q[1]; >>> qasm_2, gphase = open_qasm_file_conversion_3_to_2(example_dir + "main_converted.qasm") >>> print(qasm_2) OPENQASM 2.0; include 'include1_converted_converted.qasm'; include 'include2_converted_converted.qasm'; include "qelib1.inc"; qreg q[2]; creg c[2]; h q[0]; cx q[0],q[1]; gate2 q[0]; gate3 q[0], q[1]; measure q[0] -> c[0]; measure q[1] -> c[1]; >>> with open(example_dir + "include1_converted_converted.qasm", "r") as f: ... print(f.read()) OPENQASM 2.0; include "qelib1.inc"; gate gate2 a { u3(pi, -pi/2, pi/2) a; } >>> with open(example_dir + "include2_converted_converted.qasm", "r") as f: ... print(f.read()) OPENQASM 2.0; include "qelib1.inc"; gate gate3 a, b { u3(0, -pi/2, pi/3) a; cz a, b; }
- open_qasm_hard_includes(code, included_files, path_to_file=None, is_openqasm_header_included=False, remove_included=True)[source]
Converts an OpenQASM code (2.0 and 3.0) to use no includes, but writes every instruction in previously included files, directly in the code returned.
- Parameters
code (str) – String containing the OpenQASM code and instructions.
included_files (set[str]) – The set of files already included, used to avoid duplicate imports and circular dependencies. This set should be initialized with the name of the root file you started with.
path_to_file (Optional[str]) – Path used to localize files that are included.
is_openqasm_header_included (bool) – Boolean used to only include once the OpenQASM header.
remove_included (bool) –
- Returns
Include-less OpenQASM code.
- Return type
str
Example
>>> examples_folder = "tests/qasm/qasm_examples" >>> filename = examples_folder + "/with_include.qasm" >>> with open(filename) as f: ... print(open_qasm_hard_includes(f.read(), {filename}).strip("\n")) gate csx a, b { ctrl @ sx a, b; }
- parse_openqasm_2_file(code)[source]
Splits a complete OpenQASM2 program into individual instructions.
- Parameters
code (str) – The complete OpenQASM 2.0 program.
- Returns
List of instructions.
- Return type
list[str]
Note
we do not check for correct syntax, it is assumed that the code is well formed.
- parse_openqasm_3_file(code)[source]
Splits a complete OpenQASM 3 program into individual instructions.
- Parameters
code (str) – The complete OpenQASM 3.0 program.
- Returns
List of instructions.
- Return type
list[str]
Note
We do not check for correct syntax; it is assumed that the code is well-formed.
- parse_user_gates(qasm_code, skip_qelib1=False)[source]
Parses user gate definitions from QASM code.
- Parameters
qasm_code (str) – The QASM code containing user gate definitions.
skip_qelib1 (bool) –
- Returns
A tuple containing a dictionary of user gate definitions and the QASM string stripped of it’s user gate definitions.
- Return type
tuple[list[mpqp.qasm.open_qasm_2_and_3.UserGate], str]
Example
>>> qasm_str = '''gate rzz(theta) a,b { ... cx a,b; ... u1(theta) b; ... cx a,b; ... } ... qubit[3] q; ... creg c[2]; ... rzz(0.2) q[1], q[2]; ... c2[0] = measure q[2];''' >>> user_gates, qasm_code = parse_user_gates(qasm_str) >>> print(user_gates) [UserGate(name=rzz, parameters=['theta'], qubits=['a', 'b'], instructions=['cx a,b;', 'u1(theta) b;', 'cx a,b;'])] >>> print(qasm_code) qubit[3] q; creg c[2]; rzz(0.2) q[1], q[2]; c2[0] = measure q[2];
- qasm_code(instr)[source]
Return the string corresponding of the declaration of the instruction in parameter. It is also used to return the whole standard library string when we hard include it.
- Parameters
instr (Instr) – Instr for which we want the corresponding OpenQASM code.
- Returns
OpenQASM definition of
instr
.- Return type
str
- remove_include_and_comment(qasm_code)[source]
Removes lines that start with ‘include’ or comments (starting with ‘\’) from a given OpenQASM code string.
- Parameters
qasm_code (str) – The input QASM code as a string.
- Returns
The modified QASM code with ‘include’ lines and comments removed.
- Return type
str
Example
>>> qasm_code = '''include "stdgates.inc"; ... qreg q[2]; ... // This is a comment ... H q[0];''' >>> print(remove_include_and_comment(qasm_code)) qreg q[2]; H q[0];
- remove_user_gates(qasm_code, skip_qelib1=False)[source]
Replaces instances of user gates with their definitions in the given QASM code. This uses
parse_user_gates()
to separate the gate definitions from the rest of the code.- Parameters
qasm_code (str) – The QASM code containing user gate calls.
skip_qelib1 (bool) –
- Returns
The QASM code with user gate calls replaced by their definitions.
- Return type
str
Example
>>> qasm_str = '''gate MyGate a, b { ... h a; ... cx a, b; ... } ... qreg q[3]; ... creg c[2]; ... MyGate q[0], q[1]; ... measure q -> c;''' >>> print(remove_user_gates(qasm_str)) qreg q[3]; creg c[2]; h q[0]; cx q[0], q[1]; measure q -> c;
From OpenQASM to the providers
We use OpenQASM as the standard allowing us to translate between various SDKs, each conversion method is listed bellow.
Qiskit
The main object used to perform quantum computations in Qiskit is the
QuantumCircuit
. Qiskit naturally supports OpenQASM 2.0 to instantiate a
circuit. One can remark that few remote devices also support OpenQASM 3.0 code,
this is not generalized yet to the whole library and device. We call the
function qasm2_to_Qiskit_Circuit()
to generate the circuit from the qasm
code.
- qasm2_to_Qiskit_Circuit(qasm_str)[source]
Converting a OpenQASM 2.0 code into a Qiskit QuantumCircuit.
- Parameters
qasm_str (str) – A string representing the OpenQASM 2.0 code.
- Returns
A QuantumCircuit equivalent to the QASM code in parameter.
- Return type
QuantumCircuit
Example
>>> qasm_code = ''' ... OPENQASM 2.0; ... include "qelib1.inc"; ... qreg q[2]; ... h q[0]; ... cx q[0], q[1]; ... ''' >>> circuit = qasm2_to_Qiskit_Circuit(qasm_code) >>> print(circuit) ┌───┐ q_0: ┤ H ├──■── └───┘┌─┴─┐ q_1: ─────┤ X ├ └───┘
MyQLM
The myQLM library allows the user to instantiate a myQLM Circuit
from an
OpenQASM 2.0 code. MyQLM is able to parse most of the standard gates, and allows
us to complete the missing gates by linking them to already defined ones. We
call the function qasm2_to_myqlm_Circuit()
to generate the circuit from
the qasm code.
- qasm2_to_myqlm_Circuit(qasm_str)[source]
Converting a OpenQASM 2.0 code into a QLM Circuit.
- Parameters
qasm_str (str) – A string representing the OpenQASM 2.0 code.
- Returns
A Circuit equivalent to the QASM code in parameter.
- Return type
Circuit
Example
>>> qasm_code = ''' ... OPENQASM 2.0; ... qreg q[2]; ... h q[0]; ... cx q[0], q[1]; ... ''' >>> circuit = qasm2_to_myqlm_Circuit(qasm_code) >>> circuit.display(batchmode=True) ┌─┐ ─┤H├─●─ └─┘ │ │ ┌┴┐ ────┤X├ └─┘
Braket
Amazon Braket made the choice to directly support a subset of OpenQASM 3.0 for gate-based devices and simulators. In fact, Braket supports a set of data types, statements and pragmas (specific to Braket) for OpenQASM 3.0, sometimes with a different syntax.
Braket Circuit parser does not support for the moment the OpenQASM 3.0 native
operations (U
and gphase
) but allows to define custom gates using a
combination of supported standard gates (rx
, ry
, rz
, cnot
,
phaseshift
for instance). Besides, the inclusion of files is not yet handled
by Braket library meaning we use a mechanism of hard includes (see
hard-open_qasm_hard_includes()
)
directly in the OpenQASM 3.0 code, to be sure the parser and interpreter have
all definitions in there. We also hard-include all included files in the
OpenQASM 3.0 code inputted for conversion.
Note
In the custom hard-imported file for native and standard gate redefinitions,
we use ggphase
to define the global phase, instead of the OpenQASM 3.0
keyword gphase
, which is already used and protected by Braket.
Braket Circuit``s are created using :func:`qasm3_to_braket_Circuit`. If
needed, you can also generate a Braket ``Program
from an OpenQASM 3.0 input
string using the qasm3_to_braket_Program()
. However, in this case, the
program parser does not need to redefine the native gates, and thus only
performing a hard import of standard gates and other included file is
sufficient. However, note that a Program
cannot be used to retrieve the
statevector and expectation value in Braket.
- qasm3_to_braket_Circuit(qasm3_str)[source]
Converting a OpenQASM 3.0 code into a Braket Circuit.
- Parameters
qasm3_str (str) – A string representing the OpenQASM 3.0 code.
- Returns
A Circuit equivalent to the QASM code in parameter.
- Return type
Circuit
Example
>>> qasm_code = ''' ... OPENQASM 3.0; ... qubit[2] q; ... h q[0]; ... ''' >>> circuit = qasm3_to_braket_Circuit(qasm_code) >>> print(circuit) T : │ 0 │ ┌───┐ q0 : ─┤ H ├─ └───┘ T : │ 0 │
- qasm3_to_braket_Program(qasm3_str)[source]
Converting a OpenQASM 3.0 code into a Braket Program.
- Parameters
qasm3_str (str) – A string representing the OpenQASM 3.0 code.
- Returns
A Program equivalent to the QASM code in parameter.
- Return type
Program
Example
>>> qasm_code = ''' ... OPENQASM 3.0; ... qubit[2] q; ... h q[0]; ... ''' >>> program = qasm3_to_braket_Program(qasm_code) >>> print(program) braketSchemaHeader=BraketSchemaHeader(name='braket.ir.openqasm.program', version='1') source='\nOPENQASM 3.0;\nqubit[2] q;\nh q[0];\n' inputs=None
Cirq
The Cirq library allows the user to instantiate a Cirq Circuit
from an
OpenQASM 2.0 code.
The Cirq parser lacks native support for certain OpenQASM 2.0 operations such as
cu1
, crz
, cu3
, reset
, u0
, p
, cp
, u
, rzz
,
rxx
and custom gate
. To address this limitation, we are redefining these
gates so you can use them on Cirq devices even though Cirq doesn’t support it (a
behavior sometimes called polyfill, especially in the browser world). These
features are handled by qasm2_to_cirq_Circuit()
.
In addition, Cirq does not handle user defined gates. So an important part of
qasm2_to_cirq_Circuit()
is also a function which might be useful to you:
remove_user_gates()
.
- qasm2_to_cirq_Circuit(qasm_str)[source]
Converting a OpenQASM 2.0 code into a cirq Circuit
- Parameters
qasm_str (str) – a string representing the OpenQASM 2.0 code
- Returns
a Circuit equivalent to the QASM code in parameter
- Example:
>>> qasm_code = ''' ... OPENQASM 2.0; ... include "qelib1.inc"; ... qreg q[2]; ... h q[0]; ... cx q[0], q[1]; ... ''' >>> circuit = qasm2_to_cirq_Circuit(qasm_code) >>> print(circuit) q_0: ───I───H───@─── │ q_1: ───I───────X───
- Return type
cirq_circuit