Skip to content

Commit a1987fe

Browse files
committed
Merge branch 'main' into hops
2 parents d226781 + 27f75f8 commit a1987fe

5 files changed

Lines changed: 64 additions & 42 deletions

File tree

accelforge/frontend/mapping/mapping.py

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from pydantic import ConfigDict, Discriminator, Tag, computed_field
3232
import sympy
3333

34+
from accelforge.frontend.renames import EinsumName
3435
from accelforge.util._basetypes import (
3536
# Parsing helpers for the input files.
3637
EvalableModel,
@@ -318,7 +319,13 @@ class Loop(MappingNode):
318319
rank_variable: set[RankVariable] | RankVariable
319320
""" The rank variable(s) iterated over in this loop. This may be a
320321
single rank variable, or a set of rank variables if the loop is shared between
321-
multiple Einsums. """
322+
multiple Einsums.
323+
324+
NOTE FOR DEVELOPERS: If the rank variable is a set DURING PMAPPIN GENERATION, then
325+
it means that the loop is a placeholder for multiple loops in the given Einsum. They
326+
will be split into multiple loops later. If the rank variable
327+
328+
"""
322329

323330
tile_shape: EvalsTo[sympy.Symbol | sympy.Expr | int | str] = "symbol"
324331
"""
@@ -353,6 +360,12 @@ class Loop(MappingNode):
353360
tile_shape for details.
354361
"""
355362

363+
_einsum_to_rank_variables: dict[EinsumName, set[RankVariable]] = {}
364+
"""
365+
If there are multiple rank variables in this loop, this dictionary says which rank
366+
variables belong to which Einsum.
367+
"""
368+
356369
_calculated_n_iterations: (
357370
Literal["symbol"] | sympy.Symbol | sympy.Expr | int | str | None
358371
) = None
@@ -1073,8 +1086,11 @@ def _pop_loop_group(groups: list[list[MappingNode]]) -> list[MappingNode]:
10731086
# variables and assume that rank variable translation would fix it.
10741087
assert len(my_loop_group) == 1 or len(other_loop_group) == 1
10751088
has_one, may_not_have_one = my_loop_group, other_loop_group
1089+
1090+
switched = False
10761091
if len(has_one) != 1:
10771092
has_one, may_not_have_one = other_loop_group, my_loop_group
1093+
switched = True
10781094

10791095
l = copy.deepcopy(has_one.pop(0))
10801096
l.rank_variable = (
@@ -1093,10 +1109,21 @@ def _pop_loop_group(groups: list[list[MappingNode]]) -> list[MappingNode]:
10931109
f"Warning. Matching loops {l} and {l2}. Need rank variable translation here."
10941110
)
10951111

1112+
my_rv = l.rank_variable if isinstance(l.rank_variable, set) else set([l.rank_variable])
1113+
other_rv = l2.rank_variable if isinstance(l2.rank_variable, set) else set([l2.rank_variable])
1114+
if switched:
1115+
my_rv, other_rv = other_rv, my_rv
1116+
10961117
may_not_have_one.remove(l2)
10971118
rv = l2.rank_variable
1119+
for compute in self.get_nodes_of_type(Compute):
1120+
for r in my_rv:
1121+
l._einsum_to_rank_variables[compute.einsum] = r
10981122
rv = rv if isinstance(rv, set) else set([rv])
10991123
l.rank_variable = l.rank_variable | rv
1124+
for compute in other.get_nodes_of_type(Compute):
1125+
for r in other_rv:
1126+
l._einsum_to_rank_variables[compute.einsum] = r
11001127
to_add = [l]
11011128

11021129
zipped_groups.append(to_add)
@@ -1542,15 +1569,18 @@ def split_reservations(self):
15421569
new_nodes.append(node)
15431570
self.nodes = new_nodes
15441571

1545-
def split_loop_with_multiple_rank_variables(self, stride_and_halo: dict[str, tuple[int, int]]):
1572+
def split_loop_with_multiple_rank_variables(self, einsum_name: EinsumName):
15461573
new_nodes = []
15471574
my_rank_variables = set(x[1] for x in stride_and_halo)
15481575
for node in self.nodes:
15491576
if isinstance(node, Loop) and isinstance(node.rank_variable, set):
1550-
ranks_in_stride_and_halo = node.rank_variable & my_rank_variables
1551-
assert len(ranks_in_stride_and_halo) == 1, f"Expected 1 rank in stride and halo, got {len(ranks_in_stride_and_halo)}"
1577+
if einsum_name not in node._einsum_to_rank_variables:
1578+
raise ValueError(
1579+
f"Loop {node.compact_str()} has multiple rank variables. Make "
1580+
f"sure to populate _einsum_to_rank_variables for this loop."
1581+
)
15521582
new_node = copy.copy(node)
1553-
new_node.rank_variable = ranks_in_stride_and_halo.pop()
1583+
new_node.rank_variable = node._einsum_to_rank_variables[einsum_name]
15541584
new_nodes.append(new_node)
15551585
else:
15561586
new_nodes.append(node)

accelforge/mapper/FFM/_join_pmappings/compatibility.py

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,6 @@ def merge_next(
382382
self,
383383
right: "Compatibility",
384384
live_tensors: set[str],
385-
mixable_ranks: dict[Rank, set[Rank]],
386385
) -> "Compatibility":
387386
self_freed = self.clear_dead_tensors(live_tensors)
388387
right_freed = right.clear_dead_tensors(live_tensors)
@@ -405,9 +404,6 @@ def merge_next(
405404
| right_freed.reservation_indices,
406405
)
407406

408-
if mixable_ranks is not None and not joined._is_valid(mixable_ranks):
409-
raise ValueError(f"Invalid rank mixing.")
410-
411407
return joined
412408

413409
def has_tensor(self, *tensors: TensorReservation) -> bool:
@@ -638,20 +634,6 @@ def add_n_iteration_symbols(self) -> "Compatibility":
638634
tensors=fzs(t.add_n_iteration_symbols() for t in self.tensors)
639635
)
640636

641-
def _is_valid(self, mixable_ranks: dict[Rank, set[Rank]]) -> bool:
642-
# Mixable ranks: Ranks that may be co-iterated by a single loop.
643-
ranks_at_each_loop_index = []
644-
for i in range(self.n_loops):
645-
ranks_at_each_loop_index.append(
646-
set(t.loops[i].rank_name for t in self.tensors if i < len(t.loops))
647-
)
648-
649-
for ranks in ranks_at_each_loop_index:
650-
for r1, r2 in itertools.combinations(ranks, 2):
651-
if r1 not in mixable_ranks[r2]:
652-
return False
653-
return True
654-
655637
def clear_unrelated_columns(self, mappings: pd.DataFrame) -> "Compatibility":
656638
my_symbols = set(self.symbols())
657639
for c in my_symbols:

accelforge/mapper/FFM/_join_pmappings/join_pmappings.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from accelforge.mapper.FFM._join_pmappings.compatibility import Compatibility
12
from collections import defaultdict
23
import itertools
34
import logging
@@ -281,8 +282,6 @@ def join_pmappings(
281282
pmapping_groups, print_progress
282283
)
283284

284-
mixable_ranks = spec.workload._get_ranks_that_share_indexing_rank_variables()
285-
286285
aliased_tensors = spec.workload.get_tensor_copies()
287286

288287
n_mappings = {}
@@ -478,11 +477,7 @@ def grab_einsum_pmappings() -> (
478477
b: PmappingGroup
479478
perm_a: list[int]
480479
perm_b: list[int]
481-
key_check = (
482-
id(a),
483-
id(b),
484-
tuple((pa, pb) for pa, pb in zip(perm_a, perm_b)),
485-
)
480+
key_check = (id(a), id(b))
486481
if key_check in combined_ids:
487482
continue
488483
combined_ids.add(key_check)
@@ -494,7 +489,6 @@ def grab_einsum_pmappings() -> (
494489
compatibility_joined = compatibility_a.merge_next(
495490
compatibility_b,
496491
live_tensors,
497-
mixable_ranks,
498492
)
499493
if DO_PRINT:
500494
print(
@@ -515,6 +509,8 @@ def grab_einsum_pmappings() -> (
515509
aliased_tensors,
516510
compatibility_joined=compatibility_joined,
517511
drop_valid_reservations=drop_valid_reservations,
512+
permuted_compatibility_left=compatibility_a,
513+
permuted_compatibility_right=compatibility_b,
518514
delay=DELAY,
519515
_pmapping_row_filter_function=_pmapping_row_filter_function,
520516
ignored_resources=ignored_resources,
@@ -575,6 +571,7 @@ def no_match_lookahead_error(
575571
# Look ahead to the next Einsum and see if any of our groups will not
576572
# be able to merge with it. If so, we can drop them immediately.
577573
# ======================================================================
574+
lookahead_filter = True
578575
if lookahead_filter:
579576
cur_tensors = left_tensors | right_tensors
580577
for next_pmapping_groups in pmgroups:
@@ -589,11 +586,15 @@ def no_match_lookahead_error(
589586
).clear_tile_patterns_and_reservation_indices()
590587
for c in next_pmapping_groups.pmapping_groups
591588
}
592-
for k in list(combined):
593-
k_cleared = k.clear_dead_tensors(
594-
next_right_tensors
595-
).clear_tile_patterns_and_reservation_indices()
596-
if k_cleared not in next_keys:
589+
for k in list[PmappingGroup](combined):
590+
perms = k.make_equivalent_permutations()
591+
perms = [
592+
p[0]
593+
.clear_dead_tensors(next_right_tensors)
594+
.clear_tile_patterns_and_reservation_indices()
595+
for p in perms
596+
]
597+
if not any(p in next_keys for p in perms):
597598
if DO_PRINT:
598599
for b, _ in combined[k]:
599600
print(

accelforge/mapper/FFM/_join_pmappings/pmapping_group.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ def merge_next(
5858
aliased_tensors: dict[str, set[str]],
5959
compatibility_joined: Compatibility,
6060
ignored_resources: set[str],
61+
permuted_compatibility_left: Compatibility,
62+
permuted_compatibility_right: Compatibility,
6163
drop_valid_reservations: bool = True,
6264
delay: bool = False,
6365
_pmapping_row_filter_function: Callable[[pd.Series], bool] | None = None,
@@ -88,8 +90,8 @@ def merge_next(
8890
live_tensors_with_right,
8991
still_live_reservations,
9092
duplicated_aliased_tensors,
91-
compatibility_left=self.compatibility,
92-
compatibility_right=right.compatibility,
93+
compatibility_left=permuted_compatibility_left,
94+
compatibility_right=permuted_compatibility_right,
9395
compatibility_joined=compatibility_joined,
9496
drop_valid_reservations=drop_valid_reservations,
9597
_pmapping_row_filter_function=_pmapping_row_filter_function,
@@ -218,6 +220,7 @@ def _group(
218220
include_permutations: bool = False,
219221
clear_symbolic_tile_patterns: bool = False,
220222
try_permute_into_equivalent: bool = False,
223+
# mixable_ranks: dict[Rank, set[Rank]] = None,
221224
) -> (
222225
dict[Compatibility, list["PmappingGroup"]]
223226
| dict[Compatibility, list[tuple["PmappingGroup", list[int]]]]
@@ -253,6 +256,13 @@ def clear(c: Compatibility):
253256
len(k.reservation_indices) == 0
254257
), f"Extra reservation indices are not empty: {k.reservation_indices}"
255258

259+
# if mixable_ranks is not None:
260+
# new_grouped = {}
261+
# for c, g in grouped.items():
262+
# for c2 in c.get_equivalent_compatibilities(mixable_ranks):
263+
# new_grouped.setdefault(c2, []).extend(g)
264+
# grouped = new_grouped
265+
256266
if try_permute_into_equivalent:
257267
assert not include_permutations
258268
new_grouped = {}
@@ -339,13 +349,14 @@ def check(tensors_to_check):
339349

340350
@staticmethod
341351
def group(
342-
pmapping_groups: list["PmappingGroup"], live_tensors: set[str]
352+
pmapping_groups: list["PmappingGroup"], live_tensors: set[str]#, mixable_ranks: dict[Rank, set[Rank]] = None
343353
) -> dict[tuple[Compatibility, ...], list[tuple["PmappingGroup", list[int]]]]:
344354
x = PmappingGroup._group(
345355
pmapping_groups,
346356
live_tensors,
347357
clear_tile_patterns_and_reservation_indices=True,
348358
include_permutations=True,
359+
# mixable_ranks=mixable_ranks,
349360
)
350361
return x
351362

accelforge/model/main.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,7 @@ def evaluate_mapping(
113113
)
114114

115115
pmapping.split_reservations()
116-
pmapping.split_loop_with_multiple_rank_variables(
117-
job.stride_and_halo[einsum_name]
118-
)
116+
pmapping.split_loop_with_multiple_rank_variables(job.einsum_name)
119117
pmapping.split_tensor_holders_with_multiple_tensors()
120118
_add_backing_to_tensor_holders(pmapping)
121119

0 commit comments

Comments
 (0)