mapFolding 0.4.2__py3-none-any.whl → 0.5.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.
- mapFolding/__init__.py +3 -2
- mapFolding/basecamp.py +12 -14
- mapFolding/beDRY.py +81 -58
- mapFolding/oeis.py +35 -33
- mapFolding/someAssemblyRequired/makeJob.py +8 -7
- mapFolding/someAssemblyRequired/synthesizeModuleJAX.py +1 -3
- mapFolding/someAssemblyRequired/synthesizeNumba.py +57 -60
- mapFolding/someAssemblyRequired/synthesizeNumbaGeneralized.py +102 -30
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +18 -36
- mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +77 -31
- mapFolding/syntheticModules/numbaCount.py +158 -0
- mapFolding/syntheticModules/numba_doTheNeedful.py +5 -12
- mapFolding/theDao.py +105 -105
- mapFolding/theSSOT.py +80 -205
- mapFolding/theSSOTdatatypes.py +166 -0
- {mapFolding-0.4.2.dist-info → mapFolding-0.5.0.dist-info}/METADATA +2 -1
- mapFolding-0.5.0.dist-info/RECORD +39 -0
- tests/conftest.py +84 -26
- tests/test_computations.py +29 -66
- tests/test_oeis.py +8 -12
- tests/test_other.py +11 -7
- tests/test_tasks.py +5 -5
- mapFolding/syntheticModules/numba_countInitialize.py +0 -52
- mapFolding/syntheticModules/numba_countParallel.py +0 -65
- mapFolding/syntheticModules/numba_countSequential.py +0 -67
- mapFolding/theSSOTnumba.py +0 -125
- mapFolding-0.4.2.dist-info/RECORD +0 -42
- tests/test_types.py +0 -5
- {mapFolding-0.4.2.dist-info → mapFolding-0.5.0.dist-info}/LICENSE +0 -0
- {mapFolding-0.4.2.dist-info → mapFolding-0.5.0.dist-info}/WHEEL +0 -0
- {mapFolding-0.4.2.dist-info → mapFolding-0.5.0.dist-info}/entry_points.txt +0 -0
- {mapFolding-0.4.2.dist-info → mapFolding-0.5.0.dist-info}/top_level.txt +0 -0
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
TODO: consolidate the logic in this module."""
|
|
3
3
|
from mapFolding.someAssemblyRequired.synthesizeNumbaGeneralized import *
|
|
4
4
|
|
|
5
|
-
def insertArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arrayTarget: numpy.ndarray, allImports: UniversalImportTracker, unrollSlices:
|
|
5
|
+
def insertArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arrayTarget: numpy.ndarray, allImports: UniversalImportTracker, unrollSlices: int | None = None) -> tuple[ast.FunctionDef, UniversalImportTracker]:
|
|
6
6
|
arrayType = type(arrayTarget)
|
|
7
7
|
moduleConstructor = arrayType.__module__
|
|
8
8
|
constructorName = arrayType.__name__
|
|
9
9
|
# NOTE hack
|
|
10
10
|
constructorName = constructorName.replace('ndarray', 'array')
|
|
11
11
|
argData_dtype: numpy.dtype = arrayTarget.dtype
|
|
12
|
-
|
|
12
|
+
datatypeName = argData_dtype.name
|
|
13
|
+
dtypeAsName = f"{moduleConstructor}_{datatypeName}"
|
|
13
14
|
|
|
14
15
|
allImports.addImportFromStr(moduleConstructor, constructorName)
|
|
15
|
-
allImports.addImportFromStr(moduleConstructor,
|
|
16
|
+
allImports.addImportFromStr(moduleConstructor, datatypeName, dtypeAsName)
|
|
16
17
|
|
|
17
18
|
def insertAssign(assignee: str, arraySlice: numpy.ndarray) -> None:
|
|
18
19
|
nonlocal FunctionDefTarget
|
|
@@ -20,7 +21,7 @@ def insertArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arra
|
|
|
20
21
|
astStatement = cast(ast.Expr, ast.parse(onlyDataRLE).body[0])
|
|
21
22
|
dataAst = astStatement.value
|
|
22
23
|
|
|
23
|
-
arrayCall = Then.make_astCall(name=constructorName, args=[dataAst], list_astKeywords=[ast.keyword(arg='dtype', value=ast.Name(id=
|
|
24
|
+
arrayCall = Then.make_astCall(name=constructorName, args=[dataAst], list_astKeywords=[ast.keyword(arg='dtype', value=ast.Name(id=dtypeAsName, ctx=ast.Load()))])
|
|
24
25
|
|
|
25
26
|
assignment = ast.Assign(targets=[ast.Name(id=assignee, ctx=ast.Store())], value=arrayCall)#NOTE
|
|
26
27
|
FunctionDefTarget.body.insert(0, assignment)
|
|
@@ -33,7 +34,8 @@ def insertArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arra
|
|
|
33
34
|
|
|
34
35
|
return FunctionDefTarget, allImports
|
|
35
36
|
|
|
36
|
-
def
|
|
37
|
+
def findAndReplaceTrackArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str , arrayTarget: numpy.ndarray , allImports: UniversalImportTracker) -> tuple[ast.FunctionDef, UniversalImportTracker]:
|
|
38
|
+
|
|
37
39
|
arrayType = type(arrayTarget)
|
|
38
40
|
moduleConstructor = arrayType.__module__
|
|
39
41
|
constructorName = arrayType.__name__
|
|
@@ -41,47 +43,41 @@ def findAndReplaceArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: s
|
|
|
41
43
|
constructorName = constructorName.replace('ndarray', 'array')
|
|
42
44
|
allImports.addImportFromStr(moduleConstructor, constructorName)
|
|
43
45
|
|
|
44
|
-
for
|
|
45
|
-
if
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
astSubscript: ast.Subscript = stmt.value
|
|
51
|
-
if isinstance(astSubscript.value, ast.Name) and astSubscript.value.id == identifier and isinstance(astSubscript.slice, ast.Attribute):
|
|
52
|
-
indexAs_astAttribute: ast.Attribute = astSubscript.slice
|
|
53
|
-
indexAsStr = ast.unparse(indexAs_astAttribute)
|
|
54
|
-
arraySlice = arrayTarget[eval(indexAsStr)]
|
|
46
|
+
for statement in FunctionDefTarget.body.copy():
|
|
47
|
+
if ifThis.isUnpackingAnArray(identifier)(statement):
|
|
48
|
+
datatypeName = hackSSOTdatatype(statement.targets[0].id) # type: ignore
|
|
49
|
+
dtypeAsName = f"{moduleConstructor}_{datatypeName}"
|
|
50
|
+
indexAsStr = ast.unparse(statement.value.slice) # type: ignore
|
|
51
|
+
arraySlice = arrayTarget[eval(indexAsStr)]
|
|
55
52
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
onlyDataRLE = autoDecodingRLE(arraySlice, addSpaces=True)
|
|
54
|
+
astStatement = cast(ast.Expr, ast.parse(onlyDataRLE).body[0])
|
|
55
|
+
dataAst = astStatement.value
|
|
59
56
|
|
|
60
|
-
|
|
57
|
+
arrayCall = Then.make_astCall(name=constructorName, args=[dataAst], list_astKeywords=[ast.keyword(arg='dtype', value=ast.Name(id=dtypeAsName, ctx=ast.Load()))])
|
|
61
58
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
assignment = ast.Assign(targets=[statement.targets[0]], value=arrayCall) # type: ignore
|
|
60
|
+
FunctionDefTarget.body.insert(0, assignment)
|
|
61
|
+
FunctionDefTarget.body.remove(statement)
|
|
62
|
+
allImports.addImportFromStr(moduleConstructor, datatypeName, dtypeAsName)
|
|
65
63
|
return FunctionDefTarget, allImports
|
|
66
64
|
|
|
67
|
-
def findAndReplaceArraySubscriptIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arrayTarget: numpy.ndarray, Z0Z_listChaff:
|
|
65
|
+
def findAndReplaceArraySubscriptIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arrayTarget: numpy.ndarray, Z0Z_listChaff: list[str], allImports: UniversalImportTracker) -> tuple[ast.FunctionDef, UniversalImportTracker]:
|
|
68
66
|
moduleConstructor = Z0Z_getDatatypeModuleScalar()
|
|
69
|
-
for
|
|
70
|
-
if
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
FunctionDefTarget.body.insert(0, assignment)
|
|
84
|
-
FunctionDefTarget.body.remove(stmt)
|
|
67
|
+
for statement in FunctionDefTarget.body.copy():
|
|
68
|
+
if ifThis.isUnpackingAnArray(identifier)(statement):
|
|
69
|
+
astSubscript: ast.Subscript = statement.value # type: ignore
|
|
70
|
+
astAssignee: ast.Name = statement.targets[0] # type: ignore
|
|
71
|
+
argData_dtypeName = hackSSOTdatatype(astAssignee.id)
|
|
72
|
+
allImports.addImportFromStr(moduleConstructor, argData_dtypeName)
|
|
73
|
+
indexAs_astAttribute: ast.Attribute = astSubscript.slice # type: ignore
|
|
74
|
+
indexAsStr = ast.unparse(indexAs_astAttribute)
|
|
75
|
+
argDataSlice: int = arrayTarget[eval(indexAsStr)].item()
|
|
76
|
+
astCall = ast.Call(func=ast.Name(id=argData_dtypeName, ctx=ast.Load()), args=[ast.Constant(value=argDataSlice)], keywords=[])
|
|
77
|
+
assignment = ast.Assign(targets=[astAssignee], value=astCall)
|
|
78
|
+
if astAssignee.id not in Z0Z_listChaff:
|
|
79
|
+
FunctionDefTarget.body.insert(0, assignment)
|
|
80
|
+
FunctionDefTarget.body.remove(statement)
|
|
85
81
|
return FunctionDefTarget, allImports
|
|
86
82
|
|
|
87
83
|
def removeAssignTargetFrom_body(FunctionDefTarget: ast.FunctionDef, identifier: str) -> ast.FunctionDef:
|
|
@@ -91,7 +87,7 @@ def removeAssignTargetFrom_body(FunctionDefTarget: ast.FunctionDef, identifier:
|
|
|
91
87
|
return False
|
|
92
88
|
targetNode = astNode.targets[0]
|
|
93
89
|
return (isinstance(targetNode, ast.Subscript) and isinstance(targetNode.value, ast.Name) and targetNode.value.id == identifier) or ifThis.nameIs(identifier)(targetNode)
|
|
94
|
-
def replacementBuilder(astNode: ast.AST) ->
|
|
90
|
+
def replacementBuilder(astNode: ast.AST) -> ast.stmt | None:
|
|
95
91
|
# Returning None removes the node.
|
|
96
92
|
return None
|
|
97
93
|
FunctionDefSherpa = NodeReplacer(predicate, replacementBuilder).visit(FunctionDefTarget)
|
|
@@ -102,7 +98,7 @@ def removeAssignTargetFrom_body(FunctionDefTarget: ast.FunctionDef, identifier:
|
|
|
102
98
|
ast.fix_missing_locations(FunctionDefTarget)
|
|
103
99
|
return FunctionDefTarget
|
|
104
100
|
|
|
105
|
-
def findAndReplaceAnnAssignIn_body(FunctionDefTarget: ast.FunctionDef, allImports: UniversalImportTracker) ->
|
|
101
|
+
def findAndReplaceAnnAssignIn_body(FunctionDefTarget: ast.FunctionDef, allImports: UniversalImportTracker) -> tuple[ast.FunctionDef, UniversalImportTracker]:
|
|
106
102
|
moduleConstructor = Z0Z_getDatatypeModuleScalar()
|
|
107
103
|
for stmt in FunctionDefTarget.body.copy():
|
|
108
104
|
if isinstance(stmt, ast.AnnAssign):
|
|
@@ -136,15 +132,12 @@ def findThingyReplaceWithConstantIn_body(FunctionDefTarget: ast.FunctionDef, obj
|
|
|
136
132
|
return newFunction
|
|
137
133
|
|
|
138
134
|
def findAstNameReplaceWithConstantIn_body(FunctionDefTarget: ast.FunctionDef, name: str, value: int) -> ast.FunctionDef:
|
|
139
|
-
def findName(node: ast.AST) -> bool:
|
|
140
|
-
return isinstance(node, ast.Name) and node.id == name
|
|
141
|
-
|
|
142
135
|
def replaceWithConstant(node: ast.AST) -> ast.AST:
|
|
143
136
|
return ast.copy_location(ast.Constant(value=value), node)
|
|
144
137
|
|
|
145
|
-
return cast(ast.FunctionDef, NodeReplacer(
|
|
138
|
+
return cast(ast.FunctionDef, NodeReplacer(ifThis.nameIs(name), replaceWithConstant).visit(FunctionDefTarget))
|
|
146
139
|
|
|
147
|
-
def insertReturnStatementIn_body(FunctionDefTarget: ast.FunctionDef, arrayTarget: numpy.ndarray, allImports: UniversalImportTracker) ->
|
|
140
|
+
def insertReturnStatementIn_body(FunctionDefTarget: ast.FunctionDef, arrayTarget: numpy.ndarray, allImports: UniversalImportTracker) -> tuple[ast.FunctionDef, UniversalImportTracker]:
|
|
148
141
|
"""Add multiplication and return statement to function, properly constructing AST nodes."""
|
|
149
142
|
# Create AST for multiplication operation
|
|
150
143
|
multiplicand = Z0Z_identifierCountFolds
|
|
@@ -155,6 +148,8 @@ def insertReturnStatementIn_body(FunctionDefTarget: ast.FunctionDef, arrayTarget
|
|
|
155
148
|
|
|
156
149
|
returnStatement = ast.Return(value=multiplyOperation)
|
|
157
150
|
|
|
151
|
+
datatype = hackSSOTdatatype(Z0Z_identifierCountFolds)
|
|
152
|
+
FunctionDefTarget.returns = ast.Name(id=datatype, ctx=ast.Load())
|
|
158
153
|
datatypeModuleScalar = Z0Z_getDatatypeModuleScalar()
|
|
159
154
|
allImports.addImportFromStr(datatypeModuleScalar, datatype)
|
|
160
155
|
|
|
@@ -162,7 +157,7 @@ def insertReturnStatementIn_body(FunctionDefTarget: ast.FunctionDef, arrayTarget
|
|
|
162
157
|
|
|
163
158
|
return FunctionDefTarget, allImports
|
|
164
159
|
|
|
165
|
-
def findAndReplaceWhileLoopIn_body(FunctionDefTarget: ast.FunctionDef, iteratorName: str, iterationsTotal: int) ->
|
|
160
|
+
def findAndReplaceWhileLoopIn_body(FunctionDefTarget: ast.FunctionDef, iteratorName: str, iterationsTotal: int) -> ast.FunctionDef:
|
|
166
161
|
"""
|
|
167
162
|
Unroll all nested while loops matching the condition that their test uses `iteratorName`.
|
|
168
163
|
"""
|
|
@@ -185,11 +180,11 @@ def findAndReplaceWhileLoopIn_body(FunctionDefTarget: ast.FunctionDef, iteratorN
|
|
|
185
180
|
self.iteratorName = iteratorName
|
|
186
181
|
self.iterationsTotal = iterationsTotal
|
|
187
182
|
|
|
188
|
-
def visit_While(self, node: ast.While) ->
|
|
183
|
+
def visit_While(self, node: ast.While) -> list[ast.stmt]:
|
|
189
184
|
# Check if the while loop's test uses the iterator.
|
|
190
185
|
if isinstance(node.test, ast.Compare) and ifThis.nameIs(self.iteratorName)(node.test.left):
|
|
191
186
|
# Recurse the while loop body and remove AugAssign that increments the iterator.
|
|
192
|
-
cleanBodyStatements:
|
|
187
|
+
cleanBodyStatements: list[ast.stmt] = []
|
|
193
188
|
for loopStatement in node.body:
|
|
194
189
|
# Recursively visit nested statements.
|
|
195
190
|
visitedStatement = self.visit(loopStatement)
|
|
@@ -203,7 +198,7 @@ def findAndReplaceWhileLoopIn_body(FunctionDefTarget: ast.FunctionDef, iteratorN
|
|
|
203
198
|
continue
|
|
204
199
|
cleanBodyStatements.append(visitedStatement)
|
|
205
200
|
|
|
206
|
-
newStatements:
|
|
201
|
+
newStatements: list[ast.stmt] = []
|
|
207
202
|
# Unroll using the filtered body.
|
|
208
203
|
for iterationIndex in range(self.iterationsTotal):
|
|
209
204
|
for loopStatement in cleanBodyStatements:
|
|
@@ -227,7 +222,7 @@ def findAndReplaceWhileLoopIn_body(FunctionDefTarget: ast.FunctionDef, iteratorN
|
|
|
227
222
|
ast.fix_missing_locations(newFunctionDef)
|
|
228
223
|
return newFunctionDef
|
|
229
224
|
|
|
230
|
-
def makeLauncherBasicJobNumba(callableTarget: str, pathFilenameFoldsTotal:
|
|
225
|
+
def makeLauncherBasicJobNumba(callableTarget: str, pathFilenameFoldsTotal: Path) -> ast.Module:
|
|
231
226
|
linesLaunch = f"""
|
|
232
227
|
if __name__ == '__main__':
|
|
233
228
|
import time
|
|
@@ -240,9 +235,12 @@ if __name__ == '__main__':
|
|
|
240
235
|
"""
|
|
241
236
|
return ast.parse(linesLaunch)
|
|
242
237
|
|
|
243
|
-
def
|
|
244
|
-
|
|
245
|
-
|
|
238
|
+
def makeFunctionDef(astModule: ast.Module,
|
|
239
|
+
callableTarget: str,
|
|
240
|
+
parametersNumba: ParametersNumba | None = None,
|
|
241
|
+
inlineCallables: bool | None = False,
|
|
242
|
+
unpackArrays: bool | None = False,
|
|
243
|
+
allImports: UniversalImportTracker | None = None) -> tuple[ast.FunctionDef, UniversalImportTracker]:
|
|
246
244
|
if allImports is None:
|
|
247
245
|
allImports = UniversalImportTracker()
|
|
248
246
|
for statement in astModule.body:
|
|
@@ -253,6 +251,7 @@ def makeAstModuleForOneCallable(pythonSource: str, callableTarget: str, paramete
|
|
|
253
251
|
dictionaryFunctionDef = {statement.name: statement for statement in astModule.body if isinstance(statement, ast.FunctionDef)}
|
|
254
252
|
callableInlinerWorkhorse = RecursiveInliner(dictionaryFunctionDef)
|
|
255
253
|
# NOTE the inliner assumes each function is not called more than once
|
|
254
|
+
# TODO change the inliner to handle multiple calls to the same function
|
|
256
255
|
FunctionDefTarget = callableInlinerWorkhorse.inlineFunctionBody(callableTarget)
|
|
257
256
|
else:
|
|
258
257
|
FunctionDefTarget = next((node for node in astModule.body if isinstance(node, ast.FunctionDef) and node.name == callableTarget), None)
|
|
@@ -270,11 +269,9 @@ def makeAstModuleForOneCallable(pythonSource: str, callableTarget: str, paramete
|
|
|
270
269
|
FunctionDefTarget = cast(ast.FunctionDef, unpacker.visit(FunctionDefTarget))
|
|
271
270
|
ast.fix_missing_locations(FunctionDefTarget)
|
|
272
271
|
|
|
273
|
-
|
|
274
|
-
ast.fix_missing_locations(astModule)
|
|
275
|
-
return ast.unparse(astModule)
|
|
272
|
+
return FunctionDefTarget, allImports
|
|
276
273
|
|
|
277
|
-
def decorateCallableWithNumba(FunctionDefTarget: ast.FunctionDef, allImports: UniversalImportTracker, parametersNumba:
|
|
274
|
+
def decorateCallableWithNumba(FunctionDefTarget: ast.FunctionDef, allImports: UniversalImportTracker, parametersNumba: ParametersNumba | None = None) -> tuple[ast.FunctionDef, UniversalImportTracker]:
|
|
278
275
|
def Z0Z_UnhandledDecorators(astCallable: ast.FunctionDef) -> ast.FunctionDef:
|
|
279
276
|
# TODO: more explicit handling of decorators. I'm able to ignore this because I know `algorithmSource` doesn't have any decorators.
|
|
280
277
|
for decoratorItem in astCallable.decorator_list.copy():
|
|
@@ -311,7 +308,7 @@ def decorateCallableWithNumba(FunctionDefTarget: ast.FunctionDef, allImports: Un
|
|
|
311
308
|
datatypeModuleDecorator = Z0Z_getDatatypeModuleScalar()
|
|
312
309
|
list_argsDecorator: Sequence[ast.expr] = []
|
|
313
310
|
|
|
314
|
-
list_arg4signature_or_function:
|
|
311
|
+
list_arg4signature_or_function: list[ast.expr] = []
|
|
315
312
|
for parameter in FunctionDefTarget.args.args:
|
|
316
313
|
signatureElement = make_arg4parameter(parameter)
|
|
317
314
|
if signatureElement:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from mapFolding import (
|
|
2
2
|
computationState,
|
|
3
3
|
EnumIndices,
|
|
4
|
-
|
|
4
|
+
formatFilenameModuleDEFAULT,
|
|
5
5
|
FREAKOUT,
|
|
6
6
|
getAlgorithmDispatcher,
|
|
7
7
|
getAlgorithmSource,
|
|
@@ -35,8 +35,9 @@ from mapFolding.someAssemblyRequired.makeJob import makeStateJob
|
|
|
35
35
|
from numpy import integer
|
|
36
36
|
from numpy.typing import NDArray
|
|
37
37
|
from types import ModuleType
|
|
38
|
-
from
|
|
39
|
-
from
|
|
38
|
+
from collections.abc import Callable, Sequence
|
|
39
|
+
from typing import Any, cast
|
|
40
|
+
from Z0Z_tools import autoDecodingRLE, updateExtendPolishDictionaryLists
|
|
40
41
|
import ast
|
|
41
42
|
import autoflake
|
|
42
43
|
import collections
|
|
@@ -46,12 +47,44 @@ import inspect
|
|
|
46
47
|
import more_itertools
|
|
47
48
|
import numba
|
|
48
49
|
import numpy
|
|
49
|
-
import
|
|
50
|
-
import
|
|
50
|
+
from os import PathLike
|
|
51
|
+
from pathlib import Path
|
|
51
52
|
import python_minifier
|
|
53
|
+
import warnings
|
|
52
54
|
|
|
53
55
|
youOughtaKnow = collections.namedtuple('youOughtaKnow', ['callableSynthesized', 'pathFilenameForMe', 'astForCompetentProgrammers'])
|
|
54
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
|
+
|
|
55
88
|
# Generic
|
|
56
89
|
class ifThis:
|
|
57
90
|
"""Generic AST node predicate builder."""
|
|
@@ -71,23 +104,40 @@ class ifThis:
|
|
|
71
104
|
def isCallWithName(callableName: str) -> Callable[[ast.AST], bool]:
|
|
72
105
|
return lambda node: (isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id == callableName)
|
|
73
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
|
+
|
|
74
114
|
@staticmethod
|
|
75
115
|
def anyOf(*predicates: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
|
|
76
116
|
return lambda node: any(pred(node) for pred in predicates)
|
|
77
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
|
+
|
|
78
128
|
class Then:
|
|
79
129
|
"""Generic actions."""
|
|
80
130
|
@staticmethod
|
|
81
|
-
def copy_astCallKeywords(astCall: ast.Call) ->
|
|
131
|
+
def copy_astCallKeywords(astCall: ast.Call) -> dict[str, Any]:
|
|
82
132
|
"""Extract keyword parameters from a decorator AST node."""
|
|
83
|
-
dictionaryKeywords:
|
|
133
|
+
dictionaryKeywords: dict[str, Any] = {}
|
|
84
134
|
for keywordItem in astCall.keywords:
|
|
85
135
|
if isinstance(keywordItem.value, ast.Constant) and keywordItem.arg is not None:
|
|
86
136
|
dictionaryKeywords[keywordItem.arg] = keywordItem.value.value
|
|
87
137
|
return dictionaryKeywords
|
|
88
138
|
|
|
89
139
|
@staticmethod
|
|
90
|
-
def make_astCall(name: str, args:
|
|
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:
|
|
91
141
|
list_dictionaryKeywords = [ast.keyword(arg=keyName, value=ast.Constant(value=keyValue)) for keyName, keyValue in dictionaryKeywords.items()] if dictionaryKeywords else []
|
|
92
142
|
return ast.Call(
|
|
93
143
|
func=ast.Name(id=name, ctx=ast.Load()),
|
|
@@ -103,15 +153,14 @@ class NodeReplacer(ast.NodeTransformer):
|
|
|
103
153
|
None from the replacement builder indicates that the node should be removed.
|
|
104
154
|
|
|
105
155
|
Attributes:
|
|
106
|
-
findMe
|
|
107
|
-
nodeReplacementBuilder
|
|
108
|
-
or None to remove the node.
|
|
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.
|
|
109
158
|
|
|
110
159
|
Methods:
|
|
111
160
|
visit(node: ast.AST) -> Optional[ast.AST]:
|
|
112
161
|
Visits each node in the AST, replacing or removing it based on the predicate.
|
|
113
162
|
"""
|
|
114
|
-
def __init__(self, findMe: Callable[[ast.AST], bool], nodeReplacementBuilder: Callable[[ast.AST],
|
|
163
|
+
def __init__(self, findMe: Callable[[ast.AST], bool], nodeReplacementBuilder: Callable[[ast.AST], ast.AST | None]) -> None:
|
|
115
164
|
self.findMe = findMe
|
|
116
165
|
self.nodeReplacementBuilder = nodeReplacementBuilder
|
|
117
166
|
|
|
@@ -133,28 +182,51 @@ def thisIsAnyNumbaJitDecorator(Ima: ast.AST) -> bool:
|
|
|
133
182
|
# Domain-based
|
|
134
183
|
class UniversalImportTracker:
|
|
135
184
|
def __init__(self) -> None:
|
|
136
|
-
self.dictionaryImportFrom:
|
|
185
|
+
self.dictionaryImportFrom: dict[str, set] = collections.defaultdict(set)
|
|
137
186
|
self.setImport = set()
|
|
138
187
|
|
|
139
|
-
def addAst(self, astImport_:
|
|
188
|
+
def addAst(self, astImport_: ast.Import | ast.ImportFrom) -> None:
|
|
140
189
|
if isinstance(astImport_, ast.Import):
|
|
141
190
|
for alias in astImport_.names:
|
|
142
191
|
self.setImport.add(alias.name)
|
|
143
192
|
elif isinstance(astImport_, ast.ImportFrom):
|
|
144
193
|
if astImport_.module is not None:
|
|
145
|
-
self.dictionaryImportFrom[astImport_.module].update(alias.name for alias in astImport_.names)
|
|
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)
|
|
146
198
|
|
|
147
|
-
def addImportFromStr(self, module: str, name: str) -> None:
|
|
148
|
-
self.dictionaryImportFrom[module].add(name)
|
|
199
|
+
def addImportFromStr(self, module: str, name: str, asname: str | None = None) -> None:
|
|
200
|
+
self.dictionaryImportFrom[module].add((name, asname))
|
|
149
201
|
|
|
150
|
-
def
|
|
151
|
-
|
|
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))
|
|
152
209
|
|
|
153
|
-
|
|
154
|
-
listAstImportFrom = [ast.ImportFrom(module=module, names=[ast.alias(name=name, asname=None)], level=0) for module, names in self.dictionaryImportFrom.items() for name in names]
|
|
155
|
-
listAstImport = [ast.Import(names=[ast.alias(name=name, asname=None)]) for name in self.setImport]
|
|
210
|
+
listAstImport = [ast.Import(names=[ast.alias(name=name, asname=None)]) for name in sorted(self.setImport)]
|
|
156
211
|
return listAstImportFrom + listAstImport
|
|
157
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
|
+
|
|
158
230
|
# Intricate and specialized
|
|
159
231
|
class RecursiveInliner(ast.NodeTransformer):
|
|
160
232
|
"""
|
|
@@ -182,11 +254,11 @@ class RecursiveInliner(ast.NodeTransformer):
|
|
|
182
254
|
dictionaryFunctions, its statements are expanded in place, effectively inlining
|
|
183
255
|
the called function's statements into the surrounding context.
|
|
184
256
|
"""
|
|
185
|
-
def __init__(self, dictionaryFunctions:
|
|
257
|
+
def __init__(self, dictionaryFunctions: dict[str, ast.FunctionDef]):
|
|
186
258
|
self.dictionaryFunctions = dictionaryFunctions
|
|
187
|
-
self.callablesCompleted:
|
|
259
|
+
self.callablesCompleted: set[str] = set()
|
|
188
260
|
|
|
189
|
-
def inlineFunctionBody(self, callableTargetName: str) ->
|
|
261
|
+
def inlineFunctionBody(self, callableTargetName: str) -> ast.FunctionDef | None:
|
|
190
262
|
if (callableTargetName in self.callablesCompleted):
|
|
191
263
|
return None
|
|
192
264
|
|
|
@@ -209,7 +281,7 @@ class RecursiveInliner(ast.NodeTransformer):
|
|
|
209
281
|
return ast.Constant(value=None)
|
|
210
282
|
return callNodeVisited
|
|
211
283
|
|
|
212
|
-
def visit_Expr(self, node: ast.Expr) ->
|
|
284
|
+
def visit_Expr(self, node: ast.Expr) -> ast.AST | list[ast.AST]:
|
|
213
285
|
if (isinstance(node.value, ast.Call)):
|
|
214
286
|
if (isinstance(node.value.func, ast.Name) and node.value.func.id in self.dictionaryFunctions):
|
|
215
287
|
inlineDefinition = self.inlineFunctionBody(node.value.func.id)
|
|
@@ -242,12 +314,12 @@ class UnpackArrays(ast.NodeTransformer):
|
|
|
242
314
|
3. Replaces original array access with the local variable
|
|
243
315
|
"""
|
|
244
316
|
|
|
245
|
-
def __init__(self, enumIndexClass:
|
|
317
|
+
def __init__(self, enumIndexClass: type[EnumIndices], arrayName: str) -> None:
|
|
246
318
|
self.enumIndexClass = enumIndexClass
|
|
247
319
|
self.arrayName = arrayName
|
|
248
|
-
self.substitutions:
|
|
320
|
+
self.substitutions: dict[str, Any] = {}
|
|
249
321
|
|
|
250
|
-
def extract_member_name(self, node: ast.AST) ->
|
|
322
|
+
def extract_member_name(self, node: ast.AST) -> str | None:
|
|
251
323
|
"""Recursively extract enum member name from any node in the AST."""
|
|
252
324
|
if isinstance(node, ast.Attribute) and node.attr == 'value':
|
|
253
325
|
innerAttribute = node.value
|
|
@@ -266,7 +338,7 @@ class UnpackArrays(ast.NodeTransformer):
|
|
|
266
338
|
return ast.Name(id=member_name, ctx=node.ctx)
|
|
267
339
|
elif isinstance(node, ast.Tuple):
|
|
268
340
|
# Handle tuple slices by transforming each element
|
|
269
|
-
return ast.Tuple(elts=cast(
|
|
341
|
+
return ast.Tuple(elts=cast(list[ast.expr], [self.transform_slice_element(elt) for elt in node.elts]), ctx=node.ctx)
|
|
270
342
|
elif isinstance(node, ast.Attribute):
|
|
271
343
|
member_name = self.extract_member_name(node)
|
|
272
344
|
if member_name:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Synthesize one file to compute `foldsTotal` of `mapShape`."""
|
|
2
2
|
from mapFolding.someAssemblyRequired.synthesizeNumba import *
|
|
3
3
|
|
|
4
|
-
def doUnrollCountGaps(FunctionDefTarget: ast.FunctionDef, stateJob: computationState, allImports: UniversalImportTracker) ->
|
|
4
|
+
def doUnrollCountGaps(FunctionDefTarget: ast.FunctionDef, stateJob: computationState, allImports: UniversalImportTracker) -> tuple[ast.FunctionDef, UniversalImportTracker]:
|
|
5
5
|
"""The initial results were very bad."""
|
|
6
6
|
FunctionDefTarget = findAndReplaceWhileLoopIn_body(FunctionDefTarget, 'indexDimension', stateJob['my'][indexMy.dimensionsTotal])
|
|
7
7
|
FunctionDefTarget = removeAssignTargetFrom_body(FunctionDefTarget, 'indexDimension')
|
|
@@ -27,14 +27,7 @@ def doUnrollCountGaps(FunctionDefTarget: ast.FunctionDef, stateJob: computationS
|
|
|
27
27
|
FunctionDefTarget = transformer.visit(FunctionDefTarget)
|
|
28
28
|
return FunctionDefTarget, allImports
|
|
29
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:
|
|
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:
|
|
38
31
|
""" Parameters: **keywordArguments: most especially for `computationDivisions` if you want to make a parallel job. Also `CPUlimit`. """
|
|
39
32
|
|
|
40
33
|
""" Notes:
|
|
@@ -64,6 +57,7 @@ def writeJobNumba(mapShape: Sequence[int]
|
|
|
64
57
|
pythonSource = inspect.getsource(algorithmSource)
|
|
65
58
|
astModule = ast.parse(pythonSource)
|
|
66
59
|
setFunctionDef = {statement for statement in astModule.body if isinstance(statement, ast.FunctionDef)}
|
|
60
|
+
|
|
67
61
|
if not callableTarget:
|
|
68
62
|
if len(setFunctionDef) == 1:
|
|
69
63
|
FunctionDefTarget = setFunctionDef.pop()
|
|
@@ -71,7 +65,8 @@ def writeJobNumba(mapShape: Sequence[int]
|
|
|
71
65
|
else:
|
|
72
66
|
raise ValueError(f"I did not receive a `callableTarget` and {algorithmSource.__name__=} has more than one callable: {setFunctionDef}. Please select one.")
|
|
73
67
|
else:
|
|
74
|
-
|
|
68
|
+
listFunctionDefTarget = [statement for statement in setFunctionDef if statement.name == callableTarget]
|
|
69
|
+
FunctionDefTarget = listFunctionDefTarget[0] if listFunctionDefTarget else None
|
|
75
70
|
if not FunctionDefTarget: raise ValueError(f"I received `{callableTarget=}` and {algorithmSource.__name__=}, but I could not find that function in that source.")
|
|
76
71
|
|
|
77
72
|
# NOTE `allImports` is a complementary container to `FunctionDefTarget`; the `FunctionDefTarget` cannot track its own imports very well.
|
|
@@ -86,13 +81,15 @@ def writeJobNumba(mapShape: Sequence[int]
|
|
|
86
81
|
case 'my':
|
|
87
82
|
FunctionDefTarget, allImports = findAndReplaceArraySubscriptIn_body(FunctionDefTarget, pirateScowl.arg, stateJob[pirateScowl.arg], ['taskIndex', 'dimensionsTotal'], allImports)
|
|
88
83
|
case 'track':
|
|
89
|
-
FunctionDefTarget, allImports =
|
|
84
|
+
FunctionDefTarget, allImports = findAndReplaceTrackArrayIn_body(FunctionDefTarget, pirateScowl.arg, stateJob[pirateScowl.arg], allImports)
|
|
90
85
|
case 'connectionGraph':
|
|
91
86
|
FunctionDefTarget, allImports = insertArrayIn_body(FunctionDefTarget, pirateScowl.arg, stateJob[pirateScowl.arg], allImports)
|
|
92
87
|
case 'gapsWhere':
|
|
93
88
|
FunctionDefTarget, allImports = insertArrayIn_body(FunctionDefTarget, pirateScowl.arg, stateJob[pirateScowl.arg], allImports)
|
|
94
89
|
case 'foldGroups':
|
|
95
90
|
FunctionDefTarget = removeAssignTargetFrom_body(FunctionDefTarget, pirateScowl.arg)
|
|
91
|
+
# FunctionDefTarget, allImports = insertArrayIn_body(FunctionDefTarget, pirateScowl.arg, stateJob[pirateScowl.arg], allImports)
|
|
92
|
+
# continue
|
|
96
93
|
FunctionDefTarget.args.args.remove(pirateScowl)
|
|
97
94
|
|
|
98
95
|
# NOTE replace identifiers with static values with their values
|
|
@@ -110,8 +107,6 @@ def writeJobNumba(mapShape: Sequence[int]
|
|
|
110
107
|
FunctionDefTarget, allImports = insertReturnStatementIn_body(FunctionDefTarget, stateJob['foldGroups'], allImports)
|
|
111
108
|
|
|
112
109
|
# NOTE add the perfect decorator
|
|
113
|
-
datatype = hackSSOTdatatype(Z0Z_identifierCountFolds)
|
|
114
|
-
FunctionDefTarget.returns = ast.Name(id=datatype, ctx=ast.Load())
|
|
115
110
|
FunctionDefTarget, allImports = decorateCallableWithNumba(FunctionDefTarget, allImports, parametersNumba)
|
|
116
111
|
if thisIsNumbaDotJit(FunctionDefTarget.decorator_list[0]):
|
|
117
112
|
astCall = cast(ast.Call, FunctionDefTarget.decorator_list[0])
|
|
@@ -120,33 +115,19 @@ def writeJobNumba(mapShape: Sequence[int]
|
|
|
120
115
|
|
|
121
116
|
# NOTE add imports, make str, remove unused imports
|
|
122
117
|
astImports = allImports.makeListAst()
|
|
123
|
-
astModule = ast.Module(body=cast(
|
|
118
|
+
astModule = ast.Module(body=cast(list[ast.stmt], astImports + [FunctionDefTarget] + [astLauncher]), type_ignores=[])
|
|
124
119
|
ast.fix_missing_locations(astModule)
|
|
125
120
|
pythonSource = ast.unparse(astModule)
|
|
126
121
|
pythonSource = autoflake.fix_code(pythonSource, ['mapFolding', 'numba', 'numpy'])
|
|
127
|
-
pythonSource = python_minifier.minify(pythonSource, remove_annotations = False,
|
|
128
|
-
remove_pass = False,
|
|
129
|
-
remove_literal_statements = False,
|
|
130
|
-
combine_imports = True,
|
|
131
|
-
hoist_literals = False,
|
|
132
|
-
rename_locals = False,
|
|
133
|
-
rename_globals = False,
|
|
134
|
-
remove_object_base = False,
|
|
135
|
-
convert_posargs_to_args = False,
|
|
136
|
-
preserve_shebang = True,
|
|
137
|
-
remove_asserts = False,
|
|
138
|
-
remove_debug = False,
|
|
139
|
-
remove_explicit_return_none = False,
|
|
140
|
-
remove_builtin_exception_brackets = False,
|
|
141
|
-
constant_folding = False)
|
|
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)
|
|
142
123
|
|
|
143
124
|
# NOTE put on disk
|
|
144
125
|
if pathFilenameWriteJob is None:
|
|
145
126
|
filename = getFilenameFoldsTotal(stateJob['mapShape'])
|
|
146
127
|
pathRoot = getPathJobRootDEFAULT()
|
|
147
|
-
pathFilenameWriteJob =
|
|
128
|
+
pathFilenameWriteJob = Path(pathRoot, Path(filename).stem, Path(filename).with_suffix('.py'))
|
|
148
129
|
else:
|
|
149
|
-
pathFilenameWriteJob =
|
|
130
|
+
pathFilenameWriteJob = Path(pathFilenameWriteJob)
|
|
150
131
|
pathFilenameWriteJob.parent.mkdir(parents=True, exist_ok=True)
|
|
151
132
|
|
|
152
133
|
pathFilenameWriteJob.write_text(pythonSource)
|
|
@@ -154,18 +135,19 @@ def writeJobNumba(mapShape: Sequence[int]
|
|
|
154
135
|
return pathFilenameWriteJob
|
|
155
136
|
|
|
156
137
|
if __name__ == '__main__':
|
|
157
|
-
mapShape = [5,5]
|
|
158
|
-
from mapFolding.syntheticModules import
|
|
159
|
-
algorithmSource: ModuleType =
|
|
138
|
+
mapShape: list[int] = [5,5]
|
|
139
|
+
from mapFolding.syntheticModules import numbaCount
|
|
140
|
+
algorithmSource: ModuleType = numbaCount
|
|
160
141
|
|
|
161
|
-
callableTarget =
|
|
142
|
+
callableTarget = 'countSequential'
|
|
162
143
|
|
|
163
144
|
parametersNumba = parametersNumbaDEFAULT
|
|
145
|
+
parametersNumba['boundscheck'] = True
|
|
164
146
|
|
|
165
147
|
pathFilenameWriteJob = None
|
|
166
148
|
|
|
167
149
|
setDatatypeFoldsTotal('int64', sourGrapes=True)
|
|
168
|
-
setDatatypeElephino('
|
|
150
|
+
setDatatypeElephino('int16', sourGrapes=True)
|
|
169
151
|
setDatatypeLeavesTotal('uint8', sourGrapes=True)
|
|
170
152
|
Z0Z_setDatatypeModuleScalar('numba')
|
|
171
153
|
Z0Z_setDecoratorCallable('jit')
|