mapFolding 0.4.2__tar.gz → 0.4.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. {mapfolding-0.4.2 → mapfolding-0.4.3}/PKG-INFO +1 -1
  2. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/__init__.py +2 -1
  3. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/basecamp.py +1 -1
  4. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/beDRY.py +61 -39
  5. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/oeis.py +18 -17
  6. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/someAssemblyRequired/synthesizeNumba.py +40 -48
  7. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/someAssemblyRequired/synthesizeNumbaGeneralized.py +83 -12
  8. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +12 -23
  9. mapfolding-0.4.3/mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +129 -0
  10. mapfolding-0.4.3/mapFolding/syntheticModules/numbaCount.py +158 -0
  11. mapfolding-0.4.3/mapFolding/syntheticModules/numba_doTheNeedful.py +13 -0
  12. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/theDao.py +75 -75
  13. mapfolding-0.4.3/mapFolding/theSSOT.py +140 -0
  14. mapfolding-0.4.2/mapFolding/theSSOT.py → mapfolding-0.4.3/mapFolding/theSSOTdatatypes.py +6 -117
  15. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding.egg-info/PKG-INFO +1 -1
  16. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding.egg-info/SOURCES.txt +2 -4
  17. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding.egg-info/top_level.txt +1 -0
  18. {mapfolding-0.4.2 → mapfolding-0.4.3}/pyproject.toml +8 -1
  19. {mapfolding-0.4.2 → mapfolding-0.4.3}/tests/conftest.py +30 -1
  20. mapfolding-0.4.3/tests/test_computations.py +43 -0
  21. {mapfolding-0.4.2 → mapfolding-0.4.3}/tests/test_oeis.py +7 -10
  22. mapfolding-0.4.2/mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +0 -77
  23. mapfolding-0.4.2/mapFolding/syntheticModules/numba_countInitialize.py +0 -52
  24. mapfolding-0.4.2/mapFolding/syntheticModules/numba_countParallel.py +0 -65
  25. mapfolding-0.4.2/mapFolding/syntheticModules/numba_countSequential.py +0 -67
  26. mapfolding-0.4.2/mapFolding/syntheticModules/numba_doTheNeedful.py +0 -20
  27. mapfolding-0.4.2/mapFolding/theSSOTnumba.py +0 -125
  28. mapfolding-0.4.2/tests/test_computations.py +0 -79
  29. {mapfolding-0.4.2 → mapfolding-0.4.3}/LICENSE +0 -0
  30. {mapfolding-0.4.2 → mapfolding-0.4.3}/README.md +0 -0
  31. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/reference/flattened.py +0 -0
  32. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/reference/hunterNumba.py +0 -0
  33. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/reference/irvineJavaPort.py +0 -0
  34. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/reference/jax.py +0 -0
  35. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/reference/lunnan.py +0 -0
  36. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/reference/lunnanNumpy.py +0 -0
  37. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/reference/lunnanWhile.py +0 -0
  38. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/reference/rotatedEntryPoint.py +0 -0
  39. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/reference/total_countPlus1vsPlusN.py +0 -0
  40. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/someAssemblyRequired/__init__.py +0 -0
  41. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/someAssemblyRequired/getLLVMforNoReason.py +0 -0
  42. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/someAssemblyRequired/makeJob.py +0 -0
  43. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/someAssemblyRequired/synthesizeModuleJAX.py +0 -0
  44. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding/syntheticModules/__init__.py +0 -0
  45. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding.egg-info/dependency_links.txt +0 -0
  46. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding.egg-info/entry_points.txt +0 -0
  47. {mapfolding-0.4.2 → mapfolding-0.4.3}/mapFolding.egg-info/requires.txt +0 -0
  48. {mapfolding-0.4.2 → mapfolding-0.4.3}/setup.cfg +0 -0
  49. {mapfolding-0.4.2 → mapfolding-0.4.3}/tests/__init__.py +0 -0
  50. {mapfolding-0.4.2 → mapfolding-0.4.3}/tests/test_other.py +0 -0
  51. {mapfolding-0.4.2 → mapfolding-0.4.3}/tests/test_tasks.py +0 -0
  52. {mapfolding-0.4.2 → mapfolding-0.4.3}/tests/test_types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mapFolding
3
- Version: 0.4.2
3
+ Version: 0.4.3
4
4
  Summary: Count distinct ways to fold a map (or a strip of stamps)
5
5
  Author-email: Hunter Hogan <HunterHogan@pm.me>
6
6
  License: CC-BY-NC-4.0
@@ -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
- formatModuleNameDEFAULT,
25
+ formatFilenameModuleDEFAULT,
25
26
  getAlgorithmDispatcher,
26
27
  getAlgorithmSource,
27
28
  getPathJobRootDEFAULT,
@@ -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
 
@@ -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
- """Make a standardized filename for the computed value `foldsTotal`.
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 across platforms
34
+ - safe filesystem characters
30
35
  - unique extension
31
- - avoiding potential problems when Python is manipulating the filename, including
32
- - treating the file stem as a valid Python identifier, such as
33
- - not starting with a number
34
- - not using reserved words
35
- - no dashes or other special characters
36
- - uh, I can't remember, but I found some frustrating edge limitations
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 (e.g., [3, 2] for a 3x2 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 or how to divide the computation into tasks.
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: no division of the computation into tasks; sets task divisions to 0
107
- - int: direct set the number of task divisions; cannot exceed the map's total leaves
108
- - "maximum": divides into `leavesTotal`-many `taskDivisions`
109
- - "cpu": limits the divisions to the number of available CPUs, i.e. `concurrencyLimit`
110
- concurrencyLimit:
111
- Maximum number of concurrent tasks allowed
112
- CPUlimit: for error reporting
113
- listDimensions: for error reporting
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
- taskDivisions:
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
- ValueError
122
- If computationDivisions is an unsupported type or if resulting task divisions exceed total leaves
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 to prevent duplicate counting of folds.
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 == "maximum":
143
+ if computationDivisions == 'maximum':
137
144
  taskDivisions = leavesTotal
138
- elif computationDivisions == "cpu":
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, defaults to dtypeLargeDEFAULT. Defaults to 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
- if datatype is None:
206
- datatype = hackSSOTdtype('dtypeFoldsTotal')
207
- return numpy.zeros(shape, dtype=datatype)
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 None.
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:
@@ -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, Union
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 _validateOEISid(oeisIDcandidate: str) -> str:
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 _getFilenameOEISbFile(oeisID: str) -> str:
102
- oeisID = _validateOEISid(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 _getOEISofficial(pathFilenameCache: pathlib.Path, url: str) -> None | str:
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 _getOEISidValues(oeisID: str) -> Dict[int, int]:
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 / _getFilenameOEISbFile(oeisID)
181
- url = f"https://oeis.org/{oeisID}/{_getFilenameOEISbFile(oeisID)}"
181
+ pathFilenameCache = _pathCache / getFilenameOEISbFile(oeisID)
182
+ url = f"https://oeis.org/{oeisID}/{getFilenameOEISbFile(oeisID)}"
182
183
 
183
- oeisInformation = _getOEISofficial(pathFilenameCache, url)
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 _getOEISidInformation(oeisID: str) -> Tuple[str, int]:
190
- oeisID = _validateOEISid(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 = _getOEISofficial(pathFilenameCache, url)
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 = _getOEISidValues(oeisID)
225
- descriptionSherpa, offsetSherpa = _getOEISidInformation(oeisID)
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 = _validateOEISid(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 / _getFilenameOEISbFile(oeisID) ).unlink(missing_ok=True)
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
- argData_dtypeName = arrayTarget.dtype.name
12
+ datatypeName = argData_dtype.name
13
+ dtypeAsName = f"{moduleConstructor}_{datatypeName}"
13
14
 
14
15
  allImports.addImportFromStr(moduleConstructor, constructorName)
15
- allImports.addImportFromStr(moduleConstructor, argData_dtypeName)
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=argData_dtypeName, ctx=ast.Load()))])
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 findAndReplaceArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arrayTarget: numpy.ndarray, allImports: UniversalImportTracker) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
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 stmt in FunctionDefTarget.body.copy():
45
- if isinstance(stmt, ast.Assign):
46
- if isinstance(stmt.targets[0], ast.Name) and isinstance(stmt.value, ast.Subscript):
47
- astAssignee: ast.Name = stmt.targets[0]
48
- argData_dtypeName = hackSSOTdatatype(astAssignee.id)
49
- allImports.addImportFromStr(moduleConstructor, argData_dtypeName)
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
- onlyDataRLE = autoDecodingRLE(arraySlice, addSpaces=True)
57
- astStatement = cast(ast.Expr, ast.parse(onlyDataRLE).body[0])
58
- dataAst = astStatement.value
53
+ onlyDataRLE = autoDecodingRLE(arraySlice, addSpaces=True)
54
+ astStatement = cast(ast.Expr, ast.parse(onlyDataRLE).body[0])
55
+ dataAst = astStatement.value
59
56
 
60
- arrayCall = Then.make_astCall(name=constructorName, args=[dataAst], list_astKeywords=[ast.keyword(arg='dtype', value=ast.Name(id=argData_dtypeName, ctx=ast.Load()))])
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
- assignment = ast.Assign(targets=[astAssignee], value=arrayCall)
63
- FunctionDefTarget.body.insert(0, assignment)
64
- FunctionDefTarget.body.remove(stmt)
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 stmt in FunctionDefTarget.body.copy():
70
- if isinstance(stmt, ast.Assign):
71
- if isinstance(stmt.targets[0], ast.Name) and isinstance(stmt.value, ast.Subscript):
72
- astAssignee: ast.Name = stmt.targets[0]
73
- argData_dtypeName = hackSSOTdatatype(astAssignee.id)
74
- allImports.addImportFromStr(moduleConstructor, argData_dtypeName)
75
- astSubscript: ast.Subscript = stmt.value
76
- if isinstance(astSubscript.value, ast.Name) and astSubscript.value.id == identifier and isinstance(astSubscript.slice, ast.Attribute):
77
- indexAs_astAttribute: ast.Attribute = astSubscript.slice
78
- indexAsStr = ast.unparse(indexAs_astAttribute)
79
- argDataSlice: int = arrayTarget[eval(indexAsStr)].item()
80
- astCall = ast.Call(func=ast.Name(id=argData_dtypeName, ctx=ast.Load()), args=[ast.Constant(value=argDataSlice)], keywords=[])
81
- assignment = ast.Assign(targets=[astAssignee], value=astCall)
82
- if astAssignee.id not in Z0Z_listChaff:
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(findName, replaceWithConstant).visit(FunctionDefTarget))
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 makeAstModuleForOneCallable(pythonSource: str, callableTarget: str, parametersNumba: Optional[ParametersNumba]=None, inlineCallables: Optional[bool]=False , unpackArrays: Optional[bool]=False , allImports: Optional[UniversalImportTracker]=None ) -> str:
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
- astModule = ast.Module(body=cast(List[ast.stmt], allImports.makeListAst() + [FunctionDefTarget]), type_ignores=[])
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: