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.
- {mapfolding-0.8.4 → mapfolding-0.8.6}/PKG-INFO +3 -2
- {mapfolding-0.8.4 → mapfolding-0.8.6}/README.md +1 -1
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/__init__.py +10 -6
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/basecamp.py +3 -3
- mapfolding-0.8.6/mapFolding/beDRY.py +348 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/oeis.py +41 -26
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/hunterNumba.py +1 -1
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/someAssemblyRequired/__init__.py +16 -15
- mapfolding-0.8.6/mapFolding/someAssemblyRequired/_theTypes.py +53 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/someAssemblyRequired/_tool_Make.py +13 -5
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/someAssemblyRequired/_tool_Then.py +12 -5
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/someAssemblyRequired/_toolboxAntecedents.py +131 -99
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/someAssemblyRequired/_toolboxContainers.py +92 -15
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/someAssemblyRequired/_toolboxPython.py +17 -31
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/someAssemblyRequired/getLLVMforNoReason.py +2 -2
- mapfolding-0.8.6/mapFolding/someAssemblyRequired/newInliner.py +22 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +65 -116
- mapfolding-0.8.6/mapFolding/someAssemblyRequired/toolboxNumba.py +364 -0
- mapfolding-0.8.6/mapFolding/someAssemblyRequired/transformationTools.py +377 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/syntheticModules/numbaCount_doTheNeedful.py +0 -1
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/theSSOT.py +30 -33
- mapfolding-0.8.4/mapFolding/filesystem.py → mapfolding-0.8.6/mapFolding/toolboxFilesystem.py +90 -25
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding.egg-info/PKG-INFO +3 -2
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding.egg-info/SOURCES.txt +3 -5
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding.egg-info/requires.txt +1 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/pyproject.toml +2 -1
- {mapfolding-0.8.4 → mapfolding-0.8.6}/tests/conftest.py +30 -31
- {mapfolding-0.8.4 → mapfolding-0.8.6}/tests/test_computations.py +8 -7
- {mapfolding-0.8.4 → mapfolding-0.8.6}/tests/test_filesystem.py +2 -2
- {mapfolding-0.8.4 → mapfolding-0.8.6}/tests/test_other.py +2 -2
- {mapfolding-0.8.4 → mapfolding-0.8.6}/tests/test_tasks.py +3 -3
- mapfolding-0.8.4/mapFolding/beDRY.py +0 -175
- mapfolding-0.8.4/mapFolding/noHomeYet.py +0 -32
- mapfolding-0.8.4/mapFolding/someAssemblyRequired/_theTypes.py +0 -35
- mapfolding-0.8.4/mapFolding/someAssemblyRequired/ingredientsNumba.py +0 -199
- mapfolding-0.8.4/mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py +0 -156
- mapfolding-0.8.4/mapFolding/someAssemblyRequired/transformDataStructures.py +0 -235
- mapfolding-0.8.4/mapFolding/someAssemblyRequired/transformationTools.py +0 -156
- {mapfolding-0.8.4 → mapfolding-0.8.6}/LICENSE +0 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/py.typed +0 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/__init__.py +0 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/flattened.py +0 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/irvineJavaPort.py +0 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/jaxCount.py +0 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/jobsCompleted/[2x19]/p2x19.py +0 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/jobsCompleted/__init__.py +0 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/jobsCompleted/p2x19/p2x19.py +0 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/lunnanNumpy.py +0 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/lunnanWhile.py +0 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/rotatedEntryPoint.py +0 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/reference/total_countPlus1vsPlusN.py +0 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/syntheticModules/__init__.py +0 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding/theDao.py +0 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding.egg-info/dependency_links.txt +0 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding.egg-info/entry_points.txt +0 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/mapFolding.egg-info/top_level.txt +0 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/setup.cfg +0 -0
- {mapfolding-0.8.4 → mapfolding-0.8.6}/tests/__init__.py +0 -0
- {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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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 =
|
|
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
|
|
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.
|
|
8
|
-
2.
|
|
9
|
-
3.
|
|
10
|
-
4.
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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=
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
Z0Z_lameFindReplace,
|
|
64
|
-
Z0Z_makeDictionaryReplacementStatements,
|
|
65
|
-
)
|
|
60
|
+
from mapFolding.someAssemblyRequired._toolboxContainers import (
|
|
61
|
+
IngredientsFunction,
|
|
62
|
+
IngredientsModule,
|
|
63
|
+
LedgerOfImports,
|
|
64
|
+
RecipeSynthesizeFlow,
|
|
65
|
+
ShatteredDataclass,
|
|
66
|
+
)
|