mapFolding 0.2.0__py3-none-any.whl → 0.2.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.
mapFolding/__init__.py CHANGED
@@ -1,12 +1,3 @@
1
- """Test concept: Import priority levels. Larger priority values should be imported before smaller priority values.
2
- This seems to be a little silly: no useful information is encoded in the priority value, so I don't know if a
3
- new import should have a lower or higher priority.
4
- Crazy concept: Python doesn't cram at least two import roles into one system, call it `import` and tell us how
5
- awesome Python is. Alternatively, I learn about the secret system for mapping physical names to logical names."""
6
-
7
- # TODO Across the entire package, restructure computationDivisions.
8
- # test modules need updating still
9
-
10
1
  from .theSSOT import *
11
2
  from .beDRY import getTaskDivisions, makeConnectionGraph, outfitFoldings, setCPUlimit
12
3
  from .beDRY import getLeavesTotal, parseDimensions, validateListDimensions
mapFolding/babbage.py CHANGED
@@ -6,7 +6,25 @@ import numba
6
6
  import numpy
7
7
 
8
8
  @numba.jit(cache=True)
9
- def _countFolds(connectionGraph: NDArray[integer[Any]], foldsTotal: NDArray[integer[Any]], mapShape: Tuple[int, ...], my: NDArray[integer[Any]], gapsWhere: NDArray[integer[Any]], the: NDArray[integer[Any]], track: NDArray[integer[Any]]):
9
+ def _countFolds(connectionGraph: NDArray[integer[Any]], foldsTotal: NDArray[integer[Any]], mapShape: Tuple[int, ...], my: NDArray[integer[Any]], gapsWhere: NDArray[integer[Any]], the: NDArray[integer[Any]], track: NDArray[integer[Any]]) -> int:
10
+ """
11
+ What in tarnation is this stupid module and function?
12
+
13
+ - This function is not in the same module as `countFolds` so that we can delay Numba just-in-time (jit) compilation of this function and the finalization of its settings until we are ready.
14
+ - This function is not in the same module as `countFoldsCompiled`, which is the function that does the hard, so that we can delay `numba.jit` compilation of `countFoldsCompiled`.
15
+ - `countFoldsCompiled` is not merely "jitted", it is super jitted, which makes it too arrogant to talk to plebian Python functions. It will, however, reluctantly talk to basic jitted functions.
16
+ - The function in this module is jitted, so it can talk to `countFoldsCompiled`, and because it isn't so arrogant, it will talk to the low-class `countFolds` with only a few restrictions, such as:
17
+ - No `TypedDict`
18
+ - No Python v 3.13
19
+ - The plebs must clean up their own memory problems
20
+ - No oversized integers
21
+ - No global variables, only global constants
22
+ - They don't except pleb nonlocal variables either
23
+ - Python "class": they are all inferior to a jit
24
+ - No `**kwargs`
25
+ - and just a few dozen-jillion other things.
26
+
27
+ """
10
28
  # TODO learn if I really must change this jitted function to get the super jit to recompile
11
29
  # print('babbage')
12
30
  return countFoldsCompiled(connectionGraph, foldsTotal, my, gapsWhere, the, track)
mapFolding/beDRY.py CHANGED
@@ -4,19 +4,19 @@ from mapFolding import indexMy, indexThe, indexTrack, computationState
4
4
  from typing import Any, List, Optional, Sequence, Type, Union
5
5
  import numpy
6
6
  import numba
7
- import numba.extending
8
- import numpy.typing
7
+ from numpy.typing import NDArray
8
+ from numpy import integer
9
9
  import sys
10
10
 
11
11
  def getLeavesTotal(listDimensions: Sequence[int]) -> int:
12
12
  """
13
- Calculate the product of non-zero, non-negative integers in the given list.
13
+ How many leaves are in the map.
14
14
 
15
15
  Parameters:
16
16
  listDimensions: A list of integers representing dimensions.
17
17
 
18
18
  Returns:
19
- productDimensions: The product of all positive integer dimensions. Returns 0 if all dimensions are 0.
19
+ productDimensions: The product of all positive integer dimensions.
20
20
  """
21
21
  listNonNegative = parseDimensions(listDimensions, 'listDimensions')
22
22
  listPositive = [dimension for dimension in listNonNegative if dimension > 0]
@@ -27,18 +27,46 @@ def getLeavesTotal(listDimensions: Sequence[int]) -> int:
27
27
  productDimensions = 1
28
28
  for dimension in listPositive:
29
29
  if dimension > sys.maxsize // productDimensions:
30
- raise OverflowError("Product would exceed maximum integer size")
30
+ raise OverflowError(f"I received {dimension=} in {listDimensions=}, but the product of the dimensions exceeds the maximum size of an integer on this system.")
31
31
  productDimensions *= dimension
32
32
 
33
33
  return productDimensions
34
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"
35
+ def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrencyLimit: int, the: NDArray[integer[Any]], CPUlimit: Optional[Union[bool, float, int]], listDimensions: Sequence[int]):
36
+ """
37
+ Determines whether or how to divide the computation into tasks.
38
+
39
+ Parameters
40
+ ----------
41
+ computationDivisions (None):
42
+ Specifies how to divide computations:
43
+ - None: no division of the computation into tasks; sets task divisions to 0
44
+ - int: direct set the number of task divisions; cannot exceed the map's total leaves
45
+ - "maximum": divides into `leavesTotal`-many `taskDivisions`
46
+ - "cpu": limits the divisions to the number of available CPUs, i.e. `concurrencyLimit`
47
+ concurrencyLimit:
48
+ Maximum number of concurrent tasks allowed
49
+ the:
50
+ Array of settings, including `leavesTotal`
51
+ CPUlimit: for error reporting
52
+ listDimensions: for error reporting
53
+
54
+ Returns
55
+ -------
56
+ the
57
+ Updated settings, including for `taskDivisions`
58
+
59
+ Raises
60
+ ------
61
+ ValueError
62
+ If computationDivisions is an unsupported type or if resulting task divisions exceed total leaves
63
+
64
+ Notes
65
+ -----
66
+ Task divisions cannot exceed total leaves to prevent duplicate counting of folds.
67
+ """
39
68
 
40
69
  if not computationDivisions:
41
- # Coding it this way should cover `None`, `False`, and `0`.
42
70
  the[indexThe.taskDivisions] = 0
43
71
  elif isinstance(computationDivisions, int):
44
72
  the[indexThe.taskDivisions] = computationDivisions
@@ -56,25 +84,23 @@ def getTaskDivisions(CPUlimit, computationDivisions: Optional[Union[int, str]],
56
84
 
57
85
  return the
58
86
 
59
- def makeConnectionGraph(listDimensions: Sequence[int], dtype: Optional[Type] = numpy.int64) -> numpy.typing.NDArray[numpy.integer[Any]]:
87
+ def makeConnectionGraph(listDimensions: Sequence[int], dtype: Optional[Type] = numpy.int64) -> NDArray[integer[Any]]:
60
88
  """
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.
89
+ Constructs a multi-dimensional connection graph representing the connections between the leaves of a map with the given dimensions.
90
+ Also called a Cartesian product decomposition or dimensional product mapping.
64
91
 
65
92
  Parameters:
66
- listDimensions: A validated sequence of integers representing the dimensions of the map.
93
+ listDimensions: A sequence of integers representing the dimensions of the map.
67
94
  Returns:
68
95
  connectionGraph: A 3D numpy array with shape of (dimensionsTotal + 1, leavesTotal + 1, leavesTotal + 1).
69
96
  """
70
- leavesTotal = getLeavesTotal(listDimensions)
71
- arrayDimensions = numpy.array(listDimensions, dtype=dtype)
97
+ mapShape = validateListDimensions(listDimensions)
98
+ leavesTotal = getLeavesTotal(mapShape)
99
+ arrayDimensions = numpy.array(mapShape, dtype=dtype)
72
100
  dimensionsTotal = len(arrayDimensions)
73
101
 
74
102
  # 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]
103
+ cumulativeProduct = numpy.multiply.accumulate([1] + mapShape, dtype=dtype)
78
104
 
79
105
  # Step 2: create a coordinate system
80
106
  coordinateSystem = numpy.zeros((dimensionsTotal + 1, leavesTotal + 1), dtype=dtype)
@@ -113,13 +139,36 @@ def makeConnectionGraph(listDimensions: Sequence[int], dtype: Optional[Type] = n
113
139
 
114
140
  return connectionGraph
115
141
 
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:
142
+ def outfitFoldings(listDimensions: Sequence[int], computationDivisions: Optional[Union[int, str]] = None, CPUlimit: Optional[Union[bool, float, int]] = None, dtypeDefault: Optional[Type] = numpy.int64, dtypeLarge: Optional[Type] = numpy.int64, ) -> computationState:
143
+ """
144
+ Initializes and configures the computation state for map folding computations.
145
+
146
+ Parameters
147
+ ----------
148
+ listDimensions:
149
+ The dimensions of the map to be folded
150
+ computationDivisions (None):
151
+ Specifies how to divide the computation tasks
152
+ CPUlimit (None):
153
+ Limits the CPU usage for computations
154
+ dtypeDefault (numpy.int64):
155
+ The default numpy dtype to use for arrays
156
+ dtypeLarge (numpy.int64):
157
+ The numpy dtype to use for larger arrays
158
+
159
+ Returns
160
+ -------
161
+ computationState
162
+ An initialized computation state containing:
163
+ - connectionGraph: Graph representing connections in the map
164
+ - foldsTotal: Array tracking total folds
165
+ - mapShape: Validated and sorted dimensions of the map
166
+ - my: Array for internal state tracking
167
+ - gapsWhere: Array tracking gap positions
168
+ - the: Configured task divisions
169
+ - track: Array for tracking computation progress
170
+ """
171
+
123
172
  the = numpy.zeros(len(indexThe), dtype=dtypeDefault)
124
173
 
125
174
  mapShape = tuple(sorted(validateListDimensions(listDimensions)))
@@ -127,15 +176,13 @@ def outfitFoldings(
127
176
  the[indexThe.dimensionsTotal] = len(mapShape)
128
177
  concurrencyLimit = setCPUlimit(CPUlimit)
129
178
 
130
- the = getTaskDivisions(CPUlimit, computationDivisions, concurrencyLimit, listDimensions, the)
131
-
132
179
  stateInitialized = computationState(
133
180
  connectionGraph = makeConnectionGraph(mapShape, dtype=dtypeDefault),
134
181
  foldsTotal = numpy.zeros(the[indexThe.leavesTotal], dtype=numpy.int64),
135
182
  mapShape = mapShape,
136
183
  my = numpy.zeros(len(indexMy), dtype=dtypeLarge),
137
184
  gapsWhere = numpy.zeros(int(the[indexThe.leavesTotal]) * int(the[indexThe.leavesTotal]) + 1, dtype=dtypeDefault),
138
- the = the,
185
+ the = getTaskDivisions(computationDivisions, concurrencyLimit, the, CPUlimit, listDimensions),
139
186
  track = numpy.zeros((len(indexTrack), the[indexThe.leavesTotal] + 1), dtype=dtypeLarge)
140
187
  )
141
188
 
@@ -145,10 +192,10 @@ def outfitFoldings(
145
192
 
146
193
  def parseDimensions(dimensions: Sequence[int], parameterName: str = 'unnamed parameter') -> List[int]:
147
194
  """
148
- Parse and validate a list of dimensions.
195
+ Parse and validate dimensions are non-negative integers.
149
196
 
150
197
  Parameters:
151
- listDimensions: List of integers representing dimensions
198
+ dimensions: Sequence of integers representing dimensions
152
199
  parameterName ('unnamed parameter'): Name of the parameter for error messages. Defaults to 'unnamed parameter'
153
200
  Returns:
154
201
  listNonNegative: List of validated non-negative integers
@@ -168,43 +215,41 @@ def parseDimensions(dimensions: Sequence[int], parameterName: str = 'unnamed par
168
215
 
169
216
  return listNonNegative
170
217
 
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.
218
+ def setCPUlimit(CPUlimit: Union[bool, float, int, None]) -> int:
219
+ """Sets CPU limit for Numba concurrent operations. Note that it can only affect Numba-jitted functions that have not yet been imported.
220
+
175
221
  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
222
+ CPUlimit: whether and how to limit the CPU usage. See notes for details.
180
223
  Returns:
181
224
  concurrencyLimit: The actual concurrency limit that was set
182
225
  Raises:
183
226
  TypeError: If CPUlimit is not of the expected types
227
+
228
+ Limits on CPU usage `CPUlimit`:
229
+ - `False`, `None`, or `0`: No limits on CPU usage; uses all available CPUs. All other values will potentially limit CPU usage.
230
+ - `True`: Yes, limit the CPU usage; limits to 1 CPU.
231
+ - Integer `>= 1`: Limits usage to the specified number of CPUs.
232
+ - Decimal value (`float`) between 0 and 1: Fraction of total CPUs to use.
233
+ - Decimal value (`float`) between -1 and 0: Fraction of CPUs to *not* use.
234
+ - Integer `<= -1`: Subtract the absolute value from total CPUs.
184
235
  """
185
236
  if not (CPUlimit is None or isinstance(CPUlimit, (bool, int, float))):
186
237
  CPUlimit = oopsieKwargsie(CPUlimit)
187
238
 
188
239
  concurrencyLimit = defineConcurrencyLimit(CPUlimit)
189
- # NOTE `set_num_threads` only affects "jitted" functions that have _not_ yet been "imported"
190
240
  numba.set_num_threads(concurrencyLimit)
191
241
 
192
242
  return concurrencyLimit
193
243
 
194
244
  def validateListDimensions(listDimensions: Sequence[int]) -> List[int]:
195
245
  """
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.
246
+ Validates and sorts a sequence of at least two positive dimensions.
202
247
 
203
248
  Parameters:
204
- listDimensions: A list of integer dimensions to be validated.
249
+ listDimensions: A sequence of integer dimensions to be validated.
205
250
 
206
251
  Returns:
207
- validDimensions: A list, with at least two elements, of only positive integers.
252
+ dimensionsValidSorted: A list, with at least two elements, of only positive integers.
208
253
 
209
254
  Raises:
210
255
  ValueError: If the input listDimensions is None.
@@ -213,7 +258,7 @@ def validateListDimensions(listDimensions: Sequence[int]) -> List[int]:
213
258
  if not listDimensions:
214
259
  raise ValueError(f"listDimensions is a required parameter.")
215
260
  listNonNegative = parseDimensions(listDimensions, 'listDimensions')
216
- validDimensions = [dimension for dimension in listNonNegative if dimension > 0]
217
- if len(validDimensions) < 2:
261
+ dimensionsValid = [dimension for dimension in listNonNegative if dimension > 0]
262
+ if len(dimensionsValid) < 2:
218
263
  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
264
+ return sorted(dimensionsValid)
mapFolding/lovelace.py CHANGED
@@ -1,27 +1,43 @@
1
+ """
2
+ The algorithm for counting folds.
3
+
4
+ Starting from established data structures, the algorithm initializes some baseline values. The initialization uses a loop that is not used after the first fold is counted.
5
+
6
+ After initialization, the folds are either counted sequentially or counted with inefficiently divided parallel tasks.
7
+
8
+ All three of these actions--initialization, sequential counting, and parallel counting--use nearly identical logic. Without Numba, all of the logic is in one function with exactly one additional
9
+ conditional statement for initialization and exactly one additional conditional statement for parallel counting.
10
+
11
+ Numba's just-in-time (jit) compiler, especially super jit, is capable of radically increasing throughput and dramatically reducing the size of the compiled code, especially by ejecting unused code.
12
+
13
+ The complexity of this module is due to me allegedly applying Numba's features. Allegedly.
14
+
15
+ (The flow starts with the last function.)
16
+ """
1
17
  from mapFolding import indexMy, indexThe, indexTrack
2
18
  from numpy import integer
3
19
  from numpy.typing import NDArray
4
- from typing import Any, Optional
20
+ from typing import Any, Tuple, Optional
5
21
  import numba
6
22
  import numpy
7
23
 
8
24
  @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]]):
25
+ def ifComputationDivisions(my: NDArray[integer[Any]], the: NDArray[integer[Any]]) -> bool:
10
26
  if the[indexThe.taskDivisions.value] == 0:
11
27
  return True
12
28
  return my[indexMy.leaf1ndex.value] != the[indexThe.taskDivisions.value] or \
13
29
  (my[indexMy.leafConnectee.value] % the[indexThe.taskDivisions.value]) == my[indexMy.taskIndex.value]
14
30
 
15
31
  @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:
32
+ def insertUnconstrainedLeaf(my: NDArray[integer[Any]], the: NDArray[integer[Any]], initializeUnconstrainedLeaf: Optional[bool]) -> bool:
33
+ if initializeUnconstrainedLeaf:
18
34
  return my[indexMy.dimensionsUnconstrained.value] == the[indexThe.dimensionsTotal.value]
19
35
  else:
20
36
  return False
21
37
 
22
38
  @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:
39
+ def initializationConditionUnconstrainedLeaf(my: NDArray[integer[Any]], initializeUnconstrainedLeaf: Optional[bool]) -> bool:
40
+ if initializeUnconstrainedLeaf is None or initializeUnconstrainedLeaf is False:
25
41
  return False
26
42
  else:
27
43
  if my[indexMy.gap1ndex.value] > 0:
@@ -30,7 +46,7 @@ def initializationConditionUnconstrainedLeaf(my: NDArray[integer[Any]], Z0Z_init
30
46
  return False
31
47
 
32
48
  @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] ):
49
+ 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]], initializeUnconstrainedLeaf: Optional[bool]) -> Tuple[NDArray[integer[Any]], NDArray[integer[Any]], NDArray[integer[Any]], NDArray[integer[Any]]]:
34
50
  while my[indexMy.leaf1ndex.value] > 0:
35
51
  if my[indexMy.leaf1ndex.value] <= 1 or track[indexTrack.leafBelow.value, 0] == 1:
36
52
  if my[indexMy.leaf1ndex.value] > the[indexThe.leavesTotal.value]:
@@ -45,6 +61,7 @@ def doWhile(connectionGraph: NDArray[integer[Any]], foldsTotal: NDArray[integer[
45
61
  else:
46
62
  my[indexMy.leafConnectee.value] = connectionGraph[my[indexMy.dimension1ndex.value], my[indexMy.leaf1ndex.value], my[indexMy.leaf1ndex.value]]
47
63
  while my[indexMy.leafConnectee.value] != my[indexMy.leaf1ndex.value]:
64
+ # NOTE This conditional check should only be in the parallel counting branch
48
65
  if ifComputationDivisions(my, the):
49
66
  gapsWhere[my[indexMy.gap1ndexCeiling.value]] = my[indexMy.leafConnectee.value]
50
67
  if track[indexTrack.countDimensionsGapped.value, my[indexMy.leafConnectee.value]] == 0:
@@ -52,7 +69,8 @@ def doWhile(connectionGraph: NDArray[integer[Any]], foldsTotal: NDArray[integer[
52
69
  track[indexTrack.countDimensionsGapped.value, my[indexMy.leafConnectee.value]] += 1
53
70
  my[indexMy.leafConnectee.value] = connectionGraph[my[indexMy.dimension1ndex.value], my[indexMy.leaf1ndex.value], track[indexTrack.leafBelow.value, my[indexMy.leafConnectee.value]]]
54
71
  my[indexMy.dimension1ndex.value] += 1
55
- if insertUnconstrainedLeaf(my, the, Z0Z_initializeUnconstrainedLeaf):
72
+ # NOTE This `if` statement and `while` loop should be absent from the code that does the counting
73
+ if insertUnconstrainedLeaf(my, the, initializeUnconstrainedLeaf):
56
74
  my[indexMy.indexLeaf.value] = 0
57
75
  while my[indexMy.indexLeaf.value] < my[indexMy.leaf1ndex.value]:
58
76
  gapsWhere[my[indexMy.gap1ndexCeiling.value]] = my[indexMy.indexLeaf.value]
@@ -77,13 +95,16 @@ def doWhile(connectionGraph: NDArray[integer[Any]], foldsTotal: NDArray[integer[
77
95
  track[indexTrack.leafAbove.value, track[indexTrack.leafBelow.value, my[indexMy.leaf1ndex.value]]] = my[indexMy.leaf1ndex.value]
78
96
  track[indexTrack.gapRangeStart.value, my[indexMy.leaf1ndex.value]] = my[indexMy.gap1ndex.value]
79
97
  my[indexMy.leaf1ndex.value] += 1
80
- if initializationConditionUnconstrainedLeaf(my, Z0Z_initializeUnconstrainedLeaf):
98
+ # NOTE This check and break should be absent from the code that does the counting
99
+ if initializationConditionUnconstrainedLeaf(my, initializeUnconstrainedLeaf):
81
100
  break
82
101
  return foldsTotal, my, gapsWhere, track
83
102
 
84
103
  @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
-
104
+ 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]]) -> NDArray[integer[Any]]:
105
+ """This is the only function with the `parallel=True` option.
106
+ Make a copy of the initialized state because all task divisions can start from this baseline.
107
+ Run the counting algorithm but with conditional execution of a few lines of code, so each task has an incomplete count that does not overlap with other tasks."""
87
108
  stateFoldsSubTotal = foldsTotal.copy()
88
109
  stateMy = my.copy()
89
110
  statePotentialGaps = gapsWhere.copy()
@@ -92,18 +113,17 @@ def doTaskIndices(connectionGraph: NDArray[integer[Any]], foldsTotal: NDArray[in
92
113
  for indexSherpa in numba.prange(the[indexThe.taskDivisions.value]):
93
114
  my = stateMy.copy()
94
115
  my[indexMy.taskIndex.value] = indexSherpa
95
- foldsSubTotal, _1, _2, _3 = doWhile(connectionGraph, stateFoldsSubTotal.copy(), my, statePotentialGaps.copy(), the, stateTrack.copy(), Z0Z_initializeUnconstrainedLeaf=False)
116
+ foldsSubTotal, _1, _2, _3 = doWhile(connectionGraph, stateFoldsSubTotal.copy(), my, statePotentialGaps.copy(), the, stateTrack.copy(), initializeUnconstrainedLeaf=False)
96
117
 
97
118
  foldsTotal[indexSherpa] = foldsSubTotal[indexSherpa]
98
119
 
99
120
  return foldsTotal
100
121
 
101
122
  @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):
123
+ def countFoldsCompileBranch(connectionGraph: NDArray[integer[Any]], foldsTotal: NDArray[integer[Any]], my: NDArray[integer[Any]], gapsWhere: NDArray[integer[Any]], the: NDArray[integer[Any]], track: NDArray[integer[Any]], obviousFlagForNumba: bool) -> NDArray[integer[Any]]:
124
+ """Allegedly, `obviousFlagForNumba` allows Numba to compile two versions: one for parallel execution and one leaner version for sequential execution."""
105
125
  if obviousFlagForNumba:
106
- foldsTotal, _1, _2, _3 = doWhile(connectionGraph, foldsTotal, my, gapsWhere, the, track, Z0Z_initializeUnconstrainedLeaf=False)
126
+ foldsTotal, _1, _2, _3 = doWhile(connectionGraph, foldsTotal, my, gapsWhere, the, track, initializeUnconstrainedLeaf=False)
107
127
  else:
108
128
  foldsTotal = doTaskIndices(connectionGraph, foldsTotal, my, gapsWhere, the, track)
109
129
 
@@ -111,11 +131,15 @@ def countFoldsCompileBranch(connectionGraph: NDArray[integer[Any]], foldsTotal:
111
131
 
112
132
  @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
133
  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:
134
+ # ^ Receive the data structures.
114
135
 
115
- _0, my, gapsWhere, track = doWhile(connectionGraph, foldsTotal, my, gapsWhere, the, track, Z0Z_initializeUnconstrainedLeaf=True)
136
+ # Initialize baseline values primarily to eliminate the need for the logic of `insertUnconstrainedLeaf`
137
+ _0, my, gapsWhere, track = doWhile(connectionGraph, foldsTotal, my, gapsWhere, the, track, initializeUnconstrainedLeaf=True)
116
138
 
117
139
  obviousFlagForNumba = the[indexThe.taskDivisions.value] == int(False)
118
140
 
141
+ # Call the function that will branch to sequential or parallel counting
119
142
  foldsTotal = countFoldsCompileBranch(connectionGraph, foldsTotal, my, gapsWhere, the, track, obviousFlagForNumba)
120
143
 
144
+ # Return an `int` integer
121
145
  return numpy.sum(foldsTotal).item()
mapFolding/oeis.py CHANGED
@@ -148,6 +148,28 @@ def _getOEISidValues(oeisID: str) -> Dict[int, int]:
148
148
  return _parseBFileOEIS(OEISbFile, oeisID)
149
149
 
150
150
  def makeSettingsOEIS() -> Dict[str, SettingsOEIS]:
151
+ """
152
+ Creates a dictionary mapping OEIS IDs to their corresponding settings.
153
+
154
+ This function initializes settings for each implemented OEIS sequence by combining
155
+ hardcoded values with dynamically retrieved OEIS sequence values.
156
+
157
+ Returns:
158
+ Dict[str, SettingsOEIS]: A dictionary where:
159
+ - Keys are OEIS sequence IDs (str)
160
+ - Values are SettingsOEIS objects containing:
161
+ - description: Text description of the sequence
162
+ - getDimensions: Function to get dimensions
163
+ - valuesBenchmark: Benchmark values
164
+ - valuesKnown: Known values from OEIS
165
+ - valuesTestValidation: Values for test validation
166
+ - valueUnknown: First unknown value in sequence
167
+
168
+ Note:
169
+ Relies on global variables:
170
+ - oeisIDsImplemented: List of implemented OEIS sequence IDs
171
+ - settingsOEIShardcodedValues: Dictionary of hardcoded settings per sequence
172
+ """
151
173
  settingsTarget = {}
152
174
  for oeisID in oeisIDsImplemented:
153
175
  valuesKnownSherpa = _getOEISidValues(oeisID)
mapFolding/startHere.py CHANGED
@@ -1,27 +1,38 @@
1
- from numpy import integer
2
- from numpy.typing import NDArray
3
- from typing import Any, Tuple
4
- import numba
5
- import numpy
6
1
  from mapFolding import outfitFoldings
7
- # from mapFolding.benchmarks.benchmarking import recordBenchmarks
8
- from typing import Optional, Union, Sequence, Type
2
+ from typing import Optional, Sequence, Type, Union
9
3
  import os
10
4
  import pathlib
11
5
 
12
- # TODO the current tests expect positional `listDimensions, computationDivisions`, so after restructuring you can arrange the parameters however you want.
13
- def countFolds(
14
- listDimensions: Sequence[int],
15
- computationDivisions: Optional[Union[int, str]] = None,
16
- CPUlimit: Optional[Union[int, float, bool]] = None,
17
- writeFoldsTotal: Optional[Union[str, os.PathLike[str]]] = None,
18
- **keywordArguments: Optional[Type]
19
- ):
20
- """keywordArguments:
21
- dtypeDefault: Optional[Type]
22
- dtypeLarge: Optional[Type]
23
-
24
- writeFoldsTotal: path, filename, or pathFilename
6
+ def countFolds(listDimensions: Sequence[int], writeFoldsTotal: Optional[Union[str, os.PathLike[str]]] = None, computationDivisions: Optional[Union[int, str]] = None, CPUlimit: Optional[Union[int, float, bool]] = None, **keywordArguments: Optional[Type]) -> int:
7
+ """Count the total number of possible foldings for a given map dimensions.
8
+
9
+ Parameters:
10
+ listDimensions: List of integers representing the dimensions of the map to be folded.
11
+ writeFoldsTotal (None): Path or filename to write the total fold count.
12
+ If a directory is provided, creates a file with default name based on map dimensions.
13
+ computationDivisions (None):
14
+ Whether and how to divide the computational work. See notes for details.
15
+ CPUlimit (None): This is only relevant if there are `computationDivisions`: whether and how to limit the CPU usage. See notes for details.
16
+ **keywordArguments: Additional arguments including dtypeDefault and dtypeLarge for data type specifications.
17
+ Returns:
18
+ foldsTotal: Total number of distinct ways to fold a map of the given dimensions.
19
+
20
+ Computation divisions:
21
+ - None: no division of the computation into tasks; sets task divisions to 0
22
+ - int: direct set the number of task divisions; cannot exceed the map's total leaves
23
+ - "maximum": divides into `leavesTotal`-many `taskDivisions`
24
+ - "cpu": limits the divisions to the number of available CPUs, i.e. `concurrencyLimit`
25
+
26
+ Limits on CPU usage `CPUlimit`:
27
+ - `False`, `None`, or `0`: No limits on CPU usage; uses all available CPUs. All other values will potentially limit CPU usage.
28
+ - `True`: Yes, limit the CPU usage; limits to 1 CPU.
29
+ - Integer `>= 1`: Limits usage to the specified number of CPUs.
30
+ - Decimal value (`float`) between 0 and 1: Fraction of total CPUs to use.
31
+ - Decimal value (`float`) between -1 and 0: Fraction of CPUs to *not* use.
32
+ - Integer `<= -1`: Subtract the absolute value from total CPUs.
33
+
34
+ N.B.: You probably don't want to divide the computation into tasks.
35
+ If you want to compute a large `foldsTotal`, dividing the computation into tasks is usually a bad idea. Dividing the algorithm into tasks is inherently inefficient: efficient division into tasks means there would be no overlap in the work performed by each task. When dividing this algorithm, the amount of overlap is between 50% and 90% by all tasks: at least 50% of the work done by every task must be done by _all_ tasks. If you improve the computation time, it will only change by -10 to -50% depending on (at the very least) the ratio of the map dimensions and the number of leaves. If an undivided computation would take 10 hours on your computer, for example, the computation will still take at least 5 hours but you might reduce the time to 9 hours. Most of the time, however, you will increase the computation time. If logicalCores >= leavesTotal, it will probably be faster. If logicalCores <= 2 * leavesTotal, it will almost certainly be slower for all map dimensions.
25
36
  """
26
37
  stateUniversal = outfitFoldings(listDimensions, computationDivisions=computationDivisions, CPUlimit=CPUlimit, **keywordArguments)
27
38
 
@@ -33,6 +44,7 @@ def countFolds(
33
44
  pathFilenameFoldsTotal = pathFilenameFoldsTotal / filenameFoldsTotalDEFAULT
34
45
  pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
35
46
 
47
+ # NOTE Don't import a module with a numba.jit function until you want the function to compile and to freeze all settings for that function.
36
48
  from mapFolding.babbage import _countFolds
37
49
  foldsTotal = _countFolds(**stateUniversal)
38
50
  # foldsTotal = benchmarkSherpa(**stateUniversal)
@@ -48,6 +60,10 @@ def countFolds(
48
60
 
49
61
  return foldsTotal
50
62
 
63
+ # from numpy import integer
64
+ # from numpy.typing import NDArray
65
+ # from typing import Any, Tuple
66
+ # from mapFolding.benchmarks.benchmarking import recordBenchmarks
51
67
  # @recordBenchmarks()
52
68
  # def benchmarkSherpa(connectionGraph: NDArray[integer[Any]], foldsTotal: NDArray[integer[Any]], mapShape: Tuple[int, ...], my: NDArray[integer[Any]], gapsWhere: NDArray[integer[Any]], the: NDArray[integer[Any]], track: NDArray[integer[Any]]):
53
69
  # from mapFolding.babbage import _countFolds
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mapFolding
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Algorithm(s) for counting distinct ways to fold a map (or a strip of stamps)
5
5
  Author-email: Hunter Hogan <HunterHogan@pm.me>
6
6
  Project-URL: homepage, https://github.com/hunterhogan/mapFolding
@@ -32,11 +32,11 @@ from mapFolding import countFolds
32
32
  foldsTotal = countFolds( [2,10] )
33
33
  ```
34
34
 
35
- The directory `mapFolding/reference` has
35
+ The directory [mapFolding/reference](https://github.com/hunterhogan/mapFolding/blob/main/mapFolding/reference) has
36
36
 
37
37
  - a verbatim transcription of the "procedure" published in _The Computer Journal_,
38
38
  - multiple referential versions of the procedure with explanatory comments including
39
- - `hunterNumba.py` a one-size-fits-all, self-contained, reasonably fast, contemporary algorithm that is nevertheless infected by _noobaceae ignorancium_, and
39
+ - [hunterNumba.py](https://github.com/hunterhogan/mapFolding/blob/main/mapFolding/reference), a one-size-fits-all, self-contained, reasonably fast, contemporary algorithm that is nevertheless infected by _noobaceae ignorancium_, and
40
40
  - miscellaneous notes.
41
41
 
42
42
  [![Python Tests](https://github.com/hunterhogan/mapFolding/actions/workflows/unittests.yml/badge.svg)](https://github.com/hunterhogan/mapFolding/actions/workflows/unittests.yml)
@@ -97,13 +97,13 @@ Cache cleared from C:\apps\mapFolding\mapFolding\.cache
97
97
 
98
98
  ### The typo-laden algorithm published in 1971
99
99
 
100
- The full paper, W. F. Lunnon, Multi-dimensional map-folding, _The Computer Journal_, Volume 14, Issue 1, 1971, Pages 75–80, [https://doi.org/10.1093/comjnl/14.1.75](https://doi.org/10.1093/comjnl/14.1.75) ([BibTex](mapFolding/citations/Lunnon.bibtex) citation) is available at the DOI link. (As of 3 January 2025, the paper is a PDF of images, not text, and can be accessed without cost or login.)
100
+ The full paper, W. F. Lunnon, Multi-dimensional map-folding, _The Computer Journal_, Volume 14, Issue 1, 1971, Pages 75–80, [https://doi.org/10.1093/comjnl/14.1.75](https://doi.org/10.1093/comjnl/14.1.75) ([BibTex](https://github.com/hunterhogan/mapFolding/blob/main/mapFolding/citations/Lunnon.bibtex) citation) is available at the DOI link. (As of 3 January 2025, the paper is a PDF of images, not text, and can be accessed without cost or login.)
101
101
 
102
- In [`foldings.txt`](mapFolding/reference/foldings.txt), you can find a text transcription of the algorithm as it was printed in 1971. In [`foldings.AA`](mapFolding/reference/foldings.AA), I have corrected obvious transcription errors, documented with comments, and I have reformatted line breaks and indentation. For contemporary readers, the result is likely easier to read than the text transcription or the original paper are easy to read. This is especially true if you view the document with semantic highlighting, such as with [Algol 60 syntax highlighter](https://github.com/PolariTOON/language-algol60).
102
+ In [`foldings.txt`](https://github.com/hunterhogan/mapFolding/blob/main/mapFolding/reference/foldings.txt), you can find a text transcription of the algorithm as it was printed in 1971. In [`foldings.AA`](https://github.com/hunterhogan/mapFolding/blob/main/mapFolding/reference/foldings.AA), I have corrected obvious transcription errors, documented with comments, and I have reformatted line breaks and indentation. For contemporary readers, the result is likely easier to read than the text transcription or the original paper are easy to read. This is especially true if you view the document with semantic highlighting, such as with [Algol 60 syntax highlighter](https://github.com/PolariTOON/language-algol60).
103
103
 
104
104
  ### Java implementation(s) and improvements
105
105
 
106
- [archmageirvine](https://github.com/archmageirvine/joeis/blob/80e3e844b11f149704acbab520bc3a3a25ac34ff/src/irvine/oeis/a001/A001415.java) ([BibTex](mapFolding/citations/jOEIS.bibtex) citation) says about the Java code:
106
+ [archmageirvine](https://github.com/archmageirvine/joeis/blob/80e3e844b11f149704acbab520bc3a3a25ac34ff/src/irvine/oeis/a001/A001415.java) ([BibTex](https://github.com/hunterhogan/mapFolding/blob/main/mapFolding/citations/jOEIS.bibtex) citation) says about the Java code:
107
107
 
108
108
  ```java
109
109
  /**
@@ -122,49 +122,12 @@ In [`foldings.txt`](mapFolding/reference/foldings.txt), you can find a text tran
122
122
 
123
123
  ~~This caused my neurosis:~~ I enjoyed the following video, which is what introduced me to map folding.
124
124
 
125
- "How Many Ways Can You Fold a Map?" by Physics for the Birds, 2024 November 13 ([BibTex](mapFolding/citations/Physics_for_the_Birds.bibtex) citation)
125
+ "How Many Ways Can You Fold a Map?" by Physics for the Birds, 2024 November 13 ([BibTex](https://github.com/hunterhogan/mapFolding/blob/main/mapFolding/citations/Physics_for_the_Birds.bibtex) citation)
126
126
 
127
127
  [![How Many Ways Can You Fold a Map?](https://i.ytimg.com/vi/sfH9uIY3ln4/hq720.jpg)](https://www.youtube.com/watch?v=sfH9uIY3ln4)
128
128
 
129
- ## Install this package
130
-
131
- ### From Github
132
-
133
- ```sh
134
- pip install mapFolding@git+https://github.com/hunterhogan/mapFolding.git
135
- ```
136
-
137
- ### From a local directory
138
-
139
- #### Windows
140
-
141
- ```powershell
142
- git clone https://github.com/hunterhogan/mapFolding.git \path\to\mapFolding
143
- pip install mapFolding@file:\path\to\mapFolding
144
- ```
145
-
146
- #### POSIX
147
-
148
- ```bash
149
- git clone https://github.com/hunterhogan/mapFolding.git /path/to/mapFolding
150
- pip install mapFolding@file:/path/to/mapFolding
151
- ```
152
-
153
- ## Install updates
154
-
155
- ```sh
156
- pip install --upgrade mapFolding@git+https://github.com/hunterhogan/mapFolding.git
157
- ```
158
-
159
- ## Creating a virtual environment before installation
160
-
161
- You can isolate `mapFolding` in a virtual environment. For example, use the following commands to create a directory for the virtual environment, activate the virtual environment, and install the package. In the future, you will likely need to activate the virtual environment before using `mapFolding` again. From the command line, in a directory you want to install in.
129
+ ## Installation
162
130
 
163
131
  ```sh
164
- py -m venv mapFolding
165
- cd mapFolding
166
- cd Scripts
167
- activate
168
- cd ..
169
- pip install mapFolding@git+https://github.com/hunterhogan/mapFolding.git
132
+ pip install mapFolding
170
133
  ```
@@ -1,10 +1,10 @@
1
- mapFolding/__init__.py,sha256=fQGYxmjMofT1id_svln0L3htMBVUbMVj1UWIP74QhAQ,974
2
- mapFolding/babbage.py,sha256=tmz4ZEvMds1nPK7kb_oyl89AsGyuU0MB5udC8AAIL-I,633
3
- mapFolding/beDRY.py,sha256=JUGAuLpQYD-B_mSrgpw4E-JNM_N3gJ2VfY-FkrBP3yA,11135
1
+ mapFolding/__init__.py,sha256=3kQQWyOBriVg9wO5btGE1Cq3lNqJz6CudrkOxVWcsy8,368
2
+ mapFolding/babbage.py,sha256=3D1qcntoiJm2tqgbiCAMc7GpGA0SZTdGORNEnensyxk,1882
3
+ mapFolding/beDRY.py,sha256=QACsmdskZplzMpYGUu7DSTSCE7x5JFApEyUqvUh0H0c,12613
4
4
  mapFolding/importPackages.py,sha256=Pno5VXaNiyJKG2Jtj4sB_h6EG50IJ7tQKyivVoKFMxo,303
5
- mapFolding/lovelace.py,sha256=NdQDPYpe7yU8GS9WIv_Rb4nOE9cPt0-XPKOiLfyE_Z4,9369
6
- mapFolding/oeis.py,sha256=pI08RrA39AVAU7U6h_1IowadxXC4K6n2OQd7vgTbTTI,11096
7
- mapFolding/startHere.py,sha256=37wU-VmM92bOZkFw4Wpiz3u0_1TiLsob94HZUPBwFQg,2433
5
+ mapFolding/lovelace.py,sha256=qcyGpVEPP3n0r-RNrSUgPRP3yZJfBEfmok7jYMm1OI0,11473
6
+ mapFolding/oeis.py,sha256=c8oCAcWgLvwC-bBZzGty8yth1vWtT8lWcKQeCiNGYgw,12078
7
+ mapFolding/startHere.py,sha256=Z73A2bm35XygF6bpz710S8G2tVRdU_5-nO-nDgTaDhM,5068
8
8
  mapFolding/theSSOT.py,sha256=-t23-gPLPNWWBEeSi1mKkNCeeQA4y1AA_oPKC7tZwe4,1970
9
9
  mapFolding/JAX/lunnanJAX.py,sha256=xMZloN47q-MVfjdYOM1hi9qR4OnLq7qALmGLMraevQs,14819
10
10
  mapFolding/JAX/taskJAX.py,sha256=yJNeH0rL6EhJ6ppnATHF0Zf81CDMC10bnPnimVxE1hc,20037
@@ -19,10 +19,10 @@ mapFolding/reference/rotatedEntryPoint.py,sha256=6WVvEcGwDgRPa7dDs7ODAHUJjHDZDID
19
19
  tests/__init__.py,sha256=PGYVr7r23gATgcvZ3Sfph9D_g1MVvhgzMNWXBs_9tmY,52
20
20
  tests/conftest.py,sha256=ur6l5nDnWju7zOEMXqoNGYHY-Hf9GApOm4VT9pXtMtY,10594
21
21
  tests/test_oeis.py,sha256=h-NhWRoarl3v6qoB0RpnaUPuMGrQpWyfmsL0FXsGauU,7972
22
- tests/test_other.py,sha256=AZYRvZYL-wdDiKcbAFEBTV4a22CANmbl8mj8yVGBQ6g,4852
23
- tests/test_tasks.py,sha256=bBXsMfxb6WXz15eFjUxoS67wzRqusX_9XhetXaY-RnQ,944
24
- mapFolding-0.2.0.dist-info/METADATA,sha256=EC2InFN5XJ1LqKGptTerqGvuJQntDQdyMFG4WIvR6WU,6602
25
- mapFolding-0.2.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
26
- mapFolding-0.2.0.dist-info/entry_points.txt,sha256=F3OUeZR1XDTpoH7k3wXuRb3KF_kXTTeYhu5AGK1SiOQ,146
27
- mapFolding-0.2.0.dist-info/top_level.txt,sha256=1gP2vFaqPwHujGwb3UjtMlLEGN-943VSYFR7V4gDqW8,17
28
- mapFolding-0.2.0.dist-info/RECORD,,
22
+ tests/test_other.py,sha256=3Qc6fA39q5u_U0JhFUB9mdBd5_XcVC7IP9DwlNuG9p8,4716
23
+ tests/test_tasks.py,sha256=Hn4N8OVVfDeNjL7ad5ELKKDgdOn8sDBj6IebPGpLIlE,1254
24
+ mapFolding-0.2.1.dist-info/METADATA,sha256=r7QVig9M5_DReHsPG_wyyRNJe0UkggfUotMf8tfoF7I,5914
25
+ mapFolding-0.2.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
26
+ mapFolding-0.2.1.dist-info/entry_points.txt,sha256=F3OUeZR1XDTpoH7k3wXuRb3KF_kXTTeYhu5AGK1SiOQ,146
27
+ mapFolding-0.2.1.dist-info/top_level.txt,sha256=1gP2vFaqPwHujGwb3UjtMlLEGN-943VSYFR7V4gDqW8,17
28
+ mapFolding-0.2.1.dist-info/RECORD,,
tests/test_other.py CHANGED
@@ -21,7 +21,7 @@ import sys
21
21
  ([2, 3, 4], [2, 3, 4], [2, 3, 4], [2, 3, 4], 24),
22
22
  ([2, 3], [2, 3], [2, 3], [2, 3], 6),
23
23
  ([2] * 11, [2] * 11, [2] * 11, [2] * 11, 2048), # power of 2
24
- ([3, 2], [3, 2], [3, 2], [3, 2], 6), # return value is the input when valid
24
+ ([3, 2], [3, 2], [3, 2], [2, 3], 6), # return value is the input when valid
25
25
  ([3] * 5, [3] * 5, [3] * 5, [3, 3, 3, 3, 3], 243), # power of 3
26
26
  ([None], TypeError, TypeError, TypeError, TypeError), # None
27
27
  ([True], TypeError, TypeError, TypeError, TypeError), # bool
@@ -30,8 +30,8 @@ import sys
30
30
  ([complex(1,1)], ValueError, ValueError, ValueError, ValueError), # complex number
31
31
  ([float('inf')], ValueError, ValueError, ValueError, ValueError), # infinity
32
32
  ([float('nan')], ValueError, ValueError, ValueError, ValueError), # NaN
33
- ([sys.maxsize - 1, 1], [sys.maxsize - 1, 1], [sys.maxsize - 1, 1], [sys.maxsize - 1, 1], sys.maxsize - 1), # near maxint
34
- ([sys.maxsize // 2, sys.maxsize // 2, 2], [sys.maxsize // 2, sys.maxsize // 2, 2], [sys.maxsize // 2, sys.maxsize // 2, 2], [sys.maxsize // 2, sys.maxsize // 2, 2], OverflowError), # overflow protection
33
+ ([sys.maxsize - 1, 1], [sys.maxsize - 1, 1], [sys.maxsize - 1, 1], [1, sys.maxsize - 1], sys.maxsize - 1), # near maxint
34
+ ([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
35
35
  ([sys.maxsize, sys.maxsize], [sys.maxsize, sys.maxsize], [sys.maxsize, sys.maxsize], [sys.maxsize, sys.maxsize], OverflowError), # overflow protection
36
36
  (range(3, 7), [3, 4, 5, 6], [3, 4, 5, 6], [3, 4, 5, 6], 360), # range sequence type
37
37
  (tuple([3, 5, 7]), [3, 5, 7], [3, 5, 7], [3, 5, 7], 105), # tuple sequence type
@@ -64,8 +64,5 @@ def test_oopsieKwargsie() -> None:
64
64
  for testName, testFunction in makeTestSuiteOopsieKwargsie(oopsieKwargsie).items():
65
65
  testFunction()
66
66
 
67
- def test_countFolds_invalid_computationDivisions() -> None:
68
- standardComparison(ValueError, countFolds, [2, 2], {"wrong": "value"})
69
-
70
67
  def test_parseListDimensions_noDimensions() -> None:
71
68
  standardComparison(ValueError, parseDimensions, [])
tests/test_tasks.py CHANGED
@@ -4,15 +4,18 @@ from typing import List, Dict, Tuple
4
4
 
5
5
  # TODO add a test. `C` = number of logical cores available. `n = C + 1`. Ensure that `[2,n]` is computed correctly.
6
6
 
7
- def test_foldings_computationDivisions(listDimensionsTest_countFolds: List[int], foldsTotalKnown: Dict[Tuple[int, ...], int]) -> None:
8
- standardComparison(foldsTotalKnown[tuple(listDimensionsTest_countFolds)], countFolds, listDimensionsTest_countFolds, True)
7
+ def test_countFolds_computationDivisions(listDimensionsTest_countFolds: List[int], foldsTotalKnown: Dict[Tuple[int, ...], int]) -> None:
8
+ standardComparison(foldsTotalKnown[tuple(listDimensionsTest_countFolds)], countFolds, listDimensionsTest_countFolds, None, 'maximum')
9
9
 
10
10
  def test_defineConcurrencyLimit() -> None:
11
11
  testSuite = makeTestSuiteConcurrencyLimit(defineConcurrencyLimit)
12
12
  for testName, testFunction in testSuite.items():
13
13
  testFunction()
14
14
 
15
- @pytest.mark.parametrize("cpuLimitValue", [{"invalid": True}, ["weird"]])
16
- def test_countFolds_cpuLimitOopsie(cpuLimitValue: Dict[str, bool] | List[str]) -> None:
15
+ @pytest.mark.parametrize("CPUlimitParameter", [{"invalid": True}, ["weird"]])
16
+ def test_countFolds_cpuLimitOopsie(listDimensionsTestFunctionality: List[int], CPUlimitParameter: Dict[str, bool] | List[str]) -> None:
17
17
  # This forces CPUlimit = oopsieKwargsie(cpuLimitValue).
18
- standardComparison(ValueError, countFolds, [2, 2], True, cpuLimitValue)
18
+ standardComparison(ValueError, countFolds, listDimensionsTestFunctionality, None, 'cpu', CPUlimitParameter)
19
+
20
+ def test_countFolds_invalid_computationDivisions(listDimensionsTestFunctionality: List[int]) -> None:
21
+ standardComparison(ValueError, countFolds, listDimensionsTestFunctionality, None, {"wrong": "value"})