mapFolding 0.5.1__py3-none-any.whl → 0.7.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 (51) hide show
  1. mapFolding/__init__.py +6 -101
  2. mapFolding/basecamp.py +12 -10
  3. mapFolding/beDRY.py +96 -316
  4. mapFolding/filesystem.py +87 -0
  5. mapFolding/noHomeYet.py +20 -0
  6. mapFolding/oeis.py +39 -36
  7. mapFolding/reference/flattened.py +377 -0
  8. mapFolding/reference/hunterNumba.py +132 -0
  9. mapFolding/reference/irvineJavaPort.py +120 -0
  10. mapFolding/reference/jax.py +208 -0
  11. mapFolding/reference/lunnan.py +153 -0
  12. mapFolding/reference/lunnanNumpy.py +123 -0
  13. mapFolding/reference/lunnanWhile.py +121 -0
  14. mapFolding/reference/rotatedEntryPoint.py +240 -0
  15. mapFolding/reference/total_countPlus1vsPlusN.py +211 -0
  16. mapFolding/someAssemblyRequired/Z0Z_workbench.py +34 -0
  17. mapFolding/someAssemblyRequired/__init__.py +16 -0
  18. mapFolding/someAssemblyRequired/getLLVMforNoReason.py +21 -0
  19. mapFolding/someAssemblyRequired/ingredientsNumba.py +100 -0
  20. mapFolding/someAssemblyRequired/synthesizeCountingFunctions.py +7 -0
  21. mapFolding/someAssemblyRequired/synthesizeDataConverters.py +135 -0
  22. mapFolding/someAssemblyRequired/synthesizeNumba.py +91 -0
  23. mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +417 -0
  24. mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +91 -0
  25. mapFolding/someAssemblyRequired/transformationTools.py +425 -0
  26. mapFolding/someAssemblyRequired/whatWillBe.py +311 -0
  27. mapFolding/syntheticModules/__init__.py +0 -0
  28. mapFolding/syntheticModules/dataNamespaceFlattened.py +30 -0
  29. mapFolding/syntheticModules/numbaCount.py +90 -0
  30. mapFolding/syntheticModules/numbaCountExample.py +158 -0
  31. mapFolding/syntheticModules/numbaCountSequential.py +110 -0
  32. mapFolding/syntheticModules/numbaCount_doTheNeedful.py +13 -0
  33. mapFolding/syntheticModules/numba_doTheNeedful.py +12 -0
  34. mapFolding/syntheticModules/numba_doTheNeedfulExample.py +13 -0
  35. mapFolding/theDao.py +203 -227
  36. mapFolding/theSSOT.py +254 -123
  37. {mapFolding-0.5.1.dist-info → mapfolding-0.7.0.dist-info}/METADATA +10 -8
  38. mapfolding-0.7.0.dist-info/RECORD +50 -0
  39. {mapFolding-0.5.1.dist-info → mapfolding-0.7.0.dist-info}/WHEEL +1 -1
  40. {mapFolding-0.5.1.dist-info → mapfolding-0.7.0.dist-info}/top_level.txt +1 -0
  41. tests/__init__.py +0 -0
  42. tests/conftest.py +278 -0
  43. tests/test_computations.py +49 -0
  44. tests/test_filesystem.py +52 -0
  45. tests/test_oeis.py +128 -0
  46. tests/test_other.py +84 -0
  47. tests/test_tasks.py +50 -0
  48. mapFolding/theSSOTdatatypes.py +0 -156
  49. mapFolding-0.5.1.dist-info/RECORD +0 -14
  50. {mapFolding-0.5.1.dist-info → mapfolding-0.7.0.dist-info}/LICENSE +0 -0
  51. {mapFolding-0.5.1.dist-info → mapfolding-0.7.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,417 @@
1
+ """Synthesize one file to compute `foldsTotal` of `mapShape`."""
2
+ from collections.abc import Sequence
3
+ from typing import Any, cast, TYPE_CHECKING
4
+ from mapFolding.filesystem import getFilenameFoldsTotal, getPathFilenameFoldsTotal
5
+ from mapFolding.someAssemblyRequired import ( ifThis, Make, NodeReplacer, Then, LedgerOfImports, )
6
+ from mapFolding.theSSOT import ( ComputationState, FREAKOUT, getPathJobRootDEFAULT, )
7
+ from os import PathLike
8
+ from pathlib import Path
9
+ from types import ModuleType
10
+ from Z0Z_tools import autoDecodingRLE
11
+ import ast
12
+ import python_minifier
13
+ import autoflake
14
+ import copy
15
+ import inspect
16
+ import numpy
17
+ if TYPE_CHECKING:
18
+ from mapFolding.someAssemblyRequired.synthesizeDataConverters import makeStateJob
19
+ from mapFolding.someAssemblyRequired.synthesizeNumba import thisIsNumbaDotJit, decorateCallableWithNumba
20
+ from mapFolding.someAssemblyRequired.whatWillBe import ParametersNumba, Z0Z_getDatatypeModuleScalar, Z0Z_getDecoratorCallable, Z0Z_setDatatypeModuleScalar, Z0Z_setDecoratorCallable, parametersNumbaDEFAULT
21
+
22
+ def Z0Z_gamma(FunctionDefTarget: ast.FunctionDef, astAssignee: ast.Name, statement: ast.Assign | ast.stmt, identifier: str, arrayTarget: numpy.ndarray[tuple[int, ...], numpy.dtype[numpy.integer[Any]]], allImports: LedgerOfImports) -> tuple[ast.FunctionDef, LedgerOfImports]:
23
+ arrayType = type(arrayTarget)
24
+ moduleConstructor: str = arrayType.__module__
25
+ constructorName: str = arrayType.__name__.replace('ndarray', 'array') # NOTE hack
26
+ dataAsStrRLE: str = autoDecodingRLE(arrayTarget, addSpaces=True)
27
+ dataAs_astExpr: ast.expr = cast(ast.Expr, ast.parse(dataAsStrRLE).body[0]).value
28
+ dtypeName: str = identifier
29
+ dtypeAsName: str = f"{moduleConstructor}_{dtypeName}"
30
+ list_astKeywords: list[ast.keyword] = [ast.keyword(arg='dtype', value=ast.Name(id=dtypeAsName, ctx=ast.Load()))]
31
+ allImports.addImportFromStr(moduleConstructor, dtypeName, dtypeAsName)
32
+ astCall: ast.Call = Make.astCall(Make.astName(constructorName), [dataAs_astExpr], list_astKeywords)
33
+ assignment = ast.Assign(targets=[astAssignee], value=astCall)
34
+ FunctionDefTarget.body.insert(0, assignment)
35
+ FunctionDefTarget.body.remove(statement)
36
+ allImports.addImportFromStr(moduleConstructor, constructorName)
37
+ return FunctionDefTarget, allImports
38
+
39
+ def insertArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arrayTarget: numpy.ndarray[tuple[int, ...], numpy.dtype[numpy.integer[Any]]], allImports: LedgerOfImports, unrollSlices: int | None = None) -> tuple[ast.FunctionDef, LedgerOfImports]:
40
+ def insertAssign(FunctionDefTarget: ast.FunctionDef, assignee: str, arraySlice: numpy.ndarray[tuple[int, ...], numpy.dtype[numpy.integer[Any]]], allImports: LedgerOfImports) -> tuple[ast.FunctionDef, LedgerOfImports]:
41
+ statement = ast.Assign(targets=[ast.Name(id='beans', ctx=ast.Load())], value=ast.Constant(value='and cornbread'))
42
+ FunctionDefTarget.body.insert(0, statement)
43
+ astAssignee = ast.Name(id=assignee, ctx=ast.Store())
44
+ return Z0Z_gamma(FunctionDefTarget, astAssignee, statement, identifier, arraySlice, allImports)
45
+
46
+ if not unrollSlices:
47
+ FunctionDefTarget, allImports = insertAssign(FunctionDefTarget, identifier, arrayTarget, allImports)
48
+ else:
49
+ for index, arraySlice in enumerate(arrayTarget):
50
+ FunctionDefTarget, allImports = insertAssign(FunctionDefTarget, f"{identifier}_{index}", arraySlice, allImports)
51
+
52
+ return FunctionDefTarget, allImports
53
+
54
+ def findAndReplaceTrackArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arrayTarget: numpy.ndarray[tuple[int, ...], numpy.dtype[numpy.integer[Any]]], allImports: LedgerOfImports) -> tuple[ast.FunctionDef, LedgerOfImports]:
55
+ for statement in FunctionDefTarget.body.copy():
56
+ if ifThis.isUnpackingAnArray(identifier)(statement):
57
+ indexAsStr: str = ast.unparse(statement.value.slice) # type: ignore
58
+ arraySlice: numpy.ndarray[Any, numpy.dtype[numpy.integer[Any]]] = arrayTarget[eval(indexAsStr)]
59
+ astAssignee: ast.Name = cast(ast.Name, statement.targets[0]) # type: ignore
60
+ FunctionDefTarget, allImports = Z0Z_gamma(FunctionDefTarget, astAssignee, statement, identifier, arraySlice, allImports)
61
+ return FunctionDefTarget, allImports
62
+
63
+ def findAndReplaceArraySubscriptIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arrayTarget: numpy.ndarray[tuple[int, ...], numpy.dtype[numpy.integer[Any]]], allImports: LedgerOfImports) -> tuple[ast.FunctionDef, LedgerOfImports]:
64
+ # parameter: I define moduleConstructor
65
+ moduleConstructor = Z0Z_getDatatypeModuleScalar()
66
+
67
+ for statement in FunctionDefTarget.body.copy():
68
+ if ifThis.isUnpackingAnArray(identifier)(statement):
69
+ indexAsStr: str = ast.unparse(statement.value.slice) # type: ignore
70
+ arraySlice: numpy.ndarray[Any, numpy.dtype[numpy.integer[Any]]] = arrayTarget[eval(indexAsStr)]
71
+ astAssignee: ast.Name = cast(ast.Name, statement.targets[0]) # type: ignore
72
+ arraySliceItem: int = arraySlice.item()
73
+ constructorName: str = astAssignee.id
74
+ dataAs_astExpr = ast.Constant(value=arraySliceItem)
75
+ list_astKeywords: list[ast.keyword] = []
76
+ astCall: ast.Call = Make.astCall(Make.astName(constructorName), [dataAs_astExpr], list_astKeywords)
77
+ assignment = ast.Assign(targets=[astAssignee], value=astCall)
78
+ FunctionDefTarget.body.insert(0, assignment)
79
+ FunctionDefTarget.body.remove(statement)
80
+ allImports.addImportFromStr(moduleConstructor, constructorName)
81
+ return FunctionDefTarget, allImports
82
+
83
+ def removeAssignmentFrom_body(FunctionDefTarget: ast.FunctionDef, identifier: str) -> ast.FunctionDef:
84
+ FunctionDefSherpa: ast.AST | Sequence[ast.AST] | None = NodeReplacer(ifThis.isAnyAssignmentTo(identifier), Then.removeThis).visit(FunctionDefTarget)
85
+ if not FunctionDefSherpa:
86
+ raise FREAKOUT("Dude, where's my function?")
87
+ else:
88
+ FunctionDefTarget = cast(ast.FunctionDef, FunctionDefSherpa)
89
+ ast.fix_missing_locations(FunctionDefTarget)
90
+ return FunctionDefTarget
91
+
92
+ def findAndReplaceAnnAssignIn_body(FunctionDefTarget: ast.FunctionDef, allImports: LedgerOfImports) -> tuple[ast.FunctionDef, LedgerOfImports]:
93
+ """Unlike most of the other functions, this is generic: it tries to turn an annotation into a construction call."""
94
+ moduleConstructor: str = Z0Z_getDatatypeModuleScalar()
95
+ for stmt in FunctionDefTarget.body.copy():
96
+ if isinstance(stmt, ast.AnnAssign):
97
+ if isinstance(stmt.target, ast.Name) and isinstance(stmt.value, ast.Constant):
98
+ astAssignee: ast.Name = stmt.target
99
+ argData_dtypeName: str = astAssignee.id
100
+ allImports.addImportFromStr(moduleConstructor, argData_dtypeName)
101
+ astCall = ast.Call(func=ast.Name(id=argData_dtypeName, ctx=ast.Load()), args=[stmt.value], keywords=[])
102
+ assignment = ast.Assign(targets=[astAssignee], value=astCall)
103
+ FunctionDefTarget.body.insert(0, assignment)
104
+ FunctionDefTarget.body.remove(stmt)
105
+ return FunctionDefTarget, allImports
106
+
107
+ def findThingyReplaceWithConstantIn_body(FunctionDefTarget: ast.FunctionDef, object: str, value: int) -> ast.FunctionDef:
108
+ """
109
+ Replaces nodes in astFunction matching the AST of the string `object`
110
+ with a constant node holding the provided value.
111
+ """
112
+ targetExpression: ast.expr = ast.parse(object, mode='eval').body
113
+ targetDump: str = ast.dump(targetExpression, annotate_fields=False)
114
+
115
+ def findNode(node: ast.AST) -> bool:
116
+ return ast.dump(node, annotate_fields=False) == targetDump
117
+
118
+ def replaceWithConstant(node: ast.AST) -> ast.AST:
119
+ return ast.copy_location(ast.Constant(value=value), node)
120
+
121
+ transformer = NodeReplacer(findNode, replaceWithConstant)
122
+ newFunction: ast.FunctionDef = cast(ast.FunctionDef, transformer.visit(FunctionDefTarget))
123
+ ast.fix_missing_locations(newFunction)
124
+ return newFunction
125
+
126
+ def findAstNameReplaceWithConstantIn_body(FunctionDefTarget: ast.FunctionDef, name: str, value: int) -> ast.FunctionDef:
127
+ def replaceWithConstant(node: ast.AST) -> ast.AST:
128
+ return ast.copy_location(ast.Constant(value=value), node)
129
+
130
+ return cast(ast.FunctionDef, NodeReplacer(ifThis.isName_Identifier(name), replaceWithConstant).visit(FunctionDefTarget))
131
+
132
+ def insertReturnStatementIn_body(FunctionDefTarget: ast.FunctionDef, arrayTarget: numpy.ndarray[tuple[int, ...], numpy.dtype[numpy.integer[Any]]], allImports: LedgerOfImports) -> tuple[ast.FunctionDef, LedgerOfImports]:
133
+ """Add multiplication and return statement to function, properly constructing AST nodes."""
134
+ # Create AST for multiplication operation
135
+ multiplicand = 'Z0Z_identifierCountFolds'
136
+ multiplyOperation = ast.BinOp(
137
+ left=ast.Name(id=multiplicand, ctx=ast.Load()),
138
+ op=ast.Mult(), right=ast.Constant(value=int(arrayTarget[-1])))
139
+
140
+ returnStatement = ast.Return(value=multiplyOperation)
141
+
142
+ datatype: str = 'Z0Z_identifierCountFolds'
143
+ FunctionDefTarget.returns = ast.Name(id=datatype, ctx=ast.Load())
144
+ datatypeModuleScalar: str = Z0Z_getDatatypeModuleScalar()
145
+ allImports.addImportFromStr(datatypeModuleScalar, datatype)
146
+
147
+ FunctionDefTarget.body.append(returnStatement)
148
+
149
+ return FunctionDefTarget, allImports
150
+
151
+ def findAndReplaceWhileLoopIn_body(FunctionDefTarget: ast.FunctionDef, iteratorName: str, iterationsTotal: int) -> ast.FunctionDef:
152
+ """
153
+ Unroll all nested while loops matching the condition that their test uses `iteratorName`.
154
+ """
155
+ # Helper transformer to replace iterator occurrences with a constant.
156
+ class ReplaceIterator(ast.NodeTransformer):
157
+ def __init__(self, iteratorName: str, constantValue: int) -> None:
158
+ super().__init__()
159
+ self.iteratorName: str = iteratorName
160
+ self.constantValue: int = constantValue
161
+
162
+ def visit_Name(self, node: ast.Name) -> ast.AST:
163
+ if node.id == self.iteratorName:
164
+ return ast.copy_location(ast.Constant(value=self.constantValue), node)
165
+ return self.generic_visit(node)
166
+
167
+ # NodeTransformer that finds while loops (even if deeply nested) and unrolls them.
168
+ class WhileLoopUnroller(ast.NodeTransformer):
169
+ def __init__(self, iteratorName: str, iterationsTotal: int) -> None:
170
+ super().__init__()
171
+ self.iteratorName: str = iteratorName
172
+ self.iterationsTotal: int = iterationsTotal
173
+
174
+ def visit_While(self, node: ast.While) -> list[ast.stmt]:
175
+ # Check if the while loop's test uses the iterator.
176
+ if isinstance(node.test, ast.Compare) and ifThis.isName_Identifier(self.iteratorName)(node.test.left):
177
+ # Recurse the while loop body and remove AugAssign that increments the iterator.
178
+ cleanBodyStatements: list[ast.stmt] = []
179
+ for loopStatement in node.body:
180
+ # Recursively visit nested statements.
181
+ visitedStatement = self.visit(loopStatement)
182
+ # Remove direct AugAssign: iterator += 1.
183
+ if (isinstance(loopStatement, ast.AugAssign) and
184
+ isinstance(loopStatement.target, ast.Name) and
185
+ loopStatement.target.id == self.iteratorName and
186
+ isinstance(loopStatement.op, ast.Add) and
187
+ isinstance(loopStatement.value, ast.Constant) and
188
+ loopStatement.value.value == 1):
189
+ continue
190
+ cleanBodyStatements.append(visitedStatement)
191
+
192
+ newStatements: list[ast.stmt] = []
193
+ # Unroll using the filtered body.
194
+ for iterationIndex in range(self.iterationsTotal):
195
+ for loopStatement in cleanBodyStatements:
196
+ copiedStatement: ast.stmt = copy.deepcopy(loopStatement)
197
+ replacer = ReplaceIterator(self.iteratorName, iterationIndex)
198
+ newStatement = replacer.visit(copiedStatement)
199
+ ast.fix_missing_locations(newStatement)
200
+ newStatements.append(newStatement)
201
+ # Optionally, process the orelse block.
202
+ if node.orelse:
203
+ for elseStmt in node.orelse:
204
+ visitedElse = self.visit(elseStmt)
205
+ if isinstance(visitedElse, list):
206
+ newStatements.extend(cast(list[ast.stmt], visitedElse))
207
+ else:
208
+ newStatements.append(visitedElse)
209
+ return newStatements
210
+ return [cast(ast.stmt, self.generic_visit(node))]
211
+
212
+ newFunctionDef = WhileLoopUnroller(iteratorName, iterationsTotal).visit(FunctionDefTarget)
213
+ ast.fix_missing_locations(newFunctionDef)
214
+ return newFunctionDef
215
+
216
+ def makeLauncherTqdmJobNumba(callableTarget: str, pathFilenameFoldsTotal: Path, totalEstimated: int, leavesTotal:int) -> ast.Module:
217
+ linesLaunch: str = f"""
218
+ if __name__ == '__main__':
219
+ with ProgressBar(total={totalEstimated}, update_interval=2) as statusUpdate:
220
+ {callableTarget}(statusUpdate)
221
+ foldsTotal = statusUpdate.n * {leavesTotal}
222
+ print("", foldsTotal)
223
+ writeStream = open('{pathFilenameFoldsTotal.as_posix()}', 'w')
224
+ writeStream.write(str(foldsTotal))
225
+ writeStream.close()
226
+ """
227
+ return ast.parse(linesLaunch)
228
+
229
+ def makeLauncherBasicJobNumba(callableTarget: str, pathFilenameFoldsTotal: Path) -> ast.Module:
230
+ linesLaunch: str = f"""
231
+ if __name__ == '__main__':
232
+ import time
233
+ timeStart = time.perf_counter()
234
+ foldsTotal = {callableTarget}()
235
+ print(foldsTotal, time.perf_counter() - timeStart)
236
+ writeStream = open('{pathFilenameFoldsTotal.as_posix()}', 'w')
237
+ writeStream.write(str(foldsTotal))
238
+ writeStream.close()
239
+ """
240
+ return ast.parse(linesLaunch)
241
+
242
+ def doUnrollCountGaps(FunctionDefTarget: ast.FunctionDef, stateJob: ComputationState, allImports: LedgerOfImports) -> tuple[ast.FunctionDef, LedgerOfImports]:
243
+ """The initial results were very bad."""
244
+ FunctionDefTarget = findAndReplaceWhileLoopIn_body(FunctionDefTarget, 'indexDimension', stateJob.dimensionsTotal)
245
+ FunctionDefTarget = removeAssignmentFrom_body(FunctionDefTarget, 'indexDimension')
246
+ FunctionDefTarget = removeAssignmentFrom_body(FunctionDefTarget, 'connectionGraph')
247
+ FunctionDefTarget, allImports = insertArrayIn_body(FunctionDefTarget, 'connectionGraph', stateJob.connectionGraph, allImports, stateJob.dimensionsTotal)
248
+ for index in range(stateJob.dimensionsTotal):
249
+ class ReplaceConnectionGraph(ast.NodeTransformer):
250
+ def visit_Subscript(self, node: ast.Subscript) -> ast.AST:
251
+ node = cast(ast.Subscript, self.generic_visit(node))
252
+ if (isinstance(node.value, ast.Name) and node.value.id == "connectionGraph" and
253
+ isinstance(node.slice, ast.Tuple) and len(node.slice.elts) >= 1):
254
+ firstElement: ast.expr = node.slice.elts[0]
255
+ if isinstance(firstElement, ast.Constant) and firstElement.value == index:
256
+ newName = ast.Name(id=f"connectionGraph_{index}", ctx=ast.Load())
257
+ remainingIndices: list[ast.expr] = node.slice.elts[1:]
258
+ if len(remainingIndices) == 1:
259
+ newSlice: ast.expr = remainingIndices[0]
260
+ else:
261
+ newSlice = ast.Tuple(elts=remainingIndices, ctx=ast.Load())
262
+ return ast.copy_location(ast.Subscript(value=newName, slice=newSlice, ctx=node.ctx), node)
263
+ return node
264
+ transformer = ReplaceConnectionGraph()
265
+ FunctionDefTarget = transformer.visit(FunctionDefTarget)
266
+ return FunctionDefTarget, allImports
267
+
268
+ def writeJobNumba(mapShape: Sequence[int], algorithmSource: ModuleType, callableTarget: str | None = None, parametersNumba: ParametersNumba | None = None, pathFilenameWriteJob: str | PathLike[str] | None = None, unrollCountGaps: bool | None = False, Z0Z_totalEstimated: int = 0, **keywordArguments: Any | None) -> Path:
269
+ """ Parameters: **keywordArguments: most especially for `computationDivisions` if you want to make a parallel job. Also `CPUlimit`.
270
+ Notes:
271
+ Hypothetically, everything can now be configured with parameters and functions. And changing how the job is written is relatively easy.
272
+
273
+ Overview
274
+ - the code starts life in theDao.py, which has many optimizations; `makeNumbaOptimizedFlow` increase optimization especially by using numba; `writeJobNumba` increases optimization especially by limiting its capabilities to just one set of parameters
275
+ - the synthesized module must run well as a standalone interpreted-Python script
276
+ - the next major optimization step will (probably) be to use the module synthesized by `writeJobNumba` to compile a standalone executable
277
+ - Nevertheless, at each major optimization step, the code is constantly being improved and optimized, so everything must be well organized and able to handle upstream and downstream changes
278
+
279
+ Minutia
280
+ - perf_counter is for testing. When I run a real job, I delete those lines
281
+ - avoid `with` statement
282
+
283
+ Necessary
284
+ - Move the function's parameters to the function body,
285
+ - initialize identifiers with their state types and values,
286
+
287
+ Optimizations
288
+ - replace static-valued identifiers with their values
289
+ - narrowly focused imports
290
+ """
291
+
292
+ # NOTE get the raw ingredients: data and the algorithm
293
+ stateJob = makeStateJob(mapShape, writeJob=False, **keywordArguments)
294
+ pythonSource: str = inspect.getsource(algorithmSource)
295
+ astModule: ast.Module = ast.parse(pythonSource)
296
+ setFunctionDef: set[ast.FunctionDef] = {statement for statement in astModule.body if isinstance(statement, ast.FunctionDef)}
297
+
298
+ if not callableTarget:
299
+ if len(setFunctionDef) == 1:
300
+ FunctionDefTarget: ast.FunctionDef | None = setFunctionDef.pop()
301
+ callableTarget = FunctionDefTarget.name
302
+ else:
303
+ raise ValueError(f"I did not receive a `callableTarget` and {algorithmSource.__name__=} has more than one callable: {setFunctionDef}. Please select one.")
304
+ else:
305
+ listFunctionDefTarget: list[ast.FunctionDef] = [statement for statement in setFunctionDef if statement.name == callableTarget]
306
+ FunctionDefTarget = listFunctionDefTarget[0] if listFunctionDefTarget else None
307
+ if not FunctionDefTarget: raise ValueError(f"I received `{callableTarget=}` and {algorithmSource.__name__=}, but I could not find that function in that source.")
308
+
309
+ # NOTE `allImports` is a complementary container to `FunctionDefTarget`; the `FunctionDefTarget` cannot track its own imports very well.
310
+ allImports = LedgerOfImports(astModule)
311
+
312
+ # NOTE remove the parameters from the function signature
313
+ for pirateScowl in FunctionDefTarget.args.args.copy():
314
+ match pirateScowl.arg:
315
+ case 'connectionGraph':
316
+ FunctionDefTarget, allImports = insertArrayIn_body(FunctionDefTarget, pirateScowl.arg, stateJob.connectionGraph, allImports)
317
+ case 'gapsWhere':
318
+ FunctionDefTarget, allImports = insertArrayIn_body(FunctionDefTarget, pirateScowl.arg, stateJob.gapsWhere, allImports)
319
+ case 'foldGroups':
320
+ FunctionDefTarget = removeAssignmentFrom_body(FunctionDefTarget, pirateScowl.arg)
321
+ case _:
322
+ pass
323
+ FunctionDefTarget.args.args.remove(pirateScowl)
324
+
325
+ identifierCounter = 'Z0Z_identifierCountFolds'
326
+ astExprIncrementCounter = ast.Expr(value = Make.astCall(Make.nameDOTname(identifierCounter, 'update'), args=[ast.Constant(value=1)], list_astKeywords=[]))
327
+ FunctionDefTarget= cast(ast.FunctionDef, NodeReplacer(ifThis.isAugAssignTo(identifierCounter), Then.replaceWith(astExprIncrementCounter)).visit(FunctionDefTarget))
328
+ ast.fix_missing_locations(FunctionDefTarget)
329
+
330
+ for assignmentTarget in ['taskIndex', 'dimensionsTotal', identifierCounter]:
331
+ FunctionDefTarget = removeAssignmentFrom_body(FunctionDefTarget, assignmentTarget)
332
+ # NOTE replace identifiers with static values with their values
333
+ FunctionDefTarget = findAstNameReplaceWithConstantIn_body(FunctionDefTarget, 'dimensionsTotal', int(stateJob.dimensionsTotal))
334
+ FunctionDefTarget = findThingyReplaceWithConstantIn_body(FunctionDefTarget, 'foldGroups[-1]', int(stateJob.foldGroups[-1]))
335
+
336
+ # NOTE an attempt at optimization
337
+ if unrollCountGaps:
338
+ FunctionDefTarget, allImports = doUnrollCountGaps(FunctionDefTarget, stateJob, allImports)
339
+
340
+ # NOTE starting the count and printing the total
341
+ pathFilenameFoldsTotal: Path = getPathFilenameFoldsTotal(stateJob.mapShape)
342
+
343
+ astLauncher: ast.Module = makeLauncherBasicJobNumba(FunctionDefTarget.name, pathFilenameFoldsTotal)
344
+
345
+ # TODO create function for assigning value to `totalEstimated`
346
+ totalEstimated: int = Z0Z_totalEstimated
347
+ astLauncher: ast.Module = makeLauncherTqdmJobNumba(FunctionDefTarget.name, pathFilenameFoldsTotal, totalEstimated, stateJob.foldGroups[-1])
348
+
349
+ allImports.addImportFromStr('numba_progress', 'ProgressBar')
350
+ allImports.addImportFromStr('numba_progress', 'ProgressBarType')
351
+
352
+ # add ProgressBarType parameter to function args
353
+ counterArg = ast.arg(arg=identifierCounter, annotation=ast.Name(id='ProgressBarType', ctx=ast.Load()))
354
+ FunctionDefTarget.args.args.append(counterArg)
355
+
356
+ if parametersNumba is None:
357
+ parametersNumba = parametersNumbaDEFAULT
358
+ parametersNumba['nogil'] = True
359
+
360
+ FunctionDefTarget, allImports = insertReturnStatementIn_body(FunctionDefTarget, stateJob.foldGroups, allImports)
361
+
362
+ FunctionDefTarget, allImports = findAndReplaceAnnAssignIn_body(FunctionDefTarget, allImports)
363
+ # NOTE add the perfect decorator
364
+ FunctionDefTarget, allImports = decorateCallableWithNumba(FunctionDefTarget, allImports, parametersNumba)
365
+ if thisIsNumbaDotJit(FunctionDefTarget.decorator_list[0]):
366
+ astCall: ast.Call = cast(ast.Call, FunctionDefTarget.decorator_list[0])
367
+ astCall.func = ast.Name(id=Z0Z_getDecoratorCallable(), ctx=ast.Load())
368
+ FunctionDefTarget.decorator_list[0] = astCall
369
+
370
+ # NOTE add imports, make str, remove unused imports
371
+ astImports: list[ast.ImportFrom | ast.Import] = allImports.makeListAst()
372
+ astModule = ast.Module(body=cast(list[ast.stmt], astImports + [FunctionDefTarget] + [astLauncher]), type_ignores=[])
373
+ ast.fix_missing_locations(astModule)
374
+ pythonSource = ast.unparse(astModule)
375
+ pythonSource = autoflake.fix_code(pythonSource, ['mapFolding', 'numba', 'numpy'])
376
+ pythonSource = python_minifier.minify(pythonSource, remove_annotations = False, remove_pass = False, remove_literal_statements = False, combine_imports = True, hoist_literals = False, rename_locals = False, rename_globals = False, remove_object_base = False, convert_posargs_to_args = False, preserve_shebang = True, remove_asserts = False, remove_debug = False, remove_explicit_return_none = False, remove_builtin_exception_brackets = False, constant_folding = False)
377
+
378
+ # NOTE put on disk
379
+ if pathFilenameWriteJob is None:
380
+ filename: str = getFilenameFoldsTotal(stateJob.mapShape)
381
+ pathRoot: Path = getPathJobRootDEFAULT()
382
+ pathFilenameWriteJob = Path(pathRoot, Path(filename).stem, Path(filename).with_suffix('.py'))
383
+ else:
384
+ pathFilenameWriteJob = Path(pathFilenameWriteJob)
385
+ pathFilenameWriteJob.parent.mkdir(parents=True, exist_ok=True)
386
+
387
+ pathFilenameWriteJob.write_text(pythonSource)
388
+
389
+ return pathFilenameWriteJob
390
+
391
+ if __name__ == '__main__':
392
+ mapShape: list[int] = [5,5]
393
+ dictionaryEstimates: dict[tuple[int, ...], int] = {
394
+ (2,2,2,2,2,2,2,2): 362794844160000,
395
+ (2,21): 1493028892051200,
396
+ (3,15): 9842024675968800,
397
+ (3,3,3,3,3): 85109616000000,
398
+ (3,3,3,3): 85109616000,
399
+ (8,8): 129950723279272000,
400
+ }
401
+
402
+ totalEstimated: int = dictionaryEstimates.get(tuple(mapShape), 10**8)
403
+ from mapFolding.syntheticModules import numbaCount
404
+ algorithmSource: ModuleType = numbaCount
405
+
406
+ callableTarget = 'countSequential'
407
+
408
+ parametersNumba: ParametersNumba = parametersNumbaDEFAULT
409
+ parametersNumba['nogil'] = True
410
+ parametersNumba['boundscheck'] = False
411
+
412
+ pathFilenameWriteJob = None
413
+
414
+ Z0Z_setDatatypeModuleScalar('numba')
415
+ Z0Z_setDecoratorCallable('jit')
416
+
417
+ writeJobNumba(mapShape, algorithmSource, callableTarget, parametersNumba, pathFilenameWriteJob, Z0Z_totalEstimated=totalEstimated)
@@ -0,0 +1,91 @@
1
+ # pyright: basic
2
+ from os import PathLike
3
+ from typing import TYPE_CHECKING
4
+ if TYPE_CHECKING:
5
+ from mapFolding.someAssemblyRequired.whatWillBe import ParametersSynthesizeNumbaCallable, listNumbaCallableDispatchees
6
+ from mapFolding.theSSOT import theModuleOfSyntheticModules
7
+ from mapFolding.theSSOT import getSourceAlgorithm
8
+ import types
9
+
10
+ def makeFlowNumbaOptimized(listCallablesInline: list[ParametersSynthesizeNumbaCallable] = listNumbaCallableDispatchees, callableDispatcher: bool = True, algorithmSource: types.ModuleType = getSourceAlgorithm(), relativePathWrite: str | PathLike[str] = theModuleOfSyntheticModules, filenameModuleWrite: str = 'filenameModuleSyntheticWrite', formatFilenameWrite: str = 'formatStrFilenameForCallableSynthetic'):
11
+ from mapFolding.someAssemblyRequired.whatWillBe import ParametersSynthesizeNumbaCallable, listNumbaCallableDispatchees
12
+ from mapFolding.someAssemblyRequired.whatWillBe import LedgerOfImports, Z0Z_autoflake_additional_imports
13
+ from mapFolding.theSSOT import FREAKOUT
14
+ from mapFolding.theSSOT import thePathPackage, getDatatypePackage
15
+ from mapFolding.someAssemblyRequired.whatWillBe import FunctionInliner, YouOughtaKnow, ast_Identifier
16
+ from pathlib import Path
17
+ from typing import cast
18
+ import ast
19
+ import autoflake
20
+ import inspect
21
+ import warnings
22
+ if relativePathWrite and Path(relativePathWrite).is_absolute():
23
+ raise ValueError("The path to write the module must be relative to the root of the package.")
24
+
25
+ listStuffYouOughtaKnow: list[YouOughtaKnow] = []
26
+
27
+ listFunctionDefs: list[ast.FunctionDef] = []
28
+ allImportsModule = LedgerOfImports()
29
+ for tupleParameters in listCallablesInline:
30
+ pythonSource: str = inspect.getsource(algorithmSource)
31
+ astModule: ast.Module = ast.parse(pythonSource)
32
+ if allImports is None:
33
+ allImports = LedgerOfImports(astModule)
34
+ else:
35
+ allImports.walkThis(astModule)
36
+
37
+ if inlineCallables:
38
+ dictionaryFunctionDef: dict[ast_Identifier, ast.FunctionDef] = {statement.name: statement for statement in astModule.body if isinstance(statement, ast.FunctionDef)}
39
+ callableInlinerWorkhorse = FunctionInliner(dictionaryFunctionDef)
40
+ FunctionDefTarget = callableInlinerWorkhorse.inlineFunctionBody(callableTarget)
41
+ else:
42
+ FunctionDefTarget = next((statement for statement in astModule.body if isinstance(statement, ast.FunctionDef) and statement.name == callableTarget), None)
43
+ if not FunctionDefTarget:
44
+ raise ValueError(f"Could not find function {callableTarget} in source code")
45
+
46
+ ast.fix_missing_locations(FunctionDefTarget)
47
+ listFunctionDefs.append(FunctionDefTarget)
48
+ allImportsModule.update(allImports)
49
+
50
+ listAstImports: list[ast.ImportFrom | ast.Import] = allImportsModule.makeListAst()
51
+ additional_imports: list[str] = Z0Z_autoflake_additional_imports
52
+ additional_imports.append(getDatatypePackage())
53
+
54
+ astModule = ast.Module(body=cast(list[ast.stmt], listAstImports + listFunctionDefs), type_ignores=[])
55
+ ast.fix_missing_locations(astModule)
56
+ pythonSource: str = ast.unparse(astModule)
57
+ if not pythonSource: raise FREAKOUT
58
+ pythonSource = autoflake.fix_code(pythonSource, additional_imports)
59
+
60
+ pathWrite: Path = thePathPackage / relativePathWrite
61
+
62
+ if not filenameWrite:
63
+ if len(listCallableSynthesized) == 1:
64
+ callableTarget: str = listCallableSynthesized[0].callableTarget
65
+ else:
66
+ callableTarget = filenameWriteCallableTargetDEFAULT
67
+ # NOTE WARNING I think I broken this format string. See theSSOT.py
68
+ filenameWrite = formatFilenameWrite.format(callableTarget=callableTarget)
69
+ else:
70
+ if not filenameWrite.endswith('.py'):
71
+ warnings.warn(f"Filename {filenameWrite=} does not end with '.py'.")
72
+
73
+ pathFilename: Path = pathWrite / filenameWrite
74
+
75
+ pathFilename.write_text(pythonSource)
76
+
77
+ howIsThisStillAThing: Path = thePathPackage.parent
78
+ dumbassPythonNamespace: tuple[str, ...] = pathFilename.relative_to(howIsThisStillAThing).with_suffix('').parts
79
+ ImaModule: str = '.'.join(dumbassPythonNamespace)
80
+
81
+ for item in listCallableSynthesized:
82
+ callableTarget: str = item.callableTarget
83
+ astImportFrom = ast.ImportFrom(module=ImaModule, names=[ast.alias(name=callableTarget, asname=None)], level=0)
84
+ stuff = YouOughtaKnow(callableSynthesized=callableTarget, pathFilenameForMe=pathFilename, astForCompetentProgrammers=astImportFrom)
85
+ listStuffYouOughtaKnow.append(stuff)
86
+ listStuffYouOughtaKnow.extend(listStuff)
87
+
88
+ if callableDispatcher:
89
+ pass
90
+
91
+ return listStuffYouOughtaKnow