mapFolding 0.4.3__py3-none-any.whl → 0.5.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 CHANGED
@@ -61,7 +61,7 @@ from mapFolding.beDRY import (
61
61
  from mapFolding.basecamp import countFolds
62
62
  from mapFolding.oeis import clearOEIScache, getOEISids, oeisIDfor_n
63
63
 
64
- __all__ = [
64
+ __all__: list[str] = [
65
65
  'clearOEIScache',
66
66
  'countFolds',
67
67
  'getOEISids',
mapFolding/basecamp.py CHANGED
@@ -1,12 +1,13 @@
1
+ from collections.abc import Sequence
1
2
  from mapFolding import computationState, getDispatcherCallable, getPathFilenameFoldsTotal, outfitCountFolds, saveFoldsTotal
2
- from typing import Optional, Sequence, Union
3
- import os
4
-
5
- def countFolds(listDimensions: Sequence[int]
6
- , pathLikeWriteFoldsTotal: Optional[Union[str, os.PathLike[str]]] = None
7
- , computationDivisions: Optional[Union[int, str]] = None
8
- , CPUlimit: Optional[Union[int, float, bool]] = None
9
- , **keywordArguments: Optional[Union[str, bool]]
3
+ from os import PathLike
4
+ from pathlib import Path
5
+
6
+ def countFolds(listDimensions: Sequence[int],
7
+ pathLikeWriteFoldsTotal: str | PathLike[str] | None = None,
8
+ computationDivisions: int | str | None = None,
9
+ CPUlimit: int | float | bool | None = None,
10
+ **keywordArguments: str | bool
10
11
  ) -> int:
11
12
  """Count the total number of possible foldings for a given map dimensions.
12
13
 
@@ -40,16 +41,13 @@ def countFolds(listDimensions: Sequence[int]
40
41
  """
41
42
  stateUniversal: computationState = outfitCountFolds(listDimensions, computationDivisions=computationDivisions, CPUlimit=CPUlimit, **keywordArguments)
42
43
 
43
- pathFilenameFoldsTotal = None
44
- if pathLikeWriteFoldsTotal is not None:
45
- pathFilenameFoldsTotal = getPathFilenameFoldsTotal(stateUniversal['mapShape'], pathLikeWriteFoldsTotal)
46
-
47
44
  dispatcher = getDispatcherCallable()
48
45
  dispatcher(**stateUniversal)
49
46
 
50
47
  foldsTotal = int(stateUniversal['foldGroups'][0:-1].sum() * stateUniversal['foldGroups'][-1])
51
48
 
52
- if pathFilenameFoldsTotal is not None:
49
+ if pathLikeWriteFoldsTotal is not None:
50
+ pathFilenameFoldsTotal: Path = getPathFilenameFoldsTotal(stateUniversal['mapShape'], pathLikeWriteFoldsTotal)
53
51
  saveFoldsTotal(pathFilenameFoldsTotal, foldsTotal)
54
52
 
55
53
  return foldsTotal
mapFolding/beDRY.py CHANGED
@@ -12,17 +12,18 @@ from mapFolding import (
12
12
  setDatatypeLeavesTotal,
13
13
  setDatatypeModule,
14
14
  )
15
+ from collections.abc import Sequence
16
+ from numba import get_num_threads, set_num_threads
15
17
  from numpy import dtype, integer, ndarray
16
18
  from numpy.typing import DTypeLike, NDArray
17
- from typing import Any, List, Optional, Sequence, Tuple, Union
19
+ from pathlib import Path
20
+ from sys import maxsize as sysMaxsize
21
+ from typing import Any
18
22
  from Z0Z_tools import defineConcurrencyLimit, intInnit, oopsieKwargsie
19
- import numba
20
23
  import numpy
21
24
  import os
22
- import pathlib
23
- import sys
24
25
 
25
- def getFilenameFoldsTotal(mapShape: Union[Sequence[int], ndarray[Tuple[int], dtype[integer[Any]]]]) -> str:
26
+ def getFilenameFoldsTotal(mapShape: Sequence[int] | ndarray[tuple[int], dtype[integer[Any]]]) -> str:
26
27
  """Imagine your computer has been counting folds for 70 hours, and when it tries to save your newly discovered value,
27
28
  the filename is invalid. I bet you think this function is more important after that thought experiment.
28
29
 
@@ -66,13 +67,13 @@ def getLeavesTotal(listDimensions: Sequence[int]) -> int:
66
67
  else:
67
68
  productDimensions = 1
68
69
  for dimension in listPositive:
69
- if dimension > sys.maxsize // productDimensions:
70
+ if dimension > sysMaxsize // productDimensions:
70
71
  raise OverflowError(f"I received {dimension=} in {listDimensions=}, but the product of the dimensions exceeds the maximum size of an integer on this system.")
71
72
  productDimensions *= dimension
72
73
 
73
74
  return productDimensions
74
75
 
75
- def getPathFilenameFoldsTotal(mapShape: Union[Sequence[int], ndarray[Tuple[int], dtype[integer[Any]]]], pathLikeWriteFoldsTotal: Optional[Union[str, os.PathLike[str]]] = None) -> pathlib.Path:
76
+ def getPathFilenameFoldsTotal(mapShape: Sequence[int] | ndarray[tuple[int], dtype[integer[Any]]], pathLikeWriteFoldsTotal: str | os.PathLike[str] | None = None) -> Path:
76
77
  """Get a standardized path and filename for the computed value `foldsTotal`.
77
78
 
78
79
  If you provide a directory, the function will append a standardized filename. If you provide a filename
@@ -86,7 +87,7 @@ def getPathFilenameFoldsTotal(mapShape: Union[Sequence[int], ndarray[Tuple[int],
86
87
  Returns:
87
88
  pathFilenameFoldsTotal: Absolute path and filename.
88
89
  """
89
- pathLikeSherpa = pathlib.Path(pathLikeWriteFoldsTotal) if pathLikeWriteFoldsTotal is not None else None
90
+ pathLikeSherpa = Path(pathLikeWriteFoldsTotal) if pathLikeWriteFoldsTotal is not None else None
90
91
  if not pathLikeSherpa:
91
92
  pathLikeSherpa = getPathJobRootDEFAULT()
92
93
  if pathLikeSherpa.is_dir():
@@ -99,7 +100,7 @@ def getPathFilenameFoldsTotal(mapShape: Union[Sequence[int], ndarray[Tuple[int],
99
100
  pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
100
101
  return pathFilenameFoldsTotal
101
102
 
102
- def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrencyLimit: int, CPUlimit: Optional[Union[bool, float, int]], listDimensions: Sequence[int]) -> int:
103
+ def getTaskDivisions(computationDivisions: int | str | None, concurrencyLimit: int, CPUlimit: bool | float | int | None, listDimensions: Sequence[int]) -> int:
103
104
  """
104
105
  Determines whether to divide the computation into tasks and how many divisions.
105
106
 
@@ -152,7 +153,7 @@ def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrenc
152
153
 
153
154
  return taskDivisions
154
155
 
155
- def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optional[str]) -> ndarray[Tuple[int, int, int], dtype[integer[Any]]]:
156
+ def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: str | None) -> ndarray[tuple[int, int, int], dtype[integer[Any]]]:
156
157
  """
157
158
  Constructs a multi-dimensional connection graph representing the connections between the leaves of a map with the given dimensions.
158
159
  Also called a Cartesian product decomposition or dimensional product mapping.
@@ -179,7 +180,7 @@ def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optio
179
180
  for leaf1ndex in range(1, leavesTotal + 1):
180
181
  coordinateSystem[indexDimension, leaf1ndex] = ( ((leaf1ndex - 1) // cumulativeProduct[indexDimension]) % arrayDimensions[indexDimension] + 1 )
181
182
 
182
- connectionGraph = numpy.zeros((dimensionsTotal, leavesTotal + 1, leavesTotal + 1), dtype=dtype)
183
+ connectionGraph: ndarray[tuple[int, int, int], numpy.dtype[integer[Any]]] = numpy.zeros((dimensionsTotal, leavesTotal + 1, leavesTotal + 1), dtype=dtype)
183
184
  for indexDimension in range(dimensionsTotal):
184
185
  for activeLeaf1ndex in range(1, leavesTotal + 1):
185
186
  for connectee1ndex in range(1, activeLeaf1ndex + 1):
@@ -197,7 +198,7 @@ def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optio
197
198
 
198
199
  return connectionGraph
199
200
 
200
- def makeDataContainer(shape: Union[int, Tuple[int, ...]], datatype: Optional[DTypeLike] = None) -> NDArray[integer[Any]]:
201
+ def makeDataContainer(shape: int | tuple[int, ...], datatype: DTypeLike | None = None) -> NDArray[integer[Any]]:
201
202
  """Create a zeroed-out `ndarray` with the given shape and datatype.
202
203
 
203
204
  Parameters:
@@ -219,7 +220,7 @@ def makeDataContainer(shape: Union[int, Tuple[int, ...]], datatype: Optional[DTy
219
220
  else:
220
221
  raise NotImplementedError("Somebody done broke it.")
221
222
 
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:
223
+ def outfitCountFolds(listDimensions: Sequence[int], computationDivisions: int | str | None = None, CPUlimit: bool | float | int | None = None, **keywordArguments: str | bool | None) -> computationState:
223
224
  """
224
225
  Initializes and configures the computation state for map folding computations.
225
226
 
@@ -284,7 +285,7 @@ def outfitCountFolds(listDimensions: Sequence[int], computationDivisions: Option
284
285
 
285
286
  return stateInitialized
286
287
 
287
- def parseDimensions(dimensions: Sequence[int], parameterName: str = 'listDimensions') -> List[int]:
288
+ def parseDimensions(dimensions: Sequence[int], parameterName: str = 'listDimensions') -> list[int]:
288
289
  """
289
290
  Parse and validate the dimensions are non-negative integers.
290
291
 
@@ -306,7 +307,7 @@ def parseDimensions(dimensions: Sequence[int], parameterName: str = 'listDimensi
306
307
 
307
308
  return listNonNegative
308
309
 
309
- def saveFoldsTotal(pathFilename: Union[str, os.PathLike[str]], foldsTotal: int) -> None:
310
+ def saveFoldsTotal(pathFilename: str | os.PathLike[str], foldsTotal: int) -> None:
310
311
  """
311
312
  Save foldsTotal with multiple fallback mechanisms.
312
313
 
@@ -315,7 +316,7 @@ def saveFoldsTotal(pathFilename: Union[str, os.PathLike[str]], foldsTotal: int)
315
316
  foldsTotal: Critical computed value to save
316
317
  """
317
318
  try:
318
- pathFilenameFoldsTotal = pathlib.Path(pathFilename)
319
+ pathFilenameFoldsTotal = Path(pathFilename)
319
320
  pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
320
321
  pathFilenameFoldsTotal.write_text(str(foldsTotal))
321
322
  except Exception as ERRORmessage:
@@ -333,7 +334,7 @@ def saveFoldsTotal(pathFilename: Union[str, os.PathLike[str]], foldsTotal: int)
333
334
  except Exception:
334
335
  print(foldsTotal)
335
336
 
336
- def setCPUlimit(CPUlimit: Optional[Any]) -> int:
337
+ def setCPUlimit(CPUlimit: Any | None) -> int:
337
338
  """Sets CPU limit for Numba concurrent operations. Note that it can only affect Numba-jitted functions that have not yet been imported.
338
339
 
339
340
  Parameters:
@@ -355,12 +356,12 @@ def setCPUlimit(CPUlimit: Optional[Any]) -> int:
355
356
  CPUlimit = oopsieKwargsie(CPUlimit)
356
357
 
357
358
  concurrencyLimit = int(defineConcurrencyLimit(CPUlimit))
358
- numba.set_num_threads(concurrencyLimit)
359
- concurrencyLimit = numba.get_num_threads()
359
+ set_num_threads(concurrencyLimit)
360
+ concurrencyLimit = get_num_threads()
360
361
 
361
362
  return concurrencyLimit
362
363
 
363
- def validateListDimensions(listDimensions: Sequence[int]) -> List[int]:
364
+ def validateListDimensions(listDimensions: Sequence[int]) -> list[int]:
364
365
  """
365
366
  Validates and sorts a sequence of at least two positive dimensions.
366
367
 
mapFolding/oeis.py CHANGED
@@ -1,8 +1,8 @@
1
- """Everything implementing the The Online Encyclopedia of Integer Sequences (OEIS);
2
- _only_ things that implement _only_ the OEIS."""
1
+ """Everything implementing the The Online Encyclopedia of Integer Sequences (OEIS); _only_ things that implement _only_ the OEIS."""
2
+ from collections.abc import Callable
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, Final, TYPE_CHECKING
6
6
  import argparse
7
7
  import pathlib
8
8
  import random
@@ -17,6 +17,8 @@ if TYPE_CHECKING:
17
17
  else:
18
18
  TypedDict = dict
19
19
 
20
+ cacheDays = 7
21
+
20
22
  """
21
23
  Section: make `settingsOEIS`"""
22
24
 
@@ -24,15 +26,15 @@ _pathCache = getPathPackage() / ".cache"
24
26
 
25
27
  class SettingsOEIS(TypedDict):
26
28
  description: str
27
- getMapShape: Callable[[int], List[int]]
29
+ getMapShape: Callable[[int], list[int]]
28
30
  offset: int
29
- valuesBenchmark: List[int]
30
- valuesKnown: Dict[int, int]
31
- valuesTestParallelization: List[int]
32
- valuesTestValidation: List[int]
31
+ valuesBenchmark: list[int]
32
+ valuesKnown: dict[int, int]
33
+ valuesTestParallelization: list[int]
34
+ valuesTestValidation: list[int]
33
35
  valueUnknown: int
34
36
 
35
- settingsOEIShardcodedValues: Dict[str, Dict[str, Any]] = {
37
+ settingsOEIShardcodedValues: dict[str, dict[str, Any]] = {
36
38
  'A001415': {
37
39
  'getMapShape': lambda n: sorted([2, n]),
38
40
  'valuesBenchmark': [14],
@@ -65,7 +67,7 @@ settingsOEIShardcodedValues: Dict[str, Dict[str, Any]] = {
65
67
  },
66
68
  }
67
69
 
68
- oeisIDsImplemented: Final[List[str]] = sorted([oeisID.upper().strip() for oeisID in settingsOEIShardcodedValues.keys()])
70
+ oeisIDsImplemented: Final[list[str]] = sorted([oeisID.upper().strip() for oeisID in settingsOEIShardcodedValues.keys()])
69
71
  """Directly implemented OEIS IDs; standardized, e.g., 'A001415'."""
70
72
 
71
73
  def validateOEISid(oeisIDcandidate: str) -> str:
@@ -102,7 +104,7 @@ def getFilenameOEISbFile(oeisID: str) -> str:
102
104
  oeisID = validateOEISid(oeisID)
103
105
  return f"b{oeisID[1:]}.txt"
104
106
 
105
- def _parseBFileOEIS(OEISbFile: str, oeisID: str) -> Dict[int, int]:
107
+ def _parseBFileOEIS(OEISbFile: str, oeisID: str) -> dict[int, int]:
106
108
  """
107
109
  Parses the content of an OEIS b-file for a given sequence ID.
108
110
 
@@ -135,7 +137,6 @@ def _parseBFileOEIS(OEISbFile: str, oeisID: str) -> Dict[int, int]:
135
137
  return OEISsequence
136
138
 
137
139
  def getOEISofficial(pathFilenameCache: pathlib.Path, url: str) -> None | str:
138
- cacheDays = 7
139
140
  tryCache = False
140
141
  if pathFilenameCache.exists():
141
142
  fileAge = datetime.now() - datetime.fromtimestamp(pathFilenameCache.stat().st_mtime)
@@ -145,7 +146,7 @@ def getOEISofficial(pathFilenameCache: pathlib.Path, url: str) -> None | str:
145
146
  if tryCache:
146
147
  try:
147
148
  oeisInformation = pathFilenameCache.read_text()
148
- except IOError:
149
+ except OSError:
149
150
  tryCache = False
150
151
 
151
152
  if not tryCache:
@@ -159,7 +160,7 @@ def getOEISofficial(pathFilenameCache: pathlib.Path, url: str) -> None | str:
159
160
 
160
161
  return oeisInformation
161
162
 
162
- def getOEISidValues(oeisID: str) -> Dict[int, int]:
163
+ def getOEISidValues(oeisID: str) -> dict[int, int]:
163
164
  """
164
165
  Retrieves the specified OEIS sequence as a dictionary mapping integer indices
165
166
  to their corresponding values.
@@ -187,7 +188,7 @@ def getOEISidValues(oeisID: str) -> Dict[int, int]:
187
188
  return _parseBFileOEIS(oeisInformation, oeisID)
188
189
  return {-1: -1}
189
190
 
190
- def getOEISidInformation(oeisID: str) -> Tuple[str, int]:
191
+ def getOEISidInformation(oeisID: str) -> tuple[str, int]:
191
192
  oeisID = validateOEISid(oeisID)
192
193
  pathFilenameCache = _pathCache / f"{oeisID}.txt"
193
194
  url = f"https://oeis.org/search?q=id:{oeisID}&fmt=text"
@@ -219,7 +220,7 @@ def getOEISidInformation(oeisID: str) -> Tuple[str, int]:
219
220
  description = ' '.join(description_parts)
220
221
  return description, offset
221
222
 
222
- def makeSettingsOEIS() -> Dict[str, SettingsOEIS]:
223
+ def makeSettingsOEIS() -> dict[str, SettingsOEIS]:
223
224
  settingsTarget = {}
224
225
  for oeisID in oeisIDsImplemented:
225
226
  valuesKnownSherpa = getOEISidValues(oeisID)
@@ -236,7 +237,7 @@ def makeSettingsOEIS() -> Dict[str, SettingsOEIS]:
236
237
  )
237
238
  return settingsTarget
238
239
 
239
- settingsOEIS: Dict[str, SettingsOEIS] = makeSettingsOEIS()
240
+ settingsOEIS: dict[str, SettingsOEIS] = makeSettingsOEIS()
240
241
  """All values and settings for `oeisIDsImplemented`."""
241
242
 
242
243
  """
@@ -288,7 +289,7 @@ def oeisIDfor_n(oeisID: str, n: int) -> int:
288
289
  if not isinstance(n, int) or n < 0:
289
290
  raise ValueError("`n` must be non-negative integer.")
290
291
 
291
- listDimensions = settingsOEIS[oeisID]['getMapShape'](n)
292
+ listDimensions: list[int] = settingsOEIS[oeisID]['getMapShape'](n)
292
293
 
293
294
  if n <= 1 or len(listDimensions) < 2:
294
295
  offset = settingsOEIS[oeisID]['offset']
@@ -309,9 +310,9 @@ def OEIS_for_n() -> None:
309
310
  parserCLI.add_argument('oeisID', help="OEIS sequence identifier")
310
311
  parserCLI.add_argument('n', type=int, help="Calculate a(n) for this n")
311
312
 
312
- argumentsCLI = parserCLI.parse_args()
313
+ argumentsCLI: argparse.Namespace = parserCLI.parse_args()
313
314
 
314
- timeStart = time.perf_counter()
315
+ timeStart: float = time.perf_counter()
315
316
 
316
317
  try:
317
318
  print(oeisIDfor_n(argumentsCLI.oeisID, argumentsCLI.n), "distinct folding patterns.")
@@ -1,14 +1,15 @@
1
+ from collections.abc import Sequence
1
2
  from mapFolding import getPathFilenameFoldsTotal, computationState, outfitCountFolds, getAlgorithmSource
3
+ from pathlib import Path
2
4
  from types import ModuleType
3
- from typing import Any, Literal, Optional, Sequence, overload
4
- import pathlib
5
+ from typing import Any, Literal, overload
5
6
  import pickle
6
7
 
7
8
  @overload
8
- def makeStateJob(listDimensions: Sequence[int], *, writeJob: Literal[True] , **keywordArguments: Optional[str]) -> pathlib.Path: ...
9
+ def makeStateJob(listDimensions: Sequence[int], *, writeJob: Literal[True] , **keywordArguments: str | None) -> Path: ...
9
10
  @overload
10
- def makeStateJob(listDimensions: Sequence[int], *, writeJob: Literal[False] , **keywordArguments: Optional[str]) -> computationState: ...
11
- def makeStateJob(listDimensions: Sequence[int], *, writeJob: bool = True, **keywordArguments: Optional[Any]) -> computationState | pathlib.Path:
11
+ def makeStateJob(listDimensions: Sequence[int], *, writeJob: Literal[False] , **keywordArguments: str | None) -> computationState: ...
12
+ def makeStateJob(listDimensions: Sequence[int], *, writeJob: bool = True, **keywordArguments: Any | None) -> computationState | Path:
12
13
  """
13
14
  Creates a computation state job for map folding calculations and optionally saves it to disk.
14
15
 
@@ -27,7 +28,7 @@ def makeStateJob(listDimensions: Sequence[int], *, writeJob: bool = True, **keyw
27
28
 
28
29
  Returns
29
30
  -------
30
- Union[computationState, pathlib.Path]
31
+ Union[computationState, Path]
31
32
  If writeJob is False, returns the computation state object.
32
33
  If writeJob is True, returns the Path object pointing to the saved state file.
33
34
 
@@ -47,7 +48,7 @@ def makeStateJob(listDimensions: Sequence[int], *, writeJob: bool = True, **keyw
47
48
 
48
49
  pathFilenameChopChop = getPathFilenameFoldsTotal(stateUniversal['mapShape'])
49
50
  suffix = pathFilenameChopChop.suffix
50
- pathJob = pathlib.Path(str(pathFilenameChopChop)[0:-len(suffix)])
51
+ pathJob = Path(str(pathFilenameChopChop)[0:-len(suffix)])
51
52
  pathJob.mkdir(parents=True, exist_ok=True)
52
53
  pathFilenameJob = pathJob / 'stateJob.pkl'
53
54
 
@@ -1,15 +1,13 @@
1
1
  from mapFolding import getAlgorithmSource, getPathSyntheticModules
2
2
  from mapFolding import setDatatypeModule, setDatatypeFoldsTotal, setDatatypeElephino, setDatatypeLeavesTotal
3
- from typing import Optional
4
3
  import ast
5
4
  import inspect
6
5
  import pathlib
7
- import sys
8
6
 
9
7
  def transformPythonToJAX(codePython: str) -> None:
10
8
  astPython = ast.parse(codePython)
11
9
 
12
- def writeJax(*, codeSource: Optional[str] = None, pathFilenameAlgorithm: Optional[pathlib.Path] = None, pathFilenameDestination: Optional[pathlib.Path] = None) -> None:
10
+ def writeJax(*, codeSource: str | None = None, pathFilenameAlgorithm: pathlib.Path | None = None, pathFilenameDestination: pathlib.Path | None = None) -> None:
13
11
  if codeSource is None and pathFilenameAlgorithm is None:
14
12
  algorithmSource = getAlgorithmSource()
15
13
  codeSource = inspect.getsource(algorithmSource)
@@ -2,7 +2,7 @@
2
2
  TODO: consolidate the logic in this module."""
3
3
  from mapFolding.someAssemblyRequired.synthesizeNumbaGeneralized import *
4
4
 
5
- def insertArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arrayTarget: numpy.ndarray, allImports: UniversalImportTracker, unrollSlices: Optional[int]=None) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
5
+ def insertArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arrayTarget: numpy.ndarray, allImports: UniversalImportTracker, unrollSlices: int | None = None) -> tuple[ast.FunctionDef, UniversalImportTracker]:
6
6
  arrayType = type(arrayTarget)
7
7
  moduleConstructor = arrayType.__module__
8
8
  constructorName = arrayType.__name__
@@ -34,7 +34,7 @@ def insertArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arra
34
34
 
35
35
  return FunctionDefTarget, allImports
36
36
 
37
- def findAndReplaceTrackArrayIn_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
38
 
39
39
  arrayType = type(arrayTarget)
40
40
  moduleConstructor = arrayType.__module__
@@ -62,7 +62,7 @@ def findAndReplaceTrackArrayIn_body(FunctionDefTarget: ast.FunctionDef, identifi
62
62
  allImports.addImportFromStr(moduleConstructor, datatypeName, dtypeAsName)
63
63
  return FunctionDefTarget, allImports
64
64
 
65
- def findAndReplaceArraySubscriptIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arrayTarget: numpy.ndarray, Z0Z_listChaff: List[str], allImports: UniversalImportTracker) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
65
+ def findAndReplaceArraySubscriptIn_body(FunctionDefTarget: ast.FunctionDef, identifier: str, arrayTarget: numpy.ndarray, Z0Z_listChaff: list[str], allImports: UniversalImportTracker) -> tuple[ast.FunctionDef, UniversalImportTracker]:
66
66
  moduleConstructor = Z0Z_getDatatypeModuleScalar()
67
67
  for statement in FunctionDefTarget.body.copy():
68
68
  if ifThis.isUnpackingAnArray(identifier)(statement):
@@ -87,7 +87,7 @@ def removeAssignTargetFrom_body(FunctionDefTarget: ast.FunctionDef, identifier:
87
87
  return False
88
88
  targetNode = astNode.targets[0]
89
89
  return (isinstance(targetNode, ast.Subscript) and isinstance(targetNode.value, ast.Name) and targetNode.value.id == identifier) or ifThis.nameIs(identifier)(targetNode)
90
- def replacementBuilder(astNode: ast.AST) -> Optional[ast.stmt]:
90
+ def replacementBuilder(astNode: ast.AST) -> ast.stmt | None:
91
91
  # Returning None removes the node.
92
92
  return None
93
93
  FunctionDefSherpa = NodeReplacer(predicate, replacementBuilder).visit(FunctionDefTarget)
@@ -98,7 +98,7 @@ def removeAssignTargetFrom_body(FunctionDefTarget: ast.FunctionDef, identifier:
98
98
  ast.fix_missing_locations(FunctionDefTarget)
99
99
  return FunctionDefTarget
100
100
 
101
- def findAndReplaceAnnAssignIn_body(FunctionDefTarget: ast.FunctionDef, allImports: UniversalImportTracker) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
101
+ def findAndReplaceAnnAssignIn_body(FunctionDefTarget: ast.FunctionDef, allImports: UniversalImportTracker) -> tuple[ast.FunctionDef, UniversalImportTracker]:
102
102
  moduleConstructor = Z0Z_getDatatypeModuleScalar()
103
103
  for stmt in FunctionDefTarget.body.copy():
104
104
  if isinstance(stmt, ast.AnnAssign):
@@ -137,7 +137,7 @@ def findAstNameReplaceWithConstantIn_body(FunctionDefTarget: ast.FunctionDef, na
137
137
 
138
138
  return cast(ast.FunctionDef, NodeReplacer(ifThis.nameIs(name), replaceWithConstant).visit(FunctionDefTarget))
139
139
 
140
- def insertReturnStatementIn_body(FunctionDefTarget: ast.FunctionDef, arrayTarget: numpy.ndarray, allImports: UniversalImportTracker) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
140
+ def insertReturnStatementIn_body(FunctionDefTarget: ast.FunctionDef, arrayTarget: numpy.ndarray, allImports: UniversalImportTracker) -> tuple[ast.FunctionDef, UniversalImportTracker]:
141
141
  """Add multiplication and return statement to function, properly constructing AST nodes."""
142
142
  # Create AST for multiplication operation
143
143
  multiplicand = Z0Z_identifierCountFolds
@@ -157,7 +157,7 @@ def insertReturnStatementIn_body(FunctionDefTarget: ast.FunctionDef, arrayTarget
157
157
 
158
158
  return FunctionDefTarget, allImports
159
159
 
160
- def findAndReplaceWhileLoopIn_body(FunctionDefTarget: ast.FunctionDef, iteratorName: str, iterationsTotal: int) -> Any:
160
+ def findAndReplaceWhileLoopIn_body(FunctionDefTarget: ast.FunctionDef, iteratorName: str, iterationsTotal: int) -> ast.FunctionDef:
161
161
  """
162
162
  Unroll all nested while loops matching the condition that their test uses `iteratorName`.
163
163
  """
@@ -180,11 +180,11 @@ def findAndReplaceWhileLoopIn_body(FunctionDefTarget: ast.FunctionDef, iteratorN
180
180
  self.iteratorName = iteratorName
181
181
  self.iterationsTotal = iterationsTotal
182
182
 
183
- def visit_While(self, node: ast.While) -> List[ast.stmt]:
183
+ def visit_While(self, node: ast.While) -> list[ast.stmt]:
184
184
  # Check if the while loop's test uses the iterator.
185
185
  if isinstance(node.test, ast.Compare) and ifThis.nameIs(self.iteratorName)(node.test.left):
186
186
  # Recurse the while loop body and remove AugAssign that increments the iterator.
187
- cleanBodyStatements: List[ast.stmt] = []
187
+ cleanBodyStatements: list[ast.stmt] = []
188
188
  for loopStatement in node.body:
189
189
  # Recursively visit nested statements.
190
190
  visitedStatement = self.visit(loopStatement)
@@ -198,7 +198,7 @@ def findAndReplaceWhileLoopIn_body(FunctionDefTarget: ast.FunctionDef, iteratorN
198
198
  continue
199
199
  cleanBodyStatements.append(visitedStatement)
200
200
 
201
- newStatements: List[ast.stmt] = []
201
+ newStatements: list[ast.stmt] = []
202
202
  # Unroll using the filtered body.
203
203
  for iterationIndex in range(self.iterationsTotal):
204
204
  for loopStatement in cleanBodyStatements:
@@ -222,7 +222,7 @@ def findAndReplaceWhileLoopIn_body(FunctionDefTarget: ast.FunctionDef, iteratorN
222
222
  ast.fix_missing_locations(newFunctionDef)
223
223
  return newFunctionDef
224
224
 
225
- def makeLauncherBasicJobNumba(callableTarget: str, pathFilenameFoldsTotal: pathlib.Path) -> ast.Module:
225
+ def makeLauncherBasicJobNumba(callableTarget: str, pathFilenameFoldsTotal: Path) -> ast.Module:
226
226
  linesLaunch = f"""
227
227
  if __name__ == '__main__':
228
228
  import time
@@ -235,7 +235,12 @@ if __name__ == '__main__':
235
235
  """
236
236
  return ast.parse(linesLaunch)
237
237
 
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]:
238
+ def makeFunctionDef(astModule: ast.Module,
239
+ callableTarget: str,
240
+ parametersNumba: ParametersNumba | None = None,
241
+ inlineCallables: bool | None = False,
242
+ unpackArrays: bool | None = False,
243
+ allImports: UniversalImportTracker | None = None) -> tuple[ast.FunctionDef, UniversalImportTracker]:
239
244
  if allImports is None:
240
245
  allImports = UniversalImportTracker()
241
246
  for statement in astModule.body:
@@ -266,7 +271,7 @@ def makeFunctionDef(astModule: ast.Module, callableTarget: str, parametersNumba:
266
271
 
267
272
  return FunctionDefTarget, allImports
268
273
 
269
- def decorateCallableWithNumba(FunctionDefTarget: ast.FunctionDef, allImports: UniversalImportTracker, parametersNumba: Optional[ParametersNumba]=None) -> Tuple[ast.FunctionDef, UniversalImportTracker]:
274
+ def decorateCallableWithNumba(FunctionDefTarget: ast.FunctionDef, allImports: UniversalImportTracker, parametersNumba: ParametersNumba | None = None) -> tuple[ast.FunctionDef, UniversalImportTracker]:
270
275
  def Z0Z_UnhandledDecorators(astCallable: ast.FunctionDef) -> ast.FunctionDef:
271
276
  # TODO: more explicit handling of decorators. I'm able to ignore this because I know `algorithmSource` doesn't have any decorators.
272
277
  for decoratorItem in astCallable.decorator_list.copy():
@@ -303,7 +308,7 @@ def decorateCallableWithNumba(FunctionDefTarget: ast.FunctionDef, allImports: Un
303
308
  datatypeModuleDecorator = Z0Z_getDatatypeModuleScalar()
304
309
  list_argsDecorator: Sequence[ast.expr] = []
305
310
 
306
- list_arg4signature_or_function: List[ast.expr] = []
311
+ list_arg4signature_or_function: list[ast.expr] = []
307
312
  for parameter in FunctionDefTarget.args.args:
308
313
  signatureElement = make_arg4parameter(parameter)
309
314
  if signatureElement:
@@ -35,7 +35,8 @@ from mapFolding.someAssemblyRequired.makeJob import makeStateJob
35
35
  from numpy import integer
36
36
  from numpy.typing import NDArray
37
37
  from types import ModuleType
38
- from typing import Any, Callable, cast, Dict, List, Optional, Sequence, Set, Tuple, Type, Union
38
+ from collections.abc import Callable, Sequence
39
+ from typing import Any, cast
39
40
  from Z0Z_tools import autoDecodingRLE, updateExtendPolishDictionaryLists
40
41
  import ast
41
42
  import autoflake
@@ -46,8 +47,8 @@ import inspect
46
47
  import more_itertools
47
48
  import numba
48
49
  import numpy
49
- import os
50
- import pathlib
50
+ from os import PathLike
51
+ from pathlib import Path
51
52
  import python_minifier
52
53
  import warnings
53
54
 
@@ -63,8 +64,8 @@ class ASTBodyTransformer:
63
64
  def __init__(self, functionDefinition: ast.FunctionDef) -> None:
64
65
  self.functionDefinition = functionDefinition
65
66
 
66
- def replaceIn_body(self, predicate: Callable[[ast.stmt], bool], replacementBuilder: Callable[[ast.stmt], Optional[ast.stmt]]) -> None:
67
- newBody: List[ast.stmt] = []
67
+ def replaceIn_body(self, predicate: Callable[[ast.stmt], bool], replacementBuilder: Callable[[ast.stmt], ast.stmt | None]) -> None:
68
+ newBody: list[ast.stmt] = []
68
69
  for statement in self.functionDefinition.body:
69
70
  if predicate(statement):
70
71
  replacementStatement = replacementBuilder(statement)
@@ -127,16 +128,16 @@ class ifThis:
127
128
  class Then:
128
129
  """Generic actions."""
129
130
  @staticmethod
130
- def copy_astCallKeywords(astCall: ast.Call) -> Dict[str, Any]:
131
+ def copy_astCallKeywords(astCall: ast.Call) -> dict[str, Any]:
131
132
  """Extract keyword parameters from a decorator AST node."""
132
- dictionaryKeywords: Dict[str, Any] = {}
133
+ dictionaryKeywords: dict[str, Any] = {}
133
134
  for keywordItem in astCall.keywords:
134
135
  if isinstance(keywordItem.value, ast.Constant) and keywordItem.arg is not None:
135
136
  dictionaryKeywords[keywordItem.arg] = keywordItem.value.value
136
137
  return dictionaryKeywords
137
138
 
138
139
  @staticmethod
139
- def make_astCall(name: str, args: Optional[Sequence[ast.expr]]=None, list_astKeywords: Optional[Sequence[ast.keyword]]=None, dictionaryKeywords: Optional[Dict[str, Any]]=None) -> ast.Call:
140
+ def make_astCall(name: str, args: Sequence[ast.expr] | None = None, list_astKeywords: Sequence[ast.keyword] | None = None, dictionaryKeywords: dict[str, Any] | None = None) -> ast.Call:
140
141
  list_dictionaryKeywords = [ast.keyword(arg=keyName, value=ast.Constant(value=keyValue)) for keyName, keyValue in dictionaryKeywords.items()] if dictionaryKeywords else []
141
142
  return ast.Call(
142
143
  func=ast.Name(id=name, ctx=ast.Load()),
@@ -159,7 +160,7 @@ class NodeReplacer(ast.NodeTransformer):
159
160
  visit(node: ast.AST) -> Optional[ast.AST]:
160
161
  Visits each node in the AST, replacing or removing it based on the predicate.
161
162
  """
162
- def __init__(self, findMe: Callable[[ast.AST], bool], nodeReplacementBuilder: Callable[[ast.AST], Optional[ast.AST]]) -> None:
163
+ def __init__(self, findMe: Callable[[ast.AST], bool], nodeReplacementBuilder: Callable[[ast.AST], ast.AST | None]) -> None:
163
164
  self.findMe = findMe
164
165
  self.nodeReplacementBuilder = nodeReplacementBuilder
165
166
 
@@ -181,10 +182,10 @@ def thisIsAnyNumbaJitDecorator(Ima: ast.AST) -> bool:
181
182
  # Domain-based
182
183
  class UniversalImportTracker:
183
184
  def __init__(self) -> None:
184
- self.dictionaryImportFrom: Dict[str, Set] = collections.defaultdict(set)
185
+ self.dictionaryImportFrom: dict[str, set] = collections.defaultdict(set)
185
186
  self.setImport = set()
186
187
 
187
- def addAst(self, astImport_: Union[ast.Import, ast.ImportFrom]) -> None:
188
+ def addAst(self, astImport_: ast.Import | ast.ImportFrom) -> None:
188
189
  if isinstance(astImport_, ast.Import):
189
190
  for alias in astImport_.names:
190
191
  self.setImport.add(alias.name)
@@ -195,10 +196,10 @@ class UniversalImportTracker:
195
196
  def addImportStr(self, module: str) -> None:
196
197
  self.setImport.add(module)
197
198
 
198
- def addImportFromStr(self, module: str, name: str, asname: Optional[str] = None) -> None:
199
+ def addImportFromStr(self, module: str, name: str, asname: str | None = None) -> None:
199
200
  self.dictionaryImportFrom[module].add((name, asname))
200
201
 
201
- def makeListAst(self) -> List[Union[ast.ImportFrom, ast.Import]]:
202
+ def makeListAst(self) -> list[ast.ImportFrom | ast.Import]:
202
203
  listAstImportFrom = []
203
204
  for module, setOfNameTuples in sorted(self.dictionaryImportFrom.items()):
204
205
  listAliases = []
@@ -253,11 +254,11 @@ class RecursiveInliner(ast.NodeTransformer):
253
254
  dictionaryFunctions, its statements are expanded in place, effectively inlining
254
255
  the called function's statements into the surrounding context.
255
256
  """
256
- def __init__(self, dictionaryFunctions: Dict[str, ast.FunctionDef]):
257
+ def __init__(self, dictionaryFunctions: dict[str, ast.FunctionDef]):
257
258
  self.dictionaryFunctions = dictionaryFunctions
258
- self.callablesCompleted: Set[str] = set()
259
+ self.callablesCompleted: set[str] = set()
259
260
 
260
- def inlineFunctionBody(self, callableTargetName: str) -> Optional[ast.FunctionDef]:
261
+ def inlineFunctionBody(self, callableTargetName: str) -> ast.FunctionDef | None:
261
262
  if (callableTargetName in self.callablesCompleted):
262
263
  return None
263
264
 
@@ -280,7 +281,7 @@ class RecursiveInliner(ast.NodeTransformer):
280
281
  return ast.Constant(value=None)
281
282
  return callNodeVisited
282
283
 
283
- def visit_Expr(self, node: ast.Expr) -> Union[ast.AST, List[ast.AST]]:
284
+ def visit_Expr(self, node: ast.Expr) -> ast.AST | list[ast.AST]:
284
285
  if (isinstance(node.value, ast.Call)):
285
286
  if (isinstance(node.value.func, ast.Name) and node.value.func.id in self.dictionaryFunctions):
286
287
  inlineDefinition = self.inlineFunctionBody(node.value.func.id)
@@ -313,12 +314,12 @@ class UnpackArrays(ast.NodeTransformer):
313
314
  3. Replaces original array access with the local variable
314
315
  """
315
316
 
316
- def __init__(self, enumIndexClass: Type[EnumIndices], arrayName: str) -> None:
317
+ def __init__(self, enumIndexClass: type[EnumIndices], arrayName: str) -> None:
317
318
  self.enumIndexClass = enumIndexClass
318
319
  self.arrayName = arrayName
319
- self.substitutions: Dict[str, Any] = {}
320
+ self.substitutions: dict[str, Any] = {}
320
321
 
321
- def extract_member_name(self, node: ast.AST) -> Optional[str]:
322
+ def extract_member_name(self, node: ast.AST) -> str | None:
322
323
  """Recursively extract enum member name from any node in the AST."""
323
324
  if isinstance(node, ast.Attribute) and node.attr == 'value':
324
325
  innerAttribute = node.value
@@ -337,7 +338,7 @@ class UnpackArrays(ast.NodeTransformer):
337
338
  return ast.Name(id=member_name, ctx=node.ctx)
338
339
  elif isinstance(node, ast.Tuple):
339
340
  # Handle tuple slices by transforming each element
340
- return ast.Tuple(elts=cast(List[ast.expr], [self.transform_slice_element(elt) for elt in node.elts]), ctx=node.ctx)
341
+ return ast.Tuple(elts=cast(list[ast.expr], [self.transform_slice_element(elt) for elt in node.elts]), ctx=node.ctx)
341
342
  elif isinstance(node, ast.Attribute):
342
343
  member_name = self.extract_member_name(node)
343
344
  if member_name: