mapFolding 0.5.0__py3-none-any.whl → 0.6.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.
- mapFolding/__init__.py +96 -58
- mapFolding/basecamp.py +5 -7
- mapFolding/beDRY.py +11 -41
- mapFolding/oeis.py +71 -74
- mapFolding/theConfiguration.py +58 -0
- mapFolding/theDao.py +1 -1
- mapFolding/theSSOT.py +14 -48
- mapFolding/theSSOTdatatypes.py +25 -36
- mapFolding/theWrongWay.py +7 -0
- {mapFolding-0.5.0.dist-info → mapfolding-0.6.0.dist-info}/METADATA +6 -4
- mapfolding-0.6.0.dist-info/RECORD +16 -0
- {mapFolding-0.5.0.dist-info → mapfolding-0.6.0.dist-info}/WHEEL +1 -1
- {mapFolding-0.5.0.dist-info → mapfolding-0.6.0.dist-info}/top_level.txt +0 -1
- mapFolding/reference/flattened.py +0 -377
- mapFolding/reference/hunterNumba.py +0 -132
- mapFolding/reference/irvineJavaPort.py +0 -120
- mapFolding/reference/jax.py +0 -208
- mapFolding/reference/lunnan.py +0 -153
- mapFolding/reference/lunnanNumpy.py +0 -123
- mapFolding/reference/lunnanWhile.py +0 -121
- mapFolding/reference/rotatedEntryPoint.py +0 -240
- mapFolding/reference/total_countPlus1vsPlusN.py +0 -211
- mapFolding/someAssemblyRequired/__init__.py +0 -5
- mapFolding/someAssemblyRequired/getLLVMforNoReason.py +0 -19
- mapFolding/someAssemblyRequired/makeJob.py +0 -56
- mapFolding/someAssemblyRequired/synthesizeModuleJAX.py +0 -27
- mapFolding/someAssemblyRequired/synthesizeNumba.py +0 -345
- mapFolding/someAssemblyRequired/synthesizeNumbaGeneralized.py +0 -397
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +0 -155
- mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +0 -123
- mapFolding/syntheticModules/numbaCount.py +0 -158
- mapFolding/syntheticModules/numba_doTheNeedful.py +0 -13
- mapFolding-0.5.0.dist-info/RECORD +0 -39
- tests/__init__.py +0 -1
- tests/conftest.py +0 -335
- tests/test_computations.py +0 -42
- tests/test_oeis.py +0 -128
- tests/test_other.py +0 -175
- tests/test_tasks.py +0 -40
- /mapFolding/{syntheticModules/__init__.py → py.typed} +0 -0
- {mapFolding-0.5.0.dist-info → mapfolding-0.6.0.dist-info}/LICENSE +0 -0
- {mapFolding-0.5.0.dist-info → mapfolding-0.6.0.dist-info}/entry_points.txt +0 -0
mapFolding/__init__.py
CHANGED
|
@@ -1,69 +1,107 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from types import ModuleType
|
|
3
|
+
import importlib
|
|
4
|
+
|
|
5
|
+
_dictionaryListsImportFrom: dict[str, list[str]] = defaultdict(list)
|
|
6
|
+
|
|
7
|
+
def __getattr__(name: str):
|
|
8
|
+
if name not in _mapSymbolToModule:
|
|
9
|
+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
moduleAsStr: str = _mapSymbolToModule[name]
|
|
13
|
+
module: ModuleType = importlib.import_module(moduleAsStr)
|
|
14
|
+
blankSymbol = getattr(module, name)
|
|
15
|
+
except (ImportError, ModuleNotFoundError, AttributeError):
|
|
16
|
+
raise
|
|
17
|
+
|
|
18
|
+
# The need to inject into globals tells us that the symbol has not actually been imported
|
|
19
|
+
globals()[name] = blankSymbol
|
|
20
|
+
return blankSymbol
|
|
21
|
+
|
|
22
|
+
_dictionaryListsImportFrom['mapFolding.basecamp'].extend([
|
|
23
|
+
'countFolds',
|
|
24
|
+
])
|
|
25
|
+
|
|
26
|
+
_dictionaryListsImportFrom['mapFolding.beDRY'].extend([
|
|
27
|
+
'getFilenameFoldsTotal',
|
|
28
|
+
'getPathFilenameFoldsTotal',
|
|
29
|
+
'outfitCountFolds',
|
|
30
|
+
'saveFoldsTotal',
|
|
31
|
+
])
|
|
32
|
+
|
|
33
|
+
_dictionaryListsImportFrom['mapFolding.oeis'].extend([
|
|
34
|
+
'clearOEIScache',
|
|
35
|
+
'getOEISids',
|
|
36
|
+
'oeisIDfor_n',
|
|
37
|
+
])
|
|
38
|
+
|
|
1
39
|
# fundamentals
|
|
2
|
-
|
|
3
|
-
computationState,
|
|
4
|
-
EnumIndices,
|
|
5
|
-
getDispatcherCallable,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
)
|
|
40
|
+
_dictionaryListsImportFrom['mapFolding.theSSOT'].extend([
|
|
41
|
+
'computationState',
|
|
42
|
+
'EnumIndices',
|
|
43
|
+
'getDispatcherCallable',
|
|
44
|
+
'indexMy',
|
|
45
|
+
'indexTrack',
|
|
46
|
+
'myPackageNameIs',
|
|
47
|
+
'pathPackage',
|
|
48
|
+
])
|
|
11
49
|
|
|
12
50
|
# Datatype management
|
|
13
|
-
|
|
14
|
-
getDatatypeModule,
|
|
15
|
-
hackSSOTdatatype,
|
|
16
|
-
hackSSOTdtype,
|
|
17
|
-
setDatatypeElephino,
|
|
18
|
-
setDatatypeFoldsTotal,
|
|
19
|
-
setDatatypeLeavesTotal,
|
|
20
|
-
setDatatypeModule,
|
|
21
|
-
)
|
|
51
|
+
_dictionaryListsImportFrom['mapFolding.theSSOT'].extend([
|
|
52
|
+
'getDatatypeModule',
|
|
53
|
+
'hackSSOTdatatype',
|
|
54
|
+
'hackSSOTdtype',
|
|
55
|
+
'setDatatypeElephino',
|
|
56
|
+
'setDatatypeFoldsTotal',
|
|
57
|
+
'setDatatypeLeavesTotal',
|
|
58
|
+
'setDatatypeModule',
|
|
59
|
+
])
|
|
22
60
|
|
|
23
61
|
# Synthesize modules
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
62
|
+
_dictionaryListsImportFrom['mapFolding.theSSOT'].extend([
|
|
63
|
+
'additional_importsHARDCODED',
|
|
64
|
+
'formatFilenameModule',
|
|
65
|
+
'getAlgorithmDispatcher',
|
|
66
|
+
'getAlgorithmSource',
|
|
67
|
+
'getPathJobRootDEFAULT',
|
|
68
|
+
'getPathSyntheticModules',
|
|
69
|
+
'listCallablesDispatchees',
|
|
70
|
+
'moduleOfSyntheticModules',
|
|
71
|
+
'Z0Z_filenameModuleWrite',
|
|
72
|
+
'Z0Z_filenameWriteElseCallableTarget',
|
|
73
|
+
'Z0Z_getDatatypeModuleScalar',
|
|
74
|
+
'Z0Z_getDecoratorCallable',
|
|
75
|
+
'Z0Z_identifierCountFolds',
|
|
76
|
+
'Z0Z_setDatatypeModuleScalar',
|
|
77
|
+
'Z0Z_setDecoratorCallable',
|
|
78
|
+
])
|
|
37
79
|
|
|
38
80
|
# Parameters for the prima donna
|
|
39
|
-
|
|
40
|
-
ParametersNumba,
|
|
41
|
-
parametersNumbaDEFAULT,
|
|
42
|
-
parametersNumbaFailEarly,
|
|
43
|
-
parametersNumbaMinimum,
|
|
44
|
-
parametersNumbaParallelDEFAULT,
|
|
45
|
-
parametersNumbaSuperJit,
|
|
46
|
-
parametersNumbaSuperJitParallel,
|
|
47
|
-
)
|
|
81
|
+
_dictionaryListsImportFrom['mapFolding.theSSOT'].extend([
|
|
82
|
+
'ParametersNumba',
|
|
83
|
+
'parametersNumbaDEFAULT',
|
|
84
|
+
'parametersNumbaFailEarly',
|
|
85
|
+
'parametersNumbaMinimum',
|
|
86
|
+
'parametersNumbaParallelDEFAULT',
|
|
87
|
+
'parametersNumbaSuperJit',
|
|
88
|
+
'parametersNumbaSuperJitParallel',
|
|
89
|
+
])
|
|
48
90
|
|
|
49
91
|
# Coping
|
|
50
|
-
|
|
51
|
-
FREAKOUT,
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
from mapFolding.beDRY import (
|
|
55
|
-
getFilenameFoldsTotal,
|
|
56
|
-
getPathFilenameFoldsTotal,
|
|
57
|
-
outfitCountFolds,
|
|
58
|
-
saveFoldsTotal,
|
|
59
|
-
)
|
|
92
|
+
_dictionaryListsImportFrom['mapFolding.theSSOT'].extend([
|
|
93
|
+
'FREAKOUT',
|
|
94
|
+
])
|
|
60
95
|
|
|
61
|
-
|
|
62
|
-
|
|
96
|
+
_mapSymbolToModule: dict[str, str] = {}
|
|
97
|
+
for moduleAsStr, listSymbolsAsStr in _dictionaryListsImportFrom.items():
|
|
98
|
+
for symbolAsStr in listSymbolsAsStr:
|
|
99
|
+
_mapSymbolToModule[symbolAsStr] = moduleAsStr
|
|
63
100
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
101
|
+
from typing import TYPE_CHECKING
|
|
102
|
+
if TYPE_CHECKING:
|
|
103
|
+
from basecamp import *
|
|
104
|
+
from beDRY import *
|
|
105
|
+
from oeis import *
|
|
106
|
+
from theDao import *
|
|
107
|
+
from theSSOT import *
|
mapFolding/basecamp.py
CHANGED
|
@@ -3,11 +3,10 @@ from mapFolding import computationState, getDispatcherCallable, getPathFilenameF
|
|
|
3
3
|
from os import PathLike
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
|
-
def countFolds(listDimensions: Sequence[int]
|
|
7
|
-
pathLikeWriteFoldsTotal: str | PathLike[str] | None = None
|
|
8
|
-
computationDivisions: int | str | None = None
|
|
9
|
-
CPUlimit: int | float | bool | None = None
|
|
10
|
-
**keywordArguments: str | bool
|
|
6
|
+
def countFolds(listDimensions: Sequence[int]
|
|
7
|
+
, pathLikeWriteFoldsTotal: str | PathLike[str] | None = None
|
|
8
|
+
, computationDivisions: int | str | None = None
|
|
9
|
+
, CPUlimit: int | float | bool | None = None
|
|
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)
|
mapFolding/beDRY.py
CHANGED
|
@@ -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
|
|
|
@@ -139,7 +136,7 @@ def getTaskDivisions(computationDivisions: int | str | None, concurrencyLimit: i
|
|
|
139
136
|
pass
|
|
140
137
|
elif isinstance(computationDivisions, int):
|
|
141
138
|
taskDivisions = computationDivisions
|
|
142
|
-
elif isinstance(computationDivisions, str):
|
|
139
|
+
elif isinstance(computationDivisions, str): # type: ignore 'Unnecessary isinstance call; "str" is always an instance of "str", so sayeth Pylance'. Yeah, well "User is not always an instance of "correct input" so sayeth the programmer.
|
|
143
140
|
computationDivisions = computationDivisions.lower()
|
|
144
141
|
if computationDivisions == 'maximum':
|
|
145
142
|
taskDivisions = leavesTotal
|
|
@@ -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)))
|
|
@@ -298,8 +268,8 @@ def parseDimensions(dimensions: Sequence[int], parameterName: str = 'listDimensi
|
|
|
298
268
|
ValueError: If any dimension is negative or if the list is empty.
|
|
299
269
|
TypeError: If any element cannot be converted to integer (raised by `intInnit`).
|
|
300
270
|
"""
|
|
301
|
-
listValidated = intInnit(dimensions, parameterName)
|
|
302
|
-
listNonNegative = []
|
|
271
|
+
listValidated: list[int] = intInnit(dimensions, parameterName)
|
|
272
|
+
listNonNegative: list[int] = []
|
|
303
273
|
for dimension in listValidated:
|
|
304
274
|
if dimension < 0:
|
|
305
275
|
raise ValueError(f"Dimension {dimension} must be non-negative")
|
|
@@ -357,7 +327,7 @@ def setCPUlimit(CPUlimit: Any | None) -> int:
|
|
|
357
327
|
|
|
358
328
|
concurrencyLimit = int(defineConcurrencyLimit(CPUlimit))
|
|
359
329
|
set_num_threads(concurrencyLimit)
|
|
360
|
-
concurrencyLimit = get_num_threads()
|
|
330
|
+
concurrencyLimit: int = get_num_threads()
|
|
361
331
|
|
|
362
332
|
return concurrencyLimit
|
|
363
333
|
|
mapFolding/oeis.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
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
9
|
import random
|
|
@@ -22,7 +23,7 @@ cacheDays = 7
|
|
|
22
23
|
"""
|
|
23
24
|
Section: make `settingsOEIS`"""
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
pathCache: Path = pathPackage / ".cache"
|
|
26
27
|
|
|
27
28
|
class SettingsOEIS(TypedDict):
|
|
28
29
|
description: str
|
|
@@ -35,38 +36,37 @@ class SettingsOEIS(TypedDict):
|
|
|
35
36
|
valueUnknown: int
|
|
36
37
|
|
|
37
38
|
settingsOEIShardcodedValues: dict[str, dict[str, Any]] = {
|
|
38
|
-
|
|
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
|
-
|
|
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
|
+
},
|
|
68
69
|
}
|
|
69
|
-
|
|
70
70
|
oeisIDsImplemented: Final[list[str]] = sorted([oeisID.upper().strip() for oeisID in settingsOEIShardcodedValues.keys()])
|
|
71
71
|
"""Directly implemented OEIS IDs; standardized, e.g., 'A001415'."""
|
|
72
72
|
|
|
@@ -91,7 +91,7 @@ def validateOEISid(oeisIDcandidate: str) -> str:
|
|
|
91
91
|
if oeisIDcandidate in oeisIDsImplemented:
|
|
92
92
|
return oeisIDcandidate
|
|
93
93
|
else:
|
|
94
|
-
oeisIDcleaned = str(oeisIDcandidate).upper().strip()
|
|
94
|
+
oeisIDcleaned: str = str(oeisIDcandidate).upper().strip()
|
|
95
95
|
if oeisIDcleaned in oeisIDsImplemented:
|
|
96
96
|
return oeisIDcleaned
|
|
97
97
|
else:
|
|
@@ -123,12 +123,12 @@ def _parseBFileOEIS(OEISbFile: str, oeisID: str) -> dict[int, int]:
|
|
|
123
123
|
ValueError: If the first line of the file does not indicate the expected
|
|
124
124
|
sequence ID or if the content format is invalid.
|
|
125
125
|
"""
|
|
126
|
-
bFileLines = OEISbFile.strip().splitlines()
|
|
126
|
+
bFileLines: list[str] = OEISbFile.strip().splitlines()
|
|
127
127
|
if not bFileLines.pop(0).startswith(f"# {oeisID}"):
|
|
128
128
|
warnings.warn(f"Content does not match sequence {oeisID}")
|
|
129
129
|
return {-1: -1}
|
|
130
130
|
|
|
131
|
-
OEISsequence = {}
|
|
131
|
+
OEISsequence: dict[int, int] = {}
|
|
132
132
|
for line in bFileLines:
|
|
133
133
|
if line.startswith('#'):
|
|
134
134
|
continue
|
|
@@ -139,10 +139,10 @@ def _parseBFileOEIS(OEISbFile: str, oeisID: str) -> dict[int, int]:
|
|
|
139
139
|
def getOEISofficial(pathFilenameCache: pathlib.Path, url: str) -> None | str:
|
|
140
140
|
tryCache = False
|
|
141
141
|
if pathFilenameCache.exists():
|
|
142
|
-
fileAge = datetime.now() - datetime.fromtimestamp(pathFilenameCache.stat().st_mtime)
|
|
143
|
-
tryCache = fileAge < timedelta(days=cacheDays)
|
|
142
|
+
fileAge: timedelta = datetime.now() - datetime.fromtimestamp(pathFilenameCache.stat().st_mtime)
|
|
143
|
+
tryCache: bool = fileAge < timedelta(days=cacheDays)
|
|
144
144
|
|
|
145
|
-
oeisInformation = None
|
|
145
|
+
oeisInformation: str | None = None
|
|
146
146
|
if tryCache:
|
|
147
147
|
try:
|
|
148
148
|
oeisInformation = pathFilenameCache.read_text()
|
|
@@ -179,10 +179,10 @@ def getOEISidValues(oeisID: str) -> dict[int, int]:
|
|
|
179
179
|
IOError: If there is an error reading from or writing to the local cache.
|
|
180
180
|
"""
|
|
181
181
|
|
|
182
|
-
pathFilenameCache =
|
|
183
|
-
url = f"https://oeis.org/{oeisID}/{getFilenameOEISbFile(oeisID)}"
|
|
182
|
+
pathFilenameCache: Path = pathCache / getFilenameOEISbFile(oeisID)
|
|
183
|
+
url: str = f"https://oeis.org/{oeisID}/{getFilenameOEISbFile(oeisID)}"
|
|
184
184
|
|
|
185
|
-
oeisInformation = getOEISofficial(pathFilenameCache, url)
|
|
185
|
+
oeisInformation: None | str = getOEISofficial(pathFilenameCache, url)
|
|
186
186
|
|
|
187
187
|
if oeisInformation:
|
|
188
188
|
return _parseBFileOEIS(oeisInformation, oeisID)
|
|
@@ -190,40 +190,37 @@ def getOEISidValues(oeisID: str) -> dict[int, int]:
|
|
|
190
190
|
|
|
191
191
|
def getOEISidInformation(oeisID: str) -> tuple[str, int]:
|
|
192
192
|
oeisID = validateOEISid(oeisID)
|
|
193
|
-
pathFilenameCache =
|
|
194
|
-
url = f"https://oeis.org/search?q=id:{oeisID}&fmt=text"
|
|
193
|
+
pathFilenameCache: Path = pathCache / f"{oeisID}.txt"
|
|
194
|
+
url: str = f"https://oeis.org/search?q=id:{oeisID}&fmt=text"
|
|
195
195
|
|
|
196
|
-
oeisInformation = getOEISofficial(pathFilenameCache, url)
|
|
196
|
+
oeisInformation: None | str = getOEISofficial(pathFilenameCache, url)
|
|
197
197
|
|
|
198
198
|
if not oeisInformation:
|
|
199
199
|
return "Not found", -1
|
|
200
200
|
|
|
201
|
-
|
|
201
|
+
listDescriptionDeconstructed: list[str] = []
|
|
202
202
|
offset = None
|
|
203
|
-
for
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
offset_str = parts[2].split(',')[0]
|
|
213
|
-
offset = int(offset_str)
|
|
214
|
-
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:
|
|
215
212
|
warnings.warn(f"No description found for {oeisID}")
|
|
216
|
-
|
|
213
|
+
listDescriptionDeconstructed.append("No description found")
|
|
217
214
|
if offset is None:
|
|
218
215
|
warnings.warn(f"No offset found for {oeisID}")
|
|
219
216
|
offset = -1
|
|
220
|
-
description = ' '.join(
|
|
217
|
+
description: str = ' '.join(listDescriptionDeconstructed)
|
|
221
218
|
return description, offset
|
|
222
219
|
|
|
223
220
|
def makeSettingsOEIS() -> dict[str, SettingsOEIS]:
|
|
224
|
-
settingsTarget = {}
|
|
221
|
+
settingsTarget: dict[str, SettingsOEIS] = {}
|
|
225
222
|
for oeisID in oeisIDsImplemented:
|
|
226
|
-
valuesKnownSherpa = getOEISidValues(oeisID)
|
|
223
|
+
valuesKnownSherpa: dict[int, int] = getOEISidValues(oeisID)
|
|
227
224
|
descriptionSherpa, offsetSherpa = getOEISidInformation(oeisID)
|
|
228
225
|
settingsTarget[oeisID] = SettingsOEIS(
|
|
229
226
|
description=descriptionSherpa,
|
|
@@ -245,8 +242,8 @@ Section: private functions"""
|
|
|
245
242
|
|
|
246
243
|
def _formatHelpText() -> str:
|
|
247
244
|
"""Format standardized help text for both CLI and interactive use."""
|
|
248
|
-
exampleOEISid = oeisIDsImplemented[0]
|
|
249
|
-
exampleN = settingsOEIS[exampleOEISid]['valuesTestValidation'][-1]
|
|
245
|
+
exampleOEISid: str = oeisIDsImplemented[0]
|
|
246
|
+
exampleN: int = settingsOEIS[exampleOEISid]['valuesTestValidation'][-1]
|
|
250
247
|
|
|
251
248
|
return (
|
|
252
249
|
"\nAvailable OEIS sequences:\n"
|
|
@@ -269,7 +266,7 @@ def _formatOEISsequenceInfo() -> str:
|
|
|
269
266
|
"""
|
|
270
267
|
Section: public functions"""
|
|
271
268
|
|
|
272
|
-
def oeisIDfor_n(oeisID: str, n: int) -> int:
|
|
269
|
+
def oeisIDfor_n(oeisID: str, n: int | Any) -> int:
|
|
273
270
|
"""
|
|
274
271
|
Calculate a(n) of a sequence from "The On-Line Encyclopedia of Integer Sequences" (OEIS).
|
|
275
272
|
|
|
@@ -292,10 +289,10 @@ def oeisIDfor_n(oeisID: str, n: int) -> int:
|
|
|
292
289
|
listDimensions: list[int] = settingsOEIS[oeisID]['getMapShape'](n)
|
|
293
290
|
|
|
294
291
|
if n <= 1 or len(listDimensions) < 2:
|
|
295
|
-
offset = settingsOEIS[oeisID]['offset']
|
|
292
|
+
offset: int = settingsOEIS[oeisID]['offset']
|
|
296
293
|
if n < offset:
|
|
297
294
|
raise ArithmeticError(f"OEIS sequence {oeisID} is not defined at n={n}.")
|
|
298
|
-
foldsTotal = settingsOEIS[oeisID]['valuesKnown'][n]
|
|
295
|
+
foldsTotal: int = settingsOEIS[oeisID]['valuesKnown'][n]
|
|
299
296
|
return foldsTotal
|
|
300
297
|
|
|
301
298
|
return countFolds(listDimensions)
|
|
@@ -320,18 +317,18 @@ def OEIS_for_n() -> None:
|
|
|
320
317
|
print(f"Error: {ERRORmessage}", file=sys.stderr)
|
|
321
318
|
sys.exit(1)
|
|
322
319
|
|
|
323
|
-
timeElapsed = time.perf_counter() - timeStart
|
|
320
|
+
timeElapsed: float = time.perf_counter() - timeStart
|
|
324
321
|
print(f"Time elapsed: {timeElapsed:.3f} seconds")
|
|
325
322
|
|
|
326
323
|
def clearOEIScache() -> None:
|
|
327
324
|
"""Delete all cached OEIS sequence files."""
|
|
328
|
-
if not
|
|
329
|
-
print(f"Cache directory, {
|
|
325
|
+
if not pathCache.exists():
|
|
326
|
+
print(f"Cache directory, {pathCache}, not found - nothing to clear.")
|
|
330
327
|
return
|
|
331
328
|
for oeisID in settingsOEIS:
|
|
332
|
-
(
|
|
333
|
-
(
|
|
334
|
-
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}")
|
|
335
332
|
|
|
336
333
|
def getOEISids() -> None:
|
|
337
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)
|
mapFolding/theDao.py
CHANGED
|
@@ -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()
|