mapFolding 0.5.0__py3-none-any.whl → 0.5.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 (39) hide show
  1. mapFolding/__init__.py +93 -58
  2. mapFolding/basecamp.py +5 -5
  3. mapFolding/beDRY.py +5 -5
  4. mapFolding/oeis.py +26 -25
  5. mapFolding/theSSOT.py +3 -15
  6. mapFolding/theSSOTdatatypes.py +10 -20
  7. {mapFolding-0.5.0.dist-info → mapFolding-0.5.1.dist-info}/METADATA +2 -1
  8. mapFolding-0.5.1.dist-info/RECORD +14 -0
  9. {mapFolding-0.5.0.dist-info → mapFolding-0.5.1.dist-info}/top_level.txt +0 -1
  10. mapFolding/reference/flattened.py +0 -377
  11. mapFolding/reference/hunterNumba.py +0 -132
  12. mapFolding/reference/irvineJavaPort.py +0 -120
  13. mapFolding/reference/jax.py +0 -208
  14. mapFolding/reference/lunnan.py +0 -153
  15. mapFolding/reference/lunnanNumpy.py +0 -123
  16. mapFolding/reference/lunnanWhile.py +0 -121
  17. mapFolding/reference/rotatedEntryPoint.py +0 -240
  18. mapFolding/reference/total_countPlus1vsPlusN.py +0 -211
  19. mapFolding/someAssemblyRequired/__init__.py +0 -5
  20. mapFolding/someAssemblyRequired/getLLVMforNoReason.py +0 -19
  21. mapFolding/someAssemblyRequired/makeJob.py +0 -56
  22. mapFolding/someAssemblyRequired/synthesizeModuleJAX.py +0 -27
  23. mapFolding/someAssemblyRequired/synthesizeNumba.py +0 -345
  24. mapFolding/someAssemblyRequired/synthesizeNumbaGeneralized.py +0 -397
  25. mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +0 -155
  26. mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +0 -123
  27. mapFolding/syntheticModules/numbaCount.py +0 -158
  28. mapFolding/syntheticModules/numba_doTheNeedful.py +0 -13
  29. mapFolding-0.5.0.dist-info/RECORD +0 -39
  30. tests/__init__.py +0 -1
  31. tests/conftest.py +0 -335
  32. tests/test_computations.py +0 -42
  33. tests/test_oeis.py +0 -128
  34. tests/test_other.py +0 -175
  35. tests/test_tasks.py +0 -40
  36. /mapFolding/{syntheticModules/__init__.py → py.typed} +0 -0
  37. {mapFolding-0.5.0.dist-info → mapFolding-0.5.1.dist-info}/LICENSE +0 -0
  38. {mapFolding-0.5.0.dist-info → mapFolding-0.5.1.dist-info}/WHEEL +0 -0
  39. {mapFolding-0.5.0.dist-info → mapFolding-0.5.1.dist-info}/entry_points.txt +0 -0
tests/test_oeis.py DELETED
@@ -1,128 +0,0 @@
1
- from contextlib import redirect_stdout
2
- from tests.conftest import *
3
- from urllib.error import URLError
4
- import io
5
- import pathlib
6
- import pytest
7
- import random
8
- import re as regex
9
- import unittest
10
- import unittest.mock
11
- import urllib
12
- import urllib.request
13
-
14
- @pytest.mark.parametrize("badID", ["A999999", " A999999 ", "A999999extra"])
15
- def test__validateOEISid_invalid_id(badID: str) -> None:
16
- standardizedEqualTo(KeyError, validateOEISid, badID)
17
-
18
- def test__validateOEISid_partially_valid(oeisID_1random: str) -> None:
19
- standardizedEqualTo(KeyError, validateOEISid, f"{oeisID_1random}extra")
20
-
21
- def test__validateOEISid_valid_id(oeisID: str) -> None:
22
- standardizedEqualTo(oeisID, validateOEISid, oeisID)
23
-
24
- def test__validateOEISid_valid_id_case_insensitive(oeisID: str) -> None:
25
- standardizedEqualTo(oeisID.upper(), validateOEISid, oeisID.lower())
26
- standardizedEqualTo(oeisID.upper(), validateOEISid, oeisID.upper())
27
- standardizedEqualTo(oeisID.upper(), validateOEISid, oeisID.swapcase())
28
-
29
- parameters_test_aOFn_invalid_n = [
30
- (-random.randint(1, 100), "randomNegative"),
31
- ("foo", "string"),
32
- (1.5, "float")
33
- ]
34
- badValues, badValuesIDs = zip(*parameters_test_aOFn_invalid_n)
35
- @pytest.mark.parametrize("badN", badValues, ids=badValuesIDs)
36
- def test_aOFn_invalid_n(oeisID_1random: str, badN: Any) -> None:
37
- """Check that negative or non-integer n raises ValueError."""
38
- standardizedEqualTo(ValueError, oeisIDfor_n, oeisID_1random, badN)
39
-
40
- def test_aOFn_zeroDim_A001418() -> None:
41
- standardizedEqualTo(ArithmeticError, oeisIDfor_n, 'A001418', 0)
42
-
43
- # ===== OEIS Cache Tests =====
44
- @pytest.mark.parametrize("cacheExists", [True, False])
45
- @unittest.mock.patch('pathlib.Path.exists')
46
- @unittest.mock.patch('pathlib.Path.unlink')
47
- def test_clearOEIScache(mock_unlink: unittest.mock.MagicMock, mock_exists: unittest.mock.MagicMock, cacheExists: bool) -> None:
48
- """Test OEIS cache clearing with both existing and non-existing cache."""
49
- mock_exists.return_value = cacheExists
50
- clearOEIScache()
51
-
52
- if cacheExists:
53
- # Each OEIS ID has two cache files
54
- expected_calls = len(settingsOEIS) * 2
55
- assert mock_unlink.call_count == expected_calls
56
- mock_unlink.assert_has_calls([unittest.mock.call(missing_ok=True)] * expected_calls)
57
- else:
58
- mock_exists.assert_called_once()
59
- mock_unlink.assert_not_called()
60
-
61
- def testNetworkError(monkeypatch: pytest.MonkeyPatch, pathCacheTesting: Path) -> None:
62
- """Test network error handling."""
63
- def mockUrlopen(*args: Any, **kwargs: Any) -> NoReturn:
64
- raise URLError("Network error")
65
-
66
- monkeypatch.setattr(urllib.request, 'urlopen', mockUrlopen)
67
- standardizedEqualTo(URLError, getOEISidValues, next(iter(settingsOEIS)))
68
-
69
- # ===== Command Line Interface Tests =====
70
- def testHelpText() -> None:
71
- """Test that help text is complete and examples are valid."""
72
- outputStream = io.StringIO()
73
- with redirect_stdout(outputStream):
74
- getOEISids()
75
-
76
- helpText = outputStream.getvalue()
77
-
78
- # Verify content
79
- for oeisID in oeisIDsImplemented:
80
- assert oeisID in helpText
81
- assert settingsOEIS[oeisID]['description'] in helpText
82
-
83
- # Extract and verify examples
84
-
85
- cliMatch = regex.search(r'OEIS_for_n (\w+) (\d+)', helpText)
86
- pythonMatch = regex.search(r"oeisIDfor_n\('(\w+)', (\d+)\)", helpText)
87
-
88
- assert cliMatch and pythonMatch, "Help text missing examples"
89
- oeisID, n = pythonMatch.groups()
90
- n = int(n)
91
-
92
- # Verify CLI and Python examples use same values
93
- assert cliMatch.groups() == (oeisID, str(n)), "CLI and Python examples inconsistent"
94
-
95
- # Verify the example works
96
- expectedValue = oeisIDfor_n(oeisID, n)
97
-
98
- # Test CLI execution of the example
99
- with unittest.mock.patch('sys.argv', ['OEIS_for_n', oeisID, str(n)]):
100
- outputStream = io.StringIO()
101
- with redirect_stdout(outputStream):
102
- OEIS_for_n()
103
- standardizedEqualTo(expectedValue, lambda: int(outputStream.getvalue().strip().split()[0]))
104
-
105
- def testCLI_InvalidInputs() -> None:
106
- """Test CLI error handling."""
107
- testCases = [
108
- (['OEIS_for_n'], "missing arguments"),
109
- (['OEIS_for_n', 'A999999', '1'], "invalid OEIS ID"),
110
- (['OEIS_for_n', 'A001415', '-1'], "negative n"),
111
- (['OEIS_for_n', 'A001415', 'abc'], "non-integer n"),
112
- ]
113
-
114
- for arguments, testID in testCases:
115
- with unittest.mock.patch('sys.argv', arguments):
116
- standardizedSystemExit("error", OEIS_for_n)
117
-
118
- def testCLI_HelpFlag() -> None:
119
- """Verify --help output contains required information."""
120
- with unittest.mock.patch('sys.argv', ['OEIS_for_n', '--help']):
121
- outputStream = io.StringIO()
122
- with redirect_stdout(outputStream):
123
- standardizedSystemExit("nonError", OEIS_for_n)
124
-
125
- helpOutput = outputStream.getvalue()
126
- assert "Available OEIS sequences:" in helpOutput
127
- assert "Usage examples:" in helpOutput
128
- assert all(oeisID in helpOutput for oeisID in oeisIDsImplemented)
tests/test_other.py DELETED
@@ -1,175 +0,0 @@
1
- from contextlib import redirect_stdout
2
- from tests.conftest import *
3
- from Z0Z_tools import intInnit
4
- import io
5
- import itertools
6
- import numba
7
- import numpy
8
- import pathlib
9
- import pytest
10
- import random
11
- import sys
12
-
13
- @pytest.mark.parametrize("listDimensions,expected_intInnit,expected_parseListDimensions,expected_validateListDimensions,expected_getLeavesTotal", [
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
45
- ])
46
- def test_listDimensionsAsParameter(listDimensions: None | list[str] | list[int] | list[float] | list[None] | list[bool] | list[list[int]] | list[complex] | range | tuple[int, ...],
47
- expected_intInnit: type[ValueError] | list[int] | type[TypeError],
48
- expected_parseListDimensions: type[ValueError] | list[int] | type[TypeError],
49
- expected_validateListDimensions: type[ValueError] | type[NotImplementedError] | list[int] | type[TypeError],
50
- expected_getLeavesTotal: type[ValueError] | int | type[TypeError] | type[OverflowError]) -> None:
51
- """Test both validateListDimensions and getLeavesTotal with the same inputs."""
52
- standardizedEqualTo(expected_intInnit, intInnit, listDimensions)
53
- standardizedEqualTo(expected_parseListDimensions, parseDimensions, listDimensions)
54
- standardizedEqualTo(expected_validateListDimensions, validateListDimensions, listDimensions)
55
- standardizedEqualTo(expected_getLeavesTotal, getLeavesTotal, listDimensions)
56
-
57
- def test_getLeavesTotal_edge_cases() -> None:
58
- """Test edge cases for getLeavesTotal."""
59
- # Order independence
60
- standardizedEqualTo(getLeavesTotal([2, 3, 4]), getLeavesTotal, [4, 2, 3])
61
-
62
- # Immutability
63
- listOriginal = [2, 3]
64
- standardizedEqualTo(6, getLeavesTotal, listOriginal)
65
- standardizedEqualTo([2, 3], lambda x: x, listOriginal) # Check that the list wasn't modified
66
-
67
- @pytest.mark.parametrize("nameOfTest,callablePytest", PytestFor_intInnit())
68
- def testIntInnit(nameOfTest: str, callablePytest: Callable[[], None]) -> None:
69
- callablePytest()
70
-
71
- @pytest.mark.parametrize("nameOfTest,callablePytest", PytestFor_oopsieKwargsie())
72
- def testOopsieKwargsie(nameOfTest: str, callablePytest: Callable[[], None]) -> None:
73
- callablePytest()
74
-
75
- @pytest.mark.parametrize("CPUlimit, expectedLimit", [
76
- (None, numba.get_num_threads()),
77
- (False, numba.get_num_threads()),
78
- (True, 1),
79
- (4, 4),
80
- (0.5, max(1, numba.get_num_threads() // 2)),
81
- (-0.5, max(1, numba.get_num_threads() // 2)),
82
- (-2, max(1, numba.get_num_threads() - 2)),
83
- (0, numba.get_num_threads()),
84
- (1, 1),
85
- ])
86
- def test_setCPUlimit(CPUlimit: None | float | bool | Literal[4] | Literal[-2] | Literal[0] | Literal[1], expectedLimit: Any | int) -> None:
87
- standardizedEqualTo(expectedLimit, setCPUlimit, CPUlimit)
88
-
89
- def test_makeConnectionGraph_nonNegative(listDimensionsTestFunctionality: list[int]) -> None:
90
- connectionGraph = makeConnectionGraph(listDimensionsTestFunctionality)
91
- assert numpy.all(connectionGraph >= 0), "All values in the connection graph should be non-negative."
92
-
93
- @pytest.fixture
94
- def parameterIterator() -> Callable[[list[int]], Generator[dict[str, Any], None, None]]:
95
- """Generate random combinations of parameters for outfitCountFolds testing."""
96
- parameterSets: dict[str, list[Any]] = {
97
- 'computationDivisions': [
98
- None,
99
- 'maximum',
100
- 'cpu',
101
- ],
102
- 'CPUlimit': [
103
- None, True, False, 0, 1, -1,
104
- ],
105
- 'datatypeMedium': [
106
- None,
107
- numpy.int64,
108
- numpy.intc,
109
- numpy.uint16
110
- ],
111
- 'datatypeLarge': [
112
- None,
113
- numpy.int64,
114
- numpy.intp,
115
- numpy.uint32
116
- ]
117
- }
118
-
119
- def makeParametersDynamic(listDimensions: list[int]) -> dict[str, list[Any]]:
120
- """Add context-dependent parameter values."""
121
- parametersDynamic = parameterSets.copy()
122
- leavesTotal = getLeavesTotal(listDimensions)
123
- concurrencyLimit = min(leavesTotal, 16)
124
-
125
- # Add dynamic computationDivisions values
126
- dynamicDivisions = [random.randint(2, leavesTotal-1) for iterator in range(3)]
127
- parametersDynamic['computationDivisions'] = parametersDynamic['computationDivisions'] + dynamicDivisions
128
-
129
- # Add dynamic CPUlimit values
130
- parameterDynamicCPU = [
131
- random.random(), # 0 to 1
132
- -random.random(), # -1 to 0
133
- ]
134
- parameterDynamicCPU.extend(
135
- [random.randint(2, concurrencyLimit-1) for iterator in range(2)]
136
- )
137
- parameterDynamicCPU.extend(
138
- [random.randint(-concurrencyLimit+1, -2) for iterator in range(2)]
139
- )
140
- parametersDynamic['CPUlimit'] = parametersDynamic['CPUlimit'] + parameterDynamicCPU
141
-
142
- return parametersDynamic
143
-
144
- def generateCombinations(listDimensions: list[int]) -> Generator[dict[str, Any], None, None]:
145
- parametersDynamic = makeParametersDynamic(listDimensions)
146
- parameterKeys = list(parametersDynamic.keys())
147
- parameterValues = [parametersDynamic[key] for key in parameterKeys]
148
-
149
- # Shuffle each parameter list
150
- for valueList in parameterValues:
151
- random.shuffle(valueList)
152
-
153
- # Use zip_longest to iterate, filling with None when shorter lists are exhausted
154
- for combination in itertools.zip_longest(*parameterValues, fillvalue=None):
155
- yield dict(zip(parameterKeys, combination))
156
-
157
- return generateCombinations
158
-
159
- def test_saveFoldsTotal_fallback(pathTmpTesting: Path) -> None:
160
- foldsTotal = 123
161
- pathFilename = pathTmpTesting / "foldsTotal.txt"
162
- with unittest.mock.patch("pathlib.Path.write_text", side_effect=OSError("Simulated write failure")):
163
- with unittest.mock.patch("os.getcwd", return_value=str(pathTmpTesting)):
164
- capturedOutput = io.StringIO()
165
- with redirect_stdout(capturedOutput):
166
- saveFoldsTotal(pathFilename, foldsTotal)
167
- fallbackFiles = list(pathTmpTesting.glob("foldsTotalYO_*.txt"))
168
- assert len(fallbackFiles) == 1, "Fallback file was not created upon write failure."
169
-
170
- def test_makeDataContainer_default_datatype() -> None:
171
- """Test that makeDataContainer uses dtypeLargeDEFAULT when no datatype is specified."""
172
- testShape = (3, 4)
173
- container = makeDataContainer(testShape)
174
- assert container.dtype == hackSSOTdtype('dtypeFoldsTotal'), f"Expected datatype but got {container.dtype}"
175
- assert container.shape == testShape, f"Expected shape {testShape}, but got {container.shape}"
tests/test_tasks.py DELETED
@@ -1,40 +0,0 @@
1
- from tests.conftest import *
2
- import pytest
3
-
4
- # TODO add a test. `C` = number of logical cores available. `n = C + 1`. Ensure that `[2,n]` is computed correctly.
5
- # Or, probably smarter: limit the number of cores, then run a test with C+1.
6
-
7
- def test_countFoldsComputationDivisionsInvalid(listDimensionsTestFunctionality: list[int]) -> None:
8
- standardizedEqualTo(ValueError, countFolds, listDimensionsTestFunctionality, None, {"wrong": "value"})
9
-
10
- def test_countFoldsComputationDivisionsMaximum(listDimensionsTestParallelization: list[int], foldsTotalKnown: dict[tuple[int, ...], int]) -> None:
11
- standardizedEqualTo(foldsTotalKnown[tuple(listDimensionsTestParallelization)], countFolds, listDimensionsTestParallelization, None, 'maximum')
12
-
13
- @pytest.mark.parametrize("nameOfTest,callablePytest", PytestFor_defineConcurrencyLimit())
14
- def test_defineConcurrencyLimit(nameOfTest: str, callablePytest: Callable[[], None]) -> None:
15
- callablePytest()
16
-
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)
20
-
21
- @pytest.mark.parametrize("computationDivisions, concurrencyLimit, listDimensions, expectedTaskDivisions", [
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)
27
- ])
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:
29
- standardizedEqualTo(expectedTaskDivisions, getTaskDivisions, computationDivisions, concurrencyLimit, None, listDimensions)
30
-
31
- @pytest.mark.parametrize("expected,parameter", [
32
- (2, "2"), # string
33
- (ValueError, [4]), # list
34
- (ValueError, (2,)), # tuple
35
- (ValueError, {2}), # set
36
- (ValueError, {"cores": 2}), # dict
37
- ])
38
- def test_setCPUlimitMalformedParameter(expected: type[ValueError] | Literal[2], parameter: list[int] | tuple[int] | set[int] | dict[str, int] | Literal['2']) -> None:
39
- """Test that invalid CPUlimit types are properly handled."""
40
- standardizedEqualTo(expected, setCPUlimit, parameter)
File without changes