Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
""" Coerce actions """
#***************************************************************************** # Copyright (C) 2007 Robert Bradshaw <robertwb@math.washington.edu> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import absolute_import
import operator
from cpython.int cimport * from cpython.number cimport * from cysignals.signals cimport sig_check
from .element cimport parent, coercion_model, Element, ModuleElement from .parent cimport Parent from .coerce_exceptions import CoercionException from sage.categories.action cimport InverseAction, PrecomposedAction from sage.arith.long cimport integer_check_long
cdef _record_exception():
cdef inline an_element(R): else: pass
cdef class LAction(Action): """Action calls _l_action of the actor.""" def __init__(self, G, S): Action.__init__(self, G, S, True, operator.mul) cpdef _call_(self, g, a): return g._l_action(a) # a * g
cdef class RAction(Action): """Action calls _r_action of the actor.""" def __init__(self, G, S): Action.__init__(self, G, S, False, operator.mul) cpdef _call_(self, a, g): return g._r_action(a) # g * a
# In the code below, I take the convention that g is acting on a.
cdef class GenericAction(Action):
cdef _codomain
def __init__(self, Parent G, S, is_left, bint check=True): """ TESTS:
Note that coerce actions should only be used inside of the coercion model. For this test, we need to strongly reference the domains, for otherwise they could be garbage collected, giving rise to random errors (see :trac:`18157`). ::
sage: M = MatrixSpace(ZZ,2) sage: sage.structure.coerce_actions.ActedUponAction(M, Cusps, True) Left action by Full MatrixSpace of 2 by 2 dense matrices over Integer Ring on Set P^1(QQ) of all cusps
sage: Z6 = Zmod(6) sage: sage.structure.coerce_actions.GenericAction(QQ, Z6, True) Traceback (most recent call last): ... NotImplementedError: Action not implemented.
This will break if we tried to use it::
sage: sage.structure.coerce_actions.GenericAction(QQ, Z6, True, check=False) Left action by Rational Field on Ring of integers modulo 6
""" raise CoercionException
def codomain(self): """ Returns the "codomain" of this action, i.e. the Parent in which the result elements live. Typically, this should be the same as the acted upon set.
EXAMPLES:
Note that coerce actions should only be used inside of the coercion model. For this test, we need to strongly reference the domains, for otherwise they could be garbage collected, giving rise to random errors (see :trac:`18157`). ::
sage: M = MatrixSpace(ZZ,2) sage: A = sage.structure.coerce_actions.ActedUponAction(M, Cusps, True) sage: A.codomain() Set P^1(QQ) of all cusps
sage: S3 = SymmetricGroup(3) sage: QQxyz = QQ['x,y,z'] sage: A = sage.structure.coerce_actions.ActOnAction(S3, QQxyz, False) sage: A.codomain() Multivariate Polynomial Ring in x, y, z over Rational Field
"""
cdef class ActOnAction(GenericAction): """ Class for actions defined via the _act_on_ method. """ cpdef _call_(self, a, b): """ TESTS::
sage: G = SymmetricGroup(3) sage: R.<x,y,z> = QQ[] sage: A = sage.structure.coerce_actions.ActOnAction(G, R, False) sage: A._call_(x^2 + y - z, G((1,2))) y^2 + x - z sage: A._call_(x+2*y+3*z, G((1,3,2))) 2*x + 3*y + z
sage: type(A) <... 'sage.structure.coerce_actions.ActOnAction'> """ else:
cdef class ActedUponAction(GenericAction): """ Class for actions defined via the _acted_upon_ method. """ cpdef _call_(self, a, b): """ TESTS::
sage: M = MatrixSpace(ZZ,2) sage: A = sage.structure.coerce_actions.ActedUponAction(M, Cusps, True) sage: A.act(matrix(ZZ, 2, [1,0,2,-1]), Cusp(1,2)) Infinity sage: A._call_(matrix(ZZ, 2, [1,0,2,-1]), Cusp(1,2)) Infinity
sage: type(A) <... 'sage.structure.coerce_actions.ActedUponAction'> """ else:
def detect_element_action(Parent X, Y, bint X_on_left, X_el=None, Y_el=None): r""" Returns an action of X on Y or Y on X as defined by elements X, if any.
EXAMPLES:
Note that coerce actions should only be used inside of the coercion model. For this test, we need to strongly reference the domains, for otherwise they could be garbage collected, giving rise to random errors (see :trac:`18157`). ::
sage: from sage.structure.coerce_actions import detect_element_action sage: ZZx = ZZ['x'] sage: M = MatrixSpace(ZZ,2) sage: detect_element_action(ZZx, ZZ, False) Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring sage: detect_element_action(ZZx, QQ, True) Right scalar multiplication by Rational Field on Univariate Polynomial Ring in x over Integer Ring sage: detect_element_action(Cusps, M, False) Left action by Full MatrixSpace of 2 by 2 dense matrices over Integer Ring on Set P^1(QQ) of all cusps sage: detect_element_action(Cusps, M, True), (None,) sage: detect_element_action(ZZ, QQ, True), (None,)
TESTS:
This test checks that the issue in :trac:`7718` has been fixed::
sage: class MyParent(Parent): ....: def an_element(self): ....: pass ....: sage: A = MyParent() sage: detect_element_action(A, ZZ, True) Traceback (most recent call last): ... RuntimeError: an_element() for <__main__.MyParent object at ...> returned None """ cdef Element x else: else: raise RuntimeError("an_element() for %s returned None" % Y) else: # Elements defining _lmul_ and _rmul_ # Elements defining _act_on_ except CoercionException: _record_exception() # Elements defining _acted_upon_ except CoercionException: _record_exception()
cdef class ModuleAction(Action): """ Module action.
.. SEEALSO::
This is an abstract class, one must actually instantiate a :class:`LeftModuleAction` or a :class:`RightModuleAction`.
INPUT:
- ``G`` -- the actor, an instance of :class:`~sage.structure.parent.Parent`. - ``S`` -- the object that is acted upon. - ``g`` -- optional, an element of ``G``. - ``a`` -- optional, an element of ``S``. - ``check`` -- if True (default), then there will be no consistency tests performed on sample elements.
NOTE:
By default, the sample elements of ``S`` and ``G`` are obtained from :meth:`~sage.structure.parent.Parent.an_element`, which relies on the implementation of an ``_an_element_()`` method. This is not always awailable. But usually, the action is only needed when one already *has* two elements. Hence, by :trac:`14249`, the coercion model will pass these two elements to the :class:`ModuleAction` constructor.
The actual action is implemented by the ``_rmul_`` or ``_lmul_`` function on its elements. We must, however, be very particular about what we feed into these functions, because they operate under the assumption that the inputs lie exactly in the base ring and may segfault otherwise. Thus we handle all possible base extensions manually here.
""" def __init__(self, G, S, g=None, a=None, check=True): """ This creates an action of an element of a module by an element of its base ring. The simplest example to keep in mind is R acting on the polynomial ring R[x].
EXAMPLES:
Note that coerce actions should only be used inside of the coercion model. For this test, we need to strongly reference the domains, for otherwise they could be garbage collected, giving rise to random errors (see :trac:`18157`). ::
sage: from sage.structure.coerce_actions import LeftModuleAction sage: ZZx = ZZ['x'] sage: QQx = QQ['x'] sage: QQy = QQ['y'] sage: ZZxy = ZZ['x']['y'] sage: LeftModuleAction(ZZ, ZZx) Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring sage: LeftModuleAction(ZZ, QQx) Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Rational Field sage: LeftModuleAction(QQ, ZZx) Left scalar multiplication by Rational Field on Univariate Polynomial Ring in x over Integer Ring sage: LeftModuleAction(QQ, ZZxy) Left scalar multiplication by Rational Field on Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Integer Ring
The following tests against a problem that was relevant during work on :trac:`9944`::
sage: R.<x> = PolynomialRing(ZZ) sage: S.<x> = PolynomialRing(ZZ, sparse=True) sage: 1/R.0 1/x sage: 1/S.0 1/x
""" # only let Parents act # The right thing to do is a normal multiplication # Objects are implemented with the assumption that # _lmul_/_rmul_ are given an element of the base ring # first we try the easy case of coercing G to the base ring of S # otherwise, we try and find a base extension # this may raise a type error, which we propagate # make sure the pushout actually gave a correct base extension of S else: # this may happen if G is, say, int rather than a parent # TODO: let python types be valid actions
# Don't waste time if our connecting morphisms happen to be the identity.
self.extended_base = None
# At this point, we can assert it is safe to call _Xmul_
return raise CoercionException("The parent of %s is not %s but %s"%(g,G,parent(g))) raise CoercionException("not an Element acting on a ModuleElement") # In particular we will raise an error if res is None
def _repr_name_(self): """ The default name of this action type, which is has a sane default.
EXAMPLES:
Note that coerce actions should only be used inside of the coercion model. For this test, we need to strongly reference the domains, for otherwise they could be garbage collected, giving rise to random errors (see :trac:`18157`). ::
sage: from sage.structure.coerce_actions import LeftModuleAction, RightModuleAction sage: ZZx = ZZ['x'] sage: A = LeftModuleAction(ZZ, ZZx); A Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring sage: A._repr_name_() 'scalar multiplication'
sage: GF5 = GF(5) sage: GF5t = GF5[['t']] sage: RightModuleAction(GF5, GF5t) Right scalar multiplication by Finite Field of size 5 on Power Series Ring in t over Finite Field of size 5
"""
def codomain(self): """ The codomain of self, which may or may not be equal to the domain.
EXAMPLES:
Note that coerce actions should only be used inside of the coercion model. For this test, we need to strongly reference the domains, for otherwise they could be garbage collected, giving rise to random errors (see :trac:`18157`). ::
sage: from sage.structure.coerce_actions import LeftModuleAction sage: ZZxyz = ZZ['x,y,z'] sage: A = LeftModuleAction(QQ, ZZxyz) sage: A.codomain() Multivariate Polynomial Ring in x, y, z over Rational Field """
def domain(self): """ The domain of self, which is the module that is being acted on.
EXAMPLES:
Note that coerce actions should only be used inside of the coercion model. For this test, we need to strongly reference the domains, for otherwise they could be garbage collected, giving rise to random errors (see :trac:`18157`). ::
sage: from sage.structure.coerce_actions import LeftModuleAction sage: ZZxyz = ZZ['x,y,z'] sage: A = LeftModuleAction(QQ, ZZxyz) sage: A.domain() Multivariate Polynomial Ring in x, y, z over Integer Ring """
def __invert__(self): """ EXAMPLES:
Note that coerce actions should only be used inside of the coercion model. For this test, we need to strongly reference the domains, for otherwise they could be garbage collected, giving rise to random errors (see :trac:`18157`). ::
sage: from sage.structure.coerce_actions import RightModuleAction
sage: ZZx = ZZ['x'] sage: x = ZZx.gen() sage: QQx = QQ['x'] sage: A = ~RightModuleAction(QQ, QQx); A Right inverse action by Rational Field on Univariate Polynomial Ring in x over Rational Field sage: A(x, 2) 1/2*x
sage: A = ~RightModuleAction(QQ, ZZx); A Right inverse action by Rational Field on Univariate Polynomial Ring in x over Integer Ring sage: A.codomain() Univariate Polynomial Ring in x over Rational Field sage: A(x, 2) 1/2*x
sage: A = ~RightModuleAction(ZZ, ZZx); A Right inverse action by Rational Field on Univariate Polynomial Ring in x over Integer Ring with precomposition on right by Natural morphism: From: Integer Ring To: Rational Field sage: A.codomain() Univariate Polynomial Ring in x over Rational Field sage: A(x, 2) 1/2*x
sage: GF5x = GF(5)['x'] sage: A = ~RightModuleAction(ZZ, GF5x); A Right inverse action by Finite Field of size 5 on Univariate Polynomial Ring in x over Finite Field of size 5 with precomposition on right by Natural morphism: From: Integer Ring To: Finite Field of size 5 sage: A(x, 2) 3*x
sage: GF5xy = GF5x['y'] sage: A = ~RightModuleAction(ZZ, GF5xy); A Right inverse action by Finite Field of size 5 on Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Finite Field of size 5 with precomposition on right by Natural morphism: From: Integer Ring To: Finite Field of size 5
sage: ZZy = ZZ['y'] sage: ZZxyzw = ZZx['y']['z']['w'] sage: A = ~RightModuleAction(ZZy, ZZxyzw); A Right inverse action by Fraction Field of Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Integer Ring on Univariate Polynomial Ring in w over Univariate Polynomial Ring in z over Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Integer Ring with precomposition on right by Conversion via FractionFieldElement map: From: Univariate Polynomial Ring in y over Integer Ring To: Fraction Field of Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Integer Ring
TESTS:
See :trac:`19521`::
sage: Q.<y> = SR.subring(no_variables=True)[[]] sage: (y / 1).parent() Power Series Ring in y over Symbolic Constants Subring sage: R.<x> = SR.subring(no_variables=True)[] sage: cm = sage.structure.element.get_coercion_model() sage: cm.explain(x, 1, operator.truediv) Action discovered. Right inverse action by Symbolic Constants Subring on Univariate Polynomial Ring in x over Symbolic Constants Subring with precomposition on right by Coercion map: From: Integer Ring To: Symbolic Constants Subring Result lives in Univariate Polynomial Ring in x over Symbolic Constants Subring Univariate Polynomial Ring in x over Symbolic Constants Subring """ else: # Try to find a suitable ring between G and R in which to compute # the inverse. K = R._pseudo_fraction_field()
# Suppose we have parents of a construction tower # A -> B -> C <- D <- E -> F <- G -> H, # where the arrows specify the direction of coercion (i.e. # A -> B means A coerces to B). Note that B = FA(A), C = FB(B), ... # As we want to find a "smallest" parent with some properties, # we need the order # A, B, E, D, C, G, F, H # for our search. Thus the elements connected with a <- have to # be reversed. See code below. else:
cdef class LeftModuleAction(ModuleAction):
cpdef _call_(self, g, a): """ A left module action is an action that takes the ring element as the first argument (the left side) and the module element as the second argument (the right side).
EXAMPLES:
Note that coerce actions should only be used inside of the coercion model. For this test, we need to strongly reference the domains, for otherwise they could be garbage collected, giving rise to random errors (see :trac:`18157`). ::
sage: from sage.structure.coerce_actions import LeftModuleAction sage: R.<x> = QQ['x'] sage: A = LeftModuleAction(ZZ, R) sage: A(5, x+1) 5*x + 5 sage: R.<x> = ZZ['x'] sage: A = LeftModuleAction(QQ, R) sage: A(1/2, x+1) 1/2*x + 1/2 sage: A._call_(1/2, x+1) # safe only when arguments have exactly the correct parent 1/2*x + 1/2 """ # The way we are called, we know for sure that a is a # ModuleElement and that g is an Element. # If we change a or g, we need to explicitly check this, # which explains the <?> casts below.
cdef class RightModuleAction(ModuleAction): cpdef _call_(self, a, g): """ A right module action is an action that takes the module element as the first argument (the left side) and the ring element as the second argument (the right side).
EXAMPLES:
Note that coerce actions should only be used inside of the coercion model. For this test, we need to strongly reference the domains, for otherwise they could be garbage collected, giving rise to random errors (see :trac:`18157`). ::
sage: from sage.structure.coerce_actions import RightModuleAction sage: R.<x> = QQ['x'] sage: A = RightModuleAction(ZZ, R) sage: A(x+5, 2) 2*x + 10 sage: A._call_(x+5, 2) # safe only when arguments have exactly the correct parent 2*x + 10 """ # The way we are called, we know for sure that a is a # ModuleElement and that g is an Element. # If we change a or g, we need to explicitly check this, # which explains the <?> casts below.
cdef class IntegerAction(Action): """ Abstract base class representing some action by integers on something. Here, "integer" is defined loosely in the "duck typing" sense.
INPUT:
- ``Z`` -- a type or parent representing integers
For the other arguments, see :class:`Action`.
.. NOTE::
This class is used internally in Sage's coercion model. Outside of the coercion model, special precautions are needed to prevent domains of the action from being garbage collected. """ def __init__(self, Z, S, is_left, op):
def __invert__(self): """ EXAMPLES::
sage: from sage.structure.coerce_actions import IntegerMulAction sage: act = IntegerMulAction(ZZ, CDF) sage: ~act Traceback (most recent call last): ... TypeError: actions by ZZ cannot be inverted """
cdef class IntegerMulAction(IntegerAction): r""" Implement the action `n \cdot a = a + a + ... + a` via repeated doubling.
Both addition and negation must be defined on the set `M`.
INPUT:
- ``Z`` -- a type or parent representing integers
- ``M`` -- a ``ZZ``-module
- ``m`` -- (optional) an element of ``M``
EXAMPLES::
sage: from sage.structure.coerce_actions import IntegerMulAction sage: R.<x> = QQ['x'] sage: act = IntegerMulAction(ZZ, R) sage: act(5, x) 5*x sage: act(0, x) 0 sage: act(-3, x-1) -3*x + 3 """ def __init__(self, Z, M, is_left=True, m=None):
cpdef _call_(self, nn, a): """ EXAMPLES:
Note that coerce actions should only be used inside of the coercion model. For this test, we need to strongly reference the field ``GF(101)``, for otherwise it could be garbage collected, giving rise random errors (see :trac:`18157`). ::
sage: from sage.structure.coerce_actions import IntegerMulAction sage: GF101 = GF(101) sage: act = IntegerMulAction(ZZ, GF101) sage: act(3, 9) 27 sage: act(3^689, 9) 42 sage: 3^689 * mod(9, 101) 42
TESTS:
Use round off error to verify this is doing actual repeated addition instead of just multiplying::
sage: act = IntegerMulAction(ZZ, RR) sage: act(49, 1/49) == 49*RR(1/49) False
This used to hang before :trac:`17844`::
sage: E = EllipticCurve(GF(5), [4,0]) sage: P = E.random_element() sage: (-2^63)*P (0 : 1 : 0)
Check that large multiplications can be interrupted::
sage: alarm(0.5); (2^(10^7)) * P # not tested; see trac:#24986 Traceback (most recent call last): ... AlarmInterrupt
""" cdef long n_long
def _repr_name_(self): """ EXAMPLES:
Note that coerce actions should only be used inside of the coercion model. For this test, we need to strongly reference the field ``GF(5)``, for otherwise it could be garbage collected, giving rise random errors (see :trac:`18157`). ::
sage: from sage.structure.coerce_actions import IntegerMulAction sage: GF5 = GF(5) sage: IntegerMulAction(ZZ, GF5) Left Integer Multiplication by Integer Ring on Finite Field of size 5 """
cdef class IntegerPowAction(IntegerAction): r""" The right action ``a ^ n = a * a * ... * a`` where `n` is an integer.
The action is implemented using the ``_pow_int`` method on elements.
INPUT:
- ``Z`` -- a type or parent representing integers
- ``M`` -- a parent whose elements implement ``_pow_int``
- ``m`` -- (optional) an element of ``M``
EXAMPLES::
sage: from sage.structure.coerce_actions import IntegerPowAction sage: R.<x> = LaurentSeriesRing(QQ) sage: act = IntegerPowAction(ZZ, R) sage: act(x, 5) x^5 sage: act(x, -2) x^-2 sage: act(x, int(5)) x^5
TESTS::
sage: IntegerPowAction(ZZ, R, True) Traceback (most recent call last): ... ValueError: powering must be a right action sage: IntegerPowAction(ZZ, QQ^3) Traceback (most recent call last): ... TypeError: no integer powering action defined on Vector space of dimension 3 over Rational Field
::
sage: var('x,y') (x, y) sage: RDF('-2.3')^(x+y^3+sin(x)) (-2.3)^(y^3 + x + sin(x)) sage: RDF('-2.3')^x (-2.3)^x """ def __init__(self, Z, M, is_left=False, m=None): # Check that there is a _pow_int() method
cpdef _call_(self, a, n): """ EXAMPLES:
Note that coerce actions should only be used inside of the coercion model. For this test, we need to strongly reference the field ``GF(101)``::
sage: from sage.structure.coerce_actions import IntegerPowAction sage: GF101 = GF(101) sage: act = IntegerPowAction(ZZ, GF101) sage: act(3, 100) 1 sage: act(3, -1) 34 sage: act(3, 1000000000000000000000000000000000000000000001) 3 """ cdef int err
def _repr_name_(self): """ EXAMPLES::
sage: from sage.structure.coerce_actions import IntegerPowAction sage: IntegerPowAction(ZZ, QQ) Right Integer Powering by Integer Ring on Rational Field """
cdef inline fast_mul(a, n): n = -n a = -a
cdef inline fast_mul_long(a, long s): # It's important to change the signed s to an unsigned n, # since -LONG_MIN = LONG_MIN. See Trac #17844. cdef unsigned long n else: elif n == 1: return a elif n == 2: return a+a elif n == 3: return a+a+a |