mapFolding 0.8.6__py3-none-any.whl → 0.9.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. mapFolding/__init__.py +60 -13
  2. mapFolding/basecamp.py +32 -17
  3. mapFolding/beDRY.py +4 -5
  4. mapFolding/oeis.py +94 -7
  5. mapFolding/someAssemblyRequired/RecipeJob.py +103 -0
  6. mapFolding/someAssemblyRequired/__init__.py +71 -50
  7. mapFolding/someAssemblyRequired/_theTypes.py +11 -15
  8. mapFolding/someAssemblyRequired/_tool_Make.py +36 -9
  9. mapFolding/someAssemblyRequired/_tool_Then.py +59 -25
  10. mapFolding/someAssemblyRequired/_toolboxAntecedents.py +159 -272
  11. mapFolding/someAssemblyRequired/_toolboxContainers.py +155 -70
  12. mapFolding/someAssemblyRequired/_toolboxPython.py +168 -44
  13. mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +154 -39
  14. mapFolding/someAssemblyRequired/toolboxNumba.py +72 -230
  15. mapFolding/someAssemblyRequired/transformationTools.py +370 -141
  16. mapFolding/syntheticModules/{numbaCount_doTheNeedful.py → numbaCount.py} +7 -4
  17. mapFolding/theDao.py +19 -16
  18. mapFolding/theSSOT.py +165 -62
  19. mapFolding/toolboxFilesystem.py +1 -1
  20. mapfolding-0.9.1.dist-info/METADATA +177 -0
  21. mapfolding-0.9.1.dist-info/RECORD +47 -0
  22. tests/__init__.py +44 -0
  23. tests/conftest.py +75 -7
  24. tests/test_computations.py +92 -10
  25. tests/test_filesystem.py +32 -33
  26. tests/test_other.py +0 -1
  27. tests/test_tasks.py +1 -1
  28. mapFolding/someAssemblyRequired/newInliner.py +0 -22
  29. mapfolding-0.8.6.dist-info/METADATA +0 -190
  30. mapfolding-0.8.6.dist-info/RECORD +0 -47
  31. {mapfolding-0.8.6.dist-info → mapfolding-0.9.1.dist-info}/WHEEL +0 -0
  32. {mapfolding-0.8.6.dist-info → mapfolding-0.9.1.dist-info}/entry_points.txt +0 -0
  33. {mapfolding-0.8.6.dist-info → mapfolding-0.9.1.dist-info}/licenses/LICENSE +0 -0
  34. {mapfolding-0.8.6.dist-info → mapfolding-0.9.1.dist-info}/top_level.txt +0 -0
@@ -1,15 +1,45 @@
1
- """Synthesize one file to compute `foldsTotal` of `mapShape`."""
1
+ """
2
+ Job-specific Numba Code Generation for Map Folding Calculations
3
+
4
+ This module specializes in generating highly-optimized, single-purpose Numba modules
5
+ for specific map folding calculation jobs. Unlike the general-purpose transformation
6
+ in toolboxNumba.py, this module creates standalone Python modules optimized for a
7
+ single map shape with statically-encoded parameters.
8
+
9
+ The code generation pipeline focuses on:
10
+
11
+ 1. Converting function parameters to initialized variables with concrete values.
12
+ 2. Replacing dynamic computations with statically-known values.
13
+ 3. Eliminating unused code paths and variables.
14
+ 4. Adding progress tracking for long-running calculations.
15
+ 5. Applying appropriate Numba optimizations for the specific calculation.
16
+
17
+ This creates extremely fast, specialized implementations that can be run directly
18
+ as Python scripts or further compiled into standalone executables.
19
+ """
20
+
2
21
  from mapFolding.toolboxFilesystem import getPathFilenameFoldsTotal
3
- from mapFolding.someAssemblyRequired import ast_Identifier, be, ifThis, Make, NodeChanger, Then, IngredientsFunction, IngredientsModule, LedgerOfImports
4
- from mapFolding.someAssemblyRequired.toolboxNumba import RecipeJob, SpicesJobNumba, decorateCallableWithNumba
5
- from mapFolding.someAssemblyRequired.transformationTools import astModuleToIngredientsFunction, extractFunctionDef, write_astModule
6
- from mapFolding.someAssemblyRequired.transformationTools import makeInitializedComputationState
7
- from mapFolding.theSSOT import The, raiseIfNoneGitHubIssueNumber3
8
- from mapFolding.oeis import getFoldsTotalKnown
22
+ from mapFolding.someAssemblyRequired import (
23
+ ast_Identifier,
24
+ be,
25
+ ifThis,
26
+ IngredientsFunction,
27
+ IngredientsModule,
28
+ LedgerOfImports,
29
+ Make,
30
+ NodeChanger,
31
+ NodeTourist,
32
+ Then,
33
+ )
34
+ from mapFolding.someAssemblyRequired.toolboxNumba import parametersNumbaLight, SpicesJobNumba, decorateCallableWithNumba
35
+ from mapFolding.someAssemblyRequired.transformationTools import extractFunctionDef, write_astModule, makeInitializedComputationState
36
+ from mapFolding.someAssemblyRequired.RecipeJob import RecipeJob
37
+ from mapFolding import The, raiseIfNoneGitHubIssueNumber3, getFoldsTotalKnown
9
38
  from typing import cast
10
39
  from Z0Z_tools import autoDecodingRLE
11
40
  from pathlib import PurePosixPath
12
41
  import ast
42
+ """Synthesize one file to compute `foldsTotal` of `mapShape`."""
13
43
 
14
44
  list_IdentifiersNotUsedAllHARDCODED = ['concurrencyLimit', 'foldsTotal', 'mapShape',]
15
45
  list_IdentifiersNotUsedParallelSequentialHARDCODED = ['indexLeaf']
@@ -22,7 +52,30 @@ list_IdentifiersStaticValuesHARDCODED = ['dimensionsTotal', 'leavesTotal',]
22
52
  list_IdentifiersNotUsedHARDCODED = list_IdentifiersStaticValuesHARDCODED + list_IdentifiersReplacedHARDCODED + list_IdentifiersNotUsedAllHARDCODED + list_IdentifiersNotUsedParallelSequentialHARDCODED + list_IdentifiersNotUsedSequentialHARDCODED
23
53
 
24
54
  def addLauncherNumbaProgress(ingredientsModule: IngredientsModule, ingredientsFunction: IngredientsFunction, job: RecipeJob, spices: SpicesJobNumba) -> tuple[IngredientsModule, IngredientsFunction]:
55
+ """
56
+ Add progress tracking capabilities to a Numba-optimized function.
25
57
 
58
+ This function modifies both the module and the function to integrate Numba-compatible
59
+ progress tracking for long-running calculations. It performs several key transformations:
60
+
61
+ 1. Adds a progress bar parameter to the function signature
62
+ 2. Replaces counting increments with progress bar updates
63
+ 3. Creates a launcher section that displays and updates progress
64
+ 4. Configures file output to save results upon completion
65
+
66
+ The progress tracking is particularly important for map folding calculations
67
+ which can take hours or days to complete, providing visual feedback and
68
+ estimated completion times.
69
+
70
+ Parameters:
71
+ ingredientsModule: The module where the function is defined.
72
+ ingredientsFunction: The function to modify with progress tracking.
73
+ job: Configuration specifying shape details and output paths.
74
+ spices: Configuration specifying progress bar details.
75
+
76
+ Returns:
77
+ A tuple containing the modified module and function with progress tracking.
78
+ """
26
79
  linesLaunch: str = f"""
27
80
  if __name__ == '__main__':
28
81
  with ProgressBar(total={job.foldsTotalEstimated}, update_interval=2) as statusUpdate:
@@ -43,20 +96,51 @@ if __name__ == '__main__':
43
96
 
44
97
  findThis = ifThis.isAugAssign_targetIs(ifThis.isName_Identifier(job.shatteredDataclass.countingVariableName.id))
45
98
  doThat = Then.replaceWith(Make.Expr(Make.Call(Make.Attribute(Make.Name(spices.numbaProgressBarIdentifier),'update'),[Make.Constant(1)])))
46
- countWithProgressBar = NodeChanger(findThis, doThat) # type: ignore
99
+ countWithProgressBar = NodeChanger(findThis, doThat)
47
100
  countWithProgressBar.visit(ingredientsFunction.astFunctionDef)
48
101
 
102
+ removeReturnStatement = NodeChanger(be.Return, Then.removeIt)
103
+ removeReturnStatement.visit(ingredientsFunction.astFunctionDef)
104
+ ingredientsFunction.astFunctionDef.returns = Make.Constant(value=None)
105
+
49
106
  ingredientsModule.appendLauncher(ast.parse(linesLaunch))
50
107
 
51
108
  return ingredientsModule, ingredientsFunction
52
109
 
53
110
  def move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsFunction: IngredientsFunction, job: RecipeJob) -> IngredientsFunction:
111
+ """
112
+ Convert function parameters into initialized variables with concrete values.
113
+
114
+ This function implements a critical transformation that converts function parameters
115
+ into statically initialized variables in the function body. This enables several
116
+ optimizations:
117
+
118
+ 1. Eliminating parameter passing overhead.
119
+ 2. Embedding concrete values directly in the code.
120
+ 3. Allowing Numba to optimize based on known value characteristics.
121
+ 4. Simplifying function signatures for specialized use cases.
122
+
123
+ The function handles different data types (scalars, arrays, custom types) appropriately,
124
+ replacing abstract parameter references with concrete values from the computation state.
125
+ It also removes unused parameters and variables to eliminate dead code.
126
+
127
+ Parameters:
128
+ ingredientsFunction: The function to transform.
129
+ job: Recipe containing concrete values for parameters and field metadata.
130
+
131
+ Returns:
132
+ The modified function with parameters converted to initialized variables.
133
+ """
54
134
  ingredientsFunction.imports.update(job.shatteredDataclass.ledger)
55
135
 
56
- list_IdentifiersNotUsed = list_IdentifiersNotUsedHARDCODED
136
+ list_argCuzMyBrainRefusesToThink = ingredientsFunction.astFunctionDef.args.args + ingredientsFunction.astFunctionDef.args.posonlyargs + ingredientsFunction.astFunctionDef.args.kwonlyargs
137
+ list_arg_arg: list[ast_Identifier] = [ast_arg.arg for ast_arg in list_argCuzMyBrainRefusesToThink]
138
+ listName: list[ast.Name] = []
139
+ NodeTourist(be.Name, Then.appendTo(listName)).visit(ingredientsFunction.astFunctionDef)
140
+ list_Identifiers: list[ast_Identifier] = [astName.id for astName in listName]
141
+ list_IdentifiersNotUsed: list[ast_Identifier] = list(set(list_arg_arg) - set(list_Identifiers))
57
142
 
58
- list_argCauseMyBrainRefusesToDoThisTheRightWay = ingredientsFunction.astFunctionDef.args.args + ingredientsFunction.astFunctionDef.args.posonlyargs + ingredientsFunction.astFunctionDef.args.kwonlyargs
59
- for ast_arg in list_argCauseMyBrainRefusesToDoThisTheRightWay:
143
+ for ast_arg in list_argCuzMyBrainRefusesToThink:
60
144
  if ast_arg.arg in job.shatteredDataclass.field2AnnAssign:
61
145
  if ast_arg.arg in list_IdentifiersNotUsed:
62
146
  pass
@@ -64,11 +148,11 @@ def move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsFunction: Ingre
64
148
  ImaAnnAssign, elementConstructor = job.shatteredDataclass.Z0Z_field2AnnAssign[ast_arg.arg]
65
149
  match elementConstructor:
66
150
  case 'scalar':
67
- ImaAnnAssign.value.args[0].value = int(job.state.__dict__[ast_arg.arg]) # type: ignore
151
+ ImaAnnAssign.value.args[0].value = int(job.state.__dict__[ast_arg.arg]) # type: ignore
68
152
  case 'array':
69
153
  dataAsStrRLE: str = autoDecodingRLE(job.state.__dict__[ast_arg.arg], addSpaces=True)
70
154
  dataAs_astExpr: ast.expr = cast(ast.Expr, ast.parse(dataAsStrRLE).body[0]).value
71
- ImaAnnAssign.value.args = [dataAs_astExpr] # type: ignore
155
+ ImaAnnAssign.value.args = [dataAs_astExpr] # type: ignore
72
156
  case _:
73
157
  list_exprDOTannotation: list[ast.expr] = []
74
158
  list_exprDOTvalue: list[ast.expr] = []
@@ -87,17 +171,34 @@ def move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsFunction: Ingre
87
171
  ast.fix_missing_locations(ingredientsFunction.astFunctionDef)
88
172
  return ingredientsFunction
89
173
 
90
- def makeJobNumba(job: RecipeJob, spices: SpicesJobNumba):
91
- # get the raw ingredients: data and the algorithm
174
+ def makeJobNumba(job: RecipeJob, spices: SpicesJobNumba) -> None:
175
+ """
176
+ Generate a highly-optimized, single-purpose Numba module for a specific map shape.
177
+
178
+ This function implements the complete transformation pipeline for creating a
179
+ standalone, specialized implementation for calculating map folding solutions for
180
+ a specific shape. The process includes:
181
+
182
+ 1. Extracting the counting function from the source module
183
+ 2. Removing unused code paths based on static analysis
184
+ 3. Replacing dynamic variables with concrete values
185
+ 4. Converting parameters to initialized variables
186
+ 5. Adding progress tracking if requested
187
+ 6. Applying Numba optimizations and type specifications
188
+ 7. Writing the final module to the filesystem
189
+
190
+ The resulting Python module is both human-readable and extraordinarily efficient,
191
+ with all shape-specific optimizations statically encoded. This creates specialized
192
+ implementations that can be orders of magnitude faster than general-purpose code.
193
+
194
+ Parameters:
195
+ job: Configuration specifying the target shape, paths, and computation state.
196
+ spices: Configuration specifying Numba and progress tracking options.
197
+ """
92
198
  astFunctionDef = extractFunctionDef(job.source_astModule, job.countCallable)
93
199
  if not astFunctionDef: raise raiseIfNoneGitHubIssueNumber3
94
200
  ingredientsCount: IngredientsFunction = IngredientsFunction(astFunctionDef, LedgerOfImports())
95
201
 
96
- # Change the return so you can dynamically determine which variables are not used
97
- removeReturnStatement = NodeChanger(be.Return, Then.removeIt)
98
- removeReturnStatement.visit(ingredientsCount.astFunctionDef)
99
- ingredientsCount.astFunctionDef.returns = Make.Constant(value=None)
100
-
101
202
  # Remove `foldGroups` and any other unused statements, so you can dynamically determine which variables are not used
102
203
  findThis = ifThis.isAssignAndTargets0Is(ifThis.isSubscript_Identifier('foldGroups'))
103
204
  doThat = Then.removeIt
@@ -109,32 +210,50 @@ def makeJobNumba(job: RecipeJob, spices: SpicesJobNumba):
109
210
  for identifier in list_IdentifiersStaticValues:
110
211
  findThis = ifThis.isName_Identifier(identifier)
111
212
  doThat = Then.replaceWith(Make.Constant(int(job.state.__dict__[identifier])))
112
- NodeChanger(findThis, doThat).visit(ingredientsCount.astFunctionDef) # type: ignore
213
+ NodeChanger(findThis, doThat).visit(ingredientsCount.astFunctionDef)
113
214
 
114
- # This launcher eliminates the use of one identifier, so run it now and you can dynamically determine which variables are not used
115
215
  ingredientsModule = IngredientsModule()
216
+ # This launcher eliminates the use of one identifier, so run it now and you can dynamically determine which variables are not used
116
217
  if spices.useNumbaProgressBar:
117
218
  ingredientsModule, ingredientsCount = addLauncherNumbaProgress(ingredientsModule, ingredientsCount, job, spices)
118
219
  spices.parametersNumba['nogil'] = True
220
+ else:
221
+ linesLaunch: str = f"""
222
+ if __name__ == '__main__':
223
+ import time
224
+ timeStart = time.perf_counter()
225
+ foldsTotal = {job.countCallable}() * {job.state.leavesTotal}
226
+ print(time.perf_counter() - timeStart)
227
+ print('\\nmap {job.state.mapShape} =', foldsTotal)
228
+ writeStream = open('{job.pathFilenameFoldsTotal.as_posix()}', 'w')
229
+ writeStream.write(str(foldsTotal))
230
+ writeStream.close()
231
+ from mapFolding.oeis import getFoldsTotalKnown
232
+ print(foldsTotal == getFoldsTotalKnown({job.state.mapShape}))
233
+ """
234
+ ingredientsModule.appendLauncher(ast.parse(linesLaunch))
235
+ changeReturnParallelCallable = NodeChanger(be.Return, Then.replaceWith(Make.Return(job.shatteredDataclass.countingVariableName)))
236
+ changeReturnParallelCallable.visit(ingredientsCount.astFunctionDef)
237
+ ingredientsCount.astFunctionDef.returns = job.shatteredDataclass.countingVariableAnnotation
119
238
 
120
239
  ingredientsCount = move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsCount, job)
121
240
 
122
241
  Z0Z_Identifier = 'DatatypeLeavesTotal'
123
242
  Z0Z_type = 'uint8'
124
243
  ingredientsModule.imports.addImportFrom_asStr('numba', Z0Z_type)
125
- Z0Z_module = 'typing'
126
- Z0Z_annotation = 'TypeAlias'
127
- ingredientsModule.imports.addImportFrom_asStr(Z0Z_module, Z0Z_annotation)
128
- Z0Z_statement = Make.AnnAssign(Make.Name(Z0Z_Identifier, ast.Store()), Make.Name(Z0Z_annotation), Make.Name(Z0Z_type))
244
+ Z0Z_statement = Make.Assign([Make.Name(Z0Z_Identifier, ast.Store())], Make.Name(Z0Z_type))
129
245
  ingredientsModule.appendPrologue(statement=Z0Z_statement)
130
246
 
131
247
  Z0Z_Identifier = 'DatatypeElephino'
132
- Z0Z_type = 'int16'
248
+ Z0Z_type = 'uint8'
133
249
  ingredientsModule.imports.addImportFrom_asStr('numba', Z0Z_type)
134
- Z0Z_module = 'typing'
135
- Z0Z_annotation = 'TypeAlias'
136
- ingredientsModule.imports.addImportFrom_asStr(Z0Z_module, Z0Z_annotation)
137
- Z0Z_statement = Make.AnnAssign(Make.Name(Z0Z_Identifier, ast.Store()), Make.Name(Z0Z_annotation), Make.Name(Z0Z_type))
250
+ Z0Z_statement = Make.Assign([Make.Name(Z0Z_Identifier, ast.Store())], Make.Name(Z0Z_type))
251
+ ingredientsModule.appendPrologue(statement=Z0Z_statement)
252
+
253
+ Z0Z_Identifier = 'DatatypeFoldsTotal'
254
+ Z0Z_type = 'int64'
255
+ ingredientsModule.imports.addImportFrom_asStr('numba', Z0Z_type)
256
+ Z0Z_statement = Make.Assign([Make.Name(Z0Z_Identifier, ast.Store())], Make.Name(Z0Z_type))
138
257
  ingredientsModule.appendPrologue(statement=Z0Z_statement)
139
258
 
140
259
  ingredientsCount.imports.removeImportFromModule('mapFolding.theSSOT')
@@ -145,23 +264,18 @@ def makeJobNumba(job: RecipeJob, spices: SpicesJobNumba):
145
264
  ingredientsCount.imports.addImportFrom_asStr(Z0Z_module, Z0Z_type_name, Z0Z_asname)
146
265
  Z0Z_asname = 'Array1DElephino'
147
266
  ingredientsCount.imports.removeImportFrom(Z0Z_module, None, Z0Z_asname)
148
- Z0Z_type_name = 'int16'
267
+ Z0Z_type_name = 'uint8'
149
268
  ingredientsCount.imports.addImportFrom_asStr(Z0Z_module, Z0Z_type_name, Z0Z_asname)
150
269
  Z0Z_asname = 'Array3D'
151
270
  ingredientsCount.imports.removeImportFrom(Z0Z_module, None, Z0Z_asname)
152
271
  Z0Z_type_name = 'uint8'
153
272
  ingredientsCount.imports.addImportFrom_asStr(Z0Z_module, Z0Z_type_name, Z0Z_asname)
154
273
 
155
- from numpy import int16 as Array1DLeavesTotal, int16 as Array1DElephino, int16 as Array3D
156
-
157
274
  ingredientsCount.astFunctionDef.decorator_list = [] # TODO low-priority, handle this more elegantly
158
275
  # TODO when I add the function signature in numba style back to the decorator, the logic needs to handle `ProgressBarType:`
159
276
  ingredientsCount = decorateCallableWithNumba(ingredientsCount, spices.parametersNumba)
160
277
 
161
278
  ingredientsModule.appendIngredientsFunction(ingredientsCount)
162
-
163
- # add imports, make str, remove unused imports
164
- # put on disk
165
279
  write_astModule(ingredientsModule, job.pathFilenameModule, job.packageIdentifier)
166
280
 
167
281
  """
@@ -186,11 +300,12 @@ def makeJobNumba(job: RecipeJob, spices: SpicesJobNumba):
186
300
  """
187
301
 
188
302
  if __name__ == '__main__':
189
- mapShape = (6,6)
303
+ mapShape = (1,24)
190
304
  state = makeInitializedComputationState(mapShape)
191
305
  foldsTotalEstimated = getFoldsTotalKnown(state.mapShape) // state.leavesTotal
192
306
  pathModule = PurePosixPath(The.pathPackage, 'jobs')
193
307
  pathFilenameFoldsTotal = PurePosixPath(getPathFilenameFoldsTotal(state.mapShape, pathModule))
194
308
  aJob = RecipeJob(state, foldsTotalEstimated, pathModule=pathModule, pathFilenameFoldsTotal=pathFilenameFoldsTotal)
195
- spices = SpicesJobNumba()
309
+ spices = SpicesJobNumba(useNumbaProgressBar=False, parametersNumba=parametersNumbaLight)
310
+ # spices = SpicesJobNumba()
196
311
  makeJobNumba(aJob, spices)