mapFolding 0.9.3__py3-none-any.whl → 0.9.5__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 +41 -7
- mapFolding/basecamp.py +100 -9
- mapFolding/beDRY.py +7 -15
- mapFolding/dataBaskets.py +12 -0
- mapFolding/datatypes.py +4 -4
- mapFolding/oeis.py +2 -7
- mapFolding/someAssemblyRequired/RecipeJob.py +97 -3
- mapFolding/someAssemblyRequired/Z0Z_makeSomeModules.py +326 -0
- mapFolding/someAssemblyRequired/__init__.py +37 -29
- mapFolding/someAssemblyRequired/_theTypes.py +19 -19
- mapFolding/someAssemblyRequired/_tool_Make.py +12 -6
- mapFolding/someAssemblyRequired/_tool_Then.py +59 -21
- mapFolding/someAssemblyRequired/_toolboxAST.py +57 -0
- mapFolding/someAssemblyRequired/_toolboxAntecedents.py +123 -40
- mapFolding/someAssemblyRequired/_toolboxContainers.py +128 -37
- mapFolding/someAssemblyRequired/_toolboxPython.py +52 -50
- mapFolding/someAssemblyRequired/makeJobTheorem2Numba.py +274 -0
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +6 -4
- mapFolding/someAssemblyRequired/toolboxNumba.py +3 -27
- mapFolding/someAssemblyRequired/transformationTools.py +47 -177
- mapFolding/syntheticModules/daoOfMapFolding.py +74 -0
- mapFolding/syntheticModules/dataPacking.py +25 -0
- mapFolding/syntheticModules/initializeCount.py +49 -0
- mapFolding/syntheticModules/theorem2.py +49 -0
- mapFolding/syntheticModules/theorem2Numba.py +45 -0
- mapFolding/syntheticModules/theorem2Trimmed.py +43 -0
- {mapfolding-0.9.3.dist-info → mapfolding-0.9.5.dist-info}/METADATA +2 -1
- mapfolding-0.9.5.dist-info/RECORD +59 -0
- {mapfolding-0.9.3.dist-info → mapfolding-0.9.5.dist-info}/WHEEL +1 -1
- tests/test_computations.py +4 -2
- mapFolding/Z0Z_flowControl.py +0 -99
- mapfolding-0.9.3.dist-info/RECORD +0 -51
- /mapFolding/{theDaoOfMapFolding.py → daoOfMapFolding.py} +0 -0
- {mapfolding-0.9.3.dist-info → mapfolding-0.9.5.dist-info}/entry_points.txt +0 -0
- {mapfolding-0.9.3.dist-info → mapfolding-0.9.5.dist-info}/licenses/LICENSE +0 -0
- {mapfolding-0.9.3.dist-info → mapfolding-0.9.5.dist-info}/top_level.txt +0 -0
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Core AST Traversal and Transformation Utilities for Python Code Manipulation
|
|
3
3
|
|
|
4
|
-
This module provides the foundation for traversing and modifying Python Abstract
|
|
5
|
-
|
|
4
|
+
This module provides the foundation for traversing and modifying Python Abstract Syntax Trees (ASTs). It contains two
|
|
5
|
+
primary classes:
|
|
6
6
|
|
|
7
|
-
1. NodeTourist: Implements the visitor pattern to traverse an AST and extract information
|
|
8
|
-
|
|
7
|
+
1. NodeTourist: Implements the visitor pattern to traverse an AST and extract information from nodes that match specific
|
|
8
|
+
predicates without modifying the AST.
|
|
9
9
|
|
|
10
|
-
2. NodeChanger: Extends ast.NodeTransformer to selectively transform AST nodes that
|
|
11
|
-
|
|
10
|
+
2. NodeChanger: Extends ast.NodeTransformer to selectively transform AST nodes that match specific predicates, enabling
|
|
11
|
+
targeted code modifications.
|
|
12
12
|
|
|
13
|
-
The module also provides utilities for importing modules, loading callables from files,
|
|
14
|
-
|
|
15
|
-
analysis and transformation.
|
|
13
|
+
The module also provides utilities for importing modules, loading callables from files, and parsing Python code into AST
|
|
14
|
+
structures, creating a complete workflow for code analysis and transformation.
|
|
16
15
|
"""
|
|
17
16
|
|
|
18
17
|
from collections.abc import Callable
|
|
@@ -32,13 +31,12 @@ class NodeTourist(ast.NodeVisitor):
|
|
|
32
31
|
"""
|
|
33
32
|
Visit and extract information from AST nodes that match a predicate.
|
|
34
33
|
|
|
35
|
-
NodeTourist implements the visitor pattern to traverse an AST, applying
|
|
36
|
-
|
|
37
|
-
when they match. Unlike NodeChanger, it doesn't modify the AST but collects
|
|
34
|
+
NodeTourist implements the visitor pattern to traverse an AST, applying a predicate function to each node and
|
|
35
|
+
capturing nodes or their attributes when they match. Unlike NodeChanger, it doesn't modify the AST but collects
|
|
38
36
|
information during traversal.
|
|
39
37
|
|
|
40
|
-
This class is particularly useful for analyzing AST structures, extracting
|
|
41
|
-
|
|
38
|
+
This class is particularly useful for analyzing AST structures, extracting specific nodes or node properties, and
|
|
39
|
+
gathering information about code patterns.
|
|
42
40
|
"""
|
|
43
41
|
def __init__(self, findThis: Callable[..., Any], doThat: Callable[..., Any]) -> None:
|
|
44
42
|
self.findThis = findThis
|
|
@@ -61,12 +59,12 @@ class NodeChanger(ast.NodeTransformer):
|
|
|
61
59
|
"""
|
|
62
60
|
Transform AST nodes that match a predicate by applying a transformation function.
|
|
63
61
|
|
|
64
|
-
NodeChanger is an AST node transformer that applies a targeted transformation
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
NodeChanger is an AST node transformer that applies a targeted transformation to nodes matching a specific
|
|
63
|
+
predicate. It traverses the AST and only modifies nodes that satisfy the predicate condition, leaving other nodes
|
|
64
|
+
unchanged.
|
|
67
65
|
|
|
68
|
-
This class extends ast.NodeTransformer and implements the visitor pattern
|
|
69
|
-
|
|
66
|
+
This class extends ast.NodeTransformer and implements the visitor pattern to systematically process and transform an
|
|
67
|
+
AST tree.
|
|
70
68
|
"""
|
|
71
69
|
def __init__(self, findThis: Callable[..., Any], doThat: Callable[..., Any]) -> None:
|
|
72
70
|
self.findThis = findThis
|
|
@@ -81,18 +79,18 @@ def importLogicalPath2Callable(logicalPathModule: str_nameDOTname, identifier: a
|
|
|
81
79
|
"""
|
|
82
80
|
Import a callable object (function or class) from a module based on its logical path.
|
|
83
81
|
|
|
84
|
-
This function imports a module using `importlib.import_module()` and then retrieves
|
|
85
|
-
|
|
82
|
+
This function imports a module using `importlib.import_module()` and then retrieves a specific attribute (function,
|
|
83
|
+
class, or other object) from that module.
|
|
86
84
|
|
|
87
85
|
Parameters
|
|
88
86
|
----------
|
|
89
|
-
logicalPathModule
|
|
87
|
+
logicalPathModule
|
|
90
88
|
The logical path to the module, using dot notation (e.g., 'package.subpackage.module').
|
|
91
|
-
identifier
|
|
89
|
+
identifier
|
|
92
90
|
The name of the callable object to retrieve from the module.
|
|
93
|
-
packageIdentifierIfRelative :
|
|
94
|
-
The package name to use as the anchor point if `logicalPathModule` is a relative import.
|
|
95
|
-
|
|
91
|
+
packageIdentifierIfRelative : None
|
|
92
|
+
The package name to use as the anchor point if `logicalPathModule` is a relative import. If None, absolute
|
|
93
|
+
import is assumed.
|
|
96
94
|
|
|
97
95
|
Returns
|
|
98
96
|
-------
|
|
@@ -105,16 +103,17 @@ def importLogicalPath2Callable(logicalPathModule: str_nameDOTname, identifier: a
|
|
|
105
103
|
def importPathFilename2Callable(pathFilename: PathLike[Any] | PurePath, identifier: ast_Identifier, moduleIdentifier: ast_Identifier | None = None) -> Callable[..., Any]:
|
|
106
104
|
"""
|
|
107
105
|
Load a callable (function, class, etc.) from a Python file.
|
|
108
|
-
|
|
109
|
-
from it by name, and returns
|
|
106
|
+
|
|
107
|
+
This function imports a specified Python file as a module, extracts a callable object from it by name, and returns
|
|
108
|
+
that callable.
|
|
110
109
|
|
|
111
110
|
Parameters
|
|
112
111
|
----------
|
|
113
|
-
pathFilename
|
|
112
|
+
pathFilename
|
|
114
113
|
Path to the Python file to import.
|
|
115
|
-
identifier
|
|
114
|
+
identifier
|
|
116
115
|
Name of the callable to extract from the imported module.
|
|
117
|
-
moduleIdentifier
|
|
116
|
+
moduleIdentifier
|
|
118
117
|
Name to use for the imported module. If None, the filename stem is used.
|
|
119
118
|
|
|
120
119
|
Returns
|
|
@@ -138,49 +137,52 @@ def importPathFilename2Callable(pathFilename: PathLike[Any] | PurePath, identifi
|
|
|
138
137
|
importlibSpecification.loader.exec_module(moduleImported_jk_hahaha)
|
|
139
138
|
return getattr(moduleImported_jk_hahaha, identifier)
|
|
140
139
|
|
|
141
|
-
def parseLogicalPath2astModule(logicalPathModule: str_nameDOTname, packageIdentifierIfRelative: ast_Identifier|None=None, mode: Literal['exec'] = 'exec') -> ast.Module:
|
|
140
|
+
def parseLogicalPath2astModule(logicalPathModule: str_nameDOTname, packageIdentifierIfRelative: ast_Identifier | None = None, mode: Literal['exec'] = 'exec') -> ast.Module:
|
|
142
141
|
"""
|
|
143
|
-
Parse a logical Python module path into an
|
|
142
|
+
Parse a logical Python module path into an `ast.Module`.
|
|
144
143
|
|
|
145
|
-
This function imports a module using its logical path (e.g., 'package.subpackage.module')
|
|
146
|
-
|
|
144
|
+
This function imports a module using its logical path (e.g., 'package.subpackage.module') and converts its source
|
|
145
|
+
code into an Abstract Syntax Tree (AST) Module object.
|
|
147
146
|
|
|
148
147
|
Parameters
|
|
149
148
|
----------
|
|
150
|
-
logicalPathModule
|
|
149
|
+
logicalPathModule
|
|
151
150
|
The logical path to the module using dot notation (e.g., 'package.module').
|
|
152
|
-
packageIdentifierIfRelative :
|
|
151
|
+
packageIdentifierIfRelative : None
|
|
153
152
|
The package identifier to use if the module path is relative, defaults to None.
|
|
154
|
-
mode : Literal['exec']
|
|
155
|
-
The
|
|
153
|
+
mode : Literal['exec']
|
|
154
|
+
The mode parameter for `ast.parse`. Default is `Literal['exec']`. Options are `Literal['exec']`, `"exec"` (which
|
|
155
|
+
is _not_ the same as `Literal['exec']`), `Literal['eval']`, `Literal['func_type']`, `Literal['single']`. See
|
|
156
|
+
`ast.parse` documentation for some details and much confusion.
|
|
156
157
|
|
|
157
158
|
Returns
|
|
158
159
|
-------
|
|
159
|
-
|
|
160
|
+
astModule
|
|
160
161
|
An AST Module object representing the parsed source code of the imported module.
|
|
161
162
|
"""
|
|
162
163
|
moduleImported: ModuleType = importlib.import_module(logicalPathModule, packageIdentifierIfRelative)
|
|
163
164
|
sourcePython: str = inspect_getsource(moduleImported)
|
|
164
|
-
return ast.parse(sourcePython, mode
|
|
165
|
+
return ast.parse(sourcePython, mode)
|
|
165
166
|
|
|
166
167
|
def parsePathFilename2astModule(pathFilename: PathLike[Any] | PurePath, mode: Literal['exec'] = 'exec') -> ast.Module:
|
|
167
168
|
"""
|
|
168
|
-
Parse a file from a given path into an ast.Module
|
|
169
|
+
Parse a file from a given path into an `ast.Module`.
|
|
169
170
|
|
|
170
|
-
This function reads the content of a file specified by `pathFilename` and parses it into an
|
|
171
|
-
|
|
171
|
+
This function reads the content of a file specified by `pathFilename` and parses it into an Abstract Syntax Tree
|
|
172
|
+
(AST) Module using Python's ast module.
|
|
172
173
|
|
|
173
174
|
Parameters
|
|
174
175
|
----------
|
|
175
|
-
pathFilename
|
|
176
|
+
pathFilename
|
|
176
177
|
The path to the file to be parsed. Can be a string path, PathLike object, or PurePath object.
|
|
177
|
-
mode : Literal['exec']
|
|
178
|
-
The mode parameter for ast.parse
|
|
179
|
-
|
|
178
|
+
mode : Literal['exec']
|
|
179
|
+
The mode parameter for `ast.parse`. Default is `Literal['exec']`. Options are `Literal['exec']`, `"exec"` (which
|
|
180
|
+
is _not_ the same as `Literal['exec']`), `Literal['eval']`, `Literal['func_type']`, `Literal['single']`. See
|
|
181
|
+
`ast.parse` documentation for some details and much confusion.
|
|
180
182
|
|
|
181
183
|
Returns
|
|
182
184
|
-------
|
|
183
|
-
|
|
185
|
+
astModule
|
|
184
186
|
The parsed abstract syntax tree module.
|
|
185
187
|
"""
|
|
186
|
-
return ast.parse(Path(pathFilename).read_text(), mode
|
|
188
|
+
return ast.parse(Path(pathFilename).read_text(), mode)
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
from mapFolding import getPathFilenameFoldsTotal, raiseIfNoneGitHubIssueNumber3, The
|
|
2
|
+
from mapFolding.someAssemblyRequired import (
|
|
3
|
+
ast_Identifier,
|
|
4
|
+
be,
|
|
5
|
+
extractFunctionDef,
|
|
6
|
+
ifThis,
|
|
7
|
+
IngredientsFunction,
|
|
8
|
+
IngredientsModule,
|
|
9
|
+
LedgerOfImports,
|
|
10
|
+
Make,
|
|
11
|
+
NodeChanger,
|
|
12
|
+
NodeTourist,
|
|
13
|
+
str_nameDOTname,
|
|
14
|
+
Then,
|
|
15
|
+
)
|
|
16
|
+
from mapFolding.someAssemblyRequired.RecipeJob import RecipeJobTheorem2Numba
|
|
17
|
+
from mapFolding.someAssemblyRequired.toolboxNumba import parametersNumbaLight, SpicesJobNumba, decorateCallableWithNumba
|
|
18
|
+
from mapFolding.someAssemblyRequired.transformationTools import dictionaryEstimates, write_astModule, makeInitializedComputationState
|
|
19
|
+
from mapFolding.syntheticModules.initializeCount import initializeGroupsOfFolds
|
|
20
|
+
from mapFolding.dataBaskets import MapFoldingState
|
|
21
|
+
from pathlib import PurePosixPath
|
|
22
|
+
from typing import cast, NamedTuple
|
|
23
|
+
from Z0Z_tools import autoDecodingRLE
|
|
24
|
+
import ast
|
|
25
|
+
"""Synthesize one file to compute `foldsTotal` of `mapShape`."""
|
|
26
|
+
|
|
27
|
+
list_IdentifiersNotUsedAllHARDCODED = ['concurrencyLimit', 'foldsTotal', 'mapShape',]
|
|
28
|
+
list_IdentifiersNotUsedParallelSequentialHARDCODED = ['indexLeaf']
|
|
29
|
+
list_IdentifiersNotUsedSequentialHARDCODED = ['foldGroups', 'taskDivisions', 'taskIndex',]
|
|
30
|
+
|
|
31
|
+
list_IdentifiersReplacedHARDCODED = ['groupsOfFolds',]
|
|
32
|
+
|
|
33
|
+
list_IdentifiersStaticValuesHARDCODED = ['dimensionsTotal', 'leavesTotal',]
|
|
34
|
+
|
|
35
|
+
list_IdentifiersNotUsedHARDCODED = list_IdentifiersStaticValuesHARDCODED + list_IdentifiersReplacedHARDCODED + list_IdentifiersNotUsedAllHARDCODED + list_IdentifiersNotUsedParallelSequentialHARDCODED + list_IdentifiersNotUsedSequentialHARDCODED
|
|
36
|
+
|
|
37
|
+
def addLauncherNumbaProgress(ingredientsModule: IngredientsModule, ingredientsFunction: IngredientsFunction, job: RecipeJobTheorem2Numba, spices: SpicesJobNumba) -> tuple[IngredientsModule, IngredientsFunction]:
|
|
38
|
+
"""
|
|
39
|
+
Add progress tracking capabilities to a Numba-optimized function.
|
|
40
|
+
|
|
41
|
+
This function modifies both the module and the function to integrate Numba-compatible
|
|
42
|
+
progress tracking for long-running calculations. It performs several key transformations:
|
|
43
|
+
|
|
44
|
+
1. Adds a progress bar parameter to the function signature
|
|
45
|
+
2. Replaces counting increments with progress bar updates
|
|
46
|
+
3. Creates a launcher section that displays and updates progress
|
|
47
|
+
4. Configures file output to save results upon completion
|
|
48
|
+
|
|
49
|
+
The progress tracking is particularly important for map folding calculations
|
|
50
|
+
which can take hours or days to complete, providing visual feedback and
|
|
51
|
+
estimated completion times.
|
|
52
|
+
|
|
53
|
+
Parameters:
|
|
54
|
+
ingredientsModule: The module where the function is defined.
|
|
55
|
+
ingredientsFunction: The function to modify with progress tracking.
|
|
56
|
+
job: Configuration specifying shape details and output paths.
|
|
57
|
+
spices: Configuration specifying progress bar details.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
A tuple containing the modified module and function with progress tracking.
|
|
61
|
+
"""
|
|
62
|
+
linesLaunch: str = f"""
|
|
63
|
+
if __name__ == '__main__':
|
|
64
|
+
with ProgressBar(total={job.foldsTotalEstimated}, update_interval=2) as statusUpdate:
|
|
65
|
+
{job.countCallable}(statusUpdate)
|
|
66
|
+
foldsTotal = statusUpdate.n * {job.state.leavesTotal}
|
|
67
|
+
print('\\nmap {job.state.mapShape} =', foldsTotal)
|
|
68
|
+
writeStream = open('{job.pathFilenameFoldsTotal.as_posix()}', 'w')
|
|
69
|
+
writeStream.write(str(foldsTotal))
|
|
70
|
+
writeStream.close()
|
|
71
|
+
"""
|
|
72
|
+
numba_progressPythonClass: ast_Identifier = 'ProgressBar'
|
|
73
|
+
numba_progressNumbaType: ast_Identifier = 'ProgressBarType'
|
|
74
|
+
ingredientsModule.imports.addImportFrom_asStr('numba_progress', numba_progressPythonClass)
|
|
75
|
+
ingredientsModule.imports.addImportFrom_asStr('numba_progress', numba_progressNumbaType)
|
|
76
|
+
|
|
77
|
+
ast_argNumbaProgress = ast.arg(arg=spices.numbaProgressBarIdentifier, annotation=ast.Name(id=numba_progressPythonClass, ctx=ast.Load()))
|
|
78
|
+
ingredientsFunction.astFunctionDef.args.args.append(ast_argNumbaProgress)
|
|
79
|
+
|
|
80
|
+
findThis = ifThis.isAugAssign_targetIs(ifThis.isName_Identifier(job.shatteredDataclass.countingVariableName.id))
|
|
81
|
+
doThat = Then.replaceWith(Make.Expr(Make.Call(Make.Attribute(Make.Name(spices.numbaProgressBarIdentifier),'update'),[Make.Constant(1)])))
|
|
82
|
+
countWithProgressBar = NodeChanger(findThis, doThat)
|
|
83
|
+
countWithProgressBar.visit(ingredientsFunction.astFunctionDef)
|
|
84
|
+
|
|
85
|
+
removeReturnStatement = NodeChanger(be.Return, Then.removeIt)
|
|
86
|
+
removeReturnStatement.visit(ingredientsFunction.astFunctionDef)
|
|
87
|
+
ingredientsFunction.astFunctionDef.returns = Make.Constant(value=None)
|
|
88
|
+
|
|
89
|
+
ingredientsModule.appendLauncher(ast.parse(linesLaunch))
|
|
90
|
+
|
|
91
|
+
return ingredientsModule, ingredientsFunction
|
|
92
|
+
|
|
93
|
+
def move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsFunction: IngredientsFunction, job: RecipeJobTheorem2Numba) -> IngredientsFunction:
|
|
94
|
+
"""
|
|
95
|
+
Convert function parameters into initialized variables with concrete values.
|
|
96
|
+
|
|
97
|
+
This function implements a critical transformation that converts function parameters
|
|
98
|
+
into statically initialized variables in the function body. This enables several
|
|
99
|
+
optimizations:
|
|
100
|
+
|
|
101
|
+
1. Eliminating parameter passing overhead.
|
|
102
|
+
2. Embedding concrete values directly in the code.
|
|
103
|
+
3. Allowing Numba to optimize based on known value characteristics.
|
|
104
|
+
4. Simplifying function signatures for specialized use cases.
|
|
105
|
+
|
|
106
|
+
The function handles different data types (scalars, arrays, custom types) appropriately,
|
|
107
|
+
replacing abstract parameter references with concrete values from the computation state.
|
|
108
|
+
It also removes unused parameters and variables to eliminate dead code.
|
|
109
|
+
|
|
110
|
+
Parameters:
|
|
111
|
+
ingredientsFunction: The function to transform.
|
|
112
|
+
job: Recipe containing concrete values for parameters and field metadata.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
The modified function with parameters converted to initialized variables.
|
|
116
|
+
"""
|
|
117
|
+
ingredientsFunction.imports.update(job.shatteredDataclass.imports)
|
|
118
|
+
|
|
119
|
+
list_argCuzMyBrainRefusesToThink = ingredientsFunction.astFunctionDef.args.args + ingredientsFunction.astFunctionDef.args.posonlyargs + ingredientsFunction.astFunctionDef.args.kwonlyargs
|
|
120
|
+
list_arg_arg: list[ast_Identifier] = [ast_arg.arg for ast_arg in list_argCuzMyBrainRefusesToThink]
|
|
121
|
+
listName: list[ast.Name] = []
|
|
122
|
+
NodeTourist(be.Name, Then.appendTo(listName)).visit(ingredientsFunction.astFunctionDef)
|
|
123
|
+
list_Identifiers: list[ast_Identifier] = [astName.id for astName in listName]
|
|
124
|
+
list_IdentifiersNotUsed: list[ast_Identifier] = list(set(list_arg_arg) - set(list_Identifiers))
|
|
125
|
+
|
|
126
|
+
for ast_arg in list_argCuzMyBrainRefusesToThink:
|
|
127
|
+
if ast_arg.arg in job.shatteredDataclass.field2AnnAssign:
|
|
128
|
+
if ast_arg.arg in list_IdentifiersNotUsed:
|
|
129
|
+
pass
|
|
130
|
+
else:
|
|
131
|
+
ImaAnnAssign, elementConstructor = job.shatteredDataclass.Z0Z_field2AnnAssign[ast_arg.arg]
|
|
132
|
+
match elementConstructor:
|
|
133
|
+
case 'scalar':
|
|
134
|
+
ImaAnnAssign.value.args[0].value = int(job.state.__dict__[ast_arg.arg]) # type: ignore
|
|
135
|
+
case 'array':
|
|
136
|
+
dataAsStrRLE: str = autoDecodingRLE(job.state.__dict__[ast_arg.arg], True)
|
|
137
|
+
dataAs_astExpr: ast.expr = cast(ast.Expr, ast.parse(dataAsStrRLE).body[0]).value
|
|
138
|
+
ImaAnnAssign.value.args = [dataAs_astExpr] # type: ignore
|
|
139
|
+
case _:
|
|
140
|
+
list_exprDOTannotation: list[ast.expr] = []
|
|
141
|
+
list_exprDOTvalue: list[ast.expr] = []
|
|
142
|
+
for dimension in job.state.mapShape:
|
|
143
|
+
list_exprDOTannotation.append(Make.Name(elementConstructor))
|
|
144
|
+
list_exprDOTvalue.append(Make.Call(Make.Name(elementConstructor), [Make.Constant(dimension)]))
|
|
145
|
+
ImaAnnAssign.annotation.slice.elts = list_exprDOTannotation # type: ignore
|
|
146
|
+
ImaAnnAssign.value.elts = list_exprDOTvalue # type: ignore
|
|
147
|
+
|
|
148
|
+
ingredientsFunction.astFunctionDef.body.insert(0, ImaAnnAssign)
|
|
149
|
+
|
|
150
|
+
findThis = ifThis.is_arg_Identifier(ast_arg.arg)
|
|
151
|
+
remove_arg = NodeChanger(findThis, Then.removeIt)
|
|
152
|
+
remove_arg.visit(ingredientsFunction.astFunctionDef)
|
|
153
|
+
|
|
154
|
+
ast.fix_missing_locations(ingredientsFunction.astFunctionDef)
|
|
155
|
+
return ingredientsFunction
|
|
156
|
+
|
|
157
|
+
def makeJobNumba(job: RecipeJobTheorem2Numba, spices: SpicesJobNumba) -> None:
|
|
158
|
+
|
|
159
|
+
astFunctionDef = extractFunctionDef(job.source_astModule, job.countCallable)
|
|
160
|
+
if not astFunctionDef: raise raiseIfNoneGitHubIssueNumber3
|
|
161
|
+
ingredientsCount: IngredientsFunction = IngredientsFunction(astFunctionDef, LedgerOfImports())
|
|
162
|
+
|
|
163
|
+
# Remove `foldGroups` and any other unused statements, so you can dynamically determine which variables are not used
|
|
164
|
+
findThis = ifThis.isAssignAndTargets0Is(ifThis.isSubscript_Identifier('foldGroups'))
|
|
165
|
+
doThat = Then.removeIt
|
|
166
|
+
remove_foldGroups = NodeChanger(findThis, doThat)
|
|
167
|
+
remove_foldGroups.visit(ingredientsCount.astFunctionDef)
|
|
168
|
+
|
|
169
|
+
# replace identifiers with static values with their values, so you can dynamically determine which variables are not used
|
|
170
|
+
list_IdentifiersStaticValues = list_IdentifiersStaticValuesHARDCODED
|
|
171
|
+
for identifier in list_IdentifiersStaticValues:
|
|
172
|
+
findThis = ifThis.isName_Identifier(identifier)
|
|
173
|
+
doThat = Then.replaceWith(Make.Constant(int(job.state.__dict__[identifier])))
|
|
174
|
+
NodeChanger(findThis, doThat).visit(ingredientsCount.astFunctionDef)
|
|
175
|
+
|
|
176
|
+
ingredientsModule = IngredientsModule()
|
|
177
|
+
# This launcher eliminates the use of one identifier, so run it now and you can dynamically determine which variables are not used
|
|
178
|
+
if spices.useNumbaProgressBar:
|
|
179
|
+
ingredientsModule, ingredientsCount = addLauncherNumbaProgress(ingredientsModule, ingredientsCount, job, spices)
|
|
180
|
+
spices.parametersNumba['nogil'] = True
|
|
181
|
+
else:
|
|
182
|
+
linesLaunch: str = f"""
|
|
183
|
+
if __name__ == '__main__':
|
|
184
|
+
import time
|
|
185
|
+
timeStart = time.perf_counter()
|
|
186
|
+
foldsTotal = {job.countCallable}() * {job.state.leavesTotal}
|
|
187
|
+
print(time.perf_counter() - timeStart)
|
|
188
|
+
print('\\nmap {job.state.mapShape} =', foldsTotal)
|
|
189
|
+
writeStream = open('{job.pathFilenameFoldsTotal.as_posix()}', 'w')
|
|
190
|
+
writeStream.write(str(foldsTotal))
|
|
191
|
+
writeStream.close()
|
|
192
|
+
"""
|
|
193
|
+
# from mapFolding.oeis import getFoldsTotalKnown
|
|
194
|
+
# print(foldsTotal == getFoldsTotalKnown({job.state.mapShape}))
|
|
195
|
+
ingredientsModule.appendLauncher(ast.parse(linesLaunch))
|
|
196
|
+
changeReturnParallelCallable = NodeChanger(be.Return, Then.replaceWith(Make.Return(job.shatteredDataclass.countingVariableName)))
|
|
197
|
+
changeReturnParallelCallable.visit(ingredientsCount.astFunctionDef)
|
|
198
|
+
ingredientsCount.astFunctionDef.returns = job.shatteredDataclass.countingVariableAnnotation
|
|
199
|
+
|
|
200
|
+
ingredientsCount = move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsCount, job)
|
|
201
|
+
|
|
202
|
+
class DatatypeConfig(NamedTuple):
|
|
203
|
+
Z0Z_module: str_nameDOTname
|
|
204
|
+
fml: ast_Identifier
|
|
205
|
+
Z0Z_type_name: ast_Identifier
|
|
206
|
+
Z0Z_asname: ast_Identifier | None = None
|
|
207
|
+
|
|
208
|
+
listDatatypeConfigs = [
|
|
209
|
+
DatatypeConfig(fml='DatatypeLeavesTotal', Z0Z_module='numba', Z0Z_type_name='uint8'),
|
|
210
|
+
DatatypeConfig(fml='DatatypeElephino', Z0Z_module='numba', Z0Z_type_name='uint16'),
|
|
211
|
+
DatatypeConfig(fml='DatatypeFoldsTotal', Z0Z_module='numba', Z0Z_type_name='uint64'),
|
|
212
|
+
]
|
|
213
|
+
|
|
214
|
+
for datatypeConfig in listDatatypeConfigs:
|
|
215
|
+
ingredientsModule.imports.addImportFrom_asStr(datatypeConfig.Z0Z_module, datatypeConfig.Z0Z_type_name)
|
|
216
|
+
statement = Make.Assign(
|
|
217
|
+
[Make.Name(datatypeConfig.fml, ast.Store())],
|
|
218
|
+
Make.Name(datatypeConfig.Z0Z_type_name)
|
|
219
|
+
)
|
|
220
|
+
ingredientsModule.appendPrologue(statement=statement)
|
|
221
|
+
|
|
222
|
+
ingredientsCount.imports.removeImportFromModule('mapFolding.theSSOT')
|
|
223
|
+
|
|
224
|
+
listNumPyTypeConfigs = [
|
|
225
|
+
DatatypeConfig(fml='Array1DLeavesTotal', Z0Z_module='numpy', Z0Z_type_name='uint8', Z0Z_asname='Array1DLeavesTotal'),
|
|
226
|
+
DatatypeConfig(fml='Array1DElephino', Z0Z_module='numpy', Z0Z_type_name='uint16', Z0Z_asname='Array1DElephino'),
|
|
227
|
+
DatatypeConfig(fml='Array3D', Z0Z_module='numpy', Z0Z_type_name='uint8', Z0Z_asname='Array3D'),
|
|
228
|
+
]
|
|
229
|
+
|
|
230
|
+
for typeConfig in listNumPyTypeConfigs:
|
|
231
|
+
ingredientsCount.imports.removeImportFrom(typeConfig.Z0Z_module, None, typeConfig.fml)
|
|
232
|
+
ingredientsCount.imports.addImportFrom_asStr(typeConfig.Z0Z_module, typeConfig.Z0Z_type_name, typeConfig.Z0Z_asname)
|
|
233
|
+
|
|
234
|
+
ingredientsCount.astFunctionDef.decorator_list = [] # TODO low-priority, handle this more elegantly
|
|
235
|
+
# TODO when I add the function signature in numba style back to the decorator, the logic needs to handle `ProgressBarType:`
|
|
236
|
+
ingredientsCount = decorateCallableWithNumba(ingredientsCount, spices.parametersNumba)
|
|
237
|
+
|
|
238
|
+
ingredientsModule.appendIngredientsFunction(ingredientsCount)
|
|
239
|
+
write_astModule(ingredientsModule, job.pathFilenameModule, job.packageIdentifier)
|
|
240
|
+
|
|
241
|
+
"""
|
|
242
|
+
Overview
|
|
243
|
+
- the code starts life in theDao.py, which has many optimizations;
|
|
244
|
+
- `makeNumbaOptimizedFlow` increase optimization especially by using numba;
|
|
245
|
+
- `makeJobNumba` increases optimization especially by limiting its capabilities to just one set of parameters
|
|
246
|
+
- the synthesized module must run well as a standalone interpreted-Python script
|
|
247
|
+
- the next major optimization step will (probably) be to use the module synthesized by `makeJobNumba` to compile a standalone executable
|
|
248
|
+
- Nevertheless, at each major optimization step, the code is constantly being improved and optimized, so everything must be well organized (read: semantic) and able to handle a range of arbitrary upstream and not disrupt downstream transformations
|
|
249
|
+
|
|
250
|
+
Necessary
|
|
251
|
+
- Move the function's parameters to the function body,
|
|
252
|
+
- initialize identifiers with their state types and values,
|
|
253
|
+
|
|
254
|
+
Optimizations
|
|
255
|
+
- replace static-valued identifiers with their values
|
|
256
|
+
- narrowly focused imports
|
|
257
|
+
|
|
258
|
+
Minutia
|
|
259
|
+
- do not use `with` statement inside numba jitted code, except to use numba's obj mode
|
|
260
|
+
"""
|
|
261
|
+
|
|
262
|
+
if __name__ == '__main__':
|
|
263
|
+
mapShape = (1,46)
|
|
264
|
+
state = MapFoldingState(mapShape)
|
|
265
|
+
state = initializeGroupsOfFolds(state)
|
|
266
|
+
# foldsTotalEstimated = getFoldsTotalKnown(state.mapShape) // state.leavesTotal
|
|
267
|
+
# foldsTotalEstimated = dictionaryEstimates[state.mapShape] // state.leavesTotal
|
|
268
|
+
foldsTotalEstimated = 0
|
|
269
|
+
pathModule = PurePosixPath(The.pathPackage, 'jobs')
|
|
270
|
+
pathFilenameFoldsTotal = PurePosixPath(getPathFilenameFoldsTotal(state.mapShape, pathModule))
|
|
271
|
+
aJob = RecipeJobTheorem2Numba(state, foldsTotalEstimated, pathModule=pathModule, pathFilenameFoldsTotal=pathFilenameFoldsTotal)
|
|
272
|
+
spices = SpicesJobNumba(useNumbaProgressBar=False, parametersNumba=parametersNumbaLight)
|
|
273
|
+
# spices = SpicesJobNumba()
|
|
274
|
+
makeJobNumba(aJob, spices)
|
|
@@ -22,6 +22,7 @@ from mapFolding import getPathFilenameFoldsTotal, raiseIfNoneGitHubIssueNumber3,
|
|
|
22
22
|
from mapFolding.someAssemblyRequired import (
|
|
23
23
|
ast_Identifier,
|
|
24
24
|
be,
|
|
25
|
+
extractFunctionDef,
|
|
25
26
|
ifThis,
|
|
26
27
|
IngredientsFunction,
|
|
27
28
|
IngredientsModule,
|
|
@@ -34,7 +35,7 @@ from mapFolding.someAssemblyRequired import (
|
|
|
34
35
|
)
|
|
35
36
|
from mapFolding.someAssemblyRequired.RecipeJob import RecipeJob
|
|
36
37
|
from mapFolding.someAssemblyRequired.toolboxNumba import parametersNumbaLight, SpicesJobNumba, decorateCallableWithNumba
|
|
37
|
-
from mapFolding.someAssemblyRequired.transformationTools import dictionaryEstimates,
|
|
38
|
+
from mapFolding.someAssemblyRequired.transformationTools import dictionaryEstimates, write_astModule, makeInitializedComputationState
|
|
38
39
|
from pathlib import PurePosixPath
|
|
39
40
|
from typing import cast, NamedTuple
|
|
40
41
|
from Z0Z_tools import autoDecodingRLE
|
|
@@ -131,7 +132,7 @@ def move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsFunction: Ingre
|
|
|
131
132
|
Returns:
|
|
132
133
|
The modified function with parameters converted to initialized variables.
|
|
133
134
|
"""
|
|
134
|
-
ingredientsFunction.imports.update(job.shatteredDataclass.
|
|
135
|
+
ingredientsFunction.imports.update(job.shatteredDataclass.imports)
|
|
135
136
|
|
|
136
137
|
list_argCuzMyBrainRefusesToThink = ingredientsFunction.astFunctionDef.args.args + ingredientsFunction.astFunctionDef.args.posonlyargs + ingredientsFunction.astFunctionDef.args.kwonlyargs
|
|
137
138
|
list_arg_arg: list[ast_Identifier] = [ast_arg.arg for ast_arg in list_argCuzMyBrainRefusesToThink]
|
|
@@ -299,10 +300,11 @@ if __name__ == '__main__':
|
|
|
299
300
|
"""
|
|
300
301
|
|
|
301
302
|
if __name__ == '__main__':
|
|
302
|
-
mapShape = (
|
|
303
|
+
mapShape = (1,46)
|
|
303
304
|
state = makeInitializedComputationState(mapShape)
|
|
304
305
|
# foldsTotalEstimated = getFoldsTotalKnown(state.mapShape) // state.leavesTotal
|
|
305
|
-
foldsTotalEstimated = dictionaryEstimates[state.mapShape] // state.leavesTotal
|
|
306
|
+
# foldsTotalEstimated = dictionaryEstimates[state.mapShape] // state.leavesTotal
|
|
307
|
+
foldsTotalEstimated = 0
|
|
306
308
|
pathModule = PurePosixPath(The.pathPackage, 'jobs')
|
|
307
309
|
pathFilenameFoldsTotal = PurePosixPath(getPathFilenameFoldsTotal(state.mapShape, pathModule))
|
|
308
310
|
aJob = RecipeJob(state, foldsTotalEstimated, pathModule=pathModule, pathFilenameFoldsTotal=pathFilenameFoldsTotal)
|
|
@@ -16,23 +16,14 @@ performance improvements while preserving code semantics and correctness.
|
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
from collections.abc import Callable, Sequence
|
|
19
|
-
from mapFolding
|
|
19
|
+
from mapFolding import NotRequired, TypedDict
|
|
20
|
+
from mapFolding.someAssemblyRequired import ast_Identifier, IngredientsFunction, Make, RecipeSynthesizeFlow, str_nameDOTname
|
|
20
21
|
from mapFolding.someAssemblyRequired.transformationTools import makeNewFlow, write_astModule
|
|
21
22
|
from numba.core.compiler import CompilerBase as numbaCompilerBase
|
|
22
|
-
from typing import Any, cast, Final
|
|
23
|
+
from typing import Any, cast, Final
|
|
23
24
|
import ast
|
|
24
25
|
import dataclasses
|
|
25
26
|
|
|
26
|
-
try:
|
|
27
|
-
from typing import NotRequired
|
|
28
|
-
except Exception:
|
|
29
|
-
from typing_extensions import NotRequired # pyright: ignore[reportShadowedImports]
|
|
30
|
-
|
|
31
|
-
if TYPE_CHECKING:
|
|
32
|
-
from typing import TypedDict
|
|
33
|
-
else:
|
|
34
|
-
TypedDict = dict[str,Any]
|
|
35
|
-
|
|
36
27
|
# Consolidate settings classes through inheritance https://github.com/hunterhogan/mapFolding/issues/15
|
|
37
28
|
theNumbaFlow: RecipeSynthesizeFlow = RecipeSynthesizeFlow()
|
|
38
29
|
|
|
@@ -60,17 +51,8 @@ class ParametersNumba(TypedDict):
|
|
|
60
51
|
signature_or_function: NotRequired[Any | Callable[..., Any] | str | tuple[Any, ...]]
|
|
61
52
|
target: NotRequired[str]
|
|
62
53
|
|
|
63
|
-
parametersNumbaFailEarly: Final[ParametersNumba] = { '_nrt': True, 'boundscheck': True, 'cache': True, 'error_model': 'python', 'fastmath': False, 'forceinline': True, 'inline': 'always', 'looplift': False, 'no_cfunc_wrapper': False, 'no_cpython_wrapper': False, 'nopython': True, 'parallel': False, }
|
|
64
|
-
"""For a production function: speed is irrelevant, error discovery is paramount, must be compatible with anything downstream."""
|
|
65
54
|
parametersNumbaDefault: Final[ParametersNumba] = { '_nrt': True, 'boundscheck': False, 'cache': True, 'error_model': 'numpy', 'fastmath': True, 'forceinline': True, 'inline': 'always', 'looplift': False, 'no_cfunc_wrapper': False, 'no_cpython_wrapper': False, 'nopython': True, 'parallel': False, }
|
|
66
55
|
"""Middle of the road: fast, lean, but will talk to non-jitted functions."""
|
|
67
|
-
parametersNumbaParallelDEFAULT: Final[ParametersNumba] = { **parametersNumbaDefault, '_nrt': True, 'parallel': True, }
|
|
68
|
-
"""Middle of the road: fast, lean, but will talk to non-jitted functions."""
|
|
69
|
-
parametersNumbaSuperJit: Final[ParametersNumba] = { **parametersNumbaDefault, 'no_cfunc_wrapper': True, 'no_cpython_wrapper': True, }
|
|
70
|
-
"""Speed, no helmet, no talking to non-jitted functions."""
|
|
71
|
-
parametersNumbaSuperJitParallel: Final[ParametersNumba] = { **parametersNumbaSuperJit, '_nrt': True, 'parallel': True, }
|
|
72
|
-
"""Speed, no helmet, concurrency, no talking to non-jitted functions."""
|
|
73
|
-
parametersNumbaMinimum: Final[ParametersNumba] = { '_nrt': True, 'boundscheck': True, 'cache': True, 'error_model': 'numpy', 'fastmath': True, 'forceinline': False, 'inline': 'always', 'looplift': False, 'no_cfunc_wrapper': False, 'no_cpython_wrapper': False, 'nopython': False, 'forceobj': True, 'parallel': False, }
|
|
74
56
|
parametersNumbaLight: Final[ParametersNumba] = {'cache': True, 'error_model': 'numpy', 'fastmath': True, 'forceinline': True}
|
|
75
57
|
|
|
76
58
|
Z0Z_numbaDataTypeModule: str_nameDOTname = 'numba'
|
|
@@ -188,11 +170,5 @@ def makeNumbaFlow(numbaFlow: RecipeSynthesizeFlow) -> None:
|
|
|
188
170
|
|
|
189
171
|
write_astModule(ingredientsModuleNumbaUnified, numbaFlow.pathFilenameDispatcher, numbaFlow.packageIdentifier)
|
|
190
172
|
|
|
191
|
-
def getIt(astCallConcurrencyResult: list[ast.Call]) -> Callable[[ast.AST], ast.AST]:
|
|
192
|
-
def workhorse(node: ast.AST) -> ast.AST:
|
|
193
|
-
NodeTourist(be.Call, Then.appendTo(astCallConcurrencyResult)).visit(node)
|
|
194
|
-
return node
|
|
195
|
-
return workhorse
|
|
196
|
-
|
|
197
173
|
if __name__ == '__main__':
|
|
198
174
|
makeNumbaFlow(theNumbaFlow)
|