source:sage/rings/fraction_field_element.py@8073:c320674b52c4

Revision 8073:c320674b52c4, 16.3 KB checked in by Martin Albrecht <malb@…>, 5 years ago (diff)

two small LaTeX fixes

Line
1"""
2Fraction Field Elements
3
4AUTHOR: William Stein (input from David Joyner, David Kohel, and Joe Wetherell)
5"""
6
7#*****************************************************************************
8#
9#   SAGE: System for Algebra and Geometry Experimentation
10#
11#       Copyright (C) 2005 William Stein <wstein@gmail.com>
12#
14#
15#    This code is distributed in the hope that it will be useful,
16#    but WITHOUT ANY WARRANTY; without even the implied warranty of
17#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18#    General Public License for more details.
19#
20#  The full text of the GPL is available at:
21#
23#*****************************************************************************
24
25import operator
26
27import sage.rings.field_element as field_element
28import fraction_field
29import integer_ring
30
31import sage.misc.latex as latex
32from sage.misc.misc import prod
33
34def is_FractionFieldElement(x):
35    return isinstance(x, FractionFieldElement)
36
37class FractionFieldElement(field_element.FieldElement):
38    """
39    EXAMPLES:
40        sage: K, x = FractionField(PolynomialRing(QQ, 'x')).objgen()
41        sage: K
42        Fraction Field of Univariate Polynomial Ring in x over Rational Field
44        True
45        sage: f = (x^3 + x)/(17 - x^19); f
46        (x^3 + x)/(-x^19 + 17)
48        True
49    """
50
51    def __init__(self, parent, numerator, denominator=1,
52                 coerce=True, reduce=True):
53        field_element.FieldElement.__init__(self, parent)
54        if coerce:
55            self.__numerator = parent.ring()(numerator)
56            self.__denominator = parent.ring()(denominator)
57        else:
58            self.__numerator = numerator
59            self.__denominator = denominator
60        if reduce and parent.is_exact():
61            try:
62                self.reduce()
63            except ArithmeticError:
64                pass
65        if self.__denominator.is_zero():
66            raise ZeroDivisionError, "fraction field element division by zero"
67
68    def reduce(self):
69        try:
70            g = self.__numerator.gcd(self.__denominator)
71            if g != 1:
72                numer, _ = self.__numerator.quo_rem(g)
73                denom, _ = self.__denominator.quo_rem(g)
74            else:
75                numer = self.__numerator
76                denom = self.__denominator
77            if denom != 1 and denom.is_unit():
78                try:
79                    numer *= denom.inverse_of_unit()
80                    denom = denom.parent()(1)
81                except:
82                    pass
83            self.__numerator = numer; self.__denominator = denom
84        except AttributeError, s:
85            raise ArithmeticError, "unable to reduce because lack of gcd or quo_rem algorithm"
86        except TypeError, s:
87            raise ArithmeticError, "unable to reduce because gcd algorithm doesn't work on input"
88        except NotImplementedError, s:
89            raise ArithmeticError, "unable to reduce because gcd algorithm not implemented on input"
90
91    def copy(self):
92        return FractionFieldElement(self.parent(), self.__numerator,
93                                    self.__denominator, coerce=False, reduce=False)
94
95    def numerator(self):
96        return self.__numerator
97
98    def denominator(self):
99        return self.__denominator
100
101    def __hash__(self):
102        """
103        This function hashes in a special way to ensure that generators of a ring R
104        and generators of a fraction field of R have the same hash.  This enables them
105        to be used as keys interchangably in a dictionary (since \code{==} will claim them equal).
106        This is particularly useful for methods like subs on \code{ParentWithGens} if you
107        are passing a dictionary of substitutions.
108
109        EXAMPLES:
110            sage: R.<x>=ZZ[]
111            sage: hash(R.0)==hash(FractionField(R).0)
112            True
113            sage: ((x+1)/(x^2+1)).subs({x:1})
114            1
115            sage: d={x:1}
116            sage: d[FractionField(R).0]
117            1
118            sage: R.<x>=QQ[] # this probably has a separate implementation from ZZ[]
119            sage: hash(R.0)==hash(FractionField(R).0)
120            True
121            sage: d={x:1}
122            sage: d[FractionField(R).0]
123            1
124            sage: R.<x,y,z>=ZZ[] # this probably has a separate implementation from ZZ[]
125            sage: hash(R.0)==hash(FractionField(R).0)
126            True
127            sage: d={x:1}
128            sage: d[FractionField(R).0]
129            1
130            sage: R.<x,y,z>=QQ[] # this probably has a separate implementation from ZZ[]
131            sage: hash(R.0)==hash(FractionField(R).0)
132            True
133            sage: ((x+1)/(x^2+1)).subs({x:1})
134            1
135            sage: d={x:1}
136            sage: d[FractionField(R).0]
137            1
138            sage: hash(R(1)/R(2))==hash(1/2)
139            True
140        """
141        # This is same algorithm as used for members of QQ
142        #cdef long n, d
143        n = hash(self.__numerator)
144        d = hash(self.__denominator)
145        if d == 1:
146            return n
147        n = n ^ d
148        if n == -1:
149            return -2
150        return n
151
152    def partial_fraction_decomposition(self):
153        """
154        Decomposes fraction field element into a whole part and
155        a list of fraction field elements over prime power denominators.
156
157        The sum will be equal to the original fraction.
158        AUTHOR:
160        EXAMPLES:
161            sage: S.<t> = QQ[]
162            sage: q = 1/(t+1) + 2/(t+2) + 3/(t-3); q
163            (6*t^2 + 4*t - 6)/(t^3 - 7*t - 6)
164            sage: whole, parts = q.partial_fraction_decomposition(); parts
165            [3/(t - 3), 1/(t + 1), 2/(t + 2)]
166            sage: sum(parts) == q
167            True
168            sage: q = 1/(t^3+1) + 2/(t^2+2) + 3/(t-3)^5
169            sage: whole, parts = q.partial_fraction_decomposition(); parts
170            [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)]
171            sage: sum(parts) == q
172            True
173
174        We do the best we can over in-exact fields.
175            sage: R.<x> = RealField(20)[]
176            sage: q = 1/(x^2 + 2)^2 + 1/(x-1); q
177            (1.0000*x^4 + 4.0000*x^2 + 1.0000*x + 3.0000)/(1.0000*x^5 - 1.0000*x^4 + 4.0000*x^3 - 4.0000*x^2 + 4.0000*x - 4.0000)
178            sage: whole, parts = q.partial_fraction_decomposition(); parts
179            [(-7.6294e-6*x^2 + 1.0000)/(1.0000*x^4 + 4.0000*x^2 + 4.0000), 1.0000/(1.0000*x - 1.0000)]
180            sage: sum(parts)
181            (1.0000*x^4 - 7.6294e-6*x^3 + 4.0000*x^2 + 1.0000*x + 3.0000)/(1.0000*x^5 - 1.0000*x^4 + 4.0000*x^3 - 4.0000*x^2 + 4.0000*x - 4.0000)
182        """
183        denom = self.denominator()
184        whole, numer = self.numerator().quo_rem(denom)
185        factors = denom.factor()
186        if factors.unit_part != 1:
187            numer *= ~factors.unit_part()
188        if not self.parent().is_exact():
189            # factors not grouped in this case
190            # TODO: think about changing the factor code itself
191            # (what side effects would this have this be bad?)
192            all = {}
193            for r in factors: all[r[0]] = 0
194            for r in factors: all[r[0]] += r[1]
195            factors = all.iteritems()
196        factors = [r**e for r,e in factors]
197        parts = []
198        for d in factors:
199            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
200            parts.append(n/d)
201        return whole, parts
202
203    def __call__(self, *x):
204        """
205        Evaluate the fraction at the given arguments.  This assumes
206        that a call function is defined for the numerator and denominator.
207
208        EXAMPLES:
209            sage: x = MPolynomialRing(RationalField(),'x',3).gens()
210            sage: f = x[0] + x[1] - 2*x[1]*x[2]
211            sage: f
212            -2*x1*x2 + x0 + x1
213            sage: f(1,2,5)
214            -17
215            sage: h = f /(x[1] + x[2])
216            sage: h
217            (-2*x1*x2 + x0 + x1)/(x1 + x2)
218            sage: h(1,2,5)
219            -17/7
220        """
221        return self.__numerator(*x) / self.__denominator(*x)
222
223    def _is_atomic(self):
224        return self.__numerator._is_atomic() and self.__denominator._is_atomic()
225
226    def _repr_(self):
227        if self.is_zero():
228            return "0"
229        s = "%s"%self.__numerator
230        if self.__denominator != 1:
231            denom_string = str( self.__denominator )
232            if self.__denominator._is_atomic() and not ('*' in denom_string or '/' in denom_string):
233                s = "%s/%s"%(self.__numerator._coeff_repr(no_space=False),denom_string)
234            else:
235                s = "%s/(%s)"%(self.__numerator._coeff_repr(no_space=False),denom_string)
236        return s
237
238    def _latex_(self):
239        r"""
240        Return a latex representation of this rational function.
241
242        EXAMPLES:
243            sage: R = PolynomialRing(QQ, 'x').fraction_field()
244            sage: x = R.gen()
245            sage: a = x^2 / 1
246            sage: latex(a)
247            x^{2}
248            sage: latex(x^2/(x^2+1))
249            \frac{x^{2}}{x^{2} + 1}
250            sage: a = 1/x
251            sage: a._FractionFieldElement__numerator = R(0)
252            sage: latex(a)
253            0
254        """
255        if self.is_zero():
256            return "0"
257        if self.__denominator == 1:
258            return latex.latex(self.__numerator)
259        return "\\frac{%s}{%s}"%(latex.latex(self.__numerator),
260                                 latex.latex(self.__denominator))
261
263        if self.parent().is_exact():
264            try:
265                gcd_denom = self.__denominator.gcd(right.__denominator)
266                if not gcd_denom.is_unit():
267                    right_mul = self.__denominator // gcd_denom
268                    self_mul = right.__denominator // gcd_denom
269                    numer = self.__numerator * self_mul + right.__numerator * right_mul
270                    denom = self.__denominator * self_mul
271                    new_gcd = numer.gcd(denom)
272                    if not new_gcd.is_unit():
273                        numer = numer // new_gcd
274                        denom = denom // new_gcd
275                    return FractionFieldElement(self.parent(), numer, denom, coerce=False, reduce=False)
276                # else: no reduction necessary
277            except AttributeError: # missing gcd or quo_rem, don't reduce
278                pass
279            except NotImplementedError: # unimplemented gcd or quo_rem, don't reduce
280                pass
281        return FractionFieldElement(self.parent(),
282           self.__numerator*right.__denominator + self.__denominator*right.__numerator,
283           self.__denominator*right.__denominator,  coerce=False, reduce=False)
284
285    def _sub_(self, right):
286        if self.parent().is_exact():
287            try:
288                gcd_denom = self.__denominator.gcd(right.__denominator)
289                if not gcd_denom.is_unit():
290                    right_mul = self.__denominator // gcd_denom
291                    self_mul = right.__denominator // gcd_denom
292                    numer = self.__numerator * self_mul -  right.__numerator * right_mul
293                    denom = self.__denominator * self_mul
294                    new_gcd = numer.gcd(denom)
295                    if not new_gcd.is_unit():
296                        numer = numer // new_gcd
297                        denom = denom // new_gcd
298                    return FractionFieldElement(self.parent(), numer, denom, coerce=False, reduce=False)
299                # else: no reduction necessary
300            except AttributeError: # missing gcd or quo_rem, don't reduce
301                pass
302            except NotImplementedError: # unimplemented gcd or quo_rem, don't reduce
303                pass
304        return FractionFieldElement(self.parent(),
305           self.__numerator*right.__denominator - self.__denominator*right.__numerator,
306           self.__denominator*right.__denominator,  coerce=False, reduce=False)
307
308    def _mul_(self, right):
309        return FractionFieldElement(self.parent(),
310           self.__numerator*right.__numerator,
311           self.__denominator*right.__denominator, coerce=False, reduce=True)
312
313    def _div_(self, right):
314        return FractionFieldElement(self.parent(),
315           self.__numerator*right.__denominator,
316           self.__denominator*right.__numerator, coerce=False, reduce=True)
317
318    def __int__(self):
319        if self.__denominator == 1:
320            return int(self.__numerator)
321        else:
322            raise TypeError, "denominator must equal 1"
323
324    def _integer_(self):
325        if self.__denominator == 1:
326            try:
327                return self.__numerator._integer_()
328            except AttributeError:
329                pass
330        raise TypeError, "no way to coerce to an integer."
331
332    def _rational_(self):
333        Z = integer_ring.IntegerRing()
334        try:
335            return Z(self.__numerator) / Z(self.__denominator)
336        except AttributeError:
337            pass
338        raise TypeError, "coercion to rational not defined"
339
340    def __long__(self):
341        if self.__denominator == 1:
342            return long(self.__numerator)
343        else:
344            raise TypeError, "denominator must equal 1"
345
346    def __pow__(self, right):
347        r"""
348        Returns self raise to the $right^{th}$ power.
349
350        Note that we need to check whether or not right is negative so we
351        don't set __numerator or __denominator to an element of the
352        fraction field instead of the underlying ring.
353
354        EXAMPLES:
355            sage: R = QQ['x','y']
356            sage: FR = R.fraction_field()
357            sage: x,y = FR.gens()
358            sage: a = x^2; a
359            x^2
360            sage: type(a.numerator())
361            <type 'sage.rings.polynomial.multi_polynomial_libsingular.MPolynomial_libsingular'>
362            sage: type(a.denominator())
363            <type 'sage.rings.polynomial.multi_polynomial_libsingular.MPolynomial_libsingular'>
364            sage: a = x^(-2); a
365            1/x^2
366            sage: type(a.numerator())
367            <type 'sage.rings.polynomial.multi_polynomial_libsingular.MPolynomial_libsingular'>
368            sage: type(a.denominator())
369            <type 'sage.rings.polynomial.multi_polynomial_libsingular.MPolynomial_libsingular'>
370            sage: x^0
371            1
372            sage: ((x+y)/(x-y))^2
373            (x^2 + 2*x*y + y^2)/(x^2 - 2*x*y + y^2)
374            sage: ((x+y)/(x-y))^-2
375            (x^2 - 2*x*y + y^2)/(x^2 + 2*x*y + y^2)
376            sage: ((x+y)/(x-y))^0
377            1
378        """
379        if right == 0:
380            return FractionFieldElement(self.parent(), 1, 1, reduce=False)
381        elif right > 0:
382            return FractionFieldElement(self.parent(),
383                                        self.__numerator**right,
384                                        self.__denominator**right, coerce=False, reduce=False)
385        else:
386            right = -right
387            return FractionFieldElement(self.parent(),
388                                        self.__denominator**right,
389                                        self.__numerator**right, coerce=False, reduce=False)
390
391    def __neg__(self):
392        return FractionFieldElement(self.parent(), -self.__numerator, self.__denominator,
393                                    coerce=False, reduce=False)
394
395    def __pos__(self):
396        return self
397
398    def __abs__(self):
399        return abs(self.__numerator)/abs(self.__denominator)
400
401    def __invert__(self):
402        if self.is_zero():
403            raise ZeroDivisionError, "Cannot invert 0"
404        return FractionFieldElement(self.parent(),
405           self.__denominator, self.__numerator, coerce=False, reduce=False)
406
407    def __float__(self):
408        return float(self.__numerator) / float(self.__denominator)
409
410    def __cmp__(self, other):
411        return cmp(self.__numerator * other.__denominator, self.__denominator*other.__numerator)
412
413    def valuation(self):
414        """
415        Return the valuation of self, assuming that the numerator and
416        denominator have valuation functions defined on them.
417
418        EXAMPLES:
419            sage: x = PolynomialRing(RationalField(),'x').gen()
420            sage: f = (x**3 + x)/(x**2 - 2*x**3)
421            sage: f
422            (x^2 + 1)/(-2*x^2 + x)
423            sage: f.valuation()
424            -1
425        """
426        return self.__numerator.valuation() - self.__denominator.valuation()
Note: See TracBrowser for help on using the repository browser.