mapFolding 0.8.0__py3-none-any.whl → 0.8.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mapFolding/__init__.py +33 -4
- mapFolding/basecamp.py +14 -0
- mapFolding/beDRY.py +16 -1
- mapFolding/filesystem.py +124 -90
- mapFolding/noHomeYet.py +12 -0
- mapFolding/oeis.py +16 -1
- mapFolding/reference/__init__.py +0 -0
- mapFolding/reference/flattened.py +46 -45
- mapFolding/reference/hunterNumba.py +4 -4
- mapFolding/reference/irvineJavaPort.py +1 -1
- mapFolding/reference/lunnanNumpy.py +3 -4
- mapFolding/reference/lunnanWhile.py +5 -7
- mapFolding/reference/rotatedEntryPoint.py +2 -3
- mapFolding/someAssemblyRequired/__init__.py +29 -0
- mapFolding/someAssemblyRequired/getLLVMforNoReason.py +32 -14
- mapFolding/someAssemblyRequired/ingredientsNumba.py +22 -1
- mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py +196 -0
- mapFolding/someAssemblyRequired/synthesizeNumbaJobVESTIGIAL.py +3 -4
- mapFolding/someAssemblyRequired/transformDataStructures.py +162 -0
- mapFolding/someAssemblyRequired/transformationTools.py +216 -199
- mapFolding/theDao.py +19 -5
- mapFolding/theSSOT.py +19 -1
- {mapfolding-0.8.0.dist-info → mapfolding-0.8.1.dist-info}/METADATA +50 -44
- mapfolding-0.8.1.dist-info/RECORD +39 -0
- {mapfolding-0.8.0.dist-info → mapfolding-0.8.1.dist-info}/WHEEL +1 -1
- mapFolding/reference/lunnan.py +0 -153
- mapFolding/someAssemblyRequired/Z0Z_workbench.py +0 -350
- mapFolding/someAssemblyRequired/synthesizeDataConverters.py +0 -117
- mapFolding/syntheticModules/numbaCountHistoricalExample.py +0 -158
- mapFolding/syntheticModules/numba_doTheNeedfulHistoricalExample.py +0 -13
- mapfolding-0.8.0.dist-info/RECORD +0 -41
- {mapfolding-0.8.0.dist-info → mapfolding-0.8.1.dist-info}/entry_points.txt +0 -0
- {mapfolding-0.8.0.dist-info → mapfolding-0.8.1.dist-info/licenses}/LICENSE +0 -0
- {mapfolding-0.8.0.dist-info → mapfolding-0.8.1.dist-info}/top_level.txt +0 -0
mapFolding/__init__.py
CHANGED
|
@@ -1,9 +1,38 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Map folding enumeration and counting algorithms with optimization capabilities.
|
|
3
|
+
|
|
4
|
+
This package implements algorithms to count and enumerate the various ways
|
|
5
|
+
a rectangular map can be folded, based on the mathematical problem described
|
|
6
|
+
in Lunnon's 1971 paper. It provides multiple layers of functionality, from
|
|
7
|
+
high-level user interfaces to low-level algorithmic optimizations and code
|
|
8
|
+
transformation tools.
|
|
9
|
+
|
|
10
|
+
Core modules:
|
|
11
|
+
- basecamp: Public API with simplified interfaces for end users
|
|
12
|
+
- theDao: Core computational algorithm using a functional state-transformation approach
|
|
13
|
+
- beDRY: Utility functions for common operations and parameter management
|
|
14
|
+
- theSSOT: Single Source of Truth for configuration, types, and state management
|
|
15
|
+
- oeis: Interface to the Online Encyclopedia of Integer Sequences for known results
|
|
16
|
+
|
|
17
|
+
Extended functionality:
|
|
18
|
+
- someAssemblyRequired: Code transformation framework that optimizes the core algorithm
|
|
19
|
+
through AST manipulation, dataclass transformation, and compilation techniques
|
|
20
|
+
|
|
21
|
+
Special directories:
|
|
22
|
+
- .cache/: Stores cached data from external sources like OEIS to improve performance
|
|
23
|
+
- syntheticModules/: Contains dynamically generated, optimized implementations of the
|
|
24
|
+
core algorithm created by the code transformation framework
|
|
25
|
+
|
|
26
|
+
This package strives to balance algorithm readability and understandability with
|
|
27
|
+
high-performance computation capabilities, allowing users to compute map folding
|
|
28
|
+
totals for larger dimensions than previously feasible.
|
|
29
|
+
"""
|
|
1
30
|
from mapFolding.basecamp import countFolds as countFolds
|
|
2
31
|
from mapFolding.oeis import clearOEIScache, getOEISids, OEIS_for_n
|
|
3
32
|
|
|
4
33
|
__all__ = [
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
34
|
+
'clearOEIScache',
|
|
35
|
+
'countFolds',
|
|
36
|
+
'getOEISids',
|
|
37
|
+
'OEIS_for_n',
|
|
9
38
|
]
|
mapFolding/basecamp.py
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Public API for the map folding algorithm with simplified interface.
|
|
3
|
+
|
|
4
|
+
This module provides the main entry point for users of the mapFolding package,
|
|
5
|
+
abstracting away the complexities of the computational algorithm. It offers
|
|
6
|
+
a high-level interface to count the total number of possible ways to fold
|
|
7
|
+
a rectangular map of specified dimensions, with options for customizing the
|
|
8
|
+
computation process and saving results.
|
|
9
|
+
|
|
10
|
+
The primary function is countFolds, which handles parameter validation,
|
|
11
|
+
computation state management, dispatching to the appropriate algorithm
|
|
12
|
+
implementation, and optional persistence of results.
|
|
13
|
+
"""
|
|
14
|
+
|
|
1
15
|
from collections.abc import Sequence
|
|
2
16
|
from mapFolding.beDRY import outfitCountFolds, setCPUlimit, validateListDimensions
|
|
3
17
|
from mapFolding.filesystem import getPathFilenameFoldsTotal, saveFoldsTotal
|
mapFolding/beDRY.py
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Utility functions for maintaining DRY (Don't Repeat Yourself) principles in the mapFolding package.
|
|
3
|
+
|
|
4
|
+
This module provides a collection of helper functions that abstract common operations needed
|
|
5
|
+
throughout the package, preventing code duplication and ensuring consistency. The functions
|
|
6
|
+
manage core aspects of the computation process, including:
|
|
7
|
+
|
|
8
|
+
1. Resource allocation and system limits management
|
|
9
|
+
2. Data structure initialization and manipulation
|
|
10
|
+
3. Parameter validation and interpretation
|
|
11
|
+
4. Construction of specialized arrays and matrices for the folding algorithm
|
|
12
|
+
|
|
13
|
+
The functions in this module serve as a relatively stable API for other modules to use,
|
|
14
|
+
particularly for initializing computation state, validating inputs, and creating data
|
|
15
|
+
structures needed by the folding algorithms.
|
|
16
|
+
"""
|
|
2
17
|
from collections.abc import Sequence
|
|
3
18
|
from mapFolding.theSSOT import Array3D, ComputationState, getDatatypePackage, getNumpyDtypeDefault
|
|
4
19
|
from sys import maxsize as sysMaxsize
|
mapFolding/filesystem.py
CHANGED
|
@@ -1,95 +1,129 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Filesystem utilities for managing map folding computation results.
|
|
3
|
+
|
|
4
|
+
This module provides functions for standardized handling of files related to the mapFolding
|
|
5
|
+
package, with a focus on saving, retrieving, and naming computation results. It implements
|
|
6
|
+
consistent naming conventions and path resolution strategies to ensure that:
|
|
7
|
+
|
|
8
|
+
1. Computation results are stored in a predictable location
|
|
9
|
+
2. Filenames follow a consistent pattern based on map dimensions
|
|
10
|
+
3. Results can be reliably retrieved for future reference
|
|
11
|
+
4. The system handles file operations safely with appropriate error handling
|
|
12
|
+
|
|
13
|
+
The module serves as the interface between the computational components of the package
|
|
14
|
+
and the filesystem, abstracting away the details of file operations and path management.
|
|
15
|
+
"""
|
|
2
16
|
from pathlib import Path, PurePath
|
|
3
17
|
from typing import Any
|
|
18
|
+
from os import PathLike
|
|
4
19
|
import os
|
|
5
20
|
|
|
6
21
|
def getFilenameFoldsTotal(mapShape: tuple[int, ...]) -> str:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
22
|
+
"""
|
|
23
|
+
Create a standardized filename for a computed `foldsTotal` value.
|
|
24
|
+
|
|
25
|
+
This function generates a consistent, filesystem-safe filename based on map dimensions.
|
|
26
|
+
Standardizing filenames ensures that results can be reliably stored and retrieved,
|
|
27
|
+
avoiding potential filesystem incompatibilities or Python naming restrictions.
|
|
28
|
+
|
|
29
|
+
Parameters:
|
|
30
|
+
mapShape: A sequence of integers representing the dimensions of the map.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
filenameFoldsTotal: A filename string in format 'pMxN.foldsTotal' where M,N are sorted dimensions.
|
|
34
|
+
|
|
35
|
+
Notes:
|
|
36
|
+
The filename format ensures:
|
|
37
|
+
- No spaces in the filename
|
|
38
|
+
- Safe filesystem characters
|
|
39
|
+
- Unique extension (.foldsTotal)
|
|
40
|
+
- Python-safe strings (no starting with numbers, no reserved words)
|
|
41
|
+
- The 'p' prefix preserves compatibility with Lunnan's original code.
|
|
42
|
+
"""
|
|
43
|
+
return 'p' + 'x'.join(str(dimension) for dimension in sorted(mapShape)) + '.foldsTotal'
|
|
44
|
+
|
|
45
|
+
def getPathFilenameFoldsTotal(mapShape: tuple[int, ...], pathLikeWriteFoldsTotal: str | PathLike[str] | None = None) -> Path:
|
|
46
|
+
"""
|
|
47
|
+
Get a standardized path and filename for the computed foldsTotal value.
|
|
48
|
+
|
|
49
|
+
This function resolves paths for storing computation results, handling different
|
|
50
|
+
input types including directories, absolute paths, or relative paths. It ensures
|
|
51
|
+
that all parent directories exist in the resulting path.
|
|
52
|
+
|
|
53
|
+
Parameters:
|
|
54
|
+
mapShape: List of dimensions for the map folding problem.
|
|
55
|
+
pathLikeWriteFoldsTotal (getPathJobRootDEFAULT): Path, filename, or relative path and filename.
|
|
56
|
+
If None, uses default path. If a directory, appends standardized filename.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
pathFilenameFoldsTotal: Absolute path and filename for storing the foldsTotal value.
|
|
60
|
+
|
|
61
|
+
Notes:
|
|
62
|
+
The function creates any necessary directories in the path if they don't exist.
|
|
63
|
+
"""
|
|
64
|
+
from mapFolding.theSSOT import getPathJobRootDEFAULT
|
|
65
|
+
|
|
66
|
+
if pathLikeWriteFoldsTotal is None:
|
|
67
|
+
pathFilenameFoldsTotal = getPathJobRootDEFAULT() / getFilenameFoldsTotal(mapShape)
|
|
68
|
+
else:
|
|
69
|
+
pathLikeSherpa = Path(pathLikeWriteFoldsTotal)
|
|
70
|
+
if pathLikeSherpa.is_dir():
|
|
71
|
+
pathFilenameFoldsTotal = pathLikeSherpa / getFilenameFoldsTotal(mapShape)
|
|
72
|
+
elif pathLikeSherpa.is_file() and pathLikeSherpa.is_absolute():
|
|
73
|
+
pathFilenameFoldsTotal = pathLikeSherpa
|
|
74
|
+
else:
|
|
75
|
+
pathFilenameFoldsTotal = getPathJobRootDEFAULT() / pathLikeSherpa
|
|
76
|
+
|
|
77
|
+
pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
|
|
78
|
+
return pathFilenameFoldsTotal
|
|
79
|
+
|
|
80
|
+
def saveFoldsTotal(pathFilename: str | PathLike[str], foldsTotal: int) -> None:
|
|
81
|
+
"""
|
|
82
|
+
Save `foldsTotal` value to disk with multiple fallback mechanisms.
|
|
83
|
+
|
|
84
|
+
This function attempts to save the computed `foldsTotal` value to the specified
|
|
85
|
+
location, with backup strategies in case the primary save attempt fails.
|
|
86
|
+
The robustness is critical since these computations may take days to complete.
|
|
87
|
+
|
|
88
|
+
Parameters:
|
|
89
|
+
pathFilename: Target save location for the `foldsTotal` value
|
|
90
|
+
foldsTotal: The computed value to save
|
|
91
|
+
|
|
92
|
+
Notes:
|
|
93
|
+
If the primary save fails, the function will attempt alternative save methods:
|
|
94
|
+
1. Print the value prominently to stdout
|
|
95
|
+
2. Create a fallback file in the current working directory
|
|
96
|
+
3. As a last resort, simply print the value
|
|
97
|
+
"""
|
|
98
|
+
try:
|
|
99
|
+
pathFilenameFoldsTotal = Path(pathFilename)
|
|
100
|
+
pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
|
|
101
|
+
pathFilenameFoldsTotal.write_text(str(foldsTotal))
|
|
102
|
+
except Exception as ERRORmessage:
|
|
103
|
+
try:
|
|
104
|
+
print(f"\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n\n{foldsTotal=}\n\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n")
|
|
105
|
+
print(ERRORmessage)
|
|
106
|
+
print(f"\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n\n{foldsTotal=}\n\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n")
|
|
107
|
+
randomnessPlanB = (int(str(foldsTotal).strip()[-1]) + 1) * ['YO_']
|
|
108
|
+
filenameInfixUnique = ''.join(randomnessPlanB)
|
|
109
|
+
pathFilenamePlanB = os.path.join(os.getcwd(), 'foldsTotal' + filenameInfixUnique + '.txt')
|
|
110
|
+
writeStreamFallback = open(pathFilenamePlanB, 'w')
|
|
111
|
+
writeStreamFallback.write(str(foldsTotal))
|
|
112
|
+
writeStreamFallback.close()
|
|
113
|
+
print(str(pathFilenamePlanB))
|
|
114
|
+
except Exception:
|
|
115
|
+
print(foldsTotal)
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
def writeStringToHere(this: str, pathFilename: str | PathLike[Any] | PurePath) -> None:
|
|
119
|
+
"""
|
|
120
|
+
Write a string to a file, creating parent directories if needed.
|
|
121
|
+
|
|
122
|
+
Parameters:
|
|
123
|
+
this: The string content to write to the file
|
|
124
|
+
pathFilename: The target file path where the string should be written
|
|
125
|
+
"""
|
|
126
|
+
pathFilename = Path(pathFilename)
|
|
127
|
+
pathFilename.parent.mkdir(parents=True, exist_ok=True)
|
|
128
|
+
pathFilename.write_text(str(this))
|
|
129
|
+
return None
|
mapFolding/noHomeYet.py
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Interface for retrieving known map folding totals from OEIS (Online Encyclopedia of Integer Sequences).
|
|
3
|
+
|
|
4
|
+
This module provides utilities for accessing pre-computed map folding totals that are known
|
|
5
|
+
from mathematical literature and stored in the OEIS. The functions cache results for
|
|
6
|
+
performance and provide lookups based on map dimensions.
|
|
7
|
+
|
|
8
|
+
The main functions are:
|
|
9
|
+
- makeDictionaryFoldsTotalKnown: Creates a dictionary of known folding totals indexed by map dimensions
|
|
10
|
+
- getFoldsTotalKnown: Retrieves the folding total for a specific map shape, returning -1 if unknown
|
|
11
|
+
"""
|
|
12
|
+
|
|
1
13
|
from functools import cache
|
|
2
14
|
from mapFolding.oeis import settingsOEIS
|
|
3
15
|
|
mapFolding/oeis.py
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Interface to The Online Encyclopedia of Integer Sequences (OEIS) for map folding sequences.
|
|
3
|
+
|
|
4
|
+
This module provides a comprehensive interface for accessing and utilizing integer sequences
|
|
5
|
+
from the OEIS that relate to map folding problems. It implements functionality to:
|
|
6
|
+
|
|
7
|
+
1. Retrieve sequence data from OEIS with local caching for performance
|
|
8
|
+
2. Map sequence indices to corresponding map shapes based on sequence definitions
|
|
9
|
+
3. Provide a command-line interface for sequence lookups
|
|
10
|
+
4. Execute map folding computations for sequence terms not available in OEIS
|
|
11
|
+
|
|
12
|
+
The module maintains a registry of implemented OEIS sequences (A001415-A001418, A195646)
|
|
13
|
+
with their metadata, known values, and functions to convert between sequence indices and
|
|
14
|
+
map dimensions. This allows the package to validate results against established mathematical
|
|
15
|
+
literature and extend sequences beyond their currently known terms.
|
|
16
|
+
"""
|
|
2
17
|
from collections.abc import Callable
|
|
3
18
|
from datetime import datetime, timedelta
|
|
4
19
|
from mapFolding.theSSOT import thePathPackage
|
|
File without changes
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
"""The algorithm flattened into semantic sections.
|
|
2
2
|
This version is not maintained, so you may see differences from the current version."""
|
|
3
|
+
from collections.abc import Sequence
|
|
3
4
|
from numpy import integer
|
|
4
5
|
from numpy.typing import NDArray
|
|
5
|
-
from typing import
|
|
6
|
+
from typing import Any, Final, TypedDict
|
|
6
7
|
import enum
|
|
7
8
|
import numpy
|
|
8
9
|
import sys
|
|
9
10
|
|
|
10
|
-
def countFolds(listDimensions: Sequence[int], computationDivisions = None, CPUlimit:
|
|
11
|
-
def doWhile():
|
|
11
|
+
def countFolds(listDimensions: Sequence[int], computationDivisions: int | str | None = None, CPUlimit: int | float | bool | None = None) -> int:
|
|
12
|
+
def doWhile() -> None:
|
|
12
13
|
|
|
13
14
|
while activeLeafGreaterThan0Condition():
|
|
14
15
|
|
|
@@ -49,99 +50,99 @@ def countFolds(listDimensions: Sequence[int], computationDivisions = None, CPUli
|
|
|
49
50
|
if placeLeafCondition():
|
|
50
51
|
placeLeaf()
|
|
51
52
|
|
|
52
|
-
def activeGapIncrement():
|
|
53
|
+
def activeGapIncrement() -> None:
|
|
53
54
|
my[indexMy.gap1ndex] += 1
|
|
54
55
|
|
|
55
|
-
def activeLeafGreaterThan0Condition():
|
|
56
|
+
def activeLeafGreaterThan0Condition() -> bool:
|
|
56
57
|
return my[indexMy.leaf1ndex] > 0
|
|
57
58
|
|
|
58
|
-
def activeLeafGreaterThanLeavesTotalCondition():
|
|
59
|
+
def activeLeafGreaterThanLeavesTotalCondition() -> bool:
|
|
59
60
|
return my[indexMy.leaf1ndex] > the[indexThe.leavesTotal]
|
|
60
61
|
|
|
61
|
-
def activeLeafIsTheFirstLeafCondition():
|
|
62
|
+
def activeLeafIsTheFirstLeafCondition() -> bool:
|
|
62
63
|
return my[indexMy.leaf1ndex] <= 1
|
|
63
64
|
|
|
64
|
-
def activeLeafNotEqualToTaskDivisionsCondition():
|
|
65
|
+
def activeLeafNotEqualToTaskDivisionsCondition() -> bool:
|
|
65
66
|
return my[indexMy.leaf1ndex] != the[indexThe.taskDivisions]
|
|
66
67
|
|
|
67
|
-
def allDimensionsAreUnconstrained():
|
|
68
|
+
def allDimensionsAreUnconstrained() -> bool:
|
|
68
69
|
return my[indexMy.dimensionsUnconstrained] == the[indexThe.dimensionsTotal]
|
|
69
70
|
|
|
70
|
-
def backtrack():
|
|
71
|
+
def backtrack() -> None:
|
|
71
72
|
my[indexMy.leaf1ndex] -= 1
|
|
72
73
|
track[indexTrack.leafBelow, track[indexTrack.leafAbove, my[indexMy.leaf1ndex]]] = track[indexTrack.leafBelow, my[indexMy.leaf1ndex]]
|
|
73
74
|
track[indexTrack.leafAbove, track[indexTrack.leafBelow, my[indexMy.leaf1ndex]]] = track[indexTrack.leafAbove, my[indexMy.leaf1ndex]]
|
|
74
75
|
|
|
75
|
-
def backtrackCondition():
|
|
76
|
+
def backtrackCondition() -> bool:
|
|
76
77
|
return my[indexMy.leaf1ndex] > 0 and my[indexMy.gap1ndex] == track[indexTrack.gapRangeStart, my[indexMy.leaf1ndex] - 1]
|
|
77
78
|
|
|
78
|
-
def computationDivisionsCondition():
|
|
79
|
+
def computationDivisionsCondition() -> bool:
|
|
79
80
|
return the[indexThe.taskDivisions] == int(False)
|
|
80
81
|
|
|
81
|
-
def countGaps():
|
|
82
|
+
def countGaps() -> None:
|
|
82
83
|
gapsWhere[my[indexMy.gap1ndexCeiling]] = my[indexMy.leafConnectee]
|
|
83
84
|
if track[indexTrack.countDimensionsGapped, my[indexMy.leafConnectee]] == 0:
|
|
84
85
|
gap1ndexCeilingIncrement()
|
|
85
86
|
track[indexTrack.countDimensionsGapped, my[indexMy.leafConnectee]] += 1
|
|
86
87
|
|
|
87
|
-
def dimension1ndexIncrement():
|
|
88
|
+
def dimension1ndexIncrement() -> None:
|
|
88
89
|
my[indexMy.dimension1ndex] += 1
|
|
89
90
|
|
|
90
|
-
def dimensionsUnconstrainedCondition():
|
|
91
|
+
def dimensionsUnconstrainedCondition() -> bool:
|
|
91
92
|
return connectionGraph[my[indexMy.dimension1ndex], my[indexMy.leaf1ndex], my[indexMy.leaf1ndex]] == my[indexMy.leaf1ndex]
|
|
92
93
|
|
|
93
|
-
def dimensionsUnconstrainedIncrement():
|
|
94
|
+
def dimensionsUnconstrainedIncrement() -> None:
|
|
94
95
|
my[indexMy.dimensionsUnconstrained] += 1
|
|
95
96
|
|
|
96
|
-
def filterCommonGaps():
|
|
97
|
+
def filterCommonGaps() -> None:
|
|
97
98
|
gapsWhere[my[indexMy.gap1ndex]] = gapsWhere[my[indexMy.indexMiniGap]]
|
|
98
99
|
if track[indexTrack.countDimensionsGapped, gapsWhere[my[indexMy.indexMiniGap]]] == the[indexThe.dimensionsTotal] - my[indexMy.dimensionsUnconstrained]:
|
|
99
100
|
activeGapIncrement()
|
|
100
101
|
track[indexTrack.countDimensionsGapped, gapsWhere[my[indexMy.indexMiniGap]]] = 0
|
|
101
102
|
|
|
102
|
-
def findGapsInitializeVariables():
|
|
103
|
+
def findGapsInitializeVariables() -> None:
|
|
103
104
|
my[indexMy.dimensionsUnconstrained] = 0
|
|
104
105
|
my[indexMy.gap1ndexCeiling] = track[indexTrack.gapRangeStart, my[indexMy.leaf1ndex] - 1]
|
|
105
106
|
my[indexMy.dimension1ndex] = 1
|
|
106
107
|
|
|
107
|
-
def foldsSubTotalsIncrement():
|
|
108
|
+
def foldsSubTotalsIncrement() -> None:
|
|
108
109
|
foldsSubTotals[my[indexMy.taskIndex]] += the[indexThe.leavesTotal]
|
|
109
110
|
|
|
110
|
-
def gap1ndexCeilingIncrement():
|
|
111
|
+
def gap1ndexCeilingIncrement() -> None:
|
|
111
112
|
my[indexMy.gap1ndexCeiling] += 1
|
|
112
113
|
|
|
113
|
-
def indexMiniGapIncrement():
|
|
114
|
+
def indexMiniGapIncrement() -> None:
|
|
114
115
|
my[indexMy.indexMiniGap] += 1
|
|
115
116
|
|
|
116
|
-
def indexMiniGapInitialization():
|
|
117
|
+
def indexMiniGapInitialization() -> None:
|
|
117
118
|
my[indexMy.indexMiniGap] = my[indexMy.gap1ndex]
|
|
118
119
|
|
|
119
|
-
def insertUnconstrainedLeaf():
|
|
120
|
+
def insertUnconstrainedLeaf() -> None:
|
|
120
121
|
my[indexMy.indexLeaf] = 0
|
|
121
122
|
while my[indexMy.indexLeaf] < my[indexMy.leaf1ndex]:
|
|
122
123
|
gapsWhere[my[indexMy.gap1ndexCeiling]] = my[indexMy.indexLeaf]
|
|
123
124
|
my[indexMy.gap1ndexCeiling] += 1
|
|
124
125
|
my[indexMy.indexLeaf] += 1
|
|
125
126
|
|
|
126
|
-
def leafBelowSentinelIs1Condition():
|
|
127
|
+
def leafBelowSentinelIs1Condition() -> bool:
|
|
127
128
|
return track[indexTrack.leafBelow, 0] == 1
|
|
128
129
|
|
|
129
|
-
def leafConnecteeInitialization():
|
|
130
|
+
def leafConnecteeInitialization() -> None:
|
|
130
131
|
my[indexMy.leafConnectee] = connectionGraph[my[indexMy.dimension1ndex], my[indexMy.leaf1ndex], my[indexMy.leaf1ndex]]
|
|
131
132
|
|
|
132
|
-
def leafConnecteeUpdate():
|
|
133
|
+
def leafConnecteeUpdate() -> None:
|
|
133
134
|
my[indexMy.leafConnectee] = connectionGraph[my[indexMy.dimension1ndex], my[indexMy.leaf1ndex], track[indexTrack.leafBelow, my[indexMy.leafConnectee]]]
|
|
134
135
|
|
|
135
|
-
def loopingLeavesConnectedToActiveLeaf():
|
|
136
|
+
def loopingLeavesConnectedToActiveLeaf() -> bool:
|
|
136
137
|
return my[indexMy.leafConnectee] != my[indexMy.leaf1ndex]
|
|
137
138
|
|
|
138
|
-
def loopingTheDimensions():
|
|
139
|
+
def loopingTheDimensions() -> bool:
|
|
139
140
|
return my[indexMy.dimension1ndex] <= the[indexThe.dimensionsTotal]
|
|
140
141
|
|
|
141
|
-
def loopingToActiveGapCeiling():
|
|
142
|
+
def loopingToActiveGapCeiling() -> bool:
|
|
142
143
|
return my[indexMy.indexMiniGap] < my[indexMy.gap1ndexCeiling]
|
|
143
144
|
|
|
144
|
-
def placeLeaf():
|
|
145
|
+
def placeLeaf() -> None:
|
|
145
146
|
my[indexMy.gap1ndex] -= 1
|
|
146
147
|
track[indexTrack.leafAbove, my[indexMy.leaf1ndex]] = gapsWhere[my[indexMy.gap1ndex]]
|
|
147
148
|
track[indexTrack.leafBelow, my[indexMy.leaf1ndex]] = track[indexTrack.leafBelow, track[indexTrack.leafAbove, my[indexMy.leaf1ndex]]]
|
|
@@ -150,13 +151,13 @@ def countFolds(listDimensions: Sequence[int], computationDivisions = None, CPUli
|
|
|
150
151
|
track[indexTrack.gapRangeStart, my[indexMy.leaf1ndex]] = my[indexMy.gap1ndex]
|
|
151
152
|
my[indexMy.leaf1ndex] += 1
|
|
152
153
|
|
|
153
|
-
def placeLeafCondition():
|
|
154
|
+
def placeLeafCondition() -> bool:
|
|
154
155
|
return my[indexMy.leaf1ndex] > 0
|
|
155
156
|
|
|
156
|
-
def taskIndexCondition():
|
|
157
|
+
def taskIndexCondition() -> bool:
|
|
157
158
|
return my[indexMy.leafConnectee] % the[indexThe.taskDivisions] == my[indexMy.taskIndex]
|
|
158
159
|
|
|
159
|
-
def thereAreComputationDivisionsYouMightSkip():
|
|
160
|
+
def thereAreComputationDivisionsYouMightSkip() -> bool:
|
|
160
161
|
if computationDivisionsCondition():
|
|
161
162
|
return True
|
|
162
163
|
if activeLeafNotEqualToTaskDivisionsCondition():
|
|
@@ -166,11 +167,11 @@ def countFolds(listDimensions: Sequence[int], computationDivisions = None, CPUli
|
|
|
166
167
|
return False
|
|
167
168
|
|
|
168
169
|
stateUniversal = outfitFoldings(listDimensions, computationDivisions=computationDivisions, CPUlimit=CPUlimit)
|
|
169
|
-
connectionGraph: Final[numpy.
|
|
170
|
+
connectionGraph: Final[NDArray[numpy.integer[Any]]] = stateUniversal['connectionGraph']
|
|
170
171
|
foldsSubTotals = stateUniversal['foldsSubTotals']
|
|
171
172
|
gapsWhere = stateUniversal['gapsWhere']
|
|
172
173
|
my = stateUniversal['my']
|
|
173
|
-
the: Final[numpy.
|
|
174
|
+
the: Final[NDArray[numpy.integer[Any]]] = stateUniversal['the']
|
|
174
175
|
track = stateUniversal['track']
|
|
175
176
|
|
|
176
177
|
if the[indexThe.taskDivisions] == int(False):
|
|
@@ -192,7 +193,7 @@ def countFolds(listDimensions: Sequence[int], computationDivisions = None, CPUli
|
|
|
192
193
|
class EnumIndices(enum.IntEnum):
|
|
193
194
|
"""Base class for index enums."""
|
|
194
195
|
@staticmethod
|
|
195
|
-
def _generate_next_value_(name, start, count, last_values):
|
|
196
|
+
def _generate_next_value_(name: str, start: int, count: int, last_values: list[int]) -> int:
|
|
196
197
|
"""0-indexed."""
|
|
197
198
|
return count
|
|
198
199
|
|
|
@@ -229,7 +230,7 @@ class indexTrack(EnumIndices):
|
|
|
229
230
|
class computationState(TypedDict):
|
|
230
231
|
connectionGraph: NDArray[integer[Any]]
|
|
231
232
|
foldsSubTotals: NDArray[integer[Any]]
|
|
232
|
-
mapShape:
|
|
233
|
+
mapShape: tuple[int, ...]
|
|
233
234
|
my: NDArray[integer[Any]]
|
|
234
235
|
gapsWhere: NDArray[integer[Any]]
|
|
235
236
|
the: NDArray[integer[Any]]
|
|
@@ -262,7 +263,7 @@ def getLeavesTotal(listDimensions: Sequence[int]) -> int:
|
|
|
262
263
|
|
|
263
264
|
return productDimensions
|
|
264
265
|
|
|
265
|
-
def getTaskDivisions(computationDivisions:
|
|
266
|
+
def getTaskDivisions(computationDivisions: int | str | None, concurrencyLimit: int, CPUlimit: bool | float | int | None, listDimensions: Sequence[int]) -> int:
|
|
266
267
|
if not computationDivisions:
|
|
267
268
|
return 0
|
|
268
269
|
else:
|
|
@@ -284,7 +285,7 @@ def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrenc
|
|
|
284
285
|
|
|
285
286
|
return taskDivisions
|
|
286
287
|
|
|
287
|
-
def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments:
|
|
288
|
+
def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: type | None) -> NDArray[integer[Any]]:
|
|
288
289
|
datatype = keywordArguments.get('datatype', dtypeMedium)
|
|
289
290
|
mapShape = validateListDimensions(listDimensions)
|
|
290
291
|
leavesTotal = getLeavesTotal(mapShape)
|
|
@@ -316,12 +317,12 @@ def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optio
|
|
|
316
317
|
connectionGraph[dimension1ndex, activeLeaf1ndex, connectee1ndex] = connectee1ndex
|
|
317
318
|
return connectionGraph
|
|
318
319
|
|
|
319
|
-
def makeDataContainer(shape, datatype:
|
|
320
|
+
def makeDataContainer(shape: int | Sequence[int], datatype: type | None = None) -> NDArray[integer[Any]]:
|
|
320
321
|
if datatype is None:
|
|
321
322
|
datatype = dtypeMedium
|
|
322
323
|
return numpy.zeros(shape, dtype=datatype)
|
|
323
324
|
|
|
324
|
-
def outfitFoldings(listDimensions: Sequence[int], computationDivisions:
|
|
325
|
+
def outfitFoldings(listDimensions: Sequence[int], computationDivisions: int | str | None = None, CPUlimit: bool | float | int | None = None, **keywordArguments: type | None) -> computationState:
|
|
325
326
|
datatypeMedium = keywordArguments.get('datatypeMedium', dtypeMedium)
|
|
326
327
|
datatypeLarge = keywordArguments.get('datatypeLarge', dtypeLarge)
|
|
327
328
|
|
|
@@ -346,10 +347,10 @@ def outfitFoldings(listDimensions: Sequence[int], computationDivisions: Optional
|
|
|
346
347
|
stateInitialized['my'][indexMy.leaf1ndex] = 1
|
|
347
348
|
return stateInitialized
|
|
348
349
|
|
|
349
|
-
def parseDimensions(dimensions: Sequence[int], parameterName: str = 'unnamed parameter') ->
|
|
350
|
+
def parseDimensions(dimensions: Sequence[int], parameterName: str = 'unnamed parameter') -> list[int]:
|
|
350
351
|
# listValidated = intInnit(dimensions, parameterName)
|
|
351
352
|
listNOTValidated = dimensions if isinstance(dimensions, (list, tuple)) else list(dimensions)
|
|
352
|
-
listNonNegative = []
|
|
353
|
+
listNonNegative: list[int] = []
|
|
353
354
|
for dimension in listNOTValidated:
|
|
354
355
|
if dimension < 0:
|
|
355
356
|
raise ValueError(f"Dimension {dimension} must be non-negative")
|
|
@@ -358,7 +359,7 @@ def parseDimensions(dimensions: Sequence[int], parameterName: str = 'unnamed par
|
|
|
358
359
|
raise ValueError("At least one dimension must be non-negative")
|
|
359
360
|
return listNonNegative
|
|
360
361
|
|
|
361
|
-
def setCPUlimit(CPUlimit:
|
|
362
|
+
def setCPUlimit(CPUlimit: bool | float | int | None) -> int:
|
|
362
363
|
# if not (CPUlimit is None or isinstance(CPUlimit, (bool, int, float))):
|
|
363
364
|
# CPUlimit = oopsieKwargsie(CPUlimit)
|
|
364
365
|
# concurrencyLimit = defineConcurrencyLimit(CPUlimit)
|
|
@@ -367,7 +368,7 @@ def setCPUlimit(CPUlimit: Union[bool, float, int, None]) -> int:
|
|
|
367
368
|
concurrencyLimit = concurrencyLimitHARDCODED
|
|
368
369
|
return concurrencyLimit
|
|
369
370
|
|
|
370
|
-
def validateListDimensions(listDimensions: Sequence[int]) ->
|
|
371
|
+
def validateListDimensions(listDimensions: Sequence[int]) -> list[int]:
|
|
371
372
|
if not listDimensions:
|
|
372
373
|
raise ValueError(f"listDimensions is a required parameter.")
|
|
373
374
|
listNonNegative = parseDimensions(listDimensions, 'listDimensions')
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Any
|
|
2
2
|
import numba
|
|
3
3
|
import numpy
|
|
4
4
|
|
|
5
5
|
@numba.jit(cache=True, nopython=True, fastmath=True)
|
|
6
|
-
def countFolds(listDimensions:
|
|
6
|
+
def countFolds(listDimensions: list[int]) -> int:
|
|
7
7
|
"""
|
|
8
8
|
Count the number of distinct ways to fold a map with at least two positive dimensions.
|
|
9
9
|
|
|
@@ -13,10 +13,10 @@ def countFolds(listDimensions: List[int]) -> int:
|
|
|
13
13
|
Returns:
|
|
14
14
|
foldsTotal: The total number of distinct folds for the given map dimensions.
|
|
15
15
|
"""
|
|
16
|
-
def integerSmall(value) -> numpy.uint8:
|
|
16
|
+
def integerSmall(value: numpy.integer[Any] | Any) -> numpy.uint8:
|
|
17
17
|
return numpy.uint8(value)
|
|
18
18
|
|
|
19
|
-
def integerLarge(value) -> numpy.uint64:
|
|
19
|
+
def integerLarge(value: numpy.integer[Any] | Any) -> numpy.uint64:
|
|
20
20
|
return numpy.uint64(value)
|
|
21
21
|
|
|
22
22
|
dtypeMedium = numpy.uint8
|
|
@@ -2,7 +2,7 @@
|
|
|
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
|
+
Citation: https://github.com/hunterhogan/mapFolding/blob/134f2e6ecdf59fb6f6829c775475544a6aaaa800/citations/jOEIS.bibtex
|
|
6
6
|
"""
|
|
7
7
|
def foldings(p: list[int], res: int = 0, mod: int = 0) -> int:
|
|
8
8
|
"""
|