Skip to content

Commit

Permalink
add check_map to tests. tests passing
Browse files Browse the repository at this point in the history
  • Loading branch information
ewinston committed Sep 2, 2022
1 parent 349dca4 commit ef35278
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 202 deletions.
2 changes: 1 addition & 1 deletion qiskit/circuit/instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def __eq__(self, other):
self_param, other_param, atol=_CUTOFF_PRECISION, rtol=0
):
continue
except TypeError as err:
except TypeError:
pass

try:
Expand Down
4 changes: 1 addition & 3 deletions qiskit/dagcircuit/dagnode.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,7 @@ def semantic_eq(node1, node2, bit_indices1=None, bit_indices2=None):
):
if bit_indices1.get(node1.wire, None) == bit_indices2.get(node2.wire, None):
return True
if isinstance(node1, DAGOpNode):
print(repr(node1))
print('> ', repr(node2))

return False


Expand Down
107 changes: 11 additions & 96 deletions qiskit/transpiler/passes/routing/stochastic_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.transpiler.basepasses import TransformationPass
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.transpiler.coupling import CouplingMap
from qiskit.dagcircuit import DAGCircuit
from qiskit.circuit.library.standard_gates import SwapGate
from qiskit.transpiler.layout import Layout
from qiskit.transpiler.passes.routing.utils import (
transpile_cf_multiblock,
transpile_cf_looping,
layout_transform,
)
from qiskit.circuit import IfElseOp, WhileLoopOp, ForLoopOp, ControlFlowOp
from qiskit.converters import dag_to_circuit, circuit_to_dag

from qiskit._accelerate import stochastic_swap as stochastic_swap_rs

Expand Down Expand Up @@ -295,7 +298,6 @@ def _mapper(self, circuit_graph, coupling_graph, trials=20):
node.op = updated_ctrl_op
cf_layer = True
if cf_layer:
cf_layer = True
order = layout.reorder_bits(dagcircuit_output.qubits)
dagcircuit_output.compose(subdag, qubits=order)
success_flag = True
Expand Down Expand Up @@ -366,100 +368,13 @@ def _mapper(self, circuit_graph, coupling_graph, trials=20):

def _transpile_controlflow_op(self, cf_op, current_layout):
"""handle controlflow ops by type"""
new_coupling = layout_transform(self.coupling_map, current_layout)
_pass = self.__class__(new_coupling, initial_layout=None, seed=self.seed)
if isinstance(cf_op, IfElseOp):
return self._transpile_controlflow_multiblock(cf_op, current_layout)
return transpile_cf_multiblock(
_pass, cf_op, current_layout, new_coupling, self.qregs, seed=self.seed
)
elif isinstance(cf_op, (ForLoopOp, WhileLoopOp)):
return self._transpile_controlflow_looping(cf_op, current_layout)
return transpile_cf_looping(_pass, cf_op, current_layout, new_coupling, seed=self.seed)
else:
raise TranspilerError(f"unsupported control flow operation: {cf_op}")

def _transpile_controlflow_multiblock(self, cf_op, current_layout):
# pylint: disable=cyclic-import
from qiskit.transpiler.passes.routing import LayoutTransformation

block_circuits = [] # control flow circuit blocks
block_dags = [] # control flow dag blocks
block_layouts = [] # control flow layouts
new_coupling = layout_transform(self.coupling_map, current_layout)

for i, block in enumerate(cf_op.blocks):
dag_block = circuit_to_dag(block)
_pass = self.__class__(new_coupling, initial_layout=None, seed=(i + 1) * self.seed)
updated_dag_block = _pass.run(dag_block)
block_dags.append(updated_dag_block)
block_layouts.append(_pass.property_set["final_layout"].copy())
changed_layouts = [current_layout != layout for layout in block_layouts]
if not any(changed_layouts):
return cf_op, current_layout
depth_cnt = [bdag.depth() for bdag in block_dags]
maxind = np.argmax(depth_cnt)
for i, dag in enumerate(block_dags):
if i == maxind:
block_circuits.append(dag_to_circuit(dag))
else:
layout_xform = LayoutTransformation(
new_coupling,
block_layouts[i],
block_layouts[maxind],
seed = i*self.seed,
)
match_dag = layout_xform.run(dag)
block_circuits.append(dag_to_circuit(match_dag))

final_permutation = combine_permutations(
list(current_layout.get_physical_bits().keys()),
list(block_layouts[maxind].get_physical_bits().keys()),
)
final_layout = Layout.from_intlist(final_permutation, *self.qregs.values())
return cf_op.replace_blocks(block_circuits), final_layout

def _transpile_controlflow_looping(self, cf_op, current_layout):
"""for looping this pass adds a swap layer at the end of the loop body to bring
the layout back to the expected starting layout. This could be reduced a bit by
specializing to for_loop and while_loop.
"""
# pylint: disable=cyclic-import
from qiskit.transpiler.passes.routing import LayoutTransformation

new_coupling = layout_transform(self.coupling_map, current_layout)
dag_block = circuit_to_dag(cf_op.blocks[0])
_pass = self.__class__(new_coupling, initial_layout=None, seed=2 * self.seed)
start_qreg = QuantumRegister(len(self._qubit_indices), "q")
start_layout = Layout.generate_trivial_layout(start_qreg)
updated_dag_block = _pass.run(dag_block)
updated_layout = _pass.property_set["final_layout"].copy()
layout_xform = LayoutTransformation(new_coupling, updated_layout,
start_layout, seed=self.seed)
match_dag = layout_xform.run(updated_dag_block)
match_circ = dag_to_circuit(match_dag)
return cf_op.replace_blocks([match_circ]), current_layout


def combine_permutations(*permutations):
"""
chain a series of permutations
"""
order = permutations[0]
for this_order in permutations[1:]:
order = [order[i] for i in this_order]
return order


def layout_transform(cmap, layout, qreg=None):
"""Transform coupling map according to layout.
Args:
cmap (CouplingMap): coupling map to transform
layout (Layout): layout to apply
qreg (QuantumRegister): register to use for indexing
Returns:
CouplingMap: coupling map under specified layout.
"""
if qreg is None:
qreg = QuantumRegister(len(layout), "q")
new_map = []
vmap = layout.get_virtual_bits()
for bit0, bit1 in cmap.get_edges():
qubit0, qubit1 = qreg[bit0], qreg[bit1]
new_map.append([vmap[qubit0], vmap[qubit1]])
return CouplingMap(couplinglist=new_map)
158 changes: 158 additions & 0 deletions qiskit/transpiler/passes/routing/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2018.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Utility functions for routing"""

import numpy as np
from qiskit.transpiler.coupling import CouplingMap
from qiskit.transpiler.layout import Layout
from qiskit.circuit import QuantumRegister
from qiskit.circuit.exceptions import CircuitError


def transpile_cf_multiblock(tpass, cf_op, current_layout, coupling, qregs, seed=None):
"""Transpile control flow instructions which may contain multiple
blocks (e.g. IfElseOp). Since each control flow block may
induce a yield a different layout, this function applies swaps
to the shorter depth blocks to make all final layouts match.
Args:
tpass (BasePass): Transpiler pass object to use recursively.
cf_op (IfElseOp): multiblock instruction.
current_layout (Layout): The current layout at the start of the instruction.
coupling (CouplingMap): the coupling map to use within the control flow instruction.
qregs (list(QuantumRegister)): quantum registers for circuit
seed (int): seed for RNG of internal layout transformation.
Returns:
IfElseOp: transpiled control flow operation
final_layout (Layout): layout after instruction
"""
# pylint: disable=cyclic-import
from qiskit.transpiler.passes.routing.layout_transformation import LayoutTransformation
from qiskit.converters import dag_to_circuit, circuit_to_dag

block_circuits = [] # control flow circuit blocks
block_dags = [] # control flow dag blocks
block_layouts = [] # control flow layouts

for i, block in enumerate(cf_op.blocks):
dag_block = circuit_to_dag(block)
updated_dag_block = tpass.run(dag_block)
block_dags.append(updated_dag_block)
block_layouts.append(tpass.property_set["final_layout"].copy())
changed_layouts = [current_layout != layout for layout in block_layouts]
# if not any(changed_layouts):
# return cf_op, current_layout
depth_cnt = [bdag.depth() for bdag in block_dags]
maxind = np.argmax(depth_cnt)
for i, dag in enumerate(block_dags):
if i == maxind:
block_circuits.append(dag_to_circuit(dag))
else:
layout_xform = LayoutTransformation(
coupling, block_layouts[i], block_layouts[maxind], seed=seed
)
match_dag = layout_xform.run(dag)
block_circuits.append(dag_to_circuit(match_dag))
final_permutation = combine_permutations(
get_ordered_virtual_qubits(current_layout, qregs),
get_ordered_virtual_qubits(block_layouts[maxind], qregs),
)
final_layout = Layout.from_intlist(final_permutation, *qregs.values())
return cf_op.replace_blocks(block_circuits), final_layout


def transpile_cf_looping(tpass, cf_op, current_layout, coupling, seed=None):
"""For looping this pass adds a swap layer using LayoutTransformation
to the end of the loop body to bring the layout back to the
starting layout. This prevents reapplying layout changing
swaps for every iteration of the loop.
Args:
tpass (BasePass): pass object to run
cf_op (ForLoopOp, WhileLoopOp): looping instruction.
current_layout (Layout): The current layout at the start and by the
end of the instruction.
coupling (CouplingMap): the coupling map to use within the control flow instruction.
Returns:
tuple(ControlFlowOp, Layout): Transpiled control flow
operation and layout after instruction
"""
# pylint: disable=cyclic-import
from qiskit.transpiler.passes.routing.layout_transformation import LayoutTransformation
from qiskit.converters import dag_to_circuit, circuit_to_dag

dag_block = circuit_to_dag(cf_op.blocks[0])
start_qreg = QuantumRegister(coupling.size(), "q")
start_layout = Layout.generate_trivial_layout(start_qreg)
updated_dag_block = tpass.run(dag_block)
updated_layout = tpass.property_set["final_layout"].copy()

layout_xform = LayoutTransformation(coupling, updated_layout, start_layout, seed=seed)
match_dag = layout_xform.run(updated_dag_block)
match_circ = dag_to_circuit(match_dag)
return cf_op.replace_blocks([match_circ]), current_layout


def get_ordered_virtual_qubits(layout, qregs):
"""Get list of virtual qubits associated with odered list
of physical qubits.
Args:
layout (Layout): circuit layout
qregs (list(QuantumRegister)): list of registers for circuit
Returns:
list(int): list of virtual qubit indices
"""
p2v = layout.get_virtual_bits()
return [p2v[qubit] for qreg in qregs.values() for qubit in qreg]


def combine_permutations(*permutations):
"""
Chain a series of permutations.
Args:
*permutations (list(int)): permutations to combine
Returns:
list: combined permutation
"""
order = permutations[0]
for this_order in permutations[1:]:
order = [order[i] for i in this_order]
return order


def layout_transform(cmap, layout, qreg=None):
"""Transform coupling map according to layout.
Args:
cmap (CouplingMap): coupling map to transform
layout (Layout): layout to apply
qreg (QuantumRegister): register to use for indexing
Returns:
CouplingMap: coupling map under specified layout.
"""
if qreg is None:
qreg = QuantumRegister(len(layout), "q")
new_map = []
vmap = layout.get_virtual_bits()
for bit0, bit1 in cmap.get_edges():
qubit0, qubit1 = qreg[bit0], qreg[bit1]
new_map.append([vmap[qubit0], vmap[qubit1]])
return CouplingMap(couplinglist=new_map)
7 changes: 6 additions & 1 deletion qiskit/transpiler/passes/utils/check_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

from qiskit.transpiler.basepasses import AnalysisPass
from qiskit.circuit.controlflow import ControlFlowOp
from qiskit.transpiler.layout import Layout
from qiskit.transpiler.passes.routing.utils import layout_transform


class CheckMap(AnalysisPass):
Expand Down Expand Up @@ -43,6 +45,7 @@ def run(self, dag):
dag (DAGCircuit): DAG to map.
"""
from qiskit.converters import circuit_to_dag

self.property_set["is_swap_mapped"] = True

if self.coupling_map is None or len(self.coupling_map.graph) == 0:
Expand All @@ -67,6 +70,8 @@ def run(self, dag):
self.property_set["is_swap_mapped"] = False
return
for cf_instr in dag.op_nodes(op=ControlFlowOp):
layout = Layout(dict(enumerate(cf_instr.qargs)))
check_map = CheckMap(layout_transform(self.coupling_map, layout))
for block in cf_instr.op.blocks:
dag_block = circuit_to_dag(block)
self.run(dag_block)
check_map.run(dag_block)
1 change: 0 additions & 1 deletion test/python/transpiler/test_check_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ def test_swap_mapped_cf_false(self):
pass_ = CheckMap(coupling)
pass_.run(dag)
self.assertFalse(pass_.property_set["is_swap_mapped"])



if __name__ == "__main__":
Expand Down
Loading

0 comments on commit ef35278

Please sign in to comment.