ZhengyangZhang commited on
Commit
849e4fa
·
verified ·
1 Parent(s): 1fea394

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/__init__.cpython-312.pyc +0 -0
  2. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/coset_table.cpython-312.pyc +0 -0
  3. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/fp_groups.cpython-312.pyc +0 -0
  4. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/free_groups.cpython-312.pyc +0 -0
  5. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/galois.cpython-312.pyc +0 -0
  6. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/generators.cpython-312.pyc +0 -0
  7. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/graycode.cpython-312.pyc +0 -0
  8. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/group_constructs.cpython-312.pyc +0 -0
  9. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/group_numbers.cpython-312.pyc +0 -0
  10. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/homomorphisms.cpython-312.pyc +0 -0
  11. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/named_groups.cpython-312.pyc +0 -0
  12. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/partitions.cpython-312.pyc +0 -0
  13. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/pc_groups.cpython-312.pyc +0 -0
  14. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/polyhedron.cpython-312.pyc +0 -0
  15. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/prufer.cpython-312.pyc +0 -0
  16. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/rewritingsystem.cpython-312.pyc +0 -0
  17. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/rewritingsystem_fsm.cpython-312.pyc +0 -0
  18. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/schur_number.cpython-312.pyc +0 -0
  19. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/subsets.cpython-312.pyc +0 -0
  20. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/tensor_can.cpython-312.pyc +0 -0
  21. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/testutil.cpython-312.pyc +0 -0
  22. lib/python3.12/site-packages/sympy/combinatorics/__pycache__/util.cpython-312.pyc +0 -0
  23. lib/python3.12/site-packages/sympy/combinatorics/group_numbers.py +296 -0
  24. lib/python3.12/site-packages/sympy/combinatorics/partitions.py +745 -0
  25. lib/python3.12/site-packages/sympy/combinatorics/rewritingsystem.py +453 -0
  26. lib/python3.12/site-packages/sympy/combinatorics/tests/__init__.py +0 -0
  27. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/__init__.cpython-312.pyc +0 -0
  28. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_coset_table.cpython-312.pyc +0 -0
  29. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_fp_groups.cpython-312.pyc +0 -0
  30. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_free_groups.cpython-312.pyc +0 -0
  31. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_galois.cpython-312.pyc +0 -0
  32. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_generators.cpython-312.pyc +0 -0
  33. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_graycode.cpython-312.pyc +0 -0
  34. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_group_constructs.cpython-312.pyc +0 -0
  35. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_group_numbers.cpython-312.pyc +0 -0
  36. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_homomorphisms.cpython-312.pyc +0 -0
  37. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_named_groups.cpython-312.pyc +0 -0
  38. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_partitions.cpython-312.pyc +0 -0
  39. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_pc_groups.cpython-312.pyc +0 -0
  40. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_perm_groups.cpython-312.pyc +0 -0
  41. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_permutations.cpython-312.pyc +0 -0
  42. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_polyhedron.cpython-312.pyc +0 -0
  43. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_prufer.cpython-312.pyc +0 -0
  44. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_rewriting.cpython-312.pyc +0 -0
  45. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_schur_number.cpython-312.pyc +0 -0
  46. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_subsets.cpython-312.pyc +0 -0
  47. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_tensor_can.cpython-312.pyc +0 -0
  48. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_testutil.cpython-312.pyc +0 -0
  49. lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_util.cpython-312.pyc +0 -0
  50. lib/python3.12/site-packages/sympy/combinatorics/tests/test_coset_table.py +825 -0
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (1.67 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/coset_table.cpython-312.pyc ADDED
Binary file (48 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/fp_groups.cpython-312.pyc ADDED
Binary file (55.3 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/free_groups.cpython-312.pyc ADDED
Binary file (50.4 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/galois.cpython-312.pyc ADDED
Binary file (24.3 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/generators.cpython-312.pyc ADDED
Binary file (11.1 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/graycode.cpython-312.pyc ADDED
Binary file (14.3 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/group_constructs.cpython-312.pyc ADDED
Binary file (2.72 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/group_numbers.cpython-312.pyc ADDED
Binary file (12 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/homomorphisms.cpython-312.pyc ADDED
Binary file (23.8 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/named_groups.cpython-312.pyc ADDED
Binary file (10.2 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/partitions.cpython-312.pyc ADDED
Binary file (27.1 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/pc_groups.cpython-312.pyc ADDED
Binary file (25.7 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/polyhedron.cpython-312.pyc ADDED
Binary file (46.1 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/prufer.cpython-312.pyc ADDED
Binary file (15.3 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/rewritingsystem.cpython-312.pyc ADDED
Binary file (18.3 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/rewritingsystem_fsm.cpython-312.pyc ADDED
Binary file (3.35 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/schur_number.cpython-312.pyc ADDED
Binary file (6.21 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/subsets.cpython-312.pyc ADDED
Binary file (20.2 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/tensor_can.cpython-312.pyc ADDED
Binary file (45 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/testutil.cpython-312.pyc ADDED
Binary file (12.7 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/__pycache__/util.cpython-312.pyc ADDED
Binary file (17.4 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/group_numbers.py ADDED
@@ -0,0 +1,296 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from itertools import chain, combinations
2
+
3
+ from sympy.external.gmpy import gcd
4
+ from sympy.ntheory.factor_ import factorint
5
+ from sympy.utilities.misc import as_int
6
+
7
+
8
+ def _is_nilpotent_number(factors: dict) -> bool:
9
+ """ Check whether `n` is a nilpotent number.
10
+ Note that ``factors`` is a prime factorization of `n`.
11
+
12
+ This is a low-level helper for ``is_nilpotent_number``, for internal use.
13
+ """
14
+ for p in factors.keys():
15
+ for q, e in factors.items():
16
+ # We want to calculate
17
+ # any(pow(q, k, p) == 1 for k in range(1, e + 1))
18
+ m = 1
19
+ for _ in range(e):
20
+ m = m*q % p
21
+ if m == 1:
22
+ return False
23
+ return True
24
+
25
+
26
+ def is_nilpotent_number(n) -> bool:
27
+ """
28
+ Check whether `n` is a nilpotent number. A number `n` is said to be
29
+ nilpotent if and only if every finite group of order `n` is nilpotent.
30
+ For more information see [1]_.
31
+
32
+ Examples
33
+ ========
34
+
35
+ >>> from sympy.combinatorics.group_numbers import is_nilpotent_number
36
+ >>> from sympy import randprime
37
+ >>> is_nilpotent_number(21)
38
+ False
39
+ >>> is_nilpotent_number(randprime(1, 30)**12)
40
+ True
41
+
42
+ References
43
+ ==========
44
+
45
+ .. [1] Pakianathan, J., Shankar, K., Nilpotent Numbers,
46
+ The American Mathematical Monthly, 107(7), 631-634.
47
+ .. [2] https://oeis.org/A056867
48
+
49
+ """
50
+ n = as_int(n)
51
+ if n <= 0:
52
+ raise ValueError("n must be a positive integer, not %i" % n)
53
+ return _is_nilpotent_number(factorint(n))
54
+
55
+
56
+ def is_abelian_number(n) -> bool:
57
+ """
58
+ Check whether `n` is an abelian number. A number `n` is said to be abelian
59
+ if and only if every finite group of order `n` is abelian. For more
60
+ information see [1]_.
61
+
62
+ Examples
63
+ ========
64
+
65
+ >>> from sympy.combinatorics.group_numbers import is_abelian_number
66
+ >>> from sympy import randprime
67
+ >>> is_abelian_number(4)
68
+ True
69
+ >>> is_abelian_number(randprime(1, 2000)**2)
70
+ True
71
+ >>> is_abelian_number(60)
72
+ False
73
+
74
+ References
75
+ ==========
76
+
77
+ .. [1] Pakianathan, J., Shankar, K., Nilpotent Numbers,
78
+ The American Mathematical Monthly, 107(7), 631-634.
79
+ .. [2] https://oeis.org/A051532
80
+
81
+ """
82
+ n = as_int(n)
83
+ if n <= 0:
84
+ raise ValueError("n must be a positive integer, not %i" % n)
85
+ factors = factorint(n)
86
+ return all(e < 3 for e in factors.values()) and _is_nilpotent_number(factors)
87
+
88
+
89
+ def is_cyclic_number(n) -> bool:
90
+ """
91
+ Check whether `n` is a cyclic number. A number `n` is said to be cyclic
92
+ if and only if every finite group of order `n` is cyclic. For more
93
+ information see [1]_.
94
+
95
+ Examples
96
+ ========
97
+
98
+ >>> from sympy.combinatorics.group_numbers import is_cyclic_number
99
+ >>> from sympy import randprime
100
+ >>> is_cyclic_number(15)
101
+ True
102
+ >>> is_cyclic_number(randprime(1, 2000)**2)
103
+ False
104
+ >>> is_cyclic_number(4)
105
+ False
106
+
107
+ References
108
+ ==========
109
+
110
+ .. [1] Pakianathan, J., Shankar, K., Nilpotent Numbers,
111
+ The American Mathematical Monthly, 107(7), 631-634.
112
+ .. [2] https://oeis.org/A003277
113
+
114
+ """
115
+ n = as_int(n)
116
+ if n <= 0:
117
+ raise ValueError("n must be a positive integer, not %i" % n)
118
+ factors = factorint(n)
119
+ return all(e == 1 for e in factors.values()) and _is_nilpotent_number(factors)
120
+
121
+
122
+ def _holder_formula(prime_factors):
123
+ r""" Number of groups of order `n`.
124
+ where `n` is squarefree and its prime factors are ``prime_factors``.
125
+ i.e., ``n == math.prod(prime_factors)``
126
+
127
+ Explanation
128
+ ===========
129
+
130
+ When `n` is squarefree, the number of groups of order `n` is expressed by
131
+
132
+ .. math ::
133
+ \sum_{d \mid n} \prod_p \frac{p^{c(p, d)} - 1}{p - 1}
134
+
135
+ where `n=de`, `p` is the prime factor of `e`,
136
+ and `c(p, d)` is the number of prime factors `q` of `d` such that `q \equiv 1 \pmod{p}` [2]_.
137
+
138
+ The formula is elegant, but can be improved when implemented as an algorithm.
139
+ Since `n` is assumed to be squarefree, the divisor `d` of `n` can be identified with the power set of prime factors.
140
+ We let `N` be the set of prime factors of `n`.
141
+ `F = \{p \in N : \forall q \in N, q \not\equiv 1 \pmod{p} \}, M = N \setminus F`, we have the following.
142
+
143
+ .. math ::
144
+ \sum_{d \in 2^{M}} \prod_{p \in M \setminus d} \frac{p^{c(p, F \cup d)} - 1}{p - 1}
145
+
146
+ Practically, many prime factors are expected to be members of `F`, thus reducing computation time.
147
+
148
+ Parameters
149
+ ==========
150
+
151
+ prime_factors : set
152
+ The set of prime factors of ``n``. where `n` is squarefree.
153
+
154
+ Returns
155
+ =======
156
+
157
+ int : Number of groups of order ``n``
158
+
159
+ Examples
160
+ ========
161
+
162
+ >>> from sympy.combinatorics.group_numbers import _holder_formula
163
+ >>> _holder_formula({2}) # n = 2
164
+ 1
165
+ >>> _holder_formula({2, 3}) # n = 2*3 = 6
166
+ 2
167
+
168
+ See Also
169
+ ========
170
+
171
+ groups_count
172
+
173
+ References
174
+ ==========
175
+
176
+ .. [1] Otto Holder, Die Gruppen der Ordnungen p^3, pq^2, pqr, p^4,
177
+ Math. Ann. 43 pp. 301-412 (1893).
178
+ http://dx.doi.org/10.1007/BF01443651
179
+ .. [2] John H. Conway, Heiko Dietrich and E.A. O'Brien,
180
+ Counting groups: gnus, moas and other exotica
181
+ The Mathematical Intelligencer 30, 6-15 (2008)
182
+ https://doi.org/10.1007/BF02985731
183
+
184
+ """
185
+ F = {p for p in prime_factors if all(q % p != 1 for q in prime_factors)}
186
+ M = prime_factors - F
187
+
188
+ s = 0
189
+ powerset = chain.from_iterable(combinations(M, r) for r in range(len(M)+1))
190
+ for ps in powerset:
191
+ ps = set(ps)
192
+ prod = 1
193
+ for p in M - ps:
194
+ c = len([q for q in F | ps if q % p == 1])
195
+ prod *= (p**c - 1) // (p - 1)
196
+ if not prod:
197
+ break
198
+ s += prod
199
+ return s
200
+
201
+
202
+ def groups_count(n):
203
+ r""" Number of groups of order `n`.
204
+ In [1]_, ``gnu(n)`` is given, so we follow this notation here as well.
205
+
206
+ Parameters
207
+ ==========
208
+
209
+ n : Integer
210
+ ``n`` is a positive integer
211
+
212
+ Returns
213
+ =======
214
+
215
+ int : ``gnu(n)``
216
+
217
+ Raises
218
+ ======
219
+
220
+ ValueError
221
+ Number of groups of order ``n`` is unknown or not implemented.
222
+ For example, gnu(`2^{11}`) is not yet known.
223
+ On the other hand, gnu(12) is known to be 5,
224
+ but this has not yet been implemented in this function.
225
+
226
+ Examples
227
+ ========
228
+
229
+ >>> from sympy.combinatorics.group_numbers import groups_count
230
+ >>> groups_count(3) # There is only one cyclic group of order 3
231
+ 1
232
+ >>> # There are two groups of order 10: the cyclic group and the dihedral group
233
+ >>> groups_count(10)
234
+ 2
235
+
236
+ See Also
237
+ ========
238
+
239
+ is_cyclic_number
240
+ `n` is cyclic iff gnu(n) = 1
241
+
242
+ References
243
+ ==========
244
+
245
+ .. [1] John H. Conway, Heiko Dietrich and E.A. O'Brien,
246
+ Counting groups: gnus, moas and other exotica
247
+ The Mathematical Intelligencer 30, 6-15 (2008)
248
+ https://doi.org/10.1007/BF02985731
249
+ .. [2] https://oeis.org/A000001
250
+
251
+ """
252
+ n = as_int(n)
253
+ if n <= 0:
254
+ raise ValueError("n must be a positive integer, not %i" % n)
255
+ factors = factorint(n)
256
+ if len(factors) == 1:
257
+ (p, e) = list(factors.items())[0]
258
+ if p == 2:
259
+ A000679 = [1, 1, 2, 5, 14, 51, 267, 2328, 56092, 10494213, 49487367289]
260
+ if e < len(A000679):
261
+ return A000679[e]
262
+ if p == 3:
263
+ A090091 = [1, 1, 2, 5, 15, 67, 504, 9310, 1396077, 5937876645]
264
+ if e < len(A090091):
265
+ return A090091[e]
266
+ if e <= 2: # gnu(p) = 1, gnu(p**2) = 2
267
+ return e
268
+ if e == 3: # gnu(p**3) = 5
269
+ return 5
270
+ if e == 4: # if p is an odd prime, gnu(p**4) = 15
271
+ return 15
272
+ if e == 5: # if p >= 5, gnu(p**5) is expressed by the following equation
273
+ return 61 + 2*p + 2*gcd(p-1, 3) + gcd(p-1, 4)
274
+ if e == 6: # if p >= 6, gnu(p**6) is expressed by the following equation
275
+ return 3*p**2 + 39*p + 344 +\
276
+ 24*gcd(p-1, 3) + 11*gcd(p-1, 4) + 2*gcd(p-1, 5)
277
+ if e == 7: # if p >= 7, gnu(p**7) is expressed by the following equation
278
+ if p == 5:
279
+ return 34297
280
+ return 3*p**5 + 12*p**4 + 44*p**3 + 170*p**2 + 707*p + 2455 +\
281
+ (4*p**2 + 44*p + 291)*gcd(p-1, 3) + (p**2 + 19*p + 135)*gcd(p-1, 4) + \
282
+ (3*p + 31)*gcd(p-1, 5) + 4*gcd(p-1, 7) + 5*gcd(p-1, 8) + gcd(p-1, 9)
283
+ if any(e > 1 for e in factors.values()): # n is not squarefree
284
+ # some known values for small n that have more than 1 factor and are not square free (https://oeis.org/A000001)
285
+ small = {12: 5, 18: 5, 20: 5, 24: 15, 28: 4, 36: 14, 40: 14, 44: 4, 45: 2, 48: 52,
286
+ 50: 5, 52: 5, 54: 15, 56: 13, 60: 13, 63: 4, 68: 5, 72: 50, 75: 3, 76: 4,
287
+ 80: 52, 84: 15, 88: 12, 90: 10, 92: 4}
288
+ if n in small:
289
+ return small[n]
290
+ raise ValueError("Number of groups of order n is unknown or not implemented")
291
+ if len(factors) == 2: # n is squarefree semiprime
292
+ p, q = list(factors.keys())
293
+ if p > q:
294
+ p, q = q, p
295
+ return 2 if q % p == 1 else 1
296
+ return _holder_formula(set(factors.keys()))
lib/python3.12/site-packages/sympy/combinatorics/partitions.py ADDED
@@ -0,0 +1,745 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core import Basic, Dict, sympify, Tuple
2
+ from sympy.core.numbers import Integer
3
+ from sympy.core.sorting import default_sort_key
4
+ from sympy.core.sympify import _sympify
5
+ from sympy.functions.combinatorial.numbers import bell
6
+ from sympy.matrices import zeros
7
+ from sympy.sets.sets import FiniteSet, Union
8
+ from sympy.utilities.iterables import flatten, group
9
+ from sympy.utilities.misc import as_int
10
+
11
+
12
+ from collections import defaultdict
13
+
14
+
15
+ class Partition(FiniteSet):
16
+ """
17
+ This class represents an abstract partition.
18
+
19
+ A partition is a set of disjoint sets whose union equals a given set.
20
+
21
+ See Also
22
+ ========
23
+
24
+ sympy.utilities.iterables.partitions,
25
+ sympy.utilities.iterables.multiset_partitions
26
+ """
27
+
28
+ _rank = None
29
+ _partition = None
30
+
31
+ def __new__(cls, *partition):
32
+ """
33
+ Generates a new partition object.
34
+
35
+ This method also verifies if the arguments passed are
36
+ valid and raises a ValueError if they are not.
37
+
38
+ Examples
39
+ ========
40
+
41
+ Creating Partition from Python lists:
42
+
43
+ >>> from sympy.combinatorics import Partition
44
+ >>> a = Partition([1, 2], [3])
45
+ >>> a
46
+ Partition({3}, {1, 2})
47
+ >>> a.partition
48
+ [[1, 2], [3]]
49
+ >>> len(a)
50
+ 2
51
+ >>> a.members
52
+ (1, 2, 3)
53
+
54
+ Creating Partition from Python sets:
55
+
56
+ >>> Partition({1, 2, 3}, {4, 5})
57
+ Partition({4, 5}, {1, 2, 3})
58
+
59
+ Creating Partition from SymPy finite sets:
60
+
61
+ >>> from sympy import FiniteSet
62
+ >>> a = FiniteSet(1, 2, 3)
63
+ >>> b = FiniteSet(4, 5)
64
+ >>> Partition(a, b)
65
+ Partition({4, 5}, {1, 2, 3})
66
+ """
67
+ args = []
68
+ dups = False
69
+ for arg in partition:
70
+ if isinstance(arg, list):
71
+ as_set = set(arg)
72
+ if len(as_set) < len(arg):
73
+ dups = True
74
+ break # error below
75
+ arg = as_set
76
+ args.append(_sympify(arg))
77
+
78
+ if not all(isinstance(part, FiniteSet) for part in args):
79
+ raise ValueError(
80
+ "Each argument to Partition should be " \
81
+ "a list, set, or a FiniteSet")
82
+
83
+ # sort so we have a canonical reference for RGS
84
+ U = Union(*args)
85
+ if dups or len(U) < sum(len(arg) for arg in args):
86
+ raise ValueError("Partition contained duplicate elements.")
87
+
88
+ obj = FiniteSet.__new__(cls, *args)
89
+ obj.members = tuple(U)
90
+ obj.size = len(U)
91
+ return obj
92
+
93
+ def sort_key(self, order=None):
94
+ """Return a canonical key that can be used for sorting.
95
+
96
+ Ordering is based on the size and sorted elements of the partition
97
+ and ties are broken with the rank.
98
+
99
+ Examples
100
+ ========
101
+
102
+ >>> from sympy import default_sort_key
103
+ >>> from sympy.combinatorics import Partition
104
+ >>> from sympy.abc import x
105
+ >>> a = Partition([1, 2])
106
+ >>> b = Partition([3, 4])
107
+ >>> c = Partition([1, x])
108
+ >>> d = Partition(list(range(4)))
109
+ >>> l = [d, b, a + 1, a, c]
110
+ >>> l.sort(key=default_sort_key); l
111
+ [Partition({1, 2}), Partition({1}, {2}), Partition({1, x}), Partition({3, 4}), Partition({0, 1, 2, 3})]
112
+ """
113
+ if order is None:
114
+ members = self.members
115
+ else:
116
+ members = tuple(sorted(self.members,
117
+ key=lambda w: default_sort_key(w, order)))
118
+ return tuple(map(default_sort_key, (self.size, members, self.rank)))
119
+
120
+ @property
121
+ def partition(self):
122
+ """Return partition as a sorted list of lists.
123
+
124
+ Examples
125
+ ========
126
+
127
+ >>> from sympy.combinatorics import Partition
128
+ >>> Partition([1], [2, 3]).partition
129
+ [[1], [2, 3]]
130
+ """
131
+ if self._partition is None:
132
+ self._partition = sorted([sorted(p, key=default_sort_key)
133
+ for p in self.args])
134
+ return self._partition
135
+
136
+ def __add__(self, other):
137
+ """
138
+ Return permutation whose rank is ``other`` greater than current rank,
139
+ (mod the maximum rank for the set).
140
+
141
+ Examples
142
+ ========
143
+
144
+ >>> from sympy.combinatorics import Partition
145
+ >>> a = Partition([1, 2], [3])
146
+ >>> a.rank
147
+ 1
148
+ >>> (a + 1).rank
149
+ 2
150
+ >>> (a + 100).rank
151
+ 1
152
+ """
153
+ other = as_int(other)
154
+ offset = self.rank + other
155
+ result = RGS_unrank((offset) %
156
+ RGS_enum(self.size),
157
+ self.size)
158
+ return Partition.from_rgs(result, self.members)
159
+
160
+ def __sub__(self, other):
161
+ """
162
+ Return permutation whose rank is ``other`` less than current rank,
163
+ (mod the maximum rank for the set).
164
+
165
+ Examples
166
+ ========
167
+
168
+ >>> from sympy.combinatorics import Partition
169
+ >>> a = Partition([1, 2], [3])
170
+ >>> a.rank
171
+ 1
172
+ >>> (a - 1).rank
173
+ 0
174
+ >>> (a - 100).rank
175
+ 1
176
+ """
177
+ return self.__add__(-other)
178
+
179
+ def __le__(self, other):
180
+ """
181
+ Checks if a partition is less than or equal to
182
+ the other based on rank.
183
+
184
+ Examples
185
+ ========
186
+
187
+ >>> from sympy.combinatorics import Partition
188
+ >>> a = Partition([1, 2], [3, 4, 5])
189
+ >>> b = Partition([1], [2, 3], [4], [5])
190
+ >>> a.rank, b.rank
191
+ (9, 34)
192
+ >>> a <= a
193
+ True
194
+ >>> a <= b
195
+ True
196
+ """
197
+ return self.sort_key() <= sympify(other).sort_key()
198
+
199
+ def __lt__(self, other):
200
+ """
201
+ Checks if a partition is less than the other.
202
+
203
+ Examples
204
+ ========
205
+
206
+ >>> from sympy.combinatorics import Partition
207
+ >>> a = Partition([1, 2], [3, 4, 5])
208
+ >>> b = Partition([1], [2, 3], [4], [5])
209
+ >>> a.rank, b.rank
210
+ (9, 34)
211
+ >>> a < b
212
+ True
213
+ """
214
+ return self.sort_key() < sympify(other).sort_key()
215
+
216
+ @property
217
+ def rank(self):
218
+ """
219
+ Gets the rank of a partition.
220
+
221
+ Examples
222
+ ========
223
+
224
+ >>> from sympy.combinatorics import Partition
225
+ >>> a = Partition([1, 2], [3], [4, 5])
226
+ >>> a.rank
227
+ 13
228
+ """
229
+ if self._rank is not None:
230
+ return self._rank
231
+ self._rank = RGS_rank(self.RGS)
232
+ return self._rank
233
+
234
+ @property
235
+ def RGS(self):
236
+ """
237
+ Returns the "restricted growth string" of the partition.
238
+
239
+ Explanation
240
+ ===========
241
+
242
+ The RGS is returned as a list of indices, L, where L[i] indicates
243
+ the block in which element i appears. For example, in a partition
244
+ of 3 elements (a, b, c) into 2 blocks ([c], [a, b]) the RGS is
245
+ [1, 1, 0]: "a" is in block 1, "b" is in block 1 and "c" is in block 0.
246
+
247
+ Examples
248
+ ========
249
+
250
+ >>> from sympy.combinatorics import Partition
251
+ >>> a = Partition([1, 2], [3], [4, 5])
252
+ >>> a.members
253
+ (1, 2, 3, 4, 5)
254
+ >>> a.RGS
255
+ (0, 0, 1, 2, 2)
256
+ >>> a + 1
257
+ Partition({3}, {4}, {5}, {1, 2})
258
+ >>> _.RGS
259
+ (0, 0, 1, 2, 3)
260
+ """
261
+ rgs = {}
262
+ partition = self.partition
263
+ for i, part in enumerate(partition):
264
+ for j in part:
265
+ rgs[j] = i
266
+ return tuple([rgs[i] for i in sorted(
267
+ [i for p in partition for i in p], key=default_sort_key)])
268
+
269
+ @classmethod
270
+ def from_rgs(self, rgs, elements):
271
+ """
272
+ Creates a set partition from a restricted growth string.
273
+
274
+ Explanation
275
+ ===========
276
+
277
+ The indices given in rgs are assumed to be the index
278
+ of the element as given in elements *as provided* (the
279
+ elements are not sorted by this routine). Block numbering
280
+ starts from 0. If any block was not referenced in ``rgs``
281
+ an error will be raised.
282
+
283
+ Examples
284
+ ========
285
+
286
+ >>> from sympy.combinatorics import Partition
287
+ >>> Partition.from_rgs([0, 1, 2, 0, 1], list('abcde'))
288
+ Partition({c}, {a, d}, {b, e})
289
+ >>> Partition.from_rgs([0, 1, 2, 0, 1], list('cbead'))
290
+ Partition({e}, {a, c}, {b, d})
291
+ >>> a = Partition([1, 4], [2], [3, 5])
292
+ >>> Partition.from_rgs(a.RGS, a.members)
293
+ Partition({2}, {1, 4}, {3, 5})
294
+ """
295
+ if len(rgs) != len(elements):
296
+ raise ValueError('mismatch in rgs and element lengths')
297
+ max_elem = max(rgs) + 1
298
+ partition = [[] for i in range(max_elem)]
299
+ j = 0
300
+ for i in rgs:
301
+ partition[i].append(elements[j])
302
+ j += 1
303
+ if not all(p for p in partition):
304
+ raise ValueError('some blocks of the partition were empty.')
305
+ return Partition(*partition)
306
+
307
+
308
+ class IntegerPartition(Basic):
309
+ """
310
+ This class represents an integer partition.
311
+
312
+ Explanation
313
+ ===========
314
+
315
+ In number theory and combinatorics, a partition of a positive integer,
316
+ ``n``, also called an integer partition, is a way of writing ``n`` as a
317
+ list of positive integers that sum to n. Two partitions that differ only
318
+ in the order of summands are considered to be the same partition; if order
319
+ matters then the partitions are referred to as compositions. For example,
320
+ 4 has five partitions: [4], [3, 1], [2, 2], [2, 1, 1], and [1, 1, 1, 1];
321
+ the compositions [1, 2, 1] and [1, 1, 2] are the same as partition
322
+ [2, 1, 1].
323
+
324
+ See Also
325
+ ========
326
+
327
+ sympy.utilities.iterables.partitions,
328
+ sympy.utilities.iterables.multiset_partitions
329
+
330
+ References
331
+ ==========
332
+
333
+ .. [1] https://en.wikipedia.org/wiki/Partition_%28number_theory%29
334
+ """
335
+
336
+ _dict = None
337
+ _keys = None
338
+
339
+ def __new__(cls, partition, integer=None):
340
+ """
341
+ Generates a new IntegerPartition object from a list or dictionary.
342
+
343
+ Explanation
344
+ ===========
345
+
346
+ The partition can be given as a list of positive integers or a
347
+ dictionary of (integer, multiplicity) items. If the partition is
348
+ preceded by an integer an error will be raised if the partition
349
+ does not sum to that given integer.
350
+
351
+ Examples
352
+ ========
353
+
354
+ >>> from sympy.combinatorics.partitions import IntegerPartition
355
+ >>> a = IntegerPartition([5, 4, 3, 1, 1])
356
+ >>> a
357
+ IntegerPartition(14, (5, 4, 3, 1, 1))
358
+ >>> print(a)
359
+ [5, 4, 3, 1, 1]
360
+ >>> IntegerPartition({1:3, 2:1})
361
+ IntegerPartition(5, (2, 1, 1, 1))
362
+
363
+ If the value that the partition should sum to is given first, a check
364
+ will be made to see n error will be raised if there is a discrepancy:
365
+
366
+ >>> IntegerPartition(10, [5, 4, 3, 1])
367
+ Traceback (most recent call last):
368
+ ...
369
+ ValueError: The partition is not valid
370
+
371
+ """
372
+ if integer is not None:
373
+ integer, partition = partition, integer
374
+ if isinstance(partition, (dict, Dict)):
375
+ _ = []
376
+ for k, v in sorted(partition.items(), reverse=True):
377
+ if not v:
378
+ continue
379
+ k, v = as_int(k), as_int(v)
380
+ _.extend([k]*v)
381
+ partition = tuple(_)
382
+ else:
383
+ partition = tuple(sorted(map(as_int, partition), reverse=True))
384
+ sum_ok = False
385
+ if integer is None:
386
+ integer = sum(partition)
387
+ sum_ok = True
388
+ else:
389
+ integer = as_int(integer)
390
+
391
+ if not sum_ok and sum(partition) != integer:
392
+ raise ValueError("Partition did not add to %s" % integer)
393
+ if any(i < 1 for i in partition):
394
+ raise ValueError("All integer summands must be greater than one")
395
+
396
+ obj = Basic.__new__(cls, Integer(integer), Tuple(*partition))
397
+ obj.partition = list(partition)
398
+ obj.integer = integer
399
+ return obj
400
+
401
+ def prev_lex(self):
402
+ """Return the previous partition of the integer, n, in lexical order,
403
+ wrapping around to [1, ..., 1] if the partition is [n].
404
+
405
+ Examples
406
+ ========
407
+
408
+ >>> from sympy.combinatorics.partitions import IntegerPartition
409
+ >>> p = IntegerPartition([4])
410
+ >>> print(p.prev_lex())
411
+ [3, 1]
412
+ >>> p.partition > p.prev_lex().partition
413
+ True
414
+ """
415
+ d = defaultdict(int)
416
+ d.update(self.as_dict())
417
+ keys = self._keys
418
+ if keys == [1]:
419
+ return IntegerPartition({self.integer: 1})
420
+ if keys[-1] != 1:
421
+ d[keys[-1]] -= 1
422
+ if keys[-1] == 2:
423
+ d[1] = 2
424
+ else:
425
+ d[keys[-1] - 1] = d[1] = 1
426
+ else:
427
+ d[keys[-2]] -= 1
428
+ left = d[1] + keys[-2]
429
+ new = keys[-2]
430
+ d[1] = 0
431
+ while left:
432
+ new -= 1
433
+ if left - new >= 0:
434
+ d[new] += left//new
435
+ left -= d[new]*new
436
+ return IntegerPartition(self.integer, d)
437
+
438
+ def next_lex(self):
439
+ """Return the next partition of the integer, n, in lexical order,
440
+ wrapping around to [n] if the partition is [1, ..., 1].
441
+
442
+ Examples
443
+ ========
444
+
445
+ >>> from sympy.combinatorics.partitions import IntegerPartition
446
+ >>> p = IntegerPartition([3, 1])
447
+ >>> print(p.next_lex())
448
+ [4]
449
+ >>> p.partition < p.next_lex().partition
450
+ True
451
+ """
452
+ d = defaultdict(int)
453
+ d.update(self.as_dict())
454
+ key = self._keys
455
+ a = key[-1]
456
+ if a == self.integer:
457
+ d.clear()
458
+ d[1] = self.integer
459
+ elif a == 1:
460
+ if d[a] > 1:
461
+ d[a + 1] += 1
462
+ d[a] -= 2
463
+ else:
464
+ b = key[-2]
465
+ d[b + 1] += 1
466
+ d[1] = (d[b] - 1)*b
467
+ d[b] = 0
468
+ else:
469
+ if d[a] > 1:
470
+ if len(key) == 1:
471
+ d.clear()
472
+ d[a + 1] = 1
473
+ d[1] = self.integer - a - 1
474
+ else:
475
+ a1 = a + 1
476
+ d[a1] += 1
477
+ d[1] = d[a]*a - a1
478
+ d[a] = 0
479
+ else:
480
+ b = key[-2]
481
+ b1 = b + 1
482
+ d[b1] += 1
483
+ need = d[b]*b + d[a]*a - b1
484
+ d[a] = d[b] = 0
485
+ d[1] = need
486
+ return IntegerPartition(self.integer, d)
487
+
488
+ def as_dict(self):
489
+ """Return the partition as a dictionary whose keys are the
490
+ partition integers and the values are the multiplicity of that
491
+ integer.
492
+
493
+ Examples
494
+ ========
495
+
496
+ >>> from sympy.combinatorics.partitions import IntegerPartition
497
+ >>> IntegerPartition([1]*3 + [2] + [3]*4).as_dict()
498
+ {1: 3, 2: 1, 3: 4}
499
+ """
500
+ if self._dict is None:
501
+ groups = group(self.partition, multiple=False)
502
+ self._keys = [g[0] for g in groups]
503
+ self._dict = dict(groups)
504
+ return self._dict
505
+
506
+ @property
507
+ def conjugate(self):
508
+ """
509
+ Computes the conjugate partition of itself.
510
+
511
+ Examples
512
+ ========
513
+
514
+ >>> from sympy.combinatorics.partitions import IntegerPartition
515
+ >>> a = IntegerPartition([6, 3, 3, 2, 1])
516
+ >>> a.conjugate
517
+ [5, 4, 3, 1, 1, 1]
518
+ """
519
+ j = 1
520
+ temp_arr = list(self.partition) + [0]
521
+ k = temp_arr[0]
522
+ b = [0]*k
523
+ while k > 0:
524
+ while k > temp_arr[j]:
525
+ b[k - 1] = j
526
+ k -= 1
527
+ j += 1
528
+ return b
529
+
530
+ def __lt__(self, other):
531
+ """Return True if self is less than other when the partition
532
+ is listed from smallest to biggest.
533
+
534
+ Examples
535
+ ========
536
+
537
+ >>> from sympy.combinatorics.partitions import IntegerPartition
538
+ >>> a = IntegerPartition([3, 1])
539
+ >>> a < a
540
+ False
541
+ >>> b = a.next_lex()
542
+ >>> a < b
543
+ True
544
+ >>> a == b
545
+ False
546
+ """
547
+ return list(reversed(self.partition)) < list(reversed(other.partition))
548
+
549
+ def __le__(self, other):
550
+ """Return True if self is less than other when the partition
551
+ is listed from smallest to biggest.
552
+
553
+ Examples
554
+ ========
555
+
556
+ >>> from sympy.combinatorics.partitions import IntegerPartition
557
+ >>> a = IntegerPartition([4])
558
+ >>> a <= a
559
+ True
560
+ """
561
+ return list(reversed(self.partition)) <= list(reversed(other.partition))
562
+
563
+ def as_ferrers(self, char='#'):
564
+ """
565
+ Prints the ferrer diagram of a partition.
566
+
567
+ Examples
568
+ ========
569
+
570
+ >>> from sympy.combinatorics.partitions import IntegerPartition
571
+ >>> print(IntegerPartition([1, 1, 5]).as_ferrers())
572
+ #####
573
+ #
574
+ #
575
+ """
576
+ return "\n".join([char*i for i in self.partition])
577
+
578
+ def __str__(self):
579
+ return str(list(self.partition))
580
+
581
+
582
+ def random_integer_partition(n, seed=None):
583
+ """
584
+ Generates a random integer partition summing to ``n`` as a list
585
+ of reverse-sorted integers.
586
+
587
+ Examples
588
+ ========
589
+
590
+ >>> from sympy.combinatorics.partitions import random_integer_partition
591
+
592
+ For the following, a seed is given so a known value can be shown; in
593
+ practice, the seed would not be given.
594
+
595
+ >>> random_integer_partition(100, seed=[1, 1, 12, 1, 2, 1, 85, 1])
596
+ [85, 12, 2, 1]
597
+ >>> random_integer_partition(10, seed=[1, 2, 3, 1, 5, 1])
598
+ [5, 3, 1, 1]
599
+ >>> random_integer_partition(1)
600
+ [1]
601
+ """
602
+ from sympy.core.random import _randint
603
+
604
+ n = as_int(n)
605
+ if n < 1:
606
+ raise ValueError('n must be a positive integer')
607
+
608
+ randint = _randint(seed)
609
+
610
+ partition = []
611
+ while (n > 0):
612
+ k = randint(1, n)
613
+ mult = randint(1, n//k)
614
+ partition.append((k, mult))
615
+ n -= k*mult
616
+ partition.sort(reverse=True)
617
+ partition = flatten([[k]*m for k, m in partition])
618
+ return partition
619
+
620
+
621
+ def RGS_generalized(m):
622
+ """
623
+ Computes the m + 1 generalized unrestricted growth strings
624
+ and returns them as rows in matrix.
625
+
626
+ Examples
627
+ ========
628
+
629
+ >>> from sympy.combinatorics.partitions import RGS_generalized
630
+ >>> RGS_generalized(6)
631
+ Matrix([
632
+ [ 1, 1, 1, 1, 1, 1, 1],
633
+ [ 1, 2, 3, 4, 5, 6, 0],
634
+ [ 2, 5, 10, 17, 26, 0, 0],
635
+ [ 5, 15, 37, 77, 0, 0, 0],
636
+ [ 15, 52, 151, 0, 0, 0, 0],
637
+ [ 52, 203, 0, 0, 0, 0, 0],
638
+ [203, 0, 0, 0, 0, 0, 0]])
639
+ """
640
+ d = zeros(m + 1)
641
+ for i in range(m + 1):
642
+ d[0, i] = 1
643
+
644
+ for i in range(1, m + 1):
645
+ for j in range(m):
646
+ if j <= m - i:
647
+ d[i, j] = j * d[i - 1, j] + d[i - 1, j + 1]
648
+ else:
649
+ d[i, j] = 0
650
+ return d
651
+
652
+
653
+ def RGS_enum(m):
654
+ """
655
+ RGS_enum computes the total number of restricted growth strings
656
+ possible for a superset of size m.
657
+
658
+ Examples
659
+ ========
660
+
661
+ >>> from sympy.combinatorics.partitions import RGS_enum
662
+ >>> from sympy.combinatorics import Partition
663
+ >>> RGS_enum(4)
664
+ 15
665
+ >>> RGS_enum(5)
666
+ 52
667
+ >>> RGS_enum(6)
668
+ 203
669
+
670
+ We can check that the enumeration is correct by actually generating
671
+ the partitions. Here, the 15 partitions of 4 items are generated:
672
+
673
+ >>> a = Partition(list(range(4)))
674
+ >>> s = set()
675
+ >>> for i in range(20):
676
+ ... s.add(a)
677
+ ... a += 1
678
+ ...
679
+ >>> assert len(s) == 15
680
+
681
+ """
682
+ if (m < 1):
683
+ return 0
684
+ elif (m == 1):
685
+ return 1
686
+ else:
687
+ return bell(m)
688
+
689
+
690
+ def RGS_unrank(rank, m):
691
+ """
692
+ Gives the unranked restricted growth string for a given
693
+ superset size.
694
+
695
+ Examples
696
+ ========
697
+
698
+ >>> from sympy.combinatorics.partitions import RGS_unrank
699
+ >>> RGS_unrank(14, 4)
700
+ [0, 1, 2, 3]
701
+ >>> RGS_unrank(0, 4)
702
+ [0, 0, 0, 0]
703
+ """
704
+ if m < 1:
705
+ raise ValueError("The superset size must be >= 1")
706
+ if rank < 0 or RGS_enum(m) <= rank:
707
+ raise ValueError("Invalid arguments")
708
+
709
+ L = [1] * (m + 1)
710
+ j = 1
711
+ D = RGS_generalized(m)
712
+ for i in range(2, m + 1):
713
+ v = D[m - i, j]
714
+ cr = j*v
715
+ if cr <= rank:
716
+ L[i] = j + 1
717
+ rank -= cr
718
+ j += 1
719
+ else:
720
+ L[i] = int(rank / v + 1)
721
+ rank %= v
722
+ return [x - 1 for x in L[1:]]
723
+
724
+
725
+ def RGS_rank(rgs):
726
+ """
727
+ Computes the rank of a restricted growth string.
728
+
729
+ Examples
730
+ ========
731
+
732
+ >>> from sympy.combinatorics.partitions import RGS_rank, RGS_unrank
733
+ >>> RGS_rank([0, 1, 2, 1, 3])
734
+ 42
735
+ >>> RGS_rank(RGS_unrank(4, 7))
736
+ 4
737
+ """
738
+ rgs_size = len(rgs)
739
+ rank = 0
740
+ D = RGS_generalized(rgs_size)
741
+ for i in range(1, rgs_size):
742
+ n = len(rgs[(i + 1):])
743
+ m = max(rgs[0:i])
744
+ rank += D[n, m + 1] * rgs[i]
745
+ return rank
lib/python3.12/site-packages/sympy/combinatorics/rewritingsystem.py ADDED
@@ -0,0 +1,453 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections import deque
2
+ from sympy.combinatorics.rewritingsystem_fsm import StateMachine
3
+
4
+ class RewritingSystem:
5
+ '''
6
+ A class implementing rewriting systems for `FpGroup`s.
7
+
8
+ References
9
+ ==========
10
+ .. [1] Epstein, D., Holt, D. and Rees, S. (1991).
11
+ The use of Knuth-Bendix methods to solve the word problem in automatic groups.
12
+ Journal of Symbolic Computation, 12(4-5), pp.397-414.
13
+
14
+ .. [2] GAP's Manual on its KBMAG package
15
+ https://www.gap-system.org/Manuals/pkg/kbmag-1.5.3/doc/manual.pdf
16
+
17
+ '''
18
+ def __init__(self, group):
19
+ self.group = group
20
+ self.alphabet = group.generators
21
+ self._is_confluent = None
22
+
23
+ # these values are taken from [2]
24
+ self.maxeqns = 32767 # max rules
25
+ self.tidyint = 100 # rules before tidying
26
+
27
+ # _max_exceeded is True if maxeqns is exceeded
28
+ # at any point
29
+ self._max_exceeded = False
30
+
31
+ # Reduction automaton
32
+ self.reduction_automaton = None
33
+ self._new_rules = {}
34
+
35
+ # dictionary of reductions
36
+ self.rules = {}
37
+ self.rules_cache = deque([], 50)
38
+ self._init_rules()
39
+
40
+
41
+ # All the transition symbols in the automaton
42
+ generators = list(self.alphabet)
43
+ generators += [gen**-1 for gen in generators]
44
+ # Create a finite state machine as an instance of the StateMachine object
45
+ self.reduction_automaton = StateMachine('Reduction automaton for '+ repr(self.group), generators)
46
+ self.construct_automaton()
47
+
48
+ def set_max(self, n):
49
+ '''
50
+ Set the maximum number of rules that can be defined
51
+
52
+ '''
53
+ if n > self.maxeqns:
54
+ self._max_exceeded = False
55
+ self.maxeqns = n
56
+ return
57
+
58
+ @property
59
+ def is_confluent(self):
60
+ '''
61
+ Return `True` if the system is confluent
62
+
63
+ '''
64
+ if self._is_confluent is None:
65
+ self._is_confluent = self._check_confluence()
66
+ return self._is_confluent
67
+
68
+ def _init_rules(self):
69
+ identity = self.group.free_group.identity
70
+ for r in self.group.relators:
71
+ self.add_rule(r, identity)
72
+ self._remove_redundancies()
73
+ return
74
+
75
+ def _add_rule(self, r1, r2):
76
+ '''
77
+ Add the rule r1 -> r2 with no checking or further
78
+ deductions
79
+
80
+ '''
81
+ if len(self.rules) + 1 > self.maxeqns:
82
+ self._is_confluent = self._check_confluence()
83
+ self._max_exceeded = True
84
+ raise RuntimeError("Too many rules were defined.")
85
+ self.rules[r1] = r2
86
+ # Add the newly added rule to the `new_rules` dictionary.
87
+ if self.reduction_automaton:
88
+ self._new_rules[r1] = r2
89
+
90
+ def add_rule(self, w1, w2, check=False):
91
+ new_keys = set()
92
+
93
+ if w1 == w2:
94
+ return new_keys
95
+
96
+ if w1 < w2:
97
+ w1, w2 = w2, w1
98
+
99
+ if (w1, w2) in self.rules_cache:
100
+ return new_keys
101
+ self.rules_cache.append((w1, w2))
102
+
103
+ s1, s2 = w1, w2
104
+
105
+ # The following is the equivalent of checking
106
+ # s1 for overlaps with the implicit reductions
107
+ # {g*g**-1 -> <identity>} and {g**-1*g -> <identity>}
108
+ # for any generator g without installing the
109
+ # redundant rules that would result from processing
110
+ # the overlaps. See [1], Section 3 for details.
111
+
112
+ if len(s1) - len(s2) < 3:
113
+ if s1 not in self.rules:
114
+ new_keys.add(s1)
115
+ if not check:
116
+ self._add_rule(s1, s2)
117
+ if s2**-1 > s1**-1 and s2**-1 not in self.rules:
118
+ new_keys.add(s2**-1)
119
+ if not check:
120
+ self._add_rule(s2**-1, s1**-1)
121
+
122
+ # overlaps on the right
123
+ while len(s1) - len(s2) > -1:
124
+ g = s1[len(s1)-1]
125
+ s1 = s1.subword(0, len(s1)-1)
126
+ s2 = s2*g**-1
127
+ if len(s1) - len(s2) < 0:
128
+ if s2 not in self.rules:
129
+ if not check:
130
+ self._add_rule(s2, s1)
131
+ new_keys.add(s2)
132
+ elif len(s1) - len(s2) < 3:
133
+ new = self.add_rule(s1, s2, check)
134
+ new_keys.update(new)
135
+
136
+ # overlaps on the left
137
+ while len(w1) - len(w2) > -1:
138
+ g = w1[0]
139
+ w1 = w1.subword(1, len(w1))
140
+ w2 = g**-1*w2
141
+ if len(w1) - len(w2) < 0:
142
+ if w2 not in self.rules:
143
+ if not check:
144
+ self._add_rule(w2, w1)
145
+ new_keys.add(w2)
146
+ elif len(w1) - len(w2) < 3:
147
+ new = self.add_rule(w1, w2, check)
148
+ new_keys.update(new)
149
+
150
+ return new_keys
151
+
152
+ def _remove_redundancies(self, changes=False):
153
+ '''
154
+ Reduce left- and right-hand sides of reduction rules
155
+ and remove redundant equations (i.e. those for which
156
+ lhs == rhs). If `changes` is `True`, return a set
157
+ containing the removed keys and a set containing the
158
+ added keys
159
+
160
+ '''
161
+ removed = set()
162
+ added = set()
163
+ rules = self.rules.copy()
164
+ for r in rules:
165
+ v = self.reduce(r, exclude=r)
166
+ w = self.reduce(rules[r])
167
+ if v != r:
168
+ del self.rules[r]
169
+ removed.add(r)
170
+ if v > w:
171
+ added.add(v)
172
+ self.rules[v] = w
173
+ elif v < w:
174
+ added.add(w)
175
+ self.rules[w] = v
176
+ else:
177
+ self.rules[v] = w
178
+ if changes:
179
+ return removed, added
180
+ return
181
+
182
+ def make_confluent(self, check=False):
183
+ '''
184
+ Try to make the system confluent using the Knuth-Bendix
185
+ completion algorithm
186
+
187
+ '''
188
+ if self._max_exceeded:
189
+ return self._is_confluent
190
+ lhs = list(self.rules.keys())
191
+
192
+ def _overlaps(r1, r2):
193
+ len1 = len(r1)
194
+ len2 = len(r2)
195
+ result = []
196
+ for j in range(1, len1 + len2):
197
+ if (r1.subword(len1 - j, len1 + len2 - j, strict=False)
198
+ == r2.subword(j - len1, j, strict=False)):
199
+ a = r1.subword(0, len1-j, strict=False)
200
+ a = a*r2.subword(0, j-len1, strict=False)
201
+ b = r2.subword(j-len1, j, strict=False)
202
+ c = r2.subword(j, len2, strict=False)
203
+ c = c*r1.subword(len1 + len2 - j, len1, strict=False)
204
+ result.append(a*b*c)
205
+ return result
206
+
207
+ def _process_overlap(w, r1, r2, check):
208
+ s = w.eliminate_word(r1, self.rules[r1])
209
+ s = self.reduce(s)
210
+ t = w.eliminate_word(r2, self.rules[r2])
211
+ t = self.reduce(t)
212
+ if s != t:
213
+ if check:
214
+ # system not confluent
215
+ return [0]
216
+ try:
217
+ new_keys = self.add_rule(t, s, check)
218
+ return new_keys
219
+ except RuntimeError:
220
+ return False
221
+ return
222
+
223
+ added = 0
224
+ i = 0
225
+ while i < len(lhs):
226
+ r1 = lhs[i]
227
+ i += 1
228
+ # j could be i+1 to not
229
+ # check each pair twice but lhs
230
+ # is extended in the loop and the new
231
+ # elements have to be checked with the
232
+ # preceding ones. there is probably a better way
233
+ # to handle this
234
+ j = 0
235
+ while j < len(lhs):
236
+ r2 = lhs[j]
237
+ j += 1
238
+ if r1 == r2:
239
+ continue
240
+ overlaps = _overlaps(r1, r2)
241
+ overlaps.extend(_overlaps(r1**-1, r2))
242
+ if not overlaps:
243
+ continue
244
+ for w in overlaps:
245
+ new_keys = _process_overlap(w, r1, r2, check)
246
+ if new_keys:
247
+ if check:
248
+ return False
249
+ lhs.extend(new_keys)
250
+ added += len(new_keys)
251
+ elif new_keys == False:
252
+ # too many rules were added so the process
253
+ # couldn't complete
254
+ return self._is_confluent
255
+
256
+ if added > self.tidyint and not check:
257
+ # tidy up
258
+ r, a = self._remove_redundancies(changes=True)
259
+ added = 0
260
+ if r:
261
+ # reset i since some elements were removed
262
+ i = min(lhs.index(s) for s in r)
263
+ lhs = [l for l in lhs if l not in r]
264
+ lhs.extend(a)
265
+ if r1 in r:
266
+ # r1 was removed as redundant
267
+ break
268
+
269
+ self._is_confluent = True
270
+ if not check:
271
+ self._remove_redundancies()
272
+ return True
273
+
274
+ def _check_confluence(self):
275
+ return self.make_confluent(check=True)
276
+
277
+ def reduce(self, word, exclude=None):
278
+ '''
279
+ Apply reduction rules to `word` excluding the reduction rule
280
+ for the lhs equal to `exclude`
281
+
282
+ '''
283
+ rules = {r: self.rules[r] for r in self.rules if r != exclude}
284
+ # the following is essentially `eliminate_words()` code from the
285
+ # `FreeGroupElement` class, the only difference being the first
286
+ # "if" statement
287
+ again = True
288
+ new = word
289
+ while again:
290
+ again = False
291
+ for r in rules:
292
+ prev = new
293
+ if rules[r]**-1 > r**-1:
294
+ new = new.eliminate_word(r, rules[r], _all=True, inverse=False)
295
+ else:
296
+ new = new.eliminate_word(r, rules[r], _all=True)
297
+ if new != prev:
298
+ again = True
299
+ return new
300
+
301
+ def _compute_inverse_rules(self, rules):
302
+ '''
303
+ Compute the inverse rules for a given set of rules.
304
+ The inverse rules are used in the automaton for word reduction.
305
+
306
+ Arguments:
307
+ rules (dictionary): Rules for which the inverse rules are to computed.
308
+
309
+ Returns:
310
+ Dictionary of inverse_rules.
311
+
312
+ '''
313
+ inverse_rules = {}
314
+ for r in rules:
315
+ rule_key_inverse = r**-1
316
+ rule_value_inverse = (rules[r])**-1
317
+ if (rule_value_inverse < rule_key_inverse):
318
+ inverse_rules[rule_key_inverse] = rule_value_inverse
319
+ else:
320
+ inverse_rules[rule_value_inverse] = rule_key_inverse
321
+ return inverse_rules
322
+
323
+ def construct_automaton(self):
324
+ '''
325
+ Construct the automaton based on the set of reduction rules of the system.
326
+
327
+ Automata Design:
328
+ The accept states of the automaton are the proper prefixes of the left hand side of the rules.
329
+ The complete left hand side of the rules are the dead states of the automaton.
330
+
331
+ '''
332
+ self._add_to_automaton(self.rules)
333
+
334
+ def _add_to_automaton(self, rules):
335
+ '''
336
+ Add new states and transitions to the automaton.
337
+
338
+ Summary:
339
+ States corresponding to the new rules added to the system are computed and added to the automaton.
340
+ Transitions in the previously added states are also modified if necessary.
341
+
342
+ Arguments:
343
+ rules (dictionary) -- Dictionary of the newly added rules.
344
+
345
+ '''
346
+ # Automaton variables
347
+ automaton_alphabet = []
348
+ proper_prefixes = {}
349
+
350
+ # compute the inverses of all the new rules added
351
+ all_rules = rules
352
+ inverse_rules = self._compute_inverse_rules(all_rules)
353
+ all_rules.update(inverse_rules)
354
+
355
+ # Keep track of the accept_states.
356
+ accept_states = []
357
+
358
+ for rule in all_rules:
359
+ # The symbols present in the new rules are the symbols to be verified at each state.
360
+ # computes the automaton_alphabet, as the transitions solely depend upon the new states.
361
+ automaton_alphabet += rule.letter_form_elm
362
+ # Compute the proper prefixes for every rule.
363
+ proper_prefixes[rule] = []
364
+ letter_word_array = list(rule.letter_form_elm)
365
+ len_letter_word_array = len(letter_word_array)
366
+ for i in range (1, len_letter_word_array):
367
+ letter_word_array[i] = letter_word_array[i-1]*letter_word_array[i]
368
+ # Add accept states.
369
+ elem = letter_word_array[i-1]
370
+ if elem not in self.reduction_automaton.states:
371
+ self.reduction_automaton.add_state(elem, state_type='a')
372
+ accept_states.append(elem)
373
+ proper_prefixes[rule] = letter_word_array
374
+ # Check for overlaps between dead and accept states.
375
+ if rule in accept_states:
376
+ self.reduction_automaton.states[rule].state_type = 'd'
377
+ self.reduction_automaton.states[rule].rh_rule = all_rules[rule]
378
+ accept_states.remove(rule)
379
+ # Add dead states
380
+ if rule not in self.reduction_automaton.states:
381
+ self.reduction_automaton.add_state(rule, state_type='d', rh_rule=all_rules[rule])
382
+
383
+ automaton_alphabet = set(automaton_alphabet)
384
+
385
+ # Add new transitions for every state.
386
+ for state in self.reduction_automaton.states:
387
+ current_state_name = state
388
+ current_state_type = self.reduction_automaton.states[state].state_type
389
+ # Transitions will be modified only when suffixes of the current_state
390
+ # belongs to the proper_prefixes of the new rules.
391
+ # The rest are ignored if they cannot lead to a dead state after a finite number of transisitons.
392
+ if current_state_type == 's':
393
+ for letter in automaton_alphabet:
394
+ if letter in self.reduction_automaton.states:
395
+ self.reduction_automaton.states[state].add_transition(letter, letter)
396
+ else:
397
+ self.reduction_automaton.states[state].add_transition(letter, current_state_name)
398
+ elif current_state_type == 'a':
399
+ # Check if the transition to any new state in possible.
400
+ for letter in automaton_alphabet:
401
+ _next = current_state_name*letter
402
+ while len(_next) and _next not in self.reduction_automaton.states:
403
+ _next = _next.subword(1, len(_next))
404
+ if not len(_next):
405
+ _next = 'start'
406
+ self.reduction_automaton.states[state].add_transition(letter, _next)
407
+
408
+ # Add transitions for new states. All symbols used in the automaton are considered here.
409
+ # Ignore this if `reduction_automaton.automaton_alphabet` = `automaton_alphabet`.
410
+ if len(self.reduction_automaton.automaton_alphabet) != len(automaton_alphabet):
411
+ for state in accept_states:
412
+ current_state_name = state
413
+ for letter in self.reduction_automaton.automaton_alphabet:
414
+ _next = current_state_name*letter
415
+ while len(_next) and _next not in self.reduction_automaton.states:
416
+ _next = _next.subword(1, len(_next))
417
+ if not len(_next):
418
+ _next = 'start'
419
+ self.reduction_automaton.states[state].add_transition(letter, _next)
420
+
421
+ def reduce_using_automaton(self, word):
422
+ '''
423
+ Reduce a word using an automaton.
424
+
425
+ Summary:
426
+ All the symbols of the word are stored in an array and are given as the input to the automaton.
427
+ If the automaton reaches a dead state that subword is replaced and the automaton is run from the beginning.
428
+ The complete word has to be replaced when the word is read and the automaton reaches a dead state.
429
+ So, this process is repeated until the word is read completely and the automaton reaches the accept state.
430
+
431
+ Arguments:
432
+ word (instance of FreeGroupElement) -- Word that needs to be reduced.
433
+
434
+ '''
435
+ # Modify the automaton if new rules are found.
436
+ if self._new_rules:
437
+ self._add_to_automaton(self._new_rules)
438
+ self._new_rules = {}
439
+
440
+ flag = 1
441
+ while flag:
442
+ flag = 0
443
+ current_state = self.reduction_automaton.states['start']
444
+ for i, s in enumerate(word.letter_form_elm):
445
+ next_state_name = current_state.transitions[s]
446
+ next_state = self.reduction_automaton.states[next_state_name]
447
+ if next_state.state_type == 'd':
448
+ subst = next_state.rh_rule
449
+ word = word.substituted_word(i - len(next_state_name) + 1, i+1, subst)
450
+ flag = 1
451
+ break
452
+ current_state = next_state
453
+ return word
lib/python3.12/site-packages/sympy/combinatorics/tests/__init__.py ADDED
File without changes
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (204 Bytes). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_coset_table.cpython-312.pyc ADDED
Binary file (38.3 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_fp_groups.cpython-312.pyc ADDED
Binary file (15 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_free_groups.cpython-312.pyc ADDED
Binary file (17.5 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_galois.cpython-312.pyc ADDED
Binary file (4.04 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_generators.cpython-312.pyc ADDED
Binary file (5.82 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_graycode.cpython-312.pyc ADDED
Binary file (5.56 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_group_constructs.cpython-312.pyc ADDED
Binary file (1.09 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_group_numbers.cpython-312.pyc ADDED
Binary file (6.67 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_homomorphisms.cpython-312.pyc ADDED
Binary file (6.74 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_named_groups.cpython-312.pyc ADDED
Binary file (4.2 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_partitions.cpython-312.pyc ADDED
Binary file (8.94 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_pc_groups.cpython-312.pyc ADDED
Binary file (4.46 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_perm_groups.cpython-312.pyc ADDED
Binary file (69.4 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_permutations.cpython-312.pyc ADDED
Binary file (41.3 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_polyhedron.cpython-312.pyc ADDED
Binary file (7.08 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_prufer.cpython-312.pyc ADDED
Binary file (6.02 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_rewriting.cpython-312.pyc ADDED
Binary file (4.06 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_schur_number.cpython-312.pyc ADDED
Binary file (3.43 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_subsets.cpython-312.pyc ADDED
Binary file (5.28 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_tensor_can.cpython-312.pyc ADDED
Binary file (24.7 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_testutil.cpython-312.pyc ADDED
Binary file (2.96 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_util.cpython-312.pyc ADDED
Binary file (6.87 kB). View file
 
lib/python3.12/site-packages/sympy/combinatorics/tests/test_coset_table.py ADDED
@@ -0,0 +1,825 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.combinatorics.fp_groups import FpGroup
2
+ from sympy.combinatorics.coset_table import (CosetTable,
3
+ coset_enumeration_r, coset_enumeration_c)
4
+ from sympy.combinatorics.coset_table import modified_coset_enumeration_r
5
+ from sympy.combinatorics.free_groups import free_group
6
+
7
+ from sympy.testing.pytest import slow
8
+
9
+ """
10
+ References
11
+ ==========
12
+
13
+ [1] Holt, D., Eick, B., O'Brien, E.
14
+ "Handbook of Computational Group Theory"
15
+
16
+ [2] John J. Cannon; Lucien A. Dimino; George Havas; Jane M. Watson
17
+ Mathematics of Computation, Vol. 27, No. 123. (Jul., 1973), pp. 463-490.
18
+ "Implementation and Analysis of the Todd-Coxeter Algorithm"
19
+
20
+ """
21
+
22
+ def test_scan_1():
23
+ # Example 5.1 from [1]
24
+ F, x, y = free_group("x, y")
25
+ f = FpGroup(F, [x**3, y**3, x**-1*y**-1*x*y])
26
+ c = CosetTable(f, [x])
27
+
28
+ c.scan_and_fill(0, x)
29
+ assert c.table == [[0, 0, None, None]]
30
+ assert c.p == [0]
31
+ assert c.n == 1
32
+ assert c.omega == [0]
33
+
34
+ c.scan_and_fill(0, x**3)
35
+ assert c.table == [[0, 0, None, None]]
36
+ assert c.p == [0]
37
+ assert c.n == 1
38
+ assert c.omega == [0]
39
+
40
+ c.scan_and_fill(0, y**3)
41
+ assert c.table == [[0, 0, 1, 2], [None, None, 2, 0], [None, None, 0, 1]]
42
+ assert c.p == [0, 1, 2]
43
+ assert c.n == 3
44
+ assert c.omega == [0, 1, 2]
45
+
46
+ c.scan_and_fill(0, x**-1*y**-1*x*y)
47
+ assert c.table == [[0, 0, 1, 2], [None, None, 2, 0], [2, 2, 0, 1]]
48
+ assert c.p == [0, 1, 2]
49
+ assert c.n == 3
50
+ assert c.omega == [0, 1, 2]
51
+
52
+ c.scan_and_fill(1, x**3)
53
+ assert c.table == [[0, 0, 1, 2], [3, 4, 2, 0], [2, 2, 0, 1], \
54
+ [4, 1, None, None], [1, 3, None, None]]
55
+ assert c.p == [0, 1, 2, 3, 4]
56
+ assert c.n == 5
57
+ assert c.omega == [0, 1, 2, 3, 4]
58
+
59
+ c.scan_and_fill(1, y**3)
60
+ assert c.table == [[0, 0, 1, 2], [3, 4, 2, 0], [2, 2, 0, 1], \
61
+ [4, 1, None, None], [1, 3, None, None]]
62
+ assert c.p == [0, 1, 2, 3, 4]
63
+ assert c.n == 5
64
+ assert c.omega == [0, 1, 2, 3, 4]
65
+
66
+ c.scan_and_fill(1, x**-1*y**-1*x*y)
67
+ assert c.table == [[0, 0, 1, 2], [1, 1, 2, 0], [2, 2, 0, 1], \
68
+ [None, 1, None, None], [1, 3, None, None]]
69
+ assert c.p == [0, 1, 2, 1, 1]
70
+ assert c.n == 3
71
+ assert c.omega == [0, 1, 2]
72
+
73
+ # Example 5.2 from [1]
74
+ f = FpGroup(F, [x**2, y**3, (x*y)**3])
75
+ c = CosetTable(f, [x*y])
76
+
77
+ c.scan_and_fill(0, x*y)
78
+ assert c.table == [[1, None, None, 1], [None, 0, 0, None]]
79
+ assert c.p == [0, 1]
80
+ assert c.n == 2
81
+ assert c.omega == [0, 1]
82
+
83
+ c.scan_and_fill(0, x**2)
84
+ assert c.table == [[1, 1, None, 1], [0, 0, 0, None]]
85
+ assert c.p == [0, 1]
86
+ assert c.n == 2
87
+ assert c.omega == [0, 1]
88
+
89
+ c.scan_and_fill(0, y**3)
90
+ assert c.table == [[1, 1, 2, 1], [0, 0, 0, 2], [None, None, 1, 0]]
91
+ assert c.p == [0, 1, 2]
92
+ assert c.n == 3
93
+ assert c.omega == [0, 1, 2]
94
+
95
+ c.scan_and_fill(0, (x*y)**3)
96
+ assert c.table == [[1, 1, 2, 1], [0, 0, 0, 2], [None, None, 1, 0]]
97
+ assert c.p == [0, 1, 2]
98
+ assert c.n == 3
99
+ assert c.omega == [0, 1, 2]
100
+
101
+ c.scan_and_fill(1, x**2)
102
+ assert c.table == [[1, 1, 2, 1], [0, 0, 0, 2], [None, None, 1, 0]]
103
+ assert c.p == [0, 1, 2]
104
+ assert c.n == 3
105
+ assert c.omega == [0, 1, 2]
106
+
107
+ c.scan_and_fill(1, y**3)
108
+ assert c.table == [[1, 1, 2, 1], [0, 0, 0, 2], [None, None, 1, 0]]
109
+ assert c.p == [0, 1, 2]
110
+ assert c.n == 3
111
+ assert c.omega == [0, 1, 2]
112
+
113
+ c.scan_and_fill(1, (x*y)**3)
114
+ assert c.table == [[1, 1, 2, 1], [0, 0, 0, 2], [3, 4, 1, 0], [None, 2, 4, None], [2, None, None, 3]]
115
+ assert c.p == [0, 1, 2, 3, 4]
116
+ assert c.n == 5
117
+ assert c.omega == [0, 1, 2, 3, 4]
118
+
119
+ c.scan_and_fill(2, x**2)
120
+ assert c.table == [[1, 1, 2, 1], [0, 0, 0, 2], [3, 3, 1, 0], [2, 2, 3, 3], [2, None, None, 3]]
121
+ assert c.p == [0, 1, 2, 3, 3]
122
+ assert c.n == 4
123
+ assert c.omega == [0, 1, 2, 3]
124
+
125
+
126
+ @slow
127
+ def test_coset_enumeration():
128
+ # this test function contains the combined tests for the two strategies
129
+ # i.e. HLT and Felsch strategies.
130
+
131
+ # Example 5.1 from [1]
132
+ F, x, y = free_group("x, y")
133
+ f = FpGroup(F, [x**3, y**3, x**-1*y**-1*x*y])
134
+ C_r = coset_enumeration_r(f, [x])
135
+ C_r.compress(); C_r.standardize()
136
+ C_c = coset_enumeration_c(f, [x])
137
+ C_c.compress(); C_c.standardize()
138
+ table1 = [[0, 0, 1, 2], [1, 1, 2, 0], [2, 2, 0, 1]]
139
+ assert C_r.table == table1
140
+ assert C_c.table == table1
141
+
142
+ # E1 from [2] Pg. 474
143
+ F, r, s, t = free_group("r, s, t")
144
+ E1 = FpGroup(F, [t**-1*r*t*r**-2, r**-1*s*r*s**-2, s**-1*t*s*t**-2])
145
+ C_r = coset_enumeration_r(E1, [])
146
+ C_r.compress()
147
+ C_c = coset_enumeration_c(E1, [])
148
+ C_c.compress()
149
+ table2 = [[0, 0, 0, 0, 0, 0]]
150
+ assert C_r.table == table2
151
+ # test for issue #11449
152
+ assert C_c.table == table2
153
+
154
+ # Cox group from [2] Pg. 474
155
+ F, a, b = free_group("a, b")
156
+ Cox = FpGroup(F, [a**6, b**6, (a*b)**2, (a**2*b**2)**2, (a**3*b**3)**5])
157
+ C_r = coset_enumeration_r(Cox, [a])
158
+ C_r.compress(); C_r.standardize()
159
+ C_c = coset_enumeration_c(Cox, [a])
160
+ C_c.compress(); C_c.standardize()
161
+ table3 = [[0, 0, 1, 2],
162
+ [2, 3, 4, 0],
163
+ [5, 1, 0, 6],
164
+ [1, 7, 8, 9],
165
+ [9, 10, 11, 1],
166
+ [12, 2, 9, 13],
167
+ [14, 9, 2, 11],
168
+ [3, 12, 15, 16],
169
+ [16, 17, 18, 3],
170
+ [6, 4, 3, 5],
171
+ [4, 19, 20, 21],
172
+ [21, 22, 6, 4],
173
+ [7, 5, 23, 24],
174
+ [25, 23, 5, 18],
175
+ [19, 6, 22, 26],
176
+ [24, 27, 28, 7],
177
+ [29, 8, 7, 30],
178
+ [8, 31, 32, 33],
179
+ [33, 34, 13, 8],
180
+ [10, 14, 35, 35],
181
+ [35, 36, 37, 10],
182
+ [30, 11, 10, 29],
183
+ [11, 38, 39, 14],
184
+ [13, 39, 38, 12],
185
+ [40, 15, 12, 41],
186
+ [42, 13, 34, 43],
187
+ [44, 35, 14, 45],
188
+ [15, 46, 47, 34],
189
+ [34, 48, 49, 15],
190
+ [50, 16, 21, 51],
191
+ [52, 21, 16, 49],
192
+ [17, 50, 53, 54],
193
+ [54, 55, 56, 17],
194
+ [41, 18, 17, 40],
195
+ [18, 28, 27, 25],
196
+ [26, 20, 19, 19],
197
+ [20, 57, 58, 59],
198
+ [59, 60, 51, 20],
199
+ [22, 52, 61, 23],
200
+ [23, 62, 63, 22],
201
+ [64, 24, 33, 65],
202
+ [48, 33, 24, 61],
203
+ [62, 25, 54, 66],
204
+ [67, 54, 25, 68],
205
+ [57, 26, 59, 69],
206
+ [70, 59, 26, 63],
207
+ [27, 64, 71, 72],
208
+ [72, 73, 68, 27],
209
+ [28, 41, 74, 75],
210
+ [75, 76, 30, 28],
211
+ [31, 29, 77, 78],
212
+ [79, 77, 29, 37],
213
+ [38, 30, 76, 80],
214
+ [78, 81, 82, 31],
215
+ [43, 32, 31, 42],
216
+ [32, 83, 84, 85],
217
+ [85, 86, 65, 32],
218
+ [36, 44, 87, 88],
219
+ [88, 89, 90, 36],
220
+ [45, 37, 36, 44],
221
+ [37, 82, 81, 79],
222
+ [80, 74, 41, 38],
223
+ [39, 42, 91, 92],
224
+ [92, 93, 45, 39],
225
+ [46, 40, 94, 95],
226
+ [96, 94, 40, 56],
227
+ [97, 91, 42, 82],
228
+ [83, 43, 98, 99],
229
+ [100, 98, 43, 47],
230
+ [101, 87, 44, 90],
231
+ [82, 45, 93, 97],
232
+ [95, 102, 103, 46],
233
+ [104, 47, 46, 105],
234
+ [47, 106, 107, 100],
235
+ [61, 108, 109, 48],
236
+ [105, 49, 48, 104],
237
+ [49, 110, 111, 52],
238
+ [51, 111, 110, 50],
239
+ [112, 53, 50, 113],
240
+ [114, 51, 60, 115],
241
+ [116, 61, 52, 117],
242
+ [53, 118, 119, 60],
243
+ [60, 70, 66, 53],
244
+ [55, 67, 120, 121],
245
+ [121, 122, 123, 55],
246
+ [113, 56, 55, 112],
247
+ [56, 103, 102, 96],
248
+ [69, 124, 125, 57],
249
+ [115, 58, 57, 114],
250
+ [58, 126, 127, 128],
251
+ [128, 128, 69, 58],
252
+ [66, 129, 130, 62],
253
+ [117, 63, 62, 116],
254
+ [63, 125, 124, 70],
255
+ [65, 109, 108, 64],
256
+ [131, 71, 64, 132],
257
+ [133, 65, 86, 134],
258
+ [135, 66, 70, 136],
259
+ [68, 130, 129, 67],
260
+ [137, 120, 67, 138],
261
+ [132, 68, 73, 131],
262
+ [139, 69, 128, 140],
263
+ [71, 141, 142, 86],
264
+ [86, 143, 144, 71],
265
+ [145, 72, 75, 146],
266
+ [147, 75, 72, 144],
267
+ [73, 145, 148, 120],
268
+ [120, 149, 150, 73],
269
+ [74, 151, 152, 94],
270
+ [94, 153, 146, 74],
271
+ [76, 147, 154, 77],
272
+ [77, 155, 156, 76],
273
+ [157, 78, 85, 158],
274
+ [143, 85, 78, 154],
275
+ [155, 79, 88, 159],
276
+ [160, 88, 79, 161],
277
+ [151, 80, 92, 162],
278
+ [163, 92, 80, 156],
279
+ [81, 157, 164, 165],
280
+ [165, 166, 161, 81],
281
+ [99, 107, 106, 83],
282
+ [134, 84, 83, 133],
283
+ [84, 167, 168, 169],
284
+ [169, 170, 158, 84],
285
+ [87, 171, 172, 93],
286
+ [93, 163, 159, 87],
287
+ [89, 160, 173, 174],
288
+ [174, 175, 176, 89],
289
+ [90, 90, 89, 101],
290
+ [91, 177, 178, 98],
291
+ [98, 179, 162, 91],
292
+ [180, 95, 100, 181],
293
+ [179, 100, 95, 152],
294
+ [153, 96, 121, 148],
295
+ [182, 121, 96, 183],
296
+ [177, 97, 165, 184],
297
+ [185, 165, 97, 172],
298
+ [186, 99, 169, 187],
299
+ [188, 169, 99, 178],
300
+ [171, 101, 174, 189],
301
+ [190, 174, 101, 176],
302
+ [102, 180, 191, 192],
303
+ [192, 193, 183, 102],
304
+ [103, 113, 194, 195],
305
+ [195, 196, 105, 103],
306
+ [106, 104, 197, 198],
307
+ [199, 197, 104, 109],
308
+ [110, 105, 196, 200],
309
+ [198, 201, 133, 106],
310
+ [107, 186, 202, 203],
311
+ [203, 204, 181, 107],
312
+ [108, 116, 205, 206],
313
+ [206, 207, 132, 108],
314
+ [109, 133, 201, 199],
315
+ [200, 194, 113, 110],
316
+ [111, 114, 208, 209],
317
+ [209, 210, 117, 111],
318
+ [118, 112, 211, 212],
319
+ [213, 211, 112, 123],
320
+ [214, 208, 114, 125],
321
+ [126, 115, 215, 216],
322
+ [217, 215, 115, 119],
323
+ [218, 205, 116, 130],
324
+ [125, 117, 210, 214],
325
+ [212, 219, 220, 118],
326
+ [136, 119, 118, 135],
327
+ [119, 221, 222, 217],
328
+ [122, 182, 223, 224],
329
+ [224, 225, 226, 122],
330
+ [138, 123, 122, 137],
331
+ [123, 220, 219, 213],
332
+ [124, 139, 227, 228],
333
+ [228, 229, 136, 124],
334
+ [216, 222, 221, 126],
335
+ [140, 127, 126, 139],
336
+ [127, 230, 231, 232],
337
+ [232, 233, 140, 127],
338
+ [129, 135, 234, 235],
339
+ [235, 236, 138, 129],
340
+ [130, 132, 207, 218],
341
+ [141, 131, 237, 238],
342
+ [239, 237, 131, 150],
343
+ [167, 134, 240, 241],
344
+ [242, 240, 134, 142],
345
+ [243, 234, 135, 220],
346
+ [221, 136, 229, 244],
347
+ [149, 137, 245, 246],
348
+ [247, 245, 137, 226],
349
+ [220, 138, 236, 243],
350
+ [244, 227, 139, 221],
351
+ [230, 140, 233, 248],
352
+ [238, 249, 250, 141],
353
+ [251, 142, 141, 252],
354
+ [142, 253, 254, 242],
355
+ [154, 255, 256, 143],
356
+ [252, 144, 143, 251],
357
+ [144, 257, 258, 147],
358
+ [146, 258, 257, 145],
359
+ [259, 148, 145, 260],
360
+ [261, 146, 153, 262],
361
+ [263, 154, 147, 264],
362
+ [148, 265, 266, 153],
363
+ [246, 267, 268, 149],
364
+ [260, 150, 149, 259],
365
+ [150, 250, 249, 239],
366
+ [162, 269, 270, 151],
367
+ [262, 152, 151, 261],
368
+ [152, 271, 272, 179],
369
+ [159, 273, 274, 155],
370
+ [264, 156, 155, 263],
371
+ [156, 270, 269, 163],
372
+ [158, 256, 255, 157],
373
+ [275, 164, 157, 276],
374
+ [277, 158, 170, 278],
375
+ [279, 159, 163, 280],
376
+ [161, 274, 273, 160],
377
+ [281, 173, 160, 282],
378
+ [276, 161, 166, 275],
379
+ [283, 162, 179, 284],
380
+ [164, 285, 286, 170],
381
+ [170, 188, 184, 164],
382
+ [166, 185, 189, 173],
383
+ [173, 287, 288, 166],
384
+ [241, 254, 253, 167],
385
+ [278, 168, 167, 277],
386
+ [168, 289, 290, 291],
387
+ [291, 292, 187, 168],
388
+ [189, 293, 294, 171],
389
+ [280, 172, 171, 279],
390
+ [172, 295, 296, 185],
391
+ [175, 190, 297, 297],
392
+ [297, 298, 299, 175],
393
+ [282, 176, 175, 281],
394
+ [176, 294, 293, 190],
395
+ [184, 296, 295, 177],
396
+ [284, 178, 177, 283],
397
+ [178, 300, 301, 188],
398
+ [181, 272, 271, 180],
399
+ [302, 191, 180, 303],
400
+ [304, 181, 204, 305],
401
+ [183, 266, 265, 182],
402
+ [306, 223, 182, 307],
403
+ [303, 183, 193, 302],
404
+ [308, 184, 188, 309],
405
+ [310, 189, 185, 311],
406
+ [187, 301, 300, 186],
407
+ [305, 202, 186, 304],
408
+ [312, 187, 292, 313],
409
+ [314, 297, 190, 315],
410
+ [191, 316, 317, 204],
411
+ [204, 318, 319, 191],
412
+ [320, 192, 195, 321],
413
+ [322, 195, 192, 319],
414
+ [193, 320, 323, 223],
415
+ [223, 324, 325, 193],
416
+ [194, 326, 327, 211],
417
+ [211, 328, 321, 194],
418
+ [196, 322, 329, 197],
419
+ [197, 330, 331, 196],
420
+ [332, 198, 203, 333],
421
+ [318, 203, 198, 329],
422
+ [330, 199, 206, 334],
423
+ [335, 206, 199, 336],
424
+ [326, 200, 209, 337],
425
+ [338, 209, 200, 331],
426
+ [201, 332, 339, 240],
427
+ [240, 340, 336, 201],
428
+ [202, 341, 342, 292],
429
+ [292, 343, 333, 202],
430
+ [205, 344, 345, 210],
431
+ [210, 338, 334, 205],
432
+ [207, 335, 346, 237],
433
+ [237, 347, 348, 207],
434
+ [208, 349, 350, 215],
435
+ [215, 351, 337, 208],
436
+ [352, 212, 217, 353],
437
+ [351, 217, 212, 327],
438
+ [328, 213, 224, 323],
439
+ [354, 224, 213, 355],
440
+ [349, 214, 228, 356],
441
+ [357, 228, 214, 345],
442
+ [358, 216, 232, 359],
443
+ [360, 232, 216, 350],
444
+ [344, 218, 235, 361],
445
+ [362, 235, 218, 348],
446
+ [219, 352, 363, 364],
447
+ [364, 365, 355, 219],
448
+ [222, 358, 366, 367],
449
+ [367, 368, 353, 222],
450
+ [225, 354, 369, 370],
451
+ [370, 371, 372, 225],
452
+ [307, 226, 225, 306],
453
+ [226, 268, 267, 247],
454
+ [227, 373, 374, 233],
455
+ [233, 360, 356, 227],
456
+ [229, 357, 361, 234],
457
+ [234, 375, 376, 229],
458
+ [248, 231, 230, 230],
459
+ [231, 377, 378, 379],
460
+ [379, 380, 359, 231],
461
+ [236, 362, 381, 245],
462
+ [245, 382, 383, 236],
463
+ [384, 238, 242, 385],
464
+ [340, 242, 238, 346],
465
+ [347, 239, 246, 381],
466
+ [386, 246, 239, 387],
467
+ [388, 241, 291, 389],
468
+ [343, 291, 241, 339],
469
+ [375, 243, 364, 390],
470
+ [391, 364, 243, 383],
471
+ [373, 244, 367, 392],
472
+ [393, 367, 244, 376],
473
+ [382, 247, 370, 394],
474
+ [395, 370, 247, 396],
475
+ [377, 248, 379, 397],
476
+ [398, 379, 248, 374],
477
+ [249, 384, 399, 400],
478
+ [400, 401, 387, 249],
479
+ [250, 260, 402, 403],
480
+ [403, 404, 252, 250],
481
+ [253, 251, 405, 406],
482
+ [407, 405, 251, 256],
483
+ [257, 252, 404, 408],
484
+ [406, 409, 277, 253],
485
+ [254, 388, 410, 411],
486
+ [411, 412, 385, 254],
487
+ [255, 263, 413, 414],
488
+ [414, 415, 276, 255],
489
+ [256, 277, 409, 407],
490
+ [408, 402, 260, 257],
491
+ [258, 261, 416, 417],
492
+ [417, 418, 264, 258],
493
+ [265, 259, 419, 420],
494
+ [421, 419, 259, 268],
495
+ [422, 416, 261, 270],
496
+ [271, 262, 423, 424],
497
+ [425, 423, 262, 266],
498
+ [426, 413, 263, 274],
499
+ [270, 264, 418, 422],
500
+ [420, 427, 307, 265],
501
+ [266, 303, 428, 425],
502
+ [267, 386, 429, 430],
503
+ [430, 431, 396, 267],
504
+ [268, 307, 427, 421],
505
+ [269, 283, 432, 433],
506
+ [433, 434, 280, 269],
507
+ [424, 428, 303, 271],
508
+ [272, 304, 435, 436],
509
+ [436, 437, 284, 272],
510
+ [273, 279, 438, 439],
511
+ [439, 440, 282, 273],
512
+ [274, 276, 415, 426],
513
+ [285, 275, 441, 442],
514
+ [443, 441, 275, 288],
515
+ [289, 278, 444, 445],
516
+ [446, 444, 278, 286],
517
+ [447, 438, 279, 294],
518
+ [295, 280, 434, 448],
519
+ [287, 281, 449, 450],
520
+ [451, 449, 281, 299],
521
+ [294, 282, 440, 447],
522
+ [448, 432, 283, 295],
523
+ [300, 284, 437, 452],
524
+ [442, 453, 454, 285],
525
+ [309, 286, 285, 308],
526
+ [286, 455, 456, 446],
527
+ [450, 457, 458, 287],
528
+ [311, 288, 287, 310],
529
+ [288, 454, 453, 443],
530
+ [445, 456, 455, 289],
531
+ [313, 290, 289, 312],
532
+ [290, 459, 460, 461],
533
+ [461, 462, 389, 290],
534
+ [293, 310, 463, 464],
535
+ [464, 465, 315, 293],
536
+ [296, 308, 466, 467],
537
+ [467, 468, 311, 296],
538
+ [298, 314, 469, 470],
539
+ [470, 471, 472, 298],
540
+ [315, 299, 298, 314],
541
+ [299, 458, 457, 451],
542
+ [452, 435, 304, 300],
543
+ [301, 312, 473, 474],
544
+ [474, 475, 309, 301],
545
+ [316, 302, 476, 477],
546
+ [478, 476, 302, 325],
547
+ [341, 305, 479, 480],
548
+ [481, 479, 305, 317],
549
+ [324, 306, 482, 483],
550
+ [484, 482, 306, 372],
551
+ [485, 466, 308, 454],
552
+ [455, 309, 475, 486],
553
+ [487, 463, 310, 458],
554
+ [454, 311, 468, 485],
555
+ [486, 473, 312, 455],
556
+ [459, 313, 488, 489],
557
+ [490, 488, 313, 342],
558
+ [491, 469, 314, 472],
559
+ [458, 315, 465, 487],
560
+ [477, 492, 485, 316],
561
+ [463, 317, 316, 468],
562
+ [317, 487, 493, 481],
563
+ [329, 447, 464, 318],
564
+ [468, 319, 318, 463],
565
+ [319, 467, 448, 322],
566
+ [321, 448, 467, 320],
567
+ [475, 323, 320, 466],
568
+ [432, 321, 328, 437],
569
+ [438, 329, 322, 434],
570
+ [323, 474, 452, 328],
571
+ [483, 494, 486, 324],
572
+ [466, 325, 324, 475],
573
+ [325, 485, 492, 478],
574
+ [337, 422, 433, 326],
575
+ [437, 327, 326, 432],
576
+ [327, 436, 424, 351],
577
+ [334, 426, 439, 330],
578
+ [434, 331, 330, 438],
579
+ [331, 433, 422, 338],
580
+ [333, 464, 447, 332],
581
+ [449, 339, 332, 440],
582
+ [465, 333, 343, 469],
583
+ [413, 334, 338, 418],
584
+ [336, 439, 426, 335],
585
+ [441, 346, 335, 415],
586
+ [440, 336, 340, 449],
587
+ [416, 337, 351, 423],
588
+ [339, 451, 470, 343],
589
+ [346, 443, 450, 340],
590
+ [480, 493, 487, 341],
591
+ [469, 342, 341, 465],
592
+ [342, 491, 495, 490],
593
+ [361, 407, 414, 344],
594
+ [418, 345, 344, 413],
595
+ [345, 417, 408, 357],
596
+ [381, 446, 442, 347],
597
+ [415, 348, 347, 441],
598
+ [348, 414, 407, 362],
599
+ [356, 408, 417, 349],
600
+ [423, 350, 349, 416],
601
+ [350, 425, 420, 360],
602
+ [353, 424, 436, 352],
603
+ [479, 363, 352, 435],
604
+ [428, 353, 368, 476],
605
+ [355, 452, 474, 354],
606
+ [488, 369, 354, 473],
607
+ [435, 355, 365, 479],
608
+ [402, 356, 360, 419],
609
+ [405, 361, 357, 404],
610
+ [359, 420, 425, 358],
611
+ [476, 366, 358, 428],
612
+ [427, 359, 380, 482],
613
+ [444, 381, 362, 409],
614
+ [363, 481, 477, 368],
615
+ [368, 393, 390, 363],
616
+ [365, 391, 394, 369],
617
+ [369, 490, 480, 365],
618
+ [366, 478, 483, 380],
619
+ [380, 398, 392, 366],
620
+ [371, 395, 496, 497],
621
+ [497, 498, 489, 371],
622
+ [473, 372, 371, 488],
623
+ [372, 486, 494, 484],
624
+ [392, 400, 403, 373],
625
+ [419, 374, 373, 402],
626
+ [374, 421, 430, 398],
627
+ [390, 411, 406, 375],
628
+ [404, 376, 375, 405],
629
+ [376, 403, 400, 393],
630
+ [397, 430, 421, 377],
631
+ [482, 378, 377, 427],
632
+ [378, 484, 497, 499],
633
+ [499, 499, 397, 378],
634
+ [394, 461, 445, 382],
635
+ [409, 383, 382, 444],
636
+ [383, 406, 411, 391],
637
+ [385, 450, 443, 384],
638
+ [492, 399, 384, 453],
639
+ [457, 385, 412, 493],
640
+ [387, 442, 446, 386],
641
+ [494, 429, 386, 456],
642
+ [453, 387, 401, 492],
643
+ [389, 470, 451, 388],
644
+ [493, 410, 388, 457],
645
+ [471, 389, 462, 495],
646
+ [412, 390, 393, 399],
647
+ [462, 394, 391, 410],
648
+ [401, 392, 398, 429],
649
+ [396, 445, 461, 395],
650
+ [498, 496, 395, 460],
651
+ [456, 396, 431, 494],
652
+ [431, 397, 499, 496],
653
+ [399, 477, 481, 412],
654
+ [429, 483, 478, 401],
655
+ [410, 480, 490, 462],
656
+ [496, 497, 484, 431],
657
+ [489, 495, 491, 459],
658
+ [495, 460, 459, 471],
659
+ [460, 489, 498, 498],
660
+ [472, 472, 471, 491]]
661
+
662
+ assert C_r.table == table3
663
+ assert C_c.table == table3
664
+
665
+ # Group denoted by B2,4 from [2] Pg. 474
666
+ F, a, b = free_group("a, b")
667
+ B_2_4 = FpGroup(F, [a**4, b**4, (a*b)**4, (a**-1*b)**4, (a**2*b)**4, \
668
+ (a*b**2)**4, (a**2*b**2)**4, (a**-1*b*a*b)**4, (a*b**-1*a*b)**4])
669
+ C_r = coset_enumeration_r(B_2_4, [a])
670
+ C_c = coset_enumeration_c(B_2_4, [a])
671
+ index_r = 0
672
+ for i in range(len(C_r.p)):
673
+ if C_r.p[i] == i:
674
+ index_r += 1
675
+ assert index_r == 1024
676
+
677
+ index_c = 0
678
+ for i in range(len(C_c.p)):
679
+ if C_c.p[i] == i:
680
+ index_c += 1
681
+ assert index_c == 1024
682
+
683
+ # trivial Macdonald group G(2,2) from [2] Pg. 480
684
+ M = FpGroup(F, [b**-1*a**-1*b*a*b**-1*a*b*a**-2, a**-1*b**-1*a*b*a**-1*b*a*b**-2])
685
+ C_r = coset_enumeration_r(M, [a])
686
+ C_r.compress(); C_r.standardize()
687
+ C_c = coset_enumeration_c(M, [a])
688
+ C_c.compress(); C_c.standardize()
689
+ table4 = [[0, 0, 0, 0]]
690
+ assert C_r.table == table4
691
+ assert C_c.table == table4
692
+
693
+
694
+ def test_look_ahead():
695
+ # Section 3.2 [Test Example] Example (d) from [2]
696
+ F, a, b, c = free_group("a, b, c")
697
+ f = FpGroup(F, [a**11, b**5, c**4, (a*c)**3, b**2*c**-1*b**-1*c, a**4*b**-1*a**-1*b])
698
+ H = [c, b, c**2]
699
+ table0 = [[1, 2, 0, 0, 0, 0],
700
+ [3, 0, 4, 5, 6, 7],
701
+ [0, 8, 9, 10, 11, 12],
702
+ [5, 1, 10, 13, 14, 15],
703
+ [16, 5, 16, 1, 17, 18],
704
+ [4, 3, 1, 8, 19, 20],
705
+ [12, 21, 22, 23, 24, 1],
706
+ [25, 26, 27, 28, 1, 24],
707
+ [2, 10, 5, 16, 22, 28],
708
+ [10, 13, 13, 2, 29, 30]]
709
+ CosetTable.max_stack_size = 10
710
+ C_c = coset_enumeration_c(f, H)
711
+ C_c.compress(); C_c.standardize()
712
+ assert C_c.table[: 10] == table0
713
+
714
+ def test_modified_methods():
715
+ '''
716
+ Tests for modified coset table methods.
717
+ Example 5.7 from [1] Holt, D., Eick, B., O'Brien
718
+ "Handbook of Computational Group Theory".
719
+
720
+ '''
721
+ F, x, y = free_group("x, y")
722
+ f = FpGroup(F, [x**3, y**5, (x*y)**2])
723
+ H = [x*y, x**-1*y**-1*x*y*x]
724
+ C = CosetTable(f, H)
725
+ C.modified_define(0, x)
726
+ identity = C._grp.identity
727
+ a_0 = C._grp.generators[0]
728
+ a_1 = C._grp.generators[1]
729
+
730
+ assert C.P == [[identity, None, None, None],
731
+ [None, identity, None, None]]
732
+ assert C.table == [[1, None, None, None],
733
+ [None, 0, None, None]]
734
+
735
+ C.modified_define(1, x)
736
+ assert C.table == [[1, None, None, None],
737
+ [2, 0, None, None],
738
+ [None, 1, None, None]]
739
+ assert C.P == [[identity, None, None, None],
740
+ [identity, identity, None, None],
741
+ [None, identity, None, None]]
742
+
743
+ C.modified_scan(0, x**3, C._grp.identity, fill=False)
744
+ assert C.P == [[identity, identity, None, None],
745
+ [identity, identity, None, None],
746
+ [identity, identity, None, None]]
747
+ assert C.table == [[1, 2, None, None],
748
+ [2, 0, None, None],
749
+ [0, 1, None, None]]
750
+
751
+ C.modified_scan(0, x*y, C._grp.generators[0], fill=False)
752
+ assert C.P == [[identity, identity, None, a_0**-1],
753
+ [identity, identity, a_0, None],
754
+ [identity, identity, None, None]]
755
+ assert C.table == [[1, 2, None, 1],
756
+ [2, 0, 0, None],
757
+ [0, 1, None, None]]
758
+
759
+ C.modified_define(2, y**-1)
760
+ assert C.table == [[1, 2, None, 1],
761
+ [2, 0, 0, None],
762
+ [0, 1, None, 3],
763
+ [None, None, 2, None]]
764
+ assert C.P == [[identity, identity, None, a_0**-1],
765
+ [identity, identity, a_0, None],
766
+ [identity, identity, None, identity],
767
+ [None, None, identity, None]]
768
+
769
+ C.modified_scan(0, x**-1*y**-1*x*y*x, C._grp.generators[1])
770
+ assert C.table == [[1, 2, None, 1],
771
+ [2, 0, 0, None],
772
+ [0, 1, None, 3],
773
+ [3, 3, 2, None]]
774
+ assert C.P == [[identity, identity, None, a_0**-1],
775
+ [identity, identity, a_0, None],
776
+ [identity, identity, None, identity],
777
+ [a_1, a_1**-1, identity, None]]
778
+
779
+ C.modified_scan(2, (x*y)**2, C._grp.identity)
780
+ assert C.table == [[1, 2, 3, 1],
781
+ [2, 0, 0, None],
782
+ [0, 1, None, 3],
783
+ [3, 3, 2, 0]]
784
+ assert C.P == [[identity, identity, a_1**-1, a_0**-1],
785
+ [identity, identity, a_0, None],
786
+ [identity, identity, None, identity],
787
+ [a_1, a_1**-1, identity, a_1]]
788
+
789
+ C.modified_define(2, y)
790
+ assert C.table == [[1, 2, 3, 1],
791
+ [2, 0, 0, None],
792
+ [0, 1, 4, 3],
793
+ [3, 3, 2, 0],
794
+ [None, None, None, 2]]
795
+ assert C.P == [[identity, identity, a_1**-1, a_0**-1],
796
+ [identity, identity, a_0, None],
797
+ [identity, identity, identity, identity],
798
+ [a_1, a_1**-1, identity, a_1],
799
+ [None, None, None, identity]]
800
+
801
+ C.modified_scan(0, y**5, C._grp.identity)
802
+ assert C.table == [[1, 2, 3, 1], [2, 0, 0, 4], [0, 1, 4, 3], [3, 3, 2, 0], [None, None, 1, 2]]
803
+ assert C.P == [[identity, identity, a_1**-1, a_0**-1],
804
+ [identity, identity, a_0, a_0*a_1**-1],
805
+ [identity, identity, identity, identity],
806
+ [a_1, a_1**-1, identity, a_1],
807
+ [None, None, a_1*a_0**-1, identity]]
808
+
809
+ C.modified_scan(1, (x*y)**2, C._grp.identity)
810
+ assert C.table == [[1, 2, 3, 1],
811
+ [2, 0, 0, 4],
812
+ [0, 1, 4, 3],
813
+ [3, 3, 2, 0],
814
+ [4, 4, 1, 2]]
815
+ assert C.P == [[identity, identity, a_1**-1, a_0**-1],
816
+ [identity, identity, a_0, a_0*a_1**-1],
817
+ [identity, identity, identity, identity],
818
+ [a_1, a_1**-1, identity, a_1],
819
+ [a_0*a_1**-1, a_1*a_0**-1, a_1*a_0**-1, identity]]
820
+
821
+ # Modified coset enumeration test
822
+ f = FpGroup(F, [x**3, y**3, x**-1*y**-1*x*y])
823
+ C = coset_enumeration_r(f, [x])
824
+ C_m = modified_coset_enumeration_r(f, [x])
825
+ assert C_m.table == C.table