mapFolding 0.16.4__tar.gz → 0.17.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.16.4/mapFolding.egg-info → mapfolding-0.17.0}/PKG-INFO +9 -9
- {mapfolding-0.16.4 → mapfolding-0.17.0}/easyRun/A000682.py +2 -2
- mapfolding-0.17.0/easyRun/NOTcountingFolds.py +44 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/easyRun/countFolds.py +10 -3
- {mapfolding-0.16.4 → mapfolding-0.17.0}/easyRun/meanders.py +3 -3
- mapfolding-0.17.0/mapFolding/algorithms/A000136constraintPropagation.py +95 -0
- mapfolding-0.17.0/mapFolding/algorithms/A000136elimination.py +163 -0
- mapfolding-0.17.0/mapFolding/algorithms/A000136eliminationParallel.py +77 -0
- mapfolding-0.17.0/mapFolding/algorithms/matrixMeanders.py +129 -0
- mapfolding-0.17.0/mapFolding/algorithms/matrixMeandersNumPyndas.py +841 -0
- mapfolding-0.17.0/mapFolding/algorithms/symmetricFolds.py +35 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/basecamp.py +30 -14
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/dataBaskets.py +30 -71
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/irvineJavaPort.py +3 -3
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/meandersDumpingGround/matrixMeandersNumPyV1finalForm.py +1 -1
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/A007822/_asynchronousAnnex.py +1 -1
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/A007822/makeA007822AsynchronousModules.py +5 -3
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/A007822/makeA007822Modules.py +22 -6
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/RecipeJob.py +14 -24
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/__init__.py +1 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/_toolkitContainers.py +6 -4
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/infoBooth.py +2 -1
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/makeJobTheorem2Numba.py +75 -20
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/makeJobTheorem2codon.py +9 -10
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/makingModules_count.py +20 -22
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/makingModules_doTheNeedful.py +9 -9
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/mapFoldingModules/makeMapFoldingModules.py +6 -5
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/meanders/makeMeandersModules.py +6 -6
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/toolkitMakeModules.py +3 -29
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/toolkitNumba.py +2 -1
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/transformationTools.py +2 -3
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/syntheticModules/A007822/algorithm.py +8 -8
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/syntheticModules/A007822/asynchronous.py +12 -13
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/syntheticModules/A007822/initializeState.py +10 -8
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/syntheticModules/A007822/theorem2.py +10 -8
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/syntheticModules/A007822/theorem2Numba.py +20 -16
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/syntheticModules/A007822/theorem2Trimmed.py +10 -8
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/syntheticModules/countParallelNumba.py +5 -2
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/syntheticModules/daoOfMapFoldingNumba.py +4 -2
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/syntheticModules/initializeState.py +1 -1
- mapfolding-0.17.0/mapFolding/syntheticModules/meanders/bigInt.py +89 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/syntheticModules/theorem2.py +1 -1
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/syntheticModules/theorem2Numba.py +4 -2
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/syntheticModules/theorem2Trimmed.py +1 -1
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/tests/test_computations.py +28 -2
- {mapfolding-0.16.4 → mapfolding-0.17.0/mapFolding.egg-info}/PKG-INFO +9 -9
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding.egg-info/SOURCES.txt +4 -3
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding.egg-info/requires.txt +8 -7
- {mapfolding-0.16.4 → mapfolding-0.17.0}/pyproject.toml +11 -11
- mapfolding-0.16.4/easyRun/NOTcountingFolds.py +0 -37
- mapfolding-0.16.4/mapFolding/algorithms/matrixMeanders.py +0 -88
- mapfolding-0.16.4/mapFolding/algorithms/matrixMeandersBeDry.py +0 -182
- mapfolding-0.16.4/mapFolding/algorithms/matrixMeandersNumPy.py +0 -333
- mapfolding-0.16.4/mapFolding/algorithms/matrixMeandersPandas.py +0 -334
- mapfolding-0.16.4/mapFolding/algorithms/symmetricFolds.py +0 -36
- mapfolding-0.16.4/mapFolding/syntheticModules/meanders/bigInt.py +0 -52
- {mapfolding-0.16.4 → mapfolding-0.17.0}/LICENSE +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/README.md +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/easyRun/A005316.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/easyRun/__init__.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/easyRun/generateAllModules.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/__init__.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/_theSSOT.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/_theTypes.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/algorithms/A086345.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/algorithms/__init__.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/algorithms/daoOfMapFolding.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/algorithms/oeisIDbyFormula.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/algorithms/zCuzDocStoopidoeisIDbyFormula.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/beDRY.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/filesystemToolkit.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/oeis.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/py.typed +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/A000682facts.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/A005316facts.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/A086345Wu.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/__init__.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/flattened.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/hunterNumba.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/jaxCount.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/jobsCompleted/[2x19]/p2x19.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/jobsCompleted/__init__.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/jobsCompleted/p2x19/p2x19.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/lunnonNumpy.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/lunnonWhile.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/matrixMeandersAnalysis/__init__.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/matrixMeandersAnalysis/prefixNotationNotes.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/matrixMeandersAnalysis/signatures.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/meandersDumpingGround/A005316JavaPort.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/meandersDumpingGround/A005316imperative.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/meandersDumpingGround/A005316primitiveOptimized.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/meandersDumpingGround/A005316redis.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/meandersDumpingGround/A005316write2disk.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/meandersDumpingGround/matrixMeanders64retired.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/meandersDumpingGround/matrixMeandersBaselineV2.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/meandersDumpingGround/matrixMeandersSlicePop.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/rotatedEntryPoint.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/reference/total_countPlus1vsPlusN.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/A007822/A007822rawMaterials.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/A007822/__init__.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/_toolIfThis.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/getLLVMforNoReason.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/mapFoldingModules/__init__.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/someAssemblyRequired/meanders/__init__.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/syntheticModules/A007822/__init__.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/syntheticModules/__init__.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/syntheticModules/meanders/__init__.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/tests/__init__.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/tests/conftest.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/tests/test_filesystem.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/tests/test_oeis.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/tests/test_other.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/tests/test_tasks.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/zCuzDocStoopid/__init__.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding/zCuzDocStoopid/makeDocstrings.py +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding.egg-info/dependency_links.txt +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding.egg-info/entry_points.txt +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/mapFolding.egg-info/top_level.txt +0 -0
- {mapfolding-0.16.4 → mapfolding-0.17.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mapFolding
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.17.0
|
|
4
4
|
Summary: Map folding, meanders, stamp folding, semi-meanders. Experiment with algorithm transformations, and analyze computational states.
|
|
5
5
|
Author-email: Hunter Hogan <HunterHogan@pm.me>
|
|
6
6
|
License: CC-BY-NC-4.0
|
|
@@ -31,39 +31,39 @@ Classifier: Topic :: Software Development :: Compilers
|
|
|
31
31
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
32
32
|
Classifier: Topic :: Software Development :: Pre-processors
|
|
33
33
|
Classifier: Typing :: Typed
|
|
34
|
-
Requires-Python:
|
|
34
|
+
Requires-Python: <3.14,>=3.12
|
|
35
35
|
Description-Content-Type: text/markdown
|
|
36
36
|
License-File: LICENSE
|
|
37
|
-
Requires-Dist: astToolkit
|
|
38
|
-
Requires-Dist:
|
|
39
|
-
Requires-Dist: hunterMakesPy
|
|
40
|
-
Requires-Dist: isort
|
|
37
|
+
Requires-Dist: astToolkit>=0.9.0
|
|
38
|
+
Requires-Dist: hunterMakesPy>=0.3.0
|
|
41
39
|
Requires-Dist: numpy
|
|
42
40
|
Requires-Dist: platformdirs
|
|
43
|
-
Requires-Dist: sympy
|
|
44
41
|
Provides-Extra: development
|
|
45
42
|
Requires-Dist: ipykernel; extra == "development"
|
|
46
43
|
Requires-Dist: ipywidgets; extra == "development"
|
|
47
44
|
Requires-Dist: memray; sys_platform == "linux" and extra == "development"
|
|
48
|
-
Requires-Dist: mypy; extra == "development"
|
|
49
|
-
Requires-Dist: pyupgrade; extra == "development"
|
|
50
45
|
Requires-Dist: py-spy; extra == "development"
|
|
51
46
|
Requires-Dist: setuptools-scm; extra == "development"
|
|
52
47
|
Provides-Extra: numba
|
|
53
48
|
Requires-Dist: numba; extra == "numba"
|
|
54
49
|
Requires-Dist: numba_progress; extra == "numba"
|
|
55
50
|
Provides-Extra: pandas
|
|
51
|
+
Requires-Dist: ortools; extra == "pandas"
|
|
56
52
|
Requires-Dist: pandas; extra == "pandas"
|
|
57
53
|
Requires-Dist: pyarrow; extra == "pandas"
|
|
58
54
|
Requires-Dist: pyarrow-stubs; extra == "pandas"
|
|
55
|
+
Provides-Extra: sympy
|
|
56
|
+
Requires-Dist: sympy; extra == "sympy"
|
|
59
57
|
Provides-Extra: testing
|
|
60
58
|
Requires-Dist: numba; extra == "testing"
|
|
59
|
+
Requires-Dist: ortools; extra == "testing"
|
|
61
60
|
Requires-Dist: pandas; extra == "testing"
|
|
62
61
|
Requires-Dist: pyarrow; extra == "testing"
|
|
63
62
|
Requires-Dist: pytest-cov; extra == "testing"
|
|
64
63
|
Requires-Dist: pytest-env; extra == "testing"
|
|
65
64
|
Requires-Dist: pytest-xdist; extra == "testing"
|
|
66
65
|
Requires-Dist: pytest; extra == "testing"
|
|
66
|
+
Requires-Dist: sympy; extra == "testing"
|
|
67
67
|
Dynamic: license-file
|
|
68
68
|
|
|
69
69
|
# mapFolding
|
|
@@ -10,7 +10,7 @@ if sys.version_info >= (3, 14):
|
|
|
10
10
|
def main():
|
|
11
11
|
oeisID = 'A000682'
|
|
12
12
|
n=45
|
|
13
|
-
print(NOTcountingFolds(oeisID, n, '
|
|
13
|
+
print(NOTcountingFolds(oeisID, n, 'matrixMeanders'))
|
|
14
14
|
|
|
15
15
|
from mapFolding import dictionaryOEIS
|
|
16
16
|
if n < dictionaryOEIS[oeisID]['valueUnknown']:
|
|
@@ -21,5 +21,5 @@ if __name__ == "__main__":
|
|
|
21
21
|
|
|
22
22
|
r"""
|
|
23
23
|
deactivate && C:\apps\mapFolding\.vtail\Scripts\activate.bat && title good && cls
|
|
24
|
-
title running && start "running" /B /HIGH /wait py -X faulthandler=0 -X tracemalloc=0 -X frozen_modules=on easyRun\A000682.py
|
|
24
|
+
title running && start "running" /B /HIGH /wait py -X faulthandler=0 -X tracemalloc=0 -X frozen_modules=on easyRun\A000682.py & title I'm done
|
|
25
25
|
"""
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# ruff: noqa
|
|
2
|
+
from collections import ChainMap
|
|
3
|
+
from mapFolding import dictionaryOEIS, dictionaryOEISMapFolding
|
|
4
|
+
from mapFolding.basecamp import NOTcountingFolds
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
|
|
8
|
+
dictionaryONE = ChainMap(dictionaryOEISMapFolding, dictionaryOEIS) # pyright: ignore[reportArgumentType]
|
|
9
|
+
|
|
10
|
+
if __name__ == '__main__':
|
|
11
|
+
def _write() -> None:
|
|
12
|
+
sys.stdout.write(
|
|
13
|
+
f"{(match:=countTotal == dictionaryONE[oeisID]['valuesKnown'][n])}\t"
|
|
14
|
+
f"\033[{(not match)*91}m"
|
|
15
|
+
f"{n}\t"
|
|
16
|
+
f"{countTotal}\t"
|
|
17
|
+
f"{time.perf_counter() - timeStart:.2f}\t"
|
|
18
|
+
"\033[0m\n"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
CPUlimit: bool | float | int | None = -2
|
|
22
|
+
flow: str | None = None
|
|
23
|
+
|
|
24
|
+
oeisID = 'A007822'
|
|
25
|
+
|
|
26
|
+
flow = 'algorithm'
|
|
27
|
+
flow = 'asynchronous'
|
|
28
|
+
flow = 'theorem2Trimmed'
|
|
29
|
+
flow = 'theorem2Numba'
|
|
30
|
+
flow = 'theorem2'
|
|
31
|
+
|
|
32
|
+
# for n in range(13,16):
|
|
33
|
+
for n in range(1,4):
|
|
34
|
+
|
|
35
|
+
timeStart = time.perf_counter()
|
|
36
|
+
countTotal = NOTcountingFolds(oeisID, n, flow, CPUlimit)
|
|
37
|
+
|
|
38
|
+
_write()
|
|
39
|
+
|
|
40
|
+
r"""
|
|
41
|
+
deactivate && C:\apps\mapFolding\.vtail\Scripts\activate.bat && title good && cls
|
|
42
|
+
title running && start "working" /B /HIGH /wait py -X faulthandler=0 -X tracemalloc=0 -X frozen_modules=on easyRun\NOTcountingFolds.py & title I'm done
|
|
43
|
+
"""
|
|
44
|
+
|
|
@@ -23,14 +23,21 @@ if __name__ == '__main__':
|
|
|
23
23
|
computationDivisions: int | str | None = None
|
|
24
24
|
CPUlimit: bool | float | int | None = None
|
|
25
25
|
# mapShape: tuple[int, ...] | None = None
|
|
26
|
-
flow
|
|
26
|
+
flow = 'daoOfMapFolding'
|
|
27
|
+
flow = 'numba'
|
|
28
|
+
flow = 'theorem2'
|
|
29
|
+
flow = 'theorem2Numba'
|
|
30
|
+
flow: str | None = 'theorem2Trimmed'
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
|
|
33
|
+
oeisID: str = 'A001415'
|
|
34
|
+
oeisID: str = 'A000136'
|
|
35
|
+
for n in range(1,4):
|
|
30
36
|
|
|
31
37
|
mapShape: tuple[int, ...] = dictionaryOEISMapFolding[oeisID]['getMapShape'](n)
|
|
32
38
|
|
|
33
39
|
timeStart = time.perf_counter()
|
|
40
|
+
# foldsTotal: int = countFolds(listDimensions=None, pathLikeWriteFoldsTotal=None, computationDivisions=None, CPUlimit=None, mapShape=(2, 3), flow='theorem2Trimmed')
|
|
34
41
|
foldsTotal: int = countFolds(listDimensions=listDimensions
|
|
35
42
|
, pathLikeWriteFoldsTotal=pathLikeWriteFoldsTotal
|
|
36
43
|
, computationDivisions=computationDivisions
|
|
@@ -24,8 +24,8 @@ if __name__ == '__main__':
|
|
|
24
24
|
if sys.version_info >= (3, 14):
|
|
25
25
|
warnings.filterwarnings("ignore", category=FutureWarning)
|
|
26
26
|
|
|
27
|
-
flow = 'matrixMeanders'
|
|
28
27
|
flow = 'matrixPandas'
|
|
28
|
+
flow = 'matrixMeanders'
|
|
29
29
|
flow = 'matrixNumPy'
|
|
30
30
|
|
|
31
31
|
for oeisID in [
|
|
@@ -46,8 +46,8 @@ if __name__ == '__main__':
|
|
|
46
46
|
|
|
47
47
|
nList: list[int] = []
|
|
48
48
|
nList.extend(range(2, 10))
|
|
49
|
-
# nList.extend(range(
|
|
50
|
-
nList.extend(range(28,33))
|
|
49
|
+
# nList.extend(range(10, 28))
|
|
50
|
+
# nList.extend(range(28,33))
|
|
51
51
|
# nList.extend(range(33,38))
|
|
52
52
|
# nList.extend(range(38,43))
|
|
53
53
|
# nList.extend(range(43,45))
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
from ortools.sat.python import cp_model
|
|
2
|
+
|
|
3
|
+
def findValidFoldings(leavesTotal: int, workersMaximum: int) -> list[list[int]]: # noqa: ARG001
|
|
4
|
+
model = cp_model.CpModel()
|
|
5
|
+
listIndexAsIntVar: list[cp_model.IntVar] = [model.NewIntVar(0, leavesTotal - 1, f"leafIndexAtPosition[{positionIndex}]") for positionIndex in range(leavesTotal)]
|
|
6
|
+
indexOfLeafInIndexOfPosition: list[cp_model.IntVar] = [model.NewIntVar(0, leavesTotal - 1, f"positionOfLeafIndex[{leafNumber}]") for leafNumber in range(1, leavesTotal + 1)]
|
|
7
|
+
model.AddInverse(listIndexAsIntVar, indexOfLeafInIndexOfPosition)
|
|
8
|
+
model.Add(listIndexAsIntVar[0] == 0) # Fix leaf 1 at position 0
|
|
9
|
+
|
|
10
|
+
if leavesTotal > 2:
|
|
11
|
+
leavesTotalIsOdd: bool = leavesTotal % 2 == 1
|
|
12
|
+
if leavesTotalIsOdd:
|
|
13
|
+
leafTwoOccupiesPositionTwo: cp_model.IntVar = model.NewBoolVar("leafTwoOccupiesPositionTwo")
|
|
14
|
+
model.Add(indexOfLeafInIndexOfPosition[1] == 2).OnlyEnforceIf(leafTwoOccupiesPositionTwo)
|
|
15
|
+
model.Add(indexOfLeafInIndexOfPosition[1] != 2).OnlyEnforceIf(leafTwoOccupiesPositionTwo.Not())
|
|
16
|
+
model.Add(listIndexAsIntVar[1] == leavesTotal - 1).OnlyEnforceIf(leafTwoOccupiesPositionTwo)
|
|
17
|
+
else:
|
|
18
|
+
model.Add(indexOfLeafInIndexOfPosition[1] != 2)
|
|
19
|
+
|
|
20
|
+
if leavesTotal > 3:
|
|
21
|
+
leafTwoOccupiesFinalPosition: cp_model.IntVar = model.NewBoolVar("leafTwoOccupiesFinalPosition")
|
|
22
|
+
model.Add(indexOfLeafInIndexOfPosition[1] == leavesTotal - 1).OnlyEnforceIf(leafTwoOccupiesFinalPosition)
|
|
23
|
+
model.Add(indexOfLeafInIndexOfPosition[1] != leavesTotal - 1).OnlyEnforceIf(leafTwoOccupiesFinalPosition.Not())
|
|
24
|
+
leavesTotalIsEven: bool = leavesTotal % 2 == 0
|
|
25
|
+
if leavesTotalIsEven:
|
|
26
|
+
leafThreeOccupiesIndexTwoFromEnd: cp_model.IntVar = model.NewBoolVar("leafThreeOccupiesIndexTwoFromEnd")
|
|
27
|
+
model.Add(indexOfLeafInIndexOfPosition[2] == leavesTotal - 3).OnlyEnforceIf(leafThreeOccupiesIndexTwoFromEnd)
|
|
28
|
+
model.Add(indexOfLeafInIndexOfPosition[2] != leavesTotal - 3).OnlyEnforceIf(leafThreeOccupiesIndexTwoFromEnd.Not())
|
|
29
|
+
lastLeafOccupiesPenultimatePosition: cp_model.IntVar = model.NewBoolVar("lastLeafOccupiesPenultimatePosition")
|
|
30
|
+
model.Add(listIndexAsIntVar[leavesTotal - 2] == leavesTotal - 1).OnlyEnforceIf(lastLeafOccupiesPenultimatePosition)
|
|
31
|
+
model.Add(listIndexAsIntVar[leavesTotal - 2] != leavesTotal - 1).OnlyEnforceIf(lastLeafOccupiesPenultimatePosition.Not())
|
|
32
|
+
model.AddBoolOr([
|
|
33
|
+
leafTwoOccupiesFinalPosition.Not(),
|
|
34
|
+
leafThreeOccupiesIndexTwoFromEnd.Not(),
|
|
35
|
+
lastLeafOccupiesPenultimatePosition,
|
|
36
|
+
])
|
|
37
|
+
else:
|
|
38
|
+
model.Add(indexOfLeafInIndexOfPosition[2] != leavesTotal - 3).OnlyEnforceIf(leafTwoOccupiesFinalPosition)
|
|
39
|
+
|
|
40
|
+
listForbiddenInequalitiesDeconstructed: list[tuple[tuple[int, int], tuple[int, int], tuple[int, int]]] = []
|
|
41
|
+
for k in range(1, leavesTotal):
|
|
42
|
+
for r in range(2, leavesTotal):
|
|
43
|
+
if r == k or (k - r) % 2 != 0:
|
|
44
|
+
continue
|
|
45
|
+
k1: int = k + 1
|
|
46
|
+
r1: int = r + 1
|
|
47
|
+
|
|
48
|
+
"""All 8 forbidden forms, index of:
|
|
49
|
+
[k < r < k+1 < r+1] [r < k+1 < r+1 < k] [k+1 < r+1 < k < r] [r+1 < k < r < k+1]
|
|
50
|
+
[r < k < r+1 < k+1] [k < r+1 < k+1 < r] [r+1 < k+1 < r < k] [k+1 < r < k < r+1]
|
|
51
|
+
"""
|
|
52
|
+
listForbiddenInequalitiesDeconstructed.extend([
|
|
53
|
+
((k-1, r-1), (r-1, k1-1), (k1-1, r1-1)),
|
|
54
|
+
((k1-1, r1-1), (r1-1, k-1), (k-1, r-1)),
|
|
55
|
+
((r1-1, k-1), (k-1, r-1), (r-1, k1-1)),
|
|
56
|
+
((k-1, r1-1), (r1-1, k1-1), (k1-1, r-1)),
|
|
57
|
+
# ((r-1, k1-1), (k1-1, r1-1), (r1-1, k-1)), ((r-1, k-1), (k-1, r1-1), (r1-1, k1-1)), ((r1-1, k1-1), (k1-1, r-1), (r-1, k-1)), ((k1-1, r-1), (r-1, k-1), (k-1, r1-1)), # noqa: ERA001
|
|
58
|
+
])
|
|
59
|
+
for tupleIndices in listForbiddenInequalitiesDeconstructed:
|
|
60
|
+
listOfInequalities: list[cp_model.IntVar] = []
|
|
61
|
+
for indexLeft, indexRight in tupleIndices:
|
|
62
|
+
inequalityOf2Indices: cp_model.IntVar = model.NewBoolVar(f"order_{indexLeft}_{indexRight}")
|
|
63
|
+
model.Add(indexOfLeafInIndexOfPosition[indexLeft] < indexOfLeafInIndexOfPosition[indexRight]).OnlyEnforceIf(inequalityOf2Indices)
|
|
64
|
+
model.Add(indexOfLeafInIndexOfPosition[indexLeft] >= indexOfLeafInIndexOfPosition[indexRight]).OnlyEnforceIf(inequalityOf2Indices.Not())
|
|
65
|
+
listOfInequalities.append(inequalityOf2Indices)
|
|
66
|
+
# At least one inequality must be false to avoid forbidden pattern
|
|
67
|
+
model.AddBoolOr([inequality.Not() for inequality in listOfInequalities])
|
|
68
|
+
|
|
69
|
+
solver = cp_model.CpSolver()
|
|
70
|
+
solver.parameters.enumerate_all_solutions = True
|
|
71
|
+
|
|
72
|
+
solver.parameters.cp_model_presolve = True
|
|
73
|
+
solver.parameters.use_combined_no_overlap = True
|
|
74
|
+
solver.parameters.use_disjunctive_constraint_in_cumulative = True
|
|
75
|
+
solver.parameters.use_objective_shaving_search = True
|
|
76
|
+
solver.parameters.use_precedences_in_disjunctive_constraint = True
|
|
77
|
+
# solver.parameters.num_workers = 2 # noqa: ERA001
|
|
78
|
+
solver.parameters.num_workers = 1
|
|
79
|
+
|
|
80
|
+
class FoldingCollector(cp_model.CpSolverSolutionCallback):
|
|
81
|
+
def __init__(self, leafIndexAtPositionInput: list[cp_model.IntVar]) -> None:
|
|
82
|
+
super().__init__()
|
|
83
|
+
self.leafIndexAtPositionInput = leafIndexAtPositionInput
|
|
84
|
+
self.listFoldings: list[list[int]] = []
|
|
85
|
+
|
|
86
|
+
def OnSolutionCallback(self) -> None:
|
|
87
|
+
self.listFoldings.append([self.Value(positionVariable) + 1 for positionVariable in self.leafIndexAtPositionInput]) # pyright: ignore[reportUnknownMemberType]
|
|
88
|
+
|
|
89
|
+
foldingCollector = FoldingCollector(listIndexAsIntVar)
|
|
90
|
+
solver.Solve(model, foldingCollector)
|
|
91
|
+
return foldingCollector.listFoldings
|
|
92
|
+
|
|
93
|
+
def doTheNeedful(leavesTotal: int, workersMaximum: int = 1) -> int:
|
|
94
|
+
"""Count the number of valid foldings for a given number of leaves."""
|
|
95
|
+
return len(findValidFoldings(leavesTotal, workersMaximum)) * leavesTotal
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
from itertools import permutations, starmap
|
|
2
|
+
from typing import Final
|
|
3
|
+
|
|
4
|
+
def isThisValid(folding: tuple[int, ...]) -> bool:
|
|
5
|
+
"""Verify that a folding sequence is possible.
|
|
6
|
+
|
|
7
|
+
Parameters
|
|
8
|
+
----------
|
|
9
|
+
folding : list[int]
|
|
10
|
+
List of integers representing the folding sequence.
|
|
11
|
+
|
|
12
|
+
Returns
|
|
13
|
+
-------
|
|
14
|
+
valid : bool
|
|
15
|
+
True if the folding sequence is valid, False otherwise.
|
|
16
|
+
|
|
17
|
+
Notes
|
|
18
|
+
-----
|
|
19
|
+
All 8 forbidden forms
|
|
20
|
+
[k, r, k+1, r+1] [r, k+1, r+1, k] [k+1, r+1, k, r] [r+1, k, r, k+1]
|
|
21
|
+
[r, k, r+1, k+1] [k, r+1, k+1, r] [r+1, k+1, r, k] [k+1, r, k, r+1]
|
|
22
|
+
|
|
23
|
+
I selected the four forms in which k precedes r. Because of the flow, I _think_ that is why these four are sufficient.
|
|
24
|
+
|
|
25
|
+
Citation
|
|
26
|
+
--------
|
|
27
|
+
John E. Koehler, Folding a strip of stamps, Journal of Combinatorial Theory, Volume 5, Issue 2, 1968, Pages 135-152, ISSN
|
|
28
|
+
0021-9800, https://doi.org/10.1016/S0021-9800(68)80048-1.
|
|
29
|
+
(https://www.sciencedirect.com/science/article/pii/S0021980068800481)
|
|
30
|
+
|
|
31
|
+
See Also
|
|
32
|
+
--------
|
|
33
|
+
- "[Annotated, corrected, scanned copy]" at https://oeis.org/A001011.
|
|
34
|
+
- Citation in BibTeX format "citations/KOEHLER1968135.bib".
|
|
35
|
+
"""
|
|
36
|
+
leavesTotal: int = len(folding)
|
|
37
|
+
for index, leaf in enumerate(folding[0:-1]): # `[0:-1]` No room to interpose
|
|
38
|
+
if leaf == leavesTotal:
|
|
39
|
+
continue
|
|
40
|
+
indexLeafRightSide: int = folding.index(leaf+1)
|
|
41
|
+
leafIsOdd: int = leaf & 1
|
|
42
|
+
|
|
43
|
+
for indexInterposer, interposer in enumerate(folding[index + 1:None], start=index + 1): # [k != r]
|
|
44
|
+
if leafIsOdd != (interposer & 1): # [k%2 == r%2]
|
|
45
|
+
continue
|
|
46
|
+
if interposer == leavesTotal:
|
|
47
|
+
continue
|
|
48
|
+
|
|
49
|
+
indexInterposerRightSide: int = folding.index(interposer + 1)
|
|
50
|
+
|
|
51
|
+
if (index < indexInterposer < indexLeafRightSide < indexInterposerRightSide # [k, r, k+1, r+1]
|
|
52
|
+
or index < indexInterposerRightSide < indexLeafRightSide < indexInterposer # [k, r+1, k+1, r]
|
|
53
|
+
or indexLeafRightSide < indexInterposerRightSide < index < indexInterposer # [k+1, r+1, k, r]
|
|
54
|
+
or indexInterposerRightSide < index < indexInterposer < indexLeafRightSide # [r+1, k, r, k+1]
|
|
55
|
+
):
|
|
56
|
+
return False
|
|
57
|
+
return True
|
|
58
|
+
|
|
59
|
+
def count(prefix: list[int], permutands: list[int], postfix: list[int]) -> int:
|
|
60
|
+
"""Count the number of valid foldings for a given fixed start and remaining leaves.
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
prefix : list[int]
|
|
65
|
+
List of integers representing the fixed start of the folding sequence.
|
|
66
|
+
permutands : list[int]
|
|
67
|
+
List of elements to permute into permutations.
|
|
68
|
+
postfix : list[int]
|
|
69
|
+
List of integers representing the fixed end of the folding sequence.
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
groupsOfFolds : int
|
|
74
|
+
Number of valid foldings, which each represent a group of folds, for the given configuration.
|
|
75
|
+
"""
|
|
76
|
+
groupsOfFolds: int = 0
|
|
77
|
+
for aPermutation in permutations(permutands):
|
|
78
|
+
groupsOfFolds += isThisValid((*prefix, *aPermutation, *postfix))
|
|
79
|
+
return groupsOfFolds
|
|
80
|
+
|
|
81
|
+
def staging(prefix: list[int], permutands: list[int]) -> int:
|
|
82
|
+
"""Segregate sequences with a final `2`: necessary as part of excluding leading `1,2`.
|
|
83
|
+
|
|
84
|
+
Parameters
|
|
85
|
+
----------
|
|
86
|
+
prefix : list[int]
|
|
87
|
+
List of integers representing the fixed start of the folding sequence.
|
|
88
|
+
permutands : list[int]
|
|
89
|
+
List of elements to permute into permutations.
|
|
90
|
+
|
|
91
|
+
Notes
|
|
92
|
+
-----
|
|
93
|
+
Transformation indices:
|
|
94
|
+
|
|
95
|
+
1,3,4,5,6,2,
|
|
96
|
+
1,2,6,5,4,3,
|
|
97
|
+
|
|
98
|
+
All valid sequences that end with '2' are in the first half.
|
|
99
|
+
All valid sequences that start with '1,2' are in the second half.
|
|
100
|
+
The remaining valid sequences are evenly split between the two halves.
|
|
101
|
+
Therefore:
|
|
102
|
+
1. Filter out all '1,2' before checking validity.
|
|
103
|
+
2. If a valid sequence ends in '2', add 2 to the total count.
|
|
104
|
+
3. If a valid sequence does not end in '2', add 1 to the total count.
|
|
105
|
+
|
|
106
|
+
`leaf = leavesTotal` is evenly distributed in each half like this: (ex. from A007822(7))
|
|
107
|
+
^1,14, ,14,$ 612
|
|
108
|
+
,14,$ ^1,14, 1486
|
|
109
|
+
"""
|
|
110
|
+
groupsOfFolds: int = 0
|
|
111
|
+
postfix: list[int] = []
|
|
112
|
+
if 2 in permutands:
|
|
113
|
+
postfix.append(2)
|
|
114
|
+
postfixComplement: list[int] = permutands.copy()
|
|
115
|
+
postfixComplement.remove(2)
|
|
116
|
+
groupsOfFolds += count(prefix, postfixComplement, postfix) * 2
|
|
117
|
+
for leafPostfix in postfixComplement:
|
|
118
|
+
groupsOfFolds += count(prefix, [leaf for leaf in permutands if leaf != leafPostfix], [leafPostfix])
|
|
119
|
+
else:
|
|
120
|
+
groupsOfFolds += count(prefix, permutands, postfix)
|
|
121
|
+
return groupsOfFolds
|
|
122
|
+
|
|
123
|
+
def doTheNeedful(n: int) -> int:
|
|
124
|
+
"""Count the number of valid foldings for a given number of leaves."""
|
|
125
|
+
leavesTotal: Final[int] = n
|
|
126
|
+
listToPermute: list[tuple[list[int], list[int]]] = []
|
|
127
|
+
|
|
128
|
+
prefix: list[int] = [1]
|
|
129
|
+
listLeaves: Final[list[int]] = list(range(leavesTotal, 1, -1))
|
|
130
|
+
|
|
131
|
+
permutands: list[int] = listLeaves.copy()
|
|
132
|
+
|
|
133
|
+
# ------- Exclude leading 2 -------------------------------
|
|
134
|
+
# NOTE 1,{3..n},{2..n}...
|
|
135
|
+
excludeLeading2: list[int] = permutands.copy()
|
|
136
|
+
excludeLeading2.remove(2)
|
|
137
|
+
listToPermute.extend([([*prefix, leafPrefix], [leaf for leaf in permutands if leaf != leafPrefix]) for leafPrefix in excludeLeading2])
|
|
138
|
+
del excludeLeading2
|
|
139
|
+
|
|
140
|
+
# ------- Exclude interposed 2 ----------------------------
|
|
141
|
+
# NOTE 1,{3..n},{3..n},{2..n}...
|
|
142
|
+
# NOTE 1,{n,if n&1},{2..n-1}...
|
|
143
|
+
for aTuple in listToPermute.copy():
|
|
144
|
+
if len(aTuple[0]) != 2 or leavesTotal < 4:
|
|
145
|
+
continue
|
|
146
|
+
interposer: int = aTuple[0][1]
|
|
147
|
+
excludeInterposed2: list[int] = permutands.copy()
|
|
148
|
+
excludeInterposed2.remove(2)
|
|
149
|
+
excludeInterposed2.remove(interposer)
|
|
150
|
+
if interposer == leavesTotal and interposer & 1:
|
|
151
|
+
listToPermute.append(([*aTuple[0], 2], excludeInterposed2))
|
|
152
|
+
listToPermute.extend([([*aTuple[0], leafPrefix], [leaf for leaf in permutands if leaf not in (leafPrefix, interposer)]) for leafPrefix in excludeInterposed2])
|
|
153
|
+
listToPermute.remove(aTuple)
|
|
154
|
+
del aTuple, excludeInterposed2, interposer
|
|
155
|
+
|
|
156
|
+
return sum(starmap(staging, listToPermute)) * leavesTotal
|
|
157
|
+
|
|
158
|
+
# ------- Exclude interposed 3 ----------------------------
|
|
159
|
+
# NOTE 1,{3..n},{3..n},{2..n}...,{3..n}
|
|
160
|
+
# NOTE 1,{3..n},{3..n},{3..n}...,{4..n},{3..n},{2}
|
|
161
|
+
# NOTE 1,{3..n-1},...,{n,if ~n&1},{2}
|
|
162
|
+
# NOTE 1,{n,if n&1},{2..n-1}...
|
|
163
|
+
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from concurrent.futures import Future, ProcessPoolExecutor
|
|
2
|
+
from itertools import permutations
|
|
3
|
+
|
|
4
|
+
def isThisValid(folding: list[int]) -> bool:
|
|
5
|
+
"""Verify that a folding sequence is possible.
|
|
6
|
+
|
|
7
|
+
Parameters
|
|
8
|
+
----------
|
|
9
|
+
folding : list[int]
|
|
10
|
+
List of integers representing the folding sequence.
|
|
11
|
+
|
|
12
|
+
Returns
|
|
13
|
+
-------
|
|
14
|
+
valid : bool
|
|
15
|
+
True if the folding sequence is valid, False otherwise.
|
|
16
|
+
"""
|
|
17
|
+
leavesTotal: int = len(folding)
|
|
18
|
+
for index, leaf in enumerate(folding[0:-1]): # Last leaf cannot interpose
|
|
19
|
+
if leaf == leavesTotal:
|
|
20
|
+
continue
|
|
21
|
+
indexLeafRightSide: int = folding.index(leaf+1)
|
|
22
|
+
leafIsOdd: int = leaf & 1
|
|
23
|
+
|
|
24
|
+
for indexInterposer, interposer in enumerate(folding[index + 1:None], start=index + 1): # [k != r]
|
|
25
|
+
if leafIsOdd != (interposer & 1): # [k%2 == r%2]
|
|
26
|
+
continue
|
|
27
|
+
if interposer == leavesTotal:
|
|
28
|
+
continue
|
|
29
|
+
|
|
30
|
+
indexInterposerRightSide: int = folding.index(interposer + 1)
|
|
31
|
+
|
|
32
|
+
if (index < indexInterposer < indexLeafRightSide < indexInterposerRightSide # [k, r, k+1, r+1]
|
|
33
|
+
or index < indexInterposerRightSide < indexLeafRightSide < indexInterposer # [k, r+1, k+1, r]
|
|
34
|
+
or indexLeafRightSide < indexInterposerRightSide < index < indexInterposer # [k+1, r+1, k, r]
|
|
35
|
+
or indexInterposerRightSide < index < indexInterposer < indexLeafRightSide # [r+1, k, r, k+1]
|
|
36
|
+
):
|
|
37
|
+
return False
|
|
38
|
+
return True
|
|
39
|
+
|
|
40
|
+
def count(fixed: list[int], permutands: list[int]) -> int:
|
|
41
|
+
"""Count the number of valid foldings for a given fixed start and remaining leaves.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
fixed : list[int]
|
|
46
|
+
List of integers representing the fixed start of the folding sequence.
|
|
47
|
+
permutands : list[int]
|
|
48
|
+
List of elements to permute into permutations.
|
|
49
|
+
"""
|
|
50
|
+
validTotal: int = 0
|
|
51
|
+
for aPermutation in permutations(permutands):
|
|
52
|
+
validTotal += isThisValid([*fixed, *aPermutation])
|
|
53
|
+
return validTotal
|
|
54
|
+
|
|
55
|
+
def doTheNeedful(n: int, processesMaximum: int) -> int:
|
|
56
|
+
"""Count the number of valid foldings for a given number of leaves."""
|
|
57
|
+
validTotal: int = 0
|
|
58
|
+
listLeavesTruncated: list[int] = list(range(2, n + 1))
|
|
59
|
+
# NOTE Design goals:
|
|
60
|
+
# Minimize creation/destruction of processes.
|
|
61
|
+
# Use all processes until the end.
|
|
62
|
+
# A valid sequence takes many times more cycles to process than a sequence that is proved invalid in the first few elements.
|
|
63
|
+
# So, dividing purely on the number of sequences is not optimal.
|
|
64
|
+
# Prefer generators of lists over lists of lists.
|
|
65
|
+
|
|
66
|
+
# In the current system, each `Future` leads to a returned value, which is then summed. But, I don't care about any specific
|
|
67
|
+
# `Future`. I would rather have the processes "consume" work from a common well and return their results when the work is done.
|
|
68
|
+
workers: int = min(processesMaximum, n - 1)
|
|
69
|
+
with ProcessPoolExecutor(max_workers=workers) as processPool:
|
|
70
|
+
listFutures: list[Future[int]] = []
|
|
71
|
+
for index in range(len(listLeavesTruncated)):
|
|
72
|
+
permutands: list[int] = listLeavesTruncated.copy()
|
|
73
|
+
fixedLeaves: list[int] = [1, permutands.pop(index)]
|
|
74
|
+
listFutures.append(processPool.submit(count, fixedLeaves, permutands))
|
|
75
|
+
for futureCount in listFutures:
|
|
76
|
+
validTotal += futureCount.result()
|
|
77
|
+
return validTotal * n
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
from functools import cache
|
|
2
|
+
from mapFolding.dataBaskets import MatrixMeandersState
|
|
3
|
+
|
|
4
|
+
@cache
|
|
5
|
+
def walkDyckPath(intWithExtra_0b1: int) -> int:
|
|
6
|
+
"""Find the bit position for flipping paired curve endpoints in meander transfer matrices.
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
----------
|
|
10
|
+
intWithExtra_0b1 : int
|
|
11
|
+
Binary representation of curve locations with an extra bit encoding parity information.
|
|
12
|
+
|
|
13
|
+
Returns
|
|
14
|
+
-------
|
|
15
|
+
flipExtra_0b1_Here : int
|
|
16
|
+
Bit mask indicating the position where the balance condition fails, formatted as 2^(2k).
|
|
17
|
+
|
|
18
|
+
3L33T H@X0R
|
|
19
|
+
------------
|
|
20
|
+
Binary search for first negative balance in shifted bit pairs. Returns 2^(2k) mask for
|
|
21
|
+
bit position k where cumulative balance counter transitions from non-negative to negative.
|
|
22
|
+
|
|
23
|
+
Mathematics
|
|
24
|
+
-----------
|
|
25
|
+
Implements the Dyck path balance verification algorithm from Jensen's transfer matrix
|
|
26
|
+
enumeration. Computes the position where ∑(i=0 to k) (-1)^b_i < 0 for the first time,
|
|
27
|
+
where b_i are the bits of the input at positions 2i.
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
findTheExtra_0b1: int = 0
|
|
31
|
+
flipExtra_0b1_Here: int = 1
|
|
32
|
+
while True:
|
|
33
|
+
flipExtra_0b1_Here <<= 2
|
|
34
|
+
if intWithExtra_0b1 & flipExtra_0b1_Here == 0:
|
|
35
|
+
findTheExtra_0b1 += 1
|
|
36
|
+
else:
|
|
37
|
+
findTheExtra_0b1 -= 1
|
|
38
|
+
if findTheExtra_0b1 < 0:
|
|
39
|
+
break
|
|
40
|
+
return flipExtra_0b1_Here
|
|
41
|
+
|
|
42
|
+
def count(state: MatrixMeandersState) -> MatrixMeandersState:
|
|
43
|
+
"""Count meanders with matrix transfer algorithm using Python `int` (*int*eger) contained in a Python `dict` (*dict*ionary).
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
state : MatrixMeandersState
|
|
48
|
+
The algorithm state.
|
|
49
|
+
|
|
50
|
+
Notes
|
|
51
|
+
-----
|
|
52
|
+
The matrix transfer algorithm is sophisticated, but this implementation is straightforward: compute each `boundary` one at a
|
|
53
|
+
time, compute each `arcCode` one at a time, and compute each type of analysis one at a time.
|
|
54
|
+
"""
|
|
55
|
+
dictionaryArcCodeToCrossings: dict[int, int] = {}
|
|
56
|
+
|
|
57
|
+
while state.boundary > 0:
|
|
58
|
+
state.reduceBoundary()
|
|
59
|
+
|
|
60
|
+
dictionaryArcCodeToCrossings = state.dictionaryMeanders.copy()
|
|
61
|
+
state.dictionaryMeanders = {}
|
|
62
|
+
|
|
63
|
+
def analyzeArcCode(arcCode: int, crossings: int) -> None:
|
|
64
|
+
bitsAlpha: int = arcCode & state.bitsLocator
|
|
65
|
+
bitsAlphaHasArcs: bool = bitsAlpha > 1
|
|
66
|
+
bitsAlphaIsEven: int = bitsAlpha & 1 ^ 1
|
|
67
|
+
|
|
68
|
+
bitsZulu: int = arcCode >> 1 & state.bitsLocator
|
|
69
|
+
bitsZuluHasArcs: bool = bitsZulu > 1
|
|
70
|
+
bitsZuluIsEven: int = bitsZulu & 1 ^ 1
|
|
71
|
+
|
|
72
|
+
arcCodeAnalysis: int = (bitsZulu << 1 | bitsAlpha) << 2 | 3 # Evaluate formula step-wise left to right: (parentheses) override precedence.
|
|
73
|
+
if arcCodeAnalysis < state.MAXIMUMarcCode:
|
|
74
|
+
state.dictionaryMeanders[arcCodeAnalysis] = state.dictionaryMeanders.get(arcCodeAnalysis, 0) + crossings
|
|
75
|
+
|
|
76
|
+
if bitsAlphaHasArcs:
|
|
77
|
+
arcCodeAnalysis = bitsAlphaIsEven << 1 | bitsAlpha >> 2 | bitsZulu << 3
|
|
78
|
+
if arcCodeAnalysis < state.MAXIMUMarcCode:
|
|
79
|
+
state.dictionaryMeanders[arcCodeAnalysis] = state.dictionaryMeanders.get(arcCodeAnalysis, 0) + crossings
|
|
80
|
+
|
|
81
|
+
if bitsZuluHasArcs:
|
|
82
|
+
arcCodeAnalysis = bitsZuluIsEven | bitsAlpha << 2 | bitsZulu >> 1
|
|
83
|
+
if arcCodeAnalysis < state.MAXIMUMarcCode:
|
|
84
|
+
state.dictionaryMeanders[arcCodeAnalysis] = state.dictionaryMeanders.get(arcCodeAnalysis, 0) + crossings
|
|
85
|
+
|
|
86
|
+
if bitsAlphaHasArcs and bitsZuluHasArcs and (bitsAlphaIsEven or bitsZuluIsEven):
|
|
87
|
+
# NOTE This analysis might modify `bitsAlpha` or `bitsZulu`, so it should be last.
|
|
88
|
+
if bitsAlphaIsEven and not bitsZuluIsEven:
|
|
89
|
+
bitsAlpha ^= walkDyckPath(bitsAlpha)
|
|
90
|
+
elif bitsZuluIsEven and not bitsAlphaIsEven:
|
|
91
|
+
bitsZulu ^= walkDyckPath(bitsZulu)
|
|
92
|
+
|
|
93
|
+
arcCodeAnalysis = (bitsZulu >> 2 << 3 | bitsAlpha) >> 2 # Evaluate formula step-wise left to right: (parentheses) override precedence.
|
|
94
|
+
if arcCodeAnalysis < state.MAXIMUMarcCode:
|
|
95
|
+
state.dictionaryMeanders[arcCodeAnalysis] = state.dictionaryMeanders.get(arcCodeAnalysis, 0) + crossings
|
|
96
|
+
|
|
97
|
+
set(map(analyzeArcCode, dictionaryArcCodeToCrossings.keys(), dictionaryArcCodeToCrossings.values()))
|
|
98
|
+
|
|
99
|
+
dictionaryArcCodeToCrossings = {}
|
|
100
|
+
|
|
101
|
+
return state
|
|
102
|
+
|
|
103
|
+
def doTheNeedful(state: MatrixMeandersState) -> int:
|
|
104
|
+
"""Compute `crossings` with a transfer matrix algorithm.
|
|
105
|
+
|
|
106
|
+
Parameters
|
|
107
|
+
----------
|
|
108
|
+
state : MatrixMeandersState
|
|
109
|
+
The algorithm state.
|
|
110
|
+
|
|
111
|
+
Returns
|
|
112
|
+
-------
|
|
113
|
+
crossings : int
|
|
114
|
+
The computed value of `crossings`.
|
|
115
|
+
|
|
116
|
+
Citations
|
|
117
|
+
---------
|
|
118
|
+
- https://github.com/hunterhogan/mapFolding/blob/main/citations/Jensen.bib
|
|
119
|
+
- https://github.com/hunterhogan/mapFolding/blob/main/citations/Howroyd.bib
|
|
120
|
+
|
|
121
|
+
See Also
|
|
122
|
+
--------
|
|
123
|
+
https://oeis.org/A000682
|
|
124
|
+
https://oeis.org/A005316
|
|
125
|
+
https://github.com/archmageirvine/joeis/blob/5dc2148344bff42182e2128a6c99df78044558c5/src/irvine/oeis/a005/A005316.java
|
|
126
|
+
"""
|
|
127
|
+
state = count(state)
|
|
128
|
+
|
|
129
|
+
return sum(state.dictionaryMeanders.values())
|