def __init_extra__(self): """ Declare the canonical coercion from ``self.base_ring()`` to ``self``, if there has been none before. 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: coercion_model = sage.structure.element.get_coercion_model() sage: coercion_model.discover_coercion(QQ, A) (Generic morphism: From: Rational Field To: An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field, None) sage: A(1) # indirect doctest B[word: ] TESTS: Ensure that :trac:`28328` is fixed and that non-associative algebras are supported:: sage: class Foo(CombinatorialFreeModule): ....: def one(self): ....: return self.monomial(0) sage: from sage.categories.magmatic_algebras \ ....: import MagmaticAlgebras sage: C = MagmaticAlgebras(QQ).WithBasis().Unital() sage: F = Foo(QQ,(1,),category=C) sage: F(0) 0 sage: F(3) 3*B[0] """ # If self has an attribute _no_generic_basering_coercion # set to True, then this declaration is skipped. # This trick, introduced in #11900, is used in # sage.matrix.matrix_space.py and # sage.rings.polynomial.polynomial_ring. # It will hopefully be refactored into something more # conceptual later on. if getattr(self, '_no_generic_basering_coercion', False): return base_ring = self.base_ring() if base_ring is self: # There are rings that are their own base rings. No need to register that. return if self._is_coercion_cached(base_ring): # We will not use any generic stuff, since a (presumably) better conversion # has already been registered. return # Pick a homset for the morphism to live in... if self in Rings(): # The algebra is associative, and thus a ring. The # base ring is also a ring. Everything is OK. H = Hom(base_ring, self, Rings()) else: # If the algebra isn't associative, we would like to # use the category of unital magmatic algebras (which # are not necessarily associative) instead. But, # unfortunately, certain important rings like QQ # aren't in that category. As a result, we have to use # something weaker. cat = Magmas().Unital() cat = Category.join([cat, CommutativeAdditiveGroups()]) cat = cat.Distributive() H = Hom(base_ring, self, cat) # We need to register a coercion from the base ring to self. # # There is a generic method from_base_ring(), that just does # multiplication with the multiplicative unit. However, the # unit is constructed repeatedly, which is slow. # So, if the unit is available *now*, then we can create a # faster coercion map. # # This only applies for the generic from_base_ring() method. # If there is a specialised from_base_ring(), then it should # be used unconditionally. generic_from_base_ring = self.category( ).parent_class.from_base_ring if type(self).from_base_ring != generic_from_base_ring: # Custom from_base_ring() use_from_base_ring = True if isinstance(generic_from_base_ring, lazy_attribute): # If the category implements from_base_ring() as lazy # attribute, then we always use it. # This is for backwards compatibility, see Trac #25181 use_from_base_ring = True else: try: one = self.one() use_from_base_ring = False except (NotImplementedError, AttributeError, TypeError): # The unit is not available, yet. But there are cases # in which it will be available later. So, we use # the generic from_base_ring() after all. use_from_base_ring = True mor = None if use_from_base_ring: mor = SetMorphism(function=self.from_base_ring, parent=H) else: # We have the multiplicative unit, so implement the # coercion from the base ring as multiplying with that. # # But first we check that it actually works. If not, # then the generic implementation of from_base_ring() # would fail as well so we don't use it. try: if one._lmul_(base_ring.an_element()) is not None: # There are cases in which lmul returns None, # which means that it's not implemented. # One example: Hecke algebras. mor = SetMorphism(function=one._lmul_, parent=H) except (NotImplementedError, AttributeError, TypeError): pass if mor is not None: try: self.register_coercion(mor) except AssertionError: pass
def _coerce_map_from_base_ring(self): """ Return a suitable coercion map from the base ring of ``self``. TESTS:: sage: A = cartesian_product((QQ['z'],)); A The Cartesian product of (Univariate Polynomial Ring in z over Rational Field,) sage: A.base_ring() Rational Field sage: A._coerce_map_from_base_ring() Generic morphism: From: Rational Field To: The Cartesian product of (Univariate Polynomial Ring in z over Rational Field,) Check that :trac:`29312` is fixed:: sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace') sage: F._coerce_map_from_base_ring() Generic morphism: From: Rational Field To: Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field """ base_ring = self.base_ring() # Pick a homset for the morphism to live in... if self in Rings(): # The algebra is associative, and thus a ring. The # base ring is also a ring. Everything is OK. H = Hom(base_ring, self, Rings()) else: # If the algebra isn't associative, we would like to # use the category of unital magmatic algebras (which # are not necessarily associative) instead. But, # unfortunately, certain important rings like QQ # aren't in that category. As a result, we have to use # something weaker. cat = Magmas().Unital() cat = Category.join([cat, CommutativeAdditiveGroups()]) cat = cat.Distributive() H = Hom(base_ring, self, cat) # We need to construct a coercion from the base ring to self. # # There is a generic method from_base_ring(), that just does # multiplication with the multiplicative unit. However, the # unit is constructed repeatedly, which is slow. # So, if the unit is available *now*, then we can create a # faster coercion map. # # This only applies for the generic from_base_ring() method. # If there is a specialised from_base_ring(), then it should # be used unconditionally. generic_from_base_ring = self.category( ).parent_class.from_base_ring from_base_ring = self.from_base_ring # bound method if from_base_ring.__func__ != generic_from_base_ring: # Custom from_base_ring() use_from_base_ring = True elif isinstance(generic_from_base_ring, lazy_attribute): # If the category implements from_base_ring() as lazy # attribute, then we always use it. # This is for backwards compatibility, see Trac #25181 use_from_base_ring = True else: try: one = self.one() use_from_base_ring = False except (NotImplementedError, AttributeError, TypeError): # The unit is not available, yet. But there are cases # in which it will be available later. So, we use # the generic from_base_ring() after all. use_from_base_ring = True mor = None if use_from_base_ring: mor = SetMorphism(function=from_base_ring, parent=H) else: # We have the multiplicative unit, so implement the # coercion from the base ring as multiplying with that. # # But first we check that it actually works. If not, # then the generic implementation of from_base_ring() # would fail as well so we don't use it. try: if one._lmul_(base_ring.an_element()) is not None: # There are cases in which lmul returns None, # which means that it's not implemented. # One example: Hecke algebras. mor = SetMorphism(function=one._lmul_, parent=H) except (NotImplementedError, AttributeError, TypeError): pass return mor