mapFolding 0.12.0__py3-none-any.whl → 0.12.2__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 +42 -18
- mapFolding/_theSSOT.py +137 -0
- mapFolding/basecamp.py +28 -18
- mapFolding/beDRY.py +21 -19
- mapFolding/dataBaskets.py +170 -18
- mapFolding/datatypes.py +109 -1
- mapFolding/filesystemToolkit.py +38 -33
- mapFolding/oeis.py +209 -93
- mapFolding/someAssemblyRequired/RecipeJob.py +120 -9
- mapFolding/someAssemblyRequired/__init__.py +35 -38
- mapFolding/someAssemblyRequired/_toolIfThis.py +80 -18
- mapFolding/someAssemblyRequired/_toolkitContainers.py +123 -45
- mapFolding/someAssemblyRequired/infoBooth.py +37 -2
- mapFolding/someAssemblyRequired/makeAllModules.py +712 -0
- mapFolding/someAssemblyRequired/makeJobTheorem2Numba.py +111 -48
- mapFolding/someAssemblyRequired/toolkitNumba.py +171 -19
- mapFolding/someAssemblyRequired/transformationTools.py +93 -49
- mapfolding-0.12.2.dist-info/METADATA +167 -0
- mapfolding-0.12.2.dist-info/RECORD +53 -0
- {mapfolding-0.12.0.dist-info → mapfolding-0.12.2.dist-info}/WHEEL +1 -1
- tests/__init__.py +28 -44
- tests/conftest.py +66 -61
- tests/test_computations.py +39 -82
- tests/test_filesystem.py +25 -1
- tests/test_oeis.py +30 -1
- tests/test_other.py +27 -0
- tests/test_tasks.py +31 -1
- mapFolding/someAssemblyRequired/Z0Z_makeAllModules.py +0 -433
- mapFolding/theSSOT.py +0 -34
- mapfolding-0.12.0.dist-info/METADATA +0 -184
- mapfolding-0.12.0.dist-info/RECORD +0 -53
- {mapfolding-0.12.0.dist-info → mapfolding-0.12.2.dist-info}/entry_points.txt +0 -0
- {mapfolding-0.12.0.dist-info → mapfolding-0.12.2.dist-info}/licenses/LICENSE +0 -0
- {mapfolding-0.12.0.dist-info → mapfolding-0.12.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Map folding AST transformation system: Comprehensive transformation orchestration and module generation.
|
|
3
|
+
|
|
4
|
+
This module provides the orchestration layer of the map folding AST transformation system,
|
|
5
|
+
implementing comprehensive tools that coordinate all transformation stages to generate optimized
|
|
6
|
+
implementations with diverse computational strategies and performance characteristics. Building
|
|
7
|
+
upon the foundational pattern recognition, structural decomposition, core transformation tools,
|
|
8
|
+
Numba integration, and configuration management established in previous layers, this module
|
|
9
|
+
executes complete transformation processes that convert high-level dataclass-based algorithms
|
|
10
|
+
into specialized variants optimized for specific execution contexts.
|
|
11
|
+
|
|
12
|
+
The transformation orchestration addresses the full spectrum of optimization requirements for
|
|
13
|
+
map folding computational research through systematic application of the complete transformation
|
|
14
|
+
toolkit. The comprehensive approach decomposes dataclass parameters into primitive values for
|
|
15
|
+
Numba compatibility while removing object-oriented overhead and preserving computational logic,
|
|
16
|
+
generates concurrent execution variants using ProcessPoolExecutor with task division and result
|
|
17
|
+
aggregation, creates dedicated modules for counting variable setup with transformed loop conditions,
|
|
18
|
+
and provides theorem-specific transformations with configurable optimization levels including
|
|
19
|
+
trimmed variants and Numba-accelerated implementations.
|
|
20
|
+
|
|
21
|
+
The orchestration process operates through systematic AST manipulation that analyzes source
|
|
22
|
+
algorithms to extract dataclass dependencies, transforms data access patterns, applies performance
|
|
23
|
+
optimizations, and generates specialized modules with consistent naming conventions and filesystem
|
|
24
|
+
organization. The comprehensive transformation process coordinates pattern recognition for structural
|
|
25
|
+
analysis, dataclass decomposition for parameter optimization, function transformation for signature
|
|
26
|
+
adaptation, Numba integration for compilation optimization, and configuration management for
|
|
27
|
+
systematic generation control.
|
|
28
|
+
|
|
29
|
+
Generated modules maintain algorithmic correctness while providing significant performance
|
|
30
|
+
improvements through just-in-time compilation, parallel execution, and optimized data structures
|
|
31
|
+
tailored for specific computational requirements essential to large-scale map folding research.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from astToolkit import (
|
|
35
|
+
astModuleToIngredientsFunction, Be, ClassIsAndAttribute, DOT, extractClassDef, extractFunctionDef,
|
|
36
|
+
Grab, identifierDotAttribute, IngredientsFunction, IngredientsModule, LedgerOfImports, Make,
|
|
37
|
+
NodeChanger, NodeTourist, parseLogicalPath2astModule, parsePathFilename2astModule, Then,
|
|
38
|
+
)
|
|
39
|
+
from astToolkit.transformationTools import (
|
|
40
|
+
inlineFunctionDef, removeUnusedParameters, write_astModule,
|
|
41
|
+
)
|
|
42
|
+
from collections.abc import Sequence
|
|
43
|
+
from mapFolding import packageSettings
|
|
44
|
+
from mapFolding.someAssemblyRequired import (
|
|
45
|
+
DeReConstructField2ast, IfThis, ShatteredDataclass, sourceCallableDispatcherDEFAULT,
|
|
46
|
+
)
|
|
47
|
+
from mapFolding.someAssemblyRequired.infoBooth import (
|
|
48
|
+
algorithmSourceModuleDEFAULT, dataPackingModuleIdentifierDEFAULT, logicalPathInfixDEFAULT,
|
|
49
|
+
sourceCallableIdentifierDEFAULT, theCountingIdentifierDEFAULT,
|
|
50
|
+
)
|
|
51
|
+
from mapFolding.someAssemblyRequired.toolkitNumba import (
|
|
52
|
+
decorateCallableWithNumba, parametersNumbaLight,
|
|
53
|
+
)
|
|
54
|
+
from mapFolding.someAssemblyRequired.transformationTools import (
|
|
55
|
+
removeDataclassFromFunction, shatter_dataclassesDOTdataclass,
|
|
56
|
+
unpackDataclassCallFunctionRepackDataclass,
|
|
57
|
+
)
|
|
58
|
+
from os import PathLike
|
|
59
|
+
from pathlib import PurePath
|
|
60
|
+
from typing import Any, cast
|
|
61
|
+
from Z0Z_tools import importLogicalPath2Callable, raiseIfNone
|
|
62
|
+
import ast
|
|
63
|
+
import dataclasses
|
|
64
|
+
|
|
65
|
+
def findDataclass(ingredientsFunction: IngredientsFunction) -> tuple[str, str, str]:
|
|
66
|
+
"""
|
|
67
|
+
Extract dataclass information from a function's AST for transformation operations.
|
|
68
|
+
|
|
69
|
+
Analyzes the first parameter of a function to identify the dataclass type annotation
|
|
70
|
+
and instance identifier, then locates the module where the dataclass is defined by
|
|
71
|
+
examining the function's import statements. This information is essential for
|
|
72
|
+
dataclass decomposition and transformation operations.
|
|
73
|
+
|
|
74
|
+
Parameters:
|
|
75
|
+
ingredientsFunction: Function container with AST and import information
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
dataclassLogicalPathModule: Module logical path where the dataclass is defined
|
|
79
|
+
dataclassIdentifier: Class name of the dataclass
|
|
80
|
+
dataclassInstanceIdentifier: Parameter name for the dataclass instance
|
|
81
|
+
|
|
82
|
+
Raises:
|
|
83
|
+
ValueError: If dataclass information cannot be extracted from the function
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
dataclassName: ast.expr = raiseIfNone(NodeTourist(Be.arg, Then.extractIt(DOT.annotation)).captureLastMatch(ingredientsFunction.astFunctionDef))
|
|
87
|
+
dataclassIdentifier: str = raiseIfNone(NodeTourist(Be.Name, Then.extractIt(DOT.id)).captureLastMatch(dataclassName))
|
|
88
|
+
dataclassLogicalPathModule = None
|
|
89
|
+
for moduleWithLogicalPath, listNameTuples in ingredientsFunction.imports._dictionaryImportFrom.items():
|
|
90
|
+
for nameTuple in listNameTuples:
|
|
91
|
+
if nameTuple[0] == dataclassIdentifier:
|
|
92
|
+
dataclassLogicalPathModule = moduleWithLogicalPath
|
|
93
|
+
break
|
|
94
|
+
if dataclassLogicalPathModule:
|
|
95
|
+
break
|
|
96
|
+
dataclassInstanceIdentifier: identifierDotAttribute = raiseIfNone(NodeTourist(Be.arg, Then.extractIt(DOT.arg)).captureLastMatch(ingredientsFunction.astFunctionDef))
|
|
97
|
+
return raiseIfNone(dataclassLogicalPathModule), dataclassIdentifier, dataclassInstanceIdentifier
|
|
98
|
+
|
|
99
|
+
def _getLogicalPath(packageName: str | None = None, logicalPathInfix: str | None = None, moduleIdentifier: str | None = None, *modules: str) -> identifierDotAttribute:
|
|
100
|
+
"""
|
|
101
|
+
Construct logical module path by joining package and module components.
|
|
102
|
+
|
|
103
|
+
Builds a dot-separated logical path string from optional package name, infix path
|
|
104
|
+
components, and module identifiers. This standardizes module path construction
|
|
105
|
+
across the code generation system and ensures consistent naming conventions.
|
|
106
|
+
|
|
107
|
+
Parameters:
|
|
108
|
+
packageName: Root package name for the logical path
|
|
109
|
+
logicalPathInfix: Middle path component (typically 'syntheticModules')
|
|
110
|
+
moduleIdentifier: Primary module identifier
|
|
111
|
+
*modules: Additional module path components to append
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
logicalPath: Dot-separated logical path string suitable for module import operations
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
listLogicalPathParts: list[str] = []
|
|
118
|
+
if packageName:
|
|
119
|
+
listLogicalPathParts.append(packageName)
|
|
120
|
+
if logicalPathInfix:
|
|
121
|
+
listLogicalPathParts.append(logicalPathInfix)
|
|
122
|
+
if moduleIdentifier:
|
|
123
|
+
listLogicalPathParts.append(moduleIdentifier)
|
|
124
|
+
if modules:
|
|
125
|
+
listLogicalPathParts.extend(modules)
|
|
126
|
+
logicalPath = '.'.join(listLogicalPathParts)
|
|
127
|
+
return logicalPath
|
|
128
|
+
|
|
129
|
+
def getModule(packageName: str | None = packageSettings.packageName, logicalPathInfix: str | None = logicalPathInfixDEFAULT, moduleIdentifier: str | None = algorithmSourceModuleDEFAULT) -> ast.Module:
|
|
130
|
+
"""
|
|
131
|
+
Load source algorithm module as AST for transformation operations.
|
|
132
|
+
|
|
133
|
+
Retrieves the specified module and parses it into an AST representation that can
|
|
134
|
+
be manipulated by the transformation tools. This provides the foundation for all
|
|
135
|
+
code generation operations by making the source algorithms available for analysis
|
|
136
|
+
and modification.
|
|
137
|
+
|
|
138
|
+
Parameters:
|
|
139
|
+
packageName: Package containing the source module
|
|
140
|
+
logicalPathInfix: Path component within the package structure
|
|
141
|
+
moduleIdentifier: Specific module containing the algorithms
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
astModule: AST module representation ready for transformation operations
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
logicalPathSourceModule: identifierDotAttribute = _getLogicalPath(packageName, logicalPathInfix, moduleIdentifier)
|
|
148
|
+
astModule: ast.Module = parseLogicalPath2astModule(logicalPathSourceModule)
|
|
149
|
+
return astModule
|
|
150
|
+
|
|
151
|
+
def _getPathFilename(pathRoot: PathLike[str] | PurePath | None = packageSettings.pathPackage, logicalPathInfix: PathLike[str] | PurePath | str | None = None, moduleIdentifier: str = '', fileExtension: str = packageSettings.fileExtension) -> PurePath:
|
|
152
|
+
"""
|
|
153
|
+
Construct filesystem path for generated module files.
|
|
154
|
+
|
|
155
|
+
Builds the complete filesystem path where generated modules will be written,
|
|
156
|
+
combining root path, optional infix directory, module name, and file extension.
|
|
157
|
+
This ensures consistent file organization across all generated code.
|
|
158
|
+
|
|
159
|
+
Parameters:
|
|
160
|
+
pathRoot: Base directory for the package structure
|
|
161
|
+
logicalPathInfix: Subdirectory for organizing generated modules
|
|
162
|
+
moduleIdentifier: Name of the specific module file
|
|
163
|
+
fileExtension: File extension for Python modules
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
pathFilename: Complete filesystem path for the generated module file
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
pathFilename = PurePath(moduleIdentifier + fileExtension)
|
|
170
|
+
if logicalPathInfix:
|
|
171
|
+
pathFilename = PurePath(logicalPathInfix, pathFilename)
|
|
172
|
+
if pathRoot:
|
|
173
|
+
pathFilename = PurePath(pathRoot, pathFilename)
|
|
174
|
+
return pathFilename
|
|
175
|
+
|
|
176
|
+
def makeInitializeGroupsOfFolds(astModule: ast.Module, moduleIdentifier: str, callableIdentifier: str | None = None, logicalPathInfix: PathLike[str] | PurePath | str | None = None, sourceCallableDispatcher: str | None = None) -> PurePath:
|
|
177
|
+
"""
|
|
178
|
+
Generate initialization module for counting variable setup.
|
|
179
|
+
|
|
180
|
+
Creates a specialized module containing initialization logic for the counting variables
|
|
181
|
+
used in map folding computations. The generated function transforms the original
|
|
182
|
+
algorithm's loop conditions to use equality comparisons instead of greater-than
|
|
183
|
+
comparisons, optimizing the initialization phase.
|
|
184
|
+
|
|
185
|
+
This transformation is particularly important for ensuring that counting variables
|
|
186
|
+
are properly initialized before the main computational loops begin executing.
|
|
187
|
+
|
|
188
|
+
Parameters:
|
|
189
|
+
astModule: Source module containing the base algorithm
|
|
190
|
+
moduleIdentifier: Name for the generated initialization module
|
|
191
|
+
callableIdentifier: Name for the initialization function
|
|
192
|
+
logicalPathInfix: Directory path for organizing the generated module
|
|
193
|
+
sourceCallableDispatcher: Optional dispatcher function identifier
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
pathFilename: Filesystem path where the initialization module was written
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
sourceCallableIdentifier: identifierDotAttribute = sourceCallableIdentifierDEFAULT
|
|
200
|
+
if callableIdentifier is None:
|
|
201
|
+
callableIdentifier = sourceCallableIdentifier
|
|
202
|
+
ingredientsFunction = IngredientsFunction(inlineFunctionDef(sourceCallableIdentifier, astModule), LedgerOfImports(astModule))
|
|
203
|
+
ingredientsFunction.astFunctionDef.name = callableIdentifier
|
|
204
|
+
|
|
205
|
+
dataclassInstanceIdentifier: identifierDotAttribute = raiseIfNone(NodeTourist(Be.arg, Then.extractIt(DOT.arg)).captureLastMatch(ingredientsFunction.astFunctionDef))
|
|
206
|
+
theCountingIdentifier: identifierDotAttribute = theCountingIdentifierDEFAULT
|
|
207
|
+
|
|
208
|
+
findThis = IfThis.isWhileAttributeNamespaceIdentifierGreaterThan0(dataclassInstanceIdentifier, 'leaf1ndex')
|
|
209
|
+
doThat = Grab.testAttribute(Grab.andDoAllOf([Grab.opsAttribute(Then.replaceWith([ast.Eq()])), Grab.leftAttribute(Grab.attrAttribute(Then.replaceWith(theCountingIdentifier)))])) # pyright: ignore[reportArgumentType]
|
|
210
|
+
NodeChanger(findThis, doThat).visit(ingredientsFunction.astFunctionDef.body[0])
|
|
211
|
+
|
|
212
|
+
ingredientsModule = IngredientsModule(ingredientsFunction)
|
|
213
|
+
pathFilename: PurePath = _getPathFilename(packageSettings.pathPackage, logicalPathInfix, moduleIdentifier)
|
|
214
|
+
write_astModule(ingredientsModule, pathFilename, packageSettings.packageName)
|
|
215
|
+
|
|
216
|
+
return pathFilename
|
|
217
|
+
|
|
218
|
+
def makeDaoOfMapFolding(astModule: ast.Module, moduleIdentifier: str, callableIdentifier: str | None = None, logicalPathInfix: PathLike[str] | PurePath | str | None = None, sourceCallableDispatcher: str | None = None) -> PurePath:
|
|
219
|
+
"""
|
|
220
|
+
Generate Numba-optimized sequential implementation of map folding algorithm.
|
|
221
|
+
|
|
222
|
+
Creates a high-performance sequential version of the map folding algorithm by:
|
|
223
|
+
1. Decomposing dataclass parameters into individual primitive values
|
|
224
|
+
2. Removing dataclass dependencies that are incompatible with Numba
|
|
225
|
+
3. Applying Numba decorators for just-in-time compilation
|
|
226
|
+
4. Optionally including a dispatcher function for dataclass integration
|
|
227
|
+
|
|
228
|
+
The generated module provides significant performance improvements over the
|
|
229
|
+
original dataclass-based implementation while maintaining algorithmic correctness.
|
|
230
|
+
The transformation preserves all computational logic while restructuring data
|
|
231
|
+
access patterns for optimal Numba compilation.
|
|
232
|
+
|
|
233
|
+
Parameters:
|
|
234
|
+
astModule: Source module containing the base algorithm
|
|
235
|
+
moduleIdentifier: Name for the generated optimized module
|
|
236
|
+
callableIdentifier: Name for the main computational function
|
|
237
|
+
logicalPathInfix: Directory path for organizing the generated module
|
|
238
|
+
sourceCallableDispatcher: Optional dispatcher function for dataclass integration
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
pathFilename: Filesystem path where the optimized module was written
|
|
242
|
+
"""
|
|
243
|
+
|
|
244
|
+
sourceCallableIdentifier: identifierDotAttribute = sourceCallableIdentifierDEFAULT
|
|
245
|
+
ingredientsFunction = IngredientsFunction(inlineFunctionDef(sourceCallableIdentifier, astModule), LedgerOfImports(astModule))
|
|
246
|
+
|
|
247
|
+
if callableIdentifier is None:
|
|
248
|
+
callableIdentifier = sourceCallableIdentifier
|
|
249
|
+
ingredientsFunction.astFunctionDef.name = callableIdentifier
|
|
250
|
+
|
|
251
|
+
shatteredDataclass: ShatteredDataclass = shatter_dataclassesDOTdataclass(*findDataclass(ingredientsFunction))
|
|
252
|
+
|
|
253
|
+
ingredientsFunction.imports.update(shatteredDataclass.imports)
|
|
254
|
+
ingredientsFunction: IngredientsFunction = removeDataclassFromFunction(ingredientsFunction, shatteredDataclass)
|
|
255
|
+
ingredientsFunction = removeUnusedParameters(ingredientsFunction)
|
|
256
|
+
ingredientsFunction = decorateCallableWithNumba(ingredientsFunction, parametersNumbaLight)
|
|
257
|
+
|
|
258
|
+
ingredientsModule = IngredientsModule(ingredientsFunction)
|
|
259
|
+
|
|
260
|
+
if sourceCallableDispatcher is not None:
|
|
261
|
+
|
|
262
|
+
ingredientsFunctionDispatcher: IngredientsFunction = astModuleToIngredientsFunction(astModule, sourceCallableDispatcher)
|
|
263
|
+
ingredientsFunctionDispatcher.imports.update(shatteredDataclass.imports)
|
|
264
|
+
targetCallableIdentifier = ingredientsFunction.astFunctionDef.name
|
|
265
|
+
ingredientsFunctionDispatcher = unpackDataclassCallFunctionRepackDataclass(ingredientsFunctionDispatcher, targetCallableIdentifier, shatteredDataclass)
|
|
266
|
+
astTuple: ast.expr = raiseIfNone(NodeTourist(Be.Return, Then.extractIt(DOT.value)).captureLastMatch(ingredientsFunction.astFunctionDef))
|
|
267
|
+
cast(ast.Tuple, astTuple).ctx = ast.Store()
|
|
268
|
+
|
|
269
|
+
findThis = ClassIsAndAttribute.valueIs(ast.Assign, IfThis.isCallIdentifier(targetCallableIdentifier))
|
|
270
|
+
doThat = Then.replaceWith(Make.Assign([astTuple], value=Make.Call(Make.Name(targetCallableIdentifier), cast(ast.Tuple, astTuple).elts)))
|
|
271
|
+
changeAssignCallToTarget = NodeChanger(findThis, doThat)
|
|
272
|
+
changeAssignCallToTarget.visit(ingredientsFunctionDispatcher.astFunctionDef)
|
|
273
|
+
|
|
274
|
+
ingredientsModule.appendIngredientsFunction(ingredientsFunctionDispatcher)
|
|
275
|
+
|
|
276
|
+
ingredientsModule.removeImportFromModule('numpy')
|
|
277
|
+
|
|
278
|
+
pathFilename: PurePath = _getPathFilename(packageSettings.pathPackage, logicalPathInfix, moduleIdentifier)
|
|
279
|
+
|
|
280
|
+
write_astModule(ingredientsModule, pathFilename, packageSettings.packageName)
|
|
281
|
+
|
|
282
|
+
return pathFilename
|
|
283
|
+
|
|
284
|
+
def makeDaoOfMapFoldingParallel(astModule: ast.Module, moduleIdentifier: str, callableIdentifier: str | None = None, logicalPathInfix: PathLike[str] | PurePath | str | None = None, sourceCallableDispatcher: str | None = None) -> PurePath:
|
|
285
|
+
"""
|
|
286
|
+
Generate parallel implementation with concurrent execution and task division.
|
|
287
|
+
|
|
288
|
+
Creates a parallel processing version of the map folding algorithm that distributes
|
|
289
|
+
computational work across multiple processes using ProcessPoolExecutor. The
|
|
290
|
+
implementation includes:
|
|
291
|
+
|
|
292
|
+
1. Dataclass decomposition for both base and parallel state fields
|
|
293
|
+
2. Task division logic that partitions work based on leaf indices
|
|
294
|
+
3. Concurrent execution management with future objects
|
|
295
|
+
4. Result aggregation from multiple parallel computations
|
|
296
|
+
5. Numba optimization for the core computational kernels
|
|
297
|
+
|
|
298
|
+
The generated module contains multiple functions:
|
|
299
|
+
- Core counting function with parallel-aware task filtering
|
|
300
|
+
- Dataclass unpacking/repacking function for process communication
|
|
301
|
+
- Main dispatcher function that manages the parallel execution pipeline
|
|
302
|
+
|
|
303
|
+
Parameters:
|
|
304
|
+
astModule: Source module containing the base algorithm
|
|
305
|
+
moduleIdentifier: Name for the generated parallel module
|
|
306
|
+
callableIdentifier: Name for the core parallel counting function
|
|
307
|
+
logicalPathInfix: Directory path for organizing the generated module
|
|
308
|
+
sourceCallableDispatcher: Optional dispatcher function identifier
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
pathFilename: Filesystem path where the parallel module was written
|
|
312
|
+
"""
|
|
313
|
+
|
|
314
|
+
sourceCallableIdentifier = sourceCallableIdentifierDEFAULT
|
|
315
|
+
if callableIdentifier is None:
|
|
316
|
+
callableIdentifier = sourceCallableIdentifier
|
|
317
|
+
ingredientsFunction = IngredientsFunction(inlineFunctionDef(sourceCallableIdentifier, astModule), LedgerOfImports(astModule))
|
|
318
|
+
ingredientsFunction.astFunctionDef.name = callableIdentifier
|
|
319
|
+
|
|
320
|
+
dataclassName: ast.expr = raiseIfNone(NodeTourist(Be.arg, Then.extractIt(DOT.annotation)).captureLastMatch(ingredientsFunction.astFunctionDef))
|
|
321
|
+
dataclassIdentifier: str = raiseIfNone(NodeTourist(Be.Name, Then.extractIt(DOT.id)).captureLastMatch(dataclassName))
|
|
322
|
+
|
|
323
|
+
dataclassLogicalPathModule = None
|
|
324
|
+
for moduleWithLogicalPath, listNameTuples in ingredientsFunction.imports._dictionaryImportFrom.items():
|
|
325
|
+
for nameTuple in listNameTuples:
|
|
326
|
+
if nameTuple[0] == dataclassIdentifier:
|
|
327
|
+
dataclassLogicalPathModule = moduleWithLogicalPath
|
|
328
|
+
break
|
|
329
|
+
if dataclassLogicalPathModule:
|
|
330
|
+
break
|
|
331
|
+
if dataclassLogicalPathModule is None:
|
|
332
|
+
raise Exception
|
|
333
|
+
dataclassInstanceIdentifier: identifierDotAttribute = raiseIfNone(NodeTourist(Be.arg, Then.extractIt(DOT.arg)).captureLastMatch(ingredientsFunction.astFunctionDef))
|
|
334
|
+
shatteredDataclass: ShatteredDataclass = shatter_dataclassesDOTdataclass(dataclassLogicalPathModule, dataclassIdentifier, dataclassInstanceIdentifier)
|
|
335
|
+
|
|
336
|
+
# Start add the parallel state fields to the count function ================================================
|
|
337
|
+
dataclassBaseFields: tuple[dataclasses.Field[Any], ...] = dataclasses.fields(importLogicalPath2Callable(dataclassLogicalPathModule, dataclassIdentifier)) # pyright: ignore [reportArgumentType]
|
|
338
|
+
dataclassIdentifierParallel: identifierDotAttribute = 'Parallel' + dataclassIdentifier
|
|
339
|
+
dataclassFieldsParallel: tuple[dataclasses.Field[Any], ...] = dataclasses.fields(importLogicalPath2Callable(dataclassLogicalPathModule, dataclassIdentifierParallel)) # pyright: ignore [reportArgumentType]
|
|
340
|
+
onlyParallelFields: list[dataclasses.Field[Any]] = [field for field in dataclassFieldsParallel if field.name not in [fieldBase.name for fieldBase in dataclassBaseFields]]
|
|
341
|
+
|
|
342
|
+
Official_fieldOrder: list[str] = []
|
|
343
|
+
dictionaryDeReConstruction: dict[str, DeReConstructField2ast] = {}
|
|
344
|
+
|
|
345
|
+
dataclassClassDef: ast.ClassDef | None = extractClassDef(parseLogicalPath2astModule(dataclassLogicalPathModule), dataclassIdentifierParallel)
|
|
346
|
+
if not isinstance(dataclassClassDef, ast.ClassDef):
|
|
347
|
+
raise ValueError(f"I could not find `{dataclassIdentifierParallel = }` in `{dataclassLogicalPathModule = }`.")
|
|
348
|
+
|
|
349
|
+
for aField in onlyParallelFields:
|
|
350
|
+
Official_fieldOrder.append(aField.name)
|
|
351
|
+
dictionaryDeReConstruction[aField.name] = DeReConstructField2ast(dataclassLogicalPathModule, dataclassClassDef, dataclassInstanceIdentifier, aField)
|
|
352
|
+
|
|
353
|
+
shatteredDataclassParallel = ShatteredDataclass(
|
|
354
|
+
countingVariableAnnotation=shatteredDataclass.countingVariableAnnotation,
|
|
355
|
+
countingVariableName=shatteredDataclass.countingVariableName,
|
|
356
|
+
field2AnnAssign={**shatteredDataclass.field2AnnAssign, **{dictionaryDeReConstruction[field].name: dictionaryDeReConstruction[field].astAnnAssignConstructor for field in Official_fieldOrder}},
|
|
357
|
+
Z0Z_field2AnnAssign={**shatteredDataclass.Z0Z_field2AnnAssign, **{dictionaryDeReConstruction[field].name: dictionaryDeReConstruction[field].Z0Z_hack for field in Official_fieldOrder}},
|
|
358
|
+
list_argAnnotated4ArgumentsSpecification=shatteredDataclass.list_argAnnotated4ArgumentsSpecification + [dictionaryDeReConstruction[field].ast_argAnnotated for field in Official_fieldOrder],
|
|
359
|
+
list_keyword_field__field4init=shatteredDataclass.list_keyword_field__field4init + [dictionaryDeReConstruction[field].ast_keyword_field__field for field in Official_fieldOrder if dictionaryDeReConstruction[field].init],
|
|
360
|
+
listAnnotations=shatteredDataclass.listAnnotations + [dictionaryDeReConstruction[field].astAnnotation for field in Official_fieldOrder],
|
|
361
|
+
listName4Parameters=shatteredDataclass.listName4Parameters + [dictionaryDeReConstruction[field].astName for field in Official_fieldOrder],
|
|
362
|
+
listUnpack=shatteredDataclass.listUnpack + [Make.AnnAssign(dictionaryDeReConstruction[field].astName, dictionaryDeReConstruction[field].astAnnotation, dictionaryDeReConstruction[field].ast_nameDOTname) for field in Official_fieldOrder],
|
|
363
|
+
map_stateDOTfield2Name={**shatteredDataclass.map_stateDOTfield2Name, **{dictionaryDeReConstruction[field].ast_nameDOTname: dictionaryDeReConstruction[field].astName for field in Official_fieldOrder}},
|
|
364
|
+
)
|
|
365
|
+
shatteredDataclassParallel.fragments4AssignmentOrParameters = Make.Tuple(shatteredDataclassParallel.listName4Parameters, ast.Store())
|
|
366
|
+
shatteredDataclassParallel.repack = Make.Assign([Make.Name(dataclassInstanceIdentifier)], value=Make.Call(Make.Name(dataclassIdentifierParallel), list_keyword=shatteredDataclassParallel.list_keyword_field__field4init))
|
|
367
|
+
shatteredDataclassParallel.signatureReturnAnnotation = Make.Subscript(Make.Name('tuple'), Make.Tuple(shatteredDataclassParallel.listAnnotations))
|
|
368
|
+
|
|
369
|
+
shatteredDataclassParallel.imports.update(*(dictionaryDeReConstruction[field].ledger for field in Official_fieldOrder))
|
|
370
|
+
shatteredDataclassParallel.imports.addImportFrom_asStr(dataclassLogicalPathModule, dataclassIdentifierParallel)
|
|
371
|
+
shatteredDataclassParallel.imports.update(shatteredDataclass.imports)
|
|
372
|
+
shatteredDataclassParallel.imports.removeImportFrom(dataclassLogicalPathModule, dataclassIdentifier)
|
|
373
|
+
|
|
374
|
+
# End add the parallel state fields to the count function ================================================
|
|
375
|
+
|
|
376
|
+
ingredientsFunction.imports.update(shatteredDataclassParallel.imports)
|
|
377
|
+
ingredientsFunction: IngredientsFunction = removeDataclassFromFunction(ingredientsFunction, shatteredDataclassParallel)
|
|
378
|
+
|
|
379
|
+
# Start add the parallel logic to the count function ================================================
|
|
380
|
+
|
|
381
|
+
findThis = ClassIsAndAttribute.testIs(ast.While, ClassIsAndAttribute.leftIs(ast.Compare, IfThis.isNameIdentifier('leafConnectee')))
|
|
382
|
+
doThat = Then.extractIt(DOT.body)
|
|
383
|
+
captureCountGapsCodeBlock: NodeTourist[ast.While, Sequence[ast.stmt]] = NodeTourist(findThis, doThat)
|
|
384
|
+
countGapsCodeBlock: Sequence[ast.stmt] = raiseIfNone(captureCountGapsCodeBlock.captureLastMatch(ingredientsFunction.astFunctionDef))
|
|
385
|
+
|
|
386
|
+
thisIsMyTaskIndexCodeBlock = ast.If(ast.BoolOp(ast.Or()
|
|
387
|
+
, values=[ast.Compare(ast.Name('leaf1ndex'), ops=[ast.NotEq()], comparators=[ast.Name('taskDivisions')])
|
|
388
|
+
, ast.Compare(Make.Mod.join([ast.Name('leafConnectee'), ast.Name('taskDivisions')]), ops=[ast.Eq()], comparators=[ast.Name('taskIndex')])])
|
|
389
|
+
, body=list(countGapsCodeBlock[0:-1]))
|
|
390
|
+
|
|
391
|
+
countGapsCodeBlockNew: list[ast.stmt] = [thisIsMyTaskIndexCodeBlock, countGapsCodeBlock[-1]]
|
|
392
|
+
|
|
393
|
+
doThat = Grab.bodyAttribute(Then.replaceWith(countGapsCodeBlockNew))
|
|
394
|
+
NodeChanger(findThis, doThat).visit(ingredientsFunction.astFunctionDef)
|
|
395
|
+
|
|
396
|
+
# End add the parallel logic to the count function ================================================
|
|
397
|
+
|
|
398
|
+
ingredientsFunction = removeUnusedParameters(ingredientsFunction)
|
|
399
|
+
|
|
400
|
+
ingredientsFunction = decorateCallableWithNumba(ingredientsFunction, parametersNumbaLight)
|
|
401
|
+
|
|
402
|
+
# Start unpack/repack the dataclass function ================================================
|
|
403
|
+
sourceCallableIdentifier = sourceCallableDispatcherDEFAULT
|
|
404
|
+
|
|
405
|
+
unRepackDataclass: IngredientsFunction = astModuleToIngredientsFunction(astModule, sourceCallableIdentifier)
|
|
406
|
+
unRepackDataclass.astFunctionDef.name = 'unRepack' + dataclassIdentifierParallel
|
|
407
|
+
unRepackDataclass.imports.update(shatteredDataclassParallel.imports)
|
|
408
|
+
findThis = ClassIsAndAttribute.annotationIs(ast.arg, IfThis.isNameIdentifier(dataclassIdentifier))
|
|
409
|
+
doThat = Grab.annotationAttribute(Grab.idAttribute(Then.replaceWith(dataclassIdentifierParallel))) # pyright: ignore[reportArgumentType]
|
|
410
|
+
NodeChanger(findThis, doThat).visit(unRepackDataclass.astFunctionDef)
|
|
411
|
+
unRepackDataclass.astFunctionDef.returns = Make.Name(dataclassIdentifierParallel)
|
|
412
|
+
targetCallableIdentifier: identifierDotAttribute = ingredientsFunction.astFunctionDef.name
|
|
413
|
+
unRepackDataclass = unpackDataclassCallFunctionRepackDataclass(unRepackDataclass, targetCallableIdentifier, shatteredDataclassParallel)
|
|
414
|
+
|
|
415
|
+
astTuple: ast.expr = raiseIfNone(NodeTourist(Be.Return, Then.extractIt(DOT.value)).captureLastMatch(ingredientsFunction.astFunctionDef))
|
|
416
|
+
cast(ast.Tuple, astTuple).ctx = ast.Store()
|
|
417
|
+
findThis = ClassIsAndAttribute.valueIs(ast.Assign, IfThis.isCallIdentifier(targetCallableIdentifier))
|
|
418
|
+
doThat = Then.replaceWith(Make.Assign([astTuple], value=Make.Call(Make.Name(targetCallableIdentifier), cast(ast.Tuple, astTuple).elts)))
|
|
419
|
+
changeAssignCallToTarget = NodeChanger(findThis, doThat)
|
|
420
|
+
changeAssignCallToTarget.visit(unRepackDataclass.astFunctionDef)
|
|
421
|
+
|
|
422
|
+
ingredientsDoTheNeedful: IngredientsFunction = IngredientsFunction(
|
|
423
|
+
astFunctionDef = Make.FunctionDef('doTheNeedful'
|
|
424
|
+
, argumentSpecification=Make.arguments(list_arg=[Make.arg('state', annotation=Make.Name(dataclassIdentifierParallel)), Make.arg('concurrencyLimit', annotation=Make.Name('int'))])
|
|
425
|
+
, body=[Make.Assign([Make.Name('stateParallel', ast.Store())], value=Make.Call(Make.Name('deepcopy'), listParameters=[Make.Name('state')]))
|
|
426
|
+
, Make.AnnAssign(Make.Name('listStatesParallel', ast.Store()), annotation=Make.Subscript(value=Make.Name('list'), slice=Make.Name(dataclassIdentifierParallel))
|
|
427
|
+
, value=Make.Mult.join([Make.List([Make.Name('stateParallel')]), Make.Attribute(Make.Name('stateParallel'), 'taskDivisions')]))
|
|
428
|
+
, Make.AnnAssign(Make.Name('groupsOfFoldsTotal', ast.Store()), annotation=Make.Name('int'), value=Make.Constant(value=0))
|
|
429
|
+
|
|
430
|
+
, Make.AnnAssign(Make.Name('dictionaryConcurrency', ast.Store()), annotation=Make.Subscript(value=Make.Name('dict'), slice=Make.Tuple([Make.Name('int'), Make.Subscript(value=Make.Name('ConcurrentFuture'), slice=Make.Name(dataclassIdentifierParallel))])), value=Make.Dict())
|
|
431
|
+
, Make.With(items=[Make.withitem(context_expr=Make.Call(Make.Name('ProcessPoolExecutor'), listParameters=[Make.Name('concurrencyLimit')]), optional_vars=Make.Name('concurrencyManager', ast.Store()))]
|
|
432
|
+
, body=[Make.For(Make.Name('indexSherpa', ast.Store()), iter=Make.Call(Make.Name('range'), listParameters=[Make.Attribute(Make.Name('stateParallel'), 'taskDivisions')])
|
|
433
|
+
, body=[Make.Assign([Make.Name('state', ast.Store())], value=Make.Call(Make.Name('deepcopy'), listParameters=[Make.Name('stateParallel')]))
|
|
434
|
+
, Make.Assign([Make.Attribute(Make.Name('state'), 'taskIndex', context=ast.Store())], value=Make.Name('indexSherpa'))
|
|
435
|
+
, Make.Assign([Make.Subscript(Make.Name('dictionaryConcurrency'), slice=Make.Name('indexSherpa'), context=ast.Store())], value=Make.Call(Make.Attribute(Make.Name('concurrencyManager'), 'submit'), listParameters=[Make.Name(unRepackDataclass.astFunctionDef.name), Make.Name('state')]))])
|
|
436
|
+
, Make.For(Make.Name('indexSherpa', ast.Store()), iter=Make.Call(Make.Name('range'), listParameters=[Make.Attribute(Make.Name('stateParallel'), 'taskDivisions')])
|
|
437
|
+
, body=[Make.Assign([Make.Subscript(Make.Name('listStatesParallel'), slice=Make.Name('indexSherpa'), context=ast.Store())], value=Make.Call(Make.Attribute(Make.Subscript(Make.Name('dictionaryConcurrency'), slice=Make.Name('indexSherpa')), 'result')))
|
|
438
|
+
, Make.AugAssign(Make.Name('groupsOfFoldsTotal', ast.Store()), op=ast.Add(), value=Make.Attribute(Make.Subscript(Make.Name('listStatesParallel'), slice=Make.Name('indexSherpa')), 'groupsOfFolds'))])])
|
|
439
|
+
|
|
440
|
+
, Make.AnnAssign(Make.Name('foldsTotal', ast.Store()), annotation=Make.Name('int'), value=Make.Mult.join([Make.Name('groupsOfFoldsTotal'), Make.Attribute(Make.Name('stateParallel'), 'leavesTotal')]))
|
|
441
|
+
, Make.Return(Make.Tuple([Make.Name('foldsTotal'), Make.Name('listStatesParallel')]))]
|
|
442
|
+
, returns=Make.Subscript(Make.Name('tuple'), slice=Make.Tuple([Make.Name('int'), Make.Subscript(Make.Name('list'), slice=Make.Name(dataclassIdentifierParallel))])))
|
|
443
|
+
, imports = LedgerOfImports(Make.Module([Make.ImportFrom('concurrent.futures', list_alias=[Make.alias('Future', asName='ConcurrentFuture'), Make.alias('ProcessPoolExecutor')]),
|
|
444
|
+
Make.ImportFrom('copy', list_alias=[Make.alias('deepcopy')]),
|
|
445
|
+
Make.ImportFrom('multiprocessing', list_alias=[Make.alias('set_start_method', asName='multiprocessing_set_start_method')]),])
|
|
446
|
+
)
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
ingredientsModule = IngredientsModule([ingredientsFunction, unRepackDataclass, ingredientsDoTheNeedful]
|
|
450
|
+
, prologue = Make.Module([Make.If(test=Make.Compare(left=Make.Name('__name__'), ops=[Make.Eq()], comparators=[Make.Constant('__main__')]), body=[Make.Expr(Make.Call(Make.Name('multiprocessing_set_start_method'), listParameters=[Make.Constant('spawn')]))])])
|
|
451
|
+
)
|
|
452
|
+
ingredientsModule.removeImportFromModule('numpy')
|
|
453
|
+
|
|
454
|
+
pathFilename: PurePath = _getPathFilename(packageSettings.pathPackage, logicalPathInfix, moduleIdentifier)
|
|
455
|
+
|
|
456
|
+
write_astModule(ingredientsModule, pathFilename, packageSettings.packageName)
|
|
457
|
+
|
|
458
|
+
return pathFilename
|
|
459
|
+
|
|
460
|
+
def makeTheorem2(astModule: ast.Module, moduleIdentifier: str, callableIdentifier: str | None = None, logicalPathInfix: PathLike[str] | PurePath | str | None = None, sourceCallableDispatcher: str | None = None) -> PurePath:
|
|
461
|
+
"""
|
|
462
|
+
Generate optimized implementation applying Theorem 2 mathematical optimizations.
|
|
463
|
+
|
|
464
|
+
Creates a specialized version of the map folding algorithm that applies Theorem 2
|
|
465
|
+
optimizations for improved computational efficiency. The transformation includes:
|
|
466
|
+
|
|
467
|
+
1. Modifying loop termination conditions from general cases to Theorem 2 specifics
|
|
468
|
+
2. Restructuring conditional logic to eliminate unnecessary branch evaluations
|
|
469
|
+
3. Adding count doubling operations to leverage mathematical properties
|
|
470
|
+
4. Removing redundant computations that are not needed under Theorem 2 constraints
|
|
471
|
+
|
|
472
|
+
Theorem 2 provides mathematical guarantees that allow certain computational
|
|
473
|
+
shortcuts and optimizations that would not be valid in the general case. This
|
|
474
|
+
implementation capitalizes on those properties to achieve significant performance
|
|
475
|
+
improvements for maps that satisfy Theorem 2 conditions.
|
|
476
|
+
|
|
477
|
+
Parameters:
|
|
478
|
+
astModule: Source module containing the base algorithm
|
|
479
|
+
moduleIdentifier: Name for the generated theorem-optimized module
|
|
480
|
+
callableIdentifier: Name for the optimized computational function
|
|
481
|
+
logicalPathInfix: Directory path for organizing the generated module
|
|
482
|
+
sourceCallableDispatcher: Currently not implemented for this transformation
|
|
483
|
+
|
|
484
|
+
Returns:
|
|
485
|
+
pathFilename: Filesystem path where the theorem-optimized module was written
|
|
486
|
+
|
|
487
|
+
Raises:
|
|
488
|
+
NotImplementedError: If sourceCallableDispatcher is provided
|
|
489
|
+
"""
|
|
490
|
+
|
|
491
|
+
sourceCallableIdentifier = sourceCallableIdentifierDEFAULT
|
|
492
|
+
if callableIdentifier is None:
|
|
493
|
+
callableIdentifier = sourceCallableIdentifier
|
|
494
|
+
ingredientsFunction = IngredientsFunction(inlineFunctionDef(sourceCallableIdentifier, astModule), LedgerOfImports(astModule))
|
|
495
|
+
ingredientsFunction.astFunctionDef.name = callableIdentifier
|
|
496
|
+
|
|
497
|
+
dataclassInstanceIdentifier: identifierDotAttribute = raiseIfNone(NodeTourist(Be.arg, Then.extractIt(DOT.arg)).captureLastMatch(ingredientsFunction.astFunctionDef))
|
|
498
|
+
|
|
499
|
+
findThis = IfThis.isWhileAttributeNamespaceIdentifierGreaterThan0(dataclassInstanceIdentifier, 'leaf1ndex')
|
|
500
|
+
doThat = Grab.testAttribute(Grab.comparatorsAttribute(Then.replaceWith([Make.Constant(4)]))) # pyright: ignore[reportArgumentType]
|
|
501
|
+
NodeChanger(findThis, doThat).visit(ingredientsFunction.astFunctionDef)
|
|
502
|
+
|
|
503
|
+
findThis = IfThis.isIfAttributeNamespaceIdentifierGreaterThan0(dataclassInstanceIdentifier, 'leaf1ndex')
|
|
504
|
+
doThat = Then.extractIt(DOT.body)
|
|
505
|
+
insertLeaf: Sequence[ast.stmt] | None = NodeTourist(findThis, doThat).captureLastMatch(ingredientsFunction.astFunctionDef)
|
|
506
|
+
findThis = IfThis.isIfAttributeNamespaceIdentifierGreaterThan0(dataclassInstanceIdentifier, 'leaf1ndex')
|
|
507
|
+
doThat = Then.replaceWith(insertLeaf)
|
|
508
|
+
NodeChanger(findThis, doThat).visit(ingredientsFunction.astFunctionDef)
|
|
509
|
+
|
|
510
|
+
findThis = IfThis.isAttributeNamespaceIdentifierGreaterThan0(dataclassInstanceIdentifier, 'leaf1ndex')
|
|
511
|
+
doThat = Then.removeIt
|
|
512
|
+
NodeChanger(findThis, doThat).visit(ingredientsFunction.astFunctionDef)
|
|
513
|
+
|
|
514
|
+
findThis = IfThis.isAttributeNamespaceIdentifierLessThanOrEqual0(dataclassInstanceIdentifier, 'leaf1ndex')
|
|
515
|
+
doThat = Then.removeIt
|
|
516
|
+
NodeChanger(findThis, doThat).visit(ingredientsFunction.astFunctionDef)
|
|
517
|
+
|
|
518
|
+
theCountingIdentifier: identifierDotAttribute = theCountingIdentifierDEFAULT
|
|
519
|
+
doubleTheCount: ast.AugAssign = Make.AugAssign(Make.Attribute(ast.Name(dataclassInstanceIdentifier), theCountingIdentifier), ast.Mult(), Make.Constant(2))
|
|
520
|
+
findThis = Be.Return
|
|
521
|
+
doThat = Then.insertThisAbove([doubleTheCount])
|
|
522
|
+
NodeChanger(findThis, doThat).visit(ingredientsFunction.astFunctionDef)
|
|
523
|
+
|
|
524
|
+
ingredientsModule = IngredientsModule(ingredientsFunction)
|
|
525
|
+
|
|
526
|
+
if sourceCallableDispatcher is not None:
|
|
527
|
+
raise NotImplementedError('sourceCallableDispatcher is not implemented yet')
|
|
528
|
+
|
|
529
|
+
pathFilename: PurePath = _getPathFilename(packageSettings.pathPackage, logicalPathInfix, moduleIdentifier)
|
|
530
|
+
|
|
531
|
+
write_astModule(ingredientsModule, pathFilename, packageSettings.packageName)
|
|
532
|
+
|
|
533
|
+
return pathFilename
|
|
534
|
+
|
|
535
|
+
def trimTheorem2(astModule: ast.Module, moduleIdentifier: str, callableIdentifier: str | None = None, logicalPathInfix: PathLike[str] | PurePath | str | None = None, sourceCallableDispatcher: str | None = None) -> PurePath:
|
|
536
|
+
"""
|
|
537
|
+
Generate constrained Theorem 2 implementation by removing unnecessary logic.
|
|
538
|
+
|
|
539
|
+
Creates a trimmed version of the Theorem 2 implementation by eliminating
|
|
540
|
+
conditional logic that is not needed under specific constraint assumptions.
|
|
541
|
+
This transformation removes checks for unconstrained dimensions, simplifying
|
|
542
|
+
the algorithm for cases where dimensional constraints are guaranteed to be
|
|
543
|
+
satisfied by external conditions.
|
|
544
|
+
|
|
545
|
+
The trimming operation is particularly valuable for generating lean implementations
|
|
546
|
+
where the calling context ensures that certain conditions will always be met,
|
|
547
|
+
allowing the removal of defensive programming constructs that add computational
|
|
548
|
+
overhead without providing benefits in the constrained environment.
|
|
549
|
+
|
|
550
|
+
Parameters:
|
|
551
|
+
astModule: Source module containing the Theorem 2 implementation
|
|
552
|
+
moduleIdentifier: Name for the generated trimmed module
|
|
553
|
+
callableIdentifier: Name for the trimmed computational function
|
|
554
|
+
logicalPathInfix: Directory path for organizing the generated module
|
|
555
|
+
sourceCallableDispatcher: Optional dispatcher function identifier (unused)
|
|
556
|
+
|
|
557
|
+
Returns:
|
|
558
|
+
pathFilename: Filesystem path where the trimmed module was written
|
|
559
|
+
"""
|
|
560
|
+
|
|
561
|
+
sourceCallableIdentifier = sourceCallableIdentifierDEFAULT
|
|
562
|
+
if callableIdentifier is None:
|
|
563
|
+
callableIdentifier = sourceCallableIdentifier
|
|
564
|
+
ingredientsFunction = IngredientsFunction(inlineFunctionDef(sourceCallableIdentifier, astModule), LedgerOfImports(astModule))
|
|
565
|
+
ingredientsFunction.astFunctionDef.name = callableIdentifier
|
|
566
|
+
|
|
567
|
+
dataclassInstanceIdentifier: identifierDotAttribute = raiseIfNone(NodeTourist(Be.arg, Then.extractIt(DOT.arg)).captureLastMatch(ingredientsFunction.astFunctionDef))
|
|
568
|
+
|
|
569
|
+
findThis = IfThis.isIfUnaryNotAttributeNamespaceIdentifier(dataclassInstanceIdentifier, 'dimensionsUnconstrained')
|
|
570
|
+
doThat = Then.removeIt
|
|
571
|
+
NodeChanger(findThis, doThat).visit(ingredientsFunction.astFunctionDef)
|
|
572
|
+
|
|
573
|
+
ingredientsModule = IngredientsModule(ingredientsFunction)
|
|
574
|
+
ingredientsModule.removeImportFromModule('numpy')
|
|
575
|
+
|
|
576
|
+
pathFilename: PurePath = _getPathFilename(packageSettings.pathPackage, logicalPathInfix, moduleIdentifier)
|
|
577
|
+
|
|
578
|
+
write_astModule(ingredientsModule, pathFilename, packageSettings.packageName)
|
|
579
|
+
|
|
580
|
+
return pathFilename
|
|
581
|
+
|
|
582
|
+
def numbaOnTheorem2(astModule: ast.Module, moduleIdentifier: str, callableIdentifier: str | None = None, logicalPathInfix: PathLike[str] | PurePath | str | None = None, sourceCallableDispatcher: str | None = None) -> PurePath:
|
|
583
|
+
"""
|
|
584
|
+
Generate Numba-accelerated Theorem 2 implementation with dataclass decomposition.
|
|
585
|
+
|
|
586
|
+
Creates a highly optimized version of the Theorem 2 algorithm by combining the
|
|
587
|
+
mathematical optimizations of Theorem 2 with Numba just-in-time compilation.
|
|
588
|
+
The transformation includes:
|
|
589
|
+
|
|
590
|
+
1. Dataclass decomposition to convert structured parameters into primitives
|
|
591
|
+
2. Removal of Python object dependencies incompatible with Numba
|
|
592
|
+
3. Application of Numba decorators for maximum performance
|
|
593
|
+
4. Type annotation optimization for efficient compilation
|
|
594
|
+
|
|
595
|
+
This represents the highest level of optimization available for Theorem 2
|
|
596
|
+
implementations, providing both mathematical efficiency through theorem
|
|
597
|
+
application and computational efficiency through Numba acceleration.
|
|
598
|
+
The result is suitable for production use in high-performance computing
|
|
599
|
+
environments where maximum speed is required.
|
|
600
|
+
|
|
601
|
+
Parameters:
|
|
602
|
+
astModule: Source module containing the Theorem 2 implementation
|
|
603
|
+
moduleIdentifier: Name for the generated Numba-accelerated module
|
|
604
|
+
callableIdentifier: Name for the accelerated computational function
|
|
605
|
+
logicalPathInfix: Directory path for organizing the generated module
|
|
606
|
+
sourceCallableDispatcher: Optional dispatcher function identifier (unused)
|
|
607
|
+
|
|
608
|
+
Returns:
|
|
609
|
+
Filesystem path where the accelerated module was written
|
|
610
|
+
"""
|
|
611
|
+
|
|
612
|
+
sourceCallableIdentifier = sourceCallableIdentifierDEFAULT
|
|
613
|
+
if callableIdentifier is None:
|
|
614
|
+
callableIdentifier = sourceCallableIdentifier
|
|
615
|
+
ingredientsFunction = IngredientsFunction(inlineFunctionDef(sourceCallableIdentifier, astModule), LedgerOfImports(astModule))
|
|
616
|
+
ingredientsFunction.astFunctionDef.name = callableIdentifier
|
|
617
|
+
|
|
618
|
+
shatteredDataclass: ShatteredDataclass = shatter_dataclassesDOTdataclass(*findDataclass(ingredientsFunction))
|
|
619
|
+
|
|
620
|
+
ingredientsFunction.imports.update(shatteredDataclass.imports)
|
|
621
|
+
ingredientsFunction: IngredientsFunction = removeDataclassFromFunction(ingredientsFunction, shatteredDataclass)
|
|
622
|
+
ingredientsFunction = removeUnusedParameters(ingredientsFunction)
|
|
623
|
+
ingredientsFunction = decorateCallableWithNumba(ingredientsFunction, parametersNumbaLight)
|
|
624
|
+
|
|
625
|
+
ingredientsModule = IngredientsModule(ingredientsFunction)
|
|
626
|
+
ingredientsModule.removeImportFromModule('numpy')
|
|
627
|
+
|
|
628
|
+
pathFilename: PurePath = _getPathFilename(packageSettings.pathPackage, logicalPathInfix, moduleIdentifier)
|
|
629
|
+
|
|
630
|
+
write_astModule(ingredientsModule, pathFilename, packageSettings.packageName)
|
|
631
|
+
|
|
632
|
+
return pathFilename
|
|
633
|
+
|
|
634
|
+
def makeUnRePackDataclass(astImportFrom: ast.ImportFrom) -> None:
|
|
635
|
+
"""
|
|
636
|
+
Generate interface module for dataclass unpacking and repacking operations.
|
|
637
|
+
|
|
638
|
+
Creates a specialized module that serves as an interface between dataclass-based
|
|
639
|
+
calling code and optimized implementations that operate on decomposed primitive
|
|
640
|
+
values. The generated module includes:
|
|
641
|
+
|
|
642
|
+
1. A function that unpacks dataclass instances into individual primitive values
|
|
643
|
+
2. Calls to the specified optimized target function with decomposed parameters
|
|
644
|
+
3. Repacking of results back into appropriate dataclass instances
|
|
645
|
+
4. Import management for all required dependencies
|
|
646
|
+
|
|
647
|
+
This bridge module enables seamless integration between high-level dataclass-based
|
|
648
|
+
APIs and low-level optimized implementations, maintaining type safety and usability
|
|
649
|
+
while leveraging performance optimizations that require primitive value operations.
|
|
650
|
+
|
|
651
|
+
Parameters:
|
|
652
|
+
astImportFrom: Import statement specifying the target optimized function to call
|
|
653
|
+
|
|
654
|
+
Returns:
|
|
655
|
+
None: The generated module is written directly to the filesystem
|
|
656
|
+
"""
|
|
657
|
+
|
|
658
|
+
callableIdentifierHARDCODED: str = 'sequential'
|
|
659
|
+
|
|
660
|
+
algorithmSourceModule: identifierDotAttribute = algorithmSourceModuleDEFAULT
|
|
661
|
+
sourceCallableIdentifier: identifierDotAttribute = sourceCallableDispatcherDEFAULT
|
|
662
|
+
logicalPathSourceModule: identifierDotAttribute = '.'.join([packageSettings.packageName, algorithmSourceModule])
|
|
663
|
+
|
|
664
|
+
logicalPathInfix: identifierDotAttribute = logicalPathInfixDEFAULT
|
|
665
|
+
moduleIdentifier: identifierDotAttribute = dataPackingModuleIdentifierDEFAULT
|
|
666
|
+
callableIdentifier: identifierDotAttribute = callableIdentifierHARDCODED
|
|
667
|
+
|
|
668
|
+
ingredientsFunction: IngredientsFunction = astModuleToIngredientsFunction(parseLogicalPath2astModule(logicalPathSourceModule), sourceCallableIdentifier)
|
|
669
|
+
ingredientsFunction.astFunctionDef.name = callableIdentifier
|
|
670
|
+
|
|
671
|
+
shatteredDataclass: ShatteredDataclass = shatter_dataclassesDOTdataclass(*findDataclass(ingredientsFunction))
|
|
672
|
+
|
|
673
|
+
ingredientsFunction.imports.update(shatteredDataclass.imports)
|
|
674
|
+
ingredientsFunction.imports.addAst(astImportFrom)
|
|
675
|
+
targetCallableIdentifier = astImportFrom.names[0].name
|
|
676
|
+
ingredientsFunction = raiseIfNone(unpackDataclassCallFunctionRepackDataclass(ingredientsFunction, targetCallableIdentifier, shatteredDataclass))
|
|
677
|
+
targetFunctionDef: ast.FunctionDef = raiseIfNone(extractFunctionDef(parseLogicalPath2astModule(raiseIfNone(astImportFrom.module)), targetCallableIdentifier))
|
|
678
|
+
astTuple: ast.expr = raiseIfNone(NodeTourist(Be.Return, Then.extractIt(DOT.value)).captureLastMatch(targetFunctionDef))
|
|
679
|
+
cast(ast.Tuple, astTuple).ctx = ast.Store()
|
|
680
|
+
|
|
681
|
+
findThis = ClassIsAndAttribute.valueIs(ast.Assign, IfThis.isCallIdentifier(targetCallableIdentifier))
|
|
682
|
+
doThat = Then.replaceWith(Make.Assign([astTuple], value=Make.Call(Make.Name(targetCallableIdentifier), cast(ast.Tuple, astTuple).elts)))
|
|
683
|
+
NodeChanger(findThis, doThat).visit(ingredientsFunction.astFunctionDef)
|
|
684
|
+
|
|
685
|
+
ingredientsModule = IngredientsModule(ingredientsFunction)
|
|
686
|
+
ingredientsModule.removeImportFromModule('numpy')
|
|
687
|
+
|
|
688
|
+
pathFilename: PurePath = _getPathFilename(packageSettings.pathPackage, logicalPathInfix, moduleIdentifier)
|
|
689
|
+
|
|
690
|
+
write_astModule(ingredientsModule, pathFilename, packageSettings.packageName)
|
|
691
|
+
|
|
692
|
+
if __name__ == '__main__':
|
|
693
|
+
astModule: ast.Module = getModule(logicalPathInfix=None)
|
|
694
|
+
makeInitializeGroupsOfFolds(astModule, 'initializeCount', 'initializeGroupsOfFolds', logicalPathInfixDEFAULT)
|
|
695
|
+
|
|
696
|
+
astModule = getModule(logicalPathInfix=None)
|
|
697
|
+
pathFilename: PurePath = makeDaoOfMapFolding(astModule, 'daoOfMapFolding', None, logicalPathInfixDEFAULT, sourceCallableDispatcherDEFAULT)
|
|
698
|
+
|
|
699
|
+
astModule = getModule(logicalPathInfix=None)
|
|
700
|
+
pathFilename = makeDaoOfMapFoldingParallel(astModule, 'countParallel', None, logicalPathInfixDEFAULT, sourceCallableDispatcherDEFAULT)
|
|
701
|
+
|
|
702
|
+
astModule = getModule(logicalPathInfix=None)
|
|
703
|
+
pathFilename = makeTheorem2(astModule, 'theorem2', None, logicalPathInfixDEFAULT, None)
|
|
704
|
+
|
|
705
|
+
astModule = parsePathFilename2astModule(pathFilename)
|
|
706
|
+
pathFilename = trimTheorem2(astModule, 'theorem2Trimmed', None, logicalPathInfixDEFAULT, None)
|
|
707
|
+
|
|
708
|
+
astModule = parsePathFilename2astModule(pathFilename)
|
|
709
|
+
pathFilename = numbaOnTheorem2(astModule, 'theorem2Numba', None, logicalPathInfixDEFAULT, None)
|
|
710
|
+
|
|
711
|
+
astImportFrom: ast.ImportFrom = Make.ImportFrom(_getLogicalPath(packageSettings.packageName, logicalPathInfixDEFAULT, 'theorem2Numba'), list_alias=[Make.alias(sourceCallableIdentifierDEFAULT)])
|
|
712
|
+
makeUnRePackDataclass(astImportFrom)
|