mapFolding 0.3.9__py3-none-any.whl → 0.3.11__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 +27 -9
- mapFolding/basecamp.py +1 -1
- mapFolding/beDRY.py +25 -13
- mapFolding/oeis.py +81 -71
- mapFolding/someAssemblyRequired/makeJob.py +5 -6
- mapFolding/someAssemblyRequired/synthesizeModuleJAX.py +2 -2
- mapFolding/someAssemblyRequired/synthesizeNumba.py +805 -0
- mapFolding/syntheticModules/numba_countInitialize.py +10 -7
- mapFolding/syntheticModules/numba_countParallel.py +14 -9
- mapFolding/syntheticModules/numba_countSequential.py +11 -7
- mapFolding/syntheticModules/numba_doTheNeedful.py +10 -23
- mapFolding/theDao.py +66 -50
- mapFolding/theSSOT.py +21 -39
- mapFolding/theSSOTnumba.py +116 -0
- {mapFolding-0.3.9.dist-info → mapFolding-0.3.11.dist-info}/METADATA +3 -7
- mapFolding-0.3.11.dist-info/RECORD +39 -0
- tests/conftest.py +2 -43
- tests/test_oeis.py +7 -66
- tests/test_other.py +30 -29
- mapFolding/someAssemblyRequired/synthesizeModuleJobNumba.py +0 -212
- mapFolding/someAssemblyRequired/synthesizeModulesNumba.py +0 -506
- mapFolding/syntheticModules/__init__.py +0 -3
- mapFolding-0.3.9.dist-info/RECORD +0 -40
- {mapFolding-0.3.9.dist-info → mapFolding-0.3.11.dist-info}/LICENSE +0 -0
- {mapFolding-0.3.9.dist-info → mapFolding-0.3.11.dist-info}/WHEEL +0 -0
- {mapFolding-0.3.9.dist-info → mapFolding-0.3.11.dist-info}/entry_points.txt +0 -0
- {mapFolding-0.3.9.dist-info → mapFolding-0.3.11.dist-info}/top_level.txt +0 -0
|
@@ -1,506 +0,0 @@
|
|
|
1
|
-
from mapFolding import (
|
|
2
|
-
EnumIndices,
|
|
3
|
-
getAlgorithmSource,
|
|
4
|
-
getPathPackage,
|
|
5
|
-
getPathSyntheticModules,
|
|
6
|
-
hackSSOTdatatype,
|
|
7
|
-
hackSSOTdtype,
|
|
8
|
-
indexMy,
|
|
9
|
-
indexTrack,
|
|
10
|
-
moduleOfSyntheticModules,
|
|
11
|
-
myPackageNameIs,
|
|
12
|
-
ParametersNumba,
|
|
13
|
-
parametersNumbaDEFAULT,
|
|
14
|
-
setDatatypeElephino,
|
|
15
|
-
setDatatypeFoldsTotal,
|
|
16
|
-
setDatatypeLeavesTotal,
|
|
17
|
-
setDatatypeModule,
|
|
18
|
-
)
|
|
19
|
-
from typing import cast, Dict, List, Optional, Sequence, Set, Tuple, Type, Union
|
|
20
|
-
from types import ModuleType
|
|
21
|
-
from collections import namedtuple
|
|
22
|
-
import ast
|
|
23
|
-
import inspect
|
|
24
|
-
import numba
|
|
25
|
-
import numpy
|
|
26
|
-
import pathlib
|
|
27
|
-
|
|
28
|
-
youOughtaKnow = namedtuple('youOughtaKnow', ['callableSynthesized', 'pathFilenameForMe', 'astForCompetentProgrammers'])
|
|
29
|
-
|
|
30
|
-
"""TODO
|
|
31
|
-
Convert types
|
|
32
|
-
e.g. `groupsOfFolds: int = 0` to `groupsOfFolds = numba.types.{datatypeLarge}(0)`
|
|
33
|
-
This isn't necessary for Numba, but I may the infrastructure for other compilers or paradigms."""
|
|
34
|
-
|
|
35
|
-
class RecursiveInliner(ast.NodeTransformer):
|
|
36
|
-
"""
|
|
37
|
-
Class RecursiveInliner:
|
|
38
|
-
A custom AST NodeTransformer designed to recursively inline function calls from a given dictionary
|
|
39
|
-
of function definitions into the AST. Once a particular function has been inlined, it is marked
|
|
40
|
-
as completed to avoid repeated inlining. This transformation modifies the AST in-place by substituting
|
|
41
|
-
eligible function calls with the body of their corresponding function.
|
|
42
|
-
Attributes:
|
|
43
|
-
dictionaryFunctions (Dict[str, ast.FunctionDef]):
|
|
44
|
-
A mapping of function name to its AST definition, used as a source for inlining.
|
|
45
|
-
callablesCompleted (Set[str]):
|
|
46
|
-
A set to track function names that have already been inlined to prevent multiple expansions.
|
|
47
|
-
Methods:
|
|
48
|
-
inlineFunctionBody(callableTargetName: str) -> Optional[ast.FunctionDef]:
|
|
49
|
-
Retrieves the AST definition for a given function name from dictionaryFunctions
|
|
50
|
-
and recursively inlines any function calls within it. Returns the function definition
|
|
51
|
-
that was inlined or None if the function was already processed.
|
|
52
|
-
visit_Call(callNode: ast.Call) -> ast.AST:
|
|
53
|
-
Inspects calls within the AST. If a function call matches one in dictionaryFunctions,
|
|
54
|
-
it is replaced by the inlined body. If the last statement in the inlined body is a return
|
|
55
|
-
or an expression, that value or expression is substituted; otherwise, a constant is returned.
|
|
56
|
-
visit_Expr(node: ast.Expr) -> Union[ast.AST, List[ast.AST]]:
|
|
57
|
-
Handles expression nodes in the AST. If the expression is a function call from
|
|
58
|
-
dictionaryFunctions, its statements are expanded in place, effectively inlining
|
|
59
|
-
the called function's statements into the surrounding context.
|
|
60
|
-
"""
|
|
61
|
-
def __init__(self, dictionaryFunctions: Dict[str, ast.FunctionDef]):
|
|
62
|
-
self.dictionaryFunctions = dictionaryFunctions
|
|
63
|
-
self.callablesCompleted: Set[str] = set()
|
|
64
|
-
|
|
65
|
-
def inlineFunctionBody(self, callableTargetName: str) -> Optional[ast.FunctionDef]:
|
|
66
|
-
if (callableTargetName in self.callablesCompleted):
|
|
67
|
-
return None
|
|
68
|
-
|
|
69
|
-
self.callablesCompleted.add(callableTargetName)
|
|
70
|
-
inlineDefinition = self.dictionaryFunctions[callableTargetName]
|
|
71
|
-
for astNode in ast.walk(inlineDefinition):
|
|
72
|
-
self.visit(astNode)
|
|
73
|
-
return inlineDefinition
|
|
74
|
-
|
|
75
|
-
def visit_Call(self, node: ast.Call) -> ast.AST:
|
|
76
|
-
callNodeVisited = self.generic_visit(node)
|
|
77
|
-
if (isinstance(callNodeVisited, ast.Call) and isinstance(callNodeVisited.func, ast.Name) and callNodeVisited.func.id in self.dictionaryFunctions):
|
|
78
|
-
inlineDefinition = self.inlineFunctionBody(callNodeVisited.func.id)
|
|
79
|
-
if (inlineDefinition and inlineDefinition.body):
|
|
80
|
-
statementTerminating = inlineDefinition.body[-1]
|
|
81
|
-
if (isinstance(statementTerminating, ast.Return) and statementTerminating.value is not None):
|
|
82
|
-
return self.visit(statementTerminating.value)
|
|
83
|
-
elif (isinstance(statementTerminating, ast.Expr) and statementTerminating.value is not None):
|
|
84
|
-
return self.visit(statementTerminating.value)
|
|
85
|
-
return ast.Constant(value=None)
|
|
86
|
-
return callNodeVisited
|
|
87
|
-
|
|
88
|
-
def visit_Expr(self, node: ast.Expr) -> Union[ast.AST, List[ast.AST]]:
|
|
89
|
-
if (isinstance(node.value, ast.Call)):
|
|
90
|
-
if (isinstance(node.value.func, ast.Name) and node.value.func.id in self.dictionaryFunctions):
|
|
91
|
-
inlineDefinition = self.inlineFunctionBody(node.value.func.id)
|
|
92
|
-
if (inlineDefinition):
|
|
93
|
-
return [self.visit(stmt) for stmt in inlineDefinition.body]
|
|
94
|
-
return self.generic_visit(node)
|
|
95
|
-
|
|
96
|
-
def decorateCallableWithNumba(astCallable: ast.FunctionDef, parallel: bool=False) -> ast.FunctionDef:
|
|
97
|
-
"""
|
|
98
|
-
Decorates an AST function definition with Numba JIT compilation parameters.
|
|
99
|
-
|
|
100
|
-
This function processes an AST FunctionDef node and adds Numba-specific decorators
|
|
101
|
-
for JIT compilation. It handles array parameter typing and compilation options.
|
|
102
|
-
|
|
103
|
-
Parameters
|
|
104
|
-
----------
|
|
105
|
-
|
|
106
|
-
astCallable : ast.FunctionDef
|
|
107
|
-
The AST node representing the function to be decorated with Numba JIT.
|
|
108
|
-
parallel : bool, optional
|
|
109
|
-
Whether to enable parallel execution in Numba compilation.
|
|
110
|
-
Default is False.
|
|
111
|
-
|
|
112
|
-
Returns
|
|
113
|
-
-------
|
|
114
|
-
|
|
115
|
-
ast.FunctionDef
|
|
116
|
-
The modified AST function definition node with added Numba decorators.
|
|
117
|
-
|
|
118
|
-
Notes
|
|
119
|
-
-----
|
|
120
|
-
The function performs the following main tasks:
|
|
121
|
-
1. Processes function parameters to create Numba-compatible type signatures
|
|
122
|
-
2. Constructs appropriate Numba compilation parameters
|
|
123
|
-
3. Creates and attaches a @numba.jit decorator to the function
|
|
124
|
-
Special handling is included for the 'countInitialize' function, which receives
|
|
125
|
-
empty compilation parameters.
|
|
126
|
-
The function relies on external parameters:
|
|
127
|
-
- parametersNumbaDEFAULT: Default Numba compilation parameters
|
|
128
|
-
- ParametersNumba: Class/type for handling Numba parameters
|
|
129
|
-
- hackSSOTdatatype: Function for determining default datatypes
|
|
130
|
-
"""
|
|
131
|
-
def makeNumbaParameterSignatureElement(signatureElement: ast.arg):
|
|
132
|
-
"""
|
|
133
|
-
Converts an AST function parameter signature element into a Numba-compatible type annotation.
|
|
134
|
-
|
|
135
|
-
This function processes parameter annotations for array types, handling both shape and datatype
|
|
136
|
-
specifications. It supports multi-dimensional arrays through tuple-based shape definitions and
|
|
137
|
-
various numeric datatypes.
|
|
138
|
-
|
|
139
|
-
Parameters
|
|
140
|
-
----------
|
|
141
|
-
signatureElement : ast.arg
|
|
142
|
-
The AST argument node containing the parameter's name and type annotation.
|
|
143
|
-
Expected annotation format: Type[shape_tuple, dtype]
|
|
144
|
-
where shape_tuple can be either a single dimension or a tuple of dimensions,
|
|
145
|
-
and dtype specifies the data type.
|
|
146
|
-
|
|
147
|
-
Returns
|
|
148
|
-
-------
|
|
149
|
-
ast.Subscript
|
|
150
|
-
A Numba-compatible type annotation as an AST node, representing an array type
|
|
151
|
-
with the specified shape and datatype.
|
|
152
|
-
|
|
153
|
-
Notes
|
|
154
|
-
-----
|
|
155
|
-
The function handles two main cases for shape specifications:
|
|
156
|
-
1. Multi-dimensional arrays with tuple-based shapes
|
|
157
|
-
2. Single-dimension arrays with simple slice notation
|
|
158
|
-
The datatype can be either explicitly specified in the annotation or determined
|
|
159
|
-
through a fallback mechanism using hackSSOTdatatype().
|
|
160
|
-
"""
|
|
161
|
-
if isinstance(signatureElement.annotation, ast.Subscript) and isinstance(signatureElement.annotation.slice, ast.Tuple):
|
|
162
|
-
annotationShape = signatureElement.annotation.slice.elts[0]
|
|
163
|
-
if isinstance(annotationShape, ast.Subscript) and isinstance(annotationShape.slice, ast.Tuple):
|
|
164
|
-
shapeAsListSlices: Sequence[ast.expr] = [ast.Slice() for axis in range(len(annotationShape.slice.elts))]
|
|
165
|
-
shapeAsListSlices[-1] = ast.Slice(step=ast.Constant(value=1))
|
|
166
|
-
shapeAST = ast.Tuple(elts=list(shapeAsListSlices), ctx=ast.Load())
|
|
167
|
-
else:
|
|
168
|
-
shapeAST = ast.Slice(step=ast.Constant(value=1))
|
|
169
|
-
|
|
170
|
-
annotationDtype = signatureElement.annotation.slice.elts[1]
|
|
171
|
-
if (isinstance(annotationDtype, ast.Subscript) and isinstance(annotationDtype.slice, ast.Attribute)):
|
|
172
|
-
datatypeAST = annotationDtype.slice.attr
|
|
173
|
-
else:
|
|
174
|
-
datatypeAST = None
|
|
175
|
-
|
|
176
|
-
ndarrayName = signatureElement.arg
|
|
177
|
-
Z0Z_hacky_dtype = hackSSOTdatatype(ndarrayName)
|
|
178
|
-
datatype_attr = datatypeAST or Z0Z_hacky_dtype
|
|
179
|
-
|
|
180
|
-
datatypeNumba = ast.Attribute(value=ast.Name(id='numba', ctx=ast.Load()), attr=datatype_attr, ctx=ast.Load())
|
|
181
|
-
|
|
182
|
-
return ast.Subscript(value=datatypeNumba, slice=shapeAST, ctx=ast.Load())
|
|
183
|
-
|
|
184
|
-
# TODO: more explicit handling of decorators. I'm able to ignore this because I know `algorithmSource` doesn't have any decorators.
|
|
185
|
-
# callableSourceDecorators = [decorator for decorator in callableInlined.decorator_list]
|
|
186
|
-
|
|
187
|
-
listNumbaParameterSignature: Sequence[ast.expr] = []
|
|
188
|
-
for parameter in astCallable.args.args:
|
|
189
|
-
signatureElement = makeNumbaParameterSignatureElement(parameter)
|
|
190
|
-
if (signatureElement):
|
|
191
|
-
listNumbaParameterSignature.append(signatureElement)
|
|
192
|
-
|
|
193
|
-
astArgsNumbaSignature = ast.Tuple(elts=listNumbaParameterSignature, ctx=ast.Load())
|
|
194
|
-
|
|
195
|
-
if astCallable.name == 'countInitialize' or astCallable.name == 'doTheNeedful':
|
|
196
|
-
parametersNumba = {}
|
|
197
|
-
else:
|
|
198
|
-
parametersNumba = parametersNumbaDEFAULT if not parallel else ParametersNumba({**parametersNumbaDEFAULT, 'parallel': True})
|
|
199
|
-
listKeywordsNumbaSignature = [ast.keyword(arg=parameterName, value=ast.Constant(value=parameterValue)) for parameterName, parameterValue in parametersNumba.items()]
|
|
200
|
-
|
|
201
|
-
astDecoratorNumba = ast.Call(func=ast.Attribute(value=ast.Name(id='numba', ctx=ast.Load()), attr='jit', ctx=ast.Load()), args=[astArgsNumbaSignature], keywords=listKeywordsNumbaSignature)
|
|
202
|
-
|
|
203
|
-
astCallable.decorator_list = [astDecoratorNumba]
|
|
204
|
-
return astCallable
|
|
205
|
-
|
|
206
|
-
class UnpackArrayAccesses(ast.NodeTransformer):
|
|
207
|
-
"""
|
|
208
|
-
A class that transforms array accesses using enum indices into local variables.
|
|
209
|
-
|
|
210
|
-
This AST transformer identifies array accesses using enum indices and replaces them
|
|
211
|
-
with local variables, adding initialization statements at the start of functions.
|
|
212
|
-
|
|
213
|
-
Parameters:
|
|
214
|
-
enumIndexClass (Type[EnumIndices]): The enum class used for array indexing
|
|
215
|
-
arrayName (str): The name of the array being accessed
|
|
216
|
-
|
|
217
|
-
Attributes:
|
|
218
|
-
enumIndexClass (Type[EnumIndices]): Stored enum class for index lookups
|
|
219
|
-
arrayName (str): Name of the array being transformed
|
|
220
|
-
substitutions (dict): Tracks variable substitutions and their original nodes
|
|
221
|
-
|
|
222
|
-
The transformer handles two main cases:
|
|
223
|
-
1. Scalar array access - array[EnumIndices.MEMBER]
|
|
224
|
-
2. Array slice access - array[EnumIndices.MEMBER, other_indices...]
|
|
225
|
-
For each identified access pattern, it:
|
|
226
|
-
1. Creates a local variable named after the enum member
|
|
227
|
-
2. Adds initialization code at function start
|
|
228
|
-
3. Replaces original array access with the local variable
|
|
229
|
-
"""
|
|
230
|
-
|
|
231
|
-
def __init__(self, enumIndexClass: Type[EnumIndices], arrayName: str):
|
|
232
|
-
self.enumIndexClass = enumIndexClass
|
|
233
|
-
self.arrayName = arrayName
|
|
234
|
-
self.substitutions = {}
|
|
235
|
-
|
|
236
|
-
def extract_member_name(self, node: ast.AST) -> Optional[str]:
|
|
237
|
-
"""Recursively extract enum member name from any node in the AST."""
|
|
238
|
-
if isinstance(node, ast.Attribute) and node.attr == 'value':
|
|
239
|
-
innerAttribute = node.value
|
|
240
|
-
while isinstance(innerAttribute, ast.Attribute):
|
|
241
|
-
if (isinstance(innerAttribute.value, ast.Name) and innerAttribute.value.id == self.enumIndexClass.__name__):
|
|
242
|
-
return innerAttribute.attr
|
|
243
|
-
innerAttribute = innerAttribute.value
|
|
244
|
-
return None
|
|
245
|
-
|
|
246
|
-
def transform_slice_element(self, node: ast.AST) -> ast.AST:
|
|
247
|
-
"""Transform any enum references within a slice element."""
|
|
248
|
-
if isinstance(node, ast.Subscript):
|
|
249
|
-
if isinstance(node.slice, ast.Attribute):
|
|
250
|
-
member_name = self.extract_member_name(node.slice)
|
|
251
|
-
if member_name:
|
|
252
|
-
return ast.Name(id=member_name, ctx=node.ctx)
|
|
253
|
-
elif isinstance(node, ast.Tuple):
|
|
254
|
-
# Handle tuple slices by transforming each element
|
|
255
|
-
return ast.Tuple(elts=cast(List[ast.expr], [self.transform_slice_element(elt) for elt in node.elts]), ctx=node.ctx)
|
|
256
|
-
elif isinstance(node, ast.Attribute):
|
|
257
|
-
member_name = self.extract_member_name(node)
|
|
258
|
-
if member_name:
|
|
259
|
-
return ast.Name(id=member_name, ctx=ast.Load())
|
|
260
|
-
return node
|
|
261
|
-
|
|
262
|
-
def visit_Subscript(self, node: ast.Subscript) -> ast.AST:
|
|
263
|
-
# Recursively visit any nested subscripts in value or slice
|
|
264
|
-
node.value = self.visit(node.value)
|
|
265
|
-
node.slice = self.visit(node.slice)
|
|
266
|
-
# If node.value is not our arrayName, just return node
|
|
267
|
-
if not (isinstance(node.value, ast.Name) and node.value.id == self.arrayName):
|
|
268
|
-
return node
|
|
269
|
-
|
|
270
|
-
# Handle scalar array access
|
|
271
|
-
if isinstance(node.slice, ast.Attribute):
|
|
272
|
-
memberName = self.extract_member_name(node.slice)
|
|
273
|
-
if memberName:
|
|
274
|
-
self.substitutions[memberName] = ('scalar', node)
|
|
275
|
-
return ast.Name(id=memberName, ctx=ast.Load())
|
|
276
|
-
|
|
277
|
-
# Handle array slice access
|
|
278
|
-
if isinstance(node.slice, ast.Tuple) and node.slice.elts:
|
|
279
|
-
firstElement = node.slice.elts[0]
|
|
280
|
-
memberName = self.extract_member_name(firstElement)
|
|
281
|
-
sliceRemainder = [self.visit(elem) for elem in node.slice.elts[1:]]
|
|
282
|
-
if memberName:
|
|
283
|
-
self.substitutions[memberName] = ('array', node)
|
|
284
|
-
if len(sliceRemainder) == 0:
|
|
285
|
-
return ast.Name(id=memberName, ctx=ast.Load())
|
|
286
|
-
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())
|
|
287
|
-
|
|
288
|
-
# If single-element tuple, unwrap
|
|
289
|
-
if isinstance(node.slice, ast.Tuple) and len(node.slice.elts) == 1:
|
|
290
|
-
node.slice = node.slice.elts[0]
|
|
291
|
-
|
|
292
|
-
return node
|
|
293
|
-
|
|
294
|
-
def visit_FunctionDef(self, node: ast.FunctionDef) -> ast.FunctionDef:
|
|
295
|
-
node = cast(ast.FunctionDef, self.generic_visit(node))
|
|
296
|
-
|
|
297
|
-
initializations = []
|
|
298
|
-
for name, (kind, original_node) in self.substitutions.items():
|
|
299
|
-
if kind == 'scalar':
|
|
300
|
-
initializations.append(ast.Assign(targets=[ast.Name(id=name, ctx=ast.Store())], value=original_node))
|
|
301
|
-
else: # array
|
|
302
|
-
initializations.append(
|
|
303
|
-
ast.Assign(
|
|
304
|
-
targets=[ast.Name(id=name, ctx=ast.Store())],
|
|
305
|
-
value=ast.Subscript(value=ast.Name(id=self.arrayName, ctx=ast.Load()),
|
|
306
|
-
slice=ast.Attribute(value=ast.Attribute(
|
|
307
|
-
value=ast.Name(id=self.enumIndexClass.__name__, ctx=ast.Load()),
|
|
308
|
-
attr=name, ctx=ast.Load()), attr='value', ctx=ast.Load()), ctx=ast.Load())))
|
|
309
|
-
|
|
310
|
-
node.body = initializations + node.body
|
|
311
|
-
return node
|
|
312
|
-
|
|
313
|
-
def inlineOneCallable(codeSource: str, callableTarget: str):
|
|
314
|
-
"""
|
|
315
|
-
Inlines a target callable function and its dependencies within the provided code source.
|
|
316
|
-
|
|
317
|
-
This function performs function inlining, optionally adds Numba decorators, and handles array access unpacking
|
|
318
|
-
for specific callable targets. It processes the source code through AST manipulation and returns the modified source.
|
|
319
|
-
|
|
320
|
-
Parameters:
|
|
321
|
-
codeSource (str): The source code containing the callable to be inlined.
|
|
322
|
-
callableTarget (str): The name of the callable function to be inlined. Special handling is provided for
|
|
323
|
-
'countParallel', 'countInitialize', and 'countSequential'.
|
|
324
|
-
|
|
325
|
-
Returns:
|
|
326
|
-
str: The modified source code with the inlined callable and necessary imports.
|
|
327
|
-
|
|
328
|
-
The function performs the following operations:
|
|
329
|
-
1. Parses the source code into an AST
|
|
330
|
-
2. Extracts import statements and function definitions
|
|
331
|
-
3. Inlines the target function using RecursiveInliner
|
|
332
|
-
4. Applies Numba decoration if needed
|
|
333
|
-
5. Handles special array access unpacking for 'countSequential'
|
|
334
|
-
6. Reconstructs and returns the modified source code
|
|
335
|
-
|
|
336
|
-
Note:
|
|
337
|
-
- Special handling is provided for 'countParallel', 'countInitialize', and 'countSequential' targets
|
|
338
|
-
- For 'countSequential', additional array access unpacking is performed for 'my' and 'track' indices
|
|
339
|
-
- `UnpackArrayAccesses` would need modification to handle 'countParallel'
|
|
340
|
-
"""
|
|
341
|
-
|
|
342
|
-
codeParsed: ast.Module = ast.parse(codeSource, type_comments=True)
|
|
343
|
-
codeSourceImportStatements = {statement for statement in codeParsed.body if isinstance(statement, (ast.Import, ast.ImportFrom))}
|
|
344
|
-
dictionaryFunctions = {statement.name: statement for statement in codeParsed.body if isinstance(statement, ast.FunctionDef)}
|
|
345
|
-
callableInlinerWorkhorse = RecursiveInliner(dictionaryFunctions)
|
|
346
|
-
callableInlined = callableInlinerWorkhorse.inlineFunctionBody(callableTarget)
|
|
347
|
-
|
|
348
|
-
if callableInlined:
|
|
349
|
-
ast.fix_missing_locations(callableInlined)
|
|
350
|
-
parallel = callableTarget == 'countParallel'
|
|
351
|
-
callableDecorated = decorateCallableWithNumba(callableInlined, parallel)
|
|
352
|
-
|
|
353
|
-
if callableTarget == 'countSequential':
|
|
354
|
-
unpackerMy = UnpackArrayAccesses(indexMy, 'my')
|
|
355
|
-
callableDecorated = cast(ast.FunctionDef, unpackerMy.visit(callableDecorated))
|
|
356
|
-
ast.fix_missing_locations(callableDecorated)
|
|
357
|
-
|
|
358
|
-
unpackerTrack = UnpackArrayAccesses(indexTrack, 'track')
|
|
359
|
-
callableDecorated = cast(ast.FunctionDef, unpackerTrack.visit(callableDecorated))
|
|
360
|
-
ast.fix_missing_locations(callableDecorated)
|
|
361
|
-
|
|
362
|
-
moduleAST = ast.Module(body=cast(List[ast.stmt], list(codeSourceImportStatements) + [callableDecorated]), type_ignores=[])
|
|
363
|
-
ast.fix_missing_locations(moduleAST)
|
|
364
|
-
moduleSource = ast.unparse(moduleAST)
|
|
365
|
-
return moduleSource
|
|
366
|
-
|
|
367
|
-
def makeDispatcherNumba(codeSource: str, callableTarget: str, listStuffYouOughtaKnow: List[youOughtaKnow]) -> str:
|
|
368
|
-
"""Creates AST for the dispatcher module that coordinates the optimized functions."""
|
|
369
|
-
docstringDispatcherNumba = """
|
|
370
|
-
What in tarnation is this stupid module and function?
|
|
371
|
-
|
|
372
|
-
- This function is not in the same module as `countFolds` so that we can delay Numba just-in-time (jit) compilation of this function and the finalization of its settings until we are ready.
|
|
373
|
-
- This function is not in the same module as the next function, which does the hard work, so that we can delay `numba.jit` compilation of the next function.
|
|
374
|
-
- This function is "jitted" but the next function is super jitted, which makes it too arrogant to talk to plebian Python functions. It will, however, reluctantly talk to basic jitted functions.
|
|
375
|
-
- So this module can talk to the next function, and because this module isn't as arrogant, it will talk to the low-class `countFolds` that called this function. Well, with a few restrictions, of course:
|
|
376
|
-
- No `TypedDict`
|
|
377
|
-
- The plebs must clean up their own memory problems
|
|
378
|
-
- No oversized integers
|
|
379
|
-
- No global variables, only global constants
|
|
380
|
-
- It won't accept pleb nonlocal variables either
|
|
381
|
-
- Python "class": they are all inferior to the jit class
|
|
382
|
-
- No `**kwargs`
|
|
383
|
-
- and just a few dozen-jillion other things.
|
|
384
|
-
"""
|
|
385
|
-
|
|
386
|
-
# Parse source code
|
|
387
|
-
sourceAST = ast.parse(codeSource)
|
|
388
|
-
|
|
389
|
-
# Extract imports and target function definition
|
|
390
|
-
importsAST = [node for node in sourceAST.body if isinstance(node, (ast.Import, ast.ImportFrom))]
|
|
391
|
-
FunctionDefTarget = next((node for node in sourceAST.body if isinstance(node, ast.FunctionDef) and node.name == callableTarget), None)
|
|
392
|
-
|
|
393
|
-
if not FunctionDefTarget:
|
|
394
|
-
raise ValueError(f"Could not find function {callableTarget} in source code")
|
|
395
|
-
|
|
396
|
-
# Zero-out the decorator list
|
|
397
|
-
FunctionDefTarget.decorator_list=[]
|
|
398
|
-
# TODO: more explicit handling of decorators. I'm able to ignore this because I know `algorithmSource` doesn't have any decorators.
|
|
399
|
-
# FunctionDefTargetDecorators = [decorator for decorator in FunctionDefTarget.decorator_list]
|
|
400
|
-
|
|
401
|
-
# Add Numba decorator
|
|
402
|
-
FunctionDefTarget = decorateCallableWithNumba(FunctionDefTarget, parallel=False)
|
|
403
|
-
FunctionDefTarget.body.insert(0, ast.Expr(value=ast.Constant(value=docstringDispatcherNumba)))
|
|
404
|
-
|
|
405
|
-
# Combine everything into a module
|
|
406
|
-
moduleAST = ast.Module(
|
|
407
|
-
body=cast(List[ast.stmt]
|
|
408
|
-
, importsAST
|
|
409
|
-
+ [Don_Lapre_The_Road_to_Self_Improvement_For_Programmers_by_Using_Short_Identifiers.astForCompetentProgrammers
|
|
410
|
-
for Don_Lapre_The_Road_to_Self_Improvement_For_Programmers_by_Using_Short_Identifiers in listStuffYouOughtaKnow]
|
|
411
|
-
+ [FunctionDefTarget])
|
|
412
|
-
, type_ignores=[]
|
|
413
|
-
)
|
|
414
|
-
|
|
415
|
-
ast.fix_missing_locations(moduleAST)
|
|
416
|
-
return ast.unparse(moduleAST)
|
|
417
|
-
|
|
418
|
-
def makeNumbaOptimizedFlow(listCallablesInline: List[str], callableDispatcher: Optional[str] = None, algorithmSource: Optional[ModuleType] = None):
|
|
419
|
-
"""Synthesizes numba-optimized versions of map folding functions."""
|
|
420
|
-
|
|
421
|
-
if not algorithmSource:
|
|
422
|
-
algorithmSource = getAlgorithmSource()
|
|
423
|
-
|
|
424
|
-
formatModuleNameDEFAULT = "numba_{callableTarget}"
|
|
425
|
-
|
|
426
|
-
# When I am a more competent programmer, I will make getPathFilenameWrite dependent on makeAstImport or vice versa,
|
|
427
|
-
# so the name of the physical file doesn't get out of whack with the name of the logical module.
|
|
428
|
-
def getPathFilenameWrite(callableTarget: str
|
|
429
|
-
, pathWrite: Optional[pathlib.Path] = None
|
|
430
|
-
, formatFilenameWrite: Optional[str] = None
|
|
431
|
-
) -> pathlib.Path:
|
|
432
|
-
if not pathWrite:
|
|
433
|
-
pathWrite = getPathSyntheticModules()
|
|
434
|
-
if not formatFilenameWrite:
|
|
435
|
-
formatFilenameWrite = formatModuleNameDEFAULT + '.py'
|
|
436
|
-
|
|
437
|
-
pathFilename = pathWrite / formatFilenameWrite.format(callableTarget=callableTarget)
|
|
438
|
-
return pathFilename
|
|
439
|
-
|
|
440
|
-
def makeAstImport(callableTarget: str
|
|
441
|
-
, packageName: Optional[str] = None
|
|
442
|
-
, subPackageName: Optional[str] = None
|
|
443
|
-
, moduleName: Optional[str] = None
|
|
444
|
-
, astNodeLogicalPathThingy: Optional[ast.AST] = None
|
|
445
|
-
) -> ast.ImportFrom:
|
|
446
|
-
"""Creates import AST node for synthetic modules."""
|
|
447
|
-
if astNodeLogicalPathThingy is None:
|
|
448
|
-
if packageName is None:
|
|
449
|
-
packageName = myPackageNameIs
|
|
450
|
-
if subPackageName is None:
|
|
451
|
-
subPackageName = moduleOfSyntheticModules
|
|
452
|
-
if moduleName is None:
|
|
453
|
-
moduleName = formatModuleNameDEFAULT.format(callableTarget=callableTarget)
|
|
454
|
-
module=f'{packageName}.{subPackageName}.{moduleName}'
|
|
455
|
-
else:
|
|
456
|
-
module = str(astNodeLogicalPathThingy)
|
|
457
|
-
return ast.ImportFrom(
|
|
458
|
-
module=module,
|
|
459
|
-
names=[ast.alias(name=callableTarget, asname=None)],
|
|
460
|
-
level=0
|
|
461
|
-
)
|
|
462
|
-
|
|
463
|
-
listStuffYouOughtaKnow: List[youOughtaKnow] = []
|
|
464
|
-
|
|
465
|
-
for callableTarget in listCallablesInline:
|
|
466
|
-
codeSource = inspect.getsource(algorithmSource)
|
|
467
|
-
moduleSource = inlineOneCallable(codeSource, callableTarget)
|
|
468
|
-
if not moduleSource:
|
|
469
|
-
raise Exception("Pylance, OMG! The sky is falling!")
|
|
470
|
-
|
|
471
|
-
pathFilename = getPathFilenameWrite(callableTarget)
|
|
472
|
-
astImport = makeAstImport(callableTarget)
|
|
473
|
-
|
|
474
|
-
listStuffYouOughtaKnow.append(youOughtaKnow(
|
|
475
|
-
callableSynthesized=callableTarget,
|
|
476
|
-
pathFilenameForMe=pathFilename,
|
|
477
|
-
astForCompetentProgrammers=astImport
|
|
478
|
-
))
|
|
479
|
-
pathFilename.write_text(moduleSource)
|
|
480
|
-
|
|
481
|
-
# Generate dispatcher if requested
|
|
482
|
-
if callableDispatcher:
|
|
483
|
-
codeSource = inspect.getsource(algorithmSource)
|
|
484
|
-
moduleSource = makeDispatcherNumba(codeSource, callableDispatcher, listStuffYouOughtaKnow)
|
|
485
|
-
if not moduleSource:
|
|
486
|
-
raise Exception("Pylance, OMG! The sky is falling!")
|
|
487
|
-
|
|
488
|
-
pathFilename = getPathFilenameWrite(callableDispatcher)
|
|
489
|
-
astImport = makeAstImport(callableDispatcher)
|
|
490
|
-
|
|
491
|
-
listStuffYouOughtaKnow.append(youOughtaKnow(
|
|
492
|
-
callableSynthesized=callableDispatcher,
|
|
493
|
-
pathFilenameForMe=pathFilename,
|
|
494
|
-
astForCompetentProgrammers=astImport
|
|
495
|
-
))
|
|
496
|
-
pathFilename.write_text(moduleSource)
|
|
497
|
-
|
|
498
|
-
if __name__ == '__main__':
|
|
499
|
-
setDatatypeModule('numpy', sourGrapes=True)
|
|
500
|
-
setDatatypeFoldsTotal('int64', sourGrapes=True)
|
|
501
|
-
setDatatypeElephino('uint8', sourGrapes=True)
|
|
502
|
-
setDatatypeLeavesTotal('uint8', sourGrapes=True)
|
|
503
|
-
listCallablesInline: List[str] = ['countInitialize', 'countParallel', 'countSequential']
|
|
504
|
-
callableDispatcher = 'doTheNeedful'
|
|
505
|
-
makeNumbaOptimizedFlow(listCallablesInline, callableDispatcher)
|
|
506
|
-
# makeNumbaOptimizedFlow(listCallablesInline)
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
mapFolding/__init__.py,sha256=UWzkx8iO90WeetoipfAuHJisICTJNWN7t_Q8FRi8lok,833
|
|
2
|
-
mapFolding/basecamp.py,sha256=2Zo4ayHfpfn6RRtsumDNRvV7UUHx0GInfNYmr8-G2b4,3988
|
|
3
|
-
mapFolding/beDRY.py,sha256=otzEut41VWUiKkwM8ZE6_aduNDMGGXYsyjtNs6AcotA,17845
|
|
4
|
-
mapFolding/oeis.py,sha256=fIJySN5fIsRS-TBNbpKQdk9XVlZ4xFY6uthWzZY1jRs,12567
|
|
5
|
-
mapFolding/theDao.py,sha256=BlYx1h39ieZIkTOhvzMJ2MEI5tjB4dHyrbz0q3bJNDc,14418
|
|
6
|
-
mapFolding/theSSOT.py,sha256=ZXKFglwfr3JH4yGH8NmS1DRpfyON3Wulx8lG5gITHD0,10219
|
|
7
|
-
mapFolding/reference/flattened.py,sha256=6blZ2Y9G8mu1F3gV8SKndPE398t2VVFlsgKlyeJ765A,16538
|
|
8
|
-
mapFolding/reference/hunterNumba.py,sha256=HWndRgsajOf76rbb2LDNEZ6itsdYbyV-k3wgOFjeR6c,7104
|
|
9
|
-
mapFolding/reference/irvineJavaPort.py,sha256=Sj-63Z-OsGuDoEBXuxyjRrNmmyl0d7Yz_XuY7I47Oyg,4250
|
|
10
|
-
mapFolding/reference/jax.py,sha256=rojyK80lOATtbzxjGOHWHZngQa47CXCLJHZwIdN2MwI,14955
|
|
11
|
-
mapFolding/reference/lunnan.py,sha256=XEcql_gxvCCghb6Or3qwmPbn4IZUbZTaSmw_fUjRxZE,5037
|
|
12
|
-
mapFolding/reference/lunnanNumpy.py,sha256=HqDgSwTOZA-G0oophOEfc4zs25Mv4yw2aoF1v8miOLk,4653
|
|
13
|
-
mapFolding/reference/lunnanWhile.py,sha256=7NY2IKO5XBgol0aWWF_Fi-7oTL9pvu_z6lB0TF1uVHk,4063
|
|
14
|
-
mapFolding/reference/rotatedEntryPoint.py,sha256=z0QyDQtnMvXNj5ntWzzJUQUMFm1-xHGLVhtYzwmczUI,11530
|
|
15
|
-
mapFolding/reference/total_countPlus1vsPlusN.py,sha256=usenM8Yn_G1dqlPl7NKKkcnbohBZVZBXTQRm2S3_EDA,8106
|
|
16
|
-
mapFolding/someAssemblyRequired/__init__.py,sha256=3JnAKXfaYPtmxV_4AnZ6KpCosT_0GFV5Nw7K8sz4-Uo,34
|
|
17
|
-
mapFolding/someAssemblyRequired/getLLVMforNoReason.py,sha256=FtJzw2pZS3A4NimWdZsegXaU-vKeCw8m67kcfb5wvGM,894
|
|
18
|
-
mapFolding/someAssemblyRequired/makeJob.py,sha256=uKonSGmE4ieTpfqmIiYvVW05F3YN70eiUYJi-bj6yR8,2622
|
|
19
|
-
mapFolding/someAssemblyRequired/synthesizeModuleJAX.py,sha256=43h_gOk0pCI8dITIRJ9_DBOwx4-KV5dQpw1MWRSoxE8,1355
|
|
20
|
-
mapFolding/someAssemblyRequired/synthesizeModuleJobNumba.py,sha256=TCYNNI19vCw0C1FLP691VPpYknkedj-zpx-xQFVIVJU,9851
|
|
21
|
-
mapFolding/someAssemblyRequired/synthesizeModulesNumba.py,sha256=EpIpJnCIZ6jQ0nwyw01ULM_55cAc0kM3YSLxRB2I5A0,25481
|
|
22
|
-
mapFolding/syntheticModules/__init__.py,sha256=cER6gqkGb3SCHHE35AVjf14_ehUroXPjlM6Ot-TIt5w,149
|
|
23
|
-
mapFolding/syntheticModules/numba_countInitialize.py,sha256=WTXIETx8EOncMRGzOJzDepoHkNCP25eaQAKfAIleaVs,4065
|
|
24
|
-
mapFolding/syntheticModules/numba_countParallel.py,sha256=lMf2dtSTPoVLHT5yXemdSiXPMbdCdXJvVdLgVf_SVxM,5493
|
|
25
|
-
mapFolding/syntheticModules/numba_countSequential.py,sha256=RjO4yAH9Qu0OJC6moECJ7JTDIRNXr5mlm6wrusGr-Og,3670
|
|
26
|
-
mapFolding/syntheticModules/numba_doTheNeedful.py,sha256=iUbjbIlvoa--fK6f2tiGjaHA93E1v5jSnJUakE_5UTg,2465
|
|
27
|
-
tests/__init__.py,sha256=eg9smg-6VblOr0kisM40CpGnuDtU2JgEEWGDTFVOlW8,57
|
|
28
|
-
tests/conftest.py,sha256=4KuPXvkgYDZKWhxZlwRgnoLynMvmlnGLK8j2iqTX_9E,9044
|
|
29
|
-
tests/conftest_tmpRegistry.py,sha256=0XpGe7s2aJcjdEAqKs10vceW0_JAaK-Rp1UoPaL-BIo,2450
|
|
30
|
-
tests/conftest_uniformTests.py,sha256=2v9UJbKgbrus3UyWXsanEHLksBskbnP0MD8iKVPaIBo,2178
|
|
31
|
-
tests/test_oeis.py,sha256=xIyU88nyEfeBK5TS4NfeBBy3YErgM1rGwVSu4ry-XrU,8491
|
|
32
|
-
tests/test_other.py,sha256=yHGhoZfKh_Z-MZHXZknHC_FBm3sS9Cntr94_L9LLLhQ,12151
|
|
33
|
-
tests/test_tasks.py,sha256=ap_tKjHUN95vjzKo3xzBAQ3kMbdMJ_XXbOv9YIBJ5pY,2826
|
|
34
|
-
tests/test_types.py,sha256=HklNCGThFiqQ89AOMkE7YkcfAPiZE32DpD3GMDUPQVc,177
|
|
35
|
-
mapFolding-0.3.9.dist-info/LICENSE,sha256=NxH5Y8BdC-gNU-WSMwim3uMbID2iNDXJz7fHtuTdXhk,19346
|
|
36
|
-
mapFolding-0.3.9.dist-info/METADATA,sha256=NnN8fZG_zufvXSK5XC8Yrw0SmLwihDjFZlpIutqj-lc,7935
|
|
37
|
-
mapFolding-0.3.9.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
38
|
-
mapFolding-0.3.9.dist-info/entry_points.txt,sha256=F3OUeZR1XDTpoH7k3wXuRb3KF_kXTTeYhu5AGK1SiOQ,146
|
|
39
|
-
mapFolding-0.3.9.dist-info/top_level.txt,sha256=1gP2vFaqPwHujGwb3UjtMlLEGN-943VSYFR7V4gDqW8,17
|
|
40
|
-
mapFolding-0.3.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|