Skip to content

Commit

Permalink
Fix InverseGateCancellation runtime performance regression
Browse files Browse the repository at this point in the history
In Qiskit#11211, which was intended to improved the performance of the
InverseCancellation pass, actually introduced a regression. This was
because of the bugfix that was bundled in that PR to ensure we were
checking parameter values for gates were correct. This check was done by
doing a direct gate object equality between the self inverse gate
selected and the gate object found in a run. This however was unecessary
overhead as `Instruction.__eq__` has a large amount of overhead to
compare gate equality more generally. In the context of a transpiler
pass we can make assumptions that any gate with a given name is the same
and directly compare the parameters if the names match the inverse pair.
This will speed up the equality comparison for inverse pairs and fix the
regression.
  • Loading branch information
mtreinish committed Nov 19, 2023
1 parent 1dd4f54 commit bf0d952
Showing 1 changed file with 24 additions and 5 deletions.
29 changes: 24 additions & 5 deletions qiskit/transpiler/passes/optimization/inverse_cancellation.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"""
A generic InverseCancellation pass for any set of gate-inverse pairs.
"""
import math
from typing import List, Tuple, Union

from qiskit.circuit import Gate
Expand Down Expand Up @@ -111,7 +112,9 @@ def _run_on_self_inverse(self, dag: DAGCircuit):
partitions = []
chunk = []
for i in range(len(gate_cancel_run) - 1):
if gate_cancel_run[i].op == gate:
if gate_cancel_run[i].name == gate.name and almost_equal(
gate.params, gate_cancel_run[i].op.params
):
chunk.append(gate_cancel_run[i])
else:
if chunk:
Expand Down Expand Up @@ -157,20 +160,36 @@ def _run_on_inverse_pairs(self, dag: DAGCircuit):
while i < len(dag_nodes) - 1:
if (
dag_nodes[i].qargs == dag_nodes[i + 1].qargs
and dag_nodes[i].op == pair[0]
and dag_nodes[i + 1].op == pair[1]
and dag_nodes[i].op.name == pair[0].name
and almost_equal(dag_nodes[i].op.params, pair[0].params)
and dag_nodes[i + 1].op.name == pair[1].name
and almost_equal(dag_nodes[i + 1].op.params, pair[1].params)
):
dag.remove_op_node(dag_nodes[i])
dag.remove_op_node(dag_nodes[i + 1])
i = i + 2
elif (
dag_nodes[i].qargs == dag_nodes[i + 1].qargs
and dag_nodes[i].op == pair[1]
and dag_nodes[i + 1].op == pair[0]
and dag_nodes[i].op.name == pair[1].name
and almost_equal(dag_nodes[i].op.params, pair[1].params)
and dag_nodes[i + 1].op.name == pair[0].name
and almost_equal(dag_nodes[i + 1].op.params, pair[0].params)
):
dag.remove_op_node(dag_nodes[i])
dag.remove_op_node(dag_nodes[i + 1])
i = i + 2
else:
i = i + 1
return dag


def almost_equal(params_a, params_b):
"""Return whether params are equivalent."""
for a, b in zip(params_a, params_b):
try:
res = math.isclose(a, b)
except TypeError:
res = a == b
if not res:
return False
return True

0 comments on commit bf0d952

Please sign in to comment.