mapFolding 0.8.5__py3-none-any.whl → 0.9.0__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 (33) hide show
  1. mapFolding/__init__.py +66 -18
  2. mapFolding/basecamp.py +32 -17
  3. mapFolding/beDRY.py +3 -3
  4. mapFolding/oeis.py +121 -25
  5. mapFolding/someAssemblyRequired/__init__.py +48 -27
  6. mapFolding/someAssemblyRequired/_theTypes.py +11 -15
  7. mapFolding/someAssemblyRequired/_tool_Make.py +40 -12
  8. mapFolding/someAssemblyRequired/_tool_Then.py +59 -25
  9. mapFolding/someAssemblyRequired/_toolboxAntecedents.py +151 -276
  10. mapFolding/someAssemblyRequired/_toolboxContainers.py +185 -51
  11. mapFolding/someAssemblyRequired/_toolboxPython.py +165 -44
  12. mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +141 -20
  13. mapFolding/someAssemblyRequired/toolboxNumba.py +93 -52
  14. mapFolding/someAssemblyRequired/transformationTools.py +228 -138
  15. mapFolding/syntheticModules/numbaCount_doTheNeedful.py +0 -1
  16. mapFolding/theSSOT.py +147 -55
  17. mapFolding/toolboxFilesystem.py +1 -1
  18. mapfolding-0.9.0.dist-info/METADATA +177 -0
  19. mapfolding-0.9.0.dist-info/RECORD +46 -0
  20. tests/__init__.py +44 -0
  21. tests/conftest.py +75 -7
  22. tests/test_computations.py +90 -9
  23. tests/test_filesystem.py +32 -33
  24. tests/test_other.py +0 -1
  25. tests/test_tasks.py +2 -2
  26. mapFolding/noHomeYet.py +0 -32
  27. mapFolding/someAssemblyRequired/newInliner.py +0 -22
  28. mapfolding-0.8.5.dist-info/METADATA +0 -190
  29. mapfolding-0.8.5.dist-info/RECORD +0 -48
  30. {mapfolding-0.8.5.dist-info → mapfolding-0.9.0.dist-info}/WHEEL +0 -0
  31. {mapfolding-0.8.5.dist-info → mapfolding-0.9.0.dist-info}/entry_points.txt +0 -0
  32. {mapfolding-0.8.5.dist-info → mapfolding-0.9.0.dist-info}/licenses/LICENSE +0 -0
  33. {mapfolding-0.8.5.dist-info → mapfolding-0.9.0.dist-info}/top_level.txt +0 -0
mapFolding/theSSOT.py CHANGED
@@ -26,67 +26,107 @@ from types import ModuleType
26
26
  from typing import Any, TypeAlias, TypeVar
27
27
  import dataclasses
28
28
 
29
- # =============================================================================
30
- # The Wrong Way: Evaluate When Packaging
31
-
29
+ # Evaluate When Packaging https://github.com/hunterhogan/mapFolding/issues/18
32
30
  try:
33
31
  packageNamePACKAGING: str = tomli_load(Path("../pyproject.toml").open('rb'))["project"]["name"]
34
32
  except Exception:
35
33
  packageNamePACKAGING = "mapFolding"
36
34
 
37
- # The Wrong Way: Evaluate When Installing
38
-
35
+ # Evaluate When Installing https://github.com/hunterhogan/mapFolding/issues/18
39
36
  def getPathPackageINSTALLING() -> Path:
40
37
  pathPackage: Path = Path(inspect_getfile(importlib_import_module(packageNamePACKAGING)))
41
38
  if pathPackage.is_file():
42
39
  pathPackage = pathPackage.parent
43
40
  return pathPackage
44
41
 
45
- # =============================================================================
46
- # The Wrong Way: HARDCODED
42
+ # I believe these values should be dynamically determined, so I have conspicuously marked them "HARDCODED"
43
+ # and created downstream logic that assumes the values were dynamically determined.
47
44
  # Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
48
-
49
- # from mapFolding.someAssemblyRequired.synthesizeNumbaFlow.theNumbaFlow
50
45
  logicalPathModuleDispatcherHARDCODED: str = 'mapFolding.syntheticModules.numbaCount_doTheNeedful'
51
46
  callableDispatcherHARDCODED: str = 'doTheNeedful'
52
47
  concurrencyPackageHARDCODED = 'multiprocessing'
48
+ # from mapFolding.someAssemblyRequired.synthesizeNumbaFlow.theNumbaFlow
53
49
 
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)
50
+ # PackageSettings in theSSOT.py and immutability https://github.com/hunterhogan/mapFolding/issues/11
59
51
  @dataclasses.dataclass
60
52
  class PackageSettings:
53
+ """
54
+ Centralized configuration settings for the mapFolding package.
55
+
56
+ This class implements the Single Source of Truth (SSOT) principle for package
57
+ configuration, providing a consistent interface for accessing package settings,
58
+ paths, and dispatch functions. The primary instance of this class, named `The`,
59
+ is imported and used throughout the package to retrieve configuration values.
60
+ """
61
61
 
62
62
  logicalPathModuleDispatcher: str | None = None
63
+ """Logical import path to the module containing the dispatcher function."""
64
+
63
65
  callableDispatcher: str | None = None
64
- concurrencyPackage: str |None = None
66
+ """Name of the function within the dispatcher module that will be called."""
67
+
68
+ concurrencyPackage: str | None = None
69
+ """Package to use for concurrent execution (e.g., 'multiprocessing', 'numba')."""
70
+
71
+ # "Evaluate When Packaging" and "Evaluate When Installing" https://github.com/hunterhogan/mapFolding/issues/18
65
72
  dataclassIdentifier: str = dataclasses.field(default='ComputationState', metadata={'evaluateWhen': 'packaging'})
73
+ """Name of the dataclass used to track computation state."""
74
+
66
75
  dataclassInstance: str = dataclasses.field(default='state', metadata={'evaluateWhen': 'packaging'})
76
+ """Default variable name for instances of the computation state dataclass."""
77
+
67
78
  dataclassInstanceTaskDistributionSuffix: str = dataclasses.field(default='Parallel', metadata={'evaluateWhen': 'packaging'})
79
+ """Suffix added to dataclassInstance for parallel task distribution."""
80
+
68
81
  dataclassModule: str = dataclasses.field(default='theSSOT', metadata={'evaluateWhen': 'packaging'})
82
+ """Module containing the computation state dataclass definition."""
83
+
69
84
  datatypePackage: str = dataclasses.field(default='numpy', metadata={'evaluateWhen': 'packaging'})
85
+ """Package providing the numeric data types used in computation."""
86
+
70
87
  fileExtension: str = dataclasses.field(default='.py', metadata={'evaluateWhen': 'installing'})
88
+ """Default file extension for generated code files."""
89
+
71
90
  packageName: str = dataclasses.field(default = packageNamePACKAGING, metadata={'evaluateWhen': 'packaging'})
72
- pathPackage: Path = dataclasses.field(default_factory=getPathPackageINSTALLING, init=False, metadata={'evaluateWhen': 'installing'})
91
+ """Name of this package, used for import paths and configuration."""
92
+
93
+ pathPackage: Path = dataclasses.field(default_factory=getPathPackageINSTALLING, metadata={'evaluateWhen': 'installing'})
94
+ """Absolute path to the installed package directory."""
95
+
73
96
  sourceAlgorithm: str = dataclasses.field(default='theDao', metadata={'evaluateWhen': 'packaging'})
97
+ """Module containing the reference implementation of the algorithm."""
98
+
74
99
  sourceCallableDispatcher: str = dataclasses.field(default='doTheNeedful', metadata={'evaluateWhen': 'packaging'})
100
+ """Name of the function that dispatches computation in the source algorithm."""
101
+
75
102
  sourceCallableInitialize: str = dataclasses.field(default='countInitialize', metadata={'evaluateWhen': 'packaging'})
103
+ """Name of the function that initializes computation in the source algorithm."""
104
+
76
105
  sourceCallableParallel: str = dataclasses.field(default='countParallel', metadata={'evaluateWhen': 'packaging'})
106
+ """Name of the function that performs parallel computation in the source algorithm."""
107
+
77
108
  sourceCallableSequential: str = dataclasses.field(default='countSequential', metadata={'evaluateWhen': 'packaging'})
109
+ """Name of the function that performs sequential computation in the source algorithm."""
110
+
78
111
  sourceConcurrencyManagerIdentifier: str = dataclasses.field(default='submit', metadata={'evaluateWhen': 'packaging'})
112
+ """Method name used to submit tasks to the concurrency manager."""
113
+
79
114
  sourceConcurrencyManagerNamespace: str = dataclasses.field(default='concurrencyManager', metadata={'evaluateWhen': 'packaging'})
115
+ """Variable name used for the concurrency manager instance."""
116
+
80
117
  sourceConcurrencyPackage: str = dataclasses.field(default='multiprocessing', metadata={'evaluateWhen': 'packaging'})
118
+ """Default package used for concurrency in the source algorithm."""
119
+
120
+ dataclassInstanceTaskDistribution: str = dataclasses.field(default=None, metadata={'evaluateWhen': 'packaging'}) # pyright: ignore[reportAssignmentType]
121
+ """Variable name for the parallel distribution instance of the computation state."""
81
122
 
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. """
123
+ logicalPathModuleDataclass: str = dataclasses.field(default=None, metadata={'evaluateWhen': 'packaging'}) # pyright: ignore[reportAssignmentType]
124
+ """Fully qualified import path to the module containing the computation state dataclass."""
88
125
 
89
- @property # This is not a field, and that annoys me.
126
+ logicalPathModuleSourceAlgorithm: str = dataclasses.field(default=None, metadata={'evaluateWhen': 'packaging'}) # pyright: ignore[reportAssignmentType]
127
+ """Fully qualified import path to the module containing the source algorithm."""
128
+
129
+ @property
90
130
  def dispatcher(self) -> Callable[['ComputationState'], 'ComputationState']:
91
131
  """ _The_ callable that connects `countFolds` to the logic that does the work."""
92
132
  logicalPath: str = self.logicalPathModuleDispatcher or self.logicalPathModuleSourceAlgorithm
@@ -95,26 +135,20 @@ class PackageSettings:
95
135
  return getattr(moduleImported, identifier)
96
136
 
97
137
  def __post_init__(self) -> None:
98
- self.dataclassInstanceTaskDistribution = self.dataclassInstance + self.dataclassInstanceTaskDistributionSuffix
138
+ if self.dataclassInstanceTaskDistribution is None: # pyright: ignore[reportUnnecessaryComparison]
139
+ self.dataclassInstanceTaskDistribution = self.dataclassInstance + self.dataclassInstanceTaskDistributionSuffix
99
140
 
100
- self.logicalPathModuleDataclass = '.'.join([self.packageName, self.dataclassModule])
101
- self.logicalPathModuleSourceAlgorithm = '.'.join([self.packageName, self.sourceAlgorithm])
141
+ if self.logicalPathModuleDataclass is None: # pyright: ignore[reportUnnecessaryComparison]
142
+ self.logicalPathModuleDataclass = '.'.join([self.packageName, self.dataclassModule])
143
+ if self.logicalPathModuleSourceAlgorithm is None: # pyright: ignore[reportUnnecessaryComparison]
144
+ self.logicalPathModuleSourceAlgorithm = '.'.join([self.packageName, self.sourceAlgorithm])
102
145
 
103
146
  The = PackageSettings(logicalPathModuleDispatcher=logicalPathModuleDispatcherHARDCODED, callableDispatcher=callableDispatcherHARDCODED, concurrencyPackage=concurrencyPackageHARDCODED)
104
147
 
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
148
  # =============================================================================
114
149
  # Flexible Data Structure System Needs Enhanced Paradigm https://github.com/hunterhogan/mapFolding/issues/9
115
- # Efficient translation of Python scalar types to Numba types https://github.com/hunterhogan/mapFolding/issues/8
116
150
 
117
- numpyIntegerType = TypeVar('numpyIntegerType', bound=integer[Any], covariant=True)
151
+ NumPyIntegerType = TypeVar('NumPyIntegerType', bound=integer[Any], covariant=True)
118
152
 
119
153
  DatatypeLeavesTotal: TypeAlias = int
120
154
  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
@@ -132,32 +166,92 @@ Array1DFoldsTotal: TypeAlias = ndarray[tuple[int], dtype[NumPyFoldsTotal]]
132
166
 
133
167
  @dataclasses.dataclass
134
168
  class ComputationState:
135
- mapShape: tuple[DatatypeLeavesTotal, ...] = dataclasses.field(init=True, metadata={'elementConstructor': 'DatatypeLeavesTotal'}) # NOTE Python is anti-DRY, again, `DatatypeLeavesTotal` needs to match the type
169
+ """
170
+ Represents the complete state of a map folding computation.
171
+
172
+ This dataclass encapsulates all the information required to compute the number of
173
+ possible ways to fold a map, including the map dimensions, leaf connections,
174
+ computation progress, and fold counting. It serves as the central data structure
175
+ that flows through the entire computational algorithm.
176
+
177
+ Fields are categorized into:
178
+ 1. Input parameters (mapShape, leavesTotal, etc.)
179
+ 2. Core computational structures (connectionGraph, etc.)
180
+ 3. Tracking variables for the folding algorithm state
181
+ 4. Result accumulation fields (foldsTotal, groupsOfFolds)
182
+
183
+ The data structures and algorithms are based on Lunnon's 1971 paper on map folding.
184
+ """
185
+ # NOTE Python is anti-DRY, again, `DatatypeLeavesTotal` metadata needs to match the type
186
+ mapShape: tuple[DatatypeLeavesTotal, ...] = dataclasses.field(init=True, metadata={'elementConstructor': 'DatatypeLeavesTotal'})
187
+ """Dimensions of the map to be folded, as a tuple of integers."""
188
+
136
189
  leavesTotal: DatatypeLeavesTotal
190
+ """Total number of leaves (unit squares) in the map, equal to the product of all dimensions."""
191
+
137
192
  taskDivisions: DatatypeLeavesTotal
193
+ """Number of parallel tasks to divide the computation into. Zero means sequential computation."""
194
+
138
195
  concurrencyLimit: DatatypeElephino
196
+ """Maximum number of concurrent processes to use during computation."""
139
197
 
140
198
  connectionGraph: Array3D = dataclasses.field(init=False, metadata={'dtype': Array3D.__args__[1].__args__[0]}) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
199
+ """3D array encoding the connections between leaves in all dimensions."""
200
+
141
201
  dimensionsTotal: DatatypeLeavesTotal = dataclasses.field(init=False)
202
+ """Total number of dimensions in the map shape."""
203
+
204
+ # 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
205
+ countDimensionsGapped: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
206
+ """Tracks how many dimensions are gapped for each leaf."""
207
+
208
+ dimensionsUnconstrained: DatatypeLeavesTotal = dataclasses.field(default=None, init=True) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
209
+ """Number of dimensions that are not constrained in the current folding state."""
210
+
211
+ gapRangeStart: Array1DElephino = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DElephino.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
212
+ """Starting index for the gap range for each leaf."""
142
213
 
143
- countDimensionsGapped: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # type: ignore[arg-type, reportAssignmentType]
144
- dimensionsUnconstrained: DatatypeLeavesTotal = dataclasses.field(default=None, init=True) # type: ignore[assignment, reportAssignmentType]
145
- gapRangeStart: Array1DElephino = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DElephino.__args__[1].__args__[0]}) # type: ignore[arg-type, reportAssignmentType]
146
- gapsWhere: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # type: ignore[arg-type, reportAssignmentType]
147
- leafAbove: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # type: ignore[arg-type, reportAssignmentType]
148
- leafBelow: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # type: ignore[arg-type, reportAssignmentType]
149
- foldGroups: Array1DFoldsTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DFoldsTotal.__args__[1].__args__[0]}) # type: ignore[arg-type, reportAssignmentType]
214
+ gapsWhere: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
215
+ """Tracks where gaps occur in the folding pattern."""
216
+
217
+ leafAbove: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
218
+ """For each leaf, stores the index of the leaf above it in the folding pattern."""
219
+
220
+ leafBelow: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
221
+ """For each leaf, stores the index of the leaf below it in the folding pattern."""
222
+
223
+ foldGroups: Array1DFoldsTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DFoldsTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
224
+ """Accumulator for fold groups across parallel tasks."""
150
225
 
151
226
  foldsTotal: DatatypeFoldsTotal = DatatypeFoldsTotal(0)
227
+ """The final computed total number of distinct folding patterns."""
228
+
152
229
  gap1ndex: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
230
+ """Current index into gaps array during algorithm execution."""
231
+
153
232
  gap1ndexCeiling: DatatypeElephino = DatatypeElephino(0)
233
+ """Upper limit for gap index during the current algorithm phase."""
234
+
154
235
  groupsOfFolds: DatatypeFoldsTotal = dataclasses.field(default=DatatypeFoldsTotal(0), metadata={'theCountingIdentifier': True})
236
+ """Accumulator for the number of fold groups found during computation."""
237
+
155
238
  indexDimension: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
239
+ """Current dimension being processed during algorithm execution."""
240
+
156
241
  indexLeaf: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
242
+ """Current leaf index during iteration."""
243
+
157
244
  indexMiniGap: DatatypeElephino = DatatypeElephino(0)
245
+ """Index used when filtering common gaps."""
246
+
158
247
  leaf1ndex: DatatypeElephino = DatatypeElephino(1)
248
+ """Active leaf being processed in the folding algorithm. Starts at 1, not 0."""
249
+
159
250
  leafConnectee: DatatypeElephino = DatatypeElephino(0)
251
+ """Leaf that is being connected to the active leaf."""
252
+
160
253
  taskIndex: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
254
+ """Index of the current parallel task when using task divisions."""
161
255
 
162
256
  def __post_init__(self) -> None:
163
257
  from mapFolding.beDRY import getConnectionGraph, makeDataContainer
@@ -165,24 +259,22 @@ class ComputationState:
165
259
  leavesTotalAsInt = int(self.leavesTotal)
166
260
  self.connectionGraph = getConnectionGraph(self.mapShape, leavesTotalAsInt, self.__dataclass_fields__['connectionGraph'].metadata['dtype'])
167
261
 
168
- if self.dimensionsUnconstrained is None: # type: ignore
169
- self.dimensionsUnconstrained = DatatypeLeavesTotal(int(self.dimensionsTotal))
262
+ if self.dimensionsUnconstrained is None: self.dimensionsUnconstrained = DatatypeLeavesTotal(int(self.dimensionsTotal)) # pyright: ignore[reportUnnecessaryComparison]
170
263
 
171
- if self.foldGroups is None: # type: ignore
264
+ if self.foldGroups is None: # pyright: ignore[reportUnnecessaryComparison]
172
265
  self.foldGroups = makeDataContainer(max(2, int(self.taskDivisions) + 1), self.__dataclass_fields__['foldGroups'].metadata['dtype'])
173
266
  self.foldGroups[-1] = self.leavesTotal
174
267
 
175
- if self.gapsWhere is None: self.gapsWhere = makeDataContainer(leavesTotalAsInt * leavesTotalAsInt + 1, self.__dataclass_fields__['gapsWhere'].metadata['dtype']) # type: ignore
268
+ # Dataclasses, Default factories, and arguments in `ComputationState` https://github.com/hunterhogan/mapFolding/issues/12
269
+ if self.gapsWhere is None: self.gapsWhere = makeDataContainer(leavesTotalAsInt * leavesTotalAsInt + 1, self.__dataclass_fields__['gapsWhere'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
176
270
 
177
- if self.countDimensionsGapped is None: self.countDimensionsGapped = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['countDimensionsGapped'].metadata['dtype']) # type: ignore
178
- if self.gapRangeStart is None: self.gapRangeStart = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['gapRangeStart'].metadata['dtype']) # type: ignore
179
- if self.leafAbove is None: self.leafAbove = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafAbove'].metadata['dtype']) # type: ignore
180
- if self.leafBelow is None: self.leafBelow = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafBelow'].metadata['dtype']) # type: ignore
271
+ if self.countDimensionsGapped is None: self.countDimensionsGapped = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['countDimensionsGapped'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
272
+ if self.gapRangeStart is None: self.gapRangeStart = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['gapRangeStart'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
273
+ if self.leafAbove is None: self.leafAbove = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafAbove'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
274
+ if self.leafBelow is None: self.leafBelow = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafBelow'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
181
275
 
276
+ # Automatic, or not, calculation in dataclass `ComputationState` https://github.com/hunterhogan/mapFolding/issues/14
182
277
  def getFoldsTotal(self) -> None:
183
278
  self.foldsTotal = DatatypeFoldsTotal(self.foldGroups[0:-1].sum() * self.leavesTotal)
184
279
 
185
- # =============================================================================
186
- # The coping way.
187
-
188
280
  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.0
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/)
@@ -0,0 +1,46 @@
1
+ mapFolding/__init__.py,sha256=TXCWanI9snueVZMTtjLNpvG4um7vyPXmJeIvvrBuJCA,3887
2
+ mapFolding/basecamp.py,sha256=L0eduDE8-5SnEYpnI-GHs-am4ico9kEn75SuOUgibZ8,4770
3
+ mapFolding/beDRY.py,sha256=cWMHETsTofpGMKmbEB3E8NiiujmyYOVq0HW-VqI3-Pk,15272
4
+ mapFolding/oeis.py,sha256=qgmlMbqDZ1O3DKrhKttm2J0Lwm9iGGKqLhwJQNULLGM,16995
5
+ mapFolding/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ mapFolding/theDao.py,sha256=MVopt1LzhdIQYA97SEoq9bdzct6hbK0lEyPxBAAlVTc,9934
7
+ mapFolding/theSSOT.py,sha256=20Z0Cs2Sj5EZPFXE6dT_rb6iH6DTBVq-gaysemQTVPc,16829
8
+ mapFolding/toolboxFilesystem.py,sha256=5RZ9CshZwgjejH_sMG4_Kk4JX3QJLkK30OqFeL5jFWg,9974
9
+ mapFolding/reference/__init__.py,sha256=UIEU8BJR_YDzjFQcLel3XtHzOCJiOUGlGiWzOzbvhik,2206
10
+ mapFolding/reference/flattened.py,sha256=QK1xG9SllqCoi68e86Hyl9d9ATUAAFNpTQI-3zmcp5I,16072
11
+ mapFolding/reference/hunterNumba.py,sha256=iLfyqwGdAh6c5GbapnKsWhAsNsR3O-fyGGHAdohluLw,7258
12
+ mapFolding/reference/irvineJavaPort.py,sha256=UEfIX4QbPLl5jnyfYIyX5YRR3_rYvPUikK8jLehsFko,4076
13
+ mapFolding/reference/jaxCount.py,sha256=TuDNKOnyhQfuixKmIxO9Algv7dvy7KMGhgsV3h96FGE,14853
14
+ mapFolding/reference/lunnanNumpy.py,sha256=mMgrgbrBpe4nmo72ThEI-MGH0OwEHmfMPczSXHp2qKo,4357
15
+ mapFolding/reference/lunnanWhile.py,sha256=ZL8GAQtPs5nJZSgoDl5USrLSS_zs03y98y1Z9E4jOmQ,3799
16
+ mapFolding/reference/rotatedEntryPoint.py,sha256=5ughpKUT2JQhoAKgoDUdYNjgWQYPGV8v-7dWEAdDmfE,10274
17
+ mapFolding/reference/total_countPlus1vsPlusN.py,sha256=yJZAVLVdoXqHag2_N6_6CT-Q6HXBgRro-eny93-Rlpw,9307
18
+ mapFolding/reference/jobsCompleted/__init__.py,sha256=TU93ZGUW1xEkT6d9mQFn_rp5DvRy0ZslEB2Q6MF5ZDc,2596
19
+ mapFolding/reference/jobsCompleted/[2x19]/p2x19.py,sha256=_tvYtfzMWVo2VtUbIAieoscb4N8FFflgTdW4-ljBUuA,19626
20
+ mapFolding/reference/jobsCompleted/p2x19/p2x19.py,sha256=eZEw4Me4ocTt6VXoK2-Sbd5SowZtxRIbN9dZmc7OCVg,6395
21
+ mapFolding/someAssemblyRequired/__init__.py,sha256=ZtYMGjxMnl1mnwSLSBm73meaXP1P_MBd4RlAwlOyRXY,3318
22
+ mapFolding/someAssemblyRequired/_theTypes.py,sha256=YFiQI6zsrFNruvqGDXHJVH0OWXsOj9EwDrt4G59OVHA,3995
23
+ mapFolding/someAssemblyRequired/_tool_Make.py,sha256=-ISVrr09mYuo5dhJfjYs38jhSd_IjYPrheGC6RWUmBI,7373
24
+ mapFolding/someAssemblyRequired/_tool_Then.py,sha256=uc1of9ZPCp5Vrh5Qg80giatGHwT9wRzW3dWaKxGYQsg,4125
25
+ mapFolding/someAssemblyRequired/_toolboxAntecedents.py,sha256=bK5IDfHZ_p1p4rhQ1Q21FM4J8i9Yl6gD7mAOw6NJahU,10591
26
+ mapFolding/someAssemblyRequired/_toolboxContainers.py,sha256=yIad3WTL60qGdLd87Wr-FJccNyhGLD3tsFJGA0sdknU,24183
27
+ mapFolding/someAssemblyRequired/_toolboxPython.py,sha256=dkEWp4-SAc8xe-pkeCXh6iVUSolSlXSFCBun1Asn1gE,7639
28
+ mapFolding/someAssemblyRequired/getLLVMforNoReason.py,sha256=9RPU6vK_eUg64GtVFI_nZnvUryXw8gfHJs9NyDYHIvg,2745
29
+ mapFolding/someAssemblyRequired/synthesizeNumbaJob.py,sha256=7jwUcHpZk3T_PoFgLtZhYayx5VbJP1f15a2JPlZegFI,14453
30
+ mapFolding/someAssemblyRequired/toolboxNumba.py,sha256=GkBmcztmGSwm2bolK76FPPdc2c9AWrtMGztgZb3rS7Y,24381
31
+ mapFolding/someAssemblyRequired/transformationTools.py,sha256=bI8ApAb-Z-VIdRa-ORQilnT6LjejTPeTyC5nOrpDz94,23323
32
+ mapFolding/syntheticModules/__init__.py,sha256=evVFqhCGa-WZKDiLcnQWjs-Bj34eRnfSLqz_d7dFYZY,83
33
+ mapFolding/syntheticModules/numbaCount_doTheNeedful.py,sha256=vSqV1WbyZ0mXg2TNgMR9EndiitVKM8GQa72ijOUCEZ8,15659
34
+ mapfolding-0.9.0.dist-info/licenses/LICENSE,sha256=NxH5Y8BdC-gNU-WSMwim3uMbID2iNDXJz7fHtuTdXhk,19346
35
+ tests/__init__.py,sha256=UIvSWWz_anRXBELKPOdhfRoZ4hArHQTvghHrRquDHHw,1940
36
+ tests/conftest.py,sha256=G8vhDSTdTbYZUFBUKLFOUOzDL1Ja6NVZmICCh4biZas,14298
37
+ tests/test_computations.py,sha256=oT2f_7Hzis3dLgOmfzlA_I-5fG3D0NURWab0rZkARfc,6152
38
+ tests/test_filesystem.py,sha256=T2DkjBoI3lW6tCxd5BilPmUFrVukNKLjOOZVZxLM560,3004
39
+ tests/test_oeis.py,sha256=uxvwmgbnylSDdsVJfuAT0LuYLbIVFwSgdLxHm-xUGBM,5043
40
+ tests/test_other.py,sha256=zZmSZpcNsk4oeD4EHNLMNtmiz9hnwwoV62DFSQrLKwo,4258
41
+ tests/test_tasks.py,sha256=yrExYvFP23TEA3ta0IotMNmi59rwQ3Y9hA3fwvIhxTE,2851
42
+ mapfolding-0.9.0.dist-info/METADATA,sha256=yjarli2blxeiwu9i1nFOrPT6gevc6YRzIbDy8KMbbr4,7445
43
+ mapfolding-0.9.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
44
+ mapfolding-0.9.0.dist-info/entry_points.txt,sha256=F3OUeZR1XDTpoH7k3wXuRb3KF_kXTTeYhu5AGK1SiOQ,146
45
+ mapfolding-0.9.0.dist-info/top_level.txt,sha256=1gP2vFaqPwHujGwb3UjtMlLEGN-943VSYFR7V4gDqW8,17
46
+ mapfolding-0.9.0.dist-info/RECORD,,
tests/__init__.py CHANGED
@@ -0,0 +1,44 @@
1
+ """
2
+ Map Folding Test Suite and Validation Framework
3
+
4
+ This test suite provides comprehensive testing capabilities for the mapFolding package
5
+ and its optimization framework. It is specifically designed to enable both package
6
+ maintenance and custom extension testing, making it easy for users to validate their
7
+ own recipe configurations and job implementations.
8
+
9
+ ## Key Testing Capabilities
10
+
11
+ 1. **Algorithm Validation**
12
+ - Tests core algorithm correctness against known OEIS sequence values
13
+ - Validates both sequential and parallel execution paths
14
+ - Ensures consistency across different implementation strategies
15
+
16
+ 2. **Code Generation Testing**
17
+ - Tests the AST transformation pipeline from source to optimized implementations
18
+ - Validates that generated Numba-accelerated modules produce correct results
19
+ - Ensures robust code generation across different parameter sets
20
+
21
+ 3. **Job-Specific Testing**
22
+ - Tests specialized job module generation for specific map shapes
23
+ - Validates execution of the generated modules
24
+ - Verifies correct output file creation and value storage
25
+
26
+ ## Testing Your Own Implementations
27
+
28
+ This suite is designed to make it easy to test your custom recipes and jobs:
29
+
30
+ ### For Custom Recipes (RecipeSynthesizeFlow):
31
+ Copy and adapt the `syntheticDispatcherFixture` and associated tests from
32
+ `test_computations.py` to validate your customized code transformation pipelines.
33
+
34
+ ### For Custom Jobs (RecipeJob):
35
+ Copy and adapt the `test_writeJobNumba` function to test specialized job modules
36
+ for specific map shapes with your custom configurations.
37
+
38
+ The entire test infrastructure is built on fixtures and utilities that handle
39
+ complex setup and validation, allowing you to focus on your implementation details
40
+ while leveraging the existing validation framework.
41
+
42
+ See the module docstrings in `test_computations.py` and `conftest.py` for detailed
43
+ guidance on adapting these tests for your own purposes.
44
+ """