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,36 @@
1
+ from typing import Generic, TypeVar
2
+
3
+
4
+ T = TypeVar("T")
5
+
6
+
7
+ class fzs(frozenset[T], Generic[T]):
8
+ def __repr__(self):
9
+ return f"{{{', '.join(sorted(x.__repr__() for x in self))}}}"
10
+
11
+ def __str__(self):
12
+ return self.__repr__()
13
+
14
+ def __or__(self, other: "fzs[T]") -> "fzs[T]":
15
+ return fzs(super().__or__(other))
16
+
17
+ def __and__(self, other: "fzs[T]") -> "fzs[T]":
18
+ return fzs(super().__and__(other))
19
+
20
+ def __sub__(self, other: "fzs[T]") -> "fzs[T]":
21
+ return fzs(super().__sub__(other))
22
+
23
+ def __xor__(self, other: "fzs[T]") -> "fzs[T]":
24
+ return fzs(super().__xor__(other))
25
+
26
+ def __lt__(self, other: "fzs[T]") -> bool:
27
+ return sorted(self) < sorted(other)
28
+
29
+ def __le__(self, other: "fzs[T]") -> bool:
30
+ return sorted(self) <= sorted(other)
31
+
32
+ def __gt__(self, other: "fzs[T]") -> bool:
33
+ return sorted(self) > sorted(other)
34
+
35
+ def __ge__(self, other: "fzs[T]") -> bool:
36
+ return sorted(self) >= sorted(other)
@@ -0,0 +1,29 @@
1
+ from typing import Any, TypeVar, Generic, Type
2
+
3
+ import islpy as isl
4
+
5
+ from pydantic import BaseModel, ValidationError
6
+ from pydantic_core import core_schema
7
+
8
+
9
+ class ISLStr:
10
+ def __init__(self, isl_type: Type):
11
+ self.isl_type = isl_type
12
+
13
+ def __get_pydantic_core_schema__(self, _source, _handler):
14
+ def validate(value: Any):
15
+ if not isinstance(value, str):
16
+ raise TypeError("Value must be a string")
17
+ try:
18
+ print(value)
19
+ return self.isl_type(value)
20
+ except Exception as e:
21
+ raise ValueError(f"Invalid input for {self.isl_type.__name__}: {e}")
22
+
23
+ return core_schema.no_info_plain_validator_function(validate)
24
+
25
+
26
+ ISLAff = ISLStr(isl.PwAff)
27
+ ISLMap = ISLStr(isl.Map)
28
+ ISLSet = ISLStr(isl.Set)
29
+ ISLSpace = ISLStr(isl.Space)
@@ -0,0 +1,14 @@
1
+ from collections.abc import Iterable
2
+ from typing import TypeVar
3
+
4
+
5
+ T = TypeVar("T")
6
+ VT = TypeVar("VT")
7
+
8
+
9
+ def first(iterable: Iterable[T], default: VT = None) -> T | VT:
10
+ """Return first element in `iterable` or `default` if empty.
11
+
12
+ Equivalent to `next(iter(iterable), default)`.
13
+ """
14
+ return next(iter(iterable), default)
@@ -0,0 +1,57 @@
1
+ import functools
2
+ from math import ceil, comb
3
+ import functools
4
+
5
+
6
+ @functools.lru_cache(maxsize=None)
7
+ def _count_factorizations_imperfect(n, into_n_parts):
8
+ # Factorize n into into_n_parts parts
9
+ RUBY_STYLE_IMPERFECT = True
10
+ # RUBY_STYLE_IMPERFECT = True
11
+ if n <= 1:
12
+ return 1
13
+ if into_n_parts <= 0:
14
+ return 1
15
+
16
+ shapes = list(range(1, ceil(n**0.5) + 1))
17
+ shapes = shapes + [ceil(n / s) for s in shapes]
18
+ shapes = sorted(set(shapes))
19
+
20
+ if RUBY_STYLE_IMPERFECT:
21
+ shapes = list(range(1, n + 1))
22
+
23
+ total = 0
24
+ for s in shapes:
25
+ n = _count_factorizations_imperfect(ceil(n / s), into_n_parts - 1)
26
+ total += _count_factorizations_imperfect(ceil(n / s), into_n_parts - 1)
27
+
28
+ return total
29
+
30
+
31
+ def _prime_factorization(n):
32
+ f = []
33
+ i = 2
34
+ while n > 1:
35
+ if n % i == 0:
36
+ f.append(i)
37
+ n //= i
38
+ else:
39
+ i += 1
40
+ return f
41
+
42
+
43
+ def _count_factorizations(n, into_n_parts, imperfect=False):
44
+ if into_n_parts <= 1:
45
+ return 1
46
+ f = _prime_factorization(n)
47
+ factors = {f2: f.count(f2) for f2 in set(f)}
48
+ total = 1
49
+ for exp in factors.values():
50
+ total *= comb(exp + into_n_parts - 1, into_n_parts - 1) # n choose k
51
+
52
+ if imperfect:
53
+ n = _count_factorizations_imperfect(n, into_n_parts)
54
+ assert n >= total, f"n: {n} < total: {total}"
55
+ return n
56
+
57
+ return total
@@ -0,0 +1,339 @@
1
+ import functools
2
+ from inspect import signature
3
+ from importlib.machinery import SourceFileLoader
4
+ import logging
5
+ import math
6
+ import re
7
+ import threading
8
+ from typing import Any, Callable
9
+ from ._yaml import load_yaml, SCRIPTS_FROM
10
+ from ruamel.yaml.scalarstring import DoubleQuotedScalarString, SingleQuotedScalarString
11
+ import os
12
+ import keyword
13
+
14
+
15
+ class ParseError(Exception):
16
+ def __init__(self, *args, source_field: Any = None, message: str = None, **kwargs):
17
+ self._fields = [source_field] if source_field is not None else []
18
+ if message is None and len(args) > 0:
19
+ message = args[0]
20
+ self.message = message
21
+ super().__init__(*args, **kwargs)
22
+
23
+ def add_field(self, field: Any):
24
+ self._fields.append(field)
25
+
26
+ def __str__(self) -> str:
27
+ s = f"{self.__class__.__name__} in {'.'.join(str(field) for field in self._fields[::-1])}"
28
+ if self.message is not None:
29
+ s += f": {self.message}"
30
+ if getattr(self, "__notes__", None):
31
+ s += f"\n\n{'\n\n'.join(self.__notes__)}"
32
+ return s
33
+
34
+
35
+ class LiteralString(str):
36
+ """
37
+ A string literal that should not be parsed.
38
+ """
39
+
40
+ pass
41
+
42
+
43
+ def is_literal_string(value: Any) -> bool:
44
+ return isinstance(
45
+ value, (DoubleQuotedScalarString, SingleQuotedScalarString, LiteralString)
46
+ )
47
+
48
+
49
+ MATH_FUNCS = {
50
+ "ceil": math.ceil,
51
+ "comb": math.comb,
52
+ "copysign": math.copysign,
53
+ "fabs": math.fabs,
54
+ "factorial": math.factorial,
55
+ "floor": math.floor,
56
+ "fmod": math.fmod,
57
+ "frexp": math.frexp,
58
+ "fsum": math.fsum,
59
+ "gcd": math.gcd,
60
+ "isclose": math.isclose,
61
+ "isfinite": math.isfinite,
62
+ "isinf": math.isinf,
63
+ "isnan": math.isnan,
64
+ "isqrt": math.isqrt,
65
+ "ldexp": math.ldexp,
66
+ "modf": math.modf,
67
+ "perm": math.perm,
68
+ "prod": math.prod,
69
+ "remainder": math.remainder,
70
+ "trunc": math.trunc,
71
+ "exp": math.exp,
72
+ "expm1": math.expm1,
73
+ "log": math.log,
74
+ "log1p": math.log1p,
75
+ "log2": math.log2,
76
+ "log10": math.log10,
77
+ "pow": math.pow,
78
+ "sqrt": math.sqrt,
79
+ "acos": math.acos,
80
+ "asin": math.asin,
81
+ "atan": math.atan,
82
+ "atan2": math.atan2,
83
+ "cos": math.cos,
84
+ "dist": math.dist,
85
+ "hypot": math.hypot,
86
+ "sin": math.sin,
87
+ "tan": math.tan,
88
+ "degrees": math.degrees,
89
+ "radians": math.radians,
90
+ "acosh": math.acosh,
91
+ "asinh": math.asinh,
92
+ "atanh": math.atanh,
93
+ "cosh": math.cosh,
94
+ "sinh": math.sinh,
95
+ "tanh": math.tanh,
96
+ "erf": math.erf,
97
+ "erfc": math.erfc,
98
+ "gamma": math.gamma,
99
+ "lgamma": math.lgamma,
100
+ "pi": math.pi,
101
+ "e": math.e,
102
+ "tau": math.tau,
103
+ "inf": math.inf,
104
+ "nan": math.nan,
105
+ "abs": abs,
106
+ "round": round,
107
+ "pow": pow,
108
+ "sum": sum,
109
+ "range": range,
110
+ "len": len,
111
+ "min": min,
112
+ "max": max,
113
+ "float": float,
114
+ "int": int,
115
+ "str": str,
116
+ "bool": bool,
117
+ "list": list,
118
+ "tuple": tuple,
119
+ "enumerate": enumerate,
120
+ "getcwd": os.getcwd,
121
+ "map": map,
122
+ }
123
+ SCRIPT_FUNCS = {}
124
+
125
+ parse_expressions_local = threading.local()
126
+
127
+
128
+ class OwnedLock:
129
+ def __init__(self):
130
+ super().__init__()
131
+ self._owner = None
132
+ self.lock = threading.Lock()
133
+
134
+ def acquire(self, *args, **kwargs):
135
+ result = self.lock.acquire(*args, **kwargs)
136
+ if result:
137
+ self._owner = threading.get_ident()
138
+ return result
139
+
140
+ def release(self, *args, **kwargs):
141
+ self._owner = None
142
+ self.lock.release(*args, **kwargs)
143
+
144
+ def is_locked_by_current_thread(self):
145
+ return self._owner == threading.get_ident() and self.lock.locked()
146
+
147
+
148
+ parse_expression_thread_lock = OwnedLock()
149
+
150
+
151
+ class ParseExpressionsContext:
152
+ def __init__(self, spec: "Spec"):
153
+ self.spec = spec
154
+ self.grabbed_lock = False
155
+
156
+ def __enter__(self):
157
+ if parse_expression_thread_lock.is_locked_by_current_thread():
158
+ return
159
+ parse_expression_thread_lock.acquire()
160
+ parse_expressions_local.script_funcs = {}
161
+ for p in self.spec.config.expression_custom_functions:
162
+ if isinstance(p, str):
163
+ parse_expressions_local.script_funcs.update(load_functions_from_file(p))
164
+ elif isinstance(p, Callable):
165
+ parse_expressions_local.script_funcs[p.__name__] = p
166
+ else:
167
+ raise ValueError(f"Invalid expression custom function: {p}")
168
+ self.grabbed_lock = True
169
+
170
+ def __exit__(self, exc_type, exc_value, traceback):
171
+ if self.grabbed_lock:
172
+ self.spec = None
173
+ del parse_expressions_local.script_funcs
174
+ parse_expression_thread_lock.release()
175
+
176
+
177
+ def cast_to_numeric(x: Any) -> int | float | bool:
178
+ if str(x).lower() == "true":
179
+ return True
180
+ if str(x).lower() == "false":
181
+ return False
182
+ if float(x) == int(x):
183
+ return int(x)
184
+ return float(x)
185
+
186
+
187
+ class CallableLambda:
188
+ def __init__(self, func, expression):
189
+ self.__name__ = func.__name__
190
+ self.__doc__ = func.__doc__
191
+ self._original_expression = expression
192
+ self._func = func
193
+
194
+ def __call__(self, *args, **kwargs):
195
+ return self._func(*args, **kwargs)
196
+
197
+
198
+ @functools.lru_cache(maxsize=1000)
199
+ def infostr_log_cache(infostr: str):
200
+ logging.info(infostr)
201
+
202
+
203
+ def parse_expression(
204
+ expression, symbol_table, attr_name: str = None, location: str = None
205
+ ):
206
+ try:
207
+ return cast_to_numeric(expression)
208
+ except:
209
+ pass
210
+
211
+ if not isinstance(expression, str):
212
+ return expression
213
+
214
+ if expression in symbol_table:
215
+ result = symbol_table[expression]
216
+ if isinstance(result, str):
217
+ result = LiteralString(result)
218
+ return result
219
+
220
+ FUNCTION_BINDINGS = {}
221
+ FUNCTION_BINDINGS["__builtins__"] = None # Safety
222
+ if hasattr(parse_expressions_local, "script_funcs"):
223
+ FUNCTION_BINDINGS.update(parse_expressions_local.script_funcs)
224
+ FUNCTION_BINDINGS.update(MATH_FUNCS)
225
+
226
+ try:
227
+ v = eval(expression, FUNCTION_BINDINGS, symbol_table)
228
+ infostr = f'Calculated "{expression}" = {v}.'
229
+ if isinstance(v, str):
230
+ v = LiteralString(v)
231
+ if isinstance(v, Callable):
232
+ v = CallableLambda(v, expression)
233
+ success = True
234
+ except Exception as e:
235
+ errstr = f"Failed to evaluate: {expression}\n"
236
+ if (
237
+ isinstance(expression, str)
238
+ and expression.isidentifier()
239
+ and expression not in symbol_table
240
+ and expression not in FUNCTION_BINDINGS
241
+ ):
242
+ e = NameError(f"Name '{expression}' is not defined.")
243
+ extra = ""
244
+ if attr_name and location:
245
+ extra = f" while parsing {location}.{attr_name}"
246
+ elif attr_name:
247
+ extra = f" while parsing {attr_name}"
248
+ elif location:
249
+ extra = f" while parsing {location}"
250
+ errstr += f"Problem encountered{extra}: {e.__class__.__name__}: {e}\n"
251
+ err = errstr
252
+ errstr += f"Symbol table: "
253
+ bindings = {}
254
+ bindings.update(symbol_table)
255
+ bindings.update(getattr(parse_expressions_local, "script_funcs", {}))
256
+ extras = []
257
+ for k, v in bindings.items():
258
+ if isinstance(v, Callable):
259
+ bindings[k] = f"{k}{signature(getattr(v, '_func', v))}"
260
+ else:
261
+ extras.append(f"\n {k} = {v}")
262
+ for k, v in bindings.items():
263
+ bindings[k] = str(v).replace("\n", "\\n")
264
+ if len(bindings[k]) > 100:
265
+ bindings[k] = bindings[k][:100] + "..."
266
+ errstr += "".join(f"\n\t{k} = {v}" for k, v in bindings.items())
267
+ errstr += "\n\n" + err
268
+ errstr += (
269
+ f"Please ensure that the expression used is a valid Python expression.\n"
270
+ )
271
+ possibly_used = {
272
+ k: bindings.get(k, FUNCTION_BINDINGS.get(k, "UNDEFINED"))
273
+ for k in re.findall(r"([a-zA-Z_][a-zA-Z0-9_]*)", expression)
274
+ if k not in keyword.kwlist
275
+ }
276
+ if possibly_used:
277
+ errstr += (
278
+ f"The following may have been used in the expression:\n\t"
279
+ + "\n\t".join(f"{k} = {v}" for k, v in possibly_used.items())
280
+ )
281
+ errstr += (
282
+ "\n\nIf you meant to enter a string in a YAML file, please wrap the\n"
283
+ "expression in single or double quotes. If you meant to enter a raw \n"
284
+ "string, cast it to a accelforge.LiteralString object."
285
+ )
286
+ success = False
287
+
288
+ if not success:
289
+ raise ParseError(errstr)
290
+
291
+ infostr_log_cache(infostr)
292
+
293
+ return v
294
+
295
+
296
+ class PicklingSafeCallable:
297
+ def __init__(self, func: Callable, path: str):
298
+ self.func = func
299
+ self.__name__ = func.__name__
300
+ self.path = path
301
+
302
+ def __call__(self, *args, **kwargs):
303
+ return self.func(*args, **kwargs)
304
+
305
+ def __getstate__(self):
306
+ return {
307
+ "func": self.func.__name__,
308
+ "module": self.func.__module__,
309
+ "path": self.path,
310
+ }
311
+
312
+ def __setstate__(self, state):
313
+ # Restore required attributes so subsequent pickling works
314
+ self.path = state.get("path")
315
+ func_name = state["func"]
316
+ self.func = load_functions_from_file(self.path)[func_name]
317
+ self.__name__ = func_name
318
+
319
+ def __copy__(self):
320
+ return PicklingSafeCallable(self.func, self.path)
321
+
322
+ def __deepcopy__(self, memo):
323
+ return PicklingSafeCallable(self.func, self.path)
324
+
325
+
326
+ @functools.lru_cache(maxsize=100)
327
+ def load_functions_from_file(path: str):
328
+ path = path.strip()
329
+ if not os.path.exists(path):
330
+ raise FileNotFoundError(f"Could not find math function file {path}.")
331
+ python_module = SourceFileLoader("python_plug_in", path).load_module()
332
+ funcs = {}
333
+ defined_funcs = [
334
+ f for f in dir(python_module) if isinstance(getattr(python_module, f), Callable)
335
+ ]
336
+ for func in defined_funcs:
337
+ logging.info(f"Adding function {func} from {path} to the script library.")
338
+ funcs[func] = PicklingSafeCallable(getattr(python_module, func), path)
339
+ return funcs
@@ -0,0 +1,32 @@
1
+ import pickle
2
+ from pathlib import Path
3
+ from typing import Callable, TypeVar
4
+
5
+
6
+ T = TypeVar("T")
7
+
8
+
9
+ class PickleCache:
10
+ def __init__(self, fname: Path):
11
+ self.fname = fname
12
+
13
+ def get(self, cache_miss_thunk: Callable[[], T]) -> T:
14
+ """
15
+ Return loaded pickle at `self.fname` if exists;
16
+ otherwise, calls `cache_miss_thunk` and stores result in `self.fname`.
17
+ """
18
+ print(self.fname)
19
+ if self.fname.exists():
20
+ with open(self.fname, "rb") as f:
21
+ return pickle.load(f)
22
+ else:
23
+ result = cache_miss_thunk()
24
+ with open(self.fname, "wb") as f:
25
+ pickle.dump(result, f)
26
+ return result
27
+
28
+ def set(self, data: T) -> None:
29
+ """Set data at `self.fname`."""
30
+ with open(self.fname, "wb") as f:
31
+ pickle.dump(data, f)
32
+ return data