mapFolding 0.8.4__tar.gz → 0.8.6__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 (59) hide show
  1. {mapfolding-0.8.4 → mapfolding-0.8.6}/PKG-INFO +3 -2
  2. {mapfolding-0.8.4 → mapfolding-0.8.6}/README.md +1 -1
  3. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/__init__.py +10 -6
  4. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/basecamp.py +3 -3
  5. mapfolding-0.8.6/mapFolding/beDRY.py +348 -0
  6. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/oeis.py +41 -26
  7. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/hunterNumba.py +1 -1
  8. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/someAssemblyRequired/__init__.py +16 -15
  9. mapfolding-0.8.6/mapFolding/someAssemblyRequired/_theTypes.py +53 -0
  10. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/someAssemblyRequired/_tool_Make.py +13 -5
  11. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/someAssemblyRequired/_tool_Then.py +12 -5
  12. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/someAssemblyRequired/_toolboxAntecedents.py +131 -99
  13. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/someAssemblyRequired/_toolboxContainers.py +92 -15
  14. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/someAssemblyRequired/_toolboxPython.py +17 -31
  15. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/someAssemblyRequired/getLLVMforNoReason.py +2 -2
  16. mapfolding-0.8.6/mapFolding/someAssemblyRequired/newInliner.py +22 -0
  17. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +65 -116
  18. mapfolding-0.8.6/mapFolding/someAssemblyRequired/toolboxNumba.py +364 -0
  19. mapfolding-0.8.6/mapFolding/someAssemblyRequired/transformationTools.py +377 -0
  20. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/syntheticModules/numbaCount_doTheNeedful.py +0 -1
  21. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/theSSOT.py +30 -33
  22. mapfolding-0.8.4/mapFolding/filesystem.py → mapfolding-0.8.6/mapFolding/toolboxFilesystem.py +90 -25
  23. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding.egg-info/PKG-INFO +3 -2
  24. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding.egg-info/SOURCES.txt +3 -5
  25. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding.egg-info/requires.txt +1 -0
  26. {mapfolding-0.8.4 → mapfolding-0.8.6}/pyproject.toml +2 -1
  27. {mapfolding-0.8.4 → mapfolding-0.8.6}/tests/conftest.py +30 -31
  28. {mapfolding-0.8.4 → mapfolding-0.8.6}/tests/test_computations.py +8 -7
  29. {mapfolding-0.8.4 → mapfolding-0.8.6}/tests/test_filesystem.py +2 -2
  30. {mapfolding-0.8.4 → mapfolding-0.8.6}/tests/test_other.py +2 -2
  31. {mapfolding-0.8.4 → mapfolding-0.8.6}/tests/test_tasks.py +3 -3
  32. mapfolding-0.8.4/mapFolding/beDRY.py +0 -175
  33. mapfolding-0.8.4/mapFolding/noHomeYet.py +0 -32
  34. mapfolding-0.8.4/mapFolding/someAssemblyRequired/_theTypes.py +0 -35
  35. mapfolding-0.8.4/mapFolding/someAssemblyRequired/ingredientsNumba.py +0 -199
  36. mapfolding-0.8.4/mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py +0 -156
  37. mapfolding-0.8.4/mapFolding/someAssemblyRequired/transformDataStructures.py +0 -235
  38. mapfolding-0.8.4/mapFolding/someAssemblyRequired/transformationTools.py +0 -156
  39. {mapfolding-0.8.4 → mapfolding-0.8.6}/LICENSE +0 -0
  40. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/py.typed +0 -0
  41. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/__init__.py +0 -0
  42. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/flattened.py +0 -0
  43. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/irvineJavaPort.py +0 -0
  44. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/jaxCount.py +0 -0
  45. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/jobsCompleted/[2x19]/p2x19.py +0 -0
  46. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/jobsCompleted/__init__.py +0 -0
  47. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/jobsCompleted/p2x19/p2x19.py +0 -0
  48. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/lunnanNumpy.py +0 -0
  49. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/lunnanWhile.py +0 -0
  50. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/rotatedEntryPoint.py +0 -0
  51. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/total_countPlus1vsPlusN.py +0 -0
  52. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/syntheticModules/__init__.py +0 -0
  53. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/theDao.py +0 -0
  54. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding.egg-info/dependency_links.txt +0 -0
  55. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding.egg-info/entry_points.txt +0 -0
  56. {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding.egg-info/top_level.txt +0 -0
  57. {mapfolding-0.8.4 → mapfolding-0.8.6}/setup.cfg +0 -0
  58. {mapfolding-0.8.4 → mapfolding-0.8.6}/tests/__init__.py +0 -0
  59. {mapfolding-0.8.4 → mapfolding-0.8.6}/tests/test_oeis.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mapFolding
3
- Version: 0.8.4
3
+ Version: 0.8.6
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
@@ -35,6 +35,7 @@ Requires-Dist: more_itertools
35
35
  Requires-Dist: numba_progress
36
36
  Requires-Dist: numba
37
37
  Requires-Dist: numpy
38
+ Requires-Dist: platformdirs
38
39
  Requires-Dist: python_minifier
39
40
  Requires-Dist: tomli
40
41
  Requires-Dist: Z0Z_tools
@@ -132,7 +133,7 @@ The package provides a sophisticated transformation framework that bridges the g
132
133
  - Study the functional state-transformation approach in `theDao.py` with clear, isolated functions
133
134
  - Explore the semantic decomposition in `reference/flattened.py` to understand algorithm sections
134
135
 
135
- - **Code Transformation Pipeline**:
136
+ - **Code Transformation Assembly-line**:
136
137
  - **AST Manipulation**: Analyzes and transforms the algorithm's abstract syntax tree
137
138
  - **Dataclass "Shattering"**: Decomposes complex state objects into primitive components
138
139
  - **Optimization Applications**: Applies domain-specific optimizations for numerical computation
@@ -82,7 +82,7 @@ The package provides a sophisticated transformation framework that bridges the g
82
82
  - Study the functional state-transformation approach in `theDao.py` with clear, isolated functions
83
83
  - Explore the semantic decomposition in `reference/flattened.py` to understand algorithm sections
84
84
 
85
- - **Code Transformation Pipeline**:
85
+ - **Code Transformation Assembly-line**:
86
86
  - **AST Manipulation**: Analyzes and transforms the algorithm's abstract syntax tree
87
87
  - **Dataclass "Shattering"**: Decomposes complex state objects into primitive components
88
88
  - **Optimization Applications**: Applies domain-specific optimizations for numerical computation
@@ -1,17 +1,20 @@
1
1
  """
2
- Map folding enumeration and counting algorithms with optimization capabilities.
2
+ Map folding enumeration and counting algorithms with advanced optimization capabilities.
3
3
 
4
- This package implements algorithms to count and enumerate the various ways
4
+ This package implements algorithms to count and enumerate the distinct ways
5
5
  a rectangular map can be folded, based on the mathematical problem described
6
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
7
+ high-level user interfaces to sophisticated algorithmic optimizations and code
8
8
  transformation tools.
9
9
 
10
10
  Core modules:
11
11
  - basecamp: Public API with simplified interfaces for end users
12
12
  - theDao: Core computational algorithm using a functional state-transformation approach
13
- - beDRY: Utility functions for common operations and parameter management
13
+ - beDRY: Core utility functions implementing consistent data handling, validation, and
14
+ resource management across the package's computational assembly-line
14
15
  - theSSOT: Single Source of Truth for configuration, types, and state management
16
+ - toolboxFilesystem: Cross-platform file management services for storing and retrieving
17
+ computation results with robust error handling and fallback mechanisms
15
18
  - oeis: Interface to the Online Encyclopedia of Integer Sequences for known results
16
19
 
17
20
  Extended functionality:
@@ -26,9 +29,10 @@ Special directories:
26
29
  - reference/jobsCompleted/: Contains successful computations for previously unknown values,
27
30
  including first-ever calculations for 2x19 and 2x20 maps (OEIS A001415)
28
31
 
29
- This package strives to balance algorithm readability and understandability with
32
+ This package balances algorithm readability and understandability with
30
33
  high-performance computation capabilities, allowing users to compute map folding
31
- totals for larger dimensions than previously feasible.
34
+ totals for larger dimensions than previously feasible while also providing
35
+ a foundation for exploring advanced code transformation techniques.
32
36
  """
33
37
  from mapFolding.basecamp import countFolds
34
38
  from mapFolding.oeis import clearOEIScache, getOEISids, OEIS_for_n, oeisIDfor_n
@@ -13,9 +13,9 @@ implementation, and optional persistence of results.
13
13
  """
14
14
 
15
15
  from collections.abc import Sequence
16
- from mapFolding.beDRY import outfitCountFolds, setCPUlimit, validateListDimensions
17
- from mapFolding.filesystem import getPathFilenameFoldsTotal, saveFoldsTotal, saveFoldsTotalFAILearly
18
16
  from mapFolding.theSSOT import ComputationState, getPackageDispatcher, The
17
+ from mapFolding.beDRY import outfitCountFolds, setProcessorLimit, validateListDimensions
18
+ from mapFolding.toolboxFilesystem import getPathFilenameFoldsTotal, saveFoldsTotal, saveFoldsTotalFAILearly
19
19
  from os import PathLike
20
20
  from pathlib import PurePath
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, The.concurrencyPackage)
57
+ concurrencyLimit: int = setProcessorLimit(CPUlimit, The.concurrencyPackage)
58
58
  computationStateInitialized: ComputationState = outfitCountFolds(mapShape, computationDivisions, concurrencyLimit)
59
59
 
60
60
  if pathLikeWriteFoldsTotal is not None:
@@ -0,0 +1,348 @@
1
+ """
2
+ Core utility functions implementing DRY (Don't Repeat Yourself) principles for the mapFolding package.
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:
6
+
7
+ 1. Calculate and validate fundamental computational parameters such as leaves total and task divisions.
8
+ 2. Generate specialized connection graphs that define the folding algorithm's constraints.
9
+ 3. Provide centralized resource allocation and system limits management.
10
+ 4. Construct and manage uniform data structures for the computation state.
11
+ 5. Ensure parameter validation and safe type conversion.
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.
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.
20
+ """
21
+ from collections.abc import Sequence
22
+ from mapFolding.theSSOT import ComputationState, numpyIntegerType
23
+ from numpy import dtype as numpy_dtype, int64 as numpy_int64, ndarray
24
+ from sys import maxsize as sysMaxsize
25
+ from typing import Any
26
+ from Z0Z_tools import defineConcurrencyLimit, intInnit, oopsieKwargsie
27
+ import numpy
28
+
29
+ def getLeavesTotal(mapShape: tuple[int, ...]) -> int:
30
+ """
31
+ Calculate the total number of leaves in a map with the given dimensions.
32
+
33
+ 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
+
37
+ Parameters
38
+ ----------
39
+ mapShape
40
+ A tuple of integers representing the dimensions of the map.
41
+
42
+ Returns
43
+ -------
44
+ leavesTotal
45
+ The total number of leaves in the map, calculated as the product of all dimensions.
46
+
47
+ Raises
48
+ ------
49
+ 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.
52
+ """
53
+ productDimensions = 1
54
+ for dimension in mapShape:
55
+ # NOTE this check is one-degree short of absurd, but three lines of early absurdity is better than invalid output later. I'd add more checks if I could think of more.
56
+ if dimension > sysMaxsize // productDimensions:
57
+ raise OverflowError(f"I received `{dimension = }` in `{mapShape = }`, but the product of the dimensions exceeds the maximum size of an integer on this system.")
58
+ productDimensions *= dimension
59
+ return productDimensions
60
+
61
+ def getTaskDivisions(computationDivisions: int | str | None, concurrencyLimit: int, leavesTotal: int) -> int:
62
+ """
63
+ Determines whether to divide the computation into tasks and how many divisions.
64
+
65
+ Parameters
66
+ ----------
67
+ computationDivisions: None
68
+ Specifies how to divide computations:
69
+ - `None`: no division of the computation into tasks; sets task divisions to 0.
70
+ - int: directly set the number of task divisions; cannot exceed the map's total leaves.
71
+ - `'maximum'`: divides into `leavesTotal`-many `taskDivisions`.
72
+ - `'cpu'`: limits the divisions to the number of available CPUs: i.e., `concurrencyLimit`.
73
+ concurrencyLimit
74
+ Maximum number of concurrent tasks allowed.
75
+
76
+ Returns
77
+ -------
78
+ taskDivisions
79
+ How many tasks must finish before the job can compute the total number of folds; `0` means no tasks, only job.
80
+
81
+ Raises
82
+ ------
83
+ ValueError
84
+ If `computationDivisions` is an unsupported type or if resulting task divisions exceed total leaves.
85
+
86
+ Notes
87
+ -----
88
+ Task divisions should not exceed total leaves or the folds will be over-counted.
89
+ """
90
+ taskDivisions = 0
91
+ match computationDivisions:
92
+ case None | 0 | False:
93
+ pass
94
+ case int() as intComputationDivisions:
95
+ taskDivisions = intComputationDivisions
96
+ case str() as strComputationDivisions:
97
+ strComputationDivisions = strComputationDivisions.lower()
98
+ match strComputationDivisions:
99
+ case 'maximum':
100
+ taskDivisions = leavesTotal
101
+ case 'cpu':
102
+ taskDivisions = min(concurrencyLimit, leavesTotal)
103
+ case _:
104
+ raise ValueError(f"I received '{strComputationDivisions}' for the parameter, `computationDivisions`, but the string value is not supported.")
105
+ case _:
106
+ raise ValueError(f"I received {computationDivisions} for the parameter, `computationDivisions`, but the type {type(computationDivisions).__name__} is not supported.")
107
+
108
+ if taskDivisions > leavesTotal:
109
+ raise ValueError(f"Problem: `{taskDivisions = }`, is greater than `{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 be named `computationDivisions`, `CPUlimit` , and `listDimensions` and from my dubious-quality Python code.")
110
+ return int(max(0, taskDivisions))
111
+
112
+ def _makeConnectionGraph(mapShape: tuple[int, ...], leavesTotal: int) -> ndarray[tuple[int, int, int], numpy_dtype[numpy_int64]]:
113
+ """
114
+ Implementation of connection graph generation for map folding.
115
+
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.
120
+
121
+ Parameters
122
+ ----------
123
+ mapShape
124
+ A tuple of integers representing the dimensions of the map.
125
+ leavesTotal
126
+ The total number of leaves in the map.
127
+
128
+ Returns
129
+ -------
130
+ 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.
134
+
135
+ Notes
136
+ -----
137
+ This is an implementation detail and shouldn't be called directly by external code.
138
+ Use `getConnectionGraph` instead, which applies proper typing.
139
+
140
+ The algorithm calculates a coordinate system first, then determines connections
141
+ based on parity rules, boundary conditions, and dimensional constraints.
142
+ """
143
+ dimensionsTotal = len(mapShape)
144
+ cumulativeProduct = numpy.multiply.accumulate([1] + list(mapShape), dtype=numpy_int64)
145
+ arrayDimensions = numpy.array(mapShape, dtype=numpy_int64)
146
+ coordinateSystem = numpy.zeros((dimensionsTotal, leavesTotal + 1), dtype=numpy_int64)
147
+ for indexDimension in range(dimensionsTotal):
148
+ for leaf1ndex in range(1, leavesTotal + 1):
149
+ coordinateSystem[indexDimension, leaf1ndex] = (((leaf1ndex - 1) // cumulativeProduct[indexDimension]) % arrayDimensions[indexDimension] + 1)
150
+
151
+ connectionGraph = numpy.zeros((dimensionsTotal, leavesTotal + 1, leavesTotal + 1), dtype=numpy_int64)
152
+ for indexDimension in range(dimensionsTotal):
153
+ for activeLeaf1ndex in range(1, leavesTotal + 1):
154
+ for connectee1ndex in range(1, activeLeaf1ndex + 1):
155
+ isFirstCoord = coordinateSystem[indexDimension, connectee1ndex] == 1
156
+ isLastCoord = coordinateSystem[indexDimension, connectee1ndex] == arrayDimensions[indexDimension]
157
+ exceedsActive = connectee1ndex + cumulativeProduct[indexDimension] > activeLeaf1ndex
158
+ isEvenParity = (coordinateSystem[indexDimension, activeLeaf1ndex] & 1) == (coordinateSystem[indexDimension, connectee1ndex] & 1)
159
+
160
+ if (isEvenParity and isFirstCoord) or (not isEvenParity and (isLastCoord or exceedsActive)):
161
+ connectionGraph[indexDimension, activeLeaf1ndex, connectee1ndex] = connectee1ndex
162
+ elif isEvenParity and not isFirstCoord:
163
+ connectionGraph[indexDimension, activeLeaf1ndex, connectee1ndex] = connectee1ndex - cumulativeProduct[indexDimension]
164
+ elif not isEvenParity and not (isLastCoord or exceedsActive):
165
+ connectionGraph[indexDimension, activeLeaf1ndex, connectee1ndex] = connectee1ndex + cumulativeProduct[indexDimension]
166
+ return connectionGraph
167
+
168
+ def getConnectionGraph(mapShape: tuple[int, ...], leavesTotal: int, datatype: type[numpyIntegerType]) -> ndarray[tuple[int, int, int], numpy_dtype[numpyIntegerType]]:
169
+ """
170
+ Create a properly typed connection graph for the map folding algorithm.
171
+
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.
175
+
176
+ Parameters
177
+ ----------
178
+ mapShape
179
+ A tuple of integers representing the dimensions of the map.
180
+ leavesTotal
181
+ The total number of leaves in the map.
182
+ datatype
183
+ The NumPy integer type to use for the array elements, ensuring proper
184
+ memory usage and compatibility with the computation state.
185
+
186
+ Returns
187
+ -------
188
+ connectionGraph
189
+ A 3D NumPy array with shape (`dimensionsTotal`, `leavesTotal`+1, `leavesTotal`+1)
190
+ with the specified `datatype`, representing all possible connections between leaves.
191
+ """
192
+ connectionGraph = _makeConnectionGraph(mapShape, leavesTotal)
193
+ connectionGraph = connectionGraph.astype(datatype)
194
+ return connectionGraph
195
+
196
+ def makeDataContainer(shape: int | tuple[int, ...], datatype: type[numpyIntegerType]) -> ndarray[Any, numpy_dtype[numpyIntegerType]]:
197
+ """
198
+ Create a typed NumPy array container with initialized values.
199
+
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.
203
+
204
+ Parameters
205
+ ----------
206
+ shape
207
+ Either an integer (for 1D arrays) or a tuple of integers (for multi-dimensional arrays)
208
+ specifying the dimensions of the array.
209
+ datatype
210
+ The NumPy integer type to use for the array elements, ensuring proper type
211
+ consistency and memory efficiency.
212
+
213
+ Returns
214
+ -------
215
+ container
216
+ A NumPy array of zeros with the specified shape and `datatype`.
217
+ """
218
+ return numpy.zeros(shape, dtype=datatype)
219
+
220
+ def outfitCountFolds(mapShape: tuple[int, ...], computationDivisions: int | str | None = None, concurrencyLimit: int = 1) -> ComputationState:
221
+ """
222
+ Initialize a `ComputationState` with validated parameters for map folding calculation.
223
+
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.
227
+
228
+ Parameters
229
+ ----------
230
+ mapShape
231
+ A tuple of integers representing the dimensions of the map.
232
+ 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.
236
+ concurrencyLimit: 1
237
+ Maximum number of concurrent processes to use during computation.
238
+
239
+ Returns
240
+ -------
241
+ computationStateInitialized
242
+ A fully initialized `ComputationState` object that's ready for computation.
243
+
244
+ Notes
245
+ -----
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.
249
+ """
250
+ leavesTotal = getLeavesTotal(mapShape)
251
+ taskDivisions = getTaskDivisions(computationDivisions, concurrencyLimit, leavesTotal)
252
+ computationStateInitialized = ComputationState(mapShape, leavesTotal, taskDivisions, concurrencyLimit)
253
+ return computationStateInitialized
254
+
255
+ def setProcessorLimit(CPUlimit: Any | None, concurrencyPackage: str | None = None) -> int:
256
+ """
257
+ Sets processor limit for concurrent operations.
258
+
259
+ Parameters
260
+ ----------
261
+ CPUlimit: None
262
+ 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.
264
+ - `True`: Yes, limit the processor usage; limits to 1 processor.
265
+ - Integer `>= 1`: Limits usage to the specified number of processors.
266
+ - Decimal value (`float`) between 0 and 1: Fraction of total processors to use.
267
+ - Decimal value (`float`) between -1 and 0: Fraction of processors to _not_ use.
268
+ - Integer `<= -1`: Subtract the absolute value from total processors.
269
+ concurrencyPackage: None
270
+ Specifies which concurrency package to use:
271
+ - `None` or `'multiprocessing'`: Uses standard `multiprocessing`.
272
+ - `'numba'`: Uses Numba's threading system.
273
+
274
+ Returns
275
+ -------
276
+ concurrencyLimit
277
+ The actual concurrency limit that was set.
278
+
279
+ Raises
280
+ ------
281
+ TypeError
282
+ If `CPUlimit` is not of the expected types.
283
+ NotImplementedError
284
+ If `concurrencyPackage` is not supported.
285
+
286
+ Notes
287
+ -----
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.
291
+
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.
294
+ """
295
+ if not (CPUlimit is None or isinstance(CPUlimit, (bool, int, float))):
296
+ CPUlimit = oopsieKwargsie(CPUlimit)
297
+
298
+ match concurrencyPackage:
299
+ case 'multiprocessing' | None:
300
+ # When to use multiprocessing.set_start_method https://github.com/hunterhogan/mapFolding/issues/6
301
+ concurrencyLimit: int = defineConcurrencyLimit(CPUlimit)
302
+ case 'numba':
303
+ from numba import get_num_threads, set_num_threads
304
+ concurrencyLimit = defineConcurrencyLimit(CPUlimit, get_num_threads())
305
+ set_num_threads(concurrencyLimit)
306
+ concurrencyLimit = get_num_threads()
307
+ case _:
308
+ raise NotImplementedError(f"I received `{concurrencyPackage = }` but I don't know what to do with that.")
309
+ return concurrencyLimit
310
+
311
+ def validateListDimensions(listDimensions: Sequence[int]) -> tuple[int, ...]:
312
+ """
313
+ Validate and normalize dimensions for a map folding problem.
314
+
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.
318
+
319
+ Parameters
320
+ ----------
321
+ listDimensions
322
+ A sequence of integers representing the dimensions of the map.
323
+
324
+ Returns
325
+ -------
326
+ tuple[int, ...]
327
+ A sorted tuple of positive integers representing the validated dimensions.
328
+
329
+ Raises
330
+ ------
331
+ ValueError
332
+ If the input is empty or contains negative values.
333
+ NotImplementedError
334
+ If fewer than two positive dimensions are provided, as this would not
335
+ represent a valid map folding problem.
336
+ """
337
+ if not listDimensions:
338
+ raise ValueError("`listDimensions` is a required parameter.")
339
+ listValidated: list[int] = intInnit(listDimensions, 'listDimensions')
340
+ listNonNegative: list[int] = []
341
+ for dimension in listValidated:
342
+ if dimension < 0:
343
+ raise ValueError(f"`{dimension = }` in `{listDimensions = }`, must be a non-negative integer.")
344
+ listNonNegative.append(dimension)
345
+ dimensionsValid = [dimension for dimension in listNonNegative if dimension > 0]
346
+ if len(dimensionsValid) < 2:
347
+ 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/.")
348
+ return tuple(sorted(dimensionsValid))
@@ -2,25 +2,31 @@
2
2
  Interface to The Online Encyclopedia of Integer Sequences (OEIS) for map folding sequences.
3
3
 
4
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:
5
+ from the OEIS that relate to map folding problems. It serves as the bridge between
6
+ mathematical sequence definitions and the computational algorithm, implementing:
6
7
 
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
8
+ 1. Retrieval of sequence data from OEIS with local caching for performance optimization.
9
+ 2. Mapping of sequence indices to corresponding map shapes based on sequence definitions.
10
+ 3. Command-line and programmatic interfaces for sequence lookups and validation.
11
+ 4. Computation of sequence terms not available in the OEIS database.
11
12
 
12
13
  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
+ with their metadata, known values, and conversion functions between sequence indices and
14
15
  map dimensions. This allows the package to validate results against established mathematical
15
- literature and extend sequences beyond their currently known terms.
16
+ literature while also extending sequences beyond their currently known terms.
17
+
18
+ Each sequence is carefully mapped to its corresponding map folding problem, enabling
19
+ seamless integration between the mathematical definition in OEIS and the computational
20
+ implementation in the package.
16
21
  """
17
22
  from collections.abc import Callable
18
23
  from datetime import datetime, timedelta
24
+ from functools import cache
19
25
  from mapFolding.theSSOT import The
26
+ from mapFolding.toolboxFilesystem import writeStringToHere
20
27
  from pathlib import Path
21
28
  from typing import Any, Final, TYPE_CHECKING
22
29
  import argparse
23
- import pathlib
24
30
  import random
25
31
  import sys
26
32
  import time
@@ -31,13 +37,10 @@ import warnings
31
37
  if TYPE_CHECKING:
32
38
  from typing import TypedDict
33
39
  else:
34
- TypedDict = dict
40
+ TypedDict = dict[Any, Any]
35
41
 
36
42
  cacheDays = 30
37
43
 
38
- """
39
- Section: make `settingsOEIS`"""
40
-
41
44
  pathCache: Path = The.pathPackage / ".cache"
42
45
 
43
46
  class SettingsOEIS(TypedDict):
@@ -158,7 +161,7 @@ def _parseBFileOEIS(OEISbFile: str, oeisID: str) -> dict[int, int]:
158
161
  OEISsequence[n] = aOFn
159
162
  return OEISsequence
160
163
 
161
- def getOEISofficial(pathFilenameCache: pathlib.Path, url: str) -> None | str:
164
+ def getOEISofficial(pathFilenameCache: Path, url: str) -> None | str:
162
165
  tryCache: bool = False
163
166
  if pathFilenameCache.exists():
164
167
  fileAge: timedelta = datetime.now() - datetime.fromtimestamp(pathFilenameCache.stat().st_mtime)
@@ -174,8 +177,7 @@ def getOEISofficial(pathFilenameCache: pathlib.Path, url: str) -> None | str:
174
177
  if not tryCache:
175
178
  httpResponse: urllib.response.addinfourl = urllib.request.urlopen(url)
176
179
  oeisInformation = httpResponse.read().decode('utf-8')
177
- pathFilenameCache.parent.mkdir(parents=True, exist_ok=True)
178
- pathFilenameCache.write_text(oeisInformation)
180
+ writeStringToHere(oeisInformation, pathFilenameCache)
179
181
 
180
182
  if not oeisInformation:
181
183
  warnings.warn(f"Failed to retrieve OEIS sequence information for {pathFilenameCache.stem}.")
@@ -186,6 +188,7 @@ def getOEISidValues(oeisID: str) -> dict[int, int]:
186
188
  """
187
189
  Retrieves the specified OEIS sequence as a dictionary mapping integer indices
188
190
  to their corresponding values.
191
+
189
192
  This function checks for a cached local copy of the sequence data, using it if
190
193
  it has not expired. Otherwise, it fetches the sequence data from the OEIS
191
194
  website and writes it to the cache. The parsed data is returned as a dictionary
@@ -195,7 +198,7 @@ def getOEISidValues(oeisID: str) -> dict[int, int]:
195
198
  oeisID: The identifier of the OEIS sequence to retrieve.
196
199
  Returns:
197
200
  OEISsequence: A dictionary where each key is an integer index, `n`, and each
198
- value is the corresponding "a(n)" from the OEIS entry.
201
+ value is the corresponding `a(n)` from the OEIS entry.
199
202
  Raises:
200
203
  ValueError: If the cached or downloaded file format is invalid.
201
204
  IOError: If there is an error reading from or writing to the local cache.
@@ -259,8 +262,23 @@ def makeSettingsOEIS() -> dict[str, SettingsOEIS]:
259
262
  settingsOEIS: dict[str, SettingsOEIS] = makeSettingsOEIS()
260
263
  """All values and settings for `oeisIDsImplemented`."""
261
264
 
262
- """
263
- Section: private functions"""
265
+ @cache
266
+ def makeDictionaryFoldsTotalKnown() -> dict[tuple[int, ...], int]:
267
+ """Returns a dictionary mapping dimension tuples to their known folding totals."""
268
+ dictionaryMapDimensionsToFoldsTotalKnown: dict[tuple[int, ...], int] = {}
269
+
270
+ for settings in settingsOEIS.values():
271
+ sequence = settings['valuesKnown']
272
+
273
+ for n, foldingsTotal in sequence.items():
274
+ mapShape = settings['getMapShape'](n)
275
+ mapShape = tuple(sorted(mapShape))
276
+ dictionaryMapDimensionsToFoldsTotalKnown[mapShape] = foldingsTotal
277
+ return dictionaryMapDimensionsToFoldsTotalKnown
278
+
279
+ def getFoldsTotalKnown(mapShape: tuple[int, ...]) -> int:
280
+ lookupFoldsTotal = makeDictionaryFoldsTotalKnown()
281
+ return lookupFoldsTotal.get(tuple(mapShape), -1)
264
282
 
265
283
  def _formatHelpText() -> str:
266
284
  """Format standardized help text for both CLI and interactive use."""
@@ -285,35 +303,32 @@ def _formatOEISsequenceInfo() -> str:
285
303
  for oeisID in oeisIDsImplemented
286
304
  )
287
305
 
288
- """
289
- Section: public functions"""
290
-
291
306
  def oeisIDfor_n(oeisID: str, n: int | Any) -> int:
292
307
  """
293
- Calculate a(n) of a sequence from "The On-Line Encyclopedia of Integer Sequences" (OEIS).
308
+ Calculate `a(n)` of a sequence from "The On-Line Encyclopedia of Integer Sequences" (OEIS).
294
309
 
295
310
  Parameters:
296
311
  oeisID: The ID of the OEIS sequence.
297
312
  n: A non-negative integer for which to calculate the sequence value.
298
313
 
299
314
  Returns:
300
- sequenceValue: a(n) of the OEIS sequence.
315
+ sequenceValue: `a(n)` of the OEIS sequence.
301
316
 
302
317
  Raises:
303
- ValueError: If n is negative.
318
+ ValueError: If `n` is negative.
304
319
  KeyError: If the OEIS sequence ID is not directly implemented.
305
320
  """
306
321
  oeisID = validateOEISid(oeisID)
307
322
 
308
323
  if not isinstance(n, int) or n < 0:
309
- raise ValueError("`n` must be non-negative integer.")
324
+ raise ValueError(f"I received `{n = }` in the form of `{type(n) = }`, but it must be non-negative integer in the form of `{int}`.")
310
325
 
311
326
  mapShape: tuple[int, ...] = settingsOEIS[oeisID]['getMapShape'](n)
312
327
 
313
328
  if n <= 1 or len(mapShape) < 2:
314
329
  offset: int = settingsOEIS[oeisID]['offset']
315
330
  if n < offset:
316
- raise ArithmeticError(f"OEIS sequence {oeisID} is not defined at n={n}.")
331
+ raise ArithmeticError(f"OEIS sequence {oeisID} is not defined at {n = }.")
317
332
  foldsTotal: int = settingsOEIS[oeisID]['valuesKnown'][n]
318
333
  return foldsTotal
319
334
  from mapFolding.basecamp import countFolds
@@ -19,7 +19,7 @@ Performance considerations:
19
19
  - Incorporates lessons from multiple implementation strategies
20
20
 
21
21
  Note: This serves as a reference for manually-optimized code before the development of
22
- the automated transformation pipeline in the main package.
22
+ the automated transformation assembly-line in the main package.
23
23
  """
24
24
 
25
25
  from typing import Any
@@ -14,7 +14,7 @@ Core capabilities:
14
14
  4. Performance Optimization - Apply domain-specific optimizations for numerical computation
15
15
  5. Code Generation - Generate specialized implementations with appropriate imports and syntax
16
16
 
17
- The transformation pipeline supports multiple optimization targets, from general-purpose
17
+ The transformation assembly-line supports multiple optimization targets, from general-purpose
18
18
  acceleration to generating highly-specialized variants optimized for specific input parameters.
19
19
  This multi-level transformation approach allows for both development flexibility and
20
20
  runtime performance, preserving algorithm readability in the source while enabling
@@ -27,17 +27,21 @@ particularly for numerically-intensive algorithms that benefit from just-in-time
27
27
  from mapFolding.someAssemblyRequired._theTypes import (
28
28
  ast_expr_Slice,
29
29
  ast_Identifier,
30
- ImaAnnotationType,
31
30
  astClassHasDOTnameNotName,
32
- astClassHasDOTnameNotNameOptional,
33
31
  astClassHasDOTtarget,
34
32
  astClassHasDOTvalue,
33
+ astClassOptionallyHasDOTnameNotName,
35
34
  astMosDef,
35
+ Ima_funcTypeUNEDITED,
36
+ Ima_targetTypeUNEDITED,
37
+ ImaAnnotationType,
38
+ ImaAnnotationTypeVar,
36
39
  intORlist_ast_type_paramORstr_orNone,
37
40
  intORstr_orNone,
38
41
  list_ast_type_paramORstr_orNone,
39
42
  str_nameDOTname,
40
- typeCertified,
43
+ TypeCertified,
44
+ 个,
41
45
  )
42
46
 
43
47
  from mapFolding.someAssemblyRequired._toolboxPython import (
@@ -49,17 +53,14 @@ from mapFolding.someAssemblyRequired._toolboxPython import (
49
53
  parsePathFilename2astModule,
50
54
  )
51
55
 
52
- from mapFolding.someAssemblyRequired._toolboxAntecedents import be, ifThis, 又
56
+ from mapFolding.someAssemblyRequired._toolboxAntecedents import be, DOT, ifThis, 又
53
57
  from mapFolding.someAssemblyRequired._tool_Make import Make
54
58
  from mapFolding.someAssemblyRequired._tool_Then import Then
55
59
 
56
- from mapFolding.someAssemblyRequired.transformationTools import (
57
- dictionaryEstimates,
58
- extractClassDef,
59
- extractFunctionDef,
60
- write_astModule,
61
- Z0Z_executeActionUnlessDescendantMatches,
62
- Z0Z_inlineThisFunctionWithTheseValues,
63
- Z0Z_lameFindReplace,
64
- Z0Z_makeDictionaryReplacementStatements,
65
- )
60
+ from mapFolding.someAssemblyRequired._toolboxContainers import (
61
+ IngredientsFunction,
62
+ IngredientsModule,
63
+ LedgerOfImports,
64
+ RecipeSynthesizeFlow,
65
+ ShatteredDataclass,
66
+ )