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,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)
|
accelforge/util/_isl.py
ADDED
|
@@ -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
|