# sympy/sympy

Ordered the methods in several files alphabetically.

These were: the standalone methods in named_groups.py and
util.py, and the class methods for the classes _JGraph
and PermutationGroup in perm_groups.py.
1 parent 3ad1c91 commit 0fbc00dfe0f0f6ee77346a38c736809a16908e9a Aleksandar Makelov committed
Showing with 1,565 additions and 1,566 deletions.
1. +98 −98 sympy/combinatorics/named_groups.py
2. +1,284 −1,286 sympy/combinatorics/perm_groups.py
3. +183 −182 sympy/combinatorics/util.py
196 sympy/combinatorics/named_groups.py
 @@ -1,60 +1,98 @@ from sympy.combinatorics.perm_groups import PermutationGroup, DirectProduct from sympy.combinatorics.permutations import Permutation, _new_from_array_form -def SymmetricGroup(n): +def AbelianGroup(*cyclic_orders): """ - Generates the symmetric group on n elements as a permutation group. + Returns the direct product of cyclic groups with the given orders. - The generators taken are the n-cycle - (0 1 2 ... n-1) and the transposition (0 1) (in cycle notation). - (See [1]). After the group is generated, some of its basic properties - are set. + According to the structure theorem for finite abelian groups ([1]), + every finite abelian group can be written as the direct product of finitely + many cyclic groups. + [1] http://groupprops.subwiki.org/wiki/Structure_theorem_for_finitely + _generated_abelian_groups Examples ======== - >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> G = SymmetricGroup(4) - >>> G.order() - 24 - >>> list(G.generate_schreier_sims(af=True)) - [[0, 1, 2, 3], [0, 1, 3, 2], [0, 2, 1, 3], [0, 2, 3, 1], [0, 3, 1, 2], - [0, 3, 2, 1], [1, 2, 3, 0], [1, 2, 0, 3], [1, 3, 2, 0], - [1, 3, 0, 2], [1, 0, 2, 3], [1, 0, 3, 2], [2, 3, 0, 1], [2, 3, 1, 0], - [2, 0, 3, 1], [2, 0, 1, 3], [2, 1, 3, 0], [2, 1, 0, 3], [3, 0, 1, 2], - [3, 0, 2, 1], [3, 1, 0, 2], [3, 1, 2, 0], [3, 2, 0, 1], [3, 2, 1, 0]] + >>> from sympy.combinatorics.named_groups import AbelianGroup + >>> AbelianGroup(3,4) + PermutationGroup([Permutation([1, 2, 0, 3, 4, 5, 6]),\ + Permutation([0, 1, 2, 4, 5, 6, 3])]) See Also ======== + DirectProduct + """ + groups = [] + degree = 0 + order = 1 + for size in cyclic_orders: + degree += size + order *= size + groups.append(CyclicGroup(size)) + G = DirectProduct(*groups) + G._is_abelian = True + G._degree = degree + G._order = order - CyclicGroup, DihedralGroup, AlternatingGroup + return G + +def AlternatingGroup(n): + """ + Generates the alternating group on n elements as a permutation group. + + For n > 2, the generators taken are (0 1 2), (0 1 2 ... n-1) for + n odd + and (0 1 2), (1 2 ... n-1) for n even (See [1], p.31, ex.6.9.). + After the group is generated, some of its basic properties are set. + The cases n = 1, 2 are handled separately. + + Examples + ======== + + >>> from sympy.combinatorics.named_groups import AlternatingGroup + >>> G = AlternatingGroup(4) + >>> a = list(G.generate_dimino()) + >>> len(a) + 12 + >>> [perm.is_even for perm in a] + [True, True, True, True, True, True, True, True, True, True, True, True] + + See Also + ======== + + SymmetricGroup, CyclicGroup, DihedralGroup References ========== - [1] http://en.wikipedia.org/wiki/Symmetric_group#Generators_and_relations + [1] Armstrong, M. "Groups and Symmetry" """ - if n == 1: - G = PermutationGroup([Permutation([0])]) - elif n == 2: - G = PermutationGroup([Permutation([1, 0])]) - else: - a = range(1,n) + # small cases are special + if n == 1 or n == 2: + return PermutationGroup([Permutation([0])]) + + a = range(n) + a[0], a[1], a[2] = a[1], a[2], a[0] + gen1 = _new_from_array_form(a) + if n % 2 == 1: + a = range(1, n) a.append(0) - gen1 = _new_from_array_form(a) - a = range(n) - a[0], a[1] = a[1], a[0] gen2 = _new_from_array_form(a) - G = PermutationGroup([gen1, gen2]) + else: + a = range(2, n) + a.append(1) + gen2 = _new_from_array_form([0] + a) + G = PermutationGroup([gen1, gen2]) - if n<3: + if n<4: G._is_abelian = True else: G._is_abelian = False G._degree = n G._is_transitive = True - G._is_sym = True + G._is_alt = True return G def CyclicGroup(n): @@ -149,96 +187,58 @@ def DihedralGroup(n): G._order = 2*n return G -def AlternatingGroup(n): +def SymmetricGroup(n): """ - Generates the alternating group on n elements as a permutation group. + Generates the symmetric group on n elements as a permutation group. - For n > 2, the generators taken are (0 1 2), (0 1 2 ... n-1) for - n odd - and (0 1 2), (1 2 ... n-1) for n even (See [1], p.31, ex.6.9.). - After the group is generated, some of its basic properties are set. - The cases n = 1, 2 are handled separately. + The generators taken are the n-cycle + (0 1 2 ... n-1) and the transposition (0 1) (in cycle notation). + (See [1]). After the group is generated, some of its basic properties + are set. Examples ======== - >>> from sympy.combinatorics.named_groups import AlternatingGroup - >>> G = AlternatingGroup(4) - >>> a = list(G.generate_dimino()) - >>> len(a) - 12 - >>> [perm.is_even for perm in a] - [True, True, True, True, True, True, True, True, True, True, True, True] + >>> from sympy.combinatorics.named_groups import SymmetricGroup + >>> G = SymmetricGroup(4) + >>> G.order() + 24 + >>> list(G.generate_schreier_sims(af=True)) + [[0, 1, 2, 3], [0, 1, 3, 2], [0, 2, 1, 3], [0, 2, 3, 1], [0, 3, 1, 2], + [0, 3, 2, 1], [1, 2, 3, 0], [1, 2, 0, 3], [1, 3, 2, 0], + [1, 3, 0, 2], [1, 0, 2, 3], [1, 0, 3, 2], [2, 3, 0, 1], [2, 3, 1, 0], + [2, 0, 3, 1], [2, 0, 1, 3], [2, 1, 3, 0], [2, 1, 0, 3], [3, 0, 1, 2], + [3, 0, 2, 1], [3, 1, 0, 2], [3, 1, 2, 0], [3, 2, 0, 1], [3, 2, 1, 0]] See Also ======== - SymmetricGroup, CyclicGroup, DihedralGroup + CyclicGroup, DihedralGroup, AlternatingGroup References ========== - [1] Armstrong, M. "Groups and Symmetry" + [1] http://en.wikipedia.org/wiki/Symmetric_group#Generators_and_relations """ - # small cases are special - if n == 1 or n == 2: - return PermutationGroup([Permutation([0])]) - - a = range(n) - a[0], a[1], a[2] = a[1], a[2], a[0] - gen1 = _new_from_array_form(a) - if n % 2 == 1: - a = range(1, n) + if n == 1: + G = PermutationGroup([Permutation([0])]) + elif n == 2: + G = PermutationGroup([Permutation([1, 0])]) + else: + a = range(1,n) a.append(0) + gen1 = _new_from_array_form(a) + a = range(n) + a[0], a[1] = a[1], a[0] gen2 = _new_from_array_form(a) - else: - a = range(2, n) - a.append(1) - gen2 = _new_from_array_form([0] + a) - G = PermutationGroup([gen1, gen2]) + G = PermutationGroup([gen1, gen2]) - if n<4: + if n<3: G._is_abelian = True else: G._is_abelian = False G._degree = n G._is_transitive = True - G._is_alt = True - return G - -def AbelianGroup(*cyclic_orders): - """ - Returns the direct product of cyclic groups with the given orders. - - According to the structure theorem for finite abelian groups ([1]), - every finite abelian group can be written as the direct product of finitely - many cyclic groups. - [1] http://groupprops.subwiki.org/wiki/Structure_theorem_for_finitely - _generated_abelian_groups - - Examples - ======== - - >>> from sympy.combinatorics.named_groups import AbelianGroup - >>> AbelianGroup(3,4) - PermutationGroup([Permutation([1, 2, 0, 3, 4, 5, 6]),\ - Permutation([0, 1, 2, 4, 5, 6, 3])]) - - See Also - ======== - DirectProduct - """ - groups = [] - degree = 0 - order = 1 - for size in cyclic_orders: - degree += size - order *= size - groups.append(CyclicGroup(size)) - G = DirectProduct(*groups) - G._is_abelian = True - G._degree = degree - G._order = order - + G._is_sym = True return G
2,570 sympy/combinatorics/perm_groups.py
1,284 additions, 1,286 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
365 sympy/combinatorics/util.py
 @@ -7,6 +7,61 @@ ### ############################################ + +def _base_ordering(base, degree): + r""" + Order \{0, 1, ..., n-1\} so that base points come first and in order. + + Parameters + ========== + + base - the base + degree - the degree of the associated permutation group + + Returns + ======= + + A list base_ordering such that base_ordering[point] is the + number of point in the ordering. + Examples + ======== + + >>> from sympy.combinatorics.named_groups import SymmetricGroup + >>> from sympy.combinatorics.util import _base_ordering + >>> S = SymmetricGroup(4) + >>> S.schreier_sims() + >>> _base_ordering(S.base, S.degree) + [0, 1, 2, 3] + + Notes + ===== + + This is used in backtrack searches, when we define a relation << on + the underlying set for a permutation group of degree n, + \{0, 1, ..., n-1\}, so that if (b_1, b_2, ..., b_k) is a base we + have b_i << b_j whenever i>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> from sympy.combinatorics.permutations import Permutation - >>> from sympy.combinatorics.util import _strip - >>> S = SymmetricGroup(5) - >>> S.schreier_sims() - >>> g = Permutation([0, 2, 3, 1, 4]) - >>> _strip(g, S.base, S.basic_orbits, S.basic_transversals) - (Permutation([0, 1, 2, 3, 4]), 5) - - Notes - ===== - - The algorithm is described in [1],pp.89-90. The reason for returning - both the current state of the element being decomposed and the level - at which the sifting ends is that they provide important information for - the randomized version of the Schreier-Sims algorithm. - - References - ========== - - [1] Holt, D., Eick, B., O'Brien, E. - "Handbook of computational group theory" - - See Also - ======== - - sympy.combinatorics.perm_groups.PermutationGroup.schreier_sims - - sympy.combinatorics.perm_groups.PermutationGroup.schreier_sims_random - - """ - h = g - base_len = len(base) - for i in range(base_len): - beta = h(base[i]) - if beta == base[i]: - continue - if beta not in orbs[i]: - return h, i + 1 - u = transversals[i][beta] - h = ~u*h - return h, base_len + 1 - def _distribute_gens_by_base(base, gens): """ Distribute the group elements gens by membership in basic stabilizers. @@ -189,47 +171,70 @@ def _distribute_gens_by_base(base, gens): stabs[i].append(_new_from_array_form(range(degree))) return stabs -def _strong_gens_from_distr(distr_gens): +def _handle_precomputed_bsgs(base, strong_gens, transversals=None,\ + basic_orbits=None, distr_gens=None): """ - Retrieve strong generating set from generators of basic stabilizers. + Calculate BSGS-related structures from those present. - This is just the union of the generators of the first and second basic - stabilizers. + The base and strong generating set must be provided; if any of the + transversals, basic orbits or distributed strong generators are not + provided, they will be calculated from the base and strong generating set. Parameters ========== + base - the base + strong_gens - the strong generators + transversals - basic transversals + basic_orbits - basic orbits distr_gens - strong generators distributed by membership in basic stabilizers + Returns + ======= + + (transversals, basic_orbits, distr_gens) where transversals are the + basic transversals, basic_orbits are the basic orbits, and + distr_gens are the strong generators distributed by membership in basic + stabilizers. + Examples ======== - >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> from sympy.combinatorics.util import (_strong_gens_from_distr, - ... _distribute_gens_by_base) - >>> S = SymmetricGroup(3) - >>> S.schreier_sims() - >>> S.strong_gens - [Permutation([1, 2, 0]), Permutation([1, 0, 2]), Permutation([0, 2, 1])] - >>> distr_gens = _distribute_gens_by_base(S.base, S.strong_gens) - >>> _strong_gens_from_distr(distr_gens) - [Permutation([1, 2, 0]), Permutation([1, 0, 2]), Permutation([0, 2, 1])] + >>> from sympy.combinatorics.named_groups import DihedralGroup + >>> from sympy.combinatorics.util import _handle_precomputed_bsgs + >>> D = DihedralGroup(3) + >>> D.schreier_sims() + >>> _handle_precomputed_bsgs(D.base, D.strong_gens, + ... basic_orbits=D.basic_orbits) + ([{0: Permutation([0, 1, 2]), 1: Permutation([1, 2, 0]),\ + 2: Permutation([2, 1, 0])}, {1: Permutation([0, 1, 2]),\ + 2: Permutation([0, 2, 1])}], [[0, 1, 2], [1, 2]], [[Permutation([1, 2, 0]),\ + Permutation([2, 1, 0]), Permutation([0, 2, 1])], [Permutation([0, 2, 1])]]) See Also ======== - _distribute_gens_by_base + _orbits_transversals_from_bsgs, distribute_gens_by_base """ - if len(distr_gens) == 1: - return distr_gens[0][:] + if distr_gens is None: + distr_gens = _distribute_gens_by_base(base, strong_gens) + if transversals is None: + if basic_orbits is None: + basic_orbits, transversals =\ + _orbits_transversals_from_bsgs(base, distr_gens) + else: + transversals =\ + _orbits_transversals_from_bsgs(base, distr_gens, + transversals_only=True) else: - result = distr_gens[0] - for gen in distr_gens[1]: - if gen not in result: - result.append(gen) - return result + if basic_orbits is None: + base_len = len(base) + basic_orbits = [None]*base_len + for i in xrange(base_len): + basic_orbits[i] = transversals[i].keys() + return transversals, basic_orbits, distr_gens def _orbits_transversals_from_bsgs(base, distr_gens,\ transversals_only=False): @@ -285,124 +290,120 @@ def _orbits_transversals_from_bsgs(base, distr_gens,\ else: return basic_orbits, transversals -def _handle_precomputed_bsgs(base, strong_gens, transversals=None,\ - basic_orbits=None, distr_gens=None): +def _strip(g, base, orbs, transversals): """ - Calculate BSGS-related structures from those present. + Attempt to decompose a permutation using a (possibly partial) BSGS + structure. - The base and strong generating set must be provided; if any of the - transversals, basic orbits or distributed strong generators are not - provided, they will be calculated from the base and strong generating set. + This is done by treating the sequence base as an actual base, and + the orbits orbs and transversals transversals as basic orbits and + transversals relative to it. + This process is called "sifting". A sift is unsuccessful when a certain + orbit element is not found or when after the sift the decomposition + doesn't end with the identity element. + The argument transversals is a list of dictionaries that provides + transversal elements for the orbits orbs. Parameters ========== - base - the base - strong_gens - the strong generators - transversals - basic transversals - basic_orbits - basic orbits - distr_gens - strong generators distributed by membership in basic - stabilizers - - Returns - ======= - - (transversals, basic_orbits, distr_gens) where transversals are the - basic transversals, basic_orbits are the basic orbits, and - distr_gens are the strong generators distributed by membership in basic - stabilizers. + g - permutation to be decomposed + base - sequence of points + orbs - a list in which the i-th entry is an orbit of base[i] + under some subgroup of the pointwise stabilizer of  + base[0], base[1], ..., base[i - 1]. The groups themselves are implicit + in this function since the only infromation we need is encoded in the orbits + and transversals + transversals - a list of orbit transversals associated with the orbits + orbs. Examples ======== - >>> from sympy.combinatorics.named_groups import DihedralGroup - >>> from sympy.combinatorics.util import _handle_precomputed_bsgs - >>> D = DihedralGroup(3) - >>> D.schreier_sims() - >>> _handle_precomputed_bsgs(D.base, D.strong_gens, - ... basic_orbits=D.basic_orbits) - ([{0: Permutation([0, 1, 2]), 1: Permutation([1, 2, 0]),\ - 2: Permutation([2, 1, 0])}, {1: Permutation([0, 1, 2]),\ - 2: Permutation([0, 2, 1])}], [[0, 1, 2], [1, 2]], [[Permutation([1, 2, 0]),\ - Permutation([2, 1, 0]), Permutation([0, 2, 1])], [Permutation([0, 2, 1])]]) + >>> from sympy.combinatorics.named_groups import SymmetricGroup + >>> from sympy.combinatorics.permutations import Permutation + >>> from sympy.combinatorics.util import _strip + >>> S = SymmetricGroup(5) + >>> S.schreier_sims() + >>> g = Permutation([0, 2, 3, 1, 4]) + >>> _strip(g, S.base, S.basic_orbits, S.basic_transversals) + (Permutation([0, 1, 2, 3, 4]), 5) + + Notes + ===== + + The algorithm is described in [1],pp.89-90. The reason for returning + both the current state of the element being decomposed and the level + at which the sifting ends is that they provide important information for + the randomized version of the Schreier-Sims algorithm. + + References + ========== + + [1] Holt, D., Eick, B., O'Brien, E. + "Handbook of computational group theory" See Also ======== - _orbits_transversals_from_bsgs, distribute_gens_by_base + sympy.combinatorics.perm_groups.PermutationGroup.schreier_sims + + sympy.combinatorics.perm_groups.PermutationGroup.schreier_sims_random """ - if distr_gens is None: - distr_gens = _distribute_gens_by_base(base, strong_gens) - if transversals is None: - if basic_orbits is None: - basic_orbits, transversals =\ - _orbits_transversals_from_bsgs(base, distr_gens) - else: - transversals =\ - _orbits_transversals_from_bsgs(base, distr_gens, - transversals_only=True) - else: - if basic_orbits is None: - base_len = len(base) - basic_orbits = [None]*base_len - for i in xrange(base_len): - basic_orbits[i] = transversals[i].keys() - return transversals, basic_orbits, distr_gens + h = g + base_len = len(base) + for i in range(base_len): + beta = h(base[i]) + if beta == base[i]: + continue + if beta not in orbs[i]: + return h, i + 1 + u = transversals[i][beta] + h = ~u*h + return h, base_len + 1 -def _base_ordering(base, degree): - r""" - Order \{0, 1, ..., n-1\} so that base points come first and in order. +def _strong_gens_from_distr(distr_gens): + """ + Retrieve strong generating set from generators of basic stabilizers. + + This is just the union of the generators of the first and second basic + stabilizers. Parameters ========== - base - the base - degree - the degree of the associated permutation group - - Returns - ======= + distr_gens - strong generators distributed by membership in basic + stabilizers - A list base_ordering such that base_ordering[point] is the - number of point in the ordering. Examples ======== >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> from sympy.combinatorics.util import _base_ordering - >>> S = SymmetricGroup(4) + >>> from sympy.combinatorics.util import (_strong_gens_from_distr, + ... _distribute_gens_by_base) + >>> S = SymmetricGroup(3) >>> S.schreier_sims() - >>> _base_ordering(S.base, S.degree) - [0, 1, 2, 3] - - Notes - ===== - - This is used in backtrack searches, when we define a relation << on - the underlying set for a permutation group of degree n, - \{0, 1, ..., n-1\}, so that if (b_1, b_2, ..., b_k) is a base we - have b_i << b_j whenever i>> S.strong_gens + [Permutation([1, 2, 0]), Permutation([1, 0, 2]), Permutation([0, 2, 1])] + >>> distr_gens = _distribute_gens_by_base(S.base, S.strong_gens) + >>> _strong_gens_from_distr(distr_gens) + [Permutation([1, 2, 0]), Permutation([1, 0, 2]), Permutation([0, 2, 1])] - References - ========== + See Also + ======== - [1] Holt, D., Eick, B., O'Brien, E. - "Handbook of computational group theory" + _distribute_gens_by_base """ - base_len = len(base) - ordering = [0]*degree - for i in xrange(base_len): - ordering[base[i]] = i - current = base_len - for i in xrange(degree): - if i not in base: - ordering[i] = current - current += 1 - return ordering + if len(distr_gens) == 1: + return distr_gens[0][:] + else: + result = distr_gens[0] + for gen in distr_gens[1]: + if gen not in result: + result.append(gen) + return result def _verify_bsgs(group, base, gens): """