accelforge 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. accelforge/__init__.py +21 -0
  2. accelforge/_accelerated_imports.py +16 -0
  3. accelforge/_deprecate/_simanneal/evalmapping.py +271 -0
  4. accelforge/_deprecate/_simanneal/mapspaceglobals.py +298 -0
  5. accelforge/_deprecate/_simanneal/simanneal.py +666 -0
  6. accelforge/_deprecate/_simanneal/tracking.py +105 -0
  7. accelforge/_deprecate/_simanneal/wrappers.py +218 -0
  8. accelforge/_deprecate/_simanneal2/__init__.py +7 -0
  9. accelforge/_deprecate/_simanneal2/simanneal.py +493 -0
  10. accelforge/_deprecate/_simanneal2/tracking.py +116 -0
  11. accelforge/_deprecate/compatibility_util.py +181 -0
  12. accelforge/_deprecate/layerdeduplication/__init__.py +2 -0
  13. accelforge/_deprecate/layerdeduplication/group_similar_einsums.py +160 -0
  14. accelforge/_deprecate/layerdeduplication/grouped_einsums.py +84 -0
  15. accelforge/_deprecate/mapping_filter_tags/__init__.py +2 -0
  16. accelforge/_deprecate/mapping_filter_tags/ffmt.py +212 -0
  17. accelforge/_deprecate/mapping_filter_tags/onesplit.py +24 -0
  18. accelforge/_deprecate/mapping_filter_tags/util.py +24 -0
  19. accelforge/_deprecate/tags.py +69 -0
  20. accelforge/_deprecate/viz/__init__.py +0 -0
  21. accelforge/_deprecate/viz/interactive.py +159 -0
  22. accelforge/_deprecate/viz/reservationtree.py +307 -0
  23. accelforge/_deprecate/viz/ski_slope.py +88 -0
  24. accelforge/_version.py +15 -0
  25. accelforge/examples.py +39 -0
  26. accelforge/frontend/__init__.py +10 -0
  27. accelforge/frontend/_binding.py +129 -0
  28. accelforge/frontend/_workload_isl/__init__.py +2 -0
  29. accelforge/frontend/_workload_isl/_isl.py +149 -0
  30. accelforge/frontend/_workload_isl/_symbolic.py +141 -0
  31. accelforge/frontend/arch copy.py +1544 -0
  32. accelforge/frontend/arch.py +1642 -0
  33. accelforge/frontend/config.py +63 -0
  34. accelforge/frontend/mapper/__init__.py +5 -0
  35. accelforge/frontend/mapper/ffm.py +126 -0
  36. accelforge/frontend/mapper/mapper.py +7 -0
  37. accelforge/frontend/mapper/metrics.py +30 -0
  38. accelforge/frontend/mapping/__init__.py +1 -0
  39. accelforge/frontend/mapping/mapping.py +1736 -0
  40. accelforge/frontend/model.py +14 -0
  41. accelforge/frontend/renames.py +150 -0
  42. accelforge/frontend/spec copy.py +230 -0
  43. accelforge/frontend/spec.py +301 -0
  44. accelforge/frontend/variables.py +12 -0
  45. accelforge/frontend/workload.py +952 -0
  46. accelforge/mapper/FFM/__init__.py +9 -0
  47. accelforge/mapper/FFM/_join_pmappings/__init__.py +0 -0
  48. accelforge/mapper/FFM/_join_pmappings/compatibility.py +653 -0
  49. accelforge/mapper/FFM/_join_pmappings/compress_pmappings.py +140 -0
  50. accelforge/mapper/FFM/_join_pmappings/join_pmappings.py +703 -0
  51. accelforge/mapper/FFM/_join_pmappings/pmapping_dataframe.py +901 -0
  52. accelforge/mapper/FFM/_join_pmappings/pmapping_group.py +337 -0
  53. accelforge/mapper/FFM/_make_pmappings/contraints/__init__.py +0 -0
  54. accelforge/mapper/FFM/_make_pmappings/contraints/constraints.py +360 -0
  55. accelforge/mapper/FFM/_make_pmappings/make_pmapping_templates/__init__.py +1 -0
  56. accelforge/mapper/FFM/_make_pmappings/make_pmapping_templates/make_loops.py +373 -0
  57. accelforge/mapper/FFM/_make_pmappings/make_pmapping_templates/make_pmapping_templates.py +463 -0
  58. accelforge/mapper/FFM/_make_pmappings/make_pmapping_templates/make_reservations.py +95 -0
  59. accelforge/mapper/FFM/_make_pmappings/make_pmapping_templates/make_storage_order.py +382 -0
  60. accelforge/mapper/FFM/_make_pmappings/make_pmapping_templates/make_storages.py +155 -0
  61. accelforge/mapper/FFM/_make_pmappings/make_pmappings.py +411 -0
  62. accelforge/mapper/FFM/_make_pmappings/make_pmappings_from_templates/__init__.py +1 -0
  63. accelforge/mapper/FFM/_make_pmappings/make_pmappings_from_templates/make_pmappings_from_templates.py +407 -0
  64. accelforge/mapper/FFM/_make_pmappings/make_pmappings_from_templates/make_tile_shapes.py +1681 -0
  65. accelforge/mapper/FFM/_make_pmappings/make_pmappings_from_templates/run_model.py +170 -0
  66. accelforge/mapper/FFM/_make_pmappings/make_pmappings_from_templates/symbol_relations.py +174 -0
  67. accelforge/mapper/FFM/_make_pmappings/pmapper_job.py +282 -0
  68. accelforge/mapper/FFM/_pareto_df/df_convention.py +273 -0
  69. accelforge/mapper/FFM/_pareto_df/pareto copy.py +836 -0
  70. accelforge/mapper/FFM/_pareto_df/pareto.py +508 -0
  71. accelforge/mapper/FFM/data.py +61 -0
  72. accelforge/mapper/FFM/main copy.py +236 -0
  73. accelforge/mapper/FFM/main.py +208 -0
  74. accelforge/mapper/FFM/mappings.py +510 -0
  75. accelforge/mapper/FFM/pmappings.py +310 -0
  76. accelforge/mapper/__init__.py +4 -0
  77. accelforge/mapper.py +0 -0
  78. accelforge/model/__init__.py +1 -0
  79. accelforge/model/_looptree/__init__.py +0 -0
  80. accelforge/model/_looptree/accesses.py +335 -0
  81. accelforge/model/_looptree/capacity/__init__.py +1 -0
  82. accelforge/model/_looptree/capacity/aggregators.py +36 -0
  83. accelforge/model/_looptree/capacity/capacity.py +47 -0
  84. accelforge/model/_looptree/energy.py +150 -0
  85. accelforge/model/_looptree/equivalent_ranks.py +29 -0
  86. accelforge/model/_looptree/latency/__init__.py +1 -0
  87. accelforge/model/_looptree/latency/latency.py +98 -0
  88. accelforge/model/_looptree/latency/memory.py +120 -0
  89. accelforge/model/_looptree/latency/processors.py +92 -0
  90. accelforge/model/_looptree/mapping_utilities.py +71 -0
  91. accelforge/model/_looptree/reuse/__init__.py +4 -0
  92. accelforge/model/_looptree/reuse/isl/__init__.py +1 -0
  93. accelforge/model/_looptree/reuse/isl/des.py +59 -0
  94. accelforge/model/_looptree/reuse/isl/isl_functions.py +374 -0
  95. accelforge/model/_looptree/reuse/isl/mapping_to_isl/__init__.py +4 -0
  96. accelforge/model/_looptree/reuse/isl/mapping_to_isl/analyze_mapping.py +297 -0
  97. accelforge/model/_looptree/reuse/isl/mapping_to_isl/skews_from_mapping.py +236 -0
  98. accelforge/model/_looptree/reuse/isl/mapping_to_isl/tiling.py +685 -0
  99. accelforge/model/_looptree/reuse/isl/mapping_to_isl/types.py +188 -0
  100. accelforge/model/_looptree/reuse/isl/spatial.py +260 -0
  101. accelforge/model/_looptree/reuse/isl/temporal.py +182 -0
  102. accelforge/model/_looptree/reuse/symbolic/__init__.py +1 -0
  103. accelforge/model/_looptree/reuse/symbolic/symbolic copy 2.py +1346 -0
  104. accelforge/model/_looptree/reuse/symbolic/symbolic copy.py +1408 -0
  105. accelforge/model/_looptree/reuse/symbolic/symbolic.py +1396 -0
  106. accelforge/model/_looptree/run.py +122 -0
  107. accelforge/model/_looptree/types.py +26 -0
  108. accelforge/model/_looptree/visualization/__init__.py +0 -0
  109. accelforge/model/_looptree/visualization/occupancy.py +11 -0
  110. accelforge/model/main.py +222 -0
  111. accelforge/plotting/__init__.py +2 -0
  112. accelforge/plotting/mappings.py +219 -0
  113. accelforge/plotting/specs.py +57 -0
  114. accelforge/util/__init__.py +4 -0
  115. accelforge/util/_base_analysis_types.py +24 -0
  116. accelforge/util/_basetypes.py +1089 -0
  117. accelforge/util/_frozenset.py +36 -0
  118. accelforge/util/_isl.py +29 -0
  119. accelforge/util/_itertools.py +14 -0
  120. accelforge/util/_mathfuncs.py +57 -0
  121. accelforge/util/_parse_expressions.py +339 -0
  122. accelforge/util/_picklecache.py +32 -0
  123. accelforge/util/_setexpressions.py +268 -0
  124. accelforge/util/_sympy/__init__.py +0 -0
  125. accelforge/util/_sympy/broadcast_max.py +18 -0
  126. accelforge/util/_visualization.py +112 -0
  127. accelforge/util/_yaml.py +579 -0
  128. accelforge/util/parallel.py +193 -0
  129. accelforge-0.0.1.dist-info/METADATA +64 -0
  130. accelforge-0.0.1.dist-info/RECORD +258 -0
  131. accelforge-0.0.1.dist-info/WHEEL +5 -0
  132. accelforge-0.0.1.dist-info/licenses/LICENSE +19 -0
  133. accelforge-0.0.1.dist-info/top_level.txt +5 -0
  134. docs/_build/html/_sources/fastfusion.frontend.mapper.rst.txt +37 -0
  135. docs/_build/html/_sources/fastfusion.frontend.rst.txt +70 -0
  136. docs/_build/html/_sources/fastfusion.frontend.workload.rst.txt +21 -0
  137. docs/_build/html/_sources/fastfusion.mapper.FFM.rst.txt +37 -0
  138. docs/_build/html/_sources/fastfusion.mapper.rst.txt +18 -0
  139. docs/_build/html/_sources/fastfusion.rst.txt +20 -0
  140. docs/_build/html/_sources/fastfusion.util.rst.txt +21 -0
  141. docs/_build/html/_sources/index.rst.txt +87 -0
  142. docs/_build/html/_sources/modules.rst.txt +7 -0
  143. docs/_build/html/_sources/notes/citation.rst.txt +45 -0
  144. docs/_build/html/_sources/notes/definitions.rst.txt +43 -0
  145. docs/_build/html/_sources/notes/faqs.rst.txt +39 -0
  146. docs/_build/html/_sources/notes/modeling/accelerator_energy_latency.rst.txt +72 -0
  147. docs/_build/html/_sources/notes/modeling/component_energy_area.rst.txt +96 -0
  148. docs/_build/html/_sources/notes/modeling/mapping.rst.txt +100 -0
  149. docs/_build/html/_sources/notes/modeling.rst.txt +33 -0
  150. docs/_build/html/_sources/notes/parsing/arithmetic_parsing.rst.txt +136 -0
  151. docs/_build/html/_sources/notes/parsing/setexpressions.rst.txt +63 -0
  152. docs/_build/html/_sources/notes/parsing/yaml_parsing.rst.txt +176 -0
  153. docs/_build/html/_sources/notes/quickstart_and_installation.rst.txt +9 -0
  154. docs/_build/html/_sources/notes/spec/architecture.rst.txt +133 -0
  155. docs/_build/html/_sources/notes/spec/mapping.rst.txt +12 -0
  156. docs/_build/html/_sources/notes/spec/workload.rst.txt +83 -0
  157. docs/_build/html/_sources/notes/spec.rst.txt +36 -0
  158. docs/source/_ext/include_attrs.py +213 -0
  159. docs/source/_ext/include_docstring.py +364 -0
  160. docs/source/_ext/include_functions.py +154 -0
  161. docs/source/_ext/include_notebook.py +131 -0
  162. docs/source/_ext/include_yaml.py +119 -0
  163. docs/source/_ext/inherited_attributes.py +222 -0
  164. docs/source/_ext/paths.py +4 -0
  165. docs/source/conf.py +79 -0
  166. examples/arches/compute_in_memory/_include.yaml +74 -0
  167. examples/arches/compute_in_memory/_include_functions.py +229 -0
  168. examples/arches/compute_in_memory/_load_spec.py +57 -0
  169. examples/arches/compute_in_memory/components/c2c_multiplier.py +181 -0
  170. examples/arches/compute_in_memory/components/dac_c2c_r2r.py +605 -0
  171. examples/arches/compute_in_memory/components/misc.py +195 -0
  172. examples/arches/compute_in_memory/components/util/bit_functions.py +51 -0
  173. examples/arches/compute_in_memory/components/zero_comparator.py +92 -0
  174. examples/arches/compute_in_memory/isaac.yaml +233 -0
  175. examples/arches/compute_in_memory/memory_cells/ecram_demo.yaml +63 -0
  176. examples/arches/compute_in_memory/memory_cells/rram_example.yaml +63 -0
  177. examples/arches/compute_in_memory/memory_cells/rram_isaac_isca_2016.yaml +64 -0
  178. examples/arches/compute_in_memory/memory_cells/rram_neurosim_default.yaml +63 -0
  179. examples/arches/compute_in_memory/memory_cells/rram_raella_isca_2023.yaml +70 -0
  180. examples/arches/compute_in_memory/memory_cells/rram_wan_nature_2022.yaml +63 -0
  181. examples/arches/compute_in_memory/memory_cells/sram_colonnade_jssc_2021.yaml +63 -0
  182. examples/arches/compute_in_memory/memory_cells/sram_example.yaml +63 -0
  183. examples/arches/compute_in_memory/memory_cells/sram_jia_jssc_2020.yaml +63 -0
  184. examples/arches/compute_in_memory/memory_cells/sram_sinangil_jssc_2021.yaml +63 -0
  185. examples/arches/compute_in_memory/memory_cells/sram_wang_vlsi_2022.yaml +63 -0
  186. examples/arches/compute_in_memory/wang_vlsi_2022.yaml +289 -0
  187. examples/arches/eyeriss.yaml +68 -0
  188. examples/arches/fanout_variations/at_glb.yaml +31 -0
  189. examples/arches/fanout_variations/at_glb_with_fanout_node.yaml +34 -0
  190. examples/arches/fanout_variations/at_mac.yaml +31 -0
  191. examples/arches/fanout_variations/at_mac_with_constraints.yaml +38 -0
  192. examples/arches/fanout_variations/at_mac_with_fanout_node.yaml +34 -0
  193. examples/arches/nvdla.yaml +47 -0
  194. examples/arches/simple.yaml +28 -0
  195. examples/arches/tpu_v4i.yaml +67 -0
  196. examples/mappings/unfused_matmuls_to_simple.yaml +33 -0
  197. examples/misc/component_annotated.yaml +33 -0
  198. examples/workloads/gpt3_6.7B.yaml +124 -0
  199. examples/workloads/matmuls.yaml +20 -0
  200. examples/workloads/mobilenet_28.yaml +81 -0
  201. examples/workloads/mobilenet_various_separate.yaml +106 -0
  202. examples/workloads/three_matmuls_annotated.yaml +59 -0
  203. notebooks/.ipynb_checkpoints/fastfusion_arch_study_michael-checkpoint.ipynb +359 -0
  204. notebooks/compute_in_memory/_scripts.py +339 -0
  205. notebooks/compute_in_memory/isaac.guide.ipynb +270 -0
  206. notebooks/compute_in_memory/wang_vlsi_2022.ipynb +602 -0
  207. notebooks/paths.py +4 -0
  208. notebooks/tutorials/.ipynb_checkpoints/1_FFM-checkpoint.ipynb +3110 -0
  209. notebooks/tutorials/FFM.ipynb +3498 -0
  210. notebooks/tutorials/_include.py +48 -0
  211. notebooks/tutorials/component_energy_area.ipynb +363 -0
  212. tests/Q_mapping.yaml +38 -0
  213. tests/__init__.py +0 -0
  214. tests/conv.mapping.yaml +27 -0
  215. tests/conv.workload.yaml +13 -0
  216. tests/conv_sym.mapping.yaml +43 -0
  217. tests/copy.mapping.yaml +35 -0
  218. tests/copy.workload.yaml +15 -0
  219. tests/distribuffers/__init__.py +0 -0
  220. tests/distribuffers/multicast/test_cases.yaml +482 -0
  221. tests/distribuffers/spec/binding/valid_bindings.yaml +97 -0
  222. tests/distribuffers/spec/distributed.yaml +100 -0
  223. tests/distribuffers/spec/logical_arch.yaml +32 -0
  224. tests/distribuffers/spec/physical_arch.yaml +69 -0
  225. tests/distribuffers/test_binding.py +48 -0
  226. tests/frontend/__init__.py +0 -0
  227. tests/frontend/test_mapping_viz.py +52 -0
  228. tests/mapper/__init__.py +0 -0
  229. tests/mapper/configs/conv1d/conv1d.mapping.yaml +31 -0
  230. tests/mapper/configs/conv1d/conv1d.workload.yaml +11 -0
  231. tests/mapper/configs/two_conv1d/two_conv1d.expected.yaml +38 -0
  232. tests/mapper/configs/two_conv1d/two_conv1d.mapping.yaml +54 -0
  233. tests/mapper/configs/two_conv1d/two_conv1d.workload.yaml +19 -0
  234. tests/mapper/test_mapping_to_isl.py +90 -0
  235. tests/mapper/test_spatial_reuse_analysis.py +67 -0
  236. tests/mapper/test_temporal_reuse_analysis.py +56 -0
  237. tests/mapper/util.py +58 -0
  238. tests/matmul.mapping.yaml +29 -0
  239. tests/matmul.workload.yaml +12 -0
  240. tests/matmul_spatial.mapping.yaml +44 -0
  241. tests/mha.renames.yaml +65 -0
  242. tests/mha.workload.yaml +67 -0
  243. tests/mha.yaml +59 -0
  244. tests/mha_full.workload.yaml +67 -0
  245. tests/mobilenet.workload.yaml +35 -0
  246. tests/mobilenet_long.workload.yaml +64 -0
  247. tests/pmappingcache.py +24 -0
  248. tests/processing_stage.arch.yaml +40 -0
  249. tests/snowcat.arch.yaml +36 -0
  250. tests/test_ffm_join_pmappings.py +106 -0
  251. tests/test_ffm_make_pmappings.py +82 -0
  252. tests/test_ffm_make_tile_shapes.py +49 -0
  253. tests/test_mapper.py +100 -0
  254. tests/test_model.py +37 -0
  255. tests/test_plotting.py +72 -0
  256. tests/test_processing_stage.py +46 -0
  257. tests/test_symbolic_model.py +248 -0
  258. tests/test_workload.py +141 -0
@@ -0,0 +1,69 @@
1
+ """
2
+ These classes may be useful when constraining joining pmappings. For example,
3
+ if pmapping A is compatible with any other with the same dataflow (say,
4
+ dataflow DA), and pmapping B is conly compatible with pmappings with dataflow
5
+ DA and untiled weights in the global buffer, we can tag pmapping A with {DA}
6
+ and pmapping B with {DA, untiled_weights}.
7
+
8
+ It is often useful to compare tags that are "compatible" and tags that "match".
9
+ Moreover, we may want to use either "compatibility" or "matching" as comparison
10
+ for creating a dictionary. For convenience, one can use TagMatch and
11
+ TagCompatibility as classes for the keys.
12
+ """
13
+
14
+ from fastfusion.util._frozenset import fzs
15
+
16
+
17
+ class TagClass(fzs):
18
+ pass
19
+
20
+
21
+ class Tags(fzs):
22
+ def __repr__(self):
23
+ return f"Tags(({super().__repr__()}))"
24
+
25
+ def __str__(self):
26
+ return f"Tags({super().__repr__()})"
27
+
28
+ def is_member_of(self, tag_class: TagClass):
29
+ return all(class_string in self for class_string in tag_class)
30
+
31
+ def are_compatible_with(self, tag2):
32
+ return all(tag2_string in self for tag2_string in tag2) or all(
33
+ tag1_string in tag2 for tag1_string in self
34
+ )
35
+
36
+ # def filter_membership(tags: set["Tag"], tag_class: TagClass) -> set["Tag"]:
37
+ # return {tag for tag in tags if are_compatible_with(tag, tag_class)}
38
+
39
+ def matches(self, tag2):
40
+ return self == tag2
41
+
42
+ @staticmethod
43
+ def from_tuple(t: tuple):
44
+ return Tags(t)
45
+
46
+
47
+ class TagMatch:
48
+ def __init__(self, tags: Tags):
49
+ self.tags = tags
50
+
51
+ def __str__(self):
52
+ return f"TagMatch({repr(self.tags)})"
53
+
54
+ def __hash__(self):
55
+ return hash(self.tags)
56
+
57
+ def __eq__(self, other: "TagMatch"):
58
+ return self.tags.matches(other.tags)
59
+
60
+
61
+ class TagCompatibility:
62
+ def __init__(self, tags: Tags):
63
+ self.tags = tags
64
+
65
+ def __hash__(self):
66
+ return 0 # See note below
67
+
68
+ def __eq__(self, other: "TagCompatibility"):
69
+ return self.tags.are_compatible_with(other.tag)
File without changes
@@ -0,0 +1,159 @@
1
+ import os
2
+ from typing import Optional
3
+ import plotly
4
+ from IPython.display import SVG, display
5
+ import plotly.graph_objs as go
6
+ from ipywidgets import Output, VBox, HBox
7
+ from fastfusion._accelerated_imports import pd
8
+ from fastfusion.mapper.FFM._join_pmappings.pmapping_dataframe import row2pmappings
9
+ from fastfusion.frontend.mapping import Mapping
10
+
11
+
12
+ def make_mapping(row, einsum_names, rank_variable_bounds):
13
+ pmappings = row2pmappings(row, einsum_names, rank_variable_bounds)
14
+ newmapping = Mapping._from_pmappings(
15
+ pmappings, rank_variable_bounds=rank_variable_bounds
16
+ )
17
+ return newmapping
18
+
19
+
20
+ def mapping2svg(
21
+ mapping: pd.Series,
22
+ einsum_names: list[str],
23
+ rank_variable_bounds: Optional[dict[str, dict[str, int]]] = None,
24
+ ):
25
+ mapping: Mapping = make_mapping(mapping, einsum_names, rank_variable_bounds)
26
+ render = mapping.render()
27
+ return SVG(render)
28
+
29
+
30
+ def diplay_mappings_on_fig(
31
+ fig: plotly.graph_objs.FigureWidget,
32
+ data: dict[str, pd.DataFrame],
33
+ mapping_svg: bool,
34
+ einsum_names: list[str],
35
+ rank_variable_bounds: Optional[dict[str, dict[str, int]]] = None,
36
+ ):
37
+ # fig = go.FigureWidget(fig)
38
+ out = Output()
39
+ DUAL_OUT = True
40
+ if mapping_svg:
41
+ assert DUAL_OUT
42
+ DUAL_OUT = False
43
+
44
+ counter = 0
45
+
46
+ @out.capture()
47
+ def display_mapping(trace, points, selector):
48
+ if not points.point_inds:
49
+ return
50
+ out.clear_output()
51
+ d = data[trace.name]
52
+ index = points.point_inds[0]
53
+ display(mapping2svg(d.iloc[index], einsum_names, rank_variable_bounds))
54
+ # backing_tensors = set(
55
+ # t for tn in d.iloc[index][MAPPING_COLUMN].values() for t in tn.tensors
56
+ # )
57
+ # backing_tensors = TensorReservation.get_backing_tensors(backing_tensors)
58
+ # for t in sorted(backing_tensors):
59
+ # print(f"{t.__repr__()},")
60
+ # for v in d.iloc[index][MAPPING_COLUMN].values():
61
+ # print(v)
62
+
63
+ out2 = Output()
64
+
65
+ @out2.capture()
66
+ def display_mapping_2(trace, points, selector):
67
+ if not points.point_inds:
68
+ return
69
+ out2.clear_output()
70
+ d = data[trace.name]
71
+ index = points.point_inds[0]
72
+ display(mapping2svg(d.iloc[index]))
73
+ if mapping_svg:
74
+ os.makedirs("plots", exist_ok=True)
75
+ svg = mapping2svg(d.iloc[index])
76
+ with open(f"plots/{trace.name}.svg", "w") as f:
77
+ f.write(svg.data)
78
+ # backing_tensors = set(
79
+ # t for tn in d.iloc[index][MAPPING_COLUMN].values() for t in tn.tensors
80
+ # )
81
+ # backing_tensors = TensorReservation.get_backing_tensors(backing_tensors)
82
+ # for t in sorted(backing_tensors):
83
+ # print(f"{t.__repr__()},")
84
+ # for v in d.iloc[index][MAPPING_COLUMN].values():
85
+ # print(v)
86
+
87
+ for i in fig.data:
88
+ i.on_hover(display_mapping)
89
+ i.on_click(display_mapping_2)
90
+ if not DUAL_OUT:
91
+ return VBox([fig, out])
92
+ out.layout.width = "50%"
93
+ out2.layout.width = "50%"
94
+ return VBox([fig, HBox([out, out2])])
95
+
96
+
97
+ def plotly_show(
98
+ data: pd.DataFrame | dict[str, pd.DataFrame],
99
+ x: str,
100
+ y: str,
101
+ category: Optional[str] = None,
102
+ title: Optional[str] = None,
103
+ show_mapping: Optional[bool] = True,
104
+ logscales: bool = False,
105
+ mapping_svg: bool = False,
106
+ einsum_names: Optional[list[str]] = None,
107
+ rank_variable_bounds: Optional[dict[str, dict[str, int]]] = None,
108
+ ):
109
+ fig = go.FigureWidget()
110
+ markers = [
111
+ "circle",
112
+ "square",
113
+ "diamond",
114
+ "cross",
115
+ "x",
116
+ "triangle-up",
117
+ "triangle-down",
118
+ "triangle-left",
119
+ "triangle-right",
120
+ ]
121
+ if isinstance(data, dict):
122
+ for i, (k, v) in enumerate(data.items()):
123
+ v.sort_values(by=[x, y], inplace=True)
124
+ fig.add_scatter(
125
+ x=v[x],
126
+ y=v[y],
127
+ name=k,
128
+ line={"shape": "hv"},
129
+ mode="markers+lines",
130
+ marker={"symbol": markers[i % len(markers)]},
131
+ )
132
+ else:
133
+ data.sort_values(by=[x, y], inplace=True)
134
+ fig.add_scatter(
135
+ x=data[x],
136
+ y=data[y],
137
+ name="",
138
+ line={"shape": "hv"},
139
+ mode="markers+lines",
140
+ marker={"symbol": markers[0]},
141
+ )
142
+ data = {"": data}
143
+ if title is not None:
144
+ fig.update_layout(title=title)
145
+ if logscales:
146
+ fig.update_xaxes(type="log")
147
+ fig.update_yaxes(type="log")
148
+ fig.update_xaxes(title_text=x)
149
+ fig.update_yaxes(title_text=y)
150
+ fig.update_layout(showlegend=True)
151
+ # fig = px.scatter(data, x=x, y=y, color=category, title=title, log_x=logscales, log_y=logscales)
152
+ if show_mapping:
153
+ assert (
154
+ einsum_names is not None
155
+ ), f"einsum_names must be provided if show_mapping is True"
156
+ return diplay_mappings_on_fig(
157
+ fig, data, mapping_svg, einsum_names, rank_variable_bounds
158
+ )
159
+ return fig
@@ -0,0 +1,307 @@
1
+ from collections import defaultdict
2
+ import pydot
3
+ from typing import Any, Iterable
4
+ from fastfusion.mapper.FFM._join_pmappings.pmapping_group import (
5
+ Compatibility,
6
+ TensorReservation,
7
+ Loop,
8
+ )
9
+ from fastfusion.util import _expfmt
10
+ from fastfusion.mapper.FFM._join_pmappings.pmapping_dataframe import col2nameloop
11
+
12
+ PYDOT_NODE_DEFAULTS = {
13
+ "shape": "box",
14
+ "fontsize": "10",
15
+ # "margin": "0.0",
16
+ "margin": "0.1,0.0",
17
+ }
18
+
19
+
20
+ def get_einsum_key(name):
21
+ return f"Einsum {name}"
22
+
23
+
24
+ class Node:
25
+ def __init__(self, this_level: Iterable[Any] = (), children: Iterable["Node"] = ()):
26
+ self.this_level = list(this_level)
27
+ self.children = list(children)
28
+
29
+ def access_level(self, index: int) -> "Node":
30
+ if index == -1:
31
+ return self
32
+ if index == None:
33
+ return self.children[-1].access_level(None) if self.children else self
34
+ return self.children[-1].access_level(index - 1)
35
+
36
+ def _to_yaml(self):
37
+ this_level = [t.to_yaml() for t in self.this_level]
38
+ if self.children:
39
+ this_level.append(
40
+ {
41
+ "type": "sequential",
42
+ "children": [c._to_yaml() for c in self.children],
43
+ }
44
+ )
45
+ return this_level
46
+
47
+ def to_yaml(self):
48
+ return {"mapping": "fused", "nodes": self._to_yaml()}
49
+
50
+ def to_pydot(self, graph, parent=None, invisible_root: bool = False):
51
+ label_lines = []
52
+ for t in self.this_level:
53
+ label_lines.append(t.pydot_str() if hasattr(t, "pydot_str") else str(t))
54
+ node_label = "\n".join(sorted(label_lines))
55
+ if invisible_root:
56
+ node = None
57
+ else:
58
+ node = pydot.Node(id(self), label=node_label, **PYDOT_NODE_DEFAULTS)
59
+ graph.add_node(node)
60
+ if parent:
61
+ reservations = "\n".join(
62
+ sorted(
63
+ f"[{k}] {_expfmt(v)}" for k, v in self.get_reservations().items()
64
+ )
65
+ )
66
+ graph.add_edge(pydot.Edge(parent, node, label=reservations))
67
+ for child in self.children:
68
+ child.to_pydot(graph, node, invisible_root=False)
69
+
70
+ def add_stats(self, stats: dict[str, Any]):
71
+ if self.children:
72
+ self.children[-1].add_stats(stats)
73
+ else:
74
+ for k, v in stats.items():
75
+ self.this_level.append(f"{k}: {_expfmt(v)}")
76
+
77
+ def get_reservations(self) -> dict[str, int]:
78
+ reservations = defaultdict(lambda: 0)
79
+ for c in self.children:
80
+ for k, v in c.get_reservations().items():
81
+ reservations[k] = max(reservations[k], v)
82
+ for t in self.this_level:
83
+ if isinstance(t, TensorReservation):
84
+ reservations[str(t.resource_name)] += t.size
85
+ return dict(reservations)
86
+
87
+ def get_all_tensors(
88
+ self, _entry: bool = True, start_at: int = 0
89
+ ) -> list[TensorReservation]:
90
+ if start_at <= 0:
91
+ tensors = set(
92
+ t for t in self.this_level if isinstance(t, TensorReservation)
93
+ )
94
+ else:
95
+ tensors = set()
96
+ for c in self.children:
97
+ tensors.update(c.get_all_tensors(_entry=False, start_at=start_at - 1))
98
+ return sorted(tensors) if _entry else tensors
99
+
100
+ def get_backing_tensors(self):
101
+ return TensorReservation.get_backing_tensors(self.get_all_tensors())
102
+
103
+ def merge(self, other: "Node") -> "Node":
104
+ new_this_level = self.this_level + other.this_level
105
+ loops = [l for l in new_this_level if isinstance(l, Loop)]
106
+ new_this_level = [t for t in new_this_level if not isinstance(t, Loop)]
107
+ assert (
108
+ len(loops) <= 2
109
+ ), "Can only merge two nodes with two loops. Loops: " + str(loops)
110
+ assert len(loops) > 0, "Can only merge two nodes with loops. Loops: " + str(
111
+ loops
112
+ )
113
+ while len(loops) > 1:
114
+ loops[0] = loops[0].merge_next(loops.pop(1))
115
+ new_this_level.append(loops[0])
116
+ return Node(new_this_level, self.children + other.children)
117
+
118
+ def get_shared_tensors(
119
+ self, other: "Node", start_at: int = 0
120
+ ) -> set[TensorReservation]:
121
+ return set(self.get_all_tensors(start_at=start_at)) & set(
122
+ other.get_all_tensors(start_at=start_at)
123
+ )
124
+
125
+ def find_node_with(self, key: Any) -> "Node":
126
+ if key in self.this_level:
127
+ return self
128
+ for c in self.children:
129
+ found = c.find_node_with(key)
130
+ if found:
131
+ return found
132
+ return None
133
+
134
+ def validate_loops(self, einsum2ranks):
135
+ """
136
+ Validate the loops in the tree. This is a placeholder function and should be
137
+ implemented with actual validation logic.
138
+ """
139
+ for c in self.children:
140
+ c.validate_loops(einsum2ranks)
141
+ for l in self.this_level:
142
+ if not isinstance(l, Loop):
143
+ continue
144
+ for einsum, ranks in einsum2ranks.items():
145
+ n_ranks_in_einsum = sum(1 for r in l.rank_variable_names if r in ranks)
146
+ if n_ranks_in_einsum > 1:
147
+ return False
148
+ return True
149
+
150
+ def deduplicate_reservations(self, to_deduplicate):
151
+ for c in self.children:
152
+ c.deduplicate_reservations(to_deduplicate)
153
+ i = 0
154
+ while i < len(self.this_level):
155
+ if (
156
+ self.this_level[i] in self.this_level[:i]
157
+ and self.this_level[i] in to_deduplicate
158
+ ):
159
+ self.this_level.pop(i)
160
+ else:
161
+ i += 1
162
+
163
+
164
+ def mappings2reservationtree(
165
+ mappings: dict[str, Compatibility],
166
+ stats: dict[str, Any] = None,
167
+ skip_backing_tensors_in_right_branch: Iterable[str] = (),
168
+ still_live_tensors: set[str] = (),
169
+ skip_merge: bool = False,
170
+ per_component_energy: dict[dict[str, float]] = None,
171
+ add_reservations: dict[str, Any] = None,
172
+ ) -> Node:
173
+ prev_mappings = []
174
+ root = Node()
175
+ einsum_ids = list(mappings.keys())
176
+ if stats is not None:
177
+ assert set(einsum_ids) == set(stats.keys())
178
+
179
+ # If a tensor appears in non-back-to-back Einsums, then we need to store it for
180
+ # all Einsums in between
181
+ tensors_lifetimes = {e: [] for e in einsum_ids}
182
+ all_tensors = set().union(*[set(t.tensors) for t in mappings.values()])
183
+ backers = TensorReservation.get_backing_tensors(all_tensors)
184
+ for t in backers:
185
+ first_appearance = min(
186
+ i for i, ts in enumerate(mappings.values()) if t in ts.tensors
187
+ )
188
+ last_appearance = max(
189
+ i for i, ts in enumerate(mappings.values()) if t in ts.tensors
190
+ )
191
+ if t.name in still_live_tensors or still_live_tensors == "all":
192
+ last_appearance = len(einsum_ids) - 1
193
+ for i, l in enumerate(tensors_lifetimes.values()):
194
+ if first_appearance <= i <= last_appearance and t not in l:
195
+ l.append(t)
196
+
197
+ # Add each Einsum to the tree
198
+ for i, einsum_id in enumerate(einsum_ids):
199
+ mapping = mappings[einsum_id]
200
+ index = -1
201
+ n = root.access_level(index)
202
+ loops = mapping.loops[index:] if index != -1 else mapping.loops
203
+ for l in loops:
204
+ n.children.append(Node())
205
+ n = n.children[-1]
206
+ n.this_level.append(l)
207
+
208
+ # Add the tensors
209
+ n.children.append(Node()) # Leaf node
210
+ n.children[-1].this_level.append(get_einsum_key(einsum_id))
211
+ if per_component_energy is not None:
212
+ for k, v in per_component_energy[einsum_id].items():
213
+ n.children[-1].this_level.append(f"{k} energy: {_expfmt(v)}")
214
+ id2tensor = defaultdict(set)
215
+ for t in sorted(mapping.tensors) + tensors_lifetimes[einsum_id]:
216
+ id2tensor[t.name].add(t)
217
+ id2tensor = {k: sorted(v) for k, v in id2tensor.items()}
218
+ for tensor_name, tensors in id2tensor.items():
219
+ for tensor in tensors:
220
+ n = root.access_level(tensor.above_loop_index)
221
+ # TODO if tensor not in n.this_level or tensor not in backers:
222
+ if tensor not in n.this_level or tensor not in backers:
223
+ n.this_level.append(tensor)
224
+ if stats is not None:
225
+ root.add_stats(stats[einsum_id])
226
+ prev_mappings.append(mapping)
227
+
228
+ # Start at the root. Iterate through each leaf. Recursively:
229
+ # - If two leaves have the same tensor and this tensor is a backing tensor,
230
+ # merge the two leaves. Remove the duplicate tensor from the right leaf.
231
+ def merge_nodes(n: Node, level: int = 0):
232
+ i = 0
233
+ children = n.children
234
+ while i < len(children) - 1:
235
+ for j in range(len(children) - 1, i, -1):
236
+ shared_tensors = children[i].get_shared_tensors(children[j], start_at=1)
237
+ shared_tensors |= set(
238
+ c
239
+ for c in children[i].get_all_tensors(start_at=1)
240
+ if c.name in still_live_tensors or still_live_tensors == "all"
241
+ )
242
+ if shared_tensors & set(backers):
243
+ while j != i:
244
+ # print(f'Level {level} merging {shared_tensors} between {i} and {j}')
245
+ children[i] = children[i].merge(children.pop(i + 1))
246
+ j -= 1
247
+ break
248
+ else:
249
+ i += 1
250
+
251
+ this_level = children[i].this_level
252
+ for t0 in range(len(this_level)):
253
+ for t1 in range(len(this_level) - 1, t0, -1):
254
+ if this_level[t0] == this_level[t1] and this_level[t0] in backers:
255
+ this_level.pop(t1)
256
+
257
+ for c in children:
258
+ merge_nodes(c, level + 1)
259
+
260
+ n.children = children
261
+
262
+ if not skip_merge:
263
+ merge_nodes(root)
264
+
265
+ n = root
266
+ skip_backing_tensors_in_right_branch = set(skip_backing_tensors_in_right_branch)
267
+ while n is not None:
268
+ i = 0
269
+ while i < len(n.this_level):
270
+ t = n.this_level[i]
271
+ if t in backers and t.name in skip_backing_tensors_in_right_branch:
272
+ n.this_level.pop(i)
273
+ else:
274
+ i += 1
275
+ n = n.children[-1] if n.children else None
276
+
277
+ if add_reservations is not None:
278
+ for einsum, reservations in add_reservations.items():
279
+ for resource, size in dict(reservations).items():
280
+ nameloop = col2nameloop(resource)
281
+ if nameloop is None:
282
+ continue
283
+ name, _ = nameloop
284
+ node = root.find_node_with(get_einsum_key(einsum))
285
+ node.this_level.append(TensorReservation("Reservation", -1, name, size))
286
+
287
+ root.deduplicate_reservations(backers)
288
+
289
+ return root
290
+
291
+
292
+ def mappings2svg(
293
+ mappings: dict[str, Compatibility],
294
+ stats: dict[str, Any],
295
+ per_component_energy: dict[dict[str, float]] = None,
296
+ ):
297
+ root = mappings2reservationtree(
298
+ mappings, stats, per_component_energy=per_component_energy
299
+ )
300
+ graph = pydot.Dot(graph_type="digraph", ranksep="0.2", nodesep="0.2")
301
+ root.to_pydot(graph)
302
+ return graph.create_svg().decode("utf-8")
303
+
304
+
305
+ def mappings2yaml(mappings: dict[str, Compatibility], stats: dict[str, Any]):
306
+ root = mappings2reservationtree(mappings, stats)
307
+ return root.to_yaml()
@@ -0,0 +1,88 @@
1
+ import matplotlib.axes as mpax
2
+ import matplotlib.pyplot as plt
3
+ from fastfusion._accelerated_imports import np
4
+ from fastfusion._accelerated_imports import pd
5
+
6
+ from fastfusion.mapper.FFM._join_pmappings.pmapping_dataframe import PmappingDataframe
7
+
8
+
9
+ DATAFLOW_COLUMN = "dataflow"
10
+
11
+
12
+ def plot_ski_slope(
13
+ data: pd.DataFrame,
14
+ categorize_by_dataflow: bool = False,
15
+ split_by_dataflow: bool = False,
16
+ ax: mpax.Axes = None,
17
+ **kwargs
18
+ ):
19
+ if ax is None:
20
+ fig, ax = plt.subplots()
21
+ else:
22
+ fig = ax.get_figure()
23
+
24
+ if categorize_by_dataflow or split_by_dataflow:
25
+ _add_dataflow_to_data(data)
26
+
27
+ if not split_by_dataflow:
28
+ data = PmappingDataframe(data).data
29
+
30
+ separated_datas = []
31
+ labels = []
32
+ if categorize_by_dataflow or split_by_dataflow:
33
+ for dataflow, sub_df in data.groupby(by=DATAFLOW_COLUMN):
34
+ separated_datas.append(PmappingDataframe(sub_df).data)
35
+ labels.append(dataflow)
36
+ else:
37
+ separated_datas.append(data)
38
+ labels.append(None)
39
+
40
+ for label, sub_df in zip(labels, separated_datas):
41
+ ax.plot(
42
+ *make_staircase(
43
+ sub_df["Occupancy"].to_numpy(), sub_df["Offchip Accesses"].to_numpy()
44
+ ),
45
+ label=label,
46
+ **kwargs
47
+ )
48
+
49
+ ax.set_xlabel("Capacity")
50
+ ax.set_ylabel("Off-chip Accesses")
51
+ ax.set_xscale("log")
52
+ ax.set_yscale("log")
53
+
54
+ return fig, ax
55
+
56
+
57
+ def make_staircase(x: np.array, y: np.array):
58
+ """Make staircase from x, y coordinates."""
59
+ sort_idx = np.argsort(x)
60
+ x, y = x[sort_idx], y[sort_idx]
61
+
62
+ shifted_x = x[1:]
63
+ shifted_y = y[:-1]
64
+ x = np.concat([x, shifted_x])
65
+ y = np.concat([y, shifted_y])
66
+
67
+ sort_idx = np.lexsort([x, -y])
68
+ x, y = x[sort_idx], y[sort_idx]
69
+
70
+ return x, y
71
+
72
+
73
+ def _add_dataflow_to_data(data: pd.DataFrame):
74
+ data[DATAFLOW_COLUMN] = data["__Mappings"].apply(_dataflow_from_fullmapping)
75
+
76
+
77
+ def _dataflow_from_fullmapping(fullmapping: str):
78
+ fullmapping = fullmapping.strip("[")
79
+ fullmapping = fullmapping.strip("]")
80
+ dataflow = []
81
+ for term in fullmapping.split(","):
82
+ if term[0] == "T":
83
+ dataflow.append(int(term[1:].split(" size ")))
84
+ return tuple(dataflow)
85
+
86
+
87
+ if __name__ == "__main__":
88
+ print(make_staircase(np.array([1, 2, 3, 4]), np.array([4, 3, 2, 1])))
accelforge/_version.py ADDED
@@ -0,0 +1,15 @@
1
+ from typing import Annotated
2
+
3
+ __version__ = "0.5"
4
+
5
+
6
+ def assert_version(version: str):
7
+ if str(version) != str(__version__):
8
+ raise ValueError(
9
+ f"Version {str(version)} is not supported. "
10
+ f"Only version {__version__} is supported."
11
+ )
12
+ return str(version)
13
+
14
+
15
+ version: Annotated[str, assert_version] = __version__
accelforge/examples.py ADDED
@@ -0,0 +1,39 @@
1
+ from pathlib import Path
2
+
3
+ EXAMPLES_DIR = Path(__file__).parent.parent / "examples"
4
+
5
+
6
+ class Directory:
7
+ def __init__(self, path):
8
+ self.path: Path = path
9
+
10
+ def __repr__(self):
11
+ return f"Directory({self.path})"
12
+
13
+ def __getattr__(self, name: Path):
14
+ target_stem: Path = self.path / name
15
+ if target_stem.is_dir():
16
+ return Directory(self.path / name)
17
+
18
+ target_yaml = target_stem.with_suffix(".yaml")
19
+ if target_yaml.is_file():
20
+ return target_yaml
21
+
22
+ raise ValueError("Not found: {target_stem} or {target_yaml}")
23
+
24
+ def iter(self):
25
+ if not self.path.is_dir():
26
+ return
27
+ for path in self.path.iterdir():
28
+ yield path.stem
29
+
30
+
31
+ examples = Directory(EXAMPLES_DIR)
32
+ """
33
+ Convenient variable for getting path to examples directory.
34
+
35
+ For example:
36
+ ```
37
+ path_to_gh100_yaml: pathlib.Path = examples.arches.gh100
38
+ ```
39
+ """
@@ -0,0 +1,10 @@
1
+ """Timeloop Spec. Each piece below (minus processors) corresponds to a top key in the Timeloop spec."""
2
+
3
+ from .spec import *
4
+ import accelforge.frontend.arch as arch
5
+ import accelforge.frontend.config as config
6
+ import accelforge.frontend.mapping as mapping
7
+ import accelforge.frontend.renames as renames
8
+ import accelforge.frontend.spec as spec
9
+ import accelforge.frontend.variables as variables
10
+ import accelforge.frontend.workload as workload