Ticket #13412: trac_13412_category_for_power_series_rings.patch

File trac_13412_category_for_power_series_rings.patch, 20.8 KB (added by SimonKing, 7 years ago)

Implement category and coercion framework for power series rings

  • sage/rings/multi_power_series_ring.py

    # HG changeset patch
    # User Simon King <simon.king@uni-jena.de>
    # Date 1346332026 -7200
    # Node ID 7956267cdba8eebcd928587db2384b629218c021
    # Parent  e39420b4a8a950c8a51dabb70a1ca88a3b4df720
    #13412: Implement category and coercion framework for power series rings
    
    diff --git a/sage/rings/multi_power_series_ring.py b/sage/rings/multi_power_series_ring.py
    a b  
    99
    1010    sage: R.<t,u,v> = PowerSeriesRing(QQ); R
    1111    Multivariate Power Series Ring in t, u, v over Rational Field
     12    sage: TestSuite(R).run()
    1213    sage: p = -t + 1/2*t^3*u - 1/4*t^4*u + 2/3*v^5 + R.O(6); p
    1314    -t + 1/2*t^3*u - 1/4*t^4*u + 2/3*v^5 + O(t, u, v)^6
    1415    sage: p in R
     
    4344
    4445    sage: M = PowerSeriesRing(QQ, 4, 'k'); M
    4546    Multivariate Power Series Ring in k0, k1, k2, k3 over Rational Field
     47    sage: loads(dumps(M)) is M
     48    True
     49    sage: TestSuite(M).run()
    4650
    4751    sage: H = PowerSeriesRing(PolynomialRing(ZZ,3,'z'),4,'f'); H
    4852    Multivariate Power Series Ring in f0, f1, f2, f3 over Multivariate
    4953    Polynomial Ring in z0, z1, z2 over Integer Ring
     54    sage: TestSuite(H).run()
     55    sage: loads(dumps(H)) is H
     56    True
    5057
    5158    sage: z = H.base_ring().gens()
    5259    sage: f = H.gens()
     
    7380    -30077*x + 9485*x*y - 6260*y^3 + 12870*x^2*y^2 - 20289*y^4 + O(x, y)^5
    7481    sage: s in S
    7582    True
     83    sage: TestSuite(S).run()
     84    sage: loads(dumps(S)) is S
     85    True
    7686
    7787- Use double square bracket notation::
    7888   
     
    8797
    8898    sage: T.<a,b> = PowerSeriesRing(ZZ,order='deglex'); T
    8999    Multivariate Power Series Ring in a, b over Integer Ring
     100    sage: TestSuite(T).run()
     101    sage: loads(dumps(T)) is T
     102    True
    90103    sage: T.term_order()
    91104    Degree lexicographic term order
    92105    sage: p = - 2*b^6 + a^5*b^2 + a^7 - b^2 - a*b^3 + T.O(9); p
     
    151164    sage: type(x)
    152165    <type 'sage.symbolic.expression.Expression'>
    153166    sage: type(S(x))
    154     <class 'sage.rings.multi_power_series_ring_element.MPowerSeries'>
     167    <class 'sage.rings.multi_power_series_ring_element.MPowerSeriesRing_generic_with_category.element_class'>
    155168
    156169    sage: f = S(2/7 -100*x^2 + 1/3*x*y + y^2).O(3); f
    157170    5 - x^2 + 4*x*y + y^2 + O(x, y)^3
     
    164177AUTHORS:
    165178
    166179- Niles Johnson (2010-07): initial code
     180- Simon King (2012-08): Use category and coercion framework, :trac:`13412`
    167181
    168182
    169183"""
     
    177191#*****************************************************************************
    178192
    179193
    180 from sage.rings.commutative_ring import is_CommutativeRing
     194from sage.rings.commutative_ring import is_CommutativeRing, CommutativeRing
    181195from sage.rings.polynomial.all import PolynomialRing, is_MPolynomialRing, is_PolynomialRing
    182196from sage.rings.polynomial.term_order import TermOrder
    183197from sage.rings.power_series_ring import PowerSeriesRing, PowerSeriesRing_generic, is_PowerSeriesRing
     
    186200import sage.misc.latex as latex
    187201from sage.structure.nonexact import Nonexact
    188202
    189 from sage.structure.parent_gens import ParentWithGens
    190 
    191203from sage.rings.multi_power_series_ring_element import MPowerSeries
    192204
    193205
     
    211223        False
    212224
    213225    """
    214     return type(x) == MPowerSeriesRing_generic
     226    return isinstance(x, MPowerSeriesRing_generic)
    215227
    216228
    217229class MPowerSeriesRing_generic(PowerSeriesRing_generic, Nonexact):
     
    231243    #
    232244    # variable_names_recursive : works just fine
    233245    #
    234     # __call__ : works just fine
    235     #
    236246    # __contains__ : works just fine
    237247    #
    238248    # base_extend : works just fine
     
    253263    #### notes
    254264    #
    255265    # sparse setting may not be implemented completely
    256     #
    257    
    258 
    259 
    260    
     266    Element = MPowerSeries
    261267    def __init__(self, base_ring, num_gens, name_list,
    262268                 order='negdeglex', default_prec=10, sparse=False):
    263269        """
     
    302308            raise ValueError("Multivariate Polynomial Rings must have more than 0 variables.")
    303309        self._ngens = n
    304310        self._has_singular = False #cannot convert to Singular by default
    305         ParentWithGens.__init__(self, base_ring, name_list)
     311        # Multivariate power series rings inherit from power series rings. But
     312        # apparently we can not call their initialisation. Instead, initialise
     313        # CommutativeRing and Nonexact:
     314        CommutativeRing.__init__(self, base_ring, name_list)
    306315        Nonexact.__init__(self, default_prec)
    307316
    308317        # underlying polynomial ring in which to represent elements
     
    314323        self._bg_power_series_ring = PowerSeriesRing(self._poly_ring_, 'Tbg', sparse=sparse, default_prec=default_prec)
    315324        self._bg_indeterminate = self._bg_power_series_ring.gen()
    316325
    317         ## use the following in PowerSeriesRing_generic.__call__
    318         self._PowerSeriesRing_generic__power_series_class = MPowerSeries
    319 
    320326        self._is_sparse = sparse
    321327        self._params = (base_ring, num_gens, name_list,
    322328                         order, default_prec, sparse)
    323329        self._populate_coercion_lists_()
    324330
    325     def __reduce__(self):
    326         """
    327         EXAMPLES::
    328        
    329             sage: R.<t,u,v> = PowerSeriesRing(ZZ)
    330             sage: S = loads(dumps(R)); S
    331             Multivariate Power Series Ring in t, u, v over Integer Ring
    332             sage: type(S)
    333             <class 'sage.rings.multi_power_series_ring.MPowerSeriesRing_generic'>
    334             sage: R.<x,y,z> = PowerSeriesRing(QQ, default_prec=10, sparse=True)
    335             sage: S = loads(dumps(R)); S
    336             Sparse Multivariate Power Series Ring in x, y, z over Rational Field
    337             sage: type(S)
    338             <class 'sage.rings.multi_power_series_ring.MPowerSeriesRing_generic'>
    339         """
    340         return unpickle_multi_power_series_ring_v0, self._params
    341 
    342 
    343331    def _repr_(self):
    344332        """
    345333        Prints out a multivariate power series ring.
     
    675663
    676664            - any ring that coerces into the foreground polynomial ring of this ring
    677665
     666        EXAMPLES::
     667
     668            sage: A = GF(17)[['x','y']]
     669            sage: A.has_coerce_map_from(ZZ)
     670            True
     671            sage: A.has_coerce_map_from(ZZ['x'])
     672            True
     673            sage: A.has_coerce_map_from(ZZ['y','x'])
     674            True
     675            sage: A.has_coerce_map_from(ZZ[['x']])
     676            True
     677            sage: A.has_coerce_map_from(ZZ[['y','x']])
     678            True
     679            sage: A.has_coerce_map_from(ZZ['x','z'])
     680            False
     681            sage: A.has_coerce_map_from(GF(3)['x','y'])
     682            False
     683            sage: A.has_coerce_map_from(Frac(ZZ['y','x']))
     684            False
    678685
    679686        TESTS::
    680687
     
    719726        return self._poly_ring().has_coerce_map_from(P)
    720727               
    721728
    722     def _element_constructor_(self,f):
     729    def _element_constructor_(self,f,prec=None):
    723730        """
    724731        TESTS::
    725732       
     
    740747            Multivariate Power Series Ring in t0, t1, t2, t3, t4 over
    741748            Integer Ring
    742749        """
    743         try:
    744             prec = f.prec()
    745         except AttributeError:
    746             prec = infinity
    747         return MPowerSeries(self, f, prec)
     750        if prec is None:
     751            try:
     752                prec = f.prec()
     753            except AttributeError:
     754                prec = infinity
     755        return self.element_class(parent=self, x=f, prec=prec)
    748756
    749757    def __cmp__(self, other):
    750758        """
     
    899907        if n < 0 or n >= self._ngens:
    900908            raise ValueError("Generator not defined.")
    901909        #return self(self._poly_ring().gens()[int(n)])
    902         return MPowerSeries(self,self._poly_ring().gens()[int(n)], is_gen=True)
     910        return self.element_class(parent=self,x=self._poly_ring().gens()[int(n)], is_gen=True)
    903911
    904912    def ngens(self):
    905913        """
  • sage/rings/multi_power_series_ring_element.py

    diff --git a/sage/rings/multi_power_series_ring_element.py b/sage/rings/multi_power_series_ring_element.py
    a b  
    150150AUTHORS:
    151151
    152152- Niles Johnson (07/2010): initial code
     153- Simon King (08/2012): Use category and coercion framework, :trac:`13412`
    153154
    154155"""
    155156
     
    195196
    196197        """
    197198
    198     return type(f) == MPowerSeries
     199    return isinstance(f, MPowerSeries)
    199200
    200201
    201202
     
    420421            sage: loads(dumps(f)) == f
    421422            True
    422423        """
    423 
    424         from sage.rings.power_series_ring_element import make_element_from_parent_v0
    425         return make_element_from_parent_v0, (self._parent,self._bg_value,self._prec)
     424        return self.__class__, (self._parent,self._bg_value,self._prec)
    426425
    427426    def __call__(self, *x, **kwds):
    428427        """
  • sage/rings/power_series_poly.pyx

    diff --git a/sage/rings/power_series_poly.pyx b/sage/rings/power_series_poly.pyx
    a b  
    8888            sage: f == loads(dumps(f)) # indirect doctest
    8989            True
    9090        """
    91         # do *not* delete old versions.
    92         return make_powerseries_poly_v0, (self._parent, self.__f, self._prec, self.__is_gen)
     91        return self.__class__, (self._parent, self.__f, self._prec, self.__is_gen)
    9392
    9493    def __richcmp__(left, right, int op):
    9594       """
  • sage/rings/power_series_ring.py

    diff --git a/sage/rings/power_series_ring.py b/sage/rings/power_series_ring.py
    a b  
    8989AUTHORS:
    9090
    9191- William Stein: the code
    92 
    9392- Jeremy Cho (2006-05-17): some examples (above)
    94 
    9593- Niles Johnson (2010-09): implement multivariate power series
     94- Simon King (2012-08): use category and coercion framework, :trac:`13412`
    9695
    9796TESTS::
    9897
    9998    sage: R.<t> = PowerSeriesRing(QQ)
    100     sage: R == loads(dumps(R))
     99    sage: R is loads(dumps(R))
    101100    True
     101    sage: TestSuite(R).run()
    102102
    103103::
    104104
    105105    sage: R.<x> = PowerSeriesRing(QQ, sparse=True)
    106     sage: R == loads(dumps(R))
     106    sage: R is loads(dumps(R))
    107107    True
     108    sage: TestSuite(R).run()
    108109
    109110::
    110111
    111112    sage: M = PowerSeriesRing(QQ, 't,u,v,w', default_prec=20)
    112     sage: M == loads(dumps(M))
     113    sage: M is loads(dumps(M))
    113114    True
     115    sage: TestSuite(M).run()
    114116   
    115117"""
    116118
     
    134136from sage.interfaces.magma import MagmaElement
    135137from sage.misc.sage_eval import sage_eval
    136138
    137 from sage.structure.parent_gens import ParentWithGens
    138 
    139 
    140 _cache = {}
     139from sage.structure.unique_representation import UniqueRepresentation
     140from sage.structure.parent import normalize_names
     141import sage.categories.commutative_rings as commutative_rings
    141142
    142143def PowerSeriesRing(base_ring, name=None, arg2=None, names=None,
    143144                    sparse=False, default_prec=None, order='negdeglex', num_gens=None):
     
    184185        sage: R = PowerSeriesRing(QQ, 10)
    185186        Traceback (most recent call last):
    186187        ...
    187         ValueError: first letter of variable name must be a letter
     188        ValueError: first letter of variable name must be a letter: 10
    188189   
    189190    ::
    190191   
     
    335336    if not names is None:
    336337        name = names
    337338    try:
    338         name = gens.normalize_names(1, name)
     339        name = normalize_names(1, name)
    339340    except TypeError:
    340341        raise TypeError, "illegal variable name"
    341342   
     
    343344        raise TypeError, "You must specify the name of the indeterminate of the Power series ring."
    344345   
    345346    key = (base_ring, name, default_prec, sparse)
    346     if _cache.has_key(key):
    347         R = _cache[key]()
    348         if not R is None:
    349             return R
     347    if PowerSeriesRing_generic.__classcall__.is_in_cache(key):
     348        return PowerSeriesRing_generic(*key)
    350349
    351350    if isinstance(name, (tuple, list)):
    352351        assert len(name) == 1
     
    355354    if not (name is None or isinstance(name, str)):
    356355        raise TypeError, "variable name must be a string or None"
    357356       
    358                  
    359357    if isinstance(base_ring, field.Field):
    360358        R = PowerSeriesRing_over_field(base_ring, name, default_prec, sparse=sparse)
    361359    elif isinstance(base_ring, integral_domain.IntegralDomain):
     
    364362        R = PowerSeriesRing_generic(base_ring, name, default_prec, sparse=sparse)
    365363    else:
    366364        raise TypeError, "base_ring must be a commutative ring"
    367     _cache[key] = weakref.ref(R)
    368365    return R
    369366
    370367def _multi_variate(base_ring, num_gens=None, names=None,
     
    385382            num_gens = len(names)
    386383        else:
    387384            raise TypeError("variable names must be a string, tuple or list")
    388            
     385    names = normalize_names(num_gens, names)
     386    num_gens = len(names)
    389387    if default_prec is None:
    390388        default_prec = 12
    391389
    392 
    393     key = (base_ring, num_gens, str(names), order, default_prec, sparse)
    394     if _cache.has_key(key):
    395         R = _cache[key]()
    396         if not R is None:
    397             return R       
    398                  
    399     if isinstance(base_ring, field.Field):
    400         pass
    401     elif isinstance(base_ring, integral_domain.IntegralDomain):
    402         pass
    403     elif isinstance(base_ring, commutative_ring.CommutativeRing):
    404         pass
    405     else:
     390    if base_ring not in commutative_rings.CommutativeRings():
    406391        raise TypeError, "base_ring must be a commutative ring"
    407392    from sage.rings.multi_power_series_ring import MPowerSeriesRing_generic
    408393    R = MPowerSeriesRing_generic(base_ring, num_gens, names,
    409394                                 order=order, default_prec=default_prec, sparse=sparse)
    410     _cache[key] = weakref.ref(R)
    411395    return R
    412396
    413397
     
    433417    else:
    434418        return False
    435419
    436 class PowerSeriesRing_generic(commutative_ring.CommutativeRing, Nonexact):
     420class PowerSeriesRing_generic(UniqueRepresentation, commutative_ring.CommutativeRing, Nonexact):
    437421    """
    438422    A power series ring.
    439423
     
    448432        sage: TestSuite(R).run()
    449433
    450434    """
     435    Element = power_series_poly.PowerSeries_poly
    451436    def __init__(self, base_ring, name=None, default_prec=20, sparse=False,
    452437                 use_lazy_mpoly_ring=False):
    453438        """
     
    473458          for it to truncate away higher order terms (this is called
    474459          automatically before printing).
    475460        """
    476         commutative_ring.CommutativeRing.__init__(self, base_ring, names=name)
    477         Nonexact.__init__(self, default_prec)
    478461        R = PolynomialRing(base_ring, name, sparse=sparse)
    479462        self.__poly_ring = R
    480         self.__power_series_class = power_series_poly.PowerSeries_poly
    481         self.__generator = self.__power_series_class(self, R.gen(), check=True, is_gen=True)
    482463        self.__is_sparse = sparse
    483464        self.__params = (base_ring, name, default_prec, sparse)
    484465
     
    488469            names = K.variable_names() + (name,)
    489470            self.__mpoly_ring = PolynomialRing(K.base_ring(), names=names)
    490471            assert is_MPolynomialRing(self.__mpoly_ring)
    491             self.__power_series_class = power_series_mpoly.PowerSeries_mpoly
     472            self.Element = power_series_mpoly.PowerSeries_mpoly
     473        commutative_ring.CommutativeRing.__init__(self, base_ring, names=name)
     474        Nonexact.__init__(self, default_prec)
     475        self.__generator = self.element_class(self, R.gen(), check=True, is_gen=True)
    492476
    493477    def variable_names_recursive(self, depth=None):
    494478        r"""
     
    520504            all = all[-depth:]
    521505        return all
    522506
    523     def __reduce__(self):
    524         """
    525         TESTS::
    526        
    527             sage: R.<t> = PowerSeriesRing(ZZ)
    528             sage: S = loads(dumps(R)); S
    529             Power Series Ring in t over Integer Ring
    530             sage: type(S)
    531             <class 'sage.rings.power_series_ring.PowerSeriesRing_domain_with_category'>
    532             sage: R.<t> = PowerSeriesRing(QQ, default_prec=10, sparse=True); R
    533             Sparse Power Series Ring in t over Rational Field
    534             sage: S = loads(dumps(R)); S
    535             Sparse Power Series Ring in t over Rational Field
    536             sage: type(S)
    537             <class 'sage.rings.power_series_ring.PowerSeriesRing_over_field_with_category'>
    538         """
    539         return unpickle_power_series_ring_v0, self.__params
    540 
    541507    def _repr_(self):
    542508        """
    543509        Prints out a power series ring.
     
    571537        EXAMPLES::
    572538       
    573539            sage: R = GF(17)[['y']]
    574             sage: latex(R)
     540            sage: latex(R)  # indirect doctest
    575541            \Bold{F}_{17}[[y]]
    576542            sage: R = GF(17)[['y12']]
    577543            sage: latex(R)
     
    579545        """
    580546        return "%s[[%s]]"%(latex.latex(self.base_ring()), self.latex_variable_names()[0])
    581547
    582     def __call__(self, f, prec=infinity, check=True):
     548    def _coerce_map_from_(self, S):
     549        """
     550        A coercion from `S` exists, if `S` coerces into ``self``'s base ring,
     551        or if `S` is a univariate polynomial or power series ring with the
     552        same variable name as self, defined over a base ring that coerces into
     553        ``self``'s base ring.
     554
     555        EXAMPLES::
     556
     557            sage: A = GF(17)[['x']]
     558            sage: A.has_coerce_map_from(ZZ)  # indirect doctest
     559            True
     560            sage: A.has_coerce_map_from(ZZ['x'])
     561            True
     562            sage: A.has_coerce_map_from(ZZ['y'])
     563            False
     564            sage: A.has_coerce_map_from(ZZ[['x']])
     565            True
     566
     567        """
     568        if self.base_ring().has_coerce_map_from(S):
     569            return True
     570        if (is_PolynomialRing(S) or is_PowerSeriesRing(S)) and self.base_ring().has_coerce_map_from(S.base_ring()) \
     571           and self.variable_names()==S.variable_names():
     572            return True
     573
     574    def _element_constructor_(self, f, prec=infinity, check=True):
    583575        """
    584576        Coerce object to this power series ring.
    585577       
     
    601593        EXAMPLES::
    602594       
    603595            sage: R.<t> = PowerSeriesRing(ZZ)
    604             sage: R(t+O(t^5))
     596            sage: R(t+O(t^5))    # indirect doctest
    605597            t + O(t^5)
    606598            sage: R(13)
    607599            13
     
    625617        elif isinstance(f, MagmaElement) and str(f.Type()) == 'RngSerPowElt':
    626618            v = sage_eval(f.Eltseq())
    627619            return self(v) * (self.gen(0)**f.Valuation())
    628         return self.__power_series_class(self, f, prec, check=check)
     620        return self.element_class(self, f, prec, check=check)
    629621       
    630622    def construction(self):
    631623        """
     
    663655        EXAMPLES::
    664656       
    665657            sage: R.<t> = PowerSeriesRing(ZZ)
    666             sage: R._coerce_(t + t^2)
     658            sage: R._coerce_(t + t^2)  # indirect doctest
    667659            t + t^2
    668660            sage: R._coerce_(1/t)
    669661            Traceback (most recent call last):
    670662            ...
    671             TypeError: no canonical coercion of element into self
     663            TypeError: no canonical coercion from Laurent Series Ring in t over Integer Ring to Power Series Ring in t over Integer Ring
    672664            sage: R._coerce_(5)
    673665            5
    674666            sage: tt = PolynomialRing(ZZ,'t').gen()
     
    677669            sage: R._coerce_(1/2)
    678670            Traceback (most recent call last):
    679671            ...
    680             TypeError: no canonical coercion of element into self
     672            TypeError: no canonical coercion from Rational Field to Power Series Ring in t over Integer Ring
    681673            sage: S.<s> = PowerSeriesRing(ZZ)
    682674            sage: R._coerce_(s)
    683675            Traceback (most recent call last):
    684676            ...
    685             TypeError: no canonical coercion of element into self           
     677            TypeError: no canonical coercion from Power Series Ring in s over Integer Ring to Power Series Ring in t over Integer Ring
    686678       
    687679        We illustrate canonical coercion between power series rings with
    688680        compatible base rings::
     
    698690            sage: S._coerce_(g)
    699691            Traceback (most recent call last):
    700692            ...
    701             TypeError: no natural map between bases of power series rings
     693            TypeError: no canonical coercion from Power Series Ring in t over Univariate Polynomial Ring in w over Finite Field of size 7 to Power Series Ring in t over Integer Ring
    702694        """
    703695        try:
    704696            P = x.parent()
  • sage/rings/power_series_ring_element.pyx

    diff --git a/sage/rings/power_series_ring_element.pyx b/sage/rings/power_series_ring_element.pyx
    a b  
    77AUTHORS:
    88
    99- William Stein
    10 
    1110- David Harvey (2006-09-11): added solve_linear_de() method
    12 
    1311- Robert Bradshaw (2007-04): sqrt, rmul, lmul, shifting
    14 
    1512- Robert Bradshaw (2007-04): Cython version
     13- Simon King (2012-08): use category and coercion framework, :trac:`13412`
    1614
    1715EXAMPLE::
    1816
    1917    sage: R.<x> = PowerSeriesRing(ZZ)
     18    sage: TestSuite(R).run()
    2019    sage: R([1,2,3])
    2120    1 + 2*x + 3*x^2
    2221    sage: R([1,2,3], 10)
     
    18811880   
    18821881
    18831882def make_powerseries_poly_v0(parent,  f, prec, is_gen):
     1883    # This is only used to unpickle old pickles. The new pickling
     1884    # works differently!
    18841885    import power_series_poly
    18851886    return power_series_poly.PowerSeries_poly(parent, f, prec, 0, is_gen)
    18861887
    18871888def make_element_from_parent_v0(parent, *args):
     1889    # This is only used to unpickle old pickles. The new pickling
     1890    # works differently!
    18881891    return parent(*args)