def cycle_index(self, parent = None): r""" INPUT: - ``self`` - a permutation group `G` - ``parent`` -- a free module with basis indexed by partitions, or behave as such, with a ``term`` and ``sum`` method (default: the symmetric functions over the rational field in the p basis) Returns the *cycle index* of `G`, which is a gadget counting the elements of `G` by cycle type, averaged over the group: .. math:: P = \frac{1}{|G|} \sum_{g\in G} p_{ \operatorname{cycle\ type}(g) } EXAMPLES: Among the permutations of the symmetric group `S_4`, there is the identity, 6 cycles of length 2, 3 products of two cycles of length 2, 8 cycles of length 3, and 6 cycles of length 4:: sage: S4 = SymmetricGroup(4) sage: P = S4.cycle_index() sage: 24 * P p[1, 1, 1, 1] + 6*p[2, 1, 1] + 3*p[2, 2] + 8*p[3, 1] + 6*p[4] If `l = (l_1,\dots,l_k)` is a partition, ``|G| P[l]`` is the number of elements of `G` with cycles of length `(p_1,\dots,p_k)`:: sage: 24 * P[ Partition([3,1]) ] 8 The cycle index plays an important role in the enumeration of objects modulo the action of a group (Polya enumeration), via the use of symmetric functions and plethysms. It is therefore encoded as a symmetric function, expressed in the powersum basis:: sage: P.parent() Symmetric Functions over Rational Field in the powersum basis This symmetric function can have some nice properties; for example, for the symmetric group `S_n`, we get the complete symmetric function `h_n`:: sage: S = SymmetricFunctions(QQ); h = S.h() sage: h( P ) h[4] TODO: add some simple examples of Polya enumeration, once it will be easy to expand symmetric functions on any alphabet. Here are the cycle indices of some permutation groups:: sage: 6 * CyclicPermutationGroup(6).cycle_index() p[1, 1, 1, 1, 1, 1] + p[2, 2, 2] + 2*p[3, 3] + 2*p[6] sage: 60 * AlternatingGroup(5).cycle_index() p[1, 1, 1, 1, 1] + 15*p[2, 2, 1] + 20*p[3, 1, 1] + 24*p[5] sage: for G in TransitiveGroups(5): # optional - database_gap # long time ... G.cardinality() * G.cycle_index() p[1, 1, 1, 1, 1] + 4*p[5] p[1, 1, 1, 1, 1] + 5*p[2, 2, 1] + 4*p[5] p[1, 1, 1, 1, 1] + 5*p[2, 2, 1] + 10*p[4, 1] + 4*p[5] p[1, 1, 1, 1, 1] + 15*p[2, 2, 1] + 20*p[3, 1, 1] + 24*p[5] p[1, 1, 1, 1, 1] + 10*p[2, 1, 1, 1] + 15*p[2, 2, 1] + 20*p[3, 1, 1] + 20*p[3, 2] + 30*p[4, 1] + 24*p[5] One may specify another parent for the result:: sage: F = CombinatorialFreeModule(QQ, Partitions()) sage: P = CyclicPermutationGroup(6).cycle_index(parent = F) sage: 6 * P B[[1, 1, 1, 1, 1, 1]] + B[[2, 2, 2]] + 2*B[[3, 3]] + 2*B[[6]] sage: P.parent() is F True This parent should have a ``term`` and ``sum`` method:: sage: CyclicPermutationGroup(6).cycle_index(parent = QQ) Traceback (most recent call last): ... AssertionError: `parent` should be (or behave as) a free module with basis indexed by partitions REFERENCES: .. [Ker1991] A. Kerber. Algebraic combinatorics via finite group actions, 2.2 p. 70. BI-Wissenschaftsverlag, Mannheim, 1991. AUTHORS: - Nicolas Borie and Nicolas M. Thiery TESTS:: sage: P = PermutationGroup([]); P Permutation Group with generators [()] sage: P.cycle_index() p[1] sage: P = PermutationGroup([[(1)]]); P Permutation Group with generators [()] sage: P.cycle_index() p[1] """ from sage.combinat.permutation import Permutation if parent is None: from sage.rings.rational_field import QQ from sage.combinat.sf.sf import SymmetricFunctions parent = SymmetricFunctions(QQ).powersum() else: assert hasattr(parent, "term") and hasattr(parent, "sum"), \ "`parent` should be (or behave as) a free module with basis indexed by partitions" base_ring = parent.base_ring() # TODO: use self.conjugacy_classes() once available from sage.interfaces.gap import gap CC = ([Permutation(self(C.Representative())).cycle_type(), base_ring(C.Size())] for C in gap(self).ConjugacyClasses()) return parent.sum( parent.term( partition, coeff ) for (partition, coeff) in CC)/self.cardinality()
def cycle_index(self, parent=None): r""" Return the *cycle index* of ``self``. INPUT: - ``self`` - a permutation group `G` - ``parent`` -- a free module with basis indexed by partitions, or behave as such, with a ``term`` and ``sum`` method (default: the symmetric functions over the rational field in the `p` basis) The *cycle index* of a permutation group `G` (:wikipedia:`Cycle_index`) is a gadget counting the elements of `G` by cycle type, averaged over the group: .. MATH:: P = \frac{1}{|G|} \sum_{g\in G} p_{ \operatorname{cycle\ type}(g) } EXAMPLES: Among the permutations of the symmetric group `S_4`, there is the identity, 6 cycles of length 2, 3 products of two cycles of length 2, 8 cycles of length 3, and 6 cycles of length 4:: sage: S4 = SymmetricGroup(4) sage: P = S4.cycle_index() sage: 24 * P p[1, 1, 1, 1] + 6*p[2, 1, 1] + 3*p[2, 2] + 8*p[3, 1] + 6*p[4] If `l = (l_1,\dots,l_k)` is a partition, ``|G| P[l]`` is the number of elements of `G` with cycles of length `(p_1,\dots,p_k)`:: sage: 24 * P[ Partition([3,1]) ] 8 The cycle index plays an important role in the enumeration of objects modulo the action of a group (Pólya enumeration), via the use of symmetric functions and plethysms. It is therefore encoded as a symmetric function, expressed in the powersum basis:: sage: P.parent() Symmetric Functions over Rational Field in the powersum basis This symmetric function can have some nice properties; for example, for the symmetric group `S_n`, we get the complete symmetric function `h_n`:: sage: S = SymmetricFunctions(QQ); h = S.h() sage: h( P ) h[4] .. TODO:: Add some simple examples of Pólya enumeration, once it will be easy to expand symmetric functions on any alphabet. Here are the cycle indices of some permutation groups:: sage: 6 * CyclicPermutationGroup(6).cycle_index() p[1, 1, 1, 1, 1, 1] + p[2, 2, 2] + 2*p[3, 3] + 2*p[6] sage: 60 * AlternatingGroup(5).cycle_index() p[1, 1, 1, 1, 1] + 15*p[2, 2, 1] + 20*p[3, 1, 1] + 24*p[5] sage: for G in TransitiveGroups(5): # long time ....: G.cardinality() * G.cycle_index() p[1, 1, 1, 1, 1] + 4*p[5] p[1, 1, 1, 1, 1] + 5*p[2, 2, 1] + 4*p[5] p[1, 1, 1, 1, 1] + 5*p[2, 2, 1] + 10*p[4, 1] + 4*p[5] p[1, 1, 1, 1, 1] + 15*p[2, 2, 1] + 20*p[3, 1, 1] + 24*p[5] p[1, 1, 1, 1, 1] + 10*p[2, 1, 1, 1] + 15*p[2, 2, 1] + 20*p[3, 1, 1] + 20*p[3, 2] + 30*p[4, 1] + 24*p[5] Permutation groups with arbitrary domains are supported (see :trac:`22765`):: sage: G = PermutationGroup([['b','c','a']], domain=['a','b','c']) sage: G.cycle_index() 1/3*p[1, 1, 1] + 2/3*p[3] One may specify another parent for the result:: sage: F = CombinatorialFreeModule(QQ, Partitions()) sage: P = CyclicPermutationGroup(6).cycle_index(parent = F) sage: 6 * P B[[1, 1, 1, 1, 1, 1]] + B[[2, 2, 2]] + 2*B[[3, 3]] + 2*B[[6]] sage: P.parent() is F True This parent should be a module with basis indexed by partitions:: sage: CyclicPermutationGroup(6).cycle_index(parent = QQ) Traceback (most recent call last): ... ValueError: `parent` should be a module with basis indexed by partitions REFERENCES: - [Ke1991]_ AUTHORS: - Nicolas Borie and Nicolas M. Thiéry TESTS:: sage: P = PermutationGroup([]); P Permutation Group with generators [()] sage: P.cycle_index() p[1] sage: P = PermutationGroup([[(1)]]); P Permutation Group with generators [()] sage: P.cycle_index() p[1] """ from sage.categories.modules import Modules if parent is None: from sage.rings.rational_field import QQ from sage.combinat.sf.sf import SymmetricFunctions parent = SymmetricFunctions(QQ).powersum() elif not parent in Modules.WithBasis: raise ValueError( "`parent` should be a module with basis indexed by partitions" ) base_ring = parent.base_ring() return parent.sum_of_terms( [C.an_element().cycle_type(), base_ring(C.cardinality())] for C in self.conjugacy_classes()) / self.cardinality()
def cycle_index(self, parent = None): r""" INPUT: - ``self`` - a permutation group `G` - ``parent`` -- a free module with basis indexed by partitions, or behave as such, with a ``term`` and ``sum`` method (default: the symmetric functions over the rational field in the p basis) Returns the *cycle index* of `G`, which is a gadget counting the elements of `G` by cycle type, averaged over the group: .. math:: P = \frac{1}{|G|} \sum_{g\in G} p_{ \operatorname{cycle\ type}(g) } EXAMPLES: Among the permutations of the symmetric group `S_4`, there is the identity, 6 cycles of length 2, 3 products of two cycles of length 2, 8 cycles of length 3, and 6 cycles of length 4:: sage: S4 = SymmetricGroup(4) sage: P = S4.cycle_index() sage: 24 * P p[1, 1, 1, 1] + 6*p[2, 1, 1] + 3*p[2, 2] + 8*p[3, 1] + 6*p[4] If `l = (l_1,\dots,l_k)` is a partition, ``|G| P[l]`` is the number of elements of `G` with cycles of length `(p_1,\dots,p_k)`:: sage: 24 * P[ Partition([3,1]) ] 8 The cycle index plays an important role in the enumeration of objects modulo the action of a group (Polya enumeration), via the use of symmetric functions and plethysms. It is therefore encoded as a symmetric function, expressed in the powersum basis:: sage: P.parent() Symmetric Functions over Rational Field in the powersum basis This symmetric function can have some nice properties; for example, for the symmetric group `S_n`, we get the complete symmetric function `h_n`:: sage: S = SymmetricFunctions(QQ); h = S.h() sage: h( P ) h[4] TODO: add some simple examples of Polya enumeration, once it will be easy to expand symmetric functions on any alphabet. Here are the cycle indices of some permutation groups:: sage: 6 * CyclicPermutationGroup(6).cycle_index() p[1, 1, 1, 1, 1, 1] + p[2, 2, 2] + 2*p[3, 3] + 2*p[6] sage: 60 * AlternatingGroup(5).cycle_index() p[1, 1, 1, 1, 1] + 15*p[2, 2, 1] + 20*p[3, 1, 1] + 24*p[5] sage: for G in TransitiveGroups(5): # optional - database_gap # long time ... G.cardinality() * G.cycle_index() p[1, 1, 1, 1, 1] + 4*p[5] p[1, 1, 1, 1, 1] + 5*p[2, 2, 1] + 4*p[5] p[1, 1, 1, 1, 1] + 5*p[2, 2, 1] + 10*p[4, 1] + 4*p[5] p[1, 1, 1, 1, 1] + 15*p[2, 2, 1] + 20*p[3, 1, 1] + 24*p[5] p[1, 1, 1, 1, 1] + 10*p[2, 1, 1, 1] + 15*p[2, 2, 1] + 20*p[3, 1, 1] + 20*p[3, 2] + 30*p[4, 1] + 24*p[5] One may specify another parent for the result:: sage: F = CombinatorialFreeModule(QQ, Partitions()) sage: P = CyclicPermutationGroup(6).cycle_index(parent = F) sage: 6 * P B[[1, 1, 1, 1, 1, 1]] + B[[2, 2, 2]] + 2*B[[3, 3]] + 2*B[[6]] sage: P.parent() is F True This parent should have a ``term`` and ``sum`` method:: sage: CyclicPermutationGroup(6).cycle_index(parent = QQ) Traceback (most recent call last): ... AssertionError: `parent` should be (or behave as) a free module with basis indexed by partitions REFERENCES: .. [Ker1991] A. Kerber. Algebraic combinatorics via finite group actions, 2.2 p. 70. BI-Wissenschaftsverlag, Mannheim, 1991. AUTHORS: - Nicolas Borie and Nicolas M. Thiery TESTS:: sage: P = PermutationGroup([]); P Permutation Group with generators [()] sage: P.cycle_index() p[1] sage: P = PermutationGroup([[(1)]]); P Permutation Group with generators [()] sage: P.cycle_index() p[1] """ from sage.combinat.permutation import Permutation if parent is None: from sage.rings.rational_field import QQ from sage.combinat.sf.sf import SymmetricFunctions parent = SymmetricFunctions(QQ).powersum() else: assert hasattr(parent, "term") and hasattr(parent, "sum"), \ "`parent` should be (or behave as) a free module with basis indexed by partitions" base_ring = parent.base_ring() # TODO: use self.conjugacy_classes() once available from sage.interfaces.gap import gap CC = ([Permutation(self(C.Representative())).cycle_type(), base_ring(C.Size())] for C in gap(self).ConjugacyClasses()) return parent.sum( parent.term( partition, coeff ) for (partition, coeff) in CC)/self.cardinality()
def cycle_index(self, parent = None): r""" Return the *cycle index* of ``self``. INPUT: - ``self`` - a permutation group `G` - ``parent`` -- a free module with basis indexed by partitions, or behave as such, with a ``term`` and ``sum`` method (default: the symmetric functions over the rational field in the `p` basis) The *cycle index* of a permutation group `G` (:wikipedia:`Cycle_index`) is a gadget counting the elements of `G` by cycle type, averaged over the group: .. MATH:: P = \frac{1}{|G|} \sum_{g\in G} p_{ \operatorname{cycle\ type}(g) } EXAMPLES: Among the permutations of the symmetric group `S_4`, there is the identity, 6 cycles of length 2, 3 products of two cycles of length 2, 8 cycles of length 3, and 6 cycles of length 4:: sage: S4 = SymmetricGroup(4) sage: P = S4.cycle_index() sage: 24 * P p[1, 1, 1, 1] + 6*p[2, 1, 1] + 3*p[2, 2] + 8*p[3, 1] + 6*p[4] If `l = (l_1,\dots,l_k)` is a partition, ``|G| P[l]`` is the number of elements of `G` with cycles of length `(p_1,\dots,p_k)`:: sage: 24 * P[ Partition([3,1]) ] 8 The cycle index plays an important role in the enumeration of objects modulo the action of a group (Pólya enumeration), via the use of symmetric functions and plethysms. It is therefore encoded as a symmetric function, expressed in the powersum basis:: sage: P.parent() Symmetric Functions over Rational Field in the powersum basis This symmetric function can have some nice properties; for example, for the symmetric group `S_n`, we get the complete symmetric function `h_n`:: sage: S = SymmetricFunctions(QQ); h = S.h() sage: h( P ) h[4] .. TODO:: Add some simple examples of Pólya enumeration, once it will be easy to expand symmetric functions on any alphabet. Here are the cycle indices of some permutation groups:: sage: 6 * CyclicPermutationGroup(6).cycle_index() p[1, 1, 1, 1, 1, 1] + p[2, 2, 2] + 2*p[3, 3] + 2*p[6] sage: 60 * AlternatingGroup(5).cycle_index() p[1, 1, 1, 1, 1] + 15*p[2, 2, 1] + 20*p[3, 1, 1] + 24*p[5] sage: for G in TransitiveGroups(5): # optional - database_gap # long time ....: G.cardinality() * G.cycle_index() p[1, 1, 1, 1, 1] + 4*p[5] p[1, 1, 1, 1, 1] + 5*p[2, 2, 1] + 4*p[5] p[1, 1, 1, 1, 1] + 5*p[2, 2, 1] + 10*p[4, 1] + 4*p[5] p[1, 1, 1, 1, 1] + 15*p[2, 2, 1] + 20*p[3, 1, 1] + 24*p[5] p[1, 1, 1, 1, 1] + 10*p[2, 1, 1, 1] + 15*p[2, 2, 1] + 20*p[3, 1, 1] + 20*p[3, 2] + 30*p[4, 1] + 24*p[5] Permutation groups with arbitrary domains are supported (see :trac:`22765`):: sage: G = PermutationGroup([['b','c','a']], domain=['a','b','c']) sage: G.cycle_index() 1/3*p[1, 1, 1] + 2/3*p[3] One may specify another parent for the result:: sage: F = CombinatorialFreeModule(QQ, Partitions()) sage: P = CyclicPermutationGroup(6).cycle_index(parent = F) sage: 6 * P B[[1, 1, 1, 1, 1, 1]] + B[[2, 2, 2]] + 2*B[[3, 3]] + 2*B[[6]] sage: P.parent() is F True This parent should be a module with basis indexed by partitions:: sage: CyclicPermutationGroup(6).cycle_index(parent = QQ) Traceback (most recent call last): ... ValueError: `parent` should be a module with basis indexed by partitions REFERENCES: - [Ke1991]_ AUTHORS: - Nicolas Borie and Nicolas M. Thiéry TESTS:: sage: P = PermutationGroup([]); P Permutation Group with generators [()] sage: P.cycle_index() p[1] sage: P = PermutationGroup([[(1)]]); P Permutation Group with generators [()] sage: P.cycle_index() p[1] """ from sage.categories.modules import Modules if parent is None: from sage.rings.rational_field import QQ from sage.combinat.sf.sf import SymmetricFunctions parent = SymmetricFunctions(QQ).powersum() elif not parent in Modules.WithBasis: raise ValueError("`parent` should be a module with basis indexed by partitions") base_ring = parent.base_ring() return parent.sum_of_terms([C.an_element().cycle_type(), base_ring(C.cardinality())] for C in self.conjugacy_classes() ) / self.cardinality()