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.
Files changed (30) hide show
  1. mapFolding/__init__.py +2 -2
  2. mapFolding/basecamp.py +11 -5
  3. mapFolding/filesystem.py +134 -109
  4. mapFolding/oeis.py +1 -1
  5. mapFolding/someAssemblyRequired/__init__.py +37 -18
  6. mapFolding/someAssemblyRequired/_theTypes.py +35 -0
  7. mapFolding/someAssemblyRequired/_tool_Make.py +92 -0
  8. mapFolding/someAssemblyRequired/_tool_Then.py +65 -0
  9. mapFolding/someAssemblyRequired/_toolboxAntecedents.py +326 -0
  10. mapFolding/someAssemblyRequired/_toolboxContainers.py +306 -0
  11. mapFolding/someAssemblyRequired/_toolboxPython.py +76 -0
  12. mapFolding/someAssemblyRequired/ingredientsNumba.py +17 -24
  13. mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py +114 -169
  14. mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +247 -0
  15. mapFolding/someAssemblyRequired/transformDataStructures.py +167 -100
  16. mapFolding/someAssemblyRequired/transformationTools.py +63 -685
  17. mapFolding/syntheticModules/numbaCount_doTheNeedful.py +36 -33
  18. mapFolding/theDao.py +13 -11
  19. mapFolding/theSSOT.py +69 -112
  20. {mapfolding-0.8.3.dist-info → mapfolding-0.8.4.dist-info}/METADATA +2 -1
  21. mapfolding-0.8.4.dist-info/RECORD +49 -0
  22. {mapfolding-0.8.3.dist-info → mapfolding-0.8.4.dist-info}/WHEEL +1 -1
  23. tests/conftest.py +34 -29
  24. tests/test_computations.py +40 -31
  25. tests/test_filesystem.py +3 -3
  26. mapFolding/someAssemblyRequired/synthesizeNumbaJobVESTIGIAL.py +0 -413
  27. mapfolding-0.8.3.dist-info/RECORD +0 -43
  28. {mapfolding-0.8.3.dist-info → mapfolding-0.8.4.dist-info}/entry_points.txt +0 -0
  29. {mapfolding-0.8.3.dist-info → mapfolding-0.8.4.dist-info}/licenses/LICENSE +0 -0
  30. {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
- with ProcessPoolExecutor(state.concurrencyLimit) as concurrencyManager:
143
- for indexSherpa in range(state.taskDivisions):
144
- stateParallel = deepcopy(state)
145
- stateParallel.taskIndex = indexSherpa
146
- mapShape: tuple[DatatypeLeavesTotal, ...] = stateParallel.mapShape
147
- leavesTotal: DatatypeLeavesTotal = stateParallel.leavesTotal
148
- taskDivisions: DatatypeLeavesTotal = stateParallel.taskDivisions
149
- concurrencyLimit: DatatypeElephino = stateParallel.concurrencyLimit
150
- connectionGraph: Array3D = stateParallel.connectionGraph
151
- dimensionsTotal: DatatypeLeavesTotal = stateParallel.dimensionsTotal
152
- countDimensionsGapped: Array1DLeavesTotal = stateParallel.countDimensionsGapped
153
- dimensionsUnconstrained: DatatypeLeavesTotal = stateParallel.dimensionsUnconstrained
154
- gapRangeStart: Array1DElephino = stateParallel.gapRangeStart
155
- gapsWhere: Array1DLeavesTotal = stateParallel.gapsWhere
156
- leafAbove: Array1DLeavesTotal = stateParallel.leafAbove
157
- leafBelow: Array1DLeavesTotal = stateParallel.leafBelow
158
- foldGroups: Array1DFoldsTotal = stateParallel.foldGroups
159
- foldsTotal: DatatypeFoldsTotal = stateParallel.foldsTotal
160
- gap1ndex: DatatypeLeavesTotal = stateParallel.gap1ndex
161
- gap1ndexCeiling: DatatypeElephino = stateParallel.gap1ndexCeiling
162
- groupsOfFolds: DatatypeFoldsTotal = stateParallel.groupsOfFolds
163
- indexDimension: DatatypeLeavesTotal = stateParallel.indexDimension
164
- indexLeaf: DatatypeLeavesTotal = stateParallel.indexLeaf
165
- indexMiniGap: DatatypeElephino = stateParallel.indexMiniGap
166
- leaf1ndex: DatatypeElephino = stateParallel.leaf1ndex
167
- leafConnectee: DatatypeElephino = stateParallel.leafConnectee
168
- taskIndex: DatatypeLeavesTotal = stateParallel.taskIndex
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(state.taskDivisions):
171
- state.foldGroups[indexSherpa] = dictionaryConcurrency[indexSherpa].result()
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
- with ProcessPoolExecutor(state.concurrencyLimit) as concurrencyManager:
231
- for indexSherpa in range(state.taskDivisions):
232
- stateParallel = deepcopy(state)
233
- stateParallel.taskIndex = indexSherpa
234
- dictionaryConcurrency[indexSherpa] = concurrencyManager.submit(countParallel, stateParallel)
235
- for indexSherpa in range(state.taskDivisions):
236
- state.foldGroups[indexSherpa] = dictionaryConcurrency[indexSherpa].result().foldGroups[indexSherpa]
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
- concurrencyPackage = Z0Z_concurrencyPackage
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
- sourceInitializeCallable: str = dataclasses.field(default='countInitialize', metadata={'evaluateWhen': 'packaging'})
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
- """ Compute the task distribution identifier by concatenating dataclassInstance and dataclassInstanceTaskDistributionSuffix. """
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
- """ Compute the logical path module for the dataclass by joining packageName and dataclassModule. """
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
- The = PackageSettings()
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
- numpyElephino: TypeAlias = numpy_int16
125
+ NumPyElephino: TypeAlias = numpy_int16
105
126
 
106
127
  DatatypeFoldsTotal: TypeAlias = int
107
- numpyFoldsTotal: TypeAlias = numpy_int64
128
+ NumPyFoldsTotal: TypeAlias = numpy_int64
108
129
 
109
- Array3D: TypeAlias = ndarray[tuple[int, int, int], dtype[numpyLeavesTotal]]
110
- Array1DLeavesTotal: TypeAlias = ndarray[tuple[int], dtype[numpyLeavesTotal]]
111
- Array1DElephino: TypeAlias = ndarray[tuple[int], dtype[numpyElephino]]
112
- Array1DFoldsTotal: TypeAlias = ndarray[tuple[int], dtype[numpyFoldsTotal]]
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
- self.connectionGraph = makeConnectionGraph(self.mapShape, self.leavesTotal, numpyLeavesTotal)
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: # pyright: ignore[reportUnnecessaryComparison]
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), numpyFoldsTotal)
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
- leavesTotalAsInt = int(self.leavesTotal)
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
- self.countDimensionsGapped = makeDataContainer(leavesTotalAsInt + 1, numpyLeavesTotal)
160
- if self.gapRangeStart is None:
161
- self.gapRangeStart = makeDataContainer(leavesTotalAsInt + 1, numpyElephino)
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
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (77.0.3)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
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 RecipeSynthesizeFlow
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 listDimensionsTestCountFolds(oeisID: str):
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 = importlib_import_module(The.logicalPathModuleSourceAlgorithm)
202
- dispatcherCallable = getattr(moduleImported, The.dispatcherCallable)
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(getAlgorithmDispatcher())
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
- recipeFlow = RecipeSynthesizeFlow(
220
+ TESTINGrecipeFlow = RecipeSynthesizeFlow(
216
221
  pathPackage=PurePosixPath(pathTmpTesting.absolute()),
217
- Z0Z_flowLogicalPathRoot=None,
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(recipeFlow)
229
+ makeNumbaFlow(TESTINGrecipeFlow)
225
230
 
226
231
  # Import synthesized dispatcher
227
232
  importlibSpecificationDispatcher = importlib.util.spec_from_file_location(
228
- recipeFlow.moduleDispatcher,
229
- Path(recipeFlow.pathFilenameDispatcher),
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, recipeFlow.dispatcherCallable)
241
+ callableDispatcherSynthetic = getattr(moduleSpecificationDispatcher, TESTINGrecipeFlow.callableDispatcher)
237
242
 
238
243
  # Patch dispatcher and return callable
239
244
  useThisDispatcher(callableDispatcherSynthetic)