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,14 @@
1
+ from accelforge.frontend.mapper.metrics import Metrics
2
+ from accelforge.util._basetypes import ParsableModel
3
+
4
+
5
+ class Model(ParsableModel):
6
+ """Configuration for the model."""
7
+
8
+ metrics: Metrics = Metrics.all_metrics()
9
+ """
10
+ Metrics to evaluate.
11
+
12
+ If using spec to call mapper, leave this configuration as is. The mapper
13
+ will make necessary configurations.
14
+ """
@@ -0,0 +1,150 @@
1
+ import copy
2
+ from typing import Annotated, Any, TypeAlias
3
+ from accelforge.util._basetypes import (
4
+ ParsableList,
5
+ ParsableModel,
6
+ ParsesTo,
7
+ TryParseTo,
8
+ _PostCall,
9
+ )
10
+ from accelforge._version import assert_version, __version__
11
+ from accelforge.util._parse_expressions import ParseError
12
+ from accelforge.util._setexpressions import InvertibleSet
13
+
14
+ TensorName: TypeAlias = str
15
+ RankVariable: TypeAlias = str
16
+ Rank: TypeAlias = str
17
+ EinsumName: TypeAlias = str
18
+
19
+
20
+ class Rename(ParsableModel):
21
+ """
22
+ A rename of something into something else.
23
+ """
24
+
25
+ name: str
26
+ """ The name of the thing to be renamed. This is a string representing the new name."""
27
+
28
+ source: TryParseTo[InvertibleSet[TensorName | RankVariable]]
29
+ """ The source of the rename. This is a set expression that can be parsed, yielding
30
+ a set that can be referenced by the new name. """
31
+
32
+ expected_count: ParsesTo[int] | None = None
33
+ """
34
+ The expected count of the source set expression. If this is set, then the source
35
+ expression must resolve to the expected count or an error will be raised. Otherwise,
36
+ any count (including zero for an empty set) is allowed.
37
+ """
38
+
39
+ def _parse_expressions(self, symbol_table: dict[str, Any], *args, **kwargs):
40
+ parsed, symbol_table = super()._parse_expressions(symbol_table, *args, **kwargs)
41
+ expected_count = parsed.expected_count
42
+ if (
43
+ expected_count is not None
44
+ and isinstance(parsed.source, InvertibleSet)
45
+ and len(parsed.source) != expected_count
46
+ ):
47
+ parsed, symbol_table = super()._parse_expressions(
48
+ symbol_table, *args, **kwargs
49
+ )
50
+ raise ParseError(
51
+ f"Expected count is {parsed.expected_count}, but got "
52
+ f"{len(parsed.source)}: {parsed.source}",
53
+ source_field="source",
54
+ )
55
+ return parsed, symbol_table
56
+
57
+
58
+ def rename_list_factory(rename_list: list | dict) -> "RenameList":
59
+ if isinstance(rename_list, list):
60
+ return RenameList(rename_list)
61
+
62
+ if not isinstance(rename_list, dict):
63
+ raise TypeError(
64
+ f"Expected a list or dict, got {type(rename_list)}: {rename_list}"
65
+ )
66
+
67
+ return RenameList(
68
+ Rename(name=k, source=v, expected_count=None) for k, v in rename_list.items()
69
+ )
70
+
71
+
72
+ class RenameList(ParsableList[Rename]):
73
+ """A list of renames."""
74
+
75
+ def __dict__(self) -> dict[str, Any]:
76
+ return {r.name: r.source for r in self}
77
+
78
+ def _parse_expressions(self, symbol_table: dict[str, Any], *args, **kwargs):
79
+
80
+ cur_symbol_table = symbol_table.copy()
81
+
82
+ class PostCallRenameList(_PostCall[Rename]):
83
+ def __call__(self, field, value, parsed, symbol_table):
84
+ symbol_table[parsed.name] = parsed.source
85
+ return parsed
86
+
87
+ new, _ = super()._parse_expressions(
88
+ cur_symbol_table, *args, **kwargs, post_calls=(PostCallRenameList(),)
89
+ )
90
+ return new, symbol_table
91
+
92
+
93
+ class EinsumRename(ParsableModel):
94
+ """
95
+ Renames for a single Einsum.
96
+ """
97
+
98
+ name: EinsumName
99
+ """ The name of the Einsum. Set this to "default" to apply the renames to all
100
+ Einsums, unless overridden. Overriding is specific to a single name, so every rename
101
+ in the default must be overridden independently. """
102
+
103
+ tensor_accesses: ParsableList[Rename] = ParsableList()
104
+ """ Renames for the tensor accesses of this Einsum. This may be given either as a
105
+ dictionary ``{new_name: source_set_expression}`` expressions, or as a list of
106
+ dictionaries, each one having the structure ``{name: new_name, source:
107
+ source_set_expression, expected_count: 1}``, where expected count is optional for
108
+ each and may be set to any integer. """
109
+
110
+ rank_variables: ParsableList[Rename] = ParsableList()
111
+ """ Renames for the rank variables of this Einsum. This may be given either as a
112
+ dictionary ``{new_name: source_set_expression}`` expressions, or as a list of
113
+ dictionaries, each one having the structure ``{name: new_name, source:
114
+ source_set_expression, expected_count: 1}``, where expected count is optional for
115
+ each and may be set to any integer. """
116
+
117
+ def __init__(self, *args, **kwargs) -> None:
118
+ if "tensor_accesses" in kwargs:
119
+ kwargs["tensor_accesses"] = rename_list_factory(kwargs["tensor_accesses"])
120
+ if "rank_variables" in kwargs:
121
+ kwargs["rank_variables"] = rename_list_factory(kwargs["rank_variables"])
122
+ super().__init__(*args, **kwargs)
123
+
124
+
125
+ class Renames(ParsableModel):
126
+ # version: Annotated[str, assert_version] = __version__
127
+ einsums: list[EinsumRename] = list()
128
+ """
129
+ Renames for a workload. The Einsum list is a list of EinsumRename objects, and
130
+ renames will be applied to Einsums whose names match the EinsumRename.name. If an
131
+ EinsumRename is named "default", then its renames are applied to every Einsum unless
132
+ overridden. Overriding is specific to a single name, so every rename in the default
133
+ must be overridden independently.
134
+ """
135
+
136
+ def get_renames_for_einsum(self, einsum_name: EinsumName) -> EinsumRename:
137
+ if einsum_name not in self.einsums:
138
+ rename = EinsumRename(name=einsum_name)
139
+ else:
140
+ rename = copy.deepcopy(self.einsums[einsum_name])
141
+ for einsum in self.einsums:
142
+ if einsum.name != "default":
143
+ continue
144
+ for tensor_rename in einsum.tensor_accesses:
145
+ if tensor_rename.name not in rename.tensor_accesses:
146
+ rename.tensor_accesses.append(tensor_rename)
147
+ for rank_variable_rename in einsum.rank_variables:
148
+ if rank_variable_rename.name not in rename.rank_variables:
149
+ rename.rank_variables.append(rank_variable_rename)
150
+ return rename
@@ -0,0 +1,230 @@
1
+ from accelforge.frontend.mapper.mapper import Mapper
2
+ from accelforge.frontend.renames import EinsumName, Renames
3
+ from accelforge.util._parse_expressions import ParseError, ParseExpressionsContext
4
+ from accelforge.frontend.arch import Compute, Leaf, Component, Arch, Fanout
5
+
6
+ from accelforge.frontend.workload import Workload
7
+ from accelforge.frontend.variables import Variables
8
+ from accelforge.frontend.config import Config, get_config
9
+ from accelforge.frontend.mapping import Mapping
10
+ from accelforge.frontend.model import Model
11
+ import hwcomponents
12
+
13
+ from typing import Any, Dict, Optional, Self
14
+ from accelforge.util._basetypes import ParsableModel
15
+ from pydantic import Field
16
+
17
+
18
+ class Spec(ParsableModel):
19
+ """The top-level spec of all of the inputs to this package."""
20
+
21
+ arch: Arch = Arch()
22
+ """ The hardware architecture being used. """
23
+
24
+ mapping: Mapping = Mapping()
25
+ """ How the workload is programmed onto the architecture. Do not specify this if
26
+ you'd like the mapper to generate a mapping for you. """
27
+
28
+ workload: Workload = Workload()
29
+ """ The program to be run on the arch. """
30
+
31
+ variables: Variables = Variables()
32
+ """ Variables that can be referenced in other places in the spec. """
33
+
34
+ config: Config = Field(default_factory=get_config)
35
+ """ Configuration settings. """
36
+
37
+ renames: Renames = Renames()
38
+ """ Aliases for tensors in the workload so that they can be called
39
+ by canonical names when writing architecture constraints. For example, workload
40
+ tensors may be renamed to "input", "output", and "weight"."""
41
+
42
+ mapper: Mapper = Mapper()
43
+ """ Configures the mapper used to map the workload onto the architecture. """
44
+
45
+ model: Model = Model()
46
+ """Configures the model used to evaluate mappings."""
47
+
48
+ def _parse_expressions(
49
+ self,
50
+ symbol_table: Optional[Dict[str, Any]] = None,
51
+ **kwargs,
52
+ ) -> Self:
53
+ """
54
+ Parse all string expressions in the spec into concrete values.
55
+
56
+ Parameters
57
+ ----------
58
+ symbol_table : dict, optional
59
+ Optional pre-populated symbols to seed parsing; a shallow copy is made and
60
+ augmented with ``spec`` and ``variables``.
61
+ kwargs : dict, optional
62
+ Additional keyword arguments forwarded to the base
63
+ ``ParsableModel._parse_expressions``.
64
+
65
+ Returns
66
+ -------
67
+ Self
68
+ The parsed specification.
69
+
70
+ Raises
71
+ ------ParseError
72
+ If any field fails to parse; the error is annotated with the field path.
73
+ """
74
+ symbol_table = {} if symbol_table is None else symbol_table.copy()
75
+ symbol_table["spec"] = self
76
+ with ParseExpressionsContext(self):
77
+ try:
78
+ parsed_variables, _ = self.variables._parse_expressions(
79
+ symbol_table, **kwargs
80
+ )
81
+ except ParseError as e:
82
+ e.add_field("Spec().variables")
83
+ raise e
84
+ symbol_table.update(parsed_variables)
85
+ symbol_table["variables"] = parsed_variables
86
+ parsed_spec, _ = super()._parse_expressions(symbol_table, **kwargs)
87
+ return parsed_spec, symbol_table
88
+
89
+ def calculate_component_area_energy_latency_leak(
90
+ self,
91
+ area: bool = True,
92
+ energy: bool = True,
93
+ latency: bool = True,
94
+ leak: bool = True,
95
+ einsum_name: EinsumName | None = None,
96
+ ) -> "Spec":
97
+ """
98
+ Populates per-component area, energy, latency, and/or leak power. For each
99
+ component, populates the ``attributes.area``, ``attributes.total_area``,
100
+ ``attributes.leak_power`` and ``attributes.total_leak_power``. Additionally, for
101
+ each action of each component, populates the ``arguments.energy`` and
102
+ ``arguments.latency`` fields. Extends the ``component_modeling_log`` field with
103
+ log messages. Also populates the ``component_model`` attribute for each
104
+ component if not already set.
105
+
106
+ Some architectures may reference variables that depend on the workload. In that
107
+ case, an Einsum name can be provided to populate those symbols with the Einsum's
108
+ symbols from the workload.
109
+
110
+ Parameters
111
+ ----------
112
+ area : bool, optional
113
+ Whether to compute and populate area entries.
114
+ energy : bool, optional
115
+ Whether to compute and populate energy entries.
116
+ latency : bool, optional
117
+ Whether to compute and populate latency entries.
118
+ leak : bool, optional
119
+ Whether to compute and populate leak power entries.
120
+ symbol_table : dict[str, Any], optional
121
+ Symbol table to use for parsing expressions.
122
+ """
123
+ if not area and not energy and not latency and not leak:
124
+ return self
125
+
126
+ models = hwcomponents.get_models(
127
+ self.config.component_models,
128
+ include_installed=self.config.use_installed_component_models,
129
+ )
130
+
131
+ components = set()
132
+ if not getattr(self, "_parsed", False):
133
+ self = self._parse_expressions()
134
+ else:
135
+ self = self.copy()
136
+
137
+ for arch in self._get_flattened_architecture():
138
+ fanout = 1
139
+ for component in arch:
140
+ fanout *= component.get_fanout()
141
+ if component.name in components or isinstance(component, Fanout):
142
+ continue
143
+ assert isinstance(component, Component)
144
+ components.add(component.name)
145
+ orig: Component = self.arch.find(component.name)
146
+ if area:
147
+ c = component.calculate_area(models)
148
+ orig.attributes.area = c.attributes.area
149
+ orig.attributes.total_area = c.attributes.area * fanout
150
+ if energy:
151
+ c = component.calculate_action_energy(models)
152
+ for a in c.actions:
153
+ orig_action = orig.actions[a.name]
154
+ orig_action.arguments.energy = a.arguments.energy
155
+ if latency:
156
+ c = component.calculate_action_latency(models)
157
+ for a in c.actions:
158
+ orig_action = orig.actions[a.name]
159
+ orig_action.arguments.latency = a.arguments.latency
160
+ if leak:
161
+ c = component.calculate_leak_power(models)
162
+ orig.attributes.leak_power = c.attributes.leak_power
163
+ orig.attributes.total_leak_power = c.attributes.leak_power * fanout
164
+ orig.component_modeling_log.extend(c.component_modeling_log)
165
+ orig.component_model = c.component_model
166
+
167
+ return self
168
+
169
+ def _get_flattened_architecture(
170
+ self,
171
+ compute_node: str | Compute | None = None,
172
+ symbol_table: dict[str, Any] | None = None,
173
+ ) -> list[list[Leaf]] | list[Leaf]:
174
+ """
175
+ Return the architecture as paths of ``Leaf`` instances from the highest-level
176
+ node to each ``Compute`` node. Parses arithmetic expressions in the
177
+ architecture for each one. If a symbol table is provided, it will be used to
178
+ parse the expressions.
179
+
180
+ Parameters
181
+ ----------
182
+ compute_node : str or Compute, optional
183
+ Optional compute node (name or ``Compute``) to restrict results to a single
184
+ compute node.
185
+ symbol_table : dict[str, Any], optional
186
+ Symbol table to use for parsing expressions.
187
+
188
+ Returns
189
+ -------
190
+ - If ``compute_node`` is ``None``: list of lists of ``Leaf`` for all compute
191
+ nodes.
192
+ - Otherwise: a single-item list containing the list of ``Leaf`` for the
193
+ requested node.
194
+
195
+ Raises
196
+ ------
197
+ AssertionError
198
+ If the spec has not been parsed.
199
+ ParseError
200
+ If there are duplicate names or the requested compute node cannot be found.
201
+ """
202
+ # Assert that we've been parsed
203
+ assert getattr(
204
+ self, "_parsed", False
205
+ ), "Spec must be parsed before getting flattened architecture"
206
+ all_leaves = self.arch.get_nodes_of_type(Leaf)
207
+ found_names = set()
208
+ for leaf in all_leaves:
209
+ if leaf.name in found_names:
210
+ raise ParseError(f"Duplicate name in architecture: {leaf.name}")
211
+ found_names.add(leaf.name)
212
+
213
+ found = []
214
+ if compute_node is None:
215
+ compute_nodes = [c.name for c in self.arch.get_nodes_of_type(Compute)]
216
+ else:
217
+ compute_nodes = [
218
+ compute_node.name if isinstance(compute_node, Compute) else compute_node
219
+ ]
220
+
221
+ for c in compute_nodes:
222
+ variables = {**self.variables.model_dump(), **(symbol_table or {})}
223
+ found.append(self.arch._flatten(variables, c))
224
+ if found[-1][-1].name != c:
225
+ raise ParseError(f"Compute node {c} not found in architecture")
226
+
227
+ return found if compute_node is None else [found[0]]
228
+
229
+
230
+ Specification = Spec
@@ -0,0 +1,301 @@
1
+ from accelforge.frontend.mapper.mapper import Mapper
2
+ from accelforge.frontend.renames import EinsumName, Renames
3
+ from accelforge.util._parse_expressions import ParseError, ParseExpressionsContext
4
+ from accelforge.frontend.arch import Compute, Leaf, Component, Arch, Fanout
5
+
6
+ from accelforge.frontend.workload import Workload
7
+ from accelforge.frontend.variables import Variables
8
+ from accelforge.frontend.config import Config, get_config
9
+ from accelforge.frontend.mapping import Mapping
10
+ from accelforge.frontend.model import Model
11
+ import hwcomponents
12
+
13
+ from typing import Any, Dict, Optional, Self
14
+ from accelforge.util._basetypes import ParsableModel
15
+ from pydantic import Field
16
+
17
+
18
+ class Spec(ParsableModel):
19
+ """The top-level spec of all of the inputs to this package."""
20
+
21
+ arch: Arch = Arch()
22
+ """ The hardware architecture being used. """
23
+
24
+ mapping: Mapping = Mapping()
25
+ """ How the workload is programmed onto the architecture. Do not specify this if
26
+ you'd like the mapper to generate a mapping for you. """
27
+
28
+ workload: Workload = Workload()
29
+ """ The program to be run on the arch. """
30
+
31
+ variables: Variables = Variables()
32
+ """ Variables that can be referenced in other places in the spec. """
33
+
34
+ config: Config = Field(default_factory=get_config)
35
+ """ Configuration settings. """
36
+
37
+ renames: Renames = Renames()
38
+ """ Aliases for tensors in the workload so that they can be called
39
+ by canonical names when writing architecture constraints. For example, workload
40
+ tensors may be renamed to "input", "output", and "weight"."""
41
+
42
+ mapper: Mapper = Mapper()
43
+ """ Configures the mapper used to map the workload onto the architecture. """
44
+
45
+ model: Model = Model()
46
+ """Configures the model used to evaluate mappings."""
47
+
48
+ def _parse_expressions(
49
+ self,
50
+ einsum_name: EinsumName | None = None,
51
+ **kwargs,
52
+ ) -> tuple[Self, dict[str, Any]]:
53
+ raise NotImplementedError("Call _spec_parse_expressions instead.")
54
+
55
+ def _spec_parse_expressions(
56
+ self,
57
+ einsum_name: EinsumName | None = None,
58
+ _parse_arch: bool = True,
59
+ _parse_non_arch: bool = True,
60
+ ) -> Self:
61
+ """
62
+ Parse all string expressions in the spec into concrete values.
63
+
64
+ Parameters
65
+ ----------
66
+ einsum_name: EinsumName | None = None
67
+ Optional Einsum name to populate symbols with the Einsum's symbols from the
68
+ workload. If None, no symbols are populated from the workload.
69
+
70
+ _parse_arch: bool = True
71
+ Whether to parse the architecture.
72
+
73
+ _parse_non_arch: bool = True
74
+ Whether to parse the non-architecture fields.
75
+
76
+ Returns
77
+ -------
78
+ Self
79
+ The parsed specification.
80
+
81
+ Raises
82
+ ------
83
+ ParseError
84
+ If any field fails to parse; the error is annotated with the field path.
85
+ """
86
+ st = {}
87
+ st["spec"] = self
88
+ with ParseExpressionsContext(self):
89
+ already_parsed = {}
90
+
91
+ parsed_variables = self.variables
92
+ if _parse_non_arch:
93
+ try:
94
+ parsed_variables, st = self.variables._parse_expressions(st)
95
+ except ParseError as e:
96
+ e.add_field("Spec().variables")
97
+ raise e
98
+ already_parsed["variables"] = parsed_variables
99
+ st.update(parsed_variables)
100
+ st["variables"] = parsed_variables
101
+
102
+ parsed_renames = self.renames
103
+ if _parse_non_arch:
104
+ try:
105
+ parsed_renames, st = self.renames._parse_expressions(st)
106
+ except ParseError as e:
107
+ e.add_field("Spec().renames")
108
+ raise e
109
+ already_parsed["renames"] = parsed_renames
110
+ st["renames"] = parsed_renames
111
+
112
+ parsed_workload = self.workload
113
+ if _parse_non_arch:
114
+ try:
115
+ parsed_workload, st = self.workload._parse_expressions(
116
+ st, renames=parsed_renames
117
+ )
118
+ except ParseError as e:
119
+ e.add_field("Spec().workload")
120
+ raise e
121
+ already_parsed["workload"] = parsed_workload
122
+ st["workload"] = parsed_workload
123
+
124
+ if einsum_name is not None:
125
+ renames = parsed_workload.einsums[einsum_name].renames
126
+ st.update(**{k.name: k.source for k in renames})
127
+
128
+ if _parse_arch:
129
+ parsed_arch, st = self.arch._parse_expressions(st)
130
+ else:
131
+ parsed_arch = self.arch
132
+ st["arch"] = parsed_arch
133
+ already_parsed["arch"] = parsed_arch
134
+
135
+ parsed_spec, _ = super()._parse_expressions(
136
+ st,
137
+ already_parsed=already_parsed,
138
+ )
139
+ parsed_spec._parsed = True
140
+ return parsed_spec
141
+
142
+ def calculate_component_area_energy_latency_leak(
143
+ self,
144
+ einsum_name: EinsumName | None = None,
145
+ area: bool = True,
146
+ energy: bool = True,
147
+ latency: bool = True,
148
+ leak: bool = True,
149
+ ) -> "Spec":
150
+ """
151
+ Populates per-component area, energy, latency, and/or leak power. For each
152
+ component, populates the ``area``, ``total_area``, ``leak_power`` and
153
+ ``total_leak_power``. Additionally, for each action of each component, populates
154
+ the ``<action>.energy`` and ``<action>.latency`` fields. Extends the
155
+ ``component_modeling_log`` field with log messages. Also populates the
156
+ ``component_model`` attribute for each component if not already set.
157
+
158
+ Some architectures' attributes may depend on the workload. In that case, an
159
+ Einsum name can be provided to populate those symbols with the Einsum's symbols
160
+ from the workload.
161
+
162
+ Parameters
163
+ ----------
164
+ einsum_name: EinsumName | None = None
165
+ Optional Einsum name to populate symbols with the Einsum's symbols from the
166
+ workload. If None, and there are Einsums in the workload, the first Einsum
167
+ is used. If None and there are no Einsums in the workload, then no symbols
168
+ are populated from the workload.
169
+ area : bool, optional
170
+ Whether to compute and populate area entries.
171
+ energy : bool, optional
172
+ Whether to compute and populate energy entries.
173
+ latency : bool, optional
174
+ Whether to compute and populate latency entries.
175
+ leak : bool, optional
176
+ Whether to compute and populate leak power entries.
177
+ """
178
+ if not area and not energy and not latency and not leak:
179
+ return self
180
+
181
+ models = hwcomponents.get_models(
182
+ self.config.component_models,
183
+ include_installed=self.config.use_installed_component_models,
184
+ )
185
+
186
+ if einsum_name is None and len(self.workload.einsums) > 0:
187
+ einsum_name = self.workload.einsums[0].name
188
+
189
+ components = set()
190
+ try:
191
+ if not getattr(self, "_parsed", False):
192
+ self = self._spec_parse_expressions(einsum_name=einsum_name)
193
+ else:
194
+ self = self.copy()
195
+ except ParseError as e:
196
+ if "arch" in e.message:
197
+ e.add_note(
198
+ "If this error seems to be caused by a missing symbol that depends on \n"
199
+ "the workload, you may need to provide an appropriate einsum_name to \n"
200
+ "calculate_component_area_energy_latency_leak. This may occur if the \n"
201
+ "architecture depends on something in the workload.\n"
202
+ )
203
+ raise
204
+
205
+ for arch in self._get_flattened_architecture():
206
+ fanout = 1
207
+ for component in arch:
208
+ fanout *= component.get_fanout()
209
+ if component.name in components or isinstance(component, Fanout):
210
+ continue
211
+ assert isinstance(component, Component)
212
+ components.add(component.name)
213
+ orig: Component = self.arch.find(component.name)
214
+ c = component
215
+ if area:
216
+ c = c.calculate_area(models)
217
+ orig.area = c.area
218
+ orig.total_area = c.area * fanout
219
+ if energy:
220
+ c = c.calculate_action_energy(models)
221
+ for a in c.actions:
222
+ orig_action = orig.actions[a.name]
223
+ orig_action.energy = a.energy
224
+ if latency:
225
+ c = c.calculate_action_latency(models)
226
+ for a in c.actions:
227
+ orig_action = orig.actions[a.name]
228
+ orig_action.latency = a.latency
229
+ if leak:
230
+ c = c.calculate_leak_power(models)
231
+ orig.leak_power = c.leak_power
232
+ orig.total_leak_power = c.leak_power * fanout
233
+ orig.component_modeling_log.extend(c.component_modeling_log)
234
+ orig.component_model = c.component_model
235
+
236
+ return self
237
+
238
+ def _get_flattened_architecture(
239
+ self,
240
+ compute_node: str | Compute | None = None,
241
+ ) -> list[list[Leaf]] | list[Leaf]:
242
+ """
243
+ Return the architecture as paths of ``Leaf`` instances from the highest-level
244
+ node to each ``Compute`` node. Parses arithmetic expressions in the
245
+ architecture for each one. If a symbol table is provided, it will be used to
246
+ parse the expressions.
247
+
248
+ Parameters
249
+ ----------
250
+ compute_node : str or Compute, optional
251
+ Optional compute node (name or ``Compute``) to restrict results to a single
252
+ compute node.
253
+
254
+ Returns
255
+ -------
256
+ - If ``compute_node`` is ``None``: list of lists of ``Leaf`` for all compute
257
+ nodes.
258
+ - Otherwise: a single-item list containing the list of ``Leaf`` for the
259
+ requested node.
260
+
261
+ Raises
262
+ ------
263
+ AssertionError
264
+ If the spec has not been parsed.
265
+ ParseError
266
+ If there are duplicate names or the requested compute node cannot be found.
267
+ """
268
+ # Assert that we've been parsed
269
+ assert getattr(
270
+ self, "_parsed", False
271
+ ), "Spec must be parsed before getting flattened architecture"
272
+ all_leaves = self.arch.get_nodes_of_type(Leaf)
273
+ found_names = set()
274
+ for leaf in all_leaves:
275
+ if leaf.name in found_names:
276
+ raise ParseError(f"Duplicate name in architecture: {leaf.name}")
277
+ found_names.add(leaf.name)
278
+
279
+ found = []
280
+ if compute_node is None:
281
+ compute_nodes = [c.name for c in self.arch.get_nodes_of_type(Compute)]
282
+ else:
283
+ compute_nodes = [
284
+ compute_node.name if isinstance(compute_node, Compute) else compute_node
285
+ ]
286
+
287
+ for c in compute_nodes:
288
+ found.append(self.arch._flatten(c))
289
+ if found[-1][-1].name != c:
290
+ raise ParseError(f"Compute node {c} not found in architecture")
291
+
292
+ # These can't be pickled if they use dynamically-loaded code
293
+ for f in found:
294
+ for n in f:
295
+ if hasattr(n, "component_model"):
296
+ n.component_model = None
297
+
298
+ return found if compute_node is None else found[0]
299
+
300
+
301
+ Specification = Spec