mapFolding 0.8.0__py3-none-any.whl → 0.8.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mapFolding/__init__.py +33 -4
- mapFolding/basecamp.py +16 -2
- mapFolding/beDRY.py +40 -32
- mapFolding/filesystem.py +124 -90
- mapFolding/noHomeYet.py +12 -0
- mapFolding/oeis.py +18 -3
- mapFolding/reference/__init__.py +38 -0
- mapFolding/reference/flattened.py +66 -47
- mapFolding/reference/hunterNumba.py +28 -4
- mapFolding/reference/irvineJavaPort.py +13 -1
- mapFolding/reference/{jax.py → jaxCount.py} +46 -27
- mapFolding/reference/lunnanNumpy.py +19 -5
- mapFolding/reference/lunnanWhile.py +19 -7
- mapFolding/reference/rotatedEntryPoint.py +20 -3
- mapFolding/reference/total_countPlus1vsPlusN.py +226 -203
- mapFolding/someAssemblyRequired/__init__.py +29 -0
- mapFolding/someAssemblyRequired/getLLVMforNoReason.py +32 -14
- mapFolding/someAssemblyRequired/ingredientsNumba.py +22 -1
- mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py +193 -0
- mapFolding/someAssemblyRequired/synthesizeNumbaJobVESTIGIAL.py +3 -4
- mapFolding/someAssemblyRequired/transformDataStructures.py +168 -0
- mapFolding/someAssemblyRequired/transformationTools.py +233 -225
- mapFolding/theDao.py +19 -5
- mapFolding/theSSOT.py +89 -122
- mapfolding-0.8.2.dist-info/METADATA +187 -0
- mapfolding-0.8.2.dist-info/RECORD +39 -0
- {mapfolding-0.8.0.dist-info → mapfolding-0.8.2.dist-info}/WHEEL +1 -1
- tests/conftest.py +43 -33
- tests/test_computations.py +7 -7
- tests/test_other.py +2 -2
- mapFolding/reference/lunnan.py +0 -153
- mapFolding/someAssemblyRequired/Z0Z_workbench.py +0 -350
- mapFolding/someAssemblyRequired/synthesizeDataConverters.py +0 -117
- mapFolding/syntheticModules/numbaCountHistoricalExample.py +0 -158
- mapFolding/syntheticModules/numba_doTheNeedfulHistoricalExample.py +0 -13
- mapfolding-0.8.0.dist-info/METADATA +0 -157
- mapfolding-0.8.0.dist-info/RECORD +0 -41
- {mapfolding-0.8.0.dist-info → mapfolding-0.8.2.dist-info}/entry_points.txt +0 -0
- {mapfolding-0.8.0.dist-info → mapfolding-0.8.2.dist-info/licenses}/LICENSE +0 -0
- {mapfolding-0.8.0.dist-info → mapfolding-0.8.2.dist-info}/top_level.txt +0 -0
mapFolding/theDao.py
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
|
-
from concurrent.futures import Future as ConcurrentFuture, ProcessPoolExecutor
|
|
2
|
-
from copy import deepcopy
|
|
3
|
-
from mapFolding.theSSOT import ComputationState
|
|
4
|
-
from multiprocessing import set_start_method as multiprocessing_set_start_method
|
|
5
|
-
|
|
6
1
|
"""
|
|
2
|
+
Core computational algorithm for map folding counting and enumeration.
|
|
3
|
+
|
|
4
|
+
This module implements the core algorithms for enumerating and counting the various ways
|
|
5
|
+
a rectangular map can be folded. It uses a functional state-transformation approach, where
|
|
6
|
+
each function performs a specific state mutation and returns the updated state. The module
|
|
7
|
+
provides three main counting algorithms:
|
|
8
|
+
|
|
9
|
+
1. countInitialize: Sets up the initial state for computation
|
|
10
|
+
2. countSequential: Processes the folding computation sequentially
|
|
11
|
+
3. countParallel: Distributes the computation across multiple processes
|
|
12
|
+
|
|
13
|
+
All algorithms operate on a ComputationState object that tracks the folding process, including:
|
|
7
14
|
- A "leaf" is a unit square in the map
|
|
8
15
|
- A "gap" is a potential position where a new leaf can be folded
|
|
9
16
|
- Connections track how leaves can connect above/below each other
|
|
10
17
|
- Leaves are enumerated starting from 1, not 0; hence, leaf1ndex not leafIndex
|
|
18
|
+
|
|
19
|
+
The doTheNeedful function is the main entry point that orchestrates the computation strategy
|
|
20
|
+
based on task divisions and concurrency parameters.
|
|
11
21
|
"""
|
|
22
|
+
from concurrent.futures import Future as ConcurrentFuture, ProcessPoolExecutor
|
|
23
|
+
from copy import deepcopy
|
|
24
|
+
from mapFolding.theSSOT import ComputationState
|
|
25
|
+
from multiprocessing import set_start_method as multiprocessing_set_start_method
|
|
12
26
|
|
|
13
27
|
# When to use multiprocessing.set_start_method https://github.com/hunterhogan/mapFolding/issues/6
|
|
14
28
|
if __name__ == '__main__':
|
mapFolding/theSSOT.py
CHANGED
|
@@ -1,114 +1,103 @@
|
|
|
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
|
|
5
|
+
used throughout the mapFolding package. It implements the Single Source of Truth (SSOT)
|
|
6
|
+
principle to ensure consistency across the package's 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
|
|
16
|
+
to avoid namespace collisions when transforming algorithms.
|
|
17
|
+
"""
|
|
18
|
+
|
|
1
19
|
from collections.abc import Callable
|
|
2
20
|
from importlib import import_module as importlib_import_module
|
|
3
21
|
from inspect import getfile as inspect_getfile
|
|
4
|
-
from numpy import dtype, int64 as numpy_int64, int16 as numpy_int16, ndarray
|
|
22
|
+
from numpy import dtype, int64 as numpy_int64, int16 as numpy_int16, ndarray
|
|
5
23
|
from pathlib import Path
|
|
6
24
|
from sys import modules as sysModules
|
|
7
25
|
from tomli import load as tomli_load
|
|
8
26
|
from types import ModuleType
|
|
9
|
-
from typing import
|
|
27
|
+
from typing import TypeAlias
|
|
10
28
|
import dataclasses
|
|
11
29
|
|
|
12
|
-
"""
|
|
13
|
-
2025 March 11
|
|
14
|
-
Note to self: fundamental concept in Python:
|
|
15
|
-
Identifiers: scope and resolution, LEGB (Local, Enclosing, Global, Builtin)
|
|
16
|
-
- Local: Inside the function
|
|
17
|
-
- Enclosing: Inside enclosing functions
|
|
18
|
-
- Global: At the uppermost level
|
|
19
|
-
- Builtin: Python's built-in names
|
|
20
|
-
"""
|
|
21
|
-
|
|
22
|
-
# I _think_, in theSSOT, I have abstracted the flow settings to only these couple of lines:
|
|
23
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:
|
|
24
32
|
packageFlowSynthetic = 'numba'
|
|
25
33
|
# Z0Z_packageFlow = 'algorithm'
|
|
26
34
|
Z0Z_packageFlow = packageFlowSynthetic
|
|
27
35
|
Z0Z_concurrencyPackage = 'multiprocessing'
|
|
28
|
-
# =============================================================================
|
|
29
|
-
# The Wrong Way The Wrong Way The Wrong Way The Wrong Way The Wrong Way
|
|
30
|
-
# Evaluate When Packaging Evaluate When Packaging Evaluate When Packaging
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
dispatcherCallablePACKAGING: str = 'doTheNeedful'
|
|
35
|
-
moduleOfSyntheticModulesPACKAGING: Final[str] = 'syntheticModules'
|
|
36
|
-
|
|
37
|
-
dataclassModulePACKAGING: str = 'theSSOT'
|
|
38
|
-
dataclassIdentifierPACKAGING: str = 'ComputationState'
|
|
39
|
-
dataclassInstancePACKAGING: str = 'state'
|
|
40
|
-
dataclassInstanceTaskDistributionPACKAGING = dataclassInstancePACKAGING + 'Parallel'
|
|
41
|
-
|
|
42
|
-
sourceInitializeCallablePACKAGING = 'countInitialize'
|
|
43
|
-
sourceSequentialCallablePACKAGING = 'countSequential'
|
|
44
|
-
sourceParallelCallablePACKAGING = 'countParallel'
|
|
37
|
+
# =============================================================================
|
|
38
|
+
# The Wrong Way: Evaluate When Packaging
|
|
45
39
|
|
|
46
40
|
try:
|
|
47
|
-
|
|
41
|
+
packageNamePACKAGING: str = tomli_load(Path("../pyproject.toml").open('rb'))["project"]["name"]
|
|
48
42
|
except Exception:
|
|
49
|
-
|
|
43
|
+
packageNamePACKAGING = "mapFolding"
|
|
50
44
|
|
|
51
|
-
#
|
|
52
|
-
# The Wrong Way The Wrong Way The Wrong Way The Wrong Way The Wrong Way
|
|
53
|
-
# Evaluate When Installing Evaluate When Installing Evaluate When Installing
|
|
54
|
-
|
|
55
|
-
fileExtensionINSTALLING: str = '.py'
|
|
45
|
+
# The Wrong Way: Evaluate When Installing
|
|
56
46
|
|
|
57
47
|
def getPathPackageINSTALLING() -> Path:
|
|
58
|
-
pathPackage: Path = Path(inspect_getfile(importlib_import_module(
|
|
48
|
+
pathPackage: Path = Path(inspect_getfile(importlib_import_module(packageNamePACKAGING)))
|
|
59
49
|
if pathPackage.is_file():
|
|
60
50
|
pathPackage = pathPackage.parent
|
|
61
51
|
return pathPackage
|
|
62
52
|
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
53
|
+
# The following is an improvement, but it is not the full solution.
|
|
54
|
+
# I hope that the standardized markers, `metadata={'evaluateWhen': 'packaging'}` will help to automate
|
|
55
|
+
# whatever needs to happen so that the following is well implemented.
|
|
56
|
+
@dataclasses.dataclass(frozen=True)
|
|
57
|
+
class PackageSettings:
|
|
58
|
+
concurrencyPackage = Z0Z_concurrencyPackage
|
|
59
|
+
dataclassIdentifier: str = dataclasses.field(default='ComputationState', metadata={'evaluateWhen': 'packaging'})
|
|
60
|
+
dataclassInstance: str = dataclasses.field(default='state', metadata={'evaluateWhen': 'packaging'})
|
|
61
|
+
dataclassInstanceTaskDistributionSuffix: str = dataclasses.field(default='Parallel', metadata={'evaluateWhen': 'packaging'})
|
|
62
|
+
dataclassModule: str = dataclasses.field(default='theSSOT', metadata={'evaluateWhen': 'packaging'})
|
|
63
|
+
datatypePackage: str = dataclasses.field(default='numpy', metadata={'evaluateWhen': 'packaging'})
|
|
64
|
+
dispatcherCallable: str = dataclasses.field(default='doTheNeedful', metadata={'evaluateWhen': 'packaging'})
|
|
65
|
+
fileExtension: str = dataclasses.field(default='.py', metadata={'evaluateWhen': 'installing'})
|
|
66
|
+
moduleOfSyntheticModules: str = dataclasses.field(default='syntheticModules', metadata={'evaluateWhen': 'packaging'})
|
|
67
|
+
packageName: str = dataclasses.field(default = packageNamePACKAGING, metadata={'evaluateWhen': 'packaging'})
|
|
68
|
+
pathPackage: Path = dataclasses.field(default_factory=getPathPackageINSTALLING, init=False, metadata={'evaluateWhen': 'installing'})
|
|
69
|
+
sourceAlgorithm: str = dataclasses.field(default='theDao', metadata={'evaluateWhen': 'packaging'})
|
|
70
|
+
sourceConcurrencyManagerIdentifier: str = dataclasses.field(default='submit', metadata={'evaluateWhen': 'packaging'})
|
|
71
|
+
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'})
|
|
75
|
+
|
|
76
|
+
@property # These are not fields, and that annoys me.
|
|
77
|
+
def dataclassInstanceTaskDistribution(self) -> str:
|
|
78
|
+
""" Compute the task distribution identifier by concatenating dataclassInstance and dataclassInstanceTaskDistributionSuffix. """
|
|
79
|
+
# it follows that `metadata={'evaluateWhen': 'packaging'}`
|
|
80
|
+
return self.dataclassInstance + self.dataclassInstanceTaskDistributionSuffix
|
|
81
|
+
|
|
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
|
+
@property # These are not fields, and that annoys me.
|
|
89
|
+
def logicalPathModuleDataclass(self) -> str:
|
|
90
|
+
""" Compute the logical path module for the dataclass by joining packageName and dataclassModule. """
|
|
91
|
+
# it follows that `metadata={'evaluateWhen': 'packaging'}`
|
|
92
|
+
return '.'.join([self.packageName, self.dataclassModule])
|
|
93
|
+
|
|
94
|
+
The = PackageSettings()
|
|
95
95
|
|
|
96
96
|
# =============================================================================
|
|
97
|
-
|
|
98
|
-
# Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
|
|
99
|
-
concurrencyPackage: str = Z0Z_packageFlow
|
|
100
|
-
concurrencyPackage = Z0Z_concurrencyPackage
|
|
101
|
-
|
|
102
|
-
# =============================================================================
|
|
103
|
-
# The relatively flexible type system needs a different paradigm, but I don't
|
|
104
|
-
# know what it should be. The system needs to 1) help optimize computation, 2)
|
|
105
|
-
# make it possible to change the basic type of the package (e.g., from numpy
|
|
106
|
-
# to superTypePy), 3) make it possible to synthesize the optimized flow of used
|
|
107
|
-
# by the package, and 4) make it possible to synthesize arbitrary modules with
|
|
108
|
-
# different type systems.
|
|
97
|
+
# Flexible Data Structure System Needs Enhanced Paradigm https://github.com/hunterhogan/mapFolding/issues/9
|
|
109
98
|
|
|
110
99
|
DatatypeLeavesTotal: TypeAlias = int
|
|
111
|
-
# this would be uint8, but mapShape (2,2,2,2, 2,2,2,2) has 256 leaves, so generic containers accommodate
|
|
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
|
|
112
101
|
numpyLeavesTotal: TypeAlias = numpy_int16
|
|
113
102
|
|
|
114
103
|
DatatypeElephino: TypeAlias = int
|
|
@@ -116,18 +105,12 @@ numpyElephino: TypeAlias = numpy_int16
|
|
|
116
105
|
|
|
117
106
|
DatatypeFoldsTotal: TypeAlias = int
|
|
118
107
|
numpyFoldsTotal: TypeAlias = numpy_int64
|
|
119
|
-
numpyDtypeDefault = numpyFoldsTotal
|
|
120
108
|
|
|
121
109
|
Array3D: TypeAlias = ndarray[tuple[int, int, int], dtype[numpyLeavesTotal]]
|
|
122
110
|
Array1DLeavesTotal: TypeAlias = ndarray[tuple[int], dtype[numpyLeavesTotal]]
|
|
123
111
|
Array1DElephino: TypeAlias = ndarray[tuple[int], dtype[numpyElephino]]
|
|
124
112
|
Array1DFoldsTotal: TypeAlias = ndarray[tuple[int], dtype[numpyFoldsTotal]]
|
|
125
113
|
|
|
126
|
-
# =============================================================================
|
|
127
|
-
# The right way.
|
|
128
|
-
# (The dataclass, not the typing of the dataclass.)
|
|
129
|
-
# (Also, my noobplementation of the dataclass certainly needs improvement.)
|
|
130
|
-
|
|
131
114
|
@dataclasses.dataclass
|
|
132
115
|
class ComputationState:
|
|
133
116
|
mapShape: tuple[DatatypeLeavesTotal, ...]
|
|
@@ -149,7 +132,7 @@ class ComputationState:
|
|
|
149
132
|
foldsTotal: DatatypeFoldsTotal = DatatypeFoldsTotal(0)
|
|
150
133
|
gap1ndex: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
|
|
151
134
|
gap1ndexCeiling: DatatypeElephino = DatatypeElephino(0)
|
|
152
|
-
groupsOfFolds: DatatypeFoldsTotal = DatatypeFoldsTotal(0)
|
|
135
|
+
groupsOfFolds: DatatypeFoldsTotal = dataclasses.field(default=DatatypeFoldsTotal(0), metadata={'theCountingIdentifier': True})
|
|
153
136
|
indexDimension: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
|
|
154
137
|
indexLeaf: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
|
|
155
138
|
indexMiniGap: DatatypeElephino = DatatypeElephino(0)
|
|
@@ -173,9 +156,9 @@ class ComputationState:
|
|
|
173
156
|
leavesTotalAsInt = int(self.leavesTotal)
|
|
174
157
|
|
|
175
158
|
if self.countDimensionsGapped is None:
|
|
176
|
-
self.countDimensionsGapped = makeDataContainer(leavesTotalAsInt + 1,
|
|
159
|
+
self.countDimensionsGapped = makeDataContainer(leavesTotalAsInt + 1, numpyLeavesTotal)
|
|
177
160
|
if self.gapRangeStart is None:
|
|
178
|
-
self.gapRangeStart = makeDataContainer(leavesTotalAsInt + 1,
|
|
161
|
+
self.gapRangeStart = makeDataContainer(leavesTotalAsInt + 1, numpyElephino)
|
|
179
162
|
if self.gapsWhere is None:
|
|
180
163
|
self.gapsWhere = makeDataContainer(leavesTotalAsInt * leavesTotalAsInt + 1, numpyLeavesTotal)
|
|
181
164
|
if self.leafAbove is None:
|
|
@@ -187,75 +170,50 @@ class ComputationState:
|
|
|
187
170
|
self.foldsTotal = DatatypeFoldsTotal(self.foldGroups[0:-1].sum() * self.leavesTotal)
|
|
188
171
|
|
|
189
172
|
# =============================================================================
|
|
190
|
-
# The most right way I know how to implement.
|
|
191
|
-
|
|
192
|
-
theLogicalPathModuleSourceAlgorithm: str = '.'.join([thePackageName, theSourceAlgorithm])
|
|
193
|
-
theLogicalPathModuleDispatcher: str = theLogicalPathModuleSourceAlgorithm
|
|
194
|
-
theLogicalPathModuleDataclass: str = '.'.join([thePackageName, theDataclassModule])
|
|
195
|
-
|
|
196
|
-
def getSourceAlgorithm() -> ModuleType:
|
|
197
|
-
moduleImported: ModuleType = importlib_import_module(theLogicalPathModuleSourceAlgorithm)
|
|
198
|
-
return moduleImported
|
|
199
|
-
|
|
200
|
-
def getAlgorithmDispatcher() -> Callable[[ComputationState], ComputationState]:
|
|
201
|
-
moduleImported: ModuleType = getSourceAlgorithm()
|
|
202
|
-
dispatcherCallable = getattr(moduleImported, theDispatcherCallable)
|
|
203
|
-
return dispatcherCallable
|
|
204
|
-
|
|
205
|
-
def getPathSyntheticModules() -> Path:
|
|
206
|
-
return thePathPackage / theModuleOfSyntheticModules
|
|
207
173
|
|
|
208
174
|
# TODO learn how to see this from the user's perspective
|
|
209
175
|
def getPathJobRootDEFAULT() -> Path:
|
|
210
176
|
if 'google.colab' in sysModules:
|
|
211
177
|
pathJobDEFAULT: Path = Path("/content/drive/MyDrive") / "jobs"
|
|
212
178
|
else:
|
|
213
|
-
pathJobDEFAULT =
|
|
179
|
+
pathJobDEFAULT = The.pathPackage / "jobs"
|
|
214
180
|
return pathJobDEFAULT
|
|
215
181
|
|
|
216
182
|
_datatypePackage: str = ''
|
|
217
183
|
def getDatatypePackage() -> str:
|
|
218
184
|
global _datatypePackage
|
|
219
185
|
if not _datatypePackage:
|
|
220
|
-
_datatypePackage =
|
|
186
|
+
_datatypePackage = The.datatypePackage
|
|
221
187
|
return _datatypePackage
|
|
222
188
|
|
|
223
|
-
def getNumpyDtypeDefault() -> type[signedinteger[Any]]:
|
|
224
|
-
return numpyDtypeDefault
|
|
225
|
-
|
|
226
189
|
# =============================================================================
|
|
227
190
|
# The coping way.
|
|
228
191
|
|
|
229
192
|
class raiseIfNoneGitHubIssueNumber3(Exception): pass
|
|
230
193
|
|
|
231
194
|
# =============================================================================
|
|
232
|
-
# Temporary or transient or something; probably still the wrong way
|
|
233
|
-
|
|
234
195
|
# THIS IS A STUPID SYSTEM BUT I CAN'T FIGURE OUT AN IMPROVEMENT
|
|
235
196
|
# NOTE This section for _default_ values probably has value
|
|
236
197
|
# https://github.com/hunterhogan/mapFolding/issues/4
|
|
237
198
|
theFormatStrModuleSynthetic = "{packageFlow}Count"
|
|
238
199
|
theFormatStrModuleForCallableSynthetic = theFormatStrModuleSynthetic + "_{callableTarget}"
|
|
239
200
|
|
|
240
|
-
|
|
241
|
-
theLogicalPathModuleDispatcherSynthetic: str = '.'.join([thePackageName, theModuleOfSyntheticModules, theModuleDispatcherSynthetic])
|
|
201
|
+
theLogicalPathModuleDispatcher: str = The.logicalPathModuleSourceAlgorithm
|
|
242
202
|
|
|
243
|
-
|
|
244
|
-
|
|
203
|
+
theModuleDispatcherSynthetic: str = theFormatStrModuleForCallableSynthetic.format(packageFlow=packageFlowSynthetic, callableTarget=The.dispatcherCallable)
|
|
204
|
+
theLogicalPathModuleDispatcherSynthetic: str = '.'.join([The.packageName, The.moduleOfSyntheticModules, theModuleDispatcherSynthetic])
|
|
245
205
|
|
|
246
|
-
# https://github.com/hunterhogan/mapFolding/issues/4
|
|
247
206
|
if Z0Z_packageFlow == packageFlowSynthetic: # pyright: ignore [reportUnnecessaryComparison]
|
|
248
207
|
# NOTE this as a default value _might_ have value
|
|
249
208
|
theLogicalPathModuleDispatcher = theLogicalPathModuleDispatcherSynthetic
|
|
250
209
|
|
|
251
|
-
# https://github.com/hunterhogan/mapFolding/issues/4
|
|
252
210
|
# dynamically set the return type https://github.com/hunterhogan/mapFolding/issues/5
|
|
253
211
|
def getPackageDispatcher() -> Callable[[ComputationState], ComputationState]:
|
|
254
212
|
# NOTE but this part, if the package flow is synthetic, probably needs to be delegated
|
|
255
213
|
# to the authority for creating _that_ synthetic flow.
|
|
256
214
|
|
|
257
215
|
moduleImported: ModuleType = importlib_import_module(theLogicalPathModuleDispatcher)
|
|
258
|
-
dispatcherCallable = getattr(moduleImported,
|
|
216
|
+
dispatcherCallable = getattr(moduleImported, The.dispatcherCallable)
|
|
259
217
|
return dispatcherCallable
|
|
260
218
|
|
|
261
219
|
"""Technical concepts I am likely using and likely want to use more effectively:
|
|
@@ -270,4 +228,13 @@ theSSOT and yourSSOT
|
|
|
270
228
|
----
|
|
271
229
|
delay realization/instantiation until a concrete value is desired
|
|
272
230
|
moment of truth: when the value is needed, not when the value is defined
|
|
231
|
+
|
|
232
|
+
----
|
|
233
|
+
2025 March 11
|
|
234
|
+
Note to self: fundamental concept in Python:
|
|
235
|
+
Identifiers: scope and resolution, LEGB (Local, Enclosing, Global, Builtin)
|
|
236
|
+
- Local: Inside the function
|
|
237
|
+
- Enclosing: Inside enclosing functions
|
|
238
|
+
- Global: At the uppermost level
|
|
239
|
+
- Builtin: Python's built-in names
|
|
273
240
|
"""
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mapFolding
|
|
3
|
+
Version: 0.8.2
|
|
4
|
+
Summary: Map folding algorithm with code transformation framework for optimizing numerical computations
|
|
5
|
+
Author-email: Hunter Hogan <HunterHogan@pm.me>
|
|
6
|
+
License: CC-BY-NC-4.0
|
|
7
|
+
Project-URL: Donate, https://www.patreon.com/integrated
|
|
8
|
+
Project-URL: Homepage, https://github.com/hunterhogan/mapFolding
|
|
9
|
+
Project-URL: Repository, https://github.com/hunterhogan/mapFolding.git
|
|
10
|
+
Keywords: A001415,A001416,A001417,A001418,A195646,algorithmic optimization,AST manipulation,code generation,code transformation,combinatorics,computational geometry,dataclass transformation,folding pattern enumeration,just-in-time compilation,map folding,Numba optimization,OEIS,performance optimization,source code analysis,stamp folding
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Education
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: Natural Language :: English
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
26
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
27
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
28
|
+
Classifier: Topic :: Software Development :: Compilers
|
|
29
|
+
Classifier: Typing :: Typed
|
|
30
|
+
Requires-Python: >=3.10
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
License-File: LICENSE
|
|
33
|
+
Requires-Dist: autoflake
|
|
34
|
+
Requires-Dist: more_itertools
|
|
35
|
+
Requires-Dist: numba_progress
|
|
36
|
+
Requires-Dist: numba
|
|
37
|
+
Requires-Dist: numpy
|
|
38
|
+
Requires-Dist: python_minifier
|
|
39
|
+
Requires-Dist: tomli
|
|
40
|
+
Requires-Dist: Z0Z_tools
|
|
41
|
+
Provides-Extra: testing
|
|
42
|
+
Requires-Dist: mypy; extra == "testing"
|
|
43
|
+
Requires-Dist: pytest; extra == "testing"
|
|
44
|
+
Requires-Dist: pytest-cov; extra == "testing"
|
|
45
|
+
Requires-Dist: pytest-env; extra == "testing"
|
|
46
|
+
Requires-Dist: pytest-xdist; extra == "testing"
|
|
47
|
+
Requires-Dist: pyupgrade; extra == "testing"
|
|
48
|
+
Dynamic: license-file
|
|
49
|
+
|
|
50
|
+
# mapFolding: Algorithms for enumerating distinct map/stamp folding patterns 🗺️
|
|
51
|
+
|
|
52
|
+
[](https://pypi.org/project/mapFolding/)
|
|
53
|
+
[](https://youtu.be/g6f_miE91mk&t=4)
|
|
54
|
+
[](https://github.com/hunterhogan/mapFolding/actions/workflows/pythonTests.yml)
|
|
55
|
+

|
|
56
|
+
[](https://creativecommons.org/licenses/by-nc/4.0/)
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Quick start
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
pip install mapFolding
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`OEIS_for_n` will run a computation from the command line.
|
|
67
|
+
|
|
68
|
+
```cmd
|
|
69
|
+
(mapFolding) C:\apps\mapFolding> OEIS_for_n A001418 5
|
|
70
|
+
186086600 distinct folding patterns.
|
|
71
|
+
Time elapsed: 1.605 seconds
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Use `mapFolding.oeisIDfor_n()` to compute a(n) for an OEIS ID.
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from mapFolding import oeisIDfor_n
|
|
78
|
+
foldsTotal = oeisIDfor_n( 'A001418', 4 )
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Features
|
|
84
|
+
|
|
85
|
+
### 1. Simple, easy usage based on OEIS IDs
|
|
86
|
+
|
|
87
|
+
`mapFolding` directly implements some IDs from [_The On-Line Encyclopedia of Integer Sequences_](https://oeis.org/) ([BibTex](https://github.com/hunterhogan/mapFolding/blob/main/citations/oeis.bibtex) citation).
|
|
88
|
+
|
|
89
|
+
Use `getOEISids` to get the most up-to-date list of available OEIS IDs.
|
|
90
|
+
|
|
91
|
+
```cmd
|
|
92
|
+
(mapFolding) C:\apps\mapFolding> getOEISids
|
|
93
|
+
|
|
94
|
+
Available OEIS sequences:
|
|
95
|
+
A001415: Number of ways of folding a 2 X n strip of stamps.
|
|
96
|
+
A001416: Number of ways of folding a 3 X n strip of stamps.
|
|
97
|
+
A001417: Number of ways of folding a 2 X 2 X ... X 2 n-dimensional map.
|
|
98
|
+
A001418: Number of ways of folding an n X n sheet of stamps.
|
|
99
|
+
A195646: Number of ways of folding a 3 X 3 X ... X 3 n-dimensional map.
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 2. **Algorithm Zoo: A Historical and Performance Journey** 🦒
|
|
103
|
+
|
|
104
|
+
This package offers a comprehensive collection of map folding algorithm implementations that showcase its evolution from historical origins to high-performance computation:
|
|
105
|
+
|
|
106
|
+
- **Historical Implementations**:
|
|
107
|
+
- Carefully restored versions of Lunnon's 1971 original [algorithm](https://github.com/hunterhogan/mapFolding/blob/mapFolding/reference/foldings.txt) with corrections
|
|
108
|
+
- Atlas Autocode reconstruction in the `reference/foldings.AA` file
|
|
109
|
+
|
|
110
|
+
- **Direct Translations**:
|
|
111
|
+
- Python translations following the original control flow (`lunnanWhile.py`)
|
|
112
|
+
- NumPy-based vectorized implementations (`lunnanNumpy.py`)
|
|
113
|
+
|
|
114
|
+
- **Modern Implementations**:
|
|
115
|
+
- Java port adaptations (`irvineJavaPort.py`) providing cleaner procedural implementations
|
|
116
|
+
- Experimental JAX version (`jaxCount.py`) exploring GPU acceleration potential
|
|
117
|
+
- Semantically decomposed version (`flattened.py`) with clear function boundaries
|
|
118
|
+
|
|
119
|
+
- **Performance Optimized**:
|
|
120
|
+
- Numba-JIT accelerated implementations up to 1000× faster than pure Python (see [benchmarks](https://github.com/hunterhogan/mapFolding/blob/mapFolding/notes/Speed%20highlights.md))
|
|
121
|
+
- Algorithmic optimizations showcasing subtle yet powerful performance differences (`total_countPlus1vsPlusN.py`)
|
|
122
|
+
|
|
123
|
+
The `reference` directory serves as both a historical archive and an educational resource for understanding algorithm evolution.
|
|
124
|
+
|
|
125
|
+
### 3. **Algorithmic Transformation: From Readability to Speed** 🔬
|
|
126
|
+
|
|
127
|
+
The package provides a sophisticated transformation framework that bridges the gap between human-readable algorithms and high-performance computation:
|
|
128
|
+
|
|
129
|
+
- **Core Algorithm Understanding**:
|
|
130
|
+
- Study the functional state-transformation approach in `theDao.py` with clear, isolated functions
|
|
131
|
+
- Explore the semantic decomposition in `reference/flattened.py` to understand algorithm sections
|
|
132
|
+
|
|
133
|
+
- **Code Transformation Pipeline**:
|
|
134
|
+
- **AST Manipulation**: Analyzes and transforms the algorithm's abstract syntax tree
|
|
135
|
+
- **Dataclass "Shattering"**: Decomposes complex state objects into primitive components
|
|
136
|
+
- **Optimization Applications**: Applies domain-specific optimizations for numerical computation
|
|
137
|
+
- **LLVM Integration**: Extracts LLVM IR for low-level algorithmic analysis
|
|
138
|
+
|
|
139
|
+
- **Performance Breakthroughs**:
|
|
140
|
+
- Learn why nearly identical algorithms can have dramatically different performance (`total_countPlus1vsPlusN.py`)
|
|
141
|
+
- See how memory layout and increment strategy impact computation speed
|
|
142
|
+
- Understand the batching technique that yields order-of-magnitude improvements
|
|
143
|
+
|
|
144
|
+
### 4. **Multi-Level Architecture: From Simple API to Full Customization**
|
|
145
|
+
|
|
146
|
+
The package's architecture supports multiple levels of engagement:
|
|
147
|
+
|
|
148
|
+
- **Basic Usage**:
|
|
149
|
+
- Work with the high-level API in `basecamp.py` for standard computations
|
|
150
|
+
- Access OEIS sequence calculations with minimal code
|
|
151
|
+
|
|
152
|
+
- **Algorithm Exploration**:
|
|
153
|
+
- Compare different implementations in the `reference` directory to understand trade-offs
|
|
154
|
+
- Modify the core algorithm in `theDao.py` while preserving its functional approach
|
|
155
|
+
- Configure system-wide settings in `theSSOT.py` to adjust data types and performance characteristics
|
|
156
|
+
|
|
157
|
+
- **Advanced Transformation**:
|
|
158
|
+
- Use the `someAssemblyRequired` package to transform algorithms at the AST level
|
|
159
|
+
- Create optimized variants with different compilation settings using:
|
|
160
|
+
- `transformationTools.py` for AST manipulation
|
|
161
|
+
- `transformDataStructures.py` for complex data structure transformations
|
|
162
|
+
- `ingredientsNumba.py` for Numba-specific optimization profiles
|
|
163
|
+
- `synthesizeNumbaFlow.py` to orchestrate the transformation process
|
|
164
|
+
|
|
165
|
+
- **Custom Deployment**:
|
|
166
|
+
- Generate specialized implementations for specific dimensions
|
|
167
|
+
- Create optimized standalone modules for production use
|
|
168
|
+
- Extract LLVM IR for further analysis and optimization
|
|
169
|
+
|
|
170
|
+
The package's multi-level design allows you to start with simple API calls and progressively explore deeper optimization techniques as your computational needs grow.
|
|
171
|
+
|
|
172
|
+
## Map-folding Video
|
|
173
|
+
|
|
174
|
+
~~This caused my neurosis:~~ I enjoyed the following video, which is what introduced me to map folding.
|
|
175
|
+
|
|
176
|
+
"How Many Ways Can You Fold a Map?" by Physics for the Birds, 2024 November 13 ([BibTex](https://github.com/hunterhogan/mapFolding/blob/main/citations/Physics_for_the_Birds.bibtex) citation)
|
|
177
|
+
|
|
178
|
+
[](https://www.youtube.com/watch?v=sfH9uIY3ln4)
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## My recovery
|
|
183
|
+
|
|
184
|
+
[](https://HunterThinks.com/support)
|
|
185
|
+
[](https://www.youtube.com/@HunterHogan)
|
|
186
|
+
|
|
187
|
+
[](https://creativecommons.org/licenses/by-nc/4.0/)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
mapFolding/__init__.py,sha256=hYxPUBU6A1_XCbKEseSDamooTsb1mzN_XHqaRLPvpGk,1701
|
|
2
|
+
mapFolding/basecamp.py,sha256=uPwbb_fi8zqqBbVjb355qanSNUqqJ9aefcf_nrvA7qI,4510
|
|
3
|
+
mapFolding/beDRY.py,sha256=UhH52BryHQNRjphf_PirtMkV45rhdemdC9PmnpACq7I,9397
|
|
4
|
+
mapFolding/filesystem.py,sha256=-pYpWugd0p3TrAz7xf9YIJW-pn1X-iRCGtJgEAF9Rns,5923
|
|
5
|
+
mapFolding/noHomeYet.py,sha256=UKZeWlyn0SKlF9dhYoud7E6gWXpiSEekZOOoJp88WeI,1362
|
|
6
|
+
mapFolding/oeis.py,sha256=TbY8KtAGbQlT6eEsa_7HVMF7bMLN-aBFKclyTMHfqHk,12615
|
|
7
|
+
mapFolding/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
mapFolding/theDao.py,sha256=Blzm5j24x1BE2nvgXjdzHEeuc2na6kAH9b_eP6PcwlI,9836
|
|
9
|
+
mapFolding/theSSOT.py,sha256=HIU9UVKP0dXlxIO791z_tl1R7r5LiEcKnA5AI7H9SmU,12414
|
|
10
|
+
mapFolding/reference/__init__.py,sha256=7NYyWXyYYSdia2Q8SFJTcD0KvoUBLvvfuvTsaqcvchM,1777
|
|
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/someAssemblyRequired/__init__.py,sha256=xA5a-nZjXIwcqEOig5PEZSxde4_m3JJ5Pb0CN4aiRjw,2488
|
|
20
|
+
mapFolding/someAssemblyRequired/getLLVMforNoReason.py,sha256=bGI8RZY-RnyR9TNF0r0OXwA6fm4TYH2cHy7WzhsnddQ,1895
|
|
21
|
+
mapFolding/someAssemblyRequired/ingredientsNumba.py,sha256=g6Z7t35NpoDskzm0OLwTQhHw5CYiYktVYxI2NhCQHww,8435
|
|
22
|
+
mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py,sha256=0179DMPoGJGP6AHs-nIac_aSfVBgpDyuaeCBVKlPRe8,10691
|
|
23
|
+
mapFolding/someAssemblyRequired/synthesizeNumbaJobVESTIGIAL.py,sha256=RBSrtr7US2P7mkY-EA-b2WIOxjs2b0WJaCln1ERxOcI,22314
|
|
24
|
+
mapFolding/someAssemblyRequired/transformDataStructures.py,sha256=Uth-WLzCTJTQkU15lsrBGf1ld7nqgLK42DUYM6-Q-n8,8605
|
|
25
|
+
mapFolding/someAssemblyRequired/transformationTools.py,sha256=n_lH9B7E871htRRRVJIDqGuT4WTmGiIgBb3ZjsY7ZjA,40689
|
|
26
|
+
mapFolding/syntheticModules/numbaCount_doTheNeedful.py,sha256=52RuwJVH2fROvWU2dT8wYcQvLgRuvkNZPq01kujCC_U,15725
|
|
27
|
+
mapfolding-0.8.2.dist-info/licenses/LICENSE,sha256=NxH5Y8BdC-gNU-WSMwim3uMbID2iNDXJz7fHtuTdXhk,19346
|
|
28
|
+
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
|
+
tests/conftest.py,sha256=hTpUTW7MtYGP5aeQnnoZMKgTEGCKdLJ8Fnmnv9d4NJw,11115
|
|
30
|
+
tests/test_computations.py,sha256=RHHByyuC8w-qbaag4Iqo_QNYm_7A-9BslbstMOdbZbU,3329
|
|
31
|
+
tests/test_filesystem.py,sha256=Kou0gj5T72oISao6umYfU6L_W5Hi7QS9_IxTv2hU0Pw,3147
|
|
32
|
+
tests/test_oeis.py,sha256=uxvwmgbnylSDdsVJfuAT0LuYLbIVFwSgdLxHm-xUGBM,5043
|
|
33
|
+
tests/test_other.py,sha256=4iF6JJ192BtDZUEZ0avbVRprCwfCUSOUC8GfCPrGS8M,4232
|
|
34
|
+
tests/test_tasks.py,sha256=hkZygihT8bCEO2zc-2VcxReQrZJBwgLNbYx0YP4lTDg,2853
|
|
35
|
+
mapfolding-0.8.2.dist-info/METADATA,sha256=8tSQtHxSLzIjIINZCw4EExiSeQV_wpLQXvf4gQ4xFNo,9143
|
|
36
|
+
mapfolding-0.8.2.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
|
|
37
|
+
mapfolding-0.8.2.dist-info/entry_points.txt,sha256=F3OUeZR1XDTpoH7k3wXuRb3KF_kXTTeYhu5AGK1SiOQ,146
|
|
38
|
+
mapfolding-0.8.2.dist-info/top_level.txt,sha256=1gP2vFaqPwHujGwb3UjtMlLEGN-943VSYFR7V4gDqW8,17
|
|
39
|
+
mapfolding-0.8.2.dist-info/RECORD,,
|