mapFolding 0.5.1__tar.gz → 0.6.0__tar.gz
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-0.5.1 → mapfolding-0.6.0}/PKG-INFO +5 -4
- {mapfolding-0.5.1 → mapfolding-0.6.0}/README.md +1 -1
- {mapfolding-0.5.1 → mapfolding-0.6.0}/mapFolding/__init__.py +7 -4
- {mapfolding-0.5.1 → mapfolding-0.6.0}/mapFolding/basecamp.py +1 -3
- {mapfolding-0.5.1 → mapfolding-0.6.0}/mapFolding/beDRY.py +7 -37
- {mapfolding-0.5.1 → mapfolding-0.6.0}/mapFolding/oeis.py +53 -57
- mapfolding-0.6.0/mapFolding/theConfiguration.py +58 -0
- {mapfolding-0.5.1 → mapfolding-0.6.0}/mapFolding/theDao.py +1 -1
- {mapfolding-0.5.1 → mapfolding-0.6.0}/mapFolding/theSSOT.py +12 -34
- {mapfolding-0.5.1 → mapfolding-0.6.0}/mapFolding/theSSOTdatatypes.py +15 -16
- mapfolding-0.6.0/mapFolding/theWrongWay.py +7 -0
- {mapfolding-0.5.1 → mapfolding-0.6.0}/mapFolding.egg-info/PKG-INFO +5 -4
- {mapfolding-0.5.1 → mapfolding-0.6.0}/mapFolding.egg-info/SOURCES.txt +4 -0
- {mapfolding-0.5.1 → mapfolding-0.6.0}/mapFolding.egg-info/requires.txt +1 -0
- {mapfolding-0.5.1 → mapfolding-0.6.0}/pyproject.toml +4 -3
- {mapfolding-0.5.1 → mapfolding-0.6.0}/tests/test_oeis.py +2 -4
- {mapfolding-0.5.1 → mapfolding-0.6.0}/tests/test_other.py +18 -2
- {mapfolding-0.5.1 → mapfolding-0.6.0}/tests/test_tasks.py +1 -0
- {mapfolding-0.5.1 → mapfolding-0.6.0}/LICENSE +0 -0
- {mapfolding-0.5.1 → mapfolding-0.6.0}/mapFolding/py.typed +0 -0
- {mapfolding-0.5.1 → mapfolding-0.6.0}/mapFolding.egg-info/dependency_links.txt +0 -0
- {mapfolding-0.5.1 → mapfolding-0.6.0}/mapFolding.egg-info/entry_points.txt +0 -0
- {mapfolding-0.5.1 → mapfolding-0.6.0}/mapFolding.egg-info/top_level.txt +0 -0
- {mapfolding-0.5.1 → mapfolding-0.6.0}/setup.cfg +0 -0
- {mapfolding-0.5.1 → mapfolding-0.6.0}/tests/test_computations.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: mapFolding
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Count distinct ways to fold a map (or a strip of stamps)
|
|
5
5
|
Author-email: Hunter Hogan <HunterHogan@pm.me>
|
|
6
6
|
License: CC-BY-NC-4.0
|
|
@@ -16,12 +16,12 @@ Classifier: Intended Audience :: Other Audience
|
|
|
16
16
|
Classifier: Intended Audience :: Science/Research
|
|
17
17
|
Classifier: Natural Language :: English
|
|
18
18
|
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
21
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
22
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
23
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
24
|
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
-
Classifier: Programming Language :: Python :: 3
|
|
24
|
-
Classifier: Programming Language :: Python
|
|
25
25
|
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
26
26
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
27
|
Classifier: Typing :: Typed
|
|
@@ -35,6 +35,7 @@ Provides-Extra: testing
|
|
|
35
35
|
Requires-Dist: autoflake; extra == "testing"
|
|
36
36
|
Requires-Dist: mypy; extra == "testing"
|
|
37
37
|
Requires-Dist: more_itertools; extra == "testing"
|
|
38
|
+
Requires-Dist: numba_progress; extra == "testing"
|
|
38
39
|
Requires-Dist: pytest-cov; extra == "testing"
|
|
39
40
|
Requires-Dist: pytest-env; extra == "testing"
|
|
40
41
|
Requires-Dist: pytest-xdist; extra == "testing"
|
|
@@ -153,4 +154,4 @@ Available OEIS sequences:
|
|
|
153
154
|
[](https://HunterThinks.com/support)
|
|
154
155
|
[](https://www.youtube.com/@HunterHogan)
|
|
155
156
|
|
|
156
|
-
[](https://creativecommons.org/licenses/by-nc/4.0/)
|
|
@@ -108,4 +108,4 @@ Available OEIS sequences:
|
|
|
108
108
|
[](https://HunterThinks.com/support)
|
|
109
109
|
[](https://www.youtube.com/@HunterHogan)
|
|
110
110
|
|
|
111
|
-
[](https://creativecommons.org/licenses/by-nc/4.0/)
|
|
@@ -41,10 +41,10 @@ _dictionaryListsImportFrom['mapFolding.theSSOT'].extend([
|
|
|
41
41
|
'computationState',
|
|
42
42
|
'EnumIndices',
|
|
43
43
|
'getDispatcherCallable',
|
|
44
|
-
'getPathPackage',
|
|
45
44
|
'indexMy',
|
|
46
45
|
'indexTrack',
|
|
47
46
|
'myPackageNameIs',
|
|
47
|
+
'pathPackage',
|
|
48
48
|
])
|
|
49
49
|
|
|
50
50
|
# Datatype management
|
|
@@ -60,17 +60,21 @@ _dictionaryListsImportFrom['mapFolding.theSSOT'].extend([
|
|
|
60
60
|
|
|
61
61
|
# Synthesize modules
|
|
62
62
|
_dictionaryListsImportFrom['mapFolding.theSSOT'].extend([
|
|
63
|
-
'
|
|
63
|
+
'additional_importsHARDCODED',
|
|
64
|
+
'formatFilenameModule',
|
|
64
65
|
'getAlgorithmDispatcher',
|
|
65
66
|
'getAlgorithmSource',
|
|
66
67
|
'getPathJobRootDEFAULT',
|
|
67
68
|
'getPathSyntheticModules',
|
|
69
|
+
'listCallablesDispatchees',
|
|
68
70
|
'moduleOfSyntheticModules',
|
|
71
|
+
'Z0Z_filenameModuleWrite',
|
|
72
|
+
'Z0Z_filenameWriteElseCallableTarget',
|
|
69
73
|
'Z0Z_getDatatypeModuleScalar',
|
|
70
74
|
'Z0Z_getDecoratorCallable',
|
|
75
|
+
'Z0Z_identifierCountFolds',
|
|
71
76
|
'Z0Z_setDatatypeModuleScalar',
|
|
72
77
|
'Z0Z_setDecoratorCallable',
|
|
73
|
-
'Z0Z_identifierCountFolds',
|
|
74
78
|
])
|
|
75
79
|
|
|
76
80
|
# Parameters for the prima donna
|
|
@@ -101,4 +105,3 @@ if TYPE_CHECKING:
|
|
|
101
105
|
from oeis import *
|
|
102
106
|
from theDao import *
|
|
103
107
|
from theSSOT import *
|
|
104
|
-
from theSSOTdatatypes import *
|
|
@@ -7,7 +7,6 @@ def countFolds(listDimensions: Sequence[int]
|
|
|
7
7
|
, pathLikeWriteFoldsTotal: str | PathLike[str] | None = None
|
|
8
8
|
, computationDivisions: int | str | None = None
|
|
9
9
|
, CPUlimit: int | float | bool | None = None
|
|
10
|
-
, **keywordArguments: str | bool
|
|
11
10
|
) -> int:
|
|
12
11
|
"""Count the total number of possible foldings for a given map dimensions.
|
|
13
12
|
|
|
@@ -18,7 +17,6 @@ def countFolds(listDimensions: Sequence[int]
|
|
|
18
17
|
computationDivisions (None):
|
|
19
18
|
Whether and how to divide the computational work. See notes for details.
|
|
20
19
|
CPUlimit (None): This is only relevant if there are `computationDivisions`: whether and how to limit the CPU usage. See notes for details.
|
|
21
|
-
**keywordArguments: Datatype management. See `outfitCountFolds` for details.
|
|
22
20
|
Returns:
|
|
23
21
|
foldsTotal: Total number of distinct ways to fold a map of the given dimensions.
|
|
24
22
|
|
|
@@ -39,7 +37,7 @@ def countFolds(listDimensions: Sequence[int]
|
|
|
39
37
|
N.B.: You probably don't want to divide the computation into tasks.
|
|
40
38
|
If you want to compute a large `foldsTotal`, dividing the computation into tasks is usually a bad idea. Dividing the algorithm into tasks is inherently inefficient: efficient division into tasks means there would be no overlap in the work performed by each task. When dividing this algorithm, the amount of overlap is between 50% and 90% by all tasks: at least 50% of the work done by every task must be done by _all_ tasks. If you improve the computation time, it will only change by -10 to -50% depending on (at the very least) the ratio of the map dimensions and the number of leaves. If an undivided computation would take 10 hours on your computer, for example, the computation will still take at least 5 hours but you might reduce the time to 9 hours. Most of the time, however, you will increase the computation time. If logicalCores >= leavesTotal, it will probably be faster. If logicalCores <= 2 * leavesTotal, it will almost certainly be slower for all map dimensions.
|
|
41
39
|
"""
|
|
42
|
-
stateUniversal: computationState = outfitCountFolds(listDimensions, computationDivisions=computationDivisions, CPUlimit=CPUlimit
|
|
40
|
+
stateUniversal: computationState = outfitCountFolds(listDimensions, computationDivisions=computationDivisions, CPUlimit=CPUlimit)
|
|
43
41
|
|
|
44
42
|
dispatcher = getDispatcherCallable()
|
|
45
43
|
dispatcher(**stateUniversal)
|
|
@@ -7,13 +7,10 @@ from mapFolding import (
|
|
|
7
7
|
hackSSOTdtype,
|
|
8
8
|
indexMy,
|
|
9
9
|
indexTrack,
|
|
10
|
-
setDatatypeElephino,
|
|
11
|
-
setDatatypeFoldsTotal,
|
|
12
10
|
setDatatypeLeavesTotal,
|
|
13
|
-
setDatatypeModule,
|
|
14
11
|
)
|
|
15
12
|
from collections.abc import Sequence
|
|
16
|
-
from numba import get_num_threads, set_num_threads
|
|
13
|
+
from numba import get_num_threads, set_num_threads
|
|
17
14
|
from numpy import dtype, integer, ndarray
|
|
18
15
|
from numpy.typing import DTypeLike, NDArray
|
|
19
16
|
from pathlib import Path
|
|
@@ -24,7 +21,7 @@ import numpy
|
|
|
24
21
|
import os
|
|
25
22
|
|
|
26
23
|
def getFilenameFoldsTotal(mapShape: Sequence[int] | ndarray[tuple[int], dtype[integer[Any]]]) -> str:
|
|
27
|
-
"""Imagine your computer has been counting folds for
|
|
24
|
+
"""Imagine your computer has been counting folds for 9 days, and when it tries to save your newly discovered value,
|
|
28
25
|
the filename is invalid. I bet you think this function is more important after that thought experiment.
|
|
29
26
|
|
|
30
27
|
Make a standardized filename for the computed value `foldsTotal`.
|
|
@@ -45,7 +42,7 @@ def getFilenameFoldsTotal(mapShape: Sequence[int] | ndarray[tuple[int], dtype[in
|
|
|
45
42
|
mapShape: A sequence of integers representing the dimensions of the map.
|
|
46
43
|
|
|
47
44
|
Returns:
|
|
48
|
-
filenameFoldsTotal: A filename string in format '
|
|
45
|
+
filenameFoldsTotal: A filename string in format 'pMxN.foldsTotal' where M,N are sorted dimensions
|
|
49
46
|
"""
|
|
50
47
|
return 'p' + 'x'.join(str(dimension) for dimension in sorted(mapShape)) + '.foldsTotal'
|
|
51
48
|
|
|
@@ -220,7 +217,10 @@ def makeDataContainer(shape: int | tuple[int, ...], datatype: DTypeLike | None =
|
|
|
220
217
|
else:
|
|
221
218
|
raise NotImplementedError("Somebody done broke it.")
|
|
222
219
|
|
|
223
|
-
def outfitCountFolds(listDimensions: Sequence[int]
|
|
220
|
+
def outfitCountFolds(listDimensions: Sequence[int]
|
|
221
|
+
, computationDivisions: int | str | None = None
|
|
222
|
+
, CPUlimit: bool | float | int | None = None
|
|
223
|
+
) -> computationState:
|
|
224
224
|
"""
|
|
225
225
|
Initializes and configures the computation state for map folding computations.
|
|
226
226
|
|
|
@@ -228,40 +228,10 @@ def outfitCountFolds(listDimensions: Sequence[int], computationDivisions: int |
|
|
|
228
228
|
listDimensions: The dimensions of the map to be folded
|
|
229
229
|
computationDivisions (None): see `getTaskDivisions`
|
|
230
230
|
CPUlimit (None): see `setCPUlimit`
|
|
231
|
-
**keywordArguments: Datatype management, it's complicated: see the code below.
|
|
232
231
|
|
|
233
232
|
Returns:
|
|
234
233
|
stateInitialized: The initialized computation state
|
|
235
234
|
"""
|
|
236
|
-
# keywordArguments START
|
|
237
|
-
kwourGrapes = keywordArguments.get('sourGrapes', None)
|
|
238
|
-
if kwourGrapes:
|
|
239
|
-
sourGrapes = True
|
|
240
|
-
else:
|
|
241
|
-
sourGrapes = False
|
|
242
|
-
|
|
243
|
-
ImaSetTheDatatype = keywordArguments.get('datatypeElephino', None)
|
|
244
|
-
if ImaSetTheDatatype:
|
|
245
|
-
ImaSetTheDatatype = str(ImaSetTheDatatype)
|
|
246
|
-
setDatatypeElephino(ImaSetTheDatatype, sourGrapes)
|
|
247
|
-
|
|
248
|
-
ImaSetTheDatatype = keywordArguments.get('datatypeFoldsTotal', None)
|
|
249
|
-
if ImaSetTheDatatype:
|
|
250
|
-
ImaSetTheDatatype = str(ImaSetTheDatatype)
|
|
251
|
-
setDatatypeFoldsTotal(ImaSetTheDatatype, sourGrapes)
|
|
252
|
-
|
|
253
|
-
ImaSetTheDatatype = keywordArguments.get('datatypeLeavesTotal', None)
|
|
254
|
-
if ImaSetTheDatatype:
|
|
255
|
-
ImaSetTheDatatype = str(ImaSetTheDatatype)
|
|
256
|
-
setDatatypeLeavesTotal(ImaSetTheDatatype, sourGrapes)
|
|
257
|
-
|
|
258
|
-
# NOTE well: this might be only hypothetical because as of this writing, `makeDataContainer` only makes numpy.zeros. But it's here in case things change.
|
|
259
|
-
ImaSetTheDatatype = keywordArguments.get('datatypeModule', None)
|
|
260
|
-
if ImaSetTheDatatype:
|
|
261
|
-
ImaSetTheDatatype = str(ImaSetTheDatatype)
|
|
262
|
-
setDatatypeModule(ImaSetTheDatatype, sourGrapes)
|
|
263
|
-
# keywordArguments END
|
|
264
|
-
|
|
265
235
|
my = makeDataContainer(len(indexMy), hackSSOTdtype('my'))
|
|
266
236
|
|
|
267
237
|
mapShape = tuple(sorted(validateListDimensions(listDimensions)))
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"""Everything implementing the The Online Encyclopedia of Integer Sequences (OEIS); _only_ things that implement _only_ the OEIS."""
|
|
2
2
|
from collections.abc import Callable
|
|
3
3
|
from datetime import datetime, timedelta
|
|
4
|
-
from mapFolding import countFolds,
|
|
5
|
-
from
|
|
4
|
+
from mapFolding import countFolds, pathPackage
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, cast, Final, TYPE_CHECKING
|
|
6
7
|
import argparse
|
|
7
8
|
import pathlib
|
|
8
|
-
from pathlib import Path
|
|
9
9
|
import random
|
|
10
10
|
import sys
|
|
11
11
|
import time
|
|
@@ -23,7 +23,7 @@ cacheDays = 7
|
|
|
23
23
|
"""
|
|
24
24
|
Section: make `settingsOEIS`"""
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
pathCache: Path = pathPackage / ".cache"
|
|
27
27
|
|
|
28
28
|
class SettingsOEIS(TypedDict):
|
|
29
29
|
description: str
|
|
@@ -36,38 +36,37 @@ class SettingsOEIS(TypedDict):
|
|
|
36
36
|
valueUnknown: int
|
|
37
37
|
|
|
38
38
|
settingsOEIShardcodedValues: dict[str, dict[str, Any]] = {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
39
|
+
'A001415': {
|
|
40
|
+
'getMapShape': cast(Callable[[int], list[int]], lambda n: sorted([2, n])), # type: ignore
|
|
41
|
+
'valuesBenchmark': [14],
|
|
42
|
+
'valuesTestParallelization': [*range(3, 7)],
|
|
43
|
+
'valuesTestValidation': [random.randint(2, 9)],
|
|
44
|
+
},
|
|
45
|
+
'A001416': {
|
|
46
|
+
'getMapShape': cast(Callable[[int], list[int]], lambda n: sorted([3, n])), # type: ignore
|
|
47
|
+
'valuesBenchmark': [9],
|
|
48
|
+
'valuesTestParallelization': [*range(3, 5)],
|
|
49
|
+
'valuesTestValidation': [random.randint(2, 6)],
|
|
50
|
+
},
|
|
51
|
+
'A001417': {
|
|
52
|
+
'getMapShape': cast(Callable[[int], list[int]], lambda n: [2] * n), # type: ignore
|
|
53
|
+
'valuesBenchmark': [6],
|
|
54
|
+
'valuesTestParallelization': [*range(2, 4)],
|
|
55
|
+
'valuesTestValidation': [random.randint(2, 4)],
|
|
56
|
+
},
|
|
57
|
+
'A195646': {
|
|
58
|
+
'getMapShape': cast(Callable[[int], list[int]], lambda n: [3] * n), # type: ignore
|
|
59
|
+
'valuesBenchmark': [3],
|
|
60
|
+
'valuesTestParallelization': [*range(2, 3)],
|
|
61
|
+
'valuesTestValidation': [2],
|
|
62
|
+
},
|
|
63
|
+
'A001418': {
|
|
64
|
+
'getMapShape': cast(Callable[[int], list[int]], lambda n: [n, n]), # type: ignore
|
|
65
|
+
'valuesBenchmark': [5],
|
|
66
|
+
'valuesTestParallelization': [*range(2, 4)],
|
|
67
|
+
'valuesTestValidation': [random.randint(2, 4)],
|
|
68
|
+
},
|
|
69
69
|
}
|
|
70
|
-
|
|
71
70
|
oeisIDsImplemented: Final[list[str]] = sorted([oeisID.upper().strip() for oeisID in settingsOEIShardcodedValues.keys()])
|
|
72
71
|
"""Directly implemented OEIS IDs; standardized, e.g., 'A001415'."""
|
|
73
72
|
|
|
@@ -180,7 +179,7 @@ def getOEISidValues(oeisID: str) -> dict[int, int]:
|
|
|
180
179
|
IOError: If there is an error reading from or writing to the local cache.
|
|
181
180
|
"""
|
|
182
181
|
|
|
183
|
-
pathFilenameCache: Path =
|
|
182
|
+
pathFilenameCache: Path = pathCache / getFilenameOEISbFile(oeisID)
|
|
184
183
|
url: str = f"https://oeis.org/{oeisID}/{getFilenameOEISbFile(oeisID)}"
|
|
185
184
|
|
|
186
185
|
oeisInformation: None | str = getOEISofficial(pathFilenameCache, url)
|
|
@@ -191,7 +190,7 @@ def getOEISidValues(oeisID: str) -> dict[int, int]:
|
|
|
191
190
|
|
|
192
191
|
def getOEISidInformation(oeisID: str) -> tuple[str, int]:
|
|
193
192
|
oeisID = validateOEISid(oeisID)
|
|
194
|
-
pathFilenameCache: Path =
|
|
193
|
+
pathFilenameCache: Path = pathCache / f"{oeisID}.txt"
|
|
195
194
|
url: str = f"https://oeis.org/search?q=id:{oeisID}&fmt=text"
|
|
196
195
|
|
|
197
196
|
oeisInformation: None | str = getOEISofficial(pathFilenameCache, url)
|
|
@@ -199,26 +198,23 @@ def getOEISidInformation(oeisID: str) -> tuple[str, int]:
|
|
|
199
198
|
if not oeisInformation:
|
|
200
199
|
return "Not found", -1
|
|
201
200
|
|
|
202
|
-
|
|
201
|
+
listDescriptionDeconstructed: list[str] = []
|
|
203
202
|
offset = None
|
|
204
|
-
for
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
offset_str: str = parts[2].split(',')[0]
|
|
214
|
-
offset = int(offset_str)
|
|
215
|
-
if not description_parts:
|
|
203
|
+
for ImaStr in oeisInformation.splitlines():
|
|
204
|
+
ImaStr = ImaStr.strip() + "I am writing code to string parse machine readable data because in 2025, people can't even spell enteroperableity."
|
|
205
|
+
secretCode, title, secretData =ImaStr.split(maxsplit=2)
|
|
206
|
+
if secretCode == '%N' and title == oeisID:
|
|
207
|
+
listDescriptionDeconstructed.append(secretData)
|
|
208
|
+
if secretCode == '%O' and title == oeisID:
|
|
209
|
+
offsetAsStr: str = secretData.split(',')[0]
|
|
210
|
+
offset = int(offsetAsStr)
|
|
211
|
+
if not listDescriptionDeconstructed:
|
|
216
212
|
warnings.warn(f"No description found for {oeisID}")
|
|
217
|
-
|
|
213
|
+
listDescriptionDeconstructed.append("No description found")
|
|
218
214
|
if offset is None:
|
|
219
215
|
warnings.warn(f"No offset found for {oeisID}")
|
|
220
216
|
offset = -1
|
|
221
|
-
description: str = ' '.join(
|
|
217
|
+
description: str = ' '.join(listDescriptionDeconstructed)
|
|
222
218
|
return description, offset
|
|
223
219
|
|
|
224
220
|
def makeSettingsOEIS() -> dict[str, SettingsOEIS]:
|
|
@@ -326,13 +322,13 @@ def OEIS_for_n() -> None:
|
|
|
326
322
|
|
|
327
323
|
def clearOEIScache() -> None:
|
|
328
324
|
"""Delete all cached OEIS sequence files."""
|
|
329
|
-
if not
|
|
330
|
-
print(f"Cache directory, {
|
|
325
|
+
if not pathCache.exists():
|
|
326
|
+
print(f"Cache directory, {pathCache}, not found - nothing to clear.")
|
|
331
327
|
return
|
|
332
328
|
for oeisID in settingsOEIS:
|
|
333
|
-
(
|
|
334
|
-
(
|
|
335
|
-
print(f"Cache cleared from {
|
|
329
|
+
( pathCache / f"{oeisID}.txt" ).unlink(missing_ok=True)
|
|
330
|
+
( pathCache / getFilenameOEISbFile(oeisID) ).unlink(missing_ok=True)
|
|
331
|
+
print(f"Cache cleared from {pathCache}")
|
|
336
332
|
|
|
337
333
|
def getOEISids() -> None:
|
|
338
334
|
"""Print all available OEIS sequence IDs that are directly implemented."""
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from types import ModuleType
|
|
2
|
+
from mapFolding.theWrongWay import *
|
|
3
|
+
from sys import modules as sysModules
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from importlib import import_module as importlib_import_module
|
|
6
|
+
from inspect import getfile as inspect_getfile
|
|
7
|
+
from typing import Final
|
|
8
|
+
"""
|
|
9
|
+
evaluateWhenPACKAGING
|
|
10
|
+
evaluateWhenINSTALLING
|
|
11
|
+
"""
|
|
12
|
+
try:
|
|
13
|
+
import tomli
|
|
14
|
+
TRYmyPackageNameIs: str = tomli.load(Path("../pyproject.toml").open('rb'))["project"]["name"]
|
|
15
|
+
except Exception:
|
|
16
|
+
TRYmyPackageNameIs: str = myPackageNameIsPACKAGING
|
|
17
|
+
|
|
18
|
+
myPackageNameIs: Final[str] = TRYmyPackageNameIs
|
|
19
|
+
|
|
20
|
+
def getPathPackageINSTALLING() -> Path:
|
|
21
|
+
pathPackage = Path(inspect_getfile(importlib_import_module(myPackageNameIs)))
|
|
22
|
+
if pathPackage.is_file():
|
|
23
|
+
pathPackage: Path = pathPackage.parent
|
|
24
|
+
return pathPackage
|
|
25
|
+
|
|
26
|
+
pathPackage: Path = getPathPackageINSTALLING()
|
|
27
|
+
|
|
28
|
+
moduleOfSyntheticModules: Final[str] = "syntheticModules"
|
|
29
|
+
formatNameModule = "numba_{callableTarget}"
|
|
30
|
+
formatFilenameModule = formatNameModule + ".py"
|
|
31
|
+
dispatcherCallableName = "doTheNeedful"
|
|
32
|
+
nameModuleDispatcher: str = formatNameModule.format(callableTarget=dispatcherCallableName)
|
|
33
|
+
Z0Z_filenameModuleWrite = 'numbaCount.py'
|
|
34
|
+
Z0Z_filenameWriteElseCallableTarget: str = 'count'
|
|
35
|
+
|
|
36
|
+
def getDispatcherCallable():
|
|
37
|
+
logicalPathModule: str = f"{myPackageNameIs}.{moduleOfSyntheticModules}.{nameModuleDispatcher}"
|
|
38
|
+
moduleImported: ModuleType = importlib_import_module(logicalPathModule)
|
|
39
|
+
return getattr(moduleImported, dispatcherCallableName)
|
|
40
|
+
|
|
41
|
+
def getAlgorithmSource() -> ModuleType:
|
|
42
|
+
logicalPathModule: str = f"{myPackageNameIs}.{algorithmSourcePACKAGING}"
|
|
43
|
+
moduleImported: ModuleType = importlib_import_module(logicalPathModule)
|
|
44
|
+
return moduleImported
|
|
45
|
+
# from mapFolding import theDao
|
|
46
|
+
# return theDao
|
|
47
|
+
|
|
48
|
+
# TODO learn how to see this from the user's perspective
|
|
49
|
+
def getPathJobRootDEFAULT() -> Path:
|
|
50
|
+
if 'google.colab' in sysModules:
|
|
51
|
+
pathJobDEFAULT: Path = Path("/content/drive/MyDrive") / "jobs"
|
|
52
|
+
else:
|
|
53
|
+
pathJobDEFAULT = pathPackage / "jobs"
|
|
54
|
+
return pathJobDEFAULT
|
|
55
|
+
|
|
56
|
+
listCallablesDispatchees: list[str] = listCallablesDispatcheesHARDCODED
|
|
57
|
+
|
|
58
|
+
additional_importsHARDCODED.append(myPackageNameIs)
|
|
@@ -147,7 +147,7 @@ def countParallel(connectionGraph: ndarray[tuple[int, int, int], dtype[integer[A
|
|
|
147
147
|
|
|
148
148
|
taskDivisionsPrange = myPARALLEL[indexMy.taskDivisions.value]
|
|
149
149
|
|
|
150
|
-
for indexSherpa in prange(taskDivisionsPrange):
|
|
150
|
+
for indexSherpa in prange(taskDivisionsPrange): # type: ignore
|
|
151
151
|
groupsOfFolds: int = 0
|
|
152
152
|
|
|
153
153
|
gapsWhere = gapsWherePARALLEL.copy()
|
|
@@ -3,7 +3,6 @@ from mapFolding.theSSOTdatatypes import *
|
|
|
3
3
|
from numba.core.compiler import CompilerBase as numbaCompilerBase
|
|
4
4
|
from numpy import dtype, integer, ndarray
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from sys import modules as sysModules
|
|
7
6
|
from types import ModuleType
|
|
8
7
|
from typing import Any, Final, TYPE_CHECKING, cast
|
|
9
8
|
|
|
@@ -21,50 +20,29 @@ else:
|
|
|
21
20
|
- Configuration Registry
|
|
22
21
|
- Write-Once, Read-Many (WORM) / Immutable Initialization
|
|
23
22
|
- Lazy Initialization
|
|
24
|
-
-
|
|
23
|
+
- Separate configuration from business logic
|
|
24
|
+
|
|
25
|
+
theSSOT and yourSSOT
|
|
25
26
|
|
|
26
27
|
delay realization/instantiation until a concrete value is desired
|
|
27
28
|
moment of truth: when the value is needed, not when the value is defined
|
|
28
29
|
"""
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def getPathPackage() -> Path:
|
|
39
|
-
import importlib, inspect
|
|
40
|
-
pathPackage = Path(inspect.getfile(importlib.import_module(myPackageNameIs)))
|
|
41
|
-
if pathPackage.is_file():
|
|
42
|
-
pathPackage = pathPackage.parent
|
|
43
|
-
return pathPackage
|
|
44
|
-
|
|
45
|
-
def getPathJobRootDEFAULT() -> Path:
|
|
46
|
-
if 'google.colab' in sysModules:
|
|
47
|
-
pathJobDEFAULT = Path("/content/drive/MyDrive") / "jobs"
|
|
48
|
-
else:
|
|
49
|
-
pathJobDEFAULT = getPathPackage() / "jobs"
|
|
50
|
-
return pathJobDEFAULT
|
|
31
|
+
"""
|
|
32
|
+
listDimensions: list[int]
|
|
33
|
+
mapShape
|
|
34
|
+
tupleDimensions: tuple[int, ...]
|
|
35
|
+
dimensionsTuple
|
|
36
|
+
dimensionTuple
|
|
37
|
+
"""
|
|
51
38
|
|
|
52
39
|
def getPathSyntheticModules() -> Path:
|
|
53
|
-
|
|
54
|
-
return pathSyntheticModules
|
|
55
|
-
|
|
56
|
-
def getAlgorithmSource() -> ModuleType:
|
|
57
|
-
from mapFolding import theDao
|
|
58
|
-
return theDao
|
|
40
|
+
return pathPackage / moduleOfSyntheticModules
|
|
59
41
|
|
|
60
42
|
def getAlgorithmDispatcher() -> Callable[..., None]:
|
|
61
|
-
algorithmSource = getAlgorithmSource()
|
|
43
|
+
algorithmSource: ModuleType = getAlgorithmSource()
|
|
62
44
|
return cast(Callable[..., None], algorithmSource.doTheNeedful) # 'doTheNeedful' is duplicated and there is not a SSOT for it
|
|
63
45
|
|
|
64
|
-
def getDispatcherCallable() -> Callable[..., None]:
|
|
65
|
-
from mapFolding.syntheticModules import numba_doTheNeedful
|
|
66
|
-
return cast(Callable[..., None], numba_doTheNeedful.doTheNeedful)
|
|
67
|
-
|
|
68
46
|
# NOTE I want this _concept_, not necessarily this method, to be well implemented and usable everywhere: Python, Numba, Jax, CUDA, idc
|
|
69
47
|
class computationState(TypedDict):
|
|
70
48
|
connectionGraph: ndarray[tuple[int, int, int], dtype[integer[Any]]]
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from mapFolding.theConfiguration import *
|
|
1
2
|
from collections import defaultdict
|
|
2
3
|
from typing import Any, cast, Final
|
|
3
4
|
import enum
|
|
@@ -42,19 +43,17 @@ _datatypeDefault: Final[dict[str, str]] = {
|
|
|
42
43
|
'leavesTotal': 'uint16',
|
|
43
44
|
}
|
|
44
45
|
_datatypeModule: str = ''
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
_datatype: dict[str, str] = defaultdict(str)
|
|
46
|
+
_registryOfDatatypes: dict[str, str] = defaultdict(str)
|
|
48
47
|
|
|
49
48
|
def reportDatatypeLimit(identifier: str, datatype: str, sourGrapes: bool | None = False) -> str:
|
|
50
|
-
global
|
|
51
|
-
if not
|
|
52
|
-
|
|
53
|
-
elif
|
|
49
|
+
global _registryOfDatatypes
|
|
50
|
+
if not _registryOfDatatypes[identifier]:
|
|
51
|
+
_registryOfDatatypes[identifier] = datatype
|
|
52
|
+
elif _registryOfDatatypes[identifier] == datatype:
|
|
54
53
|
pass
|
|
55
54
|
elif sourGrapes:
|
|
56
|
-
raise Exception(f"Datatype is '{
|
|
57
|
-
return
|
|
55
|
+
raise Exception(f"Datatype is '{_registryOfDatatypes[identifier]}' not '{datatype}', so you can take your ball and go home.")
|
|
56
|
+
return _registryOfDatatypes[identifier]
|
|
58
57
|
|
|
59
58
|
def setDatatypeModule(datatypeModule: str, sourGrapes: bool | None = False) -> str:
|
|
60
59
|
global _datatypeModule
|
|
@@ -76,20 +75,20 @@ def setDatatypeLeavesTotal(datatype: str, sourGrapes: bool | None = False) -> st
|
|
|
76
75
|
return reportDatatypeLimit('leavesTotal', datatype, sourGrapes)
|
|
77
76
|
|
|
78
77
|
def _get_datatype(identifier: str) -> str:
|
|
79
|
-
global
|
|
80
|
-
if not
|
|
78
|
+
global _registryOfDatatypes
|
|
79
|
+
if not _registryOfDatatypes[identifier]:
|
|
81
80
|
if identifier in indexMy._member_names_:
|
|
82
|
-
|
|
81
|
+
_registryOfDatatypes[identifier] = _datatypeDefault.get(identifier) or _get_datatype('elephino')
|
|
83
82
|
elif identifier in indexTrack._member_names_:
|
|
84
|
-
|
|
83
|
+
_registryOfDatatypes[identifier] = _datatypeDefault.get(identifier) or _get_datatype('elephino')
|
|
85
84
|
else:
|
|
86
|
-
|
|
87
|
-
return
|
|
85
|
+
_registryOfDatatypes[identifier] = _datatypeDefault.get(identifier) or _get_datatype('foldsTotal')
|
|
86
|
+
return _registryOfDatatypes[identifier]
|
|
88
87
|
|
|
89
88
|
def getDatatypeModule() -> str:
|
|
90
89
|
global _datatypeModule
|
|
91
90
|
if not _datatypeModule:
|
|
92
|
-
_datatypeModule =
|
|
91
|
+
_datatypeModule = datatypeModulePACKAGING
|
|
93
92
|
return _datatypeModule
|
|
94
93
|
|
|
95
94
|
def setInStone(identifier: str) -> type[Any]:
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from typing import Final
|
|
2
|
+
|
|
3
|
+
datatypeModulePACKAGING: Final[str] = 'numpy'
|
|
4
|
+
myPackageNameIsPACKAGING: str = "mapFolding"
|
|
5
|
+
listCallablesDispatcheesHARDCODED: list[str] = ['countInitialize', 'countParallel', 'countSequential']
|
|
6
|
+
additional_importsHARDCODED: list[str] = ['numba']
|
|
7
|
+
algorithmSourcePACKAGING: str = 'theDao'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: mapFolding
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Count distinct ways to fold a map (or a strip of stamps)
|
|
5
5
|
Author-email: Hunter Hogan <HunterHogan@pm.me>
|
|
6
6
|
License: CC-BY-NC-4.0
|
|
@@ -16,12 +16,12 @@ Classifier: Intended Audience :: Other Audience
|
|
|
16
16
|
Classifier: Intended Audience :: Science/Research
|
|
17
17
|
Classifier: Natural Language :: English
|
|
18
18
|
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
21
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
22
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
23
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
24
|
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
-
Classifier: Programming Language :: Python :: 3
|
|
24
|
-
Classifier: Programming Language :: Python
|
|
25
25
|
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
26
26
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
27
|
Classifier: Typing :: Typed
|
|
@@ -35,6 +35,7 @@ Provides-Extra: testing
|
|
|
35
35
|
Requires-Dist: autoflake; extra == "testing"
|
|
36
36
|
Requires-Dist: mypy; extra == "testing"
|
|
37
37
|
Requires-Dist: more_itertools; extra == "testing"
|
|
38
|
+
Requires-Dist: numba_progress; extra == "testing"
|
|
38
39
|
Requires-Dist: pytest-cov; extra == "testing"
|
|
39
40
|
Requires-Dist: pytest-env; extra == "testing"
|
|
40
41
|
Requires-Dist: pytest-xdist; extra == "testing"
|
|
@@ -153,4 +154,4 @@ Available OEIS sequences:
|
|
|
153
154
|
[](https://HunterThinks.com/support)
|
|
154
155
|
[](https://www.youtube.com/@HunterHogan)
|
|
155
156
|
|
|
156
|
-
[](https://creativecommons.org/licenses/by-nc/4.0/)
|
|
@@ -6,17 +6,21 @@ pyproject.toml
|
|
|
6
6
|
./mapFolding/beDRY.py
|
|
7
7
|
./mapFolding/oeis.py
|
|
8
8
|
./mapFolding/py.typed
|
|
9
|
+
./mapFolding/theConfiguration.py
|
|
9
10
|
./mapFolding/theDao.py
|
|
10
11
|
./mapFolding/theSSOT.py
|
|
11
12
|
./mapFolding/theSSOTdatatypes.py
|
|
13
|
+
./mapFolding/theWrongWay.py
|
|
12
14
|
mapFolding/__init__.py
|
|
13
15
|
mapFolding/basecamp.py
|
|
14
16
|
mapFolding/beDRY.py
|
|
15
17
|
mapFolding/oeis.py
|
|
16
18
|
mapFolding/py.typed
|
|
19
|
+
mapFolding/theConfiguration.py
|
|
17
20
|
mapFolding/theDao.py
|
|
18
21
|
mapFolding/theSSOT.py
|
|
19
22
|
mapFolding/theSSOTdatatypes.py
|
|
23
|
+
mapFolding/theWrongWay.py
|
|
20
24
|
mapFolding.egg-info/PKG-INFO
|
|
21
25
|
mapFolding.egg-info/SOURCES.txt
|
|
22
26
|
mapFolding.egg-info/dependency_links.txt
|
|
@@ -13,12 +13,12 @@ classifiers = [
|
|
|
13
13
|
"Intended Audience :: Science/Research",
|
|
14
14
|
"Natural Language :: English",
|
|
15
15
|
"Operating System :: OS Independent",
|
|
16
|
+
"Programming Language :: Python",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
16
18
|
"Programming Language :: Python :: 3.10",
|
|
17
19
|
"Programming Language :: Python :: 3.11",
|
|
18
20
|
"Programming Language :: Python :: 3.12",
|
|
19
21
|
"Programming Language :: Python :: 3.13",
|
|
20
|
-
"Programming Language :: Python :: 3",
|
|
21
|
-
"Programming Language :: Python",
|
|
22
22
|
"Topic :: Scientific/Engineering :: Mathematics",
|
|
23
23
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
24
24
|
"Typing :: Typed",]
|
|
@@ -42,6 +42,7 @@ optional-dependencies = { testing = [
|
|
|
42
42
|
"autoflake",
|
|
43
43
|
"mypy",
|
|
44
44
|
"more_itertools",
|
|
45
|
+
"numba_progress",
|
|
45
46
|
"pytest-cov",
|
|
46
47
|
"pytest-env",
|
|
47
48
|
"pytest-xdist",
|
|
@@ -53,7 +54,7 @@ readme = { file = "README.md", content-type = "text/markdown" }
|
|
|
53
54
|
requires-python = ">=3.10"
|
|
54
55
|
scripts = { getOEISids = "mapFolding.oeis:getOEISids", clearOEIScache = "mapFolding.oeis:clearOEIScache", OEIS_for_n = "mapFolding.oeis:OEIS_for_n" }
|
|
55
56
|
urls = { Donate = "https://www.patreon.com/integrated", Homepage = "https://github.com/hunterhogan/mapFolding", Repository = "https://github.com/hunterhogan/mapFolding.git" }
|
|
56
|
-
version = "0.
|
|
57
|
+
version = "0.6.0"
|
|
57
58
|
|
|
58
59
|
[tool.coverage]
|
|
59
60
|
report = { exclude_lines = [
|
|
@@ -9,16 +9,14 @@ from tests.conftest import (
|
|
|
9
9
|
validateOEISid,
|
|
10
10
|
)
|
|
11
11
|
from mapFolding import oeisIDfor_n, getOEISids, clearOEIScache
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, NoReturn
|
|
12
14
|
from urllib.error import URLError
|
|
13
15
|
import io
|
|
14
|
-
from typing import Any, NoReturn
|
|
15
|
-
from pathlib import Path
|
|
16
16
|
import pytest
|
|
17
17
|
import random
|
|
18
18
|
import re as regex
|
|
19
|
-
import unittest
|
|
20
19
|
import unittest.mock
|
|
21
|
-
import urllib
|
|
22
20
|
import urllib.request
|
|
23
21
|
|
|
24
22
|
@pytest.mark.parametrize("badID", ["A999999", " A999999 ", "A999999extra"])
|
|
@@ -1,5 +1,21 @@
|
|
|
1
|
+
from collections.abc import Callable, Generator
|
|
1
2
|
from contextlib import redirect_stdout
|
|
2
|
-
from
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any, Literal
|
|
5
|
+
import unittest.mock
|
|
6
|
+
from tests.conftest import (
|
|
7
|
+
PytestFor_intInnit,
|
|
8
|
+
PytestFor_oopsieKwargsie,
|
|
9
|
+
getLeavesTotal,
|
|
10
|
+
hackSSOTdtype,
|
|
11
|
+
makeConnectionGraph,
|
|
12
|
+
makeDataContainer,
|
|
13
|
+
parseDimensions,
|
|
14
|
+
saveFoldsTotal,
|
|
15
|
+
setCPUlimit,
|
|
16
|
+
standardizedEqualTo,
|
|
17
|
+
validateListDimensions,
|
|
18
|
+
)
|
|
3
19
|
from Z0Z_tools import intInnit
|
|
4
20
|
import io
|
|
5
21
|
import itertools
|
|
@@ -61,7 +77,7 @@ def test_getLeavesTotal_edge_cases() -> None:
|
|
|
61
77
|
# Immutability
|
|
62
78
|
listOriginal = [2, 3]
|
|
63
79
|
standardizedEqualTo(6, getLeavesTotal, listOriginal)
|
|
64
|
-
standardizedEqualTo([2, 3], lambda x: x, listOriginal) # Check that the list wasn't modified
|
|
80
|
+
standardizedEqualTo([2, 3], lambda x: x, listOriginal) # type: ignore # Check that the list wasn't modified
|
|
65
81
|
|
|
66
82
|
@pytest.mark.parametrize("nameOfTest,callablePytest", PytestFor_intInnit())
|
|
67
83
|
def testIntInnit(nameOfTest: str, callablePytest: Callable[[], None]) -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|