mapFolding 0.15.4__py3-none-any.whl → 0.16.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/A000682.py +25 -0
- easyRun/A005316.py +21 -0
- easyRun/NOTcountingFolds.py +36 -0
- easyRun/__init__.py +0 -0
- easyRun/countFolds.py +41 -0
- easyRun/meanders.py +71 -0
- mapFolding/__init__.py +10 -55
- mapFolding/_dataPacking.py +68 -0
- mapFolding/_theSSOT.py +33 -36
- mapFolding/_theTypes.py +21 -4
- mapFolding/algorithms/daoOfMapFolding.py +1 -2
- mapFolding/algorithms/matrixMeanders.py +101 -348
- mapFolding/algorithms/matrixMeandersBeDry.py +264 -0
- mapFolding/algorithms/matrixMeandersNumPy.py +286 -0
- mapFolding/algorithms/matrixMeandersPandas.py +351 -0
- mapFolding/algorithms/oeisIDbyFormula.py +320 -76
- mapFolding/algorithms/zCuzDocStoopidoeisIDbyFormula.py +92 -0
- mapFolding/basecamp.py +261 -113
- mapFolding/beDRY.py +2 -30
- mapFolding/dataBaskets.py +120 -4
- mapFolding/oeis.py +13 -33
- mapFolding/reference/A000682facts.py +1276 -0
- mapFolding/reference/A005316facts.py +985 -0
- mapFolding/reference/matrixMeandersAnalysis/__init__.py +1 -0
- mapFolding/reference/matrixMeandersAnalysis/prefixNotationNotes.py +15 -0
- mapFolding/reference/meandersDumpingGround/A005316JavaPort.py +1 -1
- mapFolding/reference/meandersDumpingGround/A005316imperative.py +1 -1
- mapFolding/reference/meandersDumpingGround/matrixMeandersNumPyV1finalForm.py +424 -0
- mapFolding/someAssemblyRequired/A007822/A007822rawMaterials.py +54 -0
- mapFolding/someAssemblyRequired/A007822/__init__.py +0 -0
- mapFolding/someAssemblyRequired/A007822/makeA007822AsynchronousModules.py +197 -0
- mapFolding/someAssemblyRequired/A007822/makeA007822Modules.py +74 -0
- mapFolding/someAssemblyRequired/RecipeJob.py +4 -4
- mapFolding/someAssemblyRequired/__init__.py +9 -2
- mapFolding/someAssemblyRequired/_toolIfThis.py +4 -3
- mapFolding/someAssemblyRequired/_toolkitContainers.py +8 -8
- mapFolding/someAssemblyRequired/infoBooth.py +27 -30
- mapFolding/someAssemblyRequired/makeJobTheorem2Numba.py +6 -5
- mapFolding/someAssemblyRequired/makeJobTheorem2codon.py +6 -4
- mapFolding/someAssemblyRequired/makingModules_count.py +294 -0
- mapFolding/someAssemblyRequired/makingModules_doTheNeedful.py +117 -0
- mapFolding/someAssemblyRequired/mapFolding/__init__.py +0 -0
- mapFolding/someAssemblyRequired/mapFolding/makeMapFoldingModules.py +220 -0
- mapFolding/someAssemblyRequired/meanders/__init__.py +0 -0
- mapFolding/someAssemblyRequired/meanders/makeMeandersModules.py +64 -0
- mapFolding/someAssemblyRequired/toolkitMakeModules.py +152 -0
- mapFolding/someAssemblyRequired/toolkitNumba.py +1 -1
- mapFolding/someAssemblyRequired/transformationTools.py +1 -0
- mapFolding/syntheticModules/A007822/__init__.py +1 -0
- mapFolding/syntheticModules/{algorithmA007822.py → A007822/algorithm.py} +2 -3
- mapFolding/syntheticModules/{algorithmA007822Numba.py → A007822/algorithmNumba.py} +3 -6
- mapFolding/syntheticModules/A007822/asynchronous.py +148 -0
- mapFolding/syntheticModules/A007822/asynchronousAnnex.py +66 -0
- mapFolding/syntheticModules/A007822/asynchronousAnnexNumba.py +85 -0
- mapFolding/syntheticModules/A007822/asynchronousNumba.py +52 -0
- mapFolding/syntheticModules/A007822/asynchronousTheorem2.py +53 -0
- mapFolding/syntheticModules/A007822/asynchronousTrimmed.py +47 -0
- mapFolding/syntheticModules/{initializeStateA007822.py → A007822/initializeState.py} +1 -2
- mapFolding/syntheticModules/{theorem2A007822.py → A007822/theorem2.py} +1 -2
- mapFolding/syntheticModules/{theorem2A007822Numba.py → A007822/theorem2Numba.py} +6 -4
- mapFolding/syntheticModules/{theorem2A007822Trimmed.py → A007822/theorem2Trimmed.py} +1 -2
- mapFolding/syntheticModules/countParallelNumba.py +5 -2
- mapFolding/syntheticModules/daoOfMapFoldingNumba.py +4 -2
- mapFolding/syntheticModules/dataPacking.py +4 -2
- mapFolding/syntheticModules/dataPackingA007822.py +92 -26
- mapFolding/syntheticModules/meanders/__init__.py +1 -0
- mapFolding/syntheticModules/meanders/bigInt.py +62 -0
- mapFolding/syntheticModules/theorem2Numba.py +3 -2
- mapFolding/tests/conftest.py +28 -13
- mapFolding/tests/test_computations.py +69 -62
- mapFolding/tests/test_oeis.py +6 -6
- mapFolding/zCuzDocStoopid/__init__.py +4 -0
- mapFolding/zCuzDocStoopid/makeDocstrings.py +68 -0
- mapfolding-0.16.1.dist-info/METADATA +99 -0
- mapfolding-0.16.1.dist-info/RECORD +114 -0
- {mapfolding-0.15.4.dist-info → mapfolding-0.16.1.dist-info}/top_level.txt +1 -0
- mapFolding/someAssemblyRequired/A007822rawMaterials.py +0 -46
- mapFolding/someAssemblyRequired/makeAllModules.py +0 -764
- mapfolding-0.15.4.dist-info/METADATA +0 -78
- mapfolding-0.15.4.dist-info/RECORD +0 -78
- {mapfolding-0.15.4.dist-info → mapfolding-0.16.1.dist-info}/WHEEL +0 -0
- {mapfolding-0.15.4.dist-info → mapfolding-0.16.1.dist-info}/entry_points.txt +0 -0
- {mapfolding-0.15.4.dist-info → mapfolding-0.16.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"""Be DRY."""
|
|
2
|
+
from functools import cache
|
|
3
|
+
from hunterMakesPy import raiseIfNone
|
|
4
|
+
from mapFolding.dataBaskets import MatrixMeandersNumPyState
|
|
5
|
+
from mapFolding.reference.A000682facts import A000682_n_k_buckets
|
|
6
|
+
from mapFolding.reference.A005316facts import (
|
|
7
|
+
A005316_n_k_buckets, bucketsIf_k_EVEN_by_nLess_k, bucketsIf_k_ODD_by_nLess_k)
|
|
8
|
+
from math import exp, log
|
|
9
|
+
from typing import Any, NamedTuple
|
|
10
|
+
import math
|
|
11
|
+
import numpy
|
|
12
|
+
import pandas
|
|
13
|
+
|
|
14
|
+
"""Goals:
|
|
15
|
+
- Extreme abstraction.
|
|
16
|
+
- Find operations with latent intermediate arrays and make the intermediate array explicit.
|
|
17
|
+
- Reduce or eliminate intermediate arrays and selector arrays.
|
|
18
|
+
- Write formulas in prefix notation.
|
|
19
|
+
- For each formula, find an equivalent prefix notation formula that never uses the same variable as input more than once: that
|
|
20
|
+
would allow the evaluation of the expression with only a single stack, which saves memory.
|
|
21
|
+
- Standardize code as much as possible to create duplicate code.
|
|
22
|
+
- Convert duplicate code to procedures.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
class ImaKey(NamedTuple):
|
|
26
|
+
"""keys for dictionaries."""
|
|
27
|
+
|
|
28
|
+
oeisID: str
|
|
29
|
+
kIsOdd: bool
|
|
30
|
+
nLess_kIsOdd: bool
|
|
31
|
+
|
|
32
|
+
def areIntegersWide(state: MatrixMeandersNumPyState, *, dataframe: pandas.DataFrame | None = None, array: numpy.ndarray[tuple[int, ...], numpy.dtype[numpy.integer[Any]]] | None = None, fixedSizeMAXIMUMarcCode: bool = False) -> bool:
|
|
33
|
+
"""Check if the largest values are wider than the maximum limits.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
state : MatrixMeandersState
|
|
38
|
+
The current state of the computation, including `dictionaryMeanders`.
|
|
39
|
+
dataframe : pandas.DataFrame | None = None
|
|
40
|
+
Optional DataFrame containing 'analyzed' and 'crossings' columns. If provided, use this instead of `state.dictionaryMeanders`.
|
|
41
|
+
fixedSizeMAXIMUMarcCode : bool = False
|
|
42
|
+
Set this to `True` if you cast `state.MAXIMUMarcCode` to the same fixed size integer type as `state.datatypeArcCode`.
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
wider : bool
|
|
47
|
+
True if at least one integer is too wide.
|
|
48
|
+
|
|
49
|
+
Notes
|
|
50
|
+
-----
|
|
51
|
+
Casting `state.MAXIMUMarcCode` to a fixed-size 64-bit unsigned integer might cause the flow to be a little more
|
|
52
|
+
complicated because `MAXIMUMarcCode` is usually 1-bit larger than the `max(arcCode)` value.
|
|
53
|
+
|
|
54
|
+
If you start the algorithm with very large `arcCode` in your `dictionaryMeanders` (*i.e.,* A000682), then the
|
|
55
|
+
flow will go to a function that does not use fixed size integers. When the integers are below the limits (*e.g.,*
|
|
56
|
+
`bitWidthArcCodeMaximum`), the flow will go to a function with fixed size integers. In that case, casting
|
|
57
|
+
`MAXIMUMarcCode` to a fixed size merely delays the transition from one function to the other by one iteration.
|
|
58
|
+
|
|
59
|
+
If you start with small values in `dictionaryMeanders`, however, then the flow goes to the function with fixed size
|
|
60
|
+
integers and usually stays there until `crossings` is huge, which is near the end of the computation. If you cast
|
|
61
|
+
`MAXIMUMarcCode` into a 64-bit unsigned integer, however, then around `state.kOfMatrix == 28`, the bit width of
|
|
62
|
+
`MAXIMUMarcCode` might exceed the limit. That will cause the flow to go to the function that does not have fixed size
|
|
63
|
+
integers for a few iterations before returning to the function with fixed size integers.
|
|
64
|
+
"""
|
|
65
|
+
if dataframe is not None:
|
|
66
|
+
arcCodeWidest = int(dataframe['analyzed'].max()).bit_length()
|
|
67
|
+
crossingsWidest = int(dataframe['crossings'].max()).bit_length()
|
|
68
|
+
elif array is not None:
|
|
69
|
+
arcCodeWidest = int(array[state.slicerArcCode].max()).bit_length()
|
|
70
|
+
crossingsWidest = int(array[state.slicerCrossings].max()).bit_length()
|
|
71
|
+
elif not state.dictionaryMeanders:
|
|
72
|
+
arcCodeWidest = int(state.arrayMeanders[state.slicerArcCode].max()).bit_length()
|
|
73
|
+
crossingsWidest = int(state.arrayMeanders[state.slicerCrossings].max()).bit_length()
|
|
74
|
+
else:
|
|
75
|
+
arcCodeWidest: int = max(state.dictionaryMeanders.keys()).bit_length()
|
|
76
|
+
crossingsWidest: int = max(state.dictionaryMeanders.values()).bit_length()
|
|
77
|
+
|
|
78
|
+
MAXIMUMarcCode: int = 0
|
|
79
|
+
if fixedSizeMAXIMUMarcCode:
|
|
80
|
+
MAXIMUMarcCode = state.MAXIMUMarcCode
|
|
81
|
+
|
|
82
|
+
return (arcCodeWidest > raiseIfNone(state.bitWidthLimitArcCode)
|
|
83
|
+
or crossingsWidest > raiseIfNone(state.bitWidthLimitCrossings)
|
|
84
|
+
or MAXIMUMarcCode > raiseIfNone(state.bitWidthLimitArcCode)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
@cache
|
|
88
|
+
def _flipTheExtra_0b1(intWithExtra_0b1: numpy.uint64) -> numpy.uint64:
|
|
89
|
+
return numpy.uint64(intWithExtra_0b1 ^ walkDyckPath(int(intWithExtra_0b1)))
|
|
90
|
+
|
|
91
|
+
flipTheExtra_0b1AsUfunc = numpy.frompyfunc(_flipTheExtra_0b1, 1, 1)
|
|
92
|
+
"""Flip a bit based on Dyck path: element-wise ufunc (*u*niversal *func*tion) for a NumPy `ndarray` (*Num*erical *Py*thon *n-d*imensional array).
|
|
93
|
+
|
|
94
|
+
Warning
|
|
95
|
+
-------
|
|
96
|
+
The function will loop infinitely if an element does not have a bit that needs flipping.
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
arrayTarget : numpy.ndarray[tuple[int], numpy.dtype[numpy.unsignedinteger[Any]]]
|
|
101
|
+
An array with one axis of unsigned integers and unbalanced closures.
|
|
102
|
+
|
|
103
|
+
Returns
|
|
104
|
+
-------
|
|
105
|
+
arrayFlipped : numpy.ndarray[tuple[int], numpy.dtype[numpy.unsignedinteger[Any]]]
|
|
106
|
+
An array with the same shape as `arrayTarget` but with one bit flipped in each element.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
def getBucketsTotal(state: MatrixMeandersNumPyState, safetyMultiplicand: float = 1.2) -> int:
|
|
110
|
+
"""Estimate the total number of non-unique arcCode that will be computed from the existing arcCode.
|
|
111
|
+
|
|
112
|
+
Notes
|
|
113
|
+
-----
|
|
114
|
+
TODO remake this function from scratch.
|
|
115
|
+
|
|
116
|
+
Factors:
|
|
117
|
+
- The starting quantity of `arcCode`.
|
|
118
|
+
- The value(s) of the starting `arcCode`.
|
|
119
|
+
- n
|
|
120
|
+
- k
|
|
121
|
+
- Whether this bucketsTotal is increasing, as compared to all of the prior bucketsTotal.
|
|
122
|
+
- If increasing, is it exponential or logarithmic?
|
|
123
|
+
- The maximum value.
|
|
124
|
+
- If decreasing, I don't really know the factors.
|
|
125
|
+
- If I know the actual value or if I must estimate it.
|
|
126
|
+
|
|
127
|
+
Figure out an intelligent flow for so many factors.
|
|
128
|
+
"""
|
|
129
|
+
theDictionary: dict[str, dict[int, dict[int, int]]] = {'A005316': A005316_n_k_buckets, 'A000682': A000682_n_k_buckets}
|
|
130
|
+
bucketsTotal: int = theDictionary.get(state.oeisID, {}).get(state.n, {}).get(state.kOfMatrix, -8)
|
|
131
|
+
if bucketsTotal > 0:
|
|
132
|
+
return bucketsTotal
|
|
133
|
+
|
|
134
|
+
dictionaryExponentialCoefficients: dict[ImaKey, float] = {
|
|
135
|
+
(ImaKey(oeisID='', kIsOdd=False, nLess_kIsOdd=True)): 0.834,
|
|
136
|
+
(ImaKey(oeisID='', kIsOdd=False, nLess_kIsOdd=False)): 1.5803,
|
|
137
|
+
(ImaKey(oeisID='', kIsOdd=True, nLess_kIsOdd=True)): 1.556,
|
|
138
|
+
(ImaKey(oeisID='', kIsOdd=True, nLess_kIsOdd=False)): 1.8047,
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
logarithmicOffsets: dict[ImaKey, float] ={
|
|
142
|
+
(ImaKey('A000682', kIsOdd=False, nLess_kIsOdd=False)): 0.0,
|
|
143
|
+
|
|
144
|
+
(ImaKey('A000682', kIsOdd=False, nLess_kIsOdd=True)): -0.07302547148212568,
|
|
145
|
+
(ImaKey('A000682', kIsOdd=True, nLess_kIsOdd=False)): -0.00595307513938792,
|
|
146
|
+
(ImaKey('A000682', kIsOdd=True, nLess_kIsOdd=True)): -0.012201222865243722,
|
|
147
|
+
(ImaKey('A005316', kIsOdd=False, nLess_kIsOdd=False)): -0.6392728422078733,
|
|
148
|
+
(ImaKey('A005316', kIsOdd=False, nLess_kIsOdd=True)): -0.6904925299923548,
|
|
149
|
+
(ImaKey('A005316', kIsOdd=True, nLess_kIsOdd=False)): 0.0,
|
|
150
|
+
(ImaKey('A005316', kIsOdd=True, nLess_kIsOdd=True)): 0.0,
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
logarithmicParameters: dict[str, float] = {
|
|
154
|
+
'intercept': -166.1750299793178,
|
|
155
|
+
'log(n)': 1259.0051001675547,
|
|
156
|
+
'log(k)': -396.4306071056408,
|
|
157
|
+
'log(nLess_k)': -854.3309503739766,
|
|
158
|
+
'k/n': 716.530410654819,
|
|
159
|
+
'(k/n)^2': -2527.035113444166,
|
|
160
|
+
'normalized k': -882.7054406339189,
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
bucketsTotalMaximumBy_kOfMatrix: dict[int, int] = {1:3, 2:12, 3:40, 4:125, 5:392, 6:1254, 7:4087, 8:13623, 9:46181, 10:159137, 11:555469, 12:1961369, 13:6991893, 14:25134208}
|
|
164
|
+
|
|
165
|
+
xCommon = 1.57
|
|
166
|
+
|
|
167
|
+
nLess_k: int = state.n - state.kOfMatrix
|
|
168
|
+
kIsOdd: bool = bool(state.kOfMatrix & 1)
|
|
169
|
+
nLess_kIsOdd: bool = bool(nLess_k & 1)
|
|
170
|
+
kIsEven: bool = not kIsOdd
|
|
171
|
+
|
|
172
|
+
bucketsTotalAtMaximum: bool = state.kOfMatrix <= ((state.n - 1 - (state.kOfMatrix % 2)) // 3)
|
|
173
|
+
bucketsTotalGrowsExponentially: bool = state.kOfMatrix > nLess_k
|
|
174
|
+
bucketsTotalGrowsLogarithmically: bool = state.kOfMatrix > ((state.n - (state.n % 3)) // 3)
|
|
175
|
+
|
|
176
|
+
if bucketsTotalAtMaximum:
|
|
177
|
+
if state.kOfMatrix in bucketsTotalMaximumBy_kOfMatrix:
|
|
178
|
+
bucketsTotal = bucketsTotalMaximumBy_kOfMatrix[state.kOfMatrix]
|
|
179
|
+
else:
|
|
180
|
+
c = 0.95037
|
|
181
|
+
r = 3.3591258254
|
|
182
|
+
if kIsOdd:
|
|
183
|
+
c = 0.92444
|
|
184
|
+
r = 3.35776
|
|
185
|
+
bucketsTotal = int(c * r**state.kOfMatrix * safetyMultiplicand)
|
|
186
|
+
|
|
187
|
+
elif state.kOfMatrix <= max(bucketsTotalMaximumBy_kOfMatrix.keys()):
|
|
188
|
+
# If `kOfMatrix` is low, use maximum bucketsTotal. 1. Can't underestimate. 2. Skip computation that can underestimate.
|
|
189
|
+
# 3. The potential difference in memory use is relatively small.
|
|
190
|
+
bucketsTotal = bucketsTotalMaximumBy_kOfMatrix[state.kOfMatrix]
|
|
191
|
+
|
|
192
|
+
elif bucketsTotalGrowsExponentially:
|
|
193
|
+
if (state.oeisID == 'A005316') and kIsOdd and (nLess_k in bucketsIf_k_ODD_by_nLess_k):
|
|
194
|
+
# If I already know bucketsTotal.
|
|
195
|
+
bucketsTotal = bucketsIf_k_ODD_by_nLess_k[nLess_k]
|
|
196
|
+
elif (state.oeisID == 'A005316') and kIsEven and (nLess_k in bucketsIf_k_EVEN_by_nLess_k):
|
|
197
|
+
# If I already know bucketsTotal.
|
|
198
|
+
bucketsTotal = bucketsIf_k_EVEN_by_nLess_k[nLess_k]
|
|
199
|
+
else: # I estimate bucketsTotal during exponential growth.
|
|
200
|
+
xInstant: int = math.ceil(nLess_k / 2)
|
|
201
|
+
A000682adjustStartingArcCode: float = 0.25
|
|
202
|
+
startingConditionsCoefficient: float = dictionaryExponentialCoefficients[ImaKey('', kIsOdd, nLess_kIsOdd)]
|
|
203
|
+
if kIsOdd and nLess_kIsOdd:
|
|
204
|
+
A000682adjustStartingArcCode = 0.0
|
|
205
|
+
if state.oeisID == 'A000682': # NOTE Net effect is between `*= n` and `*= n * 2.2` if n=46.
|
|
206
|
+
startingConditionsCoefficient *= state.n * (((state.n // 2) + 2) ** A000682adjustStartingArcCode)
|
|
207
|
+
bucketsTotal = int(startingConditionsCoefficient * math.exp(xCommon * xInstant))
|
|
208
|
+
|
|
209
|
+
elif bucketsTotalGrowsLogarithmically:
|
|
210
|
+
xPower: float = (0
|
|
211
|
+
+ logarithmicParameters['intercept']
|
|
212
|
+
+ logarithmicParameters['log(n)'] * log(state.n)
|
|
213
|
+
+ logarithmicParameters['log(k)'] * log(state.kOfMatrix)
|
|
214
|
+
+ logarithmicParameters['log(nLess_k)'] * log(nLess_k)
|
|
215
|
+
+ logarithmicParameters['k/n'] * (state.kOfMatrix / state.n)
|
|
216
|
+
+ logarithmicParameters['(k/n)^2'] * (state.kOfMatrix / state.n)**2
|
|
217
|
+
+ logarithmicParameters['normalized k'] * nLess_k / state.n
|
|
218
|
+
+ logarithmicOffsets[ImaKey(state.oeisID, kIsOdd, nLess_kIsOdd)]
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
bucketsTotal = int(exp(xPower * safetyMultiplicand))
|
|
222
|
+
|
|
223
|
+
else:
|
|
224
|
+
message = "I shouldn't be here."
|
|
225
|
+
raise SystemError(message)
|
|
226
|
+
return bucketsTotal
|
|
227
|
+
|
|
228
|
+
@cache
|
|
229
|
+
def walkDyckPath(intWithExtra_0b1: int) -> int:
|
|
230
|
+
"""Find the bit position for flipping paired curve endpoints in meander transfer matrices.
|
|
231
|
+
|
|
232
|
+
Parameters
|
|
233
|
+
----------
|
|
234
|
+
intWithExtra_0b1 : int
|
|
235
|
+
Binary representation of curve locations with an extra bit encoding parity information.
|
|
236
|
+
|
|
237
|
+
Returns
|
|
238
|
+
-------
|
|
239
|
+
flipExtra_0b1_Here : int
|
|
240
|
+
Bit mask indicating the position where the balance condition fails, formatted as 2^(2k).
|
|
241
|
+
|
|
242
|
+
3L33T H@X0R
|
|
243
|
+
------------
|
|
244
|
+
Binary search for first negative balance in shifted bit pairs. Returns 2^(2k) mask for
|
|
245
|
+
bit position k where cumulative balance counter transitions from non-negative to negative.
|
|
246
|
+
|
|
247
|
+
Mathematics
|
|
248
|
+
-----------
|
|
249
|
+
Implements the Dyck path balance verification algorithm from Jensen's transfer matrix
|
|
250
|
+
enumeration. Computes the position where ∑(i=0 to k) (-1)^b_i < 0 for the first time,
|
|
251
|
+
where b_i are the bits of the input at positions 2i.
|
|
252
|
+
|
|
253
|
+
"""
|
|
254
|
+
findTheExtra_0b1: int = 0
|
|
255
|
+
flipExtra_0b1_Here: int = 1
|
|
256
|
+
while True:
|
|
257
|
+
flipExtra_0b1_Here <<= 2
|
|
258
|
+
if intWithExtra_0b1 & flipExtra_0b1_Here == 0:
|
|
259
|
+
findTheExtra_0b1 += 1
|
|
260
|
+
else:
|
|
261
|
+
findTheExtra_0b1 -= 1
|
|
262
|
+
if findTheExtra_0b1 < 0:
|
|
263
|
+
break
|
|
264
|
+
return flipExtra_0b1_Here
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
from gc import collect as goByeBye
|
|
2
|
+
from mapFolding import ShapeArray, ShapeSlicer
|
|
3
|
+
from mapFolding.algorithms.matrixMeandersBeDry import areIntegersWide, flipTheExtra_0b1AsUfunc, getBucketsTotal
|
|
4
|
+
from mapFolding.dataBaskets import MatrixMeandersNumPyState
|
|
5
|
+
from mapFolding.syntheticModules.meanders.bigInt import countBigInt
|
|
6
|
+
from numpy import (
|
|
7
|
+
bitwise_and, bitwise_left_shift, bitwise_or, bitwise_right_shift, bitwise_xor, greater, less_equal, multiply, subtract)
|
|
8
|
+
from numpy.typing import NDArray
|
|
9
|
+
from typing import Any, TYPE_CHECKING
|
|
10
|
+
import numpy
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from numpy.lib._arraysetops_impl import UniqueInverseResult
|
|
14
|
+
|
|
15
|
+
indicesPrepArea: int = 1
|
|
16
|
+
indexAnalysis = 0
|
|
17
|
+
slicerAnalysis: ShapeSlicer = ShapeSlicer(length=..., indices=indexAnalysis)
|
|
18
|
+
|
|
19
|
+
indicesAnalyzed: int = 2
|
|
20
|
+
indexArcCode, indexCrossings = range(indicesAnalyzed)
|
|
21
|
+
slicerArcCode: ShapeSlicer = ShapeSlicer(length=..., indices=indexArcCode)
|
|
22
|
+
slicerCrossings: ShapeSlicer = ShapeSlicer(length=..., indices=indexCrossings)
|
|
23
|
+
|
|
24
|
+
def countNumPy(state: MatrixMeandersNumPyState) -> MatrixMeandersNumPyState:
|
|
25
|
+
"""Count crossings with transfer matrix algorithm implemented in NumPy (*Num*erical *Py*thon).
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
state : MatrixMeandersState
|
|
30
|
+
The algorithm state.
|
|
31
|
+
|
|
32
|
+
Returns
|
|
33
|
+
-------
|
|
34
|
+
state : MatrixMeandersState
|
|
35
|
+
Updated state including `kOfMatrix` and `arrayMeanders`.
|
|
36
|
+
"""
|
|
37
|
+
while state.kOfMatrix > 0 and not areIntegersWide(state):
|
|
38
|
+
def aggregateAnalyzed(arrayAnalyzed: NDArray[numpy.uint64], state: MatrixMeandersNumPyState) -> MatrixMeandersNumPyState:
|
|
39
|
+
"""Create new `arrayMeanders` by deduplicating `arcCode` and summing `crossings`."""
|
|
40
|
+
unique: UniqueInverseResult[numpy.uint64] = numpy.unique_inverse(arrayAnalyzed[slicerArcCode])
|
|
41
|
+
|
|
42
|
+
shape = ShapeArray(length=len(unique.values), indices=state.indicesMeanders)
|
|
43
|
+
state.arrayMeanders = numpy.zeros(shape, dtype=state.datatypeArcCode)
|
|
44
|
+
del shape
|
|
45
|
+
|
|
46
|
+
state.arrayMeanders[state.slicerArcCode] = unique.values
|
|
47
|
+
numpy.add.at(state.arrayMeanders[state.slicerCrossings], unique.inverse_indices, arrayAnalyzed[slicerCrossings])
|
|
48
|
+
del unique
|
|
49
|
+
|
|
50
|
+
return state
|
|
51
|
+
|
|
52
|
+
def bitsAlpha(state: MatrixMeandersNumPyState) -> NDArray[numpy.uint64]:
|
|
53
|
+
return bitwise_and(state.arrayMeanders[state.slicerArcCode], state.locatorBits)
|
|
54
|
+
|
|
55
|
+
def bitsZulu(state: MatrixMeandersNumPyState) -> NDArray[numpy.uint64]:
|
|
56
|
+
IsThisMemoryEfficient: NDArray[numpy.uint64] = bitwise_right_shift(state.arrayMeanders[state.slicerArcCode], 1)
|
|
57
|
+
return bitwise_and(IsThisMemoryEfficient, state.locatorBits)
|
|
58
|
+
|
|
59
|
+
def makeStorage[个: numpy.integer[Any]](dataTarget: NDArray[个], state: MatrixMeandersNumPyState, storageTarget: NDArray[numpy.uint64], indexAssignment: int = indexArcCode) -> NDArray[个]:
|
|
60
|
+
"""Store `dataTarget` in `storageTarget` on `indexAssignment` if there is enough space, otherwise allocate a new array."""
|
|
61
|
+
lengthStorageTarget: int = len(storageTarget)
|
|
62
|
+
storageAvailable: int = lengthStorageTarget - state.indexTarget
|
|
63
|
+
lengthDataTarget: int = len(dataTarget)
|
|
64
|
+
|
|
65
|
+
if storageAvailable >= lengthDataTarget:
|
|
66
|
+
indexStart: int = lengthStorageTarget - lengthDataTarget
|
|
67
|
+
sliceStorage: slice = slice(indexStart, lengthStorageTarget)
|
|
68
|
+
del indexStart
|
|
69
|
+
slicerStorageAtIndex: ShapeSlicer = ShapeSlicer(length=sliceStorage, indices=indexAssignment)
|
|
70
|
+
del sliceStorage
|
|
71
|
+
storageTarget[slicerStorageAtIndex] = dataTarget.copy()
|
|
72
|
+
arrayStorage = storageTarget[slicerStorageAtIndex].view() # pyright: ignore[reportAssignmentType]
|
|
73
|
+
del slicerStorageAtIndex
|
|
74
|
+
else:
|
|
75
|
+
arrayStorage: NDArray[个] = dataTarget.copy()
|
|
76
|
+
|
|
77
|
+
del storageAvailable, lengthDataTarget, lengthStorageTarget
|
|
78
|
+
|
|
79
|
+
return arrayStorage
|
|
80
|
+
|
|
81
|
+
def recordAnalysis(arrayAnalyzed: NDArray[numpy.uint64], state: MatrixMeandersNumPyState, arcCode: NDArray[numpy.uint64]) -> MatrixMeandersNumPyState:
|
|
82
|
+
"""Record valid `arcCode` and corresponding `crossings` in `arrayAnalyzed`."""
|
|
83
|
+
selectorOverLimit = arcCode > state.MAXIMUMarcCode
|
|
84
|
+
arcCode[selectorOverLimit] = 0
|
|
85
|
+
del selectorOverLimit
|
|
86
|
+
|
|
87
|
+
selectorAnalysis: NDArray[numpy.intp] = numpy.flatnonzero(arcCode)
|
|
88
|
+
|
|
89
|
+
indexStop: int = state.indexTarget + len(selectorAnalysis)
|
|
90
|
+
sliceNonzero: slice = slice(state.indexTarget, indexStop)
|
|
91
|
+
state.indexTarget = indexStop
|
|
92
|
+
del indexStop
|
|
93
|
+
|
|
94
|
+
slicerArcCodeNonzero = ShapeSlicer(length=sliceNonzero, indices=indexArcCode)
|
|
95
|
+
slicerCrossingsNonzero = ShapeSlicer(length=sliceNonzero, indices=indexCrossings)
|
|
96
|
+
del sliceNonzero
|
|
97
|
+
|
|
98
|
+
arrayAnalyzed[slicerArcCodeNonzero] = arcCode[selectorAnalysis]
|
|
99
|
+
del slicerArcCodeNonzero
|
|
100
|
+
|
|
101
|
+
arrayAnalyzed[slicerCrossingsNonzero] = state.arrayMeanders[state.slicerCrossings][selectorAnalysis]
|
|
102
|
+
del slicerCrossingsNonzero, selectorAnalysis
|
|
103
|
+
|
|
104
|
+
return state
|
|
105
|
+
|
|
106
|
+
# TODO bitwidth should be automatic.
|
|
107
|
+
state.bitWidth = int(state.arrayMeanders[state.slicerArcCode].max()).bit_length()
|
|
108
|
+
|
|
109
|
+
lengthArrayAnalyzed: int = getBucketsTotal(state, 1.2)
|
|
110
|
+
shape = ShapeArray(length=lengthArrayAnalyzed, indices=indicesAnalyzed)
|
|
111
|
+
del lengthArrayAnalyzed
|
|
112
|
+
goByeBye()
|
|
113
|
+
|
|
114
|
+
arrayAnalyzed: NDArray[numpy.uint64] = numpy.zeros(shape, dtype=state.datatypeArcCode)
|
|
115
|
+
del shape
|
|
116
|
+
|
|
117
|
+
shape = ShapeArray(length=len(state.arrayMeanders[state.slicerArcCode]), indices=indicesPrepArea)
|
|
118
|
+
arrayPrepArea: NDArray[numpy.uint64] = numpy.zeros(shape, dtype=state.datatypeArcCode)
|
|
119
|
+
del shape
|
|
120
|
+
|
|
121
|
+
prepArea: NDArray[numpy.uint64] = arrayPrepArea[slicerAnalysis].view()
|
|
122
|
+
|
|
123
|
+
state.indexTarget = 0
|
|
124
|
+
|
|
125
|
+
state.kOfMatrix -= 1
|
|
126
|
+
|
|
127
|
+
# =============== analyze aligned ===== if bitsAlpha > 1 and bitsZulu > 1 =============================================
|
|
128
|
+
# ======= > * > bitsAlpha 1 bitsZulu 1 ====================
|
|
129
|
+
greater(bitsAlpha(state), 1, out=prepArea)
|
|
130
|
+
multiply(bitsZulu(state), prepArea, out=prepArea)
|
|
131
|
+
greater(prepArea, 1, out=prepArea)
|
|
132
|
+
selectorGreaterThan1: NDArray[numpy.uint64] = makeStorage(prepArea, state, arrayAnalyzed, indexArcCode)
|
|
133
|
+
|
|
134
|
+
# ======= if bitsAlphaAtEven and not bitsZuluAtEven =======
|
|
135
|
+
# ======= ^ & | ^ & bitsZulu 1 1 bitsAlpha 1 1 ============
|
|
136
|
+
bitwise_and(bitsZulu(state), 1, out=prepArea)
|
|
137
|
+
bitwise_xor(prepArea, 1, out=prepArea)
|
|
138
|
+
bitwise_or(bitsAlpha(state), prepArea, out=prepArea)
|
|
139
|
+
bitwise_and(prepArea, 1, out=prepArea)
|
|
140
|
+
bitwise_xor(prepArea, 1, out=prepArea)
|
|
141
|
+
|
|
142
|
+
bitwise_and(selectorGreaterThan1, prepArea, out=prepArea)
|
|
143
|
+
selectorAlignAlpha: NDArray[numpy.intp] = makeStorage(numpy.flatnonzero(prepArea), state, arrayAnalyzed, indexCrossings)
|
|
144
|
+
|
|
145
|
+
arrayBitsAlpha: NDArray[numpy.uint64] = bitsAlpha(state)
|
|
146
|
+
|
|
147
|
+
arrayBitsAlpha[selectorAlignAlpha] = flipTheExtra_0b1AsUfunc(arrayBitsAlpha[selectorAlignAlpha])
|
|
148
|
+
del selectorAlignAlpha # NOTE FREE indexCrossings
|
|
149
|
+
|
|
150
|
+
# ======= if bitsZuluAtEven and not bitsAlphaAtEven =======
|
|
151
|
+
# ======= ^ & | ^ & bitsAlpha 1 1 bitsZulu 1 1 ============
|
|
152
|
+
bitwise_and(bitsAlpha(state), 1, out=prepArea)
|
|
153
|
+
bitwise_xor(prepArea, 1, out=prepArea)
|
|
154
|
+
bitwise_or(bitsZulu(state), prepArea, out=prepArea)
|
|
155
|
+
bitwise_and(prepArea, 1, out=prepArea)
|
|
156
|
+
bitwise_xor(prepArea, 1, out=prepArea)
|
|
157
|
+
|
|
158
|
+
bitwise_and(selectorGreaterThan1, prepArea, out=prepArea)
|
|
159
|
+
selectorAlignZulu: NDArray[numpy.intp] = makeStorage(numpy.flatnonzero(prepArea), state, arrayAnalyzed, indexCrossings)
|
|
160
|
+
|
|
161
|
+
# ======= bitsAlphaAtEven or bitsZuluAtEven ===============
|
|
162
|
+
# ======= ^ & & bitsAlpha 1 bitsZulu 1 ====================
|
|
163
|
+
bitwise_and(bitsAlpha(state), 1, out=prepArea)
|
|
164
|
+
bitwise_and(bitsZulu(state), prepArea, out=prepArea)
|
|
165
|
+
bitwise_xor(prepArea, 1, out=prepArea)
|
|
166
|
+
|
|
167
|
+
bitwise_and(selectorGreaterThan1, prepArea, out=prepArea) # selectorBitsAtEven
|
|
168
|
+
del selectorGreaterThan1 # NOTE FREE indexArcCode
|
|
169
|
+
bitwise_xor(prepArea, 1, out=prepArea)
|
|
170
|
+
selectorDisqualified: NDArray[numpy.intp] = makeStorage(numpy.flatnonzero(prepArea), state, arrayAnalyzed, indexArcCode)
|
|
171
|
+
|
|
172
|
+
prepArea = bitsZulu(state)
|
|
173
|
+
|
|
174
|
+
prepArea[selectorAlignZulu] = flipTheExtra_0b1AsUfunc(prepArea[selectorAlignZulu])
|
|
175
|
+
del selectorAlignZulu # NOTE FREE indexCrossings
|
|
176
|
+
|
|
177
|
+
arrayBitsZulu: NDArray[numpy.uint64] = makeStorage(prepArea, state, arrayAnalyzed, indexCrossings)
|
|
178
|
+
|
|
179
|
+
# ======= (((bitsZulu >> 2) << 3) | bitsAlpha) >> 2 =======
|
|
180
|
+
# ======= >> | << >> bitsZulu 2 3 bitsAlpha 2 =============
|
|
181
|
+
bitwise_right_shift(arrayBitsZulu, 2, out=prepArea)
|
|
182
|
+
del arrayBitsZulu # NOTE FREE indexCrossings
|
|
183
|
+
bitwise_left_shift(prepArea, 3, out=prepArea)
|
|
184
|
+
bitwise_or(arrayBitsAlpha, prepArea, out=prepArea)
|
|
185
|
+
del arrayBitsAlpha
|
|
186
|
+
bitwise_right_shift(prepArea, 2, out=prepArea)
|
|
187
|
+
|
|
188
|
+
prepArea[selectorDisqualified] = 0
|
|
189
|
+
del selectorDisqualified # NOTE FREE indexArcCode
|
|
190
|
+
|
|
191
|
+
state = recordAnalysis(arrayAnalyzed, state, prepArea)
|
|
192
|
+
|
|
193
|
+
# ----------------- analyze bitsAlpha ------- (bitsAlpha >> 2) | (bitsZulu << 3) | ((1 - (bitsAlpha & 1)) << 1) ---------
|
|
194
|
+
# ------- >> | << | (<< - 1 & bitsAlpha 1 1) << bitsZulu 3 2 bitsAlpha 2 ----------
|
|
195
|
+
bitsAlphaStack: NDArray[numpy.uint64] = makeStorage(bitsAlpha(state), state, arrayAnalyzed, indexCrossings)
|
|
196
|
+
bitwise_and(bitsAlphaStack, 1, out=bitsAlphaStack)
|
|
197
|
+
subtract(1, bitsAlphaStack, out=bitsAlphaStack)
|
|
198
|
+
bitwise_left_shift(bitsAlphaStack, 1, out=bitsAlphaStack)
|
|
199
|
+
bitwise_left_shift(bitsZulu(state), 3, out=prepArea)
|
|
200
|
+
bitwise_or(bitsAlphaStack, prepArea, out=prepArea)
|
|
201
|
+
del bitsAlphaStack # NOTE FREE indexCrossings
|
|
202
|
+
bitwise_left_shift(prepArea, 2, out=prepArea)
|
|
203
|
+
bitwise_or(bitsAlpha(state), prepArea, out=prepArea)
|
|
204
|
+
bitwise_right_shift(prepArea, 2, out=prepArea)
|
|
205
|
+
|
|
206
|
+
# ------- if bitsAlpha > 1 ------------ > bitsAlpha 1 -----
|
|
207
|
+
bitsAlphaStack: NDArray[numpy.uint64] = makeStorage(bitsAlpha(state), state, arrayAnalyzed, indexCrossings)
|
|
208
|
+
less_equal(bitsAlphaStack, 1, out=bitsAlphaStack)
|
|
209
|
+
selectorUnderLimit: NDArray[numpy.intp] = makeStorage(numpy.flatnonzero(bitsAlphaStack), state, arrayAnalyzed, indexArcCode)
|
|
210
|
+
prepArea[selectorUnderLimit] = 0
|
|
211
|
+
del selectorUnderLimit # NOTE FREE indexArcCode
|
|
212
|
+
|
|
213
|
+
state = recordAnalysis(arrayAnalyzed, state, prepArea)
|
|
214
|
+
|
|
215
|
+
# ----------------- analyze bitsZulu ---------- (bitsZulu >> 1) | (bitsAlpha << 2) | (1 - (bitsZulu & 1)) -------------
|
|
216
|
+
# ------- >> | << | (- 1 & bitsZulu 1) << bitsAlpha 2 1 bitsZulu 1 ----------
|
|
217
|
+
bitsZuluStack: NDArray[numpy.uint64] = makeStorage(bitsZulu(state), state, arrayAnalyzed, indexCrossings)
|
|
218
|
+
bitwise_and(bitsZuluStack, 1, out=bitsZuluStack)
|
|
219
|
+
subtract(1, bitsZuluStack, out=bitsZuluStack)
|
|
220
|
+
bitwise_left_shift(bitsAlpha(state), 2, out=prepArea)
|
|
221
|
+
bitwise_or(bitsZuluStack, prepArea, out=prepArea)
|
|
222
|
+
del bitsZuluStack # NOTE FREE indexCrossings
|
|
223
|
+
bitwise_left_shift(prepArea, 1, out=prepArea)
|
|
224
|
+
bitwise_or(bitsZulu(state), prepArea, out=prepArea)
|
|
225
|
+
bitwise_right_shift(prepArea, 1, out=prepArea)
|
|
226
|
+
|
|
227
|
+
# ------- if bitsZulu > 1 ------------- > bitsZulu 1 ------
|
|
228
|
+
bitsZuluStack: NDArray[numpy.uint64] = makeStorage(bitsZulu(state), state, arrayAnalyzed, indexCrossings)
|
|
229
|
+
less_equal(bitsZuluStack, 1, out=bitsZuluStack)
|
|
230
|
+
selectorUnderLimit = makeStorage(numpy.flatnonzero(bitsZuluStack), state, arrayAnalyzed, indexArcCode)
|
|
231
|
+
del bitsZuluStack # NOTE FREE indexCrossings
|
|
232
|
+
prepArea[selectorUnderLimit] = 0
|
|
233
|
+
del selectorUnderLimit # NOTE FREE indexArcCode
|
|
234
|
+
|
|
235
|
+
state = recordAnalysis(arrayAnalyzed, state, prepArea)
|
|
236
|
+
|
|
237
|
+
# ----------------- analyze simple ------------------------ ((bitsAlpha | (bitsZulu << 1)) << 2) | 3 ------------------
|
|
238
|
+
# ------- | << | bitsAlpha << bitsZulu 1 2 3 --------------
|
|
239
|
+
bitwise_left_shift(bitsZulu(state), 1, out=prepArea)
|
|
240
|
+
bitwise_or(bitsAlpha(state), prepArea, out=prepArea)
|
|
241
|
+
bitwise_left_shift(prepArea, 2, out=prepArea)
|
|
242
|
+
bitwise_or(prepArea, 3, out=prepArea)
|
|
243
|
+
|
|
244
|
+
state = recordAnalysis(arrayAnalyzed, state, prepArea)
|
|
245
|
+
|
|
246
|
+
del prepArea, arrayPrepArea
|
|
247
|
+
# ----------------------------------------------- aggregation ---------------------------------------------------------
|
|
248
|
+
arrayAnalyzed.resize((state.indexTarget, indicesAnalyzed))
|
|
249
|
+
|
|
250
|
+
goByeBye()
|
|
251
|
+
state = aggregateAnalyzed(arrayAnalyzed, state)
|
|
252
|
+
|
|
253
|
+
del arrayAnalyzed
|
|
254
|
+
|
|
255
|
+
return state
|
|
256
|
+
|
|
257
|
+
def doTheNeedful(state: MatrixMeandersNumPyState) -> int:
|
|
258
|
+
"""Compute `crossings` with a transfer matrix algorithm implemented in NumPy.
|
|
259
|
+
|
|
260
|
+
Parameters
|
|
261
|
+
----------
|
|
262
|
+
state : MatrixMeandersState
|
|
263
|
+
The algorithm state.
|
|
264
|
+
|
|
265
|
+
Returns
|
|
266
|
+
-------
|
|
267
|
+
crossings : int
|
|
268
|
+
The computed value of `crossings`.
|
|
269
|
+
|
|
270
|
+
Notes
|
|
271
|
+
-----
|
|
272
|
+
Citation: https://github.com/hunterhogan/mapFolding/blob/main/citations/Jensen.bibtex
|
|
273
|
+
|
|
274
|
+
See Also
|
|
275
|
+
--------
|
|
276
|
+
https://oeis.org/A000682
|
|
277
|
+
https://oeis.org/A005316
|
|
278
|
+
"""
|
|
279
|
+
while state.kOfMatrix > 0:
|
|
280
|
+
if areIntegersWide(state):
|
|
281
|
+
state = countBigInt(state)
|
|
282
|
+
else:
|
|
283
|
+
state.makeArray()
|
|
284
|
+
state = countNumPy(state)
|
|
285
|
+
state.makeDictionary()
|
|
286
|
+
return sum(state.dictionaryMeanders.values())
|