testutil.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. from sympy.combinatorics import Permutation
  2. from sympy.combinatorics.util import _distribute_gens_by_base
  3. rmul = Permutation.rmul
  4. def _cmp_perm_lists(first, second):
  5. """
  6. Compare two lists of permutations as sets.
  7. Explanation
  8. ===========
  9. This is used for testing purposes. Since the array form of a
  10. permutation is currently a list, Permutation is not hashable
  11. and cannot be put into a set.
  12. Examples
  13. ========
  14. >>> from sympy.combinatorics.permutations import Permutation
  15. >>> from sympy.combinatorics.testutil import _cmp_perm_lists
  16. >>> a = Permutation([0, 2, 3, 4, 1])
  17. >>> b = Permutation([1, 2, 0, 4, 3])
  18. >>> c = Permutation([3, 4, 0, 1, 2])
  19. >>> ls1 = [a, b, c]
  20. >>> ls2 = [b, c, a]
  21. >>> _cmp_perm_lists(ls1, ls2)
  22. True
  23. """
  24. return {tuple(a) for a in first} == \
  25. {tuple(a) for a in second}
  26. def _naive_list_centralizer(self, other, af=False):
  27. from sympy.combinatorics.perm_groups import PermutationGroup
  28. """
  29. Return a list of elements for the centralizer of a subgroup/set/element.
  30. Explanation
  31. ===========
  32. This is a brute force implementation that goes over all elements of the
  33. group and checks for membership in the centralizer. It is used to
  34. test ``.centralizer()`` from ``sympy.combinatorics.perm_groups``.
  35. Examples
  36. ========
  37. >>> from sympy.combinatorics.testutil import _naive_list_centralizer
  38. >>> from sympy.combinatorics.named_groups import DihedralGroup
  39. >>> D = DihedralGroup(4)
  40. >>> _naive_list_centralizer(D, D)
  41. [Permutation([0, 1, 2, 3]), Permutation([2, 3, 0, 1])]
  42. See Also
  43. ========
  44. sympy.combinatorics.perm_groups.centralizer
  45. """
  46. from sympy.combinatorics.permutations import _af_commutes_with
  47. if hasattr(other, 'generators'):
  48. elements = list(self.generate_dimino(af=True))
  49. gens = [x._array_form for x in other.generators]
  50. commutes_with_gens = lambda x: all(_af_commutes_with(x, gen) for gen in gens)
  51. centralizer_list = []
  52. if not af:
  53. for element in elements:
  54. if commutes_with_gens(element):
  55. centralizer_list.append(Permutation._af_new(element))
  56. else:
  57. for element in elements:
  58. if commutes_with_gens(element):
  59. centralizer_list.append(element)
  60. return centralizer_list
  61. elif hasattr(other, 'getitem'):
  62. return _naive_list_centralizer(self, PermutationGroup(other), af)
  63. elif hasattr(other, 'array_form'):
  64. return _naive_list_centralizer(self, PermutationGroup([other]), af)
  65. def _verify_bsgs(group, base, gens):
  66. """
  67. Verify the correctness of a base and strong generating set.
  68. Explanation
  69. ===========
  70. This is a naive implementation using the definition of a base and a strong
  71. generating set relative to it. There are other procedures for
  72. verifying a base and strong generating set, but this one will
  73. serve for more robust testing.
  74. Examples
  75. ========
  76. >>> from sympy.combinatorics.named_groups import AlternatingGroup
  77. >>> from sympy.combinatorics.testutil import _verify_bsgs
  78. >>> A = AlternatingGroup(4)
  79. >>> A.schreier_sims()
  80. >>> _verify_bsgs(A, A.base, A.strong_gens)
  81. True
  82. See Also
  83. ========
  84. sympy.combinatorics.perm_groups.PermutationGroup.schreier_sims
  85. """
  86. from sympy.combinatorics.perm_groups import PermutationGroup
  87. strong_gens_distr = _distribute_gens_by_base(base, gens)
  88. current_stabilizer = group
  89. for i in range(len(base)):
  90. candidate = PermutationGroup(strong_gens_distr[i])
  91. if current_stabilizer.order() != candidate.order():
  92. return False
  93. current_stabilizer = current_stabilizer.stabilizer(base[i])
  94. if current_stabilizer.order() != 1:
  95. return False
  96. return True
  97. def _verify_centralizer(group, arg, centr=None):
  98. """
  99. Verify the centralizer of a group/set/element inside another group.
  100. This is used for testing ``.centralizer()`` from
  101. ``sympy.combinatorics.perm_groups``
  102. Examples
  103. ========
  104. >>> from sympy.combinatorics.named_groups import (SymmetricGroup,
  105. ... AlternatingGroup)
  106. >>> from sympy.combinatorics.perm_groups import PermutationGroup
  107. >>> from sympy.combinatorics.permutations import Permutation
  108. >>> from sympy.combinatorics.testutil import _verify_centralizer
  109. >>> S = SymmetricGroup(5)
  110. >>> A = AlternatingGroup(5)
  111. >>> centr = PermutationGroup([Permutation([0, 1, 2, 3, 4])])
  112. >>> _verify_centralizer(S, A, centr)
  113. True
  114. See Also
  115. ========
  116. _naive_list_centralizer,
  117. sympy.combinatorics.perm_groups.PermutationGroup.centralizer,
  118. _cmp_perm_lists
  119. """
  120. if centr is None:
  121. centr = group.centralizer(arg)
  122. centr_list = list(centr.generate_dimino(af=True))
  123. centr_list_naive = _naive_list_centralizer(group, arg, af=True)
  124. return _cmp_perm_lists(centr_list, centr_list_naive)
  125. def _verify_normal_closure(group, arg, closure=None):
  126. from sympy.combinatorics.perm_groups import PermutationGroup
  127. """
  128. Verify the normal closure of a subgroup/subset/element in a group.
  129. This is used to test
  130. sympy.combinatorics.perm_groups.PermutationGroup.normal_closure
  131. Examples
  132. ========
  133. >>> from sympy.combinatorics.named_groups import (SymmetricGroup,
  134. ... AlternatingGroup)
  135. >>> from sympy.combinatorics.testutil import _verify_normal_closure
  136. >>> S = SymmetricGroup(3)
  137. >>> A = AlternatingGroup(3)
  138. >>> _verify_normal_closure(S, A, closure=A)
  139. True
  140. See Also
  141. ========
  142. sympy.combinatorics.perm_groups.PermutationGroup.normal_closure
  143. """
  144. if closure is None:
  145. closure = group.normal_closure(arg)
  146. conjugates = set()
  147. if hasattr(arg, 'generators'):
  148. subgr_gens = arg.generators
  149. elif hasattr(arg, '__getitem__'):
  150. subgr_gens = arg
  151. elif hasattr(arg, 'array_form'):
  152. subgr_gens = [arg]
  153. for el in group.generate_dimino():
  154. for gen in subgr_gens:
  155. conjugates.add(gen ^ el)
  156. naive_closure = PermutationGroup(list(conjugates))
  157. return closure.is_subgroup(naive_closure)
  158. def canonicalize_naive(g, dummies, sym, *v):
  159. """
  160. Canonicalize tensor formed by tensors of the different types.
  161. Explanation
  162. ===========
  163. sym_i symmetry under exchange of two component tensors of type `i`
  164. None no symmetry
  165. 0 commuting
  166. 1 anticommuting
  167. Parameters
  168. ==========
  169. g : Permutation representing the tensor.
  170. dummies : List of dummy indices.
  171. msym : Symmetry of the metric.
  172. v : A list of (base_i, gens_i, n_i, sym_i) for tensors of type `i`.
  173. base_i, gens_i BSGS for tensors of this type
  174. n_i number ot tensors of type `i`
  175. Returns
  176. =======
  177. Returns 0 if the tensor is zero, else returns the array form of
  178. the permutation representing the canonical form of the tensor.
  179. Examples
  180. ========
  181. >>> from sympy.combinatorics.testutil import canonicalize_naive
  182. >>> from sympy.combinatorics.tensor_can import get_symmetric_group_sgs
  183. >>> from sympy.combinatorics import Permutation
  184. >>> g = Permutation([1, 3, 2, 0, 4, 5])
  185. >>> base2, gens2 = get_symmetric_group_sgs(2)
  186. >>> canonicalize_naive(g, [2, 3], 0, (base2, gens2, 2, 0))
  187. [0, 2, 1, 3, 4, 5]
  188. """
  189. from sympy.combinatorics.perm_groups import PermutationGroup
  190. from sympy.combinatorics.tensor_can import gens_products, dummy_sgs
  191. from sympy.combinatorics.permutations import _af_rmul
  192. v1 = []
  193. for i in range(len(v)):
  194. base_i, gens_i, n_i, sym_i = v[i]
  195. v1.append((base_i, gens_i, [[]]*n_i, sym_i))
  196. size, sbase, sgens = gens_products(*v1)
  197. dgens = dummy_sgs(dummies, sym, size-2)
  198. if isinstance(sym, int):
  199. num_types = 1
  200. dummies = [dummies]
  201. sym = [sym]
  202. else:
  203. num_types = len(sym)
  204. dgens = []
  205. for i in range(num_types):
  206. dgens.extend(dummy_sgs(dummies[i], sym[i], size - 2))
  207. S = PermutationGroup(sgens)
  208. D = PermutationGroup([Permutation(x) for x in dgens])
  209. dlist = list(D.generate(af=True))
  210. g = g.array_form
  211. st = set()
  212. for s in S.generate(af=True):
  213. h = _af_rmul(g, s)
  214. for d in dlist:
  215. q = tuple(_af_rmul(d, h))
  216. st.add(q)
  217. a = list(st)
  218. a.sort()
  219. prev = (0,)*size
  220. for h in a:
  221. if h[:-2] == prev[:-2]:
  222. if h[-1] != prev[-1]:
  223. return 0
  224. prev = h
  225. return list(a[0])
  226. def graph_certificate(gr):
  227. """
  228. Return a certificate for the graph
  229. Parameters
  230. ==========
  231. gr : adjacency list
  232. Explanation
  233. ===========
  234. The graph is assumed to be unoriented and without
  235. external lines.
  236. Associate to each vertex of the graph a symmetric tensor with
  237. number of indices equal to the degree of the vertex; indices
  238. are contracted when they correspond to the same line of the graph.
  239. The canonical form of the tensor gives a certificate for the graph.
  240. This is not an efficient algorithm to get the certificate of a graph.
  241. Examples
  242. ========
  243. >>> from sympy.combinatorics.testutil import graph_certificate
  244. >>> 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]}
  245. >>> 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]}
  246. >>> c1 = graph_certificate(gr1)
  247. >>> c2 = graph_certificate(gr2)
  248. >>> c1
  249. [0, 2, 4, 6, 1, 8, 10, 12, 3, 14, 16, 18, 5, 9, 15, 7, 11, 17, 13, 19, 20, 21]
  250. >>> c1 == c2
  251. True
  252. """
  253. from sympy.combinatorics.permutations import _af_invert
  254. from sympy.combinatorics.tensor_can import get_symmetric_group_sgs, canonicalize
  255. items = list(gr.items())
  256. items.sort(key=lambda x: len(x[1]), reverse=True)
  257. pvert = [x[0] for x in items]
  258. pvert = _af_invert(pvert)
  259. # the indices of the tensor are twice the number of lines of the graph
  260. num_indices = 0
  261. for v, neigh in items:
  262. num_indices += len(neigh)
  263. # associate to each vertex its indices; for each line
  264. # between two vertices assign the
  265. # even index to the vertex which comes first in items,
  266. # the odd index to the other vertex
  267. vertices = [[] for i in items]
  268. i = 0
  269. for v, neigh in items:
  270. for v2 in neigh:
  271. if pvert[v] < pvert[v2]:
  272. vertices[pvert[v]].append(i)
  273. vertices[pvert[v2]].append(i+1)
  274. i += 2
  275. g = []
  276. for v in vertices:
  277. g.extend(v)
  278. assert len(g) == num_indices
  279. g += [num_indices, num_indices + 1]
  280. size = num_indices + 2
  281. assert sorted(g) == list(range(size))
  282. g = Permutation(g)
  283. vlen = [0]*(len(vertices[0])+1)
  284. for neigh in vertices:
  285. vlen[len(neigh)] += 1
  286. v = []
  287. for i in range(len(vlen)):
  288. n = vlen[i]
  289. if n:
  290. base, gens = get_symmetric_group_sgs(i)
  291. v.append((base, gens, n, 0))
  292. v.reverse()
  293. dummies = list(range(num_indices))
  294. can = canonicalize(g, dummies, 0, *v)
  295. return can