mapFolding 0.4.3__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 (41) hide show
  1. mapFolding/__init__.py +93 -58
  2. mapFolding/basecamp.py +9 -11
  3. mapFolding/beDRY.py +24 -23
  4. mapFolding/oeis.py +47 -45
  5. mapFolding/theDao.py +48 -48
  6. mapFolding/theSSOT.py +22 -20
  7. mapFolding/theSSOTdatatypes.py +20 -32
  8. {mapFolding-0.4.3.dist-info → mapFolding-0.5.1.dist-info}/METADATA +3 -1
  9. mapFolding-0.5.1.dist-info/RECORD +14 -0
  10. {mapFolding-0.4.3.dist-info → mapFolding-0.5.1.dist-info}/top_level.txt +0 -1
  11. mapFolding/reference/flattened.py +0 -377
  12. mapFolding/reference/hunterNumba.py +0 -132
  13. mapFolding/reference/irvineJavaPort.py +0 -120
  14. mapFolding/reference/jax.py +0 -208
  15. mapFolding/reference/lunnan.py +0 -153
  16. mapFolding/reference/lunnanNumpy.py +0 -123
  17. mapFolding/reference/lunnanWhile.py +0 -121
  18. mapFolding/reference/rotatedEntryPoint.py +0 -240
  19. mapFolding/reference/total_countPlus1vsPlusN.py +0 -211
  20. mapFolding/someAssemblyRequired/__init__.py +0 -5
  21. mapFolding/someAssemblyRequired/getLLVMforNoReason.py +0 -19
  22. mapFolding/someAssemblyRequired/makeJob.py +0 -55
  23. mapFolding/someAssemblyRequired/synthesizeModuleJAX.py +0 -29
  24. mapFolding/someAssemblyRequired/synthesizeNumba.py +0 -340
  25. mapFolding/someAssemblyRequired/synthesizeNumbaGeneralized.py +0 -396
  26. mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +0 -162
  27. mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +0 -129
  28. mapFolding/syntheticModules/numbaCount.py +0 -158
  29. mapFolding/syntheticModules/numba_doTheNeedful.py +0 -13
  30. mapFolding-0.4.3.dist-info/RECORD +0 -40
  31. tests/__init__.py +0 -1
  32. tests/conftest.py +0 -306
  33. tests/test_computations.py +0 -43
  34. tests/test_oeis.py +0 -129
  35. tests/test_other.py +0 -171
  36. tests/test_tasks.py +0 -40
  37. tests/test_types.py +0 -5
  38. /mapFolding/{syntheticModules/__init__.py → py.typed} +0 -0
  39. {mapFolding-0.4.3.dist-info → mapFolding-0.5.1.dist-info}/LICENSE +0 -0
  40. {mapFolding-0.4.3.dist-info → mapFolding-0.5.1.dist-info}/WHEEL +0 -0
  41. {mapFolding-0.4.3.dist-info → mapFolding-0.5.1.dist-info}/entry_points.txt +0 -0
tests/test_oeis.py DELETED
@@ -1,129 +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
- # (2, "ok"), # test the test template
31
- (-random.randint(1, 100), "randomNegative"),
32
- ("foo", "string"),
33
- (1.5, "float")
34
- ]
35
- badValues, badValuesIDs = zip(*parameters_test_aOFn_invalid_n)
36
- @pytest.mark.parametrize("badN", badValues, ids=badValuesIDs)
37
- def test_aOFn_invalid_n(oeisID_1random: str, badN: Any) -> None:
38
- """Check that negative or non-integer n raises ValueError."""
39
- standardizedEqualTo(ValueError, oeisIDfor_n, oeisID_1random, badN)
40
-
41
- def test_aOFn_zeroDim_A001418() -> None:
42
- standardizedEqualTo(ArithmeticError, oeisIDfor_n, 'A001418', 0)
43
-
44
- # ===== OEIS Cache Tests =====
45
- @pytest.mark.parametrize("cacheExists", [True, False])
46
- @unittest.mock.patch('pathlib.Path.exists')
47
- @unittest.mock.patch('pathlib.Path.unlink')
48
- def test_clearOEIScache(mock_unlink: unittest.mock.MagicMock, mock_exists: unittest.mock.MagicMock, cacheExists: bool) -> None:
49
- """Test OEIS cache clearing with both existing and non-existing cache."""
50
- mock_exists.return_value = cacheExists
51
- clearOEIScache()
52
-
53
- if cacheExists:
54
- # Each OEIS ID has two cache files
55
- expected_calls = len(settingsOEIS) * 2
56
- assert mock_unlink.call_count == expected_calls
57
- mock_unlink.assert_has_calls([unittest.mock.call(missing_ok=True)] * expected_calls)
58
- else:
59
- mock_exists.assert_called_once()
60
- mock_unlink.assert_not_called()
61
-
62
- def testNetworkError(monkeypatch: pytest.MonkeyPatch, pathCacheTesting: pathlib.Path) -> None:
63
- """Test network error handling."""
64
- def mockUrlopen(*args: Any, **kwargs: Any) -> NoReturn:
65
- raise URLError("Network error")
66
-
67
- monkeypatch.setattr(urllib.request, 'urlopen', mockUrlopen)
68
- standardizedEqualTo(URLError, getOEISidValues, next(iter(settingsOEIS)))
69
-
70
- # ===== Command Line Interface Tests =====
71
- def testHelpText() -> None:
72
- """Test that help text is complete and examples are valid."""
73
- outputStream = io.StringIO()
74
- with redirect_stdout(outputStream):
75
- getOEISids()
76
-
77
- helpText = outputStream.getvalue()
78
-
79
- # Verify content
80
- for oeisID in oeisIDsImplemented:
81
- assert oeisID in helpText
82
- assert settingsOEIS[oeisID]['description'] in helpText
83
-
84
- # Extract and verify examples
85
-
86
- cliMatch = regex.search(r'OEIS_for_n (\w+) (\d+)', helpText)
87
- pythonMatch = regex.search(r"oeisIDfor_n\('(\w+)', (\d+)\)", helpText)
88
-
89
- assert cliMatch and pythonMatch, "Help text missing examples"
90
- oeisID, n = pythonMatch.groups()
91
- n = int(n)
92
-
93
- # Verify CLI and Python examples use same values
94
- assert cliMatch.groups() == (oeisID, str(n)), "CLI and Python examples inconsistent"
95
-
96
- # Verify the example works
97
- expectedValue = oeisIDfor_n(oeisID, n)
98
-
99
- # Test CLI execution of the example
100
- with unittest.mock.patch('sys.argv', ['OEIS_for_n', oeisID, str(n)]):
101
- outputStream = io.StringIO()
102
- with redirect_stdout(outputStream):
103
- OEIS_for_n()
104
- standardizedEqualTo(expectedValue, lambda: int(outputStream.getvalue().strip().split()[0]))
105
-
106
- def testCLI_InvalidInputs() -> None:
107
- """Test CLI error handling."""
108
- testCases = [
109
- (['OEIS_for_n'], "missing arguments"),
110
- (['OEIS_for_n', 'A999999', '1'], "invalid OEIS ID"),
111
- (['OEIS_for_n', 'A001415', '-1'], "negative n"),
112
- (['OEIS_for_n', 'A001415', 'abc'], "non-integer n"),
113
- ]
114
-
115
- for arguments, testID in testCases:
116
- with unittest.mock.patch('sys.argv', arguments):
117
- standardizedSystemExit("error", OEIS_for_n)
118
-
119
- def testCLI_HelpFlag() -> None:
120
- """Verify --help output contains required information."""
121
- with unittest.mock.patch('sys.argv', ['OEIS_for_n', '--help']):
122
- outputStream = io.StringIO()
123
- with redirect_stdout(outputStream):
124
- standardizedSystemExit("nonError", OEIS_for_n)
125
-
126
- helpOutput = outputStream.getvalue()
127
- assert "Available OEIS sequences:" in helpOutput
128
- assert "Usage examples:" in helpOutput
129
- assert all(oeisID in helpOutput for oeisID in oeisIDsImplemented)
tests/test_other.py DELETED
@@ -1,171 +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, ...], 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:
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)
52
-
53
- def test_getLeavesTotal_edge_cases() -> None:
54
- """Test edge cases for getLeavesTotal."""
55
- # Order independence
56
- standardizedEqualTo(getLeavesTotal([2, 3, 4]), getLeavesTotal, [4, 2, 3])
57
-
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
62
-
63
- @pytest.mark.parametrize("nameOfTest,callablePytest", PytestFor_intInnit())
64
- def testIntInnit(nameOfTest: str, callablePytest: Callable[[], None]) -> None:
65
- callablePytest()
66
-
67
- @pytest.mark.parametrize("nameOfTest,callablePytest", PytestFor_oopsieKwargsie())
68
- def testOopsieKwargsie(nameOfTest: str, callablePytest: Callable[[], None]) -> None:
69
- callablePytest()
70
-
71
- @pytest.mark.parametrize("CPUlimit, expectedLimit", [
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),
81
- ])
82
- def test_setCPUlimit(CPUlimit: None | float | bool | Literal[4] | Literal[-2] | Literal[0] | Literal[1], expectedLimit: Any | int) -> None:
83
- standardizedEqualTo(expectedLimit, setCPUlimit, CPUlimit)
84
-
85
- def test_makeConnectionGraph_nonNegative(listDimensionsTestFunctionality: List[int]) -> None:
86
- connectionGraph = makeConnectionGraph(listDimensionsTestFunctionality)
87
- assert numpy.all(connectionGraph >= 0), "All values in the connection graph should be non-negative."
88
-
89
- @pytest.fixture
90
- def parameterIterator() -> Callable[[List[int]], Generator[Dict[str, Any], None, None]]:
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."
165
-
166
- def test_makeDataContainer_default_datatype() -> None:
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 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)
tests/test_types.py DELETED
@@ -1,5 +0,0 @@
1
- """Type checking tests for mapFolding package."""
2
-
3
- def test_static_typing() -> None:
4
- """This is a placeholder. pytest-mypy will run type checking automatically."""
5
- pass
File without changes