Skip to content

Commit

Permalink
fix layer bug
Browse files Browse the repository at this point in the history
this fixes a bug where gates were dropped if they shared a layer with a
control flow op.
  • Loading branch information
ewinston committed Oct 5, 2022
1 parent 4dd68a9 commit 753a637
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 56 deletions.
111 changes: 60 additions & 51 deletions qiskit/transpiler/passes/routing/stochastic_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,29 +297,11 @@ def _mapper(self, circuit_graph, coupling_graph, trials=20):

# Iterate over layers
for i, layer in enumerate(layerlist):
subdag = layer["graph"]
cf_layer = False
cf_layout = None
for node in subdag.op_nodes(op=ControlFlowOp):
updated_ctrl_op, cf_layout, idle_qubits = self._transpile_controlflow_op(
node, layout, circuit_graph, _seed=self.seed
)
cf_subdag = DAGCircuit()
cf_qubits = [qubit for qubit in circuit_graph.qubits if qubit not in idle_qubits]
qreg = QuantumRegister(len(cf_qubits), "q")
cf_subdag.add_qreg(qreg)
for creg in subdag.cregs.values():
cf_subdag.add_creg(creg)
cf_subdag.apply_operation_back(updated_ctrl_op, cf_subdag.qubits, node.cargs)
subdag = cf_subdag
cf_layer = True
if cf_layer:
target_qubits = [
qubit for qubit in dagcircuit_output.qubits if qubit not in idle_qubits
]
order = layout.reorder_bits(target_qubits)
dagcircuit_output.compose(subdag, qubits=order)
success_flag = True
layer_dag = layer["graph"]
cf_nodes = layer_dag.op_nodes(op=ControlFlowOp)
if cf_nodes:
# handle layers with control flow serially
success_flag = False
else:
# Attempt to find a permutation for this layer
success_flag, best_circuit, best_depth, best_layout = self._layer_permutation(
Expand All @@ -336,35 +318,63 @@ def _mapper(self, circuit_graph, coupling_graph, trials=20):

# Go through each gate in the layer
for j, serial_layer in enumerate(serial_layerlist):

success_flag, best_circuit, best_depth, best_layout = self._layer_permutation(
serial_layer["partition"], layout, qubit_subset, coupling_graph, trials
)
logger.debug("mapper: layer %d, sublayer %d", i, j)
logger.debug(
"mapper: success_flag=%s,best_depth=%s,", success_flag, str(best_depth)
)

# Give up if we fail again
if not success_flag:
raise TranspilerError(
"swap mapper failed: " + "layer %d, sublayer %d" % (i, j)
)

# Update the record of qubit positions
# for each inner iteration
layout = best_layout
# Update the DAG
if not self.fake_run:
self._layer_update(
dagcircuit_output,
serial_layerlist[j],
best_layout,
best_depth,
subdag = serial_layer["graph"]
# subdag has only one operation
op_node = subdag.op_nodes()[0]
if not isinstance(op_node.op, ControlFlowOp):
(
success_flag,
best_circuit,
best_depth,
best_layout,
) = self._layer_permutation(
serial_layer["partition"], layout, qubit_subset, coupling_graph, trials
)
logger.debug("mapper: layer %d, sublayer %d", i, j)
logger.debug(
"mapper: success_flag=%s,best_depth=%s,", success_flag, str(best_depth)
)

elif not cf_layer:
# Give up if we fail again
if not success_flag:
raise TranspilerError(
"swap mapper failed: " + "layer %d, sublayer %d" % (i, j)
)

# Update the record of qubit positions
# for each inner iteration
layout = best_layout
# Update the DAG
if not self.fake_run:
self._layer_update(
dagcircuit_output,
serial_layerlist[j],
best_layout,
best_depth,
best_circuit,
)
else:
updated_ctrl_op, cf_layout, idle_qubits = self._transpile_controlflow_op(
op_node, layout, circuit_graph, _seed=self.seed
)
cf_subdag = DAGCircuit()
cf_qubits = [
qubit for qubit in circuit_graph.qubits if qubit not in idle_qubits
]
qreg = QuantumRegister(len(cf_qubits), "q")
cf_subdag.add_qreg(qreg)
for creg in subdag.cregs.values():
cf_subdag.add_creg(creg)
cf_subdag.apply_operation_back(
updated_ctrl_op, cf_subdag.qubits, op_node.cargs
)
target_qubits = [
qubit for qubit in dagcircuit_output.qubits if qubit not in idle_qubits
]
order = layout.reorder_bits(target_qubits)
dagcircuit_output.compose(cf_subdag, qubits=order)
layout = cf_layout
else:
# Update the record of qubit positions for each iteration
layout = best_layout

Expand All @@ -373,8 +383,7 @@ def _mapper(self, circuit_graph, coupling_graph, trials=20):
self._layer_update(
dagcircuit_output, layerlist[i], best_layout, best_depth, best_circuit
)
elif cf_layout:
layout = cf_layout

# This is the final edgemap. We might use it to correctly replace
# any measurements that needed to be removed earlier.
logger.debug("mapper: self.trivial_layout = %s", self.trivial_layout)
Expand Down
8 changes: 3 additions & 5 deletions qiskit/transpiler/passes/routing/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def route_cf_multiblock(tpass, cf_opnode, current_layout, qregs, root_dag, seed=
depth_cnt = [bdag.depth(recurse=True) for bdag in block_dags]
maxind = np.argmax(depth_cnt)
block_circuits = [None] * len(block_layouts)
p2v = current_layout._p2v
p2v = current_layout.get_physical_bits()
idle_qubits = [None] * len(block_dags)
for i, updated_dag_block in enumerate(block_dags):
if i == maxind:
Expand Down Expand Up @@ -122,7 +122,7 @@ def route_cf_looping(tpass, cf_opnode, current_layout, root_dag, seed=None):
from qiskit.transpiler.passes.routing.layout_transformation import LayoutTransformation
from qiskit.converters import dag_to_circuit, circuit_to_dag

cf_op = cf_opnode.op
cf_op = cf_opnode.op # control flow operation
coupling = tpass.coupling_map
dag_block = circuit_to_dag(cf_op.blocks[0])
# expand to full width for routing
Expand All @@ -131,16 +131,14 @@ def route_cf_looping(tpass, cf_opnode, current_layout, root_dag, seed=None):
order = [root_dag.qubits.index(bit) for bit in cf_opnode.qargs]
full_dag_block.compose(dag_block, qubits=order)
updated_dag_block = tpass.run(full_dag_block)

updated_layout = tpass.property_set["final_layout"].copy()

layout_xform = LayoutTransformation(
coupling, updated_layout, start_layout, seed=seed, inplace=False
)
layout_xform.run(updated_dag_block)
physical_swap_dag = layout_xform.property_set["perm_circ"].circuit
if physical_swap_dag.depth():
p2v = current_layout._p2v
p2v = current_layout.get_physical_bits()
virtual_swap_dag = updated_dag_block.copy_empty_like()
order = [
p2v[virtual_swap_dag.qubits.index(qubit)]
Expand Down
25 changes: 25 additions & 0 deletions test/python/transpiler/test_stochastic_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -1184,6 +1184,31 @@ def test_controlflow_disjoint_multiblock(self):
expected.if_else((cr[0], 1), etrue_body, efalse_body, [0, 1, 2], [])
self.assertEqual(cqc, expected)

def test_controlflow_multiple_ops_per_layer(self):
"""Test circuits with multiple operations per layer"""
num_qubits = 5
coupling = CouplingMap.from_line(num_qubits)
check_map_pass = CheckMap(coupling)
qr = QuantumRegister(num_qubits, "q")
qc = QuantumCircuit(qr)
qc.cx(0, 2)
with qc.for_loop((0,)):
qc.cx(2, 4)
cqc = StochasticSwap(coupling, seed=68745)(qc)

expected = QuantumCircuit(qr)
expected.swap(0, 1)
expected.cx(1, 2)
efor_body = QuantumCircuit(3)
efor_body.swap(1, 2)
efor_body.cx(0, 1)
efor_body.swap(2, 1)
expected.for_loop((0,), None, efor_body, [2, 3, 4], [])

check_map_pass.run(circuit_to_dag(expected))
self.assertTrue(check_map_pass.property_set["is_swap_mapped"])
self.assertEqual(cqc, expected)


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

0 comments on commit 753a637

Please sign in to comment.