mapFolding 0.2.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/beDRY.py ADDED
@@ -0,0 +1,219 @@
1
+ """A relatively stable API for oft-needed functionality."""
2
+ from mapFolding.importPackages import intInnit, defineConcurrencyLimit, oopsieKwargsie
3
+ from mapFolding import indexMy, indexThe, indexTrack, computationState
4
+ from typing import Any, List, Optional, Sequence, Type, Union
5
+ import numpy
6
+ import numba
7
+ import numba.extending
8
+ import numpy.typing
9
+ import sys
10
+
11
+ def getLeavesTotal(listDimensions: Sequence[int]) -> int:
12
+ """
13
+ Calculate the product of non-zero, non-negative integers in the given list.
14
+
15
+ Parameters:
16
+ listDimensions: A list of integers representing dimensions.
17
+
18
+ Returns:
19
+ productDimensions: The product of all positive integer dimensions. Returns 0 if all dimensions are 0.
20
+ """
21
+ listNonNegative = parseDimensions(listDimensions, 'listDimensions')
22
+ listPositive = [dimension for dimension in listNonNegative if dimension > 0]
23
+
24
+ if not listPositive:
25
+ return 0
26
+ else:
27
+ productDimensions = 1
28
+ for dimension in listPositive:
29
+ if dimension > sys.maxsize // productDimensions:
30
+ raise OverflowError("Product would exceed maximum integer size")
31
+ productDimensions *= dimension
32
+
33
+ return productDimensions
34
+
35
+ def getTaskDivisions(CPUlimit, computationDivisions: Optional[Union[int, str]], concurrencyLimit: int, listDimensions, the: numpy.typing.NDArray[numpy.integer[Any]], ):
36
+ # TODO remove after restructuring the tests
37
+ if isinstance(computationDivisions, bool) and computationDivisions:
38
+ computationDivisions = "maximum"
39
+
40
+ if not computationDivisions:
41
+ # Coding it this way should cover `None`, `False`, and `0`.
42
+ the[indexThe.taskDivisions] = 0
43
+ elif isinstance(computationDivisions, int):
44
+ the[indexThe.taskDivisions] = computationDivisions
45
+ elif isinstance(computationDivisions, str):
46
+ computationDivisions = computationDivisions.lower()
47
+ if computationDivisions == "maximum":
48
+ the[indexThe.taskDivisions] = the[indexThe.leavesTotal]
49
+ elif computationDivisions == "cpu":
50
+ the[indexThe.taskDivisions] = min(concurrencyLimit, the[indexThe.leavesTotal])
51
+ else:
52
+ raise ValueError(f"I received {computationDivisions} for the parameter, `computationDivisions`, but the so-called programmer didn't implement code for that.")
53
+
54
+ if the[indexThe.taskDivisions] > the[indexThe.leavesTotal]:
55
+ raise ValueError(f"Problem: `taskDivisions`, ({the[indexThe.taskDivisions]}), is greater than `leavesTotal`, ({the[indexThe.leavesTotal]}), which will cause duplicate counting of the folds.\n\nChallenge: you cannot directly set `taskDivisions` or `leavesTotal`. They are derived from parameters that may or may not still be named `computationDivisions`, `CPUlimit` , and `listDimensions` and from dubious-quality Python code.\n\nFor those parameters, I received {computationDivisions=}, {CPUlimit=}, and {listDimensions=}.\n\nPotential solutions: get a different hobby or set `computationDivisions` to a different value.")
56
+
57
+ return the
58
+
59
+ def makeConnectionGraph(listDimensions: Sequence[int], dtype: Optional[Type] = numpy.int64) -> numpy.typing.NDArray[numpy.integer[Any]]:
60
+ """
61
+ Constructs a connection graph for a given list of dimensions.
62
+ This function generates a multi-dimensional connection graph based on the provided list of dimensions.
63
+ The graph represents the connections between leaves in a Cartesian product decomposition or dimensional product mapping.
64
+
65
+ Parameters:
66
+ listDimensions: A validated sequence of integers representing the dimensions of the map.
67
+ Returns:
68
+ connectionGraph: A 3D numpy array with shape of (dimensionsTotal + 1, leavesTotal + 1, leavesTotal + 1).
69
+ """
70
+ leavesTotal = getLeavesTotal(listDimensions)
71
+ arrayDimensions = numpy.array(listDimensions, dtype=dtype)
72
+ dimensionsTotal = len(arrayDimensions)
73
+
74
+ # Step 1: find the cumulative product of the map's dimensions
75
+ cumulativeProduct = numpy.ones(dimensionsTotal + 1, dtype=dtype)
76
+ for index in range(1, dimensionsTotal + 1):
77
+ cumulativeProduct[index] = cumulativeProduct[index - 1] * arrayDimensions[index - 1]
78
+
79
+ # Step 2: create a coordinate system
80
+ coordinateSystem = numpy.zeros((dimensionsTotal + 1, leavesTotal + 1), dtype=dtype)
81
+
82
+ for dimension1ndex in range(1, dimensionsTotal + 1):
83
+ for leaf1ndex in range(1, leavesTotal + 1):
84
+ coordinateSystem[dimension1ndex, leaf1ndex] = (
85
+ ((leaf1ndex - 1) // cumulativeProduct[dimension1ndex - 1]) %
86
+ arrayDimensions[dimension1ndex - 1] + 1
87
+ )
88
+
89
+ # Step 3: create and fill the connection graph
90
+ connectionGraph = numpy.zeros((dimensionsTotal + 1, leavesTotal + 1, leavesTotal + 1), dtype=dtype)
91
+
92
+ for dimension1ndex in range(1, dimensionsTotal + 1):
93
+ for activeLeaf1ndex in range(1, leavesTotal + 1):
94
+ for connectee1ndex in range(1, activeLeaf1ndex + 1):
95
+ # Base coordinate conditions
96
+ isFirstCoord = coordinateSystem[dimension1ndex, connectee1ndex] == 1
97
+ isLastCoord = coordinateSystem[dimension1ndex, connectee1ndex] == arrayDimensions[dimension1ndex - 1]
98
+ exceedsActive = connectee1ndex + cumulativeProduct[dimension1ndex - 1] > activeLeaf1ndex
99
+
100
+ # Parity check
101
+ isEvenParity = (coordinateSystem[dimension1ndex, activeLeaf1ndex] & 1) == \
102
+ (coordinateSystem[dimension1ndex, connectee1ndex] & 1)
103
+
104
+ # Determine connection value
105
+ if (isEvenParity and isFirstCoord) or (not isEvenParity and (isLastCoord or exceedsActive)):
106
+ connectionGraph[dimension1ndex, activeLeaf1ndex, connectee1ndex] = connectee1ndex
107
+ elif isEvenParity and not isFirstCoord:
108
+ connectionGraph[dimension1ndex, activeLeaf1ndex, connectee1ndex] = connectee1ndex - cumulativeProduct[dimension1ndex - 1]
109
+ elif not isEvenParity and not (isLastCoord or exceedsActive):
110
+ connectionGraph[dimension1ndex, activeLeaf1ndex, connectee1ndex] = connectee1ndex + cumulativeProduct[dimension1ndex - 1]
111
+ else:
112
+ connectionGraph[dimension1ndex, activeLeaf1ndex, connectee1ndex] = connectee1ndex
113
+
114
+ return connectionGraph
115
+
116
+ def outfitFoldings(
117
+ listDimensions: Sequence[int],
118
+ computationDivisions: Optional[Union[int, str]] = None,
119
+ CPUlimit: Optional[Union[int, float, bool]] = None,
120
+ dtypeDefault: Optional[Type] = numpy.int64, # TODO consider allowing a type or a "signal", such as "minimum", "safe", "maximum"
121
+ dtypeLarge: Optional[Type] = numpy.int64, # Can/should I use numba types?
122
+ ) -> computationState:
123
+ the = numpy.zeros(len(indexThe), dtype=dtypeDefault)
124
+
125
+ mapShape = tuple(sorted(validateListDimensions(listDimensions)))
126
+ the[indexThe.leavesTotal] = getLeavesTotal(mapShape)
127
+ the[indexThe.dimensionsTotal] = len(mapShape)
128
+ concurrencyLimit = setCPUlimit(CPUlimit)
129
+
130
+ the = getTaskDivisions(CPUlimit, computationDivisions, concurrencyLimit, listDimensions, the)
131
+
132
+ stateInitialized = computationState(
133
+ connectionGraph = makeConnectionGraph(mapShape, dtype=dtypeDefault),
134
+ foldsTotal = numpy.zeros(the[indexThe.leavesTotal], dtype=numpy.int64),
135
+ mapShape = mapShape,
136
+ my = numpy.zeros(len(indexMy), dtype=dtypeLarge),
137
+ gapsWhere = numpy.zeros(int(the[indexThe.leavesTotal]) * int(the[indexThe.leavesTotal]) + 1, dtype=dtypeDefault),
138
+ the = the,
139
+ track = numpy.zeros((len(indexTrack), the[indexThe.leavesTotal] + 1), dtype=dtypeLarge)
140
+ )
141
+
142
+ stateInitialized['my'][indexMy.leaf1ndex.value] = 1
143
+
144
+ return stateInitialized
145
+
146
+ def parseDimensions(dimensions: Sequence[int], parameterName: str = 'unnamed parameter') -> List[int]:
147
+ """
148
+ Parse and validate a list of dimensions.
149
+
150
+ Parameters:
151
+ listDimensions: List of integers representing dimensions
152
+ parameterName ('unnamed parameter'): Name of the parameter for error messages. Defaults to 'unnamed parameter'
153
+ Returns:
154
+ listNonNegative: List of validated non-negative integers
155
+ Raises:
156
+ ValueError: If any dimension is negative or if the list is empty
157
+ TypeError: If any element cannot be converted to integer (raised by intInnit)
158
+ """
159
+ listValidated = intInnit(dimensions, parameterName)
160
+ listNonNegative = []
161
+ for dimension in listValidated:
162
+ if dimension < 0:
163
+ raise ValueError(f"Dimension {dimension} must be non-negative")
164
+ listNonNegative.append(dimension)
165
+
166
+ if not listNonNegative:
167
+ raise ValueError("At least one dimension must be non-negative")
168
+
169
+ return listNonNegative
170
+
171
+ def setCPUlimit(CPUlimit: Union[int, float, bool, None]):
172
+ """Sets CPU limit for concurrent operations using Numba.
173
+ This function configures the number of CPU threads that Numba can use for parallel execution.
174
+ Note that this setting only affects Numba-jitted functions that have not yet been imported.
175
+ Parameters:
176
+ CPUlimit (Union[int, float, bool, None]): The CPU limit to set.
177
+ - If int/float: Specifies number of CPU threads to use
178
+ - If bool: True uses all available CPUs, False uses 1 CPU
179
+ - If None: Uses system default
180
+ Returns:
181
+ concurrencyLimit: The actual concurrency limit that was set
182
+ Raises:
183
+ TypeError: If CPUlimit is not of the expected types
184
+ """
185
+ if not (CPUlimit is None or isinstance(CPUlimit, (bool, int, float))):
186
+ CPUlimit = oopsieKwargsie(CPUlimit)
187
+
188
+ concurrencyLimit = defineConcurrencyLimit(CPUlimit)
189
+ # NOTE `set_num_threads` only affects "jitted" functions that have _not_ yet been "imported"
190
+ numba.set_num_threads(concurrencyLimit)
191
+
192
+ return concurrencyLimit
193
+
194
+ def validateListDimensions(listDimensions: Sequence[int]) -> List[int]:
195
+ """
196
+ Validates and processes a list of dimensions.
197
+
198
+ This function ensures that the input list of dimensions is not None,
199
+ parses it to ensure all dimensions are non-negative, and then filters
200
+ out any dimensions that are not greater than zero. If the resulting
201
+ list has fewer than two dimensions, a NotImplementedError is raised.
202
+
203
+ Parameters:
204
+ listDimensions: A list of integer dimensions to be validated.
205
+
206
+ Returns:
207
+ validDimensions: A list, with at least two elements, of only positive integers.
208
+
209
+ Raises:
210
+ ValueError: If the input listDimensions is None.
211
+ NotImplementedError: If the resulting list of positive dimensions has fewer than two elements.
212
+ """
213
+ if not listDimensions:
214
+ raise ValueError(f"listDimensions is a required parameter.")
215
+ listNonNegative = parseDimensions(listDimensions, 'listDimensions')
216
+ validDimensions = [dimension for dimension in listNonNegative if dimension > 0]
217
+ if len(validDimensions) < 2:
218
+ raise NotImplementedError(f"This function requires listDimensions, {listDimensions}, to have at least two dimensions greater than 0. You may want to look at https://oeis.org/.")
219
+ return validDimensions
@@ -0,0 +1,66 @@
1
+ import multiprocessing
2
+ from typing import Callable
3
+ import numpy
4
+ import pathlib
5
+ import time
6
+
7
+ pathRecordedBenchmarks = pathlib.Path('mapFolding/benchmarks/marks')
8
+ pathRecordedBenchmarks.mkdir(parents=True, exist_ok=True)
9
+ pathFilenameRecordedBenchmarks = pathRecordedBenchmarks / "benchmarks.npy"
10
+
11
+ def recordBenchmarks():
12
+ """Decorator to benchmark a function."""
13
+ def AzeemTheWrapper(functionTarget: Callable):
14
+ def djZeph(*arguments, **keywordArguments):
15
+ timeStart = time.perf_counter_ns()
16
+ returnValueTarget = functionTarget(*arguments, **keywordArguments)
17
+ timeElapsed = (time.perf_counter_ns() - timeStart) / 1e9
18
+
19
+ # Extract mapShape from arguments
20
+ mapShape = keywordArguments['mapShape']
21
+ # mapShape = tuple(arguments)[2]
22
+ # leavesTotal = tuple(arguments[3])[4]
23
+
24
+ # Store benchmark data in single file
25
+ benchmarkEntry = numpy.array([(timeElapsed, mapShape)], dtype=[('time', 'f8'), ('mapShape', 'O')])
26
+ # benchmarkEntry = numpy.array([(timeElapsed, leavesTotal)], dtype=[('time', 'f8'), ('leaves', 'O')])
27
+
28
+ if pathFilenameRecordedBenchmarks.exists():
29
+ arrayExisting = numpy.load(str(pathFilenameRecordedBenchmarks), allow_pickle=True)
30
+ arrayBenchmark = numpy.concatenate([arrayExisting, benchmarkEntry])
31
+ else:
32
+ arrayBenchmark = benchmarkEntry
33
+
34
+ numpy.save(str(pathFilenameRecordedBenchmarks), arrayBenchmark)
35
+ return returnValueTarget
36
+
37
+ return djZeph
38
+ return AzeemTheWrapper
39
+
40
+ def runBenchmarks(benchmarkIterations: int = 30) -> None:
41
+ """Run benchmark iterations.
42
+
43
+ Parameters:
44
+ benchmarkIterations (30): Number of benchmark iterations to run
45
+ """
46
+ # TODO warmUp (False): Whether to perform one warm-up iteration
47
+
48
+ import itertools
49
+ from tqdm.auto import tqdm
50
+ from mapFolding.oeis import settingsOEIS, oeisIDfor_n
51
+ from concurrent.futures import ProcessPoolExecutor, as_completed
52
+ max_workers = 6
53
+
54
+ listParametersOEIS = [(oeisIdentifier, dimensionValue) for oeisIdentifier, settings in settingsOEIS.items() for dimensionValue in settings['valuesBenchmark']]
55
+ # for (oeisIdentifier, dimensionValue), iterationIndex in tqdm(itertools.product(listParametersOEIS, range(benchmarkIterations)), total=len(listParametersOEIS) * benchmarkIterations):
56
+ # oeisIDfor_n(oeisIdentifier, dimensionValue)
57
+ listCartesianProduct = list(itertools.product(listParametersOEIS, range(benchmarkIterations)))
58
+ with ProcessPoolExecutor(max_workers) as concurrencyManager:
59
+ listConcurrency = [concurrencyManager.submit(oeisIDfor_n, *parameters[0]) for parameters in listCartesianProduct]
60
+ for complete in tqdm(as_completed(listConcurrency), total=len(listCartesianProduct)):
61
+ pass
62
+
63
+ if __name__ == '__main__':
64
+ multiprocessing.set_start_method('spawn')
65
+ pathFilenameRecordedBenchmarks.unlink(missing_ok=True)
66
+ runBenchmarks(30)
@@ -0,0 +1,74 @@
1
+ from ...tests.conftest import *
2
+ from .benchmarking import recordBenchmarks, runBenchmarks
3
+ import numpy
4
+ import pathlib
5
+ import pytest
6
+ import unittest.mock
7
+ from typing import List
8
+
9
+ def test_recordBenchmarks_decorator(pathBenchmarksTesting: pathlib.Path,
10
+ listDimensionsTestFunctionality: List[int],
11
+ mockBenchmarkTimer: unittest.mock.MagicMock):
12
+ """Test that the decorator correctly records benchmark data."""
13
+ @recordBenchmarks()
14
+ def functionTest(listDimensions: List[int]) -> int:
15
+ return sum(listDimensions)
16
+
17
+ with mockBenchmarkTimer:
18
+ mockBenchmarkTimer.side_effect = [0, 1e9]
19
+ result = functionTest(listDimensionsTestFunctionality)
20
+
21
+ # Verify function still works normally
22
+ assert result == sum(listDimensionsTestFunctionality)
23
+
24
+ # Verify benchmark data was saved
25
+ arrayBenchmarks = numpy.load(str(pathBenchmarksTesting), allow_pickle=True)
26
+ assert len(arrayBenchmarks) == 1
27
+ assert arrayBenchmarks[0]['time'] == 1.0
28
+ assert tuple(arrayBenchmarks[0]['dimensions']) == tuple(listDimensionsTestFunctionality)
29
+
30
+ def test_recordBenchmarks_multiple_calls(pathBenchmarksTesting: pathlib.Path,
31
+ listDimensionsTestFunctionality: List[int],
32
+ mockBenchmarkTimer: unittest.mock.MagicMock):
33
+ """Test that multiple function calls append to benchmark data."""
34
+ @recordBenchmarks()
35
+ def functionTest(listDimensions: List[int]) -> int:
36
+ return sum(listDimensions)
37
+
38
+ with mockBenchmarkTimer:
39
+ mockBenchmarkTimer.side_effect = [0, 1e9, 2e9, 4e9]
40
+ functionTest(listDimensionsTestFunctionality)
41
+ functionTest(listDimensionsTestFunctionality)
42
+
43
+ arrayBenchmarks = numpy.load(str(pathBenchmarksTesting), allow_pickle=True)
44
+ assert len(arrayBenchmarks) == 2
45
+ assert arrayBenchmarks[0]['time'] == 1.0
46
+ assert arrayBenchmarks[1]['time'] == 2.0
47
+
48
+ # NOTE This test tries to collect benchmark data without ensuring that a function is decorated.
49
+ # def test_runBenchmarks_integration(pathBenchmarksTesting: pathlib.Path, listDimensionsTestFunctionality: List[int]):
50
+ # """Test runBenchmarks creates valid benchmark data."""
51
+ # countIterations = 2
52
+ # runBenchmarks(countIterations)
53
+
54
+ # arrayBenchmarks = numpy.load(str(pathBenchmarksTesting), allow_pickle=True)
55
+ # assert len(arrayBenchmarks) > 0 # Should have recorded some benchmarks
56
+
57
+ # # Verify data structure integrity
58
+ # assert arrayBenchmarks.dtype.names == ('time', 'dimensions')
59
+ # assert all(isinstance(record['time'], float) for record in arrayBenchmarks)
60
+ # assert all(isinstance(record['dimensions'], tuple) for record in arrayBenchmarks)
61
+
62
+ # # Verify at least one benchmark entry matches our test dimensions
63
+ # assert any(tuple(listDimensionsTestFunctionality) == record['dimensions'] for record in arrayBenchmarks)
64
+
65
+ # NOTE This test tries to collect benchmark data without ensuring that a function is decorated.
66
+ # @pytest.mark.parametrize("countIterations", [1, 2])
67
+ # def test_runBenchmarks_iterations(countIterations: int, pathBenchmarksTesting: pathlib.Path, listDimensionsTestFunctionality: List[int]):
68
+ # """Test runBenchmarks records data for each iteration."""
69
+ # runBenchmarks(countIterations)
70
+ # arrayBenchmarks = numpy.load(str(pathBenchmarksTesting), allow_pickle=True)
71
+
72
+ # # Should have at least countIterations entries for our test dimensions
73
+ # countMatches = sum(1 for record in arrayBenchmarks if tuple(listDimensionsTestFunctionality) == record['dimensions'])
74
+ # assert countMatches >= countIterations
@@ -0,0 +1,5 @@
1
+ """Experiment"""
2
+ from Z0Z_tools import defineConcurrencyLimit, intInnit, oopsieKwargsie
3
+ from Z0Z_tools.pytest_parseParameters import makeTestSuiteConcurrencyLimit
4
+ from Z0Z_tools.pytest_parseParameters import makeTestSuiteIntInnit
5
+ from Z0Z_tools.pytest_parseParameters import makeTestSuiteOopsieKwargsie
mapFolding/lovelace.py ADDED
@@ -0,0 +1,121 @@
1
+ from mapFolding import indexMy, indexThe, indexTrack
2
+ from numpy import integer
3
+ from numpy.typing import NDArray
4
+ from typing import Any, Optional
5
+ import numba
6
+ import numpy
7
+
8
+ @numba.jit(parallel=False, _nrt=True, boundscheck=False, error_model='numpy', fastmath=True, forceinline=True, looplift=False, no_cfunc_wrapper=True, no_cpython_wrapper=True, nogil=True, nopython=True)
9
+ def ifComputationDivisions(my: NDArray[integer[Any]], the: NDArray[integer[Any]]):
10
+ if the[indexThe.taskDivisions.value] == 0:
11
+ return True
12
+ return my[indexMy.leaf1ndex.value] != the[indexThe.taskDivisions.value] or \
13
+ (my[indexMy.leafConnectee.value] % the[indexThe.taskDivisions.value]) == my[indexMy.taskIndex.value]
14
+
15
+ @numba.jit(parallel=False, _nrt=True, boundscheck=False, error_model='numpy', fastmath=True, forceinline=True, looplift=False, no_cfunc_wrapper=True, no_cpython_wrapper=True, nogil=True, nopython=True)
16
+ def insertUnconstrainedLeaf(my: NDArray[integer[Any]], the: NDArray[integer[Any]], Z0Z_initializeUnconstrainedLeaf: Optional[bool]):
17
+ if Z0Z_initializeUnconstrainedLeaf:
18
+ return my[indexMy.dimensionsUnconstrained.value] == the[indexThe.dimensionsTotal.value]
19
+ else:
20
+ return False
21
+
22
+ @numba.jit(parallel=False, _nrt=True, boundscheck=False, error_model='numpy', fastmath=True, forceinline=True, looplift=False, no_cfunc_wrapper=True, no_cpython_wrapper=True, nogil=True, nopython=True)
23
+ def initializationConditionUnconstrainedLeaf(my: NDArray[integer[Any]], Z0Z_initializeUnconstrainedLeaf: Optional[bool]):
24
+ if Z0Z_initializeUnconstrainedLeaf is None or Z0Z_initializeUnconstrainedLeaf is False:
25
+ return False
26
+ else:
27
+ if my[indexMy.gap1ndex.value] > 0:
28
+ return True
29
+ else:
30
+ return False
31
+
32
+ @numba.jit(parallel=False, _nrt=True, boundscheck=False, error_model='numpy', fastmath=True, forceinline=True, looplift=False, no_cfunc_wrapper=True, no_cpython_wrapper=True, nogil=True, nopython=True)
33
+ def doWhile(connectionGraph: NDArray[integer[Any]], foldsTotal: NDArray[integer[Any]], my: NDArray[integer[Any]], gapsWhere: NDArray[integer[Any]], the: NDArray[integer[Any]], track: NDArray[integer[Any]], Z0Z_initializeUnconstrainedLeaf: Optional[bool] ):
34
+ while my[indexMy.leaf1ndex.value] > 0:
35
+ if my[indexMy.leaf1ndex.value] <= 1 or track[indexTrack.leafBelow.value, 0] == 1:
36
+ if my[indexMy.leaf1ndex.value] > the[indexThe.leavesTotal.value]:
37
+ foldsTotal[my[indexMy.taskIndex.value]] += the[indexThe.leavesTotal.value]
38
+ else:
39
+ my[indexMy.dimensionsUnconstrained.value] = 0
40
+ my[indexMy.gap1ndexCeiling.value] = track[indexTrack.gapRangeStart.value, my[indexMy.leaf1ndex.value] - 1]
41
+ my[indexMy.dimension1ndex.value] = 1
42
+ while my[indexMy.dimension1ndex.value] <= the[indexThe.dimensionsTotal.value]:
43
+ if connectionGraph[my[indexMy.dimension1ndex.value], my[indexMy.leaf1ndex.value], my[indexMy.leaf1ndex.value]] == my[indexMy.leaf1ndex.value]:
44
+ my[indexMy.dimensionsUnconstrained.value] += 1
45
+ else:
46
+ my[indexMy.leafConnectee.value] = connectionGraph[my[indexMy.dimension1ndex.value], my[indexMy.leaf1ndex.value], my[indexMy.leaf1ndex.value]]
47
+ while my[indexMy.leafConnectee.value] != my[indexMy.leaf1ndex.value]:
48
+ if ifComputationDivisions(my, the):
49
+ gapsWhere[my[indexMy.gap1ndexCeiling.value]] = my[indexMy.leafConnectee.value]
50
+ if track[indexTrack.countDimensionsGapped.value, my[indexMy.leafConnectee.value]] == 0:
51
+ my[indexMy.gap1ndexCeiling.value] += 1
52
+ track[indexTrack.countDimensionsGapped.value, my[indexMy.leafConnectee.value]] += 1
53
+ my[indexMy.leafConnectee.value] = connectionGraph[my[indexMy.dimension1ndex.value], my[indexMy.leaf1ndex.value], track[indexTrack.leafBelow.value, my[indexMy.leafConnectee.value]]]
54
+ my[indexMy.dimension1ndex.value] += 1
55
+ if insertUnconstrainedLeaf(my, the, Z0Z_initializeUnconstrainedLeaf):
56
+ my[indexMy.indexLeaf.value] = 0
57
+ while my[indexMy.indexLeaf.value] < my[indexMy.leaf1ndex.value]:
58
+ gapsWhere[my[indexMy.gap1ndexCeiling.value]] = my[indexMy.indexLeaf.value]
59
+ my[indexMy.gap1ndexCeiling.value] += 1
60
+ my[indexMy.indexLeaf.value] += 1
61
+ my[indexMy.indexMiniGap.value] = my[indexMy.gap1ndex.value]
62
+ while my[indexMy.indexMiniGap.value] < my[indexMy.gap1ndexCeiling.value]:
63
+ gapsWhere[my[indexMy.gap1ndex.value]] = gapsWhere[my[indexMy.indexMiniGap.value]]
64
+ if track[indexTrack.countDimensionsGapped.value, gapsWhere[my[indexMy.indexMiniGap.value]]] == the[indexThe.dimensionsTotal.value] - my[indexMy.dimensionsUnconstrained.value]:
65
+ my[indexMy.gap1ndex.value] += 1
66
+ track[indexTrack.countDimensionsGapped.value, gapsWhere[my[indexMy.indexMiniGap.value]]] = 0
67
+ my[indexMy.indexMiniGap.value] += 1
68
+ while my[indexMy.leaf1ndex.value] > 0 and my[indexMy.gap1ndex.value] == track[indexTrack.gapRangeStart.value, my[indexMy.leaf1ndex.value] - 1]:
69
+ my[indexMy.leaf1ndex.value] -= 1
70
+ track[indexTrack.leafBelow.value, track[indexTrack.leafAbove.value, my[indexMy.leaf1ndex.value]]] = track[indexTrack.leafBelow.value, my[indexMy.leaf1ndex.value]]
71
+ track[indexTrack.leafAbove.value, track[indexTrack.leafBelow.value, my[indexMy.leaf1ndex.value]]] = track[indexTrack.leafAbove.value, my[indexMy.leaf1ndex.value]]
72
+ if my[indexMy.leaf1ndex.value] > 0:
73
+ my[indexMy.gap1ndex.value] -= 1
74
+ track[indexTrack.leafAbove.value, my[indexMy.leaf1ndex.value]] = gapsWhere[my[indexMy.gap1ndex.value]]
75
+ track[indexTrack.leafBelow.value, my[indexMy.leaf1ndex.value]] = track[indexTrack.leafBelow.value, track[indexTrack.leafAbove.value, my[indexMy.leaf1ndex.value]]]
76
+ track[indexTrack.leafBelow.value, track[indexTrack.leafAbove.value, my[indexMy.leaf1ndex.value]]] = my[indexMy.leaf1ndex.value]
77
+ track[indexTrack.leafAbove.value, track[indexTrack.leafBelow.value, my[indexMy.leaf1ndex.value]]] = my[indexMy.leaf1ndex.value]
78
+ track[indexTrack.gapRangeStart.value, my[indexMy.leaf1ndex.value]] = my[indexMy.gap1ndex.value]
79
+ my[indexMy.leaf1ndex.value] += 1
80
+ if initializationConditionUnconstrainedLeaf(my, Z0Z_initializeUnconstrainedLeaf):
81
+ break
82
+ return foldsTotal, my, gapsWhere, track
83
+
84
+ @numba.jit(parallel=True, _nrt=True, boundscheck=False, error_model='numpy', fastmath=True, forceinline=True, looplift=False, no_cfunc_wrapper=True, no_cpython_wrapper=True, nogil=True, nopython=True)
85
+ def doTaskIndices(connectionGraph: NDArray[integer[Any]], foldsTotal: NDArray[integer[Any]], my: NDArray[integer[Any]], gapsWhere: NDArray[integer[Any]], the: NDArray[integer[Any]], track: NDArray[integer[Any]]):
86
+
87
+ stateFoldsSubTotal = foldsTotal.copy()
88
+ stateMy = my.copy()
89
+ statePotentialGaps = gapsWhere.copy()
90
+ stateTrack = track.copy()
91
+
92
+ for indexSherpa in numba.prange(the[indexThe.taskDivisions.value]):
93
+ my = stateMy.copy()
94
+ my[indexMy.taskIndex.value] = indexSherpa
95
+ foldsSubTotal, _1, _2, _3 = doWhile(connectionGraph, stateFoldsSubTotal.copy(), my, statePotentialGaps.copy(), the, stateTrack.copy(), Z0Z_initializeUnconstrainedLeaf=False)
96
+
97
+ foldsTotal[indexSherpa] = foldsSubTotal[indexSherpa]
98
+
99
+ return foldsTotal
100
+
101
+ @numba.jit(parallel=False, _nrt=True, boundscheck=False, error_model='numpy', fastmath=True, forceinline=True, looplift=False, no_cfunc_wrapper=True, no_cpython_wrapper=True, nogil=True, nopython=True)
102
+ def countFoldsCompileBranch(connectionGraph: NDArray[integer[Any]], foldsTotal: NDArray[integer[Any]],
103
+ my: NDArray[integer[Any]], gapsWhere: NDArray[integer[Any]], the: NDArray[integer[Any]], track: NDArray[integer[Any]],
104
+ obviousFlagForNumba: bool):
105
+ if obviousFlagForNumba:
106
+ foldsTotal, _1, _2, _3 = doWhile(connectionGraph, foldsTotal, my, gapsWhere, the, track, Z0Z_initializeUnconstrainedLeaf=False)
107
+ else:
108
+ foldsTotal = doTaskIndices(connectionGraph, foldsTotal, my, gapsWhere, the, track)
109
+
110
+ return foldsTotal
111
+
112
+ @numba.jit(parallel=False, _nrt=True, boundscheck=False, error_model='numpy', fastmath=True, forceinline=True, looplift=False, no_cfunc_wrapper=True, no_cpython_wrapper=True, nogil=True, nopython=True)
113
+ def countFoldsCompiled(connectionGraph: NDArray[integer[Any]], foldsTotal: NDArray[integer[Any]], my: NDArray[integer[Any]], gapsWhere: NDArray[integer[Any]], the: NDArray[integer[Any]], track: NDArray[integer[Any]]) -> int:
114
+
115
+ _0, my, gapsWhere, track = doWhile(connectionGraph, foldsTotal, my, gapsWhere, the, track, Z0Z_initializeUnconstrainedLeaf=True)
116
+
117
+ obviousFlagForNumba = the[indexThe.taskDivisions.value] == int(False)
118
+
119
+ foldsTotal = countFoldsCompileBranch(connectionGraph, foldsTotal, my, gapsWhere, the, track, obviousFlagForNumba)
120
+
121
+ return numpy.sum(foldsTotal).item()