mapFolding 0.16.4__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 (53) hide show
  1. easyRun/A000682.py +2 -2
  2. easyRun/NOTcountingFolds.py +16 -9
  3. easyRun/countFolds.py +10 -3
  4. easyRun/meanders.py +3 -3
  5. mapFolding/algorithms/A000136constraintPropagation.py +95 -0
  6. mapFolding/algorithms/A000136elimination.py +163 -0
  7. mapFolding/algorithms/A000136eliminationParallel.py +77 -0
  8. mapFolding/algorithms/matrixMeanders.py +59 -18
  9. mapFolding/algorithms/matrixMeandersNumPyndas.py +841 -0
  10. mapFolding/algorithms/symmetricFolds.py +24 -25
  11. mapFolding/basecamp.py +30 -14
  12. mapFolding/dataBaskets.py +30 -71
  13. mapFolding/reference/irvineJavaPort.py +3 -3
  14. mapFolding/reference/meandersDumpingGround/matrixMeandersNumPyV1finalForm.py +1 -1
  15. mapFolding/someAssemblyRequired/A007822/_asynchronousAnnex.py +1 -1
  16. mapFolding/someAssemblyRequired/A007822/makeA007822AsynchronousModules.py +5 -3
  17. mapFolding/someAssemblyRequired/A007822/makeA007822Modules.py +22 -6
  18. mapFolding/someAssemblyRequired/RecipeJob.py +14 -24
  19. mapFolding/someAssemblyRequired/__init__.py +1 -0
  20. mapFolding/someAssemblyRequired/_toolkitContainers.py +6 -4
  21. mapFolding/someAssemblyRequired/infoBooth.py +2 -1
  22. mapFolding/someAssemblyRequired/makeJobTheorem2Numba.py +75 -20
  23. mapFolding/someAssemblyRequired/makeJobTheorem2codon.py +9 -10
  24. mapFolding/someAssemblyRequired/makingModules_count.py +20 -22
  25. mapFolding/someAssemblyRequired/makingModules_doTheNeedful.py +9 -9
  26. mapFolding/someAssemblyRequired/mapFoldingModules/makeMapFoldingModules.py +6 -5
  27. mapFolding/someAssemblyRequired/meanders/makeMeandersModules.py +6 -6
  28. mapFolding/someAssemblyRequired/toolkitMakeModules.py +3 -29
  29. mapFolding/someAssemblyRequired/toolkitNumba.py +2 -1
  30. mapFolding/someAssemblyRequired/transformationTools.py +2 -3
  31. mapFolding/syntheticModules/A007822/algorithm.py +8 -8
  32. mapFolding/syntheticModules/A007822/asynchronous.py +12 -13
  33. mapFolding/syntheticModules/A007822/initializeState.py +10 -8
  34. mapFolding/syntheticModules/A007822/theorem2.py +10 -8
  35. mapFolding/syntheticModules/A007822/theorem2Numba.py +20 -16
  36. mapFolding/syntheticModules/A007822/theorem2Trimmed.py +10 -8
  37. mapFolding/syntheticModules/countParallelNumba.py +5 -2
  38. mapFolding/syntheticModules/daoOfMapFoldingNumba.py +4 -2
  39. mapFolding/syntheticModules/initializeState.py +1 -1
  40. mapFolding/syntheticModules/meanders/bigInt.py +52 -15
  41. mapFolding/syntheticModules/theorem2.py +1 -1
  42. mapFolding/syntheticModules/theorem2Numba.py +4 -2
  43. mapFolding/syntheticModules/theorem2Trimmed.py +1 -1
  44. mapFolding/tests/test_computations.py +28 -2
  45. {mapfolding-0.16.4.dist-info → mapfolding-0.17.0.dist-info}/METADATA +9 -9
  46. {mapfolding-0.16.4.dist-info → mapfolding-0.17.0.dist-info}/RECORD +50 -49
  47. mapFolding/algorithms/matrixMeandersBeDry.py +0 -182
  48. mapFolding/algorithms/matrixMeandersNumPy.py +0 -333
  49. mapFolding/algorithms/matrixMeandersPandas.py +0 -334
  50. {mapfolding-0.16.4.dist-info → mapfolding-0.17.0.dist-info}/WHEEL +0 -0
  51. {mapfolding-0.16.4.dist-info → mapfolding-0.17.0.dist-info}/entry_points.txt +0 -0
  52. {mapfolding-0.16.4.dist-info → mapfolding-0.17.0.dist-info}/licenses/LICENSE +0 -0
  53. {mapfolding-0.16.4.dist-info → mapfolding-0.17.0.dist-info}/top_level.txt +0 -0
@@ -3,34 +3,33 @@
3
3
  Notes
4
4
  -----
5
5
  - About constructing `leafComparison`:
6
- - This branch of the algorithm executes IFF `leafBelow[0] == 1`.
7
- - Therefore, `leafComparison[0]` must be `1`.
8
- - Therefore, the first iteration of the loop is hardcoded to save processing time.
9
- - I _feel_ there must be a more efficient way to do this.
6
+ - The first iteration of the loop is hardcoded to save processing time.
7
+ - I _feel_ there must be a more efficient way to do this.
10
8
  - Some implementation details are based on Numba compatibility. Incompatible:
11
- - `numpy.take(..., out=...)`
12
- - `numpy.all(..., axis=...)`
9
+ - `numpy.take(..., out=...)`
10
+ - `numpy.all(..., axis=...)`
13
11
  """
14
12
  from mapFolding.dataBaskets import SymmetricFoldsState
15
- import numpy
16
13
 
17
14
  def filterAsymmetricFolds(state: SymmetricFoldsState) -> SymmetricFoldsState:
18
- state.indexLeaf = 1
19
- state.leafComparison[0] = 1
20
- state.leafConnectee = 1
21
-
22
- while state.leafConnectee < state.leavesTotal + 1:
23
- state.indexMiniGap = state.leafBelow[state.indexLeaf]
24
- state.leafComparison[state.leafConnectee] = (state.indexMiniGap - state.indexLeaf + state.leavesTotal) % state.leavesTotal
25
- state.indexLeaf = state.indexMiniGap
26
-
27
- state.leafConnectee += 1
28
-
29
- state.arrayGroupOfFolds = numpy.take(state.leafComparison, state.indicesArrayGroupOfFolds)
30
- compared = state.arrayGroupOfFolds[..., 0:state.leavesTotal // 2] == state.arrayGroupOfFolds[..., state.leavesTotal // 2:None]
31
-
32
- for indexRow in range(len(compared)):
33
- state.groupsOfFolds += compared[indexRow].all()
34
-
35
- return state
15
+ state.indexLeaf = 1
16
+ state.leafComparison[0] = 1
17
+ state.leafConnectee = 1
18
+
19
+ while state.leafConnectee < state.leavesTotal + 1:
20
+ state.indexMiniGap = state.leafBelow[state.indexLeaf]
21
+ state.leafComparison[state.leafConnectee] = (state.indexMiniGap - state.indexLeaf + state.leavesTotal) % state.leavesTotal
22
+ state.indexLeaf = state.indexMiniGap
23
+
24
+ state.leafConnectee += 1
25
+
26
+ for listTuples in state.indices:
27
+ state.leafConnectee = 1
28
+ for indexLeft, indexRight in listTuples:
29
+ if state.leafComparison[indexLeft] != state.leafComparison[indexRight]:
30
+ state.leafConnectee = 0
31
+ break
32
+ state.symmetricFolds += state.leafConnectee
33
+
34
+ return state
36
35
 
mapFolding/basecamp.py CHANGED
@@ -174,13 +174,15 @@ def countFolds(listDimensions: Sequence[int] | None = None
174
174
 
175
175
  # ruff: noqa: E701
176
176
  else:
177
- match flow:
178
- case 'daoOfMapFolding': from mapFolding.algorithms.daoOfMapFolding import doTheNeedful
179
- case 'numba': from mapFolding.syntheticModules.daoOfMapFoldingNumba import doTheNeedful
180
- case 'theorem2': from mapFolding.syntheticModules.theorem2 import doTheNeedful
181
- case 'theorem2Numba': from mapFolding.syntheticModules.theorem2Numba import doTheNeedful
182
- case 'theorem2Trimmed': from mapFolding.syntheticModules.theorem2Trimmed import doTheNeedful
183
- case _: from mapFolding.algorithms.daoOfMapFolding import doTheNeedful
177
+ if all(dimension < 3 for dimension in mapShape):
178
+ from mapFolding.algorithms.daoOfMapFolding import doTheNeedful
179
+ else:
180
+ match flow:
181
+ case 'numba': from mapFolding.syntheticModules.daoOfMapFoldingNumba import doTheNeedful
182
+ case 'theorem2': from mapFolding.syntheticModules.theorem2 import doTheNeedful
183
+ case 'theorem2Numba': from mapFolding.syntheticModules.theorem2Numba import doTheNeedful
184
+ case 'theorem2Trimmed': from mapFolding.syntheticModules.theorem2Trimmed import doTheNeedful
185
+ case 'daoOfMapFolding' | _: from mapFolding.algorithms.daoOfMapFolding import doTheNeedful
184
186
 
185
187
  from mapFolding.dataBaskets import MapFoldingState
186
188
  mapFoldingState: MapFoldingState = MapFoldingState(mapShape)
@@ -203,7 +205,6 @@ def NOTcountingFolds(oeisID: str, oeis_n: int, flow: str | None = None
203
205
  matched_oeisID: bool = True
204
206
 
205
207
  match oeisID:
206
- case 'A000136': from mapFolding.algorithms.oeisIDbyFormula import A000136 as doTheNeedful
207
208
  case 'A000560': from mapFolding.algorithms.oeisIDbyFormula import A000560 as doTheNeedful
208
209
  case 'A001010': from mapFolding.algorithms.oeisIDbyFormula import A001010 as doTheNeedful
209
210
  case 'A001011': from mapFolding.algorithms.oeisIDbyFormula import A001011 as doTheNeedful
@@ -222,15 +223,30 @@ def NOTcountingFolds(oeisID: str, oeis_n: int, flow: str | None = None
222
223
  else:
223
224
  matched_oeisID = True
224
225
  match oeisID:
226
+ case 'A000136':
227
+ from mapFolding import setProcessorLimit
228
+ concurrencyLimit: int = setProcessorLimit(CPUlimit)
229
+ match flow:
230
+ case 'elimination':
231
+ from mapFolding.algorithms.A000136elimination import doTheNeedful
232
+ countTotal = doTheNeedful(oeis_n)
233
+ case 'eliminationParallel':
234
+ from mapFolding.algorithms.A000136eliminationParallel import doTheNeedful
235
+ countTotal = doTheNeedful(oeis_n, concurrencyLimit)
236
+ case 'constraintPropagation':
237
+ from mapFolding.algorithms.A000136constraintPropagation import doTheNeedful
238
+ countTotal = doTheNeedful(oeis_n, concurrencyLimit)
239
+ case _:
240
+ from mapFolding.algorithms.oeisIDbyFormula import A000136 as doTheNeedful
241
+ countTotal = doTheNeedful(oeis_n)
225
242
  case 'A000682' | 'A005316':
226
243
  match flow:
227
244
  case 'matrixNumPy':
228
- from mapFolding.algorithms.matrixMeandersNumPy import doTheNeedful
229
- from mapFolding.dataBaskets import MatrixMeandersNumPyState as State
245
+ from mapFolding.algorithms.matrixMeandersNumPyndas import doTheNeedful, MatrixMeandersNumPyState as State
230
246
  case 'matrixPandas':
231
- from mapFolding.algorithms.matrixMeandersPandas import doTheNeedful
232
- from mapFolding.dataBaskets import MatrixMeandersNumPyState as State
233
- case _:
247
+ from mapFolding.algorithms.matrixMeandersNumPyndas import (
248
+ doTheNeedfulPandas as doTheNeedful, MatrixMeandersNumPyState as State)
249
+ case 'matrixMeanders' | _:
234
250
  from mapFolding.algorithms.matrixMeanders import doTheNeedful
235
251
  from mapFolding.dataBaskets import MatrixMeandersState as State
236
252
 
@@ -291,7 +307,7 @@ def NOTcountingFolds(oeisID: str, oeis_n: int, flow: str | None = None
291
307
  from mapFolding.syntheticModules.A007822.algorithm import doTheNeedful
292
308
  symmetricState = doTheNeedful(symmetricState)
293
309
 
294
- countTotal = symmetricState.groupsOfFolds
310
+ countTotal = symmetricState.symmetricFolds
295
311
  case _:
296
312
  matched_oeisID = False
297
313
 
mapFolding/dataBaskets.py CHANGED
@@ -21,12 +21,9 @@ integrity throughout the recursive analysis while providing the structured data
21
21
  access patterns that enable efficient result persistence and retrieval.
22
22
  """
23
23
  from mapFolding import (
24
- Array1DElephino, Array1DLeavesTotal, Array2DLeavesTotal, Array3DLeavesTotal, DatatypeElephino, DatatypeFoldsTotal,
25
- DatatypeLeavesTotal, getConnectionGraph, getLeavesTotal, makeDataContainer)
26
- from numpy.typing import NDArray
27
- from typing import TypeAlias
24
+ Array1DElephino, Array1DLeavesTotal, Array3DLeavesTotal, DatatypeElephino, DatatypeFoldsTotal, DatatypeLeavesTotal,
25
+ getConnectionGraph, getLeavesTotal, makeDataContainer)
28
26
  import dataclasses
29
- import numpy
30
27
 
31
28
  @dataclasses.dataclass(slots=True)
32
29
  class MapFoldingState:
@@ -203,7 +200,7 @@ class SymmetricFoldsState:
203
200
  mapShape: tuple[DatatypeLeavesTotal, ...] = dataclasses.field(init=True, metadata={'elementConstructor': 'DatatypeLeavesTotal'})
204
201
  """Dimensions of the map being analyzed for folding patterns."""
205
202
 
206
- groupsOfFolds: DatatypeFoldsTotal = dataclasses.field(default=DatatypeFoldsTotal(0), metadata={'theCountingIdentifier': True})
203
+ symmetricFolds: DatatypeFoldsTotal = dataclasses.field(default=DatatypeFoldsTotal(0), metadata={'theCountingIdentifier': True})
207
204
  """Current count of symmetric folds."""
208
205
 
209
206
  gap1ndex: DatatypeElephino = DatatypeElephino(0) # noqa: RUF009
@@ -237,12 +234,12 @@ class SymmetricFoldsState:
237
234
  leafComparison: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
238
235
  """Array for finding symmetric folds."""
239
236
 
240
- arrayGroupOfFolds: Array2DLeavesTotal = dataclasses.field(init=False, metadata={'dtype': Array2DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
241
237
  connectionGraph: Array3DLeavesTotal = dataclasses.field(init=False, metadata={'dtype': Array3DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
242
238
  """Unchanging array representing connections between all leaves."""
243
239
  dimensionsTotal: DatatypeLeavesTotal = dataclasses.field(init=False)
244
240
  """Unchanging total number of dimensions in the map."""
245
- indicesArrayGroupOfFolds: Array2DLeavesTotal = dataclasses.field(init=False, metadata={'dtype': Array2DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
241
+ indices: list[list[tuple[int, int]]] = dataclasses.field(init=False)
242
+ """Precomputed index pairs for symmetric fold checking."""
246
243
  leavesTotal: DatatypeLeavesTotal = dataclasses.field(init=False)
247
244
  """Unchanging total number of leaves in the map."""
248
245
 
@@ -259,12 +256,9 @@ class SymmetricFoldsState:
259
256
  self.leavesTotal = DatatypeLeavesTotal(getLeavesTotal(self.mapShape))
260
257
 
261
258
  leavesTotalAsInt = int(self.leavesTotal)
262
-
263
259
  self.connectionGraph = getConnectionGraph(self.mapShape, leavesTotalAsInt, self.__dataclass_fields__['connectionGraph'].metadata['dtype'])
264
260
 
265
- self.indicesArrayGroupOfFolds = (numpy.arange(leavesTotalAsInt + 1)[:, None] + numpy.arange(leavesTotalAsInt)[None, :]) % (leavesTotalAsInt + 1)
266
- self.indicesArrayGroupOfFolds[..., leavesTotalAsInt // 2:None] = self.indicesArrayGroupOfFolds[..., leavesTotalAsInt - 1: leavesTotalAsInt - leavesTotalAsInt // 2 - 1: -1]
267
- self.arrayGroupOfFolds = numpy.zeros_like(self.indicesArrayGroupOfFolds)
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)]
268
262
 
269
263
  if self.dimensionsUnconstrained is None: self.dimensionsUnconstrained = DatatypeLeavesTotal(int(self.dimensionsTotal)) # pyright: ignore[reportUnnecessaryComparison] # noqa: E701
270
264
  if self.gapsWhere is None: self.gapsWhere = makeDataContainer(leavesTotalAsInt * leavesTotalAsInt + 1, self.__dataclass_fields__['gapsWhere'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison] # noqa: E701
@@ -398,6 +392,7 @@ class MatrixMeandersState:
398
392
  """The index of the meanders problem being solved."""
399
393
  oeisID: str
400
394
  """'A000682', semi-meanders, or 'A005316', meanders."""
395
+
401
396
  boundary: int
402
397
  """The algorithm analyzes `n` boundaries starting at `boundary = n - 1`."""
403
398
  dictionaryMeanders: dict[int, int]
@@ -408,14 +403,19 @@ class MatrixMeandersState:
408
403
  bitWidth: int = 0
409
404
  """At the start of an iteration enumerated by `boundary`, the number of bits of the largest value `arcCode`. The
410
405
  `dataclass` computes a `property` from `bitWidth`."""
411
-
412
- @property
413
- def MAXIMUMarcCode(self) -> int:
414
- """Compute the maximum value of `arcCode` for the current iteration of the transfer matrix."""
415
- return 1 << (2 * self.boundary + 4)
416
-
417
- @property
418
- 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:
419
419
  """Compute an odd-parity bit-mask with `bitWidth` bits.
420
420
 
421
421
  Notes
@@ -429,60 +429,19 @@ class MatrixMeandersState:
429
429
  odd numbers from even numbers, so I avoid using "odd" and "even" in the names of these bit-masks.
430
430
 
431
431
  """
432
- return sum(1 << one for one in range(0, self.bitWidth, 2))
433
-
434
- @dataclasses.dataclass(slots=True)
435
- class MatrixMeandersNumPyState(MatrixMeandersState):
436
- """Hold the state of a meanders transfer matrix algorithm computation."""
432
+ self.bitsLocator = sum(1 << one for one in range(0, self.bitWidth, 2))
437
433
 
438
- arrayArcCodes: NDArray[numpy.uint64] = dataclasses.field(default_factory=lambda: numpy.empty((0,), dtype=numpy.uint64))
439
- arrayCrossings: NDArray[numpy.uint64] = dataclasses.field(default_factory=lambda: numpy.empty((0,), dtype=numpy.uint64))
434
+ def setBitWidth(self) -> None:
435
+ """Set `bitWidth` from the current `dictionaryMeanders`."""
436
+ self.bitWidth = max(self.dictionaryMeanders.keys()).bit_length()
440
437
 
441
- bitWidthLimitArcCode: int | None = None
442
- bitWidthLimitCrossings: int | None = None
443
-
444
- datatypeArcCode: TypeAlias = numpy.uint64 # noqa: UP040
445
- """The fixed-size integer type used to store `arcCode`."""
446
- datatypeCrossings: TypeAlias = numpy.uint64 # noqa: UP040
447
- """The fixed-size integer type used to store `crossings`."""
448
-
449
- indexTarget: int = 0
450
- """What is being indexed depends on the algorithm flavor."""
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)
451
441
 
452
442
  def __post_init__(self) -> None:
453
443
  """Post init."""
454
- if self.bitWidthLimitArcCode is None:
455
- _bitWidthOfFixedSizeInteger: int = numpy.dtype(self.datatypeArcCode).itemsize * 8 # bits
456
-
457
- _offsetNecessary: int = 3 # For example, `bitsZulu << 3`.
458
- _offsetSafety: int = 1 # I don't have mathematical proof of how many extra bits I need.
459
- _offset: int = _offsetNecessary + _offsetSafety
460
-
461
- self.bitWidthLimitArcCode = _bitWidthOfFixedSizeInteger - _offset
462
-
463
- del _bitWidthOfFixedSizeInteger, _offsetNecessary, _offsetSafety, _offset
464
-
465
- if self.bitWidthLimitCrossings is None:
466
- _bitWidthOfFixedSizeInteger: int = numpy.dtype(self.datatypeCrossings).itemsize * 8 # bits
467
-
468
- _offsetNecessary: int = 0 # I don't know of any.
469
- _offsetEstimation: int = 3 # See reference directory.
470
- _offsetSafety: int = 1
471
- _offset: int = _offsetNecessary + _offsetEstimation + _offsetSafety
472
-
473
- self.bitWidthLimitCrossings = _bitWidthOfFixedSizeInteger - _offset
474
-
475
- del _bitWidthOfFixedSizeInteger, _offsetNecessary, _offsetEstimation, _offsetSafety, _offset
476
-
477
- def makeDictionary(self) -> None:
478
- """Convert from NumPy `ndarray` (*Num*erical *Py*thon *n-d*imensional array) to Python `dict` (*dict*ionary)."""
479
- self.dictionaryMeanders = {int(key): int(value) for key, value in zip(self.arrayArcCodes, self.arrayCrossings, strict=True)}
480
- self.arrayArcCodes = numpy.empty((0,), dtype=self.datatypeArcCode)
481
- self.arrayCrossings = numpy.empty((0,), dtype=self.datatypeCrossings)
444
+ self.setBitWidth()
445
+ self.setBitsLocator()
446
+ self.setMAXIMUMarcCode()
482
447
 
483
- def makeArray(self) -> None:
484
- """Convert from Python `dict` (*dict*ionary) to NumPy `ndarray` (*Num*erical *Py*thon *n-d*imensional array)."""
485
- self.arrayArcCodes = numpy.array(list(self.dictionaryMeanders.keys()), dtype=self.datatypeArcCode)
486
- self.arrayCrossings = numpy.array(list(self.dictionaryMeanders.values()), dtype=self.datatypeCrossings)
487
- self.bitWidth = int(self.arrayArcCodes.max()).bit_length()
488
- self.dictionaryMeanders = {}
@@ -1,5 +1,5 @@
1
- """
2
- 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:
@@ -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) =
@@ -34,7 +34,7 @@ def _threadDoesSomething() -> None:
34
34
  break
35
35
  state = _filterAsymmetricFolds(state)
36
36
  with LOCKsymmetricFoldsTotal:
37
- symmetricFoldsTotal += state.groupsOfFolds
37
+ symmetricFoldsTotal += state.symmetricFolds
38
38
 
39
39
  def _filterAsymmetricFolds(state: SymmetricFoldsState) -> SymmetricFoldsState:
40
40
  """Add real function during generation; the signature is here to preview its interactions with the module."""
@@ -1,10 +1,12 @@
1
1
  """addSymmetryCheckAsynchronous."""
2
- from astToolkit import Be, Grab, identifierDotAttribute, LedgerOfImports, Make, NodeChanger, NodeTourist, Then
2
+ from astToolkit import Be, Grab, identifierDotAttribute, Make, NodeChanger, NodeTourist, Then
3
+ from astToolkit.containers import LedgerOfImports
4
+ from astToolkit.transformationTools import write_astModule
3
5
  from hunterMakesPy import raiseIfNone
4
6
  from mapFolding import packageSettings
5
7
  from mapFolding.someAssemblyRequired import defaultA007822, IfThis
6
8
  from mapFolding.someAssemblyRequired.A007822.A007822rawMaterials import ExprCallFilterAsymmetricFoldsState
7
- from mapFolding.someAssemblyRequired.toolkitMakeModules import getModule, getPathFilename, write_astModule
9
+ from mapFolding.someAssemblyRequired.toolkitMakeModules import getModule, getPathFilename
8
10
  from pathlib import PurePath
9
11
  import ast
10
12
 
@@ -85,7 +87,7 @@ def addSymmetryCheckAsynchronous(astModule: ast.Module, identifierModule: str, i
85
87
 
86
88
  pathFilename: PurePath = getPathFilename(packageSettings.pathPackage, logicalPathInfix, identifierModule)
87
89
 
88
- write_astModule(astModule, pathFilename, packageSettings.identifierPackage)
90
+ write_astModule(astModule, pathFilename, identifierPackage=packageSettings.identifierPackage)
89
91
 
90
92
  return pathFilename
91
93
 
@@ -1,6 +1,8 @@
1
1
  """addSymmetryCheck."""
2
2
  from astToolkit import (
3
- Be, Grab, identifierDotAttribute, LedgerOfImports, NodeChanger, NodeTourist, parsePathFilename2astModule, Then)
3
+ Be, Grab, identifierDotAttribute, Make, NodeChanger, NodeTourist, parsePathFilename2astModule, Then)
4
+ from astToolkit.containers import LedgerOfImports
5
+ from astToolkit.transformationTools import write_astModule
4
6
  from hunterMakesPy import raiseIfNone
5
7
  from mapFolding import packageSettings
6
8
  from mapFolding.someAssemblyRequired import default, defaultA007822, IfThis
@@ -8,7 +10,7 @@ from mapFolding.someAssemblyRequired.A007822.A007822rawMaterials import (
8
10
  A007822adjustFoldsTotal, A007822incrementCount, FunctionDef_filterAsymmetricFolds)
9
11
  from mapFolding.someAssemblyRequired.makingModules_count import makeTheorem2, numbaOnTheorem2, trimTheorem2
10
12
  from mapFolding.someAssemblyRequired.makingModules_doTheNeedful import makeInitializeState
11
- from mapFolding.someAssemblyRequired.toolkitMakeModules import getModule, getPathFilename, write_astModule
13
+ from mapFolding.someAssemblyRequired.toolkitMakeModules import getModule, getPathFilename
12
14
  from pathlib import PurePath
13
15
  import ast
14
16
 
@@ -43,7 +45,21 @@ def addSymmetryCheck(astModule: ast.Module, identifierModule: str, identifierCal
43
45
 
44
46
  pathFilename: PurePath = getPathFilename(packageSettings.pathPackage, logicalPathInfix, identifierModule)
45
47
 
46
- write_astModule(astModule, pathFilename, packageSettings.identifierPackage)
48
+ write_astModule(astModule, pathFilename, identifierPackage=packageSettings.identifierPackage)
49
+
50
+ return pathFilename
51
+
52
+ def _numbaOnTheorem2(astModule: ast.Module, identifierModule: str, identifierCallable: str | None = None, logicalPathInfix: identifierDotAttribute | None = None, sourceCallableDispatcher: str | None = None) -> PurePath:
53
+ pathFilename: PurePath = numbaOnTheorem2(astModule, identifierModule, identifierCallable, logicalPathInfix, sourceCallableDispatcher)
54
+ astModule = parsePathFilename2astModule(pathFilename)
55
+
56
+ NodeChanger(Be.AnnAssign.valueIs(IfThis.isAttributeNamespaceIdentifier(defaultA007822['variable']['stateInstance'], 'indices'))
57
+ , lambda node: Grab.valueAttribute(Then.replaceWith(Make.Call(Make.Name('List'), [raiseIfNone(node.value)])))(node)
58
+ ).visit(astModule)
59
+
60
+ astModule.body.insert(0, Make.ImportFrom('numba.typed', [Make.alias('List')]))
61
+
62
+ write_astModule(astModule, pathFilename, identifierPackage=packageSettings.identifierPackage)
47
63
 
48
64
  return pathFilename
49
65
 
@@ -55,18 +71,18 @@ def makeA007822Modules() -> None:
55
71
 
56
72
  astModule = getModule(logicalPathInfix=defaultA007822['logicalPath']['synthetic'], identifierModule=defaultA007822['module']['algorithm'])
57
73
  makeInitializeState(astModule, defaultA007822['module']['initializeState']
58
- , defaultA007822['function']['initializeState'], defaultA007822['logicalPath']['synthetic'], None)
74
+ , defaultA007822['function']['initializeState'], defaultA007822['logicalPath']['synthetic'], None, identifiers=defaultA007822)
59
75
 
60
76
  astModule = getModule(logicalPathInfix=defaultA007822['logicalPath']['synthetic'], identifierModule=defaultA007822['module']['algorithm'])
61
77
  pathFilename = makeTheorem2(astModule, 'theorem2', defaultA007822['function']['counting']
62
- , defaultA007822['logicalPath']['synthetic'], defaultA007822['function']['dispatcher'])
78
+ , defaultA007822['logicalPath']['synthetic'], defaultA007822['function']['dispatcher'], identifiers=defaultA007822)
63
79
 
64
80
  astModule = parsePathFilename2astModule(pathFilename)
65
81
  pathFilename = trimTheorem2(astModule, 'theorem2Trimmed', defaultA007822['function']['counting']
66
82
  , defaultA007822['logicalPath']['synthetic'], defaultA007822['function']['dispatcher'])
67
83
 
68
84
  astModule = parsePathFilename2astModule(pathFilename)
69
- pathFilename = numbaOnTheorem2(astModule, 'theorem2Numba', defaultA007822['function']['counting']
85
+ pathFilename = _numbaOnTheorem2(astModule, 'theorem2Numba', defaultA007822['function']['counting']
70
86
  , defaultA007822['logicalPath']['synthetic'], defaultA007822['function']['dispatcher'])
71
87
 
72
88
  if __name__ == '__main__':
@@ -1,16 +1,16 @@
1
1
  """Configuration by dataclass."""
2
2
 
3
- from astToolkit import identifierDotAttribute, IngredientsFunction, IngredientsModule, parseLogicalPath2astModule
3
+ from astToolkit import identifierDotAttribute, parseLogicalPath2astModule
4
+ from astToolkit.containers import IngredientsFunction, IngredientsModule, LedgerOfImports
4
5
  from astToolkit.transformationTools import pythonCode2ast_expr
5
6
  from hunterMakesPy import autoDecodingRLE
6
- # TODO 'The...' identifiers are a vestigial semiotic system. Do I still need to import `asname`? If so, would different
7
+ # TODO 'The____' identifiers are a vestigial semiotic system. Do I still need to import `asname`? If so, would different
7
8
  # identifiers better integrate into the current semiotics?
8
9
  from mapFolding import (
9
10
  DatatypeElephino as TheDatatypeElephino, DatatypeFoldsTotal as TheDatatypeFoldsTotal,
10
11
  DatatypeLeavesTotal as TheDatatypeLeavesTotal, getPathFilenameFoldsTotal, getPathRootJobDEFAULT, packageSettings)
11
- from mapFolding.dataBaskets import MapFoldingState
12
- from mapFolding.someAssemblyRequired import DatatypeConfiguration, default
13
- from mapFolding.someAssemblyRequired._toolkitContainers import ShatteredDataclass
12
+ from mapFolding.dataBaskets import MapFoldingState, SymmetricFoldsState
13
+ from mapFolding.someAssemblyRequired import DatatypeConfiguration, default, ShatteredDataclass
14
14
  from mapFolding.someAssemblyRequired.transformationTools import shatter_dataclassesDOTdataclass
15
15
  from pathlib import Path, PurePosixPath
16
16
  from typing import cast
@@ -22,18 +22,8 @@ class RecipeJobTheorem2:
22
22
  """Configuration recipe for generating map folding computation jobs.
23
23
 
24
24
  This dataclass serves as the central configuration hub for the code transformation
25
- assembly line that converts generic map folding algorithms into highly optimized,
26
- specialized computation modules. The recipe encapsulates all parameters required
27
- for source code analysis, target file generation, datatype mapping, and compilation
28
- optimization settings.
29
-
30
- The transformation process operates by extracting functions from source modules,
31
- embedding concrete parameter values, eliminating dead code paths, and generating
32
- standalone Python modules optimized for specific map dimensions.
33
-
34
- The recipe maintains both source configuration (where to find the generic algorithm)
35
- and target configuration (where to write the optimized module), along with the
36
- computational state that provides concrete values for the transformation process.
25
+ assembly line that converts generic map folding algorithms into optimized,
26
+ specialized modules.
37
27
 
38
28
  Attributes
39
29
  ----------
@@ -87,7 +77,7 @@ class RecipeJobTheorem2:
87
77
  Type alias for leaf count datatype.
88
78
  """
89
79
 
90
- state: MapFoldingState
80
+ state: MapFoldingState | SymmetricFoldsState
91
81
  """The map folding computation state containing dimensions and initial values."""
92
82
  foldsTotalEstimated: int = 0
93
83
  """Estimated total number of folds for progress tracking."""
@@ -129,7 +119,7 @@ class RecipeJobTheorem2:
129
119
  """Logical path root; probably corresponds to physical filesystem directory."""
130
120
  moduleIdentifier: str = dataclasses.field(default=None, init=True) # pyright: ignore[reportAssignmentType]
131
121
  """Target module identifier."""
132
- countCallable: str = identifierCallableSource
122
+ identifierCallable: str = identifierCallableSource
133
123
  """Name of the counting function in generated module."""
134
124
  identifierDataclass: str | None = sourceDataclassIdentifier
135
125
  """Target dataclass identifier."""
@@ -211,12 +201,12 @@ class RecipeJobTheorem2:
211
201
  """
212
202
  pathFilenameFoldsTotal = PurePosixPath(getPathFilenameFoldsTotal(self.state.mapShape))
213
203
 
214
- if self.moduleIdentifier is None: # pyright: ignore[reportUnnecessaryComparison]
215
- self.moduleIdentifier = pathFilenameFoldsTotal.stem
216
-
217
204
  if self.pathFilenameFoldsTotal is None: # pyright: ignore[reportUnnecessaryComparison]
218
205
  self.pathFilenameFoldsTotal = pathFilenameFoldsTotal
219
206
 
207
+ if self.moduleIdentifier is None: # pyright: ignore[reportUnnecessaryComparison]
208
+ self.moduleIdentifier = self.pathFilenameFoldsTotal.stem
209
+
220
210
  if self.shatteredDataclass is None and self.logicalPathModuleDataclass and self.identifierDataclass and self.identifierDataclassInstance: # pyright: ignore[reportUnnecessaryComparison]
221
211
  self.shatteredDataclass = shatter_dataclassesDOTdataclass(self.logicalPathModuleDataclass, self.identifierDataclass, self.identifierDataclassInstance)
222
212
 
@@ -245,11 +235,11 @@ def moveShatteredDataclass_arg2body(identifier: str, job: RecipeJobTheorem2) ->
245
235
  Ima___Assign, elementConstructor = job.shatteredDataclass.Z0Z_field2AnnAssign[identifier]
246
236
  match elementConstructor:
247
237
  case 'scalar':
248
- cast('ast.Constant', cast('ast.Call', Ima___Assign.value).args[0]).value = int(eval(f"job.state.{identifier}")) # noqa: S307
238
+ cast(ast.Constant, cast(ast.Call, Ima___Assign.value).args[0]).value = int(eval(f"job.state.{identifier}")) # noqa: S307
249
239
  case 'array':
250
240
  dataAsStrRLE: str = autoDecodingRLE(eval(f"job.state.{identifier}"), assumeAddSpaces=True) # noqa: S307
251
241
  dataAs_ast_expr: ast.expr = pythonCode2ast_expr(dataAsStrRLE)
252
- cast('ast.Call', Ima___Assign.value).args = [dataAs_ast_expr]
242
+ cast(ast.Call, Ima___Assign.value).args = [dataAs_ast_expr]
253
243
  case _:
254
244
  pass
255
245
  return Ima___Assign
@@ -74,6 +74,7 @@ calculations through the strategic application of compiler optimization techniqu
74
74
 
75
75
  from mapFolding.someAssemblyRequired.infoBooth import (
76
76
  default as default,
77
+ Default as Default,
77
78
  defaultA007822 as defaultA007822,
78
79
  dictionaryEstimatesMapFolding as dictionaryEstimatesMapFolding,
79
80
  )
@@ -25,7 +25,8 @@ to low-level optimized functions while maintaining semantic equivalence and type
25
25
  the compilation process.
26
26
  """
27
27
 
28
- from astToolkit import Be, DOT, identifierDotAttribute, LedgerOfImports, Make, NodeTourist, Then
28
+ from astToolkit import Be, DOT, identifierDotAttribute, Make, NodeTourist, Then
29
+ from astToolkit.containers import LedgerOfImports
29
30
  from collections.abc import Callable
30
31
  from copy import deepcopy
31
32
  from hunterMakesPy import raiseIfNone
@@ -220,7 +221,7 @@ class DeReConstructField2ast: # slots?
220
221
  self.ast_keyword_field__field = Make.keyword(self.name, self.astName)
221
222
  self.ast_nameDOTname = Make.Attribute(Make.Name(dataclassesDOTdataclassInstanceIdentifier), self.name)
222
223
 
223
- self.astAnnotation = cast('ast.Name', raiseIfNone(NodeTourist(
224
+ self.astAnnotation = cast(ast.Name, raiseIfNone(NodeTourist(
224
225
  findThis = Be.AnnAssign.targetIs(IfThis.isNameIdentifier(self.name))
225
226
  , doThat = Then.extractIt(DOT.annotation)
226
227
  ).captureLastMatch(dataclassClassDef)))
@@ -251,8 +252,9 @@ class DeReConstructField2ast: # slots?
251
252
  self.astAnnAssignConstructor = Make.AnnAssign(self.astName, self.astAnnotation, Make.Call(self.astAnnotation, [Make.Constant(-1)]))
252
253
  self.Z0Z_hack = (self.astAnnAssignConstructor, 'scalar')
253
254
  elif isinstance(self.astAnnotation, ast.Subscript):
254
- elementConstructor: str = self.metadata['elementConstructor']
255
- self.ledger.addImportFrom_asStr(dataclassesDOTdataclassLogicalPathModule, elementConstructor)
255
+ elementConstructor: str = self.metadata.get('elementConstructor', 'generic')
256
+ if elementConstructor != 'generic':
257
+ self.ledger.addImportFrom_asStr(dataclassesDOTdataclassLogicalPathModule, elementConstructor)
256
258
  takeTheTuple = deepcopy(self.astAnnotation.slice)
257
259
  self.astAnnAssignConstructor = Make.AnnAssign(self.astName, self.astAnnotation, takeTheTuple)
258
260
  self.Z0Z_hack = (self.astAnnAssignConstructor, elementConstructor)
@@ -44,6 +44,7 @@ default = Default(
44
44
  },
45
45
  module = {
46
46
  'algorithm': 'daoOfMapFolding',
47
+ 'dataBasket': 'dataBaskets',
47
48
  'initializeState': 'initializeState',
48
49
  },
49
50
  variable = {
@@ -62,5 +63,5 @@ defaultA007822['logicalPath']['assembly'] = 'someAssemblyRequired.A007822'
62
63
  defaultA007822['logicalPath']['synthetic'] += '.A007822'
63
64
  defaultA007822['module']['algorithm'] = 'algorithm'
64
65
  defaultA007822['module']['asynchronous'] = 'asynchronous'
65
- # defaultA007822['variable']['counting'] = 'symmetricFolds'
66
+ defaultA007822['variable']['counting'] = 'symmetricFolds'
66
67
  defaultA007822['variable']['stateDataclass'] = 'SymmetricFoldsState'