mapFolding 0.15.2__py3-none-any.whl → 0.15.4__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 +15 -11
- mapFolding/_theSSOT.py +56 -63
- mapFolding/_theTypes.py +67 -5
- mapFolding/algorithms/__init__.py +1 -0
- mapFolding/algorithms/matrixMeanders.py +348 -0
- mapFolding/algorithms/oeisIDbyFormula.py +113 -0
- mapFolding/basecamp.py +105 -67
- mapFolding/oeis.py +40 -54
- mapFolding/reference/meandersDumpingGround/matrixMeanders64retired.py +160 -0
- mapFolding/{_oeisFormulas/matrixMeanders.py → reference/meandersDumpingGround/matrixMeandersBaselineV2.py} +28 -21
- mapFolding/someAssemblyRequired/A007822rawMaterials.py +1 -1
- mapFolding/someAssemblyRequired/makeAllModules.py +5 -5
- mapFolding/someAssemblyRequired/makeJobTheorem2codon.py +5 -4
- mapFolding/syntheticModules/algorithmA007822.py +1 -1
- mapFolding/syntheticModules/algorithmA007822Numba.py +5 -3
- mapFolding/syntheticModules/dataPacking.py +2 -4
- mapFolding/syntheticModules/dataPackingA007822.py +2 -4
- mapFolding/syntheticModules/initializeStateA007822.py +1 -1
- mapFolding/syntheticModules/theorem2A007822.py +1 -1
- mapFolding/syntheticModules/theorem2A007822Numba.py +1 -1
- mapFolding/syntheticModules/theorem2A007822Trimmed.py +1 -1
- mapFolding/tests/conftest.py +30 -10
- mapFolding/tests/test_computations.py +75 -46
- mapFolding/tests/test_oeis.py +2 -20
- {mapfolding-0.15.2.dist-info → mapfolding-0.15.4.dist-info}/METADATA +2 -1
- mapfolding-0.15.4.dist-info/RECORD +78 -0
- {mapfolding-0.15.2.dist-info → mapfolding-0.15.4.dist-info}/entry_points.txt +0 -1
- mapFolding/_oeisFormulas/A000136.py +0 -4
- mapFolding/_oeisFormulas/A000560.py +0 -4
- mapFolding/_oeisFormulas/A000682.py +0 -17
- mapFolding/_oeisFormulas/A001010.py +0 -19
- mapFolding/_oeisFormulas/A001011.py +0 -5
- mapFolding/_oeisFormulas/A005315.py +0 -4
- mapFolding/_oeisFormulas/A005316.py +0 -10
- mapFolding/_oeisFormulas/A223094.py +0 -7
- mapFolding/_oeisFormulas/A259702.py +0 -4
- mapFolding/_oeisFormulas/A301620.py +0 -6
- mapFolding/_oeisFormulas/Z0Z_aOFn.py +0 -33
- mapFolding/_oeisFormulas/Z0Z_oeisMeanders.py +0 -52
- mapFolding/_oeisFormulas/__init__.py +0 -1
- mapFolding/_oeisFormulas/matrixMeandersAnnex.py +0 -84
- mapfolding-0.15.2.dist-info/RECORD +0 -88
- /mapFolding/{daoOfMapFolding.py → algorithms/daoOfMapFolding.py} +0 -0
- /mapFolding/reference/{A005316JavaPort.py → meandersDumpingGround/A005316JavaPort.py} +0 -0
- /mapFolding/reference/{A005316imperative.py → meandersDumpingGround/A005316imperative.py} +0 -0
- /mapFolding/reference/{A005316intOptimized.py → meandersDumpingGround/A005316intOptimized.py} +0 -0
- /mapFolding/reference/{A005316optimized128bit.py → meandersDumpingGround/A005316optimized128bit.py} +0 -0
- /mapFolding/reference/{A005316primitiveOptimized.py → meandersDumpingGround/A005316primitiveOptimized.py} +0 -0
- /mapFolding/reference/{A005316redis.py → meandersDumpingGround/A005316redis.py} +0 -0
- /mapFolding/reference/{A005316write2disk.py → meandersDumpingGround/A005316write2disk.py} +0 -0
- /mapFolding/reference/{matrixMeandersBaseline.py → meandersDumpingGround/matrixMeandersBaseline.py} +0 -0
- /mapFolding/reference/{matrixMeandersBaselineAnnex.py → meandersDumpingGround/matrixMeandersBaselineAnnex.py} +0 -0
- /mapFolding/reference/{matrixMeandersSimpleQueue.py → meandersDumpingGround/matrixMeandersSimpleQueue.py} +0 -0
- /mapFolding/reference/{matrixMeandersSlicePop.py → meandersDumpingGround/matrixMeandersSlicePop.py} +0 -0
- {mapfolding-0.15.2.dist-info → mapfolding-0.15.4.dist-info}/WHEEL +0 -0
- {mapfolding-0.15.2.dist-info → mapfolding-0.15.4.dist-info}/licenses/LICENSE +0 -0
- {mapfolding-0.15.2.dist-info → mapfolding-0.15.4.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from functools import cache
|
|
2
|
+
from mapFolding import countFolds
|
|
3
|
+
from mapFolding.algorithms.matrixMeanders import doTheNeedful
|
|
4
|
+
|
|
5
|
+
@cache
|
|
6
|
+
def A000136(n: int) -> int:
|
|
7
|
+
return n * A000682(n)
|
|
8
|
+
|
|
9
|
+
def A000560(n: int) -> int:
|
|
10
|
+
return A000682(n + 1) // 2
|
|
11
|
+
|
|
12
|
+
def A000682getCurveLocations(n: int) -> dict[int, int]:
|
|
13
|
+
curveLocationsMAXIMUM: int = 1 << (2 * n + 4)
|
|
14
|
+
|
|
15
|
+
curveStart: int = 5 - (n & 0b1) * 4
|
|
16
|
+
listCurveLocations: list[int] = [(curveStart << 1) | curveStart]
|
|
17
|
+
|
|
18
|
+
while listCurveLocations[-1] < curveLocationsMAXIMUM:
|
|
19
|
+
curveStart = (curveStart << 4) | 0b101
|
|
20
|
+
listCurveLocations.append((curveStart << 1) | curveStart)
|
|
21
|
+
|
|
22
|
+
return dict.fromkeys(listCurveLocations, 1)
|
|
23
|
+
|
|
24
|
+
@cache
|
|
25
|
+
def A000682(n: int) -> int:
|
|
26
|
+
return doTheNeedful(n - 1, A000682getCurveLocations(n - 1))
|
|
27
|
+
|
|
28
|
+
def A001010(n: int) -> int:
|
|
29
|
+
"""Formulas.
|
|
30
|
+
|
|
31
|
+
a(2n-1) = 2*A007822(n)
|
|
32
|
+
OddQ[n], 2*A007822[[(n - 1)/2 + 1]]]
|
|
33
|
+
|
|
34
|
+
a(2n) = 2*A000682(n+1)
|
|
35
|
+
EvenQ[n], 2*A000682[[n/2 + 1]]
|
|
36
|
+
"""
|
|
37
|
+
if n == 1:
|
|
38
|
+
foldsTotal = 1
|
|
39
|
+
elif n & 0b1:
|
|
40
|
+
foldsTotal = 2 * countFolds(oeisID='A007822', oeis_n=(n - 1)//2 + 1, flow='theorem2Numba')
|
|
41
|
+
else:
|
|
42
|
+
foldsTotal = 2 * A000682(n // 2 + 1)
|
|
43
|
+
return foldsTotal
|
|
44
|
+
|
|
45
|
+
def A001011(n: int) -> int:
|
|
46
|
+
if n == 0:
|
|
47
|
+
foldsTotal = 1
|
|
48
|
+
else:
|
|
49
|
+
foldsTotal = (A001010(n) + A000136(n)) // 4
|
|
50
|
+
return foldsTotal
|
|
51
|
+
|
|
52
|
+
def A005316getCurveLocations(n: int) -> dict[int, int]:
|
|
53
|
+
if n & 0b1:
|
|
54
|
+
return {22: 1}
|
|
55
|
+
else:
|
|
56
|
+
return {15: 1}
|
|
57
|
+
|
|
58
|
+
@cache
|
|
59
|
+
def A005316(n: int) -> int:
|
|
60
|
+
return doTheNeedful(n-1, A005316getCurveLocations(n-1))
|
|
61
|
+
|
|
62
|
+
@cache
|
|
63
|
+
def A005315(n: int) -> int:
|
|
64
|
+
if n == 1:
|
|
65
|
+
foldsTotal = 1
|
|
66
|
+
else:
|
|
67
|
+
foldsTotal = A005316(2 * n - 1)
|
|
68
|
+
return foldsTotal
|
|
69
|
+
|
|
70
|
+
def A060206(n: int) -> int:
|
|
71
|
+
return A000682(2 * n + 1)
|
|
72
|
+
|
|
73
|
+
def A077460(n: int) -> int:
|
|
74
|
+
"""Formulas.
|
|
75
|
+
|
|
76
|
+
a[0] = a[1] = 1;
|
|
77
|
+
a[n_] := If[OddQ[n], (A005316[[n + 1]] + A005316[[2n]] + A000682[[n]])/4
|
|
78
|
+
a(2n+1) = (A005315(2n+1) + A005316(2n+1) + A060206(n)) / 4.
|
|
79
|
+
|
|
80
|
+
a(2n) = (A005315(2n) + 2 * A005316(2n)) / 4.
|
|
81
|
+
(A005316[[2n]] + 2 A005316[[n + 1]])/4];
|
|
82
|
+
|
|
83
|
+
"""
|
|
84
|
+
if n in {0, 1}:
|
|
85
|
+
foldsTotal = 1
|
|
86
|
+
elif n & 0b1:
|
|
87
|
+
foldsTotal = (A005315(n) + A005316(n) + A060206((n - 1) // 2)) // 4
|
|
88
|
+
else:
|
|
89
|
+
foldsTotal = (A005315(n) + 2 * A005316(n)) // 4
|
|
90
|
+
|
|
91
|
+
return foldsTotal
|
|
92
|
+
|
|
93
|
+
def A078591(n: int) -> int:
|
|
94
|
+
return A005315(n) // 2
|
|
95
|
+
|
|
96
|
+
def A178961(n: int) -> int:
|
|
97
|
+
from mapFolding.oeis import dictionaryOEISMeanders # noqa: PLC0415
|
|
98
|
+
A001010valuesKnown: dict[int, int] = dictionaryOEISMeanders['A001010']['valuesKnown']
|
|
99
|
+
foldsTotal: int = 0
|
|
100
|
+
for n下i in range(1, n+1):
|
|
101
|
+
foldsTotal += A001010valuesKnown[n下i]
|
|
102
|
+
return foldsTotal
|
|
103
|
+
|
|
104
|
+
def A223094(n: int) -> int:
|
|
105
|
+
return A000136(n) - A000682(n + 1)
|
|
106
|
+
# TODO A223094 For n >= 3: a(n) = n! - Sum_{k=3..n-1} (a(k)*n!/k!) - A000682(n+1). - _Roger Ford_, Aug 24 2024
|
|
107
|
+
|
|
108
|
+
def A259702(n: int) -> int:
|
|
109
|
+
return A000682(n) // 2 - A000682(n - 1)
|
|
110
|
+
|
|
111
|
+
def A301620(n: int) -> int:
|
|
112
|
+
return A000682(n + 2) - 2 * A000682(n + 1)
|
|
113
|
+
# TODO A301620 a(n) = Sum_{k=3..floor((n+3)/2)} (A259689(n+1,k)*(k-2)). - _Roger Ford_, Dec 10 2018
|
mapFolding/basecamp.py
CHANGED
|
@@ -1,27 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Unified interface for map folding computation orchestration.
|
|
3
|
-
|
|
4
|
-
(AI generated docstring)
|
|
5
|
-
|
|
6
|
-
This module represents the culmination of the computational ecosystem, providing
|
|
7
|
-
the primary entry point where users interact with the complete map folding analysis
|
|
8
|
-
system. It orchestrates all preceding layers: the configuration foundation,
|
|
9
|
-
type system, core utilities, state management, and persistent storage to deliver
|
|
10
|
-
a seamless computational experience.
|
|
11
|
-
|
|
12
|
-
The interface handles multiple computation flows including sequential algorithms,
|
|
13
|
-
experimental task division strategies, and various mathematical theorem implementations.
|
|
14
|
-
It provides flexible parameter validation, computation method selection, task
|
|
15
|
-
division management, processor utilization control, and automatic result persistence.
|
|
16
|
-
Integration with OEIS sequences enables research validation and mathematical
|
|
17
|
-
verification of computed results.
|
|
18
|
-
|
|
19
|
-
Through this unified interface, researchers and practitioners can access the full
|
|
20
|
-
power of Lunnon's algorithm implementation while the underlying computational
|
|
21
|
-
complexity remains elegantly abstracted. The interface ensures that whether
|
|
22
|
-
solving simple 2D problems or complex multi-dimensional challenges, users receive
|
|
23
|
-
consistent, reliable, and efficiently computed folding pattern counts.
|
|
24
|
-
"""
|
|
1
|
+
"""Unified interface for map folding computation."""
|
|
25
2
|
|
|
26
3
|
from collections.abc import Sequence
|
|
27
4
|
from mapFolding import (
|
|
@@ -31,46 +8,118 @@ from os import PathLike
|
|
|
31
8
|
from pathlib import PurePath
|
|
32
9
|
import contextlib
|
|
33
10
|
|
|
11
|
+
"""TODO new flow paradigm, incomplete
|
|
12
|
+
|
|
13
|
+
algorithms directory
|
|
14
|
+
manually coded algorithms or formulas
|
|
15
|
+
countFolds will be a stable interface for multidimensional map folding, including synthetic modules
|
|
16
|
+
This has special treatment because people may want to call mapShape not defined in OEIS
|
|
17
|
+
countMeanders will be a stable interface for meanders
|
|
18
|
+
This has special treatment because people may want to call meanders not defined in OEIS
|
|
19
|
+
an enhanced version of oeisIDfor_n will be a stable interface for calling by ID and n
|
|
20
|
+
|
|
21
|
+
General flow structure
|
|
22
|
+
doTheNeedful
|
|
23
|
+
specific to that version of that algorithm
|
|
24
|
+
abstracts the API for that algorithm, so that algorithm (such as multidimensional map folding) has a stable interface
|
|
25
|
+
The last place to do defensive programming
|
|
26
|
+
|
|
27
|
+
- Incomplete: how to count
|
|
28
|
+
- currently in parameters computationDivisions, CPUlimit, and flow
|
|
29
|
+
|
|
30
|
+
- Flow in count______
|
|
31
|
+
- DEFENSIVE PROGRAMMING
|
|
32
|
+
- FAIL EARLY
|
|
33
|
+
- Implement "common foundational logic".
|
|
34
|
+
- IDK what the correct technical term is, but I'm sure other people have researched excellent ways to do this.
|
|
35
|
+
- Example: in `countFolds`, every possible flow path needs `mapShape`. Therefore, `mapShape` is foundational logic that
|
|
36
|
+
all flow paths have in common: "common foundational logic".
|
|
37
|
+
- Example: in `countFolds`, some flow paths have more than one "task division" (i.e., the computation is divided into
|
|
38
|
+
multiple tasks), while other flow paths only have one task division. One reasonable perspective is that computing task
|
|
39
|
+
divisions is NOT "common foundational logic". My perspective for this example: to compute whether or not there are
|
|
40
|
+
task divisions and if so, how many task divisions is identical for all flow paths. Therefore, I handle computing task
|
|
41
|
+
divisions as "common foundational logic".
|
|
42
|
+
- Incomplete
|
|
43
|
+
- Initialize memorialization instructions, if asked
|
|
44
|
+
- MORE DEFENSIVE PROGRAMMING
|
|
45
|
+
- FAIL EARLIER THAN EARLY
|
|
46
|
+
- Incomplete
|
|
47
|
+
- DEFENSIVE PROGRAMMING ON BEHALF of downstream modules and functions
|
|
48
|
+
- FAIL SO EARLY IT IS BEFORE THE USER INSTALLS THE APP
|
|
49
|
+
- Incomplete
|
|
50
|
+
- REPEAT MANY OR ALL OF THE DEFENSIVE PROGRAMMING YOU HAVE ALREADY DONE
|
|
51
|
+
|
|
52
|
+
- Incomplete
|
|
53
|
+
- Pass control to the correct `doTheNeedful`
|
|
54
|
+
- I don't know how to "elegantly" pass control without putting `doTheNeedful` over `count______` in the stack, therefore,
|
|
55
|
+
control will come back here.
|
|
56
|
+
- DO NOT, for the love of puppies and cookies, DO NOT use defensive programming here. Defensive programming AFTER a
|
|
57
|
+
four-week-long computation is a tacit admission of incompetent programming.
|
|
58
|
+
- Follow memorialization instructions: which means pass control to a function will tenaciously follow the instructions.
|
|
59
|
+
- return "a(n)" (as OEIS calls it), such as foldsTotal
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
|
|
34
63
|
def countFolds(listDimensions: Sequence[int] | None = None
|
|
35
64
|
, pathLikeWriteFoldsTotal: PathLike[str] | PurePath | None = None
|
|
36
65
|
, computationDivisions: int | str | None = None
|
|
37
66
|
# , * # TODO improve `standardizedEqualToCallableReturn` so it will work with keyword arguments
|
|
38
|
-
, CPUlimit:
|
|
67
|
+
, CPUlimit: bool | float | int | None = None # noqa: FBT001
|
|
39
68
|
, mapShape: tuple[int, ...] | None = None
|
|
40
69
|
, oeisID: str | None = None
|
|
41
70
|
, oeis_n: int | None = None
|
|
42
71
|
, flow: str | None = None
|
|
43
72
|
) -> int:
|
|
44
73
|
"""
|
|
45
|
-
Count the total number of
|
|
74
|
+
Count the total number of distinct ways to fold a map.
|
|
46
75
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
This function serves as the main public interface to the map folding algorithm, handling all parameter validation,
|
|
50
|
-
computation state management, and result persistence in a user-friendly way.
|
|
76
|
+
Mathematicians also describe this as folding a strip of stamps, and they usually call the total "number of distinct ways to
|
|
77
|
+
fold" a map the map's "foldings."
|
|
51
78
|
|
|
52
79
|
Parameters
|
|
53
80
|
----------
|
|
54
|
-
listDimensions
|
|
81
|
+
listDimensions : Sequence[int] | None = None
|
|
55
82
|
List of integers representing the dimensions of the map to be folded.
|
|
56
|
-
pathLikeWriteFoldsTotal: None
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
83
|
+
pathLikeWriteFoldsTotal : PathLike[str] | PurePath | None = None
|
|
84
|
+
A filename, a path of only directories, or a path with directories and a filename to which `countFolds` will write the
|
|
85
|
+
value of `foldsTotal`. If `pathLikeWriteFoldsTotal` is a path of only directories, `countFolds` creates a filename based
|
|
86
|
+
on the map dimensions.
|
|
87
|
+
computationDivisions : int | str | None = None
|
|
60
88
|
Whether and how to divide the computational work.
|
|
61
|
-
- `None`: no division of the computation into tasks
|
|
62
|
-
- int
|
|
63
|
-
|
|
64
|
-
-
|
|
65
|
-
|
|
66
|
-
|
|
89
|
+
- `None`: no division of the computation into tasks.
|
|
90
|
+
- `int`: into how many tasks `countFolds` will divide the computation. The values 0 or 1 are identical to `None`. It is
|
|
91
|
+
mathematically impossible to divide the computation into more tasks than the map's total leaves.
|
|
92
|
+
- 'maximum': divides the computation into `leavesTotal`-many tasks.
|
|
93
|
+
- 'cpu': divides the computation into the number of available CPUs.
|
|
94
|
+
CPUlimit : bool | float | int | None = None
|
|
95
|
+
If relevant, whether and how to limit the number of processors `countFolds` will use. `CPUlimit` is an irrelevant setting
|
|
96
|
+
unless the computation is divided into more than one task with the `computationDivisions` parameter.
|
|
67
97
|
- `False`, `None`, or `0`: No limits on processor usage; uses all available processors. All other values will
|
|
68
98
|
potentially limit processor usage.
|
|
69
99
|
- `True`: Yes, limit the processor usage; limits to 1 processor.
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
-
|
|
73
|
-
-
|
|
100
|
+
- `int >= 1`: The maximum number of available processors to use.
|
|
101
|
+
- `0 < float < 1`: The maximum number of processors to use expressed as a fraction of available processors.
|
|
102
|
+
- `-1 < float < 0`: The number of processors to *not* use expressed as a fraction of available processors.
|
|
103
|
+
- `int <= -1`: The number of available processors to *not* use.
|
|
104
|
+
- If the value of `CPUlimit` is a `float` greater than 1 or less than -1, `countFolds` truncates the value to an `int`
|
|
105
|
+
with the same sign as the `float`.
|
|
106
|
+
mapShape : tuple[int, ...] | None = None
|
|
107
|
+
Tuple of integers representing the dimensions of the map to be folded. Mathematicians almost always use the term
|
|
108
|
+
"dimensions", such as in the seminal paper, "Multi-dimensional map-folding". Nevertheless, in contemporary Python
|
|
109
|
+
programming, in the context of these algorithms, the term "shape" makes it much easier to align the mathematics with the
|
|
110
|
+
syntax of the programming language.
|
|
111
|
+
oeisID : str | None = None
|
|
112
|
+
The On-Line Encyclopedia of Integer Sequences (OEIS) ID for which to compute a(n) for value of 'n' set in `oeis_n`.
|
|
113
|
+
oeis_n : int | None = None
|
|
114
|
+
The 'n' value for the `oeisID`.
|
|
115
|
+
flow : str | None = None
|
|
116
|
+
My stupid way of selecting the version of the algorithm to use in the computation. There are certainly better ways to do
|
|
117
|
+
this, but I have not yet solved this issue. As of 2025 Aug 14, these values will work:
|
|
118
|
+
- 'daoOfMapFolding'
|
|
119
|
+
- 'numba'
|
|
120
|
+
- 'theorem2'
|
|
121
|
+
- 'theorem2Numba'
|
|
122
|
+
- 'theorem2Trimmed'
|
|
74
123
|
|
|
75
124
|
Returns
|
|
76
125
|
-------
|
|
@@ -83,7 +132,7 @@ def countFolds(listDimensions: Sequence[int] | None = None
|
|
|
83
132
|
If you want to compute a large `foldsTotal`, dividing the computation into tasks is usually a bad idea. Dividing the
|
|
84
133
|
algorithm into tasks is inherently inefficient: efficient division into tasks means there would be no overlap in the
|
|
85
134
|
work performed by each task. When dividing this algorithm, the amount of overlap is between 50% and 90% by all
|
|
86
|
-
tasks: at least 50% of the work done by every task must be done by
|
|
135
|
+
tasks: at least 50% of the work done by every task must be done by each task. If you improve the computation time,
|
|
87
136
|
it will only change by -10 to -50% depending on (at the very least) the ratio of the map dimensions and the number
|
|
88
137
|
of leaves. If an undivided computation would take 10 hours on your computer, for example, the computation will still
|
|
89
138
|
take at least 5 hours but you might reduce the time to 9 hours. Most of the time, however, you will increase the
|
|
@@ -96,9 +145,9 @@ def countFolds(listDimensions: Sequence[int] | None = None
|
|
|
96
145
|
pass
|
|
97
146
|
else:
|
|
98
147
|
if oeisID and oeis_n:
|
|
99
|
-
from mapFolding.oeis import
|
|
148
|
+
from mapFolding.oeis import dictionaryOEISMapFolding # noqa: PLC0415
|
|
100
149
|
with contextlib.suppress(KeyError):
|
|
101
|
-
mapShape =
|
|
150
|
+
mapShape = dictionaryOEISMapFolding[oeisID]['getMapShape'](oeis_n)
|
|
102
151
|
if not mapShape and listDimensions:
|
|
103
152
|
mapShape = validateListDimensions(listDimensions)
|
|
104
153
|
|
|
@@ -136,27 +185,16 @@ def countFolds(listDimensions: Sequence[int] | None = None
|
|
|
136
185
|
|
|
137
186
|
# A007822 flow control until I can figure out a good way ---------------------------------
|
|
138
187
|
if oeisID == 'A007822':
|
|
139
|
-
"""
|
|
140
|
-
|
|
141
|
-
The REAL motivation for integrating into basecamp is to integrate into the test modules. No, wait: to stop having to work
|
|
142
|
-
around the test modules.
|
|
143
|
-
|
|
144
|
-
I put `if oeisID == 'A007822'` in the `elif flow ==` cascade, before the `flow` checks because I want to remove A007822
|
|
145
|
-
from those flow paths. It is fundamentally incompatible and it will cause `Exception` or incorrect computations.
|
|
188
|
+
"""To use A007822, oeisID is mandatory.
|
|
146
189
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
Parameters:
|
|
150
|
-
listDimensions should work. mapShape should work. oeis_n should work. `pathLikeWriteFoldsTotal` should work!!! I
|
|
151
|
-
didn't think about that, and I like it.
|
|
190
|
+
`if oeisID == 'A007822'` precedes the `elif flow ==` cascade because A007822 is fundamentally incompatible with those flow
|
|
191
|
+
paths and it will cause `Exception` or incorrect computations.
|
|
152
192
|
|
|
153
193
|
Parallel version:
|
|
154
|
-
idk. The computation division logic will try to execute. As of 2025 Aug
|
|
155
|
-
parallel version.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
It looks like I will need to make decisions tree just for A007822. That's probably not a big deal since all of the
|
|
159
|
-
possible routes are predictable.
|
|
194
|
+
idk. The computation division logic will try to execute. As of 2025 Aug 13 at 11 PM, I haven't tried or thought about
|
|
195
|
+
a parallel version. And I don't really care. Potential parallelism is certainly present in `filterAsymmetricFolds`.
|
|
196
|
+
But, if I want to implement that, I should almost certainly replace `filterAsymmetricFolds` with a non-blocking
|
|
197
|
+
function to which `count` can pass the necessary values to. TODO Watch out for errors.
|
|
160
198
|
|
|
161
199
|
"""
|
|
162
200
|
match flow:
|
|
@@ -210,7 +248,7 @@ def countFolds(listDimensions: Sequence[int] | None = None
|
|
|
210
248
|
from mapFolding.dataBaskets import MapFoldingState # noqa: PLC0415
|
|
211
249
|
mapFoldingState: MapFoldingState = MapFoldingState(mapShape)
|
|
212
250
|
|
|
213
|
-
from mapFolding.daoOfMapFolding import doTheNeedful # noqa: PLC0415
|
|
251
|
+
from mapFolding.algorithms.daoOfMapFolding import doTheNeedful # noqa: PLC0415
|
|
214
252
|
mapFoldingState = doTheNeedful(mapFoldingState)
|
|
215
253
|
foldsTotal = mapFoldingState.foldsTotal
|
|
216
254
|
|
mapFolding/oeis.py
CHANGED
|
@@ -24,41 +24,20 @@ redundant computation, or extending mathematical knowledge, this integration
|
|
|
24
24
|
completes the journey from configuration foundation to mathematical discovery.
|
|
25
25
|
"""
|
|
26
26
|
|
|
27
|
-
from collections.abc import Callable
|
|
28
27
|
from datetime import datetime, timedelta, UTC
|
|
29
28
|
from hunterMakesPy import writeStringToHere
|
|
30
29
|
from itertools import chain
|
|
31
|
-
from mapFolding import countFolds
|
|
32
|
-
from mapFolding._theSSOT import
|
|
30
|
+
from mapFolding import countFolds, MetadataOEISidMapFolding, MetadataOEISidMeanders, packageSettings
|
|
31
|
+
from mapFolding._theSSOT import pathCache
|
|
33
32
|
from pathlib import Path
|
|
34
|
-
from typing import Final
|
|
33
|
+
from typing import Final
|
|
35
34
|
from urllib.request import urlopen
|
|
36
35
|
import argparse
|
|
37
36
|
import sys
|
|
38
37
|
import time
|
|
39
38
|
import warnings
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
"""Settings for an implemented OEIS sequence."""
|
|
43
|
-
|
|
44
|
-
description: str
|
|
45
|
-
"""The OEIS.org description of the integer sequence."""
|
|
46
|
-
getMapShape: Callable[[int], tuple[int, ...]]
|
|
47
|
-
"""Function to convert the OEIS sequence index, 'n', to its `mapShape` tuple."""
|
|
48
|
-
offset: int
|
|
49
|
-
"""The starting index, 'n', of the sequence, typically 0 or 1."""
|
|
50
|
-
valuesBenchmark: list[int]
|
|
51
|
-
"""List of index values, 'n', to use when benchmarking the algorithm performance."""
|
|
52
|
-
valuesKnown: dict[int, int]
|
|
53
|
-
"""Dictionary of sequence indices, 'n', to their known values, `foldsTotal`."""
|
|
54
|
-
valuesTestParallelization: list[int]
|
|
55
|
-
"""List of index values, 'n', to use when testing parallelization performance."""
|
|
56
|
-
valuesTestValidation: list[int]
|
|
57
|
-
"""List of index values, 'n', to use when testing validation performance."""
|
|
58
|
-
valueUnknown: int
|
|
59
|
-
"""The smallest value of 'n' for for which `foldsTotal` is unknown."""
|
|
60
|
-
|
|
61
|
-
oeisIDsImplemented: Final[list[str]] = sorted([oeisID.upper().strip() for oeisID in settingsOEISManuallySelected])
|
|
40
|
+
oeisIDsImplemented: Final[list[str]] = sorted([oeisID.upper().strip() for oeisID in packageSettings.OEISidMapFoldingManuallySet])
|
|
62
41
|
"""Directly implemented OEIS IDs; standardized, e.g., 'A001415'."""
|
|
63
42
|
|
|
64
43
|
def _standardizeOEISid(oeisID: str) -> str:
|
|
@@ -134,13 +113,7 @@ def _parseBFileOEIS(OEISbFile: str) -> dict[int, int]:
|
|
|
134
113
|
return OEISsequence
|
|
135
114
|
|
|
136
115
|
def _getOEISofficial(pathFilenameCache: Path, url: str) -> None | str:
|
|
137
|
-
"""Retrieve OEIS
|
|
138
|
-
|
|
139
|
-
(AI generated docstring)
|
|
140
|
-
|
|
141
|
-
This function implements a caching strategy that prioritizes local cached data when it exists and
|
|
142
|
-
has not expired. Fresh data is retrieved from the OEIS website when the cache is stale or missing,
|
|
143
|
-
and the cache is updated for future use.
|
|
116
|
+
"""Retrieve OEIS ID data from oeis.org or local cache.
|
|
144
117
|
|
|
145
118
|
Parameters
|
|
146
119
|
----------
|
|
@@ -154,16 +127,11 @@ def _getOEISofficial(pathFilenameCache: Path, url: str) -> None | str:
|
|
|
154
127
|
oeisInformation : str | None
|
|
155
128
|
The retrieved OEIS sequence information as a string, or `None` if retrieval failed.
|
|
156
129
|
|
|
157
|
-
Notes
|
|
158
|
-
-----
|
|
159
|
-
Cache expiration is controlled by the module-level `cacheDays` variable. The function validates
|
|
160
|
-
URL schemes and issues warnings for failed retrievals.
|
|
161
|
-
|
|
162
130
|
"""
|
|
163
131
|
tryCache: bool = False
|
|
164
132
|
if pathFilenameCache.exists():
|
|
165
133
|
fileAge: timedelta = datetime.now(tz=UTC) - datetime.fromtimestamp(pathFilenameCache.stat().st_mtime, tz=UTC)
|
|
166
|
-
tryCache = fileAge < timedelta(days=cacheDays)
|
|
134
|
+
tryCache = fileAge < timedelta(days=packageSettings.cacheDays)
|
|
167
135
|
|
|
168
136
|
oeisInformation: str | None = None
|
|
169
137
|
if tryCache:
|
|
@@ -176,7 +144,8 @@ def _getOEISofficial(pathFilenameCache: Path, url: str) -> None | str:
|
|
|
176
144
|
if not url.startswith(("http:", "https:")):
|
|
177
145
|
message = "URL must start with 'http:' or 'https:'"
|
|
178
146
|
raise ValueError(message)
|
|
179
|
-
|
|
147
|
+
|
|
148
|
+
with urlopen(url) as response: # noqa: S310
|
|
180
149
|
oeisInformationRaw = response.read().decode('utf-8')
|
|
181
150
|
oeisInformation = str(oeisInformationRaw)
|
|
182
151
|
writeStringToHere(oeisInformation, pathFilenameCache)
|
|
@@ -279,7 +248,7 @@ def getOEISidInformation(oeisID: str) -> tuple[str, int]:
|
|
|
279
248
|
description: str = ' '.join(listDescriptionDeconstructed)
|
|
280
249
|
return description, offset
|
|
281
250
|
|
|
282
|
-
def _makeDictionaryOEIS() -> dict[str,
|
|
251
|
+
def _makeDictionaryOEIS() -> dict[str, MetadataOEISidMapFolding]:
|
|
283
252
|
"""Construct the comprehensive settings dictionary for all implemented OEIS sequences.
|
|
284
253
|
|
|
285
254
|
(AI generated docstring)
|
|
@@ -302,23 +271,23 @@ def _makeDictionaryOEIS() -> dict[str, MetadataOEISid]:
|
|
|
302
271
|
objects, containing all metadata and known values needed for computation and validation.
|
|
303
272
|
|
|
304
273
|
"""
|
|
305
|
-
dictionaryOEIS: dict[str,
|
|
274
|
+
dictionaryOEIS: dict[str, MetadataOEISidMapFolding] = {}
|
|
306
275
|
for oeisID in oeisIDsImplemented:
|
|
307
276
|
valuesKnownSherpa: dict[int, int] = getOEISidValues(oeisID)
|
|
308
277
|
descriptionSherpa, offsetSherpa = getOEISidInformation(oeisID)
|
|
309
|
-
dictionaryOEIS[oeisID] =
|
|
278
|
+
dictionaryOEIS[oeisID] = MetadataOEISidMapFolding(
|
|
310
279
|
description=descriptionSherpa,
|
|
311
280
|
offset=offsetSherpa,
|
|
312
|
-
getMapShape=
|
|
313
|
-
valuesBenchmark=
|
|
314
|
-
valuesTestParallelization=
|
|
315
|
-
valuesTestValidation=
|
|
281
|
+
getMapShape=packageSettings.OEISidMapFoldingManuallySet[oeisID]['getMapShape'],
|
|
282
|
+
valuesBenchmark=packageSettings.OEISidMapFoldingManuallySet[oeisID]['valuesBenchmark'],
|
|
283
|
+
valuesTestParallelization=packageSettings.OEISidMapFoldingManuallySet[oeisID]['valuesTestParallelization'],
|
|
284
|
+
valuesTestValidation=packageSettings.OEISidMapFoldingManuallySet[oeisID]['valuesTestValidation'] + list(range(offsetSherpa, 2)),
|
|
316
285
|
valuesKnown=valuesKnownSherpa,
|
|
317
286
|
valueUnknown=max(valuesKnownSherpa.keys(), default=0) + 1
|
|
318
287
|
)
|
|
319
288
|
return dictionaryOEIS
|
|
320
289
|
|
|
321
|
-
|
|
290
|
+
dictionaryOEISMapFolding: dict[str, MetadataOEISidMapFolding] = _makeDictionaryOEIS()
|
|
322
291
|
"""Metadata for each OEIS sequence ID."""
|
|
323
292
|
|
|
324
293
|
def makeDictionaryFoldsTotalKnown() -> dict[tuple[int, ...], int]:
|
|
@@ -332,7 +301,7 @@ def makeDictionaryFoldsTotalKnown() -> dict[tuple[int, ...], int]:
|
|
|
332
301
|
|
|
333
302
|
"""
|
|
334
303
|
return dict(chain.from_iterable(zip(map(oeisIDmetadata['getMapShape'], oeisIDmetadata['valuesKnown'].keys())
|
|
335
|
-
, oeisIDmetadata['valuesKnown'].values(), strict=True) for oeisID, oeisIDmetadata in
|
|
304
|
+
, oeisIDmetadata['valuesKnown'].values(), strict=True) for oeisID, oeisIDmetadata in dictionaryOEISMapFolding.items() if oeisID != 'A007822'))
|
|
336
305
|
|
|
337
306
|
def getFoldsTotalKnown(mapShape: tuple[int, ...]) -> int:
|
|
338
307
|
"""Retrieve the known total number of distinct folding patterns for a given map shape.
|
|
@@ -378,7 +347,7 @@ def _formatHelpText() -> str:
|
|
|
378
347
|
|
|
379
348
|
"""
|
|
380
349
|
exampleOEISid: str = oeisIDsImplemented[0]
|
|
381
|
-
exampleN: int =
|
|
350
|
+
exampleN: int = dictionaryOEISMapFolding[exampleOEISid]['valuesTestValidation'][-1]
|
|
382
351
|
|
|
383
352
|
return (
|
|
384
353
|
"\nAvailable OEIS sequences:\n"
|
|
@@ -406,7 +375,7 @@ def _formatOEISsequenceInfo() -> str:
|
|
|
406
375
|
|
|
407
376
|
"""
|
|
408
377
|
return "\n".join(
|
|
409
|
-
f" {oeisID}: {
|
|
378
|
+
f" {oeisID}: {dictionaryOEISMapFolding[oeisID]['description']}"
|
|
410
379
|
for oeisID in oeisIDsImplemented
|
|
411
380
|
)
|
|
412
381
|
|
|
@@ -441,14 +410,14 @@ def oeisIDfor_n(oeisID: str, n: int) -> int:
|
|
|
441
410
|
message = f"I received `{n = }` in the form of `{type(n) = }`, but it must be non-negative integer in the form of `{int}`."
|
|
442
411
|
raise ValueError(message)
|
|
443
412
|
|
|
444
|
-
mapShape =
|
|
413
|
+
mapShape = dictionaryOEISMapFolding[oeisID]['getMapShape'](n)
|
|
445
414
|
|
|
446
415
|
if n <= 1 or len(mapShape) < 2:
|
|
447
|
-
offset: int =
|
|
416
|
+
offset: int = dictionaryOEISMapFolding[oeisID]['offset']
|
|
448
417
|
if n < offset:
|
|
449
418
|
message = f"OEIS sequence {oeisID} is not defined at {n = }."
|
|
450
419
|
raise ArithmeticError(message)
|
|
451
|
-
foldsTotal: int =
|
|
420
|
+
foldsTotal: int = dictionaryOEISMapFolding[oeisID]['valuesKnown'][n]
|
|
452
421
|
return foldsTotal
|
|
453
422
|
|
|
454
423
|
return countFolds(mapShape, oeisID=oeisID)
|
|
@@ -513,7 +482,7 @@ def clearOEIScache() -> None:
|
|
|
513
482
|
if not pathCache.exists():
|
|
514
483
|
print(f"Cache directory, {pathCache}, not found - nothing to clear.") # noqa: T201
|
|
515
484
|
return
|
|
516
|
-
for oeisID in
|
|
485
|
+
for oeisID in dictionaryOEISMapFolding:
|
|
517
486
|
( pathCache / f"{oeisID}.txt" ).unlink(missing_ok=True)
|
|
518
487
|
( pathCache / _getFilenameOEISbFile(oeisID) ).unlink(missing_ok=True)
|
|
519
488
|
print(f"Cache cleared from {pathCache}") # noqa: T201
|
|
@@ -534,5 +503,22 @@ def getOEISids() -> None:
|
|
|
534
503
|
"""
|
|
535
504
|
print(_formatHelpText()) # noqa: T201
|
|
536
505
|
|
|
506
|
+
def _makeDictionaryOEISMeanders() -> dict[str, MetadataOEISidMeanders]:
|
|
507
|
+
dictionary: dict[str, MetadataOEISidMeanders] = {}
|
|
508
|
+
for oeisID in packageSettings.OEISidMeandersManuallySet:
|
|
509
|
+
valuesKnownSherpa: dict[int, int] = getOEISidValues(oeisID)
|
|
510
|
+
descriptionSherpa, offsetSherpa = getOEISidInformation(oeisID)
|
|
511
|
+
dictionary[oeisID] = MetadataOEISidMeanders(
|
|
512
|
+
description=descriptionSherpa,
|
|
513
|
+
offset=offsetSherpa,
|
|
514
|
+
valuesKnown=valuesKnownSherpa,
|
|
515
|
+
valuesTestValidation=packageSettings.OEISidMeandersManuallySet[oeisID]['valuesTestValidation'],
|
|
516
|
+
valueUnknown=max(valuesKnownSherpa.keys(), default=0) + 1,
|
|
517
|
+
)
|
|
518
|
+
return dictionary
|
|
519
|
+
|
|
520
|
+
dictionaryOEISMeanders: dict[str, MetadataOEISidMeanders] = _makeDictionaryOEISMeanders()
|
|
521
|
+
|
|
537
522
|
if __name__ == "__main__":
|
|
538
523
|
getOEISids()
|
|
524
|
+
|