Skip to content

Commit 7169f6d

Browse files
authored
Add ContractIdleWiresInControlFlow to default pipelines (Qiskit#13891)
This is cheap, and can be done both in the `init` stage (where it may help routing) and the the general optimisation loop (where it may allow chains of optimisations to collapse).
1 parent f1729db commit 7169f6d

File tree

2 files changed

+87
-40
lines changed

2 files changed

+87
-40
lines changed

qiskit/transpiler/preset_passmanagers/builtin_plugins.py

Lines changed: 48 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
ConsolidateBlocks,
4343
InverseCancellation,
4444
RemoveIdentityEquivalent,
45+
ContractIdleWiresInControlFlow,
4546
)
4647
from qiskit.transpiler.passes import Depth, Size, FixedPoint, MinimumPoint
4748
from qiskit.transpiler.passes.utils.gates_basis import GatesInBasis
@@ -125,22 +126,25 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
125126
pass_manager_config.qubits_initially_zero,
126127
)
127128
init.append(
128-
InverseCancellation(
129-
[
130-
CXGate(),
131-
ECRGate(),
132-
CZGate(),
133-
CYGate(),
134-
XGate(),
135-
YGate(),
136-
ZGate(),
137-
HGate(),
138-
SwapGate(),
139-
(TGate(), TdgGate()),
140-
(SGate(), SdgGate()),
141-
(SXGate(), SXdgGate()),
142-
]
143-
)
129+
[
130+
InverseCancellation(
131+
[
132+
CXGate(),
133+
ECRGate(),
134+
CZGate(),
135+
CYGate(),
136+
XGate(),
137+
YGate(),
138+
ZGate(),
139+
HGate(),
140+
SwapGate(),
141+
(TGate(), TdgGate()),
142+
(SGate(), SdgGate()),
143+
(SXGate(), SXdgGate()),
144+
]
145+
),
146+
ContractIdleWiresInControlFlow(),
147+
]
144148
)
145149

146150
elif optimization_level in {2, 3}:
@@ -155,31 +159,32 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
155159
)
156160
if pass_manager_config.routing_method != "none":
157161
init.append(ElidePermutations())
158-
init.append(RemoveDiagonalGatesBeforeMeasure())
159-
# Target not set on RemoveIdentityEquivalent because we haven't applied a Layout
160-
# yet so doing anything relative to an error rate in the target is not valid.
161-
init.append(
162-
RemoveIdentityEquivalent(
163-
approximation_degree=pass_manager_config.approximation_degree
164-
)
165-
)
166162
init.append(
167-
InverseCancellation(
168-
[
169-
CXGate(),
170-
ECRGate(),
171-
CZGate(),
172-
CYGate(),
173-
XGate(),
174-
YGate(),
175-
ZGate(),
176-
HGate(),
177-
SwapGate(),
178-
(TGate(), TdgGate()),
179-
(SGate(), SdgGate()),
180-
(SXGate(), SXdgGate()),
181-
]
182-
)
163+
[
164+
RemoveDiagonalGatesBeforeMeasure(),
165+
# Target not set on RemoveIdentityEquivalent because we haven't applied a Layout
166+
# yet so doing anything relative to an error rate in the target is not valid.
167+
RemoveIdentityEquivalent(
168+
approximation_degree=pass_manager_config.approximation_degree
169+
),
170+
InverseCancellation(
171+
[
172+
CXGate(),
173+
ECRGate(),
174+
CZGate(),
175+
CYGate(),
176+
XGate(),
177+
YGate(),
178+
ZGate(),
179+
HGate(),
180+
SwapGate(),
181+
(TGate(), TdgGate()),
182+
(SGate(), SdgGate()),
183+
(SXGate(), SXdgGate()),
184+
]
185+
),
186+
ContractIdleWiresInControlFlow(),
187+
]
183188
)
184189
init.append(CommutativeCancellation())
185190
init.append(ConsolidateBlocks())
@@ -539,6 +544,7 @@ def _opt_control(property_set):
539544
(SXGate(), SXdgGate()),
540545
]
541546
),
547+
ContractIdleWiresInControlFlow(),
542548
]
543549

544550
elif optimization_level == 2:
@@ -551,6 +557,7 @@ def _opt_control(property_set):
551557
basis=pass_manager_config.basis_gates, target=pass_manager_config.target
552558
),
553559
CommutativeCancellation(target=pass_manager_config.target),
560+
ContractIdleWiresInControlFlow(),
554561
]
555562
elif optimization_level == 3:
556563
# Steps for optimization level 3
@@ -576,6 +583,7 @@ def _opt_control(property_set):
576583
basis=pass_manager_config.basis_gates, target=pass_manager_config.target
577584
),
578585
CommutativeCancellation(target=pass_manager_config.target),
586+
ContractIdleWiresInControlFlow(),
579587
]
580588

581589
def _opt_control(property_set):

test/python/compiler/test_transpiler.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2090,6 +2090,45 @@ def test_transpile_annotated_ops_with_backend(self, opt_level):
20902090
)
20912091
self.assertLessEqual(set(transpiled.count_ops().keys()), {"u1", "u2", "u3", "cx"})
20922092

2093+
@data(1, 2, 3)
2094+
def test_optimize_decomposition_around_control_flow(self, level):
2095+
"""Test that we successfully optimise away idle wires from control flow."""
2096+
qc = QuantumCircuit(5, 1)
2097+
# This cz(0, 1) can't cancel with its friend on the other side until the data dependency is
2098+
# removed from the `if` block. Similarly, the sx(2) needs the two x(2) in the `if` to go.
2099+
qc.cz(0, 1)
2100+
qc.sx(2)
2101+
qc.cz(3, 4)
2102+
with qc.if_test((qc.clbits[0], False)):
2103+
# The `(0, 4)` data dependencies should be removed before routing, so we don't see any
2104+
# swaps in here.
2105+
qc.cz(0, 4)
2106+
qc.x(2)
2107+
qc.x(2)
2108+
qc.x(3)
2109+
qc.cz(0, 4)
2110+
qc.cz(0, 1)
2111+
qc.sxdg(2)
2112+
qc.cz(3, 4)
2113+
2114+
expected = qc.copy_empty_like()
2115+
expected.cz(3, 4)
2116+
with expected.if_test((expected.clbits[0], False)):
2117+
expected.x(3)
2118+
expected.cz(3, 4)
2119+
2120+
target = Target(5)
2121+
target.add_instruction(XGate(), {(i,): None for i in range(5)})
2122+
target.add_instruction(SXGate(), {(i,): None for i in range(5)})
2123+
target.add_instruction(RZGate(Parameter("a")), {(i,): None for i in range(5)})
2124+
target.add_instruction(CZGate(), {pair: None for pair in CouplingMap.from_line(5)})
2125+
target.add_instruction(IfElseOp, name="if_else")
2126+
2127+
self.assertEqual(
2128+
transpile(qc, target=target, optimization_level=level, initial_layout=[0, 1, 2, 3, 4]),
2129+
expected,
2130+
)
2131+
20932132

20942133
@ddt
20952134
class TestPostTranspileIntegration(QiskitTestCase):

0 commit comments

Comments
 (0)