mapFolding 0.11.1__py3-none-any.whl → 0.11.3__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 +7 -60
- mapFolding/basecamp.py +15 -13
- mapFolding/beDRY.py +4 -36
- mapFolding/dataBaskets.py +24 -2
- mapFolding/datatypes.py +0 -3
- mapFolding/{toolboxFilesystem.py → filesystemToolkit.py} +3 -3
- mapFolding/oeis.py +3 -5
- mapFolding/someAssemblyRequired/RecipeJob.py +8 -116
- mapFolding/someAssemblyRequired/Z0Z_makeAllModules.py +492 -0
- mapFolding/someAssemblyRequired/__init__.py +5 -31
- mapFolding/someAssemblyRequired/_toolIfThis.py +5 -6
- mapFolding/someAssemblyRequired/{_toolboxContainers.py → _toolkitContainers.py} +6 -127
- mapFolding/someAssemblyRequired/infoBooth.py +70 -0
- mapFolding/someAssemblyRequired/makeJobTheorem2Numba.py +13 -12
- mapFolding/someAssemblyRequired/{toolboxNumba.py → toolkitNumba.py} +2 -44
- mapFolding/someAssemblyRequired/transformationTools.py +16 -174
- mapFolding/syntheticModules/countParallel.py +98 -0
- mapFolding/syntheticModules/dataPacking.py +1 -1
- mapFolding/theSSOT.py +12 -246
- {mapfolding-0.11.1.dist-info → mapfolding-0.11.3.dist-info}/METADATA +16 -11
- mapfolding-0.11.3.dist-info/RECORD +53 -0
- {mapfolding-0.11.1.dist-info → mapfolding-0.11.3.dist-info}/WHEEL +1 -1
- tests/conftest.py +2 -79
- tests/test_computations.py +12 -19
- tests/test_filesystem.py +1 -2
- tests/test_other.py +1 -1
- tests/test_tasks.py +3 -4
- mapFolding/someAssemblyRequired/Z0Z_makeSomeModules.py +0 -325
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +0 -314
- mapFolding/syntheticModules/numbaCount.py +0 -201
- mapFolding/theDao.py +0 -243
- mapfolding-0.11.1.dist-info/RECORD +0 -54
- {mapfolding-0.11.1.dist-info → mapfolding-0.11.3.dist-info}/entry_points.txt +0 -0
- {mapfolding-0.11.1.dist-info → mapfolding-0.11.3.dist-info}/licenses/LICENSE +0 -0
- {mapfolding-0.11.1.dist-info → mapfolding-0.11.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from concurrent.futures import Future as ConcurrentFuture, ProcessPoolExecutor
|
|
2
|
+
from copy import deepcopy
|
|
3
|
+
from mapFolding.dataBaskets import Array1DElephino, Array1DLeavesTotal, Array3D, DatatypeElephino, DatatypeFoldsTotal, DatatypeLeavesTotal, ParallelMapFoldingState
|
|
4
|
+
from multiprocessing import set_start_method as multiprocessing_set_start_method
|
|
5
|
+
from numba import jit
|
|
6
|
+
if __name__ == '__main__':
|
|
7
|
+
multiprocessing_set_start_method('spawn')
|
|
8
|
+
|
|
9
|
+
@jit(cache=True, error_model='numpy', fastmath=True, forceinline=True)
|
|
10
|
+
def count(groupsOfFolds: DatatypeFoldsTotal, gap1ndex: DatatypeElephino, gap1ndexCeiling: DatatypeElephino, indexDimension: DatatypeLeavesTotal, indexLeaf: DatatypeLeavesTotal, indexMiniGap: DatatypeElephino, leaf1ndex: DatatypeLeavesTotal, leafConnectee: DatatypeLeavesTotal, dimensionsUnconstrained: DatatypeLeavesTotal, countDimensionsGapped: Array1DLeavesTotal, gapRangeStart: Array1DElephino, gapsWhere: Array1DLeavesTotal, leafAbove: Array1DLeavesTotal, leafBelow: Array1DLeavesTotal, connectionGraph: Array3D, dimensionsTotal: DatatypeLeavesTotal, leavesTotal: DatatypeLeavesTotal, taskDivisions: DatatypeLeavesTotal, taskIndex: DatatypeLeavesTotal) -> tuple[DatatypeFoldsTotal, DatatypeElephino, DatatypeElephino, DatatypeLeavesTotal, DatatypeLeavesTotal, DatatypeElephino, DatatypeLeavesTotal, DatatypeLeavesTotal, DatatypeLeavesTotal, Array1DLeavesTotal, Array1DElephino, Array1DLeavesTotal, Array1DLeavesTotal, Array1DLeavesTotal, Array3D, DatatypeLeavesTotal, DatatypeLeavesTotal, DatatypeLeavesTotal, DatatypeLeavesTotal]:
|
|
11
|
+
while leaf1ndex > 0:
|
|
12
|
+
if leaf1ndex <= 1 or leafBelow[0] == 1:
|
|
13
|
+
if leaf1ndex > leavesTotal:
|
|
14
|
+
groupsOfFolds += 1
|
|
15
|
+
else:
|
|
16
|
+
dimensionsUnconstrained = dimensionsTotal
|
|
17
|
+
gap1ndexCeiling = gapRangeStart[leaf1ndex - 1]
|
|
18
|
+
indexDimension = 0
|
|
19
|
+
while indexDimension < dimensionsTotal:
|
|
20
|
+
leafConnectee = connectionGraph[indexDimension, leaf1ndex, leaf1ndex]
|
|
21
|
+
if leafConnectee == leaf1ndex:
|
|
22
|
+
dimensionsUnconstrained -= 1
|
|
23
|
+
else:
|
|
24
|
+
while leafConnectee != leaf1ndex:
|
|
25
|
+
if leaf1ndex != taskDivisions or leafConnectee % taskDivisions == taskIndex:
|
|
26
|
+
gapsWhere[gap1ndexCeiling] = leafConnectee
|
|
27
|
+
if countDimensionsGapped[leafConnectee] == 0:
|
|
28
|
+
gap1ndexCeiling += 1
|
|
29
|
+
countDimensionsGapped[leafConnectee] += 1
|
|
30
|
+
leafConnectee = connectionGraph[indexDimension, leaf1ndex, leafBelow[leafConnectee]]
|
|
31
|
+
indexDimension += 1
|
|
32
|
+
if not dimensionsUnconstrained:
|
|
33
|
+
indexLeaf = 0
|
|
34
|
+
while indexLeaf < leaf1ndex:
|
|
35
|
+
gapsWhere[gap1ndexCeiling] = indexLeaf
|
|
36
|
+
gap1ndexCeiling += 1
|
|
37
|
+
indexLeaf += 1
|
|
38
|
+
indexMiniGap = gap1ndex
|
|
39
|
+
while indexMiniGap < gap1ndexCeiling:
|
|
40
|
+
gapsWhere[gap1ndex] = gapsWhere[indexMiniGap]
|
|
41
|
+
if countDimensionsGapped[gapsWhere[indexMiniGap]] == dimensionsUnconstrained:
|
|
42
|
+
gap1ndex += 1
|
|
43
|
+
countDimensionsGapped[gapsWhere[indexMiniGap]] = 0
|
|
44
|
+
indexMiniGap += 1
|
|
45
|
+
while leaf1ndex > 0 and gap1ndex == gapRangeStart[leaf1ndex - 1]:
|
|
46
|
+
leaf1ndex -= 1
|
|
47
|
+
leafBelow[leafAbove[leaf1ndex]] = leafBelow[leaf1ndex]
|
|
48
|
+
leafAbove[leafBelow[leaf1ndex]] = leafAbove[leaf1ndex]
|
|
49
|
+
if leaf1ndex > 0:
|
|
50
|
+
gap1ndex -= 1
|
|
51
|
+
leafAbove[leaf1ndex] = gapsWhere[gap1ndex]
|
|
52
|
+
leafBelow[leaf1ndex] = leafBelow[leafAbove[leaf1ndex]]
|
|
53
|
+
leafBelow[leafAbove[leaf1ndex]] = leaf1ndex
|
|
54
|
+
leafAbove[leafBelow[leaf1ndex]] = leaf1ndex
|
|
55
|
+
gapRangeStart[leaf1ndex] = gap1ndex
|
|
56
|
+
leaf1ndex += 1
|
|
57
|
+
return (groupsOfFolds, gap1ndex, gap1ndexCeiling, indexDimension, indexLeaf, indexMiniGap, leaf1ndex, leafConnectee, dimensionsUnconstrained, countDimensionsGapped, gapRangeStart, gapsWhere, leafAbove, leafBelow, connectionGraph, dimensionsTotal, leavesTotal, taskDivisions, taskIndex)
|
|
58
|
+
|
|
59
|
+
def unRepackParallelMapFoldingState(state: ParallelMapFoldingState) -> ParallelMapFoldingState:
|
|
60
|
+
mapShape: tuple[DatatypeLeavesTotal, ...] = state.mapShape
|
|
61
|
+
groupsOfFolds: DatatypeFoldsTotal = state.groupsOfFolds
|
|
62
|
+
gap1ndex: DatatypeElephino = state.gap1ndex
|
|
63
|
+
gap1ndexCeiling: DatatypeElephino = state.gap1ndexCeiling
|
|
64
|
+
indexDimension: DatatypeLeavesTotal = state.indexDimension
|
|
65
|
+
indexLeaf: DatatypeLeavesTotal = state.indexLeaf
|
|
66
|
+
indexMiniGap: DatatypeElephino = state.indexMiniGap
|
|
67
|
+
leaf1ndex: DatatypeLeavesTotal = state.leaf1ndex
|
|
68
|
+
leafConnectee: DatatypeLeavesTotal = state.leafConnectee
|
|
69
|
+
dimensionsUnconstrained: DatatypeLeavesTotal = state.dimensionsUnconstrained
|
|
70
|
+
countDimensionsGapped: Array1DLeavesTotal = state.countDimensionsGapped
|
|
71
|
+
gapRangeStart: Array1DElephino = state.gapRangeStart
|
|
72
|
+
gapsWhere: Array1DLeavesTotal = state.gapsWhere
|
|
73
|
+
leafAbove: Array1DLeavesTotal = state.leafAbove
|
|
74
|
+
leafBelow: Array1DLeavesTotal = state.leafBelow
|
|
75
|
+
connectionGraph: Array3D = state.connectionGraph
|
|
76
|
+
dimensionsTotal: DatatypeLeavesTotal = state.dimensionsTotal
|
|
77
|
+
leavesTotal: DatatypeLeavesTotal = state.leavesTotal
|
|
78
|
+
taskDivisions: DatatypeLeavesTotal = state.taskDivisions
|
|
79
|
+
taskIndex: DatatypeLeavesTotal = state.taskIndex
|
|
80
|
+
groupsOfFolds, gap1ndex, gap1ndexCeiling, indexDimension, indexLeaf, indexMiniGap, leaf1ndex, leafConnectee, dimensionsUnconstrained, countDimensionsGapped, gapRangeStart, gapsWhere, leafAbove, leafBelow, connectionGraph, dimensionsTotal, leavesTotal, taskDivisions, taskIndex = count(groupsOfFolds, gap1ndex, gap1ndexCeiling, indexDimension, indexLeaf, indexMiniGap, leaf1ndex, leafConnectee, dimensionsUnconstrained, countDimensionsGapped, gapRangeStart, gapsWhere, leafAbove, leafBelow, connectionGraph, dimensionsTotal, leavesTotal, taskDivisions, taskIndex)
|
|
81
|
+
state = ParallelMapFoldingState(mapShape=mapShape, groupsOfFolds=groupsOfFolds, gap1ndex=gap1ndex, gap1ndexCeiling=gap1ndexCeiling, indexDimension=indexDimension, indexLeaf=indexLeaf, indexMiniGap=indexMiniGap, leaf1ndex=leaf1ndex, leafConnectee=leafConnectee, dimensionsUnconstrained=dimensionsUnconstrained, countDimensionsGapped=countDimensionsGapped, gapRangeStart=gapRangeStart, gapsWhere=gapsWhere, leafAbove=leafAbove, leafBelow=leafBelow, taskDivisions=taskDivisions, taskIndex=taskIndex)
|
|
82
|
+
return state
|
|
83
|
+
|
|
84
|
+
def doTheNeedful(state: ParallelMapFoldingState, concurrencyLimit: int) -> tuple[int, list[ParallelMapFoldingState]]:
|
|
85
|
+
stateParallel = deepcopy(state)
|
|
86
|
+
listStatesParallel: list[ParallelMapFoldingState] = [stateParallel] * stateParallel.taskDivisions
|
|
87
|
+
groupsOfFoldsTotal: int = 0
|
|
88
|
+
dictionaryConcurrency: dict[int, ConcurrentFuture[ParallelMapFoldingState]] = {}
|
|
89
|
+
with ProcessPoolExecutor(concurrencyLimit) as concurrencyManager:
|
|
90
|
+
for indexSherpa in range(stateParallel.taskDivisions):
|
|
91
|
+
state = deepcopy(stateParallel)
|
|
92
|
+
state.taskIndex = indexSherpa
|
|
93
|
+
dictionaryConcurrency[indexSherpa] = concurrencyManager.submit(unRepackParallelMapFoldingState, state)
|
|
94
|
+
for indexSherpa in range(stateParallel.taskDivisions):
|
|
95
|
+
listStatesParallel[indexSherpa] = dictionaryConcurrency[indexSherpa].result()
|
|
96
|
+
groupsOfFoldsTotal += listStatesParallel[indexSherpa].groupsOfFolds
|
|
97
|
+
foldsTotal: int = groupsOfFoldsTotal * stateParallel.leavesTotal
|
|
98
|
+
return (foldsTotal, listStatesParallel)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from mapFolding.dataBaskets import Array1DElephino, Array1DLeavesTotal, Array3D, DatatypeElephino, DatatypeFoldsTotal, DatatypeLeavesTotal, MapFoldingState
|
|
2
2
|
from mapFolding.syntheticModules.theorem2Numba import count
|
|
3
3
|
|
|
4
|
-
def
|
|
4
|
+
def sequential(state: MapFoldingState) -> MapFoldingState:
|
|
5
5
|
mapShape: tuple[DatatypeLeavesTotal, ...] = state.mapShape
|
|
6
6
|
groupsOfFolds: DatatypeFoldsTotal = state.groupsOfFolds
|
|
7
7
|
gap1ndex: DatatypeElephino = state.gap1ndex
|
mapFolding/theSSOT.py
CHANGED
|
@@ -1,268 +1,34 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Single Source of Truth module for configuration, types, and computational state management.
|
|
3
|
-
|
|
4
|
-
This module defines the core data structures, type definitions, and configuration settings used throughout the
|
|
5
|
-
mapFolding package. It implements the Single Source of Truth (SSOT) principle to ensure consistency across the package's
|
|
6
|
-
components.
|
|
7
|
-
|
|
8
|
-
Key features:
|
|
9
|
-
1. The `ComputationState` dataclass, which encapsulates the state of the folding computation.
|
|
10
|
-
2. Unified type definitions for integers and arrays used in the computation.
|
|
11
|
-
3. Configuration settings for synthetic module generation and dispatching.
|
|
12
|
-
4. Path resolution and management for package resources and job output.
|
|
13
|
-
5. Dynamic dispatch functionality for algorithm implementations.
|
|
14
|
-
|
|
15
|
-
The module differentiates between "the" identifiers (package defaults) and other identifiers to avoid namespace
|
|
16
|
-
collisions when transforming algorithms.
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
from collections.abc import Callable
|
|
20
1
|
from importlib import import_module as importlib_import_module
|
|
21
2
|
from inspect import getfile as inspect_getfile
|
|
22
3
|
from pathlib import Path
|
|
23
4
|
from tomli import load as tomli_load
|
|
24
|
-
from types import ModuleType
|
|
25
5
|
import dataclasses
|
|
26
|
-
from mapFolding.datatypes import (
|
|
27
|
-
Array1DElephino as Array1DElephino,
|
|
28
|
-
Array1DFoldsTotal as Array1DFoldsTotal,
|
|
29
|
-
Array1DLeavesTotal as Array1DLeavesTotal,
|
|
30
|
-
Array3D as Array3D,
|
|
31
|
-
DatatypeElephino as DatatypeElephino,
|
|
32
|
-
DatatypeFoldsTotal as DatatypeFoldsTotal,
|
|
33
|
-
DatatypeLeavesTotal as DatatypeLeavesTotal,
|
|
34
|
-
NumPyElephino as NumPyElephino,
|
|
35
|
-
NumPyFoldsTotal as NumPyFoldsTotal,
|
|
36
|
-
NumPyIntegerType as NumPyIntegerType,
|
|
37
|
-
NumPyLeavesTotal as NumPyLeavesTotal,
|
|
38
|
-
)
|
|
39
6
|
|
|
40
|
-
|
|
7
|
+
packageNamePACKAGING_HARDCODED = "mapFolding"
|
|
8
|
+
concurrencyPackageHARDCODED = 'multiprocessing'
|
|
9
|
+
|
|
10
|
+
# Evaluate When Packaging
|
|
11
|
+
# https://github.com/hunterhogan/mapFolding/issues/18
|
|
41
12
|
try:
|
|
42
13
|
packageNamePACKAGING: str = tomli_load(Path("../pyproject.toml").open('rb'))["project"]["name"]
|
|
43
14
|
except Exception:
|
|
44
|
-
packageNamePACKAGING =
|
|
15
|
+
packageNamePACKAGING = packageNamePACKAGING_HARDCODED
|
|
45
16
|
|
|
46
|
-
# Evaluate When Installing
|
|
17
|
+
# Evaluate When Installing
|
|
18
|
+
# https://github.com/hunterhogan/mapFolding/issues/18
|
|
47
19
|
def getPathPackageINSTALLING() -> Path:
|
|
48
20
|
pathPackage: Path = Path(inspect_getfile(importlib_import_module(packageNamePACKAGING)))
|
|
49
21
|
if pathPackage.is_file():
|
|
50
22
|
pathPackage = pathPackage.parent
|
|
51
23
|
return pathPackage
|
|
52
24
|
|
|
53
|
-
# I believe these values should be dynamically determined, so I have conspicuously marked them "HARDCODED"
|
|
54
|
-
# and created downstream logic that assumes the values were dynamically determined.
|
|
55
|
-
# Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
|
|
56
|
-
logicalPathModuleDispatcherHARDCODED: str = 'mapFolding.syntheticModules.numbaCount'
|
|
57
|
-
callableDispatcherHARDCODED: str = 'doTheNeedful'
|
|
58
|
-
concurrencyPackageHARDCODED = 'multiprocessing'
|
|
59
|
-
# from mapFolding.someAssemblyRequired.toolboxNumba.theNumbaFlow
|
|
60
|
-
|
|
61
|
-
# PackageSettings in theSSOT.py and immutability https://github.com/hunterhogan/mapFolding/issues/11
|
|
62
25
|
@dataclasses.dataclass
|
|
63
26
|
class PackageSettings:
|
|
64
|
-
"""
|
|
65
|
-
Centralized configuration settings for the mapFolding package.
|
|
66
|
-
|
|
67
|
-
This class implements the Single Source of Truth (SSOT) principle for package configuration, providing a consistent
|
|
68
|
-
interface for accessing package settings, paths, and dispatch functions. The primary instance of this class, named
|
|
69
|
-
`The`, is imported and used throughout the package to retrieve configuration values.
|
|
70
|
-
"""
|
|
71
|
-
|
|
72
|
-
logicalPathModuleDispatcher: str | None = None
|
|
73
|
-
"""Logical import path to the module containing the dispatcher function."""
|
|
74
|
-
|
|
75
|
-
callableDispatcher: str | None = None
|
|
76
|
-
"""Name of the function within the dispatcher module that will be called."""
|
|
77
|
-
|
|
78
|
-
concurrencyPackage: str | None = None
|
|
79
|
-
"""Package to use for concurrent execution (e.g., 'multiprocessing', 'numba')."""
|
|
80
|
-
|
|
81
|
-
# "Evaluate When Packaging" and "Evaluate When Installing" https://github.com/hunterhogan/mapFolding/issues/18
|
|
82
|
-
dataclassIdentifier: str = dataclasses.field(default='ComputationState', metadata={'evaluateWhen': 'packaging'})
|
|
83
|
-
"""Name of the dataclass used to track computation state."""
|
|
84
|
-
|
|
85
|
-
dataclassInstance: str = dataclasses.field(default='state', metadata={'evaluateWhen': 'packaging'})
|
|
86
|
-
"""Default variable name for instances of the computation state dataclass."""
|
|
87
|
-
|
|
88
|
-
dataclassInstanceTaskDistributionSuffix: str = dataclasses.field(default='Parallel', metadata={'evaluateWhen': 'packaging'})
|
|
89
|
-
"""Suffix added to dataclassInstance for parallel task distribution."""
|
|
90
|
-
|
|
91
|
-
dataclassModule: str = dataclasses.field(default='theSSOT', metadata={'evaluateWhen': 'packaging'})
|
|
92
|
-
"""Module containing the computation state dataclass definition."""
|
|
93
|
-
|
|
94
|
-
datatypePackage: str = dataclasses.field(default='numpy', metadata={'evaluateWhen': 'packaging'})
|
|
95
|
-
"""Package providing the numeric data types used in computation."""
|
|
96
|
-
|
|
97
27
|
fileExtension: str = dataclasses.field(default='.py', metadata={'evaluateWhen': 'installing'})
|
|
98
|
-
"""Default file extension for generated code files."""
|
|
99
|
-
|
|
100
28
|
packageName: str = dataclasses.field(default = packageNamePACKAGING, metadata={'evaluateWhen': 'packaging'})
|
|
101
|
-
"""Name of this package, used for import paths and configuration."""
|
|
102
|
-
|
|
103
29
|
pathPackage: Path = dataclasses.field(default_factory=getPathPackageINSTALLING, metadata={'evaluateWhen': 'installing'})
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
sourceAlgorithm: str = dataclasses.field(default='theDao', metadata={'evaluateWhen': 'packaging'})
|
|
107
|
-
"""Module containing the reference implementation of the algorithm."""
|
|
108
|
-
|
|
109
|
-
sourceCallableDispatcher: str = dataclasses.field(default='doTheNeedful', metadata={'evaluateWhen': 'packaging'})
|
|
110
|
-
"""Name of the function that dispatches computation in the source algorithm."""
|
|
111
|
-
|
|
112
|
-
sourceCallableInitialize: str = dataclasses.field(default='countInitialize', metadata={'evaluateWhen': 'packaging'})
|
|
113
|
-
"""Name of the function that initializes computation in the source algorithm."""
|
|
114
|
-
|
|
115
|
-
sourceCallableParallel: str = dataclasses.field(default='countParallel', metadata={'evaluateWhen': 'packaging'})
|
|
116
|
-
"""Name of the function that performs parallel computation in the source algorithm."""
|
|
117
|
-
|
|
118
|
-
sourceCallableSequential: str = dataclasses.field(default='countSequential', metadata={'evaluateWhen': 'packaging'})
|
|
119
|
-
"""Name of the function that performs sequential computation in the source algorithm."""
|
|
120
|
-
|
|
121
|
-
sourceConcurrencyManagerIdentifier: str = dataclasses.field(default='submit', metadata={'evaluateWhen': 'packaging'})
|
|
122
|
-
"""Method name used to submit tasks to the concurrency manager."""
|
|
123
|
-
|
|
124
|
-
sourceConcurrencyManagerNamespace: str = dataclasses.field(default='concurrencyManager', metadata={'evaluateWhen': 'packaging'})
|
|
125
|
-
"""Variable name used for the concurrency manager instance."""
|
|
126
|
-
|
|
127
|
-
sourceConcurrencyPackage: str = dataclasses.field(default='multiprocessing', metadata={'evaluateWhen': 'packaging'})
|
|
128
|
-
"""Default package used for concurrency in the source algorithm."""
|
|
129
|
-
|
|
130
|
-
dataclassInstanceTaskDistribution: str = dataclasses.field(default=None, metadata={'evaluateWhen': 'packaging'}) # pyright: ignore[reportAssignmentType]
|
|
131
|
-
"""Variable name for the parallel distribution instance of the computation state."""
|
|
132
|
-
|
|
133
|
-
logicalPathModuleDataclass: str = dataclasses.field(default=None, metadata={'evaluateWhen': 'packaging'}) # pyright: ignore[reportAssignmentType]
|
|
134
|
-
"""Fully qualified import path to the module containing the computation state dataclass."""
|
|
135
|
-
|
|
136
|
-
logicalPathModuleSourceAlgorithm: str = dataclasses.field(default=None, metadata={'evaluateWhen': 'packaging'}) # pyright: ignore[reportAssignmentType]
|
|
137
|
-
"""Fully qualified import path to the module containing the source algorithm."""
|
|
138
|
-
|
|
139
|
-
@property
|
|
140
|
-
def dispatcher(self) -> Callable[['ComputationState'], 'ComputationState']:
|
|
141
|
-
""" _The_ callable that connects `countFolds` to the logic that does the work."""
|
|
142
|
-
logicalPath: str = self.logicalPathModuleDispatcher or self.logicalPathModuleSourceAlgorithm
|
|
143
|
-
identifier: str = self.callableDispatcher or self.sourceCallableDispatcher
|
|
144
|
-
moduleImported: ModuleType = importlib_import_module(logicalPath)
|
|
145
|
-
return getattr(moduleImported, identifier)
|
|
146
|
-
|
|
147
|
-
def __post_init__(self) -> None:
|
|
148
|
-
if self.dataclassInstanceTaskDistribution is None: # pyright: ignore[reportUnnecessaryComparison]
|
|
149
|
-
self.dataclassInstanceTaskDistribution = self.dataclassInstance + self.dataclassInstanceTaskDistributionSuffix
|
|
150
|
-
|
|
151
|
-
if self.logicalPathModuleDataclass is None: # pyright: ignore[reportUnnecessaryComparison]
|
|
152
|
-
self.logicalPathModuleDataclass = '.'.join([self.packageName, self.dataclassModule])
|
|
153
|
-
if self.logicalPathModuleSourceAlgorithm is None: # pyright: ignore[reportUnnecessaryComparison]
|
|
154
|
-
self.logicalPathModuleSourceAlgorithm = '.'.join([self.packageName, self.sourceAlgorithm])
|
|
155
|
-
|
|
156
|
-
The = PackageSettings(logicalPathModuleDispatcher=logicalPathModuleDispatcherHARDCODED, callableDispatcher=callableDispatcherHARDCODED, concurrencyPackage=concurrencyPackageHARDCODED)
|
|
157
|
-
|
|
158
|
-
@dataclasses.dataclass
|
|
159
|
-
class ComputationState:
|
|
160
|
-
"""
|
|
161
|
-
Represents the complete state of a map folding computation.
|
|
162
|
-
|
|
163
|
-
This dataclass encapsulates all the information required to compute the number of possible ways to fold a map,
|
|
164
|
-
including the map dimensions, leaf connections, computation progress, and fold counting. It serves as the central
|
|
165
|
-
data structure that flows through the entire computational algorithm.
|
|
166
|
-
|
|
167
|
-
Fields are categorized into:
|
|
168
|
-
1. Input parameters (`mapShape`, `leavesTotal`, etc.).
|
|
169
|
-
2. Core computational structures (`connectionGraph`, etc.).
|
|
170
|
-
3. Tracking variables for the folding algorithm state.
|
|
171
|
-
4. Result accumulation fields (`foldsTotal`, `groupsOfFolds`).
|
|
172
|
-
"""
|
|
173
|
-
# NOTE Python is anti-DRY, again, `DatatypeLeavesTotal` metadata needs to match the type
|
|
174
|
-
mapShape: tuple[DatatypeLeavesTotal, ...] = dataclasses.field(init=True, metadata={'elementConstructor': 'DatatypeLeavesTotal'})
|
|
175
|
-
"""Dimensions of the map to be folded, as a tuple of integers."""
|
|
176
|
-
|
|
177
|
-
leavesTotal: DatatypeLeavesTotal
|
|
178
|
-
"""Total number of leaves (unit squares) in the map, equal to the product of all dimensions."""
|
|
179
|
-
|
|
180
|
-
taskDivisions: DatatypeLeavesTotal
|
|
181
|
-
"""Number of parallel tasks to divide the computation into. Zero means sequential computation."""
|
|
182
|
-
|
|
183
|
-
concurrencyLimit: DatatypeElephino
|
|
184
|
-
"""Maximum number of concurrent processes to use during computation."""
|
|
185
|
-
|
|
186
|
-
connectionGraph: Array3D = dataclasses.field(init=False, metadata={'dtype': Array3D.__args__[1].__args__[0]}) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
|
|
187
|
-
"""3D array encoding the connections between leaves in all dimensions."""
|
|
188
|
-
|
|
189
|
-
dimensionsTotal: DatatypeLeavesTotal = dataclasses.field(init=False)
|
|
190
|
-
"""Total number of dimensions in the map shape."""
|
|
191
|
-
|
|
192
|
-
# I am using `dataclasses.field` metadata and `typeAlias.__args__[1].__args__[0]` to make the code more DRY. https://github.com/hunterhogan/mapFolding/issues/9
|
|
193
|
-
countDimensionsGapped: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
194
|
-
"""Tracks how many dimensions are gapped for each leaf."""
|
|
195
|
-
|
|
196
|
-
dimensionsUnconstrained: DatatypeLeavesTotal = dataclasses.field(default=None, init=True) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
197
|
-
"""Number of dimensions that are not constrained in the current folding state."""
|
|
198
|
-
|
|
199
|
-
gapRangeStart: Array1DElephino = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DElephino.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
200
|
-
"""Starting index for the gap range for each leaf."""
|
|
201
|
-
|
|
202
|
-
gapsWhere: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
203
|
-
"""Tracks where gaps occur in the folding pattern."""
|
|
204
|
-
|
|
205
|
-
leafAbove: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
206
|
-
"""For each leaf, stores the index of the leaf above it in the folding pattern."""
|
|
207
|
-
|
|
208
|
-
leafBelow: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
209
|
-
"""For each leaf, stores the index of the leaf below it in the folding pattern."""
|
|
210
|
-
|
|
211
|
-
foldGroups: Array1DFoldsTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DFoldsTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
212
|
-
"""Accumulator for fold groups across parallel tasks."""
|
|
213
|
-
|
|
214
|
-
foldsTotal: DatatypeFoldsTotal = DatatypeFoldsTotal(0)
|
|
215
|
-
"""The final computed total number of distinct folding patterns."""
|
|
216
|
-
|
|
217
|
-
gap1ndex: DatatypeElephino = DatatypeElephino(0)
|
|
218
|
-
"""Current index into gaps array during algorithm execution."""
|
|
219
|
-
|
|
220
|
-
gap1ndexCeiling: DatatypeElephino = DatatypeElephino(0)
|
|
221
|
-
"""Upper limit for gap index during the current algorithm phase."""
|
|
222
|
-
|
|
223
|
-
groupsOfFolds: DatatypeFoldsTotal = dataclasses.field(default=DatatypeFoldsTotal(0), metadata={'theCountingIdentifier': True})
|
|
224
|
-
"""Accumulator for the number of fold groups found during computation."""
|
|
225
|
-
|
|
226
|
-
indexDimension: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
|
|
227
|
-
"""Current dimension being processed during algorithm execution."""
|
|
228
|
-
|
|
229
|
-
indexLeaf: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
|
|
230
|
-
"""Current leaf index during iteration."""
|
|
231
|
-
|
|
232
|
-
indexMiniGap: DatatypeElephino = DatatypeElephino(0)
|
|
233
|
-
"""Index used when filtering common gaps."""
|
|
234
|
-
|
|
235
|
-
leaf1ndex: DatatypeLeavesTotal = DatatypeLeavesTotal(1)
|
|
236
|
-
"""Active leaf being processed in the folding algorithm. Starts at 1, not 0."""
|
|
237
|
-
|
|
238
|
-
leafConnectee: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
|
|
239
|
-
"""Leaf that is being connected to the active leaf."""
|
|
240
|
-
|
|
241
|
-
taskIndex: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
|
|
242
|
-
"""Index of the current parallel task when using task divisions."""
|
|
243
|
-
|
|
244
|
-
def __post_init__(self) -> None:
|
|
245
|
-
from mapFolding.beDRY import getConnectionGraph, makeDataContainer
|
|
246
|
-
self.dimensionsTotal = DatatypeLeavesTotal(len(self.mapShape))
|
|
247
|
-
leavesTotalAsInt = int(self.leavesTotal)
|
|
248
|
-
self.connectionGraph = getConnectionGraph(self.mapShape, leavesTotalAsInt, self.__dataclass_fields__['connectionGraph'].metadata['dtype'])
|
|
249
|
-
|
|
250
|
-
if self.dimensionsUnconstrained is None: self.dimensionsUnconstrained = DatatypeLeavesTotal(int(self.dimensionsTotal)) # pyright: ignore[reportUnnecessaryComparison]
|
|
251
|
-
|
|
252
|
-
if self.foldGroups is None: # pyright: ignore[reportUnnecessaryComparison]
|
|
253
|
-
self.foldGroups = makeDataContainer(max(2, int(self.taskDivisions) + 1), self.__dataclass_fields__['foldGroups'].metadata['dtype'])
|
|
254
|
-
self.foldGroups[-1] = self.leavesTotal
|
|
255
|
-
|
|
256
|
-
# Dataclasses, Default factories, and arguments in `ComputationState` https://github.com/hunterhogan/mapFolding/issues/12
|
|
257
|
-
if self.gapsWhere is None: self.gapsWhere = makeDataContainer(leavesTotalAsInt * leavesTotalAsInt + 1, self.__dataclass_fields__['gapsWhere'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
|
|
258
|
-
|
|
259
|
-
if self.countDimensionsGapped is None: self.countDimensionsGapped = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['countDimensionsGapped'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
|
|
260
|
-
if self.gapRangeStart is None: self.gapRangeStart = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['gapRangeStart'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
|
|
261
|
-
if self.leafAbove is None: self.leafAbove = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafAbove'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
|
|
262
|
-
if self.leafBelow is None: self.leafBelow = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafBelow'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
|
|
263
|
-
|
|
264
|
-
# Automatic, or not, calculation in dataclass `ComputationState` https://github.com/hunterhogan/mapFolding/issues/14
|
|
265
|
-
def getFoldsTotal(self) -> None:
|
|
266
|
-
self.foldsTotal = DatatypeFoldsTotal(self.foldGroups[0:-1].sum() * self.leavesTotal)
|
|
30
|
+
concurrencyPackage: str | None = None
|
|
31
|
+
"""Package to use for concurrent execution (e.g., 'multiprocessing', 'numba')."""
|
|
267
32
|
|
|
268
|
-
|
|
33
|
+
concurrencyPackage = concurrencyPackageHARDCODED
|
|
34
|
+
packageSettings = PackageSettings(concurrencyPackage=concurrencyPackage)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mapFolding
|
|
3
|
-
Version: 0.11.
|
|
3
|
+
Version: 0.11.3
|
|
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
|
|
@@ -33,8 +33,6 @@ Description-Content-Type: text/markdown
|
|
|
33
33
|
License-File: LICENSE
|
|
34
34
|
Requires-Dist: astToolkit
|
|
35
35
|
Requires-Dist: autoflake
|
|
36
|
-
Requires-Dist: cytoolz
|
|
37
|
-
Requires-Dist: more_itertools
|
|
38
36
|
Requires-Dist: numba_progress
|
|
39
37
|
Requires-Dist: numba
|
|
40
38
|
Requires-Dist: numpy
|
|
@@ -42,7 +40,6 @@ Requires-Dist: platformdirs
|
|
|
42
40
|
Requires-Dist: python_minifier
|
|
43
41
|
Requires-Dist: sympy
|
|
44
42
|
Requires-Dist: tomli
|
|
45
|
-
Requires-Dist: typeshed_client
|
|
46
43
|
Requires-Dist: Z0Z_tools
|
|
47
44
|
Provides-Extra: testing
|
|
48
45
|
Requires-Dist: mypy; extra == "testing"
|
|
@@ -67,12 +64,6 @@ Dynamic: license-file
|
|
|
67
64
|
- You're interested in solving mathematical puzzles through code
|
|
68
65
|
- You're learning about Numba and advanced Python optimization
|
|
69
66
|
|
|
70
|
-
**This package is NOT for you if:**
|
|
71
|
-
|
|
72
|
-
- You're looking for a general-purpose folding simulation tool
|
|
73
|
-
- You need commercial-ready mapping software
|
|
74
|
-
- You want simple visualization of folding patterns
|
|
75
|
-
|
|
76
67
|
## What Does This Package Actually Do?
|
|
77
68
|
|
|
78
69
|
`mapFolding` solves a specific mathematical problem: counting the number of distinct ways to fold a rectangular map. While this may sound niche, it's a fascinating computational challenge that demonstrates:
|
|
@@ -87,7 +78,7 @@ The package has achieved new computational records, including first-ever calcula
|
|
|
87
78
|
```python
|
|
88
79
|
# Compute the number of ways to fold a 5×5 grid:
|
|
89
80
|
from mapFolding import oeisIDfor_n
|
|
90
|
-
foldsTotal = oeisIDfor_n('A001418', 5)
|
|
81
|
+
foldsTotal = oeisIDfor_n('A001418', 5)
|
|
91
82
|
```
|
|
92
83
|
|
|
93
84
|
## Key Benefits for Computational Enthusiasts
|
|
@@ -178,4 +169,18 @@ If you've read this far and are intrigued by computational puzzles, algorithm op
|
|
|
178
169
|
|
|
179
170
|
Whether you use it to solve map folding problems or to study its optimization techniques, `mapFolding` offers a unique window into advanced Python programming approaches.
|
|
180
171
|
|
|
172
|
+
## My recovery
|
|
173
|
+
|
|
174
|
+
[](https://HunterThinks.com/support)
|
|
175
|
+
[](https://www.youtube.com/@HunterHogan)
|
|
176
|
+
|
|
177
|
+
## How to code
|
|
178
|
+
|
|
179
|
+
Coding One Step at a Time:
|
|
180
|
+
|
|
181
|
+
0. WRITE CODE.
|
|
182
|
+
1. Don't write stupid code that's hard to revise.
|
|
183
|
+
2. Write good code.
|
|
184
|
+
3. When revising, write better code.
|
|
185
|
+
|
|
181
186
|
[](https://creativecommons.org/licenses/by-nc/4.0/)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
mapFolding/__init__.py,sha256=5aecz_WZCLtBsArV68h30OEQxrylPPm0oVfQMt0N94A,1948
|
|
2
|
+
mapFolding/basecamp.py,sha256=3MMWSN8eW4W_dZe_q7Vs5yqmazNFoml0VpcJNY2feho,8243
|
|
3
|
+
mapFolding/beDRY.py,sha256=kQrgBhJkXEy-J-Zyu4RHILK6gRm72a7cON7a1LewgwI,14004
|
|
4
|
+
mapFolding/daoOfMapFolding.py,sha256=ncTIiBfTsM8SNVx9qefZ0bBcBtviWLSk4iPv3Z9nGiE,5442
|
|
5
|
+
mapFolding/dataBaskets.py,sha256=crfmmYJGeJ7QLCzuYi4rtVOtzCDRoOiTNfPfHbc6Foo,5620
|
|
6
|
+
mapFolding/datatypes.py,sha256=dqOAa2RbiGcsRl9X4qo4tdMamgOoZVnewrMjY4mHXS4,773
|
|
7
|
+
mapFolding/filesystemToolkit.py,sha256=O9VQ0tSXlrGUhU3qN7uWxOTAZfuQb3fcRkTrfRZrGXo,9854
|
|
8
|
+
mapFolding/oeis.py,sha256=ifs9Uu4uqgKCd46aHuCqhyXt2JXLMcPmmWRFuXIBACg,16926
|
|
9
|
+
mapFolding/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
mapFolding/theSSOT.py,sha256=7CyB3FnDWyNovTuUPRvSIfF7GxQc3aZoIsIwF1-4OyE,1456
|
|
11
|
+
mapFolding/reference/__init__.py,sha256=GKcSgYE49NcTISx-JZbELXyq-eRkMeTL5g4DXInWFw0,2206
|
|
12
|
+
mapFolding/reference/flattened.py,sha256=QK1xG9SllqCoi68e86Hyl9d9ATUAAFNpTQI-3zmcp5I,16072
|
|
13
|
+
mapFolding/reference/hunterNumba.py,sha256=iLfyqwGdAh6c5GbapnKsWhAsNsR3O-fyGGHAdohluLw,7258
|
|
14
|
+
mapFolding/reference/irvineJavaPort.py,sha256=UEfIX4QbPLl5jnyfYIyX5YRR3_rYvPUikK8jLehsFko,4076
|
|
15
|
+
mapFolding/reference/jaxCount.py,sha256=TuDNKOnyhQfuixKmIxO9Algv7dvy7KMGhgsV3h96FGE,14853
|
|
16
|
+
mapFolding/reference/lunnonNumpy.py,sha256=mMgrgbrBpe4nmo72ThEI-MGH0OwEHmfMPczSXHp2qKo,4357
|
|
17
|
+
mapFolding/reference/lunnonWhile.py,sha256=ZL8GAQtPs5nJZSgoDl5USrLSS_zs03y98y1Z9E4jOmQ,3799
|
|
18
|
+
mapFolding/reference/rotatedEntryPoint.py,sha256=5ughpKUT2JQhoAKgoDUdYNjgWQYPGV8v-7dWEAdDmfE,10274
|
|
19
|
+
mapFolding/reference/total_countPlus1vsPlusN.py,sha256=yJZAVLVdoXqHag2_N6_6CT-Q6HXBgRro-eny93-Rlpw,9307
|
|
20
|
+
mapFolding/reference/jobsCompleted/__init__.py,sha256=TU93ZGUW1xEkT6d9mQFn_rp5DvRy0ZslEB2Q6MF5ZDc,2596
|
|
21
|
+
mapFolding/reference/jobsCompleted/[2x19]/p2x19.py,sha256=_tvYtfzMWVo2VtUbIAieoscb4N8FFflgTdW4-ljBUuA,19626
|
|
22
|
+
mapFolding/reference/jobsCompleted/p2x19/p2x19.py,sha256=eZEw4Me4ocTt6VXoK2-Sbd5SowZtxRIbN9dZmc7OCVg,6395
|
|
23
|
+
mapFolding/someAssemblyRequired/RecipeJob.py,sha256=H53d1pIxh6kvEsh9Nmj7bD8agMBbpHziY2XrN808eGc,4432
|
|
24
|
+
mapFolding/someAssemblyRequired/Z0Z_makeAllModules.py,sha256=7REamiISEiZQ_goseQB2BJW4kMb4wRhoJnqhIRdX_zI,30262
|
|
25
|
+
mapFolding/someAssemblyRequired/__init__.py,sha256=Wq8rrFJzZsQV4oYQmDXSlDtAlU4TRgMc5o0EKJedvGE,2705
|
|
26
|
+
mapFolding/someAssemblyRequired/_toolIfThis.py,sha256=nq7brHOA4JOK9P-RWyOFae6QxzX40nV46DNDrNmkLYo,3122
|
|
27
|
+
mapFolding/someAssemblyRequired/_toolkitContainers.py,sha256=C-GkXSYoneWmcJWULPa5bgLidjjcIFbrEtEpnrK1m_Y,9784
|
|
28
|
+
mapFolding/someAssemblyRequired/getLLVMforNoReason.py,sha256=9RPU6vK_eUg64GtVFI_nZnvUryXw8gfHJs9NyDYHIvg,2745
|
|
29
|
+
mapFolding/someAssemblyRequired/infoBooth.py,sha256=IXzpt_YKkWfOOiaQaBh61SiEd-TnLRlHifmq_1mCow4,4249
|
|
30
|
+
mapFolding/someAssemblyRequired/makeJobTheorem2Numba.py,sha256=6bKLNI9po2qS1_JsKr4wnqwY1A5eL5LQN9uLu3jTlkM,14039
|
|
31
|
+
mapFolding/someAssemblyRequired/toolkitNumba.py,sha256=c-rq2rY-Vr0X4asnGy2cmmKTJonv2Jh_mw3oWHT1ctI,6649
|
|
32
|
+
mapFolding/someAssemblyRequired/transformationTools.py,sha256=ysvZaP8XqxXjHtSHlaErNDGHI5OrRRcqVhnzs3F8UUA,8256
|
|
33
|
+
mapFolding/syntheticModules/__init__.py,sha256=evVFqhCGa-WZKDiLcnQWjs-Bj34eRnfSLqz_d7dFYZY,83
|
|
34
|
+
mapFolding/syntheticModules/countParallel.py,sha256=OK_IB9w4yy9MMAiGvkei5ezPm_00v2nYjPrQZ_IlELg,7733
|
|
35
|
+
mapFolding/syntheticModules/daoOfMapFolding.py,sha256=cfWPABtXyCxJ0BwPI7rhfLh_2UYV_XKAL8lJ4GLNXaQ,5896
|
|
36
|
+
mapFolding/syntheticModules/dataPacking.py,sha256=m_eOZ7sMXIQ9jY5EvC3qgitQTY60n6rksy0ACMJOIC8,2292
|
|
37
|
+
mapFolding/syntheticModules/initializeCount.py,sha256=nWSlJMMfIM3DvZxMn6ISQusUJqRYAjKQyLF5hwLEdBQ,3119
|
|
38
|
+
mapFolding/syntheticModules/theorem2.py,sha256=9jrbZNNX4BWYZW1S0JjvRY2k7RU7I1RNUMV7JdCt1ZY,3017
|
|
39
|
+
mapFolding/syntheticModules/theorem2Numba.py,sha256=-cKjNyxgUMFhEyFVs0VJ7hw4LfrV0WSNK5tPYbQ1oNU,3369
|
|
40
|
+
mapFolding/syntheticModules/theorem2Trimmed.py,sha256=DHW3NxBdtABQYBKm2WRvfQ5kzc2_UwGI2h4ePuYEJoM,2685
|
|
41
|
+
mapfolding-0.11.3.dist-info/licenses/LICENSE,sha256=NxH5Y8BdC-gNU-WSMwim3uMbID2iNDXJz7fHtuTdXhk,19346
|
|
42
|
+
tests/__init__.py,sha256=5VhHf0JJ2_DSh58zJ0rR5UkpoCon-0IkdljspTCzZ04,1950
|
|
43
|
+
tests/conftest.py,sha256=eumQRoDuWVrhsjDxWXGhW0N7lH0ZZ9XD-5q81bWFqOs,10874
|
|
44
|
+
tests/test_computations.py,sha256=HNpfs9Yz3rdfJInD15Jwd6DYsSR5TCwnR5EW0n7KbeI,5682
|
|
45
|
+
tests/test_filesystem.py,sha256=imlcetleJc4G9pDZTgS1j8UAs7ADbRxXVuNPecJAvqc,2964
|
|
46
|
+
tests/test_oeis.py,sha256=uxvwmgbnylSDdsVJfuAT0LuYLbIVFwSgdLxHm-xUGBM,5043
|
|
47
|
+
tests/test_other.py,sha256=DT7YE82YCHrSjdxhpY4UJnXmZvDm1b4e1QpZV3LyzcM,3747
|
|
48
|
+
tests/test_tasks.py,sha256=pEDukf2SVTOMEsn82JpAWKQzn1ZCTlkhLzQ5hYLg2yY,2780
|
|
49
|
+
mapfolding-0.11.3.dist-info/METADATA,sha256=FyB0PTluboy3BsrccAJJGrEhGpo5nUsbd3JJe6CAsXo,7712
|
|
50
|
+
mapfolding-0.11.3.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
|
|
51
|
+
mapfolding-0.11.3.dist-info/entry_points.txt,sha256=F3OUeZR1XDTpoH7k3wXuRb3KF_kXTTeYhu5AGK1SiOQ,146
|
|
52
|
+
mapfolding-0.11.3.dist-info/top_level.txt,sha256=1gP2vFaqPwHujGwb3UjtMlLEGN-943VSYFR7V4gDqW8,17
|
|
53
|
+
mapfolding-0.11.3.dist-info/RECORD,,
|
tests/conftest.py
CHANGED
|
@@ -56,15 +56,10 @@ See the examples in `test_computations.py` for guidance on adapting these fixtur
|
|
|
56
56
|
"""
|
|
57
57
|
|
|
58
58
|
from collections.abc import Callable, Generator, Sequence
|
|
59
|
-
from mapFolding import
|
|
60
|
-
from mapFolding.beDRY import getLeavesTotal, validateListDimensions, makeDataContainer
|
|
59
|
+
from mapFolding import getLeavesTotal, validateListDimensions, makeDataContainer
|
|
61
60
|
from mapFolding.oeis import oeisIDsImplemented, settingsOEIS
|
|
62
|
-
from
|
|
63
|
-
from pathlib import Path, PurePosixPath
|
|
64
|
-
from types import ModuleType
|
|
61
|
+
from pathlib import Path
|
|
65
62
|
from typing import Any
|
|
66
|
-
import importlib
|
|
67
|
-
import importlib.util
|
|
68
63
|
import numpy
|
|
69
64
|
import pytest
|
|
70
65
|
import random
|
|
@@ -239,78 +234,6 @@ def oeisID_1random() -> str:
|
|
|
239
234
|
"""Return one random valid OEIS ID."""
|
|
240
235
|
return random.choice(oeisIDsImplemented)
|
|
241
236
|
|
|
242
|
-
@pytest.fixture
|
|
243
|
-
def useThisDispatcher() -> Generator[Callable[..., None], Any, None]:
|
|
244
|
-
"""A fixture providing a context manager for temporarily replacing the dispatcher.
|
|
245
|
-
|
|
246
|
-
Returns
|
|
247
|
-
A context manager for patching the dispatcher
|
|
248
|
-
"""
|
|
249
|
-
import mapFolding.theSSOT as theSSOT
|
|
250
|
-
from mapFolding.theSSOT import The
|
|
251
|
-
|
|
252
|
-
# Store original property method
|
|
253
|
-
original_dispatcher_property = theSSOT.PackageSettings.dispatcher
|
|
254
|
-
|
|
255
|
-
def patchDispatcher(callableTarget: Callable[..., Any]) -> None:
|
|
256
|
-
"""Patch the dispatcher property to return the target callable."""
|
|
257
|
-
# Create a new property that returns the target callable
|
|
258
|
-
def patched_dispatcher(self: theSSOT.PackageSettings) -> Callable[..., Any]:
|
|
259
|
-
def wrapper(state: Any) -> Any:
|
|
260
|
-
return callableTarget(state)
|
|
261
|
-
return wrapper
|
|
262
|
-
|
|
263
|
-
# Replace the property with our patched version
|
|
264
|
-
theSSOT.PackageSettings.dispatcher = property(patched_dispatcher) # type: ignore
|
|
265
|
-
|
|
266
|
-
yield patchDispatcher
|
|
267
|
-
|
|
268
|
-
# Restore the original property
|
|
269
|
-
theSSOT.PackageSettings.dispatcher = original_dispatcher_property # type: ignore
|
|
270
|
-
|
|
271
|
-
def getAlgorithmDispatcher() -> Callable[..., Any]:
|
|
272
|
-
moduleImported: ModuleType = importlib.import_module(The.logicalPathModuleSourceAlgorithm)
|
|
273
|
-
dispatcherCallable = getattr(moduleImported, The.sourceCallableDispatcher)
|
|
274
|
-
return dispatcherCallable
|
|
275
|
-
|
|
276
|
-
@pytest.fixture
|
|
277
|
-
def useAlgorithmSourceDispatcher(useThisDispatcher: Callable[..., Any]) -> Generator[None, None, None]:
|
|
278
|
-
"""Temporarily patches getDispatcherCallable to return the algorithm dispatcher."""
|
|
279
|
-
useThisDispatcher(importLogicalPath2Callable(The.logicalPathModuleSourceAlgorithm, The.sourceCallableDispatcher))
|
|
280
|
-
yield
|
|
281
|
-
|
|
282
|
-
@pytest.fixture
|
|
283
|
-
def syntheticDispatcherFixture(useThisDispatcher: Callable[..., Any], pathTmpTesting: Path) -> Callable[..., Any]:
|
|
284
|
-
"""Generate synthetic Numba-optimized dispatcher module and patch the dispatcher"""
|
|
285
|
-
from mapFolding.someAssemblyRequired.toolboxNumba import makeNumbaFlow
|
|
286
|
-
|
|
287
|
-
TESTINGrecipeFlow = RecipeSynthesizeFlow(
|
|
288
|
-
pathPackage=PurePosixPath(pathTmpTesting.absolute()),
|
|
289
|
-
logicalPathFlowRoot=None,
|
|
290
|
-
moduleDispatcher="test_dispatcher",
|
|
291
|
-
# Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
|
|
292
|
-
# dispatcherCallable="dispatcherSynthetic",
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
# Generate optimized module in test directory
|
|
296
|
-
makeNumbaFlow(TESTINGrecipeFlow)
|
|
297
|
-
|
|
298
|
-
# Import synthesized dispatcher
|
|
299
|
-
importlibSpecificationDispatcher = importlib.util.spec_from_file_location(
|
|
300
|
-
TESTINGrecipeFlow.moduleDispatcher,
|
|
301
|
-
Path(TESTINGrecipeFlow.pathFilenameDispatcher),
|
|
302
|
-
)
|
|
303
|
-
if importlibSpecificationDispatcher is None or importlibSpecificationDispatcher.loader is None:
|
|
304
|
-
raise ImportError("Failed to load synthetic dispatcher module")
|
|
305
|
-
|
|
306
|
-
moduleSpecificationDispatcher = importlib.util.module_from_spec(importlibSpecificationDispatcher)
|
|
307
|
-
importlibSpecificationDispatcher.loader.exec_module(moduleSpecificationDispatcher)
|
|
308
|
-
callableDispatcherSynthetic = getattr(moduleSpecificationDispatcher, TESTINGrecipeFlow.callableDispatcher)
|
|
309
|
-
|
|
310
|
-
# Patch dispatcher and return callable
|
|
311
|
-
useThisDispatcher(callableDispatcherSynthetic)
|
|
312
|
-
return callableDispatcherSynthetic
|
|
313
|
-
|
|
314
237
|
def uniformTestMessage(expected: Any, actual: Any, functionName: str, *arguments: Any) -> str:
|
|
315
238
|
"""Format assertion message for any test comparison."""
|
|
316
239
|
return (f"\nTesting: `{functionName}({', '.join(str(parameter) for parameter in arguments)})`\n"
|