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.
Files changed (34) hide show
  1. mapFolding/__init__.py +60 -13
  2. mapFolding/basecamp.py +32 -17
  3. mapFolding/beDRY.py +4 -5
  4. mapFolding/oeis.py +94 -7
  5. mapFolding/someAssemblyRequired/RecipeJob.py +103 -0
  6. mapFolding/someAssemblyRequired/__init__.py +71 -50
  7. mapFolding/someAssemblyRequired/_theTypes.py +11 -15
  8. mapFolding/someAssemblyRequired/_tool_Make.py +36 -9
  9. mapFolding/someAssemblyRequired/_tool_Then.py +59 -25
  10. mapFolding/someAssemblyRequired/_toolboxAntecedents.py +159 -272
  11. mapFolding/someAssemblyRequired/_toolboxContainers.py +155 -70
  12. mapFolding/someAssemblyRequired/_toolboxPython.py +168 -44
  13. mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +154 -39
  14. mapFolding/someAssemblyRequired/toolboxNumba.py +72 -230
  15. mapFolding/someAssemblyRequired/transformationTools.py +370 -141
  16. mapFolding/syntheticModules/{numbaCount_doTheNeedful.py → numbaCount.py} +7 -4
  17. mapFolding/theDao.py +19 -16
  18. mapFolding/theSSOT.py +165 -62
  19. mapFolding/toolboxFilesystem.py +1 -1
  20. mapfolding-0.9.1.dist-info/METADATA +177 -0
  21. mapfolding-0.9.1.dist-info/RECORD +47 -0
  22. tests/__init__.py +44 -0
  23. tests/conftest.py +75 -7
  24. tests/test_computations.py +92 -10
  25. tests/test_filesystem.py +32 -33
  26. tests/test_other.py +0 -1
  27. tests/test_tasks.py +1 -1
  28. mapFolding/someAssemblyRequired/newInliner.py +0 -22
  29. mapfolding-0.8.6.dist-info/METADATA +0 -190
  30. mapfolding-0.8.6.dist-info/RECORD +0 -47
  31. {mapfolding-0.8.6.dist-info → mapfolding-0.9.1.dist-info}/WHEEL +0 -0
  32. {mapfolding-0.8.6.dist-info → mapfolding-0.9.1.dist-info}/entry_points.txt +0 -0
  33. {mapfolding-0.8.6.dist-info → mapfolding-0.9.1.dist-info}/licenses/LICENSE +0 -0
  34. {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: DatatypeElephino, leafConnectee: DatatypeElephino, taskIndex: DatatypeLeavesTotal) -> DatatypeFoldsTotal:
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: DatatypeElephino, 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, DatatypeElephino, DatatypeElephino, DatatypeLeavesTotal]:
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: DatatypeElephino = state.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: DatatypeElephino = state.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
- # The Wrong Way: Evaluate When Installing
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
- # The Wrong Way: HARDCODED
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
- concurrencyPackage: str |None = None
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
- pathPackage: Path = dataclasses.field(default_factory=getPathPackageINSTALLING, init=False, metadata={'evaluateWhen': 'installing'})
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(init=False, metadata={'evaluateWhen': 'packaging'})
83
- """ During parallel computation, this identifier helps to create deep copies of the dataclass instance. """
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
- @property # This is not a field, and that annoys me.
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 = self.dataclassInstance + self.dataclassInstanceTaskDistributionSuffix
139
+ if self.dataclassInstanceTaskDistribution is None: # pyright: ignore[reportUnnecessaryComparison]
140
+ self.dataclassInstanceTaskDistribution = self.dataclassInstance + self.dataclassInstanceTaskDistributionSuffix
99
141
 
100
- self.logicalPathModuleDataclass = '.'.join([self.packageName, self.dataclassModule])
101
- self.logicalPathModuleSourceAlgorithm = '.'.join([self.packageName, self.sourceAlgorithm])
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
- numpyIntegerType = TypeVar('numpyIntegerType', bound=integer[Any], covariant=True)
152
+ NumPyIntegerType = TypeVar('NumPyIntegerType', bound=integer[Any], covariant=True)
117
153
 
118
- DatatypeLeavesTotal: TypeAlias = int
119
- 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
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 = numpy_int16
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
- mapShape: tuple[DatatypeLeavesTotal, ...] = dataclasses.field(init=True, metadata={'elementConstructor': 'DatatypeLeavesTotal'}) # NOTE Python is anti-DRY, again, `DatatypeLeavesTotal` needs to match the type
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
- countDimensionsGapped: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # type: ignore[arg-type, reportAssignmentType]
143
- dimensionsUnconstrained: DatatypeLeavesTotal = dataclasses.field(default=None, init=True) # type: ignore[assignment, reportAssignmentType]
144
- gapRangeStart: Array1DElephino = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DElephino.__args__[1].__args__[0]}) # type: ignore[arg-type, reportAssignmentType]
145
- gapsWhere: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # type: ignore[arg-type, reportAssignmentType]
146
- leafAbove: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # type: ignore[arg-type, reportAssignmentType]
147
- leafBelow: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # type: ignore[arg-type, reportAssignmentType]
148
- foldGroups: Array1DFoldsTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DFoldsTotal.__args__[1].__args__[0]}) # type: ignore[arg-type, reportAssignmentType]
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
- leaf1ndex: DatatypeElephino = DatatypeElephino(1)
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: # type: ignore
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: # type: ignore
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
- if self.gapsWhere is None: self.gapsWhere = makeDataContainer(leavesTotalAsInt * leavesTotalAsInt + 1, self.__dataclass_fields__['gapsWhere'].metadata['dtype']) # type: ignore
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']) # type: ignore
177
- if self.gapRangeStart is None: self.gapRangeStart = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['gapRangeStart'].metadata['dtype']) # type: ignore
178
- if self.leafAbove is None: self.leafAbove = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafAbove'].metadata['dtype']) # type: ignore
179
- if self.leafBelow is None: self.leafBelow = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafBelow'].metadata['dtype']) # type: ignore
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
@@ -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.theSSOT import The
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
+ [![pip install mapFolding](https://img.shields.io/badge/pip%20install-mapFolding-gray.svg?colorB=3b434b)](https://pypi.org/project/mapFolding/)
56
+ [![Python Tests](https://github.com/hunterhogan/mapFolding/actions/workflows/pythonTests.yml/badge.svg)](https://github.com/hunterhogan/mapFolding/actions/workflows/pythonTests.yml)
57
+ [![License: CC-BY-NC-4.0](https://img.shields.io/badge/License-CC_BY--NC_4.0-3b434b)](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
+ [![CC-BY-NC-4.0](https://github.com/hunterhogan/mapFolding/blob/main/CC-BY-NC-4.0.svg)](https://creativecommons.org/licenses/by-nc/4.0/)