mapFolding 0.8.0__py3-none-any.whl → 0.8.2__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 (40) hide show
  1. mapFolding/__init__.py +33 -4
  2. mapFolding/basecamp.py +16 -2
  3. mapFolding/beDRY.py +40 -32
  4. mapFolding/filesystem.py +124 -90
  5. mapFolding/noHomeYet.py +12 -0
  6. mapFolding/oeis.py +18 -3
  7. mapFolding/reference/__init__.py +38 -0
  8. mapFolding/reference/flattened.py +66 -47
  9. mapFolding/reference/hunterNumba.py +28 -4
  10. mapFolding/reference/irvineJavaPort.py +13 -1
  11. mapFolding/reference/{jax.py → jaxCount.py} +46 -27
  12. mapFolding/reference/lunnanNumpy.py +19 -5
  13. mapFolding/reference/lunnanWhile.py +19 -7
  14. mapFolding/reference/rotatedEntryPoint.py +20 -3
  15. mapFolding/reference/total_countPlus1vsPlusN.py +226 -203
  16. mapFolding/someAssemblyRequired/__init__.py +29 -0
  17. mapFolding/someAssemblyRequired/getLLVMforNoReason.py +32 -14
  18. mapFolding/someAssemblyRequired/ingredientsNumba.py +22 -1
  19. mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py +193 -0
  20. mapFolding/someAssemblyRequired/synthesizeNumbaJobVESTIGIAL.py +3 -4
  21. mapFolding/someAssemblyRequired/transformDataStructures.py +168 -0
  22. mapFolding/someAssemblyRequired/transformationTools.py +233 -225
  23. mapFolding/theDao.py +19 -5
  24. mapFolding/theSSOT.py +89 -122
  25. mapfolding-0.8.2.dist-info/METADATA +187 -0
  26. mapfolding-0.8.2.dist-info/RECORD +39 -0
  27. {mapfolding-0.8.0.dist-info → mapfolding-0.8.2.dist-info}/WHEEL +1 -1
  28. tests/conftest.py +43 -33
  29. tests/test_computations.py +7 -7
  30. tests/test_other.py +2 -2
  31. mapFolding/reference/lunnan.py +0 -153
  32. mapFolding/someAssemblyRequired/Z0Z_workbench.py +0 -350
  33. mapFolding/someAssemblyRequired/synthesizeDataConverters.py +0 -117
  34. mapFolding/syntheticModules/numbaCountHistoricalExample.py +0 -158
  35. mapFolding/syntheticModules/numba_doTheNeedfulHistoricalExample.py +0 -13
  36. mapfolding-0.8.0.dist-info/METADATA +0 -157
  37. mapfolding-0.8.0.dist-info/RECORD +0 -41
  38. {mapfolding-0.8.0.dist-info → mapfolding-0.8.2.dist-info}/entry_points.txt +0 -0
  39. {mapfolding-0.8.0.dist-info → mapfolding-0.8.2.dist-info/licenses}/LICENSE +0 -0
  40. {mapfolding-0.8.0.dist-info → mapfolding-0.8.2.dist-info}/top_level.txt +0 -0
@@ -1,14 +1,33 @@
1
- """The algorithm flattened into semantic sections.
2
- This version is not maintained, so you may see differences from the current version."""
1
+ """
2
+ Semantically decomposed implementation of Lunnon's algorithm with operation grouping.
3
+
4
+ This implementation restructures the map folding algorithm into semantic sections with
5
+ clear function boundaries, making the algorithm more readable and understandable. Each
6
+ operation is isolated into its own named function, providing a clear mapping between
7
+ the mathematical concepts and their implementation.
8
+
9
+ Key characteristics:
10
+ - Breaks down the algorithm into small, single-purpose functions
11
+ - Uses descriptive function names that explain what each part does
12
+ - Clearly separates logical sections of the algorithm
13
+ - Provides a more maintainable and educational view of the algorithm
14
+ - Uses Python's type hints for better code understanding
15
+
16
+ This implementation serves as a bridge between the historical implementations and the
17
+ modern functional approach used in the main package. It's particularly valuable for
18
+ understanding the algorithm's operation before diving into the highly optimized versions.
19
+ """
20
+
21
+ from collections.abc import Sequence
3
22
  from numpy import integer
4
23
  from numpy.typing import NDArray
5
- from typing import List, Any, Final, Optional, Union, Sequence, Tuple, Type, TypedDict
24
+ from typing import Any, Final, TypedDict
6
25
  import enum
7
26
  import numpy
8
27
  import sys
9
28
 
10
- def countFolds(listDimensions: Sequence[int], computationDivisions = None, CPUlimit: Optional[Union[int, float, bool]] = None):
11
- def doWhile():
29
+ def countFolds(listDimensions: Sequence[int], computationDivisions: int | str | None = None, CPUlimit: int | float | bool | None = None) -> int:
30
+ def doWhile() -> None:
12
31
 
13
32
  while activeLeafGreaterThan0Condition():
14
33
 
@@ -49,99 +68,99 @@ def countFolds(listDimensions: Sequence[int], computationDivisions = None, CPUli
49
68
  if placeLeafCondition():
50
69
  placeLeaf()
51
70
 
52
- def activeGapIncrement():
71
+ def activeGapIncrement() -> None:
53
72
  my[indexMy.gap1ndex] += 1
54
73
 
55
- def activeLeafGreaterThan0Condition():
74
+ def activeLeafGreaterThan0Condition() -> bool:
56
75
  return my[indexMy.leaf1ndex] > 0
57
76
 
58
- def activeLeafGreaterThanLeavesTotalCondition():
77
+ def activeLeafGreaterThanLeavesTotalCondition() -> bool:
59
78
  return my[indexMy.leaf1ndex] > the[indexThe.leavesTotal]
60
79
 
61
- def activeLeafIsTheFirstLeafCondition():
80
+ def activeLeafIsTheFirstLeafCondition() -> bool:
62
81
  return my[indexMy.leaf1ndex] <= 1
63
82
 
64
- def activeLeafNotEqualToTaskDivisionsCondition():
83
+ def activeLeafNotEqualToTaskDivisionsCondition() -> bool:
65
84
  return my[indexMy.leaf1ndex] != the[indexThe.taskDivisions]
66
85
 
67
- def allDimensionsAreUnconstrained():
86
+ def allDimensionsAreUnconstrained() -> bool:
68
87
  return my[indexMy.dimensionsUnconstrained] == the[indexThe.dimensionsTotal]
69
88
 
70
- def backtrack():
89
+ def backtrack() -> None:
71
90
  my[indexMy.leaf1ndex] -= 1
72
91
  track[indexTrack.leafBelow, track[indexTrack.leafAbove, my[indexMy.leaf1ndex]]] = track[indexTrack.leafBelow, my[indexMy.leaf1ndex]]
73
92
  track[indexTrack.leafAbove, track[indexTrack.leafBelow, my[indexMy.leaf1ndex]]] = track[indexTrack.leafAbove, my[indexMy.leaf1ndex]]
74
93
 
75
- def backtrackCondition():
94
+ def backtrackCondition() -> bool:
76
95
  return my[indexMy.leaf1ndex] > 0 and my[indexMy.gap1ndex] == track[indexTrack.gapRangeStart, my[indexMy.leaf1ndex] - 1]
77
96
 
78
- def computationDivisionsCondition():
97
+ def computationDivisionsCondition() -> bool:
79
98
  return the[indexThe.taskDivisions] == int(False)
80
99
 
81
- def countGaps():
100
+ def countGaps() -> None:
82
101
  gapsWhere[my[indexMy.gap1ndexCeiling]] = my[indexMy.leafConnectee]
83
102
  if track[indexTrack.countDimensionsGapped, my[indexMy.leafConnectee]] == 0:
84
103
  gap1ndexCeilingIncrement()
85
104
  track[indexTrack.countDimensionsGapped, my[indexMy.leafConnectee]] += 1
86
105
 
87
- def dimension1ndexIncrement():
106
+ def dimension1ndexIncrement() -> None:
88
107
  my[indexMy.dimension1ndex] += 1
89
108
 
90
- def dimensionsUnconstrainedCondition():
109
+ def dimensionsUnconstrainedCondition() -> bool:
91
110
  return connectionGraph[my[indexMy.dimension1ndex], my[indexMy.leaf1ndex], my[indexMy.leaf1ndex]] == my[indexMy.leaf1ndex]
92
111
 
93
- def dimensionsUnconstrainedIncrement():
112
+ def dimensionsUnconstrainedIncrement() -> None:
94
113
  my[indexMy.dimensionsUnconstrained] += 1
95
114
 
96
- def filterCommonGaps():
115
+ def filterCommonGaps() -> None:
97
116
  gapsWhere[my[indexMy.gap1ndex]] = gapsWhere[my[indexMy.indexMiniGap]]
98
117
  if track[indexTrack.countDimensionsGapped, gapsWhere[my[indexMy.indexMiniGap]]] == the[indexThe.dimensionsTotal] - my[indexMy.dimensionsUnconstrained]:
99
118
  activeGapIncrement()
100
119
  track[indexTrack.countDimensionsGapped, gapsWhere[my[indexMy.indexMiniGap]]] = 0
101
120
 
102
- def findGapsInitializeVariables():
121
+ def findGapsInitializeVariables() -> None:
103
122
  my[indexMy.dimensionsUnconstrained] = 0
104
123
  my[indexMy.gap1ndexCeiling] = track[indexTrack.gapRangeStart, my[indexMy.leaf1ndex] - 1]
105
124
  my[indexMy.dimension1ndex] = 1
106
125
 
107
- def foldsSubTotalsIncrement():
126
+ def foldsSubTotalsIncrement() -> None:
108
127
  foldsSubTotals[my[indexMy.taskIndex]] += the[indexThe.leavesTotal]
109
128
 
110
- def gap1ndexCeilingIncrement():
129
+ def gap1ndexCeilingIncrement() -> None:
111
130
  my[indexMy.gap1ndexCeiling] += 1
112
131
 
113
- def indexMiniGapIncrement():
132
+ def indexMiniGapIncrement() -> None:
114
133
  my[indexMy.indexMiniGap] += 1
115
134
 
116
- def indexMiniGapInitialization():
135
+ def indexMiniGapInitialization() -> None:
117
136
  my[indexMy.indexMiniGap] = my[indexMy.gap1ndex]
118
137
 
119
- def insertUnconstrainedLeaf():
138
+ def insertUnconstrainedLeaf() -> None:
120
139
  my[indexMy.indexLeaf] = 0
121
140
  while my[indexMy.indexLeaf] < my[indexMy.leaf1ndex]:
122
141
  gapsWhere[my[indexMy.gap1ndexCeiling]] = my[indexMy.indexLeaf]
123
142
  my[indexMy.gap1ndexCeiling] += 1
124
143
  my[indexMy.indexLeaf] += 1
125
144
 
126
- def leafBelowSentinelIs1Condition():
145
+ def leafBelowSentinelIs1Condition() -> bool:
127
146
  return track[indexTrack.leafBelow, 0] == 1
128
147
 
129
- def leafConnecteeInitialization():
148
+ def leafConnecteeInitialization() -> None:
130
149
  my[indexMy.leafConnectee] = connectionGraph[my[indexMy.dimension1ndex], my[indexMy.leaf1ndex], my[indexMy.leaf1ndex]]
131
150
 
132
- def leafConnecteeUpdate():
151
+ def leafConnecteeUpdate() -> None:
133
152
  my[indexMy.leafConnectee] = connectionGraph[my[indexMy.dimension1ndex], my[indexMy.leaf1ndex], track[indexTrack.leafBelow, my[indexMy.leafConnectee]]]
134
153
 
135
- def loopingLeavesConnectedToActiveLeaf():
154
+ def loopingLeavesConnectedToActiveLeaf() -> bool:
136
155
  return my[indexMy.leafConnectee] != my[indexMy.leaf1ndex]
137
156
 
138
- def loopingTheDimensions():
157
+ def loopingTheDimensions() -> bool:
139
158
  return my[indexMy.dimension1ndex] <= the[indexThe.dimensionsTotal]
140
159
 
141
- def loopingToActiveGapCeiling():
160
+ def loopingToActiveGapCeiling() -> bool:
142
161
  return my[indexMy.indexMiniGap] < my[indexMy.gap1ndexCeiling]
143
162
 
144
- def placeLeaf():
163
+ def placeLeaf() -> None:
145
164
  my[indexMy.gap1ndex] -= 1
146
165
  track[indexTrack.leafAbove, my[indexMy.leaf1ndex]] = gapsWhere[my[indexMy.gap1ndex]]
147
166
  track[indexTrack.leafBelow, my[indexMy.leaf1ndex]] = track[indexTrack.leafBelow, track[indexTrack.leafAbove, my[indexMy.leaf1ndex]]]
@@ -150,13 +169,13 @@ def countFolds(listDimensions: Sequence[int], computationDivisions = None, CPUli
150
169
  track[indexTrack.gapRangeStart, my[indexMy.leaf1ndex]] = my[indexMy.gap1ndex]
151
170
  my[indexMy.leaf1ndex] += 1
152
171
 
153
- def placeLeafCondition():
172
+ def placeLeafCondition() -> bool:
154
173
  return my[indexMy.leaf1ndex] > 0
155
174
 
156
- def taskIndexCondition():
175
+ def taskIndexCondition() -> bool:
157
176
  return my[indexMy.leafConnectee] % the[indexThe.taskDivisions] == my[indexMy.taskIndex]
158
177
 
159
- def thereAreComputationDivisionsYouMightSkip():
178
+ def thereAreComputationDivisionsYouMightSkip() -> bool:
160
179
  if computationDivisionsCondition():
161
180
  return True
162
181
  if activeLeafNotEqualToTaskDivisionsCondition():
@@ -166,11 +185,11 @@ def countFolds(listDimensions: Sequence[int], computationDivisions = None, CPUli
166
185
  return False
167
186
 
168
187
  stateUniversal = outfitFoldings(listDimensions, computationDivisions=computationDivisions, CPUlimit=CPUlimit)
169
- connectionGraph: Final[numpy.ndarray] = stateUniversal['connectionGraph']
188
+ connectionGraph: Final[NDArray[numpy.integer[Any]]] = stateUniversal['connectionGraph']
170
189
  foldsSubTotals = stateUniversal['foldsSubTotals']
171
190
  gapsWhere = stateUniversal['gapsWhere']
172
191
  my = stateUniversal['my']
173
- the: Final[numpy.ndarray] = stateUniversal['the']
192
+ the: Final[NDArray[numpy.integer[Any]]] = stateUniversal['the']
174
193
  track = stateUniversal['track']
175
194
 
176
195
  if the[indexThe.taskDivisions] == int(False):
@@ -192,7 +211,7 @@ def countFolds(listDimensions: Sequence[int], computationDivisions = None, CPUli
192
211
  class EnumIndices(enum.IntEnum):
193
212
  """Base class for index enums."""
194
213
  @staticmethod
195
- def _generate_next_value_(name, start, count, last_values):
214
+ def _generate_next_value_(name: str, start: int, count: int, last_values: list[int]) -> int:
196
215
  """0-indexed."""
197
216
  return count
198
217
 
@@ -229,7 +248,7 @@ class indexTrack(EnumIndices):
229
248
  class computationState(TypedDict):
230
249
  connectionGraph: NDArray[integer[Any]]
231
250
  foldsSubTotals: NDArray[integer[Any]]
232
- mapShape: Tuple[int, ...]
251
+ mapShape: tuple[int, ...]
233
252
  my: NDArray[integer[Any]]
234
253
  gapsWhere: NDArray[integer[Any]]
235
254
  the: NDArray[integer[Any]]
@@ -262,7 +281,7 @@ def getLeavesTotal(listDimensions: Sequence[int]) -> int:
262
281
 
263
282
  return productDimensions
264
283
 
265
- def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrencyLimit: int, CPUlimit: Optional[Union[bool, float, int]], listDimensions: Sequence[int]):
284
+ def getTaskDivisions(computationDivisions: int | str | None, concurrencyLimit: int, CPUlimit: bool | float | int | None, listDimensions: Sequence[int]) -> int:
266
285
  if not computationDivisions:
267
286
  return 0
268
287
  else:
@@ -284,7 +303,7 @@ def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrenc
284
303
 
285
304
  return taskDivisions
286
305
 
287
- def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optional[Type]) -> NDArray[integer[Any]]:
306
+ def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: type | None) -> NDArray[integer[Any]]:
288
307
  datatype = keywordArguments.get('datatype', dtypeMedium)
289
308
  mapShape = validateListDimensions(listDimensions)
290
309
  leavesTotal = getLeavesTotal(mapShape)
@@ -316,12 +335,12 @@ def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optio
316
335
  connectionGraph[dimension1ndex, activeLeaf1ndex, connectee1ndex] = connectee1ndex
317
336
  return connectionGraph
318
337
 
319
- def makeDataContainer(shape, datatype: Optional[Type] = None):
338
+ def makeDataContainer(shape: int | Sequence[int], datatype: type | None = None) -> NDArray[integer[Any]]:
320
339
  if datatype is None:
321
340
  datatype = dtypeMedium
322
341
  return numpy.zeros(shape, dtype=datatype)
323
342
 
324
- def outfitFoldings(listDimensions: Sequence[int], computationDivisions: Optional[Union[int, str]] = None, CPUlimit: Optional[Union[bool, float, int]] = None, **keywordArguments: Optional[Type]) -> computationState:
343
+ def outfitFoldings(listDimensions: Sequence[int], computationDivisions: int | str | None = None, CPUlimit: bool | float | int | None = None, **keywordArguments: type | None) -> computationState:
325
344
  datatypeMedium = keywordArguments.get('datatypeMedium', dtypeMedium)
326
345
  datatypeLarge = keywordArguments.get('datatypeLarge', dtypeLarge)
327
346
 
@@ -346,10 +365,10 @@ def outfitFoldings(listDimensions: Sequence[int], computationDivisions: Optional
346
365
  stateInitialized['my'][indexMy.leaf1ndex] = 1
347
366
  return stateInitialized
348
367
 
349
- def parseDimensions(dimensions: Sequence[int], parameterName: str = 'unnamed parameter') -> List[int]:
368
+ def parseDimensions(dimensions: Sequence[int], parameterName: str = 'unnamed parameter') -> list[int]:
350
369
  # listValidated = intInnit(dimensions, parameterName)
351
370
  listNOTValidated = dimensions if isinstance(dimensions, (list, tuple)) else list(dimensions)
352
- listNonNegative = []
371
+ listNonNegative: list[int] = []
353
372
  for dimension in listNOTValidated:
354
373
  if dimension < 0:
355
374
  raise ValueError(f"Dimension {dimension} must be non-negative")
@@ -358,7 +377,7 @@ def parseDimensions(dimensions: Sequence[int], parameterName: str = 'unnamed par
358
377
  raise ValueError("At least one dimension must be non-negative")
359
378
  return listNonNegative
360
379
 
361
- def setCPUlimit(CPUlimit: Union[bool, float, int, None]) -> int:
380
+ def setCPUlimit(CPUlimit: bool | float | int | None) -> int:
362
381
  # if not (CPUlimit is None or isinstance(CPUlimit, (bool, int, float))):
363
382
  # CPUlimit = oopsieKwargsie(CPUlimit)
364
383
  # concurrencyLimit = defineConcurrencyLimit(CPUlimit)
@@ -367,7 +386,7 @@ def setCPUlimit(CPUlimit: Union[bool, float, int, None]) -> int:
367
386
  concurrencyLimit = concurrencyLimitHARDCODED
368
387
  return concurrencyLimit
369
388
 
370
- def validateListDimensions(listDimensions: Sequence[int]) -> List[int]:
389
+ def validateListDimensions(listDimensions: Sequence[int]) -> list[int]:
371
390
  if not listDimensions:
372
391
  raise ValueError(f"listDimensions is a required parameter.")
373
392
  listNonNegative = parseDimensions(listDimensions, 'listDimensions')
@@ -1,9 +1,33 @@
1
- from typing import List
1
+ """
2
+ High-performance Numba-accelerated implementation of Lunnon's algorithm.
3
+
4
+ This implementation focuses on maximum computational performance by leveraging Numba's
5
+ just-in-time (JIT) compilation capabilities to generate native machine code. It represents
6
+ a manually optimized version that served as inspiration for the automated transformation
7
+ framework in the someAssemblyRequired package.
8
+
9
+ Key characteristics:
10
+ - Optimized data structures using NumPy typed arrays with appropriate data types
11
+ - Function decorators for Numba JIT compilation with performance-oriented settings
12
+ - Memory-efficient implementation with careful type management
13
+ - Reduced Python overhead through native code execution
14
+ - Algorithmic optimizations tailored for numerical computation
15
+
16
+ Performance considerations:
17
+ - Up to 1000× faster than pure Python implementations
18
+ - Optimized for larger map dimensions where computational demands increase exponentially
19
+ - Incorporates lessons from multiple implementation strategies
20
+
21
+ Note: This serves as a reference for manually-optimized code before the development of
22
+ the automated transformation pipeline in the main package.
23
+ """
24
+
25
+ from typing import Any
2
26
  import numba
3
27
  import numpy
4
28
 
5
29
  @numba.jit(cache=True, nopython=True, fastmath=True)
6
- def countFolds(listDimensions: List[int]) -> int:
30
+ def countFolds(listDimensions: list[int]) -> int:
7
31
  """
8
32
  Count the number of distinct ways to fold a map with at least two positive dimensions.
9
33
 
@@ -13,10 +37,10 @@ def countFolds(listDimensions: List[int]) -> int:
13
37
  Returns:
14
38
  foldsTotal: The total number of distinct folds for the given map dimensions.
15
39
  """
16
- def integerSmall(value) -> numpy.uint8:
40
+ def integerSmall(value: numpy.integer[Any] | Any) -> numpy.uint8:
17
41
  return numpy.uint8(value)
18
42
 
19
- def integerLarge(value) -> numpy.uint64:
43
+ def integerLarge(value: numpy.integer[Any] | Any) -> numpy.uint64:
20
44
  return numpy.uint64(value)
21
45
 
22
46
  dtypeMedium = numpy.uint8
@@ -2,8 +2,20 @@
2
2
  Ported from the Java version by Sean A. Irvine:
3
3
  https://github.com/archmageirvine/joeis/blob/80e3e844b11f149704acbab520bc3a3a25ac34ff/src/irvine/oeis/a001/A001415.java
4
4
 
5
- Citation: mapFolding/citations/jOEIS.bibtex
5
+ This implementation is a conversion from a well-known Java implementation of Lunnon's algorithm
6
+ by Sean A. Irvine, a contributor to the OEIS project. It provides a clean, procedural implementation
7
+ with straightforward variable naming and control flow that may be more approachable for
8
+ programmers familiar with modern languages.
9
+
10
+ Key characteristics:
11
+ - Clear variable naming following modern programming conventions
12
+ - Procedural implementation style similar to Java but adapted for Python
13
+ - Follows the same algorithmic structure as Lunnon's original but with cleaner organization
14
+ - Uses primitive Python data structures (lists) without NumPy dependencies
15
+
16
+ Citation: https://github.com/hunterhogan/mapFolding/blob/134f2e6ecdf59fb6f6829c775475544a6aaaa800/citations/jOEIS.bibtex
6
17
  """
18
+
7
19
  def foldings(p: list[int], res: int = 0, mod: int = 0) -> int:
8
20
  """
9
21
  Compute the total number of foldings for a map with dimensions specified in p.
@@ -1,20 +1,39 @@
1
- """I was able to implement the algorithm with JAX, but I didn't see an advantage and it's a pain in the ass.
2
- I don't maintain this module."""
3
- from mapFolding import validateListDimensions, getLeavesTotal, makeConnectionGraph
4
- from typing import List, Tuple
1
+ """
2
+ I was able to implement the algorithm with JAX, but I didn't see an advantage and it's a pain in the ass.
3
+
4
+ Experimental JAX implementation of Lunnon's algorithm for potential GPU acceleration.
5
+
6
+ This implementation explores the use of JAX (Just After eXecution) for potential GPU acceleration
7
+ of the map folding algorithm. It represents an experimental approach that attempts to leverage
8
+ JAX's transformation capabilities and hardware acceleration through XLA compilation.
9
+
10
+ Key characteristics:
11
+ - Uses JAX's functional programming model with lax control flow primitives
12
+ - Attempts to leverage GPU acceleration for numerical operations
13
+ - Demonstrates JAX-specific patterns for handling loops and conditions
14
+ - Supports jit compilation for performance optimization
15
+
16
+ As noted in the file's comment, this implementation didn't demonstrate significant advantages
17
+ over other approaches and presented additional complexity. It serves as a valuable reference
18
+ for exploring alternative acceleration strategies and understanding the limitations of
19
+ different computational frameworks for this specific algorithm.
20
+ """
21
+ from flattened import validateListDimensions, getLeavesTotal, makeConnectionGraph
22
+ from numpy.typing import NDArray
23
+ from typing import Any
5
24
  import jax
6
25
  import jaxtyping
26
+ import numpy
7
27
 
8
28
  dtypeMedium = jax.numpy.uint32
9
29
  dtypeMaximum = jax.numpy.uint32
10
30
 
11
- def countFolds(listDimensions: List[int]) -> int:
12
- listDimensionsPositive: List[int] = validateListDimensions(listDimensions)
31
+ def countFolds(listDimensions: list[int]) -> int:
32
+ listDimensionsPositive: list[int] = validateListDimensions(listDimensions)
13
33
 
14
34
  n: int = getLeavesTotal(listDimensionsPositive)
15
35
  d: int = len(listDimensions)
16
- import numpy
17
- D: numpy.ndarray = makeConnectionGraph(listDimensionsPositive)
36
+ D: NDArray[numpy.integer[Any]] = makeConnectionGraph(listDimensionsPositive)
18
37
  connectionGraph = jax.numpy.asarray(D, dtype=dtypeMedium)
19
38
  del listDimensionsPositive
20
39
 
@@ -22,14 +41,14 @@ def countFolds(listDimensions: List[int]) -> int:
22
41
 
23
42
  def foldingsJAX(leavesTotal: jaxtyping.UInt32, dimensionsTotal: jaxtyping.UInt32, connectionGraph: jaxtyping.Array) -> jaxtyping.UInt32:
24
43
 
25
- def doNothing(argument):
44
+ def doNothing(argument: Any):
26
45
  return argument
27
46
 
28
- def while_activeLeaf1ndex_greaterThan_0(comparisonValues: Tuple):
47
+ def while_activeLeaf1ndex_greaterThan_0(comparisonValues: tuple):
29
48
  comparand = comparisonValues[6]
30
49
  return comparand > 0
31
50
 
32
- def countFoldings(allValues: Tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32, jaxtyping.UInt32]):
51
+ def countFoldings(allValues: tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32, jaxtyping.UInt32]):
33
52
  _0, leafBelow, _2, _3, _4, _5, activeLeaf1ndex, _7 = allValues
34
53
 
35
54
  sentinel = leafBelow.at[0].get().astype(jax.numpy.uint32)
@@ -44,24 +63,24 @@ def foldingsJAX(leavesTotal: jaxtyping.UInt32, dimensionsTotal: jaxtyping.UInt32
44
63
  def findGapsCondition(leafBelowSentinel, activeLeafNumber):
45
64
  return jax.numpy.logical_or(jax.numpy.logical_and(leafBelowSentinel == 1, activeLeafNumber <= leavesTotal), activeLeafNumber <= 1)
46
65
 
47
- def findGapsDo(allValues: Tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32, jaxtyping.UInt32]):
48
- def for_dimension1ndex_in_range_1_to_dimensionsTotalPlus1(comparisonValues: Tuple):
66
+ def findGapsDo(allValues: tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32, jaxtyping.UInt32]):
67
+ def for_dimension1ndex_in_range_1_to_dimensionsTotalPlus1(comparisonValues: tuple):
49
68
  return comparisonValues[-1] <= dimensionsTotal
50
69
 
51
- def for_dimension1ndex_in_range_1_to_dimensionsTotalPlus1_do(for_dimension1ndex_in_range_1_to_dimensionsTotalPlus1Values: Tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32, jaxtyping.UInt32]):
70
+ def for_dimension1ndex_in_range_1_to_dimensionsTotalPlus1_do(for_dimension1ndex_in_range_1_to_dimensionsTotalPlus1Values: tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32, jaxtyping.UInt32]):
52
71
  def ifLeafIsUnconstrainedCondition(comparand):
53
72
  return jax.numpy.equal(connectionGraph[comparand, activeLeaf1ndex, activeLeaf1ndex], activeLeaf1ndex)
54
73
 
55
- def ifLeafIsUnconstrainedDo(unconstrainedValues: Tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32]):
74
+ def ifLeafIsUnconstrainedDo(unconstrainedValues: tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32]):
56
75
  unconstrained_unconstrainedLeaf = unconstrainedValues[3]
57
76
  unconstrained_unconstrainedLeaf = 1 + unconstrained_unconstrainedLeaf
58
77
  return (unconstrainedValues[0], unconstrainedValues[1], unconstrainedValues[2], unconstrained_unconstrainedLeaf)
59
78
 
60
- def ifLeafIsUnconstrainedElse(unconstrainedValues: Tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32]):
61
- def while_leaf1ndexConnectee_notEquals_activeLeaf1ndex(comparisonValues: Tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32]):
79
+ def ifLeafIsUnconstrainedElse(unconstrainedValues: tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32]):
80
+ def while_leaf1ndexConnectee_notEquals_activeLeaf1ndex(comparisonValues: tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32]):
62
81
  return comparisonValues[-1] != activeLeaf1ndex
63
82
 
64
- def countGaps(countGapsDoValues: Tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32]):
83
+ def countGaps(countGapsDoValues: tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32]):
65
84
  countGapsCountDimensionsGapped, countGapsPotentialGaps, countGapsGap1ndexLowerBound, countGapsLeaf1ndexConnectee = countGapsDoValues
66
85
 
67
86
  countGapsPotentialGaps = countGapsPotentialGaps.at[countGapsGap1ndexLowerBound].set(countGapsLeaf1ndexConnectee)
@@ -93,11 +112,11 @@ def foldingsJAX(leavesTotal: jaxtyping.UInt32, dimensionsTotal: jaxtyping.UInt32
93
112
  def almostUselessCondition(comparand):
94
113
  return comparand == dimensionsTotal
95
114
 
96
- def almostUselessConditionDo(for_leaf1ndex_in_range_activeLeaf1ndexValues: Tuple[jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32]):
115
+ def almostUselessConditionDo(for_leaf1ndex_in_range_activeLeaf1ndexValues: tuple[jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32]):
97
116
  def for_leaf1ndex_in_range_activeLeaf1ndex(comparisonValues):
98
117
  return comparisonValues[-1] < activeLeaf1ndex
99
118
 
100
- def for_leaf1ndex_in_range_activeLeaf1ndex_do(for_leaf1ndex_in_range_activeLeaf1ndexValues: Tuple[jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32]):
119
+ def for_leaf1ndex_in_range_activeLeaf1ndex_do(for_leaf1ndex_in_range_activeLeaf1ndexValues: tuple[jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32]):
101
120
  leafInRangePotentialGaps, gapNumberLowerBound, leafNumber = for_leaf1ndex_in_range_activeLeaf1ndexValues
102
121
  leafInRangePotentialGaps = leafInRangePotentialGaps.at[gapNumberLowerBound].set(leafNumber)
103
122
  gapNumberLowerBound = 1 + gapNumberLowerBound
@@ -105,10 +124,10 @@ def foldingsJAX(leavesTotal: jaxtyping.UInt32, dimensionsTotal: jaxtyping.UInt32
105
124
  return (leafInRangePotentialGaps, gapNumberLowerBound, leafNumber)
106
125
  return jax.lax.while_loop(for_leaf1ndex_in_range_activeLeaf1ndex, for_leaf1ndex_in_range_activeLeaf1ndex_do, for_leaf1ndex_in_range_activeLeaf1ndexValues)
107
126
 
108
- def for_range_from_activeGap1ndex_to_gap1ndexCeiling(comparisonValues: Tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32]):
127
+ def for_range_from_activeGap1ndex_to_gap1ndexCeiling(comparisonValues: tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32]):
109
128
  return comparisonValues[-1] < gap1ndexCeiling
110
129
 
111
- def miniGapDo(gapToGapValues: Tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32]):
130
+ def miniGapDo(gapToGapValues: tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32]):
112
131
  gapToGapCountDimensionsGapped, gapToGapPotentialGaps, activeGapNumber, index = gapToGapValues
113
132
  gapToGapPotentialGaps = gapToGapPotentialGaps.at[activeGapNumber].set(gapToGapPotentialGaps.at[index].get())
114
133
  activeGapNumber = jax.numpy.where(jax.numpy.equal(gapToGapCountDimensionsGapped.at[gapToGapPotentialGaps.at[index].get()].get(), dimensionsTotal - unconstrainedLeaf), activeGapNumber + 1, activeGapNumber).astype(jax.numpy.uint32)
@@ -144,17 +163,17 @@ def foldingsJAX(leavesTotal: jaxtyping.UInt32, dimensionsTotal: jaxtyping.UInt32
144
163
  def incrementCondition(leafBelowSentinel, activeLeafNumber):
145
164
  return jax.numpy.logical_and(activeLeafNumber > leavesTotal, leafBelowSentinel == 1)
146
165
 
147
- def incrementDo(allValues: Tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32, jaxtyping.UInt32]):
166
+ def incrementDo(allValues: tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32, jaxtyping.UInt32]):
148
167
  foldingsSubTotal = allValues[5]
149
168
  foldingsSubTotal = leavesTotal + foldingsSubTotal
150
169
  return (allValues[0], allValues[1], allValues[2], allValues[3], allValues[4], foldingsSubTotal, allValues[6], allValues[7])
151
170
 
152
- def dao(allValues: Tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32, jaxtyping.UInt32]):
153
- def whileBacktrackingCondition(backtrackingValues: Tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32]):
171
+ def dao(allValues: tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32, jaxtyping.UInt32]):
172
+ def whileBacktrackingCondition(backtrackingValues: tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32]):
154
173
  comparand = backtrackingValues[2]
155
174
  return jax.numpy.logical_and(comparand > 0, jax.numpy.equal(activeGap1ndex, gapRangeStart.at[comparand - 1].get()))
156
175
 
157
- def whileBacktrackingDo(backtrackingValues: Tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32]):
176
+ def whileBacktrackingDo(backtrackingValues: tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32]):
158
177
  backtrackAbove, backtrackBelow, activeLeafNumber = backtrackingValues
159
178
 
160
179
  activeLeafNumber = activeLeafNumber - 1
@@ -166,7 +185,7 @@ def foldingsJAX(leavesTotal: jaxtyping.UInt32, dimensionsTotal: jaxtyping.UInt32
166
185
  def if_activeLeaf1ndex_greaterThan_0(activeLeafNumber):
167
186
  return activeLeafNumber > 0
168
187
 
169
- def if_activeLeaf1ndex_greaterThan_0_do(leafPlacementValues: Tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32]):
188
+ def if_activeLeaf1ndex_greaterThan_0_do(leafPlacementValues: tuple[jaxtyping.Array, jaxtyping.Array, jaxtyping.Array, jaxtyping.UInt32, jaxtyping.UInt32]):
170
189
  placeLeafAbove, placeLeafBelow, placeGapRangeStart, activeLeafNumber, activeGapNumber = leafPlacementValues
171
190
  activeGapNumber = activeGapNumber - 1
172
191
  placeLeafAbove = placeLeafAbove.at[activeLeafNumber].set(gapsWhere.at[activeGapNumber].get())
@@ -1,11 +1,25 @@
1
1
  """
2
2
  A generally faithful translation of the original Atlas Autocode code by W. F. Lunnon to Python using NumPy.
3
- W. F. Lunnon, Multi-dimensional map-folding, The Computer Journal, Volume 14, Issue 1, 1971, Pages 75-80, https://doi.org/10.1093/comjnl/14.1.75
3
+
4
+ This implementation transforms Lunnon's 1971 algorithm to leverage NumPy's array operations for improved
5
+ performance while maintaining algorithmic fidelity. It preserves the core logic and variable naming
6
+ conventions of the original algorithm but benefits from NumPy's vectorized operations and efficient
7
+ memory management.
8
+
9
+ Key characteristics:
10
+ - Uses NumPy arrays instead of Python lists for better memory efficiency
11
+ - Maintains the original algorithm structure and control flow
12
+ - Preserves variable naming for algorithmic clarity
13
+ - Offers significant performance improvements over pure Python implementations
14
+
15
+ Reference:
16
+ W. F. Lunnon, Multi-dimensional map-folding, The Computer Journal, Volume 14, Issue 1, 1971,
17
+ Pages 75-80, https://doi.org/10.1093/comjnl/14.1.75
4
18
  """
5
- from typing import List
19
+
6
20
  import numpy
7
21
 
8
- def foldings(p: List[int]) -> int:
22
+ def foldings(p: list[int]) -> int:
9
23
  """
10
24
  Run loop with (A, B) on each folding of a p[1] x ... x p[d] map, where A and B are the above and below vectors.
11
25
 
@@ -66,7 +80,7 @@ def foldings(p: List[int]) -> int:
66
80
  # D[i][l][m] = leaf connected to m in section i when inserting l;
67
81
 
68
82
  G: int = 0
69
- l: int = 1
83
+ l = 1
70
84
 
71
85
  # kick off with null folding
72
86
  while l > 0:
@@ -86,7 +100,7 @@ def foldings(p: List[int]) -> int:
86
100
  if D[i][l][l] == l:
87
101
  dd = dd + 1
88
102
  else:
89
- m: int = D[i][l][l]
103
+ m = D[i][l][l]
90
104
  while m != l:
91
105
  gap[gg] = m
92
106
  if count[m] == 0:
@@ -1,10 +1,22 @@
1
1
  """
2
2
  A largely faithful translation of the original Atlas Autocode code by W. F. Lunnon to Python using `while`.
3
- W. F. Lunnon, Multi-dimensional map-folding, The Computer Journal, Volume 14, Issue 1, 1971, Pages 75-80, https://doi.org/10.1093/comjnl/14.1.75
3
+
4
+ This implementation closely follows the structure and logic of Lunnon's 1971 paper, preserving the
5
+ variable names and core algorithm design. It uses while loops instead of Atlas Autocode's procedural
6
+ control structures, maintaining the imperative programming style of the original.
7
+
8
+ Key characteristics:
9
+ - Preserves original algorithm structure for historical accuracy
10
+ - Uses primarily scalar operations and explicit loops
11
+ - Maintains the original variable naming from Lunnon's work
12
+ - Provides a baseline for comparison against optimized implementations
13
+
14
+ Reference:
15
+ W. F. Lunnon, Multi-dimensional map-folding, The Computer Journal, Volume 14, Issue 1, 1971,
16
+ Pages 75-80, https://doi.org/10.1093/comjnl/14.1.75
4
17
  """
5
- from typing import Sequence
6
18
 
7
- def foldings(p: Sequence[int]) -> int:
19
+ def foldings(p: list[int]) -> int:
8
20
  """
9
21
  Run loop with (A, B) on each folding of a p[1] x ... x p[d] map, where A and B are the above and below vectors.
10
22
 
@@ -38,8 +50,8 @@ def foldings(p: Sequence[int]) -> int:
38
50
  # and later gap[gapter[l]] is the gap where leaf l is currently inserted
39
51
 
40
52
  P = [1] * (d + 1)
41
- C = [[0] * (n + 1) for dimension1 in range(d + 1)]
42
- D = [[[0] * (n + 1) for dimension2 in range(n + 1)] for dimension1 in range(d + 1)]
53
+ C = [[0] * (n + 1) for _dimension1 in range(d + 1)]
54
+ D = [[[0] * (n + 1) for _dimension2 in range(n + 1)] for _dimension1 in range(d + 1)]
43
55
 
44
56
  for i in range(1, d + 1):
45
57
  P[i] = P[i - 1] * p[i - 1]
@@ -65,7 +77,7 @@ def foldings(p: Sequence[int]) -> int:
65
77
  # D[i][l][m] = leaf connected to m in section i when inserting l;
66
78
 
67
79
  G: int = 0
68
- l: int = 1
80
+ l = 1
69
81
 
70
82
  # kick off with null folding
71
83
  while l > 0:
@@ -84,7 +96,7 @@ def foldings(p: Sequence[int]) -> int:
84
96
  if D[i][l][l] == l:
85
97
  dd = dd + 1
86
98
  else:
87
- m: int = D[i][l][l]
99
+ m = D[i][l][l]
88
100
  while m != l:
89
101
  gap[gg] = m
90
102
  if count[m] == 0:
@@ -1,6 +1,23 @@
1
+ """
2
+ Alternative algorithm entry point implementation for Lunnon's map folding algorithm.
3
+
4
+ This implementation demonstrates an interesting algorithmic variation where the main processing
5
+ loop is "rotated" to enter at a different point in the execution flow. Specifically, it's
6
+ structured to enter at the modulo operator rather than the traditional starting point.
7
+
8
+ Key characteristics:
9
+ - Restructures the control flow by reorganizing the entry point of the algorithm
10
+ - Separates preparation work from the main computational loop
11
+ - Uses explicit variable naming with index constants for clarity
12
+ - Demonstrates how the same algorithm can be approached from different entry points
13
+
14
+ Note: This implementation is intentionally incomplete and requires supporting code from
15
+ other modules to function. It serves primarily as a demonstration of how algorithmic
16
+ structure can be creatively redesigned while maintaining the core computational approach.
17
+ """
18
+
1
19
  from mapFolding import outfitFoldings
2
20
  from numba import njit
3
- from typing import List
4
21
  import numpy
5
22
  from numpy.typing import NDArray
6
23
 
@@ -42,7 +59,7 @@ tricky = [
42
59
 
43
60
  COUNTindicesStatic = len(tricky)
44
61
 
45
- def countFolds(listDimensions: List[int]):
62
+ def countFolds(listDimensions: list[int]):
46
63
  static = numpy.zeros(COUNTindicesStatic, dtype=numpy.int64)
47
64
 
48
65
  listDimensions, static[leavesTotal], D, track,gapsWhere = outfitFoldings(listDimensions)
@@ -55,7 +72,7 @@ def countFolds(listDimensions: List[int]):
55
72
  return foldingsTotal
56
73
 
57
74
  # @recordBenchmarks()
58
- def _sherpa(track: NDArray, gap: NDArray, static: NDArray, D: NDArray, p: List[int]):
75
+ def _sherpa(track: NDArray, gap: NDArray, static: NDArray, D: NDArray, p: list[int]):
59
76
  """Performance critical section that counts foldings.
60
77
 
61
78
  Parameters: