{{{id=35| ZZ.category() /// Category of euclidean domains }}} {{{id=36| ZZ.categories() /// [Category of euclidean domains, Category of principal ideal domains, Category of unique factorization domains, Category of gcd domains, Category of integral domains, Category of domains, Category of commutative rings, Category of rings, Category of rngs, Category of semirings, Category of associative additive commutative additive associative additive unital distributive magmas and additive magmas, Category of additive commutative additive associative additive unital distributive magmas and additive magmas, Category of additive commutative additive associative distributive magmas and additive magmas, Category of additive associative distributive magmas and additive magmas, Category of distributive magmas and additive magmas, Category of magmas and additive magmas, Category of commutative monoids, Category of monoids, Category of semigroups, Category of commutative magmas, Category of unital magmas, Category of magmas, Category of commutative additive groups, Category of additive groups, Category of additive inverse additive unital additive magmas, Category of commutative additive monoids, Category of additive monoids, Category of additive unital additive magmas, Category of commutative additive semigroups, Category of additive commutative additive magmas, Category of additive semigroups, Category of additive magmas, Category of sets, Category of sets with partial maps, Category of objects] }}}

A class is a way to model an object and operations, called methods, on these objects. Below is an example of a class which models a signed permutation given by the pair $((s_1, \ldots, s_n), \sigma)$ where $\sigma \in S_n$. We also write a class which models the collection of all signed permutations.

{{{id=1| class SignedPermutation: def __init__(self, signs, perm): self._signs = signs self._perm = perm def __repr__(self): return repr([self._signs, self._perm]) def one_line_form(self): return [self._signs[i] * p for i,p in enumerate(self._perm)] class SignedPermutations: def __init__(self, n): self._n = n def __iter__(self): C = CartesianProduct( *([[-1,1]]*self._n) ) for p in Permutations(self._n): for c in C: yield SignedPermutation(c, p) /// }}} {{{id=37| [[-1, 1]]*5 /// [[-1, 1], [-1, 1], [-1, 1], [-1, 1], [-1, 1]] }}} {{{id=3| P = SignedPermutations(2) for sp in P: sp.one_line_form() /// [-1, -2] [-1, 2] [1, -2] [1, 2] [-2, -1] [-2, 1] [2, -1] [2, 1] }}}

Now we can inherit from one or more classes, in which case we get all of the methods that are currently implemented for that. However we don't automatically get any (instance) attributes which were defined in the __init__ method unless we explicitly call up in the child class if we override __init__.

{{{id=33| class EvenSignedPermutations(SignedPermutations): def __iter__(self): for s in SignedPermutations.__iter__(self): total = 0 for pm in s._signs: if pm == -1: total += 1 if total % 2 == 0: yield s for sp in EvenSignedPermutations(2): sp.one_line_form() /// [-1, -2] [1, 2] [-2, -1] [2, 1] }}} {{{id=34| class pSignedPermutations(SignedPermutations): def __init__(self, n, p): self._p = p SignedPermutations.__init__(self, n) # We need this, otherwise self._n would not be defined def __iter__(self): for s in SignedPermutations.__iter__(self): total = 0 for pm in s._signs: if pm == -1: total += 1 if total % self._p == 0: yield s for sp in pSignedPermutations(3, 4): sp.one_line_form() /// [1, 2, 3] [1, 3, 2] [2, 1, 3] [2, 3, 1] [3, 1, 2] [3, 2, 1] }}}

Recall from Nicolas' talk that the collection of all signed permutations is an example of a Parent, and a particular signed permutation is called an Element. Signed permutations form a group with the product given by \[ ((s_1, \ldots s_n), \sigma) \cdot ((t_1, \ldots, t_n), \tau) = ((s_1 t_{\sigma^{-1}(1)}, \ldots, s_n t_{\sigma^{-1}(n)}), \sigma \tau). \] Therefore the category will be Groups, and so we reimplement the above using Sage's parents and elements.

We first need to import somethings.

{{{id=16| from sage.structure.element import Element from sage.structure.unique_representation import UniqueRepresentation /// }}}

Now we create our parent and element classes and sprinkle in a little black magic.

{{{id=5| class SignedPermutation(Element): def __init__(self, parent, signs, perm): self._signs = signs self._perm = perm Element.__init__(self, parent=parent) def _repr_(self): return repr([self._signs, self._perm]) def _mul_(self, rhs): signs = [self._signs[i] * rhs._signs[val-1] for i,val in enumerate(~self._perm)] return self.__class__(self.parent(), signs, self._perm * rhs._perm) class SignedPermutations(Parent, UniqueRepresentation): def __init__(self, n): self._n = n Parent.__init__(self, category=Groups().Finite()) def _repr_(self): return "Signed permutations of size {}".format(self._n) def __iter__(self): C = CartesianProduct(*[[-1,1]]*self._n) for p in Permutations(self._n): for c in C: yield self.element_class(self, c, p) Element = SignedPermutation /// }}} {{{id=7| S = SignedPermutations(3) S /// Signed permutations of size 3 }}}

Using categories, we gain methods that are implemented generically for all objects of the category (and all super categories). For example, we can determine the cardinality of the group by iterating over all elements and we have the method cardinality given to us to use. (Note that the generic implementations may not be the most efficient; here we have a formula for the cardinality and for large $n$, the default implementation by iteration would become very slow.)

{{{id=8| S.cardinality() /// 48 }}} {{{id=38| x = S[25] /// }}} {{{id=9| S[25] /// [[-1, -1, 1], [2, 3, 1]] }}} {{{id=10| S[25]*S[25] /// [[-1, 1, -1], [3, 1, 2]] }}}

Recall that we can alternatively define signed permtuations by the wreath product $C_2 \wr S_n$, where $C_m$ is the cyclic group of order $m$. More generally, we can define the $m$-colored permutation group on $n$ letters by $P_n^m = C_m \wr S_n$.

This is all good, except we know a bit more aout $P_n^m$, in that these are an example of the complex reflection group $G(m, p, n)$ when $p = 1$. Complex reflection groups are known to have a lot of additional structure associated to them. For this example, we require the complex reflection group to implement a method degrees, which we then use to compute a polynomial of counting the dimesions fixed points subspaces \[ \prod_{i=1}^{\ell} (q + d_i - 1) = \sum_{w \in W} q^{\dim V^w}. \]

Now since all complex reflection groups are finite, this will be a subcategory of finite groups. In this category, we have two methods, one of which requires all objects in this category to implement a method degrees and gives them the method fixed_point_polynomial.

{{{id=12| class ComplexReflectionGroups(Category): @cached_method def super_categories(self): return [Groups().Finite()] class ParentMethods: @abstract_method def degrees(self): pass def fixed_point_polynomial(self, q): return prod(q + d - 1 for d in self.degrees()) /// }}} {{{id=18| ComplexReflectionGroups() /// Category of complex reflection groups }}} {{{id=19| for cat in ComplexReflectionGroups().all_super_categories(): cat /// Category of complex reflection groups Category of finite groups Category of finite monoids Category of groups Category of monoids Category of finite semigroups Category of semigroups Category of inverse unital magmas Category of unital magmas Category of magmas Category of finite enumerated sets Category of enumerated sets Category of finite sets Category of sets Category of sets with partial maps Category of objects }}} {{{id=17| class ColoredPermutations(Parent, UniqueRepresentation): def __init__(self, m, n): self._m = m self._n = n Parent.__init__(self, category=ComplexReflectionGroups()) def _repr_(self): return "{}-colored permutations of size {}".format(self._m, self._n) def __iter__(self): G = groups.permutation.Cyclic(self._m) C = CartesianProduct(*[G]*self._n) for p in Permutations(self._n): for c in C: yield self.element_class(self, c, p) def degrees(self): if self._m == 1: # Special case for the usual symmetric group return range(2, self._n+1) return [self._m * i for i in range(1, self._n+1)] Element = SignedPermutation # This is an abuse, but the class we implemented is general enough to work for our purposes /// }}} {{{id=20| P32 = ColoredPermutations(3,2) /// }}} {{{id=21| P32 /// 3-colored permutations of size 2 }}} {{{id=22| P32.cardinality() /// 18 }}} {{{id=24| P32.degrees() /// [3, 6] }}} {{{id=25| R. = QQ[] P32.fixed_point_polynomial(q) /// q^2 + 7*q + 10 }}} {{{id=26| P32.fixed_point_polynomial(1) == P32.cardinality() /// True }}} {{{id=28| C = Monoids() /// }}} {{{id=40| C.Finite() /// Category of finite monoids }}} {{{id=41| C.Finite().Inverse() /// Category of finite groups }}} {{{id=42| C = Magmas() /// }}} {{{id=43| C.Finite().Unital().Inverse().Commutative().Associative() /// Category of finite commutative groups }}} {{{id=44| Rings().Division().Finite_extra_super_categories() /// (Category of commutative magmas,) }}} {{{id=45| /// }}}