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

Add config option to leverage all cores for sabre (backport #12780) #12841

Merged
merged 1 commit into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 52 additions & 18 deletions qiskit/transpiler/preset_passmanagers/builtin_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

"""Built-in transpiler stage plugins for preset pass managers."""

import os

from qiskit.transpiler.passmanager import PassManager
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.transpiler.passes import BasicSwap
Expand Down Expand Up @@ -63,6 +65,10 @@
SXGate,
SXdgGate,
)
from qiskit.utils.parallel import CPU_COUNT
from qiskit import user_config

CONFIG = user_config.get_config()


class DefaultInitPassManager(PassManagerStagePlugin):
Expand Down Expand Up @@ -397,11 +403,12 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
pass_manager_config.initial_layout,
)
if optimization_level == 0:
trial_count = _get_trial_count(5)
routing_pass = SabreSwap(
coupling_map_routing,
heuristic="basic",
seed=seed_transpiler,
trials=5,
trials=trial_count,
)
return common.generate_routing_passmanager(
routing_pass,
Expand All @@ -411,11 +418,12 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
use_barrier_before_measurement=True,
)
if optimization_level == 1:
trial_count = _get_trial_count(5)
routing_pass = SabreSwap(
coupling_map_routing,
heuristic="decay",
seed=seed_transpiler,
trials=5,
trials=trial_count,
)
return common.generate_routing_passmanager(
routing_pass,
Expand All @@ -429,11 +437,13 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
use_barrier_before_measurement=True,
)
if optimization_level == 2:
trial_count = _get_trial_count(20)

routing_pass = SabreSwap(
coupling_map_routing,
heuristic="decay",
seed=seed_transpiler,
trials=10,
trials=trial_count,
)
return common.generate_routing_passmanager(
routing_pass,
Expand All @@ -446,11 +456,12 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
use_barrier_before_measurement=True,
)
if optimization_level == 3:
trial_count = _get_trial_count(20)
routing_pass = SabreSwap(
coupling_map_routing,
heuristic="decay",
seed=seed_transpiler,
trials=20,
trials=trial_count,
)
return common.generate_routing_passmanager(
routing_pass,
Expand Down Expand Up @@ -737,12 +748,15 @@ def _swap_mapped(property_set):
max_trials=2500, # Limits layout scoring to < 600ms on ~400 qubit devices
)
layout.append(ConditionalController(choose_layout_1, condition=_layout_not_perfect))

trial_count = _get_trial_count(5)

choose_layout_2 = SabreLayout(
coupling_map,
max_iterations=2,
seed=pass_manager_config.seed_transpiler,
swap_trials=5,
layout_trials=5,
swap_trials=trial_count,
layout_trials=trial_count,
skip_routing=pass_manager_config.routing_method is not None
and pass_manager_config.routing_method != "sabre",
)
Expand All @@ -769,12 +783,15 @@ def _swap_mapped(property_set):
layout.append(
ConditionalController(choose_layout_0, condition=_choose_layout_condition)
)

trial_count = _get_trial_count(20)

choose_layout_1 = SabreLayout(
coupling_map,
max_iterations=2,
seed=pass_manager_config.seed_transpiler,
swap_trials=20,
layout_trials=20,
swap_trials=trial_count,
layout_trials=trial_count,
skip_routing=pass_manager_config.routing_method is not None
and pass_manager_config.routing_method != "sabre",
)
Expand All @@ -801,12 +818,15 @@ def _swap_mapped(property_set):
layout.append(
ConditionalController(choose_layout_0, condition=_choose_layout_condition)
)

trial_count = _get_trial_count(20)

choose_layout_1 = SabreLayout(
coupling_map,
max_iterations=4,
seed=pass_manager_config.seed_transpiler,
swap_trials=20,
layout_trials=20,
swap_trials=trial_count,
layout_trials=trial_count,
skip_routing=pass_manager_config.routing_method is not None
and pass_manager_config.routing_method != "sabre",
)
Expand Down Expand Up @@ -902,42 +922,50 @@ def _swap_mapped(property_set):
layout = PassManager()
layout.append(_given_layout)
if optimization_level == 0:
trial_count = _get_trial_count(5)

layout_pass = SabreLayout(
coupling_map,
max_iterations=1,
seed=pass_manager_config.seed_transpiler,
swap_trials=5,
layout_trials=5,
swap_trials=trial_count,
layout_trials=trial_count,
skip_routing=pass_manager_config.routing_method is not None
and pass_manager_config.routing_method != "sabre",
)
elif optimization_level == 1:
trial_count = _get_trial_count(5)

layout_pass = SabreLayout(
coupling_map,
max_iterations=2,
seed=pass_manager_config.seed_transpiler,
swap_trials=5,
layout_trials=5,
swap_trials=trial_count,
layout_trials=trial_count,
skip_routing=pass_manager_config.routing_method is not None
and pass_manager_config.routing_method != "sabre",
)
elif optimization_level == 2:
trial_count = _get_trial_count(20)

layout_pass = SabreLayout(
coupling_map,
max_iterations=2,
seed=pass_manager_config.seed_transpiler,
swap_trials=20,
layout_trials=20,
swap_trials=trial_count,
layout_trials=trial_count,
skip_routing=pass_manager_config.routing_method is not None
and pass_manager_config.routing_method != "sabre",
)
elif optimization_level == 3:
trial_count = _get_trial_count(20)

layout_pass = SabreLayout(
coupling_map,
max_iterations=4,
seed=pass_manager_config.seed_transpiler,
swap_trials=20,
layout_trials=20,
swap_trials=trial_count,
layout_trials=trial_count,
skip_routing=pass_manager_config.routing_method is not None
and pass_manager_config.routing_method != "sabre",
)
Expand All @@ -957,3 +985,9 @@ def _swap_mapped(property_set):
embed = common.generate_embed_passmanager(coupling_map)
layout.append(ConditionalController(embed.to_flow_controller(), condition=_swap_mapped))
return layout


def _get_trial_count(default_trials=5):
if CONFIG.get("sabre_all_threads", None) or os.getenv("QISKIT_SABRE_ALL_THREADS"):
return max(CPU_COUNT, default_trials)
return default_trials
9 changes: 9 additions & 0 deletions qiskit/user_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class UserConfig:
transpile_optimization_level = 1
parallel = False
num_processes = 4
sabre_all_threads = true

"""

Expand Down Expand Up @@ -168,6 +169,13 @@ def read_config_file(self):
)
self.settings["num_processes"] = num_processes

# Parse sabre_all_threads
sabre_all_threads = self.config_parser.getboolean(
"default", "sabre_all_threads", fallback=None
)
if sabre_all_threads is not None:
self.settings["sabre_all_threads"] = sabre_all_threads


def set_config(key, value, section=None, file_path=None):
"""Adds or modifies a user configuration
Expand Down Expand Up @@ -208,6 +216,7 @@ def set_config(key, value, section=None, file_path=None):
"transpile_optimization_level",
"parallel",
"num_processes",
"sabre_all_threads",
}

if section in [None, "default"]:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
features_transpiler:
- |
Added a new user config file option ``sabre_all_threads`` and a
corresponding environment variable ``QISKIT_SABRE_ALL_THREADS``. When this
flag is set the preset pass managers will run the :class:`.SabreLayout`
and :class:`.SabreSwap` transpiler passes using all the available
CPUs on the local system. Using this option is a tradeoff between
determinism of output between different computers and potentially better
output with fewer :class:`.SwapGate`\s.

These transpiler passes run multiple random trials in parallel and pick
the output which results in the fewest :class:`.SwapGate`\s. As a rule of
thumb, if you run more trials, this provides the algorithm more opportunities
to find a better result. By default, the preset pass managers use a fixed
number of trials, in this release 5 trials for levels 0 and 1, and 20
trials for levels 2 and 3, but these numbers may change in future releases
(and were different in historical releases). Using a fixed number of
trials results in deterministic results regardless of the local system,
because even with a fixed seed if you were to default to the number of
local CPUs available the results would different when running between
different computers.

If the default number of trials for a given optimization level is higher
than the number of local CPUs it will use the optimization level default
which is higher.
Loading