mapFolding 0.8.4__tar.gz → 0.8.5__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.5}/PKG-INFO +3 -2
  2. {mapfolding-0.8.4 → mapfolding-0.8.5}/README.md +1 -1
  3. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/__init__.py +4 -1
  4. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/basecamp.py +3 -3
  5. mapfolding-0.8.5/mapFolding/beDRY.py +348 -0
  6. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/oeis.py +3 -3
  7. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/reference/hunterNumba.py +1 -1
  8. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/someAssemblyRequired/__init__.py +16 -15
  9. mapfolding-0.8.5/mapFolding/someAssemblyRequired/_theTypes.py +53 -0
  10. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/someAssemblyRequired/_tool_Make.py +8 -1
  11. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/someAssemblyRequired/_tool_Then.py +12 -5
  12. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/someAssemblyRequired/_toolboxAntecedents.py +131 -99
  13. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/someAssemblyRequired/_toolboxContainers.py +35 -7
  14. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/someAssemblyRequired/_toolboxPython.py +17 -31
  15. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/someAssemblyRequired/getLLVMforNoReason.py +2 -2
  16. mapfolding-0.8.5/mapFolding/someAssemblyRequired/newInliner.py +22 -0
  17. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +24 -113
  18. mapfolding-0.8.5/mapFolding/someAssemblyRequired/toolboxNumba.py +358 -0
  19. mapfolding-0.8.5/mapFolding/someAssemblyRequired/transformationTools.py +369 -0
  20. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/theSSOT.py +30 -32
  21. mapfolding-0.8.4/mapFolding/filesystem.py → mapfolding-0.8.5/mapFolding/toolboxFilesystem.py +90 -25
  22. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding.egg-info/PKG-INFO +3 -2
  23. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding.egg-info/SOURCES.txt +3 -4
  24. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding.egg-info/requires.txt +1 -0
  25. {mapfolding-0.8.4 → mapfolding-0.8.5}/pyproject.toml +2 -1
  26. {mapfolding-0.8.4 → mapfolding-0.8.5}/tests/conftest.py +30 -31
  27. {mapfolding-0.8.4 → mapfolding-0.8.5}/tests/test_computations.py +7 -6
  28. {mapfolding-0.8.4 → mapfolding-0.8.5}/tests/test_filesystem.py +2 -2
  29. {mapfolding-0.8.4 → mapfolding-0.8.5}/tests/test_other.py +2 -2
  30. {mapfolding-0.8.4 → mapfolding-0.8.5}/tests/test_tasks.py +2 -2
  31. mapfolding-0.8.4/mapFolding/beDRY.py +0 -175
  32. mapfolding-0.8.4/mapFolding/someAssemblyRequired/_theTypes.py +0 -35
  33. mapfolding-0.8.4/mapFolding/someAssemblyRequired/ingredientsNumba.py +0 -199
  34. mapfolding-0.8.4/mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py +0 -156
  35. mapfolding-0.8.4/mapFolding/someAssemblyRequired/transformDataStructures.py +0 -235
  36. mapfolding-0.8.4/mapFolding/someAssemblyRequired/transformationTools.py +0 -156
  37. {mapfolding-0.8.4 → mapfolding-0.8.5}/LICENSE +0 -0
  38. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/noHomeYet.py +0 -0
  39. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/py.typed +0 -0
  40. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/reference/__init__.py +0 -0
  41. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/reference/flattened.py +0 -0
  42. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/reference/irvineJavaPort.py +0 -0
  43. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/reference/jaxCount.py +0 -0
  44. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/reference/jobsCompleted/[2x19]/p2x19.py +0 -0
  45. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/reference/jobsCompleted/__init__.py +0 -0
  46. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/reference/jobsCompleted/p2x19/p2x19.py +0 -0
  47. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/reference/lunnanNumpy.py +0 -0
  48. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/reference/lunnanWhile.py +0 -0
  49. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/reference/rotatedEntryPoint.py +0 -0
  50. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/reference/total_countPlus1vsPlusN.py +0 -0
  51. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/syntheticModules/__init__.py +0 -0
  52. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/syntheticModules/numbaCount_doTheNeedful.py +0 -0
  53. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding/theDao.py +0 -0
  54. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding.egg-info/dependency_links.txt +0 -0
  55. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding.egg-info/entry_points.txt +0 -0
  56. {mapfolding-0.8.4 → mapfolding-0.8.5}/mapFolding.egg-info/top_level.txt +0 -0
  57. {mapfolding-0.8.4 → mapfolding-0.8.5}/setup.cfg +0 -0
  58. {mapfolding-0.8.4 → mapfolding-0.8.5}/tests/__init__.py +0 -0
  59. {mapfolding-0.8.4 → mapfolding-0.8.5}/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.5
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
@@ -10,8 +10,11 @@ transformation tools.
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:
@@ -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))
@@ -17,6 +17,7 @@ literature and extend sequences beyond their currently known terms.
17
17
  from collections.abc import Callable
18
18
  from datetime import datetime, timedelta
19
19
  from mapFolding.theSSOT import The
20
+ from mapFolding.toolboxFilesystem import writeStringToHere
20
21
  from pathlib import Path
21
22
  from typing import Any, Final, TYPE_CHECKING
22
23
  import argparse
@@ -31,7 +32,7 @@ import warnings
31
32
  if TYPE_CHECKING:
32
33
  from typing import TypedDict
33
34
  else:
34
- TypedDict = dict
35
+ TypedDict = dict[Any, Any]
35
36
 
36
37
  cacheDays = 30
37
38
 
@@ -174,8 +175,7 @@ def getOEISofficial(pathFilenameCache: pathlib.Path, url: str) -> None | str:
174
175
  if not tryCache:
175
176
  httpResponse: urllib.response.addinfourl = urllib.request.urlopen(url)
176
177
  oeisInformation = httpResponse.read().decode('utf-8')
177
- pathFilenameCache.parent.mkdir(parents=True, exist_ok=True)
178
- pathFilenameCache.write_text(oeisInformation)
178
+ writeStringToHere(oeisInformation, pathFilenameCache)
179
179
 
180
180
  if not oeisInformation:
181
181
  warnings.warn(f"Failed to retrieve OEIS sequence information for {pathFilenameCache.stem}.")
@@ -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
+ )
@@ -0,0 +1,53 @@
1
+ """It's still wrong, but typing information is being transmitted between functions, methods, and modules."""
2
+ from typing import Any, TYPE_CHECKING, TypeAlias as typing_TypeAlias, TypeVar as typing_TypeVar
3
+ import ast
4
+
5
+ stuPyd: typing_TypeAlias = str
6
+
7
+ if TYPE_CHECKING:
8
+ """ 3.12 new: ast.ParamSpec, ast.type_param, ast.TypeAlias, ast.TypeVar, ast.TypeVarTuple
9
+ 3.11 new: ast.TryStar"""
10
+ astClassHasDOTnameNotName: typing_TypeAlias = ast.alias | ast.AsyncFunctionDef | ast.ClassDef | ast.FunctionDef | ast.ParamSpec | ast.TypeVar | ast.TypeVarTuple
11
+ astClassHasDOTvalue: typing_TypeAlias = ast.AnnAssign | ast.Assign | ast.Attribute | ast.AugAssign | ast.Await | ast.Constant | ast.DictComp | ast.Expr | ast.FormattedValue | ast.keyword | ast.MatchValue | ast.NamedExpr | ast.Return | ast.Starred | ast.Subscript | ast.TypeAlias | ast.Yield | ast.YieldFrom
12
+ else:
13
+ astClassHasDOTnameNotName = stuPyd
14
+ astClassHasDOTvalue = stuPyd
15
+
16
+ astClassOptionallyHasDOTnameNotName: typing_TypeAlias = ast.ExceptHandler | ast.MatchAs | ast.MatchStar
17
+ astClassHasDOTtarget: typing_TypeAlias = ast.AnnAssign | ast.AsyncFor | ast.AugAssign | ast.comprehension | ast.For | ast.NamedExpr
18
+
19
+ ast_expr_Slice: typing_TypeAlias = ast.expr
20
+ ast_Identifier: typing_TypeAlias = str
21
+ intORlist_ast_type_paramORstr_orNone: typing_TypeAlias = Any
22
+ intORstr_orNone: typing_TypeAlias = Any
23
+ list_ast_type_paramORstr_orNone: typing_TypeAlias = Any
24
+ str_nameDOTname: typing_TypeAlias = stuPyd
25
+ ImaAnnotationType: typing_TypeAlias = ast.Attribute | ast.Constant | ast.Name | ast.Subscript
26
+ ImaAnnotationTypeVar = typing_TypeVar('ImaAnnotationTypeVar', ast.Attribute, ast.Constant, ast.Name, ast.Subscript)
27
+
28
+ Ima_funcTypeUNEDITED: typing_TypeAlias = ast.Attribute | ast.Await | ast.BinOp | ast.BoolOp | ast.Call | ast.Compare | ast.Constant | ast.Dict | ast.DictComp | ast.FormattedValue | ast.GeneratorExp | ast.IfExp | ast.JoinedStr | ast.Lambda | ast.List | ast.ListComp | ast.Name | ast.NamedExpr | ast.Set | ast.SetComp | ast.Slice | ast.Starred | ast.Subscript | ast.Tuple | ast.UnaryOp | ast.Yield | ast.YieldFrom
29
+ Ima_targetTypeUNEDITED: typing_TypeAlias = ast.AST
30
+
31
+ # TODO understand whatever the fuck `typing.TypeVar` is _supposed_ to fucking do.
32
+ TypeCertified = typing_TypeVar('TypeCertified', bound = ast.AST, covariant=True)
33
+ astMosDef = typing_TypeVar('astMosDef', bound=astClassHasDOTnameNotName)
34
+
35
+ 个 = typing_TypeVar('个', bound= ast.AST | ast_Identifier, covariant=True)
36
+
37
+ Ima_ast_boolop: typing_TypeAlias = ast.boolop | ast.And | ast.Or
38
+ Ima_ast_cmpop: typing_TypeAlias = ast.cmpop | ast.Eq | ast.NotEq | ast.Lt | ast.LtE | ast.Gt | ast.GtE | ast.Is | ast.IsNot | ast.In | ast.NotIn
39
+ Ima_ast_excepthandler: typing_TypeAlias = ast.excepthandler | ast.ExceptHandler
40
+ Ima_ast_expr_context: typing_TypeAlias = ast.expr_context | ast.Load | ast.Store | ast.Del
41
+ Ima_ast_expr: typing_TypeAlias = ast.expr | ast.Attribute | ast.Await | ast.BinOp | ast.BoolOp | ast.Call | ast.Compare | ast.Constant | ast.Dict | ast.DictComp | ast.FormattedValue | ast.GeneratorExp | ast.IfExp | ast.JoinedStr | ast.Lambda | ast.List | ast.ListComp | ast.Name | ast.NamedExpr | ast.Set | ast.SetComp | ast.Slice | ast.Starred | ast.Subscript | ast.Tuple | ast.UnaryOp | ast.Yield | ast.YieldFrom
42
+ Ima_ast_mod: typing_TypeAlias = ast.mod | ast.Expression | ast.FunctionType | ast.Interactive | ast.Module
43
+ Ima_ast_operator: typing_TypeAlias = ast.operator | ast.Add | ast.Sub | ast.Mult | ast.MatMult | ast.Div | ast.Mod | ast.Pow | ast.LShift | ast.RShift | ast.BitOr | ast.BitXor | ast.BitAnd | ast.FloorDiv
44
+ Ima_ast_orphan = ast.alias | ast.arg | ast.arguments | ast.comprehension | ast.keyword | ast.match_case | ast.withitem
45
+ iMa_ast_pattern: typing_TypeAlias = ast.pattern | ast.MatchAs | ast.MatchClass | ast.MatchMapping | ast.MatchOr | ast.MatchSequence | ast.MatchSingleton | ast.MatchStar | ast.MatchValue
46
+ Ima_ast_type_ignore: typing_TypeAlias = ast.type_ignore | ast.TypeIgnore
47
+ Ima_ast_unaryop: typing_TypeAlias = ast.unaryop | ast.Invert | ast.Not | ast.UAdd | ast.USub
48
+ if TYPE_CHECKING:
49
+ Ima_ast_stmt: typing_TypeAlias = ast.stmt | ast.AnnAssign | ast.Assert | ast.Assign | ast.AsyncFor | ast.AsyncFunctionDef | ast.AsyncWith | ast.AugAssign | ast.Break | ast.ClassDef | ast.Continue | ast.Delete | ast.Expr | ast.For | ast.FunctionDef | ast.Global | ast.If | ast.Import | ast.ImportFrom | ast.Match | ast.Nonlocal | ast.Pass | ast.Raise | ast.Return | ast.Try | ast.TryStar | ast.TypeAlias | ast.While | ast.With
50
+ Ima_ast_type_param: typing_TypeAlias = ast.type_param | ast.ParamSpec | ast.TypeVar | ast.TypeVarTuple
51
+ else:
52
+ Ima_ast_stmt = stuPyd
53
+ Ima_ast_type_param = stuPyd
@@ -1,5 +1,12 @@
1
1
  from collections.abc import Sequence
2
- from mapFolding.someAssemblyRequired import ast_expr_Slice, ast_Identifier, ImaAnnotationType, intORlist_ast_type_paramORstr_orNone, intORstr_orNone, list_ast_type_paramORstr_orNone
2
+ from mapFolding.someAssemblyRequired import (
3
+ ast_expr_Slice,
4
+ ast_Identifier,
5
+ ImaAnnotationType,
6
+ intORlist_ast_type_paramORstr_orNone,
7
+ intORstr_orNone,
8
+ list_ast_type_paramORstr_orNone,
9
+ )
3
10
  from typing import Any
4
11
  import ast
5
12
 
@@ -1,5 +1,5 @@
1
1
  from collections.abc import Callable, Sequence
2
- from mapFolding.someAssemblyRequired import ast_Identifier, astClassHasDOTvalue, astMosDef
2
+ from mapFolding.someAssemblyRequired import ast_Identifier, astClassHasDOTvalue
3
3
  from typing import Any
4
4
  import ast
5
5
 
@@ -12,8 +12,11 @@ class Then:
12
12
  return workhorse
13
13
 
14
14
  @staticmethod
15
- def appendTo(listOfAny: list[Any]) -> Callable[[ast.AST], None]:
16
- return lambda node: listOfAny.append(node)
15
+ def appendTo(listOfAny: list[Any]) -> Callable[[ast.AST | ast_Identifier], list[Any]]:
16
+ def workhorse(node: ast.AST | ast_Identifier) -> list[Any]:
17
+ listOfAny.append(node)
18
+ return listOfAny
19
+ return workhorse
17
20
 
18
21
  @staticmethod
19
22
  def DOTarg(action: Callable[[Any], Any]) -> Callable[[ast.arg | ast.keyword], ast.arg | ast.keyword]:
@@ -60,6 +63,10 @@ class Then:
60
63
  @staticmethod
61
64
  def replaceWith(astAST: ast.AST | ast_Identifier) -> Callable[[ast.AST], ast.AST | ast_Identifier]:
62
65
  return lambda _replaceMe: astAST
66
+
63
67
  @staticmethod
64
- def updateThis(dictionaryOf_astMosDef: dict[ast_Identifier, astMosDef]) -> Callable[[astMosDef], astMosDef]:
65
- return lambda node: dictionaryOf_astMosDef.setdefault(node.name, node)
68
+ def updateKeyValueIn(key: Callable[..., Any], value: Callable[..., Any], dictionary: dict[Any, Any]) -> Callable[[ast.AST], dict[Any, Any]]:
69
+ def workhorse(node: ast.AST) -> dict[Any, Any]:
70
+ dictionary.setdefault(key(node), value(node))
71
+ return dictionary
72
+ return workhorse