mapFolding 0.4.1__py3-none-any.whl → 0.4.3__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 +2 -1
- mapFolding/basecamp.py +1 -1
- mapFolding/beDRY.py +120 -113
- mapFolding/oeis.py +18 -17
- mapFolding/someAssemblyRequired/synthesizeNumba.py +41 -48
- mapFolding/someAssemblyRequired/synthesizeNumbaGeneralized.py +83 -12
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +12 -23
- mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +82 -30
- mapFolding/syntheticModules/numbaCount.py +158 -0
- mapFolding/syntheticModules/numba_doTheNeedful.py +5 -12
- mapFolding/theDao.py +76 -73
- mapFolding/theSSOT.py +50 -189
- mapFolding/theSSOTdatatypes.py +168 -0
- {mapFolding-0.4.1.dist-info → mapFolding-0.4.3.dist-info}/METADATA +1 -1
- mapFolding-0.4.3.dist-info/RECORD +40 -0
- tests/conftest.py +30 -31
- tests/test_computations.py +27 -63
- tests/test_oeis.py +7 -10
- mapFolding/syntheticModules/numba_countInitialize.py +0 -52
- mapFolding/syntheticModules/numba_countParallel.py +0 -65
- mapFolding/syntheticModules/numba_countSequential.py +0 -67
- mapFolding/theSSOTnumba.py +0 -132
- mapFolding-0.4.1.dist-info/RECORD +0 -42
- {mapFolding-0.4.1.dist-info → mapFolding-0.4.3.dist-info}/LICENSE +0 -0
- {mapFolding-0.4.1.dist-info → mapFolding-0.4.3.dist-info}/WHEEL +0 -0
- {mapFolding-0.4.1.dist-info → mapFolding-0.4.3.dist-info}/entry_points.txt +0 -0
- {mapFolding-0.4.1.dist-info → mapFolding-0.4.3.dist-info}/top_level.txt +0 -0
mapFolding/__init__.py
CHANGED
|
@@ -11,6 +11,7 @@ from mapFolding.theSSOT import (
|
|
|
11
11
|
|
|
12
12
|
# Datatype management
|
|
13
13
|
from mapFolding.theSSOT import (
|
|
14
|
+
getDatatypeModule,
|
|
14
15
|
hackSSOTdatatype,
|
|
15
16
|
hackSSOTdtype,
|
|
16
17
|
setDatatypeElephino,
|
|
@@ -21,7 +22,7 @@ from mapFolding.theSSOT import (
|
|
|
21
22
|
|
|
22
23
|
# Synthesize modules
|
|
23
24
|
from mapFolding.theSSOT import (
|
|
24
|
-
|
|
25
|
+
formatFilenameModuleDEFAULT,
|
|
25
26
|
getAlgorithmDispatcher,
|
|
26
27
|
getAlgorithmSource,
|
|
27
28
|
getPathJobRootDEFAULT,
|
mapFolding/basecamp.py
CHANGED
|
@@ -17,7 +17,7 @@ def countFolds(listDimensions: Sequence[int]
|
|
|
17
17
|
computationDivisions (None):
|
|
18
18
|
Whether and how to divide the computational work. See notes for details.
|
|
19
19
|
CPUlimit (None): This is only relevant if there are `computationDivisions`: whether and how to limit the CPU usage. See notes for details.
|
|
20
|
-
**keywordArguments: Datatype management.
|
|
20
|
+
**keywordArguments: Datatype management. See `outfitCountFolds` for details.
|
|
21
21
|
Returns:
|
|
22
22
|
foldsTotal: Total number of distinct ways to fold a map of the given dimensions.
|
|
23
23
|
|
mapFolding/beDRY.py
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
"""A relatively stable API for oft-needed functionality."""
|
|
2
2
|
from mapFolding import (
|
|
3
3
|
computationState,
|
|
4
|
+
getDatatypeModule,
|
|
4
5
|
getPathJobRootDEFAULT,
|
|
6
|
+
hackSSOTdatatype,
|
|
5
7
|
hackSSOTdtype,
|
|
6
8
|
indexMy,
|
|
7
9
|
indexTrack,
|
|
8
10
|
setDatatypeElephino,
|
|
9
11
|
setDatatypeFoldsTotal,
|
|
10
12
|
setDatatypeLeavesTotal,
|
|
13
|
+
setDatatypeModule,
|
|
11
14
|
)
|
|
12
|
-
from numpy import integer
|
|
15
|
+
from numpy import dtype, integer, ndarray
|
|
13
16
|
from numpy.typing import DTypeLike, NDArray
|
|
14
|
-
from typing import Any, List, Optional, Sequence, Tuple,
|
|
17
|
+
from typing import Any, List, Optional, Sequence, Tuple, Union
|
|
15
18
|
from Z0Z_tools import defineConcurrencyLimit, intInnit, oopsieKwargsie
|
|
16
19
|
import numba
|
|
17
20
|
import numpy
|
|
@@ -19,24 +22,26 @@ import os
|
|
|
19
22
|
import pathlib
|
|
20
23
|
import sys
|
|
21
24
|
|
|
22
|
-
def getFilenameFoldsTotal(mapShape: Union[Sequence[int],
|
|
23
|
-
"""
|
|
25
|
+
def getFilenameFoldsTotal(mapShape: Union[Sequence[int], ndarray[Tuple[int], dtype[integer[Any]]]]) -> str:
|
|
26
|
+
"""Imagine your computer has been counting folds for 70 hours, and when it tries to save your newly discovered value,
|
|
27
|
+
the filename is invalid. I bet you think this function is more important after that thought experiment.
|
|
28
|
+
|
|
29
|
+
Make a standardized filename for the computed value `foldsTotal`.
|
|
24
30
|
|
|
25
31
|
The filename takes into account
|
|
26
32
|
- the dimensions of the map, aka `mapShape`, aka `listDimensions`
|
|
27
33
|
- no spaces in the filename
|
|
28
|
-
- safe filesystem characters
|
|
34
|
+
- safe filesystem characters
|
|
29
35
|
- unique extension
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- if 'p' is still the first character, I picked that because it was the original identifier for the map shape in Lunnan's code
|
|
36
|
+
- Python-safe strings:
|
|
37
|
+
- no starting with a number
|
|
38
|
+
- no reserved words
|
|
39
|
+
- no dashes or other special characters
|
|
40
|
+
- uh, I can't remember, but I found some other frustrating limitations
|
|
41
|
+
- if 'p' is still the first character of the filename, I picked that because it was the original identifier for the map shape in Lunnan's code
|
|
37
42
|
|
|
38
43
|
Parameters:
|
|
39
|
-
mapShape: A sequence of integers representing the dimensions of the map
|
|
44
|
+
mapShape: A sequence of integers representing the dimensions of the map.
|
|
40
45
|
|
|
41
46
|
Returns:
|
|
42
47
|
filenameFoldsTotal: A filename string in format 'pNxM.foldsTotal' where N,M are sorted dimensions
|
|
@@ -67,63 +72,65 @@ def getLeavesTotal(listDimensions: Sequence[int]) -> int:
|
|
|
67
72
|
|
|
68
73
|
return productDimensions
|
|
69
74
|
|
|
70
|
-
def getPathFilenameFoldsTotal(mapShape: Union[Sequence[int],
|
|
71
|
-
"""Get path for
|
|
75
|
+
def getPathFilenameFoldsTotal(mapShape: Union[Sequence[int], ndarray[Tuple[int], dtype[integer[Any]]]], pathLikeWriteFoldsTotal: Optional[Union[str, os.PathLike[str]]] = None) -> pathlib.Path:
|
|
76
|
+
"""Get a standardized path and filename for the computed value `foldsTotal`.
|
|
72
77
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
The function ensures the parent directory exists by creating it if necessary.
|
|
78
|
+
If you provide a directory, the function will append a standardized filename. If you provide a filename
|
|
79
|
+
or a relative path and filename, the function will prepend the default path.
|
|
76
80
|
|
|
77
81
|
Parameters:
|
|
78
|
-
mapShape
|
|
79
|
-
pathLikeWriteFoldsTotal (
|
|
80
|
-
the folds total. Can be a file path or directory path. If None, uses default path.
|
|
82
|
+
mapShape: List of dimensions for the map folding problem.
|
|
83
|
+
pathLikeWriteFoldsTotal (pathJobRootDEFAULT): Path, filename, or relative path and filename. If None, uses default path.
|
|
81
84
|
Defaults to None.
|
|
82
85
|
|
|
83
86
|
Returns:
|
|
84
|
-
|
|
87
|
+
pathFilenameFoldsTotal: Absolute path and filename.
|
|
85
88
|
"""
|
|
86
|
-
|
|
87
|
-
if
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
pathLikeSherpa = pathlib.Path(pathLikeWriteFoldsTotal) if pathLikeWriteFoldsTotal is not None else None
|
|
90
|
+
if not pathLikeSherpa:
|
|
91
|
+
pathLikeSherpa = getPathJobRootDEFAULT()
|
|
92
|
+
if pathLikeSherpa.is_dir():
|
|
93
|
+
pathFilenameFoldsTotal = pathLikeSherpa / getFilenameFoldsTotal(mapShape)
|
|
94
|
+
elif pathLikeSherpa.is_absolute():
|
|
95
|
+
pathFilenameFoldsTotal = pathLikeSherpa
|
|
92
96
|
else:
|
|
93
|
-
pathFilenameFoldsTotal =
|
|
97
|
+
pathFilenameFoldsTotal = getPathJobRootDEFAULT() / pathLikeSherpa
|
|
94
98
|
|
|
95
99
|
pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
|
|
96
100
|
return pathFilenameFoldsTotal
|
|
97
101
|
|
|
98
102
|
def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrencyLimit: int, CPUlimit: Optional[Union[bool, float, int]], listDimensions: Sequence[int]) -> int:
|
|
99
103
|
"""
|
|
100
|
-
Determines whether
|
|
104
|
+
Determines whether to divide the computation into tasks and how many divisions.
|
|
101
105
|
|
|
102
106
|
Parameters
|
|
103
107
|
----------
|
|
104
|
-
computationDivisions (None)
|
|
108
|
+
computationDivisions (None)
|
|
105
109
|
Specifies how to divide computations:
|
|
106
|
-
- None
|
|
107
|
-
- int: direct set the number of task divisions; cannot exceed the map's total leaves
|
|
108
|
-
-
|
|
109
|
-
-
|
|
110
|
-
concurrencyLimit
|
|
111
|
-
Maximum number of concurrent tasks allowed
|
|
112
|
-
CPUlimit
|
|
113
|
-
|
|
110
|
+
- `None`: no division of the computation into tasks; sets task divisions to 0.
|
|
111
|
+
- int: direct set the number of task divisions; cannot exceed the map's total leaves.
|
|
112
|
+
- `'maximum'`: divides into `leavesTotal`-many `taskDivisions`.
|
|
113
|
+
- `'cpu'`: limits the divisions to the number of available CPUs, i.e. `concurrencyLimit`.
|
|
114
|
+
concurrencyLimit
|
|
115
|
+
Maximum number of concurrent tasks allowed.
|
|
116
|
+
CPUlimit
|
|
117
|
+
for error reporting.
|
|
118
|
+
listDimensions
|
|
119
|
+
for error reporting.
|
|
114
120
|
|
|
115
121
|
Returns
|
|
116
122
|
-------
|
|
117
|
-
taskDivisions
|
|
123
|
+
taskDivisions
|
|
124
|
+
How many tasks must finish before the job can compute the total number of folds; `0` means no tasks, only job.
|
|
118
125
|
|
|
119
126
|
Raises
|
|
120
127
|
------
|
|
121
128
|
ValueError
|
|
122
|
-
If computationDivisions is an unsupported type or if resulting task divisions exceed total leaves
|
|
129
|
+
If computationDivisions is an unsupported type or if resulting task divisions exceed total leaves.
|
|
123
130
|
|
|
124
131
|
Notes
|
|
125
132
|
-----
|
|
126
|
-
Task divisions
|
|
133
|
+
Task divisions should not exceed total leaves or the folds will be over-counted.
|
|
127
134
|
"""
|
|
128
135
|
taskDivisions = 0
|
|
129
136
|
leavesTotal = getLeavesTotal(listDimensions)
|
|
@@ -133,9 +140,9 @@ def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrenc
|
|
|
133
140
|
taskDivisions = computationDivisions
|
|
134
141
|
elif isinstance(computationDivisions, str):
|
|
135
142
|
computationDivisions = computationDivisions.lower()
|
|
136
|
-
if computationDivisions ==
|
|
143
|
+
if computationDivisions == 'maximum':
|
|
137
144
|
taskDivisions = leavesTotal
|
|
138
|
-
elif computationDivisions ==
|
|
145
|
+
elif computationDivisions == 'cpu':
|
|
139
146
|
taskDivisions = min(concurrencyLimit, leavesTotal)
|
|
140
147
|
else:
|
|
141
148
|
raise ValueError(f"I received {computationDivisions} for the parameter, `computationDivisions`, but the so-called programmer didn't implement code for that.")
|
|
@@ -145,7 +152,7 @@ def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrenc
|
|
|
145
152
|
|
|
146
153
|
return taskDivisions
|
|
147
154
|
|
|
148
|
-
def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optional[
|
|
155
|
+
def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optional[str]) -> ndarray[Tuple[int, int, int], dtype[integer[Any]]]:
|
|
149
156
|
"""
|
|
150
157
|
Constructs a multi-dimensional connection graph representing the connections between the leaves of a map with the given dimensions.
|
|
151
158
|
Also called a Cartesian product decomposition or dimensional product mapping.
|
|
@@ -157,21 +164,22 @@ def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optio
|
|
|
157
164
|
Returns
|
|
158
165
|
connectionGraph: A 3D numpy array with shape of (dimensionsTotal, leavesTotal + 1, leavesTotal + 1).
|
|
159
166
|
"""
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
167
|
+
ImaSetTheDatatype = keywordArguments.get('datatype', None)
|
|
168
|
+
if ImaSetTheDatatype:
|
|
169
|
+
setDatatypeLeavesTotal(ImaSetTheDatatype)
|
|
170
|
+
dtype = hackSSOTdtype('connectionGraph')
|
|
163
171
|
mapShape = validateListDimensions(listDimensions)
|
|
164
172
|
leavesTotal = getLeavesTotal(mapShape)
|
|
165
|
-
arrayDimensions = numpy.array(mapShape, dtype=
|
|
173
|
+
arrayDimensions = numpy.array(mapShape, dtype=dtype)
|
|
166
174
|
dimensionsTotal = len(arrayDimensions)
|
|
167
175
|
|
|
168
|
-
cumulativeProduct = numpy.multiply.accumulate([1] + mapShape, dtype=
|
|
169
|
-
coordinateSystem = numpy.zeros((dimensionsTotal, leavesTotal + 1), dtype=
|
|
176
|
+
cumulativeProduct = numpy.multiply.accumulate([1] + mapShape, dtype=dtype)
|
|
177
|
+
coordinateSystem = numpy.zeros((dimensionsTotal, leavesTotal + 1), dtype=dtype)
|
|
170
178
|
for indexDimension in range(dimensionsTotal):
|
|
171
179
|
for leaf1ndex in range(1, leavesTotal + 1):
|
|
172
180
|
coordinateSystem[indexDimension, leaf1ndex] = ( ((leaf1ndex - 1) // cumulativeProduct[indexDimension]) % arrayDimensions[indexDimension] + 1 )
|
|
173
181
|
|
|
174
|
-
connectionGraph = numpy.zeros((dimensionsTotal, leavesTotal + 1, leavesTotal + 1), dtype=
|
|
182
|
+
connectionGraph = numpy.zeros((dimensionsTotal, leavesTotal + 1, leavesTotal + 1), dtype=dtype)
|
|
175
183
|
for indexDimension in range(dimensionsTotal):
|
|
176
184
|
for activeLeaf1ndex in range(1, leavesTotal + 1):
|
|
177
185
|
for connectee1ndex in range(1, activeLeaf1ndex + 1):
|
|
@@ -190,70 +198,68 @@ def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optio
|
|
|
190
198
|
return connectionGraph
|
|
191
199
|
|
|
192
200
|
def makeDataContainer(shape: Union[int, Tuple[int, ...]], datatype: Optional[DTypeLike] = None) -> NDArray[integer[Any]]:
|
|
193
|
-
"""Create a zeroed-out `
|
|
201
|
+
"""Create a zeroed-out `ndarray` with the given shape and datatype.
|
|
194
202
|
|
|
195
203
|
Parameters:
|
|
196
|
-
shape
|
|
204
|
+
shape: The shape of the array. Can be an integer for 1D arrays
|
|
197
205
|
or a tuple of integers for multi-dimensional arrays.
|
|
198
|
-
datatype (
|
|
199
|
-
If None
|
|
206
|
+
datatype ('dtypeFoldsTotal'): The desired data type for the array.
|
|
207
|
+
If `None`, defaults to 'dtypeFoldsTotal'. Defaults to None.
|
|
200
208
|
|
|
201
209
|
Returns:
|
|
202
|
-
|
|
210
|
+
dataContainer: A new array of given shape and type, filled with zeros.
|
|
211
|
+
|
|
212
|
+
Notes:
|
|
213
|
+
If a version of the algorithm were to use something other than numpy, such as JAX or CUDA, because other
|
|
214
|
+
functions use this function, it would be much easier to change the datatype "ecosystem".
|
|
203
215
|
"""
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
, **keywordArguments: Optional[Union[str, bool]]) -> computationState:
|
|
216
|
+
numpyDtype = datatype or hackSSOTdtype('dtypeFoldsTotal')
|
|
217
|
+
if 'numpy' == getDatatypeModule():
|
|
218
|
+
return numpy.zeros(shape, dtype=numpyDtype)
|
|
219
|
+
else:
|
|
220
|
+
raise NotImplementedError("Somebody done broke it.")
|
|
221
|
+
|
|
222
|
+
def outfitCountFolds(listDimensions: Sequence[int], computationDivisions: Optional[Union[int, str]] = None, CPUlimit: Optional[Union[bool, float, int]] = None, **keywordArguments: Optional[Union[str, bool]]) -> computationState:
|
|
212
223
|
"""
|
|
213
224
|
Initializes and configures the computation state for map folding computations.
|
|
214
225
|
|
|
215
|
-
Parameters
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
Specifies how to divide computations:
|
|
221
|
-
- None: no division of the computation into tasks; sets task divisions to 0
|
|
222
|
-
- int: direct set the number of task divisions; cannot exceed the map's total leaves
|
|
223
|
-
- "maximum": divides into `leavesTotal`-many `taskDivisions`
|
|
224
|
-
- "cpu": limits the divisions to the number of available CPUs, i.e. `concurrencyLimit`
|
|
225
|
-
CPUlimit (None):
|
|
226
|
-
Whether and how to limit the CPU usage. See notes for details.
|
|
227
|
-
**keywordArguments:
|
|
228
|
-
Datatype management.
|
|
229
|
-
|
|
230
|
-
Returns
|
|
231
|
-
-------
|
|
232
|
-
computationState
|
|
233
|
-
An initialized computation state containing:
|
|
234
|
-
- connectionGraph: Graph representing connections in the map
|
|
235
|
-
- foldsSubTotals: Array tracking total folds
|
|
236
|
-
- mapShape: Validated and sorted dimensions of the map
|
|
237
|
-
- my: Array for internal state tracking
|
|
238
|
-
- gapsWhere: Array tracking gap positions
|
|
239
|
-
- the: Static settings and metadata
|
|
240
|
-
- track: Array for tracking computation progress
|
|
226
|
+
Parameters:
|
|
227
|
+
listDimensions: The dimensions of the map to be folded
|
|
228
|
+
computationDivisions (None): see `getTaskDivisions`
|
|
229
|
+
CPUlimit (None): see `setCPUlimit`
|
|
230
|
+
**keywordArguments: Datatype management, it's complicated: see the code below.
|
|
241
231
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
- `True`: Yes, limit the CPU usage; limits to 1 CPU.
|
|
245
|
-
- Integer `>= 1`: Limits usage to the specified number of CPUs.
|
|
246
|
-
- Decimal value (`float`) between 0 and 1: Fraction of total CPUs to use.
|
|
247
|
-
- Decimal value (`float`) between -1 and 0: Fraction of CPUs to *not* use.
|
|
248
|
-
- Integer `<= -1`: Subtract the absolute value from total CPUs.
|
|
232
|
+
Returns:
|
|
233
|
+
stateInitialized: The initialized computation state
|
|
249
234
|
"""
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
235
|
+
# keywordArguments START
|
|
236
|
+
kwourGrapes = keywordArguments.get('sourGrapes', None)
|
|
237
|
+
if kwourGrapes:
|
|
238
|
+
sourGrapes = True
|
|
239
|
+
else:
|
|
240
|
+
sourGrapes = False
|
|
241
|
+
|
|
242
|
+
ImaSetTheDatatype = keywordArguments.get('datatypeElephino', None)
|
|
243
|
+
if ImaSetTheDatatype:
|
|
244
|
+
ImaSetTheDatatype = str(ImaSetTheDatatype)
|
|
245
|
+
setDatatypeElephino(ImaSetTheDatatype, sourGrapes)
|
|
246
|
+
|
|
247
|
+
ImaSetTheDatatype = keywordArguments.get('datatypeFoldsTotal', None)
|
|
248
|
+
if ImaSetTheDatatype:
|
|
249
|
+
ImaSetTheDatatype = str(ImaSetTheDatatype)
|
|
250
|
+
setDatatypeFoldsTotal(ImaSetTheDatatype, sourGrapes)
|
|
251
|
+
|
|
252
|
+
ImaSetTheDatatype = keywordArguments.get('datatypeLeavesTotal', None)
|
|
253
|
+
if ImaSetTheDatatype:
|
|
254
|
+
ImaSetTheDatatype = str(ImaSetTheDatatype)
|
|
255
|
+
setDatatypeLeavesTotal(ImaSetTheDatatype, sourGrapes)
|
|
256
|
+
|
|
257
|
+
# 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.
|
|
258
|
+
ImaSetTheDatatype = keywordArguments.get('datatypeModule', None)
|
|
259
|
+
if ImaSetTheDatatype:
|
|
260
|
+
ImaSetTheDatatype = str(ImaSetTheDatatype)
|
|
261
|
+
setDatatypeModule(ImaSetTheDatatype, sourGrapes)
|
|
262
|
+
# keywordArguments END
|
|
257
263
|
|
|
258
264
|
my = makeDataContainer(len(indexMy), hackSSOTdtype('my'))
|
|
259
265
|
|
|
@@ -268,7 +274,7 @@ def outfitCountFolds(listDimensions: Sequence[int]
|
|
|
268
274
|
my[indexMy.dimensionsTotal] = len(mapShape)
|
|
269
275
|
my[indexMy.leaf1ndex] = 1
|
|
270
276
|
stateInitialized = computationState(
|
|
271
|
-
connectionGraph = makeConnectionGraph(mapShape, datatype=
|
|
277
|
+
connectionGraph = makeConnectionGraph(mapShape, datatype=hackSSOTdatatype('connectionGraph')),
|
|
272
278
|
foldGroups = foldGroups,
|
|
273
279
|
mapShape = numpy.array(mapShape, dtype=hackSSOTdtype('mapShape')),
|
|
274
280
|
my = my,
|
|
@@ -280,16 +286,16 @@ def outfitCountFolds(listDimensions: Sequence[int]
|
|
|
280
286
|
|
|
281
287
|
def parseDimensions(dimensions: Sequence[int], parameterName: str = 'listDimensions') -> List[int]:
|
|
282
288
|
"""
|
|
283
|
-
Parse and validate dimensions are non-negative integers.
|
|
289
|
+
Parse and validate the dimensions are non-negative integers.
|
|
284
290
|
|
|
285
291
|
Parameters:
|
|
286
|
-
dimensions: Sequence of integers representing dimensions
|
|
287
|
-
parameterName ('listDimensions'): Name of the parameter for error messages. Defaults to 'listDimensions'
|
|
292
|
+
dimensions: Sequence of integers representing dimensions.
|
|
293
|
+
parameterName ('listDimensions'): Name of the parameter for error messages. Defaults to 'listDimensions'.
|
|
288
294
|
Returns:
|
|
289
|
-
listNonNegative: List of validated non-negative integers
|
|
295
|
+
listNonNegative: List of validated non-negative integers.
|
|
290
296
|
Raises:
|
|
291
|
-
ValueError: If any dimension is negative or if the list is empty
|
|
292
|
-
TypeError: If any element cannot be converted to integer (raised by intInnit)
|
|
297
|
+
ValueError: If any dimension is negative or if the list is empty.
|
|
298
|
+
TypeError: If any element cannot be converted to integer (raised by `intInnit`).
|
|
293
299
|
"""
|
|
294
300
|
listValidated = intInnit(dimensions, parameterName)
|
|
295
301
|
listNonNegative = []
|
|
@@ -350,6 +356,7 @@ def setCPUlimit(CPUlimit: Optional[Any]) -> int:
|
|
|
350
356
|
|
|
351
357
|
concurrencyLimit = int(defineConcurrencyLimit(CPUlimit))
|
|
352
358
|
numba.set_num_threads(concurrencyLimit)
|
|
359
|
+
concurrencyLimit = numba.get_num_threads()
|
|
353
360
|
|
|
354
361
|
return concurrencyLimit
|
|
355
362
|
|
|
@@ -364,7 +371,7 @@ def validateListDimensions(listDimensions: Sequence[int]) -> List[int]:
|
|
|
364
371
|
dimensionsValidSorted: A list, with at least two elements, of only positive integers.
|
|
365
372
|
|
|
366
373
|
Raises:
|
|
367
|
-
ValueError: If the input listDimensions is
|
|
374
|
+
ValueError: If the input listDimensions is empty.
|
|
368
375
|
NotImplementedError: If the resulting list of positive dimensions has fewer than two elements.
|
|
369
376
|
"""
|
|
370
377
|
if not listDimensions:
|
mapFolding/oeis.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
_only_ things that implement _only_ the OEIS."""
|
|
3
3
|
from datetime import datetime, timedelta
|
|
4
4
|
from mapFolding import countFolds, getPathPackage
|
|
5
|
-
from typing import Any, Callable, Dict, Final, List, Tuple, TYPE_CHECKING
|
|
5
|
+
from typing import Any, Callable, Dict, Final, List, Tuple, TYPE_CHECKING
|
|
6
6
|
import argparse
|
|
7
7
|
import pathlib
|
|
8
8
|
import random
|
|
@@ -24,8 +24,8 @@ _pathCache = getPathPackage() / ".cache"
|
|
|
24
24
|
|
|
25
25
|
class SettingsOEIS(TypedDict):
|
|
26
26
|
description: str
|
|
27
|
-
offset: int
|
|
28
27
|
getMapShape: Callable[[int], List[int]]
|
|
28
|
+
offset: int
|
|
29
29
|
valuesBenchmark: List[int]
|
|
30
30
|
valuesKnown: Dict[int, int]
|
|
31
31
|
valuesTestParallelization: List[int]
|
|
@@ -68,7 +68,7 @@ settingsOEIShardcodedValues: Dict[str, Dict[str, Any]] = {
|
|
|
68
68
|
oeisIDsImplemented: Final[List[str]] = sorted([oeisID.upper().strip() for oeisID in settingsOEIShardcodedValues.keys()])
|
|
69
69
|
"""Directly implemented OEIS IDs; standardized, e.g., 'A001415'."""
|
|
70
70
|
|
|
71
|
-
def
|
|
71
|
+
def validateOEISid(oeisIDcandidate: str) -> str:
|
|
72
72
|
"""
|
|
73
73
|
Validates an OEIS sequence ID against implemented sequences.
|
|
74
74
|
|
|
@@ -98,13 +98,14 @@ def _validateOEISid(oeisIDcandidate: str) -> str:
|
|
|
98
98
|
f"Available sequences:\n{_formatOEISsequenceInfo()}"
|
|
99
99
|
)
|
|
100
100
|
|
|
101
|
-
def
|
|
102
|
-
oeisID =
|
|
101
|
+
def getFilenameOEISbFile(oeisID: str) -> str:
|
|
102
|
+
oeisID = validateOEISid(oeisID)
|
|
103
103
|
return f"b{oeisID[1:]}.txt"
|
|
104
104
|
|
|
105
105
|
def _parseBFileOEIS(OEISbFile: str, oeisID: str) -> Dict[int, int]:
|
|
106
106
|
"""
|
|
107
107
|
Parses the content of an OEIS b-file for a given sequence ID.
|
|
108
|
+
|
|
108
109
|
This function processes a multiline string representing an OEIS b-file and
|
|
109
110
|
creates a dictionary mapping integer indices to their corresponding sequence
|
|
110
111
|
values. The first line of the b-file is expected to contain a comment that
|
|
@@ -133,7 +134,7 @@ def _parseBFileOEIS(OEISbFile: str, oeisID: str) -> Dict[int, int]:
|
|
|
133
134
|
OEISsequence[n] = aOFn
|
|
134
135
|
return OEISsequence
|
|
135
136
|
|
|
136
|
-
def
|
|
137
|
+
def getOEISofficial(pathFilenameCache: pathlib.Path, url: str) -> None | str:
|
|
137
138
|
cacheDays = 7
|
|
138
139
|
tryCache = False
|
|
139
140
|
if pathFilenameCache.exists():
|
|
@@ -158,7 +159,7 @@ def _getOEISofficial(pathFilenameCache: pathlib.Path, url: str) -> None | str:
|
|
|
158
159
|
|
|
159
160
|
return oeisInformation
|
|
160
161
|
|
|
161
|
-
def
|
|
162
|
+
def getOEISidValues(oeisID: str) -> Dict[int, int]:
|
|
162
163
|
"""
|
|
163
164
|
Retrieves the specified OEIS sequence as a dictionary mapping integer indices
|
|
164
165
|
to their corresponding values.
|
|
@@ -177,21 +178,21 @@ def _getOEISidValues(oeisID: str) -> Dict[int, int]:
|
|
|
177
178
|
IOError: If there is an error reading from or writing to the local cache.
|
|
178
179
|
"""
|
|
179
180
|
|
|
180
|
-
pathFilenameCache = _pathCache /
|
|
181
|
-
url = f"https://oeis.org/{oeisID}/{
|
|
181
|
+
pathFilenameCache = _pathCache / getFilenameOEISbFile(oeisID)
|
|
182
|
+
url = f"https://oeis.org/{oeisID}/{getFilenameOEISbFile(oeisID)}"
|
|
182
183
|
|
|
183
|
-
oeisInformation =
|
|
184
|
+
oeisInformation = getOEISofficial(pathFilenameCache, url)
|
|
184
185
|
|
|
185
186
|
if oeisInformation:
|
|
186
187
|
return _parseBFileOEIS(oeisInformation, oeisID)
|
|
187
188
|
return {-1: -1}
|
|
188
189
|
|
|
189
|
-
def
|
|
190
|
-
oeisID =
|
|
190
|
+
def getOEISidInformation(oeisID: str) -> Tuple[str, int]:
|
|
191
|
+
oeisID = validateOEISid(oeisID)
|
|
191
192
|
pathFilenameCache = _pathCache / f"{oeisID}.txt"
|
|
192
193
|
url = f"https://oeis.org/search?q=id:{oeisID}&fmt=text"
|
|
193
194
|
|
|
194
|
-
oeisInformation =
|
|
195
|
+
oeisInformation = getOEISofficial(pathFilenameCache, url)
|
|
195
196
|
|
|
196
197
|
if not oeisInformation:
|
|
197
198
|
return "Not found", -1
|
|
@@ -221,8 +222,8 @@ def _getOEISidInformation(oeisID: str) -> Tuple[str, int]:
|
|
|
221
222
|
def makeSettingsOEIS() -> Dict[str, SettingsOEIS]:
|
|
222
223
|
settingsTarget = {}
|
|
223
224
|
for oeisID in oeisIDsImplemented:
|
|
224
|
-
valuesKnownSherpa =
|
|
225
|
-
descriptionSherpa, offsetSherpa =
|
|
225
|
+
valuesKnownSherpa = getOEISidValues(oeisID)
|
|
226
|
+
descriptionSherpa, offsetSherpa = getOEISidInformation(oeisID)
|
|
226
227
|
settingsTarget[oeisID] = SettingsOEIS(
|
|
227
228
|
description=descriptionSherpa,
|
|
228
229
|
offset=offsetSherpa,
|
|
@@ -282,7 +283,7 @@ def oeisIDfor_n(oeisID: str, n: int) -> int:
|
|
|
282
283
|
ValueError: If n is negative.
|
|
283
284
|
KeyError: If the OEIS sequence ID is not directly implemented.
|
|
284
285
|
"""
|
|
285
|
-
oeisID =
|
|
286
|
+
oeisID = validateOEISid(oeisID)
|
|
286
287
|
|
|
287
288
|
if not isinstance(n, int) or n < 0:
|
|
288
289
|
raise ValueError("`n` must be non-negative integer.")
|
|
@@ -328,7 +329,7 @@ def clearOEIScache() -> None:
|
|
|
328
329
|
return
|
|
329
330
|
for oeisID in settingsOEIS:
|
|
330
331
|
( _pathCache / f"{oeisID}.txt" ).unlink(missing_ok=True)
|
|
331
|
-
( _pathCache /
|
|
332
|
+
( _pathCache / getFilenameOEISbFile(oeisID) ).unlink(missing_ok=True)
|
|
332
333
|
print(f"Cache cleared from {_pathCache}")
|
|
333
334
|
|
|
334
335
|
def getOEISids() -> None:
|