def _is_a_splitting(S1, S2, n, return_automorphism=False): """ Check wether ``(S1,S2)`` is a splitting of `\ZZ/n\ZZ`. A splitting of `R = \ZZ/n\ZZ` is a pair of subsets of `R` which is a partition of `R \\backslash \{0\}` and such that there exists an element `r` of `R` such that `r S_1 = S_2` and `r S_2 = S_1` (where `r S` is the point-wise multiplication of the elements of `S` by `r`). Splittings are useful for computing idempotents in the quotient ring `Q = GF(q)[x]/(x^n-1)`. INPUT: - ``S1, S2`` -- disjoint sublists partitioning ``[1, 2, ..., n-1]`` - ``n`` (integer) - ``return_automorphism`` (boolean) -- whether to return the automorphism exchanging `S_1` and `S_2`. OUTPUT: If ``return_automorphism is False`` (default) the function returns boolean values. Otherwise, it returns a pair ``(b, r)`` where ``b`` is a boolean indicating whether `S1`, `S2` is a splitting of `n`, and `r` is such that `r S_1 = S_2` and `r S_2 = S_1` (if `b` is ``False``, `r` is equal to ``None``). EXAMPLES:: sage: from sage.coding.code_constructions import _is_a_splitting sage: _is_a_splitting([1,2],[3,4],5) True sage: _is_a_splitting([1,2],[3,4],5,return_automorphism=True) (True, 4) sage: _is_a_splitting([1,3],[2,4,5,6],7) False sage: _is_a_splitting([1,3,4],[2,5,6],7) False sage: for P in SetPartitions(6,[3,3]): ....: res,aut= _is_a_splitting(P[0],P[1],7,return_automorphism=True) ....: if res: ....: print((aut, P[0], P[1])) (6, {1, 2, 3}, {4, 5, 6}) (3, {1, 2, 4}, {3, 5, 6}) (6, {1, 3, 5}, {2, 4, 6}) (6, {1, 4, 5}, {2, 3, 6}) We illustrate now how to find idempotents in quotient rings:: sage: n = 11; q = 3 sage: C = Zmod(n).cyclotomic_cosets(q); C [[0], [1, 3, 4, 5, 9], [2, 6, 7, 8, 10]] sage: S1 = C[1] sage: S2 = C[2] sage: _is_a_splitting(S1,S2,11) True sage: F = GF(q) sage: P.<x> = PolynomialRing(F,"x") sage: I = Ideal(P,[x^n-1]) sage: Q.<x> = QuotientRing(P,I) sage: i1 = -sum([x^i for i in S1]); i1 2*x^9 + 2*x^5 + 2*x^4 + 2*x^3 + 2*x sage: i2 = -sum([x^i for i in S2]); i2 2*x^10 + 2*x^8 + 2*x^7 + 2*x^6 + 2*x^2 sage: i1^2 == i1 True sage: i2^2 == i2 True sage: (1-i1)^2 == 1-i1 True sage: (1-i2)^2 == 1-i2 True We return to dealing with polynomials (rather than elements of quotient rings), so we can construct cyclic codes:: sage: P.<x> = PolynomialRing(F,"x") sage: i1 = -sum([x^i for i in S1]) sage: i2 = -sum([x^i for i in S2]) sage: i1_sqrd = (i1^2).quo_rem(x^n-1)[1] sage: i1_sqrd == i1 True sage: i2_sqrd = (i2^2).quo_rem(x^n-1)[1] sage: i2_sqrd == i2 True sage: C1 = codes.CyclicCode(length = n, generator_pol = gcd(i1, x^n - 1)) sage: C2 = codes.CyclicCode(length = n, generator_pol = gcd(1-i2, x^n - 1)) sage: C1.dual_code().systematic_generator_matrix() == C2.systematic_generator_matrix() True This is a special case of Theorem 6.4.3 in [HP2003]_. """ R = IntegerModRing(n) S1 = set(R(x) for x in S1) S2 = set(R(x) for x in S2) # we first check whether (S1,S2) is a partition of R - {0} if (len(S1) + len(S2) != n-1 or len(S1) != len(S2) or R.zero() in S1 or R.zero() in S2 or not S1.isdisjoint(S2)): if return_automorphism: return False, None else: return False # now that we know that (S1,S2) is a partition, we look for an invertible # element b that maps S1 to S2 by multiplication for b in range(2,n): if GCD(b,n) == 1 and all(b*x in S2 for x in S1): if return_automorphism: return True, b else: return True if return_automorphism: return False, None else: return False
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 as_permutation_group(self): r""" Return the permutation group corresponding to ``self``. EXAMPLES:: sage: C = ColoredPermutations(4, 3) sage: C.as_permutation_group() Complex reflection group G(4, 1, 3) as a permutation group """ from sage.groups.perm_gps.permgroup_named import ComplexReflectionGroup return ComplexReflectionGroup(self._m, 1, self._n) 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): r""" 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): r""" 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
def _is_a_splitting(S1, S2, n, return_automorphism=False): r""" Check wether ``(S1,S2)`` is a splitting of `\ZZ/n\ZZ`. A splitting of `R = \ZZ/n\ZZ` is a pair of subsets of `R` which is a partition of `R \\backslash \{0\}` and such that there exists an element `r` of `R` such that `r S_1 = S_2` and `r S_2 = S_1` (where `r S` is the point-wise multiplication of the elements of `S` by `r`). Splittings are useful for computing idempotents in the quotient ring `Q = GF(q)[x]/(x^n-1)`. INPUT: - ``S1, S2`` -- disjoint sublists partitioning ``[1, 2, ..., n-1]`` - ``n`` (integer) - ``return_automorphism`` (boolean) -- whether to return the automorphism exchanging `S_1` and `S_2`. OUTPUT: If ``return_automorphism is False`` (default) the function returns boolean values. Otherwise, it returns a pair ``(b, r)`` where ``b`` is a boolean indicating whether `S1`, `S2` is a splitting of `n`, and `r` is such that `r S_1 = S_2` and `r S_2 = S_1` (if `b` is ``False``, `r` is equal to ``None``). EXAMPLES:: sage: from sage.coding.code_constructions import _is_a_splitting sage: _is_a_splitting([1,2],[3,4],5) True sage: _is_a_splitting([1,2],[3,4],5,return_automorphism=True) (True, 4) sage: _is_a_splitting([1,3],[2,4,5,6],7) False sage: _is_a_splitting([1,3,4],[2,5,6],7) False sage: for P in SetPartitions(6,[3,3]): ....: res,aut= _is_a_splitting(P[0],P[1],7,return_automorphism=True) ....: if res: ....: print((aut, P)) (3, {{1, 2, 4}, {3, 5, 6}}) (6, {{1, 2, 3}, {4, 5, 6}}) (6, {{1, 3, 5}, {2, 4, 6}}) (6, {{1, 4, 5}, {2, 3, 6}}) We illustrate now how to find idempotents in quotient rings:: sage: n = 11; q = 3 sage: C = Zmod(n).cyclotomic_cosets(q); C [[0], [1, 3, 4, 5, 9], [2, 6, 7, 8, 10]] sage: S1 = C[1] sage: S2 = C[2] sage: _is_a_splitting(S1,S2,11) True sage: F = GF(q) sage: P.<x> = PolynomialRing(F,"x") sage: I = Ideal(P,[x^n-1]) sage: Q.<x> = QuotientRing(P,I) sage: i1 = -sum([x^i for i in S1]); i1 2*x^9 + 2*x^5 + 2*x^4 + 2*x^3 + 2*x sage: i2 = -sum([x^i for i in S2]); i2 2*x^10 + 2*x^8 + 2*x^7 + 2*x^6 + 2*x^2 sage: i1^2 == i1 True sage: i2^2 == i2 True sage: (1-i1)^2 == 1-i1 True sage: (1-i2)^2 == 1-i2 True We return to dealing with polynomials (rather than elements of quotient rings), so we can construct cyclic codes:: sage: P.<x> = PolynomialRing(F,"x") sage: i1 = -sum([x^i for i in S1]) sage: i2 = -sum([x^i for i in S2]) sage: i1_sqrd = (i1^2).quo_rem(x^n-1)[1] sage: i1_sqrd == i1 True sage: i2_sqrd = (i2^2).quo_rem(x^n-1)[1] sage: i2_sqrd == i2 True sage: C1 = codes.CyclicCode(length = n, generator_pol = gcd(i1, x^n - 1)) sage: C2 = codes.CyclicCode(length = n, generator_pol = gcd(1-i2, x^n - 1)) sage: C1.dual_code().systematic_generator_matrix() == C2.systematic_generator_matrix() True This is a special case of Theorem 6.4.3 in [HP2003]_. """ R = IntegerModRing(n) S1 = set(R(x) for x in S1) S2 = set(R(x) for x in S2) # we first check whether (S1,S2) is a partition of R - {0} if (len(S1) + len(S2) != n - 1 or len(S1) != len(S2) or R.zero() in S1 or R.zero() in S2 or not S1.isdisjoint(S2)): if return_automorphism: return False, None else: return False # now that we know that (S1,S2) is a partition, we look for an invertible # element b that maps S1 to S2 by multiplication for b in Integer(n).coprime_integers(n): if b >= 2 and all(b * x in S2 for x in S1): if return_automorphism: return True, b else: return True if return_automorphism: return False, None else: return False
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