mapFolding 0.2.3__tar.gz → 0.2.5__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 (41) hide show
  1. {mapfolding-0.2.3 → mapfolding-0.2.5}/PKG-INFO +8 -6
  2. {mapfolding-0.2.3 → mapfolding-0.2.5}/README.md +6 -1
  3. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding/__init__.py +1 -1
  4. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding/babbage.py +9 -4
  5. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding/beDRY.py +47 -6
  6. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding/benchmarks/benchmarking.py +3 -2
  7. mapfolding-0.2.5/mapFolding/importSelector.py +7 -0
  8. mapfolding-0.2.5/mapFolding/lovelace.py +213 -0
  9. mapfolding-0.2.3/mapFolding/JAX/lunnanJAX.py → mapfolding-0.2.5/mapFolding/reference/jax.py +2 -0
  10. mapfolding-0.2.5/mapFolding/someAssemblyRequired/inlineAfunction.py +152 -0
  11. mapfolding-0.2.5/mapFolding/someAssemblyRequired/jobsAndTasks.py +47 -0
  12. mapfolding-0.2.5/mapFolding/someAssemblyRequired/makeNuitkaSource.py +99 -0
  13. mapfolding-0.2.5/mapFolding/someAssemblyRequired/makeNumbaJob.py +121 -0
  14. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding/startHere.py +8 -28
  15. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding/theSSOT.py +13 -5
  16. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding.egg-info/PKG-INFO +8 -6
  17. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding.egg-info/SOURCES.txt +6 -3
  18. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding.egg-info/requires.txt +0 -4
  19. {mapfolding-0.2.3 → mapfolding-0.2.5}/pyproject.toml +4 -5
  20. {mapfolding-0.2.3 → mapfolding-0.2.5}/tests/conftest.py +8 -1
  21. {mapfolding-0.2.3 → mapfolding-0.2.5}/tests/test_other.py +158 -88
  22. mapfolding-0.2.3/mapFolding/JAX/taskJAX.py +0 -313
  23. mapfolding-0.2.3/mapFolding/benchmarks/test_benchmarks.py +0 -74
  24. mapfolding-0.2.3/mapFolding/lovelace.py +0 -217
  25. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding/oeis.py +0 -0
  26. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding/reference/flattened.py +0 -0
  27. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding/reference/hunterNumba.py +0 -0
  28. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding/reference/irvineJavaPort.py +0 -0
  29. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding/reference/lunnan.py +0 -0
  30. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding/reference/lunnanNumpy.py +0 -0
  31. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding/reference/lunnanWhile.py +0 -0
  32. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding/reference/rotatedEntryPoint.py +0 -0
  33. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding/reference/total_countPlus1vsPlusN.py +0 -0
  34. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding.egg-info/dependency_links.txt +0 -0
  35. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding.egg-info/entry_points.txt +0 -0
  36. {mapfolding-0.2.3 → mapfolding-0.2.5}/mapFolding.egg-info/top_level.txt +0 -0
  37. {mapfolding-0.2.3 → mapfolding-0.2.5}/setup.cfg +0 -0
  38. {mapfolding-0.2.3 → mapfolding-0.2.5}/tests/__init__.py +0 -0
  39. {mapfolding-0.2.3 → mapfolding-0.2.5}/tests/pythons_idiotic_namespace.py +0 -0
  40. {mapfolding-0.2.3 → mapfolding-0.2.5}/tests/test_oeis.py +0 -0
  41. {mapfolding-0.2.3 → mapfolding-0.2.5}/tests/test_tasks.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mapFolding
3
- Version: 0.2.3
4
- Summary: Algorithm(s) for counting distinct ways to fold a map (or a strip of stamps)
3
+ Version: 0.2.5
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
  Project-URL: homepage, https://github.com/hunterhogan/mapFolding
7
7
  Requires-Python: <3.13,>=3.10
@@ -14,9 +14,6 @@ Requires-Dist: pandas; extra == "benchmark"
14
14
  Requires-Dist: jupyter; extra == "benchmark"
15
15
  Requires-Dist: ipywidgets; extra == "benchmark"
16
16
  Requires-Dist: tqdm; extra == "benchmark"
17
- Provides-Extra: jax
18
- Requires-Dist: jax; extra == "jax"
19
- Requires-Dist: jaxtyping; extra == "jax"
20
17
  Provides-Extra: testing
21
18
  Requires-Dist: pytest; extra == "testing"
22
19
  Requires-Dist: pytest-cov; extra == "testing"
@@ -39,7 +36,7 @@ The directory [mapFolding/reference](https://github.com/hunterhogan/mapFolding/b
39
36
  - [hunterNumba.py](https://github.com/hunterhogan/mapFolding/blob/main/mapFolding/reference), a one-size-fits-all, self-contained, reasonably fast, contemporary algorithm that is nevertheless infected by _noobaceae ignorancium_, and
40
37
  - miscellaneous notes.
41
38
 
42
- [![Python Tests](https://github.com/hunterhogan/mapFolding/actions/workflows/unittests.yml/badge.svg)](https://github.com/hunterhogan/mapFolding/actions/workflows/unittests.yml) [![pip install mapFolding](https://img.shields.io/badge/pip%20install-mapFolding-gray.svg?colorB=3b434b)](https://pypi.org/project/mapFolding/) ![Static Badge](https://img.shields.io/badge/stinkin'%20badges-don't%20need-b98e5e) ![PyPI - Downloads](https://img.shields.io/pypi/dd/mapFolding) ![Static Badge](https://img.shields.io/badge/issues-I%20have%20them-brightgreen) ![GitHub repo size](https://img.shields.io/github/repo-size/hunterhogan/mapFolding)
39
+ [![pip install mapFolding](https://img.shields.io/badge/pip%20install-mapFolding-gray.svg?colorB=3b434b)](https://pypi.org/project/mapFolding/) [![Python Tests](https://github.com/hunterhogan/mapFolding/actions/workflows/unittests.yml/badge.svg)](https://github.com/hunterhogan/mapFolding/actions/workflows/unittests.yml) [![Static Badge](https://img.shields.io/badge/stinkin'%20badges-don't%20need-b98e5e)](https://youtu.be/g6f_miE91mk&t=4) ![PyPI - Downloads](https://img.shields.io/pypi/dd/mapFolding) ![Static Badge](https://img.shields.io/badge/issues-I%20have%20them-brightgreen) ![GitHub repo size](https://img.shields.io/github/repo-size/hunterhogan/mapFolding)
43
40
 
44
41
  ## Simple, easy usage based on OEIS IDs
45
42
 
@@ -131,3 +128,8 @@ In [`foldings.txt`](https://github.com/hunterhogan/mapFolding/blob/main/mapFoldi
131
128
  ```sh
132
129
  pip install mapFolding
133
130
  ```
131
+
132
+ ## My recovery
133
+
134
+ [![Static Badge](https://img.shields.io/badge/2011_August-Homeless_since-blue?style=flat)](https://HunterThinks.com/support)
135
+ [![YouTube Channel Subscribers](https://img.shields.io/youtube/channel/subscribers/UC3Gx7kz61009NbhpRtPP7tw)](https://www.youtube.com/@HunterHogan)
@@ -14,7 +14,7 @@ The directory [mapFolding/reference](https://github.com/hunterhogan/mapFolding/b
14
14
  - [hunterNumba.py](https://github.com/hunterhogan/mapFolding/blob/main/mapFolding/reference), a one-size-fits-all, self-contained, reasonably fast, contemporary algorithm that is nevertheless infected by _noobaceae ignorancium_, and
15
15
  - miscellaneous notes.
16
16
 
17
- [![Python Tests](https://github.com/hunterhogan/mapFolding/actions/workflows/unittests.yml/badge.svg)](https://github.com/hunterhogan/mapFolding/actions/workflows/unittests.yml) [![pip install mapFolding](https://img.shields.io/badge/pip%20install-mapFolding-gray.svg?colorB=3b434b)](https://pypi.org/project/mapFolding/) ![Static Badge](https://img.shields.io/badge/stinkin'%20badges-don't%20need-b98e5e) ![PyPI - Downloads](https://img.shields.io/pypi/dd/mapFolding) ![Static Badge](https://img.shields.io/badge/issues-I%20have%20them-brightgreen) ![GitHub repo size](https://img.shields.io/github/repo-size/hunterhogan/mapFolding)
17
+ [![pip install mapFolding](https://img.shields.io/badge/pip%20install-mapFolding-gray.svg?colorB=3b434b)](https://pypi.org/project/mapFolding/) [![Python Tests](https://github.com/hunterhogan/mapFolding/actions/workflows/unittests.yml/badge.svg)](https://github.com/hunterhogan/mapFolding/actions/workflows/unittests.yml) [![Static Badge](https://img.shields.io/badge/stinkin'%20badges-don't%20need-b98e5e)](https://youtu.be/g6f_miE91mk&t=4) ![PyPI - Downloads](https://img.shields.io/pypi/dd/mapFolding) ![Static Badge](https://img.shields.io/badge/issues-I%20have%20them-brightgreen) ![GitHub repo size](https://img.shields.io/github/repo-size/hunterhogan/mapFolding)
18
18
 
19
19
  ## Simple, easy usage based on OEIS IDs
20
20
 
@@ -106,3 +106,8 @@ In [`foldings.txt`](https://github.com/hunterhogan/mapFolding/blob/main/mapFoldi
106
106
  ```sh
107
107
  pip install mapFolding
108
108
  ```
109
+
110
+ ## My recovery
111
+
112
+ [![Static Badge](https://img.shields.io/badge/2011_August-Homeless_since-blue?style=flat)](https://HunterThinks.com/support)
113
+ [![YouTube Channel Subscribers](https://img.shields.io/youtube/channel/subscribers/UC3Gx7kz61009NbhpRtPP7tw)](https://www.youtube.com/@HunterHogan)
@@ -1,6 +1,6 @@
1
1
  from .theSSOT import *
2
2
  from Z0Z_tools import defineConcurrencyLimit, intInnit, oopsieKwargsie
3
- from .beDRY import getFilenameFoldsTotal, outfitCountFolds
3
+ from .beDRY import getFilenameFoldsTotal, getPathFilenameFoldsTotal, outfitCountFolds, saveFoldsTotal
4
4
  from .startHere import countFolds
5
5
  from .oeis import oeisIDfor_n, getOEISids, clearOEIScache
6
6
 
@@ -1,4 +1,5 @@
1
- from mapFolding.lovelace import countFoldsCompiled
1
+ from mapFolding.importSelector import countSequential, countParallel, countInitialize
2
+ from mapFolding import indexThe
2
3
  from numpy import integer
3
4
  from numpy.typing import NDArray
4
5
  from typing import Any, Tuple
@@ -25,6 +26,10 @@ def _countFolds(connectionGraph: NDArray[integer[Any]], foldsSubTotals: NDArray[
25
26
  - and just a few dozen-jillion other things.
26
27
 
27
28
  """
28
- # TODO learn if I really must change this jitted function to get the super jit to recompile
29
- # print('babbage')
30
- countFoldsCompiled(connectionGraph=connectionGraph, foldsSubTotals=foldsSubTotals, gapsWhere=gapsWhere, my=my, the=the, track=track)
29
+ # print("babbage")
30
+ countInitialize(connectionGraph=connectionGraph, gapsWhere=gapsWhere, my=my, the=the, track=track)
31
+
32
+ if the[indexThe.taskDivisions.value] > 0:
33
+ countParallel(connectionGraph=connectionGraph, foldsSubTotals=foldsSubTotals, gapsWherePARALLEL=gapsWhere, myPARALLEL=my, the=the, trackPARALLEL=track)
34
+ else:
35
+ countSequential(connectionGraph=connectionGraph, foldsSubTotals=foldsSubTotals, gapsWhere=gapsWhere, my=my, the=the, track=track)
@@ -1,5 +1,5 @@
1
1
  """A relatively stable API for oft-needed functionality."""
2
- from mapFolding import dtypeDefault, dtypeLarge
2
+ from mapFolding import dtypeDefault, dtypeLarge, pathJobDEFAULT
3
3
  from mapFolding import indexMy, indexThe, indexTrack, computationState
4
4
  from mapFolding import intInnit, defineConcurrencyLimit, oopsieKwargsie
5
5
  from numpy import integer
@@ -7,10 +7,12 @@ from numpy.typing import NDArray
7
7
  from typing import Any, List, Optional, Sequence, Type, Union
8
8
  import numba
9
9
  import numpy
10
+ import os
11
+ import pathlib
10
12
  import sys
11
13
 
12
14
  def getFilenameFoldsTotal(listDimensions: Sequence[int]) -> str:
13
- return str(sorted(listDimensions)).replace(' ', '') + '.foldsTotal'
15
+ return str(sorted(listDimensions)).replace(', ', 'x') + '.foldsTotal'
14
16
 
15
17
  def getLeavesTotal(listDimensions: Sequence[int]) -> int:
16
18
  """
@@ -36,6 +38,14 @@ def getLeavesTotal(listDimensions: Sequence[int]) -> int:
36
38
 
37
39
  return productDimensions
38
40
 
41
+ def getPathFilenameFoldsTotal(listDimensions: Sequence[int], pathishWriteFoldsTotal: Optional[Union[str, os.PathLike[str]]] = None) -> pathlib.Path:
42
+ pathFilenameFoldsTotal = pathlib.Path(pathishWriteFoldsTotal) if pathishWriteFoldsTotal is not None else pathJobDEFAULT
43
+ if pathFilenameFoldsTotal.is_dir():
44
+ filenameFoldsTotalDEFAULT = getFilenameFoldsTotal(listDimensions)
45
+ pathFilenameFoldsTotal = pathFilenameFoldsTotal / filenameFoldsTotalDEFAULT
46
+ pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
47
+ return pathFilenameFoldsTotal
48
+
39
49
  def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrencyLimit: int, CPUlimit: Optional[Union[bool, float, int]], listDimensions: Sequence[int]):
40
50
  """
41
51
  Determines whether or how to divide the computation into tasks.
@@ -148,7 +158,7 @@ def makeDataContainer(shape, datatype: Optional[Type] = None):
148
158
  datatype = dtypeDefault
149
159
  return numpy.zeros(shape, dtype=datatype)
150
160
 
151
- def outfitCountFolds(listDimensions: Sequence[int], computationDivisions: Optional[Union[int, str]] = None, CPUlimit: Optional[Union[bool, float, int]] = None, **keywordArguments: Optional[Type]) -> computationState:
161
+ def outfitCountFolds(listDimensions: Sequence[int], computationDivisions: Optional[Union[int, str]] = None, CPUlimit: Optional[Union[bool, float, int]] = None, **keywordArguments: Optional[Type[Any]]) -> computationState:
152
162
  """
153
163
  Initializes and configures the computation state for map folding computations.
154
164
 
@@ -230,11 +240,42 @@ def parseDimensions(dimensions: Sequence[int], parameterName: str = 'unnamed par
230
240
  raise ValueError(f"Dimension {dimension} must be non-negative")
231
241
  listNonNegative.append(dimension)
232
242
 
233
- if not listNonNegative:
234
- raise ValueError("At least one dimension must be non-negative")
235
-
236
243
  return listNonNegative
237
244
 
245
+ import tempfile
246
+ import shutil
247
+ import logging
248
+ import os
249
+ def saveFoldsTotal(pathFilename: Union[str, os.PathLike[str]], foldsTotal: int) -> None:
250
+ """
251
+ Save foldsTotal with multiple fallback mechanisms.
252
+
253
+ Parameters:
254
+ pathFilename: Target save location
255
+ foldsTotal: Critical computed value to save
256
+ """
257
+ """Thoughts
258
+ Everything in a try block
259
+ Save it multiple times with multiple packages
260
+ no need for context managers, especially because they can cause errors"""
261
+ try:
262
+ pathFilenameFoldsTotal = pathlib.Path(pathFilename)
263
+ pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
264
+ pathFilenameFoldsTotal.write_text(str(foldsTotal))
265
+ except Exception as ERRORmessage:
266
+ try:
267
+ print(f"\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n\n{foldsTotal=}\n\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n")
268
+ print(ERRORmessage)
269
+ print(f"\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n\n{foldsTotal=}\n\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n")
270
+ randomnessPlanB = (int(str(foldsTotal).strip()[-1]) + 1) * ['YO_']
271
+ filenameInfixUnique = ''.join(randomnessPlanB)
272
+ import os
273
+ pathFilenamePlanB = os.path.join(os.getcwd(), 'foldsTotal' + filenameInfixUnique + '.txt')
274
+ open(pathFilenamePlanB, 'w').write(str(foldsTotal))
275
+ print(str(pathFilenamePlanB))
276
+ except:
277
+ print(foldsTotal)
278
+
238
279
  def setCPUlimit(CPUlimit: Union[bool, float, int, None]) -> int:
239
280
  """Sets CPU limit for Numba concurrent operations. Note that it can only affect Numba-jitted functions that have not yet been imported.
240
281
 
@@ -1,5 +1,6 @@
1
- import multiprocessing
1
+ """An incompetent benchmarking module for mapFolding."""
2
2
  from typing import Callable
3
+ import multiprocessing
3
4
  import numpy
4
5
  import pathlib
5
6
  import time
@@ -57,7 +58,7 @@ def runBenchmarks(benchmarkIterations: int = 30) -> None:
57
58
  listCartesianProduct = list(itertools.product(listParametersOEIS, range(benchmarkIterations)))
58
59
  with ProcessPoolExecutor(max_workers) as concurrencyManager:
59
60
  listConcurrency = [concurrencyManager.submit(oeisIDfor_n, *parameters[0]) for parameters in listCartesianProduct]
60
- for complete in tqdm(as_completed(listConcurrency), total=len(listCartesianProduct)):
61
+ for _complete in tqdm(as_completed(listConcurrency), total=len(listCartesianProduct)):
61
62
  pass
62
63
 
63
64
  if __name__ == '__main__':
@@ -0,0 +1,7 @@
1
+ from mapFolding.lovelace import countSequential
2
+ from mapFolding.lovelace import countParallel
3
+ from mapFolding.lovelace import countInitialize
4
+
5
+ # from mapFolding.countSequential import countSequential
6
+ # from mapFolding.countParallel import countParallel
7
+ # from mapFolding.countInitialize import countInitialize
@@ -0,0 +1,213 @@
1
+ from mapFolding import indexMy, indexThe, indexTrack
2
+ import numba
3
+
4
+ @numba.jit((numba.int64[::1],), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
5
+ def activeGapIncrement(my):
6
+ my[indexMy.gap1ndex.value] += 1
7
+
8
+ @numba.jit((numba.int64[::1],), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
9
+ def activeLeafGreaterThan0Condition(my):
10
+ return my[indexMy.leaf1ndex.value] > 0
11
+
12
+ @numba.jit((numba.int64[::1],numba.int64[::1]), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
13
+ def activeLeafGreaterThanLeavesTotalCondition(my, the):
14
+ return my[indexMy.leaf1ndex.value] > the[indexThe.leavesTotal.value]
15
+
16
+ @numba.jit((numba.int64[::1],), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
17
+ def activeLeafIsTheFirstLeafCondition(my):
18
+ return my[indexMy.leaf1ndex.value] <= 1
19
+
20
+ @numba.jit((numba.int64[::1],numba.int64[::1]), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
21
+ def allDimensionsAreUnconstrained(my, the):
22
+ return my[indexMy.dimensionsUnconstrained.value] == the[indexThe.dimensionsTotal.value]
23
+
24
+ @numba.jit((numba.int64[::1],numba.int64[:,::1]), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
25
+ def backtrack(my, track):
26
+ my[indexMy.leaf1ndex.value] -= 1
27
+ track[indexTrack.leafBelow.value, track[indexTrack.leafAbove.value, my[indexMy.leaf1ndex.value]]] = track[indexTrack.leafBelow.value, my[indexMy.leaf1ndex.value]]
28
+ track[indexTrack.leafAbove.value, track[indexTrack.leafBelow.value, my[indexMy.leaf1ndex.value]]] = track[indexTrack.leafAbove.value, my[indexMy.leaf1ndex.value]]
29
+
30
+ @numba.jit((numba.int64[::1],numba.int64[:,::1]), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
31
+ def backtrackCondition(my, track):
32
+ return my[indexMy.leaf1ndex.value] > 0 and my[indexMy.gap1ndex.value] == track[indexTrack.gapRangeStart.value, my[indexMy.leaf1ndex.value] - 1]
33
+
34
+ @numba.jit((numba.int64[::1],), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
35
+ def gap1ndexCeilingIncrement(my):
36
+ my[indexMy.gap1ndexCeiling.value] += 1
37
+
38
+ @numba.jit((numba.int64[::1],numba.int64[::1],numba.int64[:,::1]), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
39
+ def countGaps(gapsWhere, my, track):
40
+ gapsWhere[my[indexMy.gap1ndexCeiling.value]] = my[indexMy.leafConnectee.value]
41
+ if track[indexTrack.countDimensionsGapped.value, my[indexMy.leafConnectee.value]] == 0:
42
+ gap1ndexCeilingIncrement(my=my)
43
+ track[indexTrack.countDimensionsGapped.value, my[indexMy.leafConnectee.value]] += 1
44
+
45
+ @numba.jit((numba.int64[::1],), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
46
+ def dimension1ndexIncrement(my):
47
+ my[indexMy.dimension1ndex.value] += 1
48
+
49
+ @numba.jit((numba.int64[:,:,::1], numba.int64[::1]), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
50
+ def dimensionsUnconstrainedCondition(connectionGraph, my):
51
+ return connectionGraph[my[indexMy.dimension1ndex.value], my[indexMy.leaf1ndex.value], my[indexMy.leaf1ndex.value]] == my[indexMy.leaf1ndex.value]
52
+
53
+ @numba.jit((numba.int64[::1],), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
54
+ def dimensionsUnconstrainedIncrement(my):
55
+ my[indexMy.dimensionsUnconstrained.value] += 1
56
+
57
+ @numba.jit((numba.int64[::1],numba.int64[::1],numba.int64[::1],numba.int64[:,::1]), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
58
+ def filterCommonGaps(gapsWhere, my, the, track):
59
+ gapsWhere[my[indexMy.gap1ndex.value]] = gapsWhere[my[indexMy.indexMiniGap.value]]
60
+ if track[indexTrack.countDimensionsGapped.value, gapsWhere[my[indexMy.indexMiniGap.value]]] == the[indexThe.dimensionsTotal.value] - my[indexMy.dimensionsUnconstrained.value]:
61
+ activeGapIncrement(my=my)
62
+ track[indexTrack.countDimensionsGapped.value, gapsWhere[my[indexMy.indexMiniGap.value]]] = 0
63
+
64
+ @numba.jit((numba.int64[::1],numba.int64[:,::1]), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
65
+ def findGapsInitializeVariables(my, track):
66
+ my[indexMy.dimensionsUnconstrained.value] = 0
67
+ my[indexMy.gap1ndexCeiling.value] = track[indexTrack.gapRangeStart.value, my[indexMy.leaf1ndex.value] - 1]
68
+ my[indexMy.dimension1ndex.value] = 1
69
+
70
+ @numba.jit((numba.int64[::1],numba.int64[::1],numba.int64[::1]), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
71
+ def foldsSubTotalIncrement(foldsSubTotals, my, the):
72
+ foldsSubTotals[my[indexMy.taskIndex.value]] += the[indexThe.leavesTotal.value]
73
+
74
+ @numba.jit((numba.int64[::1],), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
75
+ def indexMiniGapIncrement(my):
76
+ my[indexMy.indexMiniGap.value] += 1
77
+
78
+ @numba.jit((numba.int64[::1],), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
79
+ def indexMiniGapInitialization(my):
80
+ my[indexMy.indexMiniGap.value] = my[indexMy.gap1ndex.value]
81
+
82
+ @numba.jit((numba.int64[::1],numba.int64[::1]), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
83
+ def insertUnconstrainedLeaf(gapsWhere, my):
84
+ my[indexMy.indexLeaf.value] = 0
85
+ while my[indexMy.indexLeaf.value] < my[indexMy.leaf1ndex.value]:
86
+ gapsWhere[my[indexMy.gap1ndexCeiling.value]] = my[indexMy.indexLeaf.value]
87
+ my[indexMy.gap1ndexCeiling.value] += 1
88
+ my[indexMy.indexLeaf.value] += 1
89
+
90
+ @numba.jit((numba.int64[:,::1],), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
91
+ def leafBelowSentinelIs1Condition(track):
92
+ return track[indexTrack.leafBelow.value, 0] == 1
93
+
94
+ @numba.jit((numba.int64[:,:,::1], numba.int64[::1]), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
95
+ def leafConnecteeInitialization(connectionGraph, my):
96
+ my[indexMy.leafConnectee.value] = connectionGraph[my[indexMy.dimension1ndex.value], my[indexMy.leaf1ndex.value], my[indexMy.leaf1ndex.value]]
97
+
98
+ @numba.jit((numba.int64[:,:,::1], numba.int64[::1],numba.int64[:,::1]), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
99
+ def leafConnecteeUpdate(connectionGraph, my, track):
100
+ my[indexMy.leafConnectee.value] = connectionGraph[my[indexMy.dimension1ndex.value], my[indexMy.leaf1ndex.value], track[indexTrack.leafBelow.value, my[indexMy.leafConnectee.value]]]
101
+
102
+ @numba.jit((numba.int64[::1],), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
103
+ def loopingLeavesConnectedToActiveLeaf(my):
104
+ return my[indexMy.leafConnectee.value] != my[indexMy.leaf1ndex.value]
105
+
106
+ @numba.jit((numba.int64[::1],numba.int64[::1]), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
107
+ def loopingTheDimensions(my, the):
108
+ return my[indexMy.dimension1ndex.value] <= the[indexThe.dimensionsTotal.value]
109
+
110
+ @numba.jit((numba.int64[::1],), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
111
+ def loopingToActiveGapCeiling(my):
112
+ return my[indexMy.indexMiniGap.value] < my[indexMy.gap1ndexCeiling.value]
113
+
114
+ @numba.jit((numba.int64[::1],numba.int64[::1],numba.int64[:,::1]), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
115
+ def placeLeaf(gapsWhere, my, track):
116
+ my[indexMy.gap1ndex.value] -= 1
117
+ track[indexTrack.leafAbove.value, my[indexMy.leaf1ndex.value]] = gapsWhere[my[indexMy.gap1ndex.value]]
118
+ track[indexTrack.leafBelow.value, my[indexMy.leaf1ndex.value]] = track[indexTrack.leafBelow.value, track[indexTrack.leafAbove.value, my[indexMy.leaf1ndex.value]]]
119
+ track[indexTrack.leafBelow.value, track[indexTrack.leafAbove.value, my[indexMy.leaf1ndex.value]]] = my[indexMy.leaf1ndex.value]
120
+ track[indexTrack.leafAbove.value, track[indexTrack.leafBelow.value, my[indexMy.leaf1ndex.value]]] = my[indexMy.leaf1ndex.value]
121
+ track[indexTrack.gapRangeStart.value, my[indexMy.leaf1ndex.value]] = my[indexMy.gap1ndex.value]
122
+ my[indexMy.leaf1ndex.value] += 1
123
+
124
+ @numba.jit((numba.int64[::1],), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
125
+ def placeLeafCondition(my):
126
+ return my[indexMy.leaf1ndex.value] > 0
127
+
128
+ @numba.jit((numba.int64[::1],numba.int64[::1]), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
129
+ def thereAreComputationDivisionsYouMightSkip(my, the):
130
+ return my[indexMy.leaf1ndex.value] != the[indexThe.taskDivisions.value] or my[indexMy.leafConnectee.value] % the[indexThe.taskDivisions.value] == my[indexMy.taskIndex.value]
131
+
132
+ @numba.jit((numba.int64[:,:,::1], numba.int64[::1], numba.int64[::1], numba.int64[::1], numba.int64[:,::1]), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
133
+ def countInitialize(connectionGraph, gapsWhere, my, the, track):
134
+ while activeLeafGreaterThan0Condition(my=my):
135
+ if activeLeafIsTheFirstLeafCondition(my=my) or leafBelowSentinelIs1Condition(track=track):
136
+ findGapsInitializeVariables(my=my, track=track)
137
+ while loopingTheDimensions(my=my, the=the):
138
+ if dimensionsUnconstrainedCondition(connectionGraph=connectionGraph, my=my):
139
+ dimensionsUnconstrainedIncrement(my=my)
140
+ else:
141
+ leafConnecteeInitialization(connectionGraph=connectionGraph, my=my)
142
+ while loopingLeavesConnectedToActiveLeaf(my=my):
143
+ countGaps(gapsWhere=gapsWhere, my=my, track=track)
144
+ leafConnecteeUpdate(connectionGraph=connectionGraph, my=my, track=track)
145
+ dimension1ndexIncrement(my=my)
146
+ if allDimensionsAreUnconstrained(my=my, the=the):
147
+ insertUnconstrainedLeaf(gapsWhere=gapsWhere, my=my)
148
+ indexMiniGapInitialization(my=my)
149
+ while loopingToActiveGapCeiling(my=my):
150
+ filterCommonGaps(gapsWhere=gapsWhere, my=my, the=the, track=track)
151
+ indexMiniGapIncrement(my=my)
152
+ if placeLeafCondition(my=my):
153
+ placeLeaf(gapsWhere=gapsWhere, my=my, track=track)
154
+ if my[indexMy.gap1ndex.value] > 0:
155
+ return
156
+
157
+ @numba.jit((numba.int64[:,:,::1], numba.int64[::1], numba.int64[::1], numba.int64[::1], numba.int64[::1], numba.int64[:,::1]), parallel=False, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
158
+ def countSequential(connectionGraph, foldsSubTotals, gapsWhere, my, the, track):
159
+ while activeLeafGreaterThan0Condition(my=my):
160
+ if activeLeafIsTheFirstLeafCondition(my=my) or leafBelowSentinelIs1Condition(track=track):
161
+ if activeLeafGreaterThanLeavesTotalCondition(my=my, the=the):
162
+ foldsSubTotalIncrement(foldsSubTotals=foldsSubTotals, my=my, the=the)
163
+ else:
164
+ findGapsInitializeVariables(my=my, track=track)
165
+ while loopingTheDimensions(my=my, the=the):
166
+ if dimensionsUnconstrainedCondition(connectionGraph=connectionGraph, my=my):
167
+ dimensionsUnconstrainedIncrement(my=my)
168
+ else:
169
+ leafConnecteeInitialization(connectionGraph=connectionGraph, my=my)
170
+ while loopingLeavesConnectedToActiveLeaf(my=my):
171
+ countGaps(gapsWhere=gapsWhere, my=my, track=track)
172
+ leafConnecteeUpdate(connectionGraph=connectionGraph, my=my, track=track)
173
+ dimension1ndexIncrement(my=my)
174
+ indexMiniGapInitialization(my=my)
175
+ while loopingToActiveGapCeiling(my=my):
176
+ filterCommonGaps(gapsWhere=gapsWhere, my=my, the=the, track=track)
177
+ indexMiniGapIncrement(my=my)
178
+ while backtrackCondition(my=my, track=track):
179
+ backtrack(my=my, track=track)
180
+ if placeLeafCondition(my=my):
181
+ placeLeaf(gapsWhere=gapsWhere, my=my, track=track)
182
+
183
+ @numba.jit((numba.int64[:,:,::1], numba.int64[::1], numba.int64[::1],numba.int64[::1],numba.int64[::1],numba.int64[:,::1]), parallel=True, boundscheck=False, error_model='numpy', fastmath=True, looplift=False, nogil=True, nopython=True)
184
+ def countParallel(connectionGraph, foldsSubTotals, gapsWherePARALLEL, myPARALLEL, the, trackPARALLEL):
185
+ for indexSherpa in numba.prange(the[indexThe.taskDivisions.value]):
186
+ gapsWhere = gapsWherePARALLEL.copy()
187
+ my = myPARALLEL.copy()
188
+ my[indexMy.taskIndex.value] = indexSherpa
189
+ track = trackPARALLEL.copy()
190
+ while activeLeafGreaterThan0Condition(my=my):
191
+ if activeLeafIsTheFirstLeafCondition(my=my) or leafBelowSentinelIs1Condition(track=track):
192
+ if activeLeafGreaterThanLeavesTotalCondition(my=my, the=the):
193
+ foldsSubTotalIncrement(foldsSubTotals=foldsSubTotals, my=my, the=the)
194
+ else:
195
+ findGapsInitializeVariables(my=my, track=track)
196
+ while loopingTheDimensions(my=my, the=the):
197
+ if dimensionsUnconstrainedCondition(connectionGraph=connectionGraph, my=my):
198
+ dimensionsUnconstrainedIncrement(my=my)
199
+ else:
200
+ leafConnecteeInitialization(connectionGraph=connectionGraph, my=my)
201
+ while loopingLeavesConnectedToActiveLeaf(my=my):
202
+ if thereAreComputationDivisionsYouMightSkip(my=my, the=the):
203
+ countGaps(gapsWhere=gapsWhere, my=my, track=track)
204
+ leafConnecteeUpdate(connectionGraph=connectionGraph, my=my, track=track)
205
+ dimension1ndexIncrement(my=my)
206
+ indexMiniGapInitialization(my=my)
207
+ while loopingToActiveGapCeiling(my=my):
208
+ filterCommonGaps(gapsWhere=gapsWhere, my=my, the=the, track=track)
209
+ indexMiniGapIncrement(my=my)
210
+ while backtrackCondition(my=my, track=track):
211
+ backtrack(my=my, track=track)
212
+ if placeLeafCondition(my=my):
213
+ placeLeaf(gapsWhere=gapsWhere, my=my, track=track)
@@ -1,3 +1,5 @@
1
+ """I was able to implement the algorithm with JAX, but I didn't see an advantage and it's a pain in the ass.
2
+ I don't maintain this module."""
1
3
  from mapFolding import validateListDimensions, getLeavesTotal, makeConnectionGraph
2
4
  from typing import List, Tuple
3
5
  import jax
@@ -0,0 +1,152 @@
1
+ from mapFolding import indexMy, indexThe, indexTrack
2
+ import ast
3
+ import copy
4
+ import pathlib
5
+
6
+ def getDictionaryEnumValues():
7
+ dictionaryEnumValues = {}
8
+ for enumIndex in [indexMy, indexThe, indexTrack]:
9
+ for memberName, memberValue in enumIndex._member_map_.items():
10
+ dictionaryEnumValues[f"{enumIndex.__name__}.{memberName}.value"] = memberValue.value
11
+ return dictionaryEnumValues
12
+
13
+ class RecursiveInlinerWithEnum(ast.NodeTransformer):
14
+ def __init__(self, dictionaryFunctions, dictionaryEnumValues):
15
+ self.dictionaryFunctions = dictionaryFunctions
16
+ self.dictionaryEnumValues = dictionaryEnumValues
17
+ self.processed = set() # Track processed functions to avoid infinite recursion
18
+
19
+ def inlineFunctionBody(self, functionName):
20
+ if functionName in self.processed:
21
+ return None
22
+
23
+ self.processed.add(functionName)
24
+ inlineDefinition = self.dictionaryFunctions[functionName]
25
+ # Recursively process the function body
26
+ for node in ast.walk(inlineDefinition):
27
+ self.visit(node)
28
+ return inlineDefinition
29
+
30
+ def visit_Attribute(self, node):
31
+ # Substitute enum identifiers (e.g., indexMy.leaf1ndex.value)
32
+ if isinstance(node.value, ast.Attribute) and isinstance(node.value.value, ast.Name):
33
+ enumPath = f"{node.value.value.id}.{node.value.attr}.{node.attr}"
34
+ if enumPath in self.dictionaryEnumValues:
35
+ return ast.Constant(value=self.dictionaryEnumValues[enumPath])
36
+ return self.generic_visit(node)
37
+
38
+ def visit_Call(self, node):
39
+ callNode = self.generic_visit(node)
40
+ if isinstance(callNode, ast.Call) and isinstance(callNode.func, ast.Name) and callNode.func.id in self.dictionaryFunctions:
41
+ inlineDefinition = self.inlineFunctionBody(callNode.func.id)
42
+ if inlineDefinition and inlineDefinition.body:
43
+ lastStmt = inlineDefinition.body[-1]
44
+ if isinstance(lastStmt, ast.Return) and lastStmt.value is not None:
45
+ return self.visit(lastStmt.value)
46
+ elif isinstance(lastStmt, ast.Expr) and lastStmt.value is not None:
47
+ return self.visit(lastStmt.value)
48
+ return None
49
+ return callNode
50
+
51
+ def visit_Expr(self, node):
52
+ if isinstance(node.value, ast.Call):
53
+ if isinstance(node.value.func, ast.Name) and node.value.func.id in self.dictionaryFunctions:
54
+ inlineDefinition = self.inlineFunctionBody(node.value.func.id)
55
+ if inlineDefinition:
56
+ return [self.visit(stmt) for stmt in inlineDefinition.body]
57
+ return self.generic_visit(node)
58
+
59
+ def findRequiredImports(node):
60
+ """Find all modules that need to be imported based on AST analysis.
61
+ NOTE: due to hardcoding, this is a glorified regex. No, wait, this is less versatile than regex."""
62
+ requiredImports = set()
63
+
64
+ class ImportFinder(ast.NodeVisitor):
65
+ def visit_Name(self, node):
66
+ if node.id in {'numba'}:
67
+ requiredImports.add(node.id)
68
+ self.generic_visit(node)
69
+
70
+ def visitDecorator(self, node):
71
+ if isinstance(node, ast.Call) and isinstance(node.func, ast.Name):
72
+ if node.func.id == 'jit':
73
+ requiredImports.add('numba')
74
+ self.generic_visit(node)
75
+
76
+ ImportFinder().visit(node)
77
+ return requiredImports
78
+
79
+ def generateImports(requiredImports):
80
+ """Generate import statements based on required modules."""
81
+ importStatements = []
82
+
83
+ # Map of module names to their import statements
84
+ importMapping = {
85
+ 'numba': 'import numba',
86
+ }
87
+
88
+ for moduleName in sorted(requiredImports):
89
+ if moduleName in importMapping:
90
+ importStatements.append(importMapping[moduleName])
91
+
92
+ return '\n'.join(importStatements)
93
+
94
+ def inlineFunctions(sourceCode, targetFunctionName, dictionaryEnumValues):
95
+ dictionaryParsed = ast.parse(sourceCode)
96
+ dictionaryFunctions = {
97
+ element.name: element
98
+ for element in dictionaryParsed.body
99
+ if isinstance(element, ast.FunctionDef)
100
+ }
101
+ nodeTarget = dictionaryFunctions[targetFunctionName]
102
+ nodeInliner = RecursiveInlinerWithEnum(dictionaryFunctions, dictionaryEnumValues)
103
+ nodeInlined = nodeInliner.visit(nodeTarget)
104
+ ast.fix_missing_locations(nodeInlined)
105
+
106
+ # Generate imports
107
+ requiredImports = findRequiredImports(nodeInlined)
108
+ importStatements = generateImports(requiredImports)
109
+
110
+ # Combine imports with inlined code
111
+ inlinedCode = importStatements + '\n\n' + ast.unparse(ast.Module(body=[nodeInlined], type_ignores=[]))
112
+ return inlinedCode
113
+
114
+ def Z0Z_inlineMapFolding():
115
+ dictionaryEnumValues = getDictionaryEnumValues()
116
+
117
+ pathFilenameSource = pathlib.Path("/apps/mapFolding/mapFolding/lovelace.py")
118
+ codeSource = pathFilenameSource.read_text()
119
+
120
+ listCallables = [
121
+ 'countInitialize',
122
+ 'countParallel',
123
+ 'countSequential',
124
+ ]
125
+
126
+ listPathFilenamesDestination: list[pathlib.Path] = []
127
+ for callableTarget in listCallables:
128
+ pathFilenameDestination = pathFilenameSource.with_stem(callableTarget)
129
+ codeInlined = inlineFunctions(codeSource, callableTarget, dictionaryEnumValues)
130
+ pathFilenameDestination.write_text(codeInlined)
131
+ listPathFilenamesDestination.append(pathFilenameDestination)
132
+
133
+ listNoNumba = [
134
+ 'countInitialize',
135
+ 'countSequential',
136
+ ]
137
+
138
+ listPathFilenamesNoNumba = []
139
+ for pathFilename in listPathFilenamesDestination:
140
+ if pathFilename.stem in listNoNumba:
141
+ pathFilenameNoNumba = pathFilename.with_name(pathFilename.stem + 'NoNumba' + pathFilename.suffix)
142
+ else:
143
+ continue
144
+ codeNoNumba = pathFilename.read_text()
145
+ for codeLine in copy.copy(codeNoNumba.splitlines()):
146
+ if 'numba' in codeLine:
147
+ codeNoNumba = codeNoNumba.replace(codeLine, '')
148
+ pathFilenameNoNumba.write_text(codeNoNumba)
149
+ listPathFilenamesNoNumba.append(pathFilenameNoNumba)
150
+
151
+ if __name__ == '__main__':
152
+ Z0Z_inlineMapFolding()
@@ -0,0 +1,47 @@
1
+ from typing import Any, Optional, Sequence, Type, Union
2
+
3
+ def Z0Z_makeJob(listDimensions: Sequence[int], **keywordArguments: Optional[Type[Any]]):
4
+ from mapFolding import outfitCountFolds
5
+ stateUniversal = outfitCountFolds(listDimensions, computationDivisions=None, CPUlimit=None, **keywordArguments)
6
+ from mapFolding.countInitialize import countInitialize
7
+ countInitialize(stateUniversal['connectionGraph'], stateUniversal['gapsWhere'], stateUniversal['my'], stateUniversal['the'], stateUniversal['track'])
8
+ from mapFolding import getPathFilenameFoldsTotal
9
+ pathFilenameChopChop = getPathFilenameFoldsTotal(stateUniversal['mapShape'])
10
+ import pathlib
11
+ suffix = pathFilenameChopChop.suffix
12
+ pathJob = pathlib.Path(str(pathFilenameChopChop)[0:-len(suffix)])
13
+ pathJob.mkdir(parents=True, exist_ok=True)
14
+ pathFilenameJob = pathJob / 'stateJob.pkl'
15
+
16
+ pathFilenameFoldsTotal = getPathFilenameFoldsTotal(stateUniversal['mapShape'], pathFilenameJob.parent)
17
+ stateJob = {**stateUniversal, 'pathFilenameFoldsTotal': pathFilenameFoldsTotal}
18
+
19
+ del stateJob['mapShape']
20
+
21
+ import pickle
22
+ pathFilenameJob.write_bytes(pickle.dumps(stateJob))
23
+ return pathFilenameJob
24
+
25
+ def runJob(pathFilename):
26
+ from typing import Final
27
+ import numpy
28
+ from pathlib import Path
29
+ pathFilenameJob = Path(pathFilename)
30
+ from pickle import loads
31
+ stateJob = loads(pathFilenameJob.read_bytes())
32
+
33
+ connectionGraph: numpy.ndarray = stateJob['connectionGraph']
34
+ foldsSubTotals: numpy.ndarray = stateJob['foldsSubTotals']
35
+ gapsWhere: numpy.ndarray = stateJob['gapsWhere']
36
+ my: numpy.ndarray = stateJob['my']
37
+ pathFilenameFoldsTotal: Final[Path] = stateJob['pathFilenameFoldsTotal']
38
+ the: Final[numpy.ndarray] = stateJob['the']
39
+ track: numpy.ndarray = stateJob['track']
40
+
41
+ from mapFolding.countSequentialNoNumba import countSequential
42
+ countSequential(connectionGraph, foldsSubTotals, gapsWhere, my, the, track)
43
+
44
+ print(foldsSubTotals.sum().item())
45
+ Path(pathFilenameFoldsTotal).parent.mkdir(parents=True, exist_ok=True)
46
+ Path(pathFilenameFoldsTotal).write_text(str(foldsSubTotals.sum().item()))
47
+ print(pathFilenameFoldsTotal)