mapFolding 0.8.1__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.
- mapFolding/basecamp.py +2 -2
- mapFolding/beDRY.py +24 -31
- mapFolding/oeis.py +2 -2
- mapFolding/reference/__init__.py +38 -0
- mapFolding/reference/flattened.py +20 -2
- mapFolding/reference/hunterNumba.py +24 -0
- mapFolding/reference/irvineJavaPort.py +12 -0
- mapFolding/reference/{jax.py → jaxCount.py} +46 -27
- mapFolding/reference/lunnanNumpy.py +16 -1
- mapFolding/reference/lunnanWhile.py +15 -1
- mapFolding/reference/rotatedEntryPoint.py +18 -0
- mapFolding/reference/total_countPlus1vsPlusN.py +226 -203
- mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py +5 -8
- mapFolding/someAssemblyRequired/transformDataStructures.py +10 -4
- mapFolding/someAssemblyRequired/transformationTools.py +19 -28
- mapFolding/theSSOT.py +70 -121
- {mapfolding-0.8.1.dist-info → mapfolding-0.8.2.dist-info}/METADATA +54 -30
- mapfolding-0.8.2.dist-info/RECORD +39 -0
- {mapfolding-0.8.1.dist-info → mapfolding-0.8.2.dist-info}/WHEEL +1 -1
- tests/conftest.py +43 -33
- tests/test_computations.py +7 -7
- tests/test_other.py +2 -2
- mapfolding-0.8.1.dist-info/RECORD +0 -39
- {mapfolding-0.8.1.dist-info → mapfolding-0.8.2.dist-info}/entry_points.txt +0 -0
- {mapfolding-0.8.1.dist-info → mapfolding-0.8.2.dist-info}/licenses/LICENSE +0 -0
- {mapfolding-0.8.1.dist-info → mapfolding-0.8.2.dist-info}/top_level.txt +0 -0
mapFolding/basecamp.py
CHANGED
|
@@ -15,7 +15,7 @@ implementation, and optional persistence of results.
|
|
|
15
15
|
from collections.abc import Sequence
|
|
16
16
|
from mapFolding.beDRY import outfitCountFolds, setCPUlimit, validateListDimensions
|
|
17
17
|
from mapFolding.filesystem import getPathFilenameFoldsTotal, saveFoldsTotal
|
|
18
|
-
from mapFolding.theSSOT import ComputationState, getPackageDispatcher
|
|
18
|
+
from mapFolding.theSSOT import ComputationState, getPackageDispatcher, The
|
|
19
19
|
from os import PathLike
|
|
20
20
|
from pathlib import Path
|
|
21
21
|
|
|
@@ -54,7 +54,7 @@ def countFolds(listDimensions: Sequence[int]
|
|
|
54
54
|
If you want to compute a large `foldsTotal`, dividing the computation into tasks is usually a bad idea. Dividing the algorithm into tasks is inherently inefficient: efficient division into tasks means there would be no overlap in the work performed by each task. When dividing this algorithm, the amount of overlap is between 50% and 90% by all tasks: at least 50% of the work done by every task must be done by _all_ tasks. If you improve the computation time, it will only change by -10 to -50% depending on (at the very least) the ratio of the map dimensions and the number of leaves. If an undivided computation would take 10 hours on your computer, for example, the computation will still take at least 5 hours but you might reduce the time to 9 hours. Most of the time, however, you will increase the computation time. If logicalCores >= leavesTotal, it will probably be faster. If logicalCores <= 2 * leavesTotal, it will almost certainly be slower for all map dimensions.
|
|
55
55
|
"""
|
|
56
56
|
mapShape: tuple[int, ...] = validateListDimensions(listDimensions)
|
|
57
|
-
concurrencyLimit: int = setCPUlimit(CPUlimit)
|
|
57
|
+
concurrencyLimit: int = setCPUlimit(CPUlimit, The.concurrencyPackage)
|
|
58
58
|
computationStateInitialized: ComputationState = outfitCountFolds(mapShape, computationDivisions, concurrencyLimit)
|
|
59
59
|
|
|
60
60
|
dispatcherCallableProxy = getPackageDispatcher()
|
mapFolding/beDRY.py
CHANGED
|
@@ -15,12 +15,15 @@ particularly for initializing computation state, validating inputs, and creating
|
|
|
15
15
|
structures needed by the folding algorithms.
|
|
16
16
|
"""
|
|
17
17
|
from collections.abc import Sequence
|
|
18
|
-
from mapFolding.theSSOT import
|
|
18
|
+
from mapFolding.theSSOT import ComputationState
|
|
19
|
+
from numpy import dtype as numpy_dtype, integer, ndarray
|
|
19
20
|
from sys import maxsize as sysMaxsize
|
|
20
|
-
from typing import Any
|
|
21
|
+
from typing import Any, TypeVar
|
|
21
22
|
from Z0Z_tools import defineConcurrencyLimit, intInnit, oopsieKwargsie
|
|
22
23
|
import numpy
|
|
23
24
|
|
|
25
|
+
numpyIntegerType = TypeVar('numpyIntegerType', bound=integer[Any])
|
|
26
|
+
|
|
24
27
|
def getLeavesTotal(mapShape: tuple[int, ...]) -> int:
|
|
25
28
|
productDimensions = 1
|
|
26
29
|
for dimension in mapShape:
|
|
@@ -81,25 +84,16 @@ def getTaskDivisions(computationDivisions: int | str | None, concurrencyLimit: i
|
|
|
81
84
|
raise ValueError(f"Problem: `taskDivisions`, ({taskDivisions}), is greater than `leavesTotal`, ({leavesTotal}), which will cause duplicate counting of the folds.\n\nChallenge: you cannot directly set `taskDivisions` or `leavesTotal`. They are derived from parameters that may or may not still be named `computationDivisions`, `CPUlimit` , and `listDimensions` and from dubious-quality Python code.")
|
|
82
85
|
return int(max(0, taskDivisions))
|
|
83
86
|
|
|
84
|
-
def
|
|
85
|
-
"""An imperfect way to reduce code duplication."""
|
|
86
|
-
if 'numpy' == getDatatypePackage():
|
|
87
|
-
numpyDtype = datatype or getNumpyDtypeDefault()
|
|
88
|
-
else:
|
|
89
|
-
raise NotImplementedError("Somebody done broke it.")
|
|
90
|
-
return numpyDtype
|
|
91
|
-
|
|
92
|
-
def makeConnectionGraph(mapShape: tuple[int, ...], leavesTotal: int, datatype: type[numpy.signedinteger[Any]] | None = None) -> Array3D:
|
|
93
|
-
numpyDtype = interpretParameter_datatype(datatype)
|
|
87
|
+
def makeConnectionGraph(mapShape: tuple[int, ...], leavesTotal: int, datatype: type[numpyIntegerType]) -> ndarray[tuple[int, int, int], numpy_dtype[numpyIntegerType]]:
|
|
94
88
|
dimensionsTotal = len(mapShape)
|
|
95
|
-
cumulativeProduct = numpy.multiply.accumulate([1] + list(mapShape), dtype=
|
|
96
|
-
arrayDimensions = numpy.array(mapShape, dtype=
|
|
97
|
-
coordinateSystem = numpy.zeros((dimensionsTotal, leavesTotal + 1), dtype=
|
|
89
|
+
cumulativeProduct = numpy.multiply.accumulate([1] + list(mapShape), dtype=datatype)
|
|
90
|
+
arrayDimensions = numpy.array(mapShape, dtype=datatype)
|
|
91
|
+
coordinateSystem = numpy.zeros((dimensionsTotal, leavesTotal + 1), dtype=datatype)
|
|
98
92
|
for indexDimension in range(dimensionsTotal):
|
|
99
93
|
for leaf1ndex in range(1, leavesTotal + 1):
|
|
100
94
|
coordinateSystem[indexDimension, leaf1ndex] = (((leaf1ndex - 1) // cumulativeProduct[indexDimension]) % arrayDimensions[indexDimension] + 1)
|
|
101
95
|
|
|
102
|
-
connectionGraph = numpy.zeros((dimensionsTotal, leavesTotal + 1, leavesTotal + 1), dtype=
|
|
96
|
+
connectionGraph = numpy.zeros((dimensionsTotal, leavesTotal + 1, leavesTotal + 1), dtype=datatype)
|
|
103
97
|
for indexDimension in range(dimensionsTotal):
|
|
104
98
|
for activeLeaf1ndex in range(1, leavesTotal + 1):
|
|
105
99
|
for connectee1ndex in range(1, activeLeaf1ndex + 1):
|
|
@@ -116,9 +110,8 @@ def makeConnectionGraph(mapShape: tuple[int, ...], leavesTotal: int, datatype: t
|
|
|
116
110
|
connectionGraph[indexDimension, activeLeaf1ndex, connectee1ndex] = connectee1ndex + cumulativeProduct[indexDimension]
|
|
117
111
|
return connectionGraph
|
|
118
112
|
|
|
119
|
-
def makeDataContainer(shape: int | tuple[int, ...], datatype: type[
|
|
120
|
-
|
|
121
|
-
return numpy.zeros(shape, dtype=numpyDtype)
|
|
113
|
+
def makeDataContainer(shape: int | tuple[int, ...], datatype: type[numpyIntegerType]) -> ndarray[Any, numpy_dtype[numpyIntegerType]]:
|
|
114
|
+
return numpy.zeros(shape, dtype=datatype)
|
|
122
115
|
|
|
123
116
|
def outfitCountFolds(mapShape: tuple[int, ...], computationDivisions: int | str | None = None, concurrencyLimit: int = 1) -> ComputationState:
|
|
124
117
|
leavesTotal = getLeavesTotal(mapShape)
|
|
@@ -126,7 +119,7 @@ def outfitCountFolds(mapShape: tuple[int, ...], computationDivisions: int | str
|
|
|
126
119
|
computationStateInitialized = ComputationState(mapShape, leavesTotal, taskDivisions, concurrencyLimit)
|
|
127
120
|
return computationStateInitialized
|
|
128
121
|
|
|
129
|
-
def setCPUlimit(CPUlimit: Any | None) -> int:
|
|
122
|
+
def setCPUlimit(CPUlimit: Any | None, concurrencyPackage: str | None = None) -> int:
|
|
130
123
|
"""Sets CPU limit for concurrent operations.
|
|
131
124
|
|
|
132
125
|
If the concurrency is managed by `numba`, the maximum number of CPUs is retrieved from `numba.get_num_threads()` and not by polling the hardware. Therefore, if there are
|
|
@@ -154,17 +147,17 @@ def setCPUlimit(CPUlimit: Any | None) -> int:
|
|
|
154
147
|
if not (CPUlimit is None or isinstance(CPUlimit, (bool, int, float))):
|
|
155
148
|
CPUlimit = oopsieKwargsie(CPUlimit)
|
|
156
149
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
150
|
+
match concurrencyPackage:
|
|
151
|
+
case 'multiprocessing' | None:
|
|
152
|
+
# When to use multiprocessing.set_start_method https://github.com/hunterhogan/mapFolding/issues/6
|
|
153
|
+
concurrencyLimit: int = defineConcurrencyLimit(CPUlimit)
|
|
154
|
+
case 'numba':
|
|
155
|
+
from numba import get_num_threads, set_num_threads
|
|
156
|
+
concurrencyLimit = defineConcurrencyLimit(CPUlimit, get_num_threads())
|
|
157
|
+
set_num_threads(concurrencyLimit)
|
|
158
|
+
concurrencyLimit = get_num_threads()
|
|
159
|
+
case _:
|
|
160
|
+
raise NotImplementedError(f"I received {concurrencyPackage=} but I don't know what to do with that.")
|
|
168
161
|
return concurrencyLimit
|
|
169
162
|
|
|
170
163
|
def validateListDimensions(listDimensions: Sequence[int]) -> tuple[int, ...]:
|
mapFolding/oeis.py
CHANGED
|
@@ -16,7 +16,7 @@ literature and extend sequences beyond their currently known terms.
|
|
|
16
16
|
"""
|
|
17
17
|
from collections.abc import Callable
|
|
18
18
|
from datetime import datetime, timedelta
|
|
19
|
-
from mapFolding.theSSOT import
|
|
19
|
+
from mapFolding.theSSOT import The
|
|
20
20
|
from pathlib import Path
|
|
21
21
|
from typing import Any, Final, TYPE_CHECKING
|
|
22
22
|
import argparse
|
|
@@ -38,7 +38,7 @@ cacheDays = 7
|
|
|
38
38
|
"""
|
|
39
39
|
Section: make `settingsOEIS`"""
|
|
40
40
|
|
|
41
|
-
pathCache: Path =
|
|
41
|
+
pathCache: Path = The.pathPackage / ".cache"
|
|
42
42
|
|
|
43
43
|
class SettingsOEIS(TypedDict):
|
|
44
44
|
description: str
|
mapFolding/reference/__init__.py
CHANGED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Historical and reference implementations of map-folding algorithms.
|
|
3
|
+
|
|
4
|
+
This directory contains various implementations of the map-folding algorithm,
|
|
5
|
+
serving both as historical references and as benchmarks for performance comparison.
|
|
6
|
+
These implementations range from direct translations of Lunnon's original 1971 code
|
|
7
|
+
to highly specialized versions using modern optimization techniques.
|
|
8
|
+
|
|
9
|
+
Categories of reference implementations:
|
|
10
|
+
|
|
11
|
+
1. Historical transcripts:
|
|
12
|
+
- foldings.txt - Original algorithm from Lunnon's 1971 paper
|
|
13
|
+
- foldings.AA - Reconstructed Atlas Autocode version with corrections
|
|
14
|
+
|
|
15
|
+
2. Direct translations:
|
|
16
|
+
- lunnanWhile.py - Python translation using while loops
|
|
17
|
+
- lunnanNumpy.py - NumPy-based translation with array operations
|
|
18
|
+
|
|
19
|
+
3. Alternative implementations:
|
|
20
|
+
- irvineJavaPort.py - Port from Sean A. Irvine's Java implementation
|
|
21
|
+
- hunterNumba.py - Numba-optimized implementation
|
|
22
|
+
- jaxCount.py - JAX implementation for GPU acceleration
|
|
23
|
+
- flattened.py - Semantically decomposed version with operation grouping
|
|
24
|
+
|
|
25
|
+
4. Specialized variants:
|
|
26
|
+
- total_countPlus1vsPlusN.py - Optimized counting with different increment strategies
|
|
27
|
+
- rotatedEntryPoint.py - Alternative entry point implementation (demonstration)
|
|
28
|
+
|
|
29
|
+
These reference implementations are valuable for:
|
|
30
|
+
- Understanding the algorithm's historical development
|
|
31
|
+
- Comparing performance characteristics across implementation strategies
|
|
32
|
+
- Studying optimization techniques and their effects
|
|
33
|
+
- Verifying the correctness of the core algorithm against known solutions
|
|
34
|
+
|
|
35
|
+
Note: These implementations are for reference only and not used in the production
|
|
36
|
+
code path of the package. The active implementation resides in theDao.py with
|
|
37
|
+
optimized variants generated by the someAssemblyRequired framework.
|
|
38
|
+
"""
|
|
@@ -1,5 +1,23 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
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
|
+
|
|
3
21
|
from collections.abc import Sequence
|
|
4
22
|
from numpy import integer
|
|
5
23
|
from numpy.typing import NDArray
|
|
@@ -1,3 +1,27 @@
|
|
|
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
|
+
|
|
1
25
|
from typing import Any
|
|
2
26
|
import numba
|
|
3
27
|
import numpy
|
|
@@ -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
|
+
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
|
+
|
|
5
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
|
-
"""
|
|
2
|
-
I
|
|
3
|
-
|
|
4
|
-
|
|
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:
|
|
12
|
-
listDimensionsPositive:
|
|
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
|
-
|
|
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:
|
|
47
|
+
def while_activeLeaf1ndex_greaterThan_0(comparisonValues: tuple):
|
|
29
48
|
comparand = comparisonValues[6]
|
|
30
49
|
return comparand > 0
|
|
31
50
|
|
|
32
|
-
def countFoldings(allValues:
|
|
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:
|
|
48
|
-
def for_dimension1ndex_in_range_1_to_dimensionsTotalPlus1(comparisonValues:
|
|
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:
|
|
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:
|
|
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:
|
|
61
|
-
def while_leaf1ndexConnectee_notEquals_activeLeaf1ndex(comparisonValues:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
153
|
-
def whileBacktrackingCondition(backtrackingValues:
|
|
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:
|
|
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:
|
|
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,7 +1,22 @@
|
|
|
1
1
|
"""
|
|
2
2
|
A generally faithful translation of the original Atlas Autocode code by W. F. Lunnon to Python using NumPy.
|
|
3
|
-
|
|
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
|
"""
|
|
19
|
+
|
|
5
20
|
import numpy
|
|
6
21
|
|
|
7
22
|
def foldings(p: list[int]) -> int:
|
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
"""
|
|
2
2
|
A largely faithful translation of the original Atlas Autocode code by W. F. Lunnon to Python using `while`.
|
|
3
|
-
|
|
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
|
"""
|
|
18
|
+
|
|
5
19
|
def foldings(p: list[int]) -> int:
|
|
6
20
|
"""
|
|
7
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.
|
|
@@ -1,3 +1,21 @@
|
|
|
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
21
|
import numpy
|