"""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 :func:`qasm2_to_cirq_Circuit`.
In addition, Cirq does not handle user defined gates. So an important part of
:func:`qasm2_to_cirq_Circuit` is also a function which might be useful to you:
:func:`remove_user_gates`.
"""
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from cirq.circuits.circuit import Circuit as cirq_circuit
from typeguard import typechecked
from mpqp.qasm.open_qasm_2_and_3 import remove_user_gates
[docs]@typechecked
def qasm2_to_cirq_Circuit(qasm_str: str) -> "cirq_circuit":
"""
Converting a OpenQASM 2.0 code into a cirq Circuit
Args:
qasm_str: a string representing the OpenQASM 2.0 code
Returns:
a Circuit equivalent to the QASM code in parameter
"""
import numpy as np
from cirq.circuits.qasm_output import QasmUGate
from cirq.contrib.qasm_import._parser import QasmGateStatement, QasmParser
from cirq.ops.common_channels import ResetChannel
from cirq.ops.common_gates import ry, rz
from cirq.ops.controlled_gate import ControlledGate
from cirq.ops.global_phase_op import GlobalPhaseGate
from cirq.ops.raw_types import Gate, Qid
from cirq.ops.wait_gate import WaitGate
from cirq.protocols.circuit_diagram_info_protocol import CircuitDiagramInfoArgs
from cirq.value.duration import Duration
qasm_str = remove_user_gates(qasm_str)
class PhaseGate(Gate):
def __init__(self, theta: complex):
super(PhaseGate, self)
self.theta = theta
def _num_qubits_(self):
return 1
def _unitary_(self):
return np.array(
[[np.exp(1j * self.theta), 0], [0, np.exp(1j * self.theta)]]
)
def _circuit_diagram_info_(self, args: CircuitDiagramInfoArgs):
return f"P({self.theta})"
class Rxx(Gate):
def __init__(self, theta: complex):
super(Rxx, self)
self.theta = theta
def _num_qubits_(self):
return 2
def _unitary_(self):
return np.array(
[
[np.cos(self.theta / 2), 0, 0, -1j * np.sin(self.theta / 2)],
[0, np.cos(self.theta / 2), -1j * np.sin(self.theta / 2), 0],
[0, -1j * np.sin(self.theta / 2), np.cos(self.theta / 2), 0],
[-1j * np.sin(self.theta / 2), 0, 0, np.cos(self.theta / 2)],
]
)
def _circuit_diagram_info_(self, args: CircuitDiagramInfoArgs):
return f"Rxx({self.theta})"
class Rzz(Gate):
def __init__(self, theta: complex):
super(Rzz, self)
self.theta = theta
def _num_qubits_(self):
return 2
def _unitary_(self):
return np.array(
[
[np.exp(-1j * self.theta / 2), 0, 0, 0],
[0, np.exp(1j * self.theta / 2), 0, 0],
[0, 0, np.exp(1j * self.theta / 2), 0],
[0, 0, 0, np.exp(-1j * self.theta / 2)],
]
)
def _circuit_diagram_info_(self, args: CircuitDiagramInfoArgs):
return f"Rzz({self.theta})"
class MyQasmUGate(QasmUGate): # pyright: ignore[reportUntypedBaseClass]
def _decompose_(self, qubits: tuple[Qid, ...]):
q = qubits[0]
return [
GlobalPhaseGate(np.exp(1j * (self.lmda + self.phi) / 2)).on(),
rz(self.lmda).on(q),
ry(self.theta).on(q),
rz(self.phi).on(q),
]
# Remove the line containing the barrier keyword
modified_lines = [line for line in qasm_str.split("\n") if "barrier" not in line]
qasm_str = "\n".join(modified_lines)
qasm_parser = QasmParser()
qs_dict = {
"cu1": QasmGateStatement(
qasm_gate="cu1",
num_params=1,
num_args=2,
cirq_gate=(
lambda params: ControlledGate(MyQasmUGate(0, 0, params[0] / np.pi))
),
),
"cu3": QasmGateStatement(
qasm_gate="cu3",
num_params=3,
num_args=2,
cirq_gate=(
lambda params: ControlledGate(MyQasmUGate(*[p / np.pi for p in params]))
),
),
"crz": QasmGateStatement(
qasm_gate="crz",
num_params=1,
num_args=2,
cirq_gate=(lambda params: ControlledGate(rz(params[0]))),
),
"reset": QasmGateStatement(
qasm_gate="reset", num_params=0, num_args=1, cirq_gate=ResetChannel()
),
"u0": QasmGateStatement(
qasm_gate="u0",
num_params=1,
num_args=1,
cirq_gate=(lambda params: WaitGate(Duration(micros=params[0]))),
),
"p": QasmGateStatement(
qasm_gate="p",
num_params=1,
num_args=1,
cirq_gate=(lambda params: PhaseGate(params[0])),
),
"cp": QasmGateStatement(
qasm_gate="cp",
num_params=1,
num_args=2,
cirq_gate=(lambda params: ControlledGate(PhaseGate(params[0]))),
),
"u": QasmGateStatement(
qasm_gate="u3",
num_params=3,
num_args=1,
cirq_gate=(lambda params: MyQasmUGate(*[p / np.pi for p in params])),
),
"rxx": QasmGateStatement(
qasm_gate="rxx",
num_params=1,
num_args=2,
cirq_gate=(lambda params: Rxx(params[0])),
),
"rzz": QasmGateStatement(
qasm_gate="rzz",
num_params=1,
num_args=2,
cirq_gate=(lambda params: Rzz(params[0])),
),
}
qasm_parser.all_gates |= qs_dict
return qasm_parser.parse(qasm_str).circuit