mapFolding 0.8.3__py3-none-any.whl → 0.8.4__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 +2 -2
- mapFolding/basecamp.py +11 -5
- mapFolding/filesystem.py +134 -109
- mapFolding/oeis.py +1 -1
- mapFolding/someAssemblyRequired/__init__.py +37 -18
- mapFolding/someAssemblyRequired/_theTypes.py +35 -0
- mapFolding/someAssemblyRequired/_tool_Make.py +92 -0
- mapFolding/someAssemblyRequired/_tool_Then.py +65 -0
- mapFolding/someAssemblyRequired/_toolboxAntecedents.py +326 -0
- mapFolding/someAssemblyRequired/_toolboxContainers.py +306 -0
- mapFolding/someAssemblyRequired/_toolboxPython.py +76 -0
- mapFolding/someAssemblyRequired/ingredientsNumba.py +17 -24
- mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py +114 -169
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +247 -0
- mapFolding/someAssemblyRequired/transformDataStructures.py +167 -100
- mapFolding/someAssemblyRequired/transformationTools.py +63 -685
- mapFolding/syntheticModules/numbaCount_doTheNeedful.py +36 -33
- mapFolding/theDao.py +13 -11
- mapFolding/theSSOT.py +69 -112
- {mapfolding-0.8.3.dist-info → mapfolding-0.8.4.dist-info}/METADATA +2 -1
- mapfolding-0.8.4.dist-info/RECORD +49 -0
- {mapfolding-0.8.3.dist-info → mapfolding-0.8.4.dist-info}/WHEEL +1 -1
- tests/conftest.py +34 -29
- tests/test_computations.py +40 -31
- tests/test_filesystem.py +3 -3
- mapFolding/someAssemblyRequired/synthesizeNumbaJobVESTIGIAL.py +0 -413
- mapfolding-0.8.3.dist-info/RECORD +0 -43
- {mapfolding-0.8.3.dist-info → mapfolding-0.8.4.dist-info}/entry_points.txt +0 -0
- {mapfolding-0.8.3.dist-info → mapfolding-0.8.4.dist-info}/licenses/LICENSE +0 -0
- {mapfolding-0.8.3.dist-info → mapfolding-0.8.4.dist-info}/top_level.txt +0 -0
|
@@ -2,6 +2,7 @@ from concurrent.futures import Future as ConcurrentFuture, ProcessPoolExecutor
|
|
|
2
2
|
from copy import deepcopy
|
|
3
3
|
from mapFolding.theSSOT import Array1DElephino, Array1DFoldsTotal, Array1DLeavesTotal, Array3D, ComputationState, DatatypeElephino, DatatypeFoldsTotal, DatatypeLeavesTotal
|
|
4
4
|
from numba import jit
|
|
5
|
+
from numpy import array, int16, int64
|
|
5
6
|
|
|
6
7
|
def countInitialize(state: ComputationState) -> ComputationState:
|
|
7
8
|
while state.leaf1ndex > 0:
|
|
@@ -22,11 +23,11 @@ def countInitialize(state: ComputationState) -> ComputationState:
|
|
|
22
23
|
state.leafConnectee = state.connectionGraph[state.indexDimension, state.leaf1ndex, state.leafBelow[state.leafConnectee]]
|
|
23
24
|
state.indexDimension += 1
|
|
24
25
|
if not state.dimensionsUnconstrained:
|
|
25
|
-
indexLeaf = 0
|
|
26
|
-
while indexLeaf < state.leaf1ndex:
|
|
27
|
-
state.gapsWhere[state.gap1ndexCeiling] = indexLeaf
|
|
26
|
+
state.indexLeaf = 0
|
|
27
|
+
while state.indexLeaf < state.leaf1ndex:
|
|
28
|
+
state.gapsWhere[state.gap1ndexCeiling] = state.indexLeaf
|
|
28
29
|
state.gap1ndexCeiling += 1
|
|
29
|
-
indexLeaf += 1
|
|
30
|
+
state.indexLeaf += 1
|
|
30
31
|
state.indexMiniGap = state.gap1ndex
|
|
31
32
|
while state.indexMiniGap < state.gap1ndexCeiling:
|
|
32
33
|
state.gapsWhere[state.gap1ndex] = state.gapsWhere[state.indexMiniGap]
|
|
@@ -139,36 +140,38 @@ def doTheNeedful(state: ComputationState) -> ComputationState:
|
|
|
139
140
|
state = countInitialize(state)
|
|
140
141
|
if state.taskDivisions > 0:
|
|
141
142
|
dictionaryConcurrency: dict[int, ConcurrentFuture[ComputationState]] = {}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
143
|
+
stateParallel = deepcopy(state)
|
|
144
|
+
with ProcessPoolExecutor(stateParallel.concurrencyLimit) as concurrencyManager:
|
|
145
|
+
for indexSherpa in range(stateParallel.taskDivisions):
|
|
146
|
+
state = deepcopy(stateParallel)
|
|
147
|
+
state.taskIndex = indexSherpa
|
|
148
|
+
mapShape: tuple[DatatypeLeavesTotal, ...] = state.mapShape
|
|
149
|
+
leavesTotal: DatatypeLeavesTotal = state.leavesTotal
|
|
150
|
+
taskDivisions: DatatypeLeavesTotal = state.taskDivisions
|
|
151
|
+
concurrencyLimit: DatatypeElephino = state.concurrencyLimit
|
|
152
|
+
connectionGraph: Array3D = state.connectionGraph
|
|
153
|
+
dimensionsTotal: DatatypeLeavesTotal = state.dimensionsTotal
|
|
154
|
+
countDimensionsGapped: Array1DLeavesTotal = state.countDimensionsGapped
|
|
155
|
+
dimensionsUnconstrained: DatatypeLeavesTotal = state.dimensionsUnconstrained
|
|
156
|
+
gapRangeStart: Array1DElephino = state.gapRangeStart
|
|
157
|
+
gapsWhere: Array1DLeavesTotal = state.gapsWhere
|
|
158
|
+
leafAbove: Array1DLeavesTotal = state.leafAbove
|
|
159
|
+
leafBelow: Array1DLeavesTotal = state.leafBelow
|
|
160
|
+
foldGroups: Array1DFoldsTotal = state.foldGroups
|
|
161
|
+
foldsTotal: DatatypeFoldsTotal = state.foldsTotal
|
|
162
|
+
gap1ndex: DatatypeLeavesTotal = state.gap1ndex
|
|
163
|
+
gap1ndexCeiling: DatatypeElephino = state.gap1ndexCeiling
|
|
164
|
+
groupsOfFolds: DatatypeFoldsTotal = state.groupsOfFolds
|
|
165
|
+
indexDimension: DatatypeLeavesTotal = state.indexDimension
|
|
166
|
+
indexLeaf: DatatypeLeavesTotal = state.indexLeaf
|
|
167
|
+
indexMiniGap: DatatypeElephino = state.indexMiniGap
|
|
168
|
+
leaf1ndex: DatatypeElephino = state.leaf1ndex
|
|
169
|
+
leafConnectee: DatatypeElephino = state.leafConnectee
|
|
170
|
+
taskIndex: DatatypeLeavesTotal = state.taskIndex
|
|
169
171
|
dictionaryConcurrency[indexSherpa] = concurrencyManager.submit(countParallel, mapShape, leavesTotal, taskDivisions, concurrencyLimit, connectionGraph, dimensionsTotal, countDimensionsGapped, dimensionsUnconstrained, gapRangeStart, gapsWhere, leafAbove, leafBelow, foldGroups, foldsTotal, gap1ndex, gap1ndexCeiling, groupsOfFolds, indexDimension, indexLeaf, indexMiniGap, leaf1ndex, leafConnectee, taskIndex)
|
|
170
|
-
for indexSherpa in range(
|
|
171
|
-
|
|
172
|
+
for indexSherpa in range(stateParallel.taskDivisions):
|
|
173
|
+
stateParallel.foldGroups[indexSherpa] = dictionaryConcurrency[indexSherpa].result()
|
|
174
|
+
state = stateParallel
|
|
172
175
|
else:
|
|
173
176
|
mapShape: tuple[DatatypeLeavesTotal, ...] = state.mapShape
|
|
174
177
|
leavesTotal: DatatypeLeavesTotal = state.leavesTotal
|
mapFolding/theDao.py
CHANGED
|
@@ -101,11 +101,11 @@ def initializeVariablesToFindGaps(state: ComputationState) -> ComputationState:
|
|
|
101
101
|
return state
|
|
102
102
|
|
|
103
103
|
def insertUnconstrainedLeaf(state: ComputationState) -> ComputationState:
|
|
104
|
-
indexLeaf = 0
|
|
105
|
-
while indexLeaf < state.leaf1ndex:
|
|
106
|
-
state.gapsWhere[state.gap1ndexCeiling] = indexLeaf
|
|
104
|
+
state.indexLeaf = 0
|
|
105
|
+
while state.indexLeaf < state.leaf1ndex:
|
|
106
|
+
state.gapsWhere[state.gap1ndexCeiling] = state.indexLeaf
|
|
107
107
|
state.gap1ndexCeiling += 1
|
|
108
|
-
indexLeaf += 1
|
|
108
|
+
state.indexLeaf += 1
|
|
109
109
|
return state
|
|
110
110
|
|
|
111
111
|
def leafBelowSentinelIs1(state: ComputationState) -> bool:
|
|
@@ -227,13 +227,15 @@ def doTheNeedful(state: ComputationState) -> ComputationState:
|
|
|
227
227
|
state = countInitialize(state)
|
|
228
228
|
if state.taskDivisions > 0:
|
|
229
229
|
dictionaryConcurrency: dict[int, ConcurrentFuture[ComputationState]] = {}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
230
|
+
stateParallel = deepcopy(state)
|
|
231
|
+
with ProcessPoolExecutor(stateParallel.concurrencyLimit) as concurrencyManager:
|
|
232
|
+
for indexSherpa in range(stateParallel.taskDivisions):
|
|
233
|
+
state = deepcopy(stateParallel)
|
|
234
|
+
state.taskIndex = indexSherpa
|
|
235
|
+
dictionaryConcurrency[indexSherpa] = concurrencyManager.submit(countParallel, state)
|
|
236
|
+
for indexSherpa in range(stateParallel.taskDivisions):
|
|
237
|
+
stateParallel.foldGroups[indexSherpa] = dictionaryConcurrency[indexSherpa].result().foldGroups[indexSherpa]
|
|
238
|
+
state = stateParallel
|
|
237
239
|
else:
|
|
238
240
|
state = countSequential(state)
|
|
239
241
|
|
mapFolding/theSSOT.py
CHANGED
|
@@ -21,19 +21,11 @@ from importlib import import_module as importlib_import_module
|
|
|
21
21
|
from inspect import getfile as inspect_getfile
|
|
22
22
|
from numpy import dtype, int64 as numpy_int64, int16 as numpy_int16, ndarray
|
|
23
23
|
from pathlib import Path
|
|
24
|
-
from sys import modules as sysModules
|
|
25
24
|
from tomli import load as tomli_load
|
|
26
25
|
from types import ModuleType
|
|
27
26
|
from typing import TypeAlias
|
|
28
27
|
import dataclasses
|
|
29
28
|
|
|
30
|
-
# Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
|
|
31
|
-
# I _think_, in theSSOT, I have abstracted the flow settings to only these couple of lines:
|
|
32
|
-
packageFlowSynthetic = 'numba'
|
|
33
|
-
# Z0Z_packageFlow = 'algorithm'
|
|
34
|
-
Z0Z_packageFlow = packageFlowSynthetic
|
|
35
|
-
Z0Z_concurrencyPackage = 'multiprocessing'
|
|
36
|
-
|
|
37
29
|
# =============================================================================
|
|
38
30
|
# The Wrong Way: Evaluate When Packaging
|
|
39
31
|
|
|
@@ -50,84 +42,113 @@ def getPathPackageINSTALLING() -> Path:
|
|
|
50
42
|
pathPackage = pathPackage.parent
|
|
51
43
|
return pathPackage
|
|
52
44
|
|
|
45
|
+
# =============================================================================
|
|
46
|
+
# The Wrong Way: HARDCODED
|
|
47
|
+
# Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
|
|
48
|
+
|
|
49
|
+
# from mapFolding.someAssemblyRequired.synthesizeNumbaFlow.theNumbaFlow
|
|
50
|
+
logicalPathModuleDispatcherHARDCODED: str = 'mapFolding.syntheticModules.numbaCount_doTheNeedful'
|
|
51
|
+
callableDispatcherHARDCODED: str = 'doTheNeedful'
|
|
52
|
+
concurrencyPackageHARDCODED = 'multiprocessing'
|
|
53
|
+
|
|
54
|
+
# =============================================================================
|
|
53
55
|
# The following is an improvement, but it is not the full solution.
|
|
54
56
|
# I hope that the standardized markers, `metadata={'evaluateWhen': 'packaging'}` will help to automate
|
|
55
57
|
# whatever needs to happen so that the following is well implemented.
|
|
56
58
|
@dataclasses.dataclass(frozen=True)
|
|
57
59
|
class PackageSettings:
|
|
58
|
-
|
|
60
|
+
|
|
61
|
+
logicalPathModuleDispatcher: str | None = None
|
|
62
|
+
callableDispatcher: str | None = None
|
|
63
|
+
concurrencyPackage: str |None = None
|
|
59
64
|
dataclassIdentifier: str = dataclasses.field(default='ComputationState', metadata={'evaluateWhen': 'packaging'})
|
|
60
65
|
dataclassInstance: str = dataclasses.field(default='state', metadata={'evaluateWhen': 'packaging'})
|
|
61
66
|
dataclassInstanceTaskDistributionSuffix: str = dataclasses.field(default='Parallel', metadata={'evaluateWhen': 'packaging'})
|
|
62
67
|
dataclassModule: str = dataclasses.field(default='theSSOT', metadata={'evaluateWhen': 'packaging'})
|
|
63
68
|
datatypePackage: str = dataclasses.field(default='numpy', metadata={'evaluateWhen': 'packaging'})
|
|
64
|
-
dispatcherCallable: str = dataclasses.field(default='doTheNeedful', metadata={'evaluateWhen': 'packaging'})
|
|
65
69
|
fileExtension: str = dataclasses.field(default='.py', metadata={'evaluateWhen': 'installing'})
|
|
66
|
-
moduleOfSyntheticModules: str = dataclasses.field(default='syntheticModules', metadata={'evaluateWhen': 'packaging'})
|
|
67
70
|
packageName: str = dataclasses.field(default = packageNamePACKAGING, metadata={'evaluateWhen': 'packaging'})
|
|
68
71
|
pathPackage: Path = dataclasses.field(default_factory=getPathPackageINSTALLING, init=False, metadata={'evaluateWhen': 'installing'})
|
|
69
72
|
sourceAlgorithm: str = dataclasses.field(default='theDao', metadata={'evaluateWhen': 'packaging'})
|
|
73
|
+
sourceCallableDispatcher: str = dataclasses.field(default='doTheNeedful', metadata={'evaluateWhen': 'packaging'})
|
|
74
|
+
sourceCallableInitialize: str = dataclasses.field(default='countInitialize', metadata={'evaluateWhen': 'packaging'})
|
|
75
|
+
sourceCallableParallel: str = dataclasses.field(default='countParallel', metadata={'evaluateWhen': 'packaging'})
|
|
76
|
+
sourceCallableSequential: str = dataclasses.field(default='countSequential', metadata={'evaluateWhen': 'packaging'})
|
|
70
77
|
sourceConcurrencyManagerIdentifier: str = dataclasses.field(default='submit', metadata={'evaluateWhen': 'packaging'})
|
|
71
78
|
sourceConcurrencyManagerNamespace: str = dataclasses.field(default='concurrencyManager', metadata={'evaluateWhen': 'packaging'})
|
|
72
|
-
|
|
73
|
-
sourceParallelCallable: str = dataclasses.field(default='countParallel', metadata={'evaluateWhen': 'packaging'})
|
|
74
|
-
sourceSequentialCallable: str = dataclasses.field(default='countSequential', metadata={'evaluateWhen': 'packaging'})
|
|
79
|
+
sourceConcurrencyPackage: str = dataclasses.field(default='multiprocessing', metadata={'evaluateWhen': 'packaging'})
|
|
75
80
|
|
|
76
81
|
@property # These are not fields, and that annoys me.
|
|
77
82
|
def dataclassInstanceTaskDistribution(self) -> str:
|
|
78
|
-
"""
|
|
83
|
+
""" During parallel computation, this identifier helps to create deep copies of the dataclass instance. """
|
|
79
84
|
# it follows that `metadata={'evaluateWhen': 'packaging'}`
|
|
80
85
|
return self.dataclassInstance + self.dataclassInstanceTaskDistributionSuffix
|
|
81
86
|
|
|
82
|
-
@property # These are not fields, and that annoys me.
|
|
83
|
-
def logicalPathModuleSourceAlgorithm(self) -> str:
|
|
84
|
-
""" Compute the logical path module for the source algorithm by joining packageName and sourceAlgorithm. """
|
|
85
|
-
# it follows that `metadata={'evaluateWhen': 'packaging'}`
|
|
86
|
-
return '.'.join([self.packageName, self.sourceAlgorithm])
|
|
87
|
-
|
|
88
87
|
@property # These are not fields, and that annoys me.
|
|
89
88
|
def logicalPathModuleDataclass(self) -> str:
|
|
90
|
-
"""
|
|
89
|
+
""" The package.module.name logical path to the dataclass. """
|
|
91
90
|
# it follows that `metadata={'evaluateWhen': 'packaging'}`
|
|
92
91
|
return '.'.join([self.packageName, self.dataclassModule])
|
|
93
92
|
|
|
94
|
-
|
|
93
|
+
@property # These are not fields, and that annoys me.
|
|
94
|
+
def logicalPathModuleSourceAlgorithm(self) -> str:
|
|
95
|
+
""" The package.module.name logical path to the source algorithm. """
|
|
96
|
+
# it follows that `metadata={'evaluateWhen': 'packaging'}`
|
|
97
|
+
return '.'.join([self.packageName, self.sourceAlgorithm])
|
|
95
98
|
|
|
99
|
+
@property # These are not fields, and that annoys me.
|
|
100
|
+
def dispatcher(self) -> Callable[['ComputationState'], 'ComputationState']:
|
|
101
|
+
""" _The_ callable that connects `countFolds` to the logic that does the work."""
|
|
102
|
+
logicalPath: str = self.logicalPathModuleDispatcher or self.logicalPathModuleSourceAlgorithm
|
|
103
|
+
identifier: str = self.callableDispatcher or self.sourceCallableDispatcher
|
|
104
|
+
moduleImported: ModuleType = importlib_import_module(logicalPath)
|
|
105
|
+
return getattr(moduleImported, identifier)
|
|
106
|
+
|
|
107
|
+
The = PackageSettings(logicalPathModuleDispatcher=logicalPathModuleDispatcherHARDCODED, callableDispatcher=callableDispatcherHARDCODED, concurrencyPackage=concurrencyPackageHARDCODED)
|
|
108
|
+
|
|
109
|
+
# To remove this function, I need to learn how to change "conftest.py" to patch this.
|
|
110
|
+
def getPackageDispatcher() -> Callable[['ComputationState'], 'ComputationState']:
|
|
111
|
+
"""Get the dispatcher callable for the package.
|
|
112
|
+
|
|
113
|
+
This function retrieves the dispatcher callable for the package based on the
|
|
114
|
+
logical path module and callable dispatcher defined in the PackageSettings.
|
|
115
|
+
"""
|
|
116
|
+
return The.dispatcher
|
|
96
117
|
# =============================================================================
|
|
97
118
|
# Flexible Data Structure System Needs Enhanced Paradigm https://github.com/hunterhogan/mapFolding/issues/9
|
|
119
|
+
# Efficient translation of Python scalar types to Numba types https://github.com/hunterhogan/mapFolding/issues/8
|
|
98
120
|
|
|
99
121
|
DatatypeLeavesTotal: TypeAlias = int
|
|
100
|
-
# this would be uint8, but mapShape (2,2,2,2, 2,2,2,2) has 256 leaves, so generic containers must accommodate at least 256 leaves
|
|
101
|
-
numpyLeavesTotal: TypeAlias = numpy_int16
|
|
122
|
+
NumPyLeavesTotal: TypeAlias = numpy_int16 # this would be uint8, but mapShape (2,2,2,2, 2,2,2,2) has 256 leaves, so generic containers must accommodate at least 256 leaves
|
|
102
123
|
|
|
103
124
|
DatatypeElephino: TypeAlias = int
|
|
104
|
-
|
|
125
|
+
NumPyElephino: TypeAlias = numpy_int16
|
|
105
126
|
|
|
106
127
|
DatatypeFoldsTotal: TypeAlias = int
|
|
107
|
-
|
|
128
|
+
NumPyFoldsTotal: TypeAlias = numpy_int64
|
|
108
129
|
|
|
109
|
-
Array3D: TypeAlias = ndarray[tuple[int, int, int], dtype[
|
|
110
|
-
Array1DLeavesTotal: TypeAlias = ndarray[tuple[int], dtype[
|
|
111
|
-
Array1DElephino: TypeAlias = ndarray[tuple[int], dtype[
|
|
112
|
-
Array1DFoldsTotal: TypeAlias = ndarray[tuple[int], dtype[
|
|
130
|
+
Array3D: TypeAlias = ndarray[tuple[int, int, int], dtype[NumPyLeavesTotal]]
|
|
131
|
+
Array1DLeavesTotal: TypeAlias = ndarray[tuple[int], dtype[NumPyLeavesTotal]]
|
|
132
|
+
Array1DElephino: TypeAlias = ndarray[tuple[int], dtype[NumPyElephino]]
|
|
133
|
+
Array1DFoldsTotal: TypeAlias = ndarray[tuple[int], dtype[NumPyFoldsTotal]]
|
|
113
134
|
|
|
114
135
|
@dataclasses.dataclass
|
|
115
136
|
class ComputationState:
|
|
116
|
-
mapShape: tuple[DatatypeLeavesTotal, ...]
|
|
137
|
+
mapShape: tuple[DatatypeLeavesTotal, ...] = dataclasses.field(init=True, metadata={'elementConstructor': 'DatatypeLeavesTotal'}) # NOTE Python is anti-DRY, again, `DatatypeLeavesTotal` needs to match the type
|
|
117
138
|
leavesTotal: DatatypeLeavesTotal
|
|
118
139
|
taskDivisions: DatatypeLeavesTotal
|
|
119
140
|
concurrencyLimit: DatatypeElephino
|
|
120
141
|
|
|
121
|
-
connectionGraph: Array3D = dataclasses.field(init=False)
|
|
142
|
+
connectionGraph: Array3D = dataclasses.field(init=False, metadata={'dtype': Array3D.__args__[1].__args__[0]}) # pyright: ignore[reportAttributeAccessIssue]
|
|
122
143
|
dimensionsTotal: DatatypeLeavesTotal = dataclasses.field(init=False)
|
|
123
144
|
|
|
124
|
-
countDimensionsGapped: Array1DLeavesTotal = dataclasses.field(default=None, init=True) # type: ignore[arg-type, reportAssignmentType]
|
|
145
|
+
countDimensionsGapped: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # type: ignore[arg-type, reportAssignmentType]
|
|
125
146
|
dimensionsUnconstrained: DatatypeLeavesTotal = dataclasses.field(default=None, init=True) # type: ignore[assignment, reportAssignmentType]
|
|
126
|
-
gapRangeStart: Array1DElephino = dataclasses.field(default=None, init=True) # type: ignore[arg-type, reportAssignmentType]
|
|
127
|
-
gapsWhere: Array1DLeavesTotal = dataclasses.field(default=None, init=True) # type: ignore[arg-type, reportAssignmentType]
|
|
128
|
-
leafAbove: Array1DLeavesTotal = dataclasses.field(default=None, init=True) # type: ignore[arg-type, reportAssignmentType]
|
|
129
|
-
leafBelow: Array1DLeavesTotal = dataclasses.field(default=None, init=True) # type: ignore[arg-type, reportAssignmentType]
|
|
130
|
-
foldGroups: Array1DFoldsTotal = dataclasses.field(default=None, init=True) # type: ignore[arg-type, reportAssignmentType]
|
|
147
|
+
gapRangeStart: Array1DElephino = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DElephino.__args__[1].__args__[0]}) # type: ignore[arg-type, reportAssignmentType]
|
|
148
|
+
gapsWhere: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # type: ignore[arg-type, reportAssignmentType]
|
|
149
|
+
leafAbove: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # type: ignore[arg-type, reportAssignmentType]
|
|
150
|
+
leafBelow: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # type: ignore[arg-type, reportAssignmentType]
|
|
151
|
+
foldGroups: Array1DFoldsTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DFoldsTotal.__args__[1].__args__[0]}) # type: ignore[arg-type, reportAssignmentType]
|
|
131
152
|
|
|
132
153
|
foldsTotal: DatatypeFoldsTotal = DatatypeFoldsTotal(0)
|
|
133
154
|
gap1ndex: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
|
|
@@ -139,95 +160,31 @@ class ComputationState:
|
|
|
139
160
|
leaf1ndex: DatatypeElephino = DatatypeElephino(1)
|
|
140
161
|
leafConnectee: DatatypeElephino = DatatypeElephino(0)
|
|
141
162
|
taskIndex: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
|
|
142
|
-
# Efficient translation of Python scalar types to Numba types https://github.com/hunterhogan/mapFolding/issues/8
|
|
143
163
|
|
|
144
164
|
def __post_init__(self) -> None:
|
|
145
165
|
from mapFolding.beDRY import makeConnectionGraph, makeDataContainer
|
|
146
166
|
self.dimensionsTotal = DatatypeLeavesTotal(len(self.mapShape))
|
|
147
|
-
|
|
167
|
+
leavesTotalAsInt = int(self.leavesTotal)
|
|
168
|
+
self.connectionGraph = makeConnectionGraph(self.mapShape, leavesTotalAsInt, self.__dataclass_fields__['connectionGraph'].metadata['dtype'])
|
|
148
169
|
|
|
149
|
-
if self.dimensionsUnconstrained is None:
|
|
170
|
+
if self.dimensionsUnconstrained is None:
|
|
150
171
|
self.dimensionsUnconstrained = DatatypeLeavesTotal(int(self.dimensionsTotal))
|
|
151
172
|
|
|
152
173
|
if self.foldGroups is None:
|
|
153
|
-
self.foldGroups = makeDataContainer(max(2, int(self.taskDivisions) + 1),
|
|
174
|
+
self.foldGroups = makeDataContainer(max(2, int(self.taskDivisions) + 1), self.__dataclass_fields__['foldGroups'].metadata['dtype'])
|
|
154
175
|
self.foldGroups[-1] = self.leavesTotal
|
|
155
176
|
|
|
156
|
-
|
|
177
|
+
if self.gapsWhere is None: self.gapsWhere = makeDataContainer(leavesTotalAsInt * leavesTotalAsInt + 1, self.__dataclass_fields__['gapsWhere'].metadata['dtype'])
|
|
157
178
|
|
|
158
|
-
if self.countDimensionsGapped is None:
|
|
159
|
-
|
|
160
|
-
if self.
|
|
161
|
-
|
|
162
|
-
if self.gapsWhere is None:
|
|
163
|
-
self.gapsWhere = makeDataContainer(leavesTotalAsInt * leavesTotalAsInt + 1, numpyLeavesTotal)
|
|
164
|
-
if self.leafAbove is None:
|
|
165
|
-
self.leafAbove = makeDataContainer(leavesTotalAsInt + 1, numpyLeavesTotal)
|
|
166
|
-
if self.leafBelow is None:
|
|
167
|
-
self.leafBelow = makeDataContainer(leavesTotalAsInt + 1, numpyLeavesTotal)
|
|
179
|
+
if self.countDimensionsGapped is None: self.countDimensionsGapped = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['countDimensionsGapped'].metadata['dtype'])
|
|
180
|
+
if self.gapRangeStart is None: self.gapRangeStart = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['gapRangeStart'].metadata['dtype'])
|
|
181
|
+
if self.leafAbove is None: self.leafAbove = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafAbove'].metadata['dtype'])
|
|
182
|
+
if self.leafBelow is None: self.leafBelow = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafBelow'].metadata['dtype'])
|
|
168
183
|
|
|
169
184
|
def getFoldsTotal(self) -> None:
|
|
170
185
|
self.foldsTotal = DatatypeFoldsTotal(self.foldGroups[0:-1].sum() * self.leavesTotal)
|
|
171
186
|
|
|
172
|
-
# =============================================================================
|
|
173
|
-
|
|
174
|
-
# TODO learn how to see this from the user's perspective
|
|
175
|
-
def getPathJobRootDEFAULT() -> Path:
|
|
176
|
-
if 'google.colab' in sysModules:
|
|
177
|
-
pathJobDEFAULT: Path = Path("/content/drive/MyDrive") / "jobs"
|
|
178
|
-
else:
|
|
179
|
-
pathJobDEFAULT = The.pathPackage / "jobs"
|
|
180
|
-
return pathJobDEFAULT
|
|
181
|
-
|
|
182
187
|
# =============================================================================
|
|
183
188
|
# The coping way.
|
|
184
189
|
|
|
185
190
|
class raiseIfNoneGitHubIssueNumber3(Exception): pass
|
|
186
|
-
|
|
187
|
-
# =============================================================================
|
|
188
|
-
# THIS IS A STUPID SYSTEM BUT I CAN'T FIGURE OUT AN IMPROVEMENT
|
|
189
|
-
# NOTE This section for _default_ values probably has value
|
|
190
|
-
# https://github.com/hunterhogan/mapFolding/issues/4
|
|
191
|
-
theFormatStrModuleSynthetic = "{packageFlow}Count"
|
|
192
|
-
theFormatStrModuleForCallableSynthetic = theFormatStrModuleSynthetic + "_{callableTarget}"
|
|
193
|
-
|
|
194
|
-
theLogicalPathModuleDispatcher: str = The.logicalPathModuleSourceAlgorithm
|
|
195
|
-
|
|
196
|
-
theModuleDispatcherSynthetic: str = theFormatStrModuleForCallableSynthetic.format(packageFlow=packageFlowSynthetic, callableTarget=The.dispatcherCallable)
|
|
197
|
-
theLogicalPathModuleDispatcherSynthetic: str = '.'.join([The.packageName, The.moduleOfSyntheticModules, theModuleDispatcherSynthetic])
|
|
198
|
-
|
|
199
|
-
if Z0Z_packageFlow == packageFlowSynthetic: # pyright: ignore [reportUnnecessaryComparison]
|
|
200
|
-
# NOTE this as a default value _might_ have value
|
|
201
|
-
theLogicalPathModuleDispatcher = theLogicalPathModuleDispatcherSynthetic
|
|
202
|
-
|
|
203
|
-
# dynamically set the return type https://github.com/hunterhogan/mapFolding/issues/5
|
|
204
|
-
def getPackageDispatcher() -> Callable[[ComputationState], ComputationState]:
|
|
205
|
-
# NOTE but this part, if the package flow is synthetic, probably needs to be delegated
|
|
206
|
-
# to the authority for creating _that_ synthetic flow.
|
|
207
|
-
|
|
208
|
-
moduleImported: ModuleType = importlib_import_module(theLogicalPathModuleDispatcher)
|
|
209
|
-
dispatcherCallable = getattr(moduleImported, The.dispatcherCallable)
|
|
210
|
-
return dispatcherCallable
|
|
211
|
-
|
|
212
|
-
"""Technical concepts I am likely using and likely want to use more effectively:
|
|
213
|
-
- Configuration Registry
|
|
214
|
-
- Write-Once, Read-Many (WORM) / Immutable Initialization
|
|
215
|
-
- Lazy Initialization
|
|
216
|
-
- Separate configuration from business logic
|
|
217
|
-
|
|
218
|
-
----
|
|
219
|
-
theSSOT and yourSSOT
|
|
220
|
-
|
|
221
|
-
----
|
|
222
|
-
delay realization/instantiation until a concrete value is desired
|
|
223
|
-
moment of truth: when the value is needed, not when the value is defined
|
|
224
|
-
|
|
225
|
-
----
|
|
226
|
-
2025 March 11
|
|
227
|
-
Note to self: fundamental concept in Python:
|
|
228
|
-
Identifiers: scope and resolution, LEGB (Local, Enclosing, Global, Builtin)
|
|
229
|
-
- Local: Inside the function
|
|
230
|
-
- Enclosing: Inside enclosing functions
|
|
231
|
-
- Global: At the uppermost level
|
|
232
|
-
- Builtin: Python's built-in names
|
|
233
|
-
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mapFolding
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.4
|
|
4
4
|
Summary: Map folding algorithm with code transformation framework for optimizing numerical computations
|
|
5
5
|
Author-email: Hunter Hogan <HunterHogan@pm.me>
|
|
6
6
|
License: CC-BY-NC-4.0
|
|
@@ -45,6 +45,7 @@ Requires-Dist: pytest-cov; extra == "testing"
|
|
|
45
45
|
Requires-Dist: pytest-env; extra == "testing"
|
|
46
46
|
Requires-Dist: pytest-xdist; extra == "testing"
|
|
47
47
|
Requires-Dist: pyupgrade; extra == "testing"
|
|
48
|
+
Requires-Dist: ruff; extra == "testing"
|
|
48
49
|
Dynamic: license-file
|
|
49
50
|
|
|
50
51
|
# mapFolding: Algorithms for enumerating distinct map/stamp folding patterns 🗺️
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
mapFolding/__init__.py,sha256=z3joPk4hgIbSEsIWGgkOukl-nrgI5u4wg0mRJ7aSRHM,1982
|
|
2
|
+
mapFolding/basecamp.py,sha256=CGJOUE0eQsS4EzHjptzJbxg-Oy4t6TsE7P3vgTRuAww,4763
|
|
3
|
+
mapFolding/beDRY.py,sha256=UhH52BryHQNRjphf_PirtMkV45rhdemdC9PmnpACq7I,9397
|
|
4
|
+
mapFolding/filesystem.py,sha256=JyeFLlkeMqhsYGp80ViwDjrrDgFaTTdGTewLtla-m00,7132
|
|
5
|
+
mapFolding/noHomeYet.py,sha256=UKZeWlyn0SKlF9dhYoud7E6gWXpiSEekZOOoJp88WeI,1362
|
|
6
|
+
mapFolding/oeis.py,sha256=F1tGo2kT9xR3eRaXyxH0bnAWscbn38VS2fIY8-VlLZs,12616
|
|
7
|
+
mapFolding/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
mapFolding/theDao.py,sha256=MVopt1LzhdIQYA97SEoq9bdzct6hbK0lEyPxBAAlVTc,9934
|
|
9
|
+
mapFolding/theSSOT.py,sha256=JCejbNNVP6s9dR2vkdttKXleyZrQroYRWUjGQrXyFaI,11845
|
|
10
|
+
mapFolding/reference/__init__.py,sha256=UIEU8BJR_YDzjFQcLel3XtHzOCJiOUGlGiWzOzbvhik,2206
|
|
11
|
+
mapFolding/reference/flattened.py,sha256=QK1xG9SllqCoi68e86Hyl9d9ATUAAFNpTQI-3zmcp5I,16072
|
|
12
|
+
mapFolding/reference/hunterNumba.py,sha256=espFiX92EPZ1Ub1YQVoBnNYvh2kFg1HR6Qa4djx8Ixg,7253
|
|
13
|
+
mapFolding/reference/irvineJavaPort.py,sha256=UEfIX4QbPLl5jnyfYIyX5YRR3_rYvPUikK8jLehsFko,4076
|
|
14
|
+
mapFolding/reference/jaxCount.py,sha256=TuDNKOnyhQfuixKmIxO9Algv7dvy7KMGhgsV3h96FGE,14853
|
|
15
|
+
mapFolding/reference/lunnanNumpy.py,sha256=mMgrgbrBpe4nmo72ThEI-MGH0OwEHmfMPczSXHp2qKo,4357
|
|
16
|
+
mapFolding/reference/lunnanWhile.py,sha256=ZL8GAQtPs5nJZSgoDl5USrLSS_zs03y98y1Z9E4jOmQ,3799
|
|
17
|
+
mapFolding/reference/rotatedEntryPoint.py,sha256=5ughpKUT2JQhoAKgoDUdYNjgWQYPGV8v-7dWEAdDmfE,10274
|
|
18
|
+
mapFolding/reference/total_countPlus1vsPlusN.py,sha256=yJZAVLVdoXqHag2_N6_6CT-Q6HXBgRro-eny93-Rlpw,9307
|
|
19
|
+
mapFolding/reference/jobsCompleted/__init__.py,sha256=TU93ZGUW1xEkT6d9mQFn_rp5DvRy0ZslEB2Q6MF5ZDc,2596
|
|
20
|
+
mapFolding/reference/jobsCompleted/[2x19]/p2x19.py,sha256=_tvYtfzMWVo2VtUbIAieoscb4N8FFflgTdW4-ljBUuA,19626
|
|
21
|
+
mapFolding/reference/jobsCompleted/p2x19/p2x19.py,sha256=eZEw4Me4ocTt6VXoK2-Sbd5SowZtxRIbN9dZmc7OCVg,6395
|
|
22
|
+
mapFolding/someAssemblyRequired/__init__.py,sha256=v8aBRcRa4d_34SUNu_mZC5K_NC7-buy849k2nnk9lTE,2704
|
|
23
|
+
mapFolding/someAssemblyRequired/_theTypes.py,sha256=nHFgpts-hVy2lNbyvIyP5JeG-ikaIlbRaMUPnEDwzhc,1924
|
|
24
|
+
mapFolding/someAssemblyRequired/_tool_Make.py,sha256=nIAjiq-Q2sBvO_bVEGFh3Z79vXmcqcfJCtuhR4Vyzqw,7081
|
|
25
|
+
mapFolding/someAssemblyRequired/_tool_Then.py,sha256=8ZCI8A6-EtUo02m71h60iJwnWfdwxWiQT0_OWJDFBho,2566
|
|
26
|
+
mapFolding/someAssemblyRequired/_toolboxAntecedents.py,sha256=USqFn2HKiu2aUYUrUI-UVRq2tAvVw2hLtH2uLJ61ZbY,19110
|
|
27
|
+
mapFolding/someAssemblyRequired/_toolboxContainers.py,sha256=jjGyKhblHqDiCzjHRfyqnI2UHNzJTe3aBwNofukcf88,15998
|
|
28
|
+
mapFolding/someAssemblyRequired/_toolboxPython.py,sha256=OSFMNBiyBofzckF6J8OYibm9Zh3GHXB8bJrMggVT8fw,3801
|
|
29
|
+
mapFolding/someAssemblyRequired/getLLVMforNoReason.py,sha256=CDbesDJSQE-P8uznXIAttRw9f413UpUt-RowK38hqbY,2735
|
|
30
|
+
mapFolding/someAssemblyRequired/ingredientsNumba.py,sha256=Qb2WVDv5XszwcQCs3zFrodS7G0GKstSqVrKDXBzdMaw,8128
|
|
31
|
+
mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py,sha256=A9H6gbTNtK0bihxJ98Xv5ePzGeOyYP6e0Fxmj2NTh-M,11186
|
|
32
|
+
mapFolding/someAssemblyRequired/synthesizeNumbaJob.py,sha256=_TPANqYkWS4O2IhU4EOatRv-OetfFCfk5b6rrxB5i48,13210
|
|
33
|
+
mapFolding/someAssemblyRequired/transformDataStructures.py,sha256=I4OtFUF7ZNpEzsAbIbcqTifnY1vIwN0v1f-XFQQp3Wk,13558
|
|
34
|
+
mapFolding/someAssemblyRequired/transformationTools.py,sha256=fXr3XKHuFntKLTxoau9ZnbZJRPOG3aS2BhjJvkgRXRs,7932
|
|
35
|
+
mapFolding/syntheticModules/__init__.py,sha256=evVFqhCGa-WZKDiLcnQWjs-Bj34eRnfSLqz_d7dFYZY,83
|
|
36
|
+
mapFolding/syntheticModules/numbaCount_doTheNeedful.py,sha256=3thXThbv2Xo0t_cRGzMbHPFXTBmLClmKejR_Ibu_jOo,15697
|
|
37
|
+
mapfolding-0.8.4.dist-info/licenses/LICENSE,sha256=NxH5Y8BdC-gNU-WSMwim3uMbID2iNDXJz7fHtuTdXhk,19346
|
|
38
|
+
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
|
+
tests/conftest.py,sha256=-5rAnacJAuDcG84jeoQAA35GvUTW6YTcT6E_xU7EXQ0,11587
|
|
40
|
+
tests/test_computations.py,sha256=R5gea8liIE_rBYvgRDIby6GljBazuGgqCeYcqKRjORg,3449
|
|
41
|
+
tests/test_filesystem.py,sha256=RplMT0GULGQxomQSbk5wLlvNsj7ehDlZmzayatJopp4,3150
|
|
42
|
+
tests/test_oeis.py,sha256=uxvwmgbnylSDdsVJfuAT0LuYLbIVFwSgdLxHm-xUGBM,5043
|
|
43
|
+
tests/test_other.py,sha256=AzsCXiX8x5WJ7i0SocWQY6lT30IJg1lKoybx03X2eqU,4281
|
|
44
|
+
tests/test_tasks.py,sha256=hkZygihT8bCEO2zc-2VcxReQrZJBwgLNbYx0YP4lTDg,2853
|
|
45
|
+
mapfolding-0.8.4.dist-info/METADATA,sha256=NMXRV8HTCO8Nab_q1Bo3W6i0eAirJ9K453LRGOv86i8,9326
|
|
46
|
+
mapfolding-0.8.4.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
47
|
+
mapfolding-0.8.4.dist-info/entry_points.txt,sha256=F3OUeZR1XDTpoH7k3wXuRb3KF_kXTTeYhu5AGK1SiOQ,146
|
|
48
|
+
mapfolding-0.8.4.dist-info/top_level.txt,sha256=1gP2vFaqPwHujGwb3UjtMlLEGN-943VSYFR7V4gDqW8,17
|
|
49
|
+
mapfolding-0.8.4.dist-info/RECORD,,
|
tests/conftest.py
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
from importlib import import_module as importlib_import_module
|
|
2
1
|
from collections.abc import Callable, Generator, Sequence
|
|
3
|
-
from types import ModuleType
|
|
4
|
-
|
|
5
|
-
import numpy
|
|
6
|
-
from mapFolding.theSSOT import ComputationState, The, getPackageDispatcher
|
|
7
2
|
from mapFolding.beDRY import getLeavesTotal, validateListDimensions, makeDataContainer
|
|
8
3
|
from mapFolding.oeis import oeisIDsImplemented, settingsOEIS
|
|
9
|
-
from mapFolding.someAssemblyRequired import
|
|
4
|
+
from mapFolding.someAssemblyRequired import importLogicalPath2Callable
|
|
10
5
|
from mapFolding.someAssemblyRequired.synthesizeNumbaFlow import makeNumbaFlow
|
|
6
|
+
from mapFolding.someAssemblyRequired._toolboxContainers import RecipeSynthesizeFlow
|
|
7
|
+
from mapFolding.theSSOT import ComputationState, The, getPackageDispatcher
|
|
11
8
|
from pathlib import Path, PurePosixPath
|
|
9
|
+
from types import ModuleType
|
|
12
10
|
from typing import Any, ContextManager
|
|
11
|
+
import importlib
|
|
13
12
|
import importlib.util
|
|
13
|
+
import numpy
|
|
14
14
|
import pytest
|
|
15
15
|
import random
|
|
16
16
|
import shutil
|
|
@@ -18,8 +18,9 @@ import unittest.mock
|
|
|
18
18
|
import uuid
|
|
19
19
|
|
|
20
20
|
# SSOT for test data paths and filenames
|
|
21
|
-
pathDataSamples = Path("tests/dataSamples")
|
|
21
|
+
pathDataSamples = Path("tests/dataSamples").absolute()
|
|
22
22
|
pathTmpRoot: Path = pathDataSamples / "tmp"
|
|
23
|
+
pathTmpRoot.mkdir(parents=True, exist_ok=True)
|
|
23
24
|
|
|
24
25
|
# The registrar maintains the register of temp files
|
|
25
26
|
registerOfTemporaryFilesystemObjects: set[Path] = set()
|
|
@@ -100,7 +101,22 @@ def setupWarningsAsErrors() -> Generator[None, Any, None]:
|
|
|
100
101
|
warnings.resetwarnings()
|
|
101
102
|
|
|
102
103
|
@pytest.fixture
|
|
103
|
-
def
|
|
104
|
+
def oneTestCuzTestsOverwritingTests(oeisID_1random: str) -> tuple[int, ...]:
|
|
105
|
+
"""For each `oeisID_1random` from the `pytest.fixture`, returns `listDimensions` from `valuesTestValidation`
|
|
106
|
+
if `validateListDimensions` approves. Each `listDimensions` is suitable for testing counts."""
|
|
107
|
+
while True:
|
|
108
|
+
n = random.choice(settingsOEIS[oeisID_1random]['valuesTestValidation'])
|
|
109
|
+
if n < 2:
|
|
110
|
+
continue
|
|
111
|
+
listDimensionsCandidate = list(settingsOEIS[oeisID_1random]['getMapShape'](n))
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
return validateListDimensions(listDimensionsCandidate)
|
|
115
|
+
except (ValueError, NotImplementedError):
|
|
116
|
+
pass
|
|
117
|
+
|
|
118
|
+
@pytest.fixture
|
|
119
|
+
def listDimensionsTestCountFolds(oeisID: str) -> tuple[int, ...]:
|
|
104
120
|
"""For each `oeisID` from the `pytest.fixture`, returns `listDimensions` from `valuesTestValidation`
|
|
105
121
|
if `validateListDimensions` approves. Each `listDimensions` is suitable for testing counts."""
|
|
106
122
|
while True:
|
|
@@ -115,7 +131,7 @@ def listDimensionsTestCountFolds(oeisID: str):
|
|
|
115
131
|
pass
|
|
116
132
|
|
|
117
133
|
@pytest.fixture
|
|
118
|
-
def mapShapeTestFunctionality(oeisID_1random: str):
|
|
134
|
+
def mapShapeTestFunctionality(oeisID_1random: str) -> tuple[int, ...]:
|
|
119
135
|
"""To test functionality, get one `listDimensions` from `valuesTestValidation` if
|
|
120
136
|
`validateListDimensions` approves. The algorithm can count the folds of the returned
|
|
121
137
|
`listDimensions` in a short enough time suitable for testing."""
|
|
@@ -159,17 +175,6 @@ def mockFoldingFunction() -> Callable[..., Callable[..., None]]:
|
|
|
159
175
|
return mock_countFolds
|
|
160
176
|
return make_mock
|
|
161
177
|
|
|
162
|
-
@pytest.fixture
|
|
163
|
-
def mockDispatcher() -> Callable[[Any], ContextManager[Any]]:
|
|
164
|
-
"""Context manager for mocking dispatcher callable."""
|
|
165
|
-
def wrapper(mockFunction: Any) -> ContextManager[Any]:
|
|
166
|
-
dispatcherCallable = getPackageDispatcher()
|
|
167
|
-
return unittest.mock.patch(
|
|
168
|
-
f"{dispatcherCallable.__module__}.{dispatcherCallable.__name__}",
|
|
169
|
-
side_effect=mockFunction
|
|
170
|
-
)
|
|
171
|
-
return wrapper
|
|
172
|
-
|
|
173
178
|
@pytest.fixture(params=oeisIDsImplemented)
|
|
174
179
|
def oeisID(request: pytest.FixtureRequest) -> Any:
|
|
175
180
|
return request.param
|
|
@@ -198,42 +203,42 @@ def useThisDispatcher() -> Generator[Callable[..., None], Any, None]:
|
|
|
198
203
|
basecamp.getPackageDispatcher = dispatcherOriginal
|
|
199
204
|
|
|
200
205
|
def getAlgorithmDispatcher() -> Callable[[ComputationState], ComputationState]:
|
|
201
|
-
moduleImported: ModuleType =
|
|
202
|
-
dispatcherCallable = getattr(moduleImported, The.
|
|
206
|
+
moduleImported: ModuleType = importlib.import_module(The.logicalPathModuleSourceAlgorithm)
|
|
207
|
+
dispatcherCallable = getattr(moduleImported, The.sourceCallableDispatcher)
|
|
203
208
|
return dispatcherCallable
|
|
204
209
|
|
|
205
210
|
@pytest.fixture
|
|
206
211
|
def useAlgorithmSourceDispatcher(useThisDispatcher: Callable[..., Any]) -> Generator[None, None, None]:
|
|
207
212
|
"""Temporarily patches getDispatcherCallable to return the algorithm dispatcher."""
|
|
208
|
-
useThisDispatcher(
|
|
213
|
+
useThisDispatcher(importLogicalPath2Callable(The.logicalPathModuleSourceAlgorithm, The.sourceCallableDispatcher))
|
|
209
214
|
yield
|
|
210
215
|
|
|
211
216
|
@pytest.fixture
|
|
212
217
|
def syntheticDispatcherFixture(useThisDispatcher: Callable[..., Any], pathTmpTesting: Path) -> Callable[..., Any]:
|
|
213
218
|
"""Generate synthetic Numba-optimized dispatcher module and patch the dispatcher"""
|
|
214
219
|
# Configure synthesis flow to use test directory
|
|
215
|
-
|
|
220
|
+
TESTINGrecipeFlow = RecipeSynthesizeFlow(
|
|
216
221
|
pathPackage=PurePosixPath(pathTmpTesting.absolute()),
|
|
217
|
-
|
|
222
|
+
logicalPathFlowRoot=None,
|
|
218
223
|
moduleDispatcher="test_dispatcher",
|
|
219
224
|
# Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
|
|
220
225
|
# dispatcherCallable="dispatcherSynthetic",
|
|
221
226
|
)
|
|
222
227
|
|
|
223
228
|
# Generate optimized module in test directory
|
|
224
|
-
makeNumbaFlow(
|
|
229
|
+
makeNumbaFlow(TESTINGrecipeFlow)
|
|
225
230
|
|
|
226
231
|
# Import synthesized dispatcher
|
|
227
232
|
importlibSpecificationDispatcher = importlib.util.spec_from_file_location(
|
|
228
|
-
|
|
229
|
-
Path(
|
|
233
|
+
TESTINGrecipeFlow.moduleDispatcher,
|
|
234
|
+
Path(TESTINGrecipeFlow.pathFilenameDispatcher),
|
|
230
235
|
)
|
|
231
236
|
if importlibSpecificationDispatcher is None or importlibSpecificationDispatcher.loader is None:
|
|
232
237
|
raise ImportError("Failed to load synthetic dispatcher module")
|
|
233
238
|
|
|
234
239
|
moduleSpecificationDispatcher = importlib.util.module_from_spec(importlibSpecificationDispatcher)
|
|
235
240
|
importlibSpecificationDispatcher.loader.exec_module(moduleSpecificationDispatcher)
|
|
236
|
-
callableDispatcherSynthetic = getattr(moduleSpecificationDispatcher,
|
|
241
|
+
callableDispatcherSynthetic = getattr(moduleSpecificationDispatcher, TESTINGrecipeFlow.callableDispatcher)
|
|
237
242
|
|
|
238
243
|
# Patch dispatcher and return callable
|
|
239
244
|
useThisDispatcher(callableDispatcherSynthetic)
|