Ticket #9945: 9945-part-frac-FpT.patch

File 9945-part-frac-FpT.patch, 24.4 KB (added by robertwb, 10 years ago)
  • sage/categories/all.py

    # HG changeset patch
    # User Robert Bradshaw <robertwb@math.washington.edu>
    # Date 1284855068 25200
    # Node ID 8f1f120b315c730ada5e8af70c26645306458e54
    # Parent  eec399f88e2219466677cb7502950eaf982a102c
    #9945 - partial_fraction_decomposition broken for FpT elements
    
    Moved these methods into the category, fixed category of QQ as well.
    
    diff -r eec399f88e22 -r 8f1f120b315c sage/categories/all.py
    a b  
    4040from finite_permutation_groups import FinitePermutationGroups
    4141
    4242# fields
    43 from quotient_fields import QuotientFields
    44 from finite_fields import FiniteFields
    4543from number_fields import NumberFields
    4644
    4745# modules
  • sage/categories/basic.py

    diff -r eec399f88e22 -r 8f1f120b315c sage/categories/basic.py
    a b  
    4545from unique_factorization_domains import UniqueFactorizationDomains
    4646
    4747from fields import Fields
     48from quotient_fields import QuotientFields
     49from finite_fields import FiniteFields
  • sage/categories/finite_fields.py

    diff -r eec399f88e22 -r 8f1f120b315c sage/categories/finite_fields.py
    a b  
    1313
    1414from sage.misc.cachefunc import cached_method
    1515from sage.categories.category import Category
    16 from sage.categories.all import Fields
    1716from sage.rings.field import is_Field
    1817
    1918class FiniteFields(Category):
     
    4746            sage: FiniteFields().super_categories()
    4847            [Category of fields]
    4948        """
     49        from sage.categories.fields import Fields
    5050        return [Fields()]
    5151
    5252    def __contains__(self, x):
  • sage/categories/quotient_fields.py

    diff -r eec399f88e22 -r 8f1f120b315c sage/categories/quotient_fields.py
    a b  
    99#******************************************************************************
    1010
    1111from sage.categories.category import Category
    12 from sage.categories.all import Fields
    1312from sage.misc.cachefunc import cached_method
     13from sage.misc.abstract_method import abstract_method
    1414
    1515class QuotientFields(Category):
    1616    """
     
    3636            sage: QuotientFields().super_categories()
    3737            [Category of fields]
    3838        """
     39        from sage.categories.fields import Fields
    3940        return [Fields()]
    4041
    4142    class ParentMethods:
    4243        pass
    4344
    4445    class ElementMethods:
    45         pass
     46       
     47        @abstract_method
     48        def numerator(self):
     49            pass
     50       
     51        @abstract_method
     52        def denominator(self):
     53            pass
     54
     55        def factor(self, *args, **kwds):
     56            """
     57            Return the factorization of ``self`` over the base ring.
     58           
     59            INPUT:
     60           
     61            - ``*args`` - Arbitrary arguments suitable over the base ring
     62            - ``**kwds`` - Arbitrary keyword arguments suitable over the base ring
     63           
     64            OUTPUT:
     65           
     66            - Factorization of ``self`` over the base ring
     67           
     68            EXAMPLES::
     69           
     70                sage: K.<x> = QQ[]
     71                sage: f = (x^3+x)/(x-3)
     72                sage: f.factor()
     73                (x - 3)^-1 * x * (x^2 + 1)
     74           
     75            Here is an example to show that ticket #7868 has been resolved::
     76           
     77                sage: R.<x,y> = GF(2)[]
     78                sage: f = x*y/(x+y)
     79                sage: f.factor()
     80                Traceback (most recent call last):
     81                ...
     82                NotImplementedError: proof = True factorization not implemented.  Call factor with proof=False.
     83                sage: f.factor(proof=False)
     84                (x + y)^-1 * y * x
     85            """
     86            return (self.numerator().factor(*args, **kwds) /
     87                    self.denominator().factor(*args, **kwds))
     88
     89        def partial_fraction_decomposition(self):
     90            """
     91            Decomposes fraction field element into a whole part and a list of
     92            fraction field elements over prime power denominators.
     93           
     94            The sum will be equal to the original fraction.
     95           
     96            AUTHORS:
     97           
     98            - Robert Bradshaw (2007-05-31)
     99           
     100            EXAMPLES::
     101           
     102                sage: S.<t> = QQ[]
     103                sage: q = 1/(t+1) + 2/(t+2) + 3/(t-3); q
     104                (6*t^2 + 4*t - 6)/(t^3 - 7*t - 6)
     105                sage: whole, parts = q.partial_fraction_decomposition(); parts
     106                [3/(t - 3), 1/(t + 1), 2/(t + 2)]
     107                sage: sum(parts) == q
     108                True
     109                sage: q = 1/(t^3+1) + 2/(t^2+2) + 3/(t-3)^5
     110                sage: whole, parts = q.partial_fraction_decomposition(); parts
     111                [1/3/(t + 1), 3/(t^5 - 15*t^4 + 90*t^3 - 270*t^2 + 405*t - 243), (-1/3*t + 2/3)/(t^2 - t + 1), 2/(t^2 + 2)]
     112                sage: sum(parts) == q
     113                True
     114           
     115            We do the best we can over in-exact fields::
     116           
     117                sage: R.<x> = RealField(20)[]
     118                sage: q = 1/(x^2 + 2)^2 + 1/(x-1); q
     119                (x^4 + 4.0000*x^2 + x + 3.0000)/(x^5 - x^4 + 4.0000*x^3 - 4.0000*x^2 + 4.0000*x - 4.0000)
     120                sage: whole, parts = q.partial_fraction_decomposition(); parts
     121                [1.0000/(x - 1.0000), 1.0000/(x^4 + 4.0000*x^2 + 4.0000)]
     122                sage: sum(parts)
     123                (x^4 + 4.0000*x^2 + x + 3.0000)/(x^5 - x^4 + 4.0000*x^3 - 4.0000*x^2 + 4.0000*x - 4.0000)
     124
     125            TESTS:
     126
     127            We test partial fraction for irreducible denominators::
     128           
     129                sage: R.<x> = ZZ[]
     130                sage: q = x^2/(x-1)
     131                sage: q.partial_fraction_decomposition()
     132                (x + 1, [1/(x - 1)])
     133                sage: q = x^10/(x-1)^5
     134                sage: whole, parts = q.partial_fraction_decomposition()
     135                sage: whole + sum(parts) == q
     136                True
     137
     138            And also over finite fields (see trac #6052, #9945)::
     139           
     140                sage: R.<x> = GF(2)[]
     141                sage: q = (x+1)/(x^3+x+1)
     142                sage: q.partial_fraction_decomposition()
     143                (0, [(x + 1)/(x^3 + x + 1)])
     144
     145                sage: R.<x> = GF(11)[]
     146                sage: q = x + 1 + 1/(x+1) + x^2/(x^3 + 2*x + 9)
     147                sage: q.partial_fraction_decomposition()
     148                (x + 1, [1/(x + 1), x^2/(x^3 + 2*x + 9)])
     149           
     150            And even the rationals::
     151           
     152                sage: (26/15).partial_fraction_decomposition()
     153                (1, [1/3, 2/5])
     154            """
     155            from sage.misc.misc import prod
     156            denom = self.denominator()
     157            whole, numer = self.numerator().quo_rem(denom)
     158            factors = denom.factor()
     159            if factors.unit() != 1:
     160                numer *= ~factors.unit()
     161            if len(factors) == 1:
     162                return whole, [numer/r**e for r,e in factors]
     163            if not self.parent().is_exact():
     164                # factors not grouped in this case
     165                # TODO: think about changing the factor code itself
     166                # (what side effects would this have this be bad?)
     167                all = {}
     168                for r in factors: all[r[0]] = 0
     169                for r in factors: all[r[0]] += r[1]
     170                factors = all.items()
     171                factors.sort() # for doctest consistency
     172            factors = [r**e for r,e in factors]
     173            parts = []
     174            for d in factors:
     175                # note that the product below is non-empty, since the case
     176                # of only one factor has been dealt with above
     177                n = numer * prod([r for r in factors if r != d]).inverse_mod(d) % d # we know the inverse exists as the two are relatively prime
     178                parts.append(n/d)
     179            return whole, parts
     180
     181        def derivative(self, *args):
     182            r"""
     183            The derivative of this rational function, with respect to variables
     184            supplied in args.
     185           
     186            Multiple variables and iteration counts may be supplied; see
     187            documentation for the global derivative() function for more
     188            details.
     189           
     190            .. seealso::
     191
     192               :meth:`_derivative`
     193           
     194            EXAMPLES::
     195           
     196                sage: F.<x> = Frac(QQ['x'])
     197                sage: (1/x).derivative()
     198                -1/x^2
     199           
     200            ::
     201           
     202                sage: (x+1/x).derivative(x, 2)
     203                2/x^3
     204           
     205            ::
     206           
     207                sage: F.<x,y> = Frac(QQ['x,y'])
     208                sage: (1/(x+y)).derivative(x,y)
     209                2/(x^3 + 3*x^2*y + 3*x*y^2 + y^3)
     210            """
     211            from sage.misc.derivative import multi_derivative
     212            return multi_derivative(self, args)
     213
     214        def _derivative(self, var=None):
     215            r"""
     216            Returns the derivative of this rational function with respect to the
     217            variable ``var``.
     218           
     219            Over an ring with a working GCD implementation, the derivative of a
     220            fraction `f/g`, supposed to be given in lowest terms, is computed as
     221            `(f'(g/d) - f(g'/d))/(g(g'/d))`, where `d` is a greatest common
     222            divisor of `f` and `g`.
     223           
     224            INPUT:
     225           
     226            - ``var`` - Variable with respect to which the derivative is computed
     227           
     228            OUTPUT:
     229           
     230            - Derivative of ``self`` with respect to ``var``
     231           
     232            .. seealso::
     233           
     234               :meth:`derivative`
     235           
     236            EXAMPLES::
     237           
     238                sage: F.<x> = Frac(QQ['x'])
     239                sage: t = 1/x^2
     240                sage: t._derivative(x)
     241                -2/x^3
     242                sage: t.derivative()
     243                -2/x^3
     244           
     245            ::
     246           
     247                sage: F.<x,y> = Frac(QQ['x,y'])
     248                sage: t = (x*y/(x+y))
     249                sage: t._derivative(x)
     250                y^2/(x^2 + 2*x*y + y^2)
     251                sage: t._derivative(y)
     252                x^2/(x^2 + 2*x*y + y^2)
     253           
     254            TESTS::
     255           
     256                sage: F.<t> = Frac(ZZ['t'])
     257                sage: F(0).derivative()
     258                0
     259                sage: F(2).derivative()
     260                0
     261                sage: t.derivative()
     262                1
     263                sage: (1+t^2).derivative()
     264                2*t
     265                sage: (1/t).derivative()
     266                -1/t^2
     267                sage: ((t+2)/(t-1)).derivative()
     268                -3/(t^2 - 2*t + 1)
     269                sage: (t/(1+2*t+t^2)).derivative()
     270                (-t + 1)/(t^3 + 3*t^2 + 3*t + 1)
     271            """
     272            R = self.parent()
     273            if var in R.gens():
     274                var = R.ring()(var)
     275           
     276            num = self.numerator()
     277            den = self.denominator()
     278           
     279            if (num.is_zero()):
     280                return R.zero_element()
     281           
     282            if R.is_exact():
     283                try:
     284                    numder = num._derivative(var)
     285                    dender = den._derivative(var)
     286                    d      = den.gcd(dender)
     287                    den    = den // d
     288                    dender = dender // d
     289                    tnum   = numder * den - num * dender
     290                    tden   = self.denominator() * den
     291                    if not tden.is_one() and tden.is_unit():
     292                        try:
     293                            tnum = tnum * tden.inverse_of_unit()
     294                            tden = R.ring().one_element()
     295                        except AttributeError:
     296                            pass
     297                        except NotImplementedError:
     298                            pass
     299                    return self.__class__(R, tnum, tden,
     300                        coerce=False, reduce=False)
     301                except AttributeError:
     302                    pass
     303                except NotImplementedError:
     304                    pass
     305                except TypeError:
     306                    pass
     307                num = self.numerator()
     308                den = self.denominator()
     309           
     310            num = num._derivative(var) * den - num * den._derivative(var)
     311            den = den**2
     312           
     313            return self.__class__(R, num, den,
     314                coerce=False, reduce=False)
     315
  • sage/rings/fraction_field.py

    diff -r eec399f88e22 -r 8f1f120b315c sage/rings/fraction_field.py
    a b  
    8282
    8383from sage.structure.parent import Parent
    8484from sage.structure.coerce_maps import CallableConvertMap
     85from sage.categories.basic import QuotientFields
    8586
    8687def FractionField(R, names=None):
    8788    """
     
    155156    The fraction field of an integral domain.
    156157    """
    157158    def __init__(self, R,
    158             element_class=fraction_field_element.FractionFieldElement):
     159            element_class=fraction_field_element.FractionFieldElement,
     160            category=QuotientFields()):
    159161        """
    160162        Create the fraction field of the integral domain R.
    161163       
     
    171173            Fraction Field of Univariate Polynomial Ring in x over Rational Field
    172174            sage: Frac(QQ['x,y']).variable_names()
    173175            ('x', 'y')
     176            sage: category(Frac(QQ['x']))
     177            Category of quotient fields
    174178        """
    175179        self._R = R
    176180        self._element_class = element_class
    177181        self._element_init_pass_parent = False
    178         Parent.__init__(self, base=R, names=R._names)
     182        Parent.__init__(self, base=R, names=R._names, category=category)
    179183
    180184    def __reduce__(self):
    181185        """
  • sage/rings/fraction_field_element.pyx

    diff -r eec399f88e22 -r 8f1f120b315c sage/rings/fraction_field_element.pyx
    a b  
    4545from rational_field import QQ
    4646
    4747import sage.misc.latex as latex
    48 from sage.misc.misc import prod
    49 from sage.misc.derivative import multi_derivative
    5048
    5149def is_FractionFieldElement(x):
    5250    """
     
    232230        """
    233231        return self.__denominator
    234232
    235     def factor(self, *args, **kwds):
    236         """
    237         Return the factorization of ``self`` over the base ring.
    238        
    239         INPUT:
    240        
    241         - ``*args`` - Arbitrary arguments suitable over the base ring
    242         - ``**kwds`` - Arbitrary keyword arguments suitable over the base ring
    243        
    244         OUTPUT:
    245        
    246         - Factorization of ``self`` over the base ring
    247        
    248         EXAMPLES::
    249        
    250             sage: K.<x> = QQ[]
    251             sage: f = (x^3+x)/(x-3)
    252             sage: f.factor()
    253             (x - 3)^-1 * x * (x^2 + 1)
    254        
    255         Here is an example to show that ticket #7868 has been resolved::
    256        
    257             sage: R.<x,y> = GF(2)[]
    258             sage: f = x*y/(x+y)
    259             sage: f.factor()
    260             Traceback (most recent call last):
    261             ...
    262             NotImplementedError: proof = True factorization not implemented.  Call factor with proof=False.
    263             sage: f.factor(proof=False)
    264             (x + y)^-1 * y * x
    265         """
    266         return self.numerator().factor(*args, **kwds) / \
    267             self.denominator().factor(*args, **kwds)
    268 
    269233    def __hash__(self):
    270234        """
    271235        This function hashes in a special way to ensure that generators of
     
    319283            return -2
    320284        return n
    321285
    322     def partial_fraction_decomposition(self):
    323         """
    324         Decomposes fraction field element into a whole part and a list of
    325         fraction field elements over prime power denominators.
    326        
    327         The sum will be equal to the original fraction.
    328        
    329         AUTHORS:
    330        
    331         - Robert Bradshaw (2007-05-31)
    332        
    333         EXAMPLES::
    334        
    335             sage: S.<t> = QQ[]
    336             sage: q = 1/(t+1) + 2/(t+2) + 3/(t-3); q
    337             (6*t^2 + 4*t - 6)/(t^3 - 7*t - 6)
    338             sage: whole, parts = q.partial_fraction_decomposition(); parts
    339             [3/(t - 3), 1/(t + 1), 2/(t + 2)]
    340             sage: sum(parts) == q
    341             True
    342             sage: q = 1/(t^3+1) + 2/(t^2+2) + 3/(t-3)^5
    343             sage: whole, parts = q.partial_fraction_decomposition(); parts
    344             [1/3/(t + 1), 3/(t^5 - 15*t^4 + 90*t^3 - 270*t^2 + 405*t - 243), (-1/3*t + 2/3)/(t^2 - t + 1), 2/(t^2 + 2)]
    345             sage: sum(parts) == q
    346             True
    347        
    348         We do the best we can over in-exact fields::
    349        
    350             sage: R.<x> = RealField(20)[]
    351             sage: q = 1/(x^2 + 2)^2 + 1/(x-1); q
    352             (x^4 + 4.0000*x^2 + x + 3.0000)/(x^5 - x^4 + 4.0000*x^3 - 4.0000*x^2 + 4.0000*x - 4.0000)
    353             sage: whole, parts = q.partial_fraction_decomposition(); parts
    354             [1.0000/(x - 1.0000), 1.0000/(x^4 + 4.0000*x^2 + 4.0000)]
    355             sage: sum(parts)
    356             (x^4 + 4.0000*x^2 + x + 3.0000)/(x^5 - x^4 + 4.0000*x^3 - 4.0000*x^2 + 4.0000*x - 4.0000)
    357 
    358         TESTS:
    359 
    360         We test partial fraction for irreducible denominators::
    361        
    362             sage: R.<x> = ZZ[]
    363             sage: q = x^2/(x-1)
    364             sage: q.partial_fraction_decomposition()
    365             (x + 1, [1/(x - 1)])
    366             sage: q = x^10/(x-1)^5
    367             sage: whole, parts = q.partial_fraction_decomposition()
    368             sage: whole + sum(parts) == q
    369             True
    370 
    371         And also over finite fields (see trac #6052)::
    372        
    373             sage: R.<x> = GF(2)[]
    374             sage: q = (x+1)/(x^3+x+1)
    375             sage: q.partial_fraction_decomposition()
    376             (0, [(x + 1)/(x^3 + x + 1)])
    377         """
    378         denom = self.denominator()
    379         whole, numer = self.numerator().quo_rem(denom)
    380         factors = denom.factor()
    381         if factors.unit() != 1:
    382             numer *= ~factors.unit()
    383         if len(factors) == 1:
    384             return whole, [numer/r**e for r,e in factors]
    385         if not self._parent.is_exact():
    386             # factors not grouped in this case
    387             # TODO: think about changing the factor code itself
    388             # (what side effects would this have this be bad?)
    389             all = {}
    390             for r in factors: all[r[0]] = 0
    391             for r in factors: all[r[0]] += r[1]
    392             factors = all.items()
    393             factors.sort() # for doctest consistency
    394         factors = [r**e for r,e in factors]
    395         parts = []
    396         for d in factors:
    397             # note that the product below is non-empty, since the case
    398             # of only one factor has been dealt with above
    399             n = numer * prod([r for r in factors if r != d]).inverse_mod(d) % d # we know the inverse exists as the two are relatively prime
    400             parts.append(n/d)
    401         return whole, parts
    402 
    403286    def __call__(self, *x, **kwds):
    404287        """
    405288        Evaluate the fraction at the given arguments. This assumes that a
     
    718601       
    719602        return self._mul_(rightinv)
    720603
    721     def derivative(self, *args):
    722         r"""
    723         The derivative of this rational function, with respect to variables
    724         supplied in args.
    725        
    726         Multiple variables and iteration counts may be supplied; see
    727         documentation for the global derivative() function for more
    728         details.
    729        
    730         .. seealso::
    731 
    732            :meth:`_derivative`
    733        
    734         EXAMPLES::
    735        
    736             sage: F = FractionField(PolynomialRing(RationalField(),'x'))
    737             sage: x = F.gen()
    738             sage: (1/x).derivative()
    739             -1/x^2
    740        
    741         ::
    742        
    743             sage: (x+1/x).derivative(x, 2)
    744             2/x^3
    745        
    746         ::
    747        
    748             sage: F = FractionField(PolynomialRing(RationalField(),'x,y'))
    749             sage: x,y = F.gens()
    750             sage: (1/(x+y)).derivative(x,y)
    751             2/(x^3 + 3*x^2*y + 3*x*y^2 + y^3)
    752         """
    753         return multi_derivative(self, args)
    754 
    755     def _derivative(self, var=None):
    756         r"""
    757         Returns the derivative of this rational function with respect to the
    758         variable ``var``.
    759        
    760         Over an ring with a working GCD implementation, the derivative of a
    761         fraction `f/g`, supposed to be given in lowest terms, is computed as
    762         `(f'(g/d) - f(g'/d))/(g(g'/d))`, where `d` is a greatest common
    763         divisor of `f` and `g`.
    764        
    765         INPUT:
    766        
    767         - ``var`` - Variable with respect to which the derivative is computed
    768        
    769         OUTPUT:
    770        
    771         - Derivative of ``self`` with respect to ``var``
    772        
    773         .. seealso::
    774        
    775            :meth:`derivative`
    776        
    777         EXAMPLES::
    778        
    779             sage: F = FractionField(PolynomialRing(RationalField(),'x'))
    780             sage: x = F.gen()
    781             sage: t = 1/x^2
    782             sage: t._derivative(x)
    783             -2/x^3
    784             sage: t.derivative()
    785             -2/x^3
    786        
    787         ::
    788        
    789             sage: F = FractionField(PolynomialRing(RationalField(),'x,y'))
    790             sage: x,y = F.gens()
    791             sage: t = (x*y/(x+y))
    792             sage: t._derivative(x)
    793             y^2/(x^2 + 2*x*y + y^2)
    794             sage: t._derivative(y)
    795             x^2/(x^2 + 2*x*y + y^2)
    796        
    797         TESTS::
    798        
    799             sage: F = Frac(PolynomialRing(ZZ, 't'))
    800             sage: t = F.gen()
    801             sage: F(0).derivative()
    802             0
    803             sage: F(2).derivative()
    804             0
    805             sage: t.derivative()
    806             1
    807             sage: (1+t^2).derivative()
    808             2*t
    809             sage: (1/t).derivative()
    810             -1/t^2
    811             sage: ((t+2)/(t-1)).derivative()
    812             -3/(t^2 - 2*t + 1)
    813             sage: (t/(1+2*t+t^2)).derivative()
    814             (-t + 1)/(t^3 + 3*t^2 + 3*t + 1)
    815         """
    816         if var in self._parent.gens():
    817             var = self._parent.ring()(var)
    818        
    819         num = self.__numerator
    820         den = self.__denominator
    821        
    822         if (num.is_zero()):
    823             return self._parent.zero_element()
    824        
    825         if self._parent.is_exact():
    826             try:
    827                 numder = num._derivative(var)
    828                 dender = den._derivative(var)
    829                 d      = den.gcd(dender)
    830                 den    = den // d
    831                 dender = dender // d
    832                 tnum   = numder * den - num * dender
    833                 tden   = self.__denominator * den
    834                 if not tden.is_one() and tden.is_unit():
    835                     try:
    836                         tnum = tnum * tden.inverse_of_unit()
    837                         tden = self._parent.ring().one_element()
    838                     except AttributeError:
    839                         pass
    840                     except NotImplementedError:
    841                         pass
    842                 return self.__class__(self._parent, tnum, tden,
    843                     coerce=False, reduce=False)
    844             except AttributeError:
    845                 pass
    846             except NotImplementedError:
    847                 pass
    848             except TypeError:
    849                 pass
    850        
    851         num = self.__numerator
    852         den = self.__denominator
    853         num = num._derivative(var) * den - num * den._derivative(var)
    854         den = den**2
    855        
    856         return self.__class__(self._parent, num, den,
    857             coerce=False, reduce=False)
    858 
    859604    def __int__(self):
    860605        """
    861606        EXAMPLES::
  • sage/rings/rational_field.py

    diff -r eec399f88e22 -r 8f1f120b315c sage/rings/rational_field.py
    a b  
    147147            sage: Q.is_field()
    148148            True
    149149            sage: Q.category()
    150             Category of fields
     150            Category of quotient fields
    151151            sage: Q.zeta()
    152152            -1
    153153       
     
    207207            sage: QQ.variable_names()
    208208            ('x',)
    209209        """
    210         from sage.categories.fields import Fields
    211         ParentWithGens.__init__(self, self, category = Fields())
     210        from sage.categories.basic import QuotientFields
     211        ParentWithGens.__init__(self, self, category = QuotientFields())
    212212        self._assign_names(('x',),normalize=False) # ???
    213213        self._populate_coercion_lists_(element_constructor=rational.Rational, init_no_parent=True)
    214214