Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Always try initial layout as first guess in preset passmanagers #5702

Merged
merged 8 commits into from
Apr 14, 2021
37 changes: 35 additions & 2 deletions qiskit/transpiler/preset_passmanagers/level2.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
from qiskit.transpiler.passes import Optimize1qGatesDecomposition
from qiskit.transpiler.passes import CommutativeCancellation
from qiskit.transpiler.passes import ApplyLayout
from qiskit.transpiler.passes import Layout2qDistance
from qiskit.transpiler.passes import CheckGateDirection
from qiskit.transpiler.passes import Collect2qBlocks
from qiskit.transpiler.passes import ConsolidateBlocks
Expand Down Expand Up @@ -99,10 +100,41 @@ def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager:
_given_layout = SetLayout(initial_layout)

def _choose_layout_condition(property_set):
# layout hasn't been set yet
return not property_set['layout']

# 1a. If layout_method is not set, first try a trivial layout
_choose_layout_0 = [] if pass_manager_config.layout_method \
else [TrivialLayout(coupling_map),
Layout2qDistance(coupling_map,
property_name='trivial_layout_score')]
# 1b. If a trivial layout wasn't perfect (ie no swaps are needed) then try using
# CSP layout to find a perfect layout
_choose_layout_1 = [] if pass_manager_config.layout_method \
else CSPLayout(coupling_map, call_limit=1000, time_limit=10, seed=seed_transpiler)

def _trivial_not_perfect(property_set):
# Verify that a trivial layout is perfect. If trivial_layout_score > 0
# the layout is not perfect. The layout is unconditionally set by trivial
# layout so we need to clear it before contuing.
if property_set['trivial_layout_score'] is not None:
if property_set['trivial_layout_score'] != 0:
property_set['layout']._wrapped = None
return True
return False

def _csp_not_found_match(property_set):
# If a layout hasn't been set by the time we run csp we need to run layout
if property_set['layout'] is None:
return True
# if CSP layout stopped for any reason other than solution found we need
# to run layout since CSP didn't converge.
if property_set['CSPLayout_stop_reason'] is not None \
and property_set['CSPLayout_stop_reason'] != "solution found":
return True
return False

# 1c. if CSP layout doesn't converge on a solution use layout_method (dense) to get a layout
if layout_method == 'trivial':
_choose_layout_2 = TrivialLayout(coupling_map)
elif layout_method == 'dense':
Expand Down Expand Up @@ -193,8 +225,9 @@ def _opt_control(property_set):
pm2 = PassManager()
if coupling_map or initial_layout:
pm2.append(_given_layout)
pm2.append(_choose_layout_1, condition=_choose_layout_condition)
pm2.append(_choose_layout_2, condition=_choose_layout_condition)
pm2.append(_choose_layout_0, condition=_choose_layout_condition)
pm2.append(_choose_layout_1, condition=_trivial_not_perfect)
pm2.append(_choose_layout_2, condition=_csp_not_found_match)
pm2.append(_embed)
pm2.append(_unroll3q)
pm2.append(_swap_check)
Expand Down
37 changes: 35 additions & 2 deletions qiskit/transpiler/preset_passmanagers/level3.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
from qiskit.transpiler.passes import ConsolidateBlocks
from qiskit.transpiler.passes import UnitarySynthesis
from qiskit.transpiler.passes import ApplyLayout
from qiskit.transpiler.passes import Layout2qDistance
from qiskit.transpiler.passes import CheckGateDirection
from qiskit.transpiler.passes import TimeUnitConversion
from qiskit.transpiler.passes import ALAPSchedule
Expand Down Expand Up @@ -105,10 +106,41 @@ def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager:
_given_layout = SetLayout(initial_layout)

def _choose_layout_condition(property_set):
# layout hasn't been set yet
return not property_set['layout']

def _csp_not_found_match(property_set):
# If a layout hasn't been set by the time we run csp we need to run layout
if property_set['layout'] is None:
return True
# if CSP layout stopped for any reason other than solution found we need
# to run layout since CSP didn't converge.
if property_set['CSPLayout_stop_reason'] is not None \
and property_set['CSPLayout_stop_reason'] != "solution found":
return True
return False

# 2a. If layout method is not set, first try a trivial layout
_choose_layout_0 = [] if pass_manager_config.layout_method \
else [TrivialLayout(coupling_map),
Layout2qDistance(coupling_map,
property_name='trivial_layout_score')]
# 2b. If trivial layout wasn't perfect (ie no swaps are needed) then try
# using CSP layout to find a perfect layout
_choose_layout_1 = [] if pass_manager_config.layout_method \
else CSPLayout(coupling_map, call_limit=10000, time_limit=60, seed=seed_transpiler)

def _trivial_not_perfect(property_set):
# Verify that a trivial layout is perfect. If trivial_layout_score > 0
# the layout is not perfect. The layout property set is unconditionally
# set by trivial layout so we clear that before running CSP
if property_set['trivial_layout_score'] is not None:
if property_set['trivial_layout_score'] != 0:
property_set['layout']._wrapped = None
return True
return False

# 2c. if CSP didn't converge on a solution use layout_method (dense).
if layout_method == 'trivial':
_choose_layout_2 = TrivialLayout(coupling_map)
elif layout_method == 'dense':
Expand Down Expand Up @@ -205,8 +237,9 @@ def _opt_control(property_set):
pm3.append(_reset + _meas)
if coupling_map or initial_layout:
pm3.append(_given_layout)
pm3.append(_choose_layout_1, condition=_choose_layout_condition)
pm3.append(_choose_layout_2, condition=_choose_layout_condition)
pm3.append(_choose_layout_0, condition=_choose_layout_condition)
pm3.append(_choose_layout_1, condition=_trivial_not_perfect)
pm3.append(_choose_layout_2, condition=_csp_not_found_match)
pm3.append(_embed)
pm3.append(_swap_check)
pm3.append(_swap, condition=_swap_condition)
Expand Down
43 changes: 43 additions & 0 deletions test/python/transpiler/test_preset_passmanagers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from qiskit.test.mock import (FakeTenerife, FakeMelbourne, FakeJohannesburg,
FakeRueschlikon, FakeTokyo, FakePoughkeepsie)
from qiskit.converters import circuit_to_dag
from qiskit.circuit.library import GraphState


def emptycircuit():
Expand Down Expand Up @@ -482,6 +483,48 @@ def test_layout_tokyo_fully_connected_cx(self, level):
result = transpile(qc, backend, optimization_level=level, seed_transpiler=42)
self.assertEqual(result._layout._p2v, expected_layouts[level])

@data(0, 1, 2, 3)
def test_all_levels_use_trivial_if_perfect(self, level):
"""Test that we always use trivial if it's a perfect match.

See: https://github.com/Qiskit/qiskit-terra/issues/5694 for more
details
"""
backend = FakeTokyo()
config = backend.configuration()

rows = [x[0] for x in config.coupling_map]
cols = [x[1] for x in config.coupling_map]

adjacency_matrix = np.zeros((20, 20))
adjacency_matrix[rows, cols] = 1
qc = GraphState(adjacency_matrix)
qc.measure_all()
expected = {
0: Qubit(QuantumRegister(20, 'q'), 0),
1: Qubit(QuantumRegister(20, 'q'), 1),
2: Qubit(QuantumRegister(20, 'q'), 2),
3: Qubit(QuantumRegister(20, 'q'), 3),
4: Qubit(QuantumRegister(20, 'q'), 4),
5: Qubit(QuantumRegister(20, 'q'), 5),
6: Qubit(QuantumRegister(20, 'q'), 6),
7: Qubit(QuantumRegister(20, 'q'), 7),
8: Qubit(QuantumRegister(20, 'q'), 8),
9: Qubit(QuantumRegister(20, 'q'), 9),
10: Qubit(QuantumRegister(20, 'q'), 10),
11: Qubit(QuantumRegister(20, 'q'), 11),
12: Qubit(QuantumRegister(20, 'q'), 12),
13: Qubit(QuantumRegister(20, 'q'), 13),
14: Qubit(QuantumRegister(20, 'q'), 14),
15: Qubit(QuantumRegister(20, 'q'), 15),
16: Qubit(QuantumRegister(20, 'q'), 16),
17: Qubit(QuantumRegister(20, 'q'), 17),
18: Qubit(QuantumRegister(20, 'q'), 18),
19: Qubit(QuantumRegister(20, 'q'), 19)
}
trans_qc = transpile(qc, backend, optimization_level=level)
self.assertEqual(trans_qc._layout._p2v, expected)

@data(0, 1)
def test_trivial_layout(self, level):
"""Test that trivial layout is preferred in level 0 and 1
Expand Down