mapFolding 0.4.2__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 +3 -2
- mapFolding/basecamp.py +12 -14
- mapFolding/beDRY.py +81 -58
- mapFolding/oeis.py +35 -33
- mapFolding/someAssemblyRequired/makeJob.py +8 -7
- mapFolding/someAssemblyRequired/synthesizeModuleJAX.py +1 -3
- mapFolding/someAssemblyRequired/synthesizeNumba.py +57 -60
- mapFolding/someAssemblyRequired/synthesizeNumbaGeneralized.py +102 -30
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +18 -36
- mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +77 -31
- mapFolding/syntheticModules/numbaCount.py +158 -0
- mapFolding/syntheticModules/numba_doTheNeedful.py +5 -12
- mapFolding/theDao.py +105 -105
- mapFolding/theSSOT.py +80 -205
- mapFolding/theSSOTdatatypes.py +166 -0
- {mapFolding-0.4.2.dist-info → mapFolding-0.5.0.dist-info}/METADATA +2 -1
- mapFolding-0.5.0.dist-info/RECORD +39 -0
- tests/conftest.py +84 -26
- tests/test_computations.py +29 -66
- tests/test_oeis.py +8 -12
- tests/test_other.py +11 -7
- tests/test_tasks.py +5 -5
- mapFolding/syntheticModules/numba_countInitialize.py +0 -52
- mapFolding/syntheticModules/numba_countParallel.py +0 -65
- mapFolding/syntheticModules/numba_countSequential.py +0 -67
- mapFolding/theSSOTnumba.py +0 -125
- mapFolding-0.4.2.dist-info/RECORD +0 -42
- tests/test_types.py +0 -5
- {mapFolding-0.4.2.dist-info → mapFolding-0.5.0.dist-info}/LICENSE +0 -0
- {mapFolding-0.4.2.dist-info → mapFolding-0.5.0.dist-info}/WHEEL +0 -0
- {mapFolding-0.4.2.dist-info → mapFolding-0.5.0.dist-info}/entry_points.txt +0 -0
- {mapFolding-0.4.2.dist-info → mapFolding-0.5.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from typing import Any, cast, Final, TYPE_CHECKING
|
|
3
|
+
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
|
|
16
|
+
|
|
17
|
+
class EnumIndices(enum.IntEnum):
|
|
18
|
+
@staticmethod
|
|
19
|
+
def _generate_next_value_(name: str, start: int, count: int, last_values: list[Any]) -> int:
|
|
20
|
+
"""0-indexed."""
|
|
21
|
+
return count
|
|
22
|
+
|
|
23
|
+
def __index__(self) -> int:
|
|
24
|
+
"""Adapt enum to the ultra-rare event of indexing a NumPy 'ndarray', which is not the
|
|
25
|
+
same as `array.array`. See NumPy.org; I think it will be very popular someday."""
|
|
26
|
+
return self.value
|
|
27
|
+
|
|
28
|
+
class indexMy(EnumIndices):
|
|
29
|
+
"""Indices for scalar values."""
|
|
30
|
+
dimensionsTotal = enum.auto()
|
|
31
|
+
dimensionsUnconstrained = enum.auto()
|
|
32
|
+
gap1ndex = enum.auto()
|
|
33
|
+
gap1ndexCeiling = enum.auto()
|
|
34
|
+
indexDimension = enum.auto()
|
|
35
|
+
indexLeaf = enum.auto()
|
|
36
|
+
indexMiniGap = enum.auto()
|
|
37
|
+
leaf1ndex = enum.auto()
|
|
38
|
+
leafConnectee = enum.auto()
|
|
39
|
+
taskDivisions = enum.auto()
|
|
40
|
+
taskIndex = enum.auto()
|
|
41
|
+
|
|
42
|
+
class indexTrack(EnumIndices):
|
|
43
|
+
"""Indices for state tracking array."""
|
|
44
|
+
leafAbove = enum.auto()
|
|
45
|
+
leafBelow = enum.auto()
|
|
46
|
+
countDimensionsGapped = enum.auto()
|
|
47
|
+
gapRangeStart = enum.auto()
|
|
48
|
+
|
|
49
|
+
_datatypeDefault: Final[dict[str, str]] = {
|
|
50
|
+
'elephino': 'uint16',
|
|
51
|
+
'foldsTotal': 'int64',
|
|
52
|
+
'leavesTotal': 'uint16',
|
|
53
|
+
}
|
|
54
|
+
_datatypeModule = ''
|
|
55
|
+
_datatypeModuleDEFAULT: Final[str] = 'numpy'
|
|
56
|
+
|
|
57
|
+
_datatype: dict[str, str] = defaultdict(str)
|
|
58
|
+
|
|
59
|
+
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:
|
|
64
|
+
pass
|
|
65
|
+
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]
|
|
68
|
+
|
|
69
|
+
def setDatatypeModule(datatypeModule: str, sourGrapes: bool | None = False) -> str:
|
|
70
|
+
global _datatypeModule
|
|
71
|
+
if not _datatypeModule:
|
|
72
|
+
_datatypeModule = datatypeModule
|
|
73
|
+
elif _datatypeModule == datatypeModule:
|
|
74
|
+
pass
|
|
75
|
+
elif sourGrapes:
|
|
76
|
+
raise Exception(f"Datatype module is '{_datatypeModule}' not '{datatypeModule}', so you can take your ball and go home.")
|
|
77
|
+
return _datatypeModule
|
|
78
|
+
|
|
79
|
+
def setDatatypeElephino(datatype: str, sourGrapes: bool | None = False) -> str:
|
|
80
|
+
return reportDatatypeLimit('elephino', datatype, sourGrapes)
|
|
81
|
+
|
|
82
|
+
def setDatatypeFoldsTotal(datatype: str, sourGrapes: bool | None = False) -> str:
|
|
83
|
+
return reportDatatypeLimit('foldsTotal', datatype, sourGrapes)
|
|
84
|
+
|
|
85
|
+
def setDatatypeLeavesTotal(datatype: str, sourGrapes: bool | None = False) -> str:
|
|
86
|
+
return reportDatatypeLimit('leavesTotal', datatype, sourGrapes)
|
|
87
|
+
|
|
88
|
+
def _get_datatype(identifier: str) -> str:
|
|
89
|
+
global _datatype
|
|
90
|
+
if not _datatype[identifier]:
|
|
91
|
+
if identifier in indexMy._member_names_:
|
|
92
|
+
_datatype[identifier] = _datatypeDefault.get(identifier) or _get_datatype('elephino')
|
|
93
|
+
elif identifier in indexTrack._member_names_:
|
|
94
|
+
_datatype[identifier] = _datatypeDefault.get(identifier) or _get_datatype('elephino')
|
|
95
|
+
else:
|
|
96
|
+
_datatype[identifier] = _datatypeDefault.get(identifier) or _get_datatype('foldsTotal')
|
|
97
|
+
return _datatype[identifier]
|
|
98
|
+
|
|
99
|
+
def getDatatypeModule() -> str:
|
|
100
|
+
global _datatypeModule
|
|
101
|
+
if not _datatypeModule:
|
|
102
|
+
_datatypeModule = _datatypeModuleDEFAULT
|
|
103
|
+
return _datatypeModule
|
|
104
|
+
|
|
105
|
+
def setInStone(identifier: str) -> type[Any]:
|
|
106
|
+
datatypeModule = getDatatypeModule()
|
|
107
|
+
datatypeStr = _get_datatype(identifier)
|
|
108
|
+
return cast(type[Any], getattr(eval(datatypeModule), datatypeStr))
|
|
109
|
+
|
|
110
|
+
def hackSSOTdtype(identifier: str) -> type[Any]:
|
|
111
|
+
_hackSSOTdtype={
|
|
112
|
+
'connectionGraph': 'dtypeLeavesTotal',
|
|
113
|
+
'dtypeElephino': 'dtypeElephino',
|
|
114
|
+
'dtypeFoldsTotal': 'dtypeFoldsTotal',
|
|
115
|
+
'dtypeLeavesTotal': 'dtypeLeavesTotal',
|
|
116
|
+
'foldGroups': 'dtypeFoldsTotal',
|
|
117
|
+
'gapsWhere': 'dtypeLeavesTotal',
|
|
118
|
+
'mapShape': 'dtypeLeavesTotal',
|
|
119
|
+
'my': 'dtypeElephino',
|
|
120
|
+
'track': 'dtypeElephino',
|
|
121
|
+
}
|
|
122
|
+
RubeGoldBerg = _hackSSOTdtype[identifier]
|
|
123
|
+
if RubeGoldBerg == 'dtypeElephino':
|
|
124
|
+
return setInStone('elephino')
|
|
125
|
+
elif RubeGoldBerg == 'dtypeFoldsTotal':
|
|
126
|
+
return setInStone('foldsTotal')
|
|
127
|
+
elif RubeGoldBerg == 'dtypeLeavesTotal':
|
|
128
|
+
return setInStone('leavesTotal')
|
|
129
|
+
raise Exception("Dude, you forgot to set a value in `hackSSOTdtype`.")
|
|
130
|
+
|
|
131
|
+
def hackSSOTdatatype(identifier: str) -> str:
|
|
132
|
+
_hackSSOTdatatype={
|
|
133
|
+
'connectionGraph': 'datatypeLeavesTotal',
|
|
134
|
+
'countDimensionsGapped': 'datatypeLeavesTotal',
|
|
135
|
+
'datatypeElephino': 'datatypeElephino',
|
|
136
|
+
'datatypeFoldsTotal': 'datatypeFoldsTotal',
|
|
137
|
+
'datatypeLeavesTotal': 'datatypeLeavesTotal',
|
|
138
|
+
'dimensionsTotal': 'datatypeLeavesTotal',
|
|
139
|
+
'dimensionsUnconstrained': 'datatypeLeavesTotal',
|
|
140
|
+
'foldGroups': 'datatypeFoldsTotal',
|
|
141
|
+
'gap1ndex': 'datatypeLeavesTotal',
|
|
142
|
+
'gap1ndexCeiling': 'datatypeElephino',
|
|
143
|
+
'gapRangeStart': 'datatypeElephino',
|
|
144
|
+
'gapsWhere': 'datatypeLeavesTotal',
|
|
145
|
+
'groupsOfFolds': 'datatypeFoldsTotal',
|
|
146
|
+
'indexDimension': 'datatypeLeavesTotal',
|
|
147
|
+
'indexLeaf': 'datatypeLeavesTotal',
|
|
148
|
+
'indexMiniGap': 'datatypeElephino',
|
|
149
|
+
'leaf1ndex': 'datatypeLeavesTotal',
|
|
150
|
+
'leafAbove': 'datatypeLeavesTotal',
|
|
151
|
+
'leafBelow': 'datatypeLeavesTotal',
|
|
152
|
+
'leafConnectee': 'datatypeLeavesTotal',
|
|
153
|
+
'mapShape': 'datatypeLeavesTotal',
|
|
154
|
+
'my': 'datatypeElephino',
|
|
155
|
+
'taskDivisions': 'datatypeLeavesTotal',
|
|
156
|
+
'taskIndex': 'datatypeLeavesTotal',
|
|
157
|
+
'track': 'datatypeElephino',
|
|
158
|
+
}
|
|
159
|
+
RubeGoldBerg = _hackSSOTdatatype[identifier]
|
|
160
|
+
if RubeGoldBerg == 'datatypeElephino':
|
|
161
|
+
return _get_datatype('elephino')
|
|
162
|
+
elif RubeGoldBerg == 'datatypeFoldsTotal':
|
|
163
|
+
return _get_datatype('foldsTotal')
|
|
164
|
+
elif RubeGoldBerg == 'datatypeLeavesTotal':
|
|
165
|
+
return _get_datatype('leavesTotal')
|
|
166
|
+
raise Exception("Dude, you forgot to set a value in `hackSSOTdatatype`.")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: mapFolding
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.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
|
|
@@ -39,6 +39,7 @@ Requires-Dist: pytest-env; extra == "testing"
|
|
|
39
39
|
Requires-Dist: pytest-xdist; extra == "testing"
|
|
40
40
|
Requires-Dist: pytest; extra == "testing"
|
|
41
41
|
Requires-Dist: python_minifier; extra == "testing"
|
|
42
|
+
Requires-Dist: pyupgrade; extra == "testing"
|
|
42
43
|
Requires-Dist: updateCitation; extra == "testing"
|
|
43
44
|
|
|
44
45
|
# mapFolding: Algorithms for enumerating distinct map/stamp folding patterns 🗺️
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
mapFolding/__init__.py,sha256=AQA9ypsiV8cDUCvWdM64kFhO9LaKFvAuSEw03jiI7ao,1358
|
|
2
|
+
mapFolding/basecamp.py,sha256=f6Z_KJ1iGV5a3YBDsxS0m7RojYABYlR4kb-dvKW8CAs,3703
|
|
3
|
+
mapFolding/beDRY.py,sha256=ViKLdomPYXD4o86BZwWgT0qPQNKqdtAZ74YoKC9Kgjg,16793
|
|
4
|
+
mapFolding/oeis.py,sha256=Jak0vOfS_gGTXH6xBI7NTUp4Clluvj792E5HNFDDt6U,11149
|
|
5
|
+
mapFolding/theDao.py,sha256=64dlLhrdtNbZAXMbqcZhGAE0ZwgqYRt8nT1on46J-cc,12611
|
|
6
|
+
mapFolding/theSSOT.py,sha256=tcEbPQLmvRWfIyuR0NpeY0Vm6l6L1dUJFyFZjbDo2Gs,6026
|
|
7
|
+
mapFolding/theSSOTdatatypes.py,sha256=OoB9hVfedQejRPxkP-dgLMHS-vqw_0imBvMLBuyLlg0,5829
|
|
8
|
+
mapFolding/reference/flattened.py,sha256=S6D9wiFTlbeoetEqaMLOcA-R22BHOzjqPRujffNxxUM,14875
|
|
9
|
+
mapFolding/reference/hunterNumba.py,sha256=jDS0ORHkIhcJ1rzA5hT49sZHKf3rgJOoGesUCcbKFFY,6054
|
|
10
|
+
mapFolding/reference/irvineJavaPort.py,sha256=7GvBU0tnS6wpFgkYad3465do9jBQW-2bYvbCYyABPHM,3341
|
|
11
|
+
mapFolding/reference/jax.py,sha256=7ji9YWia6Kof0cjcNdiS1GG1rMbC5SBjcyVr_07AeUk,13845
|
|
12
|
+
mapFolding/reference/lunnan.py,sha256=iAbJELfW6RKNMdPcBY9b6rGQ-z1zoRf-1XCurCRMOo8,3951
|
|
13
|
+
mapFolding/reference/lunnanNumpy.py,sha256=rwVP3WIDXimpAuaxhRIuBYU56nVDTKlfGiclw_FkgUU,3765
|
|
14
|
+
mapFolding/reference/lunnanWhile.py,sha256=uRrMT23jTJvoQDlD_FzeIQe_pfMXJG6_bRvs7uhC8z0,3271
|
|
15
|
+
mapFolding/reference/rotatedEntryPoint.py,sha256=USZY3n3zwhSE68ATscUuN66t1qShuEbMI790Gz9JFTw,9352
|
|
16
|
+
mapFolding/reference/total_countPlus1vsPlusN.py,sha256=wpgay-uqPOBd64Z4Pg6tg40j7-4pzWHGMM6v0bnmjhE,6288
|
|
17
|
+
mapFolding/someAssemblyRequired/__init__.py,sha256=wtec_hIz-AKz0_hGdXsWnCKTcCxdMV9-WK6SiIriAeU,396
|
|
18
|
+
mapFolding/someAssemblyRequired/getLLVMforNoReason.py,sha256=nX8tghZClYt7zJd6RpZBXhE_h-CGRHOS17biqiEdf-o,855
|
|
19
|
+
mapFolding/someAssemblyRequired/makeJob.py,sha256=neb_sFvYMx6dlZxKVmwGjKNrEsK0XaHyE1AJD9S9-nA,2408
|
|
20
|
+
mapFolding/someAssemblyRequired/synthesizeModuleJAX.py,sha256=21Wos8ynlly577EUDqWbbNc9R1qc19r5ysWaz86bCvg,1210
|
|
21
|
+
mapFolding/someAssemblyRequired/synthesizeNumba.py,sha256=6bVbZMnszgz5J81KF-_LQyi6Sw9SwUBD-k8u-mFKADo,16940
|
|
22
|
+
mapFolding/someAssemblyRequired/synthesizeNumbaGeneralized.py,sha256=vu6oYqoZGLNzbd3i8H9B2cEtL9bM1dhmbIpcJeF1NKE,16666
|
|
23
|
+
mapFolding/someAssemblyRequired/synthesizeNumbaJob.py,sha256=YRFLuEWcE7Fz4D7vgEwwof5xsrTRY2tIFl69GrFYke8,9050
|
|
24
|
+
mapFolding/someAssemblyRequired/synthesizeNumbaModules.py,sha256=3oUCPU4nnfynhEqSFqIC8BZ1x0lXK0_0_o-hIx8MCPw,5504
|
|
25
|
+
mapFolding/syntheticModules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
|
+
mapFolding/syntheticModules/numbaCount.py,sha256=447sbrCINu6wMfpJENguUyAxd6Vej3w98WzyVelz8eg,12900
|
|
27
|
+
mapFolding/syntheticModules/numba_doTheNeedful.py,sha256=8utsCDlcIvOlrjGugaD6DgecoDICIo5_BKhkYtwv-74,1150
|
|
28
|
+
tests/__init__.py,sha256=eg9smg-6VblOr0kisM40CpGnuDtU2JgEEWGDTFVOlW8,57
|
|
29
|
+
tests/conftest.py,sha256=rorOpl3Q1ksGQB-8UyLdUDkYBl9O3M19CvOKrnnzr-4,12296
|
|
30
|
+
tests/test_computations.py,sha256=67dcIj94VM69m912cY1L8HuqnXWl9NFVNhjx3zRNjvA,2831
|
|
31
|
+
tests/test_oeis.py,sha256=4EPJrd359kO019e6whDnERU-gnvMeOeUsHFgor7bA1Y,4686
|
|
32
|
+
tests/test_other.py,sha256=x34TGMWY7YmVtTduC_hlmISMVYemcx_z8_UcMcaNyIk,8375
|
|
33
|
+
tests/test_tasks.py,sha256=J8CET4Z-SYZGdy1-nj-pvVqMl6ujTXJR5ZCYkU4gbUc,2389
|
|
34
|
+
mapFolding-0.5.0.dist-info/LICENSE,sha256=NxH5Y8BdC-gNU-WSMwim3uMbID2iNDXJz7fHtuTdXhk,19346
|
|
35
|
+
mapFolding-0.5.0.dist-info/METADATA,sha256=rLrgAmUzYgyLQV_mh749XXhiD1RLrol0iHXZpA_10DU,7678
|
|
36
|
+
mapFolding-0.5.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
37
|
+
mapFolding-0.5.0.dist-info/entry_points.txt,sha256=F3OUeZR1XDTpoH7k3wXuRb3KF_kXTTeYhu5AGK1SiOQ,146
|
|
38
|
+
mapFolding-0.5.0.dist-info/top_level.txt,sha256=1gP2vFaqPwHujGwb3UjtMlLEGN-943VSYFR7V4gDqW8,17
|
|
39
|
+
mapFolding-0.5.0.dist-info/RECORD,,
|
tests/conftest.py
CHANGED
|
@@ -2,16 +2,45 @@
|
|
|
2
2
|
|
|
3
3
|
# TODO learn how to run tests and coverage analysis without `env = ["NUMBA_DISABLE_JIT=1"]`
|
|
4
4
|
|
|
5
|
-
from
|
|
6
|
-
from mapFolding import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
from collections.abc import Callable, Generator, Sequence
|
|
6
|
+
from mapFolding import (
|
|
7
|
+
getAlgorithmDispatcher,
|
|
8
|
+
getDispatcherCallable,
|
|
9
|
+
countFolds,
|
|
10
|
+
getPathFilenameFoldsTotal,
|
|
11
|
+
oeisIDfor_n,
|
|
12
|
+
saveFoldsTotal,
|
|
13
|
+
hackSSOTdtype,
|
|
14
|
+
clearOEIScache,
|
|
15
|
+
getOEISids,
|
|
16
|
+
)
|
|
17
|
+
from mapFolding import basecamp
|
|
18
|
+
from mapFolding.beDRY import (
|
|
19
|
+
getLeavesTotal,
|
|
20
|
+
validateListDimensions,
|
|
21
|
+
makeDataContainer,
|
|
22
|
+
parseDimensions,
|
|
23
|
+
setCPUlimit,
|
|
24
|
+
makeConnectionGraph,
|
|
25
|
+
getTaskDivisions,
|
|
26
|
+
)
|
|
27
|
+
from mapFolding.oeis import (
|
|
28
|
+
oeisIDsImplemented,
|
|
29
|
+
settingsOEIS,
|
|
30
|
+
validateOEISid,
|
|
31
|
+
getOEISidValues,
|
|
32
|
+
OEIS_for_n,
|
|
33
|
+
)
|
|
34
|
+
from mapFolding.someAssemblyRequired import (
|
|
35
|
+
makeFlowNumbaOptimized,
|
|
36
|
+
youOughtaKnow,
|
|
37
|
+
writeJobNumba,
|
|
38
|
+
)
|
|
39
|
+
from pathlib import Path
|
|
11
40
|
from types import ModuleType
|
|
12
|
-
from typing import Any,
|
|
41
|
+
from typing import Any, ContextManager, Literal, NoReturn, Final
|
|
13
42
|
from Z0Z_tools.pytestForYourUse import PytestFor_defineConcurrencyLimit, PytestFor_intInnit, PytestFor_oopsieKwargsie
|
|
14
|
-
import
|
|
43
|
+
import importlib.util
|
|
15
44
|
import pytest
|
|
16
45
|
import random
|
|
17
46
|
import shutil
|
|
@@ -19,14 +48,14 @@ import unittest.mock
|
|
|
19
48
|
import uuid
|
|
20
49
|
|
|
21
50
|
# SSOT for test data paths and filenames
|
|
22
|
-
pathDataSamples =
|
|
51
|
+
pathDataSamples = Path("tests/dataSamples")
|
|
23
52
|
# NOTE `tmp` is not a diminutive form of temporary: it signals a technical term. And "temp" is strongly disfavored.
|
|
24
|
-
pathTmpRoot = pathDataSamples / "tmp"
|
|
53
|
+
pathTmpRoot: Path = pathDataSamples / "tmp"
|
|
25
54
|
|
|
26
55
|
# The registrar maintains the register of temp files
|
|
27
|
-
registerOfTemporaryFilesystemObjects:
|
|
56
|
+
registerOfTemporaryFilesystemObjects: set[Path] = set()
|
|
28
57
|
|
|
29
|
-
def registrarRecordsTmpObject(path:
|
|
58
|
+
def registrarRecordsTmpObject(path: Path) -> None:
|
|
30
59
|
"""The registrar adds a tmp file to the register."""
|
|
31
60
|
registerOfTemporaryFilesystemObjects.add(path)
|
|
32
61
|
|
|
@@ -51,7 +80,7 @@ def setupTeardownTmpObjects() -> Generator[None, None, None]:
|
|
|
51
80
|
registrarDeletesTmpObjects()
|
|
52
81
|
|
|
53
82
|
@pytest.fixture
|
|
54
|
-
def pathTmpTesting(request: pytest.FixtureRequest) ->
|
|
83
|
+
def pathTmpTesting(request: pytest.FixtureRequest) -> Path:
|
|
55
84
|
# "Z0Z_" ensures the directory name does not start with a number, which would make it an invalid Python identifier
|
|
56
85
|
pathTmp = pathTmpRoot / ("Z0Z_" + str(uuid.uuid4().hex))
|
|
57
86
|
pathTmp.mkdir(parents=True, exist_ok=False)
|
|
@@ -60,7 +89,7 @@ def pathTmpTesting(request: pytest.FixtureRequest) -> pathlib.Path:
|
|
|
60
89
|
return pathTmp
|
|
61
90
|
|
|
62
91
|
@pytest.fixture
|
|
63
|
-
def pathFilenameTmpTesting(request: pytest.FixtureRequest) ->
|
|
92
|
+
def pathFilenameTmpTesting(request: pytest.FixtureRequest) -> Path:
|
|
64
93
|
try:
|
|
65
94
|
extension = request.param
|
|
66
95
|
except AttributeError:
|
|
@@ -71,14 +100,14 @@ def pathFilenameTmpTesting(request: pytest.FixtureRequest) -> pathlib.Path:
|
|
|
71
100
|
subpath = "Z0Z_" + uuidHex[0:-8]
|
|
72
101
|
filenameStem = "Z0Z_" + uuidHex[-8:None]
|
|
73
102
|
|
|
74
|
-
pathFilenameTmp =
|
|
103
|
+
pathFilenameTmp = Path(pathTmpRoot, subpath, filenameStem + extension)
|
|
75
104
|
pathFilenameTmp.parent.mkdir(parents=True, exist_ok=False)
|
|
76
105
|
|
|
77
106
|
registrarRecordsTmpObject(pathFilenameTmp)
|
|
78
107
|
return pathFilenameTmp
|
|
79
108
|
|
|
80
109
|
@pytest.fixture
|
|
81
|
-
def pathCacheTesting(pathTmpTesting:
|
|
110
|
+
def pathCacheTesting(pathTmpTesting: Path) -> Generator[Path, Any, None]:
|
|
82
111
|
"""Temporarily replace the OEIS cache directory with a test directory."""
|
|
83
112
|
from mapFolding import oeis as there_must_be_a_better_way
|
|
84
113
|
pathCacheOriginal = there_must_be_a_better_way._pathCache
|
|
@@ -87,12 +116,12 @@ def pathCacheTesting(pathTmpTesting: pathlib.Path) -> Generator[pathlib.Path, An
|
|
|
87
116
|
there_must_be_a_better_way._pathCache = pathCacheOriginal
|
|
88
117
|
|
|
89
118
|
@pytest.fixture
|
|
90
|
-
def pathFilenameFoldsTotalTesting(pathTmpTesting:
|
|
119
|
+
def pathFilenameFoldsTotalTesting(pathTmpTesting: Path) -> Path:
|
|
91
120
|
return pathTmpTesting.joinpath("foldsTotalTest.txt")
|
|
92
121
|
|
|
93
|
-
def makeDictionaryFoldsTotalKnown() ->
|
|
122
|
+
def makeDictionaryFoldsTotalKnown() -> dict[tuple[int, ...], int]:
|
|
94
123
|
"""Returns a dictionary mapping dimension tuples to their known folding totals."""
|
|
95
|
-
dictionaryMapDimensionsToFoldsTotalKnown:
|
|
124
|
+
dictionaryMapDimensionsToFoldsTotalKnown: dict[tuple[int, ...], int] = {}
|
|
96
125
|
|
|
97
126
|
for settings in settingsOEIS.values():
|
|
98
127
|
sequence = settings['valuesKnown']
|
|
@@ -115,7 +144,7 @@ def setupWarningsAsErrors() -> Generator[None, Any, None]:
|
|
|
115
144
|
warnings.resetwarnings()
|
|
116
145
|
|
|
117
146
|
@pytest.fixture
|
|
118
|
-
def foldsTotalKnown() ->
|
|
147
|
+
def foldsTotalKnown() -> dict[tuple[int, ...], int]:
|
|
119
148
|
"""Returns a dictionary mapping dimension tuples to their known folding totals.
|
|
120
149
|
NOTE I am not convinced this is the best way to do this.
|
|
121
150
|
Advantage: I call `makeDictionaryFoldsTotalKnown()` from modules other than test modules.
|
|
@@ -124,7 +153,7 @@ def foldsTotalKnown() -> Dict[Tuple[int,...], int]:
|
|
|
124
153
|
return makeDictionaryFoldsTotalKnown()
|
|
125
154
|
|
|
126
155
|
@pytest.fixture
|
|
127
|
-
def listDimensionsTestCountFolds(oeisID: str) ->
|
|
156
|
+
def listDimensionsTestCountFolds(oeisID: str) -> list[int]:
|
|
128
157
|
"""For each `oeisID` from the `pytest.fixture`, returns `listDimensions` from `valuesTestValidation`
|
|
129
158
|
if `validateListDimensions` approves. Each `listDimensions` is suitable for testing counts."""
|
|
130
159
|
while True:
|
|
@@ -139,7 +168,7 @@ def listDimensionsTestCountFolds(oeisID: str) -> List[int]:
|
|
|
139
168
|
pass
|
|
140
169
|
|
|
141
170
|
@pytest.fixture
|
|
142
|
-
def listDimensionsTestFunctionality(oeisID_1random: str) ->
|
|
171
|
+
def listDimensionsTestFunctionality(oeisID_1random: str) -> list[int]:
|
|
143
172
|
"""To test functionality, get one `listDimensions` from `valuesTestValidation` if
|
|
144
173
|
`validateListDimensions` approves. The algorithm can count the folds of the returned
|
|
145
174
|
`listDimensions` in a short enough time suitable for testing."""
|
|
@@ -155,7 +184,7 @@ def listDimensionsTestFunctionality(oeisID_1random: str) -> List[int]:
|
|
|
155
184
|
pass
|
|
156
185
|
|
|
157
186
|
@pytest.fixture
|
|
158
|
-
def listDimensionsTestParallelization(oeisID: str) ->
|
|
187
|
+
def listDimensionsTestParallelization(oeisID: str) -> list[int]:
|
|
159
188
|
"""For each `oeisID` from the `pytest.fixture`, returns `listDimensions` from `valuesTestParallelization`"""
|
|
160
189
|
n = random.choice(settingsOEIS[oeisID]['valuesTestParallelization'])
|
|
161
190
|
return settingsOEIS[oeisID]['getMapShape'](n)
|
|
@@ -170,7 +199,7 @@ def mockBenchmarkTimer() -> Generator[unittest.mock.MagicMock | unittest.mock.As
|
|
|
170
199
|
@pytest.fixture
|
|
171
200
|
def mockFoldingFunction() -> Callable[..., Callable[..., None]]:
|
|
172
201
|
"""Creates a mock function that simulates _countFolds behavior."""
|
|
173
|
-
def make_mock(foldsValue: int, listDimensions:
|
|
202
|
+
def make_mock(foldsValue: int, listDimensions: list[int]) -> Callable[..., None]:
|
|
174
203
|
mock_array = makeDataContainer(2)
|
|
175
204
|
mock_array[0] = foldsValue
|
|
176
205
|
mock_array[-1] = getLeavesTotal(listDimensions)
|
|
@@ -225,6 +254,35 @@ def useAlgorithmSourceDispatcher(useThisDispatcher: Callable) -> Generator[None,
|
|
|
225
254
|
useThisDispatcher(getAlgorithmDispatcher())
|
|
226
255
|
yield
|
|
227
256
|
|
|
257
|
+
@pytest.fixture
|
|
258
|
+
def syntheticDispatcherFixture(useThisDispatcher):
|
|
259
|
+
listCallablesInlineHARDCODED: list[str] = ['countInitialize', 'countParallel', 'countSequential']
|
|
260
|
+
listCallablesInline = listCallablesInlineHARDCODED
|
|
261
|
+
callableDispatcher = True
|
|
262
|
+
algorithmSource = None
|
|
263
|
+
relativePathWrite = None
|
|
264
|
+
filenameModuleWrite = 'pytestCount.py'
|
|
265
|
+
formatFilenameWrite = "pytest_{callableTarget}.py"
|
|
266
|
+
listSynthesizedModules: list[youOughtaKnow] = makeFlowNumbaOptimized(listCallablesInline, callableDispatcher, algorithmSource, relativePathWrite, filenameModuleWrite, formatFilenameWrite)
|
|
267
|
+
dispatcherSynthetic = youOughtaKnow('','','')
|
|
268
|
+
for stuff in listSynthesizedModules:
|
|
269
|
+
registrarRecordsTmpObject(stuff.pathFilenameForMe)
|
|
270
|
+
if stuff.callableSynthesized not in listCallablesInline:
|
|
271
|
+
dispatcherSynthetic: youOughtaKnow = stuff
|
|
272
|
+
|
|
273
|
+
dispatcherSpec = importlib.util.spec_from_file_location(dispatcherSynthetic.callableSynthesized, dispatcherSynthetic.pathFilenameForMe)
|
|
274
|
+
if dispatcherSpec is None:
|
|
275
|
+
raise ImportError(f"{dispatcherSynthetic.pathFilenameForMe=}")
|
|
276
|
+
if dispatcherSpec.loader is None:
|
|
277
|
+
raise ImportError(f"Failed to get loader for module {dispatcherSynthetic.pathFilenameForMe}")
|
|
278
|
+
|
|
279
|
+
dispatcherModule = importlib.util.module_from_spec(dispatcherSpec)
|
|
280
|
+
dispatcherSpec.loader.exec_module(dispatcherModule)
|
|
281
|
+
callableDispatcherSynthetic = getattr(dispatcherModule, dispatcherSynthetic.callableSynthesized)
|
|
282
|
+
|
|
283
|
+
useThisDispatcher(callableDispatcherSynthetic)
|
|
284
|
+
return callableDispatcherSynthetic
|
|
285
|
+
|
|
228
286
|
def uniformTestMessage(expected: Any, actual: Any, functionName: str, *arguments: Any) -> str:
|
|
229
287
|
"""Format assertion message for any test comparison."""
|
|
230
288
|
return (f"\nTesting: `{functionName}({', '.join(str(parameter) for parameter in arguments)})`\n"
|
|
@@ -233,7 +291,7 @@ def uniformTestMessage(expected: Any, actual: Any, functionName: str, *arguments
|
|
|
233
291
|
|
|
234
292
|
def standardizedEqualTo(expected: Any, functionTarget: Callable, *arguments: Any) -> None:
|
|
235
293
|
"""Template for tests expecting an error."""
|
|
236
|
-
if type(expected) is
|
|
294
|
+
if type(expected) is type[Exception]:
|
|
237
295
|
messageExpected = expected.__name__
|
|
238
296
|
else:
|
|
239
297
|
messageExpected = expected
|
|
@@ -246,7 +304,7 @@ def standardizedEqualTo(expected: Any, functionTarget: Callable, *arguments: Any
|
|
|
246
304
|
|
|
247
305
|
assert actual == expected, uniformTestMessage(messageExpected, messageActual, functionTarget.__name__, *arguments)
|
|
248
306
|
|
|
249
|
-
def standardizedSystemExit(expected:
|
|
307
|
+
def standardizedSystemExit(expected: str | int | Sequence[int], functionTarget: Callable, *arguments: Any) -> None:
|
|
250
308
|
"""Template for tests expecting SystemExit.
|
|
251
309
|
|
|
252
310
|
Parameters
|
tests/test_computations.py
CHANGED
|
@@ -1,79 +1,42 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
1
|
from tests.conftest import *
|
|
3
2
|
import importlib.util
|
|
4
3
|
import pytest
|
|
5
4
|
|
|
6
|
-
def test_algorithmSourceParallel(listDimensionsTestParallelization:
|
|
5
|
+
def test_algorithmSourceParallel(listDimensionsTestParallelization: list[int], foldsTotalKnown: dict[tuple[int, ...], int], useAlgorithmSourceDispatcher: None) -> None:
|
|
7
6
|
standardizedEqualTo(foldsTotalKnown[tuple(listDimensionsTestParallelization)], countFolds, listDimensionsTestParallelization, None, 'maximum')
|
|
8
7
|
|
|
9
|
-
def test_algorithmSourceSequential(listDimensionsTestCountFolds:
|
|
8
|
+
def test_algorithmSourceSequential(listDimensionsTestCountFolds: list[int], foldsTotalKnown: dict[tuple[int, ...], int], useAlgorithmSourceDispatcher: None) -> None:
|
|
10
9
|
standardizedEqualTo(foldsTotalKnown[tuple(listDimensionsTestCountFolds)], countFolds, listDimensionsTestCountFolds)
|
|
11
10
|
|
|
12
11
|
def test_aOFn_calculate_value(oeisID: str) -> None:
|
|
13
12
|
for n in settingsOEIS[oeisID]['valuesTestValidation']:
|
|
14
13
|
standardizedEqualTo(settingsOEIS[oeisID]['valuesKnown'][n], oeisIDfor_n, oeisID, n)
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
# the _logical_ import must be in the logical path of the package
|
|
42
|
-
# fuck python
|
|
43
|
-
# """
|
|
44
|
-
# listCallablesInlineHARDCODED: List[str] = ['countInitialize', 'countParallel', 'countSequential']
|
|
45
|
-
# listCallablesInline = listCallablesInlineHARDCODED
|
|
46
|
-
# callableDispatcher = True
|
|
47
|
-
# algorithmSource = None
|
|
48
|
-
# relativePathWrite = None
|
|
49
|
-
# # relativePathWrite = pathTmpTesting.absolute().relative_to(getPathPackage(), walk_up=True)
|
|
50
|
-
# formatFilenameWrite = "pytest_{callableTarget}"
|
|
51
|
-
# listSynthesizedModules: List[youOughtaKnow] = makeFlowNumbaOptimized(listCallablesInline, callableDispatcher, algorithmSource, relativePathWrite, formatFilenameWrite)
|
|
52
|
-
# for stuff in listSynthesizedModules:
|
|
53
|
-
# registrarRecordsTmpObject(stuff.pathFilenameForMe)
|
|
54
|
-
# if stuff.callableSynthesized not in listCallablesInline:
|
|
55
|
-
# dispatcherSynthetic: youOughtaKnow = stuff
|
|
56
|
-
# if not dispatcherSynthetic: raise FREAKOUT
|
|
57
|
-
# # dispatcherSynthetic: youOughtaKnow = next(filter(lambda x: x.callableSynthesized not in listCallablesInline, listSynthesizedModules))
|
|
58
|
-
|
|
59
|
-
# # Import the synthetic dispatcher module to get the callable
|
|
60
|
-
# dispatcherSpec = importlib.util.spec_from_file_location(
|
|
61
|
-
# dispatcherSynthetic.callableSynthesized,
|
|
62
|
-
# dispatcherSynthetic.pathFilenameForMe
|
|
63
|
-
# )
|
|
64
|
-
# if dispatcherSpec is None:
|
|
65
|
-
# raise ImportError(f"Failed to create module specification from {dispatcherSynthetic.pathFilenameForMe}")
|
|
66
|
-
# if dispatcherSpec.loader is None:
|
|
67
|
-
# raise ImportError(f"Failed to get loader for module {dispatcherSynthetic.pathFilenameForMe}")
|
|
68
|
-
|
|
69
|
-
# dispatcherModule = importlib.util.module_from_spec(dispatcherSpec)
|
|
70
|
-
# dispatcherSpec.loader.exec_module(dispatcherModule)
|
|
71
|
-
# callableDispatcherSynthetic = getattr(dispatcherModule, dispatcherSynthetic.callableSynthesized)
|
|
72
|
-
|
|
73
|
-
# useThisDispatcher(callableDispatcherSynthetic)
|
|
74
|
-
|
|
75
|
-
# def test_syntheticSequential(listDimensionsTestCountFolds: List[int], foldsTotalKnown: Dict[Tuple[int, ...], int]):
|
|
76
|
-
# standardizedEqualTo(foldsTotalKnown[tuple(listDimensionsTestCountFolds)], countFolds, listDimensionsTestCountFolds)
|
|
15
|
+
@pytest.mark.parametrize('pathFilenameTmpTesting', ['.py'], indirect=True)
|
|
16
|
+
def test_writeJobNumba(listDimensionsTestCountFolds: list[int], foldsTotalKnown: dict[tuple[int, ...], int], pathFilenameTmpTesting: Path) -> None:
|
|
17
|
+
from mapFolding.syntheticModules import numbaCount
|
|
18
|
+
algorithmSourceHARDCODED: ModuleType = numbaCount
|
|
19
|
+
algorithmSource = algorithmSourceHARDCODED
|
|
20
|
+
callableTargetHARDCODED = 'countSequential'
|
|
21
|
+
callableTarget = callableTargetHARDCODED
|
|
22
|
+
pathFilenameModule = writeJobNumba(listDimensionsTestCountFolds, algorithmSource, callableTarget, pathFilenameWriteJob=pathFilenameTmpTesting.absolute())
|
|
23
|
+
|
|
24
|
+
Don_Lapre_Road_to_Self_Improvement = importlib.util.spec_from_file_location("__main__", pathFilenameModule)
|
|
25
|
+
if Don_Lapre_Road_to_Self_Improvement is None:
|
|
26
|
+
raise ImportError(f"Failed to create module specification from {pathFilenameModule}")
|
|
27
|
+
if Don_Lapre_Road_to_Self_Improvement.loader is None:
|
|
28
|
+
raise ImportError(f"Failed to get loader for module {pathFilenameModule}")
|
|
29
|
+
module = importlib.util.module_from_spec(Don_Lapre_Road_to_Self_Improvement)
|
|
30
|
+
|
|
31
|
+
module.__name__ = "__main__"
|
|
32
|
+
Don_Lapre_Road_to_Self_Improvement.loader.exec_module(module)
|
|
33
|
+
|
|
34
|
+
pathFilenameFoldsTotal = getPathFilenameFoldsTotal(listDimensionsTestCountFolds)
|
|
35
|
+
registrarRecordsTmpObject(pathFilenameFoldsTotal)
|
|
36
|
+
standardizedEqualTo(str(foldsTotalKnown[tuple(listDimensionsTestCountFolds)]), pathFilenameFoldsTotal.read_text().strip)
|
|
37
|
+
|
|
38
|
+
def test_syntheticParallel(syntheticDispatcherFixture, listDimensionsTestParallelization: list[int], foldsTotalKnown: dict[tuple[int, ...], int]):
|
|
39
|
+
standardizedEqualTo(foldsTotalKnown[tuple(listDimensionsTestParallelization)], countFolds, listDimensionsTestParallelization, None, 'maximum')
|
|
77
40
|
|
|
78
|
-
|
|
79
|
-
|
|
41
|
+
def test_syntheticSequential(syntheticDispatcherFixture, listDimensionsTestCountFolds: list[int], foldsTotalKnown: dict[tuple[int, ...], int]):
|
|
42
|
+
standardizedEqualTo(foldsTotalKnown[tuple(listDimensionsTestCountFolds)], countFolds, listDimensionsTestCountFolds)
|
tests/test_oeis.py
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
from contextlib import redirect_stdout
|
|
2
|
-
from datetime import datetime, timedelta
|
|
3
|
-
from mapFolding.oeis import _getFilenameOEISbFile, _getOEISidValues, _parseBFileOEIS, _validateOEISid, _getOEISidInformation
|
|
4
2
|
from tests.conftest import *
|
|
5
3
|
from urllib.error import URLError
|
|
6
4
|
import io
|
|
7
|
-
import os
|
|
8
5
|
import pathlib
|
|
9
6
|
import pytest
|
|
10
7
|
import random
|
|
@@ -16,21 +13,20 @@ import urllib.request
|
|
|
16
13
|
|
|
17
14
|
@pytest.mark.parametrize("badID", ["A999999", " A999999 ", "A999999extra"])
|
|
18
15
|
def test__validateOEISid_invalid_id(badID: str) -> None:
|
|
19
|
-
standardizedEqualTo(KeyError,
|
|
16
|
+
standardizedEqualTo(KeyError, validateOEISid, badID)
|
|
20
17
|
|
|
21
18
|
def test__validateOEISid_partially_valid(oeisID_1random: str) -> None:
|
|
22
|
-
standardizedEqualTo(KeyError,
|
|
19
|
+
standardizedEqualTo(KeyError, validateOEISid, f"{oeisID_1random}extra")
|
|
23
20
|
|
|
24
21
|
def test__validateOEISid_valid_id(oeisID: str) -> None:
|
|
25
|
-
standardizedEqualTo(oeisID,
|
|
22
|
+
standardizedEqualTo(oeisID, validateOEISid, oeisID)
|
|
26
23
|
|
|
27
24
|
def test__validateOEISid_valid_id_case_insensitive(oeisID: str) -> None:
|
|
28
|
-
standardizedEqualTo(oeisID.upper(),
|
|
29
|
-
standardizedEqualTo(oeisID.upper(),
|
|
30
|
-
standardizedEqualTo(oeisID.upper(),
|
|
25
|
+
standardizedEqualTo(oeisID.upper(), validateOEISid, oeisID.lower())
|
|
26
|
+
standardizedEqualTo(oeisID.upper(), validateOEISid, oeisID.upper())
|
|
27
|
+
standardizedEqualTo(oeisID.upper(), validateOEISid, oeisID.swapcase())
|
|
31
28
|
|
|
32
29
|
parameters_test_aOFn_invalid_n = [
|
|
33
|
-
# (2, "ok"), # test the test template
|
|
34
30
|
(-random.randint(1, 100), "randomNegative"),
|
|
35
31
|
("foo", "string"),
|
|
36
32
|
(1.5, "float")
|
|
@@ -62,13 +58,13 @@ def test_clearOEIScache(mock_unlink: unittest.mock.MagicMock, mock_exists: unitt
|
|
|
62
58
|
mock_exists.assert_called_once()
|
|
63
59
|
mock_unlink.assert_not_called()
|
|
64
60
|
|
|
65
|
-
def testNetworkError(monkeypatch: pytest.MonkeyPatch, pathCacheTesting:
|
|
61
|
+
def testNetworkError(monkeypatch: pytest.MonkeyPatch, pathCacheTesting: Path) -> None:
|
|
66
62
|
"""Test network error handling."""
|
|
67
63
|
def mockUrlopen(*args: Any, **kwargs: Any) -> NoReturn:
|
|
68
64
|
raise URLError("Network error")
|
|
69
65
|
|
|
70
66
|
monkeypatch.setattr(urllib.request, 'urlopen', mockUrlopen)
|
|
71
|
-
standardizedEqualTo(URLError,
|
|
67
|
+
standardizedEqualTo(URLError, getOEISidValues, next(iter(settingsOEIS)))
|
|
72
68
|
|
|
73
69
|
# ===== Command Line Interface Tests =====
|
|
74
70
|
def testHelpText() -> None:
|