mapFolding 0.3.12__py3-none-any.whl → 0.4.1__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 (46) hide show
  1. mapFolding/__init__.py +40 -38
  2. mapFolding/basecamp.py +50 -50
  3. mapFolding/beDRY.py +336 -336
  4. mapFolding/oeis.py +262 -262
  5. mapFolding/reference/flattened.py +294 -293
  6. mapFolding/reference/hunterNumba.py +126 -126
  7. mapFolding/reference/irvineJavaPort.py +99 -99
  8. mapFolding/reference/jax.py +153 -153
  9. mapFolding/reference/lunnan.py +148 -148
  10. mapFolding/reference/lunnanNumpy.py +115 -115
  11. mapFolding/reference/lunnanWhile.py +114 -114
  12. mapFolding/reference/rotatedEntryPoint.py +183 -183
  13. mapFolding/reference/total_countPlus1vsPlusN.py +203 -203
  14. mapFolding/someAssemblyRequired/__init__.py +5 -1
  15. mapFolding/someAssemblyRequired/getLLVMforNoReason.py +12 -12
  16. mapFolding/someAssemblyRequired/makeJob.py +46 -52
  17. mapFolding/someAssemblyRequired/synthesizeModuleJAX.py +17 -17
  18. mapFolding/someAssemblyRequired/synthesizeNumba.py +343 -633
  19. mapFolding/someAssemblyRequired/synthesizeNumbaGeneralized.py +325 -0
  20. mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +173 -0
  21. mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +77 -0
  22. mapFolding/syntheticModules/__init__.py +0 -0
  23. mapFolding/syntheticModules/numba_countInitialize.py +4 -4
  24. mapFolding/syntheticModules/numba_countParallel.py +4 -4
  25. mapFolding/syntheticModules/numba_countSequential.py +4 -4
  26. mapFolding/syntheticModules/numba_doTheNeedful.py +7 -7
  27. mapFolding/theDao.py +165 -165
  28. mapFolding/theSSOT.py +177 -173
  29. mapFolding/theSSOTnumba.py +90 -74
  30. mapFolding-0.4.1.dist-info/METADATA +154 -0
  31. mapFolding-0.4.1.dist-info/RECORD +42 -0
  32. tests/conftest.py +253 -129
  33. tests/test_computations.py +79 -0
  34. tests/test_oeis.py +76 -85
  35. tests/test_other.py +136 -224
  36. tests/test_tasks.py +19 -23
  37. tests/test_types.py +2 -2
  38. mapFolding/someAssemblyRequired/synthesizeNumbaHardcoding.py +0 -188
  39. mapFolding-0.3.12.dist-info/METADATA +0 -155
  40. mapFolding-0.3.12.dist-info/RECORD +0 -40
  41. tests/conftest_tmpRegistry.py +0 -62
  42. tests/conftest_uniformTests.py +0 -53
  43. {mapFolding-0.3.12.dist-info → mapFolding-0.4.1.dist-info}/LICENSE +0 -0
  44. {mapFolding-0.3.12.dist-info → mapFolding-0.4.1.dist-info}/WHEEL +0 -0
  45. {mapFolding-0.3.12.dist-info → mapFolding-0.4.1.dist-info}/entry_points.txt +0 -0
  46. {mapFolding-0.3.12.dist-info → mapFolding-0.4.1.dist-info}/top_level.txt +0 -0
tests/test_other.py CHANGED
@@ -1,6 +1,5 @@
1
1
  from contextlib import redirect_stdout
2
2
  from tests.conftest import *
3
- from typing import Dict, List, Optional, Any, Tuple, Literal, Callable, Generator
4
3
  from Z0Z_tools import intInnit
5
4
  import io
6
5
  import itertools
@@ -12,248 +11,161 @@ import random
12
11
  import sys
13
12
 
14
13
  @pytest.mark.parametrize("listDimensions,expected_intInnit,expected_parseListDimensions,expected_validateListDimensions,expected_getLeavesTotal", [
15
- (None, ValueError, ValueError, ValueError, ValueError), # None instead of list
16
- (['a'], ValueError, ValueError, ValueError, ValueError), # string
17
- ([-4, 2], [-4, 2], ValueError, ValueError, ValueError), # negative
18
- ([-3], [-3], ValueError, ValueError, ValueError), # negative
19
- ([0, 0], [0, 0], [0, 0], NotImplementedError, 0), # no positive dimensions
20
- ([0, 5, 6], [0, 5, 6], [0, 5, 6], [5, 6], 30), # zeros ignored
21
- ([0], [0], [0], NotImplementedError, 0), # edge case
22
- ([1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], 120), # sequential
23
- ([1, sys.maxsize], [1, sys.maxsize], [1, sys.maxsize], [1, sys.maxsize], sys.maxsize), # maxint
24
- ([7.5], ValueError, ValueError, ValueError, ValueError), # float
25
- ([1] * 1000, [1] * 1000, [1] * 1000, [1] * 1000, 1), # long list
26
- ([11], [11], [11], NotImplementedError, 11), # single dimension
27
- ([13, 0, 17], [13, 0, 17], [13, 0, 17], [13, 17], 221), # zeros handled
28
- ([2, 2, 2, 2], [2, 2, 2, 2], [2, 2, 2, 2], [2, 2, 2, 2], 16), # repeated dimensions
29
- ([2, 3, 4], [2, 3, 4], [2, 3, 4], [2, 3, 4], 24),
30
- ([2, 3], [2, 3], [2, 3], [2, 3], 6),
31
- ([2] * 11, [2] * 11, [2] * 11, [2] * 11, 2048), # power of 2
32
- ([3, 2], [3, 2], [3, 2], [2, 3], 6), # return value is the input when valid
33
- ([3] * 5, [3] * 5, [3] * 5, [3, 3, 3, 3, 3], 243), # power of 3
34
- ([None], TypeError, TypeError, TypeError, TypeError), # None
35
- ([True], TypeError, TypeError, TypeError, TypeError), # bool
36
- ([[17, 39]], TypeError, TypeError, TypeError, TypeError), # nested
37
- ([], ValueError, ValueError, ValueError, ValueError), # empty
38
- ([complex(1,1)], ValueError, ValueError, ValueError, ValueError), # complex number
39
- ([float('inf')], ValueError, ValueError, ValueError, ValueError), # infinity
40
- ([float('nan')], ValueError, ValueError, ValueError, ValueError), # NaN
41
- ([sys.maxsize - 1, 1], [sys.maxsize - 1, 1], [sys.maxsize - 1, 1], [1, sys.maxsize - 1], sys.maxsize - 1), # near maxint
42
- ([sys.maxsize // 2, sys.maxsize // 2, 2], [sys.maxsize // 2, sys.maxsize // 2, 2], [sys.maxsize // 2, sys.maxsize // 2, 2], [2, sys.maxsize // 2, sys.maxsize // 2], OverflowError), # overflow protection
43
- ([sys.maxsize, sys.maxsize], [sys.maxsize, sys.maxsize], [sys.maxsize, sys.maxsize], [sys.maxsize, sys.maxsize], OverflowError), # overflow protection
44
- (range(3, 7), [3, 4, 5, 6], [3, 4, 5, 6], [3, 4, 5, 6], 360), # range sequence type
45
- (tuple([3, 5, 7]), [3, 5, 7], [3, 5, 7], [3, 5, 7], 105), # tuple sequence type
14
+ (None, ValueError, ValueError, ValueError, ValueError), # None instead of list
15
+ (['a'], ValueError, ValueError, ValueError, ValueError), # string
16
+ ([-4, 2], [-4, 2], ValueError, ValueError, ValueError), # negative
17
+ ([-3], [-3], ValueError, ValueError, ValueError), # negative
18
+ ([0, 0], [0, 0], [0, 0], NotImplementedError, 0), # no positive dimensions
19
+ ([0, 5, 6], [0, 5, 6], [0, 5, 6], [5, 6], 30), # zeros ignored
20
+ ([0], [0], [0], NotImplementedError, 0), # edge case
21
+ ([1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], 120), # sequential
22
+ ([1, sys.maxsize], [1, sys.maxsize], [1, sys.maxsize], [1, sys.maxsize], sys.maxsize), # maxint
23
+ ([7.5], ValueError, ValueError, ValueError, ValueError), # float
24
+ ([1] * 1000, [1] * 1000, [1] * 1000, [1] * 1000, 1), # long list
25
+ ([11], [11], [11], NotImplementedError, 11), # single dimension
26
+ ([13, 0, 17], [13, 0, 17], [13, 0, 17], [13, 17], 221), # zeros handled
27
+ ([2, 2, 2, 2], [2, 2, 2, 2], [2, 2, 2, 2], [2, 2, 2, 2], 16), # repeated dimensions
28
+ ([2, 3, 4], [2, 3, 4], [2, 3, 4], [2, 3, 4], 24),
29
+ ([2, 3], [2, 3], [2, 3], [2, 3], 6),
30
+ ([2] * 11, [2] * 11, [2] * 11, [2] * 11, 2048), # power of 2
31
+ ([3, 2], [3, 2], [3, 2], [2, 3], 6), # return value is the input when valid
32
+ ([3] * 5, [3] * 5, [3] * 5, [3, 3, 3, 3, 3], 243), # power of 3
33
+ ([None], TypeError, TypeError, TypeError, TypeError), # None
34
+ ([True], TypeError, TypeError, TypeError, TypeError), # bool
35
+ ([[17, 39]], TypeError, TypeError, TypeError, TypeError), # nested
36
+ ([], ValueError, ValueError, ValueError, ValueError), # empty
37
+ ([complex(1,1)], ValueError, ValueError, ValueError, ValueError), # complex number
38
+ ([float('inf')], ValueError, ValueError, ValueError, ValueError), # infinity
39
+ ([float('nan')], ValueError, ValueError, ValueError, ValueError), # NaN
40
+ ([sys.maxsize - 1, 1], [sys.maxsize - 1, 1], [sys.maxsize - 1, 1], [1, sys.maxsize - 1], sys.maxsize - 1), # near maxint
41
+ ([sys.maxsize // 2, sys.maxsize // 2, 2], [sys.maxsize // 2, sys.maxsize // 2, 2], [sys.maxsize // 2, sys.maxsize // 2, 2], [2, sys.maxsize // 2, sys.maxsize // 2], OverflowError), # overflow protection
42
+ ([sys.maxsize, sys.maxsize], [sys.maxsize, sys.maxsize], [sys.maxsize, sys.maxsize], [sys.maxsize, sys.maxsize], OverflowError), # overflow protection
43
+ (range(3, 7), [3, 4, 5, 6], [3, 4, 5, 6], [3, 4, 5, 6], 360), # range sequence type
44
+ (tuple([3, 5, 7]), [3, 5, 7], [3, 5, 7], [3, 5, 7], 105), # tuple sequence type
46
45
  ])
47
46
  def test_listDimensionsAsParameter(listDimensions: None | List[str] | List[int] | List[float] | List[None] | List[bool] | List[List[int]] | List[complex] | range | tuple[int, ...], expected_intInnit: type[ValueError] | List[int] | type[TypeError], expected_parseListDimensions: type[ValueError] | List[int] | type[TypeError], expected_validateListDimensions: type[ValueError] | type[NotImplementedError] | List[int] | type[TypeError], expected_getLeavesTotal: type[ValueError] | int | type[TypeError] | type[OverflowError]) -> None:
48
- """Test both validateListDimensions and getLeavesTotal with the same inputs."""
49
- standardizedEqualTo(expected_intInnit, intInnit, listDimensions)
50
- standardizedEqualTo(expected_parseListDimensions, parseDimensions, listDimensions)
51
- standardizedEqualTo(expected_validateListDimensions, validateListDimensions, listDimensions)
52
- standardizedEqualTo(expected_getLeavesTotal, getLeavesTotal, listDimensions)
47
+ """Test both validateListDimensions and getLeavesTotal with the same inputs."""
48
+ standardizedEqualTo(expected_intInnit, intInnit, listDimensions)
49
+ standardizedEqualTo(expected_parseListDimensions, parseDimensions, listDimensions)
50
+ standardizedEqualTo(expected_validateListDimensions, validateListDimensions, listDimensions)
51
+ standardizedEqualTo(expected_getLeavesTotal, getLeavesTotal, listDimensions)
53
52
 
54
53
  def test_getLeavesTotal_edge_cases() -> None:
55
- """Test edge cases for getLeavesTotal."""
56
- # Order independence
57
- standardizedEqualTo(getLeavesTotal([2, 3, 4]), getLeavesTotal, [4, 2, 3])
54
+ """Test edge cases for getLeavesTotal."""
55
+ # Order independence
56
+ standardizedEqualTo(getLeavesTotal([2, 3, 4]), getLeavesTotal, [4, 2, 3])
58
57
 
59
- # Immutability
60
- listOriginal = [2, 3]
61
- standardizedEqualTo(6, getLeavesTotal, listOriginal)
62
- standardizedEqualTo([2, 3], lambda x: x, listOriginal) # Check that the list wasn't modified
63
-
64
- # TODO fix this mock
65
- # @pytest.mark.parametrize("foldsValue,writeFoldsTarget", [
66
- # (756839, "foldsTotalTest.txt"), # Direct file
67
- # (2640919, "foldsTotalTest.txt"), # Direct file
68
- # (7715177, None), # Directory, will use default filename
69
- # ])
70
- # def test_countFolds_writeFoldsTotal(
71
- # listDimensionsTestFunctionality: List[int],
72
- # pathTempTesting: pathlib.Path,
73
- # mockFoldingFunction: Callable[..., Callable[..., None]],
74
- # mockDispatcher: Callable[[Callable[..., None]], Any],
75
- # foldsValue: int,
76
- # writeFoldsTarget: Optional[str]
77
- # ) -> None:
78
- # """Test writing folds total to either a file or directory."""
79
- # # For directory case, use the directory path directly
80
- # if writeFoldsTarget is None:
81
- # pathWriteTarget = pathTempTesting
82
- # filenameFoldsTotalExpected = getFilenameFoldsTotal(listDimensionsTestFunctionality)
83
- # else:
84
- # pathWriteTarget = pathTempTesting / writeFoldsTarget
85
- # filenameFoldsTotalExpected = writeFoldsTarget
86
-
87
- # foldsTotalExpected = foldsValue * getLeavesTotal(listDimensionsTestFunctionality)
88
- # mock_countFolds = mockFoldingFunction(foldsValue, listDimensionsTestFunctionality)
89
-
90
- # with mockDispatcher(mock_countFolds):
91
- # returned = countFolds(listDimensionsTestFunctionality, pathLikeWriteFoldsTotal=pathWriteTarget)
92
-
93
- # standardizedEqualTo(str(foldsTotalExpected), lambda: (pathTempTesting / filenameFoldsTotalExpected).read_text())
58
+ # Immutability
59
+ listOriginal = [2, 3]
60
+ standardizedEqualTo(6, getLeavesTotal, listOriginal)
61
+ standardizedEqualTo([2, 3], lambda x: x, listOriginal) # Check that the list wasn't modified
94
62
 
95
63
  @pytest.mark.parametrize("nameOfTest,callablePytest", PytestFor_intInnit())
96
64
  def testIntInnit(nameOfTest: str, callablePytest: Callable[[], None]) -> None:
97
- callablePytest()
65
+ callablePytest()
98
66
 
99
67
  @pytest.mark.parametrize("nameOfTest,callablePytest", PytestFor_oopsieKwargsie())
100
68
  def testOopsieKwargsie(nameOfTest: str, callablePytest: Callable[[], None]) -> None:
101
- callablePytest()
69
+ callablePytest()
102
70
 
103
71
  @pytest.mark.parametrize("CPUlimit, expectedLimit", [
104
- (None, numba.get_num_threads()),
105
- (False, numba.get_num_threads()),
106
- (True, 1),
107
- (4, 4),
108
- (0.5, max(1, numba.get_num_threads() // 2)),
109
- (-0.5, max(1, numba.get_num_threads() // 2)),
110
- (-2, max(1, numba.get_num_threads() - 2)),
111
- (0, numba.get_num_threads()),
112
- (1, 1),
72
+ (None, numba.get_num_threads()),
73
+ (False, numba.get_num_threads()),
74
+ (True, 1),
75
+ (4, 4),
76
+ (0.5, max(1, numba.get_num_threads() // 2)),
77
+ (-0.5, max(1, numba.get_num_threads() // 2)),
78
+ (-2, max(1, numba.get_num_threads() - 2)),
79
+ (0, numba.get_num_threads()),
80
+ (1, 1),
113
81
  ])
114
82
  def test_setCPUlimit(CPUlimit: None | float | bool | Literal[4] | Literal[-2] | Literal[0] | Literal[1], expectedLimit: Any | int) -> None:
115
- standardizedEqualTo(expectedLimit, setCPUlimit, CPUlimit)
83
+ standardizedEqualTo(expectedLimit, setCPUlimit, CPUlimit)
116
84
 
117
85
  def test_makeConnectionGraph_nonNegative(listDimensionsTestFunctionality: List[int]) -> None:
118
- connectionGraph = makeConnectionGraph(listDimensionsTestFunctionality)
119
- assert numpy.all(connectionGraph >= 0), "All values in the connection graph should be non-negative."
120
-
121
- # @pytest.mark.parametrize("datatype", ['int16', 'uint64'])
122
- # def test_makeConnectionGraph_datatype(listDimensionsTestFunctionality: List[int], datatype) -> None:
123
- # connectionGraph = makeConnectionGraph(listDimensionsTestFunctionality, datatype=datatype)
124
- # assert connectionGraph.dtype == datatype, f"Expected datatype {datatype}, but got {connectionGraph.dtype}."
125
-
126
- """5 parameters
127
- listDimensionsTestFunctionality
128
-
129
- computationDivisions
130
- None
131
- random: int, first included: 2, first excluded: leavesTotal
132
- maximum
133
- cpu
134
-
135
- CPUlimit
136
- None
137
- True
138
- False
139
- 0
140
- 1
141
- -1
142
- random: 0 < float < 1
143
- random: -1 < float < 0
144
- random: int, first included: 2, first excluded: (min(leavesTotal, 16) - 1)
145
- random: int, first included: -1 * (min(leavesTotal, 16) - 1), first excluded: -1
146
-
147
- datatypeMedium
148
- None
149
- numpy.int64
150
- numpy.intc
151
- numpy.uint16
152
-
153
- datatypeLarge
154
- None
155
- numpy.int64
156
- numpy.intp
157
- numpy.uint32
158
-
159
- """
86
+ connectionGraph = makeConnectionGraph(listDimensionsTestFunctionality)
87
+ assert numpy.all(connectionGraph >= 0), "All values in the connection graph should be non-negative."
160
88
 
161
89
  @pytest.fixture
162
90
  def parameterIterator() -> Callable[[List[int]], Generator[Dict[str, Any], None, None]]:
163
- """Generate random combinations of parameters for outfitCountFolds testing."""
164
- parameterSets: Dict[str, List[Any]] = {
165
- 'computationDivisions': [
166
- None,
167
- 'maximum',
168
- 'cpu',
169
- ],
170
- 'CPUlimit': [
171
- None, True, False, 0, 1, -1,
172
- ],
173
- 'datatypeMedium': [
174
- None,
175
- numpy.int64,
176
- numpy.intc,
177
- numpy.uint16
178
- ],
179
- 'datatypeLarge': [
180
- None,
181
- numpy.int64,
182
- numpy.intp,
183
- numpy.uint32
184
- ]
185
- }
186
-
187
- def makeParametersDynamic(listDimensions: List[int]) -> Dict[str, List[Any]]:
188
- """Add context-dependent parameter values."""
189
- parametersDynamic = parameterSets.copy()
190
- leavesTotal = getLeavesTotal(listDimensions)
191
- concurrencyLimit = min(leavesTotal, 16)
192
-
193
- # Add dynamic computationDivisions values
194
- dynamicDivisions = [random.randint(2, leavesTotal-1) for iterator in range(3)]
195
- parametersDynamic['computationDivisions'] = parametersDynamic['computationDivisions'] + dynamicDivisions
196
-
197
- # Add dynamic CPUlimit values
198
- parameterDynamicCPU = [
199
- random.random(), # 0 to 1
200
- -random.random(), # -1 to 0
201
- ]
202
- parameterDynamicCPU.extend(
203
- [random.randint(2, concurrencyLimit-1) for iterator in range(2)]
204
- )
205
- parameterDynamicCPU.extend(
206
- [random.randint(-concurrencyLimit+1, -2) for iterator in range(2)]
207
- )
208
- parametersDynamic['CPUlimit'] = parametersDynamic['CPUlimit'] + parameterDynamicCPU
209
-
210
- return parametersDynamic
211
-
212
- def generateCombinations(listDimensions: List[int]) -> Generator[Dict[str, Any], None, None]:
213
- parametersDynamic = makeParametersDynamic(listDimensions)
214
- parameterKeys = list(parametersDynamic.keys())
215
- parameterValues = [parametersDynamic[key] for key in parameterKeys]
216
-
217
- # Shuffle each parameter list
218
- for valueList in parameterValues:
219
- random.shuffle(valueList)
220
-
221
- # Use zip_longest to iterate, filling with None when shorter lists are exhausted
222
- for combination in itertools.zip_longest(*parameterValues, fillvalue=None):
223
- yield dict(zip(parameterKeys, combination))
224
-
225
- return generateCombinations
226
-
227
- # TODO refactor due to changes
228
- # def test_pathJobDEFAULT_colab() -> None:
229
- # """Test that pathJobDEFAULT is set correctly when running in Google Colab."""
230
- # # Mock sys.modules to simulate running in Colab
231
- # with unittest.mock.patch.dict('sys.modules', {'google.colab': unittest.mock.MagicMock()}):
232
- # # Force reload of theSSOT to trigger Colab path logic
233
- # import importlib
234
- # import mapFolding.theSSOT
235
- # importlib.reload(mapFolding.theSSOT)
236
-
237
- # # Check that path was set to Colab-specific value
238
- # assert mapFolding.theSSOT.pathJobDEFAULT == pathlib.Path("/content/drive/MyDrive") / "jobs"
239
-
240
- # # Reload one more time to restore original state
241
- # importlib.reload(mapFolding.theSSOT)
242
-
243
- def test_saveFoldsTotal_fallback(pathTempTesting: pathlib.Path) -> None:
244
- foldsTotal = 123
245
- pathFilename = pathTempTesting / "foldsTotal.txt"
246
- with unittest.mock.patch("pathlib.Path.write_text", side_effect=OSError("Simulated write failure")):
247
- with unittest.mock.patch("os.getcwd", return_value=str(pathTempTesting)):
248
- capturedOutput = io.StringIO()
249
- with redirect_stdout(capturedOutput):
250
- saveFoldsTotal(pathFilename, foldsTotal)
251
- fallbackFiles = list(pathTempTesting.glob("foldsTotalYO_*.txt"))
252
- assert len(fallbackFiles) == 1, "Fallback file was not created upon write failure."
91
+ """Generate random combinations of parameters for outfitCountFolds testing."""
92
+ parameterSets: Dict[str, List[Any]] = {
93
+ 'computationDivisions': [
94
+ None,
95
+ 'maximum',
96
+ 'cpu',
97
+ ],
98
+ 'CPUlimit': [
99
+ None, True, False, 0, 1, -1,
100
+ ],
101
+ 'datatypeMedium': [
102
+ None,
103
+ numpy.int64,
104
+ numpy.intc,
105
+ numpy.uint16
106
+ ],
107
+ 'datatypeLarge': [
108
+ None,
109
+ numpy.int64,
110
+ numpy.intp,
111
+ numpy.uint32
112
+ ]
113
+ }
114
+
115
+ def makeParametersDynamic(listDimensions: List[int]) -> Dict[str, List[Any]]:
116
+ """Add context-dependent parameter values."""
117
+ parametersDynamic = parameterSets.copy()
118
+ leavesTotal = getLeavesTotal(listDimensions)
119
+ concurrencyLimit = min(leavesTotal, 16)
120
+
121
+ # Add dynamic computationDivisions values
122
+ dynamicDivisions = [random.randint(2, leavesTotal-1) for iterator in range(3)]
123
+ parametersDynamic['computationDivisions'] = parametersDynamic['computationDivisions'] + dynamicDivisions
124
+
125
+ # Add dynamic CPUlimit values
126
+ parameterDynamicCPU = [
127
+ random.random(), # 0 to 1
128
+ -random.random(), # -1 to 0
129
+ ]
130
+ parameterDynamicCPU.extend(
131
+ [random.randint(2, concurrencyLimit-1) for iterator in range(2)]
132
+ )
133
+ parameterDynamicCPU.extend(
134
+ [random.randint(-concurrencyLimit+1, -2) for iterator in range(2)]
135
+ )
136
+ parametersDynamic['CPUlimit'] = parametersDynamic['CPUlimit'] + parameterDynamicCPU
137
+
138
+ return parametersDynamic
139
+
140
+ def generateCombinations(listDimensions: List[int]) -> Generator[Dict[str, Any], None, None]:
141
+ parametersDynamic = makeParametersDynamic(listDimensions)
142
+ parameterKeys = list(parametersDynamic.keys())
143
+ parameterValues = [parametersDynamic[key] for key in parameterKeys]
144
+
145
+ # Shuffle each parameter list
146
+ for valueList in parameterValues:
147
+ random.shuffle(valueList)
148
+
149
+ # Use zip_longest to iterate, filling with None when shorter lists are exhausted
150
+ for combination in itertools.zip_longest(*parameterValues, fillvalue=None):
151
+ yield dict(zip(parameterKeys, combination))
152
+
153
+ return generateCombinations
154
+
155
+ def test_saveFoldsTotal_fallback(pathTmpTesting: pathlib.Path) -> None:
156
+ foldsTotal = 123
157
+ pathFilename = pathTmpTesting / "foldsTotal.txt"
158
+ with unittest.mock.patch("pathlib.Path.write_text", side_effect=OSError("Simulated write failure")):
159
+ with unittest.mock.patch("os.getcwd", return_value=str(pathTmpTesting)):
160
+ capturedOutput = io.StringIO()
161
+ with redirect_stdout(capturedOutput):
162
+ saveFoldsTotal(pathFilename, foldsTotal)
163
+ fallbackFiles = list(pathTmpTesting.glob("foldsTotalYO_*.txt"))
164
+ assert len(fallbackFiles) == 1, "Fallback file was not created upon write failure."
253
165
 
254
166
  def test_makeDataContainer_default_datatype() -> None:
255
- """Test that makeDataContainer uses dtypeLargeDEFAULT when no datatype is specified."""
256
- testShape = (3, 4)
257
- container = makeDataContainer(testShape)
258
- assert container.dtype == hackSSOTdtype('dtypeFoldsTotal'), f"Expected datatype but got {container.dtype}"
259
- assert container.shape == testShape, f"Expected shape {testShape}, but got {container.shape}"
167
+ """Test that makeDataContainer uses dtypeLargeDEFAULT when no datatype is specified."""
168
+ testShape = (3, 4)
169
+ container = makeDataContainer(testShape)
170
+ assert container.dtype == hackSSOTdtype('dtypeFoldsTotal'), f"Expected datatype but got {container.dtype}"
171
+ assert container.shape == testShape, f"Expected shape {testShape}, but got {container.shape}"
tests/test_tasks.py CHANGED
@@ -1,44 +1,40 @@
1
1
  from tests.conftest import *
2
2
  import pytest
3
- from typing import List, Dict, Literal, Tuple, Any
4
3
 
5
4
  # TODO add a test. `C` = number of logical cores available. `n = C + 1`. Ensure that `[2,n]` is computed correctly.
6
5
  # Or, probably smarter: limit the number of cores, then run a test with C+1.
7
6
 
8
- def test_algorithmSourceParallel(listDimensionsTestParallelization: List[int], foldsTotalKnown: Dict[Tuple[int, ...], int], useAlgorithmDirectly: None) -> None:
9
- standardizedEqualTo(foldsTotalKnown[tuple(listDimensionsTestParallelization)], countFolds, listDimensionsTestParallelization, None, 'maximum')
10
-
11
7
  def test_countFoldsComputationDivisionsInvalid(listDimensionsTestFunctionality: List[int]) -> None:
12
- standardizedEqualTo(ValueError, countFolds, listDimensionsTestFunctionality, None, {"wrong": "value"})
8
+ standardizedEqualTo(ValueError, countFolds, listDimensionsTestFunctionality, None, {"wrong": "value"})
13
9
 
14
10
  def test_countFoldsComputationDivisionsMaximum(listDimensionsTestParallelization: List[int], foldsTotalKnown: Dict[Tuple[int, ...], int]) -> None:
15
- standardizedEqualTo(foldsTotalKnown[tuple(listDimensionsTestParallelization)], countFolds, listDimensionsTestParallelization, None, 'maximum')
11
+ standardizedEqualTo(foldsTotalKnown[tuple(listDimensionsTestParallelization)], countFolds, listDimensionsTestParallelization, None, 'maximum')
16
12
 
17
13
  @pytest.mark.parametrize("nameOfTest,callablePytest", PytestFor_defineConcurrencyLimit())
18
14
  def test_defineConcurrencyLimit(nameOfTest: str, callablePytest: Callable[[], None]) -> None:
19
- callablePytest()
15
+ callablePytest()
20
16
 
21
- # @pytest.mark.parametrize("CPUlimitParameter", [{"invalid": True}, ["weird"]])
22
- # def test_countFolds_cpuLimitOopsie(listDimensionsTestFunctionality: List[int], CPUlimitParameter: Dict[str, bool] | List[str]) -> None:
23
- # standardizedEqualTo((AttributeError or ValueError), countFolds, listDimensionsTestFunctionality, None, 'cpu', CPUlimitParameter)
17
+ @pytest.mark.parametrize("CPUlimitParameter", [{"invalid": True}, ["weird"]])
18
+ def test_countFolds_cpuLimitOopsie(listDimensionsTestFunctionality: List[int], CPUlimitParameter: Dict[str, bool] | List[str]) -> None:
19
+ standardizedEqualTo(ValueError, countFolds, listDimensionsTestFunctionality, None, 'cpu', CPUlimitParameter)
24
20
 
25
21
  @pytest.mark.parametrize("computationDivisions, concurrencyLimit, listDimensions, expectedTaskDivisions", [
26
- (None, 4, [9, 11], 0),
27
- ("maximum", 4, [7, 11], 77),
28
- ("cpu", 4, [3, 7], 4),
29
- (["invalid"], 4, [19, 23], ValueError),
30
- (20, 4, [3,5], ValueError)
22
+ (None, 4, [9, 11], 0),
23
+ ("maximum", 4, [7, 11], 77),
24
+ ("cpu", 4, [3, 7], 4),
25
+ (["invalid"], 4, [19, 23], ValueError),
26
+ (20, 4, [3,5], ValueError)
31
27
  ])
32
28
  def test_getTaskDivisions(computationDivisions: None | List[str] | Literal['maximum'] | Literal['cpu'] | Literal[20], concurrencyLimit: Literal[4], listDimensions: List[int], expectedTaskDivisions: type[ValueError] | Literal[0] | Literal[77] | Literal[4]) -> None:
33
- standardizedEqualTo(expectedTaskDivisions, getTaskDivisions, computationDivisions, concurrencyLimit, None, listDimensions)
29
+ standardizedEqualTo(expectedTaskDivisions, getTaskDivisions, computationDivisions, concurrencyLimit, None, listDimensions)
34
30
 
35
31
  @pytest.mark.parametrize("expected,parameter", [
36
- (2, "2"), # string
37
- (ValueError, [4]), # list
38
- (ValueError, (2,)), # tuple
39
- (ValueError, {2}), # set
40
- (ValueError, {"cores": 2}), # dict
32
+ (2, "2"), # string
33
+ (ValueError, [4]), # list
34
+ (ValueError, (2,)), # tuple
35
+ (ValueError, {2}), # set
36
+ (ValueError, {"cores": 2}), # dict
41
37
  ])
42
38
  def test_setCPUlimitMalformedParameter(expected: type[ValueError] | Literal[2], parameter: List[int] | Tuple[int] | set[int] | Dict[str, int] | Literal['2']) -> None:
43
- """Test that invalid CPUlimit types are properly handled."""
44
- standardizedEqualTo(expected, setCPUlimit, parameter)
39
+ """Test that invalid CPUlimit types are properly handled."""
40
+ standardizedEqualTo(expected, setCPUlimit, parameter)
tests/test_types.py CHANGED
@@ -1,5 +1,5 @@
1
1
  """Type checking tests for mapFolding package."""
2
2
 
3
3
  def test_static_typing() -> None:
4
- """This is a placeholder. pytest-mypy will run type checking automatically."""
5
- pass
4
+ """This is a placeholder. pytest-mypy will run type checking automatically."""
5
+ pass