mapFolding 0.12.2__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.
Files changed (33) hide show
  1. mapFolding/__init__.py +4 -2
  2. mapFolding/_theSSOT.py +32 -88
  3. mapFolding/{datatypes.py → _theTypes.py} +25 -3
  4. mapFolding/basecamp.py +38 -33
  5. mapFolding/beDRY.py +79 -54
  6. mapFolding/dataBaskets.py +123 -93
  7. mapFolding/filesystemToolkit.py +140 -91
  8. mapFolding/oeis.py +243 -145
  9. mapFolding/reference/flattened.py +1 -1
  10. mapFolding/someAssemblyRequired/RecipeJob.py +116 -100
  11. mapFolding/someAssemblyRequired/__init__.py +40 -15
  12. mapFolding/someAssemblyRequired/_toolIfThis.py +82 -54
  13. mapFolding/someAssemblyRequired/_toolkitContainers.py +19 -16
  14. mapFolding/someAssemblyRequired/getLLVMforNoReason.py +35 -26
  15. mapFolding/someAssemblyRequired/makeAllModules.py +353 -283
  16. mapFolding/someAssemblyRequired/makeJobTheorem2Numba.py +83 -84
  17. mapFolding/someAssemblyRequired/makeJobTheorem2codon.py +256 -0
  18. mapFolding/someAssemblyRequired/toolkitNumba.py +80 -50
  19. mapFolding/someAssemblyRequired/transformationTools.py +63 -40
  20. {tests → mapFolding/tests}/__init__.py +2 -2
  21. {tests → mapFolding/tests}/conftest.py +232 -63
  22. {tests → mapFolding/tests}/test_computations.py +58 -18
  23. {tests → mapFolding/tests}/test_filesystem.py +10 -13
  24. {tests → mapFolding/tests}/test_oeis.py +5 -18
  25. {tests → mapFolding/tests}/test_other.py +9 -9
  26. {tests → mapFolding/tests}/test_tasks.py +7 -9
  27. {mapfolding-0.12.2.dist-info → mapfolding-0.13.0.dist-info}/METADATA +24 -37
  28. mapfolding-0.13.0.dist-info/RECORD +54 -0
  29. {mapfolding-0.12.2.dist-info → mapfolding-0.13.0.dist-info}/top_level.txt +0 -1
  30. mapfolding-0.12.2.dist-info/RECORD +0 -53
  31. {mapfolding-0.12.2.dist-info → mapfolding-0.13.0.dist-info}/WHEEL +0 -0
  32. {mapfolding-0.12.2.dist-info → mapfolding-0.13.0.dist-info}/entry_points.txt +0 -0
  33. {mapfolding-0.12.2.dist-info → mapfolding-0.13.0.dist-info}/licenses/LICENSE +0 -0
@@ -29,44 +29,30 @@ essential progress feedback capabilities for large-scale computational research.
29
29
  """
30
30
 
31
31
  from astToolkit import (
32
- Be, ClassIsAndAttribute, extractFunctionDef, identifierDotAttribute, IngredientsFunction,
33
- IngredientsModule, LedgerOfImports, Make, NodeChanger, NodeTourist, Then,
34
- )
32
+ Be, extractFunctionDef, identifierDotAttribute, IngredientsFunction, IngredientsModule, LedgerOfImports, Make,
33
+ NodeChanger, NodeTourist, Then)
35
34
  from astToolkit.transformationTools import write_astModule
35
+ from hunterMakesPy import autoDecodingRLE, raiseIfNone
36
36
  from mapFolding import getPathFilenameFoldsTotal, MapFoldingState, packageSettings
37
37
  from mapFolding.someAssemblyRequired import IfThis
38
- from mapFolding.someAssemblyRequired.RecipeJob import RecipeJobTheorem2Numba
39
- from mapFolding.someAssemblyRequired.toolkitNumba import (
40
- decorateCallableWithNumba, parametersNumbaLight, SpicesJobNumba,
41
- )
38
+ from mapFolding.someAssemblyRequired.RecipeJob import RecipeJobTheorem2
39
+ from mapFolding.someAssemblyRequired.toolkitNumba import decorateCallableWithNumba, parametersNumbaLight, SpicesJobNumba
42
40
  from mapFolding.syntheticModules.initializeCount import initializeGroupsOfFolds
43
41
  from pathlib import PurePosixPath
44
- from typing import cast, NamedTuple
45
- from Z0Z_tools import autoDecodingRLE, raiseIfNone
42
+ from typing import cast, NamedTuple, TYPE_CHECKING
43
+ from typing_extensions import TypeIs
46
44
  import ast
47
45
 
48
- # Configuration lists for code optimization and dead code elimination
49
- listIdentifiersNotUsedAllHARDCODED: list[str] = ['concurrencyLimit', 'foldsTotal', 'mapShape',]
50
- """Identifiers that are universally unused across all optimization contexts."""
46
+ if TYPE_CHECKING:
47
+ from collections.abc import Callable
51
48
 
52
- listIdentifiersNotUsedParallelSequentialHARDCODED: list[str] = ['indexLeaf']
53
- """Identifiers unused in both parallel and sequential execution modes."""
49
+ listIdentifiersStaticValuesHARDCODED: list[str] = ['dimensionsTotal', 'leavesTotal']
54
50
 
55
- listIdentifiersNotUsedSequentialHARDCODED: list[str] = ['foldGroups', 'taskDivisions', 'taskIndex',]
56
- """Identifiers unused specifically in sequential execution mode."""
57
-
58
- listIdentifiersReplacedHARDCODED: list[str] = ['groupsOfFolds',]
59
- """Identifiers that get replaced with optimized equivalents during transformation."""
60
-
61
- listIdentifiersStaticValuesHARDCODED: list[str] = ['dimensionsTotal', 'leavesTotal',]
62
- """Identifiers with compile-time constant values that can be embedded directly."""
63
-
64
- listIdentifiersNotUsedHARDCODED: list[str] = listIdentifiersStaticValuesHARDCODED + listIdentifiersReplacedHARDCODED + listIdentifiersNotUsedAllHARDCODED + listIdentifiersNotUsedParallelSequentialHARDCODED + listIdentifiersNotUsedSequentialHARDCODED
65
- """Complete list of all identifiers that can be eliminated during optimization."""
66
-
67
- def addLauncherNumbaProgress(ingredientsModule: IngredientsModule, ingredientsFunction: IngredientsFunction, job: RecipeJobTheorem2Numba, spices: SpicesJobNumba) -> tuple[IngredientsModule, IngredientsFunction]:
51
+ def addLauncherNumbaProgress(ingredientsModule: IngredientsModule, ingredientsFunction: IngredientsFunction, job: RecipeJobTheorem2, spices: SpicesJobNumba) -> tuple[IngredientsModule, IngredientsFunction]:
68
52
  """Add progress tracking capabilities to a Numba-optimized function.
69
53
 
54
+ (AI generated docstring)
55
+
70
56
  This function modifies both the module and the function to integrate Numba-compatible
71
57
  progress tracking for long-running calculations. It performs several key transformations:
72
58
 
@@ -79,16 +65,23 @@ def addLauncherNumbaProgress(ingredientsModule: IngredientsModule, ingredientsFu
79
65
  which can take hours or days to complete, providing visual feedback and
80
66
  estimated completion times.
81
67
 
82
- Parameters:
83
- ingredientsModule: The module where the function is defined.
84
- ingredientsFunction: The function to modify with progress tracking.
85
- job: Configuration specifying shape details and output paths.
86
- spices: Configuration specifying progress bar details.
87
-
88
- Returns:
68
+ Parameters
69
+ ----------
70
+ ingredientsModule : IngredientsModule
71
+ The module where the function is defined.
72
+ ingredientsFunction : IngredientsFunction
73
+ The function to modify with progress tracking.
74
+ job : RecipeJobTheorem2Numba
75
+ Configuration specifying shape details and output paths.
76
+ spices : SpicesJobNumba
77
+ Configuration specifying progress bar details.
78
+
79
+ Returns
80
+ -------
81
+ moduleAndFunction : tuple[IngredientsModule, IngredientsFunction]
89
82
  Modified module and function with integrated progress tracking capabilities.
90
- """
91
83
 
84
+ """
92
85
  linesLaunch: str = f"""
93
86
  if __name__ == '__main__':
94
87
  with ProgressBar(total={job.foldsTotalEstimated}, update_interval=2) as statusUpdate:
@@ -107,9 +100,9 @@ if __name__ == '__main__':
107
100
  ast_argNumbaProgress = ast.arg(arg=spices.numbaProgressBarIdentifier, annotation=ast.Name(id=numba_progressPythonClass, ctx=ast.Load()))
108
101
  ingredientsFunction.astFunctionDef.args.args.append(ast_argNumbaProgress)
109
102
 
110
- findThis = ClassIsAndAttribute.targetIs(ast.AugAssign, IfThis.isNameIdentifier(job.shatteredDataclass.countingVariableName.id))
111
- doThat = Then.replaceWith(Make.Expr(Make.Call(Make.Attribute(Make.Name(spices.numbaProgressBarIdentifier),'update'),[Make.Constant(1)])))
112
- countWithProgressBar = NodeChanger(findThis, doThat)
103
+ findThis: Callable[[ast.AST], TypeIs[ast.AugAssign] | bool] = Be.AugAssign.targetIs(IfThis.isNameIdentifier(job.shatteredDataclass.countingVariableName.id))
104
+ doThat: Callable[[ast.AugAssign], ast.Expr] = Then.replaceWith(Make.Expr(Make.Call(Make.Attribute(Make.Name(spices.numbaProgressBarIdentifier),'update'),[Make.Constant(1)])))
105
+ countWithProgressBar: NodeChanger[ast.AugAssign, ast.Expr] = NodeChanger(findThis, doThat)
113
106
  countWithProgressBar.visit(ingredientsFunction.astFunctionDef)
114
107
 
115
108
  removeReturnStatement = NodeChanger(Be.Return, Then.removeIt)
@@ -120,9 +113,11 @@ if __name__ == '__main__':
120
113
 
121
114
  return ingredientsModule, ingredientsFunction
122
115
 
123
- def move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsFunction: IngredientsFunction, job: RecipeJobTheorem2Numba) -> IngredientsFunction:
116
+ def move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsFunction: IngredientsFunction, job: RecipeJobTheorem2) -> IngredientsFunction:
124
117
  """Convert function parameters into initialized variables with concrete values.
125
118
 
119
+ (AI generated docstring)
120
+
126
121
  This function implements a critical transformation that converts function parameters
127
122
  into statically initialized variables in the function body. This enables several
128
123
  optimizations:
@@ -136,12 +131,18 @@ def move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsFunction: Ingre
136
131
  replacing abstract parameter references with concrete values from the computation state.
137
132
  It also removes unused parameters and variables to eliminate dead code.
138
133
 
139
- Parameters:
140
- ingredientsFunction: The function to transform.
141
- job: Recipe containing concrete values for parameters and field metadata.
134
+ Parameters
135
+ ----------
136
+ ingredientsFunction : IngredientsFunction
137
+ The function to transform.
138
+ job : RecipeJobTheorem2Numba
139
+ Recipe containing concrete values for parameters and field metadata.
142
140
 
143
- Returns:
141
+ Returns
142
+ -------
143
+ modifiedFunction : IngredientsFunction
144
144
  The modified function with parameters converted to initialized variables.
145
+
145
146
  """
146
147
  ingredientsFunction.imports.update(job.shatteredDataclass.imports)
147
148
 
@@ -160,33 +161,35 @@ def move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsFunction: Ingre
160
161
  ImaAnnAssign, elementConstructor = job.shatteredDataclass.Z0Z_field2AnnAssign[ast_arg.arg]
161
162
  match elementConstructor:
162
163
  case 'scalar':
163
- cast(ast.Constant, cast(ast.Call, ImaAnnAssign.value).args[0]).value = int(job.state.__dict__[ast_arg.arg])
164
+ cast('ast.Constant', cast('ast.Call', ImaAnnAssign.value).args[0]).value = int(job.state.__dict__[ast_arg.arg])
164
165
  case 'array':
165
- dataAsStrRLE: str = autoDecodingRLE(job.state.__dict__[ast_arg.arg], True)
166
- dataAs_astExpr: ast.expr = cast(ast.Expr, ast.parse(dataAsStrRLE).body[0]).value
167
- cast(ast.Call, ImaAnnAssign.value).args = [dataAs_astExpr]
166
+ dataAsStrRLE: str = autoDecodingRLE(job.state.__dict__[ast_arg.arg], assumeAddSpaces=True)
167
+ dataAs_astExpr: ast.expr = cast('ast.Expr', ast.parse(dataAsStrRLE).body[0]).value
168
+ cast('ast.Call', ImaAnnAssign.value).args = [dataAs_astExpr]
168
169
  case _:
169
170
  list_exprDOTannotation: list[ast.expr] = []
170
171
  list_exprDOTvalue: list[ast.expr] = []
171
172
  for dimension in job.state.mapShape:
172
173
  list_exprDOTannotation.append(Make.Name(elementConstructor))
173
174
  list_exprDOTvalue.append(Make.Call(Make.Name(elementConstructor), [Make.Constant(dimension)]))
174
- cast(ast.Tuple, cast(ast.Subscript, cast(ast.AnnAssign, ImaAnnAssign).annotation).slice).elts = list_exprDOTannotation
175
- cast(ast.Tuple, ImaAnnAssign.value).elts = list_exprDOTvalue
175
+ cast('ast.Tuple', cast('ast.Subscript', cast('ast.AnnAssign', ImaAnnAssign).annotation).slice).elts = list_exprDOTannotation
176
+ cast('ast.Tuple', ImaAnnAssign.value).elts = list_exprDOTvalue
176
177
 
177
178
  ingredientsFunction.astFunctionDef.body.insert(0, ImaAnnAssign)
178
179
 
179
- findThis = IfThis.is_argIdentifier(ast_arg.arg)
180
- remove_arg = NodeChanger(findThis, Then.removeIt)
180
+ findThis: Callable[[ast.AST], TypeIs[ast.arg] | bool] = IfThis.is_argIdentifier(ast_arg.arg)
181
+ remove_arg: NodeChanger[ast.arg, None] = NodeChanger(findThis, Then.removeIt)
181
182
  remove_arg.visit(ingredientsFunction.astFunctionDef)
182
183
 
183
184
  ast.fix_missing_locations(ingredientsFunction.astFunctionDef)
184
185
  return ingredientsFunction
185
186
 
186
- def makeJobNumba(job: RecipeJobTheorem2Numba, spices: SpicesJobNumba) -> None:
187
+ def makeJobNumba(job: RecipeJobTheorem2, spices: SpicesJobNumba) -> None:
187
188
  """Generate an optimized Numba-compiled computation module for map folding calculations.
188
189
 
189
- This function orchestrates the complete code transformation pipeline to convert
190
+ (AI generated docstring)
191
+
192
+ This function orchestrates the complete code transformation assembly line to convert
190
193
  a generic map folding algorithm into a highly optimized, specialized computation
191
194
  module. The transformation process includes:
192
195
 
@@ -203,26 +206,21 @@ def makeJobNumba(job: RecipeJobTheorem2Numba, spices: SpicesJobNumba) -> None:
203
206
  map folding calculations for the specific map dimensions with maximum
204
207
  performance through just-in-time compilation.
205
208
 
206
- Parameters:
207
- job: Configuration recipe containing source locations, target paths, and state.
208
- spices: Optimization settings including Numba parameters and progress options.
209
- """
209
+ Parameters
210
+ ----------
211
+ job : RecipeJobTheorem2Numba
212
+ Configuration recipe containing source locations, target paths, and state.
213
+ spices : SpicesJobNumba
214
+ Optimization settings including Numba parameters and progress options.
210
215
 
216
+ """
211
217
  astFunctionDef: ast.FunctionDef = raiseIfNone(extractFunctionDef(job.source_astModule, job.countCallable))
212
218
  ingredientsCount: IngredientsFunction = IngredientsFunction(astFunctionDef, LedgerOfImports())
213
219
 
214
- # Remove `foldGroups` and any other unused statements, so you can dynamically determine which variables are not used
215
- findThis = ClassIsAndAttribute.targetsIs(ast.Assign, lambda list_expr: any([IfThis.isSubscriptIdentifier('foldGroups')(node) for node in list_expr ]))
216
- # findThis = IfThis.isAssignAndTargets0Is(IfThis.isSubscriptIdentifier('foldGroups'))
217
- doThat = Then.removeIt
218
- remove_foldGroups = NodeChanger(findThis, doThat)
219
- # remove_foldGroups.visit(ingredientsCount.astFunctionDef)
220
-
221
- # replace identifiers with static values with their values, so you can dynamically determine which variables are not used
222
220
  listIdentifiersStaticValues: list[str] = listIdentifiersStaticValuesHARDCODED
223
221
  for identifier in listIdentifiersStaticValues:
224
- findThis = IfThis.isNameIdentifier(identifier)
225
- doThat = Then.replaceWith(Make.Constant(int(job.state.__dict__[identifier])))
222
+ findThis: Callable[[ast.AST], TypeIs[ast.Name] | bool] = IfThis.isNameIdentifier(identifier)
223
+ doThat: Callable[[ast.Name], ast.Constant] = Then.replaceWith(Make.Constant(int(job.state.__dict__[identifier])))
226
224
  NodeChanger(findThis, doThat).visit(ingredientsCount.astFunctionDef)
227
225
 
228
226
  ingredientsModule = IngredientsModule()
@@ -242,8 +240,8 @@ if __name__ == '__main__':
242
240
  writeStream.write(str(foldsTotal))
243
241
  writeStream.close()
244
242
  """
245
- # from mapFolding.oeis import getFoldsTotalKnown
246
- # print(foldsTotal == getFoldsTotalKnown({job.state.mapShape}))
243
+ # from mapFolding.oeis import getFoldsTotalKnown # noqa: ERA001
244
+ # print(foldsTotal == getFoldsTotalKnown({job.state.mapShape})) # noqa: ERA001
247
245
  ingredientsModule.appendLauncher(ast.parse(linesLaunch))
248
246
  changeReturnParallelCallable = NodeChanger(Be.Return, Then.replaceWith(Make.Return(job.shatteredDataclass.countingVariableName)))
249
247
  changeReturnParallelCallable.visit(ingredientsCount.astFunctionDef)
@@ -258,12 +256,18 @@ if __name__ == '__main__':
258
256
  generation. Each configuration specifies the source module, target type name,
259
257
  and optional import alias for the transformation.
260
258
 
261
- Attributes:
262
- fml: Framework datatype identifier to be replaced.
263
- Z0Z_module: Module containing the target datatype (e.g., 'numba', 'numpy').
264
- Z0Z_type_name: Concrete type name in the target module.
265
- Z0Z_asname: Optional import alias for the type.
259
+ Attributes
260
+ ----------
261
+ fml : str
262
+ Framework datatype identifier to be replaced.
263
+ Z0Z_module : identifierDotAttribute
264
+ Module containing the target datatype (e.g., 'numba', 'numpy').
265
+ Z0Z_type_name : str
266
+ Concrete type name in the target module.
267
+ Z0Z_asname : str | None = None
268
+ Optional import alias for the type.
266
269
  """
270
+
267
271
  fml: str
268
272
  Z0Z_module: identifierDotAttribute
269
273
  Z0Z_type_name: str
@@ -283,7 +287,7 @@ if __name__ == '__main__':
283
287
  )
284
288
  ingredientsModule.appendPrologue(statement=statement)
285
289
 
286
- ingredientsCount.imports.removeImportFromModule('mapFolding.theSSOT')
290
+ ingredientsCount.imports.removeImportFromModule('mapFolding.dataBaskets')
287
291
 
288
292
  listNumPyTypeConfigs = [
289
293
  DatatypeConfig(fml='Array1DLeavesTotal', Z0Z_module='numpy', Z0Z_type_name='uint8', Z0Z_asname='Array1DLeavesTotal'),
@@ -296,7 +300,6 @@ if __name__ == '__main__':
296
300
  ingredientsCount.imports.addImportFrom_asStr(typeConfig.Z0Z_module, typeConfig.Z0Z_type_name, typeConfig.Z0Z_asname)
297
301
 
298
302
  ingredientsCount.astFunctionDef.decorator_list = [] # TODO low-priority, handle this more elegantly
299
- # TODO when I add the function signature in numba style back to the decorator, the logic needs to handle `ProgressBarType:`
300
303
  ingredientsCount = decorateCallableWithNumba(ingredientsCount, spices.parametersNumba)
301
304
  ingredientsModule.appendIngredientsFunction(ingredientsCount)
302
305
  write_astModule(ingredientsModule, job.pathFilenameModule, job.packageIdentifier)
@@ -323,15 +326,11 @@ if __name__ == '__main__':
323
326
  """
324
327
 
325
328
  if __name__ == '__main__':
326
- mapShape = (2,4)
327
- state = MapFoldingState(mapShape)
328
- state = initializeGroupsOfFolds(state)
329
- # foldsTotalEstimated = getFoldsTotalKnown(state.mapShape) // state.leavesTotal
330
- # foldsTotalEstimated = dictionaryEstimates[state.mapShape] // state.leavesTotal
331
- foldsTotalEstimated = 0
329
+ state = initializeGroupsOfFolds(MapFoldingState((2,4)))
332
330
  pathModule = PurePosixPath(packageSettings.pathPackage, 'jobs')
333
331
  pathFilenameFoldsTotal = PurePosixPath(getPathFilenameFoldsTotal(state.mapShape, pathModule))
334
- aJob = RecipeJobTheorem2Numba(state, foldsTotalEstimated, pathModule=pathModule, pathFilenameFoldsTotal=pathFilenameFoldsTotal)
332
+ aJob = RecipeJobTheorem2(state, pathModule=pathModule, pathFilenameFoldsTotal=pathFilenameFoldsTotal)
335
333
  spices = SpicesJobNumba(useNumbaProgressBar=False, parametersNumba=parametersNumbaLight)
336
- # spices = SpicesJobNumba()
337
334
  makeJobNumba(aJob, spices)
335
+
336
+ # TODO Improve this module with lessons learned in `makeJobTheorem2codon`.
@@ -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)