mapFolding 0.3.11__py3-none-any.whl → 0.4.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 +44 -32
- mapFolding/basecamp.py +50 -50
- mapFolding/beDRY.py +336 -336
- mapFolding/oeis.py +262 -262
- mapFolding/reference/flattened.py +294 -293
- mapFolding/reference/hunterNumba.py +126 -126
- mapFolding/reference/irvineJavaPort.py +99 -99
- mapFolding/reference/jax.py +153 -153
- mapFolding/reference/lunnan.py +148 -148
- mapFolding/reference/lunnanNumpy.py +115 -115
- mapFolding/reference/lunnanWhile.py +114 -114
- mapFolding/reference/rotatedEntryPoint.py +183 -183
- mapFolding/reference/total_countPlus1vsPlusN.py +203 -203
- mapFolding/someAssemblyRequired/__init__.py +2 -1
- mapFolding/someAssemblyRequired/getLLVMforNoReason.py +12 -12
- mapFolding/someAssemblyRequired/makeJob.py +48 -48
- mapFolding/someAssemblyRequired/synthesizeModuleJAX.py +17 -17
- mapFolding/someAssemblyRequired/synthesizeNumba.py +345 -803
- mapFolding/someAssemblyRequired/synthesizeNumbaGeneralized.py +371 -0
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +150 -0
- mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +75 -0
- mapFolding/syntheticModules/__init__.py +0 -0
- mapFolding/syntheticModules/numba_countInitialize.py +2 -2
- mapFolding/syntheticModules/numba_countParallel.py +3 -3
- mapFolding/syntheticModules/numba_countSequential.py +28 -28
- mapFolding/syntheticModules/numba_doTheNeedful.py +6 -6
- mapFolding/theDao.py +168 -169
- mapFolding/theSSOT.py +190 -162
- mapFolding/theSSOTnumba.py +91 -75
- mapFolding-0.4.0.dist-info/METADATA +122 -0
- mapFolding-0.4.0.dist-info/RECORD +41 -0
- tests/conftest.py +238 -128
- tests/test_oeis.py +80 -80
- tests/test_other.py +137 -224
- tests/test_tasks.py +21 -21
- tests/test_types.py +2 -2
- mapFolding-0.3.11.dist-info/METADATA +0 -155
- mapFolding-0.3.11.dist-info/RECORD +0 -39
- tests/conftest_tmpRegistry.py +0 -62
- tests/conftest_uniformTests.py +0 -53
- {mapFolding-0.3.11.dist-info → mapFolding-0.4.0.dist-info}/LICENSE +0 -0
- {mapFolding-0.3.11.dist-info → mapFolding-0.4.0.dist-info}/WHEEL +0 -0
- {mapFolding-0.3.11.dist-info → mapFolding-0.4.0.dist-info}/entry_points.txt +0 -0
- {mapFolding-0.3.11.dist-info → mapFolding-0.4.0.dist-info}/top_level.txt +0 -0
tests/test_oeis.py
CHANGED
|
@@ -16,126 +16,126 @@ import urllib
|
|
|
16
16
|
import urllib.request
|
|
17
17
|
|
|
18
18
|
def test_algorithmSourceSequential(oeisID: str, useAlgorithmDirectly: None) -> None:
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
for n in settingsOEIS[oeisID]['valuesTestValidation']:
|
|
20
|
+
standardizedEqualTo(settingsOEIS[oeisID]['valuesKnown'][n], oeisIDfor_n, oeisID, n)
|
|
21
21
|
|
|
22
22
|
def test_aOFn_calculate_value(oeisID: str) -> None:
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
for n in settingsOEIS[oeisID]['valuesTestValidation']:
|
|
24
|
+
standardizedEqualTo(settingsOEIS[oeisID]['valuesKnown'][n], oeisIDfor_n, oeisID, n)
|
|
25
25
|
|
|
26
26
|
@pytest.mark.parametrize("badID", ["A999999", " A999999 ", "A999999extra"])
|
|
27
27
|
def test__validateOEISid_invalid_id(badID: str) -> None:
|
|
28
|
-
|
|
28
|
+
standardizedEqualTo(KeyError, _validateOEISid, badID)
|
|
29
29
|
|
|
30
30
|
def test__validateOEISid_partially_valid(oeisID_1random: str) -> None:
|
|
31
|
-
|
|
31
|
+
standardizedEqualTo(KeyError, _validateOEISid, f"{oeisID_1random}extra")
|
|
32
32
|
|
|
33
33
|
def test__validateOEISid_valid_id(oeisID: str) -> None:
|
|
34
|
-
|
|
34
|
+
standardizedEqualTo(oeisID, _validateOEISid, oeisID)
|
|
35
35
|
|
|
36
36
|
def test__validateOEISid_valid_id_case_insensitive(oeisID: str) -> None:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
standardizedEqualTo(oeisID.upper(), _validateOEISid, oeisID.lower())
|
|
38
|
+
standardizedEqualTo(oeisID.upper(), _validateOEISid, oeisID.upper())
|
|
39
|
+
standardizedEqualTo(oeisID.upper(), _validateOEISid, oeisID.swapcase())
|
|
40
40
|
|
|
41
41
|
parameters_test_aOFn_invalid_n = [
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
# (2, "ok"), # test the test template
|
|
43
|
+
(-random.randint(1, 100), "randomNegative"),
|
|
44
|
+
("foo", "string"),
|
|
45
|
+
(1.5, "float")
|
|
46
46
|
]
|
|
47
47
|
badValues, badValuesIDs = zip(*parameters_test_aOFn_invalid_n)
|
|
48
48
|
@pytest.mark.parametrize("badN", badValues, ids=badValuesIDs)
|
|
49
49
|
def test_aOFn_invalid_n(oeisID_1random: str, badN: Any) -> None:
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
"""Check that negative or non-integer n raises ValueError."""
|
|
51
|
+
standardizedEqualTo(ValueError, oeisIDfor_n, oeisID_1random, badN)
|
|
52
52
|
|
|
53
53
|
def test_aOFn_zeroDim_A001418() -> None:
|
|
54
|
-
|
|
54
|
+
standardizedEqualTo(ArithmeticError, oeisIDfor_n, 'A001418', 0)
|
|
55
55
|
|
|
56
56
|
# ===== OEIS Cache Tests =====
|
|
57
57
|
@pytest.mark.parametrize("cacheExists", [True, False])
|
|
58
58
|
@unittest.mock.patch('pathlib.Path.exists')
|
|
59
59
|
@unittest.mock.patch('pathlib.Path.unlink')
|
|
60
60
|
def test_clearOEIScache(mock_unlink: unittest.mock.MagicMock, mock_exists: unittest.mock.MagicMock, cacheExists: bool) -> None:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
61
|
+
"""Test OEIS cache clearing with both existing and non-existing cache."""
|
|
62
|
+
mock_exists.return_value = cacheExists
|
|
63
|
+
clearOEIScache()
|
|
64
|
+
|
|
65
|
+
if cacheExists:
|
|
66
|
+
# Each OEIS ID has two cache files
|
|
67
|
+
expected_calls = len(settingsOEIS) * 2
|
|
68
|
+
assert mock_unlink.call_count == expected_calls
|
|
69
|
+
mock_unlink.assert_has_calls([unittest.mock.call(missing_ok=True)] * expected_calls)
|
|
70
|
+
else:
|
|
71
|
+
mock_exists.assert_called_once()
|
|
72
|
+
mock_unlink.assert_not_called()
|
|
73
73
|
|
|
74
74
|
def testNetworkError(monkeypatch: pytest.MonkeyPatch, pathCacheTesting: pathlib.Path) -> None:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
"""Test network error handling."""
|
|
76
|
+
def mockUrlopen(*args: Any, **kwargs: Any) -> NoReturn:
|
|
77
|
+
raise URLError("Network error")
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
monkeypatch.setattr(urllib.request, 'urlopen', mockUrlopen)
|
|
80
|
+
standardizedEqualTo(URLError, _getOEISidValues, next(iter(settingsOEIS)))
|
|
81
81
|
|
|
82
82
|
# ===== Command Line Interface Tests =====
|
|
83
83
|
def testHelpText() -> None:
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
"""Test that help text is complete and examples are valid."""
|
|
85
|
+
outputStream = io.StringIO()
|
|
86
|
+
with redirect_stdout(outputStream):
|
|
87
|
+
getOEISids()
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
helpText = outputStream.getvalue()
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
# Verify content
|
|
92
|
+
for oeisID in oeisIDsImplemented:
|
|
93
|
+
assert oeisID in helpText
|
|
94
|
+
assert settingsOEIS[oeisID]['description'] in helpText
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
# Extract and verify examples
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
cliMatch = regex.search(r'OEIS_for_n (\w+) (\d+)', helpText)
|
|
99
|
+
pythonMatch = regex.search(r"oeisIDfor_n\('(\w+)', (\d+)\)", helpText)
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
assert cliMatch and pythonMatch, "Help text missing examples"
|
|
102
|
+
oeisID, n = pythonMatch.groups()
|
|
103
|
+
n = int(n)
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
# Verify CLI and Python examples use same values
|
|
106
|
+
assert cliMatch.groups() == (oeisID, str(n)), "CLI and Python examples inconsistent"
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
# Verify the example works
|
|
109
|
+
expectedValue = oeisIDfor_n(oeisID, n)
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
111
|
+
# Test CLI execution of the example
|
|
112
|
+
with unittest.mock.patch('sys.argv', ['OEIS_for_n', oeisID, str(n)]):
|
|
113
|
+
outputStream = io.StringIO()
|
|
114
|
+
with redirect_stdout(outputStream):
|
|
115
|
+
OEIS_for_n()
|
|
116
|
+
standardizedEqualTo(expectedValue, lambda: int(outputStream.getvalue().strip().split()[0]))
|
|
117
117
|
|
|
118
118
|
def testCLI_InvalidInputs() -> None:
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
119
|
+
"""Test CLI error handling."""
|
|
120
|
+
testCases = [
|
|
121
|
+
(['OEIS_for_n'], "missing arguments"),
|
|
122
|
+
(['OEIS_for_n', 'A999999', '1'], "invalid OEIS ID"),
|
|
123
|
+
(['OEIS_for_n', 'A001415', '-1'], "negative n"),
|
|
124
|
+
(['OEIS_for_n', 'A001415', 'abc'], "non-integer n"),
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
for arguments, testID in testCases:
|
|
128
|
+
with unittest.mock.patch('sys.argv', arguments):
|
|
129
|
+
standardizedSystemExit("error", OEIS_for_n)
|
|
130
130
|
|
|
131
131
|
def testCLI_HelpFlag() -> None:
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
132
|
+
"""Verify --help output contains required information."""
|
|
133
|
+
with unittest.mock.patch('sys.argv', ['OEIS_for_n', '--help']):
|
|
134
|
+
outputStream = io.StringIO()
|
|
135
|
+
with redirect_stdout(outputStream):
|
|
136
|
+
standardizedSystemExit("nonError", OEIS_for_n)
|
|
137
|
+
|
|
138
|
+
helpOutput = outputStream.getvalue()
|
|
139
|
+
assert "Available OEIS sequences:" in helpOutput
|
|
140
|
+
assert "Usage examples:" in helpOutput
|
|
141
|
+
assert all(oeisID in helpOutput for oeisID in oeisIDsImplemented)
|
tests/test_other.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from contextlib import redirect_stdout
|
|
2
2
|
from tests.conftest import *
|
|
3
|
-
from typing import Dict, List,
|
|
3
|
+
from typing import Dict, List, Any, Literal, Callable, Generator, Optional, Union
|
|
4
4
|
from Z0Z_tools import intInnit
|
|
5
5
|
import io
|
|
6
6
|
import itertools
|
|
@@ -12,248 +12,161 @@ import random
|
|
|
12
12
|
import sys
|
|
13
13
|
|
|
14
14
|
@pytest.mark.parametrize("listDimensions,expected_intInnit,expected_parseListDimensions,expected_validateListDimensions,expected_getLeavesTotal", [
|
|
15
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
46
46
|
])
|
|
47
47
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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)
|
|
53
53
|
|
|
54
54
|
def test_getLeavesTotal_edge_cases() -> None:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
"""Test edge cases for getLeavesTotal."""
|
|
56
|
+
# Order independence
|
|
57
|
+
standardizedEqualTo(getLeavesTotal([2, 3, 4]), getLeavesTotal, [4, 2, 3])
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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())
|
|
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
|
|
94
63
|
|
|
95
64
|
@pytest.mark.parametrize("nameOfTest,callablePytest", PytestFor_intInnit())
|
|
96
65
|
def testIntInnit(nameOfTest: str, callablePytest: Callable[[], None]) -> None:
|
|
97
|
-
|
|
66
|
+
callablePytest()
|
|
98
67
|
|
|
99
68
|
@pytest.mark.parametrize("nameOfTest,callablePytest", PytestFor_oopsieKwargsie())
|
|
100
69
|
def testOopsieKwargsie(nameOfTest: str, callablePytest: Callable[[], None]) -> None:
|
|
101
|
-
|
|
70
|
+
callablePytest()
|
|
102
71
|
|
|
103
72
|
@pytest.mark.parametrize("CPUlimit, expectedLimit", [
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
73
|
+
(None, numba.get_num_threads()),
|
|
74
|
+
(False, numba.get_num_threads()),
|
|
75
|
+
(True, 1),
|
|
76
|
+
(4, 4),
|
|
77
|
+
(0.5, max(1, numba.get_num_threads() // 2)),
|
|
78
|
+
(-0.5, max(1, numba.get_num_threads() // 2)),
|
|
79
|
+
(-2, max(1, numba.get_num_threads() - 2)),
|
|
80
|
+
(0, numba.get_num_threads()),
|
|
81
|
+
(1, 1),
|
|
113
82
|
])
|
|
114
83
|
def test_setCPUlimit(CPUlimit: None | float | bool | Literal[4] | Literal[-2] | Literal[0] | Literal[1], expectedLimit: Any | int) -> None:
|
|
115
|
-
|
|
84
|
+
standardizedEqualTo(expectedLimit, setCPUlimit, CPUlimit)
|
|
116
85
|
|
|
117
86
|
def test_makeConnectionGraph_nonNegative(listDimensionsTestFunctionality: List[int]) -> None:
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
"""
|
|
87
|
+
connectionGraph = makeConnectionGraph(listDimensionsTestFunctionality)
|
|
88
|
+
assert numpy.all(connectionGraph >= 0), "All values in the connection graph should be non-negative."
|
|
160
89
|
|
|
161
90
|
@pytest.fixture
|
|
162
91
|
def parameterIterator() -> Callable[[List[int]], Generator[Dict[str, Any], None, None]]:
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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."
|
|
92
|
+
"""Generate random combinations of parameters for outfitCountFolds testing."""
|
|
93
|
+
parameterSets: Dict[str, List[Any]] = {
|
|
94
|
+
'computationDivisions': [
|
|
95
|
+
None,
|
|
96
|
+
'maximum',
|
|
97
|
+
'cpu',
|
|
98
|
+
],
|
|
99
|
+
'CPUlimit': [
|
|
100
|
+
None, True, False, 0, 1, -1,
|
|
101
|
+
],
|
|
102
|
+
'datatypeMedium': [
|
|
103
|
+
None,
|
|
104
|
+
numpy.int64,
|
|
105
|
+
numpy.intc,
|
|
106
|
+
numpy.uint16
|
|
107
|
+
],
|
|
108
|
+
'datatypeLarge': [
|
|
109
|
+
None,
|
|
110
|
+
numpy.int64,
|
|
111
|
+
numpy.intp,
|
|
112
|
+
numpy.uint32
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
def makeParametersDynamic(listDimensions: List[int]) -> Dict[str, List[Any]]:
|
|
117
|
+
"""Add context-dependent parameter values."""
|
|
118
|
+
parametersDynamic = parameterSets.copy()
|
|
119
|
+
leavesTotal = getLeavesTotal(listDimensions)
|
|
120
|
+
concurrencyLimit = min(leavesTotal, 16)
|
|
121
|
+
|
|
122
|
+
# Add dynamic computationDivisions values
|
|
123
|
+
dynamicDivisions = [random.randint(2, leavesTotal-1) for iterator in range(3)]
|
|
124
|
+
parametersDynamic['computationDivisions'] = parametersDynamic['computationDivisions'] + dynamicDivisions
|
|
125
|
+
|
|
126
|
+
# Add dynamic CPUlimit values
|
|
127
|
+
parameterDynamicCPU = [
|
|
128
|
+
random.random(), # 0 to 1
|
|
129
|
+
-random.random(), # -1 to 0
|
|
130
|
+
]
|
|
131
|
+
parameterDynamicCPU.extend(
|
|
132
|
+
[random.randint(2, concurrencyLimit-1) for iterator in range(2)]
|
|
133
|
+
)
|
|
134
|
+
parameterDynamicCPU.extend(
|
|
135
|
+
[random.randint(-concurrencyLimit+1, -2) for iterator in range(2)]
|
|
136
|
+
)
|
|
137
|
+
parametersDynamic['CPUlimit'] = parametersDynamic['CPUlimit'] + parameterDynamicCPU
|
|
138
|
+
|
|
139
|
+
return parametersDynamic
|
|
140
|
+
|
|
141
|
+
def generateCombinations(listDimensions: List[int]) -> Generator[Dict[str, Any], None, None]:
|
|
142
|
+
parametersDynamic = makeParametersDynamic(listDimensions)
|
|
143
|
+
parameterKeys = list(parametersDynamic.keys())
|
|
144
|
+
parameterValues = [parametersDynamic[key] for key in parameterKeys]
|
|
145
|
+
|
|
146
|
+
# Shuffle each parameter list
|
|
147
|
+
for valueList in parameterValues:
|
|
148
|
+
random.shuffle(valueList)
|
|
149
|
+
|
|
150
|
+
# Use zip_longest to iterate, filling with None when shorter lists are exhausted
|
|
151
|
+
for combination in itertools.zip_longest(*parameterValues, fillvalue=None):
|
|
152
|
+
yield dict(zip(parameterKeys, combination))
|
|
153
|
+
|
|
154
|
+
return generateCombinations
|
|
155
|
+
|
|
156
|
+
def test_saveFoldsTotal_fallback(pathTmpTesting: pathlib.Path) -> None:
|
|
157
|
+
foldsTotal = 123
|
|
158
|
+
pathFilename = pathTmpTesting / "foldsTotal.txt"
|
|
159
|
+
with unittest.mock.patch("pathlib.Path.write_text", side_effect=OSError("Simulated write failure")):
|
|
160
|
+
with unittest.mock.patch("os.getcwd", return_value=str(pathTmpTesting)):
|
|
161
|
+
capturedOutput = io.StringIO()
|
|
162
|
+
with redirect_stdout(capturedOutput):
|
|
163
|
+
saveFoldsTotal(pathFilename, foldsTotal)
|
|
164
|
+
fallbackFiles = list(pathTmpTesting.glob("foldsTotalYO_*.txt"))
|
|
165
|
+
assert len(fallbackFiles) == 1, "Fallback file was not created upon write failure."
|
|
253
166
|
|
|
254
167
|
def test_makeDataContainer_default_datatype() -> None:
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
168
|
+
"""Test that makeDataContainer uses dtypeLargeDEFAULT when no datatype is specified."""
|
|
169
|
+
testShape = (3, 4)
|
|
170
|
+
container = makeDataContainer(testShape)
|
|
171
|
+
assert container.dtype == hackSSOTdtype('dtypeFoldsTotal'), f"Expected datatype but got {container.dtype}"
|
|
172
|
+
assert container.shape == testShape, f"Expected shape {testShape}, but got {container.shape}"
|