Skip to content

Commit

Permalink
Add identity removal pass to presets (#13363)
Browse files Browse the repository at this point in the history
* Add identity removal pass to presets

This commit adds the newly added RemoveIdentityEquivalent transpiler
pass to the preset pass managers for optimization levels 2 and 3. The
pass is added to the init and optimization stages. For the init stage
the pass is run after 3 or more qubit decomposition. While the pass
works fine with 3 qubit gates, the thinking behind this placement was
it seems less likely for a multi qubit identity equivalent gate to be
added while components of it's decomposition are more likely to be
equivalent to an identity. For the optimization stage it is added to
the optimization loop.

One test needed to be updated because the additional pass changes the
decomposition outcome in that test. Although the output circuit is
different it is equivalent to the other decomposition and the original
circuit. A unitary equivalence check is added to the test to verify
that.

* Fix lint
  • Loading branch information
mtreinish authored Nov 6, 2024
1 parent 50217b6 commit a24470f
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 10 deletions.
16 changes: 16 additions & 0 deletions qiskit/transpiler/preset_passmanagers/builtin_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
CommutativeCancellation,
ConsolidateBlocks,
InverseCancellation,
RemoveIdentityEquivalent,
)
from qiskit.transpiler.passes import Depth, Size, FixedPoint, MinimumPoint
from qiskit.transpiler.passes.utils.gates_basis import GatesInBasis
Expand Down Expand Up @@ -156,6 +157,13 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
if pass_manager_config.routing_method != "none":
init.append(ElidePermutations())
init.append(RemoveDiagonalGatesBeforeMeasure())
# Target not set on RemoveIdentityEquivalent because we haven't applied a Layout
# yet so doing anything relative to an error rate in the target is not valid.
init.append(
RemoveIdentityEquivalent(
approximation_degree=pass_manager_config.approximation_degree
)
)
init.append(
InverseCancellation(
[
Expand Down Expand Up @@ -580,6 +588,10 @@ def _opt_control(property_set):

elif optimization_level == 2:
_opt = [
RemoveIdentityEquivalent(
approximation_degree=pass_manager_config.approximation_degree,
target=pass_manager_config.target,
),
Optimize1qGatesDecomposition(
basis=pass_manager_config.basis_gates, target=pass_manager_config.target
),
Expand All @@ -602,6 +614,10 @@ def _opt_control(property_set):
plugin_config=pass_manager_config.unitary_synthesis_plugin_config,
target=pass_manager_config.target,
),
RemoveIdentityEquivalent(
approximation_degree=pass_manager_config.approximation_degree,
target=pass_manager_config.target,
),
Optimize1qGatesDecomposition(
basis=pass_manager_config.basis_gates, target=pass_manager_config.target
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
features_transpiler:
- |
The :class:`.RemoveIdentityEquivalent` transpiler pass is now run as part
of the preset pass managers at optimization levels 2 and 3. The pass is
run in the ``init`` stage and the ``optimization`` stage, because the
optimizations it applies are valid in both stages and the pass is
fast to execute.
28 changes: 18 additions & 10 deletions test/python/compiler/test_transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1796,21 +1796,29 @@ def test_no_infinite_loop(self, optimization_level):
seed_transpiler=42,
)

# Expect a -pi/2 global phase for the U3 to RZ/SX conversion, and
# a -0.5 * theta phase for RZ to P twice, once at theta, and once at 3 pi
# for the second and third RZ gates in the U3 decomposition.
expected = QuantumCircuit(
1, global_phase=-np.pi / 2 - 0.5 * (-0.2 + np.pi) - 0.5 * 3 * np.pi
)
expected.p(-np.pi, 0)
expected.sx(0)
expected.p(np.pi - 0.2, 0)
expected.sx(0)
if optimization_level == 1:
# Expect a -pi/2 global phase for the U3 to RZ/SX conversion, and
# a -0.5 * theta phase for RZ to P twice, once at theta, and once at 3 pi
# for the second and third RZ gates in the U3 decomposition.
expected = QuantumCircuit(
1, global_phase=-np.pi / 2 - 0.5 * (-0.2 + np.pi) - 0.5 * 3 * np.pi
)
expected.p(-np.pi, 0)
expected.sx(0)
expected.p(np.pi - 0.2, 0)
expected.sx(0)
else:
expected = QuantumCircuit(1, global_phase=(15 * np.pi - 1) / 10)
expected.sx(0)
expected.p(1.0 / 5.0 + np.pi, 0)
expected.sx(0)
expected.p(3 * np.pi, 0)

error_message = (
f"\nOutput circuit:\n{out!s}\n{Operator(out).data}\n"
f"Expected circuit:\n{expected!s}\n{Operator(expected).data}"
)
self.assertEqual(Operator(qc), Operator(out))
self.assertEqual(out, expected, error_message)

@data(0, 1, 2, 3)
Expand Down

0 comments on commit a24470f

Please sign in to comment.