mapFolding 0.4.2__py3-none-any.whl → 0.4.3__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 +2 -1
- mapFolding/basecamp.py +1 -1
- mapFolding/beDRY.py +61 -39
- mapFolding/oeis.py +18 -17
- mapFolding/someAssemblyRequired/synthesizeNumba.py +40 -48
- mapFolding/someAssemblyRequired/synthesizeNumbaGeneralized.py +83 -12
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +12 -23
- mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +82 -30
- mapFolding/syntheticModules/numbaCount.py +158 -0
- mapFolding/syntheticModules/numba_doTheNeedful.py +5 -12
- mapFolding/theDao.py +75 -75
- mapFolding/theSSOT.py +50 -189
- mapFolding/theSSOTdatatypes.py +168 -0
- {mapFolding-0.4.2.dist-info → mapFolding-0.4.3.dist-info}/METADATA +1 -1
- mapFolding-0.4.3.dist-info/RECORD +40 -0
- tests/conftest.py +30 -1
- tests/test_computations.py +27 -63
- tests/test_oeis.py +7 -10
- mapFolding/syntheticModules/numba_countInitialize.py +0 -52
- mapFolding/syntheticModules/numba_countParallel.py +0 -65
- mapFolding/syntheticModules/numba_countSequential.py +0 -67
- mapFolding/theSSOTnumba.py +0 -125
- mapFolding-0.4.2.dist-info/RECORD +0 -42
- {mapFolding-0.4.2.dist-info → mapFolding-0.4.3.dist-info}/LICENSE +0 -0
- {mapFolding-0.4.2.dist-info → mapFolding-0.4.3.dist-info}/WHEEL +0 -0
- {mapFolding-0.4.2.dist-info → mapFolding-0.4.3.dist-info}/entry_points.txt +0 -0
- {mapFolding-0.4.2.dist-info → mapFolding-0.4.3.dist-info}/top_level.txt +0 -0
mapFolding/__init__.py
CHANGED
|
@@ -11,6 +11,7 @@ from mapFolding.theSSOT import (
|
|
|
11
11
|
|
|
12
12
|
# Datatype management
|
|
13
13
|
from mapFolding.theSSOT import (
|
|
14
|
+
getDatatypeModule,
|
|
14
15
|
hackSSOTdatatype,
|
|
15
16
|
hackSSOTdtype,
|
|
16
17
|
setDatatypeElephino,
|
|
@@ -21,7 +22,7 @@ from mapFolding.theSSOT import (
|
|
|
21
22
|
|
|
22
23
|
# Synthesize modules
|
|
23
24
|
from mapFolding.theSSOT import (
|
|
24
|
-
|
|
25
|
+
formatFilenameModuleDEFAULT,
|
|
25
26
|
getAlgorithmDispatcher,
|
|
26
27
|
getAlgorithmSource,
|
|
27
28
|
getPathJobRootDEFAULT,
|
mapFolding/basecamp.py
CHANGED
|
@@ -17,7 +17,7 @@ def countFolds(listDimensions: Sequence[int]
|
|
|
17
17
|
computationDivisions (None):
|
|
18
18
|
Whether and how to divide the computational work. See notes for details.
|
|
19
19
|
CPUlimit (None): This is only relevant if there are `computationDivisions`: whether and how to limit the CPU usage. See notes for details.
|
|
20
|
-
**keywordArguments: Datatype management.
|
|
20
|
+
**keywordArguments: Datatype management. See `outfitCountFolds` for details.
|
|
21
21
|
Returns:
|
|
22
22
|
foldsTotal: Total number of distinct ways to fold a map of the given dimensions.
|
|
23
23
|
|
mapFolding/beDRY.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""A relatively stable API for oft-needed functionality."""
|
|
2
2
|
from mapFolding import (
|
|
3
3
|
computationState,
|
|
4
|
+
getDatatypeModule,
|
|
4
5
|
getPathJobRootDEFAULT,
|
|
5
6
|
hackSSOTdatatype,
|
|
6
7
|
hackSSOTdtype,
|
|
@@ -9,6 +10,7 @@ from mapFolding import (
|
|
|
9
10
|
setDatatypeElephino,
|
|
10
11
|
setDatatypeFoldsTotal,
|
|
11
12
|
setDatatypeLeavesTotal,
|
|
13
|
+
setDatatypeModule,
|
|
12
14
|
)
|
|
13
15
|
from numpy import dtype, integer, ndarray
|
|
14
16
|
from numpy.typing import DTypeLike, NDArray
|
|
@@ -21,23 +23,25 @@ import pathlib
|
|
|
21
23
|
import sys
|
|
22
24
|
|
|
23
25
|
def getFilenameFoldsTotal(mapShape: Union[Sequence[int], ndarray[Tuple[int], dtype[integer[Any]]]]) -> str:
|
|
24
|
-
"""
|
|
26
|
+
"""Imagine your computer has been counting folds for 70 hours, and when it tries to save your newly discovered value,
|
|
27
|
+
the filename is invalid. I bet you think this function is more important after that thought experiment.
|
|
28
|
+
|
|
29
|
+
Make a standardized filename for the computed value `foldsTotal`.
|
|
25
30
|
|
|
26
31
|
The filename takes into account
|
|
27
32
|
- the dimensions of the map, aka `mapShape`, aka `listDimensions`
|
|
28
33
|
- no spaces in the filename
|
|
29
|
-
- safe filesystem characters
|
|
34
|
+
- safe filesystem characters
|
|
30
35
|
- unique extension
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
- if 'p' is still the first character, I picked that because it was the original identifier for the map shape in Lunnan's code
|
|
36
|
+
- Python-safe strings:
|
|
37
|
+
- no starting with a number
|
|
38
|
+
- no reserved words
|
|
39
|
+
- no dashes or other special characters
|
|
40
|
+
- uh, I can't remember, but I found some other frustrating limitations
|
|
41
|
+
- if 'p' is still the first character of the filename, I picked that because it was the original identifier for the map shape in Lunnan's code
|
|
38
42
|
|
|
39
43
|
Parameters:
|
|
40
|
-
mapShape: A sequence of integers representing the dimensions of the map
|
|
44
|
+
mapShape: A sequence of integers representing the dimensions of the map.
|
|
41
45
|
|
|
42
46
|
Returns:
|
|
43
47
|
filenameFoldsTotal: A filename string in format 'pNxM.foldsTotal' where N,M are sorted dimensions
|
|
@@ -97,33 +101,36 @@ def getPathFilenameFoldsTotal(mapShape: Union[Sequence[int], ndarray[Tuple[int],
|
|
|
97
101
|
|
|
98
102
|
def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrencyLimit: int, CPUlimit: Optional[Union[bool, float, int]], listDimensions: Sequence[int]) -> int:
|
|
99
103
|
"""
|
|
100
|
-
Determines whether
|
|
104
|
+
Determines whether to divide the computation into tasks and how many divisions.
|
|
101
105
|
|
|
102
106
|
Parameters
|
|
103
107
|
----------
|
|
104
|
-
computationDivisions (None)
|
|
108
|
+
computationDivisions (None)
|
|
105
109
|
Specifies how to divide computations:
|
|
106
|
-
- None
|
|
107
|
-
- int: direct set the number of task divisions; cannot exceed the map's total leaves
|
|
108
|
-
-
|
|
109
|
-
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
- `None`: no division of the computation into tasks; sets task divisions to 0.
|
|
111
|
+
- int: direct set the number of task divisions; cannot exceed the map's total leaves.
|
|
112
|
+
- `'maximum'`: divides into `leavesTotal`-many `taskDivisions`.
|
|
113
|
+
- `'cpu'`: limits the divisions to the number of available CPUs, i.e. `concurrencyLimit`.
|
|
114
|
+
concurrencyLimit
|
|
115
|
+
Maximum number of concurrent tasks allowed.
|
|
116
|
+
CPUlimit
|
|
117
|
+
for error reporting.
|
|
118
|
+
listDimensions
|
|
119
|
+
for error reporting.
|
|
114
120
|
|
|
115
121
|
Returns
|
|
116
122
|
-------
|
|
117
|
-
|
|
123
|
+
taskDivisions
|
|
124
|
+
How many tasks must finish before the job can compute the total number of folds; `0` means no tasks, only job.
|
|
118
125
|
|
|
119
126
|
Raises
|
|
120
127
|
------
|
|
121
|
-
|
|
122
|
-
|
|
128
|
+
ValueError
|
|
129
|
+
If computationDivisions is an unsupported type or if resulting task divisions exceed total leaves.
|
|
123
130
|
|
|
124
131
|
Notes
|
|
125
132
|
-----
|
|
126
|
-
Task divisions should not exceed total leaves
|
|
133
|
+
Task divisions should not exceed total leaves or the folds will be over-counted.
|
|
127
134
|
"""
|
|
128
135
|
taskDivisions = 0
|
|
129
136
|
leavesTotal = getLeavesTotal(listDimensions)
|
|
@@ -133,9 +140,9 @@ def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrenc
|
|
|
133
140
|
taskDivisions = computationDivisions
|
|
134
141
|
elif isinstance(computationDivisions, str):
|
|
135
142
|
computationDivisions = computationDivisions.lower()
|
|
136
|
-
if computationDivisions ==
|
|
143
|
+
if computationDivisions == 'maximum':
|
|
137
144
|
taskDivisions = leavesTotal
|
|
138
|
-
elif computationDivisions ==
|
|
145
|
+
elif computationDivisions == 'cpu':
|
|
139
146
|
taskDivisions = min(concurrencyLimit, leavesTotal)
|
|
140
147
|
else:
|
|
141
148
|
raise ValueError(f"I received {computationDivisions} for the parameter, `computationDivisions`, but the so-called programmer didn't implement code for that.")
|
|
@@ -196,15 +203,21 @@ def makeDataContainer(shape: Union[int, Tuple[int, ...]], datatype: Optional[DTy
|
|
|
196
203
|
Parameters:
|
|
197
204
|
shape: The shape of the array. Can be an integer for 1D arrays
|
|
198
205
|
or a tuple of integers for multi-dimensional arrays.
|
|
199
|
-
datatype: The desired data type for the array.
|
|
200
|
-
If None
|
|
206
|
+
datatype ('dtypeFoldsTotal'): The desired data type for the array.
|
|
207
|
+
If `None`, defaults to 'dtypeFoldsTotal'. Defaults to None.
|
|
201
208
|
|
|
202
209
|
Returns:
|
|
203
210
|
dataContainer: A new array of given shape and type, filled with zeros.
|
|
211
|
+
|
|
212
|
+
Notes:
|
|
213
|
+
If a version of the algorithm were to use something other than numpy, such as JAX or CUDA, because other
|
|
214
|
+
functions use this function, it would be much easier to change the datatype "ecosystem".
|
|
204
215
|
"""
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
216
|
+
numpyDtype = datatype or hackSSOTdtype('dtypeFoldsTotal')
|
|
217
|
+
if 'numpy' == getDatatypeModule():
|
|
218
|
+
return numpy.zeros(shape, dtype=numpyDtype)
|
|
219
|
+
else:
|
|
220
|
+
raise NotImplementedError("Somebody done broke it.")
|
|
208
221
|
|
|
209
222
|
def outfitCountFolds(listDimensions: Sequence[int], computationDivisions: Optional[Union[int, str]] = None, CPUlimit: Optional[Union[bool, float, int]] = None, **keywordArguments: Optional[Union[str, bool]]) -> computationState:
|
|
210
223
|
"""
|
|
@@ -214,11 +227,12 @@ def outfitCountFolds(listDimensions: Sequence[int], computationDivisions: Option
|
|
|
214
227
|
listDimensions: The dimensions of the map to be folded
|
|
215
228
|
computationDivisions (None): see `getTaskDivisions`
|
|
216
229
|
CPUlimit (None): see `setCPUlimit`
|
|
217
|
-
**keywordArguments: Datatype management.
|
|
230
|
+
**keywordArguments: Datatype management, it's complicated: see the code below.
|
|
218
231
|
|
|
219
232
|
Returns:
|
|
220
233
|
stateInitialized: The initialized computation state
|
|
221
234
|
"""
|
|
235
|
+
# keywordArguments START
|
|
222
236
|
kwourGrapes = keywordArguments.get('sourGrapes', None)
|
|
223
237
|
if kwourGrapes:
|
|
224
238
|
sourGrapes = True
|
|
@@ -240,6 +254,13 @@ def outfitCountFolds(listDimensions: Sequence[int], computationDivisions: Option
|
|
|
240
254
|
ImaSetTheDatatype = str(ImaSetTheDatatype)
|
|
241
255
|
setDatatypeLeavesTotal(ImaSetTheDatatype, sourGrapes)
|
|
242
256
|
|
|
257
|
+
# NOTE well: this might be only hypothetical because as of this writing, `makeDataContainer` only makes numpy.zeros. But it's here in case things change.
|
|
258
|
+
ImaSetTheDatatype = keywordArguments.get('datatypeModule', None)
|
|
259
|
+
if ImaSetTheDatatype:
|
|
260
|
+
ImaSetTheDatatype = str(ImaSetTheDatatype)
|
|
261
|
+
setDatatypeModule(ImaSetTheDatatype, sourGrapes)
|
|
262
|
+
# keywordArguments END
|
|
263
|
+
|
|
243
264
|
my = makeDataContainer(len(indexMy), hackSSOTdtype('my'))
|
|
244
265
|
|
|
245
266
|
mapShape = tuple(sorted(validateListDimensions(listDimensions)))
|
|
@@ -265,16 +286,16 @@ def outfitCountFolds(listDimensions: Sequence[int], computationDivisions: Option
|
|
|
265
286
|
|
|
266
287
|
def parseDimensions(dimensions: Sequence[int], parameterName: str = 'listDimensions') -> List[int]:
|
|
267
288
|
"""
|
|
268
|
-
Parse and validate dimensions are non-negative integers.
|
|
289
|
+
Parse and validate the dimensions are non-negative integers.
|
|
269
290
|
|
|
270
291
|
Parameters:
|
|
271
|
-
dimensions: Sequence of integers representing dimensions
|
|
272
|
-
parameterName ('listDimensions'): Name of the parameter for error messages. Defaults to 'listDimensions'
|
|
292
|
+
dimensions: Sequence of integers representing dimensions.
|
|
293
|
+
parameterName ('listDimensions'): Name of the parameter for error messages. Defaults to 'listDimensions'.
|
|
273
294
|
Returns:
|
|
274
|
-
listNonNegative: List of validated non-negative integers
|
|
295
|
+
listNonNegative: List of validated non-negative integers.
|
|
275
296
|
Raises:
|
|
276
|
-
ValueError: If any dimension is negative or if the list is empty
|
|
277
|
-
TypeError: If any element cannot be converted to integer (raised by intInnit)
|
|
297
|
+
ValueError: If any dimension is negative or if the list is empty.
|
|
298
|
+
TypeError: If any element cannot be converted to integer (raised by `intInnit`).
|
|
278
299
|
"""
|
|
279
300
|
listValidated = intInnit(dimensions, parameterName)
|
|
280
301
|
listNonNegative = []
|
|
@@ -335,6 +356,7 @@ def setCPUlimit(CPUlimit: Optional[Any]) -> int:
|
|
|
335
356
|
|
|
336
357
|
concurrencyLimit = int(defineConcurrencyLimit(CPUlimit))
|
|
337
358
|
numba.set_num_threads(concurrencyLimit)
|
|
359
|
+
concurrencyLimit = numba.get_num_threads()
|
|
338
360
|
|
|
339
361
|
return concurrencyLimit
|
|
340
362
|
|
|
@@ -349,7 +371,7 @@ def validateListDimensions(listDimensions: Sequence[int]) -> List[int]:
|
|
|
349
371
|
dimensionsValidSorted: A list, with at least two elements, of only positive integers.
|
|
350
372
|
|
|
351
373
|
Raises:
|
|
352
|
-
ValueError: If the input listDimensions is
|
|
374
|
+
ValueError: If the input listDimensions is empty.
|
|
353
375
|
NotImplementedError: If the resulting list of positive dimensions has fewer than two elements.
|
|
354
376
|
"""
|
|
355
377
|
if not listDimensions:
|
mapFolding/oeis.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
_only_ things that implement _only_ the OEIS."""
|
|
3
3
|
from datetime import datetime, timedelta
|
|
4
4
|
from mapFolding import countFolds, getPathPackage
|
|
5
|
-
from typing import Any, Callable, Dict, Final, List, Tuple, TYPE_CHECKING
|
|
5
|
+
from typing import Any, Callable, Dict, Final, List, Tuple, TYPE_CHECKING
|
|
6
6
|
import argparse
|
|
7
7
|
import pathlib
|
|
8
8
|
import random
|
|
@@ -24,8 +24,8 @@ _pathCache = getPathPackage() / ".cache"
|
|
|
24
24
|
|
|
25
25
|
class SettingsOEIS(TypedDict):
|
|
26
26
|
description: str
|
|
27
|
-
offset: int
|
|
28
27
|
getMapShape: Callable[[int], List[int]]
|
|
28
|
+
offset: int
|
|
29
29
|
valuesBenchmark: List[int]
|
|
30
30
|
valuesKnown: Dict[int, int]
|
|
31
31
|
valuesTestParallelization: List[int]
|
|
@@ -68,7 +68,7 @@ settingsOEIShardcodedValues: Dict[str, Dict[str, Any]] = {
|
|
|
68
68
|
oeisIDsImplemented: Final[List[str]] = sorted([oeisID.upper().strip() for oeisID in settingsOEIShardcodedValues.keys()])
|
|
69
69
|
"""Directly implemented OEIS IDs; standardized, e.g., 'A001415'."""
|
|
70
70
|
|
|
71
|
-
def
|
|
71
|
+
def validateOEISid(oeisIDcandidate: str) -> str:
|
|
72
72
|
"""
|
|
73
73
|
Validates an OEIS sequence ID against implemented sequences.
|
|
74
74
|
|
|
@@ -98,13 +98,14 @@ def _validateOEISid(oeisIDcandidate: str) -> str:
|
|
|
98
98
|
f"Available sequences:\n{_formatOEISsequenceInfo()}"
|
|
99
99
|
)
|
|
100
100
|
|
|
101
|
-
def
|
|
102
|
-
oeisID =
|
|
101
|
+
def getFilenameOEISbFile(oeisID: str) -> str:
|
|
102
|
+
oeisID = validateOEISid(oeisID)
|
|
103
103
|
return f"b{oeisID[1:]}.txt"
|
|
104
104
|
|
|
105
105
|
def _parseBFileOEIS(OEISbFile: str, oeisID: str) -> Dict[int, int]:
|
|
106
106
|
"""
|
|
107
107
|
Parses the content of an OEIS b-file for a given sequence ID.
|
|
108
|
+
|
|
108
109
|
This function processes a multiline string representing an OEIS b-file and
|
|
109
110
|
creates a dictionary mapping integer indices to their corresponding sequence
|
|
110
111
|
values. The first line of the b-file is expected to contain a comment that
|
|
@@ -133,7 +134,7 @@ def _parseBFileOEIS(OEISbFile: str, oeisID: str) -> Dict[int, int]:
|
|
|
133
134
|
OEISsequence[n] = aOFn
|
|
134
135
|
return OEISsequence
|
|
135
136
|
|
|
136
|
-
def
|
|
137
|
+
def getOEISofficial(pathFilenameCache: pathlib.Path, url: str) -> None | str:
|
|
137
138
|
cacheDays = 7
|
|
138
139
|
tryCache = False
|
|
139
140
|
if pathFilenameCache.exists():
|
|
@@ -158,7 +159,7 @@ def _getOEISofficial(pathFilenameCache: pathlib.Path, url: str) -> None | str:
|
|
|
158
159
|
|
|
159
160
|
return oeisInformation
|
|
160
161
|
|
|
161
|
-
def
|
|
162
|
+
def getOEISidValues(oeisID: str) -> Dict[int, int]:
|
|
162
163
|
"""
|
|
163
164
|
Retrieves the specified OEIS sequence as a dictionary mapping integer indices
|
|
164
165
|
to their corresponding values.
|
|
@@ -177,21 +178,21 @@ def _getOEISidValues(oeisID: str) -> Dict[int, int]:
|
|
|
177
178
|
IOError: If there is an error reading from or writing to the local cache.
|
|
178
179
|
"""
|
|
179
180
|
|
|
180
|
-
pathFilenameCache = _pathCache /
|
|
181
|
-
url = f"https://oeis.org/{oeisID}/{
|
|
181
|
+
pathFilenameCache = _pathCache / getFilenameOEISbFile(oeisID)
|
|
182
|
+
url = f"https://oeis.org/{oeisID}/{getFilenameOEISbFile(oeisID)}"
|
|
182
183
|
|
|
183
|
-
oeisInformation =
|
|
184
|
+
oeisInformation = getOEISofficial(pathFilenameCache, url)
|
|
184
185
|
|
|
185
186
|
if oeisInformation:
|
|
186
187
|
return _parseBFileOEIS(oeisInformation, oeisID)
|
|
187
188
|
return {-1: -1}
|
|
188
189
|
|
|
189
|
-
def
|
|
190
|
-
oeisID =
|
|
190
|
+
def getOEISidInformation(oeisID: str) -> Tuple[str, int]:
|
|
191
|
+
oeisID = validateOEISid(oeisID)
|
|
191
192
|
pathFilenameCache = _pathCache / f"{oeisID}.txt"
|
|
192
193
|
url = f"https://oeis.org/search?q=id:{oeisID}&fmt=text"
|
|
193
194
|
|
|
194
|
-
oeisInformation =
|
|
195
|
+
oeisInformation = getOEISofficial(pathFilenameCache, url)
|
|
195
196
|
|
|
196
197
|
if not oeisInformation:
|
|
197
198
|
return "Not found", -1
|
|
@@ -221,8 +222,8 @@ def _getOEISidInformation(oeisID: str) -> Tuple[str, int]:
|
|
|
221
222
|
def makeSettingsOEIS() -> Dict[str, SettingsOEIS]:
|
|
222
223
|
settingsTarget = {}
|
|
223
224
|
for oeisID in oeisIDsImplemented:
|
|
224
|
-
valuesKnownSherpa =
|
|
225
|
-
descriptionSherpa, offsetSherpa =
|
|
225
|
+
valuesKnownSherpa = getOEISidValues(oeisID)
|
|
226
|
+
descriptionSherpa, offsetSherpa = getOEISidInformation(oeisID)
|
|
226
227
|
settingsTarget[oeisID] = SettingsOEIS(
|
|
227
228
|
description=descriptionSherpa,
|
|
228
229
|
offset=offsetSherpa,
|
|
@@ -282,7 +283,7 @@ def oeisIDfor_n(oeisID: str, n: int) -> int:
|
|
|
282
283
|
ValueError: If n is negative.
|
|
283
284
|
KeyError: If the OEIS sequence ID is not directly implemented.
|
|
284
285
|
"""
|
|
285
|
-
oeisID =
|
|
286
|
+
oeisID = validateOEISid(oeisID)
|
|
286
287
|
|
|
287
288
|
if not isinstance(n, int) or n < 0:
|
|
288
289
|
raise ValueError("`n` must be non-negative integer.")
|
|
@@ -328,7 +329,7 @@ def clearOEIScache() -> None:
|
|
|
328
329
|
return
|
|
329
330
|
for oeisID in settingsOEIS:
|
|
330
331
|
( _pathCache / f"{oeisID}.txt" ).unlink(missing_ok=True)
|
|
331
|
-
( _pathCache /
|
|
332
|
+
( _pathCache / getFilenameOEISbFile(oeisID) ).unlink(missing_ok=True)
|
|
332
333
|
print(f"Cache cleared from {_pathCache}")
|
|
333
334
|
|
|
334
335
|
def getOEISids() -> None:
|
|
@@ -9,10 +9,11 @@ def insertArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arra
|
|
|
9
9
|
# NOTE hack
|
|
10
10
|
constructorName = constructorName.replace('ndarray', 'array')
|
|
11
11
|
argData_dtype: numpy.dtype = arrayTarget.dtype
|
|
12
|
-
|
|
12
|
+
datatypeName = argData_dtype.name
|
|
13
|
+
dtypeAsName = f"{moduleConstructor}_{datatypeName}"
|
|
13
14
|
|
|
14
15
|
allImports.addImportFromStr(moduleConstructor, constructorName)
|
|
15
|
-
allImports.addImportFromStr(moduleConstructor,
|
|
16
|
+
allImports.addImportFromStr(moduleConstructor, datatypeName, dtypeAsName)
|
|
16
17
|
|
|
17
18
|
def insertAssign(assignee: str, arraySlice: numpy.ndarray) -> None:
|
|
18
19
|
nonlocal FunctionDefTarget
|
|
@@ -20,7 +21,7 @@ def insertArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arra
|
|
|
20
21
|
astStatement = cast(ast.Expr, ast.parse(onlyDataRLE).body[0])
|
|
21
22
|
dataAst = astStatement.value
|
|
22
23
|
|
|
23
|
-
arrayCall = Then.make_astCall(name=constructorName, args=[dataAst], list_astKeywords=[ast.keyword(arg='dtype', value=ast.Name(id=
|
|
24
|
+
arrayCall = Then.make_astCall(name=constructorName, args=[dataAst], list_astKeywords=[ast.keyword(arg='dtype', value=ast.Name(id=dtypeAsName, ctx=ast.Load()))])
|
|
24
25
|
|
|
25
26
|
assignment = ast.Assign(targets=[ast.Name(id=assignee, ctx=ast.Store())], value=arrayCall)#NOTE
|
|
26
27
|
FunctionDefTarget.body.insert(0, assignment)
|
|
@@ -33,7 +34,8 @@ def insertArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arra
|
|
|
33
34
|
|
|
34
35
|
return FunctionDefTarget, allImports
|
|
35
36
|
|
|
36
|
-
def
|
|
37
|
+
def findAndReplaceTrackArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str , arrayTarget: numpy.ndarray , allImports: UniversalImportTracker) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
|
|
38
|
+
|
|
37
39
|
arrayType = type(arrayTarget)
|
|
38
40
|
moduleConstructor = arrayType.__module__
|
|
39
41
|
constructorName = arrayType.__name__
|
|
@@ -41,47 +43,41 @@ def findAndReplaceArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: s
|
|
|
41
43
|
constructorName = constructorName.replace('ndarray', 'array')
|
|
42
44
|
allImports.addImportFromStr(moduleConstructor, constructorName)
|
|
43
45
|
|
|
44
|
-
for
|
|
45
|
-
if
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
astSubscript: ast.Subscript = stmt.value
|
|
51
|
-
if isinstance(astSubscript.value, ast.Name) and astSubscript.value.id == identifier and isinstance(astSubscript.slice, ast.Attribute):
|
|
52
|
-
indexAs_astAttribute: ast.Attribute = astSubscript.slice
|
|
53
|
-
indexAsStr = ast.unparse(indexAs_astAttribute)
|
|
54
|
-
arraySlice = arrayTarget[eval(indexAsStr)]
|
|
46
|
+
for statement in FunctionDefTarget.body.copy():
|
|
47
|
+
if ifThis.isUnpackingAnArray(identifier)(statement):
|
|
48
|
+
datatypeName = hackSSOTdatatype(statement.targets[0].id) # type: ignore
|
|
49
|
+
dtypeAsName = f"{moduleConstructor}_{datatypeName}"
|
|
50
|
+
indexAsStr = ast.unparse(statement.value.slice) # type: ignore
|
|
51
|
+
arraySlice = arrayTarget[eval(indexAsStr)]
|
|
55
52
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
onlyDataRLE = autoDecodingRLE(arraySlice, addSpaces=True)
|
|
54
|
+
astStatement = cast(ast.Expr, ast.parse(onlyDataRLE).body[0])
|
|
55
|
+
dataAst = astStatement.value
|
|
59
56
|
|
|
60
|
-
|
|
57
|
+
arrayCall = Then.make_astCall(name=constructorName, args=[dataAst], list_astKeywords=[ast.keyword(arg='dtype', value=ast.Name(id=dtypeAsName, ctx=ast.Load()))])
|
|
61
58
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
assignment = ast.Assign(targets=[statement.targets[0]], value=arrayCall) # type: ignore
|
|
60
|
+
FunctionDefTarget.body.insert(0, assignment)
|
|
61
|
+
FunctionDefTarget.body.remove(statement)
|
|
62
|
+
allImports.addImportFromStr(moduleConstructor, datatypeName, dtypeAsName)
|
|
65
63
|
return FunctionDefTarget, allImports
|
|
66
64
|
|
|
67
65
|
def findAndReplaceArraySubscriptIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arrayTarget: numpy.ndarray, Z0Z_listChaff: List[str], allImports: UniversalImportTracker) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
|
|
68
66
|
moduleConstructor = Z0Z_getDatatypeModuleScalar()
|
|
69
|
-
for
|
|
70
|
-
if
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
FunctionDefTarget.body.insert(0, assignment)
|
|
84
|
-
FunctionDefTarget.body.remove(stmt)
|
|
67
|
+
for statement in FunctionDefTarget.body.copy():
|
|
68
|
+
if ifThis.isUnpackingAnArray(identifier)(statement):
|
|
69
|
+
astSubscript: ast.Subscript = statement.value # type: ignore
|
|
70
|
+
astAssignee: ast.Name = statement.targets[0] # type: ignore
|
|
71
|
+
argData_dtypeName = hackSSOTdatatype(astAssignee.id)
|
|
72
|
+
allImports.addImportFromStr(moduleConstructor, argData_dtypeName)
|
|
73
|
+
indexAs_astAttribute: ast.Attribute = astSubscript.slice # type: ignore
|
|
74
|
+
indexAsStr = ast.unparse(indexAs_astAttribute)
|
|
75
|
+
argDataSlice: int = arrayTarget[eval(indexAsStr)].item()
|
|
76
|
+
astCall = ast.Call(func=ast.Name(id=argData_dtypeName, ctx=ast.Load()), args=[ast.Constant(value=argDataSlice)], keywords=[])
|
|
77
|
+
assignment = ast.Assign(targets=[astAssignee], value=astCall)
|
|
78
|
+
if astAssignee.id not in Z0Z_listChaff:
|
|
79
|
+
FunctionDefTarget.body.insert(0, assignment)
|
|
80
|
+
FunctionDefTarget.body.remove(statement)
|
|
85
81
|
return FunctionDefTarget, allImports
|
|
86
82
|
|
|
87
83
|
def removeAssignTargetFrom_body(FunctionDefTarget: ast.FunctionDef, identifier: str) -> ast.FunctionDef:
|
|
@@ -136,13 +132,10 @@ def findThingyReplaceWithConstantIn_body(FunctionDefTarget: ast.FunctionDef, obj
|
|
|
136
132
|
return newFunction
|
|
137
133
|
|
|
138
134
|
def findAstNameReplaceWithConstantIn_body(FunctionDefTarget: ast.FunctionDef, name: str, value: int) -> ast.FunctionDef:
|
|
139
|
-
def findName(node: ast.AST) -> bool:
|
|
140
|
-
return isinstance(node, ast.Name) and node.id == name
|
|
141
|
-
|
|
142
135
|
def replaceWithConstant(node: ast.AST) -> ast.AST:
|
|
143
136
|
return ast.copy_location(ast.Constant(value=value), node)
|
|
144
137
|
|
|
145
|
-
return cast(ast.FunctionDef, NodeReplacer(
|
|
138
|
+
return cast(ast.FunctionDef, NodeReplacer(ifThis.nameIs(name), replaceWithConstant).visit(FunctionDefTarget))
|
|
146
139
|
|
|
147
140
|
def insertReturnStatementIn_body(FunctionDefTarget: ast.FunctionDef, arrayTarget: numpy.ndarray, allImports: UniversalImportTracker) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
|
|
148
141
|
"""Add multiplication and return statement to function, properly constructing AST nodes."""
|
|
@@ -155,6 +148,8 @@ def insertReturnStatementIn_body(FunctionDefTarget: ast.FunctionDef, arrayTarget
|
|
|
155
148
|
|
|
156
149
|
returnStatement = ast.Return(value=multiplyOperation)
|
|
157
150
|
|
|
151
|
+
datatype = hackSSOTdatatype(Z0Z_identifierCountFolds)
|
|
152
|
+
FunctionDefTarget.returns = ast.Name(id=datatype, ctx=ast.Load())
|
|
158
153
|
datatypeModuleScalar = Z0Z_getDatatypeModuleScalar()
|
|
159
154
|
allImports.addImportFromStr(datatypeModuleScalar, datatype)
|
|
160
155
|
|
|
@@ -240,9 +235,7 @@ if __name__ == '__main__':
|
|
|
240
235
|
"""
|
|
241
236
|
return ast.parse(linesLaunch)
|
|
242
237
|
|
|
243
|
-
def
|
|
244
|
-
astModule: ast.Module = ast.parse(pythonSource, type_comments=True)
|
|
245
|
-
|
|
238
|
+
def makeFunctionDef(astModule: ast.Module, callableTarget: str, parametersNumba: Optional[ParametersNumba]=None, inlineCallables: Optional[bool]=False, unpackArrays: Optional[bool]=False, allImports: Optional[UniversalImportTracker]=None) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
|
|
246
239
|
if allImports is None:
|
|
247
240
|
allImports = UniversalImportTracker()
|
|
248
241
|
for statement in astModule.body:
|
|
@@ -253,6 +246,7 @@ def makeAstModuleForOneCallable(pythonSource: str, callableTarget: str, paramete
|
|
|
253
246
|
dictionaryFunctionDef = {statement.name: statement for statement in astModule.body if isinstance(statement, ast.FunctionDef)}
|
|
254
247
|
callableInlinerWorkhorse = RecursiveInliner(dictionaryFunctionDef)
|
|
255
248
|
# NOTE the inliner assumes each function is not called more than once
|
|
249
|
+
# TODO change the inliner to handle multiple calls to the same function
|
|
256
250
|
FunctionDefTarget = callableInlinerWorkhorse.inlineFunctionBody(callableTarget)
|
|
257
251
|
else:
|
|
258
252
|
FunctionDefTarget = next((node for node in astModule.body if isinstance(node, ast.FunctionDef) and node.name == callableTarget), None)
|
|
@@ -270,9 +264,7 @@ def makeAstModuleForOneCallable(pythonSource: str, callableTarget: str, paramete
|
|
|
270
264
|
FunctionDefTarget = cast(ast.FunctionDef, unpacker.visit(FunctionDefTarget))
|
|
271
265
|
ast.fix_missing_locations(FunctionDefTarget)
|
|
272
266
|
|
|
273
|
-
|
|
274
|
-
ast.fix_missing_locations(astModule)
|
|
275
|
-
return ast.unparse(astModule)
|
|
267
|
+
return FunctionDefTarget, allImports
|
|
276
268
|
|
|
277
269
|
def decorateCallableWithNumba(FunctionDefTarget: ast.FunctionDef, allImports: UniversalImportTracker, parametersNumba: Optional[ParametersNumba]=None) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
|
|
278
270
|
def Z0Z_UnhandledDecorators(astCallable: ast.FunctionDef) -> ast.FunctionDef:
|