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.
Files changed (32) hide show
  1. mapFolding/__init__.py +3 -2
  2. mapFolding/basecamp.py +12 -14
  3. mapFolding/beDRY.py +81 -58
  4. mapFolding/oeis.py +35 -33
  5. mapFolding/someAssemblyRequired/makeJob.py +8 -7
  6. mapFolding/someAssemblyRequired/synthesizeModuleJAX.py +1 -3
  7. mapFolding/someAssemblyRequired/synthesizeNumba.py +57 -60
  8. mapFolding/someAssemblyRequired/synthesizeNumbaGeneralized.py +102 -30
  9. mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +18 -36
  10. mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +77 -31
  11. mapFolding/syntheticModules/numbaCount.py +158 -0
  12. mapFolding/syntheticModules/numba_doTheNeedful.py +5 -12
  13. mapFolding/theDao.py +105 -105
  14. mapFolding/theSSOT.py +80 -205
  15. mapFolding/theSSOTdatatypes.py +166 -0
  16. {mapFolding-0.4.2.dist-info → mapFolding-0.5.0.dist-info}/METADATA +2 -1
  17. mapFolding-0.5.0.dist-info/RECORD +39 -0
  18. tests/conftest.py +84 -26
  19. tests/test_computations.py +29 -66
  20. tests/test_oeis.py +8 -12
  21. tests/test_other.py +11 -7
  22. tests/test_tasks.py +5 -5
  23. mapFolding/syntheticModules/numba_countInitialize.py +0 -52
  24. mapFolding/syntheticModules/numba_countParallel.py +0 -65
  25. mapFolding/syntheticModules/numba_countSequential.py +0 -67
  26. mapFolding/theSSOTnumba.py +0 -125
  27. mapFolding-0.4.2.dist-info/RECORD +0 -42
  28. tests/test_types.py +0 -5
  29. {mapFolding-0.4.2.dist-info → mapFolding-0.5.0.dist-info}/LICENSE +0 -0
  30. {mapFolding-0.4.2.dist-info → mapFolding-0.5.0.dist-info}/WHEEL +0 -0
  31. {mapFolding-0.4.2.dist-info → mapFolding-0.5.0.dist-info}/entry_points.txt +0 -0
  32. {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: Optional[int]=None) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
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
- argData_dtypeName = arrayTarget.dtype.name
12
+ datatypeName = argData_dtype.name
13
+ dtypeAsName = f"{moduleConstructor}_{datatypeName}"
13
14
 
14
15
  allImports.addImportFromStr(moduleConstructor, constructorName)
15
- allImports.addImportFromStr(moduleConstructor, argData_dtypeName)
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=argData_dtypeName, ctx=ast.Load()))])
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 findAndReplaceArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arrayTarget: numpy.ndarray, allImports: UniversalImportTracker) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
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 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
- 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
- onlyDataRLE = autoDecodingRLE(arraySlice, addSpaces=True)
57
- astStatement = cast(ast.Expr, ast.parse(onlyDataRLE).body[0])
58
- dataAst = astStatement.value
53
+ onlyDataRLE = autoDecodingRLE(arraySlice, addSpaces=True)
54
+ astStatement = cast(ast.Expr, ast.parse(onlyDataRLE).body[0])
55
+ dataAst = astStatement.value
59
56
 
60
- arrayCall = Then.make_astCall(name=constructorName, args=[dataAst], list_astKeywords=[ast.keyword(arg='dtype', value=ast.Name(id=argData_dtypeName, ctx=ast.Load()))])
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
- assignment = ast.Assign(targets=[astAssignee], value=arrayCall)
63
- FunctionDefTarget.body.insert(0, assignment)
64
- FunctionDefTarget.body.remove(stmt)
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: List[str], allImports: UniversalImportTracker) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
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 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)
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) -> Optional[ast.stmt]:
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) -> Tuple[ast.FunctionDef, 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(findName, replaceWithConstant).visit(FunctionDefTarget))
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) -> Tuple[ast.FunctionDef, 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) -> Any:
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) -> List[ast.stmt]:
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: List[ast.stmt] = []
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: List[ast.stmt] = []
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: pathlib.Path) -> ast.Module:
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 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
-
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
- astModule = ast.Module(body=cast(List[ast.stmt], allImports.makeListAst() + [FunctionDefTarget]), type_ignores=[])
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: Optional[ParametersNumba]=None) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
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: List[ast.expr] = []
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
- formatModuleNameDEFAULT,
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 typing import Any, Callable, cast, Dict, List, Optional, Sequence, Set, Tuple, Type, Union
39
- from Z0Z_tools import autoDecodingRLE
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 os
50
- import pathlib
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) -> Dict[str, Any]:
131
+ def copy_astCallKeywords(astCall: ast.Call) -> dict[str, Any]:
82
132
  """Extract keyword parameters from a decorator AST node."""
83
- dictionaryKeywords: Dict[str, Any] = {}
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: Optional[Sequence[ast.expr]]=None, list_astKeywords: Optional[Sequence[ast.keyword]]=None, dictionaryKeywords: Optional[Dict[str, Any]]=None) -> ast.Call:
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 (Callable[[ast.AST], bool]): A function that determines whether a node should be replaced.
107
- nodeReplacementBuilder (Callable[[ast.AST], Optional[ast.AST]]): A function that returns a new node
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], Optional[ast.AST]]) -> None:
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: Dict[str, Set] = collections.defaultdict(set)
185
+ self.dictionaryImportFrom: dict[str, set] = collections.defaultdict(set)
137
186
  self.setImport = set()
138
187
 
139
- def addAst(self, astImport_: Union[ast.Import, ast.ImportFrom]) -> None:
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 addImportStr(self, name: str) -> None:
151
- self.setImport.add(name)
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
- def makeListAst(self) -> List[Union[ast.ImportFrom, ast.Import]]:
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: Dict[str, ast.FunctionDef]):
257
+ def __init__(self, dictionaryFunctions: dict[str, ast.FunctionDef]):
186
258
  self.dictionaryFunctions = dictionaryFunctions
187
- self.callablesCompleted: Set[str] = set()
259
+ self.callablesCompleted: set[str] = set()
188
260
 
189
- def inlineFunctionBody(self, callableTargetName: str) -> Optional[ast.FunctionDef]:
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) -> Union[ast.AST, List[ast.AST]]:
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: Type[EnumIndices], arrayName: str) -> None:
317
+ def __init__(self, enumIndexClass: type[EnumIndices], arrayName: str) -> None:
246
318
  self.enumIndexClass = enumIndexClass
247
319
  self.arrayName = arrayName
248
- self.substitutions: Dict[str, Any] = {}
320
+ self.substitutions: dict[str, Any] = {}
249
321
 
250
- def extract_member_name(self, node: ast.AST) -> Optional[str]:
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(List[ast.expr], [self.transform_slice_element(elt) for elt in node.elts]), ctx=node.ctx)
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) -> Tuple[ast.FunctionDef, 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
- FunctionDefTarget = setFunctionDef.pop() if callableTarget in {statement.name for statement in setFunctionDef} else None
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 = findAndReplaceArrayIn_body(FunctionDefTarget, pirateScowl.arg, stateJob[pirateScowl.arg], 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(List[ast.stmt], astImports + [FunctionDefTarget] + [astLauncher]), type_ignores=[])
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 = pathlib.Path(pathRoot, pathlib.Path(filename).stem, pathlib.Path(filename).with_suffix('.py'))
128
+ pathFilenameWriteJob = Path(pathRoot, Path(filename).stem, Path(filename).with_suffix('.py'))
148
129
  else:
149
- pathFilenameWriteJob = pathlib.Path(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 numba_countSequential
159
- algorithmSource: ModuleType = numba_countSequential
138
+ mapShape: list[int] = [5,5]
139
+ from mapFolding.syntheticModules import numbaCount
140
+ algorithmSource: ModuleType = numbaCount
160
141
 
161
- callableTarget = None
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('uint8', sourGrapes=True)
150
+ setDatatypeElephino('int16', sourGrapes=True)
169
151
  setDatatypeLeavesTotal('uint8', sourGrapes=True)
170
152
  Z0Z_setDatatypeModuleScalar('numba')
171
153
  Z0Z_setDecoratorCallable('jit')