mapFolding 0.3.11__py3-none-any.whl → 0.4.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 +44 -32
- mapFolding/basecamp.py +50 -50
- mapFolding/beDRY.py +336 -336
- mapFolding/oeis.py +262 -262
- mapFolding/reference/flattened.py +294 -293
- mapFolding/reference/hunterNumba.py +126 -126
- mapFolding/reference/irvineJavaPort.py +99 -99
- mapFolding/reference/jax.py +153 -153
- mapFolding/reference/lunnan.py +148 -148
- mapFolding/reference/lunnanNumpy.py +115 -115
- mapFolding/reference/lunnanWhile.py +114 -114
- mapFolding/reference/rotatedEntryPoint.py +183 -183
- mapFolding/reference/total_countPlus1vsPlusN.py +203 -203
- mapFolding/someAssemblyRequired/__init__.py +2 -1
- mapFolding/someAssemblyRequired/getLLVMforNoReason.py +12 -12
- mapFolding/someAssemblyRequired/makeJob.py +48 -48
- mapFolding/someAssemblyRequired/synthesizeModuleJAX.py +17 -17
- mapFolding/someAssemblyRequired/synthesizeNumba.py +345 -803
- mapFolding/someAssemblyRequired/synthesizeNumbaGeneralized.py +371 -0
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +150 -0
- mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +75 -0
- mapFolding/syntheticModules/__init__.py +0 -0
- mapFolding/syntheticModules/numba_countInitialize.py +2 -2
- mapFolding/syntheticModules/numba_countParallel.py +3 -3
- mapFolding/syntheticModules/numba_countSequential.py +28 -28
- mapFolding/syntheticModules/numba_doTheNeedful.py +6 -6
- mapFolding/theDao.py +168 -169
- mapFolding/theSSOT.py +190 -162
- mapFolding/theSSOTnumba.py +91 -75
- mapFolding-0.4.0.dist-info/METADATA +122 -0
- mapFolding-0.4.0.dist-info/RECORD +41 -0
- tests/conftest.py +238 -128
- tests/test_oeis.py +80 -80
- tests/test_other.py +137 -224
- tests/test_tasks.py +21 -21
- tests/test_types.py +2 -2
- mapFolding-0.3.11.dist-info/METADATA +0 -155
- mapFolding-0.3.11.dist-info/RECORD +0 -39
- tests/conftest_tmpRegistry.py +0 -62
- tests/conftest_uniformTests.py +0 -53
- {mapFolding-0.3.11.dist-info → mapFolding-0.4.0.dist-info}/LICENSE +0 -0
- {mapFolding-0.3.11.dist-info → mapFolding-0.4.0.dist-info}/WHEEL +0 -0
- {mapFolding-0.3.11.dist-info → mapFolding-0.4.0.dist-info}/entry_points.txt +0 -0
- {mapFolding-0.3.11.dist-info → mapFolding-0.4.0.dist-info}/top_level.txt +0 -0
|
@@ -1,805 +1,347 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
self.substitutions[memberName] = ('array', node)
|
|
233
|
-
if len(sliceRemainder) == 0:
|
|
234
|
-
return ast.Name(id=memberName, ctx=ast.Load())
|
|
235
|
-
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())
|
|
236
|
-
|
|
237
|
-
# If single-element tuple, unwrap
|
|
238
|
-
if isinstance(node.slice, ast.Tuple) and len(node.slice.elts) == 1:
|
|
239
|
-
node.slice = node.slice.elts[0]
|
|
240
|
-
|
|
241
|
-
return node
|
|
242
|
-
|
|
243
|
-
def visit_FunctionDef(self, node: ast.FunctionDef) -> ast.FunctionDef:
|
|
244
|
-
node = cast(ast.FunctionDef, self.generic_visit(node))
|
|
245
|
-
|
|
246
|
-
initializations = []
|
|
247
|
-
for name, (kind, original_node) in self.substitutions.items():
|
|
248
|
-
if kind == 'scalar':
|
|
249
|
-
initializations.append(ast.Assign(targets=[ast.Name(id=name, ctx=ast.Store())], value=original_node))
|
|
250
|
-
else: # array
|
|
251
|
-
initializations.append(
|
|
252
|
-
ast.Assign(
|
|
253
|
-
targets=[ast.Name(id=name, ctx=ast.Store())],
|
|
254
|
-
value=ast.Subscript(value=ast.Name(id=self.arrayName, ctx=ast.Load()),
|
|
255
|
-
slice=ast.Attribute(value=ast.Attribute(
|
|
256
|
-
value=ast.Name(id=self.enumIndexClass.__name__, ctx=ast.Load()),
|
|
257
|
-
attr=name, ctx=ast.Load()), attr='value', ctx=ast.Load()), ctx=ast.Load())))
|
|
258
|
-
|
|
259
|
-
node.body = initializations + node.body
|
|
260
|
-
return node
|
|
261
|
-
|
|
262
|
-
def decorateCallableWithNumba(FunctionDefTarget: ast.FunctionDef, parametersNumba: Optional[ParametersNumba]=None) -> ast.FunctionDef:
|
|
263
|
-
def makeNumbaParameterSignatureElement(signatureElement: ast.arg):
|
|
264
|
-
if isinstance(signatureElement.annotation, ast.Subscript) and isinstance(signatureElement.annotation.slice, ast.Tuple):
|
|
265
|
-
annotationShape = signatureElement.annotation.slice.elts[0]
|
|
266
|
-
if isinstance(annotationShape, ast.Subscript) and isinstance(annotationShape.slice, ast.Tuple):
|
|
267
|
-
shapeAsListSlices: Sequence[ast.expr] = [ast.Slice() for axis in range(len(annotationShape.slice.elts))]
|
|
268
|
-
shapeAsListSlices[-1] = ast.Slice(step=ast.Constant(value=1))
|
|
269
|
-
shapeAST = ast.Tuple(elts=list(shapeAsListSlices), ctx=ast.Load())
|
|
270
|
-
else:
|
|
271
|
-
shapeAST = ast.Slice(step=ast.Constant(value=1))
|
|
272
|
-
|
|
273
|
-
annotationDtype = signatureElement.annotation.slice.elts[1]
|
|
274
|
-
if (isinstance(annotationDtype, ast.Subscript) and isinstance(annotationDtype.slice, ast.Attribute)):
|
|
275
|
-
datatypeAST = annotationDtype.slice.attr
|
|
276
|
-
else:
|
|
277
|
-
datatypeAST = None
|
|
278
|
-
|
|
279
|
-
ndarrayName = signatureElement.arg
|
|
280
|
-
Z0Z_hacky_dtype = hackSSOTdatatype(ndarrayName)
|
|
281
|
-
datatype_attr = datatypeAST or Z0Z_hacky_dtype
|
|
282
|
-
allImports.addImportFromStr(datatypeModuleDecorator, datatype_attr)
|
|
283
|
-
datatypeNumba = ast.Name(id=datatype_attr, ctx=ast.Load())
|
|
284
|
-
|
|
285
|
-
return ast.Subscript(value=datatypeNumba, slice=shapeAST, ctx=ast.Load())
|
|
286
|
-
|
|
287
|
-
FunctionDefTarget = Z0Z_UnhandledDecorators(FunctionDefTarget)
|
|
288
|
-
|
|
289
|
-
listNumbaParameterSignature: Sequence[ast.expr] = []
|
|
290
|
-
for parameter in FunctionDefTarget.args.args:
|
|
291
|
-
signatureElement = makeNumbaParameterSignatureElement(parameter)
|
|
292
|
-
if (signatureElement):
|
|
293
|
-
listNumbaParameterSignature.append(signatureElement)
|
|
294
|
-
|
|
295
|
-
astTupleSignatureParameters = ast.Tuple(elts=listNumbaParameterSignature, ctx=ast.Load())
|
|
296
|
-
|
|
297
|
-
# TODO if `astCallable` has a return, the return needs to be added to `astArgsNumbaSignature` in the appropriate place
|
|
298
|
-
# The return, when placed in the args, is treated as a `Call`. This is logical because numba is converting to machine code.
|
|
299
|
-
# , args=[Call(func=Name(id='int64', ctx=Load()))]
|
|
300
|
-
ast_argsSignature = astTupleSignatureParameters
|
|
301
|
-
|
|
302
|
-
ImaReturn = next((node for node in FunctionDefTarget.body if isinstance(node, ast.Return)), None)
|
|
303
|
-
# Return(value=Name(id='groupsOfFolds', ctx=Load()))]
|
|
304
|
-
if ImaReturn is not None and isinstance(ImaReturn.value, ast.Name):
|
|
305
|
-
my_idIf_I_wereA_astCall_func_astName_idParameter = ImaReturn.value.id
|
|
306
|
-
ast_argsSignature = ast.Call(
|
|
307
|
-
func=ast.Name(id=my_idIf_I_wereA_astCall_func_astName_idParameter, ctx=ast.Load()),
|
|
308
|
-
args=[astTupleSignatureParameters],
|
|
309
|
-
keywords=[]
|
|
310
|
-
)
|
|
311
|
-
else:
|
|
312
|
-
ast_argsSignature = astTupleSignatureParameters
|
|
313
|
-
|
|
314
|
-
if parametersNumba is None:
|
|
315
|
-
parametersNumba = parametersNumbaDEFAULT
|
|
316
|
-
|
|
317
|
-
listKeywordsNumbaSignature = [ast.keyword(arg=parameterName, value=ast.Constant(value=parameterValue)) for parameterName, parameterValue in parametersNumba.items()]
|
|
318
|
-
allImports.addImportFromStr(datatypeModuleDecorator, 'jit')
|
|
319
|
-
astDecoratorNumba = ast.Call(func=ast.Name(id='jit', ctx=ast.Load()), args=[ast_argsSignature], keywords=listKeywordsNumbaSignature)
|
|
320
|
-
|
|
321
|
-
FunctionDefTarget.decorator_list = [astDecoratorNumba]
|
|
322
|
-
return FunctionDefTarget
|
|
323
|
-
|
|
324
|
-
def inlineOneCallable(pythonSource: str, callableTarget: str) -> str | None:
|
|
325
|
-
astModule: ast.Module = ast.parse(pythonSource, type_comments=True)
|
|
326
|
-
|
|
327
|
-
for statement in astModule.body:
|
|
328
|
-
if isinstance(statement, (ast.Import, ast.ImportFrom)):
|
|
329
|
-
allImports.addAst(statement)
|
|
330
|
-
|
|
331
|
-
dictionaryFunctionDef = {statement.name: statement for statement in astModule.body if isinstance(statement, ast.FunctionDef)}
|
|
332
|
-
callableInlinerWorkhorse = RecursiveInliner(dictionaryFunctionDef)
|
|
333
|
-
FunctionDefTarget = callableInlinerWorkhorse.inlineFunctionBody(callableTarget)
|
|
334
|
-
|
|
335
|
-
if FunctionDefTarget:
|
|
336
|
-
ast.fix_missing_locations(FunctionDefTarget)
|
|
337
|
-
parametersNumba = None
|
|
338
|
-
match callableTarget:
|
|
339
|
-
case 'countParallel':
|
|
340
|
-
parametersNumba = parametersNumbaSuperJitParallel
|
|
341
|
-
case 'countSequential':
|
|
342
|
-
parametersNumba = parametersNumbaSuperJit
|
|
343
|
-
case 'countInitialize':
|
|
344
|
-
parametersNumba = parametersNumbaDEFAULT
|
|
345
|
-
|
|
346
|
-
FunctionDefTarget = decorateCallableWithNumba(FunctionDefTarget, parametersNumba)
|
|
347
|
-
|
|
348
|
-
if callableTarget == 'countSequential':
|
|
349
|
-
unpackerMy = UnpackArrayAccesses(indexMy, 'my')
|
|
350
|
-
FunctionDefTarget = cast(ast.FunctionDef, unpackerMy.visit(FunctionDefTarget))
|
|
351
|
-
ast.fix_missing_locations(FunctionDefTarget)
|
|
352
|
-
|
|
353
|
-
unpackerTrack = UnpackArrayAccesses(indexTrack, 'track')
|
|
354
|
-
FunctionDefTarget = cast(ast.FunctionDef, unpackerTrack.visit(FunctionDefTarget))
|
|
355
|
-
ast.fix_missing_locations(FunctionDefTarget)
|
|
356
|
-
|
|
357
|
-
moduleAST = ast.Module(body=cast(List[ast.stmt], allImports.makeListAst() + [FunctionDefTarget]), type_ignores=[])
|
|
358
|
-
ast.fix_missing_locations(moduleAST)
|
|
359
|
-
moduleSource = ast.unparse(moduleAST)
|
|
360
|
-
return moduleSource
|
|
361
|
-
|
|
362
|
-
def makeDispatcherNumba(pythonSource: str, callableTarget: str, listStuffYouOughtaKnow: List[youOughtaKnow]) -> str:
|
|
363
|
-
astSource = ast.parse(pythonSource)
|
|
364
|
-
|
|
365
|
-
for statement in astSource.body:
|
|
366
|
-
if isinstance(statement, (ast.Import, ast.ImportFrom)):
|
|
367
|
-
allImports.addAst(statement)
|
|
368
|
-
|
|
369
|
-
for stuff in listStuffYouOughtaKnow:
|
|
370
|
-
statement = stuff.astForCompetentProgrammers
|
|
371
|
-
if isinstance(statement, (ast.Import, ast.ImportFrom)):
|
|
372
|
-
allImports.addAst(statement)
|
|
373
|
-
|
|
374
|
-
FunctionDefTarget = next((node for node in astSource.body if isinstance(node, ast.FunctionDef) and node.name == callableTarget), None)
|
|
375
|
-
|
|
376
|
-
if not FunctionDefTarget:
|
|
377
|
-
raise ValueError(f"Could not find function {callableTarget} in source code")
|
|
378
|
-
|
|
379
|
-
# Zero-out the decorator list
|
|
380
|
-
FunctionDefTarget = Z0Z_UnhandledDecorators(FunctionDefTarget)
|
|
381
|
-
|
|
382
|
-
FunctionDefTarget = decorateCallableWithNumba(FunctionDefTarget, parametersNumbaFailEarly)
|
|
383
|
-
|
|
384
|
-
astModule = ast.Module( body=cast(List[ast.stmt], allImports.makeListAst()
|
|
385
|
-
+ [FunctionDefTarget]), type_ignores=[])
|
|
386
|
-
|
|
387
|
-
ast.fix_missing_locations(astModule)
|
|
388
|
-
return ast.unparse(astModule)
|
|
389
|
-
|
|
390
|
-
def makeNumbaOptimizedFlow(listCallablesInline: List[str], callableDispatcher: Optional[str] = None, algorithmSource: Optional[ModuleType] = None) -> None:
|
|
391
|
-
if not algorithmSource:
|
|
392
|
-
algorithmSource = getAlgorithmSource()
|
|
393
|
-
|
|
394
|
-
formatModuleNameDEFAULT = "numba_{callableTarget}"
|
|
395
|
-
|
|
396
|
-
# When I am a more competent programmer, I will make getPathFilenameWrite dependent on makeAstImport or vice versa,
|
|
397
|
-
# so the name of the physical file doesn't get out of whack with the name of the logical module.
|
|
398
|
-
def getPathFilenameWrite(callableTarget: str
|
|
399
|
-
, pathWrite: Optional[pathlib.Path] = None
|
|
400
|
-
, formatFilenameWrite: Optional[str] = None
|
|
401
|
-
) -> pathlib.Path:
|
|
402
|
-
if not pathWrite:
|
|
403
|
-
pathWrite = getPathSyntheticModules()
|
|
404
|
-
if not formatFilenameWrite:
|
|
405
|
-
formatFilenameWrite = formatModuleNameDEFAULT + '.py'
|
|
406
|
-
|
|
407
|
-
pathFilename = pathWrite / formatFilenameWrite.format(callableTarget=callableTarget)
|
|
408
|
-
return pathFilename
|
|
409
|
-
|
|
410
|
-
def makeAstImport(callableTarget: str
|
|
411
|
-
, packageName: Optional[str] = None
|
|
412
|
-
, subPackageName: Optional[str] = None
|
|
413
|
-
, moduleName: Optional[str] = None
|
|
414
|
-
, astNodeLogicalPathThingy: Optional[ast.AST] = None
|
|
415
|
-
) -> ast.ImportFrom:
|
|
416
|
-
"""Creates import AST node for synthetic modules."""
|
|
417
|
-
if astNodeLogicalPathThingy is None:
|
|
418
|
-
if packageName is None:
|
|
419
|
-
packageName = myPackageNameIs
|
|
420
|
-
if subPackageName is None:
|
|
421
|
-
subPackageName = moduleOfSyntheticModules
|
|
422
|
-
if moduleName is None:
|
|
423
|
-
moduleName = formatModuleNameDEFAULT.format(callableTarget=callableTarget)
|
|
424
|
-
module=f'{packageName}.{subPackageName}.{moduleName}'
|
|
425
|
-
else:
|
|
426
|
-
module = str(astNodeLogicalPathThingy)
|
|
427
|
-
return ast.ImportFrom(
|
|
428
|
-
module=module,
|
|
429
|
-
names=[ast.alias(name=callableTarget, asname=None)],
|
|
430
|
-
level=0
|
|
431
|
-
)
|
|
432
|
-
|
|
433
|
-
listStuffYouOughtaKnow: List[youOughtaKnow] = []
|
|
434
|
-
|
|
435
|
-
global allImports
|
|
436
|
-
for callableTarget in listCallablesInline:
|
|
437
|
-
allImports = UniversalImportTracker()
|
|
438
|
-
pythonSource = inspect.getsource(algorithmSource)
|
|
439
|
-
pythonSource = inlineOneCallable(pythonSource, callableTarget)
|
|
440
|
-
if not pythonSource:
|
|
441
|
-
raise Exception("Pylance, OMG! The sky is falling!")
|
|
442
|
-
|
|
443
|
-
pathFilename = getPathFilenameWrite(callableTarget)
|
|
444
|
-
|
|
445
|
-
listStuffYouOughtaKnow.append(youOughtaKnow(
|
|
446
|
-
callableSynthesized=callableTarget,
|
|
447
|
-
pathFilenameForMe=pathFilename,
|
|
448
|
-
astForCompetentProgrammers=makeAstImport(callableTarget)
|
|
449
|
-
))
|
|
450
|
-
pythonSource = autoflake.fix_code(pythonSource, ['mapFolding', 'numba', 'numpy'])
|
|
451
|
-
pathFilename.write_text(pythonSource)
|
|
452
|
-
|
|
453
|
-
# Generate dispatcher if requested
|
|
454
|
-
if callableDispatcher:
|
|
455
|
-
allImports = UniversalImportTracker()
|
|
456
|
-
pythonSource = inspect.getsource(algorithmSource)
|
|
457
|
-
pythonSource = makeDispatcherNumba(pythonSource, callableDispatcher, listStuffYouOughtaKnow)
|
|
458
|
-
if not pythonSource:
|
|
459
|
-
raise Exception("Pylance, OMG! The sky is falling!")
|
|
460
|
-
|
|
461
|
-
pathFilename = getPathFilenameWrite(callableDispatcher)
|
|
462
|
-
|
|
463
|
-
listStuffYouOughtaKnow.append(youOughtaKnow(
|
|
464
|
-
callableSynthesized=callableDispatcher,
|
|
465
|
-
pathFilenameForMe=pathFilename,
|
|
466
|
-
astForCompetentProgrammers=makeAstImport(callableDispatcher)
|
|
467
|
-
))
|
|
468
|
-
pythonSource = autoflake.fix_code(pythonSource, ['mapFolding', 'numba', 'numpy'])
|
|
469
|
-
pathFilename.write_text(pythonSource)
|
|
470
|
-
|
|
471
|
-
def makeStrRLEcompacted(arrayTarget: numpy.ndarray, identifierName: Optional[str]=None) -> str:
|
|
472
|
-
"""Converts a NumPy array into a compressed string representation using run-length encoding (RLE).
|
|
473
|
-
|
|
474
|
-
This function takes a NumPy array and converts it into an optimized string representation by:
|
|
475
|
-
1. Compressing consecutive sequences of numbers into range objects
|
|
476
|
-
2. Minimizing repeated zeros using array multiplication syntax
|
|
477
|
-
3. Converting the result into a valid Python array initialization statement
|
|
478
|
-
|
|
479
|
-
Parameters:
|
|
480
|
-
arrayTarget (numpy.ndarray): The input NumPy array to be converted
|
|
481
|
-
identifierName (str): The variable name to use in the output string
|
|
482
|
-
|
|
483
|
-
Returns:
|
|
484
|
-
str: A string containing Python code that recreates the input array in compressed form.
|
|
485
|
-
Format: "{identifierName} = numpy.array({compressed_data}, dtype=numpy.{dtype})"
|
|
486
|
-
"""
|
|
487
|
-
|
|
488
|
-
def compressRangesNDArrayNoFlatten(arraySlice):
|
|
489
|
-
if isinstance(arraySlice, numpy.ndarray) and arraySlice.ndim > 1:
|
|
490
|
-
return [compressRangesNDArrayNoFlatten(arraySlice[index]) for index in range(arraySlice.shape[0])]
|
|
491
|
-
elif isinstance(arraySlice, numpy.ndarray) and arraySlice.ndim == 1:
|
|
492
|
-
listWithRanges = []
|
|
493
|
-
for group in more_itertools.consecutive_groups(arraySlice.tolist()):
|
|
494
|
-
ImaSerious = list(group)
|
|
495
|
-
if len(ImaSerious) <= 4:
|
|
496
|
-
listWithRanges += ImaSerious
|
|
497
|
-
else:
|
|
498
|
-
ImaRange = [range(ImaSerious[0], ImaSerious[-1] + 1)]
|
|
499
|
-
listWithRanges += ImaRange
|
|
500
|
-
return listWithRanges
|
|
501
|
-
return arraySlice
|
|
502
|
-
|
|
503
|
-
arrayAsNestedLists = compressRangesNDArrayNoFlatten(arrayTarget)
|
|
504
|
-
|
|
505
|
-
stringMinimized = python_minifier.minify(str(arrayAsNestedLists))
|
|
506
|
-
commaZeroMaximum = arrayTarget.shape[-1] - 1
|
|
507
|
-
stringMinimized = stringMinimized.replace('[0' + ',0'*commaZeroMaximum + ']', '[0]*'+str(commaZeroMaximum+1))
|
|
508
|
-
for countZeros in range(commaZeroMaximum, 2, -1):
|
|
509
|
-
stringMinimized = stringMinimized.replace(',0'*countZeros + ']', ']+[0]*'+str(countZeros))
|
|
510
|
-
|
|
511
|
-
stringMinimized = stringMinimized.replace('range', '*range')
|
|
512
|
-
|
|
513
|
-
if identifierName:
|
|
514
|
-
return f"{identifierName} = array({stringMinimized}, dtype={arrayTarget.dtype})"
|
|
515
|
-
return stringMinimized
|
|
516
|
-
|
|
517
|
-
def moveArrayTo_body(FunctionDefTarget: ast.FunctionDef, astArg: ast.arg, argData: numpy.ndarray) -> ast.FunctionDef:
|
|
518
|
-
arrayType = type(argData)
|
|
519
|
-
moduleConstructor = arrayType.__module__
|
|
520
|
-
constructorName = arrayType.__name__
|
|
521
|
-
# NOTE hack
|
|
522
|
-
constructorName = constructorName.replace('ndarray', 'array')
|
|
523
|
-
argData_dtype: numpy.dtype = argData.dtype
|
|
524
|
-
argData_dtypeName = argData.dtype.name
|
|
525
|
-
|
|
526
|
-
allImports.addImportFromStr(moduleConstructor, constructorName)
|
|
527
|
-
allImports.addImportFromStr(moduleConstructor, argData_dtypeName)
|
|
528
|
-
|
|
529
|
-
onlyDataRLE = makeStrRLEcompacted(argData)
|
|
530
|
-
astStatement = cast(ast.Expr, ast.parse(onlyDataRLE).body[0])
|
|
531
|
-
dataAst = astStatement.value
|
|
532
|
-
|
|
533
|
-
arrayCall = ast.Call(
|
|
534
|
-
func=ast.Name(id=constructorName, ctx=ast.Load())
|
|
535
|
-
, args=[dataAst]
|
|
536
|
-
, keywords=[ast.keyword(arg='dtype' , value=ast.Name(id=argData_dtypeName , ctx=ast.Load()) ) ] )
|
|
537
|
-
|
|
538
|
-
assignment = ast.Assign( targets=[ast.Name(id=astArg.arg, ctx=ast.Store())], value=arrayCall )
|
|
539
|
-
FunctionDefTarget.body.insert(0, assignment)
|
|
540
|
-
FunctionDefTarget.args.args.remove(astArg)
|
|
541
|
-
|
|
542
|
-
return FunctionDefTarget
|
|
543
|
-
|
|
544
|
-
def evaluateArrayIn_body(FunctionDefTarget: ast.FunctionDef, astArg: ast.arg, argData: numpy.ndarray) -> ast.FunctionDef:
|
|
545
|
-
arrayType = type(argData)
|
|
546
|
-
moduleConstructor = arrayType.__module__
|
|
547
|
-
constructorName = arrayType.__name__
|
|
548
|
-
# NOTE hack
|
|
549
|
-
constructorName = constructorName.replace('ndarray', 'array')
|
|
550
|
-
allImports.addImportFromStr(moduleConstructor, constructorName)
|
|
551
|
-
|
|
552
|
-
for stmt in FunctionDefTarget.body.copy():
|
|
553
|
-
if isinstance(stmt, ast.Assign):
|
|
554
|
-
if isinstance(stmt.targets[0], ast.Name) and isinstance(stmt.value, ast.Subscript):
|
|
555
|
-
astAssignee: ast.Name = stmt.targets[0]
|
|
556
|
-
argData_dtypeName = hackSSOTdatatype(astAssignee.id)
|
|
557
|
-
allImports.addImportFromStr(moduleConstructor, argData_dtypeName)
|
|
558
|
-
astSubscript: ast.Subscript = stmt.value
|
|
559
|
-
if isinstance(astSubscript.value, ast.Name) and astSubscript.value.id == astArg.arg and isinstance(astSubscript.slice, ast.Attribute):
|
|
560
|
-
indexAs_astAttribute: ast.Attribute = astSubscript.slice
|
|
561
|
-
indexAsStr = ast.unparse(indexAs_astAttribute)
|
|
562
|
-
argDataSlice = argData[eval(indexAsStr)]
|
|
563
|
-
|
|
564
|
-
onlyDataRLE = makeStrRLEcompacted(argDataSlice)
|
|
565
|
-
astStatement = cast(ast.Expr, ast.parse(onlyDataRLE).body[0])
|
|
566
|
-
dataAst = astStatement.value
|
|
567
|
-
|
|
568
|
-
arrayCall = ast.Call(
|
|
569
|
-
func=ast.Name(id=constructorName, ctx=ast.Load()) , args=[dataAst]
|
|
570
|
-
, keywords=[ast.keyword(arg='dtype', value=ast.Name(id=argData_dtypeName, ctx=ast.Load()) ) ] )
|
|
571
|
-
|
|
572
|
-
assignment = ast.Assign( targets=[astAssignee], value=arrayCall )
|
|
573
|
-
FunctionDefTarget.body.insert(0, assignment)
|
|
574
|
-
FunctionDefTarget.body.remove(stmt)
|
|
575
|
-
|
|
576
|
-
FunctionDefTarget.args.args.remove(astArg)
|
|
577
|
-
return FunctionDefTarget
|
|
578
|
-
|
|
579
|
-
def evaluate_argIn_body(FunctionDefTarget: ast.FunctionDef, astArg: ast.arg, argData: numpy.ndarray, Z0Z_listChaff: List[str]) -> ast.FunctionDef:
|
|
580
|
-
moduleConstructor = datatypeModuleScalar
|
|
581
|
-
for stmt in FunctionDefTarget.body.copy():
|
|
582
|
-
if isinstance(stmt, ast.Assign):
|
|
583
|
-
if isinstance(stmt.targets[0], ast.Name) and isinstance(stmt.value, ast.Subscript):
|
|
584
|
-
astAssignee: ast.Name = stmt.targets[0]
|
|
585
|
-
argData_dtypeName = hackSSOTdatatype(astAssignee.id)
|
|
586
|
-
allImports.addImportFromStr(moduleConstructor, argData_dtypeName)
|
|
587
|
-
astSubscript: ast.Subscript = stmt.value
|
|
588
|
-
if isinstance(astSubscript.value, ast.Name) and astSubscript.value.id == astArg.arg and isinstance(astSubscript.slice, ast.Attribute):
|
|
589
|
-
indexAs_astAttribute: ast.Attribute = astSubscript.slice
|
|
590
|
-
indexAsStr = ast.unparse(indexAs_astAttribute)
|
|
591
|
-
argDataSlice: int = argData[eval(indexAsStr)].item()
|
|
592
|
-
astCall = ast.Call(func=ast.Name(id=argData_dtypeName, ctx=ast.Load()) , args=[ast.Constant(value=argDataSlice)], keywords=[])
|
|
593
|
-
assignment = ast.Assign(targets=[astAssignee], value=astCall)
|
|
594
|
-
if astAssignee.id not in Z0Z_listChaff:
|
|
595
|
-
FunctionDefTarget.body.insert(0, assignment)
|
|
596
|
-
FunctionDefTarget.body.remove(stmt)
|
|
597
|
-
FunctionDefTarget.args.args.remove(astArg)
|
|
598
|
-
return FunctionDefTarget
|
|
599
|
-
|
|
600
|
-
def evaluateAnnAssignIn_body(FunctionDefTarget: ast.FunctionDef) -> ast.FunctionDef:
|
|
601
|
-
moduleConstructor = datatypeModuleScalar
|
|
602
|
-
for stmt in FunctionDefTarget.body.copy():
|
|
603
|
-
if isinstance(stmt, ast.AnnAssign):
|
|
604
|
-
if isinstance(stmt.target, ast.Name) and isinstance(stmt.value, ast.Constant):
|
|
605
|
-
astAssignee: ast.Name = stmt.target
|
|
606
|
-
argData_dtypeName = hackSSOTdatatype(astAssignee.id)
|
|
607
|
-
allImports.addImportFromStr(moduleConstructor, argData_dtypeName)
|
|
608
|
-
astCall = ast.Call(func=ast.Name(id=argData_dtypeName, ctx=ast.Load()) , args=[stmt.value], keywords=[])
|
|
609
|
-
assignment = ast.Assign(targets=[astAssignee], value=astCall)
|
|
610
|
-
FunctionDefTarget.body.insert(0, assignment)
|
|
611
|
-
FunctionDefTarget.body.remove(stmt)
|
|
612
|
-
return FunctionDefTarget
|
|
613
|
-
|
|
614
|
-
def removeIdentifierFrom_body(FunctionDefTarget: ast.FunctionDef, astArg: ast.arg) -> ast.FunctionDef:
|
|
615
|
-
for stmt in FunctionDefTarget.body.copy():
|
|
616
|
-
if isinstance(stmt, ast.Assign):
|
|
617
|
-
if isinstance(stmt.targets[0], ast.Subscript) and isinstance(stmt.targets[0].value, ast.Name):
|
|
618
|
-
if stmt.targets[0].value.id == astArg.arg:
|
|
619
|
-
FunctionDefTarget.body.remove(stmt)
|
|
620
|
-
FunctionDefTarget.args.args.remove(astArg)
|
|
621
|
-
return FunctionDefTarget
|
|
622
|
-
|
|
623
|
-
def astObjectToAstConstant(FunctionDefTarget: ast.FunctionDef, object: str, value: int) -> ast.FunctionDef:
|
|
624
|
-
"""
|
|
625
|
-
Replaces nodes in astFunction matching the AST of the string `object`
|
|
626
|
-
with a constant node holding the provided value.
|
|
627
|
-
"""
|
|
628
|
-
targetExpression = ast.parse(object, mode='eval').body
|
|
629
|
-
targetDump = ast.dump(targetExpression, annotate_fields=False)
|
|
630
|
-
|
|
631
|
-
def findNode(node: ast.AST) -> bool:
|
|
632
|
-
return ast.dump(node, annotate_fields=False) == targetDump
|
|
633
|
-
|
|
634
|
-
def replaceWithConstant(node: ast.AST) -> ast.AST:
|
|
635
|
-
return ast.copy_location(ast.Constant(value=value), node)
|
|
636
|
-
|
|
637
|
-
transformer = NodeReplacer(findNode, replaceWithConstant)
|
|
638
|
-
newFunction = cast(ast.FunctionDef, transformer.visit(FunctionDefTarget))
|
|
639
|
-
ast.fix_missing_locations(newFunction)
|
|
640
|
-
return newFunction
|
|
641
|
-
|
|
642
|
-
def astNameToAstConstant(FunctionDefTarget: ast.FunctionDef, name: str, value: int) -> ast.FunctionDef:
|
|
643
|
-
def findName(node: ast.AST) -> bool:
|
|
644
|
-
return isinstance(node, ast.Name) and node.id == name
|
|
645
|
-
|
|
646
|
-
def replaceWithConstant(node: ast.AST) -> ast.AST:
|
|
647
|
-
return ast.copy_location(ast.Constant(value=value), node)
|
|
648
|
-
|
|
649
|
-
return cast(ast.FunctionDef, NodeReplacer(findName, replaceWithConstant).visit(FunctionDefTarget))
|
|
650
|
-
|
|
651
|
-
def makeDecorator(FunctionDefTarget: ast.FunctionDef, parametersNumba: Optional[ParametersNumba]=None) -> ast.FunctionDef:
|
|
652
|
-
# Use NodeReplacer to handle decorator cleanup
|
|
653
|
-
def isNumbaJitDecorator(node: ast.AST) -> bool:
|
|
654
|
-
return ((isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute)
|
|
655
|
-
and getattr(node.func.value, "id", None) == "numba" and node.func.attr == "jit")
|
|
656
|
-
or
|
|
657
|
-
(isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id == "jit"))
|
|
658
|
-
|
|
659
|
-
def extractNumbaParams(node: ast.Call) -> None:
|
|
660
|
-
nonlocal parametersNumba
|
|
661
|
-
if parametersNumba is None:
|
|
662
|
-
parametersNumbaExtracted: Dict[str, Any] = {}
|
|
663
|
-
for keywordItem in node.keywords:
|
|
664
|
-
if isinstance(keywordItem.value, ast.Constant) and keywordItem.arg is not None:
|
|
665
|
-
parametersNumbaExtracted[keywordItem.arg] = keywordItem.value.value
|
|
666
|
-
if parametersNumbaExtracted:
|
|
667
|
-
parametersNumba = ParametersNumba(parametersNumbaExtracted) # type: ignore
|
|
668
|
-
return None
|
|
669
|
-
|
|
670
|
-
# Remove existing numba decorators
|
|
671
|
-
decoratorCleaner = NodeReplacer(isNumbaJitDecorator, extractNumbaParams)
|
|
672
|
-
FunctionDefTarget = cast(ast.FunctionDef, decoratorCleaner.visit(FunctionDefTarget))
|
|
673
|
-
|
|
674
|
-
FunctionDefTarget = Z0Z_UnhandledDecorators(FunctionDefTarget)
|
|
675
|
-
allImports.addImportFromStr('numba', 'jit')
|
|
676
|
-
FunctionDefTarget = decorateCallableWithNumba(FunctionDefTarget, parametersNumba)
|
|
677
|
-
|
|
678
|
-
# Convert @numba.jit to @jit
|
|
679
|
-
def isNumbaJitCall(node: ast.AST) -> bool:
|
|
680
|
-
return (isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute) and node.func.attr == "jit")
|
|
681
|
-
|
|
682
|
-
def convertToPlainJit(node: ast.Call) -> ast.Call:
|
|
683
|
-
node.func = ast.Name(id="jit", ctx=ast.Load())
|
|
684
|
-
return node
|
|
685
|
-
|
|
686
|
-
jitSimplifier = NodeReplacer(isNumbaJitCall, convertToPlainJit)
|
|
687
|
-
return cast(ast.FunctionDef, jitSimplifier.visit(FunctionDefTarget))
|
|
688
|
-
|
|
689
|
-
def makeLauncher(callableTarget: str) -> ast.Module:
|
|
690
|
-
linesLaunch = f"""
|
|
1
|
+
"""I think this module is free of hardcoded values.
|
|
2
|
+
TODO: consolidate the logic in this module."""
|
|
3
|
+
from mapFolding.someAssemblyRequired.synthesizeNumbaGeneralized import *
|
|
4
|
+
|
|
5
|
+
def insertArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arrayTarget: numpy.ndarray, allImports: UniversalImportTracker, unrollSlices: Optional[int]=None) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
|
|
6
|
+
arrayType = type(arrayTarget)
|
|
7
|
+
moduleConstructor = arrayType.__module__
|
|
8
|
+
constructorName = arrayType.__name__
|
|
9
|
+
# NOTE hack
|
|
10
|
+
constructorName = constructorName.replace('ndarray', 'array')
|
|
11
|
+
argData_dtype: numpy.dtype = arrayTarget.dtype
|
|
12
|
+
argData_dtypeName = arrayTarget.dtype.name
|
|
13
|
+
|
|
14
|
+
allImports.addImportFromStr(moduleConstructor, constructorName)
|
|
15
|
+
allImports.addImportFromStr(moduleConstructor, argData_dtypeName)
|
|
16
|
+
|
|
17
|
+
def insertAssign(assignee: str, arraySlice: numpy.ndarray) -> None:
|
|
18
|
+
nonlocal FunctionDefTarget
|
|
19
|
+
onlyDataRLE = makeStrRLEcompacted(arraySlice) #NOTE
|
|
20
|
+
astStatement = cast(ast.Expr, ast.parse(onlyDataRLE).body[0])
|
|
21
|
+
dataAst = astStatement.value
|
|
22
|
+
|
|
23
|
+
arrayCall = Then.make_astCall(name=constructorName, args=[dataAst], dictionaryKeywords={'dtype': ast.Name(id=argData_dtypeName, ctx=ast.Load())})
|
|
24
|
+
|
|
25
|
+
assignment = ast.Assign(targets=[ast.Name(id=assignee, ctx=ast.Store())], value=arrayCall)#NOTE
|
|
26
|
+
FunctionDefTarget.body.insert(0, assignment)
|
|
27
|
+
|
|
28
|
+
if not unrollSlices:
|
|
29
|
+
insertAssign(identifier, arrayTarget)
|
|
30
|
+
else:
|
|
31
|
+
for index, arraySlice in enumerate(arrayTarget):
|
|
32
|
+
insertAssign(f"{identifier}_{index}", arraySlice)
|
|
33
|
+
|
|
34
|
+
return FunctionDefTarget, allImports
|
|
35
|
+
|
|
36
|
+
def findAndReplaceArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arrayTarget: numpy.ndarray, allImports: UniversalImportTracker) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
|
|
37
|
+
arrayType = type(arrayTarget)
|
|
38
|
+
moduleConstructor = arrayType.__module__
|
|
39
|
+
constructorName = arrayType.__name__
|
|
40
|
+
# NOTE hack
|
|
41
|
+
constructorName = constructorName.replace('ndarray', 'array')
|
|
42
|
+
allImports.addImportFromStr(moduleConstructor, constructorName)
|
|
43
|
+
|
|
44
|
+
for stmt in FunctionDefTarget.body.copy():
|
|
45
|
+
if isinstance(stmt, ast.Assign):
|
|
46
|
+
if isinstance(stmt.targets[0], ast.Name) and isinstance(stmt.value, ast.Subscript):
|
|
47
|
+
astAssignee: ast.Name = stmt.targets[0]
|
|
48
|
+
argData_dtypeName = hackSSOTdatatype(astAssignee.id)
|
|
49
|
+
allImports.addImportFromStr(moduleConstructor, argData_dtypeName)
|
|
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
|
+
argDataSlice = arrayTarget[eval(indexAsStr)]
|
|
55
|
+
|
|
56
|
+
onlyDataRLE = makeStrRLEcompacted(argDataSlice)
|
|
57
|
+
astStatement = cast(ast.Expr, ast.parse(onlyDataRLE).body[0])
|
|
58
|
+
dataAst = astStatement.value
|
|
59
|
+
|
|
60
|
+
arrayCall = Then.make_astCall(name=constructorName, args=[dataAst], dictionaryKeywords={'dtype': ast.Name(id=argData_dtypeName, ctx=ast.Load())})
|
|
61
|
+
|
|
62
|
+
assignment = ast.Assign( targets=[astAssignee], value=arrayCall )
|
|
63
|
+
FunctionDefTarget.body.insert(0, assignment)
|
|
64
|
+
FunctionDefTarget.body.remove(stmt)
|
|
65
|
+
return FunctionDefTarget, allImports
|
|
66
|
+
|
|
67
|
+
def findAndReplaceArraySubscriptIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arrayTarget: numpy.ndarray, Z0Z_listChaff: List[str], allImports: UniversalImportTracker) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
|
|
68
|
+
moduleConstructor = Z0Z_getDatatypeModuleScalar()
|
|
69
|
+
for stmt in FunctionDefTarget.body.copy():
|
|
70
|
+
if isinstance(stmt, ast.Assign):
|
|
71
|
+
if isinstance(stmt.targets[0], ast.Name) and isinstance(stmt.value, ast.Subscript):
|
|
72
|
+
astAssignee: ast.Name = stmt.targets[0]
|
|
73
|
+
argData_dtypeName = hackSSOTdatatype(astAssignee.id)
|
|
74
|
+
allImports.addImportFromStr(moduleConstructor, argData_dtypeName)
|
|
75
|
+
astSubscript: ast.Subscript = stmt.value
|
|
76
|
+
if isinstance(astSubscript.value, ast.Name) and astSubscript.value.id == identifier and isinstance(astSubscript.slice, ast.Attribute):
|
|
77
|
+
indexAs_astAttribute: ast.Attribute = astSubscript.slice
|
|
78
|
+
indexAsStr = ast.unparse(indexAs_astAttribute)
|
|
79
|
+
argDataSlice: int = arrayTarget[eval(indexAsStr)].item()
|
|
80
|
+
astCall = ast.Call(func=ast.Name(id=argData_dtypeName, ctx=ast.Load()) , args=[ast.Constant(value=argDataSlice)], keywords=[])
|
|
81
|
+
assignment = ast.Assign(targets=[astAssignee], value=astCall)
|
|
82
|
+
if astAssignee.id not in Z0Z_listChaff:
|
|
83
|
+
FunctionDefTarget.body.insert(0, assignment)
|
|
84
|
+
FunctionDefTarget.body.remove(stmt)
|
|
85
|
+
return FunctionDefTarget, allImports
|
|
86
|
+
|
|
87
|
+
def removeAssignTargetFrom_body(FunctionDefTarget: ast.FunctionDef, identifier: str) -> ast.FunctionDef:
|
|
88
|
+
# Remove assignment nodes where the target is either a Subscript referencing `identifier` or satisfies ifThis.nameIs(identifier).
|
|
89
|
+
def predicate(astNode: ast.AST) -> bool:
|
|
90
|
+
if not isinstance(astNode, ast.Assign) or not astNode.targets:
|
|
91
|
+
return False
|
|
92
|
+
targetNode = astNode.targets[0]
|
|
93
|
+
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) -> Optional[ast.stmt]:
|
|
95
|
+
# Returning None removes the node.
|
|
96
|
+
return None
|
|
97
|
+
FunctionDefSherpa = NodeReplacer(predicate, replacementBuilder).visit(FunctionDefTarget)
|
|
98
|
+
if not FunctionDefSherpa:
|
|
99
|
+
raise FREAKOUT("Dude, where's my function?")
|
|
100
|
+
else:
|
|
101
|
+
FunctionDefTarget = cast(ast.FunctionDef, FunctionDefSherpa)
|
|
102
|
+
ast.fix_missing_locations(FunctionDefTarget)
|
|
103
|
+
return FunctionDefTarget
|
|
104
|
+
|
|
105
|
+
def findAndReplaceAnnAssignIn_body(FunctionDefTarget: ast.FunctionDef, allImports: UniversalImportTracker) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
|
|
106
|
+
moduleConstructor = Z0Z_getDatatypeModuleScalar()
|
|
107
|
+
for stmt in FunctionDefTarget.body.copy():
|
|
108
|
+
if isinstance(stmt, ast.AnnAssign):
|
|
109
|
+
if isinstance(stmt.target, ast.Name) and isinstance(stmt.value, ast.Constant):
|
|
110
|
+
astAssignee: ast.Name = stmt.target
|
|
111
|
+
argData_dtypeName = hackSSOTdatatype(astAssignee.id)
|
|
112
|
+
allImports.addImportFromStr(moduleConstructor, argData_dtypeName)
|
|
113
|
+
astCall = ast.Call(func=ast.Name(id=argData_dtypeName, ctx=ast.Load()) , args=[stmt.value], keywords=[])
|
|
114
|
+
assignment = ast.Assign(targets=[astAssignee], value=astCall)
|
|
115
|
+
FunctionDefTarget.body.insert(0, assignment)
|
|
116
|
+
FunctionDefTarget.body.remove(stmt)
|
|
117
|
+
return FunctionDefTarget, allImports
|
|
118
|
+
|
|
119
|
+
def findThingyReplaceWithConstantIn_body(FunctionDefTarget: ast.FunctionDef, object: str, value: int) -> ast.FunctionDef:
|
|
120
|
+
"""
|
|
121
|
+
Replaces nodes in astFunction matching the AST of the string `object`
|
|
122
|
+
with a constant node holding the provided value.
|
|
123
|
+
"""
|
|
124
|
+
targetExpression = ast.parse(object, mode='eval').body
|
|
125
|
+
targetDump = ast.dump(targetExpression, annotate_fields=False)
|
|
126
|
+
|
|
127
|
+
def findNode(node: ast.AST) -> bool:
|
|
128
|
+
return ast.dump(node, annotate_fields=False) == targetDump
|
|
129
|
+
|
|
130
|
+
def replaceWithConstant(node: ast.AST) -> ast.AST:
|
|
131
|
+
return ast.copy_location(ast.Constant(value=value), node)
|
|
132
|
+
|
|
133
|
+
transformer = NodeReplacer(findNode, replaceWithConstant)
|
|
134
|
+
newFunction = cast(ast.FunctionDef, transformer.visit(FunctionDefTarget))
|
|
135
|
+
ast.fix_missing_locations(newFunction)
|
|
136
|
+
return newFunction
|
|
137
|
+
|
|
138
|
+
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
|
+
def replaceWithConstant(node: ast.AST) -> ast.AST:
|
|
143
|
+
return ast.copy_location(ast.Constant(value=value), node)
|
|
144
|
+
|
|
145
|
+
return cast(ast.FunctionDef, NodeReplacer(findName, replaceWithConstant).visit(FunctionDefTarget))
|
|
146
|
+
|
|
147
|
+
def insertReturnStatementIn_body(FunctionDefTarget: ast.FunctionDef, arrayTarget: numpy.ndarray, allImports: UniversalImportTracker) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
|
|
148
|
+
"""Add multiplication and return statement to function, properly constructing AST nodes."""
|
|
149
|
+
# Create AST for multiplication operation
|
|
150
|
+
multiplicand = Z0Z_identifierCountFolds
|
|
151
|
+
datatype = hackSSOTdatatype(multiplicand)
|
|
152
|
+
multiplyOperation = ast.BinOp(
|
|
153
|
+
left=ast.Name(id=multiplicand, ctx=ast.Load()),
|
|
154
|
+
op=ast.Mult(), right=ast.Constant(value=int(arrayTarget[-1])))
|
|
155
|
+
|
|
156
|
+
returnStatement = ast.Return(value=multiplyOperation)
|
|
157
|
+
|
|
158
|
+
datatypeModuleScalar = Z0Z_getDatatypeModuleScalar()
|
|
159
|
+
allImports.addImportFromStr(datatypeModuleScalar, datatype)
|
|
160
|
+
|
|
161
|
+
FunctionDefTarget.body.append(returnStatement)
|
|
162
|
+
|
|
163
|
+
return FunctionDefTarget, allImports
|
|
164
|
+
|
|
165
|
+
def findAndReplaceWhileLoopIn_body(FunctionDefTarget: ast.FunctionDef, iteratorName: str, iterationsTotal: int) -> Any:
|
|
166
|
+
"""
|
|
167
|
+
Unroll all nested while loops matching the condition that their test uses `iteratorName`.
|
|
168
|
+
"""
|
|
169
|
+
# Helper transformer to replace iterator occurrences with a constant.
|
|
170
|
+
class ReplaceIterator(ast.NodeTransformer):
|
|
171
|
+
def __init__(self, iteratorName: str, constantValue: int) -> None:
|
|
172
|
+
super().__init__()
|
|
173
|
+
self.iteratorName = iteratorName
|
|
174
|
+
self.constantValue = constantValue
|
|
175
|
+
|
|
176
|
+
def visit_Name(self, node: ast.Name) -> ast.AST:
|
|
177
|
+
if node.id == self.iteratorName:
|
|
178
|
+
return ast.copy_location(ast.Constant(value=self.constantValue), node)
|
|
179
|
+
return self.generic_visit(node)
|
|
180
|
+
|
|
181
|
+
# NodeTransformer that finds while loops (even if deeply nested) and unrolls them.
|
|
182
|
+
class WhileLoopUnroller(ast.NodeTransformer):
|
|
183
|
+
def __init__(self, iteratorName: str, iterationsTotal: int) -> None:
|
|
184
|
+
super().__init__()
|
|
185
|
+
self.iteratorName = iteratorName
|
|
186
|
+
self.iterationsTotal = iterationsTotal
|
|
187
|
+
|
|
188
|
+
def visit_While(self, node: ast.While) -> List[ast.stmt]:
|
|
189
|
+
# Check if the while loop's test uses the iterator.
|
|
190
|
+
if isinstance(node.test, ast.Compare) and ifThis.nameIs(self.iteratorName)(node.test.left):
|
|
191
|
+
# Recurse the while loop body and remove AugAssign that increments the iterator.
|
|
192
|
+
cleanBodyStatements: List[ast.stmt] = []
|
|
193
|
+
for loopStatement in node.body:
|
|
194
|
+
# Recursively visit nested statements.
|
|
195
|
+
visitedStatement = self.visit(loopStatement)
|
|
196
|
+
# Remove direct AugAssign: iterator += 1.
|
|
197
|
+
if (isinstance(loopStatement, ast.AugAssign) and
|
|
198
|
+
isinstance(loopStatement.target, ast.Name) and
|
|
199
|
+
loopStatement.target.id == self.iteratorName and
|
|
200
|
+
isinstance(loopStatement.op, ast.Add) and
|
|
201
|
+
isinstance(loopStatement.value, ast.Constant) and
|
|
202
|
+
loopStatement.value.value == 1):
|
|
203
|
+
continue
|
|
204
|
+
cleanBodyStatements.append(visitedStatement)
|
|
205
|
+
|
|
206
|
+
newStatements: List[ast.stmt] = []
|
|
207
|
+
# Unroll using the filtered body.
|
|
208
|
+
for iterationIndex in range(self.iterationsTotal):
|
|
209
|
+
for loopStatement in cleanBodyStatements:
|
|
210
|
+
copiedStatement = copy.deepcopy(loopStatement)
|
|
211
|
+
replacer = ReplaceIterator(self.iteratorName, iterationIndex)
|
|
212
|
+
newStatement = replacer.visit(copiedStatement)
|
|
213
|
+
ast.fix_missing_locations(newStatement)
|
|
214
|
+
newStatements.append(newStatement)
|
|
215
|
+
# Optionally, process the orelse block.
|
|
216
|
+
if node.orelse:
|
|
217
|
+
for elseStmt in node.orelse:
|
|
218
|
+
visitedElse = self.visit(elseStmt)
|
|
219
|
+
if isinstance(visitedElse, list):
|
|
220
|
+
newStatements.extend(visitedElse)
|
|
221
|
+
else:
|
|
222
|
+
newStatements.append(visitedElse)
|
|
223
|
+
return newStatements
|
|
224
|
+
return [cast(ast.stmt, self.generic_visit(node))]
|
|
225
|
+
|
|
226
|
+
newFunctionDef = WhileLoopUnroller(iteratorName, iterationsTotal).visit(FunctionDefTarget)
|
|
227
|
+
ast.fix_missing_locations(newFunctionDef)
|
|
228
|
+
return newFunctionDef
|
|
229
|
+
|
|
230
|
+
def makeLauncherBasicJobNumba(callableTarget: str, pathFilenameFoldsTotal: pathlib.Path) -> ast.Module:
|
|
231
|
+
linesLaunch = f"""
|
|
691
232
|
if __name__ == '__main__':
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
233
|
+
import time
|
|
234
|
+
timeStart = time.perf_counter()
|
|
235
|
+
foldsTotal = {callableTarget}()
|
|
236
|
+
print(foldsTotal, time.perf_counter() - timeStart)
|
|
237
|
+
writeStream = open('{pathFilenameFoldsTotal.as_posix()}', 'w')
|
|
238
|
+
writeStream.write(str(foldsTotal))
|
|
239
|
+
writeStream.close()
|
|
696
240
|
"""
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
if
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
allImports = UniversalImportTracker()
|
|
805
|
-
pathFilenameModule = writeJobNumba(listDimensions, 'countSequential', algorithmSource, parametersNumbaDEFAULT)
|
|
241
|
+
return ast.parse(linesLaunch)
|
|
242
|
+
|
|
243
|
+
def makeAstModuleForOneCallable(pythonSource: str, callableTarget: str, parametersNumba: Optional[ParametersNumba]=None, inlineCallables: Optional[bool]=False , unpackArrays: Optional[bool]=False , allImports: Optional[UniversalImportTracker]=None ) -> str:
|
|
244
|
+
astModule: ast.Module = ast.parse(pythonSource, type_comments=True)
|
|
245
|
+
|
|
246
|
+
if allImports is None:
|
|
247
|
+
allImports = UniversalImportTracker()
|
|
248
|
+
for statement in astModule.body:
|
|
249
|
+
if isinstance(statement, (ast.Import, ast.ImportFrom)):
|
|
250
|
+
allImports.addAst(statement)
|
|
251
|
+
|
|
252
|
+
if inlineCallables:
|
|
253
|
+
dictionaryFunctionDef = {statement.name: statement for statement in astModule.body if isinstance(statement, ast.FunctionDef)}
|
|
254
|
+
callableInlinerWorkhorse = RecursiveInliner(dictionaryFunctionDef)
|
|
255
|
+
FunctionDefTarget = callableInlinerWorkhorse.inlineFunctionBody(callableTarget)
|
|
256
|
+
else:
|
|
257
|
+
FunctionDefTarget = next((node for node in astModule.body if isinstance(node, ast.FunctionDef) and node.name == callableTarget), None)
|
|
258
|
+
if not FunctionDefTarget:
|
|
259
|
+
raise ValueError(f"Could not find function {callableTarget} in source code")
|
|
260
|
+
|
|
261
|
+
ast.fix_missing_locations(FunctionDefTarget)
|
|
262
|
+
|
|
263
|
+
FunctionDefTarget, allImports = decorateCallableWithNumba(FunctionDefTarget, allImports, parametersNumba)
|
|
264
|
+
|
|
265
|
+
# NOTE vestigial hardcoding
|
|
266
|
+
if unpackArrays:
|
|
267
|
+
for tupleUnpack in [(indexMy, 'my'), (indexTrack, 'track')]:
|
|
268
|
+
unpacker = UnpackArrays(*tupleUnpack)
|
|
269
|
+
FunctionDefTarget = cast(ast.FunctionDef, unpacker.visit(FunctionDefTarget))
|
|
270
|
+
ast.fix_missing_locations(FunctionDefTarget)
|
|
271
|
+
|
|
272
|
+
astModule = ast.Module(body=cast(List[ast.stmt], allImports.makeListAst() + [FunctionDefTarget]), type_ignores=[])
|
|
273
|
+
ast.fix_missing_locations(astModule)
|
|
274
|
+
return ast.unparse(astModule)
|
|
275
|
+
|
|
276
|
+
def decorateCallableWithNumba(FunctionDefTarget: ast.FunctionDef, allImports: UniversalImportTracker, parametersNumba: Optional[ParametersNumba]=None) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
|
|
277
|
+
def Z0Z_UnhandledDecorators(astCallable: ast.FunctionDef) -> ast.FunctionDef:
|
|
278
|
+
# TODO: more explicit handling of decorators. I'm able to ignore this because I know `algorithmSource` doesn't have any decorators.
|
|
279
|
+
for decoratorItem in astCallable.decorator_list.copy():
|
|
280
|
+
import warnings
|
|
281
|
+
astCallable.decorator_list.remove(decoratorItem)
|
|
282
|
+
warnings.warn(f"Removed decorator {ast.unparse(decoratorItem)} from {astCallable.name}")
|
|
283
|
+
return astCallable
|
|
284
|
+
|
|
285
|
+
def make_arg4parameter(signatureElement: ast.arg) -> ast.Subscript | None:
|
|
286
|
+
if isinstance(signatureElement.annotation, ast.Subscript) and isinstance(signatureElement.annotation.slice, ast.Tuple):
|
|
287
|
+
annotationShape = signatureElement.annotation.slice.elts[0]
|
|
288
|
+
if isinstance(annotationShape, ast.Subscript) and isinstance(annotationShape.slice, ast.Tuple):
|
|
289
|
+
shapeAsListSlices = [ast.Slice() for axis in range(len(annotationShape.slice.elts))]
|
|
290
|
+
shapeAsListSlices[-1] = ast.Slice(step=ast.Constant(value=1))
|
|
291
|
+
shapeAST = ast.Tuple(elts=list(shapeAsListSlices), ctx=ast.Load())
|
|
292
|
+
else:
|
|
293
|
+
shapeAST = ast.Slice(step=ast.Constant(value=1))
|
|
294
|
+
|
|
295
|
+
annotationDtype = signatureElement.annotation.slice.elts[1]
|
|
296
|
+
if (isinstance(annotationDtype, ast.Subscript) and isinstance(annotationDtype.slice, ast.Attribute)):
|
|
297
|
+
datatypeAST = annotationDtype.slice.attr
|
|
298
|
+
else:
|
|
299
|
+
datatypeAST = None
|
|
300
|
+
|
|
301
|
+
ndarrayName = signatureElement.arg
|
|
302
|
+
Z0Z_hacky_dtype = hackSSOTdatatype(ndarrayName)
|
|
303
|
+
datatype_attr = datatypeAST or Z0Z_hacky_dtype
|
|
304
|
+
allImports.addImportFromStr(datatypeModuleDecorator, datatype_attr)
|
|
305
|
+
datatypeNumba = ast.Name(id=datatype_attr, ctx=ast.Load())
|
|
306
|
+
|
|
307
|
+
return ast.Subscript(value=datatypeNumba, slice=shapeAST, ctx=ast.Load())
|
|
308
|
+
return
|
|
309
|
+
|
|
310
|
+
datatypeModuleDecorator = Z0Z_getDatatypeModuleScalar()
|
|
311
|
+
list_argsDecorator: Sequence[ast.expr] = []
|
|
312
|
+
|
|
313
|
+
list_arg4signature_or_function: List[ast.expr] = []
|
|
314
|
+
for parameter in FunctionDefTarget.args.args:
|
|
315
|
+
signatureElement = make_arg4parameter(parameter)
|
|
316
|
+
if signatureElement:
|
|
317
|
+
list_arg4signature_or_function.append(signatureElement)
|
|
318
|
+
|
|
319
|
+
if FunctionDefTarget.returns and isinstance(FunctionDefTarget.returns, ast.Name):
|
|
320
|
+
theReturn: ast.Name = FunctionDefTarget.returns
|
|
321
|
+
list_argsDecorator = [cast(ast.expr, ast.Call(func=ast.Name(id=theReturn.id, ctx=ast.Load())
|
|
322
|
+
, args=list_arg4signature_or_function if list_arg4signature_or_function else [] , keywords=[] ) )]
|
|
323
|
+
elif list_arg4signature_or_function:
|
|
324
|
+
list_argsDecorator = [cast(ast.expr, ast.Tuple(elts=list_arg4signature_or_function, ctx=ast.Load()))]
|
|
325
|
+
|
|
326
|
+
for decorator in FunctionDefTarget.decorator_list.copy():
|
|
327
|
+
if thisIsAnyNumbaJitDecorator(decorator):
|
|
328
|
+
decorator = cast(ast.Call, decorator)
|
|
329
|
+
if parametersNumba is None:
|
|
330
|
+
parametersNumbaSherpa = Then.copy_astCallKeywords(decorator)
|
|
331
|
+
if (HunterIsSureThereAreBetterWaysToDoThis := True):
|
|
332
|
+
if parametersNumbaSherpa:
|
|
333
|
+
parametersNumba = cast(ParametersNumba, parametersNumbaSherpa)
|
|
334
|
+
FunctionDefTarget.decorator_list.remove(decorator)
|
|
335
|
+
|
|
336
|
+
FunctionDefTarget = Z0Z_UnhandledDecorators(FunctionDefTarget)
|
|
337
|
+
if parametersNumba is None:
|
|
338
|
+
parametersNumba = parametersNumbaDEFAULT
|
|
339
|
+
listDecoratorKeywords = [ast.keyword(arg=parameterName, value=ast.Constant(value=parameterValue)) for parameterName, parameterValue in parametersNumba.items()]
|
|
340
|
+
|
|
341
|
+
decoratorModule = Z0Z_getDatatypeModuleScalar()
|
|
342
|
+
decoratorCallable = Z0Z_getDecoratorCallable()
|
|
343
|
+
allImports.addImportFromStr(decoratorModule, decoratorCallable)
|
|
344
|
+
astDecorator = Then.make_astCall(decoratorCallable, list_argsDecorator, listDecoratorKeywords, None)
|
|
345
|
+
|
|
346
|
+
FunctionDefTarget.decorator_list = [astDecorator]
|
|
347
|
+
return FunctionDefTarget, allImports
|