Ticket #10771: trac10771-lcm_gcd_fraction_fields.patch
File trac10771-lcm_gcd_fraction_fields.patch, 19.7 KB (added by , 11 years ago) |
---|
-
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 b 137 137 return True 138 138 139 139 class ElementMethods: 140 pass 140 # Fields are unique factorization domains, so, there is gcd and lcm 141 # Of course, in general gcd and lcm in a field are not very interesting. 142 # However, they should be implemented! 143 def gcd(self,other): 144 """ 145 Greatest common divisor. 146 147 NOTE: 148 149 Since we are in a field and the greatest common divisor is 150 only determined up to a unit, it is correct to either return 151 zero or one. Note that fraction fields of unique factorization 152 domains provide a more sophisticated gcd. 153 154 EXAMPLES:: 155 156 sage: GF(5)(1).gcd(GF(5)(1)) 157 1 158 sage: GF(5)(1).gcd(GF(5)(0)) 159 1 160 sage: GF(5)(0).gcd(GF(5)(0)) 161 0 162 163 For fields of characteristic zero (i.e., containing the 164 integers as a sub-ring), evaluation in the integer ring is 165 attempted. This is for backwards compatibility:: 166 167 sage: gcd(6.0,8); gcd(6.0,8).parent() 168 2 169 Integer Ring 170 171 If this fails, we resort to the default we see above:: 172 173 sage: gcd(6.0*CC.0,8*CC.0); gcd(6.0*CC.0,8*CC.0).parent() 174 1.00000000000000 175 Complex Field with 53 bits of precision 176 177 AUTHOR: 178 179 - Simon King (2011-02): Trac ticket #10771 180 181 """ 182 P = self.parent() 183 try: 184 other = P(other) 185 except (TypeError, ValueError): 186 raise ArithmeticError, "The second argument can not be interpreted in the parent of the first argument. Can't compute the gcd" 187 from sage.rings.integer_ring import ZZ 188 if ZZ.is_subring(P): 189 try: 190 return ZZ(self).gcd(ZZ(other)) 191 except TypeError: 192 pass 193 # there is no custom gcd, so, we resort to something that always exists 194 # (that's new behaviour) 195 if self==0 and other==0: 196 return P.zero() 197 return P.one() 198 199 def lcm(self,other): 200 """ 201 Least common multiple. 202 203 NOTE: 204 205 Since we are in a field and the least common multiple is 206 only determined up to a unit, it is correct to either return 207 zero or one. Note that fraction fields of unique factorization 208 domains provide a more sophisticated lcm. 209 210 EXAMPLES:: 211 212 sage: GF(2)(1).lcm(GF(2)(0)) 213 0 214 sage: GF(2)(1).lcm(GF(2)(1)) 215 1 216 217 If the field contains the integer ring, it is first 218 attempted to compute the gcd there:: 219 220 sage: lcm(15.0,12.0); lcm(15.0,12.0).parent() 221 60 222 Integer Ring 223 224 If this fails, we resort to the default we see above:: 225 226 sage: lcm(6.0*CC.0,8*CC.0); lcm(6.0*CC.0,8*CC.0).parent() 227 1.00000000000000 228 Complex Field with 53 bits of precision 229 sage: lcm(15.2,12.0) 230 1.00000000000000 231 232 AUTHOR: 233 234 - Simon King (2011-02): Trac ticket #10771 235 236 """ 237 P = self.parent() 238 try: 239 other = P(other) 240 except (TypeError, ValueError): 241 raise ArithmeticError, "The second argument can not be interpreted in the parent of the first argument. Can't compute the lcm" 242 from sage.rings.integer_ring import ZZ 243 if ZZ.is_subring(P): 244 try: 245 return ZZ(self).lcm(ZZ(other)) 246 except TypeError: 247 pass 248 # there is no custom lcm, so, we resort to something that always exists 249 if self==0 or other==0: 250 return P.zero() 251 return P.one() -
sage/categories/quotient_fields.py
diff --git a/sage/categories/quotient_fields.py b/sage/categories/quotient_fields.py
a b 52 52 def denominator(self): 53 53 pass 54 54 55 def gcd(self,other): 56 """ 57 Greatest common divisor 58 59 NOTE: 60 61 In a field, the greatest common divisor is not very 62 informative, as it is only determined up to a unit. But in 63 the fraction field of an integral domain that provides 64 both gcd and lcm, it is possible to be a bit more specific 65 and define the gcd uniquely up to a unit of the base ring 66 (rather than in the fraction field). 67 68 AUTHOR: 69 70 - Simon King (2011-02): See trac ticket #10771 71 72 EXAMPLES:: 73 74 sage: R.<x>=QQ[] 75 sage: p = (1+x)^3*(1+2*x^2)/(1-x^5) 76 sage: q = (1+x)^2*(1+3*x^2)/(1-x^4) 77 sage: factor(p) 78 (-2) * (x - 1)^-1 * (x + 1)^3 * (x^2 + 1/2) * (x^4 + x^3 + x^2 + x + 1)^-1 79 sage: factor(q) 80 (-3) * (x - 1)^-1 * (x + 1) * (x^2 + 1)^-1 * (x^2 + 1/3) 81 sage: gcd(p,q) 82 (x + 1)/(x^7 + x^5 - x^2 - 1) 83 sage: factor(gcd(p,q)) 84 (x - 1)^-1 * (x + 1) * (x^2 + 1)^-1 * (x^4 + x^3 + x^2 + x + 1)^-1 85 sage: factor(gcd(p,1+x)) 86 (x - 1)^-1 * (x + 1) * (x^4 + x^3 + x^2 + x + 1)^-1 87 sage: factor(gcd(1+x,q)) 88 (x - 1)^-1 * (x + 1) * (x^2 + 1)^-1 89 90 TESTS: 91 92 The following tests that the fraction field returns a correct gcd 93 even if the base ring does not provide lcm and gcd:: 94 95 sage: R = ZZ.extension(x^2+5,names='q'); R 96 Order in Number Field in q with defining polynomial x^2 + 5 97 sage: R.1 98 q 99 sage: gcd(R.1,R.1) 100 Traceback (most recent call last): 101 ... 102 TypeError: unable to find gcd of q and q 103 sage: (R.1/1).parent() 104 Number Field in q with defining polynomial x^2 + 5 105 sage: gcd(R.1/1,R.1) 106 1 107 sage: gcd(R.1/1,0) 108 1 109 sage: gcd(R.zero(),0) 110 0 111 112 """ 113 try: 114 other = self.parent()(other) 115 except (TypeError, ValueError): 116 raise ArithmeticError, "The second argument can not be interpreted in the parent of the first argument. Can't compute the gcd" 117 try: 118 selfN = self.numerator() 119 selfD = self.denominator() 120 selfGCD = selfN.gcd(selfD) 121 otherN = other.numerator() 122 otherD = other.denominator() 123 otherGCD = otherN.gcd(otherD) 124 selfN = selfN // selfGCD 125 selfD = selfD // selfGCD 126 otherN = otherN // otherGCD 127 otherD = otherD // otherGCD 128 return selfN.gcd(otherN)/selfD.lcm(otherD) 129 except (AttributeError, NotImplementedError, TypeError, ValueError): 130 if self==0 and other==0: 131 return self.parent().zero() 132 return self.parent().one() 133 134 def lcm(self,other): 135 """ 136 Least common multiple 137 138 NOTE: 139 140 In a field, the least common multiple is not very 141 informative, as it is only determined up to a unit. But in 142 the fraction field of an integral domain that provides 143 both gcd and lcm, it is reasonable to be a bit more 144 specific and to define the least common multiple so that 145 it restricts to the usual least common multiple in the 146 base ring and is unique up to a unit of the base ring 147 (rather than up to a unit of the fraction field). 148 149 AUTHOR: 150 151 - Simon King (2011-02): See trac ticket #10771 152 153 EXAMPLES:: 154 155 sage: R.<x>=QQ[] 156 sage: p = (1+x)^3*(1+2*x^2)/(1-x^5) 157 sage: q = (1+x)^2*(1+3*x^2)/(1-x^4) 158 sage: factor(p) 159 (-2) * (x - 1)^-1 * (x + 1)^3 * (x^2 + 1/2) * (x^4 + x^3 + x^2 + x + 1)^-1 160 sage: factor(q) 161 (-3) * (x - 1)^-1 * (x + 1) * (x^2 + 1)^-1 * (x^2 + 1/3) 162 sage: factor(lcm(p,q)) 163 (x - 1)^-1 * (x + 1)^3 * (x^2 + 1/3) * (x^2 + 1/2) 164 sage: factor(lcm(p,1+x)) 165 (x + 1)^3 * (x^2 + 1/2) 166 sage: factor(lcm(1+x,q)) 167 (x + 1) * (x^2 + 1/3) 168 169 TESTS: 170 171 The following tests that the fraction field returns a correct lcm 172 even if the base ring does not provide lcm and gcd:: 173 174 sage: R = ZZ.extension(x^2+5,names='q'); R 175 Order in Number Field in q with defining polynomial x^2 + 5 176 sage: R.1 177 q 178 sage: lcm(R.1,R.1) 179 Traceback (most recent call last): 180 ... 181 TypeError: unable to find lcm of q and q 182 sage: (R.1/1).parent() 183 Number Field in q with defining polynomial x^2 + 5 184 sage: lcm(R.1/1,R.1) 185 1 186 sage: lcm(R.1/1,0) 187 0 188 sage: lcm(R.zero(),0) 189 0 190 191 """ 192 try: 193 other = self.parent()(other) 194 except (TypeError, ValueError): 195 raise ArithmeticError, "The second argument can not be interpreted in the parent of the first argument. Can't compute the lcm" 196 try: 197 selfN = self.numerator() 198 selfD = self.denominator() 199 selfGCD = selfN.gcd(selfD) 200 otherN = other.numerator() 201 otherD = other.denominator() 202 otherGCD = otherN.gcd(otherD) 203 selfN = selfN // selfGCD 204 selfD = selfD // selfGCD 205 otherN = otherN // otherGCD 206 otherD = otherD // otherGCD 207 return selfN.lcm(otherN)/selfD.gcd(otherD) 208 except (AttributeError, NotImplementedError, TypeError, ValueError): 209 if self==0 or other==0: 210 return self.parent().zero() 211 return self.parent().one() 212 55 213 def factor(self, *args, **kwds): 56 214 """ 57 215 Return the factorization of ``self`` over the base ring. … … 214 372 Returns the derivative of this rational function with respect to the 215 373 variable ``var``. 216 374 217 Over an ring with a working GCDimplementation, the derivative of a375 Over an ring with a working gcd implementation, the derivative of a 218 376 fraction `f/g`, supposed to be given in lowest terms, is computed as 219 377 `(f'(g/d) - f(g'/d))/(g(g'/d))`, where `d` is a greatest common 220 378 divisor of `f` and `g`. -
sage/rings/arith.py
diff --git a/sage/rings/arith.py b/sage/rings/arith.py
a b 1412 1412 1413 1413 Additional keyword arguments are passed to the respectively called 1414 1414 methods. 1415 1416 OUTPUT: 1417 1418 The given elements are first coerced into a common parent. Then, 1419 their greatest common divisor *in that common parent* is returned. 1415 1420 1416 1421 EXAMPLES:: 1417 1422 … … 1419 1424 1 1420 1425 sage: GCD(97*10^15, 19^20*97^2) 1421 1426 97 1422 sage: GCD(2/3, 4/ 3)1423 11427 sage: GCD(2/3, 4/5) 1428 2/15 1424 1429 sage: GCD([2,4,6,8]) 1425 1430 2 1426 1431 sage: GCD(srange(0,10000,10)) # fast !! … … 1451 1456 0 1452 1457 sage: type(gcd([])) 1453 1458 <type 'sage.rings.integer.Integer'> 1459 1460 TESTS: 1461 1462 The following shows that indeed coercion takes place before computing 1463 the gcd. This behaviour was introduced in trac ticket #10771:: 1464 1465 sage: R.<x>=QQ[] 1466 sage: S.<x>=ZZ[] 1467 sage: p = S.random_element() 1468 sage: q = R.random_element() 1469 sage: parent(gcd(1/p,q)) 1470 Fraction Field of Univariate Polynomial Ring in x over Rational Field 1471 sage: parent(gcd([1/p,q])) 1472 Fraction Field of Univariate Polynomial Ring in x over Rational Field 1473 1474 1454 1475 """ 1455 1476 # Most common use case first: 1456 1477 if b is not None: 1457 if hasattr(a, "gcd"): 1458 return a.gcd(b, **kwargs) 1459 else: 1478 from sage.structure.element import get_coercion_model 1479 cm = get_coercion_model() 1480 a,b = cm.canonical_coercion(a,b) 1481 try: 1482 GCD = a.gcd 1483 except AttributeError: 1460 1484 try: 1461 1485 return ZZ(a).gcd(ZZ(b)) 1462 1486 except TypeError: 1463 1487 raise TypeError, "unable to find gcd of %s and %s"%(a,b) 1464 1488 return GCD(b, **kwargs) 1489 1465 1490 from sage.structure.sequence import Sequence 1466 1491 seq = Sequence(a) 1467 1492 U = seq.universe() … … 1528 1553 1529 1554 - ``a`` - a list or tuple of elements of a ring with 1530 1555 lcm 1531 1556 1557 OUTPUT: 1558 1559 First, the given elements are coerced into a common parent. Then, 1560 their least common multiple *in that parent* is returned. 1532 1561 1533 1562 EXAMPLES:: 1534 1563 … … 1545 1574 sage: v = LCM(range(1,10000)) # *very* fast! 1546 1575 sage: len(str(v)) 1547 1576 4349 1577 1578 TESTS: 1579 1580 The following tests against a bug that was fixed in trac 1581 ticket #10771:: 1582 1583 sage: lcm(4/1,2) 1584 4 1585 1586 The following shows that indeed coercion takes place before 1587 computing the least common multiple:: 1588 1589 sage: R.<x>=QQ[] 1590 sage: S.<x>=ZZ[] 1591 sage: p = S.random_element() 1592 sage: q = R.random_element() 1593 sage: parent(lcm([1/p,q])) 1594 Fraction Field of Univariate Polynomial Ring in x over Rational Field 1595 1548 1596 """ 1549 1597 # Most common use case first: 1550 1598 if b is not None: 1551 if hasattr(a, "lcm"): 1552 return a.lcm(b) 1553 else: 1599 from sage.structure.element import get_coercion_model 1600 cm = get_coercion_model() 1601 a,b = cm.canonical_coercion(a,b) 1602 try: 1603 LCM = a.lcm 1604 except AttributeError: 1554 1605 try: 1555 1606 return ZZ(a).lcm(ZZ(b)) 1556 1607 except TypeError: 1557 raise TypeError, "unable to find gcd of %s and %s"%(a,b) 1558 1608 raise TypeError, "unable to find lcm of %s and %s"%(a,b) 1609 return LCM(b) 1610 1559 1611 from sage.structure.sequence import Sequence 1560 1612 seq = Sequence(a) 1561 1613 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 b 918 918 sage: f.content().parent() 919 919 Integer Ring 920 920 921 TESTS:: 921 TESTS: 922 923 Since trac ticket #10771, the gcd in QQ restricts to the 924 gcd in ZZ. 922 925 923 926 sage: R.<x,y> = QQ[] 924 927 sage: f = 4*x+6*y 925 sage: f.content() 926 1 928 sage: f.content(); f.content().parent() 929 2 930 Rational Field 927 931 928 932 """ 929 933 from sage.rings.arith import gcd 930 934 from sage.rings.all import ZZ 931 932 return gcd(self.coefficients(),integer=self.parent() is ZZ) 935 return gcd(self.coefficients()) 933 936 934 937 def is_generator(self): 935 938 r""" -
sage/rings/rational.pyx
diff --git a/sage/rings/rational.pyx b/sage/rings/rational.pyx
a b 896 896 from sage.rings.arith import gcd, lcm 897 897 return gcd(nums) / lcm(denoms) 898 898 899 def gcd(self, other, **kwds):900 """901 Return a gcd of the rational numbers self and other.902 903 If self = other = 0, this is by convention 0. In all other904 cases it can (mathematically) be any nonzero rational number,905 but for simplicity we choose to always return 1.906 907 EXAMPLES::908 909 sage: gcd(1/3,2/1)910 1911 sage: gcd(1/1,0/1)912 1913 sage: gcd(0/1,0/1)914 0915 """916 if self == 0 and other == 0:917 return Rational(0)918 else:919 return Rational(1)899 # def gcd_rational(self, other, **kwds): 900 # """ 901 # Return a gcd of the rational numbers self and other. 902 # 903 # If self = other = 0, this is by convention 0. In all other 904 # cases it can (mathematically) be any nonzero rational number, 905 # but for simplicity we choose to always return 1. 906 # 907 # EXAMPLES:: 908 # 909 # sage: (1/3).gcd_rational(2/1) 910 # 1 911 # sage: (1/1).gcd_rational(0/1) 912 # 1 913 # sage: (0/1).gcd_rational(0/1) 914 # 0 915 # """ 916 # if self == 0 and other == 0: 917 # return Rational(0) 918 # else: 919 # return Rational(1) 920 920 921 921 def valuation(self, p): 922 922 r""" -
sage/rings/ring.pyx
diff --git a/sage/rings/ring.pyx b/sage/rings/ring.pyx
a b 1612 1612 2^3 * 11 1613 1613 1614 1614 In a field, any nonzero element is a GCD of any nonempty set 1615 of elements. For concreteness, Sage returns 1 in these cases:: 1615 of nonzero elements. In previous versions, Sage used to return 1616 1 in the case of the rational field. However, since trac 1617 ticket #10771, the rational field is considered as the 1618 *fraction field* of the integer ring. For the fraction field 1619 of an integral domain that provides both GCD and LCM, it is 1620 possible to pick a GCD that is compatible with the GCD of the 1621 base ring:: 1616 1622 1617 1623 sage: QQ.gcd(ZZ(42), ZZ(48)); type(QQ.gcd(ZZ(42), ZZ(48))) 1618 11624 6 1619 1625 <type 'sage.rings.rational.Rational'> 1620 1626 sage: QQ.gcd(1/2, 1/3) 1621 1 1627 1/6 1622 1628 1623 1629 Polynomial rings over fields are GCD domains as well. Here is a simple 1624 1630 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 b 165 165 sage: std([]) 166 166 NaN 167 167 sage: std([I, sqrt(2), 3/5]) 168 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)168 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) 169 169 sage: std([RIF(1.0103, 1.0103), RIF(2)]) 170 170 0.6998235813403261? 171 171 sage: import numpy … … 230 230 sage: variance([]) 231 231 NaN 232 232 sage: variance([I, sqrt(2), 3/5]) 233 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)^2233 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 234 234 sage: variance([RIF(1.0103, 1.0103), RIF(2)]) 235 235 0.4897530450000000? 236 236 sage: import numpy