mapFolding 0.5.0__py3-none-any.whl → 0.6.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.
Files changed (42) hide show
  1. mapFolding/__init__.py +96 -58
  2. mapFolding/basecamp.py +5 -7
  3. mapFolding/beDRY.py +11 -41
  4. mapFolding/oeis.py +71 -74
  5. mapFolding/theConfiguration.py +58 -0
  6. mapFolding/theDao.py +1 -1
  7. mapFolding/theSSOT.py +14 -48
  8. mapFolding/theSSOTdatatypes.py +25 -36
  9. mapFolding/theWrongWay.py +7 -0
  10. {mapFolding-0.5.0.dist-info → mapfolding-0.6.0.dist-info}/METADATA +6 -4
  11. mapfolding-0.6.0.dist-info/RECORD +16 -0
  12. {mapFolding-0.5.0.dist-info → mapfolding-0.6.0.dist-info}/WHEEL +1 -1
  13. {mapFolding-0.5.0.dist-info → mapfolding-0.6.0.dist-info}/top_level.txt +0 -1
  14. mapFolding/reference/flattened.py +0 -377
  15. mapFolding/reference/hunterNumba.py +0 -132
  16. mapFolding/reference/irvineJavaPort.py +0 -120
  17. mapFolding/reference/jax.py +0 -208
  18. mapFolding/reference/lunnan.py +0 -153
  19. mapFolding/reference/lunnanNumpy.py +0 -123
  20. mapFolding/reference/lunnanWhile.py +0 -121
  21. mapFolding/reference/rotatedEntryPoint.py +0 -240
  22. mapFolding/reference/total_countPlus1vsPlusN.py +0 -211
  23. mapFolding/someAssemblyRequired/__init__.py +0 -5
  24. mapFolding/someAssemblyRequired/getLLVMforNoReason.py +0 -19
  25. mapFolding/someAssemblyRequired/makeJob.py +0 -56
  26. mapFolding/someAssemblyRequired/synthesizeModuleJAX.py +0 -27
  27. mapFolding/someAssemblyRequired/synthesizeNumba.py +0 -345
  28. mapFolding/someAssemblyRequired/synthesizeNumbaGeneralized.py +0 -397
  29. mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +0 -155
  30. mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +0 -123
  31. mapFolding/syntheticModules/numbaCount.py +0 -158
  32. mapFolding/syntheticModules/numba_doTheNeedful.py +0 -13
  33. mapFolding-0.5.0.dist-info/RECORD +0 -39
  34. tests/__init__.py +0 -1
  35. tests/conftest.py +0 -335
  36. tests/test_computations.py +0 -42
  37. tests/test_oeis.py +0 -128
  38. tests/test_other.py +0 -175
  39. tests/test_tasks.py +0 -40
  40. /mapFolding/{syntheticModules/__init__.py → py.typed} +0 -0
  41. {mapFolding-0.5.0.dist-info → mapfolding-0.6.0.dist-info}/LICENSE +0 -0
  42. {mapFolding-0.5.0.dist-info → mapfolding-0.6.0.dist-info}/entry_points.txt +0 -0
mapFolding/theSSOT.py CHANGED
@@ -1,22 +1,8 @@
1
1
  from collections.abc import Callable
2
- from mapFolding.theSSOTdatatypes import (
3
- EnumIndices,
4
- indexMy,
5
- indexTrack,
6
- reportDatatypeLimit,
7
- setDatatypeModule,
8
- setDatatypeElephino,
9
- setDatatypeFoldsTotal,
10
- setDatatypeLeavesTotal,
11
- getDatatypeModule,
12
- setInStone,
13
- hackSSOTdtype,
14
- hackSSOTdatatype,
15
- )
2
+ from mapFolding.theSSOTdatatypes import *
16
3
  from numba.core.compiler import CompilerBase as numbaCompilerBase
17
4
  from numpy import dtype, integer, ndarray
18
5
  from pathlib import Path
19
- from sys import modules as sysModules
20
6
  from types import ModuleType
21
7
  from typing import Any, Final, TYPE_CHECKING, cast
22
8
 
@@ -34,49 +20,29 @@ else:
34
20
  - Configuration Registry
35
21
  - Write-Once, Read-Many (WORM) / Immutable Initialization
36
22
  - Lazy Initialization
37
- - Separation of configuration from business logic
23
+ - Separate configuration from business logic
24
+
25
+ theSSOT and yourSSOT
38
26
 
39
27
  delay realization/instantiation until a concrete value is desired
40
28
  moment of truth: when the value is needed, not when the value is defined
41
29
  """
42
30
 
43
- myPackageNameIs = "mapFolding"
44
-
45
- moduleOfSyntheticModules = "syntheticModules"
46
- # TODO I'm not sure if this is the right tool for the job.
47
- formatFilenameModuleDEFAULT = "numba_{callableTarget}.py"
48
- dispatcherCallableNameDEFAULT = "doTheNeedful"
49
-
50
- def getPathPackage() -> Path:
51
- import importlib, inspect
52
- pathPackage = Path(inspect.getfile(importlib.import_module(myPackageNameIs)))
53
- if pathPackage.is_file():
54
- pathPackage = pathPackage.parent
55
- return pathPackage
56
-
57
- def getPathJobRootDEFAULT() -> Path:
58
- if 'google.colab' in sysModules:
59
- pathJobDEFAULT = Path("/content/drive/MyDrive") / "jobs"
60
- else:
61
- pathJobDEFAULT = getPathPackage() / "jobs"
62
- return pathJobDEFAULT
31
+ """
32
+ listDimensions: list[int]
33
+ mapShape
34
+ tupleDimensions: tuple[int, ...]
35
+ dimensionsTuple
36
+ dimensionTuple
37
+ """
63
38
 
64
39
  def getPathSyntheticModules() -> Path:
65
- pathSyntheticModules = getPathPackage() / moduleOfSyntheticModules
66
- return pathSyntheticModules
67
-
68
- def getAlgorithmSource() -> ModuleType:
69
- from mapFolding import theDao
70
- return theDao
40
+ return pathPackage / moduleOfSyntheticModules
71
41
 
72
42
  def getAlgorithmDispatcher() -> Callable[..., None]:
73
- algorithmSource = getAlgorithmSource()
43
+ algorithmSource: ModuleType = getAlgorithmSource()
74
44
  return cast(Callable[..., None], algorithmSource.doTheNeedful) # 'doTheNeedful' is duplicated and there is not a SSOT for it
75
45
 
76
- def getDispatcherCallable() -> Callable[..., None]:
77
- from mapFolding.syntheticModules import numba_doTheNeedful
78
- return cast(Callable[..., None], numba_doTheNeedful.doTheNeedful)
79
-
80
46
  # NOTE I want this _concept_, not necessarily this method, to be well implemented and usable everywhere: Python, Numba, Jax, CUDA, idc
81
47
  class computationState(TypedDict):
82
48
  connectionGraph: ndarray[tuple[int, int, int], dtype[integer[Any]]]
@@ -133,7 +99,7 @@ class ParametersNumba(TypedDict):
133
99
  nopython: bool
134
100
  parallel: bool
135
101
  pipeline_class: NotRequired[type[numbaCompilerBase]]
136
- signature_or_function: NotRequired[Any | Callable | str | tuple]
102
+ signature_or_function: NotRequired[Any | Callable[..., Any] | str | tuple[Any, ...]]
137
103
  target: NotRequired[str]
138
104
 
139
105
  parametersNumbaFailEarly: Final[ParametersNumba] = { '_nrt': True, 'boundscheck': True, 'cache': True, 'error_model': 'python', 'fastmath': False, 'forceinline': True, 'inline': 'always', 'looplift': False, 'no_cfunc_wrapper': False, 'no_cpython_wrapper': False, 'nopython': True, 'parallel': False, }
@@ -1,18 +1,9 @@
1
+ from mapFolding.theConfiguration import *
1
2
  from collections import defaultdict
2
- from typing import Any, cast, Final, TYPE_CHECKING
3
+ from typing import Any, cast, Final
3
4
  import enum
4
- import numba
5
- import numpy
6
-
7
- try:
8
- from typing import NotRequired
9
- except Exception:
10
- from typing_extensions import NotRequired # type: ignore
11
-
12
- if TYPE_CHECKING:
13
- from typing import TypedDict
14
- else:
15
- TypedDict = dict
5
+ import numba # type: ignore
6
+ import numpy # type: ignore
16
7
 
17
8
  class EnumIndices(enum.IntEnum):
18
9
  @staticmethod
@@ -51,20 +42,18 @@ _datatypeDefault: Final[dict[str, str]] = {
51
42
  'foldsTotal': 'int64',
52
43
  'leavesTotal': 'uint16',
53
44
  }
54
- _datatypeModule = ''
55
- _datatypeModuleDEFAULT: Final[str] = 'numpy'
56
-
57
- _datatype: dict[str, str] = defaultdict(str)
45
+ _datatypeModule: str = ''
46
+ _registryOfDatatypes: dict[str, str] = defaultdict(str)
58
47
 
59
48
  def reportDatatypeLimit(identifier: str, datatype: str, sourGrapes: bool | None = False) -> str:
60
- global _datatype
61
- if not _datatype[identifier]:
62
- _datatype[identifier] = datatype
63
- elif _datatype[identifier] == datatype:
49
+ global _registryOfDatatypes
50
+ if not _registryOfDatatypes[identifier]:
51
+ _registryOfDatatypes[identifier] = datatype
52
+ elif _registryOfDatatypes[identifier] == datatype:
64
53
  pass
65
54
  elif sourGrapes:
66
- raise Exception(f"Datatype is '{_datatype[identifier]}' not '{datatype}', so you can take your ball and go home.")
67
- return _datatype[identifier]
55
+ raise Exception(f"Datatype is '{_registryOfDatatypes[identifier]}' not '{datatype}', so you can take your ball and go home.")
56
+ return _registryOfDatatypes[identifier]
68
57
 
69
58
  def setDatatypeModule(datatypeModule: str, sourGrapes: bool | None = False) -> str:
70
59
  global _datatypeModule
@@ -86,29 +75,29 @@ def setDatatypeLeavesTotal(datatype: str, sourGrapes: bool | None = False) -> st
86
75
  return reportDatatypeLimit('leavesTotal', datatype, sourGrapes)
87
76
 
88
77
  def _get_datatype(identifier: str) -> str:
89
- global _datatype
90
- if not _datatype[identifier]:
78
+ global _registryOfDatatypes
79
+ if not _registryOfDatatypes[identifier]:
91
80
  if identifier in indexMy._member_names_:
92
- _datatype[identifier] = _datatypeDefault.get(identifier) or _get_datatype('elephino')
81
+ _registryOfDatatypes[identifier] = _datatypeDefault.get(identifier) or _get_datatype('elephino')
93
82
  elif identifier in indexTrack._member_names_:
94
- _datatype[identifier] = _datatypeDefault.get(identifier) or _get_datatype('elephino')
83
+ _registryOfDatatypes[identifier] = _datatypeDefault.get(identifier) or _get_datatype('elephino')
95
84
  else:
96
- _datatype[identifier] = _datatypeDefault.get(identifier) or _get_datatype('foldsTotal')
97
- return _datatype[identifier]
85
+ _registryOfDatatypes[identifier] = _datatypeDefault.get(identifier) or _get_datatype('foldsTotal')
86
+ return _registryOfDatatypes[identifier]
98
87
 
99
88
  def getDatatypeModule() -> str:
100
89
  global _datatypeModule
101
90
  if not _datatypeModule:
102
- _datatypeModule = _datatypeModuleDEFAULT
91
+ _datatypeModule = datatypeModulePACKAGING
103
92
  return _datatypeModule
104
93
 
105
94
  def setInStone(identifier: str) -> type[Any]:
106
- datatypeModule = getDatatypeModule()
107
- datatypeStr = _get_datatype(identifier)
95
+ datatypeModule: str = getDatatypeModule()
96
+ datatypeStr: str = _get_datatype(identifier)
108
97
  return cast(type[Any], getattr(eval(datatypeModule), datatypeStr))
109
98
 
110
99
  def hackSSOTdtype(identifier: str) -> type[Any]:
111
- _hackSSOTdtype={
100
+ _hackSSOTdtype: dict[str, str]={
112
101
  'connectionGraph': 'dtypeLeavesTotal',
113
102
  'dtypeElephino': 'dtypeElephino',
114
103
  'dtypeFoldsTotal': 'dtypeFoldsTotal',
@@ -119,7 +108,7 @@ def hackSSOTdtype(identifier: str) -> type[Any]:
119
108
  'my': 'dtypeElephino',
120
109
  'track': 'dtypeElephino',
121
110
  }
122
- RubeGoldBerg = _hackSSOTdtype[identifier]
111
+ RubeGoldBerg: str = _hackSSOTdtype[identifier]
123
112
  if RubeGoldBerg == 'dtypeElephino':
124
113
  return setInStone('elephino')
125
114
  elif RubeGoldBerg == 'dtypeFoldsTotal':
@@ -129,7 +118,7 @@ def hackSSOTdtype(identifier: str) -> type[Any]:
129
118
  raise Exception("Dude, you forgot to set a value in `hackSSOTdtype`.")
130
119
 
131
120
  def hackSSOTdatatype(identifier: str) -> str:
132
- _hackSSOTdatatype={
121
+ _hackSSOTdatatype: dict[str, str]={
133
122
  'connectionGraph': 'datatypeLeavesTotal',
134
123
  'countDimensionsGapped': 'datatypeLeavesTotal',
135
124
  'datatypeElephino': 'datatypeElephino',
@@ -156,7 +145,7 @@ def hackSSOTdatatype(identifier: str) -> str:
156
145
  'taskIndex': 'datatypeLeavesTotal',
157
146
  'track': 'datatypeElephino',
158
147
  }
159
- RubeGoldBerg = _hackSSOTdatatype[identifier]
148
+ RubeGoldBerg: str = _hackSSOTdatatype[identifier]
160
149
  if RubeGoldBerg == 'datatypeElephino':
161
150
  return _get_datatype('elephino')
162
151
  elif RubeGoldBerg == 'datatypeFoldsTotal':
@@ -0,0 +1,7 @@
1
+ from typing import Final
2
+
3
+ datatypeModulePACKAGING: Final[str] = 'numpy'
4
+ myPackageNameIsPACKAGING: str = "mapFolding"
5
+ listCallablesDispatcheesHARDCODED: list[str] = ['countInitialize', 'countParallel', 'countSequential']
6
+ additional_importsHARDCODED: list[str] = ['numba']
7
+ algorithmSourcePACKAGING: str = 'theDao'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mapFolding
3
- Version: 0.5.0
3
+ Version: 0.6.0
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
@@ -16,12 +16,12 @@ Classifier: Intended Audience :: Other Audience
16
16
  Classifier: Intended Audience :: Science/Research
17
17
  Classifier: Natural Language :: English
18
18
  Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python
20
+ Classifier: Programming Language :: Python :: 3
19
21
  Classifier: Programming Language :: Python :: 3.10
20
22
  Classifier: Programming Language :: Python :: 3.11
21
23
  Classifier: Programming Language :: Python :: 3.12
22
24
  Classifier: Programming Language :: Python :: 3.13
23
- Classifier: Programming Language :: Python :: 3
24
- Classifier: Programming Language :: Python
25
25
  Classifier: Topic :: Scientific/Engineering :: Mathematics
26
26
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
27
  Classifier: Typing :: Typed
@@ -33,7 +33,9 @@ Requires-Dist: numpy
33
33
  Requires-Dist: Z0Z_tools
34
34
  Provides-Extra: testing
35
35
  Requires-Dist: autoflake; extra == "testing"
36
+ Requires-Dist: mypy; extra == "testing"
36
37
  Requires-Dist: more_itertools; extra == "testing"
38
+ Requires-Dist: numba_progress; extra == "testing"
37
39
  Requires-Dist: pytest-cov; extra == "testing"
38
40
  Requires-Dist: pytest-env; extra == "testing"
39
41
  Requires-Dist: pytest-xdist; extra == "testing"
@@ -152,4 +154,4 @@ Available OEIS sequences:
152
154
  [![Static Badge](https://img.shields.io/badge/2011_August-Homeless_since-blue?style=flat)](https://HunterThinks.com/support)
153
155
  [![YouTube Channel Subscribers](https://img.shields.io/youtube/channel/subscribers/UC3Gx7kz61009NbhpRtPP7tw)](https://www.youtube.com/@HunterHogan)
154
156
 
155
- [![CC-BY-NC-4.0](https://github.com/hunterhogan/mapFolding/blob/main/CC-BY-NC-4.0.svg)](https://creativecommons.org/licenses/by-nc/4.0/)
157
+ [![CC-BY-NC-4.0](https://github.com/hunterhogan/mapFolding/blob/main/CC-BY-NC-4.0.png)](https://creativecommons.org/licenses/by-nc/4.0/)
@@ -0,0 +1,16 @@
1
+ mapFolding/__init__.py,sha256=pggwv9wBf1jqZUxp8qO1Si1LswyTGZQLoRqdO0ptiS4,2707
2
+ mapFolding/basecamp.py,sha256=Y2yBE89IviRcVOvLM1mRWocqPHVkmZ2awAZVkDTDTAo,3571
3
+ mapFolding/beDRY.py,sha256=5D-9AwbtBULTQ4N5WX57ZXqw6mCmPa31eCU2xpQXlf4,15750
4
+ mapFolding/oeis.py,sha256=qxNrmEfx5IYc5tw312J3Jk3xO-nAeaM8unxmxM7R8Cs,11924
5
+ mapFolding/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ mapFolding/theConfiguration.py,sha256=vKLwflVNzthbZlOWTwEC4xqh0rp9Xoyz-hUAT7a5CSA,2098
7
+ mapFolding/theDao.py,sha256=A-cDyixGLROAehYxf4k0srVjoe4aSeXJK_k40irfndU,12626
8
+ mapFolding/theSSOT.py,sha256=55sy05nmAaPpDAiuA6t27u20obHMzY8NTD_XR2Ffgos,4940
9
+ mapFolding/theSSOTdatatypes.py,sha256=V5G99QwMMaZV-J3EIPAKIKorV3fz0LHUgwErF9MP01E,5854
10
+ mapFolding/theWrongWay.py,sha256=p7nL3JM9t4mbF1xBn1gZzG1jGs9KRkyGP3XliUPkm-4,312
11
+ mapfolding-0.6.0.dist-info/LICENSE,sha256=NxH5Y8BdC-gNU-WSMwim3uMbID2iNDXJz7fHtuTdXhk,19346
12
+ mapfolding-0.6.0.dist-info/METADATA,sha256=cnO-gz66V3NnAdUIAgKKIsT8PgVxqD4I726HqgYVGSc,7768
13
+ mapfolding-0.6.0.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
14
+ mapfolding-0.6.0.dist-info/entry_points.txt,sha256=F3OUeZR1XDTpoH7k3wXuRb3KF_kXTTeYhu5AGK1SiOQ,146
15
+ mapfolding-0.6.0.dist-info/top_level.txt,sha256=aG3bjFBoxxuaV3Iu1wZAd241Ubs3cdaJtKYBQBDIjsg,11
16
+ mapfolding-0.6.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.0)
2
+ Generator: setuptools (75.8.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,377 +0,0 @@
1
- """The algorithm flattened into semantic sections.
2
- This version is not maintained, so you may see differences from the current version."""
3
- from numpy import integer
4
- from numpy.typing import NDArray
5
- from typing import List, Any, Final, Optional, Union, Sequence, Tuple, Type, TypedDict
6
- import enum
7
- import numpy
8
- import sys
9
-
10
- def countFolds(listDimensions: Sequence[int], computationDivisions = None, CPUlimit: Optional[Union[int, float, bool]] = None):
11
- def doWhile():
12
-
13
- while activeLeafGreaterThan0Condition():
14
-
15
- if activeLeafIsTheFirstLeafCondition() or leafBelowSentinelIs1Condition():
16
-
17
- if activeLeafGreaterThanLeavesTotalCondition():
18
- foldsSubTotalsIncrement()
19
-
20
- else:
21
-
22
- findGapsInitializeVariables()
23
- while loopingTheDimensions():
24
-
25
- if dimensionsUnconstrainedCondition():
26
- dimensionsUnconstrainedIncrement()
27
-
28
- else:
29
-
30
- leafConnecteeInitialization()
31
- while loopingLeavesConnectedToActiveLeaf():
32
- if thereAreComputationDivisionsYouMightSkip():
33
- countGaps()
34
- leafConnecteeUpdate()
35
-
36
- dimension1ndexIncrement()
37
-
38
- if allDimensionsAreUnconstrained():
39
- insertUnconstrainedLeaf()
40
-
41
- indexMiniGapInitialization()
42
- while loopingToActiveGapCeiling():
43
- filterCommonGaps()
44
- indexMiniGapIncrement()
45
-
46
- while backtrackCondition():
47
- backtrack()
48
-
49
- if placeLeafCondition():
50
- placeLeaf()
51
-
52
- def activeGapIncrement():
53
- my[indexMy.gap1ndex] += 1
54
-
55
- def activeLeafGreaterThan0Condition():
56
- return my[indexMy.leaf1ndex] > 0
57
-
58
- def activeLeafGreaterThanLeavesTotalCondition():
59
- return my[indexMy.leaf1ndex] > the[indexThe.leavesTotal]
60
-
61
- def activeLeafIsTheFirstLeafCondition():
62
- return my[indexMy.leaf1ndex] <= 1
63
-
64
- def activeLeafNotEqualToTaskDivisionsCondition():
65
- return my[indexMy.leaf1ndex] != the[indexThe.taskDivisions]
66
-
67
- def allDimensionsAreUnconstrained():
68
- return my[indexMy.dimensionsUnconstrained] == the[indexThe.dimensionsTotal]
69
-
70
- def backtrack():
71
- my[indexMy.leaf1ndex] -= 1
72
- track[indexTrack.leafBelow, track[indexTrack.leafAbove, my[indexMy.leaf1ndex]]] = track[indexTrack.leafBelow, my[indexMy.leaf1ndex]]
73
- track[indexTrack.leafAbove, track[indexTrack.leafBelow, my[indexMy.leaf1ndex]]] = track[indexTrack.leafAbove, my[indexMy.leaf1ndex]]
74
-
75
- def backtrackCondition():
76
- return my[indexMy.leaf1ndex] > 0 and my[indexMy.gap1ndex] == track[indexTrack.gapRangeStart, my[indexMy.leaf1ndex] - 1]
77
-
78
- def computationDivisionsCondition():
79
- return the[indexThe.taskDivisions] == int(False)
80
-
81
- def countGaps():
82
- gapsWhere[my[indexMy.gap1ndexCeiling]] = my[indexMy.leafConnectee]
83
- if track[indexTrack.countDimensionsGapped, my[indexMy.leafConnectee]] == 0:
84
- gap1ndexCeilingIncrement()
85
- track[indexTrack.countDimensionsGapped, my[indexMy.leafConnectee]] += 1
86
-
87
- def dimension1ndexIncrement():
88
- my[indexMy.dimension1ndex] += 1
89
-
90
- def dimensionsUnconstrainedCondition():
91
- return connectionGraph[my[indexMy.dimension1ndex], my[indexMy.leaf1ndex], my[indexMy.leaf1ndex]] == my[indexMy.leaf1ndex]
92
-
93
- def dimensionsUnconstrainedIncrement():
94
- my[indexMy.dimensionsUnconstrained] += 1
95
-
96
- def filterCommonGaps():
97
- gapsWhere[my[indexMy.gap1ndex]] = gapsWhere[my[indexMy.indexMiniGap]]
98
- if track[indexTrack.countDimensionsGapped, gapsWhere[my[indexMy.indexMiniGap]]] == the[indexThe.dimensionsTotal] - my[indexMy.dimensionsUnconstrained]:
99
- activeGapIncrement()
100
- track[indexTrack.countDimensionsGapped, gapsWhere[my[indexMy.indexMiniGap]]] = 0
101
-
102
- def findGapsInitializeVariables():
103
- my[indexMy.dimensionsUnconstrained] = 0
104
- my[indexMy.gap1ndexCeiling] = track[indexTrack.gapRangeStart, my[indexMy.leaf1ndex] - 1]
105
- my[indexMy.dimension1ndex] = 1
106
-
107
- def foldsSubTotalsIncrement():
108
- foldsSubTotals[my[indexMy.taskIndex]] += the[indexThe.leavesTotal]
109
-
110
- def gap1ndexCeilingIncrement():
111
- my[indexMy.gap1ndexCeiling] += 1
112
-
113
- def indexMiniGapIncrement():
114
- my[indexMy.indexMiniGap] += 1
115
-
116
- def indexMiniGapInitialization():
117
- my[indexMy.indexMiniGap] = my[indexMy.gap1ndex]
118
-
119
- def insertUnconstrainedLeaf():
120
- my[indexMy.indexLeaf] = 0
121
- while my[indexMy.indexLeaf] < my[indexMy.leaf1ndex]:
122
- gapsWhere[my[indexMy.gap1ndexCeiling]] = my[indexMy.indexLeaf]
123
- my[indexMy.gap1ndexCeiling] += 1
124
- my[indexMy.indexLeaf] += 1
125
-
126
- def leafBelowSentinelIs1Condition():
127
- return track[indexTrack.leafBelow, 0] == 1
128
-
129
- def leafConnecteeInitialization():
130
- my[indexMy.leafConnectee] = connectionGraph[my[indexMy.dimension1ndex], my[indexMy.leaf1ndex], my[indexMy.leaf1ndex]]
131
-
132
- def leafConnecteeUpdate():
133
- my[indexMy.leafConnectee] = connectionGraph[my[indexMy.dimension1ndex], my[indexMy.leaf1ndex], track[indexTrack.leafBelow, my[indexMy.leafConnectee]]]
134
-
135
- def loopingLeavesConnectedToActiveLeaf():
136
- return my[indexMy.leafConnectee] != my[indexMy.leaf1ndex]
137
-
138
- def loopingTheDimensions():
139
- return my[indexMy.dimension1ndex] <= the[indexThe.dimensionsTotal]
140
-
141
- def loopingToActiveGapCeiling():
142
- return my[indexMy.indexMiniGap] < my[indexMy.gap1ndexCeiling]
143
-
144
- def placeLeaf():
145
- my[indexMy.gap1ndex] -= 1
146
- track[indexTrack.leafAbove, my[indexMy.leaf1ndex]] = gapsWhere[my[indexMy.gap1ndex]]
147
- track[indexTrack.leafBelow, my[indexMy.leaf1ndex]] = track[indexTrack.leafBelow, track[indexTrack.leafAbove, my[indexMy.leaf1ndex]]]
148
- track[indexTrack.leafBelow, track[indexTrack.leafAbove, my[indexMy.leaf1ndex]]] = my[indexMy.leaf1ndex]
149
- track[indexTrack.leafAbove, track[indexTrack.leafBelow, my[indexMy.leaf1ndex]]] = my[indexMy.leaf1ndex]
150
- track[indexTrack.gapRangeStart, my[indexMy.leaf1ndex]] = my[indexMy.gap1ndex]
151
- my[indexMy.leaf1ndex] += 1
152
-
153
- def placeLeafCondition():
154
- return my[indexMy.leaf1ndex] > 0
155
-
156
- def taskIndexCondition():
157
- return my[indexMy.leafConnectee] % the[indexThe.taskDivisions] == my[indexMy.taskIndex]
158
-
159
- def thereAreComputationDivisionsYouMightSkip():
160
- if computationDivisionsCondition():
161
- return True
162
- if activeLeafNotEqualToTaskDivisionsCondition():
163
- return True
164
- if taskIndexCondition():
165
- return True
166
- return False
167
-
168
- stateUniversal = outfitFoldings(listDimensions, computationDivisions=computationDivisions, CPUlimit=CPUlimit)
169
- connectionGraph: Final[numpy.ndarray] = stateUniversal['connectionGraph']
170
- foldsSubTotals = stateUniversal['foldsSubTotals']
171
- gapsWhere = stateUniversal['gapsWhere']
172
- my = stateUniversal['my']
173
- the: Final[numpy.ndarray] = stateUniversal['the']
174
- track = stateUniversal['track']
175
-
176
- if the[indexThe.taskDivisions] == int(False):
177
- doWhile()
178
- else:
179
- stateUniversal['my'] = my.copy()
180
- stateUniversal['gapsWhere'] = gapsWhere.copy()
181
- stateUniversal['track'] = track.copy()
182
- for indexSherpa in range(the[indexThe.taskDivisions]):
183
- my = stateUniversal['my'].copy()
184
- my[indexMy.taskIndex] = indexSherpa
185
- gapsWhere = stateUniversal['gapsWhere'].copy()
186
- track = stateUniversal['track'].copy()
187
- doWhile()
188
-
189
- return numpy.sum(foldsSubTotals).item()
190
-
191
- @enum.verify(enum.CONTINUOUS, enum.UNIQUE) if sys.version_info >= (3, 11) else lambda x: x
192
- class EnumIndices(enum.IntEnum):
193
- """Base class for index enums."""
194
- @staticmethod
195
- def _generate_next_value_(name, start, count, last_values):
196
- """0-indexed."""
197
- return count
198
-
199
- def __index__(self) -> int:
200
- """Adapt enum to the ultra-rare event of indexing a NumPy 'ndarray', which is not the
201
- same as `array.array`. See NumPy.org; I think it will be very popular someday."""
202
- return self
203
-
204
- class indexMy(EnumIndices):
205
- """Indices for dynamic values."""
206
- dimension1ndex = enum.auto()
207
- dimensionsUnconstrained = enum.auto()
208
- gap1ndex = enum.auto()
209
- gap1ndexCeiling = enum.auto()
210
- indexLeaf = enum.auto()
211
- indexMiniGap = enum.auto()
212
- leaf1ndex = enum.auto()
213
- leafConnectee = enum.auto()
214
- taskIndex = enum.auto()
215
-
216
- class indexThe(EnumIndices):
217
- """Indices for static values."""
218
- dimensionsTotal = enum.auto()
219
- leavesTotal = enum.auto()
220
- taskDivisions = enum.auto()
221
-
222
- class indexTrack(EnumIndices):
223
- """Indices for state tracking array."""
224
- leafAbove = enum.auto()
225
- leafBelow = enum.auto()
226
- countDimensionsGapped = enum.auto()
227
- gapRangeStart = enum.auto()
228
-
229
- class computationState(TypedDict):
230
- connectionGraph: NDArray[integer[Any]]
231
- foldsSubTotals: NDArray[integer[Any]]
232
- mapShape: Tuple[int, ...]
233
- my: NDArray[integer[Any]]
234
- gapsWhere: NDArray[integer[Any]]
235
- the: NDArray[integer[Any]]
236
- track: NDArray[integer[Any]]
237
-
238
- dtypeLarge = numpy.int64
239
- dtypeMedium = dtypeLarge
240
-
241
- def getLeavesTotal(listDimensions: Sequence[int]) -> int:
242
- """
243
- How many leaves are in the map.
244
-
245
- Parameters:
246
- listDimensions: A list of integers representing dimensions.
247
-
248
- Returns:
249
- productDimensions: The product of all positive integer dimensions.
250
- """
251
- listNonNegative = parseDimensions(listDimensions, 'listDimensions')
252
- listPositive = [dimension for dimension in listNonNegative if dimension > 0]
253
-
254
- if not listPositive:
255
- return 0
256
- else:
257
- productDimensions = 1
258
- for dimension in listPositive:
259
- if dimension > sys.maxsize // productDimensions:
260
- raise OverflowError(f"I received {dimension=} in {listDimensions=}, but the product of the dimensions exceeds the maximum size of an integer on this system.")
261
- productDimensions *= dimension
262
-
263
- return productDimensions
264
-
265
- def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrencyLimit: int, CPUlimit: Optional[Union[bool, float, int]], listDimensions: Sequence[int]):
266
- if not computationDivisions:
267
- return 0
268
- else:
269
- leavesTotal = getLeavesTotal(listDimensions)
270
- taskDivisions = 0
271
- if isinstance(computationDivisions, int):
272
- taskDivisions = computationDivisions
273
- elif isinstance(computationDivisions, str):
274
- computationDivisions = computationDivisions.lower()
275
- if computationDivisions == "maximum":
276
- taskDivisions = leavesTotal
277
- elif computationDivisions == "cpu":
278
- taskDivisions = min(concurrencyLimit, leavesTotal)
279
- else:
280
- raise ValueError("Not my problem.")
281
-
282
- if taskDivisions > leavesTotal:
283
- raise ValueError("What are you doing?")
284
-
285
- return taskDivisions
286
-
287
- def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optional[Type]) -> NDArray[integer[Any]]:
288
- datatype = keywordArguments.get('datatype', dtypeMedium)
289
- mapShape = validateListDimensions(listDimensions)
290
- leavesTotal = getLeavesTotal(mapShape)
291
- arrayDimensions = numpy.array(mapShape, dtype=datatype)
292
- dimensionsTotal = len(arrayDimensions)
293
-
294
- cumulativeProduct = numpy.multiply.accumulate([1] + mapShape, dtype=datatype)
295
- coordinateSystem = numpy.zeros((dimensionsTotal + 1, leavesTotal + 1), dtype=datatype)
296
- for dimension1ndex in range(1, dimensionsTotal + 1):
297
- for leaf1ndex in range(1, leavesTotal + 1):
298
- coordinateSystem[dimension1ndex, leaf1ndex] = ( ((leaf1ndex - 1) // cumulativeProduct[dimension1ndex - 1]) % arrayDimensions[dimension1ndex - 1] + 1 )
299
-
300
- connectionGraph = numpy.zeros((dimensionsTotal + 1, leavesTotal + 1, leavesTotal + 1), dtype=datatype)
301
- for dimension1ndex in range(1, dimensionsTotal + 1):
302
- for activeLeaf1ndex in range(1, leavesTotal + 1):
303
- for connectee1ndex in range(1, activeLeaf1ndex + 1):
304
- isFirstCoord = coordinateSystem[dimension1ndex, connectee1ndex] == 1
305
- isLastCoord = coordinateSystem[dimension1ndex, connectee1ndex] == arrayDimensions[dimension1ndex - 1]
306
- exceedsActive = connectee1ndex + cumulativeProduct[dimension1ndex - 1] > activeLeaf1ndex
307
- isEvenParity = (coordinateSystem[dimension1ndex, activeLeaf1ndex] & 1) == (coordinateSystem[dimension1ndex, connectee1ndex] & 1)
308
-
309
- if (isEvenParity and isFirstCoord) or (not isEvenParity and (isLastCoord or exceedsActive)):
310
- connectionGraph[dimension1ndex, activeLeaf1ndex, connectee1ndex] = connectee1ndex
311
- elif isEvenParity and not isFirstCoord:
312
- connectionGraph[dimension1ndex, activeLeaf1ndex, connectee1ndex] = connectee1ndex - cumulativeProduct[dimension1ndex - 1]
313
- elif not isEvenParity and not (isLastCoord or exceedsActive):
314
- connectionGraph[dimension1ndex, activeLeaf1ndex, connectee1ndex] = connectee1ndex + cumulativeProduct[dimension1ndex - 1]
315
- else:
316
- connectionGraph[dimension1ndex, activeLeaf1ndex, connectee1ndex] = connectee1ndex
317
- return connectionGraph
318
-
319
- def makeDataContainer(shape, datatype: Optional[Type] = None):
320
- if datatype is None:
321
- datatype = dtypeMedium
322
- return numpy.zeros(shape, dtype=datatype)
323
-
324
- def outfitFoldings(listDimensions: Sequence[int], computationDivisions: Optional[Union[int, str]] = None, CPUlimit: Optional[Union[bool, float, int]] = None, **keywordArguments: Optional[Type]) -> computationState:
325
- datatypeMedium = keywordArguments.get('datatypeMedium', dtypeMedium)
326
- datatypeLarge = keywordArguments.get('datatypeLarge', dtypeLarge)
327
-
328
- the = makeDataContainer(len(indexThe), datatypeMedium)
329
-
330
- mapShape = tuple(sorted(validateListDimensions(listDimensions)))
331
- the[indexThe.leavesTotal] = getLeavesTotal(mapShape)
332
- the[indexThe.dimensionsTotal] = len(mapShape)
333
- concurrencyLimit = setCPUlimit(CPUlimit)
334
- the[indexThe.taskDivisions] = getTaskDivisions(computationDivisions, concurrencyLimit, CPUlimit, listDimensions)
335
-
336
- stateInitialized = computationState(
337
- connectionGraph = makeConnectionGraph(mapShape, datatype=datatypeMedium),
338
- foldsSubTotals = makeDataContainer(the[indexThe.leavesTotal], datatypeLarge),
339
- mapShape = mapShape,
340
- my = makeDataContainer(len(indexMy), datatypeLarge),
341
- gapsWhere = makeDataContainer(int(the[indexThe.leavesTotal]) * int(the[indexThe.leavesTotal]) + 1, datatypeMedium),
342
- the = the,
343
- track = makeDataContainer((len(indexTrack), the[indexThe.leavesTotal] + 1), datatypeLarge)
344
- )
345
-
346
- stateInitialized['my'][indexMy.leaf1ndex] = 1
347
- return stateInitialized
348
-
349
- def parseDimensions(dimensions: Sequence[int], parameterName: str = 'unnamed parameter') -> List[int]:
350
- # listValidated = intInnit(dimensions, parameterName)
351
- listNOTValidated = dimensions if isinstance(dimensions, (list, tuple)) else list(dimensions)
352
- listNonNegative = []
353
- for dimension in listNOTValidated:
354
- if dimension < 0:
355
- raise ValueError(f"Dimension {dimension} must be non-negative")
356
- listNonNegative.append(dimension)
357
- if not listNonNegative:
358
- raise ValueError("At least one dimension must be non-negative")
359
- return listNonNegative
360
-
361
- def setCPUlimit(CPUlimit: Union[bool, float, int, None]) -> int:
362
- # if not (CPUlimit is None or isinstance(CPUlimit, (bool, int, float))):
363
- # CPUlimit = oopsieKwargsie(CPUlimit)
364
- # concurrencyLimit = defineConcurrencyLimit(CPUlimit)
365
- # numba.set_num_threads(concurrencyLimit)
366
- concurrencyLimitHARDCODED = 1
367
- concurrencyLimit = concurrencyLimitHARDCODED
368
- return concurrencyLimit
369
-
370
- def validateListDimensions(listDimensions: Sequence[int]) -> List[int]:
371
- if not listDimensions:
372
- raise ValueError(f"listDimensions is a required parameter.")
373
- listNonNegative = parseDimensions(listDimensions, 'listDimensions')
374
- dimensionsValid = [dimension for dimension in listNonNegative if dimension > 0]
375
- if len(dimensionsValid) < 2:
376
- raise NotImplementedError(f"This function requires listDimensions, {listDimensions}, to have at least two dimensions greater than 0. You may want to look at https://oeis.org/.")
377
- return sorted(dimensionsValid)