# Ticket #10771: trac10771-lcm_gcd_fraction_fields.patch

File trac10771-lcm_gcd_fraction_fields.patch, 19.7 KB (added by SimonKing, 11 years ago)

Implement gcd and lcm for general fields, with the special case of the fraction field of a unique factorization domain

• ## sage/categories/fields.py

```# HG changeset patch
# User Simon King <simon.king@uni-jena.de>
# Date 1297947839 -3600
# Node ID 6289126bdd0bdd30ed9b7467572af6bf5e80030b
# Parent  fb00ec75853019eb9799fd863b193fe82ee97c74
#10771: gcd/lcm for fraction fields of UFDs and for general fields.
In the case of fraction fields, it restricts to gcd/lcm in the base ring.
For general fields, properly restrict to ZZ, but otherwise return 0/1.

diff --git a/sage/categories/fields.py b/sage/categories/fields.py```
 a return True class ElementMethods: pass # Fields are unique factorization domains, so, there is gcd and lcm # Of course, in general gcd and lcm in a field are not very interesting. # However, they should be implemented! def gcd(self,other): """ Greatest common divisor. NOTE: Since we are in a field and the greatest common divisor is only determined up to a unit, it is correct to either return zero or one. Note that fraction fields of unique factorization domains provide a more sophisticated gcd. EXAMPLES:: sage: GF(5)(1).gcd(GF(5)(1)) 1 sage: GF(5)(1).gcd(GF(5)(0)) 1 sage: GF(5)(0).gcd(GF(5)(0)) 0 For fields of characteristic zero (i.e., containing the integers as a sub-ring), evaluation in the integer ring is attempted. This is for backwards compatibility:: sage: gcd(6.0,8); gcd(6.0,8).parent() 2 Integer Ring If this fails, we resort to the default we see above:: sage: gcd(6.0*CC.0,8*CC.0); gcd(6.0*CC.0,8*CC.0).parent() 1.00000000000000 Complex Field with 53 bits of precision AUTHOR: - Simon King (2011-02): Trac ticket #10771 """ P = self.parent() try: other = P(other) except (TypeError, ValueError): raise ArithmeticError, "The second argument can not be interpreted in the parent of the first argument. Can't compute the gcd" from sage.rings.integer_ring import ZZ if ZZ.is_subring(P): try: return ZZ(self).gcd(ZZ(other)) except TypeError: pass # there is no custom gcd, so, we resort to something that always exists # (that's new behaviour) if self==0 and other==0: return P.zero() return P.one() def lcm(self,other): """ Least common multiple. NOTE: Since we are in a field and the least common multiple is only determined up to a unit, it is correct to either return zero or one. Note that fraction fields of unique factorization domains provide a more sophisticated lcm. EXAMPLES:: sage: GF(2)(1).lcm(GF(2)(0)) 0 sage: GF(2)(1).lcm(GF(2)(1)) 1 If the field contains the integer ring, it is first attempted to compute the gcd there:: sage: lcm(15.0,12.0); lcm(15.0,12.0).parent() 60 Integer Ring If this fails, we resort to the default we see above:: sage: lcm(6.0*CC.0,8*CC.0); lcm(6.0*CC.0,8*CC.0).parent() 1.00000000000000 Complex Field with 53 bits of precision sage: lcm(15.2,12.0) 1.00000000000000 AUTHOR: - Simon King (2011-02): Trac ticket #10771 """ P = self.parent() try: other = P(other) except (TypeError, ValueError): raise ArithmeticError, "The second argument can not be interpreted in the parent of the first argument. Can't compute the lcm" from sage.rings.integer_ring import ZZ if ZZ.is_subring(P): try: return ZZ(self).lcm(ZZ(other)) except TypeError: pass # there is no custom lcm, so, we resort to something that always exists if self==0 or other==0: return P.zero() return P.one()
• ## sage/categories/quotient_fields.py

`diff --git a/sage/categories/quotient_fields.py b/sage/categories/quotient_fields.py`
 a def denominator(self): pass def gcd(self,other): """ Greatest common divisor NOTE: In a field, the greatest common divisor is not very informative, as it is only determined up to a unit. But in the fraction field of an integral domain that provides both gcd and lcm, it is possible to be a bit more specific and define the gcd uniquely up to a unit of the base ring (rather than in the fraction field). AUTHOR: - Simon King (2011-02): See trac ticket #10771 EXAMPLES:: sage: R.=QQ[] sage: p = (1+x)^3*(1+2*x^2)/(1-x^5) sage: q = (1+x)^2*(1+3*x^2)/(1-x^4) sage: factor(p) (-2) * (x - 1)^-1 * (x + 1)^3 * (x^2 + 1/2) * (x^4 + x^3 + x^2 + x + 1)^-1 sage: factor(q) (-3) * (x - 1)^-1 * (x + 1) * (x^2 + 1)^-1 * (x^2 + 1/3) sage: gcd(p,q) (x + 1)/(x^7 + x^5 - x^2 - 1) sage: factor(gcd(p,q)) (x - 1)^-1 * (x + 1) * (x^2 + 1)^-1 * (x^4 + x^3 + x^2 + x + 1)^-1 sage: factor(gcd(p,1+x)) (x - 1)^-1 * (x + 1) * (x^4 + x^3 + x^2 + x + 1)^-1 sage: factor(gcd(1+x,q)) (x - 1)^-1 * (x + 1) * (x^2 + 1)^-1 TESTS: The following tests that the fraction field returns a correct gcd even if the base ring does not provide lcm and gcd:: sage: R = ZZ.extension(x^2+5,names='q'); R Order in Number Field in q with defining polynomial x^2 + 5 sage: R.1 q sage: gcd(R.1,R.1) Traceback (most recent call last): ... TypeError: unable to find gcd of q and q sage: (R.1/1).parent() Number Field in q with defining polynomial x^2 + 5 sage: gcd(R.1/1,R.1) 1 sage: gcd(R.1/1,0) 1 sage: gcd(R.zero(),0) 0 """ try: other = self.parent()(other) except (TypeError, ValueError): raise ArithmeticError, "The second argument can not be interpreted in the parent of the first argument. Can't compute the gcd" try: selfN = self.numerator() selfD = self.denominator() selfGCD = selfN.gcd(selfD) otherN = other.numerator() otherD = other.denominator() otherGCD = otherN.gcd(otherD) selfN = selfN // selfGCD selfD = selfD // selfGCD otherN = otherN // otherGCD otherD = otherD // otherGCD return selfN.gcd(otherN)/selfD.lcm(otherD) except (AttributeError, NotImplementedError, TypeError, ValueError): if self==0 and other==0: return self.parent().zero() return self.parent().one() def lcm(self,other): """ Least common multiple NOTE: In a field, the least common multiple is not very informative, as it is only determined up to a unit. But in the fraction field of an integral domain that provides both gcd and lcm, it is reasonable to be a bit more specific and to define the least common multiple so that it restricts to the usual least common multiple in the base ring and is unique up to a unit of the base ring (rather than up to a unit of the fraction field). AUTHOR: - Simon King (2011-02): See trac ticket #10771 EXAMPLES:: sage: R.=QQ[] sage: p = (1+x)^3*(1+2*x^2)/(1-x^5) sage: q = (1+x)^2*(1+3*x^2)/(1-x^4) sage: factor(p) (-2) * (x - 1)^-1 * (x + 1)^3 * (x^2 + 1/2) * (x^4 + x^3 + x^2 + x + 1)^-1 sage: factor(q) (-3) * (x - 1)^-1 * (x + 1) * (x^2 + 1)^-1 * (x^2 + 1/3) sage: factor(lcm(p,q)) (x - 1)^-1 * (x + 1)^3 * (x^2 + 1/3) * (x^2 + 1/2) sage: factor(lcm(p,1+x)) (x + 1)^3 * (x^2 + 1/2) sage: factor(lcm(1+x,q)) (x + 1) * (x^2 + 1/3) TESTS: The following tests that the fraction field returns a correct lcm even if the base ring does not provide lcm and gcd:: sage: R = ZZ.extension(x^2+5,names='q'); R Order in Number Field in q with defining polynomial x^2 + 5 sage: R.1 q sage: lcm(R.1,R.1) Traceback (most recent call last): ... TypeError: unable to find lcm of q and q sage: (R.1/1).parent() Number Field in q with defining polynomial x^2 + 5 sage: lcm(R.1/1,R.1) 1 sage: lcm(R.1/1,0) 0 sage: lcm(R.zero(),0) 0 """ try: other = self.parent()(other) except (TypeError, ValueError): raise ArithmeticError, "The second argument can not be interpreted in the parent of the first argument. Can't compute the lcm" try: selfN = self.numerator() selfD = self.denominator() selfGCD = selfN.gcd(selfD) otherN = other.numerator() otherD = other.denominator() otherGCD = otherN.gcd(otherD) selfN = selfN // selfGCD selfD = selfD // selfGCD otherN = otherN // otherGCD otherD = otherD // otherGCD return selfN.lcm(otherN)/selfD.gcd(otherD) except (AttributeError, NotImplementedError, TypeError, ValueError): if self==0 or other==0: return self.parent().zero() return self.parent().one() def factor(self, *args, **kwds): """ Return the factorization of ``self`` over the base ring. Returns the derivative of this rational function with respect to the variable ``var``. Over an ring with a working GCD implementation, the derivative of a Over an ring with a working gcd implementation, the derivative of a fraction `f/g`, supposed to be given in lowest terms, is computed as `(f'(g/d) - f(g'/d))/(g(g'/d))`, where `d` is a greatest common divisor of `f` and `g`.
• ## sage/rings/arith.py

`diff --git a/sage/rings/arith.py b/sage/rings/arith.py`
 a Additional keyword arguments are passed to the respectively called methods. OUTPUT: The given elements are first coerced into a common parent. Then, their greatest common divisor *in that common parent* is returned. EXAMPLES:: 1 sage: GCD(97*10^15, 19^20*97^2) 97 sage: GCD(2/3, 4/3) 1 sage: GCD(2/3, 4/5) 2/15 sage: GCD([2,4,6,8]) 2 sage: GCD(srange(0,10000,10))  # fast  !! 0 sage: type(gcd([])) TESTS: The following shows that indeed coercion takes place before computing the gcd. This behaviour was introduced in trac ticket #10771:: sage: R.=QQ[] sage: S.=ZZ[] sage: p = S.random_element() sage: q = R.random_element() sage: parent(gcd(1/p,q)) Fraction Field of Univariate Polynomial Ring in x over Rational Field sage: parent(gcd([1/p,q])) Fraction Field of Univariate Polynomial Ring in x over Rational Field """ # Most common use case first: if b is not None: if hasattr(a, "gcd"): return a.gcd(b, **kwargs) else: from sage.structure.element import get_coercion_model cm = get_coercion_model() a,b = cm.canonical_coercion(a,b) try: GCD = a.gcd except AttributeError: try: return ZZ(a).gcd(ZZ(b)) except TypeError: raise TypeError, "unable to find gcd of %s and %s"%(a,b) return GCD(b, **kwargs) from sage.structure.sequence import Sequence seq = Sequence(a) U = seq.universe() -  ``a`` - a list or tuple of elements of a ring with lcm OUTPUT: First, the given elements are coerced into a common parent. Then, their least common multiple *in that parent* is returned. EXAMPLES:: sage: v = LCM(range(1,10000))   # *very* fast! sage: len(str(v)) 4349 TESTS: The following tests against a bug that was fixed in trac ticket #10771:: sage: lcm(4/1,2) 4 The following shows that indeed coercion takes place before computing the least common multiple:: sage: R.=QQ[] sage: S.=ZZ[] sage: p = S.random_element() sage: q = R.random_element() sage: parent(lcm([1/p,q])) Fraction Field of Univariate Polynomial Ring in x over Rational Field """ # Most common use case first: if b is not None: if hasattr(a, "lcm"): return a.lcm(b) else: from sage.structure.element import get_coercion_model cm = get_coercion_model() a,b = cm.canonical_coercion(a,b) try: LCM = a.lcm except AttributeError: try: return ZZ(a).lcm(ZZ(b)) except TypeError: raise TypeError, "unable to find gcd of %s and %s"%(a,b) raise TypeError, "unable to find lcm of %s and %s"%(a,b) return LCM(b) from sage.structure.sequence import Sequence seq = Sequence(a) U = seq.universe()
• ## sage/rings/polynomial/multi_polynomial.pyx

`diff --git a/sage/rings/polynomial/multi_polynomial.pyx b/sage/rings/polynomial/multi_polynomial.pyx`
 a sage: f.content().parent() Integer Ring TESTS:: TESTS: Since trac ticket #10771, the gcd in QQ restricts to the gcd in ZZ. sage: R. = QQ[] sage: f = 4*x+6*y sage: f.content() 1 sage: f.content(); f.content().parent() 2 Rational Field """ from sage.rings.arith import gcd from sage.rings.all import ZZ return gcd(self.coefficients(),integer=self.parent() is ZZ) return gcd(self.coefficients()) def is_generator(self): r"""
• ## sage/rings/rational.pyx

`diff --git a/sage/rings/rational.pyx b/sage/rings/rational.pyx`
 a from sage.rings.arith import gcd, lcm return gcd(nums) / lcm(denoms) def gcd(self, other, **kwds): """ Return a gcd of the rational numbers self and other. If self = other = 0, this is by convention 0.  In all other cases it can (mathematically) be any nonzero rational number, but for simplicity we choose to always return 1. EXAMPLES:: sage: gcd(1/3, 2/1) 1 sage: gcd(1/1, 0/1) 1 sage: gcd(0/1, 0/1) 0 """ if self == 0 and other == 0: return Rational(0) else: return Rational(1) #    def gcd_rational(self, other, **kwds): #        """ #        Return a gcd of the rational numbers self and other. # #        If self = other = 0, this is by convention 0.  In all other #        cases it can (mathematically) be any nonzero rational number, #        but for simplicity we choose to always return 1. # #        EXAMPLES:: # #            sage: (1/3).gcd_rational(2/1) #            1 #            sage: (1/1).gcd_rational(0/1) #            1 #            sage: (0/1).gcd_rational(0/1) #            0 #        """ #        if self == 0 and other == 0: #            return Rational(0) #        else: #            return Rational(1) def valuation(self, p): r"""
• ## sage/rings/ring.pyx

`diff --git a/sage/rings/ring.pyx b/sage/rings/ring.pyx`
 a 2^3 * 11 In a field, any nonzero element is a GCD of any nonempty set of elements.  For concreteness, Sage returns 1 in these cases:: of nonzero elements. In previous versions, Sage used to return 1 in the case of the rational field. However, since trac ticket #10771, the rational field is considered as the *fraction field* of the integer ring. For the fraction field of an integral domain that provides both GCD and LCM, it is possible to pick a GCD that is compatible with the GCD of the base ring:: sage: QQ.gcd(ZZ(42), ZZ(48)); type(QQ.gcd(ZZ(42), ZZ(48))) 1 6 sage: QQ.gcd(1/2, 1/3) 1 1/6 Polynomial rings over fields are GCD domains as well. Here is a simple example over the ring of polynomials over the rationals as well as
• ## sage/stats/basic_stats.py

`diff --git a/sage/stats/basic_stats.py b/sage/stats/basic_stats.py`
 a sage: std([]) NaN sage: std([I, sqrt(2), 3/5]) sqrt(1/450*(5*sqrt(2) + 5*I - 6)^2 + 1/450*(5*sqrt(2) - 10*I + 3)^2 + 1/450*(10*sqrt(2) - 5*I - 3)^2) sqrt(1/450*(-5*sqrt(2) + 10*I - 3)^2 + 1/450*(-5*sqrt(2) - 5*I + 6)^2 + 1/450*(10*sqrt(2) - 5*I - 3)^2) sage: std([RIF(1.0103, 1.0103), RIF(2)]) 0.6998235813403261? sage: import numpy sage: variance([]) NaN sage: variance([I, sqrt(2), 3/5]) 1/450*(5*sqrt(2) + 5*I - 6)^2 + 1/450*(5*sqrt(2) - 10*I + 3)^2 + 1/450*(10*sqrt(2) - 5*I - 3)^2 1/450*(-5*sqrt(2) + 10*I - 3)^2 + 1/450*(-5*sqrt(2) - 5*I + 6)^2 + 1/450*(10*sqrt(2) - 5*I - 3)^2 sage: variance([RIF(1.0103, 1.0103), RIF(2)]) 0.4897530450000000? sage: import numpy