mapFolding 0.12.3__py3-none-any.whl → 0.13.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.
- mapFolding/basecamp.py +6 -6
- mapFolding/dataBaskets.py +38 -51
- mapFolding/oeis.py +1 -1
- mapFolding/someAssemblyRequired/RecipeJob.py +82 -82
- mapFolding/someAssemblyRequired/_toolkitContainers.py +3 -3
- mapFolding/someAssemblyRequired/makeAllModules.py +8 -11
- mapFolding/someAssemblyRequired/makeJobTheorem2Numba.py +10 -35
- mapFolding/someAssemblyRequired/makeJobTheorem2codon.py +196 -0
- mapFolding/someAssemblyRequired/toolkitNumba.py +10 -8
- {tests → mapFolding/tests}/__init__.py +2 -2
- {tests → mapFolding/tests}/conftest.py +232 -63
- {tests → mapFolding/tests}/test_computations.py +36 -14
- {tests → mapFolding/tests}/test_filesystem.py +10 -13
- {tests → mapFolding/tests}/test_oeis.py +5 -19
- {tests → mapFolding/tests}/test_other.py +6 -6
- {tests → mapFolding/tests}/test_tasks.py +5 -5
- {mapfolding-0.12.3.dist-info → mapfolding-0.13.1.dist-info}/METADATA +18 -27
- {mapfolding-0.12.3.dist-info → mapfolding-0.13.1.dist-info}/RECORD +22 -21
- {mapfolding-0.12.3.dist-info → mapfolding-0.13.1.dist-info}/top_level.txt +0 -1
- {mapfolding-0.12.3.dist-info → mapfolding-0.13.1.dist-info}/WHEEL +0 -0
- {mapfolding-0.12.3.dist-info → mapfolding-0.13.1.dist-info}/entry_points.txt +0 -0
- {mapfolding-0.12.3.dist-info → mapfolding-0.13.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"""codon.
|
|
2
|
+
|
|
3
|
+
https://docs.exaloop.io/start/install/
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from astToolkit import (
|
|
7
|
+
Be, DOT, extractFunctionDef, Grab, identifierDotAttribute, IngredientsFunction, IngredientsModule, Make, NodeChanger,
|
|
8
|
+
NodeTourist, Then)
|
|
9
|
+
from astToolkit.transformationTools import removeUnusedParameters, write_astModule
|
|
10
|
+
from hunterMakesPy import autoDecodingRLE, raiseIfNone
|
|
11
|
+
from mapFolding import getPathFilenameFoldsTotal, MapFoldingState
|
|
12
|
+
from mapFolding.someAssemblyRequired import IfThis
|
|
13
|
+
from mapFolding.someAssemblyRequired.RecipeJob import RecipeJobTheorem2
|
|
14
|
+
from mapFolding.syntheticModules.initializeCount import initializeGroupsOfFolds
|
|
15
|
+
from pathlib import Path, PurePosixPath
|
|
16
|
+
from typing import cast, NamedTuple
|
|
17
|
+
import ast
|
|
18
|
+
import subprocess
|
|
19
|
+
import sys
|
|
20
|
+
|
|
21
|
+
class DatatypeConfiguration(NamedTuple):
|
|
22
|
+
"""Configuration for mapping framework datatypes to compiled datatypes.
|
|
23
|
+
|
|
24
|
+
This configuration class defines how abstract datatypes used in the map folding framework should be replaced with compiled
|
|
25
|
+
datatypes during code generation. Each configuration specifies the source module, target type name, and optional import
|
|
26
|
+
alias for the transformation.
|
|
27
|
+
|
|
28
|
+
Attributes
|
|
29
|
+
----------
|
|
30
|
+
datatypeIdentifier : str
|
|
31
|
+
Framework datatype identifier to be replaced.
|
|
32
|
+
typeModule : identifierDotAttribute
|
|
33
|
+
Module containing the target datatype (e.g., 'codon', 'numpy').
|
|
34
|
+
typeIdentifier : str
|
|
35
|
+
Concrete type name in the target module.
|
|
36
|
+
type_asname : str | None = None
|
|
37
|
+
Optional import alias for the type.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
datatypeIdentifier: str
|
|
41
|
+
typeModule: identifierDotAttribute
|
|
42
|
+
typeIdentifier: str
|
|
43
|
+
type_asname: str | None = None
|
|
44
|
+
|
|
45
|
+
# TODO replace with dynamic system. Probably use `Final` in the dataclass.
|
|
46
|
+
listIdentifiersStaticValuesHARDCODED: list[str] = ['dimensionsTotal', 'leavesTotal']
|
|
47
|
+
|
|
48
|
+
listDatatypeConfigurations: list[DatatypeConfiguration] = [
|
|
49
|
+
DatatypeConfiguration(datatypeIdentifier='DatatypeLeavesTotal', typeModule='numpy', typeIdentifier='uint16', type_asname='DatatypeLeavesTotal'),
|
|
50
|
+
DatatypeConfiguration(datatypeIdentifier='DatatypeElephino', typeModule='numpy', typeIdentifier='uint16', type_asname='DatatypeElephino'),
|
|
51
|
+
DatatypeConfiguration(datatypeIdentifier='DatatypeFoldsTotal', typeModule='numpy', typeIdentifier='int64', type_asname='DatatypeFoldsTotal'),
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
listNumPy_dtype: list[DatatypeConfiguration] = [
|
|
55
|
+
DatatypeConfiguration(datatypeIdentifier='Array1DLeavesTotal', typeModule='numpy', typeIdentifier='uint16', type_asname='Array1DLeavesTotal'),
|
|
56
|
+
DatatypeConfiguration(datatypeIdentifier='Array1DElephino', typeModule='numpy', typeIdentifier='uint16', type_asname='Array1DElephino'),
|
|
57
|
+
DatatypeConfiguration(datatypeIdentifier='Array3D', typeModule='numpy', typeIdentifier='uint16', type_asname='Array3D'),
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
def _addWriteFoldsTotal(ingredientsFunction: IngredientsFunction, job: RecipeJobTheorem2) -> IngredientsFunction:
|
|
61
|
+
NodeChanger(Be.Return, Then.removeIt).visit(ingredientsFunction.astFunctionDef)
|
|
62
|
+
ingredientsFunction.astFunctionDef.returns = Make.Constant(None)
|
|
63
|
+
|
|
64
|
+
writeFoldsTotal = Make.Expr(Make.Call(Make.Attribute(
|
|
65
|
+
Make.Call(Make.Name('open'), listParameters=[Make.Constant(str(job.pathFilenameFoldsTotal.as_posix())), Make.Constant('w')])
|
|
66
|
+
, 'write'), listParameters=[Make.Call(Make.Name('str'), listParameters=[
|
|
67
|
+
Make.Mult().join([job.shatteredDataclass.countingVariableName, Make.Constant(job.state.leavesTotal * 2)])])]))
|
|
68
|
+
|
|
69
|
+
NodeChanger(IfThis.isAllOf(Be.AugAssign.targetIs(IfThis.isNameIdentifier(job.shatteredDataclass.countingVariableName.id))
|
|
70
|
+
, Be.AugAssign.opIs(Be.Mult), Be.AugAssign.valueIs(Be.Constant))
|
|
71
|
+
, doThat=Then.replaceWith(writeFoldsTotal)
|
|
72
|
+
).visit(ingredientsFunction.astFunctionDef)
|
|
73
|
+
|
|
74
|
+
return ingredientsFunction
|
|
75
|
+
|
|
76
|
+
def _datatypeDefinitions(ingredientsFunction: IngredientsFunction, ingredientsModule: IngredientsModule) -> tuple[IngredientsFunction, IngredientsModule]:
|
|
77
|
+
for datatypeConfig in [*listDatatypeConfigurations, *listNumPy_dtype]:
|
|
78
|
+
ingredientsFunction.imports.removeImportFrom(datatypeConfig.typeModule, None, datatypeConfig.datatypeIdentifier)
|
|
79
|
+
ingredientsFunction.imports.addImportFrom_asStr(datatypeConfig.typeModule, datatypeConfig.typeIdentifier, datatypeConfig.type_asname)
|
|
80
|
+
|
|
81
|
+
ingredientsFunction.imports.removeImportFromModule('mapFolding.dataBaskets')
|
|
82
|
+
|
|
83
|
+
return ingredientsFunction, ingredientsModule
|
|
84
|
+
|
|
85
|
+
def _pythonCode2expr(string: str) -> ast.expr:
|
|
86
|
+
"""Convert *one* expression as a string of Python code to an `ast.expr`."""
|
|
87
|
+
return raiseIfNone(NodeTourist(Be.Expr, Then.extractIt(DOT.value)).captureLastMatch(ast.parse(string)))
|
|
88
|
+
|
|
89
|
+
def _variableCompatibility(ingredientsFunction: IngredientsFunction, job: RecipeJobTheorem2) -> IngredientsFunction:
|
|
90
|
+
# On some assignment or comparison values, add a type constructor to ensure compatibility.
|
|
91
|
+
# On some values-as-indexer, add a type constructor to ensure indexing-method compatibility.
|
|
92
|
+
for ast_arg in job.shatteredDataclass.list_argAnnotated4ArgumentsSpecification:
|
|
93
|
+
identifier = ast_arg.arg
|
|
94
|
+
annotation = raiseIfNone(ast_arg.annotation)
|
|
95
|
+
|
|
96
|
+
# `identifier` in Augmented Assignment, or in Assignments and value is Constant.
|
|
97
|
+
NodeChanger(findThis=IfThis.isAnyOf(
|
|
98
|
+
Be.AugAssign.targetIs(IfThis.isNestedNameIdentifier(identifier))
|
|
99
|
+
, IfThis.isAllOf(IfThis.isAssignAndTargets0Is(IfThis.isNameIdentifier(identifier))
|
|
100
|
+
, Be.Assign.valueIs(Be.Constant))
|
|
101
|
+
)
|
|
102
|
+
, doThat=lambda node, annotation=annotation: Grab.valueAttribute(Then.replaceWith(Make.Call(annotation, listParameters=[node.value])))(node)
|
|
103
|
+
).visit(ingredientsFunction.astFunctionDef)
|
|
104
|
+
|
|
105
|
+
# `identifier` - 1.
|
|
106
|
+
NodeChanger(Be.BinOp.leftIs(IfThis.isNestedNameIdentifier(identifier))
|
|
107
|
+
, doThat=lambda node, annotation=annotation: Grab.rightAttribute(Then.replaceWith(Make.Call(annotation, listParameters=[node.right])))(node)
|
|
108
|
+
).visit(ingredientsFunction.astFunctionDef)
|
|
109
|
+
|
|
110
|
+
# `identifier` in Comparison.
|
|
111
|
+
NodeChanger(Be.Compare.leftIs(IfThis.isNestedNameIdentifier(identifier))
|
|
112
|
+
, doThat=lambda node, annotation=annotation: Grab.comparatorsAttribute(lambda at, annotation=annotation: Then.replaceWith([Make.Call(annotation, listParameters=[node.comparators[0]])])(at[0]))(node)
|
|
113
|
+
).visit(ingredientsFunction.astFunctionDef)
|
|
114
|
+
|
|
115
|
+
# `identifier` has exactly one index value.
|
|
116
|
+
NodeChanger(IfThis.isAllOf(Be.Subscript.valueIs(IfThis.isNestedNameIdentifier(identifier))
|
|
117
|
+
, lambda node: not Be.Subscript.sliceIs(Be.Tuple)(node))
|
|
118
|
+
, doThat=lambda node: Grab.sliceAttribute(Then.replaceWith(Make.Call(Make.Name('int'), listParameters=[node.slice])))(node)
|
|
119
|
+
).visit(ingredientsFunction.astFunctionDef)
|
|
120
|
+
|
|
121
|
+
# `identifier` has multiple index values.
|
|
122
|
+
NodeChanger(IfThis.isAllOf(Be.Subscript.valueIs(IfThis.isNestedNameIdentifier(identifier))
|
|
123
|
+
, Be.Subscript.sliceIs(Be.Tuple))
|
|
124
|
+
, doThat=lambda node: Grab.sliceAttribute(Grab.eltsAttribute(
|
|
125
|
+
Then.replaceWith([
|
|
126
|
+
Make.Call(Make.Name('int'), listParameters=[cast('ast.Tuple', node.slice).elts[index]])
|
|
127
|
+
for index in range(len(cast('ast.Tuple', node.slice).elts))])))(node)
|
|
128
|
+
).visit(ingredientsFunction.astFunctionDef)
|
|
129
|
+
|
|
130
|
+
return ingredientsFunction
|
|
131
|
+
|
|
132
|
+
def _move_arg2body(identifier: str, job: RecipeJobTheorem2) -> ast.AnnAssign | ast.Assign:
|
|
133
|
+
Ima___Assign, elementConstructor = job.shatteredDataclass.Z0Z_field2AnnAssign[identifier]
|
|
134
|
+
match elementConstructor:
|
|
135
|
+
case 'scalar':
|
|
136
|
+
cast('ast.Constant', cast('ast.Call', Ima___Assign.value).args[0]).value = int(job.state.__dict__[identifier])
|
|
137
|
+
case 'array':
|
|
138
|
+
dataAsStrRLE: str = autoDecodingRLE(job.state.__dict__[identifier], assumeAddSpaces=True)
|
|
139
|
+
dataAs_ast_expr: ast.expr = _pythonCode2expr(dataAsStrRLE)
|
|
140
|
+
cast('ast.Call', Ima___Assign.value).args = [dataAs_ast_expr]
|
|
141
|
+
case _:
|
|
142
|
+
pass
|
|
143
|
+
return Ima___Assign
|
|
144
|
+
|
|
145
|
+
def makeJob(job: RecipeJobTheorem2) -> None:
|
|
146
|
+
"""Generate an optimized module for map folding calculations.
|
|
147
|
+
|
|
148
|
+
This function orchestrates the complete code transformation assembly line to convert
|
|
149
|
+
a generic map folding algorithm into a highly optimized, specialized computation
|
|
150
|
+
module.
|
|
151
|
+
|
|
152
|
+
Parameters
|
|
153
|
+
----------
|
|
154
|
+
job : RecipeJobTheorem2
|
|
155
|
+
Configuration recipe containing source locations, target paths, and state.
|
|
156
|
+
|
|
157
|
+
"""
|
|
158
|
+
ingredientsCount: IngredientsFunction = IngredientsFunction(raiseIfNone(extractFunctionDef(job.source_astModule, job.countCallable)))
|
|
159
|
+
ingredientsCount.astFunctionDef.decorator_list = []
|
|
160
|
+
|
|
161
|
+
# Replace identifiers-with-static-values with their values.
|
|
162
|
+
listIdentifiersStaticValues: list[str] = listIdentifiersStaticValuesHARDCODED
|
|
163
|
+
for identifier in listIdentifiersStaticValues:
|
|
164
|
+
NodeChanger(IfThis.isNameIdentifier(identifier)
|
|
165
|
+
, Then.replaceWith(Make.Constant(int(job.state.__dict__[identifier])))
|
|
166
|
+
).visit(ingredientsCount.astFunctionDef)
|
|
167
|
+
|
|
168
|
+
ingredientsCount.imports.update(job.shatteredDataclass.imports)
|
|
169
|
+
ingredientsCount = removeUnusedParameters(ingredientsCount)
|
|
170
|
+
NodeChanger(Be.arg, lambda removeIt: ingredientsCount.astFunctionDef.body.insert(0, _move_arg2body(removeIt.arg, job))).visit(ingredientsCount.astFunctionDef)
|
|
171
|
+
|
|
172
|
+
ingredientsCount = _addWriteFoldsTotal(ingredientsCount, job)
|
|
173
|
+
ingredientsCount = _variableCompatibility(ingredientsCount, job)
|
|
174
|
+
|
|
175
|
+
ingredientsModule = IngredientsModule(launcher=Make.Module([
|
|
176
|
+
Make.If(Make.Compare(Make.Name('__name__'), [Make.Eq()], [Make.Constant('__main__')])
|
|
177
|
+
, body=[Make.Expr(Make.Call(Make.Name(job.countCallable)))])]))
|
|
178
|
+
|
|
179
|
+
ingredientsCount, ingredientsModule = _datatypeDefinitions(ingredientsCount, ingredientsModule)
|
|
180
|
+
|
|
181
|
+
ingredientsModule.appendIngredientsFunction(ingredientsCount)
|
|
182
|
+
write_astModule(ingredientsModule, pathFilename=job.pathFilenameModule, packageName=job.packageIdentifier)
|
|
183
|
+
|
|
184
|
+
if sys.platform == 'linux':
|
|
185
|
+
buildCommand: list[str] = ['codon', 'build', '--exe', '--release',
|
|
186
|
+
'--fast-math', '--enable-unsafe-fp-math', '--disable-exceptions',
|
|
187
|
+
str(job.pathFilenameModule)]
|
|
188
|
+
subprocess.run(buildCommand, check=False)
|
|
189
|
+
subprocess.run(['/usr/bin/strip', str(job.pathFilenameModule.with_suffix(''))], check=False)
|
|
190
|
+
|
|
191
|
+
if __name__ == '__main__':
|
|
192
|
+
state = initializeGroupsOfFolds(MapFoldingState((2,4)))
|
|
193
|
+
pathModule = PurePosixPath(Path.home(), 'mapFolding', 'jobs')
|
|
194
|
+
pathFilenameFoldsTotal = PurePosixPath(getPathFilenameFoldsTotal(state.mapShape, pathModule))
|
|
195
|
+
aJob = RecipeJobTheorem2(state, pathModule=pathModule, pathFilenameFoldsTotal=pathFilenameFoldsTotal)
|
|
196
|
+
makeJob(aJob)
|
|
@@ -26,7 +26,9 @@ system to produce standalone modules optimized for specific map dimensions and c
|
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
28
|
from astToolkit import identifierDotAttribute, IngredientsFunction, Make
|
|
29
|
-
from
|
|
29
|
+
from collections.abc import Callable
|
|
30
|
+
from numba.core.compiler import CompilerBase as numbaCompilerBase
|
|
31
|
+
from typing import Any, Final, NotRequired, TYPE_CHECKING, TypedDict
|
|
30
32
|
import ast
|
|
31
33
|
import dataclasses
|
|
32
34
|
import warnings
|
|
@@ -69,7 +71,7 @@ class ParametersNumba(TypedDict):
|
|
|
69
71
|
forceinline: NotRequired[bool]
|
|
70
72
|
forceobj: NotRequired[bool]
|
|
71
73
|
inline: NotRequired[str]
|
|
72
|
-
|
|
74
|
+
locals: NotRequired[dict[str, Any]]
|
|
73
75
|
looplift: NotRequired[bool]
|
|
74
76
|
no_cfunc_wrapper: NotRequired[bool]
|
|
75
77
|
no_cpython_wrapper: NotRequired[bool]
|
|
@@ -77,8 +79,8 @@ class ParametersNumba(TypedDict):
|
|
|
77
79
|
nogil: NotRequired[bool]
|
|
78
80
|
nopython: NotRequired[bool]
|
|
79
81
|
parallel: NotRequired[bool]
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
pipeline_class: NotRequired[type[numbaCompilerBase]]
|
|
83
|
+
signature_or_function: NotRequired[Any | Callable[..., Any] | str | tuple[Any, ...]]
|
|
82
84
|
target: NotRequired[str]
|
|
83
85
|
|
|
84
86
|
parametersNumbaDefault: Final[ParametersNumba] = { '_nrt': True, 'boundscheck': False, 'cache': True, 'error_model': 'numpy', 'fastmath': True, 'forceinline': True, 'inline': 'always', 'looplift': False, 'no_cfunc_wrapper': False, 'no_cpython_wrapper': False, 'nopython': True, 'parallel': False }
|
|
@@ -140,7 +142,7 @@ def decorateCallableWithNumba(ingredientsFunction: IngredientsFunction, paramete
|
|
|
140
142
|
(AI generated docstring)
|
|
141
143
|
|
|
142
144
|
This function applies Numba's `@jit` decorator to an existing function definition within
|
|
143
|
-
an `IngredientsFunction` container. It handles the complete transformation
|
|
145
|
+
an `IngredientsFunction` container. It handles the complete transformation assembly line
|
|
144
146
|
including removing any existing decorators that might conflict with Numba, constructing
|
|
145
147
|
type signatures for Numba compilation when possible, applying the `@jit` decorator with
|
|
146
148
|
specified or default parameters, and updating import requirements to include necessary
|
|
@@ -257,10 +259,10 @@ def decorateCallableWithNumba(ingredientsFunction: IngredientsFunction, paramete
|
|
|
257
259
|
|
|
258
260
|
if ingredientsFunction.astFunctionDef.returns and isinstance(ingredientsFunction.astFunctionDef.returns, ast.Name):
|
|
259
261
|
theReturn: ast.Name = ingredientsFunction.astFunctionDef.returns
|
|
260
|
-
list_argsDecorator = [
|
|
261
|
-
, list_arg4signature_or_function if list_arg4signature_or_function else [], [] )
|
|
262
|
+
list_argsDecorator = [Make.Call(Make.Name(theReturn.id)
|
|
263
|
+
, list_arg4signature_or_function if list_arg4signature_or_function else [], [] )]
|
|
262
264
|
elif list_arg4signature_or_function:
|
|
263
|
-
list_argsDecorator = [
|
|
265
|
+
list_argsDecorator = [Make.Tuple(list_arg4signature_or_function)]
|
|
264
266
|
|
|
265
267
|
ingredientsFunction.astFunctionDef = Z0Z_UnhandledDecorators(ingredientsFunction.astFunctionDef)
|
|
266
268
|
if parametersNumba is None:
|
|
@@ -4,7 +4,7 @@ This test suite provides comprehensive validation of map folding computations,
|
|
|
4
4
|
file system operations, OEIS integration, task division, and foundational
|
|
5
5
|
utilities. The tests are designed to support multiple audiences and use cases.
|
|
6
6
|
|
|
7
|
-
Test Module Organization:
|
|
7
|
+
Test Module Organization (in mapFolding/tests/):
|
|
8
8
|
- conftest.py: Testing infrastructure and shared fixtures
|
|
9
9
|
- test_computations.py: Core mathematical validation and algorithm testing
|
|
10
10
|
- test_filesystem.py: File operations and path management
|
|
@@ -25,4 +25,4 @@ ensure consistent error reporting across all tests.
|
|
|
25
25
|
For AI Assistants:
|
|
26
26
|
The testing framework emphasizes readable, predictable patterns that maintain
|
|
27
27
|
mathematical correctness while supporting code evolution and optimization.
|
|
28
|
-
"""
|
|
28
|
+
"""
|