mapFolding 0.3.12__py3-none-any.whl → 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mapFolding/__init__.py +40 -38
- mapFolding/basecamp.py +50 -50
- mapFolding/beDRY.py +336 -336
- mapFolding/oeis.py +262 -262
- mapFolding/reference/flattened.py +294 -293
- mapFolding/reference/hunterNumba.py +126 -126
- mapFolding/reference/irvineJavaPort.py +99 -99
- mapFolding/reference/jax.py +153 -153
- mapFolding/reference/lunnan.py +148 -148
- mapFolding/reference/lunnanNumpy.py +115 -115
- mapFolding/reference/lunnanWhile.py +114 -114
- mapFolding/reference/rotatedEntryPoint.py +183 -183
- mapFolding/reference/total_countPlus1vsPlusN.py +203 -203
- mapFolding/someAssemblyRequired/__init__.py +2 -1
- mapFolding/someAssemblyRequired/getLLVMforNoReason.py +12 -12
- mapFolding/someAssemblyRequired/makeJob.py +48 -48
- mapFolding/someAssemblyRequired/synthesizeModuleJAX.py +17 -17
- mapFolding/someAssemblyRequired/synthesizeNumba.py +343 -633
- mapFolding/someAssemblyRequired/synthesizeNumbaGeneralized.py +371 -0
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +150 -0
- mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +75 -0
- mapFolding/syntheticModules/__init__.py +0 -0
- mapFolding/syntheticModules/numba_countInitialize.py +3 -3
- mapFolding/syntheticModules/numba_countParallel.py +3 -3
- mapFolding/syntheticModules/numba_countSequential.py +3 -3
- mapFolding/syntheticModules/numba_doTheNeedful.py +6 -6
- mapFolding/theDao.py +165 -165
- mapFolding/theSSOT.py +176 -172
- mapFolding/theSSOTnumba.py +90 -74
- mapFolding-0.4.0.dist-info/METADATA +122 -0
- mapFolding-0.4.0.dist-info/RECORD +41 -0
- tests/conftest.py +238 -128
- tests/test_oeis.py +80 -80
- tests/test_other.py +137 -224
- tests/test_tasks.py +21 -21
- tests/test_types.py +2 -2
- mapFolding/someAssemblyRequired/synthesizeNumbaHardcoding.py +0 -188
- mapFolding-0.3.12.dist-info/METADATA +0 -155
- mapFolding-0.3.12.dist-info/RECORD +0 -40
- tests/conftest_tmpRegistry.py +0 -62
- tests/conftest_uniformTests.py +0 -53
- {mapFolding-0.3.12.dist-info → mapFolding-0.4.0.dist-info}/LICENSE +0 -0
- {mapFolding-0.3.12.dist-info → mapFolding-0.4.0.dist-info}/WHEEL +0 -0
- {mapFolding-0.3.12.dist-info → mapFolding-0.4.0.dist-info}/entry_points.txt +0 -0
- {mapFolding-0.3.12.dist-info → mapFolding-0.4.0.dist-info}/top_level.txt +0 -0
|
@@ -8,369 +8,370 @@ import numpy
|
|
|
8
8
|
import sys
|
|
9
9
|
|
|
10
10
|
def countFolds(listDimensions: Sequence[int], computationDivisions = None, CPUlimit: Optional[Union[int, float, bool]] = None):
|
|
11
|
-
|
|
11
|
+
def doWhile():
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
while activeLeafGreaterThan0Condition():
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
if activeLeafIsTheFirstLeafCondition() or leafBelowSentinelIs1Condition():
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
if activeLeafGreaterThanLeavesTotalCondition():
|
|
18
|
+
foldsSubTotalsIncrement()
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
else:
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
findGapsInitializeVariables()
|
|
23
|
+
while loopingTheDimensions():
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
if dimensionsUnconstrainedCondition():
|
|
26
|
+
dimensionsUnconstrainedIncrement()
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
else:
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
leafConnecteeInitialization()
|
|
31
|
+
while loopingLeavesConnectedToActiveLeaf():
|
|
32
|
+
if thereAreComputationDivisionsYouMightSkip():
|
|
33
|
+
countGaps()
|
|
34
|
+
leafConnecteeUpdate()
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
dimension1ndexIncrement()
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
if allDimensionsAreUnconstrained():
|
|
39
|
+
insertUnconstrainedLeaf()
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
indexMiniGapInitialization()
|
|
42
|
+
while loopingToActiveGapCeiling():
|
|
43
|
+
filterCommonGaps()
|
|
44
|
+
indexMiniGapIncrement()
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
while backtrackCondition():
|
|
47
|
+
backtrack()
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
if placeLeafCondition():
|
|
50
|
+
placeLeaf()
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
def activeGapIncrement():
|
|
53
|
+
my[indexMy.gap1ndex] += 1
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
def activeLeafGreaterThan0Condition():
|
|
56
|
+
return my[indexMy.leaf1ndex] > 0
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
def activeLeafGreaterThanLeavesTotalCondition():
|
|
59
|
+
return my[indexMy.leaf1ndex] > the[indexThe.leavesTotal]
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
def activeLeafIsTheFirstLeafCondition():
|
|
62
|
+
return my[indexMy.leaf1ndex] <= 1
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
def activeLeafNotEqualToTaskDivisionsCondition():
|
|
65
|
+
return my[indexMy.leaf1ndex] != the[indexThe.taskDivisions]
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
def allDimensionsAreUnconstrained():
|
|
68
|
+
return my[indexMy.dimensionsUnconstrained] == the[indexThe.dimensionsTotal]
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
def backtrack():
|
|
71
|
+
my[indexMy.leaf1ndex] -= 1
|
|
72
|
+
track[indexTrack.leafBelow, track[indexTrack.leafAbove, my[indexMy.leaf1ndex]]] = track[indexTrack.leafBelow, my[indexMy.leaf1ndex]]
|
|
73
|
+
track[indexTrack.leafAbove, track[indexTrack.leafBelow, my[indexMy.leaf1ndex]]] = track[indexTrack.leafAbove, my[indexMy.leaf1ndex]]
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
def backtrackCondition():
|
|
76
|
+
return my[indexMy.leaf1ndex] > 0 and my[indexMy.gap1ndex] == track[indexTrack.gapRangeStart, my[indexMy.leaf1ndex] - 1]
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
def computationDivisionsCondition():
|
|
79
|
+
return the[indexThe.taskDivisions] == int(False)
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
def countGaps():
|
|
82
|
+
gapsWhere[my[indexMy.gap1ndexCeiling]] = my[indexMy.leafConnectee]
|
|
83
|
+
if track[indexTrack.countDimensionsGapped, my[indexMy.leafConnectee]] == 0:
|
|
84
|
+
gap1ndexCeilingIncrement()
|
|
85
|
+
track[indexTrack.countDimensionsGapped, my[indexMy.leafConnectee]] += 1
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
def dimension1ndexIncrement():
|
|
88
|
+
my[indexMy.dimension1ndex] += 1
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
def dimensionsUnconstrainedCondition():
|
|
91
|
+
return connectionGraph[my[indexMy.dimension1ndex], my[indexMy.leaf1ndex], my[indexMy.leaf1ndex]] == my[indexMy.leaf1ndex]
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
def dimensionsUnconstrainedIncrement():
|
|
94
|
+
my[indexMy.dimensionsUnconstrained] += 1
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
def filterCommonGaps():
|
|
97
|
+
gapsWhere[my[indexMy.gap1ndex]] = gapsWhere[my[indexMy.indexMiniGap]]
|
|
98
|
+
if track[indexTrack.countDimensionsGapped, gapsWhere[my[indexMy.indexMiniGap]]] == the[indexThe.dimensionsTotal] - my[indexMy.dimensionsUnconstrained]:
|
|
99
|
+
activeGapIncrement()
|
|
100
|
+
track[indexTrack.countDimensionsGapped, gapsWhere[my[indexMy.indexMiniGap]]] = 0
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
def findGapsInitializeVariables():
|
|
103
|
+
my[indexMy.dimensionsUnconstrained] = 0
|
|
104
|
+
my[indexMy.gap1ndexCeiling] = track[indexTrack.gapRangeStart, my[indexMy.leaf1ndex] - 1]
|
|
105
|
+
my[indexMy.dimension1ndex] = 1
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
def foldsSubTotalsIncrement():
|
|
108
|
+
foldsSubTotals[my[indexMy.taskIndex]] += the[indexThe.leavesTotal]
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
def gap1ndexCeilingIncrement():
|
|
111
|
+
my[indexMy.gap1ndexCeiling] += 1
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
def indexMiniGapIncrement():
|
|
114
|
+
my[indexMy.indexMiniGap] += 1
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
def indexMiniGapInitialization():
|
|
117
|
+
my[indexMy.indexMiniGap] = my[indexMy.gap1ndex]
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
119
|
+
def insertUnconstrainedLeaf():
|
|
120
|
+
my[indexMy.indexLeaf] = 0
|
|
121
|
+
while my[indexMy.indexLeaf] < my[indexMy.leaf1ndex]:
|
|
122
|
+
gapsWhere[my[indexMy.gap1ndexCeiling]] = my[indexMy.indexLeaf]
|
|
123
|
+
my[indexMy.gap1ndexCeiling] += 1
|
|
124
|
+
my[indexMy.indexLeaf] += 1
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
def leafBelowSentinelIs1Condition():
|
|
127
|
+
return track[indexTrack.leafBelow, 0] == 1
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
129
|
+
def leafConnecteeInitialization():
|
|
130
|
+
my[indexMy.leafConnectee] = connectionGraph[my[indexMy.dimension1ndex], my[indexMy.leaf1ndex], my[indexMy.leaf1ndex]]
|
|
131
|
+
|
|
132
|
+
def leafConnecteeUpdate():
|
|
133
|
+
my[indexMy.leafConnectee] = connectionGraph[my[indexMy.dimension1ndex], my[indexMy.leaf1ndex], track[indexTrack.leafBelow, my[indexMy.leafConnectee]]]
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
def loopingLeavesConnectedToActiveLeaf():
|
|
136
|
+
return my[indexMy.leafConnectee] != my[indexMy.leaf1ndex]
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
def loopingTheDimensions():
|
|
139
|
+
return my[indexMy.dimension1ndex] <= the[indexThe.dimensionsTotal]
|
|
140
140
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
141
|
+
def loopingToActiveGapCeiling():
|
|
142
|
+
return my[indexMy.indexMiniGap] < my[indexMy.gap1ndexCeiling]
|
|
143
|
+
|
|
144
|
+
def placeLeaf():
|
|
145
|
+
my[indexMy.gap1ndex] -= 1
|
|
146
|
+
track[indexTrack.leafAbove, my[indexMy.leaf1ndex]] = gapsWhere[my[indexMy.gap1ndex]]
|
|
147
|
+
track[indexTrack.leafBelow, my[indexMy.leaf1ndex]] = track[indexTrack.leafBelow, track[indexTrack.leafAbove, my[indexMy.leaf1ndex]]]
|
|
148
|
+
track[indexTrack.leafBelow, track[indexTrack.leafAbove, my[indexMy.leaf1ndex]]] = my[indexMy.leaf1ndex]
|
|
149
|
+
track[indexTrack.leafAbove, track[indexTrack.leafBelow, my[indexMy.leaf1ndex]]] = my[indexMy.leaf1ndex]
|
|
150
|
+
track[indexTrack.gapRangeStart, my[indexMy.leaf1ndex]] = my[indexMy.gap1ndex]
|
|
151
|
+
my[indexMy.leaf1ndex] += 1
|
|
152
|
+
|
|
153
|
+
def placeLeafCondition():
|
|
154
|
+
return my[indexMy.leaf1ndex] > 0
|
|
155
|
+
|
|
156
|
+
def taskIndexCondition():
|
|
157
|
+
return my[indexMy.leafConnectee] % the[indexThe.taskDivisions] == my[indexMy.taskIndex]
|
|
158
|
+
|
|
159
|
+
def thereAreComputationDivisionsYouMightSkip():
|
|
160
|
+
if computationDivisionsCondition():
|
|
161
|
+
return True
|
|
162
|
+
if activeLeafNotEqualToTaskDivisionsCondition():
|
|
163
|
+
return True
|
|
164
|
+
if taskIndexCondition():
|
|
165
|
+
return True
|
|
166
|
+
return False
|
|
167
|
+
|
|
168
|
+
stateUniversal = outfitFoldings(listDimensions, computationDivisions=computationDivisions, CPUlimit=CPUlimit)
|
|
169
|
+
connectionGraph: Final[numpy.ndarray] = stateUniversal['connectionGraph']
|
|
170
|
+
foldsSubTotals = stateUniversal['foldsSubTotals']
|
|
171
|
+
gapsWhere = stateUniversal['gapsWhere']
|
|
172
|
+
my = stateUniversal['my']
|
|
173
|
+
the: Final[numpy.ndarray] = stateUniversal['the']
|
|
174
|
+
track = stateUniversal['track']
|
|
175
|
+
|
|
176
|
+
if the[indexThe.taskDivisions] == int(False):
|
|
177
|
+
doWhile()
|
|
178
|
+
else:
|
|
179
|
+
stateUniversal['my'] = my.copy()
|
|
180
|
+
stateUniversal['gapsWhere'] = gapsWhere.copy()
|
|
181
|
+
stateUniversal['track'] = track.copy()
|
|
182
|
+
for indexSherpa in range(the[indexThe.taskDivisions]):
|
|
183
|
+
my = stateUniversal['my'].copy()
|
|
184
|
+
my[indexMy.taskIndex] = indexSherpa
|
|
185
|
+
gapsWhere = stateUniversal['gapsWhere'].copy()
|
|
186
|
+
track = stateUniversal['track'].copy()
|
|
187
|
+
doWhile()
|
|
188
|
+
|
|
189
|
+
return numpy.sum(foldsSubTotals).item()
|
|
190
190
|
|
|
191
191
|
@enum.verify(enum.CONTINUOUS, enum.UNIQUE) if sys.version_info >= (3, 11) else lambda x: x
|
|
192
192
|
class EnumIndices(enum.IntEnum):
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
193
|
+
"""Base class for index enums."""
|
|
194
|
+
@staticmethod
|
|
195
|
+
def _generate_next_value_(name, start, count, last_values):
|
|
196
|
+
"""0-indexed."""
|
|
197
|
+
return count
|
|
198
198
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
199
|
+
def __index__(self) -> int:
|
|
200
|
+
"""Adapt enum to the ultra-rare event of indexing a NumPy 'ndarray', which is not the
|
|
201
|
+
same as `array.array`. See NumPy.org; I think it will be very popular someday."""
|
|
202
|
+
return self
|
|
203
203
|
|
|
204
204
|
class indexMy(EnumIndices):
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
205
|
+
"""Indices for dynamic values."""
|
|
206
|
+
dimension1ndex = enum.auto()
|
|
207
|
+
dimensionsUnconstrained = enum.auto()
|
|
208
|
+
gap1ndex = enum.auto()
|
|
209
|
+
gap1ndexCeiling = enum.auto()
|
|
210
|
+
indexLeaf = enum.auto()
|
|
211
|
+
indexMiniGap = enum.auto()
|
|
212
|
+
leaf1ndex = enum.auto()
|
|
213
|
+
leafConnectee = enum.auto()
|
|
214
|
+
taskIndex = enum.auto()
|
|
215
215
|
|
|
216
216
|
class indexThe(EnumIndices):
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
217
|
+
"""Indices for static values."""
|
|
218
|
+
dimensionsTotal = enum.auto()
|
|
219
|
+
leavesTotal = enum.auto()
|
|
220
|
+
taskDivisions = enum.auto()
|
|
221
221
|
|
|
222
222
|
class indexTrack(EnumIndices):
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
223
|
+
"""Indices for state tracking array."""
|
|
224
|
+
leafAbove = enum.auto()
|
|
225
|
+
leafBelow = enum.auto()
|
|
226
|
+
countDimensionsGapped = enum.auto()
|
|
227
|
+
gapRangeStart = enum.auto()
|
|
228
228
|
|
|
229
229
|
class computationState(TypedDict):
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
230
|
+
connectionGraph: NDArray[integer[Any]]
|
|
231
|
+
foldsSubTotals: NDArray[integer[Any]]
|
|
232
|
+
mapShape: Tuple[int, ...]
|
|
233
|
+
my: NDArray[integer[Any]]
|
|
234
|
+
gapsWhere: NDArray[integer[Any]]
|
|
235
|
+
the: NDArray[integer[Any]]
|
|
236
|
+
track: NDArray[integer[Any]]
|
|
237
237
|
|
|
238
238
|
dtypeLarge = numpy.int64
|
|
239
239
|
dtypeMedium = dtypeLarge
|
|
240
240
|
|
|
241
241
|
def getLeavesTotal(listDimensions: Sequence[int]) -> int:
|
|
242
|
-
|
|
243
|
-
|
|
242
|
+
"""
|
|
243
|
+
How many leaves are in the map.
|
|
244
244
|
|
|
245
|
-
|
|
246
|
-
|
|
245
|
+
Parameters:
|
|
246
|
+
listDimensions: A list of integers representing dimensions.
|
|
247
247
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
248
|
+
Returns:
|
|
249
|
+
productDimensions: The product of all positive integer dimensions.
|
|
250
|
+
"""
|
|
251
|
+
listNonNegative = parseDimensions(listDimensions, 'listDimensions')
|
|
252
|
+
listPositive = [dimension for dimension in listNonNegative if dimension > 0]
|
|
253
253
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
254
|
+
if not listPositive:
|
|
255
|
+
return 0
|
|
256
|
+
else:
|
|
257
|
+
productDimensions = 1
|
|
258
|
+
for dimension in listPositive:
|
|
259
|
+
if dimension > sys.maxsize // productDimensions:
|
|
260
|
+
raise OverflowError(f"I received {dimension=} in {listDimensions=}, but the product of the dimensions exceeds the maximum size of an integer on this system.")
|
|
261
|
+
productDimensions *= dimension
|
|
262
262
|
|
|
263
|
-
|
|
263
|
+
return productDimensions
|
|
264
264
|
|
|
265
265
|
def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrencyLimit: int, CPUlimit: Optional[Union[bool, float, int]], listDimensions: Sequence[int]):
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
266
|
+
if not computationDivisions:
|
|
267
|
+
return 0
|
|
268
|
+
else:
|
|
269
|
+
leavesTotal = getLeavesTotal(listDimensions)
|
|
270
|
+
taskDivisions = 0
|
|
271
|
+
if isinstance(computationDivisions, int):
|
|
272
|
+
taskDivisions = computationDivisions
|
|
273
|
+
elif isinstance(computationDivisions, str):
|
|
274
|
+
computationDivisions = computationDivisions.lower()
|
|
275
|
+
if computationDivisions == "maximum":
|
|
276
|
+
taskDivisions = leavesTotal
|
|
277
|
+
elif computationDivisions == "cpu":
|
|
278
|
+
taskDivisions = min(concurrencyLimit, leavesTotal)
|
|
279
|
+
else:
|
|
280
|
+
raise ValueError("Not my problem.")
|
|
281
|
+
|
|
282
|
+
if taskDivisions > leavesTotal:
|
|
283
|
+
raise ValueError("What are you doing?")
|
|
284
|
+
|
|
285
|
+
return taskDivisions
|
|
285
286
|
|
|
286
287
|
def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optional[Type]) -> NDArray[integer[Any]]:
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
288
|
+
datatype = keywordArguments.get('datatype', dtypeMedium)
|
|
289
|
+
mapShape = validateListDimensions(listDimensions)
|
|
290
|
+
leavesTotal = getLeavesTotal(mapShape)
|
|
291
|
+
arrayDimensions = numpy.array(mapShape, dtype=datatype)
|
|
292
|
+
dimensionsTotal = len(arrayDimensions)
|
|
293
|
+
|
|
294
|
+
cumulativeProduct = numpy.multiply.accumulate([1] + mapShape, dtype=datatype)
|
|
295
|
+
coordinateSystem = numpy.zeros((dimensionsTotal + 1, leavesTotal + 1), dtype=datatype)
|
|
296
|
+
for dimension1ndex in range(1, dimensionsTotal + 1):
|
|
297
|
+
for leaf1ndex in range(1, leavesTotal + 1):
|
|
298
|
+
coordinateSystem[dimension1ndex, leaf1ndex] = ( ((leaf1ndex - 1) // cumulativeProduct[dimension1ndex - 1]) % arrayDimensions[dimension1ndex - 1] + 1 )
|
|
299
|
+
|
|
300
|
+
connectionGraph = numpy.zeros((dimensionsTotal + 1, leavesTotal + 1, leavesTotal + 1), dtype=datatype)
|
|
301
|
+
for dimension1ndex in range(1, dimensionsTotal + 1):
|
|
302
|
+
for activeLeaf1ndex in range(1, leavesTotal + 1):
|
|
303
|
+
for connectee1ndex in range(1, activeLeaf1ndex + 1):
|
|
304
|
+
isFirstCoord = coordinateSystem[dimension1ndex, connectee1ndex] == 1
|
|
305
|
+
isLastCoord = coordinateSystem[dimension1ndex, connectee1ndex] == arrayDimensions[dimension1ndex - 1]
|
|
306
|
+
exceedsActive = connectee1ndex + cumulativeProduct[dimension1ndex - 1] > activeLeaf1ndex
|
|
307
|
+
isEvenParity = (coordinateSystem[dimension1ndex, activeLeaf1ndex] & 1) == (coordinateSystem[dimension1ndex, connectee1ndex] & 1)
|
|
308
|
+
|
|
309
|
+
if (isEvenParity and isFirstCoord) or (not isEvenParity and (isLastCoord or exceedsActive)):
|
|
310
|
+
connectionGraph[dimension1ndex, activeLeaf1ndex, connectee1ndex] = connectee1ndex
|
|
311
|
+
elif isEvenParity and not isFirstCoord:
|
|
312
|
+
connectionGraph[dimension1ndex, activeLeaf1ndex, connectee1ndex] = connectee1ndex - cumulativeProduct[dimension1ndex - 1]
|
|
313
|
+
elif not isEvenParity and not (isLastCoord or exceedsActive):
|
|
314
|
+
connectionGraph[dimension1ndex, activeLeaf1ndex, connectee1ndex] = connectee1ndex + cumulativeProduct[dimension1ndex - 1]
|
|
315
|
+
else:
|
|
316
|
+
connectionGraph[dimension1ndex, activeLeaf1ndex, connectee1ndex] = connectee1ndex
|
|
317
|
+
return connectionGraph
|
|
317
318
|
|
|
318
319
|
def makeDataContainer(shape, datatype: Optional[Type] = None):
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
320
|
+
if datatype is None:
|
|
321
|
+
datatype = dtypeMedium
|
|
322
|
+
return numpy.zeros(shape, dtype=datatype)
|
|
322
323
|
|
|
323
324
|
def outfitFoldings(listDimensions: Sequence[int], computationDivisions: Optional[Union[int, str]] = None, CPUlimit: Optional[Union[bool, float, int]] = None, **keywordArguments: Optional[Type]) -> computationState:
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
325
|
+
datatypeMedium = keywordArguments.get('datatypeMedium', dtypeMedium)
|
|
326
|
+
datatypeLarge = keywordArguments.get('datatypeLarge', dtypeLarge)
|
|
327
|
+
|
|
328
|
+
the = makeDataContainer(len(indexThe), datatypeMedium)
|
|
329
|
+
|
|
330
|
+
mapShape = tuple(sorted(validateListDimensions(listDimensions)))
|
|
331
|
+
the[indexThe.leavesTotal] = getLeavesTotal(mapShape)
|
|
332
|
+
the[indexThe.dimensionsTotal] = len(mapShape)
|
|
333
|
+
concurrencyLimit = setCPUlimit(CPUlimit)
|
|
334
|
+
the[indexThe.taskDivisions] = getTaskDivisions(computationDivisions, concurrencyLimit, CPUlimit, listDimensions)
|
|
335
|
+
|
|
336
|
+
stateInitialized = computationState(
|
|
337
|
+
connectionGraph = makeConnectionGraph(mapShape, datatype=datatypeMedium),
|
|
338
|
+
foldsSubTotals = makeDataContainer(the[indexThe.leavesTotal], datatypeLarge),
|
|
339
|
+
mapShape = mapShape,
|
|
340
|
+
my = makeDataContainer(len(indexMy), datatypeLarge),
|
|
341
|
+
gapsWhere = makeDataContainer(int(the[indexThe.leavesTotal]) * int(the[indexThe.leavesTotal]) + 1, datatypeMedium),
|
|
342
|
+
the = the,
|
|
343
|
+
track = makeDataContainer((len(indexTrack), the[indexThe.leavesTotal] + 1), datatypeLarge)
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
stateInitialized['my'][indexMy.leaf1ndex] = 1
|
|
347
|
+
return stateInitialized
|
|
347
348
|
|
|
348
349
|
def parseDimensions(dimensions: Sequence[int], parameterName: str = 'unnamed parameter') -> List[int]:
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
350
|
+
# listValidated = intInnit(dimensions, parameterName)
|
|
351
|
+
listNOTValidated = dimensions if isinstance(dimensions, (list, tuple)) else list(dimensions)
|
|
352
|
+
listNonNegative = []
|
|
353
|
+
for dimension in listNOTValidated:
|
|
354
|
+
if dimension < 0:
|
|
355
|
+
raise ValueError(f"Dimension {dimension} must be non-negative")
|
|
356
|
+
listNonNegative.append(dimension)
|
|
357
|
+
if not listNonNegative:
|
|
358
|
+
raise ValueError("At least one dimension must be non-negative")
|
|
359
|
+
return listNonNegative
|
|
359
360
|
|
|
360
361
|
def setCPUlimit(CPUlimit: Union[bool, float, int, None]) -> int:
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
362
|
+
# if not (CPUlimit is None or isinstance(CPUlimit, (bool, int, float))):
|
|
363
|
+
# CPUlimit = oopsieKwargsie(CPUlimit)
|
|
364
|
+
# concurrencyLimit = defineConcurrencyLimit(CPUlimit)
|
|
365
|
+
# numba.set_num_threads(concurrencyLimit)
|
|
366
|
+
concurrencyLimitHARDCODED = 1
|
|
367
|
+
concurrencyLimit = concurrencyLimitHARDCODED
|
|
368
|
+
return concurrencyLimit
|
|
368
369
|
|
|
369
370
|
def validateListDimensions(listDimensions: Sequence[int]) -> List[int]:
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
371
|
+
if not listDimensions:
|
|
372
|
+
raise ValueError(f"listDimensions is a required parameter.")
|
|
373
|
+
listNonNegative = parseDimensions(listDimensions, 'listDimensions')
|
|
374
|
+
dimensionsValid = [dimension for dimension in listNonNegative if dimension > 0]
|
|
375
|
+
if len(dimensionsValid) < 2:
|
|
376
|
+
raise NotImplementedError(f"This function requires listDimensions, {listDimensions}, to have at least two dimensions greater than 0. You may want to look at https://oeis.org/.")
|
|
377
|
+
return sorted(dimensionsValid)
|