Source code for mpqp.qasm.qasm_to_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
:func:`~mpqp.qasm.qasm_to_myqlm.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 :func:`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.
"""

from __future__ import annotations
import io
import warnings
from logging import StreamHandler, getLogger
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from braket.ir.openqasm import Program
    from braket.circuits import Circuit

from mpqp.core.instruction.gates.custom_gate import CustomGate
from mpqp.noise import NoiseModel
from mpqp.qasm.open_qasm_2_and_3 import open_qasm_hard_includes
from mpqp.tools.errors import UnsupportedBraketFeaturesWarning


[docs] def qasm3_to_braket_Program(qasm3_str: str) -> "Program": r"""Converting a OpenQASM 3.0 code into a Braket Program. Args: qasm3_str: A string representing the OpenQASM 3.0 code. Returns: A Program equivalent to the QASM code in parameter. Example: >>> qasm_code = ''' ... OPENQASM 3.0; ... qubit[2] q; ... h q[0]; ... ''' >>> program = qasm3_to_braket_Program(qasm_code) # doctest: +BRAKET >>> print(program) # doctest: +BRAKET braketSchemaHeader=BraketSchemaHeader(name='braket.ir.openqasm.program', version='1') source='\nOPENQASM 3.0;\nqubit[2] q;\nh q[0];\n' inputs=None """ from braket.ir.openqasm import Program # PROBLEM: import and standard gates are not supported by Braket # NOTE: however custom OpenQASM 3 gates declaration is supported by Braket, # the idea is then to hard import the standard lib and other files into the qasm string's header before # giving it to the Program. after_stdgates_included = open_qasm_hard_includes(qasm3_str, set()) program = Program(source=after_stdgates_included, inputs=None) return program
[docs] def qasm3_to_braket_Circuit(qasm3_str: str) -> "Circuit": """Converting a OpenQASM 3.0 code into a Braket Circuit. Args: qasm3_str: A string representing the OpenQASM 3.0 code. Returns: A Circuit equivalent to the QASM code in parameter. Example: >>> qasm_code = ''' ... OPENQASM 3.0; ... qubit[2] q; ... h q[0]; ... ''' >>> circuit = qasm3_to_braket_Circuit(qasm_code) # doctest: +BRAKET >>> print(circuit) # doctest: +NORMALIZE_WHITESPACE, +BRAKET T : │ 0 │ ┌───┐ q0 : ─┤ H ├─ └───┘ T : │ 0 │ """ # PROBLEM: import and standard gates are not supported by Braket # NOTE: however custom OpenQASM 3 gates declaration is supported by Braket, # SOLUTION: the idea is then to hard import the standard lib and other files into the qasm string's header before # giving it to the Circuit. # PROBLEM2: Braket doesn't support NATIVE freaking gates U and gphase, so the trick may not work for the moment # for circuit, only for program # SOLUTION: import a specific qasm file with U and gphase redefined with the supported Braket SDK gates, and by # removing from this import file the already handled gates from braket.circuits import Circuit qasm3_str = qasm3_str.replace("stdgates.inc", "braket_custom_include.inc") after_stdgates_included = open_qasm_hard_includes(qasm3_str, set()) braket_warning_message = ( "This program uses OpenQASM language features that may not be supported" " on QPUs or on-demand simulators." ) braket_logger = getLogger() logger_output_stream = io.StringIO() stream_handler = StreamHandler(logger_output_stream) braket_logger.addHandler(stream_handler) circuit = Circuit.from_ir(after_stdgates_included) braket_logger.removeHandler(stream_handler) log_lines = logger_output_stream.getvalue().split("\n") for message in log_lines: if message == braket_warning_message: from mpqp.environment.var_cache import translation_warning_enabled if translation_warning_enabled() is True: warnings.warn( "\n" + braket_warning_message, UnsupportedBraketFeaturesWarning ) else: if message != "": braket_logger.warning(message) return circuit
[docs] def braket_noise_to_mpqp(qasm3_code: str) -> tuple[list[NoiseModel], str]: """ Parse braket's qasm3 pragmas into mpqp's Noise Models. Args: qasm3_code: The OpenQASM3 string containing the noise information. Returns: A tuple of list of MPQP Noise models and cleaned qasm3 string without noise pragmas. Example: >>> qasm_code = ''' ... OPENQASM 3.0; ... qubit[2] q; ... h q[0]; ... #pragma braket noise phase_damping(0.1) q[0] ... ''' >>> noise, clean_qasm = braket_noise_to_mpqp(qasm_code) # doctest: +BRAKET >>> print(noise) # doctest: +NORMALIZE_WHITESPACE, +BRAKET [PhaseDamping(0.1, [0])] >>> print(clean_qasm) # doctest: +NORMALIZE_WHITESPACE, +BRAKET OPENQASM 3.0; qubit[2] q; h q[0]; """ from mpqp.noise import ( AmplitudeDamping, BitFlip, Depolarizing, NoiseModel, PhaseDamping, ) noises = [] cleaned_lines = [] for line in qasm3_code.split("\n"): if "depolarizing" in line or "two_qubit_depolarizing" in line: noises.append(NoiseModel.from_other_language(line, Depolarizing)) elif "bit_flip" in line: noises.append(NoiseModel.from_other_language(line, BitFlip)) elif "amplitude_damping" in line or "generalized_amplitude_damping" in line: noises.append(NoiseModel.from_other_language(line, AmplitudeDamping)) elif "phase_damping" in line: noises.append(NoiseModel.from_other_language(line, PhaseDamping)) elif "braket noise" in line: import re noise_model = re.search(r'noise\s+([^(]+)', line) if noise_model: raise NotImplementedError( f"Error: {noise_model.group(1)} is not supported." ) else: cleaned_lines.append(line) cleaned_qasm = "\n".join(cleaned_lines) return noises, cleaned_qasm
[docs] def braket_custom_gates_to_mpqp(qasm3_code: str) -> CustomGate: """ Parse braket's qasm3 pragmas into mpqp's Custom Gate. Args: qasm3_code: The OpenQASM3 string containing the instruction information. Returns: A MPQP Custom Gate contained in the qasm3 string. Example: >>> qasm_code = '''#pragma braket unitary([[0, 1.0], [1.0, 0]]) q[0]''' >>> print(repr(braket_custom_gates_to_mpqp(qasm_code))) # doctest: +NORMALIZE_WHITESPACE, +BRAKET CustomGate(array([[0., 1.], [1., 0.]]), [0]) """ import ast import re import numpy as np if "braket unitary" in qasm3_code: matrix = np.array( ast.literal_eval(qasm3_code[qasm3_code.find('[') : qasm3_code.rfind(')')]) ) indices = [int(i) for i in re.findall(r"q\[(\d+)\]", qasm3_code)] return CustomGate(matrix, indices) else: raise NotImplementedError( "Only #pragma braket unitary is supported for the moment." )