mapFolding 0.8.6__py3-none-any.whl → 0.9.1__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 +60 -13
- mapFolding/basecamp.py +32 -17
- mapFolding/beDRY.py +4 -5
- mapFolding/oeis.py +94 -7
- mapFolding/someAssemblyRequired/RecipeJob.py +103 -0
- mapFolding/someAssemblyRequired/__init__.py +71 -50
- mapFolding/someAssemblyRequired/_theTypes.py +11 -15
- mapFolding/someAssemblyRequired/_tool_Make.py +36 -9
- mapFolding/someAssemblyRequired/_tool_Then.py +59 -25
- mapFolding/someAssemblyRequired/_toolboxAntecedents.py +159 -272
- mapFolding/someAssemblyRequired/_toolboxContainers.py +155 -70
- mapFolding/someAssemblyRequired/_toolboxPython.py +168 -44
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +154 -39
- mapFolding/someAssemblyRequired/toolboxNumba.py +72 -230
- mapFolding/someAssemblyRequired/transformationTools.py +370 -141
- mapFolding/syntheticModules/{numbaCount_doTheNeedful.py → numbaCount.py} +7 -4
- mapFolding/theDao.py +19 -16
- mapFolding/theSSOT.py +165 -62
- mapFolding/toolboxFilesystem.py +1 -1
- mapfolding-0.9.1.dist-info/METADATA +177 -0
- mapfolding-0.9.1.dist-info/RECORD +47 -0
- tests/__init__.py +44 -0
- tests/conftest.py +75 -7
- tests/test_computations.py +92 -10
- tests/test_filesystem.py +32 -33
- tests/test_other.py +0 -1
- tests/test_tasks.py +1 -1
- mapFolding/someAssemblyRequired/newInliner.py +0 -22
- mapfolding-0.8.6.dist-info/METADATA +0 -190
- mapfolding-0.8.6.dist-info/RECORD +0 -47
- {mapfolding-0.8.6.dist-info → mapfolding-0.9.1.dist-info}/WHEEL +0 -0
- {mapfolding-0.8.6.dist-info → mapfolding-0.9.1.dist-info}/entry_points.txt +0 -0
- {mapfolding-0.8.6.dist-info → mapfolding-0.9.1.dist-info}/licenses/LICENSE +0 -0
- {mapfolding-0.8.6.dist-info → mapfolding-0.9.1.dist-info}/top_level.txt +0 -0
|
@@ -47,7 +47,7 @@ def countInitialize(state: ComputationState) -> ComputationState:
|
|
|
47
47
|
return state
|
|
48
48
|
|
|
49
49
|
@jit(_nrt=True, boundscheck=False, cache=True, error_model='numpy', fastmath=True, forceinline=True, inline='always', looplift=False, no_cfunc_wrapper=False, no_cpython_wrapper=False, nopython=True, parallel=False)
|
|
50
|
-
def countParallel(mapShape: tuple[DatatypeLeavesTotal, ...], leavesTotal: DatatypeLeavesTotal, taskDivisions: DatatypeLeavesTotal, concurrencyLimit: DatatypeElephino, connectionGraph: Array3D, dimensionsTotal: DatatypeLeavesTotal, countDimensionsGapped: Array1DLeavesTotal, dimensionsUnconstrained: DatatypeLeavesTotal, gapRangeStart: Array1DElephino, gapsWhere: Array1DLeavesTotal, leafAbove: Array1DLeavesTotal, leafBelow: Array1DLeavesTotal, foldGroups: Array1DFoldsTotal, foldsTotal: DatatypeFoldsTotal, gap1ndex: DatatypeLeavesTotal, gap1ndexCeiling: DatatypeElephino, groupsOfFolds: DatatypeFoldsTotal, indexDimension: DatatypeLeavesTotal, indexLeaf: DatatypeLeavesTotal, indexMiniGap: DatatypeElephino, leaf1ndex:
|
|
50
|
+
def countParallel(mapShape: tuple[DatatypeLeavesTotal, ...], leavesTotal: DatatypeLeavesTotal, taskDivisions: DatatypeLeavesTotal, concurrencyLimit: DatatypeElephino, connectionGraph: Array3D, dimensionsTotal: DatatypeLeavesTotal, countDimensionsGapped: Array1DLeavesTotal, dimensionsUnconstrained: DatatypeLeavesTotal, gapRangeStart: Array1DElephino, gapsWhere: Array1DLeavesTotal, leafAbove: Array1DLeavesTotal, leafBelow: Array1DLeavesTotal, foldGroups: Array1DFoldsTotal, foldsTotal: DatatypeFoldsTotal, gap1ndex: DatatypeLeavesTotal, gap1ndexCeiling: DatatypeElephino, groupsOfFolds: DatatypeFoldsTotal, indexDimension: DatatypeLeavesTotal, indexLeaf: DatatypeLeavesTotal, indexMiniGap: DatatypeElephino, leaf1ndex: DatatypeLeavesTotal, leafConnectee: DatatypeElephino, taskIndex: DatatypeLeavesTotal) -> DatatypeFoldsTotal:
|
|
51
51
|
while leaf1ndex > 0:
|
|
52
52
|
if leaf1ndex <= 1 or leafBelow[0] == 1:
|
|
53
53
|
if leaf1ndex > leavesTotal:
|
|
@@ -92,7 +92,7 @@ def countParallel(mapShape: tuple[DatatypeLeavesTotal, ...], leavesTotal: Dataty
|
|
|
92
92
|
return groupsOfFolds
|
|
93
93
|
|
|
94
94
|
@jit(_nrt=True, boundscheck=False, cache=True, error_model='numpy', fastmath=True, forceinline=True, inline='always', looplift=False, no_cfunc_wrapper=False, no_cpython_wrapper=False, nopython=True, parallel=False)
|
|
95
|
-
def countSequential(mapShape: tuple[DatatypeLeavesTotal, ...], leavesTotal: DatatypeLeavesTotal, taskDivisions: DatatypeLeavesTotal, concurrencyLimit: DatatypeElephino, connectionGraph: Array3D, dimensionsTotal: DatatypeLeavesTotal, countDimensionsGapped: Array1DLeavesTotal, dimensionsUnconstrained: DatatypeLeavesTotal, gapRangeStart: Array1DElephino, gapsWhere: Array1DLeavesTotal, leafAbove: Array1DLeavesTotal, leafBelow: Array1DLeavesTotal, foldGroups: Array1DFoldsTotal, foldsTotal: DatatypeFoldsTotal, gap1ndex: DatatypeLeavesTotal, gap1ndexCeiling: DatatypeElephino, groupsOfFolds: DatatypeFoldsTotal, indexDimension: DatatypeLeavesTotal, indexLeaf: DatatypeLeavesTotal, indexMiniGap: DatatypeElephino, leaf1ndex:
|
|
95
|
+
def countSequential(mapShape: tuple[DatatypeLeavesTotal, ...], leavesTotal: DatatypeLeavesTotal, taskDivisions: DatatypeLeavesTotal, concurrencyLimit: DatatypeElephino, connectionGraph: Array3D, dimensionsTotal: DatatypeLeavesTotal, countDimensionsGapped: Array1DLeavesTotal, dimensionsUnconstrained: DatatypeLeavesTotal, gapRangeStart: Array1DElephino, gapsWhere: Array1DLeavesTotal, leafAbove: Array1DLeavesTotal, leafBelow: Array1DLeavesTotal, foldGroups: Array1DFoldsTotal, foldsTotal: DatatypeFoldsTotal, gap1ndex: DatatypeLeavesTotal, gap1ndexCeiling: DatatypeElephino, groupsOfFolds: DatatypeFoldsTotal, indexDimension: DatatypeLeavesTotal, indexLeaf: DatatypeLeavesTotal, indexMiniGap: DatatypeElephino, leaf1ndex: DatatypeLeavesTotal, leafConnectee: DatatypeElephino, taskIndex: DatatypeLeavesTotal) -> tuple[tuple[DatatypeLeavesTotal, ...], DatatypeLeavesTotal, DatatypeLeavesTotal, DatatypeElephino, Array3D, DatatypeLeavesTotal, Array1DLeavesTotal, DatatypeLeavesTotal, Array1DElephino, Array1DLeavesTotal, Array1DLeavesTotal, Array1DLeavesTotal, Array1DFoldsTotal, DatatypeFoldsTotal, DatatypeLeavesTotal, DatatypeElephino, DatatypeFoldsTotal, DatatypeLeavesTotal, DatatypeLeavesTotal, DatatypeElephino, DatatypeLeavesTotal, DatatypeElephino, DatatypeLeavesTotal]:
|
|
96
96
|
while leaf1ndex > 0:
|
|
97
97
|
if leaf1ndex <= 1 or leafBelow[0] == 1:
|
|
98
98
|
if leaf1ndex > leavesTotal:
|
|
@@ -124,6 +124,9 @@ def countSequential(mapShape: tuple[DatatypeLeavesTotal, ...], leavesTotal: Data
|
|
|
124
124
|
leaf1ndex -= 1
|
|
125
125
|
leafBelow[leafAbove[leaf1ndex]] = leafBelow[leaf1ndex]
|
|
126
126
|
leafAbove[leafBelow[leaf1ndex]] = leafAbove[leaf1ndex]
|
|
127
|
+
if groupsOfFolds and leaf1ndex == 3:
|
|
128
|
+
groupsOfFolds *= 2
|
|
129
|
+
break
|
|
127
130
|
if leaf1ndex > 0:
|
|
128
131
|
gap1ndex -= 1
|
|
129
132
|
leafAbove[leaf1ndex] = gapsWhere[gap1ndex]
|
|
@@ -164,7 +167,7 @@ def doTheNeedful(state: ComputationState) -> ComputationState:
|
|
|
164
167
|
indexDimension: DatatypeLeavesTotal = state.indexDimension
|
|
165
168
|
indexLeaf: DatatypeLeavesTotal = state.indexLeaf
|
|
166
169
|
indexMiniGap: DatatypeElephino = state.indexMiniGap
|
|
167
|
-
leaf1ndex:
|
|
170
|
+
leaf1ndex: DatatypeLeavesTotal = state.leaf1ndex
|
|
168
171
|
leafConnectee: DatatypeElephino = state.leafConnectee
|
|
169
172
|
taskIndex: DatatypeLeavesTotal = state.taskIndex
|
|
170
173
|
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)
|
|
@@ -192,7 +195,7 @@ def doTheNeedful(state: ComputationState) -> ComputationState:
|
|
|
192
195
|
indexDimension: DatatypeLeavesTotal = state.indexDimension
|
|
193
196
|
indexLeaf: DatatypeLeavesTotal = state.indexLeaf
|
|
194
197
|
indexMiniGap: DatatypeElephino = state.indexMiniGap
|
|
195
|
-
leaf1ndex:
|
|
198
|
+
leaf1ndex: DatatypeLeavesTotal = state.leaf1ndex
|
|
196
199
|
leafConnectee: DatatypeElephino = state.leafConnectee
|
|
197
200
|
taskIndex: DatatypeLeavesTotal = state.taskIndex
|
|
198
201
|
mapShape, leavesTotal, taskDivisions, concurrencyLimit, connectionGraph, dimensionsTotal, countDimensionsGapped, dimensionsUnconstrained, gapRangeStart, gapsWhere, leafAbove, leafBelow, foldGroups, foldsTotal, gap1ndex, gap1ndexCeiling, groupsOfFolds, indexDimension, indexLeaf, indexMiniGap, leaf1ndex, leafConnectee, taskIndex = countSequential(mapShape, leavesTotal, taskDivisions, concurrencyLimit, connectionGraph, dimensionsTotal, countDimensionsGapped, dimensionsUnconstrained, gapRangeStart, gapsWhere, leafAbove, leafBelow, foldGroups, foldsTotal, gap1ndex, gap1ndexCeiling, groupsOfFolds, indexDimension, indexLeaf, indexMiniGap, leaf1ndex, leafConnectee, taskIndex)
|
mapFolding/theDao.py
CHANGED
|
@@ -43,12 +43,6 @@ def activeLeafIsTheFirstLeaf(state: ComputationState) -> bool:
|
|
|
43
43
|
def allDimensionsAreUnconstrained(state: ComputationState) -> bool:
|
|
44
44
|
return not state.dimensionsUnconstrained
|
|
45
45
|
|
|
46
|
-
def undoLastLeafPlacement(state: ComputationState) -> ComputationState:
|
|
47
|
-
state.leaf1ndex -= 1
|
|
48
|
-
state.leafBelow[state.leafAbove[state.leaf1ndex]] = state.leafBelow[state.leaf1ndex]
|
|
49
|
-
state.leafAbove[state.leafBelow[state.leaf1ndex]] = state.leafAbove[state.leaf1ndex]
|
|
50
|
-
return state
|
|
51
|
-
|
|
52
46
|
def countGaps(state: ComputationState) -> ComputationState:
|
|
53
47
|
state.gapsWhere[state.gap1ndexCeiling] = state.leafConnectee
|
|
54
48
|
if state.countDimensionsGapped[state.leafConnectee] == 0:
|
|
@@ -100,6 +94,16 @@ def initializeVariablesToFindGaps(state: ComputationState) -> ComputationState:
|
|
|
100
94
|
state.indexDimension = 0
|
|
101
95
|
return state
|
|
102
96
|
|
|
97
|
+
def insertLeafAtGap(state: ComputationState) -> ComputationState:
|
|
98
|
+
state.gap1ndex -= 1
|
|
99
|
+
state.leafAbove[state.leaf1ndex] = state.gapsWhere[state.gap1ndex]
|
|
100
|
+
state.leafBelow[state.leaf1ndex] = state.leafBelow[state.leafAbove[state.leaf1ndex]]
|
|
101
|
+
state.leafBelow[state.leafAbove[state.leaf1ndex]] = state.leaf1ndex
|
|
102
|
+
state.leafAbove[state.leafBelow[state.leaf1ndex]] = state.leaf1ndex
|
|
103
|
+
state.gapRangeStart[state.leaf1ndex] = state.gap1ndex
|
|
104
|
+
state.leaf1ndex += 1
|
|
105
|
+
return state
|
|
106
|
+
|
|
103
107
|
def insertUnconstrainedLeaf(state: ComputationState) -> ComputationState:
|
|
104
108
|
state.indexLeaf = 0
|
|
105
109
|
while state.indexLeaf < state.leaf1ndex:
|
|
@@ -123,22 +127,18 @@ def loopUpToDimensionsTotal(state: ComputationState) -> bool:
|
|
|
123
127
|
def noGapsHere(state: ComputationState) -> bool:
|
|
124
128
|
return (state.leaf1ndex > 0) and (state.gap1ndex == state.gapRangeStart[state.leaf1ndex - 1])
|
|
125
129
|
|
|
126
|
-
def insertLeafAtGap(state: ComputationState) -> ComputationState:
|
|
127
|
-
state.gap1ndex -= 1
|
|
128
|
-
state.leafAbove[state.leaf1ndex] = state.gapsWhere[state.gap1ndex]
|
|
129
|
-
state.leafBelow[state.leaf1ndex] = state.leafBelow[state.leafAbove[state.leaf1ndex]]
|
|
130
|
-
state.leafBelow[state.leafAbove[state.leaf1ndex]] = state.leaf1ndex
|
|
131
|
-
state.leafAbove[state.leafBelow[state.leaf1ndex]] = state.leaf1ndex
|
|
132
|
-
state.gapRangeStart[state.leaf1ndex] = state.gap1ndex
|
|
133
|
-
state.leaf1ndex += 1
|
|
134
|
-
return state
|
|
135
|
-
|
|
136
130
|
def thereIsAnActiveLeaf(state: ComputationState) -> bool:
|
|
137
131
|
return state.leaf1ndex > 0
|
|
138
132
|
|
|
139
133
|
def thisIsMyTaskIndex(state: ComputationState) -> bool:
|
|
140
134
|
return (state.leaf1ndex != state.taskDivisions) or (state.leafConnectee % state.taskDivisions == state.taskIndex)
|
|
141
135
|
|
|
136
|
+
def undoLastLeafPlacement(state: ComputationState) -> ComputationState:
|
|
137
|
+
state.leaf1ndex -= 1
|
|
138
|
+
state.leafBelow[state.leafAbove[state.leaf1ndex]] = state.leafBelow[state.leaf1ndex]
|
|
139
|
+
state.leafAbove[state.leafBelow[state.leaf1ndex]] = state.leafAbove[state.leaf1ndex]
|
|
140
|
+
return state
|
|
141
|
+
|
|
142
142
|
def updateLeafConnectee(state: ComputationState) -> ComputationState:
|
|
143
143
|
state.leafConnectee = state.connectionGraph[state.indexDimension, state.leaf1ndex, state.leafBelow[state.leafConnectee]]
|
|
144
144
|
return state
|
|
@@ -218,6 +218,9 @@ def countSequential(state: ComputationState) -> ComputationState:
|
|
|
218
218
|
state = incrementIndexMiniGap(state)
|
|
219
219
|
while noGapsHere(state):
|
|
220
220
|
state = undoLastLeafPlacement(state)
|
|
221
|
+
if state.groupsOfFolds and state.leaf1ndex == 3:
|
|
222
|
+
state.groupsOfFolds *= 2
|
|
223
|
+
break
|
|
221
224
|
if thereIsAnActiveLeaf(state):
|
|
222
225
|
state = insertLeafAtGap(state)
|
|
223
226
|
state.foldGroups[state.taskIndex] = state.groupsOfFolds
|
mapFolding/theSSOT.py
CHANGED
|
@@ -19,74 +19,115 @@ to avoid namespace collisions when transforming algorithms.
|
|
|
19
19
|
from collections.abc import Callable
|
|
20
20
|
from importlib import import_module as importlib_import_module
|
|
21
21
|
from inspect import getfile as inspect_getfile
|
|
22
|
-
from numpy import dtype, int64 as numpy_int64, int16 as numpy_int16, integer, ndarray
|
|
22
|
+
from numpy import dtype, int64 as numpy_int64, int16 as numpy_int16, integer, ndarray, uint8 as numpy_uint8
|
|
23
23
|
from pathlib import Path
|
|
24
24
|
from tomli import load as tomli_load
|
|
25
25
|
from types import ModuleType
|
|
26
26
|
from typing import Any, TypeAlias, TypeVar
|
|
27
27
|
import dataclasses
|
|
28
|
+
from numba import int64, uint8
|
|
28
29
|
|
|
29
|
-
#
|
|
30
|
-
# The Wrong Way: Evaluate When Packaging
|
|
31
|
-
|
|
30
|
+
# Evaluate When Packaging https://github.com/hunterhogan/mapFolding/issues/18
|
|
32
31
|
try:
|
|
33
32
|
packageNamePACKAGING: str = tomli_load(Path("../pyproject.toml").open('rb'))["project"]["name"]
|
|
34
33
|
except Exception:
|
|
35
34
|
packageNamePACKAGING = "mapFolding"
|
|
36
35
|
|
|
37
|
-
#
|
|
38
|
-
|
|
36
|
+
# Evaluate When Installing https://github.com/hunterhogan/mapFolding/issues/18
|
|
39
37
|
def getPathPackageINSTALLING() -> Path:
|
|
40
38
|
pathPackage: Path = Path(inspect_getfile(importlib_import_module(packageNamePACKAGING)))
|
|
41
39
|
if pathPackage.is_file():
|
|
42
40
|
pathPackage = pathPackage.parent
|
|
43
41
|
return pathPackage
|
|
44
42
|
|
|
45
|
-
#
|
|
46
|
-
#
|
|
43
|
+
# I believe these values should be dynamically determined, so I have conspicuously marked them "HARDCODED"
|
|
44
|
+
# and created downstream logic that assumes the values were dynamically determined.
|
|
47
45
|
# 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'
|
|
46
|
+
logicalPathModuleDispatcherHARDCODED: str = 'mapFolding.syntheticModules.numbaCount'
|
|
51
47
|
callableDispatcherHARDCODED: str = 'doTheNeedful'
|
|
52
48
|
concurrencyPackageHARDCODED = 'multiprocessing'
|
|
49
|
+
# from mapFolding.someAssemblyRequired.toolboxNumba.theNumbaFlow
|
|
53
50
|
|
|
54
|
-
#
|
|
55
|
-
# The following is an improvement, but it is not the full solution.
|
|
56
|
-
# I hope that the standardized markers, `metadata={'evaluateWhen': 'packaging'}` will help to automate
|
|
57
|
-
# whatever needs to happen so that the following is well implemented.
|
|
58
|
-
# @dataclasses.dataclass(frozen=True)
|
|
51
|
+
# PackageSettings in theSSOT.py and immutability https://github.com/hunterhogan/mapFolding/issues/11
|
|
59
52
|
@dataclasses.dataclass
|
|
60
53
|
class PackageSettings:
|
|
54
|
+
"""
|
|
55
|
+
Centralized configuration settings for the mapFolding package.
|
|
56
|
+
|
|
57
|
+
This class implements the Single Source of Truth (SSOT) principle for package
|
|
58
|
+
configuration, providing a consistent interface for accessing package settings,
|
|
59
|
+
paths, and dispatch functions. The primary instance of this class, named `The`,
|
|
60
|
+
is imported and used throughout the package to retrieve configuration values.
|
|
61
|
+
"""
|
|
61
62
|
|
|
62
63
|
logicalPathModuleDispatcher: str | None = None
|
|
64
|
+
"""Logical import path to the module containing the dispatcher function."""
|
|
65
|
+
|
|
63
66
|
callableDispatcher: str | None = None
|
|
64
|
-
|
|
67
|
+
"""Name of the function within the dispatcher module that will be called."""
|
|
68
|
+
|
|
69
|
+
concurrencyPackage: str | None = None
|
|
70
|
+
"""Package to use for concurrent execution (e.g., 'multiprocessing', 'numba')."""
|
|
71
|
+
|
|
72
|
+
# "Evaluate When Packaging" and "Evaluate When Installing" https://github.com/hunterhogan/mapFolding/issues/18
|
|
65
73
|
dataclassIdentifier: str = dataclasses.field(default='ComputationState', metadata={'evaluateWhen': 'packaging'})
|
|
74
|
+
"""Name of the dataclass used to track computation state."""
|
|
75
|
+
|
|
66
76
|
dataclassInstance: str = dataclasses.field(default='state', metadata={'evaluateWhen': 'packaging'})
|
|
77
|
+
"""Default variable name for instances of the computation state dataclass."""
|
|
78
|
+
|
|
67
79
|
dataclassInstanceTaskDistributionSuffix: str = dataclasses.field(default='Parallel', metadata={'evaluateWhen': 'packaging'})
|
|
80
|
+
"""Suffix added to dataclassInstance for parallel task distribution."""
|
|
81
|
+
|
|
68
82
|
dataclassModule: str = dataclasses.field(default='theSSOT', metadata={'evaluateWhen': 'packaging'})
|
|
83
|
+
"""Module containing the computation state dataclass definition."""
|
|
84
|
+
|
|
69
85
|
datatypePackage: str = dataclasses.field(default='numpy', metadata={'evaluateWhen': 'packaging'})
|
|
86
|
+
"""Package providing the numeric data types used in computation."""
|
|
87
|
+
|
|
70
88
|
fileExtension: str = dataclasses.field(default='.py', metadata={'evaluateWhen': 'installing'})
|
|
89
|
+
"""Default file extension for generated code files."""
|
|
90
|
+
|
|
71
91
|
packageName: str = dataclasses.field(default = packageNamePACKAGING, metadata={'evaluateWhen': 'packaging'})
|
|
72
|
-
|
|
92
|
+
"""Name of this package, used for import paths and configuration."""
|
|
93
|
+
|
|
94
|
+
pathPackage: Path = dataclasses.field(default_factory=getPathPackageINSTALLING, metadata={'evaluateWhen': 'installing'})
|
|
95
|
+
"""Absolute path to the installed package directory."""
|
|
96
|
+
|
|
73
97
|
sourceAlgorithm: str = dataclasses.field(default='theDao', metadata={'evaluateWhen': 'packaging'})
|
|
98
|
+
"""Module containing the reference implementation of the algorithm."""
|
|
99
|
+
|
|
74
100
|
sourceCallableDispatcher: str = dataclasses.field(default='doTheNeedful', metadata={'evaluateWhen': 'packaging'})
|
|
101
|
+
"""Name of the function that dispatches computation in the source algorithm."""
|
|
102
|
+
|
|
75
103
|
sourceCallableInitialize: str = dataclasses.field(default='countInitialize', metadata={'evaluateWhen': 'packaging'})
|
|
104
|
+
"""Name of the function that initializes computation in the source algorithm."""
|
|
105
|
+
|
|
76
106
|
sourceCallableParallel: str = dataclasses.field(default='countParallel', metadata={'evaluateWhen': 'packaging'})
|
|
107
|
+
"""Name of the function that performs parallel computation in the source algorithm."""
|
|
108
|
+
|
|
77
109
|
sourceCallableSequential: str = dataclasses.field(default='countSequential', metadata={'evaluateWhen': 'packaging'})
|
|
110
|
+
"""Name of the function that performs sequential computation in the source algorithm."""
|
|
111
|
+
|
|
78
112
|
sourceConcurrencyManagerIdentifier: str = dataclasses.field(default='submit', metadata={'evaluateWhen': 'packaging'})
|
|
113
|
+
"""Method name used to submit tasks to the concurrency manager."""
|
|
114
|
+
|
|
79
115
|
sourceConcurrencyManagerNamespace: str = dataclasses.field(default='concurrencyManager', metadata={'evaluateWhen': 'packaging'})
|
|
116
|
+
"""Variable name used for the concurrency manager instance."""
|
|
117
|
+
|
|
80
118
|
sourceConcurrencyPackage: str = dataclasses.field(default='multiprocessing', metadata={'evaluateWhen': 'packaging'})
|
|
119
|
+
"""Default package used for concurrency in the source algorithm."""
|
|
81
120
|
|
|
82
|
-
dataclassInstanceTaskDistribution: str = dataclasses.field(
|
|
83
|
-
"""
|
|
84
|
-
logicalPathModuleDataclass: str = dataclasses.field(init=False)
|
|
85
|
-
""" The package.module.name logical path to the dataclass. """
|
|
86
|
-
logicalPathModuleSourceAlgorithm: str = dataclasses.field(init=False)
|
|
87
|
-
""" The package.module.name logical path to the source algorithm. """
|
|
121
|
+
dataclassInstanceTaskDistribution: str = dataclasses.field(default=None, metadata={'evaluateWhen': 'packaging'}) # pyright: ignore[reportAssignmentType]
|
|
122
|
+
"""Variable name for the parallel distribution instance of the computation state."""
|
|
88
123
|
|
|
89
|
-
|
|
124
|
+
logicalPathModuleDataclass: str = dataclasses.field(default=None, metadata={'evaluateWhen': 'packaging'}) # pyright: ignore[reportAssignmentType]
|
|
125
|
+
"""Fully qualified import path to the module containing the computation state dataclass."""
|
|
126
|
+
|
|
127
|
+
logicalPathModuleSourceAlgorithm: str = dataclasses.field(default=None, metadata={'evaluateWhen': 'packaging'}) # pyright: ignore[reportAssignmentType]
|
|
128
|
+
"""Fully qualified import path to the module containing the source algorithm."""
|
|
129
|
+
|
|
130
|
+
@property
|
|
90
131
|
def dispatcher(self) -> Callable[['ComputationState'], 'ComputationState']:
|
|
91
132
|
""" _The_ callable that connects `countFolds` to the logic that does the work."""
|
|
92
133
|
logicalPath: str = self.logicalPathModuleDispatcher or self.logicalPathModuleSourceAlgorithm
|
|
@@ -95,35 +136,36 @@ class PackageSettings:
|
|
|
95
136
|
return getattr(moduleImported, identifier)
|
|
96
137
|
|
|
97
138
|
def __post_init__(self) -> None:
|
|
98
|
-
self.dataclassInstanceTaskDistribution
|
|
139
|
+
if self.dataclassInstanceTaskDistribution is None: # pyright: ignore[reportUnnecessaryComparison]
|
|
140
|
+
self.dataclassInstanceTaskDistribution = self.dataclassInstance + self.dataclassInstanceTaskDistributionSuffix
|
|
99
141
|
|
|
100
|
-
self.logicalPathModuleDataclass
|
|
101
|
-
|
|
142
|
+
if self.logicalPathModuleDataclass is None: # pyright: ignore[reportUnnecessaryComparison]
|
|
143
|
+
self.logicalPathModuleDataclass = '.'.join([self.packageName, self.dataclassModule])
|
|
144
|
+
if self.logicalPathModuleSourceAlgorithm is None: # pyright: ignore[reportUnnecessaryComparison]
|
|
145
|
+
self.logicalPathModuleSourceAlgorithm = '.'.join([self.packageName, self.sourceAlgorithm])
|
|
102
146
|
|
|
103
147
|
The = PackageSettings(logicalPathModuleDispatcher=logicalPathModuleDispatcherHARDCODED, callableDispatcher=callableDispatcherHARDCODED, concurrencyPackage=concurrencyPackageHARDCODED)
|
|
104
148
|
|
|
105
|
-
# To remove this function, I need to learn how to change "conftest.py" to patch this.
|
|
106
|
-
def getPackageDispatcher() -> Callable[['ComputationState'], 'ComputationState']:
|
|
107
|
-
"""Get the dispatcher callable for the package.
|
|
108
|
-
|
|
109
|
-
This function retrieves the dispatcher callable for the package based on the
|
|
110
|
-
logical path module and callable dispatcher defined in the PackageSettings.
|
|
111
|
-
"""
|
|
112
|
-
return The.dispatcher
|
|
113
149
|
# =============================================================================
|
|
114
150
|
# Flexible Data Structure System Needs Enhanced Paradigm https://github.com/hunterhogan/mapFolding/issues/9
|
|
115
151
|
|
|
116
|
-
|
|
152
|
+
NumPyIntegerType = TypeVar('NumPyIntegerType', bound=integer[Any], covariant=True)
|
|
117
153
|
|
|
118
|
-
DatatypeLeavesTotal: TypeAlias = int
|
|
119
|
-
NumPyLeavesTotal: TypeAlias =
|
|
154
|
+
# DatatypeLeavesTotal: TypeAlias = int
|
|
155
|
+
NumPyLeavesTotal: TypeAlias = numpy_uint8
|
|
156
|
+
# 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
|
|
120
157
|
|
|
121
|
-
DatatypeElephino: TypeAlias = int
|
|
122
|
-
NumPyElephino: TypeAlias =
|
|
158
|
+
# DatatypeElephino: TypeAlias = int
|
|
159
|
+
NumPyElephino: TypeAlias = numpy_uint8
|
|
160
|
+
# NumPyElephino: TypeAlias = numpy_int16
|
|
123
161
|
|
|
124
|
-
DatatypeFoldsTotal: TypeAlias = int
|
|
162
|
+
# DatatypeFoldsTotal: TypeAlias = int
|
|
125
163
|
NumPyFoldsTotal: TypeAlias = numpy_int64
|
|
126
164
|
|
|
165
|
+
DatatypeLeavesTotal = uint8
|
|
166
|
+
DatatypeElephino = uint8
|
|
167
|
+
DatatypeFoldsTotal = int64
|
|
168
|
+
|
|
127
169
|
Array3D: TypeAlias = ndarray[tuple[int, int, int], dtype[NumPyLeavesTotal]]
|
|
128
170
|
Array1DLeavesTotal: TypeAlias = ndarray[tuple[int], dtype[NumPyLeavesTotal]]
|
|
129
171
|
Array1DElephino: TypeAlias = ndarray[tuple[int], dtype[NumPyElephino]]
|
|
@@ -131,57 +173,118 @@ Array1DFoldsTotal: TypeAlias = ndarray[tuple[int], dtype[NumPyFoldsTotal]]
|
|
|
131
173
|
|
|
132
174
|
@dataclasses.dataclass
|
|
133
175
|
class ComputationState:
|
|
134
|
-
|
|
176
|
+
"""
|
|
177
|
+
Represents the complete state of a map folding computation.
|
|
178
|
+
|
|
179
|
+
This dataclass encapsulates all the information required to compute the number of
|
|
180
|
+
possible ways to fold a map, including the map dimensions, leaf connections,
|
|
181
|
+
computation progress, and fold counting. It serves as the central data structure
|
|
182
|
+
that flows through the entire computational algorithm.
|
|
183
|
+
|
|
184
|
+
Fields are categorized into:
|
|
185
|
+
1. Input parameters (mapShape, leavesTotal, etc.)
|
|
186
|
+
2. Core computational structures (connectionGraph, etc.)
|
|
187
|
+
3. Tracking variables for the folding algorithm state
|
|
188
|
+
4. Result accumulation fields (foldsTotal, groupsOfFolds)
|
|
189
|
+
|
|
190
|
+
The data structures and algorithms are based on Lunnon's 1971 paper on map folding.
|
|
191
|
+
"""
|
|
192
|
+
# NOTE Python is anti-DRY, again, `DatatypeLeavesTotal` metadata needs to match the type
|
|
193
|
+
mapShape: tuple[DatatypeLeavesTotal, ...] = dataclasses.field(init=True, metadata={'elementConstructor': 'DatatypeLeavesTotal'})
|
|
194
|
+
"""Dimensions of the map to be folded, as a tuple of integers."""
|
|
195
|
+
|
|
135
196
|
leavesTotal: DatatypeLeavesTotal
|
|
197
|
+
"""Total number of leaves (unit squares) in the map, equal to the product of all dimensions."""
|
|
198
|
+
|
|
136
199
|
taskDivisions: DatatypeLeavesTotal
|
|
200
|
+
"""Number of parallel tasks to divide the computation into. Zero means sequential computation."""
|
|
201
|
+
|
|
137
202
|
concurrencyLimit: DatatypeElephino
|
|
203
|
+
"""Maximum number of concurrent processes to use during computation."""
|
|
138
204
|
|
|
139
205
|
connectionGraph: Array3D = dataclasses.field(init=False, metadata={'dtype': Array3D.__args__[1].__args__[0]}) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
|
|
206
|
+
"""3D array encoding the connections between leaves in all dimensions."""
|
|
207
|
+
|
|
140
208
|
dimensionsTotal: DatatypeLeavesTotal = dataclasses.field(init=False)
|
|
209
|
+
"""Total number of dimensions in the map shape."""
|
|
210
|
+
|
|
211
|
+
# 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
|
|
212
|
+
countDimensionsGapped: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
213
|
+
"""Tracks how many dimensions are gapped for each leaf."""
|
|
214
|
+
|
|
215
|
+
dimensionsUnconstrained: DatatypeLeavesTotal = dataclasses.field(default=None, init=True) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
216
|
+
"""Number of dimensions that are not constrained in the current folding state."""
|
|
217
|
+
|
|
218
|
+
gapRangeStart: Array1DElephino = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DElephino.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
219
|
+
"""Starting index for the gap range for each leaf."""
|
|
220
|
+
|
|
221
|
+
gapsWhere: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
222
|
+
"""Tracks where gaps occur in the folding pattern."""
|
|
141
223
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
foldGroups: Array1DFoldsTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DFoldsTotal.__args__[1].__args__[0]}) #
|
|
224
|
+
leafAbove: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
225
|
+
"""For each leaf, stores the index of the leaf above it in the folding pattern."""
|
|
226
|
+
|
|
227
|
+
leafBelow: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
228
|
+
"""For each leaf, stores the index of the leaf below it in the folding pattern."""
|
|
229
|
+
|
|
230
|
+
foldGroups: Array1DFoldsTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DFoldsTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
231
|
+
"""Accumulator for fold groups across parallel tasks."""
|
|
149
232
|
|
|
150
233
|
foldsTotal: DatatypeFoldsTotal = DatatypeFoldsTotal(0)
|
|
234
|
+
"""The final computed total number of distinct folding patterns."""
|
|
235
|
+
|
|
151
236
|
gap1ndex: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
|
|
237
|
+
"""Current index into gaps array during algorithm execution."""
|
|
238
|
+
|
|
152
239
|
gap1ndexCeiling: DatatypeElephino = DatatypeElephino(0)
|
|
240
|
+
"""Upper limit for gap index during the current algorithm phase."""
|
|
241
|
+
|
|
153
242
|
groupsOfFolds: DatatypeFoldsTotal = dataclasses.field(default=DatatypeFoldsTotal(0), metadata={'theCountingIdentifier': True})
|
|
243
|
+
"""Accumulator for the number of fold groups found during computation."""
|
|
244
|
+
|
|
154
245
|
indexDimension: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
|
|
246
|
+
"""Current dimension being processed during algorithm execution."""
|
|
247
|
+
|
|
155
248
|
indexLeaf: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
|
|
249
|
+
"""Current leaf index during iteration."""
|
|
250
|
+
|
|
156
251
|
indexMiniGap: DatatypeElephino = DatatypeElephino(0)
|
|
157
|
-
|
|
252
|
+
"""Index used when filtering common gaps."""
|
|
253
|
+
|
|
254
|
+
leaf1ndex: DatatypeLeavesTotal = DatatypeLeavesTotal(1)
|
|
255
|
+
"""Active leaf being processed in the folding algorithm. Starts at 1, not 0."""
|
|
256
|
+
|
|
158
257
|
leafConnectee: DatatypeElephino = DatatypeElephino(0)
|
|
258
|
+
"""Leaf that is being connected to the active leaf."""
|
|
259
|
+
|
|
260
|
+
# leafSequence: list[DatatypeLeavesTotal] = dataclasses.field(default_factory=list, metadata={'elementConstructor': 'DatatypeLeavesTotal'})
|
|
261
|
+
|
|
159
262
|
taskIndex: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
|
|
263
|
+
"""Index of the current parallel task when using task divisions."""
|
|
160
264
|
|
|
161
265
|
def __post_init__(self) -> None:
|
|
266
|
+
# self.leafSequence = [self.leaf1ndex]
|
|
162
267
|
from mapFolding.beDRY import getConnectionGraph, makeDataContainer
|
|
163
268
|
self.dimensionsTotal = DatatypeLeavesTotal(len(self.mapShape))
|
|
164
269
|
leavesTotalAsInt = int(self.leavesTotal)
|
|
165
270
|
self.connectionGraph = getConnectionGraph(self.mapShape, leavesTotalAsInt, self.__dataclass_fields__['connectionGraph'].metadata['dtype'])
|
|
166
271
|
|
|
167
|
-
if self.dimensionsUnconstrained is None: #
|
|
168
|
-
self.dimensionsUnconstrained = DatatypeLeavesTotal(int(self.dimensionsTotal))
|
|
272
|
+
if self.dimensionsUnconstrained is None: self.dimensionsUnconstrained = DatatypeLeavesTotal(int(self.dimensionsTotal)) # pyright: ignore[reportUnnecessaryComparison]
|
|
169
273
|
|
|
170
|
-
if self.foldGroups is None: #
|
|
274
|
+
if self.foldGroups is None: # pyright: ignore[reportUnnecessaryComparison]
|
|
171
275
|
self.foldGroups = makeDataContainer(max(2, int(self.taskDivisions) + 1), self.__dataclass_fields__['foldGroups'].metadata['dtype'])
|
|
172
276
|
self.foldGroups[-1] = self.leavesTotal
|
|
173
277
|
|
|
174
|
-
|
|
278
|
+
# Dataclasses, Default factories, and arguments in `ComputationState` https://github.com/hunterhogan/mapFolding/issues/12
|
|
279
|
+
if self.gapsWhere is None: self.gapsWhere = makeDataContainer(leavesTotalAsInt * leavesTotalAsInt + 1, self.__dataclass_fields__['gapsWhere'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
|
|
175
280
|
|
|
176
|
-
if self.countDimensionsGapped is None: self.countDimensionsGapped = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['countDimensionsGapped'].metadata['dtype']) #
|
|
177
|
-
if self.gapRangeStart is None: self.gapRangeStart = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['gapRangeStart'].metadata['dtype']) #
|
|
178
|
-
if self.leafAbove is None: self.leafAbove = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafAbove'].metadata['dtype']) #
|
|
179
|
-
if self.leafBelow is None: self.leafBelow = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafBelow'].metadata['dtype']) #
|
|
281
|
+
if self.countDimensionsGapped is None: self.countDimensionsGapped = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['countDimensionsGapped'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
|
|
282
|
+
if self.gapRangeStart is None: self.gapRangeStart = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['gapRangeStart'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
|
|
283
|
+
if self.leafAbove is None: self.leafAbove = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafAbove'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
|
|
284
|
+
if self.leafBelow is None: self.leafBelow = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafBelow'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
|
|
180
285
|
|
|
286
|
+
# Automatic, or not, calculation in dataclass `ComputationState` https://github.com/hunterhogan/mapFolding/issues/14
|
|
181
287
|
def getFoldsTotal(self) -> None:
|
|
182
288
|
self.foldsTotal = DatatypeFoldsTotal(self.foldGroups[0:-1].sum() * self.leavesTotal)
|
|
183
289
|
|
|
184
|
-
# =============================================================================
|
|
185
|
-
# The coping way.
|
|
186
|
-
|
|
187
290
|
class raiseIfNoneGitHubIssueNumber3(Exception): pass
|
mapFolding/toolboxFilesystem.py
CHANGED
|
@@ -22,7 +22,7 @@ The functions here adhere to a consistent approach to path handling:
|
|
|
22
22
|
- Progressive fallback strategies for saving critical computation results.
|
|
23
23
|
- Preemptive filesystem validation to detect issues before computation begins.
|
|
24
24
|
"""
|
|
25
|
-
from mapFolding
|
|
25
|
+
from mapFolding import The
|
|
26
26
|
from os import PathLike
|
|
27
27
|
from pathlib import Path, PurePath
|
|
28
28
|
from sys import modules as sysModules
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mapFolding
|
|
3
|
+
Version: 0.9.1
|
|
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
|
+
Project-URL: Issues, https://github.com/hunterhogan/mapFolding/issues
|
|
11
|
+
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
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Education
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: Natural Language :: English
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
26
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
27
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
28
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
29
|
+
Classifier: Topic :: Software Development :: Compilers
|
|
30
|
+
Classifier: Typing :: Typed
|
|
31
|
+
Requires-Python: >=3.10
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
License-File: LICENSE
|
|
34
|
+
Requires-Dist: autoflake
|
|
35
|
+
Requires-Dist: more_itertools
|
|
36
|
+
Requires-Dist: numba_progress
|
|
37
|
+
Requires-Dist: numba
|
|
38
|
+
Requires-Dist: numpy
|
|
39
|
+
Requires-Dist: platformdirs
|
|
40
|
+
Requires-Dist: python_minifier
|
|
41
|
+
Requires-Dist: tomli
|
|
42
|
+
Requires-Dist: Z0Z_tools
|
|
43
|
+
Provides-Extra: testing
|
|
44
|
+
Requires-Dist: mypy; extra == "testing"
|
|
45
|
+
Requires-Dist: pytest; extra == "testing"
|
|
46
|
+
Requires-Dist: pytest-cov; extra == "testing"
|
|
47
|
+
Requires-Dist: pytest-env; extra == "testing"
|
|
48
|
+
Requires-Dist: pytest-xdist; extra == "testing"
|
|
49
|
+
Requires-Dist: pyupgrade; extra == "testing"
|
|
50
|
+
Requires-Dist: ruff; extra == "testing"
|
|
51
|
+
Dynamic: license-file
|
|
52
|
+
|
|
53
|
+
# mapFolding: High-Performance Algorithm Playground for Computational Enthusiasts 🗺️
|
|
54
|
+
|
|
55
|
+
[](https://pypi.org/project/mapFolding/)
|
|
56
|
+
[](https://github.com/hunterhogan/mapFolding/actions/workflows/pythonTests.yml)
|
|
57
|
+
[](https://creativecommons.org/licenses/by-nc/4.0/)
|
|
58
|
+
|
|
59
|
+
**This package is for you if:**
|
|
60
|
+
|
|
61
|
+
- You're fascinated by computational algorithms and their optimization
|
|
62
|
+
- You want to explore AST transformation techniques for Python performance tuning
|
|
63
|
+
- You're interested in solving mathematical puzzles through code
|
|
64
|
+
- You're learning about Numba and advanced Python optimization
|
|
65
|
+
|
|
66
|
+
**This package is NOT for you if:**
|
|
67
|
+
|
|
68
|
+
- You're looking for a general-purpose folding simulation tool
|
|
69
|
+
- You need commercial-ready mapping software
|
|
70
|
+
- You want simple visualization of folding patterns
|
|
71
|
+
|
|
72
|
+
## What Does This Package Actually Do?
|
|
73
|
+
|
|
74
|
+
`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:
|
|
75
|
+
|
|
76
|
+
1. How to transform readable algorithms into blazingly fast implementations
|
|
77
|
+
2. Advanced techniques for Python optimization using AST manipulation
|
|
78
|
+
3. Numba acceleration with specialized compilation strategies
|
|
79
|
+
4. Algorithms for problems that grow combinatorially
|
|
80
|
+
|
|
81
|
+
The package has achieved new computational records, including first-ever calculations for large maps that were previously infeasible.
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
# Compute the number of ways to fold a 5×5 grid:
|
|
85
|
+
from mapFolding import oeisIDfor_n
|
|
86
|
+
foldsTotal = oeisIDfor_n('A001418', 5) # Returns 186,086,600
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Key Benefits for Computational Enthusiasts
|
|
90
|
+
|
|
91
|
+
### 1. Algorithm Transformation Laboratory
|
|
92
|
+
|
|
93
|
+
See how the same algorithm evolves from readable Python to highly-optimized implementations:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
# The intuitive, readable version:
|
|
97
|
+
def countFolds(mapShape):
|
|
98
|
+
# ...implement readable algorithm...
|
|
99
|
+
|
|
100
|
+
# The transformed, optimized version (auto-generated):
|
|
101
|
+
@numba.jit(nopython=True, parallel=True, fastmath=True)
|
|
102
|
+
def countFolds_optimized(shape_param):
|
|
103
|
+
# ...blazingly fast implementation...
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 2. Code Generation Framework
|
|
107
|
+
|
|
108
|
+
Study and extend a complete Python code transformation pipeline:
|
|
109
|
+
|
|
110
|
+
- AST analysis and manipulation
|
|
111
|
+
- Dataclass decomposition ("shattering")
|
|
112
|
+
- Automatic import management
|
|
113
|
+
- Type specialization for numerical computing
|
|
114
|
+
|
|
115
|
+
### 3. Exhaustive Test Framework
|
|
116
|
+
|
|
117
|
+
Leverage a sophisticated test suite for validating your own optimizations:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
# Test your own recipe implementation with just a few lines:
|
|
121
|
+
@pytest.fixture
|
|
122
|
+
def myCustomRecipeFixture(useThisDispatcher, pathTmpTesting):
|
|
123
|
+
myRecipe = RecipeSynthesizeFlow(
|
|
124
|
+
# Your custom configuration here
|
|
125
|
+
)
|
|
126
|
+
# ...transformation code...
|
|
127
|
+
return customDispatcher
|
|
128
|
+
|
|
129
|
+
def test_myCustomImplementation(myCustomRecipeFixture):
|
|
130
|
+
# Automatic validation against known values
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Installation and Getting Started
|
|
134
|
+
|
|
135
|
+
```sh
|
|
136
|
+
pip install mapFolding
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Try a quick calculation:
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from mapFolding import oeisIDfor_n
|
|
143
|
+
|
|
144
|
+
# Calculate ways to fold a 2×4 map
|
|
145
|
+
result = oeisIDfor_n('A001415', 4) # Returns 8
|
|
146
|
+
print(f"A 2×4 map can be folded {result} different ways")
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Mathematical Background (For the Curious)
|
|
150
|
+
|
|
151
|
+
The map folding problem was introduced by Lunnon in 1971 and connects to combinatorial geometry, computational complexity, and integer sequence analysis. The calculations provide entries to the Online Encyclopedia of Integer Sequences (OEIS).
|
|
152
|
+
|
|
153
|
+
This package implements several OEIS sequences, including:
|
|
154
|
+
|
|
155
|
+
- A001415: Number of ways to fold a 2×n strip (now calculated up to n=20!)
|
|
156
|
+
- A001418: Number of ways to fold an n×n square grid
|
|
157
|
+
|
|
158
|
+
## Explore the Repository
|
|
159
|
+
|
|
160
|
+
The repository structure reveals the package's educational value:
|
|
161
|
+
|
|
162
|
+
- `reference/`: Historical implementations and algorithm evolution
|
|
163
|
+
- `someAssemblyRequired/`: Code transformation framework
|
|
164
|
+
- `tests/`: Comprehensive test suite with fixtures for your own implementations
|
|
165
|
+
|
|
166
|
+
## Who Is This For, Really?
|
|
167
|
+
|
|
168
|
+
If you've read this far and are intrigued by computational puzzles, algorithm optimization, or Python performance techniques, this package offers a playground for exploration. It's particularly valuable for:
|
|
169
|
+
|
|
170
|
+
- Computer science students studying algorithm optimization
|
|
171
|
+
- Python developers exploring Numba and AST manipulation
|
|
172
|
+
- Computational mathematicians interested in combinatorial problems
|
|
173
|
+
- Anyone fascinated by the intersection of mathematics and computing
|
|
174
|
+
|
|
175
|
+
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.
|
|
176
|
+
|
|
177
|
+
[](https://creativecommons.org/licenses/by-nc/4.0/)
|