123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- 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():
- for gen in subgr_gens:
- conjugates.add(gen ^ el)
- 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 ot 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)
- # the indices of the tensor are twice the number of lines of the graph
- num_indices = 0
- for v, neigh in items:
- num_indices += len(neigh)
- # associate to each vertex its indices; for each line
- # between two vertices assign the
- # even index to the vertex which comes first in items,
- # the odd index to the other vertex
- 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
|