From 28521eae79c4d9b385708d6ebae79467457cc8bf Mon Sep 17 00:00:00 2001 From: "Kevin J. Sung" Date: Thu, 9 May 2024 18:49:01 -0400 Subject: [PATCH] add DropNegligible transpiler pass --- qiskit/transpiler/passes/__init__.py | 1 + .../passes/optimization/__init__.py | 1 + .../passes/optimization/drop_negligible.py | 70 +++++++++++ .../python/transpiler/test_drop_negligible.py | 112 ++++++++++++++++++ 4 files changed, 184 insertions(+) create mode 100644 qiskit/transpiler/passes/optimization/drop_negligible.py create mode 100644 test/python/transpiler/test_drop_negligible.py diff --git a/qiskit/transpiler/passes/__init__.py b/qiskit/transpiler/passes/__init__.py index 400d98304951..23e51101114f 100644 --- a/qiskit/transpiler/passes/__init__.py +++ b/qiskit/transpiler/passes/__init__.py @@ -244,6 +244,7 @@ from .optimization import ElidePermutations from .optimization import NormalizeRXAngle from .optimization import OptimizeAnnotated +from .optimization import DropNegligible # circuit analysis from .analysis import ResourceEstimation diff --git a/qiskit/transpiler/passes/optimization/__init__.py b/qiskit/transpiler/passes/optimization/__init__.py index 082cb3f67ec9..f9c5928d7727 100644 --- a/qiskit/transpiler/passes/optimization/__init__.py +++ b/qiskit/transpiler/passes/optimization/__init__.py @@ -38,3 +38,4 @@ from .elide_permutations import ElidePermutations from .normalize_rx_angle import NormalizeRXAngle from .optimize_annotated import OptimizeAnnotated +from .drop_negligible import DropNegligible diff --git a/qiskit/transpiler/passes/optimization/drop_negligible.py b/qiskit/transpiler/passes/optimization/drop_negligible.py new file mode 100644 index 000000000000..a8895a768cd0 --- /dev/null +++ b/qiskit/transpiler/passes/optimization/drop_negligible.py @@ -0,0 +1,70 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2024. +# +# 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. + +"""Transpiler pass to drop gates with negligible effects.""" + +from __future__ import annotations + +import numpy as np +from qiskit.circuit.library import ( + CPhaseGate, + PhaseGate, + RXGate, + RXXGate, + RYGate, + RYYGate, + RZGate, + RZZGate, + XXMinusYYGate, + XXPlusYYGate, +) +from qiskit.dagcircuit import DAGCircuit +from qiskit.transpiler.basepasses import TransformationPass + +# List of Gate classes with the property that if the gate's parameters are all +# (close to) zero then the gate has (close to) no effect. +DROP_NEGLIGIBLE_GATE_CLASSES = ( + CPhaseGate, + PhaseGate, + RXGate, + RYGate, + RZGate, + RXXGate, + RYYGate, + RZZGate, + XXPlusYYGate, + XXMinusYYGate, +) + + +class DropNegligible(TransformationPass): + """Drop gates with negligible effects.""" + + def __init__(self, atol: float = 1e-8) -> None: + """Initialize the transpiler pass. + + Args: + atol: Absolute numerical tolerance for determining whether a gate's effect + is negligible. + """ + self.atol = atol + super().__init__() + + def run(self, dag: DAGCircuit) -> DAGCircuit: + for node in dag.op_nodes(): + if not isinstance(node.op, DROP_NEGLIGIBLE_GATE_CLASSES): + continue + if not all(isinstance(param, (int, float, complex)) for param in node.op.params): + continue + if np.allclose(node.op.params, 0, atol=self.atol): + dag.remove_op_node(node) + return dag diff --git a/test/python/transpiler/test_drop_negligible.py b/test/python/transpiler/test_drop_negligible.py new file mode 100644 index 000000000000..e313170cd9f2 --- /dev/null +++ b/test/python/transpiler/test_drop_negligible.py @@ -0,0 +1,112 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2024. +# +# 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. + +"""Tests for pass cancelling 2 consecutive CNOTs on the same qubits.""" + +import numpy as np + +from qiskit import QuantumCircuit, QuantumRegister +from qiskit.circuit import Parameter, QuantumCircuit, QuantumRegister +from qiskit.circuit.library import ( + CPhaseGate, + RXGate, + RXXGate, + RYGate, + RYYGate, + RZGate, + RZZGate, + XXMinusYYGate, + XXPlusYYGate, +) +from qiskit.quantum_info import Operator +from qiskit.transpiler import PassManager +from qiskit.transpiler.passes import DropNegligible + +from test import QiskitTestCase # pylint: disable=wrong-import-order + + +class TestDropNegligible(QiskitTestCase): + """Test the DropNegligible pass.""" + + def test_drops_negligible_gates(self): + """Test that negligible gates are dropped.""" + qubits = QuantumRegister(2) + circuit = QuantumCircuit(qubits) + a, b = qubits + circuit.append(CPhaseGate(1e-5), [a, b]) + circuit.append(CPhaseGate(1e-8), [a, b]) + circuit.append(RXGate(1e-5), [a]) + circuit.append(RXGate(1e-8), [a]) + circuit.append(RYGate(1e-5), [a]) + circuit.append(RYGate(1e-8), [a]) + circuit.append(RZGate(1e-5), [a]) + circuit.append(RZGate(1e-8), [a]) + circuit.append(RXXGate(1e-5), [a, b]) + circuit.append(RXXGate(1e-8), [a, b]) + circuit.append(RYYGate(1e-5), [a, b]) + circuit.append(RYYGate(1e-8), [a, b]) + circuit.append(RZZGate(1e-5), [a, b]) + circuit.append(RZZGate(1e-8), [a, b]) + circuit.append(XXPlusYYGate(1e-5, 1e-8), [a, b]) + circuit.append(XXPlusYYGate(1e-8, 1e-8), [a, b]) + circuit.append(XXMinusYYGate(1e-5, 1e-8), [a, b]) + circuit.append(XXMinusYYGate(1e-8, 1e-8), [a, b]) + pass_manager = PassManager([DropNegligible()]) + transpiled = pass_manager.run(circuit) + self.assertEqual(circuit.count_ops()["cp"], 2) + self.assertEqual(transpiled.count_ops()["cp"], 1) + self.assertEqual(circuit.count_ops()["rx"], 2) + self.assertEqual(transpiled.count_ops()["rx"], 1) + self.assertEqual(circuit.count_ops()["ry"], 2) + self.assertEqual(transpiled.count_ops()["ry"], 1) + self.assertEqual(circuit.count_ops()["rz"], 2) + self.assertEqual(transpiled.count_ops()["rz"], 1) + self.assertEqual(circuit.count_ops()["rxx"], 2) + self.assertEqual(transpiled.count_ops()["rxx"], 1) + self.assertEqual(circuit.count_ops()["ryy"], 2) + self.assertEqual(transpiled.count_ops()["ryy"], 1) + self.assertEqual(circuit.count_ops()["rzz"], 2) + self.assertEqual(transpiled.count_ops()["rzz"], 1) + self.assertEqual(circuit.count_ops()["xx_plus_yy"], 2) + self.assertEqual(transpiled.count_ops()["xx_plus_yy"], 1) + self.assertEqual(circuit.count_ops()["xx_minus_yy"], 2) + self.assertEqual(transpiled.count_ops()["xx_minus_yy"], 1) + np.testing.assert_allclose( + np.array(Operator(circuit)), np.array(Operator(transpiled)), atol=1e-7 + ) + + def test_handles_parameters(self): + """Test that gates with parameters are ignored gracefully.""" + qubits = QuantumRegister(2) + circuit = QuantumCircuit(qubits) + a, b = qubits + theta = Parameter("theta") + circuit.append(CPhaseGate(theta), [a, b]) + circuit.append(CPhaseGate(1e-5), [a, b]) + circuit.append(CPhaseGate(1e-8), [a, b]) + pass_manager = PassManager([DropNegligible()]) + transpiled = pass_manager.run(circuit) + self.assertEqual(circuit.count_ops()["cp"], 3) + self.assertEqual(transpiled.count_ops()["cp"], 2) + + def test_handles_number_types(self): + """Test that gates with different types of numbers are handled correctly.""" + qubits = QuantumRegister(2) + circuit = QuantumCircuit(qubits) + a, b = qubits + circuit.append(CPhaseGate(np.float32(1e-6)), [a, b]) + circuit.append(CPhaseGate(1e-3), [a, b]) + circuit.append(CPhaseGate(1e-8), [a, b]) + pass_manager = PassManager([DropNegligible(atol=1e-5)]) + transpiled = pass_manager.run(circuit) + self.assertEqual(circuit.count_ops()["cp"], 3) + self.assertEqual(transpiled.count_ops()["cp"], 1)