mapFolding 0.7.0__tar.gz → 0.8.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.7.0 → mapfolding-0.8.0}/PKG-INFO +6 -7
- {mapfolding-0.7.0 → mapfolding-0.8.0}/README.md +4 -4
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/__init__.py +1 -1
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/basecamp.py +2 -2
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/beDRY.py +88 -85
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/filesystem.py +37 -29
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/noHomeYet.py +2 -2
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/oeis.py +2 -2
- mapfolding-0.8.0/mapFolding/someAssemblyRequired/Z0Z_workbench.py +350 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/someAssemblyRequired/__init__.py +4 -3
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/someAssemblyRequired/getLLVMforNoReason.py +0 -1
- mapfolding-0.8.0/mapFolding/someAssemblyRequired/ingredientsNumba.py +185 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/someAssemblyRequired/synthesizeDataConverters.py +34 -52
- mapfolding-0.7.0/mapFolding/someAssemblyRequired/synthesizeNumbaJob.py → mapfolding-0.8.0/mapFolding/someAssemblyRequired/synthesizeNumbaJobVESTIGIAL.py +18 -21
- mapfolding-0.8.0/mapFolding/someAssemblyRequired/transformationTools.py +763 -0
- mapfolding-0.8.0/mapFolding/syntheticModules/numbaCount_doTheNeedful.py +198 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/theDao.py +57 -39
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/theSSOT.py +59 -59
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding.egg-info/PKG-INFO +6 -7
- mapfolding-0.8.0/mapFolding.egg-info/SOURCES.txt +44 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding.egg-info/requires.txt +1 -2
- {mapfolding-0.7.0 → mapfolding-0.8.0}/pyproject.toml +15 -14
- {mapfolding-0.7.0 → mapfolding-0.8.0}/tests/conftest.py +2 -3
- {mapfolding-0.7.0 → mapfolding-0.8.0}/tests/test_computations.py +9 -5
- {mapfolding-0.7.0 → mapfolding-0.8.0}/tests/test_filesystem.py +0 -2
- {mapfolding-0.7.0 → mapfolding-0.8.0}/tests/test_other.py +2 -3
- {mapfolding-0.7.0 → mapfolding-0.8.0}/tests/test_tasks.py +7 -5
- mapfolding-0.7.0/mapFolding/someAssemblyRequired/Z0Z_workbench.py +0 -34
- mapfolding-0.7.0/mapFolding/someAssemblyRequired/ingredientsNumba.py +0 -100
- mapfolding-0.7.0/mapFolding/someAssemblyRequired/synthesizeCountingFunctions.py +0 -7
- mapfolding-0.7.0/mapFolding/someAssemblyRequired/synthesizeNumba.py +0 -91
- mapfolding-0.7.0/mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +0 -91
- mapfolding-0.7.0/mapFolding/someAssemblyRequired/transformationTools.py +0 -425
- mapfolding-0.7.0/mapFolding/someAssemblyRequired/whatWillBe.py +0 -311
- mapfolding-0.7.0/mapFolding/syntheticModules/dataNamespaceFlattened.py +0 -30
- mapfolding-0.7.0/mapFolding/syntheticModules/numbaCount.py +0 -90
- mapfolding-0.7.0/mapFolding/syntheticModules/numbaCountSequential.py +0 -110
- mapfolding-0.7.0/mapFolding/syntheticModules/numba_doTheNeedful.py +0 -12
- mapfolding-0.7.0/mapFolding/syntheticModules/numba_doTheNeedfulExample.py +0 -13
- mapfolding-0.7.0/mapFolding.egg-info/SOURCES.txt +0 -97
- mapfolding-0.7.0/tests/__init__.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/LICENSE +0 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/py.typed +0 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/reference/flattened.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/reference/hunterNumba.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/reference/irvineJavaPort.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/reference/jax.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/reference/lunnan.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/reference/lunnanNumpy.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/reference/lunnanWhile.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/reference/rotatedEntryPoint.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding/reference/total_countPlus1vsPlusN.py +0 -0
- /mapfolding-0.7.0/mapFolding/syntheticModules/numbaCountExample.py → /mapfolding-0.8.0/mapFolding/syntheticModules/numbaCountHistoricalExample.py +0 -0
- /mapfolding-0.7.0/mapFolding/syntheticModules/numbaCount_doTheNeedful.py → /mapfolding-0.8.0/mapFolding/syntheticModules/numba_doTheNeedfulHistoricalExample.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding.egg-info/dependency_links.txt +0 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding.egg-info/entry_points.txt +0 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/mapFolding.egg-info/top_level.txt +0 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/setup.cfg +0 -0
- {mapfolding-0.7.0/mapFolding/syntheticModules → mapfolding-0.8.0/tests}/__init__.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.8.0}/tests/test_oeis.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: mapFolding
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.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
|
|
@@ -38,12 +38,11 @@ Requires-Dist: tomli
|
|
|
38
38
|
Requires-Dist: Z0Z_tools
|
|
39
39
|
Provides-Extra: testing
|
|
40
40
|
Requires-Dist: mypy; extra == "testing"
|
|
41
|
+
Requires-Dist: pytest; extra == "testing"
|
|
41
42
|
Requires-Dist: pytest-cov; extra == "testing"
|
|
42
43
|
Requires-Dist: pytest-env; extra == "testing"
|
|
43
44
|
Requires-Dist: pytest-xdist; extra == "testing"
|
|
44
|
-
Requires-Dist: pytest; extra == "testing"
|
|
45
45
|
Requires-Dist: pyupgrade; extra == "testing"
|
|
46
|
-
Requires-Dist: updateCitation; extra == "testing"
|
|
47
46
|
|
|
48
47
|
# mapFolding: Algorithms for enumerating distinct map/stamp folding patterns 🗺️
|
|
49
48
|
|
|
@@ -113,14 +112,14 @@ Available OEIS sequences:
|
|
|
113
112
|
|
|
114
113
|
### 4. **Customizing your algorithm**
|
|
115
114
|
|
|
116
|
-
- mapFolding\someAssemblyRequired\synthesizeNumbaJob.py (and/or synthesizeNumba____.py, as applicable)
|
|
115
|
+
- Renovations in progress: ~~mapFolding\someAssemblyRequired\synthesizeNumbaJob.py (and/or synthesizeNumba____.py, as applicable)~~
|
|
117
116
|
- Synthesize a Numba-optimized module for a specific mapShape
|
|
118
117
|
- Synthesize _from_ a module in mapFolding\syntheticModules or from any source you select
|
|
119
118
|
- Use the existing transformation options
|
|
120
119
|
- Or create new ways of transforming the algorithm from its source to a specific job
|
|
121
|
-
- mapFolding\someAssemblyRequired\makeJob.py
|
|
120
|
+
- Renovations in progress: ~~mapFolding\someAssemblyRequired\makeJob.py~~
|
|
122
121
|
- Initialize data for a specific mapShape
|
|
123
|
-
- mapFolding\someAssemblyRequired\synthesizeNumbaModules.py (and/or synthesizeNumba____.py, as applicable)
|
|
122
|
+
- Renovations in progress: ~~mapFolding\someAssemblyRequired\synthesizeNumbaModules.py (and/or synthesizeNumba____.py, as applicable)~~
|
|
124
123
|
- Synthesize one or more Numba-optimized modules for parallel or sequential computation
|
|
125
124
|
- Overwrite the modules in mapFolding\syntheticModules or save the module(s) to a custom path
|
|
126
125
|
- Synthesize _from_ the algorithm(s) in mapFolding\theDao.py or from any source you select
|
|
@@ -131,7 +130,7 @@ Available OEIS sequences:
|
|
|
131
130
|
- Modify the algorithms for initializing values, parallel computation, and/or sequential computation
|
|
132
131
|
- Use the modified algorithm(s) in synthesizeNumbaModules.py, above, to create Numba-optimized version(s)
|
|
133
132
|
- Then use a Numba-optimized version in synthesizeNumbaJob.py, above, to create a hyper-optimized version for a specific mapShape
|
|
134
|
-
- mapFolding\theSSOT.py
|
|
133
|
+
- mapFolding\theSSOT.py
|
|
135
134
|
- Modify broad settings or find functions to modify broad settings, such as data structures and their data types
|
|
136
135
|
- Create new settings or groups of settings
|
|
137
136
|
- mapFolding\beDRY.py
|
|
@@ -66,14 +66,14 @@ Available OEIS sequences:
|
|
|
66
66
|
|
|
67
67
|
### 4. **Customizing your algorithm**
|
|
68
68
|
|
|
69
|
-
- mapFolding\someAssemblyRequired\synthesizeNumbaJob.py (and/or synthesizeNumba____.py, as applicable)
|
|
69
|
+
- Renovations in progress: ~~mapFolding\someAssemblyRequired\synthesizeNumbaJob.py (and/or synthesizeNumba____.py, as applicable)~~
|
|
70
70
|
- Synthesize a Numba-optimized module for a specific mapShape
|
|
71
71
|
- Synthesize _from_ a module in mapFolding\syntheticModules or from any source you select
|
|
72
72
|
- Use the existing transformation options
|
|
73
73
|
- Or create new ways of transforming the algorithm from its source to a specific job
|
|
74
|
-
- mapFolding\someAssemblyRequired\makeJob.py
|
|
74
|
+
- Renovations in progress: ~~mapFolding\someAssemblyRequired\makeJob.py~~
|
|
75
75
|
- Initialize data for a specific mapShape
|
|
76
|
-
- mapFolding\someAssemblyRequired\synthesizeNumbaModules.py (and/or synthesizeNumba____.py, as applicable)
|
|
76
|
+
- Renovations in progress: ~~mapFolding\someAssemblyRequired\synthesizeNumbaModules.py (and/or synthesizeNumba____.py, as applicable)~~
|
|
77
77
|
- Synthesize one or more Numba-optimized modules for parallel or sequential computation
|
|
78
78
|
- Overwrite the modules in mapFolding\syntheticModules or save the module(s) to a custom path
|
|
79
79
|
- Synthesize _from_ the algorithm(s) in mapFolding\theDao.py or from any source you select
|
|
@@ -84,7 +84,7 @@ Available OEIS sequences:
|
|
|
84
84
|
- Modify the algorithms for initializing values, parallel computation, and/or sequential computation
|
|
85
85
|
- Use the modified algorithm(s) in synthesizeNumbaModules.py, above, to create Numba-optimized version(s)
|
|
86
86
|
- Then use a Numba-optimized version in synthesizeNumbaJob.py, above, to create a hyper-optimized version for a specific mapShape
|
|
87
|
-
- mapFolding\theSSOT.py
|
|
87
|
+
- mapFolding\theSSOT.py
|
|
88
88
|
- Modify broad settings or find functions to modify broad settings, such as data structures and their data types
|
|
89
89
|
- Create new settings or groups of settings
|
|
90
90
|
- mapFolding\beDRY.py
|
|
@@ -43,8 +43,8 @@ def countFolds(listDimensions: Sequence[int]
|
|
|
43
43
|
concurrencyLimit: int = setCPUlimit(CPUlimit)
|
|
44
44
|
computationStateInitialized: ComputationState = outfitCountFolds(mapShape, computationDivisions, concurrencyLimit)
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
computationStateComplete: ComputationState =
|
|
46
|
+
dispatcherCallableProxy = getPackageDispatcher()
|
|
47
|
+
computationStateComplete: ComputationState = dispatcherCallableProxy(computationStateInitialized)
|
|
48
48
|
|
|
49
49
|
computationStateComplete.getFoldsTotal()
|
|
50
50
|
|
|
@@ -1,30 +1,11 @@
|
|
|
1
1
|
"""A relatively stable API for oft-needed functionality."""
|
|
2
|
-
from mapFolding.theSSOT import (
|
|
3
|
-
Array3D,
|
|
4
|
-
ComputationState,
|
|
5
|
-
getDatatypePackage,
|
|
6
|
-
getNumpyDtypeDefault,
|
|
7
|
-
)
|
|
8
2
|
from collections.abc import Sequence
|
|
3
|
+
from mapFolding.theSSOT import Array3D, ComputationState, getDatatypePackage, getNumpyDtypeDefault
|
|
9
4
|
from sys import maxsize as sysMaxsize
|
|
10
5
|
from typing import Any
|
|
11
6
|
from Z0Z_tools import defineConcurrencyLimit, intInnit, oopsieKwargsie
|
|
12
7
|
import numpy
|
|
13
8
|
|
|
14
|
-
def validateListDimensions(listDimensions: Sequence[int]) -> tuple[int, ...]:
|
|
15
|
-
if not listDimensions:
|
|
16
|
-
raise ValueError("listDimensions is a required parameter.")
|
|
17
|
-
listValidated: list[int] = intInnit(listDimensions, 'listDimensions')
|
|
18
|
-
listNonNegative: list[int] = []
|
|
19
|
-
for dimension in listValidated:
|
|
20
|
-
if dimension < 0:
|
|
21
|
-
raise ValueError(f"Dimension {dimension} must be non-negative")
|
|
22
|
-
listNonNegative.append(dimension)
|
|
23
|
-
dimensionsValid = [dimension for dimension in listNonNegative if dimension > 0]
|
|
24
|
-
if len(dimensionsValid) < 2:
|
|
25
|
-
raise NotImplementedError(f"This function requires listDimensions, {listDimensions}, to have at least two dimensions greater than 0. You may want to look at https://oeis.org/.")
|
|
26
|
-
return tuple(sorted(dimensionsValid))
|
|
27
|
-
|
|
28
9
|
def getLeavesTotal(mapShape: tuple[int, ...]) -> int:
|
|
29
10
|
productDimensions = 1
|
|
30
11
|
for dimension in mapShape:
|
|
@@ -33,7 +14,59 @@ def getLeavesTotal(mapShape: tuple[int, ...]) -> int:
|
|
|
33
14
|
productDimensions *= dimension
|
|
34
15
|
return productDimensions
|
|
35
16
|
|
|
36
|
-
def
|
|
17
|
+
def getTaskDivisions(computationDivisions: int | str | None, concurrencyLimit: int, leavesTotal: int) -> int:
|
|
18
|
+
"""
|
|
19
|
+
Determines whether to divide the computation into tasks and how many divisions.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
computationDivisions (None)
|
|
24
|
+
Specifies how to divide computations:
|
|
25
|
+
- `None`: no division of the computation into tasks; sets task divisions to 0.
|
|
26
|
+
- int: direct set the number of task divisions; cannot exceed the map's total leaves.
|
|
27
|
+
- `'maximum'`: divides into `leavesTotal`-many `taskDivisions`.
|
|
28
|
+
- `'cpu'`: limits the divisions to the number of available CPUs, i.e. `concurrencyLimit`.
|
|
29
|
+
concurrencyLimit
|
|
30
|
+
Maximum number of concurrent tasks allowed.
|
|
31
|
+
CPUlimit
|
|
32
|
+
for error reporting.
|
|
33
|
+
listDimensions
|
|
34
|
+
for error reporting.
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
taskDivisions
|
|
39
|
+
How many tasks must finish before the job can compute the total number of folds; `0` means no tasks, only job.
|
|
40
|
+
|
|
41
|
+
Raises
|
|
42
|
+
------
|
|
43
|
+
ValueError
|
|
44
|
+
If computationDivisions is an unsupported type or if resulting task divisions exceed total leaves.
|
|
45
|
+
|
|
46
|
+
Notes
|
|
47
|
+
-----
|
|
48
|
+
Task divisions should not exceed total leaves or the folds will be over-counted.
|
|
49
|
+
"""
|
|
50
|
+
taskDivisions = 0
|
|
51
|
+
if not computationDivisions:
|
|
52
|
+
pass
|
|
53
|
+
elif isinstance(computationDivisions, int):
|
|
54
|
+
taskDivisions = computationDivisions
|
|
55
|
+
elif isinstance(computationDivisions, str): # type: ignore
|
|
56
|
+
# '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.
|
|
57
|
+
computationDivisions = computationDivisions.lower()
|
|
58
|
+
if computationDivisions == 'maximum':
|
|
59
|
+
taskDivisions = leavesTotal
|
|
60
|
+
elif computationDivisions == 'cpu':
|
|
61
|
+
taskDivisions = min(concurrencyLimit, leavesTotal)
|
|
62
|
+
else:
|
|
63
|
+
raise ValueError(f"I received {computationDivisions} for the parameter, `computationDivisions`, but the so-called programmer didn't implement code for that.")
|
|
64
|
+
|
|
65
|
+
if taskDivisions > leavesTotal:
|
|
66
|
+
raise ValueError(f"Problem: `taskDivisions`, ({taskDivisions}), is greater than `leavesTotal`, ({leavesTotal}), which will cause duplicate counting of the folds.\n\nChallenge: you cannot directly set `taskDivisions` or `leavesTotal`. They are derived from parameters that may or may not still be named `computationDivisions`, `CPUlimit` , and `listDimensions` and from dubious-quality Python code.")
|
|
67
|
+
return int(max(0, taskDivisions))
|
|
68
|
+
|
|
69
|
+
def interpretParameter_datatype(datatype: type[numpy.signedinteger[Any]] | None = None) -> type[numpy.signedinteger[Any]]:
|
|
37
70
|
"""An imperfect way to reduce code duplication."""
|
|
38
71
|
if 'numpy' == getDatatypePackage():
|
|
39
72
|
numpyDtype = datatype or getNumpyDtypeDefault()
|
|
@@ -42,7 +75,7 @@ def getNumpyDtype(datatype: type[numpy.signedinteger[Any]] | None = None) -> typ
|
|
|
42
75
|
return numpyDtype
|
|
43
76
|
|
|
44
77
|
def makeConnectionGraph(mapShape: tuple[int, ...], leavesTotal: int, datatype: type[numpy.signedinteger[Any]] | None = None) -> Array3D:
|
|
45
|
-
numpyDtype =
|
|
78
|
+
numpyDtype = interpretParameter_datatype(datatype)
|
|
46
79
|
dimensionsTotal = len(mapShape)
|
|
47
80
|
cumulativeProduct = numpy.multiply.accumulate([1] + list(mapShape), dtype=numpyDtype)
|
|
48
81
|
arrayDimensions = numpy.array(mapShape, dtype=numpyDtype)
|
|
@@ -69,11 +102,24 @@ def makeConnectionGraph(mapShape: tuple[int, ...], leavesTotal: int, datatype: t
|
|
|
69
102
|
return connectionGraph
|
|
70
103
|
|
|
71
104
|
def makeDataContainer(shape: int | tuple[int, ...], datatype: type[numpy.signedinteger[Any]] | None = None) -> numpy.ndarray[Any, numpy.dtype[numpy.signedinteger[Any]]]:
|
|
72
|
-
numpyDtype =
|
|
105
|
+
numpyDtype = interpretParameter_datatype(datatype)
|
|
73
106
|
return numpy.zeros(shape, dtype=numpyDtype)
|
|
74
107
|
|
|
108
|
+
def outfitCountFolds(mapShape: tuple[int, ...], computationDivisions: int | str | None = None, concurrencyLimit: int = 1) -> ComputationState:
|
|
109
|
+
leavesTotal = getLeavesTotal(mapShape)
|
|
110
|
+
taskDivisions = getTaskDivisions(computationDivisions, concurrencyLimit, leavesTotal)
|
|
111
|
+
computationStateInitialized = ComputationState(mapShape, leavesTotal, taskDivisions, concurrencyLimit)
|
|
112
|
+
return computationStateInitialized
|
|
113
|
+
|
|
75
114
|
def setCPUlimit(CPUlimit: Any | None) -> int:
|
|
76
|
-
"""Sets CPU limit for
|
|
115
|
+
"""Sets CPU limit for concurrent operations.
|
|
116
|
+
|
|
117
|
+
If the concurrency is managed by `numba`, the maximum number of CPUs is retrieved from `numba.get_num_threads()` and not by polling the hardware. Therefore, if there are
|
|
118
|
+
numba environment variables limiting the number of available CPUs, that will effect this function. That _should_ be a good thing: you control the number of CPUs available
|
|
119
|
+
to numba. But if you're not aware of that, you might be surprised by the results.
|
|
120
|
+
|
|
121
|
+
If you are designing custom modules that use numba, note that you must call `numba.set_num_threads()` (i.e., this function) before executing an `import` statement
|
|
122
|
+
on a Numba-jitted function. Otherwise, the `numba.set_num_threads()` call will have no effect on the imported function.
|
|
77
123
|
|
|
78
124
|
Parameters:
|
|
79
125
|
CPUlimit: whether and how to limit the CPU usage. See notes for details.
|
|
@@ -93,72 +139,29 @@ def setCPUlimit(CPUlimit: Any | None) -> int:
|
|
|
93
139
|
if not (CPUlimit is None or isinstance(CPUlimit, (bool, int, float))):
|
|
94
140
|
CPUlimit = oopsieKwargsie(CPUlimit)
|
|
95
141
|
|
|
96
|
-
concurrencyLimit: int = int(defineConcurrencyLimit(CPUlimit))
|
|
97
142
|
from mapFolding.theSSOT import concurrencyPackage
|
|
98
143
|
if concurrencyPackage == 'numba':
|
|
99
144
|
from numba import get_num_threads, set_num_threads
|
|
145
|
+
concurrencyLimit: int = defineConcurrencyLimit(CPUlimit, get_num_threads())
|
|
100
146
|
set_num_threads(concurrencyLimit)
|
|
101
147
|
concurrencyLimit = get_num_threads()
|
|
102
|
-
elif concurrencyPackage == '
|
|
103
|
-
|
|
148
|
+
elif concurrencyPackage == 'multiprocessing':
|
|
149
|
+
# When to use multiprocessing.set_start_method https://github.com/hunterhogan/mapFolding/issues/6
|
|
150
|
+
concurrencyLimit = defineConcurrencyLimit(CPUlimit)
|
|
104
151
|
else:
|
|
105
|
-
raise NotImplementedError("
|
|
106
|
-
|
|
152
|
+
raise NotImplementedError(f"I received {concurrencyPackage=} but I don't know what to do with that.")
|
|
107
153
|
return concurrencyLimit
|
|
108
154
|
|
|
109
|
-
def
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
Maximum number of concurrent tasks allowed.
|
|
123
|
-
CPUlimit
|
|
124
|
-
for error reporting.
|
|
125
|
-
listDimensions
|
|
126
|
-
for error reporting.
|
|
127
|
-
|
|
128
|
-
Returns
|
|
129
|
-
-------
|
|
130
|
-
taskDivisions
|
|
131
|
-
How many tasks must finish before the job can compute the total number of folds; `0` means no tasks, only job.
|
|
132
|
-
|
|
133
|
-
Raises
|
|
134
|
-
------
|
|
135
|
-
ValueError
|
|
136
|
-
If computationDivisions is an unsupported type or if resulting task divisions exceed total leaves.
|
|
137
|
-
|
|
138
|
-
Notes
|
|
139
|
-
-----
|
|
140
|
-
Task divisions should not exceed total leaves or the folds will be over-counted.
|
|
141
|
-
"""
|
|
142
|
-
taskDivisions = 0
|
|
143
|
-
if not computationDivisions:
|
|
144
|
-
pass
|
|
145
|
-
elif isinstance(computationDivisions, int):
|
|
146
|
-
taskDivisions = computationDivisions
|
|
147
|
-
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.
|
|
148
|
-
computationDivisions = computationDivisions.lower()
|
|
149
|
-
if computationDivisions == 'maximum':
|
|
150
|
-
taskDivisions = leavesTotal
|
|
151
|
-
elif computationDivisions == 'cpu':
|
|
152
|
-
taskDivisions = min(concurrencyLimit, leavesTotal)
|
|
153
|
-
else:
|
|
154
|
-
raise ValueError(f"I received {computationDivisions} for the parameter, `computationDivisions`, but the so-called programmer didn't implement code for that.")
|
|
155
|
-
|
|
156
|
-
if taskDivisions > leavesTotal:
|
|
157
|
-
raise ValueError(f"Problem: `taskDivisions`, ({taskDivisions}), is greater than `leavesTotal`, ({leavesTotal}), which will cause duplicate counting of the folds.\n\nChallenge: you cannot directly set `taskDivisions` or `leavesTotal`. They are derived from parameters that may or may not still be named `computationDivisions`, `CPUlimit` , and `listDimensions` and from dubious-quality Python code.")
|
|
158
|
-
return int(max(0, taskDivisions))
|
|
159
|
-
|
|
160
|
-
def outfitCountFolds(mapShape: tuple[int, ...], computationDivisions: int | str | None = None, concurrencyLimit: int = 1) -> ComputationState:
|
|
161
|
-
leavesTotal = getLeavesTotal(mapShape)
|
|
162
|
-
taskDivisions = getTaskDivisions(computationDivisions, concurrencyLimit, leavesTotal)
|
|
163
|
-
computationStateInitialized = ComputationState(mapShape, leavesTotal, taskDivisions)
|
|
164
|
-
return computationStateInitialized
|
|
155
|
+
def validateListDimensions(listDimensions: Sequence[int]) -> tuple[int, ...]:
|
|
156
|
+
if not listDimensions:
|
|
157
|
+
raise ValueError("listDimensions is a required parameter.")
|
|
158
|
+
listValidated: list[int] = intInnit(listDimensions, 'listDimensions')
|
|
159
|
+
listNonNegative: list[int] = []
|
|
160
|
+
for dimension in listValidated:
|
|
161
|
+
if dimension < 0:
|
|
162
|
+
raise ValueError(f"Dimension {dimension} must be non-negative")
|
|
163
|
+
listNonNegative.append(dimension)
|
|
164
|
+
dimensionsValid = [dimension for dimension in listNonNegative if dimension > 0]
|
|
165
|
+
if len(dimensionsValid) < 2:
|
|
166
|
+
raise NotImplementedError(f"This function requires listDimensions, {listDimensions}, to have at least two dimensions greater than 0. You may want to look at https://oeis.org/.")
|
|
167
|
+
return tuple(sorted(dimensionsValid))
|
|
@@ -1,35 +1,8 @@
|
|
|
1
1
|
"""Filesystem functions for mapFolding package."""
|
|
2
|
-
from pathlib import Path
|
|
2
|
+
from pathlib import Path, PurePath
|
|
3
|
+
from typing import Any
|
|
3
4
|
import os
|
|
4
5
|
|
|
5
|
-
def saveFoldsTotal(pathFilename: str | os.PathLike[str], foldsTotal: int) -> None:
|
|
6
|
-
"""
|
|
7
|
-
Save foldsTotal with multiple fallback mechanisms.
|
|
8
|
-
|
|
9
|
-
Parameters:
|
|
10
|
-
pathFilename: Target save location
|
|
11
|
-
foldsTotal: Critical computed value to save
|
|
12
|
-
"""
|
|
13
|
-
try:
|
|
14
|
-
pathFilenameFoldsTotal = Path(pathFilename)
|
|
15
|
-
pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
|
|
16
|
-
pathFilenameFoldsTotal.write_text(str(foldsTotal))
|
|
17
|
-
except Exception as ERRORmessage:
|
|
18
|
-
try:
|
|
19
|
-
print(f"\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n\n{foldsTotal=}\n\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n")
|
|
20
|
-
print(ERRORmessage)
|
|
21
|
-
print(f"\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n\n{foldsTotal=}\n\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n")
|
|
22
|
-
randomnessPlanB = (int(str(foldsTotal).strip()[-1]) + 1) * ['YO_']
|
|
23
|
-
filenameInfixUnique = ''.join(randomnessPlanB)
|
|
24
|
-
pathFilenamePlanB = os.path.join(os.getcwd(), 'foldsTotal' + filenameInfixUnique + '.txt')
|
|
25
|
-
writeStreamFallback = open(pathFilenamePlanB, 'w')
|
|
26
|
-
writeStreamFallback.write(str(foldsTotal))
|
|
27
|
-
writeStreamFallback.close()
|
|
28
|
-
print(str(pathFilenamePlanB))
|
|
29
|
-
except Exception:
|
|
30
|
-
print(foldsTotal)
|
|
31
|
-
return None
|
|
32
|
-
|
|
33
6
|
def getFilenameFoldsTotal(mapShape: tuple[int, ...]) -> str:
|
|
34
7
|
"""Imagine your computer has been counting folds for 9 days, and when it tries to save your newly discovered value,
|
|
35
8
|
the filename is invalid. I bet you think this function is more important after that thought experiment.
|
|
@@ -85,3 +58,38 @@ def getPathFilenameFoldsTotal(mapShape: tuple[int, ...], pathLikeWriteFoldsTotal
|
|
|
85
58
|
|
|
86
59
|
pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
|
|
87
60
|
return pathFilenameFoldsTotal
|
|
61
|
+
|
|
62
|
+
def saveFoldsTotal(pathFilename: str | os.PathLike[str], foldsTotal: int) -> None:
|
|
63
|
+
"""
|
|
64
|
+
Save foldsTotal with multiple fallback mechanisms.
|
|
65
|
+
|
|
66
|
+
Parameters:
|
|
67
|
+
pathFilename: Target save location
|
|
68
|
+
foldsTotal: Critical computed value to save
|
|
69
|
+
"""
|
|
70
|
+
try:
|
|
71
|
+
pathFilenameFoldsTotal = Path(pathFilename)
|
|
72
|
+
pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
|
|
73
|
+
pathFilenameFoldsTotal.write_text(str(foldsTotal))
|
|
74
|
+
except Exception as ERRORmessage:
|
|
75
|
+
try:
|
|
76
|
+
print(f"\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n\n{foldsTotal=}\n\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n")
|
|
77
|
+
print(ERRORmessage)
|
|
78
|
+
print(f"\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n\n{foldsTotal=}\n\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n")
|
|
79
|
+
randomnessPlanB = (int(str(foldsTotal).strip()[-1]) + 1) * ['YO_']
|
|
80
|
+
filenameInfixUnique = ''.join(randomnessPlanB)
|
|
81
|
+
pathFilenamePlanB = os.path.join(os.getcwd(), 'foldsTotal' + filenameInfixUnique + '.txt')
|
|
82
|
+
writeStreamFallback = open(pathFilenamePlanB, 'w')
|
|
83
|
+
writeStreamFallback.write(str(foldsTotal))
|
|
84
|
+
writeStreamFallback.close()
|
|
85
|
+
print(str(pathFilenamePlanB))
|
|
86
|
+
except Exception:
|
|
87
|
+
print(foldsTotal)
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
def writeStringToHere(this: str, pathFilename: str | os.PathLike[Any] | PurePath) -> None:
|
|
91
|
+
"""Write the string `this` to the file at `pathFilename`."""
|
|
92
|
+
pathFilename = Path(pathFilename)
|
|
93
|
+
pathFilename.parent.mkdir(parents=True, exist_ok=True)
|
|
94
|
+
pathFilename.write_text(str(this))
|
|
95
|
+
return None
|
|
@@ -11,8 +11,8 @@ def makeDictionaryFoldsTotalKnown() -> dict[tuple[int, ...], int]:
|
|
|
11
11
|
|
|
12
12
|
for n, foldingsTotal in sequence.items():
|
|
13
13
|
mapShape = settings['getMapShape'](n)
|
|
14
|
-
mapShape = sorted(mapShape)
|
|
15
|
-
dictionaryMapDimensionsToFoldsTotalKnown[
|
|
14
|
+
mapShape = tuple(sorted(mapShape))
|
|
15
|
+
dictionaryMapDimensionsToFoldsTotalKnown[mapShape] = foldingsTotal
|
|
16
16
|
return dictionaryMapDimensionsToFoldsTotalKnown
|
|
17
17
|
|
|
18
18
|
def getFoldsTotalKnown(mapShape: tuple[int, ...]) -> int:
|
|
@@ -144,10 +144,10 @@ def _parseBFileOEIS(OEISbFile: str, oeisID: str) -> dict[int, int]:
|
|
|
144
144
|
return OEISsequence
|
|
145
145
|
|
|
146
146
|
def getOEISofficial(pathFilenameCache: pathlib.Path, url: str) -> None | str:
|
|
147
|
-
tryCache = False
|
|
147
|
+
tryCache: bool = False
|
|
148
148
|
if pathFilenameCache.exists():
|
|
149
149
|
fileAge: timedelta = datetime.now() - datetime.fromtimestamp(pathFilenameCache.stat().st_mtime)
|
|
150
|
-
tryCache
|
|
150
|
+
tryCache = fileAge < timedelta(days=cacheDays)
|
|
151
151
|
|
|
152
152
|
oeisInformation: str | None = None
|
|
153
153
|
if tryCache:
|