|
from sympy.combinatorics import Permutation |
|
from sympy.combinatorics.util import _distribute_gens_by_base |
|
|
|
rmul = Permutation.rmul |
|
|
|
|
|
def _cmp_perm_lists(first, second): |
|
""" |
|
Compare two lists of permutations as sets. |
|
|
|
Explanation |
|
=========== |
|
|
|
This is used for testing purposes. Since the array form of a |
|
permutation is currently a list, Permutation is not hashable |
|
and cannot be put into a set. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.combinatorics.permutations import Permutation |
|
>>> from sympy.combinatorics.testutil import _cmp_perm_lists |
|
>>> a = Permutation([0, 2, 3, 4, 1]) |
|
>>> b = Permutation([1, 2, 0, 4, 3]) |
|
>>> c = Permutation([3, 4, 0, 1, 2]) |
|
>>> ls1 = [a, b, c] |
|
>>> ls2 = [b, c, a] |
|
>>> _cmp_perm_lists(ls1, ls2) |
|
True |
|
|
|
""" |
|
return {tuple(a) for a in first} == \ |
|
{tuple(a) for a in second} |
|
|
|
|
|
def _naive_list_centralizer(self, other, af=False): |
|
from sympy.combinatorics.perm_groups import PermutationGroup |
|
""" |
|
Return a list of elements for the centralizer of a subgroup/set/element. |
|
|
|
Explanation |
|
=========== |
|
|
|
This is a brute force implementation that goes over all elements of the |
|
group and checks for membership in the centralizer. It is used to |
|
test ``.centralizer()`` from ``sympy.combinatorics.perm_groups``. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.combinatorics.testutil import _naive_list_centralizer |
|
>>> from sympy.combinatorics.named_groups import DihedralGroup |
|
>>> D = DihedralGroup(4) |
|
>>> _naive_list_centralizer(D, D) |
|
[Permutation([0, 1, 2, 3]), Permutation([2, 3, 0, 1])] |
|
|
|
See Also |
|
======== |
|
|
|
sympy.combinatorics.perm_groups.centralizer |
|
|
|
""" |
|
from sympy.combinatorics.permutations import _af_commutes_with |
|
if hasattr(other, 'generators'): |
|
elements = list(self.generate_dimino(af=True)) |
|
gens = [x._array_form for x in other.generators] |
|
commutes_with_gens = lambda x: all(_af_commutes_with(x, gen) for gen in gens) |
|
centralizer_list = [] |
|
if not af: |
|
for element in elements: |
|
if commutes_with_gens(element): |
|
centralizer_list.append(Permutation._af_new(element)) |
|
else: |
|
for element in elements: |
|
if commutes_with_gens(element): |
|
centralizer_list.append(element) |
|
return centralizer_list |
|
elif hasattr(other, 'getitem'): |
|
return _naive_list_centralizer(self, PermutationGroup(other), af) |
|
elif hasattr(other, 'array_form'): |
|
return _naive_list_centralizer(self, PermutationGroup([other]), af) |
|
|
|
|
|
def _verify_bsgs(group, base, gens): |
|
""" |
|
Verify the correctness of a base and strong generating set. |
|
|
|
Explanation |
|
=========== |
|
|
|
This is a naive implementation using the definition of a base and a strong |
|
generating set relative to it. There are other procedures for |
|
verifying a base and strong generating set, but this one will |
|
serve for more robust testing. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.combinatorics.named_groups import AlternatingGroup |
|
>>> from sympy.combinatorics.testutil import _verify_bsgs |
|
>>> A = AlternatingGroup(4) |
|
>>> A.schreier_sims() |
|
>>> _verify_bsgs(A, A.base, A.strong_gens) |
|
True |
|
|
|
See Also |
|
======== |
|
|
|
sympy.combinatorics.perm_groups.PermutationGroup.schreier_sims |
|
|
|
""" |
|
from sympy.combinatorics.perm_groups import PermutationGroup |
|
strong_gens_distr = _distribute_gens_by_base(base, gens) |
|
current_stabilizer = group |
|
for i in range(len(base)): |
|
candidate = PermutationGroup(strong_gens_distr[i]) |
|
if current_stabilizer.order() != candidate.order(): |
|
return False |
|
current_stabilizer = current_stabilizer.stabilizer(base[i]) |
|
if current_stabilizer.order() != 1: |
|
return False |
|
return True |
|
|
|
|
|
def _verify_centralizer(group, arg, centr=None): |
|
""" |
|
Verify the centralizer of a group/set/element inside another group. |
|
|
|
This is used for testing ``.centralizer()`` from |
|
``sympy.combinatorics.perm_groups`` |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.combinatorics.named_groups import (SymmetricGroup, |
|
... AlternatingGroup) |
|
>>> from sympy.combinatorics.perm_groups import PermutationGroup |
|
>>> from sympy.combinatorics.permutations import Permutation |
|
>>> from sympy.combinatorics.testutil import _verify_centralizer |
|
>>> S = SymmetricGroup(5) |
|
>>> A = AlternatingGroup(5) |
|
>>> centr = PermutationGroup([Permutation([0, 1, 2, 3, 4])]) |
|
>>> _verify_centralizer(S, A, centr) |
|
True |
|
|
|
See Also |
|
======== |
|
|
|
_naive_list_centralizer, |
|
sympy.combinatorics.perm_groups.PermutationGroup.centralizer, |
|
_cmp_perm_lists |
|
|
|
""" |
|
if centr is None: |
|
centr = group.centralizer(arg) |
|
centr_list = list(centr.generate_dimino(af=True)) |
|
centr_list_naive = _naive_list_centralizer(group, arg, af=True) |
|
return _cmp_perm_lists(centr_list, centr_list_naive) |
|
|
|
|
|
def _verify_normal_closure(group, arg, closure=None): |
|
from sympy.combinatorics.perm_groups import PermutationGroup |
|
""" |
|
Verify the normal closure of a subgroup/subset/element in a group. |
|
|
|
This is used to test |
|
sympy.combinatorics.perm_groups.PermutationGroup.normal_closure |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.combinatorics.named_groups import (SymmetricGroup, |
|
... AlternatingGroup) |
|
>>> from sympy.combinatorics.testutil import _verify_normal_closure |
|
>>> S = SymmetricGroup(3) |
|
>>> A = AlternatingGroup(3) |
|
>>> _verify_normal_closure(S, A, closure=A) |
|
True |
|
|
|
See Also |
|
======== |
|
|
|
sympy.combinatorics.perm_groups.PermutationGroup.normal_closure |
|
|
|
""" |
|
if closure is None: |
|
closure = group.normal_closure(arg) |
|
conjugates = set() |
|
if hasattr(arg, 'generators'): |
|
subgr_gens = arg.generators |
|
elif hasattr(arg, '__getitem__'): |
|
subgr_gens = arg |
|
elif hasattr(arg, 'array_form'): |
|
subgr_gens = [arg] |
|
for el in group.generate_dimino(): |
|
conjugates.update(gen ^ el for gen in subgr_gens) |
|
naive_closure = PermutationGroup(list(conjugates)) |
|
return closure.is_subgroup(naive_closure) |
|
|
|
|
|
def canonicalize_naive(g, dummies, sym, *v): |
|
""" |
|
Canonicalize tensor formed by tensors of the different types. |
|
|
|
Explanation |
|
=========== |
|
|
|
sym_i symmetry under exchange of two component tensors of type `i` |
|
None no symmetry |
|
0 commuting |
|
1 anticommuting |
|
|
|
Parameters |
|
========== |
|
|
|
g : Permutation representing the tensor. |
|
dummies : List of dummy indices. |
|
msym : Symmetry of the metric. |
|
v : A list of (base_i, gens_i, n_i, sym_i) for tensors of type `i`. |
|
base_i, gens_i BSGS for tensors of this type |
|
n_i number of tensors of type `i` |
|
|
|
Returns |
|
======= |
|
|
|
Returns 0 if the tensor is zero, else returns the array form of |
|
the permutation representing the canonical form of the tensor. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.combinatorics.testutil import canonicalize_naive |
|
>>> from sympy.combinatorics.tensor_can import get_symmetric_group_sgs |
|
>>> from sympy.combinatorics import Permutation |
|
>>> g = Permutation([1, 3, 2, 0, 4, 5]) |
|
>>> base2, gens2 = get_symmetric_group_sgs(2) |
|
>>> canonicalize_naive(g, [2, 3], 0, (base2, gens2, 2, 0)) |
|
[0, 2, 1, 3, 4, 5] |
|
""" |
|
from sympy.combinatorics.perm_groups import PermutationGroup |
|
from sympy.combinatorics.tensor_can import gens_products, dummy_sgs |
|
from sympy.combinatorics.permutations import _af_rmul |
|
v1 = [] |
|
for i in range(len(v)): |
|
base_i, gens_i, n_i, sym_i = v[i] |
|
v1.append((base_i, gens_i, [[]]*n_i, sym_i)) |
|
size, sbase, sgens = gens_products(*v1) |
|
dgens = dummy_sgs(dummies, sym, size-2) |
|
if isinstance(sym, int): |
|
num_types = 1 |
|
dummies = [dummies] |
|
sym = [sym] |
|
else: |
|
num_types = len(sym) |
|
dgens = [] |
|
for i in range(num_types): |
|
dgens.extend(dummy_sgs(dummies[i], sym[i], size - 2)) |
|
S = PermutationGroup(sgens) |
|
D = PermutationGroup([Permutation(x) for x in dgens]) |
|
dlist = list(D.generate(af=True)) |
|
g = g.array_form |
|
st = set() |
|
for s in S.generate(af=True): |
|
h = _af_rmul(g, s) |
|
for d in dlist: |
|
q = tuple(_af_rmul(d, h)) |
|
st.add(q) |
|
a = list(st) |
|
a.sort() |
|
prev = (0,)*size |
|
for h in a: |
|
if h[:-2] == prev[:-2]: |
|
if h[-1] != prev[-1]: |
|
return 0 |
|
prev = h |
|
return list(a[0]) |
|
|
|
|
|
def graph_certificate(gr): |
|
""" |
|
Return a certificate for the graph |
|
|
|
Parameters |
|
========== |
|
|
|
gr : adjacency list |
|
|
|
Explanation |
|
=========== |
|
|
|
The graph is assumed to be unoriented and without |
|
external lines. |
|
|
|
Associate to each vertex of the graph a symmetric tensor with |
|
number of indices equal to the degree of the vertex; indices |
|
are contracted when they correspond to the same line of the graph. |
|
The canonical form of the tensor gives a certificate for the graph. |
|
|
|
This is not an efficient algorithm to get the certificate of a graph. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.combinatorics.testutil import graph_certificate |
|
>>> gr1 = {0:[1, 2, 3, 5], 1:[0, 2, 4], 2:[0, 1, 3, 4], 3:[0, 2, 4], 4:[1, 2, 3, 5], 5:[0, 4]} |
|
>>> gr2 = {0:[1, 5], 1:[0, 2, 3, 4], 2:[1, 3, 5], 3:[1, 2, 4, 5], 4:[1, 3, 5], 5:[0, 2, 3, 4]} |
|
>>> c1 = graph_certificate(gr1) |
|
>>> c2 = graph_certificate(gr2) |
|
>>> c1 |
|
[0, 2, 4, 6, 1, 8, 10, 12, 3, 14, 16, 18, 5, 9, 15, 7, 11, 17, 13, 19, 20, 21] |
|
>>> c1 == c2 |
|
True |
|
""" |
|
from sympy.combinatorics.permutations import _af_invert |
|
from sympy.combinatorics.tensor_can import get_symmetric_group_sgs, canonicalize |
|
items = list(gr.items()) |
|
items.sort(key=lambda x: len(x[1]), reverse=True) |
|
pvert = [x[0] for x in items] |
|
pvert = _af_invert(pvert) |
|
|
|
|
|
num_indices = 0 |
|
for v, neigh in items: |
|
num_indices += len(neigh) |
|
|
|
|
|
|
|
|
|
vertices = [[] for i in items] |
|
i = 0 |
|
for v, neigh in items: |
|
for v2 in neigh: |
|
if pvert[v] < pvert[v2]: |
|
vertices[pvert[v]].append(i) |
|
vertices[pvert[v2]].append(i+1) |
|
i += 2 |
|
g = [] |
|
for v in vertices: |
|
g.extend(v) |
|
assert len(g) == num_indices |
|
g += [num_indices, num_indices + 1] |
|
size = num_indices + 2 |
|
assert sorted(g) == list(range(size)) |
|
g = Permutation(g) |
|
vlen = [0]*(len(vertices[0])+1) |
|
for neigh in vertices: |
|
vlen[len(neigh)] += 1 |
|
v = [] |
|
for i in range(len(vlen)): |
|
n = vlen[i] |
|
if n: |
|
base, gens = get_symmetric_group_sgs(i) |
|
v.append((base, gens, n, 0)) |
|
v.reverse() |
|
dummies = list(range(num_indices)) |
|
can = canonicalize(g, dummies, 0, *v) |
|
return can |
|
|