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,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
|