Skip to content

Commit 46086b9

Browse files
committed
Merge branch 'main' into hops
2 parents 7a906bb + 7b017a1 commit 46086b9

17 files changed

Lines changed: 301 additions & 101 deletions

File tree

accelforge/_deprecate/_simanneal/wrappers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def join_pmappings(
201201
)
202202
for _ in range(n_threads)
203203
],
204-
n_jobs=get_n_parallel_jobs()
204+
n_jobs=get_n_parallel_jobs(),
205205
)
206206
results = pd.concat([r[0] for r in results_and_trackers])
207207
break

accelforge/examples.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import os
12
from pathlib import Path
23

34
EXAMPLES_DIR = Path(__file__).parent.parent / "examples"
@@ -19,6 +20,14 @@ def __getattr__(self, name: Path):
1920
if target_yaml.is_file():
2021
return target_yaml
2122

23+
name_replaced = name.replace(".", "_")
24+
name_yaml_replaced = target_yaml.stem.replace(".", "_")
25+
for item in os.listdir(self.path):
26+
stem = Path(item).stem
27+
if stem.replace(".", "_") in [name_replaced, name_yaml_replaced]:
28+
path = self.path / item
29+
return Directory(path) if path.is_dir() else path
30+
2231
raise ValueError(f"Not found: {target_stem} or {target_yaml}")
2332

2433
def iter(self):

accelforge/frontend/mapping/mapping.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -593,10 +593,14 @@ def _merge(self, other: "TensorHolder") -> "TensorHolder":
593593
f"Components do not match: {self.component} != {other.component}"
594594
)
595595

596+
if self._lower != other._lower:
597+
raise ValueError(f"Lower flags do not match: {self._lower} != {other._lower}")
598+
596599
new = type(self)(
597600
tensors=self.tensors + other.tensors,
598601
component=self.component,
599602
component_object=self.component_object,
603+
_lower=self._lower,
600604
)
601605
return new
602606

@@ -1185,7 +1189,7 @@ def compact_str(self) -> str:
11851189
if prev is not None:
11861190
result.append(prev)
11871191

1188-
return " ".join(node.compact_str() for node in result)
1192+
return " ".join(f"{node.compact_str()}" for node in result)
11891193

11901194
def _get_single_tensor_mapping(
11911195
self,
@@ -1519,18 +1523,33 @@ class Mapping(Nested):
15191523
def remove_reservations(self):
15201524
self.nodes = [n for n in self.nodes if not isinstance(n, Reservation)]
15211525

1522-
def split_loop_with_multiple_rank_variables(self):
1526+
def split_reservations(self):
15231527
new_nodes = []
15241528
for node in self.nodes:
1525-
if isinstance(node, Loop) and isinstance(node.rank_variable, set):
1526-
for rank_variable in node.rank_variable:
1529+
if isinstance(node, Reservation):
1530+
for purpose in node.purposes:
15271531
new_node = copy.copy(node)
1528-
new_node.rank_variable = rank_variable
1532+
new_node.purposes = [purpose]
15291533
new_nodes.append(new_node)
15301534
else:
15311535
new_nodes.append(node)
15321536
self.nodes = new_nodes
15331537

1538+
def split_loop_with_multiple_rank_variables(self, stride_and_halo: dict[str, tuple[int, int]]):
1539+
new_nodes = []
1540+
for node in self.nodes:
1541+
if isinstance(node, Loop) and isinstance(node.rank_variable, set):
1542+
ranks_in_stride_and_halo = {
1543+
r for r in node.rank_variable if r in stride_and_halo
1544+
}
1545+
assert len(ranks_in_stride_and_halo) == 1, f"Expected 1 rank in stride and halo, got {len(ranks_in_stride_and_halo)}"
1546+
new_node = copy.copy(node)
1547+
new_node.rank_variable = ranks_in_stride_and_halo.pop()
1548+
new_nodes.append(new_node)
1549+
else:
1550+
new_nodes.append(node)
1551+
self.nodes = new_nodes
1552+
15341553
def split_tensor_holders_with_multiple_tensors(self):
15351554
new_nodes = []
15361555
for node in self.nodes:

accelforge/frontend/workload.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,14 @@ def _to_formatted_string(self) -> str:
135135
if isinstance(self.projection, ImpliedProjection):
136136
return f"{self.name}<sub>{subscript}</sub>"
137137

138-
string = [self.name]
138+
string = []
139139
for k, v in self.projection.items():
140-
if len(string) < len(self.projection):
141-
string.append(f"<sup>{k},</sup><sub>{v},</sub>")
140+
if v == k.lower():
141+
string.append(v)
142142
else:
143-
string.append(f"<sup>{k}</sup><sub>{v}</sub>")
143+
string.append(f"{k}:{v}")
144+
return f"{self.name}<sub>{','.join(string)}</sub>"
145+
144146
return "".join(string)
145147

146148
@property
@@ -533,7 +535,7 @@ def _to_formatted_string(self, compress: bool = False) -> str:
533535
A string representation of this Einsum for use in a Pydot graph.
534536
"""
535537
lhs_join = ",\n" if compress else " , "
536-
rhs_join = " \n " if compress else " "
538+
rhs_join = " \n " if compress else " × "
537539
lhs = lhs_join.join(
538540
[t._to_formatted_string() for t in self.tensor_accesses if t.output]
539541
)
@@ -974,14 +976,19 @@ def render(self) -> str:
974976
"""Renders the workload as a Pydot graph. Returns an SVG string."""
975977
graph = _pydot_graph()
976978

979+
# Set ranksep to 0.3
980+
graph.set_ranksep(0.2)
981+
977982
# Add all tensors as nodes (circles)
978983
tensors = []
979984
seen_tensor_names = set()
980985
for einsum in self.einsums:
981986
node = pydot.Node(
982987
f"Einsum_{einsum.name}",
983988
shape="box",
984-
label=f"<{einsum._to_formatted_string(compress=True)}>",
989+
label=f"<{einsum._to_formatted_string(compress=False)}>",
990+
style="filled",
991+
fillcolor="#E0EEFF", # Same color as Compute nodes
985992
)
986993
graph.add_node(node)
987994
for tensor_access in einsum.tensor_accesses:
@@ -992,6 +999,8 @@ def render(self) -> str:
992999
f"Tensor_{tensor_access.name}",
9931000
shape="oval",
9941001
label=f"<{tensor_access._to_formatted_string()}>",
1002+
style="filled",
1003+
fillcolor="#D7FCD7", # Same color as Storage nodes
9951004
)
9961005
graph.add_node(node)
9971006

@@ -1002,13 +1011,17 @@ def render(self) -> str:
10021011
if tensor_access.output:
10031012
# Output tensor: einsum -> tensor
10041013
edge = pydot.Edge(
1005-
f"Einsum_{einsum.name}", f"Tensor_{tensor_access.name}"
1014+
f"Einsum_{einsum.name}",
1015+
f"Tensor_{tensor_access.name}",
1016+
dir="forward",
10061017
)
10071018
graph.add_edge(edge)
10081019
else:
10091020
# Input tensor: tensor -> einsum
10101021
edge = pydot.Edge(
1011-
f"Tensor_{tensor_access.name}", f"Einsum_{einsum.name}"
1022+
f"Tensor_{tensor_access.name}",
1023+
f"Einsum_{einsum.name}",
1024+
dir="forward",
10121025
)
10131026
graph.add_edge(edge)
10141027
return _SVGJupyterRender(graph.create_svg(prog="dot").decode("utf-8"))

accelforge/mapper/FFM/_join_pmappings/compatibility.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55
from numbers import Number
66
from typing import Literal, TypeVar
77

8-
from accelforge.frontend.workload import TensorAccess, Workload
8+
import pandas as pd
99
from accelforge.frontend.mapping import (
10-
Compute,
1110
Loop,
1211
Mapping,
1312
Spatial,
@@ -19,6 +18,7 @@
1918
)
2019
from accelforge.frontend.renames import Rank, RankVariable, TensorName
2120
from accelforge.mapper.FFM._pareto_df.df_convention import (
21+
is_fused_loop_col,
2222
make_fused_loop_col,
2323
stride2col,
2424
initial2col,
@@ -651,3 +651,11 @@ def _is_valid(self, mixable_ranks: dict[Rank, set[Rank]]) -> bool:
651651
if r1 not in mixable_ranks[r2]:
652652
return False
653653
return True
654+
655+
def clear_unrelated_columns(self, mappings: pd.DataFrame) -> "Compatibility":
656+
my_symbols = set(self.symbols())
657+
for c in my_symbols:
658+
assert c in mappings.columns, f"Column {c} not found in mappings"
659+
should_drop = lambda x: is_fused_loop_col(x) and x not in my_symbols
660+
drop = [c for c in mappings.columns if should_drop(c)]
661+
return mappings.drop(columns=drop)

accelforge/mapper/FFM/_join_pmappings/pmapping_dataframe.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,11 @@ def __len__(self) -> int:
884884
# with open(to_file, "wb") as f:
885885
# f.write(graph.create_png())
886886

887+
def clear_irrelevant_columns(self, compatibility: Compatibility) -> "PmappingDataframe":
888+
return self.update(
889+
data=compatibility.clear_unrelated_columns(self._data),
890+
skip_pareto=True,
891+
)
887892

888893
def row2pmappings(
889894
row: pd.Series,

accelforge/mapper/FFM/_join_pmappings/pmapping_group.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
from accelforge.mapper.FFM._join_pmappings.pmapping_dataframe import PmappingDataframe
88

99
from accelforge.mapper.FFM._join_pmappings.compatibility import *
10+
from accelforge.mapper.FFM._pareto_df.df_convention import (
11+
is_fused_loop_col,
12+
make_fused_loop_col,
13+
)
1014
from accelforge.util import parallel
1115

1216

@@ -19,6 +23,18 @@ def __init__(self, compatibility: Compatibility, mappings: PmappingDataframe):
1923
}
2024
self.n_pre_prune_mappings = 0
2125

26+
if isinstance(self.mappings, PmappingDataframe):
27+
checked = set()
28+
for s in self.compatibility.symbols():
29+
checked.add(s)
30+
assert (
31+
s in self.mappings.data.columns
32+
), f"Column {s} not found in mappings"
33+
34+
for col_name in self.mappings.data.columns:
35+
if col_name not in checked and is_fused_loop_col(col_name):
36+
raise ValueError(f"Column {col_name} not found in compatibility")
37+
2238
def compatibility_str(self):
2339
compatibility = ",".join(str(l) for l in self.compatibility.tensors)
2440
compatibility += " || " + ", ".join(str(t) for t in self.tensors.values())
@@ -252,7 +268,10 @@ def clear(c: Compatibility):
252268
if g:
253269
pmgroups_remaining -= {id(s) for s, _ in g}
254270
permuted = [
255-
PmappingGroup(s.compatibility.permute(lc), s.mappings)
271+
PmappingGroup(
272+
s.compatibility.permute(lc),
273+
s.mappings.clear_irrelevant_columns(s.compatibility),
274+
)
256275
for s, lc in g
257276
]
258277
new_grouped[c] = permuted

accelforge/mapper/FFM/_make_pmappings/make_pmapping_templates/make_loops.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ def insert_temporal_loops(
8181
tensor2partially_relevant_rank_vars = (
8282
einsum.tensor2expression_indexing_rank_variables
8383
)
84+
for k, v in tensor2partially_relevant_rank_vars.items():
85+
tensor2partially_relevant_rank_vars[k] = v - tensor2fully_relevant_rank_vars[k]
8486
tensor2irrelevant_rank_vars = einsum.tensor2irrelevant_rank_variables
8587
tensor2rank_vars = einsum.tensor2rank_variables
8688
tensors = einsum.tensor_names
@@ -255,10 +257,12 @@ def _get_next_storages(i: int, toll_allowed: bool = False) -> list[TensorHolder]
255257
# want to lower to reduce memory footprint, or raise to reduce number of
256258
# fused loops.
257259
elif s._backing and lowerable_backing and partially_relevant_to_previous:
260+
assert False
258261
lowering_choices.append((False, True))
259262
permutable_partially_relevant |= partially_relevant_to_previous
260263
# No backing in previous. No cost to lowering. Lower all
261264
elif not s._backing:
265+
# print(f'Can lower {s.tensors} in {s.component}')
262266
lowering_choices.append((True,))
263267
permutable_partially_relevant |= partially_relevant_to_previous
264268
# Previous TensorHolder is backing but not lowerable or there are no

accelforge/mapper/FFM/_make_pmappings/make_pmappings_from_templates/make_pmappings_from_templates.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -365,12 +365,6 @@ def make_pmappings_from_templates(
365365
mappings[v] = mappings[f"{einsum_name}<SEP>{k}"]
366366
shift_reservations_by_null_loop_indices(mappings, null_loop_indices)
367367

368-
symbols = compatibility.symbols()
369-
dropcols = [
370-
c for c in mappings.columns if is_fused_loop_col(c) and c not in symbols
371-
]
372-
mappings = mappings.drop(columns=dropcols)
373-
374368
energy_cols = [c for c in mappings.columns if "Total<SEP>energy" in c]
375369
if (mappings[energy_cols] < 0).any(axis=None):
376370
mapping_with_negative_energy = mappings[
@@ -385,6 +379,7 @@ def make_pmappings_from_templates(
385379

386380
# Skip pareto because we already did it above
387381
next_shared_loop_index_this_group = compatibility.n_loops - 1
382+
mappings = compatibility.clear_unrelated_columns(mappings)
388383
partial_mappings = PmappingDataframe(
389384
mappings,
390385
next_shared_loop_index=next_shared_loop_index_this_group,

accelforge/mapper/FFM/_make_pmappings/make_pmappings_from_templates/make_tile_shapes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ def append_vector(matrix: np.ndarray, vector: np.ndarray):
547547
max_val = max(np.max(a), np.max(b))
548548
min_val = min(np.min(a), np.min(b))
549549
assert min_val >= 0, f"min_val is {min_val}"
550-
550+
assert max_val <= 2**64, f"max_val is {max_val}"
551551
if max_val >= 2**32:
552552
dtype = np.uint64
553553
elif max_val >= 2**16:

0 commit comments

Comments
 (0)