Ticket #3738: 3738-3-new_coercion_model.patch

File 3738-3-new_coercion_model.patch, 120.5 KB (added by robertwb, 11 years ago)
  • sage/categories/action.pxd

    # HG changeset patch
    # User Robert Bradshaw <robertwb@math.washington.edu>
    # Date 1217319874 25200
    # Node ID 9f227080425a18f8fe886b70929693e96331ed09
    # Parent  fa73e421367165f2d59f2c74485bcfc1a7648f8c
    Pull in new coercion model.
    
    Sage library compiles but doen't start cleanly yet.
    
    diff -r fa73e4213671 -r 9f227080425a sage/categories/action.pxd
    a b cdef class Action(Functor): 
    77    cdef S
    88    cdef bint _is_left
    99    cdef op
    10     cdef Element _call_c(self, a, b)
    11     cdef Element _call_c_impl(self, Element a, Element b)
     10    cpdef Element _call_(self, a, b)
    1211   
    1312   
    1413cdef class InverseAction(Action):
  • sage/categories/action.pyx

    diff -r fa73e4213671 -r 9f227080425a sage/categories/action.pyx
    a b import sage.structure.element 
    3434
    3535include "../ext/stdsage.pxi"
    3636
    37 #def LeftAction(G, S, op=None):
    38 #    return Action(G, S, 1, op)
    39 #   
    40 #def RightAction(G, S, op=None):
    41 #    return Action(G, S, 0, op)
    4237
    4338cdef class Action(Functor):
    4439   
    cdef class Action(Functor): 
    4843        self.G = G
    4944        self.S = S
    5045        self._is_left = is_left
     46        self.op = op
    5147       
    5248    def _apply_functor(self, x):
    5349        return self(x)
    cdef class Action(Functor): 
    6359                raise TypeError, "%s not an element of %s"%(g, self.G)
    6460        elif len(args) == 2:
    6561            if self._is_left:
    66                 return self._call_c(self.G(args[0]), self.S(args[1]))
     62                return self._call_(self.G(args[0]), self.S(args[1]))
    6763            else:
    68                 return self._call_c(self.S(args[0]), self.G(args[1]))
     64                return self._call_(self.S(args[0]), self.G(args[1]))
    6965           
    70     def _call_(self, a, b):
    71         return self._call_c_impl(a, b)
    72            
    73     cdef Element _call_c(self, a, b):
    74         if HAS_DICTIONARY(self):
    75             return self._call_(a, b)
    76         else:
    77             return self._call_c_impl(a, b)
    78            
    79     cdef Element _call_c_impl(self, Element a, Element b):
     66    cpdef Element _call_(self, a, b):
    8067        raise NotImplementedError, "Action not implemented."
    8168       
    8269    def act(self, g, a):
    cdef class Action(Functor): 
    8572        irregardless of whether its a left or right action.
    8673        """
    8774        if self._is_left:
    88             return self._call_c(g, a)
     75            return self._call_(g, a)
    8976        else:
    90             return self._call_c(a, g)
     77            return self._call_(a, g)
    9178           
    9279    def __invert__(self):
    9380        return InverseAction(self)
    cdef class Action(Functor): 
    122109            return self.domain()
    123110        else:
    124111            return self.G
     112           
     113    def operation(self):
     114        return self.op
    125115       
    126116       
    127117cdef class InverseAction(Action):
    cdef class InverseAction(Action): 
    151141                Action.__init__(self, G, action.S, action._is_left)
    152142                self._action = action
    153143                return
     144            else:
     145                K = G.fraction_field()
     146                Action.__init__(self, K, action.S, action._is_left)
     147                self._action = action
     148                return
    154149        except (AttributeError, NotImplementedError):
    155150            pass
    156151        raise TypeError, "No inverse defined for %r." % action
    157152       
    158     cdef Element _call_c(self, a, b):
     153    cpdef Element _call_(self, a, b):
    159154        if self._action._is_left:
    160155            if self.S_precomposition is not None:
    161156                b = self.S_precomposition(b)
    162             return self._action._call_c(~a, b)
     157            return self._action._call_(~a, b)
    163158        else:
    164159            if self.S_precomposition is not None:
    165160                a = self.S_precomposition(a)
    166             return self._action._call_c(a, ~b)
     161            return self._action._call_(a, ~b)
    167162           
    168163    def __invert__(self):
    169164        return self._action
    cdef class PrecomposedAction(Action): 
    192187        self.left_precomposition = left_precomposition
    193188        self.right_precomposition = right_precomposition
    194189       
    195     cdef Element _call_c(self, a, b):
     190    cpdef Element _call_(self, a, b):
    196191        if self.left_precomposition is not None:
    197             a = self.left_precomposition._call_c(a)
     192            a = self.left_precomposition._call_(a)
    198193        if self.right_precomposition is not None:
    199             b = self.right_precomposition._call_c(b)
    200         return self._action._call_c(a, b)
     194            b = self.right_precomposition._call_(b)
     195        return self._action._call_(a, b)
    201196       
    202197    def domain(self):
    203198        if self._is_left and self.right_precomposition is not None:
    cdef class ActionEndomorphism(Morphism): 
    226221        self._action = action
    227222        self._g = g
    228223       
    229     cdef Element _call_c(self, x):
     224    cpdef Element _call_(self, x):
    230225        if self._action._is_left:
    231             return self._action._call_c(self._g, x)
     226            return self._action._call_(self._g, x)
    232227        else:
    233             return self._action._call_c(x, self._g)
     228            return self._action._call_(x, self._g)
    234229               
    235230    def _repr_(self):
    236231        return "Action of %s on %s under %s."%(self._g, self._action.S, self._action)
  • sage/categories/morphism.pxd

    diff -r fa73e4213671 -r 9f227080425a sage/categories/morphism.pxd
    a b cdef class Morphism(Element): 
    44cdef class Morphism(Element):
    55    cdef Parent _domain
    66    cdef Parent _codomain
     7
     8    cdef public int _coerce_cost # a rough measure of the cost of using this morphism in the coercion system.
     9                          # 10 by default, 100 if a DefaultCoercionMorphism, 10000 if inexact.
    710
    811# TODO: remove this requirement when we better understand pickling   
    912#    cdef __dict__
    cdef class Morphism(Element): 
    1215    cdef _extra_slots(self, _dict)
    1316   
    1417    # these methods assume x is an element of domain, and returns an element with parent codomain
    15     cdef Element _call_c(self, x)
    16     cdef Element _call_c_impl(self, Element x)
     18    cpdef Element _call_(self, x)
     19    cpdef Element _call_with_args(self, x, args=*, kwds=*)
    1720
    1821    cpdef domain(self)
    1922
    cdef class Section(Morphism): 
    2326    cdef Morphism _morphism
    2427
    2528cdef class FormalCoercionMorphism(Morphism):
    26     cdef Element _call_c(self, x)
     29    pass
    2730
    2831cdef class FormalCompositeMorphism(Morphism):
    2932    cdef Morphism __first
    3033    cdef Morphism __second
    31     pass
    32  No newline at end of file
     34    pass
     35
     36cdef class SetMorphism(Morphism):
     37    cdef object _function
  • sage/categories/morphism.pyx

    diff -r fa73e4213671 -r 9f227080425a sage/categories/morphism.pyx
    a b cdef class Morphism(Element): 
    4949        Element.__init__(self, parent)
    5050        self._domain = parent.domain()
    5151        self._codomain = parent.codomain()
     52        if self._domain.is_exact() and self._codomain.is_exact():
     53            self._coerce_cost = 10 # default value.
     54        else:
     55            self._coerce_cost = 10000 # inexact morphisms are bad.
    5256       
    5357    cdef _update_slots(self, _slots):
    5458        self._domain = _slots['_domain']
    cdef class Morphism(Element): 
    105109    def __invert__(self):  # notation in python is (~f) for the inverse of f.
    106110        raise NotImplementedError
    107111                       
    108     def __call__(self, x):
     112    def __call__(self, x, *args, **kwds):
    109113        """
    110114        Apply this morphism to x.
    111115
    cdef class Morphism(Element): 
    127131            sage: phi(I)
    128132            Ideal (y, x) of Multivariate Polynomial Ring in x, y over Rational Field
    129133        """
    130         if not PY_TYPE_CHECK(x, Element):
    131             try:
    132                 return self._call_c(x)
    133             except TypeError:
    134                 raise TypeError, "%s must be coercible into %s (and is not an element)"%(x, self._domain)
    135         elif (<Element>x)._parent is not self._domain:
    136             try:
    137                 x = self._domain(x)
    138             except TypeError:
     134        if len(args) == 0 and len(kwds) == 0:       
     135            if not PY_TYPE_CHECK(x, Element):
     136                return self._call_(x)
     137            elif (<Element>x)._parent is not self._domain:
    139138                try:
    140                     return self.pushforward(x)
     139                    x = self._domain(x)
    141140                except TypeError:
    142                     raise TypeError, "%s must be coercible into %s"%(x, self._domain)
    143         return self._call_c(x)
    144 
    145     def _call_(self, x):
    146         return self._call_c_impl(x)
    147 
    148     cdef Element _call_c(self, x):
    149         if HAS_DICTIONARY(self):
     141                    try:
     142                        return self.pushforward(x)
     143                    except (TypeError, NotImplementedError):
     144                        raise TypeError, "%s must be coercible into %s"%(x, self._domain)
    150145            return self._call_(x)
    151146        else:
    152             return self._call_c_impl(x)
    153            
    154     cdef Element _call_c_impl(self, Element x):
     147            if PY_TYPE_CHECK(x, Element):
     148                if (<Element>x)._parent is not self._domain:
     149                    x = self._domain(x)
     150            return self._call_with_args(x, args, kwds)
     151
     152    cpdef Element _call_(self, x):
    155153        raise NotImplementedError
     154
     155    cpdef Element _call_with_args(self, x, args=(), kwds={}):
     156        if len(args) == 0 and len(kwds) == 0:
     157            return self(x)
     158        else:
     159            raise NotImplementedError, "_call_with_args not overridden to accept arguments for %s" % type(self)
    156160
    157161    def pushforward(self, I):
    158162        raise NotImplementedError
    cdef class Morphism(Element): 
    177181
    178182    def _composition_(self, right, homset):
    179183        return FormalCompositeMorphism(homset, right, self)
     184       
     185    def pre_compose(self, right):
     186        if self.domain() is not right.codomain():
     187            right = right.extend_codomain(self.domain())
     188        H = homset.Hom(right.domain(), self.codomain(), self.parent().category())
     189        return self._composition_(right, H)
     190       
     191    def post_compose(self, left):
     192        H = homset.Hom(self.domain(), left.codomain(), self.parent().category())
     193        return left._composition_(self, H)
     194       
     195    def extend_domain(self, new_domain):
     196        r"""
     197        INPUT:
     198            self          -- a member of Hom(Y, Z)
     199            new_codomain  -- an object X such that there is a cannonical
     200                             coercion $\phi$ in Hom(X, Y)
     201                             
     202        OUTPUT:
     203            An element of Hom(X, Z) obtained by composing self with the $\phi$.
     204            If no cannonical $\phi$ exists, a TypeError is raised.
     205       
     206        EXAMPLES:
     207            sage: mor = CDF.coerce_map_from(RDF)
     208            sage: mor.extend_domain(QQ)
     209            Composite morphism:
     210              From: Rational Field
     211              To:   Complex Double Field
     212              Defn:   Native morphism:
     213                      From: Rational Field
     214                      To:   Real Double Field
     215                    then
     216                      Native morphism:
     217                      From: Real Double Field
     218                      To:   Complex Double Field
     219            sage: mor.extend_domain(ZZ['x'])
     220            Traceback (most recent call last):
     221            ...
     222            TypeError: No coercion from Univariate Polynomial Ring in x over Integer Ring to Real Double Field
     223        """
     224        cdef Morphism connecting = self.domain().coerce_map_from(new_domain)
     225        if connecting is None:
     226            raise TypeError, "No coercion from %s to %s" % (new_domain, self.domain())
     227        elif connecting.codomain() is not self.domain():
     228            raise RuntimeError, "BUG: coerce_map_from should always return a map to self (%s)" % self.domain()
     229        else:
     230            return self.pre_compose(connecting)
     231       
     232    def extend_codomain(self, new_codomain):
     233        r"""
     234        INPUT:
     235            self          -- a member of Hom(X, Y)
     236            new_codomain  -- an object Z such that there is a cannonical
     237                             coercion $\phi$ in Hom(Y, Z)
     238                             
     239        OUTPUT:
     240            An element of Hom(X, Z) obtained by composing self with the $\phi$.
     241            If no cannonical $\phi$ exists, a TypeError is raised.
     242       
     243        EXAMPLES:
     244            sage: mor = QQ.coerce_map_from(ZZ)
     245            sage: mor.extend_codomain(RDF)
     246            Composite morphism:
     247              From: Integer Ring
     248              To:   Real Double Field
     249              Defn:   Ring morphism:
     250                      From: Integer Ring
     251                      To:   Rational Field
     252                    then
     253                      Native morphism:
     254                      From: Rational Field
     255                      To:   Real Double Field
     256            sage: mor.extend_codomain(GF(7))
     257            Traceback (most recent call last):
     258            ...
     259            TypeError: No coercion from Rational Field to Finite Field of size 7
     260        """
     261        cdef Morphism connecting = new_codomain.coerce_map_from(self.codomain())
     262        if connecting is None:
     263            raise TypeError, "No coercion from %s to %s" % (self.codomain(), new_codomain)
     264        elif connecting.domain() is not self.codomain():
     265            raise RuntimeError, "BUG: coerce_map_from should always return a map from its input (%s)" % new_codomain
     266        else:
     267            return self.post_compose(connecting)
     268           
     269    def is_injective(self):
     270        raise NotImplementedError, type(self)
     271
     272    def is_surjective(self):
     273        raise NotImplementedError, type(self)
    180274
    181275    def __pow__(self, n, dummy):
    182276        if not self.is_endomorphism():
    183277            raise TypeError, "self must be an endomorphism."
    184278        # todo -- what about the case n=0 -- need to specify the identity map somehow.
    185279        return generic_power(self, n)
     280       
     281    def section(self):
     282        return None
    186283
    187284cdef class Section(Morphism):
    188285    def __init__(self, morphism):
    cdef class FormalCoercionMorphism(Morphi 
    203300    def _repr_type(self):
    204301        return "Coercion"
    205302
    206     # We need to override _call_c in this special case so that FormalCoercionMorphisms can operate on things that are not elements.
    207     cdef Element _call_c(self, x):
    208         return self._codomain._coerce_(x)
     303    cpdef Element _call_(self, x):
     304        return self._codomain.coerce(x)
    209305       
    210306cdef class CallMorphism(Morphism):
    211307
    212308    def _repr_type(self):
    213309        return "Call"
    214310
    215     # We need to override _call_c in this special case so that CallMorphisms can operate on things that are not elements.
    216     cdef Element _call_c(self, x):
     311    cpdef Element _call_(self, x):
    217312        return self._codomain(x)
    218313       
    219314cdef class IdentityMorphism(Morphism):
    cdef class IdentityMorphism(Morphism): 
    226321    def _repr_type(self):
    227322        return "Identity"
    228323
    229     cdef Element _call_c_impl(self, Element x):
     324    cpdef Element _call_(self, x):
    230325        return x
    231326       
     327    cpdef Element _call_with_args(self, x, args=(), kwds={}):
     328        if len(args) == 0 and len(kwds) == 0:
     329            return x
     330        elif self._codomain._element_init_pass_parent:
     331            return self._codomain._element_class(self._codomain, x, *args, **kwds)
     332        else:
     333            return self._codomain._element_class(x, *args, **kwds)
     334
    232335    def __mul__(left, right):
    233336        if not isinstance(right, Morphism):
    234337            raise TypeError, "right (=%s) must be a morphism to multiply it by %s"%(right, left)
    cdef class FormalCompositeMorphism(Morph 
    253356        Morphism.__init__(self, parent)
    254357        self.__first = first
    255358        self.__second = second
     359        self._coerce_cost = (<Morphism>first)._coerce_cost + (<Morphism>second)._coerce_cost
    256360
    257361    cdef _update_slots(self, _slots):
    258362        self.__first = _slots['__first']
    cdef class FormalCompositeMorphism(Morph 
    264368        _slots['__second'] = self.__second
    265369        return Morphism._extra_slots(self, _slots)
    266370
    267     cdef Element _call_c_impl(self, Element x):
    268         return self.__second(self.__first(x))
     371    cpdef Element _call_(self, x):
     372        return self.__second._call_(self.__first._call_(x))
    269373
     374    cpdef Element _call_with_args(self, x, args=(), kwds={}):
     375        return self.__second._call_with_args(self.__first._call_(x), args, kwds)
     376   
    270377    def _repr_type(self):
    271378        return "Composite"
    272379
    cdef class FormalCompositeMorphism(Morph 
    288395        """
    289396        return self.__second
    290397       
     398    def is_injective(self):
     399        if self.__first.is_injective():
     400            if self.__second.is_injective():
     401                return True
     402            elif self.__first.is_surjective():
     403                return False
     404            else:
     405                raise NotImplementedError, "Not enough information to deduce injectivity."
     406        else:
     407            return False
     408       
     409    def is_surjective(self):
     410        if self.__second.is_surjective():
     411            if self.__first.is_surjective():
     412                return True
     413            elif self.__second.is_injective():
     414                return False
     415            else:
     416                raise NotImplementedError, "Not enough information to deduce surjectivity."
     417        else:
     418            return False
     419       
     420cdef class SetMorphism(Morphism):
     421    def __init__(self, parent, function):
     422        """
     423        INPUT:
     424        parent -- a Homset
     425        function -- a Python function that takes elements of the domain as input
     426                    and returns elements of the domain.
     427        """
     428        self._function = function
     429
     430    cpdef Element _call_(self, x):
     431        return self._function(x)
     432       
  • sage/matrix/action.pyx

    diff -r fa73e4213671 -r 9f227080425a sage/matrix/action.pyx
    a b cdef class MatrixMatrixAction(MatrixMulA 
    8484            raise TypeError, "incompatible dimensions %s, %s" % (self.G.ncols(),  self.S.nrows())
    8585        return MatrixSpace(base, self.G.nrows(), self.S.ncols(), sparse = self.G.is_sparse() and self.S.is_sparse())
    8686       
    87     cdef Element _call_c_impl(self, Element g, Element s):
     87    cpdef Element _call_(self, g, s):
    8888        """
    8989        EXAMPLES:
    9090        Respects compatable subdivisions:
    cdef class MatrixVectorAction(MatrixMulA 
    180180            raise TypeError, "incompatible dimensions %s, %s" % (self.G.ncols(),  self.S.degree())
    181181        return FreeModule(base, self.G.nrows(), sparse = self.G.is_sparse())
    182182       
    183     cdef Element _call_c_impl(self, Element g, Element s):
     183    cpdef Element _call_(self, g, s):
    184184        cdef Matrix A = g #<Matrix>g
    185185        cdef Vector v = s #<Vector>s
    186186        if A._parent._base is not self._codomain._base:
    cdef class VectorMatrixAction(MatrixMulA 
    222222            raise TypeError, "incompatible dimensions %s, %s" % (self.G.nrows(), self.S.degree())
    223223        return FreeModule(base, self.G.ncols(), sparse = self.G.is_sparse())
    224224       
    225     cdef Element _call_c_impl(self, Element s, Element g):
     225    cpdef Element _call_(self, s, g):
    226226        cdef Matrix A = g #<Matrix>g
    227227        cdef Vector v = s #<Vector>s
    228228        if A._parent._base is not self._codomain._base:
  • sage/rings/integer.pyx

    diff -r fa73e4213671 -r 9f227080425a sage/rings/integer.pyx
    a b cdef class int_to_Z(Morphism): 
    36303630        import sage.categories.homset
    36313631        from sage.structure.parent import Set_PythonType
    36323632        Morphism.__init__(self, sage.categories.homset.Hom(Set_PythonType(int), integer_ring.ZZ))
    3633     cdef Element _call_c(self, a):
    3634         # Override this _call_c rather than _call_c_impl because a is not an element
     3633    cpdef Element _call_(self, a):
    36353634        cdef Integer r
    36363635        r = <Integer>PY_NEW(Integer)
    36373636        mpz_set_si(r.value, PyInt_AS_LONG(a))
    cdef class long_to_Z(Morphism): 
    36563655        import sage.categories.homset
    36573656        from sage.structure.parent import Set_PythonType
    36583657        Morphism.__init__(self, sage.categories.homset.Hom(Set_PythonType(long), integer_ring.ZZ))
    3659     cdef Element _call_c(self, a):
     3658    cpdef Element _call_(self, a):
    36603659        cdef Integer r
    36613660        r = <Integer>PY_NEW(Integer)
    36623661        mpz_set_pylong(r.value, a)
  • sage/structure/coerce.pxd

    diff -r fa73e4213671 -r 9f227080425a sage/structure/coerce.pxd
    a b from sage.categories.morphism cimport Mo 
    77
    88from coerce_dict cimport TripleDict, TripleDictIter
    99
    10 cdef class CoercionModel_original(CoercionModel):
    11     pass
    12 
    13 cdef class CoercionModel_cache_maps(CoercionModel_original):
     10cdef class CoercionModel_cache_maps(CoercionModel):
    1411    # This MUST be a mapping to tuples, where each
    1512    # tuple contains at least two elements that are either
    1613    # None or of type Morphism.
    cdef class CoercionModel_cache_maps(Coer 
    1916    # This MUST be a mapping to actions.
    2017    cdef TripleDict _action_maps
    2118   
    22     cdef coercion_maps_c(self, R, S)
    23     cdef discover_coercion_c(self, R, S)
     19    cpdef coercion_maps(self, R, S)
     20    cpdef discover_coercion(self, R, S)
    2421    cpdef verify_coercion_maps(self, R, S, homs, bint fix=*)
     22    cpdef verify_action(self, action, R, S, op, bint fix=*)
    2523
    26     cdef get_action_c(self, R, S, op)
    27     cdef discover_action_c(self, R, S, op)
    28     cpdef verify_action(self, action, R, S, op, bint fix=*)
    29    
    30 cdef class CoercionModel_profile(CoercionModel_cache_maps):
    31     cdef object profiling_info
    32     cdef object timer
    33     cdef void _log_time(self, xp, yp, op, time, data)
     24    cpdef get_action(self, xp, yp, op)
     25    cpdef discover_action(self, R, S, op)
    3426
    35 
    36 cdef class LeftModuleAction(Action):
    37     cdef Morphism connecting
    38     cdef extended_base
    39 
    40 cdef class RightModuleAction(Action):
    41     cdef Morphism connecting
    42     cdef extended_base
    43     cdef bint is_inplace
    44 
    45 cdef class PyScalarAction(Action):
    46     cdef Action _action
    47  No newline at end of file
     27    cdef readonly _exception_stack
     28    cdef bint _exceptions_cleared
     29   
     30 No newline at end of file
  • sage/structure/coerce.pyx

    diff -r fa73e4213671 -r 9f227080425a sage/structure/coerce.pyx
    a b  
     1r"""
     2The coercion model manages how elements of one parent get relate to elements
     3of another. For example, the integer 2 can canonically be viewed as an element
     4of the rational numbers. (The Parent of a non-element is its Python type.)
     5
     6    sage: ZZ(2).parent()
     7    Integer Ring
     8    sage: QQ(2).parent()
     9    Rational Field
     10
     11The most prominent role of the coercion model is to make sense of binary
     12operations between elements that have distinct parents. It does this by
     13finding a parent where both elements make sense, and doing the operation
     14there. For example
     15
     16    sage: a = 1/2; a.parent()
     17    Rational Field
     18    sage: b = ZZ[x].gen(); b.parent()
     19    Univariate Polynomial Ring in x over Integer Ring
     20    sage: a+b
     21    x + 1/2
     22    sage: (a+b).parent()
     23    Univariate Polynomial Ring in x over Rational Field
     24   
     25If there is a coercion (see below) from one of the parents to the other,
     26the operation is always performed in the codomain of that coercion. Otherwise
     27a reasonable attempt to create a new parent with coercion maps from both
     28original parents is made. The results of these discoveries are cached.
     29On failure, a TypeError is always raised.
     30
     31Some arithmetic operations (such as multiplication) can indicate an action
     32rather than arithmitic in a common parent. For example
     33
     34    sage: E = EllipticCurve('37a')
     35    sage: P = E(0,0)
     36    sage: 5*P
     37    (1/4 : -5/8 : 1)
     38   
     39where there is action of $\Z$ on the points of $E$ given by the additive
     40group law. Parents can specify how they act on or are acted upon by other
     41parents.
     42   
     43There are two kinds of ways to get from one parent to another, coercions and
     44conversions.
     45
     46Coercions are cannonical (possibly modulo a finite number of determanistic
     47choices) morphisms, and the set of all coercions between all parents forms a
     48comuting diagram (modulo possibly rounding issues). $\Z \rightarrow \Q$ is an
     49example of a coercion. These are invoked implictly by the coercion model.
     50
     51Conversions try to construct an element out of their input if at all possible.
     52Examples include sections of coercions, creating an element from a string or
     53list, etc. and may fail on some inputs of a given type while succeeding on
     54others (i.e. they may not be defined on the whole domain). Conversions are
     55always explicitly invoked, and never used by the coercion model to resolve
     56binary operations.
     57
     58For more information on how to specify coercions, conversions, and actions,
     59see the documentation for Parent.
     60"""
     61
     62
    163#*****************************************************************************
    264#       Copyright (C) 2007 Robert Bradshaw <robertwb@math.washington.edu>
    365#
     
    769#*****************************************************************************
    870
    971
    10 cdef extern from *:
    11     ctypedef struct RefPyObject "PyObject":
    12         int ob_refcnt
    13 
    1472include "../ext/stdsage.pxi"
    15 include "../ext/interrupt.pxi"
    1673include "../ext/python_object.pxi"
    17 include "../ext/python_number.pxi"
    18 include "../ext/python_int.pxi"
    1974include "coerce.pxi"
    2075
    2176import operator
    22 import re
    23 import time
    2477
    2578from sage_object cimport SageObject
    2679import sage.categories.morphism
    from sage.categories.action import Inver 
    2881from sage.categories.action import InverseAction, PrecomposedAction
    2982from parent import Set_PythonType
    3083
    31 from element import py_scalar_to_element
     84import sys, traceback
    3285
    33 def py_scalar_parent(py_type):
     86from coerce_actions import LeftModuleAction, RightModuleAction, PyScalarAction, IntegerMulAction
     87
     88cpdef py_scalar_parent(py_type):
     89    """
     90    Returns the Sage equivalent of the given python type, if one exists.
     91    If there is no equivalent, return None.
     92   
     93    EXAMPLES:
     94        sage: from sage.structure.coerce import py_scalar_parent
     95        sage: py_scalar_parent(int)
     96        Integer Ring
     97        sage: py_scalar_parent(long)
     98        Integer Ring
     99        sage: py_scalar_parent(float)
     100        Real Double Field
     101        sage: py_scalar_parent(complex)
     102        Complex Double Field
     103        sage: py_scalar_parent(dict),
     104        (None,)
     105    """
    34106    if py_type is int or py_type is long:
    35107        import sage.rings.integer_ring
    36108        return sage.rings.integer_ring.ZZ
    def py_scalar_parent(py_type): 
    42114        return sage.rings.complex_double.CDF
    43115    else:
    44116        return None
     117   
     118cdef object _Integer
     119cdef bint is_Integer(x):
     120    global _Integer
     121    if _Integer is None:
     122        from sage.rings.integer import Integer as _Integer
     123    return PY_TYPE_CHECK_EXACT(x, _Integer) or PY_TYPE_CHECK_EXACT(x, int)
    45124
    46 
    47 cdef class CoercionModel_original(CoercionModel):
    48     """
    49     This is the original coercion model, as of SAGE 2.6 (2007-06-02)
    50     """
    51    
    52     cdef canonical_coercion_c(self, x, y):
    53         cdef int i
    54         xp = parent_c(x)
    55         yp = parent_c(y)
    56         if xp is yp:
    57             return x, y
    58 
    59         if PY_IS_NUMERIC(x):
    60             try:
    61                 x = yp(x)
    62             except TypeError:
    63                 y = x.__class__(y)
    64                 return x, y
    65             # Calling this every time incurs overhead -- however, if a mistake
    66             # gets through then one can get infinite loops in C code hence core
    67             # dumps.  And users define _coerce_ and __call__ for rings, which
    68             # can easily have bugs in it, i.e., not really make the element
    69             # have the correct parent.  Thus this check is *crucial*.
    70             return _verify_canonical_coercion_c(x,y)
    71        
    72         elif PY_IS_NUMERIC(y):
    73             try:
    74                 y = xp(y)
    75             except TypeError:
    76                 x = y.__class__(x)
    77                 return x, y           
    78             return _verify_canonical_coercion_c(x,y)
    79 
    80         try:
    81             if xp.has_coerce_map_from(yp):
    82                 y = (<Parent>xp)._coerce_c(y)
    83                 return _verify_canonical_coercion_c(x,y)
    84         except AttributeError:
    85             pass
    86         try:
    87             if yp.has_coerce_map_from(xp):
    88                 x = (<Parent>yp)._coerce_c(x)
    89                 return _verify_canonical_coercion_c(x,y)
    90         except AttributeError:
    91             pass
    92         raise TypeError, "no common canonical parent for objects with parents: '%s' and '%s'"%(xp, yp)
    93 
    94     cdef canonical_base_coercion_c(self, Element x, Element y):
    95         if not have_same_base(x, y):
    96             if (<Parent> x._parent._base).has_coerce_map_from_c(y._parent._base):
    97                 # coerce all elements of y to the base ring of x
    98                 y = y.base_extend_c(x._parent._base)
    99             elif (<Parent> y._parent._base).has_coerce_map_from_c(x._parent._base):
    100                 # coerce x to have elements in the base ring of y
    101                 x = x.base_extend_c(y._parent._base)
    102         return x,y
    103 
    104     def canonical_base_coercion(self, x, y):
    105         try:
    106             xb = x.base_ring()
    107         except AttributeError:
    108             #raise TypeError, "unable to find base ring for %s (parent: %s)"%(x,x.parent())
    109             raise TypeError, "unable to find base ring"
    110         try:
    111             yb = y.base_ring()
    112         except AttributeError:
    113             raise TypeError, "unable to find base ring"
    114             #raise TypeError, "unable to find base ring for %s (parent: %s)"%(y,y.parent())
    115         try:
    116             b = self.canonical_coercion_c(xb(0),yb(0))[0].parent()
    117         except TypeError:
    118             raise TypeError, "unable to find base ring"
    119             #raise TypeError, "unable to find a common base ring for %s (base ring: %s) and %s (base ring %s)"%(x,xb,y,yb)
    120         return x.change_ring(b), y.change_ring(b)
    121 
    122 
    123     cdef bin_op_c(self, x, y, op):
    124         """
    125         Compute x op y, where coercion of x and y works according to
    126         SAGE's coercion rules.
    127         """
    128         # Try canonical element coercion.
    129         try:
    130             x1, y1 = self.canonical_coercion_c(x, y)
    131             return op(x1,y1)       
    132         except TypeError, msg:
    133             # print msg  # this can be useful for debugging.
    134             if not op is mul:
    135                 raise TypeError, arith_error_message(x,y,op)
    136 
    137         # If the op is multiplication, then some other algebra multiplications
    138         # may be defined
    139        
    140         # 2. Try scalar multiplication.
    141         # No way to multiply x and y using the ``coerce into a canonical
    142         # parent'' rule.
    143         # The next rule to try is scalar multiplication by coercing
    144         # into the base ring.
    145         cdef bint x_is_modelt, y_is_modelt
    146 
    147         y_is_modelt = PY_TYPE_CHECK(y, ModuleElement)
    148         if y_is_modelt:
    149             # First try to coerce x into the base ring of y if y is an element.
    150             try:
    151                 R = (<ModuleElement> y)._parent._base
    152                 if R is None:
    153                     raise RuntimeError, "base of '%s' must be set to a ring (but it is None)!"%((<ModuleElement> y)._parent)
    154                 x = (<Parent>R)._coerce_c(x)
    155                 return (<ModuleElement> y)._rmul_c(x)     # the product x * y
    156             except TypeError, msg:
    157                 pass
    158 
    159         x_is_modelt = PY_TYPE_CHECK(x, ModuleElement)
    160         if x_is_modelt:
    161             # That did not work.  Try to coerce y into the base ring of x.
    162             try:
    163                 R = (<ModuleElement> x)._parent._base
    164                 if R is None:
    165                     raise RuntimeError, "base of '%s' must be set to a ring (but it is None)!"%((<ModuleElement> x)._parent)           
    166                 y = (<Parent> R)._coerce_c(y)
    167                 return (<ModuleElement> x)._lmul_c(y)    # the product x * y
    168             except TypeError:
    169                 pass
    170 
    171         if y_is_modelt and x_is_modelt:
    172             # 3. Both canonical coercion failed, but both are module elements.
    173             # Try base extending the right object by the parent of the left
    174 
    175             ## TODO -- WORRY -- only unambiguous if one succeeds!
    176             if  PY_TYPE_CHECK(x, RingElement):
    177                 try:
    178                     return x * y.base_extend((<RingElement>x)._parent)
    179                 except (TypeError, AttributeError), msg:
    180                     pass
    181             # Also try to base extending the left object by the parent of the right
    182             if  PY_TYPE_CHECK(y, RingElement):
    183                 try:
    184                     return y * x.base_extend((<Element>y)._parent)
    185                 except (TypeError, AttributeError), msg:
    186                     pass
    187 
    188         # 4. Try _l_action or _r_action.
    189         # Test to see if an _r_action or _l_action is
    190         # defined on either side.
    191         try:
    192             return x._l_action(y)
    193         except (AttributeError, TypeError):   
    194             pass
    195         try:
    196             return y._r_action(x)
    197         except (AttributeError, TypeError):
    198             pass
    199            
    200         raise TypeError, arith_error_message(x,y,op)
    201        
    202 
    203 cdef class CoercionModel_cache_maps(CoercionModel_original):
     125cdef class CoercionModel_cache_maps(CoercionModel):
    204126    """
    205127    See also sage.categories.pushout
    206128   
    cdef class CoercionModel_cache_maps(Coer 
    221143        sage: ZZ['x,y,z'].0 + ZZ['w,x,z,a'].1
    222144        2*x
    223145       
    224     If a class is not part of the coercion system, we should call the
    225     __rmul__ method when it makes sense.
    226 
    227         sage: class Foo:                 
    228         ...      def __rmul__(self, left):
    229         ...          return 'hello'
    230         ...
    231         sage: H = Foo()
    232         sage: print int(3)*H
    233         hello
    234         sage: print Integer(3)*H
    235         hello
    236         sage: print H*3
    237         Traceback (most recent call last):
    238         ...
    239         TypeError: unsupported operand parent(s) for '*': '<type 'instance'>' and 'Integer Ring'
    240 
    241 
    242146    AUTHOR:
    243147        -- Robert Bradshaw
    244148    """
    245149
    246     def __init__(self, lookup_dict_sizes=137):
     150    def __init__(self, lookup_dict_size=127, lookup_dict_threshold=.75):
     151        """
     152        INPUT:
     153            lookup_dict_size -- initial size of the coercion hashtables
     154            lookup_dict_threshold -- maximal density of the coercion hashtables before forcing a re-hash
     155           
     156        EXAMPLES:
     157            sage: from sage.structure.coerce import CoercionModel_cache_maps
     158            sage: cm = CoercionModel_cache_maps(4, .95)
     159            sage: A = cm.get_action(ZZ, NumberField(x^2-2, 'a'), operator.mul)
     160            sage: f, g = cm.coercion_maps(QQ, int)
     161            sage: f, g = cm.coercion_maps(ZZ, int)
     162            sage: cm.get_stats()
     163            ((0, 1.0, 4), (0, 0.25, 1))
     164           
     165        NOTE: In practice 4 would be a really bad number to choose, but it makes the
     166              hashing deterministic.
     167        """
     168        self.reset_cache(lookup_dict_size, lookup_dict_threshold)
     169       
     170    def reset_cache(self, lookup_dict_size=127, lookup_dict_threshold=.75):
     171        """
     172        Clear the coercion cache.
     173       
     174        This should have no impact on the result of arithmetic operations, as
     175        the exact same coercions and actions will be re-discovered when needed.
     176       
     177        It may be useful for debugging, and may also free some memory.
     178       
     179        EXAMPLES:
     180            sage: cm = sage.structure.element.get_coercion_model()
     181            sage: cm.get_stats()                # random
     182            ((0, 0.3307086614173229, 3), (0, 0.1496062992125984, 2))
     183            sage: cm.reset_cache()
     184            sage: cm.get_stats()
     185            ((0, 0.0, 0), (0, 0.0, 0))
     186        """
    247187        # This MUST be a mapping of tuples, where each
    248188        # tuple contains at least two elements that are either
    249189        # None or of type Morphism.
    250         self._coercion_maps = TripleDict(lookup_dict_sizes)
    251         # This MUST be a mapping of actions.
    252         self._action_maps = TripleDict(lookup_dict_sizes)
     190        self._coercion_maps = TripleDict(lookup_dict_size, threshold=lookup_dict_threshold)
     191        # This MUST be a mapping to actions.
     192        self._action_maps = TripleDict(lookup_dict_size, threshold=lookup_dict_threshold)
     193
     194    def get_cache(self):
     195        """
     196        This returns the current cache of coercion maps and actions, primarily
     197        useful for debugging and introspection.
    253198       
    254     def get_cache(self):
    255         return dict(self._coercion_maps.iteritems()), dict(self._action_maps.iteritems())
     199        EXAMPLES:
     200            sage: 1 + 1/2
     201            3/2
     202            sage: cm = sage.structure.element.get_coercion_model()
     203            sage: maps, actions = cm.get_cache()
     204           
     205        Now lets see what happens when we do a binary operations with
     206        an integer and a rational:
     207            sage: left_morphism, right_morphism = maps[ZZ, QQ]
     208            sage: print left_morphism
     209            Ring morphism:
     210              From: Integer Ring
     211              To:   Rational Field
     212            sage: print right_morphism
     213            None
     214           
     215        We can see that it coerces the left operand from an integer to a
     216        rational, and doesn't do anything to the right.
     217       
     218        Now for some actions:
     219       
     220            sage: R.<x> = ZZ['x']
     221            sage: 1/2 * x
     222            1/2*x
     223            sage: maps, actions = cm.get_cache()
     224            sage: act = actions[QQ, R, operator.mul]; act
     225            Left scalar multiplication by Rational Field on Univariate Polynomial Ring in x over Integer Ring
     226            sage: act.actor()
     227            Rational Field
     228            sage: act.domain()
     229            Univariate Polynomial Ring in x over Integer Ring
     230            sage: act.codomain()
     231            Univariate Polynomial Ring in x over Rational Field
     232            sage: act(1/5, x+10)
     233            1/5*x + 2
     234        """
     235        return dict([((S, R), mors) for (S, R, op), mors in self._coercion_maps.iteritems()]), \
     236               dict(self._action_maps.iteritems())
    256237       
    257238    def get_stats(self):
     239        """
     240        This returns the state of the cache of coercion maps and actions,
     241        primarily useful for debugging and introspection.
     242    If a class is not part of the coercion system, we should call the
     243    __rmul__ method when it makes sense.
     244       
     245        The coercion maps are stored in a specialized TripleDict hashtable,
     246        and the stats returned are (min, avg, max) of the number of items
     247        per bucket. The lower the better.
     248       
     249        EXAMPLES:
     250            sage: cm = sage.structure.element.get_coercion_model()
     251            sage: cm.get_stats()                # random
     252            ((0, 0.16058394160583941, 2), (0, 0.13138686131386862, 3))
     253        """
    258254        return self._coercion_maps.stats(), self._action_maps.stats()
    259255       
    260     cdef bin_op_c(self, x, y, op):
    261         if (op is not add) and (op is not sub) and (op is not iadd) and (op is not isub):
     256    def _record_exception(self):
     257        r"""
     258        Pushes the last exception that occured onto the stack for later reference,
     259        for internal use.
     260       
     261        If the stack has not yet been flagged as cleared, we clear it now (rather
     262        than wasting time to do so for successful operations).
     263       
     264        TEST:
     265            sage: cm = sage.structure.element.get_coercion_model()
     266            sage: 1+1/2+2 # make sure there aren't any errors hanging around
     267            7/2
     268            sage: cm.exception_stack()
     269            []
     270            sage: cm._test_exception_stack()
     271            sage: cm.exception_stack()
     272            ['Traceback (most recent call last):...TypeError: just a test\n']
     273
     274            The function _test_exception_stack is executing the following code:
     275            try:
     276                raise TypeError, "just a test"
     277            except:
     278                cm._record_exception()
     279        """
     280        if not self._exceptions_cleared:
     281            self._exception_stack = []
     282            self._exceptions_cleared = True
     283        self._exception_stack.append(traceback.format_exc(sys.exc_info()[2]))
     284
     285    def _test_exception_stack(self):
     286        """
     287        A function to test the exception stack.
     288       
     289        EXAMPLES:
     290            sage: cm = sage.structure.element.get_coercion_model()
     291            sage: 1 + 1/11 # make sure there aren't any errors hanging around
     292            12/11
     293            sage: cm.exception_stack()
     294            []
     295            sage: cm._test_exception_stack()
     296            sage: cm.exception_stack()
     297            ['Traceback (most recent call last):...TypeError: just a test\n']
     298        """
     299        try:
     300            raise TypeError, "just a test"
     301        except:
     302            self._record_exception()
     303       
     304    def exception_stack(self):
     305        """
     306        Returns the list of exceptions that were caught in the course of
     307        executing the last binary operation. Useful for diagnosis when
     308        user-defined morphisms or actions raise exceptions that are caught in
     309        the course of coercion detection.
     310       
     311        If all went well, this should be the empty list. If things aren't
     312        happening as you expect, this is a good place to check.
     313       
     314        EXAMPLES:
     315            sage: cm = sage.structure.element.get_coercion_model()
     316            sage: 1/2 + 2
     317            5/2
     318            sage: cm.exception_stack()
     319            []
     320            sage: 1/2 + GF(3)(2)
     321            Traceback (most recent call last):
     322            ...
     323            TypeError: unsupported operand parent(s) for '+': 'Rational Field' and 'Finite Field of size 3'
     324           
     325        Now see what the actual problem was:
     326            sage: for e in cm.exception_stack(): print e
     327            Traceback (most recent call last):
     328            ...
     329            TypeError: no common canonical parent for objects with parents: 'Rational Field' and 'Finite Field of size 3'
     330           
     331        """
     332        if not self._exceptions_cleared:
     333            self._exception_stack = []
     334            self._exceptions_cleared = True
     335        return self._exception_stack
     336       
     337
     338    def explain(self, xp, yp, op=operator.mul, int verbosity=2):
     339        """
     340        This function can be used to understand what coercions will happen
     341        for an arithmetic operation between xp and yp (which may be either
     342        elements or parents). If the parent of the result can be determined
     343        then it will be returned.
     344       
     345        EXAMPLES:
     346            sage: cm = sage.structure.element.get_coercion_model()
     347           
     348            sage: cm.explain(ZZ, ZZ)
     349            Identical parents, arithmetic performed immediately.
     350            Result lives in Integer Ring
     351            Integer Ring
     352
     353            sage: cm.explain(QQ, int)
     354            Coercion on right operand via
     355                Native morphism:
     356                  From: Set of Python objects of type 'int'
     357                  To:   Rational Field
     358            Arithmetic performed after coercions.
     359            Result lives in Rational Field
     360            Rational Field
     361
     362            sage: cm.explain(ZZ['x'], QQ)
     363            Action discovered.
     364                Right scalar multiplication by Rational Field on Univariate Polynomial Ring in x over Integer Ring
     365            Result lives in Univariate Polynomial Ring in x over Rational Field
     366            Univariate Polynomial Ring in x over Rational Field
     367
     368            sage: cm.explain(ZZ['x'], QQ, operator.add)
     369            Coercion on left operand via
     370                Conversion morphism:
     371                  From: Univariate Polynomial Ring in x over Integer Ring
     372                  To:   Univariate Polynomial Ring in x over Rational Field
     373            Coercion on right operand via
     374                Conversion morphism:
     375                  From: Rational Field
     376                  To:   Univariate Polynomial Ring in x over Rational Field
     377            Arithmetic performed after coercions.
     378            Result lives in Univariate Polynomial Ring in x over Rational Field
     379            Univariate Polynomial Ring in x over Rational Field
     380
     381        Sometimes with non-sage types there is not enough information to deduce
     382        what will actually happen:
     383       
     384            sage: cm.explain(RealField(100), float, operator.add)
     385            Right operand is numeric, will attempt conversion in both directions.
     386            Unknown result parent.
     387            sage: parent(RealField(100)(1) + float(1))
     388            Real Field with 100 bits of precision
     389            sage: cm.explain(QQ, float, operator.add)
     390            Right operand is numeric, will attempt conversion in both directions.
     391            Unknown result parent.
     392            sage: parent(QQ(1) + float(1))
     393            <type 'float'>
     394           
     395        NOTE: This function is accurate only in so far as analyze is kept in
     396              sync with the \code{bin_op} and \code{canonical_coercion} which
     397              are kept seperate for maximal efficiency.
     398        """
     399        all, res = self.analyze(xp, yp, op)
     400        indent = " "*4
     401        if verbosity >= 2:
     402            print "\n".join([s if isinstance(s, str) else indent+(repr(s).replace("\n", "\n"+indent)) for s in all])
     403        elif verbosity >= 1:
     404            print "\n".join([s for s in all if isinstance(s, str)])
     405        if verbosity >= 1:
     406            if res is None:
     407                print "Unknown result parent."
     408            else:
     409                print "Result lives in", res
     410        return res
     411
     412    def analyze(self, xp, yp, op=operator.mul):
     413        """
     414        Emulate the process of doing arithmetic between xp and yp, returning
     415        a list of steps and the parent that the result will live in. The
     416        \code{explain} function is easier to use, but if one wants access to
     417        the acutal morphism and action objects (rather than their string
     418        representations) then this is the function to use.
     419       
     420        EXAMPLES:
     421            sage: cm = sage.structure.element.get_coercion_model()
     422            sage: steps, res = cm.analyze(GF(7), ZZ)
     423            sage: print steps
     424            ['Coercion on right operand via', Natural morphism:
     425              From: Integer Ring
     426              To:   Finite Field of size 7, 'Arithmetic performed after coercions.']
     427            sage: print res
     428            Finite Field of size 7
     429            sage: f = steps[1]; type(f)
     430            <type 'sage.rings.integer_mod.Integer_to_IntegerMod'>
     431            sage: f(100)
     432            2
     433        """
     434        self._exceptions_cleared = False
     435        if not PY_TYPE_CHECK(xp, type) and not PY_TYPE_CHECK(xp, Parent):
     436            xp = parent_c(xp)
     437        if not PY_TYPE_CHECK(yp, type) and not PY_TYPE_CHECK(yp, Parent):
     438            yp = parent_c(yp)
     439       
     440        all = []
     441        if xp is yp:
     442            all.append("Identical parents, arithmetic performed immediately." % xp)
     443            return all, xp
     444        if xp == yp:
     445            all.append("Equal but distinct parents.")
     446           
     447        if (op is not sub) and (op is not isub):
     448            action = self.get_action(xp, yp, op)
     449            if action is not None:
     450                all.append("Action discovered.")
     451                all.append(action)
     452                return all, action.codomain()
     453                   
     454        homs = self.discover_coercion(xp, yp)
     455        if homs is not None:
     456            x_mor, y_mor = homs
     457            if x_mor is not None:
     458                all.append("Coercion on left operand via")
     459                all.append(x_mor)
     460                res = x_mor.codomain()
     461            if y_mor is not None:
     462                all.append("Coercion on right operand via")
     463                all.append(y_mor)
     464                if res is not None and res is not y_mor.codomain():
     465                    raise RuntimeError, ("BUG in coercion model: codomains not equal!", x_mor, y_mor)
     466                res = y_mor.codomain()
     467            all.append("Arithmetic performed after coercions.")
     468            return all, res
     469               
     470        if PY_TYPE_CHECK(yp, Parent) and xp in [int, long, float, complex, bool]:
     471            mor = yp.coerce_map_from(xp)
     472            if mor is not None:
     473                all.append("Coercion on numeric left operand via")
     474                all.append(mor)
     475                return all, yp
     476            all.append("Left operand is numeric, will attempt conversion in both directions.")
     477        elif type(xp) is type:
     478            all.append("Left operand is not Sage element, will try _sage_.")
     479
     480        if PY_TYPE_CHECK(xp, Parent) and yp in [int, long, float, complex, bool]:
     481            mor = xp.coerce_map_from(yp)
     482            if mor is not None:
     483                all.append("Coercion on numeric right operand via")
     484                all.append(mor)
     485                return all, xp
     486            all.append("Right operand is numeric, will attempt conversion in both directions.")
     487        elif type(yp) is type:
     488            all.append("Right operand is not Sage element, will try _sage_.")
     489           
     490        if op is mul or op is imul:
     491            all.append("Will try _r_action and _l_action")
     492           
     493        return all, None
     494       
     495   
     496    cpdef bin_op(self, x, y, op):
     497        """
     498        Execute the operation op on x and y. It first looks for an action
     499        corresponding to op, and failing that, it tries to coerces x and y
     500        into the a common parent and calls op on them.
     501       
     502        If it cannot make sense of the operation, a TypeError is raised.
     503       
     504        INPUT:
     505            x  -- the left operand
     506            y  -- the right operand
     507            op -- a python function taking 2 arguments
     508                  Note: op is often an arithmitic operation, but need
     509                  not be so.
     510                   
     511        EXAMPLES:
     512            sage: cm = sage.structure.element.get_coercion_model()
     513            sage: cm.bin_op(1/2, 5, operator.mul)
     514            5/2
     515           
     516        The operator can be any callable:
     517            set Rational Field Integer Ring <function <lambda> at 0xc0b2270> None None
     518            (Rational Field, Rational Field)
     519            sage: R.<x> = ZZ['x']
     520            sage: cm.bin_op(x^2-1, x+1, gcd)
     521            x + 1
     522           
     523        Actions are detected and performed:
     524            sage: M = matrix(ZZ, 2, 2, range(4))
     525            sage: V = vector(ZZ, [5,7])
     526            sage: cm.bin_op(M, V, operator.mul)
     527            (7, 31)
     528           
     529        TESTS:
     530            sage: class Foo:                 
     531            ...      def __rmul__(self, left):
     532            ...          return 'hello'
     533            ...
     534            sage: H = Foo()
     535            sage: print int(3)*H
     536            hello
     537            sage: print Integer(3)*H
     538            hello
     539            sage: print H*3
     540            Traceback (most recent call last):
     541            ...
     542            TypeError: unsupported operand parent(s) for '*': '<type 'instance'>' and 'Integer Ring'
     543     
     544        """
     545        self._exceptions_cleared = False
     546        if (op is not sub) and (op is not isub):
    262547            # Actions take preference over common-parent coercions.
    263548            xp = parent_c(x)
    264549            yp = parent_c(y)
    265550            if xp is yp:
    266551                return op(x,y)
    267             action = self.get_action_c(xp, yp, op)
     552            action = self.get_action(xp, yp, op)
    268553            if action is not None:
    269                 return (<Action>action)._call_c(x, y)
     554                return (<Action>action)._call_(x, y)
    270555       
    271556        try:
    272             xy = self.canonical_coercion_c(x,y)
     557            xy = self.canonical_coercion(x,y)
    273558            return PyObject_CallObject(op, xy)
    274         except TypeError, msg:
    275 #            raise
    276 #            print msg
    277             pass
     559        except TypeError, err:
     560            self._record_exception()
    278561
    279562        if op is mul or op is imul:
    280563               
    cdef class CoercionModel_cache_maps(Coer 
    282565            # (e.g. sequences or parents)
    283566            try:
    284567                return x._l_action(y)
    285             except (AttributeError, TypeError):   
    286                 pass
     568            except (AttributeError, TypeError), err:   
     569                self._record_exception()
    287570            try:
    288571                return y._r_action(x)
    289             except (AttributeError, TypeError):
    290                 pass
     572            except (AttributeError, TypeError), err:   
     573                self._record_exception()
    291574            if not isinstance(y, Element):
    292575                try:
    293576                    return y.__rmul__(x)
    294577                except (AttributeError, TypeError):
    295578                    pass
    296                
    297         raise TypeError, arith_error_message(x,y,op)
    298579
    299 
     580        # We should really include the underlying error.
     581        # This causes so much headache.
     582        raise TypeError, arith_error_message(x,y,op)                   
    300583       
    301     cdef canonical_coercion_c(self, x, y):
     584    cpdef canonical_coercion(self, x, y):
     585        """
     586        Given to elements x and y, with parents S and R respectively,
     587        find a common parent Z such that there are coercions
     588        $f: S \mapsto Z$ and $g: R \mapsto Z$ and return $f(x), g(y)$
     589        which will have the same parent.
     590       
     591        Raises a type error if no such Z can be found.
     592       
     593        EXAMPLES:
     594            sage: cm = sage.structure.element.get_coercion_model()
     595            sage: cm.canonical_coercion(mod(2, 10), 17)
     596            (2, 7)
     597            sage: x, y = cm.canonical_coercion(1/2, matrix(ZZ, 2, 2, range(4)))
     598            sage: x
     599            [1/2   0]
     600            [  0 1/2]
     601            sage: y
     602            [0 1]
     603            [2 3]
     604            sage: parent(x) is parent(y)
     605            True
     606           
     607        There is some support for non-Sage datatypes as well:
     608            sage: x, y = cm.canonical_coercion(int(5), 10)
     609            sage: type(x), type(y)
     610            (<type 'sage.rings.integer.Integer'>, <type 'sage.rings.integer.Integer'>)
     611           
     612            sage: class MyClass:
     613            ...       def _sage_(self):
     614            ...           return 13
     615            sage: a, b = cm.canonical_coercion(MyClass(), 1/3)
     616            sage: a, b
     617            (13, 1/3)
     618            sage: type(a)
     619            <type 'sage.rings.rational.Rational'>
     620           
     621        We also make an exception for 0, even if $\mathbb{Z}$ does not map in:
     622            sage: canonical_coercion(vector([1, 2, 3]), 0)
     623            ((1, 2, 3), (0, 0, 0))
     624        """
    302625        xp = parent_c(x)
    303626        yp = parent_c(y)
    304627        if xp is yp:
    305628            return x,y
    306629           
    307630        cdef Element x_elt, y_elt
    308         coercions = self.coercion_maps_c(xp, yp)
     631        coercions = self.coercion_maps(xp, yp)
    309632        if coercions is not None:
    310633            x_map, y_map = coercions
    311634            if x_map is not None:
    312                 x_elt = (<Morphism>x_map)._call_c(x)
     635                x_elt = (<Morphism>x_map)._call_(x)
    313636            else:
    314637                x_elt = x
    315638            if y_map is not None:
    316                 y_elt = (<Morphism>y_map)._call_c(y)
     639                y_elt = (<Morphism>y_map)._call_(y)
    317640            else:
    318641                y_elt = y
    319642            if x_elt is None:
    cdef class CoercionModel_cache_maps(Coer 
    339662            try:
    340663                x = yp(x)
    341664                if PY_TYPE_CHECK(yp, type): return x,y
    342             except TypeError:
     665            except (TypeError, ValueError):
     666                self._record_exception()
    343667                y = x.__class__(y)
    344668                return x, y
    345669            return _verify_canonical_coercion_c(x,y)
    cdef class CoercionModel_cache_maps(Coer 
    348672            try:
    349673                y = xp(y)
    350674                if PY_TYPE_CHECK(xp, type): return x,y
    351             except TypeError:
     675            except (TypeError, ValueError):
     676                self._record_exception()
    352677                x = y.__class__(x)
    353678                return x, y           
    354679            return _verify_canonical_coercion_c(x,y)
    cdef class CoercionModel_cache_maps(Coer 
    357682            if not PY_TYPE_CHECK(x, SageObject) or not PY_TYPE_CHECK(y, SageObject):
    358683                x = x._sage_()
    359684                y = y._sage_()
    360                 return self.canonical_coercion_c(x, y)
     685                return self.canonical_coercion(x, y)
    361686        except AttributeError:
    362             pass
     687            self._record_exception()
     688           
     689        # Allow coercion of 0 even if no coercion from Z
     690        if is_Integer(x) and not x and not PY_TYPE_CHECK_EXACT(yp, type):
     691            try:
     692                return yp(0), y
     693            except:
     694                self._record_exception()
    363695
     696        if is_Integer(y) and not y and not PY_TYPE_CHECK_EXACT(xp, type):
     697            try:
     698                return x, xp(0)
     699            except:
     700                self._record_exception()
     701               
    364702        raise TypeError, "no common canonical parent for objects with parents: '%s' and '%s'"%(xp, yp)
    365703       
    366704
    367     def _coercion_error(self, x, x_map, x_elt, y, y_map, y_elt):
    368         raise RuntimeError, """There is a bug in the coercion code in SAGE.
    369 Both x (=%r) and y (=%r) are supposed to have identical parents but they don't.
    370 In fact, x has parent '%s'
    371 whereas y has parent '%s'
    372 
    373 Original elements %r (parent %s) and %r (parent %s) and morphisms
    374 %s %r
    375 %s %r"""%( x_elt, y_elt, parent_c(x_elt), parent_c(y_elt),
    376             x, parent_c(x), y, parent_c(y),
    377             type(x_map), x_map, type(y_map), y_map)
    378    
    379     def coercion_maps(self, R, S):
    380         return self.coercion_maps_c(R, S)
    381    
    382     cdef coercion_maps_c(self, R, S):
     705    cpdef coercion_maps(self, R, S):
     706        """
     707        Give two parents R and S, return a pair of coercion maps
     708        $f: R \rightarrow Z$ and $g: S \rightarrow Z$, if such a $Z$
     709        can be found.
     710       
     711        In the (common) case that $R=Z$ or $S=Z$ then \code{None} is returned
     712        for $f$ or $g$ respectively rather than constructing (and subsequently
     713        calling) the identity morphism.
     714       
     715        If no suitable $f, g$ can be found, a single None is returned.
     716        This result is cached.
     717       
     718        EXAMPLES:
     719            sage: cm = sage.structure.element.get_coercion_model()
     720            sage: f, g = cm.coercion_maps(ZZ, QQ)
     721            sage: print f
     722            Ring morphism:
     723              From: Integer Ring
     724              To:   Rational Field
     725            sage: print g
     726            None
     727           
     728            sage: f, g = cm.coercion_maps(ZZ['x'], QQ)
     729            sage: print f
     730            Conversion morphism:
     731              From: Univariate Polynomial Ring in x over Integer Ring
     732              To:   Univariate Polynomial Ring in x over Rational Field
     733            sage: print g
     734            Conversion morphism:
     735              From: Rational Field
     736              To:   Univariate Polynomial Ring in x over Rational Field
     737             
     738            sage: cm.coercion_maps(QQ, GF(7)) == None
     739            True
     740        """
    383741        try:
    384742            return self._coercion_maps.get(R, S, None)
    385743        except KeyError:
    386             homs = self.discover_coercion_c(R, S)
     744            homs = self.discover_coercion(R, S)
    387745            if 0:
    388746                # This breaks too many things that are going to change
    389747                # in the new coercion model anyways.
    Original elements %r (parent %s) and %r  
    408766            sage: cm.verify_coercion_maps(ZZ, QQ, homs) == homs
    409767            Traceback (most recent call last):
    410768            ...
    411             RuntimeError: ('BUG in coercion model, codomains must be identical', Natural morphism:
     769            RuntimeError: ('BUG in coercion model, codomains must be identical', Ring morphism:
    412770              From: Integer Ring
    413               To:   Rational Field, Coercion morphism:
     771              To:   Rational Field, Conversion morphism:
    414772              From: Rational Field
    415773              To:   Real Field with 53 bits of precision)
    416774        """
    Original elements %r (parent %s) and %r  
    458816        elif PY_TYPE_CHECK(S_map, IdentityMorphism):
    459817            S_map = None
    460818        return R_map, S_map
     819
     820   
     821    cpdef discover_coercion(self, R, S):
     822        """
     823        This actually implements the finding of coercion maps as described in
     824        the \code{coercion_maps} method.
     825       
     826        EXAMPLES:
     827            sage: cm = sage.structure.element.get_coercion_model()
    461828           
    462     cdef discover_coercion_c(self, R, S):
     829        If R is S, then two identity morphisms suffice:
     830            sage: cm.discover_coercion(SR, SR)
     831            (None, None)
     832
     833        If there is a coercion map either direction, use that:
     834            sage: cm.discover_coercion(ZZ, QQ)
     835            (Ring morphism:
     836              From: Integer Ring
     837              To:   Rational Field, None)
     838            sage: cm.discover_coercion(RR, QQ)
     839            (None,
     840             Conversion morphism:
     841              From: Rational Field
     842              To:   Real Field with 53 bits of precision)
     843       
     844        Otherwise, try and compute an appropriate cover:
     845            sage: cm.discover_coercion(ZZ['x,y'], RDF)
     846            (Conversion morphism:
     847              From: Multivariate Polynomial Ring in x, y over Integer Ring
     848              To:   Multivariate Polynomial Ring in x, y over Real Double Field,
     849             Conversion morphism:
     850              From: Real Double Field
     851              To:   Multivariate Polynomial Ring in x, y over Real Double Field)
     852             
     853        Sometimes there is a reasonable "cover," but no canonical coercion:
     854            sage: sage.categories.pushout.pushout(QQ, QQ^3)
     855            Vector space of dimension 3 over Rational Field
     856            sage: print cm.discover_coercion(QQ, QQ^3)
     857            None
     858        """
    463859        from sage.categories.homset import Hom
    464860        if R is S:
    465861            return None, None
    466862           
    467863        # See if there is a natural coercion from R to S
    468864        if PY_TYPE_CHECK(R, Parent):
    469             mor = (<Parent>R).coerce_map_from_c(S)
     865            mor = (<Parent>R).coerce_map_from(S)
    470866            if mor is not None:
    471867                return None, mor
    472868
    473869        # See if there is a natural coercion from S to R
    474870        if PY_TYPE_CHECK(S, Parent):
    475             mor = (<Parent>S).coerce_map_from_c(R)
     871            mor = (<Parent>S).coerce_map_from(R)
    476872            if mor is not None:
    477873                return mor, None
    478874       
    Original elements %r (parent %s) and %r  
    481877            from sage.categories.pushout import pushout
    482878            try:
    483879                Z = pushout(R, S)
    484                 from sage.categories.homset import Hom
    485                 # Can I trust always __call__() to do the right thing in this case?
    486                 return sage.categories.morphism.CallMorphism(Hom(R, Z)), sage.categories.morphism.CallMorphism(Hom(S, Z))
     880                coerce_R = Z.coerce_map_from(R)
     881                coerce_S = Z.coerce_map_from(S)
     882                if coerce_R is not None and coerce_S is not None:
     883                    return coerce_R, coerce_S
    487884            except:
    488                 pass
     885                self._record_exception()
    489886
    490887        return None
    491888       
    492889   
    493     def get_action(self, R, S, op):
     890    cpdef get_action(self, R, S, op):
    494891        """
    495892        Get the action of R on S or S on R associated to the operation op.
    496893       
    Original elements %r (parent %s) and %r  
    511908            sage: R.<x> = QQ['x']
    512909            sage: A = cm.get_action(R, ZZ, operator.div); A
    513910            Right inverse action by Rational Field on Univariate Polynomial Ring in x over Rational Field
    514             with precomposition on right by Natural morphism:
     911            with precomposition on right by Ring morphism:
    515912              From: Integer Ring
    516913              To:   Rational Field
    517914            sage: A(x+10, 5)
    518915            1/5*x + 2
    519916
    520917        """
    521         return self.get_action_c(R, S, op)
    522    
    523     cdef get_action_c(self, R, S, op):
    524918        try:
    525919            return self._action_maps.get(R, S, op)
    526920        except KeyError:
    527             action = self.discover_action_c(R, S, op)
    528             #action = self.verify_action(action, R, S, op)
     921            action = self.discover_action(R, S, op)
     922            action = self.verify_action(action, R, S, op)
    529923            self._action_maps.set(R, S, op, action)
    530924            return action
    531925           
    Original elements %r (parent %s) and %r  
    549943                R = Real Double Field
    550944                S = Univariate Polynomial Ring in x over Integer Ring
    551945                (should be Univariate Polynomial Ring in x over Integer Ring, Rational Field)
    552                 action = Right scalar multiplication by Rational Field on Univariate Polynomial Ring in x over Integer Ring (<type 'sage.structure.coerce.RightModuleAction'>)
     946                action = Right scalar multiplication by Rational Field on Univariate Polynomial Ring in x over Integer Ring (<type 'sage.structure.coerce_actions.RightModuleAction'>)
    553947        """
    554948        if action is None:
    555949            return action
    Original elements %r (parent %s) and %r  
    564958        except AttributeError:
    565959            ok = False
    566960        if not ok:
     961            if PY_TYPE_CHECK(R, type):
     962                R = Set_PythonType(R)
     963            if PY_TYPE_CHECK(S, type):
     964                S = Set_PythonType(S)
     965               
    567966            if action.left_domain() == R and action.right_domain() == S:
    568967                # Non-unique parents
    569968                if fix:
    Original elements %r (parent %s) and %r  
    582981
    583982        return action
    584983           
    585     cdef discover_action_c(self, R, S, op):
    586 #        print "looking", R, <int><void *>R, op, S, <int><void *>S
     984    cpdef discover_action(self, R, S, op):
     985        """
     986        INPUT
     987            R -- the left Parent (or type)
     988            S -- the right Parent (or type)
     989            op -- the operand, typically an element of the operator module.
     990           
     991        OUTPUT:
     992            An action A such that s op r is given by A(s,r).
     993       
     994        The steps taken are illistrated below.
     995       
     996        EXAMPLES:
     997            sage: P.<x> = ZZ['x']
     998            sage: P.get_action(ZZ)
     999            Right scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
     1000            sage: ZZ.get_action(P) is None
     1001            True
     1002            sage: cm = sage.structure.element.get_coercion_model()
     1003
     1004        If R or S is a Parent, ask it for an action by/on R:
     1005            sage: cm.discover_action(ZZ, P, operator.mul)
     1006            Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
     1007           
     1008        If R or S a type, recursively call get_action with the Sage versions of R and/or S:
     1009            sage: cm.discover_action(P, int, operator.mul)
     1010            Right scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
     1011            with precomposition on right by Native morphism:
     1012              From: Set of Python objects of type 'int'
     1013              To:   Integer Ring
     1014       
     1015        If op in an inplace operation, look for the non-inplace action:
     1016            sage: cm.discover_action(P, ZZ, operator.imul)
     1017            Right scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
     1018           
     1019        If op is division, look for action on right by inverse:
     1020            sage: cm.discover_action(P, ZZ, operator.div)
     1021            Right inverse action by Rational Field on Univariate Polynomial Ring in x over Integer Ring
     1022            with precomposition on right by Ring morphism:
     1023              From: Integer Ring
     1024              To:   Rational Field
     1025        """
     1026        #print "looking", R, <int><void *>R, op, S, <int><void *>S
    5871027   
    5881028        if PY_TYPE_CHECK(S, Parent):
    589             action = (<Parent>S).get_action_c(R, op, False)
     1029            action = (<Parent>S).get_action(R, op, False)
    5901030            if action is not None:
    591 #                print "found", action
     1031                #print "found1", action
    5921032                return action
    5931033                   
    5941034        if PY_TYPE_CHECK(R, Parent):
    595             action = (<Parent>R).get_action_c(S, op, True)
     1035            action = (<Parent>R).get_action(S, op, True)
    5961036            if action is not None:
    597 #                print "found", action
     1037                #print "found2", action
    5981038                return action
    5991039       
    6001040        if PY_TYPE(R) == <void *>type:
    6011041            sageR = py_scalar_parent(R)
    6021042            if sageR is not None:
    603                 action = self.discover_action_c(sageR, S, op)
     1043                action = self.discover_action(sageR, S, op)
    6041044                if action is not None:
    6051045                    if not PY_TYPE_CHECK(action, IntegerMulAction):
    6061046                        action = PrecomposedAction(action, sageR.coerce_map_from(R), None)
    Original elements %r (parent %s) and %r  
    6091049        if PY_TYPE(S) == <void *>type:
    6101050            sageS = py_scalar_parent(S)
    6111051            if sageS is not None:
    612                 action = self.discover_action_c(R, sageS, op)
     1052                action = self.discover_action(R, sageS, op)
    6131053                if action is not None:
    6141054                    if not PY_TYPE_CHECK(action, IntegerMulAction):
    6151055                        action = PrecomposedAction(action, None, sageS.coerce_map_from(S))
    Original elements %r (parent %s) and %r  
    6171057       
    6181058        if op.__name__[0] == 'i':
    6191059            try:
    620                 a = self.discover_action_c(R, S, no_inplace_op(op))
     1060                a = self.discover_action(R, S, no_inplace_op(op))
    6211061                if a is not None:
    6221062                    is_inverse = isinstance(a, InverseAction)
    6231063                    if is_inverse: a = ~a
    6241064                    if a is not None and PY_TYPE_CHECK(a, RightModuleAction):
    6251065                        # We want a new instance so that we don't alter the (potentially cached) original
    6261066                        a = RightModuleAction(S, R)
    627                         (<RightModuleAction>a).is_inplace = 1
     1067                        a.is_inplace = 1
    6281068                    if is_inverse: a = ~a
    6291069                return a
    6301070            except KeyError:
    631                 pass
     1071                self._record_exception()
    6321072       
    6331073        if op is div:
    6341074            # Division on right is the same acting on right by inverse, if it is so defined.
    Original elements %r (parent %s) and %r  
    6471087                K = None
    6481088               
    6491089            if K is not None:
    650                 action = self.get_action_c(R, K, mul)
     1090                action = self.get_action(R, K, mul)
    6511091                if action is not None and action.actor() is K:
    6521092                    try:
    6531093                        action = ~action
    Original elements %r (parent %s) and %r  
    6551095                            action = PrecomposedAction(action, None, K.coerce_map_from(S))
    6561096                        return action
    6571097                    except TypeError: # action may not be invertible
    658                         pass
    659        
    660 #        if op is operator.mul:
    661 #            from sage.rings.integer_ring import ZZ
    662 #            if S in [int, long, ZZ] and not R.has_coerce_map_from(ZZ):
    663 #                return IntegerMulAction(S, R False)
    664 #       
    665 #            if R in [int, long, ZZ] and not S.has_coerce_map_from(ZZ):
    666 #                return IntegerMulAction(R, S, True)
     1098                        self._record_exception()
    6671099       
    6681100        return None
    669                            
     1101
     1102    def _coercion_error(self, x, x_map, x_elt, y, y_map, y_elt):
     1103        """
     1104        This function is only called when someone has incorrectly implemented
     1105        a user-defined part of the coercion system (usually, a morphism).
     1106       
     1107        EXAMPLE:
     1108            sage: cm = sage.structure.element.get_coercion_model()
     1109            sage: cm._coercion_error('a', 'f', 'f(a)', 'b', 'g', 'g(b)')
     1110            Traceback (most recent call last):
     1111            ...
     1112            RuntimeError: There is a bug in the coercion code in SAGE.
     1113            Both x (='f(a)') and y (='g(b)') are supposed to have identical parents but they don't.
     1114            In fact, x has parent '<type 'str'>'
     1115            whereas y has parent '<type 'str'>'
     1116            Original elements 'a' (parent <type 'str'>) and 'b' (parent <type 'str'>) and morphisms
     1117            <type 'str'> 'f'
     1118            <type 'str'> 'g'
     1119        """
     1120        raise RuntimeError, """There is a bug in the coercion code in SAGE.
     1121Both x (=%r) and y (=%r) are supposed to have identical parents but they don't.
     1122In fact, x has parent '%s'
     1123whereas y has parent '%s'
     1124Original elements %r (parent %s) and %r (parent %s) and morphisms
     1125%s %r
     1126%s %r"""%( x_elt, y_elt, parent_c(x_elt), parent_c(y_elt),
     1127            x, parent_c(x), y, parent_c(y),
     1128            type(x_map), x_map, type(y_map), y_map)
    6701129   
    6711130
    672 cdef class CoercionModel_profile(CoercionModel_cache_maps):
    673     """
    674     This is a subclass of CoercionModel_cache_maps that can be used
    675     to inspect and profile implicit coercions.
    676 
    677     EXAMPLE:
    678         sage: from sage.structure.coerce import CoercionModel_profile
    679         sage: from sage.structure.element import set_coercion_model
    680         sage: coerce = CoercionModel_profile()
    681         sage: set_coercion_model(coerce)
    682         sage: 2 + 1/2
    683         5/2
    684         sage: coerce.profile() # random timings
    685         1    0.00015    Integer Ring    -->    Rational Field    Rational Field
    686        
    687     This shows that one coercion was performed, from $\Z$ to $\Q$ (with the result in $\Q$)
    688     and it took 0.00015 seconds.
    689    
    690         sage: R.<x> = ZZ[]
    691         sage: coerce.flush()
    692         sage: 1/2 * x + .5
    693         0.500000000000000*x + 0.500000000000000
    694         sage: coerce.profile()    # random timings
    695         1    0.00279    *    Rational Field    A->    Univariate Polynomial Ring in x over Integer Ring    Univariate Polynomial Ring in x over Rational Field
    696         1    0.00135         Univariate Polynomial Ring in x over Rational Field    <->    Real Field with 53 bits of precision    Univariate Polynomial Ring in x over Real Field with 53 bits of precision
    697         10   0.00013         <type 'int'>    -->    Rational Field    Rational Field
    698         6    0.00011         <type 'int'>    -->    Real Field with 53 bits of precision    Real Field with 53 bits of precision
    699        
    700     We can read of this data that the most expensive operation was the creation
    701     of the action of $\Q$ on $\Z[x]$ (whose result lies in $\Q[x]$. This has
    702     been cached as illustrated below.
    703    
    704         sage: coerce.flush()
    705         sage: z = 1/2 * x + .5
    706         sage: coerce.profile()     # more random timings
    707         1    0.00020         Univariate Polynomial Ring in x over Rational Field    <->    Real Field with 53 bits of precision    Univariate Polynomial Ring in x over Real Field with 53 bits of precision
    708         5    0.00005         <type 'int'>    -->    Rational Field    Rational Field
    709         1    0.00004    *    Rational Field    A->    Univariate Polynomial Ring in x over Integer Ring    Univariate Polynomial Ring in x over Rational Field
    710        
    711 
    712     NOTE:
    713        - profiles are indexable and sliceable.
    714        - they also have a nice latex view (with much less verbose ring descriptors), use the show(...) command.
    715    
    716     """
    717     def __init__(self, timer=time.time):
    718         CoercionModel_cache_maps.__init__(self)
    719         self.profiling_info = {}
    720         self.timer = timer
    721    
    722    
    723     cdef get_action_c(self, xp, yp, op):
    724         time = self.timer()
    725         action = CoercionModel_cache_maps.get_action_c(self, xp, yp, op)
    726         time = self.timer() - time
    727         if action is not None:
    728             self._log_time(xp, yp, op, time, action)
    729         return action
    730        
    731     cdef canonical_coercion_c(self, x, y):
    732         time = self.timer()
    733         xy = CoercionModel_cache_maps.canonical_coercion_c(self, x, y)
    734         time = self.timer() - time
    735         self._log_time(parent_c(x), parent_c(y), 'coerce', time, parent_c(xy[0]))
    736         return xy
    737            
    738     cdef void _log_time(self, xp, yp, op, time, data):
    739         if op == "coerce":
    740             # consolidate symmetric cases
    741             if data is xp:
    742                 xp,yp = yp,xp
    743             if data is not yp and xp > yp:
    744                 xp,yp = yp,xp
    745                
    746         try:
    747             timing = self.profiling_info[xp, yp, op]
    748         except KeyError:
    749             timing = [0, 0, time, data]
    750             self.profiling_info[xp, yp, op] = timing
    751         timing[0] += 1
    752         timing[1] += time
    753    
    754     def flush(self):
    755         self.profiling_info = {}
    756 
    757     def profile(self, filter=None):
    758            
    759         output = []
    760         import re
    761         for key, timing in self.profiling_info.iteritems():
    762             xp, yp, op = key
    763             explain, resp = self.analyze_bin_op(xp, yp, op, timing[3])
    764             if filter is not None:
    765                 if not xp in filter and not yp in filter and not resp in filter:
    766                     continue
    767                    
    768             item = CoercionProfileItem(xp=xp, yp=yp, op=op, total_time=timing[1], count=timing[0], discover_time=timing[2], explain=explain, resp=resp)
    769             output.append(item)
    770        
    771         output.sort(reverse=True)
    772         return CoercionProfile(output)
    773                    
    774        
    775     def analyze_bin_op(self, xp, yp, op, data):
    776         if isinstance(data, Action):
    777             if data.actor() == xp: 
    778                 explain = "Act on left"
    779             else:
    780                 explain = "Act on right"
    781             resp = data.domain()
    782         else:
    783             resp = data
    784             if resp is xp:
    785                 explain = "Coerce left"
    786             elif resp is yp:
    787                 explain = "Coerce right"
    788             else:
    789                 explain = "Coerce both"
    790         return explain, resp
    791 
    792 
    793 class CoercionProfile:
    794    
    795     def __init__(self, data):
    796         self.data = data
    797        
    798     def __getitem__(self, ix):
    799         return CoercionProfile([self.data[ix]])
    800 
    801     def __getslice__(self, start, end):
    802         return CoercionProfile(self.data[start:end])
    803 
    804     def _latex_(self):
    805         return r"""
    806         \begin{array}{rr|l|ccccc}
    807         %s
    808         \end{array}
    809         """ % "\\\\\n".join([row._latex_() for row in self.data])
    810        
    811     def __repr__(self):
    812         return "\n".join([repr(row) for row in self.data])
    813        
    814            
    815 class CoercionProfileItem:
    816 
    817     def __init__(self, **kwds):
    818         self.__dict__.update(kwds)
    819    
    820     def __cmp__(self, other):
    821         return cmp(self.total_time, other.total_time)
    822        
    823     def _latex_(self):
    824         return "&".join([str(self.count),
    825                          "%01.5f"%self.total_time,
    826                          self.op_name(self.op),
    827                          self.pretty_print_parent(self.xp),
    828                          self.latex_arrow(self.explain),
    829                          self.pretty_print_parent(self.yp),
    830                          "|",
    831                          self.pretty_print_parent(self.resp)])
    832                          
    833     def __repr__(self):
    834         return "\t".join([str(self.count),
    835                           "%01.5f"%self.total_time,
    836                           self.op_name(self.op),
    837                           str(self.xp),
    838                           self.text_arrow(self.explain),
    839                           str(self.yp),
    840                           str(self.resp)])
    841        
    842     def latex_arrow(self, explain):
    843         if explain == "Coerce left":
    844             return "\\leftarrow"
    845         elif explain == "Coerce right":
    846             return "\\rightarrow"
    847         elif explain == "Coerce both":
    848             return "\\leftrightarrow"
    849         elif explain == "Act on left":
    850             return "\\nearrow"
    851         elif explain == "Act on right":
    852             return "\\nwarrow"
    853         else:
    854             return "\\text{%s}" % explain
    855            
    856     def text_arrow(self, explain):
    857         if explain == "Coerce left":
    858             return "<--"
    859         elif explain == "Coerce right":
    860             return "-->"
    861         elif explain == "Coerce both":
    862             return "<->"
    863         elif explain == "Act on left":
    864             return "A->"
    865         elif explain == "Act on right":
    866             return "<-A"
    867         else:
    868             return explain
    869            
    870     def op_name(self, op):
    871         if op == "coerce":
    872             return " "
    873         try:
    874             return D[op.__name__]
    875         except AttributeError:
    876             return str(op)
    877         except KeyError:
    878             return op.__name__
    879            
    880     def pretty_print_parent(self, p):
    881         # Full text names can be really long, but latex doesn't have all the info
    882         # Perhaps there should be another method and/or a difference between str/repr
    883         # for parents.
    884         if isinstance(p, type):
    885             return "\\text{%s}" % re.sub(r"<.*'(.*)'>", r"\1", str(p))
    886        
    887         # special cases
    888         from sage.rings.all import RDF, RQDF, CDF
    889         if p == RDF:
    890             return "RDF"
    891         elif p == RQDF:
    892             return "RQDF"
    893         elif p == CDF:
    894             return "CDF"
    895            
    896         try:
    897             s = p._latex_()
    898         except AttributeError:
    899             s = str(p)
    900         try:
    901             prec = p.precision()
    902             s += " \\text{(%s)}"%prec
    903         except AttributeError:
    904             pass
    905            
    906         # try to non-distructively shorten latex
    907         if len(s) > 50:
    908             real_len = len(re.sub(r"(\\[a-zA-Z]+)|[{}]", "", s))
    909             if real_len > 50:
    910                 s = re.sub(r"([0-9]{,3})[0-9]{5,}([0-9])", "\\1...\\2", s)
    911                 real_len = len(re.sub(r"(\\[a-zA-Z]+)|[{}]", "", s))
    912                 if real_len > 50:
    913                     s = re.sub(r"([^\\]\b[^{}\\]{5,7})[^{}\\]{5,}([^{}\\]{3,})", "\\1...\\2", " "+s)
    914 
    915         return s           
    916 
    917 
    918 
    919 
    920 
    921 from sage.structure.element cimport Element # workaround SageX bug
    922 
    923 cdef class LAction(Action):
    924     """Action calls _l_action of the actor."""
    925     def __init__(self, G, S):
    926         Action.__init__(self, G, S, True, mul)
    927     cdef Element _call_c_impl(self, Element g, Element a):
    928         return g._l_action(a)  # a * g
    929 
    930 cdef class RAction(Action):
    931     """Action calls _r_action of the actor."""
    932     def __init__(self, G, S):
    933         Action.__init__(self, G, S, False, mul)
    934     cdef Element _call_c_impl(self, Element a, Element g):
    935         return g._r_action(a)  # g * a
    936 
    937 cdef class LeftModuleAction(Action):
    938     def __init__(self, G, S):
    939         if not isinstance(G, Parent):
    940             # only let Parents act
    941             raise TypeError
    942         if S.base() is S:
    943             # The right thing to do is a normal multiplication
    944             raise TypeError
    945         # Objects are implemented with the assumption that
    946         # _rmul_ is given an element of the basering
    947         if G is not S.base():
    948             # first we try the easy case of coercing G to the basering of S
    949             self.connecting = S.base().coerce_map_from(G)
    950             if self.connecting is None:
    951                 # otherwise, we try and find a base extension
    952                 from sage.categories.pushout import pushout
    953                 # this may raise a type error, which we propagate
    954                 self.extended_base = pushout(G, S)
    955                 # make sure the pushout actually gave correct a base extension of S
    956                 if self.extended_base.base() != pushout(G, S.base()):
    957                     raise TypeError, "Actor must be coercable into base."
    958                 else:
    959                     self.connecting = self.extended_base.base().coerce_map_from(G)
    960                     if self.connecting is None:
    961                         # this may happen if G is, say, int rather than a parent
    962                         # TODO: let python types be valid actions
    963                         raise TypeError
    964                
    965         # At this point, we can assert it is safe to call _rmul_c
    966         the_ring = G if self.connecting is None else self.connecting.codomain()
    967         the_set = S if self.extended_base is None else self.extended_base
    968         assert the_ring is the_set.base(), "BUG in coersion model"
    969 
    970         cdef RingElement g = G._an_element()
    971         cdef ModuleElement a = S._an_element()
    972         res = self._call_c(g, a)
    973        
    974         if parent_c(res) is not S and parent_c(res) is not self.extended_base:
    975             raise TypeError
    976            
    977         Action.__init__(self, G, S, True, mul)
    978 
    979     cdef Element _call_c_impl(self, Element g, Element a):
    980         if self.connecting is not None:
    981             g = self.connecting._call_c(g)
    982         if self.extended_base is not None:
    983             a = self.extended_base(a)
    984         return _rmul_c(<ModuleElement>a, <RingElement>g)  # a * g
    985        
    986     def _repr_name_(self):
    987         return "scalar multiplication"
    988        
    989     def domain(self):
    990         return self.S
    991 
    992     def codomain(self):
    993         if self.extended_base is not None:
    994             return self.extended_base
    995         return self.S
    996 
    997        
    998 cdef class RightModuleAction(Action):
    999     def __init__(self, G, S):
    1000         if not isinstance(G, Parent):
    1001             # only let Parents act
    1002             raise TypeError
    1003         if S.base() is S:
    1004             # The right thing to do is a normal multiplication
    1005             raise TypeError
    1006         # Objects are implemented with the assumption that
    1007         # _lmul_ is given an element of the basering
    1008         if G is not S.base() and S.base() is not S:
    1009             # first we try the easy case of coercing G to the basering of S
    1010             self.connecting = S.base().coerce_map_from(G)
    1011             if self.connecting is None:
    1012                 # otherwise, we try and find a base extension
    1013                 from sage.categories.pushout import pushout
    1014                 # this may raise a type error, which we propagate
    1015                 self.extended_base = pushout(G, S)
    1016                 # make sure the pushout actually gave correct a base extension of S
    1017                 if self.extended_base.base() != pushout(G, S.base()):
    1018                     raise TypeError, "Actor must be coercable into base."
    1019                 else:
    1020                     self.connecting = self.extended_base.base().coerce_map_from(G)
    1021 
    1022         # At this point, we can assert it is safe to call _lmul_c
    1023         the_ring = G if self.connecting is None else self.connecting.codomain()
    1024         the_set = S if self.extended_base is None else self.extended_base
    1025         assert the_ring is the_set.base(), "BUG in coersion model"
    1026 
    1027         cdef RingElement g = G._an_element()
    1028         cdef ModuleElement a = S._an_element()
    1029         res = self._call_c(a, g)
    1030 
    1031         if parent_c(res) is not S and parent_c(res) is not self.extended_base:
    1032             raise TypeError
    1033            
    1034         Action.__init__(self, G, S, False, mul)
    1035         self.is_inplace = 0
    1036        
    1037     cdef Element _call_c_impl(self, Element a, Element g):
    1038         cdef PyObject* tmp
    1039         if self.connecting is not None:
    1040             g = self.connecting._call_c(g)
    1041         if self.extended_base is not None:
    1042             a = self.extended_base(a)
    1043             # TODO: figure out where/why the polynomial constructor is caching 'a'
    1044             if (<RefPyObject *>a).ob_refcnt == 2:
    1045                 b = self.extended_base(0)
    1046             if (<RefPyObject *>a).ob_refcnt == 1:
    1047                 # This is a truely new object, mutate it
    1048                 return _ilmul_c(<ModuleElement>a, <RingElement>g)  # a * g
    1049             else:
    1050                 return _lmul_c(<ModuleElement>a, <RingElement>g)  # a * g
    1051         else:
    1052             # The 3 extra refcounts are from
    1053             #    (1) bin_op_c stack
    1054             #    (2) Action._call_c stack
    1055             #    (3) Action._call_c_impl stack
    1056             if (<RefPyObject *>a).ob_refcnt < 3 + inplace_threshold + self.is_inplace:
    1057                 return _ilmul_c(<ModuleElement>a, <RingElement>g)  # a * g
    1058             else:
    1059                 return _lmul_c(<ModuleElement>a, <RingElement>g)  # a * g
    1060        
    1061     def _repr_name_(self):
    1062         return "scalar multiplication"
    1063        
    1064     def domain(self):
    1065         return self.S
    1066 
    1067     def codomain(self):
    1068         if self.extended_base is not None:
    1069             return self.extended_base
    1070         return self.S
    1071        
    1072     def connecting_map(self):
    1073         """
    1074         Return the connecting map.
    1075 
    1076         EXAMPLES:
    1077             sage: R.<x> = QQ[]; a = 2*x^2+2
    1078             sage: import sage.structure.element as e
    1079             sage: cm = e.get_coercion_model()
    1080             sage: act = cm.get_action(parent(a), parent(2), operator.mul)
    1081             sage: type(act)
    1082             <type 'sage.structure.coerce.RightModuleAction'>
    1083             sage: h = act.connecting_map()
    1084             sage: h
    1085             Natural morphism:
    1086               From: Integer Ring
    1087               To:   Rational Field
    1088             sage: type(h)
    1089             <type 'sage.rings.rational.Z_to_Q'>
    1090         """
    1091         return self.connecting
    1092 
    1093 
    1094 cdef class IntegerMulAction(Action):
    1095    
    1096     def __init__(self, ZZ, M, is_left):
    1097         if PY_TYPE_CHECK(ZZ, type):
    1098             from sage.structure.parent import Set_PythonType
    1099             ZZ = Set_PythonType(ZZ)
    1100         test = M._an_element() + (-M._an_element()) # make sure addition and negation is allowed
    1101         Action.__init__(self, ZZ, M, is_left, mul)
    1102 
    1103     cdef Element _call_c(self, nn, a):
    1104         # Override _call_c because signature of _call_c_impl requires elements
    1105         if not self._is_left:
    1106             a, nn = nn, a
    1107         if not PyInt_CheckExact(nn):
    1108             nn = PyNumber_Int(nn)
    1109             if not PyInt_CheckExact(nn):
    1110                 return fast_mul(a, nn)
    1111                
    1112         return fast_mul_long(a, PyInt_AS_LONG(nn))
    1113        
    1114     def __inverse__(self):
    1115         raise TypeError, "No generic module division by Z."
    1116        
    1117     def _repr_type(self):
    1118         return "Integer Multiplication"
    1119 
    1120 
    1121 
    1122 cdef inline fast_mul(a, n):
    1123     _sig_on
    1124     if n < 0:
    1125         n = -n
    1126         a = -a
    1127     pow2a = a
    1128     while n & 1 == 0:
    1129         pow2a += pow2a
    1130         n = n >> 1
    1131     sum = pow2a
    1132     n = n >> 1
    1133     while n != 0:
    1134         pow2a += pow2a
    1135         if n & 1:
    1136             sum += pow2a
    1137         n = n >> 1
    1138     _sig_off
    1139     return sum
    1140 
    1141 cdef inline fast_mul_long(a, long n):
    1142     if n < 0:
    1143         n = -n
    1144         a = -a
    1145     if n < 4:
    1146         if n == 0: return (<Element>a)._parent(0)
    1147         if n == 1: return a
    1148         if n == 2: return a+a
    1149         if n == 3: return a+a+a
    1150     _sig_on
    1151     pow2a = a
    1152     while n & 1 == 0:
    1153         pow2a += pow2a
    1154         n = n >> 1
    1155     sum = pow2a
    1156     n = n >> 1
    1157     while n != 0:
    1158         pow2a += pow2a
    1159         if n & 1:
    1160             sum += pow2a
    1161         n = n >> 1
    1162     _sig_off
    1163     return sum
  • new file sage/structure/coerce_actions.pxd

    diff -r fa73e4213671 -r 9f227080425a sage/structure/coerce_actions.pxd
    - +  
     1
     2from element cimport Element, RingElement, ModuleElement
     3from parent cimport Parent
     4
     5from sage.categories.action cimport Action
     6from sage.categories.morphism cimport Morphism
     7
     8
     9cdef class ModuleAction(Action):
     10    cdef Morphism connecting
     11    cdef extended_base
     12   
     13cdef class LeftModuleAction(ModuleAction):
     14    pass
     15
     16cdef class RightModuleAction(ModuleAction):
     17    cdef public bint is_inplace
     18
     19cdef class PyScalarAction(Action):
     20    cdef Action _action
     21
     22cdef class IntegerMulAction(Action):
     23    pass
     24 No newline at end of file
  • new file sage/structure/coerce_actions.pyx

    diff -r fa73e4213671 -r 9f227080425a sage/structure/coerce_actions.pyx
    - +  
     1#*****************************************************************************
     2#     Copyright (C) 2007 Robert Bradshaw <robertwb@math.washington.edu>
     3#
     4#  Distributed under the terms of the GNU General Public License (GPL)
     5#
     6#                    http://www.gnu.org/licenses/
     7#*****************************************************************************
     8
     9import operator
     10
     11include "../ext/stdsage.pxi"
     12include "../ext/interrupt.pxi"
     13include "../ext/python_int.pxi"
     14include "../ext/python_number.pxi"
     15include "coerce.pxi"
     16
     17cdef extern from *:
     18    ctypedef struct RefPyObject "PyObject":
     19        int ob_refcnt
     20
     21
     22
     23cdef class LAction(Action):
     24    """Action calls _l_action of the actor."""
     25    def __init__(self, G, S):
     26        Action.__init__(self, G, S, True, operator.mul)
     27    cpdef Element _call_(self, g, a):
     28        return g._l_action(a)  # a * g
     29
     30
     31cdef class RAction(Action):
     32    """Action calls _r_action of the actor."""
     33    def __init__(self, G, S):
     34        Action.__init__(self, G, S, False, operator.mul)
     35    cpdef Element _call_(self, a, g):
     36        return g._r_action(a)  # g * a
     37
     38
     39
     40cdef class ModuleAction(Action):
     41
     42    def __init__(self, G, S):
     43        """
     44        This creates an action of an element of a module by an element of its
     45        base ring. The simplest example to keep in mind is R acting on the
     46        polynomial ring R[x].
     47       
     48        The actual action is implemented by the _rmul_ or _lmul_ function on
     49        its elements. We must, however, be very particular about what we feed
     50        into these functions because they operate under the assumption that
     51        the inputs lie exactly in the base ring and may segfault otherwise.
     52       
     53        Thus we handle all possible base extensions manually here. This is
     54        an abstract class, one must actually instantiate a LeftModuleAction
     55        or a RightModuleAction
     56       
     57        EXAMPLES:
     58            sage: from sage.structure.coerce_actions import LeftModuleAction
     59            sage: LeftModuleAction(ZZ, ZZ['x'])
     60            Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
     61            sage: LeftModuleAction(ZZ, QQ['x'])
     62            Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Rational Field
     63            sage: LeftModuleAction(QQ, ZZ['x'])
     64            Left scalar multiplication by Rational Field on Univariate Polynomial Ring in x over Integer Ring
     65            sage: LeftModuleAction(QQ, ZZ['x']['y'])
     66            Left scalar multiplication by Rational Field on Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Integer Ring
     67        """
     68        Action.__init__(self, G, S, not PY_TYPE_CHECK(self, RightModuleAction), operator.mul)
     69        if not isinstance(G, Parent):
     70            # only let Parents act
     71            raise TypeError
     72        if S.base() is S:
     73            # The right thing to do is a normal multiplication
     74            raise TypeError
     75        # Objects are implemented with the assumption that
     76        # _rmul_ is given an element of the basering
     77        if G is not S.base():
     78            # first we try the easy case of coercing G to the basering of S
     79            self.connecting = S.base().coerce_map_from(G)
     80            if self.connecting is None:
     81                # otherwise, we try and find a base extension
     82                from sage.categories.pushout import pushout
     83                # this may raise a type error, which we propagate
     84                self.extended_base = pushout(G, S)
     85                # make sure the pushout actually gave correct a base extension of S
     86                if self.extended_base.base() != pushout(G, S.base()):
     87                    raise TypeError, "Actor must be coercable into base."
     88                else:
     89                    self.connecting = self.extended_base.base().coerce_map_from(G)
     90                    if self.connecting is None:
     91                        # this may happen if G is, say, int rather than a parent
     92                        # TODO: let python types be valid actions
     93                        raise TypeError
     94                       
     95        # Don't waste time if our connecting morphisms happen to be the identity.
     96        if self.connecting is not None and self.connecting.codomain() is G:
     97            self.connecting = None
     98       
     99        if self.extended_base is not None and self.extended_base is S:
     100            self.extended_base = None
     101               
     102        # At this point, we can assert it is safe to call _Xmul_c
     103        the_ring = G if self.connecting is None else self.connecting.codomain()
     104        the_set = S if self.extended_base is None else self.extended_base
     105        assert the_ring is the_set.base(), "BUG in coersion model"
     106
     107        cdef RingElement g = G.an_element()
     108        cdef ModuleElement a = S.an_element()
     109        res = self.act(g, a)
     110       
     111        if parent_c(res) is not S and parent_c(res) is not self.extended_base:
     112            raise TypeError
     113           
     114
     115    def _repr_name_(self):
     116        return "scalar multiplication"
     117       
     118    def codomain(self):
     119        """
     120        The codomain of self, which may or may not be equal to the domain.
     121       
     122        EXAMPLES:
     123            sage: from sage.structure.coerce_actions import LeftModuleAction
     124            sage: A = LeftModuleAction(QQ, ZZ['x,y,z'])
     125            sage: A.codomain()
     126            Multivariate Polynomial Ring in x, y, z over Rational Field
     127        """
     128        if self.extended_base is not None:
     129            return self.extended_base
     130        return self.S
     131       
     132    def domain(self):
     133        """
     134        The domain of self, which is the module that is being acted on.
     135       
     136        EXAMPLES:
     137            sage: from sage.structure.coerce_actions import LeftModuleAction
     138            sage: A = LeftModuleAction(QQ, ZZ['x,y,z'])
     139            sage: A.domain()
     140            Multivariate Polynomial Ring in x, y, z over Integer Ring
     141        """
     142        return self.S
     143
     144
     145
     146cdef class LeftModuleAction(ModuleAction):
     147
     148    cpdef Element _call_(self, g, a):
     149        """
     150        A left module action is an action that takes the ring element as the
     151        first argument (the left side) and the module element as the second
     152        argument (the right side).
     153       
     154        EXAMPLES:
     155            sage: from sage.structure.coerce_actions import LeftModuleAction
     156            sage: R.<x> = QQ['x']
     157            sage: A = LeftModuleAction(ZZ, R)
     158            sage: A(5, x+1)
     159            5*x + 5
     160            sage: R.<x> = ZZ['x']
     161            sage: A = LeftModuleAction(QQ, R)
     162            sage: A(1/2, x+1)
     163            1/2*x + 1/2
     164            sage: A._call_(1/2, x+1) # safe only when arguments have exactly the correct parent
     165            1/2*x + 1/2
     166        """
     167        if self.connecting is not None:
     168            g = self.connecting._call_(g)
     169        if self.extended_base is not None:
     170            a = self.extended_base(a)
     171        return _rmul_c(<ModuleElement>a, <RingElement>g)  # a * g
     172       
     173       
     174cdef class RightModuleAction(ModuleAction):
     175   
     176    def __cinit__(self):
     177        self.is_inplace = 0
     178       
     179    cpdef Element _call_(self, a, g):
     180        """
     181        A rikght module action is an action that takes the module element as the
     182        first argument (the left side) and the ring element as the second
     183        argument (the right side).
     184       
     185        EXAMPLES:
     186            sage: from sage.structure.coerce_actions import RightModuleAction
     187            sage: R.<x> = QQ['x']
     188            sage: A = RightModuleAction(ZZ, R)
     189            sage: A(x+5, 2)
     190            2*x + 10
     191            sage: A._call_(x+5, 2) # safe only when arguments have exactly the correct parent
     192            2*x + 10
     193        """
     194        cdef PyObject* tmp
     195        if self.connecting is not None:
     196            g = self.connecting._call_(g)
     197        if self.extended_base is not None:
     198            a = self.extended_base(a)
     199            # TODO: figure out where/why the polynomial constructor is caching 'a'
     200            if (<RefPyObject *>a).ob_refcnt == 2:
     201                b = self.extended_base(0)
     202            if (<RefPyObject *>a).ob_refcnt == 1:
     203                # This is a truely new object, mutate it
     204                return _ilmul_c(<ModuleElement>a, <RingElement>g)  # a * g
     205            else:
     206                return _lmul_c(<ModuleElement>a, <RingElement>g)  # a * g
     207        else:
     208            if (<RefPyObject *>a).ob_refcnt < inplace_threshold + self.is_inplace:
     209                return _ilmul_c(<ModuleElement>a, <RingElement>g)  # a * g
     210            else:
     211                return _lmul_c(<ModuleElement>a, <RingElement>g)  # a * g
     212
     213
     214
     215cdef class PyScalarAction(Action):
     216    r"""
     217    This class implements the action of a python scalar (e.g. an int or float)
     218    on a \sage element.
     219    """
     220    def __init__(self, Action action):
     221        Action.__init__(self, action.G, action.S, action._is_left, action.op)
     222        self._action = action
     223
     224    cpdef Element _call_(self, a, b):
     225        if self._is_left:
     226            a = self.G(a)
     227            return self._action._call_(a,b)
     228        else:
     229            b = self.G(b)
     230            return self._action._call_(a,b)
     231   
     232    def __inverse__(self):
     233        return PyScalarAction(~self._action)
     234
     235
     236cdef class IntegerMulAction(Action):
     237
     238    def __init__(self, ZZ, M, is_left=True):
     239        r"""
     240        This class implements the action $n \cdot a = a + a + \cdots + a$ via
     241        repeated doubling.
     242       
     243        Both addition and negation must be defined on the set $A$.
     244       
     245        EXAMPLES:
     246            sage: from sage.structure.coerce_actions import IntegerMulAction
     247            sage: R.<x> = QQ['x']
     248            sage: act = IntegerMulAction(ZZ, R)
     249            sage: act(5, x)
     250            5*x
     251        """
     252        if PY_TYPE_CHECK(ZZ, type):
     253            from sage.structure.parent import Set_PythonType
     254            ZZ = Set_PythonType(ZZ)
     255        test = M.an_element() + (-M.an_element()) # make sure addition and negation is allowed
     256        Action.__init__(self, ZZ, M, is_left, operator.mul)
     257
     258    cpdef Element _call_(self, nn, a):
     259        if not self._is_left:
     260            a, nn = nn, a
     261        if not PyInt_CheckExact(nn):
     262            nn = PyNumber_Int(nn)
     263            if not PyInt_CheckExact(nn):
     264                return fast_mul(a, nn)
     265               
     266        return fast_mul_long(a, PyInt_AS_LONG(nn))
     267       
     268    def __inverse__(self):
     269        raise TypeError, "No generic module division by Z."
     270       
     271    def _repr_type(self):
     272        return "Integer Multiplication"
     273
     274
     275
     276cdef inline fast_mul(a, n):
     277    _sig_on
     278    if n < 0:
     279        n = -n
     280        a = -a
     281    pow2a = a
     282    while n & 1 == 0:
     283        pow2a += pow2a
     284        n = n >> 1
     285    sum = pow2a
     286    n = n >> 1
     287    while n != 0:
     288        pow2a += pow2a
     289        if n & 1:
     290            sum += pow2a
     291        n = n >> 1
     292    _sig_off
     293    return sum
     294
     295cdef inline fast_mul_long(a, long n):
     296    if n < 0:
     297        n = -n
     298        a = -a
     299    if n < 4:
     300        if n == 0: return parent_c(a)(0)
     301        if n == 1: return a
     302        if n == 2: return a+a
     303        if n == 3: return a+a+a
     304    _sig_on
     305    pow2a = a
     306    while n & 1 == 0:
     307        pow2a += pow2a
     308        n = n >> 1
     309    sum = pow2a
     310    n = n >> 1
     311    while n != 0:
     312        pow2a += pow2a
     313        if n & 1:
     314            sum += pow2a
     315        n = n >> 1
     316    _sig_off
     317    return sum
  • sage/structure/coerce_dict.pxd

    diff -r fa73e4213671 -r 9f227080425a sage/structure/coerce_dict.pxd
    a b cdef class TripleDict: 
    11cdef class TripleDict:
    22    cdef buckets
     3    cdef double threshold
    34    cdef get(self, k1, k2, k3)
    45    cdef set(self, k1, k2, k3, value)
    56   
  • sage/structure/coerce_dict.pyx

    diff -r fa73e4213671 -r 9f227080425a sage/structure/coerce_dict.pyx
    a b cdef class TripleDict: 
    102102       -- Robert Bradshaw, 2007-08
    103103    """
    104104   
    105     def __init__(self, size, data=None):
     105    def __init__(self, size, data=None, threshold=0):
    106106        """
    107107        Create a special dict using triples for keys.
    108108       
    cdef class TripleDict: 
    229229        return self.get(k1, k2, k3)
    230230           
    231231    cdef get(self, k1, k2, k3):
    232         cdef Py_ssize_t h = (<Py_ssize_t><void *>k1 + 13*<Py_ssize_t><void *>k2 + 503*<Py_ssize_t><void *>k3)
     232        cdef Py_ssize_t h = (<Py_ssize_t><void *>k1 + 13*<Py_ssize_t><void *>k2 ^ 503*<Py_ssize_t><void *>k3)
    233233        if h < 0: h = -h
    234234        cdef Py_ssize_t i
    235235        bucket = <object>PyList_GET_ITEM(self.buckets, h % PyList_GET_SIZE(self.buckets))
    cdef class TripleDict: 
    257257        self.set(k1, k2, k3, value)
    258258           
    259259    cdef set(self, k1, k2, k3, value):
    260         cdef Py_ssize_t h = (<Py_ssize_t><void *>k1 + 13*<Py_ssize_t><void *>k2 + 503*<Py_ssize_t><void *>k3)
     260        if self.threshold and len(self) > len(self.buckets) * self.threshold:
     261            self.resize()
     262        cdef Py_ssize_t h = (<Py_ssize_t><void *>k1 + 13*<Py_ssize_t><void *>k2 ^ 503*<Py_ssize_t><void *>k3)
    261263        if h < 0: h = -h
    262264        cdef Py_ssize_t i
    263265        bucket = <object>PyList_GET_ITEM(self.buckets, h % PyList_GET_SIZE(self.buckets))
    cdef class TripleDict: 
    284286            k1, k2, k3 = k
    285287        except (TypeError,ValueError):
    286288            raise KeyError, k
    287         cdef Py_ssize_t h = (<Py_ssize_t><void *>k1 + 13*<Py_ssize_t><void *>k2 + 503*<Py_ssize_t><void *>k3)
     289        cdef Py_ssize_t h = (<Py_ssize_t><void *>k1 + 13*<Py_ssize_t><void *>k2 ^ 503*<Py_ssize_t><void *>k3)
    288290        if h < 0: h = -h
    289291        cdef Py_ssize_t i
    290292        bucket = <object>PyList_GET_ITEM(self.buckets, h % PyList_GET_SIZE(self.buckets))
    cdef class TripleDict: 
    295297                del bucket[i:i+4]
    296298                return
    297299        raise KeyError, k
     300       
     301    def resize(self, int buckets=0):
     302        """
     303        Changes the number of buckets of self, while preserving the contents.
     304       
     305        If the number of buckes is 0 or not given, it resizes self to the
     306        smallest prime that is at least twice as large as self.
     307       
     308        EXAMPLES:
     309            sage: from sage.structure.coerce_dict import TripleDict
     310            sage: L = TripleDict(8)
     311            sage: for i in range(100): L[i,i,i] = None
     312            sage: L.bucket_lens() # random
     313            [50, 0, 0, 0, 50, 0, 0, 0]
     314            sage: L.resize(7) # random
     315            [15, 14, 14, 14, 14, 15, 14]
     316            sage: L.resize()
     317            sage: len(L.bucket_lens())
     318            17
     319        """
     320        if buckets == 0:
     321            from sage.rings.arith import next_prime
     322            buckets = next_prime(2*len(self.buckets))
     323        cdef TripleDict new = TripleDict(buckets, self)
     324        self.buckets = new.buckets
    298325           
    299326    def iteritems(self):
    300327        """
  • new file sage/structure/coerce_maps.pxd

    diff -r fa73e4213671 -r 9f227080425a sage/structure/coerce_maps.pxd
    - +  
     1from sage.categories.action cimport Action
     2from sage.categories.morphism cimport Morphism
     3
     4cdef class DefaultConvertMorphism(Morphism):
     5    cdef public bint _force_use
     6    cdef public bint _is_coercion
     7
     8cdef class DefaultConvertMorphism_unique(DefaultConvertMorphism):
     9    pass
     10
     11cdef class NamedConvertMorphism(Morphism):
     12    cdef readonly method_name
     13    cdef public bint _force_use
     14
     15cdef class TryMorphism(Morphism):
     16    cdef Morphism _morphism_p
     17    cdef Morphism _morphism_b
     18    cdef _error_types
  • new file sage/structure/coerce_maps.pyx

    diff -r fa73e4213671 -r 9f227080425a sage/structure/coerce_maps.pyx
    - +  
     1include "../ext/stdsage.pxi"
     2
     3import re
     4import types
     5
     6import sage.categories.homset as homset
     7from parent import Set_PythonType
     8from sage.structure.element cimport Element
     9from sage.structure.parent cimport Parent
     10
     11cdef object BuiltinMethodType = type(repr)
     12
     13# COERCE TODO: remove or integrate better (as this bit is only checked on an error)
     14cdef bint print_warnings = 0
     15
     16
     17cdef class DefaultConvertMorphism(Morphism):
     18    """
     19    This morphism simply differs action to the codomain's element_class method,
     20    passing in the codomain as the first argument.
     21    """
     22    def __init__(self, domain, codomain, force_use=False):
     23        if not PY_TYPE_CHECK(domain, Parent):
     24            domain = Set_PythonType(domain)
     25        Morphism.__init__(self, homset.Hom(domain, codomain))
     26        self._coerce_cost = 100
     27        self._force_use = force_use
     28        if self._codomain._element_class is None:
     29            raise RuntimeError, "BUG in coercion model, no element class for %s" % type(self._codomain)
     30
     31    cpdef Element _call_(self, x):
     32        try:
     33            return self._codomain._element_class(self._codomain, x)
     34        except:
     35            if print_warnings:
     36                print type(self._codomain), self._codomain
     37                print type(self._codomain._element_class), self._codomain._element_class
     38            raise
     39
     40    cpdef Element _call_with_args(self, x, args=(), kwds={}):
     41        try:
     42            if len(args) == 0:
     43                if len(kwds) == 0:
     44                    return self._codomain._element_class(self._codomain, x)
     45                else:
     46                    return self._codomain._element_class(self._codomain, x, **kwds)
     47            else:
     48                if len(kwds) == 0:
     49                    return self._codomain._element_class(self._codomain, x, *args)
     50                else:
     51                    return self._codomain._element_class(self._codomain, x, *args, **kwds)
     52        except:
     53            if print_warnings:
     54                print type(self._codomain), self._codomain
     55                print type(self._codomain._element_class), self._codomain._element_class
     56            raise
     57
     58    def _repr_type(self):
     59        return "Conversion"
     60
     61cdef class DefaultConvertMorphism_unique(DefaultConvertMorphism):
     62    """
     63    This morphism simply differs action to the codomain's element_class method,
     64    WITHOUT passing in the codomain as the first argument.
     65
     66    This is used for creating elements that don't take a parent as the first argument
     67    to their __init__ method, for example, Integers, Rationals, Algebraic Reals... all
     68    have a unique parent. It is also used when the element_class is a bound method
     69    (whose self argument is assumed to be bound to the codomain).
     70    """
     71    cpdef Element _call_(self, x):
     72        try:
     73            return self._codomain._element_class(x)
     74        except:
     75            if print_warnings:
     76                print type(self._codomain), self._codomain
     77                print type(self._codomain._element_class), self._codomain._element_class
     78            raise
     79
     80    cpdef Element _call_with_args(self, x, args=(), kwds={}):
     81        try:
     82            if len(args) == 0:
     83                if len(kwds) == 0:
     84                    return self._codomain._element_class(x)
     85                else:
     86                    return self._codomain._element_class(x, **kwds)
     87            else:
     88                if len(kwds) == 0:
     89                    return self._codomain._element_class(x, *args)
     90                else:
     91                    return self._codomain._element_class(x, *args, **kwds)
     92        except:
     93            if print_warnings:
     94                print type(self._codomain), self._codomain
     95                print type(self._codomain._element_class), self._codomain._element_class
     96            raise
     97
     98    def _repr_type(self):
     99        return "Coercion" if self._is_coercion else "Conversion"
     100
     101cdef class NamedConvertMorphism(Morphism):
     102    """
     103    This is used for creating a elements via the _xxx_ methods.
     104   
     105    For example, many elements implement an _integer_ method to
     106    convert to ZZ, or a _rational_ method to convert to QQ.
     107    """
     108   
     109    def __init__(self, domain, codomain, method_name, force_use=False):
     110        if PY_TYPE_CHECK(domain, type):
     111            domain = Set_PythonType(domain)
     112        Morphism.__init__(self, homset.Hom(domain, codomain))
     113        self._coerce_cost = 400
     114        self._force_use = force_use
     115        self.method_name = method_name
     116   
     117    cpdef Element _call_(self, x):
     118        try:
     119            method = getattr(x, self.method_name)
     120        except AttributeError:
     121            if print_warnings:
     122                print type(x), x
     123                print type(self._codomain), self._codomain
     124                print self.method_name
     125            raise TypeError, "Cannot coerce %s to %s"%(x, self._codomain)
     126        cdef Morphism m
     127        cdef Element e = method(self._codomain)
     128        if e is None:
     129            raise RuntimeError, "BUG in coercion model: %s method of %s returned None" % (self.method_name, type(x))
     130        if e._parent is not self._codomain:
     131            m = self._codomain.convert_map_from(e._parent)
     132            if m is None or m is self:
     133                raise TypeError
     134            e = m._call_(e)
     135        return e
     136
     137    def _repr_type(self):
     138        return "Conversion via %s" % self.method_name
     139   
     140cdef class CallableConvertMorphism(Morphism):
     141    cdef bint _parent_as_first_arg
     142    cdef _func
     143   
     144    def __init__(self, domain, codomain, func, parent_as_first_arg=None):
     145        """
     146        This lets one easily create morphims from any callable object.
     147       
     148        This is especially useful to create morphisms from bound methods.
     149       
     150        EXAMPLES:
     151            sage: from sage.structure.coerce_maps import CallableConvertMorphism
     152            sage: def foo(P, x): return x/2
     153            sage: f = CallableConvertMorphism(ZZ, QQ, foo)
     154            sage: f(3)
     155            3/2
     156            sage: f
     157            Conversion via foo morphism:
     158              From: Integer Ring
     159              To:   Rational Field
     160           
     161        Create a homomorphism from $\R$ to $\R^+$ viewed as additave groups.
     162            sage: f = CallableConvertMorphism(RR, RR, exp, parent_as_first_arg=False)
     163            sage: f(0)
     164            1.00000000000000
     165            sage: f(1)
     166            2.71828182845905
     167            sage: f(-3)
     168            0.0497870683678639
     169        """
     170        if PY_TYPE_CHECK(domain, type):
     171            domain = Set_PythonType(domain)
     172        Morphism.__init__(self, homset.Hom(domain, codomain))
     173        self._coerce_cost = 100
     174        self._func = func
     175        if parent_as_first_arg is None:
     176            if PY_TYPE_CHECK(func, types.MethodType):
     177                # can't easily access self
     178                parent_as_first_arg = False
     179            elif PY_TYPE_CHECK(func, BuiltinMethodType):
     180                parent_as_first_arg = codomain is func.__self__
     181            else:
     182                parent_as_first_arg = True
     183        self._parent_as_first_arg = parent_as_first_arg
     184   
     185    cpdef Element _call_(self, x):
     186        """
     187        Because self._func may be anything we do a little bit of sanity
     188        checking (the return value must be an element with the correct parent).
     189               
     190        TESTS:
     191            sage: from sage.structure.coerce_maps import CallableConvertMorphism
     192            sage: def foo(P, x): return x
     193            sage: f = CallableConvertMorphism(ZZ, ZZ, foo)
     194            sage: f(0)
     195            0
     196            sage: f = CallableConvertMorphism(ZZ, QQ, foo)
     197            sage: f(0)
     198            Traceback (most recent call last):
     199            ...
     200            RuntimeError: BUG in coercion model: <function foo at ...> returned element with wrong parent (expected Rational Field got Integer Ring)
     201            sage: f(None)
     202            Traceback (most recent call last):
     203            ...
     204            RuntimeError: BUG in coercion model: <function foo at ...> returned None
     205        """
     206        cdef Element y
     207        if self._parent_as_first_arg:
     208            y = self._func(self._codomain, x)
     209        else:
     210            y = self._func(x)
     211        if y is None:
     212            raise RuntimeError, "BUG in coercion model: %s returned None" % (self._func)
     213        elif y._parent is not self._codomain:
     214            raise RuntimeError, "BUG in coercion model: %s returned element with wrong parent (expected %s got %s)" % (self._func, self._codomain, y._parent)
     215        return y
     216
     217    def _repr_type(self):
     218        try:
     219            return "Conversion via %s" % self._func.__name__
     220        except AttributeError:
     221            return "Conversion via %s" % self._func
     222   
     223cdef class ListMorphism(Morphism):
     224
     225    cdef Morphism _real_morphism
     226
     227    def __init__(self, domain, Morphism real_morphism):
     228        if not PY_TYPE_CHECK(domain, Parent):
     229            domain = Set_PythonType(domain)
     230        Morphism.__init__(self, homset.Hom(domain, real_morphism.codomain()))
     231        self._coerce_cost = real_morphism._coerce_cost + 3
     232        self._real_morphism = real_morphism
     233
     234    cpdef Element _call_(self, x):
     235        try:
     236            x = x._data
     237        except AttributeError:
     238            x = list(x)
     239        return self._real_morphism._call_(x)
     240       
     241    cpdef Element _call_with_args(self, x, args=(), kwds={}):
     242        try:
     243            x = x._data
     244        except AttributeError:
     245            x = list(x)
     246        return self._real_morphism._call_with_args(x, args, kwds)
     247       
     248    def _repr_type(self):
     249        return "List"
     250
     251cdef class TryMorphism(Morphism):
     252    def __init__(self, morphism_preferred, morphism_backup, error_types=None):
     253        if morphism_preferred.parent() is not morphism_backup.parent():
     254            raise TypeError, "incorrectly matching parent"
     255        Morphism.__init__(self, morphism_preferred.parent())
     256        self._morphism_p = <Morphism?>morphism_preferred
     257        self._morphism_b = <Morphism?>morphism_backup
     258        if error_types is None:
     259            self._error_types = (ValueError, TypeError, AttributeError)
     260        else:
     261            self._error_types = error_types
     262
     263    cpdef Element _call_(self, x):
     264        try:
     265            return self._morphism_p._call_(x)
     266        except self._error_types:
     267            return self._morphism_b._call_(x)
     268
     269    cpdef Element _call_with_args(self, x, args=(), kwds={}):
     270        try:
     271            return self._morphism_p._call_with_args(x, args, kwds)
     272        except self._error_types:
     273            return self._morphism_b._call_with_args(x, args, kwds)
  • sage/structure/element.pxd

    diff -r fa73e4213671 -r 9f227080425a sage/structure/element.pxd
    a b cdef class Matrix(AlgebraElement): 
    152152   
    153153
    154154cdef class CoercionModel:
    155     cdef canonical_coercion_c(self, x, y)
    156     cdef canonical_base_coercion_c(self, Element x, Element y)
    157     cdef bin_op_c(self, x, y, op)
     155    cpdef canonical_coercion(self, x, y)
     156    cpdef bin_op(self, x, y, op)
    158157   
    159158   
  • sage/structure/element.pyx

    diff -r fa73e4213671 -r 9f227080425a sage/structure/element.pyx
    a b cdef class CoercionModel: 
    29342934    """
    29352935    Most basic coersion scheme. If it doesn't already match, throw an error.
    29362936    """
    2937     def canonical_coercion(self, x, y):
    2938         return self.canonical_coercion_c(x,y)
    2939     cdef canonical_coercion_c(self, x, y):
     2937    cpdef canonical_coercion(self, x, y):
    29402938        if parent_c(x) is parent_c(y):
    29412939            return x,y
    29422940        raise TypeError, "no common canonical parent for objects with parents: '%s' and '%s'"%(parent_c(x), parent_c(y))
    29432941
    2944     cdef canonical_base_coercion_c(self, Element x, Element y):
    2945         if have_same_base(x, y):
    2946             return x,y
    2947         raise TypeError, "Incompatible bases '%s' and '%s'"%(parent_c(x), parent_c(y))
    2948        
    2949     def bin_op(self, x, y, op):
    2950         return self.bin_op_c(x,y,op)
    2951     cdef bin_op_c(self, x, y, op):
     2942    cpdef bin_op(self, x, y, op):
    29522943        if parent_c(x) is parent_c(y):
    29532944            return op(x,y)
    29542945        raise TypeError, arith_error_message(x,y,op)
    def set_coercion_model(cm): 
    29752966def set_coercion_model(cm):
    29762967    global coercion_model
    29772968    coercion_model = cm
    2978    
    2979 def swap_coercion_model():
    2980     global coercion_model
    2981     if isinstance(coercion_model, coerce.CoercionModel_cache_maps):
    2982         coercion_model = coerce.CoercionModel_original()
    2983     else:
    2984         coercion_model = coerce.CoercionModel_cache_maps()
    2985     return coercion_model
    2986 
    29872969
    29882970
    29892971###############################################################################
  • setup.py

    diff -r fa73e4213671 -r 9f227080425a setup.py
    a b ext_modules = [ \ 
    502502
    503503    Extension('sage.structure.coerce',
    504504              sources = ['sage/structure/coerce.pyx']), \
     505
     506    Extension('sage.structure.coerce_actions',
     507              sources = ['sage/structure/coerce_actions.pyx']), \
     508
     509    Extension('sage.structure.coerce_maps',
     510              sources = ['sage/structure/coerce_maps.pyx']), \
    505511
    506512    Extension('sage.structure.coerce_dict',
    507513              sources = ['sage/structure/coerce_dict.pyx']), \