3from typing
import TYPE_CHECKING
6 from toolbox.loadbalancing.api.Tree
import Tree
13 TopDown:
"peano4::SplitInstruction::Mode::AggressiveTopDown",
14 BottomUp:
"peano4::SplitInstruction::Mode::BottomUp",
18def assign_trees_to_ranks_round_Robin(
19 number_of_subtrees: int,
24 Take the trees and assign them to the ranks in a round-robin fashion
26 Round robin is the only assignment that the offline load balancing
27 supports, as Peano numbers its trees round robin, too: Tree t always
28 lives on rank t%R for R ranks. See _validate_round_robin() and the
29 documentation of toolbox::loadbalancing::strategies::Hardcoded.
31 Note that we implicitly assume that the first tree remains on rank 0,
32 so we really only assign number_of_subtrees-1 entries.
36 for i
in range(1, number_of_subtrees):
37 result.append(i % max_ranks)
47 Turn the enumerated splits into the C++ initialiser lists of Hardcoded
49 Entry k of each list describes the kth split.
51 @param parent_trees: [int]
52 Tree which splits in the kth split.
54 @param subpartition_sizes: [int]
55 Number of cells that the kth split hands over.
57 @param destination_ranks: [int]
58 Rank which receives the new tree of the kth split.
60 assert len(parent_trees) == len(subpartition_sizes)
61 assert len(destination_ranks) == len(subpartition_sizes)
63 def initialiser_list(values):
64 return "{" +
", ".join(str(value)
for value
in values) +
"}"
67 initialiser_list(parent_trees)
69 + initialiser_list(subpartition_sizes)
71 + initialiser_list(destination_ranks)
77 Translate the per-split flags of extract_split_instructions() into markers
79 @param bottom_up_flags: [bool]
80 True means that the corresponding split is a bottom-up split.
82 return [BottomUp
if bottom_up
else TopDown
for bottom_up
in bottom_up_flags]
87 Turn the user's modes argument into one marker per split
89 @param modes: str or [str]
90 Either a single marker which holds for all splits, or a sequence with
91 one marker per split. Each marker is TopDown ("top-down") or BottomUp
94 @param number_of_splits: int
95 Number of splits that the markers have to describe.
97 if isinstance(modes, str):
98 modes = [modes] * number_of_splits
101 if len(modes) != number_of_splits:
103 "got {} split mode(s), but the decomposition requires {} split(s)".format(
104 len(modes), number_of_splits
108 if mode
not in _CppModes:
110 "unknown split mode '{}'. Use '{}' or '{}'".format(
111 mode, TopDown, BottomUp
119 Turn the markers into the C++ mode argument of Hardcoded
121 The result is a single mode if all splits share the same mode, and an
122 initialiser list with one mode per split otherwise.
124 @param markers: [str]
125 One marker per split.
128 return _CppModes[TopDown]
129 if len(set(markers)) == 1:
130 return _CppModes[markers[0]]
131 return "{" +
", ".join(_CppModes[marker]
for marker
in markers) +
"}"
136 Normalise destination rank input to one rank per split instruction.
138 Accepted input formats:
139 1. Per-instruction ranks: len(destination_ranks) == len(subtrees)
140 2. Per-subtree ranks (excluding subtree 0): len(destination_ranks) == max(subtrees)
141 The rank for subtree s>0 is taken from destination_ranks[s-1] and repeated
142 for every occurrence of s in subtrees.
144 if len(destination_ranks) == len(subtrees):
145 return destination_ranks
147 max_subtree_id = max(subtrees, default=0)
148 if len(destination_ranks) == max_subtree_id:
149 return [destination_ranks[subtree_id - 1]
for subtree_id
in subtrees]
152 "destination_ranks ({}) must either match the number of split instructions ({}) "
153 "or provide one rank per subtree excluding subtree 0 ({})".format(
154 len(destination_ranks), len(subtrees), max_subtree_id
161 Ensure that the subtrees are assigned to the ranks round robin
163 Peano's tree numbers encode the rank which hosts a tree:
164 peano4::parallel::Node::getId() gives the nth tree on rank r the number
165 n*R+r for R ranks, and peano4::parallel::Node::getRank() hence maps tree
166 number t onto rank t%R. The offline plan refers to the trees through its
167 subtree numbers, i.e. a split of subtree s is meant to split the tree
168 with number s in Peano. This only holds if subtree s lives on rank s%R,
169 which is exactly what assign_trees_to_ranks_round_Robin() yields.
171 @param subtrees: [int]
172 Subtree created by the kth split.
174 @param destination_ranks: [int]
175 Rank which receives the subtree of the kth split.
177 @param number_of_ranks: int or None
178 Number of ranks R. None derives R from the largest destination rank.
180 if number_of_ranks
is None:
181 number_of_ranks = max(destination_ranks, default=0) + 1
182 for subtree, rank
in zip(subtrees, destination_ranks):
183 if rank != subtree % number_of_ranks:
185 "subtree {} is assigned to rank {}, but Peano can only host a tree with number {} on rank {} "
186 "for {} rank(s). Assign the trees to the ranks round robin, e.g. through "
187 "assign_trees_to_ranks_round_Robin()".format(
188 subtree, rank, subtree, subtree % number_of_ranks, number_of_ranks
193def create_constructor_initialisation_string(
195 destination_ranks: list[int],
196 verbose: bool =
False,
198 number_of_ranks: int |
None =
None,
201 Construct the constructor arguments for an instance of Hardcoded
203 The routine extracts the enumerated splits from the split tree, i.e.
204 a sequence of splits where the kth split reads "tree t hands n cells
205 over to a new tree on rank r and uses mode m to select these cells".
206 toolbox::loadbalancing::strategies::Hardcoded decides itself when it
207 issues the kth split.
209 By default, the mode of each split is inferred (see image below):
210 - `AggressiveTopDown`: all subtrees assigned to rank k start on the same level l.
211 - `BottomUp`: subtrees assigned to rank k start on different levels.
213 @image html src/toolbox/loadbalancing/api/output/create_constructor_initialisation_string.png
215 You can overwrite the inferred modes through modes.
217 @param peano_tree: toolbox.loadbalancing.api.Tree.Tree
218 Tree after the split, i.e. after SplitTrees.split_tree().
220 @param destination_ranks: [int]
221 Either one rank per split or one rank per subtree excluding subtree 0.
224 Print the extracted splits.
226 @param modes: None, str or [str]
227 None infers the mode of each split. Otherwise, pass a single marker
228 TopDown ("top-down") or BottomUp ("bottom-up") which holds for all
229 splits, or a sequence with one marker per split.
231 @param number_of_ranks: int or None
232 Number of MPI ranks. None derives it from the largest destination
233 rank. See _validate_round_robin() for why we need it.
235 @return `(hardcoded_config, hardcoded_mode)` where hardcoded_config is the
236 complete constructor argument string and hardcoded_mode is the mode
237 argument within this string.
239 from toolbox.loadbalancing.api.strategies.SplitTrees
import (
240 extract_split_instructions,
248 ) = extract_split_instructions(peano_tree)
260 print(
"Extracted hardcoded parameters:")
261 for k
in range(len(subtrees)):
263 " split {}: tree {} hands {} cells over to tree {} on rank {}, {}".format(
266 subpartition_sizes[k],
268 destination_ranks[k],
284 return hardcoded_config, hardcoded_mode
_normalise_markers(modes, number_of_splits)
Turn the user's modes argument into one marker per split.
_format_modes(markers)
Turn the markers into the C++ mode argument of Hardcoded.
_format_initialisation_lists(parent_trees, subpartition_sizes, destination_ranks)
Turn the enumerated splits into the C++ initialiser lists of Hardcoded.
_validate_round_robin(subtrees, destination_ranks, number_of_ranks=None)
Ensure that the subtrees are assigned to the ranks round robin.
_markers_from_flags(bottom_up_flags)
Translate the per-split flags of extract_split_instructions() into markers.
_normalise_destination_ranks(destination_ranks, subtrees)
Normalise destination rank input to one rank per split instruction.