def __init__(self, m, n): """ Initialize ``self``. EXAMPLES:: sage: C = ColoredPermutations(4, 3) sage: TestSuite(C).run() sage: C = ColoredPermutations(2, 3) sage: TestSuite(C).run() sage: C = ColoredPermutations(1, 3) sage: TestSuite(C).run() """ if m <= 0: raise ValueError("m must be a positive integer") self._m = ZZ(m) self._n = ZZ(n) self._C = IntegerModRing(self._m) self._P = Permutations(self._n) if self._m == 1 or self._m == 2: from sage.categories.finite_coxeter_groups import FiniteCoxeterGroups category = FiniteCoxeterGroups().Irreducible() else: from sage.categories.complex_reflection_groups import ComplexReflectionGroups category = ComplexReflectionGroups().Finite().Irreducible( ).WellGenerated() Parent.__init__(self, category=category)
def SymmetricGroupWeakOrderPoset(n, labels="permutations"): """ The poset of permutations with respect to weak order. EXAMPLES:: sage: Posets.SymmetricGroupWeakOrderPoset(4) Finite poset containing 24 elements """ if n < 10 and labels == "permutations": element_labels = dict([[s, "".join(map(str, s))] for s in Permutations(n)]) if n < 10 and labels == "reduced_words": element_labels = dict( [[s, "".join(map(str, s.reduced_word_lexmin()))] for s in Permutations(n)]) def weak_covers(s): r""" Nested function for computing the covers of elements in the poset of weak order for the symmetric group. """ return [ v for v in s.bruhat_succ() if s.length() + (s.inverse() * v).length() == v.length() ] return Poset(dict([[s, weak_covers(s)] for s in Permutations(n)]), element_labels)
def cross_polytope(self, dim_n): """ Return a cross-polytope in dimension ``dim_n``. These are the generalization of the octahedron. INPUT: - ``dim_n`` -- integer. The dimension of the cross-polytope. OUTPUT: A Polyhedron object of the ``dim_n``-dimensional cross-polytope, with exact coordinates. EXAMPLES:: sage: four_cross = polytopes.cross_polytope(4) sage: four_cross.is_simple() False sage: four_cross.n_vertices() 8 """ verts = Permutations([0 for i in range(dim_n - 1)] + [1]).list() verts += Permutations([0 for i in range(dim_n - 1)] + [-1]).list() return Polyhedron(vertices=verts)
def six_hundred_cell(self): """ Return the standard 600-cell polytope. OUTPUT: A Polyhedron object of the 4-dimensional 600-cell, a regular polytope. In many ways this is an analogue of the icosahedron. The coordinates of this polytope are rational approximations of the true coordinates of the 600-cell, some of which involve the (irrational) golden ratio. EXAMPLES:: sage: p600 = polytopes.six_hundred_cell() # not tested - very long time sage: len(list(p600.bounded_edges())) # not tested - very long time 120 """ verts = [] q12 = QQ(1) / 2 base = [q12, q12, q12, q12] for i in range(2): for j in range(2): for k in range(2): for l in range(2): verts.append([x for x in base]) base[3] = base[3] * (-1) base[2] = base[2] * (-1) base[1] = base[1] * (-1) base[0] = base[0] * (-1) verts += Permutations([0, 0, 0, 1]).list() verts += Permutations([0, 0, 0, -1]).list() g = QQ(1618033) / 1000000 # Golden ratio approximation verts = verts + [ i([q12, g / 2, 1 / (g * 2), 0]) for i in AlternatingGroup(4) ] verts = verts + [ i([q12, g / 2, -1 / (g * 2), 0]) for i in AlternatingGroup(4) ] verts = verts + [ i([q12, -g / 2, 1 / (g * 2), 0]) for i in AlternatingGroup(4) ] verts = verts + [ i([q12, -g / 2, -1 / (g * 2), 0]) for i in AlternatingGroup(4) ] verts = verts + [ i([-q12, g / 2, 1 / (g * 2), 0]) for i in AlternatingGroup(4) ] verts = verts + [ i([-q12, g / 2, -1 / (g * 2), 0]) for i in AlternatingGroup(4) ] verts = verts + [ i([-q12, -g / 2, 1 / (g * 2), 0]) for i in AlternatingGroup(4) ] verts = verts + [ i([-q12, -g / 2, -1 / (g * 2), 0]) for i in AlternatingGroup(4) ] return Polyhedron(vertices=verts)
def __iter__(self): r""" Efficient generation of Baxter permutations. OUTPUT: An iterator over the Baxter permutations of size ``self._n``. EXAMPLES:: sage: BaxterPermutations(4).list() [[4, 3, 2, 1], [3, 4, 2, 1], [3, 2, 4, 1], [3, 2, 1, 4], [2, 4, 3, 1], [4, 2, 3, 1], [2, 3, 4, 1], [2, 3, 1, 4], [2, 1, 4, 3], [4, 2, 1, 3], [2, 1, 3, 4], [1, 4, 3, 2], [4, 1, 3, 2], [1, 3, 4, 2], [1, 3, 2, 4], [4, 3, 1, 2], [3, 4, 1, 2], [3, 1, 2, 4], [1, 2, 4, 3], [1, 4, 2, 3], [4, 1, 2, 3], [1, 2, 3, 4]] sage: [len(BaxterPermutations(n)) for n in xrange(9)] [1, 1, 2, 6, 22, 92, 422, 2074, 10754] TESTS:: sage: all(a in BaxterPermutations(n) for n in xrange(7) ....: for a in BaxterPermutations(n)) True ALGORITHM: The algorithm using generating trees described in [BBF08]_ is used. The idea is that all Baxter permutations of size `n + 1` can be obtained by inserting the letter `n + 1` either just before a left to right maximum or just after a right to left maximum of a Baxter permutation of size `n`. REFERENCES: .. [BBF08] N. Bonichon, M. Bousquet-Melou, E. Fusy. Baxter permutations and plane bipolar orientations. Seminaire Lotharingien de combinatoire 61A, article B61Ah, 2008. """ if self._n == 0: yield Permutations(0)([]) elif self._n == 1: yield Permutations(1)([1]) else: for b in BaxterPermutations(self._n - 1): # Left to right maxima. for i in [self._n - 2 - i for i in b.reverse().saliances()]: yield Permutations(self._n)(b[:i] + [self._n] + b[i:]) # Right to left maxima. for i in b.saliances(): yield Permutations(self._n)(b[:i + 1] + [self._n] + b[i + 1:])
def SymmetricGroupBruhatOrderPoset(n): """ The poset of permutations with respect to Bruhat order. EXAMPLES:: sage: Posets.SymmetricGroupBruhatOrderPoset(4) Finite poset containing 24 elements """ if n < 10: element_labels = dict([[s,"".join(map(str,s))] for s in Permutations(n)]) return Poset(dict([[s,s.bruhat_succ()] for s in Permutations(n)]),element_labels)
def __init__(self, R): """ EXAMPLES:: sage: X = SchubertPolynomialRing(QQ) sage: X == loads(dumps(X)) True """ self._name = "Schubert polynomial ring with X basis" self._repr_option_bracket = False self._one = Permutations()([1]) CombinatorialAlgebra.__init__(self, R, cc = Permutations(), category = GradedAlgebrasWithBasis(R)) self.print_options(prefix='X')
def SymmetricGroupWeakOrderPoset(n, labels="permutations", side="right"): r""" The poset of permutations of `\{ 1, 2, \ldots, n \}` with respect to the weak order (also known as the permutohedron order, cf. :meth:`~sage.combinat.permutation.Permutation.permutohedron_lequal`). The optional variable ``labels`` (default: ``"permutations"``) determines the labelling of the elements if `n < 10`. The optional variable ``side`` (default: ``"right"``) determines whether the right or the left permutohedron order is to be used. EXAMPLES:: sage: Posets.SymmetricGroupWeakOrderPoset(4) Finite poset containing 24 elements """ if n < 10 and labels == "permutations": element_labels = dict([[s, "".join(map(str, s))] for s in Permutations(n)]) if n < 10 and labels == "reduced_words": element_labels = dict( [[s, "".join(map(str, s.reduced_word_lexmin()))] for s in Permutations(n)]) if side == "left": def weak_covers(s): r""" Nested function for computing the covers of elements in the poset of left weak order for the symmetric group. """ return [ v for v in s.bruhat_succ() if s.length() + (s.inverse().right_action_product(v)).length() == v.length() ] else: def weak_covers(s): r""" Nested function for computing the covers of elements in the poset of right weak order for the symmetric group. """ return [ v for v in s.bruhat_succ() if s.length() + (s.inverse().left_action_product(v)).length() == v.length() ] return Poset(dict([[s, weak_covers(s)] for s in Permutations(n)]), element_labels)
def _generate_flags_slow(self): sys.stdout.write("Generating flags (SLOW VERSION).\n") sys.stdout.flush() flags = list() sys.stdout.write("Generating flags... ") for t in self.types: t_flags = list() n = int((self.N-t.N)/2 +t.N) # must be casted as int allperms = [PermFlag(x) for x in Permutations(n)] S = Subsets(range(n),t.N) for p in allperms: # check each permutation for s in S: # and all possible placings of type in it subp = PermFlag(normalize([p.perm[i-1] for i in s], t.N)) if subp == t: t_flags.append(PermFlag(p.perm, list(s))) flags.append(t_flags) sys.stdout.write("\033[32mOK\033[m.\n") sys.stdout.flush() return flags
def _generate_flags(self): """Return list of lists of admissible flags for each type (one list of flags for each type).""" flags = list() for t in self.types: t_flags = list() # list of flags on type t tperm = t.perm m = int((self.N+t.N)/2) # must be casted as int rm = range(m) srm = Set(rm) petals = Permutations(m-t.N) subsets = Subsets(range(m), t.N) # will serve as values and positions cosubsets = [srm-s for s in subsets] for petal in petals: # now type and petal are both fixed for s in range(len(subsets)): positions = subsets[s] copositions = cosubsets[s] for v in range(len(subsets)): values = subsets[v] covalues = cosubsets[v] newflag = range(m) for i in range(t.N): newflag[positions[i]] = values[tperm[i]-1]+1 for j in range(m-t.N): newflag[copositions[j]] = covalues[petal[j]-1]+1 if self._is_admissible(PermFlag(newflag)): t_flags.append(PermFlag(newflag,list(subsets[s]))) flags.append(t_flags) return flags
def random_element(self): r""" Returns a random element of self. EXAMPLES:: sage: M = PerfectMatchings(('a', 'e', 'b', 'f', 'c', 'd')) sage: M.an_element() [('a', 'b'), ('f', 'e'), ('c', 'd')] sage: all([PerfectMatchings(2*i).an_element() in PerfectMatchings(2*i) ... for i in range(2,11,2)]) True TESTS:: sage: p = PerfectMatchings(13).random_element() Traceback (most recent call last): ... ValueError: there is no perfect matching on an odd number of elements """ n = len(self._objects) if n % 2 == 1: raise ValueError("there is no perfect matching on an odd number of elements") k = n//2 from sage.combinat.permutation import Permutations p = Permutations(n).random_element() return self([(self._objects[p[2*i]-1], self._objects[p[2*i+1]-1]) for i in range(k)])
def sum_of_partition_rearrangements(self, par): """ Return the sum of all basis elements indexed by compositions which can be sorted to obtain a given partition. INPUT: - ``par`` -- a partition OUTPUT: - The sum of all ``self`` basis elements indexed by compositions which are permutations of ``par`` (without multiplicity). EXAMPLES:: sage: NCSF=NonCommutativeSymmetricFunctions(QQ) sage: elementary = NCSF.elementary() sage: elementary.sum_of_partition_rearrangements(Partition([2,2,1])) L[1, 2, 2] + L[2, 1, 2] + L[2, 2, 1] sage: elementary.sum_of_partition_rearrangements(Partition([3,2,1])) L[1, 2, 3] + L[1, 3, 2] + L[2, 1, 3] + L[2, 3, 1] + L[3, 1, 2] + L[3, 2, 1] sage: elementary.sum_of_partition_rearrangements(Partition([])) L[] """ return self.sum_of_monomials( self._basis_keys(comp) for comp in Permutations(par))
def shard_poset(n): """ Return the shard intersection order on permutations of size `n`. This is defined on the set of permutations. To every permutation, one can attach a pre-order, using the descending runs and their relative positions. The shard intersection order is given by the implication (or refinement) order on the set of pre-orders defined from all permutations. This can also be seen in a geometrical way. Every pre-order defines a cone in a vector space of dimension `n`. The shard poset is given by the inclusion of these cones. .. SEEALSO:: :func:`shard_preorder_graph` EXAMPLES:: sage: P = posets.ShardPoset(4); P # indirect doctest Finite poset containing 24 elements sage: P.chain_polynomial() 34*q^4 + 90*q^3 + 79*q^2 + 24*q + 1 sage: P.characteristic_polynomial() q^3 - 11*q^2 + 23*q - 13 sage: P.zeta_polynomial() 17/3*q^3 - 6*q^2 + 4/3*q sage: P.is_selfdual() False """ import operator Sn = [ShardPosetElement(s) for s in Permutations(n)] return Poset([Sn, operator.le], cover_relations=False, facade=True)
def n_simplex(self, dim_n=3, project=True): """ Return a rational approximation to a regular simplex in dimension ``dim_n``. INPUT: - ``dim_n`` -- The dimension of the simplex, a positive integer. - ``project`` -- Optional argument, whether to project orthogonally. Default is True. OUTPUT: A Polyhedron object of the ``dim_n``-dimensional simplex. EXAMPLES:: sage: s5 = polytopes.n_simplex(5) sage: s5.dim() 5 """ verts = Permutations([0 for i in range(dim_n)] + [1]).list() if project: verts = [Polytopes.project_1(x) for x in verts] return Polyhedron(vertices=verts)
def quantum_determinant(self, u=None): r""" Return the quantum determinant of ``self``. The quantum determinant is defined by: .. MATH:: \operatorname{qdet}(u) = \sum_{\sigma \in S_n} (-1)^{\sigma} \prod_{k=1}^n T_{\sigma(k),k}(u - k + 1). EXAMPLES:: sage: Y = Yangian(QQ, 2, 2) sage: Y.quantum_determinant() u^4 + (-2 + t(1)[1,1] + t(1)[2,2])*u^3 + (1 - t(1)[1,1] + t(1)[1,1]*t(1)[2,2] - t(1)[1,2]*t(1)[2,1] - 2*t(1)[2,2] + t(2)[1,1] + t(2)[2,2])*u^2 + (-t(1)[1,1]*t(1)[2,2] + t(1)[1,1]*t(2)[2,2] + t(1)[1,2]*t(1)[2,1] - t(1)[1,2]*t(2)[2,1] - t(1)[2,1]*t(2)[1,2] + t(1)[2,2] + t(1)[2,2]*t(2)[1,1] - t(2)[1,1] - t(2)[2,2])*u - t(1)[1,1]*t(2)[2,2] + t(1)[1,2]*t(2)[2,1] + t(2)[1,1]*t(2)[2,2] - t(2)[1,2]*t(2)[2,1] + t(2)[2,2] """ if u is None: u = PolynomialRing(self.base_ring(), 'u').gen(0) from sage.combinat.permutation import Permutations n = self._n return sum(p.sign() * prod( self.defining_polynomial(p[k], k + 1, u - k) for k in range(n)) for p in Permutations(n))
def random_element(self): r""" Return a random element of ``self``. EXAMPLES:: sage: M = PerfectMatchings(('a', 'e', 'b', 'f', 'c', 'd')) sage: M.random_element() [('a', 'b'), ('c', 'd'), ('e', 'f')] TESTS:: sage: p = PerfectMatchings(13).random_element() Traceback (most recent call last): ... ValueError: there is no perfect matching on an odd number of elements """ n = len(self._set) if n % 2 == 1: raise ValueError( "there is no perfect matching on an odd number of elements") k = n // 2 p = Permutations(n).random_element() l = list(self._set) return self.element_class(self, [(l[p[2 * i] - 1], l[p[2 * i + 1] - 1]) for i in range(k)], check=False)
def permutahedron(self, n, project=True): """ The standard permutahedron of (1,...,n) projected into n-1 dimensions. INPUT: - ``n`` -- the numbers ``(1,...,n)`` are permuted - ``project`` -- If ``False`` the polyhedron is left in dimension ``n``. OUTPUT: A Polyhedron object representing the permutahedron. EXAMPLES:: sage: perm4 = polytopes.permutahedron(4) sage: perm4 A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 24 vertices sage: polytopes.permutahedron(5).show() # long time Graphics3d Object """ verts = range(1, n + 1) verts = Permutations(verts).list() if project: verts = [Polytopes.project_1(x) for x in verts] p = Polyhedron(vertices=verts) return p
def hypersimplex(self, dim_n, k, project=True): """ The hypersimplex in dimension dim_n with d choose k vertices, projected into (dim_n - 1) dimensions. INPUT: - ``n`` -- the numbers ``(1,...,n)`` are permuted - ``project`` -- If ``False``, the polyhedron is left in dimension ``n``. OUTPUT: A Polyhedron object representing the hypersimplex. EXAMPLES:: sage: h_4_2 = polytopes.hypersimplex(4,2) # combinatorially equivalent to octahedron sage: h_4_2.n_vertices() 6 sage: h_4_2.n_inequalities() 8 """ vert0 = [0] * (dim_n - k) + [1] * k verts = Permutations(vert0).list() if project: verts = [Polytopes.project_1(x) for x in verts] return Polyhedron(vertices=verts)
def split_polyhedra(dim): r""" :: sage: from partitioner import split_polyhedra, repr_pretty_Hrepresentation sage: for P in split_polyhedra(2): ....: print(repr_pretty_Hrepresentation(P, strict_inequality=True, ....: prefix='s')) s1 > s0 s0 >= s1 sage: for P in split_polyhedra(3): ....: print(repr_pretty_Hrepresentation(P, strict_inequality=True, ....: prefix='s')) s1 >= s0, s2 > s1 s2 > s0, s1 >= s2 s2 > s0, s0 > s1 s2 > s1, s0 >= s2 s1 >= s0, s0 >= s2 s1 >= s2, s0 > s1 sage: for P in split_polyhedra(4): ....: print(repr_pretty_Hrepresentation(P, strict_inequality=True, ....: prefix='s')) s1 >= s0, s2 > s1, s3 > s2 s1 >= s0, s3 > s1, s2 >= s3 s2 >= s0, s3 > s1, s1 >= s2 s2 >= s0, s3 > s2, s1 >= s3 s3 > s0, s2 > s1, s1 >= s3 s3 > s0, s2 >= s3, s1 >= s2 s2 >= s0, s3 > s2, s0 > s1 s3 > s0, s2 >= s3, s0 > s1 s3 > s0, s2 > s1, s0 > s2 s2 > s1, s3 > s2, s0 >= s3 s2 >= s0, s3 > s1, s0 >= s3 s3 > s1, s2 >= s3, s0 > s2 s1 >= s0, s3 > s1, s0 > s2 s3 > s0, s1 >= s3, s0 > s2 s3 > s0, s1 >= s2, s0 > s1 s3 > s1, s1 >= s2, s0 >= s3 s1 >= s0, s3 > s2, s0 >= s3 s3 > s2, s1 >= s3, s0 > s1 s1 >= s0, s2 > s1, s0 >= s3 s2 >= s0, s1 >= s2, s0 >= s3 s2 >= s0, s1 >= s3, s0 > s1 s2 > s1, s1 >= s3, s0 > s2 s1 >= s0, s2 >= s3, s0 > s2 s2 >= s3, s1 >= s2, s0 > s1 """ from sage.combinat.permutation import Permutations from sage.geometry.polyhedron.constructor import Polyhedron return iter( polyhedron_break_tie( Polyhedron( ieqs=[tuple(1 if i==b else (-1 if i==a else 0) for i in range(dim+1)) for a, b in zip(pi[:-1], pi[1:])])) for pi in Permutations(dim))
def __iter__(self): """ Return an iterator for parking functions of size `n`. .. warning:: The precise order in which the parking function are generated is not fixed, and may change in the future. EXAMPLES:: sage: PF = ParkingFunctions(0) sage: [e for e in PF] # indirect doctest [[]] sage: PF = ParkingFunctions(1) sage: [e for e in PF] # indirect doctest [[1]] sage: PF = ParkingFunctions(2) sage: [e for e in PF] # indirect doctest [[1, 1], [1, 2], [2, 1]] sage: PF = ParkingFunctions(3) sage: [e for e in PF] # indirect doctest [[1, 1, 1], [1, 1, 2], [1, 2, 1], [2, 1, 1], [1, 1, 3], [1, 3, 1], [3, 1, 1], [1, 2, 2], [2, 1, 2], [2, 2, 1], [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] TESTS:: sage: PF = ParkingFunctions(5) sage: [e for e in PF] == PF.list() True sage: PF = ParkingFunctions(6) sage: [e for e in PF] == PF.list() True """ def iterator_rec(n): """ TESTS:: sage: PF = ParkingFunctions(2) sage: [e for e in PF] # indirect doctest [[1, 1], [1, 2], [2, 1]] """ if n == 0: yield [] return if n == 1: yield [1] return for res1 in iterator_rec(n - 1): for i in range(res1[-1], n + 1): res = copy(res1) res.append(i) yield res return for res in iterator_rec(self.n): for pi in Permutations(res): yield ParkingFunction(list(pi)) return
def PermutationsNK(n, k): r""" This is deprecated in :trac:`16472`. Use :class:`Permutations` instead (or ``itertools.permutations`` for iteration). EXAMPLES:: sage: from sage.combinat.permutation_nk import PermutationsNK sage: P = PermutationsNK(10,4) doctest:...: DeprecationWarning: PermutationsNK is deprecated. Please use Permutations instead (or itertools.permutations for iteration). See http://trac.sagemath.org/16472 for details. sage: [ p for p in PermutationsNK(3,2)] [[0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1]] sage: PermutationsNK(3,2).cardinality() 6 sage: PermutationsNK(5,4).cardinality() 120 """ from sage.misc.superseded import deprecation deprecation( 16472, "PermutationsNK is deprecated. Please use Permutations instead (or itertools.permutations for iteration)." ) from sage.combinat.permutation import Permutations return Permutations(range(n), k)
def to_symmetric_group_algebra_on_basis(self, S): """ Return `D_S` as a linear combination of basis elements in the symmetric group algebra. EXAMPLES:: sage: D = DescentAlgebra(QQ, 4).D() sage: [D.to_symmetric_group_algebra_on_basis(tuple(b)) ....: for b in Subsets(3)] [[1, 2, 3, 4], [2, 1, 3, 4] + [3, 1, 2, 4] + [4, 1, 2, 3], [1, 3, 2, 4] + [1, 4, 2, 3] + [2, 3, 1, 4] + [2, 4, 1, 3] + [3, 4, 1, 2], [1, 2, 4, 3] + [1, 3, 4, 2] + [2, 3, 4, 1], [3, 2, 1, 4] + [4, 2, 1, 3] + [4, 3, 1, 2], [2, 1, 4, 3] + [3, 1, 4, 2] + [3, 2, 4, 1] + [4, 1, 3, 2] + [4, 2, 3, 1], [1, 4, 3, 2] + [2, 4, 3, 1] + [3, 4, 2, 1], [4, 3, 2, 1]] """ n = self.realization_of()._n SGA = SymmetricGroupAlgebra(self.base_ring(), n) # Need to convert S to a list of positions by -1 for indexing P = Permutations(descents=([x - 1 for x in S], n)) return SGA.sum_of_terms([(p, 1) for p in P])
def __init__(self, m, n): """ Initialize ``self``. EXAMPLES:: sage: C = ColoredPermutations(4, 3) sage: TestSuite(C).run() sage: C = ColoredPermutations(2, 3) sage: TestSuite(C).run() sage: C = ColoredPermutations(1, 3) sage: TestSuite(C).run() """ if m <= 0: raise ValueError("m must be a positive integer") self._m = ZZ(m) self._n = ZZ(n) self._C = IntegerModRing(self._m) self._P = Permutations(self._n) if self._m == 1 or self._m == 2: from sage.categories.finite_coxeter_groups import FiniteCoxeterGroups category = FiniteCoxeterGroups().Irreducible() else: from sage.categories.complex_reflection_groups import ComplexReflectionGroups category = ComplexReflectionGroups().Finite().Irreducible().WellGenerated() Parent.__init__(self, category=category)
def __iter__(self): r""" Efficient generation of Baxter permutations. OUTPUT: An iterator over the Baxter permutations of size ``self._n``. EXAMPLES:: sage: BaxterPermutations(4).list() [[4, 3, 2, 1], [3, 4, 2, 1], [3, 2, 4, 1], [3, 2, 1, 4], [2, 4, 3, 1], [4, 2, 3, 1], [2, 3, 4, 1], [2, 3, 1, 4], [2, 1, 4, 3], [4, 2, 1, 3], [2, 1, 3, 4], [1, 4, 3, 2], [4, 1, 3, 2], [1, 3, 4, 2], [1, 3, 2, 4], [4, 3, 1, 2], [3, 4, 1, 2], [3, 1, 2, 4], [1, 2, 4, 3], [1, 4, 2, 3], [4, 1, 2, 3], [1, 2, 3, 4]] sage: [len(BaxterPermutations(n)) for n in range(9)] [1, 1, 2, 6, 22, 92, 422, 2074, 10754] TESTS:: sage: all(a in BaxterPermutations(n) for n in range(7) ....: for a in BaxterPermutations(n)) True ALGORITHM: The algorithm using generating trees described in [BBMF2008]_ is used. The idea is that all Baxter permutations of size `n + 1` can be obtained by inserting the letter `n + 1` either just before a left to right maximum or just after a right to left maximum of a Baxter permutation of size `n`. """ if self._n == 0: yield Permutations(0)([]) elif self._n == 1: yield Permutations(1)([1]) else: for b in BaxterPermutations(self._n - 1): # Left to right maxima. for j in b.reverse().saliances(): i = self._n - 2 - j yield Permutations(self._n)(b[:i] + [self._n] + b[i:]) # Right to left maxima. for i in b.saliances(): yield Permutations(self._n)(b[:i + 1] + [self._n] + b[i + 1:])
def product_on_basis(self, e_ij, e_kl): r""" Return the product of basis elements. EXAMPLES:: sage: S = SchurAlgebra(QQ, 2, 3) sage: B = S.basis() If we multiply two basis elements `x` and `y`, such that `x[1]` and `y[0]` are not permutations of each other, the result is zero:: sage: S.product_on_basis(((1, 1, 1), (1, 1, 2)), ((1, 2, 2), (1, 1, 2))) 0 If we multiply a basis element `x` by a basis element which consists of the same tuple repeated twice (on either side), the result is either zero (if the previous case applies) or `x`:: sage: ww = B[((1, 2, 2), (1, 2, 2))] sage: x = B[((1, 2, 2), (1, 1, 2))] sage: ww * x S((1, 2, 2), (1, 1, 2)) An arbitrary product, on the other hand, may have multiplicities:: sage: x = B[((1, 1, 1), (1, 1, 2))] sage: y = B[((1, 1, 2), (1, 2, 2))] sage: x * y 2*S((1, 1, 1), (1, 2, 2)) """ j = e_ij[1] i = e_ij[0] l = e_kl[1] l = sorted(l) # Find basis elements (p,q) such that p ~ i and q ~ l e_pq = [] for v in self.basis().keys(): if v[0] == i and sorted(v[1]) == l: e_pq.append(v) b = self.basis() product = self.zero() # Find s in I(n,r) such that (p,s) ~ (i,j) and (s,q) ~ (k,l) for e in e_pq: Z_ijklpq = self.base_ring().zero() for s in Permutations([xx for xx in j]): if (schur_representative_from_index(e[0], s) == e_ij and schur_representative_from_index(s, e[1]) == e_kl): Z_ijklpq += self.base_ring().one() product += Z_ijklpq * b[e] return product
def _element_constructor_(self, x): """ Coerce x into self. EXAMPLES:: sage: X = SchubertPolynomialRing(QQ) sage: X._element_constructor_([2,1,3]) X[2, 1] sage: X._element_constructor_(Permutation([2,1,3])) X[2, 1] sage: R.<x1, x2, x3> = QQ[] sage: X(x1^2*x2) X[3, 2, 1] TESTS: We check that :trac:`12924` is fixed:: sage: X = SchubertPolynomialRing(QQ) sage: X._element_constructor_([1,2,1]) Traceback (most recent call last): ... ValueError: The input [1, 2, 1] is not a valid permutation """ if isinstance(x, list): #checking the input to avoid symmetrica crashing Sage, see trac 12924 if not x in Permutations(): raise ValueError, "The input %s is not a valid permutation" % ( x) perm = permutation.Permutation_class(x).remove_extra_fixed_points() return self._from_dict({perm: self.base_ring()(1)}) elif isinstance(x, permutation.Permutation_class): if not list(x) in Permutations(): raise ValueError, "The input %s is not a valid permutation" % ( x) perm = x.remove_extra_fixed_points() return self._from_dict({perm: self.base_ring()(1)}) elif is_MPolynomial(x): return symmetrica.t_POLYNOM_SCHUBERT(x) else: raise TypeError
def _structures(self, structure_class, labels): """ EXAMPLES:: sage: L = species.LinearOrderSpecies() sage: L.structures([1,2,3]).list() [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] """ from sage.combinat.permutation import Permutations for p in Permutations(len(labels)): yield structure_class(self, labels, p._list)
def on_basis(A): k = A.size() ret = R.zero() if n < k: return ret for p in Permutations(k): if P(p.to_cycles()) == A: # -1 for indexing ret += R.sum(prod(x[I[i]][I[p[i]-1]] for i in range(k)) for I in Subsets(range(n), k)) return ret
def __init__(self, n): """ EXAMPLES:: sage: from sage.combinat.baxter_permutations import BaxterPermutations_size sage: BaxterPermutations_size(5) Baxter permutations of size 5 """ self.element_class = Permutations(n).element_class self._n = ZZ(n) from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets super(BaxterPermutations, self).__init__(category=FiniteEnumeratedSets())
def __init__(self, d, n, q, R): """ Initialize ``self``. EXAMPLES:: sage: Y = algebras.YokonumaHecke(5, 3) sage: elts = Y.some_elements() + list(Y.algebra_generators()) sage: TestSuite(Y).run(elements=elts) """ self._d = d self._n = n self._q = q self._Pn = Permutations(n) import itertools C = itertools.product(*([range(d)]*n)) indices = list( itertools.product(C, self._Pn)) cat = Algebras(R).WithBasis() CombinatorialFreeModule.__init__(self, R, indices, prefix='Y', category=cat) self._assign_names(self.algebra_generators().keys())
def _structures(self, structure_class, labels): """ EXAMPLES:: sage: P = species.PermutationSpecies() sage: P.structures([1,2,3]).list() [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] """ if labels == []: yield structure_class(self, labels, []) else: for p in Permutations(len(labels)): yield structure_class(self, labels, list(p))
def __init__(self, m, n, category=None): """ Initialize ``self``. EXAMPLES:: sage: C = ColoredPermutations(4, 3) sage: TestSuite(C).run() sage: C = ColoredPermutations(2, 3) sage: TestSuite(C).run() sage: C = ColoredPermutations(1, 3) sage: TestSuite(C).run() """ if m <= 0: raise ValueError("m must be a positive integer") self._m = m self._n = n self._C = IntegerModRing(self._m) self._P = Permutations(self._n) if category is None: category = (Groups(), FiniteEnumeratedSets()) Parent.__init__(self, category=category)
class YokonumaHeckeAlgebra(CombinatorialFreeModule): r""" The Yokonuma-Hecke algebra `Y_{d,n}(q)`. Let `R` be a commutative ring and `q` be a unit in `R`. The *Yokonuma-Hecke algebra* `Y_{d,n}(q)` is the associative, unital `R`-algebra generated by `t_1, t_2, \ldots, t_n, g_1, g_2, \ldots, g_{n-1}` and subject to the relations: - `g_i g_j = g_j g_i` for all `|i - j| > 1`, - `g_i g_{i+1} g_i = g_{i+1} g_i g_{i+1}`, - `t_i t_j = t_j t_i`, - `t_j g_i = g_i t_{j s_i}`, and - `t_j^d = 1`, where `s_i` is the simple transposition `(i, i+1)`, along with the quadratic relation .. MATH:: g_i^2 = 1 + \frac{(q - q^{-1})}{d} \left( \sum_{s=0}^{d-1} t_i^s t_{i+1}^{-s} \right) g_i. Thus the Yokonuma-Hecke algebra can be considered a quotient of the framed braid group `(\ZZ / d\ZZ) \wr B_n`, where `B_n` is the classical braid group on `n` strands, by the quadratic relations. Moreover, all of the algebra generators are invertible. In particular, we have .. MATH:: g_i^{-1} = g_i - (q - q^{-1}) e_i. When we specialize `q = \pm 1`, we obtain the group algebra of the complex reflection group `G(d, 1, n) = (\ZZ / d\ZZ) \wr S_n`. Moreover for `d = 1`, the Yokonuma-Hecke algebra is equal to the :class:`Iwahori-Hecke <IwahoriHeckeAlgebra>` of type `A_{n-1}`. INPUT: - ``d`` -- the maximum power of `t` - ``n`` -- the number of generators - ``q`` -- (optional) an invertible element in a commutative ring; the default is `q \in \QQ[q,q^{-1}]` - ``R`` -- (optional) a commutative ring containing ``q``; the default is the parent of `q` EXAMPLES: We construct `Y_{4,3}` and do some computations:: sage: Y = algebras.YokonumaHecke(4, 3) sage: g1, g2, t1, t2, t3 = Y.algebra_generators() sage: g1 * g2 g[1,2] sage: t1 * g1 t1*g[1] sage: g2 * t2 t3*g[2] sage: g2 * t3 t2*g[2] sage: (g2 + t1) * (g1 + t2*t3) g[2,1] + t2*t3*g[2] + t1*g[1] + t1*t2*t3 sage: g1 * g1 1 - (1/4*q^-1-1/4*q)*g[1] - (1/4*q^-1-1/4*q)*t1*t2^3*g[1] - (1/4*q^-1-1/4*q)*t1^2*t2^2*g[1] - (1/4*q^-1-1/4*q)*t1^3*t2*g[1] sage: g2 * g1 * t1 t3*g[2,1] We construct the elements `e_i` and show that they are idempotents:: sage: e1 = Y.e(1); e1 1/4 + 1/4*t1*t2^3 + 1/4*t1^2*t2^2 + 1/4*t1^3*t2 sage: e1 * e1 == e1 True sage: e2 = Y.e(2); e2 1/4 + 1/4*t2*t3^3 + 1/4*t2^2*t3^2 + 1/4*t2^3*t3 sage: e2 * e2 == e2 True REFERENCES: - [CL2013]_ - [CPdA2014]_ - [ERH2015]_ - [JPdA15]_ """ @staticmethod def __classcall_private__(cls, d, n, q=None, R=None): """ Standardize input to ensure a unique representation. TESTS:: sage: Y1 = algebras.YokonumaHecke(5, 3) sage: q = LaurentPolynomialRing(QQ, 'q').gen() sage: Y2 = algebras.YokonumaHecke(5, 3, q) sage: Y3 = algebras.YokonumaHecke(5, 3, q, q.parent()) sage: Y1 is Y2 and Y2 is Y3 True """ if q is None: q = LaurentPolynomialRing(QQ, 'q').gen() if R is None: R = q.parent() q = R(q) if R not in Rings().Commutative(): raise TypeError("base ring must be a commutative ring") return super(YokonumaHeckeAlgebra, cls).__classcall__(cls, d, n, q, R) def __init__(self, d, n, q, R): """ Initialize ``self``. EXAMPLES:: sage: Y = algebras.YokonumaHecke(5, 3) sage: elts = Y.some_elements() + list(Y.algebra_generators()) sage: TestSuite(Y).run(elements=elts) """ self._d = d self._n = n self._q = q self._Pn = Permutations(n) import itertools C = itertools.product(*([range(d)]*n)) indices = list( itertools.product(C, self._Pn)) cat = Algebras(R).WithBasis() CombinatorialFreeModule.__init__(self, R, indices, prefix='Y', category=cat) self._assign_names(self.algebra_generators().keys()) def _repr_(self): """ Return a string representation of ``self``. EXAMPLES:: sage: algebras.YokonumaHecke(5, 2) Yokonuma-Hecke algebra of rank 5 and order 2 with q=q over Univariate Laurent Polynomial Ring in q over Rational Field """ return "Yokonuma-Hecke algebra of rank {} and order {} with q={} over {}".format( self._d, self._n, self._q, self.base_ring()) def _latex_(self): r""" Return a latex representation of ``self``. EXAMPLES:: sage: Y = algebras.YokonumaHecke(5, 2) sage: latex(Y) \mathcal{Y}_{5,2}(q) """ return "\\mathcal{Y}_{%s,%s}(%s)"%(self._d, self._n, self._q) def _repr_term(self, m): """ Return a string representation of the basis element indexed by ``m``. EXAMPLES:: sage: Y = algebras.YokonumaHecke(4, 3) sage: Y._repr_term( ((1, 0, 2), Permutation([3,2,1])) ) 't1*t3^2*g[2,1,2]' """ gen_str = lambda e: '' if e == 1 else '^%s'%e lhs = '*'.join('t%s'%(j+1) + gen_str(i) for j,i in enumerate(m[0]) if i > 0) redword = m[1].reduced_word() if not redword: if not lhs: return '1' return lhs rhs = 'g[{}]'.format(','.join(str(i) for i in redword)) if not lhs: return rhs return lhs + '*' + rhs def _latex_term(self, m): r""" Return a latex representation for the basis element indexed by ``m``. EXAMPLES:: sage: Y = algebras.YokonumaHecke(4, 3) sage: Y._latex_term( ((1, 0, 2), Permutation([3,2,1])) ) 't_{1} t_{3}^2 g_{2} g_{1} g_{2}' """ gen_str = lambda e: '' if e == 1 else '^%s'%e lhs = ' '.join('t_{%s}'%(j+1) + gen_str(i) for j,i in enumerate(m[0]) if i > 0) redword = m[1].reduced_word() if not redword: if not lhs: return '1' return lhs return lhs + ' ' + ' '.join("g_{%d}"%i for i in redword) @cached_method def algebra_generators(self): """ Return the algebra generators of ``self``. EXAMPLES:: sage: Y = algebras.YokonumaHecke(5, 3) sage: dict(Y.algebra_generators()) {'g1': g[1], 'g2': g[2], 't1': t1, 't2': t2, 't3': t3} """ one = self._Pn.one() zero = [0]*self._n d = {} for i in range(self._n): r = list(zero) # Make a copy r[i] = 1 d['t%s'%(i+1)] = self.monomial( (tuple(r), one) ) G = self._Pn.group_generators() for i in range(1, self._n): d['g%s'%i] = self.monomial( (tuple(zero), G[i]) ) return Family(sorted(d), lambda i: d[i]) @cached_method def gens(self): """ Return the generators of ``self``. EXAMPLES:: sage: Y = algebras.YokonumaHecke(5, 3) sage: Y.gens() (g[1], g[2], t1, t2, t3) """ return tuple(self.algebra_generators()) @cached_method def one_basis(self): """ Return the index of the basis element of `1`. EXAMPLES:: sage: Y = algebras.YokonumaHecke(5, 3) sage: Y.one_basis() ((0, 0, 0), [1, 2, 3]) """ one = self._Pn.one() zero = [0]*self._n return (tuple(zero), one) @cached_method def e(self, i): """ Return the element `e_i`. EXAMPLES:: sage: Y = algebras.YokonumaHecke(4, 3) sage: Y.e(1) 1/4 + 1/4*t1*t2^3 + 1/4*t1^2*t2^2 + 1/4*t1^3*t2 sage: Y.e(2) 1/4 + 1/4*t2*t3^3 + 1/4*t2^2*t3^2 + 1/4*t2^3*t3 """ if i < 1 or i >= self._n: raise ValueError("invalid index") c = ~self.base_ring()(self._d) zero = [0]*self._n one = self._Pn.one() d = {} for s in range(self._d): r = list(zero) # Make a copy r[i-1] = s if s != 0: r[i] = self._d - s d[(tuple(r), one)] = c return self._from_dict(d, remove_zeros=False) def g(self, i=None): """ Return the generator(s) `g_i`. INPUT: - ``i`` -- (default: ``None``) the generator `g_i` or if ``None``, then the list of all generators `g_i` EXAMPLES:: sage: Y = algebras.YokonumaHecke(8, 3) sage: Y.g(1) g[1] sage: Y.g() [g[1], g[2]] """ G = self.algebra_generators() if i is None: return [G['g%s'%i] for i in range(1, self._n)] return G['g%s'%i] def t(self, i=None): """ Return the generator(s) `t_i`. INPUT: - ``i`` -- (default: ``None``) the generator `t_i` or if ``None``, then the list of all generators `t_i` EXAMPLES:: sage: Y = algebras.YokonumaHecke(8, 3) sage: Y.t(2) t2 sage: Y.t() [t1, t2, t3] """ G = self.algebra_generators() if i is None: return [G['t%s'%i] for i in range(1, self._n+1)] return G['t%s'%i] def product_on_basis(self, m1, m2): """ Return the product of the basis elements indexed by ``m1`` and ``m2``. EXAMPLES:: sage: Y = algebras.YokonumaHecke(4, 3) sage: m = ((1, 0, 2), Permutations(3)([2,1,3])) sage: 4 * Y.product_on_basis(m, m) -(q^-1-q)*t2^2*g[1] + 4*t1*t2 - (q^-1-q)*t1*t2*g[1] - (q^-1-q)*t1^2*g[1] - (q^-1-q)*t1^3*t2^3*g[1] Check that we apply the permutation correctly on `t_i`:: sage: Y = algebras.YokonumaHecke(4, 3) sage: g1, g2, t1, t2, t3 = Y.algebra_generators() sage: g21 = g2 * g1 sage: g21 * t1 t3*g[2,1] """ t1,g1 = m1 t2,g2 = m2 # Commmute g1 and t2, then multiply t1 and t2 #ig1 = g1 t = [(t1[i] + t2[g1.index(i+1)]) % self._d for i in range(self._n)] one = self._Pn.one() if g1 == one: return self.monomial((tuple(t), g2)) ret = self.monomial((tuple(t), g1)) # We have to reverse the reduced word due to Sage's convention # for permutation multiplication for i in g2.reduced_word(): ret = self.linear_combination((self._product_by_basis_gen(m, i), c) for m,c in ret) return ret def _product_by_basis_gen(self, m, i): r""" Return the product `t g_w g_i`. If the quadratic relation is `g_i^2 = 1 + (q + q^{-1})e_i g_i`, then we have .. MATH:: g_w g_i = \begin{cases} g_{ws_i} & \text{if } \ell(ws_i) = \ell(w) + 1, \\ g_{ws_i} - (q - q^{-1}) g_w e_i & \text{if } \ell(w s_i) = \ell(w) - 1. \end{cases} INPUT: - ``m`` -- a pair ``[t, w]``, where ``t`` encodes the monomial and ``w`` is an element of the permutation group - ``i`` -- an element of the index set EXAMPLES:: sage: Y = algebras.YokonumaHecke(4, 3) sage: m = ((1, 0, 2), Permutations(3)([2,1,3])) sage: 4 * Y._product_by_basis_gen(m, 1) -(q^-1-q)*t2*t3^2*g[1] + 4*t1*t3^2 - (q^-1-q)*t1*t3^2*g[1] - (q^-1-q)*t1^2*t2^3*t3^2*g[1] - (q^-1-q)*t1^3*t2^2*t3^2*g[1] """ t, w = m # We have to flip the side due to Sage's multiplication # convention for permutations wi = w.apply_simple_reflection(i, side="left") if not w.has_descent(i, side="left"): return self.monomial((t, wi)) R = self.base_ring() c = (self._q - ~self._q) * ~R(self._d) d = {(t, wi): R.one()} # We commute g_w and e_i and then multiply by t for s in range(self._d): r = list(t) r[w[i-1]-1] = (r[w[i-1]-1] + s) % self._d if s != 0: r[w[i]-1] = (r[w[i]-1] + self._d - s) % self._d d[(tuple(r), w)] = c return self._from_dict(d, remove_zeros=False) @cached_method def inverse_g(self, i): r""" Return the inverse of the generator `g_i`. From the quadratic relation, we have .. MATH:: g_i^{-1} = g_i - (q - q^{-1}) e_i. EXAMPLES:: sage: Y = algebras.YokonumaHecke(2, 4) sage: [2*Y.inverse_g(i) for i in range(1, 4)] [(q^-1+q) + 2*g[1] + (q^-1+q)*t1*t2, (q^-1+q) + 2*g[2] + (q^-1+q)*t2*t3, (q^-1+q) + 2*g[3] + (q^-1+q)*t3*t4] """ if i < 1 or i >= self._n: raise ValueError("invalid index") return self.g(i) + (~self._q + self._q) * self.e(i) class Element(CombinatorialFreeModule.Element): def inverse(self): r""" Return the inverse if ``self`` is a basis element. EXAMPLES:: sage: Y = algebras.YokonumaHecke(3, 3) sage: t = prod(Y.t()); t t1*t2*t3 sage: ~t t1^2*t2^2*t3^2 sage: [3*~(t*g) for g in Y.g()] [(q^-1+q)*t2*t3^2 + (q^-1+q)*t1*t3^2 + (q^-1+q)*t1^2*t2^2*t3^2 + 3*t1^2*t2^2*t3^2*g[1], (q^-1+q)*t1^2*t3 + (q^-1+q)*t1^2*t2 + (q^-1+q)*t1^2*t2^2*t3^2 + 3*t1^2*t2^2*t3^2*g[2]] """ if len(self) != 1: raise NotImplementedError("inverse only implemented for basis elements (monomials in the generators)"%self) H = self.parent() t,w = self.support_of_term() telt = H.monomial( (tuple((H._d - e) % H._d for e in t), H._Pn.one()) ) return telt * H.prod(H.inverse_g(i) for i in reversed(w.reduced_word())) __invert__ = inverse
class ColoredPermutations(Parent, UniqueRepresentation): r""" The group of `m`-colored permutations on `\{1, 2, \ldots, n\}`. Let `S_n` be the symmetric group on `n` letters and `C_m` be the cyclic group of order `m`. The `m`-colored permutation group on `n` letters is given by `P_n^m = C_m \wr S_n`. This is also the complex reflection group `G(m, 1, n)`. We define our multiplication by .. MATH:: ((s_1, \ldots s_n), \sigma) \cdot ((t_1, \ldots, t_n), \tau) = ((s_1 t_{\sigma(1)}, \ldots, s_n t_{\sigma(n)}), \tau \sigma). EXAMPLES:: sage: C = ColoredPermutations(4, 3); C 4-colored permutations of size 3 sage: s1,s2,t = C.gens() sage: (s1, s2, t) ([[0, 0, 0], [2, 1, 3]], [[0, 0, 0], [1, 3, 2]], [[0, 0, 1], [1, 2, 3]]) sage: s1*s2 [[0, 0, 0], [3, 1, 2]] sage: s1*s2*s1 == s2*s1*s2 True sage: t^4 == C.one() True sage: s2*t*s2 [[0, 1, 0], [1, 2, 3]] We can also create a colored permutation by passing either a list of tuples consisting of ``(color, element)``:: sage: x = C([(2,1), (3,3), (3,2)]); x [[2, 3, 3], [1, 3, 2]] or a list of colors and a permutation:: sage: C([[3,3,1], [1,3,2]]) [[3, 3, 1], [1, 3, 2]] There is also the natural lift from permutations:: sage: P = Permutations(3) sage: C(P.an_element()) [[0, 0, 0], [3, 1, 2]] REFERENCES: - :wikipedia:`Generalized_symmetric_group` - :wikipedia:`Complex_reflection_group` """ def __init__(self, m, n): """ Initialize ``self``. EXAMPLES:: sage: C = ColoredPermutations(4, 3) sage: TestSuite(C).run() sage: C = ColoredPermutations(2, 3) sage: TestSuite(C).run() sage: C = ColoredPermutations(1, 3) sage: TestSuite(C).run() """ if m <= 0: raise ValueError("m must be a positive integer") self._m = ZZ(m) self._n = ZZ(n) self._C = IntegerModRing(self._m) self._P = Permutations(self._n) if self._m == 1 or self._m == 2: from sage.categories.finite_coxeter_groups import FiniteCoxeterGroups category = FiniteCoxeterGroups().Irreducible() else: from sage.categories.complex_reflection_groups import ComplexReflectionGroups category = ComplexReflectionGroups().Finite().Irreducible().WellGenerated() Parent.__init__(self, category=category) def _repr_(self): """ Return a string representation of ``self``. EXAMPLES:: sage: ColoredPermutations(4, 3) 4-colored permutations of size 3 """ return "{}-colored permutations of size {}".format(self._m, self._n) @cached_method def index_set(self): """ Return the index set of ``self``. EXAMPLES:: sage: C = ColoredPermutations(3, 4) sage: C.index_set() (1, 2, 3, 4) sage: C = ColoredPermutations(1, 4) sage: C.index_set() (1, 2, 3) TESTS:: sage: S = SignedPermutations(4) sage: S.index_set() (1, 2, 3, 4) """ n = self._n if self._m != 1: n += 1 return tuple(range(1, n)) def coxeter_matrix(self): """ Return the Coxeter matrix of ``self``. EXAMPLES:: sage: C = ColoredPermutations(3, 4) sage: C.coxeter_matrix() [1 3 2 2] [3 1 3 2] [2 3 1 4] [2 2 4 1] sage: C = ColoredPermutations(1, 4) sage: C.coxeter_matrix() [1 3 2] [3 1 3] [2 3 1] TESTS:: sage: S = SignedPermutations(4) sage: S.coxeter_matrix() [1 3 2 2] [3 1 3 2] [2 3 1 4] [2 2 4 1] """ from sage.combinat.root_system.cartan_type import CartanType if self._m == 1: return CartanType(['A', self._n-1]).coxeter_matrix() return CartanType(['B', self._n]).coxeter_matrix() @cached_method def one(self): """ Return the identity element of ``self``. EXAMPLES:: sage: C = ColoredPermutations(4, 3) sage: C.one() [[0, 0, 0], [1, 2, 3]] """ return self.element_class(self, [self._C.zero()] * self._n, self._P.identity()) def simple_reflection(self, i): r""" Return the ``i``-th simple reflection of ``self``. EXAMPLES:: sage: C = ColoredPermutations(4, 3) sage: C.gens() ([[0, 0, 0], [2, 1, 3]], [[0, 0, 0], [1, 3, 2]], [[0, 0, 1], [1, 2, 3]]) sage: C.simple_reflection(2) [[0, 0, 0], [1, 3, 2]] sage: C.simple_reflection(3) [[0, 0, 1], [1, 2, 3]] sage: S = SignedPermutations(4) sage: S.simple_reflection(1) [2, 1, 3, 4] sage: S.simple_reflection(4) [1, 2, 3, -4] """ if i not in self.index_set(): raise ValueError("i must be in the index set") colors = [self._C.zero()] * self._n if i < self._n: p = list(range(1, self._n + 1)) p[i - 1] = i + 1 p[i] = i return self.element_class(self, colors, self._P(p)) colors[-1] = self._C.one() return self.element_class(self, colors, self._P.identity()) @cached_method def _inverse_simple_reflections(self): """ Return the inverse of the simple reflections of ``self``. .. WARNING:: This returns a ``dict`` that should not be mutated since the result is cached. EXAMPLES:: sage: C = ColoredPermutations(4, 3) sage: C._inverse_simple_reflections() {1: [[0, 0, 0], [2, 1, 3]], 2: [[0, 0, 0], [1, 3, 2]], 3: [[0, 0, 3], [1, 2, 3]]} """ s = self.simple_reflections() return {i: ~s[i] for i in self.index_set()} @cached_method def gens(self): """ Return the generators of ``self``. EXAMPLES:: sage: C = ColoredPermutations(4, 3) sage: C.gens() ([[0, 0, 0], [2, 1, 3]], [[0, 0, 0], [1, 3, 2]], [[0, 0, 1], [1, 2, 3]]) sage: S = SignedPermutations(4) sage: S.gens() ([2, 1, 3, 4], [1, 3, 2, 4], [1, 2, 4, 3], [1, 2, 3, -4]) """ return tuple(self.simple_reflection(i) for i in self.index_set()) def matrix_group(self): """ Return the matrix group corresponding to ``self``. EXAMPLES:: sage: C = ColoredPermutations(4, 3) sage: C.matrix_group() Matrix group over Cyclotomic Field of order 4 and degree 2 with 3 generators ( [0 1 0] [1 0 0] [ 1 0 0] [1 0 0] [0 0 1] [ 0 1 0] [0 0 1], [0 1 0], [ 0 0 zeta4] ) """ from sage.groups.matrix_gps.finitely_generated import MatrixGroup return MatrixGroup([g.to_matrix() for g in self.gens()]) def _element_constructor_(self, x): """ Construct an element of ``self`` from ``x``. INPUT: Either a list of pairs (color, element) or a pair of lists (colors, elements). TESTS:: sage: C = ColoredPermutations(4, 3) sage: x = C([(2,1), (3,3), (3,2)]); x [[2, 3, 3], [1, 3, 2]] sage: x == C([[2,3,3], [1,3,2]]) True """ if isinstance(x, list): if isinstance(x[0], tuple): c = [] p = [] for k in x: if len(k) != 2: raise ValueError("input must be pairs (color, element)") c.append(self._C(k[0])) p.append(k[1]) return self.element_class(self, c, self._P(p)) if len(x) != 2: raise ValueError("input must be a pair of a list of colors and a permutation") return self.element_class(self, [self._C(v) for v in x[0]], self._P(x[1])) def _coerce_map_from_(self, C): """ Return a coerce map from ``C`` if it exists and ``None`` otherwise. EXAMPLES:: sage: C = ColoredPermutations(2, 3) sage: S = SignedPermutations(3) sage: C.has_coerce_map_from(S) True sage: C = ColoredPermutations(4, 3) sage: C.has_coerce_map_from(S) False sage: S = SignedPermutations(4) sage: C.has_coerce_map_from(S) False sage: P = Permutations(3) sage: C.has_coerce_map_from(P) True sage: P = Permutations(4) sage: C.has_coerce_map_from(P) False """ if isinstance(C, Permutations) and C.n == self._n: return lambda P, x: P.element_class(P, [P._C.zero()]*P._n, x) if self._m == 2 and isinstance(C, SignedPermutations) and C._n == self._n: return lambda P, x: P.element_class(P, [P._C.zero() if v == 1 else P._C.one() for v in x._colors], x._perm) return super(ColoredPermutations, self)._coerce_map_from_(C) def __iter__(self): """ Iterate over ``self``. EXAMPLES:: sage: C = ColoredPermutations(2, 2) sage: [x for x in C] [[[0, 0], [1, 2]], [[0, 1], [1, 2]], [[1, 0], [1, 2]], [[1, 1], [1, 2]], [[0, 0], [2, 1]], [[0, 1], [2, 1]], [[1, 0], [2, 1]], [[1, 1], [2, 1]]] """ for p in self._P: for c in itertools.product(self._C, repeat=self._n): yield self.element_class(self, c, p) def cardinality(self): """ Return the cardinality of ``self``. EXAMPLES:: sage: C = ColoredPermutations(4, 3) sage: C.cardinality() 384 sage: C.cardinality() == 4**3 * factorial(3) True """ return self._m ** self._n * self._P.cardinality() order = cardinality def rank(self): """ Return the rank of ``self``. The rank of a complex reflection group is equal to the dimension of the complex vector space the group acts on. EXAMPLES:: sage: C = ColoredPermutations(4, 12) sage: C.rank() 12 sage: C = ColoredPermutations(7, 4) sage: C.rank() 4 sage: C = ColoredPermutations(1, 4) sage: C.rank() 3 """ if self._m == 1: return self._n - 1 return self._n def degrees(self): """ Return the degrees of ``self``. The degrees of a complex reflection group are the degrees of the fundamental invariants of the ring of polynomial invariants. If `m = 1`, then we are in the special case of the symmetric group and the degrees are `(2, 3, \ldots, n, n+1)`. Otherwise the degrees are `(m, 2m, \ldots, nm)`. EXAMPLES:: sage: C = ColoredPermutations(4, 3) sage: C.degrees() (4, 8, 12) sage: S = ColoredPermutations(1, 3) sage: S.degrees() (2, 3) We now check that the product of the degrees is equal to the cardinality of ``self``:: sage: prod(C.degrees()) == C.cardinality() True sage: prod(S.degrees()) == S.cardinality() True """ # For the usual symmetric group (self._m=1) we need to start at 2 start = 2 if self._m == 1 else 1 return tuple(self._m * i for i in range(start, self._n + 1)) def codegrees(self): r""" Return the codegrees of ``self``. Let `G` be a complex reflection group. The codegrees `d_1^* \leq d_2^* \leq \cdots \leq d_{\ell}^*` of `G` can be defined by: .. MATH:: \prod_{i=1}^{\ell} (q - d_i^* - 1) = \sum_{g \in G} \det(g) q^{\dim(V^g)}, where `V` is the natural complex vector space that `G` acts on and `\ell` is the :meth:`rank`. If `m = 1`, then we are in the special case of the symmetric group and the codegrees are `(n-2, n-3, \ldots 1, 0)`. Otherwise the degrees are `((n-1)m, (n-2)m, \ldots, m, 0)`. EXAMPLES:: sage: C = ColoredPermutations(4, 3) sage: C.codegrees() (8, 4, 0) sage: S = ColoredPermutations(1, 3) sage: S.codegrees() (1, 0) TESTS: We check the polynomial identity:: sage: R.<q> = ZZ[] sage: C = ColoredPermutations(3, 2) sage: f = prod(q - ds - 1 for ds in C.codegrees()) sage: d = lambda x: sum(1 for e in x.to_matrix().eigenvalues() if e == 1) sage: g = sum(det(x.to_matrix()) * q**d(x) for x in C) sage: f == g True """ # Special case for the usual symmetric group last = self._n-1 if self._m == 1 else self._n return tuple(self._m * i for i in reversed(range(last))) def number_of_reflection_hyperplanes(self): """ Return the number of reflection hyperplanes of ``self``. The number of reflection hyperplanes of a complex reflection group is equal to the sum of the codegrees plus the rank. EXAMPLES:: sage: C = ColoredPermutations(1, 2) sage: C.number_of_reflection_hyperplanes() 1 sage: C = ColoredPermutations(1, 3) sage: C.number_of_reflection_hyperplanes() 3 sage: C = ColoredPermutations(4, 12) sage: C.number_of_reflection_hyperplanes() 276 """ return sum(self.codegrees()) + self.rank() def fixed_point_polynomial(self, q=None): r""" The fixed point polynomial of ``self``. The fixed point polynomial `f_G` of a complex reflection group `G` is counting the dimensions of fixed points subspaces: .. MATH:: f_G(q) = \sum_{w \in W} q^{\dim V^w}. Furthermore, let `d_1, d_2, \ldots, d_{\ell}` be the degrees of `G`, where `\ell` is the :meth:`rank`. Then the fixed point polynomial is given by .. MATH:: f_G(q) = \prod_{i=1}^{\ell} (q + d_i - 1). INPUT: - ``q`` -- (default: the generator of ``ZZ['q']``) the parameter `q` EXAMPLES:: sage: C = ColoredPermutations(4, 3) sage: C.fixed_point_polynomial() q^3 + 21*q^2 + 131*q + 231 sage: S = ColoredPermutations(1, 3) sage: S.fixed_point_polynomial() q^2 + 3*q + 2 TESTS: We check the against the degrees and codegrees:: sage: R.<q> = ZZ[] sage: C = ColoredPermutations(4, 3) sage: C.fixed_point_polynomial(q) == prod(q + d - 1 for d in C.degrees()) True """ if q is None: q = PolynomialRing(ZZ, 'q').gen(0) return prod(q + d - 1 for d in self.degrees()) def is_well_generated(self): """ Return if ``self`` is a well-generated complex reflection group. A complex reflection group `G` is well-generated if it is generated by `\ell` reflections. Equivalently, `G` is well-generated if `d_i + d_i^* = d_{\ell}` for all `1 \leq i \leq \ell`. EXAMPLES:: sage: C = ColoredPermutations(4, 3) sage: C.is_well_generated() True sage: C = ColoredPermutations(2, 8) sage: C.is_well_generated() True sage: C = ColoredPermutations(1, 4) sage: C.is_well_generated() True """ deg = self.degrees() dstar = self.codegrees() return all(deg[-1] == d + dstar[i] for i, d in enumerate(deg)) Element = ColoredPermutation