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_qasm2 and to_qasm3 methods of this class.

From OpenQASM2.0 to OpenQASM3.0

The latest version of OpenQASM (3.0, started in 2020) has been released by conjoint members of IBM Quantum, AWS Quantum Computing, Zapata Computing, Zurich Instruments and University of Oxford. This version extends the 2.0 one, adding some advanced features, and modifying parts of syntax and grammar, making some part of OpenQASM 2.0 not fully retro-compatible (hence why we need to keep track of the instructions requiring custom definitions in Instr).

This being said, this “new” version was quite slow to come to SDKs, so to help the transition we are making a few conversion functions available. The main one being open_qasm_2_to_3().

The translation is performed in a two main steps:

  1. the OpenQASM 2.0 code is parsed by parse_openqasm_2_file() (it’s a basic parse, separating the instructions),

  2. each instruction is converted to it’s OpenQASM 3.0 counterpart by convert_instruction_2_to_3(),

Since open_qasm_2_to_3() needs a few tricky parameters to function properly, you also have access to a shorthand accepting directly the path of the file you want to convert: open_qasm_file_conversion_2_to_3().

In some cases, having multiple files to deal with can be cumbersome. You can avoid this pain point by uniting all your source files in a single one using open_qasm_hard_includes().

On the other hand, some providers such as Cirq do not support user defined gates, requiring to replace all user gate calls by their definition. This is done using remove_user_gates().

class Instr(value)[source]

Bases: Enum

Special instruction for which the definition needs to included in the file.

BRAKET_CUSTOM_INCLUDE = 14
C3SQRTX = 11
C3X = 9
C4X = 10
CSX = 1
CU3 = 3
OQASM2_ALL_STDGATES = 12
OQASM3_ALL_STDGATES = 13
RC3X = 8
RCCX = 7
RXX = 6
RZZ = 5
STD_LIB = 0
SXDG = 4
U0 = 2
convert_instruction_2_to_3(instr, included_instr, included_tree_current, defined_gates, path_to_main='.')[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 (str) – Path to the main folder from which include paths are described.

Returns

The upgraded instruction and the potential code to add in the header as the second element.

Return type

tuple[str, str]

open_qasm_2_to_3(code, included_tree_current_node=Node('/initial_code'), path_to_file='.', defined_gates={})[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 (Node) – Current Node in the file inclusion tree.

  • path_to_file (str) – Path to the location of the file from which the code is coming (useful for locating imports).

  • defined_gates (set[str]) – Set of custom gates already defined.

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_file_conversion_2_to_3(path)[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.

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_hard_includes(code, included_files, path_to_file='./', is_openqasm_header_included=False)[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 (str) – Path used to localize files that are included.

  • is_openqasm_header_included (bool) – Boolean used to only include once the OpenQASM header.

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_user_gates(qasm_code)[source]

Parses user gate definitions from QASM code.

Parameters

qasm_code (str) – The QASM code containing user gate definitions.

Returns

A tuple containing a dictionary of user gate definitions and the QASM string stripped of it’s user gate definitions.

Return type

tuple[dict[str, str], str]

Example

>>> qasm_str = '''gate rzz(theta) a,b {
...     cx a,b;
...     u1(theta) b;
...     cx a,b;
... }
... qubit[3] q;
... bit[1] c0;
... bit[1] c1;
... rzz(0.2) q[1], q[2];
... c2[0] = measure q[2];'''
>>> print(parse_user_gates(qasm_str))
({'rzz': [['theta'], ['a', 'b'], 'cx a,b;', 'u1(theta) b;', 'cx a,b;']}, 'qubit[3] q;\nbit[1] c0;\nbit[1] c1;\nrzz(0.2) q[1], q[2];\nc2[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_user_gates(qasm_code)[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.

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

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

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 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

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

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

Return type

cirq_circuit