mapFolding 0.2.1__py3-none-any.whl → 0.2.3__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.
@@ -12,7 +12,7 @@ def foldings(p: Sequence[int]) -> int:
12
12
  p: An array of integers representing the dimensions of the map.
13
13
 
14
14
  Returns:
15
- f: The number of distinct foldings for the given map dimensions.
15
+ G: The number of distinct foldings for the given map dimensions.
16
16
 
17
17
  NOTE If there are fewer than two dimensions, any dimensions are not positive, or any dimensions are not integers, the output will be unreliable.
18
18
  """
@@ -29,7 +29,7 @@ def foldings(p: Sequence[int]) -> int:
29
29
  B = [0] * (n + 1)
30
30
  count = [0] * (n + 1)
31
31
  gapter = [0] * (n + 1)
32
- gap = [0] * (n * n + 1)
32
+ gap = [0] * (n * n + 1)
33
33
 
34
34
  # B[m] is the leaf below leaf m in the current folding,
35
35
  # A[m] the leaf above. count[m] is the no. of sections in which
@@ -64,13 +64,13 @@ def foldings(p: Sequence[int]) -> int:
64
64
  # P[i] = p[1] x ... x p[i], C[i][m] = i-th co-ordinate of leaf m,
65
65
  # D[i][l][m] = leaf connected to m in section i when inserting l;
66
66
 
67
- f: int = 0
67
+ G: int = 0
68
68
  l: int = 1
69
69
 
70
70
  # kick off with null folding
71
71
  while l > 0:
72
72
  if l > n:
73
- f = f + 1
73
+ G = G + 1
74
74
  else:
75
75
  dd: int = 0
76
76
  gg: int = gapter[l - 1]
@@ -118,4 +118,4 @@ def foldings(p: Sequence[int]) -> int:
118
118
  A[B[l]] = l
119
119
  gapter[l] = g
120
120
  l = l + 1
121
- return f
121
+ return G
@@ -21,11 +21,11 @@ gapter = gapRangeStart = 3
21
21
 
22
22
  # Indices of array `my`, which holds dynamic, small, unsigned, integer values.
23
23
  tricky = [
24
- (activeLeaf1ndex := 0),
25
- (activeGap1ndex := 1),
24
+ (leaf1ndex := 0),
25
+ (gap1ndex := 1),
26
26
  (unconstrainedLeaf := 2),
27
27
  (gap1ndexCeiling := 3),
28
- (leaf1ndexConnectee := 4),
28
+ (leafConnectee := 4),
29
29
  (taskIndex := 5),
30
30
  (dimension1ndex := 6),
31
31
  (foldingsSubtotal := 7),
@@ -76,7 +76,7 @@ def countFoldings(TEMPLATEtrack: NDArray,
76
76
  ):
77
77
 
78
78
  TEMPLATEmy = numpy.zeros(COUNTindicesDynamic, dtype=numpy.int64)
79
- TEMPLATEmy[activeLeaf1ndex] = 1
79
+ TEMPLATEmy[leaf1ndex] = 1
80
80
 
81
81
  taskDivisions = 0
82
82
  # taskDivisions = the[leavesTotal]
@@ -87,61 +87,61 @@ def countFoldings(TEMPLATEtrack: NDArray,
87
87
  my: NDArray) -> tuple[NDArray, NDArray, NDArray]:
88
88
  foldingsTotal = 0
89
89
  while True:
90
- if my[activeLeaf1ndex] <= 1 or track[leafBelow][0] == 1:
91
- if my[activeLeaf1ndex] > the[leavesTotal]:
90
+ if my[leaf1ndex] <= 1 or track[leafBelow][0] == 1:
91
+ if my[leaf1ndex] > the[leavesTotal]:
92
92
  foldingsTotal += the[leavesTotal]
93
93
  else:
94
94
  my[unconstrainedLeaf] = 0
95
- my[gap1ndexCeiling] = track[gapRangeStart][my[activeLeaf1ndex] - 1]
96
- my[activeGap1ndex] = my[gap1ndexCeiling]
95
+ my[gap1ndexCeiling] = track[gapRangeStart][my[leaf1ndex] - 1]
96
+ my[gap1ndex] = my[gap1ndexCeiling]
97
97
 
98
98
  for PREPAREdimension1ndex in range(1, the[dimensionsPlus1]):
99
- if connectionGraph[PREPAREdimension1ndex][my[activeLeaf1ndex]][my[activeLeaf1ndex]] == my[activeLeaf1ndex]:
99
+ if connectionGraph[PREPAREdimension1ndex][my[leaf1ndex]][my[leaf1ndex]] == my[leaf1ndex]:
100
100
  my[unconstrainedLeaf] += 1
101
101
  else:
102
- my[leaf1ndexConnectee] = connectionGraph[PREPAREdimension1ndex][my[activeLeaf1ndex]][my[activeLeaf1ndex]]
103
- while my[leaf1ndexConnectee] != my[activeLeaf1ndex]:
102
+ my[leafConnectee] = connectionGraph[PREPAREdimension1ndex][my[leaf1ndex]][my[leaf1ndex]]
103
+ while my[leafConnectee] != my[leaf1ndex]:
104
104
 
105
- if my[leaf1ndexConnectee] != my[activeLeaf1ndex]:
105
+ if my[leafConnectee] != my[leaf1ndex]:
106
106
  my[dimension1ndex] = PREPAREdimension1ndex
107
107
  return track, gapsWhere, my
108
108
 
109
- if my[activeLeaf1ndex] != the[leavesTotal]:
110
- gapsWhere[my[gap1ndexCeiling]] = my[leaf1ndexConnectee]
111
- if track[countDimensionsGapped][my[leaf1ndexConnectee]] == 0:
109
+ if my[leaf1ndex] != the[leavesTotal]:
110
+ gapsWhere[my[gap1ndexCeiling]] = my[leafConnectee]
111
+ if track[countDimensionsGapped][my[leafConnectee]] == 0:
112
112
  my[gap1ndexCeiling] += 1
113
- track[countDimensionsGapped][my[leaf1ndexConnectee]] += 1
113
+ track[countDimensionsGapped][my[leafConnectee]] += 1
114
114
  else:
115
115
  print("else")
116
116
  my[dimension1ndex] = PREPAREdimension1ndex
117
117
  return track, gapsWhere, my
118
- # PREPAREmy[leaf1ndexConnectee] % the[leavesTotal] == PREPAREmy[taskIndex]
119
- my[leaf1ndexConnectee] = connectionGraph[dimension1ndex][my[activeLeaf1ndex]][track[leafBelow][my[leaf1ndexConnectee]]]
118
+ # PREPAREmy[leafConnectee] % the[leavesTotal] == PREPAREmy[taskIndex]
119
+ my[leafConnectee] = connectionGraph[dimension1ndex][my[leaf1ndex]][track[leafBelow][my[leafConnectee]]]
120
120
 
121
121
  if my[unconstrainedLeaf] == the[dimensionsTotal]:
122
- for leaf1ndex in range(my[activeLeaf1ndex]):
123
- gapsWhere[my[gap1ndexCeiling]] = leaf1ndex
122
+ for indexLeaf in range(my[leaf1ndex]):
123
+ gapsWhere[my[gap1ndexCeiling]] = indexLeaf
124
124
  my[gap1ndexCeiling] += 1
125
125
 
126
- for indexMiniGap in range(my[activeGap1ndex], my[gap1ndexCeiling]):
127
- gapsWhere[my[activeGap1ndex]] = gapsWhere[indexMiniGap]
126
+ for indexMiniGap in range(my[gap1ndex], my[gap1ndexCeiling]):
127
+ gapsWhere[my[gap1ndex]] = gapsWhere[indexMiniGap]
128
128
  if track[countDimensionsGapped][gapsWhere[indexMiniGap]] == the[dimensionsTotal] - my[unconstrainedLeaf]:
129
- my[activeGap1ndex] += 1
129
+ my[gap1ndex] += 1
130
130
  track[countDimensionsGapped][gapsWhere[indexMiniGap]] = 0
131
131
 
132
- while my[activeLeaf1ndex] > 0 and my[activeGap1ndex] == track[gapRangeStart][my[activeLeaf1ndex] - 1]:
133
- my[activeLeaf1ndex] -= 1
134
- track[leafBelow][track[leafAbove][my[activeLeaf1ndex]]] = track[leafBelow][my[activeLeaf1ndex]]
135
- track[leafAbove][track[leafBelow][my[activeLeaf1ndex]]] = track[leafAbove][my[activeLeaf1ndex]]
132
+ while my[leaf1ndex] > 0 and my[gap1ndex] == track[gapRangeStart][my[leaf1ndex] - 1]:
133
+ my[leaf1ndex] -= 1
134
+ track[leafBelow][track[leafAbove][my[leaf1ndex]]] = track[leafBelow][my[leaf1ndex]]
135
+ track[leafAbove][track[leafBelow][my[leaf1ndex]]] = track[leafAbove][my[leaf1ndex]]
136
136
 
137
- if my[activeLeaf1ndex] > 0:
138
- my[activeGap1ndex] -= 1
139
- track[leafAbove][my[activeLeaf1ndex]] = gapsWhere[my[activeGap1ndex]]
140
- track[leafBelow][my[activeLeaf1ndex]] = track[leafBelow][track[leafAbove][my[activeLeaf1ndex]]]
141
- track[leafBelow][track[leafAbove][my[activeLeaf1ndex]]] = my[activeLeaf1ndex]
142
- track[leafAbove][track[leafBelow][my[activeLeaf1ndex]]] = my[activeLeaf1ndex]
143
- track[gapRangeStart][my[activeLeaf1ndex]] = my[activeGap1ndex]
144
- my[activeLeaf1ndex] += 1
137
+ if my[leaf1ndex] > 0:
138
+ my[gap1ndex] -= 1
139
+ track[leafAbove][my[leaf1ndex]] = gapsWhere[my[gap1ndex]]
140
+ track[leafBelow][my[leaf1ndex]] = track[leafBelow][track[leafAbove][my[leaf1ndex]]]
141
+ track[leafBelow][track[leafAbove][my[leaf1ndex]]] = my[leaf1ndex]
142
+ track[leafAbove][track[leafBelow][my[leaf1ndex]]] = my[leaf1ndex]
143
+ track[gapRangeStart][my[leaf1ndex]] = my[gap1ndex]
144
+ my[leaf1ndex] += 1
145
145
 
146
146
  RETURNtrack, RETURNgapsWhere, RETURNmy = prepareWork(TEMPLATEtrack.copy(), TEMPLATEgapsWhere.copy(), TEMPLATEmy.copy())
147
147
 
@@ -166,44 +166,44 @@ def doWork(track: NDArray,
166
166
  thisIsNotTheFirstPass = False
167
167
 
168
168
  while papasGotABrandNewBag:
169
- if my[activeLeaf1ndex] <= 1 or track[leafBelow][0] == 1 or if_activeLeaf1ndex_LTE_1_or_leafBelow_index_0_equals_1 == True:
169
+ if my[leaf1ndex] <= 1 or track[leafBelow][0] == 1 or if_activeLeaf1ndex_LTE_1_or_leafBelow_index_0_equals_1 == True:
170
170
  if_activeLeaf1ndex_LTE_1_or_leafBelow_index_0_equals_1 = False
171
- if my[activeLeaf1ndex] > the[leavesTotal] and thisIsNotTheFirstPass:
171
+ if my[leaf1ndex] > the[leavesTotal] and thisIsNotTheFirstPass:
172
172
  my[foldingsSubtotal] += the[leavesTotal]
173
173
  else:
174
174
  if thisIsNotTheFirstPass:
175
175
  my[unconstrainedLeaf] = 0
176
- my[gap1ndexCeiling] = track[gapRangeStart][my[activeLeaf1ndex] - 1]
177
- my[activeGap1ndex] = my[gap1ndexCeiling]
176
+ my[gap1ndexCeiling] = track[gapRangeStart][my[leaf1ndex] - 1]
177
+ my[gap1ndex] = my[gap1ndexCeiling]
178
178
 
179
179
  for_dimension1ndex_in_range_1_to_dimensionsPlus1 = True
180
180
  while for_dimension1ndex_in_range_1_to_dimensionsPlus1 == True:
181
181
  for_dimension1ndex_in_range_1_to_dimensionsPlus1 = False
182
- if connectionGraph[my[dimension1ndex]][my[activeLeaf1ndex]][my[activeLeaf1ndex]] == my[activeLeaf1ndex] and thisIsNotTheFirstPass:
182
+ if connectionGraph[my[dimension1ndex]][my[leaf1ndex]][my[leaf1ndex]] == my[leaf1ndex] and thisIsNotTheFirstPass:
183
183
  my[unconstrainedLeaf] += 1
184
184
  else:
185
185
  if thisIsNotTheFirstPass:
186
- my[leaf1ndexConnectee] = connectionGraph[my[dimension1ndex]][my[activeLeaf1ndex]][my[activeLeaf1ndex]]
187
- if my[leaf1ndexConnectee] != my[activeLeaf1ndex]:
186
+ my[leafConnectee] = connectionGraph[my[dimension1ndex]][my[leaf1ndex]][my[leaf1ndex]]
187
+ if my[leafConnectee] != my[leaf1ndex]:
188
188
  while_leaf1ndexConnectee_notEquals_activeLeaf1ndex = True
189
189
 
190
190
  while while_leaf1ndexConnectee_notEquals_activeLeaf1ndex == True:
191
191
  while_leaf1ndexConnectee_notEquals_activeLeaf1ndex = False
192
192
  thisIsNotTheFirstPass = True
193
- if taskDivisions==0 or my[activeLeaf1ndex] != taskDivisions:
193
+ if taskDivisions==0 or my[leaf1ndex] != taskDivisions:
194
194
  myTask = True
195
195
  else:
196
- modulo = my[leaf1ndexConnectee] % the[leavesTotal]
196
+ modulo = my[leafConnectee] % the[leavesTotal]
197
197
  if modulo == my[taskIndex]: myTask = True
198
198
  else:
199
199
  myTask = False
200
200
  if myTask:
201
- gapsWhere[my[gap1ndexCeiling]] = my[leaf1ndexConnectee]
202
- if track[countDimensionsGapped][my[leaf1ndexConnectee]] == 0:
201
+ gapsWhere[my[gap1ndexCeiling]] = my[leafConnectee]
202
+ if track[countDimensionsGapped][my[leafConnectee]] == 0:
203
203
  my[gap1ndexCeiling] += 1
204
- track[countDimensionsGapped][my[leaf1ndexConnectee]] += 1
205
- my[leaf1ndexConnectee] = connectionGraph[my[dimension1ndex]][my[activeLeaf1ndex]][track[leafBelow][my[leaf1ndexConnectee]]]
206
- if my[leaf1ndexConnectee] != my[activeLeaf1ndex]:
204
+ track[countDimensionsGapped][my[leafConnectee]] += 1
205
+ my[leafConnectee] = connectionGraph[my[dimension1ndex]][my[leaf1ndex]][track[leafBelow][my[leafConnectee]]]
206
+ if my[leafConnectee] != my[leaf1ndex]:
207
207
  while_leaf1ndexConnectee_notEquals_activeLeaf1ndex = True
208
208
  my[dimension1ndex] += 1
209
209
  if my[dimension1ndex] < the[dimensionsPlus1]:
@@ -212,29 +212,29 @@ def doWork(track: NDArray,
212
212
  my[dimension1ndex] = 1
213
213
 
214
214
  if my[unconstrainedLeaf] == the[dimensionsTotal]:
215
- for leaf1ndex in range(my[activeLeaf1ndex]):
215
+ for leaf1ndex in range(my[leaf1ndex]):
216
216
  gapsWhere[my[gap1ndexCeiling]] = leaf1ndex
217
217
  my[gap1ndexCeiling] += 1
218
218
 
219
- for indexMiniGap in range(my[activeGap1ndex], my[gap1ndexCeiling]):
220
- gapsWhere[my[activeGap1ndex]] = gapsWhere[indexMiniGap]
219
+ for indexMiniGap in range(my[gap1ndex], my[gap1ndexCeiling]):
220
+ gapsWhere[my[gap1ndex]] = gapsWhere[indexMiniGap]
221
221
  if track[countDimensionsGapped][gapsWhere[indexMiniGap]] == the[dimensionsTotal] - my[unconstrainedLeaf]:
222
- my[activeGap1ndex] += 1
222
+ my[gap1ndex] += 1
223
223
  track[countDimensionsGapped][gapsWhere[indexMiniGap]] = 0
224
224
 
225
- while my[activeLeaf1ndex] > 0 and my[activeGap1ndex] == track[gapRangeStart][my[activeLeaf1ndex] - 1]:
226
- my[activeLeaf1ndex] -= 1
227
- track[leafBelow][track[leafAbove][my[activeLeaf1ndex]]] = track[leafBelow][my[activeLeaf1ndex]]
228
- track[leafAbove][track[leafBelow][my[activeLeaf1ndex]]] = track[leafAbove][my[activeLeaf1ndex]]
229
-
230
- if my[activeLeaf1ndex] > 0:
231
- my[activeGap1ndex] -= 1
232
- track[leafAbove][my[activeLeaf1ndex]] = gapsWhere[my[activeGap1ndex]]
233
- track[leafBelow][my[activeLeaf1ndex]] = track[leafBelow][track[leafAbove][my[activeLeaf1ndex]]]
234
- track[leafBelow][track[leafAbove][my[activeLeaf1ndex]]] = my[activeLeaf1ndex]
235
- track[leafAbove][track[leafBelow][my[activeLeaf1ndex]]] = my[activeLeaf1ndex]
236
- track[gapRangeStart][my[activeLeaf1ndex]] = my[activeGap1ndex]
237
- my[activeLeaf1ndex] += 1
238
-
239
- if my[activeLeaf1ndex] <= 0:
225
+ while my[leaf1ndex] > 0 and my[gap1ndex] == track[gapRangeStart][my[leaf1ndex] - 1]:
226
+ my[leaf1ndex] -= 1
227
+ track[leafBelow][track[leafAbove][my[leaf1ndex]]] = track[leafBelow][my[leaf1ndex]]
228
+ track[leafAbove][track[leafBelow][my[leaf1ndex]]] = track[leafAbove][my[leaf1ndex]]
229
+
230
+ if my[leaf1ndex] > 0:
231
+ my[gap1ndex] -= 1
232
+ track[leafAbove][my[leaf1ndex]] = gapsWhere[my[gap1ndex]]
233
+ track[leafBelow][my[leaf1ndex]] = track[leafBelow][track[leafAbove][my[leaf1ndex]]]
234
+ track[leafBelow][track[leafAbove][my[leaf1ndex]]] = my[leaf1ndex]
235
+ track[leafAbove][track[leafBelow][my[leaf1ndex]]] = my[leaf1ndex]
236
+ track[gapRangeStart][my[leaf1ndex]] = my[gap1ndex]
237
+ my[leaf1ndex] += 1
238
+
239
+ if my[leaf1ndex] <= 0:
240
240
  return my[foldingsSubtotal]
@@ -0,0 +1,211 @@
1
+ from numba import njit
2
+ import numpy
3
+
4
+ @njit(cache=True)
5
+ def foldings_plus_1(p: list[int], computationDivisions: int = 0, computationIndex: int = 0) -> int:
6
+ n: int = 1 # Total number of leaves
7
+ for dimension in p:
8
+ n *= dimension
9
+
10
+ d = len(p) # Number of dimensions
11
+ # Compute arrays P, C, D as per the algorithm
12
+ P = numpy.ones(d + 1, dtype=numpy.int64)
13
+ for i in range(1, d + 1):
14
+ P[i] = P[i - 1] * p[i - 1]
15
+
16
+ # C[i][m] holds the i-th coordinate of leaf m
17
+ C = numpy.zeros((d + 1, n + 1), dtype=numpy.int64)
18
+ for i in range(1, d + 1):
19
+ for m in range(1, n + 1):
20
+ C[i][m] = ((m - 1) // P[i - 1]) - ((m - 1) // P[i]) * p[i - 1] + 1
21
+
22
+ # D[i][l][m] computes the leaf connected to m in section i when inserting l
23
+ D = numpy.zeros((d + 1, n + 1, n + 1), dtype=numpy.int64)
24
+ for i in range(1, d + 1):
25
+ for l in range(1, n + 1):
26
+ for m in range(1, l + 1):
27
+ delta = C[i][l] - C[i][m]
28
+ if delta % 2 == 0:
29
+ # If delta is even
30
+ if C[i][m] == 1:
31
+ D[i][l][m] = m
32
+ else:
33
+ D[i][l][m] = m - P[i - 1]
34
+ else:
35
+ # If delta is odd
36
+ if C[i][m] == p[i - 1] or m + P[i - 1] > l:
37
+ D[i][l][m] = m
38
+ else:
39
+ D[i][l][m] = m + P[i - 1]
40
+ # Initialize arrays/lists
41
+ A = numpy.zeros(n + 1, dtype=numpy.int64) # Leaf above leaf m
42
+ B = numpy.zeros(n + 1, dtype=numpy.int64) # Leaf below leaf m
43
+ count = numpy.zeros(n + 1, dtype=numpy.int64) # Counts for potential gaps
44
+ gapter = numpy.zeros(n + 1, dtype=numpy.int64) # Indices for gap stack per leaf
45
+ gap = numpy.zeros(n * n + 1, dtype=numpy.int64) # Stack of potential gaps
46
+
47
+
48
+ # Initialize variables for backtracking
49
+ total_count = 0 # Total number of foldings
50
+ g = 0 # Gap index
51
+ l = 1 # Current leaf
52
+
53
+ # Start backtracking loop
54
+ while l > 0:
55
+ # If we have processed all leaves, increment total count
56
+ if l > n:
57
+ total_count += 1
58
+ else:
59
+ dd = 0 # Number of sections where leaf l is unconstrained
60
+ gg = g # Temporary gap index
61
+ g = gapter[l - 1] # Reset gap index for current leaf
62
+
63
+ # Count possible gaps for leaf l in each section
64
+ for i in range(1, d + 1):
65
+ if D[i][l][l] == l:
66
+ dd += 1
67
+ else:
68
+ m = D[i][l][l]
69
+ while m != l:
70
+ if computationDivisions == 0 or l != computationDivisions or m % computationDivisions == computationIndex:
71
+ gap[gg] = m
72
+ if count[m] == 0:
73
+ gg += 1
74
+ count[m] += 1
75
+ m = D[i][l][B[m]]
76
+
77
+ # If leaf l is unconstrained in all sections, it can be inserted anywhere
78
+ if dd == d:
79
+ for m in range(l):
80
+ gap[gg] = m
81
+ gg += 1
82
+
83
+ # Filter gaps that are common to all sections
84
+ for j in range(g, gg):
85
+ gap[g] = gap[j]
86
+ if count[gap[j]] == d - dd:
87
+ g += 1
88
+ count[gap[j]] = 0 # Reset count for next iteration
89
+
90
+ # Recursive backtracking steps
91
+ while l > 0 and g == gapter[l - 1]:
92
+ l -= 1
93
+ B[A[l]] = B[l]
94
+ A[B[l]] = A[l]
95
+
96
+ if l > 0:
97
+ g -= 1
98
+ A[l] = gap[g]
99
+ B[l] = B[A[l]]
100
+ B[A[l]] = l
101
+ A[B[l]] = l
102
+ gapter[l] = g # Save current gap index
103
+ l += 1 # Move to next leaf
104
+
105
+ return total_count
106
+
107
+ @njit(cache=True)
108
+ def foldings(p: list[int], computationDivisions: int = 0, computationIndex: int = 0) -> int:
109
+ n: int = 1 # Total number of leaves
110
+ for dimension in p:
111
+ n *= dimension
112
+
113
+ d = len(p) # Number of dimensions
114
+ # Compute arrays P, C, D as per the algorithm
115
+ P = numpy.ones(d + 1, dtype=numpy.int64)
116
+ for i in range(1, d + 1):
117
+ P[i] = P[i - 1] * p[i - 1]
118
+
119
+ # C[i][m] holds the i-th coordinate of leaf m
120
+ C = numpy.zeros((d + 1, n + 1), dtype=numpy.int64)
121
+ for i in range(1, d + 1):
122
+ for m in range(1, n + 1):
123
+ C[i][m] = ((m - 1) // P[i - 1]) - ((m - 1) // P[i]) * p[i - 1] + 1
124
+ # C[i][m] = ((m - 1) // P[i - 1]) % p[i - 1] + 1 # NOTE different, but either one works
125
+
126
+ # D[i][l][m] computes the leaf connected to m in section i when inserting l
127
+ D = numpy.zeros((d + 1, n + 1, n + 1), dtype=numpy.int64)
128
+ for i in range(1, d + 1):
129
+ for l in range(1, n + 1):
130
+ for m in range(1, l + 1):
131
+ delta = C[i][l] - C[i][m]
132
+ if delta % 2 == 0:
133
+ # If delta is even
134
+ if C[i][m] == 1:
135
+ D[i][l][m] = m
136
+ else:
137
+ D[i][l][m] = m - P[i - 1]
138
+ else:
139
+ # If delta is odd
140
+ if C[i][m] == p[i - 1] or m + P[i - 1] > l:
141
+ D[i][l][m] = m
142
+ else:
143
+ D[i][l][m] = m + P[i - 1]
144
+ # Initialize arrays/lists
145
+ A = numpy.zeros(n + 1, dtype=numpy.int64) # Leaf above leaf m
146
+ B = numpy.zeros(n + 1, dtype=numpy.int64) # Leaf below leaf m
147
+ count = numpy.zeros(n + 1, dtype=numpy.int64) # Counts for potential gaps
148
+ gapter = numpy.zeros(n + 1, dtype=numpy.int64) # Indices for gap stack per leaf
149
+ gap = numpy.zeros(n * n + 1, dtype=numpy.int64) # Stack of potential gaps
150
+
151
+
152
+ # Initialize variables for backtracking
153
+ total_count = 0 # Total number of foldings
154
+ g = 0 # Gap index
155
+ l = 1 # Current leaf
156
+
157
+ # Start backtracking loop
158
+ while l > 0:
159
+ if l <= 1 or B[0] == 1: # NOTE different
160
+ # NOTE the above `if` statement encloses the the if/else block below
161
+ # NOTE these changes increase the throughput by more than an order of magnitude
162
+ if l > n:
163
+ total_count += n
164
+ else:
165
+ dd = 0 # Number of sections where leaf l is unconstrained
166
+ gg = gapter[l - 1] # Track possible gaps # NOTE different, but not important
167
+ g = gg # NOTE different, but not important
168
+
169
+ # Count possible gaps for leaf l in each section
170
+ for i in range(1, d + 1):
171
+ if D[i][l][l] == l:
172
+ dd += 1
173
+ else:
174
+ m = D[i][l][l]
175
+ while m != l:
176
+ if computationDivisions == 0 or l != computationDivisions or m % computationDivisions == computationIndex:
177
+ gap[gg] = m
178
+ if count[m] == 0:
179
+ gg += 1
180
+ count[m] += 1
181
+ m = D[i][l][B[m]]
182
+
183
+ # If leaf l is unconstrained in all sections, it can be inserted anywhere
184
+ if dd == d:
185
+ for m in range(l):
186
+ gap[gg] = m
187
+ gg += 1
188
+
189
+ # Filter gaps that are common to all sections
190
+ for j in range(g, gg):
191
+ gap[g] = gap[j]
192
+ if count[gap[j]] == d - dd:
193
+ g += 1
194
+ count[gap[j]] = 0 # Reset count for next iteration
195
+
196
+ # Recursive backtracking steps
197
+ while l > 0 and g == gapter[l - 1]:
198
+ l -= 1
199
+ B[A[l]] = B[l]
200
+ A[B[l]] = A[l]
201
+
202
+ if l > 0:
203
+ g -= 1
204
+ A[l] = gap[g]
205
+ B[l] = B[A[l]]
206
+ B[A[l]] = l
207
+ A[B[l]] = l
208
+ gapter[l] = g # Save current gap index
209
+ l += 1 # Move to next leaf
210
+
211
+ return total_count
mapFolding/startHere.py CHANGED
@@ -1,4 +1,4 @@
1
- from mapFolding import outfitFoldings
1
+ from mapFolding import outfitCountFolds, getFilenameFoldsTotal
2
2
  from typing import Optional, Sequence, Type, Union
3
3
  import os
4
4
  import pathlib
@@ -13,9 +13,9 @@ def countFolds(listDimensions: Sequence[int], writeFoldsTotal: Optional[Union[st
13
13
  computationDivisions (None):
14
14
  Whether and how to divide the computational work. See notes for details.
15
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.
16
+ **keywordArguments: Additional arguments including `dtypeDefault` and `dtypeLarge` for data type specifications.
17
17
  Returns:
18
- foldsTotal: Total number of distinct ways to fold a map of the given dimensions.
18
+ foldsSubTotals: Total number of distinct ways to fold a map of the given dimensions.
19
19
 
20
20
  Computation divisions:
21
21
  - None: no division of the computation into tasks; sets task divisions to 0
@@ -34,29 +34,29 @@ def countFolds(listDimensions: Sequence[int], writeFoldsTotal: Optional[Union[st
34
34
  N.B.: You probably don't want to divide the computation into tasks.
35
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.
36
36
  """
37
- stateUniversal = outfitFoldings(listDimensions, computationDivisions=computationDivisions, CPUlimit=CPUlimit, **keywordArguments)
37
+ stateUniversal = outfitCountFolds(listDimensions, computationDivisions=computationDivisions, CPUlimit=CPUlimit, **keywordArguments)
38
38
 
39
39
  pathFilenameFoldsTotal = None
40
40
  if writeFoldsTotal is not None:
41
41
  pathFilenameFoldsTotal = pathlib.Path(writeFoldsTotal)
42
42
  if pathFilenameFoldsTotal.is_dir():
43
- filenameFoldsTotalDEFAULT = str(sorted(stateUniversal['mapShape'])).replace(' ', '') + '.foldsTotal'
43
+ filenameFoldsTotalDEFAULT = getFilenameFoldsTotal(stateUniversal['mapShape'])
44
44
  pathFilenameFoldsTotal = pathFilenameFoldsTotal / filenameFoldsTotalDEFAULT
45
45
  pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
46
46
 
47
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.
48
48
  from mapFolding.babbage import _countFolds
49
- foldsTotal = _countFolds(**stateUniversal)
50
- # foldsTotal = benchmarkSherpa(**stateUniversal)
49
+ _countFolds(**stateUniversal)
50
+ # foldsSubTotals = benchmarkSherpa(**stateUniversal)
51
+
52
+ foldsTotal = stateUniversal['foldsSubTotals'].sum().item()
51
53
 
52
54
  if pathFilenameFoldsTotal is not None:
53
55
  try:
54
56
  pathFilenameFoldsTotal.write_text(str(foldsTotal))
55
57
  except Exception as ERRORmessage:
56
58
  print(ERRORmessage)
57
- print("\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal")
58
- print(f"{foldsTotal=}")
59
- print("\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal")
59
+ print(f"\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n\n{foldsTotal=}\n\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal")
60
60
 
61
61
  return foldsTotal
62
62
 
@@ -65,6 +65,6 @@ def countFolds(listDimensions: Sequence[int], writeFoldsTotal: Optional[Union[st
65
65
  # from typing import Any, Tuple
66
66
  # from mapFolding.benchmarks.benchmarking import recordBenchmarks
67
67
  # @recordBenchmarks()
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]]):
68
+ # def benchmarkSherpa(connectionGraph: NDArray[integer[Any]], foldsSubTotals: NDArray[integer[Any]], gapsWhere: NDArray[integer[Any]], mapShape: Tuple[int, ...], my: NDArray[integer[Any]], the: NDArray[integer[Any]], track: NDArray[integer[Any]]):
69
69
  # from mapFolding.babbage import _countFolds
70
- # return _countFolds(connectionGraph, foldsTotal, mapShape, my, gapsWhere, the, track)
70
+ # return _countFolds(connectionGraph, foldsSubTotals, gapsWhere, mapShape, my, the, track)
mapFolding/theSSOT.py CHANGED
@@ -5,13 +5,17 @@ import numpy.typing
5
5
  import pathlib
6
6
  import sys
7
7
 
8
+ dtypeLarge = numpy.int64
9
+ dtypeDefault = dtypeLarge
10
+ dtypeSmall = dtypeDefault
11
+
8
12
  try:
9
13
  _pathModule = pathlib.Path(__file__).parent
10
14
  except NameError:
11
15
  _pathModule = pathlib.Path.cwd()
12
16
 
13
17
  pathJobDEFAULT = _pathModule / "jobs"
14
- """filenameFoldsTotal = str(sorted(mapShape)).replace(' ', '') + '.foldsTotal'"""
18
+
15
19
  if 'google.colab' in sys.modules:
16
20
  pathJobDEFAULT = pathlib.Path("/content/drive/MyDrive") / "jobs"
17
21
 
@@ -24,7 +28,8 @@ class EnumIndices(enum.IntEnum):
24
28
  return count
25
29
 
26
30
  def __index__(self) -> int:
27
- """Make the enum work with array indexing."""
31
+ """Adapt enum to the ultra-rare event of indexing a NumPy 'ndarray', which is not the
32
+ same as `array.array`. See NumPy.org; I think it will be very popular someday."""
28
33
  return self.value
29
34
 
30
35
  class indexMy(EnumIndices):
@@ -54,7 +59,7 @@ class indexTrack(EnumIndices):
54
59
 
55
60
  class computationState(TypedDict):
56
61
  connectionGraph: numpy.typing.NDArray[numpy.integer[Any]]
57
- foldsTotal: numpy.ndarray[numpy.int64, numpy.dtype[numpy.int64]]
62
+ foldsSubTotals: numpy.ndarray[numpy.int64, numpy.dtype[numpy.int64]]
58
63
  mapShape: Tuple[int, ...]
59
64
  my: numpy.typing.NDArray[numpy.integer[Any]]
60
65
  gapsWhere: numpy.typing.NDArray[numpy.integer[Any]]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mapFolding
3
- Version: 0.2.1
3
+ Version: 0.2.3
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
@@ -25,7 +25,7 @@ Requires-Dist: pytest-xdist; extra == "testing"
25
25
 
26
26
  # Algorithm(s) for counting distinct ways to fold a map (or a strip of stamps)
27
27
 
28
- `mapFolding.countFolds()` will accept arbitrary values for the map's dimensions.
28
+ The function `mapFolding.countFolds()` counts distinct ways to fold maps and strips of stamps. The function accepts two or more dimensions:
29
29
 
30
30
  ```python
31
31
  from mapFolding import countFolds
@@ -34,12 +34,12 @@ foldsTotal = countFolds( [2,10] )
34
34
 
35
35
  The directory [mapFolding/reference](https://github.com/hunterhogan/mapFolding/blob/main/mapFolding/reference) has
36
36
 
37
- - a verbatim transcription of the "procedure" published in _The Computer Journal_,
37
+ - a verbatim transcription of Lunnon's "procedure" published in 1971 by _The Computer Journal_,
38
38
  - multiple referential versions of the procedure with explanatory comments including
39
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
- [![Python Tests](https://github.com/hunterhogan/mapFolding/actions/workflows/unittests.yml/badge.svg)](https://github.com/hunterhogan/mapFolding/actions/workflows/unittests.yml)
42
+ [![Python Tests](https://github.com/hunterhogan/mapFolding/actions/workflows/unittests.yml/badge.svg)](https://github.com/hunterhogan/mapFolding/actions/workflows/unittests.yml) [![pip install mapFolding](https://img.shields.io/badge/pip%20install-mapFolding-gray.svg?colorB=3b434b)](https://pypi.org/project/mapFolding/) ![Static Badge](https://img.shields.io/badge/stinkin'%20badges-don't%20need-b98e5e) ![PyPI - Downloads](https://img.shields.io/pypi/dd/mapFolding) ![Static Badge](https://img.shields.io/badge/issues-I%20have%20them-brightgreen) ![GitHub repo size](https://img.shields.io/github/repo-size/hunterhogan/mapFolding)
43
43
 
44
44
  ## Simple, easy usage based on OEIS IDs
45
45
 
@@ -0,0 +1,30 @@
1
+ mapFolding/__init__.py,sha256=wnf2EzHR2unVha6-Y0gRoSPaE4PDdT4VngINa_dfT2E,337
2
+ mapFolding/babbage.py,sha256=LCtyapF8SKsECqBifqbLm7bR_i4n8VJ647w_TQxnQvE,1930
3
+ mapFolding/beDRY.py,sha256=UE4IRrb5lXN4nTuUghHfieNm2FBHgz7oBj_EqUgkadI,13800
4
+ mapFolding/lovelace.py,sha256=r4dkTGh_AgaVsWOsdPRdpPWVx3TjVjbWfPCVu3UTm6U,13495
5
+ mapFolding/oeis.py,sha256=_-fLGc1ybZ2eFxoiBrSmojMexeg6ROxtrLaBF2BzMn4,12144
6
+ mapFolding/startHere.py,sha256=glGxmefrWpxyuqrzXrbCvIo84yvPnv8l8l7Rmff2sAo,5105
7
+ mapFolding/theSSOT.py,sha256=rAyx034y33QC7IiRKaW89CMGGvqe2p-QBc3_fO-zEGM,2101
8
+ mapFolding/JAX/lunnanJAX.py,sha256=xMZloN47q-MVfjdYOM1hi9qR4OnLq7qALmGLMraevQs,14819
9
+ mapFolding/JAX/taskJAX.py,sha256=yJNeH0rL6EhJ6ppnATHF0Zf81CDMC10bnPnimVxE1hc,20037
10
+ mapFolding/benchmarks/benchmarking.py,sha256=kv85F6V9pGhZvTOImArOuxyg5rywA_T6JLH_qFXM8BM,3018
11
+ mapFolding/benchmarks/test_benchmarks.py,sha256=c4ANeR3jgqpKXFoxDeZkmAHxSuenMwsjmrhKJ1_XPqY,3659
12
+ mapFolding/reference/flattened.py,sha256=X9nvRzg7YDcpCtSDTL4YiidjshlX9rg2e6JVCY6i2u0,16547
13
+ mapFolding/reference/hunterNumba.py,sha256=0giUyqAFzP-XKcq3Kz8wIWCK0BVFhjABVJ1s-w4Jhu0,7109
14
+ mapFolding/reference/irvineJavaPort.py,sha256=Sj-63Z-OsGuDoEBXuxyjRrNmmyl0d7Yz_XuY7I47Oyg,4250
15
+ mapFolding/reference/lunnan.py,sha256=XEcql_gxvCCghb6Or3qwmPbn4IZUbZTaSmw_fUjRxZE,5037
16
+ mapFolding/reference/lunnanNumpy.py,sha256=HqDgSwTOZA-G0oophOEfc4zs25Mv4yw2aoF1v8miOLk,4653
17
+ mapFolding/reference/lunnanWhile.py,sha256=7NY2IKO5XBgol0aWWF_Fi-7oTL9pvu_z6lB0TF1uVHk,4063
18
+ mapFolding/reference/rotatedEntryPoint.py,sha256=z0QyDQtnMvXNj5ntWzzJUQUMFm1-xHGLVhtYzwmczUI,11530
19
+ mapFolding/reference/total_countPlus1vsPlusN.py,sha256=usenM8Yn_G1dqlPl7NKKkcnbohBZVZBXTQRm2S3_EDA,8106
20
+ tests/__init__.py,sha256=eg9smg-6VblOr0kisM40CpGnuDtU2JgEEWGDTFVOlW8,57
21
+ tests/conftest.py,sha256=WE3DETPaQ4zuUiw8pweTry4rp1PxvUPgeb8CN8eh5JI,13574
22
+ tests/pythons_idiotic_namespace.py,sha256=oOLDBergQqqhGuRpsXUnFD-R_6AlJipNKYHw-kk_OKw,33
23
+ tests/test_oeis.py,sha256=vxnwO-cSR68htkyMh9QMVv-lvxBo6qlwPg1Rbx4JylY,7963
24
+ tests/test_other.py,sha256=5DwOZsjezBHZzr4-9qEnybBYJEGsozBm2n9p0KYvs9E,10664
25
+ tests/test_tasks.py,sha256=Nwe4iuSjwGZvsw5CXCcic7tkBxgM5JX9mrGZMDYhAwE,1785
26
+ mapFolding-0.2.3.dist-info/METADATA,sha256=nEQXgDmmuu2NJko4R4gPUdjIrm1upUGwMIOn0s0JOOc,6442
27
+ mapFolding-0.2.3.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
28
+ mapFolding-0.2.3.dist-info/entry_points.txt,sha256=F3OUeZR1XDTpoH7k3wXuRb3KF_kXTTeYhu5AGK1SiOQ,146
29
+ mapFolding-0.2.3.dist-info/top_level.txt,sha256=1gP2vFaqPwHujGwb3UjtMlLEGN-943VSYFR7V4gDqW8,17
30
+ mapFolding-0.2.3.dist-info/RECORD,,
tests/__init__.py CHANGED
@@ -1 +1 @@
1
- from .conftest import makeDictionaryFoldsTotalKnown
1
+ from tests.conftest import makeDictionaryFoldsTotalKnown