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.
- accelforge/__init__.py +21 -0
- accelforge/_accelerated_imports.py +16 -0
- accelforge/_deprecate/_simanneal/evalmapping.py +271 -0
- accelforge/_deprecate/_simanneal/mapspaceglobals.py +298 -0
- accelforge/_deprecate/_simanneal/simanneal.py +666 -0
- accelforge/_deprecate/_simanneal/tracking.py +105 -0
- accelforge/_deprecate/_simanneal/wrappers.py +218 -0
- accelforge/_deprecate/_simanneal2/__init__.py +7 -0
- accelforge/_deprecate/_simanneal2/simanneal.py +493 -0
- accelforge/_deprecate/_simanneal2/tracking.py +116 -0
- accelforge/_deprecate/compatibility_util.py +181 -0
- accelforge/_deprecate/layerdeduplication/__init__.py +2 -0
- accelforge/_deprecate/layerdeduplication/group_similar_einsums.py +160 -0
- accelforge/_deprecate/layerdeduplication/grouped_einsums.py +84 -0
- accelforge/_deprecate/mapping_filter_tags/__init__.py +2 -0
- accelforge/_deprecate/mapping_filter_tags/ffmt.py +212 -0
- accelforge/_deprecate/mapping_filter_tags/onesplit.py +24 -0
- accelforge/_deprecate/mapping_filter_tags/util.py +24 -0
- accelforge/_deprecate/tags.py +69 -0
- accelforge/_deprecate/viz/__init__.py +0 -0
- accelforge/_deprecate/viz/interactive.py +159 -0
- accelforge/_deprecate/viz/reservationtree.py +307 -0
- accelforge/_deprecate/viz/ski_slope.py +88 -0
- accelforge/_version.py +15 -0
- accelforge/examples.py +39 -0
- accelforge/frontend/__init__.py +10 -0
- accelforge/frontend/_binding.py +129 -0
- accelforge/frontend/_workload_isl/__init__.py +2 -0
- accelforge/frontend/_workload_isl/_isl.py +149 -0
- accelforge/frontend/_workload_isl/_symbolic.py +141 -0
- accelforge/frontend/arch copy.py +1544 -0
- accelforge/frontend/arch.py +1642 -0
- accelforge/frontend/config.py +63 -0
- accelforge/frontend/mapper/__init__.py +5 -0
- accelforge/frontend/mapper/ffm.py +126 -0
- accelforge/frontend/mapper/mapper.py +7 -0
- accelforge/frontend/mapper/metrics.py +30 -0
- accelforge/frontend/mapping/__init__.py +1 -0
- accelforge/frontend/mapping/mapping.py +1736 -0
- accelforge/frontend/model.py +14 -0
- accelforge/frontend/renames.py +150 -0
- accelforge/frontend/spec copy.py +230 -0
- accelforge/frontend/spec.py +301 -0
- accelforge/frontend/variables.py +12 -0
- accelforge/frontend/workload.py +952 -0
- accelforge/mapper/FFM/__init__.py +9 -0
- accelforge/mapper/FFM/_join_pmappings/__init__.py +0 -0
- accelforge/mapper/FFM/_join_pmappings/compatibility.py +653 -0
- accelforge/mapper/FFM/_join_pmappings/compress_pmappings.py +140 -0
- accelforge/mapper/FFM/_join_pmappings/join_pmappings.py +703 -0
- accelforge/mapper/FFM/_join_pmappings/pmapping_dataframe.py +901 -0
- accelforge/mapper/FFM/_join_pmappings/pmapping_group.py +337 -0
- accelforge/mapper/FFM/_make_pmappings/contraints/__init__.py +0 -0
- accelforge/mapper/FFM/_make_pmappings/contraints/constraints.py +360 -0
- accelforge/mapper/FFM/_make_pmappings/make_pmapping_templates/__init__.py +1 -0
- accelforge/mapper/FFM/_make_pmappings/make_pmapping_templates/make_loops.py +373 -0
- accelforge/mapper/FFM/_make_pmappings/make_pmapping_templates/make_pmapping_templates.py +463 -0
- accelforge/mapper/FFM/_make_pmappings/make_pmapping_templates/make_reservations.py +95 -0
- accelforge/mapper/FFM/_make_pmappings/make_pmapping_templates/make_storage_order.py +382 -0
- accelforge/mapper/FFM/_make_pmappings/make_pmapping_templates/make_storages.py +155 -0
- accelforge/mapper/FFM/_make_pmappings/make_pmappings.py +411 -0
- accelforge/mapper/FFM/_make_pmappings/make_pmappings_from_templates/__init__.py +1 -0
- accelforge/mapper/FFM/_make_pmappings/make_pmappings_from_templates/make_pmappings_from_templates.py +407 -0
- accelforge/mapper/FFM/_make_pmappings/make_pmappings_from_templates/make_tile_shapes.py +1681 -0
- accelforge/mapper/FFM/_make_pmappings/make_pmappings_from_templates/run_model.py +170 -0
- accelforge/mapper/FFM/_make_pmappings/make_pmappings_from_templates/symbol_relations.py +174 -0
- accelforge/mapper/FFM/_make_pmappings/pmapper_job.py +282 -0
- accelforge/mapper/FFM/_pareto_df/df_convention.py +273 -0
- accelforge/mapper/FFM/_pareto_df/pareto copy.py +836 -0
- accelforge/mapper/FFM/_pareto_df/pareto.py +508 -0
- accelforge/mapper/FFM/data.py +61 -0
- accelforge/mapper/FFM/main copy.py +236 -0
- accelforge/mapper/FFM/main.py +208 -0
- accelforge/mapper/FFM/mappings.py +510 -0
- accelforge/mapper/FFM/pmappings.py +310 -0
- accelforge/mapper/__init__.py +4 -0
- accelforge/mapper.py +0 -0
- accelforge/model/__init__.py +1 -0
- accelforge/model/_looptree/__init__.py +0 -0
- accelforge/model/_looptree/accesses.py +335 -0
- accelforge/model/_looptree/capacity/__init__.py +1 -0
- accelforge/model/_looptree/capacity/aggregators.py +36 -0
- accelforge/model/_looptree/capacity/capacity.py +47 -0
- accelforge/model/_looptree/energy.py +150 -0
- accelforge/model/_looptree/equivalent_ranks.py +29 -0
- accelforge/model/_looptree/latency/__init__.py +1 -0
- accelforge/model/_looptree/latency/latency.py +98 -0
- accelforge/model/_looptree/latency/memory.py +120 -0
- accelforge/model/_looptree/latency/processors.py +92 -0
- accelforge/model/_looptree/mapping_utilities.py +71 -0
- accelforge/model/_looptree/reuse/__init__.py +4 -0
- accelforge/model/_looptree/reuse/isl/__init__.py +1 -0
- accelforge/model/_looptree/reuse/isl/des.py +59 -0
- accelforge/model/_looptree/reuse/isl/isl_functions.py +374 -0
- accelforge/model/_looptree/reuse/isl/mapping_to_isl/__init__.py +4 -0
- accelforge/model/_looptree/reuse/isl/mapping_to_isl/analyze_mapping.py +297 -0
- accelforge/model/_looptree/reuse/isl/mapping_to_isl/skews_from_mapping.py +236 -0
- accelforge/model/_looptree/reuse/isl/mapping_to_isl/tiling.py +685 -0
- accelforge/model/_looptree/reuse/isl/mapping_to_isl/types.py +188 -0
- accelforge/model/_looptree/reuse/isl/spatial.py +260 -0
- accelforge/model/_looptree/reuse/isl/temporal.py +182 -0
- accelforge/model/_looptree/reuse/symbolic/__init__.py +1 -0
- accelforge/model/_looptree/reuse/symbolic/symbolic copy 2.py +1346 -0
- accelforge/model/_looptree/reuse/symbolic/symbolic copy.py +1408 -0
- accelforge/model/_looptree/reuse/symbolic/symbolic.py +1396 -0
- accelforge/model/_looptree/run.py +122 -0
- accelforge/model/_looptree/types.py +26 -0
- accelforge/model/_looptree/visualization/__init__.py +0 -0
- accelforge/model/_looptree/visualization/occupancy.py +11 -0
- accelforge/model/main.py +222 -0
- accelforge/plotting/__init__.py +2 -0
- accelforge/plotting/mappings.py +219 -0
- accelforge/plotting/specs.py +57 -0
- accelforge/util/__init__.py +4 -0
- accelforge/util/_base_analysis_types.py +24 -0
- accelforge/util/_basetypes.py +1089 -0
- accelforge/util/_frozenset.py +36 -0
- accelforge/util/_isl.py +29 -0
- accelforge/util/_itertools.py +14 -0
- accelforge/util/_mathfuncs.py +57 -0
- accelforge/util/_parse_expressions.py +339 -0
- accelforge/util/_picklecache.py +32 -0
- accelforge/util/_setexpressions.py +268 -0
- accelforge/util/_sympy/__init__.py +0 -0
- accelforge/util/_sympy/broadcast_max.py +18 -0
- accelforge/util/_visualization.py +112 -0
- accelforge/util/_yaml.py +579 -0
- accelforge/util/parallel.py +193 -0
- accelforge-0.0.1.dist-info/METADATA +64 -0
- accelforge-0.0.1.dist-info/RECORD +258 -0
- accelforge-0.0.1.dist-info/WHEEL +5 -0
- accelforge-0.0.1.dist-info/licenses/LICENSE +19 -0
- accelforge-0.0.1.dist-info/top_level.txt +5 -0
- docs/_build/html/_sources/fastfusion.frontend.mapper.rst.txt +37 -0
- docs/_build/html/_sources/fastfusion.frontend.rst.txt +70 -0
- docs/_build/html/_sources/fastfusion.frontend.workload.rst.txt +21 -0
- docs/_build/html/_sources/fastfusion.mapper.FFM.rst.txt +37 -0
- docs/_build/html/_sources/fastfusion.mapper.rst.txt +18 -0
- docs/_build/html/_sources/fastfusion.rst.txt +20 -0
- docs/_build/html/_sources/fastfusion.util.rst.txt +21 -0
- docs/_build/html/_sources/index.rst.txt +87 -0
- docs/_build/html/_sources/modules.rst.txt +7 -0
- docs/_build/html/_sources/notes/citation.rst.txt +45 -0
- docs/_build/html/_sources/notes/definitions.rst.txt +43 -0
- docs/_build/html/_sources/notes/faqs.rst.txt +39 -0
- docs/_build/html/_sources/notes/modeling/accelerator_energy_latency.rst.txt +72 -0
- docs/_build/html/_sources/notes/modeling/component_energy_area.rst.txt +96 -0
- docs/_build/html/_sources/notes/modeling/mapping.rst.txt +100 -0
- docs/_build/html/_sources/notes/modeling.rst.txt +33 -0
- docs/_build/html/_sources/notes/parsing/arithmetic_parsing.rst.txt +136 -0
- docs/_build/html/_sources/notes/parsing/setexpressions.rst.txt +63 -0
- docs/_build/html/_sources/notes/parsing/yaml_parsing.rst.txt +176 -0
- docs/_build/html/_sources/notes/quickstart_and_installation.rst.txt +9 -0
- docs/_build/html/_sources/notes/spec/architecture.rst.txt +133 -0
- docs/_build/html/_sources/notes/spec/mapping.rst.txt +12 -0
- docs/_build/html/_sources/notes/spec/workload.rst.txt +83 -0
- docs/_build/html/_sources/notes/spec.rst.txt +36 -0
- docs/source/_ext/include_attrs.py +213 -0
- docs/source/_ext/include_docstring.py +364 -0
- docs/source/_ext/include_functions.py +154 -0
- docs/source/_ext/include_notebook.py +131 -0
- docs/source/_ext/include_yaml.py +119 -0
- docs/source/_ext/inherited_attributes.py +222 -0
- docs/source/_ext/paths.py +4 -0
- docs/source/conf.py +79 -0
- examples/arches/compute_in_memory/_include.yaml +74 -0
- examples/arches/compute_in_memory/_include_functions.py +229 -0
- examples/arches/compute_in_memory/_load_spec.py +57 -0
- examples/arches/compute_in_memory/components/c2c_multiplier.py +181 -0
- examples/arches/compute_in_memory/components/dac_c2c_r2r.py +605 -0
- examples/arches/compute_in_memory/components/misc.py +195 -0
- examples/arches/compute_in_memory/components/util/bit_functions.py +51 -0
- examples/arches/compute_in_memory/components/zero_comparator.py +92 -0
- examples/arches/compute_in_memory/isaac.yaml +233 -0
- examples/arches/compute_in_memory/memory_cells/ecram_demo.yaml +63 -0
- examples/arches/compute_in_memory/memory_cells/rram_example.yaml +63 -0
- examples/arches/compute_in_memory/memory_cells/rram_isaac_isca_2016.yaml +64 -0
- examples/arches/compute_in_memory/memory_cells/rram_neurosim_default.yaml +63 -0
- examples/arches/compute_in_memory/memory_cells/rram_raella_isca_2023.yaml +70 -0
- examples/arches/compute_in_memory/memory_cells/rram_wan_nature_2022.yaml +63 -0
- examples/arches/compute_in_memory/memory_cells/sram_colonnade_jssc_2021.yaml +63 -0
- examples/arches/compute_in_memory/memory_cells/sram_example.yaml +63 -0
- examples/arches/compute_in_memory/memory_cells/sram_jia_jssc_2020.yaml +63 -0
- examples/arches/compute_in_memory/memory_cells/sram_sinangil_jssc_2021.yaml +63 -0
- examples/arches/compute_in_memory/memory_cells/sram_wang_vlsi_2022.yaml +63 -0
- examples/arches/compute_in_memory/wang_vlsi_2022.yaml +289 -0
- examples/arches/eyeriss.yaml +68 -0
- examples/arches/fanout_variations/at_glb.yaml +31 -0
- examples/arches/fanout_variations/at_glb_with_fanout_node.yaml +34 -0
- examples/arches/fanout_variations/at_mac.yaml +31 -0
- examples/arches/fanout_variations/at_mac_with_constraints.yaml +38 -0
- examples/arches/fanout_variations/at_mac_with_fanout_node.yaml +34 -0
- examples/arches/nvdla.yaml +47 -0
- examples/arches/simple.yaml +28 -0
- examples/arches/tpu_v4i.yaml +67 -0
- examples/mappings/unfused_matmuls_to_simple.yaml +33 -0
- examples/misc/component_annotated.yaml +33 -0
- examples/workloads/gpt3_6.7B.yaml +124 -0
- examples/workloads/matmuls.yaml +20 -0
- examples/workloads/mobilenet_28.yaml +81 -0
- examples/workloads/mobilenet_various_separate.yaml +106 -0
- examples/workloads/three_matmuls_annotated.yaml +59 -0
- notebooks/.ipynb_checkpoints/fastfusion_arch_study_michael-checkpoint.ipynb +359 -0
- notebooks/compute_in_memory/_scripts.py +339 -0
- notebooks/compute_in_memory/isaac.guide.ipynb +270 -0
- notebooks/compute_in_memory/wang_vlsi_2022.ipynb +602 -0
- notebooks/paths.py +4 -0
- notebooks/tutorials/.ipynb_checkpoints/1_FFM-checkpoint.ipynb +3110 -0
- notebooks/tutorials/FFM.ipynb +3498 -0
- notebooks/tutorials/_include.py +48 -0
- notebooks/tutorials/component_energy_area.ipynb +363 -0
- tests/Q_mapping.yaml +38 -0
- tests/__init__.py +0 -0
- tests/conv.mapping.yaml +27 -0
- tests/conv.workload.yaml +13 -0
- tests/conv_sym.mapping.yaml +43 -0
- tests/copy.mapping.yaml +35 -0
- tests/copy.workload.yaml +15 -0
- tests/distribuffers/__init__.py +0 -0
- tests/distribuffers/multicast/test_cases.yaml +482 -0
- tests/distribuffers/spec/binding/valid_bindings.yaml +97 -0
- tests/distribuffers/spec/distributed.yaml +100 -0
- tests/distribuffers/spec/logical_arch.yaml +32 -0
- tests/distribuffers/spec/physical_arch.yaml +69 -0
- tests/distribuffers/test_binding.py +48 -0
- tests/frontend/__init__.py +0 -0
- tests/frontend/test_mapping_viz.py +52 -0
- tests/mapper/__init__.py +0 -0
- tests/mapper/configs/conv1d/conv1d.mapping.yaml +31 -0
- tests/mapper/configs/conv1d/conv1d.workload.yaml +11 -0
- tests/mapper/configs/two_conv1d/two_conv1d.expected.yaml +38 -0
- tests/mapper/configs/two_conv1d/two_conv1d.mapping.yaml +54 -0
- tests/mapper/configs/two_conv1d/two_conv1d.workload.yaml +19 -0
- tests/mapper/test_mapping_to_isl.py +90 -0
- tests/mapper/test_spatial_reuse_analysis.py +67 -0
- tests/mapper/test_temporal_reuse_analysis.py +56 -0
- tests/mapper/util.py +58 -0
- tests/matmul.mapping.yaml +29 -0
- tests/matmul.workload.yaml +12 -0
- tests/matmul_spatial.mapping.yaml +44 -0
- tests/mha.renames.yaml +65 -0
- tests/mha.workload.yaml +67 -0
- tests/mha.yaml +59 -0
- tests/mha_full.workload.yaml +67 -0
- tests/mobilenet.workload.yaml +35 -0
- tests/mobilenet_long.workload.yaml +64 -0
- tests/pmappingcache.py +24 -0
- tests/processing_stage.arch.yaml +40 -0
- tests/snowcat.arch.yaml +36 -0
- tests/test_ffm_join_pmappings.py +106 -0
- tests/test_ffm_make_pmappings.py +82 -0
- tests/test_ffm_make_tile_shapes.py +49 -0
- tests/test_mapper.py +100 -0
- tests/test_model.py +37 -0
- tests/test_plotting.py +72 -0
- tests/test_processing_stage.py +46 -0
- tests/test_symbolic_model.py +248 -0
- 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
|