mapFolding 0.16.2__py3-none-any.whl → 0.17.0__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 (80) hide show
  1. easyRun/A000682.py +2 -2
  2. easyRun/NOTcountingFolds.py +16 -8
  3. easyRun/countFolds.py +9 -2
  4. easyRun/generateAllModules.py +14 -0
  5. easyRun/meanders.py +4 -4
  6. mapFolding/__init__.py +1 -0
  7. mapFolding/_theSSOT.py +3 -2
  8. mapFolding/_theTypes.py +3 -0
  9. mapFolding/algorithms/A000136constraintPropagation.py +95 -0
  10. mapFolding/algorithms/A000136elimination.py +163 -0
  11. mapFolding/algorithms/A000136eliminationParallel.py +77 -0
  12. mapFolding/algorithms/A086345.py +75 -0
  13. mapFolding/algorithms/matrixMeanders.py +59 -18
  14. mapFolding/algorithms/matrixMeandersNumPyndas.py +841 -0
  15. mapFolding/algorithms/oeisIDbyFormula.py +2 -2
  16. mapFolding/algorithms/symmetricFolds.py +35 -0
  17. mapFolding/basecamp.py +100 -153
  18. mapFolding/dataBaskets.py +142 -65
  19. mapFolding/filesystemToolkit.py +4 -32
  20. mapFolding/oeis.py +5 -12
  21. mapFolding/reference/A086345Wu.py +25 -0
  22. mapFolding/reference/irvineJavaPort.py +3 -3
  23. mapFolding/reference/matrixMeandersAnalysis/signatures.py +3 -0
  24. mapFolding/reference/meandersDumpingGround/matrixMeandersNumPyV1finalForm.py +1 -1
  25. mapFolding/someAssemblyRequired/A007822/A007822rawMaterials.py +10 -45
  26. mapFolding/someAssemblyRequired/A007822/_asynchronousAnnex.py +51 -0
  27. mapFolding/someAssemblyRequired/A007822/makeA007822AsynchronousModules.py +39 -196
  28. mapFolding/someAssemblyRequired/A007822/makeA007822Modules.py +57 -43
  29. mapFolding/someAssemblyRequired/RecipeJob.py +84 -34
  30. mapFolding/someAssemblyRequired/__init__.py +4 -8
  31. mapFolding/someAssemblyRequired/_toolkitContainers.py +38 -7
  32. mapFolding/someAssemblyRequired/infoBooth.py +41 -23
  33. mapFolding/someAssemblyRequired/makeJobTheorem2Numba.py +140 -164
  34. mapFolding/someAssemblyRequired/makeJobTheorem2codon.py +63 -96
  35. mapFolding/someAssemblyRequired/makingModules_count.py +26 -30
  36. mapFolding/someAssemblyRequired/makingModules_doTheNeedful.py +10 -72
  37. mapFolding/someAssemblyRequired/{mapFolding → mapFoldingModules}/makeMapFoldingModules.py +30 -35
  38. mapFolding/someAssemblyRequired/meanders/makeMeandersModules.py +13 -11
  39. mapFolding/someAssemblyRequired/toolkitMakeModules.py +5 -31
  40. mapFolding/someAssemblyRequired/toolkitNumba.py +3 -2
  41. mapFolding/someAssemblyRequired/transformationTools.py +12 -15
  42. mapFolding/syntheticModules/A007822/algorithm.py +45 -50
  43. mapFolding/syntheticModules/A007822/asynchronous.py +92 -36
  44. mapFolding/syntheticModules/A007822/initializeState.py +19 -23
  45. mapFolding/syntheticModules/A007822/theorem2.py +20 -24
  46. mapFolding/syntheticModules/A007822/theorem2Numba.py +23 -25
  47. mapFolding/syntheticModules/A007822/theorem2Trimmed.py +19 -23
  48. mapFolding/syntheticModules/countParallelNumba.py +1 -2
  49. mapFolding/syntheticModules/daoOfMapFoldingNumba.py +5 -4
  50. mapFolding/syntheticModules/initializeState.py +1 -1
  51. mapFolding/syntheticModules/meanders/bigInt.py +59 -22
  52. mapFolding/syntheticModules/theorem2.py +1 -1
  53. mapFolding/syntheticModules/theorem2Numba.py +30 -9
  54. mapFolding/syntheticModules/theorem2Trimmed.py +2 -2
  55. mapFolding/tests/test_computations.py +29 -3
  56. {mapfolding-0.16.2.dist-info → mapfolding-0.17.0.dist-info}/METADATA +11 -8
  57. mapfolding-0.17.0.dist-info/RECORD +107 -0
  58. mapFolding/_dataPacking.py +0 -68
  59. mapFolding/algorithms/matrixMeandersBeDry.py +0 -182
  60. mapFolding/algorithms/matrixMeandersNumPy.py +0 -333
  61. mapFolding/algorithms/matrixMeandersPandas.py +0 -334
  62. mapFolding/reference/meandersDumpingGround/A005316intOptimized.py +0 -122
  63. mapFolding/reference/meandersDumpingGround/A005316optimized128bit.py +0 -79
  64. mapFolding/reference/meandersDumpingGround/matrixMeandersBaseline.py +0 -65
  65. mapFolding/reference/meandersDumpingGround/matrixMeandersBaselineAnnex.py +0 -84
  66. mapFolding/reference/meandersDumpingGround/matrixMeandersSimpleQueue.py +0 -90
  67. mapFolding/syntheticModules/A007822/algorithmNumba.py +0 -94
  68. mapFolding/syntheticModules/A007822/asynchronousAnnex.py +0 -66
  69. mapFolding/syntheticModules/A007822/asynchronousAnnexNumba.py +0 -70
  70. mapFolding/syntheticModules/A007822/asynchronousNumba.py +0 -79
  71. mapFolding/syntheticModules/A007822/asynchronousTheorem2.py +0 -65
  72. mapFolding/syntheticModules/A007822/asynchronousTrimmed.py +0 -56
  73. mapFolding/syntheticModules/dataPacking.py +0 -26
  74. mapFolding/syntheticModules/dataPackingA007822.py +0 -92
  75. mapfolding-0.16.2.dist-info/RECORD +0 -115
  76. /mapFolding/someAssemblyRequired/{mapFolding → mapFoldingModules}/__init__.py +0 -0
  77. {mapfolding-0.16.2.dist-info → mapfolding-0.17.0.dist-info}/WHEEL +0 -0
  78. {mapfolding-0.16.2.dist-info → mapfolding-0.17.0.dist-info}/entry_points.txt +0 -0
  79. {mapfolding-0.16.2.dist-info → mapfolding-0.17.0.dist-info}/licenses/LICENSE +0 -0
  80. {mapfolding-0.16.2.dist-info → mapfolding-0.17.0.dist-info}/top_level.txt +0 -0
mapFolding/dataBaskets.py CHANGED
@@ -23,10 +23,7 @@ access patterns that enable efficient result persistence and retrieval.
23
23
  from mapFolding import (
24
24
  Array1DElephino, Array1DLeavesTotal, Array3DLeavesTotal, DatatypeElephino, DatatypeFoldsTotal, DatatypeLeavesTotal,
25
25
  getConnectionGraph, getLeavesTotal, makeDataContainer)
26
- from numpy.typing import NDArray
27
- from typing import TypeAlias
28
26
  import dataclasses
29
- import numpy
30
27
 
31
28
  @dataclasses.dataclass(slots=True)
32
29
  class MapFoldingState:
@@ -111,8 +108,6 @@ class MapFoldingState:
111
108
  """Array tracking the leaves above to the current leaf, `leaf1ndex`, during computation."""
112
109
  leafBelow: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
113
110
  """Array tracking the leaves below to the current leaf, `leaf1ndex`, during computation."""
114
- leafComparison: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
115
- """Array for finding symmetric folds."""
116
111
 
117
112
  connectionGraph: Array3DLeavesTotal = dataclasses.field(init=False, metadata={'dtype': Array3DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
118
113
  """Unchanging array representing connections between all leaves."""
@@ -126,7 +121,7 @@ class MapFoldingState:
126
121
 
127
122
  Returns
128
123
  -------
129
- totalFoldings : DatatypeFoldsTotal
124
+ foldsTotal : DatatypeFoldsTotal
130
125
  The complete count of distinct folding patterns achievable with the current map configuration.
131
126
 
132
127
  """
@@ -154,6 +149,123 @@ class MapFoldingState:
154
149
  if self.gapRangeStart is None: self.gapRangeStart = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['gapRangeStart'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison] # noqa: E701
155
150
  if self.leafAbove is None: self.leafAbove = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafAbove'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison] # noqa: E701
156
151
  if self.leafBelow is None: self.leafBelow = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafBelow'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison] # noqa: E701
152
+
153
+ @dataclasses.dataclass(slots=True)
154
+ class SymmetricFoldsState:
155
+ """Core computational state for symmetric map folding algorithms.
156
+
157
+ Attributes
158
+ ----------
159
+ mapShape : tuple[DatatypeLeavesTotal, ...]
160
+ Dimensions of the map being analyzed for folding patterns.
161
+ groupsOfFolds : DatatypeFoldsTotal = DatatypeFoldsTotal(0)
162
+ Current count of distinct folding pattern groups: each group has `leavesTotal`-many foldings.
163
+ gap1ndex : DatatypeElephino = DatatypeElephino(0)
164
+ The current 1-indexed position of the 'gap' during computation: 1-indexed as opposed to 0-indexed.
165
+ gap1ndexCeiling : DatatypeElephino = DatatypeElephino(0)
166
+ The upper bound of `gap1ndex`.
167
+ indexDimension : DatatypeLeavesTotal = DatatypeLeavesTotal(0)
168
+ The current 0-indexed position of the dimension during computation.
169
+ indexLeaf : DatatypeLeavesTotal = DatatypeLeavesTotal(0)
170
+ The current 0-indexed position of a leaf in a loop during computation: not to be confused with `leaf1ndex`.
171
+ indexMiniGap : DatatypeElephino = DatatypeElephino(0)
172
+ The current 0-indexed position of a 'gap' in a loop during computation.
173
+ leaf1ndex : DatatypeLeavesTotal = DatatypeLeavesTotal(1)
174
+ The current 1-indexed position of the leaf during computation: 1-indexed as opposed to 0-indexed.
175
+ leafConnectee : DatatypeLeavesTotal = DatatypeLeavesTotal(0)
176
+ Target leaf for connection operations.
177
+ dimensionsUnconstrained : DatatypeLeavesTotal = None
178
+ Count of dimensions not subject to folding constraints.
179
+ countDimensionsGapped : Array1DLeavesTotal = None
180
+ Array tracking computed number of dimensions with gaps.
181
+ gapRangeStart : Array1DElephino = None
182
+ Array tracking computed starting positions of gap ranges.
183
+ gapsWhere : Array1DLeavesTotal = None
184
+ Array indicating locations of gaps in the folding pattern.
185
+ leafAbove : Array1DLeavesTotal = None
186
+ Array tracking the leaves above to the current leaf, `leaf1ndex`, during computation.
187
+ leafBelow : Array1DLeavesTotal = None
188
+ Array tracking the leaves below to the current leaf, `leaf1ndex`, during computation.
189
+ leafComparison : Array1DLeavesTotal = None
190
+ Array for finding symmetric folds.
191
+ connectionGraph : Array3DLeavesTotal
192
+ Unchanging array representing connections between all leaves.
193
+ dimensionsTotal : DatatypeLeavesTotal
194
+ Unchanging total number of dimensions in the map.
195
+ leavesTotal : DatatypeLeavesTotal
196
+ Unchanging total number of leaves in the map.
197
+
198
+ """
199
+
200
+ mapShape: tuple[DatatypeLeavesTotal, ...] = dataclasses.field(init=True, metadata={'elementConstructor': 'DatatypeLeavesTotal'})
201
+ """Dimensions of the map being analyzed for folding patterns."""
202
+
203
+ symmetricFolds: DatatypeFoldsTotal = dataclasses.field(default=DatatypeFoldsTotal(0), metadata={'theCountingIdentifier': True})
204
+ """Current count of symmetric folds."""
205
+
206
+ gap1ndex: DatatypeElephino = DatatypeElephino(0) # noqa: RUF009
207
+ """The current 1-indexed position of the 'gap' during computation: 1-indexed as opposed to 0-indexed."""
208
+ gap1ndexCeiling: DatatypeElephino = DatatypeElephino(0) # noqa: RUF009
209
+ """The upper bound of `gap1ndex`."""
210
+ indexDimension: DatatypeLeavesTotal = DatatypeLeavesTotal(0) # noqa: RUF009
211
+ """The current 0-indexed position of the dimension during computation."""
212
+ indexLeaf: DatatypeLeavesTotal = DatatypeLeavesTotal(0) # noqa: RUF009
213
+ """The current 0-indexed position of a leaf in a loop during computation: not to be confused with `leaf1ndex`."""
214
+ indexMiniGap: DatatypeElephino = DatatypeElephino(0) # noqa: RUF009
215
+ """The current 0-indexed position of a 'gap' in a loop during computation."""
216
+ leaf1ndex: DatatypeLeavesTotal = DatatypeLeavesTotal(1) # noqa: RUF009
217
+ """The current 1-indexed position of the leaf during computation: 1-indexed as opposed to 0-indexed."""
218
+ leafConnectee: DatatypeLeavesTotal = DatatypeLeavesTotal(0) # noqa: RUF009
219
+ """Target leaf for connection operations."""
220
+
221
+ dimensionsUnconstrained: DatatypeLeavesTotal = dataclasses.field(default=None, init=True) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
222
+ """Count of dimensions not subject to folding constraints."""
223
+
224
+ countDimensionsGapped: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
225
+ """Array tracking computed number of dimensions with gaps."""
226
+ gapRangeStart: Array1DElephino = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DElephino.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
227
+ """Array tracking computed starting positions of gap ranges."""
228
+ gapsWhere: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
229
+ """Array indicating locations of gaps in the folding pattern."""
230
+ leafAbove: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
231
+ """Array tracking the leaves above to the current leaf, `leaf1ndex`, during computation."""
232
+ leafBelow: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
233
+ """Array tracking the leaves below to the current leaf, `leaf1ndex`, during computation."""
234
+ leafComparison: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
235
+ """Array for finding symmetric folds."""
236
+
237
+ connectionGraph: Array3DLeavesTotal = dataclasses.field(init=False, metadata={'dtype': Array3DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
238
+ """Unchanging array representing connections between all leaves."""
239
+ dimensionsTotal: DatatypeLeavesTotal = dataclasses.field(init=False)
240
+ """Unchanging total number of dimensions in the map."""
241
+ indices: list[list[tuple[int, int]]] = dataclasses.field(init=False)
242
+ """Precomputed index pairs for symmetric fold checking."""
243
+ leavesTotal: DatatypeLeavesTotal = dataclasses.field(init=False)
244
+ """Unchanging total number of leaves in the map."""
245
+
246
+ def __post_init__(self) -> None:
247
+ """Ensure all fields have a value.
248
+
249
+ Notes
250
+ -----
251
+ Arrays that are not explicitly provided (None) are automatically allocated with appropriate sizes based on the map
252
+ dimensions. `dimensionsTotal`, `leavesTotal`, and `connectionGraph` cannot be set: they are calculated.
253
+
254
+ """
255
+ self.dimensionsTotal = DatatypeLeavesTotal(len(self.mapShape))
256
+ self.leavesTotal = DatatypeLeavesTotal(getLeavesTotal(self.mapShape))
257
+
258
+ leavesTotalAsInt = int(self.leavesTotal)
259
+ self.connectionGraph = getConnectionGraph(self.mapShape, leavesTotalAsInt, self.__dataclass_fields__['connectionGraph'].metadata['dtype'])
260
+
261
+ 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)]
262
+
263
+ if self.dimensionsUnconstrained is None: self.dimensionsUnconstrained = DatatypeLeavesTotal(int(self.dimensionsTotal)) # pyright: ignore[reportUnnecessaryComparison] # noqa: E701
264
+ if self.gapsWhere is None: self.gapsWhere = makeDataContainer(leavesTotalAsInt * leavesTotalAsInt + 1, self.__dataclass_fields__['gapsWhere'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison] # noqa: E701
265
+ if self.countDimensionsGapped is None: self.countDimensionsGapped = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['countDimensionsGapped'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison] # noqa: E701
266
+ if self.gapRangeStart is None: self.gapRangeStart = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['gapRangeStart'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison] # noqa: E701
267
+ if self.leafAbove is None: self.leafAbove = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafAbove'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison] # noqa: E701
268
+ if self.leafBelow is None: self.leafBelow = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafBelow'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison] # noqa: E701
157
269
  if self.leafComparison is None: self.leafComparison = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafComparison'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison] # noqa: E701
158
270
 
159
271
  @dataclasses.dataclass
@@ -280,6 +392,7 @@ class MatrixMeandersState:
280
392
  """The index of the meanders problem being solved."""
281
393
  oeisID: str
282
394
  """'A000682', semi-meanders, or 'A005316', meanders."""
395
+
283
396
  boundary: int
284
397
  """The algorithm analyzes `n` boundaries starting at `boundary = n - 1`."""
285
398
  dictionaryMeanders: dict[int, int]
@@ -290,14 +403,19 @@ class MatrixMeandersState:
290
403
  bitWidth: int = 0
291
404
  """At the start of an iteration enumerated by `boundary`, the number of bits of the largest value `arcCode`. The
292
405
  `dataclass` computes a `property` from `bitWidth`."""
293
-
294
- @property
295
- def MAXIMUMarcCode(self) -> int:
296
- """Compute the maximum value of `arcCode` for the current iteration of the transfer matrix."""
297
- return 1 << (2 * self.boundary + 4)
298
-
299
- @property
300
- def locatorBits(self) -> int:
406
+ bitsLocator: int = 0
407
+ """An odd-parity bit-mask with `bitWidth` bits."""
408
+ MAXIMUMarcCode: int = 0
409
+ """The maximum value of `arcCode` for the current iteration of the transfer matrix."""
410
+
411
+ def reduceBoundary(self) -> None:
412
+ """Prepare for the next iteration of the transfer matrix algorithm by reducing `boundary` by 1 and updating related fields."""
413
+ self.boundary -= 1
414
+ self.setBitWidth()
415
+ self.setBitsLocator()
416
+ self.setMAXIMUMarcCode()
417
+
418
+ def setBitsLocator(self) -> None:
301
419
  """Compute an odd-parity bit-mask with `bitWidth` bits.
302
420
 
303
421
  Notes
@@ -311,60 +429,19 @@ class MatrixMeandersState:
311
429
  odd numbers from even numbers, so I avoid using "odd" and "even" in the names of these bit-masks.
312
430
 
313
431
  """
314
- return sum(1 << one for one in range(0, self.bitWidth, 2))
315
-
316
- @dataclasses.dataclass(slots=True)
317
- class MatrixMeandersNumPyState(MatrixMeandersState):
318
- """Hold the state of a meanders transfer matrix algorithm computation."""
319
-
320
- arrayArcCodes: NDArray[numpy.uint64] = dataclasses.field(default_factory=lambda: numpy.empty((0,), dtype=numpy.uint64))
321
- arrayCrossings: NDArray[numpy.uint64] = dataclasses.field(default_factory=lambda: numpy.empty((0,), dtype=numpy.uint64))
432
+ self.bitsLocator = sum(1 << one for one in range(0, self.bitWidth, 2))
322
433
 
323
- bitWidthLimitArcCode: int | None = None
324
- bitWidthLimitCrossings: int | None = None
434
+ def setBitWidth(self) -> None:
435
+ """Set `bitWidth` from the current `dictionaryMeanders`."""
436
+ self.bitWidth = max(self.dictionaryMeanders.keys()).bit_length()
325
437
 
326
- datatypeArcCode: TypeAlias = numpy.uint64 # noqa: UP040
327
- """The fixed-size integer type used to store `arcCode`."""
328
- datatypeCrossings: TypeAlias = numpy.uint64 # noqa: UP040
329
- """The fixed-size integer type used to store `crossings`."""
330
-
331
- indexTarget: int = 0
332
- """What is being indexed depends on the algorithm flavor."""
438
+ def setMAXIMUMarcCode(self) -> None:
439
+ """Compute the maximum value of `arcCode` for the current iteration of the transfer matrix."""
440
+ self.MAXIMUMarcCode = 1 << (2 * self.boundary + 4)
333
441
 
334
442
  def __post_init__(self) -> None:
335
443
  """Post init."""
336
- if self.bitWidthLimitArcCode is None:
337
- _bitWidthOfFixedSizeInteger: int = numpy.dtype(self.datatypeArcCode).itemsize * 8 # bits
338
-
339
- _offsetNecessary: int = 3 # For example, `bitsZulu << 3`.
340
- _offsetSafety: int = 1 # I don't have mathematical proof of how many extra bits I need.
341
- _offset: int = _offsetNecessary + _offsetSafety
342
-
343
- self.bitWidthLimitArcCode = _bitWidthOfFixedSizeInteger - _offset
344
-
345
- del _bitWidthOfFixedSizeInteger, _offsetNecessary, _offsetSafety, _offset
346
-
347
- if self.bitWidthLimitCrossings is None:
348
- _bitWidthOfFixedSizeInteger: int = numpy.dtype(self.datatypeCrossings).itemsize * 8 # bits
349
-
350
- _offsetNecessary: int = 0 # I don't know of any.
351
- _offsetEstimation: int = 3 # See reference directory.
352
- _offsetSafety: int = 1
353
- _offset: int = _offsetNecessary + _offsetEstimation + _offsetSafety
354
-
355
- self.bitWidthLimitCrossings = _bitWidthOfFixedSizeInteger - _offset
356
-
357
- del _bitWidthOfFixedSizeInteger, _offsetNecessary, _offsetEstimation, _offsetSafety, _offset
358
-
359
- def makeDictionary(self) -> None:
360
- """Convert from NumPy `ndarray` (*Num*erical *Py*thon *n-d*imensional array) to Python `dict` (*dict*ionary)."""
361
- self.dictionaryMeanders = {int(key): int(value) for key, value in zip(self.arrayArcCodes, self.arrayCrossings, strict=True)}
362
- self.arrayArcCodes = numpy.empty((0,), dtype=self.datatypeArcCode)
363
- self.arrayCrossings = numpy.empty((0,), dtype=self.datatypeCrossings)
444
+ self.setBitWidth()
445
+ self.setBitsLocator()
446
+ self.setMAXIMUMarcCode()
364
447
 
365
- def makeArray(self) -> None:
366
- """Convert from Python `dict` (*dict*ionary) to NumPy `ndarray` (*Num*erical *Py*thon *n-d*imensional array)."""
367
- self.arrayArcCodes = numpy.array(list(self.dictionaryMeanders.keys()), dtype=self.datatypeArcCode)
368
- self.arrayCrossings = numpy.array(list(self.dictionaryMeanders.values()), dtype=self.datatypeCrossings)
369
- self.bitWidth = int(self.arrayArcCodes.max()).bit_length()
370
- self.dictionaryMeanders = {}
@@ -56,12 +56,10 @@ def getFilenameFoldsTotal(mapShape: tuple[int, ...]) -> str:
56
56
  return 'p' + 'x'.join(str(dimension) for dimension in sorted(mapShape)) + '.foldsTotal'
57
57
 
58
58
  def getPathFilenameFoldsTotal(mapShape: tuple[int, ...], pathLikeWriteFoldsTotal: PathLike[str] | PurePath | None = None) -> Path:
59
- """Get a standardized path and filename for the computed `foldsTotal` value.
59
+ """Get a standardized filename and create a configurable path to store the computed `foldsTotal` value.
60
60
 
61
- (AI generated docstring)
62
-
63
- This function resolves paths for storing computation results, handling different input types including directories,
64
- absolute paths, or relative paths. It ensures that all parent directories exist in the resulting path.
61
+ To help reduce duplicate code and to increase predictability, this function creates a standardized filename, has a default but
62
+ configurable path, and creates the path.
65
63
 
66
64
  Parameters
67
65
  ----------
@@ -78,10 +76,9 @@ def getPathFilenameFoldsTotal(mapShape: tuple[int, ...], pathLikeWriteFoldsTotal
78
76
  Notes
79
77
  -----
80
78
  The function creates any necessary directories in the path if they don't exist.
81
-
82
79
  """
83
80
  if pathLikeWriteFoldsTotal is None:
84
- pathFilenameFoldsTotal = getPathRootJobDEFAULT() / getFilenameFoldsTotal(mapShape)
81
+ pathFilenameFoldsTotal: Path = getPathRootJobDEFAULT() / getFilenameFoldsTotal(mapShape)
85
82
  else:
86
83
  pathLikeSherpa = Path(pathLikeWriteFoldsTotal)
87
84
  if pathLikeSherpa.is_dir():
@@ -237,28 +234,3 @@ def saveFoldsTotalFAILearly(pathFilename: PathLike[str] | PurePath) -> None:
237
234
  if foldsTotalRead != foldsTotal:
238
235
  message = f"I wrote a test file to `{pathFilename = }` with contents of `{str(foldsTotal) = }`, but I read `{foldsTotalRead = }` from the file. Python says the values are not equal. Fix that now, so your computation doesn't get corrupted later. And be pro-social."
239
236
  raise FileNotFoundError(message)
240
-
241
- def writeStringToHere(this: str, pathFilename: PathLike[str] | PurePath) -> None:
242
- """Write a string to a file, creating parent directories if needed.
243
-
244
- (AI generated docstring)
245
-
246
- This utility function provides a consistent interface for writing string content to files across the package. It
247
- handles path creation and ensures proper string conversion.
248
-
249
- Parameters
250
- ----------
251
- this : str
252
- The string content to write to the file.
253
- pathFilename : PathLike[str] | PurePath
254
- The target file path where the string should be written.
255
-
256
- Notes
257
- -----
258
- This function creates all parent directories in the path if they don't exist, making it safe to use with newly
259
- created directory structures.
260
-
261
- """
262
- pathFilename = Path(pathFilename)
263
- pathFilename.parent.mkdir(parents=True, exist_ok=True)
264
- pathFilename.write_text(str(this))
mapFolding/oeis.py CHANGED
@@ -306,12 +306,6 @@ def makeDictionaryFoldsTotalKnown() -> dict[tuple[int, ...], int]:
306
306
  def getFoldsTotalKnown(mapShape: tuple[int, ...]) -> int:
307
307
  """Retrieve the known total number of distinct folding patterns for a given map shape.
308
308
 
309
- (AI generated docstring)
310
-
311
- This function provides rapid access to precalculated folding totals from OEIS sequences without
312
- requiring computation. It serves as a validation reference for algorithm results and enables
313
- quick lookup of known values across all implemented sequences.
314
-
315
309
  Parameters
316
310
  ----------
317
311
  mapShape : tuple[int, ...]
@@ -320,17 +314,16 @@ def getFoldsTotalKnown(mapShape: tuple[int, ...]) -> int:
320
314
  Returns
321
315
  -------
322
316
  foldingsTotal : int
323
- The known total number of distinct folding patterns for the given map shape,
324
- or -1 if the map shape does not match any known values in the OEIS sequences.
317
+ The known total number of distinct folding patterns for the given map shape, or 0 if the map shape does not match any
318
+ known values in the OEIS sequences.
325
319
 
326
320
  Notes
327
321
  -----
328
- The function uses a cached dictionary for efficient retrieval without repeatedly processing
329
- OEIS data. Map shapes are matched exactly as provided without internal sorting or normalization.
322
+ Map shapes are matched exactly as provided without internal sorting or normalization.
330
323
 
331
324
  """
332
- lookupFoldsTotal = makeDictionaryFoldsTotalKnown()
333
- return lookupFoldsTotal.get(tuple(mapShape), -1)
325
+ lookupFoldsTotal: dict[tuple[int, ...], int] = makeDictionaryFoldsTotalKnown()
326
+ return lookupFoldsTotal.get(tuple(mapShape), 0)
334
327
 
335
328
  def _formatHelpText() -> str:
336
329
  """Format comprehensive help text for both command-line and interactive use.
@@ -0,0 +1,25 @@
1
+ # ruff: noqa: ANN001, T201, D103, D100, ANN201, ANN202
2
+ # pyright: standard
3
+ from fractions import Fraction
4
+ from functools import cache
5
+ from itertools import combinations
6
+ from math import factorial, gcd, prod
7
+ from sympy import divisors
8
+ from sympy.functions.combinatorial.numbers import mobius
9
+ from sympy.utilities.iterables import partitions
10
+ import time
11
+
12
+ # NOTE Because `b` and `c` are inside `A086345` instead of peers, the run time is increased by a factor of 6.
13
+
14
+ def A086345(n):
15
+ @cache
16
+ def b(n): return int(sum(Fraction(3**(sum(p[r]*p[s]*gcd(r, s) for r, s in combinations(p.keys(), 2))+sum((q-1>>1)*r+(q*r*(r-1)>>1) for q, r in p.items())), prod(q**r*factorial(r) for q, r in p.items())) for p in partitions(n))) # pyright: ignore[reportAttributeAccessIssue, reportOperatorIssue]
17
+ @cache
18
+ def c(n): return n*b(n)-sum(c(k)*b(n-k) for k in range(1, n))
19
+ return sum(mobius(d)*c(n//d) for d in divisors(n, generator=True))//n if n else 1 # Chai Wah Wu, Jul 15 2024
20
+
21
+ if __name__ == "__main__":
22
+ timeStart = time.perf_counter()
23
+ for n in range(51):
24
+ print(n, A086345(n))
25
+ print(time.perf_counter() - timeStart)
@@ -1,5 +1,5 @@
1
- """
2
- Ported from the Java version by Sean A. Irvine:
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.bibtex
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:
@@ -1,3 +1,6 @@
1
+ """buckets and signatures."""
2
+ bucketsTotalMaximumBy_boundary: 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}
3
+
1
4
  signatures: dict[str, dict[int, dict[int, int]]] = {'A000682': {2: {1: 1, 2: 3},
2
5
  3: {1: 1, 2: 3, 3: 3},
3
6
  4: {1: 1, 2: 3, 3: 4, 4: 4},
@@ -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.bibtex
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) =
@@ -1,54 +1,19 @@
1
- from astToolkit import extractFunctionDef, identifierDotAttribute, Make # noqa: D100
1
+ from astToolkit import extractFunctionDef, Make # noqa: D100
2
2
  from hunterMakesPy import raiseIfNone
3
- from mapFolding.someAssemblyRequired import (
4
- identifierCallableSourceDEFAULT, identifierCallableSourceDispatcherDEFAULT, identifierCountingDEFAULT,
5
- identifierDataclassInstanceDEFAULT, logicalPathInfixDEFAULT)
3
+ from mapFolding.someAssemblyRequired import defaultA007822
4
+ from mapFolding.someAssemblyRequired.toolkitMakeModules import getModule
6
5
  import ast
7
6
 
8
- identifierDataclass: str = identifierDataclassInstanceDEFAULT
9
- identifierCounting: str = identifierCountingDEFAULT
10
- logicalPathInfixA007822: identifierDotAttribute = logicalPathInfixDEFAULT + '.A007822'
11
- sourceCallableDispatcherA007822: str = identifierCallableSourceDispatcherDEFAULT
12
- sourceCallableIdentifierA007822: str = identifierCallableSourceDEFAULT
7
+ FunctionDef_filterAsymmetricFolds: ast.FunctionDef = raiseIfNone(extractFunctionDef(getModule(logicalPathInfix='algorithms', identifierModule='symmetricFolds'), defaultA007822['function']['filterAsymmetricFolds']))
13
8
 
14
- identifier_filterAsymmetricFolds = 'filterAsymmetricFolds'
15
-
16
- ImaString = f"""
17
- def {identifier_filterAsymmetricFolds}({identifierDataclass}: MapFoldingState) -> MapFoldingState:
18
- {identifierDataclass}.indexLeaf = 0
19
- leafConnectee = 0
20
- while leafConnectee < {identifierDataclass}.leavesTotal + 1:
21
- leafNumber = int({identifierDataclass}.leafBelow[{identifierDataclass}.indexLeaf])
22
- {identifierDataclass}.leafComparison[leafConnectee] = (leafNumber - {identifierDataclass}.indexLeaf + {identifierDataclass}.leavesTotal) % {identifierDataclass}.leavesTotal
23
- {identifierDataclass}.indexLeaf = leafNumber
24
- leafConnectee += 1
25
-
26
- indexInMiddle = {identifierDataclass}.leavesTotal // 2
27
- {identifierDataclass}.indexMiniGap = 0
28
- while {identifierDataclass}.indexMiniGap < {identifierDataclass}.leavesTotal + 1:
29
- ImaSymmetricFold = True
30
- leafConnectee = 0
31
- while leafConnectee < indexInMiddle:
32
- if {identifierDataclass}.leafComparison[({identifierDataclass}.indexMiniGap + leafConnectee) % ({identifierDataclass}.leavesTotal + 1)] != {identifierDataclass}.leafComparison[({identifierDataclass}.indexMiniGap + {identifierDataclass}.leavesTotal - 1 - leafConnectee) % ({identifierDataclass}.leavesTotal + 1)]:
33
- ImaSymmetricFold = False
34
- break
35
- leafConnectee += 1
36
- {identifierDataclass}.{identifierCounting} += ImaSymmetricFold
37
- {identifierDataclass}.indexMiniGap += 1
38
-
39
- return {identifierDataclass}
40
- """ # noqa: E501
41
-
42
- FunctionDef_filterAsymmetricFolds: ast.FunctionDef = raiseIfNone(extractFunctionDef(ast.parse(ImaString), identifier_filterAsymmetricFolds))
43
- del ImaString
44
-
45
- ImaString = f"{identifierDataclass} = {identifier_filterAsymmetricFolds}({identifierDataclass})"
9
+ ImaString: str = f"{defaultA007822['variable']['stateInstance']} = {defaultA007822['function']['filterAsymmetricFolds']}({defaultA007822['variable']['stateInstance']})"
46
10
  A007822incrementCount = ast.parse(ImaString).body[0]
47
11
  del ImaString
48
12
 
49
- ImaString = f'{identifierDataclass}.{identifierCounting} = ({identifierDataclass}.{identifierCounting} + 1) // 2'
50
- A007822adjustFoldsTotal = ast.parse(ImaString).body[0]
13
+ ImaString = f'{defaultA007822['variable']['stateInstance']}.{defaultA007822['variable']['counting']} = ({defaultA007822['variable']['stateInstance']}.{defaultA007822['variable']['counting']} + 1) // 2'
14
+ A007822adjustFoldsTotal: ast.stmt = ast.parse(ImaString).body[0]
51
15
  del ImaString
52
16
 
53
- astExprCall_filterAsymmetricFoldsDataclass: ast.Expr = Make.Expr(Make.Call(Make.Name(identifier_filterAsymmetricFolds), listParameters=[Make.Attribute(Make.Name(identifierDataclass), 'leafBelow')]))
54
- astExprCall_filterAsymmetricFoldsLeafBelow: ast.Expr = Make.Expr(Make.Call(Make.Name(identifier_filterAsymmetricFolds), listParameters=[Make.Name('leafBelow')]))
17
+ ExprCallFilterAsymmetricFolds_leafBelow: ast.Expr = Make.Expr(Make.Call(Make.Name(defaultA007822['function']['filterAsymmetricFolds']), listParameters=[Make.Name('leafBelow')]))
18
+ ExprCallFilterAsymmetricFoldsState: ast.Expr = Make.Expr(Make.Call(Make.Name(defaultA007822['function']['filterAsymmetricFolds']), listParameters=[Make.Name(defaultA007822['variable']['stateInstance'])]))
19
+ ExprCallFilterAsymmetricFoldsStateDot_leafBelow: ast.Expr = Make.Expr(Make.Call(Make.Name(defaultA007822['function']['filterAsymmetricFolds']), listParameters=[Make.Attribute(Make.Name(defaultA007822['variable']['stateInstance']), 'leafBelow')]))
@@ -0,0 +1,51 @@
1
+ # NOTE The real module is generated from this incomplete module. Comments are not preserved.
2
+ # ruff: noqa: PLW0603
3
+ from copy import deepcopy
4
+ from mapFolding import DatatypeFoldsTotal
5
+ from mapFolding.dataBaskets import SymmetricFoldsState
6
+ from queue import Queue
7
+ from threading import Lock, Thread
8
+
9
+ listThreads: list[Thread] = []
10
+ queueFutures: Queue[SymmetricFoldsState] = Queue()
11
+ symmetricFoldsTotal: int = 0
12
+ LOCKsymmetricFoldsTotal = Lock()
13
+ # TODO There isn't a better way to do this?
14
+ STOPsignal = object()
15
+
16
+ def initializeConcurrencyManager(maxWorkers: int, symmetricFolds: int=0) -> None:
17
+ global listThreads, symmetricFoldsTotal, queueFutures
18
+ listThreads = []
19
+ queueFutures = Queue()
20
+ symmetricFoldsTotal = symmetricFolds
21
+
22
+ indexThread = 0
23
+ while indexThread < maxWorkers:
24
+ thread = Thread(target=_threadDoesSomething, name=f"thread{indexThread}", daemon=True)
25
+ thread.start()
26
+ listThreads.append(thread)
27
+ indexThread += 1
28
+
29
+ def _threadDoesSomething() -> None:
30
+ global symmetricFoldsTotal
31
+ while True:
32
+ state = queueFutures.get()
33
+ if state is STOPsignal:
34
+ break
35
+ state = _filterAsymmetricFolds(state)
36
+ with LOCKsymmetricFoldsTotal:
37
+ symmetricFoldsTotal += state.symmetricFolds
38
+
39
+ def _filterAsymmetricFolds(state: SymmetricFoldsState) -> SymmetricFoldsState:
40
+ """Add real function during generation; the signature is here to preview its interactions with the module."""
41
+ return state
42
+
43
+ def filterAsymmetricFolds(state: SymmetricFoldsState) -> None:
44
+ queueFutures.put_nowait(deepcopy(state))
45
+
46
+ def getSymmetricFoldsTotal() -> DatatypeFoldsTotal:
47
+ for _thread in listThreads:
48
+ queueFutures.put(STOPsignal) # pyright: ignore[reportArgumentType]
49
+ for thread in listThreads:
50
+ thread.join()
51
+ return symmetricFoldsTotal