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