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.
Files changed (83) hide show
  1. easyRun/A000682.py +25 -0
  2. easyRun/A005316.py +21 -0
  3. easyRun/NOTcountingFolds.py +36 -0
  4. easyRun/__init__.py +0 -0
  5. easyRun/countFolds.py +41 -0
  6. easyRun/meanders.py +71 -0
  7. mapFolding/__init__.py +10 -55
  8. mapFolding/_dataPacking.py +68 -0
  9. mapFolding/_theSSOT.py +33 -36
  10. mapFolding/_theTypes.py +21 -4
  11. mapFolding/algorithms/daoOfMapFolding.py +1 -2
  12. mapFolding/algorithms/matrixMeanders.py +101 -348
  13. mapFolding/algorithms/matrixMeandersBeDry.py +264 -0
  14. mapFolding/algorithms/matrixMeandersNumPy.py +286 -0
  15. mapFolding/algorithms/matrixMeandersPandas.py +351 -0
  16. mapFolding/algorithms/oeisIDbyFormula.py +320 -76
  17. mapFolding/algorithms/zCuzDocStoopidoeisIDbyFormula.py +92 -0
  18. mapFolding/basecamp.py +261 -113
  19. mapFolding/beDRY.py +2 -30
  20. mapFolding/dataBaskets.py +120 -4
  21. mapFolding/oeis.py +13 -33
  22. mapFolding/reference/A000682facts.py +1276 -0
  23. mapFolding/reference/A005316facts.py +985 -0
  24. mapFolding/reference/matrixMeandersAnalysis/__init__.py +1 -0
  25. mapFolding/reference/matrixMeandersAnalysis/prefixNotationNotes.py +15 -0
  26. mapFolding/reference/meandersDumpingGround/A005316JavaPort.py +1 -1
  27. mapFolding/reference/meandersDumpingGround/A005316imperative.py +1 -1
  28. mapFolding/reference/meandersDumpingGround/matrixMeandersNumPyV1finalForm.py +424 -0
  29. mapFolding/someAssemblyRequired/A007822/A007822rawMaterials.py +54 -0
  30. mapFolding/someAssemblyRequired/A007822/__init__.py +0 -0
  31. mapFolding/someAssemblyRequired/A007822/makeA007822AsynchronousModules.py +197 -0
  32. mapFolding/someAssemblyRequired/A007822/makeA007822Modules.py +74 -0
  33. mapFolding/someAssemblyRequired/RecipeJob.py +4 -4
  34. mapFolding/someAssemblyRequired/__init__.py +9 -2
  35. mapFolding/someAssemblyRequired/_toolIfThis.py +4 -3
  36. mapFolding/someAssemblyRequired/_toolkitContainers.py +8 -8
  37. mapFolding/someAssemblyRequired/infoBooth.py +27 -30
  38. mapFolding/someAssemblyRequired/makeJobTheorem2Numba.py +6 -5
  39. mapFolding/someAssemblyRequired/makeJobTheorem2codon.py +6 -4
  40. mapFolding/someAssemblyRequired/makingModules_count.py +294 -0
  41. mapFolding/someAssemblyRequired/makingModules_doTheNeedful.py +117 -0
  42. mapFolding/someAssemblyRequired/mapFolding/__init__.py +0 -0
  43. mapFolding/someAssemblyRequired/mapFolding/makeMapFoldingModules.py +220 -0
  44. mapFolding/someAssemblyRequired/meanders/__init__.py +0 -0
  45. mapFolding/someAssemblyRequired/meanders/makeMeandersModules.py +64 -0
  46. mapFolding/someAssemblyRequired/toolkitMakeModules.py +152 -0
  47. mapFolding/someAssemblyRequired/toolkitNumba.py +1 -1
  48. mapFolding/someAssemblyRequired/transformationTools.py +1 -0
  49. mapFolding/syntheticModules/A007822/__init__.py +1 -0
  50. mapFolding/syntheticModules/{algorithmA007822.py → A007822/algorithm.py} +2 -3
  51. mapFolding/syntheticModules/{algorithmA007822Numba.py → A007822/algorithmNumba.py} +3 -6
  52. mapFolding/syntheticModules/A007822/asynchronous.py +148 -0
  53. mapFolding/syntheticModules/A007822/asynchronousAnnex.py +66 -0
  54. mapFolding/syntheticModules/A007822/asynchronousAnnexNumba.py +85 -0
  55. mapFolding/syntheticModules/A007822/asynchronousNumba.py +52 -0
  56. mapFolding/syntheticModules/A007822/asynchronousTheorem2.py +53 -0
  57. mapFolding/syntheticModules/A007822/asynchronousTrimmed.py +47 -0
  58. mapFolding/syntheticModules/{initializeStateA007822.py → A007822/initializeState.py} +1 -2
  59. mapFolding/syntheticModules/{theorem2A007822.py → A007822/theorem2.py} +1 -2
  60. mapFolding/syntheticModules/{theorem2A007822Numba.py → A007822/theorem2Numba.py} +6 -4
  61. mapFolding/syntheticModules/{theorem2A007822Trimmed.py → A007822/theorem2Trimmed.py} +1 -2
  62. mapFolding/syntheticModules/countParallelNumba.py +5 -2
  63. mapFolding/syntheticModules/daoOfMapFoldingNumba.py +4 -2
  64. mapFolding/syntheticModules/dataPacking.py +4 -2
  65. mapFolding/syntheticModules/dataPackingA007822.py +92 -26
  66. mapFolding/syntheticModules/meanders/__init__.py +1 -0
  67. mapFolding/syntheticModules/meanders/bigInt.py +62 -0
  68. mapFolding/syntheticModules/theorem2Numba.py +3 -2
  69. mapFolding/tests/conftest.py +28 -13
  70. mapFolding/tests/test_computations.py +69 -62
  71. mapFolding/tests/test_oeis.py +6 -6
  72. mapFolding/zCuzDocStoopid/__init__.py +4 -0
  73. mapFolding/zCuzDocStoopid/makeDocstrings.py +68 -0
  74. mapfolding-0.16.1.dist-info/METADATA +99 -0
  75. mapfolding-0.16.1.dist-info/RECORD +114 -0
  76. {mapfolding-0.15.4.dist-info → mapfolding-0.16.1.dist-info}/top_level.txt +1 -0
  77. mapFolding/someAssemblyRequired/A007822rawMaterials.py +0 -46
  78. mapFolding/someAssemblyRequired/makeAllModules.py +0 -764
  79. mapfolding-0.15.4.dist-info/METADATA +0 -78
  80. mapfolding-0.15.4.dist-info/RECORD +0 -78
  81. {mapfolding-0.15.4.dist-info → mapfolding-0.16.1.dist-info}/WHEEL +0 -0
  82. {mapfolding-0.15.4.dist-info → mapfolding-0.16.1.dist-info}/entry_points.txt +0 -0
  83. {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())