mapFolding 0.11.0__py3-none-any.whl → 0.11.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mapFolding/__init__.py +18 -75
- mapFolding/basecamp.py +13 -10
- mapFolding/beDRY.py +113 -2
- mapFolding/dataBaskets.py +24 -2
- mapFolding/{toolboxFilesystem.py → filesystemToolkit.py} +3 -3
- mapFolding/infoBooth.py +96 -0
- mapFolding/oeis.py +3 -2
- mapFolding/someAssemblyRequired/RecipeJob.py +3 -4
- mapFolding/someAssemblyRequired/Z0Z_makeSomeModules.py +187 -22
- mapFolding/someAssemblyRequired/__init__.py +10 -5
- mapFolding/someAssemblyRequired/_toolIfThis.py +10 -4
- mapFolding/someAssemblyRequired/{_toolboxContainers.py → _toolkitContainers.py} +15 -17
- mapFolding/someAssemblyRequired/makeJobTheorem2Numba.py +9 -8
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +5 -3
- mapFolding/someAssemblyRequired/{toolboxNumba.py → toolkitNumba.py} +2 -2
- mapFolding/someAssemblyRequired/transformationTools.py +17 -50
- mapFolding/syntheticModules/countParallel.py +98 -0
- mapFolding/syntheticModules/dataPacking.py +1 -1
- mapFolding/syntheticModules/numbaCount.py +189 -188
- mapFolding/theDao.py +1 -1
- mapFolding/theSSOT.py +4 -243
- {mapfolding-0.11.0.dist-info → mapfolding-0.11.2.dist-info}/METADATA +16 -8
- mapfolding-0.11.2.dist-info/RECORD +56 -0
- {mapfolding-0.11.0.dist-info → mapfolding-0.11.2.dist-info}/WHEEL +1 -1
- tests/conftest.py +7 -9
- tests/test_computations.py +1 -1
- tests/test_filesystem.py +1 -2
- tests/test_other.py +1 -1
- tests/test_tasks.py +1 -3
- mapfolding-0.11.0.dist-info/RECORD +0 -54
- {mapfolding-0.11.0.dist-info → mapfolding-0.11.2.dist-info}/entry_points.txt +0 -0
- {mapfolding-0.11.0.dist-info → mapfolding-0.11.2.dist-info}/licenses/LICENSE +0 -0
- {mapfolding-0.11.0.dist-info → mapfolding-0.11.2.dist-info}/top_level.txt +0 -0
mapFolding/__init__.py
CHANGED
|
@@ -1,76 +1,13 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Map folding enumeration and counting algorithms with advanced optimization capabilities.
|
|
3
|
-
|
|
4
|
-
This package implements algorithms to count and enumerate the distinct ways a rectangular map can be folded, based on
|
|
5
|
-
the mathematical problem described in Lunnon's 1971 paper. It provides multiple layers of functionality, from high-level
|
|
6
|
-
user interfaces to sophisticated algorithmic optimizations and code transformation tools.
|
|
7
|
-
|
|
8
|
-
Core modules:
|
|
9
|
-
- basecamp: Public API with simplified interfaces for end users
|
|
10
|
-
- theDao: Core computational algorithm using a functional state-transformation approach
|
|
11
|
-
- beDRY: Core utility functions implementing consistent data handling, validation, and resource management across the
|
|
12
|
-
package's computational assembly-line
|
|
13
|
-
- theSSOT: Single Source of Truth for configuration, types, and state management
|
|
14
|
-
- toolboxFilesystem: Cross-platform file management services for storing and retrieving computation results with robust
|
|
15
|
-
error handling and fallback mechanisms
|
|
16
|
-
- oeis: Interface to the Online Encyclopedia of Integer Sequences for known results
|
|
17
|
-
|
|
18
|
-
Extended functionality:
|
|
19
|
-
- someAssemblyRequired: Code transformation framework that optimizes the core algorithm through AST manipulation,
|
|
20
|
-
dataclass transformation, and compilation techniques
|
|
21
|
-
- The system converts readable code into high-performance implementations through a systematic analysis and
|
|
22
|
-
transformation assembly line
|
|
23
|
-
- Provides tools to "shatter" complex dataclasses into primitive components, enabling compatibility with Numba and
|
|
24
|
-
other optimization frameworks
|
|
25
|
-
- Creates specialized implementations tailored for specific input parameters
|
|
26
|
-
|
|
27
|
-
Testing and extension:
|
|
28
|
-
- tests: Comprehensive test suite designed for both verification and extension
|
|
29
|
-
- Provides fixtures and utilities that simplify testing of custom implementations
|
|
30
|
-
- Enables users to validate their own recipes and job configurations with minimal code
|
|
31
|
-
- Offers standardized testing patterns that maintain consistency across the codebase
|
|
32
|
-
- See tests/__init__.py for detailed documentation on extending the test suite
|
|
33
|
-
|
|
34
|
-
Special directories:
|
|
35
|
-
- .cache/: Stores cached data from external sources like OEIS to improve performance
|
|
36
|
-
- syntheticModules/: Contains dynamically generated, optimized implementations of the core algorithm created by the code
|
|
37
|
-
transformation framework
|
|
38
|
-
- reference/: Historical implementations and educational resources for algorithm exploration
|
|
39
|
-
- reference/jobsCompleted/: Contains successful computations for previously unknown values, including first-ever
|
|
40
|
-
calculations for 2x19 and 2x20 maps (OEIS A001415)
|
|
41
|
-
|
|
42
|
-
This package balances algorithm readability and understandability with high-performance computation capabilities,
|
|
43
|
-
allowing users to compute map folding totals for larger dimensions than previously feasible while also providing a
|
|
44
|
-
foundation for exploring advanced code transformation techniques.
|
|
45
|
-
"""
|
|
46
|
-
|
|
47
1
|
from typing import Any, TypeAlias
|
|
48
2
|
import sys
|
|
49
3
|
|
|
50
4
|
yourPythonIsOld: TypeAlias = Any
|
|
51
5
|
# ruff: noqa: E402
|
|
52
6
|
|
|
53
|
-
# if sys.version_info >= (3, 12):
|
|
54
|
-
# from ast import (
|
|
55
|
-
# ParamSpec as astDOTParamSpec,
|
|
56
|
-
# type_param as astDOTtype_param,
|
|
57
|
-
# TypeAlias as astDOTTypeAlias,
|
|
58
|
-
# TypeVar as astDOTTypeVar,
|
|
59
|
-
# TypeVarTuple as astDOTTypeVarTuple,
|
|
60
|
-
# )
|
|
61
|
-
# else:
|
|
62
|
-
# astDOTParamSpec: TypeAlias = yourPythonIsOld
|
|
63
|
-
# astDOTtype_param: TypeAlias = yourPythonIsOld
|
|
64
|
-
# astDOTTypeAlias: TypeAlias = yourPythonIsOld
|
|
65
|
-
# astDOTTypeVar: TypeAlias = yourPythonIsOld
|
|
66
|
-
# astDOTTypeVarTuple: TypeAlias = yourPythonIsOld
|
|
67
|
-
|
|
68
7
|
if sys.version_info >= (3, 11):
|
|
69
|
-
# from ast import TryStar as astDOTTryStar
|
|
70
8
|
from typing import TypedDict as TypedDict
|
|
71
9
|
from typing import NotRequired as NotRequired
|
|
72
10
|
else:
|
|
73
|
-
astDOTTryStar: TypeAlias = yourPythonIsOld
|
|
74
11
|
try:
|
|
75
12
|
from typing_extensions import TypedDict as TypedDict
|
|
76
13
|
from typing_extensions import NotRequired as NotRequired
|
|
@@ -93,8 +30,23 @@ from mapFolding.datatypes import (
|
|
|
93
30
|
NumPyLeavesTotal as NumPyLeavesTotal,
|
|
94
31
|
)
|
|
95
32
|
|
|
96
|
-
from mapFolding.theSSOT import
|
|
33
|
+
from mapFolding.theSSOT import PackageSettings as PackageSettings, packageSettings as packageSettings
|
|
34
|
+
|
|
35
|
+
from mapFolding.beDRY import (
|
|
97
36
|
ComputationState as ComputationState,
|
|
37
|
+
getConnectionGraph as getConnectionGraph,
|
|
38
|
+
getLeavesTotal as getLeavesTotal,
|
|
39
|
+
getTaskDivisions as getTaskDivisions,
|
|
40
|
+
makeDataContainer as makeDataContainer,
|
|
41
|
+
outfitCountFolds as outfitCountFolds,
|
|
42
|
+
setProcessorLimit as setProcessorLimit,
|
|
43
|
+
validateListDimensions as validateListDimensions,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
from mapFolding.dataBaskets import MapFoldingState as MapFoldingState
|
|
47
|
+
|
|
48
|
+
from mapFolding.infoBooth import (
|
|
49
|
+
PackageInformation as PackageInformation,
|
|
98
50
|
raiseIfNoneGitHubIssueNumber3 as raiseIfNoneGitHubIssueNumber3,
|
|
99
51
|
The as The,
|
|
100
52
|
)
|
|
@@ -104,23 +56,14 @@ from mapFolding.theDao import (
|
|
|
104
56
|
doTheNeedful as doTheNeedful,
|
|
105
57
|
)
|
|
106
58
|
|
|
107
|
-
from mapFolding.
|
|
108
|
-
|
|
109
|
-
getTaskDivisions as getTaskDivisions,
|
|
110
|
-
outfitCountFolds as outfitCountFolds,
|
|
111
|
-
setProcessorLimit as setProcessorLimit,
|
|
112
|
-
validateListDimensions as validateListDimensions,
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
from mapFolding.toolboxFilesystem import (
|
|
59
|
+
from mapFolding.filesystemToolkit import (
|
|
60
|
+
getFilenameFoldsTotal as getFilenameFoldsTotal,
|
|
116
61
|
getPathFilenameFoldsTotal as getPathFilenameFoldsTotal,
|
|
117
62
|
getPathRootJobDEFAULT as getPathRootJobDEFAULT,
|
|
118
63
|
saveFoldsTotal as saveFoldsTotal,
|
|
119
64
|
saveFoldsTotalFAILearly as saveFoldsTotalFAILearly,
|
|
120
65
|
)
|
|
121
66
|
|
|
122
|
-
from Z0Z_tools import writeStringToHere as writeStringToHere
|
|
123
|
-
|
|
124
67
|
from mapFolding.basecamp import countFolds as countFolds
|
|
125
68
|
|
|
126
69
|
from mapFolding.oeis import (
|
mapFolding/basecamp.py
CHANGED
|
@@ -11,9 +11,7 @@ appropriate algorithm implementation, and optional persistence of results.
|
|
|
11
11
|
|
|
12
12
|
from collections.abc import Sequence
|
|
13
13
|
from mapFolding import (
|
|
14
|
-
ComputationState,
|
|
15
14
|
getPathFilenameFoldsTotal,
|
|
16
|
-
outfitCountFolds,
|
|
17
15
|
saveFoldsTotal,
|
|
18
16
|
saveFoldsTotalFAILearly,
|
|
19
17
|
setProcessorLimit,
|
|
@@ -163,20 +161,25 @@ def countFolds(listDimensions: Sequence[int] | None = None
|
|
|
163
161
|
from mapFolding.syntheticModules.initializeCount import initializeGroupsOfFolds
|
|
164
162
|
mapFoldingState = initializeGroupsOfFolds(mapFoldingState)
|
|
165
163
|
|
|
166
|
-
from mapFolding.syntheticModules.dataPacking import
|
|
167
|
-
mapFoldingState =
|
|
164
|
+
from mapFolding.syntheticModules.dataPacking import sequential
|
|
165
|
+
mapFoldingState = sequential(mapFoldingState)
|
|
168
166
|
|
|
169
167
|
foldsTotal = mapFoldingState.foldsTotal
|
|
170
168
|
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
elif taskDivisions > 1:
|
|
170
|
+
from mapFolding.dataBaskets import ParallelMapFoldingState
|
|
171
|
+
parallelMapFoldingState: ParallelMapFoldingState = ParallelMapFoldingState(mapShape, taskDivisions=taskDivisions)
|
|
172
|
+
|
|
173
|
+
from mapFolding.syntheticModules.countParallel import doTheNeedful
|
|
174
|
+
foldsTotal, listStatesParallel = doTheNeedful(parallelMapFoldingState, concurrencyLimit)
|
|
173
175
|
|
|
174
176
|
else:
|
|
175
|
-
|
|
176
|
-
|
|
177
|
+
from mapFolding.dataBaskets import MapFoldingState
|
|
178
|
+
mapFoldingState: MapFoldingState = MapFoldingState(mapShape)
|
|
177
179
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
+
from mapFolding.syntheticModules.daoOfMapFolding import doTheNeedful
|
|
181
|
+
mapFoldingState = doTheNeedful(mapFoldingState)
|
|
182
|
+
foldsTotal = mapFoldingState.foldsTotal
|
|
180
183
|
|
|
181
184
|
# Follow memorialization instructions ===========================================
|
|
182
185
|
|
mapFolding/beDRY.py
CHANGED
|
@@ -18,12 +18,13 @@ These utilities form a stable internal API that other modules depend on, particu
|
|
|
18
18
|
theDao (core algorithm), and the synthetic module generators that produce optimized implementations.
|
|
19
19
|
"""
|
|
20
20
|
from collections.abc import Sequence
|
|
21
|
-
from mapFolding import
|
|
21
|
+
from mapFolding import Array1DElephino, Array1DFoldsTotal, Array1DLeavesTotal, Array3D, DatatypeElephino, DatatypeFoldsTotal, DatatypeLeavesTotal, NumPyIntegerType
|
|
22
22
|
from numpy import dtype as numpy_dtype, int64 as numpy_int64, ndarray
|
|
23
23
|
from sys import maxsize as sysMaxsize
|
|
24
24
|
from typing import Any
|
|
25
25
|
from Z0Z_tools import defineConcurrencyLimit, intInnit, oopsieKwargsie
|
|
26
26
|
import numpy
|
|
27
|
+
import dataclasses
|
|
27
28
|
|
|
28
29
|
def getLeavesTotal(mapShape: tuple[int, ...]) -> int:
|
|
29
30
|
"""
|
|
@@ -59,7 +60,6 @@ def getTaskDivisions(computationDivisions: int | str | None, concurrencyLimit: i
|
|
|
59
60
|
"""
|
|
60
61
|
Determines whether to divide the computation into tasks and how many divisions.
|
|
61
62
|
|
|
62
|
-
|
|
63
63
|
Parameters
|
|
64
64
|
----------
|
|
65
65
|
computationDivisions: None
|
|
@@ -208,6 +208,117 @@ def makeDataContainer(shape: int | tuple[int, ...], datatype: type[NumPyIntegerT
|
|
|
208
208
|
"""
|
|
209
209
|
return numpy.zeros(shape, dtype=datatype)
|
|
210
210
|
|
|
211
|
+
|
|
212
|
+
@dataclasses.dataclass
|
|
213
|
+
class ComputationState:
|
|
214
|
+
"""
|
|
215
|
+
Represents the complete state of a map folding computation.
|
|
216
|
+
|
|
217
|
+
This dataclass encapsulates all the information required to compute the number of possible ways to fold a map,
|
|
218
|
+
including the map dimensions, leaf connections, computation progress, and fold counting. It serves as the central
|
|
219
|
+
data structure that flows through the entire computational algorithm.
|
|
220
|
+
|
|
221
|
+
Fields are categorized into:
|
|
222
|
+
1. Input parameters (`mapShape`, `leavesTotal`, etc.).
|
|
223
|
+
2. Core computational structures (`connectionGraph`, etc.).
|
|
224
|
+
3. Tracking variables for the folding algorithm state.
|
|
225
|
+
4. Result accumulation fields (`foldsTotal`, `groupsOfFolds`).
|
|
226
|
+
"""
|
|
227
|
+
# NOTE Python is anti-DRY, again, `DatatypeLeavesTotal` metadata needs to match the type
|
|
228
|
+
mapShape: tuple[DatatypeLeavesTotal, ...] = dataclasses.field(init=True, metadata={'elementConstructor': 'DatatypeLeavesTotal'})
|
|
229
|
+
"""Dimensions of the map to be folded, as a tuple of integers."""
|
|
230
|
+
|
|
231
|
+
leavesTotal: DatatypeLeavesTotal
|
|
232
|
+
"""Total number of leaves (unit squares) in the map, equal to the product of all dimensions."""
|
|
233
|
+
|
|
234
|
+
taskDivisions: DatatypeLeavesTotal
|
|
235
|
+
"""Number of parallel tasks to divide the computation into. Zero means sequential computation."""
|
|
236
|
+
|
|
237
|
+
concurrencyLimit: DatatypeElephino
|
|
238
|
+
"""Maximum number of concurrent processes to use during computation."""
|
|
239
|
+
|
|
240
|
+
connectionGraph: Array3D = dataclasses.field(init=False, metadata={'dtype': Array3D.__args__[1].__args__[0]}) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
|
|
241
|
+
"""3D array encoding the connections between leaves in all dimensions."""
|
|
242
|
+
|
|
243
|
+
dimensionsTotal: DatatypeLeavesTotal = dataclasses.field(init=False)
|
|
244
|
+
"""Total number of dimensions in the map shape."""
|
|
245
|
+
|
|
246
|
+
# 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
|
|
247
|
+
countDimensionsGapped: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
248
|
+
"""Tracks how many dimensions are gapped for each leaf."""
|
|
249
|
+
|
|
250
|
+
dimensionsUnconstrained: DatatypeLeavesTotal = dataclasses.field(default=None, init=True) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
251
|
+
"""Number of dimensions that are not constrained in the current folding state."""
|
|
252
|
+
|
|
253
|
+
gapRangeStart: Array1DElephino = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DElephino.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
254
|
+
"""Starting index for the gap range for each leaf."""
|
|
255
|
+
|
|
256
|
+
gapsWhere: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
257
|
+
"""Tracks where gaps occur in the folding pattern."""
|
|
258
|
+
|
|
259
|
+
leafAbove: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
260
|
+
"""For each leaf, stores the index of the leaf above it in the folding pattern."""
|
|
261
|
+
|
|
262
|
+
leafBelow: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
263
|
+
"""For each leaf, stores the index of the leaf below it in the folding pattern."""
|
|
264
|
+
|
|
265
|
+
foldGroups: Array1DFoldsTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DFoldsTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
266
|
+
"""Accumulator for fold groups across parallel tasks."""
|
|
267
|
+
|
|
268
|
+
foldsTotal: DatatypeFoldsTotal = DatatypeFoldsTotal(0)
|
|
269
|
+
"""The final computed total number of distinct folding patterns."""
|
|
270
|
+
|
|
271
|
+
gap1ndex: DatatypeElephino = DatatypeElephino(0)
|
|
272
|
+
"""Current index into gaps array during algorithm execution."""
|
|
273
|
+
|
|
274
|
+
gap1ndexCeiling: DatatypeElephino = DatatypeElephino(0)
|
|
275
|
+
"""Upper limit for gap index during the current algorithm phase."""
|
|
276
|
+
|
|
277
|
+
groupsOfFolds: DatatypeFoldsTotal = dataclasses.field(default=DatatypeFoldsTotal(0), metadata={'theCountingIdentifier': True})
|
|
278
|
+
"""Accumulator for the number of fold groups found during computation."""
|
|
279
|
+
|
|
280
|
+
indexDimension: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
|
|
281
|
+
"""Current dimension being processed during algorithm execution."""
|
|
282
|
+
|
|
283
|
+
indexLeaf: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
|
|
284
|
+
"""Current leaf index during iteration."""
|
|
285
|
+
|
|
286
|
+
indexMiniGap: DatatypeElephino = DatatypeElephino(0)
|
|
287
|
+
"""Index used when filtering common gaps."""
|
|
288
|
+
|
|
289
|
+
leaf1ndex: DatatypeLeavesTotal = DatatypeLeavesTotal(1)
|
|
290
|
+
"""Active leaf being processed in the folding algorithm. Starts at 1, not 0."""
|
|
291
|
+
|
|
292
|
+
leafConnectee: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
|
|
293
|
+
"""Leaf that is being connected to the active leaf."""
|
|
294
|
+
|
|
295
|
+
taskIndex: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
|
|
296
|
+
"""Index of the current parallel task when using task divisions."""
|
|
297
|
+
|
|
298
|
+
def __post_init__(self) -> None:
|
|
299
|
+
from mapFolding.beDRY import getConnectionGraph, makeDataContainer
|
|
300
|
+
self.dimensionsTotal = DatatypeLeavesTotal(len(self.mapShape))
|
|
301
|
+
leavesTotalAsInt = int(self.leavesTotal)
|
|
302
|
+
self.connectionGraph = getConnectionGraph(self.mapShape, leavesTotalAsInt, self.__dataclass_fields__['connectionGraph'].metadata['dtype'])
|
|
303
|
+
|
|
304
|
+
if self.dimensionsUnconstrained is None: self.dimensionsUnconstrained = DatatypeLeavesTotal(int(self.dimensionsTotal)) # pyright: ignore[reportUnnecessaryComparison]
|
|
305
|
+
|
|
306
|
+
if self.foldGroups is None: # pyright: ignore[reportUnnecessaryComparison]
|
|
307
|
+
self.foldGroups = makeDataContainer(max(2, int(self.taskDivisions) + 1), self.__dataclass_fields__['foldGroups'].metadata['dtype'])
|
|
308
|
+
self.foldGroups[-1] = self.leavesTotal
|
|
309
|
+
|
|
310
|
+
# Dataclasses, Default factories, and arguments in `ComputationState` https://github.com/hunterhogan/mapFolding/issues/12
|
|
311
|
+
if self.gapsWhere is None: self.gapsWhere = makeDataContainer(leavesTotalAsInt * leavesTotalAsInt + 1, self.__dataclass_fields__['gapsWhere'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
|
|
312
|
+
|
|
313
|
+
if self.countDimensionsGapped is None: self.countDimensionsGapped = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['countDimensionsGapped'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
|
|
314
|
+
if self.gapRangeStart is None: self.gapRangeStart = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['gapRangeStart'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
|
|
315
|
+
if self.leafAbove is None: self.leafAbove = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafAbove'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
|
|
316
|
+
if self.leafBelow is None: self.leafBelow = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafBelow'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
|
|
317
|
+
|
|
318
|
+
# Automatic, or not, calculation in dataclass `ComputationState` https://github.com/hunterhogan/mapFolding/issues/14
|
|
319
|
+
def getFoldsTotal(self) -> None:
|
|
320
|
+
self.foldsTotal = DatatypeFoldsTotal(self.foldGroups[0:-1].sum() * self.leavesTotal)
|
|
321
|
+
|
|
211
322
|
def outfitCountFolds(mapShape: tuple[int, ...], computationDivisions: int | str | None = None, concurrencyLimit: int = 1) -> ComputationState:
|
|
212
323
|
"""
|
|
213
324
|
Initialize a `ComputationState` with validated parameters for map folding calculation.
|
mapFolding/dataBaskets.py
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
from mapFolding
|
|
2
|
-
|
|
1
|
+
from mapFolding import (
|
|
2
|
+
Array1DElephino,
|
|
3
|
+
Array1DLeavesTotal,
|
|
4
|
+
Array3D,
|
|
5
|
+
DatatypeElephino,
|
|
6
|
+
DatatypeFoldsTotal,
|
|
7
|
+
DatatypeLeavesTotal,
|
|
8
|
+
getConnectionGraph,
|
|
9
|
+
getLeavesTotal,
|
|
10
|
+
makeDataContainer,
|
|
11
|
+
)
|
|
3
12
|
import dataclasses
|
|
4
13
|
|
|
5
14
|
@dataclasses.dataclass
|
|
@@ -48,6 +57,19 @@ class MapFoldingState:
|
|
|
48
57
|
if self.leafAbove is None: self.leafAbove = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafAbove'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
|
|
49
58
|
if self.leafBelow is None: self.leafBelow = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafBelow'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
|
|
50
59
|
|
|
60
|
+
@dataclasses.dataclass
|
|
61
|
+
class ParallelMapFoldingState(MapFoldingState):
|
|
62
|
+
taskDivisions: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
|
|
63
|
+
"""Number of tasks into which to divide the computation. If the value is greater than `leavesTotal`, the computation will be wrong. Default is `leavesTotal`."""
|
|
64
|
+
|
|
65
|
+
taskIndex: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
|
|
66
|
+
"""Index of the current task when using task divisions."""
|
|
67
|
+
|
|
68
|
+
def __post_init__(self) -> None:
|
|
69
|
+
super().__post_init__()
|
|
70
|
+
if self.taskDivisions == 0:
|
|
71
|
+
self.taskDivisions = DatatypeLeavesTotal(int(self.leavesTotal))
|
|
72
|
+
|
|
51
73
|
@dataclasses.dataclass
|
|
52
74
|
class LeafSequenceState(MapFoldingState):
|
|
53
75
|
leafSequence: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
@@ -21,7 +21,7 @@ The functions here adhere to a consistent approach to path handling:
|
|
|
21
21
|
- Progressive fallback strategies for saving critical computation results.
|
|
22
22
|
- Preemptive filesystem validation to detect issues before computation begins.
|
|
23
23
|
"""
|
|
24
|
-
from mapFolding import
|
|
24
|
+
from mapFolding import packageSettings
|
|
25
25
|
from os import PathLike
|
|
26
26
|
from pathlib import Path, PurePath
|
|
27
27
|
from sys import modules as sysModules
|
|
@@ -101,9 +101,9 @@ def getPathRootJobDEFAULT() -> Path:
|
|
|
101
101
|
- For Google Colab, uses a specific path in Google Drive.
|
|
102
102
|
- Creates the directory if it doesn't exist.
|
|
103
103
|
"""
|
|
104
|
-
pathJobDEFAULT = Path(platformdirs.user_data_dir(appname=
|
|
104
|
+
pathJobDEFAULT = Path(platformdirs.user_data_dir(appname=packageSettings.packageName, appauthor=False, ensure_exists=True))
|
|
105
105
|
if 'google.colab' in sysModules:
|
|
106
|
-
pathJobDEFAULT = Path("/content/drive/MyDrive") /
|
|
106
|
+
pathJobDEFAULT = Path("/content/drive/MyDrive") / packageSettings.packageName
|
|
107
107
|
pathJobDEFAULT.mkdir(parents=True, exist_ok=True)
|
|
108
108
|
return pathJobDEFAULT
|
|
109
109
|
|
mapFolding/infoBooth.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from importlib import import_module as importlib_import_module
|
|
3
|
+
from mapFolding import ComputationState, PackageSettings
|
|
4
|
+
from types import ModuleType
|
|
5
|
+
import dataclasses
|
|
6
|
+
|
|
7
|
+
@dataclasses.dataclass
|
|
8
|
+
class PackageInformation(PackageSettings):
|
|
9
|
+
"""
|
|
10
|
+
In _all_ of the root directory and "syntheticModules", the _only_ `PackageInformation` that are used are:
|
|
11
|
+
The.concurrencyPackage
|
|
12
|
+
The.dispatcher
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
logicalPathModuleDispatcher: str | None = None
|
|
16
|
+
"""Logical import path to the module containing the dispatcher function."""
|
|
17
|
+
|
|
18
|
+
callableDispatcher: str | None = None
|
|
19
|
+
"""Name of the function within the dispatcher module that will be called."""
|
|
20
|
+
|
|
21
|
+
concurrencyPackage: str | None = None
|
|
22
|
+
"""Package to use for concurrent execution (e.g., 'multiprocessing', 'numba')."""
|
|
23
|
+
|
|
24
|
+
# "Evaluate When Packaging" and "Evaluate When Installing" https://github.com/hunterhogan/mapFolding/issues/18
|
|
25
|
+
dataclassIdentifier: str = dataclasses.field(default='ComputationState', metadata={'evaluateWhen': 'packaging'})
|
|
26
|
+
"""Name of the dataclass used to track computation state."""
|
|
27
|
+
|
|
28
|
+
dataclassInstance: str = dataclasses.field(default='state', metadata={'evaluateWhen': 'packaging'})
|
|
29
|
+
"""Default variable name for instances of the computation state dataclass."""
|
|
30
|
+
|
|
31
|
+
dataclassInstanceTaskDistributionSuffix: str = dataclasses.field(default='Parallel', metadata={'evaluateWhen': 'packaging'})
|
|
32
|
+
"""Suffix added to dataclassInstance for parallel task distribution."""
|
|
33
|
+
|
|
34
|
+
dataclassModule: str = dataclasses.field(default='beDRY', metadata={'evaluateWhen': 'packaging'})
|
|
35
|
+
"""Module containing the computation state dataclass definition."""
|
|
36
|
+
|
|
37
|
+
datatypePackage: str = dataclasses.field(default='numpy', metadata={'evaluateWhen': 'packaging'})
|
|
38
|
+
"""Package providing the numeric data types used in computation."""
|
|
39
|
+
|
|
40
|
+
sourceAlgorithm: str = dataclasses.field(default='theDao', metadata={'evaluateWhen': 'packaging'})
|
|
41
|
+
"""Module containing the reference implementation of the algorithm."""
|
|
42
|
+
|
|
43
|
+
sourceCallableDispatcher: str = dataclasses.field(default='doTheNeedful', metadata={'evaluateWhen': 'packaging'})
|
|
44
|
+
"""Name of the function that dispatches computation in the source algorithm."""
|
|
45
|
+
|
|
46
|
+
sourceCallableInitialize: str = dataclasses.field(default='countInitialize', metadata={'evaluateWhen': 'packaging'})
|
|
47
|
+
"""Name of the function that initializes computation in the source algorithm."""
|
|
48
|
+
|
|
49
|
+
sourceCallableParallel: str = dataclasses.field(default='countParallel', metadata={'evaluateWhen': 'packaging'})
|
|
50
|
+
"""Name of the function that performs parallel computation in the source algorithm."""
|
|
51
|
+
|
|
52
|
+
sourceCallableSequential: str = dataclasses.field(default='countSequential', metadata={'evaluateWhen': 'packaging'})
|
|
53
|
+
"""Name of the function that performs sequential computation in the source algorithm."""
|
|
54
|
+
|
|
55
|
+
sourceConcurrencyManagerIdentifier: str = dataclasses.field(default='submit', metadata={'evaluateWhen': 'packaging'})
|
|
56
|
+
"""Method name used to submit tasks to the concurrency manager."""
|
|
57
|
+
|
|
58
|
+
sourceConcurrencyManagerNamespace: str = dataclasses.field(default='concurrencyManager', metadata={'evaluateWhen': 'packaging'})
|
|
59
|
+
"""Variable name used for the concurrency manager instance."""
|
|
60
|
+
|
|
61
|
+
sourceConcurrencyPackage: str = dataclasses.field(default='multiprocessing', metadata={'evaluateWhen': 'packaging'})
|
|
62
|
+
"""Default package used for concurrency in the source algorithm."""
|
|
63
|
+
|
|
64
|
+
dataclassInstanceTaskDistribution: str = dataclasses.field(default=None, metadata={'evaluateWhen': 'packaging'}) # pyright: ignore[reportAssignmentType]
|
|
65
|
+
"""Variable name for the parallel distribution instance of the computation state."""
|
|
66
|
+
|
|
67
|
+
logicalPathModuleDataclass: str = dataclasses.field(default=None, metadata={'evaluateWhen': 'packaging'}) # pyright: ignore[reportAssignmentType]
|
|
68
|
+
"""Fully qualified import path to the module containing the computation state dataclass."""
|
|
69
|
+
|
|
70
|
+
logicalPathModuleSourceAlgorithm: str = dataclasses.field(default=None, metadata={'evaluateWhen': 'packaging'}) # pyright: ignore[reportAssignmentType]
|
|
71
|
+
"""Fully qualified import path to the module containing the source algorithm."""
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def dispatcher(self) -> Callable[['ComputationState'], 'ComputationState']:
|
|
75
|
+
""" _The_ callable that connects `countFolds` to the logic that does the work."""
|
|
76
|
+
logicalPath: str = self.logicalPathModuleDispatcher or self.logicalPathModuleSourceAlgorithm
|
|
77
|
+
identifier: str = self.callableDispatcher or self.sourceCallableDispatcher
|
|
78
|
+
moduleImported: ModuleType = importlib_import_module(logicalPath)
|
|
79
|
+
return getattr(moduleImported, identifier)
|
|
80
|
+
|
|
81
|
+
def __post_init__(self) -> None:
|
|
82
|
+
if self.dataclassInstanceTaskDistribution is None: # pyright: ignore[reportUnnecessaryComparison]
|
|
83
|
+
self.dataclassInstanceTaskDistribution = self.dataclassInstance + self.dataclassInstanceTaskDistributionSuffix
|
|
84
|
+
|
|
85
|
+
if self.logicalPathModuleDataclass is None: # pyright: ignore[reportUnnecessaryComparison]
|
|
86
|
+
self.logicalPathModuleDataclass = '.'.join([self.packageName, self.dataclassModule])
|
|
87
|
+
if self.logicalPathModuleSourceAlgorithm is None: # pyright: ignore[reportUnnecessaryComparison]
|
|
88
|
+
self.logicalPathModuleSourceAlgorithm = '.'.join([self.packageName, self.sourceAlgorithm])
|
|
89
|
+
|
|
90
|
+
class raiseIfNoneGitHubIssueNumber3(Exception): pass
|
|
91
|
+
|
|
92
|
+
logicalPathModuleDispatcherHARDCODED: str = 'mapFolding.syntheticModules.numbaCount'
|
|
93
|
+
callableDispatcherHARDCODED: str = 'doTheNeedful'
|
|
94
|
+
concurrencyPackageHARDCODED = 'multiprocessing'
|
|
95
|
+
|
|
96
|
+
The = PackageInformation(logicalPathModuleDispatcher=logicalPathModuleDispatcherHARDCODED, callableDispatcher=callableDispatcherHARDCODED, concurrencyPackage=concurrencyPackageHARDCODED)
|
mapFolding/oeis.py
CHANGED
|
@@ -20,9 +20,10 @@ mathematical definition in OEIS and the computational implementation in the pack
|
|
|
20
20
|
from collections.abc import Callable
|
|
21
21
|
from datetime import datetime, timedelta
|
|
22
22
|
from functools import cache
|
|
23
|
-
from mapFolding import countFolds,
|
|
23
|
+
from mapFolding import countFolds, packageSettings, TypedDict
|
|
24
24
|
from pathlib import Path
|
|
25
25
|
from typing import Any, Final
|
|
26
|
+
from Z0Z_tools import writeStringToHere
|
|
26
27
|
import argparse
|
|
27
28
|
import random
|
|
28
29
|
import sys
|
|
@@ -33,7 +34,7 @@ import warnings
|
|
|
33
34
|
|
|
34
35
|
cacheDays = 30
|
|
35
36
|
|
|
36
|
-
pathCache: Path =
|
|
37
|
+
pathCache: Path = packageSettings.pathPackage / ".cache"
|
|
37
38
|
|
|
38
39
|
class SettingsOEIS(TypedDict):
|
|
39
40
|
description: str
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
+
from mapFolding import ComputationState, getPathFilenameFoldsTotal, getPathRootJobDEFAULT, MapFoldingState
|
|
2
|
+
from mapFolding import DatatypeElephino as TheDatatypeElephino, DatatypeFoldsTotal as TheDatatypeFoldsTotal, DatatypeLeavesTotal as TheDatatypeLeavesTotal
|
|
1
3
|
from mapFolding.someAssemblyRequired import ShatteredDataclass, ast_Identifier, parseLogicalPath2astModule, parsePathFilename2astModule, str_nameDOTname
|
|
2
|
-
from mapFolding.someAssemblyRequired.
|
|
4
|
+
from mapFolding.someAssemblyRequired.toolkitNumba import theNumbaFlow
|
|
3
5
|
from mapFolding.someAssemblyRequired.transformationTools import shatter_dataclassesDOTdataclass
|
|
4
|
-
from mapFolding.theSSOT import ComputationState, DatatypeElephino as TheDatatypeElephino, DatatypeFoldsTotal as TheDatatypeFoldsTotal, DatatypeLeavesTotal as TheDatatypeLeavesTotal
|
|
5
|
-
from mapFolding.toolboxFilesystem import getPathFilenameFoldsTotal, getPathRootJobDEFAULT
|
|
6
|
-
from mapFolding.dataBaskets import MapFoldingState
|
|
7
6
|
from pathlib import Path, PurePosixPath
|
|
8
7
|
from typing import TypeAlias
|
|
9
8
|
import dataclasses
|