Skip to content

Commit

Permalink
add DropNegligible transpiler pass
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinsung committed May 9, 2024
1 parent b80885d commit 28521ea
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 0 deletions.
1 change: 1 addition & 0 deletions qiskit/transpiler/passes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions qiskit/transpiler/passes/optimization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
70 changes: 70 additions & 0 deletions qiskit/transpiler/passes/optimization/drop_negligible.py
Original file line number Diff line number Diff line change
@@ -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
112 changes: 112 additions & 0 deletions test/python/transpiler/test_drop_negligible.py
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit 28521ea

Please sign in to comment.