Source code for qiskit_research.utils.gate_decompositions

# (C) Copyright IBM 2022.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Gate decompositions."""

from __future__ import annotations

from collections.abc import Iterator
from math import pi

from qiskit import QuantumRegister
from qiskit.circuit import ControlledGate, Gate, Qubit
from qiskit.circuit.library import (
    CXGate,
    HGate,
    RXGate,
    RZGate,
    RZXGate,
    SdgGate,
    SGate,
    XGate,
    XXMinusYYGate,
    XXPlusYYGate,
)
from qiskit.dagcircuit import DAGCircuit
from qiskit.transpiler import Target
from qiskit.transpiler.basepasses import TransformationPass

from .gates import SECRGate


[docs] class RZXtoEchoedCR(TransformationPass): """ Class for the RZXGate to echoed cross resonance gate pass. The RZXGate is equivalent to the SECR gate plus a second XGate on the control qubit to return it to the initial state. See: https://arxiv.org/abs/1603.04821 """ def __init__( self, target: Target, ): super().__init__() self._coupling_map = target.build_coupling_map()
[docs] def run( self, dag: DAGCircuit, ) -> DAGCircuit: for rzx_run in dag.collect_runs(["rzx"]): control, _ = dag.find_bit(rzx_run[0].qargs[0]) target, _ = dag.find_bit(rzx_run[0].qargs[1]) # cr_forward_dir = cr_forward_direction( # control, target, self._inst_map, self._ctrl_chans # ) if (control, target) in self._coupling_map: cr_forward_dir = (control, target) else: cr_forward_dir = (target, control) for node in rzx_run: mini_dag = DAGCircuit() q0, q1 = QuantumRegister(2) mini_dag.add_qubits([q0, q1]) rzx_angle = node.op.params[0] if cr_forward_dir: mini_dag.apply_operation_back(SECRGate(rzx_angle), [q0, q1]) mini_dag.apply_operation_back(XGate(), [q0]) else: mini_dag.apply_operation_back(HGate(), [q0]) mini_dag.apply_operation_back(HGate(), [q1]) mini_dag.apply_operation_back(SECRGate(rzx_angle), [q1, q0]) mini_dag.apply_operation_back(XGate(), [q1]) mini_dag.apply_operation_back(HGate(), [q0]) mini_dag.apply_operation_back(HGate(), [q1]) dag.substitute_node_with_dag(node, mini_dag) return dag
class ControlledRZZToCX(TransformationPass): """Transformation pass to decompose Controlled RZZGate to CXGate.""" def _decomposition( self, register: QuantumRegister, gate: ControlledGate, ) -> Iterator[tuple[Gate, tuple[Qubit, ...]]]: a, b, c = register (theta,) = gate.params yield CXGate(), (b, c) yield RZGate(theta).control(1), (a, c) yield CXGate(), (b, c) def run( self, dag: DAGCircuit, ) -> DAGCircuit: for run in dag.collect_runs(["crzz"]): for node in run: mini_dag = DAGCircuit() register = QuantumRegister(3) mini_dag.add_qreg(register) for instr, qargs in self._decomposition(register, node.op): mini_dag.apply_operation_back(instr, qargs) dag.substitute_node_with_dag(node, mini_dag) return dag
[docs] class XXPlusYYtoRZX(TransformationPass): """Transformation pass to decompose XXPlusYYGate to RZXGate.""" def _decomposition( self, register: QuantumRegister, gate: XXPlusYYGate, ) -> Iterator[tuple[Gate, tuple[Qubit, ...]]]: a, b = register theta, beta = gate.params yield RZGate(-beta), (b,) yield HGate(), (a,) yield HGate(), (b,) yield RZGate(-0.5 * pi), (b,) yield RXGate(-0.5 * pi), (b,) yield RZGate(-0.5 * pi), (b,) yield RZXGate(0.5 * theta), (a, b) yield RXGate(0.5 * theta), (b,) yield RZGate(-0.5 * pi), (b,) yield RXGate(-0.5 * pi), (b,) yield RZGate(-0.5 * pi), (b,) yield RZGate(-0.5 * theta), (b,) yield RZGate(0.5 * pi), (a,) yield HGate(), (a,) yield RZGate(0.5 * pi), (b,) yield HGate(), (b,) yield RZGate(-0.5 * pi), (b,) yield RXGate(-0.5 * pi), (b,) yield RZGate(-0.5 * pi), (b,) yield RZXGate(0.5 * theta), (a, b) yield RXGate(0.5 * theta), (b,) yield RZGate(-0.5 * pi), (b,) yield RXGate(-0.5 * pi), (b,) yield RZGate(-0.5 * pi), (b,) yield RZGate(-0.5 * theta), (b,) yield HGate(), (a,) yield RZGate(-0.5 * pi), (a,) yield HGate(), (a,) yield HGate(), (b,) yield RZGate(-0.5 * pi), (b,) yield HGate(), (b,) yield RZGate(beta), (b,)
[docs] def run( self, dag: DAGCircuit, ) -> DAGCircuit: for run in dag.collect_runs(["xx_plus_yy"]): for node in run: mini_dag = DAGCircuit() register = QuantumRegister(2) mini_dag.add_qreg(register) for instr, qargs in self._decomposition(register, node.op): mini_dag.apply_operation_back(instr, qargs) dag.substitute_node_with_dag(node, mini_dag) return dag
[docs] class XXMinusYYtoRZX(TransformationPass): """Transformation pass to decompose XXMinusYYGate to RZXGate.""" def _decomposition( self, register: QuantumRegister, gate: XXMinusYYGate, ) -> Iterator[tuple[Gate, tuple[Qubit, ...]]]: a, b = register theta, beta = gate.params yield RZGate(-beta), (b,) yield HGate(), (a,) yield HGate(), (b,) yield RZGate(-0.5 * pi), (b,) yield RXGate(-0.5 * pi), (b,) yield RZGate(-0.5 * pi), (b,) yield RZXGate(0.5 * theta), (a, b) yield RXGate(-0.5 * theta), (b,) yield RZGate(-0.5 * pi), (b,) yield RXGate(-0.5 * pi), (b,) yield RZGate(-0.5 * pi), (b,) yield RZGate(0.5 * theta), (b,) yield RZGate(0.5 * pi), (a,) yield HGate(), (a,) yield RZGate(0.5 * pi), (b,) yield HGate(), (b,) yield RZGate(-0.5 * pi), (b,) yield RXGate(-0.5 * pi), (b,) yield RZGate(-0.5 * pi), (b,) yield RZXGate(-0.5 * theta), (a, b) yield RXGate(0.5 * theta), (b,) yield RZGate(-0.5 * pi), (b,) yield RXGate(-0.5 * pi), (b,) yield RZGate(-0.5 * pi), (b,) yield RZGate(-0.5 * theta), (b,) yield HGate(), (a,) yield RZGate(-0.5 * pi), (a,) yield HGate(), (a,) yield HGate(), (b,) yield RZGate(-0.5 * pi), (b,) yield HGate(), (b,) yield RZGate(beta), (b,)
[docs] def run( self, dag: DAGCircuit, ) -> DAGCircuit: for run in dag.collect_runs(["xx_minus_yy"]): for node in run: mini_dag = DAGCircuit() register = QuantumRegister(2) mini_dag.add_qreg(register) for instr, qargs in self._decomposition(register, node.op): mini_dag.apply_operation_back(instr, qargs) dag.substitute_node_with_dag(node, mini_dag) return dag
[docs] class RZXWeylDecomposition(TransformationPass): """ Decompose XX, YY, ZZ rotation gates using the Weyl Chamber decomposition, this version accepts Parameter values. See https://arxiv.org/abs/2105.01063 """
[docs] def run(self, dag: DAGCircuit) -> DAGCircuit: for run in dag.collect_runs(["rxx", "ryy", "rzz"]): for node in run: mini_dag = DAGCircuit() register = QuantumRegister(2) mini_dag.add_qreg(register) angle = node.op.params[0] if node.op.name == "rxx": mini_dag.apply_operation_back(HGate(), [register[0]]) mini_dag.apply_operation_back( RZXGate(angle), [register[0], register[1]] ) mini_dag.apply_operation_back(HGate(), [register[0]]) elif node.op.name == "ryy": mini_dag.apply_operation_back(SdgGate(), [register[0]]) mini_dag.apply_operation_back(SdgGate(), [register[1]]) mini_dag.apply_operation_back(HGate(), [register[0]]) mini_dag.apply_operation_back( RZXGate(angle), [register[0], register[1]] ) mini_dag.apply_operation_back(HGate(), [register[0]]) mini_dag.apply_operation_back(SGate(), [register[0]]) mini_dag.apply_operation_back(SGate(), [register[1]]) elif node.op.name == "rzz": mini_dag.apply_operation_back(HGate(), [register[1]]) mini_dag.apply_operation_back( RZXGate(angle), [register[0], register[1]] ) mini_dag.apply_operation_back(HGate(), [register[1]]) dag.substitute_node_with_dag(node, mini_dag) return dag