mapFolding 0.3.12__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.
Files changed (45) 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 +2 -1
  15. mapFolding/someAssemblyRequired/getLLVMforNoReason.py +12 -12
  16. mapFolding/someAssemblyRequired/makeJob.py +48 -48
  17. mapFolding/someAssemblyRequired/synthesizeModuleJAX.py +17 -17
  18. mapFolding/someAssemblyRequired/synthesizeNumba.py +343 -633
  19. mapFolding/someAssemblyRequired/synthesizeNumbaGeneralized.py +371 -0
  20. mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +150 -0
  21. mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +75 -0
  22. mapFolding/syntheticModules/__init__.py +0 -0
  23. mapFolding/syntheticModules/numba_countInitialize.py +3 -3
  24. mapFolding/syntheticModules/numba_countParallel.py +3 -3
  25. mapFolding/syntheticModules/numba_countSequential.py +3 -3
  26. mapFolding/syntheticModules/numba_doTheNeedful.py +6 -6
  27. mapFolding/theDao.py +165 -165
  28. mapFolding/theSSOT.py +176 -172
  29. mapFolding/theSSOTnumba.py +90 -74
  30. mapFolding-0.4.0.dist-info/METADATA +122 -0
  31. mapFolding-0.4.0.dist-info/RECORD +41 -0
  32. tests/conftest.py +238 -128
  33. tests/test_oeis.py +80 -80
  34. tests/test_other.py +137 -224
  35. tests/test_tasks.py +21 -21
  36. tests/test_types.py +2 -2
  37. mapFolding/someAssemblyRequired/synthesizeNumbaHardcoding.py +0 -188
  38. mapFolding-0.3.12.dist-info/METADATA +0 -155
  39. mapFolding-0.3.12.dist-info/RECORD +0 -40
  40. tests/conftest_tmpRegistry.py +0 -62
  41. tests/conftest_uniformTests.py +0 -53
  42. {mapFolding-0.3.12.dist-info → mapFolding-0.4.0.dist-info}/LICENSE +0 -0
  43. {mapFolding-0.3.12.dist-info → mapFolding-0.4.0.dist-info}/WHEEL +0 -0
  44. {mapFolding-0.3.12.dist-info → mapFolding-0.4.0.dist-info}/entry_points.txt +0 -0
  45. {mapFolding-0.3.12.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
- for n in settingsOEIS[oeisID]['valuesTestValidation']:
20
- standardizedEqualTo(settingsOEIS[oeisID]['valuesKnown'][n], oeisIDfor_n, oeisID, n)
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
- for n in settingsOEIS[oeisID]['valuesTestValidation']:
24
- standardizedEqualTo(settingsOEIS[oeisID]['valuesKnown'][n], oeisIDfor_n, oeisID, n)
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
- standardizedEqualTo(KeyError, _validateOEISid, badID)
28
+ standardizedEqualTo(KeyError, _validateOEISid, badID)
29
29
 
30
30
  def test__validateOEISid_partially_valid(oeisID_1random: str) -> None:
31
- standardizedEqualTo(KeyError, _validateOEISid, f"{oeisID_1random}extra")
31
+ standardizedEqualTo(KeyError, _validateOEISid, f"{oeisID_1random}extra")
32
32
 
33
33
  def test__validateOEISid_valid_id(oeisID: str) -> None:
34
- standardizedEqualTo(oeisID, _validateOEISid, oeisID)
34
+ standardizedEqualTo(oeisID, _validateOEISid, oeisID)
35
35
 
36
36
  def test__validateOEISid_valid_id_case_insensitive(oeisID: str) -> None:
37
- standardizedEqualTo(oeisID.upper(), _validateOEISid, oeisID.lower())
38
- standardizedEqualTo(oeisID.upper(), _validateOEISid, oeisID.upper())
39
- standardizedEqualTo(oeisID.upper(), _validateOEISid, oeisID.swapcase())
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
- # (2, "ok"), # test the test template
43
- (-random.randint(1, 100), "randomNegative"),
44
- ("foo", "string"),
45
- (1.5, "float")
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
- """Check that negative or non-integer n raises ValueError."""
51
- standardizedEqualTo(ValueError, oeisIDfor_n, oeisID_1random, badN)
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
- standardizedEqualTo(ArithmeticError, oeisIDfor_n, 'A001418', 0)
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
- """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()
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
- """Test network error handling."""
76
- def mockUrlopen(*args: Any, **kwargs: Any) -> NoReturn:
77
- raise URLError("Network error")
75
+ """Test network error handling."""
76
+ def mockUrlopen(*args: Any, **kwargs: Any) -> NoReturn:
77
+ raise URLError("Network error")
78
78
 
79
- monkeypatch.setattr(urllib.request, 'urlopen', mockUrlopen)
80
- standardizedEqualTo(URLError, _getOEISidValues, next(iter(settingsOEIS)))
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
- """Test that help text is complete and examples are valid."""
85
- outputStream = io.StringIO()
86
- with redirect_stdout(outputStream):
87
- getOEISids()
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
- helpText = outputStream.getvalue()
89
+ helpText = outputStream.getvalue()
90
90
 
91
- # Verify content
92
- for oeisID in oeisIDsImplemented:
93
- assert oeisID in helpText
94
- assert settingsOEIS[oeisID]['description'] in helpText
91
+ # Verify content
92
+ for oeisID in oeisIDsImplemented:
93
+ assert oeisID in helpText
94
+ assert settingsOEIS[oeisID]['description'] in helpText
95
95
 
96
- # Extract and verify examples
96
+ # Extract and verify examples
97
97
 
98
- cliMatch = regex.search(r'OEIS_for_n (\w+) (\d+)', helpText)
99
- pythonMatch = regex.search(r"oeisIDfor_n\('(\w+)', (\d+)\)", helpText)
98
+ cliMatch = regex.search(r'OEIS_for_n (\w+) (\d+)', helpText)
99
+ pythonMatch = regex.search(r"oeisIDfor_n\('(\w+)', (\d+)\)", helpText)
100
100
 
101
- assert cliMatch and pythonMatch, "Help text missing examples"
102
- oeisID, n = pythonMatch.groups()
103
- n = int(n)
101
+ assert cliMatch and pythonMatch, "Help text missing examples"
102
+ oeisID, n = pythonMatch.groups()
103
+ n = int(n)
104
104
 
105
- # Verify CLI and Python examples use same values
106
- assert cliMatch.groups() == (oeisID, str(n)), "CLI and Python examples inconsistent"
105
+ # Verify CLI and Python examples use same values
106
+ assert cliMatch.groups() == (oeisID, str(n)), "CLI and Python examples inconsistent"
107
107
 
108
- # Verify the example works
109
- expectedValue = oeisIDfor_n(oeisID, n)
108
+ # Verify the example works
109
+ expectedValue = oeisIDfor_n(oeisID, n)
110
110
 
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]))
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
- """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)
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
- """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)
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, Optional, Any, Tuple, Literal, Callable, Generator
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
- (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
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
- """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)
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
- """Test edge cases for getLeavesTotal."""
56
- # Order independence
57
- standardizedEqualTo(getLeavesTotal([2, 3, 4]), getLeavesTotal, [4, 2, 3])
55
+ """Test edge cases for getLeavesTotal."""
56
+ # Order independence
57
+ standardizedEqualTo(getLeavesTotal([2, 3, 4]), getLeavesTotal, [4, 2, 3])
58
58
 
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())
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
- callablePytest()
66
+ callablePytest()
98
67
 
99
68
  @pytest.mark.parametrize("nameOfTest,callablePytest", PytestFor_oopsieKwargsie())
100
69
  def testOopsieKwargsie(nameOfTest: str, callablePytest: Callable[[], None]) -> None:
101
- callablePytest()
70
+ callablePytest()
102
71
 
103
72
  @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),
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
- standardizedEqualTo(expectedLimit, setCPUlimit, CPUlimit)
84
+ standardizedEqualTo(expectedLimit, setCPUlimit, CPUlimit)
116
85
 
117
86
  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
- """
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
- """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."
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
- """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}"
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}"