Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/coset_table.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/fp_groups.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/free_groups.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/galois.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/generators.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/graycode.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/group_constructs.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/group_numbers.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/homomorphisms.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/named_groups.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/partitions.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/pc_groups.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/polyhedron.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/prufer.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/rewritingsystem.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/rewritingsystem_fsm.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/schur_number.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/subsets.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/tensor_can.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/testutil.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/__pycache__/util.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/group_numbers.py +296 -0
- lib/python3.12/site-packages/sympy/combinatorics/partitions.py +745 -0
- lib/python3.12/site-packages/sympy/combinatorics/rewritingsystem.py +453 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__init__.py +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_coset_table.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_fp_groups.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_free_groups.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_galois.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_generators.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_graycode.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_group_constructs.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_group_numbers.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_homomorphisms.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_named_groups.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_partitions.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_pc_groups.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_perm_groups.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_permutations.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_polyhedron.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_prufer.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_rewriting.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_schur_number.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_subsets.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_tensor_can.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_testutil.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/combinatorics/tests/__pycache__/test_util.cpython-312.pyc +0 -0
- 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
|