diff --git a/qiskit_research/utils/gate_decompositions.py b/qiskit_research/utils/gate_decompositions.py index eae8cb97..3af156f0 100644 --- a/qiskit_research/utils/gate_decompositions.py +++ b/qiskit_research/utils/gate_decompositions.py @@ -17,14 +17,15 @@ from collections.abc import Iterator from qiskit import QuantumRegister -from qiskit.circuit import Gate, Qubit +from qiskit.circuit import ControlledGate, Gate, Qubit from qiskit.circuit.library import ( + CXGate, HGate, - SGate, - SdgGate, RXGate, RZGate, RZXGate, + SdgGate, + SGate, XGate, XXMinusYYGate, XXPlusYYGate, @@ -122,6 +123,39 @@ def run( 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 + + class XXPlusYYtoRZX(TransformationPass): """Transformation pass to decompose XXPlusYYGate to RZXGate.""" diff --git a/test/utils/test_gate_decompositions.py b/test/utils/test_gate_decompositions.py index 5c97cc37..2815abaa 100644 --- a/test/utils/test_gate_decompositions.py +++ b/test/utils/test_gate_decompositions.py @@ -16,10 +16,12 @@ import numpy as np from qiskit.circuit import QuantumCircuit, QuantumRegister -from qiskit.circuit.library import XXMinusYYGate, XXPlusYYGate -from qiskit.transpiler import PassManager +from qiskit.circuit.library import RZZGate, XXMinusYYGate, XXPlusYYGate from qiskit.quantum_info import Operator +from qiskit.transpiler import PassManager + from qiskit_research.utils.gate_decompositions import ( + ControlledRZZToCX, RZXWeylDecomposition, XXMinusYYtoRZX, XXPlusYYtoRZX, @@ -29,10 +31,24 @@ class TestPasses(unittest.TestCase): """Test passes.""" + def test_controlled_rzz_to_cx(self): + """Test controlled RZZGate to CXGate decomposition.""" + rng = np.random.default_rng() + theta = rng.uniform(-10, 10) + gate = RZZGate(theta).control(1) + register = QuantumRegister(3) + circuit = QuantumCircuit(register) + circuit.append(gate, register) + pass_ = ControlledRZZToCX() + pass_manager = PassManager([pass_]) + decomposed = pass_manager.run(circuit) + self.assertTrue(Operator(circuit).equiv(Operator(decomposed))) + def test_xxplusyy_to_rzx(self): """Test XXPlusYYGate to RZXGate decomposition.""" - theta = np.random.uniform(-10, 10) - beta = np.random.uniform(-10, 10) + rng = np.random.default_rng() + theta = rng.uniform(-10, 10) + beta = rng.uniform(-10, 10) gate = XXPlusYYGate(theta, beta) register = QuantumRegister(2) circuit = QuantumCircuit(register) @@ -44,8 +60,9 @@ def test_xxplusyy_to_rzx(self): def test_xxminusyy_to_rzx(self): """Test XXMinusYYGate to RZXGate decomposition.""" - theta = np.random.uniform(-10, 10) - beta = np.random.uniform(-10, 10) + rng = np.random.default_rng() + theta = rng.uniform(-10, 10) + beta = rng.uniform(-10, 10) gate = XXMinusYYGate(theta, beta) register = QuantumRegister(2) circuit = QuantumCircuit(register)