Skip to content

Commit

Permalink
Add option collect_from_back to CollectMultiQBlocks (#13612)
Browse files Browse the repository at this point in the history
* adding option collect_from_back

* new option, tests, reno

* typo

* improving test following review

* test fix
  • Loading branch information
alexanderivrii authored Jan 8, 2025
1 parent 1cfdf2e commit 55d2da8
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,18 @@ class CollectMultiQBlocks(AnalysisPass):
Some gates may not be present in any block (e.g. if the number
of operands is greater than ``max_block_size``)
By default, blocks are collected in the direction from the inputs towards the
outputs of the DAG. The option ``collect_from_back`` allows to change this
direction, that is to collect blocks from the outputs towards the inputs.
Note that the blocks are still reported in a valid topological order.
A Disjoint Set Union data structure (DSU) is used to maintain blocks as
gates are processed. This data structure points each qubit to a set at all
times and the sets correspond to current blocks. These change over time
and the data structure allows these changes to be done quickly.
"""

def __init__(self, max_block_size=2):
def __init__(self, max_block_size=2, collect_from_back=False):
super().__init__()
self.parent = {} # parent array for the union

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

self.max_block_size = max_block_size # maximum block size
self.collect_from_back = collect_from_back # backward collection

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

op_nodes = dag.topological_op_nodes(key=collect_key)

# When collecting from the back, the order of nodes is reversed
if self.collect_from_back:
op_nodes = reversed(list(op_nodes))

for nd in op_nodes:
can_process = True
makes_too_big = False
Expand Down Expand Up @@ -222,6 +232,11 @@ def collect_key(x):
if item == index and len(self.gate_groups[index]) != 0:
block_list.append(self.gate_groups[index][:])

# When collecting from the back, both the order of the blocks
# and the order of nodes in each block should be reversed.
if self.collect_from_back:
block_list = [block[::-1] for block in block_list[::-1]]

self.property_set["block_list"] = block_list

return dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
features_transpiler:
- |
Added a new option, ``collect_from_back``, to
:class:`~qiskit.transpiler.passes.CollectMultiQBlocks`.
When set to ``True``, the blocks are collected in the reverse direction,
from the outputs towards the inputs of the circuit. The blocks are still
reported following the normal topological order.
This leads to an additional flexibility provided by the pass, and
additional optimization opportunities when combined with a circuit
resynthesis method.
40 changes: 40 additions & 0 deletions test/python/transpiler/test_collect_multiq_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,46 @@ def test_larger_blocks(self):

pass_manager.run(qc)

def test_collect_from_back(self):
"""Test the option to collect blocks from the outputs towards
the inputs.
┌───┐
q_0: ┤ H ├──■────■────■───────
└───┘┌─┴─┐ │ │
q_1: ─────┤ X ├──┼────┼───────
└───┘┌─┴─┐ │
q_2: ──────────┤ X ├──┼───────
└───┘┌─┴─┐┌───┐
q_3: ───────────────┤ X ├┤ H ├
└───┘└───┘
"""
qc = QuantumCircuit(4)
qc.h(0)
qc.cx(0, 1)
qc.cx(0, 2)
qc.cx(0, 3)
qc.h(3)

dag = circuit_to_dag(qc)
# For the circuit above, the topological order is unique
topo_ops = list(dag.topological_op_nodes())

# When collecting blocks of size-3 using the default direction,
# the first block should contain the H-gate and two CX-gates,
# and the second block should contain a single CX-gate and an H-gate.
pass_ = CollectMultiQBlocks(max_block_size=3, collect_from_back=False)
pass_.run(dag)
expected_blocks = [[topo_ops[0], topo_ops[1], topo_ops[2]], [topo_ops[3], topo_ops[4]]]
self.assertEqual(pass_.property_set["block_list"], expected_blocks)

# When collecting blocks of size-3 using the opposite direction,
# the first block should contain the H-gate and a single CX-gate,
# and the second block should contain two CX-gates and an H-gate.
pass_ = CollectMultiQBlocks(max_block_size=3, collect_from_back=True)
pass_.run(dag)
expected_blocks = [[topo_ops[0], topo_ops[1]], [topo_ops[2], topo_ops[3], topo_ops[4]]]
self.assertEqual(pass_.property_set["block_list"], expected_blocks)


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

0 comments on commit 55d2da8

Please sign in to comment.