mapFolding 0.16.4__py3-none-any.whl → 0.17.1__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.
- easyRun/NOTcountingFolds.py +28 -11
- easyRun/__init__.py +1 -0
- easyRun/countFolds.py +16 -2
- easyRun/eliminateFolds.py +60 -0
- easyRun/meanders.py +3 -3
- mapFolding/__init__.py +2 -1
- mapFolding/_theTypes.py +0 -1
- mapFolding/algorithms/A086345.py +8 -3
- mapFolding/algorithms/__init__.py +1 -1
- mapFolding/algorithms/constraintPropagation.py +184 -0
- mapFolding/algorithms/elimination.py +131 -0
- mapFolding/algorithms/eliminationCount.py +26 -0
- mapFolding/algorithms/eliminationPinned.py +35 -0
- mapFolding/algorithms/iff.py +206 -0
- mapFolding/algorithms/matrixMeanders.py +59 -18
- mapFolding/algorithms/matrixMeandersNumPyndas.py +841 -0
- mapFolding/algorithms/patternFinder.py +280 -0
- mapFolding/algorithms/pinning2Dn.py +345 -0
- mapFolding/algorithms/pinning2DnAnnex.py +43 -0
- mapFolding/algorithms/symmetricFolds.py +24 -25
- mapFolding/basecamp.py +84 -14
- mapFolding/beDRY.py +14 -1
- mapFolding/dataBaskets.py +86 -71
- mapFolding/reference/irvineJavaPort.py +3 -3
- mapFolding/reference/meandersDumpingGround/matrixMeandersNumPyV1finalForm.py +1 -1
- mapFolding/someAssemblyRequired/A007822/_asynchronousAnnex.py +1 -1
- mapFolding/someAssemblyRequired/A007822/makeA007822AsynchronousModules.py +5 -3
- mapFolding/someAssemblyRequired/A007822/makeA007822Modules.py +22 -6
- mapFolding/someAssemblyRequired/RecipeJob.py +14 -24
- mapFolding/someAssemblyRequired/__init__.py +1 -0
- mapFolding/someAssemblyRequired/_toolkitContainers.py +6 -4
- mapFolding/someAssemblyRequired/infoBooth.py +2 -1
- mapFolding/someAssemblyRequired/makeJobTheorem2Numba.py +75 -20
- mapFolding/someAssemblyRequired/makeJobTheorem2codon.py +9 -10
- mapFolding/someAssemblyRequired/makingModules_count.py +20 -22
- mapFolding/someAssemblyRequired/makingModules_doTheNeedful.py +9 -9
- mapFolding/someAssemblyRequired/mapFoldingModules/makeMapFoldingModules.py +6 -5
- mapFolding/someAssemblyRequired/meanders/makeMeandersModules.py +6 -6
- mapFolding/someAssemblyRequired/toolkitMakeModules.py +3 -29
- mapFolding/someAssemblyRequired/toolkitNumba.py +2 -1
- mapFolding/someAssemblyRequired/transformationTools.py +2 -3
- mapFolding/syntheticModules/A007822/algorithm.py +8 -8
- mapFolding/syntheticModules/A007822/asynchronous.py +12 -13
- mapFolding/syntheticModules/A007822/initializeState.py +10 -8
- mapFolding/syntheticModules/A007822/theorem2.py +10 -8
- mapFolding/syntheticModules/A007822/theorem2Numba.py +20 -16
- mapFolding/syntheticModules/A007822/theorem2Trimmed.py +10 -8
- mapFolding/syntheticModules/countParallelNumba.py +5 -2
- mapFolding/syntheticModules/daoOfMapFoldingNumba.py +4 -2
- mapFolding/syntheticModules/initializeState.py +1 -1
- mapFolding/syntheticModules/meanders/bigInt.py +52 -15
- mapFolding/syntheticModules/theorem2.py +1 -1
- mapFolding/syntheticModules/theorem2Numba.py +4 -2
- mapFolding/syntheticModules/theorem2Trimmed.py +1 -1
- mapFolding/tests/conftest.py +1 -1
- mapFolding/tests/test_computations.py +21 -4
- mapFolding/tests/verify.py +323 -0
- {mapfolding-0.16.4.dist-info → mapfolding-0.17.1.dist-info}/METADATA +14 -11
- mapfolding-0.17.1.dist-info/RECORD +112 -0
- easyRun/A000682.py +0 -25
- easyRun/A005316.py +0 -20
- mapFolding/algorithms/matrixMeandersBeDry.py +0 -182
- mapFolding/algorithms/matrixMeandersNumPy.py +0 -333
- mapFolding/algorithms/matrixMeandersPandas.py +0 -334
- mapfolding-0.16.4.dist-info/RECORD +0 -106
- {mapfolding-0.16.4.dist-info → mapfolding-0.17.1.dist-info}/WHEEL +0 -0
- {mapfolding-0.16.4.dist-info → mapfolding-0.17.1.dist-info}/entry_points.txt +0 -0
- {mapfolding-0.16.4.dist-info → mapfolding-0.17.1.dist-info}/licenses/LICENSE +0 -0
- {mapfolding-0.16.4.dist-info → mapfolding-0.17.1.dist-info}/top_level.txt +0 -0
|
@@ -3,34 +3,33 @@
|
|
|
3
3
|
Notes
|
|
4
4
|
-----
|
|
5
5
|
- About constructing `leafComparison`:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
- Therefore, the first iteration of the loop is hardcoded to save processing time.
|
|
9
|
-
- I _feel_ there must be a more efficient way to do this.
|
|
6
|
+
- The first iteration of the loop is hardcoded to save processing time.
|
|
7
|
+
- I _feel_ there must be a more efficient way to do this.
|
|
10
8
|
- Some implementation details are based on Numba compatibility. Incompatible:
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
- `numpy.take(..., out=...)`
|
|
10
|
+
- `numpy.all(..., axis=...)`
|
|
13
11
|
"""
|
|
14
12
|
from mapFolding.dataBaskets import SymmetricFoldsState
|
|
15
|
-
import numpy
|
|
16
13
|
|
|
17
14
|
def filterAsymmetricFolds(state: SymmetricFoldsState) -> SymmetricFoldsState:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
15
|
+
state.indexLeaf = 1
|
|
16
|
+
state.leafComparison[0] = 1
|
|
17
|
+
state.leafConnectee = 1
|
|
18
|
+
|
|
19
|
+
while state.leafConnectee < state.leavesTotal + 1:
|
|
20
|
+
state.indexMiniGap = state.leafBelow[state.indexLeaf]
|
|
21
|
+
state.leafComparison[state.leafConnectee] = (state.indexMiniGap - state.indexLeaf + state.leavesTotal) % state.leavesTotal
|
|
22
|
+
state.indexLeaf = state.indexMiniGap
|
|
23
|
+
|
|
24
|
+
state.leafConnectee += 1
|
|
25
|
+
|
|
26
|
+
for listTuples in state.indices:
|
|
27
|
+
state.leafConnectee = 1
|
|
28
|
+
for indexLeft, indexRight in listTuples:
|
|
29
|
+
if state.leafComparison[indexLeft] != state.leafComparison[indexRight]:
|
|
30
|
+
state.leafConnectee = 0
|
|
31
|
+
break
|
|
32
|
+
state.symmetricFolds += state.leafConnectee
|
|
33
|
+
|
|
34
|
+
return state
|
|
36
35
|
|
mapFolding/basecamp.py
CHANGED
|
@@ -169,18 +169,20 @@ def countFolds(listDimensions: Sequence[int] | None = None
|
|
|
169
169
|
|
|
170
170
|
mapFoldingParallelState: ParallelMapFoldingState = ParallelMapFoldingState(mapShape, taskDivisions=taskDivisions)
|
|
171
171
|
|
|
172
|
-
# `listStatesParallel` exists so you can research the parallel computation.
|
|
172
|
+
# NOTE `listStatesParallel` exists so you can research the parallel computation.
|
|
173
173
|
foldsTotal, _listStatesParallel = doTheNeedful(mapFoldingParallelState, concurrencyLimit)
|
|
174
174
|
|
|
175
175
|
# ruff: noqa: E701
|
|
176
176
|
else:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
177
|
+
if all(dimension < 2 for dimension in mapShape):
|
|
178
|
+
from mapFolding.algorithms.daoOfMapFolding import doTheNeedful
|
|
179
|
+
else:
|
|
180
|
+
match flow:
|
|
181
|
+
case 'numba': from mapFolding.syntheticModules.daoOfMapFoldingNumba import doTheNeedful
|
|
182
|
+
case 'theorem2': from mapFolding.syntheticModules.theorem2 import doTheNeedful
|
|
183
|
+
case 'theorem2Numba': from mapFolding.syntheticModules.theorem2Numba import doTheNeedful
|
|
184
|
+
case 'theorem2Trimmed': from mapFolding.syntheticModules.theorem2Trimmed import doTheNeedful
|
|
185
|
+
case 'daoOfMapFolding' | _: from mapFolding.algorithms.daoOfMapFolding import doTheNeedful
|
|
184
186
|
|
|
185
187
|
from mapFolding.dataBaskets import MapFoldingState
|
|
186
188
|
mapFoldingState: MapFoldingState = MapFoldingState(mapShape)
|
|
@@ -194,6 +196,75 @@ def countFolds(listDimensions: Sequence[int] | None = None
|
|
|
194
196
|
|
|
195
197
|
return foldsTotal
|
|
196
198
|
|
|
199
|
+
def eliminateFolds(mapShape: tuple[int, ...]
|
|
200
|
+
, pathLikeWriteFoldsTotal: PathLike[str] | PurePath | None = None
|
|
201
|
+
# , * # TODO improve `standardizedEqualToCallableReturn` so it will work with keyword arguments
|
|
202
|
+
, CPUlimit: bool | float | int | None = None # noqa: FBT001
|
|
203
|
+
, flow: str | None = None
|
|
204
|
+
) -> int:
|
|
205
|
+
"""
|
|
206
|
+
Compute foldsTotal by elimination.
|
|
207
|
+
|
|
208
|
+
Parameters
|
|
209
|
+
----------
|
|
210
|
+
mapShape : tuple[int, ...] | None = None
|
|
211
|
+
Tuple of integers representing the dimensions of the map to be folded. Mathematicians almost always use the term
|
|
212
|
+
"dimensions", such as in the seminal paper, "Multi-dimensional map-folding". Nevertheless, in contemporary Python
|
|
213
|
+
programming, in the context of these algorithms, the term "shape" makes it much easier to align the mathematics with the
|
|
214
|
+
syntax of the programming language.
|
|
215
|
+
pathLikeWriteFoldsTotal : PathLike[str] | PurePath | None = None
|
|
216
|
+
A filename, a path of only directories, or a path with directories and a filename to which `countFolds` will write the
|
|
217
|
+
value of `foldsTotal`. If `pathLikeWriteFoldsTotal` is a path of only directories, `countFolds` creates a filename based
|
|
218
|
+
on the map dimensions.
|
|
219
|
+
CPUlimit : bool | float | int | None = None
|
|
220
|
+
If relevant, whether and how to limit the number of processors `countFolds` will use.
|
|
221
|
+
- `False`, `None`, or `0`: No limits on processor usage; uses all available processors. All other values will
|
|
222
|
+
potentially limit processor usage.
|
|
223
|
+
- `True`: Yes, limit the processor usage; limits to 1 processor.
|
|
224
|
+
- `int >= 1`: The maximum number of available processors to use.
|
|
225
|
+
- `0 < float < 1`: The maximum number of processors to use expressed as a fraction of available processors.
|
|
226
|
+
- `-1 < float < 0`: The number of processors to *not* use expressed as a fraction of available processors.
|
|
227
|
+
- `int <= -1`: The number of available processors to *not* use.
|
|
228
|
+
- If the value of `CPUlimit` is a `float` greater than 1 or less than -1, `countFolds` truncates the value to an `int`
|
|
229
|
+
with the same sign as the `float`.
|
|
230
|
+
flow : str | None = None
|
|
231
|
+
My stupid way of selecting the version of the algorithm to use in the computation.
|
|
232
|
+
|
|
233
|
+
Returns
|
|
234
|
+
-------
|
|
235
|
+
foldsTotal : int
|
|
236
|
+
Number of distinct ways to fold a map of the given dimensions.
|
|
237
|
+
"""
|
|
238
|
+
from mapFolding.beDRY import setProcessorLimit
|
|
239
|
+
concurrencyLimit: int = setProcessorLimit(CPUlimit, packageSettings.concurrencyPackage)
|
|
240
|
+
|
|
241
|
+
# ------- memorialization instructions ---------------------------------------------
|
|
242
|
+
|
|
243
|
+
if pathLikeWriteFoldsTotal is not None:
|
|
244
|
+
pathFilenameFoldsTotal: Path | None = getPathFilenameFoldsTotal(mapShape, pathLikeWriteFoldsTotal)
|
|
245
|
+
saveFoldsTotalFAILearly(pathFilenameFoldsTotal)
|
|
246
|
+
else:
|
|
247
|
+
pathFilenameFoldsTotal = None
|
|
248
|
+
|
|
249
|
+
# ------- Algorithm version -----------------------------------------------------
|
|
250
|
+
# ruff: noqa: E701
|
|
251
|
+
match flow:
|
|
252
|
+
case 'constraintPropagation': from mapFolding.algorithms.constraintPropagation import doTheNeedful
|
|
253
|
+
case 'pinned': from mapFolding.algorithms.eliminationPinned import doTheNeedful
|
|
254
|
+
case 'elimination' | _: from mapFolding.algorithms.elimination import doTheNeedful
|
|
255
|
+
|
|
256
|
+
from mapFolding.dataBaskets import EliminationState
|
|
257
|
+
eliminationState: EliminationState = EliminationState(mapShape)
|
|
258
|
+
eliminationState = doTheNeedful(eliminationState, concurrencyLimit)
|
|
259
|
+
foldsTotal = eliminationState.foldsTotal
|
|
260
|
+
|
|
261
|
+
# ------- Follow memorialization instructions ---------------------------------------------
|
|
262
|
+
|
|
263
|
+
if pathFilenameFoldsTotal is not None:
|
|
264
|
+
saveFoldsTotal(pathFilenameFoldsTotal, foldsTotal)
|
|
265
|
+
|
|
266
|
+
return foldsTotal
|
|
267
|
+
|
|
197
268
|
def NOTcountingFolds(oeisID: str, oeis_n: int, flow: str | None = None
|
|
198
269
|
# TODO , pathLikeWriteFoldsTotal: PathLike[str] | PurePath | None = None
|
|
199
270
|
, CPUlimit: bool | float | int | None = None # noqa: FBT001
|
|
@@ -225,12 +296,11 @@ def NOTcountingFolds(oeisID: str, oeis_n: int, flow: str | None = None
|
|
|
225
296
|
case 'A000682' | 'A005316':
|
|
226
297
|
match flow:
|
|
227
298
|
case 'matrixNumPy':
|
|
228
|
-
from mapFolding.algorithms.
|
|
229
|
-
from mapFolding.dataBaskets import MatrixMeandersNumPyState as State
|
|
299
|
+
from mapFolding.algorithms.matrixMeandersNumPyndas import doTheNeedful, MatrixMeandersNumPyState as State
|
|
230
300
|
case 'matrixPandas':
|
|
231
|
-
from mapFolding.algorithms.
|
|
232
|
-
|
|
233
|
-
case _:
|
|
301
|
+
from mapFolding.algorithms.matrixMeandersNumPyndas import (
|
|
302
|
+
doTheNeedfulPandas as doTheNeedful, MatrixMeandersNumPyState as State)
|
|
303
|
+
case 'matrixMeanders' | _:
|
|
234
304
|
from mapFolding.algorithms.matrixMeanders import doTheNeedful
|
|
235
305
|
from mapFolding.dataBaskets import MatrixMeandersState as State
|
|
236
306
|
|
|
@@ -291,7 +361,7 @@ def NOTcountingFolds(oeisID: str, oeis_n: int, flow: str | None = None
|
|
|
291
361
|
from mapFolding.syntheticModules.A007822.algorithm import doTheNeedful
|
|
292
362
|
symmetricState = doTheNeedful(symmetricState)
|
|
293
363
|
|
|
294
|
-
countTotal = symmetricState.
|
|
364
|
+
countTotal = symmetricState.symmetricFolds
|
|
295
365
|
case _:
|
|
296
366
|
matched_oeisID = False
|
|
297
367
|
|
mapFolding/beDRY.py
CHANGED
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
"""Oft-needed computations or actions, especially for multi-dimensional map folding."""
|
|
2
2
|
|
|
3
|
-
from collections.abc import Sequence
|
|
3
|
+
from collections.abc import Iterable, Iterator, Sequence
|
|
4
|
+
from functools import cache
|
|
4
5
|
from hunterMakesPy import defineConcurrencyLimit, intInnit, oopsieKwargsie
|
|
5
6
|
from mapFolding import NumPyIntegerType
|
|
7
|
+
from more_itertools import extract
|
|
6
8
|
from numpy import dtype as numpy_dtype, int64 as numpy_int64, ndarray
|
|
7
9
|
from sys import maxsize as sysMaxsize
|
|
8
10
|
from typing import Any
|
|
9
11
|
import numpy
|
|
10
12
|
|
|
13
|
+
def exclude[个](iterable: Sequence[个], indices: Iterable[int]) -> Iterator[个]:
|
|
14
|
+
"""Yield items from `iterable` whose positions are not in `indices`."""
|
|
15
|
+
lengthIterable: int = len(iterable)
|
|
16
|
+
def normalizeIndex(index: int) -> int:
|
|
17
|
+
if index < 0:
|
|
18
|
+
index = (index + lengthIterable) % lengthIterable
|
|
19
|
+
return index
|
|
20
|
+
indicesInclude: list[int] = sorted(set(range(lengthIterable)).difference(map(normalizeIndex, indices)))
|
|
21
|
+
return extract(iterable, indicesInclude)
|
|
22
|
+
|
|
23
|
+
@cache
|
|
11
24
|
def getLeavesTotal(mapShape: tuple[int, ...]) -> int:
|
|
12
25
|
"""Calculate the total number of leaves in a map with the given dimensions.
|
|
13
26
|
|
mapFolding/dataBaskets.py
CHANGED
|
@@ -21,12 +21,65 @@ integrity throughout the recursive analysis while providing the structured data
|
|
|
21
21
|
access patterns that enable efficient result persistence and retrieval.
|
|
22
22
|
"""
|
|
23
23
|
from mapFolding import (
|
|
24
|
-
Array1DElephino, Array1DLeavesTotal,
|
|
25
|
-
|
|
26
|
-
from numpy.typing import NDArray
|
|
27
|
-
from typing import TypeAlias
|
|
24
|
+
Array1DElephino, Array1DLeavesTotal, Array3DLeavesTotal, DatatypeElephino, DatatypeFoldsTotal, DatatypeLeavesTotal,
|
|
25
|
+
getConnectionGraph, getLeavesTotal, makeDataContainer)
|
|
28
26
|
import dataclasses
|
|
29
|
-
|
|
27
|
+
|
|
28
|
+
@dataclasses.dataclass(slots=True)
|
|
29
|
+
class EliminationState:
|
|
30
|
+
"""Computational state for algorithms to compute foldsTotal by elimination.
|
|
31
|
+
|
|
32
|
+
Attributes
|
|
33
|
+
----------
|
|
34
|
+
mapShape : tuple[DatatypeLeavesTotal, ...]
|
|
35
|
+
Dimensions of the map being analyzed for folding patterns.
|
|
36
|
+
groupsOfFolds : DatatypeFoldsTotal = DatatypeFoldsTotal(0)
|
|
37
|
+
Current count of distinct folding pattern groups: each group has `leavesTotal`-many foldings.
|
|
38
|
+
dimensionsTotal : DatatypeLeavesTotal
|
|
39
|
+
Unchanging total number of dimensions in the map.
|
|
40
|
+
leavesTotal : DatatypeLeavesTotal
|
|
41
|
+
Unchanging total number of leaves in the map.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
mapShape: tuple[DatatypeLeavesTotal, ...] = dataclasses.field(init=True, metadata={'elementConstructor': 'DatatypeLeavesTotal'})
|
|
45
|
+
"""Dimensions of the map being analyzed for folding patterns."""
|
|
46
|
+
|
|
47
|
+
groupsOfFolds: DatatypeFoldsTotal = dataclasses.field(default=DatatypeFoldsTotal(0), metadata={'theCountingIdentifier': True})
|
|
48
|
+
"""Current count of distinct folding pattern groups: each group has `leavesTotal`-many foldings."""
|
|
49
|
+
|
|
50
|
+
listPinnedLeaves: list[dict[int, int]] = dataclasses.field(default_factory=list[dict[int, int]], init=True)
|
|
51
|
+
"""column: leaf or pile: indexLeaf"""
|
|
52
|
+
pile: DatatypeLeavesTotal = DatatypeLeavesTotal(-1) # noqa: RUF009
|
|
53
|
+
pinnedLeaves: dict[int, int] = dataclasses.field(default_factory=dict[int, int], init=True, metadata={'elementConstructor': 'DatatypeLeavesTotal'})
|
|
54
|
+
"""column: leaf or pile: indexLeaf"""
|
|
55
|
+
|
|
56
|
+
subsetsTheorem2: DatatypeLeavesTotal = DatatypeLeavesTotal(1) # noqa: RUF009
|
|
57
|
+
subsetsTheorem3: DatatypeLeavesTotal = DatatypeLeavesTotal(1) # noqa: RUF009
|
|
58
|
+
subsetsTheorem4: DatatypeLeavesTotal = DatatypeLeavesTotal(1) # noqa: RUF009
|
|
59
|
+
|
|
60
|
+
columnLast: DatatypeLeavesTotal = dataclasses.field(init=False)
|
|
61
|
+
dimensionsTotal: DatatypeLeavesTotal = dataclasses.field(init=False)
|
|
62
|
+
"""Unchanging total number of dimensions in the map."""
|
|
63
|
+
leavesTotal: DatatypeLeavesTotal = dataclasses.field(init=False)
|
|
64
|
+
"""Unchanging total number of leaves in the map."""
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def foldsTotal(self) -> DatatypeFoldsTotal:
|
|
68
|
+
"""The total number of possible folding patterns for this map.
|
|
69
|
+
|
|
70
|
+
Returns
|
|
71
|
+
-------
|
|
72
|
+
foldsTotal : DatatypeFoldsTotal
|
|
73
|
+
The complete count of distinct folding patterns achievable with the current map configuration.
|
|
74
|
+
|
|
75
|
+
"""
|
|
76
|
+
return DatatypeFoldsTotal(self.leavesTotal) * self.groupsOfFolds * self.subsetsTheorem2 * self.subsetsTheorem3 * self.subsetsTheorem4
|
|
77
|
+
|
|
78
|
+
def __post_init__(self) -> None:
|
|
79
|
+
"""Ensure all fields have a value."""
|
|
80
|
+
self.dimensionsTotal = DatatypeLeavesTotal(len(self.mapShape))
|
|
81
|
+
self.leavesTotal = DatatypeLeavesTotal(getLeavesTotal(self.mapShape))
|
|
82
|
+
self.columnLast = self.leavesTotal - DatatypeLeavesTotal(1)
|
|
30
83
|
|
|
31
84
|
@dataclasses.dataclass(slots=True)
|
|
32
85
|
class MapFoldingState:
|
|
@@ -203,7 +256,7 @@ class SymmetricFoldsState:
|
|
|
203
256
|
mapShape: tuple[DatatypeLeavesTotal, ...] = dataclasses.field(init=True, metadata={'elementConstructor': 'DatatypeLeavesTotal'})
|
|
204
257
|
"""Dimensions of the map being analyzed for folding patterns."""
|
|
205
258
|
|
|
206
|
-
|
|
259
|
+
symmetricFolds: DatatypeFoldsTotal = dataclasses.field(default=DatatypeFoldsTotal(0), metadata={'theCountingIdentifier': True})
|
|
207
260
|
"""Current count of symmetric folds."""
|
|
208
261
|
|
|
209
262
|
gap1ndex: DatatypeElephino = DatatypeElephino(0) # noqa: RUF009
|
|
@@ -237,12 +290,12 @@ class SymmetricFoldsState:
|
|
|
237
290
|
leafComparison: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
|
|
238
291
|
"""Array for finding symmetric folds."""
|
|
239
292
|
|
|
240
|
-
arrayGroupOfFolds: Array2DLeavesTotal = dataclasses.field(init=False, metadata={'dtype': Array2DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
|
|
241
293
|
connectionGraph: Array3DLeavesTotal = dataclasses.field(init=False, metadata={'dtype': Array3DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
|
|
242
294
|
"""Unchanging array representing connections between all leaves."""
|
|
243
295
|
dimensionsTotal: DatatypeLeavesTotal = dataclasses.field(init=False)
|
|
244
296
|
"""Unchanging total number of dimensions in the map."""
|
|
245
|
-
|
|
297
|
+
indices: list[list[tuple[int, int]]] = dataclasses.field(init=False)
|
|
298
|
+
"""Precomputed index pairs for symmetric fold checking."""
|
|
246
299
|
leavesTotal: DatatypeLeavesTotal = dataclasses.field(init=False)
|
|
247
300
|
"""Unchanging total number of leaves in the map."""
|
|
248
301
|
|
|
@@ -259,12 +312,9 @@ class SymmetricFoldsState:
|
|
|
259
312
|
self.leavesTotal = DatatypeLeavesTotal(getLeavesTotal(self.mapShape))
|
|
260
313
|
|
|
261
314
|
leavesTotalAsInt = int(self.leavesTotal)
|
|
262
|
-
|
|
263
315
|
self.connectionGraph = getConnectionGraph(self.mapShape, leavesTotalAsInt, self.__dataclass_fields__['connectionGraph'].metadata['dtype'])
|
|
264
316
|
|
|
265
|
-
self.
|
|
266
|
-
self.indicesArrayGroupOfFolds[..., leavesTotalAsInt // 2:None] = self.indicesArrayGroupOfFolds[..., leavesTotalAsInt - 1: leavesTotalAsInt - leavesTotalAsInt // 2 - 1: -1]
|
|
267
|
-
self.arrayGroupOfFolds = numpy.zeros_like(self.indicesArrayGroupOfFolds)
|
|
317
|
+
self.indices = [[((index + folding) % (self.leavesTotal+1), (-2-index + folding) % (self.leavesTotal+1)) for index in range(self.leavesTotal//2)] for folding in range(self.leavesTotal + 1)]
|
|
268
318
|
|
|
269
319
|
if self.dimensionsUnconstrained is None: self.dimensionsUnconstrained = DatatypeLeavesTotal(int(self.dimensionsTotal)) # pyright: ignore[reportUnnecessaryComparison] # noqa: E701
|
|
270
320
|
if self.gapsWhere is None: self.gapsWhere = makeDataContainer(leavesTotalAsInt * leavesTotalAsInt + 1, self.__dataclass_fields__['gapsWhere'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison] # noqa: E701
|
|
@@ -398,6 +448,7 @@ class MatrixMeandersState:
|
|
|
398
448
|
"""The index of the meanders problem being solved."""
|
|
399
449
|
oeisID: str
|
|
400
450
|
"""'A000682', semi-meanders, or 'A005316', meanders."""
|
|
451
|
+
|
|
401
452
|
boundary: int
|
|
402
453
|
"""The algorithm analyzes `n` boundaries starting at `boundary = n - 1`."""
|
|
403
454
|
dictionaryMeanders: dict[int, int]
|
|
@@ -408,14 +459,19 @@ class MatrixMeandersState:
|
|
|
408
459
|
bitWidth: int = 0
|
|
409
460
|
"""At the start of an iteration enumerated by `boundary`, the number of bits of the largest value `arcCode`. The
|
|
410
461
|
`dataclass` computes a `property` from `bitWidth`."""
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
462
|
+
bitsLocator: int = 0
|
|
463
|
+
"""An odd-parity bit-mask with `bitWidth` bits."""
|
|
464
|
+
MAXIMUMarcCode: int = 0
|
|
465
|
+
"""The maximum value of `arcCode` for the current iteration of the transfer matrix."""
|
|
466
|
+
|
|
467
|
+
def reduceBoundary(self) -> None:
|
|
468
|
+
"""Prepare for the next iteration of the transfer matrix algorithm by reducing `boundary` by 1 and updating related fields."""
|
|
469
|
+
self.boundary -= 1
|
|
470
|
+
self.setBitWidth()
|
|
471
|
+
self.setBitsLocator()
|
|
472
|
+
self.setMAXIMUMarcCode()
|
|
473
|
+
|
|
474
|
+
def setBitsLocator(self) -> None:
|
|
419
475
|
"""Compute an odd-parity bit-mask with `bitWidth` bits.
|
|
420
476
|
|
|
421
477
|
Notes
|
|
@@ -429,60 +485,19 @@ class MatrixMeandersState:
|
|
|
429
485
|
odd numbers from even numbers, so I avoid using "odd" and "even" in the names of these bit-masks.
|
|
430
486
|
|
|
431
487
|
"""
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
@dataclasses.dataclass(slots=True)
|
|
435
|
-
class MatrixMeandersNumPyState(MatrixMeandersState):
|
|
436
|
-
"""Hold the state of a meanders transfer matrix algorithm computation."""
|
|
437
|
-
|
|
438
|
-
arrayArcCodes: NDArray[numpy.uint64] = dataclasses.field(default_factory=lambda: numpy.empty((0,), dtype=numpy.uint64))
|
|
439
|
-
arrayCrossings: NDArray[numpy.uint64] = dataclasses.field(default_factory=lambda: numpy.empty((0,), dtype=numpy.uint64))
|
|
488
|
+
self.bitsLocator = sum(1 << one for one in range(0, self.bitWidth, 2))
|
|
440
489
|
|
|
441
|
-
|
|
442
|
-
|
|
490
|
+
def setBitWidth(self) -> None:
|
|
491
|
+
"""Set `bitWidth` from the current `dictionaryMeanders`."""
|
|
492
|
+
self.bitWidth = max(self.dictionaryMeanders.keys()).bit_length()
|
|
443
493
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
"""The fixed-size integer type used to store `crossings`."""
|
|
448
|
-
|
|
449
|
-
indexTarget: int = 0
|
|
450
|
-
"""What is being indexed depends on the algorithm flavor."""
|
|
494
|
+
def setMAXIMUMarcCode(self) -> None:
|
|
495
|
+
"""Compute the maximum value of `arcCode` for the current iteration of the transfer matrix."""
|
|
496
|
+
self.MAXIMUMarcCode = 1 << (2 * self.boundary + 4)
|
|
451
497
|
|
|
452
498
|
def __post_init__(self) -> None:
|
|
453
499
|
"""Post init."""
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
_offsetNecessary: int = 3 # For example, `bitsZulu << 3`.
|
|
458
|
-
_offsetSafety: int = 1 # I don't have mathematical proof of how many extra bits I need.
|
|
459
|
-
_offset: int = _offsetNecessary + _offsetSafety
|
|
460
|
-
|
|
461
|
-
self.bitWidthLimitArcCode = _bitWidthOfFixedSizeInteger - _offset
|
|
462
|
-
|
|
463
|
-
del _bitWidthOfFixedSizeInteger, _offsetNecessary, _offsetSafety, _offset
|
|
464
|
-
|
|
465
|
-
if self.bitWidthLimitCrossings is None:
|
|
466
|
-
_bitWidthOfFixedSizeInteger: int = numpy.dtype(self.datatypeCrossings).itemsize * 8 # bits
|
|
467
|
-
|
|
468
|
-
_offsetNecessary: int = 0 # I don't know of any.
|
|
469
|
-
_offsetEstimation: int = 3 # See reference directory.
|
|
470
|
-
_offsetSafety: int = 1
|
|
471
|
-
_offset: int = _offsetNecessary + _offsetEstimation + _offsetSafety
|
|
472
|
-
|
|
473
|
-
self.bitWidthLimitCrossings = _bitWidthOfFixedSizeInteger - _offset
|
|
474
|
-
|
|
475
|
-
del _bitWidthOfFixedSizeInteger, _offsetNecessary, _offsetEstimation, _offsetSafety, _offset
|
|
476
|
-
|
|
477
|
-
def makeDictionary(self) -> None:
|
|
478
|
-
"""Convert from NumPy `ndarray` (*Num*erical *Py*thon *n-d*imensional array) to Python `dict` (*dict*ionary)."""
|
|
479
|
-
self.dictionaryMeanders = {int(key): int(value) for key, value in zip(self.arrayArcCodes, self.arrayCrossings, strict=True)}
|
|
480
|
-
self.arrayArcCodes = numpy.empty((0,), dtype=self.datatypeArcCode)
|
|
481
|
-
self.arrayCrossings = numpy.empty((0,), dtype=self.datatypeCrossings)
|
|
500
|
+
self.setBitWidth()
|
|
501
|
+
self.setBitsLocator()
|
|
502
|
+
self.setMAXIMUMarcCode()
|
|
482
503
|
|
|
483
|
-
def makeArray(self) -> None:
|
|
484
|
-
"""Convert from Python `dict` (*dict*ionary) to NumPy `ndarray` (*Num*erical *Py*thon *n-d*imensional array)."""
|
|
485
|
-
self.arrayArcCodes = numpy.array(list(self.dictionaryMeanders.keys()), dtype=self.datatypeArcCode)
|
|
486
|
-
self.arrayCrossings = numpy.array(list(self.dictionaryMeanders.values()), dtype=self.datatypeCrossings)
|
|
487
|
-
self.bitWidth = int(self.arrayArcCodes.max()).bit_length()
|
|
488
|
-
self.dictionaryMeanders = {}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
1
|
+
"""Ported from the Java version by Sean A. Irvine.
|
|
2
|
+
|
|
3
3
|
https://github.com/archmageirvine/joeis/blob/80e3e844b11f149704acbab520bc3a3a25ac34ff/src/irvine/oeis/a001/A001415.java
|
|
4
4
|
|
|
5
5
|
This implementation is a Python version of a Java implementation of Lunnon's algorithm by Sean A. Irvine.
|
|
@@ -9,7 +9,7 @@ Key characteristics:
|
|
|
9
9
|
- A procedural paradigm more similar to Lunnon and unlike Irvine's object-oriented implementation.
|
|
10
10
|
- Only primitive Python data structures.
|
|
11
11
|
|
|
12
|
-
Citation: https://github.com/hunterhogan/mapFolding/blob/134f2e6ecdf59fb6f6829c775475544a6aaaa800/citations/jOEIS.
|
|
12
|
+
Citation: https://github.com/hunterhogan/mapFolding/blob/134f2e6ecdf59fb6f6829c775475544a6aaaa800/citations/jOEIS.bib
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
15
|
def foldings(p: list[int], res: int = 0, mod: int = 0) -> int:
|
|
@@ -360,7 +360,7 @@ def doTheNeedful(n: int, dictionaryCurveLocations: dict[int, int]) -> int:
|
|
|
360
360
|
--------------
|
|
361
361
|
|
|
362
362
|
As first computed by Iwan Jensen in 2000, A000682(41) = 6664356253639465480.
|
|
363
|
-
Citation: https://github.com/hunterhogan/mapFolding/blob/main/citations/Jensen.
|
|
363
|
+
Citation: https://github.com/hunterhogan/mapFolding/blob/main/citations/Jensen.bib
|
|
364
364
|
See also https://oeis.org/A000682
|
|
365
365
|
|
|
366
366
|
I'm sure you instantly observed that A000682(41) = (6664356253639465480).bit_length() = 63 bits. And A005316(44) =
|
|
@@ -34,7 +34,7 @@ def _threadDoesSomething() -> None:
|
|
|
34
34
|
break
|
|
35
35
|
state = _filterAsymmetricFolds(state)
|
|
36
36
|
with LOCKsymmetricFoldsTotal:
|
|
37
|
-
symmetricFoldsTotal += state.
|
|
37
|
+
symmetricFoldsTotal += state.symmetricFolds
|
|
38
38
|
|
|
39
39
|
def _filterAsymmetricFolds(state: SymmetricFoldsState) -> SymmetricFoldsState:
|
|
40
40
|
"""Add real function during generation; the signature is here to preview its interactions with the module."""
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"""addSymmetryCheckAsynchronous."""
|
|
2
|
-
from astToolkit import Be, Grab, identifierDotAttribute,
|
|
2
|
+
from astToolkit import Be, Grab, identifierDotAttribute, Make, NodeChanger, NodeTourist, Then
|
|
3
|
+
from astToolkit.containers import LedgerOfImports
|
|
4
|
+
from astToolkit.transformationTools import write_astModule
|
|
3
5
|
from hunterMakesPy import raiseIfNone
|
|
4
6
|
from mapFolding import packageSettings
|
|
5
7
|
from mapFolding.someAssemblyRequired import defaultA007822, IfThis
|
|
6
8
|
from mapFolding.someAssemblyRequired.A007822.A007822rawMaterials import ExprCallFilterAsymmetricFoldsState
|
|
7
|
-
from mapFolding.someAssemblyRequired.toolkitMakeModules import getModule, getPathFilename
|
|
9
|
+
from mapFolding.someAssemblyRequired.toolkitMakeModules import getModule, getPathFilename
|
|
8
10
|
from pathlib import PurePath
|
|
9
11
|
import ast
|
|
10
12
|
|
|
@@ -85,7 +87,7 @@ def addSymmetryCheckAsynchronous(astModule: ast.Module, identifierModule: str, i
|
|
|
85
87
|
|
|
86
88
|
pathFilename: PurePath = getPathFilename(packageSettings.pathPackage, logicalPathInfix, identifierModule)
|
|
87
89
|
|
|
88
|
-
write_astModule(astModule, pathFilename, packageSettings.identifierPackage)
|
|
90
|
+
write_astModule(astModule, pathFilename, identifierPackage=packageSettings.identifierPackage)
|
|
89
91
|
|
|
90
92
|
return pathFilename
|
|
91
93
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"""addSymmetryCheck."""
|
|
2
2
|
from astToolkit import (
|
|
3
|
-
Be, Grab, identifierDotAttribute,
|
|
3
|
+
Be, Grab, identifierDotAttribute, Make, NodeChanger, NodeTourist, parsePathFilename2astModule, Then)
|
|
4
|
+
from astToolkit.containers import LedgerOfImports
|
|
5
|
+
from astToolkit.transformationTools import write_astModule
|
|
4
6
|
from hunterMakesPy import raiseIfNone
|
|
5
7
|
from mapFolding import packageSettings
|
|
6
8
|
from mapFolding.someAssemblyRequired import default, defaultA007822, IfThis
|
|
@@ -8,7 +10,7 @@ from mapFolding.someAssemblyRequired.A007822.A007822rawMaterials import (
|
|
|
8
10
|
A007822adjustFoldsTotal, A007822incrementCount, FunctionDef_filterAsymmetricFolds)
|
|
9
11
|
from mapFolding.someAssemblyRequired.makingModules_count import makeTheorem2, numbaOnTheorem2, trimTheorem2
|
|
10
12
|
from mapFolding.someAssemblyRequired.makingModules_doTheNeedful import makeInitializeState
|
|
11
|
-
from mapFolding.someAssemblyRequired.toolkitMakeModules import getModule, getPathFilename
|
|
13
|
+
from mapFolding.someAssemblyRequired.toolkitMakeModules import getModule, getPathFilename
|
|
12
14
|
from pathlib import PurePath
|
|
13
15
|
import ast
|
|
14
16
|
|
|
@@ -43,7 +45,21 @@ def addSymmetryCheck(astModule: ast.Module, identifierModule: str, identifierCal
|
|
|
43
45
|
|
|
44
46
|
pathFilename: PurePath = getPathFilename(packageSettings.pathPackage, logicalPathInfix, identifierModule)
|
|
45
47
|
|
|
46
|
-
write_astModule(astModule, pathFilename, packageSettings.identifierPackage)
|
|
48
|
+
write_astModule(astModule, pathFilename, identifierPackage=packageSettings.identifierPackage)
|
|
49
|
+
|
|
50
|
+
return pathFilename
|
|
51
|
+
|
|
52
|
+
def _numbaOnTheorem2(astModule: ast.Module, identifierModule: str, identifierCallable: str | None = None, logicalPathInfix: identifierDotAttribute | None = None, sourceCallableDispatcher: str | None = None) -> PurePath:
|
|
53
|
+
pathFilename: PurePath = numbaOnTheorem2(astModule, identifierModule, identifierCallable, logicalPathInfix, sourceCallableDispatcher)
|
|
54
|
+
astModule = parsePathFilename2astModule(pathFilename)
|
|
55
|
+
|
|
56
|
+
NodeChanger(Be.AnnAssign.valueIs(IfThis.isAttributeNamespaceIdentifier(defaultA007822['variable']['stateInstance'], 'indices'))
|
|
57
|
+
, lambda node: Grab.valueAttribute(Then.replaceWith(Make.Call(Make.Name('List'), [raiseIfNone(node.value)])))(node)
|
|
58
|
+
).visit(astModule)
|
|
59
|
+
|
|
60
|
+
astModule.body.insert(0, Make.ImportFrom('numba.typed', [Make.alias('List')]))
|
|
61
|
+
|
|
62
|
+
write_astModule(astModule, pathFilename, identifierPackage=packageSettings.identifierPackage)
|
|
47
63
|
|
|
48
64
|
return pathFilename
|
|
49
65
|
|
|
@@ -55,18 +71,18 @@ def makeA007822Modules() -> None:
|
|
|
55
71
|
|
|
56
72
|
astModule = getModule(logicalPathInfix=defaultA007822['logicalPath']['synthetic'], identifierModule=defaultA007822['module']['algorithm'])
|
|
57
73
|
makeInitializeState(astModule, defaultA007822['module']['initializeState']
|
|
58
|
-
, defaultA007822['function']['initializeState'], defaultA007822['logicalPath']['synthetic'], None)
|
|
74
|
+
, defaultA007822['function']['initializeState'], defaultA007822['logicalPath']['synthetic'], None, identifiers=defaultA007822)
|
|
59
75
|
|
|
60
76
|
astModule = getModule(logicalPathInfix=defaultA007822['logicalPath']['synthetic'], identifierModule=defaultA007822['module']['algorithm'])
|
|
61
77
|
pathFilename = makeTheorem2(astModule, 'theorem2', defaultA007822['function']['counting']
|
|
62
|
-
, defaultA007822['logicalPath']['synthetic'], defaultA007822['function']['dispatcher'])
|
|
78
|
+
, defaultA007822['logicalPath']['synthetic'], defaultA007822['function']['dispatcher'], identifiers=defaultA007822)
|
|
63
79
|
|
|
64
80
|
astModule = parsePathFilename2astModule(pathFilename)
|
|
65
81
|
pathFilename = trimTheorem2(astModule, 'theorem2Trimmed', defaultA007822['function']['counting']
|
|
66
82
|
, defaultA007822['logicalPath']['synthetic'], defaultA007822['function']['dispatcher'])
|
|
67
83
|
|
|
68
84
|
astModule = parsePathFilename2astModule(pathFilename)
|
|
69
|
-
pathFilename =
|
|
85
|
+
pathFilename = _numbaOnTheorem2(astModule, 'theorem2Numba', defaultA007822['function']['counting']
|
|
70
86
|
, defaultA007822['logicalPath']['synthetic'], defaultA007822['function']['dispatcher'])
|
|
71
87
|
|
|
72
88
|
if __name__ == '__main__':
|