Ticket #14249: trac_14249-coercion_without_an_element.patch

File trac_14249-coercion_without_an_element.patch, 19.6 KB (added by SimonKing, 10 years ago)

Combined patch

  • sage/structure/coerce.pxd

    # HG changeset patch
    # User Simon King <simon.king@uni-jena.de>
    # Date 1363171507 -3600
    # Node ID 59cc9751e0c0725283d3ccf5362fdcd278689d48
    # Parent  3a9ba2ec449be4de0dfc3179889864589de813e5
    #14249: Do not call an_element() in coercion if actual elements are given.
    
    diff --git a/sage/structure/coerce.pxd b/sage/structure/coerce.pxd
    a b  
    2020    cpdef verify_coercion_maps(self, R, S, homs, bint fix=*)
    2121    cpdef verify_action(self, action, R, S, op, bint fix=*)
    2222
    23     cpdef get_action(self, xp, yp, op)
    24     cpdef discover_action(self, R, S, op)
     23    cpdef get_action(self, R, S, op, r=*, s=*)
     24    cpdef discover_action(self, R, S, op, r=*, s=*)
    2525
    2626    cdef bint _record_exceptions
    2727    cpdef _record_exception(self)
  • sage/structure/coerce.pyx

    diff --git a/sage/structure/coerce.pyx b/sage/structure/coerce.pyx
    a b  
    794794            yp = parent_c(y)
    795795            if xp is yp:
    796796                return op(x,y)
    797             action = self.get_action(xp, yp, op)
     797            action = self.get_action(xp, yp, op, x, y)
    798798            if action is not None:
    799799                return (<Action>action)._call_(x, y)
    800800       
     
    12341234        return None
    12351235       
    12361236   
    1237     cpdef get_action(self, R, S, op):
     1237    cpdef get_action(self, R, S, op, r=None, s=None):
    12381238        """
    12391239        Get the action of R on S or S on R associated to the operation op.
    1240        
     1240
     1241
     1242
    12411243        EXAMPLES::
    1242        
     1244
    12431245            sage: cm = sage.structure.element.get_coercion_model()
    12441246            sage: cm.get_action(ZZ['x'], ZZ, operator.mul)
    12451247            Right scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
     
    12671269            return self._action_maps.get(R, S, op)
    12681270        except KeyError:
    12691271            pass
    1270         action = self.discover_action(R, S, op)
     1272        action = self.discover_action(R, S, op, r, s)
    12711273        action = self.verify_action(action, R, S, op)
    12721274        self._action_maps.set(R, S, op, action)
    12731275        return action
     
    13301332
    13311333        return action
    13321334           
    1333     cpdef discover_action(self, R, S, op):
     1335    cpdef discover_action(self, R, S, op, r=None, s=None):
    13341336        """
    13351337        INPUT
    13361338
    13371339        - ``R`` - the left Parent (or type)
    1338 
    13391340        - ``S`` - the right Parent (or type)
    1340 
    1341         - ``op`` - the operand, typically an element of the operator module.
     1341        - ``op`` - the operand, typically an element of the operator module
     1342        - ``r`` - (optional) element of R
     1343        - ``s`` - (optional) element of S.
    13421344           
    13431345        OUTPUT:
    13441346
    13451347        - An action A such that s op r is given by A(s,r).
    1346        
     1348
    13471349        The steps taken are illustrated below.
    1348        
     1350
    13491351        EXAMPLES::
    1350        
     1352
    13511353            sage: P.<x> = ZZ['x']
    13521354            sage: P.get_action(ZZ)
    13531355            Right scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
     
    13561358            sage: cm = sage.structure.element.get_coercion_model()
    13571359
    13581360        If R or S is a Parent, ask it for an action by/on R::
    1359        
     1361
    13601362            sage: cm.discover_action(ZZ, P, operator.mul)
    13611363            Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
    1362            
     1364
    13631365        If R or S a type, recursively call get_action with the Sage versions of R and/or S::
    1364        
     1366
    13651367            sage: cm.discover_action(P, int, operator.mul)
    13661368            Right scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
    13671369            with precomposition on right by Native morphism:
    13681370              From: Set of Python objects of type 'int'
    13691371              To:   Integer Ring
    1370        
     1372
    13711373        If op in an inplace operation, look for the non-inplace action::
    1372        
     1374
    13731375            sage: cm.discover_action(P, ZZ, operator.imul)
    13741376            Right scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
    1375            
     1377
    13761378        If op is division, look for action on right by inverse::
    1377        
     1379
    13781380            sage: cm.discover_action(P, ZZ, operator.div)
    13791381            Right inverse action by Rational Field on Univariate Polynomial Ring in x over Integer Ring
    13801382            with precomposition on right by Natural morphism:
     
    13841386        #print "looking", R, <int><void *>R, op, S, <int><void *>S
    13851387   
    13861388        if PY_TYPE_CHECK(R, Parent):
    1387             action = (<Parent>R).get_action(S, op, True)
     1389            action = (<Parent>R).get_action(S, op, True, r, s)
    13881390            if action is not None:
    13891391                #print "found2", action
    13901392                return action
    13911393       
    13921394        if PY_TYPE_CHECK(S, Parent):
    1393             action = (<Parent>S).get_action(R, op, False)
     1395            action = (<Parent>S).get_action(R, op, False, s, r)
    13941396            if action is not None:
    13951397                #print "found1", action
    13961398                return action
     
    13981400        if PY_TYPE(R) == <void *>type:
    13991401            sageR = py_scalar_parent(R)
    14001402            if sageR is not None:
    1401                 action = self.discover_action(sageR, S, op)
     1403                action = self.discover_action(sageR, S, op, s=s)
    14021404                if action is not None:
    14031405                    if not PY_TYPE_CHECK(action, IntegerMulAction):
    14041406                        action = PrecomposedAction(action, sageR.coerce_map_from(R), None)
     
    14071409        if PY_TYPE(S) == <void *>type:
    14081410            sageS = py_scalar_parent(S)
    14091411            if sageS is not None:
    1410                 action = self.discover_action(R, sageS, op)
     1412                action = self.discover_action(R, sageS, op, r=r)
    14111413                if action is not None:
    14121414                    if not PY_TYPE_CHECK(action, IntegerMulAction):
    14131415                        action = PrecomposedAction(action, None, sageS.coerce_map_from(S))
     
    14151417       
    14161418        if op.__name__[0] == 'i':
    14171419            try:
    1418                 a = self.discover_action(R, S, no_inplace_op(op))
     1420                a = self.discover_action(R, S, no_inplace_op(op), r, s)
    14191421                if a is not None:
    14201422                    is_inverse = isinstance(a, InverseAction)
    14211423                    if is_inverse: a = ~a
    14221424                    if a is not None and PY_TYPE_CHECK(a, RightModuleAction):
    14231425                        # We want a new instance so that we don't alter the (potentially cached) original
    1424                         a = RightModuleAction(S, R)
     1426                        a = RightModuleAction(S, R, s, r)
    14251427                        a.is_inplace = 1
    14261428                    if is_inverse: a = ~a
    14271429                return a
  • sage/structure/coerce_actions.pyx

    diff --git a/sage/structure/coerce_actions.pyx b/sage/structure/coerce_actions.pyx
    a b  
    151151        else:
    152152            return (<Element>a)._acted_upon_(b, True)
    153153
    154 def detect_element_action(Parent X, Y, bint X_on_left):
     154def detect_element_action(Parent X, Y, bint X_on_left, X_el=None, Y_el=None):
    155155    r"""
    156156    Returns an action of X on Y or Y on X as defined by elements X, if any.
    157157
     
    171171
    172172    TESTS:
    173173
    174     This test checks that the issue in Trac #7718 has been fixed::
     174    This test checks that the issue in :trac:`7718` has been fixed::
    175175
    176176        sage: class MyParent(Parent):
    177         ...    def an_element(self):
    178         ...        pass
    179         ...
     177        ....:     def an_element(self):
     178        ....:         pass
     179        ....:
    180180        sage: A = MyParent()
    181181        sage: detect_element_action(A, ZZ, True)
    182182        Traceback (most recent call last):
    183183        ...
    184184        RuntimeError: an_element() for <class '__main__.MyParent'> returned None
    185185    """
    186     cdef Element x = an_element(X)
     186    cdef Element x
     187    if X_el is None or (parent_c(X_el) is not X):
     188        x = an_element(X)
     189    else:
     190        x = X_el
    187191    if x is None:
    188         raise RuntimeError, "an_element() for %s returned None" % X
    189     y = an_element(Y)
     192        raise RuntimeError("an_element() for %s returned None" % X)
     193    if Y_el is None or (parent_c(Y_el) is not Y):
     194        y = an_element(Y)
     195    else:
     196        y = Y_el
    190197    if y is None:
    191198        if isinstance(Y, Parent):
    192             raise RuntimeError, "an_element() for %s returned None" % Y
     199            raise RuntimeError("an_element() for %s returned None" % Y)
    193200        else:
    194201            return # don't know how to make elements of this type...
    195202    if isinstance(x, ModuleElement) and isinstance(y, RingElement):
    196203        # Elements defining _lmul_ and _rmul_
    197204        try:
    198             return (RightModuleAction if X_on_left else LeftModuleAction)(Y, X)
    199         except CoercionException:
     205            return (RightModuleAction if X_on_left else LeftModuleAction)(Y, X, y, x)
     206        except CoercionException, msg:
    200207            _record_exception()
    201208    try:
    202209        # Elements defining _act_on_
     
    214221
    215222
    216223cdef class ModuleAction(Action):
     224    """
     225    Module action.
    217226
    218     def __init__(self, G, S):
     227    .. SEEALSO::
     228
     229        This is an abstract class, one must actually instantiate a
     230        :class:`LeftModuleAction` or a :class:`RightModuleAction`.
     231
     232    INPUT:
     233
     234    - ``G`` -- the actor, an instance of :class:`~sage.structure.parent.Parent`.
     235    - ``S`` -- the object that is acted upon.
     236    - ``g`` -- optional, an element of ``G``.
     237    - ``a`` -- optional, an element of ``S``.
     238    - ``check`` -- if True (default), then there will be no consistency tests
     239      performed on sample elements.
     240
     241    NOTE:
     242
     243    By default, the sample elements of ``S`` and ``G`` are obtained from
     244    :meth:`~sage.structure.parent.Parent.an_element`, which relies on the
     245    implementation of an ``_an_element_()`` method. This is not always
     246    awailable. But usually, the action is only needed when one already
     247    *has* two elements. Hence, by :trac:`14249`, the coercion model will
     248    pass these two elements the the :class:`ModuleAction` constructor.
     249       
     250    The actual action is implemented by the ``_rmul_`` or ``_lmul_``
     251    function on its elements. We must, however, be very particular about
     252    what we feed into these functions, because they operate under the
     253    assumption that the inputs lie exactly in the base ring and may
     254    segfault otherwise. Thus we handle all possible base extensions
     255    manually here.
     256
     257    """
     258    def __init__(self, G, S, g=None, a=None, check=True):
    219259        """
    220260        This creates an action of an element of a module by an element of its
    221261        base ring. The simplest example to keep in mind is R acting on the
    222262        polynomial ring R[x].
    223263       
    224         The actual action is implemented by the _rmul_ or _lmul_ function on
    225         its elements. We must, however, be very particular about what we feed
    226         into these functions because they operate under the assumption that
    227         the inputs lie exactly in the base ring and may segfault otherwise.
    228        
    229         Thus we handle all possible base extensions manually here. This is
    230         an abstract class, one must actually instantiate a LeftModuleAction
    231         or a RightModuleAction
    232        
    233264        EXAMPLES::
    234265       
    235266            sage: from sage.structure.coerce_actions import LeftModuleAction
     
    293324        the_set = S if self.extended_base is None else self.extended_base
    294325        assert the_ring is the_set.base(), "BUG in coercion model\n    Apparently there are two versions of\n        %s\n    in the cache."%the_ring
    295326
    296         g = G.an_element()
    297         a = S.an_element()
     327        if not check:
     328            return
     329        if g is None:
     330            g = G.an_element()
     331        if parent_c(g) is not G:
     332            raise CoercionException("The parent of %s is not %s but %s"%(g,G,parent_c(g)))
     333        if a is None:
     334            a = S.an_element()
     335        if parent_c(a) is not S:
     336            raise CoercionException("The parent of %s is not %s but %s"%(a,S,parent_c(a)))
    298337        if not isinstance(g, RingElement) or not isinstance(a, ModuleElement):
    299             raise CoercionException, "Not a ring element acting on a module element."
     338            raise CoercionException("Not a ring element acting on a module element.")
    300339        res = self.act(g, a)
    301        
    302         if parent_c(res) is not S and parent_c(res) is not self.extended_base:
     340        if parent_c(res) is not the_set:
    303341            # In particular we will raise an error if res is None
    304             raise CoercionException, "Result is None or has wrong parent."
     342            raise CoercionException("Result is None or has wrong parent.")
    305343           
    306344
    307345    def _repr_name_(self):
     
    425463
    426464cdef class IntegerMulAction(Action):
    427465
    428     def __init__(self, ZZ, M, is_left=True):
     466    def __init__(self, ZZ, M, is_left=True, m=None):
    429467        r"""
    430468        This class implements the action `n \cdot a = a + a + \cdots + a` via
    431469        repeated doubling.
    432470       
    433         Both addition and negation must be defined on the set `A`.
     471        Both addition and negation must be defined on the set `M`.
     472
     473        INPUT:
     474
     475        - An integer ring, ``ZZ``
     476        - A ``ZZ`` module ``M``
     477        - Optional: An element ``m`` of ``M``
    434478       
    435479        EXAMPLES::
    436480       
     
    447491        if PY_TYPE_CHECK(ZZ, type):
    448492            from sage.structure.parent import Set_PythonType
    449493            ZZ = Set_PythonType(ZZ)
    450         test = M.an_element() + (-M.an_element()) # make sure addition and negation is allowed
     494        if m is None:
     495            m = M.an_element()
     496        test = m + (-m) # make sure addition and negation is allowed
    451497        Action.__init__(self, ZZ, M, is_left, operator.mul)
    452498
    453499    cpdef _call_(self, nn, a):
  • sage/structure/parent.pxd

    diff --git a/sage/structure/parent.pxd b/sage/structure/parent.pxd
    a b  
    4646   
    4747    # returns the Action by/on self on/by S
    4848    # corresponding to op and self_on_left
    49     cpdef get_action(self, other, op=*, bint self_on_left=*)
    50     cpdef _get_action_(self, other, op, bint self_on_left)
     49    cpdef get_action(self, S, op=*, bint self_on_left=*, self_el=*, S_el=*)
     50    cpdef _get_action_(self, S, op, bint self_on_left)
    5151   
    5252    # coerce x into self
    5353    cpdef coerce(self, x)
     
    6060    cpdef _generic_convert_map(self, S)
    6161    cdef discover_coerce_map_from(self, S)
    6262    cdef discover_convert_map_from(self, S)
    63     cdef discover_action(self, G, op, bint self_on_left)
     63    cdef discover_action(self, S, op, bint self_on_left, self_el=*, S_el=*)
    6464
    6565    # List consisting of Morphisms (from anything to self)
    6666    # and Parents for which the __call__ method of self
  • sage/structure/parent.pyx

    diff --git a/sage/structure/parent.pyx b/sage/structure/parent.pyx
    a b  
    5656    ...
    5757    AssertionError
    5858
     59When implementing an element of a ring, one would typically provide the
     60element class with ``_rmul_`` and/or ``_lmul_`` methods for the action of a
     61base ring, and with ``_mul_`` for the ring multiplication. However, prior to
     62:trac:`14249`, it would have been necessary to additionally define a method
     63``_an_element_()`` for the parent. But now, the following example works::
     64
     65    sage: from sage.structure.element import RingElement
     66    sage: class MyElement(RingElement):
     67    ....:      def __init__(self, x,y, parent = None):
     68    ....:          RingElement.__init__(self, parent)
     69    ....:      def _mul_(self, other):
     70    ....:          return self
     71    ....:      def _rmul_(self, other):
     72    ....:          return self
     73    ....:      def _lmul_(self, other):
     74    ....:          return self
     75    sage: class MyParent(Parent):
     76    ....:      Element = MyElement
     77
     78Now, we define ::
     79
     80    sage: P = MyParent(base=ZZ, category=Rings())
     81    sage: a = P(1,2)
     82    sage: a*a is a
     83    True
     84    sage: a*2 is a
     85    True
     86    sage: 2*a is a
     87    True
     88
    5989TESTS:
    6090
    6191This came up in some subtle bug once::
    6292
    6393    sage: gp(2) + gap(3)
    6494    5
     95
    6596"""
    6697
    6798cimport element
     
    23482379        """
    23492380        return None
    23502381
    2351     cpdef get_action(self, S, op=operator.mul, bint self_on_left=True):
     2382    cpdef get_action(self, S, op=operator.mul, bint self_on_left=True, self_el=None, S_el=None):
    23522383        """
    23532384        Returns an action of self on S or S on self.
    23542385       
     
    23702401
    23712402        action = self._get_action_(S, op, self_on_left)
    23722403        if action is None:
    2373             action = self.discover_action(S, op, self_on_left)
     2404            action = self.discover_action(S, op, self_on_left, self_el, S_el)
    23742405
    23752406        if action is not None:
    23762407            from sage.categories.action import Action
     
    23832414        return action
    23842415       
    23852416
    2386     cdef discover_action(self, S, op, bint self_on_left):
     2417    cdef discover_action(self, S, op, bint self_on_left, self_el=None, S_el=None):
    23872418        # G acts on S, G -> G', R -> S => G' acts on R (?)
    23882419        # NO! ZZ[x,y] acts on Matrices(ZZ[x]) but ZZ[y] does not.
    23892420        # What may be true is that if the action's destination is S, then this can be allowed.
     2421        # Note: a is either None or a sample elements of self.
     2422        # If needed, it will be passed to Left/RightModuleAction.
    23902423        from sage.categories.action import Action, PrecomposedAction
    23912424        from sage.categories.homset import Hom
    23922425        from coerce_actions import LeftModuleAction, RightModuleAction
     
    24042437                    R = action
    24052438                    _register_pair(self, R, "action") # to kill circular recursion
    24062439                    if self_on_left:
    2407                         action = LeftModuleAction(R, self) # self is acted on from right
     2440                        action = LeftModuleAction(R, self, a=S_el, g=self_el) # self is acted on from right
    24082441                    else:
    2409                         action = RightModuleAction(R, self) # self is acted on from left
     2442                        action = RightModuleAction(R, self, a=S_el, g=self_el) # self is acted on from left
    24102443                    ## The following two lines are disabled to prevent the following from working:
    24112444                    ## sage: x, y = var('x,y')
    24122445                    ## sage: parent(ZZ[x][y](1)*vector(QQ[y],[1,2]))
     
    24442477               
    24452478                # detect actions defined by _rmul_, _lmul_, _act_on_, and _acted_upon_ methods
    24462479                from coerce_actions import detect_element_action
    2447                 action = detect_element_action(self, S, self_on_left)
     2480                action = detect_element_action(self, S, self_on_left, self_el, S_el)
    24482481                if action is not None:
    24492482                    return action
    2450                
     2483
    24512484                try:
    24522485                    # maybe there is a more clever way of detecting ZZ than importing here...
    24532486                    from sage.rings.integer_ring import ZZ
    24542487                    if S is ZZ and not self.has_coerce_map_from(ZZ):
    24552488                        from sage.structure.coerce_actions import IntegerMulAction
    2456                         action = IntegerMulAction(S, self, not self_on_left)
     2489                        action = IntegerMulAction(S, self, not self_on_left, self_el)
    24572490                        return action
    24582491                except (CoercionException, TypeError):
    24592492                    _record_exception()
  • sage/structure/parent_old.pyx

    diff --git a/sage/structure/parent_old.pyx b/sage/structure/parent_old.pyx
    a b  
    241241           
    242242    cdef get_action_c_impl(self, S, op, bint self_on_left):
    243243        check_old_coerce(self)
    244         return self.discover_action(S, op, self_on_left)               
     244        return self.discover_action(S, op, self_on_left, None, None)               
    245245
    246246    #################################################################################
    247247    # Coercion support functionality