mapFolding 0.12.3__py3-none-any.whl → 0.13.0__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.
@@ -0,0 +1,256 @@
1
+ """codon.
2
+
3
+ https://docs.exaloop.io/start/install/"""
4
+
5
+ from astToolkit import (
6
+ Be, DOT, extractFunctionDef, Grab, hasDOTvalue, identifierDotAttribute, IngredientsFunction, IngredientsModule, Make,
7
+ NodeChanger, NodeTourist, Then)
8
+ from astToolkit.transformationTools import write_astModule
9
+ from hunterMakesPy import autoDecodingRLE, raiseIfNone
10
+ from mapFolding import getPathFilenameFoldsTotal, MapFoldingState, packageSettings
11
+ from mapFolding.someAssemblyRequired import IfThis
12
+ from mapFolding.someAssemblyRequired.RecipeJob import RecipeJobTheorem2
13
+ from mapFolding.syntheticModules.initializeCount import initializeGroupsOfFolds
14
+ from pathlib import Path, PurePosixPath
15
+ from typing import cast, NamedTuple
16
+ import ast
17
+ import subprocess
18
+ import sys
19
+
20
+ class DatatypeConfiguration(NamedTuple):
21
+ """Configuration for mapping framework datatypes to compiled datatypes.
22
+
23
+ This configuration class defines how abstract datatypes used in the map folding framework should be replaced with compiled
24
+ datatypes during code generation. Each configuration specifies the source module, target type name, and optional import
25
+ alias for the transformation.
26
+
27
+ Attributes
28
+ ----------
29
+ datatypeIdentifier : str
30
+ Framework datatype identifier to be replaced.
31
+ typeModule : identifierDotAttribute
32
+ Module containing the target datatype (e.g., 'codon', 'numpy').
33
+ typeIdentifier : str
34
+ Concrete type name in the target module.
35
+ type_asname : str | None = None
36
+ Optional import alias for the type.
37
+ """
38
+
39
+ datatypeIdentifier: str
40
+ typeModule: identifierDotAttribute
41
+ typeIdentifier: str
42
+ type_asname: str | None = None
43
+
44
+ # TODO replace with dynamic system. Probably use `Final` in the dataclass.
45
+ listIdentifiersStaticValuesHARDCODED: list[str] = ['dimensionsTotal', 'leavesTotal']
46
+
47
+ listDatatypeConfigs: list[DatatypeConfiguration] = [
48
+ DatatypeConfiguration(datatypeIdentifier='DatatypeLeavesTotal', typeModule='numpy', typeIdentifier='uint16', type_asname='DatatypeLeavesTotal'),
49
+ DatatypeConfiguration(datatypeIdentifier='DatatypeElephino', typeModule='numpy', typeIdentifier='uint16', type_asname='DatatypeElephino'),
50
+ DatatypeConfiguration(datatypeIdentifier='DatatypeFoldsTotal', typeModule='numpy', typeIdentifier='int64', type_asname='DatatypeFoldsTotal'),
51
+ ]
52
+
53
+ listNumPyTypeConfigs: list[DatatypeConfiguration] = [
54
+ DatatypeConfiguration(datatypeIdentifier='Array1DLeavesTotal', typeModule='numpy', typeIdentifier='uint16', type_asname='Array1DLeavesTotal'),
55
+ DatatypeConfiguration(datatypeIdentifier='Array1DElephino', typeModule='numpy', typeIdentifier='uint16', type_asname='Array1DElephino'),
56
+ DatatypeConfiguration(datatypeIdentifier='Array3D', typeModule='numpy', typeIdentifier='uint16', type_asname='Array3D'),
57
+ ]
58
+
59
+ def _datatypeDefinitions(ingredientsFunction: IngredientsFunction, ingredientsModule: IngredientsModule) -> tuple[IngredientsFunction, IngredientsModule]:
60
+ for datatypeConfig in listDatatypeConfigs:
61
+ ingredientsFunction.imports.removeImportFrom(datatypeConfig.typeModule, None, datatypeConfig.datatypeIdentifier)
62
+ ingredientsFunction.imports.addImportFrom_asStr(datatypeConfig.typeModule, datatypeConfig.typeIdentifier, datatypeConfig.type_asname)
63
+ continue
64
+ ingredientsModule.appendPrologue(statement=Make.Assign([Make.Name(datatypeConfig.datatypeIdentifier, ast.Store())]
65
+ , value=Make.Name(datatypeConfig.typeIdentifier)
66
+ ))
67
+
68
+ for datatypeConfig in listNumPyTypeConfigs:
69
+ ingredientsFunction.imports.removeImportFrom(datatypeConfig.typeModule, None, datatypeConfig.datatypeIdentifier)
70
+ ingredientsFunction.imports.addImportFrom_asStr(datatypeConfig.typeModule, datatypeConfig.typeIdentifier, datatypeConfig.type_asname)
71
+ continue
72
+ ingredientsModule.appendPrologue(statement=Make.Assign([Make.Name(datatypeConfig.datatypeIdentifier, ast.Store())]
73
+ , value=Make.Name(datatypeConfig.typeIdentifier)
74
+ ))
75
+
76
+ ingredientsFunction.imports.removeImportFromModule('mapFolding.dataBaskets')
77
+
78
+ return ingredientsFunction, ingredientsModule
79
+
80
+ def _pythonCode2expr(string: str) -> ast.expr:
81
+ """Convert *one* expression as a string of Python code to an `ast.expr`."""
82
+ return raiseIfNone(NodeTourist(Be.Expr, Then.extractIt(DOT.value)).captureLastMatch(ast.parse(string)))
83
+
84
+ def _variableCompatibility(ingredientsFunction: IngredientsFunction, job: RecipeJobTheorem2) -> IngredientsFunction:
85
+ for ast_arg in job.shatteredDataclass.list_argAnnotated4ArgumentsSpecification:
86
+ identifier = ast_arg.arg
87
+ annotation = raiseIfNone(ast_arg.annotation)
88
+
89
+ # `identifier` in Augmented Assignment.
90
+ NodeChanger(Be.AugAssign.targetIs(IfThis.isNestedNameIdentifier(identifier))
91
+ , doThat=lambda node: Grab.valueAttribute(Then.replaceWith(Make.Call(annotation, listParameters=[node.value])))(node)
92
+ ).visit(ingredientsFunction.astFunctionDef)
93
+
94
+ # `identifier` in Assignments; exclude `numpy.array`.
95
+ NodeChanger(findThis=lambda node: IfThis.isAssignAndTargets0Is(IfThis.isNameIdentifier(identifier))(node)
96
+ and Be.Assign.valueIs(Be.Constant)(node)
97
+ , doThat=lambda node: Grab.valueAttribute(Then.replaceWith(Make.Call(annotation, listParameters=[node.value])))(node)
98
+ ).visit(ingredientsFunction.astFunctionDef)
99
+
100
+ # `identifier` - 1.
101
+ NodeChanger(Be.BinOp.leftIs(IfThis.isNestedNameIdentifier(identifier))
102
+ , doThat=lambda node: Grab.rightAttribute(Then.replaceWith(Make.Call(annotation, listParameters=[node.right])))(node)
103
+ ).visit(ingredientsFunction.astFunctionDef)
104
+
105
+ # `identifier` in Comparison.
106
+ NodeChanger(Be.Compare.leftIs(IfThis.isNestedNameIdentifier(identifier))
107
+ , doThat=lambda node: Grab.comparatorsAttribute(lambda at: Then.replaceWith([Make.Call(annotation, listParameters=[node.comparators[0]])])(at[0]))(node)
108
+ ).visit(ingredientsFunction.astFunctionDef)
109
+
110
+ # `identifier` has exactly one index value.
111
+ NodeChanger(
112
+ findThis=lambda node: Be.Subscript.valueIs(IfThis.isNestedNameIdentifier(identifier))(node)
113
+ and not Be.Subscript.sliceIs(Be.Tuple)(node)
114
+ , doThat=lambda node: Grab.sliceAttribute(Then.replaceWith(Make.Call(Make.Name('int'), listParameters=[node.slice])))(node)
115
+ ).visit(ingredientsFunction.astFunctionDef)
116
+
117
+ # `identifier` has multiple index values.
118
+ NodeChanger(
119
+ findThis=lambda node: Be.Subscript.valueIs(IfThis.isNestedNameIdentifier(identifier))(node)
120
+ and Be.Subscript.sliceIs(Be.Tuple)(node)
121
+ , doThat=lambda node: Grab.sliceAttribute(Grab.eltsAttribute(
122
+ Then.replaceWith([
123
+ Make.Call(Make.Name('int'), listParameters=[cast(ast.Tuple, node.slice).elts[index]])
124
+ for index in range(len(cast(ast.Tuple, node.slice).elts))
125
+ ])
126
+ ))(node)
127
+ ).visit(ingredientsFunction.astFunctionDef)
128
+
129
+ return ingredientsFunction
130
+
131
+ def move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsFunction: IngredientsFunction, job: RecipeJobTheorem2) -> IngredientsFunction:
132
+ """Convert function parameters into initialized variables with concrete values.
133
+
134
+ This function converts function arguments into statically initialized variables in the function body.
135
+
136
+ The function handles different data types (scalars, arrays, custom types) appropriately, replacing abstract parameter
137
+ references with concrete values from the computation state. It also removes unused parameters and variables.
138
+
139
+ Parameters
140
+ ----------
141
+ ingredientsFunction : IngredientsFunction
142
+ The function to transform.
143
+ job : RecipeJobTheorem2
144
+ Recipe containing concrete values for parameters and field metadata.
145
+
146
+ Returns
147
+ -------
148
+ modifiedFunction : IngredientsFunction
149
+ The modified function with parameters converted to initialized variables.
150
+
151
+ """
152
+ ingredientsFunction.imports.update(job.shatteredDataclass.imports)
153
+
154
+ list_argCuzMyBrainRefusesToThink: list[ast.arg] = ingredientsFunction.astFunctionDef.args.args + ingredientsFunction.astFunctionDef.args.posonlyargs + ingredientsFunction.astFunctionDef.args.kwonlyargs
155
+ list_arg_arg: list[str] = [ast_arg.arg for ast_arg in list_argCuzMyBrainRefusesToThink]
156
+ listName: list[ast.Name] = []
157
+ NodeTourist(Be.Name, Then.appendTo(listName)).visit(ingredientsFunction.astFunctionDef)
158
+ listIdentifiers: list[str] = [astName.id for astName in listName]
159
+ listIdentifiersNotUsed: list[str] = list(set(list_arg_arg) - set(listIdentifiers))
160
+
161
+ for ast_arg in list_argCuzMyBrainRefusesToThink:
162
+ if ast_arg.arg in job.shatteredDataclass.field2AnnAssign:
163
+ if ast_arg.arg in listIdentifiersNotUsed:
164
+ pass
165
+ else:
166
+ Ima___Assign, elementConstructor = job.shatteredDataclass.Z0Z_field2AnnAssign[ast_arg.arg]
167
+ match elementConstructor:
168
+ case 'scalar':
169
+ cast('ast.Constant', cast('ast.Call', Ima___Assign.value).args[0]).value = int(job.state.__dict__[ast_arg.arg])
170
+ case 'array':
171
+ dataAsStrRLE: str = autoDecodingRLE(job.state.__dict__[ast_arg.arg], assumeAddSpaces=True)
172
+ dataAs_ast_expr: ast.expr = _pythonCode2expr(dataAsStrRLE)
173
+ cast('ast.Call', Ima___Assign.value).args = [dataAs_ast_expr]
174
+ case _:
175
+ pass
176
+
177
+ ingredientsFunction.astFunctionDef.body.insert(0, Ima___Assign)
178
+
179
+ NodeChanger(IfThis.is_argIdentifier(ast_arg.arg), Then.removeIt).visit(ingredientsFunction.astFunctionDef)
180
+
181
+ ast.fix_missing_locations(ingredientsFunction.astFunctionDef)
182
+ return ingredientsFunction
183
+
184
+ def makeJob(job: RecipeJobTheorem2) -> None:
185
+ """Generate an optimized module for map folding calculations.
186
+
187
+ This function orchestrates the complete code transformation assembly line to convert
188
+ a generic map folding algorithm into a highly optimized, specialized computation
189
+ module.
190
+
191
+ Parameters
192
+ ----------
193
+ job : RecipeJobTheorem2
194
+ Configuration recipe containing source locations, target paths, and state.
195
+
196
+ """
197
+ ingredientsCount: IngredientsFunction = IngredientsFunction(raiseIfNone(extractFunctionDef(job.source_astModule, job.countCallable)))
198
+ ingredientsCount.astFunctionDef.decorator_list = []
199
+
200
+ # Replace identifiers-with-static-values with their values.
201
+ listIdentifiersStaticValues: list[str] = listIdentifiersStaticValuesHARDCODED
202
+ for identifier in listIdentifiersStaticValues:
203
+ NodeChanger(IfThis.isNameIdentifier(identifier)
204
+ , Then.replaceWith(Make.Constant(int(job.state.__dict__[identifier])))
205
+ ).visit(ingredientsCount.astFunctionDef)
206
+
207
+ linesLaunch: str = f"""
208
+ if __name__ == '__main__':
209
+ foldsTotal = {job.countCallable}()
210
+ print('\\nmap {job.state.mapShape} =', foldsTotal)
211
+ writeStream = open('{job.pathFilenameFoldsTotal.as_posix()}', 'w')
212
+ writeStream.write(str(foldsTotal))
213
+ writeStream.close()
214
+ """
215
+ ingredientsModule = IngredientsModule(launcher=ast.parse(linesLaunch))
216
+
217
+ # TODO think about `groupsOfFolds *= DatatypeFoldsTotal({2 * job.state.leavesTotal}); return groupsOfFolds`
218
+ NodeChanger(Be.Return
219
+ , Then.replaceWith(Make.Return(job.shatteredDataclass.countingVariableName))).visit(ingredientsCount.astFunctionDef)
220
+ groupsOfFolds2foldsTotal = NodeChanger[ast.AugAssign, hasDOTvalue](
221
+ findThis=(lambda node: Be.AugAssign.targetIs(IfThis.isNameIdentifier(job.shatteredDataclass.countingVariableName.id))(node)
222
+ and Be.AugAssign.opIs(Be.Mult)(node)
223
+ and Be.AugAssign.valueIs(Be.Constant)(node)
224
+ )
225
+ , doThat=lambda node: Grab.valueAttribute(Then.replaceWith(Make.Constant(job.state.leavesTotal * ast.literal_eval(node.value))))(node)
226
+ )
227
+ groupsOfFolds2foldsTotal.visit(ingredientsCount.astFunctionDef)
228
+
229
+ # TODO think about assigning `returns` here, then removing `returns` a few lines from now.
230
+ ingredientsCount.astFunctionDef.returns = job.shatteredDataclass.countingVariableAnnotation
231
+
232
+ ingredientsCount = move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsCount, job)
233
+
234
+ ingredientsCount, ingredientsModule = _datatypeDefinitions(ingredientsCount, ingredientsModule)
235
+ # NOTE Reminder, `returns = None` means "type information is null"; the identifier for the return was removed by `_datatypeDefinitions`.
236
+ ingredientsCount.astFunctionDef.returns = None
237
+
238
+ ingredientsCount = _variableCompatibility(ingredientsCount, job)
239
+
240
+ ingredientsModule.appendIngredientsFunction(ingredientsCount)
241
+ write_astModule(ingredientsModule, pathFilename=job.pathFilenameModule, packageName=job.packageIdentifier)
242
+
243
+ if sys.platform == 'linux':
244
+ pathFilenameBuild = Path.home() / 'mapFolding' / 'jobs' / job.pathFilenameModule.stem
245
+ pathFilenameBuild.parent.mkdir(parents=True, exist_ok=True)
246
+
247
+ buildCommand = ['codon', 'build', '-release', '-disable-exceptions', '-o', str(pathFilenameBuild), str(job.pathFilenameModule)]
248
+ subprocess.run(buildCommand)
249
+
250
+ if __name__ == '__main__':
251
+ state = initializeGroupsOfFolds(MapFoldingState((2,4)))
252
+ pathModule = PurePosixPath(packageSettings.pathPackage, 'jobs')
253
+ # TODO put `pathFilenameFoldsTotal` in wsl.
254
+ pathFilenameFoldsTotal = PurePosixPath(getPathFilenameFoldsTotal(state.mapShape, pathModule))
255
+ aJob = RecipeJobTheorem2(state, pathModule=pathModule, pathFilenameFoldsTotal=pathFilenameFoldsTotal)
256
+ makeJob(aJob)
@@ -140,7 +140,7 @@ def decorateCallableWithNumba(ingredientsFunction: IngredientsFunction, paramete
140
140
  (AI generated docstring)
141
141
 
142
142
  This function applies Numba's `@jit` decorator to an existing function definition within
143
- an `IngredientsFunction` container. It handles the complete transformation pipeline
143
+ an `IngredientsFunction` container. It handles the complete transformation assembly line
144
144
  including removing any existing decorators that might conflict with Numba, constructing
145
145
  type signatures for Numba compilation when possible, applying the `@jit` decorator with
146
146
  specified or default parameters, and updating import requirements to include necessary
@@ -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
+ """