Skip to content

Commit

Permalink
Add Expr support to DAGCircuit.replace_block_with_node
Browse files Browse the repository at this point in the history
Control-flow operations might not include their classical resources in
their `cargs` fields while in the `DAGCircuit` form (since the resources
have no particular order, and are inherent to the operation), so we
needed to update the logic of the `wire_pos_map` sorter to have a way to
specify that some bits need not be present in the output.
  • Loading branch information
jakelishman committed Jul 4, 2023
1 parent ac942ee commit 9fea695
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 13 deletions.
34 changes: 21 additions & 13 deletions qiskit/dagcircuit/dagcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1008,9 +1008,12 @@ def replace_block_with_op(self, node_block, op, wire_pos_map, cycle_check=True):
node block to be replaced
op (qiskit.circuit.Operation): The operation to replace the
block with
wire_pos_map (Dict[Qubit, int]): The dictionary mapping the qarg to
the position. This is necessary to reconstruct the qarg order
over multiple gates in the combined single op node.
wire_pos_map (Dict[Bit, int]): The dictionary mapping the bits to their positions in the
output ``qargs`` or ``cargs``. This is necessary to reconstruct the arg order over
multiple gates in the combined single op node. If a :class:`.Bit` is not in the
dictionary, it will not be added to the args; this can be useful when dealing with
control-flow operations that have inherent bits in their ``condition`` or ``target``
fields.
cycle_check (bool): When set to True this method will check that
replacing the provided ``node_block`` with a single node
would introduce a cycle (which would invalidate the
Expand Down Expand Up @@ -1040,16 +1043,21 @@ def replace_block_with_op(self, node_block, op, wire_pos_map, cycle_check=True):
for nd in node_block:
block_qargs |= set(nd.qargs)
block_cargs |= set(nd.cargs)
cond = getattr(nd.op, "condition", None)
if cond is not None:
block_cargs.update(condition_resources(cond).clbits)

# Create replacement node
new_node = DAGOpNode(
op,
sorted(block_qargs, key=lambda x: wire_pos_map[x]),
sorted(block_cargs, key=lambda x: wire_pos_map[x]),
)
if (condition := getattr(nd.op, "condition", None)) is not None:
block_cargs.update(condition_resources(condition).clbits)
elif isinstance(nd.op, SwitchCaseOp):
if isinstance(nd.op.target, Clbit):
block_cargs.add(nd.op.target)
elif isinstance(nd.op.target, ClassicalRegister):
block_cargs.update(nd.op.target)
else:
block_cargs.update(node_resources(nd.op.target).clbits)

block_qargs = [bit for bit in block_qargs if bit in wire_pos_map]
block_qargs.sort(key=wire_pos_map.get)
block_cargs = [bit for bit in block_cargs if bit in wire_pos_map]
block_cargs.sort(key=wire_pos_map.get)
new_node = DAGOpNode(op, block_qargs, block_cargs)

try:
new_node._node_id = self._multi_graph.contract_nodes(
Expand Down
37 changes: 37 additions & 0 deletions test/python/dagcircuit/test_dagcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -2062,6 +2062,43 @@ def test_single_node_block(self):
self.assertEqual(expected_dag.count_ops(), dag.count_ops())
self.assertIsInstance(new_node.op, XGate)

def test_replace_control_flow_block(self):
"""Test that we can replace a block of control-flow nodes with a single one."""
body = QuantumCircuit(1)
body.x(0)

qr = QuantumRegister(1)
cr1 = ClassicalRegister(3)
cr2 = ClassicalRegister(3)
dag = DAGCircuit()
dag.add_qreg(qr)
dag.add_creg(cr1)
dag.add_creg(cr2)
nodes = [
dag.apply_operation_back(IfElseOp(expr.logic_not(cr1[0]), body.copy(), None), qr, []),
dag.apply_operation_back(
SwitchCaseOp(expr.lift(cr2), [((0, 1, 2, 3), body.copy())]), qr, []
),
]

dag.replace_block_with_op(
nodes,
IfElseOp(expr.logic_or(expr.logic_not(cr1[0]), expr.less(cr2, 4)), body.copy(), None),
{qr[0]: 0},
)

expected = DAGCircuit()
expected.add_qreg(qr)
expected.add_creg(cr1)
expected.add_creg(cr2)
expected.apply_operation_back(
IfElseOp(expr.logic_or(expr.logic_not(cr1[0]), expr.less(cr2, 4)), body.copy(), None),
qr,
[],
)

self.assertEqual(dag, expected)


class TestDagProperties(QiskitTestCase):
"""Test the DAG properties."""
Expand Down

0 comments on commit 9fea695

Please sign in to comment.