Skip to content

Commit 55d2da8

Browse files
Add option collect_from_back to CollectMultiQBlocks (Qiskit#13612)
* adding option collect_from_back * new option, tests, reno * typo * improving test following review * test fix
1 parent 1cfdf2e commit 55d2da8

File tree

3 files changed

+67
-1
lines changed

3 files changed

+67
-1
lines changed

qiskit/transpiler/passes/optimization/collect_multiqubit_blocks.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,18 @@ class CollectMultiQBlocks(AnalysisPass):
3333
Some gates may not be present in any block (e.g. if the number
3434
of operands is greater than ``max_block_size``)
3535
36+
By default, blocks are collected in the direction from the inputs towards the
37+
outputs of the DAG. The option ``collect_from_back`` allows to change this
38+
direction, that is to collect blocks from the outputs towards the inputs.
39+
Note that the blocks are still reported in a valid topological order.
40+
3641
A Disjoint Set Union data structure (DSU) is used to maintain blocks as
3742
gates are processed. This data structure points each qubit to a set at all
3843
times and the sets correspond to current blocks. These change over time
3944
and the data structure allows these changes to be done quickly.
4045
"""
4146

42-
def __init__(self, max_block_size=2):
47+
def __init__(self, max_block_size=2, collect_from_back=False):
4348
super().__init__()
4449
self.parent = {} # parent array for the union
4550

@@ -49,6 +54,7 @@ def __init__(self, max_block_size=2):
4954
self.gate_groups = {} # current gate lists for the groups
5055

5156
self.max_block_size = max_block_size # maximum block size
57+
self.collect_from_back = collect_from_back # backward collection
5258

5359
def find_set(self, index):
5460
"""DSU function for finding root of set of items
@@ -127,6 +133,10 @@ def collect_key(x):
127133

128134
op_nodes = dag.topological_op_nodes(key=collect_key)
129135

136+
# When collecting from the back, the order of nodes is reversed
137+
if self.collect_from_back:
138+
op_nodes = reversed(list(op_nodes))
139+
130140
for nd in op_nodes:
131141
can_process = True
132142
makes_too_big = False
@@ -222,6 +232,11 @@ def collect_key(x):
222232
if item == index and len(self.gate_groups[index]) != 0:
223233
block_list.append(self.gate_groups[index][:])
224234

235+
# When collecting from the back, both the order of the blocks
236+
# and the order of nodes in each block should be reversed.
237+
if self.collect_from_back:
238+
block_list = [block[::-1] for block in block_list[::-1]]
239+
225240
self.property_set["block_list"] = block_list
226241

227242
return dag
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
features_transpiler:
3+
- |
4+
Added a new option, ``collect_from_back``, to
5+
:class:`~qiskit.transpiler.passes.CollectMultiQBlocks`.
6+
When set to ``True``, the blocks are collected in the reverse direction,
7+
from the outputs towards the inputs of the circuit. The blocks are still
8+
reported following the normal topological order.
9+
This leads to an additional flexibility provided by the pass, and
10+
additional optimization opportunities when combined with a circuit
11+
resynthesis method.

test/python/transpiler/test_collect_multiq_blocks.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,46 @@ def test_larger_blocks(self):
290290

291291
pass_manager.run(qc)
292292

293+
def test_collect_from_back(self):
294+
"""Test the option to collect blocks from the outputs towards
295+
the inputs.
296+
┌───┐
297+
q_0: ┤ H ├──■────■────■───────
298+
└───┘┌─┴─┐ │ │
299+
q_1: ─────┤ X ├──┼────┼───────
300+
└───┘┌─┴─┐ │
301+
q_2: ──────────┤ X ├──┼───────
302+
└───┘┌─┴─┐┌───┐
303+
q_3: ───────────────┤ X ├┤ H ├
304+
└───┘└───┘
305+
"""
306+
qc = QuantumCircuit(4)
307+
qc.h(0)
308+
qc.cx(0, 1)
309+
qc.cx(0, 2)
310+
qc.cx(0, 3)
311+
qc.h(3)
312+
313+
dag = circuit_to_dag(qc)
314+
# For the circuit above, the topological order is unique
315+
topo_ops = list(dag.topological_op_nodes())
316+
317+
# When collecting blocks of size-3 using the default direction,
318+
# the first block should contain the H-gate and two CX-gates,
319+
# and the second block should contain a single CX-gate and an H-gate.
320+
pass_ = CollectMultiQBlocks(max_block_size=3, collect_from_back=False)
321+
pass_.run(dag)
322+
expected_blocks = [[topo_ops[0], topo_ops[1], topo_ops[2]], [topo_ops[3], topo_ops[4]]]
323+
self.assertEqual(pass_.property_set["block_list"], expected_blocks)
324+
325+
# When collecting blocks of size-3 using the opposite direction,
326+
# the first block should contain the H-gate and a single CX-gate,
327+
# and the second block should contain two CX-gates and an H-gate.
328+
pass_ = CollectMultiQBlocks(max_block_size=3, collect_from_back=True)
329+
pass_.run(dag)
330+
expected_blocks = [[topo_ops[0], topo_ops[1]], [topo_ops[2], topo_ops[3], topo_ops[4]]]
331+
self.assertEqual(pass_.property_set["block_list"], expected_blocks)
332+
293333

294334
if __name__ == "__main__":
295335
unittest.main()

0 commit comments

Comments
 (0)