mapFolding 0.9.1__tar.gz → 0.9.3__tar.gz

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 (58) hide show
  1. {mapfolding-0.9.1 → mapfolding-0.9.3}/PKG-INFO +4 -3
  2. {mapfolding-0.9.1 → mapfolding-0.9.3}/README.md +1 -1
  3. mapfolding-0.9.3/mapFolding/Z0Z_flowControl.py +99 -0
  4. mapfolding-0.9.3/mapFolding/__init__.py +96 -0
  5. mapfolding-0.9.3/mapFolding/basecamp.py +95 -0
  6. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/beDRY.py +72 -66
  7. mapfolding-0.9.3/mapFolding/dataBaskets.py +49 -0
  8. mapfolding-0.9.3/mapFolding/datatypes.py +21 -0
  9. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/oeis.py +57 -75
  10. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/reference/__init__.py +2 -2
  11. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/someAssemblyRequired/__init__.py +6 -4
  12. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/someAssemblyRequired/_theTypes.py +9 -1
  13. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/someAssemblyRequired/_tool_Make.py +0 -1
  14. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/someAssemblyRequired/_tool_Then.py +16 -8
  15. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/someAssemblyRequired/_toolboxAntecedents.py +111 -35
  16. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/someAssemblyRequired/_toolboxContainers.py +58 -49
  17. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +42 -42
  18. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/someAssemblyRequired/toolboxNumba.py +3 -11
  19. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/someAssemblyRequired/transformationTools.py +94 -70
  20. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/syntheticModules/numbaCount.py +9 -11
  21. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/theDao.py +19 -21
  22. mapfolding-0.9.3/mapFolding/theDaoOfMapFolding.py +142 -0
  23. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/theSSOT.py +36 -58
  24. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/toolboxFilesystem.py +29 -38
  25. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding.egg-info/PKG-INFO +4 -3
  26. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding.egg-info/SOURCES.txt +6 -2
  27. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding.egg-info/requires.txt +1 -0
  28. {mapfolding-0.9.1 → mapfolding-0.9.3}/pyproject.toml +4 -2
  29. {mapfolding-0.9.1 → mapfolding-0.9.3}/tests/__init__.py +2 -2
  30. {mapfolding-0.9.1 → mapfolding-0.9.3}/tests/conftest.py +7 -7
  31. {mapfolding-0.9.1 → mapfolding-0.9.3}/tests/test_computations.py +15 -13
  32. {mapfolding-0.9.1 → mapfolding-0.9.3}/tests/test_other.py +0 -7
  33. {mapfolding-0.9.1 → mapfolding-0.9.3}/tests/test_tasks.py +2 -2
  34. mapfolding-0.9.1/mapFolding/__init__.py +0 -93
  35. mapfolding-0.9.1/mapFolding/basecamp.py +0 -90
  36. {mapfolding-0.9.1 → mapfolding-0.9.3}/LICENSE +0 -0
  37. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/py.typed +0 -0
  38. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/reference/flattened.py +0 -0
  39. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/reference/hunterNumba.py +0 -0
  40. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/reference/irvineJavaPort.py +0 -0
  41. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/reference/jaxCount.py +0 -0
  42. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/reference/jobsCompleted/[2x19]/p2x19.py +0 -0
  43. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/reference/jobsCompleted/__init__.py +0 -0
  44. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/reference/jobsCompleted/p2x19/p2x19.py +0 -0
  45. /mapfolding-0.9.1/mapFolding/reference/lunnanNumpy.py → /mapfolding-0.9.3/mapFolding/reference/lunnonNumpy.py +0 -0
  46. /mapfolding-0.9.1/mapFolding/reference/lunnanWhile.py → /mapfolding-0.9.3/mapFolding/reference/lunnonWhile.py +0 -0
  47. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/reference/rotatedEntryPoint.py +0 -0
  48. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/reference/total_countPlus1vsPlusN.py +0 -0
  49. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/someAssemblyRequired/RecipeJob.py +0 -0
  50. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/someAssemblyRequired/_toolboxPython.py +0 -0
  51. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/someAssemblyRequired/getLLVMforNoReason.py +0 -0
  52. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding/syntheticModules/__init__.py +0 -0
  53. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding.egg-info/dependency_links.txt +0 -0
  54. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding.egg-info/entry_points.txt +0 -0
  55. {mapfolding-0.9.1 → mapfolding-0.9.3}/mapFolding.egg-info/top_level.txt +0 -0
  56. {mapfolding-0.9.1 → mapfolding-0.9.3}/setup.cfg +0 -0
  57. {mapfolding-0.9.1 → mapfolding-0.9.3}/tests/test_filesystem.py +0 -0
  58. {mapfolding-0.9.1 → mapfolding-0.9.3}/tests/test_oeis.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mapFolding
3
- Version: 0.9.1
3
+ Version: 0.9.3
4
4
  Summary: Map folding algorithm with code transformation framework for optimizing numerical computations
5
5
  Author-email: Hunter Hogan <HunterHogan@pm.me>
6
6
  License: CC-BY-NC-4.0
@@ -8,7 +8,7 @@ Project-URL: Donate, https://www.patreon.com/integrated
8
8
  Project-URL: Homepage, https://github.com/hunterhogan/mapFolding
9
9
  Project-URL: Repository, https://github.com/hunterhogan/mapFolding.git
10
10
  Project-URL: Issues, https://github.com/hunterhogan/mapFolding/issues
11
- Keywords: A001415,A001416,A001417,A001418,A195646,algorithmic optimization,AST manipulation,code generation,code transformation,combinatorics,computational geometry,dataclass transformation,folding pattern enumeration,just-in-time compilation,map folding,Numba optimization,OEIS,performance optimization,source code analysis,stamp folding
11
+ Keywords: A000136,A001415,A001416,A001417,A001418,A195646,algorithmic optimization,AST manipulation,code generation,code transformation,combinatorics,computational geometry,dataclass transformation,folding pattern enumeration,just-in-time compilation,map folding,Numba optimization,OEIS,performance optimization,source code analysis,stamp folding
12
12
  Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Environment :: Console
14
14
  Classifier: Intended Audience :: Developers
@@ -38,6 +38,7 @@ Requires-Dist: numba
38
38
  Requires-Dist: numpy
39
39
  Requires-Dist: platformdirs
40
40
  Requires-Dist: python_minifier
41
+ Requires-Dist: sympy
41
42
  Requires-Dist: tomli
42
43
  Requires-Dist: Z0Z_tools
43
44
  Provides-Extra: testing
@@ -105,7 +106,7 @@ def countFolds_optimized(shape_param):
105
106
 
106
107
  ### 2. Code Generation Framework
107
108
 
108
- Study and extend a complete Python code transformation pipeline:
109
+ Study and extend a complete Python code transformation assembly line:
109
110
 
110
111
  - AST analysis and manipulation
111
112
  - Dataclass decomposition ("shattering")
@@ -53,7 +53,7 @@ def countFolds_optimized(shape_param):
53
53
 
54
54
  ### 2. Code Generation Framework
55
55
 
56
- Study and extend a complete Python code transformation pipeline:
56
+ Study and extend a complete Python code transformation assembly line:
57
57
 
58
58
  - AST analysis and manipulation
59
59
  - Dataclass decomposition ("shattering")
@@ -0,0 +1,99 @@
1
+ from collections.abc import Sequence
2
+ from mapFolding import (
3
+ ComputationState,
4
+ getPathFilenameFoldsTotal,
5
+ outfitCountFolds,
6
+ saveFoldsTotal,
7
+ saveFoldsTotalFAILearly,
8
+ setProcessorLimit,
9
+ The,
10
+ validateListDimensions,
11
+ )
12
+ from os import PathLike
13
+ from pathlib import PurePath
14
+
15
+ def countFolds(listDimensions: Sequence[int] | None = None
16
+ , pathLikeWriteFoldsTotal: PathLike[str] | PurePath | None = None
17
+ , computationDivisions: int | str | None = None
18
+ , CPUlimit: int | float | bool | None = None
19
+ # , * I need to improve `standardizedEqualToCallableReturn` so it will work with keyword arguments
20
+ , mapShape: tuple[int, ...] | None = None
21
+ , oeisID: str | None = None
22
+ , oeis_n: int | None = None
23
+ , flow: str | None = None
24
+ ) -> int:
25
+ """
26
+ To select the execution path, I need at least:
27
+ - mapShape
28
+ - task division instructions
29
+ - memorialization instructions
30
+ """
31
+
32
+ # mapShape =====================================================================
33
+
34
+ if mapShape:
35
+ pass
36
+ else:
37
+ if oeisID and oeis_n:
38
+ from mapFolding.oeis import settingsOEIS
39
+ try:
40
+ mapShape = settingsOEIS[oeisID]['getMapShape'](oeis_n)
41
+ except KeyError:
42
+ pass
43
+ if not mapShape and listDimensions:
44
+ mapShape = validateListDimensions(listDimensions)
45
+
46
+ if mapShape is None:
47
+ raise ValueError(f"""I received these values:
48
+ `{listDimensions = }`,
49
+ `{mapShape = }`,
50
+ `{oeisID = }` and `{oeis_n = }`,
51
+ but I was unable to select a map for which to count the folds.""")
52
+
53
+ # task division instructions ===============================================
54
+
55
+ if computationDivisions:
56
+ # NOTE `The.concurrencyPackage`
57
+ concurrencyLimit: int = setProcessorLimit(CPUlimit, The.concurrencyPackage)
58
+ from mapFolding.beDRY import getLeavesTotal, getTaskDivisions
59
+ leavesTotal: int = getLeavesTotal(mapShape)
60
+ taskDivisions = getTaskDivisions(computationDivisions, concurrencyLimit, leavesTotal)
61
+ del leavesTotal
62
+ else:
63
+ concurrencyLimit = 1
64
+ taskDivisions = 0
65
+
66
+ # memorialization instructions ===========================================
67
+
68
+ if pathLikeWriteFoldsTotal is not None:
69
+ pathFilenameFoldsTotal = getPathFilenameFoldsTotal(mapShape, pathLikeWriteFoldsTotal)
70
+ saveFoldsTotalFAILearly(pathFilenameFoldsTotal)
71
+ else:
72
+ pathFilenameFoldsTotal = None
73
+
74
+ # Flow control until I can figure out a good way ===============================
75
+
76
+ if flow == 'theDaoOfMapFolding':
77
+ from mapFolding.dataBaskets import MapFoldingState
78
+ mapFoldingState: MapFoldingState = MapFoldingState(mapShape)
79
+
80
+ from mapFolding.theDaoOfMapFolding import doTheNeedful
81
+ mapFoldingState = doTheNeedful(mapFoldingState)
82
+ foldsTotal = mapFoldingState.foldsTotal
83
+
84
+ # NOTE treat this as a default?
85
+ # flow based on `The` and `ComputationState` ====================================
86
+
87
+ else:
88
+ computationStateInitialized: ComputationState = outfitCountFolds(mapShape, computationDivisions, concurrencyLimit)
89
+ computationStateComplete: ComputationState = The.dispatcher(computationStateInitialized)
90
+
91
+ computationStateComplete.getFoldsTotal()
92
+ foldsTotal = computationStateComplete.foldsTotal
93
+
94
+ # Follow memorialization instructions ===========================================
95
+
96
+ if pathFilenameFoldsTotal is not None:
97
+ saveFoldsTotal(pathFilenameFoldsTotal, foldsTotal)
98
+
99
+ return foldsTotal
@@ -0,0 +1,96 @@
1
+ """
2
+ Map folding enumeration and counting algorithms with advanced optimization capabilities.
3
+
4
+ This package implements algorithms to count and enumerate the distinct ways a rectangular map can be folded, based on
5
+ the mathematical problem described in Lunnon's 1971 paper. It provides multiple layers of functionality, from high-level
6
+ user interfaces to sophisticated algorithmic optimizations and code transformation tools.
7
+
8
+ Core modules:
9
+ - basecamp: Public API with simplified interfaces for end users
10
+ - theDao: Core computational algorithm using a functional state-transformation approach
11
+ - beDRY: Core utility functions implementing consistent data handling, validation, and resource management across the
12
+ package's computational assembly-line
13
+ - theSSOT: Single Source of Truth for configuration, types, and state management
14
+ - toolboxFilesystem: Cross-platform file management services for storing and retrieving computation results with robust
15
+ error handling and fallback mechanisms
16
+ - oeis: Interface to the Online Encyclopedia of Integer Sequences for known results
17
+
18
+ Extended functionality:
19
+ - someAssemblyRequired: Code transformation framework that optimizes the core algorithm through AST manipulation,
20
+ dataclass transformation, and compilation techniques
21
+ - The system converts readable code into high-performance implementations through a systematic analysis and
22
+ transformation assembly line
23
+ - Provides tools to "shatter" complex dataclasses into primitive components, enabling compatibility with Numba and
24
+ other optimization frameworks
25
+ - Creates specialized implementations tailored for specific input parameters
26
+
27
+ Testing and extension:
28
+ - tests: Comprehensive test suite designed for both verification and extension
29
+ - Provides fixtures and utilities that simplify testing of custom implementations
30
+ - Enables users to validate their own recipes and job configurations with minimal code
31
+ - Offers standardized testing patterns that maintain consistency across the codebase
32
+ - See tests/__init__.py for detailed documentation on extending the test suite
33
+
34
+ Special directories:
35
+ - .cache/: Stores cached data from external sources like OEIS to improve performance
36
+ - syntheticModules/: Contains dynamically generated, optimized implementations of the core algorithm created by the code
37
+ transformation framework
38
+ - reference/: Historical implementations and educational resources for algorithm exploration
39
+ - reference/jobsCompleted/: Contains successful computations for previously unknown values, including first-ever
40
+ calculations for 2x19 and 2x20 maps (OEIS A001415)
41
+
42
+ This package balances algorithm readability and understandability with high-performance computation capabilities,
43
+ allowing users to compute map folding totals for larger dimensions than previously feasible while also providing a
44
+ foundation for exploring advanced code transformation techniques.
45
+ """
46
+
47
+ from mapFolding.datatypes import (
48
+ Array1DElephino as Array1DElephino,
49
+ Array1DFoldsTotal as Array1DFoldsTotal,
50
+ Array1DLeavesTotal as Array1DLeavesTotal,
51
+ Array3D as Array3D,
52
+ DatatypeElephino as DatatypeElephino,
53
+ DatatypeFoldsTotal as DatatypeFoldsTotal,
54
+ DatatypeLeavesTotal as DatatypeLeavesTotal,
55
+ NumPyElephino as NumPyElephino,
56
+ NumPyFoldsTotal as NumPyFoldsTotal,
57
+ NumPyIntegerType as NumPyIntegerType,
58
+ NumPyLeavesTotal as NumPyLeavesTotal,
59
+ )
60
+
61
+ from mapFolding.theSSOT import (
62
+ ComputationState as ComputationState,
63
+ raiseIfNoneGitHubIssueNumber3 as raiseIfNoneGitHubIssueNumber3,
64
+ The as The,
65
+ )
66
+
67
+ from mapFolding.theDao import (
68
+ countInitialize as countInitialize,
69
+ doTheNeedful as doTheNeedful,
70
+ )
71
+
72
+ from mapFolding.beDRY import (
73
+ getLeavesTotal as getLeavesTotal,
74
+ getTaskDivisions as getTaskDivisions,
75
+ outfitCountFolds as outfitCountFolds,
76
+ setProcessorLimit as setProcessorLimit,
77
+ validateListDimensions as validateListDimensions,
78
+ )
79
+
80
+ from mapFolding.toolboxFilesystem import (
81
+ getPathFilenameFoldsTotal as getPathFilenameFoldsTotal,
82
+ getPathRootJobDEFAULT as getPathRootJobDEFAULT,
83
+ saveFoldsTotal as saveFoldsTotal,
84
+ saveFoldsTotalFAILearly as saveFoldsTotalFAILearly,
85
+ writeStringToHere as writeStringToHere,
86
+ )
87
+
88
+ from mapFolding.Z0Z_flowControl import countFolds
89
+
90
+ from mapFolding.oeis import (
91
+ clearOEIScache as clearOEIScache,
92
+ getFoldsTotalKnown as getFoldsTotalKnown,
93
+ getOEISids as getOEISids,
94
+ OEIS_for_n as OEIS_for_n,
95
+ oeisIDfor_n as oeisIDfor_n,
96
+ )
@@ -0,0 +1,95 @@
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, abstracting away the complexities of the
5
+ computational algorithm. It offers a high-level interface to count the total number of possible ways to fold a
6
+ rectangular map of specified dimensions, with options for customizing the computation process and saving results.
7
+
8
+ The primary function is countFolds, which handles parameter validation, computation state management, dispatching to the
9
+ appropriate algorithm implementation, and optional persistence of results.
10
+ """
11
+
12
+ from collections.abc import Sequence
13
+ from mapFolding import (
14
+ ComputationState,
15
+ getPathFilenameFoldsTotal,
16
+ outfitCountFolds,
17
+ saveFoldsTotal,
18
+ saveFoldsTotalFAILearly,
19
+ setProcessorLimit,
20
+ The,
21
+ validateListDimensions,
22
+ )
23
+ from os import PathLike
24
+ from pathlib import PurePath
25
+
26
+ def countFolds(listDimensions: Sequence[int]
27
+ , pathLikeWriteFoldsTotal: PathLike[str] | PurePath | None = None
28
+ , computationDivisions: int | str | None = None
29
+ , CPUlimit: int | float | bool | None = None
30
+ ) -> int:
31
+ """
32
+ Count the total number of possible foldings for a given map dimensions.
33
+
34
+ This function serves as the main public interface to the map folding algorithm, handling all parameter validation,
35
+ computation state management, and result persistence in a user-friendly way.
36
+
37
+ Parameters
38
+ ----------
39
+ listDimensions
40
+ List of integers representing the dimensions of the map to be folded.
41
+ pathLikeWriteFoldsTotal: None
42
+ Path, filename, or pathFilename to write the total fold count to. If a directory is provided, creates a file
43
+ with a default name based on map dimensions.
44
+ computationDivisions: None
45
+ Whether and how to divide the computational work.
46
+ - `None`: no division of the computation into tasks; sets task divisions to 0.
47
+ - int: directly set the number of task divisions; cannot exceed the map's total leaves.
48
+ - `'maximum'`: divides into `leavesTotal`-many `taskDivisions`.
49
+ - `'cpu'`: limits the divisions to the number of available CPUs: i.e., `concurrencyLimit`.
50
+ CPUlimit: None
51
+ This is only relevant if there are `computationDivisions`: whether and how to limit the CPU usage.
52
+ - `False`, `None`, or `0`: No limits on processor usage; uses all available processors. All other values will
53
+ potentially limit processor usage.
54
+ - `True`: Yes, limit the processor usage; limits to 1 processor.
55
+ - Integer `>= 1`: Limits usage to the specified number of processors.
56
+ - Decimal value (`float`) between 0 and 1: Fraction of total processors to use.
57
+ - Decimal value (`float`) between -1 and 0: Fraction of processors to _not_ use.
58
+ - Integer `<= -1`: Subtract the absolute value from total processors.
59
+
60
+ Returns
61
+ -------
62
+ foldsTotal: Total number of distinct ways to fold a map of the given dimensions.
63
+
64
+ Note well
65
+ ---------
66
+ You probably do not want to divide your computation into tasks.
67
+
68
+ If you want to compute a large `foldsTotal`, dividing the computation into tasks is usually a bad idea. Dividing the
69
+ algorithm into tasks is inherently inefficient: efficient division into tasks means there would be no overlap in the
70
+ work performed by each task. When dividing this algorithm, the amount of overlap is between 50% and 90% by all
71
+ tasks: at least 50% of the work done by every task must be done by _all_ tasks. If you improve the computation time,
72
+ it will only change by -10 to -50% depending on (at the very least) the ratio of the map dimensions and the number
73
+ of leaves. If an undivided computation would take 10 hours on your computer, for example, the computation will still
74
+ take at least 5 hours but you might reduce the time to 9 hours. Most of the time, however, you will increase the
75
+ computation time. If logicalCores >= `leavesTotal`, it will probably be faster. If logicalCores <= 2 * `leavesTotal`, it
76
+ will almost certainly be slower for all map dimensions.
77
+ """
78
+ mapShape: tuple[int, ...] = validateListDimensions(listDimensions)
79
+ concurrencyLimit: int = setProcessorLimit(CPUlimit, The.concurrencyPackage)
80
+ computationStateInitialized: ComputationState = outfitCountFolds(mapShape, computationDivisions, concurrencyLimit)
81
+
82
+ if pathLikeWriteFoldsTotal is not None:
83
+ pathFilenameFoldsTotal = getPathFilenameFoldsTotal(computationStateInitialized.mapShape, pathLikeWriteFoldsTotal)
84
+ saveFoldsTotalFAILearly(pathFilenameFoldsTotal)
85
+ else:
86
+ pathFilenameFoldsTotal = None
87
+
88
+ computationStateComplete: ComputationState = The.dispatcher(computationStateInitialized)
89
+
90
+ computationStateComplete.getFoldsTotal()
91
+
92
+ if pathFilenameFoldsTotal is not None:
93
+ saveFoldsTotal(pathFilenameFoldsTotal, computationStateComplete.foldsTotal)
94
+
95
+ return computationStateComplete.foldsTotal
@@ -1,8 +1,8 @@
1
1
  """
2
2
  Core utility functions implementing DRY (Don't Repeat Yourself) principles for the mapFolding package.
3
3
 
4
- This module serves as the foundation for consistent data management and parameter validation
5
- across the entire mapFolding computation assembly-line. It provides critical utility functions that:
4
+ This module serves as the foundation for consistent data management and parameter validation across the entire
5
+ mapFolding computation assembly-line. It provides critical utility functions that:
6
6
 
7
7
  1. Calculate and validate fundamental computational parameters such as leaves total and task divisions.
8
8
  2. Generate specialized connection graphs that define the folding algorithm's constraints.
@@ -10,13 +10,12 @@ across the entire mapFolding computation assembly-line. It provides critical uti
10
10
  4. Construct and manage uniform data structures for the computation state.
11
11
  5. Ensure parameter validation and safe type conversion.
12
12
 
13
- The functions in this module maintain a clear separation between data initialization and algorithm
14
- implementation, enabling the package to support multiple computational strategies (sequential,
15
- parallel, and JIT-compiled) while ensuring consistent input handling and state management.
13
+ The functions in this module maintain a clear separation between data initialization and algorithm implementation,
14
+ enabling the package to support multiple computational strategies (sequential, parallel, and JIT-compiled) while
15
+ ensuring consistent input handling and state management.
16
16
 
17
- These utilities form a stable internal API that other modules depend on, particularly theSSOT
18
- (Single Source of Truth), theDao (core algorithm), and the synthetic module generators that
19
- produce optimized implementations.
17
+ These utilities form a stable internal API that other modules depend on, particularly theSSOT (Single Source of Truth),
18
+ theDao (core algorithm), and the synthetic module generators that produce optimized implementations.
20
19
  """
21
20
  from collections.abc import Sequence
22
21
  from mapFolding import ComputationState, NumPyIntegerType
@@ -31,8 +30,6 @@ def getLeavesTotal(mapShape: tuple[int, ...]) -> int:
31
30
  Calculate the total number of leaves in a map with the given dimensions.
32
31
 
33
32
  The total number of leaves is the product of all dimensions in the map shape.
34
- This value is foundational for initializing the computation state and determining
35
- task divisions.
36
33
 
37
34
  Parameters
38
35
  ----------
@@ -47,8 +44,8 @@ def getLeavesTotal(mapShape: tuple[int, ...]) -> int:
47
44
  Raises
48
45
  ------
49
46
  OverflowError
50
- If the product of dimensions would exceed the system's maximum integer size.
51
- This check prevents silent numeric overflow issues that could lead to incorrect results.
47
+ If the product of dimensions would exceed the system's maximum integer size. This check prevents silent numeric
48
+ overflow issues that could lead to incorrect results.
52
49
  """
53
50
  productDimensions = 1
54
51
  for dimension in mapShape:
@@ -113,10 +110,9 @@ def _makeConnectionGraph(mapShape: tuple[int, ...], leavesTotal: int) -> ndarray
113
110
  """
114
111
  Implementation of connection graph generation for map folding.
115
112
 
116
- This is the internal implementation that calculates all possible connections between
117
- leaves in a map folding problem based on Lunnon's algorithm. The function constructs a
118
- three-dimensional array representing which leaves can be connected to each other for each
119
- dimension of the map.
113
+ This is the internal implementation that calculates all possible connections between leaves in a map folding problem
114
+ based on Lunnon's algorithm. The function constructs a three-dimensional array representing which leaves can be
115
+ connected to each other for each dimension of the map.
120
116
 
121
117
  Parameters
122
118
  ----------
@@ -128,17 +124,16 @@ def _makeConnectionGraph(mapShape: tuple[int, ...], leavesTotal: int) -> ndarray
128
124
  Returns
129
125
  -------
130
126
  connectionGraph
131
- A 3D NumPy array with shape (`dimensionsTotal`, `leavesTotal`+1, `leavesTotal`+1)
132
- where each entry [d,i,j] represents the leaf that would be connected to leaf j
133
- when inserting leaf i in dimension d.
127
+ A 3D NumPy array with shape (`dimensionsTotal`, `leavesTotal`+1, `leavesTotal`+1) where each entry [d,i,j]
128
+ represents the leaf that would be connected to leaf j when inserting leaf i in dimension d.
134
129
 
135
130
  Notes
136
131
  -----
137
- This is an implementation detail and shouldn't be called directly by external code.
138
- Use `getConnectionGraph` instead, which applies proper typing.
132
+ This is an implementation detail and shouldn't be called directly by external code. Use `getConnectionGraph`
133
+ instead, which applies proper typing.
139
134
 
140
- The algorithm calculates a coordinate system first, then determines connections
141
- based on parity rules, boundary conditions, and dimensional constraints.
135
+ The algorithm calculates a coordinate system first, then determines connections based on parity rules, boundary
136
+ conditions, and dimensional constraints.
142
137
  """
143
138
  dimensionsTotal = len(mapShape)
144
139
  cumulativeProduct = numpy.multiply.accumulate([1] + list(mapShape), dtype=numpy_int64)
@@ -169,9 +164,9 @@ def getConnectionGraph(mapShape: tuple[int, ...], leavesTotal: int, datatype: ty
169
164
  """
170
165
  Create a properly typed connection graph for the map folding algorithm.
171
166
 
172
- This function serves as a typed wrapper around the internal implementation that
173
- generates connection graphs. It provides the correct type information for the
174
- returned array, ensuring consistency throughout the computation assembly-line.
167
+ This function serves as a typed wrapper around the internal implementation that generates connection graphs. It
168
+ provides the correct type information for the returned array, ensuring consistency throughout the computation
169
+ assembly-line.
175
170
 
176
171
  Parameters
177
172
  ----------
@@ -180,14 +175,14 @@ def getConnectionGraph(mapShape: tuple[int, ...], leavesTotal: int, datatype: ty
180
175
  leavesTotal
181
176
  The total number of leaves in the map.
182
177
  datatype
183
- The NumPy integer type to use for the array elements, ensuring proper
184
- memory usage and compatibility with the computation state.
178
+ The NumPy integer type to use for the array elements, ensuring proper memory usage and compatibility with the
179
+ computation state.
185
180
 
186
181
  Returns
187
182
  -------
188
183
  connectionGraph
189
- A 3D NumPy array with shape (`dimensionsTotal`, `leavesTotal`+1, `leavesTotal`+1)
190
- with the specified `datatype`, representing all possible connections between leaves.
184
+ A 3D NumPy array with shape (`dimensionsTotal`, `leavesTotal`+1, `leavesTotal`+1) with the specified `datatype`,
185
+ representing all possible connections between leaves.
191
186
  """
192
187
  connectionGraph = _makeConnectionGraph(mapShape, leavesTotal)
193
188
  connectionGraph = connectionGraph.astype(datatype)
@@ -197,18 +192,16 @@ def makeDataContainer(shape: int | tuple[int, ...], datatype: type[NumPyIntegerT
197
192
  """
198
193
  Create a typed NumPy array container with initialized values.
199
194
 
200
- This function centralizes the creation of data containers used throughout the
201
- computation assembly-line, enabling easy switching between different container types
202
- or implementation strategies if needed in the future.
195
+ This function centralizes the creation of data containers used throughout the computation assembly-line, enabling
196
+ easy switching between different container types or implementation strategies if needed in the future.
203
197
 
204
198
  Parameters
205
199
  ----------
206
200
  shape
207
- Either an integer (for 1D arrays) or a tuple of integers (for multi-dimensional arrays)
208
- specifying the dimensions of the array.
201
+ Either an integer (for 1D arrays) or a tuple of integers (for multi-dimensional arrays) specifying the
202
+ dimensions of the array.
209
203
  datatype
210
- The NumPy integer type to use for the array elements, ensuring proper type
211
- consistency and memory efficiency.
204
+ The NumPy integer type to use for the array elements, ensuring proper type consistency and memory efficiency.
212
205
 
213
206
  Returns
214
207
  -------
@@ -221,18 +214,17 @@ def outfitCountFolds(mapShape: tuple[int, ...], computationDivisions: int | str
221
214
  """
222
215
  Initialize a `ComputationState` with validated parameters for map folding calculation.
223
216
 
224
- This function serves as the central initialization point for creating a properly
225
- configured `ComputationState` object, ensuring consistent calculation of the fundamental
226
- parameters (`leavesTotal` and `taskDivisions`) across the entire package.
217
+ This function serves as the central initialization point for creating a properly configured `ComputationState`
218
+ object, ensuring consistent calculation of the fundamental parameters (`leavesTotal` and `taskDivisions`) across the
219
+ entire package.
227
220
 
228
221
  Parameters
229
222
  ----------
230
223
  mapShape
231
224
  A tuple of integers representing the dimensions of the map.
232
225
  computationDivisions: None
233
- Controls how to divide the computation into parallel tasks. I know it is annoying,
234
- but please see `getTaskDivisions` for details, so that you and I both know you have the most
235
- accurate information.
226
+ Controls how to divide the computation into parallel tasks. I know it is annoying, but please see
227
+ `getTaskDivisions` for details, so that you and I both know you have the most accurate information.
236
228
  concurrencyLimit: 1
237
229
  Maximum number of concurrent processes to use during computation.
238
230
 
@@ -243,9 +235,8 @@ def outfitCountFolds(mapShape: tuple[int, ...], computationDivisions: int | str
243
235
 
244
236
  Notes
245
237
  -----
246
- This function maintains the Single Source of Truth principle for `leavesTotal`
247
- and `taskDivisions` calculation, ensuring these values are derived consistently
248
- throughout the package.
238
+ This function maintains the Single Source of Truth principle for `leavesTotal` and `taskDivisions` calculation,
239
+ ensuring these values are derived consistently throughout the package.
249
240
  """
250
241
  leavesTotal = getLeavesTotal(mapShape)
251
242
  taskDivisions = getTaskDivisions(computationDivisions, concurrencyLimit, leavesTotal)
@@ -260,7 +251,8 @@ def setProcessorLimit(CPUlimit: Any | None, concurrencyPackage: str | None = Non
260
251
  ----------
261
252
  CPUlimit: None
262
253
  Controls processor usage limits:
263
- - `False`, `None`, or `0`: No limits on processor usage; uses all available processors. All other values will potentially limit processor usage.
254
+ - `False`, `None`, or `0`: No limits on processor usage; uses all available processors. All other values will
255
+ potentially limit processor usage.
264
256
  - `True`: Yes, limit the processor usage; limits to 1 processor.
265
257
  - Integer `>= 1`: Limits usage to the specified number of processors.
266
258
  - Decimal value (`float`) between 0 and 1: Fraction of total processors to use.
@@ -285,12 +277,12 @@ def setProcessorLimit(CPUlimit: Any | None, concurrencyPackage: str | None = Non
285
277
 
286
278
  Notes
287
279
  -----
288
- If using `'numba'` as the concurrency package, the maximum number of processors is
289
- retrieved from `numba.get_num_threads()` rather than by polling the hardware.
290
- If Numba environment variables limit available processors, that will affect this function.
280
+ If using `'numba'` as the concurrency package, the maximum number of processors is retrieved from
281
+ `numba.get_num_threads()` rather than by polling the hardware. If Numba environment variables limit available
282
+ processors, that will affect this function.
291
283
 
292
- When using Numba, this function must be called before importing any Numba-jitted
293
- function for this processor limit to affect the Numba-jitted function.
284
+ When using Numba, this function must be called before importing any Numba-jitted function for this processor limit
285
+ to affect the Numba-jitted function.
294
286
  """
295
287
  if not (CPUlimit is None or isinstance(CPUlimit, (bool, int, float))):
296
288
  CPUlimit = oopsieKwargsie(CPUlimit)
@@ -312,9 +304,9 @@ def validateListDimensions(listDimensions: Sequence[int]) -> tuple[int, ...]:
312
304
  """
313
305
  Validate and normalize dimensions for a map folding problem.
314
306
 
315
- This function serves as the gatekeeper for dimension inputs, ensuring that all
316
- map dimensions provided to the package meet the requirements for valid computation.
317
- It performs multiple validation steps and normalizes the dimensions into a consistent format.
307
+ This function serves as the gatekeeper for dimension inputs, ensuring that all map dimensions provided to the
308
+ package meet the requirements for valid computation. It performs multiple validation steps and normalizes the
309
+ dimensions into a consistent format.
318
310
 
319
311
  Parameters
320
312
  ----------
@@ -323,8 +315,8 @@ def validateListDimensions(listDimensions: Sequence[int]) -> tuple[int, ...]:
323
315
 
324
316
  Returns
325
317
  -------
326
- tuple[int, ...]
327
- A sorted tuple of positive integers representing the validated dimensions.
318
+ mapShape
319
+ An _unsorted_ tuple of positive integers representing the validated dimensions.
328
320
 
329
321
  Raises
330
322
  ------
@@ -335,13 +327,27 @@ def validateListDimensions(listDimensions: Sequence[int]) -> tuple[int, ...]:
335
327
  """
336
328
  if not listDimensions:
337
329
  raise ValueError("`listDimensions` is a required parameter.")
338
- listValidated: list[int] = intInnit(listDimensions, 'listDimensions')
339
- listNonNegative: list[int] = []
340
- for dimension in listValidated:
341
- if dimension < 0:
342
- raise ValueError(f"`{dimension = }` in `{listDimensions = }`, must be a non-negative integer.")
343
- listNonNegative.append(dimension)
344
- dimensionsValid = [dimension for dimension in listNonNegative if dimension > 0]
345
- if len(dimensionsValid) < 2:
330
+ listOFint: list[int] = intInnit(listDimensions, 'listDimensions')
331
+ mapDimensions: list[int] = []
332
+ for dimension in listOFint:
333
+ if dimension <= 0:
334
+ raise ValueError(f"I received `{dimension = }` in `{listDimensions = }`, but all dimensions must be a non-negative integer.")
335
+ mapDimensions.append(dimension)
336
+ if len(mapDimensions) < 2:
346
337
  raise NotImplementedError(f"This function requires `{listDimensions = }` to have at least two dimensions greater than 0. You may want to look at https://oeis.org/.")
347
- return tuple(sorted(dimensionsValid))
338
+
339
+ """
340
+ I previously sorted the dimensions for a few reasons that may or may not be valid:
341
+ 1. After empirical testing, I believe that (2,10), for example, computes significantly faster than (10,2).
342
+ 2. Standardization, generally.
343
+ 3. If I recall correctly, after empirical testing, I concluded that sorted dimensions always leads to
344
+ non-negative values in the connection graph, but if the dimensions are not in ascending order of magnitude,
345
+ the connection graph might have negative values, which as far as I know, is not an inherent problem, but the
346
+ negative values propagate into other data structures, which requires the datatypes to hold negative values,
347
+ which means I cannot optimize the bit-widths of the datatypes as easily. (And optimized bit-widths helps with
348
+ performance.)
349
+
350
+ Furthermore, now that the package includes OEIS A000136, 1 x N stamps/maps, sorting could distort results.
351
+ """
352
+ # NOTE Do NOT sort the dimensions.
353
+ return tuple(mapDimensions)
@@ -0,0 +1,49 @@
1
+ from mapFolding.beDRY import getConnectionGraph, getLeavesTotal, makeDataContainer
2
+ from mapFolding.datatypes import Array3D, Array1DElephino, Array1DLeavesTotal, DatatypeElephino, DatatypeFoldsTotal, DatatypeLeavesTotal
3
+ import dataclasses
4
+
5
+ @dataclasses.dataclass
6
+ class MapFoldingState:
7
+ mapShape: tuple[DatatypeLeavesTotal, ...] = dataclasses.field(init=True, metadata={'elementConstructor': 'DatatypeLeavesTotal'})
8
+
9
+ groupsOfFolds: DatatypeFoldsTotal = dataclasses.field(default=DatatypeFoldsTotal(0), metadata={'theCountingIdentifier': True})
10
+
11
+ gap1ndex: DatatypeElephino = DatatypeElephino(0)
12
+ gap1ndexCeiling: DatatypeElephino = DatatypeElephino(0)
13
+ indexDimension: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
14
+ indexLeaf: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
15
+ indexMiniGap: DatatypeElephino = DatatypeElephino(0)
16
+ leaf1ndex: DatatypeLeavesTotal = DatatypeLeavesTotal(1)
17
+ leafConnectee: DatatypeLeavesTotal = DatatypeLeavesTotal(0)
18
+
19
+ dimensionsUnconstrained: DatatypeLeavesTotal = dataclasses.field(default=None, init=True) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
20
+
21
+ countDimensionsGapped: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
22
+ gapRangeStart: Array1DElephino = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DElephino.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
23
+ gapsWhere: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
24
+ leafAbove: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
25
+ leafBelow: Array1DLeavesTotal = dataclasses.field(default=None, init=True, metadata={'dtype': Array1DLeavesTotal.__args__[1].__args__[0]}) # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue, reportUnknownMemberType]
26
+
27
+ connectionGraph: Array3D = dataclasses.field(init=False, metadata={'dtype': Array3D.__args__[1].__args__[0]}) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
28
+ dimensionsTotal: DatatypeLeavesTotal = dataclasses.field(init=False)
29
+ leavesTotal: DatatypeLeavesTotal = dataclasses.field(init=False)
30
+
31
+ @property
32
+ def foldsTotal(self) -> DatatypeFoldsTotal:
33
+ _foldsTotal = DatatypeFoldsTotal(self.leavesTotal) * self.groupsOfFolds
34
+ return _foldsTotal
35
+
36
+ def __post_init__(self) -> None:
37
+ self.dimensionsTotal = DatatypeLeavesTotal(len(self.mapShape))
38
+ self.leavesTotal = DatatypeLeavesTotal(getLeavesTotal(self.mapShape))
39
+
40
+ leavesTotalAsInt = int(self.leavesTotal)
41
+
42
+ self.connectionGraph = getConnectionGraph(self.mapShape, leavesTotalAsInt, self.__dataclass_fields__['connectionGraph'].metadata['dtype'])
43
+
44
+ if self.dimensionsUnconstrained is None: self.dimensionsUnconstrained = DatatypeLeavesTotal(int(self.dimensionsTotal)) # pyright: ignore[reportUnnecessaryComparison]
45
+ if self.gapsWhere is None: self.gapsWhere = makeDataContainer(leavesTotalAsInt * leavesTotalAsInt + 1, self.__dataclass_fields__['gapsWhere'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
46
+ if self.countDimensionsGapped is None: self.countDimensionsGapped = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['countDimensionsGapped'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
47
+ if self.gapRangeStart is None: self.gapRangeStart = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['gapRangeStart'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
48
+ if self.leafAbove is None: self.leafAbove = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafAbove'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]
49
+ if self.leafBelow is None: self.leafBelow = makeDataContainer(leavesTotalAsInt + 1, self.__dataclass_fields__['leafBelow'].metadata['dtype']) # pyright: ignore[reportUnnecessaryComparison]