mapFolding 0.5.0__py3-none-any.whl → 0.6.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 (42) hide show
  1. mapFolding/__init__.py +96 -58
  2. mapFolding/basecamp.py +5 -7
  3. mapFolding/beDRY.py +11 -41
  4. mapFolding/oeis.py +71 -74
  5. mapFolding/theConfiguration.py +58 -0
  6. mapFolding/theDao.py +1 -1
  7. mapFolding/theSSOT.py +14 -48
  8. mapFolding/theSSOTdatatypes.py +25 -36
  9. mapFolding/theWrongWay.py +7 -0
  10. {mapFolding-0.5.0.dist-info → mapfolding-0.6.0.dist-info}/METADATA +6 -4
  11. mapfolding-0.6.0.dist-info/RECORD +16 -0
  12. {mapFolding-0.5.0.dist-info → mapfolding-0.6.0.dist-info}/WHEEL +1 -1
  13. {mapFolding-0.5.0.dist-info → mapfolding-0.6.0.dist-info}/top_level.txt +0 -1
  14. mapFolding/reference/flattened.py +0 -377
  15. mapFolding/reference/hunterNumba.py +0 -132
  16. mapFolding/reference/irvineJavaPort.py +0 -120
  17. mapFolding/reference/jax.py +0 -208
  18. mapFolding/reference/lunnan.py +0 -153
  19. mapFolding/reference/lunnanNumpy.py +0 -123
  20. mapFolding/reference/lunnanWhile.py +0 -121
  21. mapFolding/reference/rotatedEntryPoint.py +0 -240
  22. mapFolding/reference/total_countPlus1vsPlusN.py +0 -211
  23. mapFolding/someAssemblyRequired/__init__.py +0 -5
  24. mapFolding/someAssemblyRequired/getLLVMforNoReason.py +0 -19
  25. mapFolding/someAssemblyRequired/makeJob.py +0 -56
  26. mapFolding/someAssemblyRequired/synthesizeModuleJAX.py +0 -27
  27. mapFolding/someAssemblyRequired/synthesizeNumba.py +0 -345
  28. mapFolding/someAssemblyRequired/synthesizeNumbaGeneralized.py +0 -397
  29. mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +0 -155
  30. mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +0 -123
  31. mapFolding/syntheticModules/numbaCount.py +0 -158
  32. mapFolding/syntheticModules/numba_doTheNeedful.py +0 -13
  33. mapFolding-0.5.0.dist-info/RECORD +0 -39
  34. tests/__init__.py +0 -1
  35. tests/conftest.py +0 -335
  36. tests/test_computations.py +0 -42
  37. tests/test_oeis.py +0 -128
  38. tests/test_other.py +0 -175
  39. tests/test_tasks.py +0 -40
  40. /mapFolding/{syntheticModules/__init__.py → py.typed} +0 -0
  41. {mapFolding-0.5.0.dist-info → mapfolding-0.6.0.dist-info}/LICENSE +0 -0
  42. {mapFolding-0.5.0.dist-info → mapfolding-0.6.0.dist-info}/entry_points.txt +0 -0
@@ -1,397 +0,0 @@
1
- from mapFolding import (
2
- computationState,
3
- EnumIndices,
4
- formatFilenameModuleDEFAULT,
5
- FREAKOUT,
6
- getAlgorithmDispatcher,
7
- getAlgorithmSource,
8
- getFilenameFoldsTotal,
9
- getPathFilenameFoldsTotal,
10
- getPathJobRootDEFAULT,
11
- getPathPackage,
12
- getPathSyntheticModules,
13
- hackSSOTdatatype,
14
- indexMy,
15
- indexTrack,
16
- moduleOfSyntheticModules,
17
- myPackageNameIs,
18
- ParametersNumba,
19
- parametersNumbaDEFAULT,
20
- parametersNumbaFailEarly,
21
- parametersNumbaMinimum,
22
- parametersNumbaSuperJit,
23
- parametersNumbaSuperJitParallel,
24
- setDatatypeElephino,
25
- setDatatypeFoldsTotal,
26
- setDatatypeLeavesTotal,
27
- setDatatypeModule,
28
- Z0Z_getDatatypeModuleScalar,
29
- Z0Z_getDecoratorCallable,
30
- Z0Z_identifierCountFolds,
31
- Z0Z_setDatatypeModuleScalar,
32
- Z0Z_setDecoratorCallable,
33
- )
34
- from mapFolding.someAssemblyRequired.makeJob import makeStateJob
35
- from numpy import integer
36
- from numpy.typing import NDArray
37
- from types import ModuleType
38
- from collections.abc import Callable, Sequence
39
- from typing import Any, cast
40
- from Z0Z_tools import autoDecodingRLE, updateExtendPolishDictionaryLists
41
- import ast
42
- import autoflake
43
- import collections
44
- import copy
45
- import importlib.util
46
- import inspect
47
- import more_itertools
48
- import numba
49
- import numpy
50
- from os import PathLike
51
- from pathlib import Path
52
- import python_minifier
53
- import warnings
54
-
55
- youOughtaKnow = collections.namedtuple('youOughtaKnow', ['callableSynthesized', 'pathFilenameForMe', 'astForCompetentProgrammers'])
56
-
57
- # idk how to use this
58
- class ASTBodyTransformer:
59
- """
60
- A helper class to apply multiple transformations on an AST FunctionDef's body.
61
- This abstraction eliminates the need to write repetitive loops for removals,
62
- replacements, or insertions.
63
- """
64
- def __init__(self, functionDefinition: ast.FunctionDef) -> None:
65
- self.functionDefinition = functionDefinition
66
-
67
- def replaceIn_body(self, predicate: Callable[[ast.stmt], bool], replacementBuilder: Callable[[ast.stmt], ast.stmt | None]) -> None:
68
- newBody: list[ast.stmt] = []
69
- for statement in self.functionDefinition.body:
70
- if predicate(statement):
71
- replacementStatement = replacementBuilder(statement)
72
- if replacementStatement is not None:
73
- newBody.append(replacementStatement)
74
- else:
75
- newBody.append(statement)
76
- self.functionDefinition.body = newBody
77
-
78
- def atIndexInsert(self, index: int, statement: ast.stmt) -> None:
79
- self.functionDefinition.body.insert(index, statement)
80
-
81
- def removeAllOf(self, predicate: Callable[[ast.stmt], bool]) -> None:
82
- self.replaceIn_body(predicate, lambda stmt: None)
83
-
84
- def Z0Z_apply(self) -> ast.FunctionDef:
85
- ast.fix_missing_locations(self.functionDefinition)
86
- return self.functionDefinition
87
-
88
- # Generic
89
- class ifThis:
90
- """Generic AST node predicate builder."""
91
- @staticmethod
92
- def nameIs(allegedly: str) -> Callable[[ast.AST], bool]:
93
- return lambda node: (isinstance(node, ast.Name) and node.id == allegedly)
94
-
95
- @staticmethod
96
- def isCallWithAttribute(moduleName: str, callableName: str) -> Callable[[ast.AST], bool]:
97
- return lambda node: (isinstance(node, ast.Call)
98
- and isinstance(node.func, ast.Attribute)
99
- and isinstance(node.func.value, ast.Name)
100
- and node.func.value.id == moduleName
101
- and node.func.attr == callableName)
102
-
103
- @staticmethod
104
- def isCallWithName(callableName: str) -> Callable[[ast.AST], bool]:
105
- return lambda node: (isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id == callableName)
106
-
107
- @staticmethod
108
- def isAssignTarget(identifier: str):
109
- return lambda node: (isinstance(node, ast.Assign)
110
- and node.targets
111
- and isinstance(node.targets[0], ast.Name)
112
- and node.targets[0].id == identifier)
113
-
114
- @staticmethod
115
- def anyOf(*predicates: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
116
- return lambda node: any(pred(node) for pred in predicates)
117
-
118
- @staticmethod
119
- def isUnpackingAnArray(identifier:str):
120
- return lambda node: (isinstance(node, ast.Assign)
121
- and isinstance(node.targets[0], ast.Name)
122
- and isinstance(node.value, ast.Subscript)
123
- and isinstance(node.value.value, ast.Name)
124
- and node.value.value.id == identifier
125
- and isinstance(node.value.slice, ast.Attribute)
126
- )
127
-
128
- class Then:
129
- """Generic actions."""
130
- @staticmethod
131
- def copy_astCallKeywords(astCall: ast.Call) -> dict[str, Any]:
132
- """Extract keyword parameters from a decorator AST node."""
133
- dictionaryKeywords: dict[str, Any] = {}
134
- for keywordItem in astCall.keywords:
135
- if isinstance(keywordItem.value, ast.Constant) and keywordItem.arg is not None:
136
- dictionaryKeywords[keywordItem.arg] = keywordItem.value.value
137
- return dictionaryKeywords
138
-
139
- @staticmethod
140
- def make_astCall(name: str, args: Sequence[ast.expr] | None = None, list_astKeywords: Sequence[ast.keyword] | None = None, dictionaryKeywords: dict[str, Any] | None = None) -> ast.Call:
141
- list_dictionaryKeywords = [ast.keyword(arg=keyName, value=ast.Constant(value=keyValue)) for keyName, keyValue in dictionaryKeywords.items()] if dictionaryKeywords else []
142
- return ast.Call(
143
- func=ast.Name(id=name, ctx=ast.Load()),
144
- args=list(args) if args else [],
145
- keywords=list_dictionaryKeywords + list(list_astKeywords) if list_astKeywords else [],
146
- )
147
-
148
- class NodeReplacer(ast.NodeTransformer):
149
- """
150
- A node transformer that replaces or removes AST nodes based on a condition.
151
- This transformer traverses an AST and for each node checks a predicate. If the predicate
152
- returns True, the transformer uses the replacement builder to obtain a new node. Returning
153
- None from the replacement builder indicates that the node should be removed.
154
-
155
- Attributes:
156
- findMe: A function that determines whether a node should be replaced.
157
- nodeReplacementBuilder: A function that returns a new node or None to remove the node.
158
-
159
- Methods:
160
- visit(node: ast.AST) -> Optional[ast.AST]:
161
- Visits each node in the AST, replacing or removing it based on the predicate.
162
- """
163
- def __init__(self, findMe: Callable[[ast.AST], bool], nodeReplacementBuilder: Callable[[ast.AST], ast.AST | None]) -> None:
164
- self.findMe = findMe
165
- self.nodeReplacementBuilder = nodeReplacementBuilder
166
-
167
- def visit(self, node: ast.AST) -> ast.AST | None | Any:
168
- if self.findMe(node):
169
- return self.nodeReplacementBuilder(node)
170
- return super().visit(node)
171
-
172
- # Confusing: suspiciously specific but still reusable
173
- def thisIsNumbaDotJit(Ima: ast.AST) -> bool:
174
- return ifThis.isCallWithAttribute(Z0Z_getDatatypeModuleScalar(), Z0Z_getDecoratorCallable())(Ima)
175
-
176
- def thisIsJit(Ima: ast.AST) -> bool:
177
- return ifThis.isCallWithName(Z0Z_getDecoratorCallable())(Ima)
178
-
179
- def thisIsAnyNumbaJitDecorator(Ima: ast.AST) -> bool:
180
- return thisIsNumbaDotJit(Ima) or thisIsJit(Ima)
181
-
182
- # Domain-based
183
- class UniversalImportTracker:
184
- def __init__(self) -> None:
185
- self.dictionaryImportFrom: dict[str, set] = collections.defaultdict(set)
186
- self.setImport = set()
187
-
188
- def addAst(self, astImport_: ast.Import | ast.ImportFrom) -> None:
189
- if isinstance(astImport_, ast.Import):
190
- for alias in astImport_.names:
191
- self.setImport.add(alias.name)
192
- elif isinstance(astImport_, ast.ImportFrom):
193
- if astImport_.module is not None:
194
- self.dictionaryImportFrom[astImport_.module].update((alias.name, alias.asname) for alias in astImport_.names)
195
-
196
- def addImportStr(self, module: str) -> None:
197
- self.setImport.add(module)
198
-
199
- def addImportFromStr(self, module: str, name: str, asname: str | None = None) -> None:
200
- self.dictionaryImportFrom[module].add((name, asname))
201
-
202
- def makeListAst(self) -> list[ast.ImportFrom | ast.Import]:
203
- listAstImportFrom = []
204
- for module, setOfNameTuples in sorted(self.dictionaryImportFrom.items()):
205
- listAliases = []
206
- for name, asname in setOfNameTuples:
207
- listAliases.append(ast.alias(name=name, asname=asname))
208
- listAstImportFrom.append(ast.ImportFrom(module=module, names=listAliases, level=0))
209
-
210
- listAstImport = [ast.Import(names=[ast.alias(name=name, asname=None)]) for name in sorted(self.setImport)]
211
- return listAstImportFrom + listAstImport
212
-
213
- def update(self, *fromTracker: 'UniversalImportTracker') -> None:
214
- """
215
- Update this tracker with imports from one or more other trackers.
216
-
217
- Parameters:
218
- *fromTracker: One or more UniversalImportTracker objects to merge from.
219
- """
220
- # Merge all import-from dictionaries
221
- dictionaryMerged = updateExtendPolishDictionaryLists(self.dictionaryImportFrom, *(tracker.dictionaryImportFrom for tracker in fromTracker), destroyDuplicates=True, reorderLists=True)
222
-
223
- # Convert lists back to sets for each module's imports
224
- self.dictionaryImportFrom = {module: set(listNames) for module, listNames in dictionaryMerged.items()}
225
-
226
- # Update direct imports
227
- for tracker in fromTracker:
228
- self.setImport.update(tracker.setImport)
229
-
230
- # Intricate and specialized
231
- class RecursiveInliner(ast.NodeTransformer):
232
- """
233
- Class RecursiveInliner:
234
- A custom AST NodeTransformer designed to recursively inline function calls from a given dictionary
235
- of function definitions into the AST. Once a particular function has been inlined, it is marked
236
- as completed to avoid repeated inlining. This transformation modifies the AST in-place by substituting
237
- eligible function calls with the body of their corresponding function.
238
- Attributes:
239
- dictionaryFunctions (Dict[str, ast.FunctionDef]):
240
- A mapping of function name to its AST definition, used as a source for inlining.
241
- callablesCompleted (Set[str]):
242
- A set to track function names that have already been inlined to prevent multiple expansions.
243
- Methods:
244
- inlineFunctionBody(callableTargetName: str) -> Optional[ast.FunctionDef]:
245
- Retrieves the AST definition for a given function name from dictionaryFunctions
246
- and recursively inlines any function calls within it. Returns the function definition
247
- that was inlined or None if the function was already processed.
248
- visit_Call(callNode: ast.Call) -> ast.AST:
249
- Inspects calls within the AST. If a function call matches one in dictionaryFunctions,
250
- it is replaced by the inlined body. If the last statement in the inlined body is a return
251
- or an expression, that value or expression is substituted; otherwise, a constant is returned.
252
- visit_Expr(node: ast.Expr) -> Union[ast.AST, List[ast.AST]]:
253
- Handles expression nodes in the AST. If the expression is a function call from
254
- dictionaryFunctions, its statements are expanded in place, effectively inlining
255
- the called function's statements into the surrounding context.
256
- """
257
- def __init__(self, dictionaryFunctions: dict[str, ast.FunctionDef]):
258
- self.dictionaryFunctions = dictionaryFunctions
259
- self.callablesCompleted: set[str] = set()
260
-
261
- def inlineFunctionBody(self, callableTargetName: str) -> ast.FunctionDef | None:
262
- if (callableTargetName in self.callablesCompleted):
263
- return None
264
-
265
- self.callablesCompleted.add(callableTargetName)
266
- inlineDefinition = self.dictionaryFunctions[callableTargetName]
267
- for astNode in ast.walk(inlineDefinition):
268
- self.visit(astNode)
269
- return inlineDefinition
270
-
271
- def visit_Call(self, node: ast.Call) -> Any | ast.Constant | ast.Call | ast.AST:
272
- callNodeVisited = self.generic_visit(node)
273
- if (isinstance(callNodeVisited, ast.Call) and isinstance(callNodeVisited.func, ast.Name) and callNodeVisited.func.id in self.dictionaryFunctions):
274
- inlineDefinition = self.inlineFunctionBody(callNodeVisited.func.id)
275
- if (inlineDefinition and inlineDefinition.body):
276
- statementTerminating = inlineDefinition.body[-1]
277
- if (isinstance(statementTerminating, ast.Return) and statementTerminating.value is not None):
278
- return self.visit(statementTerminating.value)
279
- elif (isinstance(statementTerminating, ast.Expr) and statementTerminating.value is not None):
280
- return self.visit(statementTerminating.value)
281
- return ast.Constant(value=None)
282
- return callNodeVisited
283
-
284
- def visit_Expr(self, node: ast.Expr) -> ast.AST | list[ast.AST]:
285
- if (isinstance(node.value, ast.Call)):
286
- if (isinstance(node.value.func, ast.Name) and node.value.func.id in self.dictionaryFunctions):
287
- inlineDefinition = self.inlineFunctionBody(node.value.func.id)
288
- if (inlineDefinition):
289
- return [self.visit(stmt) for stmt in inlineDefinition.body]
290
- return self.generic_visit(node)
291
-
292
- class UnpackArrays(ast.NodeTransformer):
293
- """
294
- A class that transforms array accesses using enum indices into local variables.
295
-
296
- This AST transformer identifies array accesses using enum indices and replaces them
297
- with local variables, adding initialization statements at the start of functions.
298
-
299
- Parameters:
300
- enumIndexClass (Type[EnumIndices]): The enum class used for array indexing
301
- arrayName (str): The name of the array being accessed
302
-
303
- Attributes:
304
- enumIndexClass (Type[EnumIndices]): Stored enum class for index lookups
305
- arrayName (str): Name of the array being transformed
306
- substitutions (dict): Tracks variable substitutions and their original nodes
307
-
308
- The transformer handles two main cases:
309
- 1. Scalar array access - array[EnumIndices.MEMBER]
310
- 2. Array slice access - array[EnumIndices.MEMBER, other_indices...]
311
- For each identified access pattern, it:
312
- 1. Creates a local variable named after the enum member
313
- 2. Adds initialization code at function start
314
- 3. Replaces original array access with the local variable
315
- """
316
-
317
- def __init__(self, enumIndexClass: type[EnumIndices], arrayName: str) -> None:
318
- self.enumIndexClass = enumIndexClass
319
- self.arrayName = arrayName
320
- self.substitutions: dict[str, Any] = {}
321
-
322
- def extract_member_name(self, node: ast.AST) -> str | None:
323
- """Recursively extract enum member name from any node in the AST."""
324
- if isinstance(node, ast.Attribute) and node.attr == 'value':
325
- innerAttribute = node.value
326
- while isinstance(innerAttribute, ast.Attribute):
327
- if (isinstance(innerAttribute.value, ast.Name) and innerAttribute.value.id == self.enumIndexClass.__name__):
328
- return innerAttribute.attr
329
- innerAttribute = innerAttribute.value
330
- return None
331
-
332
- def transform_slice_element(self, node: ast.AST) -> ast.AST:
333
- """Transform any enum references within a slice element."""
334
- if isinstance(node, ast.Subscript):
335
- if isinstance(node.slice, ast.Attribute):
336
- member_name = self.extract_member_name(node.slice)
337
- if member_name:
338
- return ast.Name(id=member_name, ctx=node.ctx)
339
- elif isinstance(node, ast.Tuple):
340
- # Handle tuple slices by transforming each element
341
- return ast.Tuple(elts=cast(list[ast.expr], [self.transform_slice_element(elt) for elt in node.elts]), ctx=node.ctx)
342
- elif isinstance(node, ast.Attribute):
343
- member_name = self.extract_member_name(node)
344
- if member_name:
345
- return ast.Name(id=member_name, ctx=ast.Load())
346
- return node
347
-
348
- def visit_Subscript(self, node: ast.Subscript) -> ast.AST:
349
- # Recursively visit any nested subscripts in value or slice
350
- node.value = self.visit(node.value)
351
- node.slice = self.visit(node.slice)
352
- # If node.value is not our arrayName, just return node
353
- if not (isinstance(node.value, ast.Name) and node.value.id == self.arrayName):
354
- return node
355
-
356
- # Handle scalar array access
357
- if isinstance(node.slice, ast.Attribute):
358
- memberName = self.extract_member_name(node.slice)
359
- if memberName:
360
- self.substitutions[memberName] = ('scalar', node)
361
- return ast.Name(id=memberName, ctx=ast.Load())
362
-
363
- # Handle array slice access
364
- if isinstance(node.slice, ast.Tuple) and node.slice.elts:
365
- firstElement = node.slice.elts[0]
366
- memberName = self.extract_member_name(firstElement)
367
- sliceRemainder = [self.visit(elem) for elem in node.slice.elts[1:]]
368
- if memberName:
369
- self.substitutions[memberName] = ('array', node)
370
- if len(sliceRemainder) == 0:
371
- return ast.Name(id=memberName, ctx=ast.Load())
372
- return ast.Subscript(value=ast.Name(id=memberName, ctx=ast.Load()), slice=ast.Tuple(elts=sliceRemainder, ctx=ast.Load()) if len(sliceRemainder) > 1 else sliceRemainder[0], ctx=ast.Load())
373
-
374
- # If single-element tuple, unwrap
375
- if isinstance(node.slice, ast.Tuple) and len(node.slice.elts) == 1:
376
- node.slice = node.slice.elts[0]
377
-
378
- return node
379
-
380
- def visit_FunctionDef(self, node: ast.FunctionDef) -> ast.FunctionDef:
381
- node = cast(ast.FunctionDef, self.generic_visit(node))
382
-
383
- initializations = []
384
- for name, (kind, original_node) in self.substitutions.items():
385
- if kind == 'scalar':
386
- initializations.append(ast.Assign(targets=[ast.Name(id=name, ctx=ast.Store())], value=original_node))
387
- else: # array
388
- initializations.append(
389
- ast.Assign(
390
- targets=[ast.Name(id=name, ctx=ast.Store())],
391
- value=ast.Subscript(value=ast.Name(id=self.arrayName, ctx=ast.Load()),
392
- slice=ast.Attribute(value=ast.Attribute(
393
- value=ast.Name(id=self.enumIndexClass.__name__, ctx=ast.Load()),
394
- attr=name, ctx=ast.Load()), attr='value', ctx=ast.Load()), ctx=ast.Load())))
395
-
396
- node.body = initializations + node.body
397
- return node
@@ -1,155 +0,0 @@
1
- """Synthesize one file to compute `foldsTotal` of `mapShape`."""
2
- from mapFolding.someAssemblyRequired.synthesizeNumba import *
3
-
4
- def doUnrollCountGaps(FunctionDefTarget: ast.FunctionDef, stateJob: computationState, allImports: UniversalImportTracker) -> tuple[ast.FunctionDef, UniversalImportTracker]:
5
- """The initial results were very bad."""
6
- FunctionDefTarget = findAndReplaceWhileLoopIn_body(FunctionDefTarget, 'indexDimension', stateJob['my'][indexMy.dimensionsTotal])
7
- FunctionDefTarget = removeAssignTargetFrom_body(FunctionDefTarget, 'indexDimension')
8
- FunctionDefTarget = removeAssignTargetFrom_body(FunctionDefTarget, 'connectionGraph')
9
- FunctionDefTarget, allImports = insertArrayIn_body(FunctionDefTarget, 'connectionGraph', stateJob['connectionGraph'], allImports, stateJob['my'][indexMy.dimensionsTotal])
10
- for index in range(stateJob['my'][indexMy.dimensionsTotal]):
11
- class ReplaceConnectionGraph(ast.NodeTransformer):
12
- def visit_Subscript(self, node: ast.Subscript) -> ast.AST:
13
- node = cast(ast.Subscript, self.generic_visit(node))
14
- if (isinstance(node.value, ast.Name) and node.value.id == "connectionGraph" and
15
- isinstance(node.slice, ast.Tuple) and len(node.slice.elts) >= 1):
16
- firstElement = node.slice.elts[0]
17
- if isinstance(firstElement, ast.Constant) and firstElement.value == index:
18
- newName = ast.Name(id=f"connectionGraph_{index}", ctx=ast.Load())
19
- remainingIndices = node.slice.elts[1:]
20
- if len(remainingIndices) == 1:
21
- newSlice = remainingIndices[0]
22
- else:
23
- newSlice = ast.Tuple(elts=remainingIndices, ctx=ast.Load())
24
- return ast.copy_location(ast.Subscript(value=newName, slice=newSlice, ctx=node.ctx), node)
25
- return node
26
- transformer = ReplaceConnectionGraph()
27
- FunctionDefTarget = transformer.visit(FunctionDefTarget)
28
- return FunctionDefTarget, allImports
29
-
30
- 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, **keywordArguments: Any | None) -> Path:
31
- """ Parameters: **keywordArguments: most especially for `computationDivisions` if you want to make a parallel job. Also `CPUlimit`. """
32
-
33
- """ Notes:
34
- Hypothetically, everything can now be configured with parameters and functions. And changing how the job is written is relatively easy.
35
-
36
- Overview
37
- - 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
38
- - the synthesized module must run well as a standalone interpreted-Python script
39
- - the next major optimization step will (probably) be to use the module synthesized by `writeJobNumba` to compile a standalone executable
40
- - 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
41
-
42
- Minutia
43
- - perf_counter is for testing. When I run a real job, I delete those lines
44
- - avoid `with` statement
45
-
46
- Necessary
47
- - Move the function's parameters to the function body,
48
- - initialize identifiers with their state types and values,
49
-
50
- Optimizations
51
- - replace static-valued identifiers with their values
52
- - narrowly focused imports
53
- """
54
-
55
- # NOTE get the raw ingredients: data and the algorithm
56
- stateJob = makeStateJob(mapShape, writeJob=False, **keywordArguments)
57
- pythonSource = inspect.getsource(algorithmSource)
58
- astModule = ast.parse(pythonSource)
59
- setFunctionDef = {statement for statement in astModule.body if isinstance(statement, ast.FunctionDef)}
60
-
61
- if not callableTarget:
62
- if len(setFunctionDef) == 1:
63
- FunctionDefTarget = setFunctionDef.pop()
64
- callableTarget = FunctionDefTarget.name
65
- else:
66
- raise ValueError(f"I did not receive a `callableTarget` and {algorithmSource.__name__=} has more than one callable: {setFunctionDef}. Please select one.")
67
- else:
68
- listFunctionDefTarget = [statement for statement in setFunctionDef if statement.name == callableTarget]
69
- FunctionDefTarget = listFunctionDefTarget[0] if listFunctionDefTarget else None
70
- if not FunctionDefTarget: raise ValueError(f"I received `{callableTarget=}` and {algorithmSource.__name__=}, but I could not find that function in that source.")
71
-
72
- # NOTE `allImports` is a complementary container to `FunctionDefTarget`; the `FunctionDefTarget` cannot track its own imports very well.
73
- allImports = UniversalImportTracker()
74
- for statement in astModule.body:
75
- if isinstance(statement, (ast.Import, ast.ImportFrom)):
76
- allImports.addAst(statement)
77
-
78
- # NOTE remove the parameters from the function signature
79
- for pirateScowl in FunctionDefTarget.args.args.copy():
80
- match pirateScowl.arg:
81
- case 'my':
82
- FunctionDefTarget, allImports = findAndReplaceArraySubscriptIn_body(FunctionDefTarget, pirateScowl.arg, stateJob[pirateScowl.arg], ['taskIndex', 'dimensionsTotal'], allImports)
83
- case 'track':
84
- FunctionDefTarget, allImports = findAndReplaceTrackArrayIn_body(FunctionDefTarget, pirateScowl.arg, stateJob[pirateScowl.arg], allImports)
85
- case 'connectionGraph':
86
- FunctionDefTarget, allImports = insertArrayIn_body(FunctionDefTarget, pirateScowl.arg, stateJob[pirateScowl.arg], allImports)
87
- case 'gapsWhere':
88
- FunctionDefTarget, allImports = insertArrayIn_body(FunctionDefTarget, pirateScowl.arg, stateJob[pirateScowl.arg], allImports)
89
- case 'foldGroups':
90
- FunctionDefTarget = removeAssignTargetFrom_body(FunctionDefTarget, pirateScowl.arg)
91
- # FunctionDefTarget, allImports = insertArrayIn_body(FunctionDefTarget, pirateScowl.arg, stateJob[pirateScowl.arg], allImports)
92
- # continue
93
- FunctionDefTarget.args.args.remove(pirateScowl)
94
-
95
- # NOTE replace identifiers with static values with their values
96
- FunctionDefTarget, allImports = findAndReplaceAnnAssignIn_body(FunctionDefTarget, allImports)
97
- FunctionDefTarget = findAstNameReplaceWithConstantIn_body(FunctionDefTarget, 'dimensionsTotal', int(stateJob['my'][indexMy.dimensionsTotal]))
98
- FunctionDefTarget = findThingyReplaceWithConstantIn_body(FunctionDefTarget, 'foldGroups[-1]', int(stateJob['foldGroups'][-1]))
99
-
100
- # NOTE an attempt at optimization
101
- if unrollCountGaps:
102
- FunctionDefTarget, allImports = doUnrollCountGaps(FunctionDefTarget, stateJob, allImports)
103
-
104
- # NOTE starting the count and printing the total
105
- pathFilenameFoldsTotal = getPathFilenameFoldsTotal(stateJob['mapShape'])
106
- astLauncher = makeLauncherBasicJobNumba(FunctionDefTarget.name, pathFilenameFoldsTotal)
107
- FunctionDefTarget, allImports = insertReturnStatementIn_body(FunctionDefTarget, stateJob['foldGroups'], allImports)
108
-
109
- # NOTE add the perfect decorator
110
- FunctionDefTarget, allImports = decorateCallableWithNumba(FunctionDefTarget, allImports, parametersNumba)
111
- if thisIsNumbaDotJit(FunctionDefTarget.decorator_list[0]):
112
- astCall = cast(ast.Call, FunctionDefTarget.decorator_list[0])
113
- astCall.func = ast.Name(id=Z0Z_getDecoratorCallable(), ctx=ast.Load())
114
- FunctionDefTarget.decorator_list[0] = astCall
115
-
116
- # NOTE add imports, make str, remove unused imports
117
- astImports = allImports.makeListAst()
118
- astModule = ast.Module(body=cast(list[ast.stmt], astImports + [FunctionDefTarget] + [astLauncher]), type_ignores=[])
119
- ast.fix_missing_locations(astModule)
120
- pythonSource = ast.unparse(astModule)
121
- pythonSource = autoflake.fix_code(pythonSource, ['mapFolding', 'numba', 'numpy'])
122
- # 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)
123
-
124
- # NOTE put on disk
125
- if pathFilenameWriteJob is None:
126
- filename = getFilenameFoldsTotal(stateJob['mapShape'])
127
- pathRoot = getPathJobRootDEFAULT()
128
- pathFilenameWriteJob = Path(pathRoot, Path(filename).stem, Path(filename).with_suffix('.py'))
129
- else:
130
- pathFilenameWriteJob = Path(pathFilenameWriteJob)
131
- pathFilenameWriteJob.parent.mkdir(parents=True, exist_ok=True)
132
-
133
- pathFilenameWriteJob.write_text(pythonSource)
134
-
135
- return pathFilenameWriteJob
136
-
137
- if __name__ == '__main__':
138
- mapShape: list[int] = [5,5]
139
- from mapFolding.syntheticModules import numbaCount
140
- algorithmSource: ModuleType = numbaCount
141
-
142
- callableTarget = 'countSequential'
143
-
144
- parametersNumba = parametersNumbaDEFAULT
145
- parametersNumba['boundscheck'] = True
146
-
147
- pathFilenameWriteJob = None
148
-
149
- setDatatypeFoldsTotal('int64', sourGrapes=True)
150
- setDatatypeElephino('int16', sourGrapes=True)
151
- setDatatypeLeavesTotal('uint8', sourGrapes=True)
152
- Z0Z_setDatatypeModuleScalar('numba')
153
- Z0Z_setDecoratorCallable('jit')
154
-
155
- writeJobNumba(mapShape, algorithmSource, callableTarget, parametersNumba, pathFilenameWriteJob)
@@ -1,123 +0,0 @@
1
- """I suspect this function will be relatively stable for now.
2
- Managing settings and options, however, ... I've 'invented'
3
- everything I am doing. I would rather benefit from humanity's
4
- collective wisdom."""
5
- from mapFolding.someAssemblyRequired.synthesizeNumba import *
6
-
7
- def getFunctionDef(algorithmSource: ModuleType, *arguments, **keywordArguments) -> tuple[ast.FunctionDef, UniversalImportTracker]:
8
- pythonSource = inspect.getsource(algorithmSource)
9
- astModule: ast.Module = ast.parse(pythonSource, type_comments=True)
10
- FunctionDefTarget, allImports = makeFunctionDef(astModule, *arguments, **keywordArguments)
11
- return FunctionDefTarget, allImports
12
-
13
- def makePythonSource(listFunctionDefs: list[ast.FunctionDef], listAstImports: list[ast.Import | ast.ImportFrom], additional_imports: list[str]) -> str:
14
- astModule = ast.Module(body=cast(list[ast.stmt], listAstImports + listFunctionDefs), type_ignores=[])
15
- ast.fix_missing_locations(astModule)
16
- pythonSource = ast.unparse(astModule)
17
- if not pythonSource: raise FREAKOUT
18
- pythonSource = autoflake.fix_code(pythonSource, additional_imports)
19
- return pythonSource
20
-
21
- def writePythonAsModule(pythonSource: str, listCallableSynthesized: list[str], relativePathWrite: Path | None, filenameWrite: str | None, formatFilenameWrite: str | None) -> list[youOughtaKnow]:
22
- pathFilename = None
23
- if not relativePathWrite:
24
- pathWrite = getPathSyntheticModules()
25
- else:
26
- pathWrite = getPathPackage() / relativePathWrite
27
-
28
- if not formatFilenameWrite:
29
- formatFilenameWrite = formatFilenameModuleDEFAULT
30
-
31
- if not filenameWrite:
32
- if len(listCallableSynthesized) == 1:
33
- callableTarget = listCallableSynthesized[0]
34
- else:
35
- callableTarget = 'count'
36
- filenameWrite = formatFilenameWrite.format(callableTarget=callableTarget)
37
- else:
38
- if not filenameWrite.endswith('.py'):
39
- warnings.warn(f"Filename {filenameWrite=} does not end with '.py'.")
40
-
41
- pathFilename = pathWrite / filenameWrite
42
-
43
- pathFilename.write_text(pythonSource)
44
-
45
- howIsThisStillAThing = getPathPackage().parent
46
- dumbassPythonNamespace = pathFilename.relative_to(howIsThisStillAThing).with_suffix('').parts
47
- ImaModule = '.'.join(dumbassPythonNamespace)
48
-
49
- listStuffYouOughtaKnow: list[youOughtaKnow] = []
50
-
51
- for callableTarget in listCallableSynthesized:
52
- astImportFrom = ast.ImportFrom(module=ImaModule, names=[ast.alias(name=callableTarget, asname=None)], level=0)
53
- stuff = youOughtaKnow(callableSynthesized=callableTarget, pathFilenameForMe=pathFilename, astForCompetentProgrammers=astImportFrom)
54
- listStuffYouOughtaKnow.append(stuff)
55
-
56
- return listStuffYouOughtaKnow
57
-
58
- def makeFlowNumbaOptimized(listCallablesInline: list[str], callableDispatcher: bool | None = False, algorithmSource: ModuleType | None = None, relativePathWrite: Path | None = None, filenameModuleWrite: str | None = None, formatFilenameWrite: str | None = None) -> list[youOughtaKnow]:
59
- if relativePathWrite and relativePathWrite.is_absolute():
60
- raise ValueError("The path to write the module must be relative to the root of the package.")
61
- if not algorithmSource:
62
- algorithmSource = getAlgorithmSource()
63
-
64
- Z0Z_filenameModuleWrite = 'numbaCount.py'
65
-
66
- listStuffYouOughtaKnow: list[youOughtaKnow] = []
67
- additional_imports = ['mapFolding', 'numba', 'numpy']
68
-
69
- listFunctionDefs: list[ast.FunctionDef] = []
70
- allImportsModule = UniversalImportTracker()
71
- for callableTarget in listCallablesInline:
72
- parametersNumba = None
73
- inlineCallables = True
74
- unpackArrays = False
75
- allImports = None
76
- filenameWrite = None
77
- match callableTarget:
78
- case 'countParallel':
79
- parametersNumba = parametersNumbaSuperJitParallel
80
- case 'countSequential':
81
- parametersNumba = parametersNumbaSuperJit
82
- unpackArrays = True
83
- case 'countInitialize':
84
- parametersNumba = parametersNumbaDEFAULT
85
- FunctionDefTarget, allImports = getFunctionDef(algorithmSource, callableTarget, parametersNumba, inlineCallables, unpackArrays, allImports)
86
- listFunctionDefs.append(FunctionDefTarget)
87
- allImportsModule.update(allImports)
88
-
89
- listAstImports = allImportsModule.makeListAst()
90
- pythonSource = makePythonSource(listFunctionDefs, listAstImports, additional_imports)
91
-
92
- filenameWrite = filenameModuleWrite or Z0Z_filenameModuleWrite
93
-
94
- listStuff = writePythonAsModule(pythonSource, listCallablesInline, relativePathWrite, filenameWrite, formatFilenameWrite)
95
- listStuffYouOughtaKnow.extend(listStuff)
96
-
97
- if callableDispatcher:
98
- callableTarget = getAlgorithmDispatcher().__name__
99
- parametersNumba = None
100
- inlineCallables = False
101
- unpackArrays = False
102
- allImports = UniversalImportTracker()
103
- filenameWrite = None
104
- for stuff in listStuffYouOughtaKnow:
105
- statement = stuff.astForCompetentProgrammers
106
- if isinstance(statement, (ast.Import, ast.ImportFrom)):
107
- allImports.addAst(statement)
108
- FunctionDefTarget, allImports = getFunctionDef(algorithmSource, callableTarget, parametersNumba, inlineCallables, unpackArrays, allImports)
109
- listAstImports = allImports.makeListAst()
110
-
111
- pythonSource = makePythonSource([FunctionDefTarget], listAstImports, additional_imports)
112
-
113
- listStuff = writePythonAsModule(pythonSource, [callableTarget], relativePathWrite, filenameWrite, formatFilenameWrite)
114
- listStuffYouOughtaKnow.extend(listStuff)
115
-
116
- return listStuffYouOughtaKnow
117
-
118
- if __name__ == '__main__':
119
- # Z0Z_setDatatypeModuleScalar('numba')
120
- # Z0Z_setDecoratorCallable('jit')
121
- listCallablesInline: list[str] = ['countInitialize', 'countParallel', 'countSequential']
122
- callableDispatcher = True
123
- makeFlowNumbaOptimized(listCallablesInline, callableDispatcher)