r""" Additive groups """ #***************************************************************************** # Copyright (C) 2013 Nicolas M. Thiery <nthiery at users.sf.net> # # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #****************************************************************************** from sage.misc.lazy_import import LazyImport from sage.categories.category_with_axiom import CategoryWithAxiom_singleton, CategoryWithAxiom from sage.categories.algebra_functor import AlgebrasCategory from sage.categories.additive_monoids import AdditiveMonoids Groups = LazyImport('sage.categories.groups', 'Groups', at_startup=True) class AdditiveGroups(CategoryWithAxiom_singleton): r""" The category of additive groups. An *additive group* is a set with an internal binary operation `+` which is associative, admits a zero, and where every element can be negated. EXAMPLES:: sage: from sage.categories.additive_groups import AdditiveGroups sage: from sage.categories.additive_monoids import AdditiveMonoids sage: AdditiveGroups() Category of additive groups sage: AdditiveGroups().super_categories()
class WeylGroups(Category_singleton): r""" The category of Weyl groups See the :wikipedia:`Wikipedia page of Weyl Groups <Weyl_group>`. EXAMPLES:: sage: WeylGroups() Category of weyl groups sage: WeylGroups().super_categories() [Category of coxeter groups] Here are some examples:: sage: WeylGroups().example() # todo: not implemented sage: FiniteWeylGroups().example() The symmetric group on {0, ..., 3} sage: AffineWeylGroups().example() # todo: not implemented sage: WeylGroup(["B", 3]) Weyl Group of type ['B', 3] (as a matrix group acting on the ambient space) This one will eventually be also in this category:: sage: SymmetricGroup(4) Symmetric group of order 4! as a permutation group TESTS:: sage: C = WeylGroups() sage: TestSuite(C).run() """ def super_categories(self): r""" EXAMPLES:: sage: WeylGroups().super_categories() [Category of coxeter groups] """ return [CoxeterGroups()] def additional_structure(self): r""" Return ``None``. Indeed, the category of Weyl groups defines no additional structure: Weyl groups are a special class of Coxeter groups. .. SEEALSO:: :meth:`Category.additional_structure` .. TODO:: Should this category be a :class:`CategoryWithAxiom`? EXAMPLES:: sage: WeylGroups().additional_structure() """ return None Finite = LazyImport('sage.categories.finite_weyl_groups', 'FiniteWeylGroups') class ParentMethods: def pieri_factors(self, *args, **keywords): r""" Returns the set of Pieri factors in this Weyl group. For any type, the set of Pieri factors forms a lower ideal in Bruhat order, generated by all the conjugates of some special element of the Weyl group. In type `A_n`, this special element is `s_n\cdots s_1`, and the conjugates are obtained by rotating around this reduced word. These are used to compute Stanley symmetric functions. .. SEEALSO:: * :meth:`WeylGroups.ElementMethods.stanley_symmetric_function` * :mod:`sage.combinat.root_system.pieri_factors` EXAMPLES:: sage: W = WeylGroup(['A',5,1]) sage: PF = W.pieri_factors() sage: PF.cardinality() 63 sage: W = WeylGroup(['B',3]) sage: PF = W.pieri_factors() sage: [w.reduced_word() for w in PF] [[1, 2, 3, 2, 1], [1, 2, 3, 2], [2, 3, 2], [2, 3], [3, 1, 2, 1], [1, 2, 1], [2], [1, 2], [1], [], [2, 1], [3, 2, 1], [3, 1], [2, 3, 2, 1], [3], [3, 2], [1, 2, 3], [1, 2, 3, 1], [3, 1, 2], [2, 3, 1]] sage: W = WeylGroup(['C',4,1]) sage: PF = W.pieri_factors() sage: W.from_reduced_word([3,2,0]) in PF True """ # Do not remove this line which makes sure the pieri factor # code is properly inserted inside the Cartan Types import sage.combinat.root_system.pieri_factors ct = self.cartan_type() if hasattr(ct, "PieriFactors"): return ct.PieriFactors(self, *args, **keywords) raise NotImplementedError("Pieri factors for type {}".format(ct)) @cached_method def quantum_bruhat_graph(self, index_set=()): r""" Returns the quantum Bruhat graph of the quotient of the Weyl group by a parabolic subgroup `W_J`. INPUT: - ``index_set`` -- a tuple `J` of nodes of the Dynkin diagram (default: ()) By default, the value for ``index_set`` indicates that the subgroup is trivial and the quotient is the full Weyl group. EXAMPLES:: sage: W = WeylGroup(['A',3], prefix="s") sage: g = W.quantum_bruhat_graph((1,3)) sage: g Parabolic Quantum Bruhat Graph of Weyl Group of type ['A', 3] (as a matrix group acting on the ambient space) for nodes (1, 3): Digraph on 6 vertices sage: g.vertices() [s2*s3*s1*s2, s3*s1*s2, s1*s2, s3*s2, s2, 1] sage: g.edges() [(s2*s3*s1*s2, s2, alpha[2]), (s3*s1*s2, s2*s3*s1*s2, alpha[1] + alpha[2] + alpha[3]), (s3*s1*s2, 1, alpha[2]), (s1*s2, s3*s1*s2, alpha[2] + alpha[3]), (s3*s2, s3*s1*s2, alpha[1] + alpha[2]), (s2, s1*s2, alpha[1] + alpha[2]), (s2, s3*s2, alpha[2] + alpha[3]), (1, s2, alpha[2])] sage: W = WeylGroup(['A',3,1], prefix="s") sage: g = W.quantum_bruhat_graph() Traceback (most recent call last): ... ValueError: The Cartan type ['A', 3, 1] is not finite """ if not self.cartan_type().is_finite(): raise ValueError("The Cartan type {} is not finite".format( self.cartan_type())) from sage.graphs.digraph import DiGraph WP = [x for x in self if x == x.coset_representative(index_set)] return DiGraph( [[x, i[0], i[1]] for x in WP for i in x.quantum_bruhat_successors(index_set, roots=True)], name="Parabolic Quantum Bruhat Graph of %s for nodes %s" % (self, index_set)) class ElementMethods: def is_pieri_factor(self): r""" Returns whether ``self`` is a Pieri factor, as used for computing Stanley symmetric functions. .. SEEALSO:: * :meth:`stanley_symmetric_function` * :meth:`WeylGroups.ParentMethods.pieri_factors` EXAMPLES:: sage: W = WeylGroup(['A',5,1]) sage: W.from_reduced_word([3,2,5]).is_pieri_factor() True sage: W.from_reduced_word([3,2,4,5]).is_pieri_factor() False sage: W = WeylGroup(['C',4,1]) sage: W.from_reduced_word([0,2,1]).is_pieri_factor() True sage: W.from_reduced_word([0,2,1,0]).is_pieri_factor() False sage: W = WeylGroup(['B',3]) sage: W.from_reduced_word([3,2,3]).is_pieri_factor() False sage: W.from_reduced_word([2,1,2]).is_pieri_factor() True """ return self in self.parent().pieri_factors() def left_pieri_factorizations(self, max_length=infinity): r""" Returns all factorizations of ``self`` as `uv`, where `u` is a Pieri factor and `v` is an element of the Weyl group. .. SEEALSO:: * :meth:`WeylGroups.ParentMethods.pieri_factors` * :mod:`sage.combinat.root_system.pieri_factors` EXAMPLES: If we take `w = w_0` the maximal element of a strict parabolic subgroup of type `A_{n_1} \times \cdots \times A_{n_k}`, then the Pieri factorizations are in correspondence with all Pieri factors, and there are `\prod 2^{n_i}` of them:: sage: W = WeylGroup(['A', 4, 1]) sage: W.from_reduced_word([]).left_pieri_factorizations().cardinality() 1 sage: W.from_reduced_word([1]).left_pieri_factorizations().cardinality() 2 sage: W.from_reduced_word([1,2,1]).left_pieri_factorizations().cardinality() 4 sage: W.from_reduced_word([1,2,3,1,2,1]).left_pieri_factorizations().cardinality() 8 sage: W.from_reduced_word([1,3]).left_pieri_factorizations().cardinality() 4 sage: W.from_reduced_word([1,3,4,3]).left_pieri_factorizations().cardinality() 8 sage: W.from_reduced_word([2,1]).left_pieri_factorizations().cardinality() 3 sage: W.from_reduced_word([1,2]).left_pieri_factorizations().cardinality() 2 sage: [W.from_reduced_word([1,2]).left_pieri_factorizations(max_length=i).cardinality() for i in [-1, 0, 1, 2]] [0, 1, 2, 2] sage: W = WeylGroup(['C',4,1]) sage: w = W.from_reduced_word([0,3,2,1,0]) sage: w.left_pieri_factorizations().cardinality() 7 sage: [(u.reduced_word(),v.reduced_word()) for (u,v) in w.left_pieri_factorizations()] [([], [3, 2, 0, 1, 0]), ([0], [3, 2, 1, 0]), ([3], [2, 0, 1, 0]), ([3, 0], [2, 1, 0]), ([3, 2], [0, 1, 0]), ([3, 2, 0], [1, 0]), ([3, 2, 0, 1], [0])] sage: W = WeylGroup(['B',4,1]) sage: W.from_reduced_word([0,2,1,0]).left_pieri_factorizations().cardinality() 6 """ pieri_factors = self.parent().pieri_factors() def predicate(u): return u in pieri_factors and u.length() <= max_length return self.binary_factorizations(predicate) @cached_in_parent_method def stanley_symmetric_function_as_polynomial(self, max_length=infinity): r""" Returns a multivariate generating function for the number of factorizations of a Weyl group element into Pieri factors of decreasing length, weighted by a statistic on Pieri factors. .. SEEALSO:: * :meth:`stanley_symmetric_function` * :meth:`WeylGroups.ParentMethods.pieri_factors` * :mod:`sage.combinat.root_system.pieri_factors` INPUT: - ``self`` -- an element `w` of a Weyl group `W` - ``max_length`` -- a non negative integer or infinity (default: infinity) Returns the generating series for the Pieri factorizations `w = u_1 \cdots u_k`, where `u_i` is a Pieri factor for all `i`, `l(w) = \sum_{i=1}^k l(u_i)` and ``max_length`` `\geq l(u_1) \geq \cdots \geq l(u_k)`. A factorization `u_1 \cdots u_k` contributes a monomial of the form `\prod_i x_{l(u_i)}`, with coefficient given by `\prod_i 2^{c(u_i)}`, where `c` is a type-dependent statistic on Pieri factors, as returned by the method ``u[i].stanley_symm_poly_weight()``. EXAMPLES:: sage: W = WeylGroup(['A', 3, 1]) sage: W.from_reduced_word([]).stanley_symmetric_function_as_polynomial() 1 sage: W.from_reduced_word([1]).stanley_symmetric_function_as_polynomial() x1 sage: W.from_reduced_word([1,2]).stanley_symmetric_function_as_polynomial() x1^2 sage: W.from_reduced_word([2,1]).stanley_symmetric_function_as_polynomial() x1^2 + x2 sage: W.from_reduced_word([1,2,1]).stanley_symmetric_function_as_polynomial() 2*x1^3 + x1*x2 sage: W.from_reduced_word([1,2,1,0]).stanley_symmetric_function_as_polynomial() 3*x1^4 + 2*x1^2*x2 + x2^2 + x1*x3 sage: W.from_reduced_word([1,2,3,1,2,1,0]).stanley_symmetric_function_as_polynomial() # long time 22*x1^7 + 11*x1^5*x2 + 5*x1^3*x2^2 + 3*x1^4*x3 + 2*x1*x2^3 + x1^2*x2*x3 sage: W.from_reduced_word([3,1,2,0,3,1,0]).stanley_symmetric_function_as_polynomial() # long time 8*x1^7 + 4*x1^5*x2 + 2*x1^3*x2^2 + x1*x2^3 sage: W = WeylGroup(['C',3,1]) sage: W.from_reduced_word([0,2,1,0]).stanley_symmetric_function_as_polynomial() 32*x1^4 + 16*x1^2*x2 + 8*x2^2 + 4*x1*x3 sage: W = WeylGroup(['B',3,1]) sage: W.from_reduced_word([3,2,1]).stanley_symmetric_function_as_polynomial() 2*x1^3 + x1*x2 + 1/2*x3 Algorithm: Induction on the left Pieri factors. Note that this induction preserves subsets of `W` which are stable by taking right factors, and in particular Grassmanian elements. """ W = self.parent() pieri_factors = W.pieri_factors() R = QQ[','.join('x%s' % l for l in range(1, pieri_factors.max_length() + 1))] x = R.gens() if self.is_one(): return R.one() return R( sum(2**(pieri_factors.stanley_symm_poly_weight(u)) * x[u.length() - 1] * v.stanley_symmetric_function_as_polynomial( max_length=u.length()) for (u, v) in self.left_pieri_factorizations(max_length) if u != W.one())) def stanley_symmetric_function(self): r""" Return the affine Stanley symmetric function indexed by ``self``. INPUT: - ``self`` -- an element `w` of a Weyl group Returns the affine Stanley symmetric function indexed by `w`. Stanley symmetric functions are defined as generating series of the factorizations of `w` into Pieri factors and weighted by a statistic on Pieri factors. .. SEEALSO:: * :meth:`stanley_symmetric_function_as_polynomial` * :meth:`WeylGroups.ParentMethods.pieri_factors` * :mod:`sage.combinat.root_system.pieri_factors` EXAMPLES:: sage: W = WeylGroup(['A', 3, 1]) sage: W.from_reduced_word([3,1,2,0,3,1,0]).stanley_symmetric_function() 8*m[1, 1, 1, 1, 1, 1, 1] + 4*m[2, 1, 1, 1, 1, 1] + 2*m[2, 2, 1, 1, 1] + m[2, 2, 2, 1] sage: A = AffinePermutationGroup(['A',3,1]) sage: A.from_reduced_word([3,1,2,0,3,1,0]).stanley_symmetric_function() 8*m[1, 1, 1, 1, 1, 1, 1] + 4*m[2, 1, 1, 1, 1, 1] + 2*m[2, 2, 1, 1, 1] + m[2, 2, 2, 1] sage: W = WeylGroup(['C',3,1]) sage: W.from_reduced_word([0,2,1,0]).stanley_symmetric_function() 32*m[1, 1, 1, 1] + 16*m[2, 1, 1] + 8*m[2, 2] + 4*m[3, 1] sage: W = WeylGroup(['B',3,1]) sage: W.from_reduced_word([3,2,1]).stanley_symmetric_function() 2*m[1, 1, 1] + m[2, 1] + 1/2*m[3] sage: W = WeylGroup(['B',4]) sage: w = W.from_reduced_word([3,2,3,1]) sage: w.stanley_symmetric_function() # long time (6s on sage.math, 2011) 48*m[1, 1, 1, 1] + 24*m[2, 1, 1] + 12*m[2, 2] + 8*m[3, 1] + 2*m[4] sage: A = AffinePermutationGroup(['A',4,1]) sage: a = A([-2,0,1,4,12]) sage: a.stanley_symmetric_function() 6*m[1, 1, 1, 1, 1, 1, 1, 1] + 5*m[2, 1, 1, 1, 1, 1, 1] + 4*m[2, 2, 1, 1, 1, 1] + 3*m[2, 2, 2, 1, 1] + 2*m[2, 2, 2, 2] + 4*m[3, 1, 1, 1, 1, 1] + 3*m[3, 2, 1, 1, 1] + 2*m[3, 2, 2, 1] + 2*m[3, 3, 1, 1] + m[3, 3, 2] + 3*m[4, 1, 1, 1, 1] + 2*m[4, 2, 1, 1] + m[4, 2, 2] + m[4, 3, 1] One more example (:trac:`14095`):: sage: G = SymmetricGroup(4) sage: w = G.from_reduced_word([3,2,3,1]) sage: w.stanley_symmetric_function() 3*m[1, 1, 1, 1] + 2*m[2, 1, 1] + m[2, 2] + m[3, 1] REFERENCES: .. [BH1994] S. Billey, M. Haiman. *Schubert polynomials for the classical groups*. J. Amer. Math. Soc., 1994. .. [Lam2008] T. Lam. *Schubert polynomials for the affine Grassmannian*. J. Amer. Math. Soc., 2008. .. [LSS2009] T. Lam, A. Schilling, M. Shimozono. *Schubert polynomials for the affine Grassmannian of the symplectic group*. Mathematische Zeitschrift 264(4) (2010) 765-811 (:arxiv:`0710.2720`) .. [Pon2010] S. Pon. *Types B and D affine Stanley symmetric functions*, unpublished PhD Thesis, UC Davis, 2010. """ import sage.combinat.sf m = sage.combinat.sf.sf.SymmetricFunctions(QQ).monomial() return m.from_polynomial_exp( self.stanley_symmetric_function_as_polynomial()) @cached_in_parent_method def reflection_to_root(self): r""" Returns the root associated with the reflection ``self``. EXAMPLES:: sage: W=WeylGroup(['C',2],prefix="s") sage: r=W.from_reduced_word([1,2,1]) sage: r.reflection_to_root() 2*alpha[1] + alpha[2] sage: r=W.from_reduced_word([1,2]) sage: r.reflection_to_root() Traceback (most recent call last): ... ValueError: s1*s2 is not a reflection sage: W.long_element().reflection_to_root() Traceback (most recent call last): ... ValueError: s2*s1*s2*s1 is not a reflection """ i = self.first_descent() if i is None: raise ValueError("{} is not a reflection".format(self)) if self == self.parent().simple_reflection(i): return self.parent().cartan_type().root_system().root_lattice( ).simple_root(i) w = self.apply_simple_reflection(i, side='right') if not w.has_descent(i, side='left'): raise ValueError("{} is not a reflection".format(self)) return ((w.apply_simple_reflection( i, side='left')).reflection_to_root()).simple_reflection(i) @cached_in_parent_method def reflection_to_coroot(self): r""" Returns the coroot associated with the reflection ``self``. EXAMPLES:: sage: W=WeylGroup(['C',2],prefix="s") sage: r=W.from_reduced_word([1,2,1]) sage: r.reflection_to_coroot() alphacheck[1] + alphacheck[2] sage: r=W.from_reduced_word([1,2]) sage: r.reflection_to_coroot() Traceback (most recent call last): ... ValueError: s1*s2 is not a reflection sage: W.long_element().reflection_to_coroot() Traceback (most recent call last): ... ValueError: s2*s1*s2*s1 is not a reflection """ i = self.first_descent() if i is None: raise ValueError("{} is not a reflection".format(self)) if self == self.parent().simple_reflection(i): return self.parent().cartan_type().root_system().root_lattice( ).simple_coroot(i) w = self.apply_simple_reflection(i, side='right') if not w.has_descent(i, side='left'): raise ValueError("{} is not a reflection".format(self)) return ((w.apply_simple_reflection( i, side='left')).reflection_to_coroot()).simple_reflection(i) def inversions(self, side='right', inversion_type='reflections'): """ Returns the set of inversions of ``self``. INPUT: - ``side`` -- 'right' (default) or 'left' - ``inversion_type`` -- 'reflections' (default), 'roots', or 'coroots'. OUTPUT: For reflections, the set of reflections r in the Weyl group such that ``self`` ``r`` < ``self``. For (co)roots, the set of positive (co)roots that are sent by ``self`` to negative (co)roots; their associated reflections are described above. If ``side`` is 'left', the inverse Weyl group element is used. EXAMPLES:: sage: W=WeylGroup(['C',2], prefix="s") sage: w=W.from_reduced_word([1,2]) sage: w.inversions() [s2, s2*s1*s2] sage: w.inversions(inversion_type = 'reflections') [s2, s2*s1*s2] sage: w.inversions(inversion_type = 'roots') [alpha[2], alpha[1] + alpha[2]] sage: w.inversions(inversion_type = 'coroots') [alphacheck[2], alphacheck[1] + 2*alphacheck[2]] sage: w.inversions(side = 'left') [s1, s1*s2*s1] sage: w.inversions(side = 'left', inversion_type = 'roots') [alpha[1], 2*alpha[1] + alpha[2]] sage: w.inversions(side = 'left', inversion_type = 'coroots') [alphacheck[1], alphacheck[1] + alphacheck[2]] """ if side == 'left': self = self.inverse() reflections = self.inversions_as_reflections() if inversion_type == 'reflections': return reflections if inversion_type == 'roots': return [r.reflection_to_root() for r in reflections] if inversion_type == 'coroots': return [r.reflection_to_coroot() for r in reflections] raise ValueError( "inversion_type {} is invalid".format(inversion_type)) def inversion_arrangement(self, side='right'): r""" Return the inversion hyperplane arrangement of ``self``. INPUT: - ``side`` -- ``'right'`` (default) or ``'left'`` OUTPUT: A (central) hyperplane arrangement whose hyperplanes correspond to the inversions of ``self`` given as roots. The ``side`` parameter determines on which side to compute the inversions. EXAMPLES:: sage: W = WeylGroup(['A',3]) sage: w = W.from_reduced_word([1, 2, 3, 1, 2]) sage: A = w.inversion_arrangement(); A Arrangement of 5 hyperplanes of dimension 3 and rank 3 sage: A.hyperplanes() (Hyperplane 0*a1 + 0*a2 + a3 + 0, Hyperplane 0*a1 + a2 + 0*a3 + 0, Hyperplane 0*a1 + a2 + a3 + 0, Hyperplane a1 + a2 + 0*a3 + 0, Hyperplane a1 + a2 + a3 + 0) The identity element gives the empty arrangement:: sage: W = WeylGroup(['A',3]) sage: W.one().inversion_arrangement() Empty hyperplane arrangement of dimension 3 """ inv = self.inversions(side=side, inversion_type='roots') from sage.geometry.hyperplane_arrangement.arrangement import HyperplaneArrangements I = self.parent().cartan_type().index_set() H = HyperplaneArrangements(QQ, tuple(['a{}'.format(i) for i in I])) gens = H.gens() if not inv: return H() return H( [sum(c * gens[I.index(i)] for (i, c) in root) for root in inv]) def bruhat_lower_covers_coroots(self): r""" Returns all 2-tuples (``v``, `\alpha`) where ``v`` is covered by ``self`` and `\alpha` is the positive coroot such that ``self`` = ``v`` `s_\alpha` where `s_\alpha` is the reflection orthogonal to `\alpha`. ALGORITHM: See :meth:`.bruhat_lower_covers` and :meth:`.bruhat_lower_covers_reflections` for Coxeter groups. EXAMPLES:: sage: W = WeylGroup(['A',3], prefix="s") sage: w = W.from_reduced_word([3,1,2,1]) sage: w.bruhat_lower_covers_coroots() [(s1*s2*s1, alphacheck[1] + alphacheck[2] + alphacheck[3]), (s3*s2*s1, alphacheck[2]), (s3*s1*s2, alphacheck[1])] """ return [(x[0], x[1].reflection_to_coroot()) for x in self.bruhat_lower_covers_reflections()] def bruhat_upper_covers_coroots(self): r""" Returns all 2-tuples (``v``, `\alpha`) where ``v`` is covers ``self`` and `\alpha` is the positive coroot such that ``self`` = ``v`` `s_\alpha` where `s_\alpha` is the reflection orthogonal to `\alpha`. ALGORITHM: See :meth:`~CoxeterGroups.ElementMethods.bruhat_upper_covers` and :meth:`.bruhat_upper_covers_reflections` for Coxeter groups. EXAMPLES:: sage: W = WeylGroup(['A',4], prefix="s") sage: w = W.from_reduced_word([3,1,2,1]) sage: w.bruhat_upper_covers_coroots() [(s1*s2*s3*s2*s1, alphacheck[3]), (s2*s3*s1*s2*s1, alphacheck[2] + alphacheck[3]), (s3*s4*s1*s2*s1, alphacheck[4]), (s4*s3*s1*s2*s1, alphacheck[1] + alphacheck[2] + alphacheck[3] + alphacheck[4])] """ return [(x[0], x[1].reflection_to_coroot()) for x in self.bruhat_upper_covers_reflections()] def quantum_bruhat_successors(self, index_set=None, roots=False, quantum_only=False): r""" Returns the successors of ``self`` in the parabolic quantum Bruhat graph. INPUT: - ``self`` -- a Weyl group element, which is assumed to be of minimum length in its coset with respect to the parabolic subgroup - ``index_set`` -- (default: None) indicates the set of simple reflections used to generate the parabolic subgroup; the default value indicates that the subgroup is the identity - ``roots`` -- (default: False) if True, returns the list of 2-tuples (``w``, `\alpha`) where ``w`` is a successor and `\alpha` is the positive root associated with the successor relation. - ``quantum_only`` -- (default: False) if True, returns only the quantum successors Returns the successors of ``self`` in the quantum Bruhat graph on the parabolic quotient of the Weyl group determined by the subset of Dynkin nodes ``index_set``. EXAMPLES:: sage: W = WeylGroup(['A',3], prefix="s") sage: w = W.from_reduced_word([3,1,2]) sage: w.quantum_bruhat_successors([1], roots = True) [(s3, alpha[2]), (s1*s2*s3*s2, alpha[3]), (s2*s3*s1*s2, alpha[1] + alpha[2] + alpha[3])] sage: w.quantum_bruhat_successors([1,3]) [1, s2*s3*s1*s2] sage: w.quantum_bruhat_successors(roots = True) [(s3*s1*s2*s1, alpha[1]), (s3*s1, alpha[2]), (s1*s2*s3*s2, alpha[3]), (s2*s3*s1*s2, alpha[1] + alpha[2] + alpha[3])] sage: w.quantum_bruhat_successors() [s3*s1*s2*s1, s3*s1, s1*s2*s3*s2, s2*s3*s1*s2] sage: w.quantum_bruhat_successors(quantum_only = True) [s3*s1] sage: w = W.from_reduced_word([2,3]) sage: w.quantum_bruhat_successors([1,3]) Traceback (most recent call last): ... ValueError: s2*s3 is not of minimum length in its coset of the parabolic subgroup generated by the reflections (1, 3) """ W = self.parent() if not W.cartan_type().is_finite(): raise ValueError("The Cartan type {} is not finite".format( W.cartan_type())) if index_set is None: index_set = [] else: index_set = [x for x in index_set] index_set = tuple(index_set) if self != self.coset_representative(index_set): raise ValueError( "{} is not of minimum length in its coset of the parabolic subgroup generated by the reflections {}" .format(self, index_set)) lattice = W.cartan_type().root_system().root_lattice() w_length_plus_one = self.length() + 1 successors = [] for alpha in lattice.nonparabolic_positive_roots(index_set): wr = self * W.from_reduced_word(alpha.associated_reflection()) wrc = wr.coset_representative(index_set) if wrc == wr and wr.length( ) == w_length_plus_one and not quantum_only: if roots: successors.append((wr, alpha)) else: successors.append(wr) elif alpha.quantum_root() and wrc.length( ) == w_length_plus_one - lattice.nonparabolic_positive_root_sum( index_set).scalar(alpha.associated_coroot()): if roots: successors.append((wrc, alpha)) else: successors.append(wrc) return successors
class HopfAlgebrasWithBasis(CategoryWithAxiom_over_base_ring): """ The category of Hopf algebras with a distinguished basis EXAMPLES:: sage: C = HopfAlgebrasWithBasis(QQ) sage: C Category of hopf algebras with basis over Rational Field sage: C.super_categories() [Category of hopf algebras over Rational Field, Category of bialgebras with basis over Rational Field] We now show how to use a simple Hopf algebra, namely the group algebra of the dihedral group (see also AlgebrasWithBasis):: sage: A = C.example(); A An example of Hopf algebra with basis: the group algebra of the Dihedral group of order 6 as a permutation group over Rational Field sage: A.__custom_name = "A" sage: A.category() Category of finite dimensional hopf algebras with basis over Rational Field sage: A.one_basis() () sage: A.one() B[()] sage: A.base_ring() Rational Field sage: A.basis().keys() Dihedral group of order 6 as a permutation group sage: [a,b] = A.algebra_generators() sage: a, b (B[(1,2,3)], B[(1,3)]) sage: a^3, b^2 (B[()], B[()]) sage: a*b B[(1,2)] sage: A.product # todo: not quite ... <bound method MyGroupAlgebra_with_category._product_from_product_on_basis_multiply of A> sage: A.product(b,b) B[()] sage: A.zero().coproduct() 0 sage: A.zero().coproduct().parent() A # A sage: a.coproduct() B[(1,2,3)] # B[(1,2,3)] sage: TestSuite(A).run(verbose=True) running ._test_additive_associativity() . . . pass running ._test_an_element() . . . pass running ._test_antipode() . . . pass running ._test_associativity() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass running ._test_characteristic() . . . pass running ._test_distributivity() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass running ._test_eq() . . . pass running ._test_new() . . . pass running ._test_nonzero_equal() . . . pass running ._test_not_implemented_methods() . . . pass running ._test_pickling() . . . pass pass running ._test_elements_eq_reflexive() . . . pass running ._test_elements_eq_symmetric() . . . pass running ._test_elements_eq_transitive() . . . pass running ._test_elements_neq() . . . pass running ._test_eq() . . . pass running ._test_new() . . . pass running ._test_not_implemented_methods() . . . pass running ._test_one() . . . pass running ._test_pickling() . . . pass running ._test_prod() . . . pass running ._test_some_elements() . . . pass running ._test_zero() . . . pass sage: A.__class__ <class 'sage.categories.examples.hopf_algebras_with_basis.MyGroupAlgebra_with_category'> sage: A.element_class <class 'sage.categories.examples.hopf_algebras_with_basis.MyGroupAlgebra_with_category.element_class'> Let us look at the code for implementing A:: sage: A?? # todo: not implemented TESTS:: sage: TestSuite(A).run() sage: TestSuite(C).run() """ def example(self, G=None): """ Returns an example of algebra with basis:: sage: HopfAlgebrasWithBasis(QQ['x']).example() An example of Hopf algebra with basis: the group algebra of the Dihedral group of order 6 as a permutation group over Univariate Polynomial Ring in x over Rational Field An other group can be specified as optional argument:: sage: HopfAlgebrasWithBasis(QQ).example(SymmetricGroup(4)) An example of Hopf algebra with basis: the group algebra of the Symmetric group of order 4! as a permutation group over Rational Field """ from sage.categories.examples.hopf_algebras_with_basis import MyGroupAlgebra from sage.groups.perm_gps.permgroup_named import DihedralGroup if G is None: G = DihedralGroup(3) return MyGroupAlgebra(self.base_ring(), G) # This is only correct in the finite dimensional / graded case # def dual(self): # """ # Returns the dual category # EXAMPLES: # The category of Hopf algebras over any field is self dual:: # sage: C = HopfAlgebrasWithBasis(QQ) # sage: C.dual() # Category of hopf algebras with basis over Rational Field # """ # return self FiniteDimensional = LazyImport( 'sage.categories.finite_dimensional_hopf_algebras_with_basis', 'FiniteDimensionalHopfAlgebrasWithBasis') Filtered = LazyImport('sage.categories.filtered_hopf_algebras_with_basis', 'FilteredHopfAlgebrasWithBasis') Graded = LazyImport('sage.categories.graded_hopf_algebras_with_basis', 'GradedHopfAlgebrasWithBasis') Super = LazyImport('sage.categories.super_hopf_algebras_with_basis', 'SuperHopfAlgebrasWithBasis') class ParentMethods: @abstract_method(optional=True) def antipode_on_basis(self, x): """ The antipode of the Hopf algebra on the basis (optional) INPUT: - ``x`` -- an index of an element of the basis of ``self`` Returns the antipode of the basis element indexed by ``x``. If this method is implemented, then :meth:`antipode` is defined from this by linearity. EXAMPLES:: sage: A = HopfAlgebrasWithBasis(QQ).example() sage: W = A.basis().keys(); W Dihedral group of order 6 as a permutation group sage: w = W.gen(0); w (1,2,3) sage: A.antipode_on_basis(w) B[(1,3,2)] """ @lazy_attribute def antipode(self): """ The antipode of this Hopf algebra. If :meth:`.antipode_basis` is available, this constructs the antipode morphism from ``self`` to ``self`` by extending it by linearity. Otherwise, :meth:`self.antipode_by_coercion` is used, if available. EXAMPLES:: sage: A = HopfAlgebrasWithBasis(ZZ).example(); A An example of Hopf algebra with basis: the group algebra of the Dihedral group of order 6 as a permutation group over Integer Ring sage: A = HopfAlgebrasWithBasis(QQ).example() sage: [a,b] = A.algebra_generators() sage: a, A.antipode(a) (B[(1,2,3)], B[(1,3,2)]) sage: b, A.antipode(b) (B[(1,3)], B[(1,3)]) TESTS:: sage: all(A.antipode(x) * x == A.one() for x in A.basis()) True """ if self.antipode_on_basis is not NotImplemented: # Should give the information that this is an anti-morphism of algebra return self._module_morphism(self.antipode_on_basis, codomain=self) elif hasattr(self, "antipode_by_coercion"): return self.antipode_by_coercion def _test_antipode(self, **options): r""" Test the antipode. An *antipode* `S` of a Hopf algebra is a linear endomorphism of the Hopf algebra that satisfies the following conditions (see :wikipedia:`HopfAlgebra`). - If `\mu` and `\Delta` denote the product and coproduct of the Hopf algebra, respectively, then `S` satisfies .. MATH:: \mu \circ (S \tensor 1) \circ \Delta = unit \circ counit \mu \circ (1 \tensor S) \circ \Delta = unit \circ counit - `S` is an *anti*-homomorphism These properties are tested on :meth:`some_elements`. TESTS:: sage: R = NonCommutativeSymmetricFunctions(QQ).ribbon() sage: R._test_antipode() :: sage: s = SymmetricFunctions(QQ).schur() sage: s._test_antipode() """ tester = self._tester(**options) S = self.antipode IS = lambda x: self.sum(c * self.monomial(t1) * S(self.monomial( t2)) for ((t1, t2), c) in x.coproduct()) SI = lambda x: self.sum(c * S(self.monomial(t1)) * self.monomial( t2) for ((t1, t2), c) in x.coproduct()) for x in tester.some_elements(): # antipode is an anti-homomorphism for y in tester.some_elements(): tester.assertTrue(S(x) * S(y) == S(y * x)) # mu * (S # I) * delta == counit * unit tester.assertTrue(SI(x) == self.counit(x) * self.one()) # mu * (I # S) * delta == counit * unit tester.assertTrue(IS(x) == self.counit(x) * self.one()) class ElementMethods: pass class TensorProducts(TensorProductsCategory): """ The category of hopf algebras with basis constructed by tensor product of hopf algebras with basis """ @cached_method def extra_super_categories(self): """ EXAMPLES:: sage: C = HopfAlgebrasWithBasis(QQ).TensorProducts() sage: C.extra_super_categories() [Category of hopf algebras with basis over Rational Field] sage: sorted(C.super_categories(), key=str) [Category of hopf algebras with basis over Rational Field, Category of tensor products of algebras with basis over Rational Field, Category of tensor products of hopf algebras over Rational Field] """ return [self.base_category()] class ParentMethods: # todo: antipode pass class ElementMethods: pass
class ParentMethods: @abstract_method def le(self, x, y): r""" Return whether `x \le y` in the poset ``self``. INPUT: - ``x``, ``y`` -- elements of ``self``. EXAMPLES:: sage: D = Poset((divisors(30), attrcall("divides"))) sage: D.le( 3, 6 ) True sage: D.le( 3, 3 ) True sage: D.le( 3, 5 ) False """ def lt(self, x, y): r""" Return whether `x < y` in the poset ``self``. INPUT: - ``x``, ``y`` -- elements of ``self``. This default implementation delegates the work to :meth:`le`. EXAMPLES:: sage: D = Poset((divisors(30), attrcall("divides"))) sage: D.lt( 3, 6 ) True sage: D.lt( 3, 3 ) False sage: D.lt( 3, 5 ) False """ return self.le(x, y) and x != y def ge(self, x, y): r""" Return whether `x \ge y` in the poset ``self``. INPUT: - ``x``, ``y`` -- elements of ``self``. This default implementation delegates the work to :meth:`le`. EXAMPLES:: sage: D = Poset((divisors(30), attrcall("divides"))) sage: D.ge( 6, 3 ) True sage: D.ge( 3, 3 ) True sage: D.ge( 3, 5 ) False """ return self.le(y, x) def gt(self, x, y): r""" Return whether `x > y` in the poset ``self``. INPUT: - ``x``, ``y`` -- elements of ``self``. This default implementation delegates the work to :meth:`lt`. EXAMPLES:: sage: D = Poset((divisors(30), attrcall("divides"))) sage: D.gt( 3, 6 ) False sage: D.gt( 3, 3 ) False sage: D.gt( 3, 5 ) False """ return self.lt(y, x) @abstract_method(optional=True) def upper_covers(self, x): r""" Return the upper covers of `x`, that is, the elements `y` such that `x<y` and there exists no `z` such that `x<z<y`. EXAMPLES:: sage: D = Poset((divisors(30), attrcall("divides"))) sage: D.upper_covers(3) [6, 15] """ @abstract_method(optional=True) def lower_covers(self, x): r""" Return the lower covers of `x`, that is, the elements `y` such that `y<x` and there exists no `z` such that `y<z<x`. EXAMPLES:: sage: D = Poset((divisors(30), attrcall("divides"))) sage: D.lower_covers(15) [3, 5] """ @abstract_method(optional=True) def order_ideal(self, elements): r""" Return the order ideal in ``self`` generated by the elements of an iterable ``elements``. A subset `I` of a poset is said to be an order ideal if, for any `x` in `I` and `y` such that `y \le x`, then `y` is in `I`. This is also called the lower set generated by these elements. EXAMPLES:: sage: B = Posets.BooleanLattice(4) sage: B.order_ideal([7,10]) [0, 1, 2, 3, 4, 5, 6, 7, 8, 10] """ @abstract_method(optional=True) def order_filter(self, elements): r""" Return the order filter generated by a list of elements. A subset `I` of a poset is said to be an order filter if, for any `x` in `I` and `y` such that `y \ge x`, then `y` is in `I`. This is also called the upper set generated by these elements. EXAMPLES:: sage: B = Posets.BooleanLattice(4) sage: B.order_filter([3,8]) [3, 7, 8, 9, 10, 11, 12, 13, 14, 15] """ def directed_subset(self, elements, direction): r""" Return the order filter or the order ideal generated by a list of elements. If ``direction`` is 'up', the order filter (upper set) is being returned. If ``direction`` is 'down', the order ideal (lower set) is being returned. INPUT: - elements -- a list of elements. - direction -- 'up' or 'down'. EXAMPLES:: sage: B = Posets.BooleanLattice(4) sage: B.directed_subset([3, 8], 'up') [3, 7, 8, 9, 10, 11, 12, 13, 14, 15] sage: B.directed_subset([7, 10], 'down') [0, 1, 2, 3, 4, 5, 6, 7, 8, 10] """ if direction == 'up': return self.order_filter(elements) if direction == 'down': return self.order_ideal(elements) raise ValueError("Direction must be either 'up' or 'down'.") def principal_order_ideal(self, x): r""" Return the order ideal generated by an element ``x``. This is also called the lower set generated by this element. EXAMPLES:: sage: B = Posets.BooleanLattice(4) sage: B.principal_order_ideal(6) [0, 2, 4, 6] """ return self.order_ideal([x]) principal_lower_set = principal_order_ideal def principal_order_filter(self, x): r""" Return the order filter generated by an element ``x``. This is also called the upper set generated by this element. EXAMPLES:: sage: B = Posets.BooleanLattice(4) sage: B.principal_order_filter(2) [2, 3, 6, 7, 10, 11, 14, 15] """ return self.order_filter([x]) principal_upper_set = principal_order_filter def order_ideal_toggle(self, I, v): r""" Return the result of toggling the element ``v`` in the order ideal ``I``. If `v` is an element of a poset `P`, then toggling the element `v` is an automorphism of the set `J(P)` of all order ideals of `P`. It is defined as follows: If `I` is an order ideal of `P`, then the image of `I` under toggling the element `v` is - the set `I \cup \{ v \}`, if `v \not\in I` but every element of `P` smaller than `v` is in `I`; - the set `I \setminus \{ v \}`, if `v \in I` but no element of `P` greater than `v` is in `I`; - `I` otherwise. This image always is an order ideal of `P`. EXAMPLES:: sage: P = Poset({1: [2,3], 2: [4], 3: []}) sage: I = Set({1, 2}) sage: I in P.order_ideals_lattice() True sage: P.order_ideal_toggle(I, 1) {1, 2} sage: P.order_ideal_toggle(I, 2) {1} sage: P.order_ideal_toggle(I, 3) {1, 2, 3} sage: P.order_ideal_toggle(I, 4) {1, 2, 4} sage: P4 = Posets(4) sage: all(all(all(P.order_ideal_toggle(P.order_ideal_toggle(I, i), i) == I ....: for i in range(4)) ....: for I in P.order_ideals_lattice(facade=True)) ....: for P in P4) True """ if not v in I: if all(u in I for u in self.lower_covers(v)): from sage.sets.set import Set return I.union(Set({v})) else: if all(u not in I for u in self.upper_covers(v)): from sage.sets.set import Set return I.difference(Set({v})) return I def order_ideal_toggles(self, I, vs): r""" Return the result of toggling the elements of the list (or iterable) ``vs`` (one by one, from left to right) in the order ideal ``I``. See :meth:`order_ideal_toggle` for a definition of toggling. EXAMPLES:: sage: P = Poset({1: [2,3], 2: [4], 3: []}) sage: I = Set({1, 2}) sage: P.order_ideal_toggles(I, [1,2,3,4]) {1, 3} sage: P.order_ideal_toggles(I, (1,2,3,4)) {1, 3} """ for v in vs: I = self.order_ideal_toggle(I, v) return I def is_order_ideal(self, o): """ Return whether ``o`` is an order ideal of ``self``, assuming ``self`` has no infinite descending path. INPUT: - ``o`` -- a list (or set, or tuple) containing some elements of ``self`` EXAMPLES:: sage: P = Poset((divisors(12), attrcall("divides")), facade=True, linear_extension=True) sage: sorted(P.list()) [1, 2, 3, 4, 6, 12] sage: P.is_order_ideal([1, 3]) True sage: P.is_order_ideal([]) True sage: P.is_order_ideal({1, 3}) True sage: P.is_order_ideal([1, 3, 4]) False """ return all((u in self and all(x in o for x in self.lower_covers(u))) for u in o) def is_order_filter(self, o): """ Return whether ``o`` is an order filter of ``self``, assuming ``self`` has no infinite ascending path. INPUT: - ``o`` -- a list (or set, or tuple) containing some elements of ``self`` EXAMPLES:: sage: P = Poset((divisors(12), attrcall("divides")), facade=True, linear_extension=True) sage: sorted(P.list()) [1, 2, 3, 4, 6, 12] sage: P.is_order_filter([4, 12]) True sage: P.is_order_filter([]) True sage: P.is_order_filter({3, 4, 12}) False sage: P.is_order_filter({3, 6, 12}) True """ return all((u in self and all(x in o for x in self.upper_covers(u))) for u in o) def is_chain_of_poset(self, o, ordered=False): """ Return whether an iterable ``o`` is a chain of ``self``, including a check for ``o`` being ordered from smallest to largest element if the keyword ``ordered`` is set to ``True``. INPUT: - ``o`` -- an iterable (e. g., list, set, or tuple) containing some elements of ``self`` - ``ordered`` -- a Boolean (default: ``False``) which decides whether the notion of a chain includes being ordered OUTPUT: If ``ordered`` is set to ``False``, the truth value of the following assertion is returned: The subset of ``self`` formed by the elements of ``o`` is a chain in ``self``. If ``ordered`` is set to ``True``, the truth value of the following assertion is returned: Every element of the list ``o`` is (strictly!) smaller than its successor in ``self``. (This makes no sense if ``ordered`` is a set.) EXAMPLES:: sage: P = Poset((divisors(12), attrcall("divides")), facade=True, linear_extension=True) sage: sorted(P.list()) [1, 2, 3, 4, 6, 12] sage: P.is_chain_of_poset([1, 3]) True sage: P.is_chain_of_poset([3, 1]) True sage: P.is_chain_of_poset([1, 3], ordered=True) True sage: P.is_chain_of_poset([3, 1], ordered=True) False sage: P.is_chain_of_poset([]) True sage: P.is_chain_of_poset([], ordered=True) True sage: P.is_chain_of_poset((2, 12, 6)) True sage: P.is_chain_of_poset((2, 6, 12), ordered=True) True sage: P.is_chain_of_poset((2, 12, 6), ordered=True) False sage: P.is_chain_of_poset((2, 12, 6, 3)) False sage: P.is_chain_of_poset((2, 3)) False sage: Q = Poset({2: [3, 1], 3: [4], 1: [4]}) sage: Q.is_chain_of_poset([1, 2], ordered=True) False sage: Q.is_chain_of_poset([1, 2]) True sage: Q.is_chain_of_poset([2, 1], ordered=True) True sage: Q.is_chain_of_poset([2, 1, 1], ordered=True) False sage: Q.is_chain_of_poset([3]) True sage: Q.is_chain_of_poset([4, 2, 3]) True sage: Q.is_chain_of_poset([4, 2, 3], ordered=True) False sage: Q.is_chain_of_poset([2, 3, 4], ordered=True) True Examples with infinite posets:: sage: from sage.categories.examples.posets import FiniteSetsOrderedByInclusion sage: R = FiniteSetsOrderedByInclusion() sage: R.is_chain_of_poset([R(set([3, 1, 2])), R(set([1, 4])), R(set([4, 5]))]) False sage: R.is_chain_of_poset([R(set([3, 1, 2])), R(set([1, 2])), R(set([1]))], ordered=True) False sage: R.is_chain_of_poset([R(set([3, 1, 2])), R(set([1, 2])), R(set([1]))]) True sage: from sage.categories.examples.posets import PositiveIntegersOrderedByDivisibilityFacade sage: T = PositiveIntegersOrderedByDivisibilityFacade() sage: T.is_chain_of_poset((T(3), T(4), T(7))) False sage: T.is_chain_of_poset((T(3), T(6), T(3))) True sage: T.is_chain_of_poset((T(3), T(6), T(3)), ordered=True) False sage: T.is_chain_of_poset((T(3), T(3), T(6))) True sage: T.is_chain_of_poset((T(3), T(3), T(6)), ordered=True) False sage: T.is_chain_of_poset((T(3), T(6)), ordered=True) True sage: T.is_chain_of_poset((), ordered=True) True sage: T.is_chain_of_poset((T(3),), ordered=True) True sage: T.is_chain_of_poset((T(q) for q in divisors(27))) True sage: T.is_chain_of_poset((T(q) for q in divisors(18))) False """ list_o = list(o) if ordered: return all(self.lt(a, b) for a, b in zip(list_o, list_o[1:])) else: for (i, x) in enumerate(list_o): for y in list_o[:i]: if (not self.le(x, y)) and (not self.gt(x, y)): return False return True def is_antichain_of_poset(self, o): """ Return whether an iterable ``o`` is an antichain of ``self``. INPUT: - ``o`` -- an iterable (e. g., list, set, or tuple) containing some elements of ``self`` OUTPUT: ``True`` if the subset of ``self`` consisting of the entries of ``o`` is an antichain of ``self``, and ``False`` otherwise. EXAMPLES:: sage: P = Poset((divisors(12), attrcall("divides")), facade=True, linear_extension=True) sage: sorted(P.list()) [1, 2, 3, 4, 6, 12] sage: P.is_antichain_of_poset([1, 3]) False sage: P.is_antichain_of_poset([3, 1]) False sage: P.is_antichain_of_poset([1, 1, 3]) False sage: P.is_antichain_of_poset([]) True sage: P.is_antichain_of_poset([1]) True sage: P.is_antichain_of_poset([1, 1]) True sage: P.is_antichain_of_poset([3, 4]) True sage: P.is_antichain_of_poset([3, 4, 12]) False sage: P.is_antichain_of_poset([6, 4]) True sage: P.is_antichain_of_poset(i for i in divisors(12) if (2 < i and i < 6)) True sage: P.is_antichain_of_poset(i for i in divisors(12) if (2 <= i and i < 6)) False sage: Q = Poset({2: [3, 1], 3: [4], 1: [4]}) sage: Q.is_antichain_of_poset((1, 2)) False sage: Q.is_antichain_of_poset((2, 4)) False sage: Q.is_antichain_of_poset((4, 2)) False sage: Q.is_antichain_of_poset((2, 2)) True sage: Q.is_antichain_of_poset((3, 4)) False sage: Q.is_antichain_of_poset((3, 1)) True sage: Q.is_antichain_of_poset((1, )) True sage: Q.is_antichain_of_poset(()) True An infinite poset:: sage: from sage.categories.examples.posets import FiniteSetsOrderedByInclusion sage: R = FiniteSetsOrderedByInclusion() sage: R.is_antichain_of_poset([R(set([3, 1, 2])), R(set([1, 4])), R(set([4, 5]))]) True sage: R.is_antichain_of_poset([R(set([3, 1, 2, 4])), R(set([1, 4])), R(set([4, 5]))]) False """ return all(not self.lt(x, y) for x in o for y in o) CartesianProduct = LazyImport('sage.combinat.posets.cartesian_product', 'CartesianProductPoset')
class Modules(Category_module): r""" The category of all modules over a base ring `R`. An `R`-module `M` is a left and right `R`-module over a commutative ring `R` such that: .. MATH:: r*(x*s) = (r*x)*s \qquad \forall r,s \in R \text{ and } x \in M INPUT: - ``base_ring`` -- a ring `R` or subcategory of ``Rings()`` - ``dispatch`` -- a boolean (for internal use; default: ``True``) When the base ring is a field, the category of vector spaces is returned instead (unless ``dispatch == False``). .. WARNING:: Outside of the context of symmetric modules over a commutative ring, the specifications of this category are fuzzy and not yet set in stone (see below). The code in this category and its subcategories is therefore prone to bugs or arbitrary limitations in this case. EXAMPLES:: sage: Modules(ZZ) Category of modules over Integer Ring sage: Modules(QQ) Category of vector spaces over Rational Field sage: Modules(Rings()) Category of modules over rings sage: Modules(FiniteFields()) Category of vector spaces over finite enumerated fields sage: Modules(Integers(9)) Category of modules over Ring of integers modulo 9 sage: Modules(Integers(9)).super_categories() [Category of bimodules over Ring of integers modulo 9 on the left and Ring of integers modulo 9 on the right] sage: Modules(ZZ).super_categories() [Category of bimodules over Integer Ring on the left and Integer Ring on the right] sage: Modules == RingModules True sage: Modules(ZZ['x']).is_abelian() # see #6081 True TESTS:: sage: TestSuite(Modules(ZZ)).run() .. TODO:: - Clarify the distinction, if any, with ``BiModules(R, R)``. In particular, if `R` is a commutative ring (e.g. a field), some pieces of the code possibly assume that `M` is a *symmetric `R`-`R`-bimodule*: .. MATH:: r*x = x*r \qquad \forall r \in R \text{ and } x \in M - Make sure that non symmetric modules are properly supported by all the code, and advertise it. - Make sure that non commutative rings are properly supported by all the code, and advertise it. - Add support for base semirings. - Implement a ``FreeModules(R)`` category, when so prompted by a concrete use case: e.g. modeling a free module with several bases (using :meth:`Sets.SubcategoryMethods.Realizations`) or with an atlas of local maps (see e.g. :trac:`15916`). """ @staticmethod def __classcall_private__(cls, base_ring, dispatch = True): r""" Implement the dispatching of ``Modules(field)`` to ``VectorSpaces(field)``. This feature will later be extended, probably as a covariant functorial construction, to support modules over various kinds of rings (principal ideal domains, ...), or even over semirings. TESTS:: sage: C = Modules(ZZ); C Category of modules over Integer Ring sage: C is Modules(ZZ, dispatch = False) True sage: C is Modules(ZZ, dispatch = True) True sage: C._reduction (<class 'sage.categories.modules.Modules'>, (Integer Ring,), {'dispatch': False}) sage: TestSuite(C).run() sage: Modules(QQ) is VectorSpaces(QQ) True sage: Modules(QQ, dispatch = True) is VectorSpaces(QQ) True sage: C = Modules(NonNegativeIntegers()); C # todo: not implemented Category of semiring modules over Non negative integers sage: C = Modules(QQ, dispatch = False); C Category of modules over Rational Field sage: C._reduction (<class 'sage.categories.modules.Modules'>, (Rational Field,), {'dispatch': False}) sage: TestSuite(C).run() """ if dispatch: if base_ring in _Fields or (isinstance(base_ring, Category) and base_ring.is_subcategory(_Fields)): from .vector_spaces import VectorSpaces return VectorSpaces(base_ring, check=False) result = super(Modules, cls).__classcall__(cls, base_ring) result._reduction[2]['dispatch'] = False return result def super_categories(self): """ EXAMPLES:: sage: Modules(ZZ).super_categories() [Category of bimodules over Integer Ring on the left and Integer Ring on the right] Nota bene:: sage: Modules(QQ) Category of vector spaces over Rational Field sage: Modules(QQ).super_categories() [Category of modules over Rational Field] """ R = self.base_ring() return [Bimodules(R,R)] def additional_structure(self): r""" Return ``None``. Indeed, the category of modules defines no additional structure: a bimodule morphism between two modules is a module morphism. .. SEEALSO:: :meth:`Category.additional_structure` .. TODO:: Should this category be a :class:`~sage.categories.category_with_axiom.CategoryWithAxiom`? EXAMPLES:: sage: Modules(ZZ).additional_structure() """ return None class SubcategoryMethods: @cached_method def base_ring(self): r""" Return the base ring (category) for ``self``. This implements a ``base_ring`` method for all subcategories of ``Modules(K)``. EXAMPLES:: sage: C = Modules(QQ) & Semigroups(); C Join of Category of semigroups and Category of vector spaces over Rational Field sage: C.base_ring() Rational Field sage: C.base_ring.__module__ 'sage.categories.modules' sage: C = Modules(Rings()) & Semigroups(); C Join of Category of semigroups and Category of modules over rings sage: C.base_ring() Category of rings sage: C.base_ring.__module__ 'sage.categories.modules' sage: C = DescentAlgebra(QQ,3).B().category() sage: C.base_ring.__module__ 'sage.categories.modules' sage: C.base_ring() Rational Field sage: C = QuasiSymmetricFunctions(QQ).F().category() sage: C.base_ring.__module__ 'sage.categories.modules' sage: C.base_ring() Rational Field """ for C in self.super_categories(): # Is there a better way to ask if C is a subcategory of Modules? if hasattr(C, "base_ring"): return C.base_ring() assert False, "some super category of {} should be a category over base ring".format(self) def TensorProducts(self): r""" Return the full subcategory of objects of ``self`` constructed as tensor products. .. SEEALSO:: - :class:`.tensor.TensorProductsCategory` - :class:`~.covariant_functorial_construction.RegressiveCovariantFunctorialConstruction`. EXAMPLES:: sage: ModulesWithBasis(QQ).TensorProducts() Category of tensor products of vector spaces with basis over Rational Field """ return TensorProductsCategory.category_of(self) @cached_method def DualObjects(self): r""" Return the category of spaces constructed as duals of spaces of ``self``. The *dual* of a vector space `V` is the space consisting of all linear functionals on `V` (see :wikipedia:`Dual_space`). Additional structure on `V` can endow its dual with additional structure; for example, if `V` is a finite dimensional algebra, then its dual is a coalgebra. This returns the category of spaces constructed as dual of spaces in ``self``, endowed with the appropriate additional structure. .. WARNING:: - This semantic of ``dual`` and ``DualObject`` is imposed on all subcategories, in particular to make ``dual`` a covariant functorial construction. A subcategory that defines a different notion of dual needs to use a different name. - Typically, the category of graded modules should define a separate ``graded_dual`` construction (see :trac:`15647`). For now the two constructions are not distinguished which is an oversimplified model. .. SEEALSO:: - :class:`.dual.DualObjectsCategory` - :class:`~.covariant_functorial_construction.CovariantFunctorialConstruction`. EXAMPLES:: sage: VectorSpaces(QQ).DualObjects() Category of duals of vector spaces over Rational Field The dual of a vector space is a vector space:: sage: VectorSpaces(QQ).DualObjects().super_categories() [Category of vector spaces over Rational Field] The dual of an algebra is a coalgebra:: sage: sorted(Algebras(QQ).DualObjects().super_categories(), key=str) [Category of coalgebras over Rational Field, Category of duals of vector spaces over Rational Field] The dual of a coalgebra is an algebra:: sage: sorted(Coalgebras(QQ).DualObjects().super_categories(), key=str) [Category of algebras over Rational Field, Category of duals of vector spaces over Rational Field] As a shorthand, this category can be accessed with the :meth:`~Modules.SubcategoryMethods.dual` method:: sage: VectorSpaces(QQ).dual() Category of duals of vector spaces over Rational Field TESTS:: sage: C = VectorSpaces(QQ).DualObjects() sage: C.base_category() Category of vector spaces over Rational Field sage: C.super_categories() [Category of vector spaces over Rational Field] sage: latex(C) \mathbf{DualObjects}(\mathbf{VectorSpaces}_{\Bold{Q}}) sage: TestSuite(C).run() """ return DualObjectsCategory.category_of(self) dual = DualObjects @cached_method def FiniteDimensional(self): r""" Return the full subcategory of the finite dimensional objects of ``self``. EXAMPLES:: sage: Modules(ZZ).FiniteDimensional() Category of finite dimensional modules over Integer Ring sage: Coalgebras(QQ).FiniteDimensional() Category of finite dimensional coalgebras over Rational Field sage: AlgebrasWithBasis(QQ).FiniteDimensional() Category of finite dimensional algebras with basis over Rational Field TESTS:: sage: TestSuite(Modules(ZZ).FiniteDimensional()).run() sage: Coalgebras(QQ).FiniteDimensional.__module__ 'sage.categories.modules' """ return self._with_axiom("FiniteDimensional") @cached_method def Filtered(self, base_ring=None): r""" Return the subcategory of the filtered objects of ``self``. INPUT: - ``base_ring`` -- this is ignored EXAMPLES:: sage: Modules(ZZ).Filtered() Category of filtered modules over Integer Ring sage: Coalgebras(QQ).Filtered() Category of filtered coalgebras over Rational Field sage: AlgebrasWithBasis(QQ).Filtered() Category of filtered algebras with basis over Rational Field .. TODO:: - Explain why this does not commute with :meth:`WithBasis` - Improve the support for covariant functorial constructions categories over a base ring so as to get rid of the ``base_ring`` argument. TESTS:: sage: Coalgebras(QQ).Graded.__module__ 'sage.categories.modules' """ assert base_ring is None or base_ring is self.base_ring() from sage.categories.filtered_modules import FilteredModulesCategory return FilteredModulesCategory.category_of(self) @cached_method def Graded(self, base_ring=None): r""" Return the subcategory of the graded objects of ``self``. INPUT: - ``base_ring`` -- this is ignored EXAMPLES:: sage: Modules(ZZ).Graded() Category of graded modules over Integer Ring sage: Coalgebras(QQ).Graded() Category of graded coalgebras over Rational Field sage: AlgebrasWithBasis(QQ).Graded() Category of graded algebras with basis over Rational Field .. TODO:: - Explain why this does not commute with :meth:`WithBasis` - Improve the support for covariant functorial constructions categories over a base ring so as to get rid of the ``base_ring`` argument. TESTS:: sage: Coalgebras(QQ).Graded.__module__ 'sage.categories.modules' """ assert base_ring is None or base_ring is self.base_ring() from sage.categories.graded_modules import GradedModulesCategory return GradedModulesCategory.category_of(self) @cached_method def Super(self, base_ring=None): r""" Return the super-analogue category of ``self``. INPUT: - ``base_ring`` -- this is ignored EXAMPLES:: sage: Modules(ZZ).Super() Category of super modules over Integer Ring sage: Coalgebras(QQ).Super() Category of super coalgebras over Rational Field sage: AlgebrasWithBasis(QQ).Super() Category of super algebras with basis over Rational Field .. TODO:: - Explain why this does not commute with :meth:`WithBasis` - Improve the support for covariant functorial constructions categories over a base ring so as to get rid of the ``base_ring`` argument. TESTS:: sage: Coalgebras(QQ).Super.__module__ 'sage.categories.modules' """ assert base_ring is None or base_ring is self.base_ring() from sage.categories.super_modules import SuperModulesCategory return SuperModulesCategory.category_of(self) @cached_method def WithBasis(self): r""" Return the full subcategory of the objects of ``self`` with a distinguished basis. EXAMPLES:: sage: Modules(ZZ).WithBasis() Category of modules with basis over Integer Ring sage: Coalgebras(QQ).WithBasis() Category of coalgebras with basis over Rational Field sage: AlgebrasWithBasis(QQ).WithBasis() Category of algebras with basis over Rational Field TESTS:: sage: TestSuite(Modules(ZZ).WithBasis()).run() sage: Coalgebras(QQ).WithBasis.__module__ 'sage.categories.modules' """ return self._with_axiom("WithBasis") class FiniteDimensional(CategoryWithAxiom_over_base_ring): def extra_super_categories(self): """ Implement the fact that a finite dimensional module over a finite ring is finite. EXAMPLES:: sage: Modules(IntegerModRing(4)).FiniteDimensional().extra_super_categories() [Category of finite sets] sage: Modules(ZZ).FiniteDimensional().extra_super_categories() [] sage: Modules(GF(5)).FiniteDimensional().is_subcategory(Sets().Finite()) True sage: Modules(ZZ).FiniteDimensional().is_subcategory(Sets().Finite()) False sage: Modules(Rings().Finite()).FiniteDimensional().is_subcategory(Sets().Finite()) True sage: Modules(Rings()).FiniteDimensional().is_subcategory(Sets().Finite()) False """ base_ring = self.base_ring() FiniteSets = Sets().Finite() if (isinstance(base_ring, Category) and base_ring.is_subcategory(FiniteSets)) or \ base_ring in FiniteSets: return [FiniteSets] else: return [] Filtered = LazyImport('sage.categories.filtered_modules', 'FilteredModules') Graded = LazyImport('sage.categories.graded_modules', 'GradedModules') Super = LazyImport('sage.categories.super_modules', 'SuperModules') # at_startup currently needed for MatrixSpace, see #22955 (e.g., comment:20) WithBasis = LazyImport('sage.categories.modules_with_basis', 'ModulesWithBasis', at_startup=True) class ParentMethods: @cached_method def tensor_square(self): """ Returns the tensor square of ``self`` EXAMPLES:: sage: A = HopfAlgebrasWithBasis(QQ).example() sage: A.tensor_square() An example of Hopf algebra with basis: the group algebra of the Dihedral group of order 6 as a permutation group over Rational Field # An example of Hopf algebra with basis: the group algebra of the Dihedral group of order 6 as a permutation group over Rational Field """ return tensor([self, self]) class ElementMethods: pass class Homsets(HomsetsCategory): r""" The category of homomorphism sets `\hom(X,Y)` for `X`, `Y` modules. """ def extra_super_categories(self): """ EXAMPLES:: sage: Modules(ZZ).Homsets().extra_super_categories() [Category of modules over Integer Ring] """ return [Modules(self.base_category().base_ring())] def base_ring(self): """ EXAMPLES:: sage: Modules(ZZ).Homsets().base_ring() Integer Ring .. TODO:: Generalize this so that any homset category of a full subcategory of modules over a base ring is a category over this base ring. """ return self.base_category().base_ring() class ParentMethods: @cached_method def base_ring(self): """ Return the base ring of ``self``. EXAMPLES:: sage: E = CombinatorialFreeModule(ZZ, [1,2,3]) sage: F = CombinatorialFreeModule(ZZ, [2,3,4]) sage: H = Hom(E, F) sage: H.base_ring() Integer Ring This ``base_ring`` method is actually overridden by :meth:`sage.structure.category_object.CategoryObject.base_ring`:: sage: H.base_ring.__module__ Here we call it directly:: sage: method = H.category().parent_class.base_ring sage: method.__get__(H)() Integer Ring """ return self.domain().base_ring() @cached_method def zero(self): """ EXAMPLES:: sage: E = CombinatorialFreeModule(ZZ, [1,2,3]) sage: F = CombinatorialFreeModule(ZZ, [2,3,4]) sage: H = Hom(E, F) sage: f = H.zero() sage: f Generic morphism: From: Free module generated by {1, 2, 3} over Integer Ring To: Free module generated by {2, 3, 4} over Integer Ring sage: f(E.monomial(2)) 0 sage: f(E.monomial(3)) == F.zero() True TESTS: We check that ``H.zero()`` is picklable:: sage: loads(dumps(f.parent().zero())) Generic morphism: From: Free module generated by {1, 2, 3} over Integer Ring To: Free module generated by {2, 3, 4} over Integer Ring """ from sage.misc.constant_function import ConstantFunction return self(ConstantFunction(self.codomain().zero())) class Endset(CategoryWithAxiom_over_base_ring): """ The category of endomorphism sets `End(X)` for `X` a module (this is not used yet) """ def extra_super_categories(self): """ Implement the fact that the endomorphism set of a module is an algebra. .. SEEALSO:: :meth:`CategoryWithAxiom.extra_super_categories` EXAMPLES:: sage: Modules(ZZ).Endsets().extra_super_categories() [Category of magmatic algebras over Integer Ring] sage: End(ZZ^3) in Algebras(ZZ) True """ from .magmatic_algebras import MagmaticAlgebras return [MagmaticAlgebras(self.base_category().base_ring())] class CartesianProducts(CartesianProductsCategory): """ The category of modules constructed as Cartesian products of modules This construction gives the direct product of modules. The implementation is based on the following resources: - http://groups.google.fr/group/sage-devel/browse_thread/thread/35a72b1d0a2fc77a/348f42ae77a66d16#348f42ae77a66d16 - :wikipedia:`Direct_product` """ def extra_super_categories(self): """ A Cartesian product of modules is endowed with a natural module structure. EXAMPLES:: sage: Modules(ZZ).CartesianProducts().extra_super_categories() [Category of modules over Integer Ring] sage: Modules(ZZ).CartesianProducts().super_categories() [Category of Cartesian products of commutative additive groups, Category of modules over Integer Ring] """ return [self.base_category()] class ParentMethods: def base_ring(self): """ Return the base ring of this Cartesian product. EXAMPLES:: sage: E = CombinatorialFreeModule(ZZ, [1,2,3]) sage: F = CombinatorialFreeModule(ZZ, [2,3,4]) sage: C = cartesian_product([E, F]); C Free module generated by {1, 2, 3} over Integer Ring (+) Free module generated by {2, 3, 4} over Integer Ring sage: C.base_ring() Integer Ring """ return self._sets[0].base_ring() class TensorProducts(TensorProductsCategory): """ The category of modules constructed by tensor product of modules. """ @cached_method def extra_super_categories(self): """ EXAMPLES:: sage: Modules(ZZ).TensorProducts().extra_super_categories() [Category of modules over Integer Ring] sage: Modules(ZZ).TensorProducts().super_categories() [Category of modules over Integer Ring] """ return [self.base_category()]
class Monoids(CategoryWithAxiom): r""" The category of (multiplicative) monoids. A *monoid* is a unital :class:`semigroup <Semigroups>`, that is a set endowed with a multiplicative binary operation `*` which is associative and admits a unit (see :wikipedia:`Monoid`). EXAMPLES:: sage: Monoids() Category of monoids sage: Monoids().super_categories() [Category of semigroups, Category of unital magmas] sage: Monoids().all_super_categories() [Category of monoids, Category of semigroups, Category of unital magmas, Category of magmas, Category of sets, Category of sets with partial maps, Category of objects] sage: Monoids().axioms() frozenset(['Associative', 'Unital']) sage: Semigroups().Unital() Category of monoids sage: Monoids().example() An example of a monoid: the free monoid generated by ('a', 'b', 'c', 'd') TESTS:: sage: C = Monoids() sage: TestSuite(C).run() """ _base_category_class_and_axiom = (Semigroups, "Unital") Finite = LazyImport('sage.categories.finite_monoids', 'FiniteMonoids', at_startup=True) Inverse = LazyImport('sage.categories.groups', 'Groups', at_startup=True) @staticmethod def free(index_set=None, names=None, **kwds): r""" Return a free monoid on `n` generators or with the generators indexed by a set `I`. A free monoid is constructed by specifing either: - the number of generators and/or the names of the generators - the indexing set for the generators INPUT: - ``index_set`` -- (optional) an index set for the generators; if an integer, then this represents `\{0, 1, \ldots, n-1\}` - ``names`` -- a string or list/tuple/iterable of strings (default: ``'x'``); the generator names or name prefix EXAMPLES:: sage: Monoids.free(index_set=ZZ) Free monoid indexed by Integer Ring sage: Monoids().free(ZZ) Free monoid indexed by Integer Ring sage: F.<x,y,z> = Monoids().free(); F Free monoid indexed by {'x', 'y', 'z'} """ if names is not None: if isinstance(names, str): from sage.rings.all import ZZ if ',' not in names and index_set in ZZ: names = [names + repr(i) for i in range(index_set)] else: names = names.split(',') names = tuple(names) if index_set is None: index_set = names from sage.monoids.indexed_free_monoid import IndexedFreeMonoid return IndexedFreeMonoid(index_set, names=names, **kwds) class ParentMethods: def one_element(self): r""" Backward compatibility alias for :meth:`one`. TESTS:: sage: S = Monoids().example() sage: S.one_element() '' """ return self.one() def prod(self, args): r""" n-ary product of elements of ``self``. INPUT: - ``args`` -- a list (or iterable) of elements of ``self`` Returns the product of the elements in ``args``, as an element of ``self``. EXAMPLES:: sage: S = Monoids().example() sage: S.prod([S('a'), S('b')]) 'ab' """ return prod(args, self.one()) def _test_prod(self, **options): r""" Run basic tests for the product method :meth:`prod` of ``self``. See the documentation for :class:`TestSuite` for information on further options. INPUT: - ``options`` -- any keyword arguments accepted by :meth:`_tester` EXAMPLES: By default, this method tests only the elements returned by ``self.some_elements()``:: sage: S = Monoids().example() sage: S._test_prod() However, the elements tested can be customized with the ``elements`` keyword argument:: sage: S._test_prod(elements = (S('a'), S('b'))) """ tester = self._tester(**options) tester.assert_(self.prod([]) == self.one()) for x in tester.some_elements(): tester.assert_(self.prod([x]) == x) tester.assert_(self.prod([x, x]) == x**2) tester.assert_(self.prod([x, x, x]) == x**3) class ElementMethods: def is_one(self): r""" Return whether ``self`` is the one of the monoid. The default implementation is to compare with ``self.one()``. TESTS:: sage: S = Monoids().example() sage: S.one().is_one() True sage: S("aa").is_one() False """ return self == self.parent().one() def __pow__(self, n): r""" Return ``self`` to the `n^{th}` power. INPUT: - ``n`` -- a nonnegative integer EXAMPLES:: sage: S = Monoids().example() sage: x = S("aa") sage: x^0, x^1, x^2, x^3, x^4, x^5 ('', 'aa', 'aaaa', 'aaaaaa', 'aaaaaaaa', 'aaaaaaaaaa') """ if not n: # FIXME: why do we need to do that? return self.parent().one() return generic_power(self, n, self.parent().one()) def _pow_naive(self, n): r""" Return ``self`` to the `n^{th}` power (naive implementation). INPUT: - ``n`` -- a nonnegative integer This naive implementation does not use binary exponentiation; there are cases where this is actually faster due to size explosion. EXAMPLES:: sage: S = Monoids().example() sage: x = S("aa") sage: [x._pow_naive(i) for i in range(6)] ['', 'aa', 'aaaa', 'aaaaaa', 'aaaaaaaa', 'aaaaaaaaaa'] """ if not n: return self.parent().one() result = self for i in range(n - 1): result *= self return result class Commutative(CategoryWithAxiom): """ Category of commutative (abelian) monoids. A monoid `M` is *commutative* if `xy = yx` for all `x,y \in M`. """ @staticmethod def free(index_set=None, names=None, **kwds): r""" Return a free abelian monoid on `n` generators or with the generators indexed by a set `I`. A free monoid is constructed by specifing either: - the number of generators and/or the names of the generators, or - the indexing set for the generators. INPUT: - ``index_set`` -- (optional) an index set for the generators; if an integer, then this represents `\{0, 1, \ldots, n-1\}` - ``names`` -- a string or list/tuple/iterable of strings (default: ``'x'``); the generator names or name prefix EXAMPLES:: sage: Monoids.Commutative.free(index_set=ZZ) Free abelian monoid indexed by Integer Ring sage: Monoids().Commutative().free(ZZ) Free abelian monoid indexed by Integer Ring sage: F.<x,y,z> = Monoids().Commutative().free(); F Free abelian monoid indexed by {'x', 'y', 'z'} """ if names is not None: if isinstance(names, str): from sage.rings.all import ZZ if ',' not in names and index_set in ZZ: names = [names + repr(i) for i in range(index_set)] else: names = names.split(',') names = tuple(names) if index_set is None: index_set = names from sage.monoids.indexed_free_monoid import IndexedFreeAbelianMonoid return IndexedFreeAbelianMonoid(index_set, names=names, **kwds) class WithRealizations(WithRealizationsCategory): class ParentMethods: def one(self): r""" Return the unit of this monoid. This default implementation returns the unit of the realization of ``self`` given by :meth:`~Sets.WithRealizations.ParentMethods.a_realization`. EXAMPLES:: sage: A = Sets().WithRealizations().example(); A The subset algebra of {1, 2, 3} over Rational Field sage: A.one.__module__ 'sage.categories.monoids' sage: A.one() F[{}] TESTS:: sage: A.one() is A.a_realization().one() True sage: A._test_one() """ return self.a_realization().one() class Subquotients(SubquotientsCategory): class ParentMethods: def one(self): """ Returns the multiplicative unit of this monoid, obtained by retracting that of the ambient monoid. EXAMPLES:: sage: S = Monoids().Subquotients().example() # todo: not implemented sage: S.one() # todo: not implemented """ return self.retract(self.ambient().one()) class Algebras(AlgebrasCategory): def extra_super_categories(self): """ EXAMPLES:: sage: Monoids().Algebras(QQ).extra_super_categories() [Category of monoids] sage: Monoids().Algebras(QQ).super_categories() [Category of algebras with basis over Rational Field, Category of semigroup algebras over Rational Field, Category of unital magma algebras over Rational Field] """ return [Monoids()] class ParentMethods: @cached_method def one_basis(self): """ Return the unit of the monoid, which indexes the unit of this algebra, as per :meth:`AlgebrasWithBasis.ParentMethods.one_basis() <sage.categories.algebras_with_basis.AlgebrasWithBasis.ParentMethods.one_basis>`. EXAMPLES:: sage: A = Monoids().example().algebra(ZZ) sage: A.one_basis() '' sage: A.one() B[''] sage: A(3) 3*B[''] """ return self.basis().keys().one() class ElementMethods: def is_central(self): r""" Return whether the element ``self`` is central. EXAMPLES:: sage: SG4=SymmetricGroupAlgebra(ZZ,4) sage: SG4(1).is_central() True sage: SG4(Permutation([1,3,2,4])).is_central() False sage: A=GroupAlgebras(QQ).example(); A Group algebra of Dihedral group of order 8 as a permutation group over Rational Field sage: sum(i for i in A.basis()).is_central() True """ return all([ i * self == self * i for i in self.parent().algebra_generators() ]) class CartesianProducts(CartesianProductsCategory): """ The category of monoids constructed as cartesian products of monoids. This construction gives the direct product of monoids. See :wikipedia:`Direct_product` for more information. """ def extra_super_categories(self): """ A cartesian product of monoids is endowed with a natural group structure. EXAMPLES:: sage: C = Monoids().CartesianProducts() sage: C.extra_super_categories() [Category of monoids] sage: sorted(C.super_categories(), key=str) [Category of Cartesian products of semigroups, Category of Cartesian products of unital magmas, Category of monoids] """ return [self.base_category()] class ParentMethods: @cached_method def monoid_generators(self): """ Return the generators of ``self``. EXAMPLES:: sage: M = Monoids.free([1,2,3]) sage: N = Monoids.free(['a','b']) sage: C = cartesian_product([M, N]) sage: C.monoid_generators() Family ((F[1], 1), (F[2], 1), (F[3], 1), (1, F['a']), (1, F['b'])) An example with an infinitely generated group (a better output is needed):: sage: N = Monoids.free(ZZ) sage: C = cartesian_product([M, N]) sage: C.monoid_generators() Lazy family (gen(i))_{i in The cartesian product of (...)} """ F = self.cartesian_factors() ids = tuple(M.one() for M in F) def lift(i, gen): cur = list(ids) cur[i] = gen return self._cartesian_product_of_elements(cur) from sage.sets.family import Family # Finitely generated cat = FiniteEnumeratedSets() if all(M.monoid_generators() in cat or isinstance(M.monoid_generators(), (tuple, list)) for M in F): ret = [ lift(i, gen) for i, M in enumerate(F) for gen in M.monoid_generators() ] return Family(ret) # Infinitely generated # This does not return a good output, but it is "correct" # TODO: Figure out a better way to do things gens_prod = cartesian_product([ Family(M.monoid_generators(), lambda g: (i, g)) for i, M in enumerate(F) ]) return Family(gens_prod, lift, name="gen")
class Rings(CategoryWithAxiom): """ The category of rings Associative rings with unit, not necessarily commutative EXAMPLES:: sage: Rings() Category of rings sage: sorted(Rings().super_categories(), key=str) [Category of rngs, Category of semirings] sage: sorted(Rings().axioms()) ['AdditiveAssociative', 'AdditiveCommutative', 'AdditiveInverse', 'AdditiveUnital', 'Associative', 'Distributive', 'Unital'] sage: Rings() is (CommutativeAdditiveGroups() & Monoids()).Distributive() True sage: Rings() is Rngs().Unital() True sage: Rings() is Semirings().AdditiveInverse() True TESTS:: sage: TestSuite(Rings()).run() .. TODO:: (see: http://trac.sagemath.org/sage_trac/wiki/CategoriesRoadMap) - Make Rings() into a subcategory or alias of Algebras(ZZ); - A parent P in the category ``Rings()`` should automatically be in the category ``Algebras(P)``. """ _base_category_class_and_axiom = (Rngs, "Unital") class MorphismMethods: @cached_method def is_injective(self): """ Return whether or not this morphism is injective. EXAMPLES:: sage: R.<x,y> = QQ[] sage: R.hom([x, y^2], R).is_injective() True sage: R.hom([x, x^2], R).is_injective() False sage: S.<u,v> = R.quotient(x^3*y) sage: R.hom([v, u], S).is_injective() False sage: S.hom([-u, v], S).is_injective() True sage: S.cover().is_injective() False If the domain is a field, the homomorphism is injective:: sage: K.<x> = FunctionField(QQ) sage: L.<y> = FunctionField(QQ) sage: f = K.hom([y]); f Function Field morphism: From: Rational function field in x over Rational Field To: Rational function field in y over Rational Field Defn: x |--> y sage: f.is_injective() True Unless the codomain is the zero ring:: sage: codomain = Integers(1) sage: f = QQ.hom([Zmod(1)(0)], check=False) sage: f.is_injective() False Homomorphism from rings of characteristic zero to rings of positive characteristic can not be injective:: sage: R.<x> = ZZ[] sage: f = R.hom([GF(3)(1)]); f Ring morphism: From: Univariate Polynomial Ring in x over Integer Ring To: Finite Field of size 3 Defn: x |--> 1 sage: f.is_injective() False A morphism whose domain is an order in a number field is injective if the codomain has characteristic zero:: sage: K.<x> = FunctionField(QQ) sage: f = ZZ.hom(K); f Composite map: From: Integer Ring To: Rational function field in x over Rational Field Defn: Conversion via FractionFieldElement_1poly_field map: From: Integer Ring To: Fraction Field of Univariate Polynomial Ring in x over Rational Field then Isomorphism: From: Fraction Field of Univariate Polynomial Ring in x over Rational Field To: Rational function field in x over Rational Field sage: f.is_injective() True A coercion to the fraction field is injective:: sage: R = ZpFM(3) sage: R.fraction_field().coerce_map_from(R).is_injective() True """ if self.domain().is_zero(): return True if self.codomain().is_zero(): # the only map to the zero ring that is injective is the map from itself return False from sage.categories.fields import Fields if self.domain() in Fields(): # A ring homomorphism from a field to a ring is injective # (unless the codomain is the zero ring.) Note that ring # homomorphism must send the 1 element to the 1 element return True try: ker = self.kernel() except (NotImplementedError, AttributeError): pass else: return ker.is_zero() if self.domain().characteristic() == 0: if self.codomain().characteristic() != 0: return False else: from sage.categories.integral_domains import IntegralDomains if self.domain() in IntegralDomains(): # if all elements of the domain are algebraic over ZZ, # then the homomorphism must be injective (in # particular if the domain is ZZ) from sage.categories.number_fields import NumberFields if self.domain().fraction_field() in NumberFields(): return True if self._is_coercion: try: K = self.domain().fraction_field() except (TypeError, AttributeError, ValueError): pass else: if K is self.codomain(): return True try: if self.domain().cardinality() > self.codomain().cardinality(): return False except AttributeError: pass raise NotImplementedError def _is_nonzero(self): r""" Return whether this is not the zero morphism. .. NOTE:: We can not override ``is_zero()`` from the category framework and we can not implement ``__nonzero__`` because it is a special method. That this is why this has a cumbersome name. EXAMPLES:: sage: ZZ.hom(ZZ)._is_nonzero() True sage: ZZ.hom(Zmod(1))._is_nonzero() False """ return bool(self.codomain().one()) def extend_to_fraction_field(self): r""" Return the extension of this morphism to fraction fields of the domain and the codomain. EXAMPLES:: sage: S.<x> = QQ[] sage: f = S.hom([x+1]); f Ring endomorphism of Univariate Polynomial Ring in x over Rational Field Defn: x |--> x + 1 sage: g = f.extend_to_fraction_field(); g Ring endomorphism of Fraction Field of Univariate Polynomial Ring in x over Rational Field Defn: x |--> x + 1 sage: g(x) x + 1 sage: g(1/x) 1/(x + 1) If this morphism is not injective, it does not extend to the fraction field and an error is raised:: sage: f = GF(5).coerce_map_from(ZZ) sage: f.extend_to_fraction_field() Traceback (most recent call last): ... ValueError: the morphism is not injective TESTS:: sage: A.<x> = RR[] sage: phi = A.hom([x+1]) sage: phi.extend_to_fraction_field() Ring endomorphism of Fraction Field of Univariate Polynomial Ring in x over Real Field with 53 bits of precision Defn: x |--> x + 1.00000000000000 """ from sage.rings.morphism import RingHomomorphism_from_fraction_field if self.domain().is_field() and self.codomain().is_field(): return self try: if not self.is_injective(): raise ValueError("the morphism is not injective") except (NotImplementedError, TypeError): # we trust the user pass domain = self.domain().fraction_field() codomain = self.codomain().fraction_field() parent = domain.Hom( codomain) # category = category=self.category_for() ??? return RingHomomorphism_from_fraction_field(parent, self) class SubcategoryMethods: def NoZeroDivisors(self): r""" Return the full subcategory of the objects of ``self`` having no nonzero zero divisors. A *zero divisor* in a ring `R` is an element `x \in R` such that there exists a nonzero element `y \in R` such that `x \cdot y = 0` or `y \cdot x = 0` (see :wikipedia:`Zero_divisor`). EXAMPLES:: sage: Rings().NoZeroDivisors() Category of domains .. NOTE:: This could be generalized to :class:`MagmasAndAdditiveMagmas.Distributive.AdditiveUnital`. TESTS:: sage: TestSuite(Rings().NoZeroDivisors()).run() sage: Algebras(QQ).NoZeroDivisors.__module__ 'sage.categories.rings' """ return self._with_axiom('NoZeroDivisors') def Division(self): """ Return the full subcategory of the division objects of ``self``. A ring satisfies the *division axiom* if all non-zero elements have multiplicative inverses. .. NOTE:: This could be generalized to :class:`MagmasAndAdditiveMagmas.Distributive.AdditiveUnital`. EXAMPLES:: sage: Rings().Division() Category of division rings sage: Rings().Commutative().Division() Category of fields TESTS:: sage: TestSuite(Rings().Division()).run() sage: Algebras(QQ).Division.__module__ 'sage.categories.rings' """ return self._with_axiom('Division') NoZeroDivisors = LazyImport('sage.categories.domains', 'Domains', at_startup=True) Division = LazyImport('sage.categories.division_rings', 'DivisionRings', at_startup=True) Commutative = LazyImport('sage.categories.commutative_rings', 'CommutativeRings', at_startup=True) class ParentMethods: def is_ring(self): """ Return True, since this in an object of the category of rings. EXAMPLES:: sage: Parent(QQ,category=Rings()).is_ring() True """ return True def is_zero(self): """ Return ``True`` if this is the zero ring. EXAMPLES:: sage: Integers(1).is_zero() True sage: Integers(2).is_zero() False sage: QQ.is_zero() False sage: R.<x> = ZZ[] sage: R.quo(1).is_zero() True sage: R.<x> = GF(101)[] sage: R.quo(77).is_zero() True sage: R.quo(x^2+1).is_zero() False """ return self.one() == self.zero() def bracket(self, x, y): """ Returns the Lie bracket `[x, y] = x y - y x` of `x` and `y`. INPUT: - ``x``, ``y`` -- elements of ``self`` EXAMPLES:: sage: F = AlgebrasWithBasis(QQ).example() sage: F An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field sage: a,b,c = F.algebra_generators() sage: F.bracket(a,b) B[word: ab] - B[word: ba] This measures the default of commutation between `x` and `y`. `F` endowed with the bracket operation is a Lie algebra; in particular, it satisfies Jacobi's identity:: sage: F.bracket( F.bracket(a,b), c) + F.bracket(F.bracket(b,c),a) + F.bracket(F.bracket(c,a),b) 0 """ return x * y - y * x def _Hom_(self, Y, category): r""" Returns the homset from ``self`` to ``Y`` in the category ``category`` INPUT: - ``Y`` -- a ring - ``category`` -- a subcategory of :class:`Rings`() or None The sole purpose of this method is to construct the homset as a :class:`~sage.rings.homset.RingHomset`. If ``category`` is specified and is not a subcategory of :class:`Rings`, a ``TypeError`` is raised instead This method is not meant to be called directly. Please use :func:`sage.categories.homset.Hom` instead. EXAMPLES:: sage: H = QQ._Hom_(QQ, category = Rings()); H Set of Homomorphisms from Rational Field to Rational Field sage: H.__class__ <class 'sage.rings.homset.RingHomset_generic_with_category'> TESTS:: sage: Hom(QQ, QQ, category = Rings()).__class__ <class 'sage.rings.homset.RingHomset_generic_with_category'> sage: Hom(CyclotomicField(3), QQ, category = Rings()).__class__ <class 'sage.rings.number_field.homset.CyclotomicFieldHomset_with_category'> sage: TestSuite(Hom(QQ, QQ, category = Rings())).run() # indirect doctest """ if category is not None and not category.is_subcategory(Rings()): raise TypeError("%s is not a subcategory of Rings()" % category) if Y not in Rings(): raise TypeError("%s is not a ring" % Y) from sage.rings.homset import RingHomset return RingHomset(self, Y, category=category) # this is already in sage.rings.ring.Ring, # but not all rings descend from that class, # e.g., matrix spaces. def _mul_(self, x, switch_sides=False): """ Multiplication of rings with, e.g., lists. NOTE: This method is used to create ideals. It is the same as the multiplication method for :class:`~sage.rings.ring.Ring`. However, not all parents that belong to the category of rings also inherits from the base class of rings. Therefore, we implemented a ``__mul__`` method for parents, that calls a ``_mul_`` method implemented here. See :trac:`7797`. INPUT: - `x`, an object to multiply with. - `switch_sides` (optional bool): If ``False``, the product is ``self*x``; if ``True``, the product is ``x*self``. EXAMPLES: As we mentioned above, this method is called when a ring is involved that does not inherit from the base class of rings. This is the case, e.g., for matrix algebras:: sage: MS = MatrixSpace(QQ,2,2) sage: isinstance(MS,Ring) False sage: MS in Rings() True sage: MS*2 # indirect doctest Left Ideal ( [2 0] [0 2] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field In the next example, the ring and the other factor switch sides in the product:: sage: [MS.2]*MS Right Ideal ( [0 0] [1 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field AUTHOR: - Simon King (2011-03-22) """ try: if self.is_commutative(): return self.ideal(x) except (AttributeError, NotImplementedError): pass try: side = x.side() except AttributeError: return self.ideal(x, side='right' if switch_sides else 'left') # presumably x is an ideal... try: x = x.gens() except (AttributeError, NotImplementedError): pass # ... not an ideal if switch_sides: if side in ['right', 'twosided']: return self.ideal(x, side=side) elif side == 'left': return self.ideal(x, side='twosided') else: if side in ['left', 'twosided']: return self.ideal(x, side=side) elif side == 'right': return self.ideal(x, side='twosided') # duck typing failed raise TypeError( "Don't know how to transform %s into an ideal of %s" % (x, self)) @cached_method def ideal_monoid(self): """ The monoid of the ideals of this ring. NOTE: The code is copied from the base class of rings. This is since there are rings that do not inherit from that class, such as matrix algebras. See :trac:`7797`. EXAMPLES:: sage: MS = MatrixSpace(QQ,2,2) sage: isinstance(MS,Ring) False sage: MS in Rings() True sage: MS.ideal_monoid() Monoid of ideals of Full MatrixSpace of 2 by 2 dense matrices over Rational Field Note that the monoid is cached:: sage: MS.ideal_monoid() is MS.ideal_monoid() True """ try: from sage.rings.ideal_monoid import IdealMonoid return IdealMonoid(self) except TypeError: from sage.rings.noncommutative_ideals import IdealMonoid_nc return IdealMonoid_nc(self) def characteristic(self): """ Return the characteristic of this ring. EXAMPLES:: sage: QQ.characteristic() 0 sage: GF(19).characteristic() 19 sage: Integers(8).characteristic() 8 sage: Zp(5).characteristic() 0 """ from sage.rings.infinity import infinity from sage.rings.integer_ring import ZZ order_1 = self.one().additive_order() return ZZ.zero() if order_1 is infinity else order_1 def _test_characteristic(self, **options): """ Run generic tests on the method :meth:`characteristic`. See also: :class:`TestSuite`. EXAMPLES:: sage: ZZ._test_characteristic() """ tester = self._tester(**options) try: characteristic = self.characteristic() except AttributeError: return # raised when self.one() does not have a additive_order() except NotImplementedError: return # test that #12988 is fixed from sage.rings.integer import Integer tester.assertIsInstance(characteristic, Integer) def ideal(self, *args, **kwds): """ Create an ideal of this ring. NOTE: The code is copied from the base class :class:`~sage.rings.ring.Ring`. This is because there are rings that do not inherit from that class, such as matrix algebras. See :trac:`7797`. INPUT: - An element or a list/tuple/sequence of elements. - ``coerce`` (optional bool, default ``True``): First coerce the elements into this ring. - ``side``, optional string, one of ``"twosided"`` (default), ``"left"``, ``"right"``: determines whether the resulting ideal is twosided, a left ideal or a right ideal. EXAMPLES:: sage: MS = MatrixSpace(QQ,2,2) sage: isinstance(MS,Ring) False sage: MS in Rings() True sage: MS.ideal(2) Twosided Ideal ( [2 0] [0 2] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field sage: MS.ideal([MS.0,MS.1],side='right') Right Ideal ( [1 0] [0 0], <BLANKLINE> [0 1] [0 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field """ if 'coerce' in kwds: coerce = kwds['coerce'] del kwds['coerce'] else: coerce = True from sage.rings.ideal import Ideal_generic from types import GeneratorType if len(args) == 0: gens = [self(0)] else: gens = args while isinstance( gens, (list, tuple, GeneratorType)) and len(gens) == 1: first = gens[0] if isinstance(first, Ideal_generic): R = first.ring() m = self.convert_map_from(R) if m is not None: gens = [m(g) for g in first.gens()] coerce = False else: m = R.convert_map_from(self) if m is not None: raise NotImplementedError else: raise TypeError break elif isinstance(first, (list, tuple, GeneratorType)): gens = first else: try: if self.has_coerce_map_from(first): gens = first.gens( ) # we have a ring as argument elif isinstance(first, Element): gens = [first] else: raise ArithmeticError( "There is no coercion from %s to %s" % (first, self)) except TypeError: # first may be a ring element pass break if coerce: gens = [self(g) for g in gens] from sage.categories.principal_ideal_domains import PrincipalIdealDomains if self in PrincipalIdealDomains(): # Use GCD algorithm to obtain a principal ideal g = gens[0] if len(gens) == 1: try: g = g.gcd( g ) # note: we set g = gcd(g, g) to "canonicalize" the generator: make polynomials monic, etc. except (AttributeError, NotImplementedError): pass else: for h in gens[1:]: g = g.gcd(h) gens = [g] if 'ideal_class' in kwds: C = kwds['ideal_class'] del kwds['ideal_class'] else: C = self._ideal_class_(len(gens)) if len(gens) == 1 and isinstance(gens[0], (list, tuple)): gens = gens[0] return C(self, gens, **kwds) def _ideal_class_(self, n=0): """ Return the class that is used to implement ideals of this ring. NOTE: We copy the code from :class:`~sage.rings.ring.Ring`. This is necessary because not all rings inherit from that class, such as matrix algebras. INPUT: - ``n`` (optional integer, default 0): The number of generators of the ideal to be created. OUTPUT: The class that is used to implement ideals of this ring with ``n`` generators. NOTE: Often principal ideals (``n==1``) are implemented via a different class. EXAMPLES:: sage: MS = MatrixSpace(QQ,2,2) sage: MS._ideal_class_() <class 'sage.rings.noncommutative_ideals.Ideal_nc'> We don't know of a commutative ring in Sage that does not inherit from the base class of rings. So, we need to cheat in the next example:: sage: super(Ring,QQ)._ideal_class_.__module__ 'sage.categories.rings' sage: super(Ring,QQ)._ideal_class_() <class 'sage.rings.ideal.Ideal_generic'> sage: super(Ring,QQ)._ideal_class_(1) <class 'sage.rings.ideal.Ideal_principal'> sage: super(Ring,QQ)._ideal_class_(2) <class 'sage.rings.ideal.Ideal_generic'> """ from sage.rings.noncommutative_ideals import Ideal_nc try: if not self.is_commutative(): return Ideal_nc except (NotImplementedError, AttributeError): return Ideal_nc from sage.rings.ideal import Ideal_generic, Ideal_principal if n == 1: return Ideal_principal else: return Ideal_generic ## # Quotient rings # Again, this is defined in sage.rings.ring.pyx def quotient(self, I, names=None, **kwds): """ Quotient of a ring by a two-sided ideal. INPUT: - ``I``: A twosided ideal of this ring. - ``names``: a list of strings to be used as names for the variables in the quotient ring. - further named arguments that may be passed to the quotient ring constructor. EXAMPLES: Usually, a ring inherits a method :meth:`sage.rings.ring.Ring.quotient`. So, we need a bit of effort to make the following example work with the category framework:: sage: F.<x,y,z> = FreeAlgebra(QQ) sage: from sage.rings.noncommutative_ideals import Ideal_nc sage: from itertools import product sage: class PowerIdeal(Ideal_nc): ....: def __init__(self, R, n): ....: self._power = n ....: Ideal_nc.__init__(self, R, [R.prod(m) for m in product(R.gens(), repeat=n)]) ....: def reduce(self, x): ....: R = self.ring() ....: return add([c*R(m) for m,c in x if len(m) < self._power], R(0)) sage: I = PowerIdeal(F,3) sage: Q = Rings().parent_class.quotient(F, I); Q Quotient of Free Algebra on 3 generators (x, y, z) over Rational Field by the ideal (x^3, x^2*y, x^2*z, x*y*x, x*y^2, x*y*z, x*z*x, x*z*y, x*z^2, y*x^2, y*x*y, y*x*z, y^2*x, y^3, y^2*z, y*z*x, y*z*y, y*z^2, z*x^2, z*x*y, z*x*z, z*y*x, z*y^2, z*y*z, z^2*x, z^2*y, z^3) sage: Q.0 xbar sage: Q.1 ybar sage: Q.2 zbar sage: Q.0*Q.1 xbar*ybar sage: Q.0*Q.1*Q.0 0 """ from sage.rings.quotient_ring import QuotientRing return QuotientRing(self, I, names=names, **kwds) def quo(self, I, names=None, **kwds): """ Quotient of a ring by a two-sided ideal. NOTE: This is a synonym for :meth:`quotient`. EXAMPLES:: sage: MS = MatrixSpace(QQ,2) sage: I = MS*MS.gens()*MS ``MS`` is not an instance of :class:`~sage.rings.ring.Ring`. However it is an instance of the parent class of the category of rings. The quotient method is inherited from there:: sage: isinstance(MS,sage.rings.ring.Ring) False sage: isinstance(MS,Rings().parent_class) True sage: MS.quo(I,names = ['a','b','c','d']) Quotient of Full MatrixSpace of 2 by 2 dense matrices over Rational Field by the ideal ( [1 0] [0 0], <BLANKLINE> [0 1] [0 0], <BLANKLINE> [0 0] [1 0], <BLANKLINE> [0 0] [0 1] ) """ return self.quotient(I, names=names, **kwds) def quotient_ring(self, I, names=None, **kwds): """ Quotient of a ring by a two-sided ideal. NOTE: This is a synonyme for :meth:`quotient`. EXAMPLES:: sage: MS = MatrixSpace(QQ,2) sage: I = MS*MS.gens()*MS ``MS`` is not an instance of :class:`~sage.rings.ring.Ring`, but it is an instance of the parent class of the category of rings. The quotient method is inherited from there:: sage: isinstance(MS,sage.rings.ring.Ring) False sage: isinstance(MS,Rings().parent_class) True sage: MS.quotient_ring(I,names = ['a','b','c','d']) Quotient of Full MatrixSpace of 2 by 2 dense matrices over Rational Field by the ideal ( [1 0] [0 0], <BLANKLINE> [0 1] [0 0], <BLANKLINE> [0 0] [1 0], <BLANKLINE> [0 0] [0 1] ) """ return self.quotient(I, names=names, **kwds) def __truediv__(self, I): """ Since assigning generator names would not work properly, the construction of a quotient ring using division syntax is not supported. EXAMPLES:: sage: MS = MatrixSpace(QQ,2) sage: I = MS*MS.gens()*MS sage: MS/I Traceback (most recent call last): ... TypeError: Use self.quo(I) or self.quotient(I) to construct the quotient ring. """ raise TypeError( "Use self.quo(I) or self.quotient(I) to construct the quotient ring." ) def __getitem__(self, arg): """ Extend this ring by one or several elements to create a polynomial ring, a power series ring, or an algebraic extension. This is a convenience method intended primarily for interactive use. .. SEEALSO:: :func:`~sage.rings.polynomial.polynomial_ring_constructor.PolynomialRing`, :func:`~sage.rings.power_series_ring.PowerSeriesRing`, :meth:`~sage.rings.ring.Ring.extension`, :meth:`sage.rings.integer_ring.IntegerRing_class.__getitem__`, :meth:`sage.rings.matrix_space.MatrixSpace.__getitem__`, :meth:`sage.structure.parent.Parent.__getitem__` EXAMPLES: We create several polynomial rings:: sage: ZZ['x'] Univariate Polynomial Ring in x over Integer Ring sage: QQ['x'] Univariate Polynomial Ring in x over Rational Field sage: GF(17)['abc'] Univariate Polynomial Ring in abc over Finite Field of size 17 sage: GF(17)['a,b,c'] Multivariate Polynomial Ring in a, b, c over Finite Field of size 17 sage: GF(17)['a']['b'] Univariate Polynomial Ring in b over Univariate Polynomial Ring in a over Finite Field of size 17 We can create Ore polynomial rings:: sage: k.<t> = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: k['x', Frob] Ore Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 sage: R.<t> = QQ[] sage: der = R.derivation() sage: R['d', der] Ore Polynomial Ring in d over Univariate Polynomial Ring in t over Rational Field twisted by d/dt We can also create power series rings by using double brackets:: sage: QQ[['t']] Power Series Ring in t over Rational Field sage: ZZ[['W']] Power Series Ring in W over Integer Ring sage: ZZ[['x,y,z']] Multivariate Power Series Ring in x, y, z over Integer Ring sage: ZZ[['x','T']] Multivariate Power Series Ring in x, T over Integer Ring Use :func:`~sage.rings.fraction_field.Frac` or :meth:`~sage.rings.ring.CommutativeRing.fraction_field` to obtain the fields of rational functions and Laurent series:: sage: Frac(QQ['t']) Fraction Field of Univariate Polynomial Ring in t over Rational Field sage: Frac(QQ[['t']]) Laurent Series Ring in t over Rational Field sage: QQ[['t']].fraction_field() Laurent Series Ring in t over Rational Field Note that the same syntax can be used to create number fields:: sage: QQ[I] Number Field in I with defining polynomial x^2 + 1 with I = 1*I sage: QQ[I].coerce_embedding() Generic morphism: From: Number Field in I with defining polynomial x^2 + 1 with I = 1*I To: Complex Lazy Field Defn: I -> 1*I :: sage: QQ[sqrt(2)] Number Field in sqrt2 with defining polynomial x^2 - 2 with sqrt2 = 1.414213562373095? sage: QQ[sqrt(2)].coerce_embedding() Generic morphism: From: Number Field in sqrt2 with defining polynomial x^2 - 2 with sqrt2 = 1.414213562373095? To: Real Lazy Field Defn: sqrt2 -> 1.414213562373095? :: sage: QQ[sqrt(2),sqrt(3)] Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field and orders in number fields:: sage: ZZ[I] Order in Number Field in I with defining polynomial x^2 + 1 with I = 1*I sage: ZZ[sqrt(5)] Order in Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790? sage: ZZ[sqrt(2)+sqrt(3)] Order in Number Field in a with defining polynomial x^4 - 10*x^2 + 1 with a = 3.146264369941973? Embeddings are found for simple extensions (when that makes sense):: sage: QQi.<i> = QuadraticField(-1, 'i') sage: QQ[i].coerce_embedding() Generic morphism: From: Number Field in i with defining polynomial x^2 + 1 with i = 1*I To: Complex Lazy Field Defn: i -> 1*I TESTS: A few corner cases:: sage: QQ[()] Multivariate Polynomial Ring in no variables over Rational Field sage: QQ[[]] Traceback (most recent call last): ... TypeError: power series rings must have at least one variable These kind of expressions do not work:: sage: QQ['a,b','c'] Traceback (most recent call last): ... ValueError: variable name 'a,b' is not alphanumeric sage: QQ[['a,b','c']] Traceback (most recent call last): ... ValueError: variable name 'a,b' is not alphanumeric sage: QQ[[['x']]] Traceback (most recent call last): ... TypeError: expected R[...] or R[[...]], not R[[[...]]] Extension towers are built as follows and use distinct generator names:: sage: K = QQ[2^(1/3), 2^(1/2), 3^(1/3)] sage: K Number Field in a with defining polynomial x^3 - 2 over its base field sage: K.base_field() Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field sage: K.base_field().base_field() Number Field in b with defining polynomial x^3 - 3 Embeddings:: sage: QQ[I](I.pyobject()) I sage: a = 10^100; expr = (2*a + sqrt(2))/(2*a^2-1) sage: QQ[expr].coerce_embedding() is None False sage: QQ[sqrt(5)].gen() > 0 True sage: expr = sqrt(2) + I*(cos(pi/4, hold=True) - sqrt(2)/2) sage: QQ[expr].coerce_embedding() Generic morphism: From: Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? To: Real Lazy Field Defn: a -> 1.414213562373095? """ def normalize_arg(arg): if isinstance(arg, (tuple, list)): # Allowing arbitrary iterables would create confusion, but we # may want to support a few more. return tuple(arg) elif isinstance(arg, str): return tuple(arg.split(',')) else: return (arg, ) # 1. If arg is a list, try to return a power series ring. if isinstance(arg, list): if not arg: raise TypeError( "power series rings must have at least one variable") elif len(arg) == 1: # R[["a,b"]], R[[(a,b)]]... if isinstance(arg[0], list): raise TypeError( "expected R[...] or R[[...]], not R[[[...]]]") elts = normalize_arg(arg[0]) else: elts = normalize_arg(arg) from sage.rings.power_series_ring import PowerSeriesRing return PowerSeriesRing(self, elts) if isinstance(arg, tuple): from sage.categories.morphism import Morphism from sage.rings.derivation import RingDerivation if len(arg) == 2 and isinstance(arg[1], (Morphism, RingDerivation)): from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing return OrePolynomialRing(self, arg[1], names=arg[0]) # 2. Otherwise, if all specified elements are algebraic, try to # return an algebraic extension elts = normalize_arg(arg) try: minpolys = [a.minpoly() for a in elts] except (AttributeError, NotImplementedError, ValueError, TypeError): minpolys = None if minpolys: # how to pass in names? names = tuple(_gen_names(elts)) if len(elts) == 1: from sage.rings.all import CIF, CLF, RLF elt = elts[0] try: iv = CIF(elt) except (TypeError, ValueError): emb = None else: # First try creating an ANRoot manually, because # extension(..., embedding=CLF(expr)) (or # ...QQbar(expr)) would normalize the expression in # QQbar, which currently is VERY slow in many cases. # This may fail when minpoly has close roots or elt is # a complicated symbolic expression. # TODO: Rewrite using #19362 and/or #17886 and/or # #15600 once those issues are solved. from sage.rings.qqbar import AlgebraicNumber, ANRoot try: elt = AlgebraicNumber(ANRoot(minpolys[0], iv)) except ValueError: pass # Force a real embedding when possible, to get the # right ordered ring structure. if (iv.imag().is_zero() or iv.imag().contains_zero() and elt.imag().is_zero()): emb = RLF(elt) else: emb = CLF(elt) return self.extension(minpolys[0], names[0], embedding=emb) try: # Doing the extension all at once is best, if possible... return self.extension(minpolys, names) except (TypeError, ValueError): # ...but we can also construct it iteratively return reduce(lambda R, ext: R.extension(*ext), zip(minpolys, names), self) # 2. Otherwise, try to return a polynomial ring from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing return PolynomialRing(self, elts) def free_module(self, base=None, basis=None, map=True): """ Return a free module `V` over the specified subring together with maps to and from `V`. The default implementation only supports the case that the base ring is the ring itself. INPUT: - ``base`` -- a subring `R` so that this ring is isomorphic to a finite-rank free `R`-module `V` - ``basis`` -- (optional) a basis for this ring over the base - ``map`` -- boolean (default ``True``), whether to return `R`-linear maps to and from `V` OUTPUT: - A finite-rank free `R`-module `V` - An `R`-module isomorphism from `V` to this ring (only included if ``map`` is ``True``) - An `R`-module isomorphism from this ring to `V` (only included if ``map`` is ``True``) EXAMPLES:: sage: R.<x> = QQ[[]] sage: V, from_V, to_V = R.free_module(R) sage: v = to_V(1+x); v (1 + x) sage: from_V(v) 1 + x sage: W, from_W, to_W = R.free_module(R, basis=(1-x)) sage: W is V True sage: w = to_W(1+x); w (1 - x^2) sage: from_W(w) 1 + x + O(x^20) """ if base is None: base = self.base_ring() if base is self: V = self**1 if not map: return V if basis is not None: if isinstance(basis, (list, tuple)): if len(basis) != 1: raise ValueError("Basis must have length 1") basis = basis[0] basis = self(basis) if not basis.is_unit(): raise ValueError("Basis element must be a unit") from sage.modules.free_module_morphism import BaseIsomorphism1D_from_FM, BaseIsomorphism1D_to_FM Hfrom = V.Hom(self) Hto = self.Hom(V) from_V = Hfrom.__make_element_class__( BaseIsomorphism1D_from_FM)(Hfrom, basis=basis) to_V = Hto.__make_element_class__(BaseIsomorphism1D_to_FM)( Hto, basis=basis) return V, from_V, to_V else: if not self.has_coerce_map_from(base): raise ValueError("base must be a subring of this ring") raise NotImplementedError class ElementMethods: def is_unit(self): r""" Return whether this element is a unit in the ring. .. NOTE:: This is a generic implementation for (non-commutative) rings which only works for the one element, its additive inverse, and the zero element. Most rings should provide a more specialized implementation. EXAMPLES:: sage: MS = MatrixSpace(ZZ, 2) sage: MS.one().is_unit() True sage: MS.zero().is_unit() False sage: MS([1,2,3,4]).is_unit() False """ if self.is_one() or (-self).is_one(): return True if self.is_zero(): # now 0 != 1 return False raise NotImplementedError def inverse_of_unit(self): r""" Return the inverse of this element if it is a unit. OUTPUT: An element in the same ring as this element. EXAMPLES:: sage: R.<x> = ZZ[] sage: S = R.quo(x^2 + x + 1) sage: S(1).inverse_of_unit() 1 This method fails when the element is not a unit:: sage: 2.inverse_of_unit() Traceback (most recent call last): ... ArithmeticError: inverse does not exist The inverse returned is in the same ring as this element:: sage: a = -1 sage: a.parent() Integer Ring sage: a.inverse_of_unit().parent() Integer Ring Note that this is often not the case when computing inverses in other ways:: sage: (~a).parent() Rational Field sage: (1/a).parent() Rational Field """ try: if not self.is_unit(): raise ArithmeticError("element is not a unit") except NotImplementedError: # if an element does not implement is_unit, we just try to # invert it anyway; if the result is in the ring again, it was # a unit pass inverse = ~self if inverse not in self.parent(): raise ArithmeticError("element is not a unit") # return the inverse (with the correct parent) return self.parent()(inverse) def _divide_if_possible(self, y): """ Divide ``self`` by ``y`` if possible and raise a ``ValueError`` otherwise. EXAMPLES:: sage: 4._divide_if_possible(2) 2 sage: _.parent() Integer Ring :: sage: 4._divide_if_possible(3) Traceback (most recent call last): ... ValueError: 4 is not divisible by 3 """ q, r = self.quo_rem(y) if r != 0: raise ValueError("%s is not divisible by %s" % (self, y)) return q
class Rings(CategoryWithAxiom): """ The category of rings Associative rings with unit, not necessarily commutative EXAMPLES:: sage: Rings() Category of rings sage: sorted(Rings().super_categories(), key=str) [Category of rngs, Category of semirings] sage: sorted(Rings().axioms()) ['AdditiveAssociative', 'AdditiveCommutative', 'AdditiveInverse', 'AdditiveUnital', 'Associative', 'Distributive', 'Unital'] sage: Rings() is (CommutativeAdditiveGroups() & Monoids()).Distributive() True sage: Rings() is Rngs().Unital() True sage: Rings() is Semirings().AdditiveInverse() True TESTS:: sage: TestSuite(Rings()).run() .. TODO:: (see: http://trac.sagemath.org/sage_trac/wiki/CategoriesRoadMap) - Make Rings() into a subcategory or alias of Algebras(ZZ); - A parent P in the category ``Rings()`` should automatically be in the category ``Algebras(P)``. """ _base_category_class_and_axiom = (Rngs, "Unital") class SubcategoryMethods: def NoZeroDivisors(self): """ Return the full subcategory of the objects of ``self`` having no nonzero zero divisors. A *zero divisor* in a ring `R` is an element `x \in R` such that there exists a nonzero element `y \in R` such that `x \cdot y = 0` or `y \cdot x = 0` (see :wikipedia:`Zero_divisor`). EXAMPLES:: sage: Rings().NoZeroDivisors() Category of domains .. NOTE:: This could be generalized to :class:`MagmasAndAdditiveMagmas.Distributive.AdditiveUnital`. TESTS:: sage: TestSuite(Rings().NoZeroDivisors()).run() sage: Algebras(QQ).NoZeroDivisors.__module__ 'sage.categories.rings' """ return self._with_axiom('NoZeroDivisors') def Division(self): """ Return the full subcategory of the division objects of ``self``. A ring satisfies the *division axiom* if all non-zero elements have multiplicative inverses. .. NOTE:: This could be generalized to :class:`MagmasAndAdditiveMagmas.Distributive.AdditiveUnital`. EXAMPLES:: sage: Rings().Division() Category of division rings sage: Rings().Commutative().Division() Category of fields TESTS:: sage: TestSuite(Rings().Division()).run() sage: Algebras(QQ).Division.__module__ 'sage.categories.rings' """ return self._with_axiom('Division') NoZeroDivisors = LazyImport('sage.categories.domains', 'Domains', at_startup=True) Division = LazyImport('sage.categories.division_rings', 'DivisionRings', at_startup=True) Commutative = LazyImport('sage.categories.commutative_rings', 'CommutativeRings', at_startup=True) class ParentMethods: def is_ring(self): """ Return True, since this in an object of the category of rings. EXAMPLES:: sage: Parent(QQ,category=Rings()).is_ring() True """ return True def is_zero(self): """ Return ``True`` if this is the zero ring. EXAMPLES:: sage: Integers(1).is_zero() True sage: Integers(2).is_zero() False sage: QQ.is_zero() False sage: R.<x> = ZZ[] sage: R.quo(1).is_zero() True sage: R.<x> = GF(101)[] sage: R.quo(77).is_zero() True sage: R.quo(x^2+1).is_zero() False """ return self.one() == self.zero() def bracket(self, x, y): """ Returns the Lie bracket `[x, y] = x y - y x` of `x` and `y`. INPUT: - ``x``, ``y`` -- elements of ``self`` EXAMPLES:: sage: F = AlgebrasWithBasis(QQ).example() sage: F An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field sage: a,b,c = F.algebra_generators() sage: F.bracket(a,b) B[word: ab] - B[word: ba] This measures the default of commutation between `x` and `y`. `F` endowed with the bracket operation is a Lie algebra; in particular, it satisfies Jacobi's identity:: sage: F.bracket( F.bracket(a,b), c) + F.bracket(F.bracket(b,c),a) + F.bracket(F.bracket(c,a),b) 0 """ return x * y - y * x def _Hom_(self, Y, category): r""" Returns the homset from ``self`` to ``Y`` in the category ``category`` INPUT: - ``Y`` -- a ring - ``category`` -- a subcategory of :class:`Rings`() or None The sole purpose of this method is to construct the homset as a :class:`~sage.rings.homset.RingHomset`. If ``category`` is specified and is not a subcategory of :class:`Rings`, a ``TypeError`` is raised instead This method is not meant to be called directly. Please use :func:`sage.categories.homset.Hom` instead. EXAMPLES:: sage: H = QQ._Hom_(QQ, category = Rings()); H Set of Homomorphisms from Rational Field to Rational Field sage: H.__class__ <class 'sage.rings.homset.RingHomset_generic_with_category'> TESTS:: sage: Hom(QQ, QQ, category = Rings()).__class__ <class 'sage.rings.homset.RingHomset_generic_with_category'> sage: Hom(CyclotomicField(3), QQ, category = Rings()).__class__ <class 'sage.rings.number_field.morphism.CyclotomicFieldHomset_with_category'> sage: TestSuite(Hom(QQ, QQ, category = Rings())).run() # indirect doctest """ if category is not None and not category.is_subcategory(Rings()): raise TypeError("%s is not a subcategory of Rings()" % category) if Y not in Rings(): raise TypeError("%s is not a ring" % Y) from sage.rings.homset import RingHomset return RingHomset(self, Y, category=category) # this is already in sage.rings.ring.Ring, # but not all rings descend from that class, # e.g., matrix spaces. def _mul_(self, x, switch_sides=False): """ Multiplication of rings with, e.g., lists. NOTE: This method is used to create ideals. It is the same as the multiplication method for :class:`~sage.rings.ring.Ring`. However, not all parents that belong to the category of rings also inherits from the base class of rings. Therefore, we implemented a ``__mul__`` method for parents, that calls a ``_mul_`` method implemented here. See :trac:`7797`. INPUT: - `x`, an object to multiply with. - `switch_sides` (optional bool): If ``False``, the product is ``self*x``; if ``True``, the product is ``x*self``. EXAMPLE: As we mentioned above, this method is called when a ring is involved that does not inherit from the base class of rings. This is the case, e.g., for matrix algebras:: sage: MS = MatrixSpace(QQ,2,2) sage: isinstance(MS,Ring) False sage: MS in Rings() True sage: MS*2 # indirect doctest Left Ideal ( [2 0] [0 2] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field In the next example, the ring and the other factor switch sides in the product:: sage: [MS.2]*MS Right Ideal ( [0 0] [1 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field AUTHOR: - Simon King (2011-03-22) """ try: if self.is_commutative(): return self.ideal(x) except (AttributeError, NotImplementedError): pass try: side = x.side() except AttributeError: return self.ideal(x, side='right' if switch_sides else 'left') # presumably x is an ideal... try: x = x.gens() except (AttributeError, NotImplementedError): pass # ... not an ideal if switch_sides: if side in ['right', 'twosided']: return self.ideal(x, side=side) elif side == 'left': return self.ideal(x, side='twosided') else: if side in ['left', 'twosided']: return self.ideal(x, side=side) elif side == 'right': return self.ideal(x, side='twosided') # duck typing failed raise TypeError( "Don't know how to transform %s into an ideal of %s" % (x, self)) @cached_method def ideal_monoid(self): """ The monoid of the ideals of this ring. NOTE: The code is copied from the base class of rings. This is since there are rings that do not inherit from that class, such as matrix algebras. See :trac:`7797`. EXAMPLE:: sage: MS = MatrixSpace(QQ,2,2) sage: isinstance(MS,Ring) False sage: MS in Rings() True sage: MS.ideal_monoid() Monoid of ideals of Full MatrixSpace of 2 by 2 dense matrices over Rational Field Note that the monoid is cached:: sage: MS.ideal_monoid() is MS.ideal_monoid() True """ try: from sage.rings.ideal_monoid import IdealMonoid return IdealMonoid(self) except TypeError: from sage.rings.noncommutative_ideals import IdealMonoid_nc return IdealMonoid_nc(self) def characteristic(self): """ Return the characteristic of this ring. EXAMPLES:: sage: QQ.characteristic() 0 sage: GF(19).characteristic() 19 sage: Integers(8).characteristic() 8 sage: Zp(5).characteristic() 0 """ from sage.rings.infinity import infinity from sage.rings.integer_ring import ZZ order_1 = self.one().additive_order() return ZZ.zero() if order_1 is infinity else order_1 def _test_characteristic(self, **options): """ Run generic tests on the method :meth:`characteristic`. See also: :class:`TestSuite`. EXAMPLES:: sage: ZZ._test_characteristic() """ tester = self._tester(**options) try: characteristic = self.characteristic() except AttributeError: return # raised when self.one() does not have a additive_order() except NotImplementedError: return # test that #12988 is fixed from sage.rings.integer import Integer tester.assertIsInstance(characteristic, Integer) def ideal(self, *args, **kwds): """ Create an ideal of this ring. NOTE: The code is copied from the base class :class:`~sage.rings.ring.Ring`. This is because there are rings that do not inherit from that class, such as matrix algebras. See :trac:`7797`. INPUT: - An element or a list/tuple/sequence of elements. - ``coerce`` (optional bool, default ``True``): First coerce the elements into this ring. - ``side``, optional string, one of ``"twosided"`` (default), ``"left"``, ``"right"``: determines whether the resulting ideal is twosided, a left ideal or a right ideal. EXAMPLE:: sage: MS = MatrixSpace(QQ,2,2) sage: isinstance(MS,Ring) False sage: MS in Rings() True sage: MS.ideal(2) Twosided Ideal ( [2 0] [0 2] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field sage: MS.ideal([MS.0,MS.1],side='right') Right Ideal ( [1 0] [0 0], <BLANKLINE> [0 1] [0 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field """ if 'coerce' in kwds: coerce = kwds['coerce'] del kwds['coerce'] else: coerce = True from sage.rings.ideal import Ideal_generic from types import GeneratorType if len(args) == 0: gens = [self(0)] else: gens = args while isinstance( gens, (list, tuple, GeneratorType)) and len(gens) == 1: first = gens[0] if isinstance(first, Ideal_generic): R = first.ring() m = self.convert_map_from(R) if m is not None: gens = [m(g) for g in first.gens()] coerce = False else: m = R.convert_map_from(self) if m is not None: raise NotImplementedError else: raise TypeError break elif isinstance(first, (list, tuple, GeneratorType)): gens = first else: try: if self.has_coerce_map_from(first): gens = first.gens( ) # we have a ring as argument elif isinstance(first, Element): gens = [first] else: raise ArithmeticError( "There is no coercion from %s to %s" % (first, self)) except TypeError: # first may be a ring element pass break if coerce: gens = [self(g) for g in gens] from sage.categories.principal_ideal_domains import PrincipalIdealDomains if self in PrincipalIdealDomains(): # Use GCD algorithm to obtain a principal ideal g = gens[0] if len(gens) == 1: try: g = g.gcd( g ) # note: we set g = gcd(g, g) to "canonicalize" the generator: make polynomials monic, etc. except (AttributeError, NotImplementedError): pass else: for h in gens[1:]: g = g.gcd(h) gens = [g] if 'ideal_class' in kwds: C = kwds['ideal_class'] del kwds['ideal_class'] else: C = self._ideal_class_(len(gens)) if len(gens) == 1 and isinstance(gens[0], (list, tuple)): gens = gens[0] return C(self, gens, **kwds) def _ideal_class_(self, n=0): """ Return the class that is used to implement ideals of this ring. NOTE: We copy the code from :class:`~sage.rings.ring.Ring`. This is necessary because not all rings inherit from that class, such as matrix algebras. INPUT: - ``n`` (optional integer, default 0): The number of generators of the ideal to be created. OUTPUT: The class that is used to implement ideals of this ring with ``n`` generators. NOTE: Often principal ideals (``n==1``) are implemented via a different class. EXAMPLES:: sage: MS = MatrixSpace(QQ,2,2) sage: MS._ideal_class_() <class 'sage.rings.noncommutative_ideals.Ideal_nc'> We don't know of a commutative ring in Sage that does not inherit from the base class of rings. So, we need to cheat in the next example:: sage: super(Ring,QQ)._ideal_class_.__module__ 'sage.categories.rings' sage: super(Ring,QQ)._ideal_class_() <class 'sage.rings.ideal.Ideal_generic'> sage: super(Ring,QQ)._ideal_class_(1) <class 'sage.rings.ideal.Ideal_principal'> sage: super(Ring,QQ)._ideal_class_(2) <class 'sage.rings.ideal.Ideal_generic'> """ from sage.rings.noncommutative_ideals import Ideal_nc try: if not self.is_commutative(): return Ideal_nc except (NotImplementedError, AttributeError): return Ideal_nc from sage.rings.ideal import Ideal_generic, Ideal_principal if n == 1: return Ideal_principal else: return Ideal_generic ## # Quotient rings # Again, this is defined in sage.rings.ring.pyx def quotient(self, I, names=None): """ Quotient of a ring by a two-sided ideal. INPUT: - ``I``: A twosided ideal of this ring. - ``names``: a list of strings to be used as names for the variables in the quotient ring. EXAMPLES: Usually, a ring inherits a method :meth:`sage.rings.ring.Ring.quotient`. So, we need a bit of effort to make the following example work with the category framework:: sage: F.<x,y,z> = FreeAlgebra(QQ) sage: from sage.rings.noncommutative_ideals import Ideal_nc sage: from itertools import product sage: class PowerIdeal(Ideal_nc): ....: def __init__(self, R, n): ....: self._power = n ....: Ideal_nc.__init__(self, R, [R.prod(m) for m in product(R.gens(), repeat=n)]) ....: def reduce(self, x): ....: R = self.ring() ....: return add([c*R(m) for m,c in x if len(m) < self._power], R(0)) ....: sage: I = PowerIdeal(F,3) sage: Q = Rings().parent_class.quotient(F, I); Q Quotient of Free Algebra on 3 generators (x, y, z) over Rational Field by the ideal (x^3, x^2*y, x^2*z, x*y*x, x*y^2, x*y*z, x*z*x, x*z*y, x*z^2, y*x^2, y*x*y, y*x*z, y^2*x, y^3, y^2*z, y*z*x, y*z*y, y*z^2, z*x^2, z*x*y, z*x*z, z*y*x, z*y^2, z*y*z, z^2*x, z^2*y, z^3) sage: Q.0 xbar sage: Q.1 ybar sage: Q.2 zbar sage: Q.0*Q.1 xbar*ybar sage: Q.0*Q.1*Q.0 0 """ from sage.rings.quotient_ring import QuotientRing return QuotientRing(self, I, names=names) def quo(self, I, names=None): """ Quotient of a ring by a two-sided ideal. NOTE: This is a synonym for :meth:`quotient`. EXAMPLE:: sage: MS = MatrixSpace(QQ,2) sage: I = MS*MS.gens()*MS ``MS`` is not an instance of :class:`~sage.rings.ring.Ring`. However it is an instance of the parent class of the category of rings. The quotient method is inherited from there:: sage: isinstance(MS,sage.rings.ring.Ring) False sage: isinstance(MS,Rings().parent_class) True sage: MS.quo(I,names = ['a','b','c','d']) Quotient of Full MatrixSpace of 2 by 2 dense matrices over Rational Field by the ideal ( [1 0] [0 0], <BLANKLINE> [0 1] [0 0], <BLANKLINE> [0 0] [1 0], <BLANKLINE> [0 0] [0 1] ) """ return self.quotient(I, names=names) def quotient_ring(self, I, names=None): """ Quotient of a ring by a two-sided ideal. NOTE: This is a synonyme for :meth:`quotient`. EXAMPLE:: sage: MS = MatrixSpace(QQ,2) sage: I = MS*MS.gens()*MS ``MS`` is not an instance of :class:`~sage.rings.ring.Ring`, but it is an instance of the parent class of the category of rings. The quotient method is inherited from there:: sage: isinstance(MS,sage.rings.ring.Ring) False sage: isinstance(MS,Rings().parent_class) True sage: MS.quotient_ring(I,names = ['a','b','c','d']) Quotient of Full MatrixSpace of 2 by 2 dense matrices over Rational Field by the ideal ( [1 0] [0 0], <BLANKLINE> [0 1] [0 0], <BLANKLINE> [0 0] [1 0], <BLANKLINE> [0 0] [0 1] ) """ return self.quotient(I, names=names) def __truediv__(self, I): """ Since assigning generator names would not work properly, the construction of a quotient ring using division syntax is not supported. EXAMPLE:: sage: MS = MatrixSpace(QQ,2) sage: I = MS*MS.gens()*MS sage: MS/I Traceback (most recent call last): ... TypeError: Use self.quo(I) or self.quotient(I) to construct the quotient ring. """ raise TypeError( "Use self.quo(I) or self.quotient(I) to construct the quotient ring." ) def __getitem__(self, arg): """ Extend this ring by one or several elements to create a polynomial ring, a power series ring, or an algebraic extension. This is a convenience method intended primarily for interactive use. .. SEEALSO:: :func:`~sage.rings.polynomial.polynomial_ring_constructor.PolynomialRing`, :func:`~sage.rings.power_series_ring.PowerSeriesRing`, :meth:`~sage.rings.ring.Ring.extension`, :meth:`sage.rings.integer_ring.IntegerRing_class.__getitem__`, :meth:`sage.rings.matrix_space.MatrixSpace.__getitem__`, :meth:`sage.structure.parent.Parent.__getitem__` EXAMPLES: We create several polynomial rings:: sage: ZZ['x'] Univariate Polynomial Ring in x over Integer Ring sage: QQ['x'] Univariate Polynomial Ring in x over Rational Field sage: GF(17)['abc'] Univariate Polynomial Ring in abc over Finite Field of size 17 sage: GF(17)['a,b,c'] Multivariate Polynomial Ring in a, b, c over Finite Field of size 17 sage: GF(17)['a']['b'] Univariate Polynomial Ring in b over Univariate Polynomial Ring in a over Finite Field of size 17 We can also create power series rings by using double brackets:: sage: QQ[['t']] Power Series Ring in t over Rational Field sage: ZZ[['W']] Power Series Ring in W over Integer Ring sage: ZZ[['x,y,z']] Multivariate Power Series Ring in x, y, z over Integer Ring sage: ZZ[['x','T']] Multivariate Power Series Ring in x, T over Integer Ring Use :func:`~sage.rings.fraction_field.Frac` or :meth:`~sage.rings.ring.CommutativeRing.fraction_field` to obtain the fields of rational functions and Laurent series:: sage: Frac(QQ['t']) Fraction Field of Univariate Polynomial Ring in t over Rational Field sage: Frac(QQ[['t']]) Laurent Series Ring in t over Rational Field sage: QQ[['t']].fraction_field() Laurent Series Ring in t over Rational Field Note that the same syntax can be used to create number fields:: sage: QQ[I] Number Field in I with defining polynomial x^2 + 1 sage: QQ[sqrt(2)] Number Field in sqrt2 with defining polynomial x^2 - 2 sage: QQ[sqrt(2),sqrt(3)] Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field and orders in number fields:: sage: ZZ[I] Order in Number Field in I with defining polynomial x^2 + 1 sage: ZZ[sqrt(5)] Order in Number Field in sqrt5 with defining polynomial x^2 - 5 sage: ZZ[sqrt(2)+sqrt(3)] Order in Number Field in a with defining polynomial x^4 - 10*x^2 + 1 TESTS: A few corner cases:: sage: QQ[()] Multivariate Polynomial Ring in no variables over Rational Field sage: QQ[[]] Traceback (most recent call last): ... TypeError: power series rings must have at least one variable Some flexibility is allowed when specifying variables:: sage: QQ["x", SR.var('y'), polygen(CC, 'z')] Multivariate Polynomial Ring in x, y, z over Rational Field sage: QQ[["x", SR.var('y'), polygen(CC, 'z')]] Multivariate Power Series Ring in x, y, z over Rational Field but more baroque expressions do not work:: sage: QQ['a,b','c'] Traceback (most recent call last): ... ValueError: variable name 'a,b' is not alphanumeric sage: QQ[['a,b','c']] Traceback (most recent call last): ... ValueError: variable name 'a,b' is not alphanumeric sage: QQ[[['x']]] Traceback (most recent call last): ... TypeError: expected R[...] or R[[...]], not R[[[...]]] Extension towers are built as follows and use distinct generator names:: sage: K = QQ[2^(1/3), 2^(1/2), 3^(1/3)] sage: K Number Field in a with defining polynomial x^3 - 2 over its base field sage: K.base_field() Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field sage: K.base_field().base_field() Number Field in b with defining polynomial x^3 - 3 """ def normalize_arg(arg): if isinstance(arg, (tuple, list)): # Allowing arbitrary iterables would create confusion, but we # may want to support a few more. return tuple(arg) elif isinstance(arg, str): return tuple(arg.split(',')) else: return (arg, ) # 1. If arg is a list, try to return a power series ring. if isinstance(arg, list): if arg == []: raise TypeError( "power series rings must have at least one variable") elif len(arg) == 1: # R[["a,b"]], R[[(a,b)]]... if isinstance(arg[0], list): raise TypeError( "expected R[...] or R[[...]], not R[[[...]]]") elts = normalize_arg(arg[0]) else: elts = normalize_arg(arg) from sage.rings.power_series_ring import PowerSeriesRing return PowerSeriesRing(self, elts) # 2. Otherwise, if all specified elements are algebraic, try to # return an algebraic extension elts = normalize_arg(arg) try: minpolys = [a.minpoly() for a in elts] except (AttributeError, NotImplementedError, ValueError, TypeError): minpolys = None if minpolys: # how to pass in names? # TODO: set up embeddings names = tuple(_gen_names(elts)) try: # Doing the extension all at once is best, if possible... return self.extension(minpolys, names) except (TypeError, ValueError): # ...but we can also construct it iteratively return reduce(lambda R, ext: R.extension(*ext), zip(minpolys, names), self) # 2. Otherwise, try to return a polynomial ring from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing return PolynomialRing(self, elts) class ElementMethods: def is_unit(self): r""" Return whether this element is a unit in the ring. .. NOTE:: This is a generic implementation for (non-commutative) rings which only works for the one element, its additive inverse, and the zero element. Most rings should provide a more specialized implementation. EXAMPLES:: sage: MS = MatrixSpace(ZZ, 2) sage: MS.one().is_unit() True sage: MS.zero().is_unit() False sage: MS([1,2,3,4]).is_unit() False """ if self.is_one() or (-self).is_one(): return True if self.is_zero(): # now 0 != 1 return False raise NotImplementedError
class CoalgebrasWithBasis(CategoryWithAxiom_over_base_ring): """ The category of coalgebras with a distinguished basis. EXAMPLES:: sage: CoalgebrasWithBasis(ZZ) Category of coalgebras with basis over Integer Ring sage: sorted(CoalgebrasWithBasis(ZZ).super_categories(), key=str) [Category of coalgebras over Integer Ring, Category of modules with basis over Integer Ring] TESTS:: sage: TestSuite(CoalgebrasWithBasis(ZZ)).run() """ Graded = LazyImport('sage.categories.graded_coalgebras_with_basis', 'GradedCoalgebrasWithBasis') class Filtered(FilteredModulesCategory): """ Category of filtered coalgebras. """ class ParentMethods: @abstract_method(optional = True) def coproduct_on_basis(self, i): """ The coproduct of the algebra on the basis (optional). INPUT: - ``i`` -- the indices of an element of the basis of ``self`` Returns the coproduct of the corresponding basis elements If implemented, the coproduct of the algebra is defined from it by linearity. EXAMPLES:: sage: A = HopfAlgebrasWithBasis(QQ).example(); A An example of Hopf algebra with basis: the group algebra of the Dihedral group of order 6 as a permutation group over Rational Field sage: (a, b) = A._group.gens() sage: A.coproduct_on_basis(a) B[(1,2,3)] # B[(1,2,3)] """ @lazy_attribute def coproduct(self): r""" If :meth:`coproduct_on_basis` is available, construct the coproduct morphism from ``self`` to ``self`` `\otimes` ``self`` by extending it by linearity. Otherwise, use :meth:`~Coalgebras.Realizations.ParentMethods.coproduct_by_coercion`, if available. EXAMPLES:: sage: A = HopfAlgebrasWithBasis(QQ).example(); A An example of Hopf algebra with basis: the group algebra of the Dihedral group of order 6 as a permutation group over Rational Field sage: [a,b] = A.algebra_generators() sage: a, A.coproduct(a) (B[(1,2,3)], B[(1,2,3)] # B[(1,2,3)]) sage: b, A.coproduct(b) (B[(1,3)], B[(1,3)] # B[(1,3)]) """ if self.coproduct_on_basis is not NotImplemented: # TODO: if self is a hopf algebra, then one would want # to create a morphism of algebras with basis instead # should there be a method self.coproduct_homset_category? return Hom(self, tensor([self, self]), ModulesWithBasis(self.base_ring()))(on_basis = self.coproduct_on_basis) elif hasattr(self, "coproduct_by_coercion"): return self.coproduct_by_coercion @abstract_method(optional = True) def counit_on_basis(self, i): """ The counit of the algebra on the basis (optional). INPUT: - ``i`` -- the indices of an element of the basis of ``self`` Returns the counit of the corresponding basis elements If implemented, the counit of the algebra is defined from it by linearity. EXAMPLES:: sage: A = HopfAlgebrasWithBasis(QQ).example(); A An example of Hopf algebra with basis: the group algebra of the Dihedral group of order 6 as a permutation group over Rational Field sage: (a, b) = A._group.gens() sage: A.counit_on_basis(a) 1 """ @lazy_attribute def counit(self): r""" If :meth:`counit_on_basis` is available, construct the counit morphism from ``self`` to ``self`` `\otimes` ``self`` by extending it by linearity EXAMPLES:: sage: A = HopfAlgebrasWithBasis(QQ).example(); A An example of Hopf algebra with basis: the group algebra of the Dihedral group of order 6 as a permutation group over Rational Field sage: [a,b] = A.algebra_generators() sage: a, A.counit(a) (B[(1,2,3)], 1) sage: b, A.counit(b) (B[(1,3)], 1) """ if self.counit_on_basis is not NotImplemented: return self.module_morphism(self.counit_on_basis,codomain=self.base_ring()) elif hasattr(self, "counit_by_coercion"): return self.counit_by_coercion class ElementMethods: def coproduct_iterated(self, n=1): r""" Apply ``n`` coproducts to ``self``. .. TODO:: Remove dependency on ``modules_with_basis`` methods. EXAMPLES:: sage: Psi = NonCommutativeSymmetricFunctions(QQ).Psi() sage: Psi[2,2].coproduct_iterated(0) Psi[2, 2] sage: Psi[2,2].coproduct_iterated(2) Psi[] # Psi[] # Psi[2, 2] + 2*Psi[] # Psi[2] # Psi[2] + Psi[] # Psi[2, 2] # Psi[] + 2*Psi[2] # Psi[] # Psi[2] + 2*Psi[2] # Psi[2] # Psi[] + Psi[2, 2] # Psi[] # Psi[] TESTS:: sage: p = SymmetricFunctions(QQ).p() sage: p[5,2,2].coproduct_iterated() p[] # p[5, 2, 2] + 2*p[2] # p[5, 2] + p[2, 2] # p[5] + p[5] # p[2, 2] + 2*p[5, 2] # p[2] + p[5, 2, 2] # p[] sage: p([]).coproduct_iterated(3) p[] # p[] # p[] # p[] :: sage: Psi = NonCommutativeSymmetricFunctions(QQ).Psi() sage: Psi[2,2].coproduct_iterated(0) Psi[2, 2] sage: Psi[2,2].coproduct_iterated(3) Psi[] # Psi[] # Psi[] # Psi[2, 2] + 2*Psi[] # Psi[] # Psi[2] # Psi[2] + Psi[] # Psi[] # Psi[2, 2] # Psi[] + 2*Psi[] # Psi[2] # Psi[] # Psi[2] + 2*Psi[] # Psi[2] # Psi[2] # Psi[] + Psi[] # Psi[2, 2] # Psi[] # Psi[] + 2*Psi[2] # Psi[] # Psi[] # Psi[2] + 2*Psi[2] # Psi[] # Psi[2] # Psi[] + 2*Psi[2] # Psi[2] # Psi[] # Psi[] + Psi[2, 2] # Psi[] # Psi[] # Psi[] :: sage: m = SymmetricFunctionsNonCommutingVariables(QQ).m() sage: m[[1,3],[2]].coproduct_iterated(2) m{} # m{} # m{{1, 3}, {2}} + m{} # m{{1}} # m{{1, 2}} + m{} # m{{1, 2}} # m{{1}} + m{} # m{{1, 3}, {2}} # m{} + m{{1}} # m{} # m{{1, 2}} + m{{1}} # m{{1, 2}} # m{} + m{{1, 2}} # m{} # m{{1}} + m{{1, 2}} # m{{1}} # m{} + m{{1, 3}, {2}} # m{} # m{} sage: m[[]].coproduct_iterated(3), m[[1,3],[2]].coproduct_iterated(0) (m{} # m{} # m{} # m{}, m{{1, 3}, {2}}) """ if n < 0: raise ValueError("cannot take fewer than 0 coproduct iterations: %s < 0" % str(n)) if n == 0: return self if n == 1: return self.coproduct() from sage.functions.all import floor, ceil from sage.rings.integer import Integer # Use coassociativity of `\Delta` to perform many coproducts simultaneously. fn = floor(Integer(n-1)/2); cn = ceil(Integer(n-1)/2) split = lambda a,b: tensor([a.coproduct_iterated(fn), b.coproduct_iterated(cn)]) return self.coproduct().apply_multilinear_morphism(split) class Super(SuperModulesCategory): def extra_super_categories(self): """ EXAMPLES:: sage: C = Coalgebras(ZZ).WithBasis().Super() sage: sorted(C.super_categories(), key=str) # indirect doctest [Category of graded coalgebras with basis over Integer Ring, Category of super coalgebras over Integer Ring, Category of super modules with basis over Integer Ring] """ return [self.base_category().Graded()]
class FiniteDimensional(CategoryWithAxiom_over_base_ring): WithBasis = LazyImport('sage.categories.finite_dimensional_semisimple_algebras_with_basis', 'FiniteDimensionalSemisimpleAlgebrasWithBasis')
class Semigroups(CategoryWithAxiom): """ The category of (multiplicative) semigroups. A *semigroup* is an associative :class:`magma <Magmas>`, that is a set endowed with a multiplicative binary operation `*` which is associative (see :wikipedia:`Semigroup`). The operation `*` is not required to have a neutral element. A semigroup for which such an element exists is a :class:`monoid <sage.categories.monoids.Monoids>`. EXAMPLES:: sage: C = Semigroups(); C Category of semigroups sage: C.super_categories() [Category of magmas] sage: C.all_super_categories() [Category of semigroups, Category of magmas, Category of sets, Category of sets with partial maps, Category of objects] sage: C.axioms() frozenset(['Associative']) sage: C.example() An example of a semigroup: the left zero semigroup TESTS:: sage: TestSuite(C).run() """ _base_category_class_and_axiom = (Magmas, "Associative") def example(self, choice="leftzero", **kwds): r""" Returns an example of a semigroup, as per :meth:`Category.example() <sage.categories.category.Category.example>`. INPUT: - ``choice`` -- str (default: 'leftzero'). Can be either 'leftzero' for the left zero semigroup, or 'free' for the free semigroup. - ``**kwds`` -- keyword arguments passed onto the constructor for the chosen semigroup. EXAMPLES:: sage: Semigroups().example(choice='leftzero') An example of a semigroup: the left zero semigroup sage: Semigroups().example(choice='free') An example of a semigroup: the free semigroup generated by ('a', 'b', 'c', 'd') sage: Semigroups().example(choice='free', alphabet=('a','b')) An example of a semigroup: the free semigroup generated by ('a', 'b') """ import sage.categories.examples.semigroups as examples if choice == "leftzero": return examples.LeftZeroSemigroup(**kwds) else: return examples.FreeSemigroup(**kwds) class ParentMethods: def _test_associativity(self, **options): r""" Test associativity for (not necessarily all) elements of this semigroup. INPUT:: - ``options`` -- any keyword arguments accepted by :meth:`_tester` EXAMPLES: By default, this method tests only the elements returned by ``self.some_elements()``:: sage: L = Semigroups().example(choice='leftzero') sage: L._test_associativity() However, the elements tested can be customized with the ``elements`` keyword argument:: sage: L._test_associativity(elements = (L(1), L(2), L(3))) See the documentation for :class:`TestSuite` for more information. """ tester = self._tester(**options) S = tester.some_elements() from sage.combinat.cartesian_product import CartesianProduct for x,y,z in tester.some_elements(CartesianProduct(S,S,S)): tester.assert_((x * y) * z == x * (y * z)) def prod(self, args): r""" Return the product of the list of elements ``args`` inside ``self``. EXAMPLES:: sage: S = Semigroups().example("free") sage: S.prod([S('a'), S('b'), S('c')]) 'abc' sage: S.prod([]) Traceback (most recent call last): ... AssertionError: Cannot compute an empty product in a semigroup """ assert len(args) > 0, "Cannot compute an empty product in a semigroup" return prod(args[1:], args[0]) def cayley_graph(self, side="right", simple=False, elements = None, generators = None, connecting_set = None): r""" Return the Cayley graph for this finite semigroup. INPUT: - ``side`` -- "left", "right", or "twosided": the side on which the generators act (default:"right") - ``simple`` -- boolean (default:False): if True, returns a simple graph (no loops, no labels, no multiple edges) - ``generators`` -- a list, tuple, or family of elements of ``self`` (default: ``self.semigroup_generators()``) - ``connecting_set`` -- alias for ``generators``; deprecated - ``elements`` -- a list (or iterable) of elements of ``self`` OUTPUT: - :class:`DiGraph` EXAMPLES: We start with the (right) Cayley graphs of some classical groups:: sage: D4 = DihedralGroup(4); D4 Dihedral group of order 8 as a permutation group sage: G = D4.cayley_graph() sage: show(G, color_by_label=True, edge_labels=True) sage: A5 = AlternatingGroup(5); A5 Alternating group of order 5!/2 as a permutation group sage: G = A5.cayley_graph() sage: G.show3d(color_by_label=True, edge_size=0.01, edge_size2=0.02, vertex_size=0.03) sage: G.show3d(vertex_size=0.03, edge_size=0.01, edge_size2=0.02, vertex_colors={(1,1,1):G.vertices()}, bgcolor=(0,0,0), color_by_label=True, xres=700, yres=700, iterations=200) # long time (less than a minute) sage: G.num_edges() 120 sage: w = WeylGroup(['A',3]) sage: d = w.cayley_graph(); d Digraph on 24 vertices sage: d.show3d(color_by_label=True, edge_size=0.01, vertex_size=0.03) Alternative generators may be specified:: sage: G = A5.cayley_graph(generators=[A5.gens()[0]]) sage: G.num_edges() 60 sage: g=PermutationGroup([(i+1,j+1) for i in range(5) for j in range(5) if j!=i]) sage: g.cayley_graph(generators=[(1,2),(2,3)]) Digraph on 120 vertices If ``elements`` is specified, then only the subgraph induced and those elements is returned. Here we use it to display the Cayley graph of the free monoid truncated on the elements of length at most 3:: sage: M = Monoids().example(); M An example of a monoid: the free monoid generated by ('a', 'b', 'c', 'd') sage: elements = [ M.prod(w) for w in sum((list(Words(M.semigroup_generators(),k)) for k in range(4)),[]) ] sage: G = M.cayley_graph(elements = elements) sage: G.num_verts(), G.num_edges() (85, 84) sage: G.show3d(color_by_label=True, edge_size=0.001, vertex_size=0.01) We now illustrate the ``side`` and ``simple`` options on a semigroup:: sage: S = FiniteSemigroups().example(alphabet=('a','b')) sage: g = S.cayley_graph(simple=True) sage: g.vertices() ['a', 'ab', 'b', 'ba'] sage: g.edges() [('a', 'ab', None), ('b', 'ba', None)] :: sage: g = S.cayley_graph(side="left", simple=True) sage: g.vertices() ['a', 'ab', 'b', 'ba'] sage: g.edges() [('a', 'ba', None), ('ab', 'ba', None), ('b', 'ab', None), ('ba', 'ab', None)] :: sage: g = S.cayley_graph(side="twosided", simple=True) sage: g.vertices() ['a', 'ab', 'b', 'ba'] sage: g.edges() [('a', 'ab', None), ('a', 'ba', None), ('ab', 'ba', None), ('b', 'ab', None), ('b', 'ba', None), ('ba', 'ab', None)] :: sage: g = S.cayley_graph(side="twosided") sage: g.vertices() ['a', 'ab', 'b', 'ba'] sage: g.edges() [('a', 'a', (0, 'left')), ('a', 'a', (0, 'right')), ('a', 'ab', (1, 'right')), ('a', 'ba', (1, 'left')), ('ab', 'ab', (0, 'left')), ('ab', 'ab', (0, 'right')), ('ab', 'ab', (1, 'right')), ('ab', 'ba', (1, 'left')), ('b', 'ab', (0, 'left')), ('b', 'b', (1, 'left')), ('b', 'b', (1, 'right')), ('b', 'ba', (0, 'right')), ('ba', 'ab', (0, 'left')), ('ba', 'ba', (0, 'right')), ('ba', 'ba', (1, 'left')), ('ba', 'ba', (1, 'right'))] :: sage: s1 = SymmetricGroup(1); s = s1.cayley_graph(); s.vertices() [()] TESTS:: sage: SymmetricGroup(2).cayley_graph(side="both") Traceback (most recent call last): ... ValueError: option 'side' must be 'left', 'right' or 'twosided' .. TODO:: - Add more options for constructing subgraphs of the Cayley graph, handling the standard use cases when exploring large/infinite semigroups (a predicate, generators of an ideal, a maximal length in term of the generators) - Specify good default layout/plot/latex options in the graph - Generalize to combinatorial modules with module generators / operators AUTHORS: - Bobby Moretti (2007-08-10) - Robert Miller (2008-05-01): editing - Nicolas M. Thiery (2008-12): extension to semigroups, ``side``, ``simple``, and ``elements`` options, ... """ from sage.graphs.digraph import DiGraph from groups import Groups if not side in ["left", "right", "twosided"]: raise ValueError("option 'side' must be 'left', 'right' or 'twosided'") if elements is None: assert self.is_finite(), "elements should be specified for infinite semigroups" elements = list(self) elements_set = set(elements) if simple or self in Groups(): result = DiGraph() else: result = DiGraph(multiedges = True, loops = True) result.add_vertices(elements) if connecting_set is not None: generators = connecting_set if generators is None: generators = self.semigroup_generators() if isinstance(generators, (list, tuple)): generators = dict((self(g), self(g)) for g in generators) left = (side == "left" or side == "twosided") right = (side == "right" or side == "twosided") def add_edge(source, target, label, side_label): """ Skips edges whose targets are not in elements Return an appropriate edge given the options """ if target not in elements_set: return if simple: result.add_edge([source, target]) elif side == "twosided": result.add_edge([source, target, (label, side_label)]) else: result.add_edge([source, target, label]) for x in elements: for i in generators.keys(): if left: add_edge(x, generators[i]*x, i, "left" ) if right: add_edge(x, x*generators[i], i, "right") return result class ElementMethods: def _pow_(self, n): """ Return ``self`` to the `n^{th}` power. INPUT: - ``n`` -- a positive integer EXAMPLES:: sage: S = Semigroups().example("leftzero") sage: x = S("x") sage: x^1, x^2, x^3, x^4, x^5 ('x', 'x', 'x', 'x', 'x') sage: x^0 Traceback (most recent call last): ... AssertionError TESTS:: sage: x._pow_(17) 'x' """ assert n > 0 return generic_power(self, n) __pow__ = _pow_ Finite = LazyImport('sage.categories.finite_semigroups', 'FiniteSemigroups', at_startup=True) Unital = LazyImport('sage.categories.monoids', 'Monoids', at_startup=True) ####################################### class Subquotients(SubquotientsCategory): r""" The category of subquotient semi-groups. EXAMPLES:: sage: Semigroups().Subquotients().all_super_categories() [Category of subquotients of semigroups, Category of semigroups, Category of subquotients of magmas, Category of magmas, Category of subquotients of sets, Category of sets, Category of sets with partial maps, Category of objects] [Category of subquotients of semigroups, Category of semigroups, Category of subquotients of magmas, Category of magmas, Category of subquotients of sets, Category of sets, Category of sets with partial maps, Category of objects] """ def example(self): """ Returns an example of subquotient of a semigroup, as per :meth:`Category.example() <sage.categories.category.Category.example>`. EXAMPLES:: sage: Semigroups().Subquotients().example() An example of a (sub)quotient semigroup: a quotient of the left zero semigroup """ from sage.categories.examples.semigroups import QuotientOfLeftZeroSemigroup return QuotientOfLeftZeroSemigroup(category = self.Subquotients()) class Quotients(QuotientsCategory): def example(self): r""" Return an example of quotient of a semigroup, as per :meth:`Category.example() <sage.categories.category.Category.example>`. EXAMPLES:: sage: Semigroups().Quotients().example() An example of a (sub)quotient semigroup: a quotient of the left zero semigroup """ from sage.categories.examples.semigroups import QuotientOfLeftZeroSemigroup return QuotientOfLeftZeroSemigroup() class ParentMethods: def semigroup_generators(self): r""" Return semigroup generators for ``self`` by retracting the semigroup generators of the ambient semigroup. EXAMPLES:: sage: S = FiniteSemigroups().Quotients().example().semigroup_generators() # todo: not implemented """ return self.ambient().semigroup_generators().map(self.retract) class CartesianProducts(CartesianProductsCategory): def extra_super_categories(self): """ Implement the fact that a cartesian product of semigroups is a semigroup. EXAMPLES:: sage: Semigroups().CartesianProducts().extra_super_categories() [Category of semigroups] sage: Semigroups().CartesianProducts().super_categories() [Category of semigroups, Category of Cartesian products of magmas] """ return [Semigroups()] class Algebras(AlgebrasCategory): """ TESTS:: sage: TestSuite(Semigroups().Algebras(QQ)).run() sage: TestSuite(Semigroups().Finite().Algebras(QQ)).run() """ def extra_super_categories(self): """ Implement the fact that the algebra of a semigroup is indeed a (not necessarily unital) algebra. EXAMPLES:: sage: Semigroups().Algebras(QQ).extra_super_categories() [Category of semigroups] sage: Semigroups().Algebras(QQ).super_categories() [Category of associative algebras over Rational Field, Category of magma algebras over Rational Field] """ return [Semigroups()] class ParentMethods: @cached_method def algebra_generators(self): r""" The generators of this algebra, as per :meth:`MagmaticAlgebras.ParentMethods.algebra_generators() <.magmatic_algebras.MagmaticAlgebras.ParentMethods.algebra_generators>`. They correspond to the generators of the semigroup. EXAMPLES:: sage: A = FiniteSemigroups().example().algebra(ZZ) sage: A.algebra_generators() Finite family {0: B['a'], 1: B['b'], 2: B['c'], 3: B['d']} """ return self.basis().keys().semigroup_generators().map(self.monomial) def product_on_basis(self, g1, g2): r""" Product, on basis elements, as per :meth:`MagmaticAlgebras.WithBasis.ParentMethods.product_on_basis() <.magmatic_algebras.MagmaticAlgebras.WithBasis.ParentMethods.product_on_basis>`. The product of two basis elements is induced by the product of the corresponding elements of the group. EXAMPLES:: sage: S = FiniteSemigroups().example(); S An example of a finite semigroup: the left regular band generated by ('a', 'b', 'c', 'd') sage: A = S.algebra(QQ) sage: a,b,c,d = A.algebra_generators() sage: a * b + b * d * c * d B['ab'] + B['bdc'] """ return self.monomial(g1 * g2)
class AlgebrasWithBasis(CategoryWithAxiom_over_base_ring): """ The category of algebras with a distinguished basis. EXAMPLES:: sage: C = AlgebrasWithBasis(QQ); C Category of algebras with basis over Rational Field sage: sorted(C.super_categories(), key=str) [Category of algebras over Rational Field, Category of unital algebras with basis over Rational Field] We construct a typical parent in this category, and do some computations with it:: sage: A = C.example(); A An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field sage: A.category() Category of algebras with basis over Rational Field sage: A.one_basis() word: sage: A.one() B[word: ] sage: A.base_ring() Rational Field sage: A.basis().keys() Finite words over {'a', 'b', 'c'} sage: (a,b,c) = A.algebra_generators() sage: a^3, b^2 (B[word: aaa], B[word: bb]) sage: a*c*b B[word: acb] sage: A.product <bound method FreeAlgebra_with_category._product_from_product_on_basis_multiply of An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field> sage: A.product(a*b,b) B[word: abb] sage: TestSuite(A).run(verbose=True) running ._test_additive_associativity() . . . pass running ._test_an_element() . . . pass running ._test_associativity() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass running ._test_characteristic() . . . pass running ._test_distributivity() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass running ._test_eq() . . . pass running ._test_new() . . . pass running ._test_nonzero_equal() . . . pass running ._test_not_implemented_methods() . . . pass running ._test_pickling() . . . pass pass running ._test_elements_eq_reflexive() . . . pass running ._test_elements_eq_symmetric() . . . pass running ._test_elements_eq_transitive() . . . pass running ._test_elements_neq() . . . pass running ._test_eq() . . . pass running ._test_new() . . . pass running ._test_not_implemented_methods() . . . pass running ._test_one() . . . pass running ._test_pickling() . . . pass running ._test_prod() . . . pass running ._test_some_elements() . . . pass running ._test_zero() . . . pass sage: A.__class__ <class 'sage.categories.examples.algebras_with_basis.FreeAlgebra_with_category'> sage: A.element_class <class 'sage.categories.examples.algebras_with_basis.FreeAlgebra_with_category.element_class'> Please see the source code of `A` (with ``A??``) for how to implement other algebras with basis. TESTS:: sage: TestSuite(AlgebrasWithBasis(QQ)).run() """ def example(self, alphabet=('a', 'b', 'c')): """ Return an example of algebra with basis. EXAMPLES:: sage: AlgebrasWithBasis(QQ).example() An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field An other set of generators can be specified as optional argument:: sage: AlgebrasWithBasis(QQ).example((1,2,3)) An example of an algebra with basis: the free algebra on the generators (1, 2, 3) over Rational Field """ from sage.categories.examples.algebras_with_basis import Example return Example(self.base_ring(), alphabet) Filtered = LazyImport('sage.categories.filtered_algebras_with_basis', 'FilteredAlgebrasWithBasis') FiniteDimensional = LazyImport( 'sage.categories.finite_dimensional_algebras_with_basis', 'FiniteDimensionalAlgebrasWithBasis', at_startup=True) Graded = LazyImport('sage.categories.graded_algebras_with_basis', 'GradedAlgebrasWithBasis') Super = LazyImport('sage.categories.super_algebras_with_basis', 'SuperAlgebrasWithBasis') class ParentMethods: # For backward compatibility one = UnitalAlgebras.WithBasis.ParentMethods.one # Backward compatibility temporary cruft to help migrating form CombinatorialAlgebra def _product_from_combinatorial_algebra_multiply(self, left, right): r""" Returns left\*right where left and right are elements of self. product() uses either _multiply or _multiply basis to carry out the actual multiplication. EXAMPLES:: sage: s = SymmetricFunctions(QQ).schur() sage: a = s([2]) sage: s._product_from_combinatorial_algebra_multiply(a,a) s[2, 2] + s[3, 1] + s[4] sage: s.product(a,a) s[2, 2] + s[3, 1] + s[4] """ A = left.parent() BR = A.base_ring() z_elt = {} #Do the case where the user specifies how to multiply basis elements if hasattr(self, '_multiply_basis'): for (left_m, left_c) in six.iteritems(left._monomial_coefficients): for (right_m, right_c) in six.iteritems( right._monomial_coefficients): res = self._multiply_basis(left_m, right_m) #Handle the case where the user returns a dictionary #where the keys are the monomials and the values are #the coefficients. If res is not a dictionary, then #it is assumed to be an element of self if not isinstance(res, dict): if isinstance(res, self._element_class): res = res._monomial_coefficients else: res = {res: BR(1)} for m in res: if m in z_elt: z_elt[m] = z_elt[m] + left_c * right_c * res[m] else: z_elt[m] = left_c * right_c * res[m] #We assume that the user handles the multiplication correctly on #his or her own, and returns a dict with monomials as keys and #coefficients as values else: m = self._multiply(left, right) if isinstance(m, self._element_class): return m if not isinstance(m, dict): z_elt = m.monomial_coefficients() else: z_elt = m #Remove all entries that are equal to 0 BR = self.base_ring() zero = BR(0) del_list = [] for m, c in six.iteritems(z_elt): if c == zero: del_list.append(m) for m in del_list: del z_elt[m] return self._from_dict(z_elt) #def _test_product(self, **options): # tester = self._tester(**options) # tester.assertTrue(self.product is not None) # could check that self.product is in Hom( self x self, self) def hochschild_complex(self, M): """ Return the Hochschild complex of ``self`` with coefficients in ``M``. .. SEEALSO:: :class:`~sage.homology.hochschild_complex.HochschildComplex` EXAMPLES:: sage: R.<x> = QQ[] sage: A = algebras.DifferentialWeyl(R) sage: H = A.hochschild_complex(A) sage: SGA = SymmetricGroupAlgebra(QQ, 3) sage: T = SGA.trivial_representation() sage: H = SGA.hochschild_complex(T) """ from sage.homology.hochschild_complex import HochschildComplex return HochschildComplex(self, M) class ElementMethods: def __invert__(self): """ Return the inverse of ``self`` if ``self`` is a multiple of one, and one is in the basis of this algebra. Otherwise throws an error. Caveat: this generic implementation is not complete; there may be invertible elements in the algebra that can't be inversed this way. It is correct though for graded connected algebras with basis. .. WARNING:: This might produce a result which does not belong to the parent of ``self``, yet believes to do so. For instance, inverting 2 times the unity will produce 1/2 times the unity, even if 1/2 is not in the base ring. Handle with care. EXAMPLES:: sage: C = AlgebrasWithBasis(QQ).example() sage: x = C(2); x 2*B[word: ] sage: ~x 1/2*B[word: ] sage: a = C.algebra_generators().first(); a B[word: a] sage: ~a Traceback (most recent call last): ... ValueError: cannot invert self (= B[word: a]) """ # FIXME: make this generic mcs = self.monomial_coefficients(copy=False) one = self.parent().one_basis() if len(mcs) == 1 and one in mcs: return self.parent().term(one, ~mcs[one]) else: raise ValueError("cannot invert self (= %s)" % self) class CartesianProducts(CartesianProductsCategory): """ The category of algebras with basis, constructed as Cartesian products of algebras with basis. Note: this construction give the direct products of algebras with basis. See comment in :class:`Algebras.CartesianProducts <sage.categories.algebras.Algebras.CartesianProducts>` """ def extra_super_categories(self): """ A Cartesian product of algebras with basis is endowed with a natural algebra with basis structure. EXAMPLES:: sage: AlgebrasWithBasis(QQ).CartesianProducts().extra_super_categories() [Category of algebras with basis over Rational Field] sage: AlgebrasWithBasis(QQ).CartesianProducts().super_categories() [Category of algebras with basis over Rational Field, Category of Cartesian products of algebras over Rational Field, Category of Cartesian products of vector spaces with basis over Rational Field] """ return [self.base_category()] class ParentMethods: @cached_method def one_from_cartesian_product_of_one_basis(self): """ Returns the one of this Cartesian product of algebras, as per ``Monoids.ParentMethods.one`` It is constructed as the Cartesian product of the ones of the summands, using their :meth:`~AlgebrasWithBasis.ParentMethods.one_basis` methods. This implementation does not require multiplication by scalars nor calling cartesian_product. This might help keeping things as lazy as possible upon initialization. EXAMPLES:: sage: A = AlgebrasWithBasis(QQ).example(); A An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field sage: A.one_basis() word: sage: B = cartesian_product((A, A, A)) sage: B.one_from_cartesian_product_of_one_basis() B[(0, word: )] + B[(1, word: )] + B[(2, word: )] sage: B.one() B[(0, word: )] + B[(1, word: )] + B[(2, word: )] sage: cartesian_product([SymmetricGroupAlgebra(QQ, 3), SymmetricGroupAlgebra(QQ, 4)]).one() B[(0, [1, 2, 3])] + B[(1, [1, 2, 3, 4])] """ return self.sum_of_monomials( zip(self._sets_keys(), (set.one_basis() for set in self._sets))) @lazy_attribute def one(self): """ TESTS:: sage: A = AlgebrasWithBasis(QQ).example(); A An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field sage: B = cartesian_product((A, A, A)) sage: B.one() B[(0, word: )] + B[(1, word: )] + B[(2, word: )] """ if all(hasattr(module, "one_basis") for module in self._sets): return self.one_from_cartesian_product_of_one_basis else: return NotImplemented #def product_on_basis(self, t1, t2): # would be easy to implement, but without a special # version of module morphism, this would not take # advantage of the bloc structure class TensorProducts(TensorProductsCategory): """ The category of algebras with basis constructed by tensor product of algebras with basis """ @cached_method def extra_super_categories(self): """ EXAMPLES:: sage: AlgebrasWithBasis(QQ).TensorProducts().extra_super_categories() [Category of algebras with basis over Rational Field] sage: AlgebrasWithBasis(QQ).TensorProducts().super_categories() [Category of algebras with basis over Rational Field, Category of tensor products of algebras over Rational Field, Category of tensor products of vector spaces with basis over Rational Field] """ return [self.base_category()] class ParentMethods: """ implements operations on tensor products of algebras with basis """ @cached_method def one_basis(self): """ Returns the index of the one of this tensor product of algebras, as per ``AlgebrasWithBasis.ParentMethods.one_basis`` It is the tuple whose operands are the indices of the ones of the operands, as returned by their :meth:`.one_basis` methods. EXAMPLES:: sage: A = AlgebrasWithBasis(QQ).example(); A An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field sage: A.one_basis() word: sage: B = tensor((A, A, A)) sage: B.one_basis() (word: , word: , word: ) sage: B.one() B[word: ] # B[word: ] # B[word: ] """ # FIXME: this method should be conditionally defined, # so that B.one_basis returns NotImplemented if not # all modules provide one_basis if all(hasattr(module, "one_basis") for module in self._sets): return tuple(module.one_basis() for module in self._sets) else: raise NotImplementedError def product_on_basis(self, t1, t2): """ The product of the algebra on the basis, as per ``AlgebrasWithBasis.ParentMethods.product_on_basis``. EXAMPLES:: sage: A = AlgebrasWithBasis(QQ).example(); A An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field sage: (a,b,c) = A.algebra_generators() sage: x = tensor( (a, b, c) ); x B[word: a] # B[word: b] # B[word: c] sage: y = tensor( (c, b, a) ); y B[word: c] # B[word: b] # B[word: a] sage: x*y B[word: ac] # B[word: bb] # B[word: ca] sage: x = tensor( ((a+2*b), c) ) ; x B[word: a] # B[word: c] + 2*B[word: b] # B[word: c] sage: y = tensor( (c, a) ) + 1; y B[word: ] # B[word: ] + B[word: c] # B[word: a] sage: x*y B[word: a] # B[word: c] + B[word: ac] # B[word: ca] + 2*B[word: b] # B[word: c] + 2*B[word: bc] # B[word: ca] TODO: optimize this implementation! """ return tensor( (module.monomial(x1) * module.monomial(x2) for (module, x1, x2) in zip(self._sets, t1, t2))) #. class ElementMethods: """ Implements operations on elements of tensor products of algebras with basis """ pass
class LieAlgebras(Category_over_base_ring): """ The category of Lie algebras. EXAMPLES:: sage: C = LieAlgebras(QQ); C Category of Lie algebras over Rational Field sage: sorted(C.super_categories(), key=str) [Category of vector spaces over Rational Field] We construct a typical parent in this category, and do some computations with it:: sage: A = C.example(); A An example of a Lie algebra: the Lie algebra from the associative algebra Symmetric group algebra of order 3 over Rational Field generated by ([2, 1, 3], [2, 3, 1]) sage: A.category() Category of Lie algebras over Rational Field sage: A.base_ring() Rational Field sage: a,b = A.lie_algebra_generators() sage: a.bracket(b) -[1, 3, 2] + [3, 2, 1] sage: b.bracket(2*a + b) 2*[1, 3, 2] - 2*[3, 2, 1] sage: A.bracket(a, b) -[1, 3, 2] + [3, 2, 1] Please see the source code of `A` (with ``A??``) for how to implement other Lie algebras. TESTS:: sage: C = LieAlgebras(QQ) sage: TestSuite(C).run() sage: TestSuite(C.example()).run() .. TODO:: Many of these tests should use Lie algebras that are not the minimal example and need to be added after :trac:`16820` (and :trac:`16823`). """ @cached_method def super_categories(self): """ EXAMPLES:: sage: LieAlgebras(QQ).super_categories() [Category of vector spaces over Rational Field] """ # We do not also derive from (Magmatic) algebras since we don't want * # to be our Lie bracket # Also this doesn't inherit the ability to add axioms like Associative # and Unital, both of which do not make sense for Lie algebras return [Modules(self.base_ring())] # TODO: Find some way to do this without copying most of the logic. def _repr_object_names(self): r""" Return the name of the objects of this category. .. SEEALSO:: :meth:`Category._repr_object_names` EXAMPLES:: sage: LieAlgebras(QQ)._repr_object_names() 'Lie algebras over Rational Field' sage: LieAlgebras(Fields())._repr_object_names() 'Lie algebras over fields' sage: from sage.categories.category import JoinCategory sage: from sage.categories.category_with_axiom import Blahs sage: LieAlgebras(JoinCategory((Blahs().Flying(), Fields()))) Category of Lie algebras over (flying unital blahs and fields) """ base = self.base() if isinstance(base, Category): if isinstance(base, JoinCategory): name = '(' + ' and '.join( C._repr_object_names() for C in base.super_categories()) + ')' else: name = base._repr_object_names() else: name = base return "Lie algebras over {}".format(name) def example(self, gens=None): """ Return an example of a Lie algebra as per :meth:`Category.example <sage.categories.category.Category.example>`. EXAMPLES:: sage: LieAlgebras(QQ).example() An example of a Lie algebra: the Lie algebra from the associative algebra Symmetric group algebra of order 3 over Rational Field generated by ([2, 1, 3], [2, 3, 1]) Another set of generators can be specified as an optional argument:: sage: F.<x,y,z> = FreeAlgebra(QQ) sage: LieAlgebras(QQ).example(F.gens()) An example of a Lie algebra: the Lie algebra from the associative algebra Free Algebra on 3 generators (x, y, z) over Rational Field generated by (x, y, z) """ if gens is None: from sage.combinat.symmetric_group_algebra import SymmetricGroupAlgebra from sage.rings.all import QQ gens = SymmetricGroupAlgebra(QQ, 3).algebra_generators() from sage.categories.examples.lie_algebras import Example return Example(gens) WithBasis = LazyImport('sage.categories.lie_algebras_with_basis', 'LieAlgebrasWithBasis', as_name='WithBasis') class FiniteDimensional(CategoryWithAxiom_over_base_ring): WithBasis = LazyImport( 'sage.categories.finite_dimensional_lie_algebras_with_basis', 'FiniteDimensionalLieAlgebrasWithBasis', as_name='WithBasis') def extra_super_categories(self): """ Implements the fact that a finite dimensional Lie algebra over a finite ring is finite. EXAMPLES:: sage: LieAlgebras(IntegerModRing(4)).FiniteDimensional().extra_super_categories() [Category of finite sets] sage: LieAlgebras(ZZ).FiniteDimensional().extra_super_categories() [] sage: LieAlgebras(GF(5)).FiniteDimensional().is_subcategory(Sets().Finite()) True sage: LieAlgebras(ZZ).FiniteDimensional().is_subcategory(Sets().Finite()) False sage: LieAlgebras(GF(5)).WithBasis().FiniteDimensional().is_subcategory(Sets().Finite()) True """ if self.base_ring() in Sets().Finite(): return [Sets().Finite()] return [] class ParentMethods: #@abstract_method #def lie_algebra_generators(self): # """ # Return the generators of ``self`` as a Lie algebra. # """ # TODO: Move this to LieAlgebraElement, cythonize, and use more standard # coercion framework test (i.e., have_same_parent) def bracket(self, lhs, rhs): """ Return the Lie bracket ``[lhs, rhs]`` after coercing ``lhs`` and ``rhs`` into elements of ``self``. EXAMPLES:: sage: L = LieAlgebras(QQ).example() sage: x,y = L.lie_algebra_generators() sage: L.bracket(x, x + y) -[1, 3, 2] + [3, 2, 1] sage: L.bracket(x, 0) 0 sage: L.bracket(0, x) 0 """ return self(lhs)._bracket_(self(rhs)) # Do not override this. Instead implement :meth:`_construct_UEA`; # then, :meth:`lift` and :meth:`universal_enveloping_algebra` # will automatically setup the coercion. def universal_enveloping_algebra(self): """ Return the universal enveloping algebra of ``self``. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: L.universal_enveloping_algebra() Noncommutative Multivariate Polynomial Ring in b0, b1, b2 over Rational Field, nc-relations: {} :: sage: L = LieAlgebra(QQ, 3, 'x', abelian=True) sage: L.universal_enveloping_algebra() Multivariate Polynomial Ring in x0, x1, x2 over Rational Field .. SEEALSO:: :meth:`lift` """ return self.lift.codomain() @abstract_method(optional=True) def _construct_UEA(self): """ Return the universal enveloping algebra of ``self``. Unlike :meth:`universal_enveloping_algebra`, this method does not (usually) construct the canonical lift morphism from ``self`` to the universal enveloping algebra (let alone register it as a coercion). One should implement this method and the ``lift`` method for the element class to construct the morphism the universal enveloping algebra. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: L._construct_UEA() Noncommutative Multivariate Polynomial Ring in b0, b1, b2 over Rational Field, nc-relations: {} :: sage: L = LieAlgebra(QQ, 3, 'x', abelian=True) sage: L.universal_enveloping_algebra() # indirect doctest Multivariate Polynomial Ring in x0, x1, x2 over Rational Field """ @abstract_method(optional=True) def module(self): r""" Return an `R`-module which is isomorphic to the underlying `R`-module of ``self``. The rationale behind this method is to enable linear algebraic functionality on ``self`` (such as computing the span of a list of vectors in ``self``) via an isomorphism from ``self`` to an `R`-module (typically, although not always, an `R`-module of the form `R^n` for an `n \in \NN`) on which such functionality already exists. For this method to be of any use, it should return an `R`-module which has linear algebraic functionality that ``self`` does not have. For instance, if ``self`` has ordered basis `(e, f, h)`, then ``self.module()`` will be the `R`-module `R^3`, and the elements `e`, `f` and `h` of ``self`` will correspond to the basis vectors `(1, 0, 0)`, `(0, 1, 0)` and `(0, 0, 1)` of ``self.module()``. This method :meth:`module` needs to be set whenever a finite-dimensional Lie algebra with basis is intended to support linear algebra (which is, e.g., used in the computation of centralizers and lower central series). One then needs to also implement the `R`-module isomorphism from ``self`` to ``self.module()`` in both directions; that is, implement: * a ``to_vector`` ElementMethod which sends every element of ``self`` to the corresponding element of ``self.module()``; * a ``from_vector`` ParentMethod which sends every element of ``self.module()`` to an element of ``self``. The ``from_vector`` method will automatically serve as an element constructor of ``self`` (that is, ``self(v)`` for any ``v`` in ``self.module()`` will return ``self.from_vector(v)``). .. TODO:: Ensure that this is actually so. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: L.module() Vector space of dimension 3 over Rational Field """ @abstract_method(optional=True) def from_vector(self, v): """ Return the element of ``self`` corresponding to the vector ``v`` in ``self.module()``. Implement this if you implement :meth:`module`; see the documentation of the latter for how this is to be done. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: u = L.from_vector(vector(QQ, (1, 0, 0))); u (1, 0, 0) sage: parent(u) is L True """ @lazy_attribute def lift(self): r""" Construct the lift morphism from ``self`` to the universal enveloping algebra of ``self`` (the latter is implemented as :meth:`universal_enveloping_algebra`). This is a Lie algebra homomorphism. It is injective if ``self`` is a free module over its base ring, or if the base ring is a `\QQ`-algebra. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: a, b, c = L.lie_algebra_generators() sage: lifted = L.lift(2*a + b - c); lifted 2*b0 + b1 - b2 sage: lifted.parent() is L.universal_enveloping_algebra() True """ M = LiftMorphism(self, self._construct_UEA()) M.register_as_coercion() return M def subalgebra(self, gens, names=None, index_set=None, category=None): r""" Return the subalgebra of ``self`` generated by ``gens``. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: a, b, c = L.lie_algebra_generators() sage: L.subalgebra([2*a - c, b + c]) An example of a finite dimensional Lie algebra with basis: the 2-dimensional abelian Lie algebra over Rational Field with basis matrix: [ 1 0 -1/2] [ 0 1 1] :: sage: L = LieAlgebras(QQ).example() sage: x,y = L.lie_algebra_generators() sage: L.subalgebra([x + y]) Traceback (most recent call last): ... NotImplementedError: subalgebras not yet implemented: see #17416 """ raise NotImplementedError( "subalgebras not yet implemented: see #17416") #from sage.algebras.lie_algebras.subalgebra import LieSubalgebra #return LieSubalgebra(gens, names, index_set, category) def ideal(self, gens, names=None, index_set=None, category=None): r""" Return the ideal of ``self`` generated by ``gens``. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: a, b, c = L.lie_algebra_generators() sage: L.ideal([2*a - c, b + c]) An example of a finite dimensional Lie algebra with basis: the 2-dimensional abelian Lie algebra over Rational Field with basis matrix: [ 1 0 -1/2] [ 0 1 1] :: sage: L = LieAlgebras(QQ).example() sage: x,y = L.lie_algebra_generators() sage: L.ideal([x + y]) Traceback (most recent call last): ... NotImplementedError: ideals not yet implemented: see #16824 """ raise NotImplementedError("ideals not yet implemented: see #16824") #from sage.algebras.lie_algebras.ideal import LieIdeal #return LieIdeal(gens, names, index_set, category) def is_ideal(self, A): """ Return if ``self`` is an ideal of ``A``. EXAMPLES:: sage: L = LieAlgebras(QQ).example() sage: L.is_ideal(L) True """ if A == self: return True raise NotImplementedError("ideals not yet implemented: see #16824") #from sage.algebras.lie_algebras.ideal import LieIdeal #return isinstance(self, LieIdeal) and self._ambient is A @abstract_method(optional=True) def killing_form(self, x, y): """ Return the Killing form of ``x`` and ``y``. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: a, b, c = L.lie_algebra_generators() sage: L.killing_form(a, b+c) 0 """ def is_abelian(self): r""" Return ``True`` if this Lie algebra is abelian. A Lie algebra `\mathfrak{g}` is abelian if `[x, y] = 0` for all `x, y \in \mathfrak{g}`. EXAMPLES:: sage: L = LieAlgebras(QQ).example() sage: L.is_abelian() False sage: R = QQ['x,y'] sage: L = LieAlgebras(QQ).example(R.gens()) sage: L.is_abelian() True :: sage: L.<x> = LieAlgebra(QQ,1) # todo: not implemented - #16823 sage: L.is_abelian() # todo: not implemented - #16823 True sage: L.<x,y> = LieAlgebra(QQ,2) # todo: not implemented - #16823 sage: L.is_abelian() # todo: not implemented - #16823 False """ G = self.lie_algebra_generators() if G not in FiniteEnumeratedSets(): raise NotImplementedError("infinite number of generators") zero = self.zero() return all(x._bracket_(y) == zero for x in G for y in G) def is_commutative(self): """ Return if ``self`` is commutative. This is equivalent to ``self`` being abelian. EXAMPLES:: sage: L = LieAlgebras(QQ).example() sage: L.is_commutative() False :: sage: L.<x> = LieAlgebra(QQ, 1) # todo: not implemented - #16823 sage: L.is_commutative() # todo: not implemented - #16823 True """ return self.is_abelian() @abstract_method(optional=True) def is_solvable(self): """ Return if ``self`` is a solvable Lie algebra. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: L.is_solvable() True """ @abstract_method(optional=True) def is_nilpotent(self): """ Return if ``self`` is a nilpotent Lie algebra. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: L.is_nilpotent() True """ def _test_jacobi_identity(self, **options): """ Test that the Jacobi identity is satisfied on (not necessarily all) elements of this set. INPUT: - ``options`` -- any keyword arguments accepted by :meth:`_tester`. EXAMPLES: By default, this method runs the tests only on the elements returned by ``self.some_elements()``:: sage: L = LieAlgebras(QQ).example() sage: L._test_jacobi_identity() However, the elements tested can be customized with the ``elements`` keyword argument:: sage: L = LieAlgebras(QQ).example() sage: x,y = L.lie_algebra_generators() sage: L._test_jacobi_identity(elements=[x+y, x, 2*y, x.bracket(y)]) See the documentation for :class:`TestSuite` for more information. """ tester = self._tester(**options) elts = tester.some_elements() jacobi = lambda x, y, z: self.bracket(x, self.bracket(y, z)) + \ self.bracket(y, self.bracket(z, x)) + \ self.bracket(z, self.bracket(x, y)) zero = self.zero() for x in elts: for y in elts: if x == y: continue for z in elts: tester.assertTrue(jacobi(x, y, z) == zero) def _test_antisymmetry(self, **options): """ Test that the antisymmetry axiom is satisfied on (not necessarily all) elements of this set. INPUT: - ``options`` -- any keyword arguments accepted by :meth:`_tester`. EXAMPLES: By default, this method runs the tests only on the elements returned by ``self.some_elements()``:: sage: L = LieAlgebras(QQ).example() sage: L._test_antisymmetry() However, the elements tested can be customized with the ``elements`` keyword argument:: sage: L = LieAlgebras(QQ).example() sage: x,y = L.lie_algebra_generators() sage: L._test_antisymmetry(elements=[x+y, x, 2*y, x.bracket(y)]) See the documentation for :class:`TestSuite` for more information. """ tester = self._tester(**options) elts = tester.some_elements() zero = self.zero() for x in elts: tester.assertTrue(self.bracket(x, x) == zero) def _test_distributivity(self, **options): r""" Test the distributivity of the Lie bracket `[,]` on `+` on (not necessarily all) elements of this set. INPUT: - ``options`` -- any keyword arguments accepted by :meth:`_tester`. TESTS:: sage: L = LieAlgebras(QQ).example() sage: L._test_distributivity() EXAMPLES: By default, this method runs the tests only on the elements returned by ``self.some_elements()``:: sage: L = LieAlgebra(QQ, 3, 'x,y,z', representation="polynomial") sage: L.some_elements() [x + y + z] sage: L._test_distributivity() However, the elements tested can be customized with the ``elements`` keyword argument:: sage: L = LieAlgebra(QQ, cartan_type=['A', 2]) # todo: not implemented - #16821 sage: h1 = L.gen(0) # todo: not implemented - #16821 sage: h2 = L.gen(1) # todo: not implemented - #16821 sage: e2 = L.gen(3) # todo: not implemented - #16821 sage: L._test_distributivity(elements=[h1, h2, e2]) # todo: not implemented - #16821 See the documentation for :class:`TestSuite` for more information. """ tester = self._tester(**options) S = tester.some_elements() from sage.misc.misc import some_tuples for x, y, z in some_tuples(S, 3, tester._max_runs): # left distributivity tester.assertTrue( self.bracket(x, (y + z)) == self.bracket(x, y) + self.bracket(x, z)) # right distributivity tester.assertTrue( self.bracket((x + y), z) == self.bracket(x, z) + self.bracket(y, z)) class ElementMethods: @coerce_binop def bracket(self, rhs): """ Return the Lie bracket ``[self, rhs]``. EXAMPLES:: sage: L = LieAlgebras(QQ).example() sage: x,y = L.lie_algebra_generators() sage: x.bracket(y) -[1, 3, 2] + [3, 2, 1] sage: x.bracket(0) 0 """ return self._bracket_(rhs) # Implement this method to define the Lie bracket. You do not # need to deal with the coercions here. @abstract_method def _bracket_(self, y): """ Return the Lie bracket ``[self, y]``, where ``y`` is an element of the same Lie algebra as ``self``. EXAMPLES:: sage: L = LieAlgebras(QQ).example() sage: x,y = L.lie_algebra_generators() sage: x._bracket_(y) -[1, 3, 2] + [3, 2, 1] sage: y._bracket_(x) [1, 3, 2] - [3, 2, 1] sage: x._bracket_(x) 0 """ @abstract_method(optional=True) def to_vector(self): """ Return the vector in ``g.module()`` corresponding to the element ``self`` of ``g`` (where ``g`` is the parent of ``self``). Implement this if you implement ``g.module()``. See :meth:`LieAlgebras.module` for how this is to be done. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: u = L((1, 0, 0)).to_vector(); u (1, 0, 0) sage: parent(u) Vector space of dimension 3 over Rational Field """ @abstract_method(optional=True) def lift(self): """ Return the image of ``self`` under the canonical lift from the Lie algebra to its universal enveloping algebra. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: a, b, c = L.lie_algebra_generators() sage: elt = 3*a + b - c sage: elt.lift() 3*b0 + b1 - b2 :: sage: L.<x,y> = LieAlgebra(QQ, abelian=True) sage: x.lift() x """ def killing_form(self, x): """ Return the Killing form of ``self`` and ``x``. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: a, b, c = L.lie_algebra_generators() sage: a.killing_form(b) 0 """ return self.parent().killing_form(self, x)
class AdditiveMagmas(Category_singleton): """ The category of additive magmas. An additive magma is a set endowed with a binary operation `+`. EXAMPLES:: sage: AdditiveMagmas() Category of additive magmas sage: AdditiveMagmas().super_categories() [Category of sets] sage: AdditiveMagmas().all_super_categories() [Category of additive magmas, Category of sets, Category of sets with partial maps, Category of objects] The following axioms are defined by this category:: sage: AdditiveMagmas().AdditiveAssociative() Category of additive semigroups sage: AdditiveMagmas().AdditiveUnital() Category of additive unital additive magmas sage: AdditiveMagmas().AdditiveCommutative() Category of additive commutative additive magmas sage: AdditiveMagmas().AdditiveUnital().AdditiveInverse() Category of additive inverse additive unital additive magmas sage: AdditiveMagmas().AdditiveAssociative().AdditiveCommutative() Category of commutative additive semigroups sage: AdditiveMagmas().AdditiveAssociative().AdditiveCommutative().AdditiveUnital() Category of commutative additive monoids sage: AdditiveMagmas().AdditiveAssociative().AdditiveCommutative().AdditiveUnital().AdditiveInverse() Category of commutative additive groups TESTS:: sage: C = AdditiveMagmas() sage: TestSuite(C).run() """ def super_categories(self): """ EXAMPLES:: sage: AdditiveMagmas().super_categories() [Category of sets] """ return [Sets()] class SubcategoryMethods: @cached_method def AdditiveAssociative(self): r""" Return the full subcategory of the additive associative objects of ``self``. An :class:`additive magma <AdditiveMagmas>` `M` is *associative* if, for all `x,y,z \in M`, .. MATH:: x + (y + z) = (x + y) + z .. SEEALSO:: :wikipedia:`Associative_property` EXAMPLES:: sage: AdditiveMagmas().AdditiveAssociative() Category of additive semigroups TESTS:: sage: TestSuite(AdditiveMagmas().AdditiveAssociative()).run() sage: Rings().AdditiveAssociative.__module__ 'sage.categories.additive_magmas' """ return self._with_axiom('AdditiveAssociative') @cached_method def AdditiveCommutative(self): r""" Return the full subcategory of the commutative objects of ``self``. An :class:`additive magma <AdditiveMagmas>` `M` is *commutative* if, for all `x,y \in M`, .. MATH:: x + y = y + x .. SEEALSO:: :wikipedia:`Commutative_property` EXAMPLES:: sage: AdditiveMagmas().AdditiveCommutative() Category of additive commutative additive magmas sage: AdditiveMagmas().AdditiveAssociative().AdditiveUnital().AdditiveCommutative() Category of commutative additive monoids sage: _ is CommutativeAdditiveMonoids() True TESTS:: sage: TestSuite(AdditiveMagmas().AdditiveCommutative()).run() sage: Rings().AdditiveCommutative.__module__ 'sage.categories.additive_magmas' """ return self._with_axiom('AdditiveCommutative') @cached_method def AdditiveUnital(self): r""" Return the subcategory of the unital objects of ``self``. An :class:`additive magma <AdditiveMagmas>` `M` is *unital* if it admits an element `0`, called *neutral element*, such that for all `x \in M`, .. MATH:: 0 + x = x + 0 = x This element is necessarily unique, and should be provided as ``M.zero()``. .. SEEALSO:: :wikipedia:`Unital_magma#unital` EXAMPLES:: sage: AdditiveMagmas().AdditiveUnital() Category of additive unital additive magmas sage: from sage.categories.additive_semigroups import AdditiveSemigroups sage: AdditiveSemigroups().AdditiveUnital() Category of additive monoids sage: CommutativeAdditiveMonoids().AdditiveUnital() Category of commutative additive monoids TESTS:: sage: TestSuite(AdditiveMagmas().AdditiveUnital()).run() sage: CommutativeAdditiveSemigroups().AdditiveUnital.__module__ 'sage.categories.additive_magmas' """ return self._with_axiom("AdditiveUnital") AdditiveAssociative = LazyImport('sage.categories.additive_semigroups', 'AdditiveSemigroups', at_startup=True) class ParentMethods: def summation(self, x, y): r""" Return the sum of ``x`` and ``y``. The binary addition operator of this additive magma. INPUT: - ``x``, ``y`` -- elements of this additive magma EXAMPLES:: sage: S = CommutativeAdditiveSemigroups().example() sage: (a,b,c,d) = S.additive_semigroup_generators() sage: S.summation(a, b) a + b A parent in ``AdditiveMagmas()`` must either implement :meth:`.summation` in the parent class or ``_add_`` in the element class. By default, the addition method on elements ``x._add_(y)`` calls ``S.summation(x,y)``, and reciprocally. As a bonus effect, ``S.summation`` by itself models the binary function from ``S`` to ``S``:: sage: bin = S.summation sage: bin(a,b) a + b Here, ``S.summation`` is just a bound method. Whenever possible, it is recommended to enrich ``S.summation`` with extra mathematical structure. Lazy attributes can come handy for this. .. TODO:: Add an example. """ return x + y summation_from_element_class_add = summation def __init_extra__(self): """ TESTS:: sage: S = CommutativeAdditiveSemigroups().example() sage: (a,b,c,d) = S.additive_semigroup_generators() sage: a + b # indirect doctest a + b sage: a.__class__._add_ == a.__class__._add_parent True """ # This should instead register the summation to the coercion model # But this is not yet implemented in the coercion model if (self.summation != self.summation_from_element_class_add ) and hasattr(self, "element_class") and hasattr( self.element_class, "_add_parent"): self.element_class._add_ = self.element_class._add_parent def addition_table(self, names='letters', elements=None): r""" Return a table describing the addition operation. .. NOTE:: The order of the elements in the row and column headings is equal to the order given by the table's :meth:`~sage.matrix.operation_table.OperationTable.column_keys` method. The association can also be retrieved with the :meth:`~sage.matrix.operation_table.OperationTable.translation` method. INPUT: - ``names`` -- the type of names used: * ``'letters'`` - lowercase ASCII letters are used for a base 26 representation of the elements' positions in the list given by :meth:`~sage.matrix.operation_table.OperationTable.column_keys`, padded to a common width with leading 'a's. * ``'digits'`` - base 10 representation of the elements' positions in the list given by :meth:`~sage.matrix.operation_table.OperationTable.column_keys`, padded to a common width with leading zeros. * ``'elements'`` - the string representations of the elements themselves. * a list - a list of strings, where the length of the list equals the number of elements. - ``elements`` -- (default: ``None``) A list of elements of the additive magma, in forms that can be coerced into the structure, eg. their string representations. This may be used to impose an alternate ordering on the elements, perhaps when this is used in the context of a particular structure. The default is to use whatever ordering the ``S.list`` method returns. Or the ``elements`` can be a subset which is closed under the operation. In particular, this can be used when the base set is infinite. OUTPUT: The addition table as an object of the class :class:`~sage.matrix.operation_table.OperationTable` which defines several methods for manipulating and displaying the table. See the documentation there for full details to supplement the documentation here. EXAMPLES: All that is required is that an algebraic structure has an addition defined.The default is to represent elements as lowercase ASCII letters. :: sage: R=IntegerModRing(5) sage: R.addition_table() + a b c d e +---------- a| a b c d e b| b c d e a c| c d e a b d| d e a b c e| e a b c d The ``names`` argument allows displaying the elements in different ways. Requesting ``elements`` will use the representation of the elements of the set. Requesting ``digits`` will include leading zeros as padding. :: sage: R=IntegerModRing(11) sage: P=R.addition_table(names='elements') sage: P + 0 1 2 3 4 5 6 7 8 9 10 +--------------------------------- 0| 0 1 2 3 4 5 6 7 8 9 10 1| 1 2 3 4 5 6 7 8 9 10 0 2| 2 3 4 5 6 7 8 9 10 0 1 3| 3 4 5 6 7 8 9 10 0 1 2 4| 4 5 6 7 8 9 10 0 1 2 3 5| 5 6 7 8 9 10 0 1 2 3 4 6| 6 7 8 9 10 0 1 2 3 4 5 7| 7 8 9 10 0 1 2 3 4 5 6 8| 8 9 10 0 1 2 3 4 5 6 7 9| 9 10 0 1 2 3 4 5 6 7 8 10| 10 0 1 2 3 4 5 6 7 8 9 sage: T=R.addition_table(names='digits') sage: T + 00 01 02 03 04 05 06 07 08 09 10 +--------------------------------- 00| 00 01 02 03 04 05 06 07 08 09 10 01| 01 02 03 04 05 06 07 08 09 10 00 02| 02 03 04 05 06 07 08 09 10 00 01 03| 03 04 05 06 07 08 09 10 00 01 02 04| 04 05 06 07 08 09 10 00 01 02 03 05| 05 06 07 08 09 10 00 01 02 03 04 06| 06 07 08 09 10 00 01 02 03 04 05 07| 07 08 09 10 00 01 02 03 04 05 06 08| 08 09 10 00 01 02 03 04 05 06 07 09| 09 10 00 01 02 03 04 05 06 07 08 10| 10 00 01 02 03 04 05 06 07 08 09 Specifying the elements in an alternative order can provide more insight into how the operation behaves. :: sage: S=IntegerModRing(7) sage: elts = [0, 3, 6, 2, 5, 1, 4] sage: S.addition_table(elements=elts) + a b c d e f g +-------------- a| a b c d e f g b| b c d e f g a c| c d e f g a b d| d e f g a b c e| e f g a b c d f| f g a b c d e g| g a b c d e f The ``elements`` argument can be used to provide a subset of the elements of the structure. The subset must be closed under the operation. Elements need only be in a form that can be coerced into the set. The ``names`` argument can also be used to request that the elements be represented with their usual string representation. :: sage: T=IntegerModRing(12) sage: elts=[0, 3, 6, 9] sage: T.addition_table(names='elements', elements=elts) + 0 3 6 9 +-------- 0| 0 3 6 9 3| 3 6 9 0 6| 6 9 0 3 9| 9 0 3 6 The table returned can be manipulated in various ways. See the documentation for :class:`~sage.matrix.operation_table.OperationTable` for more comprehensive documentation. :: sage: R=IntegerModRing(3) sage: T=R.addition_table() sage: T.column_keys() (0, 1, 2) sage: sorted(T.translation().items()) [('a', 0), ('b', 1), ('c', 2)] sage: T.change_names(['x', 'y', 'z']) sage: sorted(T.translation().items()) [('x', 0), ('y', 1), ('z', 2)] sage: T + x y z +------ x| x y z y| y z x z| z x y """ from sage.matrix.operation_table import OperationTable import operator return OperationTable(self, operation=operator.add, names=names, elements=elements) class ElementMethods: @abstract_method(optional=True) def _add_(self, right): """ Return the sum of ``self`` and ``right``. INPUT: - ``self``, ``right`` -- two elements with the same parent OUTPUT: - an element of the same parent EXAMPLES:: sage: F = CommutativeAdditiveSemigroups().example() sage: (a,b,c,d) = F.additive_semigroup_generators() sage: a._add_(b) a + b """ def _add_parent(self, other): r""" Return the sum of the two elements, calculated using the ``summation`` method of the parent. This is the default implementation of _add_ if ``summation`` is implemented in the parent. INPUT: - ``other`` -- an element of the parent of ``self`` OUTPUT: - an element of the parent of ``self`` EXAMPLES:: sage: S = CommutativeAdditiveSemigroups().example() sage: (a,b,c,d) = S.additive_semigroup_generators() sage: a._add_parent(b) a + b """ return self.parent().summation(self, other) class Homsets(HomsetsCategory): def extra_super_categories(self): """ Implement the fact that a homset between two magmas is a magma. EXAMPLES:: sage: AdditiveMagmas().Homsets().extra_super_categories() [Category of additive magmas] sage: AdditiveMagmas().Homsets().super_categories() [Category of additive magmas, Category of homsets] """ return [AdditiveMagmas()] class CartesianProducts(CartesianProductsCategory): def extra_super_categories(self): """ Implement the fact that a Cartesian product of additive magmas is an additive magma. EXAMPLES:: sage: C = AdditiveMagmas().CartesianProducts() sage: C.extra_super_categories() [Category of additive magmas] sage: C.super_categories() [Category of additive magmas, Category of Cartesian products of sets] sage: C.axioms() frozenset() """ return [AdditiveMagmas()] class ElementMethods: def _add_(self, right): r""" EXAMPLES:: sage: G5=GF(5); G8=GF(4,'x'); GG = G5.cartesian_product(G8) sage: e = GG((G5(1),G8.primitive_element())); e (1, x) sage: e+e (2, 0) sage: e=groups.misc.AdditiveCyclic(8) sage: x=e.cartesian_product(e)((e(1),e(2))) sage: x (1, 2) sage: 4*x (4, 0) """ return self.parent()._cartesian_product_of_elements( x + y for x, y in zip(self.cartesian_factors(), right.cartesian_factors())) class Algebras(AlgebrasCategory): def extra_super_categories(self): """ EXAMPLES:: sage: AdditiveMagmas().Algebras(QQ).extra_super_categories() [Category of magmatic algebras with basis over Rational Field] sage: AdditiveMagmas().Algebras(QQ).super_categories() [Category of magmatic algebras with basis over Rational Field, Category of set algebras over Rational Field] """ from sage.categories.magmatic_algebras import MagmaticAlgebras return [MagmaticAlgebras(self.base_ring()).WithBasis()] class ParentMethods: @cached_method def algebra_generators(self): r""" The generators of this algebra, as per :meth:`MagmaticAlgebras.ParentMethods.algebra_generators() <.magmatic_algebras.MagmaticAlgebras.ParentMethods.algebra_generators>`. They correspond to the generators of the additive semigroup. EXAMPLES:: sage: S = CommutativeAdditiveSemigroups().example(); S An example of a commutative monoid: the free commutative monoid generated by ('a', 'b', 'c', 'd') sage: A = S.algebra(QQ) sage: A.algebra_generators() Finite family {0: B[a], 1: B[b], 2: B[c], 3: B[d]} """ return self.basis().keys().additive_semigroup_generators().map( self.monomial) def product_on_basis(self, g1, g2): r""" Product, on basis elements, as per :meth:`MagmaticAlgebras.WithBasis.ParentMethods.product_on_basis() <.magmatic_algebras.MagmaticAlgebras.WithBasis.ParentMethods.product_on_basis>`. The product of two basis elements is induced by the addition of the corresponding elements of the group. EXAMPLES:: sage: S = CommutativeAdditiveSemigroups().example(); S An example of a commutative monoid: the free commutative monoid generated by ('a', 'b', 'c', 'd') sage: A = S.algebra(QQ) sage: a,b,c,d = A.algebra_generators() sage: a * b + b * d * c B[b + c + d] + B[a + b] """ return self.monomial(g1 + g2) class AdditiveCommutative(CategoryWithAxiom): class CartesianProducts(CartesianProductsCategory): def extra_super_categories(self): """ Implement the fact that a Cartesian product of commutative additive magmas is a commutative additive magma. EXAMPLES:: sage: C = AdditiveMagmas().AdditiveCommutative().CartesianProducts() sage: C.extra_super_categories() [Category of additive commutative additive magmas] sage: C.axioms() frozenset({'AdditiveCommutative'}) """ return [AdditiveMagmas().AdditiveCommutative()] class Algebras(AlgebrasCategory): def extra_super_categories(self): """ Implement the fact that the algebra of a commutative additive magmas is commutative. EXAMPLES:: sage: AdditiveMagmas().AdditiveCommutative().Algebras(QQ).extra_super_categories() [Category of commutative magmas] sage: AdditiveMagmas().AdditiveCommutative().Algebras(QQ).super_categories() [Category of additive magma algebras over Rational Field, Category of commutative magmas] """ from sage.categories.magmas import Magmas return [Magmas().Commutative()] class AdditiveUnital(CategoryWithAxiom): def additional_structure(self): r""" Return whether ``self`` is a structure category. .. SEEALSO:: :meth:`Category.additional_structure` The category of unital additive magmas defines the zero as additional structure, and this zero shall be preserved by morphisms. EXAMPLES:: sage: AdditiveMagmas().AdditiveUnital().additional_structure() Category of additive unital additive magmas """ return self class SubcategoryMethods: @cached_method def AdditiveInverse(self): r""" Return the full subcategory of the additive inverse objects of ``self``. An inverse :class:`additive magma <AdditiveMagmas>` is a :class:`unital additive magma <AdditiveMagmas.Unital>` such that every element admits both an additive inverse on the left and on the right. Such an additive magma is also called an *additive loop*. .. SEEALSO:: :wikipedia:`Inverse_element`, :wikipedia:`Quasigroup` EXAMPLES:: sage: AdditiveMagmas().AdditiveUnital().AdditiveInverse() Category of additive inverse additive unital additive magmas sage: from sage.categories.additive_monoids import AdditiveMonoids sage: AdditiveMonoids().AdditiveInverse() Category of additive groups TESTS:: sage: TestSuite(AdditiveMagmas().AdditiveUnital().AdditiveInverse()).run() sage: CommutativeAdditiveMonoids().AdditiveInverse.__module__ 'sage.categories.additive_magmas' """ return self._with_axiom("AdditiveInverse") class ParentMethods: def _test_zero(self, **options): r""" Test that ``self.zero()`` is an element of self and is neutral for the addition. INPUT: - ``options`` -- any keyword arguments accepted by :meth:`_tester` EXAMPLES: By default, this method tests only the elements returned by ``self.some_elements()``:: sage: S = CommutativeAdditiveMonoids().example() sage: S._test_zero() However, the elements tested can be customized with the ``elements`` keyword argument:: sage: (a,b,c,d) = S.additive_semigroup_generators() sage: S._test_zero(elements = (a, a+c)) See the documentation for :class:`TestSuite` for more information. """ tester = self._tester(**options) zero = self.zero() # TODO: also call is_zero once it will work tester.assertTrue(self.is_parent_of(zero)) for x in tester.some_elements(): tester.assertTrue(x + zero == x) # Check that zero is immutable if it looks like we can: if hasattr(zero, "is_immutable"): tester.assertEqual(zero.is_immutable(), True) if hasattr(zero, "is_mutable"): tester.assertEqual(zero.is_mutable(), False) # Check that bool behave consistently on zero tester.assertFalse(bool(self.zero())) @cached_method def zero(self): """ Return the zero of this additive magma, that is the unique neutral element for `+`. The default implementation is to coerce ``0`` into ``self``. It is recommended to override this method because the coercion from the integers: - is not always meaningful (except for `0`), and - often uses ``self.zero()`` otherwise. EXAMPLES:: sage: S = CommutativeAdditiveMonoids().example() sage: S.zero() 0 """ # TODO: add a test that actually exercise this default implementation return self(0) def is_empty(self): r""" Return whether this set is empty. Since this set is an additive magma it has a zero element and hence is not empty. This method thus always returns ``False``. EXAMPLES:: sage: A = AdditiveAbelianGroup([3,3]) sage: A in AdditiveMagmas() True sage: A.is_empty() False sage: B = CommutativeAdditiveMonoids().example() sage: B.is_empty() False TESTS: We check that the method `is_empty` is inherited from this category in both examples above:: sage: A.is_empty.__module__ 'sage.categories.additive_magmas' sage: B.is_empty.__module__ 'sage.categories.additive_magmas' """ return False class ElementMethods: # TODO: merge with the implementation in Element which currently # overrides this one, and further requires self.parent()(0) to work. # # def is_zero(self): # """ # Returns whether self is the zero of the magma # # The default implementation, is to compare with ``self.zero()``. # # TESTS:: # # sage: S = CommutativeAdditiveMonoids().example() # sage: S.zero().is_zero() # True # sage: S("aa").is_zero() # False # """ # return self == self.parent().zero() @abstract_method def __bool__(self): """ Return whether ``self`` is not zero. All parents in the category ``CommutativeAdditiveMonoids()`` should implement this method. .. note:: This is currently not useful because this method is overridden by ``Element``. TESTS:: sage: S = CommutativeAdditiveMonoids().example() sage: bool(S.zero()) False sage: bool(S.an_element()) True """ if six.PY2: __nonzero__ = __bool__ del __bool__ def _test_nonzero_equal(self, **options): r""" Test that ``.__bool__()`` behave consistently with `` == 0``. TESTS:: sage: S = CommutativeAdditiveMonoids().example() sage: S.zero()._test_nonzero_equal() sage: S.an_element()._test_nonzero_equal() """ tester = self._tester(**options) tester.assertEqual(bool(self), self != self.parent().zero()) tester.assertEqual(not self, self == self.parent().zero()) def _sub_(left, right): r""" Default implementation of difference. EXAMPLES:: sage: F = CombinatorialFreeModule(QQ, ['a','b']) sage: a,b = F.basis() sage: a - b B['a'] - B['b'] TESTS: Check that :trac:`18275` is fixed:: sage: C = GF(5).cartesian_product(GF(5)) sage: C.one() - C.one() (0, 0) """ return left + (-right) def __neg__(self): """ Return the negation of ``self``, if it exists. This top-level implementation delegates the job to ``_neg_``, for those additive unital magmas which may choose to implement it instead of ``__neg__`` for consistency. EXAMPLES:: sage: F = CombinatorialFreeModule(QQ, ['a','b']) sage: a,b = F.basis() sage: - b -B['b'] TESTS:: sage: F = CombinatorialFreeModule(ZZ, ['a','b']) sage: a,b = F.gens() sage: FF = cartesian_product((F,F)) sage: x = cartesian_product([a,2*a-3*b]) ; x B[(0, 'a')] + 2*B[(1, 'a')] - 3*B[(1, 'b')] sage: x.parent() is FF True sage: -x -B[(0, 'a')] - 2*B[(1, 'a')] + 3*B[(1, 'b')] """ return self._neg_() class Homsets(HomsetsCategory): def extra_super_categories(self): """ Implement the fact that a homset between two unital additive magmas is a unital additive magma. EXAMPLES:: sage: AdditiveMagmas().AdditiveUnital().Homsets().extra_super_categories() [Category of additive unital additive magmas] sage: AdditiveMagmas().AdditiveUnital().Homsets().super_categories() [Category of additive unital additive magmas, Category of homsets] """ return [AdditiveMagmas().AdditiveUnital()] class ParentMethods: @cached_method def zero(self): """ EXAMPLES:: sage: R = QQ['x'] sage: H = Hom(ZZ, R, AdditiveMagmas().AdditiveUnital()) sage: f = H.zero() sage: f Generic morphism: From: Integer Ring To: Univariate Polynomial Ring in x over Rational Field sage: f(3) 0 sage: f(3) is R.zero() True TESTS: sage: TestSuite(f).run() """ from sage.misc.constant_function import ConstantFunction return self(ConstantFunction(self.codomain().zero())) class AdditiveInverse(CategoryWithAxiom): class CartesianProducts(CartesianProductsCategory): def extra_super_categories(self): """ Implement the fact that a Cartesian product of additive magmas with inverses is an additive magma with inverse. EXAMPLES:: sage: C = AdditiveMagmas().AdditiveUnital().AdditiveInverse().CartesianProducts() sage: C.extra_super_categories() [Category of additive inverse additive unital additive magmas] sage: sorted(C.axioms()) ['AdditiveInverse', 'AdditiveUnital'] """ return [ AdditiveMagmas().AdditiveUnital().AdditiveInverse() ] class ElementMethods: def _neg_(self): """ Return the negation of ``self``. EXAMPLES:: sage: x = cartesian_product((GF(7)(2),17)) ; x (2, 17) sage: -x (5, -17) TESTS:: sage: x.parent() in AdditiveMagmas().AdditiveUnital().AdditiveInverse().CartesianProducts() True """ return self.parent()._cartesian_product_of_elements( [-x for x in self.cartesian_factors()]) class CartesianProducts(CartesianProductsCategory): def extra_super_categories(self): """ Implement the fact that a Cartesian product of unital additive magmas is a unital additive magma. EXAMPLES:: sage: C = AdditiveMagmas().AdditiveUnital().CartesianProducts() sage: C.extra_super_categories() [Category of additive unital additive magmas] sage: C.axioms() frozenset({'AdditiveUnital'}) """ return [AdditiveMagmas().AdditiveUnital()] class ParentMethods: def zero(self): r""" Returns the zero of this group EXAMPLES:: sage: GF(8,'x').cartesian_product(GF(5)).zero() (0, 0) """ return self._cartesian_product_of_elements( _.zero() for _ in self.cartesian_factors()) class Algebras(AlgebrasCategory): def extra_super_categories(self): """ EXAMPLES:: sage: C = AdditiveMagmas().AdditiveUnital().Algebras(QQ) sage: C.extra_super_categories() [Category of unital magmas] sage: C.super_categories() [Category of unital algebras with basis over Rational Field, Category of additive magma algebras over Rational Field] """ from sage.categories.magmas import Magmas return [Magmas().Unital()] class ParentMethods: @cached_method def one_basis(self): """ Return the zero of this additive magma, which index the one of this algebra, as per :meth:`AlgebrasWithBasis.ParentMethods.one_basis() <sage.categories.algebras_with_basis.AlgebrasWithBasis.ParentMethods.one_basis>`. EXAMPLES:: sage: S = CommutativeAdditiveMonoids().example(); S An example of a commutative monoid: the free commutative monoid generated by ('a', 'b', 'c', 'd') sage: A = S.algebra(ZZ) sage: A.one_basis() 0 sage: A.one() B[0] sage: A(3) 3*B[0] """ return self.basis().keys().zero() class WithRealizations(WithRealizationsCategory): class ParentMethods: def zero(self): r""" Return the zero of this unital additive magma. This default implementation returns the zero of the realization of ``self`` given by :meth:`~Sets.WithRealizations.ParentMethods.a_realization`. EXAMPLES:: sage: A = Sets().WithRealizations().example(); A The subset algebra of {1, 2, 3} over Rational Field sage: A.zero.__module__ 'sage.categories.additive_magmas' sage: A.zero() 0 TESTS:: sage: A.zero() is A.a_realization().zero() True sage: A._test_zero() """ return self.a_realization().zero()
class Magmas(Category_singleton): """ The category of (multiplicative) magmas. A magma is a set with a binary operation `*`. EXAMPLES:: sage: Magmas() Category of magmas sage: Magmas().super_categories() [Category of sets] sage: Magmas().all_super_categories() [Category of magmas, Category of sets, Category of sets with partial maps, Category of objects] The following axioms are defined by this category:: sage: Magmas().Associative() Category of semigroups sage: Magmas().Unital() Category of unital magmas sage: Magmas().Commutative() Category of commutative magmas sage: Magmas().Unital().Inverse() Category of inverse unital magmas sage: Magmas().Associative() Category of semigroups sage: Magmas().Associative().Unital() Category of monoids sage: Magmas().Associative().Unital().Inverse() Category of groups TESTS:: sage: C = Magmas() sage: TestSuite(C).run() """ def super_categories(self): """ EXAMPLES:: sage: Magmas().super_categories() [Category of sets] """ return [Sets()] class SubcategoryMethods: @cached_method def Associative(self): """ Return the full subcategory of the associative objects of ``self``. A (multiplicative) :class:`magma Magmas` `M` is *associative* if, for all `x,y,z\in M`, .. MATH:: x * (y * z) = (x * y) * z .. SEEALSO:: :wikipedia:`Associative_property` EXAMPLES:: sage: Magmas().Associative() Category of semigroups TESTS:: sage: TestSuite(Magmas().Associative()).run() sage: Rings().Associative.__module__ 'sage.categories.magmas' """ return self._with_axiom('Associative') @cached_method def Commutative(self): """ Return the full subcategory of the commutative objects of ``self``. A (multiplicative) :class:`magma Magmas` `M` is *commutative* if, for all `x,y\in M`, .. MATH:: x * y = y * x .. SEEALSO:: :wikipedia:`Commutative_property` EXAMPLES:: sage: Magmas().Commutative() Category of commutative magmas sage: Monoids().Commutative() Category of commutative monoids TESTS:: sage: TestSuite(Magmas().Commutative()).run() sage: Rings().Commutative.__module__ 'sage.categories.magmas' """ return self._with_axiom('Commutative') @cached_method def Unital(self): r""" Return the subcategory of the unital objects of ``self``. A (multiplicative) :class:`magma Magmas` `M` is *unital* if it admits an element `1`, called *unit*, such that for all `x\in M`, .. MATH:: 1 * x = x * 1 = x This element is necessarily unique, and should be provided as ``M.one()``. .. SEEALSO:: :wikipedia:`Unital_magma#unital` EXAMPLES:: sage: Magmas().Unital() Category of unital magmas sage: Semigroups().Unital() Category of monoids sage: Monoids().Unital() Category of monoids sage: from sage.categories.associative_algebras import AssociativeAlgebras sage: AssociativeAlgebras(QQ).Unital() Category of algebras over Rational Field TESTS:: sage: TestSuite(Magmas().Unital()).run() sage: Semigroups().Unital.__module__ 'sage.categories.magmas' """ return self._with_axiom("Unital") @cached_method def FinitelyGeneratedAsMagma(self): r""" Return the subcategory of the objects of ``self`` that are endowed with a distinguished finite set of (multiplicative) magma generators. A set `S` of elements of a multiplicative magma form a *set of generators* if any element of the magma can be expressed recursively from elements of `S` and products thereof. It is not imposed that morphisms shall preserve the distinguished set of generators; hence this is a full subcategory. .. SEEALSO:: :wikipedia:`Unital_magma#unital` EXAMPLES:: sage: Magmas().FinitelyGeneratedAsMagma() Category of finitely generated magmas Being finitely generated does depend on the structure: for a ring, being finitely generated as a magma, as an additive magma, or as a ring are different concepts. Hence the name of this axiom is explicit:: sage: Rings().FinitelyGeneratedAsMagma() Category of finitely generated as magma rings On the other hand, it does not depend on the multiplicative structure: for example a group is finitely generated if and only if it is finitely generated as a magma. A short hand is provided when there is no ambiguity, and the output tries to reflect that:: sage: Semigroups().FinitelyGenerated() Category of finitely generated semigroups sage: Groups().FinitelyGenerated() Category of finitely generated groups sage: Semigroups().FinitelyGenerated().axioms() frozenset({'Associative', 'FinitelyGeneratedAsMagma'}) Note that the set of generators may depend on the actual category; for example, in a group, one can often use less generators since it is allowed to take inverses. TESTS:: sage: TestSuite(Magmas().FinitelyGeneratedAsMagma()).run() sage: Semigroups().FinitelyGeneratedAsMagma.__module__ 'sage.categories.magmas' """ return self._with_axiom("FinitelyGeneratedAsMagma") @cached_method def FinitelyGenerated(self): r""" Return the subcategory of the objects of ``self`` that are endowed with a distinguished finite set of (multiplicative) magma generators. EXAMPLES: This is a shorthand for :meth:`FinitelyGeneratedAsMagma`, which see:: sage: Magmas().FinitelyGenerated() Category of finitely generated magmas sage: Semigroups().FinitelyGenerated() Category of finitely generated semigroups sage: Groups().FinitelyGenerated() Category of finitely generated groups An error is raised if this is ambiguous:: sage: (Magmas() & AdditiveMagmas()).FinitelyGenerated() Traceback (most recent call last): ... ValueError: FinitelyGenerated is ambiguous for Join of Category of magmas and Category of additive magmas. Please use explicitly one of the FinitelyGeneratedAsXXX methods .. NOTE:: Checking that there is no ambiguity currently assumes that all the other "finitely generated" axioms involve an additive structure. As of Sage 6.4, this is correct. The use of this shorthand should be reserved for casual interactive use or when there is no risk of ambiguity. """ from sage.categories.additive_magmas import AdditiveMagmas if self.is_subcategory(AdditiveMagmas()): raise ValueError("FinitelyGenerated is ambiguous for {}.\nPlease use explicitly one of the FinitelyGeneratedAsXXX methods".format(self)) return self.FinitelyGeneratedAsMagma() @cached_method def Distributive(self): """ Return the full subcategory of the objects of ``self`` where `*` is distributive on `+`. INPUT: - ``self`` -- a subcategory of :class:`Magmas` and :class:`AdditiveMagmas` Given that Sage does not yet know that the category :class:`MagmasAndAdditiveMagmas` is the intersection of the categories :class:`Magmas` and :class:`AdditiveMagmas`, the method :meth:`MagmasAndAdditiveMagmas.SubcategoryMethods.Distributive` is not available, as would be desirable, for this intersection. This method is a workaround. It checks that ``self`` is a subcategory of both :class:`Magmas` and :class:`AdditiveMagmas` and upgrades it to a subcategory of :class:`MagmasAndAdditiveMagmas` before applying the axiom. It complains overwise, since the ``Distributive`` axiom does not make sense for a plain magma. EXAMPLES:: sage: (Magmas() & AdditiveMagmas()).Distributive() Category of distributive magmas and additive magmas sage: (Monoids() & CommutativeAdditiveGroups()).Distributive() Category of rings sage: Magmas().Distributive() Traceback (most recent call last): ... ValueError: The distributive axiom only makes sense on a magma which is simultaneously an additive magma sage: Semigroups().Distributive() Traceback (most recent call last): ... ValueError: The distributive axiom only makes sense on a magma which is simultaneously an additive magma TESTS:: sage: Semigroups().Distributive.__module__ 'sage.categories.magmas' sage: Rings().Distributive.__module__ 'sage.categories.magmas_and_additive_magmas' """ from .additive_magmas import AdditiveMagmas if not self.is_subcategory(AdditiveMagmas()): raise ValueError("The distributive axiom only makes sense on a magma which is simultaneously an additive magma") from .magmas_and_additive_magmas import MagmasAndAdditiveMagmas return (self & MagmasAndAdditiveMagmas()).Distributive() def JTrivial(self): r""" Return the full subcategory of the `J`-trivial objects of ``self``. This axiom is in fact only meaningful for :class:`semigroups <Semigroups>`. This stub definition is here as a workaround for :trac:`20515`, in order to define the `J`-trivial axiom as the intersection of the `L` and `R`-trivial axioms. .. SEEALSO:: :meth:`Semigroups.SubcategoryMethods.JTrivial` TESTS:: sage: Magmas().JTrivial() Category of j trivial magmas sage: (Semigroups().RTrivial() & Semigroups().LTrivial()) is Semigroups().JTrivial() True """ return self._with_axiom('JTrivial') Associative = LazyImport('sage.categories.semigroups', 'Semigroups', at_startup=True) FinitelyGeneratedAsMagma = LazyImport('sage.categories.finitely_generated_magmas', 'FinitelyGeneratedMagmas') class JTrivial(CategoryWithAxiom): # Workaround for #20515; see also Magmas.SubcategoryMethods.JTrivial pass class Algebras(AlgebrasCategory): def extra_super_categories(self): """ EXAMPLES: sage: Magmas().Commutative().Algebras(QQ).extra_super_categories() [Category of commutative magmas] This implements the fact that the algebra of a commutative magma is commutative:: sage: Magmas().Commutative().Algebras(QQ).super_categories() [Category of magma algebras over Rational Field, Category of commutative magmas] In particular, commutative monoid algebras are commutative algebras:: sage: Monoids().Commutative().Algebras(QQ).is_subcategory(Algebras(QQ).Commutative()) True """ from sage.categories.magmatic_algebras import MagmaticAlgebras return [MagmaticAlgebras(self.base_ring())] class Commutative(CategoryWithAxiom): class ParentMethods: def is_commutative(self): """ Return ``True``, since commutative magmas are commutative. EXAMPLES:: sage: Parent(QQ,category=CommutativeRings()).is_commutative() True """ return True class Algebras(AlgebrasCategory): def extra_super_categories(self): """ EXAMPLES: sage: Magmas().Commutative().Algebras(QQ).extra_super_categories() [Category of commutative magmas] This implements the fact that the algebra of a commutative magma is commutative:: sage: Magmas().Commutative().Algebras(QQ).super_categories() [Category of magma algebras over Rational Field, Category of commutative magmas] In particular, commutative monoid algebras are commutative algebras:: sage: Monoids().Commutative().Algebras(QQ).is_subcategory(Algebras(QQ).Commutative()) True """ return [Magmas().Commutative()] class CartesianProducts(CartesianProductsCategory): def extra_super_categories(self): r""" Implement the fact that a Cartesian product of commutative additive magmas is still an commutative additive magmas. EXAMPLES:: sage: C = Magmas().Commutative().CartesianProducts() sage: C.extra_super_categories() [Category of commutative magmas] sage: C.axioms() frozenset({'Commutative'}) """ return [Magmas().Commutative()] class Unital(CategoryWithAxiom): def additional_structure(self): r""" Return ``self``. Indeed, the category of unital magmas defines an additional structure, namely the unit of the magma which shall be preserved by morphisms. .. SEEALSO:: :meth:`Category.additional_structure` EXAMPLES:: sage: Magmas().Unital().additional_structure() Category of unital magmas """ return self class ParentMethods: @cached_method def one(self): r""" Return the unit of the monoid, that is the unique neutral element for `*`. .. NOTE:: The default implementation is to coerce `1` into ``self``. It is recommended to override this method because the coercion from the integers: - is not always meaningful (except for `1`); - often uses ``self.one()``. EXAMPLES:: sage: M = Monoids().example(); M An example of a monoid: the free monoid generated by ('a', 'b', 'c', 'd') sage: M.one() '' """ return self(1) def _test_one(self, **options): r""" Test that ``self.one()`` is an element of ``self`` and is neutral for the operation ``*``. INPUT: - ``options`` -- any keyword arguments accepted by :meth:`_tester` EXAMPLES: By default, this method tests only the elements returned by ``self.some_elements()``:: sage: S = Monoids().example() sage: S._test_one() However, the elements tested can be customized with the ``elements`` keyword argument:: sage: S._test_one(elements = (S('a'), S('b'))) See the documentation for :class:`TestSuite` for more information. """ tester = self._tester(**options) one = self.one() tester.assert_(self.is_parent_of(one)) for x in tester.some_elements(): tester.assert_(x * one == x) tester.assert_(one * x == x) # Check that one is immutable if it looks like we can test this if hasattr(one,"is_immutable"): tester.assertEqual(one.is_immutable(),True) if hasattr(one,"is_mutable"): tester.assertEqual(one.is_mutable(),False) def is_empty(self): r""" Return whether ``self`` is empty. Since this set is a unital magma it is not empty and this method always return ``False``. EXAMPLES:: sage: S = SymmetricGroup(2) sage: S.is_empty() False sage: M = Monoids().example() sage: M.is_empty() False TESTS:: sage: S.is_empty.__module__ 'sage.categories.magmas' sage: M.is_empty.__module__ 'sage.categories.magmas' """ return False class ElementMethods: __truediv__ = sage.categories.coercion_methods.__truediv__ __div__ = __truediv__ # For Python2/3 compatibility; see e.g. #18578 def _div_(left, right): r""" Default implementation of division, multiplying (on the right) by the inverse. INPUT: - ``left``, ``right`` -- two elements of the same unital magma .. SEEALSO:: :meth:`__div__` EXAMPLES:: sage: G = FreeGroup(2) sage: x0, x1 = G.group_generators() sage: c1 = cartesian_product([x0, x1]) sage: c2 = cartesian_product([x1, x0]) sage: c1._div_(c2) (x0*x1^-1, x1*x0^-1) With this implementation, division will fail as soon as ``right`` is not invertible, even if ``right`` actually divides ``left``:: sage: x = cartesian_product([2, 1]) sage: y = cartesian_product([1, 1]) sage: x / y (2, 1) sage: x / x Traceback (most recent call last): ... TypeError: no conversion of this rational to integer TESTS:: sage: c1._div_.__module__ 'sage.categories.magmas' """ return left._mul_(~right) class SubcategoryMethods: @cached_method def Inverse(self): r""" Return the full subcategory of the inverse objects of ``self``. An inverse :class:` (multiplicative) magma <Magmas>` is a :class:`unital magma <Magmas.Unital>` such that every element admits both an inverse on the left and on the right. Such a magma is also called a *loop*. .. SEEALSO:: :wikipedia:`Inverse_element`, :wikipedia:`Quasigroup` EXAMPLES:: sage: Magmas().Unital().Inverse() Category of inverse unital magmas sage: Monoids().Inverse() Category of groups TESTS:: sage: TestSuite(Magmas().Unital().Inverse()).run() sage: Algebras(QQ).Inverse.__module__ 'sage.categories.magmas' """ return self._with_axiom("Inverse") class Inverse(CategoryWithAxiom): class CartesianProducts(CartesianProductsCategory): def extra_super_categories(self): """ Implement the fact that a Cartesian product of magmas with inverses is a magma with inverse. EXAMPLES:: sage: C = Magmas().Unital().Inverse().CartesianProducts() sage: C.extra_super_categories(); [Category of inverse unital magmas] sage: sorted(C.axioms()) ['Inverse', 'Unital'] """ return [Magmas().Unital().Inverse()] class CartesianProducts(CartesianProductsCategory): def extra_super_categories(self): """ Implement the fact that a Cartesian product of unital magmas is a unital magma EXAMPLES:: sage: C = Magmas().Unital().CartesianProducts() sage: C.extra_super_categories(); [Category of unital magmas] sage: C.axioms() frozenset({'Unital'}) sage: Monoids().CartesianProducts().is_subcategory(Monoids()) True """ return [Magmas().Unital()] class ParentMethods: @cached_method def one(self): """ Return the unit of this Cartesian product. It is built from the units for the Cartesian factors of ``self``. EXAMPLES:: sage: cartesian_product([QQ, ZZ, RR]).one() (1, 1, 1.00000000000000) """ return self._cartesian_product_of_elements( _.one() for _ in self.cartesian_factors()) class ElementMethods: def __invert__(self): r""" Return the inverse of ``self``, if it exists. The inverse is computed by inverting each Cartesian factor and attempting to convert the result back to the original parent. For example, if one of the Cartesian factor is an element ``x`` of `\ZZ`, the result of ``~x`` is in `\QQ`. So we need to convert it back to `\ZZ`. As a side effect, this checks that ``x`` is indeed invertible in `\ZZ`. If needed an optimized version without this conversion could be implemented in :class:`Magmas.Unital.Inverse.ElementMethods`. EXAMPLES:: sage: C = cartesian_product([QQ, ZZ, RR, GF(5)]) sage: c = C([2,-1,2,2]); c (2, -1, 2.00000000000000, 2) sage: ~c (1/2, -1, 0.500000000000000, 3) This fails as soon as one of the entries is not invertible:: sage: ~C([0,2,2,2]) Traceback (most recent call last): ... ZeroDivisionError: rational division by zero sage: ~C([2,2,2,2]) Traceback (most recent call last): ... TypeError: no conversion of this rational to integer """ # variant without coercion: # return self.parent()._cartesian_product_of_elements( return self.parent()( ~x for x in self.cartesian_factors()) class Algebras(AlgebrasCategory): def extra_super_categories(self): """ EXAMPLES: sage: Magmas().Commutative().Algebras(QQ).extra_super_categories() [Category of commutative magmas] This implements the fact that the algebra of a commutative magma is commutative:: sage: Magmas().Commutative().Algebras(QQ).super_categories() [Category of magma algebras over Rational Field, Category of commutative magmas] In particular, commutative monoid algebras are commutative algebras:: sage: Monoids().Commutative().Algebras(QQ).is_subcategory(Algebras(QQ).Commutative()) True """ return [Magmas().Unital()] class Realizations(RealizationsCategory): class ParentMethods: @cached_method def one(self): r""" Return the unit element of ``self``. sage: from sage.combinat.root_system.extended_affine_weyl_group import ExtendedAffineWeylGroup sage: PvW0 = ExtendedAffineWeylGroup(['A',2,1]).PvW0() sage: PvW0 in Magmas().Unital().Realizations() True sage: PvW0.one() 1 """ return(self(self.realization_of().a_realization().one())) class ParentMethods: def product(self, x, y): """ The binary multiplication of the magma. INPUT: - ``x``, ``y`` -- elements of this magma OUTPUT: - an element of the magma (the product of ``x`` and ``y``) EXAMPLES:: sage: S = Semigroups().example("free") sage: x = S('a'); y = S('b') sage: S.product(x, y) 'ab' A parent in ``Magmas()`` must either implement :meth:`.product` in the parent class or ``_mul_`` in the element class. By default, the addition method on elements ``x._mul_(y)`` calls ``S.product(x,y)``, and reciprocally. As a bonus, ``S.product`` models the binary function from ``S`` to ``S``:: sage: bin = S.product sage: bin(x,y) 'ab' Currently, ``S.product`` is just a bound method:: sage: bin <bound method FreeSemigroup_with_category.product of An example of a semigroup: the free semigroup generated by ('a', 'b', 'c', 'd')> When Sage will support multivariate morphisms, it will be possible, and in fact recommended, to enrich ``S.product`` with extra mathematical structure. This will typically be implemented using lazy attributes.:: sage: bin # todo: not implemented Generic binary morphism: From: (S x S) To: S """ return x._mul_(y) product_from_element_class_mul = product def __init_extra__(self): """ sage: S = Semigroups().example("free") sage: S('a') * S('b') # indirect doctest 'ab' sage: S('a').__class__._mul_ == S('a').__class__._mul_parent True """ # This should instead register the multiplication to the coercion model # But this is not yet implemented in the coercion model # # Trac ticket #11900: The following used to test whether # self.product != self.product_from_element_class_mul. But # that is, of course, a bug. Namely otherwise, if the parent # has an optimized `product` then its elements will *always* use # a slow generic `_mul_`. # # So, in addition, it should be tested whether the element class exists # *and* has a custom _mul_, because in this case it must not be overridden. if (self.product.__func__ == self.product_from_element_class_mul.__func__): return if not (hasattr(self, "element_class") and hasattr(self.element_class, "_mul_parent")): return E = self.element_class if hasattr(E._mul_,'__func__'): try: el_class_mul = self.category().element_class._mul_.__func__ except AttributeError: # abstract method return if E._mul_.__func__ is el_class_mul: # self.product is custom, thus, we rely on it E._mul_ = E._mul_parent else: # E._mul_ has so far been abstract E._mul_ = E._mul_parent def multiplication_table(self, names='letters', elements=None): r""" Returns a table describing the multiplication operation. .. note:: The order of the elements in the row and column headings is equal to the order given by the table's :meth:`~sage.matrix.operation_table.OperationTable.list` method. The association can also be retrieved with the :meth:`~sage.matrix.operation_table.OperationTable.dict` method. INPUT: - ``names`` - the type of names used * ``'letters'`` - lowercase ASCII letters are used for a base 26 representation of the elements' positions in the list given by :meth:`~sage.matrix.operation_table.OperationTable.column_keys`, padded to a common width with leading 'a's. * ``'digits'`` - base 10 representation of the elements' positions in the list given by :meth:`~sage.matrix.operation_table.OperationTable.column_keys`, padded to a common width with leading zeros. * ``'elements'`` - the string representations of the elements themselves. * a list - a list of strings, where the length of the list equals the number of elements. - ``elements`` - default = ``None``. A list of elements of the magma, in forms that can be coerced into the structure, eg. their string representations. This may be used to impose an alternate ordering on the elements, perhaps when this is used in the context of a particular structure. The default is to use whatever ordering the ``S.list`` method returns. Or the ``elements`` can be a subset which is closed under the operation. In particular, this can be used when the base set is infinite. OUTPUT: The multiplication table as an object of the class :class:`~sage.matrix.operation_table.OperationTable` which defines several methods for manipulating and displaying the table. See the documentation there for full details to supplement the documentation here. EXAMPLES: The default is to represent elements as lowercase ASCII letters. :: sage: G=CyclicPermutationGroup(5) sage: G.multiplication_table() * a b c d e +---------- a| a b c d e b| b c d e a c| c d e a b d| d e a b c e| e a b c d All that is required is that an algebraic structure has a multiplication defined. A :class:`~sage.categories.examples.finite_semigroups.LeftRegularBand` is an example of a finite semigroup. The ``names`` argument allows displaying the elements in different ways. :: sage: from sage.categories.examples.finite_semigroups import LeftRegularBand sage: L=LeftRegularBand(('a','b')) sage: T=L.multiplication_table(names='digits') sage: T.column_keys() ('a', 'b', 'ab', 'ba') sage: T * 0 1 2 3 +-------- 0| 0 2 2 2 1| 3 1 3 3 2| 2 2 2 2 3| 3 3 3 3 Specifying the elements in an alternative order can provide more insight into how the operation behaves. :: sage: L=LeftRegularBand(('a','b','c')) sage: elts = sorted(L.list()) sage: L.multiplication_table(elements=elts) * a b c d e f g h i j k l m n o +------------------------------ a| a b c d e b b c c c d d e e e b| b b c c c b b c c c c c c c c c| c c c c c c c c c c c c c c c d| d e e d e e e e e e d d e e e e| e e e e e e e e e e e e e e e f| g g h h h f g h i j i j j i j g| g g h h h g g h h h h h h h h h| h h h h h h h h h h h h h h h i| j j j j j i j j i j i j j i j j| j j j j j j j j j j j j j j j k| l m m l m n o o n o k l m n o l| l m m l m m m m m m l l m m m m| m m m m m m m m m m m m m m m n| o o o o o n o o n o n o o n o o| o o o o o o o o o o o o o o o The ``elements`` argument can be used to provide a subset of the elements of the structure. The subset must be closed under the operation. Elements need only be in a form that can be coerced into the set. The ``names`` argument can also be used to request that the elements be represented with their usual string representation. :: sage: L=LeftRegularBand(('a','b','c')) sage: elts=['a', 'c', 'ac', 'ca'] sage: L.multiplication_table(names='elements', elements=elts) * 'a' 'c' 'ac' 'ca' +-------------------- 'a'| 'a' 'ac' 'ac' 'ac' 'c'| 'ca' 'c' 'ca' 'ca' 'ac'| 'ac' 'ac' 'ac' 'ac' 'ca'| 'ca' 'ca' 'ca' 'ca' The table returned can be manipulated in various ways. See the documentation for :class:`~sage.matrix.operation_table.OperationTable` for more comprehensive documentation. :: sage: G=AlternatingGroup(3) sage: T=G.multiplication_table() sage: T.column_keys() ((), (1,2,3), (1,3,2)) sage: sorted(T.translation().items()) [('a', ()), ('b', (1,2,3)), ('c', (1,3,2))] sage: T.change_names(['x', 'y', 'z']) sage: sorted(T.translation().items()) [('x', ()), ('y', (1,2,3)), ('z', (1,3,2))] sage: T * x y z +------ x| x y z y| y z x z| z x y """ from sage.matrix.operation_table import OperationTable import operator return OperationTable(self, operation=operator.mul, names=names, elements=elements) class ElementMethods: __mul__ = sage.categories.coercion_methods.__mul__ @abstract_method(optional = True) def _mul_(self, right): """ Product of two elements INPUT: - ``self``, ``right`` -- two elements with the same parent OUTPUT: - an element of the same parent EXAMPLES:: sage: S = Semigroups().example("free") sage: x = S('a'); y = S('b') sage: x._mul_(y) 'ab' """ _mul_parent = sage.categories.coercion_methods._mul_parent def is_idempotent(self): r""" Test whether ``self`` is idempotent. EXAMPLES:: sage: S = Semigroups().example("free"); S An example of a semigroup: the free semigroup generated by ('a', 'b', 'c', 'd') sage: a = S('a') sage: a^2 'aa' sage: a.is_idempotent() False :: sage: L = Semigroups().example("leftzero"); L An example of a semigroup: the left zero semigroup sage: x = L('x') sage: x^2 'x' sage: x.is_idempotent() True """ return self * self == self class CartesianProducts(CartesianProductsCategory): def extra_super_categories(self): """ This implements the fact that a subquotient (and therefore a quotient or subobject) of a finite set is finite. EXAMPLES:: sage: Semigroups().CartesianProducts().extra_super_categories() [Category of semigroups] sage: Semigroups().CartesianProducts().super_categories() [Category of semigroups, Category of Cartesian products of magmas] """ return [Magmas()] def example(self): """ Return an example of Cartesian product of magmas. EXAMPLES:: sage: C = Magmas().CartesianProducts().example(); C The Cartesian product of (Rational Field, Integer Ring, Integer Ring) sage: C.category() Category of Cartesian products of commutative rings sage: sorted(C.category().axioms()) ['AdditiveAssociative', 'AdditiveCommutative', 'AdditiveInverse', 'AdditiveUnital', 'Associative', 'Commutative', 'Distributive', 'Unital'] sage: TestSuite(C).run() """ from .cartesian_product import cartesian_product from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ return cartesian_product([QQ, ZZ, ZZ]) class ParentMethods: def product(self, left, right): """ EXAMPLES:: sage: C = Magmas().CartesianProducts().example(); C The Cartesian product of (Rational Field, Integer Ring, Integer Ring) sage: x = C.an_element(); x (1/2, 1, 1) sage: x * x (1/4, 1, 1) sage: A = SymmetricGroupAlgebra(QQ, 3); sage: x = cartesian_product([A([1,3,2]), A([2,3,1])]) sage: y = cartesian_product([A([1,3,2]), A([2,3,1])]) sage: cartesian_product([A,A]).product(x,y) B[(0, [1, 2, 3])] + B[(1, [3, 1, 2])] sage: x*y B[(0, [1, 2, 3])] + B[(1, [3, 1, 2])] """ return self._cartesian_product_of_elements([(a*b) for (a,b) in zip(left.cartesian_factors(), right.cartesian_factors())]) class Subquotients(SubquotientsCategory): r""" The category of subquotient magmas. See :meth:`Sets.SubcategoryMethods.Subquotients` for the general setup for subquotients. In the case of a subquotient magma `S` of a magma `G`, the condition that `r` be a morphism in ``As`` can be rewritten as follows: - for any two `a,b \in S` the identity `a \times_S b = r(l(a) \times_G l(b))` holds. This is used by this category to implement the product `\times_S` of `S` from `l` and `r` and the product of `G`. EXAMPLES:: sage: Semigroups().Subquotients().all_super_categories() [Category of subquotients of semigroups, Category of semigroups, Category of subquotients of magmas, Category of magmas, Category of subquotients of sets, Category of sets, Category of sets with partial maps, Category of objects] """ class ParentMethods: def product(self, x, y): """ Return the product of two elements of ``self``. EXAMPLES:: sage: S = Semigroups().Subquotients().example() sage: S An example of a (sub)quotient semigroup: a quotient of the left zero semigroup sage: S.product(S(19), S(3)) 19 Here is a more elaborate example involving a sub algebra:: sage: Z = SymmetricGroup(5).algebra(QQ).center() sage: B = Z.basis() sage: B[3] * B[2] 4*B[2] + 6*B[3] + 5*B[6] """ assert(x in self) assert(y in self) return self.retract(self.lift(x) * self.lift(y)) class Realizations(RealizationsCategory): class ParentMethods: def product_by_coercion(self, left, right): r""" Default implementation of product for realizations. This method coerces to the realization specified by ``self.realization_of().a_realization()``, computes the product in that realization, and then coerces back. EXAMPLES:: sage: Out = Sets().WithRealizations().example().Out(); Out The subset algebra of {1, 2, 3} over Rational Field in the Out basis sage: Out.product <bound method SubsetAlgebra.Out_with_category.product_by_coercion of The subset algebra of {1, 2, 3} over Rational Field in the Out basis> sage: Out.product.__module__ 'sage.categories.magmas' sage: x = Out.an_element() sage: y = Out.an_element() sage: Out.product(x, y) Out[{}] + 4*Out[{1}] + 9*Out[{2}] + Out[{1, 2}] """ R = self.realization_of().a_realization() return self(R(left) * R(right))
class DivisionRings(CategoryWithAxiom): """ The category of division rings A division ring (or skew field) is a not necessarily commutative ring where all non-zero elements have multiplicative inverses EXAMPLES:: sage: DivisionRings() Category of division rings sage: DivisionRings().super_categories() [Category of domains] TESTS:: sage: TestSuite(DivisionRings()).run() """ # This information could be deduced from the name. It's set here # just to get ``division rings`` for the name of the objects of # this category and not ``no zero divisors division rings``. See # :meth:`Category_with_axiom._repr_object_names` and the ``named`` # option of :meth:`Category_with_axiom._without_axioms _base_category_class_and_axiom = (Rings, "Division") def extra_super_categories(self): r""" Return the :class:`Domains` category. This method specifies that a division ring has no zero divisors, i.e. is a domain. .. SEEALSO:: The :ref:`axioms-deduction-rules` section in the documentation of axioms EXAMPLES: sage: DivisionRings().extra_super_categories() (Category of domains,) sage: "NoZeroDivisors" in DivisionRings().axioms() True """ return (Rings().NoZeroDivisors(), ) Commutative = LazyImport('sage.categories.fields', 'Fields', at_startup=True) def Finite_extra_super_categories(self): r""" Return extraneous super categories for ``DivisionRings().Finite()``. EXAMPLES: Any field is a division ring:: sage: Fields().is_subcategory(DivisionRings()) True This methods specifies that, by Weddeburn theorem, the reciprocal holds in the finite case: a finite division ring is commutative and thus a field:: sage: DivisionRings().Finite_extra_super_categories() (Category of commutative magmas,) sage: DivisionRings().Finite() Category of finite fields .. WARNING:: This is not implemented in ``DivisionRings.Finite.extra_super_categories`` because the categories of finite division rings and of finite fields coincide. See the section :ref:`axioms-deduction-rules` in the documentation of axioms. TESTS:: sage: DivisionRings().Finite() is Fields().Finite() True This works also for subcategories:: sage: class Foo(Category): ....: def super_categories(self): return [DivisionRings()] sage: Foo().Finite().is_subcategory(Fields()) True """ from sage.categories.magmas import Magmas return (Magmas().Commutative(), ) class ParentMethods: pass class ElementMethods: pass
class MagmasAndAdditiveMagmas(Category_singleton): """ The category of sets `(S,+,*)` with an additive operation '+' and a multiplicative operation `*` EXAMPLES:: sage: from sage.categories.magmas_and_additive_magmas import MagmasAndAdditiveMagmas sage: C = MagmasAndAdditiveMagmas(); C Category of magmas and additive magmas This is the base category for the categories of rings and their variants:: sage: C.Distributive() Category of distributive magmas and additive magmas sage: C.Distributive().Associative().AdditiveAssociative().AdditiveCommutative().AdditiveUnital().AdditiveInverse() Category of rngs sage: C.Distributive().Associative().AdditiveAssociative().AdditiveCommutative().AdditiveUnital().Unital() Category of semirings sage: C.Distributive().Associative().AdditiveAssociative().AdditiveCommutative().AdditiveUnital().AdditiveInverse().Unital() Category of rings This category is really meant to represent the intersection of the categories of :class:`Magmas` and :class:`AdditiveMagmas`; however Sage's infrastructure does not allow yet to model this:: sage: Magmas() & AdditiveMagmas() Join of Category of magmas and Category of additive magmas sage: Magmas() & AdditiveMagmas() # todo: not implemented Category of magmas and additive magmas TESTS:: sage: TestSuite(MagmasAndAdditiveMagmas()).run() """ class SubcategoryMethods: @cached_method def Distributive(self): r""" Return the full subcategory of the objects of ``self`` where `*` is distributive on `+`. A :class:`magma <Magmas>` and :class:`additive magma <AdditiveMagmas>` `M` is *distributive* if, for all `x,y,z \in M`, .. MATH:: x * (y+z) = x*y + x*z \text{ and } (x+y) * z = x*z + y*z EXAMPLES:: sage: from sage.categories.magmas_and_additive_magmas import MagmasAndAdditiveMagmas sage: C = MagmasAndAdditiveMagmas().Distributive(); C Category of distributive magmas and additive magmas .. NOTE:: Given that Sage does not know that :class:`MagmasAndAdditiveMagmas` is the intersection of :class:`Magmas` and :class:`AdditiveMagmas`, this method is not available for:: sage: Magmas() & AdditiveMagmas() Join of Category of magmas and Category of additive magmas Still, the natural syntax works:: sage: (Magmas() & AdditiveMagmas()).Distributive() Category of distributive magmas and additive magmas thanks to a workaround implemented in :meth:`Magmas.SubcategoryMethods.Distributive`:: sage: (Magmas() & AdditiveMagmas()).Distributive.__module__ 'sage.categories.magmas' TESTS:: sage: TestSuite(C).run() sage: Fields().Distributive.__module__ 'sage.categories.magmas_and_additive_magmas' """ return self._with_axiom('Distributive') def super_categories(self): """ EXAMPLES:: sage: from sage.categories.magmas_and_additive_magmas import MagmasAndAdditiveMagmas sage: MagmasAndAdditiveMagmas().super_categories() [Category of magmas, Category of additive magmas] """ return [Magmas(), AdditiveMagmas()] def additional_structure(self): r""" Return ``None``. Indeed, this category is meant to represent the join of :class:`AdditiveMagmas` and :class:`Magmas`. As such, it defines no additional structure. .. SEEALSO:: :meth:`Category.additional_structure` EXAMPLES:: sage: from sage.categories.magmas_and_additive_magmas import MagmasAndAdditiveMagmas sage: MagmasAndAdditiveMagmas().additional_structure() """ return None Distributive = LazyImport('sage.categories.distributive_magmas_and_additive_magmas', 'DistributiveMagmasAndAdditiveMagmas', at_startup=True) class CartesianProducts(CartesianProductsCategory): def extra_super_categories(self): r""" Implement the fact that this structure is stable under Cartesian products. TESTS:: sage: from sage.categories.magmas_and_additive_magmas import MagmasAndAdditiveMagmas sage: MagmasAndAdditiveMagmas().CartesianProducts().extra_super_categories() [Category of magmas and additive magmas] """ return [MagmasAndAdditiveMagmas()]
class Groups(CategoryWithAxiom): """ The category of (multiplicative) groups, i.e. monoids with inverses. EXAMPLES:: sage: Groups() Category of groups sage: Groups().super_categories() [Category of monoids, Category of inverse unital magmas] TESTS:: sage: TestSuite(Groups()).run() """ _base_category_class_and_axiom = (Monoids, "Inverse") def example(self): """ EXAMPLES:: sage: Groups().example() General Linear Group of degree 4 over Rational Field """ from sage.rings.rational_field import QQ from sage.groups.matrix_gps.linear import GL return GL(4,QQ) @staticmethod def free(index_set=None, names=None, **kwds): r""" Return the free group. INPUT: - ``index_set`` -- (optional) an index set for the generators; if an integer, then this represents `\{0, 1, \ldots, n-1\}` - ``names`` -- a string or list/tuple/iterable of strings (default: ``'x'``); the generator names or name prefix When the index set is an integer or only variable names are given, this returns :class:`~sage.groups.free_group.FreeGroup_class`, which currently has more features due to the interface with GAP than :class:`~sage.groups.indexed_free_group.IndexedFreeGroup`. EXAMPLES:: sage: Groups.free(index_set=ZZ) Free group indexed by Integer Ring sage: Groups().free(ZZ) Free group indexed by Integer Ring sage: Groups().free(5) Free Group on generators {x0, x1, x2, x3, x4} sage: F.<x,y,z> = Groups().free(); F Free Group on generators {x, y, z} """ from sage.rings.all import ZZ if index_set in ZZ or (index_set is None and names is not None): from sage.groups.free_group import FreeGroup if names is None: return FreeGroup(index_set, **kwds) return FreeGroup(index_set, names, **kwds) from sage.groups.indexed_free_group import IndexedFreeGroup return IndexedFreeGroup(index_set, **kwds) class ParentMethods: def group_generators(self): """ Return group generators for ``self``. This default implementation calls :meth:`gens`, for backward compatibility. EXAMPLES:: sage: A = AlternatingGroup(4) sage: A.group_generators() Family ((2,3,4), (1,2,3)) """ from sage.sets.family import Family try: return Family(self.gens()) except AttributeError: raise NotImplementedError("no generators are implemented for this group") def monoid_generators(self): r""" Return the generators of ``self`` as a monoid. Let `G` be a group with generating set `X`. In general, the generating set of `G` as a monoid is given by `X \cup X^{-1}`, where `X^{-1}` is the set of inverses of `X`. If `G` is a finite group, then the generating set as a monoid is `X`. EXAMPLES:: sage: A = AlternatingGroup(4) sage: A.monoid_generators() Family ((2,3,4), (1,2,3)) sage: F.<x,y> = FreeGroup() sage: F.monoid_generators() Family (x, y, x^-1, y^-1) """ G = self.group_generators() from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets if G not in FiniteEnumeratedSets(): raise NotImplementedError("currently only implemented for finitely generated groups") from sage.sets.family import Family return Family(tuple(G) + tuple(~x for x in G)) def _test_inverse(self, **options): """ Run generic tests on the method :meth:`.__invert__`. See also: :class:`TestSuite`. EXAMPLES:: sage: G = SymmetricGroup(3) sage: G._test_inverse() """ tester = self._tester(**options) for x in tester.some_elements(): tester.assertEqual(x * ~x, self.one()) tester.assertEqual(~x * x, self.one()) def semidirect_product(self, N, mapping, check = True): r""" The semi-direct product of two groups EXAMPLES:: sage: G = Groups().example() sage: G.semidirect_product(G,Morphism(G,G)) Traceback (most recent call last): ... NotImplementedError: semidirect product of General Linear Group of degree 4 over Rational Field and General Linear Group of degree 4 over Rational Field not yet implemented """ raise NotImplementedError("semidirect product of %s and %s not yet implemented"%(self, N)) def holomorph(self): r""" The holomorph of a group The holomorph of a group `G` is the semidirect product `G \rtimes_{id} Aut(G)`, where `id` is the identity function on `Aut(G)`, the automorphism group of `G`. See :wikipedia:`Holomorph (mathematics)` EXAMPLES:: sage: G = Groups().example() sage: G.holomorph() Traceback (most recent call last): ... NotImplementedError: holomorph of General Linear Group of degree 4 over Rational Field not yet implemented """ raise NotImplementedError("holomorph of %s not yet implemented"%self) def cayley_table(self, names='letters', elements=None): r""" Returns the "multiplication" table of this multiplicative group, which is also known as the "Cayley table". .. note:: The order of the elements in the row and column headings is equal to the order given by the table's :meth:`~sage.matrix.operation_table.OperationTable.column_keys` method. The association between the actual elements and the names/symbols used in the table can also be retrieved as a dictionary with the :meth:`~sage.matrix.operation_table.OperationTable.translation` method. For groups, this routine should behave identically to the :meth:`~sage.categories.magmas.Magmas.ParentMethods.multiplication_table` method for magmas, which applies in greater generality. INPUT: - ``names`` - the type of names used, values are: * ``'letters'`` - lowercase ASCII letters are used for a base 26 representation of the elements' positions in the list given by :meth:`list`, padded to a common width with leading 'a's. * ``'digits'`` - base 10 representation of the elements' positions in the list given by :meth:`~sage.matrix.operation_table.OperationTable.column_keys`, padded to a common width with leading zeros. * ``'elements'`` - the string representations of the elements themselves. * a list - a list of strings, where the length of the list equals the number of elements. - ``elements`` - default = ``None``. A list of elements of the group, in forms that can be coerced into the structure, eg. their string representations. This may be used to impose an alternate ordering on the elements, perhaps when this is used in the context of a particular structure. The default is to use whatever ordering is provided by the the group, which is reported by the :meth:`~sage.matrix.operation_table.OperationTable.column_keys` method. Or the ``elements`` can be a subset which is closed under the operation. In particular, this can be used when the base set is infinite. OUTPUT: An object representing the multiplication table. This is an :class:`~sage.matrix.operation_table.OperationTable` object and even more documentation can be found there. EXAMPLES: Permutation groups, matrix groups and abelian groups can all compute their multiplication tables. :: sage: G = DiCyclicGroup(3) sage: T = G.cayley_table() sage: T.column_keys() ((), (5,6,7), ..., (1,4,2,3)(5,7)) sage: T * a b c d e f g h i j k l +------------------------ a| a b c d e f g h i j k l b| b c a e f d i g h l j k c| c a b f d e h i g k l j d| d e f a b c j k l g h i e| e f d b c a l j k i g h f| f d e c a b k l j h i g g| g h i j k l d e f a b c h| h i g k l j f d e c a b i| i g h l j k e f d b c a j| j k l g h i a b c d e f k| k l j h i g c a b f d e l| l j k i g h b c a e f d :: sage: M = SL(2, 2) sage: M.cayley_table() * a b c d e f +------------ a| c e a f b d b| d f b e a c c| a b c d e f d| b a d c f e e| f d e b c a f| e c f a d b <BLANKLINE> :: sage: A = AbelianGroup([2, 3]) sage: A.cayley_table() * a b c d e f +------------ a| a b c d e f b| b c a e f d c| c a b f d e d| d e f a b c e| e f d b c a f| f d e c a b Lowercase ASCII letters are the default symbols used for the table, but you can also specify the use of decimal digit strings, or provide your own strings (in the proper order if they have meaning). Also, if the elements themselves are not too complex, you can choose to just use the string representations of the elements themselves. :: sage: C=CyclicPermutationGroup(11) sage: C.cayley_table(names='digits') * 00 01 02 03 04 05 06 07 08 09 10 +--------------------------------- 00| 00 01 02 03 04 05 06 07 08 09 10 01| 01 02 03 04 05 06 07 08 09 10 00 02| 02 03 04 05 06 07 08 09 10 00 01 03| 03 04 05 06 07 08 09 10 00 01 02 04| 04 05 06 07 08 09 10 00 01 02 03 05| 05 06 07 08 09 10 00 01 02 03 04 06| 06 07 08 09 10 00 01 02 03 04 05 07| 07 08 09 10 00 01 02 03 04 05 06 08| 08 09 10 00 01 02 03 04 05 06 07 09| 09 10 00 01 02 03 04 05 06 07 08 10| 10 00 01 02 03 04 05 06 07 08 09 :: sage: G=QuaternionGroup() sage: names=['1', 'I', '-1', '-I', 'J', '-K', '-J', 'K'] sage: G.cayley_table(names=names) * 1 I -1 -I J -K -J K +------------------------ 1| 1 I -1 -I J -K -J K I| I -1 -I 1 K J -K -J -1| -1 -I 1 I -J K J -K -I| -I 1 I -1 -K -J K J J| J -K -J K -1 -I 1 I -K| -K -J K J I -1 -I 1 -J| -J K J -K 1 I -1 -I K| K J -K -J -I 1 I -1 :: sage: A=AbelianGroup([2,2]) sage: A.cayley_table(names='elements') * 1 f1 f0 f0*f1 +------------------------ 1| 1 f1 f0 f0*f1 f1| f1 1 f0*f1 f0 f0| f0 f0*f1 1 f1 f0*f1| f0*f1 f0 f1 1 The :meth:`~sage.matrix.operation_table.OperationTable.change_names` routine behaves similarly, but changes an existing table "in-place." :: sage: G=AlternatingGroup(3) sage: T=G.cayley_table() sage: T.change_names('digits') sage: T * 0 1 2 +------ 0| 0 1 2 1| 1 2 0 2| 2 0 1 For an infinite group, you can still work with finite sets of elements, provided the set is closed under multiplication. Elements will be coerced into the group as part of setting up the table. :: sage: G=SL(2,ZZ) sage: G Special Linear Group of degree 2 over Integer Ring sage: identity = matrix(ZZ, [[1,0], [0,1]]) sage: G.cayley_table(elements=[identity, -identity]) * a b +---- a| a b b| b a The :class:`~sage.matrix.operation_table.OperationTable` class provides even greater flexibility, including changing the operation. Here is one such example, illustrating the computation of commutators. ``commutator`` is defined as a function of two variables, before being used to build the table. From this, the commutator subgroup seems obvious, and creating a Cayley table with just these three elements confirms that they form a closed subset in the group. :: sage: from sage.matrix.operation_table import OperationTable sage: G = DiCyclicGroup(3) sage: commutator = lambda x, y: x*y*x^-1*y^-1 sage: T = OperationTable(G, commutator) sage: T . a b c d e f g h i j k l +------------------------ a| a a a a a a a a a a a a b| a a a a a a c c c c c c c| a a a a a a b b b b b b d| a a a a a a a a a a a a e| a a a a a a c c c c c c f| a a a a a a b b b b b b g| a b c a b c a c b a c b h| a b c a b c b a c b a c i| a b c a b c c b a c b a j| a b c a b c a c b a c b k| a b c a b c b a c b a c l| a b c a b c c b a c b a sage: trans = T.translation() sage: comm = [trans['a'], trans['b'], trans['c']] sage: comm [(), (5,6,7), (5,7,6)] sage: P = G.cayley_table(elements=comm) sage: P * a b c +------ a| a b c b| b c a c| c a b .. TODO:: Arrange an ordering of elements into cosets of a normal subgroup close to size `\sqrt{n}`. Then the quotient group structure is often apparent in the table. See comments on :trac:`7555`. AUTHOR: - Rob Beezer (2010-03-15) """ from sage.matrix.operation_table import OperationTable import operator return OperationTable(self, operation=operator.mul, names=names, elements=elements) def conjugacy_class(self, g): r""" Return the conjugacy class of the element ``g``. This is a fall-back method for groups not defined over GAP. EXAMPLES:: sage: A = AbelianGroup([2,2]) sage: c = A.conjugacy_class(A.an_element()) sage: type(c) <class 'sage.groups.conjugacy_classes.ConjugacyClass_with_category'> """ from sage.groups.conjugacy_classes import ConjugacyClass return ConjugacyClass(self, g) class ElementMethods: def conjugacy_class(self): r""" Return the conjugacy class of ``self``. EXAMPLES:: sage: D = DihedralGroup(5) sage: g = D((1,3,5,2,4)) sage: g.conjugacy_class() Conjugacy class of (1,3,5,2,4) in Dihedral group of order 10 as a permutation group sage: H = MatrixGroup([matrix(GF(5),2,[1,2, -1, 1]), matrix(GF(5),2, [1,1, 0,1])]) sage: h = H(matrix(GF(5),2,[1,2, -1, 1])) sage: h.conjugacy_class() Conjugacy class of [1 2] [4 1] in Matrix group over Finite Field of size 5 with 2 generators ( [1 2] [1 1] [4 1], [0 1] ) sage: G = SL(2, GF(2)) sage: g = G.gens()[0] sage: g.conjugacy_class() Conjugacy class of [1 1] [0 1] in Special Linear Group of degree 2 over Finite Field of size 2 sage: G = SL(2, QQ) sage: g = G([[1,1],[0,1]]) sage: g.conjugacy_class() Conjugacy class of [1 1] [0 1] in Special Linear Group of degree 2 over Rational Field """ return self.parent().conjugacy_class(self) Finite = LazyImport('sage.categories.finite_groups', 'FiniteGroups', at_startup=True) Lie = LazyImport('sage.categories.lie_groups', 'LieGroups', 'Lie') Algebras = LazyImport('sage.categories.group_algebras', 'GroupAlgebras', at_startup=True) class Commutative(CategoryWithAxiom): r""" Category of commutative (abelian) groups. A group `G` is *commutative* if `xy = yx` for all `x,y \in G`. """ @staticmethod def free(index_set=None, names=None, **kwds): r""" Return the free commutative group. INPUT: - ``index_set`` -- (optional) an index set for the generators; if an integer, then this represents `\{0, 1, \ldots, n-1\}` - ``names`` -- a string or list/tuple/iterable of strings (default: ``'x'``); the generator names or name prefix EXAMPLES:: sage: Groups.Commutative.free(index_set=ZZ) Free abelian group indexed by Integer Ring sage: Groups().Commutative().free(ZZ) Free abelian group indexed by Integer Ring sage: Groups().Commutative().free(5) Multiplicative Abelian group isomorphic to Z x Z x Z x Z x Z sage: F.<x,y,z> = Groups().Commutative().free(); F Multiplicative Abelian group isomorphic to Z x Z x Z """ from sage.rings.all import ZZ if names is not None: if isinstance(names, str): if ',' not in names and index_set in ZZ: names = [names + repr(i) for i in range(index_set)] else: names = names.split(',') names = tuple(names) if index_set is None: index_set = ZZ(len(names)) if index_set in ZZ: from sage.groups.abelian_gps.abelian_group import AbelianGroup return AbelianGroup(index_set, names=names, **kwds) if index_set in ZZ: from sage.groups.abelian_gps.abelian_group import AbelianGroup return AbelianGroup(index_set, **kwds) from sage.groups.indexed_free_group import IndexedFreeAbelianGroup return IndexedFreeAbelianGroup(index_set, names=names, **kwds) class CartesianProducts(CartesianProductsCategory): """ The category of groups constructed as Cartesian products of groups. This construction gives the direct product of groups. See :wikipedia:`Direct_product` and :wikipedia:`Direct_product_of_groups` for more information. """ def extra_super_categories(self): """ A Cartesian product of groups is endowed with a natural group structure. EXAMPLES:: sage: C = Groups().CartesianProducts() sage: C.extra_super_categories() [Category of groups] sage: sorted(C.super_categories(), key=str) [Category of Cartesian products of inverse unital magmas, Category of Cartesian products of monoids, Category of groups] """ return [self.base_category()] class ParentMethods: @cached_method def group_generators(self): """ Return the group generators of ``self``. EXAMPLES:: sage: C5 = CyclicPermutationGroup(5) sage: C4 = CyclicPermutationGroup(4) sage: S4 = SymmetricGroup(3) sage: C = cartesian_product([C5, C4, S4]) sage: C.group_generators() Family (((1,2,3,4,5), (), ()), ((), (1,2,3,4), ()), ((), (), (1,2)), ((), (), (2,3))) We check the other portion of :trac:`16718` is fixed:: sage: len(C.j_classes()) 1 An example with an infinitely generated group (a better output is needed):: sage: G = Groups.free([1,2]) sage: H = Groups.free(ZZ) sage: C = cartesian_product([G, H]) sage: C.monoid_generators() Lazy family (gen(i))_{i in The Cartesian product of (...)} """ F = self.cartesian_factors() ids = tuple(G.one() for G in F) def lift(i, gen): cur = list(ids) cur[i] = gen return self._cartesian_product_of_elements(cur) from sage.sets.family import Family # Finitely generated cat = FiniteEnumeratedSets() if all(G.group_generators() in cat or isinstance(G.group_generators(), (tuple, list)) for G in F): ret = [lift(i, gen) for i,G in enumerate(F) for gen in G.group_generators()] return Family(ret) # Infinitely generated # This does not return a good output, but it is "correct" # TODO: Figure out a better way to do things gens_prod = cartesian_product([Family(G.group_generators(), lambda g: (i, g)) for i,G in enumerate(F)]) return Family(gens_prod, lift, name="gen") def order(self): r""" Return the cardinality of self. EXAMPLES:: sage: C = cartesian_product([SymmetricGroup(10), SL(2,GF(3))]) sage: C.order() 87091200 TESTS:: sage: C.order.__module__ 'sage.categories.groups' .. TODO:: this method is just here to prevent ``FiniteGroups.ParentMethods`` to call ``_cardinality_from_iterator``. """ from sage.misc.misc_c import prod return prod(c.cardinality() for c in self.cartesian_factors()) class ElementMethods: def multiplicative_order(self): r""" Return the multiplicative order of this element. EXAMPLES:: sage: G1 = SymmetricGroup(3) sage: G2 = SL(2,3) sage: G = cartesian_product([G1,G2]) sage: G((G1.gen(0), G2.gen(1))).multiplicative_order() 12 """ from sage.rings.infinity import Infinity orders = [x.multiplicative_order() for x in self.cartesian_factors()] if any(o is Infinity for o in orders): return Infinity else: from sage.arith.functions import LCM_list return LCM_list(orders) class Topological(TopologicalSpacesCategory): """
class Algebras(CategoryWithAxiom_over_base_ring): r""" The category of associative and unital algebras over a given base ring. An associative and unital algebra over a ring `R` is a module over `R` which is itself a ring. .. WARNING:: :class:`Algebras` will be eventually be replaced by :class:`.magmatic_algebras.MagmaticAlgebras` for consistency with e.g. :wikipedia:`Algebras` which assumes neither associativity nor the existence of a unit (see :trac:`15043`). .. TODO:: Should `R` be a commutative ring? EXAMPLES:: sage: Algebras(ZZ) Category of algebras over Integer Ring sage: sorted(Algebras(ZZ).super_categories(), key=str) [Category of associative algebras over Integer Ring, Category of rings, Category of unital algebras over Integer Ring] TESTS:: sage: TestSuite(Algebras(ZZ)).run() """ _base_category_class_and_axiom = (AssociativeAlgebras, 'Unital') # For backward compatibility? def __contains__(self, x): """ Membership testing EXAMPLES:: sage: QQ['x'] in Algebras(QQ) True sage: QQ^3 in Algebras(QQ) False sage: QQ['x'] in Algebras(CDF) False """ if super(Algebras, self).__contains__(x): return True from sage.rings.ring import Algebra return isinstance(x, Algebra) and x.base_ring() == self.base_ring() # def extra_super_categories(self): # """ # EXAMPLES:: # sage: Algebras(ZZ).super_categories() # [Category of associative algebras over Integer Ring, Category of rings] # """ # R = self.base_ring() # return [Rings()] # TODO: won't be needed when Rings() will be Rngs().Unital() class SubcategoryMethods: def Semisimple(self): """ Return the subcategory of semisimple objects of ``self``. .. NOTE:: This mimics the syntax of axioms for a smooth transition if ``Semisimple`` becomes one. EXAMPLES:: sage: Algebras(QQ).Semisimple() Category of semisimple algebras over Rational Field sage: Algebras(QQ).WithBasis().FiniteDimensional().Semisimple() Category of finite dimensional semisimple algebras with basis over Rational Field """ from sage.categories.semisimple_algebras import SemisimpleAlgebras return self & SemisimpleAlgebras(self.base_ring()) Commutative = LazyImport('sage.categories.commutative_algebras', 'CommutativeAlgebras', at_startup=True) Filtered = LazyImport('sage.categories.filtered_algebras', 'FilteredAlgebras') Graded = LazyImport('sage.categories.graded_algebras', 'GradedAlgebras') Super = LazyImport('sage.categories.super_algebras', 'SuperAlgebras') # at_startup currently needed for MatrixSpace, see #22955 (e.g., comment:20) WithBasis = LazyImport('sage.categories.algebras_with_basis', 'AlgebrasWithBasis', at_startup=True) #if/when Semisimple becomes an axiom Semisimple = LazyImport('sage.categories.semisimple_algebras', 'SemisimpleAlgebras') class ElementMethods: # TODO: move the content of AlgebraElement here or higher in the category hierarchy def _div_(self, y): """ Division by invertible elements # TODO: move in Monoids EXAMPLES:: sage: C = AlgebrasWithBasis(QQ).example() sage: x = C(2); x 2*B[word: ] sage: y = C.algebra_generators().first(); y B[word: a] sage: y._div_(x) 1/2*B[word: a] sage: x._div_(y) Traceback (most recent call last): ... ValueError: cannot invert self (= B[word: a]) """ return self.parent().product(self, ~y) class Quotients(QuotientsCategory): class ParentMethods: def algebra_generators(self): r""" Return algebra generators for ``self``. This implementation retracts the algebra generators from the ambient algebra. EXAMPLES:: sage: A = FiniteDimensionalAlgebrasWithBasis(QQ).example(); A An example of a finite dimensional algebra with basis: the path algebra of the Kronecker quiver (containing the arrows a:x->y and b:x->y) over Rational Field sage: S = A.semisimple_quotient() sage: S.algebra_generators() Finite family {'y': B['y'], 'x': B['x'], 'b': 0, 'a': 0} .. TODO:: this could possibly remove the elements that retract to zero. """ return self.ambient().algebra_generators().map(self.retract) class CartesianProducts(CartesianProductsCategory): """ The category of algebras constructed as Cartesian products of algebras This construction gives the direct product of algebras. See discussion on: - http://groups.google.fr/group/sage-devel/browse_thread/thread/35a72b1d0a2fc77a/348f42ae77a66d16#348f42ae77a66d16 - http://en.wikipedia.org/wiki/Direct_product """ def extra_super_categories(self): """ A Cartesian product of algebras is endowed with a natural algebra structure. EXAMPLES:: sage: C = Algebras(QQ).CartesianProducts() sage: C.extra_super_categories() [Category of algebras over Rational Field] sage: sorted(C.super_categories(), key=str) [Category of Cartesian products of distributive magmas and additive magmas, Category of Cartesian products of monoids, Category of Cartesian products of vector spaces over Rational Field, Category of algebras over Rational Field] """ return [self.base_category()] class TensorProducts(TensorProductsCategory): @cached_method def extra_super_categories(self): """ EXAMPLES:: sage: Algebras(QQ).TensorProducts().extra_super_categories() [Category of algebras over Rational Field] sage: Algebras(QQ).TensorProducts().super_categories() [Category of algebras over Rational Field, Category of tensor products of vector spaces over Rational Field] Meaning: a tensor product of algebras is an algebra """ return [self.base_category()] class ParentMethods: #def coproduct(self): # tensor products of morphisms are not yet implemented # return tensor(module.coproduct for module in self.modules) pass class ElementMethods: pass class DualObjects(DualObjectsCategory): def extra_super_categories(self): r""" Returns the dual category EXAMPLES: The category of algebras over the Rational Field is dual to the category of coalgebras over the same field:: sage: C = Algebras(QQ) sage: C.dual() Category of duals of algebras over Rational Field sage: C.dual().extra_super_categories() [Category of coalgebras over Rational Field] .. WARNING:: This is only correct in certain cases (finite dimension, ...). See :trac:`15647`. """ from sage.categories.coalgebras import Coalgebras return [Coalgebras(self.base_category().base_ring())]
class EnumeratedSets(Category_singleton): """ The category of enumerated sets An *enumerated set* is a *finite* or *countable* set or multiset `S` together with a canonical enumeration of its elements; conceptually, this is very similar to an immutable list. The main difference lies in the names and the return type of the methods, and of course the fact that the list of elements is not supposed to be expanded in memory. Whenever possible one should use one of the two sub-categories :class:`FiniteEnumeratedSets` or :class:`InfiniteEnumeratedSets`. The purpose of this category is threefold: - to fix a common interface for all these sets; - to provide a bunch of default implementations; - to provide consistency tests. The standard methods for an enumerated set ``S`` are: - ``S.cardinality()``: the number of elements of the set. This is the equivalent for ``len`` on a list except that the return value is specified to be a Sage :class:`Integer` or ``infinity``, instead of a Python ``int``. - ``iter(S)``: an iterator for the elements of the set; - ``S.list()``: the list of the elements of the set, when possible; raises a NotImplementedError if the list is predictably too large to be expanded in memory. - ``S.unrank(n)``: the ``n-th`` element of the set when ``n`` is a sage ``Integer``. This is the equivalent for ``l[n]`` on a list. - ``S.rank(e)``: the position of the element ``e`` in the set; This is equivalent to ``l.index(e)`` for a list except that the return value is specified to be a Sage :class:`Integer`, instead of a Python ``int``. - ``S.first()``: the first object of the set; it is equivalent to ``S.unrank(0)``. - ``S.next(e)``: the object of the set which follows ``e``; It is equivalent to ``S.unrank(S.rank(e)+1)``. - ``S.random_element()``: a random generator for an element of the set. Unless otherwise stated, and for finite enumerated sets, the probability is uniform. For examples and tests see: - ``FiniteEnumeratedSets().example()`` - ``InfiniteEnumeratedSets().example()`` EXAMPLES:: sage: EnumeratedSets() Category of enumerated sets sage: EnumeratedSets().super_categories() [Category of sets] sage: EnumeratedSets().all_super_categories() [Category of enumerated sets, Category of sets, Category of sets with partial maps, Category of objects] TESTS:: sage: C = EnumeratedSets() sage: TestSuite(C).run() """ def super_categories(self): """ EXAMPLES:: sage: EnumeratedSets().super_categories() [Category of sets] """ return [Sets()] def additional_structure(self): """ Return ``None``. Indeed, morphisms of enumerated sets are not required to preserve the enumeration. .. SEEALSO:: :meth:`Category.additional_structure` EXAMPLES:: sage: EnumeratedSets().additional_structure() """ return None def _call_(self, X): """ Construct an object in this category from the data in ``X``. EXAMPLES:: sage: EnumeratedSets()(Primes()) Set of all prime numbers: 2, 3, 5, 7, ... For now, lists, tuples, sets, Sets are coerced into finite enumerated sets:: sage: S = EnumeratedSets()([1, 2, 3]); S {1, 2, 3} sage: S.category() Category of facade finite enumerated sets sage: S = EnumeratedSets()((1, 2, 3)); S {1, 2, 3} sage: S = EnumeratedSets()(set([1, 2, 3])); S {1, 2, 3} sage: S = EnumeratedSets()(Set([1, 2, 3])); S {1, 2, 3} sage: S.category() Category of facade finite enumerated sets """ import sage.sets.set if isinstance(X, (tuple, list, set, sage.sets.set.Set_object_enumerated)): return sage.sets.all.FiniteEnumeratedSet(X) raise NotImplementedError class ParentMethods: def __iter__(self): """ An iterator for the enumerated set. ``iter(self)`` allows the combinatorial class to be treated as an iterable. This is the default implementation from the category ``EnumeratedSets()``; it just goes through the iterator of the set to count the number of objects. By decreasing order of priority, the second column of the following array shows which method is used to define ``__iter__``, when the methods of the first column are overloaded: +------------------------+---------------------------------+ | Needed methods | Default ``__iterator`` provided | +========================+=================================+ | ``first`` and ``next`` | ``_iterator_from_next`` | +------------------------+---------------------------------+ | ``unrank`` | ``_iterator_from_unrank`` | +------------------------+---------------------------------+ | ``list` | ``_iterator_from_next`` | +------------------------+---------------------------------+ If none of these are provided, raise a ``NotImplementedError``. EXAMPLES:: We start with an example where nothing is implemented:: sage: class broken(UniqueRepresentation, Parent): ....: def __init__(self): ....: Parent.__init__(self, category = EnumeratedSets()) ....: sage: it = iter(broken()); [next(it), next(it), next(it)] Traceback (most recent call last): ... NotImplementedError: iterator called but not implemented Here is what happens when ``first`` and ``next`` are implemented:: sage: class set_first_next(UniqueRepresentation, Parent): ....: def __init__(self): ....: Parent.__init__(self, category = EnumeratedSets()) ....: def first(self): ....: return 0 ....: def next(self, elt): ....: return elt+1 ....: sage: it = iter(set_first_next()); [next(it), next(it), next(it)] [0, 1, 2] Let us try with ``unrank``:: sage: class set_unrank(UniqueRepresentation, Parent): ....: def __init__(self): ....: Parent.__init__(self, category = EnumeratedSets()) ....: def unrank(self, i): ....: return i + 5 ....: sage: it = iter(set_unrank()); [next(it), next(it), next(it)] [5, 6, 7] Let us finally try with ``list``:: sage: class set_list(UniqueRepresentation, Parent): ....: def __init__(self): ....: Parent.__init__(self, category = EnumeratedSets()) ....: def list(self): ....: return [5, 6, 7] ....: sage: it = iter(set_list()); [next(it), next(it), next(it)] [5, 6, 7] """ # Check if .first() and .next(x) are overridden in the subclass if (self.first != self._first_from_iterator and self.next != self._next_from_iterator): return self._iterator_from_next() #Check to see if .unrank() is overridden in the subclass elif self.unrank != self._unrank_from_iterator: return self._iterator_from_unrank() #Finally, check to see if .list() is overridden in the subclass elif self.list != self._list_default: return self._iterator_from_list() else: raise NotImplementedError( "iterator called but not implemented") def is_empty(self): r""" Return whether this set is empty. EXAMPLES:: sage: F = FiniteEnumeratedSet([1,2,3]) sage: F.is_empty() False sage: F = FiniteEnumeratedSet([]) sage: F.is_empty() True TESTS:: sage: F.is_empty.__module__ 'sage.categories.enumerated_sets' """ try: next(iter(self)) except StopIteration: return True else: return False def list(self): """ Return an error since the cardinality of ``self`` is not known. EXAMPLES:: sage: class broken(UniqueRepresentation, Parent): ... def __init__(self): ... Parent.__init__(self, category = EnumeratedSets()) ... sage: broken().list() Traceback (most recent call last): ... NotImplementedError: unknown cardinality """ raise NotImplementedError("unknown cardinality") _list_default = list # needed by the check system. def _first_from_iterator(self): """ The "first" element of ``self``. ``self.first()`` returns the first element of the set ``self``. This is a generic implementation from the category ``EnumeratedSets()`` which can be used when the method ``__iter__`` is provided. EXAMPLES:: sage: C = FiniteEnumeratedSets().example() sage: C.first() # indirect doctest 1 """ return next(iter(self)) first = _first_from_iterator def _next_from_iterator(self, obj): """ The "next" element after ``obj`` in ``self``. ``self.next(e)`` returns the element of the set ``self`` which follows ``e``. This is a generic implementation from the category ``EnumeratedSets()`` which can be used when the method ``__iter__`` is provided. Remark: this is the default (brute force) implementation of the category ``EnumeratedSets()``. Its complexity is `O(r)`, where `r` is the rank of ``obj``. EXAMPLES:: sage: C = InfiniteEnumeratedSets().example() sage: C._next_from_iterator(10) # indirect doctest 11 TODO: specify the behavior when ``obj`` is not in ``self``. """ it = iter(self) el = next(it) while el != obj: el = next(it) return next(it) next = _next_from_iterator def _unrank_from_iterator(self, r): """ The ``r``-th element of ``self`` ``self.unrank(r)`` returns the ``r``-th element of self, where ``r`` is an integer between ``0`` and ``n-1`` where ``n`` is the cardinality of ``self``. This is the default (brute force) implementation from the category ``EnumeratedSets()`` which can be used when the method ``__iter__`` is provided. Its complexity is `O(r)`, where `r` is the rank of ``obj``. EXAMPLES:: sage: C = FiniteEnumeratedSets().example() sage: C.unrank(2) # indirect doctest 3 sage: C._unrank_from_iterator(5) Traceback (most recent call last): ... ValueError: the value must be between 0 and 2 inclusive """ counter = 0 for u in self: if counter == r: return u counter += 1 raise ValueError("the value must be between %s and %s inclusive" % (0, counter - 1)) unrank = _unrank_from_iterator def _rank_from_iterator(self, x): """ The rank of an element of ``self`` ``self.rank(x)`` returns the rank of `x`, that is its position in the enumeration of ``self``. This is an integer between ``0`` and ``n-1`` where ``n`` is the cardinality of ``self``, or None if `x` is not in `self`. This is the default (brute force) implementation from the category ``EnumeratedSets()`` which can be used when the method ``__iter__`` is provided. Its complexity is `O(r)`, where `r` is the rank of ``obj``. For infinite enumerated sets, this won't terminate when `x` is not in ``self`` EXAMPLES:: sage: C = FiniteEnumeratedSets().example() sage: list(C) [1, 2, 3] sage: C.rank(3) # indirect doctest 2 sage: C.rank(5) # indirect doctest """ counter = 0 for u in self: if u == x: return counter counter += 1 return None rank = _rank_from_iterator def _iterator_from_list(self): """ An iterator for the elements of ``self``. ``iter(self)`` returns an iterator for the elements of ``self``. This is a generic implementation from the category ``EnumeratedSets()`` which can be used when the method ``list`` is provided. EXAMPLES:: sage: C = FiniteEnumeratedSets().example() sage: it = C._iterator_from_list() sage: [next(it), next(it), next(it)] [1, 2, 3] """ for x in self.list(): yield x def _iterator_from_next(self): """ An iterator for the elements of ``self``. ``iter(self)`` returns an iterator for the element of the set ``self``. This is a generic implementation from the category ``EnumeratedSets()`` which can be used when the methods ``first`` and ``next`` are provided. EXAMPLES:: sage: C = InfiniteEnumeratedSets().example() sage: it = C._iterator_from_next() sage: [next(it), next(it), next(it), next(it), next(it)] [0, 1, 2, 3, 4] """ f = self.first() yield f while True: try: f = self.next(f) except (TypeError, ValueError): break if f is None or f is False: break else: yield f def _iterator_from_unrank(self): """ An iterator for the elements of ``self``. ``iter(self)`` returns an iterator for the elements of the set ``self``. This is a generic implementation from the category ``EnumeratedSets()`` which can be used when the method ``unrank`` is provided. EXAMPLES:: sage: C = InfiniteEnumeratedSets().example() sage: it = C._iterator_from_unrank() sage: [next(it), next(it), next(it), next(it), next(it)] [0, 1, 2, 3, 4] """ r = 0 try: u = self.unrank(r) except (TypeError, ValueError, IndexError): return yield u while True: r += 1 try: u = self.unrank(r) except (TypeError, ValueError, IndexError): break if u is None: break else: yield u # This @cached_method is not really needed, since the method # an_element itself is cached. We leave it for the moment, so # that Parents that do not yet inherit properly from categories # (e.g. Set([1,2,3]) can use the following trick: # _an_element_ = EnumeratedSets.ParentMethods._an_element_ @cached_method def _an_element_from_iterator(self): """ Return the first element of ``self`` returned by :meth:`__iter__` If ``self`` is empty, the exception :class:`~sage.categories.sets_cat.EmptySetError` is raised instead. This provides a generic implementation of the method :meth:`_an_element_` for all parents in :class:`EnumeratedSets`. EXAMPLES:: sage: C = FiniteEnumeratedSets().example(); C An example of a finite enumerated set: {1,2,3} sage: C.an_element() # indirect doctest 1 sage: S = Set([]) sage: S.an_element() Traceback (most recent call last): ... EmptySetError TESTS:: sage: super(Parent, C)._an_element_ Cached version of <function _an_element_from_iterator at ...> """ it = iter(self) try: return next(it) except StopIteration: raise EmptySetError # Should this be implemented from first instead? _an_element_ = _an_element_from_iterator #FIXME: use combinatorial_class_from_iterator once class_from_iterator.patch is in def _some_elements_from_iterator(self): """ Return some elements in ``self``. See :class:`TestSuite` for a typical use case. This is a generic implementation from the category ``EnumeratedSets()`` which can be used when the method ``__iter__`` is provided. It returns an iterator for up to the first 100 elements of ``self`` EXAMPLES:: sage: C = FiniteEnumeratedSets().example() sage: list(C.some_elements()) # indirect doctest [1, 2, 3] """ nb = 0 for i in self: yield i nb += 1 if nb >= 100: break some_elements = _some_elements_from_iterator def random_element(self): """ Return a random element in ``self``. Unless otherwise stated, and for finite enumerated sets, the probability is uniform. This is a generic implementation from the category ``EnumeratedSets()``. It raise a ``NotImplementedError`` since one does not know whether the set is finite. EXAMPLES:: sage: class broken(UniqueRepresentation, Parent): ... def __init__(self): ... Parent.__init__(self, category = EnumeratedSets()) ... sage: broken().random_element() Traceback (most recent call last): ... NotImplementedError: unknown cardinality """ raise NotImplementedError("unknown cardinality") def map(self, f, name=None): r""" Return the image `\{f(x) | x \in \text{self}\}` of this enumerated set by `f`, as an enumerated set. `f` is supposed to be injective. EXAMPLES:: sage: R = SymmetricGroup(3).map(attrcall('reduced_word')); R Image of Symmetric group of order 3! as a permutation group by *.reduced_word() sage: R.cardinality() 6 sage: R.list() [[], [1], [2, 1], [1, 2], [2], [1, 2, 1]] sage: [ r for r in R] [[], [1], [2, 1], [1, 2], [2], [1, 2, 1]] .. warning:: If the function is not injective, then there may be repeated elements:: sage: P = SymmetricGroup(3) sage: P.list() [(), (1,2), (1,2,3), (1,3,2), (2,3), (1,3)] sage: P.map(attrcall('length')).list() [0, 1, 2, 2, 1, 3] .. warning:: :class:`MapCombinatorialClass` needs to be refactored to use categories:: sage: R.category() # todo: not implemented Category of enumerated sets sage: TestSuite(R).run(skip=['_test_an_element', '_test_category', '_test_some_elements']) """ from sage.combinat.combinat import MapCombinatorialClass return MapCombinatorialClass(self, f, name) # # Consistency test suite for an enumerated set: # def _test_enumerated_set_contains(self, **options): """ Checks that the methods :meth:`.__contains__` and :meth:`.__iter__` are consistent. See also :class:`TestSuite`. TESTS:: sage: C = FiniteEnumeratedSets().example() sage: C._test_enumerated_set_contains() sage: TestSuite(C).run() Let us now break the class:: sage: from sage.categories.examples.finite_enumerated_sets import Example sage: class CCls(Example): ... def __contains__(self, obj): ... if obj == 3: ... return False ... else: ... return obj in C sage: CC = CCls() sage: CC._test_enumerated_set_contains() Traceback (most recent call last): ... AssertionError: False is not true """ tester = self._tester(**options) i = 0 for w in self: tester.assertTrue(w in self) i += 1 if i > tester._max_runs: return def _test_enumerated_set_iter_list(self, **options): """ Checks that the methods :meth:`.list` and :meth:`.__iter__` are consistent. See also: :class:`TestSuite`. .. NOTE:: This test does nothing if the cardinality of the set is larger than the max_runs argument. EXAMPLES:: sage: C = FiniteEnumeratedSets().example() sage: C._test_enumerated_set_iter_list() sage: TestSuite(C).run() Let us now break the class:: sage: from sage.categories.examples.finite_enumerated_sets import Example sage: class CCls(Example): ... def list(self): ... return [1,2,3,4] sage: CC = CCls() sage: CC._test_enumerated_set_iter_list() Traceback (most recent call last): ... AssertionError: 3 != 4 For a large enumerated set this test does nothing: increase tester._max_runs if you want to actually run the test:: sage: class CCls(Example): ... def list(self): ... return [1,2,3] sage: CC = CCls() sage: CC._test_enumerated_set_iter_list(verbose=True,max_runs=2) Enumerated set too big; skipping test; increase tester._max_runs """ tester = self._tester(**options) if self.list != self._list_default: # TODO: if self._cardinality is self._cardinality_from_iterator # we could make sure to stop the counting at # self.max_test_enumerated_set_loop if self.cardinality() > tester._max_runs: tester.info( "Enumerated set too big; skipping test; increase tester._max_runs" ) return ls = self.list() i = 0 for obj in self: tester.assertEqual(obj, ls[i]) i += 1 tester.assertEqual(i, len(ls)) class ElementMethods: def rank(self): """ Return the rank of ``self`` in its parent. See also :meth:`EnumeratedSets.ElementMethods.rank` EXAMPLES:: sage: F = FiniteSemigroups().example(('a','b','c')) sage: L = list(F); L ['a', 'b', 'c', 'ac', 'ab', 'ba', 'bc', 'cb', 'ca', 'acb', 'abc', 'bca', 'cba', 'bac', 'cab'] sage: L[7].rank() 7 """ return self.parent().rank(self) Finite = LazyImport('sage.categories.finite_enumerated_sets', 'FiniteEnumeratedSets', at_startup=True) Infinite = LazyImport('sage.categories.infinite_enumerated_sets', 'InfiniteEnumeratedSets', at_startup=True) class CartesianProducts(CartesianProductsCategory): class ParentMethods: def first(self): r""" Return the first element. EXAMPLES:: sage: cartesian_product([ZZ]*10).first() (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) """ return self._cartesian_product_of_elements( tuple(c.first() for c in self.cartesian_factors()))
class Monoids(CategoryWithAxiom): r""" The category of (multiplicative) monoids. A *monoid* is a unital :class:`semigroup <Semigroups>`, that is a set endowed with a multiplicative binary operation `*` which is associative and admits a unit (see :wikipedia:`Monoid`). EXAMPLES:: sage: Monoids() Category of monoids sage: Monoids().super_categories() [Category of semigroups, Category of unital magmas] sage: Monoids().all_super_categories() [Category of monoids, Category of semigroups, Category of unital magmas, Category of magmas, Category of sets, Category of sets with partial maps, Category of objects] sage: Monoids().axioms() frozenset({'Associative', 'Unital'}) sage: Semigroups().Unital() Category of monoids sage: Monoids().example() An example of a monoid: the free monoid generated by ('a', 'b', 'c', 'd') TESTS:: sage: C = Monoids() sage: TestSuite(C).run() """ _base_category_class_and_axiom = (Semigroups, "Unital") Finite = LazyImport('sage.categories.finite_monoids', 'FiniteMonoids', at_startup=True) Inverse = LazyImport('sage.categories.groups', 'Groups', at_startup=True) @staticmethod def free(index_set=None, names=None, **kwds): r""" Return a free monoid on `n` generators or with the generators indexed by a set `I`. A free monoid is constructed by specifing either: - the number of generators and/or the names of the generators - the indexing set for the generators INPUT: - ``index_set`` -- (optional) an index set for the generators; if an integer, then this represents `\{0, 1, \ldots, n-1\}` - ``names`` -- a string or list/tuple/iterable of strings (default: ``'x'``); the generator names or name prefix EXAMPLES:: sage: Monoids.free(index_set=ZZ) Free monoid indexed by Integer Ring sage: Monoids().free(ZZ) Free monoid indexed by Integer Ring sage: F.<x,y,z> = Monoids().free(); F Free monoid indexed by {'x', 'y', 'z'} """ if names is not None: if isinstance(names, str): from sage.rings.all import ZZ if ',' not in names and index_set in ZZ: names = [names + repr(i) for i in range(index_set)] else: names = names.split(',') names = tuple(names) if index_set is None: index_set = names from sage.monoids.indexed_free_monoid import IndexedFreeMonoid return IndexedFreeMonoid(index_set, names=names, **kwds) class ParentMethods: def one_element(self): r""" Backward compatibility alias for :meth:`one`. TESTS:: sage: S = Monoids().example() sage: S.one_element() doctest:...: DeprecationWarning: .one_element() is deprecated. Please use .one() instead. See http://trac.sagemath.org/17694 for details. '' """ from sage.misc.superseded import deprecation deprecation(17694, ".one_element() is deprecated. Please use .one() instead.") return self.one() def semigroup_generators(self): """ Return the generators of ``self`` as a semigroup. The generators of a monoid `M` as a semigroup are the generators of `M` as a monoid and the unit. EXAMPLES:: sage: M = Monoids().free([1,2,3]) sage: M.semigroup_generators() Family (1, F[1], F[2], F[3]) """ G = self.monoid_generators() from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets if G not in FiniteEnumeratedSets(): raise NotImplementedError("currently only implemented for finitely generated monoids") from sage.sets.family import Family return Family((self.one(),) + tuple(G)) def prod(self, args): r""" n-ary product of elements of ``self``. INPUT: - ``args`` -- a list (or iterable) of elements of ``self`` Returns the product of the elements in ``args``, as an element of ``self``. EXAMPLES:: sage: S = Monoids().example() sage: S.prod([S('a'), S('b')]) 'ab' """ return prod(args, self.one()) def _test_prod(self, **options): r""" Run basic tests for the product method :meth:`prod` of ``self``. See the documentation for :class:`TestSuite` for information on further options. INPUT: - ``options`` -- any keyword arguments accepted by :meth:`_tester` EXAMPLES: By default, this method tests only the elements returned by ``self.some_elements()``:: sage: S = Monoids().example() sage: S._test_prod() However, the elements tested can be customized with the ``elements`` keyword argument:: sage: S._test_prod(elements = (S('a'), S('b'))) """ tester = self._tester(**options) tester.assert_(self.prod([]) == self.one()) for x in tester.some_elements(): tester.assert_(self.prod([x]) == x) tester.assert_(self.prod([x, x]) == x**2) tester.assert_(self.prod([x, x, x]) == x**3) def submonoid(self, generators, category=None): r""" Return the multiplicative submonoid generated by ``generators``. INPUT: - ``generators`` -- a finite family of elements of ``self``, or a list, iterable, ... that can be converted into one (see :class:`Family`). - ``category`` -- a category This is a shorthand for :meth:`Semigroups.ParentMethods.subsemigroup` that specifies that this is a submonoid, and in particular that the unit is ``self.one()``. EXAMPLES:: sage: R = IntegerModRing(15) sage: M = R.submonoid([R(3),R(5)]); M A submonoid of (Ring of integers modulo 15) with 2 generators sage: M.list() [1, 3, 5, 9, 0, 10, 12, 6] Not the presence of the unit, unlike in:: sage: S = R.subsemigroup([R(3),R(5)]); S A subsemigroup of (Ring of integers modulo 15) with 2 generators sage: S.list() [3, 5, 9, 0, 10, 12, 6] This method is really a shorthand for subsemigroup:: sage: M2 = R.subsemigroup([R(3),R(5)], one=R.one()) sage: M2 is M True """ return self.subsemigroup(generators, one=self.one()) class ElementMethods: def is_one(self): r""" Return whether ``self`` is the one of the monoid. The default implementation is to compare with ``self.one()``. TESTS:: sage: S = Monoids().example() sage: S.one().is_one() True sage: S("aa").is_one() False """ return self == self.parent().one() def __pow__(self, n): r""" Return ``self`` to the `n^{th}` power. INPUT: - ``n`` -- a nonnegative integer EXAMPLES:: sage: S = Monoids().example() sage: x = S("aa") sage: x^0, x^1, x^2, x^3, x^4, x^5 ('', 'aa', 'aaaa', 'aaaaaa', 'aaaaaaaa', 'aaaaaaaaaa') """ if not n: # FIXME: why do we need to do that? return self.parent().one() return generic_power(self, n, self.parent().one()) def _pow_naive(self, n): r""" Return ``self`` to the `n^{th}` power (naive implementation). INPUT: - ``n`` -- a nonnegative integer This naive implementation does not use binary exponentiation; there are cases where this is actually faster due to size explosion. EXAMPLES:: sage: S = Monoids().example() sage: x = S("aa") sage: [x._pow_naive(i) for i in range(6)] ['', 'aa', 'aaaa', 'aaaaaa', 'aaaaaaaa', 'aaaaaaaaaa'] """ if not n: return self.parent().one() result = self for i in range(n - 1): result *= self return result def powers(self, n): r""" Return the list `[x^0, x^1, \ldots, x^{n-1}]`. EXAMPLES:: sage: A = Matrix([[1, 1], [-1, 0]]) sage: A.powers(6) [ [1 0] [ 1 1] [ 0 1] [-1 0] [-1 -1] [ 0 -1] [0 1], [-1 0], [-1 -1], [ 0 -1], [ 1 0], [ 1 1] ] """ if n < 0: raise ValueError("negative number of powers requested") elif n == 0: return [] x = self.parent().one() l = [x] for i in range(n - 1): x = x * self l.append(x) return l class Commutative(CategoryWithAxiom): """ Category of commutative (abelian) monoids. A monoid `M` is *commutative* if `xy = yx` for all `x,y \in M`. """ @staticmethod def free(index_set=None, names=None, **kwds): r""" Return a free abelian monoid on `n` generators or with the generators indexed by a set `I`. A free monoid is constructed by specifing either: - the number of generators and/or the names of the generators, or - the indexing set for the generators. INPUT: - ``index_set`` -- (optional) an index set for the generators; if an integer, then this represents `\{0, 1, \ldots, n-1\}` - ``names`` -- a string or list/tuple/iterable of strings (default: ``'x'``); the generator names or name prefix EXAMPLES:: sage: Monoids.Commutative.free(index_set=ZZ) Free abelian monoid indexed by Integer Ring sage: Monoids().Commutative().free(ZZ) Free abelian monoid indexed by Integer Ring sage: F.<x,y,z> = Monoids().Commutative().free(); F Free abelian monoid indexed by {'x', 'y', 'z'} """ if names is not None: if isinstance(names, str): from sage.rings.all import ZZ if ',' not in names and index_set in ZZ: names = [names + repr(i) for i in range(index_set)] else: names = names.split(',') names = tuple(names) if index_set is None: index_set = names from sage.monoids.indexed_free_monoid import IndexedFreeAbelianMonoid return IndexedFreeAbelianMonoid(index_set, names=names, **kwds) class WithRealizations(WithRealizationsCategory): class ParentMethods: def one(self): r""" Return the unit of this monoid. This default implementation returns the unit of the realization of ``self`` given by :meth:`~Sets.WithRealizations.ParentMethods.a_realization`. EXAMPLES:: sage: A = Sets().WithRealizations().example(); A The subset algebra of {1, 2, 3} over Rational Field sage: A.one.__module__ 'sage.categories.monoids' sage: A.one() F[{}] TESTS:: sage: A.one() is A.a_realization().one() True sage: A._test_one() """ return self.a_realization().one() class Subquotients(SubquotientsCategory): class ParentMethods: def one(self): """ Returns the multiplicative unit of this monoid, obtained by retracting that of the ambient monoid. EXAMPLES:: sage: S = Monoids().Subquotients().example() # todo: not implemented sage: S.one() # todo: not implemented """ return self.retract(self.ambient().one()) class Algebras(AlgebrasCategory): def extra_super_categories(self): """ EXAMPLES:: sage: Monoids().Algebras(QQ).extra_super_categories() [Category of monoids] sage: Monoids().Algebras(QQ).super_categories() [Category of algebras with basis over Rational Field, Category of semigroup algebras over Rational Field, Category of unital magma algebras over Rational Field] """ return [Monoids()] class ParentMethods: @cached_method def one_basis(self): """ Return the unit of the monoid, which indexes the unit of this algebra, as per :meth:`AlgebrasWithBasis.ParentMethods.one_basis() <sage.categories.algebras_with_basis.AlgebrasWithBasis.ParentMethods.one_basis>`. EXAMPLES:: sage: A = Monoids().example().algebra(ZZ) sage: A.one_basis() '' sage: A.one() B[''] sage: A(3) 3*B[''] """ return self.basis().keys().one() @cached_method def algebra_generators(self): r""" Return generators for this algebra. For a monoid algebra, the algebra generators are built from the monoid generators if available and from the semigroup generators otherwise. .. SEEALSO:: - :meth:`Semigroups.Algebras.ParentMethods.algebra_generators` - :meth:`MagmaticAlgebras.ParentMethods.algebra_generators() <.magmatic_algebras.MagmaticAlgebras.ParentMethods.algebra_generators>`. EXAMPLES:: sage: M = Monoids().example(); M An example of a monoid: the free monoid generated by ('a', 'b', 'c', 'd') sage: M.monoid_generators() Finite family {'a': 'a', 'c': 'c', 'b': 'b', 'd': 'd'} sage: M.algebra(ZZ).algebra_generators() Finite family {'a': B['a'], 'c': B['c'], 'b': B['b'], 'd': B['d']} sage: Z12 = Monoids().Finite().example(); Z12 An example of a finite multiplicative monoid: the integers modulo 12 sage: Z12.monoid_generators() Traceback (most recent call last): ... AttributeError: 'IntegerModMonoid_with_category' object has no attribute 'monoid_generators' sage: Z12.semigroup_generators() Family (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) sage: Z12.algebra(QQ).algebra_generators() Finite family {0: B[0], 1: B[1], 2: B[2], 3: B[3], 4: B[4], 5: B[5], 6: B[6], 7: B[7], 8: B[8], 9: B[9], 10: B[10], 11: B[11]} """ monoid = self.basis().keys() try: generators = monoid.monoid_generators() except AttributeError: generators = monoid.semigroup_generators() return generators.map(self.monomial) class ElementMethods: def is_central(self): r""" Return whether the element ``self`` is central. EXAMPLES:: sage: SG4=SymmetricGroupAlgebra(ZZ,4) sage: SG4(1).is_central() True sage: SG4(Permutation([1,3,2,4])).is_central() False sage: A=GroupAlgebras(QQ).example(); A Group algebra of Dihedral group of order 8 as a permutation group over Rational Field sage: sum(i for i in A.basis()).is_central() True """ return all([i*self == self*i for i in self.parent().algebra_generators()]) class CartesianProducts(CartesianProductsCategory): """ The category of monoids constructed as Cartesian products of monoids. This construction gives the direct product of monoids. See :wikipedia:`Direct_product` for more information. """ def extra_super_categories(self): """ A Cartesian product of monoids is endowed with a natural group structure. EXAMPLES:: sage: C = Monoids().CartesianProducts() sage: C.extra_super_categories() [Category of monoids] sage: sorted(C.super_categories(), key=str) [Category of Cartesian products of semigroups, Category of Cartesian products of unital magmas, Category of monoids] """ return [self.base_category()] class ParentMethods: @cached_method def monoid_generators(self): """ Return the generators of ``self``. EXAMPLES:: sage: M = Monoids.free([1,2,3]) sage: N = Monoids.free(['a','b']) sage: C = cartesian_product([M, N]) sage: C.monoid_generators() Family ((F[1], 1), (F[2], 1), (F[3], 1), (1, F['a']), (1, F['b'])) An example with an infinitely generated group (a better output is needed):: sage: N = Monoids.free(ZZ) sage: C = cartesian_product([M, N]) sage: C.monoid_generators() Lazy family (gen(i))_{i in The Cartesian product of (...)} """ F = self.cartesian_factors() ids = tuple(M.one() for M in F) def lift(i, gen): cur = list(ids) cur[i] = gen return self._cartesian_product_of_elements(cur) from sage.sets.family import Family # Finitely generated cat = FiniteEnumeratedSets() if all(M.monoid_generators() in cat or isinstance(M.monoid_generators(), (tuple, list)) for M in F): ret = [lift(i, gen) for i,M in enumerate(F) for gen in M.monoid_generators()] return Family(ret) # Infinitely generated # This does not return a good output, but it is "correct" # TODO: Figure out a better way to do things from sage.categories.cartesian_product import cartesian_product gens_prod = cartesian_product([Family(M.monoid_generators(), lambda g: (i, g)) for i,M in enumerate(F)]) return Family(gens_prod, lift, name="gen")
class LieAlgebras(Category_over_base_ring): """ The category of Lie algebras. EXAMPLES:: sage: C = LieAlgebras(QQ); C Category of Lie algebras over Rational Field sage: sorted(C.super_categories(), key=str) [Category of vector spaces over Rational Field] We construct a typical parent in this category, and do some computations with it:: sage: A = C.example(); A An example of a Lie algebra: the Lie algebra from the associative algebra Symmetric group algebra of order 3 over Rational Field generated by ([2, 1, 3], [2, 3, 1]) sage: A.category() Category of Lie algebras over Rational Field sage: A.base_ring() Rational Field sage: a,b = A.lie_algebra_generators() sage: a.bracket(b) -[1, 3, 2] + [3, 2, 1] sage: b.bracket(2*a + b) 2*[1, 3, 2] - 2*[3, 2, 1] sage: A.bracket(a, b) -[1, 3, 2] + [3, 2, 1] Please see the source code of `A` (with ``A??``) for how to implement other Lie algebras. TESTS:: sage: C = LieAlgebras(QQ) sage: TestSuite(C).run() sage: TestSuite(C.example()).run() .. TODO:: Many of these tests should use Lie algebras that are not the minimal example and need to be added after :trac:`16820` (and :trac:`16823`). """ @cached_method def super_categories(self): """ EXAMPLES:: sage: LieAlgebras(QQ).super_categories() [Category of vector spaces over Rational Field] """ # We do not also derive from (Magmatic) algebras since we don't want * # to be our Lie bracket # Also this doesn't inherit the ability to add axioms like Associative # and Unital, both of which do not make sense for Lie algebras return [Modules(self.base_ring())] class SubcategoryMethods: def Nilpotent(self): r""" Return the full subcategory of nilpotent objects of ``self``. A Lie algebra `L` is nilpotent if there exist an integer `s` such that all iterated brackets of `L` of length more than `s` vanish. The integer `s` is called the nilpotency step. For instance any abelian Lie algebra is nilpotent of step 1. EXAMPLES:: sage: LieAlgebras(QQ).Nilpotent() Category of nilpotent Lie algebras over Rational Field sage: LieAlgebras(QQ).WithBasis().Nilpotent() Category of nilpotent lie algebras with basis over Rational Field """ return self._with_axiom("Nilpotent") Graded = LazyImport('sage.categories.graded_lie_algebras', 'GradedLieAlgebras', as_name='Graded') # TODO: Find some way to do this without copying most of the logic. def _repr_object_names(self): r""" Return the name of the objects of this category. .. SEEALSO:: :meth:`Category._repr_object_names` EXAMPLES:: sage: LieAlgebras(QQ)._repr_object_names() 'Lie algebras over Rational Field' sage: LieAlgebras(Fields())._repr_object_names() 'Lie algebras over fields' sage: from sage.categories.category import JoinCategory sage: from sage.categories.category_with_axiom import Blahs sage: LieAlgebras(JoinCategory((Blahs().Flying(), Fields()))) Category of Lie algebras over (flying unital blahs and fields) """ base = self.base() if isinstance(base, Category): if isinstance(base, JoinCategory): name = '(' + ' and '.join( C._repr_object_names() for C in base.super_categories()) + ')' else: name = base._repr_object_names() else: name = base return "Lie algebras over {}".format(name) def example(self, gens=None): """ Return an example of a Lie algebra as per :meth:`Category.example <sage.categories.category.Category.example>`. EXAMPLES:: sage: LieAlgebras(QQ).example() An example of a Lie algebra: the Lie algebra from the associative algebra Symmetric group algebra of order 3 over Rational Field generated by ([2, 1, 3], [2, 3, 1]) Another set of generators can be specified as an optional argument:: sage: F.<x,y,z> = FreeAlgebra(QQ) sage: LieAlgebras(QQ).example(F.gens()) An example of a Lie algebra: the Lie algebra from the associative algebra Free Algebra on 3 generators (x, y, z) over Rational Field generated by (x, y, z) """ if gens is None: from sage.combinat.symmetric_group_algebra import SymmetricGroupAlgebra from sage.rings.all import QQ gens = SymmetricGroupAlgebra(QQ, 3).algebra_generators() from sage.categories.examples.lie_algebras import Example return Example(gens) WithBasis = LazyImport('sage.categories.lie_algebras_with_basis', 'LieAlgebrasWithBasis', as_name='WithBasis') class FiniteDimensional(CategoryWithAxiom_over_base_ring): WithBasis = LazyImport( 'sage.categories.finite_dimensional_lie_algebras_with_basis', 'FiniteDimensionalLieAlgebrasWithBasis', as_name='WithBasis') def extra_super_categories(self): """ Implements the fact that a finite dimensional Lie algebra over a finite ring is finite. EXAMPLES:: sage: LieAlgebras(IntegerModRing(4)).FiniteDimensional().extra_super_categories() [Category of finite sets] sage: LieAlgebras(ZZ).FiniteDimensional().extra_super_categories() [] sage: LieAlgebras(GF(5)).FiniteDimensional().is_subcategory(Sets().Finite()) True sage: LieAlgebras(ZZ).FiniteDimensional().is_subcategory(Sets().Finite()) False sage: LieAlgebras(GF(5)).WithBasis().FiniteDimensional().is_subcategory(Sets().Finite()) True """ if self.base_ring() in Sets().Finite(): return [Sets().Finite()] return [] class Nilpotent(CategoryWithAxiom_over_base_ring): r""" Category of nilpotent Lie algebras. TESTS:: sage: C = LieAlgebras(QQ).Nilpotent() sage: TestSuite(C).run() """ class ParentMethods: @abstract_method def step(self): r""" Return the nilpotency step of ``self``. EXAMPLES:: sage: h = lie_algebras.Heisenberg(ZZ, oo) sage: h.step() 2 """ def is_nilpotent(self): r""" Return ``True`` since ``self`` is nilpotent. EXAMPLES:: sage: h = lie_algebras.Heisenberg(ZZ, oo) sage: h.is_nilpotent() True """ return True class ParentMethods: #@abstract_method #def lie_algebra_generators(self): # """ # Return the generators of ``self`` as a Lie algebra. # """ # TODO: Move this to LieAlgebraElement, cythonize, and use more standard # coercion framework test (i.e., have_same_parent) def bracket(self, lhs, rhs): """ Return the Lie bracket ``[lhs, rhs]`` after coercing ``lhs`` and ``rhs`` into elements of ``self``. If ``lhs`` and ``rhs`` are Lie algebras, then this constructs the product space, and if only one of them is a Lie algebra, then it constructs the corresponding ideal. EXAMPLES:: sage: L = LieAlgebras(QQ).example() sage: x,y = L.lie_algebra_generators() sage: L.bracket(x, x + y) -[1, 3, 2] + [3, 2, 1] sage: L.bracket(x, 0) 0 sage: L.bracket(0, x) 0 Constructing the product space:: sage: L = lie_algebras.Heisenberg(QQ, 1) sage: Z = L.bracket(L, L); Z Ideal (z) of Heisenberg algebra of rank 1 over Rational Field sage: L.bracket(L, Z) Ideal () of Heisenberg algebra of rank 1 over Rational Field Constructing ideals:: sage: p,q,z = L.basis(); (p,q,z) (p1, q1, z) sage: L.bracket(3*p, L) Ideal (3*p1) of Heisenberg algebra of rank 1 over Rational Field sage: L.bracket(L, q+p) Ideal (p1 + q1) of Heisenberg algebra of rank 1 over Rational Field """ if lhs in LieAlgebras: if rhs in LieAlgebras: return lhs.product_space(rhs) return lhs.ideal(rhs) elif rhs in LieAlgebras: return rhs.ideal(lhs) return self(lhs)._bracket_(self(rhs)) # Do not override this. Instead implement :meth:`_construct_UEA`; # then, :meth:`lift` and :meth:`universal_enveloping_algebra` # will automatically setup the coercion. def universal_enveloping_algebra(self): """ Return the universal enveloping algebra of ``self``. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: L.universal_enveloping_algebra() Noncommutative Multivariate Polynomial Ring in b0, b1, b2 over Rational Field, nc-relations: {} :: sage: L = LieAlgebra(QQ, 3, 'x', abelian=True) sage: L.universal_enveloping_algebra() Multivariate Polynomial Ring in x0, x1, x2 over Rational Field .. SEEALSO:: :meth:`lift` """ return self.lift.codomain() @abstract_method(optional=True) def _construct_UEA(self): """ Return the universal enveloping algebra of ``self``. Unlike :meth:`universal_enveloping_algebra`, this method does not (usually) construct the canonical lift morphism from ``self`` to the universal enveloping algebra (let alone register it as a coercion). One should implement this method and the ``lift`` method for the element class to construct the morphism the universal enveloping algebra. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: L._construct_UEA() Noncommutative Multivariate Polynomial Ring in b0, b1, b2 over Rational Field, nc-relations: {} :: sage: L = LieAlgebra(QQ, 3, 'x', abelian=True) sage: L.universal_enveloping_algebra() # indirect doctest Multivariate Polynomial Ring in x0, x1, x2 over Rational Field """ @abstract_method(optional=True) def module(self): r""" Return an `R`-module which is isomorphic to the underlying `R`-module of ``self``. The rationale behind this method is to enable linear algebraic functionality on ``self`` (such as computing the span of a list of vectors in ``self``) via an isomorphism from ``self`` to an `R`-module (typically, although not always, an `R`-module of the form `R^n` for an `n \in \NN`) on which such functionality already exists. For this method to be of any use, it should return an `R`-module which has linear algebraic functionality that ``self`` does not have. For instance, if ``self`` has ordered basis `(e, f, h)`, then ``self.module()`` will be the `R`-module `R^3`, and the elements `e`, `f` and `h` of ``self`` will correspond to the basis vectors `(1, 0, 0)`, `(0, 1, 0)` and `(0, 0, 1)` of ``self.module()``. This method :meth:`module` needs to be set whenever a finite-dimensional Lie algebra with basis is intended to support linear algebra (which is, e.g., used in the computation of centralizers and lower central series). One then needs to also implement the `R`-module isomorphism from ``self`` to ``self.module()`` in both directions; that is, implement: * a ``to_vector`` ElementMethod which sends every element of ``self`` to the corresponding element of ``self.module()``; * a ``from_vector`` ParentMethod which sends every element of ``self.module()`` to an element of ``self``. The ``from_vector`` method will automatically serve as an element constructor of ``self`` (that is, ``self(v)`` for any ``v`` in ``self.module()`` will return ``self.from_vector(v)``). .. TODO:: Ensure that this is actually so. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: L.module() Vector space of dimension 3 over Rational Field """ @abstract_method(optional=True) def from_vector(self, v): """ Return the element of ``self`` corresponding to the vector ``v`` in ``self.module()``. Implement this if you implement :meth:`module`; see the documentation of the latter for how this is to be done. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: u = L.from_vector(vector(QQ, (1, 0, 0))); u (1, 0, 0) sage: parent(u) is L True """ @lazy_attribute def lift(self): r""" Construct the lift morphism from ``self`` to the universal enveloping algebra of ``self`` (the latter is implemented as :meth:`universal_enveloping_algebra`). This is a Lie algebra homomorphism. It is injective if ``self`` is a free module over its base ring, or if the base ring is a `\QQ`-algebra. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: a, b, c = L.lie_algebra_generators() sage: lifted = L.lift(2*a + b - c); lifted 2*b0 + b1 - b2 sage: lifted.parent() is L.universal_enveloping_algebra() True """ M = LiftMorphism(self, self._construct_UEA()) M.register_as_coercion() return M def subalgebra(self, gens, names=None, index_set=None, category=None): r""" Return the subalgebra of ``self`` generated by ``gens``. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: a, b, c = L.lie_algebra_generators() sage: L.subalgebra([2*a - c, b + c]) An example of a finite dimensional Lie algebra with basis: the 2-dimensional abelian Lie algebra over Rational Field with basis matrix: [ 1 0 -1/2] [ 0 1 1] :: sage: L = LieAlgebras(QQ).example() sage: x,y = L.lie_algebra_generators() sage: L.subalgebra([x + y]) Traceback (most recent call last): ... NotImplementedError: subalgebras not yet implemented: see #17416 """ raise NotImplementedError( "subalgebras not yet implemented: see #17416") #from sage.algebras.lie_algebras.subalgebra import LieSubalgebra #return LieSubalgebra(gens, names, index_set, category) def ideal(self, *gens, **kwds): r""" Return the ideal of ``self`` generated by ``gens``. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: a, b, c = L.lie_algebra_generators() sage: L.ideal([2*a - c, b + c]) An example of a finite dimensional Lie algebra with basis: the 2-dimensional abelian Lie algebra over Rational Field with basis matrix: [ 1 0 -1/2] [ 0 1 1] :: sage: L = LieAlgebras(QQ).example() sage: x,y = L.lie_algebra_generators() sage: L.ideal([x + y]) Traceback (most recent call last): ... NotImplementedError: ideals not yet implemented: see #16824 """ raise NotImplementedError("ideals not yet implemented: see #16824") #from sage.algebras.lie_algebras.ideal import LieIdeal #if len(gens) == 1 and isinstance(gens[0], (list, tuple)): # gens = gens[0] #names = kwds.pop("names", None) #index_set = kwds.pop("index_set", None) #category = kwds.pop("category", None) #return LieIdeal(gens, names, index_set, category) def is_ideal(self, A): """ Return if ``self`` is an ideal of ``A``. EXAMPLES:: sage: L = LieAlgebras(QQ).example() sage: L.is_ideal(L) True """ if A == self: return True raise NotImplementedError("ideals not yet implemented: see #16824") #from sage.algebras.lie_algebras.ideal import LieIdeal #return isinstance(self, LieIdeal) and self._ambient is A @abstract_method(optional=True) def killing_form(self, x, y): """ Return the Killing form of ``x`` and ``y``. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: a, b, c = L.lie_algebra_generators() sage: L.killing_form(a, b+c) 0 """ def is_abelian(self): r""" Return ``True`` if this Lie algebra is abelian. A Lie algebra `\mathfrak{g}` is abelian if `[x, y] = 0` for all `x, y \in \mathfrak{g}`. EXAMPLES:: sage: L = LieAlgebras(QQ).example() sage: L.is_abelian() False sage: R = QQ['x,y'] sage: L = LieAlgebras(QQ).example(R.gens()) sage: L.is_abelian() True :: sage: L.<x> = LieAlgebra(QQ,1) # todo: not implemented - #16823 sage: L.is_abelian() # todo: not implemented - #16823 True sage: L.<x,y> = LieAlgebra(QQ,2) # todo: not implemented - #16823 sage: L.is_abelian() # todo: not implemented - #16823 False """ G = self.lie_algebra_generators() if G not in FiniteEnumeratedSets(): raise NotImplementedError("infinite number of generators") zero = self.zero() return all(x._bracket_(y) == zero for x in G for y in G) def is_commutative(self): """ Return if ``self`` is commutative. This is equivalent to ``self`` being abelian. EXAMPLES:: sage: L = LieAlgebras(QQ).example() sage: L.is_commutative() False :: sage: L.<x> = LieAlgebra(QQ, 1) # todo: not implemented - #16823 sage: L.is_commutative() # todo: not implemented - #16823 True """ return self.is_abelian() @abstract_method(optional=True) def is_solvable(self): """ Return if ``self`` is a solvable Lie algebra. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: L.is_solvable() True """ @abstract_method(optional=True) def is_nilpotent(self): """ Return if ``self`` is a nilpotent Lie algebra. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: L.is_nilpotent() True """ def bch(self, X, Y, prec=None): r""" Return the element `\log(\exp(X)\exp(Y))`. The BCH formula is an expression for `\log(\exp(X)\exp(Y))` as a sum of Lie brackets of ``X ` and ``Y`` with rational coefficients. It is only defined if the base ring of ``self`` has a coercion from the rationals. INPUT: - ``X`` -- an element of ``self`` - ``Y`` -- an element of ``self`` - ``prec`` -- an integer; the maximum length of Lie brackets to be considered in the formula EXAMPLES: The BCH formula for the generators of a free nilpotent Lie algebra of step 4:: sage: L = LieAlgebra(QQ, 2, step=4) sage: L.inject_variables() Defining X_1, X_2, X_12, X_112, X_122, X_1112, X_1122, X_1222 sage: L.bch(X_1, X_2) X_1 + X_2 + 1/2*X_12 + 1/12*X_112 + 1/12*X_122 + 1/24*X_1122 An example of the BCH formula in a quotient:: sage: Q = L.quotient(X_112 + X_122) sage: x, y = Q.basis().list()[:2] sage: Q.bch(x, y) X_1 + X_2 + 1/2*X_12 - 1/24*X_1112 The BCH formula for a non-nilpotent Lie algebra requires the precision to be explicitly stated:: sage: L.<X,Y> = LieAlgebra(QQ) sage: L.bch(X, Y) Traceback (most recent call last): ... ValueError: the Lie algebra is not known to be nilpotent, so you must specify the precision sage: L.bch(X, Y, 4) X + 1/12*[X, [X, Y]] + 1/24*[X, [[X, Y], Y]] + 1/2*[X, Y] + 1/12*[[X, Y], Y] + Y The BCH formula requires a coercion from the rationals:: sage: L.<X,Y,Z> = LieAlgebra(ZZ, 2, step=2) sage: L.bch(X, Y) Traceback (most recent call last): ... TypeError: the BCH formula is not well defined since Integer Ring has no coercion from Rational Field """ if self not in LieAlgebras.Nilpotent and prec is None: raise ValueError( "the Lie algebra is not known to be nilpotent," " so you must specify the precision") from sage.algebras.lie_algebras.bch import bch_iterator if prec is None: return self.sum(Z for Z in bch_iterator(X, Y)) bch = bch_iterator(X, Y) return self.sum(next(bch) for k in range(prec)) baker_campbell_hausdorff = bch @abstract_method(optional=True) def lie_group(self, name='G', **kwds): r""" Return the simply connected Lie group related to ``self``. INPUT: - ``name`` -- string (default: ``'G'``); the name (symbol) given to the Lie group EXAMPLES:: sage: L = lie_algebras.Heisenberg(QQ, 1) sage: G = L.lie_group('G'); G Lie group G of Heisenberg algebra of rank 1 over Rational Field """ def _test_jacobi_identity(self, **options): """ Test that the Jacobi identity is satisfied on (not necessarily all) elements of this set. INPUT: - ``options`` -- any keyword arguments accepted by :meth:`_tester`. EXAMPLES: By default, this method runs the tests only on the elements returned by ``self.some_elements()``:: sage: L = LieAlgebras(QQ).example() sage: L._test_jacobi_identity() However, the elements tested can be customized with the ``elements`` keyword argument:: sage: L = LieAlgebras(QQ).example() sage: x,y = L.lie_algebra_generators() sage: L._test_jacobi_identity(elements=[x+y, x, 2*y, x.bracket(y)]) See the documentation for :class:`TestSuite` for more information. """ tester = self._tester(**options) elts = tester.some_elements() jacobi = lambda x, y, z: self.bracket(x, self.bracket(y, z)) + \ self.bracket(y, self.bracket(z, x)) + \ self.bracket(z, self.bracket(x, y)) zero = self.zero() for x in elts: for y in elts: if x == y: continue for z in elts: tester.assertEqual(jacobi(x, y, z), zero) def _test_antisymmetry(self, **options): """ Test that the antisymmetry axiom is satisfied on (not necessarily all) elements of this set. INPUT: - ``options`` -- any keyword arguments accepted by :meth:`_tester`. EXAMPLES: By default, this method runs the tests only on the elements returned by ``self.some_elements()``:: sage: L = LieAlgebras(QQ).example() sage: L._test_antisymmetry() However, the elements tested can be customized with the ``elements`` keyword argument:: sage: L = LieAlgebras(QQ).example() sage: x,y = L.lie_algebra_generators() sage: L._test_antisymmetry(elements=[x+y, x, 2*y, x.bracket(y)]) See the documentation for :class:`TestSuite` for more information. """ tester = self._tester(**options) elts = tester.some_elements() zero = self.zero() for x in elts: tester.assertEqual(self.bracket(x, x), zero) def _test_distributivity(self, **options): r""" Test the distributivity of the Lie bracket `[,]` on `+` on (not necessarily all) elements of this set. INPUT: - ``options`` -- any keyword arguments accepted by :meth:`_tester`. TESTS:: sage: L = LieAlgebras(QQ).example() sage: L._test_distributivity() EXAMPLES: By default, this method runs the tests only on the elements returned by ``self.some_elements()``:: sage: L = LieAlgebra(QQ, 3, 'x,y,z', representation="polynomial") sage: L.some_elements() [x + y + z] sage: L._test_distributivity() However, the elements tested can be customized with the ``elements`` keyword argument:: sage: L = LieAlgebra(QQ, cartan_type=['A', 2]) # todo: not implemented - #16821 sage: h1 = L.gen(0) # todo: not implemented - #16821 sage: h2 = L.gen(1) # todo: not implemented - #16821 sage: e2 = L.gen(3) # todo: not implemented - #16821 sage: L._test_distributivity(elements=[h1, h2, e2]) # todo: not implemented - #16821 See the documentation for :class:`TestSuite` for more information. """ tester = self._tester(**options) S = tester.some_elements() from sage.misc.misc import some_tuples for x, y, z in some_tuples(S, 3, tester._max_runs): # left distributivity tester.assertEqual(self.bracket(x, (y + z)), self.bracket(x, y) + self.bracket(x, z)) # right distributivity tester.assertEqual(self.bracket((x + y), z), self.bracket(x, z) + self.bracket(y, z)) class ElementMethods: @coerce_binop def bracket(self, rhs): """ Return the Lie bracket ``[self, rhs]``. EXAMPLES:: sage: L = LieAlgebras(QQ).example() sage: x,y = L.lie_algebra_generators() sage: x.bracket(y) -[1, 3, 2] + [3, 2, 1] sage: x.bracket(0) 0 """ return self._bracket_(rhs) # Implement this method to define the Lie bracket. You do not # need to deal with the coercions here. @abstract_method def _bracket_(self, y): """ Return the Lie bracket ``[self, y]``, where ``y`` is an element of the same Lie algebra as ``self``. EXAMPLES:: sage: L = LieAlgebras(QQ).example() sage: x,y = L.lie_algebra_generators() sage: x._bracket_(y) -[1, 3, 2] + [3, 2, 1] sage: y._bracket_(x) [1, 3, 2] - [3, 2, 1] sage: x._bracket_(x) 0 """ @abstract_method(optional=True) def to_vector(self): """ Return the vector in ``g.module()`` corresponding to the element ``self`` of ``g`` (where ``g`` is the parent of ``self``). Implement this if you implement ``g.module()``. See :meth:`LieAlgebras.module` for how this is to be done. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: u = L((1, 0, 0)).to_vector(); u (1, 0, 0) sage: parent(u) Vector space of dimension 3 over Rational Field """ @abstract_method(optional=True) def lift(self): """ Return the image of ``self`` under the canonical lift from the Lie algebra to its universal enveloping algebra. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: a, b, c = L.lie_algebra_generators() sage: elt = 3*a + b - c sage: elt.lift() 3*b0 + b1 - b2 :: sage: L.<x,y> = LieAlgebra(QQ, abelian=True) sage: x.lift() x """ def killing_form(self, x): """ Return the Killing form of ``self`` and ``x``. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: a, b, c = L.lie_algebra_generators() sage: a.killing_form(b) 0 """ return self.parent().killing_form(self, x) def exp(self, lie_group=None): r""" Return the exponential of ``self`` in ``lie_group``. INPUT: - ``lie_group`` -- (optional) the Lie group to map into; If ``lie_group`` is not given, the Lie group associated to the parent Lie algebra of ``self`` is used. EXAMPLES:: sage: L.<X,Y,Z> = LieAlgebra(QQ, 2, step=2) sage: g = (X + Y + Z).exp(); g exp(X + Y + Z) sage: h = X.exp(); h exp(X) sage: g.parent() Lie group G of Free Nilpotent Lie algebra on 3 generators (X, Y, Z) over Rational Field sage: g.parent() is h.parent() True The Lie group can be specified explicitly:: sage: H = L.lie_group('H') sage: k = Z.exp(lie_group=H); k exp(Z) sage: k.parent() Lie group H of Free Nilpotent Lie algebra on 3 generators (X, Y, Z) over Rational Field sage: g.parent() == k.parent() False """ if lie_group is None: lie_group = self.parent().lie_group() return lie_group.exp(self)
class MagmaticAlgebras(Category_over_base_ring): """ The category of algebras over a given base ring. An algebra over a ring `R` is a module over `R` endowed with a bilinear multiplication. .. WARNING:: :class:`MagmaticAlgebras` will eventually replace the current :class:`Algebras` for consistency with e.g. :wikipedia:`Algebras` which assumes neither associativity nor the existence of a unit (see :trac:`15043`). EXAMPLES:: sage: from sage.categories.magmatic_algebras import MagmaticAlgebras sage: C = MagmaticAlgebras(ZZ); C Category of magmatic algebras over Integer Ring sage: C.super_categories() [Category of additive commutative additive associative additive unital distributive magmas and additive magmas, Category of modules over Integer Ring] TESTS:: sage: TestSuite(C).run() """ @cached_method def super_categories(self): """ EXAMPLES:: sage: from sage.categories.magmatic_algebras import MagmaticAlgebras sage: MagmaticAlgebras(ZZ).super_categories() [Category of additive commutative additive associative additive unital distributive magmas and additive magmas, Category of modules over Integer Ring] sage: from sage.categories.additive_semigroups import AdditiveSemigroups sage: MagmaticAlgebras(ZZ).is_subcategory((AdditiveSemigroups() & Magmas()).Distributive()) True """ R = self.base_ring() # Note: The specifications impose `self` to be a subcategory # of the join of its super categories. Here the join is non # trivial, since some of the axioms of Modules (like the # commutativity of '+') are added to the left hand side. We # might want the infrastructure to take this join for us. return Category.join([(Magmas() & AdditiveMagmas()).Distributive(), Modules(R)], as_list=True) Associative = LazyImport('sage.categories.associative_algebras', 'AssociativeAlgebras', at_startup=True) Unital = LazyImport('sage.categories.unital_algebras', 'UnitalAlgebras', at_startup=True) class ParentMethods: @abstract_method(optional=True) def algebra_generators(self): """ Return a family of generators of this algebra. EXAMPLES:: sage: F = AlgebrasWithBasis(QQ).example(); F An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field sage: F.algebra_generators() Family (B[word: a], B[word: b], B[word: c]) """ class WithBasis(CategoryWithAxiom_over_base_ring): class ParentMethods: @abstract_method(optional=True) def product_on_basis(self, i, j): """ The product of the algebra on the basis (optional). INPUT: - ``i``, ``j`` -- the indices of two elements of the basis of ``self`` Return the product of the two corresponding basis elements indexed by ``i`` and ``j``. If implemented, :meth:`product` is defined from it by bilinearity. EXAMPLES:: sage: A = AlgebrasWithBasis(QQ).example() sage: Word = A.basis().keys() sage: A.product_on_basis(Word("abc"),Word("cba")) B[word: abccba] """ @lazy_attribute def product(self): """ The product of the algebra, as per :meth:`Magmas.ParentMethods.product() <sage.categories.magmas.Magmas.ParentMethods.product>` By default, this is implemented using one of the following methods, in the specified order: - :meth:`.product_on_basis` - :meth:`._multiply` or :meth:`._multiply_basis` - :meth:`.product_by_coercion` EXAMPLES:: sage: A = AlgebrasWithBasis(QQ).example() sage: a, b, c = A.algebra_generators() sage: A.product(a + 2*b, 3*c) 3*B[word: ac] + 6*B[word: bc] """ if self.product_on_basis is not NotImplemented: return self._product_from_product_on_basis_multiply # return self._module_morphism(self._module_morphism(self.product_on_basis, position = 0, codomain=self), # position = 1) elif hasattr(self, "_multiply") or hasattr( self, "_multiply_basis"): return self._product_from_combinatorial_algebra_multiply elif hasattr(self, "product_by_coercion"): return self.product_by_coercion else: return NotImplemented # Provides a product using the product_on_basis by calling linear_combination only once def _product_from_product_on_basis_multiply(self, left, right): r""" Computes the product of two elements by extending bilinearly the method :meth:`product_on_basis`. EXAMPLES:: sage: A = AlgebrasWithBasis(QQ).example(); A An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field sage: (a,b,c) = A.algebra_generators() sage: A._product_from_product_on_basis_multiply(a*b + 2*c, a - b) B[word: aba] - B[word: abb] + 2*B[word: ca] - 2*B[word: cb] """ return self.linear_combination( (self.product_on_basis(mon_left, mon_right), coeff_left * coeff_right) for (mon_left, coeff_left ) in left.monomial_coefficients().iteritems() for (mon_right, coeff_right ) in right.monomial_coefficients().iteritems())
class AdditiveSemigroups(CategoryWithAxiom_singleton): """ The category of additive semigroups. An *additive semigroup* is an associative :class:`additive magma <AdditiveMagmas>`, that is a set endowed with an operation `+` which is associative. EXAMPLES:: sage: from sage.categories.additive_semigroups import AdditiveSemigroups sage: C = AdditiveSemigroups(); C Category of additive semigroups sage: C.super_categories() [Category of additive magmas] sage: C.all_super_categories() [Category of additive semigroups, Category of additive magmas, Category of sets, Category of sets with partial maps, Category of objects] sage: C.axioms() frozenset({'AdditiveAssociative'}) sage: C is AdditiveMagmas().AdditiveAssociative() True TESTS:: sage: TestSuite(C).run() """ _base_category_class_and_axiom = (AdditiveMagmas, "AdditiveAssociative") AdditiveCommutative = LazyImport( 'sage.categories.commutative_additive_semigroups', 'CommutativeAdditiveSemigroups', at_startup=True) AdditiveUnital = LazyImport('sage.categories.additive_monoids', 'AdditiveMonoids', at_startup=True) class ParentMethods: def _test_additive_associativity(self, **options): r""" Test associativity for (not necessarily all) elements of this additive semigroup. INPUT: - ``options`` -- any keyword arguments accepted by :meth:`_tester` EXAMPLES: By default, this method tests only the elements returned by ``self.some_elements()``:: sage: S = CommutativeAdditiveSemigroups().example() sage: S._test_additive_associativity() However, the elements tested can be customized with the ``elements`` keyword argument:: sage: (a,b,c,d) = S.additive_semigroup_generators() sage: S._test_additive_associativity(elements = (a, b+c, d)) See the documentation for :class:`TestSuite` for more information. """ tester = self._tester(**options) S = tester.some_elements() from sage.combinat.cartesian_product import CartesianProduct for x, y, z in tester.some_elements(CartesianProduct(S, S, S)): tester.assert_((x + y) + z == x + (y + z)) class Homsets(HomsetsCategory): def extra_super_categories(self): r""" Implement the fact that a homset between two semigroups is a semigroup. EXAMPLES:: sage: from sage.categories.additive_semigroups import AdditiveSemigroups sage: AdditiveSemigroups().Homsets().extra_super_categories() [Category of additive semigroups] sage: AdditiveSemigroups().Homsets().super_categories() [Category of homsets of additive magmas, Category of additive semigroups] """ return [AdditiveSemigroups()] class CartesianProducts(CartesianProductsCategory): def extra_super_categories(self): """ Implement the fact that a cartesian product of additive semigroups is an additive semigroup. EXAMPLES:: sage: from sage.categories.additive_semigroups import AdditiveSemigroups sage: C = AdditiveSemigroups().CartesianProducts() sage: C.extra_super_categories() [Category of additive semigroups] sage: C.axioms() frozenset({'AdditiveAssociative'}) """ return [AdditiveSemigroups()] class Algebras(AlgebrasCategory): def extra_super_categories(self): """ EXAMPLES:: sage: from sage.categories.additive_semigroups import AdditiveSemigroups sage: AdditiveSemigroups().Algebras(QQ).extra_super_categories() [Category of semigroups] sage: CommutativeAdditiveSemigroups().Algebras(QQ).super_categories() [Category of additive semigroup algebras over Rational Field, Category of additive commutative additive magma algebras over Rational Field] """ from sage.categories.semigroups import Semigroups return [Semigroups()] class ParentMethods: @cached_method def algebra_generators(self): r""" Return the generators of this algebra, as per :meth:`MagmaticAlgebras.ParentMethods.algebra_generators() <.magmatic_algebras.MagmaticAlgebras.ParentMethods.algebra_generators>`. They correspond to the generators of the additive semigroup. EXAMPLES:: sage: S = CommutativeAdditiveSemigroups().example(); S An example of a commutative monoid: the free commutative monoid generated by ('a', 'b', 'c', 'd') sage: A = S.algebra(QQ) sage: A.algebra_generators() Finite family {0: B[a], 1: B[b], 2: B[c], 3: B[d]} """ return self.basis().keys().additive_semigroup_generators().map( self.monomial) def product_on_basis(self, g1, g2): r""" Product, on basis elements, as per :meth:`MagmaticAlgebras.WithBasis.ParentMethods.product_on_basis() <sage.categories.magmatic_algebras.MagmaticAlgebras.WithBasis.ParentMethods.product_on_basis>`. The product of two basis elements is induced by the addition of the corresponding elements of the group. EXAMPLES:: sage: S = CommutativeAdditiveSemigroups().example(); S An example of a commutative monoid: the free commutative monoid generated by ('a', 'b', 'c', 'd') sage: A = S.algebra(QQ) sage: a,b,c,d = A.algebra_generators() sage: a * b + b * d * c B[c + b + d] + B[a + b] """ return self.monomial(g1 + g2)
class JTrivialSemigroups: Finite = LazyImport( 'sage_semigroups.categories.finite_j_trivial_semigroups', 'FiniteJTrivialSemigroups')
class Bialgebras(Category_over_base_ring): """ The category of bialgebras EXAMPLES:: sage: Bialgebras(ZZ) Category of bialgebras over Integer Ring sage: Bialgebras(ZZ).super_categories() [Category of algebras over Integer Ring, Category of coalgebras over Integer Ring] TESTS:: sage: TestSuite(Bialgebras(ZZ)).run() """ def super_categories(self): """ EXAMPLES:: sage: Bialgebras(QQ).super_categories() [Category of algebras over Rational Field, Category of coalgebras over Rational Field] """ R = self.base_ring() return [Algebras(R), Coalgebras(R)] def additional_structure(self): r""" Return ``None``. Indeed, the category of bialgebras defines no additional structure: a morphism of coalgebras and of algebras between two bialgebras is a bialgebra morphism. .. SEEALSO:: :meth:`Category.additional_structure` .. TODO:: This category should be a :class:`CategoryWithAxiom`. EXAMPLES:: sage: Bialgebras(QQ).additional_structure() """ return None class ElementMethods: def is_primitive(self): """ Return whether ``self`` is a primitive element. EXAMPLES:: sage: s = SymmetricFunctions(QQ).schur() sage: s([5]).is_primitive() False sage: p = SymmetricFunctions(QQ).powersum() sage: p([5]).is_primitive() True """ one = self.parent().one() return self.coproduct() == one.tensor(self) + self.tensor(one) def is_grouplike(self): """ Return whether ``self`` is a grouplike element. EXAMPLES:: sage: s = SymmetricFunctions(QQ).schur() sage: s([5]).is_grouplike() False sage: s([]).is_grouplike() True """ return self.coproduct() == self.tensor(self) class Super(SuperModulesCategory): pass WithBasis = LazyImport('sage.categories.bialgebras_with_basis', 'BialgebrasWithBasis')
class Coalgebras(Category_over_base_ring): """ The category of coalgebras EXAMPLES:: sage: Coalgebras(QQ) Category of coalgebras over Rational Field sage: Coalgebras(QQ).super_categories() [Category of vector spaces over Rational Field] TESTS:: sage: TestSuite(Coalgebras(ZZ)).run() """ def super_categories(self): """ EXAMPLES:: sage: Coalgebras(QQ).super_categories() [Category of vector spaces over Rational Field] """ return [Modules(self.base_ring())] WithBasis = LazyImport('sage.categories.coalgebras_with_basis', 'CoalgebrasWithBasis') Graded = LazyImport('sage.categories.graded_coalgebras', 'GradedCoalgebras') class ParentMethods: #def __init_add__(self): # The analogue of initDomainAdd # # Will declare the coproduct of self to the coercion mechanism when it exists # pass @abstract_method def counit(self, x): """ Return the counit of ``x``. Eventually, there will be a default implementation, delegating to the overloading mechanism and forcing the conversion back EXAMPLES:: sage: A = HopfAlgebrasWithBasis(QQ).example(); A An example of Hopf algebra with basis: the group algebra of the Dihedral group of order 6 as a permutation group over Rational Field sage: [a,b] = A.algebra_generators() sage: a, A.counit(a) (B[(1,2,3)], 1) sage: b, A.counit(b) (B[(1,3)], 1) TODO: implement some tests of the axioms of coalgebras, bialgebras and Hopf algebras using the counit. """ @abstract_method def coproduct(self, x): """ Return the coproduct of ``x``. Eventually, there will be a default implementation, delegating to the overloading mechanism and forcing the conversion back EXAMPLES:: sage: A = HopfAlgebrasWithBasis(QQ).example(); A An example of Hopf algebra with basis: the group algebra of the Dihedral group of order 6 as a permutation group over Rational Field sage: [a,b] = A.algebra_generators() sage: a, A.coproduct(a) (B[(1,2,3)], B[(1,2,3)] # B[(1,2,3)]) sage: b, A.coproduct(b) (B[(1,3)], B[(1,3)] # B[(1,3)]) """ #return self.tensor_square()(overloaded_coproduct(x)) class ElementMethods: def coproduct(self): """ Return the coproduct of ``self``. EXAMPLES:: sage: A = HopfAlgebrasWithBasis(QQ).example(); A An example of Hopf algebra with basis: the group algebra of the Dihedral group of order 6 as a permutation group over Rational Field sage: [a,b] = A.algebra_generators() sage: a, a.coproduct() (B[(1,2,3)], B[(1,2,3)] # B[(1,2,3)]) sage: b, b.coproduct() (B[(1,3)], B[(1,3)] # B[(1,3)]) """ return self.parent().coproduct(self) def counit(self): """ Return the counit of ``self``. EXAMPLES:: sage: A = HopfAlgebrasWithBasis(QQ).example(); A An example of Hopf algebra with basis: the group algebra of the Dihedral group of order 6 as a permutation group over Rational Field sage: [a,b] = A.algebra_generators() sage: a, a.counit() (B[(1,2,3)], 1) sage: b, b.counit() (B[(1,3)], 1) """ return self.parent().counit(self) class SubcategoryMethods: @cached_method def Cocommutative(self): r""" Return the full subcategory of the cocommutative objects of ``self``. A coalgebra `C` is said to be *cocommutative* if .. MATH:: \Delta(c) = \sum_{(c)} c_{(1)} \otimes c_{(2)} = \sum_{(c)} c_{(2)} \otimes c_{(1)} in Sweedler's notation for all `c \in C`. EXAMPLES:: sage: C1 = Coalgebras(ZZ).Cocommutative().WithBasis(); C1 Category of cocommutative coalgebras with basis over Integer Ring sage: C2 = Coalgebras(ZZ).WithBasis().Cocommutative() sage: C1 is C2 True sage: BialgebrasWithBasis(QQ).Cocommutative() Category of cocommutative bialgebras with basis over Rational Field TESTS:: sage: TestSuite(Coalgebras(ZZ).Cocommutative()).run() """ return self._with_axiom("Cocommutative") class Cocommutative(CategoryWithAxiom_over_base_ring): """ Category of cocommutative coalgebras. """ class TensorProducts(TensorProductsCategory): @cached_method def extra_super_categories(self): """ EXAMPLES:: sage: Coalgebras(QQ).TensorProducts().extra_super_categories() [Category of coalgebras over Rational Field] sage: Coalgebras(QQ).TensorProducts().super_categories() [Category of tensor products of vector spaces over Rational Field, Category of coalgebras over Rational Field] Meaning: a tensor product of coalgebras is a coalgebra """ return [self.base_category()] class ParentMethods: # TODO: provide this default implementation of one if one_basis is not implemented #def one(self): # return tensor(module.one() for module in self.modules) pass class ElementMethods: pass class DualObjects(DualObjectsCategory): def extra_super_categories(self): r""" Return the dual category. EXAMPLES: The category of coalgebras over the Rational Field is dual to the category of algebras over the same field:: sage: C = Coalgebras(QQ) sage: C.dual() Category of duals of coalgebras over Rational Field sage: C.dual().super_categories() # indirect doctest [Category of algebras over Rational Field, Category of duals of vector spaces over Rational Field] .. WARNING:: This is only correct in certain cases (finite dimension, ...). See :trac:`15647`. """ from sage.categories.algebras import Algebras return [Algebras(self.base_category().base_ring())] class Super(SuperModulesCategory): def extra_super_categories(self): """ EXAMPLES:: sage: Coalgebras(ZZ).Super().extra_super_categories() [Category of graded coalgebras over Integer Ring] sage: Coalgebras(ZZ).Super().super_categories() [Category of graded coalgebras over Integer Ring, Category of super modules over Integer Ring] Compare this with the situation for bialgebras:: sage: Bialgebras(ZZ).Super().extra_super_categories() [] sage: Bialgebras(ZZ).Super().super_categories() [Category of super algebras over Integer Ring, Category of super coalgebras over Integer Ring] The category of bialgebras does not occur in these results, since super bialgebras are not bialgebras. """ return [self.base_category().Graded()] class SubcategoryMethods: @cached_method def Supercocommutative(self): r""" Return the full subcategory of the supercocommutative objects of ``self``. EXAMPLES:: sage: Coalgebras(ZZ).WithBasis().Super().Supercocommutative() Category of supercocommutative super coalgebras with basis over Integer Ring sage: BialgebrasWithBasis(QQ).Super().Supercocommutative() Join of Category of super algebras with basis over Rational Field and Category of super bialgebras over Rational Field and Category of super coalgebras with basis over Rational Field and Category of supercocommutative super coalgebras over Rational Field TESTS:: sage: TestSuite(HopfAlgebras(ZZ).Super().Supercocommutative()).run() """ return self._with_axiom("Supercocommutative") class Supercocommutative(CategoryWithAxiom_over_base_ring): """ Category of supercocommutative coalgebras. """ class Filtered(FilteredModulesCategory): """ Category of filtered coalgebras. """ class WithRealizations(WithRealizationsCategory): class ParentMethods: def coproduct(self, x): r""" Return the coproduct of ``x``. EXAMPLES:: sage: N = NonCommutativeSymmetricFunctions(QQ) sage: S = N.complete() sage: N.coproduct.__module__ 'sage.categories.coalgebras' sage: N.coproduct(S[2]) S[] # S[2] + S[1] # S[1] + S[2] # S[] """ return self.a_realization()(x).coproduct() def counit(self, x): r""" Return the counit of ``x``. EXAMPLES:: sage: Sym = SymmetricFunctions(QQ) sage: s = Sym.schur() sage: f = s[2,1] sage: f.counit.__module__ 'sage.categories.coalgebras' sage: f.counit() 0 :: sage: N = NonCommutativeSymmetricFunctions(QQ) sage: N.counit.__module__ 'sage.categories.coalgebras' sage: N.counit(N.one()) 1 sage: x = N.an_element(); x 2*S[] + 2*S[1] + 3*S[1, 1] sage: N.counit(x) 2 """ return self.a_realization()(x).counit() class Realizations(RealizationsCategory): class ParentMethods: def coproduct_by_coercion(self, x): r""" Return the coproduct by coercion if ``coproduct_by_basis`` is not implemented. EXAMPLES:: sage: Sym = SymmetricFunctions(QQ) sage: m = Sym.monomial() sage: f = m[2,1] sage: f.coproduct.__module__ 'sage.categories.coalgebras' sage: m.coproduct_on_basis NotImplemented sage: m.coproduct == m.coproduct_by_coercion True sage: f.coproduct() m[] # m[2, 1] + m[1] # m[2] + m[2] # m[1] + m[2, 1] # m[] :: sage: N = NonCommutativeSymmetricFunctions(QQ) sage: R = N.ribbon() sage: R.coproduct_by_coercion.__module__ 'sage.categories.coalgebras' sage: R.coproduct_on_basis NotImplemented sage: R.coproduct == R.coproduct_by_coercion True sage: R[1].coproduct() R[] # R[1] + R[1] # R[] """ R = self.realization_of().a_realization() return self.tensor_square()(R(x).coproduct()) def counit_by_coercion(self, x): r""" Return the counit of ``x`` if ``counit_by_basis`` is not implemented. EXAMPLES:: sage: sp = SymmetricFunctions(QQ).sp() sage: sp.an_element() 2*sp[] + 2*sp[1] + 3*sp[2] sage: sp.counit(sp.an_element()) 2 sage: o = SymmetricFunctions(QQ).o() sage: o.an_element() 2*o[] + 2*o[1] + 3*o[2] sage: o.counit(o.an_element()) -1 """ R = self.realization_of().a_realization() return R(x).counit()
class AdditiveGroups(CategoryWithAxiom_singleton): r""" The category of additive groups. An *additive group* is a set with an internal binary operation `+` which is associative, admits a zero, and where every element can be negated. EXAMPLES:: sage: from sage.categories.additive_groups import AdditiveGroups sage: from sage.categories.additive_monoids import AdditiveMonoids sage: AdditiveGroups() Category of additive groups sage: AdditiveGroups().super_categories() [Category of additive inverse additive unital additive magmas, Category of additive monoids] sage: AdditiveGroups().all_super_categories() [Category of additive groups, Category of additive inverse additive unital additive magmas, Category of additive monoids, Category of additive unital additive magmas, Category of additive semigroups, Category of additive magmas, Category of sets, Category of sets with partial maps, Category of objects] sage: AdditiveGroups().axioms() frozenset(['AdditiveAssociative', 'AdditiveUnital', 'AdditiveInverse']) sage: AdditiveGroups() is AdditiveMonoids().AdditiveInverse() True TESTS:: sage: C = AdditiveGroups() sage: TestSuite(C).run() """ _base_category_class_and_axiom = (AdditiveMonoids, "AdditiveInverse") AdditiveCommutative = LazyImport( 'sage.categories.commutative_additive_groups', 'CommutativeAdditiveGroups', at_startup=True) class ElementMethods: ##def -x, -(x,y): def __sub__(left, right): """ Top-level subtraction operator. See extensive documentation at the top of ``element.pyx``. EXAMPLES:: sage: F = CombinatorialFreeModule(QQ, ['a','b']) sage: a,b = F.basis() sage: a - b B['a'] - B['b'] """ if have_same_parent(left, right) and hasattr(left, "_sub_"): return left._sub_(right) from sage.structure.element import get_coercion_model import operator return get_coercion_model().bin_op(left, right, operator.sub) ################################################## # Negation ################################################## def __neg__(self): """ Top-level negation operator for elements of abelian monoids, which may choose to implement ``_neg_`` rather than ``__neg__`` for consistancy. EXAMPLES:: sage: F = CombinatorialFreeModule(QQ, ['a','b']) sage: a,b = F.basis() sage: - b -B['b'] """ return self._neg_()
class Posets(Category): r""" The category of posets i.e. sets with a partial order structure. EXAMPLES:: sage: Posets() Category of posets sage: Posets().super_categories() [Category of sets] sage: P = Posets().example(); P An example of a poset: sets ordered by inclusion The partial order is implemented by the mandatory method :meth:`~Posets.ParentMethods.le`:: sage: x = P(Set([1,3])); y = P(Set([1,2,3])) sage: x, y ({1, 3}, {1, 2, 3}) sage: P.le(x, y) True sage: P.le(x, x) True sage: P.le(y, x) False The other comparison methods are called :meth:`~Posets.ParentMethods.lt`, :meth:`~Posets.ParentMethods.ge`, :meth:`~Posets.ParentMethods.gt`, following Python's naming convention in :mod:`operator`. Default implementations are provided:: sage: P.lt(x, x) False sage: P.ge(y, x) True Unless the poset is a facade (see :class:`Sets.Facade`), one can compare directly its elements using the usual Python operators:: sage: D = Poset((divisors(30), attrcall("divides")), facade = False) sage: D(3) <= D(6) True sage: D(3) <= D(3) True sage: D(3) <= D(5) False sage: D(3) < D(3) False sage: D(10) >= D(5) True At this point, this has to be implemented by hand. Once :trac:`10130` will be resolved, this will be automatically provided by this category:: sage: x < y # todo: not implemented True sage: x < x # todo: not implemented False sage: x <= x # todo: not implemented True sage: y >= x # todo: not implemented True .. seealso:: :func:`Poset`, :class:`FinitePosets`, :class:`LatticePosets` TESTS:: sage: C = Posets() sage: TestSuite(C).run() """ @cached_method def super_categories(self): r""" Return a list of the (immediate) super categories of ``self``, as per :meth:`Category.super_categories`. EXAMPLES:: sage: Posets().super_categories() [Category of sets] """ return [Sets()] def example(self, choice=None): r""" Return examples of objects of ``Posets()``, as per :meth:`Category.example() <sage.categories.category.Category.example>`. EXAMPLES:: sage: Posets().example() An example of a poset: sets ordered by inclusion sage: Posets().example("facade") An example of a facade poset: the positive integers ordered by divisibility """ from sage.categories.examples.posets import FiniteSetsOrderedByInclusion, PositiveIntegersOrderedByDivisibilityFacade if choice == "facade": return PositiveIntegersOrderedByDivisibilityFacade() else: return FiniteSetsOrderedByInclusion() def __iter__(self): r""" Iterator over representatives of the isomorphism classes of posets with finitely many vertices. .. warning:: this feature may become deprecated, since it does of course not iterate through all posets. EXAMPLES:: sage: P = Posets() sage: it = iter(P) sage: for _ in range(10): print next(it); Finite poset containing 0 elements Finite poset containing 1 elements Finite poset containing 2 elements Finite poset containing 2 elements Finite poset containing 3 elements Finite poset containing 3 elements Finite poset containing 3 elements Finite poset containing 3 elements Finite poset containing 3 elements Finite poset containing 4 elements """ from sage.combinat.posets.posets import FinitePosets_n n = 0 while True: for P in FinitePosets_n(n): yield P n += 1 Finite = LazyImport('sage.categories.finite_posets', 'FinitePosets') class ParentMethods: @abstract_method def le(self, x, y): r""" Return whether `x \le y` in the poset ``self``. INPUT: - ``x``, ``y`` -- elements of ``self``. EXAMPLES:: sage: D = Poset((divisors(30), attrcall("divides"))) sage: D.le( 3, 6 ) True sage: D.le( 3, 3 ) True sage: D.le( 3, 5 ) False """ def lt(self, x, y): r""" Return whether `x < y` in the poset ``self``. INPUT: - ``x``, ``y`` -- elements of ``self``. This default implementation delegates the work to :meth:`le`. EXAMPLES:: sage: D = Poset((divisors(30), attrcall("divides"))) sage: D.lt( 3, 6 ) True sage: D.lt( 3, 3 ) False sage: D.lt( 3, 5 ) False """ return self.le(x, y) and x != y def ge(self, x, y): r""" Return whether `x \ge y` in the poset ``self``. INPUT: - ``x``, ``y`` -- elements of ``self``. This default implementation delegates the work to :meth:`le`. EXAMPLES:: sage: D = Poset((divisors(30), attrcall("divides"))) sage: D.ge( 6, 3 ) True sage: D.ge( 3, 3 ) True sage: D.ge( 3, 5 ) False """ return self.le(y, x) def gt(self, x, y): r""" Return whether `x > y` in the poset ``self``. INPUT: - ``x``, ``y`` -- elements of ``self``. This default implementation delegates the work to :meth:`lt`. EXAMPLES:: sage: D = Poset((divisors(30), attrcall("divides"))) sage: D.gt( 3, 6 ) False sage: D.gt( 3, 3 ) False sage: D.gt( 3, 5 ) False """ return self.lt(y, x) @abstract_method(optional=True) def upper_covers(self, x): r""" Return the upper covers of `x`, that is, the elements `y` such that `x<y` and there exists no `z` such that `x<z<y`. EXAMPLES:: sage: D = Poset((divisors(30), attrcall("divides"))) sage: D.upper_covers(3) [6, 15] """ @abstract_method(optional=True) def lower_covers(self, x): r""" Return the lower covers of `x`, that is, the elements `y` such that `y<x` and there exists no `z` such that `y<z<x`. EXAMPLES:: sage: D = Poset((divisors(30), attrcall("divides"))) sage: D.lower_covers(15) [3, 5] """ @abstract_method(optional=True) def order_ideal(self, elements): r""" Return the order ideal in ``self`` generated by the elements of an iterable ``elements``. A subset `I` of a poset is said to be an order ideal if, for any `x` in `I` and `y` such that `y \le x`, then `y` is in `I`. This is also called the lower set generated by these elements. EXAMPLES:: sage: B = Posets.BooleanLattice(4) sage: B.order_ideal([7,10]) [0, 1, 2, 3, 4, 5, 6, 7, 8, 10] """ lower_set = order_ideal @abstract_method(optional=True) def order_filter(self, elements): r""" Return the order filter generated by a list of elements. A subset `I` of a poset is said to be an order filter if, for any `x` in `I` and `y` such that `y \ge x`, then `y` is in `I`. This is also called the upper set generated by these elements. EXAMPLES:: sage: B = Posets.BooleanLattice(4) sage: B.order_filter([3,8]) [3, 7, 8, 9, 10, 11, 12, 13, 14, 15] """ upper_set = order_filter def directed_subset(self, elements, direction): r""" Return the order filter or the order ideal generated by a list of elements. If ``direction`` is 'up', the order filter (upper set) is being returned. If ``direction`` is 'down', the order ideal (lower set) is being returned. INPUT: - elements -- a list of elements. - direction -- 'up' or 'down'. EXAMPLES:: sage: B = Posets.BooleanLattice(4) sage: B.directed_subset([3, 8], 'up') [3, 7, 8, 9, 10, 11, 12, 13, 14, 15] sage: B.directed_subset([7, 10], 'down') [0, 1, 2, 3, 4, 5, 6, 7, 8, 10] """ if direction == 'up': return self.order_filter(elements) if direction == 'down': return self.order_ideal(elements) raise ValueError("Direction must be either 'up' or 'down'.") def principal_order_ideal(self, x): r""" Return the order ideal generated by an element ``x``. This is also called the lower set generated by this element. EXAMPLES:: sage: B = Posets.BooleanLattice(4) sage: B.principal_order_ideal(6) [0, 2, 4, 6] """ return self.order_ideal([x]) principal_lower_set = principal_order_ideal def principal_order_filter(self, x): r""" Return the order filter generated by an element ``x``. This is also called the upper set generated by this element. EXAMPLES:: sage: B = Posets.BooleanLattice(4) sage: B.principal_order_filter(2) [2, 3, 6, 7, 10, 11, 14, 15] """ return self.order_filter([x]) principal_upper_set = principal_order_filter def order_ideal_toggle(self, I, v): r""" Return the result of toggling the element ``v`` in the order ideal ``I``. If `v` is an element of a poset `P`, then toggling the element `v` is an automorphism of the set `J(P)` of all order ideals of `P`. It is defined as follows: If `I` is an order ideal of `P`, then the image of `I` under toggling the element `v` is - the set `I \cup \{ v \}`, if `v \not\in I` but every element of `P` smaller than `v` is in `I`; - the set `I \setminus \{ v \}`, if `v \in I` but no element of `P` greater than `v` is in `I`; - `I` otherwise. This image always is an order ideal of `P`. EXAMPLES:: sage: P = Poset({1: [2,3], 2: [4], 3: []}) sage: I = Set({1, 2}) sage: I in P.order_ideals_lattice() True sage: P.order_ideal_toggle(I, 1) {1, 2} sage: P.order_ideal_toggle(I, 2) {1} sage: P.order_ideal_toggle(I, 3) {1, 2, 3} sage: P.order_ideal_toggle(I, 4) {1, 2, 4} sage: P4 = Posets(4) sage: all(all(all(P.order_ideal_toggle(P.order_ideal_toggle(I, i), i) == I ....: for i in range(4)) ....: for I in P.order_ideals_lattice(facade=True)) ....: for P in P4) True """ if not v in I: if all(u in I for u in self.lower_covers(v)): from sage.sets.set import Set return I.union(Set({v})) else: if all(u not in I for u in self.upper_covers(v)): from sage.sets.set import Set return I.difference(Set({v})) return I def order_ideal_toggles(self, I, vs): r""" Return the result of toggling the elements of the list (or iterable) ``vs`` (one by one, from left to right) in the order ideal ``I``. See :meth:`order_ideal_toggle` for a definition of toggling. EXAMPLES:: sage: P = Poset({1: [2,3], 2: [4], 3: []}) sage: I = Set({1, 2}) sage: P.order_ideal_toggles(I, [1,2,3,4]) {1, 3} sage: P.order_ideal_toggles(I, (1,2,3,4)) {1, 3} """ for v in vs: I = self.order_ideal_toggle(I, v) return I def is_order_ideal(self, o): """ Return whether ``o`` is an order ideal of ``self``, assuming ``self`` has no infinite descending path. INPUT: - ``o`` -- a list (or set, or tuple) containing some elements of ``self`` EXAMPLES:: sage: P = Poset((divisors(12), attrcall("divides")), facade=True, linear_extension=True) sage: sorted(P.list()) [1, 2, 3, 4, 6, 12] sage: P.is_order_ideal([1, 3]) True sage: P.is_order_ideal([]) True sage: P.is_order_ideal({1, 3}) True sage: P.is_order_ideal([1, 3, 4]) False """ return all((u in self and all(x in o for x in self.lower_covers(u))) for u in o) def is_order_filter(self, o): """ Return whether ``o`` is an order filter of ``self``, assuming ``self`` has no infinite ascending path. INPUT: - ``o`` -- a list (or set, or tuple) containing some elements of ``self`` EXAMPLES:: sage: P = Poset((divisors(12), attrcall("divides")), facade=True, linear_extension=True) sage: sorted(P.list()) [1, 2, 3, 4, 6, 12] sage: P.is_order_filter([4, 12]) True sage: P.is_order_filter([]) True sage: P.is_order_filter({3, 4, 12}) False sage: P.is_order_filter({3, 6, 12}) True """ return all((u in self and all(x in o for x in self.upper_covers(u))) for u in o) def is_chain_of_poset(self, o, ordered=False): """ Return whether an iterable ``o`` is a chain of ``self``, including a check for ``o`` being ordered from smallest to largest element if the keyword ``ordered`` is set to ``True``. INPUT: - ``o`` -- an iterable (e. g., list, set, or tuple) containing some elements of ``self`` - ``ordered`` -- a Boolean (default: ``False``) which decides whether the notion of a chain includes being ordered OUTPUT: If ``ordered`` is set to ``False``, the truth value of the following assertion is returned: The subset of ``self`` formed by the elements of ``o`` is a chain in ``self``. If ``ordered`` is set to ``True``, the truth value of the following assertion is returned: Every element of the list ``o`` is (strictly!) smaller than its successor in ``self``. (This makes no sense if ``ordered`` is a set.) EXAMPLES:: sage: P = Poset((divisors(12), attrcall("divides")), facade=True, linear_extension=True) sage: sorted(P.list()) [1, 2, 3, 4, 6, 12] sage: P.is_chain_of_poset([1, 3]) True sage: P.is_chain_of_poset([3, 1]) True sage: P.is_chain_of_poset([1, 3], ordered=True) True sage: P.is_chain_of_poset([3, 1], ordered=True) False sage: P.is_chain_of_poset([]) True sage: P.is_chain_of_poset([], ordered=True) True sage: P.is_chain_of_poset((2, 12, 6)) True sage: P.is_chain_of_poset((2, 6, 12), ordered=True) True sage: P.is_chain_of_poset((2, 12, 6), ordered=True) False sage: P.is_chain_of_poset((2, 12, 6, 3)) False sage: P.is_chain_of_poset((2, 3)) False sage: Q = Poset({2: [3, 1], 3: [4], 1: [4]}) sage: Q.is_chain_of_poset([1, 2], ordered=True) False sage: Q.is_chain_of_poset([1, 2]) True sage: Q.is_chain_of_poset([2, 1], ordered=True) True sage: Q.is_chain_of_poset([2, 1, 1], ordered=True) False sage: Q.is_chain_of_poset([3]) True sage: Q.is_chain_of_poset([4, 2, 3]) True sage: Q.is_chain_of_poset([4, 2, 3], ordered=True) False sage: Q.is_chain_of_poset([2, 3, 4], ordered=True) True Examples with infinite posets:: sage: from sage.categories.examples.posets import FiniteSetsOrderedByInclusion sage: R = FiniteSetsOrderedByInclusion() sage: R.is_chain_of_poset([R(set([3, 1, 2])), R(set([1, 4])), R(set([4, 5]))]) False sage: R.is_chain_of_poset([R(set([3, 1, 2])), R(set([1, 2])), R(set([1]))], ordered=True) False sage: R.is_chain_of_poset([R(set([3, 1, 2])), R(set([1, 2])), R(set([1]))]) True sage: from sage.categories.examples.posets import PositiveIntegersOrderedByDivisibilityFacade sage: T = PositiveIntegersOrderedByDivisibilityFacade() sage: T.is_chain_of_poset((T(3), T(4), T(7))) False sage: T.is_chain_of_poset((T(3), T(6), T(3))) True sage: T.is_chain_of_poset((T(3), T(6), T(3)), ordered=True) False sage: T.is_chain_of_poset((T(3), T(3), T(6))) True sage: T.is_chain_of_poset((T(3), T(3), T(6)), ordered=True) False sage: T.is_chain_of_poset((T(3), T(6)), ordered=True) True sage: T.is_chain_of_poset((), ordered=True) True sage: T.is_chain_of_poset((T(3),), ordered=True) True sage: T.is_chain_of_poset((T(q) for q in divisors(27))) True sage: T.is_chain_of_poset((T(q) for q in divisors(18))) False """ list_o = list(o) if ordered: return all(self.lt(a, b) for a, b in zip(list_o, list_o[1:])) else: for (i, x) in enumerate(list_o): for y in list_o[:i]: if (not self.le(x, y)) and (not self.gt(x, y)): return False return True def is_antichain_of_poset(self, o): """ Return whether an iterable ``o`` is an antichain of ``self``. INPUT: - ``o`` -- an iterable (e. g., list, set, or tuple) containing some elements of ``self`` OUTPUT: ``True`` if the subset of ``self`` consisting of the entries of ``o`` is an antichain of ``self``, and ``False`` otherwise. EXAMPLES:: sage: P = Poset((divisors(12), attrcall("divides")), facade=True, linear_extension=True) sage: sorted(P.list()) [1, 2, 3, 4, 6, 12] sage: P.is_antichain_of_poset([1, 3]) False sage: P.is_antichain_of_poset([3, 1]) False sage: P.is_antichain_of_poset([1, 1, 3]) False sage: P.is_antichain_of_poset([]) True sage: P.is_antichain_of_poset([1]) True sage: P.is_antichain_of_poset([1, 1]) True sage: P.is_antichain_of_poset([3, 4]) True sage: P.is_antichain_of_poset([3, 4, 12]) False sage: P.is_antichain_of_poset([6, 4]) True sage: P.is_antichain_of_poset(i for i in divisors(12) if (2 < i and i < 6)) True sage: P.is_antichain_of_poset(i for i in divisors(12) if (2 <= i and i < 6)) False sage: Q = Poset({2: [3, 1], 3: [4], 1: [4]}) sage: Q.is_antichain_of_poset((1, 2)) False sage: Q.is_antichain_of_poset((2, 4)) False sage: Q.is_antichain_of_poset((4, 2)) False sage: Q.is_antichain_of_poset((2, 2)) True sage: Q.is_antichain_of_poset((3, 4)) False sage: Q.is_antichain_of_poset((3, 1)) True sage: Q.is_antichain_of_poset((1, )) True sage: Q.is_antichain_of_poset(()) True An infinite poset:: sage: from sage.categories.examples.posets import FiniteSetsOrderedByInclusion sage: R = FiniteSetsOrderedByInclusion() sage: R.is_antichain_of_poset([R(set([3, 1, 2])), R(set([1, 4])), R(set([4, 5]))]) True sage: R.is_antichain_of_poset([R(set([3, 1, 2, 4])), R(set([1, 4])), R(set([4, 5]))]) False """ return all(not self.lt(x, y) for x in o for y in o) class ElementMethods: pass
class Fields(CategoryWithAxiom): """ The category of (commutative) fields, i.e. commutative rings where all non-zero elements have multiplicative inverses EXAMPLES:: sage: K = Fields() sage: K Category of fields sage: Fields().super_categories() [Category of euclidean domains, Category of division rings] sage: K(IntegerRing()) Rational Field sage: K(PolynomialRing(GF(3), 'x')) Fraction Field of Univariate Polynomial Ring in x over Finite Field of size 3 sage: K(RealField()) Real Field with 53 bits of precision TESTS:: sage: TestSuite(Fields()).run() """ _base_category_class_and_axiom = (DivisionRings, "Commutative") def extra_super_categories(self): """ EXAMPLES:: sage: Fields().extra_super_categories() [Category of euclidean domains] """ return [EuclideanDomains()] def __contains__(self, x): """ EXAMPLES:: sage: GF(4, "a") in Fields() True sage: QQ in Fields() True sage: ZZ in Fields() False sage: IntegerModRing(4) in Fields() False sage: InfinityRing in Fields() False This implementation will not be needed anymore once every field in Sage will be properly declared in the category :class:`Fields`(). Caveat: this should eventually be fixed:: sage: gap.Rationals in Fields() False typically by implementing the method :meth:`category` appropriately for Gap objects:: sage: GR = gap.Rationals sage: GR.category = lambda : Fields() sage: GR in Fields() True The following tests against a memory leak fixed in :trac:`13370`:: sage: import gc sage: _ = gc.collect() sage: n = len([X for X in gc.get_objects() if isinstance(X, sage.rings.finite_rings.integer_mod_ring.IntegerModRing_generic)]) sage: for i in prime_range(100): ... R = ZZ.quotient(i) ... t = R in Fields() sage: _ = gc.collect() sage: len([X for X in gc.get_objects() if isinstance(X, sage.rings.finite_rings.integer_mod_ring.IntegerModRing_generic)]) - n 1 """ try: return self._contains_helper(x) or sage.rings.ring._is_Field(x) except Exception: return False @lazy_class_attribute def _contains_helper(cls): """ Helper for containment tests in the category of fields. This helper just tests whether the given object's category is already known to be a sub-category of the category of fields. There are, however, rings that are initialised as plain commutative rings and found out to be fields only afterwards. Hence, this helper alone is not enough for a proper containment test. TESTS:: sage: P.<x> = QQ[] sage: Q = P.quotient(x^2+2) sage: Q.category() Join of Category of commutative algebras over Rational Field and Category of subquotients of monoids and Category of quotients of semigroups sage: F = Fields() sage: F._contains_helper(Q) False sage: Q in F # This changes the category! True sage: F._contains_helper(Q) True """ return Category_contains_method_by_parent_class(cls()) def _call_(self, x): """ Construct a field from the data in ``x`` EXAMPLES:: sage: K = Fields() sage: K Category of fields sage: Fields().super_categories() [Category of euclidean domains, Category of division rings] sage: K(IntegerRing()) # indirect doctest Rational Field sage: K(PolynomialRing(GF(3), 'x')) # indirect doctest Fraction Field of Univariate Polynomial Ring in x over Finite Field of size 3 sage: K(RealField()) Real Field with 53 bits of precision """ try: return x.fraction_field() except AttributeError: raise TypeError("unable to associate a field to %s"%x) Finite = LazyImport('sage.categories.finite_fields', 'FiniteFields', at_startup=True) class ParentMethods: def is_field( self, proof=True ): r""" Returns True as ``self`` is a field. EXAMPLES:: sage: QQ.is_field() True sage: Parent(QQ,category=Fields()).is_field() True """ return True def is_integrally_closed(self): r""" Return ``True``, as per :meth:`IntegralDomain.is_integrally_closed`: for every field `F`, `F` is its own field of fractions, hence every element of `F` is integral over `F`. EXAMPLES:: sage: QQ.is_integrally_closed() True sage: QQbar.is_integrally_closed() True sage: Z5 = GF(5); Z5 Finite Field of size 5 sage: Z5.is_integrally_closed() True """ return True def is_perfect(self): r""" Return whether this field is perfect, i.e., its characteristic is `p=0` or every element has a `p`-th root. EXAMPLES:: sage: QQ.is_perfect() True sage: GF(2).is_perfect() True sage: FunctionField(GF(2), 'x').is_perfect() False """ if self.characteristic() == 0: return True else: raise NotImplementedError def _test_characteristic_fields(self, **options): """ Run generic tests on the method :meth:`.characteristic`. EXAMPLES:: sage: QQ._test_characteristic_fields() .. NOTE:: We cannot call this method ``_test_characteristic`` since that would overwrite the method in the super category, and for cython classes just calling ``super(sage.categories.fields.Fields().parent_class, self)._test_characteristic`` doesn't have the desired effect. .. SEEALSO:: :meth:`sage.categories.rings.Rings.ParentMethods._test_characteristic` """ tester = self._tester(**options) try: char = self.characteristic() tester.assertTrue(char.is_zero() or char.is_prime()) except AttributeError: return # raised when self.one() does not have a additive_order() [or when char is an int and not an Integer which is already checked by _test_characteristic for rings] except NotImplementedError: return def fraction_field(self): r""" Returns the *fraction field* of ``self``, which is ``self``. EXAMPLES:: sage: QQ.fraction_field() is QQ True """ return self def _squarefree_decomposition_univariate_polynomial(self, f): r""" Return the square-free decomposition of ``f`` over this field. This is a helper method for :meth:`sage.rings.polynomial.squarefree_decomposition`. INPUT: - ``f`` -- a univariate non-zero polynomial over this field ALGORITHM: For rings of characteristic zero, we use the algorithm descriped in [Yun]_. Other fields may provide their own implementation by overriding this method. EXAMPLES:: sage: x = polygen(QQ) sage: p = 37 * (x-1)^3 * (x-2)^3 * (x-1/3)^7 * (x-3/7) sage: p.squarefree_decomposition() (37*x - 111/7) * (x^2 - 3*x + 2)^3 * (x - 1/3)^7 sage: p = 37 * (x-2/3)^2 sage: p.squarefree_decomposition() (37) * (x - 2/3)^2 sage: x = polygen(GF(3)) sage: x.squarefree_decomposition() x sage: f = QQbar['x'](1) sage: f.squarefree_decomposition() 1 REFERENCES: .. [Yun] Yun, David YY. On square-free decomposition algorithms. In Proceedings of the third ACM symposium on Symbolic and algebraic computation, pp. 26-35. ACM, 1976. """ from sage.structure.factorization import Factorization if f.degree() == 0: return Factorization([], unit=f[0]) if self.characteristic() != 0: raise NotImplementedError("square-free decomposition not implemented for this polynomial.") factors = [] cur = f f = [f] while cur.degree() > 0: cur = cur.gcd(cur.derivative()) f.append(cur) g = [] for i in range(len(f) - 1): g.append(f[i] // f[i+1]) a = [] for i in range(len(g) - 1): a.append(g[i] // g[i+1]) a.append(g[-1]) unit = f[-1] for i in range(len(a)): if a[i].degree() > 0: factors.append((a[i], i+1)) else: unit = unit * a[i].constant_coefficient() ** (i + 1) return Factorization(factors, unit=unit, sort=False) def __pow__(self, n): r""" Returns the vector space of dimension `n` over ``self``. EXAMPLES:: sage: QQ^4 Vector space of dimension 4 over Rational Field """ from sage.modules.all import FreeModule return FreeModule(self, n) class ElementMethods: def euclidean_degree(self): r""" Return the degree of this element as an element of a euclidean domain. In a field, this returns 0 for all but the zero element (for which it is undefined). EXAMPLES:: sage: QQ.one().euclidean_degree() 0 """ if self.is_zero(): raise ValueError("euclidean degree not defined for the zero element") from sage.rings.all import ZZ return ZZ.zero() def quo_rem(self, other): r""" Return the quotient with remainder of the division of this element by ``other``. INPUT: - ``other`` -- an element of the field EXAMPLES:: sage: f,g = QQ(1), QQ(2) sage: f.quo_rem(g) (1/2, 0) """ if other.is_zero(): raise ZeroDivisionError return (self/other, self.parent().zero()) def is_unit( self ): r""" Returns True if ``self`` has a multiplicative inverse. EXAMPLES:: sage: QQ(2).is_unit() True sage: QQ(0).is_unit() False """ return not self.is_zero() # Fields are unique factorization domains, so, there is gcd and lcm # Of course, in general gcd and lcm in a field are not very interesting. # However, they should be implemented! def gcd(self,other): """ Greatest common divisor. NOTE: Since we are in a field and the greatest common divisor is only determined up to a unit, it is correct to either return zero or one. Note that fraction fields of unique factorization domains provide a more sophisticated gcd. EXAMPLES:: sage: GF(5)(1).gcd(GF(5)(1)) 1 sage: GF(5)(1).gcd(GF(5)(0)) 1 sage: GF(5)(0).gcd(GF(5)(0)) 0 For fields of characteristic zero (i.e., containing the integers as a sub-ring), evaluation in the integer ring is attempted. This is for backwards compatibility:: sage: gcd(6.0,8); gcd(6.0,8).parent() 2 Integer Ring If this fails, we resort to the default we see above:: sage: gcd(6.0*CC.0,8*CC.0); gcd(6.0*CC.0,8*CC.0).parent() 1.00000000000000 Complex Field with 53 bits of precision AUTHOR: - Simon King (2011-02): Trac ticket #10771 """ P = self.parent() try: other = P(other) except (TypeError, ValueError): raise ArithmeticError("The second argument can not be interpreted in the parent of the first argument. Can't compute the gcd") from sage.rings.integer_ring import ZZ if ZZ.is_subring(P): try: return ZZ(self).gcd(ZZ(other)) except TypeError: pass # there is no custom gcd, so, we resort to something that always exists # (that's new behaviour) if self==0 and other==0: return P.zero() return P.one() def lcm(self,other): """ Least common multiple. NOTE: Since we are in a field and the least common multiple is only determined up to a unit, it is correct to either return zero or one. Note that fraction fields of unique factorization domains provide a more sophisticated lcm. EXAMPLES:: sage: GF(2)(1).lcm(GF(2)(0)) 0 sage: GF(2)(1).lcm(GF(2)(1)) 1 If the field contains the integer ring, it is first attempted to compute the gcd there:: sage: lcm(15.0,12.0); lcm(15.0,12.0).parent() 60 Integer Ring If this fails, we resort to the default we see above:: sage: lcm(6.0*CC.0,8*CC.0); lcm(6.0*CC.0,8*CC.0).parent() 1.00000000000000 Complex Field with 53 bits of precision sage: lcm(15.2,12.0) 1.00000000000000 AUTHOR: - Simon King (2011-02): Trac ticket #10771 """ P = self.parent() try: other = P(other) except (TypeError, ValueError): raise ArithmeticError("The second argument can not be interpreted in the parent of the first argument. Can't compute the lcm") from sage.rings.integer_ring import ZZ if ZZ.is_subring(P): try: return ZZ(self).lcm(ZZ(other)) except TypeError: pass # there is no custom lcm, so, we resort to something that always exists if self==0 or other==0: return P.zero() return P.one() @coerce_binop def xgcd(self, other): """ Compute the extended gcd of ``self`` and ``other``. INPUT: - ``other`` -- an element with the same parent as ``self`` OUTPUT: A tuple ``(r, s, t)`` of elements in the parent of ``self`` such that ``r = s * self + t * other``. Since the computations are done over a field, ``r`` is zero if ``self`` and ``other`` are zero, and one otherwise. AUTHORS: - Julian Rueth (2012-10-19): moved here from :class:`sage.structure.element.FieldElement` EXAMPLES:: sage: (1/2).xgcd(2) (1, 2, 0) sage: (0/2).xgcd(2) (1, 0, 1/2) sage: (0/2).xgcd(0) (0, 0, 0) """ R = self.parent() if not self.is_zero(): return (R.one(), ~self, R.zero()) if not other.is_zero(): return (R.one(), R.zero(), ~other) # else both are 0 return (R.zero(), R.zero(), R.zero())