From 507d31230e79b0cc0bf849818fb9a99981333644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Wed, 27 Jul 2016 10:15:31 +0200 Subject: [PATCH 001/740] #21092 Unhandled case in EllipticCurve_from_cubic --- .../schemes/elliptic_curves/constructor.py | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index 8d7238f851b..e5324f2be00 100644 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -874,6 +874,16 @@ def EllipticCurve_from_cubic(F, P, morphism=True): sage: cubic = x^2*y + 4*x*y^2 + x^2*z + 8*x*y*z + 4*y^2*z + 9*x*z^2 + 9*y*z^2 sage: EllipticCurve_from_cubic(cubic, [1,-1,1], morphism=False) Elliptic Curve defined by y^2 - 882*x*y - 2560000*y = x^3 - 127281*x^2 over Rational Field + + sage: R. = QQ[] + sage: cubic = -3*x^2*y + 3*x*y^2 + 4*x^2*z + 4*y^2*z - 3*x*z^2 + 3*y*z^2 - 8*z^3 + sage: EllipticCurve_from_cubic(cubic, (-4/5, 4/5, 3/5) ) + Scheme morphism: + From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: + -3*x^2*y + 3*x*y^2 + 4*x^2*z + 4*y^2*z - 3*x*z^2 + 3*y*z^2 - 8*z^3 + To: Elliptic Curve defined by y^2 + 10*x*y + 112*y = x^3 + 46*x^2 + 336*x over Rational Field + Defn: Defined on coordinates by sending (x : y : z) to + (1/3*z : y - 1/3*z : 1/112*x - 1/112*y - 1/42*z) """ import sage.matrix.all as matrix @@ -896,9 +906,18 @@ def EllipticCurve_from_cubic(F, P, morphism=True): raise TypeError('%s is not a projective point'%P) x, y, z = R.gens() - # First case: if P = P2 then P is a flex + # First case: if P = P2 then P is a flex, or if P2 = P3 then P2 is a flex P2 = chord_and_tangent(F, P) + flex_point = None if are_projectively_equivalent(P, P2, base_ring=K): + flex_point = P + else: + P3 = chord_and_tangent(F, P2) + if are_projectively_equivalent(P2, P3, base_ring=K): + flex_point = P2 + + if flex_point is not None: + P = flex_point # find the tangent to F in P dx = K(F.derivative(x)(P)) dy = K(F.derivative(y)(P)) @@ -935,9 +954,8 @@ def EllipticCurve_from_cubic(F, P, morphism=True): fwd_defining_poly = [trans_x, trans_y, -b*trans_z] fwd_post = -a - # Second case: P is not a flex, then P, P2, P3 are different + # Second case: P, P2, P3 are different else: - P3 = chord_and_tangent(F, P2) # send P, P2, P3 to (1:0:0), (0:1:0), (0:0:1) respectively M = matrix.matrix(K, [P, P2, P3]).transpose() F2 = M.act_on_polynomial(F) From df73f437cb2e5e5c7efa0bc53d3fd66569e18565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 28 Jul 2016 09:57:45 +0200 Subject: [PATCH 002/740] trac 21092 correct failing doctest, plus link to trac ticket --- src/sage/schemes/elliptic_curves/constructor.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index e5324f2be00..63a776e760d 100644 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -875,15 +875,17 @@ def EllipticCurve_from_cubic(F, P, morphism=True): sage: EllipticCurve_from_cubic(cubic, [1,-1,1], morphism=False) Elliptic Curve defined by y^2 - 882*x*y - 2560000*y = x^3 - 127281*x^2 over Rational Field + Here is a test for :trac:`21092`:: + sage: R. = QQ[] sage: cubic = -3*x^2*y + 3*x*y^2 + 4*x^2*z + 4*y^2*z - 3*x*z^2 + 3*y*z^2 - 8*z^3 sage: EllipticCurve_from_cubic(cubic, (-4/5, 4/5, 3/5) ) Scheme morphism: From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: -3*x^2*y + 3*x*y^2 + 4*x^2*z + 4*y^2*z - 3*x*z^2 + 3*y*z^2 - 8*z^3 - To: Elliptic Curve defined by y^2 + 10*x*y + 112*y = x^3 + 46*x^2 + 336*x over Rational Field + To: Elliptic Curve defined by y^2 - 6*x*y - 112*y = x^3 + 62*x^2 + 560*x over Rational Field Defn: Defined on coordinates by sending (x : y : z) to - (1/3*z : y - 1/3*z : 1/112*x - 1/112*y - 1/42*z) + (1/3*z : -y - 1/3*z : 1/112*x - 1/112*y - 1/42*z) """ import sage.matrix.all as matrix From fcb5fd10d333868bad60edaa9596e4bae84e72e3 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Mon, 29 Sep 2014 12:00:36 +0200 Subject: [PATCH 003/740] initial commit --- augmented_valuation.py | 866 ++++++++++++++++++++++++++++ developing_valuation.py | 1052 +++++++++++++++++++++++++++++++++++ function_field_valuation.py | 189 +++++++ gauss_valuation.py | 490 ++++++++++++++++ padic_valuation.py | 777 ++++++++++++++++++++++++++ valuation.py | 7 + 6 files changed, 3381 insertions(+) create mode 100644 augmented_valuation.py create mode 100644 developing_valuation.py create mode 100644 function_field_valuation.py create mode 100644 gauss_valuation.py create mode 100644 padic_valuation.py create mode 100644 valuation.py diff --git a/augmented_valuation.py b/augmented_valuation.py new file mode 100644 index 00000000000..d4063bddc87 --- /dev/null +++ b/augmented_valuation.py @@ -0,0 +1,866 @@ +""" +Augmented valuations polynomial rings + +Implements inductive valuations as defined in [ML1936]. + +REFERENCES: + +.. [ML1936] Mac Lane, S. (1936). A construction for prime ideals as absolute +values of an algebraic field. Duke Mathematical Journal, 2(3), 492-510. + +.. [ML1936'] MacLane, S. (1936). A construction for absolute values in +polynomial rings. Transactions of the American Mathematical Society, 40(3), +363-395. + +AUTHORS: + +- Julian Rueth (15-04-2013): initial version + +""" +#***************************************************************************** +# Copyright (C) 2013 Julian Rueth +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from developing_valuation import DevelopingValuation, _lift_to_maximal_precision + +from sage.misc.cachefunc import cached_method +from sage.rings.all import infinity + +class AugmentedValuation(DevelopingValuation): + """ + An augmented valuation is a discrete valuation on a polynomial ring. It + extends another discrete valuation `v` by setting the valuation of a + polynomial `f` to the minumum of `v(f_i)i\mu` when writing `f=\sum_i + f_i\mu^i`. + + INPUT: + + - ``v`` -- a discrete valuation on a polynomial ring + + - ``phi`` -- a key polynomial over ``v`` + + - ``mu`` -- a rational number such that ``mu > v(phi)`` or ``infinity`` + + - ``check`` -- a boolean (default: ``True``), whether to check that the + parameters define an augemented value of ``v`` + + EXAMPLES:: + + sage: K. = Qq(4,5) + sage: R. = K[] + sage: v = GaussValuation(R) + sage: v.extension(x, 1/2) # indirect doctest + [ Gauss valuation induced by 2-adic valuation, v((1 + O(2^5))*x) = 1/2 ] + + """ + def __init__(self, v, phi, mu, check=True): + """ + Initialization. + + TESTS:: + + sage: from sage.rings.padics.augmented_valuation import AugmentedValuation + sage: K. = Qq(4,5) + sage: R. = K[] + sage: v = GaussValuation(R) + sage: w = AugmentedValuation(v, x, 1/2) + sage: type(w) + + + """ + if phi.parent() is not v.domain(): + raise ValueError("phi must be in the domain of v") + + if check: + is_key, reason = v.is_key(phi, explain=True) + if not is_key: + raise ValueError(reason) + if mu <= v(phi): + raise ValueError("the value of the key polynomial must strictly increase but `%s` does not exceed `%s`."%(phi, v(phi))) + + DevelopingValuation.__init__(self, v.domain(), phi) + + self._base_valuation = v + self._mu = mu + + if check and not self.residue_field().has_coerce_map_from(v.residue_field()): + raise ValueError("the residue field `%s` does not embed into `%s`"%(v.residue_field(), self.residue_field())) + + def __hash__(self): + return hash(self._base_valuation, self._mu, self._phi) + + def _cache_key(self): + return self._base_valuation, self._mu, self._phi + + def _call_(self, f): + """ + Evaluate this valuation at ``f``. + + INPUT:: + + - ``f`` -- a polynomial in the domain of this valuation + + EXAMPLES:: + + sage: R = Qp(2,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: f = x^2 + 2*x + 3 + + sage: v = v.extension( x^2 + x + 1, 1) + sage: v(f) + 0 + sage: v(f * v.phi()^3 ) + 3 + sage: v(S.zero()) + +Infinity + + """ + if f.parent() is not self.domain(): + raise ValueError("f must be in the domain of the valuation %s but is in %s"%(self.domain(),f.parent())) + + from sage.rings.all import infinity + if f.is_zero(): + return infinity + + if f.degree() < self.phi().degree(): + return self._base_valuation(f) + + # We can slightly optimize the approach of DevelopingValuation._call_ + # We know that self(f) >= self._base_valuation(f) + # as soon as we find a coefficient of f with self._base_valuation(c) == + # self._base_valuation(f) we know that this is the valuation of f + + # this optimization does only pay off for polynomials of large degree: + if f.degree() // self.phi().degree() <= 3: + return DevelopingValuation._call_(self, f) + + ret = infinity + + lower_bound = self._base_valuation(f) + + for v in self.valuations(f): + ret = min(ret, v) + if ret == lower_bound: + return ret + return ret + + @cached_method + def Q(self): + if self._mu is infinity or self.tau().is_zero(): + raise NotImplementedError + + return self.equivalence_unit(self._mu * self.tau()) + + @cached_method + def Q_(self): + ret = self.equivalence_reciprocal(self.Q()) + + assert self.is_equivalence_unit(ret) + # esentially this checks that the reduction of Q'*phi^tau is the + # generator of the residue field + assert self._base_valuation.reduce(self.Q()*ret)(self.residue_field_generator()).is_one() + + return ret + + def equivalence_unit(self, s): + """ + Return an equivalence unit of minimal degree and valuation ``s``. + + INPUT: + + - ``s`` -- a rational number + + OUTPUT: + + An element of the domain of this valuation + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.extension(x^2 + x + u, 1) + sage: w.equivalence_unit(0) + 1 + O(2^5) + sage: w.equivalence_unit(-4) + 2^-4 + O(2) + + Since an equivalence unit is of effective degree zero, `\phi` must not + divide it. Therefore, its valuation is in the value group of the base + valuation:: + + sage: w = v.extension(x, 1/2) + sage: w.equivalence_unit(3/2) + Traceback (most recent call last): + ... + ValueError: v must be an integer + sage: w.equivalence_unit(1) + 2 + O(2^6) + + An equivalence unit might not be integral, even if ``s >= 0``:: + + sage: v = v.extension(x, 3/4) + sage: w = v.extension(x^4 + 8, 5) + sage: w.equivalence_unit(1/2) + (2^-1 + O(2^4))*x^2 + + .. SEEALSO:: + + :meth:`effective_degree`, :meth:`is_equivalence_unit` + + """ + if s < 0 and not self.domain().base_ring().is_field(): + raise NotImplementedError("only implemented for polynomial rings over fields") + + ret = self._base_valuation.element_with_valuation(s) + + assert self.is_equivalence_unit(ret) + assert self(ret) == s + return ret + + def element_with_valuation(self, s): + """ + Create an element of minimal degree and of valuation ``s``. + + INPUT: + + - ``s`` -- a rational number in the value group of this valuation + + OUTPUT: + + An element in the domain of this valuation + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.extension(x^2 + x + u, 1/2) + sage: w.element_with_valuation(0) + 1 + O(2^5) + sage: w.element_with_valuation(1/2) + (1 + O(2^5))*x^2 + (1 + O(2^5))*x + u + O(2^5) + sage: w.element_with_valuation(1) + 2 + O(2^6) + sage: c = w.element_with_valuation(-1/2); c + (2^-1 + O(2^4))*x^2 + (2^-1 + O(2^4))*x + u*2^-1 + O(2^4) + sage: w(c) + -1/2 + sage: w.element_with_valuation(1/3) + Traceback (most recent call last): + ... + ValueError: s must be in the value group of the valuation + + """ + if s not in self.value_group(): + raise ValueError("s must be in the value group of the valuation") + if s < 0 and not self.domain().base_ring().is_field(): + raise NotImplementedError("only implemented for polynomial rings over fields") + + ret = self.domain().one() + while s not in self._base_valuation.value_group(): + ret *= self._phi + s -= self._mu + return ret * self._base_valuation.element_with_valuation(s) + + def _repr_(self): + """ + Return a printable representation of this valuation. + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.extension(x^2 + x + u, 1/2) + sage: w # indirect doctest + [ Gauss valuation induced by 2-adic valuation, v((1 + O(2^5))*x^2 + (1 + O(2^5))*x + u + O(2^5)) = 1/2 ] + + """ + vals = [self] + v = self + while isinstance(v, AugmentedValuation): + v = v._base_valuation + vals.append(v) + vals.reverse() + vals = [ "v(%s) = %s"%(v._phi, v._mu) if isinstance(v, AugmentedValuation) else str(v) for v in vals ] + return "[ %s ]"%", ".join(vals) + + @cached_method + def constant_valuation(self): + """ + Return the restriction of this valuation to the constants of the + polynomial ring. + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.extension(x^2 + x + u, 1/2) + sage: w.constant_valuation() + 2-adic valuation + + """ + return self._base_valuation.constant_valuation() + + @cached_method + def value_group(self): + """ + Return the value group of this valuation. + + OUTPUT: + + Currently, there is no support for additive subgroups of `\QQ`. + Therefore we create this value group as a fractional ideal of `\QQ`. + However, `\QQ` does not support fractional ideals, so we use fractional + ideals of the trivial extensions `\QQ[x]/(x)` + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.extension(x, infinity) + sage: w.value_group() + Fractional ideal (1) + + sage: w = v.extension(x^2 + x + u, 1/2) + sage: w.value_group() + Fractional ideal (1/2) + sage: w = w.extension((x^2 + x + u)^2 + 2, 5/3) + sage: w.value_group() + Fractional ideal (1/6) + + """ + base = self._base_valuation.value_group() + if self._mu is infinity: + return base + return base + base.number_field().ideal(self._mu) + + def valuations(self, f): + """ + Return the valuations of the `f_i\phi^i` in the expansion `f=\sum_i + f_i\phi^i`. + + INPUT: + + - ``f`` -- a polynomial in the domain of this valuation + + OUTPUT: + + An iterator over rational numbers, `[v(f_0), v(f_1\phi), \dots]` + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.extension(x^2 + x + u, 1/2) + sage: list(w.valuations( x^2 + 1 )) + [0, 1/2] + sage: w = w.extension((x^2 + x + u)^2 + 2, 5/3) + sage: list(w.valuations( ((x^2 + x + u)^2 + 2)^3 )) + [+Infinity, +Infinity, +Infinity, 5] + + TESTS:: + + sage: w = v.extension(x, infinity) + sage: list(w.valuations( x^2 + 1 )) + [0, +Infinity, +Infinity] + + """ + if f.parent() is not self.domain(): + raise ValueError("f must be in the domain of this valuation") + + if self._mu is infinity: + num_infty_coefficients = f.degree() // self.phi().degree() + yield self._base_valuation(self.coefficients(f).next()) + for i in range(num_infty_coefficients): + yield infinity + else: + for i,c in enumerate(self.coefficients(f)): + yield self._base_valuation(c) + i*self._mu + + def reduce(self, f): + r""" + Reduce ``f`` module this valuation. + + INPUT: + + - ``f`` -- an element of the domain of this valuation of non-negative + valuation + + OUTPUT: + + an element of the :meth:`residue_ring` of this valuation, the reduction + modulo the ideal of elements of positive valuation + + ALGORITHM: + + We follow the algorithm given in the proof of Theorem 12.1 of [ML1936]: + If ``f`` has positive valuation, the reduction is simply zero. + Otherwise, let `f=\sum f_i\phi^i` be the expansion of `f`, as computed + by :meth:`coefficients`. Since the valuation is zero, the exponents `i` + must all be multiples of :meth:`tau`. + Hence, there is an :meth:`equivalence_unit` `Q` with the same valuation + as `\phi^\tau`. Let `Q'` be its :meth:`reciprocal_inverse`. + Now, rewrite each term `f_i\phi^{i\tau}=(f_iQ^i)(\phi^\tauQ^{-1})^i`; + it turns out that the second factor in this expression is a lift of the + generator of the :meth:`residue_field`. The reduction of the first + factor can be computed recursively. + + EXAMPLES:: + + sage: R. = Qq(4,10) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.reduce(x) + x + sage: v.reduce(S(u)) + u0 + + sage: w = v.extension(x^2 + x + u, 1/2) + sage: w.reduce(S.one()) + 1 + sage: w.reduce(S(2)) + 0 + sage: w.reduce(S(u)) + u0 + sage: w.reduce(x) # this gives the generator of the residue field extension of w over v + u1 + sage: f = (x^2 + x + u)^2 / 2 + sage: w.reduce(f) + x + sage: w.reduce(f + x + 1) + x + u1 + 1 + + sage: w = w.extension((x^2 + x + u)^2 + 2, 5/3) + sage: g = ((x^2 + x + u)^2 + 2)^3 / 2^5 + sage: w.reduce(g) + x + sage: w.reduce(f) + 1 + sage: w(f-1) > 0 # checking the above + True + sage: w.reduce(f * g) + x + sage: w.reduce(f + g) + x + 1 + + REFERENCES: + + .. [ML1936] Mac Lane, S. (1936). A construction for prime ideals as absolute + values of an algebraic field. Duke Mathematical Journal, 2(3), 492-510. + + .. SEEALSO:: + + :meth:`lift` + + """ + if f.parent() is not self.domain(): + raise ValueError("f must be in the domain of the valuation") + if not self.domain().base_ring().is_field(): + raise NotImplementedError("only implemented for polynomial rings over fields") + + if self(f) < 0: + raise ValueError("f must have non-negative valuation") + elif self(f) > 0: + return self.residue_ring().zero() + + # if this extends a trivial valuation, then this is very easy: just + # return the constant coefficient in the phi-adic expansion; everything + # else must have positive valuation + if self._base_valuation.value_group() == 0: + assert self.valuations(f).next() == 0 + if self.value_group() == 0: + raise NotImplementedError + return self.residue_ring()(self.coefficients(f).next())(self.residue_field_generator()) + + CV = zip(self.coefficients(f), self.valuations(f)) + # rewrite as sum of f_i phi^{i tau}, i.e., drop most coefficients + assert not any([v==0 for i,(c,v) in enumerate(CV) if i % self.tau() != 0]) + CV = CV[::self.tau()] + + # replace f_i by f_i Q^{i tau} + vQ = self._mu * self.tau() + CV = [(c*self.Q()**i, v - vQ*i) for i,(c,v) in enumerate(CV)] + assert all([self._base_valuation(c)>=0 for c,v in CV]) + + # recursively reduce the f_i Q^{i tau} + C = [self._base_valuation.reduce(c)(self.residue_field_generator()) for c,v in CV] + + # reduce the Q'^i phi^i + return self.residue_ring()(C) + + def lift(self, F): + """ + Return a polynomial whose :meth:`reduction` is ``F``. + + INPUT: + + - ``F`` -- an element of the :meth:`residue_ring` + + OUTPUT: + + a polynomial in the domain of the valuation with reduction ``F``, monic + if ``F`` is monic + + ALGORITHM: + + Since this is the inverse of :meth:`reduce`, we only have to go backwards + through the algorithm described there. + + EXAMPLES:: + + sage: R. = Qq(4,10) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: y = v.residue_ring().gen() + sage: u0 = v.residue_field().gen() + sage: v.lift(y) + (1 + O(2^10))*x + + sage: w = v.extension(x^2 + x + u, 1/2) + sage: r = w.residue_ring() + sage: y = r.gen() + sage: u1 = w.residue_field().gen() + sage: w.lift(r.one()) + 1 + O(2^10) + sage: w.lift(r.zero()) + 0 + sage: w.lift(r(u0)) + u + O(2^10) + sage: w.lift(r(u1)) + (1 + O(2^10))*x + sage: w.reduce(w.lift(y)) == y + True + sage: w.reduce(w.lift(y + u1 + 1)) == y + u1 + 1 + True + + sage: w = w.extension((x^2 + x + u)^2 + 2, 5/3) + sage: r = w.residue_ring() + sage: y = r.gen() + sage: u2 = w.residue_field().gen() + sage: w.reduce(w.lift(y)) == y + True + sage: w.reduce(w.lift(r.one())) == 1 + True + sage: w.reduce(w.lift(y + 1)) == y + 1 + True + + A more complicated example:: + + sage: v = GaussValuation(S) + sage: v = v.extension(x^2 + x + u, 1) + sage: v = v.extension((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) + + sage: u = v.residue_field().gen() + sage: F = v.residue_ring()(u); F + u2 + sage: f = v.lift(F); f + (2^-1 + O(2^9))*x^2 + (2^-1 + O(2^9))*x + u*2^-1 + O(2^9) + sage: F == v.reduce(f) + True + + """ + if F.parent() is not self.residue_ring(): + raise ValueError("F must be an element of the residue ring of the valuation") + if not self.domain().base_ring().is_field(): + raise NotImplementedError("only implemented for polynomial rings over fields") + if F.is_constant(): + if F.is_zero(): + return self.domain().zero() + if F.is_one(): + return self.domain().one() + + if self.tau().is_zero(): + if not self._mu > 0: + raise NotImplementedError + if not F.is_constant(): + raise ValueError("any reduction is constant in this valuation") + if self.phi() != self.domain().gen(): + raise NotImplementedError + constant = F[0].lift() + assert constant.is_constant() + return self.domain()(constant[0]) + + R0 = self._base_valuation.residue_ring() + + # in the last step of reduce, the f_iQ^i are reduced, and evaluated at + # the generator of the residue field + # here, we undo this: + coeffs = [ R0(c if self.residue_field().is_prime_field() else list(c._vector_())) for c in F.coeffs() ] + coeffs = [ self._base_valuation.lift(c) for c in coeffs ] + # now the coefficients correspond to the expansion with (f_iQ^i)(Q^{-1} phi)^i + + # now we undo the factors of Q^i + coeffs = [ (c*self.Q_()**i).map_coefficients(lambda d:_lift_to_maximal_precision(d)) for i,c in enumerate(coeffs) ] + + RR = self.domain().change_ring(self.domain()) + ret = RR(coeffs)(self.phi()**self.tau()) + ret = ret.map_coefficients(lambda c:_lift_to_maximal_precision(c)) + return ret + + def lift_to_key(self, F): + """ + Lift the irreducible polynomial ``F`` to a key polynomial. + + INPUT: + + - ``F`` -- an irreducible non-constant polynomial in the + :meth:`residue_ring` of this valuation + + OUTPUT: + + A polynomial `f` in the domain of this valuation which is a key + polynomial for this valuation and which, for a suitable equivalence + unit `R`, satifies that the reduction of `Rf` is ``F`` + + ALGORITHM: + + We follow the algorithm described in Theorem 13.1 [ML1936] which, after + a :meth:`lift` of ``F``, essentially shifts the valuations of all terms + in the `\phi`-adic expansion up and then kills the leading coefficient. + + EXAMPLES:: + + sage: R. = Qq(4,10) + sage: S. = R[] + sage: v = GaussValuation(S) + + sage: w = v.extension(x^2 + x + u, 1/2) + sage: y = w.residue_ring().gen() + sage: f = w.lift_to_key(y + 1); f + (1 + O(2^10))*x^4 + (2 + O(2^11))*x^3 + (1 + u*2 + O(2^10))*x^2 + (u*2 + O(2^11))*x + (u + 1) + u*2 + u*2^2 + u*2^3 + u*2^4 + u*2^5 + u*2^6 + u*2^7 + u*2^8 + u*2^9 + O(2^10) + sage: w.is_key(f) + True + + A more complicated example:: + + sage: v = GaussValuation(S) + sage: v = v.extension(x^2 + x + u, 1) + sage: v = v.extension((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) + + sage: u = v.residue_field().gen() + sage: y = v.residue_ring().gen() + sage: f = v.lift_to_key(y^3+y+u) + sage: f.degree() + 12 + sage: v.is_key(f) + True + + """ + if F.parent() is not self.residue_ring(): + raise ValueError("F must be an element of the residue ring of the valuation") + if not self.domain().base_ring().is_field(): + raise NotImplementedError("only implemented for polynomial rings over fields") + if F.is_constant(): + raise ValueError("F must not be constant") + if not F.is_monic(): + raise ValueError("F must be monic") + if not F.is_irreducible(): + raise ValueError("F must be irreducible") + if F == F.parent().gen(): + return self.phi() + + f = self.lift(F) + assert self(f) == 0 + assert self.reduce(f) == F + + f *= self.Q()**F.degree() + CV = zip(self.coefficients(f), self.valuations(f)) + vf = self(f) + CV = [(c,v) if v==vf else (c.parent().zero(),infinity) for c,v in CV] + while CV[-1][1] is infinity: + CV.pop() + + CV[-1] = (CV[-1][0].parent().one(), vf) + ret = self.domain().change_ring(self.domain())([c for c,v in CV])(self.phi()) + ret = ret.map_coefficients(lambda c:_lift_to_maximal_precision(c)) + assert self.is_key(ret) + return ret + + def tau(self): + """ + Return the factor which is needed to turn an element of the value group + of this valuation into an element of the value group of its base + valuation. + + OUTPUT: + + a positive integer + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.extension(x^2 + x + u, 1/2) + sage: w.tau() + 2 + sage: w = w.extension((x^2 + x + u)^2 + 2, 5/3) + sage: w.tau() + 3 + + """ + from sage.rings.all import ZZ + + if self._base_valuation.value_group() == 0: + return ZZ.zero() + + assert self.value_group().numerator() == 1 + assert self._base_valuation.value_group().numerator() == 1 + return ZZ(self.value_group().denominator().gen(0)) // ZZ(self._base_valuation.value_group().denominator().gen(0)) + + @cached_method + def psi(self): + """ + Return the minimal polynomial of the residue field extension of this valuation. + + OUTPUT: + + a polynomial in the residue ring of the base valuation + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.extension(x^2 + x + u, 1/2) + sage: w.psi() + x^2 + x + u0 + sage: w = w.extension((x^2 + x + u)^2 + 2, 5/3) + sage: w.psi() + x + 1 + + """ + R = self._base_valuation.equivalence_unit(-self._base_valuation(self._phi)) + F = self._base_valuation.reduce(self._phi*R) + assert F.is_irreducible() + return F + + @cached_method + def residue_field(self, generator=None): + """ + Return the residue field of this valuation. + + INPUT: + + - ``generator`` -- a string or ``None`` (default: ``None``), the name + of the generator of the residue field extension, if ``None`` a name + is automatically chosen + + OUTPUT: + + a relative extension of the residue field of the base valuation + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.extension(x^2 + x + u, 1/2) + sage: w.residue_field() + Univariate Quotient Polynomial Ring in u1 over Finite Field in u0 of size 2^2 with modulus u1^2 + u1 + u0 + sage: w = w.extension((x^2 + x + u)^2 + 2, 5/3) + sage: w.residue_field() + Univariate Quotient Polynomial Ring in u1 over Finite Field in u0 of size 2^2 with modulus u1^2 + u1 + u0 + + """ + if generator is None: + level = 0 + v = self + while isinstance(v, AugmentedValuation): + level += 1 + v = v._base_valuation + generator = 'u%s'%level + if self.psi().degree() == 1: + ret = self._base_valuation.residue_field() + #ret.gen = "use augmented_valuation.residue_field_generator() instead" # temporary hack to find bugs when .gen() is used - .residue_field_generator() instead + return ret + else: + return self._base_valuation.residue_field().extension(self.psi(), names=generator) + + @cached_method + def residue_field_generator(self): + if self.psi().degree() == 1: + ret = -self.psi()[0] + else: + ret = self.residue_field().gen() + + assert ret.parent() is self.residue_field() + assert self.psi()(ret).is_zero() + return ret + + def is_commensurable_inductive(self): + """ + Return whether this valuation is a commensurable inductive valuation + over the discrete valuation of the base ring of the polynomial ring, as + defined in section 4 of [ML1936]. + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.extension(x^2 + x + u, 1) + sage: w.is_commensurable_inductive() + True + + REFERENCES: + + .. [ML1936] Mac Lane, S. (1936). A construction for prime ideals as absolute + values of an algebraic field. Duke Mathematical Journal, 2(3), 492-510. + + """ + return self._base_valuation.is_commensurable_inductive() + + def E(self): + """ + Return the ramification index of this valuation over its underlying + Gauss valuation. + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.extension(x^2 + x + u, 1) + sage: w.E() + 1 + sage: w = v.extension(x, 1/2) + sage: w.E() + 2 + + """ + return self.tau() * self._base_valuation.E() + + def F(self): + """ + Return the degree of the residue field extension of this valuation + over the Gauss valuation. + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.extension(x^2 + x + u, 1) + sage: w.F() + 2 + sage: w = v.extension(x, 1/2) + sage: w.F() + 1 + + """ + return self.psi().degree() * self._base_valuation.F() + + def change_ring(self, base_ring): + return AugmentedValuation(self._base_valuation.change_ring(base_ring), self.phi().change_ring(base_ring), self._mu) diff --git a/developing_valuation.py b/developing_valuation.py new file mode 100644 index 00000000000..a3ba13b6899 --- /dev/null +++ b/developing_valuation.py @@ -0,0 +1,1052 @@ +""" +Valuations on polynomial rings based on `\phi`-adic expansions + +This file implements a base class for discrete valuations on polynomial rings, +defined by a `\phi`-adic expansion. + +AUTHORS: + +- Julian Rueth (15-04-2013): initial version + +REFERENCES: + +.. [ML1936] Mac Lane, S. (1936). A construction for prime ideals as absolute +values of an algebraic field. Duke Mathematical Journal, 2(3), 492-510. + +.. [ML1936'] MacLane, S. (1936). A construction for absolute values in +polynomial rings. Transactions of the American Mathematical Society, 40(3), +363-395. + +TODO: Check that things work out when v is a pseudo-valuation! + +""" +#***************************************************************************** +# Copyright (C) 2013 Julian Rueth +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from discrete_valuation import DiscreteValuation +from sage.misc.abstract_method import abstract_method + +from sage.misc.cachefunc import cached_method + +def _lift_to_maximal_precision(c): + """ + Lift ``c`` to maximal precision if the parent is not exact. + + EXAMPLES:: + + sage: from sage.rings.padics.developing_valuation import _lift_to_maximal_precision + sage: R = Zp(2,5) + sage: x = R(1,2); x + 1 + O(2^2) + sage: _lift_to_maximal_precision(x) + 1 + O(2^5) + + sage: x = 1 + sage: _lift_to_maximal_precision(x) + 1 + + """ + return c if c.parent().is_exact() else c.lift_to_precision() + +class DevelopingValuation(DiscreteValuation): + """ + An abstract base class for a discrete valuation of polynomials defined over + the polynomial ring ``domain`` by the `\phi`-adic development. + + INPUT: + + - ``domain`` -- a polynomial ring + + - ``phi`` -- a monic element of ``domain`` + + EXAMPLES:: + + sage: from sage.rings.padics.developing_valuation import DevelopingValuation + sage: R = Zp(2,5) + sage: S. = R[] + sage: DevelopingValuation(S, x) + `(1 + O(2^5))*x`-adic valuation of Univariate Polynomial Ring in x over 2-adic Ring with capped relative precision 5 + + """ + def __init__(self, domain, phi): + """ + Initialization. + + TESTS:: + + sage: from sage.rings.padics.developing_valuation import DevelopingValuation + sage: R = Zp(2,5) + sage: S. = R[] + sage: v = DevelopingValuation(S, x) + sage: type(v) + + + """ + if phi.parent() is not domain: + raise ValueError("phi must be in the domain of the valuation") + if phi.is_constant(): + raise ValueError("phi must not be constant") + if not phi.leading_coefficient().is_one(): + raise ValueError("phi must be monic") + + DiscreteValuation.__init__(self, domain) + + self._phi = phi + + def phi(self): + """ + Return the polynomial `\phi`, the key polynomial of this valuation. + + EXAMPLES:: + + sage: R = Zp(2,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.phi() + (1 + O(2^5))*x + + """ + return self._phi + + def effective_degree(self, f): + """ + Return the effective degree of ``f`` with respect to this valuation. + + The effective degree of `f` is the largest `i` such that the valuation + of `f` and the valuation of `f_i\phi^i` in the development `f=\sum_j + f_j\phi^j` coincide. + + INPUT: + + - ``f`` -- a non-zero polynomial in the domain of this valuation + + EXAMPLES:: + + sage: R = Zp(2,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.effective_degree(x) + 1 + sage: v.effective_degree(2*x + 1) + 0 + + """ + # defined on p.497 of [ML1936'] + if f.parent() is not self.domain(): + raise ValueError("f must be in the domain of the valuation") + if f.is_zero(): + raise ValueError("the effective degree is only defined for non-zero polynomials") + + v = self(f) + return [i for i,w in enumerate(self.valuations(f)) if w == v][-1] + + @cached_method + def is_equivalence_irreducible(self, f): + """ + Return whether the polynomial ``f`` is equivalence irreducible, i.e., + whether its :meth:`equivalence_decomposition` is irreducible. + + INPUT: + + - ``f`` -- a polynomial in the domain of this valuation + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.is_equivalence_irreducible(x) + True + sage: v.is_equivalence_irreducible(x^2) + False + sage: v.is_equivalence_irreducible(x^2 + 2) + False + + """ + if f.parent() is not self.domain(): + raise ValueError("f must be in the domain of the valuation") + if f.is_constant(): + raise ValueError("f must not be constant") + + from sage.misc.cachefunc import _cache_key + key = _cache_key(f) + + if self.equivalence_decomposition.is_in_cache(key): + F = self.equivalence_decomposition(f) + return len(F) <= 1 and (len(F) == 0 or F[0][1] == 1) + + if self.is_commensurable_inductive(): + # use the characterization of Theorem 13.1 in [ML1936] + if not f.is_monic(): + raise NotImplementedError("is_equivalence_irreducible() only implemented for monic polynomials") + + # special case: phi is factor of f + if self.valuations(f).next() > self(f): + f = f-self.coefficients(f).next() + assert self.phi().divides(f) + f,_ = f.quo_rem(self.phi()) + return f.is_constant() + + R = self.equivalence_unit(-self(f)) + + # check irreducibility in reduction + F = self.reduce(f*R) + F = F.factor() + if len(F) > 1 or (len(F) and F[0][1] > 1): + return False + + return True + + raise NotImplementedError("is_equivalence_irreducible() only implemented for inductive values") + + def is_equivalence_unit(self, f): + """ + Return whether ``f`` is an equivalence unit, i.e., an element of + :meth:`effective_degree` zero. + + INPUT: + + - ``f`` -- a polynomial in the domain of this valuation + + EXAMPLES:: + + sage: R = Zp(2,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.is_equivalence_unit(x) + False + sage: v.is_equivalence_unit(S.zero()) + False + sage: v.is_equivalence_unit(2*x + 1) + True + + """ + # defined on p.497 of [ML1936'] + if f.parent() is not self.domain(): + raise ValueError("f must be in the domain of the valuation") + + if f.is_zero(): + return False + return self.effective_degree(f) == 0 + + def equivalence_reciprocal(self, f): + """ + Return an equivalence reciprocal of ``f``. + + An equivalence reciprocal of `f` is a polynomial `h` such that `f\cdot + h` is equivalent to 1 modulo this valuation. + + INPUT: + + - ``f`` -- a polynomial in the domain of this valuation which is an + equivalence unit + + EXAMPLES:: + + sage: R = Zp(3,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: f = 3*x + 2 + sage: h = v.equivalence_reciprocal(f); h + 2 + 3 + 3^2 + 3^3 + 3^4 + O(3^5) + sage: v.is_equivalent(f*h, 1) + True + + In an extended valuation over an extension field:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v = v.extension(x^2 + x + u, 1) + sage: f = 2*x + u + sage: h = v.equivalence_reciprocal(f); h + (u + 1) + (u + 1)*2 + 2^2 + u*2^3 + 2^4 + O(2^5) + sage: v.is_equivalent(f*h, 1) + True + + Extending the valuation once more:: + + sage: v = v.extension((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) + sage: h = v.equivalence_reciprocal(f); h + (u + 1) + (u + 1)*2 + (u + 1)*2^2 + (u + 1)*2^3 + u*2^4 + O(2^5) + sage: v.is_equivalent(f*h, 1) + True + + TESTS: + + A case that caused problems at some point:: + + sage: K = Qp(2, 4) + sage: R. = K[] + sage: L. = K.extension(x^4 + 4*x^3 + 6*x^2 + 4*x + 2) + sage: R. = L[] + sage: v = GaussValuation(R) + sage: w = v.extension(t + 1, 5/4) + sage: w = w.extension(t^4 + (a^8 + a^12 + a^14 + a^16 + a^17 + a^19 + a^20 + a^23)*t^3 + (a^6 + a^9 + a^13 + a^15 + a^18 + a^19 + a^21)*t^2 + a^10*t + 1 + a^4 + a^5 + a^8 + a^13 + a^14 + a^15, 17/2) + sage: f = a^-15*t^2 + (a^-11 + a^-9 + a^-6 + a^-5 + a^-3 + a^-2)*t + a^-15 + sage: w.equivalence_reciprocal(f) + (a^10 + a^13 + a^14 + a^17 + a^18 + a^21 + a^23 + O(a^26))*t^2 + a^10 + a^13 + O(a^26) + sage: f = f.parent()([f[0],f[1].add_bigoh(1),f[2]]) + sage: w.equivalence_reciprocal(f) + (a^10 + a^13 + O(a^26))*t^2 + a^10 + a^13 + O(a^26) + + .. SEEALSO:: + + :meth:`is_equivalence_unit` + + """ + # defined on p.497 of [ML1936'] + if f.parent() is not self.domain(): + raise ValueError("f must be in the domain of the valuation") + if not self.is_equivalence_unit(f): + raise ValueError("f must be an equivalence unit") + + e0 = self.coefficients(f).next() + one,g,h = self.phi().xgcd(e0) + assert one.is_one() + + # it might be the case that f*h has non-zero valuation because h has + # insufficient precision, so we must not assert that here but only + # until we lifted to higher precision + + # We do not actually need g*phi + h*e0 = 1, it is only important that + # the RHS is 1 in reduction. + # This allows us to do two things: + # - we may lift h to arbitrary precision + # - we can add anything which times e0 has positive valuation, e.g., we + # may drop coefficients of positive valuation + h = h.map_coefficients(lambda c:_lift_to_maximal_precision(c)) + h = h.parent()([ c if self(e0*c) <= 0 else c.parent().zero() for c in h.coeffs()]) + + assert self(f*h) == 0 + assert self(f*h - 1) > 0 + + return h + + @cached_method + def extension(self, phi, mu, check=True): + """ + Return the inductive valuation which extends this valuation by mapping + ``phi`` to ``mu``. + + INPUT: + + - ``phi`` -- a polynomial in the domain of this valuation; this must be + a key polynomial, see :meth:`is_key` for properties of key + polynomials. + + - ``mu`` -- a rational number, the valuation of ``phi`` in the extended + valuation + + - ``check`` -- whether or not to check the correctness of the + parameters + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v = v.extension(x^2 + x + u, 1) + sage: v = v.extension((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) + sage: v + [ Gauss valuation induced by 2-adic valuation, v((1 + O(2^5))*x^2 + (1 + O(2^5))*x + u + O(2^5)) = 1, v((1 + O(2^5))*x^4 + (2^2 + O(2^6))*x^3 + (1 + (u + 1)*2 + O(2^5))*x^2 + ((u + 1)*2^2 + O(2^6))*x + (u + 1) + (u + 1)*2 + (u + 1)*2^2 + (u + 1)*2^3 + (u + 1)*2^4 + O(2^5)) = 3 ] + sage: v.residue_field() + Univariate Quotient Polynomial Ring in u2 over Univariate Quotient Polynomial Ring in u1 over Finite Field in u0 of size 2^2 with modulus u1^2 + u1 + u0 with modulus u2^2 + u1*u2 + u1 + + .. SEEALSO:: + + :class:`AugmentedValuation` + + """ + from augmented_valuation import AugmentedValuation + return AugmentedValuation(self, phi, mu, check) + + def is_key(self, phi, explain=False): + """ + Return whether ``phi`` is a key polynomial for this valuation. + + A key polynomial must satisfy the following conditions: + + - it must be monic + - it must be equivalence-irreducible (see :meth:`is_equivalence_irreducible`) + - it must be minimal (see :meth:`is_minimal`) + + INPUT: + + - ``phi`` -- a polynomial in the domain of this valuation + + - ``explain`` -- a boolean (default: ``False``), if ``True``, return a + string explaining why ``phi`` is not a key polynomial + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.is_key(x) + True + sage: v.is_key(2*x, explain = True) + (False, 'phi must be monic') + sage: v.is_key(x^2, explain = True) + (False, 'phi must be equivalence irreducible') + + sage: w = v.extension(x, 1) + sage: w.is_key(x + 1, explain = True) + (False, 'phi must be minimal') + + """ + if phi.parent() is not self.domain(): + raise ValueError("phi must be in the domain of the valuation") + + reason = None + + if not phi.is_monic(): + reason = "phi must be monic" + elif not self.is_equivalence_irreducible(phi): + reason = "phi must be equivalence irreducible" + elif not self.is_minimal(phi): + reason = "phi must be minimal" + + if explain: + return reason is None, reason + else: + return reason is None + + @abstract_method + def is_commensurable_inductive(self): + """ + Return whether this valuation is a commensurable inductive valuation + over the discrete valuation of the base ring of the polynomial ring, as + defined in section 4 of [ML1936]. + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.is_commensurable_inductive() + True + sage: w = v.extension(x, 1) + sage: w.is_commensurable_inductive() + True + + REFERENCES: + + .. [ML1936] Mac Lane, S. (1936). A construction for prime ideals as absolute + values of an algebraic field. Duke Mathematical Journal, 2(3), 492-510. + + """ + pass + + def is_minimal(self, f): + """ + Return whether the polynomial ``f`` is minimal with respect to this + valuation, as defined in definition 4.1 of [ML1936]. + + INPUT: + + - ``f`` -- a polynomial in the domain of this valuation + + ALGORITHM: + + When ``f`` :meth:`is_equivalence_irreducible` for this valuation, then + Theorem 9.4 of [ML1936'] describes what to do. TODO: what if not? + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.is_minimal(x + 1) + True + sage: w = v.extension(x, 1) + sage: w.is_minimal(x + 1) + False + + TODO: An example that failed for Stefan: + + sage: K = Qp(2,10) + sage: R. = K[] + sage: vp=pAdicValuation(K) + sage: v0 = GaussValuation(R,vp) + sage: f=x^5+x^4+2 + sage: v1 = v0.extension(x,1/4) + sage: v2 = v1.extension(x^4+2,5/4) + sage: v2.is_minimal(f) + False + + """ + if f.parent() is not self.domain(): + raise ValueError("f must be in the domain of the valuation") + if f.is_constant(): + raise ValueError("f must not be constant") + + if self.is_commensurable_inductive(): + # use the characterization of theorem 9.4 in [ML1936] + if not f.is_monic(): + raise NotImplementedError("is_minimal() only implemented for monic polynomials") + if not self.is_equivalence_irreducible(f): + raise NotImplementedError("is_minimal() only implemented for equivalence-irreducible polynomials") + from gauss_valuation import GaussValuation + if isinstance(self,GaussValuation): + # TODO: what is correct in this case? + return f.is_monic() + return list(self.valuations(f))[-1] == self(f) and list(self.coefficients(f))[-1].is_constant() and list(self.valuations(f))[0] == self(f) and self.tau().divides(len(list(self.coefficients(f)))-1) + + raise NotImplementedError("is_minimal() only implemented for commensurable inductive values") + + @cached_method + def equivalence_decomposition(self, f): + """ + Return an equivalence decomposition of ``f``, i.e., a polynomial + `g(x)=e(x)\prod_i \phi_i(x)` with `e(x)` an equivalence unit (see + :meth:`is_equivalence_unit()`) and the `\phi_i` key polynomials (see + :meth:`is_key`) such that ``f`` :meth:`is_equivalent` to `g`. + + INPUT: + + - ``f`` -- a polynomial in the domain of this valuation + + ALGORITHM: + + We use the algorithm described in Theorem 4.4 of [ML1936']. After + removing all factors `\phi` from a polynomial `f`, there is an + equivalence unit `R` such that `Rf` has valuation zero. Now `Rf` can be + factored as `\prod_i \alpha_i` over the :meth:`residue_field`. Lifting + all `\alpha_i` to key polynomials `\phi_i` gives `Rf=\prod_i R_i f_i` + for suitable equivalence units `R_i` (see :meth:`lift_to_key`). Taking + `R'` an :meth:`equivalence_reciprocal` of `R`, we have `f` equivalent + to `(R'\prod_i R_i)\prod_i\phi_i`. + + EXAMPLES:: + + sage: R. = Qq(4,10) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.equivalence_decomposition(S.zero()) + Traceback (most recent call last): + ... + ValueError: equivalence decomposition of zero is not defined + sage: v.equivalence_decomposition(S.one()) + 1 + O(2^10) + sage: v.equivalence_decomposition(x^2+2) + ((1 + O(2^10))*x)^2 + sage: v.equivalence_decomposition(x^2+1) + ((1 + O(2^10))*x + 1 + O(2^10))^2 + + A polynomial that is an equivalence unit, is returned as the unit part + of a :class:`sage.structure.factorization.Factorization`, leading to a unit + non-minimal degree:: + + sage: w = v.extension(x, 1) + sage: F = w.equivalence_decomposition(x^2+1); F + (1 + O(2^10))*x^2 + 1 + O(2^10) + sage: F.unit() + (1 + O(2^10))*x^2 + 1 + O(2^10) + + However, if the polynomial has a non-unit factor, then the unit might + be replaced by a factor of lower degree:: + + sage: f = x * (x^2 + 1) + sage: F = w.equivalence_decomposition(f); F + (1 + O(2^10))*x + sage: F.unit() + 1 + O(2^10) + + Examples over an iterated unramified extension: + + sage: v = v.extension(x^2 + x + u, 1) + sage: v = v.extension((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) + + sage: v.equivalence_decomposition(x) + (1 + O(2^10))*x + sage: F = v.equivalence_decomposition( v.phi() ) + sage: len(F) + 1 + sage: F = v.equivalence_decomposition( v.phi() * (x^4 + 4*x^3 + (7 + 2*u)*x^2 + (8 + 4*u)*x + 1023 + 3*u) ) + sage: len(F) + 2 + + REFERENCES: + + .. [ML1936'] MacLane, S. (1936). A construction for absolute values in + polynomial rings. Transactions of the American Mathematical Society, 40(3), + 363-395. + + """ + if f.parent() is not self.domain(): + raise ValueError("f must be in the domain of the valuation") + if f.is_zero(): + raise ValueError("equivalence decomposition of zero is not defined") + if any([self.constant_valuation()(c)<0 for c in f.list()]): + raise ValueError("f must be integral") + + from sage.structure.factorization import Factorization + if not self.domain().base_ring().is_field(): + v = self.change_ring(self.domain().base_ring().fraction_field()) + ret = v.equivalence_decomposition(v.domain()(f)) + return Factorization([(g.change_ring(self.domain().base_ring()),e) for g,e in ret], unit=ret.unit().change_ring(self.domain().base_ring())) + + if self.is_equivalence_unit(f): + return Factorization([],unit=f) + + if not self.is_commensurable_inductive(): + raise NotImplementedError("only implemented for inductive valuations") + + f0 = f # used to check correctness of the output + + phi_divides = 0 + while self.valuations(f).next() > self(f): + f = f-self.coefficients(f).next() + assert self.phi().divides(f) + f,_ = f.quo_rem(self.phi()) + phi_divides += 1 + + R = self.equivalence_unit(-self(f)) + R_ = self.equivalence_reciprocal(R) + + F = self.reduce(f*R) + F = F.factor() + from sage.misc.misc import verbose + verbose("%s factors as %s = %s in reduction"%(f0,F.prod(),F),caller_name="equivalence_decomposition") + unit = F.unit() + + F = list(F) + unit = self.lift( self.residue_ring()(unit) ) + + from sage.misc.all import prod + unit *= self.lift(self.residue_ring()(prod([ psi.leading_coefficient()**e for psi,e in F ]))) + F = [(self.lift_to_key(psi/psi.leading_coefficient()),e) for psi,e in F] + + unit *= R_ * prod([self.equivalence_unit(-self(g))**e for g,e in F]) + + if phi_divides: + for i,(g,e) in enumerate(F): + if g == self.phi(): + F[i] = (self.phi(),e+phi_divides) + break + else: + F.append((self.phi(),phi_divides)) + + ret = Factorization(F, unit=unit) + # assert self.is_equivalent(ret.prod(), f0) -- this might fail because of leading zeros + assert self((ret.prod() - f0).map_coefficients(lambda c:_lift_to_maximal_precision(c))) + assert self.is_equivalence_unit(ret.unit()) + return ret + + def minimal_representative(self, f): + """ + Return a minimal representative for ``f``, i.e., a pair `e, a` + such that ``f`` :meth:`is_equivalent`` to `e a`, `e` is + an equivalence unit and `a` is minimal and monic. + + INPUT: + + - ``f`` -- a polynomial in the domain of this valuation + + OUTPUT: + + A factorization which has `e` as its unit and `a` as its unique factor. + + ALGORITHM: + + We use the algorithm described in the proof of Lemma 4.1 of [ML1936']. + In the expansion `f=\sum_i f_i\phi^i` take `e=f_i` for the largest `i` + with `f_i\phi^i` minimal (see :meth:`effective_degree`). + Let `h` be the :meth:`equivalence_reciprocal` of `e` and take `a` given + by the terms of minimal valuation in the expansion of `e f`. + + EXAMPLES:: + + sage: R. = Qq(4,10) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.minimal_representative(x + 2) + (1 + O(2^10))*x + + sage: v = v.extension(x, 1) + sage: v.minimal_representative(x + 2) + (1 + O(2^10))*x + 2 + O(2^11) + sage: f = x^3 + 6*x + 4 + sage: F = v.minimal_representative(f); F + (2 + 2^2 + O(2^11)) * ((1 + O(2^10))*x + 2 + 2^2 + 2^4 + 2^6 + 2^8 + 2^10 + O(2^11)) + sage: v.is_minimal(F[0][0]) + True + sage: v.is_equivalent(F[0][0], f) + True + + REFERENCES: + + .. [ML1936'] MacLane, S. (1936). A construction for absolute values in + polynomial rings. Transactions of the American Mathematical Society, 40(3), + 363-395. + + """ + if f.parent() is not self.domain(): + raise ValueError("f must be in the domain of the valuation") + if f.is_zero(): + raise ValueError("the minimal representative of zero is not defined") + + if not self.is_commensurable_inductive(): + raise NotImplemented("only implemented for inductive valuations") + + f0 = f + e = list(self.coefficients(f))[self.effective_degree(f)] + f *= self.equivalence_reciprocal(e).map_coefficients(lambda c:_lift_to_maximal_precision(c)) + + coeffs = [c if v == self(f) else c.parent().zero() for v,c in zip(self.valuations(f),self.coefficients(f))] + coeffs[self.effective_degree(f0)] = self.domain().base_ring().one() + ret = sum([c*self._phi**i for i,c in enumerate(coeffs)]) + assert self.effective_degree(ret) == self.effective_degree(f0) + assert ret.is_monic(), coeffs + assert self.is_minimal(ret) + from sage.structure.factorization import Factorization + ret = Factorization([(ret,1)],unit=e) + # assert self.is_equivalent(ret.prod(), f0) -- this might fail because of leading zeros + assert self((ret.prod() - f0).map_coefficients(lambda c:_lift_to_maximal_precision(c))) + return ret + + def _normalize_leading_coefficients(self, f): + """ + This method removes leading zero coefficients from ``f`` when + appropriate. + + INPUT: + + - ``f`` -- a polynomial in the domain of this valuation + + OUTPUT: + + ``f`` with leading zero coefficients removed. + + .. NOTE:: + + When ``f`` has leading zero coefficients, one could argue that we + should never strip these but they often arise naturally, e.g., when + when working with expressions as ``g-g`` or ``(g+c)-g``. We strip + such coefficients if they are zero to sufficient precision. To be + precise, if their precision exceeds the valuation of any other + coefficient. + It is not clear that this is the right way to do this. + + EXAMPLES:: + + sage: R = Qp(2,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: g = x + sage: list(v.coefficients(g-g)) # indirect doctest + [] + sage: (g-g).list() + [0, O(2^5)] + sage: f = x*R(0,1) + R(1,2); f + 1 + O(2^2) + sage: list(v.coefficients(f)) # indirect doctest + [1 + O(2^2)] + sage: f = x*R(0,1) + R(2,2); f + 2 + O(2^2) + sage: list(v.coefficients(f)) # indirect doctest + Traceback (most recent call last): + ... + ValueError: f must not have leading zero coefficients + + """ + if len(f.list()) > f.degree()+1: + from sage.rings.all import infinity + # f has leading zero coefficients + m = min([self.constant_valuation()(c) for c in f.list()[f.degree()+1:]]) + if f.is_zero(): + f= f.parent().zero() + elif m is infinity or m > max([self.constant_valuation()(c) for c in f.list()[:f.degree()+1]]): + f= self.domain()(f.list()[:f.degree()+1]) + else: + raise ValueError("f must not have leading zero coefficients") + + return f + + def coefficients(self, f): + """ + Return the `\phi`-adic expansion of ``f``. + + INPUT: + + - ``f`` -- a monic polynomial in the domain of this valuation + + OUTPUT: + + An iterator `[f_0,f_1,\dots]` of polynomials in the domain of this + valuation such that `f=\sum_i f_i\phi^i` + + EXAMPLES:: + + sage: R = Qp(2,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: f = x^2 + 2*x + 3 + sage: list(v.coefficients(f)) # note that these constants are in the polynomial ring + [1 + 2 + O(2^5), 2 + O(2^6), 1 + O(2^5)] + sage: v = v.extension( x^2 + x + 1, 1) + sage: list(v.coefficients(f)) + [(1 + O(2^5))*x + 2 + O(2^5), 1 + O(2^5)] + + """ + if f.parent() is not self.domain(): + raise ValueError("f must be in the domain of the valuation") + f = self._normalize_leading_coefficients(f) + + if self.phi().degree() == 1: + from itertools import imap + return imap(f.parent(), f(self.phi().parent().gen() - self.phi()[0]).coeffs()) + else: + return self.__coefficients(f) + + def __coefficients(self, f): + """ + Helper method for :meth:`coefficients` to create an iterator if `\phi` + is not linear. + + INPUT: + + - ``f`` -- a monic polynomial in the domain of this valuation + + OUTPUT: + + An iterator `[f_0,f_1,\dots]` of polynomials in the domain of this + valuation such that `f=\sum_i f_i\phi^i` + + EXAMPLES:: + + sage: R = Qp(2,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v = v.extension( x^2 + x + 1, 1) + sage: f = x^2 + 2*x + 3 + sage: list(v.coefficients(f)) # indirect doctest + [(1 + O(2^5))*x + 2 + O(2^5), 1 + O(2^5)] + """ + while f.degree() >= 0: + f,r = self.__quo_rem(f) + yield r + + def __quo_rem(self, f): + qr = [ self.__quo_rem_monomial(i) for i in range(f.degree()+1) ] + q = [ f[i]*g for i,(g,_) in enumerate(qr) ] + r = [ f[i]*h for i,(_,h) in enumerate(qr) ] + return sum(q), sum(r) + + @cached_method + def __quo_rem_monomial(self, degree): + f = self.domain().one() << degree + return f.quo_rem(self.phi()) + + def newton_polygon(self, f): + """ + Return the newton polygon the `\phi`-adic development of ``f``. + + INPUT:: + + - ``f`` -- a polynomial in the domain of this valuation + + EXAMPLES:: + + sage: R = Qp(2,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: f = x^2 + 2*x + 3 + sage: v.newton_polygon(f) + Newton Polygon with vertices [(0, 0), (2, 0)] + + sage: v = v.extension( x^2 + x + 1, 1) + sage: v.newton_polygon(f) + Newton Polygon with vertices [(0, 0), (1, 1)] + sage: v.newton_polygon( f * v.phi()^3 ) + Newton Polygon with vertices [(0, +Infinity), (3, 3), (4, 4)] + + .. SEEALSO:: + + :class:`newton_polygon.NewtonPolygon` + + """ + if f.parent() is not self.domain(): + raise ValueError("f must be in the domain of the valuation") + + from newton_polygon import NewtonPolygon + return NewtonPolygon(self.valuations(f)) + + def _call_(self, f): + """ + Evaluate this valuation at ``f``. + + INPUT:: + + - ``f`` -- a polynomial in the domain of this valuation + + EXAMPLES:: + + sage: R = Qp(2,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: f = x^2 + 2*x + 3 + sage: v(f) + 0 + + sage: v = v.extension( x^2 + x + 1, 1) + sage: v(f) + 0 + sage: v(f * v.phi()^3 ) + 3 + sage: v(S.zero()) + +Infinity + + """ + if f.parent() is not self.domain(): + raise ValueError("f must be in the domain of the valuation %s but is in %s"%(self.domain(),f.parent())) + + if f.is_zero(): + from sage.rings.all import infinity + return infinity + + return min(self.valuations(f)) + + def _repr_(self): + """ + Return a printable representation of this valuation. + + EXAMPLES:: + + sage: R = Qp(2,5) + sage: S. = R[] + sage: from sage.rings.padics.developing_valuation import DevelopingValuation + sage: DevelopingValuation(S, x) + `(1 + O(2^5))*x`-adic valuation of Univariate Polynomial Ring in x over 2-adic Field with capped relative precision 5 + + """ + return "`%s`-adic valuation of %s"%(self._phi, self.domain()) + + def residue_ring(self): + """ + Return the residue ring of this valuation, i.e., a polynomial ring over + the :meth:`residue_field` + + EXAMPLES:: + + sage: R = Qp(2,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.residue_ring() + Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) + + """ + return self.domain().change_ring(self.residue_field()) + + def mac_lane_step(self, G, assume_squarefree=False): + r""" + + TESTS:: + + sage: K.=FunctionField(QQ) + sage: S.=K[] + sage: F=y^2-x^2-x^3-3 + sage: v0=GaussValuation(K._ring,pAdicValuation(QQ,3)) + sage: v1=v0.extension(K._ring.gen(),1/3) + sage: from sage.rings.padics.function_field_valuation import RationalFunctionFieldValuation + sage: mu0=RationalFunctionFieldValuation(K,v1) + sage: eta0=GaussValuation(S,mu0) + sage: eta1=eta0.mac_lane_step(F)[0] + sage: eta2=eta1.mac_lane_step(F)[0] + + """ + from sage.misc.misc import verbose + verbose("Expanding %s towards %s"%(self,G),caller_name="mac_lane_step") + assert not G.is_constant() + R = G.parent() + if R is not self.domain(): + raise ValueError("G must be defined over the domain of this valuation") + if not assume_squarefree and not G.is_squarefree(): + raise ValueError("G must be squarefree") + + from sage.rings.all import infinity + + if self(G) is infinity: + raise ValueError("G must not have valuation infinity") + + if self.is_key(G): + return [self.extension(G, infinity)] + + F = self.equivalence_decomposition(G) + assert len(F), "%s factored as a unit %s"%(G,F) + + ret = [] + for phi,e in F: + if G == phi: + # something strange happened here: + # G is not a key (we checked that before) but phi==G is; so phi must have less precision than G + # this can happen if not all coefficients of G have the same precision + # if we drop some precision of G then it will be a key + assert not G.base_ring().is_exact() + prec = min([c.precision_absolute() for c in phi.list()]) + g = G.map_coefficients(lambda c:c.add_bigoh(prec)) + assert self.is_key(g) + return [self.extension(g, infinity)] + + if phi == self.phi(): + # self.phi() always is a key over self but it will not lead to an extension of this valuation + from gauss_valuation import GaussValuation + if isinstance(self,GaussValuation): # unless in the first step + pass + elif len(F)==1: # unless this is the only factor, a terminating case which should give a valuation with v(phi)=infinity + pass + else: + continue + + verbose("Determining the valuation for %s"%phi,level=2,caller_name="mac_lane_step") + w = self.extension(phi, self(phi), check=False) + NP = w.newton_polygon(G).principal_part() + verbose("Newton-Polygon for v(phi)=%s : %s"%(self(phi),NP),level=2,caller_name="mac_lane_step") + # assert len(NP) + if not NP: + q,r = G.quo_rem(phi) + assert not r.is_zero() + phi = phi.coeffs() + for i,c in enumerate(r.coeffs()): + if not c.is_zero(): + v = c.valuation() + # for a correct result we need to add O(pi^v) in degree i + # we try to find the coefficient of phi where such an error can be introduced without losing much absolute precision on phi + best = i + for j in range(i): + if q[j].valuation() < q[best].valuation(): + best = j + # now add the right O() to phi in degree i-best + phi[i-best] = phi[i-best].add_bigoh(c.valuation()-q[best].valuation()) + + phi = G.parent()(phi) + w = self._base_valuation.extension(phi, infinity) + ret.append(w) + continue + + for i in range(len(NP.slopes())): + slope = NP.slopes()[i] + verbose("Slope = %s"%slope,level=3,caller_name="mac_lane_step") + side = NP.sides()[i] + verbose("Left end is %s"%(list(w.coefficients(G))[side[0][0]]),level=3,caller_name="mac_lane_step") + new_mu = self(phi) - slope + base = self + if phi.degree() == base.phi().degree(): + assert new_mu > self(phi) + from gauss_valuation import GaussValuation + if not isinstance(base, GaussValuation): + base = base._base_valuation + + new_leaf = base.extension(phi, new_mu) + assert slope is -infinity or 0 in new_leaf.newton_polygon(G).slopes() + ret.append(new_leaf) + + assert ret + return ret diff --git a/function_field_valuation.py b/function_field_valuation.py new file mode 100644 index 00000000000..4b702687f24 --- /dev/null +++ b/function_field_valuation.py @@ -0,0 +1,189 @@ +from discrete_valuation import DiscreteValuation + +from sage.rings.all import ZZ, infinity + +class RationalFunctionFieldValuation(DiscreteValuation): + def __init__(self, domain, base_valuation): + if base_valuation.domain() is not domain._ring: + raise ValueError + + self._base_valuation = base_valuation + DiscreteValuation.__init__(self, domain) + + def _call_(self, x): + return self._base_valuation(x.numerator()) - self._base_valuation(x.denominator()) + + def __hash__(self): + return hash(self._base_valuation) + hash(self.domain()) + + def _cache_key(self): + return self._base_valuation, self.domain() + + def __cmp__(self, other): + if type(self) != type(other): + return cmp(type(self),type(other)) + return cmp(self._base_valuation,other._base_valuation) + + def shift(self, f, v): + if f.parent() is not self.domain(): + raise ValueError + + if v == 0: + return f + elif v < 0: + return f/self._base_valuation.element_with_valuation(-v) + else: + return f*self._base_valuation.element_with_valuation(v) + + def value_group(self): + return self._base_valuation.value_group() + + def residue_field(self): + from sage.rings.all import FunctionField + return FunctionField(self._base_valuation.residue_field(), names=self.domain().variable_names()) + + def reduce(self, f): + if f.parent() is not self.domain(): + raise ValueError + if self(f) > 0: + return self.residue_field().zero() + if self(f) < 0: + raise ValueError + + base = self._base_valuation + + num = f.numerator() + den = f.denominator() + + assert base(num) == base(den) + shift = base.element_with_valuation(-base(num)) + num *= shift + den *= shift + ret = base.reduce(num) / base.reduce(den) + assert not ret.is_zero() + return self.residue_field()(ret) + + def lift(self, F): + if F.parent() is not self.residue_field(): + raise ValueError + + return self.domain()(self._base_valuation.lift(F.numerator()) / self._base_valuation.lift(F.denominator())) + + def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): + """ + Some difficult cases from Mark van Hoeij:: + + sage: from sage.rings.padics.function_field_valuation import RationalFunctionFieldValuation, TrivialValuation + sage: k = GF(2) + sage: K. = FunctionField(k) + sage: R. = K[] + sage: F = y^21 + x*y^20 + (x^3 + x + 1)*y^18 + (x^3 + 1)*y^17 + (x^4 + x)*y^16 + (x^7 + x^6 + x^3 + x + 1)*y^15 + x^7*y^14 + (x^8 + x^7 + x^6 + x^4 + x^3 + 1)*y^13 + (x^9 + x^8 + x^4 + 1)*y^12 + (x^11 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2)*y^11 + (x^12 + x^9 + x^8 + x^7 + x^5 + x^3 + x + 1)*y^10 + (x^14 + x^13 + x^10 + x^9 + x^8 + x^7 + x^6 + x^3 + x^2 + 1)*y^9 + (x^13 + x^9 + x^8 + x^6 + x^4 + x^3 + x)*y^8 + (x^16 + x^15 + x^13 + x^12 + x^11 + x^7 + x^3 + x)*y^7 + (x^17 + x^16 + x^13 + x^9 + x^8 + x)*y^6 + (x^17 + x^16 + x^12 + x^7 + x^5 + x^2 + x + 1)*y^5 + (x^19 + x^16 + x^15 + x^12 + x^6 + x^5 + x^3 + 1)*y^4 + (x^18 + x^15 + x^12 + x^10 + x^9 + x^7 + x^4 + x)*y^3 + (x^22 + x^21 + x^20 + x^18 + x^13 + x^12 + x^9 + x^8 + x^7 + x^5 + x^4 + x^3)*y^2 + (x^23 + x^22 + x^20 + x^17 + x^15 + x^14 + x^12 + x^9)*y + x^25 + x^23 + x^19 + x^17 + x^15 + x^13 + x^11 + x^5 + sage: x = K._ring.gen() + sage: v0 = RationalFunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).extension(x,1)) + sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assume_squarefree for speed, not tested - factorization over function fields over finite fields is missing + [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x + 1) = 3/2 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y) = 4/3, v(y^3 + x^4) = 13/3 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x) = 2 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y^15 + y^13 + (x + 1)*y^12 + x*y^11 + (x + 1)*y^10 + y^9 + y^8 + x*y^6 + x*y^5 + y^4 + y^3 + y^2 + (x + 1)*y + x + 1) = 2 ]] + sage: v0 = RationalFunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).extension(x+1,1)) + sage: v0.mac_lane_approximants(F, assume_squarefree=True) # not tested, factorization over function fields over finite fields is missing + [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y) = 7/2, v(y^2 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1) = 15/2 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y + x^2 + 1) = 7/2 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y) = 3/4, v(y^4 + x^3 + x^2 + x + 1) = 15/4 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y^13 + x*y^12 + y^10 + (x + 1)*y^9 + (x + 1)*y^8 + x*y^7 + x*y^6 + (x + 1)*y^4 + y^3 + (x + 1)*y^2 + 1) = 2 ]] + sage: v0 = RationalFunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).extension(x^3+x^2+1,1)) + sage: v0.mac_lane_approximants(F, assume_squarefree=True) # not tested, factorization over function fields over finite fields is missing + [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y + x^3 + x^2 + x) = 2, v(y^2 + (x^6 + x^4 + 1)*y + x^14 + x^10 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2 + x) = 5 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^2 + (x^7 + x^5 + x^4 + x^3 + x^2 + x)*y + x^7 + x^5 + x + 1) = 3 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^3 + (x^8 + x^5 + x^4 + x^3 + x + 1)*y^2 + (x^7 + x^6 + x^5)*y + x^8 + x^5 + x^4 + x^3 + 1) = 3 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^3 + (x^8 + x^4 + x^3 + x + 1)*y^2 + (x^4 + x^3 + 1)*y + x^8 + x^7 + x^4 + x + 1) = 3 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^4 + (x^8 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1)*y^3 + (x^8 + x^5 + x^4 + x^3 + x^2 + x + 1)*y^2 + (x^8 + x^7 + x^6 + x^5 + x^3 + x^2 + 1)*y + x^8 + x^7 + x^6 + x^5 + x^3 + 1) = 3 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^7 + (x^8 + x^5 + x^4 + x)*y^6 + (x^7 + 1)*y^5 + (x^4 + x^2)*y^4 + (x^8 + x^3 + x + 1)*y^3 + (x^7 + x^6 + x^4 + x^2 + x + 1)*y^2 + (x^8 + x^7 + x^5 + x^3 + 1)*y + x^7 + x^6 + x^5 + x^4 + x^3 + x^2) = 3 ]] + + Some cases with trivial residue field extensions:: + + sage: K. = FunctionField(QQ) + sage: S. = K[] + sage: F = y^2 - x^2 - x^3 - 3 + sage: v0 = GaussValuation(K._ring,pAdicValuation(QQ,3)) + sage: v1 = v0.extension(K._ring.gen(),1/3) + sage: mu0 = RationalFunctionFieldValuation(K, v1) + sage: mu0.mac_lane_approximants(F) + [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + x) = 2/3 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + 2*x) = 2/3 ]] + + """ + # TODO: move this to discrete valuation + R = G.parent() + if R.base_ring() is not self.domain(): + raise ValueError("G must be defined over the domain of this valuation") + if not assume_squarefree and not G.is_squarefree(): + raise ValueError("G must be squarefree") + + from sage.rings.all import infinity + from gauss_valuation import GaussValuation + + leaves = [ GaussValuation(R, self)] + while True: + ef = [ v.E()*v.F() for v in leaves] + if sum(ef) == G.degree(): + if precision_cap is None or all([v(v.phi())>precision_cap for v in leaves]): + return leaves + + expandables = [] + new_leaves = [] + for v in leaves: + if v(G) is infinity: + new_leaves.append(v) + else: + expandables.append(v) + leaves = new_leaves + + if not expandables: + return leaves + + for v in expandables: + leaves.extend(v.mac_lane_step(G)) + + def _repr_(self): + return "Valuation on rational function field induced by %s"%self._base_valuation + +from sage.structure.unique_representation import UniqueRepresentation +class TrivialValuation(UniqueRepresentation, DiscreteValuation): + def __init__(self, domain): + DiscreteValuation.__init__(self, domain) + + def _call_(self, x): + if x.is_zero(): + return infinity + else: + return ZZ.zero() + + def shift(self, x, v): + if x.parent() is not self.domain(): + raise ValueError + + if v == 0: + return x + else: + raise ValueError + + def value_group(self): + return self._value_group(0) + + def residue_field(self): + if self.domain().is_field(): + return self.domain() + else: + raise NotImplementedError("residue ring is not a field") + + def reduce(self, x): + if x.parent() is not self.domain(): + raise ValueError + + return x + + lift = reduce + + def _repr_(self): + return "Trivial valuation" diff --git a/gauss_valuation.py b/gauss_valuation.py new file mode 100644 index 00000000000..4190a3b79ae --- /dev/null +++ b/gauss_valuation.py @@ -0,0 +1,490 @@ +""" +Gauss valuations on polynomial rings + +This file implements Gauss valuations for polynomial rings, i.e. valuations +which assign to a polynomial the minimal valuation of its coefficients. + +AUTHORS: + +- Julian Rueth (15-04-2013): initial version + +EXAMPLES:: + + sage: R. = QQ[] + sage: v0 = pAdicValuation(QQ, 2) + sage: v = GaussValuation(R, v0); v + Gauss valuation induced by 2-adic valuation + sage: v(2*x + 2) + 1 + +Gauss valuations can also be defined iteratively based on valuations over +polynomial rings:: + + sage: v = v.extension(x, 1/4); v + [ Gauss valuation induced by 2-adic valuation, v(x) = 1/4 ] + sage: v = v.extension(x^4+2*x^3+2*x^2+2*x+2, 4/3); v + [ Gauss valuation induced by 2-adic valuation, v(x) = 1/4, v(x^4 + 2*x^3 + 2*x^2 + 2*x + 2) = 4/3 ] + sage: S. = R[] + sage: w = GaussValuation(S, v); w + Gauss valuation induced by [ Gauss valuation induced by 2-adic valuation, v(x) = 1/4, v(x^4 + 2*x^3 + 2*x^2 + 2*x + 2) = 4/3 ] + sage: w(2*T + 1) + 0 + +""" +#***************************************************************************** +# Copyright (C) 2013 Julian Rueth +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from developing_valuation import DevelopingValuation + +from sage.misc.cachefunc import cached_method +from sage.structure.unique_representation import UniqueRepresentation + +class GaussValuation(UniqueRepresentation, DevelopingValuation): + """ + A Gauss valuation on a polynomial ring ``domain``. + + INPUT: + + - ``domain`` -- a polynomial ring over a valued ring `R` + + - ``v`` -- a discrete valuation on `R` or ``None`` (default: ``None``), if + ``None`` and the ring comes with a natural valuation (such as a `p`-adic + ring), then this one is used + + EXAMPLES:: + + sage: R = Zp(3,5) + sage: S. = R[] + sage: v0 = pAdicValuation(R) + sage: v = GaussValuation(S, v0); v + Gauss valuation induced by 3-adic valuation + + sage: S. = QQ[] + sage: v = GaussValuation(S, pAdicValuation(QQ, 5)); v + Gauss valuation induced by 5-adic valuation + + """ + def __init__(self, domain, v=None): + """ + Initialization. + + EXAMPLES:: + + sage: S. = QQ[] + sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) + sage: type(v) + + + """ + if not domain.ngens() == 1: + raise NotImplementedError("only implemented for univariate rings") + + from sage.rings.padics.padic_generic import pAdicGeneric + from padic_valuation import pAdicValuation + if v is None: + v = pAdicValuation(domain.base_ring()) + if not v.domain() is domain.base_ring(): + raise ValueError("the domain of v must be the base ring of domain") + + DevelopingValuation.__init__(self, domain, domain.gen()) + + self._base_valuation = v + + def __hash__(self): + return hash(self.domain()) + + def value_group(self): + """ + Return the value group of this valuation. + + OUTPUT: + + Currently, there is no support for additive subgroups of `\QQ`. + Therefore we create this value group as a fractional ideal of `\QQ`. + However, `\QQ` does not support fractional ideals, so we use fractional + ideals of the trivial extensions `\QQ[x]/(x)` + + EXAMPLES:: + + sage: S. = QQ[] + sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) + sage: v.value_group() + Fractional ideal (1) + + """ + return self._base_valuation.value_group() + + def _repr_(self): + """ + Return a printable representation of this valuation. + + EXAMPLES:: + + sage: S. = QQ[] + sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) + sage: v + Gauss valuation induced by 5-adic valuation + + """ + return "Gauss valuation induced by %s"%self._base_valuation + + @cached_method + def uniformizer(self): + """ + Return a uniformizer of this valuation, i.e., a uniformizer of the + valuation of the base ring. + + EXAMPLES:: + + sage: S. = QQ[] + sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) + sage: v.uniformizer() + 5 + + """ + return self.domain()(self._base_valuation.uniformizer()) + + def shift(self, f, s): + """ + Multiply ``f`` by the ``s``th power of the uniformizer of this + valuation. + + INPUT: + + - ``f`` -- a polynomial in the domain of this valuation + + - ``s`` -- an integer + + EXAMPLES:: + + sage: S. = QQ[] + sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) + sage: v.shift(x, -2) + 1/25*x + + It is an error to perform a shift if the result is not in the domain of + the valuation anymore:: + + sage: S. = Zp(2,5)[] + sage: v = GaussValuation(S) + sage: f = v.shift(x, 2); f + (2^2 + O(2^7))*x + sage: f.parent() is S + True + sage: f = v.shift(x, -2) + Traceback (most recent call last): + ... + ValueError: -s must not exceed the valuation of f + + Of course, the above example works over a field:: + + sage: S. = Qp(2,5)[] + sage: v = GaussValuation(S) + sage: f = v.shift(x, -2); f + (2^-2 + O(2^3))*x + + """ + if f.parent() is not self.domain(): + raise ValueError("f must be in the domain of this valuation") + if -s > self(f) and self.domain().base_ring() is not self.domain().base_ring().fraction_field(): + raise ValueError("-s must not exceed the valuation of f") + + return f.map_coefficients(lambda c:self._base_valuation.shift(c, s)) + + def valuations(self, f): + """ + Return the valuations of the `f_i\phi^i` in the expansion `f=\sum f_i\phi^i`. + + INPUT: + + - ``f`` -- a polynomial in the domain of this valuation + + OUTPUT: + + A list of rational numbers, the valuations of `f_0, f_1\phi, \dots` + + EXAMPLES:: + + sage: R = Qp(2,5) + sage: S. = R[] + sage: v = GaussValuation(S, pAdicValuation(R)) + sage: f = x^2 + 2*x + 16 + sage: list(v.valuations(f)) + [4, 1, 0] + + TESTS: + + The treatment of (inexact) zero values is slightly complicated, see + :meth:`DevelopingValuation._normalize_leading_coefficients`:: + + sage: list(v.valuations(S.zero())) + [] + sage: list(v.valuations(S([R(0,1),R(0,2)]))) + [] + sage: list(v.valuations(S([R(0,2),R(0,1)]))) + [] + sage: list(v.valuations(S([R(1,1),R(0,1)]))) + [0] + sage: list(v.valuations(S([R(4,3),R(0,1)]))) + Traceback (most recent call last): + ... + ValueError: f must not have leading zero coefficients + + """ + if f.parent() is not self.domain(): + raise ValueError("f must be in the domain of this valuation") + + for c in self.coefficients(f): + yield self._base_valuation(self.domain().base_ring()(c)) + + @cached_method + def residue_field(self): + """ + Return the residue field of this valuation, i.e., the base ring of the + residue polynomial ring of this valuation. + + EXAMPLES:: + + sage: S. = Qp(2,5)[] + sage: v = GaussValuation(S) + sage: v.residue_field() + Finite Field of size 2 + + """ + return self._base_valuation.residue_field() + + @cached_method + def residue_ring(self): + """ + Return the residue ring of this valuation. + + EXAMPLES:: + + sage: S. = Qp(2,5)[] + sage: v = GaussValuation(S) + sage: v.residue_ring() + Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) + + """ + return self.domain().change_ring(self.residue_field()) + + def reduce(self, f): + """ + Return the reduction of ``f`` modulo this valuation. + + INPUT: + + - ``f`` -- an integral element of the domain of this valuation + + OUTPUT: + + A polynomial over the residue field of this valuation + + EXAMPLES:: + + sage: S. = Qp(2,5)[] + sage: v = GaussValuation(S) + sage: f = x^2 + 2*x + 16 + sage: v.reduce(f) + x^2 + + The reduction is only defined for integral elements:: + + sage: f = x^2/2 + sage: v.reduce(f) + Traceback (most recent call last): + ... + ValueError: reduction not defined for non-integral elements + + .. SEEALSO:: + + :meth: `lift` + + """ + if f.parent() is not self.domain(): + raise ValueError("f must be in the domain of this valuation") + if not all([v>=0 for v in self.valuations(f)]): + raise ValueError("reduction not defined for non-integral elements") + + return f.map_coefficients(lambda c:self._base_valuation.reduce(c), self.residue_field()) + + def lift(self, reduction): + """ + Return a lift of ``reduction``. + + INPUT:: + + - ``reduction`` -- a polynomial over the residue ring of this valuation + + OUTPUT: + + a (possibly non-monic) polynomial in the domain of this valuation which + reduces to ``reduction`` + + EXAMPLES:: + + sage: S. = Qp(3,5)[] + sage: v = GaussValuation(S) + sage: f = x^2 + 2*x + 16 + sage: F = v.reduce(f); F + x^2 + 2*x + 1 + sage: g = v.lift(F); g + (1 + O(3^5))*x^2 + (2 + O(3^5))*x + 1 + O(3^5) + sage: v.is_equivalent(f,g) + True + + .. SEEALSO:: + + :meth:`reduce` + + """ + if reduction.parent() is not self.residue_ring(): + raise ValueError("f must be in the residue ring of this valuation") + + return reduction.map_coefficients(lambda c:self._base_valuation.lift(c), self._base_valuation.domain()) + + def lift_to_key(self, F): + """ + Lift the irreducible polynomial ``F`` to a key polynomial. + + INPUT: + + - ``F`` -- an irreducible non-constant polynomial in + :meth:`residue_ring` of this valuation + + OUTPUT: + + A polynomial `f` in the domain of this valuation which is a key + polynomial for this valuation and which, for a suitable equivalence + unit `R`, satifies that the reduction of `Rf` is ``F`` + + EXAMPLES:: + + sage: R. = Qq(4,10) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: y = v.residue_ring().gen() + sage: u0 = v.residue_field().gen() + sage: f = v.lift_to_key(y^2 + y + u0); f + (1 + O(2^10))*x^2 + (1 + O(2^10))*x + u + O(2^10) + + """ + if F.parent() is not self.residue_ring(): + raise ValueError("F must be an element of the residue ring of the valuation") + if F.is_constant(): + raise ValueError("F must not be constant") + if not F.is_monic(): + raise ValueError("F must be monic") + if not F.is_irreducible(): + raise ValueError("F must be irreducible") + + return self.lift(F) + + def constant_valuation(self): + """ + Return the restriction of this valuations to the constants of the + polynomial ring. + + EXAMPLES:: + + sage: S. = Qp(3,5)[] + sage: v = GaussValuation(S) + sage: v.constant_valuation() + 3-adic valuation + + """ + return self._base_valuation + + def equivalence_unit(self, s): + """ + Return an equivalence unit of valuation ``s``. + + INPUT: + + - ``s`` -- an integer + + EXAMPLES:: + + sage: S. = Qp(3,5)[] + sage: v = GaussValuation(S) + sage: v.equivalence_unit(2) + 3^2 + O(3^7) + sage: v.equivalence_unit(-2) + 3^-2 + O(3^3) + + """ + ret = self._base_valuation.domain().one() + ret = self._base_valuation.shift(ret, s) + return self.domain()(ret) + + element_with_valuation = equivalence_unit + + def is_commensurable_inductive(self): + """ + Return whether this valuation is a commensurable inductive valuation + over the discrete valuation of the base ring of the polynomial ring, as + defined in section 4 of [ML1936]. + + OUTPUT: + + Returns ``True`` since a Gauss valuation always is. + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.is_commensurable_inductive() + True + + REFERENCES: + + .. [ML1936] Mac Lane, S. (1936). A construction for prime ideals as absolute + values of an algebraic field. Duke Mathematical Journal, 2(3), 492-510. + + """ + return True + + def E(self): + """ + Return the ramification index of this valuation over its underlying + Gauss valuation, i.e., 1. + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.E() + 1 + + """ + from sage.rings.all import ZZ + return ZZ.one() + + def F(self): + """ + Return the degree of the residue field extension of this valuation + over the Gauss valuation, i.e., 1. + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.F() + 1 + + """ + from sage.rings.all import ZZ + return ZZ.one() + + def change_ring(self, base_ring): + base_valuation = self._base_valuation.change_ring(base_ring) + return GaussValuation(self.domain().change_ring(base_ring), base_valuation) diff --git a/padic_valuation.py b/padic_valuation.py new file mode 100644 index 00000000000..5f6fd606886 --- /dev/null +++ b/padic_valuation.py @@ -0,0 +1,777 @@ +""" +The discrete valuation of a `p`-adic ring + +This file makes it possible to use `p`-adics, integers, and rationals in the +general discrete valuation framework. + +AUTHORS: + +- Julian Rueth (2013-03-16): initial version + +""" +#***************************************************************************** +# Copyright (C) 2013 Julian Rueth +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from discrete_valuation import DiscreteValuation +from sage.structure.unique_representation import UniqueRepresentation +from sage.misc.cachefunc import cached_method + +def pAdicValuation(R, prime = None): + """ + Create a ``prime``-adic valuation on ``R``. + + INPUT: + + - ``R`` -- a ring + + - ``prime`` -- a prime or ``None`` (default: ``None``), if ``None``, tries + to automatically determine the appropriate prime for ``R`` + + EXAMPLES:: + + sage: pAdicValuation(ZZ, 3) + 3-adic valuation + + ``prime`` may be ``None`` for `p`-adic rings:: + + sage: pAdicValuation(Qp(2)) + 2-adic valuation + sage: pAdicValuation(ZZ) + Traceback (most recent call last): + ... + ValueError: prime must be specified for this ring + + """ + from sage.rings.all import ZZ, QQ + from sage.rings.number_field.number_field import is_NumberField + from sage.rings.padics.padic_generic import pAdicGeneric + if R is ZZ or R is QQ: + if prime is None: + raise ValueError("prime must be specified for this ring") + return pAdicValuation_int(R, prime) + elif is_NumberField(R): + if prime is None: + raise ValueError("prime must be specified for this ring") + prime = R.ideal(prime) + if not prime.is_prime(): + raise ValueError("prime must be prime") + return pAdicValuation_number_field(R, prime) + elif isinstance(R, pAdicGeneric): + if prime is None: + prime = R.prime() + if prime != R.prime(): + raise NotImplementedError("p-adic valuation not implemented for this ring") + return pAdicValuation_padic(R) + else: + raise NotImplementedError("p-adic valuation not implemented for this ring") + +class pAdicValuation_base(UniqueRepresentation, DiscreteValuation): + """ + Common base class for `p`-adic valuations on integral domains. + + INPUT: + + - ``ring`` -- an integral domain whose elements provide a method + ``valuation`` which takes ``prime`` as a parameter + + - ``prime`` -- a prime + + - ``check`` -- a boolean (default: ``True``) whether to perform checks on + the parameters + + EXAMPLES:: + + sage: pAdicValuation(ZZ, 3) + 3-adic valuation + + sage: pAdicValuation(QQ, 5) + 5-adic valuation + + For `p`-adic rings, ``prime`` has to match the prime of the ring. + + sage: v = pAdicValuation(Zp(3), 2); v + Traceback (most recent call last): + ... + NotImplementedError: p-adic valuation not implemented for this ring + + TESTS:: + + sage: TestSuite(pAdicValuation(ZZ, 3)).run(skip="_test_category") + sage: TestSuite(pAdicValuation(QQ, 5)).run(skip="_test_category") + sage: TestSuite(pAdicValuation(Zp(5), 5)).run(skip="_test_category") + + """ + def __init__(self, ring, prime): + """ + Initialization. + + TESTS:: + + sage: type(pAdicValuation(ZZ, 3)) + + + """ + DiscreteValuation.__init__(self, ring) + self._prime = prime + + def prime(self): + """ + Return the prime `p` of this `p`-adic valuation. + + EXAMPLES:: + + sage: pAdicValuation(ZZ, 3).prime() + 3 + + """ + return self._prime + + def value_group(self): + return self._value_group(1) + + def _repr_(self): + """ + Return a printable representation of this valuation. + + EXAMPLES:: + + sage: pAdicValuation(ZZ, 3)._repr_() + '3-adic valuation' + + """ + return "%s-adic valuation"%(self.prime()) + + def _call_(self, x): + """ + Evaluate this valuation at ``x``. + + INPUT:: + + - ``x`` -- an element in the domain of this valuation + + EXAMPLES:: + + sage: pAdicValuation(ZZ, 3)(9) + 2 + + """ + if x.parent() is not self.domain(): + raise ValueError("x must be in the domain of the valuation") + + return x.valuation(self.prime()) + + def reduce(self, x): + """ + Reduce ``x`` modulo the ideal of elements of positive valuation. + + INPUT: + + - ``x`` -- an element in the domain of this valuation + + OUTPUT: + + An element of the :meth:`residue_field`. + + EXAMPLES:: + + sage: pAdicValuation(ZZ, 3).reduce(4) + 1 + + """ + if x.parent() is not self.domain(): + raise ValueError("x must be in the domain of the valuation") + + return self.residue_field()(x) + + def lift(self, x): + """ + Lift ``x`` from the residue field to the domain of this valuation. + + INPUT: + + - ``x`` -- an element of the :meth:`residue_field` + + EXAMPLES:: + + sage: v = pAdicValuation(ZZ, 3) + sage: xbar = v.reduce(4) + sage: v.lift(xbar) + 1 + + """ + if x.parent() is not self.residue_field(): + raise ValueError("x must be in the residue field of the valuation") + + return self.domain()(x) + + def uniformizer(self): + """ + Return a uniformizer of this valuation, i.e., `p` as an element of the domain. + + EXAMPLES:: + + sage: v = pAdicValuation(ZZ, 3) + sage: v.uniformizer() + 3 + + """ + return self.domain()(self._prime) + + def residue_field(self): + """ + Return the residue field of the ring of integers of this valuation. + + EXAMPLES:: + + sage: v = pAdicValuation(ZZ, 3) + sage: v.residue_field() + Finite Field of size 3 + + """ + from sage.rings.all import GF + return GF(self._prime,names=('u',)) + + def shift(self, c, v): + """ + Multiply ``c`` by a ``v``th power of the uniformizer. + + INPUT: + + - ``c`` -- an element of the domain of this valuation + + - ``v`` -- an integer + + OUTPUT: + + If the resulting element has negative valation, then it will be brought + to the fraction field, otherwise it is an element of the domain. + + EXAMPLES:: + + sage: v = pAdicValuation(ZZ, 3) + sage: v.shift(2,3) + 54 + sage: v.shift(2,-3) + 2/27 + + """ + from sage.rings.all import ZZ + if c.parent() is not self.domain(): + raise ValueError("c must be in the domain of the valuation") + if not v in ZZ: + raise ValueError("v must be an integer") + v = ZZ(v) + + c = self.domain().fraction_field()(c) + ret = c*self.uniformizer()**v + + if self(c) >= -v: ret = self.domain()(ret) + return ret + + def is_unramified(self, G, include_steps=False, assume_squarefree=False): + """ + Return whether ``G`` defines an unramified extension. + + NOTE: It is not enough to check whether ``G`` is irreducible in + reduction for this. + + """ + R = G.parent() + if R.base_ring() is not self.domain(): + raise ValueError("G must be defined over the domain of this valuation") + if not assume_squarefree and not G.is_squarefree(): + raise ValueError("G must be squarefree") + + from sage.rings.all import infinity + from gauss_valuation import GaussValuation + + steps = [ GaussValuation(R, self) ] + while True: + v = steps[-1] + if v.E() > 1: + ret = False + break + if v.F() == G.degree(): + ret = True + break + + assert v(G) is not infinity + if v.is_key(G): + ret = True + break + + next = v.mac_lane_step(G, assume_squarefree=True) + if len(next)>1: + ret = False + break + steps.append(next[0]) + + if include_steps: + return ret, steps + else: + return ret + + def is_totally_ramified(self, G, include_steps=False, assume_squarefree=False): + """ + Return whether ``G`` defines a totally ramified extension. + + INPUT: + + - ``G`` -- a monic polynomial over the domain of this valuation + + - ``include_steps`` -- a boolean (default: ``False``), include the the + valuations produced during the process of checking whether ``G`` is + totally ramified in the return value + + - ``assume_squarefree`` -- a boolean (default: ``False``), whether to + assume the input to be squarefree + + ALGORITHM: + + This is simplified version of :meth:`mac_lane_approximants`. + + EXAMPLES:: + + sage: k=Qp(5,4) + sage: v = pAdicValuation(k) + sage: R.=k[] + sage: G = x^2 + 1 + sage: v.is_totally_ramified(G) + False + sage: G = x + 1 + sage: v.is_totally_ramified(G) + True + sage: G = x^2 + 2 + sage: v.is_totally_ramified(G) + False + sage: G = x^2 + 5 + sage: v.is_totally_ramified(G) + True + sage: v.is_totally_ramified(G, include_steps=True) + (True, [Gauss valuation induced by 5-adic valuation, [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x) = 1/2 ]]) + + """ + R = G.parent() + if R.base_ring() is not self.domain(): + raise ValueError("G must be defined over the domain of this valuation") + if not assume_squarefree and not G.is_squarefree(): + raise ValueError("G must be squarefree") + + from sage.rings.all import infinity + from gauss_valuation import GaussValuation + + steps = [ GaussValuation(R, self) ] + while True: + v = steps[-1] + if v.F() > 1: + ret = False + break + if v.E() == G.degree(): + ret = True + break + + assert v(G) is not infinity + if v.is_key(G): + ret = False + break + + next = v.mac_lane_step(G, assume_squarefree=True) + if len(next)>1: + ret = False + break + steps.append(next[0]) + + if include_steps: + return ret, steps + else: + return ret + + def montes_factorization(self, G, assume_squarefree=False): + """ + Factor ``G`` by computing :meth:`mac_lane_approximants` to arbitrary + precision. + + INPUT: + + - ``G`` -- a monic polynomial over the domain of this valuation + + - ``assume_squarefree`` -- a boolean (default: ``False``), whether to + assume the input to be squarefree + + EXAMPLES:: + + sage: k=Qp(5,4) + sage: v = pAdicValuation(k) + sage: R.=k[] + sage: G = x^2 + 1 + sage: v.montes_factorization(G) # long time + ((1 + O(5^4))*x + 2 + 5 + 2*5^2 + 5^3 + O(5^4)) * ((1 + O(5^4))*x + 3 + 3*5 + 2*5^2 + 3*5^3 + O(5^4)) + + REFERENCES: + + .. [GMN2008] Jordi Guardia, Jesus Montes, Enric Nart (2008). Newton + polygons of higher order in algebraic number theory. arXiv:0807.2620 + [math.NT] + + """ + R = G.parent() + if R.base_ring() is not self.domain(): + raise ValueError("G must be defined over the domain of this valuation") + if not G.is_monic(): + raise ValueError("G must be monic") + if not all([self(c)>=0 for c in G.coefficients()]): + raise ValueError("G must be integral") + + from sage.rings.all import infinity + # W contains approximate factors of G + W = self.mac_lane_approximants(G, precision_cap=infinity,assume_squarefree=assume_squarefree) + ret = [w.phi() for w in W] + + # the polynomials in ret give "a" factorization; there is no guarantee + # that every factorization is of this form... TODO + from sage.structure.factorization import Factorization + return Factorization([ (g,1) for g in ret ], simplify=False) + + def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): + """ + Compute the extensions `w` of this valuation to a polynomial ring over + the domain which have `w(G)=\infty`. + + INPUT: + + - ``G`` -- a monic polynomial over the domain of this valuation + + - ``precision_cap`` -- a rational, ``infinity`` or ``None`` (default: + ``None``), stop computation as soon as no new branches can show up in + the tree of valuations and the valuation of the key polynomials is at + least ``precision_cap`` (if not ``None``). + + - ``assume_squarefree`` -- a boolean (default: ``False``), whether to + assume the input to be squarefree + + EXAMPLES:: + + sage: k=Qp(2,10) + sage: v = pAdicValuation(k) + + sage: R.=k[] + sage: G = x + sage: v.mac_lane_approximants(G) + [Gauss valuation induced by 2-adic valuation] + sage: v.mac_lane_approximants(G, precision_cap = infinity) + [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = +Infinity ]] + + sage: G = x^2 + 1 + sage: v.mac_lane_approximants(G) + [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x + 1 + O(2^10)) = 1/2 ]] + sage: v.mac_lane_approximants(G, precision_cap = infinity) + [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x + 1 + O(2^10)) = 1/2, v((1 + O(2^10))*x^2 + 1 + O(2^10)) = +Infinity ]] + + sage: G = x^4 + 2*x^3 + 2*x^2 - 2*x + 2 + sage: v.mac_lane_approximants(G) + [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = 1/4 ]] + sage: v.mac_lane_approximants(G,infinity) + [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = 1/4, v((1 + O(2^10))*x^4 + (2 + O(2^11))*x^3 + (2 + O(2^11))*x^2 + (2 + 2^2 + 2^3 + 2^4 + 2^5 + 2^6 + 2^7 + 2^8 + 2^9 + 2^10 + O(2^11))*x + 2 + O(2^11)) = +Infinity ]] + + The factorization of primes in the Gaussian integers can be read off + the Mac Lane approximants:: + + sage: v0 = pAdicValuation(QQ, 2) + sage: R. = QQ[] + sage: G = x^2 + 1 + sage: v0.mac_lane_approximants(G) + [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/2 ]] + + sage: v0 = pAdicValuation(QQ, 3) + sage: v0.mac_lane_approximants(G) + [[ Gauss valuation induced by 3-adic valuation, v(x^2 + 1) = +Infinity ]] + + sage: v0 = pAdicValuation(QQ, 5) + sage: v0.mac_lane_approximants(G) + [[ Gauss valuation induced by 5-adic valuation, v(x + 2) = 1 ], [ Gauss valuation induced by 5-adic valuation, v(x + 3) = 1 ]] + sage: v0.mac_lane_approximants(G, precision_cap = 10) # long time + [[ Gauss valuation induced by 5-adic valuation, v(x + 25670807) = 11 ], [ Gauss valuation induced by 5-adic valuation, v(x + 23157318) = 11 ]] + + The same example over the 5-adic numbers. In the quadratic extension + `\QQ[x]/(x^2+1)`, 5 factors `-(x - 2)(x + 2)`, this behaviour can be + read off the Mac Lane approximants:: + + sage: k=Qp(5,4) + sage: v = pAdicValuation(k) + sage: R.=k[] + sage: G = x^2 + 1 + sage: v1,v2 = v.mac_lane_approximants(G); v1,v2 + ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 2 + O(5^4)) = 1 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 3 + O(5^4)) = 1 ]) + sage: w1, w2 = v.mac_lane_approximants(G,precision_cap=2); w1,w2 + ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 2 + 5 + 2*5^2 + O(5^4)) = 3 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 3 + 3*5 + 2*5^2 + O(5^4)) = 3 ]) + + Note how the latter give a better approximation to the factors of `x^2 + 1`:: + + sage: v1.phi() * v2.phi() - G + (5 + O(5^4))*x + 5 + O(5^4) + sage: w1.phi() * w2.phi() - G + (5^3 + O(5^4))*x + 5^3 + O(5^4) + + In this example, the process stops with a factorization of `x^2 + 1`:: + + sage: v.mac_lane_approximants(G, precision_cap=infinity) + [[ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 2 + 5 + 2*5^2 + 5^3 + O(5^4)) = +Infinity ], + [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 3 + 3*5 + 2*5^2 + 3*5^3 + O(5^4)) = +Infinity ]] + + This obviously cannot happen over the rationals where we only get an + approximate factorization:: + + sage: v = pAdicValuation(QQ, 5) + sage: R.=QQ[] + sage: G = x^2 + 1 + sage: v.mac_lane_approximants(G) + [[ Gauss valuation induced by 5-adic valuation, v(x + 2) = 1 ], [ Gauss valuation induced by 5-adic valuation, v(x + 3) = 1 ]] + sage: v.mac_lane_approximants(G, precision_cap=5) + [[ Gauss valuation induced by 5-adic valuation, v(x + 14557) = 6 ], [ Gauss valuation induced by 5-adic valuation, v(x + 32318) = 7 ]] + + TESTS: + + Initial versions ran into problems with the trivial residue field + extensions in this case:: + + sage: K = Qp(3,20) + sage: R. = K[] + + sage: alpha = T^3/4 + sage: G = 3^3*T^3*(alpha^4 - alpha)^2 - (4*alpha^3 - 1)^3 + sage: G = G/G.leading_coefficient() + sage: pAdicValuation(K).mac_lane_approximants(G) # long time + [[ Gauss valuation induced by 3-adic valuation, v((1 + O(3^20))*T + (2 + O(3^20))) = 1/9, v((1 + O(3^20))*T^9 + (2*3 + 2*3^2 + O(3^21))*T^8 + (3 + 3^5 + O(3^21))*T^7 + (2*3 + 2*3^2 + 3^3 + 2*3^4 + 2*3^5 + 3^6 + O(3^21))*T^6 + (2*3 + 2*3^2 + 3^4 + 3^6 + 2*3^7 + O(3^21))*T^5 + (3 + 3^2 + 3^3 + 2*3^6 + 2*3^7 + 3^8 + O(3^21))*T^4 + (2*3 + 2*3^2 + 3^3 + 2*3^5 + 2*3^6 + 2*3^7 + 2*3^8 + O(3^21))*T^3 + (2*3 + 2*3^2 + 3^3 + 2*3^4 + 3^5 + 2*3^6 + 2*3^7 + 2*3^8 + O(3^21))*T^2 + (3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^7 + 3^8 + O(3^21))*T + (2 + 2*3 + 2*3^2 + 2*3^4 + 2*3^5 + 3^7 + O(3^20))) = 55/27 ]] + + A similar example:: + + sage: R. = QQ[] + sage: v = pAdicValuation(QQ, 3) + sage: G = (x^3 + 3)^3 - 81 + sage: v.mac_lane_approximants(G) + [[ Gauss valuation induced by 3-adic valuation, v(x) = 1/3, v(x^3 + 3*x + 3) = 13/9 ]] + + OUTPUT: + + A list of valuations over the parent of ``G``. + + .. NOTE:: + + For an irreducible ``G`` over `K`, these extensions correspond to + the extensions of this valuation to the field `K[x]/(G(x))`, with + `K` the number field corresponding to the domain of this valuation, + see Theorem 2.1 in [ML1936] + + REFERENCES: + + .. [ML1936] Mac Lane, S. (1936). A construction for prime ideals as absolute + values of an algebraic field. Duke Mathematical Journal, 2(3), 492-510. + + """ + R = G.parent() + if R.base_ring() is not self.domain(): + raise ValueError("G must be defined over the domain of this valuation") + if not assume_squarefree and not G.is_squarefree(): + raise ValueError("G must be squarefree") + if not G.is_monic(): + raise ValueError("G must be monic") + + from sage.rings.all import infinity + from gauss_valuation import GaussValuation + + leaves = [ GaussValuation(R, self)] + while True: + ef = [ v.E()*v.F() for v in leaves] + if sum(ef) == G.degree(): + if precision_cap is None or all([v(v.phi())>precision_cap for v in leaves]): + return leaves + + expandables = [] + new_leaves = [] + for v in leaves: + if v(G) is infinity: + new_leaves.append(v) + else: + expandables.append(v) + leaves = new_leaves + + if not expandables: + return leaves + + for v in expandables: + leaves.extend(v.mac_lane_step(G)) + + def change_ring(self, base_ring): + return pAdicValuation(base_ring) + +class pAdicValuation_padic(pAdicValuation_base): + """ + The `p`-adic valuation of a `p`-adic ring. + + INPUT: + + - ``R`` -- a `p`-adic ring + + EXAMPLES:: + + sage: pAdicValuation(Qp(2)) #indirect doctest + 2-adic valuation + + """ + def __init__(self, R): + """ + Initialization. + + TESTS:: + + sage: type(pAdicValuation(Qp(2))) #indirect doctest + + + """ + pAdicValuation_base.__init__(self, R, R.prime()) + + def reduce(self, x): + """ + Reduce ``x`` modulo the ideal of elements of positive valuation. + + INPUT: + + - ``x`` -- an element of the domain of this valuation + + OUTPUT: + + An element of the :meth:`residue_field`. + + EXAMPLES:: + + sage: R = Zp(3) + sage: pAdicValuation(Zp(3)).reduce(R(4)) + 1 + + """ + if x.parent() is not self.domain(): + raise ValueError("x must be in the domain of the valuation") + + return self.residue_field()(x.residue()) + + def lift(self, x): + """ + Lift ``x`` from the residue field to the domain of this valuation. + + INPUT: + + - ``x`` -- an element of the :meth:`residue_field` + + EXAMPLES:: + + sage: R = Zp(3) + sage: v = pAdicValuation(R) + sage: xbar = v.reduce(R(4)) + sage: v.lift(xbar) + 1 + O(3^20) + + """ + if x.parent() is not self.residue_field(): + raise ValueError("x must be in the residue field of the valuation") + + return self.domain()(x).lift_to_precision() + + def uniformizer(self): + """ + Return a uniformizer of this valuation, i.e., `p` as an element of the + domain. + + EXAMPLES:: + + sage: v = pAdicValuation(Zp(3)) + sage: v.uniformizer() + 3 + O(3^21) + + """ + return self.domain().uniformizer() + + def residue_field(self): + """ + Return the residue field of the ring of integers of this valuation. + + EXAMPLES:: + + sage: v = pAdicValuation(Zp(3)) + sage: v.residue_field() + Finite Field of size 3 + + """ + return self.domain().residue_field() + + def shift(self, c, v): + """ + Multiply ``c`` by a ``v``th power of the uniformizer. + + INPUT: + + - ``c`` -- an element of the domain of this valuation + + - ``v`` -- an integer + + OUTPUT: + + If the resulting element has negative valation, then it will be brought + to the fraction field, otherwise it is an element of the domain. + + EXAMPLES:: + + sage: R = Zp(3) + sage: v = pAdicValuation(Zp(3)) + sage: v.shift(R(2),3) + 2*3^3 + O(3^23) + + """ + from sage.rings.all import ZZ + if c.parent() is not self.domain(): + raise ValueError("c must be in the domain of the valuation") + if not v in ZZ: + raise ValueError("v must be an integer") + v = ZZ(v) + + return c< Date: Thu, 27 Nov 2014 00:30:35 +0100 Subject: [PATCH 004/740] polymod valuations for function fields --- function_field_valuation.py | 87 +++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/function_field_valuation.py b/function_field_valuation.py index 4b702687f24..a47ef491459 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -148,6 +148,93 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): def _repr_(self): return "Valuation on rational function field induced by %s"%self._base_valuation +class FunctionFieldPolymodValuation(DiscreteValuation): + def __init__(self, domain, base_valuation): + from sage.rings.all import infinity + if base_valuation.domain() is not domain._ring: + raise ValueError + if base_valuation(domain.polynomial()) is not infinity: + raise ValueError + + self._base_valuation = base_valuation + DiscreteValuation.__init__(self, domain) + + def _call_(self, x): + return self._base_valuation(x.element()) + + def __hash__(self): + return hash(self._base_valuation) + hash(self.domain()) + + def _cache_key(self): + return self._base_valuation, self.domain() + + def __cmp__(self, other): + if type(self) != type(other): + return cmp(type(self),type(other)) + return cmp(self._base_valuation,other._base_valuation) + + def shift(self, f, v): + if f.parent() is not self.domain(): + raise ValueError + + if v == 0: + return f + elif v < 0: + return f/self._base_valuation.element_with_valuation(-v) + else: + return f*self._base_valuation.element_with_valuation(v) + + def value_group(self): + return self._base_valuation.value_group() + + def residue_field(self): + return self._base_valuation.residue_field() + + def reduce(self, f): + return self.residue_field()(self._base_valuation._base_valuation.reduce(f.element())) + + def lift(self, F): + if F.parent() is not self.residue_field(): + raise ValueError + + return self.domain()(self._base_valuation._base_valuation.lift(F.element())) + + def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): + # TODO: move this to discrete valuation + R = G.parent() + if R.base_ring() is not self.domain(): + raise ValueError("G must be defined over the domain of this valuation") + if not assume_squarefree and not G.is_squarefree(): + raise ValueError("G must be squarefree") + + from sage.rings.all import infinity + from gauss_valuation import GaussValuation + + leaves = [ GaussValuation(R, self)] + while True: + ef = [ v.E()*v.F() for v in leaves] + if sum(ef) == G.degree(): + if precision_cap is None or all([v(v.phi())>precision_cap for v in leaves]): + return leaves + + expandables = [] + new_leaves = [] + for v in leaves: + if v(G) is infinity: + new_leaves.append(v) + else: + expandables.append(v) + leaves = new_leaves + + if not expandables: + return leaves + + for v in expandables: + leaves.extend(v.mac_lane_step(G)) + + def _repr_(self): + return "Valuation on rational function field induced by %s"%self._base_valuation + from sage.structure.unique_representation import UniqueRepresentation class TrivialValuation(UniqueRepresentation, DiscreteValuation): def __init__(self, domain): From c42afb2114b791b905ca97f751453e878a0f47d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 27 Nov 2014 04:47:43 +0100 Subject: [PATCH 005/740] moved extension to pAdicValuation --- augmented_valuation.py | 3 +++ padic_valuation.py | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/augmented_valuation.py b/augmented_valuation.py index d4063bddc87..a2208ae9ee6 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -864,3 +864,6 @@ def F(self): def change_ring(self, base_ring): return AugmentedValuation(self._base_valuation.change_ring(base_ring), self.phi().change_ring(base_ring), self._mu) + + def uniformizer(self): + return self.element_with_valuation(1/self.E()) diff --git a/padic_valuation.py b/padic_valuation.py index 5f6fd606886..7e105e2de76 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -610,6 +610,25 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): def change_ring(self, base_ring): return pAdicValuation(base_ring) + def extension(self, L, algorithm="mac_lane"): + K = self.domain() + if L is K: + return self + if L.base() is not K: + raise ValueError("L must be a simple finite extension of %s"%K) + + if algorithm == "ideal": + I = L.ideal(self.prime()).factor() + if len(I) > 1: + raise ValueError("extension to %s is not unique"%L) + return pAdicValuation(L, I[0]) + if algorithm == "mac_lane": + W = self.mac_lane_approximants(L.defining_polynomial()) + if len(W) > 1: + raise ValueError("extension to %s is not unique"%L) + prime = L.ideal(W[0].uniformizer()(L.gen()), self.residue_field().characteristic()) + return pAdicValuation(L, prime) + class pAdicValuation_padic(pAdicValuation_base): """ The `p`-adic valuation of a `p`-adic ring. From 99d209a166ec16e8ecaca83b0b17038498c9280f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 30 Nov 2014 02:39:43 +0100 Subject: [PATCH 006/740] unique extensions of valuations --- function_field_valuation.py | 17 +++++++++++++++++ padic_valuation.py | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/function_field_valuation.py b/function_field_valuation.py index a47ef491459..3080905cae9 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -148,6 +148,23 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): def _repr_(self): return "Valuation on rational function field induced by %s"%self._base_valuation + def extension(self, L, algorithm="mac_lane"): + K = self.domain() + if L is K: + return self + if L.base() is not K: + raise ValueError("L must be a simple finite extension of %s"%K) + + if algorithm == "mac_lane": + W = self.mac_lane_approximants(L.polynomial(),precision_cap=infinity) + if len(W) > 1: + raise ValueError("extension to %s is not unique"%L) + return FunctionFieldPolymodValuation(L, W[0]) + else: raise ValueError() + + def uniformizer(self): + return self.domain()(self._base_valuation.uniformizer()) + class FunctionFieldPolymodValuation(DiscreteValuation): def __init__(self, domain, base_valuation): from sage.rings.all import infinity diff --git a/padic_valuation.py b/padic_valuation.py index 7e105e2de76..860b06e2053 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -622,12 +622,13 @@ def extension(self, L, algorithm="mac_lane"): if len(I) > 1: raise ValueError("extension to %s is not unique"%L) return pAdicValuation(L, I[0]) - if algorithm == "mac_lane": + elif algorithm == "mac_lane": W = self.mac_lane_approximants(L.defining_polynomial()) if len(W) > 1: raise ValueError("extension to %s is not unique"%L) prime = L.ideal(W[0].uniformizer()(L.gen()), self.residue_field().characteristic()) return pAdicValuation(L, prime) + else: raise ValueError class pAdicValuation_padic(pAdicValuation_base): """ From c391856b1125dea0ff670ad74e27819ccc260644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 8 Dec 2014 07:11:02 +0100 Subject: [PATCH 007/740] latex representation of augmented valuations --- augmented_valuation.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/augmented_valuation.py b/augmented_valuation.py index a2208ae9ee6..a70f9584dd2 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -269,6 +269,17 @@ def element_with_valuation(self, s): s -= self._mu return ret * self._base_valuation.element_with_valuation(s) + def _latex_(self): + vals = [self] + v = self + while isinstance(v, AugmentedValuation): + v = v._base_valuation + vals.append(v) + vals.reverse() + from sage.misc.latex import latex + vals = [ "v_%s(%s) = %s"%(i,latex(v._phi), latex(v._mu)) if isinstance(v, AugmentedValuation) else latex(v) for i,v in enumerate(vals) ] + return "[ %s ]"%", ".join(vals) + def _repr_(self): """ Return a printable representation of this valuation. From dc2a711ce41336b697dbd08a6bc4b0d20b39ca47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 8 Dec 2014 07:11:10 +0100 Subject: [PATCH 008/740] fix lift() --- augmented_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index a70f9584dd2..4ab20a762ea 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -607,7 +607,7 @@ def lift(self, F): # in the last step of reduce, the f_iQ^i are reduced, and evaluated at # the generator of the residue field # here, we undo this: - coeffs = [ R0(c if self.residue_field().is_prime_field() else list(c._vector_())) for c in F.coeffs() ] + coeffs = [ R0(c if self.phi().degree()==1 else list(c._vector_())) for c in F.coeffs() ] coeffs = [ self._base_valuation.lift(c) for c in coeffs ] # now the coefficients correspond to the expansion with (f_iQ^i)(Q^{-1} phi)^i From d60ee6e1c6830625989222f3e6a8c2f15a0b6eba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 12 Dec 2014 09:09:50 +0100 Subject: [PATCH 009/740] fixed a bug in lift() --- augmented_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 4ab20a762ea..46e3092a957 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -607,7 +607,7 @@ def lift(self, F): # in the last step of reduce, the f_iQ^i are reduced, and evaluated at # the generator of the residue field # here, we undo this: - coeffs = [ R0(c if self.phi().degree()==1 else list(c._vector_())) for c in F.coeffs() ] + coeffs = [ R0(c if self.psi().degree()==1 else list(c._vector_())) for c in F.coeffs() ] coeffs = [ self._base_valuation.lift(c) for c in coeffs ] # now the coefficients correspond to the expansion with (f_iQ^i)(Q^{-1} phi)^i From 2b96124baac3d9c01ecb629aefc506f34ace9238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 13 Dec 2014 20:34:54 +0100 Subject: [PATCH 010/740] implemented some _cache_keys --- gauss_valuation.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gauss_valuation.py b/gauss_valuation.py index 4190a3b79ae..367498c0e27 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -99,6 +99,9 @@ def __init__(self, domain, v=None): def __hash__(self): return hash(self.domain()) + def _cache_key(self): + return (self.domain(),) + def value_group(self): """ Return the value group of this valuation. From ed01b4dcb37a84e04e4632219a9e9f3d3fcd7094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 16 Dec 2014 10:11:38 +0100 Subject: [PATCH 011/740] hacks --- developing_valuation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/developing_valuation.py b/developing_valuation.py index a3ba13b6899..0ce9dcdd7fe 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -766,7 +766,8 @@ def _normalize_leading_coefficients(self, f): elif m is infinity or m > max([self.constant_valuation()(c) for c in f.list()[:f.degree()+1]]): f= self.domain()(f.list()[:f.degree()+1]) else: - raise ValueError("f must not have leading zero coefficients") + print "WARNING: DROPPING LEADING ZEROS!" + #raise ValueError("f must not have leading zero coefficients") return f From c08bb6466ed94cd6f5af4c47e9373b902fbfd6cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 17 Dec 2014 16:38:15 +0100 Subject: [PATCH 012/740] pregauss --- augmented_valuation.py | 17 ++++++++++++++++- developing_valuation.py | 9 ++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 46e3092a957..dd59c18252b 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -159,7 +159,19 @@ def Q(self): @cached_method def Q_(self): - ret = self.equivalence_reciprocal(self.Q()) + try: + ret = self.equivalence_reciprocal(self.Q()) + + assert self.is_equivalence_unit(ret) + # esentially this checks that the reduction of Q'*phi^tau is the + # generator of the residue field + assert self._base_valuation.reduce(self.Q()*ret)(self.residue_field_generator()).is_one() + + except ValueError: + print "CHEATING - HARD CODED RECIPROCAL" + Q = self.Q() + pi = Q.parent().base().constant_base_field().uniformizer() + ret = Q/(pi**(self(Q)*2)) assert self.is_equivalence_unit(ret) # esentially this checks that the reduction of Q'*phi^tau is the @@ -481,6 +493,8 @@ def reduce(self, f): raise NotImplementedError("only implemented for polynomial rings over fields") if self(f) < 0: + assert self(f) < 0 + print self(f) raise ValueError("f must have non-negative valuation") elif self(f) > 0: return self.residue_ring().zero() @@ -695,6 +709,7 @@ def lift_to_key(self, F): CV[-1] = (CV[-1][0].parent().one(), vf) ret = self.domain().change_ring(self.domain())([c for c,v in CV])(self.phi()) ret = ret.map_coefficients(lambda c:_lift_to_maximal_precision(c)) + assert (ret == self.phi()) == (F == F.parent().gen()) assert self.is_key(ret) return ret diff --git a/developing_valuation.py b/developing_valuation.py index 0ce9dcdd7fe..7f693dfe119 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -495,8 +495,7 @@ def is_minimal(self, f): raise NotImplementedError("is_minimal() only implemented for equivalence-irreducible polynomials") from gauss_valuation import GaussValuation if isinstance(self,GaussValuation): - # TODO: what is correct in this case? - return f.is_monic() + return f.is_monic() and self.reduce(f).is_irreducible() return list(self.valuations(f))[-1] == self(f) and list(self.coefficients(f))[-1].is_constant() and list(self.valuations(f))[0] == self(f) and self.tau().divides(len(list(self.coefficients(f)))-1) raise NotImplementedError("is_minimal() only implemented for commensurable inductive values") @@ -1017,15 +1016,15 @@ def mac_lane_step(self, G, assume_squarefree=False): phi = phi.coeffs() for i,c in enumerate(r.coeffs()): if not c.is_zero(): - v = c.valuation() + v = w(c) # for a correct result we need to add O(pi^v) in degree i # we try to find the coefficient of phi where such an error can be introduced without losing much absolute precision on phi best = i for j in range(i): - if q[j].valuation() < q[best].valuation(): + if w(q[j]) < w(q[best]): best = j # now add the right O() to phi in degree i-best - phi[i-best] = phi[i-best].add_bigoh(c.valuation()-q[best].valuation()) + phi[i-best] = phi[i-best].add_bigoh(w(c)-w(q[best])) phi = G.parent()(phi) w = self._base_valuation.extension(phi, infinity) From 8189626af839738d20e9b1722c0fd40d5e4aff73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 26 Mar 2015 00:47:57 +0100 Subject: [PATCH 013/740] replace coeffs() with coefficients(sparse=False) to silence deprecation warnings --- augmented_valuation.py | 2 +- developing_valuation.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index dd59c18252b..d58312e1b33 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -621,7 +621,7 @@ def lift(self, F): # in the last step of reduce, the f_iQ^i are reduced, and evaluated at # the generator of the residue field # here, we undo this: - coeffs = [ R0(c if self.psi().degree()==1 else list(c._vector_())) for c in F.coeffs() ] + coeffs = [ R0(c if self.psi().degree()==1 else list(c._vector_())) for c in F.coefficients(sparse=False) ] coeffs = [ self._base_valuation.lift(c) for c in coeffs ] # now the coefficients correspond to the expansion with (f_iQ^i)(Q^{-1} phi)^i diff --git a/developing_valuation.py b/developing_valuation.py index 7f693dfe119..eb5a470cf69 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -322,7 +322,7 @@ def equivalence_reciprocal(self, f): # - we can add anything which times e0 has positive valuation, e.g., we # may drop coefficients of positive valuation h = h.map_coefficients(lambda c:_lift_to_maximal_precision(c)) - h = h.parent()([ c if self(e0*c) <= 0 else c.parent().zero() for c in h.coeffs()]) + h = h.parent()([ c if self(e0*c) <= 0 else c.parent().zero() for c in h.coefficients(sparse=False)]) assert self(f*h) == 0 assert self(f*h - 1) > 0 @@ -802,7 +802,7 @@ def coefficients(self, f): if self.phi().degree() == 1: from itertools import imap - return imap(f.parent(), f(self.phi().parent().gen() - self.phi()[0]).coeffs()) + return imap(f.parent(), f(self.phi().parent().gen() - self.phi()[0]).coefficients(sparse=False)) else: return self.__coefficients(f) @@ -1013,8 +1013,8 @@ def mac_lane_step(self, G, assume_squarefree=False): if not NP: q,r = G.quo_rem(phi) assert not r.is_zero() - phi = phi.coeffs() - for i,c in enumerate(r.coeffs()): + phi = phi.coefficients(sparse=False) + for i,c in enumerate(r.coefficients(sparse=False)): if not c.is_zero(): v = w(c) # for a correct result we need to add O(pi^v) in degree i From 1737b410802a448568a8c6b0bc730de26aa0a6d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 26 Mar 2015 00:48:18 +0100 Subject: [PATCH 014/740] fix lifting for number fields --- developing_valuation.py | 16 ++++++++++++++++ padic_valuation.py | 6 +++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/developing_valuation.py b/developing_valuation.py index eb5a470cf69..59a2ac20cf8 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -572,6 +572,22 @@ def equivalence_decomposition(self, f): sage: len(F) 2 + TESTS:: + + sage: R. = QQ[] + sage: K1.=NumberField(x^3-2) + sage: K.=K1.galois_closure() + sage: p=K.primes_above(2)[0] + sage: R.=K[] + sage: vp=pAdicValuation(K,p) + sage: v0=GaussValuation(R,vp) + sage: G=x^36 + 36*x^35 + 630*x^34 + 7144*x^33 + 59055*x^32 + 379688*x^31 +1978792*x^30 + 8604440*x^29 + 31895428*x^28 + 102487784*x^27 + 289310720*x^26 + 725361352*x^25 + 1629938380*x^24 + 3307417800*x^23 + 6098786184*x^22+10273444280*x^21 + 15878121214*x^20 + 22596599536*x^19 + 29695703772*x^18 +36117601976*x^17 + 40722105266*x^16 + 42608585080*x^15 + 41395961848*x^14 +37344435656*x^13 + 31267160756*x^12 + 24271543640*x^11 + 17439809008*x^10 + 11571651608*x^9 + 7066815164*x^8 + 3953912472*x^7 + 2013737432*x^6 + 925014888*x^5 + 378067657*x^4 + 134716588*x^3 + 40441790*x^2 + 9532544*x + 1584151 + sage: v1=v0.mac_lane_step(G)[0] + sage: V=v1.mac_lane_step(G) + sage: v2=V[0] + sage: v2.equivalence_decomposition(G) + (-80634880/9*alpha^5 + 102406400/3*alpha^4 - 259072000/3*alpha^3 + 163850240*alpha^2 - 208064000*alpha + 6144256) * (x^4 + 4*x^3 + 6*x^2 + 4*x - 35/54*alpha^5 + 13/18*alpha^4 - 2/9*alpha^3 - 11/3*alpha^2 + 41/3*alpha - 33)^3 + REFERENCES: .. [ML1936'] MacLane, S. (1936). A construction for absolute values in diff --git a/padic_valuation.py b/padic_valuation.py index 860b06e2053..3ec720ea1f5 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -792,6 +792,10 @@ def reduce(self, x): def lift(self, x): if x.parent() is not self.residue_field(): raise ValueError("x must be in the residue field of the valuation") + if x.is_one(): + return self.domain().one() + if x.is_zero(): + return self.domain().zero() k = self.domain().residue_field(self._prime) - return self.domain()(x.polynomial(k.gen())) + return self.domain()(k.lift(x.polynomial()(k.gen()))) From 92041c5c8601ef6b1324e243e126016027a5ccf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 26 Mar 2015 00:48:37 +0100 Subject: [PATCH 015/740] unexpected precision gains --- developing_valuation.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/developing_valuation.py b/developing_valuation.py index 59a2ac20cf8..287abfe0d03 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -290,11 +290,15 @@ def equivalence_reciprocal(self, f): sage: w = v.extension(t + 1, 5/4) sage: w = w.extension(t^4 + (a^8 + a^12 + a^14 + a^16 + a^17 + a^19 + a^20 + a^23)*t^3 + (a^6 + a^9 + a^13 + a^15 + a^18 + a^19 + a^21)*t^2 + a^10*t + 1 + a^4 + a^5 + a^8 + a^13 + a^14 + a^15, 17/2) sage: f = a^-15*t^2 + (a^-11 + a^-9 + a^-6 + a^-5 + a^-3 + a^-2)*t + a^-15 - sage: w.equivalence_reciprocal(f) - (a^10 + a^13 + a^14 + a^17 + a^18 + a^21 + a^23 + O(a^26))*t^2 + a^10 + a^13 + O(a^26) + sage: f_ = w.equivalence_reciprocal(f); f_ + (a^10 + a^13 + a^14 + a^17 + a^18 + a^19 + a^20 + a^24 + a^25 + O(a^26))*t^2 + a^10 + a^13 + a^18 + a^19 + a^22 + a^23 + a^24 + a^25 + O(a^26) + sage: w.reduce(f*f_) + 1 sage: f = f.parent()([f[0],f[1].add_bigoh(1),f[2]]) - sage: w.equivalence_reciprocal(f) - (a^10 + a^13 + O(a^26))*t^2 + a^10 + a^13 + O(a^26) + sage: f_ = w.equivalence_reciprocal(f); f_ + (a^10 + a^13 + a^14 + a^17 + a^18 + a^19 + a^20 + a^24 + a^25 + O(a^26))*t^2 + a^10 + a^13 + a^18 + a^19 + a^22 + a^23 + a^24 + a^25 + O(a^26) + sage: w.reduce(f*f_) + 1 .. SEEALSO:: From 5871dacef0037f075bf933a3831c5219b580cb08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 30 Mar 2015 19:43:25 +0200 Subject: [PATCH 016/740] fix p-adic valuations in iterated relative extensions --- padic_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/padic_valuation.py b/padic_valuation.py index 3ec720ea1f5..ac65ff3c494 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -614,7 +614,7 @@ def extension(self, L, algorithm="mac_lane"): K = self.domain() if L is K: return self - if L.base() is not K: + if L.base_ring() is not K: raise ValueError("L must be a simple finite extension of %s"%K) if algorithm == "ideal": From 91e06a946a36dbb37784376cf19eb9a63a566f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 31 Mar 2015 18:40:08 +0200 Subject: [PATCH 017/740] fix caching for pAdic valuations on number fields plus an attempt to turn relative extensions into absolute ones --- padic_valuation.py | 140 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 114 insertions(+), 26 deletions(-) diff --git a/padic_valuation.py b/padic_valuation.py index ac65ff3c494..3d10aff1d81 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -19,10 +19,11 @@ #***************************************************************************** from discrete_valuation import DiscreteValuation -from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.factory import UniqueFactory from sage.misc.cachefunc import cached_method +from sage.misc.fast_methods import WithEqualityById -def pAdicValuation(R, prime = None): +class PadicValuationFactory(UniqueFactory): """ Create a ``prime``-adic valuation on ``R``. @@ -47,31 +48,72 @@ def pAdicValuation(R, prime = None): ... ValueError: prime must be specified for this ring + TESTS: + + A case that took very long due to the hashing of number fields:: + + sage: R. = QQ[] + sage: Delta1= x^12 + 20*x^11 + 154*x^10 + 664*x^9 + 1873*x^8 + 3808*x^7 + 5980*x^6 + 7560*x^5 + 7799*x^4 + 6508*x^3 + 4290*x^2 + 2224*x + 887 + sage: K.=NumberField(x^9-2) + sage: vK=pAdicValuation(QQ,2).extension(K) + sage: G=Delta1.change_ring(K) + sage: v0=GaussValuation(G.parent(),vK) + sage: V=v0.mac_lane_step(G) + sage: while len(V)==1:V=V[0].mac_lane_step(G) + sage: v=V[0] + sage: while v(v.phi())<50: v=v.mac_lane_step(G)[0] + sage: F=v.phi() + sage: L.=K.extension(F) + sage: vK.mac_lane_approximants(F) + sage: vL = vK.extension(L) + """ - from sage.rings.all import ZZ, QQ - from sage.rings.number_field.number_field import is_NumberField - from sage.rings.padics.padic_generic import pAdicGeneric - if R is ZZ or R is QQ: - if prime is None: - raise ValueError("prime must be specified for this ring") - return pAdicValuation_int(R, prime) - elif is_NumberField(R): - if prime is None: - raise ValueError("prime must be specified for this ring") - prime = R.ideal(prime) - if not prime.is_prime(): - raise ValueError("prime must be prime") - return pAdicValuation_number_field(R, prime) - elif isinstance(R, pAdicGeneric): - if prime is None: - prime = R.prime() - if prime != R.prime(): + def create_key_and_extra_args(self, R, prime=None, check=True): + from sage.rings.all import ZZ, QQ + from sage.rings.number_field.number_field import is_NumberField + from sage.rings.padics.padic_generic import pAdicGeneric + if R is ZZ or R is QQ: + if prime is None: + raise ValueError("prime must be specified for this ring") + return (R, prime), {} + elif isinstance(R, pAdicGeneric): + if prime is None: + prime = R.prime() + if prime != R.prime(): + raise NotImplementedError("p-adic valuation not implemented for this ring") + return (R, None), {} + elif is_NumberField(R): + if prime is None: + raise ValueError("prime must be specified for this ring") + prime = R.ideal(prime) + if check and not prime.is_prime(): + raise ValueError("prime must be prime") + return (id(R), prime.gens()), {"R":R, "prime":prime} + else: raise NotImplementedError("p-adic valuation not implemented for this ring") - return pAdicValuation_padic(R) - else: - raise NotImplementedError("p-adic valuation not implemented for this ring") -class pAdicValuation_base(UniqueRepresentation, DiscreteValuation): + def create_object(self, version, key, **extra_args): + from sage.rings.all import ZZ, QQ + from sage.rings.padics.padic_generic import pAdicGeneric + R, prime = key + if R is ZZ or R is QQ: + return pAdicValuation_int(R, prime) + elif isinstance(R, pAdicGeneric): + return pAdicValuation_padic(R) + else: + R, prime = extra_args['R'], extra_args['prime'] + return pAdicValuation_number_field(R, prime) + # TODO: + # The code below tries to turn a relative extension into an absolute extension and then speed up PARI through the maximize_at_primes=(p,) switch. However, in large examples this crashes PARI. + #if R.base() is R.base_ring(): + # return pAdicValuation_number_field(R, prime) + #else: + # return pAdicValuation_number_field_relative(R, prime) + +pAdicValuation = PadicValuationFactory("pAdicValuation") + + +class pAdicValuation_base(WithEqualityById, DiscreteValuation): """ Common base class for `p`-adic valuations on integral domains. @@ -621,13 +663,14 @@ def extension(self, L, algorithm="mac_lane"): I = L.ideal(self.prime()).factor() if len(I) > 1: raise ValueError("extension to %s is not unique"%L) - return pAdicValuation(L, I[0]) + I = I[0] + return pAdicValuation(L, I, check=False) elif algorithm == "mac_lane": W = self.mac_lane_approximants(L.defining_polynomial()) if len(W) > 1: raise ValueError("extension to %s is not unique"%L) prime = L.ideal(W[0].uniformizer()(L.gen()), self.residue_field().characteristic()) - return pAdicValuation(L, prime) + return pAdicValuation(L, prime, check=False) else: raise ValueError class pAdicValuation_padic(pAdicValuation_base): @@ -799,3 +842,48 @@ def lift(self, x): k = self.domain().residue_field(self._prime) return self.domain()(k.lift(x.polynomial()(k.gen()))) + +class pAdicValuation_number_field_relative(pAdicValuation_base): + def __init__(self, R, prime): + pAdicValuation_base.__init__(self, R, prime) + if R.base_ring() is R.base(): + raise ValueError("R must be a relative extension") + from sage.rings.all import ZZ + p = None + for g in prime.gens(): + if g in ZZ: p = ZZ(g) + if p is None: + raise NotImplementedError("determine prime below in ZZ") + self._absolute_field = R.absolute_field(R.variable_name()+"_",maximize_at_primes=(p,)) + self._to_absolute_field = self._absolute_field.structure()[1] + self._from_absolute_field = self._absolute_field.structure()[0] + self._absolute_ideal = self._absolute_field.ideal([self._to_absolute_field(g) for g in prime.gens()]) + self._implementation = pAdicValuation(self._absolute_field, self._absolute_ideal, check=False) + + @cached_method + def uniformizer(self): + return self._from_absolute_field(self._implementation.uniformizer()) + + @cached_method + def residue_field(self): + return self._implementation.residue_field() + + def _repr_(self): + return "%s-adic valuation"%(self._prime) + + def reduce(self, x): + if x.parent() is not self.domain(): + raise ValueError("x must be in the domain of the valuation") + + return self._implementation.reduce(self._to_absolute_field(x)) + + def lift(self, x): + if x.parent() is not self.residue_field(): + raise ValueError("x must be in the residue field of the valuation") + return self._from_absolute_field(self._implementation.lift(x)) + + def _call_(self, x): + if x.parent() is not self.domain(): + raise ValueError("x must be in the domain of the valuation") + + return self._implementation(self._to_absolute_field(x)) From 9fb92d9d2aad4de28816f5d7ca61068cad4cbb25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 9 Apr 2015 02:59:53 +0200 Subject: [PATCH 018/740] implemented reduction for infinite valuations --- augmented_valuation.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/augmented_valuation.py b/augmented_valuation.py index d58312e1b33..9e3384592da 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -508,6 +508,11 @@ def reduce(self, f): raise NotImplementedError return self.residue_ring()(self.coefficients(f).next())(self.residue_field_generator()) + # if this is an infinite valuation, then we can simply drop all but the + # constant term + if self._mu is infinity: + return self.residue_ring()(self._base_valuation.reduce(self.coefficients(f).next())) + CV = zip(self.coefficients(f), self.valuations(f)) # rewrite as sum of f_i phi^{i tau}, i.e., drop most coefficients assert not any([v==0 for i,(c,v) in enumerate(CV) if i % self.tau() != 0]) From c12261ad5defd5e5b025321a0fe9980d90e9f24d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 9 Apr 2015 03:00:13 +0200 Subject: [PATCH 019/740] valuations on number fields via mac lane --- padic_valuation.py | 118 ++++++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 66 deletions(-) diff --git a/padic_valuation.py b/padic_valuation.py index 3d10aff1d81..b9ddaef3ea2 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -18,7 +18,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from discrete_valuation import DiscreteValuation +from discrete_valuation import DiscreteValuation, DiscretePseudoValuation from sage.structure.factory import UniqueFactory from sage.misc.cachefunc import cached_method from sage.misc.fast_methods import WithEqualityById @@ -85,10 +85,15 @@ def create_key_and_extra_args(self, R, prime=None, check=True): elif is_NumberField(R): if prime is None: raise ValueError("prime must be specified for this ring") - prime = R.ideal(prime) - if check and not prime.is_prime(): - raise ValueError("prime must be prime") - return (id(R), prime.gens()), {"R":R, "prime":prime} + if not isinstance(prime, DiscretePseudoValuation): + raise ValueError("prime must be a discrete (pseudo) valuation on a polynomial ring over the base of the number field") + normalized_primes = prime._base_valuation._base_valuation.mac_lane_approximants(R.relative_polynomial()) + normalized_prime = [v for v in normalized_primes if v(prime.phi()) == prime(prime.phi())] + if len(normalized_prime) == 0: + raise ValueError("prime does not define a valuation on this number field.") + if len(normalized_prime) > 1: + raise ValueError("prime does not define a unique valuation on this number field.") + return (id(R), normalized_prime[0]), {"R":R} else: raise NotImplementedError("p-adic valuation not implemented for this ring") @@ -101,7 +106,7 @@ def create_object(self, version, key, **extra_args): elif isinstance(R, pAdicGeneric): return pAdicValuation_padic(R) else: - R, prime = extra_args['R'], extra_args['prime'] + R = extra_args['R'] return pAdicValuation_number_field(R, prime) # TODO: # The code below tries to turn a relative extension into an absolute extension and then speed up PARI through the maximize_at_primes=(p,) switch. However, in large examples this crashes PARI. @@ -669,8 +674,7 @@ def extension(self, L, algorithm="mac_lane"): W = self.mac_lane_approximants(L.defining_polynomial()) if len(W) > 1: raise ValueError("extension to %s is not unique"%L) - prime = L.ideal(W[0].uniformizer()(L.gen()), self.residue_field().characteristic()) - return pAdicValuation(L, prime, check=False) + return pAdicValuation(L, W[0], check=False) else: raise ValueError class pAdicValuation_padic(pAdicValuation_base): @@ -808,82 +812,64 @@ class pAdicValuation_int(pAdicValuation_base): pass class pAdicValuation_number_field(pAdicValuation_base): - @cached_method - def uniformizer(self): - #assert self._prime.is_principal() - #return self._prime.gen(0) - for g in self._prime.gens(): - if g.valuation(self._prime) == 1: - return g - raise NotImplementedError + def __init__(self, R, valuation): + p = valuation.residue_field().characteristic() + assert(p>0) + pAdicValuation_base.__init__(self, R, valuation.uniformizer()(R.gen())) + self._valuation = valuation + + def _mac_lane_step(self): + E,F = self._valuation.E(),self._valuation.F() + self._valuation = self._valuation.mac_lane_step(self.domain().relative_polynomial()) + assert len(self._valuation)== 1 + self._valuation = self._valuation[0] + assert E == self._valuation.E() + assert F == self._valuation.F() @cached_method def residue_field(self): - from sage.rings.all import GF - return GF(self._prime.residue_field().order(), names=('u',)) + return self._valuation.residue_field() def _repr_(self): return "%s-adic valuation"%(self._prime) - def reduce(self, x): + def _call_(self, x): if x.parent() is not self.domain(): raise ValueError("x must be in the domain of the valuation") - k = self.domain().residue_field(self._prime) - return self.residue_field()(k(x).polynomial()) - - def lift(self, x): - if x.parent() is not self.residue_field(): - raise ValueError("x must be in the residue field of the valuation") - if x.is_one(): - return self.domain().one() if x.is_zero(): - return self.domain().zero() - - k = self.domain().residue_field(self._prime) - return self.domain()(k.lift(x.polynomial()(k.gen()))) - -class pAdicValuation_number_field_relative(pAdicValuation_base): - def __init__(self, R, prime): - pAdicValuation_base.__init__(self, R, prime) - if R.base_ring() is R.base(): - raise ValueError("R must be a relative extension") - from sage.rings.all import ZZ - p = None - for g in prime.gens(): - if g in ZZ: p = ZZ(g) - if p is None: - raise NotImplementedError("determine prime below in ZZ") - self._absolute_field = R.absolute_field(R.variable_name()+"_",maximize_at_primes=(p,)) - self._to_absolute_field = self._absolute_field.structure()[1] - self._from_absolute_field = self._absolute_field.structure()[0] - self._absolute_ideal = self._absolute_field.ideal([self._to_absolute_field(g) for g in prime.gens()]) - self._implementation = pAdicValuation(self._absolute_field, self._absolute_ideal, check=False) - - @cached_method - def uniformizer(self): - return self._from_absolute_field(self._implementation.uniformizer()) - - @cached_method - def residue_field(self): - return self._implementation.residue_field() - - def _repr_(self): - return "%s-adic valuation"%(self._prime) + return infinity + + f = self._valuation.domain()(x.list()) + while True: + vals = self._valuation.valuations(f) + ret = min(vals) + argmin = [1 for t in vals if t == ret] + if len(argmin)>1: + self._mac_lane_step() + continue + return ret*self._valuation.E() def reduce(self, x): if x.parent() is not self.domain(): raise ValueError("x must be in the domain of the valuation") - return self._implementation.reduce(self._to_absolute_field(x)) + if self(x)>0: + return self.residue_field().zero() + f = self._valuation.domain()(x.list()) + while True: + ret = self._valuation.reduce(f) + if ret.degree(): + self._mac_lane_step() + continue + return ret[0] def lift(self, x): if x.parent() is not self.residue_field(): raise ValueError("x must be in the residue field of the valuation") - return self._from_absolute_field(self._implementation.lift(x)) - - def _call_(self, x): - if x.parent() is not self.domain(): - raise ValueError("x must be in the domain of the valuation") + if x.is_one(): + return self.domain().one() + if x.is_zero(): + return self.domain().zero() - return self._implementation(self._to_absolute_field(x)) + return self._valuation.lift(x)(self.domain().gen()) From 884832abdf87532c30033e580ae651cdcf12fd68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 12 Apr 2015 16:28:44 +0200 Subject: [PATCH 020/740] fixup for prime ideal free maclane on number fields --- padic_valuation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/padic_valuation.py b/padic_valuation.py index b9ddaef3ea2..98d0c47cb87 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -87,7 +87,7 @@ def create_key_and_extra_args(self, R, prime=None, check=True): raise ValueError("prime must be specified for this ring") if not isinstance(prime, DiscretePseudoValuation): raise ValueError("prime must be a discrete (pseudo) valuation on a polynomial ring over the base of the number field") - normalized_primes = prime._base_valuation._base_valuation.mac_lane_approximants(R.relative_polynomial()) + normalized_primes = prime.constant_valuation().mac_lane_approximants(R.relative_polynomial()) normalized_prime = [v for v in normalized_primes if v(prime.phi()) == prime(prime.phi())] if len(normalized_prime) == 0: raise ValueError("prime does not define a valuation on this number field.") @@ -838,6 +838,7 @@ def _call_(self, x): raise ValueError("x must be in the domain of the valuation") if x.is_zero(): + from sage.rings.all import infinity return infinity f = self._valuation.domain()(x.list()) From b9b6734fded65801e5c118fe0a07b26afa12bd1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 17 Apr 2015 16:05:37 +0200 Subject: [PATCH 021/740] fixed lift/reduce for new number field valuations --- augmented_valuation.py | 15 +++++++++++---- developing_valuation.py | 26 +++++++++++++------------- padic_valuation.py | 26 +++++++++++++++++++++----- 3 files changed, 45 insertions(+), 22 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 9e3384592da..a67a00fe138 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -511,7 +511,7 @@ def reduce(self, f): # if this is an infinite valuation, then we can simply drop all but the # constant term if self._mu is infinity: - return self.residue_ring()(self._base_valuation.reduce(self.coefficients(f).next())) + return self.residue_ring()(self._base_valuation.reduce(self.coefficients(f).next())(self.residue_field().gen())) CV = zip(self.coefficients(f), self.valuations(f)) # rewrite as sum of f_i phi^{i tau}, i.e., drop most coefficients @@ -600,6 +600,8 @@ def lift(self, F): True """ + if F.parent() is self.residue_ring().base(): + F = self.residue_ring()(F) if F.parent() is not self.residue_ring(): raise ValueError("F must be an element of the residue ring of the valuation") if not self.domain().base_ring().is_field(): @@ -630,11 +632,16 @@ def lift(self, F): coeffs = [ self._base_valuation.lift(c) for c in coeffs ] # now the coefficients correspond to the expansion with (f_iQ^i)(Q^{-1} phi)^i - # now we undo the factors of Q^i - coeffs = [ (c*self.Q_()**i).map_coefficients(lambda d:_lift_to_maximal_precision(d)) for i,c in enumerate(coeffs) ] + # now we undo the factors of Q^i (the if else is necessary to handle the case when mu is infinity, i.e., when Q_() is undefined) + coeffs = [ (c if i == 0 else c*self.Q_()**i).map_coefficients(lambda d:_lift_to_maximal_precision(d)) for i,c in enumerate(coeffs) ] RR = self.domain().change_ring(self.domain()) - ret = RR(coeffs)(self.phi()**self.tau()) + + if self._mu is infinity: + assert len(coeffs) <= 1 + ret = RR(coeffs)[0] + else: + ret = RR(coeffs)(self.phi()**self.tau()) ret = ret.map_coefficients(lambda c:_lift_to_maximal_precision(c)) return ret diff --git a/developing_valuation.py b/developing_valuation.py index 287abfe0d03..c10cadb61a4 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -578,19 +578,19 @@ def equivalence_decomposition(self, f): TESTS:: - sage: R. = QQ[] - sage: K1.=NumberField(x^3-2) - sage: K.=K1.galois_closure() - sage: p=K.primes_above(2)[0] - sage: R.=K[] - sage: vp=pAdicValuation(K,p) - sage: v0=GaussValuation(R,vp) - sage: G=x^36 + 36*x^35 + 630*x^34 + 7144*x^33 + 59055*x^32 + 379688*x^31 +1978792*x^30 + 8604440*x^29 + 31895428*x^28 + 102487784*x^27 + 289310720*x^26 + 725361352*x^25 + 1629938380*x^24 + 3307417800*x^23 + 6098786184*x^22+10273444280*x^21 + 15878121214*x^20 + 22596599536*x^19 + 29695703772*x^18 +36117601976*x^17 + 40722105266*x^16 + 42608585080*x^15 + 41395961848*x^14 +37344435656*x^13 + 31267160756*x^12 + 24271543640*x^11 + 17439809008*x^10 + 11571651608*x^9 + 7066815164*x^8 + 3953912472*x^7 + 2013737432*x^6 + 925014888*x^5 + 378067657*x^4 + 134716588*x^3 + 40441790*x^2 + 9532544*x + 1584151 - sage: v1=v0.mac_lane_step(G)[0] - sage: V=v1.mac_lane_step(G) - sage: v2=V[0] - sage: v2.equivalence_decomposition(G) - (-80634880/9*alpha^5 + 102406400/3*alpha^4 - 259072000/3*alpha^3 + 163850240*alpha^2 - 208064000*alpha + 6144256) * (x^4 + 4*x^3 + 6*x^2 + 4*x - 35/54*alpha^5 + 13/18*alpha^4 - 2/9*alpha^3 - 11/3*alpha^2 + 41/3*alpha - 33)^3 + sage: R. = QQ[] + sage: K1.=NumberField(x^3-2) + sage: K.=K1.galois_closure() + sage: R.=K[] + sage: vp=pAdicValuation(QQ,2) + sage: vp=vp.extension(K) + sage: v0=GaussValuation(R,vp) + sage: G=x^36 + 36*x^35 + 630*x^34 + 7144*x^33 + 59055*x^32 + 379688*x^31 +1978792*x^30 + 8604440*x^29 + 31895428*x^28 + 102487784*x^27 + 289310720*x^26 + 725361352*x^25 + 1629938380*x^24 + 3307417800*x^23 + 6098786184*x^22+10273444280*x^21 + 15878121214*x^20 + 22596599536*x^19 + 29695703772*x^18 +36117601976*x^17 + 40722105266*x^16 + 42608585080*x^15 + 41395961848*x^14 +37344435656*x^13 + 31267160756*x^12 + 24271543640*x^11 + 17439809008*x^10 + 11571651608*x^9 + 7066815164*x^8 + 3953912472*x^7 + 2013737432*x^6 + 925014888*x^5 + 378067657*x^4 + 134716588*x^3 + 40441790*x^2 + 9532544*x + 1584151 + sage: v1=v0.mac_lane_step(G)[0] + sage: V=v1.mac_lane_step(G) + sage: v2=V[0] + sage: v2.equivalence_decomposition(G) + (x^4 + 4*x^3 + 6*x^2 + 4*x + alpha^4 + alpha^3 + 1)^3 * (x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*alpha^4 + alpha^3 - 27*alpha + 1)^3 * (x^4 + 4*x^3 + 6*x^2 + 4*x + 3/2*alpha^4 + alpha^3 - 27*alpha + 1)^3 REFERENCES: diff --git a/padic_valuation.py b/padic_valuation.py index 98d0c47cb87..53f44f3f964 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -65,6 +65,7 @@ class PadicValuationFactory(UniqueFactory): sage: F=v.phi() sage: L.=K.extension(F) sage: vK.mac_lane_approximants(F) + [[ Gauss valuation induced by theta-adic valuation, v(x + 1) = 9/4 ]] sage: vL = vK.extension(L) """ @@ -598,11 +599,26 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): A similar example:: - sage: R. = QQ[] - sage: v = pAdicValuation(QQ, 3) - sage: G = (x^3 + 3)^3 - 81 - sage: v.mac_lane_approximants(G) - [[ Gauss valuation induced by 3-adic valuation, v(x) = 1/3, v(x^3 + 3*x + 3) = 13/9 ]] + sage: R. = QQ[] + sage: v = pAdicValuation(QQ, 3) + sage: G = (x^3 + 3)^3 - 81 + sage: v.mac_lane_approximants(G) + [[ Gauss valuation induced by 3-adic valuation, v(x) = 1/3, v(x^3 + 3*x + 3) = 13/9 ]] + + Another problematic case:: + + sage: R.=QQ[] + sage: Delta=x^12 + 20*x^11 + 154*x^10 + 664*x^9 + 1873*x^8 + 3808*x^7 + 5980*x^6 + 7560*x^5 + 7799*x^4 + 6508*x^3 + 4290*x^2 + 2224*x + 887 + sage: K.=NumberField(x^6+108) + sage: K.is_galois() + True + sage: vK=pAdicValuation(QQ,2).extension(K) + sage: vK(2) + 3 + sage: vK(theta) + 1 + sage: G=Delta.change_ring(K) + sage: V=vK.mac_lane_approximants(G) OUTPUT: From bc644d05b1d964558bbf9d3411a82f5bc2cf88ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 19 Apr 2015 17:37:54 +0200 Subject: [PATCH 022/740] fix Kai's example --- padic_valuation.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/padic_valuation.py b/padic_valuation.py index 53f44f3f964..d65d3b4a03a 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -80,6 +80,9 @@ def create_key_and_extra_args(self, R, prime=None, check=True): elif isinstance(R, pAdicGeneric): if prime is None: prime = R.prime() + if isinstance(prime, DiscretePseudoValuation): + if prime(R.prime())==1: + prime = R.prime() if prime != R.prime(): raise NotImplementedError("p-adic valuation not implemented for this ring") return (R, None), {} From 52d3f2cb15c607264eed8112eadbbe0008ae1b31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 27 Apr 2015 23:00:02 +0200 Subject: [PATCH 023/740] fixed valuations for ramified extensions of Qp --- padic_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/padic_valuation.py b/padic_valuation.py index d65d3b4a03a..18f7e477d1d 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -81,7 +81,7 @@ def create_key_and_extra_args(self, R, prime=None, check=True): if prime is None: prime = R.prime() if isinstance(prime, DiscretePseudoValuation): - if prime(R.prime())==1: + if prime(R.prime())>0: prime = R.prime() if prime != R.prime(): raise NotImplementedError("p-adic valuation not implemented for this ring") From 0ffbbc96481d80a5f4988e3bb560982a6058ded3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 8 Aug 2015 11:42:52 -0500 Subject: [PATCH 024/740] Merge branch 'develop' into experimental --- function_field_valuation.py | 3 ++ gauss_valuation.py | 58 +++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/function_field_valuation.py b/function_field_valuation.py index 3080905cae9..ffeacf47e22 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -254,6 +254,9 @@ def _repr_(self): from sage.structure.unique_representation import UniqueRepresentation class TrivialValuation(UniqueRepresentation, DiscreteValuation): + from gauss_valuation import classmaker + __metaclass__ = classmaker() + def __init__(self, domain): DiscreteValuation.__init__(self, domain) diff --git a/gauss_valuation.py b/gauss_valuation.py index 367498c0e27..abdaafe441b 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -45,6 +45,62 @@ from sage.misc.cachefunc import cached_method from sage.structure.unique_representation import UniqueRepresentation +##METACLASS HACK +import inspect, types, __builtin__ + +############## preliminary: two utility functions ##################### +def skip_redundant(iterable, skipset=None): + "Redundant items are repeated items or items in the original skipset." + if skipset is None: skipset = set() + for item in iterable: + if item not in skipset: + skipset.add(item) + yield item + + +def remove_redundant(metaclasses): + skipset = set([types.ClassType]) + for meta in metaclasses: # determines the metaclasses to be skipped + skipset.update(inspect.getmro(meta)[1:]) + return tuple(skip_redundant(metaclasses, skipset)) + +################################################################## +## now the core of the module: two mutually recursive functions ## +################################################################## + +memoized_metaclasses_map = {} + +def get_noconflict_metaclass(bases, left_metas, right_metas): + """Not intended to be used outside of this module, unless you know + what you are doing.""" + # make tuple of needed metaclasses in specified priority order + metas = left_metas + tuple(map(type, bases)) + right_metas + needed_metas = remove_redundant(metas) + + # return existing confict-solving meta, if any + if needed_metas in memoized_metaclasses_map: + return memoized_metaclasses_map[needed_metas] + # nope: compute, memoize and return needed conflict-solving meta + elif not needed_metas: # wee, a trivial case, happy us + meta = type + elif len(needed_metas) == 1: # another trivial case + meta = needed_metas[0] + # check for recursion, can happen i.e. for Zope ExtensionClasses + elif needed_metas == bases: + raise TypeError("Incompatible root metatypes", needed_metas) + else: # gotta work ... + metaname = '_' + ''.join([m.__name__ for m in needed_metas]) + meta = classmaker()(metaname, needed_metas, {}) + memoized_metaclasses_map[needed_metas] = meta + return meta + +def classmaker(left_metas=(), right_metas=()): + def make_class(name, bases, adict): + metaclass = get_noconflict_metaclass(bases, left_metas, right_metas) + return metaclass(name, bases, adict) + return make_class +##END METACLASS HACK + class GaussValuation(UniqueRepresentation, DevelopingValuation): """ A Gauss valuation on a polynomial ring ``domain``. @@ -70,6 +126,8 @@ class GaussValuation(UniqueRepresentation, DevelopingValuation): Gauss valuation induced by 5-adic valuation """ + __metaclass__ = classmaker() + def __init__(self, domain, v=None): """ Initialization. From 62c2c2e6e98be99dc0dfbbb7de0a29fc8bc33054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 25 Sep 2016 00:42:43 -0600 Subject: [PATCH 025/740] Key polynomial modifications according to Stefan --- developing_valuation.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/developing_valuation.py b/developing_valuation.py index c10cadb61a4..20425118781 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -363,6 +363,27 @@ def extension(self, phi, mu, check=True): sage: v.residue_field() Univariate Quotient Polynomial Ring in u2 over Univariate Quotient Polynomial Ring in u1 over Finite Field in u0 of size 2^2 with modulus u1^2 + u1 + u0 with modulus u2^2 + u1*u2 + u1 + TESTS: + + Make sure that we do not make the assumption that the degrees of the + key polynomials are strictly increasing:: + + sage: v_K = pAdicValuation(QQ,3) + sage: A. = QQ[] + sage: v0 = GaussValuation(A,v_K) + sage: f2 = 9*t+30*t^3+27*t^6+15*t^8 + sage: f3 = 3+30*t^2+18*t^3+198*t^5+180*t^7+189*t^8+342*t^10+145*t^12 + sage: F = f2*f3 + + sage: v1 = v0.extension(t,1/12) + sage: v2 = v1.extension(t^12+3,7/6) + sage: v3 = v2.extension(t^12+3*t^2+3,9/4) + sage: v4 = v1.extension(t^12+3*t^2+3,9/4) + sage: v3 == v4 # rather: check for equivalence + True + sage: v4.equivalence_decomposition(F) + sage: v3.equivalence_decomposition(F) + .. SEEALSO:: :class:`AugmentedValuation` @@ -485,6 +506,9 @@ def is_minimal(self, f): sage: v2.is_minimal(f) False + TODO: Add examples for polynomials which have the same degree as the + key polynomial (See Stefan's mail Sep 8 2016). + """ if f.parent() is not self.domain(): raise ValueError("f must be in the domain of the valuation") @@ -500,7 +524,11 @@ def is_minimal(self, f): from gauss_valuation import GaussValuation if isinstance(self,GaussValuation): return f.is_monic() and self.reduce(f).is_irreducible() - return list(self.valuations(f))[-1] == self(f) and list(self.coefficients(f))[-1].is_constant() and list(self.valuations(f))[0] == self(f) and self.tau().divides(len(list(self.coefficients(f)))-1) + if self.is_equivalent(self.phi(), f): + # TODO: reference new Lemma + return f.degree() == self.phi().degree() + else: + return list(self.valuations(f))[-1] == self(f) and list(self.coefficients(f))[-1].is_constant() and list(self.valuations(f))[0] == self(f) and self.tau().divides(len(list(self.coefficients(f)))-1) raise NotImplementedError("is_minimal() only implemented for commensurable inductive values") From 3718349e74949464dcee5b7229094430679ef593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 25 Sep 2016 16:45:10 -0600 Subject: [PATCH 026/740] Symlink MacLane code to use it outside of the sage tree --- discrete_valuation.py | 1 + discrete_value_group.py | 1 + 2 files changed, 2 insertions(+) create mode 120000 discrete_valuation.py create mode 120000 discrete_value_group.py diff --git a/discrete_valuation.py b/discrete_valuation.py new file mode 120000 index 00000000000..f836d6389b1 --- /dev/null +++ b/discrete_valuation.py @@ -0,0 +1 @@ +../src/sage/rings/padics/discrete_valuation.py \ No newline at end of file diff --git a/discrete_value_group.py b/discrete_value_group.py new file mode 120000 index 00000000000..d94f267d4fd --- /dev/null +++ b/discrete_value_group.py @@ -0,0 +1 @@ +../src/sage/rings/padics/discrete_value_group.py \ No newline at end of file From 59f32bfd8b3aa144feff39556cc0bbe4c35a9fb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 13 Oct 2016 13:30:26 -0500 Subject: [PATCH 027/740] Make basic valuation infrastructure work in standalone mode and restore quite some sanity wrt to caching, hashing, and equality. --- augmented_valuation.py | 5 +- developing_valuation.py | 10 +++- gauss_valuation.py | 112 +++++++++++++--------------------------- padic_valuation.py | 96 ++++++++++++++++++++++++---------- 4 files changed, 115 insertions(+), 108 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index a67a00fe138..2a5ddc24b37 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -365,7 +365,8 @@ def value_group(self): base = self._base_valuation.value_group() if self._mu is infinity: return base - return base + base.number_field().ideal(self._mu) + from discrete_value_group import DiscreteValueGroup + return base + DiscreteValueGroup(self._mu) def valuations(self, f): """ @@ -755,7 +756,7 @@ def tau(self): assert self.value_group().numerator() == 1 assert self._base_valuation.value_group().numerator() == 1 - return ZZ(self.value_group().denominator().gen(0)) // ZZ(self._base_valuation.value_group().denominator().gen(0)) + return ZZ(self.value_group().denominator()) // ZZ(self._base_valuation.value_group().denominator()) @cached_method def psi(self): diff --git a/developing_valuation.py b/developing_valuation.py index 20425118781..0592fa40738 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -29,6 +29,12 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) +import sys, os +if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: + sys.path.append(os.getcwd()) + sys.path.append(os.path.dirname(os.getcwd())) + from discrete_valuation import DiscreteValuation from sage.misc.abstract_method import abstract_method @@ -174,8 +180,8 @@ def is_equivalence_irreducible(self, f): if f.is_constant(): raise ValueError("f must not be constant") - from sage.misc.cachefunc import _cache_key - key = _cache_key(f) + from sage.misc.cachefunc import cache_key + key = cache_key(f) if self.equivalence_decomposition.is_in_cache(key): F = self.equivalence_decomposition(f) diff --git a/gauss_valuation.py b/gauss_valuation.py index abdaafe441b..3fc1ddf4e89 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -10,6 +10,7 @@ EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v0 = pAdicValuation(QQ, 2) sage: v = GaussValuation(R, v0); v @@ -40,68 +41,38 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) +import sys, os +if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: + sys.path.append(os.getcwd()) + sys.path.append(os.path.dirname(os.getcwd())) + from developing_valuation import DevelopingValuation from sage.misc.cachefunc import cached_method from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.factory import UniqueFactory + +class GaussValuationFactory(UniqueFactory): + def create_key(self, domain, v=None): + if not domain.ngens() == 1: + raise NotImplementedError("only implemented for univariate rings") + + from sage.rings.padics.padic_generic import pAdicGeneric + from padic_valuation import pAdicValuation + if v is None: + v = pAdicValuation(domain.base_ring()) + if not v.domain() is domain.base_ring(): + raise ValueError("the domain of v must be the base ring of domain") + + return (domain, v) + + def create_object(self, version, key, **extra_args): + return GaussValuation_generic(*key) + +GaussValuation = GaussValuationFactory("GaussValuation") -##METACLASS HACK -import inspect, types, __builtin__ - -############## preliminary: two utility functions ##################### -def skip_redundant(iterable, skipset=None): - "Redundant items are repeated items or items in the original skipset." - if skipset is None: skipset = set() - for item in iterable: - if item not in skipset: - skipset.add(item) - yield item - - -def remove_redundant(metaclasses): - skipset = set([types.ClassType]) - for meta in metaclasses: # determines the metaclasses to be skipped - skipset.update(inspect.getmro(meta)[1:]) - return tuple(skip_redundant(metaclasses, skipset)) - -################################################################## -## now the core of the module: two mutually recursive functions ## -################################################################## - -memoized_metaclasses_map = {} - -def get_noconflict_metaclass(bases, left_metas, right_metas): - """Not intended to be used outside of this module, unless you know - what you are doing.""" - # make tuple of needed metaclasses in specified priority order - metas = left_metas + tuple(map(type, bases)) + right_metas - needed_metas = remove_redundant(metas) - - # return existing confict-solving meta, if any - if needed_metas in memoized_metaclasses_map: - return memoized_metaclasses_map[needed_metas] - # nope: compute, memoize and return needed conflict-solving meta - elif not needed_metas: # wee, a trivial case, happy us - meta = type - elif len(needed_metas) == 1: # another trivial case - meta = needed_metas[0] - # check for recursion, can happen i.e. for Zope ExtensionClasses - elif needed_metas == bases: - raise TypeError("Incompatible root metatypes", needed_metas) - else: # gotta work ... - metaname = '_' + ''.join([m.__name__ for m in needed_metas]) - meta = classmaker()(metaname, needed_metas, {}) - memoized_metaclasses_map[needed_metas] = meta - return meta - -def classmaker(left_metas=(), right_metas=()): - def make_class(name, bases, adict): - metaclass = get_noconflict_metaclass(bases, left_metas, right_metas) - return metaclass(name, bases, adict) - return make_class -##END METACLASS HACK - -class GaussValuation(UniqueRepresentation, DevelopingValuation): +class GaussValuation_generic(DevelopingValuation): """ A Gauss valuation on a polynomial ring ``domain``. @@ -115,6 +86,7 @@ class GaussValuation(UniqueRepresentation, DevelopingValuation): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R = Zp(3,5) sage: S. = R[] sage: v0 = pAdicValuation(R) @@ -126,40 +98,23 @@ class GaussValuation(UniqueRepresentation, DevelopingValuation): Gauss valuation induced by 5-adic valuation """ - __metaclass__ = classmaker() - def __init__(self, domain, v=None): """ Initialization. EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) sage: type(v) """ - if not domain.ngens() == 1: - raise NotImplementedError("only implemented for univariate rings") - - from sage.rings.padics.padic_generic import pAdicGeneric - from padic_valuation import pAdicValuation - if v is None: - v = pAdicValuation(domain.base_ring()) - if not v.domain() is domain.base_ring(): - raise ValueError("the domain of v must be the base ring of domain") - DevelopingValuation.__init__(self, domain, domain.gen()) self._base_valuation = v - def __hash__(self): - return hash(self.domain()) - - def _cache_key(self): - return (self.domain(),) - def value_group(self): """ Return the value group of this valuation. @@ -173,10 +128,11 @@ def value_group(self): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) sage: v.value_group() - Fractional ideal (1) + DiscreteValueGroup(1) """ return self._base_valuation.value_group() @@ -187,6 +143,7 @@ def _repr_(self): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) sage: v @@ -203,6 +160,7 @@ def uniformizer(self): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) sage: v.uniformizer() @@ -224,6 +182,7 @@ def shift(self, f, s): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) sage: v.shift(x, -2) @@ -272,6 +231,7 @@ def valuations(self, f): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R = Qp(2,5) sage: S. = R[] sage: v = GaussValuation(S, pAdicValuation(R)) diff --git a/padic_valuation.py b/padic_valuation.py index 18f7e477d1d..7c86b929650 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -18,6 +18,12 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) +import sys, os +if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: + sys.path.append(os.getcwd()) + sys.path.append(os.path.dirname(os.getcwd())) + from discrete_valuation import DiscreteValuation, DiscretePseudoValuation from sage.structure.factory import UniqueFactory from sage.misc.cachefunc import cached_method @@ -31,7 +37,7 @@ class PadicValuationFactory(UniqueFactory): - ``R`` -- a ring - - ``prime`` -- a prime or ``None`` (default: ``None``), if ``None``, tries + - ``prime`` -- a prime or ``None`` (default: ``None``), if ``None``, try to automatically determine the appropriate prime for ``R`` EXAMPLES:: @@ -52,6 +58,7 @@ class PadicValuationFactory(UniqueFactory): A case that took very long due to the hashing of number fields:: + sage: from mac_lane import * # optional: standalone sage: R. = QQ[] sage: Delta1= x^12 + 20*x^11 + 154*x^10 + 664*x^9 + 1873*x^8 + 3808*x^7 + 5980*x^6 + 7560*x^5 + 7799*x^4 + 6508*x^3 + 4290*x^2 + 2224*x + 887 sage: K.=NumberField(x^9-2) @@ -59,9 +66,9 @@ class PadicValuationFactory(UniqueFactory): sage: G=Delta1.change_ring(K) sage: v0=GaussValuation(G.parent(),vK) sage: V=v0.mac_lane_step(G) - sage: while len(V)==1:V=V[0].mac_lane_step(G) + sage: while len(V) == 1: V=V[0].mac_lane_step(G) sage: v=V[0] - sage: while v(v.phi())<50: v=v.mac_lane_step(G)[0] + sage: while v(v.phi()) < 50: v=v.mac_lane_step(G)[0] sage: F=v.phi() sage: L.=K.extension(F) sage: vK.mac_lane_approximants(F) @@ -85,7 +92,7 @@ def create_key_and_extra_args(self, R, prime=None, check=True): prime = R.prime() if prime != R.prime(): raise NotImplementedError("p-adic valuation not implemented for this ring") - return (R, None), {} + return (R, prime), {} elif is_NumberField(R): if prime is None: raise ValueError("prime must be specified for this ring") @@ -112,17 +119,10 @@ def create_object(self, version, key, **extra_args): else: R = extra_args['R'] return pAdicValuation_number_field(R, prime) - # TODO: - # The code below tries to turn a relative extension into an absolute extension and then speed up PARI through the maximize_at_primes=(p,) switch. However, in large examples this crashes PARI. - #if R.base() is R.base_ring(): - # return pAdicValuation_number_field(R, prime) - #else: - # return pAdicValuation_number_field_relative(R, prime) pAdicValuation = PadicValuationFactory("pAdicValuation") - -class pAdicValuation_base(WithEqualityById, DiscreteValuation): +class pAdicValuation_base(DiscreteValuation): """ Common base class for `p`-adic valuations on integral domains. @@ -164,8 +164,9 @@ def __init__(self, ring, prime): TESTS:: - sage: type(pAdicValuation(ZZ, 3)) - + sage: from sage.rings.padics.padic_valuation import pAdicValuation_int # optional: integrated + sage: isinstance(pAdicValuation(ZZ, 3), pAdicValuation_int) + True """ DiscreteValuation.__init__(self, ring) @@ -184,7 +185,17 @@ def prime(self): return self._prime def value_group(self): - return self._value_group(1) + r""" + Return the value group \ZZ of this valuation. + + EXAMPLES:: + + sage: pAdicValuation(ZZ, 3).value_group() + DiscreteValueGroup(1) + + """ + from discrete_value_group import DiscreteValueGroup + return DiscreteValueGroup(1) def _repr_(self): """ @@ -263,7 +274,8 @@ def lift(self, x): def uniformizer(self): """ - Return a uniformizer of this valuation, i.e., `p` as an element of the domain. + Return a uniformizer of this `p`-adic valuation, i.e., `p` as an + element of the domain. EXAMPLES:: @@ -286,11 +298,11 @@ def residue_field(self): """ from sage.rings.all import GF - return GF(self._prime,names=('u',)) + return GF(self._prime) def shift(self, c, v): """ - Multiply ``c`` by a ``v``th power of the uniformizer. + Multiply ``c`` by a ``v``-th power of the uniformizer. INPUT: @@ -327,7 +339,29 @@ def shift(self, c, v): def is_unramified(self, G, include_steps=False, assume_squarefree=False): """ - Return whether ``G`` defines an unramified extension. + Return whether ``G`` defines an unramified extension of the `p`-adic + completion of the domain this valuation. + + INPUT: + + - ``G`` -- a monic polynomial over the domain of this valuation + + - ``include_steps`` -- a boolean (default: ``False``); whether or not + to include the approximate valuations that were used to determine + the result in the return value. + + - ``assume_squarefree`` -- a boolean (default: ``False``); whether or + not to assume that ``G`` is square-free over the `p`-adic completion + of the domain of this valuation. Setting this to ``True`` can + significantly improve the performance. + + EXAMPLES: + + We consider an extension as unramified if its ramification index is 1. + Hence, a trivial extension is unramified:: + + sage: pass + NOTE: It is not enough to check whether ``G`` is irreducible in reduction for this. @@ -520,15 +554,15 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): sage: G = x^2 + 1 sage: v.mac_lane_approximants(G) - [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x + 1 + O(2^10)) = 1/2 ]] + [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x + (1 + O(2^10))) = 1/2 ]] sage: v.mac_lane_approximants(G, precision_cap = infinity) - [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x + 1 + O(2^10)) = 1/2, v((1 + O(2^10))*x^2 + 1 + O(2^10)) = +Infinity ]] + [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x + (1 + O(2^10))) = 1/2, v((1 + O(2^10))*x^2 + (1 + O(2^10))) = +Infinity ]] sage: G = x^4 + 2*x^3 + 2*x^2 - 2*x + 2 sage: v.mac_lane_approximants(G) [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = 1/4 ]] sage: v.mac_lane_approximants(G,infinity) - [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = 1/4, v((1 + O(2^10))*x^4 + (2 + O(2^11))*x^3 + (2 + O(2^11))*x^2 + (2 + 2^2 + 2^3 + 2^4 + 2^5 + 2^6 + 2^7 + 2^8 + 2^9 + 2^10 + O(2^11))*x + 2 + O(2^11)) = +Infinity ]] + [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = 1/4, v((1 + O(2^10))*x^4 + (2 + O(2^11))*x^3 + (2 + O(2^11))*x^2 + (2 + 2^2 + 2^3 + 2^4 + 2^5 + 2^6 + 2^7 + 2^8 + 2^9 + 2^10 + O(2^11))*x + (2 + O(2^11))) = +Infinity ]] The factorization of primes in the Gaussian integers can be read off the Mac Lane approximants:: @@ -558,9 +592,9 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): sage: R.=k[] sage: G = x^2 + 1 sage: v1,v2 = v.mac_lane_approximants(G); v1,v2 - ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 2 + O(5^4)) = 1 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 3 + O(5^4)) = 1 ]) + ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + O(5^4))) = 1 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 3 + O(5^4)) = 1 ]) sage: w1, w2 = v.mac_lane_approximants(G,precision_cap=2); w1,w2 - ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 2 + 5 + 2*5^2 + O(5^4)) = 3 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 3 + 3*5 + 2*5^2 + O(5^4)) = 3 ]) + ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + 2*5^2 + O(5^4))) = 3 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 3 + 3*5 + 2*5^2 + O(5^4)) = 3 ]) Note how the latter give a better approximation to the factors of `x^2 + 1`:: @@ -572,8 +606,8 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): In this example, the process stops with a factorization of `x^2 + 1`:: sage: v.mac_lane_approximants(G, precision_cap=infinity) - [[ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 2 + 5 + 2*5^2 + 5^3 + O(5^4)) = +Infinity ], - [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 3 + 3*5 + 2*5^2 + 3*5^3 + O(5^4)) = +Infinity ]] + [[ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + 2*5^2 + 5^3 + O(5^4))) = +Infinity ], + [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + 3*5 + 2*5^2 + 3*5^3 + O(5^4))) = +Infinity ]] This obviously cannot happen over the rationals where we only get an approximate factorization:: @@ -716,8 +750,9 @@ def __init__(self, R): TESTS:: - sage: type(pAdicValuation(Qp(2))) #indirect doctest - + sage: from sage.rings.padics.padic_valuation import padicValuation_padic # optional: integrated + sage: isinstance(pAdicValuation(Qp(2)), pAdicValuation_padic) + True """ pAdicValuation_base.__init__(self, R, R.prime()) @@ -893,3 +928,8 @@ def lift(self, x): return self.domain().zero() return self._valuation.lift(x)(self.domain().gen()) + +# Fix doctests so they work in standalone mode +import sys, os +if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: + sys.path.append(os.path.dirname(os.getcwd())) From 369900062d48a18d9e611670444da1e1062cf26a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 13 Oct 2016 13:56:23 -0500 Subject: [PATCH 028/740] Create mac_lane module --- __init__.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 __init__.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 00000000000..78e102d4dd0 --- /dev/null +++ b/__init__.py @@ -0,0 +1,18 @@ +from .padic_valuation import pAdicValuation +from .gauss_valuation import GaussValuation +import discrete_valuation, padic_valuation + +# ================= +# MONKEY PATCH SAGE +# ================= +import sys +import sage +# Register all modules within sage (to make doctests pass) +sys.modules['sage.rings.padics.discrete_valuation'] = discrete_valuation +sage.rings.padics.discrete_valuation = discrete_valuation +sys.modules['sage.rings.padics.padic_valuation'] = padic_valuation +sage.rings.padics.padic_valuation = padic_valuation + +# fix pAdicValuation pickling +from sage.structure.factory import register_factory_unpickle +register_factory_unpickle("pAdicValuation", pAdicValuation) From 646313a822c0db76e7b1fcb24a2654f0d63c2bd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 13 Oct 2016 13:56:37 -0500 Subject: [PATCH 029/740] add symlink for newton polygons --- newton_polygon.py | 1 + 1 file changed, 1 insertion(+) create mode 120000 newton_polygon.py diff --git a/newton_polygon.py b/newton_polygon.py new file mode 120000 index 00000000000..ee1a6011cf6 --- /dev/null +++ b/newton_polygon.py @@ -0,0 +1 @@ +../src/sage/rings/padics/newton_polygon.py \ No newline at end of file From 9c4e3037b96628acfc1a1562d60f70816de93d18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 13 Oct 2016 16:49:15 -0500 Subject: [PATCH 030/740] fix Gauss valuation doctests --- __init__.py | 3 ++ augmented_valuation.py | 4 +++ developing_valuation.py | 5 ++- gauss_valuation.py | 76 ++++++++++++++++++++++++++++++++++++----- padic_valuation.py | 3 +- 5 files changed, 77 insertions(+), 14 deletions(-) diff --git a/__init__.py b/__init__.py index 78e102d4dd0..dab0cd6f252 100644 --- a/__init__.py +++ b/__init__.py @@ -13,6 +13,9 @@ sys.modules['sage.rings.padics.padic_valuation'] = padic_valuation sage.rings.padics.padic_valuation = padic_valuation +# Implement Qp/Zp.valuation +sage.rings.padics.padic_generic.pAdicGeneric.valuation = lambda self: pAdicValuation(self) + # fix pAdicValuation pickling from sage.structure.factory import register_factory_unpickle register_factory_unpickle("pAdicValuation", pAdicValuation) diff --git a/augmented_valuation.py b/augmented_valuation.py index 2a5ddc24b37..e2a3c0a7967 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -906,3 +906,7 @@ def change_ring(self, base_ring): def uniformizer(self): return self.element_with_valuation(1/self.E()) + + def is_gauss_valuation(self): + # Is this correct? Can there be trivial augmentations? + return False diff --git a/developing_valuation.py b/developing_valuation.py index 0592fa40738..0b1154d7b3c 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -528,7 +528,7 @@ def is_minimal(self, f): if not self.is_equivalence_irreducible(f): raise NotImplementedError("is_minimal() only implemented for equivalence-irreducible polynomials") from gauss_valuation import GaussValuation - if isinstance(self,GaussValuation): + if self.is_gauss_valuation(): return f.is_monic() and self.reduce(f).is_irreducible() if self.is_equivalent(self.phi(), f): # TODO: reference new Lemma @@ -819,8 +819,7 @@ def _normalize_leading_coefficients(self, f): elif m is infinity or m > max([self.constant_valuation()(c) for c in f.list()[:f.degree()+1]]): f= self.domain()(f.list()[:f.degree()+1]) else: - print "WARNING: DROPPING LEADING ZEROS!" - #raise ValueError("f must not have leading zero coefficients") + raise ValueError("f must not have leading zero coefficients") return f diff --git a/gauss_valuation.py b/gauss_valuation.py index 3fc1ddf4e89..b48c1fadeeb 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -54,20 +54,72 @@ from sage.structure.factory import UniqueFactory class GaussValuationFactory(UniqueFactory): - def create_key(self, domain, v=None): + r""" + Create a Gauss valuation on ``domain``. + + INPUT: + + - ``domain`` -- a univariate polynomial ring + + - ``v`` -- a valuation on the base ring of ``domain``, the underlying + valuation on the constants of the polynomial ring (if unspecified take + the canonical valuation on the valued ring ``domain``.) + + EXAMPLES: + + The Gauss valuation is the minimum of the valuation of the coefficients:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 2) + sage: R. = QQ[] + sage: w = GaussValuation(R, v) + sage: w(2) + 1 + sage: w(x) + 0 + sage: w(x + 2) + 0 + + """ + def create_key(self, domain, v = None): + r""" + Normalize and check the parameters to create a Gauss valuation. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 2) + sage: R. = ZZ[] + sage: GaussValuation.create_key(R, v) + Traceback (most recent call last): + ... + ValueError: the domain of v must be the base ring of domain + + """ if not domain.ngens() == 1: raise NotImplementedError("only implemented for univariate rings") - from sage.rings.padics.padic_generic import pAdicGeneric - from padic_valuation import pAdicValuation if v is None: - v = pAdicValuation(domain.base_ring()) + v = domain.base_ring().valuation() + if not v.domain() is domain.base_ring(): raise ValueError("the domain of v must be the base ring of domain") return (domain, v) def create_object(self, version, key, **extra_args): + r""" + Create a Gauss valuation from the normalized parameters. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 2) + sage: R. = QQ[] + sage: GaussValuation.create_object(0, (R, v)) + Gauss valuation induced by 2-adic valuation + + """ return GaussValuation_generic(*key) GaussValuation = GaussValuationFactory("GaussValuation") @@ -104,11 +156,14 @@ def __init__(self, domain, v=None): EXAMPLES:: + sage: from sage.rings.padics import GaussValuation # optional: integrated + sage: from sage.rings.padics.gauss_valuation import GaussValuation_generic # optional: integrated sage: from mac_lane import * # optional: standalone + sage: from mac_lane.gauss_valuation import GaussValuation_generic # optional: standalone sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) - sage: type(v) - + sage: isinstance(v, GaussValuation_generic) + True """ DevelopingValuation.__init__(self, domain, domain.gen()) @@ -356,7 +411,7 @@ def lift(self, reduction): sage: F = v.reduce(f); F x^2 + 2*x + 1 sage: g = v.lift(F); g - (1 + O(3^5))*x^2 + (2 + O(3^5))*x + 1 + O(3^5) + (1 + O(3^5))*x^2 + (2 + O(3^5))*x + (1 + O(3^5)) sage: v.is_equivalent(f,g) True @@ -435,9 +490,9 @@ def equivalence_unit(self, s): sage: S. = Qp(3,5)[] sage: v = GaussValuation(S) sage: v.equivalence_unit(2) - 3^2 + O(3^7) + (3^2 + O(3^7)) sage: v.equivalence_unit(-2) - 3^-2 + O(3^3) + (3^-2 + O(3^3)) """ ret = self._base_valuation.domain().one() @@ -509,3 +564,6 @@ def F(self): def change_ring(self, base_ring): base_valuation = self._base_valuation.change_ring(base_ring) return GaussValuation(self.domain().change_ring(base_ring), base_valuation) + + def is_gauss_valuation(self): + return True diff --git a/padic_valuation.py b/padic_valuation.py index 7c86b929650..06ce7c9907b 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -104,7 +104,7 @@ def create_key_and_extra_args(self, R, prime=None, check=True): raise ValueError("prime does not define a valuation on this number field.") if len(normalized_prime) > 1: raise ValueError("prime does not define a unique valuation on this number field.") - return (id(R), normalized_prime[0]), {"R":R} + return (R, normalized_prime[0]) else: raise NotImplementedError("p-adic valuation not implemented for this ring") @@ -117,7 +117,6 @@ def create_object(self, version, key, **extra_args): elif isinstance(R, pAdicGeneric): return pAdicValuation_padic(R) else: - R = extra_args['R'] return pAdicValuation_number_field(R, prime) pAdicValuation = PadicValuationFactory("pAdicValuation") From 82b581cc23b0f332984e00e3f14c8fc3668edf61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 13 Oct 2016 17:12:01 -0500 Subject: [PATCH 031/740] Improved Gauss valuation doctests --- __init__.py | 3 +- gauss_valuation.py | 78 ++++++++++++++++++++++++++++++++-------------- padic_valuation.py | 2 +- 3 files changed, 57 insertions(+), 26 deletions(-) diff --git a/__init__.py b/__init__.py index dab0cd6f252..d6434598834 100644 --- a/__init__.py +++ b/__init__.py @@ -16,6 +16,7 @@ # Implement Qp/Zp.valuation sage.rings.padics.padic_generic.pAdicGeneric.valuation = lambda self: pAdicValuation(self) -# fix pAdicValuation pickling +# fix unpickling of factories from sage.structure.factory import register_factory_unpickle register_factory_unpickle("pAdicValuation", pAdicValuation) +register_factory_unpickle("GaussValuation", GaussValuation) diff --git a/gauss_valuation.py b/gauss_valuation.py index b48c1fadeeb..3f9f00fc5df 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -1,8 +1,9 @@ """ Gauss valuations on polynomial rings -This file implements Gauss valuations for polynomial rings, i.e. valuations -which assign to a polynomial the minimal valuation of its coefficients. +This file implements Gauss valuations for polynomial rings, i.e. discrete +valuations which assign to a polynomial the minimal valuation of its +coefficients. AUTHORS: @@ -63,7 +64,7 @@ class GaussValuationFactory(UniqueFactory): - ``v`` -- a valuation on the base ring of ``domain``, the underlying valuation on the constants of the polynomial ring (if unspecified take - the canonical valuation on the valued ring ``domain``.) + the natural valuation on the valued ring ``domain``.) EXAMPLES: @@ -132,9 +133,7 @@ class GaussValuation_generic(DevelopingValuation): - ``domain`` -- a polynomial ring over a valued ring `R` - - ``v`` -- a discrete valuation on `R` or ``None`` (default: ``None``), if - ``None`` and the ring comes with a natural valuation (such as a `p`-adic - ring), then this one is used + - ``v`` -- a discrete valuation on `R` EXAMPLES:: @@ -149,8 +148,12 @@ class GaussValuation_generic(DevelopingValuation): sage: v = GaussValuation(S, pAdicValuation(QQ, 5)); v Gauss valuation induced by 5-adic valuation + TESTS:: + + sage: TestSuite(v).run(skip="_test_category") + """ - def __init__(self, domain, v=None): + def __init__(self, domain, v): """ Initialization. @@ -174,13 +177,6 @@ def value_group(self): """ Return the value group of this valuation. - OUTPUT: - - Currently, there is no support for additive subgroups of `\QQ`. - Therefore we create this value group as a fractional ideal of `\QQ`. - However, `\QQ` does not support fractional ideals, so we use fractional - ideals of the trivial extensions `\QQ[x]/(x)` - EXAMPLES:: sage: from mac_lane import * # optional: standalone @@ -205,7 +201,7 @@ def _repr_(self): Gauss valuation induced by 5-adic valuation """ - return "Gauss valuation induced by %s"%self._base_valuation + return "Gauss valuation induced by %r"%self._base_valuation @cached_method def uniformizer(self): @@ -220,6 +216,8 @@ def uniformizer(self): sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) sage: v.uniformizer() 5 + sage: v.uniformizer().parent() is S + True """ return self.domain()(self._base_valuation.uniformizer()) @@ -233,7 +231,7 @@ def shift(self, f, s): - ``f`` -- a polynomial in the domain of this valuation - - ``s`` -- an integer + - ``s`` -- an element of the :meth:`value_group` EXAMPLES:: @@ -269,6 +267,8 @@ def shift(self, f, s): raise ValueError("f must be in the domain of this valuation") if -s > self(f) and self.domain().base_ring() is not self.domain().base_ring().fraction_field(): raise ValueError("-s must not exceed the valuation of f") + if s not in self.value_group(): + raise ValueError("s must be in the value group of this valuation") return f.map_coefficients(lambda c:self._base_valuation.shift(c, s)) @@ -338,7 +338,8 @@ def residue_field(self): @cached_method def residue_ring(self): """ - Return the residue ring of this valuation. + Return the residue ring of this valuation, i.e., the elements of + valuation zero module the elements of positive valuation. EXAMPLES:: @@ -360,7 +361,7 @@ def reduce(self, f): OUTPUT: - A polynomial over the residue field of this valuation + A polynomial in the :meth:`residue_ring` of this valuation. EXAMPLES:: @@ -369,6 +370,8 @@ def reduce(self, f): sage: f = x^2 + 2*x + 16 sage: v.reduce(f) x^2 + sage: v.reduce(f).parent() is v.residue_ring() + True The reduction is only defined for integral elements:: @@ -396,7 +399,7 @@ def lift(self, reduction): INPUT:: - - ``reduction`` -- a polynomial over the residue ring of this valuation + - ``reduction`` -- a polynomial over the :meth:`residue_ring` of this valuation OUTPUT: @@ -414,6 +417,8 @@ def lift(self, reduction): (1 + O(3^5))*x^2 + (2 + O(3^5))*x + (1 + O(3^5)) sage: v.is_equivalent(f,g) True + sage: g.parent() is v.domain() + True .. SEEALSO:: @@ -431,7 +436,7 @@ def lift_to_key(self, F): INPUT: - - ``F`` -- an irreducible non-constant polynomial in + - ``F`` -- an irreducible non-constant monic polynomial in :meth:`residue_ring` of this valuation OUTPUT: @@ -464,8 +469,8 @@ def lift_to_key(self, F): def constant_valuation(self): """ - Return the restriction of this valuations to the constants of the - polynomial ring. + Return the restriction of this valuations to the constants of its + domain. EXAMPLES:: @@ -483,7 +488,7 @@ def equivalence_unit(self, s): INPUT: - - ``s`` -- an integer + - ``s`` -- an element of the :meth:`value_group` EXAMPLES:: @@ -509,7 +514,7 @@ def is_commensurable_inductive(self): OUTPUT: - Returns ``True`` since a Gauss valuation always is. + ``True`` since a Gauss valuation always is commensurable inductive. EXAMPLES:: @@ -562,8 +567,33 @@ def F(self): return ZZ.one() def change_ring(self, base_ring): + r""" + Change the base ring of this valuation to ``base_ring``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 2) + sage: R. = ZZ[] + sage: w = GaussValuation(R, v) + sage: w.change_ring(QQ) + Gauss valuation induced by 2-adic valuation + + """ base_valuation = self._base_valuation.change_ring(base_ring) return GaussValuation(self.domain().change_ring(base_ring), base_valuation) def is_gauss_valuation(self): + r""" + Return whether this valuation is a Gauss valuation. + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.is_gauss_valuation() + True + + """ return True diff --git a/padic_valuation.py b/padic_valuation.py index 06ce7c9907b..25755fc8d34 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -707,7 +707,7 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): leaves.extend(v.mac_lane_step(G)) def change_ring(self, base_ring): - return pAdicValuation(base_ring) + return pAdicValuation(base_ring, self.prime()) def extension(self, L, algorithm="mac_lane"): K = self.domain() From 46a7817003d8a78f0ddcf65b15a7f50c0c007d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 16 Oct 2016 16:57:01 -0500 Subject: [PATCH 032/740] Refactored parent structure of valuations to automagically inherit methods from the Hom space. --- __init__.py | 11 +- augmented_valuation.py | 39 ++- developing_valuation.py | 10 +- discrete_valuation.py | 1 - discrete_value_group.py | 1 - domains_with_valuation.py | 1 + gauss_valuation.py | 8 + newton_polygon.py | 1 - padic_valuation.py | 396 ++++++++++++++++++++----- ring_with_valuation.py | 1 + rings_with_valuation.py | 1 + trivial_valuation.py | 306 ++++++++++++++++++++ valuation.py | 217 +++++++++++++- valuation_space.py | 587 ++++++++++++++++++++++++++++++++++++++ value_group.py | 355 +++++++++++++++++++++++ 15 files changed, 1840 insertions(+), 95 deletions(-) delete mode 120000 discrete_valuation.py delete mode 120000 discrete_value_group.py create mode 120000 domains_with_valuation.py delete mode 120000 newton_polygon.py create mode 120000 ring_with_valuation.py create mode 120000 rings_with_valuation.py create mode 100644 trivial_valuation.py create mode 100644 valuation_space.py create mode 100644 value_group.py diff --git a/__init__.py b/__init__.py index d6434598834..53cd88e5303 100644 --- a/__init__.py +++ b/__init__.py @@ -1,17 +1,14 @@ +from .valuation_space import DiscretePseudoValuationSpace, DiscreteValuationSpace +from .trivial_valuation import TrivialDiscreteValuation, TrivialDiscretePseudoValuation from .padic_valuation import pAdicValuation from .gauss_valuation import GaussValuation -import discrete_valuation, padic_valuation +from .value_group import DiscreteValuationCodomain, DiscreteValueGroup +import valuation, padic_valuation, mac_lane # ================= # MONKEY PATCH SAGE # ================= -import sys import sage -# Register all modules within sage (to make doctests pass) -sys.modules['sage.rings.padics.discrete_valuation'] = discrete_valuation -sage.rings.padics.discrete_valuation = discrete_valuation -sys.modules['sage.rings.padics.padic_valuation'] = padic_valuation -sage.rings.padics.padic_valuation = padic_valuation # Implement Qp/Zp.valuation sage.rings.padics.padic_generic.pAdicGeneric.valuation = lambda self: pAdicValuation(self) diff --git a/augmented_valuation.py b/augmented_valuation.py index e2a3c0a7967..52d9b8d3eb7 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -306,15 +306,42 @@ def _repr_(self): [ Gauss valuation induced by 2-adic valuation, v((1 + O(2^5))*x^2 + (1 + O(2^5))*x + u + O(2^5)) = 1/2 ] """ - vals = [self] - v = self - while isinstance(v, AugmentedValuation): - v = v._base_valuation - vals.append(v) - vals.reverse() + vals = self._augmentations() vals = [ "v(%s) = %s"%(v._phi, v._mu) if isinstance(v, AugmentedValuation) else str(v) for v in vals ] return "[ %s ]"%", ".join(vals) + def _augmentations(self): + return self._base_valuation._augmentations() + [self] + + def _richcmp_(self, other, op): + if op == 2: # == + if not isinstance(other, AugmentedValuation): + return False + if self.phi() != other.phi() or self._mu != other._mu or self._base_valuation != other._base_valuation: + return False + return True + if op == 3: # != + return not (self == other) + return DevelopingValuation._richcmp_(self, other, op) + + def __hash__(self): + r""" + The hash value of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * + sage: v = pAdicValuation(QQ, 2) + sage: hash(v) == hash(v) + True + + """ + vals = self._augmentations() + return hash((vals[0]) + ((v.phi(), v._mu) for v in vals[1:])) + + def _cmp_(self, other): + raise NotImplementedError("No total ordering for this valuation.") + @cached_method def constant_valuation(self): """ diff --git a/developing_valuation.py b/developing_valuation.py index 0b1154d7b3c..a2fdb9ee8f6 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -35,7 +35,7 @@ sys.path.append(os.getcwd()) sys.path.append(os.path.dirname(os.getcwd())) -from discrete_valuation import DiscreteValuation +from .valuation import DiscretePseudoValuation from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method @@ -60,7 +60,7 @@ def _lift_to_maximal_precision(c): """ return c if c.parent().is_exact() else c.lift_to_precision() -class DevelopingValuation(DiscreteValuation): +class DevelopingValuation(DiscretePseudoValuation): """ An abstract base class for a discrete valuation of polynomials defined over the polynomial ring ``domain`` by the `\phi`-adic development. @@ -101,7 +101,7 @@ def __init__(self, domain, phi): if not phi.leading_coefficient().is_one(): raise ValueError("phi must be monic") - DiscreteValuation.__init__(self, domain) + DiscretePseudoValuation.__init__(self, domain) self._phi = phi @@ -1051,7 +1051,7 @@ def mac_lane_step(self, G, assume_squarefree=False): if phi == self.phi(): # self.phi() always is a key over self but it will not lead to an extension of this valuation from gauss_valuation import GaussValuation - if isinstance(self,GaussValuation): # unless in the first step + if self.is_gauss_valuation(): # unless in the first step pass elif len(F)==1: # unless this is the only factor, a terminating case which should give a valuation with v(phi)=infinity pass @@ -1094,7 +1094,7 @@ def mac_lane_step(self, G, assume_squarefree=False): if phi.degree() == base.phi().degree(): assert new_mu > self(phi) from gauss_valuation import GaussValuation - if not isinstance(base, GaussValuation): + if not base.is_gauss_valuation(): base = base._base_valuation new_leaf = base.extension(phi, new_mu) diff --git a/discrete_valuation.py b/discrete_valuation.py deleted file mode 120000 index f836d6389b1..00000000000 --- a/discrete_valuation.py +++ /dev/null @@ -1 +0,0 @@ -../src/sage/rings/padics/discrete_valuation.py \ No newline at end of file diff --git a/discrete_value_group.py b/discrete_value_group.py deleted file mode 120000 index d94f267d4fd..00000000000 --- a/discrete_value_group.py +++ /dev/null @@ -1 +0,0 @@ -../src/sage/rings/padics/discrete_value_group.py \ No newline at end of file diff --git a/domains_with_valuation.py b/domains_with_valuation.py new file mode 120000 index 00000000000..0e3c0b715ca --- /dev/null +++ b/domains_with_valuation.py @@ -0,0 +1 @@ +../src/sage/categories/domains_with_valuation.py \ No newline at end of file diff --git a/gauss_valuation.py b/gauss_valuation.py index 3f9f00fc5df..8c160a0d3c2 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -597,3 +597,11 @@ def is_gauss_valuation(self): """ return True + + def _augmentations(self): + r""" + EXAMPLES:: + + sage: TODO + """ + return [self] diff --git a/newton_polygon.py b/newton_polygon.py deleted file mode 120000 index ee1a6011cf6..00000000000 --- a/newton_polygon.py +++ /dev/null @@ -1 +0,0 @@ -../src/sage/rings/padics/newton_polygon.py \ No newline at end of file diff --git a/padic_valuation.py b/padic_valuation.py index 25755fc8d34..047e2394d42 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -24,7 +24,7 @@ sys.path.append(os.getcwd()) sys.path.append(os.path.dirname(os.getcwd())) -from discrete_valuation import DiscreteValuation, DiscretePseudoValuation +from valuation import DiscretePseudoValuation from sage.structure.factory import UniqueFactory from sage.misc.cachefunc import cached_method from sage.misc.fast_methods import WithEqualityById @@ -35,36 +35,137 @@ class PadicValuationFactory(UniqueFactory): INPUT: - - ``R`` -- a ring + - ``R`` -- a subring of a number field or a subring of a local field in + characteristic zero. - - ``prime`` -- a prime or ``None`` (default: ``None``), if ``None``, try - to automatically determine the appropriate prime for ``R`` + - ``prime`` -- a prime that does not split, a discrete (pseudo-)valuation, + a fractional ideal, or ``None`` (default: ``None``) - EXAMPLES:: + EXAMPLES: + + For integers and rational numbers, ``prime`` is just a prime of the + integers:: + sage: from mac_lane import * # optional: standalone sage: pAdicValuation(ZZ, 3) 3-adic valuation - ``prime`` may be ``None`` for `p`-adic rings:: + sage: pAdicValuation(QQ, 3) + 3-adic valuation + + ``prime`` may be ``None`` for local rings:: sage: pAdicValuation(Qp(2)) 2-adic valuation + + sage: pAdicValuation(Zp(2)) + 2-adic valuation + + But it must be specified in all other cases:: + sage: pAdicValuation(ZZ) Traceback (most recent call last): ... ValueError: prime must be specified for this ring + For number fields, ``prime`` can be an integer that is completely ramified + in ``R``:: + + sage: pAdicValuation(GaussianIntegers().fraction_field(), 2) + 2-adic valuation + + For number fields, ``prime`` can be an integer that is unramified in ``R``: + + sage: pAdicValuation(GaussianIntegers().fraction_field(), 3) + 3-adic valuation + + The same applies if ``R`` is a subring of a number field:: + + sage: pAdicValuation(GaussianIntegers(), 3) + 3-adic valuation + + However, this is only supported if ``prime`` does not factor into + pairwise distinct factors:: + + sage: pAdicValuation(GaussianIntegers(), 5) + Traceback (most recent call last): + ... + ValueError: 5 does not single out a unique extension of 5-adic valuation to Number Field in I with defining polynomial x^2 + 1 + + When ``R`` is an absolute or relative number field, or a subring thereof, + ``prime`` can also be specified by providing a valuation on the base ring + that has a unique extension:: + + sage: pAdicValuation(GaussianIntegers(), pAdicValuation(ZZ, 3)) + 3-adic valuation + + When the extension is not unique, this does not work:: + + sage: pAdicValuation(GaussianIntegers(), pAdicValuation(ZZ, 5)) + Traceback (most recent call last): + ... + ValueError: 5-adic valuation does not single out a unique extension of 5-adic valuation to Number Field in I with defining polynomial x^2 + 1 + + For a number field which is of the form `K[x]/(G)`, you can specify a + valuation by providing a discrete pseudo-valuation on `K[x]` which sends + `G` to `\infty`. This lets us specify which extension of the 5-adic + valuation we care about in the above example:: + + sage: R. = QQ[] + sage: v = pAdicValuation(GaussianIntegers(), GaussValuation(R, pAdicValuation(QQ, 5)).extension(x + 2, infinity)) + sage: w = pAdicValuation(GaussianIntegers(), GaussValuation(R, pAdicValuation(QQ, 5)).extension(x + 1/2, infinity)) + sage: v == w + False + + Note that you get the same valuation, even if you write down the + pseudo-valuation differently:: + + sage: ww = pAdicValuation(GaussianIntegers(), GaussValuation(R, pAdicValuation(QQ, 5)).extension(x + 3, infinity)) + sage: w is ww + True + + The valuation ``prime`` does not need to send the defining polynomial `G` + to `\infty`. It is sufficient if it singles out one of the valuations on + the number field. This is important if the prime only factors over the + completion, i.e., if it is not possible to write down one of the factors + within the number field:: + + sage: TODO + + Finally, ``prime`` can also be a fractional ideal of a number field if it + singles out an extension of a `p`-adic valuation of the base field:: + + sage: TODO + TESTS: - A case that took very long due to the hashing of number fields:: + Check that we produce different valuations for distinguishable number + fields:: sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: L. = QQ.extension(x^2 - 2) + sage: R. = L[] + sage: N. = L.extension(y^2 - s) + sage: N = N.absolute_field('u') + + sage: M. = QQ.extension(x^4 - 2) + sage: M is N + False + sage: M == N + True + + sage: pAdicValuation(QQ, 2).extension(M) is pAdicValuation(QQ, 2).extension(N) + False + + A case that took very long due to the hashing of number fields:: + sage: R. = QQ[] sage: Delta1= x^12 + 20*x^11 + 154*x^10 + 664*x^9 + 1873*x^8 + 3808*x^7 + 5980*x^6 + 7560*x^5 + 7799*x^4 + 6508*x^3 + 4290*x^2 + 2224*x + 887 sage: K.=NumberField(x^9-2) - sage: vK=pAdicValuation(QQ,2).extension(K) + sage: vK=pAdicValuation(QQ, 2).extension(K) sage: G=Delta1.change_ring(K) - sage: v0=GaussValuation(G.parent(),vK) + sage: v0=GaussValuation(G.parent(), vK) sage: V=v0.mac_lane_step(G) sage: while len(V) == 1: V=V[0].mac_lane_step(G) sage: v=V[0] @@ -76,52 +177,182 @@ class PadicValuationFactory(UniqueFactory): sage: vL = vK.extension(L) """ - def create_key_and_extra_args(self, R, prime=None, check=True): + def create_key_and_extra_args(self, R, prime=None): + r""" + Create a unique key identifying the valuation of ``R`` with respect to + ``prime``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(QQ, 2) # indirect doctest + 2-adic valuation + + """ from sage.rings.all import ZZ, QQ - from sage.rings.number_field.number_field import is_NumberField from sage.rings.padics.padic_generic import pAdicGeneric + from sage.rings.number_field.number_field import is_NumberField + + if R.characteristic() != 0: + # We do not support equal characteristic yet + raise ValueError("R must be a ring of characteristic zero.") + if R is ZZ or R is QQ: - if prime is None: - raise ValueError("prime must be specified for this ring") - return (R, prime), {} + return self.create_key_for_integers(R, prime), {} elif isinstance(R, pAdicGeneric): - if prime is None: - prime = R.prime() - if isinstance(prime, DiscretePseudoValuation): - if prime(R.prime())>0: - prime = R.prime() - if prime != R.prime(): - raise NotImplementedError("p-adic valuation not implemented for this ring") - return (R, prime), {} - elif is_NumberField(R): - if prime is None: - raise ValueError("prime must be specified for this ring") - if not isinstance(prime, DiscretePseudoValuation): - raise ValueError("prime must be a discrete (pseudo) valuation on a polynomial ring over the base of the number field") - normalized_primes = prime.constant_valuation().mac_lane_approximants(R.relative_polynomial()) - normalized_prime = [v for v in normalized_primes if v(prime.phi()) == prime(prime.phi())] - if len(normalized_prime) == 0: - raise ValueError("prime does not define a valuation on this number field.") - if len(normalized_prime) > 1: - raise ValueError("prime does not define a unique valuation on this number field.") - return (R, normalized_prime[0]) + return self.create_key_for_local_ring(R, prime), {} + elif is_NumberField(R.fraction_field()): + return self.create_key_and_extra_args_for_number_field(R, prime) + else: + raise NotImplementedError("p-adic valuations not implemented for %r"%(R,)) + + def create_key_for_integers(self, R, prime): + from sage.rings.all import ZZ + if prime is None: + raise ValueError("prime must be specified for this ring") + if isinstance(prime, DiscretePseudoValuation): + prime = prime.uniformizer() + if prime not in ZZ or not ZZ(prime).is_prime(): + raise ValueError("prime must be a prime in the integers but %s is not"%(prime,)) + return R, prime + + def create_key_for_local_ring(self, R, prime): + # We do not care much about the value of prime since there is only one + # reasonable p-adic valuation here + if prime is not None and R(prime).valuation() <= 0: + raise ValueError("prime must be an element of positive valuation") + + return (R,) + + def create_key_and_extra_args_for_number_field(self, R, prime): + from sage.rings.number_field.number_field_ideal import NumberFieldFractionalIdeal + # To make our lives easier, we move prime to the fraction field of R + # which we denote in the following as L = K[x]/(G), do all computations + # there and then come back the original ring + L = R.fraction_field() + G = L.relative_polynomial() + K = L.base_ring() + + if isinstance(prime, DiscretePseudoValuation): + return self.create_key_and_extra_args_for_number_field_from_valuation(R, prime, prime) + elif prime in K: + return self.create_key_and_extra_args_for_number_field_from_valuation(R, pAdicValuation(K, prime), prime) + elif prime in L or isinstance(prime, NumberFieldFractionalIdeal): + return self.create_key_and_extra_args_for_number_field_from_ideal(R, L.fractional_ideal(prime), prime) + else: + raise ValueError("prime must be a discrete pseudo-valuation, a prime in the base ring, or a fractional ideal") + + def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime): + r""" + prime is only used to provide more meaningful error messages + """ + # To make our lives easier, we move prime to the fraction field of R + # which we denote in the following as L = K[x]/(G), do all computations + # there and then come back the original ring + L = R.fraction_field() + G = L.relative_polynomial() + K = L.base_ring() + + # Lift v to a pseudo-valuation on K[x] + # First, we lift valuations defined on subrings of K to valuations on K[x] + if v.domain().fraction_field() is not G.parent().fraction_field(): + if v.domain() is not K: + v = pAdicValuation(K, v) + from gauss_valuation import GaussValuation + v = GaussValuation(G.parent(), v) + # Then, we lift valuations defined on polynmial rings which are subrings of K[x] to K[x] + if v.domain() != G.parent(): + v = v.change_ring(K) + assert(v.domain() is G.parent()) + + # To obtain uniqueness of p-adic valuations, we need a canonical + # description of v. We consider all extensions of vK to L and select + # the one approximated by v. + vK = v.constant_valuation() + candidates = vK.mac_lane_approximants(G) + + # We make use of the partial order on discrete pseudo-valuations to + # single out our candidate + + # First, we see if v extends an approximant (necessarily there can be only one) + below_v = [c for c in candidates if v >= c] + assert(len(below_v) <= 1) + if len(below_v) == 1: + ret = below_v[0] + if v(G) >= ret(G): + # equality of number fields has it quirks since it says that two + # fields are == even if they are distinguishable (because they come + # from different constructions.) + # Including structure() into the key seems to be a way to distinguish such cases properly. + return (R, ret, L.construction()), {'approximants': candidates} + else: + raise ValueError("%s does not approximate a valuation on K[x]/(G) with G=%s"%(prime, G)) + + # Even if it does not extend an approximant, v could be an + # approximation of a single approximant + over_v = [c for c in candidates if v <= c] + if len(over_v) > 1: + raise ValueError("%s does not single out a unique extension of %s to %s"%(prime, vK, L)) + elif len(over_v) == 0: + raise ValueError("%s is unrelated to extensions of %s to %s"%(prime, vK, L)) + else: + # equality of number fields has it quirks since it says that two + # fields are == even if they are distinguishable (because they come + # from different constructions.) + # Including structure() into the key seems to be a way to distinguish such cases properly. + return (R, over_v[0], L.construction()), {'approximants': candidates} + + def create_key_and_extra_args_for_number_field_from_ideal(self, R, I, prime): + r""" + prime only for error messages + """ + # To make our lives easier, we move prime to the fraction field of R + # which we denote in the following as L = K[x]/(G), do all computations + # there and then come back the original ring + L = R.fraction_field() + G = L.relative_polynomial() + K = L.base_ring() + + # To obtain uniqueness of p-adic valuations, we need a canonical + # description of v. We consider all extensions of vK to L and select + # the one approximated by v. + # Of course, this only works if I comes from a single prime downstairs. + vK = pAdicValuation(K, I.relative_norm()) + candidates = vK.mac_lane_approximants(G) + + candidates_for_I = [c for c in candidates if all(c(g) > 0 for g in I.gens())] + assert(len(candidates_for_I > 0)) # This should not be possible, unless I contains a unit + if len(candidates_for_I) > 1: + raise ValueError("%s does not single out a unique extension of %s to %s"%(prime, vK, L)) else: - raise NotImplementedError("p-adic valuation not implemented for this ring") + # equality of number fields has it quirks since it says that two + # fields are == even if they are distinguishable (because they come + # from different constructions.) + # Including structure() into the key seems to be a way to distinguish such cases properly. + return (R, candidates_for_I[0], L.construction()), {'approximants': candidates} def create_object(self, version, key, **extra_args): from sage.rings.all import ZZ, QQ from sage.rings.padics.padic_generic import pAdicGeneric - R, prime = key - if R is ZZ or R is QQ: - return pAdicValuation_int(R, prime) - elif isinstance(R, pAdicGeneric): - return pAdicValuation_padic(R) - else: - return pAdicValuation_number_field(R, prime) + from valuation_space import DiscreteValuationSpace + R = key[0] + parent = DiscreteValuationSpace(R) + if isinstance(R, pAdicGeneric): + assert(len(key)==1) + return parent.__make_element_class__(pAdicValuation_padic)(R) + elif R is ZZ or R is QQ: + prime = key[1] + assert(len(key)==2) + return parent.__make_element_class__(pAdicValuation_int)(R, prime) + else: # Number field case + v = key[1] + _ = key[2] # ignored + approximants = extra_args['approximants'] + return parent.__make_element_class__(pAdicValuation_number_field)(R, v, approximants) pAdicValuation = PadicValuationFactory("pAdicValuation") -class pAdicValuation_base(DiscreteValuation): +class pAdicValuation_base(DiscretePseudoValuation): """ Common base class for `p`-adic valuations on integral domains. @@ -132,9 +363,6 @@ class pAdicValuation_base(DiscreteValuation): - ``prime`` -- a prime - - ``check`` -- a boolean (default: ``True``) whether to perform checks on - the parameters - EXAMPLES:: sage: pAdicValuation(ZZ, 3) @@ -148,13 +376,13 @@ class pAdicValuation_base(DiscreteValuation): sage: v = pAdicValuation(Zp(3), 2); v Traceback (most recent call last): ... - NotImplementedError: p-adic valuation not implemented for this ring + ValueError: prime must be an element of positive valuation TESTS:: - sage: TestSuite(pAdicValuation(ZZ, 3)).run(skip="_test_category") - sage: TestSuite(pAdicValuation(QQ, 5)).run(skip="_test_category") - sage: TestSuite(pAdicValuation(Zp(5), 5)).run(skip="_test_category") + sage: TestSuite(pAdicValuation(ZZ, 3)).run() + sage: TestSuite(pAdicValuation(QQ, 5)).run() + sage: TestSuite(pAdicValuation(Zp(5), 5)).run() """ def __init__(self, ring, prime): @@ -168,7 +396,9 @@ def __init__(self, ring, prime): True """ - DiscreteValuation.__init__(self, ring) + from rings_with_valuation import RingsWithDiscreteValuation + from valuation_space import DiscreteValuationSpace + DiscretePseudoValuation.__init__(self, DiscreteValuationSpace(ring)) self._prime = prime def prime(self): @@ -185,7 +415,7 @@ def prime(self): def value_group(self): r""" - Return the value group \ZZ of this valuation. + Return the value group of this valuation. EXAMPLES:: @@ -193,7 +423,7 @@ def value_group(self): DiscreteValueGroup(1) """ - from discrete_value_group import DiscreteValueGroup + from value_group import DiscreteValueGroup return DiscreteValueGroup(1) def _repr_(self): @@ -207,6 +437,15 @@ def _repr_(self): """ return "%s-adic valuation"%(self.prime()) + if isinstance(self.prime(), DiscretePseudoValuation): + vals = self.prime()._augmentations() + if vals[0].is_gauss_valuation(): + return "[ %r over %r ]-adic valuation"%(", ".join("v(%r) = %r"%(v.phi().change_var(self.variable_name()), v(v.phi())) for v in vals[1:]), vals[0].constant_valuation()) + else: + return vals[0] + return "%s-adic valuation"%(self.prime()) + else: + return "%s-adic valuation"%(self.prime()) def _call_(self, x): """ @@ -222,9 +461,6 @@ def _call_(self, x): 2 """ - if x.parent() is not self.domain(): - raise ValueError("x must be in the domain of the valuation") - return x.valuation(self.prime()) def reduce(self, x): @@ -591,9 +827,9 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): sage: R.=k[] sage: G = x^2 + 1 sage: v1,v2 = v.mac_lane_approximants(G); v1,v2 - ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + O(5^4))) = 1 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 3 + O(5^4)) = 1 ]) + ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + O(5^4))) = 1 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + O(5^4))) = 1 ]) sage: w1, w2 = v.mac_lane_approximants(G,precision_cap=2); w1,w2 - ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + 2*5^2 + O(5^4))) = 3 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 3 + 3*5 + 2*5^2 + O(5^4)) = 3 ]) + ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + 2*5^2 + O(5^4))) = 3 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + 3*5 + 2*5^2 + O(5^4))) = 3 ]) Note how the latter give a better approximation to the factors of `x^2 + 1`:: @@ -721,12 +957,12 @@ def extension(self, L, algorithm="mac_lane"): if len(I) > 1: raise ValueError("extension to %s is not unique"%L) I = I[0] - return pAdicValuation(L, I, check=False) + return pAdicValuation(L, I) elif algorithm == "mac_lane": W = self.mac_lane_approximants(L.defining_polynomial()) if len(W) > 1: raise ValueError("extension to %s is not unique"%L) - return pAdicValuation(L, W[0], check=False) + return pAdicValuation(L, W[0]) else: raise ValueError class pAdicValuation_padic(pAdicValuation_base): @@ -865,11 +1101,13 @@ class pAdicValuation_int(pAdicValuation_base): pass class pAdicValuation_number_field(pAdicValuation_base): - def __init__(self, R, valuation): + def __init__(self, R, valuation, approximants): p = valuation.residue_field().characteristic() assert(p>0) - pAdicValuation_base.__init__(self, R, valuation.uniformizer()(R.gen())) + pAdicValuation_base.__init__(self, R, valuation.uniformizer()(R.fraction_field().gen())) + assert(valuation in approximants) self._valuation = valuation + self._approximants = approximants def _mac_lane_step(self): E,F = self._valuation.E(),self._valuation.F() @@ -884,7 +1122,7 @@ def residue_field(self): return self._valuation.residue_field() def _repr_(self): - return "%s-adic valuation"%(self._prime) + return "%r-adic valuation"%(self._valuation) def _call_(self, x): if x.parent() is not self.domain(): @@ -926,9 +1164,31 @@ def lift(self, x): if x.is_zero(): return self.domain().zero() - return self._valuation.lift(x)(self.domain().gen()) + return self._valuation.lift(x)(self.domain().fraction_field().gen()) -# Fix doctests so they work in standalone mode -import sys, os -if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: - sys.path.append(os.path.dirname(os.getcwd())) + def _repr_(self): + """ + Return a printable representation of this valuation. + + EXAMPLES:: + + sage: pAdicValuation(ZZ, 3)._repr_() + '3-adic valuation' + + """ + if isinstance(self._valuation, DiscretePseudoValuation): + from augmented_valuation import AugmentedValuation + # print the minimal information that singles out this valuation from all approximants + approximants = [v._augmentations() for v in self._approximants] + augmentations = self._valuation._augmentations() + unique_approximant = self._valuation + for l in range(len(augmentations)): + if len([a for a in approximants if a[:l+1] == augmentations[:l+1]]) == 1: + unique_approximant = augmentations[:l+1] + break + if unique_approximant[0].is_gauss_valuation(): + unique_approximant[0] = unique_approximant[0].constant_valuation() + if len(unique_approximant) == 1: + return repr(unique_approximant[0]) + return "[ %s ]-adic valuation"%(", ".join("v(%r) = %r" if (isinstance(v, AugmentedValuation) and v.domain() == self._valuation.domain()) else repr(v) for v in unique_approximant)) + return "%s-adic valuation"%(self._valuation) diff --git a/ring_with_valuation.py b/ring_with_valuation.py new file mode 120000 index 00000000000..c4c83221c04 --- /dev/null +++ b/ring_with_valuation.py @@ -0,0 +1 @@ +../src/sage/rings/valuation/ring_with_valuation.py \ No newline at end of file diff --git a/rings_with_valuation.py b/rings_with_valuation.py new file mode 120000 index 00000000000..b3b592df7e3 --- /dev/null +++ b/rings_with_valuation.py @@ -0,0 +1 @@ +../src/sage/categories/rings_with_valuation.py \ No newline at end of file diff --git a/trivial_valuation.py b/trivial_valuation.py new file mode 100644 index 00000000000..76b139e6df8 --- /dev/null +++ b/trivial_valuation.py @@ -0,0 +1,306 @@ +# -*- coding: utf-8 -*- +r""" +Trivial valuations + +This module provides trivial valuations which mainly exist to be able to +provide an implementation for +:meth:`DiscretePseudoValuationSpace._an_element_`. + +EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = DiscreteValuationSpace(QQ).an_element(); v + Trivial valuation on Rational Field + sage: v(1) + 0 + +.. NOTE: + +Note that the tests in this module do not create instances of valuations +directly since this gives the wrong inheritance structure on the resulting +objects:: + + sage: v = TrivialDiscretePseudoValuation(QQ) + sage: v._test_category() + Traceback (most recent call last): + ... + AssertionError: False is not true + +Instead, the valuations need to be created through the +``__make_element_class__`` of the containing space:: + + sage: H = DiscretePseudoValuationSpace(QQ) + sage: v = H.__make_element_class__(TrivialDiscretePseudoValuation)(QQ) + sage: v._test_category() + +Using ``an_element`` provides a much more readable shortcut for this:: + + sage: v = H.an_element() + sage: v._test_category() + +AUTHORS: + +- Julian Rueth (2016-10-14): initial version + +""" +#***************************************************************************** +# Copyright (C) 2016 Julian Rüth +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) +import sys, os +if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: + sys.path.append(os.getcwd()) + sys.path.append(os.path.dirname(os.getcwd())) + +from valuation import DiscretePseudoValuation + +class TrivialDiscretePseudoValuation_base(DiscretePseudoValuation): + r""" + Base class for code shared by trivial valuations. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = DiscretePseudoValuationSpace(ZZ).an_element(); v # indirect doctest + Trivial pseudo-valuation on Integer Ring + + TESTS:: + + sage: TestSuite(v).run() + + """ + def __reduce__(self): + r""" + Return pickling information for this valuation. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = DiscretePseudoValuationSpace(ZZ).an_element() + sage: loads(dumps(v)) == v # indirect doctest + True + + """ + return self.__class__, (self.domain(), ) + + def _eq_(self, other): + r""" + Return whether this valuation is indistinguishable to the + pseudo-valuation ``other``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = DiscretePseudoValuationSpace(ZZ).an_element() + sage: w = DiscretePseudoValuationSpace(ZZ).an_element() + sage: v == w + True + + """ + # other lives in the same space of valuations, say the space of + # discrete valuations on the integers; therefore, it suffices to + # compare the types since there are no paremeters to trivial valuations + return type(self) == type(other) + + def uniformizer(self): + r""" + Return a uniformizing element for this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = DiscretePseudoValuationSpace(QQ).an_element() + sage: v.uniformizer() + Traceback (most recent call last): + ... + ValueError: Trivial valuations do not define a uniformizing element + + """ + raise ValueError("Trivial valuations do not define a uniformizing element") + + def is_trivial(self): + r""" + Return whether this valuation is trivial. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = DiscretePseudoValuationSpace(QQ).an_element() + sage: v.is_trivial() + True + + """ + return True + +class TrivialDiscretePseudoValuation(TrivialDiscretePseudoValuation_base): + r""" + The trivial pseudo-valuation that is `\infty` everywhere. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = DiscretePseudoValuationSpace(QQ).an_element(); v + Trivial pseudo-valuation on Rational Field + + TESTS:: + + sage: TestSuite(v).run() + + """ + def __init__(self, domain): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = DiscretePseudoValuationSpace(QQ).an_element() + sage: isinstance(v, TrivialDiscretePseudoValuation) + True + + """ + from .valuation_space import DiscretePseudoValuationSpace + TrivialDiscretePseudoValuation_base.__init__(self, DiscretePseudoValuationSpace(domain)) + + def is_discrete_valuation(self): + r""" + Return whether this is a discrete valuation. + + EXAMPLES: + + Returns ``False`` since this is only a pseudo-valuation:: + + sage: from mac_lane import * # optional: standalone + sage: v = DiscretePseudoValuationSpace(QQ).an_element() + sage: v.is_discrete_valuation() + False + + """ + return False + + def _call_(self, x): + r""" + Evaluate this valuation at ``x``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = DiscretePseudoValuationSpace(QQ).an_element() + sage: v(0) + +Infinity + sage: v(1) + +Infinity + + """ + from sage.rings.all import infinity + return infinity + + def _repr_(self): + r""" + Return a printable representation of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = DiscretePseudoValuationSpace(QQ).an_element(); v # indirect doctest + Trivial pseudo-valuation on Rational Field + + """ + return "Trivial pseudo-valuation on %r"%(self.domain(),) + + def value_group(self): + r""" + Return the value group of this valuation. + + EXAMPLES: + + A trivial discrete pseudo-valuation has no value group:: + + sage: from mac_lane import * # optional: standalone + sage: v = DiscretePseudoValuationSpace(QQ).an_element() + sage: v.value_group() + Traceback (most recent call last): + ... + ValueError: The trivial pseudo-valuation that is infinity everywhere does not have a value group. + + """ + raise ValueError("The trivial pseudo-valuation that is infinity everywhere does not have a value group.") + +class TrivialDiscreteValuation(TrivialDiscretePseudoValuation_base): + r""" + The trivial valuation that is zero on non-zero elements. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = DiscreteValuationSpace(QQ).an_element(); v # indirect doctest + Trivial valuation on Rational Field + + TESTS:: + + sage: TestSuite(v).run() + + """ + def __init__(self, domain): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = DiscreteValuationSpace(QQ).an_element() + sage: isinstance(v, TrivialDiscreteValuation) + True + + """ + from .valuation_space import DiscreteValuationSpace + TrivialDiscretePseudoValuation_base.__init__(self, DiscreteValuationSpace(domain)) + + def _call_(self, x): + r""" + Evaluate this valuation at ``x``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = DiscreteValuationSpace(QQ).an_element() + sage: v(0) + +Infinity + sage: v(1) + 0 + + """ + from sage.rings.all import infinity + return infinity if x == 0 else self.codomain().zero() + + def _repr_(self): + r""" + Return a printable representation of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = DiscreteValuationSpace(QQ).an_element(); v # indirect doctest + Trivial valuation on Rational Field + + """ + return "Trivial valuation on %r"%(self.domain(),) + + def value_group(self): + r""" + Return the value group of this valuation. + + EXAMPLES: + + A trivial discrete valuation has a trivial value group:: + + sage: from mac_lane import * # optional: standalone + sage: v = DiscreteValuationSpace(QQ).an_element() + sage: v.value_group() + Trivial Additive Abelian Group + + """ + from .value_group import DiscreteValueGroup + return DiscreteValueGroup(0) diff --git a/valuation.py b/valuation.py index 127d3bcc1f1..c5628982c49 100644 --- a/valuation.py +++ b/valuation.py @@ -1,7 +1,212 @@ -from sage.categories.map import Map -from sage.categories.homset import Hom -from sage.categories.sets_cat import Sets +r""" +Discrete valuations -class Valuation(Map): - def __init__(self, domain, codomain): - Map.__init__(self, Hom(domain, codomain, Sets())) +This file defines abstract base classes for discrete (pseudo-)valuations. + +AUTHORS: + +- Julian Rueth (2013-03-16): initial version + +""" +#***************************************************************************** +# Copyright (C) 2013 Julian Rueth +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) +import sys, os +if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: + sys.path.append(os.getcwd()) + sys.path.append(os.path.dirname(os.getcwd())) + +from sage.categories.morphism import Morphism + +class DiscretePseudoValuation(Morphism): + r""" + Abstract base class for discrete pseudo-valuations, i.e., discrete + valuations which might send more that just zero to infinity. + + INPUT: + + - ``domain`` -- an integral domain + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 2); v # indirect doctest + 2-adic valuation + + TESTS:: + + sage: TestSuite(v).run() + + """ + def __init__(self, parent): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: isinstance(pAdicValuation(ZZ, 2), DiscretePseudoValuation) + True + + """ + Morphism.__init__(self, parent=parent) + + def is_equivalent(self, f, g): + r""" + Return whether ``f`` and ``g`` are equivalent. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 2) + sage: v.is_equivalent(2, 1) + False + sage: v.is_equivalent(2, -2) + True + sage: v.is_equivalent(2, 0) + False + sage: v.is_equivalent(0, 0) + True + + """ + vf = self(f) + vg = self(g) + from sage.rings.all import infinity + if self(f) == infinity and self(g) == infinity: + return True + if self(f) == infinity or self(g) == infinity: + return False + return self(f-g) > vf + + def __hash__(self): + r""" + The hash value of this valuation. + + We redirect to :meth:`_hash_`, so that subclasses can only override + :meth:`_hash_` and :meth:`_eq` if they want to provide a different + notion of equality but they can leave the partial and total operators + untouched. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 2) + sage: hash(v) == hash(v) # indirect doctest + True + + """ + return self._hash_() + + def _hash_(self): + r""" + Return a hash value for this valuation. + + We override the strange default provided by + ``sage.categories.marphism.Morphism`` here and implement equality by + ``id``. This works fine for objects which use unique representation + which is the case for most valuations. + """ + return id(self) + + def _cmp_(self, other): + r""" + Compare this element to ``other``. + + Since there is no reasonable total order on valuations, this method + just throws an exception. + + EXAMPLES:: + + sage: from mac_lane import * + sage: v = pAdicValuation(QQ, 2) + sage: v < v + Traceback (most recent call last): + ... + NotImplementedError: Operator not implemented for this valuation. + + """ + raise NotImplementedError("No total order for valuation."); + + def _richcmp_(self, other, op): + r""" + Compare this element to ``other``. + + We redirect to methods :meth:`_eq_`, :meth:`_lt_`, and :meth:`_gt_` to + make it easier for subclasses to override only parts of this + functionality. + + Note that valuations usually implement ``x == y`` as ``x`` and ``y`` + are indistinguishable. Whereas ``x <= y`` and ``x >= y`` are + implemented with respect to the natural partial order of valuations. + As a result, ``x <= y and x >= y`` does not imply ``x == y``. + + EXAMPLES:: + + sage: from mac_lane import * + sage: v = pAdicValuation(QQ, 2) + sage: v == v + True + sage: v != v + False + sage: w = pAdicValuation(QQ, 3) + sage: v == w + False + sage: v != w + True + + """ + if op == 1: # <= + return self._lt_(other) + if op == 2: # == + return self._eq_(other) + if op == 3: # != + return not self == other + if op == 5: # >= + return self._gt_(other) + raise NotImplementedError("Operator not implemented for this valuation.") + + def _eq_(self, other): + r""" + Return whether this valuation and ``other`` are indistinguishable. + + We override the strange default provided by + ``sage.categories.marphism.Morphism`` here and implement equality by + ``id``. This is the right behaviour in many cases. + + When overriding this method, you can assume that ``other`` is a + (pseudo-)valuation on the same domain. + """ + return self is other + + def _lt_(self, other): + r""" + Return whether this valuation is less than or equal to ``other`` + pointwise. + + When overriding this method, you can assume that ``other`` is a + (pseudo-)valuation on the same domain. + """ + if self == other: return True + raise NotImplementedError("Operator not implemented for this valuation.") + + def _gt_(self, other): + r""" + Return whether this valuation is greater than or equal to ``other`` + pointwise. + + When overriding this method, you can assume that ``other`` is a + (pseudo-)valuation on the same domain. + """ + if self == other: return True + raise NotImplementedError("Operator not implemented for this valuation.") + + # Remove the default implementation of Map.__reduce__ that does not play + # nice with factories (a factory, does not override Map.__reduce__ because + # it is not the generic reduce of object) and that does not match equality + # by id. + __reduce__ = object.__reduce__ diff --git a/valuation_space.py b/valuation_space.py new file mode 100644 index 00000000000..d9654fff39c --- /dev/null +++ b/valuation_space.py @@ -0,0 +1,587 @@ +# -*- coding: utf-8 -*- +r""" +Spaces of valuations + +This module provides spaces of exponential pseudo-valuations on integral +domains. It currently, only provides support for such valuations if they are +discrete, i.e., their image is a discrete additive subgroup of the rational +numbers extended by `\infty`. + +EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(QQ, 2).parent() + Discrete valuations on Rational Field + +AUTHORS: + +- Julian Rueth (2016-10-14): initial version + +""" +#***************************************************************************** +# Copyright (C) 2016 Julian Rüth +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) +import sys, os +if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: + sys.path.append(os.getcwd()) + sys.path.append(os.path.dirname(os.getcwd())) + +from sage.categories.homset import Homset +from sage.misc.lazy_attribute import lazy_attribute +from sage.misc.abstract_method import abstract_method +from sage.structure.unique_representation import UniqueRepresentation + +class DiscretePseudoValuationSpace(UniqueRepresentation, Homset): + r""" + The space of discrete pseudo-valuations on ``domain``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: H = DiscretePseudoValuationSpace(QQ) + sage: pAdicValuation(QQ, 2) in H + True + + TESTS:: + + sage: TestSuite(H).run() + + """ + def __init__(self, domain): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: isinstance(pAdicValuation(QQ, 2).parent(), DiscretePseudoValuationSpace) + True + + """ + from sage.categories.domains import Domains + if domain not in Domains(): + raise ValueError("domain must be an integral domain") + + from value_group import DiscreteValuationCodomain + # A valuation is a map from an additive semigroup to an additive semigroup, however, it + # does not preserve that structure. It is therefore only a morphism in the category of sets. + from sage.categories.all import Sets + Homset.__init__(self, domain, DiscreteValuationCodomain(), category = Sets()) + + @lazy_attribute + def _abstract_element_class(self): + r""" + Return an abstract base class for all valuations in this space. + + This is used to extend every valuation with a number of generic methods + that are independent of implementation details. + + Usually, extensions of this kind would be done by implementing an + appropriate class ``MorphismMethods`` in the category of this homset. + However, there is no category whose arrows are the valuations, so we + need to move this magic down to the level of the actual homset. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: isinstance(pAdicValuation(QQ, 2), DiscretePseudoValuationSpace.ElementMethods) # indirect doctest + True + + """ + class_name = "%s._abstract_element_class"%self.__class__.__name__ + from sage.structure.dynamic_class import dynamic_class + return dynamic_class(class_name, (super(DiscretePseudoValuationSpace,self)._abstract_element_class, self.__class__.ElementMethods)) + + def _an_element_(self): + r""" + Return a trivial valuation in this space. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: DiscretePseudoValuationSpace(QQ).an_element() # indirect doctest + Trivial pseudo-valuation on Rational Field + + """ + from trivial_valuation import TrivialDiscretePseudoValuation + return self.__make_element_class__(TrivialDiscretePseudoValuation)(self.domain()) + + def _repr_(self): + r""" + Return a printable representation of this space. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: DiscretePseudoValuationSpace(QQ) # indirect doctest + Discrete pseudo-valuations on Rational Field + + """ + return "Discrete pseudo-valuations on %r"%(self.domain(),) + + def __contains__(self, x): + r""" + Return whether ``x`` is a valuation in this space. + + EXAMPLES: + + sage: from mac_lane import * # optional: standalone + sage: H = DiscretePseudoValuationSpace(QQ) + sage: H.an_element() in H + True + + Elements of spaces which embed into this spaces are correctly handled:: + + sage: pAdicValuation(QQ, 2) in H + True + + """ + # override the logic from Homset with the original implementation for Parent + # which entirely relies on a proper implementation of + # _element_constructor_ and coercion maps + from sage.structure.parent import Parent + return Parent.__contains__(self, x) + + def __call__(self, x): + r""" + Create an element in this space from ``x``. + + EXAMPLES: + + sage: from mac_lane import * # optional: standalone + sage: H = DiscretePseudoValuationSpace(QQ) + sage: H(pAdicValuation(QQ, 2)) + 2-adic valuation + + """ + # override the logic from Homset with the original implementation for Parent + # which entirely relies on a proper implementation of + # _element_constructor_ and coercion maps + from sage.structure.parent import Parent + return Parent.__call__(self, x) + + def _element_constructor_(self, x): + r""" + Create an element in this space from ``x``, + + EXAMPLES: + + We try to convert valuations defined on different domains by changing + their base ring:: + + sage: from mac_lane import * # optional: standalone + sage: Z = DiscretePseudoValuationSpace(ZZ) + sage: Q = DiscretePseudoValuationSpace(QQ) + sage: v = pAdicValuation(ZZ, 2) + sage: v in Q + False + sage: Q(v) in Q + True + sage: Q(v) in Z + False + sage: Z(Q(v)) in Z + True + + We support coercions and conversions, even though they are not + implemented here:: + + sage: Z(v) + 2-adic valuation + + """ + if isinstance(x.parent(), DiscretePseudoValuationSpace): + return self(x.change_ring(self.domain())) + raise ValueError("element can not be converted into the space of %r"%(self,)) + + class ElementMethods: + r""" + Provides methods for discrete pseudo-valuations that are added + automatically to valuations in this space. + + EXAMPLES: + + Here is an example of a method that is automagically added to a + discrete valuation:: + + sage: from mac_lane import * # optional: standalone + sage: H = DiscretePseudoValuationSpace(QQ) + sage: pAdicValuation(QQ, 2).is_discrete_pseudo_valuation() # indirect doctest + True + + The methods will be provided even if the concrete types is not created + with :meth:`__make_element_class__`:: + + sage: from valuation import DiscretePseudoValuation + sage: m = DiscretePseudoValuation(H) + sage: m.parent() is H + True + sage: m.is_discrete_pseudo_valuation() + True + + However, the category framework advises you to use inheritance:: + + sage: m._test_category() + Traceback (most recent call last): + ... + AssertionError: False is not true + + Using :meth:`__make_element_class__`, makes your concrete valuation + inherit from this class:: + + sage: m = H.__make_element_class__(DiscretePseudoValuation)(H) + sage: m._test_category() + + """ + def is_discrete_pseudo_valuation(self): + r""" + Return whether this valuation is a discrete pseudo-valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(QQ, 2).is_discrete_pseudo_valuation() + True + + """ + return True + + @abstract_method + def is_discrete_valuation(self): + r""" + Return whether this valuation is a discrete valuation, i.e., + whether it is a :meth:`is_discrete_pseudo_valuation` that only + sends zero to `\infty`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(QQ, 2).is_discrete_valuation() + True + + """ + + def is_trivial(self): + r""" + Return whether this valuation is trivial, i.e., whether it is + constant `\infty` or constant zero for everything but the zero + element. + + Subclasses need to override this method if they do not implement + :meth:`uniformizer`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(QQ, 7).is_trivial() + False + + """ + from sage.rings.all import infinity + if self(self.domain().one()) == infinity: + # the constant infinity + return True + if self(self.uniformizer()) != 0: + # not constant on the non-zero elements + return False + return True + + @abstract_method + def uniformizer(self): + r""" + Return an element in :meth:`domain` which has positive valuation + and generates the value group of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(QQ, 11).uniformizer() + 11 + + Trivial valuations have no uniformizer:: + + sage: v = DiscretePseudoValuationSpace(QQ).an_element() + sage: v.is_trivial() + True + sage: v.uniformizer() + Traceback (most recent call last): + ... + ValueError: Trivial valuations do not define a uniformizing element + + """ + + def value_group(self, **options): + r""" + Return the value group of this discrete pseudo-valuation, a + discrete additive subgroup of the rational numbers. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(QQ, 2).value_group() + Additive Abelian Group generated by 1 + + A pseudo-valuation that is `\infty` everywhere, does not have a + value group:: + + sage: v = DiscretePseudoValuationSpace(QQ).an_element() + sage: v.value_group() + Traceback (most recent call last): + ... + ValueError: The trivial pseudo-valuation that is infinity everywhere does not have a value group. + + """ + return DiscreteValueGroup(self(self.uniformizer)) + + def _test_add(self, **options): + r""" + Check that the (strict) triangle equality is satisfied for the + valuation of this ring. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 3) + sage: v._test_add() + + """ + tester = self._tester(**options) + S = tester.some_elements(self.domain().some_elements()) + from sage.categories.cartesian_product import cartesian_product + for x,y in tester.some_elements(cartesian_product([S,S])): + tester.assertGreaterEqual(self(x+y),min(self(x),self(y))) + if self(x) != self(y): + tester.assertEqual(self(x+y),min(self(x),self(y))) + + def _test_infinite_zero(self, **options): + r""" + Check that zero is sent to infinity. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: v._test_infinite_zero() + + """ + tester = self._tester(**options) + from sage.rings.all import infinity + tester.assertEqual(self(self.domain().zero()), infinity) + + def _test_mul(self, **options): + r""" + Check that multiplication translates to addition of valuations. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: v._test_mul() + + """ + tester = self._tester(**options) + S = list(self.domain().some_elements()) + from sage.categories.cartesian_product import cartesian_product + for x,y in tester.some_elements(cartesian_product([S,S])): + tester.assertEqual(self(x*y),self(x)+self(y)) + + def _test_no_infinite_units(self, **options): + r""" + Checks that no units are sent to infinity. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: v._test_no_infinite_units() + + As multiplication translates to addition, pseudo-valuations which + send a unit to infinity are necessarily trivial:: + + sage: v = DiscretePseudoValuationSpace(QQ).an_element() + sage: v(1) + +Infinity + sage: v.is_trivial() + True + + """ + if not self.is_discrete_valuation() and self.is_trivial(): + return + + from sage.rings.all import infinity + tester = self._tester(**options) + for x in tester.some_elements(self.domain().some_elements()): + if self(x) == infinity: + tester.assertFalse(x.is_unit()) + + def _test_value_group(self, **options): + r""" + Check correctness of the value group. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: v._test_value_group() + + """ + from sage.rings.all import infinity + tester = self._tester(**options) + # check consistency of trivial valuations first + if self.is_trivial(): + if self(self.domain().one()) == infinity: + # a trivial pseudo-valuation that sends everything to infinity + with tester.assertRaises(ValueError): + self.value_group() + return + + # check that all valuations are in the value group + for x in tester.some_elements(self.domain().some_elements()): + if self(x) != infinity: + tester.assertIn(self(x), self.value_group()) + + if not self.is_trivial(): + # check that the uniformizer generates the value group + tester.assertEqual(self.value_group().gen(), self(self.uniformizer())) + +class DiscreteValuationSpace(DiscretePseudoValuationSpace): + r""" + The space of discrete valuations on ``domain``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: H = DiscreteValuationSpace(QQ) + sage: pAdicValuation(QQ, 2) in H + True + + TESTS:: + + sage: TestSuite(H).run() + + """ + def __init__(self, domain): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: isinstance(pAdicValuation(QQ, 2).parent(), DiscreteValuationSpace) + True + + """ + DiscretePseudoValuationSpace.__init__(self, domain) + + # discrete valuations are discrete pseudo-valuations + self.register_embedding(self.hom(lambda x:x, DiscretePseudoValuationSpace(domain))) + + def _element_constructor_(self, x): + r""" + Create an element in this space from ``x``. + + TESTS: + + Discrete pseudo-valuations that are actually valuations are contained + in this space:: + + sage: from mac_lane import * # optional: standalone + sage: H = DiscretePseudoValuationSpace(QQ) + sage: G = DiscreteValuationSpace(QQ) + sage: v = pAdicValuation(QQ, 2) + sage: v._set_parent(H) + sage: v in G + True + + Discrete pseudo-valuations that are discrete valuations can be + converted into this space:: + + sage: H = DiscreteValuationSpace(ZZ) + sage: v = pAdicValuation(ZZ, 2) + sage: v._set_parent(H) + sage: v in G + False + sage: G(v) in G + True + + """ + # We try to convert discrete pseudo-valuations that claims to be a + # valuation into this space + if isinstance(x.parent(), DiscretePseudoValuationSpace) and x.is_discrete_valuation(): + # after we base-changed them into our domain + return DiscretePseudoValuationSpace(self.domain())(x) + raise ValueError("element does not convert to a discrete valuation in %r"%(self,)) + + def _repr_(self): + r""" + Return a printable representation of this space. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: DiscreteValuationSpace(QQ) # indirect doctest + Discrete valuations on Rational Field + + """ + return "Discrete valuations on %r"%(self.domain(),) + + def _an_element_(self): + r""" + Return the trivial valuation in this space. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: DiscreteValuationSpace(QQ).an_element() # indirect doctest + Trivial valuation on Rational Field + + """ + from trivial_valuation import TrivialDiscreteValuation + return self.__make_element_class__(TrivialDiscreteValuation)(self.domain()) + + class ElementMethods(DiscretePseudoValuationSpace.ElementMethods): + r""" + Provides methods for discrete valuations that are added + automatically to valuations in this space. + + EXAMPLES: + + Here is an example of a method that is automagically added to a + discrete valuation:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(QQ, 2).is_discrete_valuation() # indirect doctest + True + + """ + def is_discrete_valuation(self): + r""" + Return whether this valuation is a discrete valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(QQ, 2).is_discrete_valuation() + True + + """ + return True + + def _test_no_infinite_nonzero(self, **options): + r""" + Check that only zero is sent to infinity. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: v._test_no_infinite_nonzero() + + """ + from sage.rings.all import infinity + tester = self._tester(**options) + for x in tester.some_elements(self.domain().some_elements()): + if self(x) is infinity: + tester.assertEqual(x, 0) diff --git a/value_group.py b/value_group.py new file mode 100644 index 00000000000..26780b09ef4 --- /dev/null +++ b/value_group.py @@ -0,0 +1,355 @@ +# -*- coding: utf-8 -*- +r""" +Value groups of discrete valuations + +This file defines additive subgroups of \QQ generated by a rational number and +related structures. + +EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(QQ, 2).value_group() + Additive Abelian Group generated by 1 + +AUTHORS: + +- Julian Rueth (2013-09-06): initial version + +""" +#***************************************************************************** +# Copyright (C) 2013-2016 Julian Rüth +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) +import sys, os +if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: + sys.path.append(os.getcwd()) + sys.path.append(os.path.dirname(os.getcwd())) + +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.rings.all import QQ, ZZ, infinity + +class DiscreteValuationCodomain(UniqueRepresentation, Parent): + r""" + The codomain of discrete valuations, the rational numbers extended by + `\infty`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: C = DiscreteValuationCodomain(); C + Codomain of Discrete Valuations + + TESTS:: + + sage: TestSuite(C).run() + + """ + def __init__(self): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: isinstance(pAdicValuation(QQ, 2).codomain(), DiscreteValuationCodomain) + True + + """ + from sage.sets.finite_enumerated_set import FiniteEnumeratedSet + from sage.categories.additive_monoids import AdditiveMonoids + Parent.__init__(self, facade=(QQ, FiniteEnumeratedSet([infinity])), category=AdditiveMonoids()) + + def _element_constructor_(self, x): + r""" + Create an element from ``x``. + + INPUT: + + - ``x`` -- a rational number or `\infty` + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: DiscreteValuationCodomain()(0) + 0 + sage: DiscreteValuationCodomain()(infinity) + +Infinity + sage: DiscreteValuationCodomain()(-infinity) + Traceback (most recent call last): + ... + ValueError: must be a rational number or infinity + + """ + if x is infinity: + return x + if x not in QQ: + raise ValueError("must be a rational number or infinity") + from sage.misc.functional import coerce + return coerce(QQ, x) + + def _repr_(self): + r""" + Return a printable representation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: DiscreteValuationCodomain() # indirect doctest + Codomain of Discrete Valuations + + """ + return "Codomain of Discrete Valuations" + +class DiscreteValueGroup(UniqueRepresentation, Parent): + r""" + The value group of a discrete valuation, an additive subgroup of \QQ + generated by ``generator``. + + INPUT: + + - ``generator`` -- a rational number + + .. NOTE:: + + We do not rely on the functionality provided by additive abelian groups + in Sage since these require the underlying set to be the integers. + Therefore, we roll our own \Z-module here. + We could have used :class:`AdditiveAbelianGroupWrapper` here, but it + seems to be somewhat outdated. In particular, generic group + functionality should now come from the category and not from the + super-class. A facade of \Q appeared to be the better approach. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: D1 = DiscreteValueGroup(0); D1 + Trivial Additive Abelian Group + sage: D2 = DiscreteValueGroup(4/3); D2 + Additive Abelian Group generated by 4/3 + sage: D3 = DiscreteValueGroup(-1/3); D3 + Additive Abelian Group generated by 1/3 + + TESTS:: + + sage: TestSuite(D1).run() + sage: TestSuite(D2).run() + sage: TestSuite(D3).run() + + """ + @staticmethod + def __classcall__(cls, generator): + r""" + Normalizes ``generator`` to a positive rational so that this is a + unique parent. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: DiscreteValueGroup(1) is DiscreteValueGroup(-1) + True + + """ + from sage.misc.functional import coerce + generator = coerce(QQ, generator) + generator = generator.abs() + return super(DiscreteValueGroup, cls).__classcall__(cls, generator) + + def __init__(self, generator): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: isinstance(DiscreteValueGroup(0), DiscreteValueGroup) + True + + """ + from sage.categories.modules import Modules + from sage.categories.additive_monoids import AdditiveMonoids + self._generator = generator + + # We can not set the facade to DiscreteValuationCodomain since there + # are some issues with iterated facades currently + Parent.__init__(self, facade=QQ, category=Modules(ZZ)) + + def _element_constructor_(self, x): + r""" + Create an element in this group from ``x``. + + INPUT: + + - ``x`` -- a rational number + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: DiscreteValueGroup(0)(0) + 0 + sage: DiscreteValueGroup(0)(1) + Traceback (most recent call last): + ... + ValueError: `1` is not in Trivial Additive Abelian Group. + sage: DiscreteValueGroup(1)(1) + 1 + sage: DiscreteValueGroup(1)(1/2) + Traceback (most recent call last): + ... + ValueError: `1/2` is not in Additive Abelian Group generated by 1. + + """ + from sage.misc.functional import coerce + x = coerce(QQ, x) + if x == 0 or (self._generator != 0 and x/self._generator in ZZ): + return x + + raise ValueError("`{0}` is not in {1}.".format(x,self)) + + def _repr_(self): + r""" + Return a printable representation for this group. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: DiscreteValueGroup(0) # indirect doctest + Trivial Additive Abelian Group + + """ + if self._generator.is_zero(): + return "Trivial Additive Abelian Group" + return "Additive Abelian Group generated by %r"%(self._generator,) + + def __add__(self, other): + r""" + Return the subgroup of \QQ generated by this group and ``other``. + + INPUT: + + - ``other`` -- a discrete value group or a rational number + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: D = DiscreteValueGroup(1/2) + sage: D + 1/3 + Additive Abelian Group generated by 1/6 + sage: D + D + Additive Abelian Group generated by 1/2 + sage: D + 1 + Additive Abelian Group generated by 1/2 + sage: DiscreteValueGroup(2/7) + DiscreteValueGroup(4/9) + Additive Abelian Group generated by 2/63 + + """ + if not isinstance(other, DiscreteValueGroup): + from sage.structure.element import is_Element + if is_Element(other) and QQ.has_coerce_map_from(other.parent()): + return self + DiscreteValueGroup(other) + raise ValueError("`other` must be a DiscreteValueGroup or a rational number") + return DiscreteValueGroup(self._generator.gcd(other._generator)) + + def _mul_(self, other, switch_sides=False): + r""" + Return the group generated by ``other`` times the generator of this + group. + + INPUT: + + - ``other`` -- a rational number + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: D = DiscreteValueGroup(1/2) + sage: 1/2 * D + Additive Abelian Group generated by 1/4 + sage: D * (1/2) + Additive Abelian Group generated by 1/4 + sage: D * 0 + Trivial Additive Abelian Group + + """ + from sage.misc.functional import coerce + other = coerce(QQ, other) + return DiscreteValueGroup(self._generator*other) + + def index(self, other): + r""" + Return the index of ``other`` in this group. + + INPUT: + + - ``other`` -- a subgroup of this group + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: DiscreteValueGroup(3/8).index(DiscreteValueGroup(3)) + 8 + sage: DiscreteValueGroup(3).index(DiscreteValueGroup(3/8)) + Traceback (most recent call last): + ... + ValueError: `other` must be a subgroup of this group + sage: DiscreteValueGroup(3).index(DiscreteValueGroup(0)) + +Infinity + sage: DiscreteValueGroup(0).index(DiscreteValueGroup(0)) + 1 + sage: DiscreteValueGroup(0).index(DiscreteValueGroup(3)) + Traceback (most recent call last): + ... + ValueError: `other` must be a subgroup of this group + + """ + if not isinstance(other, DiscreteValueGroup): + raise ValueError("`other` must be a DiscreteValueGroup") + if other._generator not in self: + raise ValueError("`other` must be a subgroup of this group") + if other._generator == 0: + if self._generator == 0: + return ZZ(1) + else: + return infinity + return ZZ(other._generator / self._generator) + + def numerator(self): + r""" + Return the numerator of a generator of this group. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: DiscreteValueGroup(3/8).numerator() + 3 + + """ + return self._generator.numerator() + + def denominator(self): + r""" + Return the denominator of a generator of this group. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: DiscreteValueGroup(3/8).denominator() + 8 + + """ + return self._generator.denominator() + + def gen(self): + r""" + Return a generator of this group. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: DiscreteValueGroup(-3/8).gen() + 3/8 + + """ + return self._generator From c6aa68eb719a06e751a11fdc0c8e0b9b8d9c420f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 16 Oct 2016 23:31:39 -0500 Subject: [PATCH 033/740] towards better support for valuations on function fields --- __init__.py | 47 ++++++- augmented_valuation.py | 7 +- developing_valuation.py | 8 +- function_field_valuation.py | 257 +++++++++++++++++++++++++++++------- gauss_valuation.py | 10 +- trivial_valuation.py | 182 +++++++++++++++++-------- valuation_space.py | 208 ++++++++++++++++++++++++++++- value_group.py | 12 ++ 8 files changed, 613 insertions(+), 118 deletions(-) diff --git a/__init__.py b/__init__.py index 53cd88e5303..acd82e0beed 100644 --- a/__init__.py +++ b/__init__.py @@ -1,9 +1,14 @@ from .valuation_space import DiscretePseudoValuationSpace, DiscreteValuationSpace -from .trivial_valuation import TrivialDiscreteValuation, TrivialDiscretePseudoValuation +from .trivial_valuation import TrivialValuation, TrivialPseudoValuation from .padic_valuation import pAdicValuation from .gauss_valuation import GaussValuation from .value_group import DiscreteValuationCodomain, DiscreteValueGroup -import valuation, padic_valuation, mac_lane +from .function_field_valuation import FunctionFieldValuation + +# fix unpickling and type checks of classes (otherwise, the instances of the +# local file and the instances that come from the mac_lane import define +# different types) +from .trivial_valuation import TrivialDiscreteValuation, TrivialDiscretePseudoValuation # ================= # MONKEY PATCH SAGE @@ -13,7 +18,45 @@ # Implement Qp/Zp.valuation sage.rings.padics.padic_generic.pAdicGeneric.valuation = lambda self: pAdicValuation(self) +# Fix contains check of rational fuction fields +r""" +sage: K. = FunctionField(QQ) +sage: K(1) in QQ +True +sage: K(x) in K._ring +True +""" +def to_polynomial(self, x): + R = x.parent()._ring + K = x.parent().constant_base_field() + if x.denominator() in K: + return x.numerator()/K(x.denominator()) + raise ValueError("Only polynomials can be converted to the underlying polynomial ring") + +sage.rings.function_field.function_field.RationalFunctionField._to_polynomial = to_polynomial +sage.rings.function_field.function_field.RationalFunctionField.__old_init__ = sage.rings.function_field.function_field.RationalFunctionField.__init__ +def __init__(self, *args, **kwargs): + self.__old_init__(*args, **kwargs) + from sage.categories.morphism import SetMorphism + self._ring.register_conversion(SetMorphism(self.Hom(self._ring), self._to_polynomial)) + +sage.rings.function_field.function_field.RationalFunctionField.__init__ = __init__ +del(__init__) +del(to_polynomial) + +import imp, sys +# register modules at some standard places so imports work as exepcted +r""" +sage: from sage.rings.valuation.gauss_valuation import GaussValuation +""" +sage.rings.valuation = sys.modules['sage.rings.valuation'] = imp.new_module('sage.rings.valuation') +sage.rings.valuation.gauss_valuation = sys.modules['sage.rings.valuation.gauss_valuation'] = gauss_valuation +sage.rings.valuation.valuation = sys.modules['sage.rings.valuation.valuation'] = valuation +sage.rings.valuation.valuation_space = sys.modules['sage.rings.valuation.valuation_space'] = valuation_space + # fix unpickling of factories from sage.structure.factory import register_factory_unpickle register_factory_unpickle("pAdicValuation", pAdicValuation) register_factory_unpickle("GaussValuation", GaussValuation) +register_factory_unpickle("TrivialValuation", TrivialValuation) +register_factory_unpickle("TrivialPseudoValuation", TrivialPseudoValuation) diff --git a/augmented_valuation.py b/augmented_valuation.py index 52d9b8d3eb7..92419c04063 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -83,7 +83,12 @@ def __init__(self, v, phi, mu, check=True): if mu <= v(phi): raise ValueError("the value of the key polynomial must strictly increase but `%s` does not exceed `%s`."%(phi, v(phi))) - DevelopingValuation.__init__(self, v.domain(), phi) + if mu < infinity: + parent = v.parent() + else: + from valuation_space import DiscretePseudoValuationSpace + parent = DiscretePseudoValuationSpace(v.domain()) + DevelopingValuation.__init__(self, parent, phi) self._base_valuation = v self._mu = mu diff --git a/developing_valuation.py b/developing_valuation.py index a2fdb9ee8f6..02c98cbc308 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -35,7 +35,7 @@ sys.path.append(os.getcwd()) sys.path.append(os.path.dirname(os.getcwd())) -from .valuation import DiscretePseudoValuation +from valuation import DiscretePseudoValuation from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method @@ -80,7 +80,7 @@ class DevelopingValuation(DiscretePseudoValuation): `(1 + O(2^5))*x`-adic valuation of Univariate Polynomial Ring in x over 2-adic Ring with capped relative precision 5 """ - def __init__(self, domain, phi): + def __init__(self, parent, phi): """ Initialization. @@ -94,6 +94,8 @@ def __init__(self, domain, phi): """ + domain = parent.domain() + if phi.parent() is not domain: raise ValueError("phi must be in the domain of the valuation") if phi.is_constant(): @@ -101,7 +103,7 @@ def __init__(self, domain, phi): if not phi.leading_coefficient().is_one(): raise ValueError("phi must be monic") - DiscretePseudoValuation.__init__(self, domain) + DiscretePseudoValuation.__init__(self, parent) self._phi = phi diff --git a/function_field_valuation.py b/function_field_valuation.py index ffeacf47e22..655b0d16055 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -1,14 +1,215 @@ -from discrete_valuation import DiscreteValuation +# -*- coding: utf-8 -*- +r""" +Discrete valuations on function fields + +AUTHORS: + +- Julian Rueth (2016-10-16): initial version + +""" +#***************************************************************************** +# Copyright (C) 2016 Julian Rüth +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) +import sys, os +if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: + sys.path.append(os.getcwd()) + sys.path.append(os.path.dirname(os.getcwd())) + +from sage.structure.factory import UniqueFactory + +from valuation import DiscretePseudoValuation +from trivial_valuation import TrivialValuation + +class FunctionFieldValuationFactory(UniqueFactory): + r""" + Create a valuation on ``domain`` corresponding to ``prime``. + + INPUT: + + - ``domain`` -- a function field + + - ``prime`` -- a place of the function field or a valuation on a subring + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + + We create a valuation that correspond to a finite rational place of a function + field:: + + sage: v = FunctionFieldValuation(K, 1); v + sage: v(x) + 0 + sage: v(x - 1) + 1 + + A place can also be specified with an irreducible polynomial:: + + sage: v = FunctionFieldValuation(K, x - 1); v + + Similarly, for a finite non-rational place:: + + sage: v = FunctionFieldValuation(K, x^2 + 1); v + sage: v(x^2 + 1) + 1 + sage: v(x) + 0 + + Or for the infinite place:: + + sage: v = FunctionFieldValuation(K, 1/x); v + sage: v(x) + -1 + + Instead of specifying a generator of a place, we can define a valuation on a + rational function field by giving a discrete valuation on the underlying + polynomial ring:: + + sage: R. = QQ[] + sage: w = GaussValuation(R, TrivialValuation(QQ)).extend(x - 1, 1) + sage: v = FunctionFieldValuation(K, w); v + + Note that this allows us to specify valuations which do not correspond to a + place of the function field:: + + sage: w = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = FunctionFieldValuation(K, w); v + + """ + def create_key(self, domain, prime): + r""" + Create a unique key which identifies the valuation given by ``prime`` + on ``domain``. + + TESTS: + + We specify a valuation on a function field by two different means and + get the same object:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, x - 1) # indirect doctest + + sage: R. = QQ[] + sage: w = GaussValuation(R, TrivialValuation(QQ)).extend(x - 1, 1) + sage: FunctionFieldValuation(K, w) is v + True + + """ + from sage.categories.function_fields import FunctionFields + from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace + if domain not in FunctionFields(): + raise ValueError("Domain must be a function field.") + + if prime in domain: + # prime defines a place + return self.create_key_from_place(domain, prime) + elif prime in DiscretePseudoValuationSpace(domain._ring): + # prime is a discrete (pseudo-)valuation on the polynomial ring + # that the domain is constructed from + return self.create_key_from_valuation(domain, prime) + elif prime in DiscretePseudoValuationSpace(domain.base_field()): + # prime is a discrete (pseudo-)valuation on the domain or a subfield + # that has a unique extension + return self.create_key_from_valuation(domain, prime) + elif domain.base_field() is not domain: + # prime might define a valuation on a subfield of domain that has a + # unique extension + base_valuation = FunctionFieldValuation(domain.base_field(), prime) + return create_key(domain, base_valuation) + + raise NotImplementedError("argument must be a place or a pseudo-valuation on an underlying polynomial ring") + + def create_key_from_place(self, domain, generator): + r""" + Create a unique key which identifies the valuation at the place + specified by ``generator``. + + TESTS: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, 1/x) # indirect doctest -from sage.rings.all import ZZ, infinity + """ + if generator not in domain.base_field(): + raise NotImplementedError("a place must be defined over a rational function field") + + if domain.base_field() is not domain: + # if this is an extension field, construct the unique place over + # the place on the subfield + return self.create_key(domain.base_field(), FunctionFieldValuation(domain.base_field(), generator)) + + if generator in domain.constant_base_field(): + # generator is a constant, we associate to it the place which + # corresponds to the polynomial (x - generator) + return self.create_key(domain, domain.gen() - generator) + + if generator in domain._ring: + # generator is a polynomial + generator = domain._ring(generator) + if not generator.is_monic() or not generator.is_irreducible(): + raise ValueError("place must be defined by a monic irreducible polynomial") + # we construct the corresponding valuation on the polynomial ring + # with v(generator) = 1 + from sage.rings.valuation.gauss_valuation import GaussValuation + valuation = GaussValuation(domain._ring, TrivialValuation(domain.constant_base_field())).extension(generator, 1) + return self.create_key(domain, valuation) + elif generator == ~domain.gen(): + # generator is 1/x, the infinite place + return (domain, ~domain.gen()) + else: + raise ValueError("a place must be given by an irreducible polynomial or the inverse of the generator") -class RationalFunctionFieldValuation(DiscreteValuation): + def create_key_from_valuation(self, domain, valuation): + r""" + Create a unique key which identifies the valuation which extends + ``valuation``. + + TESTS: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = QQ[] + sage: w = GaussValuation(R, TrivialValuation(QQ)).extend(x - 1, 1) + sage: v = FunctionFieldValuation(K, w) + + """ + raise NotImplementedError() + + def create_object(self, key, **extra_args): + r""" + Create the valuation specified by ``key``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: w = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = FunctionFieldValuation(K, w); v + + """ + raise NotImplementedError() + +FunctionFieldValuation = FunctionFieldValuationFactory("FunctionFieldValuation") + +from sage.rings.all import QQ, ZZ, infinity + +class RationalFunctionFieldValuation(DiscretePseudoValuation): def __init__(self, domain, base_valuation): if base_valuation.domain() is not domain._ring: raise ValueError self._base_valuation = base_valuation - DiscreteValuation.__init__(self, domain) + DiscretePseudoValuation.__init__(self, domain) def _call_(self, x): return self._base_valuation(x.numerator()) - self._base_valuation(x.denominator()) @@ -39,6 +240,7 @@ def value_group(self): return self._base_valuation.value_group() def residue_field(self): + # This is incorrect. For classical valuations on function fields, this is not correct. from sage.rings.all import FunctionField return FunctionField(self._base_valuation.residue_field(), names=self.domain().variable_names()) @@ -165,7 +367,7 @@ def extension(self, L, algorithm="mac_lane"): def uniformizer(self): return self.domain()(self._base_valuation.uniformizer()) -class FunctionFieldPolymodValuation(DiscreteValuation): +class FunctionFieldPolymodValuation(DiscretePseudoValuation): def __init__(self, domain, base_valuation): from sage.rings.all import infinity if base_valuation.domain() is not domain._ring: @@ -174,7 +376,7 @@ def __init__(self, domain, base_valuation): raise ValueError self._base_valuation = base_valuation - DiscreteValuation.__init__(self, domain) + DiscretePseudoValuation.__init__(self, domain) def _call_(self, x): return self._base_valuation(x.element()) @@ -251,46 +453,3 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): def _repr_(self): return "Valuation on rational function field induced by %s"%self._base_valuation - -from sage.structure.unique_representation import UniqueRepresentation -class TrivialValuation(UniqueRepresentation, DiscreteValuation): - from gauss_valuation import classmaker - __metaclass__ = classmaker() - - def __init__(self, domain): - DiscreteValuation.__init__(self, domain) - - def _call_(self, x): - if x.is_zero(): - return infinity - else: - return ZZ.zero() - - def shift(self, x, v): - if x.parent() is not self.domain(): - raise ValueError - - if v == 0: - return x - else: - raise ValueError - - def value_group(self): - return self._value_group(0) - - def residue_field(self): - if self.domain().is_field(): - return self.domain() - else: - raise NotImplementedError("residue ring is not a field") - - def reduce(self, x): - if x.parent() is not self.domain(): - raise ValueError - - return x - - lift = reduce - - def _repr_(self): - return "Trivial valuation" diff --git a/gauss_valuation.py b/gauss_valuation.py index 8c160a0d3c2..25102a148d8 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -121,7 +121,10 @@ def create_object(self, version, key, **extra_args): Gauss valuation induced by 2-adic valuation """ - return GaussValuation_generic(*key) + domain, v = key + from sage.rings.valuation.valuation_space import DiscreteValuationSpace + parent = DiscreteValuationSpace(domain) + return GaussValuation_generic(parent, v) GaussValuation = GaussValuationFactory("GaussValuation") @@ -153,7 +156,7 @@ class GaussValuation_generic(DevelopingValuation): sage: TestSuite(v).run(skip="_test_category") """ - def __init__(self, domain, v): + def __init__(self, parent, v): """ Initialization. @@ -169,7 +172,8 @@ def __init__(self, domain, v): True """ - DevelopingValuation.__init__(self, domain, domain.gen()) + domain = parent.domain() + DevelopingValuation.__init__(self, parent, domain.gen()) self._base_valuation = v diff --git a/trivial_valuation.py b/trivial_valuation.py index 76b139e6df8..e823d2525ab 100644 --- a/trivial_valuation.py +++ b/trivial_valuation.py @@ -2,14 +2,10 @@ r""" Trivial valuations -This module provides trivial valuations which mainly exist to be able to -provide an implementation for -:meth:`DiscretePseudoValuationSpace._an_element_`. - EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: v = DiscreteValuationSpace(QQ).an_element(); v + sage: v = TrivialValuation(QQ); v Trivial valuation on Rational Field sage: v(1) 0 @@ -20,7 +16,8 @@ directly since this gives the wrong inheritance structure on the resulting objects:: - sage: v = TrivialDiscretePseudoValuation(QQ) + sage: H = DiscretePseudoValuationSpace(QQ) + sage: v = TrivialDiscretePseudoValuation(H) sage: v._test_category() Traceback (most recent call last): ... @@ -29,13 +26,13 @@ Instead, the valuations need to be created through the ``__make_element_class__`` of the containing space:: - sage: H = DiscretePseudoValuationSpace(QQ) - sage: v = H.__make_element_class__(TrivialDiscretePseudoValuation)(QQ) + sage: v = H.__make_element_class__(TrivialDiscretePseudoValuation)(H) sage: v._test_category() -Using ``an_element`` provides a much more readable shortcut for this:: +The factories ``TrivialValuation`` and ``TrivialPseudoValuation`` provide the +right inheritance structure:: - sage: v = H.an_element() + sage: v = TrivialPseudoValuation(QQ) sage: v._test_category() AUTHORS: @@ -59,55 +56,74 @@ sys.path.append(os.path.dirname(os.getcwd())) from valuation import DiscretePseudoValuation +from valuation_space import DiscreteValuationSpace, DiscretePseudoValuationSpace +from sage.structure.factory import UniqueFactory -class TrivialDiscretePseudoValuation_base(DiscretePseudoValuation): +class TrivialValuationFactory(UniqueFactory): r""" - Base class for code shared by trivial valuations. + Create a trivial valuation on ``domain``. EXAMPLES:: - sage: from mac_lane import * # optional: standalone - sage: v = DiscretePseudoValuationSpace(ZZ).an_element(); v # indirect doctest - Trivial pseudo-valuation on Integer Ring - - TESTS:: - - sage: TestSuite(v).run() + sage: v = TrivialValuation(QQ); v + Trivial valuation on Rational Field + sage: v(1) + 0 """ - def __reduce__(self): + def __init__(self, clazz, parent, *args, **kwargs): r""" - Return pickling information for this valuation. - TESTS:: - sage: from mac_lane import * # optional: standalone - sage: v = DiscretePseudoValuationSpace(ZZ).an_element() - sage: loads(dumps(v)) == v # indirect doctest + sage: isinstance(TrivialValuation, TrivialValuationFactory) True """ - return self.__class__, (self.domain(), ) + UniqueFactory.__init__(self, *args, **kwargs) + self._class = clazz + self._parent = parent - def _eq_(self, other): + def create_key(self, domain): r""" - Return whether this valuation is indistinguishable to the - pseudo-valuation ``other``. + Create a key that identifies this valuation. EXAMPLES:: - sage: from mac_lane import * # optional: standalone - sage: v = DiscretePseudoValuationSpace(ZZ).an_element() - sage: w = DiscretePseudoValuationSpace(ZZ).an_element() - sage: v == w + sage: TrivialValuation(QQ) is TrivialValuation(QQ) # indirect doctest True """ - # other lives in the same space of valuations, say the space of - # discrete valuations on the integers; therefore, it suffices to - # compare the types since there are no paremeters to trivial valuations - return type(self) == type(other) + return domain, + + def create_object(self, version, key, **extra_args): + r""" + Create a trivial valuation from ``key``. + + EXAMPLES:: + + sage: TrivialValuation(QQ) # indirect doctest + Trivial valuation on Rational Field + + """ + domain, = key + parent = self._parent(domain) + return parent.__make_element_class__(self._class)(parent) + +class TrivialDiscretePseudoValuation_base(DiscretePseudoValuation): + r""" + Base class for code shared by trivial valuations. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = TrivialPseudoValuation(ZZ); v + Trivial pseudo-valuation on Integer Ring + + TESTS:: + + sage: TestSuite(v).run() + """ def uniformizer(self): r""" Return a uniformizing element for this valuation. @@ -115,7 +131,7 @@ def uniformizer(self): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: v = DiscretePseudoValuationSpace(QQ).an_element() + sage: v = TrivialPseudoValuation(ZZ) sage: v.uniformizer() Traceback (most recent call last): ... @@ -131,7 +147,7 @@ def is_trivial(self): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: v = DiscretePseudoValuationSpace(QQ).an_element() + sage: v = TrivialPseudoValuation(QQ) sage: v.is_trivial() True @@ -145,7 +161,7 @@ class TrivialDiscretePseudoValuation(TrivialDiscretePseudoValuation_base): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: v = DiscretePseudoValuationSpace(QQ).an_element(); v + sage: v = TrivialPseudoValuation(QQ); v Trivial pseudo-valuation on Rational Field TESTS:: @@ -153,18 +169,17 @@ class TrivialDiscretePseudoValuation(TrivialDiscretePseudoValuation_base): sage: TestSuite(v).run() """ - def __init__(self, domain): + def __init__(self, parent): r""" TESTS:: sage: from mac_lane import * # optional: standalone - sage: v = DiscretePseudoValuationSpace(QQ).an_element() + sage: v = TrivialPseudoValuation(QQ) sage: isinstance(v, TrivialDiscretePseudoValuation) True """ - from .valuation_space import DiscretePseudoValuationSpace - TrivialDiscretePseudoValuation_base.__init__(self, DiscretePseudoValuationSpace(domain)) + TrivialDiscretePseudoValuation_base.__init__(self, parent) def is_discrete_valuation(self): r""" @@ -175,7 +190,7 @@ def is_discrete_valuation(self): Returns ``False`` since this is only a pseudo-valuation:: sage: from mac_lane import * # optional: standalone - sage: v = DiscretePseudoValuationSpace(QQ).an_element() + sage: v = TrivialPseudoValuation(QQ) sage: v.is_discrete_valuation() False @@ -189,7 +204,7 @@ def _call_(self, x): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: v = DiscretePseudoValuationSpace(QQ).an_element() + sage: v = TrivialPseudoValuation(QQ) sage: v(0) +Infinity sage: v(1) @@ -206,7 +221,7 @@ def _repr_(self): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: v = DiscretePseudoValuationSpace(QQ).an_element(); v # indirect doctest + sage: TrivialPseudoValuation(QQ) # indirect doctest Trivial pseudo-valuation on Rational Field """ @@ -221,7 +236,7 @@ def value_group(self): A trivial discrete pseudo-valuation has no value group:: sage: from mac_lane import * # optional: standalone - sage: v = DiscretePseudoValuationSpace(QQ).an_element() + sage: v = TrivialPseudoValuation(QQ) sage: v.value_group() Traceback (most recent call last): ... @@ -230,6 +245,32 @@ def value_group(self): """ raise ValueError("The trivial pseudo-valuation that is infinity everywhere does not have a value group.") + def residue_ring(self): + r""" + Return the residue ring of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: TrivialPseudoValuation(QQ).residue_ring() + + """ + return self.domain().quo(self.one()) + + def reduce(self, x): + r""" + Reduce ``x`` modulo the positive elements of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = TrivialPseudoValuation(QQ) + sage: v.reduce(1) + + """ + self.domain().coerce(x) + return self.residue_ring().zero() + class TrivialDiscreteValuation(TrivialDiscretePseudoValuation_base): r""" The trivial valuation that is zero on non-zero elements. @@ -237,7 +278,7 @@ class TrivialDiscreteValuation(TrivialDiscretePseudoValuation_base): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: v = DiscreteValuationSpace(QQ).an_element(); v # indirect doctest + sage: v = TrivialValuation(QQ); v Trivial valuation on Rational Field TESTS:: @@ -245,18 +286,17 @@ class TrivialDiscreteValuation(TrivialDiscretePseudoValuation_base): sage: TestSuite(v).run() """ - def __init__(self, domain): + def __init__(self, parent): r""" TESTS:: sage: from mac_lane import * # optional: standalone - sage: v = DiscreteValuationSpace(QQ).an_element() + sage: v = TrivialValuation(QQ) sage: isinstance(v, TrivialDiscreteValuation) True """ - from .valuation_space import DiscreteValuationSpace - TrivialDiscretePseudoValuation_base.__init__(self, DiscreteValuationSpace(domain)) + TrivialDiscretePseudoValuation_base.__init__(self, parent) def _call_(self, x): r""" @@ -265,7 +305,7 @@ def _call_(self, x): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: v = DiscreteValuationSpace(QQ).an_element() + sage: v = TrivialValuation(QQ) sage: v(0) +Infinity sage: v(1) @@ -282,7 +322,7 @@ def _repr_(self): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: v = DiscreteValuationSpace(QQ).an_element(); v # indirect doctest + sage: TrivialValuation(QQ) # indirect doctest Trivial valuation on Rational Field """ @@ -297,10 +337,40 @@ def value_group(self): A trivial discrete valuation has a trivial value group:: sage: from mac_lane import * # optional: standalone - sage: v = DiscreteValuationSpace(QQ).an_element() + sage: v = TrivialValuation(QQ) sage: v.value_group() Trivial Additive Abelian Group """ from .value_group import DiscreteValueGroup return DiscreteValueGroup(0) + + def residue_ring(self): + r""" + Return the residue ring of this valuation. + + EXAMPLES:: + + sage: TrivialValuation(QQ).residue_ring() + Rational Field + + """ + return self.domain() + + def reduce(self, x): + r""" + Reduce ``x`` modulo the positive elements of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = TrivialValuation(QQ) + sage: v.reduce(1) + 1 + + """ + return self.domain().coerce(x) + +TrivialValuation = TrivialValuationFactory(TrivialDiscreteValuation, DiscreteValuationSpace, "TrivialValuation") +TrivialPseudoValuation = TrivialValuationFactory(TrivialDiscretePseudoValuation, DiscretePseudoValuationSpace, "TrivialPseudoValuation") + diff --git a/valuation_space.py b/valuation_space.py index d9654fff39c..ce67c7a4504 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -108,8 +108,8 @@ def _an_element_(self): Trivial pseudo-valuation on Rational Field """ - from trivial_valuation import TrivialDiscretePseudoValuation - return self.__make_element_class__(TrivialDiscretePseudoValuation)(self.domain()) + from trivial_valuation import TrivialPseudoValuation + return TrivialPseudoValuation(self.domain()) def _repr_(self): r""" @@ -337,6 +337,84 @@ def value_group(self, **options): """ return DiscreteValueGroup(self(self.uniformizer)) + def shift(self, x, s): + r""" + Return a modified version of ``x`` whose valuation is increased by + ``s``. + + The element returned has essentially the same reduction, i.e., if + ``x`` has valuation `v`, then the reduction of ``x`` in the residue + ring of elements of valuation `\ge v` module elements of valuation + `> v` is naturally the same as the reduction of ``shift(x, s)`` in + the correspoding residue ring of elements of valuation `\ge v + s`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 2) + sage: v.shift(1, 10) + 1024 + sage: v.shift(1, -10) + + """ + x = self.domain().coerce(x) + from sage.rings.all import QQ + s = QQ.coerce(s) + if s == 0: + return x + if s not in self.value_group(): + raise ValueError("s must be in the value group of this valuation") + return self.domain()(x * (self.uniformizer()**n)) + + @abstract_method + def residue_ring(self): + r""" + Return the residue ring of this valuation, i.e., the elements of + non-negative valuation module the elements of positive valuation. + + This is identical to :meth:`residue_field` when a residue field + exists. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(QQ, 2).residue_ring() + + sage: TrivialValuation(QQ).residue_ring() + Rational Field + + Note that a residue ring always exists, even when a residue field + may not:: + + sage: TrivialPseudoValuation(QQ).residue_ring() + Zero ring + + sage: TrivialValuation(ZZ).residue_ring() + + sage: GaussValuation(ZZ['x'], pAdicValuation(ZZ, 2)).residue_ring() + + """ + + @abstract_method + def reduce(self, x): + r""" + Return the image of ``x`` in the :meth:`residue_ring` of this + valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 2) + sage: v.reduce(1) + 0 + sage: v.reduce(1) + 1 + sage: v.reduce(1/3) + 1 + sage: v.reduce(1/2) + + """ + def _test_add(self, **options): r""" Check that the (strict) triangle equality is satisfied for the @@ -448,6 +526,77 @@ def _test_value_group(self, **options): # check that the uniformizer generates the value group tester.assertEqual(self.value_group().gen(), self(self.uniformizer())) + def _test_shift(self, **options): + r""" + Check correctness of shifts. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: v._test_shift() + + """ + tester = self._tester(**options) + + if self.is_trivial(): + # trivial valuations can not perform non-trivial shifts + return + + S = tester.some_elements(self.domain().some_elements()) + V = tester.some_elements(self.value_group()) + from sage.categories.cartesian_product import cartesian_product + for x, n in tester.some_elements(cartesian_product([S,V])): + v = self(x) + if n < 0 and -n > v and not self.domain() in Fields(): + continue + tester.assertEqual(self.shift(x, n), v + n) + # shifts preserve reductions + tester.assertEqual(self.reduce(self.shift(self.shift(x, n), -n)), self.reduce(x)) + + def _test_residue_ring(self, **options): + r""" + Check the correctness of residue fields. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: v._test_residue_ring() + + """ + tester = self._tester(**options) + + c = self.residue_ring().characteristic() + if c != 0: + tester.assertGreater(self(c), 0) + + def _test_reduce(self, **options): + r""" + Check the correctness of reductions. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: v._test_reduce() + + """ + tester = self._tester(**options) + + for x in tester.some_elements(self.domain().some_elements()): + if self(x) < 0: + with tester.assertRaises(ValueError): + self.reduce(x) + continue + if self(x) == 0: + tester.assertNotEqual(self.reduce(x), 0) + if x.is_unit() and ~x in self.domain(): + tester.assertTrue(self.reduce(x).is_unit()) + tester.assertEqual(~self.reduce(x), self.reduce(~x)) + if self(x) > 0: + tester.assertEqual(self.reduce(x), 0) + class DiscreteValuationSpace(DiscretePseudoValuationSpace): r""" The space of discrete valuations on ``domain``. @@ -538,8 +687,8 @@ def _an_element_(self): Trivial valuation on Rational Field """ - from trivial_valuation import TrivialDiscreteValuation - return self.__make_element_class__(TrivialDiscreteValuation)(self.domain()) + from trivial_valuation import TrivialValuation + return TrivialValuation(self.domain()) class ElementMethods(DiscretePseudoValuationSpace.ElementMethods): r""" @@ -569,6 +718,33 @@ def is_discrete_valuation(self): """ return True + def residue_field(self): + r""" + Return the residue field of this valuation, i.e., the elements of + non-negative valuation module the elements of positive valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(QQ, 2).residue_field() + + sage: TrivialValuation(QQ).residue_field() + Rational Field + + Note that discrete valuations do not always define a residue + field:: + + sage: TrivialValuation(ZZ).residue_field() + + sage: GaussValuation(ZZ['x'], pAdicValuation(ZZ, 2)).residue_field() + + """ + ret = self.residue_ring() + from sage.categories.fields import Fields + if ret not in Fields(): + raise ValueError("The residue ring of this valuation is not a field.") + return ret + def _test_no_infinite_nonzero(self, **options): r""" Check that only zero is sent to infinity. @@ -585,3 +761,27 @@ def _test_no_infinite_nonzero(self, **options): for x in tester.some_elements(self.domain().some_elements()): if self(x) is infinity: tester.assertEqual(x, 0) + + def _test_residue_field(self, **options): + r""" + Check the correctness of residue fields. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: v._test_residue_field() + + """ + tester = self._tester(**options) + try: + k = self.residue_field() + except ValueError: + from sage.categories.fields import Fields + # a discrete valuation on a field has a residue field + tester.assertFalse(self.domain() in Fields()) + return + + c = self.residue_field().characteristic() + if c != 0: + tester.assertGreater(self(c), 0) diff --git a/value_group.py b/value_group.py index 26780b09ef4..6a0f421c8ba 100644 --- a/value_group.py +++ b/value_group.py @@ -353,3 +353,15 @@ def gen(self): """ return self._generator + + def some_elements(self): + r""" + Return some typical elements in this group. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: DiscreteValueGroup(-3/8).some_elements() + + """ + return [self._generator, -self._generator] + [x for x in QQ.some_elements() if x in self] From 3e5ef857a512c90f2ed72f5ccce1053745255544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 16 Oct 2016 23:45:42 -0500 Subject: [PATCH 034/740] make some function field valuations work again --- __init__.py | 2 ++ function_field_valuation.py | 31 +++++++++++++++++++++++++------ value_group.py | 1 + 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/__init__.py b/__init__.py index acd82e0beed..c5e22d46829 100644 --- a/__init__.py +++ b/__init__.py @@ -4,6 +4,7 @@ from .gauss_valuation import GaussValuation from .value_group import DiscreteValuationCodomain, DiscreteValueGroup from .function_field_valuation import FunctionFieldValuation +from .augmented_valuation import AugmentedValuation # fix unpickling and type checks of classes (otherwise, the instances of the # local file and the instances that come from the mac_lane import define @@ -53,6 +54,7 @@ def __init__(self, *args, **kwargs): sage.rings.valuation.gauss_valuation = sys.modules['sage.rings.valuation.gauss_valuation'] = gauss_valuation sage.rings.valuation.valuation = sys.modules['sage.rings.valuation.valuation'] = valuation sage.rings.valuation.valuation_space = sys.modules['sage.rings.valuation.valuation_space'] = valuation_space +sage.rings.valuation.augmented_valuation = sys.modules['sage.rings.valuation.augmented_valuation'] = augmented_valuation # fix unpickling of factories from sage.structure.factory import register_factory_unpickle diff --git a/function_field_valuation.py b/function_field_valuation.py index 655b0d16055..554daf7a275 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -46,6 +46,7 @@ class FunctionFieldValuationFactory(UniqueFactory): field:: sage: v = FunctionFieldValuation(K, 1); v + (x - 1)-adic valuation sage: v(x) 0 sage: v(x - 1) @@ -54,10 +55,12 @@ class FunctionFieldValuationFactory(UniqueFactory): A place can also be specified with an irreducible polynomial:: sage: v = FunctionFieldValuation(K, x - 1); v + (x - 1)-adic valuation Similarly, for a finite non-rational place:: sage: v = FunctionFieldValuation(K, x^2 + 1); v + (x^2 + 1)-adic valuation sage: v(x^2 + 1) 1 sage: v(x) @@ -180,12 +183,15 @@ def create_key_from_valuation(self, domain, valuation): sage: K. = FunctionField(QQ) sage: R. = QQ[] sage: w = GaussValuation(R, TrivialValuation(QQ)).extend(x - 1, 1) - sage: v = FunctionFieldValuation(K, w) + sage: v = FunctionFieldValuation(K, w) # indirect doctest """ + if valuation.domain() is domain._ring: + # TODO: check validity + return domain, valuation raise NotImplementedError() - def create_object(self, key, **extra_args): + def create_object(self, version, key, **extra_args): r""" Create the valuation specified by ``key``. @@ -194,22 +200,29 @@ def create_object(self, key, **extra_args): sage: from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: w = GaussValuation(R, pAdicValuation(QQ, 2)) - sage: v = FunctionFieldValuation(K, w); v + sage: v = FunctionFieldValuation(K, w); v # indirect doctest """ - raise NotImplementedError() + domain, valuation = key + if valuation in domain: + assert(valuation == ~domain.gen()) + raise NotImplementedError + from sage.rings.valuation.valuation_space import DiscreteValuationSpace + parent = DiscreteValuationSpace(domain) + return parent.__make_element_class__(RationalFunctionFieldValuation)(parent, valuation) FunctionFieldValuation = FunctionFieldValuationFactory("FunctionFieldValuation") from sage.rings.all import QQ, ZZ, infinity class RationalFunctionFieldValuation(DiscretePseudoValuation): - def __init__(self, domain, base_valuation): + def __init__(self, parent, base_valuation): + domain = parent.domain() if base_valuation.domain() is not domain._ring: raise ValueError self._base_valuation = base_valuation - DiscretePseudoValuation.__init__(self, domain) + DiscretePseudoValuation.__init__(self, parent) def _call_(self, x): return self._base_valuation(x.numerator()) - self._base_valuation(x.denominator()) @@ -348,6 +361,12 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): leaves.extend(v.mac_lane_step(G)) def _repr_(self): + from sage.rings.valuation.augmented_valuation import AugmentedValuation + if isinstance(self._base_valuation, AugmentedValuation): + from sage.rings.valuation.gauss_valuation import GaussValuation + if self._base_valuation._base_valuation == GaussValuation(self.domain()._ring, TrivialValuation(self.domain().constant_field())): + if self._base_valuation._mu == 1: + return "(%r)-adic valuation"%(self._base_valuation.phi()) return "Valuation on rational function field induced by %s"%self._base_valuation def extension(self, L, algorithm="mac_lane"): diff --git a/value_group.py b/value_group.py index 6a0f421c8ba..6a67cd66f16 100644 --- a/value_group.py +++ b/value_group.py @@ -362,6 +362,7 @@ def some_elements(self): sage: from mac_lane import * # optional: standalone sage: DiscreteValueGroup(-3/8).some_elements() + [3/8, -3/8, 0, 42, 3/2, -3/2, 9/8, -9/8] """ return [self._generator, -self._generator] + [x for x in QQ.some_elements() if x in self] From c723f04d84dcc1951bae437c50f34ab49825a7a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 17 Oct 2016 08:31:01 -0500 Subject: [PATCH 035/740] support valuations at infinite places --- __init__.py | 3 + augmented_valuation.py | 24 +-- function_field_valuation.py | 296 ++++++++++++++++++++++++++++++------ gauss_valuation.py | 23 +-- padic_valuation.py | 56 +------ trivial_valuation.py | 4 +- valuation_space.py | 60 ++++++-- value_group.py | 17 ++- 8 files changed, 342 insertions(+), 141 deletions(-) diff --git a/__init__.py b/__init__.py index c5e22d46829..879d7543c1b 100644 --- a/__init__.py +++ b/__init__.py @@ -10,6 +10,7 @@ # local file and the instances that come from the mac_lane import define # different types) from .trivial_valuation import TrivialDiscreteValuation, TrivialDiscretePseudoValuation +from .function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base # ================= # MONKEY PATCH SAGE @@ -55,6 +56,7 @@ def __init__(self, *args, **kwargs): sage.rings.valuation.valuation = sys.modules['sage.rings.valuation.valuation'] = valuation sage.rings.valuation.valuation_space = sys.modules['sage.rings.valuation.valuation_space'] = valuation_space sage.rings.valuation.augmented_valuation = sys.modules['sage.rings.valuation.augmented_valuation'] = augmented_valuation +sage.rings.function_field.function_field_valuation = sys.modules['sage.rings.function_field.function_field_valuation'] = function_field_valuation # fix unpickling of factories from sage.structure.factory import register_factory_unpickle @@ -62,3 +64,4 @@ def __init__(self, *args, **kwargs): register_factory_unpickle("GaussValuation", GaussValuation) register_factory_unpickle("TrivialValuation", TrivialValuation) register_factory_unpickle("TrivialPseudoValuation", TrivialPseudoValuation) +register_factory_unpickle("FunctionFieldValuation", FunctionFieldValuation) diff --git a/augmented_valuation.py b/augmented_valuation.py index 92419c04063..fd973e5ca1a 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -1,5 +1,5 @@ """ -Augmented valuations polynomial rings +Augmented valuations on polynomial rings Implements inductive valuations as defined in [ML1936]. @@ -93,7 +93,7 @@ def __init__(self, v, phi, mu, check=True): self._base_valuation = v self._mu = mu - if check and not self.residue_field().has_coerce_map_from(v.residue_field()): + if check and not self.constant_valuation().residue_field().has_coerce_map_from(v.constant_valuation().residue_field()): raise ValueError("the residue field `%s` does not embed into `%s`"%(v.residue_field(), self.residue_field())) def __hash__(self): @@ -397,7 +397,7 @@ def value_group(self): base = self._base_valuation.value_group() if self._mu is infinity: return base - from discrete_value_group import DiscreteValueGroup + from value_group import DiscreteValueGroup return base + DiscreteValueGroup(self._mu) def valuations(self, f): @@ -535,16 +535,16 @@ def reduce(self, f): # if this extends a trivial valuation, then this is very easy: just # return the constant coefficient in the phi-adic expansion; everything # else must have positive valuation - if self._base_valuation.value_group() == 0: + if self._base_valuation.value_group().is_trivial(): assert self.valuations(f).next() == 0 - if self.value_group() == 0: + if self.value_group().is_trivial(): raise NotImplementedError return self.residue_ring()(self.coefficients(f).next())(self.residue_field_generator()) # if this is an infinite valuation, then we can simply drop all but the # constant term if self._mu is infinity: - return self.residue_ring()(self._base_valuation.reduce(self.coefficients(f).next())(self.residue_field().gen())) + return self.residue_ring()(self._base_valuation.reduce(self.coefficients(f).next())(self.constant_valuation().residue_field().gen())) CV = zip(self.coefficients(f), self.valuations(f)) # rewrite as sum of f_i phi^{i tau}, i.e., drop most coefficients @@ -783,7 +783,7 @@ def tau(self): """ from sage.rings.all import ZZ - if self._base_valuation.value_group() == 0: + if self._base_valuation.value_group().is_trivial(): return ZZ.zero() assert self.value_group().numerator() == 1 @@ -853,11 +853,11 @@ def residue_field(self, generator=None): v = v._base_valuation generator = 'u%s'%level if self.psi().degree() == 1: - ret = self._base_valuation.residue_field() + ret = self._base_valuation.constant_valuation().residue_field() #ret.gen = "use augmented_valuation.residue_field_generator() instead" # temporary hack to find bugs when .gen() is used - .residue_field_generator() instead return ret else: - return self._base_valuation.residue_field().extension(self.psi(), names=generator) + return self._base_valuation.constant_valuation().residue_field().extension(self.psi(), names=generator) @cached_method def residue_field_generator(self): @@ -911,6 +911,8 @@ def E(self): 2 """ + if self._base_valuation.is_trivial(): + raise ValueError("there is no ramification over a trivial valuation") return self.tau() * self._base_valuation.E() def F(self): @@ -931,13 +933,15 @@ def F(self): 1 """ + if self._base_valuation.is_trivial(): + raise ValueError("there is no residual degree over a trivial valuation") return self.psi().degree() * self._base_valuation.F() def change_ring(self, base_ring): return AugmentedValuation(self._base_valuation.change_ring(base_ring), self.phi().change_ring(base_ring), self._mu) def uniformizer(self): - return self.element_with_valuation(1/self.E()) + return self.element_with_valuation(self.value_group()._generator) def is_gauss_valuation(self): # Is this correct? Can there be trivial augmentations? diff --git a/function_field_valuation.py b/function_field_valuation.py index 554daf7a275..9a42b14e8b8 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -23,6 +23,7 @@ sys.path.append(os.path.dirname(os.getcwd())) from sage.structure.factory import UniqueFactory +from sage.rings.all import QQ, ZZ, infinity from valuation import DiscretePseudoValuation from trivial_valuation import TrivialValuation @@ -69,6 +70,7 @@ class FunctionFieldValuationFactory(UniqueFactory): Or for the infinite place:: sage: v = FunctionFieldValuation(K, 1/x); v + Valuation at the infinite place sage: v(x) -1 @@ -77,14 +79,16 @@ class FunctionFieldValuationFactory(UniqueFactory): polynomial ring:: sage: R. = QQ[] - sage: w = GaussValuation(R, TrivialValuation(QQ)).extend(x - 1, 1) + sage: w = GaussValuation(R, TrivialValuation(QQ)).extension(x - 1, 1) sage: v = FunctionFieldValuation(K, w); v + (x - 1)-adic valuation Note that this allows us to specify valuations which do not correspond to a place of the function field:: sage: w = GaussValuation(R, pAdicValuation(QQ, 2)) sage: v = FunctionFieldValuation(K, w); v + 2-adic valuation """ def create_key(self, domain, prime): @@ -102,7 +106,7 @@ def create_key(self, domain, prime): sage: v = FunctionFieldValuation(K, x - 1) # indirect doctest sage: R. = QQ[] - sage: w = GaussValuation(R, TrivialValuation(QQ)).extend(x - 1, 1) + sage: w = GaussValuation(R, TrivialValuation(QQ)).extension(x - 1, 1) sage: FunctionFieldValuation(K, w) is v True @@ -182,12 +186,24 @@ def create_key_from_valuation(self, domain, valuation): sage: from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = QQ[] - sage: w = GaussValuation(R, TrivialValuation(QQ)).extend(x - 1, 1) + sage: w = GaussValuation(R, TrivialValuation(QQ)).extension(x - 1, 1) sage: v = FunctionFieldValuation(K, w) # indirect doctest """ if valuation.domain() is domain._ring: - # TODO: check validity + if domain.base_field() is not domain: + # on a extension of the form K[x]/(G), a valuation on K[x] must + # send exactly (G) to infinity + G = L.polynomial() + if valuation(G) != infinity: + raise ValueError("valuation must be a pseudo-valuation which sends the defining polynomial to infinity but %r sends %r to %r"%(valuation, G, valuation(G))) + if valuation.constant_valuation().domain() is not domain.base_field(): + raise ValueError("valuation must extend a valuation on the base field but %r extends %r whose domain is not %r"%(valuation, valuation.constant_valuation(), domain.base_field())) + else: + # on a rational function field K(x), any valuation on K[x] that + # is not only a pseudo-valuation extends to a valuation on K(x) + if not valuation.is_discrete_valuation(): + raise ValueError("valuation must be a discrete valuation but %r is not."%(valuation,)) return domain, valuation raise NotImplementedError() @@ -199,67 +215,110 @@ def create_object(self, version, key, **extra_args): sage: from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) + sage: R. = QQ[] sage: w = GaussValuation(R, pAdicValuation(QQ, 2)) sage: v = FunctionFieldValuation(K, w); v # indirect doctest + 2-adic valuation """ domain, valuation = key - if valuation in domain: - assert(valuation == ~domain.gen()) - raise NotImplementedError - from sage.rings.valuation.valuation_space import DiscreteValuationSpace - parent = DiscreteValuationSpace(domain) - return parent.__make_element_class__(RationalFunctionFieldValuation)(parent, valuation) + if domain.base_field() is domain: + from sage.rings.valuation.valuation_space import DiscreteValuationSpace + parent = DiscreteValuationSpace(domain) + if valuation in domain: + assert(valuation == ~domain.gen()) + return parent.__make_element_class__(InfiniteRationalFunctionFieldValuation)(parent) + return parent.__make_element_class__(FiniteRationalFunctionFieldValuation)(parent, valuation) + raise NotImplementedError FunctionFieldValuation = FunctionFieldValuationFactory("FunctionFieldValuation") -from sage.rings.all import QQ, ZZ, infinity +class FunctionFieldValuation_base(DiscretePseudoValuation): + r""" + Base class for valuations on function fields. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, x) # indirect doctest + sage: isinstance(v, FunctionFieldValuation_base) + True + + """ + +class RationalFunctionFieldValuation_base(FunctionFieldValuation_base): + r""" + Base class for discrete valuations on rational function fields. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, x) # indirect doctest + sage: isinstance(v, RationalFunctionFieldValuation_base) + True + + """ -class RationalFunctionFieldValuation(DiscretePseudoValuation): +class InducedFunctionFieldValuation_base(FunctionFieldValuation_base): + r""" + Base class for function field valuation induced by a valuation on the + underlying polynomial ring. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: TestSuite(FunctionFieldValuation(K, x)).run() # indirect doctest + + """ def __init__(self, parent, base_valuation): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, x) # indirect doctest + sage: isinstance(v, InducedFunctionFieldValuation_base) + True + + """ domain = parent.domain() if base_valuation.domain() is not domain._ring: raise ValueError self._base_valuation = base_valuation - DiscretePseudoValuation.__init__(self, parent) - - def _call_(self, x): - return self._base_valuation(x.numerator()) - self._base_valuation(x.denominator()) + RationalFunctionFieldValuation_base.__init__(self, parent) - def __hash__(self): - return hash(self._base_valuation) + hash(self.domain()) - - def _cache_key(self): - return self._base_valuation, self.domain() + def uniformizer(self): + r""" + Return a uniformizing element for this valuation. - def __cmp__(self, other): - if type(self) != type(other): - return cmp(type(self),type(other)) - return cmp(self._base_valuation,other._base_valuation) + EXAMPLES:: - def shift(self, f, v): - if f.parent() is not self.domain(): - raise ValueError + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: FunctionFieldValuation(K, x).uniformizer() + x + + """ + return self.domain()(self._base_valuation.uniformizer()) - if v == 0: + def shift(self, f, s): + if s == 0: return f - elif v < 0: - return f/self._base_valuation.element_with_valuation(-v) + elif s < 0: + return f/self._base_valuation.element_with_valuation(-s) else: - return f*self._base_valuation.element_with_valuation(v) + return f*self._base_valuation.element_with_valuation(s) def value_group(self): return self._base_valuation.value_group() - def residue_field(self): - # This is incorrect. For classical valuations on function fields, this is not correct. - from sage.rings.all import FunctionField - return FunctionField(self._base_valuation.residue_field(), names=self.domain().variable_names()) - def reduce(self, f): - if f.parent() is not self.domain(): - raise ValueError + f = self.domain().coerce(f) + if self(f) > 0: return self.residue_field().zero() if self(f) < 0: @@ -288,25 +347,25 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): """ Some difficult cases from Mark van Hoeij:: - sage: from sage.rings.padics.function_field_valuation import RationalFunctionFieldValuation, TrivialValuation + sage: from mac_lane import * # optional: standalone sage: k = GF(2) sage: K. = FunctionField(k) sage: R. = K[] sage: F = y^21 + x*y^20 + (x^3 + x + 1)*y^18 + (x^3 + 1)*y^17 + (x^4 + x)*y^16 + (x^7 + x^6 + x^3 + x + 1)*y^15 + x^7*y^14 + (x^8 + x^7 + x^6 + x^4 + x^3 + 1)*y^13 + (x^9 + x^8 + x^4 + 1)*y^12 + (x^11 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2)*y^11 + (x^12 + x^9 + x^8 + x^7 + x^5 + x^3 + x + 1)*y^10 + (x^14 + x^13 + x^10 + x^9 + x^8 + x^7 + x^6 + x^3 + x^2 + 1)*y^9 + (x^13 + x^9 + x^8 + x^6 + x^4 + x^3 + x)*y^8 + (x^16 + x^15 + x^13 + x^12 + x^11 + x^7 + x^3 + x)*y^7 + (x^17 + x^16 + x^13 + x^9 + x^8 + x)*y^6 + (x^17 + x^16 + x^12 + x^7 + x^5 + x^2 + x + 1)*y^5 + (x^19 + x^16 + x^15 + x^12 + x^6 + x^5 + x^3 + 1)*y^4 + (x^18 + x^15 + x^12 + x^10 + x^9 + x^7 + x^4 + x)*y^3 + (x^22 + x^21 + x^20 + x^18 + x^13 + x^12 + x^9 + x^8 + x^7 + x^5 + x^4 + x^3)*y^2 + (x^23 + x^22 + x^20 + x^17 + x^15 + x^14 + x^12 + x^9)*y + x^25 + x^23 + x^19 + x^17 + x^15 + x^13 + x^11 + x^5 sage: x = K._ring.gen() - sage: v0 = RationalFunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).extension(x,1)) + sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).extension(x,1)) sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assume_squarefree for speed, not tested - factorization over function fields over finite fields is missing [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x + 1) = 3/2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y) = 4/3, v(y^3 + x^4) = 13/3 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x) = 2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y^15 + y^13 + (x + 1)*y^12 + x*y^11 + (x + 1)*y^10 + y^9 + y^8 + x*y^6 + x*y^5 + y^4 + y^3 + y^2 + (x + 1)*y + x + 1) = 2 ]] - sage: v0 = RationalFunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).extension(x+1,1)) + sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).extension(x+1,1)) sage: v0.mac_lane_approximants(F, assume_squarefree=True) # not tested, factorization over function fields over finite fields is missing [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y) = 7/2, v(y^2 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1) = 15/2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y + x^2 + 1) = 7/2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y) = 3/4, v(y^4 + x^3 + x^2 + x + 1) = 15/4 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y^13 + x*y^12 + y^10 + (x + 1)*y^9 + (x + 1)*y^8 + x*y^7 + x*y^6 + (x + 1)*y^4 + y^3 + (x + 1)*y^2 + 1) = 2 ]] - sage: v0 = RationalFunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).extension(x^3+x^2+1,1)) + sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).extension(x^3+x^2+1,1)) sage: v0.mac_lane_approximants(F, assume_squarefree=True) # not tested, factorization over function fields over finite fields is missing [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y + x^3 + x^2 + x) = 2, v(y^2 + (x^6 + x^4 + 1)*y + x^14 + x^10 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2 + x) = 5 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^2 + (x^7 + x^5 + x^4 + x^3 + x^2 + x)*y + x^7 + x^5 + x + 1) = 3 ], @@ -322,7 +381,7 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): sage: F = y^2 - x^2 - x^3 - 3 sage: v0 = GaussValuation(K._ring,pAdicValuation(QQ,3)) sage: v1 = v0.extension(K._ring.gen(),1/3) - sage: mu0 = RationalFunctionFieldValuation(K, v1) + sage: mu0 = FunctionFieldValuation(K, v1) sage: mu0.mac_lane_approximants(F) [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + x) = 2/3 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + 2*x) = 2/3 ]] @@ -362,11 +421,13 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): def _repr_(self): from sage.rings.valuation.augmented_valuation import AugmentedValuation + from sage.rings.valuation.gauss_valuation import GaussValuation if isinstance(self._base_valuation, AugmentedValuation): - from sage.rings.valuation.gauss_valuation import GaussValuation if self._base_valuation._base_valuation == GaussValuation(self.domain()._ring, TrivialValuation(self.domain().constant_field())): if self._base_valuation._mu == 1: return "(%r)-adic valuation"%(self._base_valuation.phi()) + if self._base_valuation == GaussValuation(self.domain()._ring, self._base_valuation.constant_valuation()): + return repr(self._base_valuation.constant_valuation()) return "Valuation on rational function field induced by %s"%self._base_valuation def extension(self, L, algorithm="mac_lane"): @@ -383,10 +444,151 @@ def extension(self, L, algorithm="mac_lane"): return FunctionFieldPolymodValuation(L, W[0]) else: raise ValueError() +class InfiniteRationalFunctionFieldValuation(RationalFunctionFieldValuation_base): + r""" + Valuation of the infinite place of a function field. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, 1/x) # indirect doctest + + TESTS:: + + sage: TestSuite(v).run() + + """ + def _call_(self, f): + r""" + Evaluate this valuation at the rational function ``f``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, 1/x) + sage: v(x/(x^2 + 1)) + 1 + + """ + if f.is_zero(): + return infinity + return f.denominator().degree() - f.numerator().degree() + + def _repr_(self): + r""" + Return a printable representation of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: FunctionFieldValuation(K, 1/x) # indirect doctest + Valuation at the infinite place + + """ + return "Valuation at the infinite place" + + def residue_ring(self): + r""" + Return the residue field of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: FunctionFieldValuation(K, 1/x).residue_ring() + Rational Field + + """ + return self.domain().constant_field() + def uniformizer(self): - return self.domain()(self._base_valuation.uniformizer()) + r""" + Return a uniformizer of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: FunctionFieldValuation(K, 1/x).uniformizer() + 1/x + + """ + return ~self.domain().gen() + + def reduce(self, f): + r""" + Return the reduction of ``f`` with respect to this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: v = FunctionFieldValuation(K, 1/x) + sage: v.reduce(1/x) + 0 + sage: v.reduce( x*(x+1) / (x^2+x+1) ) + 1 + sage: v.reduce(x) + Traceback (most recent call last): + ... + ValueError: reduction is only defined for elements of non-negative valuation + + """ + f = self.domain().coerce(f) + + if self(f) < 0: + raise ValueError("reduction is only defined for elements of non-negative valuation") + if self(f) > 0: + return self.domain().zero() + + from sage.rings.function_field.function_field_valuation import FunctionFieldValuation + return FunctionFieldValuation(self.domain(), self.domain().gen()).reduce(f.element()(~self.domain().gen())) + +class FiniteRationalFunctionFieldValuation(RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base): + r""" + Valuation of a finite place of a function field. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: FunctionFieldValuation(K, x + 1) # indirect doctest + (x + 1)-adic valuation + + """ + def _call_(self, f): + r""" + Evaluate this valuation at the function ``f``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, x) # indirect doctest + sage: v((x+1)/x^2) + -2 + + """ + return self._base_valuation(f.numerator()) - self._base_valuation(f.denominator()) + + def residue_ring(self): + r""" + Return the residue field of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: FunctionFieldValuation(K, 1/x).residue_ring() + Rational Field + + """ + return self._base_valuation.residue_field() -class FunctionFieldPolymodValuation(DiscretePseudoValuation): +class FunctionFieldPolymodValuation(InducedFunctionFieldValuation_base): def __init__(self, domain, base_valuation): from sage.rings.all import infinity if base_valuation.domain() is not domain._ring: diff --git a/gauss_valuation.py b/gauss_valuation.py index 25102a148d8..ee57540a5eb 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -323,22 +323,6 @@ def valuations(self, f): for c in self.coefficients(f): yield self._base_valuation(self.domain().base_ring()(c)) - @cached_method - def residue_field(self): - """ - Return the residue field of this valuation, i.e., the base ring of the - residue polynomial ring of this valuation. - - EXAMPLES:: - - sage: S. = Qp(2,5)[] - sage: v = GaussValuation(S) - sage: v.residue_field() - Finite Field of size 2 - - """ - return self._base_valuation.residue_field() - @cached_method def residue_ring(self): """ @@ -353,7 +337,7 @@ def residue_ring(self): Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) """ - return self.domain().change_ring(self.residue_field()) + return self.domain().change_ring(self._base_valuation.residue_ring()) def reduce(self, f): """ @@ -395,7 +379,7 @@ def reduce(self, f): if not all([v>=0 for v in self.valuations(f)]): raise ValueError("reduction not defined for non-integral elements") - return f.map_coefficients(lambda c:self._base_valuation.reduce(c), self.residue_field()) + return f.map_coefficients(lambda c:self._base_valuation.reduce(c), self.constant_valuation().residue_field()) def lift(self, reduction): """ @@ -609,3 +593,6 @@ def _augmentations(self): sage: TODO """ return [self] + + def is_trivial(self): + return self._base_valuation.is_trivial() diff --git a/padic_valuation.py b/padic_valuation.py index 047e2394d42..8fdcdfe8654 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -481,9 +481,9 @@ def reduce(self, x): 1 """ - if x.parent() is not self.domain(): - raise ValueError("x must be in the domain of the valuation") - + x = self.domain().coerce(x) + if self(x) < 0: + raise ValueError("reduction is only defined for elements of non-negative valuation") return self.residue_field()(x) def lift(self, x): @@ -521,57 +521,20 @@ def uniformizer(self): """ return self.domain()(self._prime) - def residue_field(self): + def residue_ring(self): """ - Return the residue field of the ring of integers of this valuation. + Return the residue field of this valuation. EXAMPLES:: sage: v = pAdicValuation(ZZ, 3) - sage: v.residue_field() + sage: v.residue_ring() Finite Field of size 3 """ from sage.rings.all import GF return GF(self._prime) - def shift(self, c, v): - """ - Multiply ``c`` by a ``v``-th power of the uniformizer. - - INPUT: - - - ``c`` -- an element of the domain of this valuation - - - ``v`` -- an integer - - OUTPUT: - - If the resulting element has negative valation, then it will be brought - to the fraction field, otherwise it is an element of the domain. - - EXAMPLES:: - - sage: v = pAdicValuation(ZZ, 3) - sage: v.shift(2,3) - 54 - sage: v.shift(2,-3) - 2/27 - - """ - from sage.rings.all import ZZ - if c.parent() is not self.domain(): - raise ValueError("c must be in the domain of the valuation") - if not v in ZZ: - raise ValueError("v must be an integer") - v = ZZ(v) - - c = self.domain().fraction_field()(c) - ret = c*self.uniformizer()**v - - if self(c) >= -v: ret = self.domain()(ret) - return ret - def is_unramified(self, G, include_steps=False, assume_squarefree=False): """ Return whether ``G`` defines an unramified extension of the `p`-adic @@ -1011,9 +974,7 @@ def reduce(self, x): 1 """ - if x.parent() is not self.domain(): - raise ValueError("x must be in the domain of the valuation") - + x = self.domain().coerce(x) return self.residue_field()(x.residue()) def lift(self, x): @@ -1143,8 +1104,7 @@ def _call_(self, x): return ret*self._valuation.E() def reduce(self, x): - if x.parent() is not self.domain(): - raise ValueError("x must be in the domain of the valuation") + x = self.domain().coerce(x) if self(x)>0: return self.residue_field().zero() diff --git a/trivial_valuation.py b/trivial_valuation.py index e823d2525ab..d2d7466da12 100644 --- a/trivial_valuation.py +++ b/trivial_valuation.py @@ -253,9 +253,10 @@ def residue_ring(self): sage: from mac_lane import * # optional: standalone sage: TrivialPseudoValuation(QQ).residue_ring() + Quotient of Rational Field by the ideal (1) """ - return self.domain().quo(self.one()) + return self.domain().quo(self.domain().one()) def reduce(self, x): r""" @@ -266,6 +267,7 @@ def reduce(self, x): sage: from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ) sage: v.reduce(1) + 0 """ self.domain().coerce(x) diff --git a/valuation_space.py b/valuation_space.py index ce67c7a4504..3d5b5e4db21 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -335,7 +335,8 @@ def value_group(self, **options): ValueError: The trivial pseudo-valuation that is infinity everywhere does not have a value group. """ - return DiscreteValueGroup(self(self.uniformizer)) + from value_group import DiscreteValueGroup + return DiscreteValueGroup(self(self.uniformizer())) def shift(self, x, s): r""" @@ -355,16 +356,19 @@ def shift(self, x, s): sage: v.shift(1, 10) 1024 sage: v.shift(1, -10) + Traceback (most recent call last): + ... + TypeError: no conversion of this rational to integer """ x = self.domain().coerce(x) - from sage.rings.all import QQ + from sage.rings.all import QQ, ZZ s = QQ.coerce(s) if s == 0: return x if s not in self.value_group(): raise ValueError("s must be in the value group of this valuation") - return self.domain()(x * (self.uniformizer()**n)) + return self.domain()(x * (self.uniformizer() ** ZZ(s/self.value_group().gen()))) @abstract_method def residue_ring(self): @@ -379,7 +383,7 @@ def residue_ring(self): sage: from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2).residue_ring() - + Finite Field of size 2 sage: TrivialValuation(QQ).residue_ring() Rational Field @@ -387,11 +391,12 @@ def residue_ring(self): may not:: sage: TrivialPseudoValuation(QQ).residue_ring() - Zero ring - + Quotient of Rational Field by the ideal (1) sage: TrivialValuation(ZZ).residue_ring() - + Integer Ring sage: GaussValuation(ZZ['x'], pAdicValuation(ZZ, 2)).residue_ring() + Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) + """ @@ -405,13 +410,16 @@ def reduce(self, x): sage: from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) - sage: v.reduce(1) + sage: v.reduce(2) 0 sage: v.reduce(1) 1 sage: v.reduce(1/3) 1 sage: v.reduce(1/2) + Traceback (most recent call last): + ... + ValueError: reduction is only defined for elements of non-negative valuation """ @@ -544,15 +552,21 @@ def _test_shift(self, **options): return S = tester.some_elements(self.domain().some_elements()) - V = tester.some_elements(self.value_group()) + V = tester.some_elements(self.value_group().some_elements()) from sage.categories.cartesian_product import cartesian_product for x, n in tester.some_elements(cartesian_product([S,V])): v = self(x) + from sage.categories.fields import Fields if n < 0 and -n > v and not self.domain() in Fields(): continue - tester.assertEqual(self.shift(x, n), v + n) + y = self.shift(x, n) + tester.assertIs(y.parent(), self.domain()) + tester.assertEqual(self(y), v + n) # shifts preserve reductions - tester.assertEqual(self.reduce(self.shift(self.shift(x, n), -n)), self.reduce(x)) + z = self.shift(y, -n) + tester.assertEqual(self(z), v) + if v >= 0: + tester.assertEqual(self.reduce(z), self.reduce(x)) def _test_residue_ring(self, **options): r""" @@ -567,6 +581,12 @@ def _test_residue_ring(self, **options): """ tester = self._tester(**options) + r = self.residue_ring() + if r.zero() == r.one(): + # residue ring is the zero rng + tester.assertGreater(self(1), 0) + return + c = self.residue_ring().characteristic() if c != 0: tester.assertGreater(self(c), 0) @@ -590,10 +610,13 @@ def _test_reduce(self, **options): self.reduce(x) continue if self(x) == 0: - tester.assertNotEqual(self.reduce(x), 0) + y = self.reduce(x) + tester.assertIn(y, self.residue_ring()) + tester.assertNotEqual(y, 0) if x.is_unit() and ~x in self.domain(): - tester.assertTrue(self.reduce(x).is_unit()) - tester.assertEqual(~self.reduce(x), self.reduce(~x)) + tester.assertTrue(y.is_unit()) + tester.assertIn(~y, self.residue_ring()) + tester.assertEqual(~y, self.reduce(~x)) if self(x) > 0: tester.assertEqual(self.reduce(x), 0) @@ -727,7 +750,7 @@ def residue_field(self): sage: from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2).residue_field() - + Finite Field of size 2 sage: TrivialValuation(QQ).residue_field() Rational Field @@ -735,8 +758,13 @@ def residue_field(self): field:: sage: TrivialValuation(ZZ).residue_field() - + Traceback (most recent call last): + ... + ValueError: The residue ring of this valuation is not a field. sage: GaussValuation(ZZ['x'], pAdicValuation(ZZ, 2)).residue_field() + Traceback (most recent call last): + ... + ValueError: The residue ring of this valuation is not a field. """ ret = self.residue_ring() diff --git a/value_group.py b/value_group.py index 6a67cd66f16..ba9ea0e7b53 100644 --- a/value_group.py +++ b/value_group.py @@ -219,7 +219,7 @@ def _repr_(self): Trivial Additive Abelian Group """ - if self._generator.is_zero(): + if self.is_trivial(): return "Trivial Additive Abelian Group" return "Additive Abelian Group generated by %r"%(self._generator,) @@ -366,3 +366,18 @@ def some_elements(self): """ return [self._generator, -self._generator] + [x for x in QQ.some_elements() if x in self] + + def is_trivial(self): + r""" + Return whether this is the trivial additive abelian group. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: DiscreteValueGroup(-3/8).is_trivial() + False + sage: DiscreteValueGroup(0).is_trivial() + True + + """ + return self._generator.is_zero() From 51f299ce4172dafc1f51dca5d8e028dc82bf8798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 17 Oct 2016 11:08:55 -0500 Subject: [PATCH 036/740] use built-in newton polygons --- __init__.py | 7 +++- augmented_valuation.py | 4 +-- developing_valuation.py | 10 +++--- function_field_valuation.py | 72 ++++++++++++++++++++++++++++++------- trivial_valuation.py | 29 +++++++++++++++ valuation_space.py | 35 ++++++++++++++++++ 6 files changed, 137 insertions(+), 20 deletions(-) diff --git a/__init__.py b/__init__.py index 879d7543c1b..1605297dec8 100644 --- a/__init__.py +++ b/__init__.py @@ -10,7 +10,7 @@ # local file and the instances that come from the mac_lane import define # different types) from .trivial_valuation import TrivialDiscreteValuation, TrivialDiscretePseudoValuation -from .function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base +from .function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalRationalFunctionFieldValuation_base # ================= # MONKEY PATCH SAGE @@ -46,6 +46,11 @@ def __init__(self, *args, **kwargs): del(__init__) del(to_polynomial) +# implement principal_part for newton polygons +import sage.geometry.newton_polygon +sage.geometry.newton_polygon.NewtonPolygon_element.principal_part = lambda self: sage.geometry.newton_polygon.NewtonPolygon(self.vertices(), last_slope=0) +sage.geometry.newton_polygon.NewtonPolygon_element.sides = lambda self: zip(self.vertices(), self.vertices()[1:]) + import imp, sys # register modules at some standard places so imports work as exepcted r""" diff --git a/augmented_valuation.py b/augmented_valuation.py index fd973e5ca1a..35654d3c806 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -652,8 +652,8 @@ def lift(self, F): raise ValueError("any reduction is constant in this valuation") if self.phi() != self.domain().gen(): raise NotImplementedError - constant = F[0].lift() - assert constant.is_constant() + constant = self.constant_valuation().lift(F[0]) + assert constant in self.domain().base_ring() return self.domain()(constant[0]) R0 = self._base_valuation.residue_ring() diff --git a/developing_valuation.py b/developing_valuation.py index 02c98cbc308..908e31c38e6 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -931,8 +931,8 @@ def newton_polygon(self, f): if f.parent() is not self.domain(): raise ValueError("f must be in the domain of the valuation") - from newton_polygon import NewtonPolygon - return NewtonPolygon(self.valuations(f)) + from sage.geometry.newton_polygon import NewtonPolygon + return NewtonPolygon(enumerate(self.valuations(f))) def _call_(self, f): """ @@ -1065,7 +1065,7 @@ def mac_lane_step(self, G, assume_squarefree=False): NP = w.newton_polygon(G).principal_part() verbose("Newton-Polygon for v(phi)=%s : %s"%(self(phi),NP),level=2,caller_name="mac_lane_step") # assert len(NP) - if not NP: + if not NP.slopes(): q,r = G.quo_rem(phi) assert not r.is_zero() phi = phi.coefficients(sparse=False) @@ -1086,7 +1086,7 @@ def mac_lane_step(self, G, assume_squarefree=False): ret.append(w) continue - for i in range(len(NP.slopes())): + for i in range(len(NP.slopes(repetition=False))): slope = NP.slopes()[i] verbose("Slope = %s"%slope,level=3,caller_name="mac_lane_step") side = NP.sides()[i] @@ -1100,7 +1100,7 @@ def mac_lane_step(self, G, assume_squarefree=False): base = base._base_valuation new_leaf = base.extension(phi, new_mu) - assert slope is -infinity or 0 in new_leaf.newton_polygon(G).slopes() + assert slope is -infinity or 0 in new_leaf.newton_polygon(G).slopes(repetition=False) ret.append(new_leaf) assert ret diff --git a/function_field_valuation.py b/function_field_valuation.py index 9a42b14e8b8..109c2809681 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -205,7 +205,7 @@ def create_key_from_valuation(self, domain, valuation): if not valuation.is_discrete_valuation(): raise ValueError("valuation must be a discrete valuation but %r is not."%(valuation,)) return domain, valuation - raise NotImplementedError() + raise NotImplementedError("automatic extension of valuations to the full domain not implemented yet") def create_object(self, version, key, **extra_args): r""" @@ -261,6 +261,21 @@ class RationalFunctionFieldValuation_base(FunctionFieldValuation_base): """ +class ClassicalRationalFunctionFieldValuation_base(RationalFunctionFieldValuation_base): + r""" + Base class for discrete valuations on rational function fields that come + from points on the projective line. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, x) # indirect doctest + sage: isinstance(v, ClassicalRationalFunctionFieldValuation_base) + True + + """ + class InducedFunctionFieldValuation_base(FunctionFieldValuation_base): r""" Base class for function field valuation induced by a valuation on the @@ -305,6 +320,28 @@ def uniformizer(self): """ return self.domain()(self._base_valuation.uniformizer()) + def lift(self, F): + r""" + Return a lift of ``F`` to the :meth:`domain` of this valuation such + that :meth:`reduce` returns the original element. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, x) + sage: v.lift(0) + 0 + sage: v.lift(1) + 1 + + """ + F = self.residue_ring().coerce(F) + if F in self._base_valuation.residue_ring(): + f = self._base_valuation.lift(F) + return self.domain()(f) + raise NotImplementedError("lifting not implemented for this valuation") + def shift(self, f, s): if s == 0: return f @@ -337,12 +374,6 @@ def reduce(self, f): assert not ret.is_zero() return self.residue_field()(ret) - def lift(self, F): - if F.parent() is not self.residue_field(): - raise ValueError - - return self.domain()(self._base_valuation.lift(F.numerator()) / self._base_valuation.lift(F.denominator())) - def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): """ Some difficult cases from Mark van Hoeij:: @@ -444,7 +475,7 @@ def extension(self, L, algorithm="mac_lane"): return FunctionFieldPolymodValuation(L, W[0]) else: raise ValueError() -class InfiniteRationalFunctionFieldValuation(RationalFunctionFieldValuation_base): +class InfiniteRationalFunctionFieldValuation(ClassicalRationalFunctionFieldValuation_base): r""" Valuation of the infinite place of a function field. @@ -547,7 +578,27 @@ def reduce(self, f): from sage.rings.function_field.function_field_valuation import FunctionFieldValuation return FunctionFieldValuation(self.domain(), self.domain().gen()).reduce(f.element()(~self.domain().gen())) -class FiniteRationalFunctionFieldValuation(RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base): + def lift(self, F): + r""" + Lift ``F`` from :meth:`residue_ring` to :meth:`domain` such that + :meth:`reduce` of the lift produces ``F``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: v = FunctionFieldValuation(K, 1/x) + + sage: v.lift(v.residue_ring().one()) + 1 + + """ + F = self.residue_ring().coerce(F) + # the infinite place has a trivial residue field extension + assert F in self.domain() + return self.domain()(F) + +class FiniteRationalFunctionFieldValuation(ClassicalRationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base): r""" Valuation of a finite place of a function field. @@ -627,9 +678,6 @@ def shift(self, f, v): def value_group(self): return self._base_valuation.value_group() - def residue_field(self): - return self._base_valuation.residue_field() - def reduce(self, f): return self.residue_field()(self._base_valuation._base_valuation.reduce(f.element())) diff --git a/trivial_valuation.py b/trivial_valuation.py index d2d7466da12..b8edff2e156 100644 --- a/trivial_valuation.py +++ b/trivial_valuation.py @@ -273,6 +273,21 @@ def reduce(self, x): self.domain().coerce(x) return self.residue_ring().zero() + def lift(self, X): + r""" + Return a lift of ``X`` to the domain of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = TrivialPseudoValuation(QQ) + sage: v.lift(v.residue_ring().zero()) + 0 + + """ + self.residue_ring().coerce(X) # ignore the output + return self.domain().zero() + class TrivialDiscreteValuation(TrivialDiscretePseudoValuation_base): r""" The trivial valuation that is zero on non-zero elements. @@ -373,6 +388,20 @@ def reduce(self, x): """ return self.domain().coerce(x) + def lift(self, X): + r""" + Return a lift of ``X`` to the domain of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = TrivialPseudoValuation(QQ) + sage: v.lift(v.residue_ring().zero()) + 0 + + """ + return self.residue_ring().coerce(X) + TrivialValuation = TrivialValuationFactory(TrivialDiscreteValuation, DiscreteValuationSpace, "TrivialValuation") TrivialPseudoValuation = TrivialValuationFactory(TrivialDiscretePseudoValuation, DiscretePseudoValuationSpace, "TrivialPseudoValuation") diff --git a/valuation_space.py b/valuation_space.py index 3d5b5e4db21..3cc14cecc8e 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -423,6 +423,21 @@ def reduce(self, x): """ + @abstract_method + def lift(self, X): + r""" + Return a lift of ``X`` in :meth:`domain` which reduces down to + ``X`` again via :meth:`reduce`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 2) + sage: v.lift(v.residue_ring().one()) + 1 + + """ + def _test_add(self, **options): r""" Check that the (strict) triangle equality is satisfied for the @@ -620,6 +635,26 @@ def _test_reduce(self, **options): if self(x) > 0: tester.assertEqual(self.reduce(x), 0) + def _test_lift(self, **options): + r""" + Check the correctness of lifts. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: v._test_lift() + + """ + tester = self._tester(**options) + + for X in tester.some_elements(self.residue_ring().some_elements()): + x = self.lift(X) + y = self.reduce(x) + tester.assertEqual(X, y) + if X != 0: + tester.assertEqual(self(x), 0) + class DiscreteValuationSpace(DiscretePseudoValuationSpace): r""" The space of discrete valuations on ``domain``. From 7352b43e3d7cd224b05bf8256b6b8e19a0fa66b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 18 Oct 2016 20:55:46 -0500 Subject: [PATCH 037/740] Fix all doctests related to function field valuations --- __init__.py | 29 +++++++ augmented_valuation.py | 28 +++++-- developing_valuation.py | 14 ++-- function_field_valuation.py | 153 +++++++++++++++++++++++++----------- gauss_valuation.py | 5 ++ padic_valuation.py | 35 ++++----- trivial_valuation.py | 18 ++++- valuation_space.py | 80 +++++++++++++++---- 8 files changed, 269 insertions(+), 93 deletions(-) diff --git a/__init__.py b/__init__.py index 1605297dec8..93532609416 100644 --- a/__init__.py +++ b/__init__.py @@ -51,6 +51,35 @@ def __init__(self, *args, **kwargs): sage.geometry.newton_polygon.NewtonPolygon_element.principal_part = lambda self: sage.geometry.newton_polygon.NewtonPolygon(self.vertices(), last_slope=0) sage.geometry.newton_polygon.NewtonPolygon_element.sides = lambda self: zip(self.vertices(), self.vertices()[1:]) +# implement coercion of function fields that comes from coercion of their base fields +def _coerce_map_from_(target, source): + from sage.categories.function_fields import FunctionFields + if source in FunctionFields(): + if source.base_field() is source and target.base_field() is target: + if source.variable_name() == target.variable_name(): + # source and target are rational function fields in the same variable + base_coercion = target.constant_field().coerce_map_from(source.constant_field()) + if base_coercion is not None: + return source.hom([target.gen()], base_morphism=base_coercion) + if source.base_field() is not source and target.base_field() is not target: + # source and target are extensions of rational function fields + base_coercion = target.base_field().coerce_map_from(source.base_field()) + if base_coercion is not None: + # The base field of source coerces into the base field of target. + if source.polynomial().map_coefficients(base_coercion)(target.gen()) == 0: + # The defining polynomial of source has a root in target, + # therefore there is a map. To be sure that it is + # canonical, we require a root of the defining polynomial + # of target to be a root of the defining polynomial of + # source (and that the variables are named equally): + if source.variable_name() == target.variable_name(): + return source.hom([target.gen()], base_morphism=base_coercion) + +sage.rings.function_field.function_field.FunctionField._coerce_map_from_ = _coerce_map_from_ + +del(_coerce_map_from_) + + import imp, sys # register modules at some standard places so imports work as exepcted r""" diff --git a/augmented_valuation.py b/augmented_valuation.py index 35654d3c806..095eb35077f 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -650,11 +650,27 @@ def lift(self, F): raise NotImplementedError if not F.is_constant(): raise ValueError("any reduction is constant in this valuation") - if self.phi() != self.domain().gen(): + F = F[0] + if self.phi() == self.domain().gen(): + # this is a valuation of the form [p-adic valuation, v(x) = 1] + constant = self.constant_valuation().lift(F) + assert constant in self.domain().base_ring() + return self.domain()(constant) + else: + if self.phi().degree() == 1: + # this is a classical valuation of a rational point, of the + # form [trivial, v(x + 1) = 1] + assert self.domain().base_ring() is self.residue_field() + return self.domain()(F) + if self.phi().change_variable_name(self.residue_field().polynomial().variable_name()) == self.residue_field().polynomial(): + # this is a classical valuation of a point, of the from + # [trivial, v(x^2 + 1) = 1] + if hasattr(F, 'polynomial'): + u = F.polynomial() + if hasattr(F, 'element'): + u = F.element() + return self.domain()(u.change_variable_name(self.phi().variable_name())) raise NotImplementedError - constant = self.constant_valuation().lift(F[0]) - assert constant in self.domain().base_ring() - return self.domain()(constant[0]) R0 = self._base_valuation.residue_ring() @@ -937,8 +953,8 @@ def F(self): raise ValueError("there is no residual degree over a trivial valuation") return self.psi().degree() * self._base_valuation.F() - def change_ring(self, base_ring): - return AugmentedValuation(self._base_valuation.change_ring(base_ring), self.phi().change_ring(base_ring), self._mu) + def extension(self, ring): + return AugmentedValuation(self._base_valuation.extension(ring), self.phi().change_ring(ring.base_ring()), self._mu) def uniformizer(self): return self.element_with_valuation(self.value_group()._generator) diff --git a/developing_valuation.py b/developing_valuation.py index 908e31c38e6..0b9b7ba6570 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -342,7 +342,7 @@ def equivalence_reciprocal(self, f): return h @cached_method - def extension(self, phi, mu, check=True): + def augmentation(self, phi, mu, check=True): """ Return the inductive valuation which extends this valuation by mapping ``phi`` to ``mu``. @@ -1009,7 +1009,7 @@ def mac_lane_step(self, G, assume_squarefree=False): sage: S.=K[] sage: F=y^2-x^2-x^3-3 sage: v0=GaussValuation(K._ring,pAdicValuation(QQ,3)) - sage: v1=v0.extension(K._ring.gen(),1/3) + sage: v1=v0.augmentation(K._ring.gen(),1/3) sage: from sage.rings.padics.function_field_valuation import RationalFunctionFieldValuation sage: mu0=RationalFunctionFieldValuation(K,v1) sage: eta0=GaussValuation(S,mu0) @@ -1032,7 +1032,7 @@ def mac_lane_step(self, G, assume_squarefree=False): raise ValueError("G must not have valuation infinity") if self.is_key(G): - return [self.extension(G, infinity)] + return [self.augmentation(G, infinity)] F = self.equivalence_decomposition(G) assert len(F), "%s factored as a unit %s"%(G,F) @@ -1048,7 +1048,7 @@ def mac_lane_step(self, G, assume_squarefree=False): prec = min([c.precision_absolute() for c in phi.list()]) g = G.map_coefficients(lambda c:c.add_bigoh(prec)) assert self.is_key(g) - return [self.extension(g, infinity)] + return [self.augmentation(g, infinity)] if phi == self.phi(): # self.phi() always is a key over self but it will not lead to an extension of this valuation @@ -1061,7 +1061,7 @@ def mac_lane_step(self, G, assume_squarefree=False): continue verbose("Determining the valuation for %s"%phi,level=2,caller_name="mac_lane_step") - w = self.extension(phi, self(phi), check=False) + w = self.augmentation(phi, self(phi), check=False) NP = w.newton_polygon(G).principal_part() verbose("Newton-Polygon for v(phi)=%s : %s"%(self(phi),NP),level=2,caller_name="mac_lane_step") # assert len(NP) @@ -1082,7 +1082,7 @@ def mac_lane_step(self, G, assume_squarefree=False): phi[i-best] = phi[i-best].add_bigoh(w(c)-w(q[best])) phi = G.parent()(phi) - w = self._base_valuation.extension(phi, infinity) + w = self._base_valuation.augmentation(phi, infinity) ret.append(w) continue @@ -1099,7 +1099,7 @@ def mac_lane_step(self, G, assume_squarefree=False): if not base.is_gauss_valuation(): base = base._base_valuation - new_leaf = base.extension(phi, new_mu) + new_leaf = base.augmentation(phi, new_mu) assert slope is -infinity or 0 in new_leaf.newton_polygon(G).slopes(repetition=False) ret.append(new_leaf) diff --git a/function_field_valuation.py b/function_field_valuation.py index 109c2809681..b364cb4388e 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -79,7 +79,7 @@ class FunctionFieldValuationFactory(UniqueFactory): polynomial ring:: sage: R. = QQ[] - sage: w = GaussValuation(R, TrivialValuation(QQ)).extension(x - 1, 1) + sage: w = GaussValuation(R, TrivialValuation(QQ)).augmentation(x - 1, 1) sage: v = FunctionFieldValuation(K, w); v (x - 1)-adic valuation @@ -90,6 +90,29 @@ class FunctionFieldValuationFactory(UniqueFactory): sage: v = FunctionFieldValuation(K, w); v 2-adic valuation + Note that classical valuations at finite places or the infinite place are + always normalized such that the uniformizing element has valuation 1:: + + sage: K. = FunctionField(GF(3)) + sage: M. = FunctionField(K) + sage: v = FunctionFieldValuation(M, x^3 - t) + sage: v(x^3 - t) + 1 + + However, if such a valuation comes out of a base change of the ground + field, this is not the case anymore. In the example below, the unique + extension of ``v`` to ``L`` still has valuation 1 on ``x^3 - t`` but it has + valuation ``1/3`` on its uniformizing element ``x - w``:: + + sage: R. = K[] + sage: L. = K.extension(w^3 - t) + sage: N. = FunctionField(L) + sage: w = v.extension(N) # optional: integrated + sage: w(x^3 - t) # optional: integrated + 1 + sage: w(x - w) # optional: integrated + 1/3 + """ def create_key(self, domain, prime): r""" @@ -106,7 +129,7 @@ def create_key(self, domain, prime): sage: v = FunctionFieldValuation(K, x - 1) # indirect doctest sage: R. = QQ[] - sage: w = GaussValuation(R, TrivialValuation(QQ)).extension(x - 1, 1) + sage: w = GaussValuation(R, TrivialValuation(QQ)).augmentation(x - 1, 1) sage: FunctionFieldValuation(K, w) is v True @@ -163,12 +186,14 @@ def create_key_from_place(self, domain, generator): if generator in domain._ring: # generator is a polynomial generator = domain._ring(generator) - if not generator.is_monic() or not generator.is_irreducible(): - raise ValueError("place must be defined by a monic irreducible polynomial") + if not generator.is_monic(): + raise ValueError("place must be defined by a monic polynomiala but %r is not monic"%(generator,)) + if not generator.is_irreducible(): + raise ValueError("place must be defined by an irreducible polynomial but %r factors over %r"%(generator, domain._ring)) # we construct the corresponding valuation on the polynomial ring # with v(generator) = 1 from sage.rings.valuation.gauss_valuation import GaussValuation - valuation = GaussValuation(domain._ring, TrivialValuation(domain.constant_base_field())).extension(generator, 1) + valuation = GaussValuation(domain._ring, TrivialValuation(domain.constant_base_field())).augmentation(generator, 1) return self.create_key(domain, valuation) elif generator == ~domain.gen(): # generator is 1/x, the infinite place @@ -186,7 +211,7 @@ def create_key_from_valuation(self, domain, valuation): sage: from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = QQ[] - sage: w = GaussValuation(R, TrivialValuation(QQ)).extension(x - 1, 1) + sage: w = GaussValuation(R, TrivialValuation(QQ)).augmentation(x - 1, 1) sage: v = FunctionFieldValuation(K, w) # indirect doctest """ @@ -244,6 +269,7 @@ class FunctionFieldValuation_base(DiscretePseudoValuation): sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, FunctionFieldValuation_base) True + sage: TestSuite(v).run() """ @@ -254,10 +280,11 @@ class RationalFunctionFieldValuation_base(FunctionFieldValuation_base): TESTS:: sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) + sage: K. = FunctionField(GF(2)) sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, RationalFunctionFieldValuation_base) True + sage: TestSuite(v).run() """ @@ -269,10 +296,11 @@ class ClassicalRationalFunctionFieldValuation_base(RationalFunctionFieldValuatio TESTS:: sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) + sage: K. = FunctionField(GF(5)) sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, ClassicalRationalFunctionFieldValuation_base) True + sage: TestSuite(v).run() """ @@ -285,7 +313,8 @@ class InducedFunctionFieldValuation_base(FunctionFieldValuation_base): sage: from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) - sage: TestSuite(FunctionFieldValuation(K, x)).run() # indirect doctest + sage: v = FunctionFieldValuation(K, x^2 + 1) # indirect doctest + sage: TestSuite(v).run() """ def __init__(self, parent, base_valuation): @@ -342,18 +371,33 @@ def lift(self, F): return self.domain()(f) raise NotImplementedError("lifting not implemented for this valuation") - def shift(self, f, s): - if s == 0: - return f - elif s < 0: - return f/self._base_valuation.element_with_valuation(-s) - else: - return f*self._base_valuation.element_with_valuation(s) - def value_group(self): + r""" + Return the value group of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: FunctionFieldValuation(K, x).value_group() + Additive Abelian Group generated by 1 + + """ return self._base_valuation.value_group() def reduce(self, f): + r""" + Return the reduction of ``f`` in :meth:`residue_ring`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, x^2 + 1) + sage: v.reduce(x) + u1 + + """ f = self.domain().coerce(f) if self(f) > 0: @@ -384,19 +428,19 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): sage: R. = K[] sage: F = y^21 + x*y^20 + (x^3 + x + 1)*y^18 + (x^3 + 1)*y^17 + (x^4 + x)*y^16 + (x^7 + x^6 + x^3 + x + 1)*y^15 + x^7*y^14 + (x^8 + x^7 + x^6 + x^4 + x^3 + 1)*y^13 + (x^9 + x^8 + x^4 + 1)*y^12 + (x^11 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2)*y^11 + (x^12 + x^9 + x^8 + x^7 + x^5 + x^3 + x + 1)*y^10 + (x^14 + x^13 + x^10 + x^9 + x^8 + x^7 + x^6 + x^3 + x^2 + 1)*y^9 + (x^13 + x^9 + x^8 + x^6 + x^4 + x^3 + x)*y^8 + (x^16 + x^15 + x^13 + x^12 + x^11 + x^7 + x^3 + x)*y^7 + (x^17 + x^16 + x^13 + x^9 + x^8 + x)*y^6 + (x^17 + x^16 + x^12 + x^7 + x^5 + x^2 + x + 1)*y^5 + (x^19 + x^16 + x^15 + x^12 + x^6 + x^5 + x^3 + 1)*y^4 + (x^18 + x^15 + x^12 + x^10 + x^9 + x^7 + x^4 + x)*y^3 + (x^22 + x^21 + x^20 + x^18 + x^13 + x^12 + x^9 + x^8 + x^7 + x^5 + x^4 + x^3)*y^2 + (x^23 + x^22 + x^20 + x^17 + x^15 + x^14 + x^12 + x^9)*y + x^25 + x^23 + x^19 + x^17 + x^15 + x^13 + x^11 + x^5 sage: x = K._ring.gen() - sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).extension(x,1)) + sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).augmentation(x,1)) sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assume_squarefree for speed, not tested - factorization over function fields over finite fields is missing [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x + 1) = 3/2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y) = 4/3, v(y^3 + x^4) = 13/3 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x) = 2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y^15 + y^13 + (x + 1)*y^12 + x*y^11 + (x + 1)*y^10 + y^9 + y^8 + x*y^6 + x*y^5 + y^4 + y^3 + y^2 + (x + 1)*y + x + 1) = 2 ]] - sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).extension(x+1,1)) + sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).augmentation(x+1,1)) sage: v0.mac_lane_approximants(F, assume_squarefree=True) # not tested, factorization over function fields over finite fields is missing [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y) = 7/2, v(y^2 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1) = 15/2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y + x^2 + 1) = 7/2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y) = 3/4, v(y^4 + x^3 + x^2 + x + 1) = 15/4 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y^13 + x*y^12 + y^10 + (x + 1)*y^9 + (x + 1)*y^8 + x*y^7 + x*y^6 + (x + 1)*y^4 + y^3 + (x + 1)*y^2 + 1) = 2 ]] - sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).extension(x^3+x^2+1,1)) + sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).augmentation(x^3+x^2+1,1)) sage: v0.mac_lane_approximants(F, assume_squarefree=True) # not tested, factorization over function fields over finite fields is missing [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y + x^3 + x^2 + x) = 2, v(y^2 + (x^6 + x^4 + 1)*y + x^14 + x^10 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2 + x) = 5 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^2 + (x^7 + x^5 + x^4 + x^3 + x^2 + x)*y + x^7 + x^5 + x + 1) = 3 ], @@ -411,7 +455,7 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): sage: S. = K[] sage: F = y^2 - x^2 - x^3 - 3 sage: v0 = GaussValuation(K._ring,pAdicValuation(QQ,3)) - sage: v1 = v0.extension(K._ring.gen(),1/3) + sage: v1 = v0.augmentation(K._ring.gen(),1/3) sage: mu0 = FunctionFieldValuation(K, v1) sage: mu0.mac_lane_approximants(F) [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + x) = 2/3 ], @@ -461,19 +505,27 @@ def _repr_(self): return repr(self._base_valuation.constant_valuation()) return "Valuation on rational function field induced by %s"%self._base_valuation - def extension(self, L, algorithm="mac_lane"): + def extension(self, L): K = self.domain() - if L is K: + from sage.categories.function_fields import FunctionFields + if L is self.domain(): return self - if L.base() is not K: - raise ValueError("L must be a simple finite extension of %s"%K) - - if algorithm == "mac_lane": - W = self.mac_lane_approximants(L.polynomial(),precision_cap=infinity) - if len(W) > 1: - raise ValueError("extension to %s is not unique"%L) - return FunctionFieldPolymodValuation(L, W[0]) - else: raise ValueError() + if L in FunctionFields(): + if L.has_coerce_map_from(K): + if L.base() is K: + # L is a simple extension of the domain of this valuation + W = self.mac_lane_approximants(L.polynomial(), precision_cap=infinity) + if len(W) > 1: + raise ValueError("valuation %r on %r does not uniquely extend to %r"%(self, K, L)) + return FunctionFieldValuation(L, W[0]) + elif L.base() is not L and L.base().has_coerce_map_from(K): + # recursively call this method for the tower of fields + return self.extension(L.base()).extension(L) + elif L.constant_field() is not K.constant_field() and L.constant_field().has_coerce_map_from(K.constant_field()): + # extend the underlying valuation on the polynomial ring + w = self._base_valuation.extension(L._ring) + return FunctionFieldValuation(L, w) + raise NotImplementedError("extension of %r from %r to %r not implemented"%(self, K, L)) class InfiniteRationalFunctionFieldValuation(ClassicalRationalFunctionFieldValuation_base): r""" @@ -606,9 +658,33 @@ class FiniteRationalFunctionFieldValuation(ClassicalRationalFunctionFieldValuati sage: from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) - sage: FunctionFieldValuation(K, x + 1) # indirect doctest + sage: v = FunctionFieldValuation(K, x + 1); v # indirect doctest (x + 1)-adic valuation + A finite place with residual degree:: + + sage: w = FunctionFieldValuation(K, x^2 + 1); w + (x^2 + 1)-adic valuation + + A finite place with ramification:: + + sage: K. = FunctionField(GF(3)) + sage: L. = FunctionField(K) + sage: u = FunctionFieldValuation(L, x^3 - t); u + (x^3 + 2*t)-adic valuation + + A finite place with residual degree and ramification:: + + sage: q = FunctionFieldValuation(L, x^6 - t); q + (x^6 + 2*t)-adic valuation + + TESTS:: + + sage: TestSuite(v).run() + sage: TestSuite(w).run() + sage: TestSuite(u).run() + sage: TestSuite(q).run() + """ def _call_(self, f): r""" @@ -664,17 +740,6 @@ def __cmp__(self, other): return cmp(type(self),type(other)) return cmp(self._base_valuation,other._base_valuation) - def shift(self, f, v): - if f.parent() is not self.domain(): - raise ValueError - - if v == 0: - return f - elif v < 0: - return f/self._base_valuation.element_with_valuation(-v) - else: - return f*self._base_valuation.element_with_valuation(v) - def value_group(self): return self._base_valuation.value_group() diff --git a/gauss_valuation.py b/gauss_valuation.py index ee57540a5eb..759cda79214 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -571,6 +571,11 @@ def change_ring(self, base_ring): base_valuation = self._base_valuation.change_ring(base_ring) return GaussValuation(self.domain().change_ring(base_ring), base_valuation) + def extension(self, ring): + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + if is_PolynomialRing(ring) and len(ring.gens()) == 1: + return GaussValuation(ring, self._base_valuation.extension(ring.base())) + def is_gauss_valuation(self): r""" Return whether this valuation is a Gauss valuation. diff --git a/padic_valuation.py b/padic_valuation.py index 8fdcdfe8654..436b0a33a27 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -905,15 +905,25 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): for v in expandables: leaves.extend(v.mac_lane_step(G)) - def change_ring(self, base_ring): - return pAdicValuation(base_ring, self.prime()) + def change_ring(self, ring): + return pAdicValuation(ring, self.prime()) + + def extension(self, ring): + if ring.has_coerce_map_from(self.domain()): + return pAdicValuation(ring, self) + raise NotImplementedError + + def restriction(self, ring): + if self.domain().has_coerce_map_from(ring): + return pAdicValuation(ring, self.prime()) - def extension(self, L, algorithm="mac_lane"): K = self.domain() if L is K: return self + if L is K.fraction_field(): + return pAdicValuation(L, self) if L.base_ring() is not K: - raise ValueError("L must be a simple finite extension of %s"%K) + raise ValueError("L must be a simple finite extension of %r but %r is not"%(K, L)) if algorithm == "ideal": I = L.ideal(self.prime()).factor() @@ -1013,19 +1023,6 @@ def uniformizer(self): """ return self.domain().uniformizer() - def residue_field(self): - """ - Return the residue field of the ring of integers of this valuation. - - EXAMPLES:: - - sage: v = pAdicValuation(Zp(3)) - sage: v.residue_field() - Finite Field of size 3 - - """ - return self.domain().residue_field() - def shift(self, c, v): """ Multiply ``c`` by a ``v``th power of the uniformizer. @@ -1078,10 +1075,6 @@ def _mac_lane_step(self): assert E == self._valuation.E() assert F == self._valuation.F() - @cached_method - def residue_field(self): - return self._valuation.residue_field() - def _repr_(self): return "%r-adic valuation"%(self._valuation) diff --git a/trivial_valuation.py b/trivial_valuation.py index b8edff2e156..47ec7a03c02 100644 --- a/trivial_valuation.py +++ b/trivial_valuation.py @@ -395,13 +395,29 @@ def lift(self, X): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: v = TrivialPseudoValuation(QQ) + sage: v = TrivialValuation(QQ) sage: v.lift(v.residue_ring().zero()) 0 """ return self.residue_ring().coerce(X) + def extension(self, ring): + r""" + Return the unique extension of this valuation to ``ring``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = TrivialValuation(ZZ) + sage: v.extension(QQ) + Trivial valuation on Rational Field + + """ + if ring.has_coerce_map_from(self.domain()): + return TrivialValuation(ring) + return super(DiscretePseudoValuation, self).extension(ring) + TrivialValuation = TrivialValuationFactory(TrivialDiscreteValuation, DiscreteValuationSpace, "TrivialValuation") TrivialPseudoValuation = TrivialValuationFactory(TrivialDiscretePseudoValuation, DiscretePseudoValuationSpace, "TrivialPseudoValuation") diff --git a/valuation_space.py b/valuation_space.py index 3cc14cecc8e..1691dc9be8e 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -438,6 +438,60 @@ def lift(self, X): """ + def extension(self, ring): + r""" + Return the unique extension of this valuation to ``ring``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 2) + sage: w = v.extension(QQ) + sage: w.domain() + Rational Field + + """ + if ring is self.domain(): + return self + raise NotImplementedError("extending %r from %r to %r not implemented"%(self, self.domain(), ring)) + + def restriction(self, ring): + r""" + Return the restriction of this valuation to ``ring``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 2) + sage: w = v.restriction(ZZ) + sage: w.domain() + Integer Ring + + """ + if ring is self.domain(): + return self + raise NotImplementedError("restricting %r from %r to %r not implemented"%(self, self.domain(), ring)) + + def change_ring(self, ring): + r""" + Return this valuation over ``ring``. + + Unlike :meth:`extension` or meth:`reduction`, this might not be + completely sane mathematically. It is essentially a conversion of + this valuation into another space of valuations. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 3) + sage: v.change_ring(ZZ) + 3-adic valuation + + """ + if ring is self.domain(): + return self + raise NotImplementedError("changing %r from %r to %r not implemented"%(self, self.domain(), ring)) + def _test_add(self, **options): r""" Check that the (strict) triangle equality is satisfied for the @@ -778,8 +832,9 @@ def is_discrete_valuation(self): def residue_field(self): r""" - Return the residue field of this valuation, i.e., the elements of - non-negative valuation module the elements of positive valuation. + Return the residue field of this valuation, i.e., the field of + fractions of the :meth:`residue_ring`, the elements of non-negative + valuation module the elements of positive valuation. EXAMPLES:: @@ -789,24 +844,21 @@ def residue_field(self): sage: TrivialValuation(QQ).residue_field() Rational Field - Note that discrete valuations do not always define a residue - field:: - sage: TrivialValuation(ZZ).residue_field() - Traceback (most recent call last): - ... - ValueError: The residue ring of this valuation is not a field. + Rational Field sage: GaussValuation(ZZ['x'], pAdicValuation(ZZ, 2)).residue_field() - Traceback (most recent call last): - ... - ValueError: The residue ring of this valuation is not a field. + Rational function field in x over Finite Field of size 2 """ ret = self.residue_ring() from sage.categories.fields import Fields - if ret not in Fields(): - raise ValueError("The residue ring of this valuation is not a field.") - return ret + if ret in Fields(): + return ret + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + if is_PolynomialRing(ret): + from sage.rings.function_field.all import FunctionField + return FunctionField(ret.base_ring().fraction_field(), names=(ret.variable_name(),)) + return ret.fraction_field() def _test_no_infinite_nonzero(self, **options): r""" From 2adeb071f2233ed2630e8298a6127bc1391eea54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 18 Oct 2016 21:00:05 -0500 Subject: [PATCH 038/740] add trivial tests for restriction, extension, and change_ring --- valuation_space.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/valuation_space.py b/valuation_space.py index 1691dc9be8e..c43df1d6a79 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -709,6 +709,52 @@ def _test_lift(self, **options): if X != 0: tester.assertEqual(self(x), 0) + def _test_restriction(self, **options): + r""" + Check the correctness of reductions. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: v._test_restriction() + + """ + tester = self._tester(**options) + + tester.assertEqual(self.restriction(self.domain()), self) + + def _test_extension(self, **options): + r""" + Check the correctness of extensions. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: v._test_extension() + + """ + tester = self._tester(**options) + + tester.assertEqual(self.extension(self.domain()), self) + + def _test_change_ring(self, **options): + r""" + Check the correctness of :meth:`change_ring`. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: v._test_change_ring() + + """ + tester = self._tester(**options) + + tester.assertEqual(self.change_ring(self.domain()), self) + + class DiscreteValuationSpace(DiscretePseudoValuationSpace): r""" The space of discrete valuations on ``domain``. From 715e5bae53ab5883c0c1aa725235deed0902d37e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 18 Oct 2016 23:48:22 -0500 Subject: [PATCH 039/740] Use is_subring() instead of has_coerce_map_from since the latter is not injective --- function_field_valuation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/function_field_valuation.py b/function_field_valuation.py index b364cb4388e..5bfea861396 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -511,17 +511,17 @@ def extension(self, L): if L is self.domain(): return self if L in FunctionFields(): - if L.has_coerce_map_from(K): + if K.is_subring(K): if L.base() is K: # L is a simple extension of the domain of this valuation W = self.mac_lane_approximants(L.polynomial(), precision_cap=infinity) if len(W) > 1: raise ValueError("valuation %r on %r does not uniquely extend to %r"%(self, K, L)) return FunctionFieldValuation(L, W[0]) - elif L.base() is not L and L.base().has_coerce_map_from(K): + elif L.base() is not L and K.is_subring(L): # recursively call this method for the tower of fields return self.extension(L.base()).extension(L) - elif L.constant_field() is not K.constant_field() and L.constant_field().has_coerce_map_from(K.constant_field()): + elif L.constant_field() is not K.constant_field() and K.constant_field().is_subring(L): # extend the underlying valuation on the polynomial ring w = self._base_valuation.extension(L._ring) return FunctionFieldValuation(L, w) From 01ac0ef038ad8df49e962b8c6dda582026abcaaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 19 Oct 2016 08:51:50 -0500 Subject: [PATCH 040/740] test classical valuations --- function_field_valuation.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/function_field_valuation.py b/function_field_valuation.py index 5bfea861396..82933df3249 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -303,6 +303,26 @@ class ClassicalRationalFunctionFieldValuation_base(RationalFunctionFieldValuatio sage: TestSuite(v).run() """ + def _test_classical_residue_field(self, **options): + r""" + Check correctness of the residue field of a discrete valuation at a + classical point. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, x^2 + 1) + sage: v._test_classical_residue_field() + + """ + tester = self._tester(**options) + + try: + tester.assertTrue(self.domain().constant_field().is_subring(self.residue_field())) + except NotImplementedError: + # is_injective is often not implemented by morphisms + pass class InducedFunctionFieldValuation_base(FunctionFieldValuation_base): r""" From 5d2aee921c2f7f950c8f3c5ca91e4dc558a891f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 26 Oct 2016 16:10:51 -0500 Subject: [PATCH 041/740] Implement limit valuations for function fields and p-adics --- __init__.py | 3 +- augmented_valuation.py | 5 +- developing_valuation.py | 33 ++- function_field_valuation.py | 186 +++++++----- gauss_valuation.py | 29 ++ limit_valuation.py | 548 ++++++++++++++++++++++++++++++++++++ padic_valuation.py | 30 +- valuation_space.py | 50 +++- 8 files changed, 781 insertions(+), 103 deletions(-) create mode 100644 limit_valuation.py diff --git a/__init__.py b/__init__.py index 93532609416..3c92f0ba1b0 100644 --- a/__init__.py +++ b/__init__.py @@ -10,7 +10,8 @@ # local file and the instances that come from the mac_lane import define # different types) from .trivial_valuation import TrivialDiscreteValuation, TrivialDiscretePseudoValuation -from .function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalRationalFunctionFieldValuation_base +from .function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldLimitValuation +from .limit_valuation import LimitValuation_base # ================= # MONKEY PATCH SAGE diff --git a/augmented_valuation.py b/augmented_valuation.py index 095eb35077f..47eff910e45 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -677,7 +677,7 @@ def lift(self, F): # in the last step of reduce, the f_iQ^i are reduced, and evaluated at # the generator of the residue field # here, we undo this: - coeffs = [ R0(c if self.psi().degree()==1 else list(c._vector_())) for c in F.coefficients(sparse=False) ] + coeffs = [ R0(c if self.psi().degree()==1 else list(c._vector_() if hasattr(c, '_vector_') else c.list())) for c in F.coefficients(sparse=False) ] coeffs = [ self._base_valuation.lift(c) for c in coeffs ] # now the coefficients correspond to the expansion with (f_iQ^i)(Q^{-1} phi)^i @@ -962,3 +962,6 @@ def uniformizer(self): def is_gauss_valuation(self): # Is this correct? Can there be trivial augmentations? return False + + def _make_monic_integral(self, G): + return self._base_valuation._make_monic_integral(G) diff --git a/developing_valuation.py b/developing_valuation.py index 0b9b7ba6570..72adfde2a43 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -236,8 +236,8 @@ def is_equivalence_unit(self, f): """ # defined on p.497 of [ML1936'] - if f.parent() is not self.domain(): - raise ValueError("f must be in the domain of the valuation") + + f = self.domain().coerce(f) if f.is_zero(): return False @@ -531,7 +531,10 @@ def is_minimal(self, f): raise NotImplementedError("is_minimal() only implemented for equivalence-irreducible polynomials") from gauss_valuation import GaussValuation if self.is_gauss_valuation(): - return f.is_monic() and self.reduce(f).is_irreducible() + if f.is_monic(): + if self(f) < 0: + raise NotImplementedError("Investigate what is the right behaviour here") + return self.reduce(f).is_irreducible() if self.is_equivalent(self.phi(), f): # TODO: reference new Lemma return f.degree() == self.phi().degree() @@ -542,7 +545,7 @@ def is_minimal(self, f): @cached_method def equivalence_decomposition(self, f): - """ + r""" Return an equivalence decomposition of ``f``, i.e., a polynomial `g(x)=e(x)\prod_i \phi_i(x)` with `e(x)` an equivalence unit (see :meth:`is_equivalence_unit()`) and the `\phi_i` key polynomials (see @@ -639,8 +642,6 @@ def equivalence_decomposition(self, f): raise ValueError("f must be in the domain of the valuation") if f.is_zero(): raise ValueError("equivalence decomposition of zero is not defined") - if any([self.constant_valuation()(c)<0 for c in f.list()]): - raise ValueError("f must be integral") from sage.structure.factorization import Factorization if not self.domain().base_ring().is_field(): @@ -690,8 +691,7 @@ def equivalence_decomposition(self, f): F.append((self.phi(),phi_divides)) ret = Factorization(F, unit=unit) - # assert self.is_equivalent(ret.prod(), f0) -- this might fail because of leading zeros - assert self((ret.prod() - f0).map_coefficients(lambda c:_lift_to_maximal_precision(c))) + assert self.is_equivalent(ret.prod(), f0) # this might fail because of leading zeros assert self.is_equivalence_unit(ret.unit()) return ret @@ -1000,6 +1000,11 @@ def residue_ring(self): """ return self.domain().change_ring(self.residue_field()) + def _make_monic_integral(self, G): + if G.is_monic() and self(G) >= 0: + return G + raise NotImplementedError("The polynomial %r is not monic integral and %r does not provide the means to rewrite it to a monic integral polynomial."%(G, self)) + def mac_lane_step(self, G, assume_squarefree=False): r""" @@ -1019,10 +1024,18 @@ def mac_lane_step(self, G, assume_squarefree=False): """ from sage.misc.misc import verbose verbose("Expanding %s towards %s"%(self,G),caller_name="mac_lane_step") - assert not G.is_constant() R = G.parent() if R is not self.domain(): raise ValueError("G must be defined over the domain of this valuation") + if not G.is_monic(): + # G must be monic, there is no fundamental reason for this, but the implementation makes this assumption in some places. + # We try to turn G into a monic integral polynomial that describes the same extension + return self.mac_lane_step(self._make_monic_integral(G), assume_squarefree=assume_squarefree) + if self(G) < 0: + # G must be integral, otherwise, e.g., the effective degree is too low + # We try to turn G into a monic integral polynomial that describes the same extension + return self.mac_lane_step(self._make_monic_integral(G), assume_squarefree=assume_squarefree) + assert not G.is_constant() if not assume_squarefree and not G.is_squarefree(): raise ValueError("G must be squarefree") @@ -1035,7 +1048,7 @@ def mac_lane_step(self, G, assume_squarefree=False): return [self.augmentation(G, infinity)] F = self.equivalence_decomposition(G) - assert len(F), "%s factored as a unit %s"%(G,F) + assert len(F), "%s equivalence-decomposese as an equivalence-unit %s"%(G,F) ret = [] for phi,e in F: diff --git a/function_field_valuation.py b/function_field_valuation.py index 82933df3249..4cc70ae9ad8 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -27,6 +27,7 @@ from valuation import DiscretePseudoValuation from trivial_valuation import TrivialValuation +from limit_valuation import LimitValuationFiniteExtension class FunctionFieldValuationFactory(UniqueFactory): r""" @@ -113,6 +114,19 @@ class FunctionFieldValuationFactory(UniqueFactory): sage: w(x - w) # optional: integrated 1/3 + There are several ways to create valuations on extensions of rational + function fields:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x); L + Function field in y defined by y^2 - x + + A place that has a unique extension can just be defined downstairs:: + + sage: v = FunctionFieldValuation(L, x); v + (x - 1)-adic valuation + """ def create_key(self, domain, prime): r""" @@ -176,7 +190,7 @@ def create_key_from_place(self, domain, generator): if domain.base_field() is not domain: # if this is an extension field, construct the unique place over # the place on the subfield - return self.create_key(domain.base_field(), FunctionFieldValuation(domain.base_field(), generator)) + return self.create_key(domain, FunctionFieldValuation(domain.base_field(), generator)) if generator in domain.constant_base_field(): # generator is a constant, we associate to it the place which @@ -197,7 +211,7 @@ def create_key_from_place(self, domain, generator): return self.create_key(domain, valuation) elif generator == ~domain.gen(): # generator is 1/x, the infinite place - return (domain, ~domain.gen()) + return domain, ~domain.gen() else: raise ValueError("a place must be given by an irreducible polynomial or the inverse of the generator") @@ -215,11 +229,13 @@ def create_key_from_valuation(self, domain, valuation): sage: v = FunctionFieldValuation(K, w) # indirect doctest """ + if valuation.domain() is domain: + return domain, valuation if valuation.domain() is domain._ring: if domain.base_field() is not domain: - # on a extension of the form K[x]/(G), a valuation on K[x] must + # on an extension of the form K[x]/(G), a valuation on K[x] must # send exactly (G) to infinity - G = L.polynomial() + G = domain.polynomial() if valuation(G) != infinity: raise ValueError("valuation must be a pseudo-valuation which sends the defining polynomial to infinity but %r sends %r to %r"%(valuation, G, valuation(G))) if valuation.constant_valuation().domain() is not domain.base_field(): @@ -230,7 +246,12 @@ def create_key_from_valuation(self, domain, valuation): if not valuation.is_discrete_valuation(): raise ValueError("valuation must be a discrete valuation but %r is not."%(valuation,)) return domain, valuation - raise NotImplementedError("automatic extension of valuations to the full domain not implemented yet") + + if valuation.domain().is_subring(domain.base_field()): + # valuation is defined on a subring of this function field, try to lift it + return self.create_key(domain, valuation.extension(domain)) + + raise NotImplementedError("extension of valuation from %r to %r not implemented yet"%(valuation.domain(), domain)) def create_object(self, version, key, **extra_args): r""" @@ -247,14 +268,16 @@ def create_object(self, version, key, **extra_args): """ domain, valuation = key + from sage.rings.valuation.valuation_space import DiscreteValuationSpace + parent = DiscreteValuationSpace(domain) + if valuation in domain: + assert(valuation == ~domain.gen()) + return parent.__make_element_class__(InfiniteRationalFunctionFieldValuation)(parent) + if domain is valuation.domain(): + return valuation if domain.base_field() is domain: - from sage.rings.valuation.valuation_space import DiscreteValuationSpace - parent = DiscreteValuationSpace(domain) - if valuation in domain: - assert(valuation == ~domain.gen()) - return parent.__make_element_class__(InfiniteRationalFunctionFieldValuation)(parent) return parent.__make_element_class__(FiniteRationalFunctionFieldValuation)(parent, valuation) - raise NotImplementedError + raise NotImplementedError("valuation on %r from %r on %r"%(domain, valuation, valuation.domain())) FunctionFieldValuation = FunctionFieldValuationFactory("FunctionFieldValuation") @@ -272,6 +295,63 @@ class FunctionFieldValuation_base(DiscretePseudoValuation): sage: TestSuite(v).run() """ + def _extensions_from_constant_field(self, L): + raise NotImplementedError + + def extensions(self, L): + K = self.domain() + from sage.categories.function_fields import FunctionFields + if L is self.domain(): + return self + if L in FunctionFields(): + if K.is_subring(K): + if L.base() is K: + # L is a simple extension of the domain of this valuation + W = self.mac_lane_approximants(L.polynomial()) + from valuation_space import DiscreteValuationSpace + parent = DiscreteValuationSpace(L) + return [parent.__make_element_class__(FunctionFieldLimitValuation)(parent, w, L.polynomial()) for w in W] + elif L.base() is not L and K.is_subring(L): + # recursively call this method for the tower of fields + from operator import add + return reduce(add, A, []) + elif L.constant_field() is not K.constant_field() and K.constant_field().is_subring(L): + return self._extensions_from_constant_field(L) + raise NotImplementedError("extension of %r from %r to %r not implemented"%(self, K, L)) + + def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): + # TODO: move this to discrete valuation + R = G.parent() + if R.base_ring() is not self.domain(): + raise ValueError("G must be defined over the domain of this valuation") + if not assume_squarefree and not G.is_squarefree(): + raise ValueError("G must be squarefree") + + from sage.rings.all import infinity + from gauss_valuation import GaussValuation + + leaves = [ GaussValuation(R, self)] + while True: + ef = [ v.E()*v.F() for v in leaves] + if sum(ef) == G.degree(): + if precision_cap is None or all([v(v.phi())>precision_cap for v in leaves]): + return leaves + + expandables = [] + new_leaves = [] + for v in leaves: + if v(G) is infinity: + new_leaves.append(v) + else: + expandables.append(v) + leaves = new_leaves + + if not expandables: + return leaves + + for v in expandables: + leaves.extend(v.mac_lane_step(G)) + class RationalFunctionFieldValuation_base(FunctionFieldValuation_base): r""" @@ -288,7 +368,7 @@ class RationalFunctionFieldValuation_base(FunctionFieldValuation_base): """ -class ClassicalRationalFunctionFieldValuation_base(RationalFunctionFieldValuation_base): +class ClassicalFunctionFieldValuation_base(FunctionFieldValuation_base): r""" Base class for discrete valuations on rational function fields that come from points on the projective line. @@ -298,7 +378,7 @@ class ClassicalRationalFunctionFieldValuation_base(RationalFunctionFieldValuatio sage: from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(5)) sage: v = FunctionFieldValuation(K, x) # indirect doctest - sage: isinstance(v, ClassicalRationalFunctionFieldValuation_base) + sage: isinstance(v, ClassicalFunctionFieldValuation_base) True sage: TestSuite(v).run() @@ -350,7 +430,7 @@ def __init__(self, parent, base_valuation): """ domain = parent.domain() if base_valuation.domain() is not domain._ring: - raise ValueError + raise ValueError("base valuation must be defined on %r but %r is defined on %r"%(domain._ring, base_valuation, base_valuation.domain())) self._base_valuation = base_valuation RationalFunctionFieldValuation_base.__init__(self, parent) @@ -449,19 +529,19 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): sage: F = y^21 + x*y^20 + (x^3 + x + 1)*y^18 + (x^3 + 1)*y^17 + (x^4 + x)*y^16 + (x^7 + x^6 + x^3 + x + 1)*y^15 + x^7*y^14 + (x^8 + x^7 + x^6 + x^4 + x^3 + 1)*y^13 + (x^9 + x^8 + x^4 + 1)*y^12 + (x^11 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2)*y^11 + (x^12 + x^9 + x^8 + x^7 + x^5 + x^3 + x + 1)*y^10 + (x^14 + x^13 + x^10 + x^9 + x^8 + x^7 + x^6 + x^3 + x^2 + 1)*y^9 + (x^13 + x^9 + x^8 + x^6 + x^4 + x^3 + x)*y^8 + (x^16 + x^15 + x^13 + x^12 + x^11 + x^7 + x^3 + x)*y^7 + (x^17 + x^16 + x^13 + x^9 + x^8 + x)*y^6 + (x^17 + x^16 + x^12 + x^7 + x^5 + x^2 + x + 1)*y^5 + (x^19 + x^16 + x^15 + x^12 + x^6 + x^5 + x^3 + 1)*y^4 + (x^18 + x^15 + x^12 + x^10 + x^9 + x^7 + x^4 + x)*y^3 + (x^22 + x^21 + x^20 + x^18 + x^13 + x^12 + x^9 + x^8 + x^7 + x^5 + x^4 + x^3)*y^2 + (x^23 + x^22 + x^20 + x^17 + x^15 + x^14 + x^12 + x^9)*y + x^25 + x^23 + x^19 + x^17 + x^15 + x^13 + x^11 + x^5 sage: x = K._ring.gen() sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).augmentation(x,1)) - sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assume_squarefree for speed, not tested - factorization over function fields over finite fields is missing + sage: v0.mac_lane_approximants(F, assume_squarefree=True) # optional: integrated; assumes squarefree for speed [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x + 1) = 3/2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y) = 4/3, v(y^3 + x^4) = 13/3 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x) = 2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y^15 + y^13 + (x + 1)*y^12 + x*y^11 + (x + 1)*y^10 + y^9 + y^8 + x*y^6 + x*y^5 + y^4 + y^3 + y^2 + (x + 1)*y + x + 1) = 2 ]] sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).augmentation(x+1,1)) - sage: v0.mac_lane_approximants(F, assume_squarefree=True) # not tested, factorization over function fields over finite fields is missing + sage: v0.mac_lane_approximants(F, assume_squarefree=True) # optional: integrated; assumes squarefree for speed [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y) = 7/2, v(y^2 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1) = 15/2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y + x^2 + 1) = 7/2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y) = 3/4, v(y^4 + x^3 + x^2 + x + 1) = 15/4 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y^13 + x*y^12 + y^10 + (x + 1)*y^9 + (x + 1)*y^8 + x*y^7 + x*y^6 + (x + 1)*y^4 + y^3 + (x + 1)*y^2 + 1) = 2 ]] sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).augmentation(x^3+x^2+1,1)) - sage: v0.mac_lane_approximants(F, assume_squarefree=True) # not tested, factorization over function fields over finite fields is missing + sage: v0.mac_lane_approximants(F, assume_squarefree=True) # optional: integrated; assumes squarefree for speed [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y + x^3 + x^2 + x) = 2, v(y^2 + (x^6 + x^4 + 1)*y + x^14 + x^10 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2 + x) = 5 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^2 + (x^7 + x^5 + x^4 + x^3 + x^2 + x)*y + x^7 + x^5 + x + 1) = 3 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^3 + (x^8 + x^5 + x^4 + x^3 + x + 1)*y^2 + (x^7 + x^6 + x^5)*y + x^8 + x^5 + x^4 + x^3 + 1) = 3 ], @@ -525,29 +605,12 @@ def _repr_(self): return repr(self._base_valuation.constant_valuation()) return "Valuation on rational function field induced by %s"%self._base_valuation - def extension(self, L): - K = self.domain() - from sage.categories.function_fields import FunctionFields - if L is self.domain(): - return self - if L in FunctionFields(): - if K.is_subring(K): - if L.base() is K: - # L is a simple extension of the domain of this valuation - W = self.mac_lane_approximants(L.polynomial(), precision_cap=infinity) - if len(W) > 1: - raise ValueError("valuation %r on %r does not uniquely extend to %r"%(self, K, L)) - return FunctionFieldValuation(L, W[0]) - elif L.base() is not L and K.is_subring(L): - # recursively call this method for the tower of fields - return self.extension(L.base()).extension(L) - elif L.constant_field() is not K.constant_field() and K.constant_field().is_subring(L): - # extend the underlying valuation on the polynomial ring - w = self._base_valuation.extension(L._ring) - return FunctionFieldValuation(L, w) - raise NotImplementedError("extension of %r from %r to %r not implemented"%(self, K, L)) + def _extensions_from_constant_field(self, L): + # extend the underlying valuation on the polynomial ring + W = self._base_valuation.extensions(L._ring) + return [FunctionFieldValuation(L, w) for w in W] -class InfiniteRationalFunctionFieldValuation(ClassicalRationalFunctionFieldValuation_base): +class InfiniteRationalFunctionFieldValuation(RationalFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base): r""" Valuation of the infinite place of a function field. @@ -645,7 +708,7 @@ def reduce(self, f): if self(f) < 0: raise ValueError("reduction is only defined for elements of non-negative valuation") if self(f) > 0: - return self.domain().zero() + return self.residue_ring().zero() from sage.rings.function_field.function_field_valuation import FunctionFieldValuation return FunctionFieldValuation(self.domain(), self.domain().gen()).reduce(f.element()(~self.domain().gen())) @@ -670,7 +733,7 @@ def lift(self, F): assert F in self.domain() return self.domain()(F) -class FiniteRationalFunctionFieldValuation(ClassicalRationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base): +class FiniteRationalFunctionFieldValuation(ClassicalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, RationalFunctionFieldValuation_base): r""" Valuation of a finite place of a function field. @@ -702,8 +765,8 @@ class FiniteRationalFunctionFieldValuation(ClassicalRationalFunctionFieldValuati sage: TestSuite(v).run() sage: TestSuite(w).run() - sage: TestSuite(u).run() - sage: TestSuite(q).run() + sage: TestSuite(u).run() # long time + sage: TestSuite(q).run() # long time """ def _call_(self, f): @@ -735,6 +798,10 @@ def residue_ring(self): """ return self._base_valuation.residue_field() +class FunctionFieldLimitValuation(LimitValuationFiniteExtension): + def _to_approximation_domain(self, f): + return f.element() + class FunctionFieldPolymodValuation(InducedFunctionFieldValuation_base): def __init__(self, domain, base_valuation): from sage.rings.all import infinity @@ -772,38 +839,5 @@ def lift(self, F): return self.domain()(self._base_valuation._base_valuation.lift(F.element())) - def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): - # TODO: move this to discrete valuation - R = G.parent() - if R.base_ring() is not self.domain(): - raise ValueError("G must be defined over the domain of this valuation") - if not assume_squarefree and not G.is_squarefree(): - raise ValueError("G must be squarefree") - - from sage.rings.all import infinity - from gauss_valuation import GaussValuation - - leaves = [ GaussValuation(R, self)] - while True: - ef = [ v.E()*v.F() for v in leaves] - if sum(ef) == G.degree(): - if precision_cap is None or all([v(v.phi())>precision_cap for v in leaves]): - return leaves - - expandables = [] - new_leaves = [] - for v in leaves: - if v(G) is infinity: - new_leaves.append(v) - else: - expandables.append(v) - leaves = new_leaves - - if not expandables: - return leaves - - for v in expandables: - leaves.extend(v.mac_lane_step(G)) - def _repr_(self): return "Valuation on rational function field induced by %s"%self._base_valuation diff --git a/gauss_valuation.py b/gauss_valuation.py index 759cda79214..26c475d220e 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -601,3 +601,32 @@ def _augmentations(self): def is_trivial(self): return self._base_valuation.is_trivial() + + def _gt_(self, other): + if isinstance(other, GaussValuation_generic): + return self._base_valuation >= other.base_valuation + from augmented_valuation import AugmentedValuation + if isinstance(other, AugmentedValuation): + return False + raise NotImplementedError("Operator not implemented for these valuations.") + + def _lt_(self, other): + if isinstance(other, GaussValuation_generic): + return self._base_valuation <= other._base_valuation + from augmented_valuation import AugmentedValuation + if isinstance(other, AugmentedValuation): + return self <= other._base_valuation + raise NotImplementedError("Operator not implemented for these valuations.") + + def _make_monic_integral(self, G): + if not G.is_monic(): + # this might fail if the base ring is not a field + G = G / G.leading_coefficient() + while self(G) < 0: + u = self._base_valuation.uniformizer() + x = G.parent().gen() + # this might fail if the base ring is not a field + G = G.parent(G(x/u) * (u ** G.degree())) + assert G.is_monic() + return G + diff --git a/limit_valuation.py b/limit_valuation.py new file mode 100644 index 00000000000..0609071b277 --- /dev/null +++ b/limit_valuation.py @@ -0,0 +1,548 @@ +# -*- coding: utf-8 -*- +r""" +Valuations which are defined as limits of valuations. + +The discrete valuation of a complete field extends uniquely to a finite field +extension. This is not the case anymore for fields which are not complete with +respect to their discrete valuation. In this case, the extensions essentially +correspond to the factors of the defining polynomial of the extension over the +completion. However, these factors only exist over the completion and so the +valuation can not written down finitely as a number of +:class:`AugmentedValuation`s but only as a limit thereof. + +EXAMPLES: + +In this function field, the unique place of ``K`` which corresponds to the zero +point has two extensions to ``L``. The valuations corresponding to these +extensions can only be approximated:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 1) + sage: w = v.extensions(L); w + [[ Gauss valuation induced by (x - 1)-adic valuation, v(y - 1) = 1 , … ], + [ Gauss valuation induced by (x - 1)-adic valuation, v(y + 1) = 1 , … ]] + +The same phenomenon can be observed for valuations on number fields:: + + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 5) + sage: w = v.extensions(L); w + [[ Gauss valuation induced by 5-adic valuation, v(t + 2) = 1 , … ], + [ Gauss valuation induced by 5-adic valuation, v(t + 3) = 1 , … ]] + +.. NOTE:: + + We often rely on approximations of valuations even if we could represent the + valuation without using a limit. This is done to improve performance as many + computations already can be done correctly with an approximation:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L); w + [ Gauss valuation induced by Valuation at the infinite place, v(y) = 1/2 , … ] + sage: w._improve_approximation() + sage: w._approximation + [ Gauss valuation induced by Valuation at the infinite place, v(y) = 1/2, v(y^2 - 1/x) = +Infinity ] + +AUTHORS: + +- Julian Rueth (2016-10-19): initial version + +""" +#***************************************************************************** +# Copyright (C) 2016 Julian Rüth +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) +import sys, os +if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: + sys.path.append(os.getcwd()) + sys.path.append(os.path.dirname(os.getcwd())) + +from sage.misc.abstract_method import abstract_method +from valuation import DiscretePseudoValuation + +class LimitValuation_base(DiscretePseudoValuation): + r""" + Base class for limit valuations. + + A limit valuation is realized as an approximation of a valuation and means + to improve that approximation when necessary. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L) + + The currently used approximation can be found in the ``_approximation`` + field:: + + sage: w._approximation + [ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2 ] + + Note that the approximation might be defined on a different domain:: + + sage: w.domain() + Function field in y defined by y^2 - x + sage: w._approximation.domain() + Univariate Polynomial Ring in y over Rational function field in x over Rational Field + + The methods :meth:`_to_approximation_domain` and + :meth:`_from_approximation_domain` move items back and forth between the + domains of definition. + + TESTS:: + + sage: isinstance(w, LimitValuation_base) + True + + sage: TestSuite(w).run() + + """ + def reduce(self, f): + r""" + Return the reduction of ``f`` as an element of :meth:`residue_ring`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 1) + sage: w = v.extensions(L)[0] + sage: w.reduce(y) + 1 + + """ + f = self.domain().coerce(f) + self._improve_approximation_for_reduce(f) + F = self._approximation.reduce(self._to_approximation_domain(f)) + return self.residue_ring()(F) + + @abstract_method + def _improve_approximation_for_reduce(self, f): + r""" + Replace our approximation with a sufficiently precise approximation to + correctly compute the reduction of ``f``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + For the unique extension over the place at zero, the initial + approximation is sufficient to compute the reduction of ``y``:: + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L) + sage: w._approximation + [ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2 ] + sage: w.reduce(y) + 0 + sage: w._approximation + [ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2 ] + + However, at a places over 1, the initial approximation is not + sufficient for some values:: + + sage: v = FunctionFieldValuation(K, 1) + sage: w = v.extensions(L)[0] + sage: w._approximation + [ Gauss valuation induced by (x - 1)-adic valuation, v(y - 1) = 1 ] + sage: w.reduce((y - 1) / (x - 1)) # indirect doctest + 1/2 + sage: w._approximation + [ Gauss valuation induced by (x - 1)-adic valuation, v(y - 1/2*x - 1/2) = 2 ] + sage: w.reduce((y - 1/2*x - 1/2) / (x - 1)^2) # indirect doctest + -1/8 + sage: w._approximation + [ Gauss valuation induced by (x - 1)-adic valuation, v(y + 1/8*x^2 - 3/4*x - 3/8) = 3 ] + + """ + + def _call_(self, f): + r""" + Return the valuation of ``f``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L) + sage: w(y) + 1/2 + + """ + f = self.domain().coerce(f) + self._improve_approximation_for_call(f) + return self._approximation(self._to_approximation_domain(f)) + + @abstract_method + def _improve_approximation_for_call(self, f): + r""" + Replace our approximation with a sufficiently precise approximation to + correctly compute the valuation of ``f``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + For the unique extension over the place at zero, the initial + approximation is sufficient to compute all valuations:: + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L) + sage: w._approximation + [ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2 ] + sage: w(x) + 1 + sage: w._approximation + [ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2 ] + + However, due to performance reasons, sometimes we improve the + approximation though it would not have been necessary (performing the + improvement step is faster in this case than checking whether the + approximation is sufficient):: + + sage: w(y) # indirect doctest + 1/2 + sage: w._approximation + [ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2, v(y^2 - x) = +Infinity ] + + """ + + def _to_approximation_domain(self, f): + r""" + Return ``f`` as an element in the domain of ``_approximation``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extensions(L)[0] + sage: w._to_approximation_domain(y).parent() + Univariate Polynomial Ring in y over Rational function field in x over Rational Field + + """ + return self._approximation.domain().coerce(f) + + def _from_approximation_domain(self, f): + r""" + Return ``f`` as an element in the domain of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extensions(L)[0] + sage: w._from_approximation_domain(w._approximation.domain().gen()).parent() + Function field in y defined by y^2 - x + + """ + return self.domain().coerce(f) + + def lift(self, F): + r""" + Return a lift of ``F`` from the :meth:`residue_ring` to the + :meth:`domain` of this valuatiion. + + EXAMPLES;; + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^4 - x^2 - 2*x - 1) + + sage: v = FunctionFieldValuation(K, 1) + sage: w = v.extensions(L)[0]; w + [ Gauss valuation induced by (x - 1)-adic valuation, v(y^2 - 2) = 1 , … ] + sage: u = w.reduce(y); u + u1 + sage: y == w.lift(u) + True + + """ + F = self.residue_ring().coerce(F) + f = self._approximation.lift(F) + return self._from_approximation_domain(f) + + def uniformizer(self): + r""" + Return a uniformizing element for this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L) + sage: w(w.uniformizer()) + 1/2 + + """ + return self._from_approximation_domain(self._approximation.uniformizer()) + +class LimitValuationFiniteExtension(LimitValuation_base): + r""" + A limit valuation that comes from a finite simple extension of the form + `L=K[x]/(G)`. + + Starting from ``approximation``, the approximation used to perform + computations is improved using the Mac Lane algorithm. The initial value of + ``approximation`` must already have the final residue field and + ramification index and it must approximate a unique extension on `L`. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L) + + sage: TestSuite(w).run() + + """ + def __init__(self, parent, approximation, G): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 5) + sage: w = v.extensions(L); w + [[ Gauss valuation induced by 5-adic valuation, v(t + 2) = 1 , … ], + [ Gauss valuation induced by 5-adic valuation, v(t + 3) = 1 , … ]] + + sage: isinstance(w[0], LimitValuation_base) + True + sage: isinstance(w[1], LimitValuation_base) + True + + """ + DiscretePseudoValuation.__init__(self, parent) + self._initial_approximation = approximation # kept for consistent printing + self._approximation = approximation + self._G = G + + def _improve_approximation(self): + r""" + Perform one step of the Mac Lane algorithm to improve our approximation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: w = v.extension(L) + sage: w._approximation + [ Gauss valuation induced by 2-adic valuation, v(t + 1) = 1/2 ] + sage: w._improve_approximation() + sage: w._approximation + [ Gauss valuation induced by 2-adic valuation, v(t + 1) = 1/2, v(t^2 + 1) = +Infinity ] + sage: w._improve_approximation() + sage: w._approximation + [ Gauss valuation induced by 2-adic valuation, v(t + 1) = 1/2, v(t^2 + 1) = +Infinity ] + + """ + from sage.rings.all import infinity + if self._approximation(self._G) == infinity: + # an infinite valuation can not be improved further + return + + approximations = self._approximation.mac_lane_step(self._G) + assert(len(approximations)==1) + self._approximation = approximations[0] + + def _improve_approximation_for_call(self, f): + r""" + Replace our approximation with a sufficiently precise approximation to + correctly compute the valuation of ``f``. + + EXAMPLES: + + In this examples, the approximation is increased unnecessarily. The + first approximation would have been precise enough to compute the + valuation of ``t + 2``. However, it is faster to improve the + approximation (perform one step of the Mac Lane algorithm) than to + check for this:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 5) + sage: w = v.extensions(L)[0] + sage: w._approximation + [ Gauss valuation induced by 5-adic valuation, v(t + 2) = 1 ] + sage: w(t + 2) # indirect doctest + 1 + sage: w._approximation + [ Gauss valuation induced by 5-adic valuation, v(t + 7) = 2 ] + + """ + from sage.rings.all import infinity + if self._approximation._mu == infinity: + return + if f == 0: + return + g = self._to_approximation_domain(f) + if self._approximation.is_equivalence_unit(g): + return + + self._improve_approximation() + return self._improve_approximation_for_call(f) + + def _improve_approximation_for_reduce(self, f): + r""" + Replace our approximation with a sufficiently precise approximation to + correctly compute the reduction of ``f``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 5) + sage: w = v.extensions(L)[0] + sage: w._approximation + [ Gauss valuation induced by 5-adic valuation, v(t + 2) = 1 ] + sage: w.reduce((t + 2) / 5) # indirect doctest + 4 + sage: w._approximation + [ Gauss valuation induced by 5-adic valuation, v(t + 7) = 2 ] + + """ + g = self._to_approximation_domain(f) + if self._approximation(g) > 0: + return + self._improve_approximation_for_call(f) + + def _repr_(self): + r""" + Return a printable representation of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: v.extension(L) # indirect doctest + [ Gauss valuation induced by 2-adic valuation, v(t + 1) = 1/2 , … ] + + """ + from sage.rings.all import infinity + from augmented_valuation import AugmentedValuation + if self._initial_approximation(self._G) < infinity: + if isinstance(self._initial_approximation, AugmentedValuation): + return repr(self._initial_approximation)[:-1] + ", … ]" + return repr(self._initial_approximation) + + def _eq_(self, other): + r""" + Return whether this valuation is indistinguishable from ``other``. + + EXAMPLES: + + We deem two valuations indistinguishable if they can not be + distinguished without considering hidden fields:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: w = v.extension(L) + sage: ww = v.extension(L) + sage: w == ww # indirect doctest + True + sage: w._improve_approximation() + sage: w == ww + True + sage: w._approximation == ww._approximation + False + + """ + return isinstance(other, LimitValuationFiniteExtension) and self._G == other._G and self._initial_approximation == other._initial_approximation + + def residue_ring(self): + r""" + Return the residue field of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: w = v.extension(L) + sage: w.residue_ring() + Finite Field of size 2 + + TESTS:: + + sage: w._improve_approximation() + sage: w.residue_ring() + Finite Field of size 2 + + """ + R = self._approximation.residue_ring() + from sage.categories.fields import Fields + if R in Fields(): + # the approximation ends in v(phi)=infty + return R + else: + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + assert(is_PolynomialRing(R)) + return R.base_ring() diff --git a/padic_valuation.py b/padic_valuation.py index 436b0a33a27..175bc6973c9 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -25,6 +25,7 @@ sys.path.append(os.path.dirname(os.getcwd())) from valuation import DiscretePseudoValuation +from limit_valuation import LimitValuationFiniteExtension from sage.structure.factory import UniqueFactory from sage.misc.cachefunc import cached_method from sage.misc.fast_methods import WithEqualityById @@ -262,14 +263,14 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime) v = GaussValuation(G.parent(), v) # Then, we lift valuations defined on polynmial rings which are subrings of K[x] to K[x] if v.domain() != G.parent(): - v = v.change_ring(K) + v = v.extension(G.parent()) assert(v.domain() is G.parent()) # To obtain uniqueness of p-adic valuations, we need a canonical # description of v. We consider all extensions of vK to L and select # the one approximated by v. vK = v.constant_valuation() - candidates = vK.mac_lane_approximants(G) + candidates = vK.extensions(L) # We make use of the partial order on discrete pseudo-valuations to # single out our candidate @@ -908,10 +909,23 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): def change_ring(self, ring): return pAdicValuation(ring, self.prime()) - def extension(self, ring): - if ring.has_coerce_map_from(self.domain()): - return pAdicValuation(ring, self) - raise NotImplementedError + def extensions(self, ring): + if self.domain() is ring: + return [self] + if self.domain().fraction_field() is not self.domain(): + if self.domain().fraction_field().is_subring(ring): + return pAdicValuation(self.domain().fraction_field(), self).extensions(ring) + if self.domain().is_subring(ring): + from sage.rings.number_field.number_field import is_NumberField + if is_NumberField(ring): + if ring.base() is self.domain(): + from valuation_space import DiscreteValuationSpace + from limit_valuation import LimitValuationFiniteExtension + parent = DiscreteValuationSpace(ring) + return [parent.__make_element_class__(pAdicLimitValuation)(parent, approximant, ring.relative_polynomial()) for approximant in self.mac_lane_approximants(ring.relative_polynomial())] + if ring.base() is not ring and self.domain().is_subring(ring.base()): + return sum([w.extensions(ring) for w in self.extensions(ring.base())], []) + raise NotImplementedError("extensions of %r from %r to %r not implemented"%(self, self.domain(), ring)) def restriction(self, ring): if self.domain().has_coerce_map_from(ring): @@ -1145,3 +1159,7 @@ def _repr_(self): return repr(unique_approximant[0]) return "[ %s ]-adic valuation"%(", ".join("v(%r) = %r" if (isinstance(v, AugmentedValuation) and v.domain() == self._valuation.domain()) else repr(v) for v in unique_approximant)) return "%s-adic valuation"%(self._valuation) + +class pAdicLimitValuation(LimitValuationFiniteExtension): + def _to_approximation_domain(self, f): + return f.polynomial(self.domain().variable_name()) diff --git a/valuation_space.py b/valuation_space.py index c43df1d6a79..277291e55dd 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -195,7 +195,17 @@ def _element_constructor_(self, x): """ if isinstance(x.parent(), DiscretePseudoValuationSpace): - return self(x.change_ring(self.domain())) + if x.domain() is not self.domain(): + try: + return self(x.change_ring(self.domain())) + except NotImplementedError: + pass + else: + # If this is an element of a discrete pseudo-valuation space over the same domain, + # then we treat it as an element of this space (see __contains__), i.e., we do not + # actually change x.parent() to self here if it is, e.g., a discrete valuation space. + # This might be surprising but is how facades work for example. + return x raise ValueError("element can not be converted into the space of %r"%(self,)) class ElementMethods: @@ -450,9 +460,27 @@ def extension(self, ring): sage: w.domain() Rational Field + """ + extensions = self.extensions(ring) + assert(len(extensions)) + if len(extensions) > 1: + raise ValueError("there is no unique extension of %r from %r to %r"%(self, self.domain(), ring)) + return extensions[0] + + def extensions(self, ring): + r""" + Return the extensions of this valuation to ``ring``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 2) + sage: v.extensions(QQ) + [2-adic valuation] + """ if ring is self.domain(): - return self + return [self] raise NotImplementedError("extending %r from %r to %r not implemented"%(self, self.domain(), ring)) def restriction(self, ring): @@ -738,6 +766,7 @@ def _test_extension(self, **options): tester = self._tester(**options) tester.assertEqual(self.extension(self.domain()), self) + tester.assertEqual(self.extensions(self.domain()), [self]) def _test_change_ring(self, **options): r""" @@ -782,9 +811,6 @@ def __init__(self, domain): """ DiscretePseudoValuationSpace.__init__(self, domain) - # discrete valuations are discrete pseudo-valuations - self.register_embedding(self.hom(lambda x:x, DiscretePseudoValuationSpace(domain))) - def _element_constructor_(self, x): r""" Create an element in this space from ``x``. @@ -814,11 +840,17 @@ def _element_constructor_(self, x): True """ - # We try to convert discrete pseudo-valuations that claims to be a - # valuation into this space + # We accept any discrete pseudo-valuation that claims to be a discrete valuation if isinstance(x.parent(), DiscretePseudoValuationSpace) and x.is_discrete_valuation(): - # after we base-changed them into our domain - return DiscretePseudoValuationSpace(self.domain())(x) + if x.domain() is not self.domain(): + # after we base-changed them into our domain + return DiscretePseudoValuationSpace(self.domain())(x) + # If this is a valuation in a discrete pseudo-valuation space over the same domain, + # then we treat it as an element of this space (see __contains__), i.e., we do not + # actually change x.parent() to self here if it is, e.g., a + # discrete pseudo-valuation space. This might be surprising but is + # also how facades work for example. + return x raise ValueError("element does not convert to a discrete valuation in %r"%(self,)) def _repr_(self): From d117f0a74388b55a118b81b3dad4dff26b32b5a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 27 Oct 2016 17:16:44 -0500 Subject: [PATCH 042/740] Fix comments for limit valuations They are actually not limits but based on limits --- limit_valuation.py | 52 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/limit_valuation.py b/limit_valuation.py index 0609071b277..be713ed879c 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -325,13 +325,25 @@ def uniformizer(self): class LimitValuationFiniteExtension(LimitValuation_base): r""" - A limit valuation that comes from a finite simple extension of the form - `L=K[x]/(G)`. + A valuation on a quotient of the form `L=K[x]/(G)` with an irreducible `G`. - Starting from ``approximation``, the approximation used to perform - computations is improved using the Mac Lane algorithm. The initial value of - ``approximation`` must already have the final residue field and - ramification index and it must approximate a unique extension on `L`. + Internally, this valuation is represented as a limit of valuations on + `K[x]` which sends `G` to infinity. Starting from an ``approximation``, + i.e., one element of the sequence that the limit taken over, that + approximation is used to perform computations and it is improved using the + Mac Lane algorithm whenever a better approximation is needed. + + INPUT: + + - ``parent`` -- the containing valuation space (usually the space of + discrete valuations on `L`) + + - ``approximation`` -- a Gauss valuation or an augmentation thereof; a + discrete valuation on `K[x]` which already has the residue field, and + ramification index of the limit and which approximates exactly one + valuation on `L`. + + - ``G`` -- an irreducible polynomial in `K[x]` TESTS:: @@ -427,15 +439,41 @@ def _improve_approximation_for_call(self, f): sage: w._approximation [ Gauss valuation induced by 5-adic valuation, v(t + 7) = 2 ] + ALGORITHM: + + Write `L=K[x]/(G)` and consider `g` a representative of the class + of ``f`` in `K[x]` (of minimal degree.) Write `v` for + ``self._approximation` and `\phi` for the last key polynomial of + `v`. With repeated quotient and remainder `g` has a unique + expansion as `g=\sum a_i\phi^i`. Suppose that `g` is an + equivalence-unit with respect to ``self._approximation``, i.e., + `v(a_0) < v(a_i\phi^i)` for all `i\ne 0`. If we denote the limit + valuation as `w`, then `v(a_i\phi^i)=w(a_i\phi^i)` since the + valuation of key polynomials does not change during augmentations + (Theorem 6.4 in [ML1936'].) By the strict triangle inequality, + `w(g)=v(g)`. + Note that any `g` which is not in `(G)` is an equivalence-unit + after finitely many steps of the Mac Lane algorithm. Indeed, + otherwise the valuation of `g` would be infinite (follows from + Theorem 5.1 in [ML1936']) since the valuation of the key + polynomials increases. + """ from sage.rings.all import infinity if self._approximation._mu == infinity: + # an infinite valuation can not be improved further return + if f == 0: + # zero always has infinite valuation (actually, this might + # not be desirable for inexact zero elements with leading + # zero coefficients.) return + g = self._to_approximation_domain(f) if self._approximation.is_equivalence_unit(g): - return + # see ALGORITHM above + return self._improve_approximation() return self._improve_approximation_for_call(f) From cb6fe4f92040a8b1ffb705031ff394afd0038cc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 27 Oct 2016 17:18:44 -0500 Subject: [PATCH 043/740] describe reduction algorithm --- limit_valuation.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/limit_valuation.py b/limit_valuation.py index be713ed879c..a0994da2607 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -498,6 +498,11 @@ def _improve_approximation_for_reduce(self, f): sage: w._approximation [ Gauss valuation induced by 5-adic valuation, v(t + 7) = 2 ] + ALGORITHM: + + The reduction is correct for an equivalence-unit, see + :meth:`_improve_approximation_for_call`. + """ g = self._to_approximation_domain(f) if self._approximation(g) > 0: From 5d57d92c8cc4095e3067c75a784424f64427251f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 29 Oct 2016 17:47:23 -0500 Subject: [PATCH 044/740] Properly model valuations on finite field extensions by limit valuations --- __init__.py | 4 +- augmented_valuation.py | 21 +- function_field_valuation.py | 173 ++++++---- gauss_valuation.py | 23 +- limit_valuation.py | 647 ++++++++++++++++++++++++------------ padic_valuation.py | 34 +- trivial_valuation.py | 22 +- valuation.py | 18 +- valuation_space.py | 5 +- 9 files changed, 606 insertions(+), 341 deletions(-) diff --git a/__init__.py b/__init__.py index 3c92f0ba1b0..82f4e047ece 100644 --- a/__init__.py +++ b/__init__.py @@ -10,8 +10,8 @@ # local file and the instances that come from the mac_lane import define # different types) from .trivial_valuation import TrivialDiscreteValuation, TrivialDiscretePseudoValuation -from .function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldLimitValuation -from .limit_valuation import LimitValuation_base +from .function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldFromLimitValuation +from .limit_valuation import LimitValuation, MacLaneLimitValuation, FiniteExtensionFromInfiniteValuation, FiniteExtensionFromLimitValuation # ================= # MONKEY PATCH SAGE diff --git a/augmented_valuation.py b/augmented_valuation.py index 47eff910e45..491e6132203 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -527,7 +527,6 @@ def reduce(self, f): if self(f) < 0: assert self(f) < 0 - print self(f) raise ValueError("f must have non-negative valuation") elif self(f) > 0: return self.residue_ring().zero() @@ -541,10 +540,12 @@ def reduce(self, f): raise NotImplementedError return self.residue_ring()(self.coefficients(f).next())(self.residue_field_generator()) - # if this is an infinite valuation, then we can simply drop all but the - # constant term if self._mu is infinity: - return self.residue_ring()(self._base_valuation.reduce(self.coefficients(f).next())(self.constant_valuation().residue_field().gen())) + # if this is an infinite valuation, then we can simply drop all but the + # constant term + constant_term = self.coefficients(f).next() + constant_term_reduced = self._base_valuation.reduce(constant_term) + return constant_term_reduced(self.residue_field().gen()) CV = zip(self.coefficients(f), self.valuations(f)) # rewrite as sum of f_i phi^{i tau}, i.e., drop most coefficients @@ -965,3 +966,15 @@ def is_gauss_valuation(self): def _make_monic_integral(self, G): return self._base_valuation._make_monic_integral(G) + + def _gt_(self, other): + from gauss_valuation import GaussValuation_generic + if isinstance(other, GaussValuation_generic): + return self._base_valuation >= other + if isinstance(other, AugmentedValuation): + if self(other._phi) >= other._mu: + return self >= other._base_valuation + else: + return False + + raise NotImplementedError("Operator not implemented for these valuations.") diff --git a/function_field_valuation.py b/function_field_valuation.py index 4cc70ae9ad8..0b2bea3a027 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -2,6 +2,39 @@ r""" Discrete valuations on function fields +EXAMPLES: + +We can create classical valuations that correspond to finite and infinite +places on a rational function field:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, 1); v + (x - 1)-adic valuation + sage: v = FunctionFieldValuation(K, x^2 + 1); v + (x^2 + 1)-adic valuation + sage: v = FunctionFieldValuation(K, 1/x); v + Valuation at the infinite place + +Note that we can also specify valuations which do not correspond to a place of +the function field:: + + sage: R. = QQ[] + sage: w = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = FunctionFieldValuation(K, w); v + 2-adic valuation + +Valuations on a rational function field can then be extended to finite +extensions:: + + sage: v = FunctionFieldValuation(K, x - 1); v + (x - 1)-adic valuation + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: v.extensions(L) + [[ Gauss valuation induced by (x - 1)-adic valuation, v(y - 1) = 1 , … ], + [ Gauss valuation induced by (x - 1)-adic valuation, v(y + 1) = 1 , … ]] + AUTHORS: - Julian Rueth (2016-10-16): initial version @@ -27,7 +60,7 @@ from valuation import DiscretePseudoValuation from trivial_valuation import TrivialValuation -from limit_valuation import LimitValuationFiniteExtension +from limit_valuation import FiniteExtensionFromLimitValuation class FunctionFieldValuationFactory(UniqueFactory): r""" @@ -125,7 +158,7 @@ class FunctionFieldValuationFactory(UniqueFactory): A place that has a unique extension can just be defined downstairs:: sage: v = FunctionFieldValuation(L, x); v - (x - 1)-adic valuation + [ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2 , … ] """ def create_key(self, domain, prime): @@ -153,24 +186,30 @@ def create_key(self, domain, prime): if domain not in FunctionFields(): raise ValueError("Domain must be a function field.") + if prime in DiscretePseudoValuationSpace(domain): + # prime is already a valuation of the requested domain + # if we returned (domain, prime), we would break caching + # because this element has been created from a different key + # Instead, we return the key that was used to create prime + # so the caller gets back a correctly cached version of prime + if not hasattr(prime, "_factory_data"): + raise NotImplementedError("Valuations on function fields must be unique and come out of the FunctionFieldValuatino factory but %r has been created by other means"%(prime,)) + return prime._factory_data[2] + if prime in domain: # prime defines a place return self.create_key_from_place(domain, prime) - elif prime in DiscretePseudoValuationSpace(domain._ring): + if prime in DiscretePseudoValuationSpace(domain._ring): # prime is a discrete (pseudo-)valuation on the polynomial ring # that the domain is constructed from return self.create_key_from_valuation(domain, prime) - elif prime in DiscretePseudoValuationSpace(domain.base_field()): - # prime is a discrete (pseudo-)valuation on the domain or a subfield - # that has a unique extension - return self.create_key_from_valuation(domain, prime) - elif domain.base_field() is not domain: - # prime might define a valuation on a subfield of domain that has a - # unique extension + if domain.base_field() is not domain: + # prime might define a valuation on a subring of domain and have a + # unique extension to domain base_valuation = FunctionFieldValuation(domain.base_field(), prime) - return create_key(domain, base_valuation) + return self.create_key_from_valuation(domain, base_valuation) - raise NotImplementedError("argument must be a place or a pseudo-valuation on an underlying polynomial ring") + raise NotImplementedError("argument must be a place or a pseudo-valuation on a supported subring but %r does not satisfy this for the domain %r"%(prime, domain)) def create_key_from_place(self, domain, generator): r""" @@ -213,7 +252,7 @@ def create_key_from_place(self, domain, generator): # generator is 1/x, the infinite place return domain, ~domain.gen() else: - raise ValueError("a place must be given by an irreducible polynomial or the inverse of the generator") + raise ValueError("a place must be given by an irreducible polynomial or the inverse of the generator; %r does not define a place over %r"%(generator, domain)) def create_key_from_valuation(self, domain, valuation): r""" @@ -229,17 +268,46 @@ def create_key_from_valuation(self, domain, valuation): sage: v = FunctionFieldValuation(K, w) # indirect doctest """ - if valuation.domain() is domain: - return domain, valuation + # this should have been handled by create_key already + assert valuation.domain() is not domain + if valuation.domain() is domain._ring: if domain.base_field() is not domain: - # on an extension of the form K[x]/(G), a valuation on K[x] must - # send exactly (G) to infinity - G = domain.polynomial() - if valuation(G) != infinity: - raise ValueError("valuation must be a pseudo-valuation which sends the defining polynomial to infinity but %r sends %r to %r"%(valuation, G, valuation(G))) if valuation.constant_valuation().domain() is not domain.base_field(): raise ValueError("valuation must extend a valuation on the base field but %r extends %r whose domain is not %r"%(valuation, valuation.constant_valuation(), domain.base_field())) + G = domain.polynomial() + # valuation is an approximant that may need to be + # improved further on demand + + # We now check thet valuation is an approximant for a valuation + # on domain that extends its restriction to the base field. + if valuation(G) != infinity: + G_integral = valuation._make_monic_integral(G) + v = valuation + while not v.is_gauss_valuation(): + if v(G_integral) <= v._base_valuation(G_integral): + raise ValueError("The valuation %r is not an approximant for a valuation on %r since the valuation of %r does not increase in every step"%(valuation, domain, G_integral)) + v = v._base_valuation + + # For uniqueness of valuations (which provides better caching + # and easier pickling) we need to find a normal form of + # valuation, i.e., the smallest approximant that describes this + # valuation + approximants = valuation.constant_valuation().mac_lane_approximants(G) + + greater_approximants = [w for w in approximants if w <= valuation] + if len(greater_approximants) > 1: + raise ValueError("valuation %r does not uniquely describe an extension of %r to %r"%(valuation, valuation.constant_valuation(), domain)) + if len(greater_approximants) == 1: + return domain, greater_approximants[0] + + smaller_approximants = [w for w in approximants if w >= valuation] + assert len(smaller_approximants) <= 1 + if len(smaller_approximants) == 0: + raise ValueError("valuation %r does not describe an extension of %r to %r"%(valuation, valuation.constant_valuation(), domain)) + assert len(smaller_approximants) == 1 + return domain, smaller_approximants[0] + else: # on a rational function field K(x), any valuation on K[x] that # is not only a pseudo-valuation extends to a valuation on K(x) @@ -270,13 +338,25 @@ def create_object(self, version, key, **extra_args): domain, valuation = key from sage.rings.valuation.valuation_space import DiscreteValuationSpace parent = DiscreteValuationSpace(domain) + if valuation in domain: + # only the infinite place is handled with an element instead of a base valuation + # any other element should have been replace with a valuation by _create_key_from_place assert(valuation == ~domain.gen()) return parent.__make_element_class__(InfiniteRationalFunctionFieldValuation)(parent) + if domain is valuation.domain(): - return valuation + # we can not just return valuation in this case + # as this would break uniqueness and pickling + raise ValueError("valuation must not be a valuation on domain yet but %r is a valuation on %r"%(valuation, domain)) + if domain.base_field() is domain: + # valuation is a base valuation on K[x] that induces a valuation on K(x) return parent.__make_element_class__(FiniteRationalFunctionFieldValuation)(parent, valuation) + else: + # valuation is a limit valuation that singles out an extension + return parent.__make_element_class__(FunctionFieldFromLimitValuation)(parent, valuation, domain.polynomial()) + raise NotImplementedError("valuation on %r from %r on %r"%(domain, valuation, valuation.domain())) FunctionFieldValuation = FunctionFieldValuationFactory("FunctionFieldValuation") @@ -302,15 +382,12 @@ def extensions(self, L): K = self.domain() from sage.categories.function_fields import FunctionFields if L is self.domain(): - return self + return [self] if L in FunctionFields(): if K.is_subring(K): if L.base() is K: # L is a simple extension of the domain of this valuation - W = self.mac_lane_approximants(L.polynomial()) - from valuation_space import DiscreteValuationSpace - parent = DiscreteValuationSpace(L) - return [parent.__make_element_class__(FunctionFieldLimitValuation)(parent, w, L.polynomial()) for w in W] + return [FunctionFieldValuation(L, w) for w in self.mac_lane_approximants(L.polynomial())] elif L.base() is not L and K.is_subring(L): # recursively call this method for the tower of fields from operator import add @@ -798,46 +875,6 @@ def residue_ring(self): """ return self._base_valuation.residue_field() -class FunctionFieldLimitValuation(LimitValuationFiniteExtension): - def _to_approximation_domain(self, f): +class FunctionFieldFromLimitValuation(FiniteExtensionFromLimitValuation): + def _to_base_domain(self, f): return f.element() - -class FunctionFieldPolymodValuation(InducedFunctionFieldValuation_base): - def __init__(self, domain, base_valuation): - from sage.rings.all import infinity - if base_valuation.domain() is not domain._ring: - raise ValueError - if base_valuation(domain.polynomial()) is not infinity: - raise ValueError - - self._base_valuation = base_valuation - DiscretePseudoValuation.__init__(self, domain) - - def _call_(self, x): - return self._base_valuation(x.element()) - - def __hash__(self): - return hash(self._base_valuation) + hash(self.domain()) - - def _cache_key(self): - return self._base_valuation, self.domain() - - def __cmp__(self, other): - if type(self) != type(other): - return cmp(type(self),type(other)) - return cmp(self._base_valuation,other._base_valuation) - - def value_group(self): - return self._base_valuation.value_group() - - def reduce(self, f): - return self.residue_field()(self._base_valuation._base_valuation.reduce(f.element())) - - def lift(self, F): - if F.parent() is not self.residue_field(): - raise ValueError - - return self.domain()(self._base_valuation._base_valuation.lift(F.element())) - - def _repr_(self): - return "Valuation on rational function field induced by %s"%self._base_valuation diff --git a/gauss_valuation.py b/gauss_valuation.py index 26c475d220e..2c9b42f0457 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -602,22 +602,6 @@ def _augmentations(self): def is_trivial(self): return self._base_valuation.is_trivial() - def _gt_(self, other): - if isinstance(other, GaussValuation_generic): - return self._base_valuation >= other.base_valuation - from augmented_valuation import AugmentedValuation - if isinstance(other, AugmentedValuation): - return False - raise NotImplementedError("Operator not implemented for these valuations.") - - def _lt_(self, other): - if isinstance(other, GaussValuation_generic): - return self._base_valuation <= other._base_valuation - from augmented_valuation import AugmentedValuation - if isinstance(other, AugmentedValuation): - return self <= other._base_valuation - raise NotImplementedError("Operator not implemented for these valuations.") - def _make_monic_integral(self, G): if not G.is_monic(): # this might fail if the base ring is not a field @@ -630,3 +614,10 @@ def _make_monic_integral(self, G): assert G.is_monic() return G + def _gt_(self, other): + if isinstance(other, GaussValuation_generic): + return self._base_valuation >= other._base_valuation + from augmented_valuation import AugmentedValuation + if isinstance(other, AugmentedValuation): + return False + raise NotImplementedError("Operator not implemented for these valuations.") diff --git a/limit_valuation.py b/limit_valuation.py index a0994da2607..e9de946e554 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -6,9 +6,19 @@ extension. This is not the case anymore for fields which are not complete with respect to their discrete valuation. In this case, the extensions essentially correspond to the factors of the defining polynomial of the extension over the -completion. However, these factors only exist over the completion and so the -valuation can not written down finitely as a number of -:class:`AugmentedValuation`s but only as a limit thereof. +completion. However, these factors only exist over the completion and this +makes it difficult to write down such valuations with a representation of +finite length. + +More specifically, let `v` be a discrete valuation on `K` and let `L=K[x]/(G)` +a finite extension thereof. An extension of `v` to `L` can be represented as a +discrete pseudo-valuation `w'` on `K[x]` which sends `G` to infinity. +However, such `w'` might not be described by an :class:`AugmentedValuation` +over a :class:`GaussValuation` anymore. Instead, we may need to write is as a +limit of augmented valuations. + +The classes in this module provide the means of writing down such limits and +resulting valuations on quotients. EXAMPLES: @@ -49,8 +59,8 @@ sage: v = FunctionFieldValuation(K, 1/x) sage: w = v.extension(L); w [ Gauss valuation induced by Valuation at the infinite place, v(y) = 1/2 , … ] - sage: w._improve_approximation() - sage: w._approximation + sage: w._base_valuation._improve_approximation() + sage: w._base_valuation._approximation [ Gauss valuation induced by Valuation at the infinite place, v(y) = 1/2, v(y^2 - 1/x) = +Infinity ] AUTHORS: @@ -74,9 +84,9 @@ sys.path.append(os.path.dirname(os.getcwd())) from sage.misc.abstract_method import abstract_method -from valuation import DiscretePseudoValuation +from valuation import DiscretePseudoValuation, InfiniteDiscretePseudoValuation, DiscreteValuation -class LimitValuation_base(DiscretePseudoValuation): +class LimitValuation(DiscretePseudoValuation): r""" Base class for limit valuations. @@ -92,32 +102,38 @@ class LimitValuation_base(DiscretePseudoValuation): sage: v = FunctionFieldValuation(K, 0) sage: w = v.extension(L) + sage: w._base_valuation + [ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2 , … ] The currently used approximation can be found in the ``_approximation`` field:: - sage: w._approximation + sage: w._base_valuation._approximation [ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2 ] - Note that the approximation might be defined on a different domain:: - - sage: w.domain() - Function field in y defined by y^2 - x - sage: w._approximation.domain() - Univariate Polynomial Ring in y over Rational function field in x over Rational Field - - The methods :meth:`_to_approximation_domain` and - :meth:`_from_approximation_domain` move items back and forth between the - domains of definition. - TESTS:: - sage: isinstance(w, LimitValuation_base) + sage: isinstance(w._base_valuation, LimitValuation) True - - sage: TestSuite(w).run() + sage: TestSuite(w._base_valuation).run() """ + def __init__(self, parent, approximation): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: K. = QQ.extension(x^2 + 1) + sage: v = pAdicValuation(K, 2) + sage: isinstance(v._base_valuation, LimitValuation) + True + + """ + DiscretePseudoValuation.__init__(self, parent) + self._initial_approximation = approximation # kept for consistent printing and equality tests + self._approximation = approximation + def reduce(self, f): r""" Return the reduction of ``f`` as an element of :meth:`residue_ring`. @@ -127,24 +143,22 @@ def reduce(self, f): sage: from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - x) + sage: L. = K.extension(y^2 - (x - 1)) - sage: v = FunctionFieldValuation(K, 1) - sage: w = v.extensions(L)[0] - sage: w.reduce(y) - 1 + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L) + sage: w.reduce(y) # indirect doctest + u1 """ f = self.domain().coerce(f) self._improve_approximation_for_reduce(f) - F = self._approximation.reduce(self._to_approximation_domain(f)) + F = self._approximation.reduce(f) return self.residue_ring()(F) - @abstract_method - def _improve_approximation_for_reduce(self, f): + def _call_(self, f): r""" - Replace our approximation with a sufficiently precise approximation to - correctly compute the reduction of ``f``. + Return the valuation of ``f``. EXAMPLES:: @@ -153,56 +167,59 @@ def _improve_approximation_for_reduce(self, f): sage: R. = K[] sage: L. = K.extension(y^2 - x) - For the unique extension over the place at zero, the initial - approximation is sufficient to compute the reduction of ``y``:: - sage: v = FunctionFieldValuation(K, 0) sage: w = v.extension(L) - sage: w._approximation - [ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2 ] - sage: w.reduce(y) - 0 - sage: w._approximation - [ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2 ] - - However, at a places over 1, the initial approximation is not - sufficient for some values:: - - sage: v = FunctionFieldValuation(K, 1) - sage: w = v.extensions(L)[0] - sage: w._approximation - [ Gauss valuation induced by (x - 1)-adic valuation, v(y - 1) = 1 ] - sage: w.reduce((y - 1) / (x - 1)) # indirect doctest + sage: w(y) # indirect doctest 1/2 - sage: w._approximation - [ Gauss valuation induced by (x - 1)-adic valuation, v(y - 1/2*x - 1/2) = 2 ] - sage: w.reduce((y - 1/2*x - 1/2) / (x - 1)^2) # indirect doctest - -1/8 - sage: w._approximation - [ Gauss valuation induced by (x - 1)-adic valuation, v(y + 1/8*x^2 - 3/4*x - 3/8) = 3 ] """ + self._improve_approximation_for_call(f) + return self._approximation(f) - def _call_(self, f): + @abstract_method + def _improve_approximation_for_reduce(self, f): r""" - Return the valuation of ``f``. + Replace our approximation with a sufficiently precise approximation to + correctly compute the reduction of ``f``. EXAMPLES:: sage: from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - x) + sage: L. = K.extension(y^2 - (x - 1337)) - sage: v = FunctionFieldValuation(K, 0) + For the unique extension over the place at 1337, the initial + approximation is sufficient to compute the reduction of ``y``:: + + sage: v = FunctionFieldValuation(K, 1337) sage: w = v.extension(L) - sage: w(y) - 1/2 + sage: u = w._base_valuation + sage: u._approximation + [ Gauss valuation induced by (x - 1337)-adic valuation, v(y) = 1/2 ] + sage: w.reduce(y) + 0 + sage: u._approximation + [ Gauss valuation induced by (x - 1337)-adic valuation, v(y) = 1/2 ] + + However, at a place over 1341, the initial approximation is not sufficient + for some values (note that 1341-1337 is a square):: + + sage: v = FunctionFieldValuation(K, 1341) + sage: w = v.extensions(L)[0] + sage: u = w._base_valuation + sage: u._approximation + [ Gauss valuation induced by (x - 1341)-adic valuation, v(y - 2) = 1 ] + sage: w.reduce((y - 2) / (x - 1341)) # indirect doctest + 1/4 + sage: u._approximation + [ Gauss valuation induced by (x - 1341)-adic valuation, v(y - 1/4*x + 1333/4) = 2 ] + sage: w.reduce((y - 1/4*x + 1333/4) / (x - 1341)^2) # indirect doctest + -1/64 + sage: u._approximation + [ Gauss valuation induced by (x - 1341)-adic valuation, v(y + 1/64*x^2 - 1349/32*x + 1819609/64) = 3 ] """ - f = self.domain().coerce(f) - self._improve_approximation_for_call(f) - return self._approximation(self._to_approximation_domain(f)) @abstract_method def _improve_approximation_for_call(self, f): @@ -215,19 +232,20 @@ def _improve_approximation_for_call(self, f): sage: from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - x) + sage: L. = K.extension(y^2 - (x - 23)) - For the unique extension over the place at zero, the initial + For the unique extension over the place at 23, the initial approximation is sufficient to compute all valuations:: - sage: v = FunctionFieldValuation(K, 0) + sage: v = FunctionFieldValuation(K, 23) sage: w = v.extension(L) - sage: w._approximation - [ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2 ] - sage: w(x) + sage: u = w._base_valuation + sage: u._approximation + [ Gauss valuation induced by (x - 23)-adic valuation, v(y) = 1/2 ] + sage: w(x - 23) 1 - sage: w._approximation - [ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2 ] + sage: u._approximation + [ Gauss valuation induced by (x - 23)-adic valuation, v(y) = 1/2 ] However, due to performance reasons, sometimes we improve the approximation though it would not have been necessary (performing the @@ -236,48 +254,72 @@ def _improve_approximation_for_call(self, f): sage: w(y) # indirect doctest 1/2 - sage: w._approximation - [ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2, v(y^2 - x) = +Infinity ] + sage: u._approximation + [ Gauss valuation induced by (x - 23)-adic valuation, v(y) = 1/2, v(y^2 - x + 23) = +Infinity ] """ - def _to_approximation_domain(self, f): + def _repr_(self): r""" - Return ``f`` as an element in the domain of ``_approximation``. + Return a printable representation of this valuation. EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - - sage: v = FunctionFieldValuation(K, 0) - sage: w = v.extensions(L)[0] - sage: w._to_approximation_domain(y).parent() - Univariate Polynomial Ring in y over Rational function field in x over Rational Field + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: w = v.extension(L) + sage: w._base_valuation # indirect doctest + [ Gauss valuation induced by 2-adic valuation, v(t + 1) = 1/2 , … ] """ - return self._approximation.domain().coerce(f) + from sage.rings.all import infinity + from augmented_valuation import AugmentedValuation + if self._initial_approximation(self._G) < infinity: + if isinstance(self._initial_approximation, AugmentedValuation): + return repr(self._initial_approximation)[:-1] + ", … ]" + return repr(self._initial_approximation) - def _from_approximation_domain(self, f): - r""" - Return ``f`` as an element in the domain of this valuation. - EXAMPLES:: +class MacLaneLimitValuation(LimitValuation, InfiniteDiscretePseudoValuation): + r""" + A limit valuation that is a pseudo-valuation on polynomial ring `K[x]` + which sends an irreducible polynomial `G` to infinity. - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) + This uses the MacLane algorithm to compute the next element in the limit. - sage: v = FunctionFieldValuation(K, 0) - sage: w = v.extensions(L)[0] - sage: w._from_approximation_domain(w._approximation.domain().gen()).parent() - Function field in y defined by y^2 - x + It starts from a first valuation ``approximation`` whose uniformizer must + be a uniformizer of the limit and whose residue field must contain the + residue field of the limit. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: K. = QQ.extension(x^2 + 1) + + sage: v = pAdicValuation(K, 2) + sage: u = v._base_valuation; u + [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/2 , … ] + """ + def __init__(self, parent, approximation, G): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: K. = QQ.extension(x^2 + 1) + sage: v = pAdicValuation(K, 2) + sage: u = v._base_valuation + sage: isinstance(u, MacLaneLimitValuation) + True + """ - return self.domain().coerce(f) + LimitValuation.__init__(self, parent, approximation) + self._G = G def lift(self, F): r""" @@ -294,15 +336,14 @@ def lift(self, F): sage: v = FunctionFieldValuation(K, 1) sage: w = v.extensions(L)[0]; w [ Gauss valuation induced by (x - 1)-adic valuation, v(y^2 - 2) = 1 , … ] - sage: u = w.reduce(y); u + sage: s = w.reduce(y); s u1 - sage: y == w.lift(u) - True + sage: w.lift(s) # indirect doctest + y """ F = self.residue_ring().coerce(F) - f = self._approximation.lift(F) - return self._from_approximation_domain(f) + return self._approximation.lift(F) def uniformizer(self): r""" @@ -317,70 +358,11 @@ def uniformizer(self): sage: v = FunctionFieldValuation(K, 0) sage: w = v.extension(L) - sage: w(w.uniformizer()) - 1/2 - - """ - return self._from_approximation_domain(self._approximation.uniformizer()) - -class LimitValuationFiniteExtension(LimitValuation_base): - r""" - A valuation on a quotient of the form `L=K[x]/(G)` with an irreducible `G`. - - Internally, this valuation is represented as a limit of valuations on - `K[x]` which sends `G` to infinity. Starting from an ``approximation``, - i.e., one element of the sequence that the limit taken over, that - approximation is used to perform computations and it is improved using the - Mac Lane algorithm whenever a better approximation is needed. - - INPUT: - - - ``parent`` -- the containing valuation space (usually the space of - discrete valuations on `L`) - - - ``approximation`` -- a Gauss valuation or an augmentation thereof; a - discrete valuation on `K[x]` which already has the residue field, and - ramification index of the limit and which approximates exactly one - valuation on `L`. - - - ``G`` -- an irreducible polynomial in `K[x]` - - TESTS:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - - sage: v = FunctionFieldValuation(K, 0) - sage: w = v.extension(L) - - sage: TestSuite(w).run() - - """ - def __init__(self, parent, approximation, G): - r""" - TESTS:: - - sage: from mac_lane import * # optional: standalone - sage: K = QQ - sage: R. = K[] - sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 5) - sage: w = v.extensions(L); w - [[ Gauss valuation induced by 5-adic valuation, v(t + 2) = 1 , … ], - [ Gauss valuation induced by 5-adic valuation, v(t + 3) = 1 , … ]] - - sage: isinstance(w[0], LimitValuation_base) - True - sage: isinstance(w[1], LimitValuation_base) - True + sage: w.uniformizer() # indirect doctest + y """ - DiscretePseudoValuation.__init__(self, parent) - self._initial_approximation = approximation # kept for consistent printing - self._approximation = approximation - self._G = G + return self._approximation.uniformizer() def _improve_approximation(self): r""" @@ -394,13 +376,18 @@ def _improve_approximation(self): sage: L. = K.extension(t^2 + 1) sage: v = pAdicValuation(QQ, 2) sage: w = v.extension(L) - sage: w._approximation + sage: u = w._base_valuation + sage: u._approximation [ Gauss valuation induced by 2-adic valuation, v(t + 1) = 1/2 ] - sage: w._improve_approximation() - sage: w._approximation + sage: u._improve_approximation() + sage: u._approximation [ Gauss valuation induced by 2-adic valuation, v(t + 1) = 1/2, v(t^2 + 1) = +Infinity ] - sage: w._improve_approximation() - sage: w._approximation + + This method has no effect, if the approximation is already an infinite + valuation:: + + sage: u._improve_approximation() + sage: u._approximation [ Gauss valuation induced by 2-adic valuation, v(t + 1) = 1/2, v(t^2 + 1) = +Infinity ] """ @@ -432,11 +419,12 @@ def _improve_approximation_for_call(self, f): sage: L. = K.extension(t^2 + 1) sage: v = pAdicValuation(QQ, 5) sage: w = v.extensions(L)[0] - sage: w._approximation + sage: u = w._base_valuation + sage: u._approximation [ Gauss valuation induced by 5-adic valuation, v(t + 2) = 1 ] sage: w(t + 2) # indirect doctest 1 - sage: w._approximation + sage: u._approximation [ Gauss valuation induced by 5-adic valuation, v(t + 7) = 2 ] ALGORITHM: @@ -470,8 +458,7 @@ def _improve_approximation_for_call(self, f): # zero coefficients.) return - g = self._to_approximation_domain(f) - if self._approximation.is_equivalence_unit(g): + if self._approximation.is_equivalence_unit(f): # see ALGORITHM above return @@ -491,46 +478,24 @@ def _improve_approximation_for_reduce(self, f): sage: L. = K.extension(t^2 + 1) sage: v = pAdicValuation(QQ, 5) sage: w = v.extensions(L)[0] - sage: w._approximation + sage: u = w._base_valuation + sage: u._approximation [ Gauss valuation induced by 5-adic valuation, v(t + 2) = 1 ] sage: w.reduce((t + 2) / 5) # indirect doctest 4 - sage: w._approximation + sage: u._approximation [ Gauss valuation induced by 5-adic valuation, v(t + 7) = 2 ] ALGORITHM: - The reduction is correct for an equivalence-unit, see - :meth:`_improve_approximation_for_call`. + The reduction produced by the approximation is correct for an + equivalence-unit, see :meth:`_improve_approximation_for_call`. """ - g = self._to_approximation_domain(f) - if self._approximation(g) > 0: + if self._approximation(f) > 0: return self._improve_approximation_for_call(f) - def _repr_(self): - r""" - Return a printable representation of this valuation. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K = QQ - sage: R. = K[] - sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) - sage: v.extension(L) # indirect doctest - [ Gauss valuation induced by 2-adic valuation, v(t + 1) = 1/2 , … ] - - """ - from sage.rings.all import infinity - from augmented_valuation import AugmentedValuation - if self._initial_approximation(self._G) < infinity: - if isinstance(self._initial_approximation, AugmentedValuation): - return repr(self._initial_approximation)[:-1] + ", … ]" - return repr(self._initial_approximation) - def _eq_(self, other): r""" Return whether this valuation is indistinguishable from ``other``. @@ -549,14 +514,14 @@ def _eq_(self, other): sage: ww = v.extension(L) sage: w == ww # indirect doctest True - sage: w._improve_approximation() + sage: w._base_valuation._improve_approximation() sage: w == ww True - sage: w._approximation == ww._approximation + sage: w._base_valuation._approximation == ww._base_valuation._approximation False """ - return isinstance(other, LimitValuationFiniteExtension) and self._G == other._G and self._initial_approximation == other._initial_approximation + return isinstance(other, MacLaneLimitValuation) and self._G == other._G and self._initial_approximation == other._initial_approximation def residue_ring(self): r""" @@ -573,12 +538,6 @@ def residue_ring(self): sage: w.residue_ring() Finite Field of size 2 - TESTS:: - - sage: w._improve_approximation() - sage: w.residue_ring() - Finite Field of size 2 - """ R = self._approximation.residue_ring() from sage.categories.fields import Fields @@ -589,3 +548,267 @@ def residue_ring(self): from sage.rings.polynomial.polynomial_ring import is_PolynomialRing assert(is_PolynomialRing(R)) return R.base_ring() + +class FiniteExtensionFromInfiniteValuation(DiscreteValuation): + r""" + A valuation on a quotient of the form `L=K[x]/(G)` with an irreducible `G` + which is internally backed by a pseudo-valuations on `K[x]` which sends `G` + to infinity. + + INPUT: + + - ``parent`` -- the containing valuation space (usually the space of + discrete valuations on `L`) + + - ``base_valuation`` -- an infinite valuation on `K[x]` which takes `G` to + infinity. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L); w + [ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2 , … ] + + TESTS:: + + sage: TestSuite(w).run() + + """ + def __init__(self, parent, base_valuation): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x^2 + 1) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L); w + [ Gauss valuation induced by (x)-adic valuation, v(y^2 - x^2 + 1) = +Infinity ] + sage: isinstance(w, FiniteExtensionFromInfiniteValuation) + True + + """ + DiscretePseudoValuation.__init__(self, parent) + self._base_valuation = base_valuation + + def _eq_(self, other): + r""" + Return whether this valuation is indistinguishable from ``other``. + + EXAMPLES: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: w = v.extension(L) + sage: ww = v.extension(L) + sage: w == ww # indirect doctest + True + + """ + return isinstance(other, FiniteExtensionFromInfiniteValuation) and self._base_valuation == other._base_valuation + + def _repr_(self): + r""" + Return a printable representation of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: v.extension(L) # indirect doctest + [ Gauss valuation induced by 2-adic valuation, v(t + 1) = 1/2 , … ] + + """ + return repr(self._base_valuation) + + def residue_ring(self): + r""" + Return the residue field of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: v.extension(L).residue_ring() + Finite Field of size 2 + + """ + return self._base_valuation.residue_ring() + + def uniformizer(self): + r""" + Return a uniformizing element of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: v.extension(L).uniformizer() + t + 1 + + """ + return self._from_base_domain(self._base_valuation.uniformizer()) + + def _to_base_domain(self, f): + r""" + Return ``f`` as an element in the domain of ``_base_valuation``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extensions(L)[0] + sage: w._to_base_domain(y).parent() + Univariate Polynomial Ring in y over Rational function field in x over Rational Field + + """ + return self._base_valuation.domain().coerce(f) + + def _from_base_domain(self, f): + r""" + Return ``f`` as an element in the domain of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L) + sage: w._from_base_domain(w._base_valuation.domain().gen()).parent() + Function field in y defined by y^2 - x + + """ + return self.domain().coerce(f) + + def _call_(self, f): + r""" + Evaluate this valuation at ``f``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L) + sage: w(y) # indirect doctest + 1/2 + + """ + return self._base_valuation(self._to_base_domain(f)) + + def reduce(self, f): + r""" + Return the reduction of ``f`` in the :meth:`residue_field` of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - (x - 2)) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L) + sage: w.reduce(y) + u1 + + """ + return self._base_valuation.reduce(self._to_base_domain(f)) + + def lift(self, F): + r""" + Lift ``F`` from the :meth;`residue_field` of this valuation into its + domain. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 2) + sage: w = v.extension(L) + sage: w.lift(w.residue_field().gen()) + y + + """ + F = self.residue_ring().coerce(F) + F = self._base_valuation.residue_ring().coerce(F) + f = self._base_valuation.lift(F) + return self._from_base_domain(f) + +class FiniteExtensionFromLimitValuation(FiniteExtensionFromInfiniteValuation): + r""" + An extension of a valuation on a finite field extensions `L=K[x]/(G)` which + is induced by an infinite limit valuation on `K[x]`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: v = FunctionFieldValuation(K, 1) + sage: w = v.extensions(L); w + [[ Gauss valuation induced by (x - 1)-adic valuation, v(y - 1) = 1 , … ], + [ Gauss valuation induced by (x - 1)-adic valuation, v(y + 1) = 1 , … ]] + + TESTS:: + + sage: TestSuite(w[0]).run() + sage: TestSuite(w[1]).run() + + """ + def __init__(self, parent, approximation, G): + r""" + EXAMPLES: + + Note that this implementation is also used when the underlying limit is + only taken over a finite sequence of valuations:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: v = FunctionFieldValuation(K, 2) + sage: w = v.extension(L); w + [ Gauss valuation induced by (x - 2)-adic valuation, v(y^2 - x) = +Infinity ] + sage: isinstance(w, FiniteExtensionFromLimitValuation) + True + + """ + from limit_valuation import MacLaneLimitValuation + from valuation_space import DiscretePseudoValuationSpace + limit_parent = DiscretePseudoValuationSpace(approximation.domain()) + limit = limit_parent.__make_element_class__(MacLaneLimitValuation)(limit_parent, approximation, G) + FiniteExtensionFromInfiniteValuation.__init__(self, parent, limit) diff --git a/padic_valuation.py b/padic_valuation.py index 175bc6973c9..0700ffe71ac 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -25,7 +25,7 @@ sys.path.append(os.path.dirname(os.getcwd())) from valuation import DiscretePseudoValuation -from limit_valuation import LimitValuationFiniteExtension +from limit_valuation import FiniteExtensionFromLimitValuation from sage.structure.factory import UniqueFactory from sage.misc.cachefunc import cached_method from sage.misc.fast_methods import WithEqualityById @@ -280,14 +280,11 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime) assert(len(below_v) <= 1) if len(below_v) == 1: ret = below_v[0] - if v(G) >= ret(G): - # equality of number fields has it quirks since it says that two - # fields are == even if they are distinguishable (because they come - # from different constructions.) - # Including structure() into the key seems to be a way to distinguish such cases properly. - return (R, ret, L.construction()), {'approximants': candidates} - else: - raise ValueError("%s does not approximate a valuation on K[x]/(G) with G=%s"%(prime, G)) + # equality of number fields has it quirks since it says that two + # fields are == even if they are distinguishable (because they come + # from different constructions.) + # Including construction() into the key seems to be a way to distinguish such cases properly. + return (R, ret, L.construction()), {'approximants': candidates} # Even if it does not extend an approximant, v could be an # approximation of a single approximant @@ -349,7 +346,9 @@ def create_object(self, version, key, **extra_args): v = key[1] _ = key[2] # ignored approximants = extra_args['approximants'] - return parent.__make_element_class__(pAdicValuation_number_field)(R, v, approximants) + parent = DiscreteValuationSpace(R) + return parent.__make_element_class__(pAdicFromLimitValuation)(parent, v, R.relative_polynomial()) + # return parent.__make_element_class__(pAdicValuation_number_field)(R, v, approximants) pAdicValuation = PadicValuationFactory("pAdicValuation") @@ -920,9 +919,10 @@ def extensions(self, ring): if is_NumberField(ring): if ring.base() is self.domain(): from valuation_space import DiscreteValuationSpace - from limit_valuation import LimitValuationFiniteExtension + from limit_valuation import FiniteExtensionFromInfiniteValuation parent = DiscreteValuationSpace(ring) - return [parent.__make_element_class__(pAdicLimitValuation)(parent, approximant, ring.relative_polynomial()) for approximant in self.mac_lane_approximants(ring.relative_polynomial())] + approximants = self.mac_lane_approximants(ring.relative_polynomial()) + return [parent.__make_element_class__(pAdicFromLimitValuation)(parent, approximant, ring.relative_polynomial()) for approximant in approximants] if ring.base() is not ring and self.domain().is_subring(ring.base()): return sum([w.extensions(ring) for w in self.extensions(ring.base())], []) raise NotImplementedError("extensions of %r from %r to %r not implemented"%(self, self.domain(), ring)) @@ -1076,7 +1076,7 @@ class pAdicValuation_number_field(pAdicValuation_base): def __init__(self, R, valuation, approximants): p = valuation.residue_field().characteristic() assert(p>0) - pAdicValuation_base.__init__(self, R, valuation.uniformizer()(R.fraction_field().gen())) + pAdicValuation_base.__init__(self, R, p) assert(valuation in approximants) self._valuation = valuation self._approximants = approximants @@ -1160,6 +1160,8 @@ def _repr_(self): return "[ %s ]-adic valuation"%(", ".join("v(%r) = %r" if (isinstance(v, AugmentedValuation) and v.domain() == self._valuation.domain()) else repr(v) for v in unique_approximant)) return "%s-adic valuation"%(self._valuation) -class pAdicLimitValuation(LimitValuationFiniteExtension): - def _to_approximation_domain(self, f): - return f.polynomial(self.domain().variable_name()) +class pAdicFromLimitValuation(FiniteExtensionFromLimitValuation): + def _to_base_domain(self, f): + return f.polynomial()(self._base_valuation.domain().gen()) + def _from_base_domain(self, f): + return f(self.domain().gen()) diff --git a/trivial_valuation.py b/trivial_valuation.py index 47ec7a03c02..b7408535205 100644 --- a/trivial_valuation.py +++ b/trivial_valuation.py @@ -55,7 +55,7 @@ sys.path.append(os.getcwd()) sys.path.append(os.path.dirname(os.getcwd())) -from valuation import DiscretePseudoValuation +from valuation import DiscretePseudoValuation, DiscreteValuation, InfiniteDiscretePseudoValuation from valuation_space import DiscreteValuationSpace, DiscretePseudoValuationSpace from sage.structure.factory import UniqueFactory @@ -154,7 +154,7 @@ def is_trivial(self): """ return True -class TrivialDiscretePseudoValuation(TrivialDiscretePseudoValuation_base): +class TrivialDiscretePseudoValuation(TrivialDiscretePseudoValuation_base, InfiniteDiscretePseudoValuation): r""" The trivial pseudo-valuation that is `\infty` everywhere. @@ -181,22 +181,6 @@ def __init__(self, parent): """ TrivialDiscretePseudoValuation_base.__init__(self, parent) - def is_discrete_valuation(self): - r""" - Return whether this is a discrete valuation. - - EXAMPLES: - - Returns ``False`` since this is only a pseudo-valuation:: - - sage: from mac_lane import * # optional: standalone - sage: v = TrivialPseudoValuation(QQ) - sage: v.is_discrete_valuation() - False - - """ - return False - def _call_(self, x): r""" Evaluate this valuation at ``x``. @@ -288,7 +272,7 @@ def lift(self, X): self.residue_ring().coerce(X) # ignore the output return self.domain().zero() -class TrivialDiscreteValuation(TrivialDiscretePseudoValuation_base): +class TrivialDiscreteValuation(TrivialDiscretePseudoValuation_base, DiscreteValuation): r""" The trivial valuation that is zero on non-zero elements. diff --git a/valuation.py b/valuation.py index c5628982c49..9399fa4016f 100644 --- a/valuation.py +++ b/valuation.py @@ -191,8 +191,7 @@ def _lt_(self, other): When overriding this method, you can assume that ``other`` is a (pseudo-)valuation on the same domain. """ - if self == other: return True - raise NotImplementedError("Operator not implemented for this valuation.") + return other >= self def _gt_(self, other): r""" @@ -210,3 +209,18 @@ def _gt_(self, other): # it is not the generic reduce of object) and that does not match equality # by id. __reduce__ = object.__reduce__ + +class InfiniteDiscretePseudoValuation(DiscretePseudoValuation): + r""" + sage: TODO + """ + def is_discrete_valuation(self): + r""" + sage: TODO + """ + return False + +class DiscreteValuation(DiscretePseudoValuation): + r""" + sage: TODO + """ diff --git a/valuation_space.py b/valuation_space.py index 277291e55dd..4cb6f038014 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -654,7 +654,8 @@ def _test_shift(self, **options): for x, n in tester.some_elements(cartesian_product([S,V])): v = self(x) from sage.categories.fields import Fields - if n < 0 and -n > v and not self.domain() in Fields(): + if n < 0 and self.domain() not in Fields(): + # note that shifting might not be possible in this case even if -n > v continue y = self.shift(x, n) tester.assertIs(y.parent(), self.domain()) @@ -713,7 +714,7 @@ def _test_reduce(self, **options): if x.is_unit() and ~x in self.domain(): tester.assertTrue(y.is_unit()) tester.assertIn(~y, self.residue_ring()) - tester.assertEqual(~y, self.reduce(~x)) + tester.assertEqual(~y, self.reduce(self.domain()(~x))) if self(x) > 0: tester.assertEqual(self.reduce(x), 0) From 38da4316be2e629982e478a296fc2291fa77c470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 30 Oct 2016 15:51:18 -0500 Subject: [PATCH 045/740] Move some factory code to mac_lane_approximant so it can used by both the p-adic and the function field factory --- function_field_valuation.py | 70 ++++--------------------------------- valuation.py | 68 +++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 64 deletions(-) diff --git a/function_field_valuation.py b/function_field_valuation.py index 0b2bea3a027..47977338be9 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -58,7 +58,7 @@ from sage.structure.factory import UniqueFactory from sage.rings.all import QQ, ZZ, infinity -from valuation import DiscretePseudoValuation +from valuation import DiscreteValuation from trivial_valuation import TrivialValuation from limit_valuation import FiniteExtensionFromLimitValuation @@ -275,45 +275,19 @@ def create_key_from_valuation(self, domain, valuation): if domain.base_field() is not domain: if valuation.constant_valuation().domain() is not domain.base_field(): raise ValueError("valuation must extend a valuation on the base field but %r extends %r whose domain is not %r"%(valuation, valuation.constant_valuation(), domain.base_field())) - G = domain.polynomial() - # valuation is an approximant that may need to be - # improved further on demand - - # We now check thet valuation is an approximant for a valuation - # on domain that extends its restriction to the base field. - if valuation(G) != infinity: - G_integral = valuation._make_monic_integral(G) - v = valuation - while not v.is_gauss_valuation(): - if v(G_integral) <= v._base_valuation(G_integral): - raise ValueError("The valuation %r is not an approximant for a valuation on %r since the valuation of %r does not increase in every step"%(valuation, domain, G_integral)) - v = v._base_valuation - + # Valuation is an approximant that describes a single valuation + # on domain. # For uniqueness of valuations (which provides better caching # and easier pickling) we need to find a normal form of # valuation, i.e., the smallest approximant that describes this # valuation - approximants = valuation.constant_valuation().mac_lane_approximants(G) - - greater_approximants = [w for w in approximants if w <= valuation] - if len(greater_approximants) > 1: - raise ValueError("valuation %r does not uniquely describe an extension of %r to %r"%(valuation, valuation.constant_valuation(), domain)) - if len(greater_approximants) == 1: - return domain, greater_approximants[0] - - smaller_approximants = [w for w in approximants if w >= valuation] - assert len(smaller_approximants) <= 1 - if len(smaller_approximants) == 0: - raise ValueError("valuation %r does not describe an extension of %r to %r"%(valuation, valuation.constant_valuation(), domain)) - assert len(smaller_approximants) == 1 - return domain, smaller_approximants[0] - + return domain, valuation.constant_valuation().mac_lane_approximant(domain.polynomial(), valuation) else: # on a rational function field K(x), any valuation on K[x] that # is not only a pseudo-valuation extends to a valuation on K(x) if not valuation.is_discrete_valuation(): raise ValueError("valuation must be a discrete valuation but %r is not."%(valuation,)) - return domain, valuation + return domain, valuation if valuation.domain().is_subring(domain.base_field()): # valuation is defined on a subring of this function field, try to lift it @@ -361,7 +335,7 @@ def create_object(self, version, key, **extra_args): FunctionFieldValuation = FunctionFieldValuationFactory("FunctionFieldValuation") -class FunctionFieldValuation_base(DiscretePseudoValuation): +class FunctionFieldValuation_base(DiscreteValuation): r""" Base class for valuations on function fields. @@ -396,38 +370,6 @@ def extensions(self, L): return self._extensions_from_constant_field(L) raise NotImplementedError("extension of %r from %r to %r not implemented"%(self, K, L)) - def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): - # TODO: move this to discrete valuation - R = G.parent() - if R.base_ring() is not self.domain(): - raise ValueError("G must be defined over the domain of this valuation") - if not assume_squarefree and not G.is_squarefree(): - raise ValueError("G must be squarefree") - - from sage.rings.all import infinity - from gauss_valuation import GaussValuation - - leaves = [ GaussValuation(R, self)] - while True: - ef = [ v.E()*v.F() for v in leaves] - if sum(ef) == G.degree(): - if precision_cap is None or all([v(v.phi())>precision_cap for v in leaves]): - return leaves - - expandables = [] - new_leaves = [] - for v in leaves: - if v(G) is infinity: - new_leaves.append(v) - else: - expandables.append(v) - leaves = new_leaves - - if not expandables: - return leaves - - for v in expandables: - leaves.extend(v.mac_lane_step(G)) class RationalFunctionFieldValuation_base(FunctionFieldValuation_base): diff --git a/valuation.py b/valuation.py index 9399fa4016f..44ad6a61a05 100644 --- a/valuation.py +++ b/valuation.py @@ -224,3 +224,71 @@ class DiscreteValuation(DiscretePseudoValuation): r""" sage: TODO """ + def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): + r""" + sage: TODO + """ + R = G.parent() + if R.base_ring() is not self.domain(): + raise ValueError("G must be defined over the domain of this valuation") + if not assume_squarefree and not G.is_squarefree(): + raise ValueError("G must be squarefree") + + from sage.rings.all import infinity + from gauss_valuation import GaussValuation + + leaves = [ GaussValuation(R, self)] + while True: + ef = [ v.E()*v.F() for v in leaves] + if sum(ef) == G.degree(): + if precision_cap is None or all([v(v.phi())>precision_cap for v in leaves]): + return leaves + + expandables = [] + new_leaves = [] + for v in leaves: + if v(G) is infinity: + new_leaves.append(v) + else: + expandables.append(v) + leaves = new_leaves + + if not expandables: + return leaves + + for v in expandables: + leaves.extend(v.mac_lane_step(G)) + + def mac_lane_approximant(self, G, valuation): + r""" + sage: TODO + """ + if valuation.constant_valuation() != self: + raise ValueError + + # Check thet valuation is an approximant for a valuation + # on domain that extends its restriction to the base field. + from sage.rings.all import infinity + if valuation(G) != infinity: + G_integral = valuation._make_monic_integral(G) + v = valuation + while not v.is_gauss_valuation(): + if v(G_integral) <= v._base_valuation(G_integral): + raise ValueError("The valuation %r is not an approximant for a valuation on %r since the valuation of %r does not increase in every step"%(valuation, domain, G_integral)) + v = v._base_valuation + + approximants = self.mac_lane_approximants(G) + + greater_approximants = [w for w in approximants if w <= valuation] + if len(greater_approximants) > 1: + raise ValueError("valuation %r does not uniquely describe an extension of %r to %r"%(valuation, self, domain)) + if len(greater_approximants) == 1: + return greater_approximants[0] + + smaller_approximants = [w for w in approximants if w >= valuation] + assert len(smaller_approximants) <= 1 + if len(smaller_approximants) == 0: + raise ValueError("valuation %r does not describe an extension of %r to %r"%(valuation, valuation.constant_valuation(), domain)) + assert len(smaller_approximants) == 1 + return smaller_approximants[0] + From e703a8ca2a0939cc971e705430028755de118300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 31 Oct 2016 00:46:30 -0500 Subject: [PATCH 046/740] Improve function field valuations and fixed is_subring implementations --- __init__.py | 35 ++++++- augmented_valuation.py | 26 ++++- function_field_valuation.py | 185 +++++++++++++++++++----------------- gauss_valuation.py | 12 ++- trivial_valuation.py | 10 +- valuation.py | 44 +++++++++ 6 files changed, 213 insertions(+), 99 deletions(-) diff --git a/__init__.py b/__init__.py index 82f4e047ece..5666ce67599 100644 --- a/__init__.py +++ b/__init__.py @@ -77,15 +77,46 @@ def _coerce_map_from_(target, source): return source.hom([target.gen()], base_morphism=base_coercion) sage.rings.function_field.function_field.FunctionField._coerce_map_from_ = _coerce_map_from_ - del(_coerce_map_from_) +# patch is_injective() for many morphisms +def patch_is_injective(method, patch_map): + def patched(*args, **kwargs): + ret = method(*args, **kwargs) + if type(ret) in patch_map: + ret = patch_map[type(ret)](ret) + return ret + return patched + +# a ring homomorphism from a field into a ring is injective (as it respects inverses) +class RingHomomorphism_coercion_patched(sage.rings.morphism.RingHomomorphism_coercion): + def is_injective(self): + from sage.categories.fields import Fields + if self.domain() in Fields(): return True + coercion = self.codomain().coerce_map_from(self.domain()) + if coercion is not None: + return coercion.is_injective() + raise NotImplementedError +sage.rings.homset.RingHomset_generic.natural_map = patch_is_injective(sage.rings.homset.RingHomset_generic.natural_map, {sage.rings.morphism.RingHomomorphism_coercion: (lambda coercion: RingHomomorphism_coercion_patched(coercion.parent()))}) + +# a morphism of polynomial rings which is induced by a ring morphism on the base is injective if the morphis on the base is +class PolynomialRingHomomorphism_from_base_patched(sage.rings.polynomial.polynomial_ring_homomorphism.PolynomialRingHomomorphism_from_base): + def is_injective(self): + return self.underlying_map().is_injective() +sage.rings.polynomial.polynomial_ring.PolynomialRing_general._coerce_map_from_ = patch_is_injective(sage.rings.polynomial.polynomial_ring.PolynomialRing_general._coerce_map_from_, {sage.rings.polynomial.polynomial_ring_homomorphism.PolynomialRingHomomorphism_from_base: (lambda morphism: PolynomialRingHomomorphism_from_base_patched(morphism.parent(), morphism.underlying_map()))}) + +# morphisms of number fields are injective +class Q_to_quadratic_field_element_patched(sage.rings.number_field.number_field_element_quadratic.Q_to_quadratic_field_element): + def is_injective(self): return True +class Z_to_quadratic_field_element_patched(sage.rings.number_field.number_field_element_quadratic.Z_to_quadratic_field_element): + def is_injective(self): return True +sage.rings.number_field.number_field.NumberField_quadratic._coerce_map_from_ = patch_is_injective(sage.rings.number_field.number_field.NumberField_quadratic._coerce_map_from_, {sage.rings.number_field.number_field_element_quadratic.Q_to_quadratic_field_element: (lambda morphism: Q_to_quadratic_field_element_patched(morphism.codomain())), sage.rings.number_field.number_field_element_quadratic.Z_to_quadratic_field_element: (lambda morphism: Z_to_quadratic_field_element_patched(morphism.codomain()))}) -import imp, sys # register modules at some standard places so imports work as exepcted r""" sage: from sage.rings.valuation.gauss_valuation import GaussValuation """ +import imp, sys sage.rings.valuation = sys.modules['sage.rings.valuation'] = imp.new_module('sage.rings.valuation') sage.rings.valuation.gauss_valuation = sys.modules['sage.rings.valuation.gauss_valuation'] = gauss_valuation sage.rings.valuation.valuation = sys.modules['sage.rings.valuation.valuation'] = valuation diff --git a/augmented_valuation.py b/augmented_valuation.py index 491e6132203..abda5332379 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -954,8 +954,26 @@ def F(self): raise ValueError("there is no residual degree over a trivial valuation") return self.psi().degree() * self._base_valuation.F() - def extension(self, ring): - return AugmentedValuation(self._base_valuation.extension(ring), self.phi().change_ring(ring.base_ring()), self._mu) + def extensions(self, ring): + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + if not is_PolynomialRing(ring) and len(ring.gens()) != 1: + raise NotImplementedError("Can not compute extensions to a ring that is not a univariate polynomial ring such as %r"%ring) + + base_valuations = self._base_valuation.extensions(ring) + phi = self.phi().change_ring(ring.base_ring()) + + ret = [] + for v in base_valuations: + F = v.equivalence_decomposition(phi) + mu0 = v(phi) + for f,e in F: + # We construct a valuation with [v, w(phi) = mu] which should be such that + # self(phi) = self._mu, i.e., w(phi) = w(unit) + sum e_i * w(f_i) where + # the sum runs over all the factors in the equivalence decomposition of phi + # Solving for mu gives + mu = (self._mu - v(F.unit()) - sum([ee*v(ff) for ee,ff in F if ff != f])) / e + ret.append(AugmentedValuation(v, f, mu)) + return ret def uniformizer(self): return self.element_with_valuation(self.value_group()._generator) @@ -978,3 +996,7 @@ def _gt_(self, other): return False raise NotImplementedError("Operator not implemented for these valuations.") + + def is_discrete_valuation(self): + from sage.rings.all import infinity + return self._mu != infinity diff --git a/function_field_valuation.py b/function_field_valuation.py index 47977338be9..3095a46ed21 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -57,6 +57,7 @@ from sage.structure.factory import UniqueFactory from sage.rings.all import QQ, ZZ, infinity +from sage.misc.abstract_method import abstract_method from valuation import DiscreteValuation from trivial_valuation import TrivialValuation @@ -349,16 +350,27 @@ class FunctionFieldValuation_base(DiscreteValuation): sage: TestSuite(v).run() """ - def _extensions_from_constant_field(self, L): - raise NotImplementedError - def extensions(self, L): + r""" + Return the extensions of this valuation to ``L``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, x) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: v.extensions(L) + [[ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2 , … ]] + + """ K = self.domain() from sage.categories.function_fields import FunctionFields if L is self.domain(): return [self] if L in FunctionFields(): - if K.is_subring(K): + if K.is_subring(L): if L.base() is K: # L is a simple extension of the domain of this valuation return [FunctionFieldValuation(L, w) for w in self.mac_lane_approximants(L.polynomial())] @@ -367,11 +379,11 @@ def extensions(self, L): from operator import add return reduce(add, A, []) elif L.constant_field() is not K.constant_field() and K.constant_field().is_subring(L): - return self._extensions_from_constant_field(L) + # subclasses should override this method and handle this case, so we never get here + raise NotImplementedError("Can not compute the extensions of %r from %r to %r since the base ring changes."%(self, self.domain(), ring)) raise NotImplementedError("extension of %r from %r to %r not implemented"%(self, K, L)) - class RationalFunctionFieldValuation_base(FunctionFieldValuation_base): r""" Base class for discrete valuations on rational function fields. @@ -387,6 +399,7 @@ class RationalFunctionFieldValuation_base(FunctionFieldValuation_base): """ + class ClassicalFunctionFieldValuation_base(FunctionFieldValuation_base): r""" Base class for discrete valuations on rational function fields that come @@ -417,11 +430,7 @@ def _test_classical_residue_field(self, **options): """ tester = self._tester(**options) - try: - tester.assertTrue(self.domain().constant_field().is_subring(self.residue_field())) - except NotImplementedError: - # is_injective is often not implemented by morphisms - pass + tester.assertTrue(self.domain().constant_field().is_subring(self.residue_field())) class InducedFunctionFieldValuation_base(FunctionFieldValuation_base): r""" @@ -537,83 +546,18 @@ def reduce(self, f): assert not ret.is_zero() return self.residue_field()(ret) - def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): - """ - Some difficult cases from Mark van Hoeij:: + def _repr_(self): + r""" + Return a printable representation of this valuation. - sage: from mac_lane import * # optional: standalone - sage: k = GF(2) - sage: K. = FunctionField(k) - sage: R. = K[] - sage: F = y^21 + x*y^20 + (x^3 + x + 1)*y^18 + (x^3 + 1)*y^17 + (x^4 + x)*y^16 + (x^7 + x^6 + x^3 + x + 1)*y^15 + x^7*y^14 + (x^8 + x^7 + x^6 + x^4 + x^3 + 1)*y^13 + (x^9 + x^8 + x^4 + 1)*y^12 + (x^11 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2)*y^11 + (x^12 + x^9 + x^8 + x^7 + x^5 + x^3 + x + 1)*y^10 + (x^14 + x^13 + x^10 + x^9 + x^8 + x^7 + x^6 + x^3 + x^2 + 1)*y^9 + (x^13 + x^9 + x^8 + x^6 + x^4 + x^3 + x)*y^8 + (x^16 + x^15 + x^13 + x^12 + x^11 + x^7 + x^3 + x)*y^7 + (x^17 + x^16 + x^13 + x^9 + x^8 + x)*y^6 + (x^17 + x^16 + x^12 + x^7 + x^5 + x^2 + x + 1)*y^5 + (x^19 + x^16 + x^15 + x^12 + x^6 + x^5 + x^3 + 1)*y^4 + (x^18 + x^15 + x^12 + x^10 + x^9 + x^7 + x^4 + x)*y^3 + (x^22 + x^21 + x^20 + x^18 + x^13 + x^12 + x^9 + x^8 + x^7 + x^5 + x^4 + x^3)*y^2 + (x^23 + x^22 + x^20 + x^17 + x^15 + x^14 + x^12 + x^9)*y + x^25 + x^23 + x^19 + x^17 + x^15 + x^13 + x^11 + x^5 - sage: x = K._ring.gen() - sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).augmentation(x,1)) - sage: v0.mac_lane_approximants(F, assume_squarefree=True) # optional: integrated; assumes squarefree for speed - [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x + 1) = 3/2 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y) = 4/3, v(y^3 + x^4) = 13/3 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x) = 2 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y^15 + y^13 + (x + 1)*y^12 + x*y^11 + (x + 1)*y^10 + y^9 + y^8 + x*y^6 + x*y^5 + y^4 + y^3 + y^2 + (x + 1)*y + x + 1) = 2 ]] - sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).augmentation(x+1,1)) - sage: v0.mac_lane_approximants(F, assume_squarefree=True) # optional: integrated; assumes squarefree for speed - [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y) = 7/2, v(y^2 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1) = 15/2 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y + x^2 + 1) = 7/2 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y) = 3/4, v(y^4 + x^3 + x^2 + x + 1) = 15/4 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y^13 + x*y^12 + y^10 + (x + 1)*y^9 + (x + 1)*y^8 + x*y^7 + x*y^6 + (x + 1)*y^4 + y^3 + (x + 1)*y^2 + 1) = 2 ]] - sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).augmentation(x^3+x^2+1,1)) - sage: v0.mac_lane_approximants(F, assume_squarefree=True) # optional: integrated; assumes squarefree for speed - [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y + x^3 + x^2 + x) = 2, v(y^2 + (x^6 + x^4 + 1)*y + x^14 + x^10 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2 + x) = 5 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^2 + (x^7 + x^5 + x^4 + x^3 + x^2 + x)*y + x^7 + x^5 + x + 1) = 3 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^3 + (x^8 + x^5 + x^4 + x^3 + x + 1)*y^2 + (x^7 + x^6 + x^5)*y + x^8 + x^5 + x^4 + x^3 + 1) = 3 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^3 + (x^8 + x^4 + x^3 + x + 1)*y^2 + (x^4 + x^3 + 1)*y + x^8 + x^7 + x^4 + x + 1) = 3 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^4 + (x^8 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1)*y^3 + (x^8 + x^5 + x^4 + x^3 + x^2 + x + 1)*y^2 + (x^8 + x^7 + x^6 + x^5 + x^3 + x^2 + 1)*y + x^8 + x^7 + x^6 + x^5 + x^3 + 1) = 3 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^7 + (x^8 + x^5 + x^4 + x)*y^6 + (x^7 + 1)*y^5 + (x^4 + x^2)*y^4 + (x^8 + x^3 + x + 1)*y^3 + (x^7 + x^6 + x^4 + x^2 + x + 1)*y^2 + (x^8 + x^7 + x^5 + x^3 + 1)*y + x^7 + x^6 + x^5 + x^4 + x^3 + x^2) = 3 ]] - - Some cases with trivial residue field extensions:: + EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) - sage: S. = K[] - sage: F = y^2 - x^2 - x^3 - 3 - sage: v0 = GaussValuation(K._ring,pAdicValuation(QQ,3)) - sage: v1 = v0.augmentation(K._ring.gen(),1/3) - sage: mu0 = FunctionFieldValuation(K, v1) - sage: mu0.mac_lane_approximants(F) - [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + x) = 2/3 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + 2*x) = 2/3 ]] + sage: FunctionFieldValuation(K, x^2 + 1) # indirect doctest + (x^2 + 1)-adic valuation """ - # TODO: move this to discrete valuation - R = G.parent() - if R.base_ring() is not self.domain(): - raise ValueError("G must be defined over the domain of this valuation") - if not assume_squarefree and not G.is_squarefree(): - raise ValueError("G must be squarefree") - - from sage.rings.all import infinity - from gauss_valuation import GaussValuation - - leaves = [ GaussValuation(R, self)] - while True: - ef = [ v.E()*v.F() for v in leaves] - if sum(ef) == G.degree(): - if precision_cap is None or all([v(v.phi())>precision_cap for v in leaves]): - return leaves - - expandables = [] - new_leaves = [] - for v in leaves: - if v(G) is infinity: - new_leaves.append(v) - else: - expandables.append(v) - leaves = new_leaves - - if not expandables: - return leaves - - for v in expandables: - leaves.extend(v.mac_lane_step(G)) - - def _repr_(self): from sage.rings.valuation.augmented_valuation import AugmentedValuation from sage.rings.valuation.gauss_valuation import GaussValuation if isinstance(self._base_valuation, AugmentedValuation): @@ -624,10 +568,41 @@ def _repr_(self): return repr(self._base_valuation.constant_valuation()) return "Valuation on rational function field induced by %s"%self._base_valuation - def _extensions_from_constant_field(self, L): - # extend the underlying valuation on the polynomial ring - W = self._base_valuation.extensions(L._ring) - return [FunctionFieldValuation(L, w) for w in W] + def extensions(self, L): + r""" + Return all extensions of this valuation to ``L`` which has a larger + constant field than the :meth:`domain` of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, x^2 + 1) + sage: L. = FunctionField(GaussianIntegers().fraction_field()) + sage: v.extensions(L) # indirect doctest + [(x - I)-adic valuation, (x + I)-adic valuation] + + """ + K = self.domain() + if L is K: + return [self] + + from sage.categories.function_fields import FunctionFields + if L in FunctionFields() \ + and K.is_subring(L) \ + and L.base() is L \ + and L.constant_field() is not K.constant_field() \ + and K.constant_field().is_subring(L.constant_field()): + # The above condition checks whether L is an extension of K that + # comes from an extension of the field of constants + # Condition "L.base() is L" is important so we do not call this + # code for extensions from K(x) to K(x)(y) + + # We extend the underlying valuation on the polynomial ring + W = self._base_valuation.extensions(L._ring) + return [FunctionFieldValuation(L, w) for w in W] + + return super(InducedFunctionFieldValuation_base, self).extensions(L) class InfiniteRationalFunctionFieldValuation(RationalFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base): r""" @@ -818,5 +793,41 @@ def residue_ring(self): return self._base_valuation.residue_field() class FunctionFieldFromLimitValuation(FiniteExtensionFromLimitValuation): + r""" + A valuation on a finit extensions of function fields `L=K/(G)` where `K` is + another function field. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - (x^2 + x + 1)) + sage: v = FunctionFieldValuation(K, x - 1) # indirect doctest + sage: w = v.extension(L); w + [ Gauss valuation induced by (x - 1)-adic valuation, v(y^2 - x^2 - x - 1) = +Infinity ] + + TESTS:: + + sage: isinstance(w, FunctionFieldFromLimitValuation) + True + sage: TestSuite(w).run() + + """ def _to_base_domain(self, f): + r""" + Return ``f`` as an element of the domain of the underlying limit valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - (x^2 + x + 1)) + sage: v = FunctionFieldValuation(K, x - 1) # indirect doctest + sage: w = v.extension(L) + sage: w._to_base_domain(y).parent() + Univariate Polynomial Ring in y over Rational function field in x over Rational Field + + """ return f.element() diff --git a/gauss_valuation.py b/gauss_valuation.py index 2c9b42f0457..a0b1aa5ce01 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -571,10 +571,13 @@ def change_ring(self, base_ring): base_valuation = self._base_valuation.change_ring(base_ring) return GaussValuation(self.domain().change_ring(base_ring), base_valuation) - def extension(self, ring): + def extensions(self, ring): from sage.rings.polynomial.polynomial_ring import is_PolynomialRing - if is_PolynomialRing(ring) and len(ring.gens()) == 1: - return GaussValuation(ring, self._base_valuation.extension(ring.base())) + if not is_PolynomialRing(ring) and len(ring.gens()) != 1: + raise NotImplementedError("Can not compute extensions of %r to a ring that is not a univariate polynomial ring such as %r"%(self, ring)) + if not self.domain().is_subring(ring): + raise ValueError("Extension must be to a larger ring but %r is not a subring of %r"%(self.domain(), ring)) + return [GaussValuation(ring, w) for w in self._base_valuation.extensions(ring.base())] def is_gauss_valuation(self): r""" @@ -621,3 +624,6 @@ def _gt_(self, other): if isinstance(other, AugmentedValuation): return False raise NotImplementedError("Operator not implemented for these valuations.") + + def is_discrete_valuation(self): + return True diff --git a/trivial_valuation.py b/trivial_valuation.py index b7408535205..1c09cf4eebe 100644 --- a/trivial_valuation.py +++ b/trivial_valuation.py @@ -386,7 +386,7 @@ def lift(self, X): """ return self.residue_ring().coerce(X) - def extension(self, ring): + def extensions(self, ring): r""" Return the unique extension of this valuation to ``ring``. @@ -394,13 +394,13 @@ def extension(self, ring): sage: from mac_lane import * # optional: standalone sage: v = TrivialValuation(ZZ) - sage: v.extension(QQ) + sage: v.extensions(QQ) Trivial valuation on Rational Field """ - if ring.has_coerce_map_from(self.domain()): - return TrivialValuation(ring) - return super(DiscretePseudoValuation, self).extension(ring) + if self.domain().is_subring(ring): + return [TrivialValuation(ring)] + return super(DiscretePseudoValuation, self).extensions(ring) TrivialValuation = TrivialValuationFactory(TrivialDiscreteValuation, DiscreteValuationSpace, "TrivialValuation") TrivialPseudoValuation = TrivialValuationFactory(TrivialDiscretePseudoValuation, DiscretePseudoValuationSpace, "TrivialPseudoValuation") diff --git a/valuation.py b/valuation.py index 44ad6a61a05..2348c75c012 100644 --- a/valuation.py +++ b/valuation.py @@ -227,6 +227,50 @@ class DiscreteValuation(DiscretePseudoValuation): def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): r""" sage: TODO + + TESTS: + + Some difficult cases provided by Mark van Hoeij:: + + sage: from mac_lane import * # optional: standalone + sage: k = GF(2) + sage: K. = FunctionField(k) + sage: R. = K[] + sage: F = y^21 + x*y^20 + (x^3 + x + 1)*y^18 + (x^3 + 1)*y^17 + (x^4 + x)*y^16 + (x^7 + x^6 + x^3 + x + 1)*y^15 + x^7*y^14 + (x^8 + x^7 + x^6 + x^4 + x^3 + 1)*y^13 + (x^9 + x^8 + x^4 + 1)*y^12 + (x^11 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2)*y^11 + (x^12 + x^9 + x^8 + x^7 + x^5 + x^3 + x + 1)*y^10 + (x^14 + x^13 + x^10 + x^9 + x^8 + x^7 + x^6 + x^3 + x^2 + 1)*y^9 + (x^13 + x^9 + x^8 + x^6 + x^4 + x^3 + x)*y^8 + (x^16 + x^15 + x^13 + x^12 + x^11 + x^7 + x^3 + x)*y^7 + (x^17 + x^16 + x^13 + x^9 + x^8 + x)*y^6 + (x^17 + x^16 + x^12 + x^7 + x^5 + x^2 + x + 1)*y^5 + (x^19 + x^16 + x^15 + x^12 + x^6 + x^5 + x^3 + 1)*y^4 + (x^18 + x^15 + x^12 + x^10 + x^9 + x^7 + x^4 + x)*y^3 + (x^22 + x^21 + x^20 + x^18 + x^13 + x^12 + x^9 + x^8 + x^7 + x^5 + x^4 + x^3)*y^2 + (x^23 + x^22 + x^20 + x^17 + x^15 + x^14 + x^12 + x^9)*y + x^25 + x^23 + x^19 + x^17 + x^15 + x^13 + x^11 + x^5 + sage: x = K._ring.gen() + sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).augmentation(x,1)) + sage: v0.mac_lane_approximants(F, assume_squarefree=True) # optional: integrated; assumes squarefree for speed + [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x + 1) = 3/2 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y) = 4/3, v(y^3 + x^4) = 13/3 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x) = 2 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y^15 + y^13 + (x + 1)*y^12 + x*y^11 + (x + 1)*y^10 + y^9 + y^8 + x*y^6 + x*y^5 + y^4 + y^3 + y^2 + (x + 1)*y + x + 1) = 2 ]] + sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).augmentation(x+1,1)) + sage: v0.mac_lane_approximants(F, assume_squarefree=True) # optional: integrated; assumes squarefree for speed + [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y) = 7/2, v(y^2 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1) = 15/2 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y + x^2 + 1) = 7/2 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y) = 3/4, v(y^4 + x^3 + x^2 + x + 1) = 15/4 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y^13 + x*y^12 + y^10 + (x + 1)*y^9 + (x + 1)*y^8 + x*y^7 + x*y^6 + (x + 1)*y^4 + y^3 + (x + 1)*y^2 + 1) = 2 ]] + sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).augmentation(x^3+x^2+1,1)) + sage: v0.mac_lane_approximants(F, assume_squarefree=True) # optional: integrated; assumes squarefree for speed + [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y + x^3 + x^2 + x) = 2, v(y^2 + (x^6 + x^4 + 1)*y + x^14 + x^10 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2 + x) = 5 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^2 + (x^7 + x^5 + x^4 + x^3 + x^2 + x)*y + x^7 + x^5 + x + 1) = 3 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^3 + (x^8 + x^5 + x^4 + x^3 + x + 1)*y^2 + (x^7 + x^6 + x^5)*y + x^8 + x^5 + x^4 + x^3 + 1) = 3 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^3 + (x^8 + x^4 + x^3 + x + 1)*y^2 + (x^4 + x^3 + 1)*y + x^8 + x^7 + x^4 + x + 1) = 3 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^4 + (x^8 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1)*y^3 + (x^8 + x^5 + x^4 + x^3 + x^2 + x + 1)*y^2 + (x^8 + x^7 + x^6 + x^5 + x^3 + x^2 + 1)*y + x^8 + x^7 + x^6 + x^5 + x^3 + 1) = 3 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^7 + (x^8 + x^5 + x^4 + x)*y^6 + (x^7 + 1)*y^5 + (x^4 + x^2)*y^4 + (x^8 + x^3 + x + 1)*y^3 + (x^7 + x^6 + x^4 + x^2 + x + 1)*y^2 + (x^8 + x^7 + x^5 + x^3 + 1)*y + x^7 + x^6 + x^5 + x^4 + x^3 + x^2) = 3 ]] + + Cases with trivial residue field extensions:: + + sage: K. = FunctionField(QQ) + sage: S. = K[] + sage: F = y^2 - x^2 - x^3 - 3 + sage: v0 = GaussValuation(K._ring,pAdicValuation(QQ,3)) + sage: v1 = v0.augmentation(K._ring.gen(),1/3) + sage: mu0 = FunctionFieldValuation(K, v1) + sage: mu0.mac_lane_approximants(F) + [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + x) = 2/3 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + 2*x) = 2/3 ]] + """ R = G.parent() if R.base_ring() is not self.domain(): From ee124d764357cd540748e58d2da7a6e22a55c699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 31 Oct 2016 17:54:37 -0500 Subject: [PATCH 047/740] Fix printing of valuations and space confusion --- __init__.py | 8 +- augmented_valuation.py | 96 +++++++--------- function_field_valuation.py | 54 ++++----- gauss_valuation.py | 8 +- limit_valuation.py | 129 ++++++++++++--------- padic_valuation.py | 65 ++++------- rings_with_valuation.py | 1 - trivial_valuation.py | 4 +- valuation.py | 50 +++++++- valuation_space.py | 222 ++++++++++-------------------------- 10 files changed, 280 insertions(+), 357 deletions(-) delete mode 120000 rings_with_valuation.py diff --git a/__init__.py b/__init__.py index 5666ce67599..cd3ea6d4709 100644 --- a/__init__.py +++ b/__init__.py @@ -1,4 +1,4 @@ -from .valuation_space import DiscretePseudoValuationSpace, DiscreteValuationSpace +from .valuation_space import DiscretePseudoValuationSpace from .trivial_valuation import TrivialValuation, TrivialPseudoValuation from .padic_valuation import pAdicValuation from .gauss_valuation import GaussValuation @@ -11,7 +11,9 @@ # different types) from .trivial_valuation import TrivialDiscreteValuation, TrivialDiscretePseudoValuation from .function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldFromLimitValuation -from .limit_valuation import LimitValuation, MacLaneLimitValuation, FiniteExtensionFromInfiniteValuation, FiniteExtensionFromLimitValuation +from .limit_valuation import LimitValuation, MacLaneLimitValuation, FiniteExtensionFromInfiniteValuation, FiniteExtensionFromLimitValuation, LimitValuation_generic +from .augmented_valuation import AugmentedValuation_generic +from .gauss_valuation import GaussValuation_generic # ================= # MONKEY PATCH SAGE @@ -131,3 +133,5 @@ def is_injective(self): return True register_factory_unpickle("TrivialValuation", TrivialValuation) register_factory_unpickle("TrivialPseudoValuation", TrivialPseudoValuation) register_factory_unpickle("FunctionFieldValuation", FunctionFieldValuation) +register_factory_unpickle("AugmentedValuation", AugmentedValuation) +register_factory_unpickle("LimitValuation", LimitValuation) diff --git a/augmented_valuation.py b/augmented_valuation.py index abda5332379..322628bbc19 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -26,12 +26,44 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) +import sys, os +if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: + sys.path.append(os.getcwd()) + sys.path.append(os.path.dirname(os.getcwd())) + from developing_valuation import DevelopingValuation, _lift_to_maximal_precision from sage.misc.cachefunc import cached_method from sage.rings.all import infinity +from sage.structure.factory import UniqueFactory + +class AugmentedValuationFactory(UniqueFactory): + def create_key(self, base_valuation, phi, mu, check=True): + if check: + is_key, reason = base_valuation.is_key(phi, explain=True) + if not is_key: + raise ValueError(reason) + if mu <= base_valuation(phi): + raise ValueError("the value of the key polynomial must strictly increase but `%s` does not exceed `%s`."%(phi, v(phi))) + + return base_valuation, phi, mu + + def create_object(self, version, key): + base_valuation, phi, mu = key + + if mu < infinity: + from valuation_space import DiscretePseudoValuationSpace + parent = DiscretePseudoValuationSpace(base_valuation.domain()) + else: + from valuation_space import DiscretePseudoValuationSpace + parent = DiscretePseudoValuationSpace(base_valuation.domain()) + + return parent.__make_element_class__(AugmentedValuation_generic)(parent, base_valuation, phi, mu) -class AugmentedValuation(DevelopingValuation): +AugmentedValuation = AugmentedValuationFactory("AugmentedValuation") + +class AugmentedValuation_generic(DevelopingValuation): """ An augmented valuation is a discrete valuation on a polynomial ring. It extends another discrete valuation `v` by setting the valuation of a @@ -58,7 +90,7 @@ class AugmentedValuation(DevelopingValuation): [ Gauss valuation induced by 2-adic valuation, v((1 + O(2^5))*x) = 1/2 ] """ - def __init__(self, v, phi, mu, check=True): + def __init__(self, parent, v, phi, mu): """ Initialization. @@ -76,32 +108,11 @@ def __init__(self, v, phi, mu, check=True): if phi.parent() is not v.domain(): raise ValueError("phi must be in the domain of v") - if check: - is_key, reason = v.is_key(phi, explain=True) - if not is_key: - raise ValueError(reason) - if mu <= v(phi): - raise ValueError("the value of the key polynomial must strictly increase but `%s` does not exceed `%s`."%(phi, v(phi))) - - if mu < infinity: - parent = v.parent() - else: - from valuation_space import DiscretePseudoValuationSpace - parent = DiscretePseudoValuationSpace(v.domain()) DevelopingValuation.__init__(self, parent, phi) self._base_valuation = v self._mu = mu - if check and not self.constant_valuation().residue_field().has_coerce_map_from(v.constant_valuation().residue_field()): - raise ValueError("the residue field `%s` does not embed into `%s`"%(v.residue_field(), self.residue_field())) - - def __hash__(self): - return hash(self._base_valuation, self._mu, self._phi) - - def _cache_key(self): - return self._base_valuation, self._mu, self._phi - def _call_(self, f): """ Evaluate this valuation at ``f``. @@ -289,12 +300,12 @@ def element_with_valuation(self, s): def _latex_(self): vals = [self] v = self - while isinstance(v, AugmentedValuation): + while isinstance(v, AugmentedValuation_generic): v = v._base_valuation vals.append(v) vals.reverse() from sage.misc.latex import latex - vals = [ "v_%s(%s) = %s"%(i,latex(v._phi), latex(v._mu)) if isinstance(v, AugmentedValuation) else latex(v) for i,v in enumerate(vals) ] + vals = [ "v_%s(%s) = %s"%(i,latex(v._phi), latex(v._mu)) if isinstance(v, AugmentedValuation_generic) else latex(v) for i,v in enumerate(vals) ] return "[ %s ]"%", ".join(vals) def _repr_(self): @@ -312,41 +323,12 @@ def _repr_(self): """ vals = self._augmentations() - vals = [ "v(%s) = %s"%(v._phi, v._mu) if isinstance(v, AugmentedValuation) else str(v) for v in vals ] + vals = [ "v(%s) = %s"%(v._phi, v._mu) if isinstance(v, AugmentedValuation_generic) else str(v) for v in vals ] return "[ %s ]"%", ".join(vals) def _augmentations(self): return self._base_valuation._augmentations() + [self] - def _richcmp_(self, other, op): - if op == 2: # == - if not isinstance(other, AugmentedValuation): - return False - if self.phi() != other.phi() or self._mu != other._mu or self._base_valuation != other._base_valuation: - return False - return True - if op == 3: # != - return not (self == other) - return DevelopingValuation._richcmp_(self, other, op) - - def __hash__(self): - r""" - The hash value of this valuation. - - EXAMPLES:: - - sage: from mac_lane import * - sage: v = pAdicValuation(QQ, 2) - sage: hash(v) == hash(v) - True - - """ - vals = self._augmentations() - return hash((vals[0]) + ((v.phi(), v._mu) for v in vals[1:])) - - def _cmp_(self, other): - raise NotImplementedError("No total ordering for this valuation.") - @cached_method def constant_valuation(self): """ @@ -865,7 +847,7 @@ def residue_field(self, generator=None): if generator is None: level = 0 v = self - while isinstance(v, AugmentedValuation): + while isinstance(v, AugmentedValuation_generic): level += 1 v = v._base_valuation generator = 'u%s'%level @@ -989,7 +971,7 @@ def _gt_(self, other): from gauss_valuation import GaussValuation_generic if isinstance(other, GaussValuation_generic): return self._base_valuation >= other - if isinstance(other, AugmentedValuation): + if isinstance(other, AugmentedValuation_generic): if self(other._phi) >= other._mu: return self >= other._base_valuation else: diff --git a/function_field_valuation.py b/function_field_valuation.py index 3095a46ed21..db33c58ffb4 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -32,8 +32,8 @@ sage: R. = K[] sage: L. = K.extension(y^2 - x) sage: v.extensions(L) - [[ Gauss valuation induced by (x - 1)-adic valuation, v(y - 1) = 1 , … ], - [ Gauss valuation induced by (x - 1)-adic valuation, v(y + 1) = 1 , … ]] + [[ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation, + [ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation] AUTHORS: @@ -151,18 +151,18 @@ class FunctionFieldValuationFactory(UniqueFactory): There are several ways to create valuations on extensions of rational function fields:: - sage: K. = FunctionField(QQ) - sage: R. = K[] + sage: K. = FunctionField(QQ) + sage: R. = K[] sage: L. = K.extension(y^2 - x); L Function field in y defined by y^2 - x A place that has a unique extension can just be defined downstairs:: sage: v = FunctionFieldValuation(L, x); v - [ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2 , … ] + (x)-adic valuation """ - def create_key(self, domain, prime): + def create_key_and_extra_args(self, domain, prime): r""" Create a unique key which identifies the valuation given by ``prime`` on ``domain``. @@ -195,24 +195,24 @@ def create_key(self, domain, prime): # so the caller gets back a correctly cached version of prime if not hasattr(prime, "_factory_data"): raise NotImplementedError("Valuations on function fields must be unique and come out of the FunctionFieldValuatino factory but %r has been created by other means"%(prime,)) - return prime._factory_data[2] + return prime._factory_data[2], {} if prime in domain: # prime defines a place - return self.create_key_from_place(domain, prime) + return self.create_key_and_extra_args_from_place(domain, prime) if prime in DiscretePseudoValuationSpace(domain._ring): # prime is a discrete (pseudo-)valuation on the polynomial ring # that the domain is constructed from - return self.create_key_from_valuation(domain, prime) + return self.create_key_and_extra_args_from_valuation(domain, prime) if domain.base_field() is not domain: # prime might define a valuation on a subring of domain and have a # unique extension to domain base_valuation = FunctionFieldValuation(domain.base_field(), prime) - return self.create_key_from_valuation(domain, base_valuation) + return self.create_key_and_extra_args_from_valuation(domain, base_valuation) raise NotImplementedError("argument must be a place or a pseudo-valuation on a supported subring but %r does not satisfy this for the domain %r"%(prime, domain)) - def create_key_from_place(self, domain, generator): + def create_key_and_extra_args_from_place(self, domain, generator): r""" Create a unique key which identifies the valuation at the place specified by ``generator``. @@ -230,12 +230,12 @@ def create_key_from_place(self, domain, generator): if domain.base_field() is not domain: # if this is an extension field, construct the unique place over # the place on the subfield - return self.create_key(domain, FunctionFieldValuation(domain.base_field(), generator)) + return self.create_key_and_extra_args(domain, FunctionFieldValuation(domain.base_field(), generator)) if generator in domain.constant_base_field(): # generator is a constant, we associate to it the place which # corresponds to the polynomial (x - generator) - return self.create_key(domain, domain.gen() - generator) + return self.create_key_and_extra_args(domain, domain.gen() - generator) if generator in domain._ring: # generator is a polynomial @@ -248,14 +248,14 @@ def create_key_from_place(self, domain, generator): # with v(generator) = 1 from sage.rings.valuation.gauss_valuation import GaussValuation valuation = GaussValuation(domain._ring, TrivialValuation(domain.constant_base_field())).augmentation(generator, 1) - return self.create_key(domain, valuation) + return self.create_key_and_extra_args(domain, valuation) elif generator == ~domain.gen(): # generator is 1/x, the infinite place - return domain, ~domain.gen() + return (domain, ~domain.gen()), {} else: raise ValueError("a place must be given by an irreducible polynomial or the inverse of the generator; %r does not define a place over %r"%(generator, domain)) - def create_key_from_valuation(self, domain, valuation): + def create_key_and_extra_args_from_valuation(self, domain, valuation): r""" Create a unique key which identifies the valuation which extends ``valuation``. @@ -282,17 +282,19 @@ def create_key_from_valuation(self, domain, valuation): # and easier pickling) we need to find a normal form of # valuation, i.e., the smallest approximant that describes this # valuation - return domain, valuation.constant_valuation().mac_lane_approximant(domain.polynomial(), valuation) + approximants = valuation.constant_valuation().mac_lane_approximants(domain.polynomial()) + approximant = valuation.constant_valuation().mac_lane_approximant(domain.polynomial(), valuation, approximants) + return (domain, approximant), {'approximants': approximants} else: # on a rational function field K(x), any valuation on K[x] that # is not only a pseudo-valuation extends to a valuation on K(x) if not valuation.is_discrete_valuation(): raise ValueError("valuation must be a discrete valuation but %r is not."%(valuation,)) - return domain, valuation + return (domain, valuation), {} if valuation.domain().is_subring(domain.base_field()): # valuation is defined on a subring of this function field, try to lift it - return self.create_key(domain, valuation.extension(domain)) + return self.create_key_and_extra_args(domain, valuation.extension(domain)) raise NotImplementedError("extension of valuation from %r to %r not implemented yet"%(valuation.domain(), domain)) @@ -311,8 +313,8 @@ def create_object(self, version, key, **extra_args): """ domain, valuation = key - from sage.rings.valuation.valuation_space import DiscreteValuationSpace - parent = DiscreteValuationSpace(domain) + from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace + parent = DiscretePseudoValuationSpace(domain) if valuation in domain: # only the infinite place is handled with an element instead of a base valuation @@ -330,7 +332,7 @@ def create_object(self, version, key, **extra_args): return parent.__make_element_class__(FiniteRationalFunctionFieldValuation)(parent, valuation) else: # valuation is a limit valuation that singles out an extension - return parent.__make_element_class__(FunctionFieldFromLimitValuation)(parent, valuation, domain.polynomial()) + return parent.__make_element_class__(FunctionFieldFromLimitValuation)(parent, valuation, domain.polynomial(), extra_args['approximants']) raise NotImplementedError("valuation on %r from %r on %r"%(domain, valuation, valuation.domain())) @@ -362,7 +364,7 @@ def extensions(self, L): sage: R. = K[] sage: L. = K.extension(y^2 - x) sage: v.extensions(L) - [[ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2 , … ]] + [(x)-adic valuation] """ K = self.domain() @@ -558,9 +560,9 @@ def _repr_(self): (x^2 + 1)-adic valuation """ - from sage.rings.valuation.augmented_valuation import AugmentedValuation + from sage.rings.valuation.augmented_valuation import AugmentedValuation_generic from sage.rings.valuation.gauss_valuation import GaussValuation - if isinstance(self._base_valuation, AugmentedValuation): + if isinstance(self._base_valuation, AugmentedValuation_generic): if self._base_valuation._base_valuation == GaussValuation(self.domain()._ring, TrivialValuation(self.domain().constant_field())): if self._base_valuation._mu == 1: return "(%r)-adic valuation"%(self._base_valuation.phi()) @@ -805,7 +807,7 @@ class FunctionFieldFromLimitValuation(FiniteExtensionFromLimitValuation): sage: L. = K.extension(y^2 - (x^2 + x + 1)) sage: v = FunctionFieldValuation(K, x - 1) # indirect doctest sage: w = v.extension(L); w - [ Gauss valuation induced by (x - 1)-adic valuation, v(y^2 - x^2 - x - 1) = +Infinity ] + (x - 1)-adic valuation TESTS:: diff --git a/gauss_valuation.py b/gauss_valuation.py index a0b1aa5ce01..91907337dbc 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -122,8 +122,8 @@ def create_object(self, version, key, **extra_args): """ domain, v = key - from sage.rings.valuation.valuation_space import DiscreteValuationSpace - parent = DiscreteValuationSpace(domain) + from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace + parent = DiscretePseudoValuationSpace(domain) return GaussValuation_generic(parent, v) GaussValuation = GaussValuationFactory("GaussValuation") @@ -620,8 +620,8 @@ def _make_monic_integral(self, G): def _gt_(self, other): if isinstance(other, GaussValuation_generic): return self._base_valuation >= other._base_valuation - from augmented_valuation import AugmentedValuation - if isinstance(other, AugmentedValuation): + from augmented_valuation import AugmentedValuation_generic + if isinstance(other, AugmentedValuation_generic): return False raise NotImplementedError("Operator not implemented for these valuations.") diff --git a/limit_valuation.py b/limit_valuation.py index e9de946e554..a6907c9c35b 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -33,8 +33,8 @@ sage: v = FunctionFieldValuation(K, 1) sage: w = v.extensions(L); w - [[ Gauss valuation induced by (x - 1)-adic valuation, v(y - 1) = 1 , … ], - [ Gauss valuation induced by (x - 1)-adic valuation, v(y + 1) = 1 , … ]] + [[ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation, + [ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation] The same phenomenon can be observed for valuations on number fields:: @@ -43,8 +43,8 @@ sage: L. = K.extension(t^2 + 1) sage: v = pAdicValuation(QQ, 5) sage: w = v.extensions(L); w - [[ Gauss valuation induced by 5-adic valuation, v(t + 2) = 1 , … ], - [ Gauss valuation induced by 5-adic valuation, v(t + 3) = 1 , … ]] + [[ 5-adic valuation, v(t + 2) = 1 ]-adic valuation, + [ 5-adic valuation, v(t + 3) = 1 ]-adic valuation] .. NOTE:: @@ -58,7 +58,7 @@ sage: v = FunctionFieldValuation(K, 1/x) sage: w = v.extension(L); w - [ Gauss valuation induced by Valuation at the infinite place, v(y) = 1/2 , … ] + Valuation at the infinite place sage: w._base_valuation._improve_approximation() sage: w._base_valuation._approximation [ Gauss valuation induced by Valuation at the infinite place, v(y) = 1/2, v(y^2 - 1/x) = +Infinity ] @@ -85,8 +85,21 @@ from sage.misc.abstract_method import abstract_method from valuation import DiscretePseudoValuation, InfiniteDiscretePseudoValuation, DiscreteValuation +from sage.structure.factory import UniqueFactory -class LimitValuation(DiscretePseudoValuation): +class LimitValuationFactory(UniqueFactory): + def create_key(self, base_valuation, G): + return base_valuation, G + + def create_object(self, version, key): + base_valuation, G = key + from valuation_space import DiscretePseudoValuationSpace + parent = DiscretePseudoValuationSpace(base_valuation.domain()) + return parent.__make_element_class__(MacLaneLimitValuation)(parent, base_valuation, G) + +LimitValuation = LimitValuationFactory("LimitValuation") + +class LimitValuation_generic(DiscretePseudoValuation): r""" Base class for limit valuations. @@ -113,7 +126,7 @@ class LimitValuation(DiscretePseudoValuation): TESTS:: - sage: isinstance(w._base_valuation, LimitValuation) + sage: isinstance(w._base_valuation, LimitValuation_generic) True sage: TestSuite(w._base_valuation).run() @@ -126,7 +139,7 @@ def __init__(self, parent, approximation): sage: R. = QQ[] sage: K. = QQ.extension(x^2 + 1) sage: v = pAdicValuation(K, 2) - sage: isinstance(v._base_valuation, LimitValuation) + sage: isinstance(v._base_valuation, LimitValuation_generic) True """ @@ -276,14 +289,14 @@ def _repr_(self): """ from sage.rings.all import infinity - from augmented_valuation import AugmentedValuation + from augmented_valuation import AugmentedValuation_generic if self._initial_approximation(self._G) < infinity: - if isinstance(self._initial_approximation, AugmentedValuation): + if isinstance(self._initial_approximation, AugmentedValuation_generic): return repr(self._initial_approximation)[:-1] + ", … ]" return repr(self._initial_approximation) -class MacLaneLimitValuation(LimitValuation, InfiniteDiscretePseudoValuation): +class MacLaneLimitValuation(LimitValuation_generic, InfiniteDiscretePseudoValuation): r""" A limit valuation that is a pseudo-valuation on polynomial ring `K[x]` which sends an irreducible polynomial `G` to infinity. @@ -318,7 +331,7 @@ def __init__(self, parent, approximation, G): True """ - LimitValuation.__init__(self, parent, approximation) + LimitValuation_generic.__init__(self, parent, approximation) self._G = G def lift(self, F): @@ -335,7 +348,7 @@ def lift(self, F): sage: v = FunctionFieldValuation(K, 1) sage: w = v.extensions(L)[0]; w - [ Gauss valuation induced by (x - 1)-adic valuation, v(y^2 - 2) = 1 , … ] + [ (x - 1)-adic valuation, v(y^2 - 2) = 1 ]-adic valuation sage: s = w.reduce(y); s u1 sage: w.lift(s) # indirect doctest @@ -476,15 +489,15 @@ def _improve_approximation_for_reduce(self, f): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 5) + sage: v = pAdicValuation(QQ, 13) sage: w = v.extensions(L)[0] sage: u = w._base_valuation sage: u._approximation - [ Gauss valuation induced by 5-adic valuation, v(t + 2) = 1 ] - sage: w.reduce((t + 2) / 5) # indirect doctest - 4 + [ Gauss valuation induced by 13-adic valuation, v(t + 5) = 1 ] + sage: w.reduce((t + 5) / 13) # indirect doctest + 8 sage: u._approximation - [ Gauss valuation induced by 5-adic valuation, v(t + 7) = 2 ] + [ Gauss valuation induced by 13-adic valuation, v(t + 70) = 2 ] ALGORITHM: @@ -496,33 +509,6 @@ def _improve_approximation_for_reduce(self, f): return self._improve_approximation_for_call(f) - def _eq_(self, other): - r""" - Return whether this valuation is indistinguishable from ``other``. - - EXAMPLES: - - We deem two valuations indistinguishable if they can not be - distinguished without considering hidden fields:: - - sage: from mac_lane import * # optional: standalone - sage: K = QQ - sage: R. = K[] - sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) - sage: w = v.extension(L) - sage: ww = v.extension(L) - sage: w == ww # indirect doctest - True - sage: w._base_valuation._improve_approximation() - sage: w == ww - True - sage: w._base_valuation._approximation == ww._base_valuation._approximation - False - - """ - return isinstance(other, MacLaneLimitValuation) and self._G == other._G and self._initial_approximation == other._initial_approximation - def residue_ring(self): r""" Return the residue field of this valuation. @@ -572,7 +558,7 @@ class FiniteExtensionFromInfiniteValuation(DiscreteValuation): sage: v = FunctionFieldValuation(K, 0) sage: w = v.extension(L); w - [ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2 , … ] + (x)-adic valuation TESTS:: @@ -590,7 +576,7 @@ def __init__(self, parent, base_valuation): sage: v = FunctionFieldValuation(K, 0) sage: w = v.extension(L); w - [ Gauss valuation induced by (x)-adic valuation, v(y^2 - x^2 + 1) = +Infinity ] + (x)-adic valuation sage: isinstance(w, FiniteExtensionFromInfiniteValuation) True @@ -629,7 +615,7 @@ def _repr_(self): sage: L. = K.extension(t^2 + 1) sage: v = pAdicValuation(QQ, 2) sage: v.extension(L) # indirect doctest - [ Gauss valuation induced by 2-adic valuation, v(t + 1) = 1/2 , … ] + 2-adic valuation """ return repr(self._base_valuation) @@ -780,8 +766,8 @@ class FiniteExtensionFromLimitValuation(FiniteExtensionFromInfiniteValuation): sage: L. = K.extension(y^2 - x) sage: v = FunctionFieldValuation(K, 1) sage: w = v.extensions(L); w - [[ Gauss valuation induced by (x - 1)-adic valuation, v(y - 1) = 1 , … ], - [ Gauss valuation induced by (x - 1)-adic valuation, v(y + 1) = 1 , … ]] + [[ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation, + [ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation] TESTS:: @@ -789,7 +775,7 @@ class FiniteExtensionFromLimitValuation(FiniteExtensionFromInfiniteValuation): sage: TestSuite(w[1]).run() """ - def __init__(self, parent, approximation, G): + def __init__(self, parent, approximant, G, approximants): r""" EXAMPLES: @@ -802,13 +788,46 @@ def __init__(self, parent, approximation, G): sage: L. = K.extension(y^2 - x) sage: v = FunctionFieldValuation(K, 2) sage: w = v.extension(L); w - [ Gauss valuation induced by (x - 2)-adic valuation, v(y^2 - x) = +Infinity ] + (x - 2)-adic valuation sage: isinstance(w, FiniteExtensionFromLimitValuation) True """ - from limit_valuation import MacLaneLimitValuation + # keep track of all extensions to this field extension so we can print + # this valuation nicely, dropping any unnecessary information + self._approximants = approximants + from valuation_space import DiscretePseudoValuationSpace - limit_parent = DiscretePseudoValuationSpace(approximation.domain()) - limit = limit_parent.__make_element_class__(MacLaneLimitValuation)(limit_parent, approximation, G) + from limit_valuation import LimitValuation + limit = LimitValuation(approximant, G) FiniteExtensionFromInfiniteValuation.__init__(self, parent, limit) + + def _repr_(self): + """ + Return a printable representation of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(GaussianIntegers().fraction_field(), 2) # indirect doctest + 2-adic valuation + + """ + if isinstance(self._base_valuation, MacLaneLimitValuation): + # print the minimal information that singles out this valuation from all approximants + assert(self._base_valuation._initial_approximation in self._approximants) + approximants = [v._augmentations() for v in self._approximants] + augmentations = self._base_valuation._approximation._augmentations() + unique_approximant = self._base_valuation._initial_approximation + for l in range(len(augmentations)): + if len([a for a in approximants if a[:l+1] == augmentations[:l+1]]) == 1: + unique_approximant = augmentations[:l+1] + break + if unique_approximant[0].is_gauss_valuation(): + unique_approximant[0] = unique_approximant[0].constant_valuation() + if len(unique_approximant) == 1: + return repr(unique_approximant[0]) + from augmented_valuation import AugmentedValuation_generic + return "[ %s ]-adic valuation"%(", ".join("v(%r) = %r"%(v._phi, v._mu) if (isinstance(v, AugmentedValuation_generic) and v.domain() == self._base_valuation.domain()) else repr(v) for v in unique_approximant)) + return "%s-adic valuation"%(self._base_valuation) + diff --git a/padic_valuation.py b/padic_valuation.py index 0700ffe71ac..dcee0f7fecb 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -24,7 +24,7 @@ sys.path.append(os.getcwd()) sys.path.append(os.path.dirname(os.getcwd())) -from valuation import DiscretePseudoValuation +from valuation import DiscreteValuation from limit_valuation import FiniteExtensionFromLimitValuation from sage.structure.factory import UniqueFactory from sage.misc.cachefunc import cached_method @@ -211,6 +211,7 @@ def create_key_for_integers(self, R, prime): from sage.rings.all import ZZ if prime is None: raise ValueError("prime must be specified for this ring") + from valuation import DiscretePseudoValuation if isinstance(prime, DiscretePseudoValuation): prime = prime.uniformizer() if prime not in ZZ or not ZZ(prime).is_prime(): @@ -234,6 +235,7 @@ def create_key_and_extra_args_for_number_field(self, R, prime): G = L.relative_polynomial() K = L.base_ring() + from valuation import DiscretePseudoValuation if isinstance(prime, DiscretePseudoValuation): return self.create_key_and_extra_args_for_number_field_from_valuation(R, prime, prime) elif prime in K: @@ -270,35 +272,10 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime) # description of v. We consider all extensions of vK to L and select # the one approximated by v. vK = v.constant_valuation() - candidates = vK.extensions(L) + approximants = vK.mac_lane_approximants(L.relative_polynomial()) + approximant = vK.mac_lane_approximant(L.relative_polynomial(), v, approximants=approximants) - # We make use of the partial order on discrete pseudo-valuations to - # single out our candidate - - # First, we see if v extends an approximant (necessarily there can be only one) - below_v = [c for c in candidates if v >= c] - assert(len(below_v) <= 1) - if len(below_v) == 1: - ret = below_v[0] - # equality of number fields has it quirks since it says that two - # fields are == even if they are distinguishable (because they come - # from different constructions.) - # Including construction() into the key seems to be a way to distinguish such cases properly. - return (R, ret, L.construction()), {'approximants': candidates} - - # Even if it does not extend an approximant, v could be an - # approximation of a single approximant - over_v = [c for c in candidates if v <= c] - if len(over_v) > 1: - raise ValueError("%s does not single out a unique extension of %s to %s"%(prime, vK, L)) - elif len(over_v) == 0: - raise ValueError("%s is unrelated to extensions of %s to %s"%(prime, vK, L)) - else: - # equality of number fields has it quirks since it says that two - # fields are == even if they are distinguishable (because they come - # from different constructions.) - # Including structure() into the key seems to be a way to distinguish such cases properly. - return (R, over_v[0], L.construction()), {'approximants': candidates} + return (R, approximant, L.construction()), {'approximants': approximants} def create_key_and_extra_args_for_number_field_from_ideal(self, R, I, prime): r""" @@ -332,9 +309,9 @@ def create_key_and_extra_args_for_number_field_from_ideal(self, R, I, prime): def create_object(self, version, key, **extra_args): from sage.rings.all import ZZ, QQ from sage.rings.padics.padic_generic import pAdicGeneric - from valuation_space import DiscreteValuationSpace + from valuation_space import DiscretePseudoValuationSpace R = key[0] - parent = DiscreteValuationSpace(R) + parent = DiscretePseudoValuationSpace(R) if isinstance(R, pAdicGeneric): assert(len(key)==1) return parent.__make_element_class__(pAdicValuation_padic)(R) @@ -346,13 +323,12 @@ def create_object(self, version, key, **extra_args): v = key[1] _ = key[2] # ignored approximants = extra_args['approximants'] - parent = DiscreteValuationSpace(R) - return parent.__make_element_class__(pAdicFromLimitValuation)(parent, v, R.relative_polynomial()) - # return parent.__make_element_class__(pAdicValuation_number_field)(R, v, approximants) + parent = DiscretePseudoValuationSpace(R) + return parent.__make_element_class__(pAdicFromLimitValuation)(parent, v, R.relative_polynomial(), approximants) pAdicValuation = PadicValuationFactory("pAdicValuation") -class pAdicValuation_base(DiscretePseudoValuation): +class pAdicValuation_base(DiscreteValuation): """ Common base class for `p`-adic valuations on integral domains. @@ -397,8 +373,8 @@ def __init__(self, ring, prime): """ from rings_with_valuation import RingsWithDiscreteValuation - from valuation_space import DiscreteValuationSpace - DiscretePseudoValuation.__init__(self, DiscreteValuationSpace(ring)) + from valuation_space import DiscretePseudoValuationSpace + DiscreteValuation.__init__(self, DiscretePseudoValuationSpace(ring)) self._prime = prime def prime(self): @@ -437,6 +413,7 @@ def _repr_(self): """ return "%s-adic valuation"%(self.prime()) + from valuation import DiscretePseudoValuation if isinstance(self.prime(), DiscretePseudoValuation): vals = self.prime()._augmentations() if vals[0].is_gauss_valuation(): @@ -918,11 +895,11 @@ def extensions(self, ring): from sage.rings.number_field.number_field import is_NumberField if is_NumberField(ring): if ring.base() is self.domain(): - from valuation_space import DiscreteValuationSpace + from valuation_space import DiscretePseudoValuationSpace from limit_valuation import FiniteExtensionFromInfiniteValuation - parent = DiscreteValuationSpace(ring) + parent = DiscretePseudoValuationSpace(ring) approximants = self.mac_lane_approximants(ring.relative_polynomial()) - return [parent.__make_element_class__(pAdicFromLimitValuation)(parent, approximant, ring.relative_polynomial()) for approximant in approximants] + return [pAdicValuation(ring, approximant) for approximant in approximants] if ring.base() is not ring and self.domain().is_subring(ring.base()): return sum([w.extensions(ring) for w in self.extensions(ring.base())], []) raise NotImplementedError("extensions of %r from %r to %r not implemented"%(self, self.domain(), ring)) @@ -1089,9 +1066,6 @@ def _mac_lane_step(self): assert E == self._valuation.E() assert F == self._valuation.F() - def _repr_(self): - return "%r-adic valuation"%(self._valuation) - def _call_(self, x): if x.parent() is not self.domain(): raise ValueError("x must be in the domain of the valuation") @@ -1143,8 +1117,9 @@ def _repr_(self): '3-adic valuation' """ + from valuation import DiscretePseudoValuation if isinstance(self._valuation, DiscretePseudoValuation): - from augmented_valuation import AugmentedValuation + from augmented_valuation import AugmentedValuation_generic # print the minimal information that singles out this valuation from all approximants approximants = [v._augmentations() for v in self._approximants] augmentations = self._valuation._augmentations() @@ -1157,7 +1132,7 @@ def _repr_(self): unique_approximant[0] = unique_approximant[0].constant_valuation() if len(unique_approximant) == 1: return repr(unique_approximant[0]) - return "[ %s ]-adic valuation"%(", ".join("v(%r) = %r" if (isinstance(v, AugmentedValuation) and v.domain() == self._valuation.domain()) else repr(v) for v in unique_approximant)) + return "[ %s ]-adic valuation"%(", ".join("v(%r) = %r" if (isinstance(v, AugmentedValuation_generic) and v.domain() == self._valuation.domain()) else repr(v) for v in unique_approximant)) return "%s-adic valuation"%(self._valuation) class pAdicFromLimitValuation(FiniteExtensionFromLimitValuation): diff --git a/rings_with_valuation.py b/rings_with_valuation.py deleted file mode 120000 index b3b592df7e3..00000000000 --- a/rings_with_valuation.py +++ /dev/null @@ -1 +0,0 @@ -../src/sage/categories/rings_with_valuation.py \ No newline at end of file diff --git a/trivial_valuation.py b/trivial_valuation.py index 1c09cf4eebe..9ec51b03eb1 100644 --- a/trivial_valuation.py +++ b/trivial_valuation.py @@ -56,7 +56,7 @@ sys.path.append(os.path.dirname(os.getcwd())) from valuation import DiscretePseudoValuation, DiscreteValuation, InfiniteDiscretePseudoValuation -from valuation_space import DiscreteValuationSpace, DiscretePseudoValuationSpace +from valuation_space import DiscretePseudoValuationSpace from sage.structure.factory import UniqueFactory class TrivialValuationFactory(UniqueFactory): @@ -402,6 +402,6 @@ def extensions(self, ring): return [TrivialValuation(ring)] return super(DiscretePseudoValuation, self).extensions(ring) -TrivialValuation = TrivialValuationFactory(TrivialDiscreteValuation, DiscreteValuationSpace, "TrivialValuation") +TrivialValuation = TrivialValuationFactory(TrivialDiscreteValuation, DiscretePseudoValuationSpace, "TrivialValuation") TrivialPseudoValuation = TrivialValuationFactory(TrivialDiscretePseudoValuation, DiscretePseudoValuationSpace, "TrivialPseudoValuation") diff --git a/valuation.py b/valuation.py index 2348c75c012..e5217f097f0 100644 --- a/valuation.py +++ b/valuation.py @@ -129,8 +129,20 @@ def _cmp_(self, other): ... NotImplementedError: Operator not implemented for this valuation. + Note that this does not affect comparison of valuations which do not + coerce into a common parent. This is by design in Sage, see + :meth:`sage.structure.element.Element.__cmp__`. When the valuations do + not coerce into a common parent, a rather random comparison of ``id`` + happens:: + + sage: w = TrivialValuation(GF(2)) + sage: w < v # random output + True + sage: v < w # random output + False + """ - raise NotImplementedError("No total order for valuation."); + raise NotImplementedError("No total order for these valuations.") def _richcmp_(self, other, op): r""" @@ -159,6 +171,18 @@ def _richcmp_(self, other, op): sage: v != w True + Note that this does not affect comparison of valuations which do not + coerce into a common parent. This is by design in Sage, see + :meth:`sage.structure.element.Element.__richcmp__`. When the valuations + do not coerce into a common parent, a rather random comparison of + ``id`` happens:: + + sage: w = TrivialValuation(GF(2)) + sage: w <= v # random output + True + sage: v <= w # random output + False + """ if op == 1: # <= return self._lt_(other) @@ -224,6 +248,20 @@ class DiscreteValuation(DiscretePseudoValuation): r""" sage: TODO """ + def is_discrete_valuation(self): + r""" + Return whether this valuation is a discrete valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = TrivialValuation(ZZ) + sage: v.is_discrete_valuation() + True + + """ + return True + def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): r""" sage: TODO @@ -303,7 +341,7 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): for v in expandables: leaves.extend(v.mac_lane_step(G)) - def mac_lane_approximant(self, G, valuation): + def mac_lane_approximant(self, G, valuation, approximants = None): r""" sage: TODO """ @@ -321,7 +359,10 @@ def mac_lane_approximant(self, G, valuation): raise ValueError("The valuation %r is not an approximant for a valuation on %r since the valuation of %r does not increase in every step"%(valuation, domain, G_integral)) v = v._base_valuation - approximants = self.mac_lane_approximants(G) + if approximants is None: + approximants = self.mac_lane_approximants(G) + + assert all(approximant.domain() is valuation.domain() for approximant in approximants) greater_approximants = [w for w in approximants if w <= valuation] if len(greater_approximants) > 1: @@ -332,7 +373,6 @@ def mac_lane_approximant(self, G, valuation): smaller_approximants = [w for w in approximants if w >= valuation] assert len(smaller_approximants) <= 1 if len(smaller_approximants) == 0: - raise ValueError("valuation %r does not describe an extension of %r to %r"%(valuation, valuation.constant_valuation(), domain)) + raise ValueError("valuation %r does not describe an extension of %r with respect to %r"%(valuation, valuation.constant_valuation(), G)) assert len(smaller_approximants) == 1 return smaller_approximants[0] - diff --git a/valuation_space.py b/valuation_space.py index 4cb6f038014..ef9b4416179 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -11,7 +11,7 @@ sage: from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2).parent() - Discrete valuations on Rational Field + Discrete pseudo-valuations on Rational Field AUTHORS: @@ -49,6 +49,27 @@ class DiscretePseudoValuationSpace(UniqueRepresentation, Homset): sage: pAdicValuation(QQ, 2) in H True + .. NOTE:: + + We do not distinguish between the space of discrete valuations and the + space of discrete pseudo-valuations. This is entirely for practical + reasons: We would like to model the fact that every discrete valuation + is also a discrete pseudo-valuation. At first, it seems to be + sufficient to make sure that the ``in`` operator works which can + essentially be achieved by overriding ``_element_constructor_`` of + the space of discrete pseudo-valuations to accept discrete valuations + by just returning them. Currently, however, if one does not change the + parent of an element in ``_element_constructor_`` to ``self``, then + one can not register that conversion as a coercion. Consequently, the + operators ``<=`` and ``>=`` can not be made to work between discrete + valuations and discrete pseudo-valuations on the same domain (because + the implementation only calls ``_richcmp`` if both operands have the + same parent.) Of course, we could override ``__ge__`` and ``__le__`` + but then we would likely run into other surprises. + So in the end, we went for a single homspace for all discrete + valuations (pseudo or not) as this makes the implementation much + easier. + TESTS:: sage: TestSuite(H).run() @@ -201,10 +222,6 @@ def _element_constructor_(self, x): except NotImplementedError: pass else: - # If this is an element of a discrete pseudo-valuation space over the same domain, - # then we treat it as an element of this space (see __contains__), i.e., we do not - # actually change x.parent() to self here if it is, e.g., a discrete valuation space. - # This might be surprising but is how facades work for example. return x raise ValueError("element can not be converted into the space of %r"%(self,)) @@ -410,6 +427,40 @@ def residue_ring(self): """ + def residue_field(self): + r""" + Return the residue field of this valuation, i.e., the field of + fractions of the :meth:`residue_ring`, the elements of non-negative + valuation module the elements of positive valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(QQ, 2).residue_field() + Finite Field of size 2 + sage: TrivialValuation(QQ).residue_field() + Rational Field + + sage: TrivialValuation(ZZ).residue_field() + Rational Field + sage: GaussValuation(ZZ['x'], pAdicValuation(ZZ, 2)).residue_field() + Rational function field in x over Finite Field of size 2 + + """ + if not self.is_discrete_valuation(): + raise NotImplementedError + + ret = self.residue_ring() + from sage.categories.fields import Fields + if ret in Fields(): + return ret + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + if is_PolynomialRing(ret): + from sage.rings.function_field.all import FunctionField + return FunctionField(ret.base_ring().fraction_field(), names=(ret.variable_name(),)) + return ret.fraction_field() + + @abstract_method def reduce(self, x): r""" @@ -784,161 +835,6 @@ def _test_change_ring(self, **options): tester.assertEqual(self.change_ring(self.domain()), self) - -class DiscreteValuationSpace(DiscretePseudoValuationSpace): - r""" - The space of discrete valuations on ``domain``. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: H = DiscreteValuationSpace(QQ) - sage: pAdicValuation(QQ, 2) in H - True - - TESTS:: - - sage: TestSuite(H).run() - - """ - def __init__(self, domain): - r""" - TESTS:: - - sage: from mac_lane import * # optional: standalone - sage: isinstance(pAdicValuation(QQ, 2).parent(), DiscreteValuationSpace) - True - - """ - DiscretePseudoValuationSpace.__init__(self, domain) - - def _element_constructor_(self, x): - r""" - Create an element in this space from ``x``. - - TESTS: - - Discrete pseudo-valuations that are actually valuations are contained - in this space:: - - sage: from mac_lane import * # optional: standalone - sage: H = DiscretePseudoValuationSpace(QQ) - sage: G = DiscreteValuationSpace(QQ) - sage: v = pAdicValuation(QQ, 2) - sage: v._set_parent(H) - sage: v in G - True - - Discrete pseudo-valuations that are discrete valuations can be - converted into this space:: - - sage: H = DiscreteValuationSpace(ZZ) - sage: v = pAdicValuation(ZZ, 2) - sage: v._set_parent(H) - sage: v in G - False - sage: G(v) in G - True - - """ - # We accept any discrete pseudo-valuation that claims to be a discrete valuation - if isinstance(x.parent(), DiscretePseudoValuationSpace) and x.is_discrete_valuation(): - if x.domain() is not self.domain(): - # after we base-changed them into our domain - return DiscretePseudoValuationSpace(self.domain())(x) - # If this is a valuation in a discrete pseudo-valuation space over the same domain, - # then we treat it as an element of this space (see __contains__), i.e., we do not - # actually change x.parent() to self here if it is, e.g., a - # discrete pseudo-valuation space. This might be surprising but is - # also how facades work for example. - return x - raise ValueError("element does not convert to a discrete valuation in %r"%(self,)) - - def _repr_(self): - r""" - Return a printable representation of this space. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: DiscreteValuationSpace(QQ) # indirect doctest - Discrete valuations on Rational Field - - """ - return "Discrete valuations on %r"%(self.domain(),) - - def _an_element_(self): - r""" - Return the trivial valuation in this space. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: DiscreteValuationSpace(QQ).an_element() # indirect doctest - Trivial valuation on Rational Field - - """ - from trivial_valuation import TrivialValuation - return TrivialValuation(self.domain()) - - class ElementMethods(DiscretePseudoValuationSpace.ElementMethods): - r""" - Provides methods for discrete valuations that are added - automatically to valuations in this space. - - EXAMPLES: - - Here is an example of a method that is automagically added to a - discrete valuation:: - - sage: from mac_lane import * # optional: standalone - sage: pAdicValuation(QQ, 2).is_discrete_valuation() # indirect doctest - True - - """ - def is_discrete_valuation(self): - r""" - Return whether this valuation is a discrete valuation. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: pAdicValuation(QQ, 2).is_discrete_valuation() - True - - """ - return True - - def residue_field(self): - r""" - Return the residue field of this valuation, i.e., the field of - fractions of the :meth:`residue_ring`, the elements of non-negative - valuation module the elements of positive valuation. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: pAdicValuation(QQ, 2).residue_field() - Finite Field of size 2 - sage: TrivialValuation(QQ).residue_field() - Rational Field - - sage: TrivialValuation(ZZ).residue_field() - Rational Field - sage: GaussValuation(ZZ['x'], pAdicValuation(ZZ, 2)).residue_field() - Rational function field in x over Finite Field of size 2 - - """ - ret = self.residue_ring() - from sage.categories.fields import Fields - if ret in Fields(): - return ret - from sage.rings.polynomial.polynomial_ring import is_PolynomialRing - if is_PolynomialRing(ret): - from sage.rings.function_field.all import FunctionField - return FunctionField(ret.base_ring().fraction_field(), names=(ret.variable_name(),)) - return ret.fraction_field() - def _test_no_infinite_nonzero(self, **options): r""" Check that only zero is sent to infinity. @@ -950,6 +846,9 @@ def _test_no_infinite_nonzero(self, **options): sage: v._test_no_infinite_nonzero() """ + if not self.is_discrete_valuation(): + return + from sage.rings.all import infinity tester = self._tester(**options) for x in tester.some_elements(self.domain().some_elements()): @@ -967,6 +866,9 @@ def _test_residue_field(self, **options): sage: v._test_residue_field() """ + if not self.is_discrete_valuation(): + return + tester = self._tester(**options) try: k = self.residue_field() From e69aace15e584baf91360fb034f07b686d62b691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 31 Oct 2016 20:43:48 -0500 Subject: [PATCH 048/740] Move MacLane code to a single spot and implement some comparison operators --- __init__.py | 3 +- augmented_valuation.py | 23 ++- developing_valuation.py | 20 +- gauss_valuation.py | 2 +- padic_valuation.py | 191 +------------------ trivial_valuation.py | 36 ++++ valuation.py | 405 ++++++++++++++++++++++++++++++++++++---- valuation_space.py | 37 ++++ 8 files changed, 482 insertions(+), 235 deletions(-) diff --git a/__init__.py b/__init__.py index cd3ea6d4709..133849e5761 100644 --- a/__init__.py +++ b/__init__.py @@ -12,8 +12,9 @@ from .trivial_valuation import TrivialDiscreteValuation, TrivialDiscretePseudoValuation from .function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldFromLimitValuation from .limit_valuation import LimitValuation, MacLaneLimitValuation, FiniteExtensionFromInfiniteValuation, FiniteExtensionFromLimitValuation, LimitValuation_generic -from .augmented_valuation import AugmentedValuation_generic +from .augmented_valuation import AugmentedValuation_generic, InfiniteAugmentedValuation from .gauss_valuation import GaussValuation_generic +from .valuation import DiscretePseudoValuation, DiscreteValuation, InfiniteDiscretePseudoValuation # ================= # MONKEY PATCH SAGE diff --git a/augmented_valuation.py b/augmented_valuation.py index 322628bbc19..eacbe061f0d 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -33,6 +33,7 @@ sys.path.append(os.path.dirname(os.getcwd())) from developing_valuation import DevelopingValuation, _lift_to_maximal_precision +from valuation import InfiniteDiscretePseudoValuation, DiscreteValuation from sage.misc.cachefunc import cached_method from sage.rings.all import infinity @@ -52,18 +53,16 @@ def create_key(self, base_valuation, phi, mu, check=True): def create_object(self, version, key): base_valuation, phi, mu = key + from valuation_space import DiscretePseudoValuationSpace + parent = DiscretePseudoValuationSpace(base_valuation.domain()) if mu < infinity: - from valuation_space import DiscretePseudoValuationSpace - parent = DiscretePseudoValuationSpace(base_valuation.domain()) + return parent.__make_element_class__(AugmentedValuation_generic)(parent, base_valuation, phi, mu) else: - from valuation_space import DiscretePseudoValuationSpace - parent = DiscretePseudoValuationSpace(base_valuation.domain()) - - return parent.__make_element_class__(AugmentedValuation_generic)(parent, base_valuation, phi, mu) + return parent.__make_element_class__(InfiniteAugmentedValuation)(parent, base_valuation, phi, mu) AugmentedValuation = AugmentedValuationFactory("AugmentedValuation") -class AugmentedValuation_generic(DevelopingValuation): +class AugmentedValuation_generic(DevelopingValuation, DiscreteValuation): """ An augmented valuation is a discrete valuation on a polynomial ring. It extends another discrete valuation `v` by setting the valuation of a @@ -967,8 +966,10 @@ def is_gauss_valuation(self): def _make_monic_integral(self, G): return self._base_valuation._make_monic_integral(G) - def _gt_(self, other): + def _ge_(self, other): from gauss_valuation import GaussValuation_generic + if other.is_trivial(): + return other.is_discrete_valuation() if isinstance(other, GaussValuation_generic): return self._base_valuation >= other if isinstance(other, AugmentedValuation_generic): @@ -977,8 +978,12 @@ def _gt_(self, other): else: return False - raise NotImplementedError("Operator not implemented for these valuations.") + return super(AugmentedValuation_generic, self)._ge_(other) def is_discrete_valuation(self): from sage.rings.all import infinity return self._mu != infinity + +class InfiniteAugmentedValuation(AugmentedValuation_generic, InfiniteDiscretePseudoValuation): + pass + diff --git a/developing_valuation.py b/developing_valuation.py index 72adfde2a43..83679c4681d 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -1064,12 +1064,15 @@ def mac_lane_step(self, G, assume_squarefree=False): return [self.augmentation(g, infinity)] if phi == self.phi(): - # self.phi() always is a key over self but it will not lead to an extension of this valuation + # a factor phi in the equivalence decomposition means that we + # founnd an actual factor of G, i.e., we can set + # v(phi)=infinity + # However, this should already have happened in the last step + # (when this polynomial had -infinite slope in the Newton + # polygon.) from gauss_valuation import GaussValuation if self.is_gauss_valuation(): # unless in the first step pass - elif len(F)==1: # unless this is the only factor, a terminating case which should give a valuation with v(phi)=infinity - pass else: continue @@ -1078,7 +1081,10 @@ def mac_lane_step(self, G, assume_squarefree=False): NP = w.newton_polygon(G).principal_part() verbose("Newton-Polygon for v(phi)=%s : %s"%(self(phi),NP),level=2,caller_name="mac_lane_step") # assert len(NP) - if not NP.slopes(): + slopes = NP.slopes(repetition=False) + if NP.vertices()[0][0] != 0: + slopes = [-infinity] + slopes + if not slopes: q,r = G.quo_rem(phi) assert not r.is_zero() phi = phi.coefficients(sparse=False) @@ -1099,11 +1105,9 @@ def mac_lane_step(self, G, assume_squarefree=False): ret.append(w) continue - for i in range(len(NP.slopes(repetition=False))): - slope = NP.slopes()[i] + for i in range(len(slopes)): + slope = slopes[i] verbose("Slope = %s"%slope,level=3,caller_name="mac_lane_step") - side = NP.sides()[i] - verbose("Left end is %s"%(list(w.coefficients(G))[side[0][0]]),level=3,caller_name="mac_lane_step") new_mu = self(phi) - slope base = self if phi.degree() == base.phi().degree(): diff --git a/gauss_valuation.py b/gauss_valuation.py index 91907337dbc..9dc4ad6998b 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -617,7 +617,7 @@ def _make_monic_integral(self, G): assert G.is_monic() return G - def _gt_(self, other): + def _ge_(self, other): if isinstance(other, GaussValuation_generic): return self._base_valuation >= other._base_valuation from augmented_valuation import AugmentedValuation_generic diff --git a/padic_valuation.py b/padic_valuation.py index dcee0f7fecb..46257d0af13 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -698,190 +698,6 @@ def montes_factorization(self, G, assume_squarefree=False): from sage.structure.factorization import Factorization return Factorization([ (g,1) for g in ret ], simplify=False) - def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): - """ - Compute the extensions `w` of this valuation to a polynomial ring over - the domain which have `w(G)=\infty`. - - INPUT: - - - ``G`` -- a monic polynomial over the domain of this valuation - - - ``precision_cap`` -- a rational, ``infinity`` or ``None`` (default: - ``None``), stop computation as soon as no new branches can show up in - the tree of valuations and the valuation of the key polynomials is at - least ``precision_cap`` (if not ``None``). - - - ``assume_squarefree`` -- a boolean (default: ``False``), whether to - assume the input to be squarefree - - EXAMPLES:: - - sage: k=Qp(2,10) - sage: v = pAdicValuation(k) - - sage: R.=k[] - sage: G = x - sage: v.mac_lane_approximants(G) - [Gauss valuation induced by 2-adic valuation] - sage: v.mac_lane_approximants(G, precision_cap = infinity) - [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = +Infinity ]] - - sage: G = x^2 + 1 - sage: v.mac_lane_approximants(G) - [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x + (1 + O(2^10))) = 1/2 ]] - sage: v.mac_lane_approximants(G, precision_cap = infinity) - [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x + (1 + O(2^10))) = 1/2, v((1 + O(2^10))*x^2 + (1 + O(2^10))) = +Infinity ]] - - sage: G = x^4 + 2*x^3 + 2*x^2 - 2*x + 2 - sage: v.mac_lane_approximants(G) - [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = 1/4 ]] - sage: v.mac_lane_approximants(G,infinity) - [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = 1/4, v((1 + O(2^10))*x^4 + (2 + O(2^11))*x^3 + (2 + O(2^11))*x^2 + (2 + 2^2 + 2^3 + 2^4 + 2^5 + 2^6 + 2^7 + 2^8 + 2^9 + 2^10 + O(2^11))*x + (2 + O(2^11))) = +Infinity ]] - - The factorization of primes in the Gaussian integers can be read off - the Mac Lane approximants:: - - sage: v0 = pAdicValuation(QQ, 2) - sage: R. = QQ[] - sage: G = x^2 + 1 - sage: v0.mac_lane_approximants(G) - [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/2 ]] - - sage: v0 = pAdicValuation(QQ, 3) - sage: v0.mac_lane_approximants(G) - [[ Gauss valuation induced by 3-adic valuation, v(x^2 + 1) = +Infinity ]] - - sage: v0 = pAdicValuation(QQ, 5) - sage: v0.mac_lane_approximants(G) - [[ Gauss valuation induced by 5-adic valuation, v(x + 2) = 1 ], [ Gauss valuation induced by 5-adic valuation, v(x + 3) = 1 ]] - sage: v0.mac_lane_approximants(G, precision_cap = 10) # long time - [[ Gauss valuation induced by 5-adic valuation, v(x + 25670807) = 11 ], [ Gauss valuation induced by 5-adic valuation, v(x + 23157318) = 11 ]] - - The same example over the 5-adic numbers. In the quadratic extension - `\QQ[x]/(x^2+1)`, 5 factors `-(x - 2)(x + 2)`, this behaviour can be - read off the Mac Lane approximants:: - - sage: k=Qp(5,4) - sage: v = pAdicValuation(k) - sage: R.=k[] - sage: G = x^2 + 1 - sage: v1,v2 = v.mac_lane_approximants(G); v1,v2 - ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + O(5^4))) = 1 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + O(5^4))) = 1 ]) - sage: w1, w2 = v.mac_lane_approximants(G,precision_cap=2); w1,w2 - ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + 2*5^2 + O(5^4))) = 3 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + 3*5 + 2*5^2 + O(5^4))) = 3 ]) - - Note how the latter give a better approximation to the factors of `x^2 + 1`:: - - sage: v1.phi() * v2.phi() - G - (5 + O(5^4))*x + 5 + O(5^4) - sage: w1.phi() * w2.phi() - G - (5^3 + O(5^4))*x + 5^3 + O(5^4) - - In this example, the process stops with a factorization of `x^2 + 1`:: - - sage: v.mac_lane_approximants(G, precision_cap=infinity) - [[ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + 2*5^2 + 5^3 + O(5^4))) = +Infinity ], - [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + 3*5 + 2*5^2 + 3*5^3 + O(5^4))) = +Infinity ]] - - This obviously cannot happen over the rationals where we only get an - approximate factorization:: - - sage: v = pAdicValuation(QQ, 5) - sage: R.=QQ[] - sage: G = x^2 + 1 - sage: v.mac_lane_approximants(G) - [[ Gauss valuation induced by 5-adic valuation, v(x + 2) = 1 ], [ Gauss valuation induced by 5-adic valuation, v(x + 3) = 1 ]] - sage: v.mac_lane_approximants(G, precision_cap=5) - [[ Gauss valuation induced by 5-adic valuation, v(x + 14557) = 6 ], [ Gauss valuation induced by 5-adic valuation, v(x + 32318) = 7 ]] - - TESTS: - - Initial versions ran into problems with the trivial residue field - extensions in this case:: - - sage: K = Qp(3,20) - sage: R. = K[] - - sage: alpha = T^3/4 - sage: G = 3^3*T^3*(alpha^4 - alpha)^2 - (4*alpha^3 - 1)^3 - sage: G = G/G.leading_coefficient() - sage: pAdicValuation(K).mac_lane_approximants(G) # long time - [[ Gauss valuation induced by 3-adic valuation, v((1 + O(3^20))*T + (2 + O(3^20))) = 1/9, v((1 + O(3^20))*T^9 + (2*3 + 2*3^2 + O(3^21))*T^8 + (3 + 3^5 + O(3^21))*T^7 + (2*3 + 2*3^2 + 3^3 + 2*3^4 + 2*3^5 + 3^6 + O(3^21))*T^6 + (2*3 + 2*3^2 + 3^4 + 3^6 + 2*3^7 + O(3^21))*T^5 + (3 + 3^2 + 3^3 + 2*3^6 + 2*3^7 + 3^8 + O(3^21))*T^4 + (2*3 + 2*3^2 + 3^3 + 2*3^5 + 2*3^6 + 2*3^7 + 2*3^8 + O(3^21))*T^3 + (2*3 + 2*3^2 + 3^3 + 2*3^4 + 3^5 + 2*3^6 + 2*3^7 + 2*3^8 + O(3^21))*T^2 + (3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^7 + 3^8 + O(3^21))*T + (2 + 2*3 + 2*3^2 + 2*3^4 + 2*3^5 + 3^7 + O(3^20))) = 55/27 ]] - - A similar example:: - - sage: R. = QQ[] - sage: v = pAdicValuation(QQ, 3) - sage: G = (x^3 + 3)^3 - 81 - sage: v.mac_lane_approximants(G) - [[ Gauss valuation induced by 3-adic valuation, v(x) = 1/3, v(x^3 + 3*x + 3) = 13/9 ]] - - Another problematic case:: - - sage: R.=QQ[] - sage: Delta=x^12 + 20*x^11 + 154*x^10 + 664*x^9 + 1873*x^8 + 3808*x^7 + 5980*x^6 + 7560*x^5 + 7799*x^4 + 6508*x^3 + 4290*x^2 + 2224*x + 887 - sage: K.=NumberField(x^6+108) - sage: K.is_galois() - True - sage: vK=pAdicValuation(QQ,2).extension(K) - sage: vK(2) - 3 - sage: vK(theta) - 1 - sage: G=Delta.change_ring(K) - sage: V=vK.mac_lane_approximants(G) - - OUTPUT: - - A list of valuations over the parent of ``G``. - - .. NOTE:: - - For an irreducible ``G`` over `K`, these extensions correspond to - the extensions of this valuation to the field `K[x]/(G(x))`, with - `K` the number field corresponding to the domain of this valuation, - see Theorem 2.1 in [ML1936] - - REFERENCES: - - .. [ML1936] Mac Lane, S. (1936). A construction for prime ideals as absolute - values of an algebraic field. Duke Mathematical Journal, 2(3), 492-510. - - """ - R = G.parent() - if R.base_ring() is not self.domain(): - raise ValueError("G must be defined over the domain of this valuation") - if not assume_squarefree and not G.is_squarefree(): - raise ValueError("G must be squarefree") - if not G.is_monic(): - raise ValueError("G must be monic") - - from sage.rings.all import infinity - from gauss_valuation import GaussValuation - - leaves = [ GaussValuation(R, self)] - while True: - ef = [ v.E()*v.F() for v in leaves] - if sum(ef) == G.degree(): - if precision_cap is None or all([v(v.phi())>precision_cap for v in leaves]): - return leaves - - expandables = [] - new_leaves = [] - for v in leaves: - if v(G) is infinity: - new_leaves.append(v) - else: - expandables.append(v) - leaves = new_leaves - - if not expandables: - return leaves - - for v in expandables: - leaves.extend(v.mac_lane_step(G)) - def change_ring(self, ring): return pAdicValuation(ring, self.prime()) @@ -929,6 +745,13 @@ def restriction(self, ring): return pAdicValuation(L, W[0]) else: raise ValueError + def _ge_(self, other): + if other.is_trivial(): + return other.is_discrete_valuation() + if isinstance(other, pAdicValuation_base): + return self.prime() == other.prime() + return super(pAdicValuation_base, self)._ge_(self, other) + class pAdicValuation_padic(pAdicValuation_base): """ The `p`-adic valuation of a `p`-adic ring. diff --git a/trivial_valuation.py b/trivial_valuation.py index 9ec51b03eb1..5cfb09a57da 100644 --- a/trivial_valuation.py +++ b/trivial_valuation.py @@ -272,6 +272,23 @@ def lift(self, X): self.residue_ring().coerce(X) # ignore the output return self.domain().zero() + def _ge_(self, other): + r""" + Return whether this valuation is bigger or equal than ``other`` + everywhere. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = TrivialPseudoValuation(QQ) + sage: w = TrivialValuation(QQ) + sage: v >= w + True + + """ + # the trivial discrete valuation is the biggest valuation + return True + class TrivialDiscreteValuation(TrivialDiscretePseudoValuation_base, DiscreteValuation): r""" The trivial valuation that is zero on non-zero elements. @@ -402,6 +419,25 @@ def extensions(self, ring): return [TrivialValuation(ring)] return super(DiscretePseudoValuation, self).extensions(ring) + def _ge_(self, other): + r""" + Return whether this valuation is bigger or equal than ``other`` + everywhere. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = TrivialPseudoValuation(QQ) + sage: w = TrivialValuation(QQ) + sage: w >= v + False + + """ + # the trivial discrete valuation is the smallest valuation + if self is other: + return True + return False + TrivialValuation = TrivialValuationFactory(TrivialDiscreteValuation, DiscretePseudoValuationSpace, "TrivialValuation") TrivialPseudoValuation = TrivialValuationFactory(TrivialDiscretePseudoValuation, DiscretePseudoValuationSpace, "TrivialPseudoValuation") diff --git a/valuation.py b/valuation.py index e5217f097f0..df81cfd946d 100644 --- a/valuation.py +++ b/valuation.py @@ -74,21 +74,18 @@ def is_equivalent(self, f, g): True """ - vf = self(f) - vg = self(g) from sage.rings.all import infinity - if self(f) == infinity and self(g) == infinity: - return True - if self(f) == infinity or self(g) == infinity: - return False - return self(f-g) > vf + if self(f) == infinity: + return self(g) == infinity + + return self(f-g) > self(f) def __hash__(self): r""" The hash value of this valuation. We redirect to :meth:`_hash_`, so that subclasses can only override - :meth:`_hash_` and :meth:`_eq` if they want to provide a different + :meth:`_hash_` and :meth:`_eq_` if they want to provide a different notion of equality but they can leave the partial and total operators untouched. @@ -108,8 +105,19 @@ def _hash_(self): We override the strange default provided by ``sage.categories.marphism.Morphism`` here and implement equality by - ``id``. This works fine for objects which use unique representation - which is the case for most valuations. + ``id``. This works fine for objects which use unique representation. + + Note that the vast majority of valuations come out of a + :class:`sage.structure.factory.UniqueFactory` and therefore override + our implementation of :meth:`__hash__` and :meth:`__eq__`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 2) + sage: hash(v) == hash(v) # indirect doctest + True + """ return id(self) @@ -122,7 +130,7 @@ def _cmp_(self, other): EXAMPLES:: - sage: from mac_lane import * + sage: from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: v < v Traceback (most recent call last): @@ -159,7 +167,7 @@ def _richcmp_(self, other, op): EXAMPLES:: - sage: from mac_lane import * + sage: from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: v == v True @@ -185,13 +193,13 @@ def _richcmp_(self, other, op): """ if op == 1: # <= - return self._lt_(other) + return self._le_(other) if op == 2: # == return self._eq_(other) if op == 3: # != return not self == other if op == 5: # >= - return self._gt_(other) + return self._ge_(other) raise NotImplementedError("Operator not implemented for this valuation.") def _eq_(self, other): @@ -202,28 +210,82 @@ def _eq_(self, other): ``sage.categories.marphism.Morphism`` here and implement equality by ``id``. This is the right behaviour in many cases. + Note that the vast majority of valuations come out of a + :class:`sage.structure.factory.UniqueFactory` and therefore override + our implementation of :meth:`__hash__` and :meth:`__eq__`. + When overriding this method, you can assume that ``other`` is a (pseudo-)valuation on the same domain. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = TrivialValuation(QQ) + sage: v == v + True + """ return self is other - def _lt_(self, other): + def _le_(self, other): r""" Return whether this valuation is less than or equal to ``other`` pointwise. When overriding this method, you can assume that ``other`` is a (pseudo-)valuation on the same domain. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = TrivialValuation(QQ) + sage: w = pAdicValuation(QQ, 2) + sage: v <= w + True + + Note that this does not affect comparison of valuations which do not + coerce into a common parent. This is by design in Sage, see + :meth:`sage.structure.element.Element.__richcmp__`. When the valuations + do not coerce into a common parent, a rather random comparison of + ``id`` happens:: + + sage: w = TrivialValuation(GF(2)) + sage: w <= v # random output + True + sage: v <= w # random output + False + """ return other >= self - def _gt_(self, other): + def _ge_(self, other): r""" Return whether this valuation is greater than or equal to ``other`` pointwise. When overriding this method, you can assume that ``other`` is a (pseudo-)valuation on the same domain. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = TrivialValuation(QQ) + sage: w = pAdicValuation(QQ, 2) + sage: v >= w + False + + Note that this does not affect comparison of valuations which do not + coerce into a common parent. This is by design in Sage, see + :meth:`sage.structure.element.Element.__richcmp__`. When the valuations + do not coerce into a common parent, a rather random comparison of + ``id`` happens:: + + sage: w = TrivialValuation(GF(2)) + sage: w <= v # random output + True + sage: v <= w # random output + False + """ if self == other: return True raise NotImplementedError("Operator not implemented for this valuation.") @@ -236,17 +298,61 @@ def _gt_(self, other): class InfiniteDiscretePseudoValuation(DiscretePseudoValuation): r""" - sage: TODO + Abstract base class for infinite discrete pseudo-valuations, i.e., discrete + pseudo-valuations which are not discrete valuations. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 2) + sage: R. = QQ[] + sage: v = GaussValuation(R, v) + sage: w = v.augmentation(x, infinity); w # indirect doctest + [ Gauss valuation induced by 2-adic valuation, v(x) = +Infinity ] + + TESTS:: + + sage: isinstance(w, InfiniteDiscretePseudoValuation) + True + sage: TestSuite(w).run() + """ def is_discrete_valuation(self): r""" - sage: TODO + Return whether this valuation is a discrete valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 2) + sage: R. = QQ[] + sage: v = GaussValuation(R, v) + sage: w = v.augmentation(x, infinity) + sage: w.is_discrete_valuation() + False + """ return False class DiscreteValuation(DiscretePseudoValuation): r""" - sage: TODO + Abstract base class for discrete valuations. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 2) + sage: R. = QQ[] + sage: v = GaussValuation(R, v) + sage: w = v.augmentation(x, 1337); w # indirect doctest + [ Gauss valuation induced by 2-adic valuation, v(x) = 1337 ] + + TESTS:: + + sage: isinstance(w, DiscreteValuation) + True + sage: TestSuite(w).run() + """ def is_discrete_valuation(self): r""" @@ -264,13 +370,60 @@ def is_discrete_valuation(self): def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): r""" - sage: TODO + Return approximants on `K[x]` for the extensions of this valuation to + `L=K[x]/(G)`. + + If `G` is an irreducible polynomial, then this corresponds to the + extensions of this valuation to the completion of `L`. + + INPUT: + + - ``G`` -- a square-free polynomial defined over a univariate + polynomial ring over the :meth:`domain` of this valuation. + + - ``precision_cap`` -- a number, infinity, or ``None`` (default: + ``None``); the approximants are always determined such that they are in + one-to-one correspondance to the extensions of this valuation to `L` + and such that the approximants have the ramification index and + residual degree of these extensions. + If ``precision_cap`` is not ``None``, then the approximants are + determined such that they last key polynomial also has valuation at + least ``precision_cap``. + + - ``assume_squarefree`` -- a boolean (default: ``False``), whether or + not to assume that ``G`` is squarefree. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 2) + sage: R. = QQ[] + sage: v.mac_lane_approximants(x^2 + 1) + [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/2 ]] + sage: v.mac_lane_approximants(x^2 + 1, precision_cap=infinity) + [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/2, v(x^2 + 1) = +Infinity ]] + sage: v.mac_lane_approximants(x^2 + x + 1) + [[ Gauss valuation induced by 2-adic valuation, v(x^2 + x + 1) = +Infinity ]] + + Note that ``G`` does not need to be irreducible. Here, we detect a + factor of the polynomial `x + 1` and an approximate factor `x + 1` + (which is an approximation to `x - 1`):: + + sage: v.mac_lane_approximants(x^2 - 1) + [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = +Infinity ], + [ Gauss valuation induced by 2-adic valuation, v(x + 3) = 2 ]] + + However, it needs to be squarefree:: + + sage: v.mac_lane_approximants(x^2) + Traceback (most recent call last): + ... + ValueError: G must be squarefree TESTS: Some difficult cases provided by Mark van Hoeij:: - sage: from mac_lane import * # optional: standalone sage: k = GF(2) sage: K. = FunctionField(k) sage: R. = K[] @@ -309,6 +462,129 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + x) = 2/3 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + 2*x) = 2/3 ]] + Over a complete base field:: + + sage: k=Qp(2,10) + sage: v = pAdicValuation(k) + + sage: R.=k[] + sage: G = x + sage: v.mac_lane_approximants(G) + [Gauss valuation induced by 2-adic valuation] + sage: v.mac_lane_approximants(G, precision_cap = infinity) + [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = +Infinity ]] + + sage: G = x^2 + 1 + sage: v.mac_lane_approximants(G) # optional: integrated + [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x + (1 + O(2^10))) = 1/2 ]] + sage: v.mac_lane_approximants(G, precision_cap = infinity) # optional: integrated + [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x + (1 + O(2^10))) = 1/2, v((1 + O(2^10))*x^2 + (1 + O(2^10))) = +Infinity ]] + + sage: G = x^4 + 2*x^3 + 2*x^2 - 2*x + 2 + sage: v.mac_lane_approximants(G) + [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = 1/4 ]] + sage: v.mac_lane_approximants(G,infinity) + [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = 1/4, v((1 + O(2^10))*x^4 + (2 + O(2^11))*x^3 + (2 + O(2^11))*x^2 + (2 + 2^2 + 2^3 + 2^4 + 2^5 + 2^6 + 2^7 + 2^8 + 2^9 + 2^10 + O(2^11))*x + (2 + O(2^11))) = +Infinity ]] + + The factorization of primes in the Gaussian integers can be read off + the Mac Lane approximants:: + + sage: v0 = pAdicValuation(QQ, 2) + sage: R. = QQ[] + sage: G = x^2 + 1 + sage: v0.mac_lane_approximants(G) + [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/2 ]] + + sage: v0 = pAdicValuation(QQ, 3) + sage: v0.mac_lane_approximants(G) + [[ Gauss valuation induced by 3-adic valuation, v(x^2 + 1) = +Infinity ]] + + sage: v0 = pAdicValuation(QQ, 5) + sage: v0.mac_lane_approximants(G) + [[ Gauss valuation induced by 5-adic valuation, v(x + 2) = 1 ], + [ Gauss valuation induced by 5-adic valuation, v(x + 3) = 1 ]] + sage: v0.mac_lane_approximants(G, precision_cap = 10) # long time + [[ Gauss valuation induced by 5-adic valuation, v(x + 25670807) = 11 ], + [ Gauss valuation induced by 5-adic valuation, v(x + 23157318) = 11 ]] + + The same example over the 5-adic numbers. In the quadratic extension + `\QQ[x]/(x^2+1)`, 5 factors `-(x - 2)(x + 2)`, this behaviour can be + read off the Mac Lane approximants:: + + sage: k=Qp(5,4) + sage: v = pAdicValuation(k) + sage: R.=k[] + sage: G = x^2 + 1 + sage: v1,v2 = v.mac_lane_approximants(G); v1,v2 + ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + O(5^4))) = 1 ], + [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + O(5^4))) = 1 ]) + sage: w1, w2 = v.mac_lane_approximants(G,precision_cap=2); w1,w2 + ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + O(5^4))) = 2 ], + [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + 3*5 + O(5^4))) = 2 ]) + + Note how the latter give a better approximation to the factors of `x^2 + 1`:: + + sage: v1.phi() * v2.phi() - G # optional: integrated + (5 + O(5^4))*x + 5 + O(5^4) + sage: w1.phi() * w2.phi() - G # optional: integrated + (5^3 + O(5^4))*x + 5^3 + O(5^4) + + In this example, the process stops with a factorization of `x^2 + 1`:: + + sage: v.mac_lane_approximants(G, precision_cap=infinity) + [[ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + 2*5^2 + 5^3 + O(5^4))) = +Infinity ], + [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + 3*5 + 2*5^2 + 3*5^3 + O(5^4))) = +Infinity ]] + + This obviously cannot happen over the rationals where we only get an + approximate factorization:: + + sage: v = pAdicValuation(QQ, 5) + sage: R.=QQ[] + sage: G = x^2 + 1 + sage: v.mac_lane_approximants(G) + [[ Gauss valuation induced by 5-adic valuation, v(x + 2) = 1 ], [ Gauss valuation induced by 5-adic valuation, v(x + 3) = 1 ]] + sage: v.mac_lane_approximants(G, precision_cap=5) + [[ Gauss valuation induced by 5-adic valuation, v(x + 2057) = 5 ], + [ Gauss valuation induced by 5-adic valuation, v(x + 1068) = 6 ]] + + Initial versions ran into problems with the trivial residue field + extensions in this case:: + + sage: K = Qp(3,20) + sage: R. = K[] + + sage: alpha = T^3/4 + sage: G = 3^3*T^3*(alpha^4 - alpha)^2 - (4*alpha^3 - 1)^3 + sage: G = G/G.leading_coefficient() + sage: pAdicValuation(K).mac_lane_approximants(G) # long time + [[ Gauss valuation induced by 3-adic valuation, v((1 + O(3^20))*T + (2 + O(3^20))) = 1/9, v((1 + O(3^20))*T^9 + (2*3 + 2*3^2 + O(3^21))*T^8 + (3 + 3^5 + O(3^21))*T^7 + (2*3 + 2*3^2 + 3^3 + 2*3^4 + 2*3^5 + 3^6 + O(3^21))*T^6 + (2*3 + 2*3^2 + 3^4 + 3^6 + 2*3^7 + O(3^21))*T^5 + (3 + 3^2 + 3^3 + 2*3^6 + 2*3^7 + 3^8 + O(3^21))*T^4 + (2*3 + 2*3^2 + 3^3 + 2*3^5 + 2*3^6 + 2*3^7 + 2*3^8 + O(3^21))*T^3 + (2*3 + 2*3^2 + 3^3 + 2*3^4 + 3^5 + 2*3^6 + 2*3^7 + 2*3^8 + O(3^21))*T^2 + (3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^7 + 3^8 + O(3^21))*T + (2 + 2*3 + 2*3^2 + 2*3^4 + 2*3^5 + 3^7 + O(3^20))) = 55/27 ]] + + A similar example:: + + sage: R. = QQ[] + sage: v = pAdicValuation(QQ, 3) + sage: G = (x^3 + 3)^3 - 81 + sage: v.mac_lane_approximants(G) # optional: integrated + [[ Gauss valuation induced by 3-adic valuation, v(x) = 1/3, v(x^3 + 3*x + 3) = 13/9 ]] + + Another problematic case:: + + sage: R. = QQ[] + sage: Delta = x^12 + 20*x^11 + 154*x^10 + 664*x^9 + 1873*x^8 + 3808*x^7 + 5980*x^6 + 7560*x^5 + 7799*x^4 + 6508*x^3 + 4290*x^2 + 2224*x + 887 + sage: K. = NumberField(x^6 + 108) + sage: K.is_galois() + True + sage: vK = pAdicValuation(QQ, 2).extension(K) + sage: vK(2) + 1 + sage: vK(theta) + 1/3 + sage: G=Delta.change_ring(K) + sage: V=vK.mac_lane_approximants(G); V + [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + theta^4 + theta^3 + 1) = 5/3 ], + [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*theta^4 + theta^3 - 27*theta + 1) = 5/3 ], + [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + 3/2*theta^4 + theta^3 - 27*theta + 1) = 5/3 ]] + """ R = G.parent() if R.base_ring() is not self.domain(): @@ -323,8 +599,12 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): while True: ef = [ v.E()*v.F() for v in leaves] if sum(ef) == G.degree(): - if precision_cap is None or all([v(v.phi())>precision_cap for v in leaves]): - return leaves + # the ramification indexes and residual degrees are final + if precision_cap is None or all([v(v.phi()) >= precision_cap for v in leaves]): + # the precision to which we have determined the approximants is sufficient + if not [v for v in leaves if [w for w in leaves if w != v and w <= v]]: + # the approximants do not approximate each other + break expandables = [] new_leaves = [] @@ -335,15 +615,75 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): expandables.append(v) leaves = new_leaves - if not expandables: - return leaves + assert expandables for v in expandables: leaves.extend(v.mac_lane_step(G)) + return leaves + def mac_lane_approximant(self, G, valuation, approximants = None): r""" - sage: TODO + Return the approximant from :meth:`mac_lane_approximants` for ``G`` + which is approximated by or approximates ``valuation``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 2) + sage: R. = QQ[] + sage: G = x^2 + 1 + + We can select an approximant by approximating it:: + + sage: w = GaussValuation(R, v).augmentation(x + 1, 1/2) + sage: v.mac_lane_approximant(G, w) + [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/2 ] + + As long as this is the only matching approximant, the approximation can + be very coarse:: + + sage: w = GaussValuation(R, v) + sage: v.mac_lane_approximant(G, w) + [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/2 ] + + Or it can be very specific:: + + sage: w = GaussValuation(R, v).augmentation(x + 1, 1/2).augmentation(G, infinity) + sage: v.mac_lane_approximant(G, w) + [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/2 ] + + But it must be an approximation of an approximant:: + + sage: w = GaussValuation(R, v).augmentation(x, 1/2) + sage: v.mac_lane_approximant(G, w) + Traceback (most recent call last): + ... + ValueError: The valuation [ Gauss valuation induced by 2-adic valuation, v(x) = 1/2 ] is not an approximant for a valuation which extends 2-adic valuation with respect to x^2 + 1 since the valuation of x^2 + 1 does not increase in every step + + The ``valuation`` must single out one approximant:: + + sage: G = x^2 - 1 + sage: w = GaussValuation(R, v) + sage: v.mac_lane_approximant(G, w) + Traceback (most recent call last): + ... + ValueError: The valuation Gauss valuation induced by 2-adic valuation does not approximate a unique extension of 2-adic valuation with respect to x^2 - 1 + + sage: w = GaussValuation(R, v).augmentation(x + 1, 1) + sage: v.mac_lane_approximant(G, w) + Traceback (most recent call last): + ... + ValueError: The valuation [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1 ] does not approximate a unique extension of 2-adic valuation with respect to x^2 - 1 + + sage: w = GaussValuation(R, v).augmentation(x + 1, 2) + sage: v.mac_lane_approximant(G, w) + [ Gauss valuation induced by 2-adic valuation, v(x + 1) = +Infinity ] + + sage: w = GaussValuation(R, v).augmentation(x + 3, 2) + sage: v.mac_lane_approximant(G, w) + [ Gauss valuation induced by 2-adic valuation, v(x + 3) = 2 ] + """ if valuation.constant_valuation() != self: raise ValueError @@ -356,7 +696,7 @@ def mac_lane_approximant(self, G, valuation, approximants = None): v = valuation while not v.is_gauss_valuation(): if v(G_integral) <= v._base_valuation(G_integral): - raise ValueError("The valuation %r is not an approximant for a valuation on %r since the valuation of %r does not increase in every step"%(valuation, domain, G_integral)) + raise ValueError("The valuation %r is not an approximant for a valuation which extends %r with respect to %r since the valuation of %r does not increase in every step"%(valuation, self, G, G_integral)) v = v._base_valuation if approximants is None: @@ -364,15 +704,16 @@ def mac_lane_approximant(self, G, valuation, approximants = None): assert all(approximant.domain() is valuation.domain() for approximant in approximants) - greater_approximants = [w for w in approximants if w <= valuation] + greater_approximants = [w for w in approximants if w >= valuation] if len(greater_approximants) > 1: - raise ValueError("valuation %r does not uniquely describe an extension of %r to %r"%(valuation, self, domain)) + raise ValueError("The valuation %r does not approximate a unique extension of %r with respect to %r"%(valuation, self, G)) if len(greater_approximants) == 1: return greater_approximants[0] - smaller_approximants = [w for w in approximants if w >= valuation] - assert len(smaller_approximants) <= 1 + smaller_approximants = [w for w in approximants if w <= valuation] + if len(smaller_approximants) > 1: + raise ValueError("The valuation %r is not approximated by a unique extension of %r with respect to %r"%(valuation, valuation.constant_valuation(), G)) if len(smaller_approximants) == 0: - raise ValueError("valuation %r does not describe an extension of %r with respect to %r"%(valuation, valuation.constant_valuation(), G)) + raise ValueError("The valuation %r is not related to an extension of %r with respect to %r"%(valuation, valuation.constant_valuation(), G)) assert len(smaller_approximants) == 1 return smaller_approximants[0] diff --git a/valuation_space.py b/valuation_space.py index ef9b4416179..6a34d6d05db 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -881,3 +881,40 @@ def _test_residue_field(self, **options): c = self.residue_field().characteristic() if c != 0: tester.assertGreater(self(c), 0) + + def _test_ge(self, **options): + r""" + Check the correctness of the ``>=`` operator. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: v._test_ge() + + """ + tester = self._tester(**options) + + from trivial_valuation import TrivialPseudoValuation, TrivialValuation + tester.assertGreaterEqual(self, self) + tester.assertGreaterEqual(self, TrivialValuation(self.domain())) + tester.assertLessEqual(self, TrivialPseudoValuation(self.domain())) + + def _test_le(self, **options): + r""" + Check the correctness of the ``<=`` operator. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: v._test_ge() + + """ + tester = self._tester(**options) + + from trivial_valuation import TrivialPseudoValuation, TrivialValuation + tester.assertGreaterEqual(self, self) + tester.assertLessEqual(TrivialValuation(self.domain()), self) + tester.assertGreaterEqual(TrivialPseudoValuation(self.domain()), self) + From 3c2a93eb2cfa7234a8ccdce7d1ea5237f6ce7635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 31 Oct 2016 22:14:09 -0500 Subject: [PATCH 049/740] Implement trivial comparisons and streamline residue_ring() and residue_field() --- augmented_valuation.py | 83 ++++++++++++++----------------------- developing_valuation.py | 16 ------- function_field_valuation.py | 24 ++++++++++- gauss_valuation.py | 13 +++--- limit_valuation.py | 23 +++++++++- valuation.py | 18 ++++++++ 6 files changed, 99 insertions(+), 78 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index eacbe061f0d..caef572d310 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -521,12 +521,12 @@ def reduce(self, f): raise NotImplementedError return self.residue_ring()(self.coefficients(f).next())(self.residue_field_generator()) - if self._mu is infinity: + if self._mu == infinity: # if this is an infinite valuation, then we can simply drop all but the # constant term constant_term = self.coefficients(f).next() constant_term_reduced = self._base_valuation.reduce(constant_term) - return constant_term_reduced(self.residue_field().gen()) + return constant_term_reduced(self.residue_ring().gen()) CV = zip(self.coefficients(f), self.valuations(f)) # rewrite as sum of f_i phi^{i tau}, i.e., drop most coefficients @@ -568,14 +568,14 @@ def lift(self, F): sage: S. = R[] sage: v = GaussValuation(S) sage: y = v.residue_ring().gen() - sage: u0 = v.residue_field().gen() + sage: u0 = v.residue_ring().base().gen() sage: v.lift(y) (1 + O(2^10))*x sage: w = v.extension(x^2 + x + u, 1/2) sage: r = w.residue_ring() sage: y = r.gen() - sage: u1 = w.residue_field().gen() + sage: u1 = w.residue_ring().base().gen() sage: w.lift(r.one()) 1 + O(2^10) sage: w.lift(r.zero()) @@ -592,7 +592,7 @@ def lift(self, F): sage: w = w.extension((x^2 + x + u)^2 + 2, 5/3) sage: r = w.residue_ring() sage: y = r.gen() - sage: u2 = w.residue_field().gen() + sage: u2 = w.residue_ring().base().gen() sage: w.reduce(w.lift(y)) == y True sage: w.reduce(w.lift(r.one())) == 1 @@ -606,7 +606,7 @@ def lift(self, F): sage: v = v.extension(x^2 + x + u, 1) sage: v = v.extension((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) - sage: u = v.residue_field().gen() + sage: u = v.residue_ring().base().gen() sage: F = v.residue_ring()(u); F u2 sage: f = v.lift(F); f @@ -615,12 +615,16 @@ def lift(self, F): True """ - if F.parent() is self.residue_ring().base(): - F = self.residue_ring()(F) - if F.parent() is not self.residue_ring(): - raise ValueError("F must be an element of the residue ring of the valuation") + F = self.residue_ring().coerce(F) + if not self.domain().base_ring().is_field(): raise NotImplementedError("only implemented for polynomial rings over fields") + if self._mu == infinity: + if self.psi().degree() == 1: + return self._base_valuation.lift(F) + else: + return self._base_valuation.lift(F.polynomial(self._base_valuation.residue_ring().variable_name())) + if F.is_constant(): if F.is_zero(): return self.domain().zero() @@ -642,9 +646,9 @@ def lift(self, F): if self.phi().degree() == 1: # this is a classical valuation of a rational point, of the # form [trivial, v(x + 1) = 1] - assert self.domain().base_ring() is self.residue_field() + assert self.domain().base_ring() is self.residue_ring().base() return self.domain()(F) - if self.phi().change_variable_name(self.residue_field().polynomial().variable_name()) == self.residue_field().polynomial(): + if self.phi().change_variable_name(self.residue_ring().base().polynomial().variable_name()) == self.residue_ring().base().polynomial(): # this is a classical valuation of a point, of the from # [trivial, v(x^2 + 1) = 1] if hasattr(F, 'polynomial'): @@ -716,7 +720,7 @@ def lift_to_key(self, F): sage: v = v.extension(x^2 + x + u, 1) sage: v = v.extension((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) - sage: u = v.residue_field().gen() + sage: u = v.residue_ring().base().gen() sage: y = v.residue_ring().gen() sage: f = v.lift_to_key(y^3+y+u) sage: f.degree() @@ -729,6 +733,9 @@ def lift_to_key(self, F): raise ValueError("F must be an element of the residue ring of the valuation") if not self.domain().base_ring().is_field(): raise NotImplementedError("only implemented for polynomial rings over fields") + if self._base_valuation.is_gauss_valuation() and self._mu == infinity: + raise TypeError("there are no keys over this valuation") + if F.is_constant(): raise ValueError("F must not be constant") if not F.is_monic(): @@ -816,55 +823,25 @@ def psi(self): return F @cached_method - def residue_field(self, generator=None): - """ - Return the residue field of this valuation. - - INPUT: - - - ``generator`` -- a string or ``None`` (default: ``None``), the name - of the generator of the residue field extension, if ``None`` a name - is automatically chosen - - OUTPUT: + def residue_ring(self): + generator = 'u' + str(len(self._augmentations()) - 1) - a relative extension of the residue field of the base valuation - - EXAMPLES:: - - sage: R. = Qq(4,5) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: w = v.extension(x^2 + x + u, 1/2) - sage: w.residue_field() - Univariate Quotient Polynomial Ring in u1 over Finite Field in u0 of size 2^2 with modulus u1^2 + u1 + u0 - sage: w = w.extension((x^2 + x + u)^2 + 2, 5/3) - sage: w.residue_field() - Univariate Quotient Polynomial Ring in u1 over Finite Field in u0 of size 2^2 with modulus u1^2 + u1 + u0 - - """ - if generator is None: - level = 0 - v = self - while isinstance(v, AugmentedValuation_generic): - level += 1 - v = v._base_valuation - generator = 'u%s'%level - if self.psi().degree() == 1: - ret = self._base_valuation.constant_valuation().residue_field() - #ret.gen = "use augmented_valuation.residue_field_generator() instead" # temporary hack to find bugs when .gen() is used - .residue_field_generator() instead - return ret + base = self._base_valuation.residue_ring().base() + if self.psi().degree() > 1: + base = base.extension(self.psi(), names=generator) + if self._mu == infinity: + return base else: - return self._base_valuation.constant_valuation().residue_field().extension(self.psi(), names=generator) + return base[self.domain().variable_name()] @cached_method def residue_field_generator(self): if self.psi().degree() == 1: ret = -self.psi()[0] else: - ret = self.residue_field().gen() + ret = self.residue_ring().base().gen() - assert ret.parent() is self.residue_field() + assert ret.parent() is self.residue_ring().base() assert self.psi()(ret).is_zero() return ret diff --git a/developing_valuation.py b/developing_valuation.py index 83679c4681d..5623ddb9817 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -984,22 +984,6 @@ def _repr_(self): """ return "`%s`-adic valuation of %s"%(self._phi, self.domain()) - def residue_ring(self): - """ - Return the residue ring of this valuation, i.e., a polynomial ring over - the :meth:`residue_field` - - EXAMPLES:: - - sage: R = Qp(2,5) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: v.residue_ring() - Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) - - """ - return self.domain().change_ring(self.residue_field()) - def _make_monic_integral(self, G): if G.is_monic() and self(G) >= 0: return G diff --git a/function_field_valuation.py b/function_field_valuation.py index db33c58ffb4..7b30d4c1f92 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -434,6 +434,28 @@ def _test_classical_residue_field(self, **options): tester.assertTrue(self.domain().constant_field().is_subring(self.residue_field())) + def _ge_(self, other): + r""" + Return whether ``self`` is greater or equal to ``other`` everywhere. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, x^2 + 1) + sage: w = FunctionFieldValuation(K, x) + sage: v >= w + False + sage: w >= v + False + + """ + if other.is_trivial(): + return other.is_discrete_valuation() + if isinstance(other, ClassicalFunctionFieldValuation_base): + return self == other + super(ClassicalFunctionFieldValuation_base, self)._ge_(other) + class InducedFunctionFieldValuation_base(FunctionFieldValuation_base): r""" Base class for function field valuation induced by a valuation on the @@ -792,7 +814,7 @@ def residue_ring(self): Rational Field """ - return self._base_valuation.residue_field() + return self._base_valuation.residue_ring().base() class FunctionFieldFromLimitValuation(FiniteExtensionFromLimitValuation): r""" diff --git a/gauss_valuation.py b/gauss_valuation.py index 9dc4ad6998b..0ce522ca746 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -381,18 +381,18 @@ def reduce(self, f): return f.map_coefficients(lambda c:self._base_valuation.reduce(c), self.constant_valuation().residue_field()) - def lift(self, reduction): + def lift(self, F): """ - Return a lift of ``reduction``. + Return a lift of ``F``. INPUT:: - - ``reduction`` -- a polynomial over the :meth:`residue_ring` of this valuation + - ``F`` -- a polynomial over the :meth:`residue_ring` of this valuation OUTPUT: a (possibly non-monic) polynomial in the domain of this valuation which - reduces to ``reduction`` + reduces to ``F`` EXAMPLES:: @@ -413,10 +413,9 @@ def lift(self, reduction): :meth:`reduce` """ - if reduction.parent() is not self.residue_ring(): - raise ValueError("f must be in the residue ring of this valuation") + F = self.residue_ring().coerce(F) - return reduction.map_coefficients(lambda c:self._base_valuation.lift(c), self._base_valuation.domain()) + return F.map_coefficients(lambda c:self._base_valuation.lift(c), self._base_valuation.domain()) def lift_to_key(self, F): """ diff --git a/limit_valuation.py b/limit_valuation.py index a6907c9c35b..e255f7b91e5 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -535,6 +535,27 @@ def residue_ring(self): assert(is_PolynomialRing(R)) return R.base_ring() + def _ge_(self, other): + r""" + Return whether this valuation is greater or equal than ``other`` + everywhere. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: w = v.extension(L) + sage: w >= w + True + + """ + if other.is_trivial(): + return other.is_discrete_valuation() + return super(MacLaneLimitValuation, self)._ge_(other) + class FiniteExtensionFromInfiniteValuation(DiscreteValuation): r""" A valuation on a quotient of the form `L=K[x]/(G)` with an irreducible `G` @@ -581,7 +602,7 @@ def __init__(self, parent, base_valuation): True """ - DiscretePseudoValuation.__init__(self, parent) + DiscreteValuation.__init__(self, parent) self._base_valuation = base_valuation def _eq_(self, other): diff --git a/valuation.py b/valuation.py index df81cfd946d..edcbcc0ad11 100644 --- a/valuation.py +++ b/valuation.py @@ -717,3 +717,21 @@ def mac_lane_approximant(self, G, valuation, approximants = None): raise ValueError("The valuation %r is not related to an extension of %r with respect to %r"%(valuation, valuation.constant_valuation(), G)) assert len(smaller_approximants) == 1 return smaller_approximants[0] + + def _ge_(self, other): + r""" + Return whether this valuation is greater than or equal to ``other`` + pointwise. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = TrivialValuation(QQ) + sage: w = pAdicValuation(QQ, 2) + sage: v >= w + False + + """ + if other.is_trivial(): + return other.is_discrete_valuation() + return super(DiscreteValuation, self)._ge_(other) From 220b469b9ae34bc9fcbc26a4016dc435fb6f5d8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 31 Oct 2016 22:35:14 -0500 Subject: [PATCH 050/740] fix doctest coverage --- limit_valuation.py | 57 +++++++++++++++++++++++++++++++++++++++++++++- valuation_space.py | 2 +- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/limit_valuation.py b/limit_valuation.py index e255f7b91e5..d0a556e98c1 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -88,10 +88,65 @@ from sage.structure.factory import UniqueFactory class LimitValuationFactory(UniqueFactory): + r""" + Return a limit valuation which sends the polynomial ``G`` to infinity and + is greater than or equal than ``base_valuation``. + + INPUT: + + - ``base_valuation`` -- a discrete (pseudo-)valuation on a polynomial ring + which can be augmented (possibly only in the limit) to a pseudo-valuation + that sends ``G`` to infinity. + + - ``G`` -- a squarefree polynomial in the domain of ``base_valuation``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = LimitValuation(v, x) + sage: w(x) + +Infinity + + """ def create_key(self, base_valuation, G): + r""" + Create a key from the parameters of this valuation. + + EXAMPLES: + + Note that this does not normalize ``base_valuation`` in anyway. It is + easily possible to create the same limit in two different ways:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = LimitValuation(v, x) # indirect doctest + sage: v = v.augmentation(x, infinity) + sage: u = LimitValuation(v, x) + sage: u == w + False + + The point here is that this is not meant to be invoked from user code. + But mostly from other factories which have made sure that the + parameters are normalized already. + + """ return base_valuation, G def create_object(self, version, key): + r""" + Create an object from ``key``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = LimitValuation(v, x^2 + 1) # indirect doctest + + """ base_valuation, G = key from valuation_space import DiscretePseudoValuationSpace parent = DiscretePseudoValuationSpace(base_valuation.domain()) @@ -461,7 +516,7 @@ def _improve_approximation_for_call(self, f): """ from sage.rings.all import infinity - if self._approximation._mu == infinity: + if self._approximation(self._approximation.phi()) == infinity: # an infinite valuation can not be improved further return diff --git a/valuation_space.py b/valuation_space.py index 6a34d6d05db..e739cc96e8b 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -908,7 +908,7 @@ def _test_le(self, **options): sage: from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) - sage: v._test_ge() + sage: v._test_le() """ tester = self._tester(**options) From 0e636844f77e70b027b5b28a670d27dd19c3f4ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 31 Oct 2016 22:37:54 -0500 Subject: [PATCH 051/740] mark slow tests --- function_field_valuation.py | 4 ++-- limit_valuation.py | 8 ++++---- valuation.py | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/function_field_valuation.py b/function_field_valuation.py index 7b30d4c1f92..c1d2c0f7c06 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -466,7 +466,7 @@ class InducedFunctionFieldValuation_base(FunctionFieldValuation_base): sage: from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x^2 + 1) # indirect doctest - sage: TestSuite(v).run() + sage: TestSuite(v).run() # long time """ def __init__(self, parent, base_valuation): @@ -835,7 +835,7 @@ class FunctionFieldFromLimitValuation(FiniteExtensionFromLimitValuation): sage: isinstance(w, FunctionFieldFromLimitValuation) True - sage: TestSuite(w).run() + sage: TestSuite(w).run() # long time """ def _to_base_domain(self, f): diff --git a/limit_valuation.py b/limit_valuation.py index d0a556e98c1..263ee4ceada 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -183,7 +183,7 @@ class LimitValuation_generic(DiscretePseudoValuation): sage: isinstance(w._base_valuation, LimitValuation_generic) True - sage: TestSuite(w._base_valuation).run() + sage: TestSuite(w._base_valuation).run() # long time """ def __init__(self, parent, approximation): @@ -638,7 +638,7 @@ class FiniteExtensionFromInfiniteValuation(DiscreteValuation): TESTS:: - sage: TestSuite(w).run() + sage: TestSuite(w).run() # long time """ def __init__(self, parent, base_valuation): @@ -847,8 +847,8 @@ class FiniteExtensionFromLimitValuation(FiniteExtensionFromInfiniteValuation): TESTS:: - sage: TestSuite(w[0]).run() - sage: TestSuite(w[1]).run() + sage: TestSuite(w[0]).run() # long time + sage: TestSuite(w[1]).run() # long time """ def __init__(self, parent, approximant, G, approximants): diff --git a/valuation.py b/valuation.py index edcbcc0ad11..82015353c99 100644 --- a/valuation.py +++ b/valuation.py @@ -518,7 +518,7 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): sage: v1,v2 = v.mac_lane_approximants(G); v1,v2 ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + O(5^4))) = 1 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + O(5^4))) = 1 ]) - sage: w1, w2 = v.mac_lane_approximants(G,precision_cap=2); w1,w2 + sage: w1, w2 = v.mac_lane_approximants(G,precision_cap=2); w1,w2 # long time ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + O(5^4))) = 2 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + 3*5 + O(5^4))) = 2 ]) @@ -531,7 +531,7 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): In this example, the process stops with a factorization of `x^2 + 1`:: - sage: v.mac_lane_approximants(G, precision_cap=infinity) + sage: v.mac_lane_approximants(G, precision_cap=infinity) # long time [[ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + 2*5^2 + 5^3 + O(5^4))) = +Infinity ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + 3*5 + 2*5^2 + 3*5^3 + O(5^4))) = +Infinity ]] @@ -580,7 +580,7 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): sage: vK(theta) 1/3 sage: G=Delta.change_ring(K) - sage: V=vK.mac_lane_approximants(G); V + sage: V=vK.mac_lane_approximants(G); V # long time [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + theta^4 + theta^3 + 1) = 5/3 ], [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*theta^4 + theta^3 - 27*theta + 1) = 5/3 ], [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + 3/2*theta^4 + theta^3 - 27*theta + 1) = 5/3 ]] From 0bc5b80a5733683c1d17606cae1c9124f7e6f03d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 31 Oct 2016 22:39:15 -0500 Subject: [PATCH 052/740] fix long running doctests --- valuation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/valuation.py b/valuation.py index 82015353c99..cd6e2d98748 100644 --- a/valuation.py +++ b/valuation.py @@ -504,8 +504,8 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): [[ Gauss valuation induced by 5-adic valuation, v(x + 2) = 1 ], [ Gauss valuation induced by 5-adic valuation, v(x + 3) = 1 ]] sage: v0.mac_lane_approximants(G, precision_cap = 10) # long time - [[ Gauss valuation induced by 5-adic valuation, v(x + 25670807) = 11 ], - [ Gauss valuation induced by 5-adic valuation, v(x + 23157318) = 11 ]] + [[ Gauss valuation induced by 5-adic valuation, v(x + 6139557) = 10 ], + [ Gauss valuation induced by 5-adic valuation, v(x + 3626068) = 10 ]] The same example over the 5-adic numbers. In the quadratic extension `\QQ[x]/(x^2+1)`, 5 factors `-(x - 2)(x + 2)`, this behaviour can be @@ -556,7 +556,7 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): sage: alpha = T^3/4 sage: G = 3^3*T^3*(alpha^4 - alpha)^2 - (4*alpha^3 - 1)^3 sage: G = G/G.leading_coefficient() - sage: pAdicValuation(K).mac_lane_approximants(G) # long time + sage: pAdicValuation(K).mac_lane_approximants(G) # optional: integrated, long time [[ Gauss valuation induced by 3-adic valuation, v((1 + O(3^20))*T + (2 + O(3^20))) = 1/9, v((1 + O(3^20))*T^9 + (2*3 + 2*3^2 + O(3^21))*T^8 + (3 + 3^5 + O(3^21))*T^7 + (2*3 + 2*3^2 + 3^3 + 2*3^4 + 2*3^5 + 3^6 + O(3^21))*T^6 + (2*3 + 2*3^2 + 3^4 + 3^6 + 2*3^7 + O(3^21))*T^5 + (3 + 3^2 + 3^3 + 2*3^6 + 2*3^7 + 3^8 + O(3^21))*T^4 + (2*3 + 2*3^2 + 3^3 + 2*3^5 + 2*3^6 + 2*3^7 + 2*3^8 + O(3^21))*T^3 + (2*3 + 2*3^2 + 3^3 + 2*3^4 + 3^5 + 2*3^6 + 2*3^7 + 2*3^8 + O(3^21))*T^2 + (3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^7 + 3^8 + O(3^21))*T + (2 + 2*3 + 2*3^2 + 2*3^4 + 2*3^5 + 3^7 + O(3^20))) = 55/27 ]] A similar example:: From e996594c606556abe106ea0b13843a3c85c2bb93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 31 Oct 2016 23:33:27 -0500 Subject: [PATCH 053/740] fix existing p-adic doctests --- __init__.py | 1 + padic_valuation.py | 171 ++++++++++++++++++++++++++++----------------- 2 files changed, 109 insertions(+), 63 deletions(-) diff --git a/__init__.py b/__init__.py index 133849e5761..74f9c6eeff5 100644 --- a/__init__.py +++ b/__init__.py @@ -15,6 +15,7 @@ from .augmented_valuation import AugmentedValuation_generic, InfiniteAugmentedValuation from .gauss_valuation import GaussValuation_generic from .valuation import DiscretePseudoValuation, DiscreteValuation, InfiniteDiscretePseudoValuation +from .padic_valuation import pAdicValuation_base, pAdicValuation_number_field, pAdicValuation_int, pAdicValuation_padic # ================= # MONKEY PATCH SAGE diff --git a/padic_valuation.py b/padic_valuation.py index 46257d0af13..40692cd2a0c 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -1,8 +1,5 @@ """ -The discrete valuation of a `p`-adic ring - -This file makes it possible to use `p`-adics, integers, and rationals in the -general discrete valuation framework. +`p`-adic valuations on number fields and their subrings and completions. AUTHORS: @@ -10,7 +7,7 @@ """ #***************************************************************************** -# Copyright (C) 2013 Julian Rueth +# Copyright (C) 2013-2016 Julian Rueth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -91,7 +88,7 @@ class PadicValuationFactory(UniqueFactory): sage: pAdicValuation(GaussianIntegers(), 5) Traceback (most recent call last): ... - ValueError: 5 does not single out a unique extension of 5-adic valuation to Number Field in I with defining polynomial x^2 + 1 + ValueError: The valuation Gauss valuation induced by 5-adic valuation does not approximate a unique extension of 5-adic valuation with respect to x^2 + 1 When ``R`` is an absolute or relative number field, or a subring thereof, ``prime`` can also be specified by providing a valuation on the base ring @@ -105,7 +102,7 @@ class PadicValuationFactory(UniqueFactory): sage: pAdicValuation(GaussianIntegers(), pAdicValuation(ZZ, 5)) Traceback (most recent call last): ... - ValueError: 5-adic valuation does not single out a unique extension of 5-adic valuation to Number Field in I with defining polynomial x^2 + 1 + ValueError: The valuation Gauss valuation induced by 5-adic valuation does not approximate a unique extension of 5-adic valuation with respect to x^2 + 1 For a number field which is of the form `K[x]/(G)`, you can specify a valuation by providing a discrete pseudo-valuation on `K[x]` which sends @@ -113,15 +110,15 @@ class PadicValuationFactory(UniqueFactory): valuation we care about in the above example:: sage: R. = QQ[] - sage: v = pAdicValuation(GaussianIntegers(), GaussValuation(R, pAdicValuation(QQ, 5)).extension(x + 2, infinity)) - sage: w = pAdicValuation(GaussianIntegers(), GaussValuation(R, pAdicValuation(QQ, 5)).extension(x + 1/2, infinity)) + sage: v = pAdicValuation(GaussianIntegers(), GaussValuation(R, pAdicValuation(QQ, 5)).augmentation(x + 2, infinity)) + sage: w = pAdicValuation(GaussianIntegers(), GaussValuation(R, pAdicValuation(QQ, 5)).augmentation(x + 1/2, infinity)) sage: v == w False Note that you get the same valuation, even if you write down the pseudo-valuation differently:: - sage: ww = pAdicValuation(GaussianIntegers(), GaussValuation(R, pAdicValuation(QQ, 5)).extension(x + 3, infinity)) + sage: ww = pAdicValuation(GaussianIntegers(), GaussValuation(R, pAdicValuation(QQ, 5)).augmentation(x + 3, infinity)) sage: w is ww True @@ -131,51 +128,17 @@ class PadicValuationFactory(UniqueFactory): completion, i.e., if it is not possible to write down one of the factors within the number field:: - sage: TODO + sage: v = GaussValuation(R, pAdicValuation(QQ, 5)).augmentation(x + 3, 1) + sage: pAdicValuation(GaussianIntegers().fraction_field(), v) + [ 5-adic valuation, v(x + 3) = 1 ]-adic valuation Finally, ``prime`` can also be a fractional ideal of a number field if it singles out an extension of a `p`-adic valuation of the base field:: - sage: TODO - - TESTS: - - Check that we produce different valuations for distinguishable number - fields:: - - sage: from mac_lane import * # optional: standalone - sage: R. = QQ[] - sage: L. = QQ.extension(x^2 - 2) - sage: R. = L[] - sage: N. = L.extension(y^2 - s) - sage: N = N.absolute_field('u') - - sage: M. = QQ.extension(x^4 - 2) - sage: M is N - False - sage: M == N - True - - sage: pAdicValuation(QQ, 2).extension(M) is pAdicValuation(QQ, 2).extension(N) - False - - A case that took very long due to the hashing of number fields:: - - sage: R. = QQ[] - sage: Delta1= x^12 + 20*x^11 + 154*x^10 + 664*x^9 + 1873*x^8 + 3808*x^7 + 5980*x^6 + 7560*x^5 + 7799*x^4 + 6508*x^3 + 4290*x^2 + 2224*x + 887 - sage: K.=NumberField(x^9-2) - sage: vK=pAdicValuation(QQ, 2).extension(K) - sage: G=Delta1.change_ring(K) - sage: v0=GaussValuation(G.parent(), vK) - sage: V=v0.mac_lane_step(G) - sage: while len(V) == 1: V=V[0].mac_lane_step(G) - sage: v=V[0] - sage: while v(v.phi()) < 50: v=v.mac_lane_step(G)[0] - sage: F=v.phi() - sage: L.=K.extension(F) - sage: vK.mac_lane_approximants(F) - [[ Gauss valuation induced by theta-adic valuation, v(x + 1) = 9/4 ]] - sage: vL = vK.extension(L) + sage: R = GaussianIntegers() + sage: I = R.gen(1) + sage: pAdicValuation(R, I + 1) + 2-adic valuation """ def create_key_and_extra_args(self, R, prime=None): @@ -208,6 +171,17 @@ def create_key_and_extra_args(self, R, prime=None): raise NotImplementedError("p-adic valuations not implemented for %r"%(R,)) def create_key_for_integers(self, R, prime): + r""" + Create a unique key identifying the valuation of ``R`` with respect to + ``prime``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(QQ, 2) # indirect doctest + 2-adic valuation + + """ from sage.rings.all import ZZ if prime is None: raise ValueError("prime must be specified for this ring") @@ -219,6 +193,17 @@ def create_key_for_integers(self, R, prime): return R, prime def create_key_for_local_ring(self, R, prime): + r""" + Create a unique key identifying the valuation of ``R`` with respect to + ``prime``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(Qp(2)) # indirect doctest + 2-adic valuation + + """ # We do not care much about the value of prime since there is only one # reasonable p-adic valuation here if prime is not None and R(prime).valuation() <= 0: @@ -227,6 +212,17 @@ def create_key_for_local_ring(self, R, prime): return (R,) def create_key_and_extra_args_for_number_field(self, R, prime): + r""" + Create a unique key identifying the valuation of ``R`` with respect to + ``prime``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(GaussianIntegers(), 2) # indirect doctest + 2-adic valuation + + """ from sage.rings.number_field.number_field_ideal import NumberFieldFractionalIdeal # To make our lives easier, we move prime to the fraction field of R # which we denote in the following as L = K[x]/(G), do all computations @@ -247,7 +243,21 @@ def create_key_and_extra_args_for_number_field(self, R, prime): def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime): r""" - prime is only used to provide more meaningful error messages + Create a unique key identifying the valuation of ``R`` with respect to + ``v``. + + .. NOTE:: + + ``prime``, the original parameter that was passed to + :meth:`create_key_and_extra_args``, is only used to provide more + meaningful error messages + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(GaussianIntegers(), pAdicValuation(ZZ, 2)) # indirect doctest + 2-adic valuation + """ # To make our lives easier, we move prime to the fraction field of R # which we denote in the following as L = K[x]/(G), do all computations @@ -279,7 +289,21 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime) def create_key_and_extra_args_for_number_field_from_ideal(self, R, I, prime): r""" - prime only for error messages + Create a unique key identifying the valuation of ``R`` with respect to + ``I``. + + .. NOTE:: + + ``prime``, the original parameter that was passed to + :meth:`create_key_and_extra_args``, is only used to provide more + meaningful error messages + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(GaussianIntegers(), GaussianIntegers().ideal(2)) # indirect doctest + 2-adic valuation + """ # To make our lives easier, we move prime to the fraction field of R # which we denote in the following as L = K[x]/(G), do all computations @@ -292,11 +316,15 @@ def create_key_and_extra_args_for_number_field_from_ideal(self, R, I, prime): # description of v. We consider all extensions of vK to L and select # the one approximated by v. # Of course, this only works if I comes from a single prime downstairs. - vK = pAdicValuation(K, I.relative_norm()) + p = I.relative_norm() + F = p.factor() + if len(F) != 1: + raise ValueError("%r does not lie over a single prime of %r"%(I, K)) + vK = pAdicValuation(K, F[0][0]) candidates = vK.mac_lane_approximants(G) - candidates_for_I = [c for c in candidates if all(c(g) > 0 for g in I.gens())] - assert(len(candidates_for_I > 0)) # This should not be possible, unless I contains a unit + candidates_for_I = [c for c in candidates if all(c(g.polynomial()) > 0 for g in I.gens())] + assert(len(candidates_for_I) > 0) # This should not be possible, unless I contains a unit if len(candidates_for_I) > 1: raise ValueError("%s does not single out a unique extension of %s to %s"%(prime, vK, L)) else: @@ -304,13 +332,28 @@ def create_key_and_extra_args_for_number_field_from_ideal(self, R, I, prime): # fields are == even if they are distinguishable (because they come # from different constructions.) # Including structure() into the key seems to be a way to distinguish such cases properly. + # This used to be an issue but seems to be fixed, namely, the + # absolute_field of a number field was deemed equivalent to the + # directly created absolute field, even though the absolute_field + # carried the information where it came from return (R, candidates_for_I[0], L.construction()), {'approximants': candidates} def create_object(self, version, key, **extra_args): + r""" + Create a `p`-adic valuation from ``key``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(ZZ, 5) # indirect doctest + 5-adic valuation + + """ from sage.rings.all import ZZ, QQ from sage.rings.padics.padic_generic import pAdicGeneric from valuation_space import DiscretePseudoValuationSpace R = key[0] + K = R.fraction_field() parent = DiscretePseudoValuationSpace(R) if isinstance(R, pAdicGeneric): assert(len(key)==1) @@ -324,7 +367,7 @@ def create_object(self, version, key, **extra_args): _ = key[2] # ignored approximants = extra_args['approximants'] parent = DiscretePseudoValuationSpace(R) - return parent.__make_element_class__(pAdicFromLimitValuation)(parent, v, R.relative_polynomial(), approximants) + return parent.__make_element_class__(pAdicFromLimitValuation)(parent, v, K.relative_polynomial().change_ring(R.base()), approximants) pAdicValuation = PadicValuationFactory("pAdicValuation") @@ -341,6 +384,7 @@ class pAdicValuation_base(DiscreteValuation): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: pAdicValuation(ZZ, 3) 3-adic valuation @@ -356,8 +400,8 @@ class pAdicValuation_base(DiscreteValuation): TESTS:: - sage: TestSuite(pAdicValuation(ZZ, 3)).run() - sage: TestSuite(pAdicValuation(QQ, 5)).run() + sage: TestSuite(pAdicValuation(ZZ, 3)).run() # long time + sage: TestSuite(pAdicValuation(QQ, 5)).run() # long time sage: TestSuite(pAdicValuation(Zp(5), 5)).run() """ @@ -396,7 +440,7 @@ def value_group(self): EXAMPLES:: sage: pAdicValuation(ZZ, 3).value_group() - DiscreteValueGroup(1) + Additive Abelian Group generated by 1 """ from value_group import DiscreteValueGroup @@ -459,8 +503,10 @@ def reduce(self, x): """ x = self.domain().coerce(x) + if self(x) < 0: raise ValueError("reduction is only defined for elements of non-negative valuation") + return self.residue_field()(x) def lift(self, x): @@ -479,8 +525,7 @@ def lift(self, x): 1 """ - if x.parent() is not self.residue_field(): - raise ValueError("x must be in the residue field of the valuation") + x = self.residue_field().coerce(x) return self.domain()(x) @@ -671,7 +716,7 @@ def montes_factorization(self, G, assume_squarefree=False): sage: R.=k[] sage: G = x^2 + 1 sage: v.montes_factorization(G) # long time - ((1 + O(5^4))*x + 2 + 5 + 2*5^2 + 5^3 + O(5^4)) * ((1 + O(5^4))*x + 3 + 3*5 + 2*5^2 + 3*5^3 + O(5^4)) + ((1 + O(5^4))*x + (2 + 5 + 2*5^2 + 5^3 + O(5^4))) * ((1 + O(5^4))*x + (3 + 3*5 + 2*5^2 + 3*5^3 + O(5^4))) REFERENCES: From 250b40fac665be3192aa4429bb18b0ad2b677636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 1 Nov 2016 01:02:21 -0500 Subject: [PATCH 054/740] Doctests for everything p-adic --- __init__.py | 8 +- augmented_valuation.py | 4 + function_field_valuation.py | 10 +- padic_valuation.py | 607 ++++++++++++++++++------------------ valuation.py | 73 ++++- 5 files changed, 394 insertions(+), 308 deletions(-) diff --git a/__init__.py b/__init__.py index 74f9c6eeff5..62cf1cb041a 100644 --- a/__init__.py +++ b/__init__.py @@ -15,7 +15,7 @@ from .augmented_valuation import AugmentedValuation_generic, InfiniteAugmentedValuation from .gauss_valuation import GaussValuation_generic from .valuation import DiscretePseudoValuation, DiscreteValuation, InfiniteDiscretePseudoValuation -from .padic_valuation import pAdicValuation_base, pAdicValuation_number_field, pAdicValuation_int, pAdicValuation_padic +from .padic_valuation import pAdicValuation_base, pAdicValuation_int, pAdicValuation_padic, pAdicFromLimitValuation # ================= # MONKEY PATCH SAGE @@ -116,6 +116,12 @@ class Z_to_quadratic_field_element_patched(sage.rings.number_field.number_field_ def is_injective(self): return True sage.rings.number_field.number_field.NumberField_quadratic._coerce_map_from_ = patch_is_injective(sage.rings.number_field.number_field.NumberField_quadratic._coerce_map_from_, {sage.rings.number_field.number_field_element_quadratic.Q_to_quadratic_field_element: (lambda morphism: Q_to_quadratic_field_element_patched(morphism.codomain())), sage.rings.number_field.number_field_element_quadratic.Z_to_quadratic_field_element: (lambda morphism: Z_to_quadratic_field_element_patched(morphism.codomain()))}) +# the integers embed into the rationals +class Z_to_Q_patched(sage.rings.rational.Z_to_Q): + def is_injective(self): return True +from sage.rings.all import QQ +QQ.coerce_map_from = patch_is_injective(QQ.coerce_map_from, {sage.rings.rational.Z_to_Q: (lambda morphism: Z_to_Q_patched())}) + # register modules at some standard places so imports work as exepcted r""" sage: from sage.rings.valuation.gauss_valuation import GaussValuation diff --git a/augmented_valuation.py b/augmented_valuation.py index caef572d310..a4adcefd44e 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -104,8 +104,12 @@ def __init__(self, parent, v, phi, mu): """ + from sage.rings.all import QQ, infinity + if phi.parent() is not v.domain(): raise ValueError("phi must be in the domain of v") + if mu is not infinity: + mu = QQ(mu) DevelopingValuation.__init__(self, parent, phi) diff --git a/function_field_valuation.py b/function_field_valuation.py index c1d2c0f7c06..14764a867b5 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -31,7 +31,7 @@ (x - 1)-adic valuation sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v.extensions(L) + sage: v.extensions(L) # long time [[ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation, [ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation] @@ -349,7 +349,7 @@ class FunctionFieldValuation_base(DiscreteValuation): sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, FunctionFieldValuation_base) True - sage: TestSuite(v).run() + sage: TestSuite(v).run() # long time """ def extensions(self, L): @@ -414,7 +414,7 @@ class ClassicalFunctionFieldValuation_base(FunctionFieldValuation_base): sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, ClassicalFunctionFieldValuation_base) True - sage: TestSuite(v).run() + sage: TestSuite(v).run() # long time """ def _test_classical_residue_field(self, **options): @@ -781,8 +781,8 @@ class FiniteRationalFunctionFieldValuation(ClassicalFunctionFieldValuation_base, TESTS:: - sage: TestSuite(v).run() - sage: TestSuite(w).run() + sage: TestSuite(v).run() # long time + sage: TestSuite(w).run() # long time sage: TestSuite(u).run() # long time sage: TestSuite(q).run() # long time diff --git a/padic_valuation.py b/padic_valuation.py index 40692cd2a0c..670a36974f5 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -94,8 +94,8 @@ class PadicValuationFactory(UniqueFactory): ``prime`` can also be specified by providing a valuation on the base ring that has a unique extension:: - sage: pAdicValuation(GaussianIntegers(), pAdicValuation(ZZ, 3)) - 3-adic valuation + sage: pAdicValuation(CyclotomicField(5), pAdicValuation(ZZ, 5)) + 5-adic valuation When the extension is not unique, this does not work:: @@ -136,7 +136,7 @@ class PadicValuationFactory(UniqueFactory): singles out an extension of a `p`-adic valuation of the base field:: sage: R = GaussianIntegers() - sage: I = R.gen(1) + sage: I = R.fraction_field().gen() sage: pAdicValuation(R, I + 1) 2-adic valuation @@ -269,10 +269,11 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime) # Lift v to a pseudo-valuation on K[x] # First, we lift valuations defined on subrings of K to valuations on K[x] if v.domain().fraction_field() is not G.parent().fraction_field(): - if v.domain() is not K: - v = pAdicValuation(K, v) - from gauss_valuation import GaussValuation - v = GaussValuation(G.parent(), v) + if v.domain().is_subring(K): + if v.domain() is not K: + v = pAdicValuation(K, v) + from gauss_valuation import GaussValuation + v = GaussValuation(G.parent(), v) # Then, we lift valuations defined on polynmial rings which are subrings of K[x] to K[x] if v.domain() != G.parent(): v = v.extension(G.parent()) @@ -357,11 +358,11 @@ def create_object(self, version, key, **extra_args): parent = DiscretePseudoValuationSpace(R) if isinstance(R, pAdicGeneric): assert(len(key)==1) - return parent.__make_element_class__(pAdicValuation_padic)(R) + return parent.__make_element_class__(pAdicValuation_padic)(parent) elif R is ZZ or R is QQ: prime = key[1] assert(len(key)==2) - return parent.__make_element_class__(pAdicValuation_int)(R, prime) + return parent.__make_element_class__(pAdicValuation_int)(parent, prime) else: # Number field case v = key[1] _ = key[2] # ignored @@ -373,14 +374,14 @@ def create_object(self, version, key, **extra_args): class pAdicValuation_base(DiscreteValuation): """ - Common base class for `p`-adic valuations on integral domains. + Abstract base class for `p`-adic valuations. INPUT: - - ``ring`` -- an integral domain whose elements provide a method - ``valuation`` which takes ``prime`` as a parameter + - ``ring`` -- an integral domain - - ``prime`` -- a prime + - ``p`` -- a rational prime over which this valuation lies, not + necessarily a uniformizer for the valuation EXAMPLES:: @@ -391,7 +392,7 @@ class pAdicValuation_base(DiscreteValuation): sage: pAdicValuation(QQ, 5) 5-adic valuation - For `p`-adic rings, ``prime`` has to match the prime of the ring. + For `p`-adic rings, ``p`` has to match the `p` of the ring. sage: v = pAdicValuation(Zp(3), 2); v Traceback (most recent call last): @@ -405,84 +406,32 @@ class pAdicValuation_base(DiscreteValuation): sage: TestSuite(pAdicValuation(Zp(5), 5)).run() """ - def __init__(self, ring, prime): + def __init__(self, parent, p): """ - Initialization. - TESTS:: - sage: from sage.rings.padics.padic_valuation import pAdicValuation_int # optional: integrated - sage: isinstance(pAdicValuation(ZZ, 3), pAdicValuation_int) + sage: from mac_lane import * # optional: standalone + sage: isinstance(pAdicValuation(ZZ, 3), pAdicValuation_base) True """ - from rings_with_valuation import RingsWithDiscreteValuation - from valuation_space import DiscretePseudoValuationSpace - DiscreteValuation.__init__(self, DiscretePseudoValuationSpace(ring)) - self._prime = prime - - def prime(self): - """ - Return the prime `p` of this `p`-adic valuation. - - EXAMPLES:: - - sage: pAdicValuation(ZZ, 3).prime() - 3 - - """ - return self._prime - - def value_group(self): - r""" - Return the value group of this valuation. - - EXAMPLES:: - - sage: pAdicValuation(ZZ, 3).value_group() - Additive Abelian Group generated by 1 - - """ - from value_group import DiscreteValueGroup - return DiscreteValueGroup(1) - - def _repr_(self): - """ - Return a printable representation of this valuation. - - EXAMPLES:: - - sage: pAdicValuation(ZZ, 3)._repr_() - '3-adic valuation' + from sage.rings.all import ZZ - """ - return "%s-adic valuation"%(self.prime()) - from valuation import DiscretePseudoValuation - if isinstance(self.prime(), DiscretePseudoValuation): - vals = self.prime()._augmentations() - if vals[0].is_gauss_valuation(): - return "[ %r over %r ]-adic valuation"%(", ".join("v(%r) = %r"%(v.phi().change_var(self.variable_name()), v(v.phi())) for v in vals[1:]), vals[0].constant_valuation()) - else: - return vals[0] - return "%s-adic valuation"%(self.prime()) - else: - return "%s-adic valuation"%(self.prime()) + DiscreteValuation.__init__(self, parent) + self._p = ZZ(p) - def _call_(self, x): + def p(self): """ - Evaluate this valuation at ``x``. - - INPUT:: - - - ``x`` -- an element in the domain of this valuation + Return the `p` of this `p`-adic valuation. EXAMPLES:: - sage: pAdicValuation(ZZ, 3)(9) + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(GaussianIntegers(), 2).p() 2 """ - return x.valuation(self.prime()) + return self._p def reduce(self, x): """ @@ -498,7 +447,9 @@ def reduce(self, x): EXAMPLES:: - sage: pAdicValuation(ZZ, 3).reduce(4) + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 3) + sage: v.reduce(4) 1 """ @@ -529,67 +480,53 @@ def lift(self, x): return self.domain()(x) - def uniformizer(self): - """ - Return a uniformizer of this `p`-adic valuation, i.e., `p` as an - element of the domain. - - EXAMPLES:: - - sage: v = pAdicValuation(ZZ, 3) - sage: v.uniformizer() - 3 - - """ - return self.domain()(self._prime) - - def residue_ring(self): - """ - Return the residue field of this valuation. - - EXAMPLES:: - - sage: v = pAdicValuation(ZZ, 3) - sage: v.residue_ring() - Finite Field of size 3 - - """ - from sage.rings.all import GF - return GF(self._prime) - def is_unramified(self, G, include_steps=False, assume_squarefree=False): """ - Return whether ``G`` defines an unramified extension of the `p`-adic - completion of the domain this valuation. + Return whether ``G`` defines a single unramified extension of the + completion of the domain of this valuation. INPUT: - - ``G`` -- a monic polynomial over the domain of this valuation + - ``G`` -- a monic squarefree polynomial over the domain of this valuation - - ``include_steps`` -- a boolean (default: ``False``); whether or not - to include the approximate valuations that were used to determine - the result in the return value. + - ``include_steps`` -- a boolean (default: ``False``); whether to + include the approximate valuations that were used to determine the + result in the return value. - - ``assume_squarefree`` -- a boolean (default: ``False``); whether or - not to assume that ``G`` is square-free over the `p`-adic completion - of the domain of this valuation. Setting this to ``True`` can - significantly improve the performance. + - ``assume_squarefree`` -- a boolean (default: ``False``); whether to + assume that ``G`` is square-free over the completion of the domain of + this valuation. Setting this to ``True`` can significantly improve + the performance. EXAMPLES: We consider an extension as unramified if its ramification index is 1. Hence, a trivial extension is unramified:: - sage: pass + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = pAdicValuation(QQ, 2) + sage: v.is_unramified(x) + True + If ``G`` remains irreducible in reduction, then it defines an + unramified extension:: - NOTE: It is not enough to check whether ``G`` is irreducible in - reduction for this. + sage: v.is_unramified(x^2 + x + 1) + True + + However, even if ``G`` factors, it might define an unramified + extension:: + + sage: v.is_unramified(x^2 + 2*x + 4) + True """ R = G.parent() - if R.base_ring() is not self.domain(): - raise ValueError("G must be defined over the domain of this valuation") + + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + if not is_PolynomialRing(R) or R.base_ring() is not self.domain() or not len(R.gens()) == 1 or not G.is_monic(): + raise ValueError("G must be a monic univariate polynomial over the domain of this valuation") if not assume_squarefree and not G.is_squarefree(): raise ValueError("G must be squarefree") @@ -624,25 +561,29 @@ def is_unramified(self, G, include_steps=False, assume_squarefree=False): def is_totally_ramified(self, G, include_steps=False, assume_squarefree=False): """ - Return whether ``G`` defines a totally ramified extension. + Return whether ``G`` defines a single totally ramified extension of the + completion of the domain of this valuation. INPUT: - - ``G`` -- a monic polynomial over the domain of this valuation + - ``G`` -- a monic squarefree polynomial over the domain of this valuation - - ``include_steps`` -- a boolean (default: ``False``), include the the - valuations produced during the process of checking whether ``G`` is - totally ramified in the return value + - ``include_steps`` -- a boolean (default: ``False``); where to include + the valuations produced during the process of checking whether ``G`` + is totally ramified in the return value - - ``assume_squarefree`` -- a boolean (default: ``False``), whether to - assume the input to be squarefree + - ``assume_squarefree`` -- a boolean (default: ``False``); whether to + assume that ``G`` is square-free over the completion of the domain of + this valuation. Setting this to ``True`` can significantly improve + the performance. ALGORITHM: - This is simplified version of :meth:`mac_lane_approximants`. + This is a simplified version of :meth:`mac_lane_approximants`. EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: k=Qp(5,4) sage: v = pAdicValuation(k) sage: R.=k[] @@ -663,8 +604,10 @@ def is_totally_ramified(self, G, include_steps=False, assume_squarefree=False): """ R = G.parent() - if R.base_ring() is not self.domain(): - raise ValueError("G must be defined over the domain of this valuation") + + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + if not is_PolynomialRing(R) or R.base_ring() is not self.domain() or not len(R.gens()) == 1 or not G.is_monic(): + raise ValueError("G must be a monic univariate polynomial over the domain of this valuation") if not assume_squarefree and not G.is_squarefree(): raise ValueError("G must be squarefree") @@ -697,56 +640,32 @@ def is_totally_ramified(self, G, include_steps=False, assume_squarefree=False): else: return ret - def montes_factorization(self, G, assume_squarefree=False): - """ - Factor ``G`` by computing :meth:`mac_lane_approximants` to arbitrary - precision. - - INPUT: - - - ``G`` -- a monic polynomial over the domain of this valuation - - - ``assume_squarefree`` -- a boolean (default: ``False``), whether to - assume the input to be squarefree + def change_ring(self, ring): + r""" + Change the domain of this valuation to ``ring`` if possible. EXAMPLES:: - sage: k=Qp(5,4) - sage: v = pAdicValuation(k) - sage: R.=k[] - sage: G = x^2 + 1 - sage: v.montes_factorization(G) # long time - ((1 + O(5^4))*x + (2 + 5 + 2*5^2 + 5^3 + O(5^4))) * ((1 + O(5^4))*x + (3 + 3*5 + 2*5^2 + 3*5^3 + O(5^4))) - - REFERENCES: - - .. [GMN2008] Jordi Guardia, Jesus Montes, Enric Nart (2008). Newton - polygons of higher order in algebraic number theory. arXiv:0807.2620 - [math.NT] + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 2) + sage: v.change_ring(QQ).domain() + Rational Field """ - R = G.parent() - if R.base_ring() is not self.domain(): - raise ValueError("G must be defined over the domain of this valuation") - if not G.is_monic(): - raise ValueError("G must be monic") - if not all([self(c)>=0 for c in G.coefficients()]): - raise ValueError("G must be integral") + return pAdicValuation(ring, self.p()) - from sage.rings.all import infinity - # W contains approximate factors of G - W = self.mac_lane_approximants(G, precision_cap=infinity,assume_squarefree=assume_squarefree) - ret = [w.phi() for w in W] + def extensions(self, ring): + r""" + Return the extensions of this valuation to ``ring``. - # the polynomials in ret give "a" factorization; there is no guarantee - # that every factorization is of this form... TODO - from sage.structure.factorization import Factorization - return Factorization([ (g,1) for g in ret ], simplify=False) + EXAMPLES:: - def change_ring(self, ring): - return pAdicValuation(ring, self.prime()) + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 2) + sage: v.extensions(GaussianIntegers()) + [2-adic valuation] - def extensions(self, ring): + """ if self.domain() is ring: return [self] if self.domain().fraction_field() is not self.domain(): @@ -754,52 +673,37 @@ def extensions(self, ring): return pAdicValuation(self.domain().fraction_field(), self).extensions(ring) if self.domain().is_subring(ring): from sage.rings.number_field.number_field import is_NumberField - if is_NumberField(ring): - if ring.base() is self.domain(): + if is_NumberField(ring.fraction_field()): + if ring.base().fraction_field() is self.domain().fraction_field(): from valuation_space import DiscretePseudoValuationSpace from limit_valuation import FiniteExtensionFromInfiniteValuation parent = DiscretePseudoValuationSpace(ring) - approximants = self.mac_lane_approximants(ring.relative_polynomial()) + approximants = self.mac_lane_approximants(ring.fraction_field().relative_polynomial().change_ring(self.domain())) return [pAdicValuation(ring, approximant) for approximant in approximants] if ring.base() is not ring and self.domain().is_subring(ring.base()): return sum([w.extensions(ring) for w in self.extensions(ring.base())], []) - raise NotImplementedError("extensions of %r from %r to %r not implemented"%(self, self.domain(), ring)) + raise NotImplementedError("can not compute extensions of %r from %r to %r"%(self, self.domain(), ring)) def restriction(self, ring): - if self.domain().has_coerce_map_from(ring): - return pAdicValuation(ring, self.prime()) - - K = self.domain() - if L is K: - return self - if L is K.fraction_field(): - return pAdicValuation(L, self) - if L.base_ring() is not K: - raise ValueError("L must be a simple finite extension of %r but %r is not"%(K, L)) - - if algorithm == "ideal": - I = L.ideal(self.prime()).factor() - if len(I) > 1: - raise ValueError("extension to %s is not unique"%L) - I = I[0] - return pAdicValuation(L, I) - elif algorithm == "mac_lane": - W = self.mac_lane_approximants(L.defining_polynomial()) - if len(W) > 1: - raise ValueError("extension to %s is not unique"%L) - return pAdicValuation(L, W[0]) - else: raise ValueError + r""" + Return the restriction of this valuation to ``ring``. - def _ge_(self, other): - if other.is_trivial(): - return other.is_discrete_valuation() - if isinstance(other, pAdicValuation_base): - return self.prime() == other.prime() - return super(pAdicValuation_base, self)._ge_(self, other) + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(GaussianIntegers(), 2) + sage: v.restriction(ZZ) + 2-adic valuation + + """ + if not ring.is_subring(self.domain()): + raise ValueError("ring must be a subring of the domain of this valuation but %r is not a subring of %r"%(ring, self.domain())) + + return pAdicValuation(ring, self.p()) class pAdicValuation_padic(pAdicValuation_base): """ - The `p`-adic valuation of a `p`-adic ring. + The `p`-adic valuation of a complete `p`-adic ring. INPUT: @@ -807,22 +711,26 @@ class pAdicValuation_padic(pAdicValuation_base): EXAMPLES:: - sage: pAdicValuation(Qp(2)) #indirect doctest + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(Qp(2)); v #indirect doctest 2-adic valuation + TESTS:: + + sage: TestSuite(v).run() # optional: integrated + """ - def __init__(self, R): + def __init__(self, parent): """ - Initialization. - TESTS:: + sage: from mac_lane import * # optional: standalone sage: from sage.rings.padics.padic_valuation import padicValuation_padic # optional: integrated sage: isinstance(pAdicValuation(Qp(2)), pAdicValuation_padic) True """ - pAdicValuation_base.__init__(self, R, R.prime()) + pAdicValuation_base.__init__(self, parent, parent.domain().prime()) def reduce(self, x): """ @@ -838,6 +746,7 @@ def reduce(self, x): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R = Zp(3) sage: pAdicValuation(Zp(3)).reduce(R(4)) 1 @@ -848,7 +757,8 @@ def reduce(self, x): def lift(self, x): """ - Lift ``x`` from the residue field to the domain of this valuation. + Lift ``x`` from the :meth:`residue_field` to the :meth:`domain` of this + valuation. INPUT: @@ -856,6 +766,7 @@ def lift(self, x): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R = Zp(3) sage: v = pAdicValuation(R) sage: xbar = v.reduce(R(4)) @@ -863,18 +774,16 @@ def lift(self, x): 1 + O(3^20) """ - if x.parent() is not self.residue_field(): - raise ValueError("x must be in the residue field of the valuation") - + x = self.residue_field().coerce(x) return self.domain()(x).lift_to_precision() def uniformizer(self): """ - Return a uniformizer of this valuation, i.e., `p` as an element of the - domain. + Return a uniformizer of this valuation. EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: v = pAdicValuation(Zp(3)) sage: v.uniformizer() 3 + O(3^21) @@ -884,7 +793,7 @@ def uniformizer(self): def shift(self, c, v): """ - Multiply ``c`` by a ``v``th power of the uniformizer. + Multiply ``c`` by a ``v``-th power of the uniformizer. INPUT: @@ -894,11 +803,12 @@ def shift(self, c, v): OUTPUT: - If the resulting element has negative valation, then it will be brought - to the fraction field, otherwise it is an element of the domain. + If the resulting element has negative valation, it will be brought to + the fraction field, otherwise it is an element of the domain. EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R = Zp(3) sage: v = pAdicValuation(Zp(3)) sage: v.shift(R(2),3) @@ -906,105 +816,204 @@ def shift(self, c, v): """ from sage.rings.all import ZZ - if c.parent() is not self.domain(): - raise ValueError("c must be in the domain of the valuation") - if not v in ZZ: - raise ValueError("v must be an integer") + c = self.domain().coerce(c) v = ZZ(v) - return c<0) - pAdicValuation_base.__init__(self, R, p) - assert(valuation in approximants) - self._valuation = valuation - self._approximants = approximants - - def _mac_lane_step(self): - E,F = self._valuation.E(),self._valuation.F() - self._valuation = self._valuation.mac_lane_step(self.domain().relative_polynomial()) - assert len(self._valuation)== 1 - self._valuation = self._valuation[0] - assert E == self._valuation.E() - assert F == self._valuation.F() + def _repr_(self): + """ + Return a printable representation of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(ZZ, 3)._repr_() + '3-adic valuation' + + """ + return "%s-adic valuation"%(self.p()) def _call_(self, x): - if x.parent() is not self.domain(): - raise ValueError("x must be in the domain of the valuation") - - if x.is_zero(): - from sage.rings.all import infinity - return infinity - - f = self._valuation.domain()(x.list()) - while True: - vals = self._valuation.valuations(f) - ret = min(vals) - argmin = [1 for t in vals if t == ret] - if len(argmin)>1: - self._mac_lane_step() - continue - return ret*self._valuation.E() + r""" + Evaluate this valuation at ``x``. - def reduce(self, x): - x = self.domain().coerce(x) + EXAMPLES:: - if self(x)>0: - return self.residue_field().zero() - f = self._valuation.domain()(x.list()) - while True: - ret = self._valuation.reduce(f) - if ret.degree(): - self._mac_lane_step() - continue - return ret[0] + sage: from mac_lane import * # optional: standalone + sage: K = Qp(3) + sage: R. = K[] + sage: L. = K.extension(y^2 - 3) + sage: pAdicValuation(L, 3)(3) + 1 - def lift(self, x): - if x.parent() is not self.residue_field(): - raise ValueError("x must be in the residue field of the valuation") - if x.is_one(): - return self.domain().one() - if x.is_zero(): - return self.domain().zero() + """ + return x.ordp() + + def residue_ring(self): + r""" + Return the residue field of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(Qq(9, names='a'), 3).residue_ring() + Finite Field in a0 of size 3^2 + + """ + return self.domain().residue_field() + +class pAdicValuation_int(pAdicValuation_base): + r""" + A `p`-adic valuation on the integers or the rationals. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 3); v + 3-adic valuation - return self._valuation.lift(x)(self.domain().fraction_field().gen()) + TESTS:: + sage: TestSuite(v).run() + + """ def _repr_(self): """ Return a printable representation of this valuation. EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: pAdicValuation(ZZ, 3)._repr_() '3-adic valuation' """ - from valuation import DiscretePseudoValuation - if isinstance(self._valuation, DiscretePseudoValuation): - from augmented_valuation import AugmentedValuation_generic - # print the minimal information that singles out this valuation from all approximants - approximants = [v._augmentations() for v in self._approximants] - augmentations = self._valuation._augmentations() - unique_approximant = self._valuation - for l in range(len(augmentations)): - if len([a for a in approximants if a[:l+1] == augmentations[:l+1]]) == 1: - unique_approximant = augmentations[:l+1] - break - if unique_approximant[0].is_gauss_valuation(): - unique_approximant[0] = unique_approximant[0].constant_valuation() - if len(unique_approximant) == 1: - return repr(unique_approximant[0]) - return "[ %s ]-adic valuation"%(", ".join("v(%r) = %r" if (isinstance(v, AugmentedValuation_generic) and v.domain() == self._valuation.domain()) else repr(v) for v in unique_approximant)) - return "%s-adic valuation"%(self._valuation) - -class pAdicFromLimitValuation(FiniteExtensionFromLimitValuation): + return "%s-adic valuation"%(self.p()) + + def _call_(self, x): + """ + Evaluate this valuation at ``x``. + + INPUT:: + + - ``x`` -- an element in the domain of this valuation + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(ZZ, 3)(9) + 2 + + """ + return x.valuation(self.p()) + + def uniformizer(self): + """ + Return a uniformizer of this `p`-adic valuation, i.e., `p` as an + element of the domain. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 3) + sage: v.uniformizer() + 3 + + """ + return self.domain()(self.p()) + + def residue_ring(self): + """ + Return the residue field of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 3) + sage: v.residue_ring() + Finite Field of size 3 + + """ + from sage.rings.all import GF + return GF(self.p()) + + def _ge_(self, other): + r""" + Return whether this valuation is greater than or equal than ``other`` + everywhere. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 2) + sage: w = TrivialValuation(ZZ) + sage: v >= w + True + + """ + if other.is_trivial(): + return other.is_discrete_valuation() + if isinstance(other, pAdicValuation_int): + return self.p() == other.p() + return super(pAdicValuation_base, self)._ge_(self, other) + + +class pAdicFromLimitValuation(FiniteExtensionFromLimitValuation, pAdicValuation_base): + r""" + A `p`-adic valuation on a number field or a subring thereof, i.e., a + valuation that extends the `p`-adic valuation on the integers. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(GaussianIntegers(), 3); v + 3-adic valuation + + TESTS:: + + sage: TestSuite(v).run() + + """ + def __init__(self, parent, approximant, G, approximants): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(GaussianIntegers(), 3) + sage: isinstance(v, pAdicFromLimitValuation) + True + + """ + FiniteExtensionFromLimitValuation.__init__(self, parent, approximant, G, approximants) + pAdicValuation_base.__init__(self, parent, approximant.constant_valuation().p()) + def _to_base_domain(self, f): + r""" + Return ``f``, an element of the domain of this valuation, as an element + of the domain of the underlying limit valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(GaussianIntegers(), 3) + sage: I = GaussianIntegers().fraction_field().gen() + sage: v._to_base_domain(I) + x + + """ return f.polynomial()(self._base_valuation.domain().gen()) + def _from_base_domain(self, f): - return f(self.domain().gen()) + r""" + Return ``f``, an element of the domain of this valuation, as an element + of the domain of the underlying limit valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(GaussianIntegers(), 3) + sage: v._from_base_domain(v._base_valuation.domain().gen()) + I + + """ + return f(self.domain().fraction_field().gen()) diff --git a/valuation.py b/valuation.py index cd6e2d98748..ad3b7e9d0fc 100644 --- a/valuation.py +++ b/valuation.py @@ -42,7 +42,7 @@ class DiscretePseudoValuation(Morphism): TESTS:: - sage: TestSuite(v).run() + sage: TestSuite(v).run() # long time """ def __init__(self, parent): @@ -314,7 +314,7 @@ class InfiniteDiscretePseudoValuation(DiscretePseudoValuation): sage: isinstance(w, InfiniteDiscretePseudoValuation) True - sage: TestSuite(w).run() + sage: TestSuite(w).run() # long time """ def is_discrete_valuation(self): @@ -351,7 +351,7 @@ class DiscreteValuation(DiscretePseudoValuation): sage: isinstance(w, DiscreteValuation) True - sage: TestSuite(w).run() + sage: TestSuite(w).run() # long time """ def is_discrete_valuation(self): @@ -718,6 +718,73 @@ def mac_lane_approximant(self, G, valuation, approximants = None): assert len(smaller_approximants) == 1 return smaller_approximants[0] + def montes_factorization(self, G, assume_squarefree=False, precision_cap=None): + """ + Factor ``G`` over the completion of the domain of this valuation. + + INPUT: + + - ``G`` -- a monic polynomial over the domain of this valuation + + - ``assume_squarefree`` -- a boolean (default: ``False``), whether to + assume ``G`` to be squarefree + + - ``precision_cap`` -- a number, ``None``, or infinity (default: + ``None``); if ``None`` or ``infinity``, the returned are actual + factors of ``G``, otherwise they are only factors with precision at + least ``precision_cap``. + + ALGORITHM: + + We compute :meth:`mac_lane_approximants` with ``precision_cap``. + The key polynomials approximate factors of ``G``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: k=Qp(5,4) + sage: v = pAdicValuation(k) + sage: R.=k[] + sage: G = x^2 + 1 + sage: v.montes_factorization(G) # long time + ((1 + O(5^4))*x + (2 + 5 + 2*5^2 + 5^3 + O(5^4))) * ((1 + O(5^4))*x + (3 + 3*5 + 2*5^2 + 3*5^3 + O(5^4))) + + The computation might not terminate over incomplete fields (in + particular because the factors can not be represented there):: + + sage: R. = QQ[] + sage: v = pAdicValuation(QQ, 2) + sage: v.montes_factorization(x^2 + 1) + x^2 + 1 + + sage: v.montes_factorization(x^2 - 1) # not tested, does not terminate + + sage: v.montes_factorization(x^2 - 1, precision_cap=10) + (x + 1) * (x + 1023) + + REFERENCES: + + .. [GMN2008] Jordi Guardia, Jesus Montes, Enric Nart (2008). Newton + polygons of higher order in algebraic number theory. arXiv:0807.2620 + [math.NT] + + """ + R = G.parent() + if R.base_ring() is not self.domain(): + raise ValueError("G must be defined over the domain of this valuation") + if not G.is_monic(): + raise ValueError("G must be monic") + if not all([self(c)>=0 for c in G.coefficients()]): + raise ValueError("G must be integral") + + from sage.rings.all import infinity + # W contains approximate factors of G + W = self.mac_lane_approximants(G, precision_cap=precision_cap or infinity,assume_squarefree=assume_squarefree) + ret = [w.phi() for w in W] + + from sage.structure.factorization import Factorization + return Factorization([ (g,1) for g in ret ], simplify=False) + def _ge_(self, other): r""" Return whether this valuation is greater than or equal to ``other`` From 5cf184ef4477a0466b23b675632f8574d00565a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 1 Nov 2016 01:06:40 -0500 Subject: [PATCH 055/740] fix fractional ideal test case --- padic_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/padic_valuation.py b/padic_valuation.py index 670a36974f5..c1d9c713b6e 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -137,7 +137,7 @@ class PadicValuationFactory(UniqueFactory): sage: R = GaussianIntegers() sage: I = R.fraction_field().gen() - sage: pAdicValuation(R, I + 1) + sage: pAdicValuation(R, R.fractional_ideal(I + 1)) 2-adic valuation """ From 0a373848545a685274d3fcc76056dfdf12504c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 1 Nov 2016 01:35:27 -0500 Subject: [PATCH 056/740] Added tests for valuations over the infinite place --- function_field_valuation.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/function_field_valuation.py b/function_field_valuation.py index 14764a867b5..f2085eb8c9b 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -151,8 +151,8 @@ class FunctionFieldValuationFactory(UniqueFactory): There are several ways to create valuations on extensions of rational function fields:: - sage: K. = FunctionField(QQ) - sage: R. = K[] + sage: K. = FunctionField(QQ) + sage: R. = K[] sage: L. = K.extension(y^2 - x); L Function field in y defined by y^2 - x @@ -365,7 +365,18 @@ def extensions(self, L): sage: L. = K.extension(y^2 - x) sage: v.extensions(L) [(x)-adic valuation] - + + TESTS: + + Valuations over the infinite place:: + + sage: v = FunctionFieldValuation(K, 1/x) + sage: R. = K[] + sage: L. = K.extension(y^2 - 1/(x + 1)) + sage: v.extensions(L) + [[ Valuation at the infinite place, v(y - 1/x) = 3 ]-adic valuation, + [ Valuation at the infinite place, v(y + 1/x) = 3 ]-adic valuation] + """ K = self.domain() from sage.categories.function_fields import FunctionFields From eca38380854848eac7dd5f6de3f6c937229ea9f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 1 Nov 2016 12:47:35 -0500 Subject: [PATCH 057/740] fixed typo --- function_field_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/function_field_valuation.py b/function_field_valuation.py index f2085eb8c9b..57547d0c89d 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -372,7 +372,7 @@ def extensions(self, L): sage: v = FunctionFieldValuation(K, 1/x) sage: R. = K[] - sage: L. = K.extension(y^2 - 1/(x + 1)) + sage: L. = K.extension(y^2 - 1/(x^2 + 1)) sage: v.extensions(L) [[ Valuation at the infinite place, v(y - 1/x) = 3 ]-adic valuation, [ Valuation at the infinite place, v(y + 1/x) = 3 ]-adic valuation] From 9465e36e8a68b5753985ec09b1d17ad2ffc4a2ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 1 Nov 2016 12:49:18 -0500 Subject: [PATCH 058/740] fixed typo --- trivial_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trivial_valuation.py b/trivial_valuation.py index 5cfb09a57da..a7dfe8ff29e 100644 --- a/trivial_valuation.py +++ b/trivial_valuation.py @@ -412,7 +412,7 @@ def extensions(self, ring): sage: from mac_lane import * # optional: standalone sage: v = TrivialValuation(ZZ) sage: v.extensions(QQ) - Trivial valuation on Rational Field + [Trivial valuation on Rational Field] """ if self.domain().is_subring(ring): From 9a9505e53c098d6b04fff7a6b68951732ea951b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 1 Nov 2016 15:13:04 -0500 Subject: [PATCH 059/740] Fix doctests of Gauss valuation --- __init__.py | 12 ++ augmented_valuation.py | 27 ++--- developing_valuation.py | 7 +- function_field_valuation.py | 14 ++- gauss_valuation.py | 233 +++++++++++++++++++++++++----------- limit_valuation.py | 2 +- padic_valuation.py | 8 +- valuation.py | 8 +- valuation_space.py | 18 +-- 9 files changed, 216 insertions(+), 113 deletions(-) diff --git a/__init__.py b/__init__.py index 62cf1cb041a..d4763ce1dbc 100644 --- a/__init__.py +++ b/__init__.py @@ -122,6 +122,18 @@ def is_injective(self): return True from sage.rings.all import QQ QQ.coerce_map_from = patch_is_injective(QQ.coerce_map_from, {sage.rings.rational.Z_to_Q: (lambda morphism: Z_to_Q_patched())}) +# the integers embed into their extensions in number fields +class DefaultConvertMap_unique_patched(sage.structure.coerce_maps.DefaultConvertMap_unique): + def is_injective(self): return True +def _coerce_map_from_patched(self, domain): + from sage.rings.all import ZZ + if domain is ZZ or domain is int or domain is long: + return DefaultConvertMap_unique_patched(domain, self) + return False +from sage.rings.number_field.order import Order +Order._coerce_map_from_ = _coerce_map_from_patched +del(_coerce_map_from_patched) + # register modules at some standard places so imports work as exepcted r""" sage: from sage.rings.valuation.gauss_valuation import GaussValuation diff --git a/augmented_valuation.py b/augmented_valuation.py index a4adcefd44e..2de2db2a704 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -1,4 +1,4 @@ -""" +r""" Augmented valuations on polynomial rings Implements inductive valuations as defined in [ML1936]. @@ -332,24 +332,6 @@ def _repr_(self): def _augmentations(self): return self._base_valuation._augmentations() + [self] - @cached_method - def constant_valuation(self): - """ - Return the restriction of this valuation to the constants of the - polynomial ring. - - EXAMPLES:: - - sage: R. = Qq(4,5) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: w = v.extension(x^2 + x + u, 1/2) - sage: w.constant_valuation() - 2-adic valuation - - """ - return self._base_valuation.constant_valuation() - @cached_method def value_group(self): """ @@ -643,7 +625,7 @@ def lift(self, F): F = F[0] if self.phi() == self.domain().gen(): # this is a valuation of the form [p-adic valuation, v(x) = 1] - constant = self.constant_valuation().lift(F) + constant = self.restriction(self.domain().base_ring()).lift(F) assert constant in self.domain().base_ring() return self.domain()(constant) else: @@ -937,6 +919,11 @@ def extensions(self, ring): ret.append(AugmentedValuation(v, f, mu)) return ret + def restriction(self, ring): + if ring is self.domain().base_ring(): + return self._base_valuation.restriction(ring) + return super(AugmentedValuation_generic, self).restriction(ring) + def uniformizer(self): return self.element_with_valuation(self.value_group()._generator) diff --git a/developing_valuation.py b/developing_valuation.py index 5623ddb9817..a4156c77aa3 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -645,7 +645,7 @@ def equivalence_decomposition(self, f): from sage.structure.factorization import Factorization if not self.domain().base_ring().is_field(): - v = self.change_ring(self.domain().base_ring().fraction_field()) + v = self.extension(self.domain().change_ring(self.domain().base_ring().fraction_field())) ret = v.equivalence_decomposition(v.domain()(f)) return Factorization([(g.change_ring(self.domain().base_ring()),e) for g,e in ret], unit=ret.unit().change_ring(self.domain().base_ring())) @@ -815,10 +815,11 @@ def _normalize_leading_coefficients(self, f): if len(f.list()) > f.degree()+1: from sage.rings.all import infinity # f has leading zero coefficients - m = min([self.constant_valuation()(c) for c in f.list()[f.degree()+1:]]) + v = self.restriction(self.domain().base_ring()) + m = min([v(c) for c in f.list()[f.degree()+1:]]) if f.is_zero(): f= f.parent().zero() - elif m is infinity or m > max([self.constant_valuation()(c) for c in f.list()[:f.degree()+1]]): + elif m is infinity or m > max([v(c) for c in f.list()[:f.degree()+1]]): f= self.domain()(f.list()[:f.degree()+1]) else: raise ValueError("f must not have leading zero coefficients") diff --git a/function_field_valuation.py b/function_field_valuation.py index 57547d0c89d..b4410151be0 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -274,16 +274,17 @@ def create_key_and_extra_args_from_valuation(self, domain, valuation): if valuation.domain() is domain._ring: if domain.base_field() is not domain: - if valuation.constant_valuation().domain() is not domain.base_field(): - raise ValueError("valuation must extend a valuation on the base field but %r extends %r whose domain is not %r"%(valuation, valuation.constant_valuation(), domain.base_field())) + vK = valuation.restriction(valuation.domain().base_ring()) + if vK.domain() is not domain.base_field(): + raise ValueError("valuation must extend a valuation on the base field but %r extends %r whose domain is not %r"%(valuation, vK, domain.base_field())) # Valuation is an approximant that describes a single valuation # on domain. # For uniqueness of valuations (which provides better caching # and easier pickling) we need to find a normal form of # valuation, i.e., the smallest approximant that describes this # valuation - approximants = valuation.constant_valuation().mac_lane_approximants(domain.polynomial()) - approximant = valuation.constant_valuation().mac_lane_approximant(domain.polynomial(), valuation, approximants) + approximants = vK.mac_lane_approximants(domain.polynomial()) + approximant = vK.mac_lane_approximant(domain.polynomial(), valuation, approximants) return (domain, approximant), {'approximants': approximants} else: # on a rational function field K(x), any valuation on K[x] that @@ -599,8 +600,9 @@ def _repr_(self): if self._base_valuation._base_valuation == GaussValuation(self.domain()._ring, TrivialValuation(self.domain().constant_field())): if self._base_valuation._mu == 1: return "(%r)-adic valuation"%(self._base_valuation.phi()) - if self._base_valuation == GaussValuation(self.domain()._ring, self._base_valuation.constant_valuation()): - return repr(self._base_valuation.constant_valuation()) + vK = self._base_valuation.restriction(self._base_valuation.domain().base_ring()) + if self._base_valuation == GaussValuation(self.domain()._ring, vK): + return repr(vK) return "Valuation on rational function field induced by %s"%self._base_valuation def extensions(self, L): diff --git a/gauss_valuation.py b/gauss_valuation.py index 0ce522ca746..01815612e81 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -22,9 +22,9 @@ Gauss valuations can also be defined iteratively based on valuations over polynomial rings:: - sage: v = v.extension(x, 1/4); v + sage: v = v.augmentation(x, 1/4); v [ Gauss valuation induced by 2-adic valuation, v(x) = 1/4 ] - sage: v = v.extension(x^4+2*x^3+2*x^2+2*x+2, 4/3); v + sage: v = v.augmentation(x^4+2*x^3+2*x^2+2*x+2, 4/3); v [ Gauss valuation induced by 2-adic valuation, v(x) = 1/4, v(x^4 + 2*x^3 + 2*x^2 + 2*x + 2) = 4/3 ] sage: S. = R[] sage: w = GaussValuation(S, v); w @@ -34,7 +34,7 @@ """ #***************************************************************************** -# Copyright (C) 2013 Julian Rueth +# Copyright (C) 2013-2016 Julian Rueth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -94,23 +94,28 @@ def create_key(self, domain, v = None): sage: GaussValuation.create_key(R, v) Traceback (most recent call last): ... - ValueError: the domain of v must be the base ring of domain + ValueError: the domain of v must be the base ring of domain but 2-adic valuation is not defined over Integer Ring but over Rational Field """ + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + if not is_PolynomialRing(domain): + raise TypeError("GaussValuations can only be created over polynomial rings but %r is not a polynomial ring"%(domain,)) if not domain.ngens() == 1: - raise NotImplementedError("only implemented for univariate rings") + raise NotImplementedError("domain must be univariate but %r is not univariate"%(domain,)) if v is None: v = domain.base_ring().valuation() if not v.domain() is domain.base_ring(): - raise ValueError("the domain of v must be the base ring of domain") + raise ValueError("the domain of v must be the base ring of domain but %r is not defined over %r but over %r"%(v, domain.base_ring(), v.domain())) + if not v.is_discrete_valuation(): + raise ValueError("v must be a discrete valuation but %r is not"%(v,)) return (domain, v) def create_object(self, version, key, **extra_args): r""" - Create a Gauss valuation from the normalized parameters. + Create a Gauss valuation from normalized parameters. TESTS:: @@ -124,7 +129,7 @@ def create_object(self, version, key, **extra_args): domain, v = key from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace parent = DiscretePseudoValuationSpace(domain) - return GaussValuation_generic(parent, v) + return parent.__make_element_class__(GaussValuation_generic)(parent, v) GaussValuation = GaussValuationFactory("GaussValuation") @@ -134,7 +139,7 @@ class GaussValuation_generic(DevelopingValuation): INPUT: - - ``domain`` -- a polynomial ring over a valued ring `R` + - ``domain`` -- a univariate polynomial ring over a valued ring `R` - ``v`` -- a discrete valuation on `R` @@ -153,17 +158,13 @@ class GaussValuation_generic(DevelopingValuation): TESTS:: - sage: TestSuite(v).run(skip="_test_category") + sage: TestSuite(v).run() """ def __init__(self, parent, v): """ - Initialization. - - EXAMPLES:: + TESTS:: - sage: from sage.rings.padics import GaussValuation # optional: integrated - sage: from sage.rings.padics.gauss_valuation import GaussValuation_generic # optional: integrated sage: from mac_lane import * # optional: standalone sage: from mac_lane.gauss_valuation import GaussValuation_generic # optional: standalone sage: S. = QQ[] @@ -172,8 +173,7 @@ def __init__(self, parent, v): True """ - domain = parent.domain() - DevelopingValuation.__init__(self, parent, domain.gen()) + DevelopingValuation.__init__(self, parent, parent.domain().gen()) self._base_valuation = v @@ -187,7 +187,7 @@ def value_group(self): sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) sage: v.value_group() - DiscreteValueGroup(1) + Additive Abelian Group generated by 1 """ return self._base_valuation.value_group() @@ -201,7 +201,7 @@ def _repr_(self): sage: from mac_lane import * # optional: standalone sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) - sage: v + sage: v # indirect doctest Gauss valuation induced by 5-adic valuation """ @@ -257,7 +257,7 @@ def shift(self, f, s): sage: f = v.shift(x, -2) Traceback (most recent call last): ... - ValueError: -s must not exceed the valuation of f + ValueError: since 2-adic Ring with capped relative precision 5 is not a field, -s must not exceed the valuation of f but 2 does exceed 0 Of course, the above example works over a field:: @@ -267,15 +267,16 @@ def shift(self, f, s): (2^-2 + O(2^3))*x """ - if f.parent() is not self.domain(): - raise ValueError("f must be in the domain of this valuation") - if -s > self(f) and self.domain().base_ring() is not self.domain().base_ring().fraction_field(): - raise ValueError("-s must not exceed the valuation of f") - if s not in self.value_group(): - raise ValueError("s must be in the value group of this valuation") + f = self.domain().coerce(f) + s = self.value_group()(s) + + from sage.categories.fields import Fields + if -s > self(f) and self.domain().base_ring() not in Fields(): + raise ValueError("since %r is not a field, -s must not exceed the valuation of f but %r does exceed %r"%(self.domain().base_ring(), -s, self(f))) return f.map_coefficients(lambda c:self._base_valuation.shift(c, s)) + # TODO: declare this upstairs def valuations(self, f): """ Return the valuations of the `f_i\phi^i` in the expansion `f=\sum f_i\phi^i`. @@ -317,8 +318,7 @@ def valuations(self, f): ValueError: f must not have leading zero coefficients """ - if f.parent() is not self.domain(): - raise ValueError("f must be in the domain of this valuation") + f = self.domain().coerce(f) for c in self.coefficients(f): yield self._base_valuation(self.domain().base_ring()(c)) @@ -331,6 +331,7 @@ def residue_ring(self): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: S. = Qp(2,5)[] sage: v = GaussValuation(S) sage: v.residue_ring() @@ -353,6 +354,7 @@ def reduce(self, f): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: S. = Qp(2,5)[] sage: v = GaussValuation(S) sage: f = x^2 + 2*x + 16 @@ -367,19 +369,18 @@ def reduce(self, f): sage: v.reduce(f) Traceback (most recent call last): ... - ValueError: reduction not defined for non-integral elements + ValueError: reduction not defined for non-integral elements and (2^-1 + O(2^4))*x^2 is not integral over Gauss valuation induced by 2-adic valuation .. SEEALSO:: :meth: `lift` """ - if f.parent() is not self.domain(): - raise ValueError("f must be in the domain of this valuation") + f = self.domain().coerce(f) if not all([v>=0 for v in self.valuations(f)]): - raise ValueError("reduction not defined for non-integral elements") + raise ValueError("reduction not defined for non-integral elements and %r is not integral over %r"%(f, self)) - return f.map_coefficients(lambda c:self._base_valuation.reduce(c), self.constant_valuation().residue_field()) + return f.map_coefficients(lambda c:self._base_valuation.reduce(c), self.restriction(self.domain().base_ring()).residue_field()) def lift(self, F): """ @@ -396,6 +397,7 @@ def lift(self, F): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: S. = Qp(3,5)[] sage: v = GaussValuation(S) sage: f = x^2 + 2*x + 16 @@ -417,6 +419,7 @@ def lift(self, F): return F.map_coefficients(lambda c:self._base_valuation.lift(c), self._base_valuation.domain()) + # TODO: declare this upstairs def lift_to_key(self, F): """ Lift the irreducible polynomial ``F`` to a key polynomial. @@ -434,41 +437,28 @@ def lift_to_key(self, F): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,10) sage: S. = R[] sage: v = GaussValuation(S) sage: y = v.residue_ring().gen() - sage: u0 = v.residue_field().gen() + sage: u0 = v.residue_ring().base_ring().gen() sage: f = v.lift_to_key(y^2 + y + u0); f (1 + O(2^10))*x^2 + (1 + O(2^10))*x + u + O(2^10) """ - if F.parent() is not self.residue_ring(): - raise ValueError("F must be an element of the residue ring of the valuation") + F = self.residue_ring().coerce(F) + if F.is_constant(): - raise ValueError("F must not be constant") + raise ValueError("F must not be constant but %r is constant"%(F,)) if not F.is_monic(): - raise ValueError("F must be monic") + raise ValueError("F must be monic but %r is not monic"%(F,)) if not F.is_irreducible(): - raise ValueError("F must be irreducible") + raise ValueError("F must be irreducible but %r factors"%(F,)) return self.lift(F) - def constant_valuation(self): - """ - Return the restriction of this valuations to the constants of its - domain. - - EXAMPLES:: - - sage: S. = Qp(3,5)[] - sage: v = GaussValuation(S) - sage: v.constant_valuation() - 3-adic valuation - - """ - return self._base_valuation - + # TODO: declare this upstairs def equivalence_unit(self, s): """ Return an equivalence unit of valuation ``s``. @@ -479,6 +469,7 @@ def equivalence_unit(self, s): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: S. = Qp(3,5)[] sage: v = GaussValuation(S) sage: v.equivalence_unit(2) @@ -487,12 +478,14 @@ def equivalence_unit(self, s): (3^-2 + O(3^3)) """ - ret = self._base_valuation.domain().one() - ret = self._base_valuation.shift(ret, s) + one = self._base_valuation.domain().one() + ret = self._base_valuation.shift(one, s) return self.domain()(ret) + # TODO: eliminate this element_with_valuation = equivalence_unit + # TODO: declare this upstairs def is_commensurable_inductive(self): """ Return whether this valuation is a commensurable inductive valuation @@ -505,6 +498,7 @@ def is_commensurable_inductive(self): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -519,6 +513,7 @@ def is_commensurable_inductive(self): """ return True + # TODO: declare this uptstairs def E(self): """ Return the ramification index of this valuation over its underlying @@ -526,6 +521,7 @@ def E(self): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -536,6 +532,7 @@ def E(self): from sage.rings.all import ZZ return ZZ.one() + # TODO: declare this upstairs def F(self): """ Return the degree of the residue field extension of this valuation @@ -543,6 +540,7 @@ def F(self): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -553,9 +551,9 @@ def F(self): from sage.rings.all import ZZ return ZZ.one() - def change_ring(self, base_ring): + def change_domain(self, ring): r""" - Change the base ring of this valuation to ``base_ring``. + Return this valuation as a valuation over ``ring``. EXAMPLES:: @@ -563,27 +561,62 @@ def change_ring(self, base_ring): sage: v = pAdicValuation(ZZ, 2) sage: R. = ZZ[] sage: w = GaussValuation(R, v) - sage: w.change_ring(QQ) + sage: w.change_domain(QQ['x']) Gauss valuation induced by 2-adic valuation """ - base_valuation = self._base_valuation.change_ring(base_ring) - return GaussValuation(self.domain().change_ring(base_ring), base_valuation) + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + if is_PolynomialRing(ring) and ring.ngens() == 1: + base_valuation = self._base_valuation.change_domain(ring.base_ring()) + return GaussValuation(self.domain().change_ring(ring.base_ring()), base_valuation) + return super(GaussValuation_generic, self).change_domain(ring) def extensions(self, ring): + r""" + Return the extensions of this valuation to ``ring``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 2) + sage: R. = ZZ[] + sage: w = GaussValuation(R, v) + sage: w.extensions(GaussianIntegers()['x']) + [Gauss valuation induced by 2-adic valuation] + + """ from sage.rings.polynomial.polynomial_ring import is_PolynomialRing - if not is_PolynomialRing(ring) and len(ring.gens()) != 1: - raise NotImplementedError("Can not compute extensions of %r to a ring that is not a univariate polynomial ring such as %r"%(self, ring)) - if not self.domain().is_subring(ring): - raise ValueError("Extension must be to a larger ring but %r is not a subring of %r"%(self.domain(), ring)) - return [GaussValuation(ring, w) for w in self._base_valuation.extensions(ring.base())] + if is_PolynomialRing(ring) and ring.ngens() == 1: + if self.domain().is_subring(ring): + return [GaussValuation(ring, w) for w in self._base_valuation.extensions(ring.base_ring())] + return super(GaussValuation_generic, self).extensions(ring) + + def restriction(self, ring): + r""" + Return the restriction of this valuation to ``ring``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 2) + sage: R. = ZZ[] + sage: w = GaussValuation(R, v) + sage: w.restriction(ZZ) + 2-adic valuation + + """ + if ring is self.domain().base_ring(): + return self._base_valuation + return super(GaussValuation_generic, self).restriction(ring) + # TODO: declare this upstairs def is_gauss_valuation(self): r""" Return whether this valuation is a Gauss valuation. EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -593,18 +626,52 @@ def is_gauss_valuation(self): """ return True + # TODO: declare this upstairs under a better name def _augmentations(self): r""" EXAMPLES:: - sage: TODO + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v._augmentations() + [Gauss valuation induced by 2-adic valuation] + """ return [self] def is_trivial(self): + r""" + Return whether this is a trivial valuation (sending everything but zero + to zero.) + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v.is_trivial() + True + + """ return self._base_valuation.is_trivial() + # TODO: declare this upstairs under a better name def _make_monic_integral(self, G): + r""" + Return a polynomial ``G`` which defines the self extension of the base + ring of the domain of this valuation but which is monic and integral. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v._make_monic_integral(5*x^2 + 1/2*x + 1/4) + x^2 + 1/5*x + 1/5 + + """ if not G.is_monic(): # this might fail if the base ring is not a field G = G / G.leading_coefficient() @@ -617,12 +684,42 @@ def _make_monic_integral(self, G): return G def _ge_(self, other): + r""" + Return whether this valuation is greater than or equal to ``other`` + everywhere. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = GaussValuation(R, pAdicValuation(QQ, 3)) + sage: v >= w + False + sage: w >= v + False + + """ if isinstance(other, GaussValuation_generic): return self._base_valuation >= other._base_valuation from augmented_valuation import AugmentedValuation_generic if isinstance(other, AugmentedValuation_generic): return False - raise NotImplementedError("Operator not implemented for these valuations.") + if other.is_trivial(): + return other.is_discrete_valuation() + return super(GaussValuation_generic, self)._ge_(other) def is_discrete_valuation(self): + r""" + Return whether this is a discrete valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v.is_discrete_valuation() + True + + """ return True diff --git a/limit_valuation.py b/limit_valuation.py index 263ee4ceada..3a485461626 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -900,7 +900,7 @@ def _repr_(self): unique_approximant = augmentations[:l+1] break if unique_approximant[0].is_gauss_valuation(): - unique_approximant[0] = unique_approximant[0].constant_valuation() + unique_approximant[0] = unique_approximant[0].restriction(unique_approximant[0].domain().base_ring()) if len(unique_approximant) == 1: return repr(unique_approximant[0]) from augmented_valuation import AugmentedValuation_generic diff --git a/padic_valuation.py b/padic_valuation.py index c1d9c713b6e..75c78fb3904 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -282,7 +282,7 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime) # To obtain uniqueness of p-adic valuations, we need a canonical # description of v. We consider all extensions of vK to L and select # the one approximated by v. - vK = v.constant_valuation() + vK = v.restriction(v.domain().base_ring()) approximants = vK.mac_lane_approximants(L.relative_polynomial()) approximant = vK.mac_lane_approximant(L.relative_polynomial(), v, approximants=approximants) @@ -640,7 +640,7 @@ def is_totally_ramified(self, G, include_steps=False, assume_squarefree=False): else: return ret - def change_ring(self, ring): + def change_domain(self, ring): r""" Change the domain of this valuation to ``ring`` if possible. @@ -648,7 +648,7 @@ def change_ring(self, ring): sage: from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) - sage: v.change_ring(QQ).domain() + sage: v.change_domain(QQ).domain() Rational Field """ @@ -985,7 +985,7 @@ def __init__(self, parent, approximant, G, approximants): """ FiniteExtensionFromLimitValuation.__init__(self, parent, approximant, G, approximants) - pAdicValuation_base.__init__(self, parent, approximant.constant_valuation().p()) + pAdicValuation_base.__init__(self, parent, approximant.restriction(approximant.domain().base_ring()).p()) def _to_base_domain(self, f): r""" diff --git a/valuation.py b/valuation.py index ad3b7e9d0fc..4207541595f 100644 --- a/valuation.py +++ b/valuation.py @@ -543,7 +543,7 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): sage: G = x^2 + 1 sage: v.mac_lane_approximants(G) [[ Gauss valuation induced by 5-adic valuation, v(x + 2) = 1 ], [ Gauss valuation induced by 5-adic valuation, v(x + 3) = 1 ]] - sage: v.mac_lane_approximants(G, precision_cap=5) + sage: v.mac_lane_approximants(G, precision_cap=5) # long time [[ Gauss valuation induced by 5-adic valuation, v(x + 2057) = 5 ], [ Gauss valuation induced by 5-adic valuation, v(x + 1068) = 6 ]] @@ -685,7 +685,7 @@ def mac_lane_approximant(self, G, valuation, approximants = None): [ Gauss valuation induced by 2-adic valuation, v(x + 3) = 2 ] """ - if valuation.constant_valuation() != self: + if valuation.restriction(valuation.domain().base_ring()) is not self: raise ValueError # Check thet valuation is an approximant for a valuation @@ -712,9 +712,9 @@ def mac_lane_approximant(self, G, valuation, approximants = None): smaller_approximants = [w for w in approximants if w <= valuation] if len(smaller_approximants) > 1: - raise ValueError("The valuation %r is not approximated by a unique extension of %r with respect to %r"%(valuation, valuation.constant_valuation(), G)) + raise ValueError("The valuation %r is not approximated by a unique extension of %r with respect to %r"%(valuation, self, G)) if len(smaller_approximants) == 0: - raise ValueError("The valuation %r is not related to an extension of %r with respect to %r"%(valuation, valuation.constant_valuation(), G)) + raise ValueError("The valuation %r is not related to an extension of %r with respect to %r"%(valuation, self, G)) assert len(smaller_approximants) == 1 return smaller_approximants[0] diff --git a/valuation_space.py b/valuation_space.py index e739cc96e8b..8756806d6b8 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -218,7 +218,7 @@ def _element_constructor_(self, x): if isinstance(x.parent(), DiscretePseudoValuationSpace): if x.domain() is not self.domain(): try: - return self(x.change_ring(self.domain())) + return self(x.change_domain(self.domain())) except NotImplementedError: pass else: @@ -551,7 +551,7 @@ def restriction(self, ring): return self raise NotImplementedError("restricting %r from %r to %r not implemented"%(self, self.domain(), ring)) - def change_ring(self, ring): + def change_domain(self, ring): r""" Return this valuation over ``ring``. @@ -563,12 +563,16 @@ def change_ring(self, ring): sage: from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 3) - sage: v.change_ring(ZZ) + sage: v.change_domain(ZZ) 3-adic valuation """ if ring is self.domain(): return self + if self.domain().is_subring(ring): + return self.extension(ring) + if ring.is_subring(self.domain()): + return self.restriction(ring) raise NotImplementedError("changing %r from %r to %r not implemented"%(self, self.domain(), ring)) def _test_add(self, **options): @@ -820,20 +824,20 @@ def _test_extension(self, **options): tester.assertEqual(self.extension(self.domain()), self) tester.assertEqual(self.extensions(self.domain()), [self]) - def _test_change_ring(self, **options): + def _test_change_domain(self, **options): r""" - Check the correctness of :meth:`change_ring`. + Check the correctness of :meth:`change_domain`. TESTS:: sage: from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) - sage: v._test_change_ring() + sage: v._test_change_domain() """ tester = self._tester(**options) - tester.assertEqual(self.change_ring(self.domain()), self) + tester.assertEqual(self.change_domain(self.domain()), self) def _test_no_infinite_nonzero(self, **options): r""" From 2a07432a6ef4040c5abf0dd4a557e1f1b7546e03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 1 Nov 2016 20:32:56 -0500 Subject: [PATCH 060/740] Split developing valuation into parts that are actually about inductive valuations and parts that are note --- TODO | 1 + __init__.py | 2 +- augmented_valuation.py | 32 +- developing_valuation.py | 889 +++--------------------------------- function_field_valuation.py | 6 +- gauss_valuation.py | 28 +- inductive_valuation.py | 882 +++++++++++++++++++++++++++++++++++ limit_valuation.py | 8 +- valuation.py | 17 +- 9 files changed, 982 insertions(+), 883 deletions(-) create mode 100644 TODO create mode 100644 inductive_valuation.py diff --git a/TODO b/TODO new file mode 100644 index 00000000000..3715d2df7e5 --- /dev/null +++ b/TODO @@ -0,0 +1 @@ +* Eliminate isinstance calls from code (usually by moving things like _repr_ or _ge_ up in the hierarchy) diff --git a/__init__.py b/__init__.py index d4763ce1dbc..d1be5c09257 100644 --- a/__init__.py +++ b/__init__.py @@ -12,7 +12,7 @@ from .trivial_valuation import TrivialDiscreteValuation, TrivialDiscretePseudoValuation from .function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldFromLimitValuation from .limit_valuation import LimitValuation, MacLaneLimitValuation, FiniteExtensionFromInfiniteValuation, FiniteExtensionFromLimitValuation, LimitValuation_generic -from .augmented_valuation import AugmentedValuation_generic, InfiniteAugmentedValuation +from .augmented_valuation import FiniteAugmentedValuation, InfiniteAugmentedValuation from .gauss_valuation import GaussValuation_generic from .valuation import DiscretePseudoValuation, DiscreteValuation, InfiniteDiscretePseudoValuation from .padic_valuation import pAdicValuation_base, pAdicValuation_int, pAdicValuation_padic, pAdicFromLimitValuation diff --git a/augmented_valuation.py b/augmented_valuation.py index 2de2db2a704..75b27e4252c 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -32,7 +32,8 @@ sys.path.append(os.getcwd()) sys.path.append(os.path.dirname(os.getcwd())) -from developing_valuation import DevelopingValuation, _lift_to_maximal_precision +from developing_valuation import _lift_to_maximal_precision +from inductive_valuation import FiniteInductiveValuation, InfiniteInductiveValuation, InductiveValuation from valuation import InfiniteDiscretePseudoValuation, DiscreteValuation from sage.misc.cachefunc import cached_method @@ -46,7 +47,7 @@ def create_key(self, base_valuation, phi, mu, check=True): if not is_key: raise ValueError(reason) if mu <= base_valuation(phi): - raise ValueError("the value of the key polynomial must strictly increase but `%s` does not exceed `%s`."%(phi, v(phi))) + raise ValueError("the value of the key polynomial must strictly increase but `%s` does not exceed `%s`."%(mu, base_valuation(phi))) return base_valuation, phi, mu @@ -56,13 +57,13 @@ def create_object(self, version, key): from valuation_space import DiscretePseudoValuationSpace parent = DiscretePseudoValuationSpace(base_valuation.domain()) if mu < infinity: - return parent.__make_element_class__(AugmentedValuation_generic)(parent, base_valuation, phi, mu) + return parent.__make_element_class__(FiniteAugmentedValuation)(parent, base_valuation, phi, mu) else: return parent.__make_element_class__(InfiniteAugmentedValuation)(parent, base_valuation, phi, mu) AugmentedValuation = AugmentedValuationFactory("AugmentedValuation") -class AugmentedValuation_generic(DevelopingValuation, DiscreteValuation): +class AugmentedValuation_base(InductiveValuation): """ An augmented valuation is a discrete valuation on a polynomial ring. It extends another discrete valuation `v` by setting the valuation of a @@ -111,7 +112,7 @@ def __init__(self, parent, v, phi, mu): if mu is not infinity: mu = QQ(mu) - DevelopingValuation.__init__(self, parent, phi) + InductiveValuation.__init__(self, parent, phi) self._base_valuation = v self._mu = mu @@ -157,7 +158,7 @@ def _call_(self, f): # this optimization does only pay off for polynomials of large degree: if f.degree() // self.phi().degree() <= 3: - return DevelopingValuation._call_(self, f) + return super(AugmentedValuation_base, self)._call_(f) ret = infinity @@ -303,12 +304,12 @@ def element_with_valuation(self, s): def _latex_(self): vals = [self] v = self - while isinstance(v, AugmentedValuation_generic): + while isinstance(v, AugmentedValuation_base): v = v._base_valuation vals.append(v) vals.reverse() from sage.misc.latex import latex - vals = [ "v_%s(%s) = %s"%(i,latex(v._phi), latex(v._mu)) if isinstance(v, AugmentedValuation_generic) else latex(v) for i,v in enumerate(vals) ] + vals = [ "v_%s(%s) = %s"%(i,latex(v._phi), latex(v._mu)) if isinstance(v, AugmentedValuation_base) else latex(v) for i,v in enumerate(vals) ] return "[ %s ]"%", ".join(vals) def _repr_(self): @@ -326,7 +327,7 @@ def _repr_(self): """ vals = self._augmentations() - vals = [ "v(%s) = %s"%(v._phi, v._mu) if isinstance(v, AugmentedValuation_generic) else str(v) for v in vals ] + vals = [ "v(%s) = %s"%(v._phi, v._mu) if isinstance(v, AugmentedValuation_base) else str(v) for v in vals ] return "[ %s ]"%", ".join(vals) def _augmentations(self): @@ -915,14 +916,14 @@ def extensions(self, ring): # self(phi) = self._mu, i.e., w(phi) = w(unit) + sum e_i * w(f_i) where # the sum runs over all the factors in the equivalence decomposition of phi # Solving for mu gives - mu = (self._mu - v(F.unit()) - sum([ee*v(ff) for ee,ff in F if ff != f])) / e + mu = (self._mu - v(F.unit()) - sum([ee*v(ff) for ff,ee in F if ff != f])) / e ret.append(AugmentedValuation(v, f, mu)) return ret def restriction(self, ring): if ring is self.domain().base_ring(): return self._base_valuation.restriction(ring) - return super(AugmentedValuation_generic, self).restriction(ring) + return super(AugmentedValuation_base, self).restriction(ring) def uniformizer(self): return self.element_with_valuation(self.value_group()._generator) @@ -940,18 +941,21 @@ def _ge_(self, other): return other.is_discrete_valuation() if isinstance(other, GaussValuation_generic): return self._base_valuation >= other - if isinstance(other, AugmentedValuation_generic): + if isinstance(other, AugmentedValuation_base): if self(other._phi) >= other._mu: return self >= other._base_valuation else: return False - return super(AugmentedValuation_generic, self)._ge_(other) + return super(AugmentedValuation_base, self)._ge_(other) def is_discrete_valuation(self): from sage.rings.all import infinity return self._mu != infinity -class InfiniteAugmentedValuation(AugmentedValuation_generic, InfiniteDiscretePseudoValuation): +class FiniteAugmentedValuation(AugmentedValuation_base, FiniteInductiveValuation): + pass + +class InfiniteAugmentedValuation(AugmentedValuation_base, InfiniteInductiveValuation): pass diff --git a/developing_valuation.py b/developing_valuation.py index a4156c77aa3..8344fb5b67e 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -1,4 +1,5 @@ -""" +# -*- coding: utf-8 -*- +r""" Valuations on polynomial rings based on `\phi`-adic expansions This file implements a base class for discrete valuations on polynomial rings, @@ -8,20 +9,9 @@ - Julian Rueth (15-04-2013): initial version -REFERENCES: - -.. [ML1936] Mac Lane, S. (1936). A construction for prime ideals as absolute -values of an algebraic field. Duke Mathematical Journal, 2(3), 492-510. - -.. [ML1936'] MacLane, S. (1936). A construction for absolute values in -polynomial rings. Transactions of the American Mathematical Society, 40(3), -363-395. - -TODO: Check that things work out when v is a pseudo-valuation! - """ #***************************************************************************** -# Copyright (C) 2013 Julian Rueth +# Copyright (C) 2013-2016 Julian Rüth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -41,7 +31,7 @@ from sage.misc.cachefunc import cached_method def _lift_to_maximal_precision(c): - """ + r""" Lift ``c`` to maximal precision if the parent is not exact. EXAMPLES:: @@ -61,54 +51,47 @@ def _lift_to_maximal_precision(c): return c if c.parent().is_exact() else c.lift_to_precision() class DevelopingValuation(DiscretePseudoValuation): - """ - An abstract base class for a discrete valuation of polynomials defined over + r""" + Abstract base class for a discrete valuation of polynomials defined over the polynomial ring ``domain`` by the `\phi`-adic development. - INPUT: + EXAMPLES:: - - ``domain`` -- a polynomial ring + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 7)) - - ``phi`` -- a monic element of ``domain`` + TESTS:: - EXAMPLES:: - - sage: from sage.rings.padics.developing_valuation import DevelopingValuation - sage: R = Zp(2,5) - sage: S. = R[] - sage: DevelopingValuation(S, x) - `(1 + O(2^5))*x`-adic valuation of Univariate Polynomial Ring in x over 2-adic Ring with capped relative precision 5 + sage: TestSuite(v).run() """ def __init__(self, parent, phi): - """ - Initialization. - + r""" TESTS:: - sage: from sage.rings.padics.developing_valuation import DevelopingValuation - sage: R = Zp(2,5) - sage: S. = R[] - sage: v = DevelopingValuation(S, x) - sage: type(v) - + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 7)) + sage: isinstance(v, DevelopingValuation) """ domain = parent.domain() + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + if not is_PolynomialRing(domain): + raise TypeError("domain must be a polynomial ring but %r is not"%(domain,)) + if not domain.ngens() == 1: + raise NotImplementedError("domain must be a univariate polynomial ring but %r is not"%(domain, )) - if phi.parent() is not domain: - raise ValueError("phi must be in the domain of the valuation") - if phi.is_constant(): - raise ValueError("phi must not be constant") - if not phi.leading_coefficient().is_one(): - raise ValueError("phi must be monic") + phi = domain.coerce(phi) + if phi.is_constant() or not phi.is_monic(): + raise ValueError("phi must be a monic non-constant polynomial but %r is not"%(phi,)) DiscretePseudoValuation.__init__(self, parent) - self._phi = phi def phi(self): - """ + r""" Return the polynomial `\phi`, the key polynomial of this valuation. EXAMPLES:: @@ -123,12 +106,12 @@ def phi(self): return self._phi def effective_degree(self, f): - """ + r""" Return the effective degree of ``f`` with respect to this valuation. The effective degree of `f` is the largest `i` such that the valuation of `f` and the valuation of `f_i\phi^i` in the development `f=\sum_j - f_j\phi^j` coincide. + f_j\phi^j` coincide (see [ML1936'] p.497.) INPUT: @@ -145,689 +128,16 @@ def effective_degree(self, f): 0 """ - # defined on p.497 of [ML1936'] - if f.parent() is not self.domain(): - raise ValueError("f must be in the domain of the valuation") + f = self.domain().coerce(f) + if f.is_zero(): raise ValueError("the effective degree is only defined for non-zero polynomials") v = self(f) return [i for i,w in enumerate(self.valuations(f)) if w == v][-1] - @cached_method - def is_equivalence_irreducible(self, f): - """ - Return whether the polynomial ``f`` is equivalence irreducible, i.e., - whether its :meth:`equivalence_decomposition` is irreducible. - - INPUT: - - - ``f`` -- a polynomial in the domain of this valuation - - EXAMPLES:: - - sage: R. = Qq(4,5) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: v.is_equivalence_irreducible(x) - True - sage: v.is_equivalence_irreducible(x^2) - False - sage: v.is_equivalence_irreducible(x^2 + 2) - False - - """ - if f.parent() is not self.domain(): - raise ValueError("f must be in the domain of the valuation") - if f.is_constant(): - raise ValueError("f must not be constant") - - from sage.misc.cachefunc import cache_key - key = cache_key(f) - - if self.equivalence_decomposition.is_in_cache(key): - F = self.equivalence_decomposition(f) - return len(F) <= 1 and (len(F) == 0 or F[0][1] == 1) - - if self.is_commensurable_inductive(): - # use the characterization of Theorem 13.1 in [ML1936] - if not f.is_monic(): - raise NotImplementedError("is_equivalence_irreducible() only implemented for monic polynomials") - - # special case: phi is factor of f - if self.valuations(f).next() > self(f): - f = f-self.coefficients(f).next() - assert self.phi().divides(f) - f,_ = f.quo_rem(self.phi()) - return f.is_constant() - - R = self.equivalence_unit(-self(f)) - - # check irreducibility in reduction - F = self.reduce(f*R) - F = F.factor() - if len(F) > 1 or (len(F) and F[0][1] > 1): - return False - - return True - - raise NotImplementedError("is_equivalence_irreducible() only implemented for inductive values") - - def is_equivalence_unit(self, f): - """ - Return whether ``f`` is an equivalence unit, i.e., an element of - :meth:`effective_degree` zero. - - INPUT: - - - ``f`` -- a polynomial in the domain of this valuation - - EXAMPLES:: - - sage: R = Zp(2,5) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: v.is_equivalence_unit(x) - False - sage: v.is_equivalence_unit(S.zero()) - False - sage: v.is_equivalence_unit(2*x + 1) - True - - """ - # defined on p.497 of [ML1936'] - - f = self.domain().coerce(f) - - if f.is_zero(): - return False - return self.effective_degree(f) == 0 - - def equivalence_reciprocal(self, f): - """ - Return an equivalence reciprocal of ``f``. - - An equivalence reciprocal of `f` is a polynomial `h` such that `f\cdot - h` is equivalent to 1 modulo this valuation. - - INPUT: - - - ``f`` -- a polynomial in the domain of this valuation which is an - equivalence unit - - EXAMPLES:: - - sage: R = Zp(3,5) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: f = 3*x + 2 - sage: h = v.equivalence_reciprocal(f); h - 2 + 3 + 3^2 + 3^3 + 3^4 + O(3^5) - sage: v.is_equivalent(f*h, 1) - True - - In an extended valuation over an extension field:: - - sage: R. = Qq(4,5) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: v = v.extension(x^2 + x + u, 1) - sage: f = 2*x + u - sage: h = v.equivalence_reciprocal(f); h - (u + 1) + (u + 1)*2 + 2^2 + u*2^3 + 2^4 + O(2^5) - sage: v.is_equivalent(f*h, 1) - True - - Extending the valuation once more:: - - sage: v = v.extension((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) - sage: h = v.equivalence_reciprocal(f); h - (u + 1) + (u + 1)*2 + (u + 1)*2^2 + (u + 1)*2^3 + u*2^4 + O(2^5) - sage: v.is_equivalent(f*h, 1) - True - - TESTS: - - A case that caused problems at some point:: - - sage: K = Qp(2, 4) - sage: R. = K[] - sage: L. = K.extension(x^4 + 4*x^3 + 6*x^2 + 4*x + 2) - sage: R. = L[] - sage: v = GaussValuation(R) - sage: w = v.extension(t + 1, 5/4) - sage: w = w.extension(t^4 + (a^8 + a^12 + a^14 + a^16 + a^17 + a^19 + a^20 + a^23)*t^3 + (a^6 + a^9 + a^13 + a^15 + a^18 + a^19 + a^21)*t^2 + a^10*t + 1 + a^4 + a^5 + a^8 + a^13 + a^14 + a^15, 17/2) - sage: f = a^-15*t^2 + (a^-11 + a^-9 + a^-6 + a^-5 + a^-3 + a^-2)*t + a^-15 - sage: f_ = w.equivalence_reciprocal(f); f_ - (a^10 + a^13 + a^14 + a^17 + a^18 + a^19 + a^20 + a^24 + a^25 + O(a^26))*t^2 + a^10 + a^13 + a^18 + a^19 + a^22 + a^23 + a^24 + a^25 + O(a^26) - sage: w.reduce(f*f_) - 1 - sage: f = f.parent()([f[0],f[1].add_bigoh(1),f[2]]) - sage: f_ = w.equivalence_reciprocal(f); f_ - (a^10 + a^13 + a^14 + a^17 + a^18 + a^19 + a^20 + a^24 + a^25 + O(a^26))*t^2 + a^10 + a^13 + a^18 + a^19 + a^22 + a^23 + a^24 + a^25 + O(a^26) - sage: w.reduce(f*f_) - 1 - - .. SEEALSO:: - - :meth:`is_equivalence_unit` - - """ - # defined on p.497 of [ML1936'] - if f.parent() is not self.domain(): - raise ValueError("f must be in the domain of the valuation") - if not self.is_equivalence_unit(f): - raise ValueError("f must be an equivalence unit") - - e0 = self.coefficients(f).next() - one,g,h = self.phi().xgcd(e0) - assert one.is_one() - - # it might be the case that f*h has non-zero valuation because h has - # insufficient precision, so we must not assert that here but only - # until we lifted to higher precision - - # We do not actually need g*phi + h*e0 = 1, it is only important that - # the RHS is 1 in reduction. - # This allows us to do two things: - # - we may lift h to arbitrary precision - # - we can add anything which times e0 has positive valuation, e.g., we - # may drop coefficients of positive valuation - h = h.map_coefficients(lambda c:_lift_to_maximal_precision(c)) - h = h.parent()([ c if self(e0*c) <= 0 else c.parent().zero() for c in h.coefficients(sparse=False)]) - - assert self(f*h) == 0 - assert self(f*h - 1) > 0 - - return h - - @cached_method - def augmentation(self, phi, mu, check=True): - """ - Return the inductive valuation which extends this valuation by mapping - ``phi`` to ``mu``. - - INPUT: - - - ``phi`` -- a polynomial in the domain of this valuation; this must be - a key polynomial, see :meth:`is_key` for properties of key - polynomials. - - - ``mu`` -- a rational number, the valuation of ``phi`` in the extended - valuation - - - ``check`` -- whether or not to check the correctness of the - parameters - - EXAMPLES:: - - sage: R. = Qq(4,5) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: v = v.extension(x^2 + x + u, 1) - sage: v = v.extension((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) - sage: v - [ Gauss valuation induced by 2-adic valuation, v((1 + O(2^5))*x^2 + (1 + O(2^5))*x + u + O(2^5)) = 1, v((1 + O(2^5))*x^4 + (2^2 + O(2^6))*x^3 + (1 + (u + 1)*2 + O(2^5))*x^2 + ((u + 1)*2^2 + O(2^6))*x + (u + 1) + (u + 1)*2 + (u + 1)*2^2 + (u + 1)*2^3 + (u + 1)*2^4 + O(2^5)) = 3 ] - sage: v.residue_field() - Univariate Quotient Polynomial Ring in u2 over Univariate Quotient Polynomial Ring in u1 over Finite Field in u0 of size 2^2 with modulus u1^2 + u1 + u0 with modulus u2^2 + u1*u2 + u1 - - TESTS: - - Make sure that we do not make the assumption that the degrees of the - key polynomials are strictly increasing:: - - sage: v_K = pAdicValuation(QQ,3) - sage: A. = QQ[] - sage: v0 = GaussValuation(A,v_K) - sage: f2 = 9*t+30*t^3+27*t^6+15*t^8 - sage: f3 = 3+30*t^2+18*t^3+198*t^5+180*t^7+189*t^8+342*t^10+145*t^12 - sage: F = f2*f3 - - sage: v1 = v0.extension(t,1/12) - sage: v2 = v1.extension(t^12+3,7/6) - sage: v3 = v2.extension(t^12+3*t^2+3,9/4) - sage: v4 = v1.extension(t^12+3*t^2+3,9/4) - sage: v3 == v4 # rather: check for equivalence - True - sage: v4.equivalence_decomposition(F) - sage: v3.equivalence_decomposition(F) - - .. SEEALSO:: - - :class:`AugmentedValuation` - - """ - from augmented_valuation import AugmentedValuation - return AugmentedValuation(self, phi, mu, check) - - def is_key(self, phi, explain=False): - """ - Return whether ``phi`` is a key polynomial for this valuation. - - A key polynomial must satisfy the following conditions: - - - it must be monic - - it must be equivalence-irreducible (see :meth:`is_equivalence_irreducible`) - - it must be minimal (see :meth:`is_minimal`) - - INPUT: - - - ``phi`` -- a polynomial in the domain of this valuation - - - ``explain`` -- a boolean (default: ``False``), if ``True``, return a - string explaining why ``phi`` is not a key polynomial - - EXAMPLES:: - - sage: R. = Qq(4,5) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: v.is_key(x) - True - sage: v.is_key(2*x, explain = True) - (False, 'phi must be monic') - sage: v.is_key(x^2, explain = True) - (False, 'phi must be equivalence irreducible') - - sage: w = v.extension(x, 1) - sage: w.is_key(x + 1, explain = True) - (False, 'phi must be minimal') - - """ - if phi.parent() is not self.domain(): - raise ValueError("phi must be in the domain of the valuation") - - reason = None - - if not phi.is_monic(): - reason = "phi must be monic" - elif not self.is_equivalence_irreducible(phi): - reason = "phi must be equivalence irreducible" - elif not self.is_minimal(phi): - reason = "phi must be minimal" - - if explain: - return reason is None, reason - else: - return reason is None - - @abstract_method - def is_commensurable_inductive(self): - """ - Return whether this valuation is a commensurable inductive valuation - over the discrete valuation of the base ring of the polynomial ring, as - defined in section 4 of [ML1936]. - - EXAMPLES:: - - sage: R. = Qq(4,5) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: v.is_commensurable_inductive() - True - sage: w = v.extension(x, 1) - sage: w.is_commensurable_inductive() - True - - REFERENCES: - - .. [ML1936] Mac Lane, S. (1936). A construction for prime ideals as absolute - values of an algebraic field. Duke Mathematical Journal, 2(3), 492-510. - - """ - pass - - def is_minimal(self, f): - """ - Return whether the polynomial ``f`` is minimal with respect to this - valuation, as defined in definition 4.1 of [ML1936]. - - INPUT: - - - ``f`` -- a polynomial in the domain of this valuation - - ALGORITHM: - - When ``f`` :meth:`is_equivalence_irreducible` for this valuation, then - Theorem 9.4 of [ML1936'] describes what to do. TODO: what if not? - - EXAMPLES:: - - sage: R. = Qq(4,5) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: v.is_minimal(x + 1) - True - sage: w = v.extension(x, 1) - sage: w.is_minimal(x + 1) - False - - TODO: An example that failed for Stefan: - - sage: K = Qp(2,10) - sage: R. = K[] - sage: vp=pAdicValuation(K) - sage: v0 = GaussValuation(R,vp) - sage: f=x^5+x^4+2 - sage: v1 = v0.extension(x,1/4) - sage: v2 = v1.extension(x^4+2,5/4) - sage: v2.is_minimal(f) - False - - TODO: Add examples for polynomials which have the same degree as the - key polynomial (See Stefan's mail Sep 8 2016). - - """ - if f.parent() is not self.domain(): - raise ValueError("f must be in the domain of the valuation") - if f.is_constant(): - raise ValueError("f must not be constant") - - if self.is_commensurable_inductive(): - # use the characterization of theorem 9.4 in [ML1936] - if not f.is_monic(): - raise NotImplementedError("is_minimal() only implemented for monic polynomials") - if not self.is_equivalence_irreducible(f): - raise NotImplementedError("is_minimal() only implemented for equivalence-irreducible polynomials") - from gauss_valuation import GaussValuation - if self.is_gauss_valuation(): - if f.is_monic(): - if self(f) < 0: - raise NotImplementedError("Investigate what is the right behaviour here") - return self.reduce(f).is_irreducible() - if self.is_equivalent(self.phi(), f): - # TODO: reference new Lemma - return f.degree() == self.phi().degree() - else: - return list(self.valuations(f))[-1] == self(f) and list(self.coefficients(f))[-1].is_constant() and list(self.valuations(f))[0] == self(f) and self.tau().divides(len(list(self.coefficients(f)))-1) - - raise NotImplementedError("is_minimal() only implemented for commensurable inductive values") - - @cached_method - def equivalence_decomposition(self, f): - r""" - Return an equivalence decomposition of ``f``, i.e., a polynomial - `g(x)=e(x)\prod_i \phi_i(x)` with `e(x)` an equivalence unit (see - :meth:`is_equivalence_unit()`) and the `\phi_i` key polynomials (see - :meth:`is_key`) such that ``f`` :meth:`is_equivalent` to `g`. - - INPUT: - - - ``f`` -- a polynomial in the domain of this valuation - - ALGORITHM: - - We use the algorithm described in Theorem 4.4 of [ML1936']. After - removing all factors `\phi` from a polynomial `f`, there is an - equivalence unit `R` such that `Rf` has valuation zero. Now `Rf` can be - factored as `\prod_i \alpha_i` over the :meth:`residue_field`. Lifting - all `\alpha_i` to key polynomials `\phi_i` gives `Rf=\prod_i R_i f_i` - for suitable equivalence units `R_i` (see :meth:`lift_to_key`). Taking - `R'` an :meth:`equivalence_reciprocal` of `R`, we have `f` equivalent - to `(R'\prod_i R_i)\prod_i\phi_i`. - - EXAMPLES:: - - sage: R. = Qq(4,10) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: v.equivalence_decomposition(S.zero()) - Traceback (most recent call last): - ... - ValueError: equivalence decomposition of zero is not defined - sage: v.equivalence_decomposition(S.one()) - 1 + O(2^10) - sage: v.equivalence_decomposition(x^2+2) - ((1 + O(2^10))*x)^2 - sage: v.equivalence_decomposition(x^2+1) - ((1 + O(2^10))*x + 1 + O(2^10))^2 - - A polynomial that is an equivalence unit, is returned as the unit part - of a :class:`sage.structure.factorization.Factorization`, leading to a unit - non-minimal degree:: - - sage: w = v.extension(x, 1) - sage: F = w.equivalence_decomposition(x^2+1); F - (1 + O(2^10))*x^2 + 1 + O(2^10) - sage: F.unit() - (1 + O(2^10))*x^2 + 1 + O(2^10) - - However, if the polynomial has a non-unit factor, then the unit might - be replaced by a factor of lower degree:: - - sage: f = x * (x^2 + 1) - sage: F = w.equivalence_decomposition(f); F - (1 + O(2^10))*x - sage: F.unit() - 1 + O(2^10) - - Examples over an iterated unramified extension: - - sage: v = v.extension(x^2 + x + u, 1) - sage: v = v.extension((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) - - sage: v.equivalence_decomposition(x) - (1 + O(2^10))*x - sage: F = v.equivalence_decomposition( v.phi() ) - sage: len(F) - 1 - sage: F = v.equivalence_decomposition( v.phi() * (x^4 + 4*x^3 + (7 + 2*u)*x^2 + (8 + 4*u)*x + 1023 + 3*u) ) - sage: len(F) - 2 - - TESTS:: - - sage: R. = QQ[] - sage: K1.=NumberField(x^3-2) - sage: K.=K1.galois_closure() - sage: R.=K[] - sage: vp=pAdicValuation(QQ,2) - sage: vp=vp.extension(K) - sage: v0=GaussValuation(R,vp) - sage: G=x^36 + 36*x^35 + 630*x^34 + 7144*x^33 + 59055*x^32 + 379688*x^31 +1978792*x^30 + 8604440*x^29 + 31895428*x^28 + 102487784*x^27 + 289310720*x^26 + 725361352*x^25 + 1629938380*x^24 + 3307417800*x^23 + 6098786184*x^22+10273444280*x^21 + 15878121214*x^20 + 22596599536*x^19 + 29695703772*x^18 +36117601976*x^17 + 40722105266*x^16 + 42608585080*x^15 + 41395961848*x^14 +37344435656*x^13 + 31267160756*x^12 + 24271543640*x^11 + 17439809008*x^10 + 11571651608*x^9 + 7066815164*x^8 + 3953912472*x^7 + 2013737432*x^6 + 925014888*x^5 + 378067657*x^4 + 134716588*x^3 + 40441790*x^2 + 9532544*x + 1584151 - sage: v1=v0.mac_lane_step(G)[0] - sage: V=v1.mac_lane_step(G) - sage: v2=V[0] - sage: v2.equivalence_decomposition(G) - (x^4 + 4*x^3 + 6*x^2 + 4*x + alpha^4 + alpha^3 + 1)^3 * (x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*alpha^4 + alpha^3 - 27*alpha + 1)^3 * (x^4 + 4*x^3 + 6*x^2 + 4*x + 3/2*alpha^4 + alpha^3 - 27*alpha + 1)^3 - - REFERENCES: - - .. [ML1936'] MacLane, S. (1936). A construction for absolute values in - polynomial rings. Transactions of the American Mathematical Society, 40(3), - 363-395. - - """ - if f.parent() is not self.domain(): - raise ValueError("f must be in the domain of the valuation") - if f.is_zero(): - raise ValueError("equivalence decomposition of zero is not defined") - - from sage.structure.factorization import Factorization - if not self.domain().base_ring().is_field(): - v = self.extension(self.domain().change_ring(self.domain().base_ring().fraction_field())) - ret = v.equivalence_decomposition(v.domain()(f)) - return Factorization([(g.change_ring(self.domain().base_ring()),e) for g,e in ret], unit=ret.unit().change_ring(self.domain().base_ring())) - - if self.is_equivalence_unit(f): - return Factorization([],unit=f) - - if not self.is_commensurable_inductive(): - raise NotImplementedError("only implemented for inductive valuations") - - f0 = f # used to check correctness of the output - - phi_divides = 0 - while self.valuations(f).next() > self(f): - f = f-self.coefficients(f).next() - assert self.phi().divides(f) - f,_ = f.quo_rem(self.phi()) - phi_divides += 1 - - R = self.equivalence_unit(-self(f)) - R_ = self.equivalence_reciprocal(R) - - F = self.reduce(f*R) - F = F.factor() - from sage.misc.misc import verbose - verbose("%s factors as %s = %s in reduction"%(f0,F.prod(),F),caller_name="equivalence_decomposition") - unit = F.unit() - - F = list(F) - unit = self.lift( self.residue_ring()(unit) ) - - from sage.misc.all import prod - unit *= self.lift(self.residue_ring()(prod([ psi.leading_coefficient()**e for psi,e in F ]))) - F = [(self.lift_to_key(psi/psi.leading_coefficient()),e) for psi,e in F] - - unit *= R_ * prod([self.equivalence_unit(-self(g))**e for g,e in F]) - - if phi_divides: - for i,(g,e) in enumerate(F): - if g == self.phi(): - F[i] = (self.phi(),e+phi_divides) - break - else: - F.append((self.phi(),phi_divides)) - - ret = Factorization(F, unit=unit) - assert self.is_equivalent(ret.prod(), f0) # this might fail because of leading zeros - assert self.is_equivalence_unit(ret.unit()) - return ret - - def minimal_representative(self, f): - """ - Return a minimal representative for ``f``, i.e., a pair `e, a` - such that ``f`` :meth:`is_equivalent`` to `e a`, `e` is - an equivalence unit and `a` is minimal and monic. - - INPUT: - - - ``f`` -- a polynomial in the domain of this valuation - - OUTPUT: - - A factorization which has `e` as its unit and `a` as its unique factor. - - ALGORITHM: - - We use the algorithm described in the proof of Lemma 4.1 of [ML1936']. - In the expansion `f=\sum_i f_i\phi^i` take `e=f_i` for the largest `i` - with `f_i\phi^i` minimal (see :meth:`effective_degree`). - Let `h` be the :meth:`equivalence_reciprocal` of `e` and take `a` given - by the terms of minimal valuation in the expansion of `e f`. - - EXAMPLES:: - - sage: R. = Qq(4,10) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: v.minimal_representative(x + 2) - (1 + O(2^10))*x - - sage: v = v.extension(x, 1) - sage: v.minimal_representative(x + 2) - (1 + O(2^10))*x + 2 + O(2^11) - sage: f = x^3 + 6*x + 4 - sage: F = v.minimal_representative(f); F - (2 + 2^2 + O(2^11)) * ((1 + O(2^10))*x + 2 + 2^2 + 2^4 + 2^6 + 2^8 + 2^10 + O(2^11)) - sage: v.is_minimal(F[0][0]) - True - sage: v.is_equivalent(F[0][0], f) - True - - REFERENCES: - - .. [ML1936'] MacLane, S. (1936). A construction for absolute values in - polynomial rings. Transactions of the American Mathematical Society, 40(3), - 363-395. - - """ - if f.parent() is not self.domain(): - raise ValueError("f must be in the domain of the valuation") - if f.is_zero(): - raise ValueError("the minimal representative of zero is not defined") - - if not self.is_commensurable_inductive(): - raise NotImplemented("only implemented for inductive valuations") - - f0 = f - e = list(self.coefficients(f))[self.effective_degree(f)] - f *= self.equivalence_reciprocal(e).map_coefficients(lambda c:_lift_to_maximal_precision(c)) - - coeffs = [c if v == self(f) else c.parent().zero() for v,c in zip(self.valuations(f),self.coefficients(f))] - coeffs[self.effective_degree(f0)] = self.domain().base_ring().one() - ret = sum([c*self._phi**i for i,c in enumerate(coeffs)]) - assert self.effective_degree(ret) == self.effective_degree(f0) - assert ret.is_monic(), coeffs - assert self.is_minimal(ret) - from sage.structure.factorization import Factorization - ret = Factorization([(ret,1)],unit=e) - # assert self.is_equivalent(ret.prod(), f0) -- this might fail because of leading zeros - assert self((ret.prod() - f0).map_coefficients(lambda c:_lift_to_maximal_precision(c))) - return ret - - def _normalize_leading_coefficients(self, f): - """ - This method removes leading zero coefficients from ``f`` when - appropriate. - - INPUT: - - - ``f`` -- a polynomial in the domain of this valuation - - OUTPUT: - - ``f`` with leading zero coefficients removed. - - .. NOTE:: - - When ``f`` has leading zero coefficients, one could argue that we - should never strip these but they often arise naturally, e.g., when - when working with expressions as ``g-g`` or ``(g+c)-g``. We strip - such coefficients if they are zero to sufficient precision. To be - precise, if their precision exceeds the valuation of any other - coefficient. - It is not clear that this is the right way to do this. - - EXAMPLES:: - - sage: R = Qp(2,5) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: g = x - sage: list(v.coefficients(g-g)) # indirect doctest - [] - sage: (g-g).list() - [0, O(2^5)] - sage: f = x*R(0,1) + R(1,2); f - 1 + O(2^2) - sage: list(v.coefficients(f)) # indirect doctest - [1 + O(2^2)] - sage: f = x*R(0,1) + R(2,2); f - 2 + O(2^2) - sage: list(v.coefficients(f)) # indirect doctest - Traceback (most recent call last): - ... - ValueError: f must not have leading zero coefficients - - """ - if len(f.list()) > f.degree()+1: - from sage.rings.all import infinity - # f has leading zero coefficients - v = self.restriction(self.domain().base_ring()) - m = min([v(c) for c in f.list()[f.degree()+1:]]) - if f.is_zero(): - f= f.parent().zero() - elif m is infinity or m > max([v(c) for c in f.list()[:f.degree()+1]]): - f= self.domain()(f.list()[:f.degree()+1]) - else: - raise ValueError("f must not have leading zero coefficients") - - return f - def coefficients(self, f): - """ + r""" Return the `\phi`-adic expansion of ``f``. INPUT: @@ -854,7 +164,6 @@ def coefficients(self, f): """ if f.parent() is not self.domain(): raise ValueError("f must be in the domain of the valuation") - f = self._normalize_leading_coefficients(f) if self.phi().degree() == 1: from itertools import imap @@ -863,7 +172,7 @@ def coefficients(self, f): return self.__coefficients(f) def __coefficients(self, f): - """ + r""" Helper method for :meth:`coefficients` to create an iterator if `\phi` is not linear. @@ -902,7 +211,7 @@ def __quo_rem_monomial(self, degree): return f.quo_rem(self.phi()) def newton_polygon(self, f): - """ + r""" Return the newton polygon the `\phi`-adic development of ``f``. INPUT:: @@ -936,7 +245,7 @@ def newton_polygon(self, f): return NewtonPolygon(enumerate(self.valuations(f))) def _call_(self, f): - """ + r""" Evaluate this valuation at ``f``. INPUT:: @@ -970,8 +279,12 @@ def _call_(self, f): return min(self.valuations(f)) + @abstract_method + def valuations(self, f): + pass + def _repr_(self): - """ + r""" Return a printable representation of this valuation. EXAMPLES:: @@ -990,120 +303,22 @@ def _make_monic_integral(self, G): return G raise NotImplementedError("The polynomial %r is not monic integral and %r does not provide the means to rewrite it to a monic integral polynomial."%(G, self)) - def mac_lane_step(self, G, assume_squarefree=False): + def _test_effective_degree(self, **options): r""" + Test the correctness of :meth:`effective_degree`. - TESTS:: - - sage: K.=FunctionField(QQ) - sage: S.=K[] - sage: F=y^2-x^2-x^3-3 - sage: v0=GaussValuation(K._ring,pAdicValuation(QQ,3)) - sage: v1=v0.augmentation(K._ring.gen(),1/3) - sage: from sage.rings.padics.function_field_valuation import RationalFunctionFieldValuation - sage: mu0=RationalFunctionFieldValuation(K,v1) - sage: eta0=GaussValuation(S,mu0) - sage: eta1=eta0.mac_lane_step(F)[0] - sage: eta2=eta1.mac_lane_step(F)[0] + EXAMPLES:: + sage: R = Zp(2,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v._test_effective_degree() + """ - from sage.misc.misc import verbose - verbose("Expanding %s towards %s"%(self,G),caller_name="mac_lane_step") - R = G.parent() - if R is not self.domain(): - raise ValueError("G must be defined over the domain of this valuation") - if not G.is_monic(): - # G must be monic, there is no fundamental reason for this, but the implementation makes this assumption in some places. - # We try to turn G into a monic integral polynomial that describes the same extension - return self.mac_lane_step(self._make_monic_integral(G), assume_squarefree=assume_squarefree) - if self(G) < 0: - # G must be integral, otherwise, e.g., the effective degree is too low - # We try to turn G into a monic integral polynomial that describes the same extension - return self.mac_lane_step(self._make_monic_integral(G), assume_squarefree=assume_squarefree) - assert not G.is_constant() - if not assume_squarefree and not G.is_squarefree(): - raise ValueError("G must be squarefree") - - from sage.rings.all import infinity - - if self(G) is infinity: - raise ValueError("G must not have valuation infinity") - - if self.is_key(G): - return [self.augmentation(G, infinity)] - - F = self.equivalence_decomposition(G) - assert len(F), "%s equivalence-decomposese as an equivalence-unit %s"%(G,F) - - ret = [] - for phi,e in F: - if G == phi: - # something strange happened here: - # G is not a key (we checked that before) but phi==G is; so phi must have less precision than G - # this can happen if not all coefficients of G have the same precision - # if we drop some precision of G then it will be a key - assert not G.base_ring().is_exact() - prec = min([c.precision_absolute() for c in phi.list()]) - g = G.map_coefficients(lambda c:c.add_bigoh(prec)) - assert self.is_key(g) - return [self.augmentation(g, infinity)] - - if phi == self.phi(): - # a factor phi in the equivalence decomposition means that we - # founnd an actual factor of G, i.e., we can set - # v(phi)=infinity - # However, this should already have happened in the last step - # (when this polynomial had -infinite slope in the Newton - # polygon.) - from gauss_valuation import GaussValuation - if self.is_gauss_valuation(): # unless in the first step - pass - else: - continue - - verbose("Determining the valuation for %s"%phi,level=2,caller_name="mac_lane_step") - w = self.augmentation(phi, self(phi), check=False) - NP = w.newton_polygon(G).principal_part() - verbose("Newton-Polygon for v(phi)=%s : %s"%(self(phi),NP),level=2,caller_name="mac_lane_step") - # assert len(NP) - slopes = NP.slopes(repetition=False) - if NP.vertices()[0][0] != 0: - slopes = [-infinity] + slopes - if not slopes: - q,r = G.quo_rem(phi) - assert not r.is_zero() - phi = phi.coefficients(sparse=False) - for i,c in enumerate(r.coefficients(sparse=False)): - if not c.is_zero(): - v = w(c) - # for a correct result we need to add O(pi^v) in degree i - # we try to find the coefficient of phi where such an error can be introduced without losing much absolute precision on phi - best = i - for j in range(i): - if w(q[j]) < w(q[best]): - best = j - # now add the right O() to phi in degree i-best - phi[i-best] = phi[i-best].add_bigoh(w(c)-w(q[best])) - - phi = G.parent()(phi) - w = self._base_valuation.augmentation(phi, infinity) - ret.append(w) + tester = self._tester(**options) + S = tester.some_elements(self.domain().base_ring().some_elements()) + for x in S: + if x == 0: continue + tester.assertEqual(self.effective_degree(x), 0) - for i in range(len(slopes)): - slope = slopes[i] - verbose("Slope = %s"%slope,level=3,caller_name="mac_lane_step") - new_mu = self(phi) - slope - base = self - if phi.degree() == base.phi().degree(): - assert new_mu > self(phi) - from gauss_valuation import GaussValuation - if not base.is_gauss_valuation(): - base = base._base_valuation - - new_leaf = base.augmentation(phi, new_mu) - assert slope is -infinity or 0 in new_leaf.newton_polygon(G).slopes(repetition=False) - ret.append(new_leaf) - - assert ret - return ret diff --git a/function_field_valuation.py b/function_field_valuation.py index b4410151be0..7f3fdd2c2db 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -594,9 +594,9 @@ def _repr_(self): (x^2 + 1)-adic valuation """ - from sage.rings.valuation.augmented_valuation import AugmentedValuation_generic + from sage.rings.valuation.augmented_valuation import AugmentedValuation_base from sage.rings.valuation.gauss_valuation import GaussValuation - if isinstance(self._base_valuation, AugmentedValuation_generic): + if isinstance(self._base_valuation, AugmentedValuation_base): if self._base_valuation._base_valuation == GaussValuation(self.domain()._ring, TrivialValuation(self.domain().constant_field())): if self._base_valuation._mu == 1: return "(%r)-adic valuation"%(self._base_valuation.phi()) @@ -653,7 +653,7 @@ class InfiniteRationalFunctionFieldValuation(RationalFunctionFieldValuation_base TESTS:: - sage: TestSuite(v).run() + sage: TestSuite(v).run() # long time """ def _call_(self, f): diff --git a/gauss_valuation.py b/gauss_valuation.py index 01815612e81..1993c2d2b74 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -48,7 +48,7 @@ sys.path.append(os.getcwd()) sys.path.append(os.path.dirname(os.getcwd())) -from developing_valuation import DevelopingValuation +from inductive_valuation import FiniteInductiveValuation from sage.misc.cachefunc import cached_method from sage.structure.unique_representation import UniqueRepresentation @@ -133,7 +133,7 @@ def create_object(self, version, key, **extra_args): GaussValuation = GaussValuationFactory("GaussValuation") -class GaussValuation_generic(DevelopingValuation): +class GaussValuation_generic(FiniteInductiveValuation): """ A Gauss valuation on a polynomial ring ``domain``. @@ -173,7 +173,7 @@ def __init__(self, parent, v): True """ - DevelopingValuation.__init__(self, parent, parent.domain().gen()) + FiniteInductiveValuation.__init__(self, parent, parent.domain().gen()) self._base_valuation = v @@ -299,24 +299,6 @@ def valuations(self, f): sage: list(v.valuations(f)) [4, 1, 0] - TESTS: - - The treatment of (inexact) zero values is slightly complicated, see - :meth:`DevelopingValuation._normalize_leading_coefficients`:: - - sage: list(v.valuations(S.zero())) - [] - sage: list(v.valuations(S([R(0,1),R(0,2)]))) - [] - sage: list(v.valuations(S([R(0,2),R(0,1)]))) - [] - sage: list(v.valuations(S([R(1,1),R(0,1)]))) - [0] - sage: list(v.valuations(S([R(4,3),R(0,1)]))) - Traceback (most recent call last): - ... - ValueError: f must not have leading zero coefficients - """ f = self.domain().coerce(f) @@ -702,8 +684,8 @@ def _ge_(self, other): """ if isinstance(other, GaussValuation_generic): return self._base_valuation >= other._base_valuation - from augmented_valuation import AugmentedValuation_generic - if isinstance(other, AugmentedValuation_generic): + from augmented_valuation import AugmentedValuation_base + if isinstance(other, AugmentedValuation_base): return False if other.is_trivial(): return other.is_discrete_valuation() diff --git a/inductive_valuation.py b/inductive_valuation.py new file mode 100644 index 00000000000..dcfb3a0d7e4 --- /dev/null +++ b/inductive_valuation.py @@ -0,0 +1,882 @@ +# -*- coding: utf-8 -*- +r""" +Inductive valuations on polynomial rings + +This module provides functionality for inductive valuations, i.e., finite +chains of :class:`AugmentedValuation`s on top of a :class:`GaussValuation`. + +AUTHORS: + +- Julian Rueth (01-11-2016): initial version + +REFERENCES: + +.. [ML1936] Mac Lane, S. (1936). A construction for prime ideals as absolute +values of an algebraic field. Duke Mathematical Journal, 2(3), 492-510. + +.. [ML1936'] MacLane, S. (1936). A construction for absolute values in +polynomial rings. Transactions of the American Mathematical Society, 40(3), +363-395. + +""" +#***************************************************************************** +# Copyright (C) 2016 Julian Rüth +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) +import sys, os +if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: + sys.path.append(os.getcwd()) + sys.path.append(os.path.dirname(os.getcwd())) + +from valuation import DiscreteValuation, InfiniteDiscretePseudoValuation +from developing_valuation import DevelopingValuation + +from sage.misc.cachefunc import cached_method + +class InductiveValuation(DevelopingValuation): + r""" + Abstract base class for iterated :class:`AugmentedValuation` on top of a + :class:`GaussValuation`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 5)) + + TESTS:: + + sage: TestSuite(v).run() + + """ + def is_equivalence_unit(self, f): + r""" + Return whether ``f`` is an equivalence unit, i.e., an element of + :meth:`effective_degree` zero (see [ML1936'] p.497.) + + INPUT: + + - ``f`` -- a polynomial in the domain of this valuation + + EXAMPLES:: + + sage: R = Zp(2,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.is_equivalence_unit(x) + False + sage: v.is_equivalence_unit(S.zero()) + False + sage: v.is_equivalence_unit(2*x + 1) + True + + """ + f = self.domain().coerce(f) + + if f.is_zero(): + return False + return self.effective_degree(f) == 0 + + def equivalence_reciprocal(self, f): + r""" + Return an equivalence reciprocal of ``f``. + + An equivalence reciprocal of `f` is a polynomial `h` such that `f\cdot + h` is equivalent to 1 modulo this valuation (see [ML1936'] p.497.) + + INPUT: + + - ``f`` -- a polynomial in the domain of this valuation which is an + :meth:`equivalence_unit` + + EXAMPLES:: + + sage: R = Zp(3,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: f = 3*x + 2 + sage: h = v.equivalence_reciprocal(f); h + 2 + 3 + 3^2 + 3^3 + 3^4 + O(3^5) + sage: v.is_equivalent(f*h, 1) + True + + In an extended valuation over an extension field:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v = v.extension(x^2 + x + u, 1) + sage: f = 2*x + u + sage: h = v.equivalence_reciprocal(f); h + (u + 1) + (u + 1)*2 + 2^2 + u*2^3 + 2^4 + O(2^5) + sage: v.is_equivalent(f*h, 1) + True + + Extending the valuation once more:: + + sage: v = v.extension((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) + sage: h = v.equivalence_reciprocal(f); h + (u + 1) + (u + 1)*2 + (u + 1)*2^2 + (u + 1)*2^3 + u*2^4 + O(2^5) + sage: v.is_equivalent(f*h, 1) + True + + TESTS: + + A case that caused problems at some point:: + + sage: K = Qp(2, 4) + sage: R. = K[] + sage: L. = K.extension(x^4 + 4*x^3 + 6*x^2 + 4*x + 2) + sage: R. = L[] + sage: v = GaussValuation(R) + sage: w = v.extension(t + 1, 5/4) + sage: w = w.extension(t^4 + (a^8 + a^12 + a^14 + a^16 + a^17 + a^19 + a^20 + a^23)*t^3 + (a^6 + a^9 + a^13 + a^15 + a^18 + a^19 + a^21)*t^2 + a^10*t + 1 + a^4 + a^5 + a^8 + a^13 + a^14 + a^15, 17/2) + sage: f = a^-15*t^2 + (a^-11 + a^-9 + a^-6 + a^-5 + a^-3 + a^-2)*t + a^-15 + sage: f_ = w.equivalence_reciprocal(f); f_ + (a^10 + a^13 + a^14 + a^17 + a^18 + a^19 + a^20 + a^24 + a^25 + O(a^26))*t^2 + a^10 + a^13 + a^18 + a^19 + a^22 + a^23 + a^24 + a^25 + O(a^26) + sage: w.reduce(f*f_) + 1 + sage: f = f.parent()([f[0],f[1].add_bigoh(1),f[2]]) + sage: f_ = w.equivalence_reciprocal(f); f_ + (a^10 + a^13 + a^14 + a^17 + a^18 + a^19 + a^20 + a^24 + a^25 + O(a^26))*t^2 + a^10 + a^13 + a^18 + a^19 + a^22 + a^23 + a^24 + a^25 + O(a^26) + sage: w.reduce(f*f_) + 1 + + """ + f = self.domain().coerce(f) + + if not self.is_equivalence_unit(f): + raise ValueError("f must be an equivalence unit but %r is not"%(f,)) + + e0 = self.coefficients(f).next() + one,g,h = self.phi().xgcd(e0) + assert one.is_one() + + # it might be the case that f*h has non-zero valuation because h has + # insufficient precision, so we must not assert that here but only + # until we lifted to higher precision + + # We do not actually need g*phi + h*e0 = 1, it is only important that + # the RHS is 1 in reduction. + # This allows us to do two things: + # - we may lift h to arbitrary precision + # - we can add anything which times e0 has positive valuation, e.g., we + # may drop coefficients of positive valuation + h = h.map_coefficients(lambda c:_lift_to_maximal_precision(c)) + h = h.parent()([ c if self(e0*c) <= 0 else c.parent().zero() for c in h.coefficients(sparse=False)]) + + assert self(f*h) == 0 + assert self(f*h - 1) > 0 + + return h + + def minimal_representative(self, f): + r""" + Return a minimal representative for ``f``, i.e., a pair `e, a` + such that ``f`` :meth:`is_equivalent`` to `e a`, `e` is + an equivalence unit and `a` is minimal and monic. + + INPUT: + + - ``f`` -- a polynomial in the domain of this valuation + + OUTPUT: + + A factorization which has `e` as its unit and `a` as its unique factor. + + ALGORITHM: + + We use the algorithm described in the proof of Lemma 4.1 of [ML1936']. + In the expansion `f=\sum_i f_i\phi^i` take `e=f_i` for the largest `i` + with `f_i\phi^i` minimal (see :meth:`effective_degree`). + Let `h` be the :meth:`equivalence_reciprocal` of `e` and take `a` given + by the terms of minimal valuation in the expansion of `e f`. + + EXAMPLES:: + + sage: R. = Qq(4,10) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.minimal_representative(x + 2) + (1 + O(2^10))*x + + sage: v = v.extension(x, 1) + sage: v.minimal_representative(x + 2) + (1 + O(2^10))*x + 2 + O(2^11) + sage: f = x^3 + 6*x + 4 + sage: F = v.minimal_representative(f); F + (2 + 2^2 + O(2^11)) * ((1 + O(2^10))*x + 2 + 2^2 + 2^4 + 2^6 + 2^8 + 2^10 + O(2^11)) + sage: v.is_minimal(F[0][0]) + True + sage: v.is_equivalent(F[0][0], f) + True + + REFERENCES: + + .. [ML1936'] MacLane, S. (1936). A construction for absolute values in + polynomial rings. Transactions of the American Mathematical Society, 40(3), + 363-395. + + """ + if f.parent() is not self.domain(): + raise ValueError("f must be in the domain of the valuation") + if f.is_zero(): + raise ValueError("the minimal representative of zero is not defined") + + if not self.is_commensurable_inductive(): + raise NotImplemented("only implemented for inductive valuations") + + f0 = f + e = list(self.coefficients(f))[self.effective_degree(f)] + f *= self.equivalence_reciprocal(e).map_coefficients(lambda c:_lift_to_maximal_precision(c)) + + coeffs = [c if v == self(f) else c.parent().zero() for v,c in zip(self.valuations(f),self.coefficients(f))] + coeffs[self.effective_degree(f0)] = self.domain().base_ring().one() + ret = sum([c*self._phi**i for i,c in enumerate(coeffs)]) + assert self.effective_degree(ret) == self.effective_degree(f0) + assert ret.is_monic(), coeffs + assert self.is_minimal(ret) + from sage.structure.factorization import Factorization + ret = Factorization([(ret,1)],unit=e) + # assert self.is_equivalent(ret.prod(), f0) -- this might fail because of leading zeros + assert self((ret.prod() - f0).map_coefficients(lambda c:_lift_to_maximal_precision(c))) + return ret + + def _test_is_equivalence_unit(self, **options): + r""" + Test the correctness of :meth:`is_equivalence_unit`. + + EXAMPLES:: + + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v._test_is_equivalence_unit() + + """ + tester = self._tester(**options) + tester.assertFalse(self.is_equivalence_unit(self.phi())) + + def _test_equivalence_reciprocal(self, **options): + r""" + Test the correctness of :meth:`equivalence_reciprocal`. + + EXAMPLES:: + + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v._test_equivalence_reciprocal() + + """ + tester = self._tester(**options) + S = tester.some_elements(self.domain().some_elements()) + for f in S: + if self.is_equivalence_unit(f): + g = self.equivalence_reciprocal(f) + tester.assertEqual(self.reduce(f*g), 1) + +class FiniteInductiveValuation(InductiveValuation, DiscreteValuation): + r""" + Abstract base class for iterated :class:`AugmentedValuation` on top of a + :class:`GaussValuation` which is a discrete valuation, i.e., the last key + polynomial has finite valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + + TESTS:: + + sage: TestSuite(v).run() + + """ + def augmentation(self, phi, mu, check=True): + r""" + Return the inductive valuation which extends this valuation by mapping + ``phi`` to ``mu``. + + INPUT: + + - ``phi`` -- a polynomial in the domain of this valuation; this must be + a key polynomial, see :meth:`is_key` for properties of key + polynomials. + + - ``mu`` -- a rational number, the valuation of ``phi`` in the extended + valuation + + - ``check`` -- whether or not to check the correctness of the + parameters + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v = v.extension(x^2 + x + u, 1) + sage: v = v.extension((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) + sage: v + [ Gauss valuation induced by 2-adic valuation, v((1 + O(2^5))*x^2 + (1 + O(2^5))*x + u + O(2^5)) = 1, v((1 + O(2^5))*x^4 + (2^2 + O(2^6))*x^3 + (1 + (u + 1)*2 + O(2^5))*x^2 + ((u + 1)*2^2 + O(2^6))*x + (u + 1) + (u + 1)*2 + (u + 1)*2^2 + (u + 1)*2^3 + (u + 1)*2^4 + O(2^5)) = 3 ] + sage: v.residue_field() + Univariate Quotient Polynomial Ring in u2 over Univariate Quotient Polynomial Ring in u1 over Finite Field in u0 of size 2^2 with modulus u1^2 + u1 + u0 with modulus u2^2 + u1*u2 + u1 + + TESTS: + + Make sure that we do not make the assumption that the degrees of the + key polynomials are strictly increasing:: + + sage: v_K = pAdicValuation(QQ,3) + sage: A. = QQ[] + sage: v0 = GaussValuation(A,v_K) + sage: f2 = 9*t+30*t^3+27*t^6+15*t^8 + sage: f3 = 3+30*t^2+18*t^3+198*t^5+180*t^7+189*t^8+342*t^10+145*t^12 + sage: F = f2*f3 + + sage: v1 = v0.extension(t,1/12) + sage: v2 = v1.extension(t^12+3,7/6) + sage: v3 = v2.extension(t^12+3*t^2+3,9/4) + sage: v4 = v1.extension(t^12+3*t^2+3,9/4) + sage: v3 == v4 # rather: check for equivalence + True + sage: v4.equivalence_decomposition(F) + sage: v3.equivalence_decomposition(F) + + .. SEEALSO:: + + :class:`AugmentedValuation` + + """ + from augmented_valuation import AugmentedValuation + return AugmentedValuation(self, phi, mu, check) + + def is_key(self, phi, explain=False): + r""" + Return whether ``phi`` is a key polynomial for this valuation. + + A key polynomial must satisfy the following conditions: + + - it must be monic + - it must be equivalence-irreducible (see :meth:`is_equivalence_irreducible`) + - it must be minimal (see :meth:`is_minimal`) + + INPUT: + + - ``phi`` -- a polynomial in the domain of this valuation + + - ``explain`` -- a boolean (default: ``False``), if ``True``, return a + string explaining why ``phi`` is not a key polynomial + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.is_key(x) + True + sage: v.is_key(2*x, explain = True) + (False, 'phi must be monic') + sage: v.is_key(x^2, explain = True) + (False, 'phi must be equivalence irreducible') + + sage: w = v.extension(x, 1) + sage: w.is_key(x + 1, explain = True) + (False, 'phi must be minimal') + + """ + if phi.parent() is not self.domain(): + raise ValueError("phi must be in the domain of the valuation") + + reason = None + + if not phi.is_monic(): + reason = "phi must be monic" + elif not self.is_equivalence_irreducible(phi): + reason = "phi must be equivalence irreducible" + elif not self.is_minimal(phi): + reason = "phi must be minimal" + + if explain: + return reason is None, reason + else: + return reason is None + + def is_minimal(self, f): + r""" + Return whether the polynomial ``f`` is minimal with respect to this + valuation, as defined in definition 4.1 of [ML1936]. + + INPUT: + + - ``f`` -- a polynomial in the domain of this valuation + + ALGORITHM: + + When ``f`` :meth:`is_equivalence_irreducible` for this valuation, then + Theorem 9.4 of [ML1936'] describes what to do. TODO: what if not? + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.is_minimal(x + 1) + True + sage: w = v.extension(x, 1) + sage: w.is_minimal(x + 1) + False + + TODO: An example that failed for Stefan: + + sage: K = Qp(2,10) + sage: R. = K[] + sage: vp=pAdicValuation(K) + sage: v0 = GaussValuation(R,vp) + sage: f=x^5+x^4+2 + sage: v1 = v0.extension(x,1/4) + sage: v2 = v1.extension(x^4+2,5/4) + sage: v2.is_minimal(f) + False + + TODO: Add examples for polynomials which have the same degree as the + key polynomial (See Stefan's mail Sep 8 2016). + + """ + if f.parent() is not self.domain(): + raise ValueError("f must be in the domain of the valuation") + if f.is_constant(): + raise ValueError("f must not be constant") + + if self.is_commensurable_inductive(): + # use the characterization of theorem 9.4 in [ML1936] + if not f.is_monic(): + raise NotImplementedError("is_minimal() only implemented for monic polynomials") + if not self.is_equivalence_irreducible(f): + raise NotImplementedError("is_minimal() only implemented for equivalence-irreducible polynomials") + from gauss_valuation import GaussValuation + if self.is_gauss_valuation(): + if f.is_monic(): + if self(f) < 0: + raise NotImplementedError("Investigate what is the right behaviour here") + return self.reduce(f).is_irreducible() + if self.is_equivalent(self.phi(), f): + # TODO: reference new Lemma + return f.degree() == self.phi().degree() + else: + return list(self.valuations(f))[-1] == self(f) and list(self.coefficients(f))[-1].is_constant() and list(self.valuations(f))[0] == self(f) and self.tau().divides(len(list(self.coefficients(f)))-1) + + raise NotImplementedError("is_minimal() only implemented for commensurable inductive values") + + def mac_lane_step(self, G, assume_squarefree=False): + r""" + + TESTS:: + + sage: K.=FunctionField(QQ) + sage: S.=K[] + sage: F=y^2-x^2-x^3-3 + sage: v0=GaussValuation(K._ring,pAdicValuation(QQ,3)) + sage: v1=v0.augmentation(K._ring.gen(),1/3) + sage: from sage.rings.padics.function_field_valuation import RationalFunctionFieldValuation + sage: mu0=RationalFunctionFieldValuation(K,v1) + sage: eta0=GaussValuation(S,mu0) + sage: eta1=eta0.mac_lane_step(F)[0] + sage: eta2=eta1.mac_lane_step(F)[0] + + """ + from sage.misc.misc import verbose + verbose("Expanding %s towards %s"%(self,G),caller_name="mac_lane_step") + R = G.parent() + if R is not self.domain(): + raise ValueError("G must be defined over the domain of this valuation") + if not G.is_monic(): + # G must be monic, there is no fundamental reason for this, but the implementation makes this assumption in some places. + # We try to turn G into a monic integral polynomial that describes the same extension + return self.mac_lane_step(self._make_monic_integral(G), assume_squarefree=assume_squarefree) + if self(G) < 0: + # G must be integral, otherwise, e.g., the effective degree is too low + # We try to turn G into a monic integral polynomial that describes the same extension + return self.mac_lane_step(self._make_monic_integral(G), assume_squarefree=assume_squarefree) + assert not G.is_constant() + if not assume_squarefree and not G.is_squarefree(): + raise ValueError("G must be squarefree") + + from sage.rings.all import infinity + + if self(G) is infinity: + raise ValueError("G must not have valuation infinity") + + if self.is_key(G): + return [self.augmentation(G, infinity)] + + F = self.equivalence_decomposition(G) + assert len(F), "%s equivalence-decomposese as an equivalence-unit %s"%(G,F) + + ret = [] + for phi,e in F: + if G == phi: + # something strange happened here: + # G is not a key (we checked that before) but phi==G is; so phi must have less precision than G + # this can happen if not all coefficients of G have the same precision + # if we drop some precision of G then it will be a key + assert not G.base_ring().is_exact() + prec = min([c.precision_absolute() for c in phi.list()]) + g = G.map_coefficients(lambda c:c.add_bigoh(prec)) + assert self.is_key(g) + return [self.augmentation(g, infinity)] + + if phi == self.phi(): + # a factor phi in the equivalence decomposition means that we + # founnd an actual factor of G, i.e., we can set + # v(phi)=infinity + # However, this should already have happened in the last step + # (when this polynomial had -infinite slope in the Newton + # polygon.) + from gauss_valuation import GaussValuation + if self.is_gauss_valuation(): # unless in the first step + pass + else: + continue + + verbose("Determining the valuation for %s"%phi,level=2,caller_name="mac_lane_step") + w = self.augmentation(phi, self(phi), check=False) + NP = w.newton_polygon(G).principal_part() + verbose("Newton-Polygon for v(phi)=%s : %s"%(self(phi),NP),level=2,caller_name="mac_lane_step") + # assert len(NP) + slopes = NP.slopes(repetition=False) + if NP.vertices()[0][0] != 0: + slopes = [-infinity] + slopes + if not slopes: + q,r = G.quo_rem(phi) + assert not r.is_zero() + phi = phi.coefficients(sparse=False) + for i,c in enumerate(r.coefficients(sparse=False)): + if not c.is_zero(): + v = w(c) + # for a correct result we need to add O(pi^v) in degree i + # we try to find the coefficient of phi where such an error can be introduced without losing much absolute precision on phi + best = i + for j in range(i): + if w(q[j]) < w(q[best]): + best = j + # now add the right O() to phi in degree i-best + phi[i-best] = phi[i-best].add_bigoh(w(c)-w(q[best])) + + phi = G.parent()(phi) + w = self._base_valuation.augmentation(phi, infinity) + ret.append(w) + continue + + for i in range(len(slopes)): + slope = slopes[i] + verbose("Slope = %s"%slope,level=3,caller_name="mac_lane_step") + new_mu = self(phi) - slope + base = self + if phi.degree() == base.phi().degree(): + assert new_mu > self(phi) + from gauss_valuation import GaussValuation + if not base.is_gauss_valuation(): + base = base._base_valuation + + new_leaf = base.augmentation(phi, new_mu) + assert slope is -infinity or 0 in new_leaf.newton_polygon(G).slopes(repetition=False) + ret.append(new_leaf) + + assert ret + return ret + + @cached_method + def is_equivalence_irreducible(self, f): + r""" + Return whether the polynomial ``f`` is equivalence irreducible, i.e., + whether its :meth:`equivalence_decomposition` is trivial. + + INPUT: + + - ``f`` -- a non-constant polynomial in the domain of this valuation + + EXAMPLES:: + + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.is_equivalence_irreducible(x) + True + sage: v.is_equivalence_irreducible(x^2) + False + sage: v.is_equivalence_irreducible(x^2 + 2) + False + + ALGORITHM: + + We do not actually compute the :meth:`equivalence_decomposition` of + ``f`` (unless we have it cached already) but use the + characterization of Theorem 13.1 in [ML1936], i.e., we only perform + a factorization in reduction without lifting the factors back to + key polynomials. + + """ + f = self.domain().coerce(f) + if f.is_constant(): + raise ValueError("f must not be constant") + + from sage.misc.cachefunc import cache_key + key = cache_key(f) + + if self.equivalence_decomposition.is_in_cache(key): + F = self.equivalence_decomposition(f) + return len(F) <= 1 and (len(F) == 0 or F[0][1] == 1) + + # base change from R[x] to K[x], so divisions work and sufficient + # elements of negative valuation exist + v = self.extension(self.domain().change_ring(self.domain().base_ring().fraction_field())) + if v is not self: + return v.is_equivalence_irreducible(f) + + if not f.is_monic(): + # if f is not monic we can safely drop the leading coefficient since a + # constant is always an equivalence-unit + f = v.domain().coerce(f) + return self.is_equivalence_irreducible(f / f.leading_coefficient()) + + if self.valuations(f).next() > self(f): + # phi is an equivalence-factor of f + f = f-self.coefficients(f).next() + assert self.phi().divides(f) + f,_ = f.quo_rem(self.phi()) + return self.effective_degree(f) == 0 + + R = self.equivalence_unit(-self(f)) + + # check irreducibility in reduction + F = self.reduce(f*R) + F = F.factor() + if len(F) > 1 or (len(F) and F[0][1] > 1): + return False + + return True + + @cached_method + def equivalence_decomposition(self, f): + r""" + Return an equivalence decomposition of ``f``, i.e., a polynomial + `g(x)=e(x)\prod_i \phi_i(x)` with `e(x)` an equivalence unit (see + :meth:`is_equivalence_unit()`) and the `\phi_i` key polynomials (see + :meth:`is_key`) such that ``f`` :meth:`is_equivalent` to `g`. + + INPUT: + + - ``f`` -- a polynomial in the domain of this valuation + + ALGORITHM: + + We use the algorithm described in Theorem 4.4 of [ML1936']. After + removing all factors `\phi` from a polynomial `f`, there is an + equivalence unit `R` such that `Rf` has valuation zero. Now `Rf` can be + factored as `\prod_i \alpha_i` over the :meth:`residue_field`. Lifting + all `\alpha_i` to key polynomials `\phi_i` gives `Rf=\prod_i R_i f_i` + for suitable equivalence units `R_i` (see :meth:`lift_to_key`). Taking + `R'` an :meth:`equivalence_reciprocal` of `R`, we have `f` equivalent + to `(R'\prod_i R_i)\prod_i\phi_i`. + + EXAMPLES:: + + sage: R. = Qq(4,10) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.equivalence_decomposition(S.zero()) + Traceback (most recent call last): + ... + ValueError: equivalence decomposition of zero is not defined + sage: v.equivalence_decomposition(S.one()) + 1 + O(2^10) + sage: v.equivalence_decomposition(x^2+2) + ((1 + O(2^10))*x)^2 + sage: v.equivalence_decomposition(x^2+1) + ((1 + O(2^10))*x + 1 + O(2^10))^2 + + A polynomial that is an equivalence unit, is returned as the unit part + of a :class:`sage.structure.factorization.Factorization`, leading to a unit + non-minimal degree:: + + sage: w = v.extension(x, 1) + sage: F = w.equivalence_decomposition(x^2+1); F + (1 + O(2^10))*x^2 + 1 + O(2^10) + sage: F.unit() + (1 + O(2^10))*x^2 + 1 + O(2^10) + + However, if the polynomial has a non-unit factor, then the unit might + be replaced by a factor of lower degree:: + + sage: f = x * (x^2 + 1) + sage: F = w.equivalence_decomposition(f); F + (1 + O(2^10))*x + sage: F.unit() + 1 + O(2^10) + + Examples over an iterated unramified extension: + + sage: v = v.extension(x^2 + x + u, 1) + sage: v = v.extension((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) + + sage: v.equivalence_decomposition(x) + (1 + O(2^10))*x + sage: F = v.equivalence_decomposition( v.phi() ) + sage: len(F) + 1 + sage: F = v.equivalence_decomposition( v.phi() * (x^4 + 4*x^3 + (7 + 2*u)*x^2 + (8 + 4*u)*x + 1023 + 3*u) ) + sage: len(F) + 2 + + TESTS:: + + sage: R. = QQ[] + sage: K1.=NumberField(x^3-2) + sage: K.=K1.galois_closure() + sage: R.=K[] + sage: vp=pAdicValuation(QQ,2) + sage: vp=vp.extension(K) + sage: v0=GaussValuation(R,vp) + sage: G=x^36 + 36*x^35 + 630*x^34 + 7144*x^33 + 59055*x^32 + 379688*x^31 +1978792*x^30 + 8604440*x^29 + 31895428*x^28 + 102487784*x^27 + 289310720*x^26 + 725361352*x^25 + 1629938380*x^24 + 3307417800*x^23 + 6098786184*x^22+10273444280*x^21 + 15878121214*x^20 + 22596599536*x^19 + 29695703772*x^18 +36117601976*x^17 + 40722105266*x^16 + 42608585080*x^15 + 41395961848*x^14 +37344435656*x^13 + 31267160756*x^12 + 24271543640*x^11 + 17439809008*x^10 + 11571651608*x^9 + 7066815164*x^8 + 3953912472*x^7 + 2013737432*x^6 + 925014888*x^5 + 378067657*x^4 + 134716588*x^3 + 40441790*x^2 + 9532544*x + 1584151 + sage: v1=v0.mac_lane_step(G)[0] + sage: V=v1.mac_lane_step(G) + sage: v2=V[0] + sage: v2.equivalence_decomposition(G) + (x^4 + 4*x^3 + 6*x^2 + 4*x + alpha^4 + alpha^3 + 1)^3 * (x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*alpha^4 + alpha^3 - 27*alpha + 1)^3 * (x^4 + 4*x^3 + 6*x^2 + 4*x + 3/2*alpha^4 + alpha^3 - 27*alpha + 1)^3 + + REFERENCES: + + .. [ML1936'] MacLane, S. (1936). A construction for absolute values in + polynomial rings. Transactions of the American Mathematical Society, 40(3), + 363-395. + + """ + if f.parent() is not self.domain(): + raise ValueError("f must be in the domain of the valuation") + if f.is_zero(): + raise ValueError("equivalence decomposition of zero is not defined") + + from sage.structure.factorization import Factorization + if not self.domain().base_ring().is_field(): + v = self.extension(self.domain().change_ring(self.domain().base_ring().fraction_field())) + ret = v.equivalence_decomposition(v.domain()(f)) + return Factorization([(g.change_ring(self.domain().base_ring()),e) for g,e in ret], unit=ret.unit().change_ring(self.domain().base_ring())) + + if self.is_equivalence_unit(f): + return Factorization([],unit=f) + + if not self.is_commensurable_inductive(): + raise NotImplementedError("only implemented for inductive valuations") + + f0 = f # used to check correctness of the output + + phi_divides = 0 + while self.valuations(f).next() > self(f): + f = f-self.coefficients(f).next() + assert self.phi().divides(f) + f,_ = f.quo_rem(self.phi()) + phi_divides += 1 + + R = self.equivalence_unit(-self(f)) + R_ = self.equivalence_reciprocal(R) + + F = self.reduce(f*R) + F = F.factor() + from sage.misc.misc import verbose + verbose("%s factors as %s = %s in reduction"%(f0,F.prod(),F),caller_name="equivalence_decomposition") + unit = F.unit() + + F = list(F) + unit = self.lift( self.residue_ring()(unit) ) + + from sage.misc.all import prod + unit *= self.lift(self.residue_ring()(prod([ psi.leading_coefficient()**e for psi,e in F ]))) + F = [(self.lift_to_key(psi/psi.leading_coefficient()),e) for psi,e in F] + + unit *= R_ * prod([self.equivalence_unit(-self(g))**e for g,e in F]) + + if phi_divides: + for i,(g,e) in enumerate(F): + if g == self.phi(): + F[i] = (self.phi(),e+phi_divides) + break + else: + F.append((self.phi(),phi_divides)) + + ret = Factorization(F, unit=unit) + assert self.is_equivalent(ret.prod(), f0) # this might fail because of leading zeros + assert self.is_equivalence_unit(ret.unit()) + return ret + + def _test_is_equivalence_irreducible(self, **options): + r""" + Test the correctness of :meth:`is_equivalence_irreducible`. + + EXAMPLES:: + + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v._test_is_equivalence_irreducible() + + """ + tester = self._tester(**options) + S = tester.some_elements(self.domain().some_elements()) + for f in S: + if f.is_constant(): continue + is_equivalence_irreducible = self.is_equivalence_irreducible(f) + F = self.equivalence_decomposition(f) + tester.assertEqual(is_equivalence_irreducible, len(F)==0 or (len(F)==1 and F[0][1]==1)) + if self.is_equivalence_unit(f): + tester.assertTrue(f.is_constant() or self.is_equivalence_irreducible(f)) + + tester.assertTrue(self.is_equivalence_irreducible(self.phi())) + tester.assertTrue(self.is_equivalence_irreducible(-self.phi())) + tester.assertFalse(self.is_equivalence_irreducible(self.phi() ** 2)) + + +class InfiniteInductiveValuation(InductiveValuation, InfiniteDiscretePseudoValuation): + r""" + Abstract base class for iterated :class:`AugmentedValuation` on top of a + :class:`GaussValuation` which is not discrete valuation, i.e., the last key + polynomial has infinite valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, infinity) + + TESTS:: + + sage: TestSuite(w).run() + + """ + pass + + +def _lift_to_maximal_precision(c): + r""" + Lift ``c`` to maximal precision if the parent is not exact. + + EXAMPLES:: + + sage: from sage.rings.padics.developing_valuation import _lift_to_maximal_precision + sage: R = Zp(2,5) + sage: x = R(1,2); x + 1 + O(2^2) + sage: _lift_to_maximal_precision(x) + 1 + O(2^5) + + sage: x = 1 + sage: _lift_to_maximal_precision(x) + 1 + + """ + return c if c.parent().is_exact() else c.lift_to_precision() + diff --git a/limit_valuation.py b/limit_valuation.py index 3a485461626..3521be9ecac 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -344,9 +344,9 @@ def _repr_(self): """ from sage.rings.all import infinity - from augmented_valuation import AugmentedValuation_generic + from augmented_valuation import AugmentedValuation_base if self._initial_approximation(self._G) < infinity: - if isinstance(self._initial_approximation, AugmentedValuation_generic): + if isinstance(self._initial_approximation, AugmentedValuation_base): return repr(self._initial_approximation)[:-1] + ", … ]" return repr(self._initial_approximation) @@ -903,7 +903,7 @@ def _repr_(self): unique_approximant[0] = unique_approximant[0].restriction(unique_approximant[0].domain().base_ring()) if len(unique_approximant) == 1: return repr(unique_approximant[0]) - from augmented_valuation import AugmentedValuation_generic - return "[ %s ]-adic valuation"%(", ".join("v(%r) = %r"%(v._phi, v._mu) if (isinstance(v, AugmentedValuation_generic) and v.domain() == self._base_valuation.domain()) else repr(v) for v in unique_approximant)) + from augmented_valuation import AugmentedValuation_base + return "[ %s ]-adic valuation"%(", ".join("v(%r) = %r"%(v._phi, v._mu) if (isinstance(v, AugmentedValuation_base) and v.domain() == self._base_valuation.domain()) else repr(v) for v in unique_approximant)) return "%s-adic valuation"%(self._base_valuation) diff --git a/valuation.py b/valuation.py index 4207541595f..4a7f1237943 100644 --- a/valuation.py +++ b/valuation.py @@ -296,6 +296,21 @@ def _ge_(self, other): # by id. __reduce__ = object.__reduce__ + def _test_valuation_inheritance(self, **options): + r""" + Test that every instance of this class is either a + :class:`InfiniteDiscretePseudoValuation` or a + :class:`DiscreteValuation`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(QQ, 2)._test_valuation_inheritance() + + """ + tester = self._tester(**options) + tester.assertTrue(isinstance(self, InfiniteDiscretePseudoValuation) != isinstance(self, DiscreteValuation)) + class InfiniteDiscretePseudoValuation(DiscretePseudoValuation): r""" Abstract base class for infinite discrete pseudo-valuations, i.e., discrete @@ -483,7 +498,7 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): sage: G = x^4 + 2*x^3 + 2*x^2 - 2*x + 2 sage: v.mac_lane_approximants(G) [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = 1/4 ]] - sage: v.mac_lane_approximants(G,infinity) + sage: v.mac_lane_approximants(G, infinity) [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = 1/4, v((1 + O(2^10))*x^4 + (2 + O(2^11))*x^3 + (2 + O(2^11))*x^2 + (2 + 2^2 + 2^3 + 2^4 + 2^5 + 2^6 + 2^7 + 2^8 + 2^9 + 2^10 + O(2^11))*x + (2 + O(2^11))) = +Infinity ]] The factorization of primes in the Gaussian integers can be read off From 631fdb52ea739d0fe1706434354543b12b397874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 1 Nov 2016 21:26:23 -0500 Subject: [PATCH 061/740] Updated copyright notices and UTF-8 headers --- __init__.py | 11 +++++++++++ augmented_valuation.py | 5 +++-- developing_valuation.py | 2 +- domains_with_valuation.py | 1 - function_field_valuation.py | 2 +- gauss_valuation.py | 5 +++-- inductive_valuation.py | 2 +- limit_valuation.py | 2 +- padic_valuation.py | 5 +++-- ring_with_valuation.py | 1 - trivial_valuation.py | 2 +- valuation.py | 5 +++-- valuation_space.py | 2 +- value_group.py | 2 +- 14 files changed, 30 insertions(+), 17 deletions(-) delete mode 120000 domains_with_valuation.py delete mode 120000 ring_with_valuation.py diff --git a/__init__.py b/__init__.py index d1be5c09257..37b8119d900 100644 --- a/__init__.py +++ b/__init__.py @@ -1,3 +1,14 @@ +# -*- coding: utf-8 -*- + +#***************************************************************************** +# Copyright (C) 2016 Julian Rüth +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + from .valuation_space import DiscretePseudoValuationSpace from .trivial_valuation import TrivialValuation, TrivialPseudoValuation from .padic_valuation import pAdicValuation diff --git a/augmented_valuation.py b/augmented_valuation.py index 75b27e4252c..8ad5650b66d 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Augmented valuations on polynomial rings @@ -14,11 +15,11 @@ AUTHORS: -- Julian Rueth (15-04-2013): initial version +- Julian Rüth (15-04-2013): initial version """ #***************************************************************************** -# Copyright (C) 2013 Julian Rueth +# Copyright (C) 2013-2016 Julian Rüth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of diff --git a/developing_valuation.py b/developing_valuation.py index 8344fb5b67e..346944137b7 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -7,7 +7,7 @@ AUTHORS: -- Julian Rueth (15-04-2013): initial version +- Julian Rüth (15-04-2013): initial version """ #***************************************************************************** diff --git a/domains_with_valuation.py b/domains_with_valuation.py deleted file mode 120000 index 0e3c0b715ca..00000000000 --- a/domains_with_valuation.py +++ /dev/null @@ -1 +0,0 @@ -../src/sage/categories/domains_with_valuation.py \ No newline at end of file diff --git a/function_field_valuation.py b/function_field_valuation.py index 7f3fdd2c2db..157c8710a24 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -37,7 +37,7 @@ AUTHORS: -- Julian Rueth (2016-10-16): initial version +- Julian Rüth (2016-10-16): initial version """ #***************************************************************************** diff --git a/gauss_valuation.py b/gauss_valuation.py index 1993c2d2b74..e1a21f4d790 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Gauss valuations on polynomial rings @@ -7,7 +8,7 @@ AUTHORS: -- Julian Rueth (15-04-2013): initial version +- Julian Rüth (15-04-2013): initial version EXAMPLES:: @@ -34,7 +35,7 @@ """ #***************************************************************************** -# Copyright (C) 2013-2016 Julian Rueth +# Copyright (C) 2013-2016 Julian Rüth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of diff --git a/inductive_valuation.py b/inductive_valuation.py index dcfb3a0d7e4..175a48c042d 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -7,7 +7,7 @@ AUTHORS: -- Julian Rueth (01-11-2016): initial version +- Julian Rüth (01-11-2016): initial version REFERENCES: diff --git a/limit_valuation.py b/limit_valuation.py index 3521be9ecac..10f725c5228 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -65,7 +65,7 @@ AUTHORS: -- Julian Rueth (2016-10-19): initial version +- Julian Rüth (2016-10-19): initial version """ #***************************************************************************** diff --git a/padic_valuation.py b/padic_valuation.py index 75c78fb3904..2bb0dcbc620 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -1,13 +1,14 @@ +# -*- coding: utf-8 -*- """ `p`-adic valuations on number fields and their subrings and completions. AUTHORS: -- Julian Rueth (2013-03-16): initial version +- Julian Rüth (2013-03-16): initial version """ #***************************************************************************** -# Copyright (C) 2013-2016 Julian Rueth +# Copyright (C) 2013-2016 Julian Rüth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of diff --git a/ring_with_valuation.py b/ring_with_valuation.py deleted file mode 120000 index c4c83221c04..00000000000 --- a/ring_with_valuation.py +++ /dev/null @@ -1 +0,0 @@ -../src/sage/rings/valuation/ring_with_valuation.py \ No newline at end of file diff --git a/trivial_valuation.py b/trivial_valuation.py index a7dfe8ff29e..ae6d1826bab 100644 --- a/trivial_valuation.py +++ b/trivial_valuation.py @@ -37,7 +37,7 @@ AUTHORS: -- Julian Rueth (2016-10-14): initial version +- Julian Rüth (2016-10-14): initial version """ #***************************************************************************** diff --git a/valuation.py b/valuation.py index 4a7f1237943..739cb07ca86 100644 --- a/valuation.py +++ b/valuation.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Discrete valuations @@ -5,11 +6,11 @@ AUTHORS: -- Julian Rueth (2013-03-16): initial version +- Julian Rüth (2013-03-16): initial version """ #***************************************************************************** -# Copyright (C) 2013 Julian Rueth +# Copyright (C) 2013-2016 Julian Rüth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of diff --git a/valuation_space.py b/valuation_space.py index 8756806d6b8..0e79fde888b 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -15,7 +15,7 @@ AUTHORS: -- Julian Rueth (2016-10-14): initial version +- Julian Rüth (2016-10-14): initial version """ #***************************************************************************** diff --git a/value_group.py b/value_group.py index ba9ea0e7b53..5d7fc968aff 100644 --- a/value_group.py +++ b/value_group.py @@ -13,7 +13,7 @@ AUTHORS: -- Julian Rueth (2013-09-06): initial version +- Julian Rüth (2013-09-06): initial version """ #***************************************************************************** From 9a256ee1dd367caa26b5e352c602412c94e7c036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 1 Nov 2016 22:32:05 -0500 Subject: [PATCH 062/740] Monkey patch factorization over iterated finite fields --- __init__.py | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/__init__.py b/__init__.py index 37b8119d900..4a694d91ab4 100644 --- a/__init__.py +++ b/__init__.py @@ -145,6 +145,118 @@ def _coerce_map_from_patched(self, domain): Order._coerce_map_from_ = _coerce_map_from_patched del(_coerce_map_from_patched) +# factorization in polynomial quotient fields +def _factor_univariate_polynomial(self, f): + from sage.structure.factorization import Factorization + + if f.is_zero(): + raise ValueError("factorization of 0 is not defined") + elif f.degree() <= 1: + return Factorization([(f,1)]) + + from_absolute_field, to_absolute_field, absolute_field = self.absolute_extension() + + F = f.map_coefficients(lambda c:to_absolute_field(c), absolute_field).factor() + return Factorization([(g.map_coefficients(lambda c:from_absolute_field(c), self), e) for g,e in F], unit=from_absolute_field(F.unit())) +sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRing_generic._factor_univariate_polynomial = _factor_univariate_polynomial +del(_factor_univariate_polynomial) + +# factorization needs to go to the absolute field and back +from sage.misc.cachefunc import cached_method +@cached_method +def absolute_extension(self): + """ + Return a ring isomorphic to this ring which is not a + :class:`PolynomialQuotientRing` but of a type which offers more + functionality. + + INUPT: + + - ``name`` -- a list of strings or ``None`` (default: ``None``), the + name of the generator of the absolute extension. If ``None``, this + will be the same as the name of the generator of this ring. + + EXAMPLES:: + + sage: k. = GF(4) + sage: R. = k[] + sage: l. = k.extension(b^2+b+a); l + Univariate Quotient Polynomial Ring in b over Finite Field in a of size 2^2 with modulus b^2 + b + a + sage: from_ll,to_ll, ll = l.absolute_extension(); ll + Finite Field in z4 of size 2^4 + sage: all([to_ll(from_ll(ll.gen()**i)) == ll.gen()**i for i in range(ll.degree())]) + True + + sage: R. = l[] + sage: m. = l.extension(c^2+b*c+b); m + Univariate Quotient Polynomial Ring in c over Univariate Quotient Polynomial Ring in b over Finite Field in a of size 2^2 with modulus b^2 + b + a with modulus c^2 + b*c + b + sage: from_mm, to_mm, mm = m.absolute_extension(); mm + Finite Field in z8 of size 2^8 + sage: all([to_mm(from_mm(mm.gen()**i)) == mm.gen()**i for i in range(mm.degree())]) + True + + """ + from sage.rings.polynomial.polynomial_quotient_ring import PolynomialQuotientRing_generic + if not self.is_field(): + raise NotImplementedError("absolute_extension() only implemented for fields") + + if self.is_finite(): + if self.base_ring().is_prime_field(): + if self.modulus().degree() == 1: + ret = self.base_ring() + from sage.categories.homset import Hom + from sage.categories.morphism import SetMorphism + to_ret = SetMorphism(Hom(self, ret), lambda x: x.lift()[0]) + from_ret = self.coerce_map_from(ret) + return from_ret, to_ret, ret + else: + raise NotImplementedError + + if isinstance(self.base_ring(), PolynomialQuotientRing_generic): + abs_base_to_base, base_to_abs_base, abs_base = self.base_ring().absolute_extension() + modulus_over_abs_base = self.modulus().map_coefficients(lambda c:base_to_abs_base(c), abs_base) + new_self = modulus_over_abs_base.parent().quo(modulus_over_abs_base) + ret_to_new_self, new_self_to_ret, ret = new_self.absolute_extension() + from_ret = ret.hom([ret_to_new_self(ret.gen()).lift().map_coefficients(abs_base_to_base, self.base_ring())(self.gen())], check=False) + to_ret = lambda x: x.lift().map_coefficients(lambda c: new_self_to_ret(base_to_abs_base(c)), ret)(new_self_to_ret(new_self.gen())) + from sage.categories.homset import Hom + from sage.categories.morphism import SetMorphism + to_ret = SetMorphism(Hom(self, ret), to_ret) + return from_ret, to_ret, ret + else: + N = self.cardinality() + from sage.rings.all import GF + ret = GF(N,prefix='v') + base_to_ret = self.base_ring().hom([self.base_ring().modulus().change_ring(ret).roots()[0][0]]) + im_gen = self.modulus().map_coefficients(lambda c:base_to_ret(c), ret).roots()[0][0] + to_ret = lambda x: x.lift().map_coefficients(base_to_ret, ret)(im_gen) + from sage.categories.homset import Hom + from sage.categories.morphism import SetMorphism + to_ret = SetMorphism(Hom(self, ret), to_ret) + + basis = [self.gen()**i*self.base_ring().gen()**j for i in range(self.degree()) for j in range(self.base_ring().degree())] + assert len(basis) == ret.degree() + basis_in_ret = [to_ret(b)._vector_() for b in basis] + from sage.matrix.constructor import matrix + A = matrix(basis_in_ret) + assert A.is_square() + x = A.solve_left(A.column_space().basis()[1]) + from_ret = ret.hom([sum(c*b for c,b in zip(x.list(),basis))], check=False) + return from_ret, to_ret, ret + else: + raise NotImplementedError +sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRing_generic.absolute_extension = absolute_extension +del(absolute_extension) + +# factorization needs some linear algebra (it seems) +def vector_space(self): + if not self.base().base_ring().is_field(): + raise ValueError + + return self.base().base_ring()**self.modulus().degree() +sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRing_generic.vector_space = vector_space +del(vector_space) + # register modules at some standard places so imports work as exepcted r""" sage: from sage.rings.valuation.gauss_valuation import GaussValuation From 4ff6c0fe3e4c15f3ab0a6586c6faf14e81e27ed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 1 Nov 2016 22:54:52 -0500 Subject: [PATCH 063/740] Fix doctests copied over to inductive_valuation --- inductive_valuation.py | 85 +++++++++++++++++++++++------------------- padic_valuation.py | 10 +++-- 2 files changed, 54 insertions(+), 41 deletions(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index 175a48c042d..19e03b1fe74 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -52,7 +52,7 @@ class InductiveValuation(DevelopingValuation): TESTS:: - sage: TestSuite(v).run() + sage: TestSuite(v).run() # long time """ def is_equivalence_unit(self, f): @@ -66,6 +66,7 @@ def is_equivalence_unit(self, f): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R = Zp(2,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -97,13 +98,14 @@ def equivalence_reciprocal(self, f): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R = Zp(3,5) sage: S. = R[] sage: v = GaussValuation(S) sage: f = 3*x + 2 - sage: h = v.equivalence_reciprocal(f); h + sage: h = v.equivalence_reciprocal(f); h # optional: integrated (needs xgcd for polynomials with p-adic coefficients) 2 + 3 + 3^2 + 3^3 + 3^4 + O(3^5) - sage: v.is_equivalent(f*h, 1) + sage: v.is_equivalent(f*h, 1) # optional: integrated True In an extended valuation over an extension field:: @@ -111,7 +113,7 @@ def equivalence_reciprocal(self, f): sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) - sage: v = v.extension(x^2 + x + u, 1) + sage: v = v.augmentation(x^2 + x + u, 1) sage: f = 2*x + u sage: h = v.equivalence_reciprocal(f); h (u + 1) + (u + 1)*2 + 2^2 + u*2^3 + 2^4 + O(2^5) @@ -120,7 +122,7 @@ def equivalence_reciprocal(self, f): Extending the valuation once more:: - sage: v = v.extension((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) + sage: v = v.augmentation((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) sage: h = v.equivalence_reciprocal(f); h (u + 1) + (u + 1)*2 + (u + 1)*2^2 + (u + 1)*2^3 + u*2^4 + O(2^5) sage: v.is_equivalent(f*h, 1) @@ -135,16 +137,14 @@ def equivalence_reciprocal(self, f): sage: L. = K.extension(x^4 + 4*x^3 + 6*x^2 + 4*x + 2) sage: R. = L[] sage: v = GaussValuation(R) - sage: w = v.extension(t + 1, 5/4) - sage: w = w.extension(t^4 + (a^8 + a^12 + a^14 + a^16 + a^17 + a^19 + a^20 + a^23)*t^3 + (a^6 + a^9 + a^13 + a^15 + a^18 + a^19 + a^21)*t^2 + a^10*t + 1 + a^4 + a^5 + a^8 + a^13 + a^14 + a^15, 17/2) + sage: w = v.augmentation(t + 1, 5/16) + sage: w = w.augmentation(t^4 + (a^8 + a^12 + a^14 + a^16 + a^17 + a^19 + a^20 + a^23)*t^3 + (a^6 + a^9 + a^13 + a^15 + a^18 + a^19 + a^21)*t^2 + a^10*t + 1 + a^4 + a^5 + a^8 + a^13 + a^14 + a^15, 17/8) sage: f = a^-15*t^2 + (a^-11 + a^-9 + a^-6 + a^-5 + a^-3 + a^-2)*t + a^-15 - sage: f_ = w.equivalence_reciprocal(f); f_ - (a^10 + a^13 + a^14 + a^17 + a^18 + a^19 + a^20 + a^24 + a^25 + O(a^26))*t^2 + a^10 + a^13 + a^18 + a^19 + a^22 + a^23 + a^24 + a^25 + O(a^26) + sage: f_ = w.equivalence_reciprocal(f) sage: w.reduce(f*f_) 1 sage: f = f.parent()([f[0],f[1].add_bigoh(1),f[2]]) - sage: f_ = w.equivalence_reciprocal(f); f_ - (a^10 + a^13 + a^14 + a^17 + a^18 + a^19 + a^20 + a^24 + a^25 + O(a^26))*t^2 + a^10 + a^13 + a^18 + a^19 + a^22 + a^23 + a^24 + a^25 + O(a^26) + sage: f_ = w.equivalence_reciprocal(f) sage: w.reduce(f*f_) 1 @@ -200,13 +200,14 @@ def minimal_representative(self, f): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,10) sage: S. = R[] sage: v = GaussValuation(S) sage: v.minimal_representative(x + 2) (1 + O(2^10))*x - sage: v = v.extension(x, 1) + sage: v = v.augmentation(x, 1) sage: v.minimal_representative(x + 2) (1 + O(2^10))*x + 2 + O(2^11) sage: f = x^3 + 6*x + 4 @@ -214,7 +215,7 @@ def minimal_representative(self, f): (2 + 2^2 + O(2^11)) * ((1 + O(2^10))*x + 2 + 2^2 + 2^4 + 2^6 + 2^8 + 2^10 + O(2^11)) sage: v.is_minimal(F[0][0]) True - sage: v.is_equivalent(F[0][0], f) + sage: v.is_equivalent(F.prod(), f) True REFERENCES: @@ -254,6 +255,7 @@ def _test_is_equivalence_unit(self, **options): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: v._test_is_equivalence_unit() @@ -268,6 +270,7 @@ def _test_equivalence_reciprocal(self, **options): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: v._test_equivalence_reciprocal() @@ -316,15 +319,16 @@ def augmentation(self, phi, mu, check=True): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) - sage: v = v.extension(x^2 + x + u, 1) - sage: v = v.extension((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) + sage: v = v.augmentation(x^2 + x + u, 1) + sage: v = v.augmentation((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) sage: v [ Gauss valuation induced by 2-adic valuation, v((1 + O(2^5))*x^2 + (1 + O(2^5))*x + u + O(2^5)) = 1, v((1 + O(2^5))*x^4 + (2^2 + O(2^6))*x^3 + (1 + (u + 1)*2 + O(2^5))*x^2 + ((u + 1)*2^2 + O(2^6))*x + (u + 1) + (u + 1)*2 + (u + 1)*2^2 + (u + 1)*2^3 + (u + 1)*2^4 + O(2^5)) = 3 ] sage: v.residue_field() - Univariate Quotient Polynomial Ring in u2 over Univariate Quotient Polynomial Ring in u1 over Finite Field in u0 of size 2^2 with modulus u1^2 + u1 + u0 with modulus u2^2 + u1*u2 + u1 + Rational function field in x over Univariate Quotient Polynomial Ring in u2 over Univariate Quotient Polynomial Ring in u1 over Finite Field in u0 of size 2^2 with modulus u1^2 + u1 + u0 with modulus u2^2 + u1*u2 + u1 TESTS: @@ -338,14 +342,14 @@ def augmentation(self, phi, mu, check=True): sage: f3 = 3+30*t^2+18*t^3+198*t^5+180*t^7+189*t^8+342*t^10+145*t^12 sage: F = f2*f3 - sage: v1 = v0.extension(t,1/12) - sage: v2 = v1.extension(t^12+3,7/6) - sage: v3 = v2.extension(t^12+3*t^2+3,9/4) - sage: v4 = v1.extension(t^12+3*t^2+3,9/4) - sage: v3 == v4 # rather: check for equivalence + sage: v1 = v0.augmentation(t,1/12) + sage: v2 = v1.augmentation(t^12+3,7/6) + sage: v3 = v2.augmentation(t^12+3*t^2+3,9/4) + sage: v4 = v1.augmentation(t^12+3*t^2+3,9/4) + sage: v3 <= v4 and v3 >= v4 True - sage: v4.equivalence_decomposition(F) - sage: v3.equivalence_decomposition(F) + sage: v4.equivalence_decomposition(F) # optional: integrated + sage: v3.equivalence_decomposition(F) # optional: integrated .. SEEALSO:: @@ -374,6 +378,7 @@ def is_key(self, phi, explain=False): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -384,7 +389,7 @@ def is_key(self, phi, explain=False): sage: v.is_key(x^2, explain = True) (False, 'phi must be equivalence irreducible') - sage: w = v.extension(x, 1) + sage: w = v.augmentation(x, 1) sage: w.is_key(x + 1, explain = True) (False, 'phi must be minimal') @@ -422,12 +427,13 @@ def is_minimal(self, f): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) sage: v.is_minimal(x + 1) True - sage: w = v.extension(x, 1) + sage: w = v.augmentation(x, 1) sage: w.is_minimal(x + 1) False @@ -438,9 +444,9 @@ def is_minimal(self, f): sage: vp=pAdicValuation(K) sage: v0 = GaussValuation(R,vp) sage: f=x^5+x^4+2 - sage: v1 = v0.extension(x,1/4) - sage: v2 = v1.extension(x^4+2,5/4) - sage: v2.is_minimal(f) + sage: v1 = v0.augmentation(x,1/4) + sage: v2 = v1.augmentation(x^4+2,5/4) + sage: v2.is_minimal(f) # long time False TODO: Add examples for polynomials which have the same degree as the @@ -477,13 +483,13 @@ def mac_lane_step(self, G, assume_squarefree=False): TESTS:: + sage: from mac_lane import * # optional: standalone sage: K.=FunctionField(QQ) sage: S.=K[] sage: F=y^2-x^2-x^3-3 sage: v0=GaussValuation(K._ring,pAdicValuation(QQ,3)) sage: v1=v0.augmentation(K._ring.gen(),1/3) - sage: from sage.rings.padics.function_field_valuation import RationalFunctionFieldValuation - sage: mu0=RationalFunctionFieldValuation(K,v1) + sage: mu0=FunctionFieldValuation(K,v1) sage: eta0=GaussValuation(S,mu0) sage: eta1=eta0.mac_lane_step(F)[0] sage: eta2=eta1.mac_lane_step(F)[0] @@ -602,6 +608,7 @@ def is_equivalence_irreducible(self, f): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -686,6 +693,7 @@ def equivalence_decomposition(self, f): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,10) sage: S. = R[] sage: v = GaussValuation(S) @@ -704,7 +712,7 @@ def equivalence_decomposition(self, f): of a :class:`sage.structure.factorization.Factorization`, leading to a unit non-minimal degree:: - sage: w = v.extension(x, 1) + sage: w = v.augmentation(x, 1) sage: F = w.equivalence_decomposition(x^2+1); F (1 + O(2^10))*x^2 + 1 + O(2^10) sage: F.unit() @@ -721,8 +729,8 @@ def equivalence_decomposition(self, f): Examples over an iterated unramified extension: - sage: v = v.extension(x^2 + x + u, 1) - sage: v = v.extension((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) + sage: v = v.augmentation(x^2 + x + u, 1) + sage: v = v.augmentation((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) sage: v.equivalence_decomposition(x) (1 + O(2^10))*x @@ -743,10 +751,10 @@ def equivalence_decomposition(self, f): sage: vp=vp.extension(K) sage: v0=GaussValuation(R,vp) sage: G=x^36 + 36*x^35 + 630*x^34 + 7144*x^33 + 59055*x^32 + 379688*x^31 +1978792*x^30 + 8604440*x^29 + 31895428*x^28 + 102487784*x^27 + 289310720*x^26 + 725361352*x^25 + 1629938380*x^24 + 3307417800*x^23 + 6098786184*x^22+10273444280*x^21 + 15878121214*x^20 + 22596599536*x^19 + 29695703772*x^18 +36117601976*x^17 + 40722105266*x^16 + 42608585080*x^15 + 41395961848*x^14 +37344435656*x^13 + 31267160756*x^12 + 24271543640*x^11 + 17439809008*x^10 + 11571651608*x^9 + 7066815164*x^8 + 3953912472*x^7 + 2013737432*x^6 + 925014888*x^5 + 378067657*x^4 + 134716588*x^3 + 40441790*x^2 + 9532544*x + 1584151 - sage: v1=v0.mac_lane_step(G)[0] - sage: V=v1.mac_lane_step(G) - sage: v2=V[0] - sage: v2.equivalence_decomposition(G) + sage: v1=v0.mac_lane_step(G)[0] # long time + sage: V=v1.mac_lane_step(G) # long time + sage: v2=V[0] # long time + sage: v2.equivalence_decomposition(G) # long time (x^4 + 4*x^3 + 6*x^2 + 4*x + alpha^4 + alpha^3 + 1)^3 * (x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*alpha^4 + alpha^3 - 27*alpha + 1)^3 * (x^4 + 4*x^3 + 6*x^2 + 4*x + 3/2*alpha^4 + alpha^3 - 27*alpha + 1)^3 REFERENCES: @@ -819,6 +827,7 @@ def _test_is_equivalence_irreducible(self, **options): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: v._test_is_equivalence_irreducible() @@ -866,7 +875,7 @@ def _lift_to_maximal_precision(c): EXAMPLES:: - sage: from sage.rings.padics.developing_valuation import _lift_to_maximal_precision + sage: from mac_lane import * # optional: standalone sage: R = Zp(2,5) sage: x = R(1,2); x 1 + O(2^2) diff --git a/padic_valuation.py b/padic_valuation.py index 2bb0dcbc620..02f74d52de7 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -794,7 +794,8 @@ def uniformizer(self): def shift(self, c, v): """ - Multiply ``c`` by a ``v``-th power of the uniformizer. + Multiply ``c`` by a power of a uniformizer such that its valuation + changes by ``v``. INPUT: @@ -816,9 +817,12 @@ def shift(self, c, v): 2*3^3 + O(3^23) """ - from sage.rings.all import ZZ + from sage.rings.all import QQ, ZZ c = self.domain().coerce(c) - v = ZZ(v) + v = QQ(v) + if v not in self.value_group(): + raise ValueError("%r is not in the value group of %r"%(v, self)) + v = ZZ(v * self.domain().ramification_index()) return c< Date: Wed, 2 Nov 2016 01:52:27 -0500 Subject: [PATCH 064/740] Add doctests for everything in inductive_valuation --- TODO | 2 + augmented_valuation.py | 39 ++-- gauss_valuation.py | 28 --- inductive_valuation.py | 511 +++++++++++++++++++++-------------------- valuation.py | 2 +- 5 files changed, 289 insertions(+), 293 deletions(-) diff --git a/TODO b/TODO index 3715d2df7e5..e9bf4cccebb 100644 --- a/TODO +++ b/TODO @@ -1 +1,3 @@ * Eliminate isinstance calls from code (usually by moving things like _repr_ or _ge_ up in the hierarchy) + +* Replace almost all doctests with the ring case. If it works over rings, then it will work over fields. diff --git a/augmented_valuation.py b/augmented_valuation.py index 8ad5650b66d..8e1655d3c76 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -248,7 +248,8 @@ def equivalence_unit(self, s): :meth:`effective_degree`, :meth:`is_equivalence_unit` """ - if s < 0 and not self.domain().base_ring().is_field(): + from sage.categories.fields import Fields + if s < 0 and not self.domain().base_ring() in Fields(): raise NotImplementedError("only implemented for polynomial rings over fields") ret = self._base_valuation.element_with_valuation(s) @@ -293,7 +294,8 @@ def element_with_valuation(self, s): """ if s not in self.value_group(): raise ValueError("s must be in the value group of the valuation") - if s < 0 and not self.domain().base_ring().is_field(): + from sage.categories.fields import Fields + if s < 0 and not self.domain().base_ring() in Fields(): raise NotImplementedError("only implemented for polynomial rings over fields") ret = self.domain().one() @@ -491,7 +493,8 @@ def reduce(self, f): """ if f.parent() is not self.domain(): raise ValueError("f must be in the domain of the valuation") - if not self.domain().base_ring().is_field(): + from sage.categories.fields import Fields + if not self.domain().base_ring() in Fields(): raise NotImplementedError("only implemented for polynomial rings over fields") if self(f) < 0: @@ -605,7 +608,8 @@ def lift(self, F): """ F = self.residue_ring().coerce(F) - if not self.domain().base_ring().is_field(): + from sage.categories.fields import Fields + if not self.domain().base_ring() in Fields(): raise NotImplementedError("only implemented for polynomial rings over fields") if self._mu == infinity: if self.psi().degree() == 1: @@ -719,7 +723,8 @@ def lift_to_key(self, F): """ if F.parent() is not self.residue_ring(): raise ValueError("F must be an element of the residue ring of the valuation") - if not self.domain().base_ring().is_field(): + from sage.categories.fields import Fields + if not self.domain().base_ring() in Fields(): raise NotImplementedError("only implemented for polynomial rings over fields") if self._base_valuation.is_gauss_valuation() and self._mu == infinity: raise TypeError("there are no keys over this valuation") @@ -901,6 +906,9 @@ def F(self): return self.psi().degree() * self._base_valuation.F() def extensions(self, ring): + if ring is self.domain(): + return [self] + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing if not is_PolynomialRing(ring) and len(ring.gens()) != 1: raise NotImplementedError("Can not compute extensions to a ring that is not a univariate polynomial ring such as %r"%ring) @@ -910,15 +918,18 @@ def extensions(self, ring): ret = [] for v in base_valuations: - F = v.equivalence_decomposition(phi) - mu0 = v(phi) - for f,e in F: - # We construct a valuation with [v, w(phi) = mu] which should be such that - # self(phi) = self._mu, i.e., w(phi) = w(unit) + sum e_i * w(f_i) where - # the sum runs over all the factors in the equivalence decomposition of phi - # Solving for mu gives - mu = (self._mu - v(F.unit()) - sum([ee*v(ff) for ff,ee in F if ff != f])) / e - ret.append(AugmentedValuation(v, f, mu)) + if v.is_key(phi): + ret.append(AugmentedValuation(v, phi, self._mu)) + else: + F = v.equivalence_decomposition(phi) + mu0 = v(phi) + for f,e in F: + # We construct a valuation with [v, w(phi) = mu] which should be such that + # self(phi) = self._mu, i.e., w(phi) = w(unit) + sum e_i * w(f_i) where + # the sum runs over all the factors in the equivalence decomposition of phi + # Solving for mu gives + mu = (self._mu - v(F.unit()) - sum([ee*v(ff) for ff,ee in F if ff != f])) / e + ret.append(AugmentedValuation(v, f, mu)) return ret def restriction(self, ring): diff --git a/gauss_valuation.py b/gauss_valuation.py index e1a21f4d790..6d2996ea962 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -468,34 +468,6 @@ def equivalence_unit(self, s): # TODO: eliminate this element_with_valuation = equivalence_unit - # TODO: declare this upstairs - def is_commensurable_inductive(self): - """ - Return whether this valuation is a commensurable inductive valuation - over the discrete valuation of the base ring of the polynomial ring, as - defined in section 4 of [ML1936]. - - OUTPUT: - - ``True`` since a Gauss valuation always is commensurable inductive. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,5) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: v.is_commensurable_inductive() - True - - REFERENCES: - - .. [ML1936] Mac Lane, S. (1936). A construction for prime ideals as absolute - values of an algebraic field. Duke Mathematical Journal, 2(3), 492-510. - - """ - return True - # TODO: declare this uptstairs def E(self): """ diff --git a/inductive_valuation.py b/inductive_valuation.py index 19e03b1fe74..c4bcb416eeb 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -143,7 +143,7 @@ def equivalence_reciprocal(self, f): sage: f_ = w.equivalence_reciprocal(f) sage: w.reduce(f*f_) 1 - sage: f = f.parent()([f[0],f[1].add_bigoh(1),f[2]]) + sage: f = f.parent()([f[0], f[1].add_bigoh(1), f[2]]) sage: f_ = w.equivalence_reciprocal(f) sage: w.reduce(f*f_) 1 @@ -171,84 +171,8 @@ def equivalence_reciprocal(self, f): h = h.map_coefficients(lambda c:_lift_to_maximal_precision(c)) h = h.parent()([ c if self(e0*c) <= 0 else c.parent().zero() for c in h.coefficients(sparse=False)]) - assert self(f*h) == 0 - assert self(f*h - 1) > 0 - return h - def minimal_representative(self, f): - r""" - Return a minimal representative for ``f``, i.e., a pair `e, a` - such that ``f`` :meth:`is_equivalent`` to `e a`, `e` is - an equivalence unit and `a` is minimal and monic. - - INPUT: - - - ``f`` -- a polynomial in the domain of this valuation - - OUTPUT: - - A factorization which has `e` as its unit and `a` as its unique factor. - - ALGORITHM: - - We use the algorithm described in the proof of Lemma 4.1 of [ML1936']. - In the expansion `f=\sum_i f_i\phi^i` take `e=f_i` for the largest `i` - with `f_i\phi^i` minimal (see :meth:`effective_degree`). - Let `h` be the :meth:`equivalence_reciprocal` of `e` and take `a` given - by the terms of minimal valuation in the expansion of `e f`. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,10) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: v.minimal_representative(x + 2) - (1 + O(2^10))*x - - sage: v = v.augmentation(x, 1) - sage: v.minimal_representative(x + 2) - (1 + O(2^10))*x + 2 + O(2^11) - sage: f = x^3 + 6*x + 4 - sage: F = v.minimal_representative(f); F - (2 + 2^2 + O(2^11)) * ((1 + O(2^10))*x + 2 + 2^2 + 2^4 + 2^6 + 2^8 + 2^10 + O(2^11)) - sage: v.is_minimal(F[0][0]) - True - sage: v.is_equivalent(F.prod(), f) - True - - REFERENCES: - - .. [ML1936'] MacLane, S. (1936). A construction for absolute values in - polynomial rings. Transactions of the American Mathematical Society, 40(3), - 363-395. - - """ - if f.parent() is not self.domain(): - raise ValueError("f must be in the domain of the valuation") - if f.is_zero(): - raise ValueError("the minimal representative of zero is not defined") - - if not self.is_commensurable_inductive(): - raise NotImplemented("only implemented for inductive valuations") - - f0 = f - e = list(self.coefficients(f))[self.effective_degree(f)] - f *= self.equivalence_reciprocal(e).map_coefficients(lambda c:_lift_to_maximal_precision(c)) - - coeffs = [c if v == self(f) else c.parent().zero() for v,c in zip(self.valuations(f),self.coefficients(f))] - coeffs[self.effective_degree(f0)] = self.domain().base_ring().one() - ret = sum([c*self._phi**i for i,c in enumerate(coeffs)]) - assert self.effective_degree(ret) == self.effective_degree(f0) - assert ret.is_monic(), coeffs - assert self.is_minimal(ret) - from sage.structure.factorization import Factorization - ret = Factorization([(ret,1)],unit=e) - # assert self.is_equivalent(ret.prod(), f0) -- this might fail because of leading zeros - assert self((ret.prod() - f0).map_coefficients(lambda c:_lift_to_maximal_precision(c))) - return ret - def _test_is_equivalence_unit(self, **options): r""" Test the correctness of :meth:`is_equivalence_unit`. @@ -311,11 +235,11 @@ def augmentation(self, phi, mu, check=True): a key polynomial, see :meth:`is_key` for properties of key polynomials. - - ``mu`` -- a rational number, the valuation of ``phi`` in the extended - valuation + - ``mu`` -- a rational number or infinity, the valuation of ``phi`` in + the extended valuation - - ``check`` -- whether or not to check the correctness of the - parameters + - ``check`` -- a boolean (default: ``True``), whether or not to check + the correctness of the parameters EXAMPLES:: @@ -326,9 +250,9 @@ def augmentation(self, phi, mu, check=True): sage: v = v.augmentation(x^2 + x + u, 1) sage: v = v.augmentation((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) sage: v - [ Gauss valuation induced by 2-adic valuation, v((1 + O(2^5))*x^2 + (1 + O(2^5))*x + u + O(2^5)) = 1, v((1 + O(2^5))*x^4 + (2^2 + O(2^6))*x^3 + (1 + (u + 1)*2 + O(2^5))*x^2 + ((u + 1)*2^2 + O(2^6))*x + (u + 1) + (u + 1)*2 + (u + 1)*2^2 + (u + 1)*2^3 + (u + 1)*2^4 + O(2^5)) = 3 ] - sage: v.residue_field() - Rational function field in x over Univariate Quotient Polynomial Ring in u2 over Univariate Quotient Polynomial Ring in u1 over Finite Field in u0 of size 2^2 with modulus u1^2 + u1 + u0 with modulus u2^2 + u1*u2 + u1 + [ Gauss valuation induced by 2-adic valuation, + v((1 + O(2^5))*x^2 + (1 + O(2^5))*x + u + O(2^5)) = 1, + v((1 + O(2^5))*x^4 + (2^2 + O(2^6))*x^3 + (1 + (u + 1)*2 + O(2^5))*x^2 + ((u + 1)*2^2 + O(2^6))*x + (u + 1) + (u + 1)*2 + (u + 1)*2^2 + (u + 1)*2^3 + (u + 1)*2^4 + O(2^5)) = 3 ] TESTS: @@ -338,22 +262,17 @@ def augmentation(self, phi, mu, check=True): sage: v_K = pAdicValuation(QQ,3) sage: A. = QQ[] sage: v0 = GaussValuation(A,v_K) - sage: f2 = 9*t+30*t^3+27*t^6+15*t^8 - sage: f3 = 3+30*t^2+18*t^3+198*t^5+180*t^7+189*t^8+342*t^10+145*t^12 - sage: F = f2*f3 - - sage: v1 = v0.augmentation(t,1/12) - sage: v2 = v1.augmentation(t^12+3,7/6) - sage: v3 = v2.augmentation(t^12+3*t^2+3,9/4) - sage: v4 = v1.augmentation(t^12+3*t^2+3,9/4) + + sage: v1 = v0.augmentation(t, 1/12) + sage: v2 = v1.augmentation(t^12 + 3, 7/6) + sage: v3 = v2.augmentation(t^12 + 3*t^2 + 3, 9/4) + sage: v4 = v1.augmentation(t^12 + 3*t^2 + 3, 9/4) sage: v3 <= v4 and v3 >= v4 True - sage: v4.equivalence_decomposition(F) # optional: integrated - sage: v3.equivalence_decomposition(F) # optional: integrated .. SEEALSO:: - :class:`AugmentedValuation` + :meth:`AugmentedValuation` """ from augmented_valuation import AugmentedValuation @@ -361,13 +280,9 @@ def augmentation(self, phi, mu, check=True): def is_key(self, phi, explain=False): r""" - Return whether ``phi`` is a key polynomial for this valuation. - - A key polynomial must satisfy the following conditions: - - - it must be monic - - it must be equivalence-irreducible (see :meth:`is_equivalence_irreducible`) - - it must be minimal (see :meth:`is_minimal`) + Return whether ``phi`` is a key polynomial for this valuation, i.e., + whether it is monic, whether it :meth:`is_equivalence_irreducible`, and + whether it is :meth:`is_minimal`. INPUT: @@ -379,7 +294,7 @@ def is_key(self, phi, explain=False): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,5) + sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) sage: v.is_key(x) @@ -394,8 +309,7 @@ def is_key(self, phi, explain=False): (False, 'phi must be minimal') """ - if phi.parent() is not self.domain(): - raise ValueError("phi must be in the domain of the valuation") + phi = self.domain().coerce(phi) reason = None @@ -414,21 +328,19 @@ def is_key(self, phi, explain=False): def is_minimal(self, f): r""" Return whether the polynomial ``f`` is minimal with respect to this - valuation, as defined in definition 4.1 of [ML1936]. - - INPUT: - - - ``f`` -- a polynomial in the domain of this valuation + valuation, i.e., whether ``f`` is not constant any non-constant + polynomial `h` has at least the degree of ``f`` or ``f`` is not + divisible by `h` with respect to this valuation, i.e., there is no `c` + such that `c h` :meth:`is_equivalent` to `f`. ALGORITHM: - When ``f`` :meth:`is_equivalence_irreducible` for this valuation, then - Theorem 9.4 of [ML1936'] describes what to do. TODO: what if not? + Based on Theorem 9.4 of [ML1936']. EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,5) + sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) sage: v.is_minimal(x + 1) @@ -437,99 +349,130 @@ def is_minimal(self, f): sage: w.is_minimal(x + 1) False - TODO: An example that failed for Stefan: + TESTS:: - sage: K = Qp(2,10) + sage: K = Qp(2, 10) sage: R. = K[] - sage: vp=pAdicValuation(K) - sage: v0 = GaussValuation(R,vp) - sage: f=x^5+x^4+2 - sage: v1 = v0.augmentation(x,1/4) - sage: v2 = v1.augmentation(x^4+2,5/4) - sage: v2.is_minimal(f) # long time + sage: vp = pAdicValuation(K) + sage: v0 = GaussValuation(R, vp) + sage: v1 = v0.augmentation(x, 1/4) + sage: v2 = v1.augmentation(x^4 + 2, 5/4) + sage: v2.is_minimal(x^5 + x^4 + 2) # long time False - TODO: Add examples for polynomials which have the same degree as the - key polynomial (See Stefan's mail Sep 8 2016). + Polynomials which are equivalent to the key polynomial are minimal if + and only if they have the same degree as the key polynomial:: + + sage: v2.is_minimal(x^4 + 2) + True + sage: v2.is_minimal(x^4 + 4) + False """ - if f.parent() is not self.domain(): - raise ValueError("f must be in the domain of the valuation") + f = self.domain().coerce(f) + if f.is_constant(): - raise ValueError("f must not be constant") + return False + + if not self.is_equivalence_irreducible(f): + # any factor divides f with respect to this valuation + return False - if self.is_commensurable_inductive(): - # use the characterization of theorem 9.4 in [ML1936] - if not f.is_monic(): - raise NotImplementedError("is_minimal() only implemented for monic polynomials") - if not self.is_equivalence_irreducible(f): - raise NotImplementedError("is_minimal() only implemented for equivalence-irreducible polynomials") - from gauss_valuation import GaussValuation - if self.is_gauss_valuation(): - if f.is_monic(): - if self(f) < 0: - raise NotImplementedError("Investigate what is the right behaviour here") - return self.reduce(f).is_irreducible() - if self.is_equivalent(self.phi(), f): - # TODO: reference new Lemma - return f.degree() == self.phi().degree() + if not f.is_monic(): + # divide out the leading factor, it does not change minimality + v = self + if not self.domain().base_ring().is_field(): + domain = self.domain().change_ring(self.domain().base_ring().fraction_field()) + v = self.extension(domain) + f = domain(f) + return v.is_minimal(f / f.leading_coefficient()) + + if self.is_gauss_valuation(): + if self(f) == 0: + F = self.reduce(f) + assert not F.is_constant() + return F.is_irreducible() else: - return list(self.valuations(f))[-1] == self(f) and list(self.coefficients(f))[-1].is_constant() and list(self.valuations(f))[0] == self(f) and self.tau().divides(len(list(self.coefficients(f)))-1) + assert(self(f) <= 0) # f is monic + # f is not minimal: + # Let g be f stripped of its leading term, i.e., g = f - x^n. + # Then g and f are equivalent with respect to this valuation + # and in particular g divides f with respect to this valuation + return False + + if self.is_equivalent(self.phi(), f): + assert f.degree() >= self.phi().degree() + # If an h divides f with respect to this valuation, then it also divides phi: + # v(f - c*h) > v(f) = v(c*h) => v(phi - c*h) = v((phi - f) + (f - c*h)) > v(phi) = v(c*h) + # So if f were not minimal then phi would not be minimal but it is. + return f.degree() == self.phi().degree() - raise NotImplementedError("is_minimal() only implemented for commensurable inductive values") + else: + # see Theorem 9.4 of [ML1936'] + return list(self.valuations(f))[-1] == self(f) and \ + list(self.coefficients(f))[-1].is_constant() and \ + list(self.valuations(f))[0] == self(f) and \ + self.tau().divides(len(list(self.coefficients(f))) - 1) def mac_lane_step(self, G, assume_squarefree=False): r""" + Perform an approximation step towards the squarefree non-constant + polynomial ``G`` with this valuation. + + This performs the individual steps that are used in + :meth:`mac_lane_approximants`. TESTS:: sage: from mac_lane import * # optional: standalone - sage: K.=FunctionField(QQ) - sage: S.=K[] - sage: F=y^2-x^2-x^3-3 - sage: v0=GaussValuation(K._ring,pAdicValuation(QQ,3)) - sage: v1=v0.augmentation(K._ring.gen(),1/3) - sage: mu0=FunctionFieldValuation(K,v1) - sage: eta0=GaussValuation(S,mu0) - sage: eta1=eta0.mac_lane_step(F)[0] - sage: eta2=eta1.mac_lane_step(F)[0] + sage: K. = FunctionField(QQ) + sage: S. = K[] + sage: F = y^2 - x^2 - x^3 - 3 + sage: v0 = GaussValuation(K._ring,pAdicValuation(QQ, 3)) + sage: v1 = v0.augmentation(K._ring.gen(), 1/3) + sage: mu0 = FunctionFieldValuation(K, v1) + sage: eta0 = GaussValuation(S, mu0) + sage: eta1 = eta0.mac_lane_step(F)[0] + sage: eta2 = eta1.mac_lane_step(F)[0] + sage: eta2 + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + x) = 2/3 ] """ + G = self.domain().coerce(G) + + if G.is_constant(): + raise ValueError("G must not be constant") + from sage.misc.misc import verbose - verbose("Expanding %s towards %s"%(self,G),caller_name="mac_lane_step") - R = G.parent() - if R is not self.domain(): - raise ValueError("G must be defined over the domain of this valuation") - if not G.is_monic(): + verbose("Expanding %s towards %s"%(self, G), caller_name = "mac_lane_step") + + if not G.is_monic() or self(G) < 0: # G must be monic, there is no fundamental reason for this, but the implementation makes this assumption in some places. - # We try to turn G into a monic integral polynomial that describes the same extension - return self.mac_lane_step(self._make_monic_integral(G), assume_squarefree=assume_squarefree) - if self(G) < 0: # G must be integral, otherwise, e.g., the effective degree is too low # We try to turn G into a monic integral polynomial that describes the same extension + # This might fail if the constants of our polynomial ring do not form a field return self.mac_lane_step(self._make_monic_integral(G), assume_squarefree=assume_squarefree) - assert not G.is_constant() + if not assume_squarefree and not G.is_squarefree(): raise ValueError("G must be squarefree") from sage.rings.all import infinity - - if self(G) is infinity: - raise ValueError("G must not have valuation infinity") + assert self(G) != infinity # this is a valuation and G is non-zero if self.is_key(G): return [self.augmentation(G, infinity)] F = self.equivalence_decomposition(G) - assert len(F), "%s equivalence-decomposese as an equivalence-unit %s"%(G,F) + assert len(F), "%s equivalence-decomposese as an equivalence-unit %s"%(G, F) ret = [] for phi,e in F: if G == phi: - # something strange happened here: + # Something strange happened here: # G is not a key (we checked that before) but phi==G is; so phi must have less precision than G # this can happen if not all coefficients of G have the same precision - # if we drop some precision of G then it will be a key + # if we drop some precision of G then it will be a key (but is + # that really what we should do?) assert not G.base_ring().is_exact() prec = min([c.precision_absolute() for c in phi.list()]) g = G.map_coefficients(lambda c:c.add_bigoh(prec)) @@ -538,25 +481,24 @@ def mac_lane_step(self, G, assume_squarefree=False): if phi == self.phi(): # a factor phi in the equivalence decomposition means that we - # founnd an actual factor of G, i.e., we can set + # found an actual factor of G, i.e., we can set # v(phi)=infinity # However, this should already have happened in the last step # (when this polynomial had -infinite slope in the Newton # polygon.) - from gauss_valuation import GaussValuation if self.is_gauss_valuation(): # unless in the first step pass else: continue - verbose("Determining the valuation for %s"%phi,level=2,caller_name="mac_lane_step") + verbose("Determining the valuation for %s"%phi, level=2, caller_name="mac_lane_step") w = self.augmentation(phi, self(phi), check=False) NP = w.newton_polygon(G).principal_part() - verbose("Newton-Polygon for v(phi)=%s : %s"%(self(phi),NP),level=2,caller_name="mac_lane_step") - # assert len(NP) + verbose("Newton-Polygon for v(phi)=%s : %s"%(self(phi), NP), level=2, caller_name="mac_lane_step") slopes = NP.slopes(repetition=False) if NP.vertices()[0][0] != 0: slopes = [-infinity] + slopes + if not slopes: q,r = G.quo_rem(phi) assert not r.is_zero() @@ -565,12 +507,14 @@ def mac_lane_step(self, G, assume_squarefree=False): if not c.is_zero(): v = w(c) # for a correct result we need to add O(pi^v) in degree i - # we try to find the coefficient of phi where such an error can be introduced without losing much absolute precision on phi + # we try to find the coefficient of phi where such an + # error can be introduced without losing much absolute + # precision on phi best = i for j in range(i): if w(q[j]) < w(q[best]): best = j - # now add the right O() to phi in degree i-best + # now add the right O() to phi in degree i - best phi[i-best] = phi[i-best].add_bigoh(w(c)-w(q[best])) phi = G.parent()(phi) @@ -580,15 +524,13 @@ def mac_lane_step(self, G, assume_squarefree=False): for i in range(len(slopes)): slope = slopes[i] - verbose("Slope = %s"%slope,level=3,caller_name="mac_lane_step") + verbose("Slope = %s"%slope, level=3, caller_name="mac_lane_step") new_mu = self(phi) - slope base = self if phi.degree() == base.phi().degree(): assert new_mu > self(phi) - from gauss_valuation import GaussValuation if not base.is_gauss_valuation(): base = base._base_valuation - new_leaf = base.augmentation(phi, new_mu) assert slope is -infinity or 0 in new_leaf.newton_polygon(G).slopes(repetition=False) ret.append(new_leaf) @@ -596,12 +538,59 @@ def mac_lane_step(self, G, assume_squarefree=False): assert ret return ret + @cached_method + def _equivalence_reduction(self, f): + r""" + Helper method for :meth:`is_equivalence_irreducible` and + :meth:`equivalence_decomposition` which essentially returns the + reduction of ``f`` after multiplication with an ``R`` which + :meth:`is_equivalence_unit`. + + This only works when ``f`` is not divisible by :meth:`phi` with respect + to this valuation. Therefore, we also return the number of times that + we took out :meth:`phi` of ``f`` before we computed the reduction. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v._equivalence_reduction(2*x^6 + 4*x^5 + 2*x^4 + 8) + (1/2, 4, x^2 + 1) + + """ + f = self.domain().coerce(f) + + # base change from R[x] to K[x], so divisions work and sufficient + # elements of negative valuation exist + if not self.domain().base_ring().is_field(): + domain = self.domain().change_ring(self.domain().base_ring().fraction_field()) + v = self.extension(domain) + assert self.residue_ring() is v.residue_ring() + return v._equivalence_reduction(f) + + phi_divides = 0 + while self.valuations(f).next() > self(f): + # phi is an equivalence-factor of f + f = f-self.coefficients(f).next() + assert self.phi().divides(f) + f,_ = f.quo_rem(self.phi()) + phi_divides += 1 + + R = self.equivalence_unit(-self(f)) + return R, phi_divides, self.reduce(f*R) + @cached_method def is_equivalence_irreducible(self, f): r""" - Return whether the polynomial ``f`` is equivalence irreducible, i.e., + Return whether the polynomial ``f`` is equivalence-irreducible, i.e., whether its :meth:`equivalence_decomposition` is trivial. + ALGORITHM: + + We use the same algorithm as in :meth:`equivalence_decomposition` we + just do not lift the result to key polynomials. + INPUT: - ``f`` -- a non-constant polynomial in the domain of this valuation @@ -619,57 +608,22 @@ def is_equivalence_irreducible(self, f): sage: v.is_equivalence_irreducible(x^2 + 2) False - ALGORITHM: - - We do not actually compute the :meth:`equivalence_decomposition` of - ``f`` (unless we have it cached already) but use the - characterization of Theorem 13.1 in [ML1936], i.e., we only perform - a factorization in reduction without lifting the factors back to - key polynomials. - """ f = self.domain().coerce(f) + if f.is_constant(): raise ValueError("f must not be constant") - from sage.misc.cachefunc import cache_key - key = cache_key(f) - - if self.equivalence_decomposition.is_in_cache(key): - F = self.equivalence_decomposition(f) - return len(F) <= 1 and (len(F) == 0 or F[0][1] == 1) - - # base change from R[x] to K[x], so divisions work and sufficient - # elements of negative valuation exist - v = self.extension(self.domain().change_ring(self.domain().base_ring().fraction_field())) - if v is not self: - return v.is_equivalence_irreducible(f) - - if not f.is_monic(): - # if f is not monic we can safely drop the leading coefficient since a - # constant is always an equivalence-unit - f = v.domain().coerce(f) - return self.is_equivalence_irreducible(f / f.leading_coefficient()) - - if self.valuations(f).next() > self(f): - # phi is an equivalence-factor of f - f = f-self.coefficients(f).next() - assert self.phi().divides(f) - f,_ = f.quo_rem(self.phi()) - return self.effective_degree(f) == 0 - - R = self.equivalence_unit(-self(f)) - - # check irreducibility in reduction - F = self.reduce(f*R) - F = F.factor() - if len(F) > 1 or (len(F) and F[0][1] > 1): + _, phi_divides, F = self._equivalence_reduction(f) + if phi_divides == 0: + return F.is_constant() or F.is_irreducible() + if phi_divides == 1: + return F.is_constant() + if phi_divides > 1: return False - return True - @cached_method - def equivalence_decomposition(self, f): + def equivalence_decomposition(self, f, partial=False): r""" Return an equivalence decomposition of ``f``, i.e., a polynomial `g(x)=e(x)\prod_i \phi_i(x)` with `e(x)` an equivalence unit (see @@ -678,7 +632,7 @@ def equivalence_decomposition(self, f): INPUT: - - ``f`` -- a polynomial in the domain of this valuation + - ``f`` -- a non-zero polynomial in the domain of this valuation ALGORITHM: @@ -764,49 +718,33 @@ def equivalence_decomposition(self, f): 363-395. """ - if f.parent() is not self.domain(): - raise ValueError("f must be in the domain of the valuation") + f = self.domain().coerce(f) + if f.is_zero(): raise ValueError("equivalence decomposition of zero is not defined") from sage.structure.factorization import Factorization - if not self.domain().base_ring().is_field(): - v = self.extension(self.domain().change_ring(self.domain().base_ring().fraction_field())) - ret = v.equivalence_decomposition(v.domain()(f)) - return Factorization([(g.change_ring(self.domain().base_ring()),e) for g,e in ret], unit=ret.unit().change_ring(self.domain().base_ring())) - if self.is_equivalence_unit(f): return Factorization([],unit=f) - if not self.is_commensurable_inductive(): - raise NotImplementedError("only implemented for inductive valuations") - - f0 = f # used to check correctness of the output - - phi_divides = 0 - while self.valuations(f).next() > self(f): - f = f-self.coefficients(f).next() - assert self.phi().divides(f) - f,_ = f.quo_rem(self.phi()) - phi_divides += 1 - - R = self.equivalence_unit(-self(f)) - R_ = self.equivalence_reciprocal(R) + if not self.domain().base_ring().is_field(): + domain = self.domain().change_ring(self.domain().base_ring().fraction_field()) + v = self.extension(domain) + ret = v.equivalence_decomposition(v.domain()(f)) + return Factorization([(g.change_ring(self.domain().base_ring()),e) for g,e in ret], unit=ret.unit().change_ring(self.domain().base_ring())) - F = self.reduce(f*R) + R, phi_divides, F = self._equivalence_reduction(f) F = F.factor() from sage.misc.misc import verbose - verbose("%s factors as %s = %s in reduction"%(f0,F.prod(),F),caller_name="equivalence_decomposition") - unit = F.unit() + verbose("%s factors as %s = %s in reduction"%(f, F.prod(), F), caller_name="equivalence_decomposition") + unit = self.lift(self.residue_ring()(F.unit())) * self.equivalence_reciprocal(R) F = list(F) - unit = self.lift( self.residue_ring()(unit) ) from sage.misc.all import prod unit *= self.lift(self.residue_ring()(prod([ psi.leading_coefficient()**e for psi,e in F ]))) F = [(self.lift_to_key(psi/psi.leading_coefficient()),e) for psi,e in F] - - unit *= R_ * prod([self.equivalence_unit(-self(g))**e for g,e in F]) + unit *= prod([self.equivalence_unit(-self(g))**e for g,e in F]) if phi_divides: for i,(g,e) in enumerate(F): @@ -817,8 +755,81 @@ def equivalence_decomposition(self, f): F.append((self.phi(),phi_divides)) ret = Factorization(F, unit=unit) - assert self.is_equivalent(ret.prod(), f0) # this might fail because of leading zeros + + assert self.is_equivalent(ret.prod(), f) # this might fail because of leading zeros in inexact rings assert self.is_equivalence_unit(ret.unit()) + + return ret + + def minimal_representative(self, f): + r""" + Return a minimal representative for ``f``, i.e., a pair `e, a` such + that ``f`` :meth:`is_equivalent` to `e a`, `e` is an + :meth:`equivalence_unit`, and `a` :meth:`is_minimal` and monic. + + INPUT: + + - ``f`` -- a non-zero polynomial which is not an equivalence unit + + OUTPUT: + + A factorization which has `e` as its unit and `a` as its unique factor. + + ALGORITHM: + + We use the algorithm described in the proof of Lemma 4.1 of [ML1936']. + In the expansion `f=\sum_i f_i\phi^i` take `e=f_i` for the largest `i` + with `f_i\phi^i` minimal (see :meth:`effective_degree`). + Let `h` be the :meth:`equivalence_reciprocal` of `e` and take `a` given + by the terms of minimal valuation in the expansion of `e f`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,10) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.minimal_representative(x + 2) + (1 + O(2^10))*x + + sage: v = v.augmentation(x, 1) + sage: v.minimal_representative(x + 2) + (1 + O(2^10))*x + 2 + O(2^11) + sage: f = x^3 + 6*x + 4 + sage: F = v.minimal_representative(f); F + (2 + 2^2 + O(2^11)) * ((1 + O(2^10))*x + 2 + 2^2 + 2^4 + 2^6 + 2^8 + 2^10 + O(2^11)) + sage: v.is_minimal(F[0][0]) + True + sage: v.is_equivalent(F.prod(), f) + True + + """ + f = self.domain().coerce(f) + + if f.is_zero(): + raise ValueError("zero has no minimal representative") + + degree = self.effective_degree(f) + if degree == 0: + raise ValueError("equivalence units can not have a minimal representative") + + e = list(self.coefficients(f))[degree] + h = self.equivalence_reciprocal(e).map_coefficients(lambda c:_lift_to_maximal_precision(c)) + g = h*f + vg = self(g) + + coeffs = [c if v == vg else c.parent().zero() for v,c in zip(self.valuations(g), self.coefficients(g))] + coeffs[degree] = self.domain().base_ring().one() + ret = sum([c*self._phi**i for i,c in enumerate(coeffs)]) + + assert self.effective_degree(ret) == degree + assert ret.is_monic() + assert self.is_minimal(ret) + + from sage.structure.factorization import Factorization + ret = Factorization([(ret, 1)], unit=e) + + assert self.is_equivalent(ret.prod(), f) # this might fail because of leading zeros return ret def _test_is_equivalence_irreducible(self, **options): diff --git a/valuation.py b/valuation.py index 739cb07ca86..9cf2d3f83ae 100644 --- a/valuation.py +++ b/valuation.py @@ -499,7 +499,7 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): sage: G = x^4 + 2*x^3 + 2*x^2 - 2*x + 2 sage: v.mac_lane_approximants(G) [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = 1/4 ]] - sage: v.mac_lane_approximants(G, infinity) + sage: v.mac_lane_approximants(G, infinity) # long time [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = 1/4, v((1 + O(2^10))*x^4 + (2 + O(2^11))*x^3 + (2 + O(2^11))*x^2 + (2 + 2^2 + 2^3 + 2^4 + 2^5 + 2^6 + 2^7 + 2^8 + 2^9 + 2^10 + O(2^11))*x + (2 + O(2^11))) = +Infinity ]] The factorization of primes in the Gaussian integers can be read off From 830b21e92e607c0d049e71f8eaa24013436485b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 2 Nov 2016 01:54:22 -0500 Subject: [PATCH 065/740] fix typo --- gauss_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gauss_valuation.py b/gauss_valuation.py index 6d2996ea962..78b5cd5131b 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -615,7 +615,7 @@ def is_trivial(self): # TODO: declare this upstairs under a better name def _make_monic_integral(self, G): r""" - Return a polynomial ``G`` which defines the self extension of the base + Return a polynomial ``G`` which defines the same extension of the base ring of the domain of this valuation but which is monic and integral. EXAMPLES:: From 47c87637d522cbe645b847445a3d48ea9e9eb1d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 2 Nov 2016 01:57:52 -0500 Subject: [PATCH 066/740] enforce inheritance of inductive valuations --- gauss_valuation.py | 15 --------------- inductive_valuation.py | 17 +++++++++++++++++ trivial_valuation.py | 2 +- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/gauss_valuation.py b/gauss_valuation.py index 78b5cd5131b..58c4e1127cf 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -663,18 +663,3 @@ def _ge_(self, other): if other.is_trivial(): return other.is_discrete_valuation() return super(GaussValuation_generic, self)._ge_(other) - - def is_discrete_valuation(self): - r""" - Return whether this is a discrete valuation. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) - sage: v.is_discrete_valuation() - True - - """ - return True diff --git a/inductive_valuation.py b/inductive_valuation.py index c4bcb416eeb..15f34bae1f5 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -207,6 +207,23 @@ def _test_equivalence_reciprocal(self, **options): g = self.equivalence_reciprocal(f) tester.assertEqual(self.reduce(f*g), 1) + def _test_inductive_valuation_inheritance(self, **options): + r""" + Test that every instance that is a :class:`InductiveValuation` is + either a :class:`FiniteInductiveValuation` or a + :class:`InfiniteInductiveValuation`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v._test_inductive_valuation_inheritance() + + """ + tester = self._tester(**options) + tester.assertTrue(isinstance(self, InfiniteInductiveValuation) != isinstance(self, FiniteInductiveValuation)) + class FiniteInductiveValuation(InductiveValuation, DiscreteValuation): r""" Abstract base class for iterated :class:`AugmentedValuation` on top of a diff --git a/trivial_valuation.py b/trivial_valuation.py index ae6d1826bab..d29e2aa6a48 100644 --- a/trivial_valuation.py +++ b/trivial_valuation.py @@ -301,7 +301,7 @@ class TrivialDiscreteValuation(TrivialDiscretePseudoValuation_base, DiscreteValu TESTS:: - sage: TestSuite(v).run() + sage: TestSuite(v).run() # long time """ def __init__(self, parent): From f3ab95970a3092262ef8df6a2c81f0a806020cee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 2 Nov 2016 20:57:59 -0500 Subject: [PATCH 067/740] fix docstring of shift --- gauss_valuation.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gauss_valuation.py b/gauss_valuation.py index 58c4e1127cf..6fb1f737f27 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -229,8 +229,7 @@ def uniformizer(self): def shift(self, f, s): """ - Multiply ``f`` by the ``s``th power of the uniformizer of this - valuation. + Multiply ``f`` by a power of the uniformizer which has valuation ``s``. INPUT: From bc7224718b0fb49af7c2985d8ba84258fb79b0d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 2 Nov 2016 22:02:32 -0500 Subject: [PATCH 068/740] Declare more methods in inductive valuation and fix related doctests --- augmented_valuation.py | 13 +- developing_valuation.py | 26 +++- gauss_valuation.py | 64 +++++---- inductive_valuation.py | 281 +++++++++++++++++++++++++++++++++++++++- limit_valuation.py | 4 +- valuation.py | 2 +- 6 files changed, 350 insertions(+), 40 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 8e1655d3c76..a878b3716a3 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -329,12 +329,13 @@ def _repr_(self): [ Gauss valuation induced by 2-adic valuation, v((1 + O(2^5))*x^2 + (1 + O(2^5))*x + u + O(2^5)) = 1/2 ] """ - vals = self._augmentations() + vals = self.augmentation_chain() + vals.reverse() vals = [ "v(%s) = %s"%(v._phi, v._mu) if isinstance(v, AugmentedValuation_base) else str(v) for v in vals ] return "[ %s ]"%", ".join(vals) - def _augmentations(self): - return self._base_valuation._augmentations() + [self] + def augmentation_chain(self): + return [self] + self._base_valuation.augmentation_chain() @cached_method def value_group(self): @@ -817,7 +818,7 @@ def psi(self): @cached_method def residue_ring(self): - generator = 'u' + str(len(self._augmentations()) - 1) + generator = 'u' + str(len(self.augmentation_chain()) - 1) base = self._base_valuation.residue_ring().base() if self.psi().degree() > 1: @@ -944,8 +945,8 @@ def is_gauss_valuation(self): # Is this correct? Can there be trivial augmentations? return False - def _make_monic_integral(self, G): - return self._base_valuation._make_monic_integral(G) + def monic_integral_model(self, G): + return self._base_valuation.monic_integral_model(G) def _ge_(self, other): from gauss_valuation import GaussValuation_generic diff --git a/developing_valuation.py b/developing_valuation.py index 346944137b7..8c4cfc46f52 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -281,7 +281,29 @@ def _call_(self, f): @abstract_method def valuations(self, f): - pass + """ + Return the valuations of the `f_i\phi^i` in the expansion `f=\sum f_i\phi^i`. + + INPUT: + + - ``f`` -- a polynomial in the domain of this valuation + + OUTPUT: + + A list, each entry a rational numbers or infinity, the valuations of + `f_0, f_1\phi, \dots` + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R = Qp(2,5) + sage: S. = R[] + sage: v = GaussValuation(S, pAdicValuation(R)) + sage: f = x^2 + 2*x + 16 + sage: list(v.valuations(f)) + [4, 1, 0] + + """ def _repr_(self): r""" @@ -298,7 +320,7 @@ def _repr_(self): """ return "`%s`-adic valuation of %s"%(self._phi, self.domain()) - def _make_monic_integral(self, G): + def monic_integral_model(self, G): if G.is_monic() and self(G) >= 0: return G raise NotImplementedError("The polynomial %r is not monic integral and %r does not provide the means to rewrite it to a monic integral polynomial."%(G, self)) diff --git a/gauss_valuation.py b/gauss_valuation.py index 6fb1f737f27..cd7ea7a7fda 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -276,7 +276,6 @@ def shift(self, f, s): return f.map_coefficients(lambda c:self._base_valuation.shift(c, s)) - # TODO: declare this upstairs def valuations(self, f): """ Return the valuations of the `f_i\phi^i` in the expansion `f=\sum f_i\phi^i`. @@ -287,14 +286,14 @@ def valuations(self, f): OUTPUT: - A list of rational numbers, the valuations of `f_0, f_1\phi, \dots` + A list, each entry a rational numbers or infinity, the valuations of `f_0, f_1\phi, \dots` EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R = Qp(2,5) + sage: R = ZZ sage: S. = R[] - sage: v = GaussValuation(S, pAdicValuation(R)) + sage: v = GaussValuation(S, pAdicValuation(R, 2)) sage: f = x^2 + 2*x + 16 sage: list(v.valuations(f)) [4, 1, 0] @@ -401,10 +400,10 @@ def lift(self, F): return F.map_coefficients(lambda c:self._base_valuation.lift(c), self._base_valuation.domain()) - # TODO: declare this upstairs def lift_to_key(self, F): """ - Lift the irreducible polynomial ``F`` to a key polynomial. + Lift the irreducible polynomial ``F`` from the :meth:`residue_ring` to + a key polynomial over this valuation. INPUT: @@ -420,13 +419,12 @@ def lift_to_key(self, F): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,10) + sage: R. = QQ sage: S. = R[] - sage: v = GaussValuation(S) + sage: v = GaussValuation(S, pAdicValuation(QQ, 2)) sage: y = v.residue_ring().gen() - sage: u0 = v.residue_ring().base_ring().gen() - sage: f = v.lift_to_key(y^2 + y + u0); f - (1 + O(2^10))*x^2 + (1 + O(2^10))*x + u + O(2^10) + sage: f = v.lift_to_key(y^2 + y + 1); f + x^2 + x + 1 """ F = self.residue_ring().coerce(F) @@ -440,7 +438,6 @@ def lift_to_key(self, F): return self.lift(F) - # TODO: declare this upstairs def equivalence_unit(self, s): """ Return an equivalence unit of valuation ``s``. @@ -464,10 +461,21 @@ def equivalence_unit(self, s): ret = self._base_valuation.shift(one, s) return self.domain()(ret) - # TODO: eliminate this - element_with_valuation = equivalence_unit + def element_with_valuation(self, s): + r""" + Return a polynomial of minimal degree with valuation ``s``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v.element_with_valuation(-2) + 1/4 + + """ + return self.equivalence_unit(s) - # TODO: declare this uptstairs def E(self): """ Return the ramification index of this valuation over its underlying @@ -486,7 +494,6 @@ def E(self): from sage.rings.all import ZZ return ZZ.one() - # TODO: declare this upstairs def F(self): """ Return the degree of the residue field extension of this valuation @@ -563,7 +570,6 @@ def restriction(self, ring): return self._base_valuation return super(GaussValuation_generic, self).restriction(ring) - # TODO: declare this upstairs def is_gauss_valuation(self): r""" Return whether this valuation is a Gauss valuation. @@ -580,16 +586,18 @@ def is_gauss_valuation(self): """ return True - # TODO: declare this upstairs under a better name - def _augmentations(self): + def augmentation_chain(self): r""" + Return a list with the chain of augmentations down to the underlying + :class:`GaussValuation`. + EXAMPLES:: sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) - sage: v._augmentations() + sage: v.augmentation_chain() [Gauss valuation induced by 2-adic valuation] """ @@ -611,19 +619,19 @@ def is_trivial(self): """ return self._base_valuation.is_trivial() - # TODO: declare this upstairs under a better name - def _make_monic_integral(self, G): + def monic_integral_model(self, G): r""" - Return a polynomial ``G`` which defines the same extension of the base - ring of the domain of this valuation but which is monic and integral. + Return a monic integral irreducible polynomial which defines the same + extension of the base ring of the domain as the irreducible polynomial + ``G``. EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) - sage: v._make_monic_integral(5*x^2 + 1/2*x + 1/4) - x^2 + 1/5*x + 1/5 + sage: R. = Qp(2, 5)[] + sage: v = GaussValuation(R) + sage: v.monic_integral_model(5*x^2 + 1/2*x + 1/4) + (1 + O(2^5))*x^2 + (1 + 2^2 + 2^3 + O(2^5))*x + (1 + 2^2 + 2^3 + O(2^5)) """ if not G.is_monic(): diff --git a/inductive_valuation.py b/inductive_valuation.py index 15f34bae1f5..1558c13a777 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -38,6 +38,7 @@ from developing_valuation import DevelopingValuation from sage.misc.cachefunc import cached_method +from sage.misc.abstract_method import abstract_method class InductiveValuation(DevelopingValuation): r""" @@ -173,6 +174,229 @@ def equivalence_reciprocal(self, f): return h + @abstract_method + def equivalence_unit(self, s): + """ + Return an equivalence unit of valuation ``s``. + + INPUT: + + - ``s`` -- an element of the :meth:`value_group` + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: S. = Qp(3,5)[] + sage: v = GaussValuation(S) + sage: v.equivalence_unit(2) + (3^2 + O(3^7)) + sage: v.equivalence_unit(-2) + (3^-2 + O(3^3)) + + Note that this might fail for negative ``s`` if the domain is not + defined over a field:: + + sage: v = pAdicValuation(ZZ, 2) + sage: R. = ZZ[] + sage: w = GaussValuation(R, v) + sage: w.equivalence_unit(1) + 2 + sage: w.equivalence_unit(-1) + Traceback (most recent call last): + ... + TypeError: no conversion of this rational to integer + + """ + + @abstract_method + def augmentation_chain(self): + r""" + Return a list with the chain of augmentations down to the underlying + :class:`GaussValuation`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.augmentation_chain() + [Gauss valuation induced by 2-adic valuation] + + """ + + @abstract_method + def is_gauss_valuation(self): + r""" + Return whether this valuation is a Gauss valuation over the domain. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.is_gauss_valuation() + True + + """ + + @abstract_method + def E(self): + """ + Return the ramification index of this valuation over its underlying + Gauss valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.E() + 1 + + """ + + @abstract_method + def F(self): + """ + Return the residual degree of this valuation over its Gauss extension. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.F() + 1 + + """ + + @abstract_method + def monic_integral_model(self, G): + r""" + Return a monic integral irreducible polynomial which defines the same + extension of the base ring of the domain as the irreducible polynomial + ``G``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v.monic_integral_model(5*x^2 + 1/2*x + 1/4) + x^2 + 1/5*x + 1/5 + + """ + + @abstract_method + def element_with_valuation(self, s): + r""" + Return a polynomial of minimal degree with valuation ``s``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v.element_with_valuation(-2) + 1/4 + + """ + + def _test_element_with_valuation(self, **options): + r""" + Test the correctness of :meth:`element_with_valuation`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v._test_element_with_valuation() + + """ + tester = self._tester(**options) + chain = self.augmentation_chain() + for s in tester.some_elements(self.value_group().some_elements()): + R = self.element_with_valuation(s) + tester.assertEqual(self(R), s) + if chain != [self]: + base = chain[1] + if s in base.value_group(): + S = base.element_with_valuation(s) + tester.assertEqual(self(S), s) + tester.assertGreaterEqual(S.degree(), R.degree()) + + def _test_EF(self, **options): + r""" + Test the correctness of :meth:`E` and :meth:`F`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v._test_EF() + + """ + tester = self._tester(**options) + chain = self.augmentation_chain() + for w,v in zip(chain, chain[1:]): + from sage.rings.all import infinity, ZZ + if w(w.phi()) == infinity: + tester.assertEqual(w.E(), v.E()) + tester.assertIn(w.E(), ZZ) + tester.assertIn(w.F(), ZZ) + tester.assertGreaterEqual(w.E(), v.E()) + tester.assertGreaterEqual(w.F(), v.F()) + + def _test_augmentation_chain(self, **options): + r""" + Test the correctness of :meth:`augmentation_chain`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v._test_augmentation_chain() + + """ + tester = self._tester(**options) + chain = self.augmentation_chain() + tester.assertIs(chain[0], self) + tester.assertTrue(chain[-1].is_gauss_valuation()) + for w,v in zip(chain, chain[1:]): + tester.assertGreaterEqual(w, v) + + def _test_equivalence_unit(self, **options): + r""" + Test the correctness of :meth:`lift_to_key`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v._test_equivalence_unit() + + """ + tester = self._tester(**options) + for s in tester.some_elements(self.value_group().some_elements()): + try: + R = self.equivalence_unit(s) + except ValueError: + if s >= 0 or self.domain().base_ring() in Fields(): + raise + + tester.assertIs(R.parent(), self.domain()) + tester.assertEqual(self(R), s) + tester.assertTrue(self.is_equivalence_unit(R)) + def _test_is_equivalence_unit(self, **options): r""" Test the correctness of :meth:`is_equivalence_unit`. @@ -468,7 +692,7 @@ def mac_lane_step(self, G, assume_squarefree=False): # G must be integral, otherwise, e.g., the effective degree is too low # We try to turn G into a monic integral polynomial that describes the same extension # This might fail if the constants of our polynomial ring do not form a field - return self.mac_lane_step(self._make_monic_integral(G), assume_squarefree=assume_squarefree) + return self.mac_lane_step(self.monic_integral_model(G), assume_squarefree=assume_squarefree) if not assume_squarefree and not G.is_squarefree(): raise ValueError("G must be squarefree") @@ -849,6 +1073,61 @@ def minimal_representative(self, f): assert self.is_equivalent(ret.prod(), f) # this might fail because of leading zeros return ret + @abstract_method + def lift_to_key(self, F): + """ + Lift the irreducible polynomial ``F`` from the ;meth:`residue_ring` to + a key polynomial over this valuation. + + INPUT: + + - ``F`` -- an irreducible non-constant monic polynomial in + :meth:`residue_ring` of this valuation + + OUTPUT: + + A polynomial `f` in the domain of this valuation which is a key + polynomial for this valuation and which, for a suitable equivalence + unit `R`, satifies that the reduction of `Rf` is ``F`` + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,10) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: y = v.residue_ring().gen() + sage: u0 = v.residue_ring().base_ring().gen() + sage: f = v.lift_to_key(y^2 + y + u0); f + (1 + O(2^10))*x^2 + (1 + O(2^10))*x + u + O(2^10) + + """ + + def _test_lift_to_key(self, **options): + r""" + Test the correctness of :meth:`lift_to_key`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v._test_lift_to_key() + + """ + tester = self._tester(**options) + S = tester.some_elements(self.residue_ring().some_elements()) + for F in S: + if F.is_monic() and not F.is_constant() and F.is_irreducible(): + f = self.lift_to_key(F) + tester.assertIs(f.parent(), self.domain()) + tester.assertTrue(self.is_key(f)) + + from sage.categories.fields import Fields + if self.domain().base_ring() in Fields(): + R = self.equivalence_unit(-self(f)) + tester.assertEqual(self.reduce(f*R), F) + def _test_is_equivalence_irreducible(self, **options): r""" Test the correctness of :meth:`is_equivalence_irreducible`. diff --git a/limit_valuation.py b/limit_valuation.py index 10f725c5228..ee061e5da94 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -892,8 +892,8 @@ def _repr_(self): if isinstance(self._base_valuation, MacLaneLimitValuation): # print the minimal information that singles out this valuation from all approximants assert(self._base_valuation._initial_approximation in self._approximants) - approximants = [v._augmentations() for v in self._approximants] - augmentations = self._base_valuation._approximation._augmentations() + approximants = [v.augmentation_chain()[::-1] for v in self._approximants] + augmentations = self._base_valuation._approximation.augmentation_chain()[::-1] unique_approximant = self._base_valuation._initial_approximation for l in range(len(augmentations)): if len([a for a in approximants if a[:l+1] == augmentations[:l+1]]) == 1: diff --git a/valuation.py b/valuation.py index 9cf2d3f83ae..37c2784d633 100644 --- a/valuation.py +++ b/valuation.py @@ -708,7 +708,7 @@ def mac_lane_approximant(self, G, valuation, approximants = None): # on domain that extends its restriction to the base field. from sage.rings.all import infinity if valuation(G) != infinity: - G_integral = valuation._make_monic_integral(G) + G_integral = valuation.monic_integral_model(G) v = valuation while not v.is_gauss_valuation(): if v(G_integral) <= v._base_valuation(G_integral): From f29a8a2085d0bf37b423532b22f42f8d1364c3fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 2 Nov 2016 22:16:08 -0500 Subject: [PATCH 069/740] Fixed doctests in developing_valuation --- __init__.py | 1 + augmented_valuation.py | 2 +- developing_valuation.py | 125 ++++++++++------------------------------ 3 files changed, 32 insertions(+), 96 deletions(-) diff --git a/__init__.py b/__init__.py index 4a694d91ab4..2c0ac39e7e5 100644 --- a/__init__.py +++ b/__init__.py @@ -27,6 +27,7 @@ from .gauss_valuation import GaussValuation_generic from .valuation import DiscretePseudoValuation, DiscreteValuation, InfiniteDiscretePseudoValuation from .padic_valuation import pAdicValuation_base, pAdicValuation_int, pAdicValuation_padic, pAdicFromLimitValuation +from .developing_valuation import DevelopingValuation # ================= # MONKEY PATCH SAGE diff --git a/augmented_valuation.py b/augmented_valuation.py index a878b3716a3..e4849ab1478 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -33,7 +33,7 @@ sys.path.append(os.getcwd()) sys.path.append(os.path.dirname(os.getcwd())) -from developing_valuation import _lift_to_maximal_precision +from inductive_valuation import _lift_to_maximal_precision from inductive_valuation import FiniteInductiveValuation, InfiniteInductiveValuation, InductiveValuation from valuation import InfiniteDiscretePseudoValuation, DiscreteValuation diff --git a/developing_valuation.py b/developing_valuation.py index 8c4cfc46f52..d4235b6f3bc 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -30,26 +30,6 @@ from sage.misc.cachefunc import cached_method -def _lift_to_maximal_precision(c): - r""" - Lift ``c`` to maximal precision if the parent is not exact. - - EXAMPLES:: - - sage: from sage.rings.padics.developing_valuation import _lift_to_maximal_precision - sage: R = Zp(2,5) - sage: x = R(1,2); x - 1 + O(2^2) - sage: _lift_to_maximal_precision(x) - 1 + O(2^5) - - sage: x = 1 - sage: _lift_to_maximal_precision(x) - 1 - - """ - return c if c.parent().is_exact() else c.lift_to_precision() - class DevelopingValuation(DiscretePseudoValuation): r""" Abstract base class for a discrete valuation of polynomials defined over @@ -74,14 +54,13 @@ def __init__(self, parent, phi): sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 7)) sage: isinstance(v, DevelopingValuation) + True """ domain = parent.domain() from sage.rings.polynomial.polynomial_ring import is_PolynomialRing - if not is_PolynomialRing(domain): - raise TypeError("domain must be a polynomial ring but %r is not"%(domain,)) - if not domain.ngens() == 1: - raise NotImplementedError("domain must be a univariate polynomial ring but %r is not"%(domain, )) + if not is_PolynomialRing(domain) or not domain.ngens() == 1: + raise TypeError("domain must be a univariate polynomial ring but %r is not"%(domain,)) phi = domain.coerce(phi) if phi.is_constant() or not phi.is_monic(): @@ -96,6 +75,7 @@ def phi(self): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R = Zp(2,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -119,6 +99,7 @@ def effective_degree(self, f): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R = Zp(2,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -151,62 +132,40 @@ def coefficients(self, f): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R = Qp(2,5) sage: S. = R[] sage: v = GaussValuation(S) sage: f = x^2 + 2*x + 3 sage: list(v.coefficients(f)) # note that these constants are in the polynomial ring - [1 + 2 + O(2^5), 2 + O(2^6), 1 + O(2^5)] - sage: v = v.extension( x^2 + x + 1, 1) + [(1 + 2 + O(2^5)), (2 + O(2^6)), (1 + O(2^5))] + sage: v = v.augmentation( x^2 + x + 1, 1) sage: list(v.coefficients(f)) - [(1 + O(2^5))*x + 2 + O(2^5), 1 + O(2^5)] + [(1 + O(2^5))*x + (2 + O(2^5)), (1 + O(2^5))] """ - if f.parent() is not self.domain(): - raise ValueError("f must be in the domain of the valuation") + f = self.domain().coerce(f) if self.phi().degree() == 1: from itertools import imap - return imap(f.parent(), f(self.phi().parent().gen() - self.phi()[0]).coefficients(sparse=False)) + for c in imap(f.parent(), f(self.phi().parent().gen() - self.phi()[0]).coefficients(sparse=False)): yield c else: - return self.__coefficients(f) + while f.degree() >= 0: + f,r = self._quo_rem(f) + yield r - def __coefficients(self, f): + def _quo_rem(self, f): r""" - Helper method for :meth:`coefficients` to create an iterator if `\phi` - is not linear. - - INPUT: - - - ``f`` -- a monic polynomial in the domain of this valuation - - OUTPUT: - - An iterator `[f_0,f_1,\dots]` of polynomials in the domain of this - valuation such that `f=\sum_i f_i\phi^i` - - EXAMPLES:: - - sage: R = Qp(2,5) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: v = v.extension( x^2 + x + 1, 1) - sage: f = x^2 + 2*x + 3 - sage: list(v.coefficients(f)) # indirect doctest - [(1 + O(2^5))*x + 2 + O(2^5), 1 + O(2^5)] + Compute the quotient and remainder of ``f`` divided by the key + polynomial :meth:`phi`. """ - while f.degree() >= 0: - f,r = self.__quo_rem(f) - yield r - - def __quo_rem(self, f): - qr = [ self.__quo_rem_monomial(i) for i in range(f.degree()+1) ] + qr = [ self._quo_rem_monomial(i) for i in range(f.degree()+1) ] q = [ f[i]*g for i,(g,_) in enumerate(qr) ] r = [ f[i]*h for i,(_,h) in enumerate(qr) ] return sum(q), sum(r) @cached_method - def __quo_rem_monomial(self, degree): + def _quo_rem_monomial(self, degree): f = self.domain().one() << degree return f.quo_rem(self.phi()) @@ -220,26 +179,22 @@ def newton_polygon(self, f): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R = Qp(2,5) sage: S. = R[] sage: v = GaussValuation(S) sage: f = x^2 + 2*x + 3 sage: v.newton_polygon(f) - Newton Polygon with vertices [(0, 0), (2, 0)] + Finite Newton polygon with 2 vertices: (0, 0), (2, 0) - sage: v = v.extension( x^2 + x + 1, 1) + sage: v = v.augmentation( x^2 + x + 1, 1) sage: v.newton_polygon(f) - Newton Polygon with vertices [(0, 0), (1, 1)] + Finite Newton polygon with 2 vertices: (0, 0), (1, 1) sage: v.newton_polygon( f * v.phi()^3 ) - Newton Polygon with vertices [(0, +Infinity), (3, 3), (4, 4)] - - .. SEEALSO:: - - :class:`newton_polygon.NewtonPolygon` + Finite Newton polygon with 2 vertices: (3, 3), (4, 4) """ - if f.parent() is not self.domain(): - raise ValueError("f must be in the domain of the valuation") + f = self.domain().coerce(f) from sage.geometry.newton_polygon import NewtonPolygon return NewtonPolygon(enumerate(self.valuations(f))) @@ -254,6 +209,7 @@ def _call_(self, f): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R = Qp(2,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -261,7 +217,7 @@ def _call_(self, f): sage: v(f) 0 - sage: v = v.extension( x^2 + x + 1, 1) + sage: v = v.augmentation( x^2 + x + 1, 1) sage: v(f) 0 sage: v(f * v.phi()^3 ) @@ -270,8 +226,7 @@ def _call_(self, f): +Infinity """ - if f.parent() is not self.domain(): - raise ValueError("f must be in the domain of the valuation %s but is in %s"%(self.domain(),f.parent())) + f = self.domain().coerce(f) if f.is_zero(): from sage.rings.all import infinity @@ -281,7 +236,7 @@ def _call_(self, f): @abstract_method def valuations(self, f): - """ + r""" Return the valuations of the `f_i\phi^i` in the expansion `f=\sum f_i\phi^i`. INPUT: @@ -305,32 +260,13 @@ def valuations(self, f): """ - def _repr_(self): - r""" - Return a printable representation of this valuation. - - EXAMPLES:: - - sage: R = Qp(2,5) - sage: S. = R[] - sage: from sage.rings.padics.developing_valuation import DevelopingValuation - sage: DevelopingValuation(S, x) - `(1 + O(2^5))*x`-adic valuation of Univariate Polynomial Ring in x over 2-adic Field with capped relative precision 5 - - """ - return "`%s`-adic valuation of %s"%(self._phi, self.domain()) - - def monic_integral_model(self, G): - if G.is_monic() and self(G) >= 0: - return G - raise NotImplementedError("The polynomial %r is not monic integral and %r does not provide the means to rewrite it to a monic integral polynomial."%(G, self)) - def _test_effective_degree(self, **options): r""" Test the correctness of :meth:`effective_degree`. EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R = Zp(2,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -343,4 +279,3 @@ def _test_effective_degree(self, **options): if x == 0: continue tester.assertEqual(self.effective_degree(x), 0) - From 81c39847066a4edc175b5609f1c5f79836a5f9e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 3 Nov 2016 08:05:16 -0500 Subject: [PATCH 070/740] Completed doctests for developing_valuation --- developing_valuation.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/developing_valuation.py b/developing_valuation.py index d4235b6f3bc..6fb7d68c2f1 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -156,8 +156,17 @@ def coefficients(self, f): def _quo_rem(self, f): r""" - Compute the quotient and remainder of ``f`` divided by the key + Return the quotient and remainder of ``f`` divided by the key polynomial :meth:`phi`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: S. = QQ[] + sage: v = GaussValuation(S, pAdicValuation(QQ, 2)) + sage: v._quo_rem(x^2 + 1) + (x, 1) + """ qr = [ self._quo_rem_monomial(i) for i in range(f.degree()+1) ] q = [ f[i]*g for i,(g,_) in enumerate(qr) ] @@ -166,6 +175,19 @@ def _quo_rem(self, f): @cached_method def _quo_rem_monomial(self, degree): + r""" + Return the quotient and remainder of `x^\mathrm{degree}` divided by the + key polynomial :meth:`phy`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: S. = QQ[] + sage: v = GaussValuation(S, pAdicValuation(QQ, 2)) + sage: v._quo_rem_monomial(10) + (x^9, 0) + + """ f = self.domain().one() << degree return f.quo_rem(self.phi()) From 2321a876749b319737cf5c264e45ccba294ae319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 3 Nov 2016 08:30:50 -0500 Subject: [PATCH 071/740] Integrated doctests for __init__ and augmented_valuation --- __init__.py | 86 +++++++++++++++++++--------- augmented_valuation.py | 123 +++++++++++++++++++---------------------- 2 files changed, 118 insertions(+), 91 deletions(-) diff --git a/__init__.py b/__init__.py index 2c0ac39e7e5..b296b21f856 100644 --- a/__init__.py +++ b/__init__.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- - +r""" +Monkey patches to make the MacLane code work in standalone mode, i.e., without +modifying the sage source code at build time. +""" #***************************************************************************** # Copyright (C) 2016 Julian Rüth # @@ -9,25 +12,39 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from .valuation_space import DiscretePseudoValuationSpace -from .trivial_valuation import TrivialValuation, TrivialPseudoValuation -from .padic_valuation import pAdicValuation -from .gauss_valuation import GaussValuation -from .value_group import DiscreteValuationCodomain, DiscreteValueGroup -from .function_field_valuation import FunctionFieldValuation -from .augmented_valuation import AugmentedValuation +# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) +import sys, os +if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: + sys.path.append(os.getcwd()) + sys.path.append(os.path.dirname(os.getcwd())) + +import valuation_space +from valuation_space import DiscretePseudoValuationSpace +import trivial_valuation +from trivial_valuation import TrivialValuation, TrivialPseudoValuation +import padic_valuation +from padic_valuation import pAdicValuation +import gauss_valuation +from gauss_valuation import GaussValuation +import value_group +from value_group import DiscreteValuationCodomain, DiscreteValueGroup +import function_field_valuation +from function_field_valuation import FunctionFieldValuation +import augmented_valuation +from augmented_valuation import AugmentedValuation # fix unpickling and type checks of classes (otherwise, the instances of the # local file and the instances that come from the mac_lane import define # different types) -from .trivial_valuation import TrivialDiscreteValuation, TrivialDiscretePseudoValuation -from .function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldFromLimitValuation -from .limit_valuation import LimitValuation, MacLaneLimitValuation, FiniteExtensionFromInfiniteValuation, FiniteExtensionFromLimitValuation, LimitValuation_generic -from .augmented_valuation import FiniteAugmentedValuation, InfiniteAugmentedValuation -from .gauss_valuation import GaussValuation_generic -from .valuation import DiscretePseudoValuation, DiscreteValuation, InfiniteDiscretePseudoValuation -from .padic_valuation import pAdicValuation_base, pAdicValuation_int, pAdicValuation_padic, pAdicFromLimitValuation -from .developing_valuation import DevelopingValuation +from trivial_valuation import TrivialDiscreteValuation, TrivialDiscretePseudoValuation +from function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldFromLimitValuation +from limit_valuation import LimitValuation, MacLaneLimitValuation, FiniteExtensionFromInfiniteValuation, FiniteExtensionFromLimitValuation, LimitValuation_generic +from augmented_valuation import FiniteAugmentedValuation, InfiniteAugmentedValuation +from gauss_valuation import GaussValuation_generic +from valuation import DiscretePseudoValuation, DiscreteValuation, InfiniteDiscretePseudoValuation +from padic_valuation import pAdicValuation_base, pAdicValuation_int, pAdicValuation_padic, pAdicFromLimitValuation +from developing_valuation import DevelopingValuation +from augmented_valuation import AugmentedValuation_base # ================= # MONKEY PATCH SAGE @@ -38,26 +55,45 @@ sage.rings.padics.padic_generic.pAdicGeneric.valuation = lambda self: pAdicValuation(self) # Fix contains check of rational fuction fields -r""" -sage: K. = FunctionField(QQ) -sage: K(1) in QQ -True -sage: K(x) in K._ring -True -""" def to_polynomial(self, x): + r""" + TESTS:: + + sage; from mac_lane import * + sage: K. = FunctionField(QQ) + sage: K(x) in K._ring + True + + """ R = x.parent()._ring K = x.parent().constant_base_field() if x.denominator() in K: return x.numerator()/K(x.denominator()) raise ValueError("Only polynomials can be converted to the underlying polynomial ring") +def to_constant(self, x): + r""" + TESTS:: + + sage; from mac_lane import * + sage: K. = FunctionField(QQ) + sage: K(1) in QQ + True + + """ + K = x.parent().constant_base_field() + if x.denominator() in K and x.numerator() in K: + return K(x.numerator()) / K(x.denominator()) + raise ValueError("only constants can be converted to the underlying constant field") + sage.rings.function_field.function_field.RationalFunctionField._to_polynomial = to_polynomial +sage.rings.function_field.function_field.RationalFunctionField._to_constant = to_constant sage.rings.function_field.function_field.RationalFunctionField.__old_init__ = sage.rings.function_field.function_field.RationalFunctionField.__init__ def __init__(self, *args, **kwargs): self.__old_init__(*args, **kwargs) from sage.categories.morphism import SetMorphism self._ring.register_conversion(SetMorphism(self.Hom(self._ring), self._to_polynomial)) + self.constant_base_field().register_conversion(SetMorphism(self.Hom(self.constant_base_field()), self._to_constant)) sage.rings.function_field.function_field.RationalFunctionField.__init__ = __init__ del(__init__) @@ -184,7 +220,7 @@ def absolute_extension(self): sage: l. = k.extension(b^2+b+a); l Univariate Quotient Polynomial Ring in b over Finite Field in a of size 2^2 with modulus b^2 + b + a sage: from_ll,to_ll, ll = l.absolute_extension(); ll - Finite Field in z4 of size 2^4 + Finite Field in v4 of size 2^4 sage: all([to_ll(from_ll(ll.gen()**i)) == ll.gen()**i for i in range(ll.degree())]) True @@ -192,7 +228,7 @@ def absolute_extension(self): sage: m. = l.extension(c^2+b*c+b); m Univariate Quotient Polynomial Ring in c over Univariate Quotient Polynomial Ring in b over Finite Field in a of size 2^2 with modulus b^2 + b + a with modulus c^2 + b*c + b sage: from_mm, to_mm, mm = m.absolute_extension(); mm - Finite Field in z8 of size 2^8 + Finite Field in v8 of size 2^8 sage: all([to_mm(from_mm(mm.gen()**i)) == mm.gen()**i for i in range(mm.degree())]) True diff --git a/augmented_valuation.py b/augmented_valuation.py index e4849ab1478..8d78485097c 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -84,10 +84,11 @@ class AugmentedValuation_base(InductiveValuation): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: K. = Qq(4,5) sage: R. = K[] sage: v = GaussValuation(R) - sage: v.extension(x, 1/2) # indirect doctest + sage: v.augmentation(x, 1/2) # indirect doctest [ Gauss valuation induced by 2-adic valuation, v((1 + O(2^5))*x) = 1/2 ] """ @@ -97,13 +98,13 @@ def __init__(self, parent, v, phi, mu): TESTS:: - sage: from sage.rings.padics.augmented_valuation import AugmentedValuation + sage: from mac_lane import * # optional: standalone sage: K. = Qq(4,5) sage: R. = K[] sage: v = GaussValuation(R) sage: w = AugmentedValuation(v, x, 1/2) - sage: type(w) - + sage: isinstance(w, AugmentedValuation_base) + True """ from sage.rings.all import QQ, infinity @@ -128,12 +129,13 @@ def _call_(self, f): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R = Qp(2,5) sage: S. = R[] sage: v = GaussValuation(S) sage: f = x^2 + 2*x + 3 - sage: v = v.extension( x^2 + x + 1, 1) + sage: v = v.augmentation( x^2 + x + 1, 1) sage: v(f) 0 sage: v(f * v.phi()^3 ) @@ -215,10 +217,11 @@ def equivalence_unit(self, s): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) - sage: w = v.extension(x^2 + x + u, 1) + sage: w = v.augmentation(x^2 + x + u, 1) sage: w.equivalence_unit(0) 1 + O(2^5) sage: w.equivalence_unit(-4) @@ -228,18 +231,18 @@ def equivalence_unit(self, s): divide it. Therefore, its valuation is in the value group of the base valuation:: - sage: w = v.extension(x, 1/2) + sage: w = v.augmentation(x, 1/2) sage: w.equivalence_unit(3/2) Traceback (most recent call last): ... - ValueError: v must be an integer + ValueError: 3/2 is not in the value group of 2-adic valuation sage: w.equivalence_unit(1) 2 + O(2^6) An equivalence unit might not be integral, even if ``s >= 0``:: - sage: v = v.extension(x, 3/4) - sage: w = v.extension(x^4 + 8, 5) + sage: v = v.augmentation(x, 3/4) + sage: w = v.augmentation(x^4 + 8, 5) sage: w.equivalence_unit(1/2) (2^-1 + O(2^4))*x^2 @@ -272,10 +275,11 @@ def element_with_valuation(self, s): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) - sage: w = v.extension(x^2 + x + u, 1/2) + sage: w = v.augmentation(x^2 + x + u, 1/2) sage: w.element_with_valuation(0) 1 + O(2^5) sage: w.element_with_valuation(1/2) @@ -321,10 +325,11 @@ def _repr_(self): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) - sage: w = v.extension(x^2 + x + u, 1/2) + sage: w = v.augmentation(x^2 + x + u, 1/2) sage: w # indirect doctest [ Gauss valuation induced by 2-adic valuation, v((1 + O(2^5))*x^2 + (1 + O(2^5))*x + u + O(2^5)) = 1/2 ] @@ -351,19 +356,20 @@ def value_group(self): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) - sage: w = v.extension(x, infinity) + sage: w = v.augmentation(x, infinity) sage: w.value_group() - Fractional ideal (1) + Additive Abelian Group generated by 1 - sage: w = v.extension(x^2 + x + u, 1/2) + sage: w = v.augmentation(x^2 + x + u, 1/2) sage: w.value_group() - Fractional ideal (1/2) - sage: w = w.extension((x^2 + x + u)^2 + 2, 5/3) + Additive Abelian Group generated by 1/2 + sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) sage: w.value_group() - Fractional ideal (1/6) + Additive Abelian Group generated by 1/6 """ base = self._base_valuation.value_group() @@ -387,19 +393,20 @@ def valuations(self, f): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) - sage: w = v.extension(x^2 + x + u, 1/2) + sage: w = v.augmentation(x^2 + x + u, 1/2) sage: list(w.valuations( x^2 + 1 )) [0, 1/2] - sage: w = w.extension((x^2 + x + u)^2 + 2, 5/3) + sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) sage: list(w.valuations( ((x^2 + x + u)^2 + 2)^3 )) [+Infinity, +Infinity, +Infinity, 5] TESTS:: - sage: w = v.extension(x, infinity) + sage: w = v.augmentation(x, infinity) sage: list(w.valuations( x^2 + 1 )) [0, +Infinity, +Infinity] @@ -446,6 +453,7 @@ def reduce(self, f): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,10) sage: S. = R[] sage: v = GaussValuation(S) @@ -454,7 +462,7 @@ def reduce(self, f): sage: v.reduce(S(u)) u0 - sage: w = v.extension(x^2 + x + u, 1/2) + sage: w = v.augmentation(x^2 + x + u, 1/2) sage: w.reduce(S.one()) 1 sage: w.reduce(S(2)) @@ -469,7 +477,7 @@ def reduce(self, f): sage: w.reduce(f + x + 1) x + u1 + 1 - sage: w = w.extension((x^2 + x + u)^2 + 2, 5/3) + sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) sage: g = ((x^2 + x + u)^2 + 2)^3 / 2^5 sage: w.reduce(g) x @@ -556,6 +564,7 @@ def lift(self, F): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,10) sage: S. = R[] sage: v = GaussValuation(S) @@ -564,7 +573,7 @@ def lift(self, F): sage: v.lift(y) (1 + O(2^10))*x - sage: w = v.extension(x^2 + x + u, 1/2) + sage: w = v.augmentation(x^2 + x + u, 1/2) sage: r = w.residue_ring() sage: y = r.gen() sage: u1 = w.residue_ring().base().gen() @@ -581,7 +590,7 @@ def lift(self, F): sage: w.reduce(w.lift(y + u1 + 1)) == y + u1 + 1 True - sage: w = w.extension((x^2 + x + u)^2 + 2, 5/3) + sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) sage: r = w.residue_ring() sage: y = r.gen() sage: u2 = w.residue_ring().base().gen() @@ -595,8 +604,8 @@ def lift(self, F): A more complicated example:: sage: v = GaussValuation(S) - sage: v = v.extension(x^2 + x + u, 1) - sage: v = v.extension((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) + sage: v = v.augmentation(x^2 + x + u, 1) + sage: v = v.augmentation((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) sage: u = v.residue_ring().base().gen() sage: F = v.residue_ring()(u); F @@ -696,11 +705,12 @@ def lift_to_key(self, F): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,10) sage: S. = R[] sage: v = GaussValuation(S) - sage: w = v.extension(x^2 + x + u, 1/2) + sage: w = v.augmentation(x^2 + x + u, 1/2) sage: y = w.residue_ring().gen() sage: f = w.lift_to_key(y + 1); f (1 + O(2^10))*x^4 + (2 + O(2^11))*x^3 + (1 + u*2 + O(2^10))*x^2 + (u*2 + O(2^11))*x + (u + 1) + u*2 + u*2^2 + u*2^3 + u*2^4 + u*2^5 + u*2^6 + u*2^7 + u*2^8 + u*2^9 + O(2^10) @@ -710,8 +720,8 @@ def lift_to_key(self, F): A more complicated example:: sage: v = GaussValuation(S) - sage: v = v.extension(x^2 + x + u, 1) - sage: v = v.extension((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) + sage: v = v.augmentation(x^2 + x + u, 1) + sage: v = v.augmentation((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) sage: u = v.residue_ring().base().gen() sage: y = v.residue_ring().gen() @@ -769,13 +779,14 @@ def tau(self): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) - sage: w = v.extension(x^2 + x + u, 1/2) + sage: w = v.augmentation(x^2 + x + u, 1/2) sage: w.tau() 2 - sage: w = w.extension((x^2 + x + u)^2 + 2, 5/3) + sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) sage: w.tau() 3 @@ -800,13 +811,14 @@ def psi(self): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) - sage: w = v.extension(x^2 + x + u, 1/2) + sage: w = v.augmentation(x^2 + x + u, 1/2) sage: w.psi() x^2 + x + u0 - sage: w = w.extension((x^2 + x + u)^2 + 2, 5/3) + sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) sage: w.psi() x + 1 @@ -839,29 +851,6 @@ def residue_field_generator(self): assert self.psi()(ret).is_zero() return ret - def is_commensurable_inductive(self): - """ - Return whether this valuation is a commensurable inductive valuation - over the discrete valuation of the base ring of the polynomial ring, as - defined in section 4 of [ML1936]. - - EXAMPLES:: - - sage: R. = Qq(4,5) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: w = v.extension(x^2 + x + u, 1) - sage: w.is_commensurable_inductive() - True - - REFERENCES: - - .. [ML1936] Mac Lane, S. (1936). A construction for prime ideals as absolute - values of an algebraic field. Duke Mathematical Journal, 2(3), 492-510. - - """ - return self._base_valuation.is_commensurable_inductive() - def E(self): """ Return the ramification index of this valuation over its underlying @@ -869,13 +858,14 @@ def E(self): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) - sage: w = v.extension(x^2 + x + u, 1) + sage: w = v.augmentation(x^2 + x + u, 1) sage: w.E() 1 - sage: w = v.extension(x, 1/2) + sage: w = v.augmentation(x, 1/2) sage: w.E() 2 @@ -891,13 +881,14 @@ def F(self): EXAMPLES:: + sage: from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) - sage: w = v.extension(x^2 + x + u, 1) + sage: w = v.augmentation(x^2 + x + u, 1) sage: w.F() 2 - sage: w = v.extension(x, 1/2) + sage: w = v.augmentation(x, 1/2) sage: w.F() 1 @@ -953,12 +944,12 @@ def _ge_(self, other): if other.is_trivial(): return other.is_discrete_valuation() if isinstance(other, GaussValuation_generic): - return self._base_valuation >= other + return self._base_valuation >= other if isinstance(other, AugmentedValuation_base): - if self(other._phi) >= other._mu: - return self >= other._base_valuation - else: - return False + if self(other._phi) >= other._mu: + return self >= other._base_valuation + else: + return False return super(AugmentedValuation_base, self)._ge_(other) From 614a251dfe3c446ab79b51db8d39c428c66742e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 6 Nov 2016 14:48:35 -0500 Subject: [PATCH 072/740] Doctest mac_lane/__init__ --- __init__.py | 182 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 163 insertions(+), 19 deletions(-) diff --git a/__init__.py b/__init__.py index b296b21f856..4b798fd2f53 100644 --- a/__init__.py +++ b/__init__.py @@ -59,7 +59,6 @@ def to_polynomial(self, x): r""" TESTS:: - sage; from mac_lane import * sage: K. = FunctionField(QQ) sage: K(x) in K._ring True @@ -75,7 +74,6 @@ def to_constant(self, x): r""" TESTS:: - sage; from mac_lane import * sage: K. = FunctionField(QQ) sage: K(1) in QQ True @@ -90,6 +88,14 @@ def to_constant(self, x): sage.rings.function_field.function_field.RationalFunctionField._to_constant = to_constant sage.rings.function_field.function_field.RationalFunctionField.__old_init__ = sage.rings.function_field.function_field.RationalFunctionField.__init__ def __init__(self, *args, **kwargs): + r""" + TESTS:: + + sage: K. = FunctionField(QQ) + sage: K(1/2) in QQ + True + + """ self.__old_init__(*args, **kwargs) from sage.categories.morphism import SetMorphism self._ring.register_conversion(SetMorphism(self.Hom(self._ring), self._to_polynomial)) @@ -100,26 +106,63 @@ def __init__(self, *args, **kwargs): del(to_polynomial) # implement principal_part for newton polygons +r""" +TESTS:: + + sage: NP = sage.geometry.newton_polygon.NewtonPolygon([(0,1),(1,0),(2,1)]) + sage: NP.principal_part() + Infinite Newton polygon with 2 vertices: (0, 1), (1, 0) ending by an infinite line of slope 0 + +""" import sage.geometry.newton_polygon sage.geometry.newton_polygon.NewtonPolygon_element.principal_part = lambda self: sage.geometry.newton_polygon.NewtonPolygon(self.vertices(), last_slope=0) sage.geometry.newton_polygon.NewtonPolygon_element.sides = lambda self: zip(self.vertices(), self.vertices()[1:]) # implement coercion of function fields that comes from coercion of their base fields def _coerce_map_from_(target, source): + r""" + TESTS:: + + sage: K. = FunctionField(QQ) + sage: L. = FunctionField(GaussianIntegers().fraction_field()) + sage: L.has_coerce_map_from(K) + True + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^3 + 1) + sage: K. = FunctionField(GaussianIntegers().fraction_field()) + sage: R. = K[] + sage: M. = K.extension(y^3 + 1) + sage: M.has_coerce_map_from(L) # not tested, base morphism is not implemented + True + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(I^2 + 1) + sage: M. = FunctionField(GaussianIntegers().fraction_field()) + sage: M.has_coerce_map_from(L) # not tested, base_morphism is not implemented + True + + """ from sage.categories.function_fields import FunctionFields if source in FunctionFields(): - if source.base_field() is source and target.base_field() is target: - if source.variable_name() == target.variable_name(): - # source and target are rational function fields in the same variable - base_coercion = target.constant_field().coerce_map_from(source.constant_field()) - if base_coercion is not None: - return source.hom([target.gen()], base_morphism=base_coercion) - if source.base_field() is not source and target.base_field() is not target: - # source and target are extensions of rational function fields - base_coercion = target.base_field().coerce_map_from(source.base_field()) + if source.base_field() is source: + if target.base_field() is target: + # source and target are rational function fields + if source.variable_name() == target.variable_name(): + # ... in the same variable + base_coercion = target.constant_field().coerce_map_from(source.constant_field()) + if base_coercion is not None: + return source.hom([target.gen()], base_morphism=base_coercion) + else: + # source is an extensions of rational function fields + base_coercion = target.coerce_map_from(source.base_field()) if base_coercion is not None: - # The base field of source coerces into the base field of target. - if source.polynomial().map_coefficients(base_coercion)(target.gen()) == 0: + # the base field of source coerces into the base field of target + target_polynomial = source.polynomial().map_coefficients(base_coercion) + # try to find a root of the defining polynomial in target + if target_polynomial(target.gen()) == 0: # The defining polynomial of source has a root in target, # therefore there is a map. To be sure that it is # canonical, we require a root of the defining polynomial @@ -128,11 +171,30 @@ def _coerce_map_from_(target, source): if source.variable_name() == target.variable_name(): return source.hom([target.gen()], base_morphism=base_coercion) + roots = target_polynomial.roots() + for root, _ in roots: + if target_polynomial(root) == 0: + # The defining polynomial of source has a root in target, + # therefore there is a map. To be sure that it is + # canonical, we require the names of the roots to match + if source.variable_name() == repr(root): + return source.hom([root], base_morphism=base_coercion) + sage.rings.function_field.function_field.FunctionField._coerce_map_from_ = _coerce_map_from_ del(_coerce_map_from_) # patch is_injective() for many morphisms def patch_is_injective(method, patch_map): + r""" + Patch ``method`` to return ``patch_map[type]`` if it returned a result of + ``type``. + + TESTS:: + + sage: QQ.coerce_map_from(ZZ).is_injective() # indirect doctest + True + + """ def patched(*args, **kwargs): ret = method(*args, **kwargs) if type(ret) in patch_map: @@ -143,6 +205,13 @@ def patched(*args, **kwargs): # a ring homomorphism from a field into a ring is injective (as it respects inverses) class RingHomomorphism_coercion_patched(sage.rings.morphism.RingHomomorphism_coercion): def is_injective(self): + r""" + TESTS:: + + sage: QQ.coerce_map_from(ZZ).is_injective() # indirect doctest + True + + """ from sage.categories.fields import Fields if self.domain() in Fields(): return True coercion = self.codomain().coerce_map_from(self.domain()) @@ -154,36 +223,99 @@ def is_injective(self): # a morphism of polynomial rings which is induced by a ring morphism on the base is injective if the morphis on the base is class PolynomialRingHomomorphism_from_base_patched(sage.rings.polynomial.polynomial_ring_homomorphism.PolynomialRingHomomorphism_from_base): def is_injective(self): + r""" + TESTS:: + + sage: QQ['x'].coerce_map_from(ZZ['x']).is_injective() # indirect doctest + True + + """ return self.underlying_map().is_injective() sage.rings.polynomial.polynomial_ring.PolynomialRing_general._coerce_map_from_ = patch_is_injective(sage.rings.polynomial.polynomial_ring.PolynomialRing_general._coerce_map_from_, {sage.rings.polynomial.polynomial_ring_homomorphism.PolynomialRingHomomorphism_from_base: (lambda morphism: PolynomialRingHomomorphism_from_base_patched(morphism.parent(), morphism.underlying_map()))}) # morphisms of number fields are injective class Q_to_quadratic_field_element_patched(sage.rings.number_field.number_field_element_quadratic.Q_to_quadratic_field_element): - def is_injective(self): return True + def is_injective(self): + r""" + TESTS:: + + sage: GaussianIntegers().fraction_field().coerce_map_from(QQ).is_injective() + True + + """ + return True class Z_to_quadratic_field_element_patched(sage.rings.number_field.number_field_element_quadratic.Z_to_quadratic_field_element): - def is_injective(self): return True + def is_injective(self): + r""" + TESTS:: + + sage: GaussianIntegers().fraction_field().coerce_map_from(ZZ).is_injective() + True + + """ + return True sage.rings.number_field.number_field.NumberField_quadratic._coerce_map_from_ = patch_is_injective(sage.rings.number_field.number_field.NumberField_quadratic._coerce_map_from_, {sage.rings.number_field.number_field_element_quadratic.Q_to_quadratic_field_element: (lambda morphism: Q_to_quadratic_field_element_patched(morphism.codomain())), sage.rings.number_field.number_field_element_quadratic.Z_to_quadratic_field_element: (lambda morphism: Z_to_quadratic_field_element_patched(morphism.codomain()))}) # the integers embed into the rationals class Z_to_Q_patched(sage.rings.rational.Z_to_Q): - def is_injective(self): return True + def is_injective(self): + r""" + TESTS:: + + sage: QQ.coerce_map_from(ZZ).is_injective() + True + + """ + return True from sage.rings.all import QQ QQ.coerce_map_from = patch_is_injective(QQ.coerce_map_from, {sage.rings.rational.Z_to_Q: (lambda morphism: Z_to_Q_patched())}) # the integers embed into their extensions in number fields class DefaultConvertMap_unique_patched(sage.structure.coerce_maps.DefaultConvertMap_unique): - def is_injective(self): return True + def is_injective(self): + r""" + TESTS:: + + sage: CyclotomicField(5).maximal_order().coerce_map_from(ZZ).is_injective() + True + + """ + from sage.rings.all import ZZ + if self.domain() is ZZ or domain is int or domain is long: + return True + return super(DefaultConvertMap_unique, self).is_injective() + def _coerce_map_from_patched(self, domain): + r""" + TESTS:: + + sage: CyclotomicField(5).maximal_order().coerce_map_from(ZZ).is_injective() + True + + """ from sage.rings.all import ZZ if domain is ZZ or domain is int or domain is long: return DefaultConvertMap_unique_patched(domain, self) return False -from sage.rings.number_field.order import Order -Order._coerce_map_from_ = _coerce_map_from_patched + +sage.rings.number_field.order.Order._coerce_map_from_ = _coerce_map_from_patched del(_coerce_map_from_patched) # factorization in polynomial quotient fields def _factor_univariate_polynomial(self, f): + r""" + TESTS:: + + sage: K = GF(2) + sage: R. = K[] + sage: L. = K.extension(x^2 + x + 1) + sage: R. = L[] + sage: L. = L.extension(y^2 + y + x) + sage: R. = L[] + sage: (T^2 + T + x).factor() + (T + y) * (T + y + 1) + + """ from sage.structure.factorization import Factorization if f.is_zero(): @@ -287,6 +419,18 @@ def absolute_extension(self): # factorization needs some linear algebra (it seems) def vector_space(self): + r""" + TESTS:: + + sage: K = GF(2) + sage: R. = K[] + sage: L. = K.extension(x^2 + x + 1) + sage: R. = L[] + sage: L. = L.extension(y^2 + y + x) + sage: L.vector_space() + Vector space of dimension 2 over Finite Field in x of size 2^2 + + """ if not self.base().base_ring().is_field(): raise ValueError From 5497fc7efca0eaf56684c730287bbcfff6dc04b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 6 Nov 2016 14:54:13 -0500 Subject: [PATCH 073/740] Fix coverage report in mac_lane/__init__.py --- __init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/__init__.py b/__init__.py index 4b798fd2f53..a67a259127e 100644 --- a/__init__.py +++ b/__init__.py @@ -60,7 +60,7 @@ def to_polynomial(self, x): TESTS:: sage: K. = FunctionField(QQ) - sage: K(x) in K._ring + sage: K(x) in K._ring # indirect doctest True """ @@ -75,7 +75,7 @@ def to_constant(self, x): TESTS:: sage: K. = FunctionField(QQ) - sage: K(1) in QQ + sage: K(1) in QQ # indirect doctest True """ @@ -289,7 +289,7 @@ def _coerce_map_from_patched(self, domain): r""" TESTS:: - sage: CyclotomicField(5).maximal_order().coerce_map_from(ZZ).is_injective() + sage: CyclotomicField(5).maximal_order().coerce_map_from(ZZ).is_injective() # indirect doctest True """ @@ -312,7 +312,7 @@ def _factor_univariate_polynomial(self, f): sage: R. = L[] sage: L. = L.extension(y^2 + y + x) sage: R. = L[] - sage: (T^2 + T + x).factor() + sage: (T^2 + T + x).factor() # indirect doctest (T + y) * (T + y + 1) """ From 07554db053825a8688bbd3ecaaf7634eb28a8032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 6 Nov 2016 19:21:29 -0500 Subject: [PATCH 074/740] Drop tau() and drop trivial intermediate valuations again The method tau() has been somewhat fuzzy since it depends on the implementation detail of which valuation is underlying the current one. The end user should not be able to care about this (easily.) --- augmented_valuation.py | 182 ++++++++++++++++++++++------------------- inductive_valuation.py | 3 +- 2 files changed, 102 insertions(+), 83 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 8d78485097c..ddc31593040 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -42,7 +42,51 @@ from sage.structure.factory import UniqueFactory class AugmentedValuationFactory(UniqueFactory): + r""" + Factory for augmented valuations. + + EXAMPLES: + + This factory is not meant to be called directly. Instead, + :meth:`augmentation` of a valuation should be called:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x, 1) # indirect doctest + + Note that trivial parts of the augmented valuation might be dropped, so you + should not rely on ``_base_valuation`` to be the valuation you started + with:: + + sage: w = w.augmentation(x, 2) + sage: w._base_valuation is v + True + + """ def create_key(self, base_valuation, phi, mu, check=True): + r""" + Create a key which uniquely identifies the valuation over + ``base_valuation`` which sends ``phi`` to ``mu``. + + .. NOTE:: + + Uniquenesse of the resulting valuation would not be too important. + However, it makes pickling and equality checks much easier. At the + same time, going through a factory makes it easier to enforce that + all instances correctly inherit methods from the parent Hom space. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x, 1) # indirect doctest + sage: ww = v.augmentation(x, 1) + sage: w is ww + True + + """ if check: is_key, reason = base_valuation.is_key(phi, explain=True) if not is_key: @@ -50,9 +94,25 @@ def create_key(self, base_valuation, phi, mu, check=True): if mu <= base_valuation(phi): raise ValueError("the value of the key polynomial must strictly increase but `%s` does not exceed `%s`."%(mu, base_valuation(phi))) + if isinstance(base_valuation, AugmentedValuation_base): + if phi.degree() == base_valuation.phi().degree(): + # drop base_valuation and extend base_valuation._base_valuation instead + return self.create_key(base_valuation._base_valuation, phi, mu, check=check) + return base_valuation, phi, mu def create_object(self, version, key): + r""" + Create the augmented valuation represented by ``key``. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x, 1) # indirect doctest + + """ base_valuation, phi, mu = key from valuation_space import DiscretePseudoValuationSpace @@ -69,7 +129,7 @@ class AugmentedValuation_base(InductiveValuation): An augmented valuation is a discrete valuation on a polynomial ring. It extends another discrete valuation `v` by setting the valuation of a polynomial `f` to the minumum of `v(f_i)i\mu` when writing `f=\sum_i - f_i\mu^i`. + f_i\phi^i`. INPUT: @@ -79,9 +139,6 @@ class AugmentedValuation_base(InductiveValuation): - ``mu`` -- a rational number such that ``mu > v(phi)`` or ``infinity`` - - ``check`` -- a boolean (default: ``True``), whether to check that the - parameters define an augemented value of ``v`` - EXAMPLES:: sage: from mac_lane import * # optional: standalone @@ -94,8 +151,6 @@ class AugmentedValuation_base(InductiveValuation): """ def __init__(self, parent, v, phi, mu): """ - Initialization. - TESTS:: sage: from mac_lane import * # optional: standalone @@ -144,8 +199,7 @@ def _call_(self, f): +Infinity """ - if f.parent() is not self.domain(): - raise ValueError("f must be in the domain of the valuation %s but is in %s"%(self.domain(),f.parent())) + f = self.domain().coerce(f) from sage.rings.all import infinity if f.is_zero(): @@ -173,36 +227,6 @@ def _call_(self, f): return ret return ret - @cached_method - def Q(self): - if self._mu is infinity or self.tau().is_zero(): - raise NotImplementedError - - return self.equivalence_unit(self._mu * self.tau()) - - @cached_method - def Q_(self): - try: - ret = self.equivalence_reciprocal(self.Q()) - - assert self.is_equivalence_unit(ret) - # esentially this checks that the reduction of Q'*phi^tau is the - # generator of the residue field - assert self._base_valuation.reduce(self.Q()*ret)(self.residue_field_generator()).is_one() - - except ValueError: - print "CHEATING - HARD CODED RECIPROCAL" - Q = self.Q() - pi = Q.parent().base().constant_base_field().uniformizer() - ret = Q/(pi**(self(Q)*2)) - - assert self.is_equivalence_unit(ret) - # esentially this checks that the reduction of Q'*phi^tau is the - # generator of the residue field - assert self._base_valuation.reduce(self.Q()*ret)(self.residue_field_generator()).is_one() - - return ret - def equivalence_unit(self, s): """ Return an equivalence unit of minimal degree and valuation ``s``. @@ -443,7 +467,8 @@ def reduce(self, f): If ``f`` has positive valuation, the reduction is simply zero. Otherwise, let `f=\sum f_i\phi^i` be the expansion of `f`, as computed by :meth:`coefficients`. Since the valuation is zero, the exponents `i` - must all be multiples of :meth:`tau`. + must all be multiples of `\tau`, the index the value group of the base + valuation in the value group of this valuation. Hence, there is an :meth:`equivalence_unit` `Q` with the same valuation as `\phi^\tau`. Let `Q'` be its :meth:`reciprocal_inverse`. Now, rewrite each term `f_i\phi^{i\tau}=(f_iQ^i)(\phi^\tauQ^{-1})^i`; @@ -530,12 +555,13 @@ def reduce(self, f): CV = zip(self.coefficients(f), self.valuations(f)) # rewrite as sum of f_i phi^{i tau}, i.e., drop most coefficients - assert not any([v==0 for i,(c,v) in enumerate(CV) if i % self.tau() != 0]) - CV = CV[::self.tau()] + tau = self.value_group().index(self._base_valuation.value_group()) + assert not any([v==0 for i,(c,v) in enumerate(CV) if i % tau != 0]) + CV = CV[::tau] # replace f_i by f_i Q^{i tau} - vQ = self._mu * self.tau() - CV = [(c*self.Q()**i, v - vQ*i) for i,(c,v) in enumerate(CV)] + vQ = self._mu * tau + CV = [(c*self._Q()**i, v - vQ*i) for i,(c,v) in enumerate(CV)] assert all([self._base_valuation(c)>=0 for c,v in CV]) # recursively reduce the f_i Q^{i tau} @@ -633,7 +659,7 @@ def lift(self, F): if F.is_one(): return self.domain().one() - if self.tau().is_zero(): + if self._base_valuation.is_trivial(): if not self._mu > 0: raise NotImplementedError if not F.is_constant(): @@ -669,8 +695,8 @@ def lift(self, F): coeffs = [ self._base_valuation.lift(c) for c in coeffs ] # now the coefficients correspond to the expansion with (f_iQ^i)(Q^{-1} phi)^i - # now we undo the factors of Q^i (the if else is necessary to handle the case when mu is infinity, i.e., when Q_() is undefined) - coeffs = [ (c if i == 0 else c*self.Q_()**i).map_coefficients(lambda d:_lift_to_maximal_precision(d)) for i,c in enumerate(coeffs) ] + # now we undo the factors of Q^i (the if else is necessary to handle the case when mu is infinity, i.e., when _Q_reciprocal() is undefined) + coeffs = [ (c if i == 0 else c*self._Q_reciprocal()**i).map_coefficients(lambda d:_lift_to_maximal_precision(d)) for i,c in enumerate(coeffs) ] RR = self.domain().change_ring(self.domain()) @@ -678,7 +704,8 @@ def lift(self, F): assert len(coeffs) <= 1 ret = RR(coeffs)[0] else: - ret = RR(coeffs)(self.phi()**self.tau()) + tau = self.value_group().index(self._base_valuation.value_group()) + ret = RR(coeffs)(self.phi()**tau) ret = ret.map_coefficients(lambda c:_lift_to_maximal_precision(c)) return ret @@ -753,7 +780,7 @@ def lift_to_key(self, F): assert self(f) == 0 assert self.reduce(f) == F - f *= self.Q()**F.degree() + f *= self._Q()**F.degree() CV = zip(self.coefficients(f), self.valuations(f)) vf = self(f) CV = [(c,v) if v==vf else (c.parent().zero(),infinity) for c,v in CV] @@ -767,39 +794,6 @@ def lift_to_key(self, F): assert self.is_key(ret) return ret - def tau(self): - """ - Return the factor which is needed to turn an element of the value group - of this valuation into an element of the value group of its base - valuation. - - OUTPUT: - - a positive integer - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,5) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: w = v.augmentation(x^2 + x + u, 1/2) - sage: w.tau() - 2 - sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) - sage: w.tau() - 3 - - """ - from sage.rings.all import ZZ - - if self._base_valuation.value_group().is_trivial(): - return ZZ.zero() - - assert self.value_group().numerator() == 1 - assert self._base_valuation.value_group().numerator() == 1 - return ZZ(self.value_group().denominator()) // ZZ(self._base_valuation.value_group().denominator()) - @cached_method def psi(self): """ @@ -872,7 +866,7 @@ def E(self): """ if self._base_valuation.is_trivial(): raise ValueError("there is no ramification over a trivial valuation") - return self.tau() * self._base_valuation.E() + return self.value_group().index(self._base_valuation.value_group()) * self._base_valuation.E() def F(self): """ @@ -957,6 +951,30 @@ def is_discrete_valuation(self): from sage.rings.all import infinity return self._mu != infinity + @cached_method + def _Q(self): + r""" + Return the polynomial `Q` used in the construction to :meth:`reduce` an + element to the :meth:`residue_ring`. + """ + if self._mu == infinity: + raise NotImplementedError("Q is not defined for infinite valuations") + + tau = self.value_group().index(self._base_valuation.value_group()) + return self.equivalence_unit(self._mu * tau) + + @cached_method + def _Q_reciprocal(self): + ret = self.equivalence_reciprocal(self._Q()) + + assert self.is_equivalence_unit(ret) + # esentially this checks that the reduction of Q'*phi^tau is the + # generator of the residue field + assert self._base_valuation.reduce(self._Q()*ret)(self.residue_field_generator()).is_one() + + return ret + + class FiniteAugmentedValuation(AugmentedValuation_base, FiniteInductiveValuation): pass diff --git a/inductive_valuation.py b/inductive_valuation.py index 1558c13a777..7ebcf04495a 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -649,11 +649,12 @@ def is_minimal(self, f): return f.degree() == self.phi().degree() else: + tau = self.value_group().index(self._base_valuation.value_group()) # see Theorem 9.4 of [ML1936'] return list(self.valuations(f))[-1] == self(f) and \ list(self.coefficients(f))[-1].is_constant() and \ list(self.valuations(f))[0] == self(f) and \ - self.tau().divides(len(list(self.coefficients(f))) - 1) + tau.divides(len(list(self.coefficients(f))) - 1) def mac_lane_step(self, G, assume_squarefree=False): r""" From a6485fd15e882ca847733c074d9ed88355715cf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 6 Nov 2016 19:48:35 -0500 Subject: [PATCH 075/740] Cleaning up augmented_valuation --- augmented_valuation.py | 74 +++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index ddc31593040..a50c2e7fc98 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -141,12 +141,19 @@ class AugmentedValuation_base(InductiveValuation): EXAMPLES:: - sage: from mac_lane import * # optional: standalone - sage: K. = Qq(4,5) - sage: R. = K[] - sage: v = GaussValuation(R) - sage: v.augmentation(x, 1/2) # indirect doctest - [ Gauss valuation induced by 2-adic valuation, v((1 + O(2^5))*x) = 1/2 ] + sage: from mac_lane import * # optional: standalone + sage: K. = CyclotomicField(5) + sage: R. = K[] + sage: v = GaussValuation(R, pAdicValuation(K, 2)) + sage: w = v.augmentation(x, 1/2); w # indirect doctest + [ Gauss valuation induced by 2-adic valuation, v(x) = 1/2 ] + sage: ww = w.augmentation(x^4 + 2*x^2 + 4*u, 3); ww + [ Gauss valuation induced by 2-adic valuation, v(x) = 1/2, v(x^4 + 2*x^2 + 4*u) = 3 ] + + TESTS:: + + sage: TestSuite(w).run() # long time + sage: TestSuite(ww).run() # long time """ def __init__(self, parent, v, phi, mu): @@ -332,17 +339,6 @@ def element_with_valuation(self, s): s -= self._mu return ret * self._base_valuation.element_with_valuation(s) - def _latex_(self): - vals = [self] - v = self - while isinstance(v, AugmentedValuation_base): - v = v._base_valuation - vals.append(v) - vals.reverse() - from sage.misc.latex import latex - vals = [ "v_%s(%s) = %s"%(i,latex(v._phi), latex(v._mu)) if isinstance(v, AugmentedValuation_base) else latex(v) for i,v in enumerate(vals) ] - return "[ %s ]"%", ".join(vals) - def _repr_(self): """ Return a printable representation of this valuation. @@ -364,6 +360,31 @@ def _repr_(self): return "[ %s ]"%", ".join(vals) def augmentation_chain(self): + r""" + Return a list with the chain of augmentations down to the underlying + :class:`GaussValuation`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x, 1) + sage: w.augmentation_chain() + [[ Gauss valuation induced by 2-adic valuation, v(x) = 1 ], + Gauss valuation induced by 2-adic valuation] + + For performance reasons, (and to simplify the underlying + implementation,) trivial augmentations might get dropped. You should + not rely on :meth:`augmentation_chain` to contain all the steps that + you specified to create the current valuation:: + + sage: ww = w.augmentation(x, 2) + sage: ww.augmentation_chain() + [[ Gauss valuation induced by 2-adic valuation, v(x) = 2 ], + Gauss valuation induced by 2-adic valuation] + + """ return [self] + self._base_valuation.augmentation_chain() @cached_method @@ -371,13 +392,6 @@ def value_group(self): """ Return the value group of this valuation. - OUTPUT: - - Currently, there is no support for additive subgroups of `\QQ`. - Therefore we create this value group as a fractional ideal of `\QQ`. - However, `\QQ` does not support fractional ideals, so we use fractional - ideals of the trivial extensions `\QQ[x]/(x)` - EXAMPLES:: sage: from mac_lane import * # optional: standalone @@ -397,7 +411,7 @@ def value_group(self): """ base = self._base_valuation.value_group() - if self._mu is infinity: + if self._mu == infinity: return base from value_group import DiscreteValueGroup return base + DiscreteValueGroup(self._mu) @@ -413,7 +427,7 @@ def valuations(self, f): OUTPUT: - An iterator over rational numbers, `[v(f_0), v(f_1\phi), \dots]` + An iterator over rational numbers (or infinity) `[v(f_0), v(f_1\phi), \dots]` EXAMPLES:: @@ -435,8 +449,7 @@ def valuations(self, f): [0, +Infinity, +Infinity] """ - if f.parent() is not self.domain(): - raise ValueError("f must be in the domain of this valuation") + f = self.domain().coerce(f) if self._mu is infinity: num_infty_coefficients = f.degree() // self.phi().degree() @@ -927,7 +940,6 @@ def uniformizer(self): return self.element_with_valuation(self.value_group()._generator) def is_gauss_valuation(self): - # Is this correct? Can there be trivial augmentations? return False def monic_integral_model(self, G): @@ -947,10 +959,6 @@ def _ge_(self, other): return super(AugmentedValuation_base, self)._ge_(other) - def is_discrete_valuation(self): - from sage.rings.all import infinity - return self._mu != infinity - @cached_method def _Q(self): r""" From 8682ad673f409e9d32c45effae85df67ca03ec66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 6 Nov 2016 19:48:51 -0500 Subject: [PATCH 076/740] fix tests --- inductive_valuation.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index 7ebcf04495a..e1d3bf45ac3 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -386,7 +386,11 @@ def _test_equivalence_unit(self, **options): """ tester = self._tester(**options) - for s in tester.some_elements(self.value_group().some_elements()): + if self.is_gauss_valuation(): + value_group = self.value_group() + else: + value_group = self.augmentation_chain()[1].value_group() + for s in tester.some_elements(value_group.some_elements()): try: R = self.equivalence_unit(s) except ValueError: @@ -1088,8 +1092,9 @@ def lift_to_key(self, F): OUTPUT: A polynomial `f` in the domain of this valuation which is a key - polynomial for this valuation and which, for a suitable equivalence - unit `R`, satifies that the reduction of `Rf` is ``F`` + polynomial for this valuation and which is such that an + :meth:`augmentation` with this polynomial adjoins a root of ``F`` to + the resulting :meth:`residue_ring`. EXAMPLES:: @@ -1124,10 +1129,8 @@ def _test_lift_to_key(self, **options): tester.assertIs(f.parent(), self.domain()) tester.assertTrue(self.is_key(f)) - from sage.categories.fields import Fields - if self.domain().base_ring() in Fields(): - R = self.equivalence_unit(-self(f)) - tester.assertEqual(self.reduce(f*R), F) + w = self.augmentation(f, self(f) + 1) + tester.assertGreaterEqual(len(w.residue_ring()(F).roots()), 1) def _test_is_equivalence_irreducible(self, **options): r""" From 36aa7f3fbd6bd50a7c34bb37dbb71495d47ca7fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 6 Nov 2016 19:51:59 -0500 Subject: [PATCH 077/740] Split implementation of AugmentedValuation.value_group() --- augmented_valuation.py | 65 ++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index a50c2e7fc98..e645957f109 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -387,35 +387,6 @@ def augmentation_chain(self): """ return [self] + self._base_valuation.augmentation_chain() - @cached_method - def value_group(self): - """ - Return the value group of this valuation. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,5) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: w = v.augmentation(x, infinity) - sage: w.value_group() - Additive Abelian Group generated by 1 - - sage: w = v.augmentation(x^2 + x + u, 1/2) - sage: w.value_group() - Additive Abelian Group generated by 1/2 - sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) - sage: w.value_group() - Additive Abelian Group generated by 1/6 - - """ - base = self._base_valuation.value_group() - if self._mu == infinity: - return base - from value_group import DiscreteValueGroup - return base + DiscreteValueGroup(self._mu) - def valuations(self, f): """ Return the valuations of the `f_i\phi^i` in the expansion `f=\sum_i @@ -984,8 +955,40 @@ def _Q_reciprocal(self): class FiniteAugmentedValuation(AugmentedValuation_base, FiniteInductiveValuation): - pass + @cached_method + def value_group(self): + """ + Return the value group of this valuation. + + EXAMPLES:: + + sage: w = v.augmentation(x^2 + x + u, 1/2) + sage: w.value_group() + Additive Abelian Group generated by 1/2 + sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) + sage: w.value_group() + Additive Abelian Group generated by 1/6 + + """ + base = self._base_valuation.value_group() + from value_group import DiscreteValueGroup + return base + DiscreteValueGroup(self._mu) class InfiniteAugmentedValuation(AugmentedValuation_base, InfiniteInductiveValuation): - pass + @cached_method + def value_group(self): + """ + Return the value group of this valuation. + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x, infinity) + sage: w.value_group() + Additive Abelian Group generated by 1 + + """ + return self._base_valuation.value_group() From ccf9c9dfd1acc0cd0c145a2091efd47639b675bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 6 Nov 2016 20:00:21 -0500 Subject: [PATCH 078/740] Split the implementation of AugmentedValuation.valuations() in a finite and infinite part --- augmented_valuation.py | 117 ++++++++++++++++++++++++++--------------- 1 file changed, 76 insertions(+), 41 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index e645957f109..757d52c3dc3 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -387,49 +387,17 @@ def augmentation_chain(self): """ return [self] + self._base_valuation.augmentation_chain() - def valuations(self, f): - """ - Return the valuations of the `f_i\phi^i` in the expansion `f=\sum_i - f_i\phi^i`. - - INPUT: - - - ``f`` -- a polynomial in the domain of this valuation - - OUTPUT: - - An iterator over rational numbers (or infinity) `[v(f_0), v(f_1\phi), \dots]` - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,5) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: w = v.augmentation(x^2 + x + u, 1/2) - sage: list(w.valuations( x^2 + 1 )) - [0, 1/2] - sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) - sage: list(w.valuations( ((x^2 + x + u)^2 + 2)^3 )) - [+Infinity, +Infinity, +Infinity, 5] - - TESTS:: - - sage: w = v.augmentation(x, infinity) - sage: list(w.valuations( x^2 + 1 )) - [0, +Infinity, +Infinity] - - """ - f = self.domain().coerce(f) + @cached_method + def residue_ring(self): + generator = 'u' + str(len(self.augmentation_chain()) - 1) - if self._mu is infinity: - num_infty_coefficients = f.degree() // self.phi().degree() - yield self._base_valuation(self.coefficients(f).next()) - for i in range(num_infty_coefficients): - yield infinity + base = self._base_valuation.residue_ring().base() + if self.psi().degree() > 1: + base = base.extension(self.psi(), names=generator) + if self._mu == infinity: + return base else: - for i,c in enumerate(self.coefficients(f)): - yield self._base_valuation(c) + i*self._mu + return base[self.domain().variable_name()] def reduce(self, f): r""" @@ -962,6 +930,10 @@ def value_group(self): EXAMPLES:: + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) sage: w = v.augmentation(x^2 + x + u, 1/2) sage: w.value_group() Additive Abelian Group generated by 1/2 @@ -974,6 +946,38 @@ def value_group(self): from value_group import DiscreteValueGroup return base + DiscreteValueGroup(self._mu) + def valuations(self, f): + """ + Return the valuations of the `f_i\phi^i` in the expansion `f=\sum_i + f_i\phi^i`. + + INPUT: + + - ``f`` -- a polynomial in the domain of this valuation + + OUTPUT: + + An iterator over rational numbers (or infinity) `[v(f_0), v(f_1\phi), \dots]` + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, 1/2) + sage: list(w.valuations( x^2 + 1 )) + [0, 1/2] + sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) + sage: list(w.valuations( ((x^2 + x + u)^2 + 2)^3 )) + [+Infinity, +Infinity, +Infinity, 5] + + """ + f = self.domain().coerce(f) + + for i,c in enumerate(self.coefficients(f)): + yield self._base_valuation(c) + i*self._mu + class InfiniteAugmentedValuation(AugmentedValuation_base, InfiniteInductiveValuation): @cached_method def value_group(self): @@ -992,3 +996,34 @@ def value_group(self): """ return self._base_valuation.value_group() + + def valuations(self, f): + """ + Return the valuations of the `f_i\phi^i` in the expansion `f=\sum_i + f_i\phi^i`. + + INPUT: + + - ``f`` -- a polynomial in the domain of this valuation + + OUTPUT: + + An iterator over rational numbers (or infinity) `[v(f_0), v(f_1\phi), \dots]` + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x, infinity) + sage: list(w.valuations( x^2 + 1 )) + [0, +Infinity, +Infinity] + + """ + f = self.domain().coerce(f) + + num_infty_coefficients = f.degree() // self.phi().degree() + yield self._base_valuation(self.coefficients(f).next()) + for i in range(num_infty_coefficients): + yield infinity From b789a2f08fb831ecfec1ced3d12d09e5a387e997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 6 Nov 2016 20:22:37 -0500 Subject: [PATCH 079/740] Separate AugmentedValuation.residue_ring() for the finite and infinite case --- augmented_valuation.py | 102 ++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 26 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 757d52c3dc3..a6e43fef63b 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -387,18 +387,6 @@ def augmentation_chain(self): """ return [self] + self._base_valuation.augmentation_chain() - @cached_method - def residue_ring(self): - generator = 'u' + str(len(self.augmentation_chain()) - 1) - - base = self._base_valuation.residue_ring().base() - if self.psi().degree() > 1: - base = base.extension(self.psi(), names=generator) - if self._mu == infinity: - return base - else: - return base[self.domain().variable_name()] - def reduce(self, f): r""" Reduce ``f`` module this valuation. @@ -774,22 +762,11 @@ def psi(self): assert F.is_irreducible() return F - @cached_method - def residue_ring(self): - generator = 'u' + str(len(self.augmentation_chain()) - 1) - - base = self._base_valuation.residue_ring().base() - if self.psi().degree() > 1: - base = base.extension(self.psi(), names=generator) - if self._mu == infinity: - return base - else: - return base[self.domain().variable_name()] - @cached_method def residue_field_generator(self): - if self.psi().degree() == 1: - ret = -self.psi()[0] + if self.residue_ring() == self._base_valuation.residue_ring(): + assert self.psi().degree() == 1 + ret = self.residue_ring().base()(-self.psi()[0]) else: ret = self.residue_ring().base().gen() @@ -978,6 +955,42 @@ def valuations(self, f): for i,c in enumerate(self.coefficients(f)): yield self._base_valuation(c) + i*self._mu + @cached_method + def residue_ring(self): + r""" + Return the residue ring of this valuation, i.e., the elements of + non-negative valuation modulo the elements of positive valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, 1) + sage: w.residue_ring() + Univariate Polynomial Ring in x over Finite Field in u1 of size 2^2 + + Since trivial valuations of finite fields are not implemented, the + resulting ring might be identical to the residue ring of the underlying + valuation:: + + sage: w = v.augmentation(x, 1) + sage: w.residue_ring() + Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) + + """ + generator = 'u' + str(len(self.augmentation_chain()) - 1) + + base = self._base_valuation.residue_ring().base() + if self.psi().degree() > 1: + # Do not call extension if self.psi().degree() == 1: + # In that case the resulting field appears to be the same as the original field, + # however, it is not == to the original field (for finite fields at + # least) but a distinct copy (this is a bug in finite field's + # extension() implementation.) + base = base.extension(self.psi(), names=generator) + return base[self.domain().variable_name()] + class InfiniteAugmentedValuation(AugmentedValuation_base, InfiniteInductiveValuation): @cached_method def value_group(self): @@ -1027,3 +1040,40 @@ def valuations(self, f): yield self._base_valuation(self.coefficients(f).next()) for i in range(num_infty_coefficients): yield infinity + + @cached_method + def residue_ring(self): + r""" + Return the residue ring of this valuation, i.e., the elements of + non-negative valuation modulo the elements of positive valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, infinity) + sage: w.residue_ring() + Finite Field in u1 of size 2^2 + + Since trivial valuations of finite fields are not implemented, the + resulting ring might be identical to the residue ring of the underlying + valuation:: + + sage: w = v.augmentation(x, infinity) + sage: w.residue_ring() + Finite Field of size 2 + + """ + generator = 'u' + str(len(self.augmentation_chain()) - 1) + + ret = self._base_valuation.residue_ring().base() + if self.psi().degree() > 1: + # Do not call extension if self.psi().degree() == 1: + # In that case the resulting field appears to be the same as the original field, + # however, it is not == to the original field (for finite fields at + # least) but a distinct copy (this is a bug in finite field's + # extension() implementation.) + return ret.extension(self.psi(), names=generator) + else: + return ret From aaf3f002d729704ecbff91e781b8f2d8ef1c8367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 6 Nov 2016 20:47:48 -0500 Subject: [PATCH 080/740] Split implementation of residue_field_generator() for the finite and infinite case --- augmented_valuation.py | 349 +++++++++++++++++++++++++---------------- 1 file changed, 213 insertions(+), 136 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index a6e43fef63b..dc84fa22400 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -387,129 +387,6 @@ def augmentation_chain(self): """ return [self] + self._base_valuation.augmentation_chain() - def reduce(self, f): - r""" - Reduce ``f`` module this valuation. - - INPUT: - - - ``f`` -- an element of the domain of this valuation of non-negative - valuation - - OUTPUT: - - an element of the :meth:`residue_ring` of this valuation, the reduction - modulo the ideal of elements of positive valuation - - ALGORITHM: - - We follow the algorithm given in the proof of Theorem 12.1 of [ML1936]: - If ``f`` has positive valuation, the reduction is simply zero. - Otherwise, let `f=\sum f_i\phi^i` be the expansion of `f`, as computed - by :meth:`coefficients`. Since the valuation is zero, the exponents `i` - must all be multiples of `\tau`, the index the value group of the base - valuation in the value group of this valuation. - Hence, there is an :meth:`equivalence_unit` `Q` with the same valuation - as `\phi^\tau`. Let `Q'` be its :meth:`reciprocal_inverse`. - Now, rewrite each term `f_i\phi^{i\tau}=(f_iQ^i)(\phi^\tauQ^{-1})^i`; - it turns out that the second factor in this expression is a lift of the - generator of the :meth:`residue_field`. The reduction of the first - factor can be computed recursively. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,10) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: v.reduce(x) - x - sage: v.reduce(S(u)) - u0 - - sage: w = v.augmentation(x^2 + x + u, 1/2) - sage: w.reduce(S.one()) - 1 - sage: w.reduce(S(2)) - 0 - sage: w.reduce(S(u)) - u0 - sage: w.reduce(x) # this gives the generator of the residue field extension of w over v - u1 - sage: f = (x^2 + x + u)^2 / 2 - sage: w.reduce(f) - x - sage: w.reduce(f + x + 1) - x + u1 + 1 - - sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) - sage: g = ((x^2 + x + u)^2 + 2)^3 / 2^5 - sage: w.reduce(g) - x - sage: w.reduce(f) - 1 - sage: w(f-1) > 0 # checking the above - True - sage: w.reduce(f * g) - x - sage: w.reduce(f + g) - x + 1 - - REFERENCES: - - .. [ML1936] Mac Lane, S. (1936). A construction for prime ideals as absolute - values of an algebraic field. Duke Mathematical Journal, 2(3), 492-510. - - .. SEEALSO:: - - :meth:`lift` - - """ - if f.parent() is not self.domain(): - raise ValueError("f must be in the domain of the valuation") - from sage.categories.fields import Fields - if not self.domain().base_ring() in Fields(): - raise NotImplementedError("only implemented for polynomial rings over fields") - - if self(f) < 0: - assert self(f) < 0 - raise ValueError("f must have non-negative valuation") - elif self(f) > 0: - return self.residue_ring().zero() - - # if this extends a trivial valuation, then this is very easy: just - # return the constant coefficient in the phi-adic expansion; everything - # else must have positive valuation - if self._base_valuation.value_group().is_trivial(): - assert self.valuations(f).next() == 0 - if self.value_group().is_trivial(): - raise NotImplementedError - return self.residue_ring()(self.coefficients(f).next())(self.residue_field_generator()) - - if self._mu == infinity: - # if this is an infinite valuation, then we can simply drop all but the - # constant term - constant_term = self.coefficients(f).next() - constant_term_reduced = self._base_valuation.reduce(constant_term) - return constant_term_reduced(self.residue_ring().gen()) - - CV = zip(self.coefficients(f), self.valuations(f)) - # rewrite as sum of f_i phi^{i tau}, i.e., drop most coefficients - tau = self.value_group().index(self._base_valuation.value_group()) - assert not any([v==0 for i,(c,v) in enumerate(CV) if i % tau != 0]) - CV = CV[::tau] - - # replace f_i by f_i Q^{i tau} - vQ = self._mu * tau - CV = [(c*self._Q()**i, v - vQ*i) for i,(c,v) in enumerate(CV)] - assert all([self._base_valuation(c)>=0 for c,v in CV]) - - # recursively reduce the f_i Q^{i tau} - C = [self._base_valuation.reduce(c)(self.residue_field_generator()) for c,v in CV] - - # reduce the Q'^i phi^i - return self.residue_ring()(C) - def lift(self, F): """ Return a polynomial whose :meth:`reduction` is ``F``. @@ -762,18 +639,6 @@ def psi(self): assert F.is_irreducible() return F - @cached_method - def residue_field_generator(self): - if self.residue_ring() == self._base_valuation.residue_ring(): - assert self.psi().degree() == 1 - ret = self.residue_ring().base()(-self.psi()[0]) - else: - ret = self.residue_ring().base().gen() - - assert ret.parent() is self.residue_ring().base() - assert self.psi()(ret).is_zero() - return ret - def E(self): """ Return the ramification index of this valuation over its underlying @@ -894,7 +759,7 @@ def _Q_reciprocal(self): assert self.is_equivalence_unit(ret) # esentially this checks that the reduction of Q'*phi^tau is the # generator of the residue field - assert self._base_valuation.reduce(self._Q()*ret)(self.residue_field_generator()).is_one() + assert self._base_valuation.reduce(self._Q()*ret)(self._residue_field_generator()).is_one() return ret @@ -991,6 +856,130 @@ def residue_ring(self): base = base.extension(self.psi(), names=generator) return base[self.domain().variable_name()] + def reduce(self, f): + r""" + Reduce ``f`` module this valuation. + + OUTPUT: + + an element of the :meth:`residue_ring` of this valuation, the reduction + modulo the ideal of elements of positive valuation + + ALGORITHM: + + We follow the algorithm given in the proof of Theorem 12.1 of [ML1936]: + If ``f`` has positive valuation, the reduction is simply zero. + Otherwise, let `f=\sum f_i\phi^i` be the expansion of `f`, as computed + by :meth:`coefficients`. Since the valuation is zero, the exponents `i` + must all be multiples of `\tau`, the index the value group of the base + valuation in the value group of this valuation. + Hence, there is an :meth:`equivalence_unit` `Q` with the same valuation + as `\phi^\tau`. Let `Q'` be its :meth:`reciprocal_inverse`. + Now, rewrite each term `f_i\phi^{i\tau}=(f_iQ^i)(\phi^\tauQ^{-1})^i`; + it turns out that the second factor in this expression is a lift of the + generator of the :meth:`residue_field`. The reduction of the first + factor can be computed recursively. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,10) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.reduce(x) + x + sage: v.reduce(S(u)) + u0 + + sage: w = v.augmentation(x^2 + x + u, 1/2) + sage: w.reduce(S.one()) + 1 + sage: w.reduce(S(2)) + 0 + sage: w.reduce(S(u)) + u0 + sage: w.reduce(x) # this gives the generator of the residue field extension of w over v + u1 + sage: f = (x^2 + x + u)^2 / 2 + sage: w.reduce(f) + x + sage: w.reduce(f + x + 1) + x + u1 + 1 + + sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) + sage: g = ((x^2 + x + u)^2 + 2)^3 / 2^5 + sage: w.reduce(g) + x + sage: w.reduce(f) + 1 + sage: w(f-1) > 0 # checking the above + True + sage: w.reduce(f * g) + x + sage: w.reduce(f + g) + x + 1 + + """ + f = self.domain().coerce(f) + + if self(f) < 0: + raise ValueError("f must have non-negative valuation") + elif self(f) > 0: + return self.residue_ring().zero() + + if self._base_valuation.value_group().is_trivial(): + # if this extends a trivial valuation, then this is very easy: just + # return the constant coefficient in the phi-adic expansion; everything + # else must have positive valuation + assert not self.value_group().is_trivial() + assert self.valuations(f).next() == 0 + return self.residue_ring()(self.coefficients(f).next())(self._residue_field_generator()) + + CV = zip(self.coefficients(f), self.valuations(f)) + # rewrite as sum of f_i phi^{i tau}, i.e., drop the coefficients that + # can have no influence on the reduction + tau = self.value_group().index(self._base_valuation.value_group()) + assert not any([v==0 for i,(c,v) in enumerate(CV) if i % tau != 0]) + CV = CV[::tau] + + # replace f_i by f_i Q^{i tau} + vQ = self._mu * tau + CV = [(c*self._Q()**i, v - vQ*i) for i,(c,v) in enumerate(CV)] + assert all([self._base_valuation(c)>=0 for c,v in CV]) + + # recursively reduce the f_i Q^{i tau} + C = [self._base_valuation.reduce(c)(self._residue_field_generator()) for c,v in CV] + + # reduce the Q'^i phi^i + return self.residue_ring()(C) + + @cached_method + def _residue_field_generator(self): + r""" + Return a root of :meth:`psi` in :meth:`residue_ring`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,10) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, 1/2) + sage: w._residue_field_generator() + u1 + + """ + if self.residue_ring() == self._base_valuation.residue_ring(): + assert self.psi().degree() == 1 + ret = self.residue_ring().base()(-self.psi()[0]) + else: + ret = self.residue_ring().base().gen() + + assert ret.parent() is self.residue_ring().base() + assert self.psi()(ret).is_zero() + return ret + + class InfiniteAugmentedValuation(AugmentedValuation_base, InfiniteInductiveValuation): @cached_method def value_group(self): @@ -1077,3 +1066,91 @@ def residue_ring(self): return ret.extension(self.psi(), names=generator) else: return ret + + def reduce(self, f): + r""" + Reduce ``f`` module this valuation. + + OUTPUT: + + an element of the :meth:`residue_ring` of this valuation, the reduction + modulo the ideal of elements of positive valuation + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,10) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, infinity) + sage: w.reduce(S.one()) + 1 + sage: w.reduce(S(2)) + 0 + sage: w.reduce(S(u)) + u0 + sage: w.reduce(x) # this gives the generator of the residue field extension of w over v + u1 + sage: f = (x^2 + x + u)^2 / 2 + sage: w.reduce(f) + 0 + sage: w.reduce(f + x + 1) + u1 + 1 + + sage: w = v.augmentation(x^2 + x + u, 1/2) + sage: w = w.augmentation((x^2 + x + u)^2 + 2, infinity) + sage: g = ((x^2 + x + u)^2 + 2)^3 / 2^5 + sage: w.reduce(g) + 0 + sage: w.reduce(f) + 1 + sage: w(f-1) > 0 # 1 is really the reduction of f + True + sage: w.reduce(f * g) + 0 + sage: w.reduce(f + g) + 1 + + REFERENCES: + + .. [ML1936] Mac Lane, S. (1936). A construction for prime ideals as absolute + values of an algebraic field. Duke Mathematical Journal, 2(3), 492-510. + + """ + f = self.domain().coerce(f) + + if self(f) < 0: + assert self(f) < 0 + raise ValueError("f must have non-negative valuation") + elif self(f) > 0: + return self.residue_ring().zero() + + constant_term = self.coefficients(f).next() + constant_term_reduced = self._base_valuation.reduce(constant_term) + return constant_term_reduced(self._residue_field_generator()) + + @cached_method + def _residue_field_generator(self): + r""" + Return a root of :meth:`psi` in :meth:`residue_ring`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,10) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, infinity) + sage: w._residue_field_generator() + u1 + + """ + if self.residue_ring() == self._base_valuation.residue_ring().base(): + assert self.psi().degree() == 1 + ret = self.residue_ring()(-self.psi()[0]) + else: + ret = self.residue_ring().gen() + + assert ret.parent() is self.residue_ring() + assert self.psi()(ret).is_zero() + return ret From b1cfe1aaccebf044bfb12e18c369c5ec72341e27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 7 Nov 2016 03:08:41 -0500 Subject: [PATCH 081/740] Split remaining implementations of augmented_valuation --- augmented_valuation.py | 627 ++++++++++++++++++++++++----------------- gauss_valuation.py | 8 +- padic_valuation.py | 2 +- 3 files changed, 383 insertions(+), 254 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index dc84fa22400..bdccf709553 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -387,230 +387,6 @@ def augmentation_chain(self): """ return [self] + self._base_valuation.augmentation_chain() - def lift(self, F): - """ - Return a polynomial whose :meth:`reduction` is ``F``. - - INPUT: - - - ``F`` -- an element of the :meth:`residue_ring` - - OUTPUT: - - a polynomial in the domain of the valuation with reduction ``F``, monic - if ``F`` is monic - - ALGORITHM: - - Since this is the inverse of :meth:`reduce`, we only have to go backwards - through the algorithm described there. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,10) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: y = v.residue_ring().gen() - sage: u0 = v.residue_ring().base().gen() - sage: v.lift(y) - (1 + O(2^10))*x - - sage: w = v.augmentation(x^2 + x + u, 1/2) - sage: r = w.residue_ring() - sage: y = r.gen() - sage: u1 = w.residue_ring().base().gen() - sage: w.lift(r.one()) - 1 + O(2^10) - sage: w.lift(r.zero()) - 0 - sage: w.lift(r(u0)) - u + O(2^10) - sage: w.lift(r(u1)) - (1 + O(2^10))*x - sage: w.reduce(w.lift(y)) == y - True - sage: w.reduce(w.lift(y + u1 + 1)) == y + u1 + 1 - True - - sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) - sage: r = w.residue_ring() - sage: y = r.gen() - sage: u2 = w.residue_ring().base().gen() - sage: w.reduce(w.lift(y)) == y - True - sage: w.reduce(w.lift(r.one())) == 1 - True - sage: w.reduce(w.lift(y + 1)) == y + 1 - True - - A more complicated example:: - - sage: v = GaussValuation(S) - sage: v = v.augmentation(x^2 + x + u, 1) - sage: v = v.augmentation((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) - - sage: u = v.residue_ring().base().gen() - sage: F = v.residue_ring()(u); F - u2 - sage: f = v.lift(F); f - (2^-1 + O(2^9))*x^2 + (2^-1 + O(2^9))*x + u*2^-1 + O(2^9) - sage: F == v.reduce(f) - True - - """ - F = self.residue_ring().coerce(F) - - from sage.categories.fields import Fields - if not self.domain().base_ring() in Fields(): - raise NotImplementedError("only implemented for polynomial rings over fields") - if self._mu == infinity: - if self.psi().degree() == 1: - return self._base_valuation.lift(F) - else: - return self._base_valuation.lift(F.polynomial(self._base_valuation.residue_ring().variable_name())) - - if F.is_constant(): - if F.is_zero(): - return self.domain().zero() - if F.is_one(): - return self.domain().one() - - if self._base_valuation.is_trivial(): - if not self._mu > 0: - raise NotImplementedError - if not F.is_constant(): - raise ValueError("any reduction is constant in this valuation") - F = F[0] - if self.phi() == self.domain().gen(): - # this is a valuation of the form [p-adic valuation, v(x) = 1] - constant = self.restriction(self.domain().base_ring()).lift(F) - assert constant in self.domain().base_ring() - return self.domain()(constant) - else: - if self.phi().degree() == 1: - # this is a classical valuation of a rational point, of the - # form [trivial, v(x + 1) = 1] - assert self.domain().base_ring() is self.residue_ring().base() - return self.domain()(F) - if self.phi().change_variable_name(self.residue_ring().base().polynomial().variable_name()) == self.residue_ring().base().polynomial(): - # this is a classical valuation of a point, of the from - # [trivial, v(x^2 + 1) = 1] - if hasattr(F, 'polynomial'): - u = F.polynomial() - if hasattr(F, 'element'): - u = F.element() - return self.domain()(u.change_variable_name(self.phi().variable_name())) - raise NotImplementedError - - R0 = self._base_valuation.residue_ring() - - # in the last step of reduce, the f_iQ^i are reduced, and evaluated at - # the generator of the residue field - # here, we undo this: - coeffs = [ R0(c if self.psi().degree()==1 else list(c._vector_() if hasattr(c, '_vector_') else c.list())) for c in F.coefficients(sparse=False) ] - coeffs = [ self._base_valuation.lift(c) for c in coeffs ] - # now the coefficients correspond to the expansion with (f_iQ^i)(Q^{-1} phi)^i - - # now we undo the factors of Q^i (the if else is necessary to handle the case when mu is infinity, i.e., when _Q_reciprocal() is undefined) - coeffs = [ (c if i == 0 else c*self._Q_reciprocal()**i).map_coefficients(lambda d:_lift_to_maximal_precision(d)) for i,c in enumerate(coeffs) ] - - RR = self.domain().change_ring(self.domain()) - - if self._mu is infinity: - assert len(coeffs) <= 1 - ret = RR(coeffs)[0] - else: - tau = self.value_group().index(self._base_valuation.value_group()) - ret = RR(coeffs)(self.phi()**tau) - ret = ret.map_coefficients(lambda c:_lift_to_maximal_precision(c)) - return ret - - def lift_to_key(self, F): - """ - Lift the irreducible polynomial ``F`` to a key polynomial. - - INPUT: - - - ``F`` -- an irreducible non-constant polynomial in the - :meth:`residue_ring` of this valuation - - OUTPUT: - - A polynomial `f` in the domain of this valuation which is a key - polynomial for this valuation and which, for a suitable equivalence - unit `R`, satifies that the reduction of `Rf` is ``F`` - - ALGORITHM: - - We follow the algorithm described in Theorem 13.1 [ML1936] which, after - a :meth:`lift` of ``F``, essentially shifts the valuations of all terms - in the `\phi`-adic expansion up and then kills the leading coefficient. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,10) - sage: S. = R[] - sage: v = GaussValuation(S) - - sage: w = v.augmentation(x^2 + x + u, 1/2) - sage: y = w.residue_ring().gen() - sage: f = w.lift_to_key(y + 1); f - (1 + O(2^10))*x^4 + (2 + O(2^11))*x^3 + (1 + u*2 + O(2^10))*x^2 + (u*2 + O(2^11))*x + (u + 1) + u*2 + u*2^2 + u*2^3 + u*2^4 + u*2^5 + u*2^6 + u*2^7 + u*2^8 + u*2^9 + O(2^10) - sage: w.is_key(f) - True - - A more complicated example:: - - sage: v = GaussValuation(S) - sage: v = v.augmentation(x^2 + x + u, 1) - sage: v = v.augmentation((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) - - sage: u = v.residue_ring().base().gen() - sage: y = v.residue_ring().gen() - sage: f = v.lift_to_key(y^3+y+u) - sage: f.degree() - 12 - sage: v.is_key(f) - True - - """ - if F.parent() is not self.residue_ring(): - raise ValueError("F must be an element of the residue ring of the valuation") - from sage.categories.fields import Fields - if not self.domain().base_ring() in Fields(): - raise NotImplementedError("only implemented for polynomial rings over fields") - if self._base_valuation.is_gauss_valuation() and self._mu == infinity: - raise TypeError("there are no keys over this valuation") - - if F.is_constant(): - raise ValueError("F must not be constant") - if not F.is_monic(): - raise ValueError("F must be monic") - if not F.is_irreducible(): - raise ValueError("F must be irreducible") - if F == F.parent().gen(): - return self.phi() - - f = self.lift(F) - assert self(f) == 0 - assert self.reduce(f) == F - - f *= self._Q()**F.degree() - CV = zip(self.coefficients(f), self.valuations(f)) - vf = self(f) - CV = [(c,v) if v==vf else (c.parent().zero(),infinity) for c,v in CV] - while CV[-1][1] is infinity: - CV.pop() - - CV[-1] = (CV[-1][0].parent().one(), vf) - ret = self.domain().change_ring(self.domain())([c for c,v in CV])(self.phi()) - ret = ret.map_coefficients(lambda c:_lift_to_maximal_precision(c)) - assert (ret == self.phi()) == (F == F.parent().gen()) - assert self.is_key(ret) - return ret - @cached_method def psi(self): """ @@ -658,14 +434,14 @@ def E(self): 2 """ - if self._base_valuation.is_trivial(): - raise ValueError("there is no ramification over a trivial valuation") + if self.augmentation_chain()[-1].is_trivial(): + raise NotImplementedError("ramification index is not defined over a trivial Gauss valuation") return self.value_group().index(self._base_valuation.value_group()) * self._base_valuation.E() def F(self): """ Return the degree of the residue field extension of this valuation - over the Gauss valuation. + over the underlying Gauss valuation. EXAMPLES:: @@ -681,52 +457,145 @@ def F(self): 1 """ - if self._base_valuation.is_trivial(): - raise ValueError("there is no residual degree over a trivial valuation") return self.psi().degree() * self._base_valuation.F() def extensions(self, ring): + r""" + Return the extensions of this valuation to ``ring``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, 1) + sage: w.extensions(GaussianIntegers().fraction_field()['x']) + [[ Gauss valuation induced by 2-adic valuation, v(x^2 + x + 1) = 1 ]] + + """ if ring is self.domain(): return [self] from sage.rings.polynomial.polynomial_ring import is_PolynomialRing - if not is_PolynomialRing(ring) and len(ring.gens()) != 1: - raise NotImplementedError("Can not compute extensions to a ring that is not a univariate polynomial ring such as %r"%ring) - - base_valuations = self._base_valuation.extensions(ring) - phi = self.phi().change_ring(ring.base_ring()) + if is_PolynomialRing(ring) and ring.ngens() == 1: + base_valuations = self._base_valuation.extensions(ring) + phi = self.phi().change_ring(ring.base_ring()) + + ret = [] + for v in base_valuations: + if v.is_key(phi): + ret.append(AugmentedValuation(v, phi, self._mu)) + else: + F = v.equivalence_decomposition(phi) + mu0 = v(phi) + for f,e in F: + # We construct a valuation with [v, w(phi) = mu] which should be such that + # self(phi) = self._mu, i.e., w(phi) = w(unit) + sum e_i * w(f_i) where + # the sum runs over all the factors in the equivalence decomposition of phi + # Solving for mu gives + mu = (self._mu - v(F.unit()) - sum([ee*v(ff) for ff,ee in F if ff != f])) / e + ret.append(AugmentedValuation(v, f, mu)) + return ret - ret = [] - for v in base_valuations: - if v.is_key(phi): - ret.append(AugmentedValuation(v, phi, self._mu)) - else: - F = v.equivalence_decomposition(phi) - mu0 = v(phi) - for f,e in F: - # We construct a valuation with [v, w(phi) = mu] which should be such that - # self(phi) = self._mu, i.e., w(phi) = w(unit) + sum e_i * w(f_i) where - # the sum runs over all the factors in the equivalence decomposition of phi - # Solving for mu gives - mu = (self._mu - v(F.unit()) - sum([ee*v(ff) for ff,ee in F if ff != f])) / e - ret.append(AugmentedValuation(v, f, mu)) - return ret + return super(AugmentedValuation_base, self).extensions(ring) def restriction(self, ring): - if ring is self.domain().base_ring(): - return self._base_valuation.restriction(ring) + r""" + Return the restriction of this valuation to ``ring``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = GaussianIntegers().fraction_field() + sage: R. = K[] + sage: v = GaussValuation(R, pAdicValuation(K, 2)) + sage: w = v.augmentation(x^2 + x + 1, 1) + sage: w.restriction(QQ['x']) + [ Gauss valuation induced by 2-adic valuation, v(x^2 + x + 1) = 1 ] + + """ + if ring.is_subring(self.domain()): + base = self._base_valuation.restriction(ring) + if ring.is_subring(self.domain().base_ring()): + return base + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + if is_PolynomialRing(ring) and ring.ngens() == 1: + return base.augmentation(self.phi().change_ring(ring.base()), self._mu) return super(AugmentedValuation_base, self).restriction(ring) def uniformizer(self): + r""" + Return a uniformizing element for this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, 1) + sage: w.uniformizer() + 2 + + """ return self.element_with_valuation(self.value_group()._generator) def is_gauss_valuation(self): + r""" + Return whether this valuation is a Gauss valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, 1) + sage: w.is_gauss_valuation() + False + + """ return False def monic_integral_model(self, G): + r""" + Return a monic integral irreducible polynomial which defines the same + extension of the base ring of the domain as the irreducible polynomial + ``G``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, 1) + sage: w.monic_integral_model(5*x^2 + 1/2*x + 1/4) + x^2 + 1/5*x + 1/5 + + """ return self._base_valuation.monic_integral_model(G) def _ge_(self, other): + r""" + Return whether this valuation is greater or equal than ``other`` + everywhere. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, 1) + sage: w >= v + True + sage: ww = v.augmentation(x^2 + x + 1, 2) + sage: ww >= w + True + sage: www = w.augmentation(x^4 + 2*x^3 + 5*x^2 + 8*x + 3, 16/3) + sage: www >= w + True + sage: www >= ww + False + + """ from gauss_valuation import GaussValuation_generic if other.is_trivial(): return other.is_discrete_valuation() @@ -979,6 +848,215 @@ def _residue_field_generator(self): assert self.psi()(ret).is_zero() return ret + def lift(self, F): + """ + Return a polynomial which :meth:`reduce`s to ``F``. + + INPUT: + + - ``F`` -- an element of the :meth:`residue_ring` + + OUTPUT: + + a polynomial in the domain of the valuation with reduction ``F``, monic + if ``F`` is monic + + ALGORITHM: + + Since this is the inverse of :meth:`reduce`, we only have to go backwards + through the algorithm described there. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,10) + sage: S. = R[] + sage: v = GaussValuation(S) + + sage: w = v.augmentation(x^2 + x + u, 1/2) + sage: r = w.residue_ring() + sage: y = r.gen() + sage: u1 = w.residue_ring().base().gen() + sage: w.lift(1) + 1 + O(2^10) + sage: w.lift(0) + 0 + sage: w.lift(u1) + (1 + O(2^10))*x + sage: w.reduce(w.lift(y)) == y + True + sage: w.reduce(w.lift(y + u1 + 1)) == y + u1 + 1 + True + + sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) + sage: r = w.residue_ring() + sage: y = r.gen() + sage: u2 = w.residue_ring().base().gen() + sage: w.reduce(w.lift(y)) == y + True + sage: w.reduce(w.lift(1)) == 1 + True + sage: w.reduce(w.lift(y + 1)) == y + 1 + True + + A more complicated example:: + + sage: v = GaussValuation(S) + sage: v = v.augmentation(x^2 + x + u, 1) + sage: v = v.augmentation((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) + + sage: u = v.residue_ring().base().gen() + sage: F = v.residue_ring()(u); F + u2 + sage: f = v.lift(F); f + (2^-1 + O(2^9))*x^2 + (2^-1 + O(2^9))*x + u*2^-1 + O(2^9) + sage: F == v.reduce(f) + True + + """ + F = self.residue_ring().coerce(F) + + from sage.categories.fields import Fields + if not self.domain().base_ring() in Fields(): + raise NotImplementedError("only implemented for polynomial rings over fields") + + if F.is_constant(): + if F.is_zero(): + return self.domain().zero() + if F.is_one(): + return self.domain().one() + + if self._base_valuation.is_trivial(): + if self._mu == 0: + raise NotImplementedError + if not F.is_constant(): + raise ValueError("any reduction is constant in this valuation") + F = F[0] + if self.phi() == self.domain().gen(): + # this is a valuation of the form [p-adic valuation, v(x) = 1] + constant = self.restriction(self.domain().base_ring()).lift(F) + assert constant in self.domain().base_ring() + return self.domain()(constant) + else: + if self.phi().degree() == 1: + # this is a classical valuation of a rational point, of the + # form [trivial, v(x + 1) = 1] + assert self.domain().base_ring() is self.residue_ring().base() + return self.domain()(F) + if self.phi().change_variable_name(self.residue_ring().base().polynomial().variable_name()) == self.residue_ring().base().polynomial(): + # this is a classical valuation of a point, of the from + # [trivial, v(x^2 + 1) = 1] + if hasattr(F, 'polynomial'): + u = F.polynomial() + if hasattr(F, 'element'): + u = F.element() + return self.domain()(u.change_variable_name(self.phi().variable_name())) + raise NotImplementedError + + R0 = self._base_valuation.residue_ring() + + # in the last step of reduce, the f_iQ^i are reduced, and evaluated at + # the generator of the residue field + # here, we undo this: + coeffs = [ R0(c if self.psi().degree()==1 else list(c._vector_() if hasattr(c, '_vector_') else c.list())) for c in F.coefficients(sparse=False) ] + coeffs = [ self._base_valuation.lift(c) for c in coeffs ] + # now the coefficients correspond to the expansion with (f_iQ^i)(Q^{-1} phi)^i + + # now we undo the factors of Q^i (the if else is necessary to handle the case when mu is infinity, i.e., when _Q_reciprocal() is undefined) + coeffs = [ (c if i == 0 else c*self._Q_reciprocal()**i).map_coefficients(lambda d:_lift_to_maximal_precision(d)) for i,c in enumerate(coeffs) ] + + RR = self.domain().change_ring(self.domain()) + + tau = self.value_group().index(self._base_valuation.value_group()) + ret = RR(coeffs)(self.phi()**tau) + ret = ret.map_coefficients(lambda c:_lift_to_maximal_precision(c)) + return ret + + def lift_to_key(self, F): + """ + Lift the irreducible polynomial ``F`` to a key polynomial. + + INPUT: + + - ``F`` -- an irreducible non-constant polynomial in the + :meth:`residue_ring` of this valuation + + OUTPUT: + + A polynomial `f` in the domain of this valuation which is a key + polynomial for this valuation and which, for a suitable equivalence + unit `R`, satifies that the reduction of `Rf` is ``F`` + + ALGORITHM: + + We follow the algorithm described in Theorem 13.1 [ML1936] which, after + a :meth:`lift` of ``F``, essentially shifts the valuations of all terms + in the `\phi`-adic expansion up and then kills the leading coefficient. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,10) + sage: S. = R[] + sage: v = GaussValuation(S) + + sage: w = v.augmentation(x^2 + x + u, 1/2) + sage: y = w.residue_ring().gen() + sage: f = w.lift_to_key(y + 1); f + (1 + O(2^10))*x^4 + (2 + O(2^11))*x^3 + (1 + u*2 + O(2^10))*x^2 + (u*2 + O(2^11))*x + (u + 1) + u*2 + u*2^2 + u*2^3 + u*2^4 + u*2^5 + u*2^6 + u*2^7 + u*2^8 + u*2^9 + O(2^10) + sage: w.is_key(f) + True + + A more complicated example:: + + sage: v = GaussValuation(S) + sage: v = v.augmentation(x^2 + x + u, 1) + sage: v = v.augmentation((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) + + sage: u = v.residue_ring().base().gen() + sage: y = v.residue_ring().gen() + sage: f = v.lift_to_key(y^3+y+u) + sage: f.degree() + 12 + sage: v.is_key(f) + True + + """ + if F.parent() is not self.residue_ring(): + raise ValueError("F must be an element of the residue ring of the valuation") + from sage.categories.fields import Fields + if not self.domain().base_ring() in Fields(): + raise NotImplementedError("only implemented for polynomial rings over fields") + if self._base_valuation.is_gauss_valuation() and self._mu == infinity: + raise TypeError("there are no keys over this valuation") + + if F.is_constant(): + raise ValueError("F must not be constant") + if not F.is_monic(): + raise ValueError("F must be monic") + if not F.is_irreducible(): + raise ValueError("F must be irreducible") + if F == F.parent().gen(): + return self.phi() + + f = self.lift(F) + assert self(f) == 0 + assert self.reduce(f) == F + + f *= self._Q()**F.degree() + CV = zip(self.coefficients(f), self.valuations(f)) + vf = self(f) + CV = [(c,v) if v==vf else (c.parent().zero(),infinity) for c,v in CV] + while CV[-1][1] is infinity: + CV.pop() + + CV[-1] = (CV[-1][0].parent().one(), vf) + ret = self.domain().change_ring(self.domain())([c for c,v in CV])(self.phi()) + ret = ret.map_coefficients(lambda c:_lift_to_maximal_precision(c)) + assert (ret == self.phi()) == (F == F.parent().gen()) + assert self.is_key(ret) + return ret + class InfiniteAugmentedValuation(AugmentedValuation_base, InfiniteInductiveValuation): @cached_method @@ -1154,3 +1232,50 @@ def _residue_field_generator(self): assert ret.parent() is self.residue_ring() assert self.psi()(ret).is_zero() return ret + + def lift(self, F): + """ + Return a polynomial which :meth:`reduce`s to ``F``. + + INPUT: + + - ``F`` -- an element of the :meth:`residue_ring` + + OUTPUT: + + a polynomial in the domain of the valuation with reduction ``F``, monic + if ``F`` is monic + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,10) + sage: S. = R[] + sage: v = GaussValuation(S) + + sage: w = v.augmentation(x^2 + x + u, infinity) + sage: r = w.residue_ring() + sage: u1 = w.residue_ring().gen() + sage: w.lift(1) + 1 + O(2^10) + sage: w.lift(0) + 0 + sage: w.lift(u1) + (1 + O(2^10))*x + + """ + F = self.residue_ring().coerce(F) + + if self.residue_ring() == self._base_valuation.residue_ring(): + return self._base_valuation.lift(F) + else: + if F not in self._base_valuation.residue_ring(): + if hasattr(F, 'polynomial'): + F = F.polynomial(self._base_valuation.residue_ring().variable_name()) + elif hasattr(F, 'element'): + F = F.element(self._base_valuation.residue_ring().variable_name()) + elif hasattr(F, 'lift'): + F = F.lift().change_variable_name(self._base_valuation.residue_ring().variable_name()) + else: + raise NotImplementedError + return self._base_valuation.lift(F) diff --git a/gauss_valuation.py b/gauss_valuation.py index cd7ea7a7fda..252154a5600 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -566,8 +566,12 @@ def restriction(self, ring): 2-adic valuation """ - if ring is self.domain().base_ring(): - return self._base_valuation + if ring.is_subring(self.domain().base_ring()): + return self._base_valuation.restriction(ring) + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + if is_PolynomialRing(ring) and ring.ngens() == 1: + if ring.base().is_subring(self.domain().base()): + return GaussValuation(ring, self._base_valuation.restriction(ring.base())) return super(GaussValuation_generic, self).restriction(ring) def is_gauss_valuation(self): diff --git a/padic_valuation.py b/padic_valuation.py index 02f74d52de7..37781e3388e 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -264,8 +264,8 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime) # which we denote in the following as L = K[x]/(G), do all computations # there and then come back the original ring L = R.fraction_field() - G = L.relative_polynomial() K = L.base_ring() + G = L.relative_polynomial().change_ring(K) # Lift v to a pseudo-valuation on K[x] # First, we lift valuations defined on subrings of K to valuations on K[x] From 31814d2170f400afccf653f5b03554689ad64a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 7 Nov 2016 03:14:51 -0500 Subject: [PATCH 082/740] Fully doctest augmented_valuation --- TODO | 2 ++ augmented_valuation.py | 67 +++++++++++++++++++++++++++--------------- 2 files changed, 46 insertions(+), 23 deletions(-) diff --git a/TODO b/TODO index e9bf4cccebb..80998d7118d 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,5 @@ * Eliminate isinstance calls from code (usually by moving things like _repr_ or _ge_ up in the hierarchy) * Replace almost all doctests with the ring case. If it works over rings, then it will work over fields. + +* Run TestSuite for all valuations created in doctests diff --git a/augmented_valuation.py b/augmented_valuation.py index bdccf709553..78c3fb9e6a5 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -609,29 +609,6 @@ def _ge_(self, other): return super(AugmentedValuation_base, self)._ge_(other) - @cached_method - def _Q(self): - r""" - Return the polynomial `Q` used in the construction to :meth:`reduce` an - element to the :meth:`residue_ring`. - """ - if self._mu == infinity: - raise NotImplementedError("Q is not defined for infinite valuations") - - tau = self.value_group().index(self._base_valuation.value_group()) - return self.equivalence_unit(self._mu * tau) - - @cached_method - def _Q_reciprocal(self): - ret = self.equivalence_reciprocal(self._Q()) - - assert self.is_equivalence_unit(ret) - # esentially this checks that the reduction of Q'*phi^tau is the - # generator of the residue field - assert self._base_valuation.reduce(self._Q()*ret)(self._residue_field_generator()).is_one() - - return ret - class FiniteAugmentedValuation(AugmentedValuation_base, FiniteInductiveValuation): @cached_method @@ -1057,6 +1034,50 @@ def lift_to_key(self, F): assert self.is_key(ret) return ret + @cached_method + def _Q(self): + r""" + Return the polynomial `Q` used in the construction to :meth:`reduce` an + element to the :meth:`residue_ring`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, 1) + sage: w._Q() + 2 + + """ + tau = self.value_group().index(self._base_valuation.value_group()) + return self.equivalence_unit(self._mu * tau) + + @cached_method + def _Q_reciprocal(self): + r""" + Return the :meth:`equivalence_reciprocal` of :meth:`_Q`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, 1) + sage: w._Q_reciprocal() + 1/2 + + """ + ret = self.equivalence_reciprocal(self._Q()) + + assert self.is_equivalence_unit(ret) + # esentially this checks that the reduction of Q'*phi^tau is the + # generator of the residue field + assert self._base_valuation.reduce(self._Q()*ret)(self._residue_field_generator()).is_one() + + return ret + + class InfiniteAugmentedValuation(AugmentedValuation_base, InfiniteInductiveValuation): @cached_method From 20fddf7d750c3d0d26a63acbbbddc4e7c285ec4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 8 Nov 2016 02:35:44 -0500 Subject: [PATCH 083/740] Run TestSuites for many augmented valuations --- augmented_valuation.py | 104 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 78c3fb9e6a5..11e300e1252 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -2,7 +2,109 @@ r""" Augmented valuations on polynomial rings -Implements inductive valuations as defined in [ML1936]. +Implements augmentations of valutions as defined in [ML1936]. + +TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x, 1) + sage: TestSuite(w).run() # long time + + sage: w = v.augmentation(x, 2) + sage: TestSuite(w).run() # long time + +Run the test suite for a valuation with a residual extension:: + + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, 1) + sage: TestSuite(w).run() # long time + +Run the test suite for an iterated residual extension starting from a +non-prime residue field:: + + sage: R. = Qq(4, 40) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, 1/2) + sage: TestSuite(w).run() # long time + + sage: ww = w.augmentation(x^8 + 4*x^7 + 2*x^6 + 2*x^5 + x^4 + 2*x^3 + 4*(u + 1)*x^2 + 6*(u + 1)*x + 4 + 3*u, 10) + sage: TestSuite(ww).run() # long time + +Run the test suite for an augmentation of a ramified augmentation:: + + sage: R. = Qq(4, 5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x, 3/4) + sage: TestSuite(w).run() # long time + + sage: ww = w.augmentation(x^4 + 8, 5) + sage: TestSuite(ww).run() # long time + + +Run the test suite for a ramified augmentation of an unramified augmentation:: + + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, 1) + sage: TestSuite(w).run() # long time + + sage: ww = w.augmentation(x^4 + 2*x^3 + 5*x^2 + 8*x + 3, 16/3) + sage: TestSuite(ww).run() # long time + +Run the test suite for a ramified augmentation of a ramified augmentation:: + + sage: R. = Qq(4, 20) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, 1/2) + sage: TestSuite(w).run() # long time + + sage: ww = w.augmentation((x^2 + x + u)^2 + 2, 5/3) + sage: TestSuite(ww).run() # long time + +Run the test suite for another augmentation with iterated residue field extensions:: + + sage: R. = Qq(4, 10) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, 1) + sage: TestSuite(w).run() # long time + + sage: ww = w.augmentation((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) + sage: TestSuite(ww).run() # long time + +Run the test suite for a rather trivial pseudo-valuation:: + + sage: R. = Qq(4, 5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x, infinity) + sage: TestSuite(w).run() + +Run the test suite for an infinite valuation which extends the residue field:: + + sage: R. = Qq(4, 5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, infinity) + sage: TestSuite(w).run() # long time + +Run the test suite for an infinite valuation which extends a valuation which +extends the residue field:: + + sage: R. = Qq(4, 5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, 1/2) + sage: TestSuite(w).run() # long time + + sage: ww = w.augmentation((x^2 + x + u)^2 + 2, infinity) + sage: TestSuite(ww).run() # long time REFERENCES: From aed93ffc12a851c8e7e83b59a13352edea43b83b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 8 Nov 2016 02:36:20 -0500 Subject: [PATCH 084/740] Cleanup doctests and some minor implementation fixes --- limit_valuation.py | 2 +- value_group.py | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/limit_valuation.py b/limit_valuation.py index ee061e5da94..a7f9388b1ea 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -32,7 +32,7 @@ sage: L. = K.extension(y^2 - x) sage: v = FunctionFieldValuation(K, 1) - sage: w = v.extensions(L); w + sage: w = v.extensions(L); w # long time [[ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation, [ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation] diff --git a/value_group.py b/value_group.py index 5d7fc968aff..6d11b3626f2 100644 --- a/value_group.py +++ b/value_group.py @@ -293,26 +293,28 @@ def index(self, other): sage: DiscreteValueGroup(3).index(DiscreteValueGroup(3/8)) Traceback (most recent call last): ... - ValueError: `other` must be a subgroup of this group + ValueError: other must be a subgroup of this group sage: DiscreteValueGroup(3).index(DiscreteValueGroup(0)) - +Infinity + Traceback (most recent call last): + ... + ValueError: other must have finite index in this group sage: DiscreteValueGroup(0).index(DiscreteValueGroup(0)) 1 sage: DiscreteValueGroup(0).index(DiscreteValueGroup(3)) Traceback (most recent call last): ... - ValueError: `other` must be a subgroup of this group + ValueError: other must be a subgroup of this group """ if not isinstance(other, DiscreteValueGroup): - raise ValueError("`other` must be a DiscreteValueGroup") + raise ValueError("other must be a DiscreteValueGroup") if other._generator not in self: - raise ValueError("`other` must be a subgroup of this group") + raise ValueError("other must be a subgroup of this group") if other._generator == 0: if self._generator == 0: return ZZ(1) else: - return infinity + raise ValueError("other must have finite index in this group") return ZZ(other._generator / self._generator) def numerator(self): From 43d0d7591e9c43e449516e12c6940bc91d5011e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 8 Nov 2016 02:37:49 -0500 Subject: [PATCH 085/740] Docstring cleanups and minor implementation tweaks. Also gave shift() a well-defined meaning. --- augmented_valuation.py | 275 ++++++++++++++++++++++++++--------------- valuation_space.py | 63 ++++++++-- 2 files changed, 228 insertions(+), 110 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 11e300e1252..d1778a04c47 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -161,8 +161,8 @@ class AugmentedValuationFactory(UniqueFactory): should not rely on ``_base_valuation`` to be the valuation you started with:: - sage: w = w.augmentation(x, 2) - sage: w._base_valuation is v + sage: ww = w.augmentation(x, 2) + sage: ww._base_valuation is v True """ @@ -173,10 +173,11 @@ def create_key(self, base_valuation, phi, mu, check=True): .. NOTE:: - Uniquenesse of the resulting valuation would not be too important. - However, it makes pickling and equality checks much easier. At the - same time, going through a factory makes it easier to enforce that - all instances correctly inherit methods from the parent Hom space. + The uniqueness that this factory provides is not why we chose to + use a factory. However, it makes pickling and equality checks much + easier. At the same time, going through a factory makes it easier + to enforce that all instances correctly inherit methods from the + parent Hom space. TESTS:: @@ -195,6 +196,13 @@ def create_key(self, base_valuation, phi, mu, check=True): raise ValueError(reason) if mu <= base_valuation(phi): raise ValueError("the value of the key polynomial must strictly increase but `%s` does not exceed `%s`."%(mu, base_valuation(phi))) + if not isinstance(base_valuation, InductiveValuation): + raise TypeError("base_valuation must be inductive") + + phi = base_valuation.domain().coerce(phi) + from sage.rings.all import QQ, infinity + if mu is not infinity: + mu = QQ(mu) if isinstance(base_valuation, AugmentedValuation_base): if phi.degree() == base_valuation.phi().degree(): @@ -212,7 +220,7 @@ def create_object(self, version, key): sage: from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) - sage: w = v.augmentation(x, 1) # indirect doctest + sage: w = v.augmentation(x^2 + x + 1, 1) # indirect doctest """ base_valuation, phi, mu = key @@ -235,9 +243,9 @@ class AugmentedValuation_base(InductiveValuation): INPUT: - - ``v`` -- a discrete valuation on a polynomial ring + - ``v`` -- a :class:`InductiveValuation` on a polynomial ring - - ``phi`` -- a key polynomial over ``v`` + - ``phi`` -- a key polynomial over ``v`` (see :meth:`is_key`) - ``mu`` -- a rational number such that ``mu > v(phi)`` or ``infinity`` @@ -263,21 +271,16 @@ def __init__(self, parent, v, phi, mu): TESTS:: sage: from mac_lane import * # optional: standalone - sage: K. = Qq(4,5) + sage: K. = Qq(4, 5) sage: R. = K[] sage: v = GaussValuation(R) sage: w = AugmentedValuation(v, x, 1/2) sage: isinstance(w, AugmentedValuation_base) True - """ - from sage.rings.all import QQ, infinity - - if phi.parent() is not v.domain(): - raise ValueError("phi must be in the domain of v") - if mu is not infinity: - mu = QQ(mu) + sage: TestSuite(w).run() # long time + """ InductiveValuation.__init__(self, parent, phi) self._base_valuation = v @@ -287,24 +290,19 @@ def _call_(self, f): """ Evaluate this valuation at ``f``. - INPUT:: - - - ``f`` -- a polynomial in the domain of this valuation - EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R = Qp(2,5) + sage: R = Qp(2, 5) sage: S. = R[] sage: v = GaussValuation(S) - sage: f = x^2 + 2*x + 3 + sage: w = v.augmentation(x^2 + x + 1, 1) - sage: v = v.augmentation( x^2 + x + 1, 1) - sage: v(f) + sage: w(x^2 + 2*x + 3) 0 - sage: v(f * v.phi()^3 ) + sage: w((x^2 + 2*x + 3) * w.phi()^3 ) 3 - sage: v(S.zero()) + sage: w(0) +Infinity """ @@ -333,7 +331,8 @@ def _call_(self, f): for v in self.valuations(f): ret = min(ret, v) if ret == lower_bound: - return ret + break + return ret def equivalence_unit(self, s): @@ -346,15 +345,17 @@ def equivalence_unit(self, s): OUTPUT: - An element of the domain of this valuation + A polynomial in the domain of this valuation which + :meth:`is_equivalence_unit` for this valuation. EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,5) + sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) sage: w = v.augmentation(x^2 + x + u, 1) + sage: w.equivalence_unit(0) 1 + O(2^5) sage: w.equivalence_unit(-4) @@ -365,6 +366,7 @@ def equivalence_unit(self, s): valuation:: sage: w = v.augmentation(x, 1/2) + sage: w.equivalence_unit(3/2) Traceback (most recent call last): ... @@ -374,14 +376,11 @@ def equivalence_unit(self, s): An equivalence unit might not be integral, even if ``s >= 0``:: - sage: v = v.augmentation(x, 3/4) - sage: w = v.augmentation(x^4 + 8, 5) - sage: w.equivalence_unit(1/2) - (2^-1 + O(2^4))*x^2 - - .. SEEALSO:: + sage: w = v.augmentation(x, 3/4) + sage: ww = w.augmentation(x^4 + 8, 5) - :meth:`effective_degree`, :meth:`is_equivalence_unit` + sage: ww.equivalence_unit(1/2) + (2^-1 + O(2^4))*x^2 """ from sage.categories.fields import Fields @@ -409,7 +408,7 @@ def element_with_valuation(self, s): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,5) + sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) sage: w = v.augmentation(x^2 + x + u, 1/2) @@ -441,6 +440,65 @@ def element_with_valuation(self, s): s -= self._mu return ret * self._base_valuation.element_with_valuation(s) + def shift(self, x, s): + r""" + Return a modified version of ``x`` whose valuation is increased by ``s``. + + The element returned is such that repeated shifts which go back to + the original valuation produce the same element in reduction. + + EXAMPLES: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x, 1) + sage: w.shift(1, 1) + 2 + + Whenever there is ramification, a shift with such consistency is not + possible:: + + sage: w = v.augmentation(x, 1/2) + + sage: w.shift(1, -1/2) + Traceback (most recent call last): + ... + NotImplementedError: Shifts with consistent reduction not implemented for this augmented valuation + + Multiplication by an :meth:`element_with_valuation` might sometimes + produce useful results in such cases:: + + sage: 1 * w.element_with_valuation(-1/2) + 1/2*x + + However, this does not preserve the element in reduction:: + + sage: 1 * w.element_with_valuation(-1/2) * w.element_with_valuation(1/2) + 1/2*x^2 + + In general this is only possible by using an + :meth:`equivalence_unit` and its :meth:`equialence_reciprocal`. + These do, however, not exist for all values of ``s``. + + """ + if s not in self.value_group(): + raise ValueError("s must be in the value group of the valuation") + + if self.value_group() == self._base_valuation.value_group(): + return self._base_valuation.shift(x, s) + + if self._base_valuation.value_group().is_trivial(): + # We could implement a consistent shift in this case by multplying + # and dividing by powers of the key polynomial. Since an element of + # positive valuation has to be a power of the key polynomial, there + # can be no ambiguity here + raise NotImplementedError("Shifts with consistent reduction not implemented for augmented valuations over trivial valuations") + + # Except for very few special cases, it is not possible to implement a + # consistent shift for augmented valuations + raise NotImplementedError("Shifts with consistent reduction not implemented for this augmented valuation") + def _repr_(self): """ Return a printable representation of this valuation. @@ -448,7 +506,7 @@ def _repr_(self): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,5) + sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) sage: w = v.augmentation(x^2 + x + u, 1/2) @@ -496,25 +554,27 @@ def psi(self): OUTPUT: - a polynomial in the residue ring of the base valuation + A polynomial in the residue ring of the base valuation EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,5) + sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, 1/2) sage: w.psi() x^2 + x + u0 - sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) - sage: w.psi() + + sage: ww = w.augmentation((x^2 + x + u)^2 + 2, 5/3) + sage: ww.psi() x + 1 """ R = self._base_valuation.equivalence_unit(-self._base_valuation(self._phi)) F = self._base_valuation.reduce(self._phi*R) - assert F.is_irreducible() + assert(F.is_irreducible()) return F def E(self): @@ -525,12 +585,14 @@ def E(self): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,5) + sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, 1) sage: w.E() 1 + sage: w = v.augmentation(x, 1/2) sage: w.E() 2 @@ -548,12 +610,14 @@ def F(self): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,5) + sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, 1) sage: w.F() 2 + sage: w = v.augmentation(x, 1/2) sage: w.F() 1 @@ -571,6 +635,7 @@ def extensions(self, ring): sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) + sage: w.extensions(GaussianIntegers().fraction_field()['x']) [[ Gauss valuation induced by 2-adic valuation, v(x^2 + x + 1) = 1 ]] @@ -612,6 +677,7 @@ def restriction(self, ring): sage: R. = K[] sage: v = GaussValuation(R, pAdicValuation(K, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) + sage: w.restriction(QQ['x']) [ Gauss valuation induced by 2-adic valuation, v(x^2 + x + 1) = 1 ] @@ -635,6 +701,7 @@ def uniformizer(self): sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) + sage: w.uniformizer() 2 @@ -651,10 +718,12 @@ def is_gauss_valuation(self): sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) + sage: w.is_gauss_valuation() False """ + assert(self._mu > 0) return False def monic_integral_model(self, G): @@ -669,6 +738,7 @@ def monic_integral_model(self, G): sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) + sage: w.monic_integral_model(5*x^2 + 1/2*x + 1/4) x^2 + 1/5*x + 1/5 @@ -721,14 +791,16 @@ def value_group(self): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,5) + sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, 1/2) sage: w.value_group() Additive Abelian Group generated by 1/2 - sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) - sage: w.value_group() + + sage: ww = w.augmentation((x^2 + x + u)^2 + 2, 5/3) + sage: ww.value_group() Additive Abelian Group generated by 1/6 """ @@ -752,14 +824,16 @@ def valuations(self, f): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,5) + sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, 1/2) sage: list(w.valuations( x^2 + 1 )) [0, 1/2] - sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) - sage: list(w.valuations( ((x^2 + x + u)^2 + 2)^3 )) + + sage: ww = w.augmentation((x^2 + x + u)^2 + 2, 5/3) + sage: list(ww.valuations( ((x^2 + x + u)^2 + 2)^3 )) [+Infinity, +Infinity, +Infinity, 5] """ @@ -779,6 +853,7 @@ def residue_ring(self): sage: from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, 1) sage: w.residue_ring() Univariate Polynomial Ring in x over Finite Field in u1 of size 2^2 @@ -796,7 +871,7 @@ def residue_ring(self): base = self._base_valuation.residue_ring().base() if self.psi().degree() > 1: - # Do not call extension if self.psi().degree() == 1: + # Do not call extension() if self.psi().degree() == 1: # In that case the resulting field appears to be the same as the original field, # however, it is not == to the original field (for finite fields at # least) but a distinct copy (this is a bug in finite field's @@ -831,7 +906,7 @@ def reduce(self, f): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,10) + sage: R. = Qq(4, 10) sage: S. = R[] sage: v = GaussValuation(S) sage: v.reduce(x) @@ -854,17 +929,17 @@ def reduce(self, f): sage: w.reduce(f + x + 1) x + u1 + 1 - sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) + sage: ww = w.augmentation((x^2 + x + u)^2 + 2, 5/3) sage: g = ((x^2 + x + u)^2 + 2)^3 / 2^5 - sage: w.reduce(g) + sage: ww.reduce(g) x - sage: w.reduce(f) + sage: ww.reduce(f) 1 - sage: w(f-1) > 0 # checking the above + sage: ww.is_equivalent(f, 1) True - sage: w.reduce(f * g) + sage: ww.reduce(f * g) x - sage: w.reduce(f + g) + sage: ww.reduce(f + g) x + 1 """ @@ -909,7 +984,7 @@ def _residue_field_generator(self): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,10) + sage: R. = Qq(4, 10) sage: S. = R[] sage: v = GaussValuation(S) sage: w = v.augmentation(x^2 + x + u, 1/2) @@ -937,8 +1012,8 @@ def lift(self, F): OUTPUT: - a polynomial in the domain of the valuation with reduction ``F``, monic - if ``F`` is monic + A polynomial in the domain of the valuation with reduction ``F``, monic + if ``F`` is monic. ALGORITHM: @@ -948,14 +1023,14 @@ def lift(self, F): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,10) + sage: R. = Qq(4, 10) sage: S. = R[] sage: v = GaussValuation(S) sage: w = v.augmentation(x^2 + x + u, 1/2) - sage: r = w.residue_ring() - sage: y = r.gen() + sage: y = w.residue_ring().gen() sage: u1 = w.residue_ring().base().gen() + sage: w.lift(1) 1 + O(2^10) sage: w.lift(0) @@ -967,29 +1042,29 @@ def lift(self, F): sage: w.reduce(w.lift(y + u1 + 1)) == y + u1 + 1 True - sage: w = w.augmentation((x^2 + x + u)^2 + 2, 5/3) - sage: r = w.residue_ring() - sage: y = r.gen() - sage: u2 = w.residue_ring().base().gen() - sage: w.reduce(w.lift(y)) == y + sage: ww = w.augmentation((x^2 + x + u)^2 + 2, 5/3) + sage: y = ww.residue_ring().gen() + sage: u2 = ww.residue_ring().base().gen() + + sage: ww.reduce(ww.lift(y)) == y True - sage: w.reduce(w.lift(1)) == 1 + sage: ww.reduce(ww.lift(1)) == 1 True - sage: w.reduce(w.lift(y + 1)) == y + 1 + sage: ww.reduce(ww.lift(y + 1)) == y + 1 True A more complicated example:: sage: v = GaussValuation(S) - sage: v = v.augmentation(x^2 + x + u, 1) - sage: v = v.augmentation((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) + sage: w = v.augmentation(x^2 + x + u, 1) + sage: ww = w.augmentation((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) + sage: u = ww.residue_ring().base().gen() - sage: u = v.residue_ring().base().gen() - sage: F = v.residue_ring()(u); F + sage: F = ww.residue_ring()(u); F u2 - sage: f = v.lift(F); f + sage: f = ww.lift(F); f (2^-1 + O(2^9))*x^2 + (2^-1 + O(2^9))*x + u*2^-1 + O(2^9) - sage: F == v.reduce(f) + sage: F == ww.reduce(f) True """ @@ -1075,7 +1150,7 @@ def lift_to_key(self, F): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,10) + sage: R. = Qq(4, 10) sage: S. = R[] sage: v = GaussValuation(S) @@ -1089,15 +1164,15 @@ def lift_to_key(self, F): A more complicated example:: sage: v = GaussValuation(S) - sage: v = v.augmentation(x^2 + x + u, 1) - sage: v = v.augmentation((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) + sage: w = v.augmentation(x^2 + x + u, 1) + sage: ww = w.augmentation((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) - sage: u = v.residue_ring().base().gen() - sage: y = v.residue_ring().gen() - sage: f = v.lift_to_key(y^3+y+u) + sage: u = ww.residue_ring().base().gen() + sage: y = ww.residue_ring().gen() + sage: f = ww.lift_to_key(y^3+y+u) sage: f.degree() 12 - sage: v.is_key(f) + sage: ww.is_key(f) True """ @@ -1148,6 +1223,7 @@ def _Q(self): sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) + sage: w._Q() 2 @@ -1166,6 +1242,7 @@ def _Q_reciprocal(self): sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) + sage: w._Q_reciprocal() 1/2 @@ -1190,7 +1267,7 @@ def value_group(self): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,5) + sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) sage: w = v.augmentation(x, infinity) @@ -1216,11 +1293,11 @@ def valuations(self, f): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,5) + sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) sage: w = v.augmentation(x, infinity) - sage: list(w.valuations( x^2 + 1 )) + sage: list(w.valuations(x^2 + 1)) [0, +Infinity, +Infinity] """ @@ -1280,10 +1357,11 @@ def reduce(self, f): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,10) + sage: R. = Qq(4, 10) sage: S. = R[] sage: v = GaussValuation(S) sage: w = v.augmentation(x^2 + x + u, infinity) + sage: w.reduce(S.one()) 1 sage: w.reduce(S(2)) @@ -1299,17 +1377,18 @@ def reduce(self, f): u1 + 1 sage: w = v.augmentation(x^2 + x + u, 1/2) - sage: w = w.augmentation((x^2 + x + u)^2 + 2, infinity) + sage: ww = w.augmentation((x^2 + x + u)^2 + 2, infinity) + sage: g = ((x^2 + x + u)^2 + 2)^3 / 2^5 - sage: w.reduce(g) + sage: ww.reduce(g) 0 - sage: w.reduce(f) + sage: ww.reduce(f) 1 - sage: w(f-1) > 0 # 1 is really the reduction of f + sage: ww.is_equivalent(f, 1) True - sage: w.reduce(f * g) + sage: ww.reduce(f * g) 0 - sage: w.reduce(f + g) + sage: ww.reduce(f + g) 1 REFERENCES: @@ -1338,7 +1417,7 @@ def _residue_field_generator(self): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,10) + sage: R. = Qq(4, 10) sage: S. = R[] sage: v = GaussValuation(S) sage: w = v.augmentation(x^2 + x + u, infinity) @@ -1372,7 +1451,7 @@ def lift(self, F): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4,10) + sage: R. = Qq(4, 10) sage: S. = R[] sage: v = GaussValuation(S) diff --git a/valuation_space.py b/valuation_space.py index 0e79fde888b..bc09c47ade7 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -367,26 +367,61 @@ def value_group(self, **options): def shift(self, x, s): r""" - Return a modified version of ``x`` whose valuation is increased by - ``s``. + Return a modified version of ``x`` whose valuation is increased by ``s``. - The element returned has essentially the same reduction, i.e., if - ``x`` has valuation `v`, then the reduction of ``x`` in the residue - ring of elements of valuation `\ge v` module elements of valuation - `> v` is naturally the same as the reduction of ``shift(x, s)`` in - the correspoding residue ring of elements of valuation `\ge v + s`. + The element returned is such that repeated shifts which go back to + the original valuation produce the same element in reduction. - EXAMPLES:: + EXAMPLES: + + Negative shifts are not always possible:: sage: from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) - sage: v.shift(1, 10) - 1024 - sage: v.shift(1, -10) + sage: v.shift(2, -1) + 1 + sage: v.shift(2, -2) Traceback (most recent call last): ... TypeError: no conversion of this rational to integer + The element is the same after several shifts that produce an + element of the original valuation:: + + sage: v.shift(v.shift(1, 10), -10) + 1 + + However, this is not always possible unless we are over a field:: + + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x, 1/2) + + sage: w.shift(1, -1/2) + Traceback (most recent call last): + ... + NotImplementedError: Shifts with consistent reduction not implemented for this augmented valuation + + Of course, we could return ``x/2`` here, but what would + ``v.shift(1, -1)`` return? ``x^2/4`` or rather ``1/2``? + There is no way to make this consistent in general unless we go to + the fraction field of ``R``. + + Multiplication by an :meth:`element_with_valuation` might sometimes + produce useful results in such cases:: + + sage: 1 * w.element_with_valuation(-1/2) + 1/2*x + + However, this does not preserve the element in reduction:: + + sage: 1 * w.element_with_valuation(-1/2) * w.element_with_valuation(1/2) + 1/2*x^2 + + In general this is only possible by using an + :meth:`equivalence_unit` and its :meth:`equialence_reciprocal`. + These do, however, not exist for all values of ``s``. + """ x = self.domain().coerce(x) from sage.rings.all import QQ, ZZ @@ -712,7 +747,11 @@ def _test_shift(self, **options): if n < 0 and self.domain() not in Fields(): # note that shifting might not be possible in this case even if -n > v continue - y = self.shift(x, n) + try: + y = self.shift(x, n) + except NotImplementedError: + # not all valuations can implement consistent shifts + continue tester.assertIs(y.parent(), self.domain()) tester.assertEqual(self(y), v + n) # shifts preserve reductions From 362bfe0aa138f33fb3a197c58952cd4f677acfc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 8 Nov 2016 14:36:00 -0500 Subject: [PATCH 086/740] All isinstance calls that could go away are gone --- TODO | 2 -- 1 file changed, 2 deletions(-) diff --git a/TODO b/TODO index 80998d7118d..9a5700f3f92 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,3 @@ -* Eliminate isinstance calls from code (usually by moving things like _repr_ or _ge_ up in the hierarchy) - * Replace almost all doctests with the ring case. If it works over rings, then it will work over fields. * Run TestSuite for all valuations created in doctests From 55b1beb799aa53a648f1b3c53560b0404732d3b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 8 Nov 2016 19:04:20 -0500 Subject: [PATCH 087/740] implement non-trivial some_elements() --- __init__.py | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/__init__.py b/__init__.py index a67a259127e..f83b071b607 100644 --- a/__init__.py +++ b/__init__.py @@ -438,6 +438,87 @@ def vector_space(self): sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRing_generic.vector_space = vector_space del(vector_space) +# make some_elements() non-trivial for number fields +def some_elements(self): + r""" + TESTS:: + + sage: K = GaussianIntegers().fraction_field() + sage: list(K.some_elements()) + [I, 0, 1, 1/2, 2*I, -I, -2, 0, 0] + + """ + for element in self.polynomial_ring().some_elements(): + yield element(self.gen()) +sage.rings.number_field.number_field.NumberField_generic.some_elements = some_elements +del(some_elements) + +# make some_elements() deterministic for function fields +def some_elements(self): + r""" + TESTS:: + + sage: K. = FunctionField(QQ) + sage: list(K.some_elements()) == list(K.some_elements()) + True + + """ + for num in self._ring.some_elements(): + for den in self._ring.some_elements(): + if den != 0: + yield self(num) / self(den) +sage.rings.function_field.function_field.RationalFunctionField.some_elements = some_elements +del(some_elements) + +def some_elements(self): + r""" + TESTS:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: list(L.some_elements()) == list(L.some_elements()) + True + + """ + for element in self._ring.some_elements(): + yield self(element) +sage.rings.function_field.function_field.FunctionField_polymod.some_elements = some_elements +del(some_elements) + +# make some_elements() non-trivial for fraction fields +def some_elements(self): + r""" + TESTS:: + + sage: R. = QQ[] + sage: K = R.fraction_field() + sage: len(list(K.some_elements())) + 72 + + """ + for num in self.ring().some_elements(): + for den in self.ring().some_elements(): + if den != 0: + yield self(num) / self(den) +sage.rings.fraction_field.FractionField_generic.some_elements = some_elements + +# make some_elements() non-trivial for orders in number fields +def some_elements(self): + r""" + TESTS:: + + sage: R = GaussianIntegers() + sage: list(R.some_elements()) + [I, 0, 1, 2*I, -I, -2, 0, 0] + + """ + for element in self.fraction_field().some_elements(): + if element in self: + yield self(element) +sage.rings.number_field.order.Order.some_elements = some_elements +del(some_elements) + # register modules at some standard places so imports work as exepcted r""" sage: from sage.rings.valuation.gauss_valuation import GaussValuation From d78f767350f229de853f3efed8c5bfa6981a411d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 8 Nov 2016 19:04:37 -0500 Subject: [PATCH 088/740] run test suite for valuations on function fields --- function_field_valuation.py | 73 ++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/function_field_valuation.py b/function_field_valuation.py index 157c8710a24..090bd64f15d 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -31,10 +31,81 @@ (x - 1)-adic valuation sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v.extensions(L) # long time + sage: w = v.extensions(L); w # long time [[ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation, [ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation] +TESTS: + +Run test suite for classical places over rational function fields:: + + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, 1) + sage: TestSuite(v).run() # long time + + sage: v = FunctionFieldValuation(K, x^2 + 1) + sage: TestSuite(v).run() # long time + + sage: v = FunctionFieldValuation(K, 1/x) + sage: TestSuite(v).run() # long time + +Run test suite over classical places of finite extensions:: + + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, x - 1) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: ws = v.extensions(L) # long time + sage: for w in ws: TestSuite(w).run() # long time + +Run test suite for valuations that do not correspond to a classical place:: + + sage: K. = FunctionField(QQ) + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = FunctionFieldValuation(K, v) + sage: TestSuite(w).run() # long time + +Run test suite for some other classical places over large ground fields:: + + sage: K. = FunctionField(GF(3)) + sage: M. = FunctionField(K) + sage: v = FunctionFieldValuation(M, x^3 - t) + sage: TestSuite(v).run() # long time + +Run test suite for extensions over the infinite place:: + + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, 1/x) + sage: R. = K[] + sage: L. = K.extension(y^2 - 1/(x^2 + 1)) + sage: w = v.extensions(L) + sage: TestSuite(w).run() + +Run test suite for extensions which come from the splitting in the base field:: + + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, x^2 + 1) + sage: L. = FunctionField(GaussianIntegers().fraction_field()) + sage: ws = v.extensions(L) + sage: for w in ws: TestSuite(w).run() # long time + +Run test suite for a finite place with residual degree and ramification:: + + sage: K. = FunctionField(GF(3)) + sage: L. = FunctionField(K) + sage: v = FunctionFieldValuation(L, x^6 - t) + sage: TestSuite(v).run() # long time + +Run test suite for a valuation which is backed by limit valuation:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - (x^2 + x + 1)) + sage: v = FunctionFieldValuation(K, x - 1) + sage: w = v.extension(L) + sage: TestSuite(w).run() # long time + AUTHORS: - Julian Rüth (2016-10-16): initial version From 03f6a659875a2b2aafd84873b3c247a5030748d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 8 Nov 2016 19:05:05 -0500 Subject: [PATCH 089/740] Not all function field valuations are finite or infinite --- function_field_valuation.py | 97 ++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 35 deletions(-) diff --git a/function_field_valuation.py b/function_field_valuation.py index 090bd64f15d..96e13bf4f4a 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -401,7 +401,11 @@ def create_object(self, version, key, **extra_args): if domain.base_field() is domain: # valuation is a base valuation on K[x] that induces a valuation on K(x) - return parent.__make_element_class__(FiniteRationalFunctionFieldValuation)(parent, valuation) + if valuation.restriction(domain.constant_base_field()).is_trivial(): + # valuation corresponds to a finite place + return parent.__make_element_class__(FiniteRationalFunctionFieldValuation)(parent, valuation) + else: + return parent.__make_element_class__(NonClassicalRationalFunctionFieldValuation)(parent, valuation) else: # valuation is a limit valuation that singles out an extension return parent.__make_element_class__(FunctionFieldFromLimitValuation)(parent, valuation, domain.polynomial(), extra_args['approximants']) @@ -601,9 +605,11 @@ def lift(self, F): """ F = self.residue_ring().coerce(F) - if F in self._base_valuation.residue_ring(): - f = self._base_valuation.lift(F) - return self.domain()(f) + if F in self._base_valuation.residue_ring().fraction_field(): + num = self._base_valuation.residue_ring()(F.numerator()) + den = self._base_valuation.residue_ring()(F.denominator()) + + return self.domain()(self._base_valuation.lift(num)) / self.domain()(self._base_valuation.lift(den)) raise NotImplementedError("lifting not implemented for this valuation") def value_group(self): @@ -712,6 +718,47 @@ def extensions(self, L): return super(InducedFunctionFieldValuation_base, self).extensions(L) + def _call_(self, f): + r""" + Evaluate this valuation at the function ``f``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, x) # indirect doctest + sage: v((x+1)/x^2) + -2 + + """ + return self._base_valuation(f.numerator()) - self._base_valuation(f.denominator()) + + def residue_ring(self): + r""" + Return the residue field of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: FunctionFieldValuation(K, x).residue_ring() + Rational Field + + sage: K. = FunctionField(QQ) + sage: v = GaussValuation(QQ['x'], pAdicValuation(QQ, 2)) + sage: w = FunctionFieldValuation(K, v) + sage: w.residue_ring() + Fraction Field of Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) + + sage: R. = QQ[] + sage: vv = v.augmentation(x, 1) + sage: w = FunctionFieldValuation(K, vv) + sage: w.residue_ring() + Fraction Field of Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) + + """ + return self._base_valuation.residue_ring().fraction_field() + class InfiniteRationalFunctionFieldValuation(RationalFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base): r""" Valuation of the infinite place of a function field. @@ -863,42 +910,22 @@ class FiniteRationalFunctionFieldValuation(ClassicalFunctionFieldValuation_base, sage: q = FunctionFieldValuation(L, x^6 - t); q (x^6 + 2*t)-adic valuation - TESTS:: - - sage: TestSuite(v).run() # long time - sage: TestSuite(w).run() # long time - sage: TestSuite(u).run() # long time - sage: TestSuite(q).run() # long time - """ - def _call_(self, f): - r""" - Evaluate this valuation at the function ``f``. - - EXAMPLES:: - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, x) # indirect doctest - sage: v((x+1)/x^2) - -2 - - """ - return self._base_valuation(f.numerator()) - self._base_valuation(f.denominator()) - - def residue_ring(self): - r""" - Return the residue field of this valuation. +class NonClassicalRationalFunctionFieldValuation(InducedFunctionFieldValuation_base, RationalFunctionFieldValuation_base): + r""" + Valuation induced by a valuation on the underlying polynomial ring which is + non-classical. - EXAMPLES:: + EXAMPLES:: - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: FunctionFieldValuation(K, 1/x).residue_ring() - Rational Field + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = GaussValuation(QQ['x'], pAdicValuation(QQ, 2)) + sage: w = FunctionFieldValuation(K, v); w # indirect doctest + 2-adic valuation - """ - return self._base_valuation.residue_ring().base() + """ class FunctionFieldFromLimitValuation(FiniteExtensionFromLimitValuation): r""" From 9ae3ced554f92efde62dbb25c08666e8758263cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 8 Nov 2016 19:05:33 -0500 Subject: [PATCH 090/740] mark test suite runs, now that they take longer --- augmented_valuation.py | 2 +- function_field_valuation.py | 2 +- padic_valuation.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index d1778a04c47..7563685e03b 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -84,7 +84,7 @@ sage: S. = R[] sage: v = GaussValuation(S) sage: w = v.augmentation(x, infinity) - sage: TestSuite(w).run() + sage: TestSuite(w).run() # long time Run the test suite for an infinite valuation which extends the residue field:: diff --git a/function_field_valuation.py b/function_field_valuation.py index 96e13bf4f4a..01fa1a30b8e 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -484,7 +484,7 @@ class RationalFunctionFieldValuation_base(FunctionFieldValuation_base): sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, RationalFunctionFieldValuation_base) True - sage: TestSuite(v).run() + sage: TestSuite(v).run() # long time """ diff --git a/padic_valuation.py b/padic_valuation.py index 37781e3388e..2c4b5bd37c2 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -976,7 +976,7 @@ class pAdicFromLimitValuation(FiniteExtensionFromLimitValuation, pAdicValuation_ TESTS:: - sage: TestSuite(v).run() + sage: TestSuite(v).run() # long time """ def __init__(self, parent, approximant, G, approximants): From 88e31d29869acb1dc29cf55155f555610ba91a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 8 Nov 2016 19:06:27 -0500 Subject: [PATCH 091/740] Separate augmented valuations depending on whether the residue ring is a polynomial ring or not --- augmented_valuation.py | 417 +++++++++++++++++++++++----------------- developing_valuation.py | 2 +- gauss_valuation.py | 8 +- inductive_valuation.py | 267 +++++++++++++------------ valuation.py | 2 +- 5 files changed, 382 insertions(+), 314 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 7563685e03b..13d3f0e093e 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -136,7 +136,7 @@ sys.path.append(os.path.dirname(os.getcwd())) from inductive_valuation import _lift_to_maximal_precision -from inductive_valuation import FiniteInductiveValuation, InfiniteInductiveValuation, InductiveValuation +from inductive_valuation import NonFinalInductiveValuation, FiniteInductiveValuation, InfiniteInductiveValuation, InductiveValuation from valuation import InfiniteDiscretePseudoValuation, DiscreteValuation from sage.misc.cachefunc import cached_method @@ -228,7 +228,10 @@ def create_object(self, version, key): from valuation_space import DiscretePseudoValuationSpace parent = DiscretePseudoValuationSpace(base_valuation.domain()) if mu < infinity: - return parent.__make_element_class__(FiniteAugmentedValuation)(parent, base_valuation, phi, mu) + if base_valuation.is_trivial(): + return parent.__make_element_class__(FiniteAugmentedValuationWithTrivialResidueRing)(parent, base_valuation, phi, mu) + else: + return parent.__make_element_class__(FiniteAugmentedValuationWithNonTrivialResidueRing)(parent, base_valuation, phi, mu) else: return parent.__make_element_class__(InfiniteAugmentedValuation)(parent, base_valuation, phi, mu) @@ -782,66 +785,214 @@ def _ge_(self, other): return super(AugmentedValuation_base, self)._ge_(other) -class FiniteAugmentedValuation(AugmentedValuation_base, FiniteInductiveValuation): +class AugmentedValuationWithTrivialResidueRing(AugmentedValuation_base, InductiveValuation): @cached_method - def value_group(self): - """ - Return the value group of this valuation. + def residue_ring(self): + r""" + Return the residue ring of this valuation, i.e., the elements of + non-negative valuation modulo the elements of positive valuation. EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4, 5) - sage: S. = R[] - sage: v = GaussValuation(S) + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) - sage: w = v.augmentation(x^2 + x + u, 1/2) - sage: w.value_group() - Additive Abelian Group generated by 1/2 + sage: w = v.augmentation(x, 1) + sage: w.residue_ring() + Rational Field - sage: ww = w.augmentation((x^2 + x + u)^2 + 2, 5/3) - sage: ww.value_group() - Additive Abelian Group generated by 1/6 + sage: w = v.augmentation(x^2 + x + 1, infinity) + sage: w.residue_ring() + Number Field in u1 with defining polynomial x^2 + x + 1 - """ - base = self._base_valuation.value_group() - from value_group import DiscreteValueGroup - return base + DiscreteValueGroup(self._mu) + An example with a non-trivial base valuation:: + + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, infinity) + sage: w.residue_ring() + Finite Field in u1 of size 2^2 + + Since trivial extensions of finite fields are not implemented, the + resulting ring might be identical to the residue ring of the underlying + valuation:: + + sage: w = v.augmentation(x, infinity) + sage: w.residue_ring() + Finite Field of size 2 - def valuations(self, f): """ - Return the valuations of the `f_i\phi^i` in the expansion `f=\sum_i - f_i\phi^i`. + generator = 'u' + str(len(self.augmentation_chain()) - 1) - INPUT: + base = self._base_valuation.residue_ring().base() + if self.psi().degree() > 1: + # Do not call extension() if self.psi().degree() == 1: + # In that case the resulting field appears to be the same as the original field, + # however, it is not == to the original field (for finite fields at + # least) but a distinct copy (this is a bug in finite field's + # extension() implementation.) + return base.extension(self.psi(), names=generator) + else: + return base - - ``f`` -- a polynomial in the domain of this valuation + def reduce(self, f): + r""" + Reduce ``f`` module this valuation. OUTPUT: - An iterator over rational numbers (or infinity) `[v(f_0), v(f_1\phi), \dots]` + an element of the :meth:`residue_ring` of this valuation, the reduction + modulo the ideal of elements of positive valuation EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4, 5) + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + + sage: w = v.augmentation(x, 1) + sage: w.reduce(x^2 + x + 1) + 1 + + sage: w = v.augmentation(x^2 + x + 1, infinity) + sage: w.reduce(x) + u1 + + TESTS: + + Cases with non-trivial base valuation:: + + sage: R. = Qq(4, 10) sage: S. = R[] sage: v = GaussValuation(S) + sage: v.reduce(x) + x + sage: v.reduce(S(u)) + u0 sage: w = v.augmentation(x^2 + x + u, 1/2) - sage: list(w.valuations( x^2 + 1 )) - [0, 1/2] + sage: w.reduce(S.one()) + 1 + sage: w.reduce(S(2)) + 0 + sage: w.reduce(S(u)) + u0 + sage: w.reduce(x) # this gives the generator of the residue field extension of w over v + u1 + sage: f = (x^2 + x + u)^2 / 2 + sage: w.reduce(f) + x + sage: w.reduce(f + x + 1) + x + u1 + 1 sage: ww = w.augmentation((x^2 + x + u)^2 + 2, 5/3) - sage: list(ww.valuations( ((x^2 + x + u)^2 + 2)^3 )) - [+Infinity, +Infinity, +Infinity, 5] + sage: g = ((x^2 + x + u)^2 + 2)^3 / 2^5 + sage: ww.reduce(g) + x + sage: ww.reduce(f) + 1 + sage: ww.is_equivalent(f, 1) + True + sage: ww.reduce(f * g) + x + sage: ww.reduce(f + g) + x + 1 """ f = self.domain().coerce(f) - for i,c in enumerate(self.coefficients(f)): - yield self._base_valuation(c) + i*self._mu + if self(f) < 0: + raise ValueError("f must have non-negative valuation") + elif self(f) > 0: + return self.residue_ring().zero() + + constant_term = self.coefficients(f).next() + constant_term_reduced = self._base_valuation.reduce(constant_term) + return constant_term_reduced(self._residue_field_generator()) + @cached_method + def _residue_field_generator(self): + r""" + Return a root of :meth:`psi` in :meth:`residue_ring`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + + sage: w = v.augmentation(x, 1) + sage: w._residue_field_generator() + 0 + + sage: w = v.augmentation(x^2 + x + 1, infinity) + sage: w._residue_field_generator() + u1 + + A case with non-trivial base valuation:: + + sage: R. = Qq(4, 10) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, infinity) + sage: w._residue_field_generator() + u1 + + """ + if self.psi().degree() == 1: + ret = self.residue_ring()(-self.psi()[0]) + else: + ret = self.residue_ring().gen() + + assert self.psi()(ret).is_zero() + return ret + + def lift(self, F): + """ + Return a polynomial which :meth:`reduce`s to ``F``. + + INPUT: + + - ``F`` -- an element of the :meth:`residue_ring` + + OUTPUT: + + A polynomial in the domain of the valuation with reduction ``F``. + + """ + F = self.residue_ring().coerce(F) + + if F.is_zero(): + return self.domain().zero() + if F.is_one(): + return self.domain().one() + + if self._base_valuation.is_trivial(): + if self._mu == 0: + raise NotImplementedError + if self.phi() == self.domain().gen(): + # this is a valuation of the form [p-adic valuation, v(x) = 1] + constant = self.restriction(self.domain().base_ring()).lift(F) + assert constant in self.domain().base_ring() + return self.domain()(constant) + else: + if self.phi().degree() == 1: + # this is a classical valuation of a rational point, of the + # form [trivial, v(x + 1) = 1] + assert self.domain().base_ring() is self.residue_ring() + return self.domain()(F) + if self.phi().change_variable_name(self.residue_ring().polynomial().variable_name()) == self.residue_ring().polynomial(): + # this is a classical valuation of a point, of the from + # [trivial, v(x^2 + 1) = 1] + if hasattr(F, 'polynomial'): + u = F.polynomial() + if hasattr(F, 'element'): + u = F.element() + return self.domain()(u.change_variable_name(self.phi().variable_name())) + raise NotImplementedError + + +class AugmentedValuationWithNonTrivialResidueRing(AugmentedValuation_base, NonFinalInductiveValuation): @cached_method def residue_ring(self): r""" @@ -950,14 +1101,6 @@ def reduce(self, f): elif self(f) > 0: return self.residue_ring().zero() - if self._base_valuation.value_group().is_trivial(): - # if this extends a trivial valuation, then this is very easy: just - # return the constant coefficient in the phi-adic expansion; everything - # else must have positive valuation - assert not self.value_group().is_trivial() - assert self.valuations(f).next() == 0 - return self.residue_ring()(self.coefficients(f).next())(self._residue_field_generator()) - CV = zip(self.coefficients(f), self.valuations(f)) # rewrite as sum of f_i phi^{i tau}, i.e., drop the coefficients that # can have no influence on the reduction @@ -1080,33 +1223,6 @@ def lift(self, F): if F.is_one(): return self.domain().one() - if self._base_valuation.is_trivial(): - if self._mu == 0: - raise NotImplementedError - if not F.is_constant(): - raise ValueError("any reduction is constant in this valuation") - F = F[0] - if self.phi() == self.domain().gen(): - # this is a valuation of the form [p-adic valuation, v(x) = 1] - constant = self.restriction(self.domain().base_ring()).lift(F) - assert constant in self.domain().base_ring() - return self.domain()(constant) - else: - if self.phi().degree() == 1: - # this is a classical valuation of a rational point, of the - # form [trivial, v(x + 1) = 1] - assert self.domain().base_ring() is self.residue_ring().base() - return self.domain()(F) - if self.phi().change_variable_name(self.residue_ring().base().polynomial().variable_name()) == self.residue_ring().base().polynomial(): - # this is a classical valuation of a point, of the from - # [trivial, v(x^2 + 1) = 1] - if hasattr(F, 'polynomial'): - u = F.polynomial() - if hasattr(F, 'element'): - u = F.element() - return self.domain()(u.change_variable_name(self.phi().variable_name())) - raise NotImplementedError - R0 = self._base_valuation.residue_ring() # in the last step of reduce, the f_iQ^i are reduced, and evaluated at @@ -1176,8 +1292,8 @@ def lift_to_key(self, F): True """ - if F.parent() is not self.residue_ring(): - raise ValueError("F must be an element of the residue ring of the valuation") + F = self.residue_ring().coerce(F) + from sage.categories.fields import Fields if not self.domain().base_ring() in Fields(): raise NotImplementedError("only implemented for polynomial rings over fields") @@ -1257,8 +1373,7 @@ def _Q_reciprocal(self): return ret - -class InfiniteAugmentedValuation(AugmentedValuation_base, InfiniteInductiveValuation): +class FiniteAugmentedValuation(AugmentedValuation_base, FiniteInductiveValuation): @cached_method def value_group(self): """ @@ -1270,12 +1385,19 @@ def value_group(self): sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) - sage: w = v.augmentation(x, infinity) + + sage: w = v.augmentation(x^2 + x + u, 1/2) sage: w.value_group() - Additive Abelian Group generated by 1 + Additive Abelian Group generated by 1/2 + + sage: ww = w.augmentation((x^2 + x + u)^2 + 2, 5/3) + sage: ww.value_group() + Additive Abelian Group generated by 1/6 """ - return self._base_valuation.value_group() + base = self._base_valuation.value_group() + from value_group import DiscreteValueGroup + return base + DiscreteValueGroup(self._mu) def valuations(self, f): """ @@ -1296,144 +1418,79 @@ def valuations(self, f): sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) - sage: w = v.augmentation(x, infinity) - sage: list(w.valuations(x^2 + 1)) - [0, +Infinity, +Infinity] + + sage: w = v.augmentation(x^2 + x + u, 1/2) + sage: list(w.valuations( x^2 + 1 )) + [0, 1/2] + + sage: ww = w.augmentation((x^2 + x + u)^2 + 2, 5/3) + sage: list(ww.valuations( ((x^2 + x + u)^2 + 2)^3 )) + [+Infinity, +Infinity, +Infinity, 5] """ f = self.domain().coerce(f) - num_infty_coefficients = f.degree() // self.phi().degree() - yield self._base_valuation(self.coefficients(f).next()) - for i in range(num_infty_coefficients): - yield infinity + for i,c in enumerate(self.coefficients(f)): + yield self._base_valuation(c) + i*self._mu - @cached_method - def residue_ring(self): - r""" - Return the residue ring of this valuation, i.e., the elements of - non-negative valuation modulo the elements of positive valuation. - EXAMPLES:: +class FiniteAugmentedValuationWithTrivialResidueRing(FiniteAugmentedValuation, AugmentedValuationWithTrivialResidueRing): + pass - sage: from mac_lane import * # optional: standalone - sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) - sage: w = v.augmentation(x^2 + x + 1, infinity) - sage: w.residue_ring() - Finite Field in u1 of size 2^2 - Since trivial valuations of finite fields are not implemented, the - resulting ring might be identical to the residue ring of the underlying - valuation:: +class FiniteAugmentedValuationWithNonTrivialResidueRing(FiniteAugmentedValuation, AugmentedValuationWithNonTrivialResidueRing): + pass - sage: w = v.augmentation(x, infinity) - sage: w.residue_ring() - Finite Field of size 2 +class InfiniteAugmentedValuation(AugmentedValuationWithTrivialResidueRing, InfiniteInductiveValuation): + @cached_method + def value_group(self): """ - generator = 'u' + str(len(self.augmentation_chain()) - 1) - - ret = self._base_valuation.residue_ring().base() - if self.psi().degree() > 1: - # Do not call extension if self.psi().degree() == 1: - # In that case the resulting field appears to be the same as the original field, - # however, it is not == to the original field (for finite fields at - # least) but a distinct copy (this is a bug in finite field's - # extension() implementation.) - return ret.extension(self.psi(), names=generator) - else: - return ret - - def reduce(self, f): - r""" - Reduce ``f`` module this valuation. - - OUTPUT: - - an element of the :meth:`residue_ring` of this valuation, the reduction - modulo the ideal of elements of positive valuation + Return the value group of this valuation. EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4, 10) + sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) - sage: w = v.augmentation(x^2 + x + u, infinity) - - sage: w.reduce(S.one()) - 1 - sage: w.reduce(S(2)) - 0 - sage: w.reduce(S(u)) - u0 - sage: w.reduce(x) # this gives the generator of the residue field extension of w over v - u1 - sage: f = (x^2 + x + u)^2 / 2 - sage: w.reduce(f) - 0 - sage: w.reduce(f + x + 1) - u1 + 1 - - sage: w = v.augmentation(x^2 + x + u, 1/2) - sage: ww = w.augmentation((x^2 + x + u)^2 + 2, infinity) - - sage: g = ((x^2 + x + u)^2 + 2)^3 / 2^5 - sage: ww.reduce(g) - 0 - sage: ww.reduce(f) - 1 - sage: ww.is_equivalent(f, 1) - True - sage: ww.reduce(f * g) - 0 - sage: ww.reduce(f + g) - 1 - - REFERENCES: + sage: w = v.augmentation(x, infinity) + sage: w.value_group() + Additive Abelian Group generated by 1 - .. [ML1936] Mac Lane, S. (1936). A construction for prime ideals as absolute - values of an algebraic field. Duke Mathematical Journal, 2(3), 492-510. + """ + return self._base_valuation.value_group() + def valuations(self, f): """ - f = self.domain().coerce(f) + Return the valuations of the `f_i\phi^i` in the expansion `f=\sum_i + f_i\phi^i`. - if self(f) < 0: - assert self(f) < 0 - raise ValueError("f must have non-negative valuation") - elif self(f) > 0: - return self.residue_ring().zero() + INPUT: - constant_term = self.coefficients(f).next() - constant_term_reduced = self._base_valuation.reduce(constant_term) - return constant_term_reduced(self._residue_field_generator()) + - ``f`` -- a polynomial in the domain of this valuation - @cached_method - def _residue_field_generator(self): - r""" - Return a root of :meth:`psi` in :meth:`residue_ring`. + OUTPUT: + + An iterator over rational numbers (or infinity) `[v(f_0), v(f_1\phi), \dots]` EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4, 10) + sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) - sage: w = v.augmentation(x^2 + x + u, infinity) - sage: w._residue_field_generator() - u1 + sage: w = v.augmentation(x, infinity) + sage: list(w.valuations(x^2 + 1)) + [0, +Infinity, +Infinity] """ - if self.residue_ring() == self._base_valuation.residue_ring().base(): - assert self.psi().degree() == 1 - ret = self.residue_ring()(-self.psi()[0]) - else: - ret = self.residue_ring().gen() + f = self.domain().coerce(f) - assert ret.parent() is self.residue_ring() - assert self.psi()(ret).is_zero() - return ret + num_infty_coefficients = f.degree() // self.phi().degree() + yield self._base_valuation(self.coefficients(f).next()) + for i in range(num_infty_coefficients): + yield infinity def lift(self, F): """ diff --git a/developing_valuation.py b/developing_valuation.py index 6fb7d68c2f1..b99e25f88e4 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -43,7 +43,7 @@ class DevelopingValuation(DiscretePseudoValuation): TESTS:: - sage: TestSuite(v).run() + sage: TestSuite(v).run() # long time """ def __init__(self, parent, phi): diff --git a/gauss_valuation.py b/gauss_valuation.py index 252154a5600..4d229ab1b99 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -49,7 +49,7 @@ sys.path.append(os.getcwd()) sys.path.append(os.path.dirname(os.getcwd())) -from inductive_valuation import FiniteInductiveValuation +from inductive_valuation import NonFinalInductiveValuation from sage.misc.cachefunc import cached_method from sage.structure.unique_representation import UniqueRepresentation @@ -134,7 +134,7 @@ def create_object(self, version, key, **extra_args): GaussValuation = GaussValuationFactory("GaussValuation") -class GaussValuation_generic(FiniteInductiveValuation): +class GaussValuation_generic(NonFinalInductiveValuation): """ A Gauss valuation on a polynomial ring ``domain``. @@ -159,7 +159,7 @@ class GaussValuation_generic(FiniteInductiveValuation): TESTS:: - sage: TestSuite(v).run() + sage: TestSuite(v).run() # long time """ def __init__(self, parent, v): @@ -174,7 +174,7 @@ def __init__(self, parent, v): True """ - FiniteInductiveValuation.__init__(self, parent, parent.domain().gen()) + NonFinalInductiveValuation.__init__(self, parent, parent.domain().gen()) self._base_valuation = v diff --git a/inductive_valuation.py b/inductive_valuation.py index e1d3bf45ac3..7bc20b1946e 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -452,6 +452,7 @@ def _test_inductive_valuation_inheritance(self, **options): tester = self._tester(**options) tester.assertTrue(isinstance(self, InfiniteInductiveValuation) != isinstance(self, FiniteInductiveValuation)) + class FiniteInductiveValuation(InductiveValuation, DiscreteValuation): r""" Abstract base class for iterated :class:`AugmentedValuation` on top of a @@ -466,9 +467,12 @@ class FiniteInductiveValuation(InductiveValuation, DiscreteValuation): TESTS:: - sage: TestSuite(v).run() + sage: TestSuite(v).run() # long time """ + + +class NonFinalInductiveValuation(FiniteInductiveValuation, DiscreteValuation): def augmentation(self, phi, mu, check=True): r""" Return the inductive valuation which extends this valuation by mapping @@ -523,6 +527,130 @@ def augmentation(self, phi, mu, check=True): from augmented_valuation import AugmentedValuation return AugmentedValuation(self, phi, mu, check) + def mac_lane_step(self, G, assume_squarefree=False): + r""" + Perform an approximation step towards the squarefree non-constant + polynomial ``G`` with this valuation. + + This performs the individual steps that are used in + :meth:`mac_lane_approximants`. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: S. = K[] + sage: F = y^2 - x^2 - x^3 - 3 + sage: v0 = GaussValuation(K._ring,pAdicValuation(QQ, 3)) + sage: v1 = v0.augmentation(K._ring.gen(), 1/3) + sage: mu0 = FunctionFieldValuation(K, v1) + sage: eta0 = GaussValuation(S, mu0) + sage: eta1 = eta0.mac_lane_step(F)[0] + sage: eta2 = eta1.mac_lane_step(F)[0] + sage: eta2 + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + x) = 2/3 ] + + """ + G = self.domain().coerce(G) + + if G.is_constant(): + raise ValueError("G must not be constant") + + from sage.misc.misc import verbose + verbose("Expanding %s towards %s"%(self, G), caller_name = "mac_lane_step") + + if not G.is_monic() or self(G) < 0: + # G must be monic, there is no fundamental reason for this, but the implementation makes this assumption in some places. + # G must be integral, otherwise, e.g., the effective degree is too low + # We try to turn G into a monic integral polynomial that describes the same extension + # This might fail if the constants of our polynomial ring do not form a field + return self.mac_lane_step(self.monic_integral_model(G), assume_squarefree=assume_squarefree) + + if not assume_squarefree and not G.is_squarefree(): + raise ValueError("G must be squarefree") + + from sage.rings.all import infinity + assert self(G) != infinity # this is a valuation and G is non-zero + + if self.is_key(G): + return [self.augmentation(G, infinity)] + + F = self.equivalence_decomposition(G) + assert len(F), "%s equivalence-decomposese as an equivalence-unit %s"%(G, F) + + ret = [] + for phi,e in F: + if G == phi: + # Something strange happened here: + # G is not a key (we checked that before) but phi==G is; so phi must have less precision than G + # this can happen if not all coefficients of G have the same precision + # if we drop some precision of G then it will be a key (but is + # that really what we should do?) + assert not G.base_ring().is_exact() + prec = min([c.precision_absolute() for c in phi.list()]) + g = G.map_coefficients(lambda c:c.add_bigoh(prec)) + assert self.is_key(g) + return [self.augmentation(g, infinity)] + + if phi == self.phi(): + # a factor phi in the equivalence decomposition means that we + # found an actual factor of G, i.e., we can set + # v(phi)=infinity + # However, this should already have happened in the last step + # (when this polynomial had -infinite slope in the Newton + # polygon.) + if self.is_gauss_valuation(): # unless in the first step + pass + else: + continue + + verbose("Determining the valuation for %s"%phi, level=2, caller_name="mac_lane_step") + w = self.augmentation(phi, self(phi), check=False) + NP = w.newton_polygon(G).principal_part() + verbose("Newton-Polygon for v(phi)=%s : %s"%(self(phi), NP), level=2, caller_name="mac_lane_step") + slopes = NP.slopes(repetition=False) + if NP.vertices()[0][0] != 0: + slopes = [-infinity] + slopes + + if not slopes: + q,r = G.quo_rem(phi) + assert not r.is_zero() + phi = phi.coefficients(sparse=False) + for i,c in enumerate(r.coefficients(sparse=False)): + if not c.is_zero(): + v = w(c) + # for a correct result we need to add O(pi^v) in degree i + # we try to find the coefficient of phi where such an + # error can be introduced without losing much absolute + # precision on phi + best = i + for j in range(i): + if w(q[j]) < w(q[best]): + best = j + # now add the right O() to phi in degree i - best + phi[i-best] = phi[i-best].add_bigoh(w(c)-w(q[best])) + + phi = G.parent()(phi) + w = self._base_valuation.augmentation(phi, infinity) + ret.append(w) + continue + + for i in range(len(slopes)): + slope = slopes[i] + verbose("Slope = %s"%slope, level=3, caller_name="mac_lane_step") + new_mu = self(phi) - slope + base = self + if phi.degree() == base.phi().degree(): + assert new_mu > self(phi) + if not base.is_gauss_valuation(): + base = base._base_valuation + new_leaf = base.augmentation(phi, new_mu) + assert slope is -infinity or 0 in new_leaf.newton_polygon(G).slopes(repetition=False) + ret.append(new_leaf) + + assert ret + return ret + def is_key(self, phi, explain=False): r""" Return whether ``phi`` is a key polynomial for this valuation, i.e., @@ -660,130 +788,6 @@ def is_minimal(self, f): list(self.valuations(f))[0] == self(f) and \ tau.divides(len(list(self.coefficients(f))) - 1) - def mac_lane_step(self, G, assume_squarefree=False): - r""" - Perform an approximation step towards the squarefree non-constant - polynomial ``G`` with this valuation. - - This performs the individual steps that are used in - :meth:`mac_lane_approximants`. - - TESTS:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: S. = K[] - sage: F = y^2 - x^2 - x^3 - 3 - sage: v0 = GaussValuation(K._ring,pAdicValuation(QQ, 3)) - sage: v1 = v0.augmentation(K._ring.gen(), 1/3) - sage: mu0 = FunctionFieldValuation(K, v1) - sage: eta0 = GaussValuation(S, mu0) - sage: eta1 = eta0.mac_lane_step(F)[0] - sage: eta2 = eta1.mac_lane_step(F)[0] - sage: eta2 - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + x) = 2/3 ] - - """ - G = self.domain().coerce(G) - - if G.is_constant(): - raise ValueError("G must not be constant") - - from sage.misc.misc import verbose - verbose("Expanding %s towards %s"%(self, G), caller_name = "mac_lane_step") - - if not G.is_monic() or self(G) < 0: - # G must be monic, there is no fundamental reason for this, but the implementation makes this assumption in some places. - # G must be integral, otherwise, e.g., the effective degree is too low - # We try to turn G into a monic integral polynomial that describes the same extension - # This might fail if the constants of our polynomial ring do not form a field - return self.mac_lane_step(self.monic_integral_model(G), assume_squarefree=assume_squarefree) - - if not assume_squarefree and not G.is_squarefree(): - raise ValueError("G must be squarefree") - - from sage.rings.all import infinity - assert self(G) != infinity # this is a valuation and G is non-zero - - if self.is_key(G): - return [self.augmentation(G, infinity)] - - F = self.equivalence_decomposition(G) - assert len(F), "%s equivalence-decomposese as an equivalence-unit %s"%(G, F) - - ret = [] - for phi,e in F: - if G == phi: - # Something strange happened here: - # G is not a key (we checked that before) but phi==G is; so phi must have less precision than G - # this can happen if not all coefficients of G have the same precision - # if we drop some precision of G then it will be a key (but is - # that really what we should do?) - assert not G.base_ring().is_exact() - prec = min([c.precision_absolute() for c in phi.list()]) - g = G.map_coefficients(lambda c:c.add_bigoh(prec)) - assert self.is_key(g) - return [self.augmentation(g, infinity)] - - if phi == self.phi(): - # a factor phi in the equivalence decomposition means that we - # found an actual factor of G, i.e., we can set - # v(phi)=infinity - # However, this should already have happened in the last step - # (when this polynomial had -infinite slope in the Newton - # polygon.) - if self.is_gauss_valuation(): # unless in the first step - pass - else: - continue - - verbose("Determining the valuation for %s"%phi, level=2, caller_name="mac_lane_step") - w = self.augmentation(phi, self(phi), check=False) - NP = w.newton_polygon(G).principal_part() - verbose("Newton-Polygon for v(phi)=%s : %s"%(self(phi), NP), level=2, caller_name="mac_lane_step") - slopes = NP.slopes(repetition=False) - if NP.vertices()[0][0] != 0: - slopes = [-infinity] + slopes - - if not slopes: - q,r = G.quo_rem(phi) - assert not r.is_zero() - phi = phi.coefficients(sparse=False) - for i,c in enumerate(r.coefficients(sparse=False)): - if not c.is_zero(): - v = w(c) - # for a correct result we need to add O(pi^v) in degree i - # we try to find the coefficient of phi where such an - # error can be introduced without losing much absolute - # precision on phi - best = i - for j in range(i): - if w(q[j]) < w(q[best]): - best = j - # now add the right O() to phi in degree i - best - phi[i-best] = phi[i-best].add_bigoh(w(c)-w(q[best])) - - phi = G.parent()(phi) - w = self._base_valuation.augmentation(phi, infinity) - ret.append(w) - continue - - for i in range(len(slopes)): - slope = slopes[i] - verbose("Slope = %s"%slope, level=3, caller_name="mac_lane_step") - new_mu = self(phi) - slope - base = self - if phi.degree() == base.phi().degree(): - assert new_mu > self(phi) - if not base.is_gauss_valuation(): - base = base._base_valuation - new_leaf = base.augmentation(phi, new_mu) - assert slope is -infinity or 0 in new_leaf.newton_polygon(G).slopes(repetition=False) - ret.append(new_leaf) - - assert ret - return ret - @cached_method def _equivalence_reduction(self, f): r""" @@ -1129,8 +1133,11 @@ def _test_lift_to_key(self, **options): tester.assertIs(f.parent(), self.domain()) tester.assertTrue(self.is_key(f)) - w = self.augmentation(f, self(f) + 1) - tester.assertGreaterEqual(len(w.residue_ring()(F).roots()), 1) + from sage.rings.all import infinity + w = self.augmentation(f, infinity) + F = F.change_ring(w.residue_ring()) + roots = F.roots(multiplicities=False) + tester.assertGreaterEqual(len(roots), 1) def _test_is_equivalence_irreducible(self, **options): r""" @@ -1159,7 +1166,11 @@ def _test_is_equivalence_irreducible(self, **options): tester.assertFalse(self.is_equivalence_irreducible(self.phi() ** 2)) -class InfiniteInductiveValuation(InductiveValuation, InfiniteDiscretePseudoValuation): +class FinalInductiveValuation(InductiveValuation): + pass + + +class InfiniteInductiveValuation(FinalInductiveValuation, InfiniteDiscretePseudoValuation): r""" Abstract base class for iterated :class:`AugmentedValuation` on top of a :class:`GaussValuation` which is not discrete valuation, i.e., the last key diff --git a/valuation.py b/valuation.py index 37c2784d633..0ba746922ab 100644 --- a/valuation.py +++ b/valuation.py @@ -474,7 +474,7 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): sage: v0 = GaussValuation(K._ring,pAdicValuation(QQ,3)) sage: v1 = v0.augmentation(K._ring.gen(),1/3) sage: mu0 = FunctionFieldValuation(K, v1) - sage: mu0.mac_lane_approximants(F) + sage: mu0.mac_lane_approximants(F) # long time [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + x) = 2/3 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + 2*x) = 2/3 ]] From a4755cbebd62306af8aef9541473b6f1fd886597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 8 Nov 2016 19:11:30 -0500 Subject: [PATCH 092/740] fix lifting of function field valuations --- function_field_valuation.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/function_field_valuation.py b/function_field_valuation.py index 01fa1a30b8e..3667d13bcea 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -605,12 +605,16 @@ def lift(self, F): """ F = self.residue_ring().coerce(F) - if F in self._base_valuation.residue_ring().fraction_field(): + if F in self._base_valuation.residue_ring(): + num = self._base_valuation.residue_ring()(F) + den = self._base_valuation.residue_ring()(1) + elif F in self._base_valuation.residue_ring().fraction_field(): num = self._base_valuation.residue_ring()(F.numerator()) den = self._base_valuation.residue_ring()(F.denominator()) + else: + raise NotImplementedError("lifting not implemented for this valuation") - return self.domain()(self._base_valuation.lift(num)) / self.domain()(self._base_valuation.lift(den)) - raise NotImplementedError("lifting not implemented for this valuation") + return self.domain()(self._base_valuation.lift(num)) / self.domain()(self._base_valuation.lift(den)) def value_group(self): r""" From 01133f898440e7e09126033365fa0059915769e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 8 Nov 2016 22:17:06 -0500 Subject: [PATCH 093/740] Simplify and fix lifting of valuations which have a trivial residue ring --- augmented_valuation.py | 85 ++++++++---------------------------------- 1 file changed, 16 insertions(+), 69 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 13d3f0e093e..2c537a1fab0 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -955,6 +955,10 @@ def lift(self, F): - ``F`` -- an element of the :meth:`residue_ring` + ALGORITHM: + + We simply undo the steps performed in :meth:`reduce`. + OUTPUT: A polynomial in the domain of the valuation with reduction ``F``. @@ -967,29 +971,19 @@ def lift(self, F): if F.is_one(): return self.domain().one() - if self._base_valuation.is_trivial(): - if self._mu == 0: - raise NotImplementedError - if self.phi() == self.domain().gen(): - # this is a valuation of the form [p-adic valuation, v(x) = 1] - constant = self.restriction(self.domain().base_ring()).lift(F) - assert constant in self.domain().base_ring() - return self.domain()(constant) + # Write F as a polynomial in self._residue_field_generator() + # We only have to do that if psi is non-trivial + if self.psi().degree() > 1: + from sage.rings.polynomial.polynomial_quotient_ring_element import PolynomialQuotientRingElement + if isinstance(F, PolynomialQuotientRingElement): + G = F.lift().change_variable_name(self._base_valuation.residue_ring().variable_name()) else: - if self.phi().degree() == 1: - # this is a classical valuation of a rational point, of the - # form [trivial, v(x + 1) = 1] - assert self.domain().base_ring() is self.residue_ring() - return self.domain()(F) - if self.phi().change_variable_name(self.residue_ring().polynomial().variable_name()) == self.residue_ring().polynomial(): - # this is a classical valuation of a point, of the from - # [trivial, v(x^2 + 1) = 1] - if hasattr(F, 'polynomial'): - u = F.polynomial() - if hasattr(F, 'element'): - u = F.element() - return self.domain()(u.change_variable_name(self.phi().variable_name())) - raise NotImplementedError + G = F.polynomial(self._base_valuation.residue_ring().variable_name()) + assert(G(self._residue_field_generator()) == F) + F = G + + H = self._base_valuation.lift(F) + return self.domain()(H) class AugmentedValuationWithNonTrivialResidueRing(AugmentedValuation_base, NonFinalInductiveValuation): @@ -1491,50 +1485,3 @@ def valuations(self, f): yield self._base_valuation(self.coefficients(f).next()) for i in range(num_infty_coefficients): yield infinity - - def lift(self, F): - """ - Return a polynomial which :meth:`reduce`s to ``F``. - - INPUT: - - - ``F`` -- an element of the :meth:`residue_ring` - - OUTPUT: - - a polynomial in the domain of the valuation with reduction ``F``, monic - if ``F`` is monic - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: R. = Qq(4, 10) - sage: S. = R[] - sage: v = GaussValuation(S) - - sage: w = v.augmentation(x^2 + x + u, infinity) - sage: r = w.residue_ring() - sage: u1 = w.residue_ring().gen() - sage: w.lift(1) - 1 + O(2^10) - sage: w.lift(0) - 0 - sage: w.lift(u1) - (1 + O(2^10))*x - - """ - F = self.residue_ring().coerce(F) - - if self.residue_ring() == self._base_valuation.residue_ring(): - return self._base_valuation.lift(F) - else: - if F not in self._base_valuation.residue_ring(): - if hasattr(F, 'polynomial'): - F = F.polynomial(self._base_valuation.residue_ring().variable_name()) - elif hasattr(F, 'element'): - F = F.element(self._base_valuation.residue_ring().variable_name()) - elif hasattr(F, 'lift'): - F = F.lift().change_variable_name(self._base_valuation.residue_ring().variable_name()) - else: - raise NotImplementedError - return self._base_valuation.lift(F) From f27d91ae99dc893bb532862353bd997d62e1c49a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 8 Nov 2016 22:22:55 -0500 Subject: [PATCH 094/740] long time markers --- function_field_valuation.py | 4 ++-- inductive_valuation.py | 2 +- trivial_valuation.py | 2 +- valuation_space.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/function_field_valuation.py b/function_field_valuation.py index 3667d13bcea..a345960f1e4 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -79,8 +79,8 @@ sage: v = FunctionFieldValuation(K, 1/x) sage: R. = K[] sage: L. = K.extension(y^2 - 1/(x^2 + 1)) - sage: w = v.extensions(L) - sage: TestSuite(w).run() + sage: w = v.extensions(L) # long time + sage: TestSuite(w).run() # long time Run test suite for extensions which come from the splitting in the base field:: diff --git a/inductive_valuation.py b/inductive_valuation.py index 7bc20b1946e..4f8ab737e2e 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -1185,7 +1185,7 @@ class InfiniteInductiveValuation(FinalInductiveValuation, InfiniteDiscretePseudo TESTS:: - sage: TestSuite(w).run() + sage: TestSuite(w).run() # long time """ pass diff --git a/trivial_valuation.py b/trivial_valuation.py index d29e2aa6a48..57cc736cfb4 100644 --- a/trivial_valuation.py +++ b/trivial_valuation.py @@ -166,7 +166,7 @@ class TrivialDiscretePseudoValuation(TrivialDiscretePseudoValuation_base, Infini TESTS:: - sage: TestSuite(v).run() + sage: TestSuite(v).run() # long time """ def __init__(self, parent): diff --git a/valuation_space.py b/valuation_space.py index bc09c47ade7..3b98102d559 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -729,7 +729,7 @@ def _test_shift(self, **options): sage: from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) - sage: v._test_shift() + sage: v._test_shift() # long time """ tester = self._tester(**options) From e6c384b7a656ce13212d4565a524fc1afc62fc14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 8 Nov 2016 22:23:11 -0500 Subject: [PATCH 095/740] Fix lifting over function field extensions --- augmented_valuation.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 2c537a1fab0..529c5921b00 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -975,12 +975,15 @@ def lift(self, F): # We only have to do that if psi is non-trivial if self.psi().degree() > 1: from sage.rings.polynomial.polynomial_quotient_ring_element import PolynomialQuotientRingElement + from sage.rings.function_field.function_field_element import FunctionFieldElement_polymod if isinstance(F, PolynomialQuotientRingElement): - G = F.lift().change_variable_name(self._base_valuation.residue_ring().variable_name()) + G = F.lift() + elif isinstance(F, FunctionFieldElement_polymod): + G = F.element() else: - G = F.polynomial(self._base_valuation.residue_ring().variable_name()) + G = F.polynomial() assert(G(self._residue_field_generator()) == F) - F = G + F = G.change_variable_name(self._base_valuation.residue_ring().variable_name()) H = self._base_valuation.lift(F) return self.domain()(H) From 16a27e231c14d8caab5df6739fa1b610eb5132eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 8 Nov 2016 22:30:57 -0500 Subject: [PATCH 096/740] add doctest for lift() --- augmented_valuation.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/augmented_valuation.py b/augmented_valuation.py index 529c5921b00..f0d612c6d6d 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -963,6 +963,29 @@ def lift(self, F): A polynomial in the domain of the valuation with reduction ``F``. + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + + sage: w = v.augmentation(x, 1) + sage: w.lift(1/2) + 1/2 + + sage: w = v.augmentation(x^2 + x + 1, infinity) + sage: w.lift(w.residue_ring().gen()) + x + + A case with non-trivial base valuation:: + + sage: R. = Qq(4, 10) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, infinity) + sage: w.lift(w.residue_ring().gen()) + (1 + O(2^10))*x + """ F = self.residue_ring().coerce(F) From c202dfb36638e407c8e54a9da814c8ac6a8ecb87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 8 Nov 2016 23:00:38 -0500 Subject: [PATCH 097/740] Add doctests to classes --- __init__.py | 3 +- augmented_valuation.py | 105 +++++++++++++++++++++++++++++++++++++---- inductive_valuation.py | 41 +++++++++------- 3 files changed, 122 insertions(+), 27 deletions(-) diff --git a/__init__.py b/__init__.py index f83b071b607..0c53a83e393 100644 --- a/__init__.py +++ b/__init__.py @@ -44,7 +44,8 @@ from valuation import DiscretePseudoValuation, DiscreteValuation, InfiniteDiscretePseudoValuation from padic_valuation import pAdicValuation_base, pAdicValuation_int, pAdicValuation_padic, pAdicFromLimitValuation from developing_valuation import DevelopingValuation -from augmented_valuation import AugmentedValuation_base +from augmented_valuation import AugmentedValuation_base, FinalAugmentedValuation, NonFinalAugmentedValuation, FinalFiniteAugmentedValuation, NonFinalFiniteAugmentedValuation +from inductive_valuation import FiniteInductiveValuation, FinalInductiveValuation, InfiniteInductiveValuation # ================= # MONKEY PATCH SAGE diff --git a/augmented_valuation.py b/augmented_valuation.py index f0d612c6d6d..954681aa55d 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -136,7 +136,7 @@ sys.path.append(os.path.dirname(os.getcwd())) from inductive_valuation import _lift_to_maximal_precision -from inductive_valuation import NonFinalInductiveValuation, FiniteInductiveValuation, InfiniteInductiveValuation, InductiveValuation +from inductive_valuation import FinalInductiveValuation, NonFinalInductiveValuation, FiniteInductiveValuation, InfiniteInductiveValuation, InductiveValuation from valuation import InfiniteDiscretePseudoValuation, DiscreteValuation from sage.misc.cachefunc import cached_method @@ -229,9 +229,9 @@ def create_object(self, version, key): parent = DiscretePseudoValuationSpace(base_valuation.domain()) if mu < infinity: if base_valuation.is_trivial(): - return parent.__make_element_class__(FiniteAugmentedValuationWithTrivialResidueRing)(parent, base_valuation, phi, mu) + return parent.__make_element_class__(FinalFiniteAugmentedValuation)(parent, base_valuation, phi, mu) else: - return parent.__make_element_class__(FiniteAugmentedValuationWithNonTrivialResidueRing)(parent, base_valuation, phi, mu) + return parent.__make_element_class__(NonFinalFiniteAugmentedValuation)(parent, base_valuation, phi, mu) else: return parent.__make_element_class__(InfiniteAugmentedValuation)(parent, base_valuation, phi, mu) @@ -785,7 +785,21 @@ def _ge_(self, other): return super(AugmentedValuation_base, self)._ge_(other) -class AugmentedValuationWithTrivialResidueRing(AugmentedValuation_base, InductiveValuation): +class FinalAugmentedValuation(AugmentedValuation_base, FinalInductiveValuation): + r""" + An augmented valuation which can not be augmented anymore, either because + it augments a trivial valuation or because it is infinite. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: w = v.augmentation(x, 1) + sage: isinstance(w, FinalAugmentedValuation) + True + + """ @cached_method def residue_ring(self): r""" @@ -1012,7 +1026,20 @@ def lift(self, F): return self.domain()(H) -class AugmentedValuationWithNonTrivialResidueRing(AugmentedValuation_base, NonFinalInductiveValuation): +class NonFinalAugmentedValuation(AugmentedValuation_base, NonFinalInductiveValuation): + r""" + An augmented valuation which can be augmented further. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, 1) + sage: isinstance(w, NonFinalAugmentedValuation) + True + + """ @cached_method def residue_ring(self): r""" @@ -1394,6 +1421,22 @@ def _Q_reciprocal(self): class FiniteAugmentedValuation(AugmentedValuation_base, FiniteInductiveValuation): + r""" + A finite augmented valuation, i.e., an augmented valuation which is + discrete, or equivalently an augmented valuation which assigns to its last + key polynomial a finite valuation. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4, 5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, 1/2) + sage: isinstance(w, FiniteAugmentedValuation) + True + + """ @cached_method def value_group(self): """ @@ -1454,15 +1497,57 @@ def valuations(self, f): yield self._base_valuation(c) + i*self._mu -class FiniteAugmentedValuationWithTrivialResidueRing(FiniteAugmentedValuation, AugmentedValuationWithTrivialResidueRing): - pass +class FinalFiniteAugmentedValuation(FiniteAugmentedValuation, FinalAugmentedValuation): + r""" + An augmented valuation which is discrete, i.e., which assigns a finite + valuation to its last key polynomial, but which can not be further + augmented. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: w = v.augmentation(x, 1) + sage: isinstance(w, FinalFiniteAugmentedValuation) + True + + """ + + +class NonFinalFiniteAugmentedValuation(FiniteAugmentedValuation, NonFinalAugmentedValuation): + r""" + An augmented valuation which is discrete, i.e., which assigns a finite + valuation to its last key polynomial, and which can be augmented furter. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x, 1) + sage: isinstance(w, NonFinalFiniteAugmentedValuation) + True + + """ + +class InfiniteAugmentedValuation(FinalAugmentedValuation, InfiniteInductiveValuation): + r""" + An augmented valuation which is infinite, i.e., which assigns valuation + infinity to its last key polynomial (and which can therefore not be + augmented further.) -class FiniteAugmentedValuationWithNonTrivialResidueRing(FiniteAugmentedValuation, AugmentedValuationWithNonTrivialResidueRing): - pass + TESTS:: + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x, infinity) + sage: isinstance(w, InfiniteAugmentedValuation) + True -class InfiniteAugmentedValuation(AugmentedValuationWithTrivialResidueRing, InfiniteInductiveValuation): + """ @cached_method def value_group(self): """ diff --git a/inductive_valuation.py b/inductive_valuation.py index 4f8ab737e2e..28b21712107 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -439,7 +439,9 @@ def _test_inductive_valuation_inheritance(self, **options): r""" Test that every instance that is a :class:`InductiveValuation` is either a :class:`FiniteInductiveValuation` or a - :class:`InfiniteInductiveValuation`. + :class:`InfiniteInductiveValuation`. Same for + :class:`FinalInductiveValuation` and + :class:`NonFinalInductiveValuation`. EXAMPLES:: @@ -451,6 +453,7 @@ def _test_inductive_valuation_inheritance(self, **options): """ tester = self._tester(**options) tester.assertTrue(isinstance(self, InfiniteInductiveValuation) != isinstance(self, FiniteInductiveValuation)) + tester.assertTrue(isinstance(self, FinalInductiveValuation) != isinstance(self, NonFinalInductiveValuation)) class FiniteInductiveValuation(InductiveValuation, DiscreteValuation): @@ -459,15 +462,13 @@ class FiniteInductiveValuation(InductiveValuation, DiscreteValuation): :class:`GaussValuation` which is a discrete valuation, i.e., the last key polynomial has finite valuation. - EXAMPLES:: + TESTS:: sage: from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) - - TESTS:: - - sage: TestSuite(v).run() # long time + sage: isinstance(v, FiniteInductiveValuation) + True """ @@ -1167,28 +1168,36 @@ def _test_is_equivalence_irreducible(self, **options): class FinalInductiveValuation(InductiveValuation): - pass + r""" + Abstract base class for an inductive valuation which can not be augmented further. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: w = v.augmentation(x^2 + x + 1, infinity) + sage: isinstance(w, FinalInductiveValuation) + True + + """ class InfiniteInductiveValuation(FinalInductiveValuation, InfiniteDiscretePseudoValuation): r""" - Abstract base class for iterated :class:`AugmentedValuation` on top of a - :class:`GaussValuation` which is not discrete valuation, i.e., the last key - polynomial has infinite valuation. + Abstract base class for an inductive valuation which is not discrete, i.e., + which assigns infinite valuation to its last key polynomial. - EXAMPLES:: + TESTS:: sage: from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, infinity) - - TESTS:: - - sage: TestSuite(w).run() # long time + sage: isinstance(w, InfiniteInductiveValuation) + True """ - pass def _lift_to_maximal_precision(c): From d8ecc199d4c81ae2f55b1d1eac9684e594506fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 9 Nov 2016 00:51:42 -0500 Subject: [PATCH 098/740] Fix shifts and work around ZpCA/ZpCR bugs --- TODO | 2 -- augmented_valuation.py | 7 ++----- inductive_valuation.py | 20 +++++++++++++++++-- padic_valuation.py | 44 ++++++++++++++++++++++++++++++++++++++++++ valuation_space.py | 29 +++++++++++++++++++++------- 5 files changed, 86 insertions(+), 16 deletions(-) diff --git a/TODO b/TODO index 9a5700f3f92..2c5e4b27100 100644 --- a/TODO +++ b/TODO @@ -1,3 +1 @@ * Replace almost all doctests with the ring case. If it works over rings, then it will work over fields. - -* Run TestSuite for all valuations created in doctests diff --git a/augmented_valuation.py b/augmented_valuation.py index 954681aa55d..56faeb7506f 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -7,8 +7,8 @@ TESTS:: sage: from mac_lane import * # optional: standalone - sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: R. = ZZ[] + sage: v = GaussValuation(R, pAdicValuation(ZZ, 2)) sage: w = v.augmentation(x, 1) sage: TestSuite(w).run() # long time @@ -433,9 +433,6 @@ def element_with_valuation(self, s): """ if s not in self.value_group(): raise ValueError("s must be in the value group of the valuation") - from sage.categories.fields import Fields - if s < 0 and not self.domain().base_ring() in Fields(): - raise NotImplementedError("only implemented for polynomial rings over fields") ret = self.domain().one() while s not in self._base_valuation.value_group(): diff --git a/inductive_valuation.py b/inductive_valuation.py index 28b21712107..d83329ad11c 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -204,7 +204,7 @@ def equivalence_unit(self, s): sage: w.equivalence_unit(-1) Traceback (most recent call last): ... - TypeError: no conversion of this rational to integer + ValueError: can not shift 1 down by 1 in Integer Ring with respect to 2-adic valuation """ @@ -303,6 +303,16 @@ def element_with_valuation(self, s): sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: v.element_with_valuation(-2) 1/4 + + Depending on the base ring, an element of valuation ``s`` might not + exist:: + + sage: R. = ZZ[] + sage: v = GaussValuation(R, pAdicValuation(ZZ, 2)) + sage: v.element_with_valuation(-2) + Traceback (most recent call last): + ... + ValueError: can not shift 1 down by 2 in Integer Ring with respect to 2-adic valuation """ @@ -321,7 +331,13 @@ def _test_element_with_valuation(self, **options): tester = self._tester(**options) chain = self.augmentation_chain() for s in tester.some_elements(self.value_group().some_elements()): - R = self.element_with_valuation(s) + try: + R = self.element_with_valuation(s) + except ValueError: + from sage.categories.fields import Fields + if self.domain() not in Fields(): + raise + continue tester.assertEqual(self(R), s) if chain != [self]: base = chain[1] diff --git a/padic_valuation.py b/padic_valuation.py index 2c4b5bd37c2..c46f4051d17 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -816,13 +816,57 @@ def shift(self, c, v): sage: v.shift(R(2),3) 2*3^3 + O(3^23) + TESTS: + + Make sure that we work around the following bug in p-adics:: + + sage: R = Zp(3) + sage: R(1) >> 1 # this should throw an exception + O(3^19) + + However, our shift gets this right:: + + sage: v = pAdicValuation(Zp(3)) + sage: v.shift(R(1), -1) + Traceback (most recent call last): + ... + ValueError: can not shift down 1 + O(3^20) by 1 in 3-adic Ring with capped relative precision 20 with respect to 3-adic valuation + + Note that the same happens for ZpCA:: + + sage: R = ZpCA(3) + sage: R(1) >> 1 # this should throw an exception + O(3^19) + + But we also detect that one:: + + sage: v = pAdicValuation(ZpCA(3)) + sage: v.shift(R(1), -1) + Traceback (most recent call last): + ... + ValueError: can not shift down 1 + O(3^20) by 1 in 3-adic Ring with capped absolute precision 20 with respect to 3-adic valuation + """ from sage.rings.all import QQ, ZZ c = self.domain().coerce(c) + if c == 0: + if v == 0: + return c + raise ValueError("can not shift a zero") + v = QQ(v) if v not in self.value_group(): raise ValueError("%r is not in the value group of %r"%(v, self)) v = ZZ(v * self.domain().ramification_index()) + + # Work around a bug in ZpCR/ZpCA p-adics + # TODO: fix this upstream + from sage.rings.padics.padic_capped_absolute_element import pAdicCappedAbsoluteElement + from sage.rings.padics.padic_capped_relative_element import pAdicCappedRelativeElement + from sage.categories.fields import Fields + if (isinstance(c, pAdicCappedAbsoluteElement) or (isinstance(c, pAdicCappedRelativeElement) and c.parent() not in Fields())) and -v > self(c): + raise ValueError("can not shift down %r by %r in %r with respect to %r"%(c, -v, self.domain(), self)) + return c< self(x): + raise ValueError("can not shift %r down by %r in %r with respect to %r"%(x, -s, self.domain(), self)) + return self.domain()(x * (self.domain().fraction_field()(self.uniformizer()) ** ZZ(s/self.value_group().gen()))) @abstract_method def residue_ring(self): @@ -742,16 +749,24 @@ def _test_shift(self, **options): V = tester.some_elements(self.value_group().some_elements()) from sage.categories.cartesian_product import cartesian_product for x, n in tester.some_elements(cartesian_product([S,V])): - v = self(x) - from sage.categories.fields import Fields - if n < 0 and self.domain() not in Fields(): - # note that shifting might not be possible in this case even if -n > v + if x == 0 and n != 0: + with tester.assertRaises(ValueError): + self.shift(x, n) continue + + v = self(x) try: y = self.shift(x, n) except NotImplementedError: # not all valuations can implement consistent shifts continue + except ValueError: + from sage.categories.fields import Fields + if -n > v and self.domain() not in Fields(): + # note that shifting might not be possible in this case even if -n > v + continue + raise + tester.assertIs(y.parent(), self.domain()) tester.assertEqual(self(y), v + n) # shifts preserve reductions From 54b35e6308715365f065cec1d21e4a7a68087864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 9 Nov 2016 00:55:20 -0500 Subject: [PATCH 099/740] fix shifts of Gauss valuations --- gauss_valuation.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gauss_valuation.py b/gauss_valuation.py index 4d229ab1b99..a39ddb2313c 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -270,10 +270,16 @@ def shift(self, f, s): f = self.domain().coerce(f) s = self.value_group()(s) + if s == 0: + return f + from sage.categories.fields import Fields if -s > self(f) and self.domain().base_ring() not in Fields(): raise ValueError("since %r is not a field, -s must not exceed the valuation of f but %r does exceed %r"%(self.domain().base_ring(), -s, self(f))) + if f == 0: + raise ValueError("can not shift zero") + return f.map_coefficients(lambda c:self._base_valuation.shift(c, s)) def valuations(self, f): From 2aa219aeb4eae02033aea10e05742fffcb393d2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 9 Nov 2016 01:30:15 -0500 Subject: [PATCH 100/740] More shift fixes over rings --- augmented_valuation.py | 14 +++++++++----- inductive_valuation.py | 8 ++++++-- valuation_space.py | 3 +-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 56faeb7506f..d2c4acad80c 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -386,10 +386,6 @@ def equivalence_unit(self, s): (2^-1 + O(2^4))*x^2 """ - from sage.categories.fields import Fields - if s < 0 and not self.domain().base_ring() in Fields(): - raise NotImplementedError("only implemented for polynomial rings over fields") - ret = self._base_valuation.element_with_valuation(s) assert self.is_equivalence_unit(ret) @@ -486,7 +482,15 @@ def shift(self, x, s): raise ValueError("s must be in the value group of the valuation") if self.value_group() == self._base_valuation.value_group(): - return self._base_valuation.shift(x, s) + try: + return self._base_valuation.shift(x, s) + except ValueError: + from sage.categories.fields import Fields + # only report a ValueError if we are over a field, otherwise, + # falling through to the NotImplementedError below seems to be + # the better error to report + if self.domain().base() in Fields(): + raise if self._base_valuation.value_group().is_trivial(): # We could implement a consistent shift in this case by multplying diff --git a/inductive_valuation.py b/inductive_valuation.py index d83329ad11c..957db8c7e5d 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -335,7 +335,7 @@ def _test_element_with_valuation(self, **options): R = self.element_with_valuation(s) except ValueError: from sage.categories.fields import Fields - if self.domain() not in Fields(): + if self.domain().base() in Fields(): raise continue tester.assertEqual(self(R), s) @@ -402,16 +402,20 @@ def _test_equivalence_unit(self, **options): """ tester = self._tester(**options) + if self.is_gauss_valuation(): value_group = self.value_group() else: value_group = self.augmentation_chain()[1].value_group() + for s in tester.some_elements(value_group.some_elements()): try: R = self.equivalence_unit(s) except ValueError: - if s >= 0 or self.domain().base_ring() in Fields(): + from sage.categories.fields import Fields + if s >= 0 or self.domain().base() in Fields(): raise + continue tester.assertIs(R.parent(), self.domain()) tester.assertEqual(self(R), s) diff --git a/valuation_space.py b/valuation_space.py index aa5044550fc..1ef0baa0721 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -750,7 +750,7 @@ def _test_shift(self, **options): from sage.categories.cartesian_product import cartesian_product for x, n in tester.some_elements(cartesian_product([S,V])): if x == 0 and n != 0: - with tester.assertRaises(ValueError): + with tester.assertRaises((ValueError, NotImplementedError)): self.shift(x, n) continue @@ -763,7 +763,6 @@ def _test_shift(self, **options): except ValueError: from sage.categories.fields import Fields if -n > v and self.domain() not in Fields(): - # note that shifting might not be possible in this case even if -n > v continue raise From fbf8ebc21333b6e026f2ff63bda2b30d07c6f0c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 9 Nov 2016 23:04:06 -0500 Subject: [PATCH 101/740] Better handling of valuations over rings --- TODO | 2 +- augmented_valuation.py | 68 +++++++++++++++++++++++++++++++++++------- inductive_valuation.py | 62 +++++++++++++++++++++++++++++++------- valuation_space.py | 44 +++++++++++++++++++++++++-- 4 files changed, 152 insertions(+), 24 deletions(-) diff --git a/TODO b/TODO index 2c5e4b27100..0d54ebcbc95 100644 --- a/TODO +++ b/TODO @@ -1 +1 @@ -* Replace almost all doctests with the ring case. If it works over rings, then it will work over fields. +* Check every methods about limitations in the ring case (and correction of the docstring) diff --git a/augmented_valuation.py b/augmented_valuation.py index d2c4acad80c..ce845b84d5e 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -4,11 +4,31 @@ Implements augmentations of valutions as defined in [ML1936]. -TESTS:: +Starting from a :class:`GaussValuation`, we can create augmented valuations on +polynomial rings:: sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x, 1); w + [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ] + sage: w(x) + 1 + +This also works for polynomial rings over base rings which are not fields. +However, much of the functionality is only available over fields:: + sage: R. = ZZ[] sage: v = GaussValuation(R, pAdicValuation(ZZ, 2)) + sage: w = v.augmentation(x, 1); w + [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ] + sage: w(x) + 1 + +TESTS:: + + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x, 1) sage: TestSuite(w).run() # long time @@ -106,6 +126,13 @@ sage: ww = w.augmentation((x^2 + x + u)^2 + 2, infinity) sage: TestSuite(ww).run() # long time +Run the test suite if the polynomial ring is not over a field:: + + sage: R. = ZZ[] + sage: v = GaussValuation(R, pAdicValuation(ZZ, 2)) + sage: w = v.augmentation(x, 1) + sage: TestSuite(w).run() # long time + REFERENCES: .. [ML1936] Mac Lane, S. (1936). A construction for prime ideals as absolute @@ -478,19 +505,15 @@ def shift(self, x, s): These do, however, not exist for all values of ``s``. """ + from sage.categories.fields import Fields + if not self.domain().base_ring() in Fields(): + raise NotImplementedError("only implemented for polynomial rings over fields") + if s not in self.value_group(): raise ValueError("s must be in the value group of the valuation") if self.value_group() == self._base_valuation.value_group(): - try: - return self._base_valuation.shift(x, s) - except ValueError: - from sage.categories.fields import Fields - # only report a ValueError if we are over a field, otherwise, - # falling through to the NotImplementedError below seems to be - # the better error to report - if self.domain().base() in Fields(): - raise + return self._base_valuation.shift(x, s) if self._base_valuation.value_group().is_trivial(): # We could implement a consistent shift in this case by multplying @@ -785,6 +808,24 @@ def _ge_(self, other): return super(AugmentedValuation_base, self)._ge_(other) + def is_trivial(self): + r""" + Return whether this valuation is trivial, i.e., zero outside of zero. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, 1) + sage: w.is_trivial() + False + + """ + # We need to override the default implementation from valuation_space + # because that one uses uniformizer() which might not be implemented if + # the base ring is not a field. + return False class FinalAugmentedValuation(AugmentedValuation_base, FinalInductiveValuation): r""" @@ -837,6 +878,8 @@ def residue_ring(self): Finite Field of size 2 """ + # the following is correct, even if the polynomial ring is not over a field + generator = 'u' + str(len(self.augmentation_chain()) - 1) base = self._base_valuation.residue_ring().base() @@ -1066,6 +1109,10 @@ def residue_ring(self): Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) """ + from sage.categories.fields import Fields + if self.domain().base() not in Fields(): + raise NotImplementedError("only implemented for polynomial rings over fields") + generator = 'u' + str(len(self.augmentation_chain()) - 1) base = self._base_valuation.residue_ring().base() @@ -1345,6 +1392,7 @@ def lift_to_key(self, F): from sage.categories.fields import Fields if not self.domain().base_ring() in Fields(): raise NotImplementedError("only implemented for polynomial rings over fields") + if self._base_valuation.is_gauss_valuation() and self._mu == infinity: raise TypeError("there are no keys over this valuation") diff --git a/inductive_valuation.py b/inductive_valuation.py index 957db8c7e5d..a04e33b8b17 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -152,6 +152,11 @@ def equivalence_reciprocal(self, f): """ f = self.domain().coerce(f) + from sage.categories.fields import Fields + if not self.domain().base_ring() in Fields(): + # the xgcd does in general not work, i.e., return 1, unless over a field + raise NotImplementedError("only implemented for polynomial rings over fields") + if not self.is_equivalence_unit(f): raise ValueError("f must be an equivalence unit but %r is not"%(f,)) @@ -333,11 +338,13 @@ def _test_element_with_valuation(self, **options): for s in tester.some_elements(self.value_group().some_elements()): try: R = self.element_with_valuation(s) - except ValueError: + except (ValueError, NotImplementedError): + # this is often not possible unless the underlying ring of + # constants is a field from sage.categories.fields import Fields - if self.domain().base() in Fields(): - raise - continue + if self.domain().base() not in Fields(): + continue + raise tester.assertEqual(self(R), s) if chain != [self]: base = chain[1] @@ -411,12 +418,13 @@ def _test_equivalence_unit(self, **options): for s in tester.some_elements(value_group.some_elements()): try: R = self.equivalence_unit(s) - except ValueError: + except (ValueError, NotImplementedError): + # this is often not possible unless the underlying ring of + # constants is a field from sage.categories.fields import Fields - if s >= 0 or self.domain().base() in Fields(): - raise - continue - + if self.domain().base() not in Fields(): + continue + raise tester.assertIs(R.parent(), self.domain()) tester.assertEqual(self(R), s) tester.assertTrue(self.is_equivalence_unit(R)) @@ -452,7 +460,15 @@ def _test_equivalence_reciprocal(self, **options): S = tester.some_elements(self.domain().some_elements()) for f in S: if self.is_equivalence_unit(f): - g = self.equivalence_reciprocal(f) + try: + g = self.equivalence_reciprocal(f) + except (ValueError, NotImplementedError): + # this is often not possible unless the underlying ring of + # constants is a field + from sage.categories.fields import Fields + if self.domain().base() not in Fields(): + continue + raise tester.assertEqual(self.reduce(f*g), 1) def _test_inductive_valuation_inheritance(self, **options): @@ -882,6 +898,11 @@ def is_equivalence_irreducible(self, f): """ f = self.domain().coerce(f) + if not self.domain().base_ring().is_field(): + domain = self.domain().change_ring(self.domain().base_ring().fraction_field()) + v = self.extension(domain) + return v.is_equivalence_irreducible(v.domain()(f)) + if f.is_constant(): raise ValueError("f must not be constant") @@ -1077,6 +1098,10 @@ def minimal_representative(self, f): """ f = self.domain().coerce(f) + from sage.categories.fields import Fields + if not self.domain().base_ring() in Fields(): + raise NotImplementedError("only implemented for polynomial rings over fields") + if f.is_zero(): raise ValueError("zero has no minimal representative") @@ -1147,10 +1172,25 @@ def _test_lift_to_key(self, **options): """ tester = self._tester(**options) + + try: + k = self.residue_ring() + except NotImplementedError: + from sage.categories.fields import Fields + if self.domain().base() in Fields(): + raise + return + S = tester.some_elements(self.residue_ring().some_elements()) for F in S: if F.is_monic() and not F.is_constant() and F.is_irreducible(): - f = self.lift_to_key(F) + try: + f = self.lift_to_key(F) + except NotImplementedError: + from sage.categories.fields import Fields + if self.domain().base() in Fields(): + raise + continue tester.assertIs(f.parent(), self.domain()) tester.assertTrue(self.is_key(f)) diff --git a/valuation_space.py b/valuation_space.py index 1ef0baa0721..2976e7f0d63 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -776,7 +776,7 @@ def _test_shift(self, **options): def _test_residue_ring(self, **options): r""" - Check the correctness of residue fields. + Check the correctness of residue rings. TESTS:: @@ -787,7 +787,17 @@ def _test_residue_ring(self, **options): """ tester = self._tester(**options) - r = self.residue_ring() + try: + r = self.residue_ring() + except NotImplementedError: + # over non-fields (and especially polynomial rings over + # non-fields) computation of the residue ring is often + # difficult and not very interesting + from sage.categories.fields import Fields + if self.domain() in Fields(): + raise + return + if r.zero() == r.one(): # residue ring is the zero rng tester.assertGreater(self(1), 0) @@ -810,6 +820,17 @@ def _test_reduce(self, **options): """ tester = self._tester(**options) + try: + k = self.residue_ring() + except NotImplementedError: + # over non-fields (and especially polynomial rings over + # non-fields) computation of the residue ring is often + # difficult and not very interesting + from sage.categories.fields import Fields + if self.domain() in Fields(): + raise + return + for x in tester.some_elements(self.domain().some_elements()): if self(x) < 0: with tester.assertRaises(ValueError): @@ -839,6 +860,17 @@ def _test_lift(self, **options): """ tester = self._tester(**options) + try: + k = self.residue_ring() + except NotImplementedError: + # over non-fields (and especially polynomial rings over + # non-fields) computation of the residue ring is often + # difficult and not very interesting + from sage.categories.fields import Fields + if self.domain() in Fields(): + raise + return + for X in tester.some_elements(self.residue_ring().some_elements()): x = self.lift(X) y = self.reduce(x) @@ -934,6 +966,14 @@ def _test_residue_field(self, **options): # a discrete valuation on a field has a residue field tester.assertFalse(self.domain() in Fields()) return + except NotImplementedError: + # over non-fields (and especially polynomial rings over + # non-fields) computation of the residue field is often + # difficult and not very interesting + from sage.categories.fields import Fields + if self.domain() in Fields(): + raise + return c = self.residue_field().characteristic() if c != 0: From 561e2be14098b2b809c9f640c766cc3506918417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 9 Nov 2016 23:04:48 -0500 Subject: [PATCH 102/740] fixed typos --- augmented_valuation.py | 2 +- inductive_valuation.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index ce845b84d5e..043e065f2aa 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -470,7 +470,7 @@ def shift(self, x, s): The element returned is such that repeated shifts which go back to the original valuation produce the same element in reduction. - EXAMPLES: + EXAMPLES:: sage: from mac_lane import * # optional: standalone sage: R. = QQ[] diff --git a/inductive_valuation.py b/inductive_valuation.py index a04e33b8b17..bff96b88c7e 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -1131,7 +1131,7 @@ def minimal_representative(self, f): @abstract_method def lift_to_key(self, F): """ - Lift the irreducible polynomial ``F`` from the ;meth:`residue_ring` to + Lift the irreducible polynomial ``F`` from the :meth:`residue_ring` to a key polynomial over this valuation. INPUT: From 59193b1d65a8dc95632a36f0d3f1a1fc8ca1dd27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 9 Nov 2016 23:36:15 -0500 Subject: [PATCH 103/740] shrink function field test cases --- function_field_valuation.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/function_field_valuation.py b/function_field_valuation.py index a345960f1e4..5bf65943c49 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -41,13 +41,13 @@ sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, 1) - sage: TestSuite(v).run() # long time + sage: TestSuite(v).run(max_runs=100) # long time sage: v = FunctionFieldValuation(K, x^2 + 1) - sage: TestSuite(v).run() # long time + sage: TestSuite(v).run(max_runs=100) # long time sage: v = FunctionFieldValuation(K, 1/x) - sage: TestSuite(v).run() # long time + sage: TestSuite(v).run(max_runs=100) # long time Run test suite over classical places of finite extensions:: @@ -56,7 +56,7 @@ sage: R. = K[] sage: L. = K.extension(y^2 - x) sage: ws = v.extensions(L) # long time - sage: for w in ws: TestSuite(w).run() # long time + sage: for w in ws: TestSuite(w).run(max_runs=100) # long time Run test suite for valuations that do not correspond to a classical place:: @@ -71,7 +71,7 @@ sage: K. = FunctionField(GF(3)) sage: M. = FunctionField(K) sage: v = FunctionFieldValuation(M, x^3 - t) - sage: TestSuite(v).run() # long time + sage: TestSuite(v).run(max_runs=10) # long time Run test suite for extensions over the infinite place:: @@ -88,14 +88,14 @@ sage: v = FunctionFieldValuation(K, x^2 + 1) sage: L. = FunctionField(GaussianIntegers().fraction_field()) sage: ws = v.extensions(L) - sage: for w in ws: TestSuite(w).run() # long time + sage: for w in ws: TestSuite(w).run(max_runs=100) # long time Run test suite for a finite place with residual degree and ramification:: sage: K. = FunctionField(GF(3)) sage: L. = FunctionField(K) sage: v = FunctionFieldValuation(L, x^6 - t) - sage: TestSuite(v).run() # long time + sage: TestSuite(v).run(max_runs=10) # long time Run test suite for a valuation which is backed by limit valuation:: @@ -425,7 +425,7 @@ class FunctionFieldValuation_base(DiscreteValuation): sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, FunctionFieldValuation_base) True - sage: TestSuite(v).run() # long time + sage: TestSuite(v).run(max_runs=100) # long time """ def extensions(self, L): From f92c132a6b75c95dc3af651a334353f72a7f789b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 9 Nov 2016 23:36:24 -0500 Subject: [PATCH 104/740] Use initial approximation in limit where possible --- limit_valuation.py | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/limit_valuation.py b/limit_valuation.py index a7f9388b1ea..db9a97bc5ac 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -199,7 +199,7 @@ def __init__(self, parent, approximation): """ DiscretePseudoValuation.__init__(self, parent) - self._initial_approximation = approximation # kept for consistent printing and equality tests + self._initial_approximation = approximation self._approximation = approximation def reduce(self, f): @@ -411,7 +411,29 @@ def lift(self, F): """ F = self.residue_ring().coerce(F) - return self._approximation.lift(F) + return self._initial_approximation.lift(F) + + def shift(self, x, s): + r""" + Return a modified version of ``x`` whose valuation is increased by ``s``. + + The element returned is such that repeated shifts which go back to + the original valuation produce the same element in reduction. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - (x - 1)) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L) + sage: w.shift(y, 1) + x*y + + """ + return self._initial_approximation.shift(x, s) def uniformizer(self): r""" @@ -430,7 +452,7 @@ def uniformizer(self): y """ - return self._approximation.uniformizer() + return self._initial_approximation.uniformizer() def _improve_approximation(self): r""" @@ -580,7 +602,7 @@ def residue_ring(self): Finite Field of size 2 """ - R = self._approximation.residue_ring() + R = self._initial_approximation.residue_ring() from sage.categories.fields import Fields if R in Fields(): # the approximation ends in v(phi)=infty From 9bc783c51ceaa3522cc66a76d56ce1689a51b091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 9 Nov 2016 23:36:56 -0500 Subject: [PATCH 105/740] Work around bug in EnumeratedSet the cartesian_product of [[ZZ(1)]] and [[QQ(1)]] is the same due to some broken caching/unique representation. --- valuation_space.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/valuation_space.py b/valuation_space.py index 2976e7f0d63..0180860268f 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -630,9 +630,9 @@ def _test_add(self, **options): """ tester = self._tester(**options) - S = tester.some_elements(self.domain().some_elements()) - from sage.categories.cartesian_product import cartesian_product - for x,y in tester.some_elements(cartesian_product([S,S])): + S = self.domain().some_elements() + from itertools import product + for x,y in tester.some_elements(product(S,S)): tester.assertGreaterEqual(self(x+y),min(self(x),self(y))) if self(x) != self(y): tester.assertEqual(self(x+y),min(self(x),self(y))) @@ -664,9 +664,9 @@ def _test_mul(self, **options): """ tester = self._tester(**options) - S = list(self.domain().some_elements()) - from sage.categories.cartesian_product import cartesian_product - for x,y in tester.some_elements(cartesian_product([S,S])): + S = self.domain().some_elements() + from itertools import product + for x,y in tester.some_elements(product(S,S)): tester.assertEqual(self(x*y),self(x)+self(y)) def _test_no_infinite_units(self, **options): @@ -745,10 +745,10 @@ def _test_shift(self, **options): # trivial valuations can not perform non-trivial shifts return - S = tester.some_elements(self.domain().some_elements()) - V = tester.some_elements(self.value_group().some_elements()) - from sage.categories.cartesian_product import cartesian_product - for x, n in tester.some_elements(cartesian_product([S,V])): + S = self.domain().some_elements() + V = self.value_group().some_elements() + from itertools import product + for x, n in tester.some_elements(product(S,V)): if x == 0 and n != 0: with tester.assertRaises((ValueError, NotImplementedError)): self.shift(x, n) From f49fd9565500a97f318f2e1165f46137a567e221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 9 Nov 2016 23:51:32 -0500 Subject: [PATCH 106/740] Fix printing of limit valuations by considering the chain of the initial approximant not the current one --- limit_valuation.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/limit_valuation.py b/limit_valuation.py index db9a97bc5ac..800cc11c90a 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -915,12 +915,13 @@ def _repr_(self): # print the minimal information that singles out this valuation from all approximants assert(self._base_valuation._initial_approximation in self._approximants) approximants = [v.augmentation_chain()[::-1] for v in self._approximants] - augmentations = self._base_valuation._approximation.augmentation_chain()[::-1] - unique_approximant = self._base_valuation._initial_approximation + augmentations = self._base_valuation._initial_approximation.augmentation_chain()[::-1] + unique_approximant = None for l in range(len(augmentations)): if len([a for a in approximants if a[:l+1] == augmentations[:l+1]]) == 1: unique_approximant = augmentations[:l+1] break + assert(unique_approximant is not None) if unique_approximant[0].is_gauss_valuation(): unique_approximant[0] = unique_approximant[0].restriction(unique_approximant[0].domain().base_ring()) if len(unique_approximant) == 1: From 3d19c4eef7faf973108eeaf688035e122795c3d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 10 Nov 2016 02:10:46 -0500 Subject: [PATCH 107/740] updated TODO --- TODO | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TODO b/TODO index 0d54ebcbc95..6d545ba585a 100644 --- a/TODO +++ b/TODO @@ -1 +1,5 @@ * Check every methods about limitations in the ring case (and correction of the docstring) + +* Fix date formatting 2016-11-01 + +* Properly call super classes From 2d7b2f64a0ae4ebcfa1426626018258b1fe679a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 10 Nov 2016 02:15:08 -0500 Subject: [PATCH 108/740] Honour substitutions when creating valuations from the monic integral model --- __init__.py | 4 +- augmented_valuation.py | 9 +- function_field_valuation.py | 26 +- gauss_valuation.py | 28 +- inductive_valuation.py | 23 +- limit_valuation.py | 302 +----------------- mapped_valuation.py | 593 ++++++++++++++++++++++++++++++++++++ padic_valuation.py | 3 +- valuation.py | 9 +- 9 files changed, 665 insertions(+), 332 deletions(-) create mode 100644 mapped_valuation.py diff --git a/__init__.py b/__init__.py index 0c53a83e393..021ac09513a 100644 --- a/__init__.py +++ b/__init__.py @@ -38,7 +38,8 @@ # different types) from trivial_valuation import TrivialDiscreteValuation, TrivialDiscretePseudoValuation from function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldFromLimitValuation -from limit_valuation import LimitValuation, MacLaneLimitValuation, FiniteExtensionFromInfiniteValuation, FiniteExtensionFromLimitValuation, LimitValuation_generic +from limit_valuation import LimitValuation, MacLaneLimitValuation, LimitValuation_generic +from mapped_valuation import MappedValuation_base, FiniteExtensionFromLimitValuation, FiniteExtensionFromInfiniteValuation, DomainMappedValuation_base, DomainMappedValuation, DomainMappedDiscreteValuation from augmented_valuation import FiniteAugmentedValuation, InfiniteAugmentedValuation from gauss_valuation import GaussValuation_generic from valuation import DiscretePseudoValuation, DiscreteValuation, InfiniteDiscretePseudoValuation @@ -541,3 +542,4 @@ def some_elements(self): register_factory_unpickle("FunctionFieldValuation", FunctionFieldValuation) register_factory_unpickle("AugmentedValuation", AugmentedValuation) register_factory_unpickle("LimitValuation", LimitValuation) +register_factory_unpickle("DomainMappedValuation", DomainMappedValuation) diff --git a/augmented_valuation.py b/augmented_valuation.py index 043e065f2aa..25d702e3be8 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -65,7 +65,6 @@ sage: ww = w.augmentation(x^4 + 8, 5) sage: TestSuite(ww).run() # long time - Run the test suite for a ramified augmentation of an unramified augmentation:: sage: R. = QQ[] @@ -757,7 +756,7 @@ def monic_integral_model(self, G): r""" Return a monic integral irreducible polynomial which defines the same extension of the base ring of the domain as the irreducible polynomial - ``G``. + ``G`` together with maps between the old and the new polynomial. EXAMPLES:: @@ -767,7 +766,11 @@ def monic_integral_model(self, G): sage: w = v.augmentation(x^2 + x + 1, 1) sage: w.monic_integral_model(5*x^2 + 1/2*x + 1/4) - x^2 + 1/5*x + 1/5 + (Ring endomorphism of Univariate Polynomial Ring in x over Rational Field + Defn: x |--> 1/2*x, + Ring endomorphism of Univariate Polynomial Ring in x over Rational Field + Defn: x |--> 2*x, + x^2 + 1/5*x + 1/5) """ return self._base_valuation.monic_integral_model(G) diff --git a/function_field_valuation.py b/function_field_valuation.py index 5bf65943c49..0f6d44207f4 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -132,7 +132,7 @@ from valuation import DiscreteValuation from trivial_valuation import TrivialValuation -from limit_valuation import FiniteExtensionFromLimitValuation +from mapped_valuation import FiniteExtensionFromLimitValuation, DomainMappedValuation class FunctionFieldValuationFactory(UniqueFactory): r""" @@ -456,12 +456,32 @@ def extensions(self, L): """ K = self.domain() from sage.categories.function_fields import FunctionFields - if L is self.domain(): + if L is K: return [self] if L in FunctionFields(): if K.is_subring(L): if L.base() is K: - # L is a simple extension of the domain of this valuation + # L = K[y]/(G) is a simple extension of the domain of this valuation + G = L.polynomial() + if not G.is_monic(): + G = G / G.leading_coefficient() + if any(self(c) < 0 for c in G.coefficients()): + # rewrite L = K[u]/(H) with H integral and compute the extensions + from gauss_valuation import GaussValuation + g = GaussValuation(G.parent(), self) + y_to_u, u_to_y, H = g.monic_integral_model(G) + M = K.extension(H, names=L.variable_names()) + H_extensions = self.extensions(M) + + # turn these extensions into extensions on L by applying the substitutions + from sage.rings.morphism import RingHomomorphism_im_gens + if type(y_to_u) == RingHomomorphism_im_gens and type(u_to_y) == RingHomomorphism_im_gens: + # both maps are trivial on the constants + M_to_L = M.hom(L(u_to_y(u_to_y.domain().gen()))) + L_to_M = L.hom(M(y_to_u(y_to_u.domain().gen()))) + return [DomainMappedValuation(L, w, L_to_M, M_to_L) for w in H_extensions] + else: + raise NotImplementedError return [FunctionFieldValuation(L, w) for w in self.mac_lane_approximants(L.polynomial())] elif L.base() is not L and K.is_subring(L): # recursively call this method for the tower of fields diff --git a/gauss_valuation.py b/gauss_valuation.py index a39ddb2313c..87974d1595f 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -633,7 +633,7 @@ def monic_integral_model(self, G): r""" Return a monic integral irreducible polynomial which defines the same extension of the base ring of the domain as the irreducible polynomial - ``G``. + ``G`` together with maps between the old and the new polynomial. EXAMPLES:: @@ -641,19 +641,31 @@ def monic_integral_model(self, G): sage: R. = Qp(2, 5)[] sage: v = GaussValuation(R) sage: v.monic_integral_model(5*x^2 + 1/2*x + 1/4) - (1 + O(2^5))*x^2 + (1 + 2^2 + 2^3 + O(2^5))*x + (1 + 2^2 + 2^3 + O(2^5)) + (Ring endomorphism of Univariate Polynomial Ring in x over 2-adic Field with capped relative precision 5 + Defn: (1 + O(2^5))*x |--> (2^-1 + O(2^4))*x, + Ring endomorphism of Univariate Polynomial Ring in x over 2-adic Field with capped relative precision 5 + Defn: (1 + O(2^5))*x |--> (2 + O(2^6))*x, + (1 + O(2^5))*x^2 + (1 + 2^2 + 2^3 + O(2^5))*x + (1 + 2^2 + 2^3 + O(2^5))) """ if not G.is_monic(): # this might fail if the base ring is not a field G = G / G.leading_coefficient() - while self(G) < 0: - u = self._base_valuation.uniformizer() - x = G.parent().gen() + + x = G.parent().gen() + u = self._base_valuation.uniformizer() + + factor = 1 + substitution = x + H = G + while self(H) < 0: # this might fail if the base ring is not a field - G = G.parent(G(x/u) * (u ** G.degree())) - assert G.is_monic() - return G + factor *= u + substitution = x/factor + H = G(substitution) * (factor ** G.degree()) + + assert H.is_monic() + return H.parent().hom(substitution, G.parent()), G.parent().hom(x / substitution[1], H.parent()), H def _ge_(self, other): r""" diff --git a/inductive_valuation.py b/inductive_valuation.py index bff96b88c7e..6047e953ed8 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -284,7 +284,7 @@ def monic_integral_model(self, G): r""" Return a monic integral irreducible polynomial which defines the same extension of the base ring of the domain as the irreducible polynomial - ``G``. + ``G`` together with maps between the old and the new polynomial. EXAMPLES:: @@ -292,7 +292,11 @@ def monic_integral_model(self, G): sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: v.monic_integral_model(5*x^2 + 1/2*x + 1/4) - x^2 + 1/5*x + 1/5 + (Ring endomorphism of Univariate Polynomial Ring in x over Rational Field + Defn: x |--> 1/2*x, + Ring endomorphism of Univariate Polynomial Ring in x over Rational Field + Defn: x |--> 2*x, + x^2 + 1/5*x + 1/5) """ @@ -566,8 +570,8 @@ def augmentation(self, phi, mu, check=True): def mac_lane_step(self, G, assume_squarefree=False): r""" - Perform an approximation step towards the squarefree non-constant - polynomial ``G`` with this valuation. + Perform an approximation step towards the squarefree monic non-constant + integral polynomial ``G`` with this valuation. This performs the individual steps that are used in :meth:`mac_lane_approximants`. @@ -596,12 +600,11 @@ def mac_lane_step(self, G, assume_squarefree=False): from sage.misc.misc import verbose verbose("Expanding %s towards %s"%(self, G), caller_name = "mac_lane_step") - if not G.is_monic() or self(G) < 0: - # G must be monic, there is no fundamental reason for this, but the implementation makes this assumption in some places. - # G must be integral, otherwise, e.g., the effective degree is too low - # We try to turn G into a monic integral polynomial that describes the same extension - # This might fail if the constants of our polynomial ring do not form a field - return self.mac_lane_step(self.monic_integral_model(G), assume_squarefree=assume_squarefree) + if not G.is_monic(): + raise ValueError("G must be monic") + + if self(G) < 0: + raise ValueError("G must be integral") if not assume_squarefree and not G.is_squarefree(): raise ValueError("G must be squarefree") diff --git a/limit_valuation.py b/limit_valuation.py index 800cc11c90a..194b958be19 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -59,8 +59,8 @@ sage: v = FunctionFieldValuation(K, 1/x) sage: w = v.extension(L); w Valuation at the infinite place - sage: w._base_valuation._improve_approximation() - sage: w._base_valuation._approximation + sage: w._base_valuation._base_valuation._improve_approximation() + sage: w._base_valuation._base_valuation._approximation [ Gauss valuation induced by Valuation at the infinite place, v(y) = 1/2, v(y^2 - 1/x) = +Infinity ] AUTHORS: @@ -632,301 +632,3 @@ def _ge_(self, other): if other.is_trivial(): return other.is_discrete_valuation() return super(MacLaneLimitValuation, self)._ge_(other) - -class FiniteExtensionFromInfiniteValuation(DiscreteValuation): - r""" - A valuation on a quotient of the form `L=K[x]/(G)` with an irreducible `G` - which is internally backed by a pseudo-valuations on `K[x]` which sends `G` - to infinity. - - INPUT: - - - ``parent`` -- the containing valuation space (usually the space of - discrete valuations on `L`) - - - ``base_valuation`` -- an infinite valuation on `K[x]` which takes `G` to - infinity. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - - sage: v = FunctionFieldValuation(K, 0) - sage: w = v.extension(L); w - (x)-adic valuation - - TESTS:: - - sage: TestSuite(w).run() # long time - - """ - def __init__(self, parent, base_valuation): - r""" - TESTS:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x^2 + 1) - - sage: v = FunctionFieldValuation(K, 0) - sage: w = v.extension(L); w - (x)-adic valuation - sage: isinstance(w, FiniteExtensionFromInfiniteValuation) - True - - """ - DiscreteValuation.__init__(self, parent) - self._base_valuation = base_valuation - - def _eq_(self, other): - r""" - Return whether this valuation is indistinguishable from ``other``. - - EXAMPLES: - - sage: from mac_lane import * # optional: standalone - sage: K = QQ - sage: R. = K[] - sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) - sage: w = v.extension(L) - sage: ww = v.extension(L) - sage: w == ww # indirect doctest - True - - """ - return isinstance(other, FiniteExtensionFromInfiniteValuation) and self._base_valuation == other._base_valuation - - def _repr_(self): - r""" - Return a printable representation of this valuation. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K = QQ - sage: R. = K[] - sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) - sage: v.extension(L) # indirect doctest - 2-adic valuation - - """ - return repr(self._base_valuation) - - def residue_ring(self): - r""" - Return the residue field of this valuation. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K = QQ - sage: R. = K[] - sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) - sage: v.extension(L).residue_ring() - Finite Field of size 2 - - """ - return self._base_valuation.residue_ring() - - def uniformizer(self): - r""" - Return a uniformizing element of this valuation. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K = QQ - sage: R. = K[] - sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) - sage: v.extension(L).uniformizer() - t + 1 - - """ - return self._from_base_domain(self._base_valuation.uniformizer()) - - def _to_base_domain(self, f): - r""" - Return ``f`` as an element in the domain of ``_base_valuation``. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - - sage: v = FunctionFieldValuation(K, 0) - sage: w = v.extensions(L)[0] - sage: w._to_base_domain(y).parent() - Univariate Polynomial Ring in y over Rational function field in x over Rational Field - - """ - return self._base_valuation.domain().coerce(f) - - def _from_base_domain(self, f): - r""" - Return ``f`` as an element in the domain of this valuation. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - - sage: v = FunctionFieldValuation(K, 0) - sage: w = v.extension(L) - sage: w._from_base_domain(w._base_valuation.domain().gen()).parent() - Function field in y defined by y^2 - x - - """ - return self.domain().coerce(f) - - def _call_(self, f): - r""" - Evaluate this valuation at ``f``. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - - sage: v = FunctionFieldValuation(K, 0) - sage: w = v.extension(L) - sage: w(y) # indirect doctest - 1/2 - - """ - return self._base_valuation(self._to_base_domain(f)) - - def reduce(self, f): - r""" - Return the reduction of ``f`` in the :meth:`residue_field` of this valuation. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - (x - 2)) - - sage: v = FunctionFieldValuation(K, 0) - sage: w = v.extension(L) - sage: w.reduce(y) - u1 - - """ - return self._base_valuation.reduce(self._to_base_domain(f)) - - def lift(self, F): - r""" - Lift ``F`` from the :meth;`residue_field` of this valuation into its - domain. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - - sage: v = FunctionFieldValuation(K, 2) - sage: w = v.extension(L) - sage: w.lift(w.residue_field().gen()) - y - - """ - F = self.residue_ring().coerce(F) - F = self._base_valuation.residue_ring().coerce(F) - f = self._base_valuation.lift(F) - return self._from_base_domain(f) - -class FiniteExtensionFromLimitValuation(FiniteExtensionFromInfiniteValuation): - r""" - An extension of a valuation on a finite field extensions `L=K[x]/(G)` which - is induced by an infinite limit valuation on `K[x]`. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 1) - sage: w = v.extensions(L); w - [[ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation, - [ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation] - - TESTS:: - - sage: TestSuite(w[0]).run() # long time - sage: TestSuite(w[1]).run() # long time - - """ - def __init__(self, parent, approximant, G, approximants): - r""" - EXAMPLES: - - Note that this implementation is also used when the underlying limit is - only taken over a finite sequence of valuations:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 2) - sage: w = v.extension(L); w - (x - 2)-adic valuation - sage: isinstance(w, FiniteExtensionFromLimitValuation) - True - - """ - # keep track of all extensions to this field extension so we can print - # this valuation nicely, dropping any unnecessary information - self._approximants = approximants - - from valuation_space import DiscretePseudoValuationSpace - from limit_valuation import LimitValuation - limit = LimitValuation(approximant, G) - FiniteExtensionFromInfiniteValuation.__init__(self, parent, limit) - - def _repr_(self): - """ - Return a printable representation of this valuation. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: pAdicValuation(GaussianIntegers().fraction_field(), 2) # indirect doctest - 2-adic valuation - - """ - if isinstance(self._base_valuation, MacLaneLimitValuation): - # print the minimal information that singles out this valuation from all approximants - assert(self._base_valuation._initial_approximation in self._approximants) - approximants = [v.augmentation_chain()[::-1] for v in self._approximants] - augmentations = self._base_valuation._initial_approximation.augmentation_chain()[::-1] - unique_approximant = None - for l in range(len(augmentations)): - if len([a for a in approximants if a[:l+1] == augmentations[:l+1]]) == 1: - unique_approximant = augmentations[:l+1] - break - assert(unique_approximant is not None) - if unique_approximant[0].is_gauss_valuation(): - unique_approximant[0] = unique_approximant[0].restriction(unique_approximant[0].domain().base_ring()) - if len(unique_approximant) == 1: - return repr(unique_approximant[0]) - from augmented_valuation import AugmentedValuation_base - return "[ %s ]-adic valuation"%(", ".join("v(%r) = %r"%(v._phi, v._mu) if (isinstance(v, AugmentedValuation_base) and v.domain() == self._base_valuation.domain()) else repr(v) for v in unique_approximant)) - return "%s-adic valuation"%(self._base_valuation) - diff --git a/mapped_valuation.py b/mapped_valuation.py new file mode 100644 index 00000000000..b0b450f014b --- /dev/null +++ b/mapped_valuation.py @@ -0,0 +1,593 @@ +# -*- coding: utf-8 -*- +r""" +Valuations which are implemented through a map to another valuation. + +EXAMPLES: + +Extensions of valuations over finite field extensions `L=K[x]/(G)` are realized +through an infinite valuation on `K[x]` which maps `G` to infinity:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L); w + (x)-adic valuation + + sage: w._base_valuation + [ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2 , … ] + +AUTHORS: + +- Julian Rüth (10-11-2016): initial version + +""" +#***************************************************************************** +# Copyright (C) 2016 Julian Rüth +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) +import sys, os +if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: + sys.path.append(os.getcwd()) + sys.path.append(os.path.dirname(os.getcwd())) + +from valuation import DiscreteValuation, DiscretePseudoValuation +from sage.structure.factory import UniqueFactory + +class DomainMappedValuationFactory(UniqueFactory): + r""" + Return a valuation which treats the valuation ``base`` like a valuation on + ``domain`` by sending its arguments through ``to_base`` and ``from_base`` + respectively. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L); w + Valuation at the infinite place + + """ + def create_key(self, domain, base, to_base, from_base): + r""" + Create a key which uniquely identifies a valuation. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L) # indirect doctest + sage: ww = v.extension(L) + sage: w is ww + True + + """ + return domain, base, to_base, from_base + + def create_object(self, version, key): + r""" + Create a valuation from ``key``. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L) # indirect doctest + + """ + domain, base, to_base, from_base = key + from valuation_space import DiscretePseudoValuationSpace + parent = DiscretePseudoValuationSpace(domain) + if base.is_discrete_valuation(): + return parent.__make_element_class__(DomainMappedDiscreteValuation)(parent, base, to_base, from_base) + else: + raise NotImplementedError + +DomainMappedValuation = DomainMappedValuationFactory("DomainMappedValuation") + +class MappedValuation_base(DiscretePseudoValuation): + r""" + A valuation which is implemented through another proxy "base" valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L); w + (x)-adic valuation + + TESTS:: + + sage: TestSuite(w).run() # long time + + """ + def __init__(self, parent, base_valuation): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x^2 + 1) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L); w + (x)-adic valuation + sage: isinstance(w, MappedValuation_base) + True + + """ + DiscretePseudoValuation.__init__(self, parent) + self._base_valuation = base_valuation + + def _repr_(self): + r""" + Return a printable representation of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: v.extension(L) # indirect doctest + 2-adic valuation + + """ + return repr(self._base_valuation) + + def residue_ring(self): + r""" + Return the residue field of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: v.extension(L).residue_ring() + Finite Field of size 2 + + """ + return self._base_valuation.residue_ring() + + def uniformizer(self): + r""" + Return a uniformizing element of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: v.extension(L).uniformizer() + t + 1 + + """ + return self._from_base_domain(self._base_valuation.uniformizer()) + + def _to_base_domain(self, f): + r""" + Return ``f`` as an element in the domain of ``_base_valuation``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extensions(L)[0] + sage: w._to_base_domain(y).parent() + Univariate Polynomial Ring in y over Rational function field in x over Rational Field + + """ + return self._base_valuation.domain().coerce(f) + + def _from_base_domain(self, f): + r""" + Return ``f`` as an element in the domain of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L) + sage: w._from_base_domain(w._base_valuation.domain().gen()).parent() + Function field in y defined by y^2 - x + + """ + return self.domain().coerce(f) + + def _call_(self, f): + r""" + Evaluate this valuation at ``f``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L) + sage: w(y) # indirect doctest + 1/2 + + """ + return self._base_valuation(self._to_base_domain(f)) + + def reduce(self, f): + r""" + Return the reduction of ``f`` in the :meth:`residue_field` of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - (x - 2)) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L) + sage: w.reduce(y) + u1 + + """ + return self._base_valuation.reduce(self._to_base_domain(f)) + + def lift(self, F): + r""" + Lift ``F`` from the :meth;`residue_field` of this valuation into its + domain. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 2) + sage: w = v.extension(L) + sage: w.lift(w.residue_field().gen()) + y + + """ + F = self.residue_ring().coerce(F) + F = self._to_base_residue_ring(F) + f = self._base_valuation.lift(F) + return self._from_base_domain(f) + + def _to_base_residue_ring(self, F): + r""" + Return ``F``, an element of :meth:`residue_ring`, as an element of the + residue ring of the ``_base_valuation``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extensions(L)[0] + sage: w._to_base_residue_ring(1) + 1 + + """ + return self._base_valuation.residue_ring().coerce(F) + + def _from_base_residue_ring(self, F): + r""" + Return ``F``, an element of the residue ring of ``_base_valuation``, as + an element of this valuation's :meth:`residue_ring`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extensions(L)[0] + sage: w._from_base_residue_ring(1) + 1 + + """ + return self.residue_ring().coerce(F) + + def _test_to_from_base_domain(self, **options): + r""" + Check the correctness of :meth:`to_base_domain` and + :meth:`from_base_domain`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extensions(L)[0] + sage: w._test_to_from_base_domain() + + """ + tester = self._tester(**options) + + for x in tester.some_elements(self.domain().some_elements()): + tester.assertEqual(x, self._from_base_domain(self._to_base_domain(x))) + # note that the converse might not be true + + +class DomainMappedValuation_base(MappedValuation_base): + r""" + A valuation which is implemented another proxy "base" valuation with which + is shares the :meth:`residue_ring` but not the domain. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L); w + Valuation at the infinite place + + sage: w(x) + -1 + sage: w(y) + -3/2 + sage: w.uniformizer() + 1/x^2*y + + """ + def __init__(self, parent, base_valuation, to_base, from_base): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L) + sage: isinstance(w, DomainMappedValuation_base) + True + sage: TestSuite(w).run() # long time + + """ + DiscreteValuation.__init__(self, parent) + MappedValuation_base.__init__(self, parent, base_valuation) + + from sage.categories.homset import Hom + if to_base not in Hom(self.domain(), base_valuation.domain()): + raise ValueError("to_base must map from %r to %r"%(self.domain(), base_valuation.domain())) + if from_base not in Hom(base_valuation.domain(), self.domain()): + raise ValueError("from_base must map from %r to %r"%(base_valuation.domain(), self.domain())) + + self._to_base = to_base + self._from_base = from_base + + def _to_base_domain(self, f): + r""" + Return ``f`` as an element in the domain of ``_base_valuation``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L) + sage: w._to_base_domain(y) + x^2*y + + """ + return self._to_base(f) + + def _from_base_domain(self, f): + r""" + Return ``f`` as an element in the domain of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L) + sage: w._from_base_domain(w._to_base_domain(y)) + y + + r""" + return self._from_base(f) + + +class DomainMappedDiscreteValuation(DomainMappedValuation_base, DiscreteValuation): + r""" + A discrete valuation which is implemented through another proxy "base" + valuation. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L) + sage: isinstance(w, DomainMappedDiscreteValuation) + True + """ + pass + + +class FiniteExtensionFromInfiniteValuation(MappedValuation_base, DiscreteValuation): + r""" + A valuation on a quotient of the form `L=K[x]/(G)` with an irreducible `G` + which is internally backed by a pseudo-valuations on `K[x]` which sends `G` + to infinity. + + INPUT: + + - ``parent`` -- the containing valuation space (usually the space of + discrete valuations on `L`) + + - ``base_valuation`` -- an infinite valuation on `K[x]` which takes `G` to + infinity. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L); w + (x)-adic valuation + + TESTS:: + + sage: TestSuite(w).run() # long time + + """ + def _eq_(self, other): + r""" + Return whether this valuation is indistinguishable from ``other``. + + EXAMPLES: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: w = v.extension(L) + sage: ww = v.extension(L) + sage: w == ww # indirect doctest + True + + """ + return isinstance(other, FiniteExtensionFromInfiniteValuation) and self._base_valuation == other._base_valuation + + +class FiniteExtensionFromLimitValuation(FiniteExtensionFromInfiniteValuation): + r""" + An extension of a valuation on a finite field extensions `L=K[x]/(G)` which + is induced by an infinite limit valuation on `K[x]`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: v = FunctionFieldValuation(K, 1) + sage: w = v.extensions(L); w + [[ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation, + [ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation] + + TESTS:: + + sage: TestSuite(w[0]).run() # long time + sage: TestSuite(w[1]).run() # long time + + """ + def __init__(self, parent, approximant, G, approximants): + r""" + EXAMPLES: + + Note that this implementation is also used when the underlying limit is + only taken over a finite sequence of valuations:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: v = FunctionFieldValuation(K, 2) + sage: w = v.extension(L); w + (x - 2)-adic valuation + sage: isinstance(w, FiniteExtensionFromLimitValuation) + True + + """ + # keep track of all extensions to this field extension so we can print + # this valuation nicely, dropping any unnecessary information + self._approximants = approximants + + from valuation_space import DiscretePseudoValuationSpace + from limit_valuation import LimitValuation + limit = LimitValuation(approximant, G) + FiniteExtensionFromInfiniteValuation.__init__(self, parent, limit) + + def _repr_(self): + """ + Return a printable representation of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(GaussianIntegers().fraction_field(), 2) # indirect doctest + 2-adic valuation + + """ + from limit_valuation import MacLaneLimitValuation + if isinstance(self._base_valuation, MacLaneLimitValuation): + # print the minimal information that singles out this valuation from all approximants + assert(self._base_valuation._initial_approximation in self._approximants) + approximants = [v.augmentation_chain()[::-1] for v in self._approximants] + augmentations = self._base_valuation._initial_approximation.augmentation_chain()[::-1] + unique_approximant = None + for l in range(len(augmentations)): + if len([a for a in approximants if a[:l+1] == augmentations[:l+1]]) == 1: + unique_approximant = augmentations[:l+1] + break + assert(unique_approximant is not None) + if unique_approximant[0].is_gauss_valuation(): + unique_approximant[0] = unique_approximant[0].restriction(unique_approximant[0].domain().base_ring()) + if len(unique_approximant) == 1: + return repr(unique_approximant[0]) + from augmented_valuation import AugmentedValuation_base + return "[ %s ]-adic valuation"%(", ".join("v(%r) = %r"%(v._phi, v._mu) if (isinstance(v, AugmentedValuation_base) and v.domain() == self._base_valuation.domain()) else repr(v) for v in unique_approximant)) + return "%s-adic valuation"%(self._base_valuation) + diff --git a/padic_valuation.py b/padic_valuation.py index c46f4051d17..d45ba4d32a5 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -23,7 +23,7 @@ sys.path.append(os.path.dirname(os.getcwd())) from valuation import DiscreteValuation -from limit_valuation import FiniteExtensionFromLimitValuation +from mapped_valuation import FiniteExtensionFromLimitValuation from sage.structure.factory import UniqueFactory from sage.misc.cachefunc import cached_method from sage.misc.fast_methods import WithEqualityById @@ -677,7 +677,6 @@ def extensions(self, ring): if is_NumberField(ring.fraction_field()): if ring.base().fraction_field() is self.domain().fraction_field(): from valuation_space import DiscretePseudoValuationSpace - from limit_valuation import FiniteExtensionFromInfiniteValuation parent = DiscretePseudoValuationSpace(ring) approximants = self.mac_lane_approximants(ring.fraction_field().relative_polynomial().change_ring(self.domain())) return [pAdicValuation(ring, approximant) for approximant in approximants] diff --git a/valuation.py b/valuation.py index 0ba746922ab..ac9c49f3653 100644 --- a/valuation.py +++ b/valuation.py @@ -394,8 +394,8 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): INPUT: - - ``G`` -- a square-free polynomial defined over a univariate - polynomial ring over the :meth:`domain` of this valuation. + - ``G`` -- a square-free monic integral polynomial defined over a + univariate polynomial ring over the :meth:`domain` of this valuation. - ``precision_cap`` -- a number, infinity, or ``None`` (default: ``None``); the approximants are always determined such that they are in @@ -708,11 +708,10 @@ def mac_lane_approximant(self, G, valuation, approximants = None): # on domain that extends its restriction to the base field. from sage.rings.all import infinity if valuation(G) != infinity: - G_integral = valuation.monic_integral_model(G) v = valuation while not v.is_gauss_valuation(): - if v(G_integral) <= v._base_valuation(G_integral): - raise ValueError("The valuation %r is not an approximant for a valuation which extends %r with respect to %r since the valuation of %r does not increase in every step"%(valuation, self, G, G_integral)) + if v(G) <= v._base_valuation(G): + raise ValueError("The valuation %r is not an approximant for a valuation which extends %r with respect to %r since the valuation of %r does not increase in every step"%(valuation, self, G, G)) v = v._base_valuation if approximants is None: From 54abc7a6a52892ff2a1184e791bcdd2fd3e2c3e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 10 Nov 2016 15:55:41 -0500 Subject: [PATCH 109/740] fix date format --- TODO | 2 -- augmented_valuation.py | 2 +- developing_valuation.py | 2 +- gauss_valuation.py | 2 +- inductive_valuation.py | 2 +- mapped_valuation.py | 2 +- 6 files changed, 5 insertions(+), 7 deletions(-) diff --git a/TODO b/TODO index 6d545ba585a..0394e694062 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,3 @@ * Check every methods about limitations in the ring case (and correction of the docstring) -* Fix date formatting 2016-11-01 - * Properly call super classes diff --git a/augmented_valuation.py b/augmented_valuation.py index 25d702e3be8..a9539aac48e 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -143,7 +143,7 @@ AUTHORS: -- Julian Rüth (15-04-2013): initial version +- Julian Rüth (2013-04-15): initial version """ #***************************************************************************** diff --git a/developing_valuation.py b/developing_valuation.py index b99e25f88e4..9f190ed180c 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -7,7 +7,7 @@ AUTHORS: -- Julian Rüth (15-04-2013): initial version +- Julian Rüth (2013-04-15): initial version """ #***************************************************************************** diff --git a/gauss_valuation.py b/gauss_valuation.py index 87974d1595f..df6b8298aa9 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -8,7 +8,7 @@ AUTHORS: -- Julian Rüth (15-04-2013): initial version +- Julian Rüth (2013-04-15): initial version EXAMPLES:: diff --git a/inductive_valuation.py b/inductive_valuation.py index 6047e953ed8..888ff872ab3 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -7,7 +7,7 @@ AUTHORS: -- Julian Rüth (01-11-2016): initial version +- Julian Rüth (2016-11-01): initial version REFERENCES: diff --git a/mapped_valuation.py b/mapped_valuation.py index b0b450f014b..1e126e268c9 100644 --- a/mapped_valuation.py +++ b/mapped_valuation.py @@ -21,7 +21,7 @@ AUTHORS: -- Julian Rüth (10-11-2016): initial version +- Julian Rüth (2016-11-10): initial version """ #***************************************************************************** From a197dcb6cd7cd1888a29109a7875bda5142c03b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 10 Nov 2016 16:03:06 -0500 Subject: [PATCH 110/740] add missing superclass init calls --- TODO | 2 - __init__.py | 4 +- augmented_valuation.py | 113 ++++++++++++++++++++++++++++++------ developing_valuation.py | 3 +- function_field_valuation.py | 46 ++++++++++++++- inductive_valuation.py | 65 +++++++++++++++++++-- limit_valuation.py | 3 + mapped_valuation.py | 2 +- padic_valuation.py | 4 +- trivial_valuation.py | 2 + valuation_space.py | 11 ++-- value_group.py | 2 + 12 files changed, 220 insertions(+), 37 deletions(-) diff --git a/TODO b/TODO index 0394e694062..0d54ebcbc95 100644 --- a/TODO +++ b/TODO @@ -1,3 +1 @@ * Check every methods about limitations in the ring case (and correction of the docstring) - -* Properly call super classes diff --git a/__init__.py b/__init__.py index 021ac09513a..314467bf22c 100644 --- a/__init__.py +++ b/__init__.py @@ -37,7 +37,7 @@ # local file and the instances that come from the mac_lane import define # different types) from trivial_valuation import TrivialDiscreteValuation, TrivialDiscretePseudoValuation -from function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldFromLimitValuation +from function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldFromLimitValuation, InfiniteRationalFunctionFieldValuation, FiniteRationalFunctionFieldValuation, NonClassicalRationalFunctionFieldValuation from limit_valuation import LimitValuation, MacLaneLimitValuation, LimitValuation_generic from mapped_valuation import MappedValuation_base, FiniteExtensionFromLimitValuation, FiniteExtensionFromInfiniteValuation, DomainMappedValuation_base, DomainMappedValuation, DomainMappedDiscreteValuation from augmented_valuation import FiniteAugmentedValuation, InfiniteAugmentedValuation @@ -46,7 +46,7 @@ from padic_valuation import pAdicValuation_base, pAdicValuation_int, pAdicValuation_padic, pAdicFromLimitValuation from developing_valuation import DevelopingValuation from augmented_valuation import AugmentedValuation_base, FinalAugmentedValuation, NonFinalAugmentedValuation, FinalFiniteAugmentedValuation, NonFinalFiniteAugmentedValuation -from inductive_valuation import FiniteInductiveValuation, FinalInductiveValuation, InfiniteInductiveValuation +from inductive_valuation import FiniteInductiveValuation, FinalInductiveValuation, InfiniteInductiveValuation, NonFinalInductiveValuation # ================= # MONKEY PATCH SAGE diff --git a/augmented_valuation.py b/augmented_valuation.py index a9539aac48e..bb58fca852e 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -835,16 +835,29 @@ class FinalAugmentedValuation(AugmentedValuation_base, FinalInductiveValuation): An augmented valuation which can not be augmented anymore, either because it augments a trivial valuation or because it is infinite. - TESTS:: + EXAMPLES:: sage: from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: w = v.augmentation(x, 1) - sage: isinstance(w, FinalAugmentedValuation) - True """ + def __init__(self, parent, v, phi, mu): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: w = v.augmentation(x, 1) + sage: isinstance(w, FinalAugmentedValuation) + True + + """ + AugmentedValuation_base.__init__(self, parent, v, phi, mu) + FinalInductiveValuation.__init__(self, parent, phi) + @cached_method def residue_ring(self): r""" @@ -1077,16 +1090,29 @@ class NonFinalAugmentedValuation(AugmentedValuation_base, NonFinalInductiveValua r""" An augmented valuation which can be augmented further. - TESTS:: + EXAMPLES:: sage: from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) - sage: isinstance(w, NonFinalAugmentedValuation) - True """ + def __init__(self, parent, v, phi, mu): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, 1) + sage: isinstance(w, NonFinalAugmentedValuation) + True + + """ + AugmentedValuation_base.__init__(self, parent, v, phi, mu) + NonFinalInductiveValuation.__init__(self, parent, phi) + @cached_method def residue_ring(self): r""" @@ -1478,17 +1504,31 @@ class FiniteAugmentedValuation(AugmentedValuation_base, FiniteInductiveValuation discrete, or equivalently an augmented valuation which assigns to its last key polynomial a finite valuation. - TESTS:: + EXAMPLES:: sage: from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) sage: w = v.augmentation(x^2 + x + u, 1/2) - sage: isinstance(w, FiniteAugmentedValuation) - True """ + def __init__(self, parent, v, phi, mu): + r""" + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4, 5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, 1/2) + sage: isinstance(w, FiniteAugmentedValuation) + True + + """ + AugmentedValuation_base.__init__(self, parent, v, phi, mu) + FiniteInductiveValuation.__init__(self, parent, phi) + @cached_method def value_group(self): """ @@ -1555,16 +1595,28 @@ class FinalFiniteAugmentedValuation(FiniteAugmentedValuation, FinalAugmentedValu valuation to its last key polynomial, but which can not be further augmented. - TESTS:: + EXAMPLES:: sage: from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: w = v.augmentation(x, 1) - sage: isinstance(w, FinalFiniteAugmentedValuation) - True """ + def __init__(self, parent, v, phi, mu): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: w = v.augmentation(x, 1) + sage: isinstance(w, FinalFiniteAugmentedValuation) + True + + """ + FiniteAugmentedValuation.__init__(self, parent, v, phi, mu) + FinalAugmentedValuation.__init__(self, parent, v, phi, mu) class NonFinalFiniteAugmentedValuation(FiniteAugmentedValuation, NonFinalAugmentedValuation): @@ -1572,16 +1624,28 @@ class NonFinalFiniteAugmentedValuation(FiniteAugmentedValuation, NonFinalAugment An augmented valuation which is discrete, i.e., which assigns a finite valuation to its last key polynomial, and which can be augmented furter. - TESTS:: + EXAMPLES:: sage: from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x, 1) - sage: isinstance(w, NonFinalFiniteAugmentedValuation) - True """ + def __init__(self, parent, v, phi, mu): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x, 1) + sage: isinstance(w, NonFinalFiniteAugmentedValuation) + True + + """ + FiniteAugmentedValuation.__init__(self, parent, v, phi, mu) + NonFinalAugmentedValuation.__init__(self, parent, v, phi, mu) class InfiniteAugmentedValuation(FinalAugmentedValuation, InfiniteInductiveValuation): @@ -1590,16 +1654,29 @@ class InfiniteAugmentedValuation(FinalAugmentedValuation, InfiniteInductiveValua infinity to its last key polynomial (and which can therefore not be augmented further.) - TESTS:: + EXAMPLES:: sage: from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x, infinity) - sage: isinstance(w, InfiniteAugmentedValuation) - True """ + def __init__(self, parent, v, phi, mu): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x, infinity) + sage: isinstance(w, InfiniteAugmentedValuation) + True + + """ + FinalAugmentedValuation.__init__(self, parent, v, phi, mu) + InfiniteInductiveValuation.__init__(self, parent, phi) + @cached_method def value_group(self): """ diff --git a/developing_valuation.py b/developing_valuation.py index 9f190ed180c..94650038050 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -57,6 +57,8 @@ def __init__(self, parent, phi): True """ + DiscretePseudoValuation.__init__(self, parent) + domain = parent.domain() from sage.rings.polynomial.polynomial_ring import is_PolynomialRing if not is_PolynomialRing(domain) or not domain.ngens() == 1: @@ -66,7 +68,6 @@ def __init__(self, parent, phi): if phi.is_constant() or not phi.is_monic(): raise ValueError("phi must be a monic non-constant polynomial but %r is not"%(phi,)) - DiscretePseudoValuation.__init__(self, parent) self._phi = phi def phi(self): diff --git a/function_field_valuation.py b/function_field_valuation.py index 0f6d44207f4..d85522e4230 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -587,12 +587,13 @@ def __init__(self, parent, base_valuation): True """ + FunctionFieldValuation_base.__init__(self, parent) + domain = parent.domain() if base_valuation.domain() is not domain._ring: raise ValueError("base valuation must be defined on %r but %r is defined on %r"%(domain._ring, base_valuation, base_valuation.domain())) self._base_valuation = base_valuation - RationalFunctionFieldValuation_base.__init__(self, parent) def uniformizer(self): r""" @@ -798,6 +799,20 @@ class InfiniteRationalFunctionFieldValuation(RationalFunctionFieldValuation_base sage: TestSuite(v).run() # long time """ + def __init__(self, parent): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, 1/x) # indirect doctest + sage: isinstance(v, InfiniteRationalFunctionFieldValuation) + True + + """ + RationalFunctionFieldValuation_base.__init__(self, parent) + ClassicalFunctionFieldValuation_base.__init__(self, parent) + def _call_(self, f): r""" Evaluate this valuation at the rational function ``f``. @@ -935,6 +950,21 @@ class FiniteRationalFunctionFieldValuation(ClassicalFunctionFieldValuation_base, (x^6 + 2*t)-adic valuation """ + def __init__(self, parent, base_valuation): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, x + 1) + sage: isinstance(v, FiniteRationalFunctionFieldValuation) + True + + """ + ClassicalFunctionFieldValuation_base.__init__(self, parent) + InducedFunctionFieldValuation_base.__init__(self, parent, base_valuation) + RationalFunctionFieldValuation_base.__init__(self, parent) + class NonClassicalRationalFunctionFieldValuation(InducedFunctionFieldValuation_base, RationalFunctionFieldValuation_base): r""" @@ -950,6 +980,20 @@ class NonClassicalRationalFunctionFieldValuation(InducedFunctionFieldValuation_b 2-adic valuation """ + def __init__(self, parent, base_valuation): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = GaussValuation(QQ['x'], pAdicValuation(QQ, 2)) + sage: w = FunctionFieldValuation(K, v) + sage: isinstance(w, NonClassicalRationalFunctionFieldValuation) + True + + """ + InducedFunctionFieldValuation_base.__init__(self, parent, base_valuation) + RationalFunctionFieldValuation_base.__init__(self, parent) class FunctionFieldFromLimitValuation(FiniteExtensionFromLimitValuation): r""" diff --git a/inductive_valuation.py b/inductive_valuation.py index 888ff872ab3..88aa16a733a 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -502,18 +502,59 @@ class FiniteInductiveValuation(InductiveValuation, DiscreteValuation): :class:`GaussValuation` which is a discrete valuation, i.e., the last key polynomial has finite valuation. - TESTS:: + EXAMPLES:: sage: from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) - sage: isinstance(v, FiniteInductiveValuation) - True """ + def __init__(self, parent, phi): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: isinstance(v, FiniteInductiveValuation) + True + + """ + InductiveValuation.__init__(self, parent, phi) + DiscreteValuation.__init__(self, parent) class NonFinalInductiveValuation(FiniteInductiveValuation, DiscreteValuation): + r""" + Abstract base class for iterated :class:`AugmentedValuation` on top of a + :class:`GaussValuation` which can be extended further through + :meth:`augmentation`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v = v.augmentation(x^2 + x + u, 1) + + """ + def __init__(self, parent, phi): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v = v.augmentation(x^2 + x + u, 1) + sage: isinstance(v, NonFinalInductiveValuation) + True + + """ + FiniteInductiveValuation.__init__(self, parent, phi) + DiscreteValuation.__init__(self, parent) + def augmentation(self, phi, mu, check=True): r""" Return the inductive valuation which extends this valuation by mapping @@ -1251,16 +1292,28 @@ class InfiniteInductiveValuation(FinalInductiveValuation, InfiniteDiscretePseudo Abstract base class for an inductive valuation which is not discrete, i.e., which assigns infinite valuation to its last key polynomial. - TESTS:: + EXAMPLES:: sage: from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, infinity) - sage: isinstance(w, InfiniteInductiveValuation) - True """ + def __init__(self, parent, base_valuation): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, infinity) + sage: isinstance(w, InfiniteInductiveValuation) + True + + """ + FinalInductiveValuation.__init__(self, parent, base_valuation) + InfiniteDiscretePseudoValuation.__init__(self, parent) def _lift_to_maximal_precision(c): diff --git a/limit_valuation.py b/limit_valuation.py index 194b958be19..92e17abd4bf 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -199,6 +199,7 @@ def __init__(self, parent, approximation): """ DiscretePseudoValuation.__init__(self, parent) + self._initial_approximation = approximation self._approximation = approximation @@ -387,6 +388,8 @@ def __init__(self, parent, approximation, G): """ LimitValuation_generic.__init__(self, parent, approximation) + InfiniteDiscretePseudoValuation.__init__(self, parent) + self._G = G def lift(self, F): diff --git a/mapped_valuation.py b/mapped_valuation.py index 1e126e268c9..ab276db6304 100644 --- a/mapped_valuation.py +++ b/mapped_valuation.py @@ -139,6 +139,7 @@ def __init__(self, parent, base_valuation): """ DiscretePseudoValuation.__init__(self, parent) + self._base_valuation = base_valuation def _repr_(self): @@ -393,7 +394,6 @@ def __init__(self, parent, base_valuation, to_base, from_base): sage: TestSuite(w).run() # long time """ - DiscreteValuation.__init__(self, parent) MappedValuation_base.__init__(self, parent, base_valuation) from sage.categories.homset import Hom diff --git a/padic_valuation.py b/padic_valuation.py index d45ba4d32a5..0045c4564b1 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -416,9 +416,9 @@ def __init__(self, parent, p): True """ - from sage.rings.all import ZZ - DiscreteValuation.__init__(self, parent) + + from sage.rings.all import ZZ self._p = ZZ(p) def p(self): diff --git a/trivial_valuation.py b/trivial_valuation.py index 57cc736cfb4..6ffc7c89f28 100644 --- a/trivial_valuation.py +++ b/trivial_valuation.py @@ -180,6 +180,7 @@ def __init__(self, parent): """ TrivialDiscretePseudoValuation_base.__init__(self, parent) + InfiniteDiscretePseudoValuation.__init__(self, parent) def _call_(self, x): r""" @@ -315,6 +316,7 @@ def __init__(self, parent): """ TrivialDiscretePseudoValuation_base.__init__(self, parent) + DiscreteValuation.__init__(self, parent) def _call_(self, x): r""" diff --git a/valuation_space.py b/valuation_space.py index 0180860268f..3b578c52574 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -84,16 +84,19 @@ def __init__(self, domain): True """ - from sage.categories.domains import Domains - if domain not in Domains(): - raise ValueError("domain must be an integral domain") - from value_group import DiscreteValuationCodomain # A valuation is a map from an additive semigroup to an additive semigroup, however, it # does not preserve that structure. It is therefore only a morphism in the category of sets. from sage.categories.all import Sets + + UniqueRepresentation.__init__(self) Homset.__init__(self, domain, DiscreteValuationCodomain(), category = Sets()) + from sage.categories.domains import Domains + if domain not in Domains(): + raise ValueError("domain must be an integral domain") + + @lazy_attribute def _abstract_element_class(self): r""" diff --git a/value_group.py b/value_group.py index 6d11b3626f2..4936e262d4b 100644 --- a/value_group.py +++ b/value_group.py @@ -62,6 +62,7 @@ def __init__(self): """ from sage.sets.finite_enumerated_set import FiniteEnumeratedSet from sage.categories.additive_monoids import AdditiveMonoids + UniqueRepresentation.__init__(self) Parent.__init__(self, facade=(QQ, FiniteEnumeratedSet([infinity])), category=AdditiveMonoids()) def _element_constructor_(self, x): @@ -174,6 +175,7 @@ def __init__(self, generator): # We can not set the facade to DiscreteValuationCodomain since there # are some issues with iterated facades currently + UniqueRepresentation.__init__(self) Parent.__init__(self, facade=QQ, category=Modules(ZZ)) def _element_constructor_(self, x): From 573566ef3f05c85b0d85b493c3261fe72ee046ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 10 Nov 2016 23:58:52 -0500 Subject: [PATCH 111/740] Add scaled valuations --- __init__.py | 4 + augmented_valuation.py | 19 ++++ gauss_valuation.py | 18 ++++ padic_valuation.py | 6 +- scaled_valuation.py | 240 +++++++++++++++++++++++++++++++++++++++++ valuation_space.py | 173 ++++++++++++++++++++++++++++- 6 files changed, 455 insertions(+), 5 deletions(-) create mode 100644 scaled_valuation.py diff --git a/__init__.py b/__init__.py index 314467bf22c..c812dc631a6 100644 --- a/__init__.py +++ b/__init__.py @@ -32,6 +32,8 @@ from function_field_valuation import FunctionFieldValuation import augmented_valuation from augmented_valuation import AugmentedValuation +import scaled_valuation +from scaled_valuation import ScaledValuation # fix unpickling and type checks of classes (otherwise, the instances of the # local file and the instances that come from the mac_lane import define @@ -47,6 +49,7 @@ from developing_valuation import DevelopingValuation from augmented_valuation import AugmentedValuation_base, FinalAugmentedValuation, NonFinalAugmentedValuation, FinalFiniteAugmentedValuation, NonFinalFiniteAugmentedValuation from inductive_valuation import FiniteInductiveValuation, FinalInductiveValuation, InfiniteInductiveValuation, NonFinalInductiveValuation +from scaled_valuation import ScaledValuation_generic # ================= # MONKEY PATCH SAGE @@ -543,3 +546,4 @@ def some_elements(self): register_factory_unpickle("AugmentedValuation", AugmentedValuation) register_factory_unpickle("LimitValuation", LimitValuation) register_factory_unpickle("DomainMappedValuation", DomainMappedValuation) +register_factory_unpickle("ScaledValuation", ScaledValuation) diff --git a/augmented_valuation.py b/augmented_valuation.py index bb58fca852e..738451bf747 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -830,6 +830,25 @@ def is_trivial(self): # the base ring is not a field. return False + def scale(self, scalar): + r""" + Return this valuation scaled by ``scalar``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, 1) + sage: 3*w # indirect doctest + [ Gauss valuation induced by 3 * 2-adic valuation, v(x^2 + x + 1) = 3 ] + + """ + from sage.rings.all import QQ + if scalar in QQ and scalar != 0: + return self._base_valuation.scale(scalar).augmentation(self.phi(), scalar*self._mu) + return super(AugmentedValuation_base, self).scale(scalar) + class FinalAugmentedValuation(AugmentedValuation_base, FinalInductiveValuation): r""" An augmented valuation which can not be augmented anymore, either because diff --git a/gauss_valuation.py b/gauss_valuation.py index df6b8298aa9..e31ed1ee0d1 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -692,3 +692,21 @@ def _ge_(self, other): if other.is_trivial(): return other.is_discrete_valuation() return super(GaussValuation_generic, self)._ge_(other) + + def scale(self, scalar): + r""" + Return this valuation scaled by ``scalar``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: 3*v # indirect doctest + Gauss valuation induced by 3 * 2-adic valuation + + """ + from sage.rings.all import QQ + if scalar in QQ and scalar != 0: + return GaussValuation(self.domain(), self._base_valuation.scale(scalar)) + return super(GaussValuation_generic, self).scale(scalar) diff --git a/padic_valuation.py b/padic_valuation.py index 0045c4564b1..d3214a6bd4f 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -404,7 +404,7 @@ class pAdicValuation_base(DiscreteValuation): sage: TestSuite(pAdicValuation(ZZ, 3)).run() # long time sage: TestSuite(pAdicValuation(QQ, 5)).run() # long time - sage: TestSuite(pAdicValuation(Zp(5), 5)).run() + sage: TestSuite(pAdicValuation(Zp(5), 5)).run() # long time """ def __init__(self, parent, p): @@ -717,7 +717,7 @@ class pAdicValuation_padic(pAdicValuation_base): TESTS:: - sage: TestSuite(v).run() # optional: integrated + sage: TestSuite(v).run() # optional: integrated, long time """ def __init__(self, parent): @@ -922,7 +922,7 @@ class pAdicValuation_int(pAdicValuation_base): TESTS:: - sage: TestSuite(v).run() + sage: TestSuite(v).run() # long time """ def _repr_(self): diff --git a/scaled_valuation.py b/scaled_valuation.py new file mode 100644 index 00000000000..483996a6848 --- /dev/null +++ b/scaled_valuation.py @@ -0,0 +1,240 @@ +# -*- coding: utf-8 -*- +r""" +Valuations which are scaled versions of another valuation. + +EXAMPLES: + + sage: from mac_lane import * # optional: standalone + sage: 3*pAdicValuation(ZZ, 3) + 3 * 3-adic valuation + +AUTHORS: + +- Julian Rüth (2016-11-10): initial version + +""" +#***************************************************************************** +# Copyright (C) 2016 Julian Rüth +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) +import sys, os +if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: + sys.path.append(os.getcwd()) + sys.path.append(os.path.dirname(os.getcwd())) + +from sage.structure.factory import UniqueFactory + +from valuation import DiscreteValuation + +class ScaledValuationFactory(UniqueFactory): + r""" + Return a valuation which scales the valuation ``base`` by the factor ``s``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: 3*pAdicValuation(ZZ, 2) # indirect doctest + 3 * 2-adic valuation + + """ + def create_key(self, base, s): + r""" + Create a key which uniquely identifies a valuation. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: 3*pAdicValuation(ZZ, 2) is 2*(3/2*pAdicValuation(ZZ, 2)) # indirect doctest + True + + """ + from sage.rings.all import infinity, QQ + if s == infinity or s not in QQ or s <= 0: + # for these values we can not return a TrivialValuation() in + # create_object() because that would override that instance's + # _factory_data and lead to pickling errors + raise ValueError("s must be a positive rational") + if base.is_trivial(): + # for the same reason we can not accept trivial valuations here + raise ValueError("base must not be trivial") + s = QQ.coerce(s) + if s == 1: + # we would override the _factory_data of base if we just returned + # it in create_object() so we just refuse to do so + raise ValueError("s must not be 1") + + if isinstance(base, ScaledValuation_generic): + return self.create_key(base._base_valuation, s*base._scale) + + return base, s + + def create_object(self, version, key): + r""" + Create a valuation from ``key``. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: 3*pAdicValuation(ZZ, 2) # indirect doctest + 3 * 2-adic valuation + + """ + base, s = key + + assert not isinstance(base, ScaledValuation_generic) + + from valuation_space import DiscretePseudoValuationSpace + parent = DiscretePseudoValuationSpace(base.domain()) + return parent.__make_element_class__(ScaledValuation_generic)(parent, base, s) + + +ScaledValuation = ScaledValuationFactory("ScaledValuation") + +class ScaledValuation_generic(DiscreteValuation): + r""" + A valuation which scales another ``base_valuation`` by a finite positive factor ``s``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = 3*pAdicValuation(ZZ, 3); v + 3 * 3-adic valuation + + TESTS:: + + sage: TestSuite(v).run() # long time + + """ + def __init__(self, parent, base_valuation, s): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = 3*pAdicValuation(ZZ, 2) + sage: isinstance(v, ScaledValuation_generic) + True + + """ + DiscreteValuation.__init__(self, parent) + + self._base_valuation = base_valuation + self._scale = s + + def _repr_(self): + r""" + Return a printable representation of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: 3*pAdicValuation(ZZ, 2) # indirect doctest + 3 * 2-adic valuation + + """ + return "%r * %r"%(self._scale, self._base_valuation) + + def residue_ring(self): + r""" + Return the residue field of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = 3*pAdicValuation(ZZ, 2) + sage: v.residue_ring() + Finite Field of size 2 + + """ + return self._base_valuation.residue_ring() + + def uniformizer(self): + r""" + Return a uniformizing element of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = 3*pAdicValuation(ZZ, 2) + sage: v.uniformizer() + 2 + + """ + return self._base_valuation.uniformizer() + + def _call_(self, f): + r""" + Evaluate this valuation at ``f``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = 3*pAdicValuation(ZZ, 2) + sage: v(2) + 3 + + """ + return self._scale * self._base_valuation(f) + + def reduce(self, f): + r""" + Return the reduction of ``f`` in the :meth:`residue_field` of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = 3*pAdicValuation(ZZ, 2) + sage: v.reduce(1) + 1 + + """ + return self._base_valuation.reduce(f) + + def lift(self, F): + r""" + Lift ``F`` from the :meth;`residue_field` of this valuation into its + domain. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = 3*pAdicValuation(ZZ, 2) + sage: v.lift(1) + 1 + + """ + return self._base_valuation.lift(F) + + def extensions(self, ring): + r""" + Return the extensions of this valuation to ``ring``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = 3*pAdicValuation(ZZ, 5) + sage: v.extensions(GaussianIntegers().fraction_field()) + [3 * [ 5-adic valuation, v(x + 2) = 1 ]-adic valuation, + 3 * [ 5-adic valuation, v(x + 3) = 1 ]-adic valuation] + + """ + return [ScaledValuation(w, self._scale) for w in self._base_valuation.extensions(ring)] + + def restriction(self, ring): + r""" + Return the restriction of this valuation to ``ring``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = 3*pAdicValuation(QQ, 5) + sage: v.restriction(ZZ) + 3 * 5-adic valuation + + """ + return ScaledValuation(self._base_valuation.restriction(ring), self._scale) diff --git a/valuation_space.py b/valuation_space.py index 3b578c52574..1e3af0cc747 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -72,7 +72,7 @@ class DiscretePseudoValuationSpace(UniqueRepresentation, Homset): TESTS:: - sage: TestSuite(H).run() + sage: TestSuite(H).run() # long time """ def __init__(self, domain): @@ -96,7 +96,6 @@ def __init__(self, domain): if domain not in Domains(): raise ValueError("domain must be an integral domain") - @lazy_attribute def _abstract_element_class(self): r""" @@ -121,6 +120,26 @@ def _abstract_element_class(self): from sage.structure.dynamic_class import dynamic_class return dynamic_class(class_name, (super(DiscretePseudoValuationSpace,self)._abstract_element_class, self.__class__.ElementMethods)) + def _get_action_(self, S, op, self_on_left): + r""" + Return the ``op`` action of ``S`` on elements in this space. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 2) + sage: from operator import mul + sage: v.parent().get_action(ZZ, mul) # indirect doctest + + """ + from operator import mul, div + from sage.rings.all import QQ, InfinityRing, ZZ + if op == mul and not self_on_left and (S is InfinityRing or S is QQ or S is ZZ): + return ScaleAction(S, self) + if op == div and self_on_left and (S is InfinityRing or S is QQ or S is ZZ): + return InverseScaleAction(self, S) + return None + def _an_element_(self): r""" Return a trivial valuation in this space. @@ -620,6 +639,102 @@ def change_domain(self, ring): return self.restriction(ring) raise NotImplementedError("changing %r from %r to %r not implemented"%(self, self.domain(), ring)) + def scale(self, scalar): + r""" + Return this valuation scaled by ``scalar``. + + INPUT: + + - ``scalar`` -- a non-negative rational number or infinity + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 3) + sage: w = v.scale(3) + sage: w(3) + 3 + + Scaling can also be done through multiplication with a scalar:: + + sage: w/3 == v + True + + Multiplication by zero produces the trivial discrete valuation:: + + sage: w = 0*v + sage: w(3) + 0 + sage: w(0) + +Infinity + + Multiplication by infinity produces the trivial discrete + pseudo-valuation:: + + sage: w = infinity*v + sage: w(3) + +Infinity + sage: w(0) + +Infinity + + """ + from sage.rings.all import infinity + if scalar == infinity: + from trivial_valuation import TrivialPseudoValuation + return TrivialPseudoValuation(self.domain()) + if scalar == 0: + from trivial_valuation import TrivialValuation + return TrivialValuation(self.domain()) + if scalar == 1: + return self + if scalar < 0: + raise ValueError("scalar must be non-negative") + if self.is_trivial(): + return self + + from scaled_valuation import ScaledValuation_generic + if isinstance(self, ScaledValuation_generic): + return self._base_valuation.scale(scalar * self._scale) + + from scaled_valuation import ScaledValuation + return ScaledValuation(self, scalar) + + def _test_scale(self, **options): + r""" + Check that :meth:`scale` works correctly. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 3) + sage: v._test_scale() + + """ + tester = self._tester(**options) + + from sage.rings.all import infinity, QQ + from trivial_valuation import TrivialValuation, TrivialPseudoValuation + + tester.assertEqual(QQ(0)*self, TrivialValuation(self.domain())) + tester.assertEqual(infinity*self, TrivialPseudoValuation(self.domain())) + + for s in tester.some_elements(QQ.some_elements()): + if s < 0: + with tester.assertRaises(ValueError): + s * self + continue + if s == 0: + continue + + scaled = s * self + + tester.assertEqual(self.is_trivial(), scaled.is_trivial()) + if not self.is_trivial(): + tester.assertEqual(self.uniformizer(), scaled.uniformizer()) + tester.assertEqual(scaled(self.uniformizer()), s * self(self.uniformizer())) + unscaled = scaled / s + tester.assertEqual(self, unscaled) + def _test_add(self, **options): r""" Check that the (strict) triangle equality is satisfied for the @@ -1018,3 +1133,57 @@ def _test_le(self, **options): tester.assertLessEqual(TrivialValuation(self.domain()), self) tester.assertGreaterEqual(TrivialPseudoValuation(self.domain()), self) +from sage.categories.action import Action +class ScaleAction(Action): + r""" + Action of integers, rationals and the infinity ring on valuations by + scaling it. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: from operator import mul + sage: v.parent().get_action(IntegerRing, mul, self_on_left=False) + + """ + def _call_(self, s, v): + r""" + Let ``s`` act on ``v``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: 3*v # indirect doctest + 3 * 5-adic valuation + + """ + return v.scale(s) + +class InverseScaleAction(Action): + r""" + Action of integers, rationals and the infinity ring on valuations by + scaling it (with the inverse of the scalar.) + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: from operator import div + sage: v.parent().get_action(IntegerRing, div, self_on_left=True) + + """ + def _call_(self, v, s): + r""" + Let ``s`` act on ``v`` (by division.) + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: v/3 # indirect doctest + 1/3 * 5-adic valuation + + """ + return v.scale(1/s) From 268e93b1705ed9b66a5c438ff04d4b3586acc4e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 11 Nov 2016 00:23:46 -0500 Subject: [PATCH 112/740] Implement scaling of function field valuations --- augmented_valuation.py | 2 +- function_field_valuation.py | 20 ++++++++++++++++++++ gauss_valuation.py | 2 +- mapped_valuation.py | 21 +++++++++++++++++++++ 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 738451bf747..c6a117a93e7 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -845,7 +845,7 @@ def scale(self, scalar): """ from sage.rings.all import QQ - if scalar in QQ and scalar != 0: + if scalar in QQ and scalar > 0 and scalar != 1: return self._base_valuation.scale(scalar).augmentation(self.phi(), scalar*self._mu) return super(AugmentedValuation_base, self).scale(scalar) diff --git a/function_field_valuation.py b/function_field_valuation.py index d85522e4230..f6a2ddea53c 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -1034,3 +1034,23 @@ def _to_base_domain(self, f): """ return f.element() + + def scale(self, scalar): + r""" + Return this valuation scaled by ``scalar``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - (x^2 + x + 1)) + sage: v = FunctionFieldValuation(K, x - 1) # indirect doctest + sage: w = v.extension(L) + sage: 3*w + 3 * (x - 1)-adic valuation + + """ + if scalar in QQ and scalar > 0 and scalar != 1: + return FunctionFieldValuation(self.domain(), self._base_valuation._initial_approximation.scale(scalar)) + return super(FunctionFieldFromLimitValuation, self).scale(scalar) diff --git a/gauss_valuation.py b/gauss_valuation.py index e31ed1ee0d1..f1ce5ce98a8 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -707,6 +707,6 @@ def scale(self, scalar): """ from sage.rings.all import QQ - if scalar in QQ and scalar != 0: + if scalar in QQ and scalar > 0 and scalar != 1: return GaussValuation(self.domain(), self._base_valuation.scale(scalar)) return super(GaussValuation_generic, self).scale(scalar) diff --git a/mapped_valuation.py b/mapped_valuation.py index ab276db6304..6d0abddf61b 100644 --- a/mapped_valuation.py +++ b/mapped_valuation.py @@ -441,6 +441,27 @@ def _from_base_domain(self, f): r""" return self._from_base(f) + def scale(self, scalar): + r""" + Return this valuation scaled by ``scalar``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L) + sage: 3*w + 3 * Valuation at the infinite place + + """ + from sage.rings.all import QQ + if scalar in QQ and scalar > 0 and scalar != 1: + return DomainMappedValuation(self.domain(), self._base_valuation.scale(scalar), self._to_base, self._from_base) + return super(DomainMappedValuation_base, self).scale(scalar) + class DomainMappedDiscreteValuation(DomainMappedValuation_base, DiscreteValuation): r""" From b76c4f7bc31dbd390aebde365aadb28f081839d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 11 Nov 2016 15:52:22 -0500 Subject: [PATCH 113/740] fix typo --- function_field_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/function_field_valuation.py b/function_field_valuation.py index f6a2ddea53c..dfed1c94da1 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -265,7 +265,7 @@ def create_key_and_extra_args(self, domain, prime): # Instead, we return the key that was used to create prime # so the caller gets back a correctly cached version of prime if not hasattr(prime, "_factory_data"): - raise NotImplementedError("Valuations on function fields must be unique and come out of the FunctionFieldValuatino factory but %r has been created by other means"%(prime,)) + raise NotImplementedError("Valuations on function fields must be unique and come out of the FunctionFieldValuation factory but %r has been created by other means"%(prime,)) return prime._factory_data[2], {} if prime in domain: From d074ba59905887eac20fdf4df3bccd3afbc2c29b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 11 Nov 2016 15:59:02 -0500 Subject: [PATCH 114/740] Do not check squarefreeness too often --- valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/valuation.py b/valuation.py index ac9c49f3653..7ef77e26277 100644 --- a/valuation.py +++ b/valuation.py @@ -634,7 +634,7 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): assert expandables for v in expandables: - leaves.extend(v.mac_lane_step(G)) + leaves.extend(v.mac_lane_step(G, assume_squarefree=True)) return leaves From 37d6efdbf9ae9e1ff454ea238d06beff7f1f0056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 11 Nov 2016 16:03:22 -0500 Subject: [PATCH 115/740] Properly handle mapped function field valuations They should not go through a generic DomainMappedValuation so they do not loose general function field features. Also, implemented restriction() in a lot of places. --- __init__.py | 5 +- function_field_valuation.py | 220 ++++++++++++++++++++++++++++++++- limit_valuation.py | 20 +++ mapped_valuation.py | 235 +++++++----------------------------- 4 files changed, 279 insertions(+), 201 deletions(-) diff --git a/__init__.py b/__init__.py index c812dc631a6..1e8849bbbba 100644 --- a/__init__.py +++ b/__init__.py @@ -39,9 +39,9 @@ # local file and the instances that come from the mac_lane import define # different types) from trivial_valuation import TrivialDiscreteValuation, TrivialDiscretePseudoValuation -from function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldFromLimitValuation, InfiniteRationalFunctionFieldValuation, FiniteRationalFunctionFieldValuation, NonClassicalRationalFunctionFieldValuation +from function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldFromLimitValuation, InfiniteRationalFunctionFieldValuation, FiniteRationalFunctionFieldValuation, NonClassicalRationalFunctionFieldValuation, FunctionFieldMappedValuation from limit_valuation import LimitValuation, MacLaneLimitValuation, LimitValuation_generic -from mapped_valuation import MappedValuation_base, FiniteExtensionFromLimitValuation, FiniteExtensionFromInfiniteValuation, DomainMappedValuation_base, DomainMappedValuation, DomainMappedDiscreteValuation +from mapped_valuation import MappedValuation_base, FiniteExtensionFromLimitValuation, FiniteExtensionFromInfiniteValuation, MappedValuation_base from augmented_valuation import FiniteAugmentedValuation, InfiniteAugmentedValuation from gauss_valuation import GaussValuation_generic from valuation import DiscretePseudoValuation, DiscreteValuation, InfiniteDiscretePseudoValuation @@ -545,5 +545,4 @@ def some_elements(self): register_factory_unpickle("FunctionFieldValuation", FunctionFieldValuation) register_factory_unpickle("AugmentedValuation", AugmentedValuation) register_factory_unpickle("LimitValuation", LimitValuation) -register_factory_unpickle("DomainMappedValuation", DomainMappedValuation) register_factory_unpickle("ScaledValuation", ScaledValuation) diff --git a/function_field_valuation.py b/function_field_valuation.py index dfed1c94da1..12024a71f28 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -132,7 +132,7 @@ from valuation import DiscreteValuation from trivial_valuation import TrivialValuation -from mapped_valuation import FiniteExtensionFromLimitValuation, DomainMappedValuation +from mapped_valuation import FiniteExtensionFromLimitValuation, MappedValuation_base class FunctionFieldValuationFactory(UniqueFactory): r""" @@ -142,7 +142,9 @@ class FunctionFieldValuationFactory(UniqueFactory): - ``domain`` -- a function field - - ``prime`` -- a place of the function field or a valuation on a subring + - ``prime`` -- a place of the function field, a valuation on a subring, or + a valuation on another function field together with information for + isomorphisms to and from that function field EXAMPLES:: @@ -258,6 +260,11 @@ def create_key_and_extra_args(self, domain, prime): if domain not in FunctionFields(): raise ValueError("Domain must be a function field.") + if isinstance(prime, tuple) and len(prime) == 3: + # prime is a triple of a valuation on another function field with + # isomorphism information + return self.create_key_and_extra_args_from_valuation_on_isomorphic_field(domain, prime[0], prime[1], prime[2]) + if prime in DiscretePseudoValuationSpace(domain): # prime is already a valuation of the requested domain # if we returned (domain, prime), we would break caching @@ -370,6 +377,38 @@ def create_key_and_extra_args_from_valuation(self, domain, valuation): raise NotImplementedError("extension of valuation from %r to %r not implemented yet"%(valuation.domain(), domain)) + def create_key_and_extra_args_from_valuation_on_isomorphic_field(self, domain, valuation, gen_image, gen_preimage): + r""" + Create a unique key which identifies the valuation which is + ``valuation`` after mapping the generator of this function field to + ``gen_image``. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L) # indirect doctest + + """ + # equality is probably broken if we allow for trivial maps + assert(gen_image != gen_preimage) + + if gen_image not in valuation.domain(): + raise ValueError("gen_image must be an element of the domain of valuation but %r is not in %r"%(gen_image, valuation.domain())) + gen_image = valuation.domain()(gen_image) + + if gen_preimage not in domain: + raise ValueError("gen_preimage must be an element of the domain but %r is not in %r"%(gen_preimage, domain)) + gen_preimage = domain(gen_preimage) + + if valuation.domain() is valuation.domain().base(): + raise NotImplementedError("mapped valuations on rational function fields are not supported") + + return (domain, (valuation, gen_image, gen_preimage)), {} + def create_object(self, version, key, **extra_args): r""" Create the valuation specified by ``key``. @@ -388,6 +427,10 @@ def create_object(self, version, key, **extra_args): from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace parent = DiscretePseudoValuationSpace(domain) + if isinstance(valuation, tuple) and len(valuation) == 3: + valuation, gen_image, gen_preimage = valuation + return parent.__make_element_class__(FunctionFieldMappedValuation)(parent, valuation, gen_image, gen_preimage) + if valuation in domain: # only the infinite place is handled with an element instead of a base valuation # any other element should have been replace with a valuation by _create_key_from_place @@ -453,6 +496,20 @@ def extensions(self, L): [[ Valuation at the infinite place, v(y - 1/x) = 3 ]-adic valuation, [ Valuation at the infinite place, v(y + 1/x) = 3 ]-adic valuation] + Iterated extensions over the infinite place:: + + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L) + sage: R. = L[] + sage: M. = L.extension(z^2 - y) + sage: w.extension(M) # squarefreeness is not implemented here + Traceback (most recent call last): + ... + NotImplementedError + """ K = self.domain() from sage.categories.function_fields import FunctionFields @@ -477,9 +534,9 @@ def extensions(self, L): from sage.rings.morphism import RingHomomorphism_im_gens if type(y_to_u) == RingHomomorphism_im_gens and type(u_to_y) == RingHomomorphism_im_gens: # both maps are trivial on the constants - M_to_L = M.hom(L(u_to_y(u_to_y.domain().gen()))) - L_to_M = L.hom(M(y_to_u(y_to_u.domain().gen()))) - return [DomainMappedValuation(L, w, L_to_M, M_to_L) for w in H_extensions] + gen_preimage = L(u_to_y(u_to_y.domain().gen())) + gen_image = M(y_to_u(y_to_u.domain().gen())) + return [FunctionFieldValuation(L, (w, gen_image, gen_preimage)) for w in H_extensions] else: raise NotImplementedError return [FunctionFieldValuation(L, w) for w in self.mac_lane_approximants(L.polynomial())] @@ -997,7 +1054,7 @@ def __init__(self, parent, base_valuation): class FunctionFieldFromLimitValuation(FiniteExtensionFromLimitValuation): r""" - A valuation on a finit extensions of function fields `L=K/(G)` where `K` is + A valuation on a finite extensions of function fields `L=K[y]/(G)` where `K` is another function field. EXAMPLES:: @@ -1054,3 +1111,154 @@ def scale(self, scalar): if scalar in QQ and scalar > 0 and scalar != 1: return FunctionFieldValuation(self.domain(), self._base_valuation._initial_approximation.scale(scalar)) return super(FunctionFieldFromLimitValuation, self).scale(scalar) + + +class FunctionFieldMappedValuation(FunctionFieldValuation_base, MappedValuation_base): + r""" + A valuation on a finite extensions of function fields `L=K[y]/(G)` where `K` is + another function field which redirects to another ``base_valuation`` on an + isomorphism function field `M=K[y]/(H)`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L) + + sage: w(x) + -1 + sage: w(y) + -3/2 + sage: w.uniformizer() + 1/x^2*y + + """ + def __init__(self, parent, base_valuation, gen_image, gen_preimage): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L) + sage: isinstance(w, FunctionFieldMappedValuation) + True + sage: TestSuite(w).run() # long time + + """ + FunctionFieldValuation_base.__init__(self, parent) + MappedValuation_base.__init__(self, parent, base_valuation) + + self._gen_image = gen_image + self._gen_preimage = gen_preimage + + self._to_base = self.domain().hom(gen_image) + self._from_base = base_valuation.domain().hom(gen_preimage) + + def _to_base_domain(self, f): + r""" + Return ``f`` as an element in the domain of ``_base_valuation``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L) + sage: w._to_base_domain(y) + x^2*y + + """ + return self._to_base(f) + + def _from_base_domain(self, f): + r""" + Return ``f`` as an element in the domain of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L) + sage: w._from_base_domain(w._to_base_domain(y)) + y + + r""" + return self._from_base(f) + + def scale(self, scalar): + r""" + Return this valuation scaled by ``scalar``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L) + sage: 3*w + 3 * Valuation at the infinite place + + """ + from sage.rings.all import QQ + if scalar in QQ and scalar > 0 and scalar != 1: + return FunctionFieldValuation(self.domain(), (self._base_valuation.scale(scalar), self._gen_image, self._gen_preimage)) + return super(FunctionFieldMappedValuation, self).scale(scalar) + + def _repr_(self): + r""" + Return a printable representation of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L); w + Valuation at the infinite place + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - 1/x^2 - 1) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extensions(L) + + """ + assert(self.domain().base() is not self.domain()) + if repr(self._base_valuation) == repr(self.restriction(self.domain().base())): + return repr(self._base_valuation) + return "%r (in %r after %r |--> %r)"%(self._base_valuation, self._base_valuation.domain(), self.domain().gen(), self._gen_image) + + def restriction(self, ring): + r""" + Return the restriction of this valuation to ``ring``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L) + sage: w.restriction(K) is v + True + + """ + if ring.is_subring(self.domain().base()): + return self._base_valuation.restriction(ring) + return super(FunctionFieldMappedValuation, self).restriction(ring) diff --git a/limit_valuation.py b/limit_valuation.py index 92e17abd4bf..dcfd2ec033a 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -635,3 +635,23 @@ def _ge_(self, other): if other.is_trivial(): return other.is_discrete_valuation() return super(MacLaneLimitValuation, self)._ge_(other) + + def restriction(self, ring): + r""" + Return the restriction of this valuation to ``ring``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: w = v.extension(L) + sage: w._base_valuation.restriction(K) + 2-adic valuation + + """ + if ring.is_subring(self.domain().base()): + return self._initial_approximation.restriction(ring) + return super(MacLaneLimitValuation, self).restriction(ring) diff --git a/mapped_valuation.py b/mapped_valuation.py index 6d0abddf61b..d0eff892111 100644 --- a/mapped_valuation.py +++ b/mapped_valuation.py @@ -40,67 +40,7 @@ sys.path.append(os.path.dirname(os.getcwd())) from valuation import DiscreteValuation, DiscretePseudoValuation -from sage.structure.factory import UniqueFactory - -class DomainMappedValuationFactory(UniqueFactory): - r""" - Return a valuation which treats the valuation ``base`` like a valuation on - ``domain`` by sending its arguments through ``to_base`` and ``from_base`` - respectively. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) - sage: w = v.extension(L); w - Valuation at the infinite place - - """ - def create_key(self, domain, base, to_base, from_base): - r""" - Create a key which uniquely identifies a valuation. - - TESTS:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) - sage: w = v.extension(L) # indirect doctest - sage: ww = v.extension(L) - sage: w is ww - True - - """ - return domain, base, to_base, from_base - - def create_object(self, version, key): - r""" - Create a valuation from ``key``. - - TESTS:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) - sage: w = v.extension(L) # indirect doctest - - """ - domain, base, to_base, from_base = key - from valuation_space import DiscretePseudoValuationSpace - parent = DiscretePseudoValuationSpace(domain) - if base.is_discrete_valuation(): - return parent.__make_element_class__(DomainMappedDiscreteValuation)(parent, base, to_base, from_base) - else: - raise NotImplementedError - -DomainMappedValuation = DomainMappedValuationFactory("DomainMappedValuation") +from sage.misc.abstract_method import abstract_method class MappedValuation_base(DiscretePseudoValuation): r""" @@ -142,10 +82,13 @@ def __init__(self, parent, base_valuation): self._base_valuation = base_valuation + @abstract_method def _repr_(self): r""" Return a printable representation of this valuation. + Subclasses must override this method. + EXAMPLES:: sage: from mac_lane import * # optional: standalone @@ -157,7 +100,6 @@ def _repr_(self): 2-adic valuation """ - return repr(self._base_valuation) def residue_ring(self): r""" @@ -356,132 +298,6 @@ def _test_to_from_base_domain(self, **options): # note that the converse might not be true -class DomainMappedValuation_base(MappedValuation_base): - r""" - A valuation which is implemented another proxy "base" valuation with which - is shares the :meth:`residue_ring` but not the domain. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) - sage: w = v.extension(L); w - Valuation at the infinite place - - sage: w(x) - -1 - sage: w(y) - -3/2 - sage: w.uniformizer() - 1/x^2*y - - """ - def __init__(self, parent, base_valuation, to_base, from_base): - r""" - TESTS:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) - sage: w = v.extension(L) - sage: isinstance(w, DomainMappedValuation_base) - True - sage: TestSuite(w).run() # long time - - """ - MappedValuation_base.__init__(self, parent, base_valuation) - - from sage.categories.homset import Hom - if to_base not in Hom(self.domain(), base_valuation.domain()): - raise ValueError("to_base must map from %r to %r"%(self.domain(), base_valuation.domain())) - if from_base not in Hom(base_valuation.domain(), self.domain()): - raise ValueError("from_base must map from %r to %r"%(base_valuation.domain(), self.domain())) - - self._to_base = to_base - self._from_base = from_base - - def _to_base_domain(self, f): - r""" - Return ``f`` as an element in the domain of ``_base_valuation``. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) - sage: w = v.extension(L) - sage: w._to_base_domain(y) - x^2*y - - """ - return self._to_base(f) - - def _from_base_domain(self, f): - r""" - Return ``f`` as an element in the domain of this valuation. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) - sage: w = v.extension(L) - sage: w._from_base_domain(w._to_base_domain(y)) - y - - r""" - return self._from_base(f) - - def scale(self, scalar): - r""" - Return this valuation scaled by ``scalar``. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) - sage: w = v.extension(L) - sage: 3*w - 3 * Valuation at the infinite place - - """ - from sage.rings.all import QQ - if scalar in QQ and scalar > 0 and scalar != 1: - return DomainMappedValuation(self.domain(), self._base_valuation.scale(scalar), self._to_base, self._from_base) - return super(DomainMappedValuation_base, self).scale(scalar) - - -class DomainMappedDiscreteValuation(DomainMappedValuation_base, DiscreteValuation): - r""" - A discrete valuation which is implemented through another proxy "base" - valuation. - - TESTS:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) - sage: w = v.extension(L) - sage: isinstance(w, DomainMappedDiscreteValuation) - True - """ - pass - - class FiniteExtensionFromInfiniteValuation(MappedValuation_base, DiscreteValuation): r""" A valuation on a quotient of the form `L=K[x]/(G)` with an irreducible `G` @@ -507,11 +323,26 @@ class FiniteExtensionFromInfiniteValuation(MappedValuation_base, DiscreteValuati sage: w = v.extension(L); w (x)-adic valuation - TESTS:: - - sage: TestSuite(w).run() # long time - """ + def __init__(self, parent, base_valuation): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = FunctionFieldValuation(K, 0) + sage: w = v.extension(L) + sage: isinstance(w, FiniteExtensionFromInfiniteValuation) + True + sage: TestSuite(w).run() # long time + + """ + MappedValuation_base.__init__(self, parent, base_valuation) + DiscreteValuation.__init__(self, parent) + def _eq_(self, other): r""" Return whether this valuation is indistinguishable from ``other``. @@ -531,6 +362,26 @@ def _eq_(self, other): """ return isinstance(other, FiniteExtensionFromInfiniteValuation) and self._base_valuation == other._base_valuation + def restriction(self, ring): + r""" + Return the restriction of this valuation to ``ring``. + + EXAMPLES: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: w = v.extension(L) + sage: w.restriction(K) is v + True + + """ + if ring.is_subring(self._base_valuation.domain().base()): + return self._base_valuation.restriction(ring) + return super(FiniteExtensionFromInfiniteValuation, self).restriction(ring) + class FiniteExtensionFromLimitValuation(FiniteExtensionFromInfiniteValuation): r""" From 494b9526bd3b69f5ad854b20c51cc1c0e85ee1b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 11 Nov 2016 16:26:21 -0500 Subject: [PATCH 116/740] Valuation at the infinite place is just a mapped valuation --- __init__.py | 2 +- function_field_valuation.py | 288 +++++++++++++----------------------- 2 files changed, 102 insertions(+), 188 deletions(-) diff --git a/__init__.py b/__init__.py index 1e8849bbbba..365503c923d 100644 --- a/__init__.py +++ b/__init__.py @@ -39,7 +39,7 @@ # local file and the instances that come from the mac_lane import define # different types) from trivial_valuation import TrivialDiscreteValuation, TrivialDiscretePseudoValuation -from function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldFromLimitValuation, InfiniteRationalFunctionFieldValuation, FiniteRationalFunctionFieldValuation, NonClassicalRationalFunctionFieldValuation, FunctionFieldMappedValuation +from function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldFromLimitValuation, InfiniteRationalFunctionFieldValuation, FiniteRationalFunctionFieldValuation, NonClassicalRationalFunctionFieldValuation, FunctionFieldMappedValuation_base, FunctionFieldExtensionMappedValuation from limit_valuation import LimitValuation, MacLaneLimitValuation, LimitValuation_generic from mapped_valuation import MappedValuation_base, FiniteExtensionFromLimitValuation, FiniteExtensionFromInfiniteValuation, MappedValuation_base from augmented_valuation import FiniteAugmentedValuation, InfiniteAugmentedValuation diff --git a/function_field_valuation.py b/function_field_valuation.py index 12024a71f28..ba104bd92e0 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -329,7 +329,7 @@ def create_key_and_extra_args_from_place(self, domain, generator): return self.create_key_and_extra_args(domain, valuation) elif generator == ~domain.gen(): # generator is 1/x, the infinite place - return (domain, ~domain.gen()), {} + return (domain, (FunctionFieldValuation(domain, domain.gen()), ~domain.gen(), ~domain.gen())), {} else: raise ValueError("a place must be given by an irreducible polynomial or the inverse of the generator; %r does not define a place over %r"%(generator, domain)) @@ -394,7 +394,7 @@ def create_key_and_extra_args_from_valuation_on_isomorphic_field(self, domain, v """ # equality is probably broken if we allow for trivial maps - assert(gen_image != gen_preimage) + assert(gen_image != domain.gen()) if gen_image not in valuation.domain(): raise ValueError("gen_image must be an element of the domain of valuation but %r is not in %r"%(gen_image, valuation.domain())) @@ -405,7 +405,8 @@ def create_key_and_extra_args_from_valuation_on_isomorphic_field(self, domain, v gen_preimage = domain(gen_preimage) if valuation.domain() is valuation.domain().base(): - raise NotImplementedError("mapped valuations on rational function fields are not supported") + if gen_image != ~domain.gen() or gen_preimage != ~domain.gen() or valuation != FunctionFieldValuation(domain, domain.gen()): + raise NotImplementedError("mapped valuations on rational function fields are not supported") return (domain, (valuation, gen_image, gen_preimage)), {} @@ -429,13 +430,11 @@ def create_object(self, version, key, **extra_args): if isinstance(valuation, tuple) and len(valuation) == 3: valuation, gen_image, gen_preimage = valuation - return parent.__make_element_class__(FunctionFieldMappedValuation)(parent, valuation, gen_image, gen_preimage) - - if valuation in domain: - # only the infinite place is handled with an element instead of a base valuation - # any other element should have been replace with a valuation by _create_key_from_place - assert(valuation == ~domain.gen()) - return parent.__make_element_class__(InfiniteRationalFunctionFieldValuation)(parent) + if gen_image == ~domain.gen() and gen_preimage == ~domain.gen() and valuation == FunctionFieldValuation(domain, domain.gen()): + # valuation at the infinite place + return parent.__make_element_class__(InfiniteRationalFunctionFieldValuation)(parent) + else: + return parent.__make_element_class__(FunctionFieldExtensionMappedValuation)(parent, valuation, gen_image, gen_preimage) if domain is valuation.domain(): # we can not just return valuation in this case @@ -468,7 +467,6 @@ class FunctionFieldValuation_base(DiscreteValuation): sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, FunctionFieldValuation_base) True - sage: TestSuite(v).run(max_runs=100) # long time """ def extensions(self, L): @@ -492,7 +490,7 @@ def extensions(self, L): sage: v = FunctionFieldValuation(K, 1/x) sage: R. = K[] sage: L. = K.extension(y^2 - 1/(x^2 + 1)) - sage: v.extensions(L) + sage: v.extensions(L) # long time [[ Valuation at the infinite place, v(y - 1/x) = 3 ]-adic valuation, [ Valuation at the infinite place, v(y + 1/x) = 3 ]-adic valuation] @@ -561,7 +559,6 @@ class RationalFunctionFieldValuation_base(FunctionFieldValuation_base): sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, RationalFunctionFieldValuation_base) True - sage: TestSuite(v).run() # long time """ @@ -578,7 +575,6 @@ class ClassicalFunctionFieldValuation_base(FunctionFieldValuation_base): sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, ClassicalFunctionFieldValuation_base) True - sage: TestSuite(v).run() # long time """ def _test_classical_residue_field(self, **options): @@ -630,7 +626,6 @@ class InducedFunctionFieldValuation_base(FunctionFieldValuation_base): sage: from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x^2 + 1) # indirect doctest - sage: TestSuite(v).run() # long time """ def __init__(self, parent, base_valuation): @@ -841,143 +836,6 @@ def residue_ring(self): """ return self._base_valuation.residue_ring().fraction_field() -class InfiniteRationalFunctionFieldValuation(RationalFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base): - r""" - Valuation of the infinite place of a function field. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, 1/x) # indirect doctest - - TESTS:: - - sage: TestSuite(v).run() # long time - - """ - def __init__(self, parent): - r""" - TESTS:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, 1/x) # indirect doctest - sage: isinstance(v, InfiniteRationalFunctionFieldValuation) - True - - """ - RationalFunctionFieldValuation_base.__init__(self, parent) - ClassicalFunctionFieldValuation_base.__init__(self, parent) - - def _call_(self, f): - r""" - Evaluate this valuation at the rational function ``f``. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, 1/x) - sage: v(x/(x^2 + 1)) - 1 - - """ - if f.is_zero(): - return infinity - return f.denominator().degree() - f.numerator().degree() - - def _repr_(self): - r""" - Return a printable representation of this valuation. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: FunctionFieldValuation(K, 1/x) # indirect doctest - Valuation at the infinite place - - """ - return "Valuation at the infinite place" - - def residue_ring(self): - r""" - Return the residue field of this valuation. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: FunctionFieldValuation(K, 1/x).residue_ring() - Rational Field - - """ - return self.domain().constant_field() - - def uniformizer(self): - r""" - Return a uniformizer of this valuation. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: FunctionFieldValuation(K, 1/x).uniformizer() - 1/x - - """ - return ~self.domain().gen() - - def reduce(self, f): - r""" - Return the reduction of ``f`` with respect to this valuation. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(GF(2)) - sage: v = FunctionFieldValuation(K, 1/x) - sage: v.reduce(1/x) - 0 - sage: v.reduce( x*(x+1) / (x^2+x+1) ) - 1 - sage: v.reduce(x) - Traceback (most recent call last): - ... - ValueError: reduction is only defined for elements of non-negative valuation - - """ - f = self.domain().coerce(f) - - if self(f) < 0: - raise ValueError("reduction is only defined for elements of non-negative valuation") - if self(f) > 0: - return self.residue_ring().zero() - - from sage.rings.function_field.function_field_valuation import FunctionFieldValuation - return FunctionFieldValuation(self.domain(), self.domain().gen()).reduce(f.element()(~self.domain().gen())) - - def lift(self, F): - r""" - Lift ``F`` from :meth:`residue_ring` to :meth:`domain` such that - :meth:`reduce` of the lift produces ``F``. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(GF(2)) - sage: v = FunctionFieldValuation(K, 1/x) - - sage: v.lift(v.residue_ring().one()) - 1 - - """ - F = self.residue_ring().coerce(F) - # the infinite place has a trivial residue field extension - assert F in self.domain() - return self.domain()(F) - class FiniteRationalFunctionFieldValuation(ClassicalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, RationalFunctionFieldValuation_base): r""" Valuation of a finite place of a function field. @@ -1071,7 +929,6 @@ class FunctionFieldFromLimitValuation(FiniteExtensionFromLimitValuation): sage: isinstance(w, FunctionFieldFromLimitValuation) True - sage: TestSuite(w).run() # long time """ def _to_base_domain(self, f): @@ -1113,27 +970,17 @@ def scale(self, scalar): return super(FunctionFieldFromLimitValuation, self).scale(scalar) -class FunctionFieldMappedValuation(FunctionFieldValuation_base, MappedValuation_base): +class FunctionFieldMappedValuation_base(FunctionFieldValuation_base, MappedValuation_base): r""" - A valuation on a finite extensions of function fields `L=K[y]/(G)` where `K` is - another function field which redirects to another ``base_valuation`` on an - isomorphism function field `M=K[y]/(H)`. + A valuation on a function field which relies on a ``base_valuation`` on an + isomorphic function field. EXAMPLES:: - + sage: from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) - sage: w = v.extension(L) - - sage: w(x) - -1 - sage: w(y) - -3/2 - sage: w.uniformizer() - 1/x^2*y + sage: v = FunctionFieldValuation(K, 1/x); v + Valuation at the infinite place """ def __init__(self, parent, base_valuation, gen_image, gen_preimage): @@ -1142,13 +989,9 @@ def __init__(self, parent, base_valuation, gen_image, gen_preimage): sage: from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) sage: v = FunctionFieldValuation(K, 1/x) - sage: w = v.extension(L) - sage: isinstance(w, FunctionFieldMappedValuation) + sage: isinstance(v, FunctionFieldMappedValuation_base) True - sage: TestSuite(w).run() # long time """ FunctionFieldValuation_base.__init__(self, parent) @@ -1196,27 +1039,77 @@ def _from_base_domain(self, f): r""" return self._from_base(f) - def scale(self, scalar): + +class InfiniteRationalFunctionFieldValuation(FunctionFieldMappedValuation_base, RationalFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base): + r""" + Valuation of the infinite place of a function field. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, 1/x) # indirect doctest + + """ + def __init__(self, parent): r""" - Return this valuation scaled by ``scalar``. + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, 1/x) # indirect doctest + sage: isinstance(v, InfiniteRationalFunctionFieldValuation) + True + + """ + x = parent.domain().gen() + FunctionFieldMappedValuation_base.__init__(self, parent, FunctionFieldValuation(parent.domain(), x), 1/x, 1/x) + RationalFunctionFieldValuation_base.__init__(self, parent) + ClassicalFunctionFieldValuation_base.__init__(self, parent) + + def _repr_(self): + r""" + Return a printable representation of this valuation. EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) - sage: w = v.extension(L) - sage: 3*w - 3 * Valuation at the infinite place + sage: K. = FunctionField(QQ) + sage: FunctionFieldValuation(K, 1/x) # indirect doctest + Valuation at the infinite place """ - from sage.rings.all import QQ - if scalar in QQ and scalar > 0 and scalar != 1: - return FunctionFieldValuation(self.domain(), (self._base_valuation.scale(scalar), self._gen_image, self._gen_preimage)) - return super(FunctionFieldMappedValuation, self).scale(scalar) + return "Valuation at the infinite place" + + +class FunctionFieldExtensionMappedValuation(FunctionFieldMappedValuation_base): + r""" + A valuation on a finite extensions of function fields `L=K[y]/(G)` where `K` is + another function field which redirects to another ``base_valuation`` on an + isomorphism function field `M=K[y]/(H)`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L) + + sage: w(x) + -1 + sage: w(y) + -3/2 + sage: w.uniformizer() + 1/x^2*y + + TESTS:: + + sage: isinstance(w, FunctionFieldExtensionMappedValuation) + True + """ def _repr_(self): r""" Return a printable representation of this valuation. @@ -1262,3 +1155,24 @@ def restriction(self, ring): if ring.is_subring(self.domain().base()): return self._base_valuation.restriction(ring) return super(FunctionFieldMappedValuation, self).restriction(ring) + + def scale(self, scalar): + r""" + Return this valuation scaled by ``scalar``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L) + sage: 3*w + 3 * Valuation at the infinite place + + """ + from sage.rings.all import QQ + if scalar in QQ and scalar > 0 and scalar != 1: + return FunctionFieldValuation(self.domain(), (self._base_valuation.scale(scalar), self._gen_image, self._gen_preimage)) + return super(FunctionFieldMappedValuation_base, self).scale(scalar) From 4813d991aefb9f3341cb2224223b215b94eba0a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 11 Nov 2016 21:14:16 -0500 Subject: [PATCH 117/740] Add framework for separating elements this implementation just tries to be lucky using uniformizing elements. --- mapped_valuation.py | 4 +- scaled_valuation.py | 44 ++++++++++++ valuation_space.py | 166 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 212 insertions(+), 2 deletions(-) diff --git a/mapped_valuation.py b/mapped_valuation.py index d0eff892111..66f9787cbf2 100644 --- a/mapped_valuation.py +++ b/mapped_valuation.py @@ -347,7 +347,7 @@ def _eq_(self, other): r""" Return whether this valuation is indistinguishable from ``other``. - EXAMPLES: + EXAMPLES:: sage: from mac_lane import * # optional: standalone sage: K = QQ @@ -366,7 +366,7 @@ def restriction(self, ring): r""" Return the restriction of this valuation to ``ring``. - EXAMPLES: + EXAMPLES:: sage: from mac_lane import * # optional: standalone sage: K = QQ diff --git a/scaled_valuation.py b/scaled_valuation.py index 483996a6848..4432d4f1a41 100644 --- a/scaled_valuation.py +++ b/scaled_valuation.py @@ -238,3 +238,47 @@ def restriction(self, ring): """ return ScaledValuation(self._base_valuation.restriction(ring), self._scale) + + def _strictly_separating_element(self, other): + r""" + Return an element in the domain of this valuation which has positive + valuation with respect to this valuation but negative valuation with + respect to ``other``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v2 = pAdicValuation(QQ, 2) + sage: v3 = 12 * pAdicValuation(QQ, 3) + sage: v2._strictly_separating_element(v3) + 2/3 + + """ + return self._base_valuation._strictly_separating_element(other) + + def _weakly_separating_element(self, other): + r""" + Return an element in the domain of this valuation which has + positive valuation with respect to this valuation and higher + valuation with respect to this valuation than with respect to + ``other``. + + .. NOTE:: + + Overriding this method tends to be a nuissance as you need to + handle all possible types (as in Python type) of valuations. + This is essentially the same problem that you have when + implementing operators such as ``+`` or ``>=``. A sufficiently + fancy multimethod implementation could solve that here but + there is currently nothing like that in Sage/Python. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v2 = pAdicValuation(QQ, 2) + sage: v3 = 12 * pAdicValuation(QQ, 3) + sage: v2._weakly_separating_element(v3) + 2 + + """ + return self._base_valuation._weakly_separating_element(other) diff --git a/valuation_space.py b/valuation_space.py index 1e3af0cc747..dbfe9e98efb 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -699,6 +699,172 @@ def scale(self, scalar): from scaled_valuation import ScaledValuation return ScaledValuation(self, scalar) + def separating_element(self, others): + r""" + Return an element in the domain of this valuation which has + positive valuation with respect to this valuation but negative + valuation with respect to the valuations in ``others``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v2 = pAdicValuation(QQ, 2) + sage: v3 = pAdicValuation(QQ, 3) + sage: v5 = pAdicValuation(QQ, 5) + sage: v2.separating_element([v3,v5]) + 4/15 + + """ + try: + iter(others) + except TypeError: + raise ValueError("others must be a list of valuations") + + for other in others + [self]: + if other.parent() is not self.parent(): + raise ValueError("all valuations must be valuations on %r but %r is a valuation on %r"%(self.domain(), other, other.domain())) + if not other.is_discrete_valuation(): + raise ValueError("all valuationss must be discrete valuations but %r is not"%(other,)) + if other.is_trivial(): + raise ValueError("all valuations must be non-trivial but %r is not"%(other,)) + + if len(others)==0: + return self.uniformizer() + + ret = self._strictly_separating_element(others[0]) + for i in range(1, len(others)): + # ret is an element which separates self and others[:i] + if others[i](ret) < 0: + # it also separates self and others[i] + continue + + delta = self._strictly_separating_element(others[i]) + if others[i](ret) == 0: + # combining powers of ret and delta, we produce a separating element for self and others[:i+1] + factor = ret + ret = delta + while any(other(ret) >= 0 for other in others[:i]): + assert(others[i](ret) < 0) + ret *= factor + else: # others[i](ret) < 0 + # construct an element which approximates a unit with respect to others[i] + # and has negative valuation with respect to others[:i] + from sage.rings.all import NN + for r in iter(NN): + factor = (ret**r)/(1+ret**r) + assert(others[i](factor) > 0) + assert(all(other(factor) < 0 for other in others)) + if others[i](ret * factor) < 0: + ret *= factor + break + return ret + + def _strictly_separating_element(self, other): + r""" + Return an element in the domain of this valuation which has + positive valuation with respect to this valuation but negative + valuation with respect to ``other``. + + .. NOTE:: + + Overriding this method tends to be a nuissance as you need to + handle all possible types (as in Python type) of valuations. + This is essentially the same problem that you have when + implementing operators such as ``+`` or ``>=``. A sufficiently + fancy multimethod implementation could solve that here but + there is currently nothing like that in Sage/Python. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v2 = pAdicValuation(QQ, 2) + sage: v3 = pAdicValuation(QQ, 3) + sage: v2._strictly_separating_element(v3) + 2/3 + + """ + from sage.rings.all import ZZ, NN, infinity + + numerator = self._weakly_separating_element(other) + n = self(numerator) + nn = other(numerator) + assert(n > 0) + assert(nn != infinity) + if (nn < 0): + return numerator + + denominator = other._weakly_separating_element(self) + d = self(denominator) + dd = other(denominator) + assert(dd > 0) + assert(d != infinity) + if d < 0: + return self.domain()(1/denominator) + + # We need non-negative integers a and b such that + # a*n - b*d > 0 and a*nn - b*dd < 0 + if nn == 0: + # the above becomes b != 0 and a/b > d/n + b = 1 + if d/n in ZZ: + a = d/n + 1 + else: + a = (d/n).ceil() + else: + # Since n,nn,d,dd are all non-negative this is essentially equivalent to + # a/b > d/n and b/a > nn/dd + # which is + # dd/nn > a/b > d/n + assert(dd/nn > d/n) + for b in iter(NN): + # we naĩvely find the smallest b which can satisfy such an equation + # there are faster algorithms for this https://dl.acm.org/citation.cfm?id=1823943&CFID=864015776&CFTOKEN=26270402 + if b == 0: + continue + assert(b <= n + nn) # (a+b)/(n+nn) is a solution + if nn/dd/b in ZZ: + a = nn/dd/b + 1 + else: + a = (nn/dd/b).ceil() + assert(a/b > d/n) + if dd/nn > a/b: + break + + ret = self.domain()(numerator**a / denominator**b) + assert(self(ret) > 0) + assert(other(ret) < 0) + return ret + + def _weakly_separating_element(self, other): + r""" + Return an element in the domain of this valuation which has + positive valuation with respect to this valuation and higher + valuation with respect to this valuation than with respect to + ``other``. + + .. NOTE:: + + Overriding this method tends to be a nuissance as you need to + handle all possible types (as in Python type) of valuations. + This is essentially the same problem that you have when + implementing operators such as ``+`` or ``>=``. A sufficiently + fancy multimethod implementation could solve that here but + there is currently nothing like that in Sage/Python. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v2 = pAdicValuation(QQ, 2) + sage: v3 = pAdicValuation(QQ, 3) + sage: v2._weakly_separating_element(v3) + 2 + + """ + ret = self.uniformizer() + if self(ret) > other(ret): + return ret + raise NotImplementedError("weakly separating element for %r and %r"%(self, other)) + def _test_scale(self, **options): r""" Check that :meth:`scale` works correctly. From 4d614aba372c8532457eabfb11b81443f072aa53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 11 Nov 2016 21:33:37 -0500 Subject: [PATCH 118/740] Fix construction of separating elements the condition only holds in the limit --- valuation_space.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/valuation_space.py b/valuation_space.py index dbfe9e98efb..1e497c2cf3c 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -731,6 +731,7 @@ def separating_element(self, others): if len(others)==0: return self.uniformizer() + # see the proof of Lemma 6.9 in http://www1.spms.ntu.edu.sg/~frederique/antchap6.pdf ret = self._strictly_separating_element(others[0]) for i in range(1, len(others)): # ret is an element which separates self and others[:i] @@ -752,10 +753,8 @@ def separating_element(self, others): from sage.rings.all import NN for r in iter(NN): factor = (ret**r)/(1+ret**r) - assert(others[i](factor) > 0) - assert(all(other(factor) < 0 for other in others)) - if others[i](ret * factor) < 0: - ret *= factor + ret = factor * delta + if others[i](ret) < 0 and all([other(ret) < 0 for other in others[:i]]): break return ret From 0ba548baf7f3639876f66e6f6cef3763d97882c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 11 Nov 2016 21:35:10 -0500 Subject: [PATCH 119/740] weakly separating elements for Mac Lane valuations --- limit_valuation.py | 40 ++++++++++++++++++++++++++++++++++++++++ mapped_valuation.py | 24 ++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/limit_valuation.py b/limit_valuation.py index dcfd2ec033a..21ac0928e64 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -655,3 +655,43 @@ def restriction(self, ring): if ring.is_subring(self.domain().base()): return self._initial_approximation.restriction(ring) return super(MacLaneLimitValuation, self).restriction(ring) + + def _weakly_separating_element(self, other): + r""" + Return an element in the domain of this valuation which has + positive valuation with respect to this valuation and higher + valuation with respect to this valuation than with respect to + ``other``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: w = v.extension(L) + sage: v = pAdicValuation(QQ, 5) + sage: u,uu = v.extensions(L) + sage: w._base_valuation._weakly_separating_element(u._base_valuation) + sage: u._base_valuation._weakly_separating_element(uu._base_valuation) + + """ + from scaled_valuation import ScaledValuation_generic + v = self.restriction(self.domain().base()) + if isinstance(v, ScaledValuation_generic): + v = v._base_valuation + u = other.restriction(self.domain().base()) + if isinstance(u, ScaledValuation_generic): + u = u._base_valuation + + if u == v: + # phi of the initial approximant must be good enough to separate it + # from any other approximant of an extension + ret = self._initial_approximation.phi() + assert(self(ret) > other(ret)) # I could not come up with an example where this fails + return ret + else: + # if the valuations are sane, it should be possible to separate + # them with constants + return self.domain()(v._weakly_separating_element(u)) diff --git a/mapped_valuation.py b/mapped_valuation.py index 66f9787cbf2..5c28b6f4f4b 100644 --- a/mapped_valuation.py +++ b/mapped_valuation.py @@ -382,6 +382,30 @@ def restriction(self, ring): return self._base_valuation.restriction(ring) return super(FiniteExtensionFromInfiniteValuation, self).restriction(ring) + def _weakly_separating_element(self, other): + r""" + Return an element in the domain of this valuation which has + positive valuation with respect to this valuation and higher + valuation with respect to this valuation than with respect to + ``other``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: w = v.extension(L) + sage: v = pAdicValuation(QQ, 5) + sage: u,uu = v.extensions(L) + sage: u.separating_element([w,uu]) # indirect doctest + + """ + if isinstance(other, FiniteExtensionFromInfiniteValuation): + return self.domain()(self._base_valuation._weakly_separating_element(other._base_valuation)) + super(FiniteExtensionFromInfiniteValuation, self)._weakly_separating_element(other) + class FiniteExtensionFromLimitValuation(FiniteExtensionFromInfiniteValuation): r""" From b47adb4ca2696296a1968ce9738f08d87585f577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 11 Nov 2016 21:52:14 -0500 Subject: [PATCH 120/740] fixed typo --- limit_valuation.py | 12 ++++++++++++ valuation_space.py | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/limit_valuation.py b/limit_valuation.py index 21ac0928e64..16475e7dd75 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -676,6 +676,18 @@ def _weakly_separating_element(self, other): sage: w._base_valuation._weakly_separating_element(u._base_valuation) sage: u._base_valuation._weakly_separating_element(uu._base_valuation) + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, 1/x) + sage: R. = K[] + sage: L. = K.extension(y^2 - 1/(x^2 + 1)) + sage: u,uu = v.extensions(L) + sage: v = FunctionFieldValuation(K, x) + sage: w,ww = v.extensions(L) + sage: v = FunctionFieldValuation(K, 1) + sage: v = v.extension(L) + sage: u.separating_element([uu,ww,w,v]) + ((-8*x^4 - 12*x^2 - 4)/(x^2 - x))*y + (8*x^4 + 8*x^2 + 1)/(x^3 - x^2) + """ from scaled_valuation import ScaledValuation_generic v = self.restriction(self.domain().base()) diff --git a/valuation_space.py b/valuation_space.py index 1e497c2cf3c..3e5c9860968 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -747,7 +747,7 @@ def separating_element(self, others): while any(other(ret) >= 0 for other in others[:i]): assert(others[i](ret) < 0) ret *= factor - else: # others[i](ret) < 0 + else: # others[i](ret) > 0 # construct an element which approximates a unit with respect to others[i] # and has negative valuation with respect to others[:i] from sage.rings.all import NN From 17e37d26509c0db00bfea86aea66ab869dd10ddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 12 Nov 2016 10:45:36 -0500 Subject: [PATCH 121/740] Code cleanup and tests for separating elements --- limit_valuation.py | 18 +++++++++++++----- mapped_valuation.py | 1 + valuation_space.py | 12 +++++++++--- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/limit_valuation.py b/limit_valuation.py index 16475e7dd75..ff21d6ba987 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -672,22 +672,30 @@ def _weakly_separating_element(self, other): sage: v = pAdicValuation(QQ, 2) sage: w = v.extension(L) sage: v = pAdicValuation(QQ, 5) - sage: u,uu = v.extensions(L) - sage: w._base_valuation._weakly_separating_element(u._base_valuation) - sage: u._base_valuation._weakly_separating_element(uu._base_valuation) + sage: u,uu = v.extensions(L) # long time + sage: w._base_valuation._weakly_separating_element(u._base_valuation) # long time + 2 + sage: u._base_valuation._weakly_separating_element(uu._base_valuation) # long time + t + 2 sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, 1/x) sage: R. = K[] sage: L. = K.extension(y^2 - 1/(x^2 + 1)) - sage: u,uu = v.extensions(L) + sage: u,uu = v.extensions(L) # long time sage: v = FunctionFieldValuation(K, x) sage: w,ww = v.extensions(L) sage: v = FunctionFieldValuation(K, 1) sage: v = v.extension(L) - sage: u.separating_element([uu,ww,w,v]) + sage: u.separating_element([uu,ww,w,v]) # long time ((-8*x^4 - 12*x^2 - 4)/(x^2 - x))*y + (8*x^4 + 8*x^2 + 1)/(x^3 - x^2) + The underlying algorithm is quite naive and might not terminate in + reasonable time. In particular, the order of the arguments sometimes + has a huge impact on the runtime:: + + sage: u.separating_element([ww,w,v,uu]) # not tested, takes forever + """ from scaled_valuation import ScaledValuation_generic v = self.restriction(self.domain().base()) diff --git a/mapped_valuation.py b/mapped_valuation.py index 5c28b6f4f4b..db6760f2630 100644 --- a/mapped_valuation.py +++ b/mapped_valuation.py @@ -400,6 +400,7 @@ def _weakly_separating_element(self, other): sage: v = pAdicValuation(QQ, 5) sage: u,uu = v.extensions(L) sage: u.separating_element([w,uu]) # indirect doctest + 1/20*t + 7/20 """ if isinstance(other, FiniteExtensionFromInfiniteValuation): diff --git a/valuation_space.py b/valuation_space.py index 3e5c9860968..8cd4e4f8e73 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -741,7 +741,8 @@ def separating_element(self, others): delta = self._strictly_separating_element(others[i]) if others[i](ret) == 0: - # combining powers of ret and delta, we produce a separating element for self and others[:i+1] + # combining powers of ret and delta, we produce a + # separating element for self and others[:i+1] factor = ret ret = delta while any(other(ret) >= 0 for other in others[:i]): @@ -752,9 +753,13 @@ def separating_element(self, others): # and has negative valuation with respect to others[:i] from sage.rings.all import NN for r in iter(NN): + # When we enter this loop we are essentially out of + # luck. The size of the coefficients is likely going + # through the roof here and this is not going to + # terminate in reasonable time. factor = (ret**r)/(1+ret**r) ret = factor * delta - if others[i](ret) < 0 and all([other(ret) < 0 for other in others[:i]]): + if all([other(ret) < 0 for other in others[:i+1]]): break return ret @@ -817,7 +822,8 @@ def _strictly_separating_element(self, other): assert(dd/nn > d/n) for b in iter(NN): # we naĩvely find the smallest b which can satisfy such an equation - # there are faster algorithms for this https://dl.acm.org/citation.cfm?id=1823943&CFID=864015776&CFTOKEN=26270402 + # there are faster algorithms for this + # https://dl.acm.org/citation.cfm?id=1823943&CFID=864015776&CFTOKEN=26270402 if b == 0: continue assert(b <= n + nn) # (a+b)/(n+nn) is a solution From 11e53be9b6abaaa994351c23866498c5eb734e74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 13 Nov 2016 12:37:10 -0500 Subject: [PATCH 122/740] Try harder to find an unused variable name when creating a field extension --- augmented_valuation.py | 61 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index c6a117a93e7..4915ab43da2 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -849,6 +849,39 @@ def scale(self, scalar): return self._base_valuation.scale(scalar).augmentation(self.phi(), scalar*self._mu) return super(AugmentedValuation_base, self).scale(scalar) + def _residue_ring_generator_name(self): + r""" + Return a name for a generator of the residue ring. + + This method is used by :meth:`residue_ring` to work around name clashes + with names in subrings of the residue ring. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, 1) + sage: w._residue_ring_generator_name() + 'u1' + + """ + base = self._base_valuation.residue_ring().base() + # we need a name for a generator that is not present already in base + generator = 'u' + str(len(self.augmentation_chain()) - 1) + while True: + try: + base(generator) + generator = 'u' + generator + except NameError: + # use this name, it has no meaning in base + return generator + except TypeError: + # use this name, base can not handle strings, so hopefully, + # there are no variable names (such as in QQ or GF(p)) + return generator + + class FinalAugmentedValuation(AugmentedValuation_base, FinalInductiveValuation): r""" An augmented valuation which can not be augmented anymore, either because @@ -912,20 +945,33 @@ def residue_ring(self): sage: w.residue_ring() Finite Field of size 2 + TESTS: + + We avoid clashes in generator names:: + + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, x^2 + 2) + sage: R. = K[] + sage: L. = K.extension(y^2 + x^2) + sage: w = v.extension(L) + sage: w.residue_field() + Number Field in uu1 with defining polynomial y^2 - 2 over its base field + sage: w.residue_field().base_field() + Number Field in u1 with defining polynomial x^2 + 2 + """ # the following is correct, even if the polynomial ring is not over a field - generator = 'u' + str(len(self.augmentation_chain()) - 1) - base = self._base_valuation.residue_ring().base() if self.psi().degree() > 1: + generator = self._residue_ring_generator_name() + return base.extension(self.psi(), names=generator) + else: # Do not call extension() if self.psi().degree() == 1: # In that case the resulting field appears to be the same as the original field, # however, it is not == to the original field (for finite fields at # least) but a distinct copy (this is a bug in finite field's # extension() implementation.) - return base.extension(self.psi(), names=generator) - else: return base def reduce(self, f): @@ -1161,16 +1207,17 @@ def residue_ring(self): if self.domain().base() not in Fields(): raise NotImplementedError("only implemented for polynomial rings over fields") - generator = 'u' + str(len(self.augmentation_chain()) - 1) - base = self._base_valuation.residue_ring().base() if self.psi().degree() > 1: + generator = self._residue_ring_generator_name() + base = base.extension(self.psi(), names=generator) + else: # Do not call extension() if self.psi().degree() == 1: # In that case the resulting field appears to be the same as the original field, # however, it is not == to the original field (for finite fields at # least) but a distinct copy (this is a bug in finite field's # extension() implementation.) - base = base.extension(self.psi(), names=generator) + pass return base[self.domain().variable_name()] def reduce(self, f): From 185a1c3dadcb4d9f7e72a1662375e6514ebcb907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 16 Nov 2016 15:05:19 -0500 Subject: [PATCH 123/740] Fix equivalence_decomposition() to use the reciprocal of the equivalence unit which is in general not the same as the equivalence unit of the negative valuation. --- inductive_valuation.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index 88aa16a733a..f94e6841d5b 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -1080,7 +1080,7 @@ def equivalence_decomposition(self, f, partial=False): from sage.misc.all import prod unit *= self.lift(self.residue_ring()(prod([ psi.leading_coefficient()**e for psi,e in F ]))) F = [(self.lift_to_key(psi/psi.leading_coefficient()),e) for psi,e in F] - unit *= prod([self.equivalence_unit(-self(g))**e for g,e in F]) + unit *= prod([self.equivalence_reciprocal(self.equivalence_unit(self(g)))**e for g,e in F]) if phi_divides: for i,(g,e) in enumerate(F): @@ -1190,6 +1190,11 @@ def lift_to_key(self, F): :meth:`augmentation` with this polynomial adjoins a root of ``F`` to the resulting :meth:`residue_ring`. + More specifically, if ``F`` is not the generator of the residue ring, + then multiplying ``f`` with the :meth:`equivalence_reciprocal` of the + :meth:`equivalence_unit` of the valuation of ``f``, produces a unit + which reduces to ``F``. + EXAMPLES:: sage: from mac_lane import * # optional: standalone @@ -1238,11 +1243,20 @@ def _test_lift_to_key(self, **options): tester.assertIs(f.parent(), self.domain()) tester.assertTrue(self.is_key(f)) + # check that augmentation produces a valuation with roots of F + # in the residue ring from sage.rings.all import infinity w = self.augmentation(f, infinity) F = F.change_ring(w.residue_ring()) roots = F.roots(multiplicities=False) tester.assertGreaterEqual(len(roots), 1) + + # check that f has the right reduction + if F == F.parent().gen(): + tester.assertTrue(self.is_equivalent(f, self.phi())) + else: + tester.assertEqual(self.reduce(f * self.equivalence_reciprocal(self.equivalence_unit(self(f)))), F) + def _test_is_equivalence_irreducible(self, **options): r""" From 80b189e800f5a997209666bc95cb310fa201d1af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 16 Nov 2016 15:05:57 -0500 Subject: [PATCH 124/740] MacLane works for squarefree polynomials --- limit_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/limit_valuation.py b/limit_valuation.py index ff21d6ba987..8c30f965cf5 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -355,7 +355,7 @@ def _repr_(self): class MacLaneLimitValuation(LimitValuation_generic, InfiniteDiscretePseudoValuation): r""" A limit valuation that is a pseudo-valuation on polynomial ring `K[x]` - which sends an irreducible polynomial `G` to infinity. + which sends a square-free polynomial `G` to infinity. This uses the MacLane algorithm to compute the next element in the limit. From f0922c79cf453a126c1ddc326b9a63e4065eacb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 17 Nov 2016 00:12:30 -0500 Subject: [PATCH 125/740] Implement valuations with v(1/x) > 0 which are not trivial on the base field --- __init__.py | 2 +- function_field_valuation.py | 231 ++++++++++++++++++++++++++---------- 2 files changed, 167 insertions(+), 66 deletions(-) diff --git a/__init__.py b/__init__.py index 365503c923d..6815e881bea 100644 --- a/__init__.py +++ b/__init__.py @@ -39,7 +39,7 @@ # local file and the instances that come from the mac_lane import define # different types) from trivial_valuation import TrivialDiscreteValuation, TrivialDiscretePseudoValuation -from function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldFromLimitValuation, InfiniteRationalFunctionFieldValuation, FiniteRationalFunctionFieldValuation, NonClassicalRationalFunctionFieldValuation, FunctionFieldMappedValuation_base, FunctionFieldExtensionMappedValuation +from function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldFromLimitValuation, InfiniteRationalFunctionFieldValuation, FiniteRationalFunctionFieldValuation, NonClassicalRationalFunctionFieldValuation, FunctionFieldMappedValuation_base, FunctionFieldExtensionMappedValuation, RationalFunctionFieldMappedValuation from limit_valuation import LimitValuation, MacLaneLimitValuation, LimitValuation_generic from mapped_valuation import MappedValuation_base, FiniteExtensionFromLimitValuation, FiniteExtensionFromInfiniteValuation, MappedValuation_base from augmented_valuation import FiniteAugmentedValuation, InfiniteAugmentedValuation diff --git a/function_field_valuation.py b/function_field_valuation.py index ba104bd92e0..d05a0932f3c 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -82,6 +82,16 @@ sage: w = v.extensions(L) # long time sage: TestSuite(w).run() # long time +Run test suite for a valuation with `v(1/x) > 0` which does not come from a +classical valuation of the infinite place:: + + sage: K. = FunctionField(QQ) + sage: R. = QQ[] + sage: w = GaussValuation(R, pAdicValuation(QQ, 2)).augmentation(x, 1) + sage: w = FunctionFieldValuation(K, w) + sage: v = FunctionFieldValuation(K, (w, K.hom([~K.gen()]), K.hom([~K.gen()]))) + sage: TestSuite(v).run() # long time + Run test suite for extensions which come from the splitting in the base field:: sage: K. = FunctionField(QQ) @@ -198,6 +208,18 @@ class FunctionFieldValuationFactory(UniqueFactory): sage: v = FunctionFieldValuation(K, w); v 2-adic valuation + The same is possible for valuations with `v(1/x) > 0` by passing in an + extra pair of parameters, an isomorphism between this function field and an + isomorphic function field. That way you can, for example, indicate that the + valuation is to be understood as a valuation on `K[1/x]`, i.e., after + applying the substitution `x \mapsto 1/x` (here, the inverse map is also `x + \mapsto 1/x`):: + + sage: w = GaussValuation(R, pAdicValuation(QQ, 2)).augmentation(x, 1) + sage: w = FunctionFieldValuation(K, w) + sage: v = FunctionFieldValuation(K, (w, K.hom([~K.gen()]), K.hom([~K.gen()]))); v + Valuation on rational function field induced by [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ] (in Rational function field in x over Rational Field after x |--> 1/x) + Note that classical valuations at finite places or the infinite place are always normalized such that the uniformizing element has valuation 1:: @@ -253,17 +275,26 @@ def create_key_and_extra_args(self, domain, prime): sage: w = GaussValuation(R, TrivialValuation(QQ)).augmentation(x - 1, 1) sage: FunctionFieldValuation(K, w) is v True + + The normalization is, however, not smart enough, to unwrap + substitutions that turn out to be trivial:: + sage: w = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = FunctionFieldValuation(K, w) + sage: w is FunctionFieldValuation(K, (w, K.hom([~K.gen()]), K.hom([~K.gen()]))) + False + """ from sage.categories.function_fields import FunctionFields from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace if domain not in FunctionFields(): raise ValueError("Domain must be a function field.") - if isinstance(prime, tuple) and len(prime) == 3: - # prime is a triple of a valuation on another function field with - # isomorphism information - return self.create_key_and_extra_args_from_valuation_on_isomorphic_field(domain, prime[0], prime[1], prime[2]) + if isinstance(prime, tuple): + if len(prime) == 3: + # prime is a triple of a valuation on another function field with + # isomorphism information + return self.create_key_and_extra_args_from_valuation_on_isomorphic_field(domain, prime[0], prime[1], prime[2]) if prime in DiscretePseudoValuationSpace(domain): # prime is already a valuation of the requested domain @@ -329,7 +360,7 @@ def create_key_and_extra_args_from_place(self, domain, generator): return self.create_key_and_extra_args(domain, valuation) elif generator == ~domain.gen(): # generator is 1/x, the infinite place - return (domain, (FunctionFieldValuation(domain, domain.gen()), ~domain.gen(), ~domain.gen())), {} + return (domain, (FunctionFieldValuation(domain, domain.gen()), domain.hom(~domain.gen()), domain.hom(~domain.gen()))), {} else: raise ValueError("a place must be given by an irreducible polynomial or the inverse of the generator; %r does not define a place over %r"%(generator, domain)) @@ -377,11 +408,10 @@ def create_key_and_extra_args_from_valuation(self, domain, valuation): raise NotImplementedError("extension of valuation from %r to %r not implemented yet"%(valuation.domain(), domain)) - def create_key_and_extra_args_from_valuation_on_isomorphic_field(self, domain, valuation, gen_image, gen_preimage): + def create_key_and_extra_args_from_valuation_on_isomorphic_field(self, domain, valuation, to_valuation_domain, from_valuation_domain): r""" Create a unique key which identifies the valuation which is - ``valuation`` after mapping the generator of this function field to - ``gen_image``. + ``valuation`` after mapping through ``to_valuation_domain``. TESTS:: @@ -393,22 +423,43 @@ def create_key_and_extra_args_from_valuation_on_isomorphic_field(self, domain, v sage: w = v.extension(L) # indirect doctest """ - # equality is probably broken if we allow for trivial maps - assert(gen_image != domain.gen()) - - if gen_image not in valuation.domain(): - raise ValueError("gen_image must be an element of the domain of valuation but %r is not in %r"%(gen_image, valuation.domain())) - gen_image = valuation.domain()(gen_image) - - if gen_preimage not in domain: - raise ValueError("gen_preimage must be an element of the domain but %r is not in %r"%(gen_preimage, domain)) - gen_preimage = domain(gen_preimage) + from sage.categories.function_fields import FunctionFields + if valuation.domain() not in FunctionFields(): + raise ValueError("valuation must be defined over an isomorphic function field but %r is not a function field"%(valuation.domain(),)) + + from sage.categories.homset import Hom + if to_valuation_domain not in Hom(domain, valuation.domain()): + raise ValueError("to_valuation_domain must map from %r to %r but %r maps from %r to %r"%(domain, valuation.domain(), to_valuation_domain, to_valuation_domain.domain(), to_valuation_domain.codomain())) + if from_valuation_domain not in Hom(valuation.domain(), domain): + raise ValueError("from_valuation_domain must map from %r to %r but %r maps from %r to %r"%(valuation.domain(), domain, from_valuation_domain, from_valuation_domain.domain(), from_valuation_domain.codomain())) + + if domain is domain.base(): + # over rational function fields, we only support the map x |--> 1/x with another rational function field + if valuation.domain() is not valuation.domain().base() or valuation.domain().constant_base_field() != domain.constant_base_field(): + raise NotImplementedError("maps must be isomorphisms with a rational function field over the same base field, not with %r"%(valuation.domain(),)) + if to_valuation_domain != domain.hom([~valuation.domain().gen()]): + raise NotImplementedError("to_valuation_domain must be the map %r not %r"%(domain.hom([~valuation.domain().gen()]), to_valuation_domain)) + if from_valuation_domain != valuation.domain().hom([~domain.gen()]): + raise NotImplementedError("from_valuation_domain must be the map %r not %r"%(valuation.domain().hom([domain.gen()]), from_valuation_domain)) + if domain != valuation.domain(): + # make it harder to create different representations of the same valuation + # (nothing bad happens if we did, but >= and <= are only implemented when this is the case.) + raise NotImplementedError("domain and valuation.domain() must be the same rational function field but %r is not %r"%(domain, valuation.domain())) + else: + if domain.base() is not valuation.domain().base(): + raise NotImplementedError("domain and valuation.domain() must have the same base field but %r is not %r"%(domain.base(), valuation.domain().base())) + if to_valuation_domain != domain.hom([to_valuation_domain(domain.gen())]): + raise NotImplementedError("to_valuation_domain must be trivial on the base fields but %r is not %r"%(to_valuation_domain, domain.hom([to_valuation_domain(domain.gen())]))) + if from_valuation_domain != valuation.domain().hom([from_valuation_domain(valuation.domain().gen())]): + raise NotImplementedError("from_valuation_domain must be trivial on the base fields but %r is not %r"%(from_valuation_domain, valuation.domain().hom([from_valuation_domain(valuation.domain().gen())]))) + if to_valuation_domain(domain.gen()) == valuation.domain().gen(): + raise NotImplementedError("to_valuation_domain seems to be trivial but trivial maps would currently break partial orders of valuations") - if valuation.domain() is valuation.domain().base(): - if gen_image != ~domain.gen() or gen_preimage != ~domain.gen() or valuation != FunctionFieldValuation(domain, domain.gen()): - raise NotImplementedError("mapped valuations on rational function fields are not supported") + if from_valuation_domain(to_valuation_domain(domain.gen())) != domain.gen(): + # only a necessary condition + raise ValueError("to_valuation_domain and from_valuation_domain are not inverses of each other") - return (domain, (valuation, gen_image, gen_preimage)), {} + return (domain, (valuation, to_valuation_domain, from_valuation_domain)), {} def create_object(self, version, key, **extra_args): r""" @@ -429,12 +480,14 @@ def create_object(self, version, key, **extra_args): parent = DiscretePseudoValuationSpace(domain) if isinstance(valuation, tuple) and len(valuation) == 3: - valuation, gen_image, gen_preimage = valuation - if gen_image == ~domain.gen() and gen_preimage == ~domain.gen() and valuation == FunctionFieldValuation(domain, domain.gen()): - # valuation at the infinite place - return parent.__make_element_class__(InfiniteRationalFunctionFieldValuation)(parent) - else: - return parent.__make_element_class__(FunctionFieldExtensionMappedValuation)(parent, valuation, gen_image, gen_preimage) + valuation, to_valuation_domain, from_valuation_domain = valuation + if domain is domain.base() and valuation.domain() is valuation.domain().base() and to_valuation_domain == domain.hom([~valuation.domain().gen()]) and from_valuation_domain == valuation.domain().hom([~domain.gen()]): + # valuation on the rational function field after x |--> 1/x + if valuation == FunctionFieldValuation(valuation.domain(), valuation.domain().gen()): + # the classical valuation at the place 1/x + return parent.__make_element_class__(InfiniteRationalFunctionFieldValuation)(parent) + return parent.__make_element_class__(RationalFunctionFieldMappedValuation)(parent, valuation, to_valuation_domain, from_valuation_domain) + return parent.__make_element_class__(FunctionFieldExtensionMappedValuation)(parent, valuation, to_valuation_domain, from_valuation_domain) if domain is valuation.domain(): # we can not just return valuation in this case @@ -528,15 +581,10 @@ def extensions(self, L): M = K.extension(H, names=L.variable_names()) H_extensions = self.extensions(M) - # turn these extensions into extensions on L by applying the substitutions from sage.rings.morphism import RingHomomorphism_im_gens if type(y_to_u) == RingHomomorphism_im_gens and type(u_to_y) == RingHomomorphism_im_gens: - # both maps are trivial on the constants - gen_preimage = L(u_to_y(u_to_y.domain().gen())) - gen_image = M(y_to_u(y_to_u.domain().gen())) - return [FunctionFieldValuation(L, (w, gen_image, gen_preimage)) for w in H_extensions] - else: - raise NotImplementedError + return [FunctionFieldValuation(L, (w, L.hom([M(y_to_u(y_to_u.domain().gen()))]), M.hom([L(u_to_y(u_to_y.domain().gen()))]))) for w in H_extensions] + raise NotImplementedError return [FunctionFieldValuation(L, w) for w in self.mac_lane_approximants(L.polynomial())] elif L.base() is not L and K.is_subring(L): # recursively call this method for the tower of fields @@ -983,7 +1031,7 @@ class FunctionFieldMappedValuation_base(FunctionFieldValuation_base, MappedValua Valuation at the infinite place """ - def __init__(self, parent, base_valuation, gen_image, gen_preimage): + def __init__(self, parent, base_valuation, to_base_valuation_domain, from_base_valuation_domain): r""" TESTS:: @@ -997,11 +1045,8 @@ def __init__(self, parent, base_valuation, gen_image, gen_preimage): FunctionFieldValuation_base.__init__(self, parent) MappedValuation_base.__init__(self, parent, base_valuation) - self._gen_image = gen_image - self._gen_preimage = gen_preimage - - self._to_base = self.domain().hom(gen_image) - self._from_base = base_valuation.domain().hom(gen_preimage) + self._to_base = to_base_valuation_domain + self._from_base = from_base_valuation_domain def _to_base_domain(self, f): r""" @@ -1039,6 +1084,79 @@ def _from_base_domain(self, f): r""" return self._from_base(f) + def scale(self, scalar): + r""" + Return this valuation scaled by ``scalar``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: w = v.extension(L) + sage: 3*w + 3 * (x)-adic valuation (in Rational function field in x over Finite Field of size 2 after x |--> 1/x) + + """ + from sage.rings.all import QQ + if scalar in QQ and scalar > 0 and scalar != 1: + return FunctionFieldValuation(self.domain(), (self._base_valuation.scale(scalar), self._to_base, self._from_base)) + return super(FunctionFieldMappedValuation_base, self).scale(scalar) + + def _repr_(self): + r""" + Return a printable representation of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = FunctionFieldValuation(K, 1/x) + sage: v.extension(L) # indirect doctest + Valuation at the infinite place + + """ + to_base = repr(self._to_base) + if hasattr(self._to_base, '_repr_defn'): + to_base = self._to_base._repr_defn().replace('\n', ', ') + return "%r (in %r after %s)"%(self._base_valuation, self._base_valuation.domain(), to_base) + +class RationalFunctionFieldMappedValuation(FunctionFieldMappedValuation_base, RationalFunctionFieldValuation_base): + r""" + Valuation on a rational function field that is implemented after a map to + an isomorphic (rational) function field. + + EXAMPLES:: + + sage: from mac_lane import * + sage: K. = FunctionField(QQ) + sage: R. = QQ[] + sage: w = GaussValuation(R, pAdicValuation(QQ, 2)).augmentation(x, 1) + sage: w = FunctionFieldValuation(K, w) + sage: v = FunctionFieldValuation(K, (w, K.hom([~K.gen()]), K.hom([~K.gen()]))); v + Valuation on rational function field induced by [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ] (in Rational function field in x over Rational Field after x |--> 1/x) + + """ + def __init__(self, parent, base_valuation, to_base_valuation_doain, from_base_valuation_domain): + r""" + TESTS:: + + sage: from mac_lane import * + sage: K. = FunctionField(QQ) + sage: R. = QQ[] + sage: w = GaussValuation(R, pAdicValuation(QQ, 2)).augmentation(x, 1) + sage: w = FunctionFieldValuation(K, w) + sage: v = FunctionFieldValuation(K, (w, K.hom([~K.gen()]), K.hom([~K.gen()]))) + sage: isinstance(v, RationalFunctionFieldMappedValuation) + True + + """ + FunctionFieldMappedValuation_base.__init__(self, parent, base_valuation, to_base_valuation_doain, from_base_valuation_domain) + RationalFunctionFieldValuation_base.__init__(self, parent) class InfiniteRationalFunctionFieldValuation(FunctionFieldMappedValuation_base, RationalFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base): r""" @@ -1063,7 +1181,7 @@ def __init__(self, parent): """ x = parent.domain().gen() - FunctionFieldMappedValuation_base.__init__(self, parent, FunctionFieldValuation(parent.domain(), x), 1/x, 1/x) + FunctionFieldMappedValuation_base.__init__(self, parent, FunctionFieldValuation(parent.domain(), x), parent.domain().hom([1/x]), parent.domain().hom([1/x])) RationalFunctionFieldValuation_base.__init__(self, parent) ClassicalFunctionFieldValuation_base.__init__(self, parent) @@ -1088,6 +1206,8 @@ class FunctionFieldExtensionMappedValuation(FunctionFieldMappedValuation_base): another function field which redirects to another ``base_valuation`` on an isomorphism function field `M=K[y]/(H)`. + The isomorphisms must be trivial on ``K``. + EXAMPLES:: sage: from mac_lane import * # optional: standalone @@ -1128,13 +1248,15 @@ def _repr_(self): sage: R. = K[] sage: L. = K.extension(y^2 - 1/x^2 - 1) sage: v = FunctionFieldValuation(K, 1/x) - sage: w = v.extensions(L) + sage: w = v.extensions(L); w + [[ Valuation at the infinite place, v(y - 1) = 2 ]-adic valuation, + [ Valuation at the infinite place, v(y + 1) = 2 ]-adic valuation] """ assert(self.domain().base() is not self.domain()) if repr(self._base_valuation) == repr(self.restriction(self.domain().base())): return repr(self._base_valuation) - return "%r (in %r after %r |--> %r)"%(self._base_valuation, self._base_valuation.domain(), self.domain().gen(), self._gen_image) + return super(FunctionFieldExtensionMappedValuation, self)._repr_() def restriction(self, ring): r""" @@ -1155,24 +1277,3 @@ def restriction(self, ring): if ring.is_subring(self.domain().base()): return self._base_valuation.restriction(ring) return super(FunctionFieldMappedValuation, self).restriction(ring) - - def scale(self, scalar): - r""" - Return this valuation scaled by ``scalar``. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) - sage: w = v.extension(L) - sage: 3*w - 3 * Valuation at the infinite place - - """ - from sage.rings.all import QQ - if scalar in QQ and scalar > 0 and scalar != 1: - return FunctionFieldValuation(self.domain(), (self._base_valuation.scale(scalar), self._gen_image, self._gen_preimage)) - return super(FunctionFieldMappedValuation_base, self).scale(scalar) From e62d4f3451af46825f344c379f53e53b4da9ea8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 17 Nov 2016 02:34:26 -0500 Subject: [PATCH 126/740] fix method call --- padic_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/padic_valuation.py b/padic_valuation.py index d3214a6bd4f..d7b8925e4b1 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -1003,7 +1003,7 @@ def _ge_(self, other): return other.is_discrete_valuation() if isinstance(other, pAdicValuation_int): return self.p() == other.p() - return super(pAdicValuation_base, self)._ge_(self, other) + return super(pAdicValuation_base, self)._ge_(other) class pAdicFromLimitValuation(FiniteExtensionFromLimitValuation, pAdicValuation_base): From 4f9d704aacc36b150184537778cbb0f2af3a5462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 17 Nov 2016 02:34:42 -0500 Subject: [PATCH 127/740] Implement < and > even though there is no total order --- valuation.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/valuation.py b/valuation.py index 7ef77e26277..b1645327f6c 100644 --- a/valuation.py +++ b/valuation.py @@ -129,14 +129,16 @@ def _cmp_(self, other): Since there is no reasonable total order on valuations, this method just throws an exception. - EXAMPLES:: + EXAMPLES: + + However, comparison with the operators ``>`` and ``<`` might still work + when they can fall back to the implementation through ``>=`` and + ``<=``:: sage: from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) - sage: v < v - Traceback (most recent call last): - ... - NotImplementedError: Operator not implemented for this valuation. + sage: v > v + False Note that this does not affect comparison of valuations which do not coerce into a common parent. This is by design in Sage, see @@ -193,12 +195,16 @@ def _richcmp_(self, other, op): False """ + if op == 0: # < + return self <= other and not (self >= other) if op == 1: # <= return self._le_(other) if op == 2: # == return self._eq_(other) if op == 3: # != return not self == other + if op == 4: # > + return self >= other and not (self <= other) if op == 5: # >= return self._ge_(other) raise NotImplementedError("Operator not implemented for this valuation.") From 1aded40e50486bd032335112336a6683b58d462e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 17 Nov 2016 02:35:05 -0500 Subject: [PATCH 128/740] Better handling of scaled valuations --- scaled_valuation.py | 61 +++++++++++++++++++++++++++++++++++++++++++++ valuation.py | 3 +++ 2 files changed, 64 insertions(+) diff --git a/scaled_valuation.py b/scaled_valuation.py index 4432d4f1a41..e9f26399cd3 100644 --- a/scaled_valuation.py +++ b/scaled_valuation.py @@ -282,3 +282,64 @@ def _weakly_separating_element(self, other): """ return self._base_valuation._weakly_separating_element(other) + + def _ge_(self, other): + r""" + Return whether this valuation is greater or equal to ``other``, a + valuation on the same domain. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v2 = pAdicValuation(QQ, 2) + sage: 2*v2 >= v2 + True + sage: v2/2 >= 2*v2 + False + sage: 3*v2 > 2*v2 + True + + Test that non-scaled valuations call through to this method to resolve + the scaling:: + + sage: v2 > v2/2 + True + + """ + if self == other: + return True + if isinstance(other, ScaledValuation_generic): + return (self._scale / other._scale) * self._base_valuation >= other._base_valuation + if self._scale >= 1: + if self._base_valuation >= other: + return True + else: + assert not self.is_trivial() + if self._base_valuation <= other: + return False + return super(ScaledValuation_generic, self)._ge_(other) + + def _le_(self, other): + r""" + Return whether this valuation is smaller or equal to ``other``, a + valuation on the same domain. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v2 = pAdicValuation(QQ, 2) + sage: 2*v2 <= v2 + False + sage: v2/2 <= 2*v2 + True + sage: 3*v2 < 2*v2 + False + + Test that non-scaled valuations call through to this method to resolve + the scaling:: + + sage: v2 < v2/2 + False + + """ + return other / self._scale >= self._base_valuation diff --git a/valuation.py b/valuation.py index b1645327f6c..342bdf8d061 100644 --- a/valuation.py +++ b/valuation.py @@ -295,6 +295,9 @@ def _ge_(self, other): """ if self == other: return True + from scaled_valuation import ScaledValuation_generic + if isinstance(other, ScaledValuation_generic): + return other <= self raise NotImplementedError("Operator not implemented for this valuation.") # Remove the default implementation of Map.__reduce__ that does not play From 0a75103ac7627205e759c73c3deea515c08bb052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 17 Nov 2016 12:57:38 -0500 Subject: [PATCH 129/740] Mac Lane steps can not be performed with respect to equivalence units --- inductive_valuation.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index f94e6841d5b..34ed1e1a256 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -612,7 +612,7 @@ def augmentation(self, phi, mu, check=True): def mac_lane_step(self, G, assume_squarefree=False): r""" Perform an approximation step towards the squarefree monic non-constant - integral polynomial ``G`` with this valuation. + integral polynomial ``G`` which is not an :meth:`equivalence_unit`. This performs the individual steps that are used in :meth:`mac_lane_approximants`. @@ -647,6 +647,9 @@ def mac_lane_step(self, G, assume_squarefree=False): if self(G) < 0: raise ValueError("G must be integral") + if self.is_equivalence_unit(G): + raise ValueError("G must not be an equivalence-unit") + if not assume_squarefree and not G.is_squarefree(): raise ValueError("G must be squarefree") @@ -657,7 +660,7 @@ def mac_lane_step(self, G, assume_squarefree=False): return [self.augmentation(G, infinity)] F = self.equivalence_decomposition(G) - assert len(F), "%s equivalence-decomposese as an equivalence-unit %s"%(G, F) + assert len(F), "%s equivalence-decomposes as an equivalence-unit %s"%(G, F) ret = [] for phi,e in F: From b99f7407f1142f272c3df1e3b798493687525eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 21 Nov 2016 14:36:06 -0500 Subject: [PATCH 130/740] Add LICENSE and .gitignore as generated by github --- .gitignore | 89 ++++++++++++++ LICENSE | 339 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 428 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..72364f99fe4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,89 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# IPython Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# dotenv +.env + +# virtualenv +venv/ +ENV/ + +# Spyder project settings +.spyderproject + +# Rope project settings +.ropeproject diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000000..23cb790338e --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. From 0919b5fd1be4154ee387b79ccf3f71bdfd0914c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 21 Nov 2016 14:41:12 -0500 Subject: [PATCH 131/740] minimal README --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000000..92c2b82e0af --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +Mac Lane infrastructure for discrete (pseudo-)valuations which should work on an unmodified Sage 7.4 or later. + +``` +$ sage +sage: from mac_lane import * +sage: pAdicValuation(QQ, 2) +2-adic valuation +``` From cdb8c3e3bcf7361678deb57454615c3d221077e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 27 Nov 2016 14:08:08 -0500 Subject: [PATCH 132/740] added TODO --- TODO | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO b/TODO index 0d54ebcbc95..0fb6319d690 100644 --- a/TODO +++ b/TODO @@ -1 +1,2 @@ * Check every methods about limitations in the ring case (and correction of the docstring) +* Remove shift(). It is not very useful and not consistent with p-adic shifts. From 9b366a16793a650e4d79cefb18308f5b181e05c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 27 Nov 2016 14:08:31 -0500 Subject: [PATCH 133/740] Make ZqCA[] -> QqCR[] injective via monkey patching --- __init__.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/__init__.py b/__init__.py index 6815e881bea..a5dc9108eab 100644 --- a/__init__.py +++ b/__init__.py @@ -233,8 +233,19 @@ def is_injective(self): sage: QQ['x'].coerce_map_from(ZZ['x']).is_injective() # indirect doctest True + + This should be fixed in + `sage.rings.padics.qadic_flint_CA.pAdicCoercion_CA_frac_field` + instead:: + + sage: R. = ZqCA(9) + sage: R['x'].is_subring(R.fraction_field()['x']) + True """ + if self.underlying_map().codomain() is self.underlying_map().domain().fraction_field(): + # fix this in pAdicCoercion_CA_frac_field and similar + return True return self.underlying_map().is_injective() sage.rings.polynomial.polynomial_ring.PolynomialRing_general._coerce_map_from_ = patch_is_injective(sage.rings.polynomial.polynomial_ring.PolynomialRing_general._coerce_map_from_, {sage.rings.polynomial.polynomial_ring_homomorphism.PolynomialRingHomomorphism_from_base: (lambda morphism: PolynomialRingHomomorphism_from_base_patched(morphism.parent(), morphism.underlying_map()))}) From 8527d3b92565bd1d892b293a8269c9c644e2a001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 27 Nov 2016 14:09:13 -0500 Subject: [PATCH 134/740] Implement extensions for K[x] to K(x) --- inductive_valuation.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/inductive_valuation.py b/inductive_valuation.py index 34ed1e1a256..71470226afa 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -523,6 +523,28 @@ def __init__(self, parent, phi): InductiveValuation.__init__(self, parent, phi) DiscreteValuation.__init__(self, parent) + def extensions(self, other): + r""" + Return the extensions of this valuation to ``other``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: K. = FunctionField(QQ) + sage: v.extensions(K) + [Trivial valuation on Rational Field] + + """ + from sage.categories.function_fields import FunctionFields + if other in FunctionFields() and other.ngens() == 1: + # extend to K[x] and from there to K(x) + v = self.extension(self.domain().change_ring(self.domain().base().fraction_field())) + from function_field_valuation import FunctionFieldValuation + return [FunctionFieldValuation(other, self)] + return super(FiniteInductiveValuation, self).extensions(other) + class NonFinalInductiveValuation(FiniteInductiveValuation, DiscreteValuation): r""" From 7ebf96345a040874417ac830140b541111a50627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 27 Nov 2016 14:09:34 -0500 Subject: [PATCH 135/740] added a long time --- trivial_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trivial_valuation.py b/trivial_valuation.py index 6ffc7c89f28..defce8ad6d3 100644 --- a/trivial_valuation.py +++ b/trivial_valuation.py @@ -121,7 +121,7 @@ class TrivialDiscretePseudoValuation_base(DiscretePseudoValuation): TESTS:: - sage: TestSuite(v).run() + sage: TestSuite(v).run() # long time """ def uniformizer(self): From 7e1b51ea5785c607a722a886d749e4e170be9c28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 27 Nov 2016 14:13:32 -0500 Subject: [PATCH 136/740] K[x] -> K(x) is injective via a monkey-patch --- __init__.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/__init__.py b/__init__.py index a5dc9108eab..ed4724c5756 100644 --- a/__init__.py +++ b/__init__.py @@ -124,6 +124,24 @@ def __init__(self, *args, **kwargs): sage.geometry.newton_polygon.NewtonPolygon_element.sides = lambda self: zip(self.vertices(), self.vertices()[1:]) # implement coercion of function fields that comes from coercion of their base fields + +# Frac(K[x]) injects into K(x) +class DefaultConvertMap_unique_patched2(sage.structure.coerce_maps.DefaultConvertMap_unique): + def is_injective(self): + r""" + TESTS:: + + sage: R. = QQ[] + sage: K. = FunctionField(QQ) + sage: R.is_subring(K) # indirect doctest + True + + """ + from sage.categories.fields import Fields + if self.domain() in Fields(): + return True + raise NotImplementedError + def _coerce_map_from_(target, source): r""" TESTS:: From bc92f147e3b121d9276d28a92be40a99b2f55910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 27 Nov 2016 14:18:36 -0500 Subject: [PATCH 137/740] Implement some is_injective() via monkey patches --- __init__.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index ed4724c5756..2393d798ee5 100644 --- a/__init__.py +++ b/__init__.py @@ -133,7 +133,7 @@ def is_injective(self): sage: R. = QQ[] sage: K. = FunctionField(QQ) - sage: R.is_subring(K) # indirect doctest + sage: R.fraction_field().is_subring(K) # indirect doctest True """ @@ -202,6 +202,10 @@ def _coerce_map_from_(target, source): # canonical, we require the names of the roots to match if source.variable_name() == repr(root): return source.hom([root], base_morphism=base_coercion) + if source is target._ring: + return DefaultConvertMap_unique_patched2(source, target) + if source is target._ring.fraction_field(): + return DefaultConvertMap_unique_patched2(source, target) sage.rings.function_field.function_field.FunctionField._coerce_map_from_ = _coerce_map_from_ del(_coerce_map_from_) @@ -335,6 +339,33 @@ def _coerce_map_from_patched(self, domain): sage.rings.number_field.order.Order._coerce_map_from_ = _coerce_map_from_patched del(_coerce_map_from_patched) +# a ring embeds into its field of fractions +class CallableConvertMap_patched(sage.rings.fraction_field.CallableConvertMap): + def is_injective(self): + r""" + TESTS:: + + sage: R. = QQ[] + sage: R.is_subring(R.fraction_field()) + True + + """ + return True + + def is_surjective(self): + r""" + TESTS:: + + sage: R. = QQ[] + sage: R.fraction_field().coerce_map_from(R).is_surjective() + False + + """ + return False + +sage.rings.fraction_field.CallableConvertMap = CallableConvertMap_patched + + # factorization in polynomial quotient fields def _factor_univariate_polynomial(self, f): r""" From 3a561845d1098d06485b523547741f23dccb8e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 27 Nov 2016 14:31:58 -0500 Subject: [PATCH 138/740] Add value_semigroup() for valuations This is often helpful when we need to decide whether the localization/completion of the domain with respect to the valuation is a DVR or a DVF. --- __init__.py | 2 +- augmented_valuation.py | 44 +++++- gauss_valuation.py | 15 ++ inductive_valuation.py | 4 +- limit_valuation.py | 40 +++++ padic_valuation.py | 20 +++ scaled_valuation.py | 14 ++ valuation_space.py | 103 ++++++++++++- value_group.py | 323 +++++++++++++++++++++++++++++++++++++++-- 9 files changed, 547 insertions(+), 18 deletions(-) diff --git a/__init__.py b/__init__.py index 2393d798ee5..02c86625894 100644 --- a/__init__.py +++ b/__init__.py @@ -27,7 +27,7 @@ import gauss_valuation from gauss_valuation import GaussValuation import value_group -from value_group import DiscreteValuationCodomain, DiscreteValueGroup +from value_group import DiscreteValuationCodomain, DiscreteValueGroup, DiscreteValueSemigroup import function_field_valuation from function_field_valuation import FunctionFieldValuation import augmented_valuation diff --git a/augmented_valuation.py b/augmented_valuation.py index 4915ab43da2..1cbf1d09e97 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -1616,9 +1616,29 @@ def value_group(self): Additive Abelian Group generated by 1/6 """ - base = self._base_valuation.value_group() - from value_group import DiscreteValueGroup - return base + DiscreteValueGroup(self._mu) + return self._base_valuation.value_group() + self._mu + + def value_semigroup(self): + r""" + Return the value semigroup of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Zq(4, 5) + sage: S. = R[] + sage: v = GaussValuation(S) + + sage: w = v.augmentation(x^2 + x + u, 1/2) + sage: w.value_semigroup() + Additive Abelian Semigroup generated by 1/2 + + sage: ww = w.augmentation((x^2 + x + u)^2 + 2, 5/3) + sage: ww.value_semigroup() + Additive Abelian Semigroup generated by 1/2, 5/3 + + """ + return self._base_valuation.value_semigroup() + self._mu def valuations(self, f): """ @@ -1761,6 +1781,24 @@ def value_group(self): """ return self._base_valuation.value_group() + @cached_method + def value_semigroup(self): + """ + Return the value semigroup of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Zq(4, 5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x, infinity) + sage: w.value_semigroup() + Additive Abelian Semigroup generated by 1 + + """ + return self._base_valuation.value_semigroup() + def valuations(self, f): """ Return the valuations of the `f_i\phi^i` in the expansion `f=\sum_i diff --git a/gauss_valuation.py b/gauss_valuation.py index f1ce5ce98a8..40318615033 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -193,6 +193,21 @@ def value_group(self): """ return self._base_valuation.value_group() + def value_semigroup(self): + r""" + Return the value semigroup of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: S. = QQ[] + sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) + sage: v.value_semigroup() + Additive Abelian Semigroup generated by -1, 1 + + """ + return self._base_valuation.value_semigroup() + def _repr_(self): """ Return a printable representation of this valuation. diff --git a/inductive_valuation.py b/inductive_valuation.py index 71470226afa..0487bca371d 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -325,7 +325,7 @@ def element_with_valuation(self, s): """ - def _test_element_with_valuation(self, **options): + def _test_element_with_valuation_inductive_valuation(self, **options): r""" Test the correctness of :meth:`element_with_valuation`. @@ -334,7 +334,7 @@ def _test_element_with_valuation(self, **options): sage: from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) - sage: v._test_element_with_valuation() + sage: v._test_element_with_valuation_inductive_valuation() """ tester = self._tester(**options) diff --git a/limit_valuation.py b/limit_valuation.py index 8c30f965cf5..99898ecc283 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -715,3 +715,43 @@ def _weakly_separating_element(self, other): # if the valuations are sane, it should be possible to separate # them with constants return self.domain()(v._weakly_separating_element(u)) + + def value_semigroup(self): + r""" + Return the value semigroup of this valuation. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: w = v.extension(L) + sage: v = pAdicValuation(QQ, 5) + sage: u,uu = v.extensions(L) # long time + sage: u.value_semigroup() # long time + Additive Abelian Semigroup generated by -1, 1 + + """ + return self._initial_approximation.value_semigroup() + + def element_with_valuation(self, s): + r""" + Return an element with valuation ``s``. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: w = v.extension(L) + sage: v = pAdicValuation(QQ, 2) + sage: u, = v.extensions(L) + sage: u.element_with_valuation(1/2) + t + 1 + + """ + return self._initial_approximation.element_with_valuation(s) diff --git a/padic_valuation.py b/padic_valuation.py index d7b8925e4b1..031f7eba08d 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -23,6 +23,7 @@ sys.path.append(os.path.dirname(os.getcwd())) from valuation import DiscreteValuation +from value_group import DiscreteValueSemigroup from mapped_valuation import FiniteExtensionFromLimitValuation from sage.structure.factory import UniqueFactory from sage.misc.cachefunc import cached_method @@ -701,6 +702,25 @@ def restriction(self, ring): return pAdicValuation(ring, self.p()) + def value_semigroup(self): + r""" + Return the value semigroup of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(GaussianIntegers(), 2) + sage: v.value_semigroup() + Additive Abelian Semigroup generated by 1/2 + + """ + from sage.categories.all import Fields + v = self(self.uniformizer()) + if self.domain() in Fields(): + return DiscreteValueSemigroup([-v,v]) + else: + return DiscreteValueSemigroup([v]) + class pAdicValuation_padic(pAdicValuation_base): """ The `p`-adic valuation of a complete `p`-adic ring. diff --git a/scaled_valuation.py b/scaled_valuation.py index e9f26399cd3..8f827d46337 100644 --- a/scaled_valuation.py +++ b/scaled_valuation.py @@ -343,3 +343,17 @@ def _le_(self, other): """ return other / self._scale >= self._base_valuation + + def value_semigroup(self): + r""" + Return the value semigroup of this valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v2 = pAdicValuation(QQ, 2) + sage: (2*v2).value_semigroup() + Additive Abelian Semigroup generated by -2, 2 + + """ + return self._scale * self._base_valuation.value_semigroup() diff --git a/valuation_space.py b/valuation_space.py index 8cd4e4f8e73..f0ef28f7440 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -363,10 +363,11 @@ def uniformizer(self): """ - def value_group(self, **options): + def value_group(self): r""" - Return the value group of this discrete pseudo-valuation, a - discrete additive subgroup of the rational numbers. + Return the value group of this discrete pseudo-valuation, the + discrete additive subgroup of the rational numbers which is + generated by the valuation of the :meth:`uniformizer`. EXAMPLES:: @@ -387,6 +388,62 @@ def value_group(self, **options): from value_group import DiscreteValueGroup return DiscreteValueGroup(self(self.uniformizer())) + def value_semigroup(self): + r""" + Return the value semigroup of this discrete pseudo-valuation, the + additive subsemigroup of the rational numbers which is generated by + the valuations of the elements in :meth:`domain`. + + EXAMPLES: + + Most commonly, in particular over fields, the semigroup is the + group generated by the valuation of the uniformizer:: + + sage: from mac_lane import * # optional: standalone + sage: G = pAdicValuation(QQ, 2).value_semigroup(); G + Additive Abelian Semigroup generated by -1, 1 + sage: G in AdditiveMagmas().AdditiveAssociative().AdditiveUnital().AdditiveInverse() + True + + If the domain is a discrete valuation ring, then the semigroup + consists of the positive elements of the :meth:`value_group`:: + + sage: pAdicValuation(Zp(2), 2).value_semigroup() + Additive Abelian Semigroup generated by 1 + + The semigroup can have a more complicated structure when the + uniformizer is not in the domain:: + + sage: v = pAdicValuation(ZZ, 2) + sage: R. = ZZ[] + sage: w = GaussValuation(R, v) + sage: u = w.augmentation(x, 5/3) + sage: u.value_semigroup() + Additive Abelian Semigroup generated by 1, 5/3 + + """ + from sage.categories.fields import Fields + if self.domain() in Fields(): + from value_group import DiscreteValueSemigroup + return DiscreteValueSemigroup([]) + self.value_group() + raise NotImplementedError("can not determine value semigroup of %r"%(self,)) + + def element_with_valuation(self, s): + r""" + Return an element in the :meth:`domain` of this valuation with + valuation ``s``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 2) + sage: v.element_with_valuation(10) + 1024 + + """ + s = self.value_semigroup()(s) + return self.shift(1, s) + def shift(self, x, s): r""" Return a modified version of ``x`` whose valuation is increased by ``s``. @@ -1017,6 +1074,46 @@ def _test_value_group(self, **options): # check that the uniformizer generates the value group tester.assertEqual(self.value_group().gen(), self(self.uniformizer())) + def _test_value_semigroup(self, **options): + r""" + Check correctness of the value semigroup. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: v._test_value_semigroup() + + """ + tester = self._tester(**options) + + if self.is_trivial() and not self.is_discrete_valuation(): + # the trivial pseudo-valuation does not have a value semigroup + return + + for s in tester.some_elements(self.value_semigroup().some_elements()): + tester.assertIn(s, self.value_group()) + + def _test_element_with_valuation(self, **options): + r""" + Check correctness of :meth:`element_with_valuation`. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 5) + sage: v._test_element_with_valuation() + + """ + tester = self._tester(**options) + + if self.is_trivial() and not self.is_discrete_valuation(): + # the trivial pseudo-valuation does not have a value semigroup + return + + for s in tester.some_elements(self.value_semigroup().some_elements()): + tester.assertEqual(self(self.element_with_valuation(s)), s) + def _test_shift(self, **options): r""" Check correctness of shifts. diff --git a/value_group.py b/value_group.py index 4936e262d4b..f18599a4783 100644 --- a/value_group.py +++ b/value_group.py @@ -2,14 +2,16 @@ r""" Value groups of discrete valuations -This file defines additive subgroups of \QQ generated by a rational number and -related structures. +This file defines additive sub(semi-)groups of \QQ and related structures. EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: pAdicValuation(QQ, 2).value_group() + sage: v = pAdicValuation(ZZ, 2) + sage: v.value_group() Additive Abelian Group generated by 1 + sage: v.value_semigroup() + Additive Abelian Semigroup generated by 1 AUTHORS: @@ -34,6 +36,7 @@ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.rings.all import QQ, ZZ, infinity +from sage.misc.cachefunc import cached_method class DiscreteValuationCodomain(UniqueRepresentation, Parent): r""" @@ -247,12 +250,14 @@ def __add__(self, other): Additive Abelian Group generated by 2/63 """ - if not isinstance(other, DiscreteValueGroup): - from sage.structure.element import is_Element - if is_Element(other) and QQ.has_coerce_map_from(other.parent()): - return self + DiscreteValueGroup(other) - raise ValueError("`other` must be a DiscreteValueGroup or a rational number") - return DiscreteValueGroup(self._generator.gcd(other._generator)) + if isinstance(other, DiscreteValueGroup): + return DiscreteValueGroup(self._generator.gcd(other._generator)) + if isinstance(other, DiscreteValueSemigroup): + return other + self + from sage.structure.element import is_Element + if is_Element(other) and QQ.has_coerce_map_from(other.parent()): + return self + DiscreteValueGroup(other) + raise ValueError("`other` must be a DiscreteValueGroup or a rational number") def _mul_(self, other, switch_sides=False): r""" @@ -385,3 +390,303 @@ def is_trivial(self): """ return self._generator.is_zero() + +class DiscreteValueSemigroup(UniqueRepresentation, Parent): + r""" + The value semigroup of a discrete valuation, an additive subsemigroup of + \QQ generated by ``generators``. + + INPUT: + + - ``generators`` -- rational numbers + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: D1 = DiscreteValueSemigroup(0); D1 + Trivial Additive Abelian Semigroup + sage: D2 = DiscreteValueSemigroup(4/3); D2 + Additive Abelian Semigroup generated by 4/3 + sage: D3 = DiscreteValueSemigroup([-1/3, 1/2]); D3 + Additive Abelian Semigroup generated by -1/3, 1/2 + + TESTS:: + + sage: TestSuite(D1).run() # long time + sage: TestSuite(D2).run() # long time + sage: TestSuite(D3).run() # long time + + """ + @staticmethod + def __classcall__(cls, generators): + r""" + Normalize ``generators``. + + TESTS: + + We do not find minimal generators or something like that but just sort the + generators and drop generators that are trivially contained in the + semigroup generated by the remaining generators:: + + sage: from mac_lane import * # optional: standalone + sage: DiscreteValueSemigroup([1,2]) is DiscreteValueSemigroup([1]) + True + + In this case, the normalization is not sufficient to determine that + these are the same semigroup:: + + sage: DiscreteValueSemigroup([1,-1,1/3]) is DiscreteValueSemigroup([1/3,-1/3]) + False + + """ + if generators in QQ: + generators = [generators] + generators = list(set([QQ.coerce(g) for g in generators if g != 0])) + generators.sort() + simplified_generators = generators + + # this is not very efficient but there should never be more than a + # couple of generators + for g in generators: + for h in generators: + if g == h: continue + from sage.rings.all import NN + if h/g in NN: + simplified_generators.remove(h) + break + + return super(DiscreteValueSemigroup, cls).__classcall__(cls, tuple(simplified_generators)) + + def __init__(self, generators): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: isinstance(DiscreteValueSemigroup([0]), DiscreteValueSemigroup) + True + + """ + from sage.categories.all import AdditiveMagmas + self._generators = generators + + category = AdditiveMagmas().AdditiveAssociative().AdditiveUnital() + if all([-g in generators for g in generators]): + # check whether this is trivially a group + # is_group() performs a complete check that is very costly and + # refines the category + category = category.AdditiveInverse() + + # We can not set the facade to DiscreteValuationCodomain since there + # are some issues with iterated facades currently + Parent.__init__(self, facade=QQ, category=category) + + def _solve_linear_program(self, target): + r""" + Return the coefficients of a linear combination to write ``target`` in + terms of the generators of this semigroup. + + Return ``None`` if no such combination exists. + + EXAMPLES:: + + sage: D = DiscreteValueSemigroup([2,3,5]) + sage: D._solve_linear_program(12) + {0: 1, 1: 0, 2: 2} + sage: 1*2 + 0*3 + 2*5 + 12 + + """ + if len(self._generators) == 0: + if target == 0: + return {} + else: + return None + + from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException + P = MixedIntegerLinearProgram(maximization=False, solver = "ppl") + x = P.new_variable(integer=True, nonnegative=True) + constraint = sum([g*x[i] for i,g in enumerate(self._generators)]) == target + P.add_constraint(constraint) + P.set_objective(None) + try: + P.solve() + except MIPSolverException: + return None + return P.get_values(x) + + def _element_constructor_(self, x): + r""" + Create an element in this group from ``x``. + + INPUT: + + - ``x`` -- a rational number + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: DiscreteValueSemigroup([])(0) + 0 + sage: DiscreteValueSemigroup([])(1) + Traceback (most recent call last): + ... + ValueError: `1` is not in Trivial Additive Abelian Semigroup. + sage: DiscreteValueSemigroup([1])(1) + 1 + sage: DiscreteValueSemigroup([1])(-1) + Traceback (most recent call last): + ... + ValueError: `-1` is not in Additive Abelian Semigroup generated by 1. + + """ + x = QQ.coerce(x) + if x in self._generators or self._solve_linear_program(x) is not None: + return x + + raise ValueError("`{0}` is not in {1}.".format(x,self)) + + def _repr_(self): + r""" + Return a printable representation for this semigroup. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: DiscreteValueSemigroup(0) # indirect doctest + Trivial Additive Abelian Semigroup + + """ + if self.is_trivial(): + return "Trivial Additive Abelian Semigroup" + return "Additive Abelian Semigroup generated by %s"%(', '.join([repr(g) for g in self._generators]),) + + def __add__(self, other): + r""" + Return the subsemigroup of \QQ generated by this semigroup and ``other``. + + INPUT: + + - ``other`` -- a discrete value (semi-)group or a rational number + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: D = DiscreteValueSemigroup(1/2) + sage: D + 1/3 + Additive Abelian Semigroup generated by 1/3, 1/2 + sage: D + D + Additive Abelian Semigroup generated by 1/2 + sage: D + 1 + Additive Abelian Semigroup generated by 1/2 + sage: DiscreteValueGroup(2/7) + DiscreteValueSemigroup(4/9) + Additive Abelian Semigroup generated by -2/7, 2/7, 4/9 + + """ + if isinstance(other, DiscreteValueSemigroup): + return DiscreteValueSemigroup(self._generators + other._generators) + if isinstance(other, DiscreteValueGroup): + return DiscreteValueSemigroup(self._generators + (other._generator, -other._generator)) + from sage.structure.element import is_Element + if is_Element(other) and QQ.has_coerce_map_from(other.parent()): + return self + DiscreteValueSemigroup(other) + raise ValueError("`other` must be a DiscreteValueGroup, a DiscreteValueSemigroup or a rational number") + + def _mul_(self, other, switch_sides=False): + r""" + Return the semigroup generated by ``other`` times the generators of this + semigroup. + + INPUT: + + - ``other`` -- a rational number + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: D = DiscreteValueSemigroup(1/2) + sage: 1/2 * D + Additive Abelian Semigroup generated by 1/4 + sage: D * (1/2) + Additive Abelian Semigroup generated by 1/4 + sage: D * 0 + Trivial Additive Abelian Semigroup + + """ + other = QQ.coerce(other) + return DiscreteValueSemigroup([g*other for g in self._generators]) + + def gens(self): + r""" + Return the generators of this semigroup. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: DiscreteValueSemigroup(-3/8).gens() + (-3/8,) + + """ + return tuple(self._generators) + + def some_elements(self): + r""" + Return some typical elements in this semigroup. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: list(DiscreteValueSemigroup([-3/8,1/2]).some_elements()) + [0, -3/8, 1/2, ...] + + """ + yield self(0) + if self.is_trivial(): + return + for g in self._generators: + yield g + from sage.rings.all import ZZ + for x in (ZZ**len(self._generators)).some_elements(): + yield QQ.coerce(sum([abs(c)*g for c,g in zip(x,self._generators)])) + + def is_trivial(self): + r""" + Return whether this is the trivial additive abelian semigroup. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: DiscreteValueSemigroup(-3/8).is_trivial() + False + sage: DiscreteValueSemigroup([]).is_trivial() + True + + """ + return len(self._generators) == 0 + + @cached_method + def is_group(self): + r""" + Return whether this semigroup is a group. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: DiscreteValueSemigroup(1).is_group() + False + sage: D = DiscreteValueSemigroup([-1, 1]) + sage: D.is_group() + True + + Invoking this method also changes the category of this semigroup if it + is a group:: + + sage: D in AdditiveMagmas().AdditiveAssociative().AdditiveUnital().AdditiveInverse() + True + + """ + for x in self._generators: + if -x not in self: + return False + else: + self._refine_category_(self.category().AdditiveInverse()) + return True From 92c3871b3c18ea71b15369878e621c756e85d6cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 27 Nov 2016 14:34:30 -0500 Subject: [PATCH 139/740] some doctest markers --- __init__.py | 2 +- value_group.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/__init__.py b/__init__.py index 02c86625894..c4f1f5b67cb 100644 --- a/__init__.py +++ b/__init__.py @@ -346,7 +346,7 @@ def is_injective(self): TESTS:: sage: R. = QQ[] - sage: R.is_subring(R.fraction_field()) + sage: R.is_subring(R.fraction_field()) # indirect doctest True """ diff --git a/value_group.py b/value_group.py index f18599a4783..77ef5ea33e8 100644 --- a/value_group.py +++ b/value_group.py @@ -51,7 +51,7 @@ class DiscreteValuationCodomain(UniqueRepresentation, Parent): TESTS:: - sage: TestSuite(C).run() + sage: TestSuite(C).run() # long time """ def __init__(self): @@ -140,9 +140,9 @@ class DiscreteValueGroup(UniqueRepresentation, Parent): TESTS:: - sage: TestSuite(D1).run() - sage: TestSuite(D2).run() - sage: TestSuite(D3).run() + sage: TestSuite(D1).run() # long time + sage: TestSuite(D2).run() # long time + sage: TestSuite(D3).run() # long time """ @staticmethod From 9543635d9869248313de301291e08c5515afb15a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 27 Nov 2016 14:34:54 -0500 Subject: [PATCH 140/740] allow lifting of p-adic valuations from base fields to extension --- padic_valuation.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/padic_valuation.py b/padic_valuation.py index 031f7eba08d..1b5e7870a49 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -208,8 +208,12 @@ def create_key_for_local_ring(self, R, prime): """ # We do not care much about the value of prime since there is only one # reasonable p-adic valuation here - if prime is not None and R(prime).valuation() <= 0: - raise ValueError("prime must be an element of positive valuation") + if prime is not None: + if prime in R: + if R(prime).valuation() <= 0: + raise ValueError("prime must be an element of positive valuation") + elif prime(R.prime()) <= 0: + raise ValueError("prime must be an element of positive valuation") return (R,) From dfda82017f6b21ce20ff647485b270ad5cd84eb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 27 Nov 2016 14:35:04 -0500 Subject: [PATCH 141/740] clarify the broken shift --- valuation_space.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/valuation_space.py b/valuation_space.py index f0ef28f7440..3b32951e106 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -464,6 +464,20 @@ def shift(self, x, s): ... ValueError: can not shift 2 down by 2 in Integer Ring with respect to 2-adic valuation + Note how this is different from shifting `p`-adic elements where + shifting down removes coefficients of small powers of `p` in the + expansion of an element:: + + sage: R = ZpCA(2) + sage: x = R(7); x + 1 + 2 + 2^2 + O(2^20) + sage: x >> 1 + 1 + 2 + O(2^19) + sage: x >> 2 + 1 + O(2^18) + sage: x >> 3 + O(2^17) + The element is the same after several shifts that produce an element of the original valuation:: From 0be16b90f716fbe6dbefb908f74f0f9d1111cff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 27 Nov 2016 14:35:19 -0500 Subject: [PATCH 142/740] fix typo --- valuation_space.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/valuation_space.py b/valuation_space.py index 3b32951e106..a657492d373 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -536,7 +536,7 @@ def shift(self, x, s): def residue_ring(self): r""" Return the residue ring of this valuation, i.e., the elements of - non-negative valuation module the elements of positive valuation. + non-negative valuation modulo the elements of positive valuation. This is identical to :meth:`residue_field` when a residue field exists. @@ -566,7 +566,7 @@ def residue_field(self): r""" Return the residue field of this valuation, i.e., the field of fractions of the :meth:`residue_ring`, the elements of non-negative - valuation module the elements of positive valuation. + valuation modulo the elements of positive valuation. EXAMPLES:: From d0d2661b37189dff6b015187dd91432a8b4854df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 27 Nov 2016 14:35:38 -0500 Subject: [PATCH 143/740] use QQ.coerce() instead of coerce(QQ,) --- value_group.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/value_group.py b/value_group.py index 77ef5ea33e8..707e024db83 100644 --- a/value_group.py +++ b/value_group.py @@ -93,8 +93,7 @@ def _element_constructor_(self, x): return x if x not in QQ: raise ValueError("must be a rational number or infinity") - from sage.misc.functional import coerce - return coerce(QQ, x) + return QQ.coerce(x) def _repr_(self): r""" @@ -158,8 +157,7 @@ def __classcall__(cls, generator): True """ - from sage.misc.functional import coerce - generator = coerce(QQ, generator) + generator = QQ.coerce(generator) generator = generator.abs() return super(DiscreteValueGroup, cls).__classcall__(cls, generator) @@ -173,7 +171,6 @@ def __init__(self, generator): """ from sage.categories.modules import Modules - from sage.categories.additive_monoids import AdditiveMonoids self._generator = generator # We can not set the facade to DiscreteValuationCodomain since there @@ -206,8 +203,7 @@ def _element_constructor_(self, x): ValueError: `1/2` is not in Additive Abelian Group generated by 1. """ - from sage.misc.functional import coerce - x = coerce(QQ, x) + x = QQ.coerce(x) if x == 0 or (self._generator != 0 and x/self._generator in ZZ): return x @@ -280,8 +276,7 @@ def _mul_(self, other, switch_sides=False): Trivial Additive Abelian Group """ - from sage.misc.functional import coerce - other = coerce(QQ, other) + other = QQ.coerce(other) return DiscreteValueGroup(self._generator*other) def index(self, other): From 6a3126c99c48a5d2dcdb459493a0290c090b4ba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 28 Nov 2016 02:24:03 -0500 Subject: [PATCH 144/740] Remove shift() it is inconsistent with shifts everywhere else (not dropping coefficients) and it also is not really useful as it can not be implemented everywhere. This might cause some performance regression. --- augmented_valuation.py | 65 +------------------ gauss_valuation.py | 58 +---------------- inductive_valuation.py | 4 +- limit_valuation.py | 22 ------- padic_valuation.py | 69 +++----------------- valuation_space.py | 140 +++-------------------------------------- 6 files changed, 19 insertions(+), 339 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 1cbf1d09e97..7caaef90366 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -399,7 +399,7 @@ def equivalence_unit(self, s): sage: w.equivalence_unit(3/2) Traceback (most recent call last): ... - ValueError: 3/2 is not in the value group of 2-adic valuation + ValueError: 3/2 is not in the value semigroup of 2-adic valuation sage: w.equivalence_unit(1) 2 + O(2^6) @@ -462,69 +462,6 @@ def element_with_valuation(self, s): s -= self._mu return ret * self._base_valuation.element_with_valuation(s) - def shift(self, x, s): - r""" - Return a modified version of ``x`` whose valuation is increased by ``s``. - - The element returned is such that repeated shifts which go back to - the original valuation produce the same element in reduction. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) - sage: w = v.augmentation(x, 1) - sage: w.shift(1, 1) - 2 - - Whenever there is ramification, a shift with such consistency is not - possible:: - - sage: w = v.augmentation(x, 1/2) - - sage: w.shift(1, -1/2) - Traceback (most recent call last): - ... - NotImplementedError: Shifts with consistent reduction not implemented for this augmented valuation - - Multiplication by an :meth:`element_with_valuation` might sometimes - produce useful results in such cases:: - - sage: 1 * w.element_with_valuation(-1/2) - 1/2*x - - However, this does not preserve the element in reduction:: - - sage: 1 * w.element_with_valuation(-1/2) * w.element_with_valuation(1/2) - 1/2*x^2 - - In general this is only possible by using an - :meth:`equivalence_unit` and its :meth:`equialence_reciprocal`. - These do, however, not exist for all values of ``s``. - - """ - from sage.categories.fields import Fields - if not self.domain().base_ring() in Fields(): - raise NotImplementedError("only implemented for polynomial rings over fields") - - if s not in self.value_group(): - raise ValueError("s must be in the value group of the valuation") - - if self.value_group() == self._base_valuation.value_group(): - return self._base_valuation.shift(x, s) - - if self._base_valuation.value_group().is_trivial(): - # We could implement a consistent shift in this case by multplying - # and dividing by powers of the key polynomial. Since an element of - # positive valuation has to be a power of the key polynomial, there - # can be no ambiguity here - raise NotImplementedError("Shifts with consistent reduction not implemented for augmented valuations over trivial valuations") - - # Except for very few special cases, it is not possible to implement a - # consistent shift for augmented valuations - raise NotImplementedError("Shifts with consistent reduction not implemented for this augmented valuation") - def _repr_(self): """ Return a printable representation of this valuation. diff --git a/gauss_valuation.py b/gauss_valuation.py index 40318615033..f1b81755fcf 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -242,61 +242,6 @@ def uniformizer(self): """ return self.domain()(self._base_valuation.uniformizer()) - def shift(self, f, s): - """ - Multiply ``f`` by a power of the uniformizer which has valuation ``s``. - - INPUT: - - - ``f`` -- a polynomial in the domain of this valuation - - - ``s`` -- an element of the :meth:`value_group` - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: S. = QQ[] - sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) - sage: v.shift(x, -2) - 1/25*x - - It is an error to perform a shift if the result is not in the domain of - the valuation anymore:: - - sage: S. = Zp(2,5)[] - sage: v = GaussValuation(S) - sage: f = v.shift(x, 2); f - (2^2 + O(2^7))*x - sage: f.parent() is S - True - sage: f = v.shift(x, -2) - Traceback (most recent call last): - ... - ValueError: since 2-adic Ring with capped relative precision 5 is not a field, -s must not exceed the valuation of f but 2 does exceed 0 - - Of course, the above example works over a field:: - - sage: S. = Qp(2,5)[] - sage: v = GaussValuation(S) - sage: f = v.shift(x, -2); f - (2^-2 + O(2^3))*x - - """ - f = self.domain().coerce(f) - s = self.value_group()(s) - - if s == 0: - return f - - from sage.categories.fields import Fields - if -s > self(f) and self.domain().base_ring() not in Fields(): - raise ValueError("since %r is not a field, -s must not exceed the valuation of f but %r does exceed %r"%(self.domain().base_ring(), -s, self(f))) - - if f == 0: - raise ValueError("can not shift zero") - - return f.map_coefficients(lambda c:self._base_valuation.shift(c, s)) - def valuations(self, f): """ Return the valuations of the `f_i\phi^i` in the expansion `f=\sum f_i\phi^i`. @@ -478,8 +423,7 @@ def equivalence_unit(self, s): (3^-2 + O(3^3)) """ - one = self._base_valuation.domain().one() - ret = self._base_valuation.shift(one, s) + ret = self._base_valuation.element_with_valuation(s) return self.domain()(ret) def element_with_valuation(self, s): diff --git a/inductive_valuation.py b/inductive_valuation.py index 0487bca371d..6a3991ef88d 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -209,7 +209,7 @@ def equivalence_unit(self, s): sage: w.equivalence_unit(-1) Traceback (most recent call last): ... - ValueError: can not shift 1 down by 1 in Integer Ring with respect to 2-adic valuation + ValueError: s must be in the value semigroup of this valuation but -1 is not in Additive Abelian Semigroup generated by 1 """ @@ -321,7 +321,7 @@ def element_with_valuation(self, s): sage: v.element_with_valuation(-2) Traceback (most recent call last): ... - ValueError: can not shift 1 down by 2 in Integer Ring with respect to 2-adic valuation + ValueError: s must be in the value semigroup of this valuation but -2 is not in Additive Abelian Semigroup generated by 1 """ diff --git a/limit_valuation.py b/limit_valuation.py index 99898ecc283..8fcf2059faf 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -416,28 +416,6 @@ def lift(self, F): F = self.residue_ring().coerce(F) return self._initial_approximation.lift(F) - def shift(self, x, s): - r""" - Return a modified version of ``x`` whose valuation is increased by ``s``. - - The element returned is such that repeated shifts which go back to - the original valuation produce the same element in reduction. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - (x - 1)) - - sage: v = FunctionFieldValuation(K, 0) - sage: w = v.extension(L) - sage: w.shift(y, 1) - x*y - - """ - return self._initial_approximation.shift(x, s) - def uniformizer(self): r""" Return a uniformizing element for this valuation. diff --git a/padic_valuation.py b/padic_valuation.py index 1b5e7870a49..8e59e1f50b4 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -815,82 +815,29 @@ def uniformizer(self): """ return self.domain().uniformizer() - def shift(self, c, v): + def element_with_valuation(self, v): """ - Multiply ``c`` by a power of a uniformizer such that its valuation - changes by ``v``. + Return an element of valuation ``v``. INPUT: - - ``c`` -- an element of the domain of this valuation - - - ``v`` -- an integer - - OUTPUT: - - If the resulting element has negative valation, it will be brought to - the fraction field, otherwise it is an element of the domain. + - ``v`` -- an element of the :meth:`value_semigroup` of this valuation EXAMPLES:: sage: from mac_lane import * # optional: standalone sage: R = Zp(3) sage: v = pAdicValuation(Zp(3)) - sage: v.shift(R(2),3) - 2*3^3 + O(3^23) - - TESTS: - - Make sure that we work around the following bug in p-adics:: - - sage: R = Zp(3) - sage: R(1) >> 1 # this should throw an exception - O(3^19) - - However, our shift gets this right:: - - sage: v = pAdicValuation(Zp(3)) - sage: v.shift(R(1), -1) - Traceback (most recent call last): - ... - ValueError: can not shift down 1 + O(3^20) by 1 in 3-adic Ring with capped relative precision 20 with respect to 3-adic valuation - - Note that the same happens for ZpCA:: - - sage: R = ZpCA(3) - sage: R(1) >> 1 # this should throw an exception - O(3^19) - - But we also detect that one:: - - sage: v = pAdicValuation(ZpCA(3)) - sage: v.shift(R(1), -1) - Traceback (most recent call last): - ... - ValueError: can not shift down 1 + O(3^20) by 1 in 3-adic Ring with capped absolute precision 20 with respect to 3-adic valuation + sage: v.element_with_valuation(3) + 3^3 + O(3^23) """ from sage.rings.all import QQ, ZZ - c = self.domain().coerce(c) - if c == 0: - if v == 0: - return c - raise ValueError("can not shift a zero") - v = QQ(v) - if v not in self.value_group(): - raise ValueError("%r is not in the value group of %r"%(v, self)) + if v not in self.value_semigroup(): + raise ValueError("%r is not in the value semigroup of %r"%(v, self)) v = ZZ(v * self.domain().ramification_index()) - - # Work around a bug in ZpCR/ZpCA p-adics - # TODO: fix this upstream - from sage.rings.padics.padic_capped_absolute_element import pAdicCappedAbsoluteElement - from sage.rings.padics.padic_capped_relative_element import pAdicCappedRelativeElement - from sage.categories.fields import Fields - if (isinstance(c, pAdicCappedAbsoluteElement) or (isinstance(c, pAdicCappedRelativeElement) and c.parent() not in Fields())) and -v > self(c): - raise ValueError("can not shift down %r by %r in %r with respect to %r"%(c, -v, self.domain(), self)) - - return c<> 1 - 1 + 2 + O(2^19) - sage: x >> 2 - 1 + O(2^18) - sage: x >> 3 - O(2^17) - - The element is the same after several shifts that produce an - element of the original valuation:: - - sage: v.shift(v.shift(1, 10), -10) - 1 - - However, this is not always possible unless we are over a field:: - - sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) - sage: w = v.augmentation(x, 1/2) - - sage: w.shift(1, -1/2) - Traceback (most recent call last): - ... - NotImplementedError: Shifts with consistent reduction not implemented for this augmented valuation - - Of course, we could return ``x/2`` here, but what would - ``v.shift(1, -1)`` return? ``x^2/4`` or rather ``1/2``? - There is no way to make this consistent in general unless we go to - the fraction field of ``R``. - - Multiplication by an :meth:`element_with_valuation` might sometimes - produce useful results in such cases:: - - sage: 1 * w.element_with_valuation(-1/2) - 1/2*x - - However, this does not preserve the element in reduction:: - - sage: 1 * w.element_with_valuation(-1/2) * w.element_with_valuation(1/2) - 1/2*x^2 - - In general this is only possible by using an - :meth:`equivalence_unit` and its :meth:`equialence_reciprocal`. - These do, however, not exist for all values of ``s``. - - """ - x = self.domain().coerce(x) from sage.rings.all import QQ, ZZ, infinity s = QQ.coerce(s) + if s not in self.value_semigroup(): + raise ValueError("s must be in the value semigroup of this valuation but %r is not in %r"%(s, self.value_semigroup())) if s == 0: - return x - if x == 0: - raise ValueError("can not shift zero") - if s not in self.value_group(): - raise ValueError("s must be in the value group of this valuation") - if s < 0 and ~self.uniformizer() not in self.domain(): - from sage.categories.fields import Fields - assert(self.domain() not in Fields()) - if -s > self(x): - raise ValueError("can not shift %r down by %r in %r with respect to %r"%(x, -s, self.domain(), self)) - return self.domain()(x * (self.domain().fraction_field()(self.uniformizer()) ** ZZ(s/self.value_group().gen()))) + return self.domain().one() + exp = s/self.value_group().gen() + if exp not in ZZ: + raise NotImplementedError("s must be a multiple of %r but %r is not"%(self.value_group().gen(), s)) + return self.domain()(self.uniformizer() ** ZZ(exp)) @abstract_method def residue_ring(self): @@ -1128,52 +1048,6 @@ def _test_element_with_valuation(self, **options): for s in tester.some_elements(self.value_semigroup().some_elements()): tester.assertEqual(self(self.element_with_valuation(s)), s) - def _test_shift(self, **options): - r""" - Check correctness of shifts. - - TESTS:: - - sage: from mac_lane import * # optional: standalone - sage: v = pAdicValuation(QQ, 5) - sage: v._test_shift() # long time - - """ - tester = self._tester(**options) - - if self.is_trivial(): - # trivial valuations can not perform non-trivial shifts - return - - S = self.domain().some_elements() - V = self.value_group().some_elements() - from itertools import product - for x, n in tester.some_elements(product(S,V)): - if x == 0 and n != 0: - with tester.assertRaises((ValueError, NotImplementedError)): - self.shift(x, n) - continue - - v = self(x) - try: - y = self.shift(x, n) - except NotImplementedError: - # not all valuations can implement consistent shifts - continue - except ValueError: - from sage.categories.fields import Fields - if -n > v and self.domain() not in Fields(): - continue - raise - - tester.assertIs(y.parent(), self.domain()) - tester.assertEqual(self(y), v + n) - # shifts preserve reductions - z = self.shift(y, -n) - tester.assertEqual(self(z), v) - if v >= 0: - tester.assertEqual(self.reduce(z), self.reduce(x)) - def _test_residue_ring(self, **options): r""" Check the correctness of residue rings. From ff01e8ee50f94783b77c4b470fd178a795b85d0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 28 Nov 2016 02:25:03 -0500 Subject: [PATCH 145/740] faster solver for semigroups --- value_group.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/value_group.py b/value_group.py index 707e024db83..d17e4ce704e 100644 --- a/value_group.py +++ b/value_group.py @@ -497,8 +497,22 @@ def _solve_linear_program(self, target): else: return None + if len(self._generators) == 1: + from sage.rings.all import NN + exp = target / self._generators[0] + if exp not in NN: + return None + return {0 : exp} + + if len(self._generators) == 2 and self._generators[0] == - self._generators[1]: + from sage.rings.all import ZZ + exp = target / self._generators[0] + if exp not in ZZ: + return None + return {0: exp, 1: 0} + from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException - P = MixedIntegerLinearProgram(maximization=False, solver = "ppl") + P = MixedIntegerLinearProgram(maximization=False, solver="ppl") x = P.new_variable(integer=True, nonnegative=True) constraint = sum([g*x[i] for i,g in enumerate(self._generators)]) == target P.add_constraint(constraint) From 94dc385b9c49368afa8c315b31b3c783339aa996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 28 Nov 2016 05:34:49 -0500 Subject: [PATCH 146/740] fix extensions of inductive valuations to function fields --- inductive_valuation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index 6a3991ef88d..c06d336408b 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -530,8 +530,8 @@ def extensions(self, other): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: R. = ZZ[] + sage: v = GaussValuation(R, TrivialValuation(ZZ)) sage: K. = FunctionField(QQ) sage: v.extensions(K) [Trivial valuation on Rational Field] @@ -542,7 +542,7 @@ def extensions(self, other): # extend to K[x] and from there to K(x) v = self.extension(self.domain().change_ring(self.domain().base().fraction_field())) from function_field_valuation import FunctionFieldValuation - return [FunctionFieldValuation(other, self)] + return [FunctionFieldValuation(other, v)] return super(FiniteInductiveValuation, self).extensions(other) From c3e7d73d0ec5b483d52860ce0bacf1b1694b2815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 29 Nov 2016 01:29:09 -0500 Subject: [PATCH 147/740] Implement shift() again it has a well-defined meaning (in most cases) --- padic_valuation.py | 30 ++++++++++++++++ valuation_space.py | 86 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/padic_valuation.py b/padic_valuation.py index 8e59e1f50b4..99d7cb5eecb 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -725,6 +725,7 @@ def value_semigroup(self): else: return DiscreteValueSemigroup([v]) + class pAdicValuation_padic(pAdicValuation_base): """ The `p`-adic valuation of a complete `p`-adic ring. @@ -881,6 +882,35 @@ def residue_ring(self): """ return self.domain().residue_field() + def shift(self, x, s): + r""" + Shift ``x`` in its expansion with respect to :meth:`uniformizer` by + ``s`` "digits". + + For non-negative ``s``, this just returns ``x`` multiplied by a + power of the uniformizer `\pi`. + + For negative ``s``, it does the same but when not over a field, it + drops coefficients in the `\pi`-adic expension which have negative + valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R = ZpCA(2) + sage: v = pAdicValuation(R) + sage: v.shift(R.one(), 1) + 2 + O(2^20) + sage: v.shift(R.one(), -1) + O(2^19) + + """ + from sage.rings.all import ZZ + x = self.domain().coerce(x) + s = self.value_group()(s) + v = ZZ(s / self.domain().ramification_index()) + return x << v + class pAdicValuation_int(pAdicValuation_base): r""" A `p`-adic valuation on the integers or the rationals. diff --git a/valuation_space.py b/valuation_space.py index 94c74636484..95e00179aca 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -897,6 +897,92 @@ def _test_scale(self, **options): unscaled = scaled / s tester.assertEqual(self, unscaled) + def shift(self, x, s): + r""" + Shift ``x`` in its expansion with respect to :meth:`uniformizer` by + ``s`` "digits". + + For non-negative ``s``, this just returns ``x`` multiplied by a + power of the uniformizer `\pi`. + + For negative ``s``, it does the same but when not over a field, it + drops coefficients in the `\pi`-adic expension which have negative + valuation. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 2) + sage: v.shift(1, 10) + 1024 + sage: v.shift(11, -1) + 5 + + For some rings, there is no clear `\pi`-adic expansion. In this + case, this method performs negative shifts by iterated division by + the uniformizer and substraction of a lift of the reduction:: + + sage: R. = ZZ[] + sage: v = pAdicValuation(ZZ, 2) + sage: w = GaussValuation(R, v) + sage: w.shift(x, 1) + 2*x + sage: w.shift(2*x, -1) + x + sage: w.shift(x + 2*x^2, -1) + x^2 + + """ + from sage.rings.all import ZZ + x = self.domain().coerce(x) + s = self.value_group()(s) + if s == 0: + return x + + s = ZZ(s / self.value_group().gen()) + if s > 0: + return x * self.uniformizer()**s + else: # s < 0 + if ~self.uniformizer() in self.domain(): + return x / self.uniformizer()**(-s) + else: + for i in range(-s): + if self(x) < 0: + raise NotImplementedError("can not compute general shifts over non-fields which do contain elements of negative valuation") + x -= self.lift(self.reduce(x)) + x //= self.uniformizer() + return x + + def _test_shift(self, **options): + r""" + Check that :meth:`shift` works correctly. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 3) + sage: v._test_shift() + + """ + if self.is_trivial() and not self.is_discrete_valuation(): + return + + try: + self.residue_ring() + except: + # it is not clear what a shift should be in this case + return + + tester = self._tester(**options) + X = self.domain().some_elements() + S = self.value_group().some_elements() + from itertools import product + for x,s in tester.some_elements(product(X, S)): + if self(x) < 0 and ~self.uniformizer() not in self.domain(): + # it is not clear what a shift should be in this case + continue + self.shift(x, s) + def _test_add(self, **options): r""" Check that the (strict) triangle equality is satisfied for the From fada92c79775219429c86e4391b24f5f0ddc9db0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 30 Nov 2016 15:03:40 -0500 Subject: [PATCH 148/740] Fix extensions on relative number fields --- padic_valuation.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/padic_valuation.py b/padic_valuation.py index 99d7cb5eecb..8360338d226 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -374,7 +374,7 @@ def create_object(self, version, key, **extra_args): _ = key[2] # ignored approximants = extra_args['approximants'] parent = DiscretePseudoValuationSpace(R) - return parent.__make_element_class__(pAdicFromLimitValuation)(parent, v, K.relative_polynomial().change_ring(R.base()), approximants) + return parent.__make_element_class__(pAdicFromLimitValuation)(parent, v, K.relative_polynomial().change_ring(R.base_ring()), approximants) pAdicValuation = PadicValuationFactory("pAdicValuation") @@ -671,6 +671,15 @@ def extensions(self, ring): sage: v.extensions(GaussianIntegers()) [2-adic valuation] + TESTS:: + + sage: R. = QQ[] + sage: L. = QQ.extension(x^3 - 2) + sage: R. = L[] + sage: M. = L.extension(b^2 + 2*b + a) + sage: pAdicValuation(M, 2) + 2-adic valuation + """ if self.domain() is ring: return [self] @@ -680,14 +689,14 @@ def extensions(self, ring): if self.domain().is_subring(ring): from sage.rings.number_field.number_field import is_NumberField if is_NumberField(ring.fraction_field()): - if ring.base().fraction_field() is self.domain().fraction_field(): + if ring.base_ring().fraction_field() is self.domain().fraction_field(): from valuation_space import DiscretePseudoValuationSpace parent = DiscretePseudoValuationSpace(ring) approximants = self.mac_lane_approximants(ring.fraction_field().relative_polynomial().change_ring(self.domain())) return [pAdicValuation(ring, approximant) for approximant in approximants] - if ring.base() is not ring and self.domain().is_subring(ring.base()): - return sum([w.extensions(ring) for w in self.extensions(ring.base())], []) - raise NotImplementedError("can not compute extensions of %r from %r to %r"%(self, self.domain(), ring)) + if ring.base_ring() is not ring and self.domain().is_subring(ring.base_ring()): + return sum([w.extensions(ring) for w in self.extensions(ring.base_ring())], []) + return super(pAdicValuation_base, self).extensions(ring) def restriction(self, ring): r""" From 756b4bba744f48f8ae8d4e7e8462ed742a5c7c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 1 Dec 2016 00:40:50 -0500 Subject: [PATCH 149/740] improve error reporting in padicValuation factory --- padic_valuation.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/padic_valuation.py b/padic_valuation.py index 8360338d226..a56a94d7d14 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -265,24 +265,30 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime) 2-adic valuation """ - # To make our lives easier, we move prime to the fraction field of R + # To make our lives easier, we rewrite v over the fraction field of R # which we denote in the following as L = K[x]/(G), do all computations - # there and then come back the original ring + # there and then come back to the original ring L = R.fraction_field() K = L.base_ring() G = L.relative_polynomial().change_ring(K) - # Lift v to a pseudo-valuation on K[x] - # First, we lift valuations defined on subrings of K to valuations on K[x] - if v.domain().fraction_field() is not G.parent().fraction_field(): - if v.domain().is_subring(K): - if v.domain() is not K: - v = pAdicValuation(K, v) - from gauss_valuation import GaussValuation - v = GaussValuation(G.parent(), v) - # Then, we lift valuations defined on polynmial rings which are subrings of K[x] to K[x] - if v.domain() != G.parent(): - v = v.extension(G.parent()) + if v.domain().is_subring(G.parent()): + # v is defined on a subring of K[x] + # We try to lift v to a pseudo-valuation on K[x] + # First, we lift valuations defined on subrings of K to valuations on K[x] + if v.domain().fraction_field() is not G.parent().fraction_field(): + if v.domain().is_subring(K): + if v.domain() is not K: + v = pAdicValuation(K, v) + from gauss_valuation import GaussValuation + v = GaussValuation(G.parent(), v) + if v.domain() != G.parent(): + # Then, we lift valuations defined on polynmial rings which are + # subrings of K[x] to K[x] + v = v.extension(G.parent()) + else: + raise NotImplementedError("can not rewrite %r which is defined on %r as a pseudo-valuation on %r"%(v, v.domain(), G.parent())) + assert(v.domain() is G.parent()) # To obtain uniqueness of p-adic valuations, we need a canonical From a16d67bf1ed5ec2e98d7f88bd034dc1e757048ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 1 Dec 2016 00:41:00 -0500 Subject: [PATCH 150/740] extend p-adic valuation to fraction field --- padic_valuation.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/padic_valuation.py b/padic_valuation.py index a56a94d7d14..d1d06a7f75e 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -1081,3 +1081,24 @@ def _from_base_domain(self, f): """ return f(self.domain().fraction_field().gen()) + + def extensions(self, ring): + r""" + Return the extensions of this valuation to ``ring``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(GaussianIntegers(), 3) + sage: v.extensions(v.domain().fraction_field()) + [3-adic valuation] + + """ + if ring is self.domain().fraction_field(): + if self.domain() is not self.domain().fraction_field(): + base_ring = self.domain().base_ring() + base_valuation = self.restriction(base_ring).extension(base_ring.fraction_field()) + G = ring.relative_polynomial() + approximant = self._base_valuation.change_domain(G.parent())._initial_approximation + return [pAdicValuation(ring, approximant)] + return super(pAdicFromLimitValuation, self).extensions(ring) From 77cec3f8df40adc8d9bd6707f78dd29fb282b0a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 1 Dec 2016 13:58:20 -0500 Subject: [PATCH 151/740] fix double loading of module --- __init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index c4f1f5b67cb..e9c6e2d9b79 100644 --- a/__init__.py +++ b/__init__.py @@ -91,7 +91,8 @@ def to_constant(self, x): sage.rings.function_field.function_field.RationalFunctionField._to_polynomial = to_polynomial sage.rings.function_field.function_field.RationalFunctionField._to_constant = to_constant -sage.rings.function_field.function_field.RationalFunctionField.__old_init__ = sage.rings.function_field.function_field.RationalFunctionField.__init__ +if not hasattr(sage.rings.function_field.function_field.RationalFunctionField, "__old_init__"): + sage.rings.function_field.function_field.RationalFunctionField.__old_init__ = sage.rings.function_field.function_field.RationalFunctionField.__init__ def __init__(self, *args, **kwargs): r""" TESTS:: From 3c8b60b4364e238060d5ed9b322457a72cae652a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 1 Dec 2016 13:58:34 -0500 Subject: [PATCH 152/740] Disable monkey patch if 21872 is detected --- __init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index e9c6e2d9b79..d5bc71cc161 100644 --- a/__init__.py +++ b/__init__.py @@ -105,7 +105,11 @@ def __init__(self, *args, **kwargs): self.__old_init__(*args, **kwargs) from sage.categories.morphism import SetMorphism self._ring.register_conversion(SetMorphism(self.Hom(self._ring), self._to_polynomial)) - self.constant_base_field().register_conversion(SetMorphism(self.Hom(self.constant_base_field()), self._to_constant)) + try: + self.constant_base_field().register_conversion(SetMorphism(self.Hom(self.constant_base_field()), self._to_constant)) + except AssertionError: + # since #21872 there is already such a conversion + pass sage.rings.function_field.function_field.RationalFunctionField.__init__ = __init__ del(__init__) From 1819be0d1b61c755ff7c2bc482a44cd7c8cef9b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 1 Dec 2016 13:58:50 -0500 Subject: [PATCH 153/740] Do not sort elements in factorizations the ring elements might not have a total order defined --- inductive_valuation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index c06d336408b..6bfc83a8ea9 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -1086,13 +1086,13 @@ def equivalence_decomposition(self, f, partial=False): from sage.structure.factorization import Factorization if self.is_equivalence_unit(f): - return Factorization([],unit=f) + return Factorization([], unit=f, sort=False) if not self.domain().base_ring().is_field(): domain = self.domain().change_ring(self.domain().base_ring().fraction_field()) v = self.extension(domain) ret = v.equivalence_decomposition(v.domain()(f)) - return Factorization([(g.change_ring(self.domain().base_ring()),e) for g,e in ret], unit=ret.unit().change_ring(self.domain().base_ring())) + return Factorization([(g.change_ring(self.domain().base_ring()),e) for g,e in ret], unit=ret.unit().change_ring(self.domain().base_ring()), sort=False) R, phi_divides, F = self._equivalence_reduction(f) F = F.factor() @@ -1115,7 +1115,7 @@ def equivalence_decomposition(self, f, partial=False): else: F.append((self.phi(),phi_divides)) - ret = Factorization(F, unit=unit) + ret = Factorization(F, unit=unit, sort=False) assert self.is_equivalent(ret.prod(), f) # this might fail because of leading zeros in inexact rings assert self.is_equivalence_unit(ret.unit()) @@ -1192,7 +1192,7 @@ def minimal_representative(self, f): assert self.is_minimal(ret) from sage.structure.factorization import Factorization - ret = Factorization([(ret, 1)], unit=e) + ret = Factorization([(ret, 1)], unit=e, sort=False) assert self.is_equivalent(ret.prod(), f) # this might fail because of leading zeros return ret From 62416a928a8b861b1a23cb01aac131f78bd4be3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 3 Dec 2016 19:13:55 -0500 Subject: [PATCH 154/740] Normalize residue field polynomials to be monic The degree one code expects this to be the case --- augmented_valuation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 7caaef90366..26a48aa31b0 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -536,8 +536,8 @@ def psi(self): """ R = self._base_valuation.equivalence_unit(-self._base_valuation(self._phi)) - F = self._base_valuation.reduce(self._phi*R) - assert(F.is_irreducible()) + F = self._base_valuation.reduce(self._phi*R).monic() + assert F.is_irreducible() return F def E(self): From 81e66c415e1bfe31cbfac8db01b81d2d322d936d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 3 Dec 2016 19:15:18 -0500 Subject: [PATCH 155/740] Go back in the tree of valuations when computing MacLane approximants to keep the coefficients small --- inductive_valuation.py | 10 ++++----- valuation.py | 48 ++++++++++++++++++++++++++++++------------ 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index 6bfc83a8ea9..10f2e700493 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -661,7 +661,7 @@ def mac_lane_step(self, G, assume_squarefree=False): raise ValueError("G must not be constant") from sage.misc.misc import verbose - verbose("Expanding %s towards %s"%(self, G), caller_name = "mac_lane_step") + verbose("Augmenting %s towards %s"%(self, G), level=10) if not G.is_monic(): raise ValueError("G must be monic") @@ -710,10 +710,10 @@ def mac_lane_step(self, G, assume_squarefree=False): else: continue - verbose("Determining the valuation for %s"%phi, level=2, caller_name="mac_lane_step") + verbose("Determining the valuation for %s"%phi, level=11) w = self.augmentation(phi, self(phi), check=False) NP = w.newton_polygon(G).principal_part() - verbose("Newton-Polygon for v(phi)=%s : %s"%(self(phi), NP), level=2, caller_name="mac_lane_step") + verbose("Newton-Polygon for v(phi)=%s : %s"%(self(phi), NP), level=11) slopes = NP.slopes(repetition=False) if NP.vertices()[0][0] != 0: slopes = [-infinity] + slopes @@ -743,7 +743,7 @@ def mac_lane_step(self, G, assume_squarefree=False): for i in range(len(slopes)): slope = slopes[i] - verbose("Slope = %s"%slope, level=3, caller_name="mac_lane_step") + verbose("Slope = %s"%slope, level=12) new_mu = self(phi) - slope base = self if phi.degree() == base.phi().degree(): @@ -1097,7 +1097,7 @@ def equivalence_decomposition(self, f, partial=False): R, phi_divides, F = self._equivalence_reduction(f) F = F.factor() from sage.misc.misc import verbose - verbose("%s factors as %s = %s in reduction"%(f, F.prod(), F), caller_name="equivalence_decomposition") + verbose("%s factors as %s = %s in reduction"%(f, F.prod(), F), level=20) unit = self.lift(self.residue_ring()(F.unit())) * self.equivalence_reciprocal(R) F = list(F) diff --git a/valuation.py b/valuation.py index 342bdf8d061..0d1ee1e2696 100644 --- a/valuation.py +++ b/valuation.py @@ -617,35 +617,57 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): if not assume_squarefree and not G.is_squarefree(): raise ValueError("G must be squarefree") + from sage.misc.misc import verbose + verbose("Approximants of %r towards %r"%(self, G), level=3) + from sage.rings.all import infinity from gauss_valuation import GaussValuation - leaves = [ GaussValuation(R, self)] + def is_sufficiently_precise(v, others): + if precision_cap is None or v(v.phi()) >= precision_cap: + # the precision to which we have determined the approximants is sufficient + if not [w for w in others if w <= v or w>=v]: + # the approximants do not approximate each other + return True + return False + + leaves = [ (GaussValuation(R, self), None) ] while True: - ef = [ v.E()*v.F() for v in leaves] + ef = [ v.E()*v.F() for v,_ in leaves] + verbose("Augmenting with ef=%r"%(ef,), level=5) if sum(ef) == G.degree(): # the ramification indexes and residual degrees are final - if precision_cap is None or all([v(v.phi()) >= precision_cap for v in leaves]): - # the precision to which we have determined the approximants is sufficient - if not [v for v in leaves if [w for w in leaves if w != v and w <= v]]: - # the approximants do not approximate each other - break + if all([is_sufficiently_precise(v, [w for w,_ in leaves if w != v]) for v,_ in leaves]): + break expandables = [] new_leaves = [] - for v in leaves: + for v,parent in leaves: if v(G) is infinity: - new_leaves.append(v) + new_leaves.append((v,parent)) else: - expandables.append(v) + expandables.append((v,parent)) leaves = new_leaves assert expandables - for v in expandables: - leaves.extend(v.mac_lane_step(G, assume_squarefree=True)) + for v,parent in expandables: + leaves.extend([(w,(v,parent)) for w in v.mac_lane_step(G, assume_squarefree=True)]) + + # rollback each approximant as much as possible to make the + # coefficients of the key polynomials as small as possible + ret = [] + for i in range(len(leaves)): + others = [w for w,_ in leaves if w != leaves[i][0]] + while leaves[i][1] is not None and leaves[i][0].E() == leaves[i][1][0].E() and leaves[i][0].F() == leaves[i][1][0].F() and is_sufficiently_precise(leaves[i][1][0], others): + verbose("Replacing %r with %r which is sufficiently precise"%(leaves[i][0], leaves[i][1][0]), level=9) + leaves[i] = leaves[i][1] + assert is_sufficiently_precise(leaves[i][0], others) + + for v,parent in leaves: + w=v,parent - return leaves + return [v for v,_ in leaves] def mac_lane_approximant(self, G, valuation, approximants = None): r""" From 258c1be10d6a961f08cb13f3c8335d808e2457b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 4 Dec 2016 12:44:57 -0500 Subject: [PATCH 156/740] Remove optimization that does not make things faster it appears --- augmented_valuation.py | 49 ------------------------------------------ 1 file changed, 49 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 26a48aa31b0..ebb1ae93c31 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -315,55 +315,6 @@ def __init__(self, parent, v, phi, mu): self._base_valuation = v self._mu = mu - def _call_(self, f): - """ - Evaluate this valuation at ``f``. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: R = Qp(2, 5) - sage: S. = R[] - sage: v = GaussValuation(S) - sage: w = v.augmentation(x^2 + x + 1, 1) - - sage: w(x^2 + 2*x + 3) - 0 - sage: w((x^2 + 2*x + 3) * w.phi()^3 ) - 3 - sage: w(0) - +Infinity - - """ - f = self.domain().coerce(f) - - from sage.rings.all import infinity - if f.is_zero(): - return infinity - - if f.degree() < self.phi().degree(): - return self._base_valuation(f) - - # We can slightly optimize the approach of DevelopingValuation._call_ - # We know that self(f) >= self._base_valuation(f) - # as soon as we find a coefficient of f with self._base_valuation(c) == - # self._base_valuation(f) we know that this is the valuation of f - - # this optimization does only pay off for polynomials of large degree: - if f.degree() // self.phi().degree() <= 3: - return super(AugmentedValuation_base, self)._call_(f) - - ret = infinity - - lower_bound = self._base_valuation(f) - - for v in self.valuations(f): - ret = min(ret, v) - if ret == lower_bound: - break - - return ret - def equivalence_unit(self, s): """ Return an equivalence unit of minimal degree and valuation ``s``. From 8485152de245fd7f155a7e7a48b592cbeac08c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 4 Dec 2016 13:08:26 -0500 Subject: [PATCH 157/740] cache valuation --- augmented_valuation.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index ebb1ae93c31..ee25a80890b 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -928,9 +928,10 @@ def reduce(self, f): """ f = self.domain().coerce(f) - if self(f) < 0: + v = self(f) + if v < 0: raise ValueError("f must have non-negative valuation") - elif self(f) > 0: + elif v > 0: return self.residue_ring().zero() constant_term = self.coefficients(f).next() @@ -1174,9 +1175,10 @@ def reduce(self, f): """ f = self.domain().coerce(f) - if self(f) < 0: + v = self(f) + if v < 0: raise ValueError("f must have non-negative valuation") - elif self(f) > 0: + elif v > 0: return self.residue_ring().zero() CV = zip(self.coefficients(f), self.valuations(f)) From 99c9ce9d2a1797650b088fcfad6488184aca7f90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 4 Dec 2016 13:08:35 -0500 Subject: [PATCH 158/740] remove caches that do not help much --- inductive_valuation.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index 10f2e700493..a7a8f812b9c 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -894,7 +894,6 @@ def is_minimal(self, f): list(self.valuations(f))[0] == self(f) and \ tau.divides(len(list(self.coefficients(f))) - 1) - @cached_method def _equivalence_reduction(self, f): r""" Helper method for :meth:`is_equivalence_irreducible` and @@ -936,7 +935,6 @@ def _equivalence_reduction(self, f): R = self.equivalence_unit(-self(f)) return R, phi_divides, self.reduce(f*R) - @cached_method def is_equivalence_irreducible(self, f): r""" Return whether the polynomial ``f`` is equivalence-irreducible, i.e., From 4a9f7dc73bab82f2ade561beb00423637152b9fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 5 Dec 2016 01:43:29 -0500 Subject: [PATCH 159/740] Replace == infinity with is infinity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sage: %timeit 0 == infinity 100000 loops, best of 3: 7.8 µs per loop sage: %timeit 0 is infinity 10000000 loops, best of 3: 129 ns per loop --- augmented_valuation.py | 4 ++-- inductive_valuation.py | 4 ++-- limit_valuation.py | 6 +++--- scaled_valuation.py | 2 +- valuation.py | 6 +++--- valuation_space.py | 14 +++++++------- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index ee25a80890b..b5839636aa5 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -253,7 +253,7 @@ def create_object(self, version, key): from valuation_space import DiscretePseudoValuationSpace parent = DiscretePseudoValuationSpace(base_valuation.domain()) - if mu < infinity: + if mu is not infinity: if base_valuation.is_trivial(): return parent.__make_element_class__(FinalFiniteAugmentedValuation)(parent, base_valuation, phi, mu) else: @@ -1378,7 +1378,7 @@ def lift_to_key(self, F): if not self.domain().base_ring() in Fields(): raise NotImplementedError("only implemented for polynomial rings over fields") - if self._base_valuation.is_gauss_valuation() and self._mu == infinity: + if self._base_valuation.is_gauss_valuation() and self._mu is infinity: raise TypeError("there are no keys over this valuation") if F.is_constant(): diff --git a/inductive_valuation.py b/inductive_valuation.py index a7a8f812b9c..e86669877f0 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -374,7 +374,7 @@ def _test_EF(self, **options): chain = self.augmentation_chain() for w,v in zip(chain, chain[1:]): from sage.rings.all import infinity, ZZ - if w(w.phi()) == infinity: + if w(w.phi()) is infinity: tester.assertEqual(w.E(), v.E()) tester.assertIn(w.E(), ZZ) tester.assertIn(w.F(), ZZ) @@ -676,7 +676,7 @@ def mac_lane_step(self, G, assume_squarefree=False): raise ValueError("G must be squarefree") from sage.rings.all import infinity - assert self(G) != infinity # this is a valuation and G is non-zero + assert self(G) is not infinity # this is a valuation and G is non-zero if self.is_key(G): return [self.augmentation(G, infinity)] diff --git a/limit_valuation.py b/limit_valuation.py index 8fcf2059faf..ab33f480351 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -346,7 +346,7 @@ def _repr_(self): """ from sage.rings.all import infinity from augmented_valuation import AugmentedValuation_base - if self._initial_approximation(self._G) < infinity: + if self._initial_approximation(self._G) is not infinity: if isinstance(self._initial_approximation, AugmentedValuation_base): return repr(self._initial_approximation)[:-1] + ", … ]" return repr(self._initial_approximation) @@ -463,7 +463,7 @@ def _improve_approximation(self): """ from sage.rings.all import infinity - if self._approximation(self._G) == infinity: + if self._approximation(self._G) is infinity: # an infinite valuation can not be improved further return @@ -519,7 +519,7 @@ def _improve_approximation_for_call(self, f): """ from sage.rings.all import infinity - if self._approximation(self._approximation.phi()) == infinity: + if self._approximation(self._approximation.phi()) is infinity: # an infinite valuation can not be improved further return diff --git a/scaled_valuation.py b/scaled_valuation.py index 8f827d46337..8f4f2e96891 100644 --- a/scaled_valuation.py +++ b/scaled_valuation.py @@ -55,7 +55,7 @@ def create_key(self, base, s): """ from sage.rings.all import infinity, QQ - if s == infinity or s not in QQ or s <= 0: + if s is infinity or s not in QQ or s <= 0: # for these values we can not return a TrivialValuation() in # create_object() because that would override that instance's # _factory_data and lead to pickling errors diff --git a/valuation.py b/valuation.py index 0d1ee1e2696..5d58d6fb4a8 100644 --- a/valuation.py +++ b/valuation.py @@ -76,8 +76,8 @@ def is_equivalent(self, f, g): """ from sage.rings.all import infinity - if self(f) == infinity: - return self(g) == infinity + if self(f) is infinity: + return self(g) is infinity return self(f-g) > self(f) @@ -738,7 +738,7 @@ def mac_lane_approximant(self, G, valuation, approximants = None): # Check thet valuation is an approximant for a valuation # on domain that extends its restriction to the base field. from sage.rings.all import infinity - if valuation(G) != infinity: + if valuation(G) is not infinity: v = valuation while not v.is_gauss_valuation(): if v(G) <= v._base_valuation(G): diff --git a/valuation_space.py b/valuation_space.py index 95e00179aca..33d633ff7cc 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -331,7 +331,7 @@ def is_trivial(self): """ from sage.rings.all import infinity - if self(self.domain().one()) == infinity: + if self(self.domain().one()) is infinity: # the constant infinity return True if self(self.uniformizer()) != 0: @@ -670,7 +670,7 @@ def scale(self, scalar): """ from sage.rings.all import infinity - if scalar == infinity: + if scalar is infinity: from trivial_valuation import TrivialPseudoValuation return TrivialPseudoValuation(self.domain()) if scalar == 0: @@ -784,7 +784,7 @@ def _strictly_separating_element(self, other): n = self(numerator) nn = other(numerator) assert(n > 0) - assert(nn != infinity) + assert(nn is not infinity) if (nn < 0): return numerator @@ -792,7 +792,7 @@ def _strictly_separating_element(self, other): d = self(denominator) dd = other(denominator) assert(dd > 0) - assert(d != infinity) + assert(d is not infinity) if d < 0: return self.domain()(1/denominator) @@ -1061,7 +1061,7 @@ def _test_no_infinite_units(self, **options): from sage.rings.all import infinity tester = self._tester(**options) for x in tester.some_elements(self.domain().some_elements()): - if self(x) == infinity: + if self(x) is infinity: tester.assertFalse(x.is_unit()) def _test_value_group(self, **options): @@ -1079,7 +1079,7 @@ def _test_value_group(self, **options): tester = self._tester(**options) # check consistency of trivial valuations first if self.is_trivial(): - if self(self.domain().one()) == infinity: + if self(self.domain().one()) is infinity: # a trivial pseudo-valuation that sends everything to infinity with tester.assertRaises(ValueError): self.value_group() @@ -1087,7 +1087,7 @@ def _test_value_group(self, **options): # check that all valuations are in the value group for x in tester.some_elements(self.domain().some_elements()): - if self(x) != infinity: + if self(x) is not infinity: tester.assertIn(self(x), self.value_group()) if not self.is_trivial(): From 75375cac7c4ecbb821c795151a14a774289f0e4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 5 Dec 2016 02:18:00 -0500 Subject: [PATCH 160/740] Special case infinity return values for p-adic valuations --- padic_valuation.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/padic_valuation.py b/padic_valuation.py index d1d06a7f75e..e5041f3671c 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -29,6 +29,8 @@ from sage.misc.cachefunc import cached_method from sage.misc.fast_methods import WithEqualityById +from sage.rings.all import infinity + class PadicValuationFactory(UniqueFactory): """ Create a ``prime``-adic valuation on ``R``. @@ -542,7 +544,6 @@ def is_unramified(self, G, include_steps=False, assume_squarefree=False): if not assume_squarefree and not G.is_squarefree(): raise ValueError("G must be squarefree") - from sage.rings.all import infinity from gauss_valuation import GaussValuation steps = [ GaussValuation(R, self) ] @@ -623,7 +624,6 @@ def is_totally_ramified(self, G, include_steps=False, assume_squarefree=False): if not assume_squarefree and not G.is_squarefree(): raise ValueError("G must be squarefree") - from sage.rings.all import infinity from gauss_valuation import GaussValuation steps = [ GaussValuation(R, self) ] @@ -969,6 +969,11 @@ def _call_(self, x): 2 """ + if x.is_zero(): + # x.valuation() is a factor 10 slower when computing the valuation + # of a rational zero than when computing the valuation of another + # small rational. Special casing this is a factor 100 faster. + return infinity return x.valuation(self.p()) def uniformizer(self): From abe8f30055db0aa64207ec0cee26fc682aff9261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 6 Dec 2016 12:13:25 -0500 Subject: [PATCH 161/740] Avoid arithmetic with infinities --- augmented_valuation.py | 6 +++++- developing_valuation.py | 9 +++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index b5839636aa5..82a92a1b849 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -1562,7 +1562,11 @@ def valuations(self, f): f = self.domain().coerce(f) for i,c in enumerate(self.coefficients(f)): - yield self._base_valuation(c) + i*self._mu + v = self._base_valuation(c) + if v is infinity: + yield v + else: + yield v + i*self._mu class FinalFiniteAugmentedValuation(FiniteAugmentedValuation, FinalAugmentedValuation): diff --git a/developing_valuation.py b/developing_valuation.py index 94650038050..5121c870bbb 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -251,11 +251,16 @@ def _call_(self, f): """ f = self.domain().coerce(f) + from sage.rings.all import infinity if f.is_zero(): - from sage.rings.all import infinity return infinity - return min(self.valuations(f)) + ret = infinity + for v in self.valuations(f): + if ret is infinity or (v is not infinity and v < ret): + # "ret is infinity" is redundant but much faster than < when ret is infinite + ret = v + return ret @abstract_method def valuations(self, f): From 568fbfdc262f5443b497a7d3ec67b8d84ed6fdd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 6 Dec 2016 12:40:39 -0500 Subject: [PATCH 162/740] Special case the very common phi=x to make it a bit faster --- developing_valuation.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/developing_valuation.py b/developing_valuation.py index 5121c870bbb..473b79d21bc 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -145,11 +145,17 @@ def coefficients(self, f): [(1 + O(2^5))*x + (2 + O(2^5)), (1 + O(2^5))] """ - f = self.domain().coerce(f) + domain = self.domain() + f = domain.coerce(f) - if self.phi().degree() == 1: + if f.degree() < self.phi().degree(): + yield f + elif self.phi().degree() == 1: from itertools import imap - for c in imap(f.parent(), f(self.phi().parent().gen() - self.phi()[0]).coefficients(sparse=False)): yield c + if self.phi() != domain.gen() or not domain.is_exact(): + f = f(domain.gen() - self.phi()[0]) + for c in imap(domain, f.coefficients(sparse=False)): + yield c else: while f.degree() >= 0: f,r = self._quo_rem(f) From bc53aa0479b4b1930a0a352bb1f7c929c4575879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 6 Dec 2016 12:41:42 -0500 Subject: [PATCH 163/740] no need to convert to the base ring --- gauss_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gauss_valuation.py b/gauss_valuation.py index f1b81755fcf..51211727dbc 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -268,7 +268,7 @@ def valuations(self, f): f = self.domain().coerce(f) for c in self.coefficients(f): - yield self._base_valuation(self.domain().base_ring()(c)) + yield self._base_valuation(c) @cached_method def residue_ring(self): From c9759b2e6354673632af63763831525711c3543e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 6 Dec 2016 13:04:23 -0500 Subject: [PATCH 164/740] Extend p-adic valuations to work on quotient rings as these are much faster for some computations as they do not need to compute an absolute polynomial. --- padic_valuation.py | 91 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 24 deletions(-) diff --git a/padic_valuation.py b/padic_valuation.py index e5041f3671c..a1e73d0e437 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -144,6 +144,17 @@ class PadicValuationFactory(UniqueFactory): sage: pAdicValuation(R, R.fractional_ideal(I + 1)) 2-adic valuation + It can sometimes be beneficial to define a number field extension as a + quotient of a polynomial ring (since number field extensions always compute + an absolute polynomial defining the extension which can be very costly):: + + sage: R. = QQ[] + sage: K. = NumberField(x^2 + 1) + sage: R. = K[] + sage: L. = R.quo(x^2 + a) + sage: pAdicValuation(L, 2) + 2-adic valuation + """ def create_key_and_extra_args(self, R, prime=None): r""" @@ -160,6 +171,7 @@ def create_key_and_extra_args(self, R, prime=None): from sage.rings.all import ZZ, QQ from sage.rings.padics.padic_generic import pAdicGeneric from sage.rings.number_field.number_field import is_NumberField + from sage.rings.polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing if R.characteristic() != 0: # We do not support equal characteristic yet @@ -169,7 +181,7 @@ def create_key_and_extra_args(self, R, prime=None): return self.create_key_for_integers(R, prime), {} elif isinstance(R, pAdicGeneric): return self.create_key_for_local_ring(R, prime), {} - elif is_NumberField(R.fraction_field()): + elif is_NumberField(R.fraction_field()) or is_PolynomialQuotientRing(R): return self.create_key_and_extra_args_for_number_field(R, prime) else: raise NotImplementedError("p-adic valuations not implemented for %r"%(R,)) @@ -231,14 +243,9 @@ def create_key_and_extra_args_for_number_field(self, R, prime): 2-adic valuation """ - from sage.rings.number_field.number_field_ideal import NumberFieldFractionalIdeal - # To make our lives easier, we move prime to the fraction field of R - # which we denote in the following as L = K[x]/(G), do all computations - # there and then come back the original ring - L = R.fraction_field() - G = L.relative_polynomial() - K = L.base_ring() + K, L, G = self._normalize_number_field_data(R.fraction_field()) + from sage.rings.number_field.number_field_ideal import NumberFieldFractionalIdeal from valuation import DiscretePseudoValuation if isinstance(prime, DiscretePseudoValuation): return self.create_key_and_extra_args_for_number_field_from_valuation(R, prime, prime) @@ -267,12 +274,7 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime) 2-adic valuation """ - # To make our lives easier, we rewrite v over the fraction field of R - # which we denote in the following as L = K[x]/(G), do all computations - # there and then come back to the original ring - L = R.fraction_field() - K = L.base_ring() - G = L.relative_polynomial().change_ring(K) + K, L, G = self._normalize_number_field_data(R.fraction_field()) if v.domain().is_subring(G.parent()): # v is defined on a subring of K[x] @@ -297,8 +299,8 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime) # description of v. We consider all extensions of vK to L and select # the one approximated by v. vK = v.restriction(v.domain().base_ring()) - approximants = vK.mac_lane_approximants(L.relative_polynomial()) - approximant = vK.mac_lane_approximant(L.relative_polynomial(), v, approximants=approximants) + approximants = vK.mac_lane_approximants(G) + approximant = vK.mac_lane_approximant(G, v, approximants=tuple(approximants)) return (R, approximant, L.construction()), {'approximants': approximants} @@ -320,12 +322,7 @@ def create_key_and_extra_args_for_number_field_from_ideal(self, R, I, prime): 2-adic valuation """ - # To make our lives easier, we move prime to the fraction field of R - # which we denote in the following as L = K[x]/(G), do all computations - # there and then come back the original ring - L = R.fraction_field() - G = L.relative_polynomial() - K = L.base_ring() + K, L, G = self._normalize_number_field_data(R.fraction_field()) # To obtain uniqueness of p-adic valuations, we need a canonical # description of v. We consider all extensions of vK to L and select @@ -353,6 +350,44 @@ def create_key_and_extra_args_for_number_field_from_ideal(self, R, I, prime): # carried the information where it came from return (R, candidates_for_I[0], L.construction()), {'approximants': candidates} + def _normalize_number_field_data(self, R): + r""" + Helper method which returns the defining data of the number field + ``R``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: K = R.quo(x^2 + 1) + sage: pAdicValuation._normalize_number_field_data(K) + (Rational Field, + Univariate Quotient Polynomial Ring in xbar over Rational Field with modulus x^2 + 1, + x^2 + 1) + + """ + # To make our lives easier, we rewrite v over the fraction field of R + # which we denote in the following as L = K[x]/(G), do all computations + # there and then come back to the original ring + from sage.rings.polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing + from sage.rings.number_field.number_field import is_NumberField + if is_NumberField(R): + L = R.fraction_field() + G = L.relative_polynomial() + K = L.base_ring() + elif is_PolynomialQuotientRing(R): + from sage.categories.all import NumberFields + if R.base_ring() not in NumberFields(): + raise NotImplementedError("can not normalize quotients over %r"%(R.base_ring(),)) + L = R + G = R.modulus() + K = R.base_ring() + else: + raise NotImplementedError("can not normalize %r"%(R,)) + + return K, L, G + + def create_object(self, version, key, **extra_args): r""" Create a `p`-adic valuation from ``key``. @@ -367,6 +402,8 @@ def create_object(self, version, key, **extra_args): from sage.rings.all import ZZ, QQ from sage.rings.padics.padic_generic import pAdicGeneric from valuation_space import DiscretePseudoValuationSpace + from sage.rings.polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing + from sage.rings.number_field.number_field import is_NumberField R = key[0] K = R.fraction_field() parent = DiscretePseudoValuationSpace(R) @@ -377,12 +414,18 @@ def create_object(self, version, key, **extra_args): prime = key[1] assert(len(key)==2) return parent.__make_element_class__(pAdicValuation_int)(parent, prime) - else: # Number field case + else: v = key[1] _ = key[2] # ignored approximants = extra_args['approximants'] parent = DiscretePseudoValuationSpace(R) - return parent.__make_element_class__(pAdicFromLimitValuation)(parent, v, K.relative_polynomial().change_ring(R.base_ring()), approximants) + if is_NumberField(K): + G = K.relative_polynomial() + elif is_PolynomialQuotientRing(R): + G = K.modulus() + else: + raise NotImplementedError + return parent.__make_element_class__(pAdicFromLimitValuation)(parent, v, G.change_ring(R.base_ring()), approximants) pAdicValuation = PadicValuationFactory("pAdicValuation") From 8138640e88d35f606cc40f890e042d6a41094730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 6 Dec 2016 13:34:01 -0500 Subject: [PATCH 165/740] Implement extensions to quotient rings --- padic_valuation.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/padic_valuation.py b/padic_valuation.py index a1e73d0e437..4c12fa1ea4e 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -729,6 +729,15 @@ def extensions(self, ring): sage: pAdicValuation(M, 2) 2-adic valuation + Check that we can extend to a field written as a quotient:: + + sage: R. = QQ[] + sage: K. = QQ.extension(x^2 + 1) + sage: R. = K[] + sage: L. = R.quo(x^2 + a) + sage: pAdicValuation(QQ, 2).extensions(L) + [2-adic valuation] + """ if self.domain() is ring: return [self] @@ -736,12 +745,23 @@ def extensions(self, ring): if self.domain().fraction_field().is_subring(ring): return pAdicValuation(self.domain().fraction_field(), self).extensions(ring) if self.domain().is_subring(ring): + from sage.rings.polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing + if is_PolynomialQuotientRing(ring): + if ring.base_ring() is self.domain(): + from sage.categories.all import Fields + if ring in Fields(): + from valuation_space import DiscretePseudoValuationSpace + parent = DiscretePseudoValuationSpace(ring) + approximants = self.mac_lane_approximants(ring.modulus().change_ring(self.domain()), assume_squarefree=True) + return [pAdicValuation(ring, approximant) for approximant in approximants] + else: + return sum([w.extensions(ring) for w in self.extensions(ring.base_ring())], []) from sage.rings.number_field.number_field import is_NumberField if is_NumberField(ring.fraction_field()): if ring.base_ring().fraction_field() is self.domain().fraction_field(): from valuation_space import DiscretePseudoValuationSpace parent = DiscretePseudoValuationSpace(ring) - approximants = self.mac_lane_approximants(ring.fraction_field().relative_polynomial().change_ring(self.domain())) + approximants = self.mac_lane_approximants(ring.fraction_field().relative_polynomial().change_ring(self.domain()), assume_squarefree=True) return [pAdicValuation(ring, approximant) for approximant in approximants] if ring.base_ring() is not ring and self.domain().is_subring(ring.base_ring()): return sum([w.extensions(ring) for w in self.extensions(ring.base_ring())], []) From 1af1f5111da8672e1d4f33e5982c917700fd3533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 6 Dec 2016 16:29:04 -0500 Subject: [PATCH 166/740] simplify equivalence_reduction code --- inductive_valuation.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index e86669877f0..577d357c102 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -894,6 +894,10 @@ def is_minimal(self, f): list(self.valuations(f))[0] == self(f) and \ tau.divides(len(list(self.coefficients(f))) - 1) + @cached_method + def mu(self): + return self(self.phi()) + def _equivalence_reduction(self, f): r""" Helper method for :meth:`is_equivalence_irreducible` and @@ -924,15 +928,20 @@ def _equivalence_reduction(self, f): assert self.residue_ring() is v.residue_ring() return v._equivalence_reduction(f) - phi_divides = 0 - while self.valuations(f).next() > self(f): - # phi is an equivalence-factor of f - f = f-self.coefficients(f).next() - assert self.phi().divides(f) - f,_ = f.quo_rem(self.phi()) - phi_divides += 1 + valuations = list(self.valuations(f)) + valuation = min(valuations) + for phi_divides in range(len(valuations)): + # count how many times phi divides f + if valuations[phi_divides] <= valuation: + break + + print self,f,phi_divides + + if phi_divides: + f = f.parent()(list(self.coefficients(f))[phi_divides:])(self.phi()) + valuation -= self.mu()*phi_divides - R = self.equivalence_unit(-self(f)) + R = self.equivalence_unit(-valuation) return R, phi_divides, self.reduce(f*R) def is_equivalence_irreducible(self, f): From 31e31462d2ec5d4f312bf9d6dacf2b62ffc44448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 7 Dec 2016 01:40:41 -0500 Subject: [PATCH 167/740] Do not double check inputs --- inductive_valuation.py | 22 +++++++++++----------- limit_valuation.py | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index 577d357c102..2bf7fe787fa 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -631,7 +631,7 @@ def augmentation(self, phi, mu, check=True): from augmented_valuation import AugmentedValuation return AugmentedValuation(self, phi, mu, check) - def mac_lane_step(self, G, assume_squarefree=False): + def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducible=False): r""" Perform an approximation step towards the squarefree monic non-constant integral polynomial ``G`` which is not an :meth:`equivalence_unit`. @@ -678,8 +678,8 @@ def mac_lane_step(self, G, assume_squarefree=False): from sage.rings.all import infinity assert self(G) is not infinity # this is a valuation and G is non-zero - if self.is_key(G): - return [self.augmentation(G, infinity)] + if self.is_key(G, assume_equivalence_irreducible=assume_equivalence_irreducible): + return [self.augmentation(G, infinity, check=False)] F = self.equivalence_decomposition(G) assert len(F), "%s equivalence-decomposes as an equivalence-unit %s"%(G, F) @@ -696,7 +696,7 @@ def mac_lane_step(self, G, assume_squarefree=False): prec = min([c.precision_absolute() for c in phi.list()]) g = G.map_coefficients(lambda c:c.add_bigoh(prec)) assert self.is_key(g) - return [self.augmentation(g, infinity)] + return [self.augmentation(g, infinity, check=False)] if phi == self.phi(): # a factor phi in the equivalence decomposition means that we @@ -737,7 +737,7 @@ def mac_lane_step(self, G, assume_squarefree=False): phi[i-best] = phi[i-best].add_bigoh(w(c)-w(q[best])) phi = G.parent()(phi) - w = self._base_valuation.augmentation(phi, infinity) + w = self._base_valuation.augmentation(phi, infinity, check=False) ret.append(w) continue @@ -750,14 +750,14 @@ def mac_lane_step(self, G, assume_squarefree=False): assert new_mu > self(phi) if not base.is_gauss_valuation(): base = base._base_valuation - new_leaf = base.augmentation(phi, new_mu) + new_leaf = base.augmentation(phi, new_mu, check=False) assert slope is -infinity or 0 in new_leaf.newton_polygon(G).slopes(repetition=False) ret.append(new_leaf) assert ret return ret - def is_key(self, phi, explain=False): + def is_key(self, phi, explain=False, assume_equivalence_irreducible=False): r""" Return whether ``phi`` is a key polynomial for this valuation, i.e., whether it is monic, whether it :meth:`is_equivalence_irreducible`, and @@ -794,9 +794,9 @@ def is_key(self, phi, explain=False): if not phi.is_monic(): reason = "phi must be monic" - elif not self.is_equivalence_irreducible(phi): + elif not assume_equivalence_irreducible and not self.is_equivalence_irreducible(phi): reason = "phi must be equivalence irreducible" - elif not self.is_minimal(phi): + elif not self.is_minimal(phi, assume_equivalence_irreducible=True): reason = "phi must be minimal" if explain: @@ -804,7 +804,7 @@ def is_key(self, phi, explain=False): else: return reason is None - def is_minimal(self, f): + def is_minimal(self, f, assume_equivalence_irreducible=False): r""" Return whether the polynomial ``f`` is minimal with respect to this valuation, i.e., whether ``f`` is not constant any non-constant @@ -853,7 +853,7 @@ def is_minimal(self, f): if f.is_constant(): return False - if not self.is_equivalence_irreducible(f): + if not assume_equivalence_irreducible and not self.is_equivalence_irreducible(f): # any factor divides f with respect to this valuation return False diff --git a/limit_valuation.py b/limit_valuation.py index ab33f480351..2f207aa26b3 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -467,7 +467,7 @@ def _improve_approximation(self): # an infinite valuation can not be improved further return - approximations = self._approximation.mac_lane_step(self._G) + approximations = self._approximation.mac_lane_step(self._G, assume_squarefree=True, assume_equivalence_irreducible=True) assert(len(approximations)==1) self._approximation = approximations[0] From ae60595551844477c05e50e741bcc26d28ac1a29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 7 Dec 2016 01:40:54 -0500 Subject: [PATCH 168/740] Only check inputs of reduction when the reduction failed --- gauss_valuation.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/gauss_valuation.py b/gauss_valuation.py index 51211727dbc..96807f1a238 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -324,10 +324,13 @@ def reduce(self, f): """ f = self.domain().coerce(f) - if not all([v>=0 for v in self.valuations(f)]): - raise ValueError("reduction not defined for non-integral elements and %r is not integral over %r"%(f, self)) - return f.map_coefficients(lambda c:self._base_valuation.reduce(c), self.restriction(self.domain().base_ring()).residue_field()) + try: + return f.map_coefficients(self._base_valuation.reduce, self._base_valuation.residue_field()) + except: + if not all([v>=0 for v in self.valuations(f)]): + raise ValueError("reduction not defined for non-integral elements and %r is not integral over %r"%(f, self)) + raise def lift(self, F): """ From 6c3ef76ee29945e1c3bed6e27cf29dc4eba96452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 7 Dec 2016 01:41:09 -0500 Subject: [PATCH 169/740] remove debug output --- inductive_valuation.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index 2bf7fe787fa..53d5ba5db36 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -935,8 +935,6 @@ def _equivalence_reduction(self, f): if valuations[phi_divides] <= valuation: break - print self,f,phi_divides - if phi_divides: f = f.parent()(list(self.coefficients(f))[phi_divides:])(self.phi()) valuation -= self.mu()*phi_divides From d200f960577de8c1f19e29ee821c213276c35a2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 7 Dec 2016 01:41:20 -0500 Subject: [PATCH 170/740] Cache value groups --- inductive_valuation.py | 1 - padic_valuation.py | 1 + valuation_space.py | 2 ++ 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index 53d5ba5db36..562abca5283 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -988,7 +988,6 @@ def is_equivalence_irreducible(self, f): if phi_divides > 1: return False - @cached_method def equivalence_decomposition(self, f, partial=False): r""" Return an equivalence decomposition of ``f``, i.e., a polynomial diff --git a/padic_valuation.py b/padic_valuation.py index 4c12fa1ea4e..af1bc849fbe 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -784,6 +784,7 @@ def restriction(self, ring): return pAdicValuation(ring, self.p()) + @cached_method def value_semigroup(self): r""" Return the value semigroup of this valuation. diff --git a/valuation_space.py b/valuation_space.py index 33d633ff7cc..8431ab8f338 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -37,6 +37,7 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.misc.abstract_method import abstract_method from sage.structure.unique_representation import UniqueRepresentation +from sage.misc.cachefunc import cached_method class DiscretePseudoValuationSpace(UniqueRepresentation, Homset): r""" @@ -363,6 +364,7 @@ def uniformizer(self): """ + @cached_method def value_group(self): r""" Return the value group of this discrete pseudo-valuation, the From 7097974805162fe8964c7d568e421676adfe9c46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 7 Dec 2016 01:53:30 -0500 Subject: [PATCH 171/740] Faster reduce() --- augmented_valuation.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 82a92a1b849..c3112ded3d0 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -1175,16 +1175,17 @@ def reduce(self, f): """ f = self.domain().coerce(f) - v = self(f) - if v < 0: - raise ValueError("f must have non-negative valuation") - elif v > 0: - return self.residue_ring().zero() - CV = zip(self.coefficients(f), self.valuations(f)) + if all([v > 0 for (c,v) in CV]): + return self.residue_ring().zero() # rewrite as sum of f_i phi^{i tau}, i.e., drop the coefficients that # can have no influence on the reduction tau = self.value_group().index(self._base_valuation.value_group()) + for i,(c,v) in enumerate(CV): + if v < 0: + raise ValueError("f must have non-negative valuation") + assert v != 0 or i % tau == 0 + assert not any([v==0 for i,(c,v) in enumerate(CV) if i % tau != 0]) CV = CV[::tau] From 57c5aad3f79356fb13bd02a581838c6a5d5c89ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 7 Dec 2016 01:55:02 -0500 Subject: [PATCH 172/740] Remove expensive assert this is checked by the call to reduce() in the next line anyway. --- augmented_valuation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index c3112ded3d0..58cf303b6e2 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -1192,7 +1192,6 @@ def reduce(self, f): # replace f_i by f_i Q^{i tau} vQ = self._mu * tau CV = [(c*self._Q()**i, v - vQ*i) for i,(c,v) in enumerate(CV)] - assert all([self._base_valuation(c)>=0 for c,v in CV]) # recursively reduce the f_i Q^{i tau} C = [self._base_valuation.reduce(c)(self._residue_field_generator()) for c,v in CV] From f2384bb51ee69d7b7d6c54c51738502c6b4e0050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 7 Dec 2016 01:58:40 -0500 Subject: [PATCH 173/740] Faster effective_degree() --- developing_valuation.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/developing_valuation.py b/developing_valuation.py index 473b79d21bc..a3ca532426c 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -115,8 +115,9 @@ def effective_degree(self, f): if f.is_zero(): raise ValueError("the effective degree is only defined for non-zero polynomials") - v = self(f) - return [i for i,w in enumerate(self.valuations(f)) if w == v][-1] + valuations = list(self.valuations(f)) + v = min(valuations) + return [i for i,w in enumerate(valuations) if w == v][-1] def coefficients(self, f): r""" From e534d0936dbc2bd4f8d2540174c90bffbb928a15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 7 Dec 2016 02:32:24 -0500 Subject: [PATCH 174/740] Faster computation of CV pairs --- augmented_valuation.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 58cf303b6e2..5abfd8434e4 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -1175,7 +1175,8 @@ def reduce(self, f): """ f = self.domain().coerce(f) - CV = zip(self.coefficients(f), self.valuations(f)) + coefficients = list(self.coefficients(f)) + CV = zip(coefficients, self.valuations(f, coefficients=coefficients)) if all([v > 0 for (c,v) in CV]): return self.residue_ring().zero() # rewrite as sum of f_i phi^{i tau}, i.e., drop the coefficients that @@ -1395,7 +1396,8 @@ def lift_to_key(self, F): assert self.reduce(f) == F f *= self._Q()**F.degree() - CV = zip(self.coefficients(f), self.valuations(f)) + coefficients = list(self.coefficients(f)) + CV = zip(coefficients, self.valuations(f, coefficients=coefficients)) vf = self(f) CV = [(c,v) if v==vf else (c.parent().zero(),infinity) for c,v in CV] while CV[-1][1] is infinity: @@ -1530,7 +1532,7 @@ def value_semigroup(self): """ return self._base_valuation.value_semigroup() + self._mu - def valuations(self, f): + def valuations(self, f, coefficients=None): """ Return the valuations of the `f_i\phi^i` in the expansion `f=\sum_i f_i\phi^i`. @@ -1561,7 +1563,7 @@ def valuations(self, f): """ f = self.domain().coerce(f) - for i,c in enumerate(self.coefficients(f)): + for i,c in enumerate(coefficients or self.coefficients(f)): v = self._base_valuation(c) if v is infinity: yield v @@ -1693,7 +1695,7 @@ def value_semigroup(self): """ return self._base_valuation.value_semigroup() - def valuations(self, f): + def valuations(self, f, coefficients=None): """ Return the valuations of the `f_i\phi^i` in the expansion `f=\sum_i f_i\phi^i`. @@ -1720,6 +1722,6 @@ def valuations(self, f): f = self.domain().coerce(f) num_infty_coefficients = f.degree() // self.phi().degree() - yield self._base_valuation(self.coefficients(f).next()) + yield self._base_valuation(coefficients or self.coefficients(f).next()) for i in range(num_infty_coefficients): yield infinity From a017aa0b6ea5202098a4f96f8ea77e8878e6dc49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 7 Dec 2016 02:32:44 -0500 Subject: [PATCH 175/740] simplify map_coefficients call --- augmented_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 5abfd8434e4..1f04dd3e08a 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -1314,7 +1314,7 @@ def lift(self, F): # now the coefficients correspond to the expansion with (f_iQ^i)(Q^{-1} phi)^i # now we undo the factors of Q^i (the if else is necessary to handle the case when mu is infinity, i.e., when _Q_reciprocal() is undefined) - coeffs = [ (c if i == 0 else c*self._Q_reciprocal()**i).map_coefficients(lambda d:_lift_to_maximal_precision(d)) for i,c in enumerate(coeffs) ] + coeffs = [ (c if i == 0 else c*self._Q_reciprocal()**i).map_coefficients(_lift_to_maximal_precision) for i,c in enumerate(coeffs) ] RR = self.domain().change_ring(self.domain()) From 052bd61279ef73d95e4236a7a9bed1741e83864d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 7 Dec 2016 02:32:49 -0500 Subject: [PATCH 176/740] Remove redundant assert --- augmented_valuation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 1f04dd3e08a..d7ea0a4e69b 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -1392,7 +1392,6 @@ def lift_to_key(self, F): return self.phi() f = self.lift(F) - assert self(f) == 0 assert self.reduce(f) == F f *= self._Q()**F.degree() From 1fd47aaad2f699133ac31f1245ddd33a9ed7ce03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 7 Dec 2016 13:16:05 -0500 Subject: [PATCH 177/740] Document passing in coefficients into valuations() --- augmented_valuation.py | 10 ++++++++++ gauss_valuation.py | 9 +++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index d7ea0a4e69b..7a74304981a 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -1540,6 +1540,11 @@ def valuations(self, f, coefficients=None): - ``f`` -- a polynomial in the domain of this valuation + - ``coefficients`` -- the coefficients of ``f`` as produced by + :meth:`coefficients` or ``None`` (default: ``None``); this can be + used to speed up the computation when the expansion of ``f`` is + already known from a previous computation. + OUTPUT: An iterator over rational numbers (or infinity) `[v(f_0), v(f_1\phi), \dots]` @@ -1703,6 +1708,11 @@ def valuations(self, f, coefficients=None): - ``f`` -- a polynomial in the domain of this valuation + - ``coefficients`` -- the coefficients of ``f`` as produced by + :meth:`coefficients` or ``None`` (default: ``None``); this can be + used to speed up the computation when the expansion of ``f`` is + already known from a previous computation. + OUTPUT: An iterator over rational numbers (or infinity) `[v(f_0), v(f_1\phi), \dots]` diff --git a/gauss_valuation.py b/gauss_valuation.py index 96807f1a238..f098f4c5f84 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -242,7 +242,7 @@ def uniformizer(self): """ return self.domain()(self._base_valuation.uniformizer()) - def valuations(self, f): + def valuations(self, f, coefficients=None): """ Return the valuations of the `f_i\phi^i` in the expansion `f=\sum f_i\phi^i`. @@ -250,6 +250,11 @@ def valuations(self, f): - ``f`` -- a polynomial in the domain of this valuation + - ``coefficients`` -- the coefficients of ``f`` as produced by + :meth:`coefficients` or ``None`` (default: ``None``); this can be + used to speed up the computation when the expansion of ``f`` is + already known from a previous computation. + OUTPUT: A list, each entry a rational numbers or infinity, the valuations of `f_0, f_1\phi, \dots` @@ -267,7 +272,7 @@ def valuations(self, f): """ f = self.domain().coerce(f) - for c in self.coefficients(f): + for c in coefficients or self.coefficients(f): yield self._base_valuation(c) @cached_method From 79b33801b7aca5b73184641031a0f50ac57c5921 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 7 Dec 2016 13:16:26 -0500 Subject: [PATCH 178/740] Fix _equivalence_reduction for unramified steps --- inductive_valuation.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index 562abca5283..2fc8f24dcdf 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -928,7 +928,8 @@ def _equivalence_reduction(self, f): assert self.residue_ring() is v.residue_ring() return v._equivalence_reduction(f) - valuations = list(self.valuations(f)) + coefficients = list(self.coefficients(f)) + valuations = list(self.valuations(f, coefficients=coefficients)) valuation = min(valuations) for phi_divides in range(len(valuations)): # count how many times phi divides f @@ -936,7 +937,9 @@ def _equivalence_reduction(self, f): break if phi_divides: - f = f.parent()(list(self.coefficients(f))[phi_divides:])(self.phi()) + from sage.rings.all import PolynomialRing + R = PolynomialRing(f.parent(), 'phi') + f = R(coefficients[phi_divides:])(self.phi()) valuation -= self.mu()*phi_divides R = self.equivalence_unit(-valuation) From 6445ddf6e46166f9e742962027f7e49b02b78bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 7 Dec 2016 13:17:36 -0500 Subject: [PATCH 179/740] docstring for mu() --- inductive_valuation.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/inductive_valuation.py b/inductive_valuation.py index 2fc8f24dcdf..5121495882c 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -896,6 +896,18 @@ def is_minimal(self, f, assume_equivalence_irreducible=False): @cached_method def mu(self): + r""" + Return the valuation of :meth:`phi`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v.mu() + 0 + + """ return self(self.phi()) def _equivalence_reduction(self, f): From ee3cb6b210c95dd0a970eb291306fc59ed1fbeaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 8 Dec 2016 21:45:22 -0500 Subject: [PATCH 180/740] Move test method to the right place --- valuation_space.py | 72 +++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/valuation_space.py b/valuation_space.py index 8431ab8f338..0b98629dae1 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -863,42 +863,6 @@ def _weakly_separating_element(self, other): return ret raise NotImplementedError("weakly separating element for %r and %r"%(self, other)) - def _test_scale(self, **options): - r""" - Check that :meth:`scale` works correctly. - - TESTS:: - - sage: from mac_lane import * # optional: standalone - sage: v = pAdicValuation(ZZ, 3) - sage: v._test_scale() - - """ - tester = self._tester(**options) - - from sage.rings.all import infinity, QQ - from trivial_valuation import TrivialValuation, TrivialPseudoValuation - - tester.assertEqual(QQ(0)*self, TrivialValuation(self.domain())) - tester.assertEqual(infinity*self, TrivialPseudoValuation(self.domain())) - - for s in tester.some_elements(QQ.some_elements()): - if s < 0: - with tester.assertRaises(ValueError): - s * self - continue - if s == 0: - continue - - scaled = s * self - - tester.assertEqual(self.is_trivial(), scaled.is_trivial()) - if not self.is_trivial(): - tester.assertEqual(self.uniformizer(), scaled.uniformizer()) - tester.assertEqual(scaled(self.uniformizer()), s * self(self.uniformizer())) - unscaled = scaled / s - tester.assertEqual(self, unscaled) - def shift(self, x, s): r""" Shift ``x`` in its expansion with respect to :meth:`uniformizer` by @@ -985,6 +949,42 @@ def _test_shift(self, **options): continue self.shift(x, s) + def _test_scale(self, **options): + r""" + Check that :meth:`scale` works correctly. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 3) + sage: v._test_scale() + + """ + tester = self._tester(**options) + + from sage.rings.all import infinity, QQ + from trivial_valuation import TrivialValuation, TrivialPseudoValuation + + tester.assertEqual(QQ(0)*self, TrivialValuation(self.domain())) + tester.assertEqual(infinity*self, TrivialPseudoValuation(self.domain())) + + for s in tester.some_elements(QQ.some_elements()): + if s < 0: + with tester.assertRaises(ValueError): + s * self + continue + if s == 0: + continue + + scaled = s * self + + tester.assertEqual(self.is_trivial(), scaled.is_trivial()) + if not self.is_trivial(): + tester.assertEqual(self.uniformizer(), scaled.uniformizer()) + tester.assertEqual(scaled(self.uniformizer()), s * self(self.uniformizer())) + unscaled = scaled / s + tester.assertEqual(self, unscaled) + def _test_add(self, **options): r""" Check that the (strict) triangle equality is satisfied for the From 0c46e05865c77355bee8fe64fda9508c29e22b26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 8 Dec 2016 21:45:39 -0500 Subject: [PATCH 181/740] Handle number fields written as quotients --- padic_valuation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/padic_valuation.py b/padic_valuation.py index af1bc849fbe..81719570134 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -1134,7 +1134,8 @@ def _to_base_domain(self, f): x """ - return f.polynomial()(self._base_valuation.domain().gen()) + polynomial = f.polynomial() if hasattr(f,'polynomial') else f.lift() + return polynomial(self._base_valuation.domain().gen()) def _from_base_domain(self, f): r""" From d354a73563bb6591510ad21f3db846a4c98385d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 8 Dec 2016 22:31:46 -0500 Subject: [PATCH 182/740] simplifications for p-adics --- padic_valuation.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++ valuation_space.py | 65 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) diff --git a/padic_valuation.py b/padic_valuation.py index 81719570134..1ee26877d82 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -990,6 +990,35 @@ def shift(self, x, s): v = ZZ(s / self.domain().ramification_index()) return x << v + def simplify(self, x, error=None): + r""" + Return a simplified version of ``x``. + + Produce an element which differs from ``x`` by an element of + valuation strictly greater than the valuation of ``x`` (or strictly + greater than ``error`` if set.) + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R = Zp(2) + sage: v = pAdicValuation(R, 2) + sage: v.simplify(6) + 2 + O(2^21) + sage: v.simplify(6, error=0) + 0 + + """ + x = self.domain().coerce(x) + + if error is None: + error = self(x) + from sage.rings.all import infinity + if error is infinity: + return x + return x.add_bigoh(error + self.value_group().gen()).lift_to_precision() + + class pAdicValuation_int(pAdicValuation_base): r""" A `p`-adic valuation on the integers or the rationals. @@ -1090,6 +1119,48 @@ def _ge_(self, other): return self.p() == other.p() return super(pAdicValuation_base, self)._ge_(other) + def simplify(self, x, error=None): + r""" + Return a simplified version of ``x``. + + Produce an element which differs from ``x`` by an element of + valuation strictly greater than the valuation of ``x`` (or strictly + greater than ``error`` if set.) + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 2) + sage: v.simplify(6) + 2 + sage: v.simplify(6, error=0) + 0 + + """ + x = self.domain().coerce(x) + + v = self(x) + if error is None: + error = v + from sage.rings.all import infinity + if error is infinity: + return x + if error < v: + return self.domain().zero() + + from sage.rings.all import Qp + precision_ring = Qp(self.p(), error + 1 - v) + reduced = precision_ring(x) + if error - v >= 5: + # If there is not much relative precision left, it is better to + # just go with the integer/rational lift. The rational + # reconstruction is likely not smaller. + reconstruction = reduced.rational_reconstruction() + if reconstruction in self.domain(): + return self.domain()(reconstruction) + + return self.domain()(reduced.lift()) + class pAdicFromLimitValuation(FiniteExtensionFromLimitValuation, pAdicValuation_base): r""" diff --git a/valuation_space.py b/valuation_space.py index 0b98629dae1..5fa62bdaf69 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -919,6 +919,71 @@ def shift(self, x, s): x //= self.uniformizer() return x + def simplify(self, x, error=None): + r""" + Return a simplified version of ``x``. + + Produce an element which differs from ``x`` by an element of + valuation strictly greater than the valuation of ``x`` (or strictly + greater than ``error`` if set.) + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 2) + sage: v.simplify(6) + 2 + sage: v.simplify(6, error=0) + 0 + + """ + x = self.domain().coerce(x) + + if error is not None and self(x) > error: + return self.domain().zero() + return x + + def _test_simplify(self, **options): + r""" + Check that :meth:`simplify` works correctly. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 3) + sage: v._test_simplify() + + """ + tester = self._tester(**options) + + try: + k = self.residue_ring() + has_residue_ring = True + except NotImplementedError: + # over non-fields (and especially polynomial rings over + # non-fields) computation of the residue ring is often + # difficult and not very interesting + has_residue_ring = False + + X = self.domain().some_elements() + for x in tester.some_elements(X): + y = self.simplify(x) + tester.assertEqual(self(x), self(y)) + if self(x) >= 0 and has_residue_ring: + tester.assertEqual(self.reduce(x), self.reduce(y)) + + if self.is_trivial() and not self.is_discrete_valuation(): + return + + S = self.value_group().some_elements() + from itertools import product + for x,s in tester.some_elements(product(X, S)): + y = self.simplify(x, error=s) + if self.domain().is_exact(): + tester.assertGreaterEqual(self(x-y), s) + elif hasattr(y, 'precision_absolute'): + tester.assertGreaterEqual(self(x-y), min(s, y.precision_absolute())) + def _test_shift(self, **options): r""" Check that :meth:`shift` works correctly. From b20487f8e6f85911e59972bdf6d3aa2a838031f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 9 Dec 2016 15:35:14 -0500 Subject: [PATCH 183/740] Implement missing simplifications --- augmented_valuation.py | 59 ++++++++++++++++++++++++++++++++++++++++++ gauss_valuation.py | 26 +++++++++++++++++++ limit_valuation.py | 35 ++++++++++++++++++++++--- mapped_valuation.py | 27 +++++++++++++++++++ 4 files changed, 144 insertions(+), 3 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 7a74304981a..2879017ba94 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -1574,6 +1574,36 @@ def valuations(self, f, coefficients=None): else: yield v + i*self._mu + def simplify(self, f, error=None): + r""" + Return a simplified version of ``f``. + + Produce an element which differs from ``f`` by an element of valuation + strictly greater than the valuation of ``f`` (or strictly greater than + ``error`` if set.) + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4, 5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, 1/2) + sage: w.simplify(x^10/2 + 1) + (u + 1)*2^-1 + O(2^4) + + """ + f = self.domain().coerce(f) + + coefficients = list(self.coefficients(f)) + if error is None: + error = min(self.valuations(f, coefficients=coefficients)) + + from sage.rings.all import PolynomialRing + R = PolynomialRing(f.parent(), 'phi') + f = R([self._base_valuation.simplify(c, error=error - i*self._mu) for i,c in enumerate(coefficients)]) + return f(self.phi()) + class FinalFiniteAugmentedValuation(FiniteAugmentedValuation, FinalAugmentedValuation): r""" @@ -1734,3 +1764,32 @@ def valuations(self, f, coefficients=None): yield self._base_valuation(coefficients or self.coefficients(f).next()) for i in range(num_infty_coefficients): yield infinity + + def simplify(self, f, error=None): + r""" + Return a simplified version of ``f``. + + Produce an element which differs from ``f`` by an element of valuation + strictly greater than the valuation of ``f`` (or strictly greater than + ``error`` if set.) + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4, 5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, infinity) + sage: w.simplify(x^10/2 + 1) + (u + 1)*2^-1 + O(2^4) + + """ + f = self.domain().coerce(f) + + if error is None: + error = self(f) + + if error is infinity: + return f + + return self.domain()(self._base_valuation.simplify(self.coefficients(f).next(), error)) diff --git a/gauss_valuation.py b/gauss_valuation.py index f098f4c5f84..f8c08540c2e 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -677,3 +677,29 @@ def scale(self, scalar): if scalar in QQ and scalar > 0 and scalar != 1: return GaussValuation(self.domain(), self._base_valuation.scale(scalar)) return super(GaussValuation_generic, self).scale(scalar) + + def simplify(self, f, error=None): + r""" + Return a simplified version of ``f``. + + Produce an element which differs from ``f`` by an element of valuation + strictly greater than the valuation of ``f`` (or strictly greater than + ``error`` if set.) + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4, 5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, infinity) + sage: w.simplify(x^10/2 + 1) + (u + 1)*2^-1 + O(2^4) + + """ + f = self.domain().coerce(f) + + if error is None: + error = self(f) + + return f.map_coefficients(lambda c: self._base_valuation.simplify(c, error)) diff --git a/limit_valuation.py b/limit_valuation.py index 2f207aa26b3..44ecf8c658a 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -725,11 +725,40 @@ def element_with_valuation(self, s): sage: R. = K[] sage: L. = K.extension(t^2 + 1) sage: v = pAdicValuation(QQ, 2) - sage: w = v.extension(L) - sage: v = pAdicValuation(QQ, 2) - sage: u, = v.extensions(L) + sage: u = v.extension(L) sage: u.element_with_valuation(1/2) t + 1 """ return self._initial_approximation.element_with_valuation(s) + + def simplify(self, f, error=None): + r""" + Return a simplified version of ``f``. + + Produce an element which differs from ``f`` by an element of valuation + strictly greater than the valuation of ``f`` (or strictly greater than + ``error`` if set.) + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: u = v.extension(L) + sage: u.simplify(t + 1024) + 1 + + """ + f = self.domain().coerce(f) + + v = self(f) + # now _approximation is sufficiently precise to compute a valid + # simplification of f + + if error is None: + error = v + + return self._approximation.simplify(f, error) diff --git a/mapped_valuation.py b/mapped_valuation.py index db6760f2630..90e4a8499cb 100644 --- a/mapped_valuation.py +++ b/mapped_valuation.py @@ -407,6 +407,33 @@ def _weakly_separating_element(self, other): return self.domain()(self._base_valuation._weakly_separating_element(other._base_valuation)) super(FiniteExtensionFromInfiniteValuation, self)._weakly_separating_element(other) + def simplify(self, x, error=None): + r""" + Return a simplified version of ``x``. + + Produce an element which differs from ``x`` by an element of + valuation strictly greater than the valuation of ``x`` (or strictly + greater than ``error`` if set.) + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 5) + sage: u,uu = v.extensions(L) + sage: u.simplify(125*t + 1) + 1 + + """ + x = self.domain().coerce(x) + + if error is None: + error = self(x) + + return self._from_base_domain(self._base_valuation.simplify(self._to_base_domain(x), error)) + class FiniteExtensionFromLimitValuation(FiniteExtensionFromInfiniteValuation): r""" From ae9005a05bf5cb340197315c9fe921154019201d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 9 Dec 2016 15:35:28 -0500 Subject: [PATCH 184/740] Fix coefficient passing for infinite valuations --- augmented_valuation.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 2879017ba94..2d2dbb904c6 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -1761,7 +1761,11 @@ def valuations(self, f, coefficients=None): f = self.domain().coerce(f) num_infty_coefficients = f.degree() // self.phi().degree() - yield self._base_valuation(coefficients or self.coefficients(f).next()) + if coefficients is not None: + constant_coefficient = coefficients[0] + else: + constant_coefficient = self.coefficients(f).next() + yield self._base_valuation(constant_coefficient) for i in range(num_infty_coefficients): yield infinity From b039a19675b6a7f4f1ac1fe8bf84ebe3d2e2da3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 9 Dec 2016 15:35:45 -0500 Subject: [PATCH 185/740] removed redundant doctest --- limit_valuation.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/limit_valuation.py b/limit_valuation.py index 44ecf8c658a..1ba578f5a6a 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -704,8 +704,6 @@ def value_semigroup(self): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) - sage: w = v.extension(L) sage: v = pAdicValuation(QQ, 5) sage: u,uu = v.extensions(L) # long time sage: u.value_semigroup() # long time From 063a38e046ea8be83ee046c2a61cf588b5680a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 9 Dec 2016 15:35:58 -0500 Subject: [PATCH 186/740] p-adics store their valuations as integers i.e., they are not normalized such that p has valuation 1. --- padic_valuation.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/padic_valuation.py b/padic_valuation.py index 1ee26877d82..6bc2bc6a0b0 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -1016,7 +1016,8 @@ def simplify(self, x, error=None): from sage.rings.all import infinity if error is infinity: return x - return x.add_bigoh(error + self.value_group().gen()).lift_to_precision() + normalized_error = (error / self.value_group().gen()).ceil() + return x.add_bigoh(normalized_error + 1).lift_to_precision() class pAdicValuation_int(pAdicValuation_base): @@ -1147,6 +1148,7 @@ def simplify(self, x, error=None): return x if error < v: return self.domain().zero() + error = error.ceil() from sage.rings.all import Qp precision_ring = Qp(self.p(), error + 1 - v) From 10e29822a90af8c21643a3e6ea22ea332139439a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 9 Dec 2016 15:52:07 -0500 Subject: [PATCH 187/740] Simplify element_with_valuation and make it cached --- augmented_valuation.py | 5 ++++- inductive_valuation.py | 3 ++- valuation.py | 6 +++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 2d2dbb904c6..88a4f3b7bef 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -369,6 +369,7 @@ def equivalence_unit(self, s): assert self(ret) == s return ret + @cached_method def element_with_valuation(self, s): """ Create an element of minimal degree and of valuation ``s``. @@ -406,12 +407,14 @@ def element_with_valuation(self, s): """ if s not in self.value_group(): raise ValueError("s must be in the value group of the valuation") + error = s ret = self.domain().one() while s not in self._base_valuation.value_group(): ret *= self._phi s -= self._mu - return ret * self._base_valuation.element_with_valuation(s) + ret = ret * self._base_valuation.element_with_valuation(s) + return self.simplify(ret, error=error) def _repr_(self): """ diff --git a/inductive_valuation.py b/inductive_valuation.py index 5121495882c..78e6ec9a899 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -1089,7 +1089,7 @@ def equivalence_decomposition(self, f, partial=False): sage: V=v1.mac_lane_step(G) # long time sage: v2=V[0] # long time sage: v2.equivalence_decomposition(G) # long time - (x^4 + 4*x^3 + 6*x^2 + 4*x + alpha^4 + alpha^3 + 1)^3 * (x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*alpha^4 + alpha^3 - 27*alpha + 1)^3 * (x^4 + 4*x^3 + 6*x^2 + 4*x + 3/2*alpha^4 + alpha^3 - 27*alpha + 1)^3 + (x^4 + 4*x^3 + 6*x^2 + 4*x + alpha^3 + 2*alpha + 1)^3 * (x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*alpha^4 + alpha^3 + alpha + 1)^3 * (x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*alpha^4 + alpha^3 + 3*alpha + 1)^3 REFERENCES: @@ -1125,6 +1125,7 @@ def equivalence_decomposition(self, f, partial=False): unit *= self.lift(self.residue_ring()(prod([ psi.leading_coefficient()**e for psi,e in F ]))) F = [(self.lift_to_key(psi/psi.leading_coefficient()),e) for psi,e in F] unit *= prod([self.equivalence_reciprocal(self.equivalence_unit(self(g)))**e for g,e in F]) + unit = self.simplify(unit) if phi_divides: for i,(g,e) in enumerate(F): diff --git a/valuation.py b/valuation.py index 5d58d6fb4a8..b3b45fae255 100644 --- a/valuation.py +++ b/valuation.py @@ -606,9 +606,9 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): 1/3 sage: G=Delta.change_ring(K) sage: V=vK.mac_lane_approximants(G); V # long time - [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + theta^4 + theta^3 + 1) = 5/3 ], - [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*theta^4 + theta^3 - 27*theta + 1) = 5/3 ], - [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + 3/2*theta^4 + theta^3 - 27*theta + 1) = 5/3 ]] + [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + theta^3 + 2*theta + 1) = 5/3 ], + [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*theta^4 + theta^3 + theta + 1) = 5/3 ], + [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*theta^4 + theta^3 + 3*theta + 1) = 5/3 ]] """ R = G.parent() From b8ba8cddb1099bbb394221c228ed7357e465ea1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 9 Dec 2016 15:52:44 -0500 Subject: [PATCH 188/740] Handle int inputs in simplify --- padic_valuation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/padic_valuation.py b/padic_valuation.py index 6bc2bc6a0b0..fd38f17a4eb 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -1148,7 +1148,8 @@ def simplify(self, x, error=None): return x if error < v: return self.domain().zero() - error = error.ceil() + from sage.rings.all import QQ + error = QQ(error).ceil() from sage.rings.all import Qp precision_ring = Qp(self.p(), error + 1 - v) From 9d40ef5c1918484d35b6c46f293d05c4f985d440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 9 Dec 2016 15:59:41 -0500 Subject: [PATCH 189/740] Simplify lifts to key --- augmented_valuation.py | 1 + inductive_valuation.py | 2 +- valuation.py | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 88a4f3b7bef..2495992ea26 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -1407,6 +1407,7 @@ def lift_to_key(self, F): CV[-1] = (CV[-1][0].parent().one(), vf) ret = self.domain().change_ring(self.domain())([c for c,v in CV])(self.phi()) + ret = self.simplify(ret) ret = ret.map_coefficients(lambda c:_lift_to_maximal_precision(c)) assert (ret == self.phi()) == (F == F.parent().gen()) assert self.is_key(ret) diff --git a/inductive_valuation.py b/inductive_valuation.py index 78e6ec9a899..2db9b355f86 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -1089,7 +1089,7 @@ def equivalence_decomposition(self, f, partial=False): sage: V=v1.mac_lane_step(G) # long time sage: v2=V[0] # long time sage: v2.equivalence_decomposition(G) # long time - (x^4 + 4*x^3 + 6*x^2 + 4*x + alpha^3 + 2*alpha + 1)^3 * (x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*alpha^4 + alpha^3 + alpha + 1)^3 * (x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*alpha^4 + alpha^3 + 3*alpha + 1)^3 + (x^4 + 4*x^3 + 6*x^2 + 4*x + 2*alpha + 3)^3 * (x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*alpha^4 + alpha + 3)^3 * (x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*alpha^4 + 3*alpha + 3)^3 REFERENCES: diff --git a/valuation.py b/valuation.py index b3b45fae255..2170496a0dd 100644 --- a/valuation.py +++ b/valuation.py @@ -606,9 +606,9 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): 1/3 sage: G=Delta.change_ring(K) sage: V=vK.mac_lane_approximants(G); V # long time - [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + theta^3 + 2*theta + 1) = 5/3 ], - [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*theta^4 + theta^3 + theta + 1) = 5/3 ], - [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*theta^4 + theta^3 + 3*theta + 1) = 5/3 ]] + [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + 2*theta + 3) = 5/3 ], + [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*theta^4 + theta + 3) = 5/3 ], + [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*theta^4 + 3*theta + 3) = 5/3 ]] """ R = G.parent() From e8b4fe91c7a480483e7625245b9b93268f804b54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 12 Dec 2016 23:28:46 -0500 Subject: [PATCH 190/740] Reimplemented MacLane algorithm to provide more granular control over the amount of precision produced (and get better runtime) --- developing_valuation.py | 5 +- inductive_valuation.py | 200 +++++++++++++++++++++------------------- limit_valuation.py | 2 +- valuation.py | 177 ++++++++++++++++++++--------------- 4 files changed, 212 insertions(+), 172 deletions(-) diff --git a/developing_valuation.py b/developing_valuation.py index a3ca532426c..7ab1e518b20 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -86,7 +86,7 @@ def phi(self): """ return self._phi - def effective_degree(self, f): + def effective_degree(self, f, valuations=None): r""" Return the effective degree of ``f`` with respect to this valuation. @@ -115,7 +115,8 @@ def effective_degree(self, f): if f.is_zero(): raise ValueError("the effective degree is only defined for non-zero polynomials") - valuations = list(self.valuations(f)) + if valuations is None: + valuations = list(self.valuations(f)) v = min(valuations) return [i for i,w in enumerate(valuations) if w == v][-1] diff --git a/inductive_valuation.py b/inductive_valuation.py index 2db9b355f86..6953795245e 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -56,7 +56,7 @@ class InductiveValuation(DevelopingValuation): sage: TestSuite(v).run() # long time """ - def is_equivalence_unit(self, f): + def is_equivalence_unit(self, f, valuations=None): r""" Return whether ``f`` is an equivalence unit, i.e., an element of :meth:`effective_degree` zero (see [ML1936'] p.497.) @@ -83,7 +83,7 @@ def is_equivalence_unit(self, f): if f.is_zero(): return False - return self.effective_degree(f) == 0 + return self.effective_degree(f, valuations=valuations) == 0 def equivalence_reciprocal(self, f): r""" @@ -179,6 +179,22 @@ def equivalence_reciprocal(self, f): return h + @cached_method + def mu(self): + r""" + Return the valuation of :meth:`phi`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v.mu() + 0 + + """ + return self(self.phi()) + @abstract_method def equivalence_unit(self, s): """ @@ -631,7 +647,7 @@ def augmentation(self, phi, mu, check=True): from augmented_valuation import AugmentedValuation return AugmentedValuation(self, phi, mu, check) - def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducible=False): + def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducible=False, report_degree_bounds=False): r""" Perform an approximation step towards the squarefree monic non-constant integral polynomial ``G`` which is not an :meth:`equivalence_unit`. @@ -666,10 +682,12 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib if not G.is_monic(): raise ValueError("G must be monic") - if self(G) < 0: + valuations = list(self.valuations(G)) + + if min(valuations) < 0: raise ValueError("G must be integral") - if self.is_equivalence_unit(G): + if self.is_equivalence_unit(G, valuations=valuations): raise ValueError("G must not be an equivalence-unit") if not assume_squarefree and not G.is_squarefree(): @@ -678,83 +696,95 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib from sage.rings.all import infinity assert self(G) is not infinity # this is a valuation and G is non-zero + ret = [] + if self.is_key(G, assume_equivalence_irreducible=assume_equivalence_irreducible): - return [self.augmentation(G, infinity, check=False)] + ret.append((self.augmentation(G, infinity, check=False), G.degree())) + else: + F = self.equivalence_decomposition(G, assume_not_equivalence_unit=True) + assert len(F), "%s equivalence-decomposes as an equivalence-unit %s"%(G, F) + + for phi,e in F: + if G == phi: + # Something strange happened here: + # G is not a key (we checked that before) but phi==G is; so phi must have less precision than G + # this can happen if not all coefficients of G have the same precision + # if we drop some precision of G then it will be a key (but is + # that really what we should do?) + assert not G.base_ring().is_exact() + prec = min([c.precision_absolute() for c in phi.list()]) + g = G.map_coefficients(lambda c:c.add_bigoh(prec)) + assert self.is_key(g) + ret.append((self.augmentation(g, infinity, check=False), g.degree())) + assert len(F) == 1 + break - F = self.equivalence_decomposition(G) - assert len(F), "%s equivalence-decomposes as an equivalence-unit %s"%(G, F) + if phi == self.phi(): + # a factor phi in the equivalence decomposition means that we + # found an actual factor of G, i.e., we can set + # v(phi)=infinity + # However, this should already have happened in the last step + # (when this polynomial had -infinite slope in the Newton + # polygon.) + if self.is_gauss_valuation(): # unless in the first step + pass + else: + continue - ret = [] - for phi,e in F: - if G == phi: - # Something strange happened here: - # G is not a key (we checked that before) but phi==G is; so phi must have less precision than G - # this can happen if not all coefficients of G have the same precision - # if we drop some precision of G then it will be a key (but is - # that really what we should do?) - assert not G.base_ring().is_exact() - prec = min([c.precision_absolute() for c in phi.list()]) - g = G.map_coefficients(lambda c:c.add_bigoh(prec)) - assert self.is_key(g) - return [self.augmentation(g, infinity, check=False)] - - if phi == self.phi(): - # a factor phi in the equivalence decomposition means that we - # found an actual factor of G, i.e., we can set - # v(phi)=infinity - # However, this should already have happened in the last step - # (when this polynomial had -infinite slope in the Newton - # polygon.) - if self.is_gauss_valuation(): # unless in the first step - pass - else: + verbose("Determining the augmentation for %s"%phi, level=11) + w = self.augmentation(phi, self(phi), check=False) + NP = w.newton_polygon(G).principal_part() + verbose("Newton-Polygon for v(phi)=%s : %s"%(self(phi), NP), level=11) + slopes = NP.slopes(repetition=True) + multiplicities = {slope : len([s for s in slopes if s == slope]) for slope in slopes} + slopes = multiplicities.keys() + if NP.vertices()[0][0] != 0: + slopes = [-infinity] + slopes + multiplicities[-infinity] = 1 + + if not slopes: + q,r = G.quo_rem(phi) + assert not r.is_zero() + phi = phi.coefficients(sparse=False) + for i,c in enumerate(r.coefficients(sparse=False)): + if not c.is_zero(): + v = w(c) + # for a correct result we need to add O(pi^v) in degree i + # we try to find the coefficient of phi where such an + # error can be introduced without losing much absolute + # precision on phi + best = i + for j in range(i): + if w(q[j]) < w(q[best]): + best = j + # now add the right O() to phi in degree i - best + phi[i-best] = phi[i-best].add_bigoh(w(c)-w(q[best])) + + phi = G.parent()(phi) + w = self._base_valuation.augmentation(phi, infinity, check=False) + ret.append((w, phi.degree())) continue - verbose("Determining the valuation for %s"%phi, level=11) - w = self.augmentation(phi, self(phi), check=False) - NP = w.newton_polygon(G).principal_part() - verbose("Newton-Polygon for v(phi)=%s : %s"%(self(phi), NP), level=11) - slopes = NP.slopes(repetition=False) - if NP.vertices()[0][0] != 0: - slopes = [-infinity] + slopes - - if not slopes: - q,r = G.quo_rem(phi) - assert not r.is_zero() - phi = phi.coefficients(sparse=False) - for i,c in enumerate(r.coefficients(sparse=False)): - if not c.is_zero(): - v = w(c) - # for a correct result we need to add O(pi^v) in degree i - # we try to find the coefficient of phi where such an - # error can be introduced without losing much absolute - # precision on phi - best = i - for j in range(i): - if w(q[j]) < w(q[best]): - best = j - # now add the right O() to phi in degree i - best - phi[i-best] = phi[i-best].add_bigoh(w(c)-w(q[best])) - - phi = G.parent()(phi) - w = self._base_valuation.augmentation(phi, infinity, check=False) - ret.append(w) - continue - - for i in range(len(slopes)): - slope = slopes[i] - verbose("Slope = %s"%slope, level=12) - new_mu = self(phi) - slope - base = self - if phi.degree() == base.phi().degree(): - assert new_mu > self(phi) - if not base.is_gauss_valuation(): - base = base._base_valuation - new_leaf = base.augmentation(phi, new_mu, check=False) - assert slope is -infinity or 0 in new_leaf.newton_polygon(G).slopes(repetition=False) - ret.append(new_leaf) + for i, slope in enumerate(slopes): + slope = slopes[i] + verbose("Slope = %s"%slope, level=12) + new_mu = self(phi) - slope + base = self + if phi.degree() == base.phi().degree(): + assert new_mu > self(phi) + if not base.is_gauss_valuation(): + base = base._base_valuation + w = base.augmentation(phi, new_mu, check=False) + assert slope is -infinity or 0 in w.newton_polygon(G).slopes(repetition=False) + + from sage.rings.all import ZZ + assert (phi.degree() / self.phi().degree()) in ZZ + degree_bound = multiplicities[slope] * self.phi().degree() + ret.append((w, degree_bound)) assert ret + if not report_degree_bounds: + ret = [v for v,_ in ret] return ret def is_key(self, phi, explain=False, assume_equivalence_irreducible=False): @@ -894,22 +924,6 @@ def is_minimal(self, f, assume_equivalence_irreducible=False): list(self.valuations(f))[0] == self(f) and \ tau.divides(len(list(self.coefficients(f))) - 1) - @cached_method - def mu(self): - r""" - Return the valuation of :meth:`phi`. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) - sage: v.mu() - 0 - - """ - return self(self.phi()) - def _equivalence_reduction(self, f): r""" Helper method for :meth:`is_equivalence_irreducible` and @@ -1003,7 +1017,7 @@ def is_equivalence_irreducible(self, f): if phi_divides > 1: return False - def equivalence_decomposition(self, f, partial=False): + def equivalence_decomposition(self, f, assume_not_equivalence_unit=False): r""" Return an equivalence decomposition of ``f``, i.e., a polynomial `g(x)=e(x)\prod_i \phi_i(x)` with `e(x)` an equivalence unit (see @@ -1104,7 +1118,7 @@ def equivalence_decomposition(self, f, partial=False): raise ValueError("equivalence decomposition of zero is not defined") from sage.structure.factorization import Factorization - if self.is_equivalence_unit(f): + if not assume_not_equivalence_unit and self.is_equivalence_unit(f): return Factorization([], unit=f, sort=False) if not self.domain().base_ring().is_field(): diff --git a/limit_valuation.py b/limit_valuation.py index 1ba578f5a6a..67c8ac4b2fe 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -406,7 +406,7 @@ def lift(self, F): sage: v = FunctionFieldValuation(K, 1) sage: w = v.extensions(L)[0]; w - [ (x - 1)-adic valuation, v(y^2 - 2) = 1 ]-adic valuation + [ (x - 1)-adic valuation, v(y^2 - x - 1) = +Infinity ]-adic valuation sage: s = w.reduce(y); s u1 sage: w.lift(s) # indirect doctest diff --git a/valuation.py b/valuation.py index 2170496a0dd..5785ddecd41 100644 --- a/valuation.py +++ b/valuation.py @@ -393,30 +393,45 @@ def is_discrete_valuation(self): """ return True - def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): + def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=True, required_precision=-1, require_incomparability=False, require_maximal_degree=False): r""" Return approximants on `K[x]` for the extensions of this valuation to `L=K[x]/(G)`. - If `G` is an irreducible polynomial, then this corresponds to the + If `G` is an irreducible polynomial, then this corresponds to extensions of this valuation to the completion of `L`. INPUT: - - ``G`` -- a square-free monic integral polynomial defined over a + - ``G`` -- a monic squarefree integral polynomial defined over a univariate polynomial ring over the :meth:`domain` of this valuation. - - ``precision_cap`` -- a number, infinity, or ``None`` (default: - ``None``); the approximants are always determined such that they are in - one-to-one correspondance to the extensions of this valuation to `L` - and such that the approximants have the ramification index and - residual degree of these extensions. - If ``precision_cap`` is not ``None``, then the approximants are - determined such that they last key polynomial also has valuation at - least ``precision_cap``. - - - ``assume_squarefree`` -- a boolean (default: ``False``), whether or - not to assume that ``G`` is squarefree. + - ``assume_squarefree`` -- a boolean (default: ``False``), whether to + assume that ``G`` is squarefree. If ``True``, the squafreeness of + ``G`` is not verified though it is necessary when + ``require_final_EF`` is set for the algorithm to terminate. + + - ``require_final_EF`` -- a boolean (default: ``True``); whether to + require the returned key polynomials to be in one-to-one + correspondance to the extensions of this valuation to ``L`` and + require them to have the ramification index and residue degree of the + valuations they correspond to. + + - ``required_precision`` -- a number or infinity (default: -1); whether + to require the last key polynomial of the returned valuations to have + at least that valuation. + + - ``require_incomparability`` -- a boolean (default: ``False``); + whether to require require the returned valuations to be incomparable + (with respect to the partial order on valuations defined by comparing + them pointwise.) + + - ``require_maximal_degree`` -- a boolean (deault: ``False``); whether + to require the last key polynomial of the returned valuation to have + maximal degree. This is most relevant when using this algorithm to + compute approximate factorizations of ``G``, when set to ``True``, + the last key polynomial has the same degree as the corresponding + factor. EXAMPLES:: @@ -425,18 +440,18 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): sage: R. = QQ[] sage: v.mac_lane_approximants(x^2 + 1) [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/2 ]] - sage: v.mac_lane_approximants(x^2 + 1, precision_cap=infinity) + sage: v.mac_lane_approximants(x^2 + 1, required_precision=infinity) [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/2, v(x^2 + 1) = +Infinity ]] sage: v.mac_lane_approximants(x^2 + x + 1) [[ Gauss valuation induced by 2-adic valuation, v(x^2 + x + 1) = +Infinity ]] Note that ``G`` does not need to be irreducible. Here, we detect a - factor of the polynomial `x + 1` and an approximate factor `x + 1` - (which is an approximation to `x - 1`):: + factor `x + 1` and an approximate factor `x + 1` (which is an + approximation to `x - 1`):: sage: v.mac_lane_approximants(x^2 - 1) [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = +Infinity ], - [ Gauss valuation induced by 2-adic valuation, v(x + 3) = 2 ]] + [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1 ]] However, it needs to be squarefree:: @@ -496,19 +511,19 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): sage: G = x sage: v.mac_lane_approximants(G) [Gauss valuation induced by 2-adic valuation] - sage: v.mac_lane_approximants(G, precision_cap = infinity) + sage: v.mac_lane_approximants(G, required_precision = infinity) [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = +Infinity ]] sage: G = x^2 + 1 sage: v.mac_lane_approximants(G) # optional: integrated [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x + (1 + O(2^10))) = 1/2 ]] - sage: v.mac_lane_approximants(G, precision_cap = infinity) # optional: integrated + sage: v.mac_lane_approximants(G, required_precision = infinity) # optional: integrated [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x + (1 + O(2^10))) = 1/2, v((1 + O(2^10))*x^2 + (1 + O(2^10))) = +Infinity ]] sage: G = x^4 + 2*x^3 + 2*x^2 - 2*x + 2 sage: v.mac_lane_approximants(G) [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = 1/4 ]] - sage: v.mac_lane_approximants(G, infinity) # long time + sage: v.mac_lane_approximants(G, required_precision=infinity) # long time [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = 1/4, v((1 + O(2^10))*x^4 + (2 + O(2^11))*x^3 + (2 + O(2^11))*x^2 + (2 + 2^2 + 2^3 + 2^4 + 2^5 + 2^6 + 2^7 + 2^8 + 2^9 + 2^10 + O(2^11))*x + (2 + O(2^11))) = +Infinity ]] The factorization of primes in the Gaussian integers can be read off @@ -528,7 +543,7 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): sage: v0.mac_lane_approximants(G) [[ Gauss valuation induced by 5-adic valuation, v(x + 2) = 1 ], [ Gauss valuation induced by 5-adic valuation, v(x + 3) = 1 ]] - sage: v0.mac_lane_approximants(G, precision_cap = 10) # long time + sage: v0.mac_lane_approximants(G, required_precision = 10) # long time [[ Gauss valuation induced by 5-adic valuation, v(x + 6139557) = 10 ], [ Gauss valuation induced by 5-adic valuation, v(x + 3626068) = 10 ]] @@ -543,7 +558,7 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): sage: v1,v2 = v.mac_lane_approximants(G); v1,v2 ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + O(5^4))) = 1 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + O(5^4))) = 1 ]) - sage: w1, w2 = v.mac_lane_approximants(G,precision_cap=2); w1,w2 # long time + sage: w1, w2 = v.mac_lane_approximants(G, required_precision = 2); w1,w2 # long time ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + O(5^4))) = 2 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + 3*5 + O(5^4))) = 2 ]) @@ -556,7 +571,7 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): In this example, the process stops with a factorization of `x^2 + 1`:: - sage: v.mac_lane_approximants(G, precision_cap=infinity) # long time + sage: v.mac_lane_approximants(G, required_precision=infinity) # long time [[ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + 2*5^2 + 5^3 + O(5^4))) = +Infinity ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + 3*5 + 2*5^2 + 3*5^3 + O(5^4))) = +Infinity ]] @@ -568,7 +583,7 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): sage: G = x^2 + 1 sage: v.mac_lane_approximants(G) [[ Gauss valuation induced by 5-adic valuation, v(x + 2) = 1 ], [ Gauss valuation induced by 5-adic valuation, v(x + 3) = 1 ]] - sage: v.mac_lane_approximants(G, precision_cap=5) # long time + sage: v.mac_lane_approximants(G, required_precision=5) # long time [[ Gauss valuation induced by 5-adic valuation, v(x + 2057) = 5 ], [ Gauss valuation induced by 5-adic valuation, v(x + 1068) = 6 ]] @@ -614,8 +629,6 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): R = G.parent() if R.base_ring() is not self.domain(): raise ValueError("G must be defined over the domain of this valuation") - if not assume_squarefree and not G.is_squarefree(): - raise ValueError("G must be squarefree") from sage.misc.misc import verbose verbose("Approximants of %r towards %r"%(self, G), level=3) @@ -623,51 +636,60 @@ def mac_lane_approximants(self, G, precision_cap=None, assume_squarefree=False): from sage.rings.all import infinity from gauss_valuation import GaussValuation - def is_sufficiently_precise(v, others): - if precision_cap is None or v(v.phi()) >= precision_cap: - # the precision to which we have determined the approximants is sufficient - if not [w for w in others if w <= v or w>=v]: - # the approximants do not approximate each other - return True - return False + # Leaves in the computation of the tree of approximants. Each vertex + # consists of a triple (v,ef,p) where v is an approximant, i.e., a + # valuation, ef is a boolean, and p is the parent of this vertex. + # The boolean ef denotes whether v already has the final ramification + # index E and residue degree F of this approximant. + # An edge V -- P represents the relation P.v ≤ V.v (pointwise on the + # polynomial ring K[x]) between the valuations. + leaves = [ (GaussValuation(R, self), G.degree() == 1, None) ] + + if require_maximal_degree: + # we can only assert maximality of degrees when E and F are final + require_final_EF = True + + if not assume_squarefree: + if require_final_EF and not G.is_squarefree(): + raise ValueError("G must be squarefree") + else: + # if only required_precision is set, we do not need to check + # whether G is squarefree. If G is not squarefree, we compute + # valuations corresponding to approximants for all the + # squarefree factors of G (up to required_precision.) + pass + + def is_sufficient(leaf, others): + valuation, ef, parent = leaf + if valuation.mu() < required_precision: + return False + if require_final_EF and not ef: + return False + if require_maximal_degree and valuation.phi().degree() != valuation.E()*valuation.F(): + return False + if require_incomparability: + if any(valuation <= o for o in others): + return False + return True - leaves = [ (GaussValuation(R, self), None) ] while True: - ef = [ v.E()*v.F() for v,_ in leaves] - verbose("Augmenting with ef=%r"%(ef,), level=5) - if sum(ef) == G.degree(): - # the ramification indexes and residual degrees are final - if all([is_sufficiently_precise(v, [w for w,_ in leaves if w != v]) for v,_ in leaves]): - break - - expandables = [] new_leaves = [] - for v,parent in leaves: - if v(G) is infinity: - new_leaves.append((v,parent)) + for leaf in leaves: + v, ef, parent = leaf + others = [w for (w,_,_) in leaves if w != v] + if is_sufficient((v,ef,parent), others): + new_leaves.append(leaf) else: - expandables.append((v,parent)) - leaves = new_leaves - - assert expandables - - for v,parent in expandables: - leaves.extend([(w,(v,parent)) for w in v.mac_lane_step(G, assume_squarefree=True)]) + augmentations = v.mac_lane_step(G, report_degree_bounds=True) + for w, bound in augmentations: + ef = bound == w.E()*w.F() + new_leaves.append((w, ef, leaf)) - # rollback each approximant as much as possible to make the - # coefficients of the key polynomials as small as possible - ret = [] - for i in range(len(leaves)): - others = [w for w,_ in leaves if w != leaves[i][0]] - while leaves[i][1] is not None and leaves[i][0].E() == leaves[i][1][0].E() and leaves[i][0].F() == leaves[i][1][0].F() and is_sufficiently_precise(leaves[i][1][0], others): - verbose("Replacing %r with %r which is sufficiently precise"%(leaves[i][0], leaves[i][1][0]), level=9) - leaves[i] = leaves[i][1] - assert is_sufficiently_precise(leaves[i][0], others) - - for v,parent in leaves: - w=v,parent + if leaves == new_leaves: + break + leaves = new_leaves - return [v for v,_ in leaves] + return [v for v,_,_ in leaves] def mac_lane_approximant(self, G, valuation, approximants = None): r""" @@ -729,7 +751,7 @@ def mac_lane_approximant(self, G, valuation, approximants = None): sage: w = GaussValuation(R, v).augmentation(x + 3, 2) sage: v.mac_lane_approximant(G, w) - [ Gauss valuation induced by 2-adic valuation, v(x + 3) = 2 ] + [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1 ] """ if valuation.restriction(valuation.domain().base_ring()) is not self: @@ -764,7 +786,7 @@ def mac_lane_approximant(self, G, valuation, approximants = None): assert len(smaller_approximants) == 1 return smaller_approximants[0] - def montes_factorization(self, G, assume_squarefree=False, precision_cap=None): + def montes_factorization(self, G, assume_squarefree=False, required_precision=None): """ Factor ``G`` over the completion of the domain of this valuation. @@ -775,14 +797,14 @@ def montes_factorization(self, G, assume_squarefree=False, precision_cap=None): - ``assume_squarefree`` -- a boolean (default: ``False``), whether to assume ``G`` to be squarefree - - ``precision_cap`` -- a number, ``None``, or infinity (default: - ``None``); if ``None`` or ``infinity``, the returned are actual - factors of ``G``, otherwise they are only factors with precision at - least ``precision_cap``. + - ``required_precision`` -- a number or infinity (default: + infinity); if ``infinity``, the returned polynomials are actual factors of + ``G``, otherwise they are only factors with precision at least + ``required_precision``. ALGORITHM: - We compute :meth:`mac_lane_approximants` with ``precision_cap``. + We compute :meth:`mac_lane_approximants` with ``required_precision``. The key polynomials approximate factors of ``G``. EXAMPLES:: @@ -805,7 +827,7 @@ def montes_factorization(self, G, assume_squarefree=False, precision_cap=None): sage: v.montes_factorization(x^2 - 1) # not tested, does not terminate - sage: v.montes_factorization(x^2 - 1, precision_cap=10) + sage: v.montes_factorization(x^2 - 1, required_precision=10) (x + 1) * (x + 1023) REFERENCES: @@ -815,6 +837,10 @@ def montes_factorization(self, G, assume_squarefree=False, precision_cap=None): [math.NT] """ + if required_precision is None: + from sage.rings.all import infinity + required_precision = infinity + R = G.parent() if R.base_ring() is not self.domain(): raise ValueError("G must be defined over the domain of this valuation") @@ -823,9 +849,8 @@ def montes_factorization(self, G, assume_squarefree=False, precision_cap=None): if not all([self(c)>=0 for c in G.coefficients()]): raise ValueError("G must be integral") - from sage.rings.all import infinity # W contains approximate factors of G - W = self.mac_lane_approximants(G, precision_cap=precision_cap or infinity,assume_squarefree=assume_squarefree) + W = self.mac_lane_approximants(G, required_precision=required_precision, require_maximal_degree=True, assume_squarefree=assume_squarefree) ret = [w.phi() for w in W] from sage.structure.factorization import Factorization From 05ea97c1597c8d189c3bf3d26261d08aedb44eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 12 Dec 2016 23:56:30 -0500 Subject: [PATCH 191/740] Swap code paths in mac_lane_step to improve performance --- inductive_valuation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index 6953795245e..4f18802268c 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -698,12 +698,12 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib ret = [] - if self.is_key(G, assume_equivalence_irreducible=assume_equivalence_irreducible): + F = self.equivalence_decomposition(G, assume_not_equivalence_unit=True) + assert len(F), "%s equivalence-decomposes as an equivalence-unit %s"%(G, F) + if len(F) == 1 and F[0][1] == 1 and F[0][0].degree() == G.degree(): + assert self.is_key(G, assume_equivalence_irreducible=assume_equivalence_irreducible) ret.append((self.augmentation(G, infinity, check=False), G.degree())) else: - F = self.equivalence_decomposition(G, assume_not_equivalence_unit=True) - assert len(F), "%s equivalence-decomposes as an equivalence-unit %s"%(G, F) - for phi,e in F: if G == phi: # Something strange happened here: From 0a87c38ba01514f1785c131b85823fd70722865a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 13 Dec 2016 00:17:37 -0500 Subject: [PATCH 192/740] faster equivalence reduction from cached reciprocals --- augmented_valuation.py | 12 ++++++++++-- gauss_valuation.py | 10 +++++++++- inductive_valuation.py | 17 +++++++++++------ 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 2495992ea26..3a1fb0f9236 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -315,7 +315,8 @@ def __init__(self, parent, v, phi, mu): self._base_valuation = v self._mu = mu - def equivalence_unit(self, s): + @cached_method + def equivalence_unit(self, s, reciprocal=False): """ Return an equivalence unit of minimal degree and valuation ``s``. @@ -323,6 +324,10 @@ def equivalence_unit(self, s): - ``s`` -- a rational number + - ``reciprocal`` -- a boolean (default: ``False``); whether or not to + return the equivalence unit as the :meth:`equivalence_reciprocal` of + the equivalence unit of valuation ``-s``. + OUTPUT: A polynomial in the domain of this valuation which @@ -363,7 +368,10 @@ def equivalence_unit(self, s): (2^-1 + O(2^4))*x^2 """ - ret = self._base_valuation.element_with_valuation(s) + if reciprocal: + ret = self.equivalence_reciprocal(self.equivalence_unit(-s)) + else: + ret = self._base_valuation.element_with_valuation(s) assert self.is_equivalence_unit(ret) assert self(ret) == s diff --git a/gauss_valuation.py b/gauss_valuation.py index f8c08540c2e..00bf1173f4a 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -412,7 +412,8 @@ def lift_to_key(self, F): return self.lift(F) - def equivalence_unit(self, s): + @cached_method + def equivalence_unit(self, s, reciprocal=False): """ Return an equivalence unit of valuation ``s``. @@ -420,6 +421,10 @@ def equivalence_unit(self, s): - ``s`` -- an element of the :meth:`value_group` + - ``reciprocal`` -- a boolean (default: ``False``); whether or not to + return the equivalence unit as the :meth:`equivalence_reciprocal` of + the equivalence unit of valuation ``-s``. + EXAMPLES:: sage: from mac_lane import * # optional: standalone @@ -431,6 +436,9 @@ def equivalence_unit(self, s): (3^-2 + O(3^3)) """ + if reciprocal: + return self.equivalence_reciprocal(self.equivalence_unit(-s)) + ret = self._base_valuation.element_with_valuation(s) return self.domain()(ret) diff --git a/inductive_valuation.py b/inductive_valuation.py index 4f18802268c..a92508684ec 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -196,7 +196,7 @@ def mu(self): return self(self.phi()) @abstract_method - def equivalence_unit(self, s): + def equivalence_unit(self, s, reciprocal=False): """ Return an equivalence unit of valuation ``s``. @@ -204,6 +204,10 @@ def equivalence_unit(self, s): - ``s`` -- an element of the :meth:`value_group` + - ``reciprocal`` -- a boolean (default: ``False``); whether or not to + return the equivalence unit as the :meth:`equivalence_reciprocal` of + the equivalence unit of valuation ``-s``. + EXAMPLES:: sage: from mac_lane import * # optional: standalone @@ -941,7 +945,7 @@ def _equivalence_reduction(self, f): sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: v._equivalence_reduction(2*x^6 + 4*x^5 + 2*x^4 + 8) - (1/2, 4, x^2 + 1) + (1, 4, x^2 + 1) """ f = self.domain().coerce(f) @@ -969,7 +973,7 @@ def _equivalence_reduction(self, f): valuation -= self.mu()*phi_divides R = self.equivalence_unit(-valuation) - return R, phi_divides, self.reduce(f*R) + return valuation, phi_divides, self.reduce(f*R) def is_equivalence_irreducible(self, f): r""" @@ -1127,18 +1131,19 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False): ret = v.equivalence_decomposition(v.domain()(f)) return Factorization([(g.change_ring(self.domain().base_ring()),e) for g,e in ret], unit=ret.unit().change_ring(self.domain().base_ring()), sort=False) - R, phi_divides, F = self._equivalence_reduction(f) + valuation, phi_divides, F = self._equivalence_reduction(f) F = F.factor() from sage.misc.misc import verbose verbose("%s factors as %s = %s in reduction"%(f, F.prod(), F), level=20) - unit = self.lift(self.residue_ring()(F.unit())) * self.equivalence_reciprocal(R) + R_ = self.equivalence_unit(valuation, reciprocal=True) + unit = self.lift(self.residue_ring()(F.unit())) * R_ F = list(F) from sage.misc.all import prod unit *= self.lift(self.residue_ring()(prod([ psi.leading_coefficient()**e for psi,e in F ]))) F = [(self.lift_to_key(psi/psi.leading_coefficient()),e) for psi,e in F] - unit *= prod([self.equivalence_reciprocal(self.equivalence_unit(self(g)))**e for g,e in F]) + unit *= prod([self.equivalence_unit(-self(g), reciprocal=True)**e for g,e in F]) unit = self.simplify(unit) if phi_divides: From fc7616158c3787919ea0ea7075efc1b8403db9d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 13 Dec 2016 00:35:27 -0500 Subject: [PATCH 193/740] Slight speedup by caching valuations in equivalence_decomposition --- inductive_valuation.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index a92508684ec..75d6b21c288 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -686,7 +686,8 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib if not G.is_monic(): raise ValueError("G must be monic") - valuations = list(self.valuations(G)) + coefficients = list(self.coefficients(G)) + valuations = list(self.valuations(G, coefficients=coefficients)) if min(valuations) < 0: raise ValueError("G must be integral") @@ -702,7 +703,7 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib ret = [] - F = self.equivalence_decomposition(G, assume_not_equivalence_unit=True) + F = self.equivalence_decomposition(G, assume_not_equivalence_unit=True, coefficients=coefficients, valuations=valuations) assert len(F), "%s equivalence-decomposes as an equivalence-unit %s"%(G, F) if len(F) == 1 and F[0][1] == 1 and F[0][0].degree() == G.degree(): assert self.is_key(G, assume_equivalence_irreducible=assume_equivalence_irreducible) @@ -928,7 +929,7 @@ def is_minimal(self, f, assume_equivalence_irreducible=False): list(self.valuations(f))[0] == self(f) and \ tau.divides(len(list(self.coefficients(f))) - 1) - def _equivalence_reduction(self, f): + def _equivalence_reduction(self, f, coefficients=None, valuations=None): r""" Helper method for :meth:`is_equivalence_irreducible` and :meth:`equivalence_decomposition` which essentially returns the @@ -958,8 +959,10 @@ def _equivalence_reduction(self, f): assert self.residue_ring() is v.residue_ring() return v._equivalence_reduction(f) - coefficients = list(self.coefficients(f)) - valuations = list(self.valuations(f, coefficients=coefficients)) + if coefficients is None: + coefficients = list(self.coefficients(f)) + if valuations is None: + valuations = list(self.valuations(f, coefficients=coefficients)) valuation = min(valuations) for phi_divides in range(len(valuations)): # count how many times phi divides f @@ -975,7 +978,7 @@ def _equivalence_reduction(self, f): R = self.equivalence_unit(-valuation) return valuation, phi_divides, self.reduce(f*R) - def is_equivalence_irreducible(self, f): + def is_equivalence_irreducible(self, f, coefficients=None, valuations=None): r""" Return whether the polynomial ``f`` is equivalence-irreducible, i.e., whether its :meth:`equivalence_decomposition` is trivial. @@ -1013,7 +1016,7 @@ def is_equivalence_irreducible(self, f): if f.is_constant(): raise ValueError("f must not be constant") - _, phi_divides, F = self._equivalence_reduction(f) + _, phi_divides, F = self._equivalence_reduction(f, coefficients=coefficients, valuations=valuations) if phi_divides == 0: return F.is_constant() or F.is_irreducible() if phi_divides == 1: @@ -1021,7 +1024,7 @@ def is_equivalence_irreducible(self, f): if phi_divides > 1: return False - def equivalence_decomposition(self, f, assume_not_equivalence_unit=False): + def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coefficients=None, valuations=None): r""" Return an equivalence decomposition of ``f``, i.e., a polynomial `g(x)=e(x)\prod_i \phi_i(x)` with `e(x)` an equivalence unit (see @@ -1131,7 +1134,7 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False): ret = v.equivalence_decomposition(v.domain()(f)) return Factorization([(g.change_ring(self.domain().base_ring()),e) for g,e in ret], unit=ret.unit().change_ring(self.domain().base_ring()), sort=False) - valuation, phi_divides, F = self._equivalence_reduction(f) + valuation, phi_divides, F = self._equivalence_reduction(f, coefficients=coefficients, valuations=valuations) F = F.factor() from sage.misc.misc import verbose verbose("%s factors as %s = %s in reduction"%(f, F.prod(), F), level=20) From d39634a11f4926356f35f68947ad394e42ca0d91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 13 Dec 2016 01:01:33 -0500 Subject: [PATCH 194/740] Minor speedup by computing valuation directly --- augmented_valuation.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 3a1fb0f9236..21c3b1dd156 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -1407,8 +1407,9 @@ def lift_to_key(self, F): f *= self._Q()**F.degree() coefficients = list(self.coefficients(f)) - CV = zip(coefficients, self.valuations(f, coefficients=coefficients)) - vf = self(f) + valuations = list(self.valuations(f, coefficients=coefficients)) + CV = zip(coefficients, valuations) + vf = min(valuations) CV = [(c,v) if v==vf else (c.parent().zero(),infinity) for c,v in CV] while CV[-1][1] is infinity: CV.pop() From 2414d0dd7f7aa32a58a80bf891dbcd21c8c24ac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 13 Dec 2016 01:23:33 -0500 Subject: [PATCH 195/740] Save one valuation evaluation in mac_lane_step --- inductive_valuation.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index 75d6b21c288..1caa4354654 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -737,7 +737,8 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib continue verbose("Determining the augmentation for %s"%phi, level=11) - w = self.augmentation(phi, self(phi), check=False) + old_mu = self(phi) + w = self.augmentation(phi, old_mu, check=False) NP = w.newton_polygon(G).principal_part() verbose("Newton-Polygon for v(phi)=%s : %s"%(self(phi), NP), level=11) slopes = NP.slopes(repetition=True) @@ -773,7 +774,7 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib for i, slope in enumerate(slopes): slope = slopes[i] verbose("Slope = %s"%slope, level=12) - new_mu = self(phi) - slope + new_mu = old_mu - slope base = self if phi.degree() == base.phi().degree(): assert new_mu > self(phi) From 1912ed5835916d4204385b7f0e2607caebc1d508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 13 Dec 2016 01:58:52 -0500 Subject: [PATCH 196/740] Minor speedup by more agressive caching Not sure if this is really worth the little change it makes --- developing_valuation.py | 6 ++++-- inductive_valuation.py | 25 +++++++++++++++---------- valuation.py | 24 +++++++++++++----------- 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/developing_valuation.py b/developing_valuation.py index 7ab1e518b20..468b003de92 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -200,7 +200,7 @@ def _quo_rem_monomial(self, degree): f = self.domain().one() << degree return f.quo_rem(self.phi()) - def newton_polygon(self, f): + def newton_polygon(self, f, valuations=None): r""" Return the newton polygon the `\phi`-adic development of ``f``. @@ -228,7 +228,9 @@ def newton_polygon(self, f): f = self.domain().coerce(f) from sage.geometry.newton_polygon import NewtonPolygon - return NewtonPolygon(enumerate(self.valuations(f))) + if valuations is None: + valuations = self.valuations(f) + return NewtonPolygon(enumerate(valuations)) def _call_(self, f): r""" diff --git a/inductive_valuation.py b/inductive_valuation.py index 1caa4354654..073a01badde 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -651,7 +651,7 @@ def augmentation(self, phi, mu, check=True): from augmented_valuation import AugmentedValuation return AugmentedValuation(self, phi, mu, check) - def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducible=False, report_degree_bounds=False): + def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducible=False, report_degree_bounds_and_caches=False, coefficients=None, valuations=None): r""" Perform an approximation step towards the squarefree monic non-constant integral polynomial ``G`` which is not an :meth:`equivalence_unit`. @@ -686,8 +686,10 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib if not G.is_monic(): raise ValueError("G must be monic") - coefficients = list(self.coefficients(G)) - valuations = list(self.valuations(G, coefficients=coefficients)) + if coefficients is None: + coefficients = list(self.coefficients(G)) + if valuations is None: + valuations = list(self.valuations(G, coefficients=coefficients)) if min(valuations) < 0: raise ValueError("G must be integral") @@ -707,7 +709,7 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib assert len(F), "%s equivalence-decomposes as an equivalence-unit %s"%(G, F) if len(F) == 1 and F[0][1] == 1 and F[0][0].degree() == G.degree(): assert self.is_key(G, assume_equivalence_irreducible=assume_equivalence_irreducible) - ret.append((self.augmentation(G, infinity, check=False), G.degree())) + ret.append((self.augmentation(G, infinity, check=False), G.degree(), None, None)) else: for phi,e in F: if G == phi: @@ -720,7 +722,7 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib prec = min([c.precision_absolute() for c in phi.list()]) g = G.map_coefficients(lambda c:c.add_bigoh(prec)) assert self.is_key(g) - ret.append((self.augmentation(g, infinity, check=False), g.degree())) + ret.append((self.augmentation(g, infinity, check=False), g.degree(), None, None)) assert len(F) == 1 break @@ -739,7 +741,9 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib verbose("Determining the augmentation for %s"%phi, level=11) old_mu = self(phi) w = self.augmentation(phi, old_mu, check=False) - NP = w.newton_polygon(G).principal_part() + w_coefficients = list(w.coefficients(G)) + w_valuations = list(w.valuations(G, coefficients=w_coefficients)) + NP = w.newton_polygon(G, valuations=w_valuations).principal_part() verbose("Newton-Polygon for v(phi)=%s : %s"%(self(phi), NP), level=11) slopes = NP.slopes(repetition=True) multiplicities = {slope : len([s for s in slopes if s == slope]) for slope in slopes} @@ -768,13 +772,14 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib phi = G.parent()(phi) w = self._base_valuation.augmentation(phi, infinity, check=False) - ret.append((w, phi.degree())) + ret.append((w, phi.degree(), None, None)) continue for i, slope in enumerate(slopes): slope = slopes[i] verbose("Slope = %s"%slope, level=12) new_mu = old_mu - slope + new_valuations = [val - (j*slope if slope is not -infinity else (0 if j == 0 else -infinity)) for j,val in enumerate(w_valuations)] base = self if phi.degree() == base.phi().degree(): assert new_mu > self(phi) @@ -786,11 +791,11 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib from sage.rings.all import ZZ assert (phi.degree() / self.phi().degree()) in ZZ degree_bound = multiplicities[slope] * self.phi().degree() - ret.append((w, degree_bound)) + ret.append((w, degree_bound, w_coefficients, new_valuations)) assert ret - if not report_degree_bounds: - ret = [v for v,_ in ret] + if not report_degree_bounds_and_caches: + ret = [v for v,_,_,_ in ret] return ret def is_key(self, phi, explain=False, assume_equivalence_irreducible=False): diff --git a/valuation.py b/valuation.py index 5785ddecd41..125dd0cd008 100644 --- a/valuation.py +++ b/valuation.py @@ -637,13 +637,15 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru from gauss_valuation import GaussValuation # Leaves in the computation of the tree of approximants. Each vertex - # consists of a triple (v,ef,p) where v is an approximant, i.e., a - # valuation, ef is a boolean, and p is the parent of this vertex. + # consists of a tuple (v,ef,p,coeffs,vals) where v is an approximant, i.e., a + # valuation, ef is a boolean, p is the parent of this vertex, and + # coeffs and vals are cached values. (Only v and ef are relevant, + # everything else are caches/debug info.) # The boolean ef denotes whether v already has the final ramification # index E and residue degree F of this approximant. # An edge V -- P represents the relation P.v ≤ V.v (pointwise on the # polynomial ring K[x]) between the valuations. - leaves = [ (GaussValuation(R, self), G.degree() == 1, None) ] + leaves = [ (GaussValuation(R, self), G.degree() == 1, None, None, None) ] if require_maximal_degree: # we can only assert maximality of degrees when E and F are final @@ -660,7 +662,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru pass def is_sufficient(leaf, others): - valuation, ef, parent = leaf + valuation, ef, _, _, _ = leaf if valuation.mu() < required_precision: return False if require_final_EF and not ef: @@ -675,21 +677,21 @@ def is_sufficient(leaf, others): while True: new_leaves = [] for leaf in leaves: - v, ef, parent = leaf - others = [w for (w,_,_) in leaves if w != v] - if is_sufficient((v,ef,parent), others): + v, ef, coefficients, valuations, parent = leaf + others = [w for (w,_,_,_,_) in leaves if w != v] + if is_sufficient(leaf, others): new_leaves.append(leaf) else: - augmentations = v.mac_lane_step(G, report_degree_bounds=True) - for w, bound in augmentations: + augmentations = v.mac_lane_step(G, report_degree_bounds_and_caches=True, coefficients=coefficients, valuations=valuations) + for w, bound, coefficients, valuations in augmentations: ef = bound == w.E()*w.F() - new_leaves.append((w, ef, leaf)) + new_leaves.append((w, ef, coefficients, valuations, leaf)) if leaves == new_leaves: break leaves = new_leaves - return [v for v,_,_ in leaves] + return [v for v,_,_,_,_ in leaves] def mac_lane_approximant(self, G, valuation, approximants = None): r""" From efce59b53142cc703aecdd8f98a4aa95010afd32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 13 Dec 2016 22:27:18 -0500 Subject: [PATCH 197/740] Monkey-patch inverse_of_unit() for polynomial quotient rings --- __init__.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/__init__.py b/__init__.py index d5bc71cc161..73ade7f1ec9 100644 --- a/__init__.py +++ b/__init__.py @@ -370,6 +370,24 @@ def is_surjective(self): sage.rings.fraction_field.CallableConvertMap = CallableConvertMap_patched +# inverses of quotient ring elements +def inverse_of_unit(self): + r""" + TESTS:: + + sage: R. = ZZ[] + sage: S = R.quo(x^2+x+1) + sage: S(1).inverse_of_unit() + 1 + + """ + inverse = ~self + if inverse.parent() is self.parent(): + return inverse + raise NotImplementedError + +sage.rings.polynomial.polynomial_quotient_ring_element.PolynomialQuotientRingElement.inverse_of_unit = inverse_of_unit +del(inverse_of_unit) # factorization in polynomial quotient fields def _factor_univariate_polynomial(self, f): From 402a8a7632a30dfeb0ac58281179d9fa18175997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 13 Dec 2016 22:28:16 -0500 Subject: [PATCH 198/740] Add some injective maps of polynomial quotient rings --- __init__.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/__init__.py b/__init__.py index 73ade7f1ec9..ff282e0a1f0 100644 --- a/__init__.py +++ b/__init__.py @@ -344,6 +344,47 @@ def _coerce_map_from_patched(self, domain): sage.rings.number_field.order.Order._coerce_map_from_ = _coerce_map_from_patched del(_coerce_map_from_patched) +# quotient rings embed if their underlying rings do +class DefaultConvertMap_unique_patched3(sage.structure.coerce_maps.DefaultConvertMap_unique): + def is_injective(self): + r""" + TESTS:: + + sage: R. = ZZ[] + sage: S. = QQ[] + sage: S.quo(x^2 + 1).coerce_map_from(R.quo(x^2 + 1)).is_injective() + True + + """ + if self.codomain().base().coerce_map_from(self.domain().base()).is_injective(): + return True + raise NotImplementedError + +sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRing_generic._coerce_map_from_original = sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRing_generic._coerce_map_from_ +def _coerce_map_from_patched(self, domain): + r""" + TESTS:: + + sage: R. = ZZ[] + sage: S. = QQ[] + sage: S.quo(x^2 + 1).coerce_map_from(R.quo(x^2 + 1)).is_injective() # indirect doctest + True + + """ + from sage.rings.polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing + if is_PolynomialQuotientRing(domain) and domain.modulus() == self.modulus(): + if self.base().has_coerce_map_from(domain.base()): + return DefaultConvertMap_unique_patched3(domain, self) + from sage.rings.fraction_field import is_FractionField + if is_FractionField(domain): + # this should be implemented on a much higher level: + # if there is a morphism R -> K then there is a morphism Frac(R) -> K + if self.has_coerce_map_from(domain.base()): + return True + return self._coerce_map_from_original(domain) +sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRing_generic._coerce_map_from_ = _coerce_map_from_patched +del(_coerce_map_from_patched) + # a ring embeds into its field of fractions class CallableConvertMap_patched(sage.rings.fraction_field.CallableConvertMap): def is_injective(self): From deeb6ed670c67f2489646da0503bd63c49949e18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 13 Dec 2016 22:31:38 -0500 Subject: [PATCH 199/740] Implement extensions() of MacLaneLimitValuation when we just change to the field of fractions of the base ring. --- limit_valuation.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/limit_valuation.py b/limit_valuation.py index 67c8ac4b2fe..f4399cd3eb5 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -392,6 +392,31 @@ def __init__(self, parent, approximation, G): self._G = G + def extensions(self, ring): + r""" + Return the extensions of this valuation to ``ring``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(GaussianIntegers(), 2) + sage: u = v._base_valuation + sage: u.extensions(QQ['x']) + [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/2 , … ]] + + """ + if self.domain() is ring: + return [self] + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + if is_PolynomialRing(ring) and self.domain().base_ring().is_subring(ring.base_ring()): + if self.domain().base_ring().fraction_field() is ring.base_ring(): + return [LimitValuation(self._initial_approximation.change_domain(ring), self._G.change_ring(ring.base_ring()))] + else: + # we need to recompute the mac lane approximants over this base + # ring because it could split differently + pass + return super(MacLaneLimitValuation, self).extensions(ring) + def lift(self, F): r""" Return a lift of ``F`` from the :meth:`residue_ring` to the From 029ceac155747b95e2e12dab38a05cee652e631b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 13 Dec 2016 22:35:22 -0500 Subject: [PATCH 200/740] Improve handling of quotient rings for p-adic valuations --- padic_valuation.py | 89 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 19 deletions(-) diff --git a/padic_valuation.py b/padic_valuation.py index fd38f17a4eb..e097206934c 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -243,7 +243,7 @@ def create_key_and_extra_args_for_number_field(self, R, prime): 2-adic valuation """ - K, L, G = self._normalize_number_field_data(R.fraction_field()) + K, L, G = self._normalize_number_field_data(R) from sage.rings.number_field.number_field_ideal import NumberFieldFractionalIdeal from valuation import DiscretePseudoValuation @@ -273,14 +273,26 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime) sage: pAdicValuation(GaussianIntegers(), pAdicValuation(ZZ, 2)) # indirect doctest 2-adic valuation + TESTS: + + We can extend to the field of fractions of a quotient ring:: + + sage: R. = ZZ[] + sage: S = R.quo(x^2 + 1) + sage: v = pAdicValuation(S, 2) + sage: R. = QQ[] + sage: S = R.quo(x^2 + 1) + sage: v = pAdicValuation(S, v) + """ - K, L, G = self._normalize_number_field_data(R.fraction_field()) + K, L, G = self._normalize_number_field_data(R) if v.domain().is_subring(G.parent()): - # v is defined on a subring of K[x] - # We try to lift v to a pseudo-valuation on K[x] - # First, we lift valuations defined on subrings of K to valuations on K[x] - if v.domain().fraction_field() is not G.parent().fraction_field(): + # v is defined on a subring of K[x]. + # We try to lift v to a pseudo-valuation on K[x]. + if _fraction_field(v.domain()) is not _fraction_field(G.parent()): + # First, we lift valuations defined on subrings of K to + # valuations on K[x]. if v.domain().is_subring(K): if v.domain() is not K: v = pAdicValuation(K, v) @@ -290,15 +302,19 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime) # Then, we lift valuations defined on polynmial rings which are # subrings of K[x] to K[x] v = v.extension(G.parent()) + elif _fraction_field(v.domain()) == L: + # v is defined on a ring whose field of fractions is L + v = v._base_valuation._initial_approximation.change_domain(G.parent()) else: raise NotImplementedError("can not rewrite %r which is defined on %r as a pseudo-valuation on %r"%(v, v.domain(), G.parent())) + assert(v.domain() is G.parent()) # To obtain uniqueness of p-adic valuations, we need a canonical # description of v. We consider all extensions of vK to L and select # the one approximated by v. - vK = v.restriction(v.domain().base_ring()) + vK = v.restriction(v.domain().base_ring()).extension(K) approximants = vK.mac_lane_approximants(G) approximant = vK.mac_lane_approximant(G, v, approximants=tuple(approximants)) @@ -322,7 +338,7 @@ def create_key_and_extra_args_for_number_field_from_ideal(self, R, I, prime): 2-adic valuation """ - K, L, G = self._normalize_number_field_data(R.fraction_field()) + K, L, G = self._normalize_number_field_data(R) # To obtain uniqueness of p-adic valuations, we need a canonical # description of v. We consider all extensions of vK to L and select @@ -371,17 +387,18 @@ def _normalize_number_field_data(self, R): # there and then come back to the original ring from sage.rings.polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing from sage.rings.number_field.number_field import is_NumberField - if is_NumberField(R): + from sage.rings.fraction_field import is_FractionField + if is_NumberField(R.fraction_field()): L = R.fraction_field() G = L.relative_polynomial() K = L.base_ring() elif is_PolynomialQuotientRing(R): from sage.categories.all import NumberFields - if R.base_ring() not in NumberFields(): + if R.base_ring().fraction_field() not in NumberFields(): raise NotImplementedError("can not normalize quotients over %r"%(R.base_ring(),)) - L = R - G = R.modulus() - K = R.base_ring() + L = R.fraction_field() + K = R.base_ring().fraction_field() + G = R.modulus().change_ring(K) else: raise NotImplementedError("can not normalize %r"%(R,)) @@ -422,7 +439,7 @@ def create_object(self, version, key, **extra_args): if is_NumberField(K): G = K.relative_polynomial() elif is_PolynomialQuotientRing(R): - G = K.modulus() + G = R.modulus() else: raise NotImplementedError return parent.__make_element_class__(pAdicFromLimitValuation)(parent, v, G.change_ring(R.base_ring()), approximants) @@ -741,15 +758,20 @@ def extensions(self, ring): """ if self.domain() is ring: return [self] - if self.domain().fraction_field() is not self.domain(): - if self.domain().fraction_field().is_subring(ring): - return pAdicValuation(self.domain().fraction_field(), self).extensions(ring) + domain_fraction_field = _fraction_field(self.domain()) + if domain_fraction_field is not self.domain(): + if domain_fraction_field.is_subring(ring): + return pAdicValuation(domain_fraction_field, self).extensions(ring) if self.domain().is_subring(ring): from sage.rings.polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing if is_PolynomialQuotientRing(ring): + if is_PolynomialQuotientRing(self.domain()): + if self.domain().modulus() == ring.modulus(): + base_extensions = self._base_valuation.extensions(self._base_valuation.domain().change_ring(self._base_valuation.domain().base_ring().fraction_field())) + return [pAdicValuation(ring, base._initial_approximation) for base in base_extensions] if ring.base_ring() is self.domain(): - from sage.categories.all import Fields - if ring in Fields(): + from sage.categories.all import IntegralDomains + if ring in IntegralDomains(): from valuation_space import DiscretePseudoValuationSpace parent = DiscretePseudoValuationSpace(ring) approximants = self.mac_lane_approximants(ring.modulus().change_ring(self.domain()), assume_squarefree=True) @@ -1246,3 +1268,32 @@ def extensions(self, ring): approximant = self._base_valuation.change_domain(G.parent())._initial_approximation return [pAdicValuation(ring, approximant)] return super(pAdicFromLimitValuation, self).extensions(ring) + +def _fraction_field(ring): + r""" + Return a fraction field of ``ring``. + + EXAMPLES: + + This works around some annoyances with ``ring.fraction_field()``:: + + sage: R. = ZZ[] + sage: S = R.quo(x^2 + 1) + sage: S.fraction_field() + Fraction Field of Univariate Quotient Polynomial Ring in xbar over Integer Ring with modulus x^2 + 1 + + sage: from mac_lane.padic_valuation import _fraction_field + sage: _fraction_field(S) + Univariate Quotient Polynomial Ring in xbar over Rational Field with modulus x^2 + 1 + + """ + from sage.categories.all import Fields + if ring in Fields(): + return ring + + from sage.rings.polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing + if is_PolynomialQuotientRing(ring): + from sage.categories.all import IntegralDomains + if ring in IntegralDomains(): + return ring.base().change_ring(ring.base_ring().fraction_field()).quo(ring.modulus()) + return ring.fraction_field() From 6e8f0f9da7e6c51def59f81a257a2db7db95fa7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 13 Dec 2016 22:40:51 -0500 Subject: [PATCH 201/740] Use the proper generator for quotient rings --- padic_valuation.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/padic_valuation.py b/padic_valuation.py index e097206934c..924c4027362 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -1216,6 +1216,12 @@ def __init__(self, parent, approximant, G, approximants): FiniteExtensionFromLimitValuation.__init__(self, parent, approximant, G, approximants) pAdicValuation_base.__init__(self, parent, approximant.restriction(approximant.domain().base_ring()).p()) + from sage.rings.polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing + if is_PolynomialQuotientRing(self.domain()): + self._gen = self.domain().gen() + else: + self._gen = self.domain()(self.domain().fraction_field().gen()) + def _to_base_domain(self, f): r""" Return ``f``, an element of the domain of this valuation, as an element @@ -1246,7 +1252,7 @@ def _from_base_domain(self, f): I """ - return f(self.domain().fraction_field().gen()) + return f(self._gen) def extensions(self, ring): r""" From af126e14de20a66deee600331f7201689d69816b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 14 Dec 2016 01:22:07 -0500 Subject: [PATCH 202/740] Fix degree bound --- inductive_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index 073a01badde..7ce189cfbee 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -790,7 +790,7 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib from sage.rings.all import ZZ assert (phi.degree() / self.phi().degree()) in ZZ - degree_bound = multiplicities[slope] * self.phi().degree() + degree_bound = multiplicities[slope] * self.phi().degree() * e ret.append((w, degree_bound, w_coefficients, new_valuations)) assert ret From d6bbbbc037e621fc5db5396d5d61cfd9c460f516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 11 Jan 2017 22:21:38 -0500 Subject: [PATCH 203/740] Limit coefficient growth by heuristical simplifications --- TODO | 3 +- augmented_valuation.py | 368 +++++++++++++++++++++++++++++------- developing_valuation.py | 23 +-- function_field_valuation.py | 6 +- gauss_valuation.py | 161 ++++++++++++++-- inductive_valuation.py | 181 ++++++++++++++---- limit_valuation.py | 111 +++++++++-- mapped_valuation.py | 83 +++++++- padic_valuation.py | 105 +++++++--- valuation.py | 183 ++++++++++++------ valuation_space.py | 92 ++++++++- 11 files changed, 1066 insertions(+), 250 deletions(-) diff --git a/TODO b/TODO index 0fb6319d690..89a149bc134 100644 --- a/TODO +++ b/TODO @@ -1,2 +1 @@ -* Check every methods about limitations in the ring case (and correction of the docstring) -* Remove shift(). It is not very useful and not consistent with p-adic shifts. +* Use _coerce_map_via more often diff --git a/augmented_valuation.py b/augmented_valuation.py index 21c3b1dd156..9d530a6227b 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -166,7 +166,7 @@ from valuation import InfiniteDiscretePseudoValuation, DiscreteValuation from sage.misc.cachefunc import cached_method -from sage.rings.all import infinity +from sage.rings.all import infinity, QQ, ZZ from sage.structure.factory import UniqueFactory class AugmentedValuationFactory(UniqueFactory): @@ -226,7 +226,6 @@ def create_key(self, base_valuation, phi, mu, check=True): raise TypeError("base_valuation must be inductive") phi = base_valuation.domain().coerce(phi) - from sage.rings.all import QQ, infinity if mu is not infinity: mu = QQ(mu) @@ -369,7 +368,11 @@ def equivalence_unit(self, s, reciprocal=False): """ if reciprocal: - ret = self.equivalence_reciprocal(self.equivalence_unit(-s)) + ret = self._base_valuation.element_with_valuation(s) + residue = self.reduce(ret*self._base_valuation.element_with_valuation(-s), check=False) + assert residue.is_constant() + ret *= self.lift(~(residue[0])) + #ret = self.equivalence_reciprocal(self.equivalence_unit(-s)) else: ret = self._base_valuation.element_with_valuation(s) @@ -498,10 +501,11 @@ def psi(self): """ R = self._base_valuation.equivalence_unit(-self._base_valuation(self._phi)) - F = self._base_valuation.reduce(self._phi*R).monic() + F = self._base_valuation.reduce(self._phi*R, check=False).monic() assert F.is_irreducible() return F + @cached_method def E(self): """ Return the ramification index of this valuation over its underlying @@ -527,6 +531,7 @@ def E(self): raise NotImplementedError("ramification index is not defined over a trivial Gauss valuation") return self.value_group().index(self._base_valuation.value_group()) * self._base_valuation.E() + @cached_method def F(self): """ Return the degree of the residue field extension of this valuation @@ -548,7 +553,7 @@ def F(self): 1 """ - return self.psi().degree() * self._base_valuation.F() + return self.phi().degree() // self._base_valuation.E() def extensions(self, ring): r""" @@ -743,7 +748,6 @@ def scale(self, scalar): [ Gauss valuation induced by 3 * 2-adic valuation, v(x^2 + x + 1) = 3 ] """ - from sage.rings.all import QQ if scalar in QQ and scalar > 0 and scalar != 1: return self._base_valuation.scale(scalar).augmentation(self.phi(), scalar*self._mu) return super(AugmentedValuation_base, self).scale(scalar) @@ -779,7 +783,35 @@ def _residue_ring_generator_name(self): # use this name, base can not handle strings, so hopefully, # there are no variable names (such as in QQ or GF(p)) return generator - + + def _relative_size(self, f): + r""" + Return an estimate on the coefficient size of ``f``. + + The number returned is an estimate on the factor between the number of + bits used by ``f`` and the minimal number of bits used by an element + congruent to ``f``. + + This is used by :meth:`simplify` to decide whether simplification of + coefficients is going to lead to a significant shrinking of the + coefficients of ``f``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: K. = QQ.extension(u^2 + u+ 1) + sage: S. = K[] + sage: v = GaussValuation(S, pAdicValuation(K, 2)) + sage: w = v.augmentation(x^2 + x + u, 1/2) + sage: w._relative_size(x^2 + x + 1) + 1 + sage: w._relative_size(1048576*x^2 + 1048576*x + 1048576) + 21 + + """ + return self._base_valuation._relative_size(f) + class FinalAugmentedValuation(AugmentedValuation_base, FinalInductiveValuation): r""" @@ -873,10 +905,20 @@ def residue_ring(self): # extension() implementation.) return base - def reduce(self, f): + def reduce(self, f, check=True, degree_bound=None): r""" Reduce ``f`` module this valuation. + INPUT: + + - ``f`` -- an element in the domain of this valuation + + - ``check`` -- whether or not to check whether ``f`` has non-negative + valuation (default: ``True``) + + - ``degree_bound`` -- an a-priori known bound on the degree of the + result which can speed up the computation (default: not set) + OUTPUT: an element of the :meth:`residue_ring` of this valuation, the reduction @@ -939,11 +981,12 @@ def reduce(self, f): """ f = self.domain().coerce(f) - v = self(f) - if v < 0: - raise ValueError("f must have non-negative valuation") - elif v > 0: - return self.residue_ring().zero() + if check: + v = self(f) + if v < 0: + raise ValueError("f must have non-negative valuation") + elif v > 0: + return self.residue_ring().zero() constant_term = self.coefficients(f).next() constant_term_reduced = self._base_valuation.reduce(constant_term) @@ -1120,10 +1163,20 @@ def residue_ring(self): pass return base[self.domain().variable_name()] - def reduce(self, f): + def reduce(self, f, check=True, degree_bound=None): r""" Reduce ``f`` module this valuation. + INPUT: + + - ``f`` -- an element in the domain of this valuation + + - ``check`` -- whether or not to check whether ``f`` has non-negative + valuation (default: ``True``) + + - ``degree_bound`` -- an a-priori known bound on the degree of the + result which can speed up the computation (default: not set) + OUTPUT: an element of the :meth:`residue_ring` of this valuation, the reduction @@ -1186,27 +1239,43 @@ def reduce(self, f): """ f = self.domain().coerce(f) - coefficients = list(self.coefficients(f)) - CV = zip(coefficients, self.valuations(f, coefficients=coefficients)) - if all([v > 0 for (c,v) in CV]): + if self.lower_bound(f) > 0: return self.residue_ring().zero() - # rewrite as sum of f_i phi^{i tau}, i.e., drop the coefficients that - # can have no influence on the reduction + tau = self.value_group().index(self._base_valuation.value_group()) - for i,(c,v) in enumerate(CV): - if v < 0: - raise ValueError("f must have non-negative valuation") - assert v != 0 or i % tau == 0 - assert not any([v==0 for i,(c,v) in enumerate(CV) if i % tau != 0]) - CV = CV[::tau] + coefficients = self.coefficients(f) + if degree_bound is not None: + from itertools import islice + coefficients = islice(coefficients, 0, tau*degree_bound + 1, 1) + coefficients = list(coefficients) + + valuations = [] + + # rewrite as sum of f_i phi^{i tau}, i.e., drop the coefficients that + # can have no influence on the reduction + for i,c in enumerate(coefficients): + if i % tau != 0: + if check: + v = self._base_valuation(c) + i*self._mu + assert v != 0 # this can not happen for an augmented valuation + if v < 0: + raise ValueError("f must not have negative valuation") + else: + # the validity of the coefficients with i % tau == 0 is checked by + # the recursive call to reduce below + # replace f_i by f_i Q^{i tau} + v = self._base_valuation.lower_bound(c) + valuations.append(v) + if v is infinity or v > i*self._mu: + coefficients[i] = self.domain().zero() + else: + coefficients[i] = c * self._Q(i//tau) - # replace f_i by f_i Q^{i tau} - vQ = self._mu * tau - CV = [(c*self._Q()**i, v - vQ*i) for i,(c,v) in enumerate(CV)] + coefficients = coefficients[::tau] # recursively reduce the f_i Q^{i tau} - C = [self._base_valuation.reduce(c)(self._residue_field_generator()) for c,v in CV] + C = [self._base_valuation.reduce(c, check=False)(self._residue_field_generator()) if valuations[i] <= i*self._mu else self._base_valuation.residue_ring().zero() for i,c in enumerate(coefficients)] # reduce the Q'^i phi^i return self.residue_ring()(C) @@ -1325,7 +1394,7 @@ def lift(self, F): # now the coefficients correspond to the expansion with (f_iQ^i)(Q^{-1} phi)^i # now we undo the factors of Q^i (the if else is necessary to handle the case when mu is infinity, i.e., when _Q_reciprocal() is undefined) - coeffs = [ (c if i == 0 else c*self._Q_reciprocal()**i).map_coefficients(_lift_to_maximal_precision) for i,c in enumerate(coeffs) ] + coeffs = [ (c if i == 0 else c*self._Q_reciprocal(i)).map_coefficients(_lift_to_maximal_precision) for i,c in enumerate(coeffs) ] RR = self.domain().change_ring(self.domain()) @@ -1334,7 +1403,7 @@ def lift(self, F): ret = ret.map_coefficients(lambda c:_lift_to_maximal_precision(c)) return ret - def lift_to_key(self, F): + def lift_to_key(self, F, check=True): """ Lift the irreducible polynomial ``F`` to a key polynomial. @@ -1343,6 +1412,9 @@ def lift_to_key(self, F): - ``F`` -- an irreducible non-constant polynomial in the :meth:`residue_ring` of this valuation + - ``check`` -- whether or not to check correctness of ``F`` (default: + ``True``) + OUTPUT: A polynomial `f` in the domain of this valuation which is a key @@ -1365,7 +1437,7 @@ def lift_to_key(self, F): sage: w = v.augmentation(x^2 + x + u, 1/2) sage: y = w.residue_ring().gen() sage: f = w.lift_to_key(y + 1); f - (1 + O(2^10))*x^4 + (2 + O(2^11))*x^3 + (1 + u*2 + O(2^10))*x^2 + (u*2 + O(2^11))*x + (u + 1) + u*2 + u*2^2 + u*2^3 + u*2^4 + u*2^5 + u*2^6 + u*2^7 + u*2^8 + u*2^9 + O(2^10) + (1 + O(2^10))*x^4 + (2 + O(2^11))*x^3 + (1 + u*2 + O(2^10))*x^2 + (u*2 + O(2^11))*x + (u + 1) + u*2 + O(2^10) sage: w.is_key(f) True @@ -1390,22 +1462,23 @@ def lift_to_key(self, F): if not self.domain().base_ring() in Fields(): raise NotImplementedError("only implemented for polynomial rings over fields") - if self._base_valuation.is_gauss_valuation() and self._mu is infinity: - raise TypeError("there are no keys over this valuation") + if check: + if self._base_valuation.is_gauss_valuation() and self._mu is infinity: + raise TypeError("there are no keys over this valuation") + if F.is_constant(): + raise ValueError("F must not be constant") + if not F.is_monic(): + raise ValueError("F must be monic") + if not F.is_irreducible(): + raise ValueError("F must be irreducible") - if F.is_constant(): - raise ValueError("F must not be constant") - if not F.is_monic(): - raise ValueError("F must be monic") - if not F.is_irreducible(): - raise ValueError("F must be irreducible") if F == F.parent().gen(): return self.phi() f = self.lift(F) assert self.reduce(f) == F - f *= self._Q()**F.degree() + f *= self._Q(F.degree()) coefficients = list(self.coefficients(f)) valuations = list(self.valuations(f, coefficients=coefficients)) CV = zip(coefficients, valuations) @@ -1416,16 +1489,16 @@ def lift_to_key(self, F): CV[-1] = (CV[-1][0].parent().one(), vf) ret = self.domain().change_ring(self.domain())([c for c,v in CV])(self.phi()) - ret = self.simplify(ret) + ret = self.simplify(ret, error=vf, force=True) ret = ret.map_coefficients(lambda c:_lift_to_maximal_precision(c)) assert (ret == self.phi()) == (F == F.parent().gen()) assert self.is_key(ret) return ret @cached_method - def _Q(self): + def _Q(self, e): r""" - Return the polynomial `Q` used in the construction to :meth:`reduce` an + Return the polynomial `Q^e` used in the construction to :meth:`reduce` an element to the :meth:`residue_ring`. EXAMPLES:: @@ -1435,17 +1508,19 @@ def _Q(self): sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) - sage: w._Q() + sage: w._Q(1) 2 """ tau = self.value_group().index(self._base_valuation.value_group()) - return self.equivalence_unit(self._mu * tau) + v = self._mu * tau + return self._pow(self.equivalence_unit(v), e, error=v*e) @cached_method - def _Q_reciprocal(self): + def _Q_reciprocal(self, e=1): r""" - Return the :meth:`equivalence_reciprocal` of :meth:`_Q`. + Return the :meth:`equivalence_reciprocal` of the ``e``-th power of + :meth:`_Q`. EXAMPLES:: @@ -1458,12 +1533,17 @@ def _Q_reciprocal(self): 1/2 """ - ret = self.equivalence_reciprocal(self._Q()) + if e == 1: + return self.equivalence_reciprocal(self._Q(1), check=False) + + tau = self.value_group().index(self._base_valuation.value_group()) + v = -self._mu * tau + ret = self._pow(self._Q_reciprocal(1), e, error=v*e) assert self.is_equivalence_unit(ret) # esentially this checks that the reduction of Q'*phi^tau is the # generator of the residue field - assert self._base_valuation.reduce(self._Q()*ret)(self._residue_field_generator()).is_one() + assert self._base_valuation.reduce(self._Q(e)*ret)(self._residue_field_generator()).is_one() return ret @@ -1544,7 +1624,7 @@ def value_semigroup(self): """ return self._base_valuation.value_semigroup() + self._mu - def valuations(self, f, coefficients=None): + def valuations(self, f, coefficients=None, call_error=False): """ Return the valuations of the `f_i\phi^i` in the expansion `f=\sum_i f_i\phi^i`. @@ -1558,6 +1638,10 @@ def valuations(self, f, coefficients=None): used to speed up the computation when the expansion of ``f`` is already known from a previous computation. + - ``call_error` -- whether or not to speed up the computation by + assuming that the result is only used to compute the valuation of + ``f`` (default: ``False``) + OUTPUT: An iterator over rational numbers (or infinity) `[v(f_0), v(f_1\phi), \dots]` @@ -1580,14 +1664,26 @@ def valuations(self, f, coefficients=None): """ f = self.domain().coerce(f) + if call_error: + lowest_valuation = infinity for i,c in enumerate(coefficients or self.coefficients(f)): + if call_error: + if lowest_valuation is not infinity: + v = self._base_valuation.lower_bound(c) + if v is infinity or v >= lowest_valuation: + yield infinity + continue v = self._base_valuation(c) if v is infinity: yield v else: - yield v + i*self._mu + ret = v + i*self._mu + if call_error: + if lowest_valuation is infinity or ret < lowest_valuation: + lowest_valuation = ret + yield ret - def simplify(self, f, error=None): + def simplify(self, f, error=None, force=False, size_heuristic_bound=32): r""" Return a simplified version of ``f``. @@ -1595,6 +1691,21 @@ def simplify(self, f, error=None): strictly greater than the valuation of ``f`` (or strictly greater than ``error`` if set.) + INPUT: + + - ``f`` -- an element in the domain of this valuation + + - ``error`` -- a rational, infinity, or ``None`` (default: ``None``), + the error allowed to introduce through the simplification + + - ``force`` -- whether or not to simplify ``f`` even if there is + heuristically no change in the coefficient size of ``f`` expected + (default: ``False``) + + - ``size_heuristic_bound` -- when ``force`` is not set, the expected + factor by which the coefficients need to shrink to perform an actual + simplification (default: 32) + EXAMPLES:: sage: from mac_lane import * # optional: standalone @@ -1602,20 +1713,92 @@ def simplify(self, f, error=None): sage: S. = R[] sage: v = GaussValuation(S) sage: w = v.augmentation(x^2 + x + u, 1/2) - sage: w.simplify(x^10/2 + 1) + sage: w.simplify(x^10/2 + 1, force=True) # not tested - error is incorrect (u + 1)*2^-1 + O(2^4) """ f = self.domain().coerce(f) - coefficients = list(self.coefficients(f)) + if not force and self._relative_size(f) < size_heuristic_bound: + return f + if error is None: - error = min(self.valuations(f, coefficients=coefficients)) + error = self.upper_bound(f) + + return self._base_valuation.simplify(f, error=error, force=force) + + def lower_bound(self, f): + r""" + Return a lower bound of this valuation at ``f``. + + Use this method to get an approximation of the valuation of ``f`` + when speed is more important than accuracy. + + ALGORITHM: + + The main cost of evaluation is the computation of the + :meth:`coefficients` of the :meth:`phi`-adic expansion of ``f`` (which + often leads to coefficient bloat.) So unless :meth:`phi` is trivial, we + fall back to valuation which this valuation augments since it is + guaranteed to be smaller everywhere. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4, 5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, 1/2) + sage: w.lower_bound(x^2 + x + u) + 0 + + """ + f = self.domain().coerce(f) + + if self.phi() == self.domain().gen(): + constant_valuation = self.restriction(f.base_ring()) + ret = infinity + for i,c in enumerate(f.coefficients(sparse=False)): + v = constant_valuation.lower_bound(c) + if v is infinity: + continue + v += i*self._mu + if ret is infinity or v < ret: + ret = v + return ret + else: + return self._base_valuation.lower_bound(f) + + def upper_bound(self, f): + r""" + Return an upper bound of this valuation at ``f``. + + Use this method to get an approximation of the valuation of ``f`` + when speed is more important than accuracy. + + ALGORITHM: + + Any entry of :meth:`valuations` serves as an upper bound. However, + computation of the :meth:`phi`-adic expansion of ``f`` is quite costly. + Therefore, we produce an upper bound on the last entry of + :meth:`valuations`, namely the valuation of the leading coefficient of + ``f`` plus the valuation of the appropriate power of :meth:`phi`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4, 5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, 1/2) + sage: w.upper_bound(x^2 + x + u) + 1/2 + + """ + f = self.domain().coerce(f) - from sage.rings.all import PolynomialRing - R = PolynomialRing(f.parent(), 'phi') - f = R([self._base_valuation.simplify(c, error=error - i*self._mu) for i,c in enumerate(coefficients)]) - return f(self.phi()) + len_coefficients_bound = (QQ(f.degree()) / self.phi().degree()).ceil() + return self.restriction(f.base_ring())(f.leading_coefficient()) + len_coefficients_bound * self._mu class FinalFiniteAugmentedValuation(FiniteAugmentedValuation, FinalAugmentedValuation): @@ -1742,7 +1925,7 @@ def value_semigroup(self): """ return self._base_valuation.value_semigroup() - def valuations(self, f, coefficients=None): + def valuations(self, f, coefficients=None, call_error=False): """ Return the valuations of the `f_i\phi^i` in the expansion `f=\sum_i f_i\phi^i`. @@ -1756,6 +1939,10 @@ def valuations(self, f, coefficients=None): used to speed up the computation when the expansion of ``f`` is already known from a previous computation. + - ``call_error` -- whether or not to speed up the computation by + assuming that the result is only used to compute the valuation of + ``f`` (default: ``False``) + OUTPUT: An iterator over rational numbers (or infinity) `[v(f_0), v(f_1\phi), \dots]` @@ -1782,7 +1969,7 @@ def valuations(self, f, coefficients=None): for i in range(num_infty_coefficients): yield infinity - def simplify(self, f, error=None): + def simplify(self, f, error=None, force=False): r""" Return a simplified version of ``f``. @@ -1790,6 +1977,17 @@ def simplify(self, f, error=None): strictly greater than the valuation of ``f`` (or strictly greater than ``error`` if set.) + INPUT: + + - ``f`` -- an element in the domain of this valuation + + - ``error`` -- a rational, infinity, or ``None`` (default: ``None``), + the error allowed to introduce through the simplification + + - ``force`` -- whether or not to simplify ``f`` even if there is + heuristically no change in the coefficient size of ``f`` expected + (default: ``False``) + EXAMPLES:: sage: from mac_lane import * # optional: standalone @@ -1797,16 +1995,56 @@ def simplify(self, f, error=None): sage: S. = R[] sage: v = GaussValuation(S) sage: w = v.augmentation(x^2 + x + u, infinity) - sage: w.simplify(x^10/2 + 1) + sage: w.simplify(x^10/2 + 1, force=True) # not tested - error incorrect (u + 1)*2^-1 + O(2^4) """ f = self.domain().coerce(f) if error is None: - error = self(f) + error = self.upper_bound(f) if error is infinity: return f - return self.domain()(self._base_valuation.simplify(self.coefficients(f).next(), error)) + return self.domain()(self._base_valuation.simplify(self.coefficients(f).next(), error, force=force)) + + def lower_bound(self, f): + r""" + Return a lower bound of this valuation at ``f``. + + Use this method to get an approximation of the valuation of ``f`` + when speed is more important than accuracy. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4, 5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, infinity) + sage: w.lower_bound(x^2 + x + u) + +Infinity + + """ + return self._base_valuation.lower_bound(self.coefficients(f).next()) + + def upper_bound(self, f): + r""" + Return an upper bound of this valuation at ``f``. + + Use this method to get an approximation of the valuation of ``f`` + when speed is more important than accuracy. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4, 5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: w = v.augmentation(x^2 + x + u, infinity) + sage: w.upper_bound(x^2 + x + u) + +Infinity + + """ + return self._base_valuation.upper_bound(self.coefficients(f).next()) diff --git a/developing_valuation.py b/developing_valuation.py index 468b003de92..8964c98ae49 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -177,27 +177,6 @@ def _quo_rem(self, f): (x, 1) """ - qr = [ self._quo_rem_monomial(i) for i in range(f.degree()+1) ] - q = [ f[i]*g for i,(g,_) in enumerate(qr) ] - r = [ f[i]*h for i,(_,h) in enumerate(qr) ] - return sum(q), sum(r) - - @cached_method - def _quo_rem_monomial(self, degree): - r""" - Return the quotient and remainder of `x^\mathrm{degree}` divided by the - key polynomial :meth:`phy`. - - EXAMPLES:: - - sage: from mac_lane import * # optional: standalone - sage: S. = QQ[] - sage: v = GaussValuation(S, pAdicValuation(QQ, 2)) - sage: v._quo_rem_monomial(10) - (x^9, 0) - - """ - f = self.domain().one() << degree return f.quo_rem(self.phi()) def newton_polygon(self, f, valuations=None): @@ -266,7 +245,7 @@ def _call_(self, f): return infinity ret = infinity - for v in self.valuations(f): + for v in self.valuations(f, call_error=True): if ret is infinity or (v is not infinity and v < ret): # "ret is infinity" is redundant but much faster than < when ret is infinite ret = v diff --git a/function_field_valuation.py b/function_field_valuation.py index d05a0932f3c..a5ac7993872 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -543,9 +543,9 @@ def extensions(self, L): sage: v = FunctionFieldValuation(K, 1/x) sage: R. = K[] sage: L. = K.extension(y^2 - 1/(x^2 + 1)) - sage: v.extensions(L) # long time - [[ Valuation at the infinite place, v(y - 1/x) = 3 ]-adic valuation, - [ Valuation at the infinite place, v(y + 1/x) = 3 ]-adic valuation] + sage: sorted(v.extensions(L), key=str) # long time + [[ Valuation at the infinite place, v(y + 1/x) = 3 ]-adic valuation, + [ Valuation at the infinite place, v(y - 1/x) = 3 ]-adic valuation] Iterated extensions over the infinite place:: diff --git a/gauss_valuation.py b/gauss_valuation.py index 00bf1173f4a..266308d911b 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -242,7 +242,7 @@ def uniformizer(self): """ return self.domain()(self._base_valuation.uniformizer()) - def valuations(self, f, coefficients=None): + def valuations(self, f, coefficients=None, call_error=False): """ Return the valuations of the `f_i\phi^i` in the expansion `f=\sum f_i\phi^i`. @@ -255,6 +255,10 @@ def valuations(self, f, coefficients=None): used to speed up the computation when the expansion of ``f`` is already known from a previous computation. + - ``call_error` -- whether or not to speed up the computation by + assuming that the result is only used to compute the valuation of + ``f`` (default: ``False``) + OUTPUT: A list, each entry a rational numbers or infinity, the valuations of `f_0, f_1\phi, \dots` @@ -272,8 +276,30 @@ def valuations(self, f, coefficients=None): """ f = self.domain().coerce(f) - for c in coefficients or self.coefficients(f): - yield self._base_valuation(c) + if f.is_constant(): + yield self._base_valuation(f[0]) + return + + from sage.rings.all import infinity, QQ + if f == self.domain().gen(): + yield infinity + yield QQ(0) + return + + if call_error: + lowest_valuation = infinity + for c in coefficients or f.coefficients(sparse=False): + if call_error: + if lowest_valuation is not infinity: + v = self._base_valuation.lower_bound(c) + if v is infinity or v >= lowest_valuation: + yield infinity + continue + ret = self._base_valuation(c) + if call_error: + if ret is not infinity and (lowest_valuation is infinity or ret < lowest_valuation): + lowest_valuation = ret + yield ret @cached_method def residue_ring(self): @@ -292,7 +318,7 @@ def residue_ring(self): """ return self.domain().change_ring(self._base_valuation.residue_ring()) - def reduce(self, f): + def reduce(self, f, check=True, degree_bound=None): """ Return the reduction of ``f`` modulo this valuation. @@ -300,6 +326,12 @@ def reduce(self, f): - ``f`` -- an integral element of the domain of this valuation + - ``check`` -- whether or not to check whether ``f`` has non-negative + valuation (default: ``True``) + + - ``degree_bound`` -- an a-priori known bound on the degree of the + result which can speed up the computation (default: not set) + OUTPUT: A polynomial in the :meth:`residue_ring` of this valuation. @@ -330,10 +362,13 @@ def reduce(self, f): """ f = self.domain().coerce(f) + if degree_bound is not None: + f = f.truncate(degree_bound + 1) + try: return f.map_coefficients(self._base_valuation.reduce, self._base_valuation.residue_field()) except: - if not all([v>=0 for v in self.valuations(f)]): + if check and not all([v>=0 for v in self.valuations(f)]): raise ValueError("reduction not defined for non-integral elements and %r is not integral over %r"%(f, self)) raise @@ -686,7 +721,37 @@ def scale(self, scalar): return GaussValuation(self.domain(), self._base_valuation.scale(scalar)) return super(GaussValuation_generic, self).scale(scalar) - def simplify(self, f, error=None): + def _relative_size(self, f): + r""" + Return an estimate on the coefficient size of ``f``. + + The number returned is an estimate on the factor between the number of + Bits used by ``f`` and the minimal number of bits used by an element + Congruent to ``f``. + + This is used by :meth:`simplify` to decide whether simplification of + Coefficients is going to lead to a significant shrinking of the + Coefficients of ``f``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v._relative_size(x + 1024) + 11 + + For performance reasons, only the constant coefficient is considered. + (In common appplications, the constant coefficient shows the most + critical coefficient growth):: + + sage: v._relative_size(1024*x + 1) + 1 + + """ + return self._base_valuation._relative_size(f[0]) + + def simplify(self, f, error=None, force=False, size_heuristic_bound=32): r""" Return a simplified version of ``f``. @@ -694,20 +759,94 @@ def simplify(self, f, error=None): strictly greater than the valuation of ``f`` (or strictly greater than ``error`` if set.) + INPUT: + + - ``f`` -- an element in the domain of this valuation + + - ``error`` -- a rational, infinity, or ``None`` (default: ``None``), + the error allowed to introduce through the simplification + + - ``force`` -- whether or not to simplify ``f`` even if there is + heuristically no change in the coefficient size of ``f`` expected + (default: ``False``) + + - ``size_heuristic_bound` -- when ``force`` is not set, the expected + factor by which the coefficients need to shrink to perform an actual + simplification (default: 32) + EXAMPLES:: sage: from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) - sage: w = v.augmentation(x^2 + x + u, infinity) - sage: w.simplify(x^10/2 + 1) - (u + 1)*2^-1 + O(2^4) + sage: f = x^10/2 + 1 + sage: v.simplify(f) + (2^-1 + O(2^4))*x^10 + 1 + O(2^5) """ f = self.domain().coerce(f) + if not force and self._relative_size(f) < size_heuristic_bound: + return f + if error is None: - error = self(f) + error = self.upper_bound(f) - return f.map_coefficients(lambda c: self._base_valuation.simplify(c, error)) + return f.map_coefficients(lambda c: self._base_valuation.simplify(c, error=error, force=force)) + + def lower_bound(self, f): + r""" + Return an lower bound of this valuation at ``f``. + + Use this method to get an approximation of the valuation of ``f`` + when speed is more important than accuracy. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4, 5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.lower_bound(1024*x + 2) + 1 + sage: v(1024*x + 2) + 1 + + """ + from sage.rings.all import infinity, QQ + coefficients = f.coefficients(sparse=True) + coefficients.reverse() + ret = infinity + for c in coefficients: + v = self._base_valuation.lower_bound(c) + if c is not infinity and (ret is infinity or v < ret): + ret = v + return ret + + def upper_bound(self, f): + r""" + Return an upper bound of this valuation at ``f``. + + Use this method to get an approximation of the valuation of ``f`` + when speed is more important than accuracy. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = Qq(4, 5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v.upper_bound(1024*x + 1) + 10 + sage: v(1024*x + 1) + 0 + + """ + f = self.domain().coerce(f) + coefficients = f.coefficients(sparse=True) + if not coefficients: + from sage.rings.all import infinity + return infinity + else: + return self._base_valuation.upper_bound(coefficients[-1]) diff --git a/inductive_valuation.py b/inductive_valuation.py index 7ce189cfbee..617435fda21 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -85,7 +85,7 @@ def is_equivalence_unit(self, f, valuations=None): return False return self.effective_degree(f, valuations=valuations) == 0 - def equivalence_reciprocal(self, f): + def equivalence_reciprocal(self, f, coefficients=None, valuations=None, check=True): r""" Return an equivalence reciprocal of ``f``. @@ -97,6 +97,15 @@ def equivalence_reciprocal(self, f): - ``f`` -- a polynomial in the domain of this valuation which is an :meth:`equivalence_unit` + - ``coefficients`` -- the coefficients of ``f`` in the :meth:`phi`-adic + expansion if known (default: ``None``) + + - ``valuations`` -- the valuations of ``coefficients`` if known + (default: ``None``) + + - ``check`` -- whether or not to check the validity of ``f`` (default: + ``True``) + EXAMPLES:: sage: from mac_lane import * # optional: standalone @@ -117,7 +126,7 @@ def equivalence_reciprocal(self, f): sage: v = v.augmentation(x^2 + x + u, 1) sage: f = 2*x + u sage: h = v.equivalence_reciprocal(f); h - (u + 1) + (u + 1)*2 + 2^2 + u*2^3 + 2^4 + O(2^5) + (u + 1) + O(2^5) sage: v.is_equivalent(f*h, 1) True @@ -125,7 +134,7 @@ def equivalence_reciprocal(self, f): sage: v = v.augmentation((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) sage: h = v.equivalence_reciprocal(f); h - (u + 1) + (u + 1)*2 + (u + 1)*2^2 + (u + 1)*2^3 + u*2^4 + O(2^5) + (u + 1) + O(2^5) sage: v.is_equivalent(f*h, 1) True @@ -157,12 +166,34 @@ def equivalence_reciprocal(self, f): # the xgcd does in general not work, i.e., return 1, unless over a field raise NotImplementedError("only implemented for polynomial rings over fields") - if not self.is_equivalence_unit(f): - raise ValueError("f must be an equivalence unit but %r is not"%(f,)) + if check: + if coefficients is None: + coefficients = list(self.coefficients(f)) + if valuations is None: + valuations = list(self.valuations(f, coefficients=coefficients)) + if not self.is_equivalence_unit(f, valuations=valuations): + raise ValueError("f must be an equivalence unit but %r is not"%(f,)) - e0 = self.coefficients(f).next() - one,g,h = self.phi().xgcd(e0) - assert one.is_one() + if coefficients is None: + e0 = self.coefficients(f).next() + else: + e0 = coefficients[0] + + # f is an equivalence unit, its valuation is given by the constant coefficient + if valuations is None: + vf = self(e0) + else: + vf = valuations[0] + + e0 = self.simplify(e0, error=vf) + s_ = self.equivalence_unit(-vf) + residue = self.reduce(e0 * s_) + if not isinstance(self, FinalInductiveValuation): + assert residue.is_constant() + residue = residue[0] + h = self.lift(~residue) * s_ + + h = self.simplify(h, -vf) # it might be the case that f*h has non-zero valuation because h has # insufficient precision, so we must not assert that here but only @@ -175,7 +206,6 @@ def equivalence_reciprocal(self, f): # - we can add anything which times e0 has positive valuation, e.g., we # may drop coefficients of positive valuation h = h.map_coefficients(lambda c:_lift_to_maximal_precision(c)) - h = h.parent()([ c if self(e0*c) <= 0 else c.parent().zero() for c in h.coefficients(sparse=False)]) return h @@ -651,7 +681,7 @@ def augmentation(self, phi, mu, check=True): from augmented_valuation import AugmentedValuation return AugmentedValuation(self, phi, mu, check) - def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducible=False, report_degree_bounds_and_caches=False, coefficients=None, valuations=None): + def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, assume_equivalence_irreducible=False, report_degree_bounds_and_caches=False, coefficients=None, valuations=None, check=True): r""" Perform an approximation step towards the squarefree monic non-constant integral polynomial ``G`` which is not an :meth:`equivalence_unit`. @@ -659,6 +689,33 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib This performs the individual steps that are used in :meth:`mac_lane_approximants`. + INPUT: + + - ``G`` -- a sqaurefree monic non-constant integral polynomial ``G`` + which is not an :meth:`equivalence_unit` + + - ``principal_part_bound`` -- an integer or ``None`` (default: + ``None``), a bound on the length of the principal part, i.e., the + section of negative slope, of the Newton polygon of ``G`` + + - ``assume_squarefree`` -- whether or not to assume that ``G`` is + squarefree (default: ``False``) + + - ``assume_equivalence_irreducible`` -- whether or not to assume that + ``G`` is equivalence irreducible (default: ``False``) + + - ``report_degree_bounds_and_caches`` -- whether or not to include internal state with the returned value (used by :meth:`mac_lane_approximants` to speed up sequential calls) + + - ``coefficients`` -- the coefficients of ``G`` in the + :meth:`phi`-adic expansion if known (default: ``None``) + + - ``valauations`` -- the valuations of ``coefficients`` if known + (default: ``None``) + + - ``check`` -- whether to check that ``G`` is a squarefree monic + non-constant integral polynomial and not an :meth:`equivalence_unit` + (default: ``True``) + TESTS:: sage: from mac_lane import * # optional: standalone @@ -680,6 +737,7 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib if G.is_constant(): raise ValueError("G must not be constant") + from itertools import islice from sage.misc.misc import verbose verbose("Augmenting %s towards %s"%(self, G), level=10) @@ -687,17 +745,23 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib raise ValueError("G must be monic") if coefficients is None: - coefficients = list(self.coefficients(G)) + coefficients = self.coefficients(G) + if principal_part_bound: + coefficients = islice(coefficients, 0, principal_part_bound + 1, 1) + coefficients = list(coefficients) if valuations is None: - valuations = list(self.valuations(G, coefficients=coefficients)) + valuations = self.valuations(G, coefficients=coefficients) + if principal_part_bound: + valuations = islice(valuations, 0, principal_part_bound + 1, 1) + valuations = list(valuations) - if min(valuations) < 0: + if check and min(valuations) < 0: raise ValueError("G must be integral") - if self.is_equivalence_unit(G, valuations=valuations): + if check and self.is_equivalence_unit(G, valuations=valuations): raise ValueError("G must not be an equivalence-unit") - if not assume_squarefree and not G.is_squarefree(): + if check and not assume_squarefree and not G.is_squarefree(): raise ValueError("G must be squarefree") from sage.rings.all import infinity @@ -705,11 +769,11 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib ret = [] - F = self.equivalence_decomposition(G, assume_not_equivalence_unit=True, coefficients=coefficients, valuations=valuations) + F = self.equivalence_decomposition(G, assume_not_equivalence_unit=True, coefficients=coefficients, valuations=valuations, compute_unit=False, degree_bound=principal_part_bound) assert len(F), "%s equivalence-decomposes as an equivalence-unit %s"%(G, F) if len(F) == 1 and F[0][1] == 1 and F[0][0].degree() == G.degree(): assert self.is_key(G, assume_equivalence_irreducible=assume_equivalence_irreducible) - ret.append((self.augmentation(G, infinity, check=False), G.degree(), None, None)) + ret.append((self.augmentation(G, infinity, check=False), G.degree(), principal_part_bound, None, None)) else: for phi,e in F: if G == phi: @@ -722,7 +786,7 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib prec = min([c.precision_absolute() for c in phi.list()]) g = G.map_coefficients(lambda c:c.add_bigoh(prec)) assert self.is_key(g) - ret.append((self.augmentation(g, infinity, check=False), g.degree(), None, None)) + ret.append((self.augmentation(g, infinity, check=False), g.degree(), principal_part_bound, None, None)) assert len(F) == 1 break @@ -738,11 +802,20 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib else: continue - verbose("Determining the augmentation for %s"%phi, level=11) + verbose("Determining the augmentation of %s for %s"%(self, phi), level=11) old_mu = self(phi) w = self.augmentation(phi, old_mu, check=False) - w_coefficients = list(w.coefficients(G)) - w_valuations = list(w.valuations(G, coefficients=w_coefficients)) + + w_coefficients = w.coefficients(G) + if principal_part_bound: + w_coefficients = islice(w_coefficients, 0, principal_part_bound + 1, 1) + w_coefficients = [self.simplify(c) for c in w_coefficients] + + w_valuations = w.valuations(G, coefficients=w_coefficients) + if principal_part_bound: + w_valuations = islice(w_valuations, 0, principal_part_bound + 1, 1) + w_valuations = list(w_valuations) + NP = w.newton_polygon(G, valuations=w_valuations).principal_part() verbose("Newton-Polygon for v(phi)=%s : %s"%(self(phi), NP), level=11) slopes = NP.slopes(repetition=True) @@ -772,7 +845,7 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib phi = G.parent()(phi) w = self._base_valuation.augmentation(phi, infinity, check=False) - ret.append((w, phi.degree(), None, None)) + ret.append((w, phi.degree(), principal_part_bound, None, None)) continue for i, slope in enumerate(slopes): @@ -790,12 +863,14 @@ def mac_lane_step(self, G, assume_squarefree=False, assume_equivalence_irreducib from sage.rings.all import ZZ assert (phi.degree() / self.phi().degree()) in ZZ - degree_bound = multiplicities[slope] * self.phi().degree() * e - ret.append((w, degree_bound, w_coefficients, new_valuations)) + degree_bound = multiplicities[slope] * phi.degree() + assert degree_bound <= G.degree() + assert degree_bound >= phi.degree() + ret.append((w, degree_bound, multiplicities[slope], w_coefficients, new_valuations)) assert ret if not report_degree_bounds_and_caches: - ret = [v for v,_,_,_ in ret] + ret = [v for v,_,_,_,_ in ret] return ret def is_key(self, phi, explain=False, assume_equivalence_irreducible=False): @@ -909,7 +984,7 @@ def is_minimal(self, f, assume_equivalence_irreducible=False): if self.is_gauss_valuation(): if self(f) == 0: - F = self.reduce(f) + F = self.reduce(f, check=False) assert not F.is_constant() return F.is_irreducible() else: @@ -935,7 +1010,7 @@ def is_minimal(self, f, assume_equivalence_irreducible=False): list(self.valuations(f))[0] == self(f) and \ tau.divides(len(list(self.coefficients(f))) - 1) - def _equivalence_reduction(self, f, coefficients=None, valuations=None): + def _equivalence_reduction(self, f, coefficients=None, valuations=None, degree_bound=None): r""" Helper method for :meth:`is_equivalence_irreducible` and :meth:`equivalence_decomposition` which essentially returns the @@ -982,7 +1057,9 @@ def _equivalence_reduction(self, f, coefficients=None, valuations=None): valuation -= self.mu()*phi_divides R = self.equivalence_unit(-valuation) - return valuation, phi_divides, self.reduce(f*R) + if degree_bound is not None: + R = R.truncate(degree_bound*self.phi().degree() + 1) + return valuation, phi_divides, self.reduce(f*R, check=False, degree_bound=degree_bound) def is_equivalence_irreducible(self, f, coefficients=None, valuations=None): r""" @@ -1030,7 +1107,7 @@ def is_equivalence_irreducible(self, f, coefficients=None, valuations=None): if phi_divides > 1: return False - def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coefficients=None, valuations=None): + def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coefficients=None, valuations=None, compute_unit=True, degree_bound=None): r""" Return an equivalence decomposition of ``f``, i.e., a polynomial `g(x)=e(x)\prod_i \phi_i(x)` with `e(x)` an equivalence unit (see @@ -1041,6 +1118,21 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi - ``f`` -- a non-zero polynomial in the domain of this valuation + - ``assume_not_equivalence_unit`` -- whether or not to assume that + ``f`` is not an :meth:`equivalence_unit` (default: ``False``) + + - ``coefficients`` -- the coefficients of ``f`` in the :meth:`phi`-adic + expansion if known (default: ``None``) + + - ``valuations`` -- the valuations of ``coefficients`` if known + (default: ``None``) + + - ``compute_unit`` -- whether or not to compute the unit part of the + decomposition (default: ``True``) + + - ``degree_bound`` -- a bound on the degree of the + :meth:`_equivalence_reduction` of ``f`` (default: ``None``) + ALGORITHM: We use the algorithm described in Theorem 4.4 of [ML1936']. After @@ -1116,7 +1208,7 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi sage: V=v1.mac_lane_step(G) # long time sage: v2=V[0] # long time sage: v2.equivalence_decomposition(G) # long time - (x^4 + 4*x^3 + 6*x^2 + 4*x + 2*alpha + 3)^3 * (x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*alpha^4 + alpha + 3)^3 * (x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*alpha^4 + 3*alpha + 3)^3 + (1/387420489) * (x^4 + 2*x^2 + alpha^4 + alpha^3 + 1)^3 * (x^4 + 2*x^2 + 1/2*alpha^4 + alpha^3 + 5*alpha + 1)^3 * (x^4 + 2*x^2 + 3/2*alpha^4 + alpha^3 + 5*alpha + 1)^3 REFERENCES: @@ -1140,20 +1232,28 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi ret = v.equivalence_decomposition(v.domain()(f)) return Factorization([(g.change_ring(self.domain().base_ring()),e) for g,e in ret], unit=ret.unit().change_ring(self.domain().base_ring()), sort=False) - valuation, phi_divides, F = self._equivalence_reduction(f, coefficients=coefficients, valuations=valuations) + valuation, phi_divides, F = self._equivalence_reduction(f, coefficients=coefficients, valuations=valuations, degree_bound=degree_bound) F = F.factor() from sage.misc.misc import verbose verbose("%s factors as %s = %s in reduction"%(f, F.prod(), F), level=20) - R_ = self.equivalence_unit(valuation, reciprocal=True) - unit = self.lift(self.residue_ring()(F.unit())) * R_ + unit = self.domain().one() + if compute_unit: + R_ = self.equivalence_unit(valuation, reciprocal=True) + unit = self.lift(self.residue_ring()(F.unit())) * R_ F = list(F) - from sage.misc.all import prod - unit *= self.lift(self.residue_ring()(prod([ psi.leading_coefficient()**e for psi,e in F ]))) + if compute_unit: + from sage.misc.all import prod + unit *= self.lift(self.residue_ring()(prod([ psi.leading_coefficient()**e for psi,e in F ]))) + F = [(self.lift_to_key(psi/psi.leading_coefficient()),e) for psi,e in F] - unit *= prod([self.equivalence_unit(-self(g), reciprocal=True)**e for g,e in F]) - unit = self.simplify(unit) + + if compute_unit: + for g,e in F: + v_g = self(g) + unit *= self._pow(self.equivalence_unit(-v_g, reciprocal=True), e, error=-v_g*e) + unit = self.simplify(unit) if phi_divides: for i,(g,e) in enumerate(F): @@ -1165,8 +1265,9 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi ret = Factorization(F, unit=unit, sort=False) - assert self.is_equivalent(ret.prod(), f) # this might fail because of leading zeros in inexact rings - assert self.is_equivalence_unit(ret.unit()) + if compute_unit: + assert self.is_equivalent(ret.prod(), f) # this might fail because of leading zeros in inexact rings + assert self.is_equivalence_unit(ret.unit()) return ret @@ -1206,7 +1307,7 @@ def minimal_representative(self, f): (1 + O(2^10))*x + 2 + O(2^11) sage: f = x^3 + 6*x + 4 sage: F = v.minimal_representative(f); F - (2 + 2^2 + O(2^11)) * ((1 + O(2^10))*x + 2 + 2^2 + 2^4 + 2^6 + 2^8 + 2^10 + O(2^11)) + (2 + 2^2 + O(2^11)) * ((1 + O(2^10))*x + 2 + O(2^11)) sage: v.is_minimal(F[0][0]) True sage: v.is_equivalent(F.prod(), f) diff --git a/limit_valuation.py b/limit_valuation.py index f4399cd3eb5..2ebe8a2491d 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -203,10 +203,18 @@ def __init__(self, parent, approximation): self._initial_approximation = approximation self._approximation = approximation - def reduce(self, f): + def reduce(self, f, check=True): r""" Return the reduction of ``f`` as an element of :meth:`residue_ring`. + INPUT: + + - ``f`` -- an element in the domain of this valuation of non-negative + valuation + + - ``check`` -- whether or not to check that ``f`` has non-negative + valuation (default: ``True``) + EXAMPLES:: sage: from mac_lane import * # optional: standalone @@ -222,7 +230,7 @@ def reduce(self, f): """ f = self.domain().coerce(f) self._improve_approximation_for_reduce(f) - F = self._approximation.reduce(f) + F = self._approximation.reduce(f, check=check) return self.residue_ring()(F) def _call_(self, f): @@ -391,6 +399,8 @@ def __init__(self, parent, approximation, G): InfiniteDiscretePseudoValuation.__init__(self, parent) self._G = G + self._next_coefficients = None + self._next_valuations = None def extensions(self, ring): r""" @@ -431,7 +441,7 @@ def lift(self, F): sage: v = FunctionFieldValuation(K, 1) sage: w = v.extensions(L)[0]; w - [ (x - 1)-adic valuation, v(y^2 - x - 1) = +Infinity ]-adic valuation + [ (x - 1)-adic valuation, v(y^2 - 2) = 1 ]-adic valuation sage: s = w.reduce(y); s u1 sage: w.lift(s) # indirect doctest @@ -492,9 +502,9 @@ def _improve_approximation(self): # an infinite valuation can not be improved further return - approximations = self._approximation.mac_lane_step(self._G, assume_squarefree=True, assume_equivalence_irreducible=True) + approximations = self._approximation.mac_lane_step(self._G, assume_squarefree=True, assume_equivalence_irreducible=True, check=False, principal_part_bound=1, report_degree_bounds_and_caches=True) assert(len(approximations)==1) - self._approximation = approximations[0] + self._approximation, _, _, self._next_coefficients, self._next_valuations = approximations[0] def _improve_approximation_for_call(self, f): r""" @@ -690,8 +700,8 @@ def _weakly_separating_element(self, other): sage: w,ww = v.extensions(L) sage: v = FunctionFieldValuation(K, 1) sage: v = v.extension(L) - sage: u.separating_element([uu,ww,w,v]) # long time - ((-8*x^4 - 12*x^2 - 4)/(x^2 - x))*y + (8*x^4 + 8*x^2 + 1)/(x^3 - x^2) + sage: u.separating_element([uu,ww,w,v]) # long time, random output + ((8*x^4 + 12*x^2 + 4)/(x^2 - x))*y + (8*x^4 + 8*x^2 + 1)/(x^3 - x^2) The underlying algorithm is quite naive and might not terminate in reasonable time. In particular, the order of the arguments sometimes @@ -755,7 +765,33 @@ def element_with_valuation(self, s): """ return self._initial_approximation.element_with_valuation(s) - def simplify(self, f, error=None): + def _relative_size(self, f): + r""" + Return an estimate on the coefficient size of ``f``. + + The number returned is an estimate on the factor between the number of + bits used by ``f`` and the minimal number of bits used by an element + congruent to ``f``. + + This is used by :meth:`simplify` to decide whether simplification of + coefficients is going to lead to a significant shrinking of the + coefficients of ``f``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: u = v.extension(L) + sage: u._relative_size(1024*t + 1024) + 11 + + """ + return self._initial_approximation._relative_size(f) + + def simplify(self, f, error=None, force=False): r""" Return a simplified version of ``f``. @@ -771,17 +807,66 @@ def simplify(self, f, error=None): sage: L. = K.extension(t^2 + 1) sage: v = pAdicValuation(QQ, 2) sage: u = v.extension(L) - sage: u.simplify(t + 1024) - 1 + sage: u.simplify(t + 1024, force=True) + t """ f = self.domain().coerce(f) - v = self(f) + self._improve_approximation_for_call(f) # now _approximation is sufficiently precise to compute a valid # simplification of f if error is None: - error = v + error = self.upper_bound(f) + + return self._approximation.simplify(f, error, force=force) + + def lower_bound(self, f): + r""" + Return a lower bound of this valuation at ``x``. + + Use this method to get an approximation of the valuation of ``x`` + when speed is more important than accuracy. + + EXAMPLES:: - return self._approximation.simplify(f, error) + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: u = v.extension(L) + sage: u.lower_bound(1024*t + 1024) + 10 + sage: u(1024*t + 1024) + 21/2 + + """ + f = self.domain().coerce(f) + return self._approximation.lower_bound(f) + + def upper_bound(self, f): + r""" + Return an upper bound of this valuation at ``x``. + + Use this method to get an approximation of the valuation of ``x`` + when speed is more important than accuracy. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: u = v.extension(L) + sage: u.upper_bound(1024*t + 1024) + 21/2 + sage: u(1024*t + 1024) + 21/2 + + """ + f = self.domain().coerce(f) + self._improve_approximation_for_call(f) + return self._approximation.upper_bound(f) diff --git a/mapped_valuation.py b/mapped_valuation.py index 90e4a8499cb..45ec9d2cdf9 100644 --- a/mapped_valuation.py +++ b/mapped_valuation.py @@ -407,7 +407,33 @@ def _weakly_separating_element(self, other): return self.domain()(self._base_valuation._weakly_separating_element(other._base_valuation)) super(FiniteExtensionFromInfiniteValuation, self)._weakly_separating_element(other) - def simplify(self, x, error=None): + def _relative_size(self, x): + r""" + Return an estimate on the coefficient size of ``x``. + + The number returned is an estimate on the factor between the number of + Bits used by ``x`` and the minimal number of bits used by an element + Congruent to ``x``. + + This is used by :meth:`simplify` to decide whether simplification of + Coefficients is going to lead to a significant shrinking of the + Coefficients of ``x``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: w = v.extension(L) + sage: w._relative_size(1024*t + 1024) + 11 + + """ + return self._base_valuation._relative_size(self._to_base_domain(x)) + + def simplify(self, x, error=None, force=False): r""" Return a simplified version of ``x``. @@ -423,16 +449,65 @@ def simplify(self, x, error=None): sage: L. = K.extension(t^2 + 1) sage: v = pAdicValuation(QQ, 5) sage: u,uu = v.extensions(L) - sage: u.simplify(125*t + 1) + sage: f = 125*t + 1 + sage: u.simplify(f, error=u(f), force=True) 1 """ x = self.domain().coerce(x) if error is None: - error = self(x) + error = self.upper_bound(x) + + return self._from_base_domain(self._base_valuation.simplify(self._to_base_domain(x), error, force=force)) + + def lower_bound(self, x): + r""" + Return an lower bound of this valuation at ``x``. + + Use this method to get an approximation of the valuation of ``x`` + when speed is more important than accuracy. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 5) + sage: u,uu = v.extensions(L) + sage: u.lower_bound(t + 2) + 0 + sage: u(t + 2) + 1 + + """ + x = self.domain().coerce(x) + return self._base_valuation.lower_bound(self._to_base_domain(x)) + + def upper_bound(self, x): + r""" + Return an upper bound of this valuation at ``x``. + + Use this method to get an approximation of the valuation of ``x`` + when speed is more important than accuracy. - return self._from_base_domain(self._base_valuation.simplify(self._to_base_domain(x), error)) + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 5) + sage: u,uu = v.extensions(L) + sage: u.upper_bound(t + 2) + 3 + sage: u(t + 2) + 1 + + """ + x = self.domain().coerce(x) + return self._base_valuation.upper_bound(self._to_base_domain(x)) class FiniteExtensionFromLimitValuation(FiniteExtensionFromInfiniteValuation): diff --git a/padic_valuation.py b/padic_valuation.py index 924c4027362..f320ddb1ebc 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -156,7 +156,7 @@ class PadicValuationFactory(UniqueFactory): 2-adic valuation """ - def create_key_and_extra_args(self, R, prime=None): + def create_key_and_extra_args(self, R, prime=None, approximants=None): r""" Create a unique key identifying the valuation of ``R`` with respect to ``prime``. @@ -182,7 +182,7 @@ def create_key_and_extra_args(self, R, prime=None): elif isinstance(R, pAdicGeneric): return self.create_key_for_local_ring(R, prime), {} elif is_NumberField(R.fraction_field()) or is_PolynomialQuotientRing(R): - return self.create_key_and_extra_args_for_number_field(R, prime) + return self.create_key_and_extra_args_for_number_field(R, prime, approximants=approximants) else: raise NotImplementedError("p-adic valuations not implemented for %r"%(R,)) @@ -231,7 +231,7 @@ def create_key_for_local_ring(self, R, prime): return (R,) - def create_key_and_extra_args_for_number_field(self, R, prime): + def create_key_and_extra_args_for_number_field(self, R, prime, approximants): r""" Create a unique key identifying the valuation of ``R`` with respect to ``prime``. @@ -248,15 +248,15 @@ def create_key_and_extra_args_for_number_field(self, R, prime): from sage.rings.number_field.number_field_ideal import NumberFieldFractionalIdeal from valuation import DiscretePseudoValuation if isinstance(prime, DiscretePseudoValuation): - return self.create_key_and_extra_args_for_number_field_from_valuation(R, prime, prime) + return self.create_key_and_extra_args_for_number_field_from_valuation(R, prime, prime, approximants=approximants) elif prime in K: - return self.create_key_and_extra_args_for_number_field_from_valuation(R, pAdicValuation(K, prime), prime) + return self.create_key_and_extra_args_for_number_field_from_valuation(R, pAdicValuation(K, prime), prime, approximants=approximants) elif prime in L or isinstance(prime, NumberFieldFractionalIdeal): return self.create_key_and_extra_args_for_number_field_from_ideal(R, L.fractional_ideal(prime), prime) else: raise ValueError("prime must be a discrete pseudo-valuation, a prime in the base ring, or a fractional ideal") - def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime): + def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime, approximants): r""" Create a unique key identifying the valuation of ``R`` with respect to ``v``. @@ -315,7 +315,9 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime) # description of v. We consider all extensions of vK to L and select # the one approximated by v. vK = v.restriction(v.domain().base_ring()).extension(K) - approximants = vK.mac_lane_approximants(G) + if approximants is None: + approximants = vK.mac_lane_approximants(G) + approximants = [approximant.extension(v.domain()) for approximant in approximants] approximant = vK.mac_lane_approximant(G, v, approximants=tuple(approximants)) return (R, approximant, L.construction()), {'approximants': approximants} @@ -382,9 +384,6 @@ def _normalize_number_field_data(self, R): x^2 + 1) """ - # To make our lives easier, we rewrite v over the fraction field of R - # which we denote in the following as L = K[x]/(G), do all computations - # there and then come back to the original ring from sage.rings.polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing from sage.rings.number_field.number_field import is_NumberField from sage.rings.fraction_field import is_FractionField @@ -775,7 +774,7 @@ def extensions(self, ring): from valuation_space import DiscretePseudoValuationSpace parent = DiscretePseudoValuationSpace(ring) approximants = self.mac_lane_approximants(ring.modulus().change_ring(self.domain()), assume_squarefree=True) - return [pAdicValuation(ring, approximant) for approximant in approximants] + return [pAdicValuation(ring, approximant, approximants) for approximant in approximants] else: return sum([w.extensions(ring) for w in self.extensions(ring.base_ring())], []) from sage.rings.number_field.number_field import is_NumberField @@ -784,7 +783,7 @@ def extensions(self, ring): from valuation_space import DiscretePseudoValuationSpace parent = DiscretePseudoValuationSpace(ring) approximants = self.mac_lane_approximants(ring.fraction_field().relative_polynomial().change_ring(self.domain()), assume_squarefree=True) - return [pAdicValuation(ring, approximant) for approximant in approximants] + return [pAdicValuation(ring, approximant, approximants) for approximant in approximants] if ring.base_ring() is not ring and self.domain().is_subring(ring.base_ring()): return sum([w.extensions(ring) for w in self.extensions(ring.base_ring())], []) return super(pAdicValuation_base, self).extensions(ring) @@ -801,6 +800,9 @@ def restriction(self, ring): 2-adic valuation """ + if ring is self.domain(): + return self + if not ring.is_subring(self.domain()): raise ValueError("ring must be a subring of the domain of this valuation but %r is not a subring of %r"%(ring, self.domain())) @@ -1012,7 +1014,7 @@ def shift(self, x, s): v = ZZ(s / self.domain().ramification_index()) return x << v - def simplify(self, x, error=None): + def simplify(self, x, error=None, force=False): r""" Return a simplified version of ``x``. @@ -1020,6 +1022,15 @@ def simplify(self, x, error=None): valuation strictly greater than the valuation of ``x`` (or strictly greater than ``error`` if set.) + INPUT: + + - ``x`` -- an element in the domain of this valuation + + - ``error`` -- a rational, infinity, or ``None`` (default: ``None``), + the error allowed to introduce through the simplification + + - ``force`` -- ignored + EXAMPLES:: sage: from mac_lane import * # optional: standalone @@ -1090,7 +1101,10 @@ def _call_(self, x): # of a rational zero than when computing the valuation of another # small rational. Special casing this is a factor 100 faster. return infinity - return x.valuation(self.p()) + #if x.global_height() > 128: + # import pdb + # pdb.set_trace() + return x.valuation(self._p) def uniformizer(self): """ @@ -1142,7 +1156,32 @@ def _ge_(self, other): return self.p() == other.p() return super(pAdicValuation_base, self)._ge_(other) - def simplify(self, x, error=None): + def _relative_size(self, x): + r""" + Return an estimate on the coefficient size of ``x``. + + The number returned is an estimate on the factor between the number of + bits used by ``x`` and the minimal number of bits used by an element + congruent to ``x``. + + This is used by :meth:`simplify` to decide whether simplification of + coefficients is going to lead to a significant shrinking of the + coefficients of ``x``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 2) + sage: v._relative_size(2) + 2 + sage: v._relative_size(2**20) + 21 + + """ + x = self.domain().coerce(x) + return x.numerator().nbits() + x.denominator().nbits() - 1 + + def simplify(self, x, error=None, force=False, size_heuristic_bound=32): r""" Return a simplified version of ``x``. @@ -1150,16 +1189,32 @@ def simplify(self, x, error=None): valuation strictly greater than the valuation of ``x`` (or strictly greater than ``error`` if set.) + INPUT: + + - ``x`` -- an element in the domain of this valuation + + - ``error`` -- a rational, infinity, or ``None`` (default: ``None``), + the error allowed to introduce through the simplification + + - ``force`` -- ignored + + - ``size_heuristic_bound` -- when ``force`` is not set, the expected + factor by which the ``x`` need to shrink to perform an actual + simplification (default: 32) + EXAMPLES:: sage: from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) - sage: v.simplify(6) + sage: v.simplify(6, force=True) 2 - sage: v.simplify(6, error=0) + sage: v.simplify(6, error=0, force=True) 0 """ + if not force and self._relative_size(x) <= size_heuristic_bound: + return x + x = self.domain().coerce(x) v = self(x) @@ -1180,9 +1235,11 @@ def simplify(self, x, error=None): # If there is not much relative precision left, it is better to # just go with the integer/rational lift. The rational # reconstruction is likely not smaller. - reconstruction = reduced.rational_reconstruction() - if reconstruction in self.domain(): - return self.domain()(reconstruction) + try: + reconstruction = reduced.rational_reconstruction() + if reconstruction in self.domain(): + return self.domain()(reconstruction) + except ArithmeticError:pass return self.domain()(reduced.lift()) @@ -1216,12 +1273,6 @@ def __init__(self, parent, approximant, G, approximants): FiniteExtensionFromLimitValuation.__init__(self, parent, approximant, G, approximants) pAdicValuation_base.__init__(self, parent, approximant.restriction(approximant.domain().base_ring()).p()) - from sage.rings.polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing - if is_PolynomialQuotientRing(self.domain()): - self._gen = self.domain().gen() - else: - self._gen = self.domain()(self.domain().fraction_field().gen()) - def _to_base_domain(self, f): r""" Return ``f``, an element of the domain of this valuation, as an element @@ -1252,7 +1303,7 @@ def _from_base_domain(self, f): I """ - return f(self._gen) + return self.domain()(f) def extensions(self, ring): r""" diff --git a/valuation.py b/valuation.py index 125dd0cd008..17a954e6b7a 100644 --- a/valuation.py +++ b/valuation.py @@ -26,6 +26,8 @@ from sage.categories.morphism import Morphism +from sage.misc.cachefunc import cached_method + class DiscretePseudoValuation(Morphism): r""" Abstract base class for discrete pseudo-valuations, i.e., discrete @@ -498,9 +500,9 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: v0 = GaussValuation(K._ring,pAdicValuation(QQ,3)) sage: v1 = v0.augmentation(K._ring.gen(),1/3) sage: mu0 = FunctionFieldValuation(K, v1) - sage: mu0.mac_lane_approximants(F) # long time - [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + x) = 2/3 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + 2*x) = 2/3 ]] + sage: sorted(mu0.mac_lane_approximants(F), key=str) # long time + [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + 2*x) = 2/3 ], + [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + x) = 2/3 ]] Over a complete base field:: @@ -543,9 +545,9 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: v0.mac_lane_approximants(G) [[ Gauss valuation induced by 5-adic valuation, v(x + 2) = 1 ], [ Gauss valuation induced by 5-adic valuation, v(x + 3) = 1 ]] - sage: v0.mac_lane_approximants(G, required_precision = 10) # long time - [[ Gauss valuation induced by 5-adic valuation, v(x + 6139557) = 10 ], - [ Gauss valuation induced by 5-adic valuation, v(x + 3626068) = 10 ]] + sage: sorted(v0.mac_lane_approximants(G, required_precision = 10), key=str) # long time + [[ Gauss valuation induced by 5-adic valuation, v(x + 3626068) = 10 ], + [ Gauss valuation induced by 5-adic valuation, v(x + 6139557) = 10 ]] The same example over the 5-adic numbers. In the quadratic extension `\QQ[x]/(x^2+1)`, 5 factors `-(x - 2)(x + 2)`, this behaviour can be @@ -555,23 +557,23 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: v = pAdicValuation(k) sage: R.=k[] sage: G = x^2 + 1 - sage: v1,v2 = v.mac_lane_approximants(G); v1,v2 - ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + O(5^4))) = 1 ], - [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + O(5^4))) = 1 ]) - sage: w1, w2 = v.mac_lane_approximants(G, required_precision = 2); w1,w2 # long time - ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + O(5^4))) = 2 ], - [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + 3*5 + O(5^4))) = 2 ]) + sage: v1,v2 = v.mac_lane_approximants(G); sorted([v1,v2], key=str) + [[ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + O(5^4))) = 1 ], + [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + O(5^4))) = 1 ]] + sage: w1, w2 = v.mac_lane_approximants(G, required_precision = 2); sorted([w1,w2], key=str) # long time + [[ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + O(5^4))) = 2 ], + [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + 3*5 + O(5^4))) = 2 ]] Note how the latter give a better approximation to the factors of `x^2 + 1`:: sage: v1.phi() * v2.phi() - G # optional: integrated (5 + O(5^4))*x + 5 + O(5^4) - sage: w1.phi() * w2.phi() - G # optional: integrated + sage: w1.phi() * w2.phi() - G # optional: integrated, long time (5^3 + O(5^4))*x + 5^3 + O(5^4) In this example, the process stops with a factorization of `x^2 + 1`:: - sage: v.mac_lane_approximants(G, required_precision=infinity) # long time + sage: sorted(v.mac_lane_approximants(G, required_precision=infinity), key=str) # long time [[ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + 2*5^2 + 5^3 + O(5^4))) = +Infinity ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + 3*5 + 2*5^2 + 3*5^3 + O(5^4))) = +Infinity ]] @@ -583,9 +585,9 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: G = x^2 + 1 sage: v.mac_lane_approximants(G) [[ Gauss valuation induced by 5-adic valuation, v(x + 2) = 1 ], [ Gauss valuation induced by 5-adic valuation, v(x + 3) = 1 ]] - sage: v.mac_lane_approximants(G, required_precision=5) # long time - [[ Gauss valuation induced by 5-adic valuation, v(x + 2057) = 5 ], - [ Gauss valuation induced by 5-adic valuation, v(x + 1068) = 6 ]] + sage: sorted(v.mac_lane_approximants(G, required_precision=5), key=str) # long time + [[ Gauss valuation induced by 5-adic valuation, v(x + 1068) = 6 ], + [ Gauss valuation induced by 5-adic valuation, v(x + 2057) = 5 ]] Initial versions ran into problems with the trivial residue field extensions in this case:: @@ -620,10 +622,10 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: vK(theta) 1/3 sage: G=Delta.change_ring(K) - sage: V=vK.mac_lane_approximants(G); V # long time - [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + 2*theta + 3) = 5/3 ], - [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*theta^4 + theta + 3) = 5/3 ], - [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 4*x^3 + 6*x^2 + 4*x + 1/2*theta^4 + 3*theta + 3) = 5/3 ]] + sage: sorted(vK.mac_lane_approximants(G), key=str) # long time + [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 2*x^2 + 1/2*theta^4 + theta^3 + 5*theta + 1) = 5/3 ], + [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 2*x^2 + 3/2*theta^4 + theta^3 + 5*theta + 1) = 5/3 ], + [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 2*x^2 + theta^4 + theta^3 + 1) = 5/3 ]] """ R = G.parent() @@ -631,22 +633,11 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru raise ValueError("G must be defined over the domain of this valuation") from sage.misc.misc import verbose - verbose("Approximants of %r towards %r"%(self, G), level=3) + verbose("Approximants of %r on %r towards %r"%(self, self.domain(), G), level=3) from sage.rings.all import infinity from gauss_valuation import GaussValuation - # Leaves in the computation of the tree of approximants. Each vertex - # consists of a tuple (v,ef,p,coeffs,vals) where v is an approximant, i.e., a - # valuation, ef is a boolean, p is the parent of this vertex, and - # coeffs and vals are cached values. (Only v and ef are relevant, - # everything else are caches/debug info.) - # The boolean ef denotes whether v already has the final ramification - # index E and residue degree F of this approximant. - # An edge V -- P represents the relation P.v ≤ V.v (pointwise on the - # polynomial ring K[x]) between the valuations. - leaves = [ (GaussValuation(R, self), G.degree() == 1, None, None, None) ] - if require_maximal_degree: # we can only assert maximality of degrees when E and F are final require_final_EF = True @@ -662,36 +653,109 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru pass def is_sufficient(leaf, others): - valuation, ef, _, _, _ = leaf - if valuation.mu() < required_precision: + if leaf.valuation.mu() < required_precision: return False - if require_final_EF and not ef: + if require_final_EF and not leaf.ef: return False - if require_maximal_degree and valuation.phi().degree() != valuation.E()*valuation.F(): + if require_maximal_degree and leaf.valuation.phi().degree() != leaf.valuation.E()*leaf.valuation.F(): return False if require_incomparability: - if any(valuation <= o for o in others): + if any(leaf.valuation <= o.valuation for o in others): return False return True - while True: - new_leaves = [] - for leaf in leaves: - v, ef, coefficients, valuations, parent = leaf - others = [w for (w,_,_,_,_) in leaves if w != v] - if is_sufficient(leaf, others): - new_leaves.append(leaf) - else: - augmentations = v.mac_lane_step(G, report_degree_bounds_and_caches=True, coefficients=coefficients, valuations=valuations) - for w, bound, coefficients, valuations in augmentations: - ef = bound == w.E()*w.F() - new_leaves.append((w, ef, coefficients, valuations, leaf)) - - if leaves == new_leaves: - break - leaves = new_leaves - - return [v for v,_,_,_,_ in leaves] + # Leaves in the computation of the tree of approximants. Each vertex + # consists of a tuple (v,ef,p,coeffs,vals) where v is an approximant, i.e., a + # valuation, ef is a boolean, p is the parent of this vertex, and + # coeffs and vals are cached values. (Only v and ef are relevant, + # everything else are caches/debug info.) + # The boolean ef denotes whether v already has the final ramification + # index E and residue degree F of this approximant. + # An edge V -- P represents the relation P.v ≤ V.v (pointwise on the + # polynomial ring K[x]) between the valuations. + class Node(object): + def __init__(self, valuation, parent, ef, principal_part_bound, coefficients, valuations): + self.valuation = valuation + self.parent = parent + self.ef = ef + self.principal_part_bound = principal_part_bound + self.coefficients = coefficients + self.valuations = valuations + self.forced_leaf = False + import mac_lane + mac_lane.valuation.Node = Node + + seed = Node(GaussValuation(R,self), None, G.degree() == 1, G.degree(), None, None) + seed.forced_leaf = is_sufficient(seed, []) + + def create_children(node): + new_leafs = [] + if node.forced_leaf: + return new_leafs + augmentations = node.valuation.mac_lane_step(G, report_degree_bounds_and_caches=True, coefficients=node.coefficients, valuations=node.valuations, check=False, principal_part_bound=node.principal_part_bound) + for w, bound, principal_part_bound, coefficients, valuations in augmentations: + ef = bound == w.E()*w.F() + new_leafs.append(Node(w, node, ef, principal_part_bound, coefficients, valuations)) + for leaf in new_leafs: + if is_sufficient(leaf, [l for l in new_leafs if l is not leaf]): + leaf.forced_leaf = True + return new_leafs + + def reduce_tree(v, w): + return v + w + + from sage.all import RecursivelyEnumeratedSet + tree = RecursivelyEnumeratedSet([seed], + successors = create_children, + structure = 'forest', + enumeration = 'breadth') + # this is a tad faster but annoying for profiling / debugging + #nodes = tree.map_reduce( + # map_function = lambda x: [x], + # reduce_init = []) + from sage.parallel.map_reduce import RESetMapReduce + nodes = RESetMapReduce( + forest = tree, + map_function = lambda x: [x], + reduce_init = []).run_serial() + leafs = set([node.valuation for node in nodes]) + for node in nodes: + if node.parent is None: + continue + v = node.parent.valuation + if v in leafs: + leafs.remove(v) + + + return list(leafs) + + @cached_method + def _pow(self, x, e, error): + r""" + Return `x^e`. + + This method does not compute the exact value of `x^e` but only an + element that differs from the correct result by an error with valuation + at least ``error``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(QQ, 2) + sage: v._pow(2, 2, error=4) + 4 + sage: v._pow(2, 1000, error=4) + 0 + + """ + if e == 0: + return self.domain().one() + if e == 1: + return self.simplify(x, error=error) + if e % 2 == 0: + return self._pow(self.simplify(x*x, error=error*2/e), e//2, error=error) + else: + return self.simplify(x*self._pow(x, e-1, error=error*(e-1)/e), error=error) def mac_lane_approximant(self, G, valuation, approximants = None): r""" @@ -827,10 +891,11 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No sage: v.montes_factorization(x^2 + 1) x^2 + 1 - sage: v.montes_factorization(x^2 - 1) # not tested, does not terminate + sage: v.montes_factorization(x^2 - 1) + (x - 1) * (x + 1) - sage: v.montes_factorization(x^2 - 1, required_precision=10) - (x + 1) * (x + 1023) + sage: v.montes_factorization(x^2 - 1, required_precision=5) + (x + 1) * (x + 31) REFERENCES: diff --git a/valuation_space.py b/valuation_space.py index 5fa62bdaf69..4c221509223 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -315,6 +315,7 @@ def is_discrete_valuation(self): """ + @cached_method def is_trivial(self): r""" Return whether this valuation is trivial, i.e., whether it is @@ -452,7 +453,8 @@ def element_with_valuation(self, s): exp = s/self.value_group().gen() if exp not in ZZ: raise NotImplementedError("s must be a multiple of %r but %r is not"%(self.value_group().gen(), s)) - return self.domain()(self.uniformizer() ** ZZ(exp)) + ret = self.domain()(self.uniformizer() ** ZZ(exp)) + return self.simplify(ret, error=s) @abstract_method def residue_ring(self): @@ -919,7 +921,7 @@ def shift(self, x, s): x //= self.uniformizer() return x - def simplify(self, x, error=None): + def simplify(self, x, error=None, force=False): r""" Return a simplified version of ``x``. @@ -931,9 +933,9 @@ def simplify(self, x, error=None): sage: from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) - sage: v.simplify(6) + sage: v.simplify(6, force=True) 2 - sage: v.simplify(6, error=0) + sage: v.simplify(6, error=0, force=True) 0 """ @@ -943,6 +945,88 @@ def simplify(self, x, error=None): return self.domain().zero() return x + def lower_bound(self, x): + r""" + Return a lower bound of this valuation at ``x``. + + Use this method to get an approximation of the valuation of ``x`` + when speed is more important than accuracy. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 2) + sage: v.lower_bound(2^10) + 10 + + """ + return self(x) + + def upper_bound(self, x): + r""" + Return an upper bound of this valuation at ``x``. + + Use this method to get an approximation of the valuation of ``x`` + when speed is more important than accuracy. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 2) + sage: v.upper_bound(2^10) + 10 + + """ + return self(x) + + def _relative_size(self, x): + r""" + Return an estimate on the coefficient size of ``x``. + + The number returned is an estimate on the factor between the number of + Bits used by ``x`` and the minimal number of bits used by an element + Congruent to ``x``. + + This is used by :meth:`simplify` to decide whether simplification of + Coefficients is going to lead to a significant shrinking of the + Coefficients of ``x``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(Qp(2)) + sage: v._relative_size(2) + 1 + + Some valuations do not overwrite this method because simplification + does not increase the speed of valuations, e.g., some `p`-adic + valuations:: + + sage: v._relative_size(2**20) + 1 + + """ + return 1 + + def _test_bounds(self, **options): + r""" + Check that :meth:`lower_bound` and :meth:`upper_bound` work + correctly. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 3) + sage: v._test_bounds() + + """ + tester = self._tester(**options) + + X = self.domain().some_elements() + for x in tester.some_elements(X): + tester.assertGreaterEqual(self.upper_bound(x), self(x)) + tester.assertLessEqual(self.lower_bound(x), self(x)) + def _test_simplify(self, **options): r""" Check that :meth:`simplify` works correctly. From c1e1f5fa08a68338d17e2a98ab80ecbf7bb40ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 14 Jan 2017 12:20:56 -0500 Subject: [PATCH 204/740] reduce() accepts coefficients and valuations --- augmented_valuation.py | 52 ++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 9d530a6227b..5d97ac1777c 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -905,7 +905,7 @@ def residue_ring(self): # extension() implementation.) return base - def reduce(self, f, check=True, degree_bound=None): + def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations=None): r""" Reduce ``f`` module this valuation. @@ -919,6 +919,14 @@ def reduce(self, f, check=True, degree_bound=None): - ``degree_bound`` -- an a-priori known bound on the degree of the result which can speed up the computation (default: not set) + - ``coefficients`` -- the coefficients of ``f`` as produced by + :meth:`coefficients` or ``None`` (default: ``None``); this can be + used to speed up the computation when the expansion of ``f`` is + already known from a previous computation. + + - ``valuations`` -- the valuations of ``coefficients`` or ``None`` + (default: ``None``); ignored + OUTPUT: an element of the :meth:`residue_ring` of this valuation, the reduction @@ -988,7 +996,10 @@ def reduce(self, f, check=True, degree_bound=None): elif v > 0: return self.residue_ring().zero() - constant_term = self.coefficients(f).next() + if coefficients is None: + constant_term = self.coefficients(f).next() + else: + constant_term = coefficients[0] constant_term_reduced = self._base_valuation.reduce(constant_term) return constant_term_reduced(self._residue_field_generator()) @@ -1163,7 +1174,7 @@ def residue_ring(self): pass return base[self.domain().variable_name()] - def reduce(self, f, check=True, degree_bound=None): + def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations=None): r""" Reduce ``f`` module this valuation. @@ -1177,6 +1188,14 @@ def reduce(self, f, check=True, degree_bound=None): - ``degree_bound`` -- an a-priori known bound on the degree of the result which can speed up the computation (default: not set) + - ``coefficients`` -- the coefficients of ``f`` as produced by + :meth:`coefficients` or ``None`` (default: ``None``); this can be + used to speed up the computation when the expansion of ``f`` is + already known from a previous computation. + + - ``valuations`` -- the valuations of ``coefficients`` or ``None`` + (default: ``None``) + OUTPUT: an element of the :meth:`residue_ring` of this valuation, the reduction @@ -1244,13 +1263,16 @@ def reduce(self, f, check=True, degree_bound=None): tau = self.value_group().index(self._base_valuation.value_group()) - coefficients = self.coefficients(f) - if degree_bound is not None: - from itertools import islice - coefficients = islice(coefficients, 0, tau*degree_bound + 1, 1) + if coefficients is None: + coefficients = self.coefficients(f) + if degree_bound is not None: + from itertools import islice + coefficients = islice(coefficients, 0, tau*degree_bound + 1, 1) coefficients = list(coefficients) - valuations = [] + if valuations is None: + valuations = [] + valuations = valuations[::tau] # rewrite as sum of f_i phi^{i tau}, i.e., drop the coefficients that # can have no influence on the reduction @@ -1265,17 +1287,23 @@ def reduce(self, f, check=True, degree_bound=None): # the validity of the coefficients with i % tau == 0 is checked by # the recursive call to reduce below # replace f_i by f_i Q^{i tau} - v = self._base_valuation.lower_bound(c) - valuations.append(v) - if v is infinity or v > i*self._mu: + if i//tau >= len(valuations): + # we do not the correct valuation of the coefficient, but + # the computation is faster if we know that the coefficient + # has positive valuation + valuations.append(self._base_valuation.lower_bound(c) + i*self._mu) + v = valuations[i//tau] + if v is infinity or v > 0: coefficients[i] = self.domain().zero() + valuations[i//tau] = infinity else: coefficients[i] = c * self._Q(i//tau) + valuations[i//tau] -= i*self._mu coefficients = coefficients[::tau] # recursively reduce the f_i Q^{i tau} - C = [self._base_valuation.reduce(c, check=False)(self._residue_field_generator()) if valuations[i] <= i*self._mu else self._base_valuation.residue_ring().zero() for i,c in enumerate(coefficients)] + C = [self._base_valuation.reduce(c, check=False)(self._residue_field_generator()) if valuations[i] is not infinity else self._base_valuation.residue_ring().zero() for i,c in enumerate(coefficients)] # reduce the Q'^i phi^i return self.residue_ring()(C) From a9a7bf1216c20bcf21b096b00f65c7f727cfe524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 14 Jan 2017 12:25:10 -0500 Subject: [PATCH 205/740] Faster lifting by not composing the resulting polynomial when it has to be decomposed in the next step anyway --- augmented_valuation.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 5d97ac1777c..652bd5f4658 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -1054,7 +1054,7 @@ def lift(self, F): OUTPUT: - A polynomial in the domain of the valuation with reduction ``F``. + A polynomial in the domain of the valuation with reduction ``F`` EXAMPLES:: @@ -1334,7 +1334,7 @@ def _residue_field_generator(self): assert self.psi()(ret).is_zero() return ret - def lift(self, F): + def lift(self, F, report_coefficients=False): """ Return a polynomial which :meth:`reduce`s to ``F``. @@ -1342,6 +1342,10 @@ def lift(self, F): - ``F`` -- an element of the :meth:`residue_ring` + - ``report_coefficients`` -- whether to return the coefficients of the + :meth:`phi`-adic expansion or the actual polynomial (default: + ``False``, i.e., return the polynomial) + OUTPUT: A polynomial in the domain of the valuation with reduction ``F``, monic @@ -1424,6 +1428,9 @@ def lift(self, F): # now we undo the factors of Q^i (the if else is necessary to handle the case when mu is infinity, i.e., when _Q_reciprocal() is undefined) coeffs = [ (c if i == 0 else c*self._Q_reciprocal(i)).map_coefficients(_lift_to_maximal_precision) for i,c in enumerate(coeffs) ] + if report_coefficients: + return coeffs + RR = self.domain().change_ring(self.domain()) tau = self.value_group().index(self._base_valuation.value_group()) From 7b78f909c3a616a2e722198fcc175da5cd79d57b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 14 Jan 2017 12:26:27 -0500 Subject: [PATCH 206/740] use report_coefficients in lift_to_key --- augmented_valuation.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 652bd5f4658..d61ca4c765d 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -1510,20 +1510,11 @@ def lift_to_key(self, F, check=True): if F == F.parent().gen(): return self.phi() - f = self.lift(F) - assert self.reduce(f) == F - - f *= self._Q(F.degree()) - coefficients = list(self.coefficients(f)) - valuations = list(self.valuations(f, coefficients=coefficients)) - CV = zip(coefficients, valuations) - vf = min(valuations) - CV = [(c,v) if v==vf else (c.parent().zero(),infinity) for c,v in CV] - while CV[-1][1] is infinity: - CV.pop() - - CV[-1] = (CV[-1][0].parent().one(), vf) - ret = self.domain().change_ring(self.domain())([c for c,v in CV])(self.phi()) + coefficients = self.lift(F, report_coefficients=True)[:-1] + coefficients = [c*self._Q(F.degree()) for c in coefficients] + [self.domain().one()] + tau = self.value_group().index(self._base_valuation.value_group()) + vf = self._mu * tau * F.degree() + ret = self.domain().change_ring(self.domain())([c for c in coefficients])(self.phi()**tau) ret = self.simplify(ret, error=vf, force=True) ret = ret.map_coefficients(lambda c:_lift_to_maximal_precision(c)) assert (ret == self.phi()) == (F == F.parent().gen()) From 2a20b664175921aa3b46470030f2b4a7446aba99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 14 Jan 2017 12:29:12 -0500 Subject: [PATCH 207/740] Make _pow aware of effective degrees --- augmented_valuation.py | 20 ++++++++++++++++---- developing_valuation.py | 29 +++++++++++++++++++++++++++++ gauss_valuation.py | 17 +++++++++++++++-- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index d61ca4c765d..7951462a6cc 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -1540,7 +1540,7 @@ def _Q(self, e): """ tau = self.value_group().index(self._base_valuation.value_group()) v = self._mu * tau - return self._pow(self.equivalence_unit(v), e, error=v*e) + return self._pow(self.equivalence_unit(v), e, error=v*e, effective_degree=0) @cached_method def _Q_reciprocal(self, e=1): @@ -1564,7 +1564,7 @@ def _Q_reciprocal(self, e=1): tau = self.value_group().index(self._base_valuation.value_group()) v = -self._mu * tau - ret = self._pow(self._Q_reciprocal(1), e, error=v*e) + ret = self._pow(self._Q_reciprocal(1), e, error=v*e, effective_degree=0) assert self.is_equivalence_unit(ret) # esentially this checks that the reduction of Q'*phi^tau is the @@ -1709,7 +1709,7 @@ def valuations(self, f, coefficients=None, call_error=False): lowest_valuation = ret yield ret - def simplify(self, f, error=None, force=False, size_heuristic_bound=32): + def simplify(self, f, error=None, force=False, effective_degree=None, size_heuristic_bound=32): r""" Return a simplified version of ``f``. @@ -1728,6 +1728,10 @@ def simplify(self, f, error=None, force=False, size_heuristic_bound=32): heuristically no change in the coefficient size of ``f`` expected (default: ``False``) + - ``effective_degree`` -- when set, assume that coefficients beyond + ``effective_degree`` in the :meth:`phi`-adic development can be + safely dropped (default: ``None``) + - ``size_heuristic_bound` -- when ``force`` is not set, the expected factor by which the coefficients need to shrink to perform an actual simplification (default: 32) @@ -1745,6 +1749,11 @@ def simplify(self, f, error=None, force=False, size_heuristic_bound=32): """ f = self.domain().coerce(f) + if effective_degree is not None: + if (QQ(f.degree())/self.phi().degree()).ceil() > effective_degree: + from itertools import islice + f = self.domain().change_ring(self.domain())(list(islice(self.coefficients(f), 0, effective_degree + 1, 1)))(self.phi()) + if not force and self._relative_size(f) < size_heuristic_bound: return f @@ -1995,7 +2004,7 @@ def valuations(self, f, coefficients=None, call_error=False): for i in range(num_infty_coefficients): yield infinity - def simplify(self, f, error=None, force=False): + def simplify(self, f, error=None, force=False, effective_degree=None): r""" Return a simplified version of ``f``. @@ -2014,6 +2023,9 @@ def simplify(self, f, error=None, force=False): heuristically no change in the coefficient size of ``f`` expected (default: ``False``) + - ``effective_degree`` -- ignored; for compatibility with other + ``simplify`` methods + EXAMPLES:: sage: from mac_lane import * # optional: standalone diff --git a/developing_valuation.py b/developing_valuation.py index 8964c98ae49..46f684d6a3a 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -120,6 +120,35 @@ def effective_degree(self, f, valuations=None): v = min(valuations) return [i for i,w in enumerate(valuations) if w == v][-1] + @cached_method + def _pow(self, x, e, error, effective_degree): + r""" + Return `x^e`. + + This method does not compute the exact value of `x^e` but only an + element that differs from the correct result by an error with valuation + at least ``error``. The output is assumed to have at most + ``effective_degree``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R = Zp(2,5) + sage: S. = R[] + sage: v = GaussValuation(S) + sage: v._pow(2*x + 1, 10, effective_degree=0, error=5) + (1 + O(2^5)) + + """ + if e == 0: + return self.domain().one() + if e == 1: + return self.simplify(x, error=error) + if e % 2 == 0: + return self._pow(self.simplify(x*x, error=error*2/e, effective_degree=effective_degree*2/e), e//2, error=error, effective_degree=effective_degree) + else: + return self.simplify(x*self._pow(x, e-1, error=error*(e-1)/e, effective_degree=effective_degree*(e-1)/e), error=error, effective_degree=effective_degree) + def coefficients(self, f): r""" Return the `\phi`-adic expansion of ``f``. diff --git a/gauss_valuation.py b/gauss_valuation.py index 266308d911b..eab625cfac2 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -318,7 +318,7 @@ def residue_ring(self): """ return self.domain().change_ring(self._base_valuation.residue_ring()) - def reduce(self, f, check=True, degree_bound=None): + def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations=None): """ Return the reduction of ``f`` modulo this valuation. @@ -332,6 +332,12 @@ def reduce(self, f, check=True, degree_bound=None): - ``degree_bound`` -- an a-priori known bound on the degree of the result which can speed up the computation (default: not set) + - ``coefficients`` -- the coefficients of ``f`` as produced by + :meth:`coefficients` or ``None`` (default: ``None``); ignored + + - ``valuations`` -- the valuations of ``coefficients`` or ``None`` + (default: ``None``); ignored + OUTPUT: A polynomial in the :meth:`residue_ring` of this valuation. @@ -751,7 +757,7 @@ def _relative_size(self, f): """ return self._base_valuation._relative_size(f[0]) - def simplify(self, f, error=None, force=False, size_heuristic_bound=32): + def simplify(self, f, error=None, force=False, size_heuristic_bound=32, effective_degree=None): r""" Return a simplified version of ``f``. @@ -770,6 +776,9 @@ def simplify(self, f, error=None, force=False, size_heuristic_bound=32): heuristically no change in the coefficient size of ``f`` expected (default: ``False``) + - ``effective_degree`` -- when set, assume that coefficients beyond + ``effective_degree`` can be safely dropped (default: ``None``) + - ``size_heuristic_bound` -- when ``force`` is not set, the expected factor by which the coefficients need to shrink to perform an actual simplification (default: 32) @@ -787,6 +796,10 @@ def simplify(self, f, error=None, force=False, size_heuristic_bound=32): """ f = self.domain().coerce(f) + if effective_degree is not None: + if effective_degree < f.degree(): + f = f.truncate(effective_degree + 1) + if not force and self._relative_size(f) < size_heuristic_bound: return f From 881fc53cd9f2305700a7fc51066e86f17eac2a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 14 Jan 2017 12:34:05 -0500 Subject: [PATCH 208/740] Do not simplify coefficients in mac_lane_step so we can use them to compute better approximations without recomputing them (not implemented yet.) --- inductive_valuation.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index 617435fda21..1aec87b3b15 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -809,7 +809,12 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a w_coefficients = w.coefficients(G) if principal_part_bound: w_coefficients = islice(w_coefficients, 0, principal_part_bound + 1, 1) - w_coefficients = [self.simplify(c) for c in w_coefficients] + # We do not simplify w_coefficients here. + # In some cases (in particular when we are quite sure that the + # Newton polygon is not going to split,) we can reuse the + # coefficients a few times to compute better approximations + # without having to recompute them (which is often the most + # expnesive step of the whole process.) w_valuations = w.valuations(G, coefficients=w_coefficients) if principal_part_bound: @@ -817,6 +822,7 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a w_valuations = list(w_valuations) NP = w.newton_polygon(G, valuations=w_valuations).principal_part() + verbose("Newton-Polygon for v(phi)=%s : %s"%(self(phi), NP), level=11) slopes = NP.slopes(repetition=True) multiplicities = {slope : len([s for s in slopes if s == slope]) for slope in slopes} From beb46e3a335a195ec3ea346e23d26d18ef9f9d2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 14 Jan 2017 12:34:54 -0500 Subject: [PATCH 209/740] faster reductions by passing in coefficients and valuations --- inductive_valuation.py | 15 ++++++++++----- limit_valuation.py | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index 1aec87b3b15..7a0408caf72 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -1060,12 +1060,17 @@ def _equivalence_reduction(self, f, coefficients=None, valuations=None, degree_b from sage.rings.all import PolynomialRing R = PolynomialRing(f.parent(), 'phi') f = R(coefficients[phi_divides:])(self.phi()) - valuation -= self.mu()*phi_divides + valuations = [v-self.mu()*phi_divides for v in valuations[phi_divides:]] + coefficients = coefficients[phi_divides:] + valuation = min(valuations) R = self.equivalence_unit(-valuation) - if degree_bound is not None: - R = R.truncate(degree_bound*self.phi().degree() + 1) - return valuation, phi_divides, self.reduce(f*R, check=False, degree_bound=degree_bound) + R = self.coefficients(R).next() + fR_valuations = [v-valuation for v in valuations] + from sage.rings.all import infinity + fR_coefficients = [self.coefficients(c*R).next() if v is not infinity and v == 0 else 0 for c,v in zip(coefficients,fR_valuations)] + + return valuation, phi_divides, self.reduce(f*R, check=False, degree_bound=degree_bound, coefficients=fR_coefficients, valuations=fR_valuations) def is_equivalence_irreducible(self, f, coefficients=None, valuations=None): r""" @@ -1258,7 +1263,7 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi if compute_unit: for g,e in F: v_g = self(g) - unit *= self._pow(self.equivalence_unit(-v_g, reciprocal=True), e, error=-v_g*e) + unit *= self._pow(self.equivalence_unit(-v_g, reciprocal=True), e, error=-v_g*e, effective_degree=0) unit = self.simplify(unit) if phi_divides: diff --git a/limit_valuation.py b/limit_valuation.py index 2ebe8a2491d..27267b24ef1 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -502,7 +502,7 @@ def _improve_approximation(self): # an infinite valuation can not be improved further return - approximations = self._approximation.mac_lane_step(self._G, assume_squarefree=True, assume_equivalence_irreducible=True, check=False, principal_part_bound=1, report_degree_bounds_and_caches=True) + approximations = self._approximation.mac_lane_step(self._G, assume_squarefree=True, assume_equivalence_irreducible=True, check=False, principal_part_bound=1 if self._approximation.E()*self._approximation.F() == self._approximation.phi().degree() else None, report_degree_bounds_and_caches=True) assert(len(approximations)==1) self._approximation, _, _, self._next_coefficients, self._next_valuations = approximations[0] From bee2a5c9eaef6350280f6ad2e4bd7f131a76ba24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 14 Jan 2017 12:36:27 -0500 Subject: [PATCH 210/740] make coefficients a list (not an islice) --- inductive_valuation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/inductive_valuation.py b/inductive_valuation.py index 7a0408caf72..c490c9b829a 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -809,6 +809,7 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a w_coefficients = w.coefficients(G) if principal_part_bound: w_coefficients = islice(w_coefficients, 0, principal_part_bound + 1, 1) + w_coefficients = list(w_coefficients) # We do not simplify w_coefficients here. # In some cases (in particular when we are quite sure that the # Newton polygon is not going to split,) we can reuse the From 9755f0256697fb1da0f259451166a10286b5011a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 14 Jan 2017 13:15:14 -0500 Subject: [PATCH 211/740] Fix lift_to_key --- augmented_valuation.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 7951462a6cc..3c2bcc2ee14 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -1511,7 +1511,14 @@ def lift_to_key(self, F, check=True): return self.phi() coefficients = self.lift(F, report_coefficients=True)[:-1] - coefficients = [c*self._Q(F.degree()) for c in coefficients] + [self.domain().one()] + coefficients = [c*self._Q(F.degree()) for i,c in enumerate(coefficients)] + [self.domain().one()] + if len(coefficients) >= 2: + # The second-highest coefficient could %phi spill over into the + # highest constant (which is a constant one) so we need to mod it + # away. + # This can not happen for other coefficients because self._Q() has + # degree at most the degree of phi. + coefficients[-2] %= self.phi() tau = self.value_group().index(self._base_valuation.value_group()) vf = self._mu * tau * F.degree() ret = self.domain().change_ring(self.domain())([c for c in coefficients])(self.phi()**tau) From 734497c4e426824dee2c827a5263bfcdef04bf19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 14 Jan 2017 15:05:23 -0500 Subject: [PATCH 212/740] expose parallel algorithm in mac_lane_approximants --- valuation.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/valuation.py b/valuation.py index 17a954e6b7a..cebbb2063ed 100644 --- a/valuation.py +++ b/valuation.py @@ -395,7 +395,7 @@ def is_discrete_valuation(self): """ return True - def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=True, required_precision=-1, require_incomparability=False, require_maximal_degree=False): + def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=True, required_precision=-1, require_incomparability=False, require_maximal_degree=False, algorithm="serial"): r""" Return approximants on `K[x]` for the extensions of this valuation to `L=K[x]/(G)`. @@ -435,6 +435,9 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru the last key polynomial has the same degree as the corresponding factor. + - ``algorithm`` -- one of ``"serial"`` or ``"parallel"`` (default: + ``"serial"``); whether or not to parallelize the algorithm + EXAMPLES:: sage: from mac_lane import * # optional: standalone @@ -710,14 +713,18 @@ def reduce_tree(v, w): structure = 'forest', enumeration = 'breadth') # this is a tad faster but annoying for profiling / debugging - #nodes = tree.map_reduce( - # map_function = lambda x: [x], - # reduce_init = []) - from sage.parallel.map_reduce import RESetMapReduce - nodes = RESetMapReduce( - forest = tree, - map_function = lambda x: [x], - reduce_init = []).run_serial() + if algorithm == 'parallel': + nodes = tree.map_reduce( + map_function = lambda x: [x], + reduce_init = []) + elif algorithm == 'serial': + from sage.parallel.map_reduce import RESetMapReduce + nodes = RESetMapReduce( + forest = tree, + map_function = lambda x: [x], + reduce_init = []).run_serial() + else: + raise NotImplementedError(algorithm) leafs = set([node.valuation for node in nodes]) for node in nodes: if node.parent is None: From 9c5d1a512be57717cc40d5b3324da94965d9aa1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 15 Jan 2017 16:16:01 -0500 Subject: [PATCH 213/740] comment on potential speedup --- inductive_valuation.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/inductive_valuation.py b/inductive_valuation.py index c490c9b829a..e857cbc6cca 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -1259,6 +1259,12 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi from sage.misc.all import prod unit *= self.lift(self.residue_ring()(prod([ psi.leading_coefficient()**e for psi,e in F ]))) + # A potential speedup that we tried to implement here: + # When F factors as T^n - a, then instead of using any lift of T^n - a + # we tried to take a lift that approximates well an n-th root of the + # constant coefficient of f[0]. Doing so saved a few invocations of + # mac_lane_step but in the end made hardly any difference. + F = [(self.lift_to_key(psi/psi.leading_coefficient()),e) for psi,e in F] if compute_unit: From af890c0681ed1a3e2fa7aa18ef7da330e71e91aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 15 Jan 2017 16:18:13 -0500 Subject: [PATCH 214/740] comment on a possible (but failed) speedup --- inductive_valuation.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/inductive_valuation.py b/inductive_valuation.py index e857cbc6cca..e2e91362abe 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -806,16 +806,18 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a old_mu = self(phi) w = self.augmentation(phi, old_mu, check=False) + # we made some experiments here: instead of computing the + # coefficients again from scratch, update the coefficients when + # phi - self.phi() is a constant. + # It turned out to be slightly slower than just recomputing the + # coefficients. The main issue with the approach was that we + # needed to keep track of all the coefficients and not just of + # the coefficients up to principal_part_bound. + w_coefficients = w.coefficients(G) if principal_part_bound: w_coefficients = islice(w_coefficients, 0, principal_part_bound + 1, 1) w_coefficients = list(w_coefficients) - # We do not simplify w_coefficients here. - # In some cases (in particular when we are quite sure that the - # Newton polygon is not going to split,) we can reuse the - # coefficients a few times to compute better approximations - # without having to recompute them (which is often the most - # expnesive step of the whole process.) w_valuations = w.valuations(G, coefficients=w_coefficients) if principal_part_bound: From 5b6704bebace325979c0bbb86c3295bda55e419b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 15 Jan 2017 16:44:28 -0500 Subject: [PATCH 215/740] Remove long time markers things are much faster now --- function_field_valuation.py | 8 ++++---- inductive_valuation.py | 10 +++++----- limit_valuation.py | 10 +++++----- valuation.py | 18 +++++++++--------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/function_field_valuation.py b/function_field_valuation.py index a5ac7993872..7b083821ac6 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -31,7 +31,7 @@ (x - 1)-adic valuation sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: w = v.extensions(L); w # long time + sage: w = v.extensions(L); w [[ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation, [ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation] @@ -55,7 +55,7 @@ sage: v = FunctionFieldValuation(K, x - 1) sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: ws = v.extensions(L) # long time + sage: ws = v.extensions(L) sage: for w in ws: TestSuite(w).run(max_runs=100) # long time Run test suite for valuations that do not correspond to a classical place:: @@ -79,7 +79,7 @@ sage: v = FunctionFieldValuation(K, 1/x) sage: R. = K[] sage: L. = K.extension(y^2 - 1/(x^2 + 1)) - sage: w = v.extensions(L) # long time + sage: w = v.extensions(L) sage: TestSuite(w).run() # long time Run test suite for a valuation with `v(1/x) > 0` which does not come from a @@ -543,7 +543,7 @@ def extensions(self, L): sage: v = FunctionFieldValuation(K, 1/x) sage: R. = K[] sage: L. = K.extension(y^2 - 1/(x^2 + 1)) - sage: sorted(v.extensions(L), key=str) # long time + sage: sorted(v.extensions(L), key=str) [[ Valuation at the infinite place, v(y + 1/x) = 3 ]-adic valuation, [ Valuation at the infinite place, v(y - 1/x) = 3 ]-adic valuation] diff --git a/inductive_valuation.py b/inductive_valuation.py index e2e91362abe..dff1bdf4b63 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -961,7 +961,7 @@ def is_minimal(self, f, assume_equivalence_irreducible=False): sage: v0 = GaussValuation(R, vp) sage: v1 = v0.augmentation(x, 1/4) sage: v2 = v1.augmentation(x^4 + 2, 5/4) - sage: v2.is_minimal(x^5 + x^4 + 2) # long time + sage: v2.is_minimal(x^5 + x^4 + 2) False Polynomials which are equivalent to the key polynomial are minimal if @@ -1218,10 +1218,10 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi sage: vp=vp.extension(K) sage: v0=GaussValuation(R,vp) sage: G=x^36 + 36*x^35 + 630*x^34 + 7144*x^33 + 59055*x^32 + 379688*x^31 +1978792*x^30 + 8604440*x^29 + 31895428*x^28 + 102487784*x^27 + 289310720*x^26 + 725361352*x^25 + 1629938380*x^24 + 3307417800*x^23 + 6098786184*x^22+10273444280*x^21 + 15878121214*x^20 + 22596599536*x^19 + 29695703772*x^18 +36117601976*x^17 + 40722105266*x^16 + 42608585080*x^15 + 41395961848*x^14 +37344435656*x^13 + 31267160756*x^12 + 24271543640*x^11 + 17439809008*x^10 + 11571651608*x^9 + 7066815164*x^8 + 3953912472*x^7 + 2013737432*x^6 + 925014888*x^5 + 378067657*x^4 + 134716588*x^3 + 40441790*x^2 + 9532544*x + 1584151 - sage: v1=v0.mac_lane_step(G)[0] # long time - sage: V=v1.mac_lane_step(G) # long time - sage: v2=V[0] # long time - sage: v2.equivalence_decomposition(G) # long time + sage: v1=v0.mac_lane_step(G)[0] + sage: V=v1.mac_lane_step(G) + sage: v2=V[0] + sage: v2.equivalence_decomposition(G) (1/387420489) * (x^4 + 2*x^2 + alpha^4 + alpha^3 + 1)^3 * (x^4 + 2*x^2 + 1/2*alpha^4 + alpha^3 + 5*alpha + 1)^3 * (x^4 + 2*x^2 + 3/2*alpha^4 + alpha^3 + 5*alpha + 1)^3 REFERENCES: diff --git a/limit_valuation.py b/limit_valuation.py index 27267b24ef1..fec087f9f9c 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -32,7 +32,7 @@ sage: L. = K.extension(y^2 - x) sage: v = FunctionFieldValuation(K, 1) - sage: w = v.extensions(L); w # long time + sage: w = v.extensions(L); w [[ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation, [ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation] @@ -685,7 +685,7 @@ def _weakly_separating_element(self, other): sage: v = pAdicValuation(QQ, 2) sage: w = v.extension(L) sage: v = pAdicValuation(QQ, 5) - sage: u,uu = v.extensions(L) # long time + sage: u,uu = v.extensions(L) sage: w._base_valuation._weakly_separating_element(u._base_valuation) # long time 2 sage: u._base_valuation._weakly_separating_element(uu._base_valuation) # long time @@ -695,7 +695,7 @@ def _weakly_separating_element(self, other): sage: v = FunctionFieldValuation(K, 1/x) sage: R. = K[] sage: L. = K.extension(y^2 - 1/(x^2 + 1)) - sage: u,uu = v.extensions(L) # long time + sage: u,uu = v.extensions(L) sage: v = FunctionFieldValuation(K, x) sage: w,ww = v.extensions(L) sage: v = FunctionFieldValuation(K, 1) @@ -740,8 +740,8 @@ def value_semigroup(self): sage: R. = K[] sage: L. = K.extension(t^2 + 1) sage: v = pAdicValuation(QQ, 5) - sage: u,uu = v.extensions(L) # long time - sage: u.value_semigroup() # long time + sage: u,uu = v.extensions(L) + sage: u.value_semigroup() Additive Abelian Semigroup generated by -1, 1 """ diff --git a/valuation.py b/valuation.py index cebbb2063ed..d3132b1fde1 100644 --- a/valuation.py +++ b/valuation.py @@ -503,7 +503,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: v0 = GaussValuation(K._ring,pAdicValuation(QQ,3)) sage: v1 = v0.augmentation(K._ring.gen(),1/3) sage: mu0 = FunctionFieldValuation(K, v1) - sage: sorted(mu0.mac_lane_approximants(F), key=str) # long time + sage: sorted(mu0.mac_lane_approximants(F), key=str) [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + 2*x) = 2/3 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + x) = 2/3 ]] @@ -528,7 +528,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: G = x^4 + 2*x^3 + 2*x^2 - 2*x + 2 sage: v.mac_lane_approximants(G) [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = 1/4 ]] - sage: v.mac_lane_approximants(G, required_precision=infinity) # long time + sage: v.mac_lane_approximants(G, required_precision=infinity) [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = 1/4, v((1 + O(2^10))*x^4 + (2 + O(2^11))*x^3 + (2 + O(2^11))*x^2 + (2 + 2^2 + 2^3 + 2^4 + 2^5 + 2^6 + 2^7 + 2^8 + 2^9 + 2^10 + O(2^11))*x + (2 + O(2^11))) = +Infinity ]] The factorization of primes in the Gaussian integers can be read off @@ -548,7 +548,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: v0.mac_lane_approximants(G) [[ Gauss valuation induced by 5-adic valuation, v(x + 2) = 1 ], [ Gauss valuation induced by 5-adic valuation, v(x + 3) = 1 ]] - sage: sorted(v0.mac_lane_approximants(G, required_precision = 10), key=str) # long time + sage: sorted(v0.mac_lane_approximants(G, required_precision = 10), key=str) [[ Gauss valuation induced by 5-adic valuation, v(x + 3626068) = 10 ], [ Gauss valuation induced by 5-adic valuation, v(x + 6139557) = 10 ]] @@ -563,7 +563,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: v1,v2 = v.mac_lane_approximants(G); sorted([v1,v2], key=str) [[ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + O(5^4))) = 1 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + O(5^4))) = 1 ]] - sage: w1, w2 = v.mac_lane_approximants(G, required_precision = 2); sorted([w1,w2], key=str) # long time + sage: w1, w2 = v.mac_lane_approximants(G, required_precision = 2); sorted([w1,w2], key=str) [[ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + O(5^4))) = 2 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + 3*5 + O(5^4))) = 2 ]] @@ -571,12 +571,12 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: v1.phi() * v2.phi() - G # optional: integrated (5 + O(5^4))*x + 5 + O(5^4) - sage: w1.phi() * w2.phi() - G # optional: integrated, long time + sage: w1.phi() * w2.phi() - G # optional: integrated (5^3 + O(5^4))*x + 5^3 + O(5^4) In this example, the process stops with a factorization of `x^2 + 1`:: - sage: sorted(v.mac_lane_approximants(G, required_precision=infinity), key=str) # long time + sage: sorted(v.mac_lane_approximants(G, required_precision=infinity), key=str) [[ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + 2*5^2 + 5^3 + O(5^4))) = +Infinity ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + 3*5 + 2*5^2 + 3*5^3 + O(5^4))) = +Infinity ]] @@ -588,7 +588,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: G = x^2 + 1 sage: v.mac_lane_approximants(G) [[ Gauss valuation induced by 5-adic valuation, v(x + 2) = 1 ], [ Gauss valuation induced by 5-adic valuation, v(x + 3) = 1 ]] - sage: sorted(v.mac_lane_approximants(G, required_precision=5), key=str) # long time + sage: sorted(v.mac_lane_approximants(G, required_precision=5), key=str) [[ Gauss valuation induced by 5-adic valuation, v(x + 1068) = 6 ], [ Gauss valuation induced by 5-adic valuation, v(x + 2057) = 5 ]] @@ -601,7 +601,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: alpha = T^3/4 sage: G = 3^3*T^3*(alpha^4 - alpha)^2 - (4*alpha^3 - 1)^3 sage: G = G/G.leading_coefficient() - sage: pAdicValuation(K).mac_lane_approximants(G) # optional: integrated, long time + sage: pAdicValuation(K).mac_lane_approximants(G) # optional: integrated [[ Gauss valuation induced by 3-adic valuation, v((1 + O(3^20))*T + (2 + O(3^20))) = 1/9, v((1 + O(3^20))*T^9 + (2*3 + 2*3^2 + O(3^21))*T^8 + (3 + 3^5 + O(3^21))*T^7 + (2*3 + 2*3^2 + 3^3 + 2*3^4 + 2*3^5 + 3^6 + O(3^21))*T^6 + (2*3 + 2*3^2 + 3^4 + 3^6 + 2*3^7 + O(3^21))*T^5 + (3 + 3^2 + 3^3 + 2*3^6 + 2*3^7 + 3^8 + O(3^21))*T^4 + (2*3 + 2*3^2 + 3^3 + 2*3^5 + 2*3^6 + 2*3^7 + 2*3^8 + O(3^21))*T^3 + (2*3 + 2*3^2 + 3^3 + 2*3^4 + 3^5 + 2*3^6 + 2*3^7 + 2*3^8 + O(3^21))*T^2 + (3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^7 + 3^8 + O(3^21))*T + (2 + 2*3 + 2*3^2 + 2*3^4 + 2*3^5 + 3^7 + O(3^20))) = 55/27 ]] A similar example:: @@ -887,7 +887,7 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No sage: v = pAdicValuation(k) sage: R.=k[] sage: G = x^2 + 1 - sage: v.montes_factorization(G) # long time + sage: v.montes_factorization(G) ((1 + O(5^4))*x + (2 + 5 + 2*5^2 + 5^3 + O(5^4))) * ((1 + O(5^4))*x + (3 + 3*5 + 2*5^2 + 3*5^3 + O(5^4))) The computation might not terminate over incomplete fields (in From c68548f1646b906265cccd34e21084929670927f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 24 Jan 2017 18:14:15 -0500 Subject: [PATCH 216/740] fixed typos --- valuation_space.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/valuation_space.py b/valuation_space.py index 4c221509223..835adbcc981 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -985,11 +985,11 @@ def _relative_size(self, x): The number returned is an estimate on the factor between the number of Bits used by ``x`` and the minimal number of bits used by an element - Congruent to ``x``. + congruent to ``x``. This is used by :meth:`simplify` to decide whether simplification of - Coefficients is going to lead to a significant shrinking of the - Coefficients of ``x``. + coefficients is going to lead to a significant shrinking of the + coefficients of ``x``. EXAMPLES:: From 4498bafd5c2d05f4717c8df57b5fdbf893ee07ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 14 Feb 2017 00:03:10 -0500 Subject: [PATCH 217/740] Improve error message for augmented valuation --- augmented_valuation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 3c2bcc2ee14..17ac9512539 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -413,11 +413,11 @@ def element_with_valuation(self, s): sage: w.element_with_valuation(1/3) Traceback (most recent call last): ... - ValueError: s must be in the value group of the valuation + ValueError: s must be in the value group of the valuation but 1/3 is not in Additive Abelian Group generated by 1/2. """ if s not in self.value_group(): - raise ValueError("s must be in the value group of the valuation") + raise ValueError("s must be in the value group of the valuation but %r is not in %r."%(s, self.value_group())) error = s ret = self.domain().one() From 5ab8ada43ce77a1b74faa33dbdd23283bc932510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 14 Feb 2017 00:26:38 -0500 Subject: [PATCH 218/740] Add framework to support pseudo valuations on function fields --- __init__.py | 2 +- function_field_valuation.py | 88 +++++++++++++++++++++++++++++++------ 2 files changed, 75 insertions(+), 15 deletions(-) diff --git a/__init__.py b/__init__.py index ff282e0a1f0..80371fe9a9e 100644 --- a/__init__.py +++ b/__init__.py @@ -39,7 +39,7 @@ # local file and the instances that come from the mac_lane import define # different types) from trivial_valuation import TrivialDiscreteValuation, TrivialDiscretePseudoValuation -from function_field_valuation import FunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldFromLimitValuation, InfiniteRationalFunctionFieldValuation, FiniteRationalFunctionFieldValuation, NonClassicalRationalFunctionFieldValuation, FunctionFieldMappedValuation_base, FunctionFieldExtensionMappedValuation, RationalFunctionFieldMappedValuation +from function_field_valuation import FunctionFieldValuation_base, DiscreteFunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldFromLimitValuation, InfiniteRationalFunctionFieldValuation, FiniteRationalFunctionFieldValuation, NonClassicalRationalFunctionFieldValuation, InfiniteRationalFunctionFieldValuation, FunctionFieldMappedValuation_base, FunctionFieldExtensionMappedValuation, RationalFunctionFieldMappedValuation from limit_valuation import LimitValuation, MacLaneLimitValuation, LimitValuation_generic from mapped_valuation import MappedValuation_base, FiniteExtensionFromLimitValuation, FiniteExtensionFromInfiniteValuation, MappedValuation_base from augmented_valuation import FiniteAugmentedValuation, InfiniteAugmentedValuation diff --git a/function_field_valuation.py b/function_field_valuation.py index 7b083821ac6..81d6529a984 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -140,7 +140,7 @@ from sage.rings.all import QQ, ZZ, infinity from sage.misc.abstract_method import abstract_method -from valuation import DiscreteValuation +from valuation import DiscreteValuation, DiscretePseudoValuation, InfiniteDiscretePseudoValuation from trivial_valuation import TrivialValuation from mapped_valuation import FiniteExtensionFromLimitValuation, MappedValuation_base @@ -486,7 +486,14 @@ def create_object(self, version, key, **extra_args): if valuation == FunctionFieldValuation(valuation.domain(), valuation.domain().gen()): # the classical valuation at the place 1/x return parent.__make_element_class__(InfiniteRationalFunctionFieldValuation)(parent) - return parent.__make_element_class__(RationalFunctionFieldMappedValuation)(parent, valuation, to_valuation_domain, from_valuation_domain) + + from sage.structure.dynamic_class import dynamic_class + clazz = RationalFunctionFieldMappedValuation + if valuation.is_discrete_valuation(): + clazz = dynamic_class("RationalFunctionFieldMappedValuation_discrete", (clazz, DiscreteValuation)) + else: + clazz = dynamic_class("RationalFunctionFieldMappedValuation_infinite", (clazz, InfiniteDiscretePseudoValuation)) + return parent.__make_element_class__(clazz)(parent, valuation, to_valuation_domain, from_valuation_domain) return parent.__make_element_class__(FunctionFieldExtensionMappedValuation)(parent, valuation, to_valuation_domain, from_valuation_domain) if domain is valuation.domain(): @@ -500,7 +507,13 @@ def create_object(self, version, key, **extra_args): # valuation corresponds to a finite place return parent.__make_element_class__(FiniteRationalFunctionFieldValuation)(parent, valuation) else: - return parent.__make_element_class__(NonClassicalRationalFunctionFieldValuation)(parent, valuation) + from sage.structure.dynamic_class import dynamic_class + clazz = NonClassicalRationalFunctionFieldValuation + if valuation.is_discrete_valuation(): + clazz = dynamic_class("NonClassicalRationalFunctionFieldValuation_discrete", (clazz, DiscreteValuation)) + else: + clazz = dynamic_class("NonClassicalRationalFunctionFieldValuation_infinite", (clazz, InfiniteDiscretePseudoValuation)) + return parent.__make_element_class__(clazz)(parent, valuation) else: # valuation is a limit valuation that singles out an extension return parent.__make_element_class__(FunctionFieldFromLimitValuation)(parent, valuation, domain.polynomial(), extra_args['approximants']) @@ -509,9 +522,11 @@ def create_object(self, version, key, **extra_args): FunctionFieldValuation = FunctionFieldValuationFactory("FunctionFieldValuation") -class FunctionFieldValuation_base(DiscreteValuation): + +class FunctionFieldValuation_base(DiscretePseudoValuation): r""" - Base class for valuations on function fields. + Abstract base class for any discrete (pseudo-)valuation on a function + field. TESTS:: @@ -522,6 +537,21 @@ class FunctionFieldValuation_base(DiscreteValuation): True """ + + +class DiscreteFunctionFieldValuation_base(DiscreteValuation): + r""" + Base class for discrete valuations on function fields. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: v = FunctionFieldValuation(K, x) # indirect doctest + sage: isinstance(v, DiscreteFunctionFieldValuation_base) + True + + """ def extensions(self, L): r""" Return the extensions of this valuation to ``L``. @@ -592,13 +622,13 @@ def extensions(self, L): return reduce(add, A, []) elif L.constant_field() is not K.constant_field() and K.constant_field().is_subring(L): # subclasses should override this method and handle this case, so we never get here - raise NotImplementedError("Can not compute the extensions of %r from %r to %r since the base ring changes."%(self, self.domain(), ring)) + raise NotImplementedError("Can not compute the extensions of %r from %r to %r since the base ring changes."%(self, self.domain(), L)) raise NotImplementedError("extension of %r from %r to %r not implemented"%(self, K, L)) class RationalFunctionFieldValuation_base(FunctionFieldValuation_base): r""" - Base class for discrete valuations on rational function fields. + Base class for valuations on rational function fields. TESTS:: @@ -611,7 +641,7 @@ class RationalFunctionFieldValuation_base(FunctionFieldValuation_base): """ -class ClassicalFunctionFieldValuation_base(FunctionFieldValuation_base): +class ClassicalFunctionFieldValuation_base(DiscreteFunctionFieldValuation_base): r""" Base class for discrete valuations on rational function fields that come from points on the projective line. @@ -664,6 +694,7 @@ def _ge_(self, other): return self == other super(ClassicalFunctionFieldValuation_base, self)._ge_(other) + class InducedFunctionFieldValuation_base(FunctionFieldValuation_base): r""" Base class for function field valuation induced by a valuation on the @@ -884,7 +915,8 @@ def residue_ring(self): """ return self._base_valuation.residue_ring().fraction_field() -class FiniteRationalFunctionFieldValuation(ClassicalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, RationalFunctionFieldValuation_base): + +class FiniteRationalFunctionFieldValuation(InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, RationalFunctionFieldValuation_base): r""" Valuation of a finite place of a function field. @@ -924,8 +956,8 @@ def __init__(self, parent, base_valuation): True """ - ClassicalFunctionFieldValuation_base.__init__(self, parent) InducedFunctionFieldValuation_base.__init__(self, parent, base_valuation) + ClassicalFunctionFieldValuation_base.__init__(self, parent) RationalFunctionFieldValuation_base.__init__(self, parent) @@ -945,20 +977,29 @@ class NonClassicalRationalFunctionFieldValuation(InducedFunctionFieldValuation_b """ def __init__(self, parent, base_valuation): r""" - TESTS:: + TESTS: + + There is some support for discrete pseudo-valuations on rational + function fields in the code. However, since these valuations must send + elments to `-\infty`, they are not supported yet:: sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(QQ['x'], pAdicValuation(QQ, 2)).augmentation(x, infinity) sage: K. = FunctionField(QQ) - sage: v = GaussValuation(QQ['x'], pAdicValuation(QQ, 2)) sage: w = FunctionFieldValuation(K, v) - sage: isinstance(w, NonClassicalRationalFunctionFieldValuation) + Traceback (most recent call last): + ... + ValueError: valuation must be a discrete valuation but [ Gauss valuation induced by 2-adic valuation, v(x) = +Infinity ] is not. + sage: isinstance(w, NonClassicalRationalFunctionFieldValuation) # not tested True """ InducedFunctionFieldValuation_base.__init__(self, parent, base_valuation) RationalFunctionFieldValuation_base.__init__(self, parent) -class FunctionFieldFromLimitValuation(FiniteExtensionFromLimitValuation): + +class FunctionFieldFromLimitValuation(FiniteExtensionFromLimitValuation, DiscreteFunctionFieldValuation_base): r""" A valuation on a finite extensions of function fields `L=K[y]/(G)` where `K` is another function field. @@ -979,6 +1020,23 @@ class FunctionFieldFromLimitValuation(FiniteExtensionFromLimitValuation): True """ + def __init__(self, parent, approximant, G, approximants): + r""" + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - (x^2 + x + 1)) + sage: v = FunctionFieldValuation(K, x - 1) # indirect doctest + sage: w = v.extension(L) + sage: isinstance(w, FunctionFieldFromLimitValuation) + True + + """ + FiniteExtensionFromLimitValuation.__init__(self, parent, approximant, G, approximants) + DiscreteFunctionFieldValuation_base.__init__(self, parent) + def _to_base_domain(self, f): r""" Return ``f`` as an element of the domain of the underlying limit valuation. @@ -1125,6 +1183,7 @@ def _repr_(self): to_base = self._to_base._repr_defn().replace('\n', ', ') return "%r (in %r after %s)"%(self._base_valuation, self._base_valuation.domain(), to_base) + class RationalFunctionFieldMappedValuation(FunctionFieldMappedValuation_base, RationalFunctionFieldValuation_base): r""" Valuation on a rational function field that is implemented after a map to @@ -1158,6 +1217,7 @@ def __init__(self, parent, base_valuation, to_base_valuation_doain, from_base_va FunctionFieldMappedValuation_base.__init__(self, parent, base_valuation, to_base_valuation_doain, from_base_valuation_domain) RationalFunctionFieldValuation_base.__init__(self, parent) + class InfiniteRationalFunctionFieldValuation(FunctionFieldMappedValuation_base, RationalFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base): r""" Valuation of the infinite place of a function field. From b511a79f8b1c62d2378164441daba4b6a8ab96f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 14 Feb 2017 00:27:12 -0500 Subject: [PATCH 219/740] Clarify that a valaution must uniquely single out a limit --- limit_valuation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/limit_valuation.py b/limit_valuation.py index fec087f9f9c..9468b087722 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -95,8 +95,8 @@ class LimitValuationFactory(UniqueFactory): INPUT: - ``base_valuation`` -- a discrete (pseudo-)valuation on a polynomial ring - which can be augmented (possibly only in the limit) to a pseudo-valuation - that sends ``G`` to infinity. + which can be unqiuely augmented (possibly only in the limit) to a + pseudo-valuation that sends ``G`` to infinity. - ``G`` -- a squarefree polynomial in the domain of ``base_valuation``. From cf005bb1de95990a654cd28bbf67af96433f7751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 14 Feb 2017 13:21:56 -0500 Subject: [PATCH 220/740] MacLaneLimitValuation now detects elements with infinite valuation ``` sage: K = QQ sage: R. = K[] sage: vK = pAdicValuation(K, 2) sage: f = x^2 + 7 sage: V = vK.mac_lane_approximants(f) sage: v = LimitValuation(V[0], f) sage: v(f) +Infinity ``` --- limit_valuation.py | 68 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/limit_valuation.py b/limit_valuation.py index 9468b087722..c4eb3864c69 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -470,6 +470,39 @@ def uniformizer(self): """ return self._initial_approximation.uniformizer() + def _call_(self, f): + r""" + Return the valuation of ``f``. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: vK = pAdicValuation(K, 2) + sage: f = (x^2 + 7) * (x^2 + 9) + sage: V = vK.mac_lane_approximants(f, require_incomparability=True) + sage: V = sorted(V, key=str) + + sage: w = LimitValuation(V[0], f) + sage: w((x^2 + 7) * (x + 3)) + 3/2 + + sage: w = LimitValuation(V[1], f) + sage: w((x^2 + 7) * (x + 3)) + +Infinity + + sage: w = LimitValuation(V[2], f) + sage: w((x^2 + 7) * (x + 3)) + +Infinity + + """ + self._improve_approximation_for_call(f) + if self._G.divides(f): + from sage.rings.all import infinity + return infinity + return self._approximation(f) + def _improve_approximation(self): r""" Perform one step of the Mac Lane algorithm to improve our approximation. @@ -539,18 +572,24 @@ def _improve_approximation_for_call(self, f): of ``f`` in `K[x]` (of minimal degree.) Write `v` for ``self._approximation` and `\phi` for the last key polynomial of `v`. With repeated quotient and remainder `g` has a unique - expansion as `g=\sum a_i\phi^i`. Suppose that `g` is an + expansion as `g=\sum a_i\phi^i`. Suppose that `g` is an equivalence-unit with respect to ``self._approximation``, i.e., `v(a_0) < v(a_i\phi^i)` for all `i\ne 0`. If we denote the limit valuation as `w`, then `v(a_i\phi^i)=w(a_i\phi^i)` since the valuation of key polynomials does not change during augmentations (Theorem 6.4 in [ML1936'].) By the strict triangle inequality, `w(g)=v(g)`. - Note that any `g` which is not in `(G)` is an equivalence-unit + Note that any `g` which is coprime to `G` is an equivalence-unit after finitely many steps of the Mac Lane algorithm. Indeed, otherwise the valuation of `g` would be infinite (follows from Theorem 5.1 in [ML1936']) since the valuation of the key polynomials increases. + When `f` is not coprime to `G`, consider `s=gcd(f,G)` and write + `G=st`. Since `G` is squarefree, either `s` or `t` have finite + valuation. With the above algorithm, this can be decided in + finitely many steps. From this we can deduce the valuation of `s` + (and in fact replace `G` with the factor with infinite valuation + for all future computations.) """ from sage.rings.all import infinity @@ -564,12 +603,25 @@ def _improve_approximation_for_call(self, f): # zero coefficients.) return - if self._approximation.is_equivalence_unit(f): - # see ALGORITHM above - return - - self._improve_approximation() - return self._improve_approximation_for_call(f) + while not self._approximation.is_equivalence_unit(f): + # TODO: I doubt that this really works over inexact fields + s = self._G.gcd(f) + if s.is_constant(): + self._improve_approximation() + else: + t = self._G // s + + while True: + if self._approximation.is_equivalence_unit(s): + # t has infinite valuation + self._G = t + return self._improve_approximation_for_call(f // s) + if self._approximation.is_equivalence_unit(t): + # s has infinite valuation + self._G = s + return + + self._improve_approximation() def _improve_approximation_for_reduce(self, f): r""" From 03e45e64a176988a6e9d506096c2706fd4a04679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 16 Feb 2017 17:06:01 -0500 Subject: [PATCH 221/740] Implement comparison for limit valuations --- limit_valuation.py | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/limit_valuation.py b/limit_valuation.py index c4eb3864c69..39a01f9b4b3 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -688,17 +688,41 @@ def _ge_(self, other): EXAMPLES:: sage: from mac_lane import * # optional: standalone - sage: K = QQ - sage: R. = K[] - sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) - sage: w = v.extension(L) - sage: w >= w + sage: R. = QQ[] + sage: F = (x^2 + 7) * (x^2 + 9) + sage: G = (x^2 + 7) + sage: V = pAdicValuation(QQ, 2).mac_lane_approximants(F, require_incomparability=True) + sage: V = sorted(V, key=str) + sage: LimitValuation(V[0], F) >= LimitValuation(V[1], F) + False + sage: LimitValuation(V[1], F) >= LimitValuation(V[1], G) + True + sage: LimitValuation(V[2], F) >= LimitValuation(V[2], G) True """ if other.is_trivial(): return other.is_discrete_valuation() + if isinstance(other, MacLaneLimitValuation): + if self._approximation.restriction(self._approximation.domain().base_ring()) == other._approximation.restriction(other._approximation.domain().base_ring()): + # Two MacLane limit valuations v,w over the same constant + # valuation are either equal or incomparable; neither v>w nor + # v= other._initial_approximation or self._initial_approximation <= other._initial_approximation + return super(MacLaneLimitValuation, self)._ge_(other) def restriction(self, ring): From d81e98c18c6eca79417181e7cead5177b87b6e6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 25 Feb 2017 00:12:16 -0500 Subject: [PATCH 222/740] Add support for valuations which attain -infty The interesting use case for this are pseudo-valuations on rational function fields which are induced by pseudo-valuations on polynomial rings. Originally, there were no plans to support valuations like that so they likely do not play well with the existing code yet or at least the error messages raised are not very helpful. --- augmented_valuation.py | 19 ++++++++ function_field_valuation.py | 27 +++++++---- limit_valuation.py | 30 ++++++++++-- trivial_valuation.py | 14 ++++++ valuation.py | 37 ++++++++++++++ valuation_space.py | 97 +++++++++++++++++++++++++++++-------- value_group.py | 10 ++-- 7 files changed, 196 insertions(+), 38 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 17ac9512539..4d20f226fcb 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -812,6 +812,25 @@ def _relative_size(self, f): """ return self._base_valuation._relative_size(f) + def is_negative_pseudo_valuation(self): + r""" + Return whether this valuation attains `-\infty`. + + EXAMPLES: + + No element in the domain of an augmented valuation can have valuation + `-\infty`, so this method always returns ``False``:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: w = v.augmentation(x, infinity) + sage: w.is_negative_pseudo_valuation() + False + + """ + return False + class FinalAugmentedValuation(AugmentedValuation_base, FinalInductiveValuation): r""" diff --git a/function_field_valuation.py b/function_field_valuation.py index 81d6529a984..0cf9c66ad9e 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -116,6 +116,15 @@ sage: w = v.extension(L) sage: TestSuite(w).run() # long time +Run test suite for a valuation which sends an element to `-\infty`:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(QQ['x'], pAdicValuation(QQ, 2)).augmentation(x, infinity) + sage: K. = FunctionField(QQ) + sage: w = FunctionFieldValuation(K, v) + sage: TestSuite(w).run() # long time + AUTHORS: - Julian Rüth (2016-10-16): initial version @@ -140,7 +149,7 @@ from sage.rings.all import QQ, ZZ, infinity from sage.misc.abstract_method import abstract_method -from valuation import DiscreteValuation, DiscretePseudoValuation, InfiniteDiscretePseudoValuation +from valuation import DiscreteValuation, DiscretePseudoValuation, InfiniteDiscretePseudoValuation, NegativeInfiniteDiscretePseudoValuation from trivial_valuation import TrivialValuation from mapped_valuation import FiniteExtensionFromLimitValuation, MappedValuation_base @@ -397,9 +406,10 @@ def create_key_and_extra_args_from_valuation(self, domain, valuation): return (domain, approximant), {'approximants': approximants} else: # on a rational function field K(x), any valuation on K[x] that - # is not only a pseudo-valuation extends to a valuation on K(x) - if not valuation.is_discrete_valuation(): - raise ValueError("valuation must be a discrete valuation but %r is not."%(valuation,)) + # does not have an element with valuation -infty extends to a + # pseudo-valuation on K(x) + if valuation.is_negative_pseudo_valuation(): + raise ValueError("there must not be an element of valuation -Infinity in the domain of valuation"%(valuation,)) return (domain, valuation), {} if valuation.domain().is_subring(domain.base_field()): @@ -503,7 +513,7 @@ def create_object(self, version, key, **extra_args): if domain.base_field() is domain: # valuation is a base valuation on K[x] that induces a valuation on K(x) - if valuation.restriction(domain.constant_base_field()).is_trivial(): + if valuation.restriction(domain.constant_base_field()).is_trivial() and valuation.is_discrete_valuation(): # valuation corresponds to a finite place return parent.__make_element_class__(FiniteRationalFunctionFieldValuation)(parent, valuation) else: @@ -512,7 +522,7 @@ def create_object(self, version, key, **extra_args): if valuation.is_discrete_valuation(): clazz = dynamic_class("NonClassicalRationalFunctionFieldValuation_discrete", (clazz, DiscreteValuation)) else: - clazz = dynamic_class("NonClassicalRationalFunctionFieldValuation_infinite", (clazz, InfiniteDiscretePseudoValuation)) + clazz = dynamic_class("NonClassicalRationalFunctionFieldValuation_negative_infinite", (clazz, NegativeInfiniteDiscretePseudoValuation)) return parent.__make_element_class__(clazz)(parent, valuation) else: # valuation is a limit valuation that singles out an extension @@ -988,10 +998,7 @@ def __init__(self, parent, base_valuation): sage: v = GaussValuation(QQ['x'], pAdicValuation(QQ, 2)).augmentation(x, infinity) sage: K. = FunctionField(QQ) sage: w = FunctionFieldValuation(K, v) - Traceback (most recent call last): - ... - ValueError: valuation must be a discrete valuation but [ Gauss valuation induced by 2-adic valuation, v(x) = +Infinity ] is not. - sage: isinstance(w, NonClassicalRationalFunctionFieldValuation) # not tested + sage: isinstance(w, NonClassicalRationalFunctionFieldValuation) True """ diff --git a/limit_valuation.py b/limit_valuation.py index 39a01f9b4b3..f46495d98aa 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -69,7 +69,7 @@ """ #***************************************************************************** -# Copyright (C) 2016 Julian Rüth +# Copyright (C) 2016-2017 Julian Rüth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -95,8 +95,9 @@ class LimitValuationFactory(UniqueFactory): INPUT: - ``base_valuation`` -- a discrete (pseudo-)valuation on a polynomial ring - which can be unqiuely augmented (possibly only in the limit) to a - pseudo-valuation that sends ``G`` to infinity. + which is a discrete valuation on the coefficient ring which can be + unqiuely augmented (possibly only in the limit) to a pseudo-valuation + that sends ``G`` to infinity. - ``G`` -- a squarefree polynomial in the domain of ``base_valuation``. @@ -133,6 +134,8 @@ def create_key(self, base_valuation, G): parameters are normalized already. """ + if not base_valuation.restriction(G.parent().base_ring()).is_discrete_valuation(): + raise ValueError("base_valuation must be discrete on the coefficient ring.") return base_valuation, G def create_object(self, version, key): @@ -946,3 +949,24 @@ def upper_bound(self, f): f = self.domain().coerce(f) self._improve_approximation_for_call(f) return self._approximation.upper_bound(f) + + def is_negative_pseudo_valuation(self): + r""" + Return whether this valuation attains `-\infty`. + + EXAMPLES: + + For a Mac Lane limit valuation, this is never the case, so this + method always returns ``False``:: + + sage: from mac_lane import * # optional: standalone + sage: K = QQ + sage: R. = K[] + sage: L. = K.extension(t^2 + 1) + sage: v = pAdicValuation(QQ, 2) + sage: u = v.extension(L) + sage: u.is_negative_pseudo_valuation() + False + + """ + return False diff --git a/trivial_valuation.py b/trivial_valuation.py index defce8ad6d3..529886d0ddd 100644 --- a/trivial_valuation.py +++ b/trivial_valuation.py @@ -154,6 +154,20 @@ def is_trivial(self): """ return True + def is_negative_pseudo_valuation(self): + r""" + Return whether this valuatios attains the value `-\infty`. + + EXAMPLES: + + sage: from mac_lane import * # optional: standalone + sage: v = TrivialPseudoValuation(QQ) + sage: v.is_negative_pseudo_valuation() + False + + """ + return False + class TrivialDiscretePseudoValuation(TrivialDiscretePseudoValuation_base, InfiniteDiscretePseudoValuation): r""" The trivial pseudo-valuation that is `\infty` everywhere. diff --git a/valuation.py b/valuation.py index d3132b1fde1..3d05431d902 100644 --- a/valuation.py +++ b/valuation.py @@ -361,6 +361,43 @@ def is_discrete_valuation(self): """ return False +class NegativeInfiniteDiscretePseudoValuation(InfiniteDiscretePseudoValuation): + r""" + Abstract base class for pseudo-valuations which attain the value `\infty` + and `-\infty`, i.e., whose domain contains an element of valuation `\infty` + and its inverse. + + EXAMPLES: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)).augmentation(x, infinity) + sage: K. = FunctionField(QQ) + sage: w = FunctionFieldValuation(K, v) + + TESTS:: + + sage: TestSuite(w).run() # long time + + """ + def is_negative_pseudo_valuation(self): + r""" + Return whether this valuation attains the value `-\infty`. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, TrivialValuation(QQ)).augmentation(x, infinity) + sage: K. = FunctionField(QQ) + sage: w = FunctionFieldValuation(K, v) + sage: w.is_negative_pseudo_valuation() + True + + """ + return True + + class DiscreteValuation(DiscretePseudoValuation): r""" Abstract base class for discrete valuations. diff --git a/valuation_space.py b/valuation_space.py index 835adbcc981..e6c75461c47 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -71,6 +71,10 @@ class DiscretePseudoValuationSpace(UniqueRepresentation, Homset): valuations (pseudo or not) as this makes the implementation much easier. + .. TODO:: + + The comparison problem might be fixed by :trac:`22029` or similar. + TESTS:: sage: TestSuite(H).run() # long time @@ -315,6 +319,26 @@ def is_discrete_valuation(self): """ + def is_negative_pseudo_valuation(self): + r""" + Return whether this valuation is a discrete pseudo-valuation that + does attain `-\infty`, i.e., it is non-trivial and its domain + contains an element with valuation `\infty` that has an inverse. + + EXAMPLES:: + + sage: from mac_lane import * # optional: standalone + sage: pAdicValuation(QQ, 2).is_negative_pseudo_valuation() + False + + """ + from sage.categories.all import Fields + if self.is_discrete_valuation(): + return False + elif self.domain() in Fields(): + return True + raise NotImplementedError + @cached_method def is_trivial(self): r""" @@ -506,9 +530,6 @@ def residue_field(self): Rational function field in x over Finite Field of size 2 """ - if not self.is_discrete_valuation(): - raise NotImplementedError - ret = self.residue_ring() from sage.categories.fields import Fields if ret in Fields(): @@ -1008,6 +1029,29 @@ def _relative_size(self, x): """ return 1 + def _test_is_negative_pseudo_valuation(self, **options): + r""" + Check that :meth:`is_negative_pseudo_valuation` works correctly. + + TESTS:: + + sage: from mac_lane import * # optional: standalone + sage: v = pAdicValuation(ZZ, 3) + sage: v._test_is_negative_pseudo_valuation() + + """ + tester = self._tester(**options) + + if self.is_discrete_valuation(): + tester.assertFalse(self.is_negative_pseudo_valuation()) + return + + if not self.is_negative_pseudo_valuation(): + X = self.domain().some_elements() + for x in tester.some_elements(X): + from sage.rings.all import infinity + tester.assertNotEqual(self(x), -infinity) + def _test_bounds(self, **options): r""" Check that :meth:`lower_bound` and :meth:`upper_bound` work @@ -1047,7 +1091,10 @@ def _test_simplify(self, **options): # over non-fields (and especially polynomial rings over # non-fields) computation of the residue ring is often # difficult and not very interesting - has_residue_ring = False + from sage.categories.fields import Fields + if self.domain() not in Fields(): + return + raise X = self.domain().some_elements() for x in tester.some_elements(X): @@ -1208,6 +1255,8 @@ def _test_no_infinite_units(self, **options): """ if not self.is_discrete_valuation() and self.is_trivial(): return + if self.is_negative_pseudo_valuation(): + return from sage.rings.all import infinity tester = self._tester(**options) @@ -1238,7 +1287,7 @@ def _test_value_group(self, **options): # check that all valuations are in the value group for x in tester.some_elements(self.domain().some_elements()): - if self(x) is not infinity: + if self(x) is not infinity and self(x) is not -infinity: tester.assertIn(self(x), self.value_group()) if not self.is_trivial(): @@ -1305,9 +1354,9 @@ def _test_residue_ring(self, **options): # non-fields) computation of the residue ring is often # difficult and not very interesting from sage.categories.fields import Fields - if self.domain() in Fields(): - raise - return + if self.domain() not in Fields(): + return + raise if r.zero() == r.one(): # residue ring is the zero rng @@ -1338,9 +1387,9 @@ def _test_reduce(self, **options): # non-fields) computation of the residue ring is often # difficult and not very interesting from sage.categories.fields import Fields - if self.domain() in Fields(): - raise - return + if self.domain() not in Fields(): + return + raise for x in tester.some_elements(self.domain().some_elements()): if self(x) < 0: @@ -1378,9 +1427,9 @@ def _test_lift(self, **options): # non-fields) computation of the residue ring is often # difficult and not very interesting from sage.categories.fields import Fields - if self.domain() in Fields(): - raise - return + if self.domain() not in Fields(): + return + raise for X in tester.some_elements(self.residue_ring().some_elements()): x = self.lift(X) @@ -1479,12 +1528,12 @@ def _test_residue_field(self, **options): return except NotImplementedError: # over non-fields (and especially polynomial rings over - # non-fields) computation of the residue field is often + # non-fields) computation of the residue ring is often # difficult and not very interesting from sage.categories.fields import Fields - if self.domain() in Fields(): - raise - return + if self.domain() not in Fields(): + return + raise c = self.residue_field().characteristic() if c != 0: @@ -1503,8 +1552,12 @@ def _test_ge(self, **options): """ tester = self._tester(**options) - from trivial_valuation import TrivialPseudoValuation, TrivialValuation tester.assertGreaterEqual(self, self) + + if self.is_negative_pseudo_valuation(): + return + + from trivial_valuation import TrivialPseudoValuation, TrivialValuation tester.assertGreaterEqual(self, TrivialValuation(self.domain())) tester.assertLessEqual(self, TrivialPseudoValuation(self.domain())) @@ -1521,8 +1574,12 @@ def _test_le(self, **options): """ tester = self._tester(**options) - from trivial_valuation import TrivialPseudoValuation, TrivialValuation tester.assertGreaterEqual(self, self) + + if self.is_negative_pseudo_valuation(): + return + + from trivial_valuation import TrivialPseudoValuation, TrivialValuation tester.assertLessEqual(TrivialValuation(self.domain()), self) tester.assertGreaterEqual(TrivialPseudoValuation(self.domain()), self) diff --git a/value_group.py b/value_group.py index d17e4ce704e..f5f4e66150b 100644 --- a/value_group.py +++ b/value_group.py @@ -41,7 +41,7 @@ class DiscreteValuationCodomain(UniqueRepresentation, Parent): r""" The codomain of discrete valuations, the rational numbers extended by - `\infty`. + `\pm\infty`. EXAMPLES:: @@ -66,7 +66,7 @@ def __init__(self): from sage.sets.finite_enumerated_set import FiniteEnumeratedSet from sage.categories.additive_monoids import AdditiveMonoids UniqueRepresentation.__init__(self) - Parent.__init__(self, facade=(QQ, FiniteEnumeratedSet([infinity])), category=AdditiveMonoids()) + Parent.__init__(self, facade=(QQ, FiniteEnumeratedSet([infinity, -infinity])), category=AdditiveMonoids()) def _element_constructor_(self, x): r""" @@ -84,13 +84,13 @@ def _element_constructor_(self, x): sage: DiscreteValuationCodomain()(infinity) +Infinity sage: DiscreteValuationCodomain()(-infinity) - Traceback (most recent call last): - ... - ValueError: must be a rational number or infinity + -Infinity """ if x is infinity: return x + if x is -infinity: + return x if x not in QQ: raise ValueError("must be a rational number or infinity") return QQ.coerce(x) From 07f5c9f1254d891ce63db23c47050ce6754558d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 15 Apr 2017 12:30:51 +0200 Subject: [PATCH 223/740] Made the doctests work in Sage 8.0 the rules for inclusion of packages has changed it seems --- README.md | 4 +- __init__.py | 27 ++++++++ augmented_valuation.py | 125 ++++++++++++++++----------------- developing_valuation.py | 29 +++----- function_field_valuation.py | 101 +++++++++++++-------------- gauss_valuation.py | 71 +++++++++---------- inductive_valuation.py | 84 +++++++++++------------ limit_valuation.py | 71 +++++++++---------- mapped_valuation.py | 59 +++++++--------- padic_valuation.py | 94 ++++++++++++------------- scaled_valuation.py | 45 ++++++------ trivial_valuation.py | 59 ++++++++-------- valuation.py | 51 ++++++-------- valuation_space.py | 133 +++++++++++++++++------------------- value_group.py | 66 ++++++++---------- 15 files changed, 483 insertions(+), 536 deletions(-) diff --git a/README.md b/README.md index 92c2b82e0af..f74f903dfdb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Mac Lane infrastructure for discrete (pseudo-)valuations which should work on an unmodified Sage 7.4 or later. +Mac Lane infrastructure for discrete (pseudo-)valuations which should work on an unmodified Sage 8.0 or later. ``` $ sage @@ -6,3 +6,5 @@ sage: from mac_lane import * sage: pAdicValuation(QQ, 2) 2-adic valuation ``` + +To run the included tests, execute `sage -tp --optional=sage,standalone mac_lane/`. diff --git a/__init__.py b/__init__.py index 80371fe9a9e..e63939a904c 100644 --- a/__init__.py +++ b/__init__.py @@ -64,6 +64,7 @@ def to_polynomial(self, x): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: K(x) in K._ring # indirect doctest True @@ -79,6 +80,7 @@ def to_constant(self, x): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: K(1) in QQ # indirect doctest True @@ -97,6 +99,7 @@ def __init__(self, *args, **kwargs): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: K(1/2) in QQ True @@ -119,6 +122,7 @@ def __init__(self, *args, **kwargs): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: NP = sage.geometry.newton_polygon.NewtonPolygon([(0,1),(1,0),(2,1)]) sage: NP.principal_part() Infinite Newton polygon with 2 vertices: (0, 1), (1, 0) ending by an infinite line of slope 0 @@ -136,6 +140,7 @@ def is_injective(self): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: K. = FunctionField(QQ) sage: R.fraction_field().is_subring(K) # indirect doctest @@ -151,6 +156,7 @@ def _coerce_map_from_(target, source): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: L. = FunctionField(GaussianIntegers().fraction_field()) sage: L.has_coerce_map_from(K) @@ -223,6 +229,7 @@ def patch_is_injective(method, patch_map): TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: QQ.coerce_map_from(ZZ).is_injective() # indirect doctest True @@ -240,6 +247,7 @@ def is_injective(self): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: QQ.coerce_map_from(ZZ).is_injective() # indirect doctest True @@ -258,6 +266,7 @@ def is_injective(self): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: QQ['x'].coerce_map_from(ZZ['x']).is_injective() # indirect doctest True @@ -282,6 +291,7 @@ def is_injective(self): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: GaussianIntegers().fraction_field().coerce_map_from(QQ).is_injective() True @@ -292,6 +302,7 @@ def is_injective(self): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: GaussianIntegers().fraction_field().coerce_map_from(ZZ).is_injective() True @@ -305,6 +316,7 @@ def is_injective(self): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: QQ.coerce_map_from(ZZ).is_injective() True @@ -319,6 +331,7 @@ def is_injective(self): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: CyclotomicField(5).maximal_order().coerce_map_from(ZZ).is_injective() True @@ -332,6 +345,7 @@ def _coerce_map_from_patched(self, domain): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: CyclotomicField(5).maximal_order().coerce_map_from(ZZ).is_injective() # indirect doctest True @@ -350,6 +364,7 @@ def is_injective(self): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = ZZ[] sage: S. = QQ[] sage: S.quo(x^2 + 1).coerce_map_from(R.quo(x^2 + 1)).is_injective() @@ -365,6 +380,7 @@ def _coerce_map_from_patched(self, domain): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = ZZ[] sage: S. = QQ[] sage: S.quo(x^2 + 1).coerce_map_from(R.quo(x^2 + 1)).is_injective() # indirect doctest @@ -391,6 +407,7 @@ def is_injective(self): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: R.is_subring(R.fraction_field()) # indirect doctest True @@ -402,6 +419,7 @@ def is_surjective(self): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: R.fraction_field().coerce_map_from(R).is_surjective() False @@ -416,6 +434,7 @@ def inverse_of_unit(self): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = ZZ[] sage: S = R.quo(x^2+x+1) sage: S(1).inverse_of_unit() @@ -435,6 +454,7 @@ def _factor_univariate_polynomial(self, f): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = GF(2) sage: R. = K[] sage: L. = K.extension(x^2 + x + 1) @@ -476,6 +496,7 @@ def absolute_extension(self): EXAMPLES:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: k. = GF(4) sage: R. = k[] sage: l. = k.extension(b^2+b+a); l @@ -551,6 +572,7 @@ def vector_space(self): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = GF(2) sage: R. = K[] sage: L. = K.extension(x^2 + x + 1) @@ -572,6 +594,7 @@ def some_elements(self): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = GaussianIntegers().fraction_field() sage: list(K.some_elements()) [I, 0, 1, 1/2, 2*I, -I, -2, 0, 0] @@ -587,6 +610,7 @@ def some_elements(self): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: list(K.some_elements()) == list(K.some_elements()) True @@ -603,6 +627,7 @@ def some_elements(self): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -620,6 +645,7 @@ def some_elements(self): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: K = R.fraction_field() sage: len(list(K.some_elements())) @@ -637,6 +663,7 @@ def some_elements(self): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = GaussianIntegers() sage: list(R.some_elements()) [I, 0, 1, 2*I, -I, -2, 0, 0] diff --git a/augmented_valuation.py b/augmented_valuation.py index 4d20f226fcb..4f140850a24 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -7,7 +7,7 @@ Starting from a :class:`GaussValuation`, we can create augmented valuations on polynomial rings:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x, 1); w @@ -154,13 +154,6 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** - -# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) -import sys, os -if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: - sys.path.append(os.getcwd()) - sys.path.append(os.path.dirname(os.getcwd())) - from inductive_valuation import _lift_to_maximal_precision from inductive_valuation import FinalInductiveValuation, NonFinalInductiveValuation, FiniteInductiveValuation, InfiniteInductiveValuation, InductiveValuation from valuation import InfiniteDiscretePseudoValuation, DiscreteValuation @@ -178,7 +171,7 @@ class AugmentedValuationFactory(UniqueFactory): This factory is not meant to be called directly. Instead, :meth:`augmentation` of a valuation should be called:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x, 1) # indirect doctest @@ -207,7 +200,7 @@ def create_key(self, base_valuation, phi, mu, check=True): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x, 1) # indirect doctest @@ -242,7 +235,7 @@ def create_object(self, version, key): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) # indirect doctest @@ -279,7 +272,7 @@ class AugmentedValuation_base(InductiveValuation): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = CyclotomicField(5) sage: R. = K[] sage: v = GaussValuation(R, pAdicValuation(K, 2)) @@ -298,7 +291,7 @@ def __init__(self, parent, v, phi, mu): """ TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = Qq(4, 5) sage: R. = K[] sage: v = GaussValuation(R) @@ -334,7 +327,7 @@ def equivalence_unit(self, s, reciprocal=False): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -395,7 +388,7 @@ def element_with_valuation(self, s): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -433,7 +426,7 @@ def _repr_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -454,7 +447,7 @@ def augmentation_chain(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x, 1) @@ -486,7 +479,7 @@ def psi(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -513,7 +506,7 @@ def E(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -539,7 +532,7 @@ def F(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -561,7 +554,7 @@ def extensions(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -602,7 +595,7 @@ def restriction(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = GaussianIntegers().fraction_field() sage: R. = K[] sage: v = GaussValuation(R, pAdicValuation(K, 2)) @@ -627,7 +620,7 @@ def uniformizer(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -644,7 +637,7 @@ def is_gauss_valuation(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -664,7 +657,7 @@ def monic_integral_model(self, G): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -686,7 +679,7 @@ def _ge_(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -721,7 +714,7 @@ def is_trivial(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -740,7 +733,7 @@ def scale(self, scalar): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -761,7 +754,7 @@ def _residue_ring_generator_name(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -798,7 +791,7 @@ def _relative_size(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: K. = QQ.extension(u^2 + u+ 1) sage: S. = K[] @@ -821,7 +814,7 @@ def is_negative_pseudo_valuation(self): No element in the domain of an augmented valuation can have valuation `-\infty`, so this method always returns ``False``:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: w = v.augmentation(x, infinity) @@ -839,7 +832,7 @@ class FinalAugmentedValuation(AugmentedValuation_base, FinalInductiveValuation): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: w = v.augmentation(x, 1) @@ -849,7 +842,7 @@ def __init__(self, parent, v, phi, mu): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: w = v.augmentation(x, 1) @@ -868,7 +861,7 @@ def residue_ring(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) @@ -953,7 +946,7 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) @@ -1029,7 +1022,7 @@ def _residue_field_generator(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) @@ -1077,7 +1070,7 @@ def lift(self, F): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) @@ -1130,7 +1123,7 @@ class NonFinalAugmentedValuation(AugmentedValuation_base, NonFinalInductiveValua EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -1140,7 +1133,7 @@ def __init__(self, parent, v, phi, mu): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -1159,7 +1152,7 @@ def residue_ring(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) @@ -1237,7 +1230,7 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 10) sage: S. = R[] sage: v = GaussValuation(S) @@ -1334,7 +1327,7 @@ def _residue_field_generator(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 10) sage: S. = R[] sage: v = GaussValuation(S) @@ -1377,7 +1370,7 @@ def lift(self, F, report_coefficients=False): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 10) sage: S. = R[] sage: v = GaussValuation(S) @@ -1483,7 +1476,7 @@ def lift_to_key(self, F, check=True): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 10) sage: S. = R[] sage: v = GaussValuation(S) @@ -1555,7 +1548,7 @@ def _Q(self, e): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -1576,7 +1569,7 @@ def _Q_reciprocal(self, e=1): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -1608,7 +1601,7 @@ class FiniteAugmentedValuation(AugmentedValuation_base, FiniteInductiveValuation EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1619,7 +1612,7 @@ def __init__(self, parent, v, phi, mu): r""" EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1638,7 +1631,7 @@ def value_group(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1660,7 +1653,7 @@ def value_semigroup(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Zq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1700,7 +1693,7 @@ def valuations(self, f, coefficients=None, call_error=False): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1764,7 +1757,7 @@ def simplify(self, f, error=None, force=False, effective_degree=None, size_heuri EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1805,7 +1798,7 @@ def lower_bound(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1847,7 +1840,7 @@ def upper_bound(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1870,7 +1863,7 @@ class FinalFiniteAugmentedValuation(FiniteAugmentedValuation, FinalAugmentedValu EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: w = v.augmentation(x, 1) @@ -1880,7 +1873,7 @@ def __init__(self, parent, v, phi, mu): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: w = v.augmentation(x, 1) @@ -1899,7 +1892,7 @@ class NonFinalFiniteAugmentedValuation(FiniteAugmentedValuation, NonFinalAugment EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x, 1) @@ -1909,7 +1902,7 @@ def __init__(self, parent, v, phi, mu): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x, 1) @@ -1929,7 +1922,7 @@ class InfiniteAugmentedValuation(FinalAugmentedValuation, InfiniteInductiveValua EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x, infinity) @@ -1939,7 +1932,7 @@ def __init__(self, parent, v, phi, mu): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x, infinity) @@ -1957,7 +1950,7 @@ def value_group(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1975,7 +1968,7 @@ def value_semigroup(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Zq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -2010,7 +2003,7 @@ def valuations(self, f, coefficients=None, call_error=False): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -2054,7 +2047,7 @@ def simplify(self, f, error=None, force=False, effective_degree=None): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -2082,7 +2075,7 @@ def lower_bound(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -2102,7 +2095,7 @@ def upper_bound(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) diff --git a/developing_valuation.py b/developing_valuation.py index 46f684d6a3a..f51f9878955 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -18,13 +18,6 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** - -# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) -import sys, os -if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: - sys.path.append(os.getcwd()) - sys.path.append(os.path.dirname(os.getcwd())) - from valuation import DiscretePseudoValuation from sage.misc.abstract_method import abstract_method @@ -37,7 +30,7 @@ class DevelopingValuation(DiscretePseudoValuation): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 7)) @@ -50,7 +43,7 @@ def __init__(self, parent, phi): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 7)) sage: isinstance(v, DevelopingValuation) @@ -76,7 +69,7 @@ def phi(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(2,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -100,7 +93,7 @@ def effective_degree(self, f, valuations=None): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(2,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -132,7 +125,7 @@ def _pow(self, x, e, error, effective_degree): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(2,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -164,7 +157,7 @@ def coefficients(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Qp(2,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -199,7 +192,7 @@ def _quo_rem(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 2)) sage: v._quo_rem(x^2 + 1) @@ -218,7 +211,7 @@ def newton_polygon(self, f, valuations=None): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Qp(2,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -250,7 +243,7 @@ def _call_(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Qp(2,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -296,7 +289,7 @@ def valuations(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Qp(2,5) sage: S. = R[] sage: v = GaussValuation(S, pAdicValuation(R)) @@ -312,7 +305,7 @@ def _test_effective_degree(self, **options): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(2,5) sage: S. = R[] sage: v = GaussValuation(S) diff --git a/function_field_valuation.py b/function_field_valuation.py index 0cf9c66ad9e..1d3dc14ccf9 100644 --- a/function_field_valuation.py +++ b/function_field_valuation.py @@ -7,7 +7,7 @@ We can create classical valuations that correspond to finite and infinite places on a rational function field:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, 1); v (x - 1)-adic valuation @@ -118,7 +118,7 @@ Run test suite for a valuation which sends an element to `-\infty`:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(QQ['x'], pAdicValuation(QQ, 2)).augmentation(x, infinity) sage: K. = FunctionField(QQ) @@ -138,13 +138,6 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** - -# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) -import sys, os -if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: - sys.path.append(os.getcwd()) - sys.path.append(os.path.dirname(os.getcwd())) - from sage.structure.factory import UniqueFactory from sage.rings.all import QQ, ZZ, infinity from sage.misc.abstract_method import abstract_method @@ -167,7 +160,7 @@ class FunctionFieldValuationFactory(UniqueFactory): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) We create a valuation that correspond to a finite rational place of a function @@ -276,7 +269,7 @@ def create_key_and_extra_args(self, domain, prime): We specify a valuation on a function field by two different means and get the same object:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x - 1) # indirect doctest @@ -337,7 +330,7 @@ def create_key_and_extra_args_from_place(self, domain, generator): TESTS: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, 1/x) # indirect doctest @@ -380,7 +373,7 @@ def create_key_and_extra_args_from_valuation(self, domain, valuation): TESTS: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = QQ[] sage: w = GaussValuation(R, TrivialValuation(QQ)).augmentation(x - 1, 1) @@ -425,7 +418,7 @@ def create_key_and_extra_args_from_valuation_on_isomorphic_field(self, domain, v TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) @@ -477,7 +470,7 @@ def create_object(self, version, key, **extra_args): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = QQ[] sage: w = GaussValuation(R, pAdicValuation(QQ, 2)) @@ -540,7 +533,7 @@ class FunctionFieldValuation_base(DiscretePseudoValuation): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, FunctionFieldValuation_base) @@ -555,7 +548,7 @@ class DiscreteFunctionFieldValuation_base(DiscreteValuation): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, DiscreteFunctionFieldValuation_base) @@ -568,7 +561,7 @@ def extensions(self, L): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x) sage: R. = K[] @@ -642,7 +635,7 @@ class RationalFunctionFieldValuation_base(FunctionFieldValuation_base): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, RationalFunctionFieldValuation_base) @@ -658,7 +651,7 @@ class ClassicalFunctionFieldValuation_base(DiscreteFunctionFieldValuation_base): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(5)) sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, ClassicalFunctionFieldValuation_base) @@ -672,7 +665,7 @@ def _test_classical_residue_field(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x^2 + 1) sage: v._test_classical_residue_field() @@ -688,7 +681,7 @@ def _ge_(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x^2 + 1) sage: w = FunctionFieldValuation(K, x) @@ -712,7 +705,7 @@ class InducedFunctionFieldValuation_base(FunctionFieldValuation_base): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x^2 + 1) # indirect doctest @@ -721,7 +714,7 @@ def __init__(self, parent, base_valuation): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, InducedFunctionFieldValuation_base) @@ -742,7 +735,7 @@ def uniformizer(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: FunctionFieldValuation(K, x).uniformizer() x @@ -757,7 +750,7 @@ def lift(self, F): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x) sage: v.lift(0) @@ -784,7 +777,7 @@ def value_group(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: FunctionFieldValuation(K, x).value_group() Additive Abelian Group generated by 1 @@ -798,7 +791,7 @@ def reduce(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x^2 + 1) sage: v.reduce(x) @@ -831,7 +824,7 @@ def _repr_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: FunctionFieldValuation(K, x^2 + 1) # indirect doctest (x^2 + 1)-adic valuation @@ -855,7 +848,7 @@ def extensions(self, L): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x^2 + 1) sage: L. = FunctionField(GaussianIntegers().fraction_field()) @@ -890,7 +883,7 @@ def _call_(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: v((x+1)/x^2) @@ -905,7 +898,7 @@ def residue_ring(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: FunctionFieldValuation(K, x).residue_ring() Rational Field @@ -932,7 +925,7 @@ class FiniteRationalFunctionFieldValuation(InducedFunctionFieldValuation_base, C EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x + 1); v # indirect doctest (x + 1)-adic valuation @@ -959,7 +952,7 @@ def __init__(self, parent, base_valuation): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x + 1) sage: isinstance(v, FiniteRationalFunctionFieldValuation) @@ -978,7 +971,7 @@ class NonClassicalRationalFunctionFieldValuation(InducedFunctionFieldValuation_b EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = GaussValuation(QQ['x'], pAdicValuation(QQ, 2)) sage: w = FunctionFieldValuation(K, v); w # indirect doctest @@ -993,7 +986,7 @@ def __init__(self, parent, base_valuation): function fields in the code. However, since these valuations must send elments to `-\infty`, they are not supported yet:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(QQ['x'], pAdicValuation(QQ, 2)).augmentation(x, infinity) sage: K. = FunctionField(QQ) @@ -1013,7 +1006,7 @@ class FunctionFieldFromLimitValuation(FiniteExtensionFromLimitValuation, Discret EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x^2 + x + 1)) @@ -1031,7 +1024,7 @@ def __init__(self, parent, approximant, G, approximants): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x^2 + x + 1)) @@ -1050,7 +1043,7 @@ def _to_base_domain(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x^2 + x + 1)) @@ -1068,7 +1061,7 @@ def scale(self, scalar): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x^2 + x + 1)) @@ -1090,7 +1083,7 @@ class FunctionFieldMappedValuation_base(FunctionFieldValuation_base, MappedValua EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: v = FunctionFieldValuation(K, 1/x); v Valuation at the infinite place @@ -1100,7 +1093,7 @@ def __init__(self, parent, base_valuation, to_base_valuation_domain, from_base_v r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: v = FunctionFieldValuation(K, 1/x) sage: isinstance(v, FunctionFieldMappedValuation_base) @@ -1119,7 +1112,7 @@ def _to_base_domain(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) @@ -1137,7 +1130,7 @@ def _from_base_domain(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) @@ -1155,7 +1148,7 @@ def scale(self, scalar): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) @@ -1176,7 +1169,7 @@ def _repr_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) @@ -1198,7 +1191,7 @@ class RationalFunctionFieldMappedValuation(FunctionFieldMappedValuation_base, Ra EXAMPLES:: - sage: from mac_lane import * + sage: sys.path.append(os.getcwd()); from mac_lane import * sage: K. = FunctionField(QQ) sage: R. = QQ[] sage: w = GaussValuation(R, pAdicValuation(QQ, 2)).augmentation(x, 1) @@ -1211,7 +1204,7 @@ def __init__(self, parent, base_valuation, to_base_valuation_doain, from_base_va r""" TESTS:: - sage: from mac_lane import * + sage: sys.path.append(os.getcwd()); from mac_lane import * sage: K. = FunctionField(QQ) sage: R. = QQ[] sage: w = GaussValuation(R, pAdicValuation(QQ, 2)).augmentation(x, 1) @@ -1231,7 +1224,7 @@ class InfiniteRationalFunctionFieldValuation(FunctionFieldMappedValuation_base, EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, 1/x) # indirect doctest @@ -1240,7 +1233,7 @@ def __init__(self, parent): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, 1/x) # indirect doctest sage: isinstance(v, InfiniteRationalFunctionFieldValuation) @@ -1258,7 +1251,7 @@ def _repr_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: FunctionFieldValuation(K, 1/x) # indirect doctest Valuation at the infinite place @@ -1277,7 +1270,7 @@ class FunctionFieldExtensionMappedValuation(FunctionFieldMappedValuation_base): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) @@ -1303,7 +1296,7 @@ def _repr_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) @@ -1331,7 +1324,7 @@ def restriction(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) diff --git a/gauss_valuation.py b/gauss_valuation.py index eab625cfac2..b0744c2e59a 100644 --- a/gauss_valuation.py +++ b/gauss_valuation.py @@ -12,7 +12,7 @@ EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v0 = pAdicValuation(QQ, 2) sage: v = GaussValuation(R, v0); v @@ -42,13 +42,6 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** - -# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) -import sys, os -if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: - sys.path.append(os.getcwd()) - sys.path.append(os.path.dirname(os.getcwd())) - from inductive_valuation import NonFinalInductiveValuation from sage.misc.cachefunc import cached_method @@ -71,7 +64,7 @@ class GaussValuationFactory(UniqueFactory): The Gauss valuation is the minimum of the valuation of the coefficients:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: R. = QQ[] sage: w = GaussValuation(R, v) @@ -89,7 +82,7 @@ def create_key(self, domain, v = None): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: R. = ZZ[] sage: GaussValuation.create_key(R, v) @@ -120,7 +113,7 @@ def create_object(self, version, key, **extra_args): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: R. = QQ[] sage: GaussValuation.create_object(0, (R, v)) @@ -146,7 +139,7 @@ class GaussValuation_generic(NonFinalInductiveValuation): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(3,5) sage: S. = R[] sage: v0 = pAdicValuation(R) @@ -166,7 +159,7 @@ def __init__(self, parent, v): """ TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: from mac_lane.gauss_valuation import GaussValuation_generic # optional: standalone sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) @@ -184,7 +177,7 @@ def value_group(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) sage: v.value_group() @@ -199,7 +192,7 @@ def value_semigroup(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) sage: v.value_semigroup() @@ -214,7 +207,7 @@ def _repr_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) sage: v # indirect doctest @@ -231,7 +224,7 @@ def uniformizer(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) sage: v.uniformizer() @@ -265,7 +258,7 @@ def valuations(self, f, coefficients=None, call_error=False): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = ZZ sage: S. = R[] sage: v = GaussValuation(S, pAdicValuation(R, 2)) @@ -309,7 +302,7 @@ def residue_ring(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = Qp(2,5)[] sage: v = GaussValuation(S) sage: v.residue_ring() @@ -344,7 +337,7 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = Qp(2,5)[] sage: v = GaussValuation(S) sage: f = x^2 + 2*x + 16 @@ -393,7 +386,7 @@ def lift(self, F): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = Qp(3,5)[] sage: v = GaussValuation(S) sage: f = x^2 + 2*x + 16 @@ -433,7 +426,7 @@ def lift_to_key(self, F): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ sage: S. = R[] sage: v = GaussValuation(S, pAdicValuation(QQ, 2)) @@ -468,7 +461,7 @@ def equivalence_unit(self, s, reciprocal=False): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = Qp(3,5)[] sage: v = GaussValuation(S) sage: v.equivalence_unit(2) @@ -489,7 +482,7 @@ def element_with_valuation(self, s): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: v.element_with_valuation(-2) @@ -505,7 +498,7 @@ def E(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -523,7 +516,7 @@ def F(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -540,7 +533,7 @@ def change_domain(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: R. = ZZ[] sage: w = GaussValuation(R, v) @@ -560,7 +553,7 @@ def extensions(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: R. = ZZ[] sage: w = GaussValuation(R, v) @@ -580,7 +573,7 @@ def restriction(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: R. = ZZ[] sage: w = GaussValuation(R, v) @@ -602,7 +595,7 @@ def is_gauss_valuation(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -619,7 +612,7 @@ def augmentation_chain(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -636,7 +629,7 @@ def is_trivial(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: v.is_trivial() @@ -653,7 +646,7 @@ def monic_integral_model(self, G): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qp(2, 5)[] sage: v = GaussValuation(R) sage: v.monic_integral_model(5*x^2 + 1/2*x + 1/4) @@ -690,7 +683,7 @@ def _ge_(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = GaussValuation(R, pAdicValuation(QQ, 3)) @@ -715,7 +708,7 @@ def scale(self, scalar): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: 3*v # indirect doctest @@ -741,7 +734,7 @@ def _relative_size(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: v._relative_size(x + 1024) @@ -785,7 +778,7 @@ def simplify(self, f, error=None, force=False, size_heuristic_bound=32, effectiv EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -817,7 +810,7 @@ def lower_bound(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -846,7 +839,7 @@ def upper_bound(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) diff --git a/inductive_valuation.py b/inductive_valuation.py index dff1bdf4b63..584c94f336d 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -27,13 +27,6 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** - -# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) -import sys, os -if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: - sys.path.append(os.getcwd()) - sys.path.append(os.path.dirname(os.getcwd())) - from valuation import DiscreteValuation, InfiniteDiscretePseudoValuation from developing_valuation import DevelopingValuation @@ -47,7 +40,7 @@ class InductiveValuation(DevelopingValuation): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 5)) @@ -67,7 +60,7 @@ def is_equivalence_unit(self, f, valuations=None): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(2,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -108,7 +101,7 @@ def equivalence_reciprocal(self, f, coefficients=None, valuations=None, check=Tr EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(3,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -216,7 +209,7 @@ def mu(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: v.mu() @@ -240,7 +233,7 @@ def equivalence_unit(self, s, reciprocal=False): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = Qp(3,5)[] sage: v = GaussValuation(S) sage: v.equivalence_unit(2) @@ -271,7 +264,7 @@ def augmentation_chain(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -287,7 +280,7 @@ def is_gauss_valuation(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -304,7 +297,7 @@ def E(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -320,7 +313,7 @@ def F(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -338,7 +331,7 @@ def monic_integral_model(self, G): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: v.monic_integral_model(5*x^2 + 1/2*x + 1/4) @@ -357,7 +350,7 @@ def element_with_valuation(self, s): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: v.element_with_valuation(-2) @@ -381,7 +374,7 @@ def _test_element_with_valuation_inductive_valuation(self, **options): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: v._test_element_with_valuation_inductive_valuation() @@ -413,7 +406,7 @@ def _test_EF(self, **options): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -437,7 +430,7 @@ def _test_augmentation_chain(self, **options): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: v._test_augmentation_chain() @@ -456,7 +449,7 @@ def _test_equivalence_unit(self, **options): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: v._test_equivalence_unit() @@ -489,7 +482,7 @@ def _test_is_equivalence_unit(self, **options): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: v._test_is_equivalence_unit() @@ -504,7 +497,7 @@ def _test_equivalence_reciprocal(self, **options): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: v._test_equivalence_reciprocal() @@ -535,7 +528,7 @@ def _test_inductive_valuation_inheritance(self, **options): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: v._test_inductive_valuation_inheritance() @@ -554,7 +547,7 @@ class FiniteInductiveValuation(InductiveValuation, DiscreteValuation): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) @@ -563,7 +556,7 @@ def __init__(self, parent, phi): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: isinstance(v, FiniteInductiveValuation) @@ -579,7 +572,7 @@ def extensions(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = ZZ[] sage: v = GaussValuation(R, TrivialValuation(ZZ)) sage: K. = FunctionField(QQ) @@ -604,7 +597,7 @@ class NonFinalInductiveValuation(FiniteInductiveValuation, DiscreteValuation): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -615,7 +608,7 @@ def __init__(self, parent, phi): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -646,7 +639,7 @@ def augmentation(self, phi, mu, check=True): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -718,7 +711,7 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: S. = K[] sage: F = y^2 - x^2 - x^3 - 3 @@ -897,7 +890,7 @@ def is_key(self, phi, explain=False, assume_equivalence_irreducible=False): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -943,7 +936,7 @@ def is_minimal(self, f, assume_equivalence_irreducible=False): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1032,7 +1025,7 @@ def _equivalence_reduction(self, f, coefficients=None, valuations=None, degree_b EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: v._equivalence_reduction(2*x^6 + 4*x^5 + 2*x^4 + 8) @@ -1091,7 +1084,7 @@ def is_equivalence_irreducible(self, f, coefficients=None, valuations=None): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1160,7 +1153,7 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,10) sage: S. = R[] sage: v = GaussValuation(S) @@ -1315,7 +1308,7 @@ def minimal_representative(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,10) sage: S. = R[] sage: v = GaussValuation(S) @@ -1391,7 +1384,7 @@ def lift_to_key(self, F): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,10) sage: S. = R[] sage: v = GaussValuation(S) @@ -1408,7 +1401,7 @@ def _test_lift_to_key(self, **options): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: v._test_lift_to_key() @@ -1458,7 +1451,7 @@ def _test_is_equivalence_irreducible(self, **options): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: v._test_is_equivalence_irreducible() @@ -1485,7 +1478,7 @@ class FinalInductiveValuation(InductiveValuation): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: w = v.augmentation(x^2 + x + 1, infinity) @@ -1502,7 +1495,7 @@ class InfiniteInductiveValuation(FinalInductiveValuation, InfiniteDiscretePseudo EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, infinity) @@ -1512,7 +1505,7 @@ def __init__(self, parent, base_valuation): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, infinity) @@ -1530,7 +1523,8 @@ def _lift_to_maximal_precision(c): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone + sage: from mac_lane.inductive_valuation import _lift_to_maximal_precision # optional: standalone sage: R = Zp(2,5) sage: x = R(1,2); x 1 + O(2^2) diff --git a/limit_valuation.py b/limit_valuation.py index f46495d98aa..a465b54f966 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -26,7 +26,7 @@ point has two extensions to ``L``. The valuations corresponding to these extensions can only be approximated:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -76,13 +76,6 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** - -# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) -import sys, os -if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: - sys.path.append(os.getcwd()) - sys.path.append(os.path.dirname(os.getcwd())) - from sage.misc.abstract_method import abstract_method from valuation import DiscretePseudoValuation, InfiniteDiscretePseudoValuation, DiscreteValuation from sage.structure.factory import UniqueFactory @@ -103,7 +96,7 @@ class LimitValuationFactory(UniqueFactory): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = LimitValuation(v, x) @@ -117,10 +110,10 @@ def create_key(self, base_valuation, G): EXAMPLES: - Note that this does not normalize ``base_valuation`` in anyway. It is + Note that this does not normalize ``base_valuation`` in any way. It is easily possible to create the same limit in two different ways:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = LimitValuation(v, x) # indirect doctest @@ -144,7 +137,7 @@ def create_object(self, version, key): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = LimitValuation(v, x^2 + 1) # indirect doctest @@ -166,7 +159,7 @@ class LimitValuation_generic(DiscretePseudoValuation): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -193,7 +186,7 @@ def __init__(self, parent, approximation): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: K. = QQ.extension(x^2 + 1) sage: v = pAdicValuation(K, 2) @@ -220,7 +213,7 @@ def reduce(self, f, check=True): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x - 1)) @@ -242,7 +235,7 @@ def _call_(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -264,7 +257,7 @@ def _improve_approximation_for_reduce(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x - 1337)) @@ -309,7 +302,7 @@ def _improve_approximation_for_call(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x - 23)) @@ -345,7 +338,7 @@ def _repr_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -376,7 +369,7 @@ class MacLaneLimitValuation(LimitValuation_generic, InfiniteDiscretePseudoValuat EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: K. = QQ.extension(x^2 + 1) @@ -389,7 +382,7 @@ def __init__(self, parent, approximation, G): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: K. = QQ.extension(x^2 + 1) sage: v = pAdicValuation(K, 2) @@ -411,7 +404,7 @@ def extensions(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(GaussianIntegers(), 2) sage: u = v._base_valuation sage: u.extensions(QQ['x']) @@ -437,7 +430,7 @@ def lift(self, F): EXAMPLES;; - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^4 - x^2 - 2*x - 1) @@ -460,7 +453,7 @@ def uniformizer(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -479,7 +472,7 @@ def _call_(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: vK = pAdicValuation(K, 2) @@ -512,7 +505,7 @@ def _improve_approximation(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -555,7 +548,7 @@ def _improve_approximation_for_call(self, f): approximation (perform one step of the Mac Lane algorithm) than to check for this:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -633,7 +626,7 @@ def _improve_approximation_for_reduce(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -663,7 +656,7 @@ def residue_ring(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -690,7 +683,7 @@ def _ge_(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: F = (x^2 + 7) * (x^2 + 9) sage: G = (x^2 + 7) @@ -734,7 +727,7 @@ def restriction(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -757,7 +750,7 @@ def _weakly_separating_element(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -814,7 +807,7 @@ def value_semigroup(self): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -832,7 +825,7 @@ def element_with_valuation(self, s): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -858,7 +851,7 @@ def _relative_size(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -880,7 +873,7 @@ def simplify(self, f, error=None, force=False): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -910,7 +903,7 @@ def lower_bound(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -934,7 +927,7 @@ def upper_bound(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -959,7 +952,7 @@ def is_negative_pseudo_valuation(self): For a Mac Lane limit valuation, this is never the case, so this method always returns ``False``:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) diff --git a/mapped_valuation.py b/mapped_valuation.py index 45ec9d2cdf9..29d19268edc 100644 --- a/mapped_valuation.py +++ b/mapped_valuation.py @@ -7,7 +7,7 @@ Extensions of valuations over finite field extensions `L=K[x]/(G)` are realized through an infinite valuation on `K[x]` which maps `G` to infinity:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -32,13 +32,6 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** - -# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) -import sys, os -if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: - sys.path.append(os.getcwd()) - sys.path.append(os.path.dirname(os.getcwd())) - from valuation import DiscreteValuation, DiscretePseudoValuation from sage.misc.abstract_method import abstract_method @@ -48,7 +41,7 @@ class MappedValuation_base(DiscretePseudoValuation): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -66,7 +59,7 @@ def __init__(self, parent, base_valuation): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x^2 + 1) @@ -91,7 +84,7 @@ def _repr_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -107,7 +100,7 @@ def residue_ring(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -124,7 +117,7 @@ def uniformizer(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -141,7 +134,7 @@ def _to_base_domain(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -160,7 +153,7 @@ def _from_base_domain(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -179,7 +172,7 @@ def _call_(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -198,7 +191,7 @@ def reduce(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x - 2)) @@ -218,7 +211,7 @@ def lift(self, F): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -241,7 +234,7 @@ def _to_base_residue_ring(self, F): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -261,7 +254,7 @@ def _from_base_residue_ring(self, F): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -281,7 +274,7 @@ def _test_to_from_base_domain(self, **options): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -314,7 +307,7 @@ class FiniteExtensionFromInfiniteValuation(MappedValuation_base, DiscreteValuati EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -328,7 +321,7 @@ def __init__(self, parent, base_valuation): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -349,7 +342,7 @@ def _eq_(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -368,7 +361,7 @@ def restriction(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -391,7 +384,7 @@ def _weakly_separating_element(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -421,7 +414,7 @@ def _relative_size(self, x): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -443,7 +436,7 @@ def simplify(self, x, error=None, force=False): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -470,7 +463,7 @@ def lower_bound(self, x): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -494,7 +487,7 @@ def upper_bound(self, x): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -517,7 +510,7 @@ class FiniteExtensionFromLimitValuation(FiniteExtensionFromInfiniteValuation): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -539,7 +532,7 @@ def __init__(self, parent, approximant, G, approximants): Note that this implementation is also used when the underlying limit is only taken over a finite sequence of valuations:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -565,7 +558,7 @@ def _repr_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(GaussianIntegers().fraction_field(), 2) # indirect doctest 2-adic valuation diff --git a/padic_valuation.py b/padic_valuation.py index f320ddb1ebc..14904b9bfd8 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -15,13 +15,6 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** - -# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) -import sys, os -if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: - sys.path.append(os.getcwd()) - sys.path.append(os.path.dirname(os.getcwd())) - from valuation import DiscreteValuation from value_group import DiscreteValueSemigroup from mapped_valuation import FiniteExtensionFromLimitValuation @@ -48,7 +41,7 @@ class PadicValuationFactory(UniqueFactory): For integers and rational numbers, ``prime`` is just a prime of the integers:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(ZZ, 3) 3-adic valuation @@ -163,7 +156,7 @@ def create_key_and_extra_args(self, R, prime=None, approximants=None): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2) # indirect doctest 2-adic valuation @@ -193,7 +186,7 @@ def create_key_for_integers(self, R, prime): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2) # indirect doctest 2-adic valuation @@ -215,7 +208,7 @@ def create_key_for_local_ring(self, R, prime): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(Qp(2)) # indirect doctest 2-adic valuation @@ -238,7 +231,7 @@ def create_key_and_extra_args_for_number_field(self, R, prime, approximants): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(GaussianIntegers(), 2) # indirect doctest 2-adic valuation @@ -269,7 +262,7 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime, EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(GaussianIntegers(), pAdicValuation(ZZ, 2)) # indirect doctest 2-adic valuation @@ -335,7 +328,7 @@ def create_key_and_extra_args_for_number_field_from_ideal(self, R, I, prime): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(GaussianIntegers(), GaussianIntegers().ideal(2)) # indirect doctest 2-adic valuation @@ -375,7 +368,7 @@ def _normalize_number_field_data(self, R): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: K = R.quo(x^2 + 1) sage: pAdicValuation._normalize_number_field_data(K) @@ -410,7 +403,7 @@ def create_object(self, version, key, **extra_args): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(ZZ, 5) # indirect doctest 5-adic valuation @@ -458,7 +451,7 @@ class pAdicValuation_base(DiscreteValuation): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(ZZ, 3) 3-adic valuation @@ -483,7 +476,7 @@ def __init__(self, parent, p): """ TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: isinstance(pAdicValuation(ZZ, 3), pAdicValuation_base) True @@ -499,7 +492,7 @@ def p(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(GaussianIntegers(), 2).p() 2 @@ -520,7 +513,7 @@ def reduce(self, x): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: v.reduce(4) 1 @@ -543,6 +536,7 @@ def lift(self, x): EXAMPLES:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: xbar = v.reduce(4) sage: v.lift(xbar) @@ -576,7 +570,7 @@ def is_unramified(self, G, include_steps=False, assume_squarefree=False): We consider an extension as unramified if its ramification index is 1. Hence, a trivial extension is unramified:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = pAdicValuation(QQ, 2) sage: v.is_unramified(x) @@ -655,7 +649,7 @@ def is_totally_ramified(self, G, include_steps=False, assume_squarefree=False): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: k=Qp(5,4) sage: v = pAdicValuation(k) sage: R.=k[] @@ -717,7 +711,7 @@ def change_domain(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.change_domain(QQ).domain() Rational Field @@ -731,7 +725,7 @@ def extensions(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.extensions(GaussianIntegers()) [2-adic valuation] @@ -794,7 +788,7 @@ def restriction(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(GaussianIntegers(), 2) sage: v.restriction(ZZ) 2-adic valuation @@ -815,7 +809,7 @@ def value_semigroup(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(GaussianIntegers(), 2) sage: v.value_semigroup() Additive Abelian Semigroup generated by 1/2 @@ -839,7 +833,7 @@ class pAdicValuation_padic(pAdicValuation_base): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(Qp(2)); v #indirect doctest 2-adic valuation @@ -852,7 +846,7 @@ def __init__(self, parent): """ TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: from sage.rings.padics.padic_valuation import padicValuation_padic # optional: integrated sage: isinstance(pAdicValuation(Qp(2)), pAdicValuation_padic) True @@ -874,7 +868,7 @@ def reduce(self, x): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(3) sage: pAdicValuation(Zp(3)).reduce(R(4)) 1 @@ -894,7 +888,7 @@ def lift(self, x): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(3) sage: v = pAdicValuation(R) sage: xbar = v.reduce(R(4)) @@ -911,7 +905,7 @@ def uniformizer(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(Zp(3)) sage: v.uniformizer() 3 + O(3^21) @@ -929,7 +923,7 @@ def element_with_valuation(self, v): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(3) sage: v = pAdicValuation(Zp(3)) sage: v.element_with_valuation(3) @@ -949,7 +943,7 @@ def _repr_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(ZZ, 3)._repr_() '3-adic valuation' @@ -962,7 +956,7 @@ def _call_(self, x): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = Qp(3) sage: R. = K[] sage: L. = K.extension(y^2 - 3) @@ -978,7 +972,7 @@ def residue_ring(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(Qq(9, names='a'), 3).residue_ring() Finite Field in a0 of size 3^2 @@ -999,7 +993,7 @@ def shift(self, x, s): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = ZpCA(2) sage: v = pAdicValuation(R) sage: v.shift(R.one(), 1) @@ -1033,7 +1027,7 @@ def simplify(self, x, error=None, force=False): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(2) sage: v = pAdicValuation(R, 2) sage: v.simplify(6) @@ -1059,7 +1053,7 @@ class pAdicValuation_int(pAdicValuation_base): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3); v 3-adic valuation @@ -1074,7 +1068,7 @@ def _repr_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(ZZ, 3)._repr_() '3-adic valuation' @@ -1091,7 +1085,7 @@ def _call_(self, x): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(ZZ, 3)(9) 2 @@ -1113,7 +1107,7 @@ def uniformizer(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: v.uniformizer() 3 @@ -1127,7 +1121,7 @@ def residue_ring(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: v.residue_ring() Finite Field of size 3 @@ -1143,7 +1137,7 @@ def _ge_(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: w = TrivialValuation(ZZ) sage: v >= w @@ -1170,7 +1164,7 @@ def _relative_size(self, x): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v._relative_size(2) 2 @@ -1204,7 +1198,7 @@ def simplify(self, x, error=None, force=False, size_heuristic_bound=32): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.simplify(6, force=True) 2 @@ -1251,7 +1245,7 @@ class pAdicFromLimitValuation(FiniteExtensionFromLimitValuation, pAdicValuation_ EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(GaussianIntegers(), 3); v 3-adic valuation @@ -1264,7 +1258,7 @@ def __init__(self, parent, approximant, G, approximants): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(GaussianIntegers(), 3) sage: isinstance(v, pAdicFromLimitValuation) True @@ -1280,7 +1274,7 @@ def _to_base_domain(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(GaussianIntegers(), 3) sage: I = GaussianIntegers().fraction_field().gen() sage: v._to_base_domain(I) @@ -1297,7 +1291,7 @@ def _from_base_domain(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(GaussianIntegers(), 3) sage: v._from_base_domain(v._base_valuation.domain().gen()) I @@ -1311,7 +1305,7 @@ def extensions(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(GaussianIntegers(), 3) sage: v.extensions(v.domain().fraction_field()) [3-adic valuation] diff --git a/scaled_valuation.py b/scaled_valuation.py index 8f4f2e96891..586a6f298e8 100644 --- a/scaled_valuation.py +++ b/scaled_valuation.py @@ -4,7 +4,7 @@ EXAMPLES: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: 3*pAdicValuation(ZZ, 3) 3 * 3-adic valuation @@ -21,13 +21,6 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** - -# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) -import sys, os -if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: - sys.path.append(os.getcwd()) - sys.path.append(os.path.dirname(os.getcwd())) - from sage.structure.factory import UniqueFactory from valuation import DiscreteValuation @@ -38,7 +31,7 @@ class ScaledValuationFactory(UniqueFactory): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: 3*pAdicValuation(ZZ, 2) # indirect doctest 3 * 2-adic valuation @@ -49,7 +42,7 @@ def create_key(self, base, s): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: 3*pAdicValuation(ZZ, 2) is 2*(3/2*pAdicValuation(ZZ, 2)) # indirect doctest True @@ -80,7 +73,7 @@ def create_object(self, version, key): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: 3*pAdicValuation(ZZ, 2) # indirect doctest 3 * 2-adic valuation @@ -102,7 +95,7 @@ class ScaledValuation_generic(DiscreteValuation): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = 3*pAdicValuation(ZZ, 3); v 3 * 3-adic valuation @@ -115,7 +108,7 @@ def __init__(self, parent, base_valuation, s): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = 3*pAdicValuation(ZZ, 2) sage: isinstance(v, ScaledValuation_generic) True @@ -132,7 +125,7 @@ def _repr_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: 3*pAdicValuation(ZZ, 2) # indirect doctest 3 * 2-adic valuation @@ -145,7 +138,7 @@ def residue_ring(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = 3*pAdicValuation(ZZ, 2) sage: v.residue_ring() Finite Field of size 2 @@ -159,7 +152,7 @@ def uniformizer(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = 3*pAdicValuation(ZZ, 2) sage: v.uniformizer() 2 @@ -173,7 +166,7 @@ def _call_(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = 3*pAdicValuation(ZZ, 2) sage: v(2) 3 @@ -187,7 +180,7 @@ def reduce(self, f): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = 3*pAdicValuation(ZZ, 2) sage: v.reduce(1) 1 @@ -202,7 +195,7 @@ def lift(self, F): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = 3*pAdicValuation(ZZ, 2) sage: v.lift(1) 1 @@ -216,7 +209,7 @@ def extensions(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = 3*pAdicValuation(ZZ, 5) sage: v.extensions(GaussianIntegers().fraction_field()) [3 * [ 5-adic valuation, v(x + 2) = 1 ]-adic valuation, @@ -231,7 +224,7 @@ def restriction(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = 3*pAdicValuation(QQ, 5) sage: v.restriction(ZZ) 3 * 5-adic valuation @@ -247,7 +240,7 @@ def _strictly_separating_element(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v2 = pAdicValuation(QQ, 2) sage: v3 = 12 * pAdicValuation(QQ, 3) sage: v2._strictly_separating_element(v3) @@ -274,7 +267,7 @@ def _weakly_separating_element(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v2 = pAdicValuation(QQ, 2) sage: v3 = 12 * pAdicValuation(QQ, 3) sage: v2._weakly_separating_element(v3) @@ -290,7 +283,7 @@ def _ge_(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v2 = pAdicValuation(QQ, 2) sage: 2*v2 >= v2 True @@ -326,7 +319,7 @@ def _le_(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v2 = pAdicValuation(QQ, 2) sage: 2*v2 <= v2 False @@ -350,7 +343,7 @@ def value_semigroup(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v2 = pAdicValuation(QQ, 2) sage: (2*v2).value_semigroup() Additive Abelian Semigroup generated by -2, 2 diff --git a/trivial_valuation.py b/trivial_valuation.py index 529886d0ddd..76b1744146d 100644 --- a/trivial_valuation.py +++ b/trivial_valuation.py @@ -4,7 +4,7 @@ EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ); v Trivial valuation on Rational Field sage: v(1) @@ -48,13 +48,6 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** - -# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) -import sys, os -if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: - sys.path.append(os.getcwd()) - sys.path.append(os.path.dirname(os.getcwd())) - from valuation import DiscretePseudoValuation, DiscreteValuation, InfiniteDiscretePseudoValuation from valuation_space import DiscretePseudoValuationSpace from sage.structure.factory import UniqueFactory @@ -65,6 +58,7 @@ class TrivialValuationFactory(UniqueFactory): EXAMPLES:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ); v Trivial valuation on Rational Field sage: v(1) @@ -75,6 +69,8 @@ def __init__(self, clazz, parent, *args, **kwargs): r""" TESTS:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone + sage: from mac_lane.trivial_valuation import TrivialValuationFactory sage: isinstance(TrivialValuation, TrivialValuationFactory) True @@ -89,6 +85,7 @@ def create_key(self, domain): EXAMPLES:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: TrivialValuation(QQ) is TrivialValuation(QQ) # indirect doctest True @@ -101,6 +98,7 @@ def create_object(self, version, key, **extra_args): EXAMPLES:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: TrivialValuation(QQ) # indirect doctest Trivial valuation on Rational Field @@ -115,7 +113,7 @@ class TrivialDiscretePseudoValuation_base(DiscretePseudoValuation): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(ZZ); v Trivial pseudo-valuation on Integer Ring @@ -130,7 +128,7 @@ def uniformizer(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(ZZ) sage: v.uniformizer() Traceback (most recent call last): @@ -146,7 +144,7 @@ def is_trivial(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ) sage: v.is_trivial() True @@ -160,7 +158,7 @@ def is_negative_pseudo_valuation(self): EXAMPLES: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ) sage: v.is_negative_pseudo_valuation() False @@ -174,7 +172,7 @@ class TrivialDiscretePseudoValuation(TrivialDiscretePseudoValuation_base, Infini EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ); v Trivial pseudo-valuation on Rational Field @@ -187,7 +185,7 @@ def __init__(self, parent): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ) sage: isinstance(v, TrivialDiscretePseudoValuation) True @@ -202,7 +200,7 @@ def _call_(self, x): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ) sage: v(0) +Infinity @@ -219,7 +217,7 @@ def _repr_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: TrivialPseudoValuation(QQ) # indirect doctest Trivial pseudo-valuation on Rational Field @@ -234,7 +232,7 @@ def value_group(self): A trivial discrete pseudo-valuation has no value group:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ) sage: v.value_group() Traceback (most recent call last): @@ -250,7 +248,7 @@ def residue_ring(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: TrivialPseudoValuation(QQ).residue_ring() Quotient of Rational Field by the ideal (1) @@ -263,7 +261,7 @@ def reduce(self, x): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ) sage: v.reduce(1) 0 @@ -278,7 +276,7 @@ def lift(self, X): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ) sage: v.lift(v.residue_ring().zero()) 0 @@ -294,7 +292,7 @@ def _ge_(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ) sage: w = TrivialValuation(QQ) sage: v >= w @@ -310,7 +308,7 @@ class TrivialDiscreteValuation(TrivialDiscretePseudoValuation_base, DiscreteValu EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ); v Trivial valuation on Rational Field @@ -323,7 +321,7 @@ def __init__(self, parent): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ) sage: isinstance(v, TrivialDiscreteValuation) True @@ -338,7 +336,7 @@ def _call_(self, x): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ) sage: v(0) +Infinity @@ -355,7 +353,7 @@ def _repr_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: TrivialValuation(QQ) # indirect doctest Trivial valuation on Rational Field @@ -370,7 +368,7 @@ def value_group(self): A trivial discrete valuation has a trivial value group:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ) sage: v.value_group() Trivial Additive Abelian Group @@ -385,6 +383,7 @@ def residue_ring(self): EXAMPLES:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: TrivialValuation(QQ).residue_ring() Rational Field @@ -397,7 +396,7 @@ def reduce(self, x): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ) sage: v.reduce(1) 1 @@ -411,7 +410,7 @@ def lift(self, X): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ) sage: v.lift(v.residue_ring().zero()) 0 @@ -425,7 +424,7 @@ def extensions(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(ZZ) sage: v.extensions(QQ) [Trivial valuation on Rational Field] @@ -442,7 +441,7 @@ def _ge_(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ) sage: w = TrivialValuation(QQ) sage: w >= v diff --git a/valuation.py b/valuation.py index 3d05431d902..f875554ca39 100644 --- a/valuation.py +++ b/valuation.py @@ -17,13 +17,6 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** - -# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) -import sys, os -if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: - sys.path.append(os.getcwd()) - sys.path.append(os.path.dirname(os.getcwd())) - from sage.categories.morphism import Morphism from sage.misc.cachefunc import cached_method @@ -39,7 +32,7 @@ class DiscretePseudoValuation(Morphism): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2); v # indirect doctest 2-adic valuation @@ -52,7 +45,7 @@ def __init__(self, parent): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: isinstance(pAdicValuation(ZZ, 2), DiscretePseudoValuation) True @@ -65,7 +58,7 @@ def is_equivalent(self, f, g): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: v.is_equivalent(2, 1) False @@ -94,7 +87,7 @@ def __hash__(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: hash(v) == hash(v) # indirect doctest True @@ -116,7 +109,7 @@ def _hash_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: hash(v) == hash(v) # indirect doctest True @@ -137,7 +130,7 @@ def _cmp_(self, other): when they can fall back to the implementation through ``>=`` and ``<=``:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: v > v False @@ -172,7 +165,7 @@ def _richcmp_(self, other, op): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: v == v True @@ -228,7 +221,7 @@ def _eq_(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ) sage: v == v True @@ -246,7 +239,7 @@ def _le_(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ) sage: w = pAdicValuation(QQ, 2) sage: v <= w @@ -277,7 +270,7 @@ def _ge_(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ) sage: w = pAdicValuation(QQ, 2) sage: v >= w @@ -316,7 +309,7 @@ def _test_valuation_inheritance(self, **options): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2)._test_valuation_inheritance() """ @@ -330,7 +323,7 @@ class InfiniteDiscretePseudoValuation(DiscretePseudoValuation): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: R. = QQ[] sage: v = GaussValuation(R, v) @@ -350,7 +343,7 @@ def is_discrete_valuation(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: R. = QQ[] sage: v = GaussValuation(R, v) @@ -369,7 +362,7 @@ class NegativeInfiniteDiscretePseudoValuation(InfiniteDiscretePseudoValuation): EXAMPLES: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)).augmentation(x, infinity) sage: K. = FunctionField(QQ) @@ -386,7 +379,7 @@ def is_negative_pseudo_valuation(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)).augmentation(x, infinity) sage: K. = FunctionField(QQ) @@ -404,7 +397,7 @@ class DiscreteValuation(DiscretePseudoValuation): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: R. = QQ[] sage: v = GaussValuation(R, v) @@ -424,7 +417,7 @@ def is_discrete_valuation(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(ZZ) sage: v.is_discrete_valuation() True @@ -477,7 +470,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: R. = QQ[] sage: v.mac_lane_approximants(x^2 + 1) @@ -784,7 +777,7 @@ def _pow(self, x, e, error): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: v._pow(2, 2, error=4) 4 @@ -808,7 +801,7 @@ def mac_lane_approximant(self, G, valuation, approximants = None): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: R. = QQ[] sage: G = x^2 + 1 @@ -919,7 +912,7 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: k=Qp(5,4) sage: v = pAdicValuation(k) sage: R.=k[] @@ -974,7 +967,7 @@ def _ge_(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ) sage: w = pAdicValuation(QQ, 2) sage: v >= w diff --git a/valuation_space.py b/valuation_space.py index e6c75461c47..3d47abbe905 100644 --- a/valuation_space.py +++ b/valuation_space.py @@ -9,7 +9,7 @@ EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2).parent() Discrete pseudo-valuations on Rational Field @@ -26,13 +26,6 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** - -# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) -import sys, os -if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: - sys.path.append(os.getcwd()) - sys.path.append(os.path.dirname(os.getcwd())) - from sage.categories.homset import Homset from sage.misc.lazy_attribute import lazy_attribute from sage.misc.abstract_method import abstract_method @@ -45,7 +38,7 @@ class DiscretePseudoValuationSpace(UniqueRepresentation, Homset): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: H = DiscretePseudoValuationSpace(QQ) sage: pAdicValuation(QQ, 2) in H True @@ -84,7 +77,7 @@ def __init__(self, domain): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: isinstance(pAdicValuation(QQ, 2).parent(), DiscretePseudoValuationSpace) True @@ -116,7 +109,7 @@ def _abstract_element_class(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: isinstance(pAdicValuation(QQ, 2), DiscretePseudoValuationSpace.ElementMethods) # indirect doctest True @@ -131,7 +124,7 @@ def _get_action_(self, S, op, self_on_left): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: from operator import mul sage: v.parent().get_action(ZZ, mul) # indirect doctest @@ -151,7 +144,7 @@ def _an_element_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscretePseudoValuationSpace(QQ).an_element() # indirect doctest Trivial pseudo-valuation on Rational Field @@ -165,7 +158,7 @@ def _repr_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscretePseudoValuationSpace(QQ) # indirect doctest Discrete pseudo-valuations on Rational Field @@ -178,7 +171,7 @@ def __contains__(self, x): EXAMPLES: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: H = DiscretePseudoValuationSpace(QQ) sage: H.an_element() in H True @@ -201,7 +194,7 @@ def __call__(self, x): EXAMPLES: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: H = DiscretePseudoValuationSpace(QQ) sage: H(pAdicValuation(QQ, 2)) 2-adic valuation @@ -222,7 +215,7 @@ def _element_constructor_(self, x): We try to convert valuations defined on different domains by changing their base ring:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: Z = DiscretePseudoValuationSpace(ZZ) sage: Q = DiscretePseudoValuationSpace(QQ) sage: v = pAdicValuation(ZZ, 2) @@ -262,7 +255,7 @@ class ElementMethods: Here is an example of a method that is automagically added to a discrete valuation:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: H = DiscretePseudoValuationSpace(QQ) sage: pAdicValuation(QQ, 2).is_discrete_pseudo_valuation() # indirect doctest True @@ -270,7 +263,7 @@ class ElementMethods: The methods will be provided even if the concrete types is not created with :meth:`__make_element_class__`:: - sage: from valuation import DiscretePseudoValuation + sage: from mac_lane.valuation import DiscretePseudoValuation sage: m = DiscretePseudoValuation(H) sage: m.parent() is H True @@ -297,7 +290,7 @@ def is_discrete_pseudo_valuation(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2).is_discrete_pseudo_valuation() True @@ -313,7 +306,7 @@ def is_discrete_valuation(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2).is_discrete_valuation() True @@ -327,7 +320,7 @@ def is_negative_pseudo_valuation(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2).is_negative_pseudo_valuation() False @@ -351,7 +344,7 @@ def is_trivial(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 7).is_trivial() False @@ -373,7 +366,7 @@ def uniformizer(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 11).uniformizer() 11 @@ -398,7 +391,7 @@ def value_group(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2).value_group() Additive Abelian Group generated by 1 @@ -426,7 +419,7 @@ def value_semigroup(self): Most commonly, in particular over fields, the semigroup is the group generated by the valuation of the uniformizer:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: G = pAdicValuation(QQ, 2).value_semigroup(); G Additive Abelian Semigroup generated by -1, 1 sage: G in AdditiveMagmas().AdditiveAssociative().AdditiveUnital().AdditiveInverse() @@ -462,7 +455,7 @@ def element_with_valuation(self, s): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.element_with_valuation(10) 1024 @@ -491,7 +484,7 @@ def residue_ring(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2).residue_ring() Finite Field of size 2 sage: TrivialValuation(QQ).residue_ring() @@ -518,7 +511,7 @@ def residue_field(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2).residue_field() Finite Field of size 2 sage: TrivialValuation(QQ).residue_field() @@ -549,7 +542,7 @@ def reduce(self, x): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: v.reduce(2) 0 @@ -572,7 +565,7 @@ def lift(self, X): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: v.lift(v.residue_ring().one()) 1 @@ -585,7 +578,7 @@ def extension(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: w = v.extension(QQ) sage: w.domain() @@ -604,7 +597,7 @@ def extensions(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.extensions(QQ) [2-adic valuation] @@ -620,7 +613,7 @@ def restriction(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: w = v.restriction(ZZ) sage: w.domain() @@ -641,7 +634,7 @@ def change_domain(self, ring): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 3) sage: v.change_domain(ZZ) 3-adic valuation @@ -665,7 +658,7 @@ def scale(self, scalar): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: w = v.scale(3) sage: w(3) @@ -723,7 +716,7 @@ def separating_element(self, others): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v2 = pAdicValuation(QQ, 2) sage: v3 = pAdicValuation(QQ, 3) sage: v5 = pAdicValuation(QQ, 5) @@ -796,7 +789,7 @@ def _strictly_separating_element(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v2 = pAdicValuation(QQ, 2) sage: v3 = pAdicValuation(QQ, 3) sage: v2._strictly_separating_element(v3) @@ -874,7 +867,7 @@ def _weakly_separating_element(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v2 = pAdicValuation(QQ, 2) sage: v3 = pAdicValuation(QQ, 3) sage: v2._weakly_separating_element(v3) @@ -900,7 +893,7 @@ def shift(self, x, s): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.shift(1, 10) 1024 @@ -952,7 +945,7 @@ def simplify(self, x, error=None, force=False): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.simplify(6, force=True) 2 @@ -975,7 +968,7 @@ def lower_bound(self, x): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.lower_bound(2^10) 10 @@ -992,7 +985,7 @@ def upper_bound(self, x): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.upper_bound(2^10) 10 @@ -1014,7 +1007,7 @@ def _relative_size(self, x): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(Qp(2)) sage: v._relative_size(2) 1 @@ -1035,7 +1028,7 @@ def _test_is_negative_pseudo_valuation(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: v._test_is_negative_pseudo_valuation() @@ -1059,7 +1052,7 @@ def _test_bounds(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: v._test_bounds() @@ -1077,7 +1070,7 @@ def _test_simplify(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: v._test_simplify() @@ -1121,7 +1114,7 @@ def _test_shift(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: v._test_shift() @@ -1151,7 +1144,7 @@ def _test_scale(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: v._test_scale() @@ -1188,7 +1181,7 @@ def _test_add(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: v._test_add() @@ -1207,7 +1200,7 @@ def _test_infinite_zero(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_infinite_zero() @@ -1222,7 +1215,7 @@ def _test_mul(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_mul() @@ -1239,7 +1232,7 @@ def _test_no_infinite_units(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_no_infinite_units() @@ -1270,7 +1263,7 @@ def _test_value_group(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_value_group() @@ -1300,7 +1293,7 @@ def _test_value_semigroup(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_value_semigroup() @@ -1320,7 +1313,7 @@ def _test_element_with_valuation(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_element_with_valuation() @@ -1340,7 +1333,7 @@ def _test_residue_ring(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_residue_ring() @@ -1373,7 +1366,7 @@ def _test_reduce(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_reduce() @@ -1413,7 +1406,7 @@ def _test_lift(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_lift() @@ -1444,7 +1437,7 @@ def _test_restriction(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_restriction() @@ -1459,7 +1452,7 @@ def _test_extension(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_extension() @@ -1475,7 +1468,7 @@ def _test_change_domain(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_change_domain() @@ -1490,7 +1483,7 @@ def _test_no_infinite_nonzero(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_no_infinite_nonzero() @@ -1510,7 +1503,7 @@ def _test_residue_field(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_residue_field() @@ -1545,7 +1538,7 @@ def _test_ge(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_ge() @@ -1567,7 +1560,7 @@ def _test_le(self, **options): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_le() @@ -1591,7 +1584,7 @@ class ScaleAction(Action): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: from operator import mul sage: v.parent().get_action(IntegerRing, mul, self_on_left=False) @@ -1603,7 +1596,7 @@ def _call_(self, s, v): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: 3*v # indirect doctest 3 * 5-adic valuation @@ -1618,7 +1611,7 @@ class InverseScaleAction(Action): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: from operator import div sage: v.parent().get_action(IntegerRing, div, self_on_left=True) @@ -1630,7 +1623,7 @@ def _call_(self, v, s): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v/3 # indirect doctest 1/3 * 5-adic valuation diff --git a/value_group.py b/value_group.py index f5f4e66150b..cdd7c280fb6 100644 --- a/value_group.py +++ b/value_group.py @@ -6,7 +6,7 @@ EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.value_group() Additive Abelian Group generated by 1 @@ -26,13 +26,6 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** - -# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) -import sys, os -if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: - sys.path.append(os.getcwd()) - sys.path.append(os.path.dirname(os.getcwd())) - from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.rings.all import QQ, ZZ, infinity @@ -45,7 +38,7 @@ class DiscreteValuationCodomain(UniqueRepresentation, Parent): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: C = DiscreteValuationCodomain(); C Codomain of Discrete Valuations @@ -58,7 +51,7 @@ def __init__(self): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: isinstance(pAdicValuation(QQ, 2).codomain(), DiscreteValuationCodomain) True @@ -78,7 +71,7 @@ def _element_constructor_(self, x): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValuationCodomain()(0) 0 sage: DiscreteValuationCodomain()(infinity) @@ -101,7 +94,7 @@ def _repr_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValuationCodomain() # indirect doctest Codomain of Discrete Valuations @@ -129,7 +122,7 @@ class DiscreteValueGroup(UniqueRepresentation, Parent): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: D1 = DiscreteValueGroup(0); D1 Trivial Additive Abelian Group sage: D2 = DiscreteValueGroup(4/3); D2 @@ -152,7 +145,7 @@ def __classcall__(cls, generator): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueGroup(1) is DiscreteValueGroup(-1) True @@ -165,7 +158,7 @@ def __init__(self, generator): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: isinstance(DiscreteValueGroup(0), DiscreteValueGroup) True @@ -188,7 +181,7 @@ def _element_constructor_(self, x): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueGroup(0)(0) 0 sage: DiscreteValueGroup(0)(1) @@ -215,7 +208,7 @@ def _repr_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueGroup(0) # indirect doctest Trivial Additive Abelian Group @@ -234,7 +227,7 @@ def __add__(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: D = DiscreteValueGroup(1/2) sage: D + 1/3 Additive Abelian Group generated by 1/6 @@ -266,7 +259,7 @@ def _mul_(self, other, switch_sides=False): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: D = DiscreteValueGroup(1/2) sage: 1/2 * D Additive Abelian Group generated by 1/4 @@ -289,7 +282,7 @@ def index(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueGroup(3/8).index(DiscreteValueGroup(3)) 8 sage: DiscreteValueGroup(3).index(DiscreteValueGroup(3/8)) @@ -325,7 +318,7 @@ def numerator(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueGroup(3/8).numerator() 3 @@ -338,7 +331,7 @@ def denominator(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueGroup(3/8).denominator() 8 @@ -351,7 +344,7 @@ def gen(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueGroup(-3/8).gen() 3/8 @@ -364,7 +357,7 @@ def some_elements(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueGroup(-3/8).some_elements() [3/8, -3/8, 0, 42, 3/2, -3/2, 9/8, -9/8] @@ -377,7 +370,7 @@ def is_trivial(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueGroup(-3/8).is_trivial() False sage: DiscreteValueGroup(0).is_trivial() @@ -397,7 +390,7 @@ class DiscreteValueSemigroup(UniqueRepresentation, Parent): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: D1 = DiscreteValueSemigroup(0); D1 Trivial Additive Abelian Semigroup sage: D2 = DiscreteValueSemigroup(4/3); D2 @@ -423,7 +416,7 @@ def __classcall__(cls, generators): generators and drop generators that are trivially contained in the semigroup generated by the remaining generators:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueSemigroup([1,2]) is DiscreteValueSemigroup([1]) True @@ -456,7 +449,7 @@ def __init__(self, generators): r""" TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: isinstance(DiscreteValueSemigroup([0]), DiscreteValueSemigroup) True @@ -484,6 +477,7 @@ def _solve_linear_program(self, target): EXAMPLES:: + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: D = DiscreteValueSemigroup([2,3,5]) sage: D._solve_linear_program(12) {0: 1, 1: 0, 2: 2} @@ -533,7 +527,7 @@ def _element_constructor_(self, x): TESTS:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueSemigroup([])(0) 0 sage: DiscreteValueSemigroup([])(1) @@ -560,7 +554,7 @@ def _repr_(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueSemigroup(0) # indirect doctest Trivial Additive Abelian Semigroup @@ -579,7 +573,7 @@ def __add__(self, other): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: D = DiscreteValueSemigroup(1/2) sage: D + 1/3 Additive Abelian Semigroup generated by 1/3, 1/2 @@ -611,7 +605,7 @@ def _mul_(self, other, switch_sides=False): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: D = DiscreteValueSemigroup(1/2) sage: 1/2 * D Additive Abelian Semigroup generated by 1/4 @@ -630,7 +624,7 @@ def gens(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueSemigroup(-3/8).gens() (-3/8,) @@ -643,7 +637,7 @@ def some_elements(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: list(DiscreteValueSemigroup([-3/8,1/2]).some_elements()) [0, -3/8, 1/2, ...] @@ -663,7 +657,7 @@ def is_trivial(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueSemigroup(-3/8).is_trivial() False sage: DiscreteValueSemigroup([]).is_trivial() @@ -679,7 +673,7 @@ def is_group(self): EXAMPLES:: - sage: from mac_lane import * # optional: standalone + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueSemigroup(1).is_group() False sage: D = DiscreteValueSemigroup([-1, 1]) From a2d87677349ce01f2c2daf4da74e86bc6d149026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 15 Apr 2017 12:31:23 +0200 Subject: [PATCH 224/740] Repair broken is_injective/is_injective checks --- __init__.py | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index e63939a904c..79e4044ebb6 100644 --- a/__init__.py +++ b/__init__.py @@ -251,12 +251,45 @@ def is_injective(self): sage: QQ.coerce_map_from(ZZ).is_injective() # indirect doctest True + sage: Hom(ZZ,QQ['x']).natural_map().is_injective() + True + + """ + from sage.categories.fields import Fields + if self.domain() in Fields(): return True + coercion = self.codomain().coerce_map_from(self.domain()) + if coercion is not None: + try: + return coercion.is_injective() + except NotImplementedError: + # PolynomialBaseringInjection does not implement is_surjective/is_injective + if isinstance(coercion, sage.categories.map.FormalCompositeMap): + if all([f.is_injective() for f in list(coercion)]): + return True + except AttributeError: # DefaultConvertMap_unique does not implement is_injective/surjective at all + pass + raise NotImplementedError + def is_surjective(self): + r""" + TESTS:: + + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone + sage: QQ.coerce_map_from(ZZ).is_surjective() # indirect doctest + False + """ from sage.categories.fields import Fields if self.domain() in Fields(): return True coercion = self.codomain().coerce_map_from(self.domain()) if coercion is not None: - return coercion.is_injective() + try: + return coercion.is_surjective() + except AttributeError: # DefaultConvertMap_unique does not implement is_injective/surjective at all + # PolynomialBaseringInjection does not implement is_surjective/is_injective (TODO: fix the logic of FormalCompositeMap, i.e., postpone without_bij) + if isinstance(coercion, sage.categories.map.FormalCompositeMap): + if all([f.is_surjective() for f in list(coercion)]): + return True + pass raise NotImplementedError sage.rings.homset.RingHomset_generic.natural_map = patch_is_injective(sage.rings.homset.RingHomset_generic.natural_map, {sage.rings.morphism.RingHomomorphism_coercion: (lambda coercion: RingHomomorphism_coercion_patched(coercion.parent()))}) @@ -322,6 +355,16 @@ def is_injective(self): """ return True + def is_surjective(self): + r""" + TESTS:: + + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone + sage: QQ.coerce_map_from(ZZ).is_surjective() + False + + """ + return False from sage.rings.all import QQ QQ.coerce_map_from = patch_is_injective(QQ.coerce_map_from, {sage.rings.rational.Z_to_Q: (lambda morphism: Z_to_Q_patched())}) From 5f428487bebd31d680ec44bee66214a8ca238902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 25 Apr 2017 22:12:02 +0200 Subject: [PATCH 225/740] Clarify that the first approximation must have a unique augmentation to a factor --- limit_valuation.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/limit_valuation.py b/limit_valuation.py index a465b54f966..79113ed8c6f 100644 --- a/limit_valuation.py +++ b/limit_valuation.py @@ -363,9 +363,10 @@ class MacLaneLimitValuation(LimitValuation_generic, InfiniteDiscretePseudoValuat This uses the MacLane algorithm to compute the next element in the limit. - It starts from a first valuation ``approximation`` whose uniformizer must - be a uniformizer of the limit and whose residue field must contain the - residue field of the limit. + It starts from a first valuation ``approximation`` which has a unique + augmentation that sends `G` to infinity and whose uniformizer must be a + uniformizer of the limit and whose residue field must contain the residue + field of the limit. EXAMPLES:: From e3358f25a6fa39ee9d004c64ba63c2c7a4b9bf15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 29 Apr 2017 22:23:15 +0200 Subject: [PATCH 226/740] Monkey-patch another ZZ injection --- __init__.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/__init__.py b/__init__.py index 79e4044ebb6..0dc0ef8703a 100644 --- a/__init__.py +++ b/__init__.py @@ -254,9 +254,16 @@ def is_injective(self): sage: Hom(ZZ,QQ['x']).natural_map().is_injective() True + sage: R. = ZZ[] + sage: R. = R.quo(x^2+x+1) + sage: R.coerce_map_from(ZZ).is_injective() + True + """ from sage.categories.fields import Fields + # this should be implemented as far down as possible if self.domain() in Fields(): return True + if self.domain() == sage.all.ZZ and self.codomain().characteristic() == 0: return True coercion = self.codomain().coerce_map_from(self.domain()) if coercion is not None: try: @@ -268,7 +275,9 @@ def is_injective(self): return True except AttributeError: # DefaultConvertMap_unique does not implement is_injective/surjective at all pass + raise NotImplementedError + def is_surjective(self): r""" TESTS:: @@ -290,7 +299,9 @@ def is_surjective(self): if all([f.is_surjective() for f in list(coercion)]): return True pass + raise NotImplementedError + sage.rings.homset.RingHomset_generic.natural_map = patch_is_injective(sage.rings.homset.RingHomset_generic.natural_map, {sage.rings.morphism.RingHomomorphism_coercion: (lambda coercion: RingHomomorphism_coercion_patched(coercion.parent()))}) # a morphism of polynomial rings which is induced by a ring morphism on the base is injective if the morphis on the base is From b3521574201284cac3dec4e36acf04a74e187e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 29 Apr 2017 22:23:48 +0200 Subject: [PATCH 227/740] Fix typo --- developing_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/developing_valuation.py b/developing_valuation.py index f51f9878955..ff1117e04ab 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -203,7 +203,7 @@ def _quo_rem(self, f): def newton_polygon(self, f, valuations=None): r""" - Return the newton polygon the `\phi`-adic development of ``f``. + Return the newton polygon of the `\phi`-adic development of ``f``. INPUT:: From c85cd5fd46eb43aca24b6718a9d95b725d0272c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 6 May 2017 22:42:07 +0200 Subject: [PATCH 228/740] Rings embed into their polynomial rings and orders often embed --- __init__.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 0dc0ef8703a..6de80ceaeef 100644 --- a/__init__.py +++ b/__init__.py @@ -259,11 +259,21 @@ def is_injective(self): sage: R.coerce_map_from(ZZ).is_injective() True + sage: R. = QQbar[] + sage: R.coerce_map_from(QQbar).is_injective() + True + """ - from sage.categories.fields import Fields + from sage.categories.all import Fields, IntegralDomains + from sage.rings.number_field.order import AbsoluteOrder + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing # this should be implemented as far down as possible if self.domain() in Fields(): return True if self.domain() == sage.all.ZZ and self.codomain().characteristic() == 0: return True + if isinstance(self.domain(), AbsoluteOrder) and self(self.domain().gen()) != 0 and self.codomain() in IntegralDomains(): return True + # this should be implemented somewhere else + if is_PolynomialRing(self.codomain()) and self.codomain().base_ring() is self.domain(): + return True coercion = self.codomain().coerce_map_from(self.domain()) if coercion is not None: try: From b1ff317c8bbdbff082eb657ca3949396c0089b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 6 May 2017 22:42:41 +0200 Subject: [PATCH 229/740] removed typos --- valuation.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/valuation.py b/valuation.py index f875554ca39..65091ca3555 100644 --- a/valuation.py +++ b/valuation.py @@ -148,7 +148,7 @@ def _cmp_(self, other): False """ - raise NotImplementedError("No total order for these valuations.") + raise NotImplementedError("No total order for these valuations") def _richcmp_(self, other, op): r""" @@ -202,7 +202,7 @@ def _richcmp_(self, other, op): return self >= other and not (self <= other) if op == 5: # >= return self._ge_(other) - raise NotImplementedError("Operator not implemented for this valuation.") + raise NotImplementedError("Operator not implemented for this valuation") def _eq_(self, other): r""" @@ -293,7 +293,7 @@ def _ge_(self, other): from scaled_valuation import ScaledValuation_generic if isinstance(other, ScaledValuation_generic): return other <= self - raise NotImplementedError("Operator not implemented for this valuation.") + raise NotImplementedError("Operator not implemented for this valuation") # Remove the default implementation of Map.__reduce__ that does not play # nice with factories (a factory, does not override Map.__reduce__ because @@ -636,11 +636,11 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru A similar example:: - sage: R. = QQ[] - sage: v = pAdicValuation(QQ, 3) - sage: G = (x^3 + 3)^3 - 81 - sage: v.mac_lane_approximants(G) # optional: integrated - [[ Gauss valuation induced by 3-adic valuation, v(x) = 1/3, v(x^3 + 3*x + 3) = 13/9 ]] + sage: R. = QQ[] + sage: v = pAdicValuation(QQ, 3) + sage: G = (x^3 + 3)^3 - 81 + sage: v.mac_lane_approximants(G) # optional: integrated + [[ Gauss valuation induced by 3-adic valuation, v(x) = 1/3, v(x^3 + 3*x + 3) = 13/9 ]] Another problematic case:: From cea62a6e5fb82ee8af6d6d660e2e5e42413ea40f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 6 May 2017 22:42:50 +0200 Subject: [PATCH 230/740] improved error message --- valuation.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/valuation.py b/valuation.py index 65091ca3555..6e16de05aae 100644 --- a/valuation.py +++ b/valuation.py @@ -660,6 +660,15 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 2*x^2 + 3/2*theta^4 + theta^3 + 5*theta + 1) = 5/3 ], [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 2*x^2 + theta^4 + theta^3 + 1) = 5/3 ]] + An easy case that produced the wrong error at some point:: + + sage: R. = QQ[] + sage: v = pAdicValuation(QQ, 2) + sage: v.mac_lane_approximants(x^2 - 1/2) + Traceback (most recent call last): + ... + ValueError: G must be integral. + """ R = G.parent() if R.base_ring() is not self.domain(): @@ -671,6 +680,9 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru from sage.rings.all import infinity from gauss_valuation import GaussValuation + if not all([self(c) >= 0 for c in G.coefficients()]): + raise ValueError("G must be integral") + if require_maximal_degree: # we can only assert maximality of degrees when E and F are final require_final_EF = True From 0c113610dadd65faedfb4744bbbebb4c81b8b12d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 8 May 2017 13:14:44 +0200 Subject: [PATCH 231/740] fix doctests --- __init__.py | 2 +- valuation.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/__init__.py b/__init__.py index 6de80ceaeef..4527ba01205 100644 --- a/__init__.py +++ b/__init__.py @@ -256,7 +256,7 @@ def is_injective(self): sage: R. = ZZ[] sage: R. = R.quo(x^2+x+1) - sage: R.coerce_map_from(ZZ).is_injective() + sage: Hom(ZZ,R).natural_map().is_injective() True sage: R. = QQbar[] diff --git a/valuation.py b/valuation.py index 6e16de05aae..6eba38b6a7a 100644 --- a/valuation.py +++ b/valuation.py @@ -667,7 +667,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: v.mac_lane_approximants(x^2 - 1/2) Traceback (most recent call last): ... - ValueError: G must be integral. + ValueError: G must be integral """ R = G.parent() From 4bfdbb8804cd7892ece56e3a1e260d0bd7d9d614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 11 May 2017 12:06:26 +0200 Subject: [PATCH 232/740] Implement more .change_domain() to allow for faster conversion of valuations --- augmented_valuation.py | 21 +++++++++++++++++++++ inductive_valuation.py | 21 +++++++++++++++++++++ padic_valuation.py | 23 +++++++++++++++++++---- 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/augmented_valuation.py b/augmented_valuation.py index 4f140850a24..4ac2094ee41 100644 --- a/augmented_valuation.py +++ b/augmented_valuation.py @@ -824,6 +824,27 @@ def is_negative_pseudo_valuation(self): """ return False + def change_domain(self, ring): + r""" + Return this valuation over ``ring``. + + EXAMPLES: + + We can change the domain of an augmented valuation even if there is no coercion between rings:: + + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone + sage: R. = GaussianIntegers()[] + sage: v = GaussValuation(R, pAdicValuation(GaussianIntegers(), 2)) + sage: v = v.augmentation(x, 1) + sage: v.change_domain(QQ['x']) + [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ] + + """ + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + if is_PolynomialRing(ring) and ring.ngens() == 1 and ring.variable_name() == self.domain().variable_name(): + return self._base_valuation.change_domain(ring).augmentation(self.phi().change_ring(ring.base_ring()), self._mu, check=False) + return super(AugmentedValuation_base, self).change_domain(ring) + class FinalAugmentedValuation(AugmentedValuation_base, FinalInductiveValuation): r""" diff --git a/inductive_valuation.py b/inductive_valuation.py index 584c94f336d..ff40530cf42 100644 --- a/inductive_valuation.py +++ b/inductive_valuation.py @@ -1516,6 +1516,27 @@ def __init__(self, parent, base_valuation): FinalInductiveValuation.__init__(self, parent, base_valuation) InfiniteDiscretePseudoValuation.__init__(self, parent) + def change_domain(self, ring): + r""" + Return this valuation over ``ring``. + + EXAMPLES: + + We can turn an infinite valuation into a valuation on the quotient:: + + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = v.augmentation(x^2 + x + 1, infinity) + sage: w.change_domain(R.quo(x^2 + x + 1)) + 2-adic valuation + + """ + from sage.rings.polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing + if is_PolynomialQuotientRing(ring) and ring.base() is self.domain() and ring.modulus() == self.phi(): + return self.restriction(self.domain().base())._extensions_to_quotient(ring, approximants=[self])[0] + return super(InfiniteInductiveValuation, self).change_domain(ring) + def _lift_to_maximal_precision(c): r""" diff --git a/padic_valuation.py b/padic_valuation.py index 14904b9bfd8..9768fb9df7f 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -719,6 +719,24 @@ def change_domain(self, ring): """ return pAdicValuation(ring, self.p()) + def _extensions_to_quotient(self, ring, approximants=None): + r""" + Return the extensions of this valuation to an integral quotient over + the domain of this valuation. + + EXAMPLES:: + + sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone + sage: R. = QQ[] + sage: pAdicValuation(QQ, 2)._extensions_to_quotient(R.quo(x^2 + x + 1)) + [2-adic valuation] + + """ + from valuation_space import DiscretePseudoValuationSpace + parent = DiscretePseudoValuationSpace(ring) + approximants = approximants or self.mac_lane_approximants(ring.modulus().change_ring(self.domain()), assume_squarefree=True) + return [pAdicValuation(ring, approximant, approximants) for approximant in approximants] + def extensions(self, ring): r""" Return the extensions of this valuation to ``ring``. @@ -765,10 +783,7 @@ def extensions(self, ring): if ring.base_ring() is self.domain(): from sage.categories.all import IntegralDomains if ring in IntegralDomains(): - from valuation_space import DiscretePseudoValuationSpace - parent = DiscretePseudoValuationSpace(ring) - approximants = self.mac_lane_approximants(ring.modulus().change_ring(self.domain()), assume_squarefree=True) - return [pAdicValuation(ring, approximant, approximants) for approximant in approximants] + return self._extensions_to_quotient(ring) else: return sum([w.extensions(ring) for w in self.extensions(ring.base_ring())], []) from sage.rings.number_field.number_field import is_NumberField From 6482692173875c271f854c4da4a9cb10c5f2bc79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 11 May 2017 12:06:47 +0200 Subject: [PATCH 233/740] Compute Newton polygons non-lazily it does not make a difference since all the values are needed anyway but it makes it easier to see in the profiler that the actual Newton polygon computation is not responsible for CPU time. --- developing_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/developing_valuation.py b/developing_valuation.py index ff1117e04ab..57ee6687642 100644 --- a/developing_valuation.py +++ b/developing_valuation.py @@ -231,7 +231,7 @@ def newton_polygon(self, f, valuations=None): from sage.geometry.newton_polygon import NewtonPolygon if valuations is None: valuations = self.valuations(f) - return NewtonPolygon(enumerate(valuations)) + return NewtonPolygon(list(enumerate(valuations))) def _call_(self, f): r""" From a31fd705ed35084a484833cfdac4961484ba4ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 11 May 2017 12:07:54 +0200 Subject: [PATCH 234/740] remove debug code --- padic_valuation.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/padic_valuation.py b/padic_valuation.py index 9768fb9df7f..cde2f182fea 100644 --- a/padic_valuation.py +++ b/padic_valuation.py @@ -1110,9 +1110,6 @@ def _call_(self, x): # of a rational zero than when computing the valuation of another # small rational. Special casing this is a factor 100 faster. return infinity - #if x.global_height() > 128: - # import pdb - # pdb.set_trace() return x.valuation(self._p) def uniformizer(self): From 925d8fa225a38c76c3c8dd9d35f51b8e5416f7ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 11 May 2017 12:08:11 +0200 Subject: [PATCH 235/740] fix typo in comment --- valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/valuation.py b/valuation.py index 6eba38b6a7a..4a6848ee797 100644 --- a/valuation.py +++ b/valuation.py @@ -872,7 +872,7 @@ def mac_lane_approximant(self, G, valuation, approximants = None): if valuation.restriction(valuation.domain().base_ring()) is not self: raise ValueError - # Check thet valuation is an approximant for a valuation + # Check that valuation is an approximant for a valuation # on domain that extends its restriction to the base field. from sage.rings.all import infinity if valuation(G) is not infinity: From c4a7b1a1b42b3736aa9d2dfac00759ad91351ef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 12 May 2017 19:18:14 +0200 Subject: [PATCH 236/740] Added a tutorial in the README --- README.md | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 202 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f74f903dfdb..584e6da39f9 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,208 @@ -Mac Lane infrastructure for discrete (pseudo-)valuations which should work on an unmodified Sage 8.0 or later. +Mac Lane's Algorithms in Sage +============================= +This package implements most of Mac Lane's algorithms [1,2] and related +structures to represent discrete valuations and discrete pseudo-valuations on +rings in Sage. +The package should run on an unmodified Sage 8.0 and can be imported with ``` -$ sage sage: from mac_lane import * -sage: pAdicValuation(QQ, 2) -2-adic valuation ``` To run the included tests, execute `sage -tp --optional=sage,standalone mac_lane/`. + +High-Level Interface +==================== +Valuations can be defined conveniently on some Sage rings such as p-adic rings and function fields. + +p-adic valuations +----------------- +Valuations on number fields can be easily specified if they uniquely extend the valuation of a rational prime: +``` +sage: v = pAdicValuation(QQ, 2) +sage: v(1024) +10 +``` + +They are normalized such that the rational prime has valuation 1. +``` +sage: K. = NumberField(x^2 + x + 1) +sage: v = pAdicValuation(K, 2) +sage: v(1024) +10 +``` + +If there are multiple valuations over a prime, they can be obtained by extending a valuation from a smaller ring. +``` +sage: K. = NumberField(x^2 + x + 1) +sage: v = pAdicValuation(QQ, 7) +sage: v.extensions(K) +[[ 7-adic valuation, v(x + 3) = 1 ]-adic valuation, +[ 7-adic valuation, v(x + 5) = 1 ]-adic valuation] +sage: w,ww = _ +sage: w(a + 3), ww(a + 3) +(1, 0) +sage: w(a + 5), ww(a + 5) +(0, 1) +``` + +Function Field valuations +------------------------- +Similarly, valuations can be defined on function fields: +``` +sage: K. = FunctionField(QQ) +sage: v = FunctionFieldValuation(K, x) +sage: v(1/x) +-1 + +sage: v = FunctionFieldValuation(K, 1/x) +sage: v(1/x) +1 +``` + +On extensions of function fields, valuations can be specified explicitly by +providing a prime on the underlying rational function field when the extension +is unique:: +``` +sage: K. = FunctionField(QQ) +sage: R. = K[] +sage: L. = K.extension(y^2 - x) +sage: v = FunctionFieldValuation(L, x) +sage: v(x) +1 +``` + +Valuations can also be extended from smaller function fields. +``` +sage: K. = FunctionField(QQ) +sage: v = FunctionFieldValuation(K, x - 4) +sage: R. = K[] +sage: L. = K.extension(y^2 - x) +sage: v.extensions(L) +[ (x - 4)-adic valuation, v(y - 2) = 1 ]-adic valuation, + [ (x - 4)-adic valuation, v(y + 2) = 1 ]-adic valuation] +``` + +Low-Level Interface +=================== + +Mac Lane valuations +------------------- +Internally, all the above is backed by the algorithms described in [1,2]. Let +us consider the extensions of `FunctionFieldValuation(K, x - 4)` to the field +`L` above to outline how this works internally. + +First, the valuation on K is induced by a valuation on ℚ[x]. To construct this +valuation, we start from the trivial valuation on ℚ and consider its induced +Gauss valuation on ℚ[x], i.e., the valuation that assigns to a polynomial the +minimum of the coefficient valuations. +``` +sage: R. = QQ[] +sage: v = TrivialValuation(QQ) +sage: v = GaussValuation(R, v) +``` +The Gauss valuation can be augmented by specifying that x - 4 has valuation 1. +``` +sage: v = v.augmentation(x - 4, 1); v +[ Gauss valuation induced by Trivial valuation on Rational Field, v(x - 4) = 1 ] +``` + +This valuation then extends uniquely to the fraction field. +``` +sage: K. = FunctionField(QQ) +sage: v = v.extension(K); v +(x - 4)-adic valuation +``` + +Over the function field we repeat the above process, i.e., we define the Gauss +valuation induced by it and augment it to approximate an extension to L. +``` +sage: R. = K[] +sage: w = GaussValuation(R, v) +sage: w = w.augmentation(y - 2, 1); w +[ Gauss valuation induced by (x - 4)-adic valuation, v(y - 2) = 1 ] +sage: L. = K.extension(y^2 - x) +sage: ww = w.extension(L); ww +[ (x - 4)-adic valuation, v(y - 2) = 1 ]-adic valuation +``` + +Limit valuations +---------------- +In the previous example the final valuation `ww` is not merely given by evaluating `w` on the ring `K[y]`. +``` +sage: ww(y^2 - x) ++Infinity +sage: y = R.gen() +sage: w(y^2 - x) +1 +``` + +Instead `ww` is given by a limit, i.e., an infinite sequence of augmentations of valuations. +``` +sage: ww._base_valuation +[ Gauss valuation induced by (x - 4)-adic valuation, v(y - 2) = 1 , … ] +``` + +The terms of this infinite sequence are computed on demand. +``` +sage: ww._base_valuation._approximation +[ Gauss valuation induced by (x - 4)-adic valuation, v(y - 2) = 1 ] +sage: ww(y - 1/4*x - 1) +2 +sage: ww._base_valuation._approximation +[ Gauss valuation induced by (x - 4)-adic valuation, v(y - 1/4*x - 1) = 2 ] +``` + +Non-classical valuations +------------------------ +Using the low-level interface we are not limited to classical valuations on +function fields that correspond to points on the corresponding curves. Instead +we can start with a non-trivial valuation on the field of constants. +``` +sage: v = pAdicValuation(QQ, 2) +sage: R. = QQ[] +sage: w = GaussValuation(R, v) # v is not trivial +sage: K. = FunctionField(QQ) +sage: w = w.extension(K) +sage: w.residue_field() +Fraction Field of Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) +``` + +Mac Lane Approximants +===================== +The main tool underlying this package is an algorithm by Mac Lane to compute, +starting from a Gauss valuation on a polynomial ring and a monic squarefree +polynomial G, approximations to the limit valuation which send G to infinity. +``` +sage: v = pAdicValuation(QQ, 2) +sage: R. = QQ[] +sage: f = x^5 + 3*x^4 + 5*x^3 + 8*x^2 + 6*x + 12 +sage: v.mac_lane_approximants(f) +[[ Gauss valuation induced by 2-adic valuation, v(x^2 + x + 1) = 3 ], + [ Gauss valuation induced by 2-adic valuation, v(x) = 1/2 ], + [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ]] +``` + +Note that from these approximants one can already see the residual degrees and +ramification indices of the corresponding extensions. The approximants can be +pushed to arbitrary precision. +``` +sage: sage: v.mac_lane_approximants(f, required_precision=10) +[[ Gauss valuation induced by 2-adic valuation, v(x^2 + 193*x + 13/21) = 10 ], + [ Gauss valuation induced by 2-adic valuation, v(x + 86) = 10 ], + [ Gauss valuation induced by 2-adic valuation, v(x) = 1/2, v(x^2 + 36/11*x + 2/17) = 11 ]] +``` + +Note that in the limit they are factors of `f`. + +References +========== + +[1] Mac Lane, S. (1936). A construction for prime ideals as absolute values of +an algebraic field. Duke Mathematical Journal, 2(3), 492-510. + +[2] MacLane, S. (1936). A construction for absolute values in polynomial rings. +Transactions of the American Mathematical Society, 40(3), 363-395. + +[3] Rüth, J. (2014). Models of Curves and Valuations (PhD thesis) Chapter 4 +"Mac Lane Valuations". From 30437170dcb4ca118a55c9ad8f236d7f3d7227fe Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sat, 20 May 2017 10:22:05 -0700 Subject: [PATCH 237/740] File shifted_tableaux.py added in combinat. --- src/sage/combinat/shifted_tableaux.py | 784 ++++++++++++++++++++++++++ 1 file changed, 784 insertions(+) create mode 100644 src/sage/combinat/shifted_tableaux.py diff --git a/src/sage/combinat/shifted_tableaux.py b/src/sage/combinat/shifted_tableaux.py new file mode 100644 index 00000000000..92a8b5da12b --- /dev/null +++ b/src/sage/combinat/shifted_tableaux.py @@ -0,0 +1,784 @@ +from __future__ import print_function, absolute_import +from six import add_metaclass +from six.moves import range, zip, map + +import numpy as np + +from sage.combinat.partition import Partition +from sage.combinat.permutation import Permutation +from sage.combinat.posets.posets import Poset +from sage.combinat.tableau import Tableaux + +from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from sage.functions.other import factorial +from sage.misc.cachefunc import cached_method +from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass +from sage.misc.misc_c import prod +from sage.misc.prandom import randrange +from sage.rings.integer import Integer +from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets +from sage.sets.family import Family +from sage.sets.non_negative_integers import NonNegativeIntegers +from sage.structure.list_clone import ClonableArray +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation + + +@add_metaclass(InheritComparisonClasscallMetaclass) +class ShiftedPrimedTableau(ClonableArray): + """ + A shifted primed tableau. + + EXAMPLES:: + + sage: T = ShiftedPrimedTableaux([4,2]) + sage: T([[1,"2'","3'",3],[2,"3'"]])[1] + [2, 3'] + sage: t = ShiftedPrimedTableau([[1,1.5,2.5,3],[0,2,2.5]]) + sage: t[1] + [2, 3'] + sage: t = ShiftedPrimedTableau([[1,2,2.5,3],[0,2,2.5]]) + ValueError: not a primed tableau + """ + + @staticmethod + def __classcall_private__(cls, t): + + """ + This ensures that a shifted tableau is only ever constructed + as an ``element_class`` call of an appropriate parent. + EXAMPLES:: + + sage: data = [[1,2',3',3],[2,3']] + sage: t = ShiftedPrimedTableau(data) + sage: T = ShiftedPrimedTableaux([4,2]) + sage: t == T(data) + True + """ + + if isinstance(t, cls): + return t + + #Accounting for zeros at the beginning and at the end of a row + i=0 + while i < len(t): + row = t[i] + try: + while row[0]==0 or row[0]=='0': + row.pop(0) + while row[-1]==0 or row[-1]=='0': + row.pop(-1) + except IndexError: + t.remove(t[i]) + continue + t[i] = row + i += 1 + + shape = [len(row) for row in t] + return ShiftedPrimedTableaux(shape = shape)(t) + + + def __init__(self,parent, t): + """ + Preprocessing of list t and initializing a shifted tableau. + + TESTS:: + + sage: s = ShiftedPrimedTableau([[1,"2'","3'",3],[2,"3'"]]) + sage: t = ShiftedPrimedTableaux([4,2])([[1,"2p","3p",3],[0, 2,"3p"]]) + sage: s==t + True + sage: t.parent() + Shifted primed tableaux of shape [4, 2] + sage: s.parent() + Shifted primed tableaux of shape [4, 2] + sage: r = ShiftedPrimedTableaux([4, 2])(s); r.parent() + Shifted primed tableaux of shape [4, 2] + sage: s is t # identical shifted tableaux are distinct objects + False + + A shifted primed tableau is shallowly immutable. The entries + themselves may be mutable objects, though in that case the + resulting ShiftedPrimedTableau should be unhashable. + + sage: t = ShiftedPrimedTableau([[1,"2p","3p",3],[2,"3p"]]) + sage: t0 = t[0] + sage: t0[1] = 3 + Traceback (most recent call last): + ... + TypeError: 'tuple' object does not support item assignment + sage: t[0][1] = 3 + Traceback (most recent call last): + ... + TypeError: 'tuple' object does not support item assignment + """ + if isinstance(t, np.ndarray): + t = t.tolist() + + # Preprocessing list t for primes and other symbols + for i in range(len(t)): + row = t[i] + for j in range(len(row)): + string = str(row[j]) + if string[-1] == "'" and string[:-1].isdigit() == True: + row[j] = float(float(string[:-1]) - .5) + continue + if string[-1] == "p" and string[:-1].isdigit() == True: + row[j] = float(float(string[:-1]) - .5) + continue + try: + row[j] = float(string) + except ValueError: + row.pop(string) + t[i] = row + + #Accounting for zeros at the beginning and at the end of a row + i=0 + while i < len(t): + row = t[i] + try: + while row[0]==0: + row.pop(0) + while row[-1]==0: + row.pop(-1) + except IndexError: + t.remove(t[i]) + continue + t[i] = row + i += 1 + + # Normalize t to be a list of tuples. + t = [tuple(_) for _ in t] + + if isinstance(t, ShiftedPrimedTableau): + # Since we are (supposed to be) immutable, we can share the underlying data + ClonableArray.__init__(self, parent, t) + return + + ClonableArray.__init__(self, parent, t) + # This dispatches the input verification to the :meth:`check` + # method. + + + def to_matrix(self): + array = [] + m = len(self[0]) + for i in range(len(self)): + array.append([0]*i + self[i] + [0]*(m-i-len(self[i]))) + array = np.array(array) + return array + + def check(self): + """ + Check that ``self`` is a valid primed tableaux. + + EXAMPLES:: + + sage: T = ShiftedPrimedTableaux([4,2]) + sage: t = T([[1,'2p',2,2],[2,'3p']]) + sage: t.check() + """ + if [len(_) for _ in self] not in StrictPartitions(): + raise ValueError('shape must be a strict partition') + for i,row in enumerate(self): + if i > 0: + if not all(val > self[i-1][j+1] for j,val in enumerate(row) if val in ZZ): + raise ValueError('column is not strictly increasing in non-primes') + if not all(val >= self[i-1][j+1] for j,val in enumerate(row) if val not in ZZ): + raise ValueError('column is not weakly increasing in primes') + if not all(row[j] <= row[j+1] for j in range(len(row)-1) if row[j] in ZZ): + raise ValueError('row is not weakly increasing in non-primes') + if not all(row[j] < row[j+1] for j in range(len(row)-1) if row[j] not in ZZ): + raise ValueError('row is not strictly increasing in primes') + if not all(row[0] in ZZ for row in self): + raise ValueError('diagonal elements must be non-primes') + + + def _repr_(self): + + string_list = '' + for i,row in enumerate(self): + string_list += ' '*i + for val in row: + if val in ZZ: + string_list += str(int(val))+' ' + else: + string_list += str(int(val+.5))+"'" + string_list += '\n' + + return (string_list) + + def shape(self): + return + def weight(self): + return + def entry(self,position): + return + def reading_word(self): + return + + +class ShiftedPrimedTableaux(UniqueRepresentation, Parent): + r""" + A factory for the various classes of shifted standard tableaux. + + INPUT: + + - a weight and/or a partition + + OUTPUT: + + - with no argument, the class of all primed tableaux + + - with a partition argument, the class of all primed tableaux of that + shape (infinite set if we don't specify the weight) + + - with a weight argument, the class of all primed tableaux of that + weight (finite set) + + + A primed tableau is a shifted tableau on the alphabet + X' = {1' < 1 < 2' < 2 <...< n' < n} such that + 1). the entries are weakly increasing along rows and columns + 2). a row can't have two repeated primed elements, and a column + can't have two repeated non-primed elements + 3). there are only non-primed elements along the main diagonal + + The weight of a tableau is defined to be the vector with i-th component + equal to the number of letters i and i' in the tableau. + The sum of the entries in the weight vector must be equal to the number + of boxes in the partition. + + + TESTS:: + + sage: ShiftedPrimedTableaux([]) + [] + sage: SPT = ShiftedPrimedTableaux((1,2,2),[3,2]); SPT + Shifted primed tableaux of shape [3, 2] and weight (1,2,2) + sage: SPT.cardinality() + 2 + sage: SPT.list() + [[[1, 2, 3], [4, 5]], [[1, 2, 4], [3, 5]]] + """ + # use Tableaux options + options = Tableaux.options + + @staticmethod + def __classcall_private__(cls, *args, **kwargs): + r""" + This is a factory class which returns the appropriate parent based on + arguments. See the documentation for :class:`ShiftedPrimedTableaux` for + more information. + + TESTS:: + + sage: ShiftedPrimedTableaux() + Shifted primed tableaux + sage: ShiftedPrimedTableaux(3) + Shifted tableaux of size 3 + sage: ShiftedTableaux([2,1]) + Shifted tableaux of shape [2, 1] + sage: ShiftedTableaux(0) + Shifted tableaux of size 0 + + sage: ShiftedTableaux(-1) + Traceback (most recent call last): + ... + ValueError: the argument must be a non-negative integer or a partition + sage: ShiftedTableaux([[1]]) + Traceback (most recent call last): + ... + ValueError: the argument must be a non-negative integer or a partition + """ + weight = None + shape = None + + if 'size' in kwargs and isinstance(kwargs['size'],(list,tuple,Partition)): + shape = Partition(kwargs['size']) + + if 'shape' in kwargs: + shape = Partition(kwargs['shape']) + + if 'weight' in kwargs: + weight = tuple(kwargs['weight']) + + if args: + if isinstance(args[0],tuple) and weight==None: + weight = args[0] + if len(args)>1: + if (isinstance(args[1],(list,Partition)) or args[1]==None) and shape==None: + shape = args[1] + else: + raise ValueError('weight argument must be a tuple and shape argument must be a strictly increasing partition') + + elif isinstance(args[0],(list,Partition)) and shape==None: + shape = args[0] + if len(args)>1: + if (isinstance(args[1],tuple) or args[1]==None) and weight==None: + weight = args[1] + else: + raise ValueError('weight argument must be a tuple and shape argument must be a strictly increasing partition') + else: + raise ValueError('weight argument must be a tuple and shape argument must be a strictly increasing partition') + + if shape is not None: + try: + shape = Partition(shape) + except ValueError: + raise ValueError('{} is not a strict partition'.format(shape)) + + if shape is None and weight is None: + return ShiftedPrimedTableaux_all() + + + elif weight is None and shape in StrictPartitions(): + return ShiftedPrimedTableaux_shape(Partition(shape)) + + elif shape is None: + return ShiftedPrimedTableaux_weight(weight) + + if shape not in StrictPartitions(): + raise ValueError("{} is not a strict partition".format(shape)) + + if sum(shape) != sum(weight): + raise ValueError("the sum of weights should match the sum of parts of the shape") + + return ShiftedPrimedTableaux_weight_shape(weight,shape) + + def __contains__(self, t): + """ + EXAMPLES:: + + """ + if isinstance(t, ShiftedPrimedTableau) or t == []: + return True + + if isinstance(t, np.ndarray): + t = t.tolist() + + # Preprocessing list t for primes and other symbols + for i in range(len(t)): + row = t[i] + for j in range(len(row)): + string = str(row[j]) + if string[-1] == "'" and string[:-1].isdigit() == True: + row[j] = float(float(string[:-1]) - .5) + continue + if string[-1] == "p" and string[:-1].isdigit() == True: + row[j] = float(float(string[:-1]) - .5) + continue + try: + row[j] = float(string) + except ValueError: + row.pop(string) + t[i] = row + + #Accounting for zeros at the beginning and at the end of a row + i=0 + while i < len(t): + row = t[i] + try: + while row[0]==0: + row.pop(0) + while row[-1]==0: + row.pop(-1) + except IndexError: + t.remove(t[i]) + continue + t[i] = row + i += 1 + + # Normalize t to be a list of tuples. + t = [tuple(_) for _ in t] + + for i,row in enumerate(t): + if i > 0: + if not all(val > t[i-1][j+1] for j,val in enumerate(row) if val in ZZ): + return False + if not all(val >= t[i-1][j+1] for j,val in enumerate(row) if val not in ZZ): + return False + if not all(row[j] <= row[j+1] for j in range(len(row)-1) if row[j] in ZZ): + return False + if not all(row[j] < row[j+1] for j in range(len(row)-1) if row[j] not in ZZ): + return False + if not all(row[0] in ZZ for row in t): + return False + + return True + + _is_a = __contains__ + + def an_element(self): + r""" + Return a particular shifted tableaux in the class. + + TESTS:: + + sage: ShiftedTableaux().an_element() + [] + sage: ShiftedTableaux(4).an_element() + [[1, 2, 3, 4]] + sage: ShiftedTableaux([3,1]).an_element() + [[1, 2, 3], [4]] + """ + return self[0] + +class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): + """ + Shifted tableaux of a fixed shape. + """ + Element = ShiftedPrimedTableau + + def __init__(self, shape): + r""" + Initializes the class of semistandard tableaux of shape ``p`` and no + maximum entry. + + TESTS:: + + sage: TestSuite( ShiftedTableaux([3,2,1]) ).run() + """ + Parent.__init__(self, category=FiniteEnumeratedSets()) + self._shape = shape + + def _repr_(self): + """ + Return a string representation of ``self``. + + TESTS:: + + sage: ShiftedTableaux([3,2,1]) # indirect doctest + Shifted tableaux of shape [3, 2, 1] + """ + return "Shifted tableaux of shape {}".format(self._shape) + + def __contains__(self, x): + """ + TESTS:: + + sage: ST421 = ShiftedTableaux([4,2,1]) + sage: all([st in ST421 for st in ST421]) + True + sage: ST42 = ShiftedTableaux([4,2]) + sage: filter(lambda x: x in ST42, ST421) + [] + """ + if isinstance(x, ShiftedPrimedTableau): + return [len(row) for row in x] == self._shape + + return (ShiftedPrimedTableaux.__contains__(self, x) + and [len(row) for row in x] == self._shape) + + def _element_constructor_(self, t): + r""" + Constructs an object from ``t`` as an element of ``self``, if + possible. + + INPUT: + + - ``t`` -- data which can be interpreted as a tableau + + OUTPUT: + + - the corresponding tableau object + + TESTS:: + + sage: ShiftedTableaux([3])([[1,2,3]]).parent() is ShiftedTableaux([3]) + True + sage: ShiftedTableaux([3])([[1,2]]) + Traceback (most recent call last): + ... + ValueError: [[1, 2]] is not an element of Shifted tableaux of shape [3] + """ + if not t in self: + raise ValueError("{} is not an element of {}".format(t, self)) + + return self.element_class(self, t) + + def shape(self): + """ + Return the shape of the shifted tableaux ``self``. + + EXAMPLES:: + + sage: ShiftedTableaux([6,4,3,1]).shape() + [6, 4, 3, 1] + """ + return self._shape + + @cached_method + def size(self): + """ + Return the shape of the shifted tableaux ``self``. + + EXAMPLES:: + + sage: ShiftedTableaux([6,4,3,1]).size() + 14 + """ + return self._shape.size() + +###################### +# Strict Partitions # +###################### +class StrictPartitions(Partitions): + r""" + The class of **strict partitions**. + + A strict partition of an integer `n` is a :class:`Partition` with + distinct parts. + + INPUT: + + - ``n`` -- a non-negative integer, the size of the strict partitions + """ + @staticmethod + def __classcall_private__(cls, size=None): + """ + Normalize the input to ensure a unique representation. + + TESTS:: + + sage: from sage.combinat.partition import StrictPartitions + sage: P = StrictPartitions() + sage: P12 = StrictPartitions(12) + sage: P is P12 + False + """ + if size is not None: + return StrictPartitions_size( ZZ(size) ) + + return StrictPartitions_all() + + def __contains__(self, x): + """ + Return ``True`` if ``x`` is contained in ``self`` and ``False`` + otherwise. + + EXAMPLES:: + + sage: from sage.combinat.partition import StrictPartitions + sage: [5,2] in StrictPartitions() + True + sage: [5,2] in StrictPartitions(7) + True + sage: [5,2] in StrictPartitions(5) + False + sage: [5,2,2] in StrictPartitions(5) + False + sage: [5,2,0] in StrictPartitions(7) + True + sage: Partition([5,2]) in StrictPartitions() + True + sage: Partition([5,2]) in StrictPartitions(7) + True + sage: Partition([5,2]) in StrictPartitions(3) + False + """ + if not Partitions.__contains__(self, x): + return False + return len(x) == 0 or (x[-1] in NN and all(x[i]>x[i+1] for i in range(len(x)-1) if x[i]>0)) + +class StrictPartitions_all(StrictPartitions, DisjointUnionEnumeratedSets): + """ + Class of all strict partitions. + + EXAMPLES:: + + sage: from sage.combinat.partition import StrictPartitions + sage: StrictPartitions() + Strict Partitions + """ + def __init__(self): + """ + Initialize ``self``. + + TESTS:: + + sage: from sage.combinat.partition import StrictPartitions + sage: TestSuite(StrictPartitions()).run() + """ + # I'd rather use super() here but Partitions() complains + DisjointUnionEnumeratedSets.__init__(self, + family=Family(NonNegativeIntegers(), StrictPartitions_size), + facade=True, keepkey=False + ) + Partitions.__init__(self, is_infinite=True) + + def _repr_(self): + """ + Return a string representation of ``self``. + + TESTS:: + + sage: from sage.combinat.partition import StrictPartitions + sage: StrictPartitions(5) + Strict Partitions of the integer 5 + """ + return "Strict Partitions" + +class StrictPartitions_size(StrictPartitions): + """ + Strict Partitions of the integer ``size``. + + TESTS:: + + sage: TestSuite( sage.combinat.partition.StrictPartitions_size(0) ).run() + sage: TestSuite( sage.combinat.partition.StrictPartitions_size(10) ).run() + """ + + def __init__(self, n): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: from sage.combinat.partition import StrictPartitions + sage: StrictPartitions(3) + Strict Partitions of the integer 3 + + TESTS:: + + sage: from sage.combinat.partition import StrictPartitions + sage: TestSuite(StrictPartitions(9)).run() + """ + Partitions.__init__(self, is_infinite=False) + self.n = n # would be better to call this size, but for consistency... + self.size = n + + def _repr_(self): + """ + Return a string representation of ``self``. + + TESTS:: + + sage: from sage.combinat.partition import StrictPartitions + sage: StrictPartitions(5) + Strict Partitions of the integer 5 + """ + return "Strict Partitions of the integer {}".format(self.n) + + def __contains__(self, x): + """ + Return ``True`` if ``x`` is contained in ``self`` and ``False`` + otherwise. + + Examples:: + + sage: from sage.combinat.partition import StrictPartitions + sage: [5,2] in StrictPartitions(7) + True + sage: [5,2] in StrictPartitions(5) + False + sage: [5,2,2] in StrictPartitions(5) + False + sage: [5,2,0,0] in StrictPartitions(7) + True + sage: Partition([5,2]) in StrictPartitions(7) + True + sage: Partition([5,2,1]) in StrictPartitions(7) + False + """ + return StrictPartitions.__contains__(self, x) and sum(x) == self.size + + def __iter__(self): + """ + Iterate over ``self``. + + EXAMPLES:: + + sage: from sage.combinat.partition import StrictPartitions + sage: StrictPartitions(10)[:] #indirect doct test + [[10], + [9, 1], + [8, 2], + [7, 3], + [7, 2, 1], + [6, 4], + [6, 3, 1], + [5, 4, 1], + [5, 3, 2], + [4, 3, 2, 1]] + """ + for p in self._fast_iterator(self.n, self.n+1): + yield self.element_class(self, p) + + def _fast_iterator(self, size, max): + """ + A fast (recursive) iterator which returns a list. + + This method is not intended to be called directy. + + INPUT: + + - ``size`` -- a positive integer, giving the size of the partitions + + - ``max`` -- a positive integer giving the maximu size of the parts of + the partitions to be returned + + OUTPUT: + + - an iterator of the strict partitions of size ``size`` + + EXAMPLES:: + + sage: from sage.combinat.partition import StrictPartitions + sage: StrictPartitions(7)[:] # indirect doc test + [[7], [6, 1], [5, 2], [4, 3], [4, 2, 1]] + """ + if size < max: + yield [size] + + for m in reversed(range(1, min(size, max))): + for mu in self._fast_iterator(size-m, m): + yield [m] + mu + return + + def an_element(self): + """ + Returns a partition in ``self``. + + EXAMPLES:: + + sage: from sage.combinat.partition import StrictPartitions + sage: StrictPartitions(4).an_element() # indirect doctest + [3, 1] + sage: StrictPartitions(0).an_element() + [] + sage: StrictPartitions(1).an_element() + [1] + """ + if self.n == 0: + elt = [] + elif self.n == 1: + elt = [1] + else: + elt = [self.n-1, 1] + return self.element_class(self, elt) + + def cardinality(self): + """ + Return the cardinality of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.partition import StrictPartitions + sage: StrictPartitions(7).cardinality() + 5 + """ + return ZZ(len( [1 for p in self._fast_iterator(self.n, self.n+1)] )) + + def random_element(self, measure = 'uniform'): + r""" + Return a random strict partition. + + EXAMPLE: + + sage: from sage.combinat.partition import StrictPartitions + sage: StrictPartitions(7).cardinality() # random + """ + from sage.misc.prandom import randrange + partitions = self.list() + return partitions[randrange(len(partitions))] From f6aa0ef473eef8401abcdb13c0876e1c73122c23 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sat, 27 May 2017 17:50:51 -0700 Subject: [PATCH 238/740] New functions and comments --- src/sage/combinat/shifted_tableaux.py | 823 ++++++++++++++++++++------ 1 file changed, 637 insertions(+), 186 deletions(-) diff --git a/src/sage/combinat/shifted_tableaux.py b/src/sage/combinat/shifted_tableaux.py index 92a8b5da12b..cf18ea94dd8 100644 --- a/src/sage/combinat/shifted_tableaux.py +++ b/src/sage/combinat/shifted_tableaux.py @@ -1,4 +1,4 @@ -from __future__ import print_function, absolute_import +#from __future__ import print_function, absolute_import from six import add_metaclass from six.moves import range, zip, map @@ -26,60 +26,68 @@ @add_metaclass(InheritComparisonClasscallMetaclass) class ShiftedPrimedTableau(ClonableArray): - """ - A shifted primed tableau. + r""" + A shifted primed tableau with primed elements stored as half-integers + + A primed tableau is a shifted tableau on the alphabet + X' = {1' < 1 < 2' < 2 <...< n' < n} such that + 1). the entries are weakly increasing along rows and columns + 2). a row can't have two repeated primed elements, and a column + can't have two repeated non-primed elements + 3). there are only non-primed elements on the main diagonal + EXAMPLES:: sage: T = ShiftedPrimedTableaux([4,2]) sage: T([[1,"2'","3'",3],[2,"3'"]])[1] - [2, 3'] - sage: t = ShiftedPrimedTableau([[1,1.5,2.5,3],[0,2,2.5]]) + (2.0, 2.5) + sage: t = ShiftedPrimedTableau([[1,"2p",2.5,3],[0,2,2.5]]) sage: t[1] - [2, 3'] + (2.0, 2.5) sage: t = ShiftedPrimedTableau([[1,2,2.5,3],[0,2,2.5]]) - ValueError: not a primed tableau + ValueError: [(1.0, 2.0, 2.5, 3.0), (2.0, 2.5)] is not an element of Shifted Primed Tableaux of weight (1, 2, 3) and shape [4, 2] """ @staticmethod - def __classcall_private__(cls, t): + def __classcall_private__(cls, T): - """ + r""" This ensures that a shifted tableau is only ever constructed as an ``element_class`` call of an appropriate parent. EXAMPLES:: - sage: data = [[1,2',3',3],[2,3']] + sage: data = [[1,"2'","2",3],[2,"3'"]] sage: t = ShiftedPrimedTableau(data) - sage: T = ShiftedPrimedTableaux([4,2]) + sage: T = ShiftedPrimedTableaux(shape=[4,2],weight=(1,3,2)) sage: t == T(data) True + + sage: S = ShiftedPrimedTableaux(shape=[4,2]) + sage: t == S(data) + True + """ - if isinstance(t, cls): - return t + if isinstance(T, cls): + return T - #Accounting for zeros at the beginning and at the end of a row - i=0 - while i < len(t): - row = t[i] - try: - while row[0]==0 or row[0]=='0': - row.pop(0) - while row[-1]==0 or row[-1]=='0': - row.pop(-1) - except IndexError: - t.remove(t[i]) - continue - t[i] = row - i += 1 + t = preprocessing(T) shape = [len(row) for row in t] - return ShiftedPrimedTableaux(shape = shape)(t) + flat = [round(x) for x in flatten(t)] + if flat == []: + max_ind = 0 + else: + max_ind = max(flat) + weight = tuple([flat.count(i+1) for i in range(max_ind)]) + + + return ShiftedPrimedTableaux(shape = shape, weight = weight)(T) - def __init__(self,parent, t): - """ + def __init__(self,parent, T): + r""" Preprocessing of list t and initializing a shifted tableau. TESTS:: @@ -91,15 +99,13 @@ def __init__(self,parent, t): sage: t.parent() Shifted primed tableaux of shape [4, 2] sage: s.parent() - Shifted primed tableaux of shape [4, 2] + Shifted Primed Tableaux of weight (1, 2, 3) and shape [4, 2] sage: r = ShiftedPrimedTableaux([4, 2])(s); r.parent() Shifted primed tableaux of shape [4, 2] sage: s is t # identical shifted tableaux are distinct objects False - A shifted primed tableau is shallowly immutable. The entries - themselves may be mutable objects, though in that case the - resulting ShiftedPrimedTableau should be unhashable. + A shifted primed tableau is shallowly immutable, the rows are represented as tuples. sage: t = ShiftedPrimedTableau([[1,"2p","3p",3],[2,"3p"]]) sage: t0 = t[0] @@ -112,43 +118,7 @@ def __init__(self,parent, t): ... TypeError: 'tuple' object does not support item assignment """ - if isinstance(t, np.ndarray): - t = t.tolist() - - # Preprocessing list t for primes and other symbols - for i in range(len(t)): - row = t[i] - for j in range(len(row)): - string = str(row[j]) - if string[-1] == "'" and string[:-1].isdigit() == True: - row[j] = float(float(string[:-1]) - .5) - continue - if string[-1] == "p" and string[:-1].isdigit() == True: - row[j] = float(float(string[:-1]) - .5) - continue - try: - row[j] = float(string) - except ValueError: - row.pop(string) - t[i] = row - - #Accounting for zeros at the beginning and at the end of a row - i=0 - while i < len(t): - row = t[i] - try: - while row[0]==0: - row.pop(0) - while row[-1]==0: - row.pop(-1) - except IndexError: - t.remove(t[i]) - continue - t[i] = row - i += 1 - - # Normalize t to be a list of tuples. - t = [tuple(_) for _ in t] + t = preprocessing(T) if isinstance(t, ShiftedPrimedTableau): # Since we are (supposed to be) immutable, we can share the underlying data @@ -159,13 +129,56 @@ def __init__(self,parent, t): # This dispatches the input verification to the :meth:`check` # method. + def __eq__(self, other): + r""" + Check whether ``self`` is equal to ``other``. + + INPUT: + + ``other`` -- the element that ``self`` is compared to + + OUTPUT: + + Boolean. + + TESTS:: + + sage: t = ShiftedPrimedTableau([[1,"2p"]]) + sage: t == 0 + False + sage: t == ShiftedPrimedTableaux([2])([1,"2p"]) + True + """ + if isinstance(other, ShiftedPrimedTableau): + return list(self) == list(other) + else: + return list(self) == preprocessing(other) + def to_matrix(self): + """ + Returns a 2-dimensional numpy.array representation of a shifted tableau + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t.to_matrix() + array([[ 1. , 1.5, 2. , 2. ], + [ 0. , 2. , 2.5, 0. ]]) + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p'],[3]]) + sage: mat = t.to_matrix() + sage: mat + array([[ 1. , 1.5, 2. , 2. ], + [ 0. , 2. , 2.5, 0. ], + [ 0. , 0. , 3. , 0. ]]) + sage: t == ShiftedPrimedTableau(mat) + True + """ array = [] m = len(self[0]) for i in range(len(self)): - array.append([0]*i + self[i] + [0]*(m-i-len(self[i]))) - array = np.array(array) + array.append([0]*i + list(self[i]) + [0]*(m-i-len(self[i]))) + array = np.array(array, dtype = 'float') return array def check(self): @@ -193,33 +206,353 @@ def check(self): if not all(row[0] in ZZ for row in self): raise ValueError('diagonal elements must be non-primes') - def _repr_(self): + """ + Represents Shifted Primed Tableau as a list of half-integers + EXAMPLES:: + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t + [[1.0, 1.5, 2.0, 2.0], [2.0, 2.5]] + """ + return repr([list(_) for _ in self]) + + def pp(self): + """ + Prints out a nice version of ``self``. + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t.pp() + 1 2' 2 2 + 2 3' + sage: t = ShiftedPrimedTableau([[10,'11p',11,11],[11,'12']]) + 10 11' 11 11 + 11 12 + """ + if flatten(self) == []: + max_ind = 0 + else: + max_ind = max(flatten(self)) + max_len = len(str(round(max_ind))) string_list = '' for i,row in enumerate(self): - string_list += ' '*i + string_list += ' ' * i * (max_len) for val in row: if val in ZZ: - string_list += str(int(val))+' ' + string_list += str(int(val)) + ' ' * (max_len-len(str(int(val)))) else: - string_list += str(int(val+.5))+"'" + string_list += str(int(val+.5)) + "'" + ' '*(max_len-1- len(str(int(val+.5)))) string_list += '\n' - - return (string_list) + string_list = string_list[:-2] + print(string_list) + return def shape(self): - return + """ + Returnes the shape of the underlying partition of ``self`` in list format + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t.shape() + [4,2] + """ + return ([len(row) for row in self]) + + def __call__(self,*cell): + """ + Function call of ``self``. + + INPUT: + + - ``cell`` -- a pair of integers, tuple, or list specifying a cell in + the tableau. + + OUTPUT: + + - the element in the corresponding cell. if the element is primed, + returnes half-integer value. + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t(1,0) + 2.0 + sage: t((1,2)) + Traceback (most recent call last): + ... + IndexError: the cell (1,2) is not contained in the shifted tableau + [[1.0, 1.5, 2.0, 2.0], [2.0, 2.5]] + sage: t((1,1)) + 2.5 + """ + + try: + i,j = cell + except ValueError: + i,j = cell[0] + + try: + return self[i][j] + except IndexError: + raise IndexError("the cell (%d,%d) is not contained in the shifted tableau \n%s"%(i, j, repr(self))) + + def weight(self): - return - def entry(self,position): - return + """ + Returnes the weight of ``self`` in tuple format. + The weight of a shifted primed tableau is defined to be the vector with i-th component + equal to the number of letters i and i' in the tableau. + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t.weight() + (1,4,1) + """ + flat = [round(x) for x in flatten(self)] + if flat == []: + max_ind = 0 + else: + max_ind = max(flat) + weight = tuple([flat.count(i+1) for i in range(max_ind)]) + return tuple(weight) + + def reading_word_with_positions(self): + """ + Returnes the reading word of ``self`` together with positions of the corresponding + letters in ``self``. + The reading word of a shifted primed tableau is constructed as follows: + (1) List all primed letters in the tableau, column by column, in decreasing order + within each column, moving from the rightmost column to the left, and with all the + primes removed (i.e. all letters are increased by half a unit). + (2) Then list all unprimed elements, row by row, in increasing order within each row, + moving from the bottommost row to the top. + + EXAMPLES:: + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t.reading_word_with_positions() + [((1, 2), 3), ((0, 1), 2), ((1, 1), 2), ((0, 0), 1), ((0, 2), 2), ((0, 3), 2)] + """ + mat = self.to_matrix() + list_with_positions = [] + for (i,j), x in np.ndenumerate(mat[:,::-1].T): + if int(x) != x: + list_with_positions.append(((j,mat.shape[1]-i-1),int(x+0.5))) + for (i,j), x in np.ndenumerate(mat[::-1,:]): + if int(x) == x and int(x) != 0: + list_with_positions.append(((mat.shape[0]-i-1,j),int(x))) + return list_with_positions + def reading_word(self): - return + """ + Returnes the reading word of ``self``. + The reading word of a shifted primed tableau is constructed as follows: + (1) List all primed letters in the tableau, column by column, in decreasing order + within each column, moving from the rightmost column to the left, and with all the + primes removed (i.e. all letters are increased by half a unit). + (2) Then list all unprimed elements, row by row, in increasing order within each row, + moving from the bottommost row to the top. + EXAMPLES:: + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t.reading_word() + [3, 2, 2, 1, 2, 2] + """ + return [tup[1] for tup in self.reading_word_with_positions()] + + + def crystal_f(self, ind): + """ + A function to compute an action of the crystal operator F_i on an Primed Tableau + using cases in the paper. + + INPUT: + + self -- primed tableau + ind -- index of the crystal operator F_i. + + OUTPUT: + + Primed tableau or 'None'. + + EXAMPLE: + + sage: t = ShiftedPrimedTableau([[1,1,1,1,2.5],[2,2,2,2.5],[3,3]]) + sage: t.pp() + 1 1 1 1 3' + 2 2 2 3' + 3 3 + sage: s = t.crystal_f(2) + sage: print(s) + None + sage: t = ShiftedPrimedTableau([[1,1,1,1.5,2.5],[0,2,2,3,3],[0,0,3,4]]) + sage: t.pp() + 1 1 1 2' 3' + 2 2 3 3 + 3 4 + sage: s = t.crystal_f(2) + sage: s.pp() + 1 1 1 2' 3' + 2 3' 3 3 + 3 4 + + """ + T = self.to_matrix() + + read_word = self.reading_word_with_positions() + read_word = [num for num in read_word if num[1]==ind or num[1]==ind+1] + + element_to_change = None + count=0 + + for element in read_word: + if element[1] == ind+1: + count += 1 + elif count == 0: + element_to_change = element + else: + count -= 1 + + if element_to_change == None: + return None + + (r,c), elt = element_to_change + + if T[r,c] == ind- .5: + T = T.T + .5 + r,c = c,r + h,l = T.shape + + if (c+1==l or T[r,c+1]>=ind+1 or T[r,c+1]<1): + + (tp_r,tp_c)=(r,c) + while True: + + if tp_r+1==h or T[tp_r+1,tp_c]>ind+1 or T[tp_r+1,tp_c]<1: + break + if T[tp_r+1,tp_r+1]==ind+1: + tp_r += 1 + tp_c = tp_r + break + if (ind+.5 not in T[tp_r+1]): + break + tp_r += 1 + tp_c = np.where(T[tp_r]==ind+.5)[0] + + + if tp_r == r: + T[r,c] += 1 + + elif tp_r == tp_c: + T[r,c] += .5 + + else: + T[r,c] += .5 + T[tp_r,tp_c] += .5 + + elif T[r,c+1]==ind+.5: + T[r,c+1] += .5 + T[r,c] += .5 + + if r>c: + T = T.T - .5 + + return(ShiftedPrimedTableau(T)) + + def crystal_e(self,ind): + """ + A function to compute an action of the crystal operator E_i on an Primed Tableau + using cases in the paper. + + INPUT: + + self -- primed tableau + ind -- index of the crystal operator E_i. + + OUTPUT: + + Primed tableau or 'None'. + + EXAMPLE: + + sage: t = ShiftedPrimedTableau([[1,1,1,1.5,2.5],[0,2,"3p",3,3],[0,0,3,4]]) + sage: t.pp() + 1 1 1 2' 3' + 2 3' 3 3 + 3 4 + sage: s = t.crystal_e(2) + sage: s.pp() + 1 1 1 2' 3' + 2 2 3 3 + 3 4 + sage: t == s.crystal_f(2) + True + + """ + + T = self.to_matrix() + + read_word = self.reading_word_with_positions() + read_word = [num for num in read_word if num[1]==ind or num[1]==ind+1] + + element_to_change = None + count=0 + + for element in read_word[::-1]: + if element[1] == ind: + count += 1 + elif count == 0: + element_to_change = element + else: + count -= 1 + + if element_to_change == None: + return None + (r,c), elt = element_to_change + + if T[r,c] == ind + .5: + T = T.T + .5 + r,c = c,r + h,l = T.shape + + if (c==0 or T[r,c-1]<=ind or T[r,c-1]<1): + + (tp_r,tp_c)=(r,c) + while True: + + if tp_r==0 or T[tp_r-1,tp_c]c: + T = T.T - .5 + + return(ShiftedPrimedTableau(T)) + + class ShiftedPrimedTableaux(UniqueRepresentation, Parent): - r""" + """ A factory for the various classes of shifted standard tableaux. INPUT: @@ -253,13 +586,9 @@ class ShiftedPrimedTableaux(UniqueRepresentation, Parent): TESTS:: sage: ShiftedPrimedTableaux([]) - [] + Shifted tableaux of shape [] sage: SPT = ShiftedPrimedTableaux((1,2,2),[3,2]); SPT Shifted primed tableaux of shape [3, 2] and weight (1,2,2) - sage: SPT.cardinality() - 2 - sage: SPT.list() - [[[1, 2, 3], [4, 5]], [[1, 2, 4], [3, 5]]] """ # use Tableaux options options = Tableaux.options @@ -274,22 +603,31 @@ def __classcall_private__(cls, *args, **kwargs): TESTS:: sage: ShiftedPrimedTableaux() - Shifted primed tableaux + Shifted Primed Tableaux + sage: ShiftedPrimedTableaux(3) - Shifted tableaux of size 3 - sage: ShiftedTableaux([2,1]) + Traceback (most recent call last): + ... + ValueError: weight argument must be a tuple and shape argument must be a strictly increasing list + + sage: ShiftedPrimedTableaux([2,1]) Shifted tableaux of shape [2, 1] - sage: ShiftedTableaux(0) - Shifted tableaux of size 0 - sage: ShiftedTableaux(-1) + sage: ShiftedPrimedTableaux([[1]]) Traceback (most recent call last): ... - ValueError: the argument must be a non-negative integer or a partition - sage: ShiftedTableaux([[1]]) + ValueError: [[1]] is not a strict partition + + sage: ShiftedPrimedTableaux(weight=(1,2,2), shape=[3,2]) + Shifted Primed Tableaux of weight (1, 2, 2) and shape [3, 2] + + sage: ShiftedPrimedTableaux(weight=(2,2,2), shape=[3,2]) Traceback (most recent call last): ... - ValueError: the argument must be a non-negative integer or a partition + ValueError: the sum of weights should match the sum of parts of the shape + + sage: ShiftedPrimedTableaux((3,2,1)) + Shifted tableaux of weight (3, 2, 1) """ weight = None shape = None @@ -329,8 +667,7 @@ def __classcall_private__(cls, *args, **kwargs): raise ValueError('{} is not a strict partition'.format(shape)) if shape is None and weight is None: - return ShiftedPrimedTableaux_all() - + return ShiftedPrimedTableaux_all() elif weight is None and shape in StrictPartitions(): return ShiftedPrimedTableaux_shape(Partition(shape)) @@ -346,51 +683,31 @@ def __classcall_private__(cls, *args, **kwargs): return ShiftedPrimedTableaux_weight_shape(weight,shape) - def __contains__(self, t): + def __contains__(self, T): """ EXAMPLES:: + sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux() + True + sage: [[1,1],[2,2]] in ShiftedPrimedTableaux() + False + sage: [[1,1],['2p']] in ShiftedPrimedTableaux() + False + sage: [[1,1,'3p'],[2,'3p']] in ShiftedPrimedTableaux() + True + sage: [[1,1,2],[2,2]] in ShiftedPrimedTableaux() + False + sage: [] in ShiftedPrimedTableaux() + True + """ - if isinstance(t, ShiftedPrimedTableau) or t == []: + if isinstance(T, ShiftedPrimedTableau) or T == []: return True - if isinstance(t, np.ndarray): - t = t.tolist() - - # Preprocessing list t for primes and other symbols - for i in range(len(t)): - row = t[i] - for j in range(len(row)): - string = str(row[j]) - if string[-1] == "'" and string[:-1].isdigit() == True: - row[j] = float(float(string[:-1]) - .5) - continue - if string[-1] == "p" and string[:-1].isdigit() == True: - row[j] = float(float(string[:-1]) - .5) - continue - try: - row[j] = float(string) - except ValueError: - row.pop(string) - t[i] = row - - #Accounting for zeros at the beginning and at the end of a row - i=0 - while i < len(t): - row = t[i] - try: - while row[0]==0: - row.pop(0) - while row[-1]==0: - row.pop(-1) - except IndexError: - t.remove(t[i]) - continue - t[i] = row - i += 1 + t=preprocessing(T) - # Normalize t to be a list of tuples. - t = [tuple(_) for _ in t] + if [len(_) for _ in t] not in StrictPartitions(): + return False # must have strict partition shape for i,row in enumerate(t): if i > 0: @@ -409,35 +726,43 @@ def __contains__(self, t): _is_a = __contains__ - def an_element(self): - r""" - Return a particular shifted tableaux in the class. - TESTS:: - sage: ShiftedTableaux().an_element() - [] - sage: ShiftedTableaux(4).an_element() - [[1, 2, 3, 4]] - sage: ShiftedTableaux([3,1]).an_element() - [[1, 2, 3], [4]] - """ - return self[0] +class ShiftedPrimedTableaux_all(ShiftedPrimedTableaux): + + Element = ShiftedPrimedTableau + + def __init__(self): + + Parent.__init__(self, category=FiniteEnumeratedSets()) + + def _repr_(self): + return "Shifted Primed Tableaux" + + def _element_constructor_(self, t): + if not t in self: + raise ValueError("{} is not an element of {}".format(t, self)) + + return self.element_class(self, t) + + def __contains__(self, x): + + if isinstance(x, ShiftedPrimedTableau): + return True + return (ShiftedPrimedTableaux.__contains__(self, x)) + + class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): """ - Shifted tableaux of a fixed shape. + Shifted Primed tableaux of a fixed shape. """ Element = ShiftedPrimedTableau def __init__(self, shape): r""" - Initializes the class of semistandard tableaux of shape ``p`` and no - maximum entry. + Initializes the class of semistandard tableaux of a fixed shape. - TESTS:: - - sage: TestSuite( ShiftedTableaux([3,2,1]) ).run() """ Parent.__init__(self, category=FiniteEnumeratedSets()) self._shape = shape @@ -448,27 +773,26 @@ def _repr_(self): TESTS:: - sage: ShiftedTableaux([3,2,1]) # indirect doctest - Shifted tableaux of shape [3, 2, 1] + sage: ShiftedPrimedTableaux([3,2,1]) # indirect doctest + Shifted Primed tableaux of shape [3, 2, 1] """ - return "Shifted tableaux of shape {}".format(self._shape) + return "Shifted Primed tableaux of shape {}".format(self._shape) - def __contains__(self, x): + def __contains__(self, T): """ TESTS:: - sage: ST421 = ShiftedTableaux([4,2,1]) - sage: all([st in ST421 for st in ST421]) - True - sage: ST42 = ShiftedTableaux([4,2]) - sage: filter(lambda x: x in ST42, ST421) - [] + sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux([4,2]) + True """ - if isinstance(x, ShiftedPrimedTableau): - return [len(row) for row in x] == self._shape + + if isinstance(T, ShiftedPrimedTableau): + return [len(row) for row in T] == self._shape - return (ShiftedPrimedTableaux.__contains__(self, x) - and [len(row) for row in x] == self._shape) + t = preprocessing(T) + + return (ShiftedPrimedTableaux.__contains__(self, t) + and [len(row) for row in t] == self._shape) def _element_constructor_(self, t): r""" @@ -477,20 +801,20 @@ def _element_constructor_(self, t): INPUT: - - ``t`` -- data which can be interpreted as a tableau + - ``t`` -- data which can be interpreted as a primed tableau OUTPUT: - - the corresponding tableau object + - the corresponding primed tableau object TESTS:: - sage: ShiftedTableaux([3])([[1,2,3]]).parent() is ShiftedTableaux([3]) + sage: ShiftedPrimedTableaux([3])([[1,2,3]]).parent() is ShiftedPrimedTableaux([3]) True - sage: ShiftedTableaux([3])([[1,2]]) + sage: ShiftedPrimedTableaux([3])([[1,2]]) Traceback (most recent call last): ... - ValueError: [[1, 2]] is not an element of Shifted tableaux of shape [3] + ValueError: [[1, 2]] is not an element of Shifted Primed tableaux of shape [3] """ if not t in self: raise ValueError("{} is not an element of {}".format(t, self)) @@ -503,22 +827,149 @@ def shape(self): EXAMPLES:: - sage: ShiftedTableaux([6,4,3,1]).shape() + sage: ShiftedPrimedTableaux([6,4,3,1]).shape() [6, 4, 3, 1] """ return self._shape - @cached_method - def size(self): - """ - Return the shape of the shifted tableaux ``self``. - EXAMPLES:: - sage: ShiftedTableaux([6,4,3,1]).size() - 14 - """ - return self._shape.size() +class ShiftedPrimedTableaux_weight(ShiftedPrimedTableaux): + """ + Shifted Primed Tableaux of a fixed weight + """ + Element = ShiftedPrimedTableau + + def __init__(self, weight): + Parent.__init__(self, category=FiniteEnumeratedSets()) + self._weight = weight + + def _repr_(self): + return "Shifted tableaux of weight {}".format(self._weight) + + def __contains__(self, T): + if isinstance(x, ShiftedPrimedTableau): + return T.weight() == self._weight + + t = preprocessing(T) + + flat = [round(x) for x in flatten(t)] + if flat == []: + max_ind = 0 + else: + max_ind = max(flat) + weight = tuple([flat.count(i+1) for i in range(max_ind)]) + + return (ShiftedPrimedTableaux.__contains__(self, t) + and weight == self._weight) + + def _element_constructor_(self, t): + if not t in self: + raise ValueError("{} is not an element of {}".format(t, self)) + + return self.element_class(self, t) + + +class ShiftedPrimedTableaux_weight_shape(ShiftedPrimedTableaux): + Element = ShiftedPrimedTableau + + def __init__(self, weight, shape): + Parent.__init__(self, category=FiniteEnumeratedSets()) + self._weight = weight + self._shape = shape + + def _repr_(self): + return ("Shifted Primed Tableaux of weight {} and shape {}" .format(self._weight,self._shape)) + + def __contains__(self, T): + if isinstance(T, ShiftedPrimedTableau): + return (T.weight() == self._weight and T.shape() == self._shape) + + t = preprocessing(T) + + flat = [round(x) for x in flatten(t)] + if flat == []: + max_ind = 0 + else: + max_ind = max(flat) + weight = tuple([flat.count(i+1) for i in range(max_ind)]) + return(ShiftedPrimedTableaux.__contains__(self, t) and weight == self._weight + and [len(row) for row in t] == self._shape) + + def _element_constructor_(self, t): + if not t in self: + raise ValueError("{} is not an element of {}".format(t, self)) + + return self.element_class(self, t) + + +# Helper function, preprocessing of the input array to fit Shifted Primed Tableau format. +# Right now it requires 4 preprocessing calls to initialize ShiftedPrimedTableau element. +# Donno how to fix. +def preprocessing(T): + t= [] + if isinstance(T, np.ndarray): + t = T.tolist() + elif not isinstance(T,list): + return [[]] + elif all(isinstance(item, (list,tuple)) for item in T): + # Preprocessing list T for primes and other symbols + for i in range(len(T)): + row = list(T[i]) + for j in range(len(row)): + string = str(row[j]) + if string[-1] == "'" and string[:-1].isdigit() == True: + row[j] = float(float(string[:-1]) - .5) + continue + if string[-1] == "p" and string[:-1].isdigit() == True: + row[j] = float(float(string[:-1]) - .5) + continue + try: + row[j] = float(int(2*float(string))/2) + except ValueError: + row.pop(string) + t.append(row) + else: + row = list(T) + for j in range(len(row)): + string = str(row[j]) + if string[-1] == "'" and string[:-1].isdigit() == True: + row[j] = float(float(string[:-1]) - .5) + continue + if string[-1] == "p" and string[:-1].isdigit() == True: + row[j] = float(float(string[:-1]) - .5) + continue + try: + row[j] = float(int(2*float(string))/2) + except ValueError: + row.pop(string) + t.append(row) + + + #Accounting for zeros at the beginning and at the end of a row + i=0 + while i < len(t): + row = t[i] + try: + while row[0]==0: + row.pop(0) + while row[-1]==0: + row.pop(-1) + except IndexError: + t.remove(t[i]) + continue + t[i] = row + i += 1 + + # Normalize t to be a list of tuples. + t = [tuple(_) for _ in t] + return t + + + +###################### +string1 = 'absdf' +print(string1[:-2]) ###################### # Strict Partitions # From d00e09bd336c663facf0185813d6be64df10b3a9 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sat, 27 May 2017 19:38:19 -0700 Subject: [PATCH 239/740] Added proper links --- src/doc/en/reference/combinat/module_list.rst | 1 + src/sage/combinat/all.py | 4 +- src/sage/combinat/catalog_partitions.py | 1 + src/sage/combinat/partition.py | 268 ++++++++++++++ src/sage/combinat/tableau.py | 1 - ..._tableaux.py => tableau_shifted_primed.py} | 333 ++---------------- 6 files changed, 304 insertions(+), 304 deletions(-) rename src/sage/combinat/{shifted_tableaux.py => tableau_shifted_primed.py} (76%) diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 74bcbf1ec98..6882e3bb01d 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -333,6 +333,7 @@ Comprehensive Module list sage/combinat/tableau sage/combinat/tableau_residues sage/combinat/tableau_tuple + sage/combinat/tableau_shifted_primed sage/combinat/tamari_lattices sage/combinat/tiling sage/combinat/tools diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index c6bee3f1a7c..ef825ed255d 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -60,7 +60,8 @@ #Partitions from .partition import Partition, Partitions, PartitionsInBox,\ OrderedPartitions, PartitionsGreatestLE, PartitionsGreatestEQ,\ - PartitionsGreatestLE, PartitionsGreatestEQ, number_of_partitions + PartitionsGreatestLE, PartitionsGreatestEQ, number_of_partitions,\ + StrictPartitions from sage.combinat.partition_tuple import PartitionTuple, PartitionTuples from .skew_partition import SkewPartition, SkewPartitions @@ -95,6 +96,7 @@ from .k_tableau import WeakTableau, WeakTableaux, StrongTableau, StrongTableaux lazy_import('sage.combinat.lr_tableau', ['LittlewoodRichardsonTableau', 'LittlewoodRichardsonTableaux']) +lazy_import('sage.combinat.tableau_shifted_primed', ['ShiftedPrimedTableaux', 'ShiftedPrimedTableau']) #Words from .words.all import * diff --git a/src/sage/combinat/catalog_partitions.py b/src/sage/combinat/catalog_partitions.py index c9b5e350a00..e3b43947183 100644 --- a/src/sage/combinat/catalog_partitions.py +++ b/src/sage/combinat/catalog_partitions.py @@ -21,4 +21,5 @@ - :ref:`sage.combinat.rsk` - :ref:`sage.combinat.growth` - :ref:`sage.combinat.tableau_residues` +- :ref:`sage.combinat.tableau_shifted_primed` """ diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 5004bffe025..6a49d8398e6 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -62,6 +62,8 @@ all in the category framework except ``PartitionsRestricted`` (which will eventually be removed). Cleaned up documentation. +- Andrew Mathas (2016-07-20): Added ``StrictPartitions`` + EXAMPLES: There are `5` partitions of the integer `4`:: @@ -304,6 +306,9 @@ from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.sets.non_negative_integers import NonNegativeIntegers +from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets +from sage.sets.family import Family + from sage.rings.all import QQ, ZZ, NN, IntegerModRing from sage.arith.all import factorial, gcd from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -7318,6 +7323,269 @@ def cardinality(self): if self._ell > self.n: return Partitions_n.cardinality(self) return ZZ.sum(1 for x in self) + +###################### +# Strict Partitions # +###################### +class StrictPartitions(Partitions): + r""" + The class of **strict partitions**. + + A strict partition of an integer `n` is a :class:`Partition` with + distinct parts. + + INPUT: + + - ``n`` -- a non-negative integer, the size of the strict partitions + """ + @staticmethod + def __classcall_private__(cls, size=None): + """ + Normalize the input to ensure a unique representation. + + TESTS:: + + sage: from sage.combinat.partition import StrictPartitions + sage: P = StrictPartitions() + sage: P12 = StrictPartitions(12) + sage: P is P12 + False + """ + if size is not None: + return StrictPartitions_size( ZZ(size) ) + + return StrictPartitions_all() + + def __contains__(self, x): + """ + Return ``True`` if ``x`` is contained in ``self`` and ``False`` + otherwise. + + EXAMPLES:: + + sage: from sage.combinat.partition import StrictPartitions + sage: [5,2] in StrictPartitions() + True + sage: [5,2] in StrictPartitions(7) + True + sage: [5,2] in StrictPartitions(5) + False + sage: [5,2,2] in StrictPartitions(5) + False + sage: [5,2,0] in StrictPartitions(7) + True + sage: Partition([5,2]) in StrictPartitions() + True + sage: Partition([5,2]) in StrictPartitions(7) + True + sage: Partition([5,2]) in StrictPartitions(3) + False + """ + if not Partitions.__contains__(self, x): + return False + return len(x) == 0 or (x[-1] in NN and all(x[i]>x[i+1] for i in range(len(x)-1) if x[i]>0)) + +class StrictPartitions_all(StrictPartitions, DisjointUnionEnumeratedSets): + """ + Class of all strict partitions. + + EXAMPLES:: + + sage: from sage.combinat.partition import StrictPartitions + sage: StrictPartitions() + Strict Partitions + """ + def __init__(self): + """ + Initialize ``self``. + + TESTS:: + + sage: from sage.combinat.partition import StrictPartitions + sage: TestSuite(StrictPartitions()).run() + """ + # I'd rather use super() here but Partitions() complains + DisjointUnionEnumeratedSets.__init__(self, + family=Family(NonNegativeIntegers(), StrictPartitions_size), + facade=True, keepkey=False + ) + Partitions.__init__(self, is_infinite=True) + + def _repr_(self): + """ + Return a string representation of ``self``. + + TESTS:: + + sage: from sage.combinat.partition import StrictPartitions + sage: StrictPartitions(5) + Strict Partitions of the integer 5 + """ + return "Strict Partitions" + +class StrictPartitions_size(StrictPartitions): + """ + Strict Partitions of the integer ``size``. + + TESTS:: + + sage: TestSuite( sage.combinat.partition.StrictPartitions_size(0) ).run() + sage: TestSuite( sage.combinat.partition.StrictPartitions_size(10) ).run() + """ + + def __init__(self, n): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: from sage.combinat.partition import StrictPartitions + sage: StrictPartitions(3) + Strict Partitions of the integer 3 + + TESTS:: + + sage: from sage.combinat.partition import StrictPartitions + sage: TestSuite(StrictPartitions(9)).run() + """ + Partitions.__init__(self, is_infinite=False) + self.n = n # would be better to call this size, but for consistency... + self.size = n + + def _repr_(self): + """ + Return a string representation of ``self``. + + TESTS:: + + sage: from sage.combinat.partition import StrictPartitions + sage: StrictPartitions(5) + Strict Partitions of the integer 5 + """ + return "Strict Partitions of the integer {}".format(self.n) + + def __contains__(self, x): + """ + Return ``True`` if ``x`` is contained in ``self`` and ``False`` + otherwise. + + Examples:: + + sage: from sage.combinat.partition import StrictPartitions + sage: [5,2] in StrictPartitions(7) + True + sage: [5,2] in StrictPartitions(5) + False + sage: [5,2,2] in StrictPartitions(5) + False + sage: [5,2,0,0] in StrictPartitions(7) + True + sage: Partition([5,2]) in StrictPartitions(7) + True + sage: Partition([5,2,1]) in StrictPartitions(7) + False + """ + return StrictPartitions.__contains__(self, x) and sum(x) == self.size + + def __iter__(self): + """ + Iterate over ``self``. + + EXAMPLES:: + + sage: from sage.combinat.partition import StrictPartitions + sage: StrictPartitions(10)[:] #indirect doct test + [[10], + [9, 1], + [8, 2], + [7, 3], + [7, 2, 1], + [6, 4], + [6, 3, 1], + [5, 4, 1], + [5, 3, 2], + [4, 3, 2, 1]] + """ + for p in self._fast_iterator(self.n, self.n+1): + yield self.element_class(self, p) + + def _fast_iterator(self, size, max): + """ + A fast (recursive) iterator which returns a list. + + This method is not intended to be called directy. + + INPUT: + + - ``size`` -- a positive integer, giving the size of the partitions + + - ``max`` -- a positive integer giving the maximu size of the parts of + the partitions to be returned + + OUTPUT: + + - an iterator of the strict partitions of size ``size`` + + EXAMPLES:: + + sage: from sage.combinat.partition import StrictPartitions + sage: StrictPartitions(7)[:] # indirect doc test + [[7], [6, 1], [5, 2], [4, 3], [4, 2, 1]] + """ + if size < max: + yield [size] + + for m in reversed(range(1, min(size, max))): + for mu in self._fast_iterator(size-m, m): + yield [m] + mu + return + + def an_element(self): + """ + Returns a partition in ``self``. + + EXAMPLES:: + + sage: from sage.combinat.partition import StrictPartitions + sage: StrictPartitions(4).an_element() # indirect doctest + [3, 1] + sage: StrictPartitions(0).an_element() + [] + sage: StrictPartitions(1).an_element() + [1] + """ + if self.n == 0: + elt = [] + elif self.n == 1: + elt = [1] + else: + elt = [self.n-1, 1] + return self.element_class(self, elt) + + def cardinality(self): + """ + Return the cardinality of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.partition import StrictPartitions + sage: StrictPartitions(7).cardinality() + 5 + """ + return ZZ(len( [1 for p in self._fast_iterator(self.n, self.n+1)] )) + + def random_element(self, measure = 'uniform'): + r""" + Return a random strict partition. + + EXAMPLE: + + sage: from sage.combinat.partition import StrictPartitions + sage: StrictPartitions(7).cardinality() # random + """ + from sage.misc.prandom import randrange + partitions = self.list() + return partitions[randrange(len(partitions))] ###################### # Ordered Partitions # diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index a5025f6a171..abd4a13fc71 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -1446,7 +1446,6 @@ def reading_word_permutation(self): bottommost row (in English notation). EXAMPLES:: - sage: StandardTableau([[1,2],[3,4]]).reading_word_permutation() [3, 4, 1, 2] diff --git a/src/sage/combinat/shifted_tableaux.py b/src/sage/combinat/tableau_shifted_primed.py similarity index 76% rename from src/sage/combinat/shifted_tableaux.py rename to src/sage/combinat/tableau_shifted_primed.py index cf18ea94dd8..26b2c376dcd 100644 --- a/src/sage/combinat/shifted_tableaux.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -1,24 +1,24 @@ #from __future__ import print_function, absolute_import from six import add_metaclass -from six.moves import range, zip, map +#from six.moves import range, zip, map import numpy as np -from sage.combinat.partition import Partition -from sage.combinat.permutation import Permutation -from sage.combinat.posets.posets import Poset -from sage.combinat.tableau import Tableaux +from sage.combinat.partition import Partition, StrictPartitions +#from sage.combinat.permutation import Permutation +#from sage.combinat.posets.posets import Poset +#from sage.combinat.tableau import Tableaux from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets -from sage.functions.other import factorial -from sage.misc.cachefunc import cached_method +#from sage.functions.other import factorial +#from sage.misc.cachefunc import cached_method from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass -from sage.misc.misc_c import prod -from sage.misc.prandom import randrange -from sage.rings.integer import Integer +#from sage.misc.misc_c import prod +#from sage.misc.prandom import randrange +#from sage.rings.integer import Integer from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets -from sage.sets.family import Family -from sage.sets.non_negative_integers import NonNegativeIntegers +#from sage.sets.family import Family +#from sage.sets.non_negative_integers import NonNegativeIntegers from sage.structure.list_clone import ClonableArray from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation @@ -75,11 +75,11 @@ def __classcall_private__(cls, T): t = preprocessing(T) shape = [len(row) for row in t] - flat = [round(x) for x in flatten(t)] + flat = [round(item) for sublist in t for item in sublist] if flat == []: max_ind = 0 else: - max_ind = max(flat) + max_ind = int(max(flat)) weight = tuple([flat.count(i+1) for i in range(max_ind)]) @@ -195,15 +195,15 @@ def check(self): raise ValueError('shape must be a strict partition') for i,row in enumerate(self): if i > 0: - if not all(val > self[i-1][j+1] for j,val in enumerate(row) if val in ZZ): + if not all(val > self[i-1][j+1] for j,val in enumerate(row) if int(val)==val): raise ValueError('column is not strictly increasing in non-primes') - if not all(val >= self[i-1][j+1] for j,val in enumerate(row) if val not in ZZ): + if not all(val >= self[i-1][j+1] for j,val in enumerate(row) if int(val)!=val): raise ValueError('column is not weakly increasing in primes') - if not all(row[j] <= row[j+1] for j in range(len(row)-1) if row[j] in ZZ): + if not all(row[j] <= row[j+1] for j in range(len(row)-1) if int(row[j])==row[j]): raise ValueError('row is not weakly increasing in non-primes') - if not all(row[j] < row[j+1] for j in range(len(row)-1) if row[j] not in ZZ): + if not all(row[j] < row[j+1] for j in range(len(row)-1) if int(row[j])!=row[j]): raise ValueError('row is not strictly increasing in primes') - if not all(row[0] in ZZ for row in self): + if not all(int(row[0])==row[0] for row in self): raise ValueError('diagonal elements must be non-primes') def _repr_(self): @@ -230,16 +230,16 @@ def pp(self): 10 11' 11 11 11 12 """ - if flatten(self) == []: + if [item for sublist in self for item in sublist] == []: max_ind = 0 else: - max_ind = max(flatten(self)) + max_ind = max([item for sublist in self for item in sublist]) max_len = len(str(round(max_ind))) string_list = '' for i,row in enumerate(self): string_list += ' ' * i * (max_len) for val in row: - if val in ZZ: + if int(val)==val: string_list += str(int(val)) + ' ' * (max_len-len(str(int(val)))) else: string_list += str(int(val+.5)) + "'" + ' '*(max_len-1- len(str(int(val+.5)))) @@ -311,7 +311,7 @@ def weight(self): sage: t.weight() (1,4,1) """ - flat = [round(x) for x in flatten(self)] + flat = [round(item) for sublist in self for item in sublist] if flat == []: max_ind = 0 else: @@ -590,8 +590,6 @@ class ShiftedPrimedTableaux(UniqueRepresentation, Parent): sage: SPT = ShiftedPrimedTableaux((1,2,2),[3,2]); SPT Shifted primed tableaux of shape [3, 2] and weight (1,2,2) """ - # use Tableaux options - options = Tableaux.options @staticmethod def __classcall_private__(cls, *args, **kwargs): @@ -711,15 +709,15 @@ def __contains__(self, T): for i,row in enumerate(t): if i > 0: - if not all(val > t[i-1][j+1] for j,val in enumerate(row) if val in ZZ): + if not all(val > t[i-1][j+1] for j,val in enumerate(row) if int(val) == val): return False - if not all(val >= t[i-1][j+1] for j,val in enumerate(row) if val not in ZZ): + if not all(val >= t[i-1][j+1] for j,val in enumerate(row) if int(val) != val): return False - if not all(row[j] <= row[j+1] for j in range(len(row)-1) if row[j] in ZZ): + if not all(row[j] <= row[j+1] for j in range(len(row)-1) if int(row[j]) == row[j]): return False - if not all(row[j] < row[j+1] for j in range(len(row)-1) if row[j] not in ZZ): + if not all(row[j] < row[j+1] for j in range(len(row)-1) if int(row[j]) != row[j]): return False - if not all(row[0] in ZZ for row in t): + if not all(int(row[0]) == row[0] for row in t): return False return True @@ -853,7 +851,7 @@ def __contains__(self, T): t = preprocessing(T) - flat = [round(x) for x in flatten(t)] + flat = [round(item) for sublist in t for item in sublist] if flat == []: max_ind = 0 else: @@ -887,11 +885,11 @@ def __contains__(self, T): t = preprocessing(T) - flat = [round(x) for x in flatten(t)] + flat = [round(item) for sublist in t for item in sublist] if flat == []: max_ind = 0 else: - max_ind = max(flat) + max_ind = int(max(flat)) weight = tuple([flat.count(i+1) for i in range(max_ind)]) return(ShiftedPrimedTableaux.__contains__(self, t) and weight == self._weight and [len(row) for row in t] == self._shape) @@ -964,272 +962,3 @@ def preprocessing(T): # Normalize t to be a list of tuples. t = [tuple(_) for _ in t] return t - - - -###################### -string1 = 'absdf' -print(string1[:-2]) - -###################### -# Strict Partitions # -###################### -class StrictPartitions(Partitions): - r""" - The class of **strict partitions**. - - A strict partition of an integer `n` is a :class:`Partition` with - distinct parts. - - INPUT: - - - ``n`` -- a non-negative integer, the size of the strict partitions - """ - @staticmethod - def __classcall_private__(cls, size=None): - """ - Normalize the input to ensure a unique representation. - - TESTS:: - - sage: from sage.combinat.partition import StrictPartitions - sage: P = StrictPartitions() - sage: P12 = StrictPartitions(12) - sage: P is P12 - False - """ - if size is not None: - return StrictPartitions_size( ZZ(size) ) - - return StrictPartitions_all() - - def __contains__(self, x): - """ - Return ``True`` if ``x`` is contained in ``self`` and ``False`` - otherwise. - - EXAMPLES:: - - sage: from sage.combinat.partition import StrictPartitions - sage: [5,2] in StrictPartitions() - True - sage: [5,2] in StrictPartitions(7) - True - sage: [5,2] in StrictPartitions(5) - False - sage: [5,2,2] in StrictPartitions(5) - False - sage: [5,2,0] in StrictPartitions(7) - True - sage: Partition([5,2]) in StrictPartitions() - True - sage: Partition([5,2]) in StrictPartitions(7) - True - sage: Partition([5,2]) in StrictPartitions(3) - False - """ - if not Partitions.__contains__(self, x): - return False - return len(x) == 0 or (x[-1] in NN and all(x[i]>x[i+1] for i in range(len(x)-1) if x[i]>0)) - -class StrictPartitions_all(StrictPartitions, DisjointUnionEnumeratedSets): - """ - Class of all strict partitions. - - EXAMPLES:: - - sage: from sage.combinat.partition import StrictPartitions - sage: StrictPartitions() - Strict Partitions - """ - def __init__(self): - """ - Initialize ``self``. - - TESTS:: - - sage: from sage.combinat.partition import StrictPartitions - sage: TestSuite(StrictPartitions()).run() - """ - # I'd rather use super() here but Partitions() complains - DisjointUnionEnumeratedSets.__init__(self, - family=Family(NonNegativeIntegers(), StrictPartitions_size), - facade=True, keepkey=False - ) - Partitions.__init__(self, is_infinite=True) - - def _repr_(self): - """ - Return a string representation of ``self``. - - TESTS:: - - sage: from sage.combinat.partition import StrictPartitions - sage: StrictPartitions(5) - Strict Partitions of the integer 5 - """ - return "Strict Partitions" - -class StrictPartitions_size(StrictPartitions): - """ - Strict Partitions of the integer ``size``. - - TESTS:: - - sage: TestSuite( sage.combinat.partition.StrictPartitions_size(0) ).run() - sage: TestSuite( sage.combinat.partition.StrictPartitions_size(10) ).run() - """ - - def __init__(self, n): - """ - Initialize ``self``. - - EXAMPLES:: - - sage: from sage.combinat.partition import StrictPartitions - sage: StrictPartitions(3) - Strict Partitions of the integer 3 - - TESTS:: - - sage: from sage.combinat.partition import StrictPartitions - sage: TestSuite(StrictPartitions(9)).run() - """ - Partitions.__init__(self, is_infinite=False) - self.n = n # would be better to call this size, but for consistency... - self.size = n - - def _repr_(self): - """ - Return a string representation of ``self``. - - TESTS:: - - sage: from sage.combinat.partition import StrictPartitions - sage: StrictPartitions(5) - Strict Partitions of the integer 5 - """ - return "Strict Partitions of the integer {}".format(self.n) - - def __contains__(self, x): - """ - Return ``True`` if ``x`` is contained in ``self`` and ``False`` - otherwise. - - Examples:: - - sage: from sage.combinat.partition import StrictPartitions - sage: [5,2] in StrictPartitions(7) - True - sage: [5,2] in StrictPartitions(5) - False - sage: [5,2,2] in StrictPartitions(5) - False - sage: [5,2,0,0] in StrictPartitions(7) - True - sage: Partition([5,2]) in StrictPartitions(7) - True - sage: Partition([5,2,1]) in StrictPartitions(7) - False - """ - return StrictPartitions.__contains__(self, x) and sum(x) == self.size - - def __iter__(self): - """ - Iterate over ``self``. - - EXAMPLES:: - - sage: from sage.combinat.partition import StrictPartitions - sage: StrictPartitions(10)[:] #indirect doct test - [[10], - [9, 1], - [8, 2], - [7, 3], - [7, 2, 1], - [6, 4], - [6, 3, 1], - [5, 4, 1], - [5, 3, 2], - [4, 3, 2, 1]] - """ - for p in self._fast_iterator(self.n, self.n+1): - yield self.element_class(self, p) - - def _fast_iterator(self, size, max): - """ - A fast (recursive) iterator which returns a list. - - This method is not intended to be called directy. - - INPUT: - - - ``size`` -- a positive integer, giving the size of the partitions - - - ``max`` -- a positive integer giving the maximu size of the parts of - the partitions to be returned - - OUTPUT: - - - an iterator of the strict partitions of size ``size`` - - EXAMPLES:: - - sage: from sage.combinat.partition import StrictPartitions - sage: StrictPartitions(7)[:] # indirect doc test - [[7], [6, 1], [5, 2], [4, 3], [4, 2, 1]] - """ - if size < max: - yield [size] - - for m in reversed(range(1, min(size, max))): - for mu in self._fast_iterator(size-m, m): - yield [m] + mu - return - - def an_element(self): - """ - Returns a partition in ``self``. - - EXAMPLES:: - - sage: from sage.combinat.partition import StrictPartitions - sage: StrictPartitions(4).an_element() # indirect doctest - [3, 1] - sage: StrictPartitions(0).an_element() - [] - sage: StrictPartitions(1).an_element() - [1] - """ - if self.n == 0: - elt = [] - elif self.n == 1: - elt = [1] - else: - elt = [self.n-1, 1] - return self.element_class(self, elt) - - def cardinality(self): - """ - Return the cardinality of ``self``. - - EXAMPLES:: - - sage: from sage.combinat.partition import StrictPartitions - sage: StrictPartitions(7).cardinality() - 5 - """ - return ZZ(len( [1 for p in self._fast_iterator(self.n, self.n+1)] )) - - def random_element(self, measure = 'uniform'): - r""" - Return a random strict partition. - - EXAMPLE: - - sage: from sage.combinat.partition import StrictPartitions - sage: StrictPartitions(7).cardinality() # random - """ - from sage.misc.prandom import randrange - partitions = self.list() - return partitions[randrange(len(partitions))] From 13b6c8a3dc35efc14a77d4c73ecf4a8bb538c665 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sun, 4 Jun 2017 20:12:10 -0700 Subject: [PATCH 240/740] added crystal structure --- src/sage/combinat/all.py | 2 +- src/sage/combinat/tableau_shifted_primed.py | 299 ++++++++++++++++++-- 2 files changed, 277 insertions(+), 24 deletions(-) diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index ef825ed255d..c175a962ad1 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -96,7 +96,7 @@ from .k_tableau import WeakTableau, WeakTableaux, StrongTableau, StrongTableaux lazy_import('sage.combinat.lr_tableau', ['LittlewoodRichardsonTableau', 'LittlewoodRichardsonTableaux']) -lazy_import('sage.combinat.tableau_shifted_primed', ['ShiftedPrimedTableaux', 'ShiftedPrimedTableau']) +lazy_import('sage.combinat.tableau_shifted_primed', ['ShiftedPrimedTableaux', 'ShiftedPrimedTableau','SPTCrystal']) #Words from .words.all import * diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index 26b2c376dcd..cc8936c1fd8 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -4,25 +4,25 @@ import numpy as np -from sage.combinat.partition import Partition, StrictPartitions -#from sage.combinat.permutation import Permutation -#from sage.combinat.posets.posets import Poset -#from sage.combinat.tableau import Tableaux +from sage.combinat.partition import Partition, StrictPartitions, OrderedPartitions +from sage.combinat.integer_vector import IntegerVectors from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets -#from sage.functions.other import factorial -#from sage.misc.cachefunc import cached_method from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass -#from sage.misc.misc_c import prod -#from sage.misc.prandom import randrange -#from sage.rings.integer import Integer -from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets -#from sage.sets.family import Family -#from sage.sets.non_negative_integers import NonNegativeIntegers + from sage.structure.list_clone import ClonableArray from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation +#Imports for the crystal +from sage.structure.element_wrapper import ElementWrapper +from sage.categories.classical_crystals import ClassicalCrystals +from sage.graphs.all import DiGraph +from sage.categories.enumerated_sets import EnumeratedSets +from sage.combinat.root_system.cartan_type import CartanType + + + @add_metaclass(InheritComparisonClasscallMetaclass) class ShiftedPrimedTableau(ClonableArray): @@ -237,6 +237,7 @@ def pp(self): max_len = len(str(round(max_ind))) string_list = '' for i,row in enumerate(self): + string_list += ' ' string_list += ' ' * i * (max_len) for val in row: if int(val)==val: @@ -246,7 +247,32 @@ def pp(self): string_list += '\n' string_list = string_list[:-2] print(string_list) - return + return + + def _latex_(self): + r""" + Return LaTex code for ``self``. + + EXAMPLES:: + + sage: T = ShiftedTableaux([4,2]) + sage: latex(T([[1,"2p",2,"3p"],[2,3]])) + + + """ + from sage.combinat.output import tex_from_array + L = list() + for i,row in enumerate(self): + num_list = [None]*i + for let in row: + if int(let)==let: + num_list.append(int(let)) + else: + num_list.append(str(int(let+0.5))+"'") + L.append(num_list) + + return tex_from_array(L) + def shape(self): """ @@ -363,7 +389,7 @@ def reading_word(self): return [tup[1] for tup in self.reading_word_with_positions()] - def crystal_f(self, ind): + def f(self, ind): """ A function to compute an action of the crystal operator F_i on an Primed Tableau using cases in the paper. @@ -384,7 +410,7 @@ def crystal_f(self, ind): 1 1 1 1 3' 2 2 2 3' 3 3 - sage: s = t.crystal_f(2) + sage: s = t.f(2) sage: print(s) None sage: t = ShiftedPrimedTableau([[1,1,1,1.5,2.5],[0,2,2,3,3],[0,0,3,4]]) @@ -392,7 +418,7 @@ def crystal_f(self, ind): 1 1 1 2' 3' 2 2 3 3 3 4 - sage: s = t.crystal_f(2) + sage: s = t.f(2) sage: s.pp() 1 1 1 2' 3' 2 3' 3 3 @@ -432,7 +458,7 @@ def crystal_f(self, ind): if tp_r+1==h or T[tp_r+1,tp_c]>ind+1 or T[tp_r+1,tp_c]<1: break - if T[tp_r+1,tp_r+1]==ind+1: + if tp_r<=tp_c and T[tp_r+1,tp_r+1]==ind+1: tp_r += 1 tp_c = tp_r break @@ -461,7 +487,7 @@ def crystal_f(self, ind): return(ShiftedPrimedTableau(T)) - def crystal_e(self,ind): + def e(self,ind): """ A function to compute an action of the crystal operator E_i on an Primed Tableau using cases in the paper. @@ -482,12 +508,12 @@ def crystal_e(self,ind): 1 1 1 2' 3' 2 3' 3 3 3 4 - sage: s = t.crystal_e(2) + sage: s = t.e(2) sage: s.pp() 1 1 1 2' 3' 2 2 3 3 3 4 - sage: t == s.crystal_f(2) + sage: t == s.f(2) True """ @@ -548,6 +574,39 @@ def crystal_e(self,ind): T = T.T - .5 return(ShiftedPrimedTableau(T)) + + def epsilon(self,i): + b = self + count = -1 + while b is not None: + b = b.e(i) + count +=1 + return count + + def phi(self,i): + b = self + count = -1 + while b is not None: + b = b.f(i) + count +=1 + return count + + def is_highest_weight(self): + read_w = self.reading_word() + count = {} + for l in read_w[::-1]: + try: + count[l] +=1 + except KeyError: + count[l] = 1 + if l>1: + try: + if count[l]>count[l-1]: + return False + except KeyError: + return False + return True + @@ -629,6 +688,16 @@ def __classcall_private__(cls, *args, **kwargs): """ weight = None shape = None + max_element = None + + if 'max_elt' in kwargs: + max_element = int(kwargs['max_elt']) + + if 'max_element' in kwargs: + max_element = int(kwargs['max_element']) + + if 'max' in kwargs: + max_element = int(kwargs['max']) if 'size' in kwargs and isinstance(kwargs['size'],(list,tuple,Partition)): shape = Partition(kwargs['size']) @@ -657,18 +726,29 @@ def __classcall_private__(cls, *args, **kwargs): raise ValueError('weight argument must be a tuple and shape argument must be a strictly increasing partition') else: raise ValueError('weight argument must be a tuple and shape argument must be a strictly increasing partition') - + if shape is not None: try: shape = Partition(shape) except ValueError: raise ValueError('{} is not a strict partition'.format(shape)) + if weight is not None: + while weight[-1]==0: + weight = weight[:-1] + + if max_element is not None and weight is not None: + if len(weight)!=max_element: + raise ValueError("maximum element can not be smaller then the length of the weight vector") + + if max_element is not None and shape is not None: + if max_element1 and isinstance(args[1],int): + n = args[1] + else: + if isinstance(args[0],int): + n = args[0] + if len(args)>1 and isinstance(args[1],(list,tuple,Partition)): + shape = tuple(args[1]) + if shape is None: + raise ValueError('input size of tableaux as a list or a tuple') + + if n is None: + raise ValueError('input crystal type') + + if n+1 < len(shape): + raise ValueError('crystal index should be greater or equal to the number of rows minus one') + return ShiftedPrimedTableauxCrystal(shape =shape, n=n) + + +class ShiftedPrimedTableauxCrystal(SPTCrystal): + def __init__(self, shape=[4,2],n=2): + Parent.__init__(self, category = ClassicalCrystals()) + self.n = n + self._shape = shape + self._cartan_type = CartanType(['A',n]) + self.module_generators = ShiftedPrimedTableaux(shape=shape,max_element=n+1).list_decreasing_weight() + + def _repr_(self): + return ("Crystal of Shifted Primed Tableaux of type A_%s of shape "%(self.n) + str(self._shape)) + +#################### +# Helper functions # +#################### + +def add_strip(sub_tab, full_tab, length): + if sum(sub_tab)+length > sum(full_tab): + raise ValueError("strip does not fit") + + if len(sub_tab)==0: + cliff_list = [] + else: + cliff_list = [int(sub_tab[0]!=full_tab[0])] + + for row in range(1,len(sub_tab)): + if sub_tab[row] == full_tab[row]: + cliff_list.append(0) + elif sub_tab[row-1]-1 == sub_tab[row]: + cliff_list[-1] += 1 + else: + cliff_list.append(1) + + if len(sub_tab) j) for j in range(cliff)]) + row += cliff + plat_list = list() + + if len(sub_tab)0: + plat_list.append(full_tab[0] - sub_tab[0]- primed_strip[0]) + else: + plat_list.append(full_tab[0]) + + if sum(plat_list) Date: Mon, 19 Jun 2017 17:11:23 -0700 Subject: [PATCH 241/740] Fixed functions --- src/sage/combinat/tableau_shifted_primed.py | 466 ++++++++++++-------- 1 file changed, 281 insertions(+), 185 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index cc8936c1fd8..eef7b0740dc 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -1,10 +1,7 @@ -#from __future__ import print_function, absolute_import from six import add_metaclass -#from six.moves import range, zip, map - import numpy as np -from sage.combinat.partition import Partition, StrictPartitions, OrderedPartitions +from sage.combinat.partition import Partition,StrictPartitions, OrderedPartitions from sage.combinat.integer_vector import IntegerVectors from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets @@ -14,16 +11,12 @@ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation -#Imports for the crystal -from sage.structure.element_wrapper import ElementWrapper +# Imports for the crystal + from sage.categories.classical_crystals import ClassicalCrystals -from sage.graphs.all import DiGraph -from sage.categories.enumerated_sets import EnumeratedSets from sage.combinat.root_system.cartan_type import CartanType - - @add_metaclass(InheritComparisonClasscallMetaclass) class ShiftedPrimedTableau(ClonableArray): r""" @@ -46,15 +39,17 @@ class ShiftedPrimedTableau(ClonableArray): sage: t[1] (2.0, 2.5) sage: t = ShiftedPrimedTableau([[1,2,2.5,3],[0,2,2.5]]) - ValueError: [(1.0, 2.0, 2.5, 3.0), (2.0, 2.5)] is not an element of Shifted Primed Tableaux of weight (1, 2, 3) and shape [4, 2] + Traceback (most recent call last): + ... + ValueError: [(1.0, 2.0, 2.5, 3.0), (2.0, 2.5)] is not an element of Shifted Primed Tableaux """ @staticmethod def __classcall_private__(cls, T): - r""" This ensures that a shifted tableau is only ever constructed as an ``element_class`` call of an appropriate parent. + EXAMPLES:: sage: data = [[1,"2'","2",3],[2,"3'"]] @@ -62,7 +57,6 @@ def __classcall_private__(cls, T): sage: T = ShiftedPrimedTableaux(shape=[4,2],weight=(1,3,2)) sage: t == T(data) True - sage: S = ShiftedPrimedTableaux(shape=[4,2]) sage: t == S(data) True @@ -107,12 +101,9 @@ def __init__(self,parent, T): A shifted primed tableau is shallowly immutable, the rows are represented as tuples. + :: + sage: t = ShiftedPrimedTableau([[1,"2p","3p",3],[2,"3p"]]) - sage: t0 = t[0] - sage: t0[1] = 3 - Traceback (most recent call last): - ... - TypeError: 'tuple' object does not support item assignment sage: t[0][1] = 3 Traceback (most recent call last): ... @@ -130,7 +121,7 @@ def __init__(self,parent, T): # method. def __eq__(self, other): - r""" + """ Check whether ``self`` is equal to ``other``. INPUT: @@ -141,11 +132,9 @@ def __eq__(self, other): Boolean. - TESTS:: + EXAMPLES:: sage: t = ShiftedPrimedTableau([[1,"2p"]]) - sage: t == 0 - False sage: t == ShiftedPrimedTableaux([2])([1,"2p"]) True """ @@ -157,14 +146,10 @@ def __eq__(self, other): def to_matrix(self): """ - Returns a 2-dimensional numpy.array representation of a shifted tableau + Return a 2-dimensional numpy.array representation of a shifted tableau EXAMPLES:: - sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) - sage: t.to_matrix() - array([[ 1. , 1.5, 2. , 2. ], - [ 0. , 2. , 2.5, 0. ]]) sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p'],[3]]) sage: mat = t.to_matrix() sage: mat @@ -208,17 +193,19 @@ def check(self): def _repr_(self): """ - Represents Shifted Primed Tableau as a list of half-integers + Represent Shifted Primed Tableau as a list of rows, rows are represented as tuples of half-integers. + EXAMPLES:: + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t - [[1.0, 1.5, 2.0, 2.0], [2.0, 2.5]] + [(1.0, 1.5, 2.0, 2.0), (2.0, 2.5)] """ - return repr([list(_) for _ in self]) + return repr([tuple(_) for _ in self]) def pp(self): """ - Prints out a nice version of ``self``. + Print out a nice version of ``self``. EXAMPLES:: @@ -227,6 +214,7 @@ def pp(self): 1 2' 2 2 2 3' sage: t = ShiftedPrimedTableau([[10,'11p',11,11],[11,'12']]) + sage: t.pp() 10 11' 11 11 11 12 """ @@ -255,10 +243,14 @@ def _latex_(self): EXAMPLES:: - sage: T = ShiftedTableaux([4,2]) + sage: T = ShiftedPrimedTableaux([4,2]) sage: latex(T([[1,"2p",2,"3p"],[2,3]])) - - + {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} + \raisebox{-.6ex}{$\begin{array}[b]{*{4}c}\cline{1-4} + \lr{1}&\lr{2'}&\lr{2}&\lr{3'}\\\cline{1-4} + &\lr{2}&\lr{3}\\\cline{2-3} + \end{array}$} + } """ from sage.combinat.output import tex_from_array L = list() @@ -276,13 +268,13 @@ def _latex_(self): def shape(self): """ - Returnes the shape of the underlying partition of ``self`` in list format + Return the shape of the underlying partition of ``self`` in list format. EXAMPLES:: sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t.shape() - [4,2] + [4, 2] """ return ([len(row) for row in self]) @@ -309,7 +301,7 @@ def __call__(self,*cell): Traceback (most recent call last): ... IndexError: the cell (1,2) is not contained in the shifted tableau - [[1.0, 1.5, 2.0, 2.0], [2.0, 2.5]] + [(1.0, 1.5, 2.0, 2.0), (2.0, 2.5)] sage: t((1,1)) 2.5 """ @@ -331,11 +323,11 @@ def weight(self): The weight of a shifted primed tableau is defined to be the vector with i-th component equal to the number of letters i and i' in the tableau. - EXAMPLES:: + EXAMPLE:: sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t.weight() - (1,4,1) + (1, 4, 1) """ flat = [round(item) for sublist in self for item in sublist] if flat == []: @@ -356,7 +348,8 @@ def reading_word_with_positions(self): (2) Then list all unprimed elements, row by row, in increasing order within each row, moving from the bottommost row to the top. - EXAMPLES:: + EXAMPLE:: + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t.reading_word_with_positions() [((1, 2), 3), ((0, 1), 2), ((1, 1), 2), ((0, 0), 1), ((0, 2), 2), ((0, 3), 2)] @@ -381,7 +374,8 @@ def reading_word(self): (2) Then list all unprimed elements, row by row, in increasing order within each row, moving from the bottommost row to the top. - EXAMPLES:: + EXAMPLE:: + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t.reading_word() [3, 2, 2, 1, 2, 2] @@ -391,40 +385,43 @@ def reading_word(self): def f(self, ind): """ - A function to compute an action of the crystal operator F_i on an Primed Tableau - using cases in the paper. + A function to compute the action of the crystal operator $f_i$ on a Shifted Primed Tableau + using cases from the paper [GPS.17]. INPUT: - self -- primed tableau - ind -- index of the crystal operator F_i. + self -- shifted primed tableau + ind -- index of the crystal operator $f_i$. OUTPUT: Primed tableau or 'None'. + + EXAMPLES:: - EXAMPLE: - - sage: t = ShiftedPrimedTableau([[1,1,1,1,2.5],[2,2,2,2.5],[3,3]]) - sage: t.pp() - 1 1 1 1 3' - 2 2 2 3' - 3 3 - sage: s = t.f(2) - sage: print(s) - None - sage: t = ShiftedPrimedTableau([[1,1,1,1.5,2.5],[0,2,2,3,3],[0,0,3,4]]) - sage: t.pp() - 1 1 1 2' 3' - 2 2 3 3 - 3 4 - sage: s = t.f(2) - sage: s.pp() - 1 1 1 2' 3' - 2 3' 3 3 - 3 4 - + sage: t = ShiftedPrimedTableau([[1,1,1,1,2.5],[2,2,2,2.5],[3,3]]) + sage: t.pp() + 1 1 1 1 3' + 2 2 2 3' + 3 3 + sage: s = t.f(2) + sage: print(s) + None + sage: t = ShiftedPrimedTableau([[1,1,1,1.5,2.5],[0,2,2,3,3],[0,0,3,4]]) + sage: t.pp() + 1 1 1 2' 3' + 2 2 3 3 + 3 4 + sage: s = t.f(2) + sage: s.pp() + 1 1 1 2' 3' + 2 3' 3 3 + 3 4 + """ + if self is None: + return None + T = self.to_matrix() read_word = self.reading_word_with_positions() @@ -489,35 +486,36 @@ def f(self, ind): def e(self,ind): """ - A function to compute an action of the crystal operator E_i on an Primed Tableau - using cases in the paper. + A function to compute an action of the crystal operator $e_i$ on a Primed Tableau + using cases from the paper [GPS.17]. INPUT: - self -- primed tableau - ind -- index of the crystal operator E_i. + self -- shifted primed tableau + ind -- index of the crystal operator $e_i$. OUTPUT: Primed tableau or 'None'. - EXAMPLE: + EXAMPLES:: - sage: t = ShiftedPrimedTableau([[1,1,1,1.5,2.5],[0,2,"3p",3,3],[0,0,3,4]]) - sage: t.pp() - 1 1 1 2' 3' - 2 3' 3 3 - 3 4 - sage: s = t.e(2) - sage: s.pp() - 1 1 1 2' 3' - 2 2 3 3 - 3 4 - sage: t == s.f(2) - True + sage: t = ShiftedPrimedTableau([[1,1,1,1.5,2.5],[0,2,"3p",3,3],[0,0,3,4]]) + sage: t.pp() + 1 1 1 2' 3' + 2 3' 3 3 + 3 4 + sage: s = t.e(2) + sage: s.pp() + 1 1 1 2' 3' + 2 2 3 3 + 3 4 + sage: t == s.f(2) + True """ - + if self is None: + return None T = self.to_matrix() read_word = self.reading_word_with_positions() @@ -576,6 +574,29 @@ def e(self,ind): return(ShiftedPrimedTableau(T)) def epsilon(self,i): + r""" + Compute value of the crystal function $\varepsilon_i$ applied to a shifted primed tableau ``self``. + The function $\varepsilon_i$ is defined to be the maximal number of operators $e_i$ one can apply to + ``self`` before vanishing into ``None``. + + INPUT: + + self -- shifted primed tableau + ind -- index of the function $\varepsilon_i$ associated with a crystal operator $e_i$. + + OUTPUT: + + Value of the function $\varepsilon_i$. + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.5, 2.5), (2.0, 2.5, 3.0, 3.0), (3.0, 4.0)]) + sage: t.epsilon(2) + 3 + sage: s = t.e(2).e(2).e(2) + sage: s.epsilon(2) + 0 + """ b = self count = -1 while b is not None: @@ -584,6 +605,29 @@ def epsilon(self,i): return count def phi(self,i): + r""" + Compute value of the crystal function $\phi_i$ applied to a shifted primed tableau ``self``. + The function $\phi_i$ is defined to be the maximal number of operators $f_i$ one can apply to + ``self`` before vanishing into ``None``. + + INPUT: + + self -- shifted primed tableau + ind -- index of the function $\varepsilon_i$ associated with a crystal operator $f_i$. + + OUTPUT: + + Value of the function $\varepsilon_i$. + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.5, 2.0), (2.0, 2.0, 2.0, 2.5), (3.0, 4.0)]) + sage: t.phi(2) + 3 + sage: s = t.f(2).f(2).f(2) + sage: s.phi(2) + 0 + """ b = self count = -1 while b is not None: @@ -592,6 +636,21 @@ def phi(self,i): return count def is_highest_weight(self): + """ + Check wether the shifted primed tableau ``self`` is a highest weight element of the crystal. + Highest weight element defined to be vanishing under any crystal operator $e_i$. + + EXAMPLE:: + + sage: t = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.0, 1.0), (2.0, 2.0, 2.0, 2.5), (3.0, 3.0)]) + sage: print(t.e(1)) + None + sage: print(t.e(2)) + None + sage: t.is_highest_weight() + True + + """ read_w = self.reading_word() count = {} for l in read_w[::-1]: @@ -622,12 +681,13 @@ class ShiftedPrimedTableaux(UniqueRepresentation, Parent): - with no argument, the class of all primed tableaux - - with a partition argument, the class of all primed tableaux of that - shape (infinite set if we don't specify the weight) + - with a list or partition argument, the class of all primed tableaux of that + shape (infinite set if we don't specify the weight or maximum element) + - with an additional integer argument ``max_element``, the class of all primed + tableaux of a given shape and a given maximum element - - with a weight argument, the class of all primed tableaux of that + - with a tuple argument, the class of all primed tableaux of that weight (finite set) - A primed tableau is a shifted tableau on the alphabet X' = {1' < 1 < 2' < 2 <...< n' < n} such that @@ -641,13 +701,33 @@ class ShiftedPrimedTableaux(UniqueRepresentation, Parent): The sum of the entries in the weight vector must be equal to the number of boxes in the partition. + All classes of Shifted Primed Tableaux are not iterateble. - TESTS:: + EXAMPLES:: - sage: ShiftedPrimedTableaux([]) - Shifted tableaux of shape [] - sage: SPT = ShiftedPrimedTableaux((1,2,2),[3,2]); SPT - Shifted primed tableaux of shape [3, 2] and weight (1,2,2) + sage: SPT = ShiftedPrimedTableaux(weight=(1,2,2), shape=[3,2]); SPT + Shifted Primed Tableaux of weight (1, 2, 2) and shape [3, 2] + sage: SPT.list() + [[(1.0, 2.0, 2.0), (3.0, 3.0)], + [(1.0, 1.5, 2.5), (2.0, 3.0)], + [(1.0, 1.5, 2.5), (2.0, 2.5)], + [(1.0, 1.5, 2.0), (3.0, 3.0)]] + + sage: SPT = ShiftedPrimedTableaux((1,2)); SPT + Shifted Primed Tableaux of weight (1, 2) + sage: list(SPT) + [[(1.0, 2.0, 2.0)], [(1.0, 1.5, 2.0)], [(1.0, 1.5), (2.0,)]] + + sage: SPT = ShiftedPrimedTableaux([3,2], max_element = 2); SPT + Shifted Primed Tableaux of shape [3, 2] and maximum element 2 + sage: list(SPT) + [[(1.0, 1.0, 1.0), (2.0, 2.0)], [(1.0, 1.0, 1.5), (2.0, 2.0)]] + sage: SPT.list_highest_weight() + [[(1.0, 1.0, 1.0), (2.0, 2.0)]] + + .. SEEALSO:: + + - :class:`ShiftedPrimedTableau` """ @staticmethod @@ -657,34 +737,32 @@ def __classcall_private__(cls, *args, **kwargs): arguments. See the documentation for :class:`ShiftedPrimedTableaux` for more information. + TESTS:: - sage: ShiftedPrimedTableaux() - Shifted Primed Tableaux + sage: ShiftedPrimedTableaux([]) + Shifted Primed Tableaux of shape [] sage: ShiftedPrimedTableaux(3) Traceback (most recent call last): ... - ValueError: weight argument must be a tuple and shape argument must be a strictly increasing list + ValueError: weight argument must be a tuple and shape argument must be a strictly increasing partition - sage: ShiftedPrimedTableaux([2,1]) - Shifted tableaux of shape [2, 1] + sage: ShiftedPrimedTableaux(weight=(2,2,2), shape=[3,2]) + Traceback (most recent call last): + ... + ValueError: the sum of weights should match the sum of parts of the shape sage: ShiftedPrimedTableaux([[1]]) Traceback (most recent call last): ... - ValueError: [[1]] is not a strict partition + ValueError: [[1]] is not a partition - sage: ShiftedPrimedTableaux(weight=(1,2,2), shape=[3,2]) - Shifted Primed Tableaux of weight (1, 2, 2) and shape [3, 2] - - sage: ShiftedPrimedTableaux(weight=(2,2,2), shape=[3,2]) + sage: ShiftedPrimedTableaux(weight=(2,2,2), max_element=2) Traceback (most recent call last): ... - ValueError: the sum of weights should match the sum of parts of the shape + ValueError: maximum element can not be smaller then the length of the weight vector - sage: ShiftedPrimedTableaux((3,2,1)) - Shifted tableaux of weight (3, 2, 1) """ weight = None shape = None @@ -731,7 +809,8 @@ def __classcall_private__(cls, *args, **kwargs): try: shape = Partition(shape) except ValueError: - raise ValueError('{} is not a strict partition'.format(shape)) + raise ValueError('{} is not a partition'.format(shape)) + if weight is not None: while weight[-1]==0: weight = weight[:-1] @@ -743,8 +822,11 @@ def __classcall_private__(cls, *args, **kwargs): if max_element is not None and shape is not None: if max_element shape_[i+1] for i in range(len(shape_)-1)) + for tab in ShiftedPrimedTableaux(shape = shape_, weight = self._weight)) + + class ShiftedPrimedTableaux_weight_shape(ShiftedPrimedTableaux): Element = ShiftedPrimedTableau @@ -999,13 +1094,19 @@ def __contains__(self, T): return(ShiftedPrimedTableaux.__contains__(self, t) and weight == self._weight and [len(row) for row in t] == self._shape) - def _element_constructor_(self, t): + def _element_constructor_(self, T): + t = preprocessing(T) + if not t in ShiftedPrimedTableaux(): + raise ValueError("{!r} is not an element of Shifted Primed Tableaux" .format(t)) if not t in self: raise ValueError("{} is not an element of {}".format(t, self)) return self.element_class(self, t) def __iter__(self): + if not self._shape.dominates(Partition(sorted(list(self._weight), key=int, reverse=True))): + return + yield #TODO: More efficient algorithm with generators full_shape = self._shape sub_tab = [] @@ -1013,9 +1114,7 @@ def __iter__(self): for i,w in enumerate(self._weight): tab_list_old = tab_list_new tab_list_new = [] - - for sub_tab in tab_list_old: - + for sub_tab in tab_list_old: sub_shape = [len(sub_tab[r]) for r in range(len(sub_tab))] for strip in add_strip(sub_shape, full_shape, w): l = int(len(strip)/2) @@ -1028,32 +1127,9 @@ def __iter__(self): new_tab = [sub_tab[r] + [float(i+.5)]*int(strip[r]) + [float(i+1)]*int(strip[-r-1]) for r in range(l)] tab_list_new.append(new_tab) - return (ShiftedPrimedTableau(tab) for tab in tab_list_new) + for tab in tab_list_new: + yield(ShiftedPrimedTableau(tab)) - - def list(self): - full_shape = self._shape - sub_tab = [] - tab_list_new = [[]] - for i,w in enumerate(self._weight): - tab_list_old = tab_list_new - tab_list_new = [] - - for sub_tab in tab_list_old: - - sub_shape = [len(sub_tab[r]) for r in range(len(sub_tab))] - for strip in add_strip(sub_shape, full_shape, w): - l = int(len(strip)/2) - if len(sub_shape) Date: Sat, 24 Jun 2017 09:20:54 +0300 Subject: [PATCH 242/740] Added documentation, cleaned up the code --- src/sage/combinat/all.py | 3 +- src/sage/combinat/partition.py | 264 ---------- src/sage/combinat/tableau_shifted_primed.py | 539 ++++++++++++++------ 3 files changed, 388 insertions(+), 418 deletions(-) diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index c175a962ad1..dc6b9dddf05 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -60,8 +60,7 @@ #Partitions from .partition import Partition, Partitions, PartitionsInBox,\ OrderedPartitions, PartitionsGreatestLE, PartitionsGreatestEQ,\ - PartitionsGreatestLE, PartitionsGreatestEQ, number_of_partitions,\ - StrictPartitions + PartitionsGreatestLE, PartitionsGreatestEQ, number_of_partitions from sage.combinat.partition_tuple import PartitionTuple, PartitionTuples from .skew_partition import SkewPartition, SkewPartitions diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 6a49d8398e6..dfe6fb4eb03 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -62,7 +62,6 @@ all in the category framework except ``PartitionsRestricted`` (which will eventually be removed). Cleaned up documentation. -- Andrew Mathas (2016-07-20): Added ``StrictPartitions`` EXAMPLES: @@ -7323,269 +7322,6 @@ def cardinality(self): if self._ell > self.n: return Partitions_n.cardinality(self) return ZZ.sum(1 for x in self) - -###################### -# Strict Partitions # -###################### -class StrictPartitions(Partitions): - r""" - The class of **strict partitions**. - - A strict partition of an integer `n` is a :class:`Partition` with - distinct parts. - - INPUT: - - - ``n`` -- a non-negative integer, the size of the strict partitions - """ - @staticmethod - def __classcall_private__(cls, size=None): - """ - Normalize the input to ensure a unique representation. - - TESTS:: - - sage: from sage.combinat.partition import StrictPartitions - sage: P = StrictPartitions() - sage: P12 = StrictPartitions(12) - sage: P is P12 - False - """ - if size is not None: - return StrictPartitions_size( ZZ(size) ) - - return StrictPartitions_all() - - def __contains__(self, x): - """ - Return ``True`` if ``x`` is contained in ``self`` and ``False`` - otherwise. - - EXAMPLES:: - - sage: from sage.combinat.partition import StrictPartitions - sage: [5,2] in StrictPartitions() - True - sage: [5,2] in StrictPartitions(7) - True - sage: [5,2] in StrictPartitions(5) - False - sage: [5,2,2] in StrictPartitions(5) - False - sage: [5,2,0] in StrictPartitions(7) - True - sage: Partition([5,2]) in StrictPartitions() - True - sage: Partition([5,2]) in StrictPartitions(7) - True - sage: Partition([5,2]) in StrictPartitions(3) - False - """ - if not Partitions.__contains__(self, x): - return False - return len(x) == 0 or (x[-1] in NN and all(x[i]>x[i+1] for i in range(len(x)-1) if x[i]>0)) - -class StrictPartitions_all(StrictPartitions, DisjointUnionEnumeratedSets): - """ - Class of all strict partitions. - - EXAMPLES:: - - sage: from sage.combinat.partition import StrictPartitions - sage: StrictPartitions() - Strict Partitions - """ - def __init__(self): - """ - Initialize ``self``. - - TESTS:: - - sage: from sage.combinat.partition import StrictPartitions - sage: TestSuite(StrictPartitions()).run() - """ - # I'd rather use super() here but Partitions() complains - DisjointUnionEnumeratedSets.__init__(self, - family=Family(NonNegativeIntegers(), StrictPartitions_size), - facade=True, keepkey=False - ) - Partitions.__init__(self, is_infinite=True) - - def _repr_(self): - """ - Return a string representation of ``self``. - - TESTS:: - - sage: from sage.combinat.partition import StrictPartitions - sage: StrictPartitions(5) - Strict Partitions of the integer 5 - """ - return "Strict Partitions" - -class StrictPartitions_size(StrictPartitions): - """ - Strict Partitions of the integer ``size``. - - TESTS:: - - sage: TestSuite( sage.combinat.partition.StrictPartitions_size(0) ).run() - sage: TestSuite( sage.combinat.partition.StrictPartitions_size(10) ).run() - """ - - def __init__(self, n): - """ - Initialize ``self``. - - EXAMPLES:: - - sage: from sage.combinat.partition import StrictPartitions - sage: StrictPartitions(3) - Strict Partitions of the integer 3 - - TESTS:: - - sage: from sage.combinat.partition import StrictPartitions - sage: TestSuite(StrictPartitions(9)).run() - """ - Partitions.__init__(self, is_infinite=False) - self.n = n # would be better to call this size, but for consistency... - self.size = n - - def _repr_(self): - """ - Return a string representation of ``self``. - - TESTS:: - - sage: from sage.combinat.partition import StrictPartitions - sage: StrictPartitions(5) - Strict Partitions of the integer 5 - """ - return "Strict Partitions of the integer {}".format(self.n) - - def __contains__(self, x): - """ - Return ``True`` if ``x`` is contained in ``self`` and ``False`` - otherwise. - - Examples:: - - sage: from sage.combinat.partition import StrictPartitions - sage: [5,2] in StrictPartitions(7) - True - sage: [5,2] in StrictPartitions(5) - False - sage: [5,2,2] in StrictPartitions(5) - False - sage: [5,2,0,0] in StrictPartitions(7) - True - sage: Partition([5,2]) in StrictPartitions(7) - True - sage: Partition([5,2,1]) in StrictPartitions(7) - False - """ - return StrictPartitions.__contains__(self, x) and sum(x) == self.size - - def __iter__(self): - """ - Iterate over ``self``. - - EXAMPLES:: - - sage: from sage.combinat.partition import StrictPartitions - sage: StrictPartitions(10)[:] #indirect doct test - [[10], - [9, 1], - [8, 2], - [7, 3], - [7, 2, 1], - [6, 4], - [6, 3, 1], - [5, 4, 1], - [5, 3, 2], - [4, 3, 2, 1]] - """ - for p in self._fast_iterator(self.n, self.n+1): - yield self.element_class(self, p) - - def _fast_iterator(self, size, max): - """ - A fast (recursive) iterator which returns a list. - - This method is not intended to be called directy. - - INPUT: - - - ``size`` -- a positive integer, giving the size of the partitions - - - ``max`` -- a positive integer giving the maximu size of the parts of - the partitions to be returned - - OUTPUT: - - - an iterator of the strict partitions of size ``size`` - - EXAMPLES:: - - sage: from sage.combinat.partition import StrictPartitions - sage: StrictPartitions(7)[:] # indirect doc test - [[7], [6, 1], [5, 2], [4, 3], [4, 2, 1]] - """ - if size < max: - yield [size] - - for m in reversed(range(1, min(size, max))): - for mu in self._fast_iterator(size-m, m): - yield [m] + mu - return - - def an_element(self): - """ - Returns a partition in ``self``. - - EXAMPLES:: - - sage: from sage.combinat.partition import StrictPartitions - sage: StrictPartitions(4).an_element() # indirect doctest - [3, 1] - sage: StrictPartitions(0).an_element() - [] - sage: StrictPartitions(1).an_element() - [1] - """ - if self.n == 0: - elt = [] - elif self.n == 1: - elt = [1] - else: - elt = [self.n-1, 1] - return self.element_class(self, elt) - - def cardinality(self): - """ - Return the cardinality of ``self``. - - EXAMPLES:: - - sage: from sage.combinat.partition import StrictPartitions - sage: StrictPartitions(7).cardinality() - 5 - """ - return ZZ(len( [1 for p in self._fast_iterator(self.n, self.n+1)] )) - - def random_element(self, measure = 'uniform'): - r""" - Return a random strict partition. - - EXAMPLE: - - sage: from sage.combinat.partition import StrictPartitions - sage: StrictPartitions(7).cardinality() # random - """ - from sage.misc.prandom import randrange - partitions = self.list() - return partitions[randrange(len(partitions))] ###################### # Ordered Partitions # diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index eef7b0740dc..92f16feb41a 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -1,7 +1,7 @@ from six import add_metaclass import numpy as np -from sage.combinat.partition import Partition,StrictPartitions, OrderedPartitions +from sage.combinat.partition import Partition, OrderedPartitions from sage.combinat.integer_vector import IntegerVectors from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets @@ -65,19 +65,8 @@ def __classcall_private__(cls, T): if isinstance(T, cls): return T - - t = preprocessing(T) - - shape = [len(row) for row in t] - flat = [round(item) for sublist in t for item in sublist] - if flat == []: - max_ind = 0 - else: - max_ind = int(max(flat)) - weight = tuple([flat.count(i+1) for i in range(max_ind)]) - - return ShiftedPrimedTableaux(shape = shape, weight = weight)(T) + return ShiftedPrimedTableaux()(T) def __init__(self,parent, T): @@ -109,16 +98,66 @@ def __init__(self,parent, T): ... TypeError: 'tuple' object does not support item assignment """ - t = preprocessing(T) - - if isinstance(t, ShiftedPrimedTableau): - # Since we are (supposed to be) immutable, we can share the underlying data - ClonableArray.__init__(self, parent, t) + + if isinstance(T,ShiftedPrimedTableau): + ClonableArray.__init__(self, parent, T) return + + if not isinstance(T, list): + try: + t_ = list(T) + except TypeError: + t_ = [T] + else: + t_ = T + + if not all(isinstance(row, (list,tuple,np.ndarray)) for row in t_): + t_ = [t_] + + t = [] + # Preprocessing list t for primes and other symbols + for row in t_: + row_new = [] + for element in row: + + if isinstance(element,str): + if element[-1] == "'" and element[:-1].isdigit() == True: # Check if an element has "'" at the end + row_new.append(float(float(element[:-1]) - .5)) + continue + if element[-1] == "p" and element[:-1].isdigit() == True: # Check if an element has "p" at the end + row_new.append(float(float(element[:-1]) - .5)) + continue + try: + if int(float(element)*2) == float(element)*2: # Check if an element is a half-integer + row_new.append(float(element)) + continue + else: + raise ValueError("all numbers must be half-integers") + + except (TypeError,ValueError): + raise ValueError("primed elements should be half-integers or have symbols p or ' at the end") + + t.append(row_new) + + #Accounting for zeros at the beginning and at the end of a row' + # TODO: one line + i=0 + while i < len(t): + row = t[i] + try: + while row[0]==0: + row.pop(0) + while row[-1]==0: + row.pop(-1) + except IndexError: + t.remove(t[i]) + continue + t[i] = row + i += 1 + + t = [tuple(_) for _ in t] ClonableArray.__init__(self, parent, t) - # This dispatches the input verification to the :meth:`check` - # method. def __eq__(self, other): """ @@ -135,13 +174,15 @@ def __eq__(self, other): EXAMPLES:: sage: t = ShiftedPrimedTableau([[1,"2p"]]) - sage: t == ShiftedPrimedTableaux([2])([1,"2p"]) + sage: t == ShiftedPrimedTableaux([2])([1,1.5]) True """ - if isinstance(other, ShiftedPrimedTableau): - return list(self) == list(other) - else: - return list(self) == preprocessing(other) + try: + Tab = ShiftedPrimedTableau(other) + except ValueError: + return False + return (list(self) == list(Tab)) + def to_matrix(self): @@ -176,7 +217,7 @@ def check(self): sage: t = T([[1,'2p',2,2],[2,'3p']]) sage: t.check() """ - if [len(_) for _ in self] not in StrictPartitions(): + if not all(len(self[i]) > len(self[i+1]) for i in range(len(self)-1)): raise ValueError('shape must be a strict partition') for i,row in enumerate(self): if i > 0: @@ -265,6 +306,18 @@ def _latex_(self): return tex_from_array(L) + def max_element(self): + """ + Return the maximum element in the primed tableaux ``self``, rounded up. + + EXAMPLE:: + + sage: Tab = ShiftedPrimedTableau([(1,1,1.5,2.5),(2,2)]) + sage: Tab.max_element() + 3.0 + """ + return round(max(flatten(self))) + def shape(self): """ @@ -730,6 +783,8 @@ class ShiftedPrimedTableaux(UniqueRepresentation, Parent): - :class:`ShiftedPrimedTableau` """ + Element = ShiftedPrimedTableau + @staticmethod def __classcall_private__(cls, *args, **kwargs): r""" @@ -778,10 +833,10 @@ def __classcall_private__(cls, *args, **kwargs): max_element = int(kwargs['max']) if 'size' in kwargs and isinstance(kwargs['size'],(list,tuple,Partition)): - shape = Partition(kwargs['size']) + shape = list(kwargs['size']) if 'shape' in kwargs: - shape = Partition(kwargs['shape']) + shape = list(kwargs['shape']) if 'weight' in kwargs: weight = tuple(kwargs['weight']) @@ -809,7 +864,7 @@ def __classcall_private__(cls, *args, **kwargs): try: shape = Partition(shape) except ValueError: - raise ValueError('{} is not a partition'.format(shape)) + raise ValueError('shape {} is not a partition'.format(shape)) if weight is not None: while weight[-1]==0: @@ -829,14 +884,14 @@ def __classcall_private__(cls, *args, **kwargs): raise ValueError("maximum element can not be the only argument, specify shape or weight") return ShiftedPrimedTableaux_all() - elif weight is None and shape in StrictPartitions(): + elif weight is None and all(shape[i]>shape[i+1] for i in range(len(shape)-1)): return ShiftedPrimedTableaux_shape(Partition(shape), max_element) elif shape is None: return ShiftedPrimedTableaux_weight(weight) - if shape not in StrictPartitions(): - raise ValueError("{} is not a strict partition".format(shape)) + if not all(shape[i]>shape[i+1] for i in range(len(shape)-1)): + raise ValueError("shape {} is not a strict partition".format(shape)) if sum(shape) != sum(weight): raise ValueError("the sum of weights should match the sum of parts of the shape") @@ -859,69 +914,117 @@ def __contains__(self, T): False sage: [1,1,1] in ShiftedPrimedTableaux() True + + TESTS:: + sage: 1 in ShiftedPrimedTableaux() + True + sage: [] in ShiftedPrimedTableaux() + True """ - if isinstance(T, ShiftedPrimedTableau) or T == []: + try: + self.element_class(self,T) return True - - t=preprocessing(T) - - if [len(_) for _ in t] not in StrictPartitions(): - return False # must have strict partition shape - - for i,row in enumerate(t): - if i > 0: - if not all(val > t[i-1][j+1] for j,val in enumerate(row) if int(val) == val): - return False - if not all(val >= t[i-1][j+1] for j,val in enumerate(row) if int(val) != val): - return False - if not all(row[j] <= row[j+1] for j in range(len(row)-1) if int(row[j]) == row[j]): - return False - if not all(row[j] < row[j+1] for j in range(len(row)-1) if int(row[j]) != row[j]): - return False - if not all(int(row[0]) == row[0] for row in t): + except ValueError: return False - return True - _is_a = __contains__ class ShiftedPrimedTableaux_all(ShiftedPrimedTableaux): - + """ + The class of all Shifted Primed Tableaux. + """ Element = ShiftedPrimedTableau def __init__(self): + """ + Initializes the class of all shifted tableaux. + + TESTS:: + + sage: [[1,1.5],[2]] in ShiftedPrimedTableaux() + True + sage: [[1,1.5],[1.5]] in ShiftedPrimedTableaux() + False + sage: [[1,1],[1]] in ShiftedPrimedTableaux() + False + sage: [[1,1],[2,2]] in ShiftedPrimedTableaux() + False + """ Parent.__init__(self, category=FiniteEnumeratedSets()) def _repr_(self): + """ + Return a string representation of ``self``. + + TESTS:: + + sage: ShiftedPrimedTableaux() + ShiftedPrimedTableaux + """ return "Shifted Primed Tableaux" def _element_constructor_(self, T): - t = preprocessing(T) - if not t in self: - raise ValueError("{} is not an element of {}".format(t, self)) - - return self.element_class(self, t) + """ + Constructs an object from ``T`` as an element of ``self``, if + possible. - def __contains__(self, x): - - if isinstance(x, ShiftedPrimedTableau): + INPUT: + + - ``T`` -- data which can be interpreted as a tableau + + OUTPUT: + + - the corresponding tableau object + + TESTS:: + + sage: Tab=ShiftedPrimedTableaux()([1,1,"2p"]); Tab + [(1.0, 1.0, 1.5)] + sage: Tab.parent() + Shifted Primed Tableaux + sage: Tab=ShiftedPrimedTableaux()([[1,1,2],[2,2]]) + Traceback (most recent call last): + ... + ValueError: [[1, 1, 2], [2, 2]] is not an element of Shifted Primed Tableaux + + """ + try: + return self.element_class(self,T) + except ValueError: + raise ValueError ("{} is not an element of Shifted Primed Tableaux" + .format(T)) + + + def __contains__(self, T): + """ + TESTS:: + + sage: [1,1,2] in ShiftedPrimedTableaux() + True + sage: (2,1) in ShiftedPrimedTableaux() + False + """ + try: + self.element_class(self,T) return True - return (ShiftedPrimedTableaux.__contains__(self, x)) + except ValueError: + return False class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): """ - Shifted Primed tableaux of a fixed shape. + Shifted Primed Tableaux of a fixed shape. """ Element = ShiftedPrimedTableau def __init__(self, shape, max_elt): - r""" - Initializes the class of semistandard tableaux of a fixed shape. + """ + Initializes the class of Shifted Primed Tableaux of a given shape. + If ``max_elt`` is specified, a finite set with entries smaller or equal to ``max_elt``. """ Parent.__init__(self, category=FiniteEnumeratedSets()) @@ -934,13 +1037,14 @@ def _repr_(self): TESTS:: - sage: ShiftedPrimedTableaux([3,2,1]) # indirect doctest + sage: ShiftedPrimedTableaux([3,2,1]) Shifted Primed tableaux of shape [3, 2, 1] """ if self._max_elt is None: return "Shifted Primed Tableaux of shape {}".format(self._shape) if self._max_elt is not None: - return "Shifted Primed Tableaux of shape {} and maximum element {}".format(self._shape, self._max_elt) + return ("Shifted Primed Tableaux of shape {} and maximum element {}" + .format(self._shape, self._max_elt)) def __contains__(self, T): """ @@ -950,27 +1054,26 @@ def __contains__(self, T): True """ - if isinstance(T, ShiftedPrimedTableau): - return [len(row) for row in T] == self._shape - - t = preprocessing(T) + try: + Tab = self.element_class(self, T) + except ValueError: + return False - if self._max_elt is not None: - return (ShiftedPrimedTableaux.__contains__(self, t) - and [len(row) for row in t] == self._shape - and max(flatten(t)) <= self._max_elt) - - return (ShiftedPrimedTableaux.__contains__(self, t) - and [len(row) for row in t] == self._shape) + if self._max_elt is None: + return (Tab.shape() == self._shape) + + return (Tab.shape() == self._shape + and Tab.max_element() <= self._max_elt) + def _element_constructor_(self, T): - r""" - Constructs an object from ``t`` as an element of ``self``, if + """ + Constructs an object from ``T`` as an element of ``self``, if possible. INPUT: - - ``t`` -- data which can be interpreted as a primed tableau + - ``T`` -- data which can be interpreted as a primed tableau OUTPUT: @@ -978,24 +1081,35 @@ def _element_constructor_(self, T): TESTS:: - sage: ShiftedPrimedTableaux([3])([[1,2,3]]).parent() is ShiftedPrimedTableaux([3]) - True - sage: ShiftedPrimedTableaux([3])([[1,2]]) + sage: tab= ShiftedPrimedTableaux([3])([1,1,1.5]); tab + [(1.0, 1.0, 1.5)] + sage: tab.parent() + Shifted Primed Tableaux of shape [3] + sage: ShiftedPrimedTableaux([3])([1,1]) Traceback (most recent call last): ... - ValueError: [[1, 2]] is not an element of Shifted Primed tableaux of shape [3] + ValueError: [1, 1] is not an element of Shifted Primed tableaux of shape [3] """ - t = preprocessing(T) - if not t in self: - raise ValueError("{} is not an element of {}".format(t, self)) - - return self.element_class(self, t) + + try: + Tab = self.element_class(self, T) + except ValueError: + raise ValueError ("{} is not an element of Shifted Primed Tableaux" + .format(T)) + + if self._max_elt is None: + if Tab.shape() == self._shape: + return Tab + else: + if Tab.shape() == self._shape and Tab.max_element() <= self._max_elt: + return Tab + raise ValueError ("{} is not an element of {}".format(T, self)) def shape(self): """ Return the shape of the shifted tableaux ``self``. - EXAMPLES:: + TEST:: sage: ShiftedPrimedTableaux([6,4,3,1]).shape() [6, 4, 3, 1] @@ -1003,6 +1117,28 @@ def shape(self): return self._shape def __iter__(self): + """ + Iterate over ``self``, if ``max_element`` is specified. + + EXAMPLES:: + + sage: Tabs = ShiftedPrimedTableaux([3,2], max_element=3) + sage: Tabs[:4] + [[(1.0, 1.0, 1.0), (2.0, 2.0)], + [(1.0, 1.0, 1.0), (2.0, 3.0)], + [(1.0, 1.0, 1.0), (2.0, 2.5)], + [(1.0, 1.0, 1.0), (3.0, 3.0)]] + sage: len(list(Tabs)) + 24 + + TEST:: + + sage: Tabs = ShiftedPrimedTableaux([3,2]) + sage: Tabs[:3] + Traceback (most recent call last): + ... + ValueError: set is infinite + """ if self._max_elt is None: raise ValueError("set is infinite") for weight in OrderedPartitions(sum(self._shape)+self._max_elt,k=self._max_elt): @@ -1011,6 +1147,15 @@ def __iter__(self): yield (tab) def list_decreasing_weight(self): + """ + List elements of ``self`` with weakly decreasing weight. + + EXAMPLE:: + + sage: Tabs = ShiftedPrimedTableaux([2,1]) + sage: Tabs.list_decreasing_weight() + [[(1.0, 1.0), (2.0,)], [(1.0, 2.0), (3.0,)], [(1.0, 1.5), (3.0,)]] + """ list_dw = [] if self._max_elt is None: max_element = sum(self._shape) @@ -1022,88 +1167,196 @@ def list_decreasing_weight(self): return list_dw def list_highest_weight(self): + """ + List elements of ``self`` that are highest weight elements in the crystal. + + EXAMPLE:: + + sage: Tabs = ShiftedPrimedTableaux([3,1]) + sage: Tabs.list_highest_weight() + [[(1.0, 1.0, 1.0), (2.0,)], + [(1.0, 1.0, 1.5), (2.0,)], + [(1.0, 1.0, 2.5), (2.0,)]] + """ return [tab for tab in self.list_decreasing_weight() if tab.is_highest_weight()] class ShiftedPrimedTableaux_weight(ShiftedPrimedTableaux): """ - Shifted Primed Tableaux of fixed weight + Shifted Primed Tableaux of fixed weight. """ Element = ShiftedPrimedTableau def __init__(self, weight): + """ + Initializes the class of Shifted Primed Tableaux of a given weight. + + TESTS:: + + sage: TestSuite( ShiftedPrimedTableaux((3,2,1)) ).run() + """ Parent.__init__(self, category=FiniteEnumeratedSets()) self._weight = weight def _repr_(self): + """ + Return a string representation of ``self``. + + TEST:: + + sage: ShiftedPrimedTableaux((3,2,1)) + Shifted Primed Tableaux of weight (3, 2, 1) + """ return "Shifted Primed Tableaux of weight {}".format(self._weight) def __contains__(self, T): - if isinstance(x, ShiftedPrimedTableau): - return T.weight() == self._weight - - t = preprocessing(T) + """ + TESTS:: - flat = [round(item) for sublist in t for item in sublist] - if flat == []: - max_ind = 0 - else: - max_ind = max(flat) - weight = tuple([flat.count(i+1) for i in range(max_ind)]) - return (ShiftedPrimedTableaux.__contains__(self, t) - and weight == self._weight) + sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux((1,4,1)) + True + """ + try: + Tab = self.element_class(self, T) + except ValueError: + return False + return Tab.weight()==self._weight def _element_constructor_(self, T): - t = preprocessing(T) - if not t in self: - raise ValueError("{} is not an element of {}".format(t, self)) - return self.element_class(self, t) + """ + Constructs an object from ``T`` as an element of ``self``, if + possible. + + INPUT: + + - ``T`` -- data which can be interpreted as a primed tableau + + OUTPUT: + + - the corresponding primed tableau object + + TESTS:: + + sage: tab= ShiftedPrimedTableaux((2,1))([1,1,1.5]); tab + [(1.0, 1.0, 1.5)] + sage: tab.parent() + Shifted Primed Tableaux of weight (2, 1) + """ + try: + Tab = self.element_class(self, T) + except ValueError: + raise ValueError ("{} is not an element of Shifted Primed Tableaux" + .format(T)) + if Tab.weight() == self._weight: + return Tab + raise ValueError ("{} is not an element of {}".format(T, self)) + def __iter__(self): - #for size in Partition(sorted(list(self._weight), key=int, reverse=True)).dominated_partitions(): + """ + Iterate over ``self``. + + EXAMPLES:: + + sage: Tabs = ShiftedPrimedTableaux((2,3)) + sage: Tabs[:4] + [[(1.0, 1.0, 2.0, 2.0, 2.0)], + [(1.0, 1.0, 1.5, 2.0, 2.0)], + [(1.0, 1.0, 2.0, 2.0), (2.0,)], + [(1.0, 1.0, 1.5, 2.0), (2.0,)]] + sage: len(list(Tabs)) + 5 + """ return (tab for shape_ in Partitions(sum(self._weight)) if all(shape_[i] > shape_[i+1] for i in range(len(shape_)-1)) - for tab in ShiftedPrimedTableaux(shape = shape_, weight = self._weight)) + for tab in + ShiftedPrimedTableaux(shape = shape_, weight = self._weight)) class ShiftedPrimedTableaux_weight_shape(ShiftedPrimedTableaux): + """ + Shifted Primed Tableaux of the fixed weight and shape. + """ Element = ShiftedPrimedTableau def __init__(self, weight, shape): + """ + Initializes the class of Shifted Primed Tableaux of the given weight and shape. + """ Parent.__init__(self, category=FiniteEnumeratedSets()) self._weight = weight self._shape = shape def _repr_(self): + """ + Return a string representation of ``self``. + + TESTS:: + + sage: ShiftedPrimedTableaux([3,2,1],(4,2)) + Shifted Primed Tableaux of weight (4, 2) and shape [3, 2, 1] + """ return ("Shifted Primed Tableaux of weight {} and shape {}" .format(self._weight,self._shape)) def __contains__(self, T): - if isinstance(T, ShiftedPrimedTableau): - return (T.weight() == self._weight and T.shape() == self._shape) + """ + TESTS:: - t = preprocessing(T) + sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux([4,2],(1,4,1)) + True + """ + try: + Tab = self.element_class(self, T) + except ValueError: + return False - flat = [round(item) for sublist in t for item in sublist] - if flat == []: - max_ind = 0 - else: - max_ind = int(max(flat)) - weight = tuple([flat.count(i+1) for i in range(max_ind)]) - return(ShiftedPrimedTableaux.__contains__(self, t) and weight == self._weight - and [len(row) for row in t] == self._shape) + return (Tab.weight() == self._weight + and Tab.shape() == self._shape) def _element_constructor_(self, T): - t = preprocessing(T) - if not t in ShiftedPrimedTableaux(): - raise ValueError("{!r} is not an element of Shifted Primed Tableaux" .format(t)) - if not t in self: - raise ValueError("{} is not an element of {}".format(t, self)) + """ + Constructs an object from ``T`` as an element of ``self``, if + possible. + + TESTS:: + + sage: tab= ShiftedPrimedTableaux([3],(2,1))([1,1,1.5]); tab + [(1.0, 1.0, 1.5)] + sage: tab.parent() + Shifted Primed Tableaux of weight (2, 1) and shape [3] + sage: ShiftedPrimedTableaux([3],(2,1))([1,1]) + Traceback (most recent call last): + ... + ValueError: [1, 1] is not an element of Shifted Primed tableaux of weight (2, 1) and shape [3] + """ + try: + Tab = self.element_class(self, T) + except ValueError: + raise ValueError ("{} is not an element of Shifted Primed Tableaux" + .format(T)) - return self.element_class(self, t) + if Tab.shape() == self._shape and Tab.weight() == self._weight: + return Tab + raise ValueError("{} is not an element of {}".format(T, self)) + def __iter__(self): + """ + Iterate over ``self``. + + EXAMPLES:: + + sage: Tabs = ShiftedPrimedTableaux([3,2], (1,2,2)) + sage: Tabs[:4] + [[(1.0, 2.0, 2.0), (3.0, 3.0)], + [(1.0, 1.5, 2.5), (2.0, 3.0)], + [(1.0, 1.5, 2.5), (2.0, 2.5)], + [(1.0, 1.5, 2.0), (3.0, 3.0)]] + sage: len(list(Tabs)) + 4 + """ if not self._shape.dominates(Partition(sorted(list(self._weight), key=int, reverse=True))): return yield @@ -1241,8 +1494,13 @@ def preprocessing(T): elif isinstance(T, np.ndarray): t = T.tolist() elif not isinstance(T,(list,tuple)): - raise ValueError ("input must be list, tuple or tableau") - elif all(isinstance(row, (list,tuple)) for row in T): + try: + return list(T) + except TypeError: + return T + else: + if not all(isinstance(row, (list,tuple)) for row in T): + T =[T] t = [] # Preprocessing list T for primes and other symbols for row in T: @@ -1268,29 +1526,6 @@ def preprocessing(T): except ValueError: raise ValueError("primed elements should be half-integers or have p or ' at the end") t.append(row_new) - else: - row_new=[] - for element in T: - try: - if int(element*2) == element*2: - row_new.append(float(element)) - continue - else: - raise ValueError("all numbers must be half-integers") - except (ValueError,TypeError): - pass - if isinstance(element,str): - if element[-1] == "'" and element[:-1].isdigit() == True: - row_new.append(float(float(element[:-1]) - .5)) - continue - if element[-1] == "p" and element[:-1].isdigit() == True: - row_new.append(float(float(element[:-1]) - .5)) - continue - try: - row_new.append(float(element)) - except ValueError: - raise ValueError("primed elements should be half-integers or have p or ' at the end") - t=[row_new] #Accounting for zeros at the beginning and at the end of a row From 5bc82dcf2b4659def502f089910b3ea09a234b0e Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sat, 24 Jun 2017 10:05:21 +0300 Subject: [PATCH 243/740] Fixed doctests --- src/sage/combinat/tableau_shifted_primed.py | 35 ++++++++++++--------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index 92f16feb41a..d7f9f9c12b0 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -1,7 +1,7 @@ from six import add_metaclass import numpy as np -from sage.combinat.partition import Partition, OrderedPartitions +from sage.combinat.partition import Partition, Partitions, OrderedPartitions from sage.combinat.integer_vector import IntegerVectors from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets @@ -38,11 +38,14 @@ class ShiftedPrimedTableau(ClonableArray): sage: t = ShiftedPrimedTableau([[1,"2p",2.5,3],[0,2,2.5]]) sage: t[1] (2.0, 2.5) + + TEST:: + sage: t = ShiftedPrimedTableau([[1,2,2.5,3],[0,2,2.5]]) Traceback (most recent call last): ... - ValueError: [(1.0, 2.0, 2.5, 3.0), (2.0, 2.5)] is not an element of Shifted Primed Tableaux - """ + ValueError: [[1, 2, 2.50000000000000, 3], [0, 2, 2.50000000000000]] is not an element of Shifted Primed Tableaux + """ @staticmethod def __classcall_private__(cls, T): @@ -80,11 +83,11 @@ def __init__(self,parent, T): sage: s==t True sage: t.parent() - Shifted primed tableaux of shape [4, 2] + Shifted Primed Tableaux of shape [4, 2] sage: s.parent() - Shifted Primed Tableaux of weight (1, 2, 3) and shape [4, 2] + Shifted Primed Tableaux sage: r = ShiftedPrimedTableaux([4, 2])(s); r.parent() - Shifted primed tableaux of shape [4, 2] + Shifted Primed Tableaux of shape [4, 2] sage: s is t # identical shifted tableaux are distinct objects False @@ -314,9 +317,13 @@ def max_element(self): sage: Tab = ShiftedPrimedTableau([(1,1,1.5,2.5),(2,2)]) sage: Tab.max_element() - 3.0 + 3 """ - return round(max(flatten(self))) + if self==[]: + return 0 + else: + flat = [item for sublist in self for item in sublist] + return int(round(max(flat))) def shape(self): @@ -386,7 +393,7 @@ def weight(self): if flat == []: max_ind = 0 else: - max_ind = max(flat) + max_ind = int(max(flat)) weight = tuple([flat.count(i+1) for i in range(max_ind)]) return tuple(weight) @@ -811,7 +818,7 @@ def __classcall_private__(cls, *args, **kwargs): sage: ShiftedPrimedTableaux([[1]]) Traceback (most recent call last): ... - ValueError: [[1]] is not a partition + ValueError: shape [[1]] is not a partition sage: ShiftedPrimedTableaux(weight=(2,2,2), max_element=2) Traceback (most recent call last): @@ -962,7 +969,7 @@ def _repr_(self): TESTS:: sage: ShiftedPrimedTableaux() - ShiftedPrimedTableaux + Shifted Primed Tableaux """ return "Shifted Primed Tableaux" @@ -1038,7 +1045,7 @@ def _repr_(self): TESTS:: sage: ShiftedPrimedTableaux([3,2,1]) - Shifted Primed tableaux of shape [3, 2, 1] + Shifted Primed Tableaux of shape [3, 2, 1] """ if self._max_elt is None: return "Shifted Primed Tableaux of shape {}".format(self._shape) @@ -1088,7 +1095,7 @@ def _element_constructor_(self, T): sage: ShiftedPrimedTableaux([3])([1,1]) Traceback (most recent call last): ... - ValueError: [1, 1] is not an element of Shifted Primed tableaux of shape [3] + ValueError: [1, 1] is not an element of Shifted Primed Tableaux of shape [3] """ try: @@ -1329,7 +1336,7 @@ def _element_constructor_(self, T): sage: ShiftedPrimedTableaux([3],(2,1))([1,1]) Traceback (most recent call last): ... - ValueError: [1, 1] is not an element of Shifted Primed tableaux of weight (2, 1) and shape [3] + ValueError: [1, 1] is not an element of Shifted Primed Tableaux of weight (2, 1) and shape [3] """ try: Tab = self.element_class(self, T) From 20e6110a26f0aa26a98d1bccc2da738b612f2df3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 15 Jul 2017 21:19:28 -0500 Subject: [PATCH 244/740] move to subdirectory for merging with sage tree --- LICENSE => mac_lane/LICENSE | 0 README.md => mac_lane/README.md | 0 TODO => mac_lane/TODO | 0 __init__.py => mac_lane/__init__.py | 0 augmented_valuation.py => mac_lane/augmented_valuation.py | 0 developing_valuation.py => mac_lane/developing_valuation.py | 0 .../function_field_valuation.py | 0 gauss_valuation.py => mac_lane/gauss_valuation.py | 0 inductive_valuation.py => mac_lane/inductive_valuation.py | 0 limit_valuation.py => mac_lane/limit_valuation.py | 0 mapped_valuation.py => mac_lane/mapped_valuation.py | 0 padic_valuation.py => mac_lane/padic_valuation.py | 0 scaled_valuation.py => mac_lane/scaled_valuation.py | 0 trivial_valuation.py => mac_lane/trivial_valuation.py | 0 valuation.py => mac_lane/valuation.py | 0 valuation_space.py => mac_lane/valuation_space.py | 0 value_group.py => mac_lane/value_group.py | 0 17 files changed, 0 insertions(+), 0 deletions(-) rename LICENSE => mac_lane/LICENSE (100%) rename README.md => mac_lane/README.md (100%) rename TODO => mac_lane/TODO (100%) rename __init__.py => mac_lane/__init__.py (100%) rename augmented_valuation.py => mac_lane/augmented_valuation.py (100%) rename developing_valuation.py => mac_lane/developing_valuation.py (100%) rename function_field_valuation.py => mac_lane/function_field_valuation.py (100%) rename gauss_valuation.py => mac_lane/gauss_valuation.py (100%) rename inductive_valuation.py => mac_lane/inductive_valuation.py (100%) rename limit_valuation.py => mac_lane/limit_valuation.py (100%) rename mapped_valuation.py => mac_lane/mapped_valuation.py (100%) rename padic_valuation.py => mac_lane/padic_valuation.py (100%) rename scaled_valuation.py => mac_lane/scaled_valuation.py (100%) rename trivial_valuation.py => mac_lane/trivial_valuation.py (100%) rename valuation.py => mac_lane/valuation.py (100%) rename valuation_space.py => mac_lane/valuation_space.py (100%) rename value_group.py => mac_lane/value_group.py (100%) diff --git a/LICENSE b/mac_lane/LICENSE similarity index 100% rename from LICENSE rename to mac_lane/LICENSE diff --git a/README.md b/mac_lane/README.md similarity index 100% rename from README.md rename to mac_lane/README.md diff --git a/TODO b/mac_lane/TODO similarity index 100% rename from TODO rename to mac_lane/TODO diff --git a/__init__.py b/mac_lane/__init__.py similarity index 100% rename from __init__.py rename to mac_lane/__init__.py diff --git a/augmented_valuation.py b/mac_lane/augmented_valuation.py similarity index 100% rename from augmented_valuation.py rename to mac_lane/augmented_valuation.py diff --git a/developing_valuation.py b/mac_lane/developing_valuation.py similarity index 100% rename from developing_valuation.py rename to mac_lane/developing_valuation.py diff --git a/function_field_valuation.py b/mac_lane/function_field_valuation.py similarity index 100% rename from function_field_valuation.py rename to mac_lane/function_field_valuation.py diff --git a/gauss_valuation.py b/mac_lane/gauss_valuation.py similarity index 100% rename from gauss_valuation.py rename to mac_lane/gauss_valuation.py diff --git a/inductive_valuation.py b/mac_lane/inductive_valuation.py similarity index 100% rename from inductive_valuation.py rename to mac_lane/inductive_valuation.py diff --git a/limit_valuation.py b/mac_lane/limit_valuation.py similarity index 100% rename from limit_valuation.py rename to mac_lane/limit_valuation.py diff --git a/mapped_valuation.py b/mac_lane/mapped_valuation.py similarity index 100% rename from mapped_valuation.py rename to mac_lane/mapped_valuation.py diff --git a/padic_valuation.py b/mac_lane/padic_valuation.py similarity index 100% rename from padic_valuation.py rename to mac_lane/padic_valuation.py diff --git a/scaled_valuation.py b/mac_lane/scaled_valuation.py similarity index 100% rename from scaled_valuation.py rename to mac_lane/scaled_valuation.py diff --git a/trivial_valuation.py b/mac_lane/trivial_valuation.py similarity index 100% rename from trivial_valuation.py rename to mac_lane/trivial_valuation.py diff --git a/valuation.py b/mac_lane/valuation.py similarity index 100% rename from valuation.py rename to mac_lane/valuation.py diff --git a/valuation_space.py b/mac_lane/valuation_space.py similarity index 100% rename from valuation_space.py rename to mac_lane/valuation_space.py diff --git a/value_group.py b/mac_lane/value_group.py similarity index 100% rename from value_group.py rename to mac_lane/value_group.py From ed206ec4806b8d2c62f8ceaa47bdf6c74f1833a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 15 Jul 2017 21:21:24 -0500 Subject: [PATCH 245/740] removing gitignore for merge with sage tree --- .gitignore | 89 ------------------------------------------------------ 1 file changed, 89 deletions(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 72364f99fe4..00000000000 --- a/.gitignore +++ /dev/null @@ -1,89 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*,cover -.hypothesis/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# IPython Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# dotenv -.env - -# virtualenv -venv/ -ENV/ - -# Spyder project settings -.spyderproject - -# Rope project settings -.ropeproject From 0767201f63191f227c5bba3db86c948e22b8da24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 15 Jul 2017 21:22:10 -0500 Subject: [PATCH 246/740] remove mac_lane LICENSE Sage has the same license already --- mac_lane/LICENSE | 339 ----------------------------------------------- 1 file changed, 339 deletions(-) delete mode 100644 mac_lane/LICENSE diff --git a/mac_lane/LICENSE b/mac_lane/LICENSE deleted file mode 100644 index 23cb790338e..00000000000 --- a/mac_lane/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - {description} - Copyright (C) {year} {fullname} - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - {signature of Ty Coon}, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. From bd1dcbf970652778411e2b3effe9f5ff7d15c4ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 15 Jul 2017 21:26:14 -0500 Subject: [PATCH 247/740] remove obsolete TODOs --- mac_lane/TODO | 1 - 1 file changed, 1 deletion(-) delete mode 100644 mac_lane/TODO diff --git a/mac_lane/TODO b/mac_lane/TODO deleted file mode 100644 index 89a149bc134..00000000000 --- a/mac_lane/TODO +++ /dev/null @@ -1 +0,0 @@ -* Use _coerce_map_via more often From 0eb3fffe1c4fef455a8a86f3449ccedbbbd749db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 15 Jul 2017 21:26:38 -0500 Subject: [PATCH 248/740] remove monkey patches --- mac_lane/__init__.py | 763 ------------------------------------------- 1 file changed, 763 deletions(-) delete mode 100644 mac_lane/__init__.py diff --git a/mac_lane/__init__.py b/mac_lane/__init__.py deleted file mode 100644 index 4527ba01205..00000000000 --- a/mac_lane/__init__.py +++ /dev/null @@ -1,763 +0,0 @@ -# -*- coding: utf-8 -*- -r""" -Monkey patches to make the MacLane code work in standalone mode, i.e., without -modifying the sage source code at build time. -""" -#***************************************************************************** -# Copyright (C) 2016 Julian Rüth -# -# Distributed under the terms of the GNU General Public License (GPL) -# as published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - -# Fix doctests so they work in standalone mode (when invoked with sage -t, they run within the mac_lane/ directory) -import sys, os -if hasattr(sys.modules['__main__'], 'DC') and 'standalone' in sys.modules['__main__'].DC.options.optional: - sys.path.append(os.getcwd()) - sys.path.append(os.path.dirname(os.getcwd())) - -import valuation_space -from valuation_space import DiscretePseudoValuationSpace -import trivial_valuation -from trivial_valuation import TrivialValuation, TrivialPseudoValuation -import padic_valuation -from padic_valuation import pAdicValuation -import gauss_valuation -from gauss_valuation import GaussValuation -import value_group -from value_group import DiscreteValuationCodomain, DiscreteValueGroup, DiscreteValueSemigroup -import function_field_valuation -from function_field_valuation import FunctionFieldValuation -import augmented_valuation -from augmented_valuation import AugmentedValuation -import scaled_valuation -from scaled_valuation import ScaledValuation - -# fix unpickling and type checks of classes (otherwise, the instances of the -# local file and the instances that come from the mac_lane import define -# different types) -from trivial_valuation import TrivialDiscreteValuation, TrivialDiscretePseudoValuation -from function_field_valuation import FunctionFieldValuation_base, DiscreteFunctionFieldValuation_base, RationalFunctionFieldValuation_base, InducedFunctionFieldValuation_base, ClassicalFunctionFieldValuation_base, FunctionFieldFromLimitValuation, InfiniteRationalFunctionFieldValuation, FiniteRationalFunctionFieldValuation, NonClassicalRationalFunctionFieldValuation, InfiniteRationalFunctionFieldValuation, FunctionFieldMappedValuation_base, FunctionFieldExtensionMappedValuation, RationalFunctionFieldMappedValuation -from limit_valuation import LimitValuation, MacLaneLimitValuation, LimitValuation_generic -from mapped_valuation import MappedValuation_base, FiniteExtensionFromLimitValuation, FiniteExtensionFromInfiniteValuation, MappedValuation_base -from augmented_valuation import FiniteAugmentedValuation, InfiniteAugmentedValuation -from gauss_valuation import GaussValuation_generic -from valuation import DiscretePseudoValuation, DiscreteValuation, InfiniteDiscretePseudoValuation -from padic_valuation import pAdicValuation_base, pAdicValuation_int, pAdicValuation_padic, pAdicFromLimitValuation -from developing_valuation import DevelopingValuation -from augmented_valuation import AugmentedValuation_base, FinalAugmentedValuation, NonFinalAugmentedValuation, FinalFiniteAugmentedValuation, NonFinalFiniteAugmentedValuation -from inductive_valuation import FiniteInductiveValuation, FinalInductiveValuation, InfiniteInductiveValuation, NonFinalInductiveValuation -from scaled_valuation import ScaledValuation_generic - -# ================= -# MONKEY PATCH SAGE -# ================= -import sage - -# Implement Qp/Zp.valuation -sage.rings.padics.padic_generic.pAdicGeneric.valuation = lambda self: pAdicValuation(self) - -# Fix contains check of rational fuction fields -def to_polynomial(self, x): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: K(x) in K._ring # indirect doctest - True - - """ - R = x.parent()._ring - K = x.parent().constant_base_field() - if x.denominator() in K: - return x.numerator()/K(x.denominator()) - raise ValueError("Only polynomials can be converted to the underlying polynomial ring") - -def to_constant(self, x): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: K(1) in QQ # indirect doctest - True - - """ - K = x.parent().constant_base_field() - if x.denominator() in K and x.numerator() in K: - return K(x.numerator()) / K(x.denominator()) - raise ValueError("only constants can be converted to the underlying constant field") - -sage.rings.function_field.function_field.RationalFunctionField._to_polynomial = to_polynomial -sage.rings.function_field.function_field.RationalFunctionField._to_constant = to_constant -if not hasattr(sage.rings.function_field.function_field.RationalFunctionField, "__old_init__"): - sage.rings.function_field.function_field.RationalFunctionField.__old_init__ = sage.rings.function_field.function_field.RationalFunctionField.__init__ -def __init__(self, *args, **kwargs): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: K(1/2) in QQ - True - - """ - self.__old_init__(*args, **kwargs) - from sage.categories.morphism import SetMorphism - self._ring.register_conversion(SetMorphism(self.Hom(self._ring), self._to_polynomial)) - try: - self.constant_base_field().register_conversion(SetMorphism(self.Hom(self.constant_base_field()), self._to_constant)) - except AssertionError: - # since #21872 there is already such a conversion - pass - -sage.rings.function_field.function_field.RationalFunctionField.__init__ = __init__ -del(__init__) -del(to_polynomial) - -# implement principal_part for newton polygons -r""" -TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: NP = sage.geometry.newton_polygon.NewtonPolygon([(0,1),(1,0),(2,1)]) - sage: NP.principal_part() - Infinite Newton polygon with 2 vertices: (0, 1), (1, 0) ending by an infinite line of slope 0 - -""" -import sage.geometry.newton_polygon -sage.geometry.newton_polygon.NewtonPolygon_element.principal_part = lambda self: sage.geometry.newton_polygon.NewtonPolygon(self.vertices(), last_slope=0) -sage.geometry.newton_polygon.NewtonPolygon_element.sides = lambda self: zip(self.vertices(), self.vertices()[1:]) - -# implement coercion of function fields that comes from coercion of their base fields - -# Frac(K[x]) injects into K(x) -class DefaultConvertMap_unique_patched2(sage.structure.coerce_maps.DefaultConvertMap_unique): - def is_injective(self): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: R. = QQ[] - sage: K. = FunctionField(QQ) - sage: R.fraction_field().is_subring(K) # indirect doctest - True - - """ - from sage.categories.fields import Fields - if self.domain() in Fields(): - return True - raise NotImplementedError - -def _coerce_map_from_(target, source): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: L. = FunctionField(GaussianIntegers().fraction_field()) - sage: L.has_coerce_map_from(K) - True - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^3 + 1) - sage: K. = FunctionField(GaussianIntegers().fraction_field()) - sage: R. = K[] - sage: M. = K.extension(y^3 + 1) - sage: M.has_coerce_map_from(L) # not tested, base morphism is not implemented - True - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(I^2 + 1) - sage: M. = FunctionField(GaussianIntegers().fraction_field()) - sage: M.has_coerce_map_from(L) # not tested, base_morphism is not implemented - True - - """ - from sage.categories.function_fields import FunctionFields - if source in FunctionFields(): - if source.base_field() is source: - if target.base_field() is target: - # source and target are rational function fields - if source.variable_name() == target.variable_name(): - # ... in the same variable - base_coercion = target.constant_field().coerce_map_from(source.constant_field()) - if base_coercion is not None: - return source.hom([target.gen()], base_morphism=base_coercion) - else: - # source is an extensions of rational function fields - base_coercion = target.coerce_map_from(source.base_field()) - if base_coercion is not None: - # the base field of source coerces into the base field of target - target_polynomial = source.polynomial().map_coefficients(base_coercion) - # try to find a root of the defining polynomial in target - if target_polynomial(target.gen()) == 0: - # The defining polynomial of source has a root in target, - # therefore there is a map. To be sure that it is - # canonical, we require a root of the defining polynomial - # of target to be a root of the defining polynomial of - # source (and that the variables are named equally): - if source.variable_name() == target.variable_name(): - return source.hom([target.gen()], base_morphism=base_coercion) - - roots = target_polynomial.roots() - for root, _ in roots: - if target_polynomial(root) == 0: - # The defining polynomial of source has a root in target, - # therefore there is a map. To be sure that it is - # canonical, we require the names of the roots to match - if source.variable_name() == repr(root): - return source.hom([root], base_morphism=base_coercion) - if source is target._ring: - return DefaultConvertMap_unique_patched2(source, target) - if source is target._ring.fraction_field(): - return DefaultConvertMap_unique_patched2(source, target) - -sage.rings.function_field.function_field.FunctionField._coerce_map_from_ = _coerce_map_from_ -del(_coerce_map_from_) - -# patch is_injective() for many morphisms -def patch_is_injective(method, patch_map): - r""" - Patch ``method`` to return ``patch_map[type]`` if it returned a result of - ``type``. - - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: QQ.coerce_map_from(ZZ).is_injective() # indirect doctest - True - - """ - def patched(*args, **kwargs): - ret = method(*args, **kwargs) - if type(ret) in patch_map: - ret = patch_map[type(ret)](ret) - return ret - return patched - -# a ring homomorphism from a field into a ring is injective (as it respects inverses) -class RingHomomorphism_coercion_patched(sage.rings.morphism.RingHomomorphism_coercion): - def is_injective(self): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: QQ.coerce_map_from(ZZ).is_injective() # indirect doctest - True - - sage: Hom(ZZ,QQ['x']).natural_map().is_injective() - True - - sage: R. = ZZ[] - sage: R. = R.quo(x^2+x+1) - sage: Hom(ZZ,R).natural_map().is_injective() - True - - sage: R. = QQbar[] - sage: R.coerce_map_from(QQbar).is_injective() - True - - """ - from sage.categories.all import Fields, IntegralDomains - from sage.rings.number_field.order import AbsoluteOrder - from sage.rings.polynomial.polynomial_ring import is_PolynomialRing - # this should be implemented as far down as possible - if self.domain() in Fields(): return True - if self.domain() == sage.all.ZZ and self.codomain().characteristic() == 0: return True - if isinstance(self.domain(), AbsoluteOrder) and self(self.domain().gen()) != 0 and self.codomain() in IntegralDomains(): return True - # this should be implemented somewhere else - if is_PolynomialRing(self.codomain()) and self.codomain().base_ring() is self.domain(): - return True - coercion = self.codomain().coerce_map_from(self.domain()) - if coercion is not None: - try: - return coercion.is_injective() - except NotImplementedError: - # PolynomialBaseringInjection does not implement is_surjective/is_injective - if isinstance(coercion, sage.categories.map.FormalCompositeMap): - if all([f.is_injective() for f in list(coercion)]): - return True - except AttributeError: # DefaultConvertMap_unique does not implement is_injective/surjective at all - pass - - raise NotImplementedError - - def is_surjective(self): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: QQ.coerce_map_from(ZZ).is_surjective() # indirect doctest - False - - """ - from sage.categories.fields import Fields - if self.domain() in Fields(): return True - coercion = self.codomain().coerce_map_from(self.domain()) - if coercion is not None: - try: - return coercion.is_surjective() - except AttributeError: # DefaultConvertMap_unique does not implement is_injective/surjective at all - # PolynomialBaseringInjection does not implement is_surjective/is_injective (TODO: fix the logic of FormalCompositeMap, i.e., postpone without_bij) - if isinstance(coercion, sage.categories.map.FormalCompositeMap): - if all([f.is_surjective() for f in list(coercion)]): - return True - pass - - raise NotImplementedError - -sage.rings.homset.RingHomset_generic.natural_map = patch_is_injective(sage.rings.homset.RingHomset_generic.natural_map, {sage.rings.morphism.RingHomomorphism_coercion: (lambda coercion: RingHomomorphism_coercion_patched(coercion.parent()))}) - -# a morphism of polynomial rings which is induced by a ring morphism on the base is injective if the morphis on the base is -class PolynomialRingHomomorphism_from_base_patched(sage.rings.polynomial.polynomial_ring_homomorphism.PolynomialRingHomomorphism_from_base): - def is_injective(self): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: QQ['x'].coerce_map_from(ZZ['x']).is_injective() # indirect doctest - True - - This should be fixed in - `sage.rings.padics.qadic_flint_CA.pAdicCoercion_CA_frac_field` - instead:: - - sage: R. = ZqCA(9) - sage: R['x'].is_subring(R.fraction_field()['x']) - True - - """ - if self.underlying_map().codomain() is self.underlying_map().domain().fraction_field(): - # fix this in pAdicCoercion_CA_frac_field and similar - return True - return self.underlying_map().is_injective() -sage.rings.polynomial.polynomial_ring.PolynomialRing_general._coerce_map_from_ = patch_is_injective(sage.rings.polynomial.polynomial_ring.PolynomialRing_general._coerce_map_from_, {sage.rings.polynomial.polynomial_ring_homomorphism.PolynomialRingHomomorphism_from_base: (lambda morphism: PolynomialRingHomomorphism_from_base_patched(morphism.parent(), morphism.underlying_map()))}) - -# morphisms of number fields are injective -class Q_to_quadratic_field_element_patched(sage.rings.number_field.number_field_element_quadratic.Q_to_quadratic_field_element): - def is_injective(self): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: GaussianIntegers().fraction_field().coerce_map_from(QQ).is_injective() - True - - """ - return True -class Z_to_quadratic_field_element_patched(sage.rings.number_field.number_field_element_quadratic.Z_to_quadratic_field_element): - def is_injective(self): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: GaussianIntegers().fraction_field().coerce_map_from(ZZ).is_injective() - True - - """ - return True -sage.rings.number_field.number_field.NumberField_quadratic._coerce_map_from_ = patch_is_injective(sage.rings.number_field.number_field.NumberField_quadratic._coerce_map_from_, {sage.rings.number_field.number_field_element_quadratic.Q_to_quadratic_field_element: (lambda morphism: Q_to_quadratic_field_element_patched(morphism.codomain())), sage.rings.number_field.number_field_element_quadratic.Z_to_quadratic_field_element: (lambda morphism: Z_to_quadratic_field_element_patched(morphism.codomain()))}) - -# the integers embed into the rationals -class Z_to_Q_patched(sage.rings.rational.Z_to_Q): - def is_injective(self): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: QQ.coerce_map_from(ZZ).is_injective() - True - - """ - return True - def is_surjective(self): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: QQ.coerce_map_from(ZZ).is_surjective() - False - - """ - return False -from sage.rings.all import QQ -QQ.coerce_map_from = patch_is_injective(QQ.coerce_map_from, {sage.rings.rational.Z_to_Q: (lambda morphism: Z_to_Q_patched())}) - -# the integers embed into their extensions in number fields -class DefaultConvertMap_unique_patched(sage.structure.coerce_maps.DefaultConvertMap_unique): - def is_injective(self): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: CyclotomicField(5).maximal_order().coerce_map_from(ZZ).is_injective() - True - - """ - from sage.rings.all import ZZ - if self.domain() is ZZ or domain is int or domain is long: - return True - return super(DefaultConvertMap_unique, self).is_injective() - -def _coerce_map_from_patched(self, domain): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: CyclotomicField(5).maximal_order().coerce_map_from(ZZ).is_injective() # indirect doctest - True - - """ - from sage.rings.all import ZZ - if domain is ZZ or domain is int or domain is long: - return DefaultConvertMap_unique_patched(domain, self) - return False - -sage.rings.number_field.order.Order._coerce_map_from_ = _coerce_map_from_patched -del(_coerce_map_from_patched) - -# quotient rings embed if their underlying rings do -class DefaultConvertMap_unique_patched3(sage.structure.coerce_maps.DefaultConvertMap_unique): - def is_injective(self): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: R. = ZZ[] - sage: S. = QQ[] - sage: S.quo(x^2 + 1).coerce_map_from(R.quo(x^2 + 1)).is_injective() - True - - """ - if self.codomain().base().coerce_map_from(self.domain().base()).is_injective(): - return True - raise NotImplementedError - -sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRing_generic._coerce_map_from_original = sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRing_generic._coerce_map_from_ -def _coerce_map_from_patched(self, domain): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: R. = ZZ[] - sage: S. = QQ[] - sage: S.quo(x^2 + 1).coerce_map_from(R.quo(x^2 + 1)).is_injective() # indirect doctest - True - - """ - from sage.rings.polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing - if is_PolynomialQuotientRing(domain) and domain.modulus() == self.modulus(): - if self.base().has_coerce_map_from(domain.base()): - return DefaultConvertMap_unique_patched3(domain, self) - from sage.rings.fraction_field import is_FractionField - if is_FractionField(domain): - # this should be implemented on a much higher level: - # if there is a morphism R -> K then there is a morphism Frac(R) -> K - if self.has_coerce_map_from(domain.base()): - return True - return self._coerce_map_from_original(domain) -sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRing_generic._coerce_map_from_ = _coerce_map_from_patched -del(_coerce_map_from_patched) - -# a ring embeds into its field of fractions -class CallableConvertMap_patched(sage.rings.fraction_field.CallableConvertMap): - def is_injective(self): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: R. = QQ[] - sage: R.is_subring(R.fraction_field()) # indirect doctest - True - - """ - return True - - def is_surjective(self): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: R. = QQ[] - sage: R.fraction_field().coerce_map_from(R).is_surjective() - False - - """ - return False - -sage.rings.fraction_field.CallableConvertMap = CallableConvertMap_patched - -# inverses of quotient ring elements -def inverse_of_unit(self): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: R. = ZZ[] - sage: S = R.quo(x^2+x+1) - sage: S(1).inverse_of_unit() - 1 - - """ - inverse = ~self - if inverse.parent() is self.parent(): - return inverse - raise NotImplementedError - -sage.rings.polynomial.polynomial_quotient_ring_element.PolynomialQuotientRingElement.inverse_of_unit = inverse_of_unit -del(inverse_of_unit) - -# factorization in polynomial quotient fields -def _factor_univariate_polynomial(self, f): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: K = GF(2) - sage: R. = K[] - sage: L. = K.extension(x^2 + x + 1) - sage: R. = L[] - sage: L. = L.extension(y^2 + y + x) - sage: R. = L[] - sage: (T^2 + T + x).factor() # indirect doctest - (T + y) * (T + y + 1) - - """ - from sage.structure.factorization import Factorization - - if f.is_zero(): - raise ValueError("factorization of 0 is not defined") - elif f.degree() <= 1: - return Factorization([(f,1)]) - - from_absolute_field, to_absolute_field, absolute_field = self.absolute_extension() - - F = f.map_coefficients(lambda c:to_absolute_field(c), absolute_field).factor() - return Factorization([(g.map_coefficients(lambda c:from_absolute_field(c), self), e) for g,e in F], unit=from_absolute_field(F.unit())) -sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRing_generic._factor_univariate_polynomial = _factor_univariate_polynomial -del(_factor_univariate_polynomial) - -# factorization needs to go to the absolute field and back -from sage.misc.cachefunc import cached_method -@cached_method -def absolute_extension(self): - """ - Return a ring isomorphic to this ring which is not a - :class:`PolynomialQuotientRing` but of a type which offers more - functionality. - - INUPT: - - - ``name`` -- a list of strings or ``None`` (default: ``None``), the - name of the generator of the absolute extension. If ``None``, this - will be the same as the name of the generator of this ring. - - EXAMPLES:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: k. = GF(4) - sage: R. = k[] - sage: l. = k.extension(b^2+b+a); l - Univariate Quotient Polynomial Ring in b over Finite Field in a of size 2^2 with modulus b^2 + b + a - sage: from_ll,to_ll, ll = l.absolute_extension(); ll - Finite Field in v4 of size 2^4 - sage: all([to_ll(from_ll(ll.gen()**i)) == ll.gen()**i for i in range(ll.degree())]) - True - - sage: R. = l[] - sage: m. = l.extension(c^2+b*c+b); m - Univariate Quotient Polynomial Ring in c over Univariate Quotient Polynomial Ring in b over Finite Field in a of size 2^2 with modulus b^2 + b + a with modulus c^2 + b*c + b - sage: from_mm, to_mm, mm = m.absolute_extension(); mm - Finite Field in v8 of size 2^8 - sage: all([to_mm(from_mm(mm.gen()**i)) == mm.gen()**i for i in range(mm.degree())]) - True - - """ - from sage.rings.polynomial.polynomial_quotient_ring import PolynomialQuotientRing_generic - if not self.is_field(): - raise NotImplementedError("absolute_extension() only implemented for fields") - - if self.is_finite(): - if self.base_ring().is_prime_field(): - if self.modulus().degree() == 1: - ret = self.base_ring() - from sage.categories.homset import Hom - from sage.categories.morphism import SetMorphism - to_ret = SetMorphism(Hom(self, ret), lambda x: x.lift()[0]) - from_ret = self.coerce_map_from(ret) - return from_ret, to_ret, ret - else: - raise NotImplementedError - - if isinstance(self.base_ring(), PolynomialQuotientRing_generic): - abs_base_to_base, base_to_abs_base, abs_base = self.base_ring().absolute_extension() - modulus_over_abs_base = self.modulus().map_coefficients(lambda c:base_to_abs_base(c), abs_base) - new_self = modulus_over_abs_base.parent().quo(modulus_over_abs_base) - ret_to_new_self, new_self_to_ret, ret = new_self.absolute_extension() - from_ret = ret.hom([ret_to_new_self(ret.gen()).lift().map_coefficients(abs_base_to_base, self.base_ring())(self.gen())], check=False) - to_ret = lambda x: x.lift().map_coefficients(lambda c: new_self_to_ret(base_to_abs_base(c)), ret)(new_self_to_ret(new_self.gen())) - from sage.categories.homset import Hom - from sage.categories.morphism import SetMorphism - to_ret = SetMorphism(Hom(self, ret), to_ret) - return from_ret, to_ret, ret - else: - N = self.cardinality() - from sage.rings.all import GF - ret = GF(N,prefix='v') - base_to_ret = self.base_ring().hom([self.base_ring().modulus().change_ring(ret).roots()[0][0]]) - im_gen = self.modulus().map_coefficients(lambda c:base_to_ret(c), ret).roots()[0][0] - to_ret = lambda x: x.lift().map_coefficients(base_to_ret, ret)(im_gen) - from sage.categories.homset import Hom - from sage.categories.morphism import SetMorphism - to_ret = SetMorphism(Hom(self, ret), to_ret) - - basis = [self.gen()**i*self.base_ring().gen()**j for i in range(self.degree()) for j in range(self.base_ring().degree())] - assert len(basis) == ret.degree() - basis_in_ret = [to_ret(b)._vector_() for b in basis] - from sage.matrix.constructor import matrix - A = matrix(basis_in_ret) - assert A.is_square() - x = A.solve_left(A.column_space().basis()[1]) - from_ret = ret.hom([sum(c*b for c,b in zip(x.list(),basis))], check=False) - return from_ret, to_ret, ret - else: - raise NotImplementedError -sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRing_generic.absolute_extension = absolute_extension -del(absolute_extension) - -# factorization needs some linear algebra (it seems) -def vector_space(self): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: K = GF(2) - sage: R. = K[] - sage: L. = K.extension(x^2 + x + 1) - sage: R. = L[] - sage: L. = L.extension(y^2 + y + x) - sage: L.vector_space() - Vector space of dimension 2 over Finite Field in x of size 2^2 - - """ - if not self.base().base_ring().is_field(): - raise ValueError - - return self.base().base_ring()**self.modulus().degree() -sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRing_generic.vector_space = vector_space -del(vector_space) - -# make some_elements() non-trivial for number fields -def some_elements(self): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: K = GaussianIntegers().fraction_field() - sage: list(K.some_elements()) - [I, 0, 1, 1/2, 2*I, -I, -2, 0, 0] - - """ - for element in self.polynomial_ring().some_elements(): - yield element(self.gen()) -sage.rings.number_field.number_field.NumberField_generic.some_elements = some_elements -del(some_elements) - -# make some_elements() deterministic for function fields -def some_elements(self): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: list(K.some_elements()) == list(K.some_elements()) - True - - """ - for num in self._ring.some_elements(): - for den in self._ring.some_elements(): - if den != 0: - yield self(num) / self(den) -sage.rings.function_field.function_field.RationalFunctionField.some_elements = some_elements -del(some_elements) - -def some_elements(self): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: list(L.some_elements()) == list(L.some_elements()) - True - - """ - for element in self._ring.some_elements(): - yield self(element) -sage.rings.function_field.function_field.FunctionField_polymod.some_elements = some_elements -del(some_elements) - -# make some_elements() non-trivial for fraction fields -def some_elements(self): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: R. = QQ[] - sage: K = R.fraction_field() - sage: len(list(K.some_elements())) - 72 - - """ - for num in self.ring().some_elements(): - for den in self.ring().some_elements(): - if den != 0: - yield self(num) / self(den) -sage.rings.fraction_field.FractionField_generic.some_elements = some_elements - -# make some_elements() non-trivial for orders in number fields -def some_elements(self): - r""" - TESTS:: - - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: R = GaussianIntegers() - sage: list(R.some_elements()) - [I, 0, 1, 2*I, -I, -2, 0, 0] - - """ - for element in self.fraction_field().some_elements(): - if element in self: - yield self(element) -sage.rings.number_field.order.Order.some_elements = some_elements -del(some_elements) - -# register modules at some standard places so imports work as exepcted -r""" -sage: from sage.rings.valuation.gauss_valuation import GaussValuation -""" -import imp, sys -sage.rings.valuation = sys.modules['sage.rings.valuation'] = imp.new_module('sage.rings.valuation') -sage.rings.valuation.gauss_valuation = sys.modules['sage.rings.valuation.gauss_valuation'] = gauss_valuation -sage.rings.valuation.valuation = sys.modules['sage.rings.valuation.valuation'] = valuation -sage.rings.valuation.valuation_space = sys.modules['sage.rings.valuation.valuation_space'] = valuation_space -sage.rings.valuation.augmented_valuation = sys.modules['sage.rings.valuation.augmented_valuation'] = augmented_valuation -sage.rings.function_field.function_field_valuation = sys.modules['sage.rings.function_field.function_field_valuation'] = function_field_valuation - -# fix unpickling of factories -from sage.structure.factory import register_factory_unpickle -register_factory_unpickle("pAdicValuation", pAdicValuation) -register_factory_unpickle("GaussValuation", GaussValuation) -register_factory_unpickle("TrivialValuation", TrivialValuation) -register_factory_unpickle("TrivialPseudoValuation", TrivialPseudoValuation) -register_factory_unpickle("FunctionFieldValuation", FunctionFieldValuation) -register_factory_unpickle("AugmentedValuation", AugmentedValuation) -register_factory_unpickle("LimitValuation", LimitValuation) -register_factory_unpickle("ScaledValuation", ScaledValuation) From 6c3a301cfa35807f210dc22f265de5be18ac3c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 15 Jul 2017 21:27:54 -0500 Subject: [PATCH 249/740] move valuation code to valuation/ --- {mac_lane => src/sage/rings/valuation}/README.md | 0 {mac_lane => src/sage/rings/valuation}/augmented_valuation.py | 0 {mac_lane => src/sage/rings/valuation}/developing_valuation.py | 0 .../sage/rings/valuation}/function_field_valuation.py | 0 {mac_lane => src/sage/rings/valuation}/gauss_valuation.py | 0 {mac_lane => src/sage/rings/valuation}/inductive_valuation.py | 0 {mac_lane => src/sage/rings/valuation}/limit_valuation.py | 0 {mac_lane => src/sage/rings/valuation}/mapped_valuation.py | 0 {mac_lane => src/sage/rings/valuation}/padic_valuation.py | 0 {mac_lane => src/sage/rings/valuation}/scaled_valuation.py | 0 {mac_lane => src/sage/rings/valuation}/trivial_valuation.py | 0 {mac_lane => src/sage/rings/valuation}/valuation.py | 0 {mac_lane => src/sage/rings/valuation}/valuation_space.py | 0 {mac_lane => src/sage/rings/valuation}/value_group.py | 0 14 files changed, 0 insertions(+), 0 deletions(-) rename {mac_lane => src/sage/rings/valuation}/README.md (100%) rename {mac_lane => src/sage/rings/valuation}/augmented_valuation.py (100%) rename {mac_lane => src/sage/rings/valuation}/developing_valuation.py (100%) rename {mac_lane => src/sage/rings/valuation}/function_field_valuation.py (100%) rename {mac_lane => src/sage/rings/valuation}/gauss_valuation.py (100%) rename {mac_lane => src/sage/rings/valuation}/inductive_valuation.py (100%) rename {mac_lane => src/sage/rings/valuation}/limit_valuation.py (100%) rename {mac_lane => src/sage/rings/valuation}/mapped_valuation.py (100%) rename {mac_lane => src/sage/rings/valuation}/padic_valuation.py (100%) rename {mac_lane => src/sage/rings/valuation}/scaled_valuation.py (100%) rename {mac_lane => src/sage/rings/valuation}/trivial_valuation.py (100%) rename {mac_lane => src/sage/rings/valuation}/valuation.py (100%) rename {mac_lane => src/sage/rings/valuation}/valuation_space.py (100%) rename {mac_lane => src/sage/rings/valuation}/value_group.py (100%) diff --git a/mac_lane/README.md b/src/sage/rings/valuation/README.md similarity index 100% rename from mac_lane/README.md rename to src/sage/rings/valuation/README.md diff --git a/mac_lane/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py similarity index 100% rename from mac_lane/augmented_valuation.py rename to src/sage/rings/valuation/augmented_valuation.py diff --git a/mac_lane/developing_valuation.py b/src/sage/rings/valuation/developing_valuation.py similarity index 100% rename from mac_lane/developing_valuation.py rename to src/sage/rings/valuation/developing_valuation.py diff --git a/mac_lane/function_field_valuation.py b/src/sage/rings/valuation/function_field_valuation.py similarity index 100% rename from mac_lane/function_field_valuation.py rename to src/sage/rings/valuation/function_field_valuation.py diff --git a/mac_lane/gauss_valuation.py b/src/sage/rings/valuation/gauss_valuation.py similarity index 100% rename from mac_lane/gauss_valuation.py rename to src/sage/rings/valuation/gauss_valuation.py diff --git a/mac_lane/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py similarity index 100% rename from mac_lane/inductive_valuation.py rename to src/sage/rings/valuation/inductive_valuation.py diff --git a/mac_lane/limit_valuation.py b/src/sage/rings/valuation/limit_valuation.py similarity index 100% rename from mac_lane/limit_valuation.py rename to src/sage/rings/valuation/limit_valuation.py diff --git a/mac_lane/mapped_valuation.py b/src/sage/rings/valuation/mapped_valuation.py similarity index 100% rename from mac_lane/mapped_valuation.py rename to src/sage/rings/valuation/mapped_valuation.py diff --git a/mac_lane/padic_valuation.py b/src/sage/rings/valuation/padic_valuation.py similarity index 100% rename from mac_lane/padic_valuation.py rename to src/sage/rings/valuation/padic_valuation.py diff --git a/mac_lane/scaled_valuation.py b/src/sage/rings/valuation/scaled_valuation.py similarity index 100% rename from mac_lane/scaled_valuation.py rename to src/sage/rings/valuation/scaled_valuation.py diff --git a/mac_lane/trivial_valuation.py b/src/sage/rings/valuation/trivial_valuation.py similarity index 100% rename from mac_lane/trivial_valuation.py rename to src/sage/rings/valuation/trivial_valuation.py diff --git a/mac_lane/valuation.py b/src/sage/rings/valuation/valuation.py similarity index 100% rename from mac_lane/valuation.py rename to src/sage/rings/valuation/valuation.py diff --git a/mac_lane/valuation_space.py b/src/sage/rings/valuation/valuation_space.py similarity index 100% rename from mac_lane/valuation_space.py rename to src/sage/rings/valuation/valuation_space.py diff --git a/mac_lane/value_group.py b/src/sage/rings/valuation/value_group.py similarity index 100% rename from mac_lane/value_group.py rename to src/sage/rings/valuation/value_group.py From 0f615c771cac39a2cce4d54b4fd190f1c84992de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 15 Jul 2017 21:28:46 -0500 Subject: [PATCH 250/740] remove specific valuation code out of valuation/ --- .../{valuation => function_field}/function_field_valuation.py | 0 src/sage/rings/{valuation => padics}/padic_valuation.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/sage/rings/{valuation => function_field}/function_field_valuation.py (100%) rename src/sage/rings/{valuation => padics}/padic_valuation.py (100%) diff --git a/src/sage/rings/valuation/function_field_valuation.py b/src/sage/rings/function_field/function_field_valuation.py similarity index 100% rename from src/sage/rings/valuation/function_field_valuation.py rename to src/sage/rings/function_field/function_field_valuation.py diff --git a/src/sage/rings/valuation/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py similarity index 100% rename from src/sage/rings/valuation/padic_valuation.py rename to src/sage/rings/padics/padic_valuation.py From 8afc3611251682ca726195739c264812c334ac03 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Wed, 19 Jul 2017 01:34:17 +0000 Subject: [PATCH 251/740] remove standalone import commands --- .../function_field_valuation.py | 47 -------------- src/sage/rings/padics/padic_valuation.py | 46 -------------- .../rings/valuation/augmented_valuation.py | 62 +----------------- .../rings/valuation/developing_valuation.py | 11 ---- src/sage/rings/valuation/gauss_valuation.py | 34 +--------- .../rings/valuation/inductive_valuation.py | 40 ------------ src/sage/rings/valuation/limit_valuation.py | 31 --------- src/sage/rings/valuation/mapped_valuation.py | 26 -------- src/sage/rings/valuation/scaled_valuation.py | 19 ------ src/sage/rings/valuation/trivial_valuation.py | 29 --------- src/sage/rings/valuation/valuation.py | 22 ------- src/sage/rings/valuation/valuation_space.py | 63 ------------------- src/sage/rings/valuation/value_group.py | 30 --------- 13 files changed, 2 insertions(+), 458 deletions(-) diff --git a/src/sage/rings/function_field/function_field_valuation.py b/src/sage/rings/function_field/function_field_valuation.py index 1d3dc14ccf9..859d0c44308 100644 --- a/src/sage/rings/function_field/function_field_valuation.py +++ b/src/sage/rings/function_field/function_field_valuation.py @@ -7,7 +7,6 @@ We can create classical valuations that correspond to finite and infinite places on a rational function field:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, 1); v (x - 1)-adic valuation @@ -118,7 +117,6 @@ Run test suite for a valuation which sends an element to `-\infty`:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(QQ['x'], pAdicValuation(QQ, 2)).augmentation(x, infinity) sage: K. = FunctionField(QQ) @@ -160,7 +158,6 @@ class FunctionFieldValuationFactory(UniqueFactory): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) We create a valuation that correspond to a finite rational place of a function @@ -269,7 +266,6 @@ def create_key_and_extra_args(self, domain, prime): We specify a valuation on a function field by two different means and get the same object:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x - 1) # indirect doctest @@ -330,7 +326,6 @@ def create_key_and_extra_args_from_place(self, domain, generator): TESTS: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, 1/x) # indirect doctest @@ -373,7 +368,6 @@ def create_key_and_extra_args_from_valuation(self, domain, valuation): TESTS: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = QQ[] sage: w = GaussValuation(R, TrivialValuation(QQ)).augmentation(x - 1, 1) @@ -418,7 +412,6 @@ def create_key_and_extra_args_from_valuation_on_isomorphic_field(self, domain, v TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) @@ -470,7 +463,6 @@ def create_object(self, version, key, **extra_args): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = QQ[] sage: w = GaussValuation(R, pAdicValuation(QQ, 2)) @@ -533,7 +525,6 @@ class FunctionFieldValuation_base(DiscretePseudoValuation): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, FunctionFieldValuation_base) @@ -548,7 +539,6 @@ class DiscreteFunctionFieldValuation_base(DiscreteValuation): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, DiscreteFunctionFieldValuation_base) @@ -561,7 +551,6 @@ def extensions(self, L): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x) sage: R. = K[] @@ -635,7 +624,6 @@ class RationalFunctionFieldValuation_base(FunctionFieldValuation_base): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, RationalFunctionFieldValuation_base) @@ -651,7 +639,6 @@ class ClassicalFunctionFieldValuation_base(DiscreteFunctionFieldValuation_base): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(5)) sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, ClassicalFunctionFieldValuation_base) @@ -665,7 +652,6 @@ def _test_classical_residue_field(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x^2 + 1) sage: v._test_classical_residue_field() @@ -681,7 +667,6 @@ def _ge_(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x^2 + 1) sage: w = FunctionFieldValuation(K, x) @@ -705,7 +690,6 @@ class InducedFunctionFieldValuation_base(FunctionFieldValuation_base): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x^2 + 1) # indirect doctest @@ -714,7 +698,6 @@ def __init__(self, parent, base_valuation): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: isinstance(v, InducedFunctionFieldValuation_base) @@ -735,7 +718,6 @@ def uniformizer(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: FunctionFieldValuation(K, x).uniformizer() x @@ -750,7 +732,6 @@ def lift(self, F): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x) sage: v.lift(0) @@ -777,7 +758,6 @@ def value_group(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: FunctionFieldValuation(K, x).value_group() Additive Abelian Group generated by 1 @@ -791,7 +771,6 @@ def reduce(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x^2 + 1) sage: v.reduce(x) @@ -824,7 +803,6 @@ def _repr_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: FunctionFieldValuation(K, x^2 + 1) # indirect doctest (x^2 + 1)-adic valuation @@ -848,7 +826,6 @@ def extensions(self, L): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x^2 + 1) sage: L. = FunctionField(GaussianIntegers().fraction_field()) @@ -883,7 +860,6 @@ def _call_(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x) # indirect doctest sage: v((x+1)/x^2) @@ -898,7 +874,6 @@ def residue_ring(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: FunctionFieldValuation(K, x).residue_ring() Rational Field @@ -925,7 +900,6 @@ class FiniteRationalFunctionFieldValuation(InducedFunctionFieldValuation_base, C EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x + 1); v # indirect doctest (x + 1)-adic valuation @@ -952,7 +926,6 @@ def __init__(self, parent, base_valuation): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, x + 1) sage: isinstance(v, FiniteRationalFunctionFieldValuation) @@ -971,7 +944,6 @@ class NonClassicalRationalFunctionFieldValuation(InducedFunctionFieldValuation_b EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = GaussValuation(QQ['x'], pAdicValuation(QQ, 2)) sage: w = FunctionFieldValuation(K, v); w # indirect doctest @@ -986,7 +958,6 @@ def __init__(self, parent, base_valuation): function fields in the code. However, since these valuations must send elments to `-\infty`, they are not supported yet:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(QQ['x'], pAdicValuation(QQ, 2)).augmentation(x, infinity) sage: K. = FunctionField(QQ) @@ -1006,7 +977,6 @@ class FunctionFieldFromLimitValuation(FiniteExtensionFromLimitValuation, Discret EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x^2 + x + 1)) @@ -1024,7 +994,6 @@ def __init__(self, parent, approximant, G, approximants): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x^2 + x + 1)) @@ -1043,7 +1012,6 @@ def _to_base_domain(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x^2 + x + 1)) @@ -1061,7 +1029,6 @@ def scale(self, scalar): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x^2 + x + 1)) @@ -1083,7 +1050,6 @@ class FunctionFieldMappedValuation_base(FunctionFieldValuation_base, MappedValua EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: v = FunctionFieldValuation(K, 1/x); v Valuation at the infinite place @@ -1093,7 +1059,6 @@ def __init__(self, parent, base_valuation, to_base_valuation_domain, from_base_v r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: v = FunctionFieldValuation(K, 1/x) sage: isinstance(v, FunctionFieldMappedValuation_base) @@ -1112,7 +1077,6 @@ def _to_base_domain(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) @@ -1130,7 +1094,6 @@ def _from_base_domain(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) @@ -1148,7 +1111,6 @@ def scale(self, scalar): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) @@ -1169,7 +1131,6 @@ def _repr_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) @@ -1191,7 +1152,6 @@ class RationalFunctionFieldMappedValuation(FunctionFieldMappedValuation_base, Ra EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * sage: K. = FunctionField(QQ) sage: R. = QQ[] sage: w = GaussValuation(R, pAdicValuation(QQ, 2)).augmentation(x, 1) @@ -1204,7 +1164,6 @@ def __init__(self, parent, base_valuation, to_base_valuation_doain, from_base_va r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * sage: K. = FunctionField(QQ) sage: R. = QQ[] sage: w = GaussValuation(R, pAdicValuation(QQ, 2)).augmentation(x, 1) @@ -1224,7 +1183,6 @@ class InfiniteRationalFunctionFieldValuation(FunctionFieldMappedValuation_base, EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, 1/x) # indirect doctest @@ -1233,7 +1191,6 @@ def __init__(self, parent): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: v = FunctionFieldValuation(K, 1/x) # indirect doctest sage: isinstance(v, InfiniteRationalFunctionFieldValuation) @@ -1251,7 +1208,6 @@ def _repr_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: FunctionFieldValuation(K, 1/x) # indirect doctest Valuation at the infinite place @@ -1270,7 +1226,6 @@ class FunctionFieldExtensionMappedValuation(FunctionFieldMappedValuation_base): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) @@ -1296,7 +1251,6 @@ def _repr_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) @@ -1324,7 +1278,6 @@ def restriction(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index cde2f182fea..c2461ca7a41 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -41,7 +41,6 @@ class PadicValuationFactory(UniqueFactory): For integers and rational numbers, ``prime`` is just a prime of the integers:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(ZZ, 3) 3-adic valuation @@ -156,7 +155,6 @@ def create_key_and_extra_args(self, R, prime=None, approximants=None): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2) # indirect doctest 2-adic valuation @@ -186,7 +184,6 @@ def create_key_for_integers(self, R, prime): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2) # indirect doctest 2-adic valuation @@ -208,7 +205,6 @@ def create_key_for_local_ring(self, R, prime): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(Qp(2)) # indirect doctest 2-adic valuation @@ -231,7 +227,6 @@ def create_key_and_extra_args_for_number_field(self, R, prime, approximants): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(GaussianIntegers(), 2) # indirect doctest 2-adic valuation @@ -262,7 +257,6 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime, EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(GaussianIntegers(), pAdicValuation(ZZ, 2)) # indirect doctest 2-adic valuation @@ -328,7 +322,6 @@ def create_key_and_extra_args_for_number_field_from_ideal(self, R, I, prime): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(GaussianIntegers(), GaussianIntegers().ideal(2)) # indirect doctest 2-adic valuation @@ -368,7 +361,6 @@ def _normalize_number_field_data(self, R): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: K = R.quo(x^2 + 1) sage: pAdicValuation._normalize_number_field_data(K) @@ -403,7 +395,6 @@ def create_object(self, version, key, **extra_args): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(ZZ, 5) # indirect doctest 5-adic valuation @@ -451,7 +442,6 @@ class pAdicValuation_base(DiscreteValuation): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(ZZ, 3) 3-adic valuation @@ -476,7 +466,6 @@ def __init__(self, parent, p): """ TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: isinstance(pAdicValuation(ZZ, 3), pAdicValuation_base) True @@ -492,7 +481,6 @@ def p(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(GaussianIntegers(), 2).p() 2 @@ -513,7 +501,6 @@ def reduce(self, x): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: v.reduce(4) 1 @@ -536,7 +523,6 @@ def lift(self, x): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: xbar = v.reduce(4) sage: v.lift(xbar) @@ -570,7 +556,6 @@ def is_unramified(self, G, include_steps=False, assume_squarefree=False): We consider an extension as unramified if its ramification index is 1. Hence, a trivial extension is unramified:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = pAdicValuation(QQ, 2) sage: v.is_unramified(x) @@ -649,7 +634,6 @@ def is_totally_ramified(self, G, include_steps=False, assume_squarefree=False): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: k=Qp(5,4) sage: v = pAdicValuation(k) sage: R.=k[] @@ -711,7 +695,6 @@ def change_domain(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.change_domain(QQ).domain() Rational Field @@ -726,7 +709,6 @@ def _extensions_to_quotient(self, ring, approximants=None): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: pAdicValuation(QQ, 2)._extensions_to_quotient(R.quo(x^2 + x + 1)) [2-adic valuation] @@ -743,7 +725,6 @@ def extensions(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.extensions(GaussianIntegers()) [2-adic valuation] @@ -803,7 +784,6 @@ def restriction(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(GaussianIntegers(), 2) sage: v.restriction(ZZ) 2-adic valuation @@ -824,7 +804,6 @@ def value_semigroup(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(GaussianIntegers(), 2) sage: v.value_semigroup() Additive Abelian Semigroup generated by 1/2 @@ -848,7 +827,6 @@ class pAdicValuation_padic(pAdicValuation_base): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(Qp(2)); v #indirect doctest 2-adic valuation @@ -861,7 +839,6 @@ def __init__(self, parent): """ TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: from sage.rings.padics.padic_valuation import padicValuation_padic # optional: integrated sage: isinstance(pAdicValuation(Qp(2)), pAdicValuation_padic) True @@ -883,7 +860,6 @@ def reduce(self, x): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(3) sage: pAdicValuation(Zp(3)).reduce(R(4)) 1 @@ -903,7 +879,6 @@ def lift(self, x): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(3) sage: v = pAdicValuation(R) sage: xbar = v.reduce(R(4)) @@ -920,7 +895,6 @@ def uniformizer(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(Zp(3)) sage: v.uniformizer() 3 + O(3^21) @@ -938,7 +912,6 @@ def element_with_valuation(self, v): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(3) sage: v = pAdicValuation(Zp(3)) sage: v.element_with_valuation(3) @@ -958,7 +931,6 @@ def _repr_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(ZZ, 3)._repr_() '3-adic valuation' @@ -971,7 +943,6 @@ def _call_(self, x): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = Qp(3) sage: R. = K[] sage: L. = K.extension(y^2 - 3) @@ -987,7 +958,6 @@ def residue_ring(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(Qq(9, names='a'), 3).residue_ring() Finite Field in a0 of size 3^2 @@ -1008,7 +978,6 @@ def shift(self, x, s): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = ZpCA(2) sage: v = pAdicValuation(R) sage: v.shift(R.one(), 1) @@ -1042,7 +1011,6 @@ def simplify(self, x, error=None, force=False): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(2) sage: v = pAdicValuation(R, 2) sage: v.simplify(6) @@ -1068,7 +1036,6 @@ class pAdicValuation_int(pAdicValuation_base): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3); v 3-adic valuation @@ -1083,7 +1050,6 @@ def _repr_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(ZZ, 3)._repr_() '3-adic valuation' @@ -1100,7 +1066,6 @@ def _call_(self, x): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(ZZ, 3)(9) 2 @@ -1119,7 +1084,6 @@ def uniformizer(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: v.uniformizer() 3 @@ -1133,7 +1097,6 @@ def residue_ring(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: v.residue_ring() Finite Field of size 3 @@ -1149,7 +1112,6 @@ def _ge_(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: w = TrivialValuation(ZZ) sage: v >= w @@ -1176,7 +1138,6 @@ def _relative_size(self, x): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v._relative_size(2) 2 @@ -1210,7 +1171,6 @@ def simplify(self, x, error=None, force=False, size_heuristic_bound=32): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.simplify(6, force=True) 2 @@ -1257,7 +1217,6 @@ class pAdicFromLimitValuation(FiniteExtensionFromLimitValuation, pAdicValuation_ EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(GaussianIntegers(), 3); v 3-adic valuation @@ -1270,7 +1229,6 @@ def __init__(self, parent, approximant, G, approximants): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(GaussianIntegers(), 3) sage: isinstance(v, pAdicFromLimitValuation) True @@ -1286,7 +1244,6 @@ def _to_base_domain(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(GaussianIntegers(), 3) sage: I = GaussianIntegers().fraction_field().gen() sage: v._to_base_domain(I) @@ -1303,7 +1260,6 @@ def _from_base_domain(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(GaussianIntegers(), 3) sage: v._from_base_domain(v._base_valuation.domain().gen()) I @@ -1317,7 +1273,6 @@ def extensions(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(GaussianIntegers(), 3) sage: v.extensions(v.domain().fraction_field()) [3-adic valuation] @@ -1345,7 +1300,6 @@ def _fraction_field(ring): sage: S.fraction_field() Fraction Field of Univariate Quotient Polynomial Ring in xbar over Integer Ring with modulus x^2 + 1 - sage: from mac_lane.padic_valuation import _fraction_field sage: _fraction_field(S) Univariate Quotient Polynomial Ring in xbar over Rational Field with modulus x^2 + 1 diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index 4ac2094ee41..7c345c25f85 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -7,7 +7,6 @@ Starting from a :class:`GaussValuation`, we can create augmented valuations on polynomial rings:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x, 1); w @@ -147,7 +146,7 @@ """ #***************************************************************************** -# Copyright (C) 2013-2016 Julian Rüth +# Copyright (C) 2013-2017 Julian Rüth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -171,7 +170,6 @@ class AugmentedValuationFactory(UniqueFactory): This factory is not meant to be called directly. Instead, :meth:`augmentation` of a valuation should be called:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x, 1) # indirect doctest @@ -200,7 +198,6 @@ def create_key(self, base_valuation, phi, mu, check=True): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x, 1) # indirect doctest @@ -235,7 +232,6 @@ def create_object(self, version, key): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) # indirect doctest @@ -272,7 +268,6 @@ class AugmentedValuation_base(InductiveValuation): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = CyclotomicField(5) sage: R. = K[] sage: v = GaussValuation(R, pAdicValuation(K, 2)) @@ -291,7 +286,6 @@ def __init__(self, parent, v, phi, mu): """ TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = Qq(4, 5) sage: R. = K[] sage: v = GaussValuation(R) @@ -327,7 +321,6 @@ def equivalence_unit(self, s, reciprocal=False): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -388,7 +381,6 @@ def element_with_valuation(self, s): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -426,7 +418,6 @@ def _repr_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -447,7 +438,6 @@ def augmentation_chain(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x, 1) @@ -479,7 +469,6 @@ def psi(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -506,7 +495,6 @@ def E(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -532,7 +520,6 @@ def F(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -554,7 +541,6 @@ def extensions(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -595,7 +581,6 @@ def restriction(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = GaussianIntegers().fraction_field() sage: R. = K[] sage: v = GaussValuation(R, pAdicValuation(K, 2)) @@ -620,7 +605,6 @@ def uniformizer(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -637,7 +621,6 @@ def is_gauss_valuation(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -657,7 +640,6 @@ def monic_integral_model(self, G): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -679,7 +661,6 @@ def _ge_(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -714,7 +695,6 @@ def is_trivial(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -733,7 +713,6 @@ def scale(self, scalar): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -754,7 +733,6 @@ def _residue_ring_generator_name(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -791,7 +769,6 @@ def _relative_size(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: K. = QQ.extension(u^2 + u+ 1) sage: S. = K[] @@ -814,7 +791,6 @@ def is_negative_pseudo_valuation(self): No element in the domain of an augmented valuation can have valuation `-\infty`, so this method always returns ``False``:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: w = v.augmentation(x, infinity) @@ -832,7 +808,6 @@ def change_domain(self, ring): We can change the domain of an augmented valuation even if there is no coercion between rings:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = GaussianIntegers()[] sage: v = GaussValuation(R, pAdicValuation(GaussianIntegers(), 2)) sage: v = v.augmentation(x, 1) @@ -853,7 +828,6 @@ class FinalAugmentedValuation(AugmentedValuation_base, FinalInductiveValuation): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: w = v.augmentation(x, 1) @@ -863,7 +837,6 @@ def __init__(self, parent, v, phi, mu): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: w = v.augmentation(x, 1) @@ -882,7 +855,6 @@ def residue_ring(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) @@ -967,7 +939,6 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) @@ -1043,7 +1014,6 @@ def _residue_field_generator(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) @@ -1091,7 +1061,6 @@ def lift(self, F): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) @@ -1144,7 +1113,6 @@ class NonFinalAugmentedValuation(AugmentedValuation_base, NonFinalInductiveValua EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -1154,7 +1122,6 @@ def __init__(self, parent, v, phi, mu): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -1173,7 +1140,6 @@ def residue_ring(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) @@ -1251,7 +1217,6 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 10) sage: S. = R[] sage: v = GaussValuation(S) @@ -1348,7 +1313,6 @@ def _residue_field_generator(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 10) sage: S. = R[] sage: v = GaussValuation(S) @@ -1391,7 +1355,6 @@ def lift(self, F, report_coefficients=False): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 10) sage: S. = R[] sage: v = GaussValuation(S) @@ -1497,7 +1460,6 @@ def lift_to_key(self, F, check=True): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 10) sage: S. = R[] sage: v = GaussValuation(S) @@ -1569,7 +1531,6 @@ def _Q(self, e): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -1590,7 +1551,6 @@ def _Q_reciprocal(self, e=1): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, 1) @@ -1622,7 +1582,6 @@ class FiniteAugmentedValuation(AugmentedValuation_base, FiniteInductiveValuation EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1633,7 +1592,6 @@ def __init__(self, parent, v, phi, mu): r""" EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1652,7 +1610,6 @@ def value_group(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1674,7 +1631,6 @@ def value_semigroup(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Zq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1714,7 +1670,6 @@ def valuations(self, f, coefficients=None, call_error=False): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1778,7 +1733,6 @@ def simplify(self, f, error=None, force=False, effective_degree=None, size_heuri EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1819,7 +1773,6 @@ def lower_bound(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1861,7 +1814,6 @@ def upper_bound(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1884,7 +1836,6 @@ class FinalFiniteAugmentedValuation(FiniteAugmentedValuation, FinalAugmentedValu EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: w = v.augmentation(x, 1) @@ -1894,7 +1845,6 @@ def __init__(self, parent, v, phi, mu): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: w = v.augmentation(x, 1) @@ -1913,7 +1863,6 @@ class NonFinalFiniteAugmentedValuation(FiniteAugmentedValuation, NonFinalAugment EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x, 1) @@ -1923,7 +1872,6 @@ def __init__(self, parent, v, phi, mu): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x, 1) @@ -1943,7 +1891,6 @@ class InfiniteAugmentedValuation(FinalAugmentedValuation, InfiniteInductiveValua EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x, infinity) @@ -1953,7 +1900,6 @@ def __init__(self, parent, v, phi, mu): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x, infinity) @@ -1971,7 +1917,6 @@ def value_group(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1989,7 +1934,6 @@ def value_semigroup(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Zq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -2024,7 +1968,6 @@ def valuations(self, f, coefficients=None, call_error=False): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -2068,7 +2011,6 @@ def simplify(self, f, error=None, force=False, effective_degree=None): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -2096,7 +2038,6 @@ def lower_bound(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -2116,7 +2057,6 @@ def upper_bound(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) diff --git a/src/sage/rings/valuation/developing_valuation.py b/src/sage/rings/valuation/developing_valuation.py index 57ee6687642..3057139cfae 100644 --- a/src/sage/rings/valuation/developing_valuation.py +++ b/src/sage/rings/valuation/developing_valuation.py @@ -30,7 +30,6 @@ class DevelopingValuation(DiscretePseudoValuation): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 7)) @@ -43,7 +42,6 @@ def __init__(self, parent, phi): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 7)) sage: isinstance(v, DevelopingValuation) @@ -69,7 +67,6 @@ def phi(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(2,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -93,7 +90,6 @@ def effective_degree(self, f, valuations=None): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(2,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -125,7 +121,6 @@ def _pow(self, x, e, error, effective_degree): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(2,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -157,7 +152,6 @@ def coefficients(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Qp(2,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -192,7 +186,6 @@ def _quo_rem(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 2)) sage: v._quo_rem(x^2 + 1) @@ -211,7 +204,6 @@ def newton_polygon(self, f, valuations=None): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Qp(2,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -243,7 +235,6 @@ def _call_(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Qp(2,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -289,7 +280,6 @@ def valuations(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Qp(2,5) sage: S. = R[] sage: v = GaussValuation(S, pAdicValuation(R)) @@ -305,7 +295,6 @@ def _test_effective_degree(self, **options): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(2,5) sage: S. = R[] sage: v = GaussValuation(S) diff --git a/src/sage/rings/valuation/gauss_valuation.py b/src/sage/rings/valuation/gauss_valuation.py index b0744c2e59a..10facdf8b43 100644 --- a/src/sage/rings/valuation/gauss_valuation.py +++ b/src/sage/rings/valuation/gauss_valuation.py @@ -12,7 +12,6 @@ EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v0 = pAdicValuation(QQ, 2) sage: v = GaussValuation(R, v0); v @@ -64,7 +63,6 @@ class GaussValuationFactory(UniqueFactory): The Gauss valuation is the minimum of the valuation of the coefficients:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: R. = QQ[] sage: w = GaussValuation(R, v) @@ -82,7 +80,6 @@ def create_key(self, domain, v = None): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: R. = ZZ[] sage: GaussValuation.create_key(R, v) @@ -113,7 +110,6 @@ def create_object(self, version, key, **extra_args): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: R. = QQ[] sage: GaussValuation.create_object(0, (R, v)) @@ -139,7 +135,6 @@ class GaussValuation_generic(NonFinalInductiveValuation): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(3,5) sage: S. = R[] sage: v0 = pAdicValuation(R) @@ -159,8 +154,7 @@ def __init__(self, parent, v): """ TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: from mac_lane.gauss_valuation import GaussValuation_generic # optional: standalone + sage: from gauss_valuation import GaussValuation_generic sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) sage: isinstance(v, GaussValuation_generic) @@ -177,7 +171,6 @@ def value_group(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) sage: v.value_group() @@ -192,7 +185,6 @@ def value_semigroup(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) sage: v.value_semigroup() @@ -207,7 +199,6 @@ def _repr_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) sage: v # indirect doctest @@ -224,7 +215,6 @@ def uniformizer(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = QQ[] sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) sage: v.uniformizer() @@ -258,7 +248,6 @@ def valuations(self, f, coefficients=None, call_error=False): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = ZZ sage: S. = R[] sage: v = GaussValuation(S, pAdicValuation(R, 2)) @@ -302,7 +291,6 @@ def residue_ring(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = Qp(2,5)[] sage: v = GaussValuation(S) sage: v.residue_ring() @@ -337,7 +325,6 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = Qp(2,5)[] sage: v = GaussValuation(S) sage: f = x^2 + 2*x + 16 @@ -386,7 +373,6 @@ def lift(self, F): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = Qp(3,5)[] sage: v = GaussValuation(S) sage: f = x^2 + 2*x + 16 @@ -426,7 +412,6 @@ def lift_to_key(self, F): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ sage: S. = R[] sage: v = GaussValuation(S, pAdicValuation(QQ, 2)) @@ -461,7 +446,6 @@ def equivalence_unit(self, s, reciprocal=False): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = Qp(3,5)[] sage: v = GaussValuation(S) sage: v.equivalence_unit(2) @@ -482,7 +466,6 @@ def element_with_valuation(self, s): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: v.element_with_valuation(-2) @@ -498,7 +481,6 @@ def E(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -516,7 +498,6 @@ def F(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -533,7 +514,6 @@ def change_domain(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: R. = ZZ[] sage: w = GaussValuation(R, v) @@ -553,7 +533,6 @@ def extensions(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: R. = ZZ[] sage: w = GaussValuation(R, v) @@ -573,7 +552,6 @@ def restriction(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: R. = ZZ[] sage: w = GaussValuation(R, v) @@ -595,7 +573,6 @@ def is_gauss_valuation(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -612,7 +589,6 @@ def augmentation_chain(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -629,7 +605,6 @@ def is_trivial(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: v.is_trivial() @@ -646,7 +621,6 @@ def monic_integral_model(self, G): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qp(2, 5)[] sage: v = GaussValuation(R) sage: v.monic_integral_model(5*x^2 + 1/2*x + 1/4) @@ -683,7 +657,6 @@ def _ge_(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = GaussValuation(R, pAdicValuation(QQ, 3)) @@ -708,7 +681,6 @@ def scale(self, scalar): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: 3*v # indirect doctest @@ -734,7 +706,6 @@ def _relative_size(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: v._relative_size(x + 1024) @@ -778,7 +749,6 @@ def simplify(self, f, error=None, force=False, size_heuristic_bound=32, effectiv EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -810,7 +780,6 @@ def lower_bound(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -839,7 +808,6 @@ def upper_bound(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index ff40530cf42..fddee29a56f 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -40,7 +40,6 @@ class InductiveValuation(DevelopingValuation): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 5)) @@ -60,7 +59,6 @@ def is_equivalence_unit(self, f, valuations=None): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(2,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -101,7 +99,6 @@ def equivalence_reciprocal(self, f, coefficients=None, valuations=None, check=Tr EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R = Zp(3,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -209,7 +206,6 @@ def mu(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: v.mu() @@ -233,7 +229,6 @@ def equivalence_unit(self, s, reciprocal=False): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: S. = Qp(3,5)[] sage: v = GaussValuation(S) sage: v.equivalence_unit(2) @@ -264,7 +259,6 @@ def augmentation_chain(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -280,7 +274,6 @@ def is_gauss_valuation(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -297,7 +290,6 @@ def E(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -313,7 +305,6 @@ def F(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -331,7 +322,6 @@ def monic_integral_model(self, G): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: v.monic_integral_model(5*x^2 + 1/2*x + 1/4) @@ -350,7 +340,6 @@ def element_with_valuation(self, s): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: v.element_with_valuation(-2) @@ -374,7 +363,6 @@ def _test_element_with_valuation_inductive_valuation(self, **options): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: v._test_element_with_valuation_inductive_valuation() @@ -406,7 +394,6 @@ def _test_EF(self, **options): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -430,7 +417,6 @@ def _test_augmentation_chain(self, **options): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: v._test_augmentation_chain() @@ -449,7 +435,6 @@ def _test_equivalence_unit(self, **options): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: v._test_equivalence_unit() @@ -482,7 +467,6 @@ def _test_is_equivalence_unit(self, **options): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: v._test_is_equivalence_unit() @@ -497,7 +481,6 @@ def _test_equivalence_reciprocal(self, **options): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: v._test_equivalence_reciprocal() @@ -528,7 +511,6 @@ def _test_inductive_valuation_inheritance(self, **options): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: v._test_inductive_valuation_inheritance() @@ -547,7 +529,6 @@ class FiniteInductiveValuation(InductiveValuation, DiscreteValuation): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) @@ -556,7 +537,6 @@ def __init__(self, parent, phi): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: isinstance(v, FiniteInductiveValuation) @@ -572,7 +552,6 @@ def extensions(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = ZZ[] sage: v = GaussValuation(R, TrivialValuation(ZZ)) sage: K. = FunctionField(QQ) @@ -597,7 +576,6 @@ class NonFinalInductiveValuation(FiniteInductiveValuation, DiscreteValuation): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -608,7 +586,6 @@ def __init__(self, parent, phi): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -639,7 +616,6 @@ def augmentation(self, phi, mu, check=True): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -711,7 +687,6 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: S. = K[] sage: F = y^2 - x^2 - x^3 - 3 @@ -890,7 +865,6 @@ def is_key(self, phi, explain=False, assume_equivalence_irreducible=False): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -936,7 +910,6 @@ def is_minimal(self, f, assume_equivalence_irreducible=False): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4, 5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1025,7 +998,6 @@ def _equivalence_reduction(self, f, coefficients=None, valuations=None, degree_b EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: v._equivalence_reduction(2*x^6 + 4*x^5 + 2*x^4 + 8) @@ -1084,7 +1056,6 @@ def is_equivalence_irreducible(self, f, coefficients=None, valuations=None): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,5) sage: S. = R[] sage: v = GaussValuation(S) @@ -1153,7 +1124,6 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,10) sage: S. = R[] sage: v = GaussValuation(S) @@ -1308,7 +1278,6 @@ def minimal_representative(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,10) sage: S. = R[] sage: v = GaussValuation(S) @@ -1384,7 +1353,6 @@ def lift_to_key(self, F): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = Qq(4,10) sage: S. = R[] sage: v = GaussValuation(S) @@ -1401,7 +1369,6 @@ def _test_lift_to_key(self, **options): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: v._test_lift_to_key() @@ -1451,7 +1418,6 @@ def _test_is_equivalence_irreducible(self, **options): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: v._test_is_equivalence_irreducible() @@ -1478,7 +1444,6 @@ class FinalInductiveValuation(InductiveValuation): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)) sage: w = v.augmentation(x^2 + x + 1, infinity) @@ -1495,7 +1460,6 @@ class InfiniteInductiveValuation(FinalInductiveValuation, InfiniteDiscretePseudo EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, infinity) @@ -1505,7 +1469,6 @@ def __init__(self, parent, base_valuation): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, infinity) @@ -1524,7 +1487,6 @@ def change_domain(self, ring): We can turn an infinite valuation into a valuation on the quotient:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = v.augmentation(x^2 + x + 1, infinity) @@ -1544,8 +1506,6 @@ def _lift_to_maximal_precision(c): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: from mac_lane.inductive_valuation import _lift_to_maximal_precision # optional: standalone sage: R = Zp(2,5) sage: x = R(1,2); x 1 + O(2^2) diff --git a/src/sage/rings/valuation/limit_valuation.py b/src/sage/rings/valuation/limit_valuation.py index 79113ed8c6f..b6c76b353dc 100644 --- a/src/sage/rings/valuation/limit_valuation.py +++ b/src/sage/rings/valuation/limit_valuation.py @@ -26,7 +26,6 @@ point has two extensions to ``L``. The valuations corresponding to these extensions can only be approximated:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -96,7 +95,6 @@ class LimitValuationFactory(UniqueFactory): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = LimitValuation(v, x) @@ -113,7 +111,6 @@ def create_key(self, base_valuation, G): Note that this does not normalize ``base_valuation`` in any way. It is easily possible to create the same limit in two different ways:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = LimitValuation(v, x) # indirect doctest @@ -137,7 +134,6 @@ def create_object(self, version, key): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) sage: w = LimitValuation(v, x^2 + 1) # indirect doctest @@ -159,7 +155,6 @@ class LimitValuation_generic(DiscretePseudoValuation): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -186,7 +181,6 @@ def __init__(self, parent, approximation): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: K. = QQ.extension(x^2 + 1) sage: v = pAdicValuation(K, 2) @@ -213,7 +207,6 @@ def reduce(self, f, check=True): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x - 1)) @@ -235,7 +228,6 @@ def _call_(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -257,7 +249,6 @@ def _improve_approximation_for_reduce(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x - 1337)) @@ -302,7 +293,6 @@ def _improve_approximation_for_call(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x - 23)) @@ -338,7 +328,6 @@ def _repr_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -370,7 +359,6 @@ class MacLaneLimitValuation(LimitValuation_generic, InfiniteDiscretePseudoValuat EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: K. = QQ.extension(x^2 + 1) @@ -383,7 +371,6 @@ def __init__(self, parent, approximation, G): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: K. = QQ.extension(x^2 + 1) sage: v = pAdicValuation(K, 2) @@ -405,7 +392,6 @@ def extensions(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(GaussianIntegers(), 2) sage: u = v._base_valuation sage: u.extensions(QQ['x']) @@ -431,7 +417,6 @@ def lift(self, F): EXAMPLES;; - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^4 - x^2 - 2*x - 1) @@ -454,7 +439,6 @@ def uniformizer(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -473,7 +457,6 @@ def _call_(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: vK = pAdicValuation(K, 2) @@ -506,7 +489,6 @@ def _improve_approximation(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -549,7 +531,6 @@ def _improve_approximation_for_call(self, f): approximation (perform one step of the Mac Lane algorithm) than to check for this:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -627,7 +608,6 @@ def _improve_approximation_for_reduce(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -657,7 +637,6 @@ def residue_ring(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -684,7 +663,6 @@ def _ge_(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: F = (x^2 + 7) * (x^2 + 9) sage: G = (x^2 + 7) @@ -728,7 +706,6 @@ def restriction(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -751,7 +728,6 @@ def _weakly_separating_element(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -808,7 +784,6 @@ def value_semigroup(self): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -826,7 +801,6 @@ def element_with_valuation(self, s): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -852,7 +826,6 @@ def _relative_size(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -874,7 +847,6 @@ def simplify(self, f, error=None, force=False): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -904,7 +876,6 @@ def lower_bound(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -928,7 +899,6 @@ def upper_bound(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -953,7 +923,6 @@ def is_negative_pseudo_valuation(self): For a Mac Lane limit valuation, this is never the case, so this method always returns ``False``:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) diff --git a/src/sage/rings/valuation/mapped_valuation.py b/src/sage/rings/valuation/mapped_valuation.py index 29d19268edc..86e3afd9271 100644 --- a/src/sage/rings/valuation/mapped_valuation.py +++ b/src/sage/rings/valuation/mapped_valuation.py @@ -7,7 +7,6 @@ Extensions of valuations over finite field extensions `L=K[x]/(G)` are realized through an infinite valuation on `K[x]` which maps `G` to infinity:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -41,7 +40,6 @@ class MappedValuation_base(DiscretePseudoValuation): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -59,7 +57,6 @@ def __init__(self, parent, base_valuation): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x^2 + 1) @@ -84,7 +81,6 @@ def _repr_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -100,7 +96,6 @@ def residue_ring(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -117,7 +112,6 @@ def uniformizer(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -134,7 +128,6 @@ def _to_base_domain(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -153,7 +146,6 @@ def _from_base_domain(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -172,7 +164,6 @@ def _call_(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -191,7 +182,6 @@ def reduce(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x - 2)) @@ -211,7 +201,6 @@ def lift(self, F): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -234,7 +223,6 @@ def _to_base_residue_ring(self, F): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -254,7 +242,6 @@ def _from_base_residue_ring(self, F): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -274,7 +261,6 @@ def _test_to_from_base_domain(self, **options): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -307,7 +293,6 @@ class FiniteExtensionFromInfiniteValuation(MappedValuation_base, DiscreteValuati EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -321,7 +306,6 @@ def __init__(self, parent, base_valuation): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -342,7 +326,6 @@ def _eq_(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -361,7 +344,6 @@ def restriction(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -384,7 +366,6 @@ def _weakly_separating_element(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -414,7 +395,6 @@ def _relative_size(self, x): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -436,7 +416,6 @@ def simplify(self, x, error=None, force=False): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -463,7 +442,6 @@ def lower_bound(self, x): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -487,7 +465,6 @@ def upper_bound(self, x): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) @@ -510,7 +487,6 @@ class FiniteExtensionFromLimitValuation(FiniteExtensionFromInfiniteValuation): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -532,7 +508,6 @@ def __init__(self, parent, approximant, G, approximants): Note that this implementation is also used when the underlying limit is only taken over a finite sequence of valuations:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -558,7 +533,6 @@ def _repr_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(GaussianIntegers().fraction_field(), 2) # indirect doctest 2-adic valuation diff --git a/src/sage/rings/valuation/scaled_valuation.py b/src/sage/rings/valuation/scaled_valuation.py index 586a6f298e8..bd665ce3f7f 100644 --- a/src/sage/rings/valuation/scaled_valuation.py +++ b/src/sage/rings/valuation/scaled_valuation.py @@ -4,7 +4,6 @@ EXAMPLES: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: 3*pAdicValuation(ZZ, 3) 3 * 3-adic valuation @@ -31,7 +30,6 @@ class ScaledValuationFactory(UniqueFactory): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: 3*pAdicValuation(ZZ, 2) # indirect doctest 3 * 2-adic valuation @@ -42,7 +40,6 @@ def create_key(self, base, s): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: 3*pAdicValuation(ZZ, 2) is 2*(3/2*pAdicValuation(ZZ, 2)) # indirect doctest True @@ -73,7 +70,6 @@ def create_object(self, version, key): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: 3*pAdicValuation(ZZ, 2) # indirect doctest 3 * 2-adic valuation @@ -95,7 +91,6 @@ class ScaledValuation_generic(DiscreteValuation): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = 3*pAdicValuation(ZZ, 3); v 3 * 3-adic valuation @@ -108,7 +103,6 @@ def __init__(self, parent, base_valuation, s): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = 3*pAdicValuation(ZZ, 2) sage: isinstance(v, ScaledValuation_generic) True @@ -125,7 +119,6 @@ def _repr_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: 3*pAdicValuation(ZZ, 2) # indirect doctest 3 * 2-adic valuation @@ -138,7 +131,6 @@ def residue_ring(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = 3*pAdicValuation(ZZ, 2) sage: v.residue_ring() Finite Field of size 2 @@ -152,7 +144,6 @@ def uniformizer(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = 3*pAdicValuation(ZZ, 2) sage: v.uniformizer() 2 @@ -166,7 +157,6 @@ def _call_(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = 3*pAdicValuation(ZZ, 2) sage: v(2) 3 @@ -180,7 +170,6 @@ def reduce(self, f): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = 3*pAdicValuation(ZZ, 2) sage: v.reduce(1) 1 @@ -195,7 +184,6 @@ def lift(self, F): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = 3*pAdicValuation(ZZ, 2) sage: v.lift(1) 1 @@ -209,7 +197,6 @@ def extensions(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = 3*pAdicValuation(ZZ, 5) sage: v.extensions(GaussianIntegers().fraction_field()) [3 * [ 5-adic valuation, v(x + 2) = 1 ]-adic valuation, @@ -224,7 +211,6 @@ def restriction(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = 3*pAdicValuation(QQ, 5) sage: v.restriction(ZZ) 3 * 5-adic valuation @@ -240,7 +226,6 @@ def _strictly_separating_element(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v2 = pAdicValuation(QQ, 2) sage: v3 = 12 * pAdicValuation(QQ, 3) sage: v2._strictly_separating_element(v3) @@ -267,7 +252,6 @@ def _weakly_separating_element(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v2 = pAdicValuation(QQ, 2) sage: v3 = 12 * pAdicValuation(QQ, 3) sage: v2._weakly_separating_element(v3) @@ -283,7 +267,6 @@ def _ge_(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v2 = pAdicValuation(QQ, 2) sage: 2*v2 >= v2 True @@ -319,7 +302,6 @@ def _le_(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v2 = pAdicValuation(QQ, 2) sage: 2*v2 <= v2 False @@ -343,7 +325,6 @@ def value_semigroup(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v2 = pAdicValuation(QQ, 2) sage: (2*v2).value_semigroup() Additive Abelian Semigroup generated by -2, 2 diff --git a/src/sage/rings/valuation/trivial_valuation.py b/src/sage/rings/valuation/trivial_valuation.py index 76b1744146d..d50078fdf78 100644 --- a/src/sage/rings/valuation/trivial_valuation.py +++ b/src/sage/rings/valuation/trivial_valuation.py @@ -4,7 +4,6 @@ EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ); v Trivial valuation on Rational Field sage: v(1) @@ -58,7 +57,6 @@ class TrivialValuationFactory(UniqueFactory): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ); v Trivial valuation on Rational Field sage: v(1) @@ -69,8 +67,6 @@ def __init__(self, clazz, parent, *args, **kwargs): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone - sage: from mac_lane.trivial_valuation import TrivialValuationFactory sage: isinstance(TrivialValuation, TrivialValuationFactory) True @@ -85,7 +81,6 @@ def create_key(self, domain): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: TrivialValuation(QQ) is TrivialValuation(QQ) # indirect doctest True @@ -98,7 +93,6 @@ def create_object(self, version, key, **extra_args): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: TrivialValuation(QQ) # indirect doctest Trivial valuation on Rational Field @@ -113,7 +107,6 @@ class TrivialDiscretePseudoValuation_base(DiscretePseudoValuation): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(ZZ); v Trivial pseudo-valuation on Integer Ring @@ -128,7 +121,6 @@ def uniformizer(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(ZZ) sage: v.uniformizer() Traceback (most recent call last): @@ -144,7 +136,6 @@ def is_trivial(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ) sage: v.is_trivial() True @@ -158,7 +149,6 @@ def is_negative_pseudo_valuation(self): EXAMPLES: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ) sage: v.is_negative_pseudo_valuation() False @@ -172,7 +162,6 @@ class TrivialDiscretePseudoValuation(TrivialDiscretePseudoValuation_base, Infini EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ); v Trivial pseudo-valuation on Rational Field @@ -185,7 +174,6 @@ def __init__(self, parent): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ) sage: isinstance(v, TrivialDiscretePseudoValuation) True @@ -200,7 +188,6 @@ def _call_(self, x): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ) sage: v(0) +Infinity @@ -217,7 +204,6 @@ def _repr_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: TrivialPseudoValuation(QQ) # indirect doctest Trivial pseudo-valuation on Rational Field @@ -232,7 +218,6 @@ def value_group(self): A trivial discrete pseudo-valuation has no value group:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ) sage: v.value_group() Traceback (most recent call last): @@ -248,7 +233,6 @@ def residue_ring(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: TrivialPseudoValuation(QQ).residue_ring() Quotient of Rational Field by the ideal (1) @@ -261,7 +245,6 @@ def reduce(self, x): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ) sage: v.reduce(1) 0 @@ -276,7 +259,6 @@ def lift(self, X): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ) sage: v.lift(v.residue_ring().zero()) 0 @@ -292,7 +274,6 @@ def _ge_(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ) sage: w = TrivialValuation(QQ) sage: v >= w @@ -308,7 +289,6 @@ class TrivialDiscreteValuation(TrivialDiscretePseudoValuation_base, DiscreteValu EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ); v Trivial valuation on Rational Field @@ -321,7 +301,6 @@ def __init__(self, parent): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ) sage: isinstance(v, TrivialDiscreteValuation) True @@ -336,7 +315,6 @@ def _call_(self, x): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ) sage: v(0) +Infinity @@ -353,7 +331,6 @@ def _repr_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: TrivialValuation(QQ) # indirect doctest Trivial valuation on Rational Field @@ -368,7 +345,6 @@ def value_group(self): A trivial discrete valuation has a trivial value group:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ) sage: v.value_group() Trivial Additive Abelian Group @@ -383,7 +359,6 @@ def residue_ring(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: TrivialValuation(QQ).residue_ring() Rational Field @@ -396,7 +371,6 @@ def reduce(self, x): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ) sage: v.reduce(1) 1 @@ -410,7 +384,6 @@ def lift(self, X): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ) sage: v.lift(v.residue_ring().zero()) 0 @@ -424,7 +397,6 @@ def extensions(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(ZZ) sage: v.extensions(QQ) [Trivial valuation on Rational Field] @@ -441,7 +413,6 @@ def _ge_(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialPseudoValuation(QQ) sage: w = TrivialValuation(QQ) sage: w >= v diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 4a6848ee797..c852138cc51 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -32,7 +32,6 @@ class DiscretePseudoValuation(Morphism): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2); v # indirect doctest 2-adic valuation @@ -45,7 +44,6 @@ def __init__(self, parent): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: isinstance(pAdicValuation(ZZ, 2), DiscretePseudoValuation) True @@ -58,7 +56,6 @@ def is_equivalent(self, f, g): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: v.is_equivalent(2, 1) False @@ -87,7 +84,6 @@ def __hash__(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: hash(v) == hash(v) # indirect doctest True @@ -109,7 +105,6 @@ def _hash_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: hash(v) == hash(v) # indirect doctest True @@ -130,7 +125,6 @@ def _cmp_(self, other): when they can fall back to the implementation through ``>=`` and ``<=``:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: v > v False @@ -165,7 +159,6 @@ def _richcmp_(self, other, op): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: v == v True @@ -221,7 +214,6 @@ def _eq_(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ) sage: v == v True @@ -239,7 +231,6 @@ def _le_(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ) sage: w = pAdicValuation(QQ, 2) sage: v <= w @@ -270,7 +261,6 @@ def _ge_(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ) sage: w = pAdicValuation(QQ, 2) sage: v >= w @@ -309,7 +299,6 @@ def _test_valuation_inheritance(self, **options): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2)._test_valuation_inheritance() """ @@ -323,7 +312,6 @@ class InfiniteDiscretePseudoValuation(DiscretePseudoValuation): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: R. = QQ[] sage: v = GaussValuation(R, v) @@ -343,7 +331,6 @@ def is_discrete_valuation(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: R. = QQ[] sage: v = GaussValuation(R, v) @@ -362,7 +349,6 @@ class NegativeInfiniteDiscretePseudoValuation(InfiniteDiscretePseudoValuation): EXAMPLES: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)).augmentation(x, infinity) sage: K. = FunctionField(QQ) @@ -379,7 +365,6 @@ def is_negative_pseudo_valuation(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: R. = QQ[] sage: v = GaussValuation(R, TrivialValuation(QQ)).augmentation(x, infinity) sage: K. = FunctionField(QQ) @@ -397,7 +382,6 @@ class DiscreteValuation(DiscretePseudoValuation): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: R. = QQ[] sage: v = GaussValuation(R, v) @@ -417,7 +401,6 @@ def is_discrete_valuation(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(ZZ) sage: v.is_discrete_valuation() True @@ -470,7 +453,6 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: R. = QQ[] sage: v.mac_lane_approximants(x^2 + 1) @@ -789,7 +771,6 @@ def _pow(self, x, e, error): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: v._pow(2, 2, error=4) 4 @@ -813,7 +794,6 @@ def mac_lane_approximant(self, G, valuation, approximants = None): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: R. = QQ[] sage: G = x^2 + 1 @@ -924,7 +904,6 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: k=Qp(5,4) sage: v = pAdicValuation(k) sage: R.=k[] @@ -979,7 +958,6 @@ def _ge_(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = TrivialValuation(QQ) sage: w = pAdicValuation(QQ, 2) sage: v >= w diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index 3d47abbe905..19593a595b2 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -9,7 +9,6 @@ EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2).parent() Discrete pseudo-valuations on Rational Field @@ -38,7 +37,6 @@ class DiscretePseudoValuationSpace(UniqueRepresentation, Homset): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: H = DiscretePseudoValuationSpace(QQ) sage: pAdicValuation(QQ, 2) in H True @@ -77,7 +75,6 @@ def __init__(self, domain): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: isinstance(pAdicValuation(QQ, 2).parent(), DiscretePseudoValuationSpace) True @@ -109,7 +106,6 @@ def _abstract_element_class(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: isinstance(pAdicValuation(QQ, 2), DiscretePseudoValuationSpace.ElementMethods) # indirect doctest True @@ -124,7 +120,6 @@ def _get_action_(self, S, op, self_on_left): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: from operator import mul sage: v.parent().get_action(ZZ, mul) # indirect doctest @@ -144,7 +139,6 @@ def _an_element_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscretePseudoValuationSpace(QQ).an_element() # indirect doctest Trivial pseudo-valuation on Rational Field @@ -158,7 +152,6 @@ def _repr_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscretePseudoValuationSpace(QQ) # indirect doctest Discrete pseudo-valuations on Rational Field @@ -171,7 +164,6 @@ def __contains__(self, x): EXAMPLES: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: H = DiscretePseudoValuationSpace(QQ) sage: H.an_element() in H True @@ -194,7 +186,6 @@ def __call__(self, x): EXAMPLES: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: H = DiscretePseudoValuationSpace(QQ) sage: H(pAdicValuation(QQ, 2)) 2-adic valuation @@ -215,7 +206,6 @@ def _element_constructor_(self, x): We try to convert valuations defined on different domains by changing their base ring:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: Z = DiscretePseudoValuationSpace(ZZ) sage: Q = DiscretePseudoValuationSpace(QQ) sage: v = pAdicValuation(ZZ, 2) @@ -255,7 +245,6 @@ class ElementMethods: Here is an example of a method that is automagically added to a discrete valuation:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: H = DiscretePseudoValuationSpace(QQ) sage: pAdicValuation(QQ, 2).is_discrete_pseudo_valuation() # indirect doctest True @@ -263,7 +252,6 @@ class ElementMethods: The methods will be provided even if the concrete types is not created with :meth:`__make_element_class__`:: - sage: from mac_lane.valuation import DiscretePseudoValuation sage: m = DiscretePseudoValuation(H) sage: m.parent() is H True @@ -290,7 +278,6 @@ def is_discrete_pseudo_valuation(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2).is_discrete_pseudo_valuation() True @@ -306,7 +293,6 @@ def is_discrete_valuation(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2).is_discrete_valuation() True @@ -320,7 +306,6 @@ def is_negative_pseudo_valuation(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2).is_negative_pseudo_valuation() False @@ -344,7 +329,6 @@ def is_trivial(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 7).is_trivial() False @@ -366,7 +350,6 @@ def uniformizer(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 11).uniformizer() 11 @@ -391,7 +374,6 @@ def value_group(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2).value_group() Additive Abelian Group generated by 1 @@ -419,7 +401,6 @@ def value_semigroup(self): Most commonly, in particular over fields, the semigroup is the group generated by the valuation of the uniformizer:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: G = pAdicValuation(QQ, 2).value_semigroup(); G Additive Abelian Semigroup generated by -1, 1 sage: G in AdditiveMagmas().AdditiveAssociative().AdditiveUnital().AdditiveInverse() @@ -455,7 +436,6 @@ def element_with_valuation(self, s): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.element_with_valuation(10) 1024 @@ -484,7 +464,6 @@ def residue_ring(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2).residue_ring() Finite Field of size 2 sage: TrivialValuation(QQ).residue_ring() @@ -511,7 +490,6 @@ def residue_field(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: pAdicValuation(QQ, 2).residue_field() Finite Field of size 2 sage: TrivialValuation(QQ).residue_field() @@ -542,7 +520,6 @@ def reduce(self, x): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: v.reduce(2) 0 @@ -565,7 +542,6 @@ def lift(self, X): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: v.lift(v.residue_ring().one()) 1 @@ -578,7 +554,6 @@ def extension(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: w = v.extension(QQ) sage: w.domain() @@ -597,7 +572,6 @@ def extensions(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.extensions(QQ) [2-adic valuation] @@ -613,7 +587,6 @@ def restriction(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 2) sage: w = v.restriction(ZZ) sage: w.domain() @@ -634,7 +607,6 @@ def change_domain(self, ring): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 3) sage: v.change_domain(ZZ) 3-adic valuation @@ -658,7 +630,6 @@ def scale(self, scalar): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: w = v.scale(3) sage: w(3) @@ -716,7 +687,6 @@ def separating_element(self, others): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v2 = pAdicValuation(QQ, 2) sage: v3 = pAdicValuation(QQ, 3) sage: v5 = pAdicValuation(QQ, 5) @@ -789,7 +759,6 @@ def _strictly_separating_element(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v2 = pAdicValuation(QQ, 2) sage: v3 = pAdicValuation(QQ, 3) sage: v2._strictly_separating_element(v3) @@ -867,7 +836,6 @@ def _weakly_separating_element(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v2 = pAdicValuation(QQ, 2) sage: v3 = pAdicValuation(QQ, 3) sage: v2._weakly_separating_element(v3) @@ -893,7 +861,6 @@ def shift(self, x, s): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.shift(1, 10) 1024 @@ -945,7 +912,6 @@ def simplify(self, x, error=None, force=False): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.simplify(6, force=True) 2 @@ -968,7 +934,6 @@ def lower_bound(self, x): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.lower_bound(2^10) 10 @@ -985,7 +950,6 @@ def upper_bound(self, x): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.upper_bound(2^10) 10 @@ -1007,7 +971,6 @@ def _relative_size(self, x): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(Qp(2)) sage: v._relative_size(2) 1 @@ -1028,7 +991,6 @@ def _test_is_negative_pseudo_valuation(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: v._test_is_negative_pseudo_valuation() @@ -1052,7 +1014,6 @@ def _test_bounds(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: v._test_bounds() @@ -1070,7 +1031,6 @@ def _test_simplify(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: v._test_simplify() @@ -1114,7 +1074,6 @@ def _test_shift(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: v._test_shift() @@ -1144,7 +1103,6 @@ def _test_scale(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: v._test_scale() @@ -1181,7 +1139,6 @@ def _test_add(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 3) sage: v._test_add() @@ -1200,7 +1157,6 @@ def _test_infinite_zero(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_infinite_zero() @@ -1215,7 +1171,6 @@ def _test_mul(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_mul() @@ -1232,7 +1187,6 @@ def _test_no_infinite_units(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_no_infinite_units() @@ -1263,7 +1217,6 @@ def _test_value_group(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_value_group() @@ -1293,7 +1246,6 @@ def _test_value_semigroup(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_value_semigroup() @@ -1313,7 +1265,6 @@ def _test_element_with_valuation(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_element_with_valuation() @@ -1333,7 +1284,6 @@ def _test_residue_ring(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_residue_ring() @@ -1366,7 +1316,6 @@ def _test_reduce(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_reduce() @@ -1406,7 +1355,6 @@ def _test_lift(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_lift() @@ -1437,7 +1385,6 @@ def _test_restriction(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_restriction() @@ -1452,7 +1399,6 @@ def _test_extension(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_extension() @@ -1468,7 +1414,6 @@ def _test_change_domain(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_change_domain() @@ -1483,7 +1428,6 @@ def _test_no_infinite_nonzero(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_no_infinite_nonzero() @@ -1503,7 +1447,6 @@ def _test_residue_field(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_residue_field() @@ -1538,7 +1481,6 @@ def _test_ge(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_ge() @@ -1560,7 +1502,6 @@ def _test_le(self, **options): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v._test_le() @@ -1584,7 +1525,6 @@ class ScaleAction(Action): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: from operator import mul sage: v.parent().get_action(IntegerRing, mul, self_on_left=False) @@ -1596,7 +1536,6 @@ def _call_(self, s, v): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: 3*v # indirect doctest 3 * 5-adic valuation @@ -1611,7 +1550,6 @@ class InverseScaleAction(Action): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: from operator import div sage: v.parent().get_action(IntegerRing, div, self_on_left=True) @@ -1623,7 +1561,6 @@ def _call_(self, v, s): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(QQ, 5) sage: v/3 # indirect doctest 1/3 * 5-adic valuation diff --git a/src/sage/rings/valuation/value_group.py b/src/sage/rings/valuation/value_group.py index cdd7c280fb6..0ed992eb842 100644 --- a/src/sage/rings/valuation/value_group.py +++ b/src/sage/rings/valuation/value_group.py @@ -6,7 +6,6 @@ EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(ZZ, 2) sage: v.value_group() Additive Abelian Group generated by 1 @@ -38,7 +37,6 @@ class DiscreteValuationCodomain(UniqueRepresentation, Parent): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: C = DiscreteValuationCodomain(); C Codomain of Discrete Valuations @@ -51,7 +49,6 @@ def __init__(self): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: isinstance(pAdicValuation(QQ, 2).codomain(), DiscreteValuationCodomain) True @@ -71,7 +68,6 @@ def _element_constructor_(self, x): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValuationCodomain()(0) 0 sage: DiscreteValuationCodomain()(infinity) @@ -94,7 +90,6 @@ def _repr_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValuationCodomain() # indirect doctest Codomain of Discrete Valuations @@ -122,7 +117,6 @@ class DiscreteValueGroup(UniqueRepresentation, Parent): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: D1 = DiscreteValueGroup(0); D1 Trivial Additive Abelian Group sage: D2 = DiscreteValueGroup(4/3); D2 @@ -145,7 +139,6 @@ def __classcall__(cls, generator): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueGroup(1) is DiscreteValueGroup(-1) True @@ -158,7 +151,6 @@ def __init__(self, generator): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: isinstance(DiscreteValueGroup(0), DiscreteValueGroup) True @@ -181,7 +173,6 @@ def _element_constructor_(self, x): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueGroup(0)(0) 0 sage: DiscreteValueGroup(0)(1) @@ -208,7 +199,6 @@ def _repr_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueGroup(0) # indirect doctest Trivial Additive Abelian Group @@ -227,7 +217,6 @@ def __add__(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: D = DiscreteValueGroup(1/2) sage: D + 1/3 Additive Abelian Group generated by 1/6 @@ -259,7 +248,6 @@ def _mul_(self, other, switch_sides=False): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: D = DiscreteValueGroup(1/2) sage: 1/2 * D Additive Abelian Group generated by 1/4 @@ -282,7 +270,6 @@ def index(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueGroup(3/8).index(DiscreteValueGroup(3)) 8 sage: DiscreteValueGroup(3).index(DiscreteValueGroup(3/8)) @@ -318,7 +305,6 @@ def numerator(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueGroup(3/8).numerator() 3 @@ -331,7 +317,6 @@ def denominator(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueGroup(3/8).denominator() 8 @@ -344,7 +329,6 @@ def gen(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueGroup(-3/8).gen() 3/8 @@ -357,7 +341,6 @@ def some_elements(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueGroup(-3/8).some_elements() [3/8, -3/8, 0, 42, 3/2, -3/2, 9/8, -9/8] @@ -370,7 +353,6 @@ def is_trivial(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueGroup(-3/8).is_trivial() False sage: DiscreteValueGroup(0).is_trivial() @@ -390,7 +372,6 @@ class DiscreteValueSemigroup(UniqueRepresentation, Parent): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: D1 = DiscreteValueSemigroup(0); D1 Trivial Additive Abelian Semigroup sage: D2 = DiscreteValueSemigroup(4/3); D2 @@ -416,7 +397,6 @@ def __classcall__(cls, generators): generators and drop generators that are trivially contained in the semigroup generated by the remaining generators:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueSemigroup([1,2]) is DiscreteValueSemigroup([1]) True @@ -449,7 +429,6 @@ def __init__(self, generators): r""" TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: isinstance(DiscreteValueSemigroup([0]), DiscreteValueSemigroup) True @@ -477,7 +456,6 @@ def _solve_linear_program(self, target): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: D = DiscreteValueSemigroup([2,3,5]) sage: D._solve_linear_program(12) {0: 1, 1: 0, 2: 2} @@ -527,7 +505,6 @@ def _element_constructor_(self, x): TESTS:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueSemigroup([])(0) 0 sage: DiscreteValueSemigroup([])(1) @@ -554,7 +531,6 @@ def _repr_(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueSemigroup(0) # indirect doctest Trivial Additive Abelian Semigroup @@ -573,7 +549,6 @@ def __add__(self, other): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: D = DiscreteValueSemigroup(1/2) sage: D + 1/3 Additive Abelian Semigroup generated by 1/3, 1/2 @@ -605,7 +580,6 @@ def _mul_(self, other, switch_sides=False): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: D = DiscreteValueSemigroup(1/2) sage: 1/2 * D Additive Abelian Semigroup generated by 1/4 @@ -624,7 +598,6 @@ def gens(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueSemigroup(-3/8).gens() (-3/8,) @@ -637,7 +610,6 @@ def some_elements(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: list(DiscreteValueSemigroup([-3/8,1/2]).some_elements()) [0, -3/8, 1/2, ...] @@ -657,7 +629,6 @@ def is_trivial(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueSemigroup(-3/8).is_trivial() False sage: DiscreteValueSemigroup([]).is_trivial() @@ -673,7 +644,6 @@ def is_group(self): EXAMPLES:: - sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: DiscreteValueSemigroup(1).is_group() False sage: D = DiscreteValueSemigroup([-1, 1]) From 4153ef9b3a920d079437825960d904205fc2ae53 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Wed, 19 Jul 2017 07:22:45 +0000 Subject: [PATCH 252/740] Wired up valuations in the Sage library and fixed very many import errors --- src/sage/rings/all.py | 3 + .../rings/function_field/function_field.py | 117 ++++++- .../function_field_valuation.py | 314 +++++++----------- src/sage/rings/integer_ring.pyx | 15 + src/sage/rings/number_field/number_field.py | 13 + src/sage/rings/number_field/order.py | 12 + src/sage/rings/padics/all.py | 1 - src/sage/rings/padics/discrete_value_group.py | 215 ------------ src/sage/rings/padics/padic_generic.py | 32 ++ src/sage/rings/padics/padic_valuation.py | 187 ++++++----- src/sage/rings/rational_field.py | 15 + src/sage/rings/valuation/__init__.py | 0 src/sage/rings/valuation/all.py | 4 + .../rings/valuation/augmented_valuation.py | 92 ++--- .../rings/valuation/developing_valuation.py | 11 +- src/sage/rings/valuation/gauss_valuation.py | 48 +-- .../rings/valuation/inductive_valuation.py | 102 +++--- src/sage/rings/valuation/limit_valuation.py | 99 +++--- src/sage/rings/valuation/mapped_valuation.py | 59 ++-- src/sage/rings/valuation/scaled_valuation.py | 45 +-- src/sage/rings/valuation/trivial_valuation.py | 71 ++-- src/sage/rings/valuation/valuation.py | 161 +++++---- src/sage/rings/valuation/valuation_space.py | 161 ++++----- .../rings/valuation/valuations_catalog.py | 6 + src/sage/rings/valuation/value_group.py | 35 +- 25 files changed, 916 insertions(+), 902 deletions(-) delete mode 100644 src/sage/rings/padics/discrete_value_group.py create mode 100644 src/sage/rings/valuation/__init__.py create mode 100644 src/sage/rings/valuation/all.py create mode 100644 src/sage/rings/valuation/valuations_catalog.py diff --git a/src/sage/rings/all.py b/src/sage/rings/all.py index f965a3b09c3..b170d583848 100644 --- a/src/sage/rings/all.py +++ b/src/sage/rings/all.py @@ -63,6 +63,9 @@ from .padics.all import * from .padics.padic_printing import _printer_defaults as padic_printing +# valuations +from .valuation.all import * + # Semirings from .semirings.all import * diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index f9dcb2e4c46..6b3c948a5cc 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -77,7 +77,7 @@ #***************************************************************************** # Copyright (C) 2010 William Stein # Copyright (C) 2010 Robert Bradshaw -# Copyright (C) 2011-2016 Julian Rueth +# Copyright (C) 2011-2017 Julian Rueth # Copyright (C) 2011 Maarten Derickx # # Distributed under the terms of the GNU General Public License (GPL) @@ -537,6 +537,121 @@ def rational_function_field(self): """ return self if is_RationalFunctionField(self) else self.base_field().rational_function_field() + def valuation(self, prime): + r""" + Return the discrete valuation on this function field defined by + ``prime``. + + INPUT: + + - ``prime`` -- a place of the function field, a valuation on a subring, + or a valuation on another function field together with information + for isomorphisms to and from that function field + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + + We create a valuation that correspond to a finite rational place of a function + field:: + + sage: v = K.valuation(1); v + (x - 1)-adic valuation + sage: v(x) + 0 + sage: v(x - 1) + 1 + + A place can also be specified with an irreducible polynomial:: + + sage: v = K.valuation(x - 1); v + (x - 1)-adic valuation + + Similarly, for a finite non-rational place:: + + sage: v = K.valuation(x^2 + 1); v + (x^2 + 1)-adic valuation + sage: v(x^2 + 1) + 1 + sage: v(x) + 0 + + Or for the infinite place:: + + sage: v = K.valuation(1/x); v + Valuation at the infinite place + sage: v(x) + -1 + + Instead of specifying a generator of a place, we can define a valuation on a + rational function field by giving a discrete valuation on the underlying + polynomial ring:: + + sage: R. = QQ[] + sage: w = GaussValuation(R, TrivialValuation(QQ)).augmentation(x - 1, 1) + sage: v = K.valuation(w); v + (x - 1)-adic valuation + + Note that this allows us to specify valuations which do not correspond to a + place of the function field:: + + sage: w = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = K.valuation(w); v + 2-adic valuation + + The same is possible for valuations with `v(1/x) > 0` by passing in an + extra pair of parameters, an isomorphism between this function field and an + isomorphic function field. That way you can, for example, indicate that the + valuation is to be understood as a valuation on `K[1/x]`, i.e., after + applying the substitution `x \mapsto 1/x` (here, the inverse map is also `x + \mapsto 1/x`):: + + sage: w = GaussValuation(R, pAdicValuation(QQ, 2)).augmentation(x, 1) + sage: w = K.valuation(w) + sage: v = K.valuation((w, K.hom([~K.gen()]), K.hom([~K.gen()]))); v + Valuation on rational function field induced by [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ] (in Rational function field in x over Rational Field after x |--> 1/x) + + Note that classical valuations at finite places or the infinite place are + always normalized such that the uniformizing element has valuation 1:: + + sage: K. = FunctionField(GF(3)) + sage: M. = FunctionField(K) + sage: v = M.valuation(x^3 - t) + sage: v(x^3 - t) + 1 + + However, if such a valuation comes out of a base change of the ground + field, this is not the case anymore. In the example below, the unique + extension of ``v`` to ``L`` still has valuation 1 on ``x^3 - t`` but it has + valuation ``1/3`` on its uniformizing element ``x - w``:: + + sage: R. = K[] + sage: L. = K.extension(w^3 - t) + sage: N. = FunctionField(L) + sage: w = v.extension(N) # optional: integrated + sage: w(x^3 - t) # optional: integrated + 1 + sage: w(x - w) # optional: integrated + 1/3 + + There are several ways to create valuations on extensions of rational + function fields:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x); L + Function field in y defined by y^2 - x + + A place that has a unique extension can just be defined downstairs:: + + sage: v = L.valuation(x); v + (x)-adic valuation + + """ + from sage.rings.function_field.function_field_valuation import FunctionFieldValuation + return FunctionFieldValuation(self, prime) + + class FunctionField_polymod(FunctionField): """ A function field defined by a univariate polynomial, as an diff --git a/src/sage/rings/function_field/function_field_valuation.py b/src/sage/rings/function_field/function_field_valuation.py index 859d0c44308..a53234862bb 100644 --- a/src/sage/rings/function_field/function_field_valuation.py +++ b/src/sage/rings/function_field/function_field_valuation.py @@ -8,25 +8,25 @@ places on a rational function field:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, 1); v + sage: v = K.valuation(1); v (x - 1)-adic valuation - sage: v = FunctionFieldValuation(K, x^2 + 1); v + sage: v = K.valuation(x^2 + 1); v (x^2 + 1)-adic valuation - sage: v = FunctionFieldValuation(K, 1/x); v + sage: v = K.valuation(1/x); v Valuation at the infinite place Note that we can also specify valuations which do not correspond to a place of the function field:: sage: R. = QQ[] - sage: w = GaussValuation(R, pAdicValuation(QQ, 2)) - sage: v = FunctionFieldValuation(K, w); v + sage: w = valuations.GaussValuation(R, QQ.valuation(2)) + sage: v = K.valuation(w); v 2-adic valuation Valuations on a rational function field can then be extended to finite extensions:: - sage: v = FunctionFieldValuation(K, x - 1); v + sage: v = K.valuation(x - 1); v (x - 1)-adic valuation sage: R. = K[] sage: L. = K.extension(y^2 - x) @@ -39,19 +39,19 @@ Run test suite for classical places over rational function fields:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, 1) + sage: v = K.valuation(1) sage: TestSuite(v).run(max_runs=100) # long time - sage: v = FunctionFieldValuation(K, x^2 + 1) + sage: v = K.valuation(x^2 + 1) sage: TestSuite(v).run(max_runs=100) # long time - sage: v = FunctionFieldValuation(K, 1/x) + sage: v = K.valuation(1/x) sage: TestSuite(v).run(max_runs=100) # long time Run test suite over classical places of finite extensions:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, x - 1) + sage: v = K.valuation(x - 1) sage: R. = K[] sage: L. = K.extension(y^2 - x) sage: ws = v.extensions(L) @@ -61,21 +61,21 @@ sage: K. = FunctionField(QQ) sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) - sage: w = FunctionFieldValuation(K, v) + sage: v = GaussValuation(R, QQ.valuation(2)) + sage: w = K.valuation(v) sage: TestSuite(w).run() # long time Run test suite for some other classical places over large ground fields:: sage: K. = FunctionField(GF(3)) sage: M. = FunctionField(K) - sage: v = FunctionFieldValuation(M, x^3 - t) + sage: v = M.valuation(x^3 - t) sage: TestSuite(v).run(max_runs=10) # long time Run test suite for extensions over the infinite place:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, 1/x) + sage: v = K.valuation(1/x) sage: R. = K[] sage: L. = K.extension(y^2 - 1/(x^2 + 1)) sage: w = v.extensions(L) @@ -86,15 +86,15 @@ sage: K. = FunctionField(QQ) sage: R. = QQ[] - sage: w = GaussValuation(R, pAdicValuation(QQ, 2)).augmentation(x, 1) - sage: w = FunctionFieldValuation(K, w) - sage: v = FunctionFieldValuation(K, (w, K.hom([~K.gen()]), K.hom([~K.gen()]))) + sage: w = GaussValuation(R, QQ.valuation(2)).augmentation(x, 1) + sage: w = K.valuation(w) + sage: v = K.valuation((w, K.hom([~K.gen()]), K.hom([~K.gen()]))) sage: TestSuite(v).run() # long time Run test suite for extensions which come from the splitting in the base field:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, x^2 + 1) + sage: v = K.valuation(x^2 + 1) sage: L. = FunctionField(GaussianIntegers().fraction_field()) sage: ws = v.extensions(L) sage: for w in ws: TestSuite(w).run(max_runs=100) # long time @@ -103,7 +103,7 @@ sage: K. = FunctionField(GF(3)) sage: L. = FunctionField(K) - sage: v = FunctionFieldValuation(L, x^6 - t) + sage: v = L.valuation(x^6 - t) sage: TestSuite(v).run(max_runs=10) # long time Run test suite for a valuation which is backed by limit valuation:: @@ -111,16 +111,16 @@ sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x^2 + x + 1)) - sage: v = FunctionFieldValuation(K, x - 1) + sage: v = K.valuation(x - 1) sage: w = v.extension(L) sage: TestSuite(w).run() # long time Run test suite for a valuation which sends an element to `-\infty`:: sage: R. = QQ[] - sage: v = GaussValuation(QQ['x'], pAdicValuation(QQ, 2)).augmentation(x, infinity) + sage: v = GaussValuation(QQ['x'], QQ.valuation(2)).augmentation(x, infinity) sage: K. = FunctionField(QQ) - sage: w = FunctionFieldValuation(K, v) + sage: w = K.valuation(v) sage: TestSuite(w).run() # long time AUTHORS: @@ -129,7 +129,7 @@ """ #***************************************************************************** -# Copyright (C) 2016 Julian Rüth +# Copyright (C) 2016-2017 Julian Rüth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -140,9 +140,9 @@ from sage.rings.all import QQ, ZZ, infinity from sage.misc.abstract_method import abstract_method -from valuation import DiscreteValuation, DiscretePseudoValuation, InfiniteDiscretePseudoValuation, NegativeInfiniteDiscretePseudoValuation -from trivial_valuation import TrivialValuation -from mapped_valuation import FiniteExtensionFromLimitValuation, MappedValuation_base +from sage.rings.valuation.valuation import DiscreteValuation, DiscretePseudoValuation, InfiniteDiscretePseudoValuation, NegativeInfiniteDiscretePseudoValuation +from sage.rings.valuation.trivial_valuation import TrivialValuation +from sage.rings.valuation.mapped_valuation import FiniteExtensionFromLimitValuation, MappedValuation_base class FunctionFieldValuationFactory(UniqueFactory): r""" @@ -159,101 +159,14 @@ class FunctionFieldValuationFactory(UniqueFactory): EXAMPLES:: sage: K. = FunctionField(QQ) - - We create a valuation that correspond to a finite rational place of a function - field:: - - sage: v = FunctionFieldValuation(K, 1); v + sage: v = K.valuation(1); v # indirect doctest (x - 1)-adic valuation sage: v(x) 0 sage: v(x - 1) 1 - A place can also be specified with an irreducible polynomial:: - - sage: v = FunctionFieldValuation(K, x - 1); v - (x - 1)-adic valuation - - Similarly, for a finite non-rational place:: - - sage: v = FunctionFieldValuation(K, x^2 + 1); v - (x^2 + 1)-adic valuation - sage: v(x^2 + 1) - 1 - sage: v(x) - 0 - - Or for the infinite place:: - - sage: v = FunctionFieldValuation(K, 1/x); v - Valuation at the infinite place - sage: v(x) - -1 - - Instead of specifying a generator of a place, we can define a valuation on a - rational function field by giving a discrete valuation on the underlying - polynomial ring:: - - sage: R. = QQ[] - sage: w = GaussValuation(R, TrivialValuation(QQ)).augmentation(x - 1, 1) - sage: v = FunctionFieldValuation(K, w); v - (x - 1)-adic valuation - - Note that this allows us to specify valuations which do not correspond to a - place of the function field:: - - sage: w = GaussValuation(R, pAdicValuation(QQ, 2)) - sage: v = FunctionFieldValuation(K, w); v - 2-adic valuation - - The same is possible for valuations with `v(1/x) > 0` by passing in an - extra pair of parameters, an isomorphism between this function field and an - isomorphic function field. That way you can, for example, indicate that the - valuation is to be understood as a valuation on `K[1/x]`, i.e., after - applying the substitution `x \mapsto 1/x` (here, the inverse map is also `x - \mapsto 1/x`):: - - sage: w = GaussValuation(R, pAdicValuation(QQ, 2)).augmentation(x, 1) - sage: w = FunctionFieldValuation(K, w) - sage: v = FunctionFieldValuation(K, (w, K.hom([~K.gen()]), K.hom([~K.gen()]))); v - Valuation on rational function field induced by [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ] (in Rational function field in x over Rational Field after x |--> 1/x) - - Note that classical valuations at finite places or the infinite place are - always normalized such that the uniformizing element has valuation 1:: - - sage: K. = FunctionField(GF(3)) - sage: M. = FunctionField(K) - sage: v = FunctionFieldValuation(M, x^3 - t) - sage: v(x^3 - t) - 1 - - However, if such a valuation comes out of a base change of the ground - field, this is not the case anymore. In the example below, the unique - extension of ``v`` to ``L`` still has valuation 1 on ``x^3 - t`` but it has - valuation ``1/3`` on its uniformizing element ``x - w``:: - - sage: R. = K[] - sage: L. = K.extension(w^3 - t) - sage: N. = FunctionField(L) - sage: w = v.extension(N) # optional: integrated - sage: w(x^3 - t) # optional: integrated - 1 - sage: w(x - w) # optional: integrated - 1/3 - - There are several ways to create valuations on extensions of rational - function fields:: - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x); L - Function field in y defined by y^2 - x - - A place that has a unique extension can just be defined downstairs:: - - sage: v = FunctionFieldValuation(L, x); v - (x)-adic valuation + See :meth:`function_field.FunctionField.valuation` for further examples. """ def create_key_and_extra_args(self, domain, prime): @@ -267,19 +180,19 @@ def create_key_and_extra_args(self, domain, prime): get the same object:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, x - 1) # indirect doctest + sage: v = K.valuation(x - 1) # indirect doctest sage: R. = QQ[] - sage: w = GaussValuation(R, TrivialValuation(QQ)).augmentation(x - 1, 1) - sage: FunctionFieldValuation(K, w) is v + sage: w = GaussValuation(R, valuations.TrivialValuation(QQ)).augmentation(x - 1, 1) + sage: K.valuation(w) is v True The normalization is, however, not smart enough, to unwrap substitutions that turn out to be trivial:: - sage: w = GaussValuation(R, pAdicValuation(QQ, 2)) - sage: w = FunctionFieldValuation(K, w) - sage: w is FunctionFieldValuation(K, (w, K.hom([~K.gen()]), K.hom([~K.gen()]))) + sage: w = GaussValuation(R, QQ.valuation(2)) + sage: w = K.valuation(w) + sage: w is K.valuation((w, K.hom([~K.gen()]), K.hom([~K.gen()]))) False """ @@ -314,7 +227,7 @@ def create_key_and_extra_args(self, domain, prime): if domain.base_field() is not domain: # prime might define a valuation on a subring of domain and have a # unique extension to domain - base_valuation = FunctionFieldValuation(domain.base_field(), prime) + base_valuation = domain.base_field().valuation(prime) return self.create_key_and_extra_args_from_valuation(domain, base_valuation) raise NotImplementedError("argument must be a place or a pseudo-valuation on a supported subring but %r does not satisfy this for the domain %r"%(prime, domain)) @@ -327,7 +240,7 @@ def create_key_and_extra_args_from_place(self, domain, generator): TESTS: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, 1/x) # indirect doctest + sage: v = K.valuation(1/x) # indirect doctest """ if generator not in domain.base_field(): @@ -336,7 +249,7 @@ def create_key_and_extra_args_from_place(self, domain, generator): if domain.base_field() is not domain: # if this is an extension field, construct the unique place over # the place on the subfield - return self.create_key_and_extra_args(domain, FunctionFieldValuation(domain.base_field(), generator)) + return self.create_key_and_extra_args(domain, domain.base_field().valuation(generator)) if generator in domain.constant_base_field(): # generator is a constant, we associate to it the place which @@ -357,7 +270,7 @@ def create_key_and_extra_args_from_place(self, domain, generator): return self.create_key_and_extra_args(domain, valuation) elif generator == ~domain.gen(): # generator is 1/x, the infinite place - return (domain, (FunctionFieldValuation(domain, domain.gen()), domain.hom(~domain.gen()), domain.hom(~domain.gen()))), {} + return (domain, (domain.valuation(domain.gen()), domain.hom(~domain.gen()), domain.hom(~domain.gen()))), {} else: raise ValueError("a place must be given by an irreducible polynomial or the inverse of the generator; %r does not define a place over %r"%(generator, domain)) @@ -370,8 +283,8 @@ def create_key_and_extra_args_from_valuation(self, domain, valuation): sage: K. = FunctionField(QQ) sage: R. = QQ[] - sage: w = GaussValuation(R, TrivialValuation(QQ)).augmentation(x - 1, 1) - sage: v = FunctionFieldValuation(K, w) # indirect doctest + sage: w = GaussValuation(R, valuations.TrivialValuation(QQ)).augmentation(x - 1, 1) + sage: v = K.valuation(w) # indirect doctest """ # this should have been handled by create_key already @@ -415,7 +328,7 @@ def create_key_and_extra_args_from_valuation_on_isomorphic_field(self, domain, v sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) + sage: v = K.valuation(1/x) sage: w = v.extension(L) # indirect doctest """ @@ -465,8 +378,8 @@ def create_object(self, version, key, **extra_args): sage: K. = FunctionField(QQ) sage: R. = QQ[] - sage: w = GaussValuation(R, pAdicValuation(QQ, 2)) - sage: v = FunctionFieldValuation(K, w); v # indirect doctest + sage: w = valuations.GaussValuation(R, QQ.valuation(2)) + sage: v = K.valuation(w); v # indirect doctest 2-adic valuation """ @@ -478,7 +391,7 @@ def create_object(self, version, key, **extra_args): valuation, to_valuation_domain, from_valuation_domain = valuation if domain is domain.base() and valuation.domain() is valuation.domain().base() and to_valuation_domain == domain.hom([~valuation.domain().gen()]) and from_valuation_domain == valuation.domain().hom([~domain.gen()]): # valuation on the rational function field after x |--> 1/x - if valuation == FunctionFieldValuation(valuation.domain(), valuation.domain().gen()): + if valuation == valuation.domain().valuation(valuation.domain().gen()): # the classical valuation at the place 1/x return parent.__make_element_class__(InfiniteRationalFunctionFieldValuation)(parent) @@ -526,7 +439,8 @@ class FunctionFieldValuation_base(DiscretePseudoValuation): TESTS:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, x) # indirect doctest + sage: v = K.valuation(x) # indirect doctest + sage: from sage.rings.function_field.function_field_valuation import FunctionFieldValuation_base sage: isinstance(v, FunctionFieldValuation_base) True @@ -540,7 +454,8 @@ class DiscreteFunctionFieldValuation_base(DiscreteValuation): TESTS:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, x) # indirect doctest + sage: v = K.valuation(x) # indirect doctest + sage: from sage.rings.function_field.function_field_valuation import DiscreteFunctionFieldValuation_base sage: isinstance(v, DiscreteFunctionFieldValuation_base) True @@ -552,7 +467,7 @@ def extensions(self, L): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, x) + sage: v = K.valuation(x) sage: R. = K[] sage: L. = K.extension(y^2 - x) sage: v.extensions(L) @@ -562,7 +477,7 @@ def extensions(self, L): Valuations over the infinite place:: - sage: v = FunctionFieldValuation(K, 1/x) + sage: v = K.valuation(1/x) sage: R. = K[] sage: L. = K.extension(y^2 - 1/(x^2 + 1)) sage: sorted(v.extensions(L), key=str) @@ -574,7 +489,7 @@ def extensions(self, L): sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) + sage: v = K.valuation(1/x) sage: w = v.extension(L) sage: R. = L[] sage: M. = L.extension(z^2 - y) @@ -597,7 +512,7 @@ def extensions(self, L): G = G / G.leading_coefficient() if any(self(c) < 0 for c in G.coefficients()): # rewrite L = K[u]/(H) with H integral and compute the extensions - from gauss_valuation import GaussValuation + from sage.rings.valuation.gauss_valuation import GaussValuation g = GaussValuation(G.parent(), self) y_to_u, u_to_y, H = g.monic_integral_model(G) M = K.extension(H, names=L.variable_names()) @@ -605,9 +520,9 @@ def extensions(self, L): from sage.rings.morphism import RingHomomorphism_im_gens if type(y_to_u) == RingHomomorphism_im_gens and type(u_to_y) == RingHomomorphism_im_gens: - return [FunctionFieldValuation(L, (w, L.hom([M(y_to_u(y_to_u.domain().gen()))]), M.hom([L(u_to_y(u_to_y.domain().gen()))]))) for w in H_extensions] + return [L.valuation((w, L.hom([M(y_to_u(y_to_u.domain().gen()))]), M.hom([L(u_to_y(u_to_y.domain().gen()))]))) for w in H_extensions] raise NotImplementedError - return [FunctionFieldValuation(L, w) for w in self.mac_lane_approximants(L.polynomial())] + return [L.valuation(w) for w in self.mac_lane_approximants(L.polynomial())] elif L.base() is not L and K.is_subring(L): # recursively call this method for the tower of fields from operator import add @@ -625,7 +540,8 @@ class RationalFunctionFieldValuation_base(FunctionFieldValuation_base): TESTS:: sage: K. = FunctionField(GF(2)) - sage: v = FunctionFieldValuation(K, x) # indirect doctest + sage: v = K.valuation(x) # indirect doctest + sage: from sage.rings.function_field.function_field_valuation import RationalFunctionFieldValuation_base sage: isinstance(v, RationalFunctionFieldValuation_base) True @@ -640,7 +556,8 @@ class ClassicalFunctionFieldValuation_base(DiscreteFunctionFieldValuation_base): TESTS:: sage: K. = FunctionField(GF(5)) - sage: v = FunctionFieldValuation(K, x) # indirect doctest + sage: v = K.valuation(x) # indirect doctest + sage: from sage.rings.function_field.function_field_valuation import ClassicalFunctionFieldValuation_base sage: isinstance(v, ClassicalFunctionFieldValuation_base) True @@ -653,7 +570,7 @@ def _test_classical_residue_field(self, **options): TESTS:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, x^2 + 1) + sage: v = K.valuation(x^2 + 1) sage: v._test_classical_residue_field() """ @@ -668,8 +585,8 @@ def _ge_(self, other): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, x^2 + 1) - sage: w = FunctionFieldValuation(K, x) + sage: v = K.valuation(x^2 + 1) + sage: w = K.valuation(x) sage: v >= w False sage: w >= v @@ -691,7 +608,7 @@ class InducedFunctionFieldValuation_base(FunctionFieldValuation_base): TESTS:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, x^2 + 1) # indirect doctest + sage: v = K.valuation(x^2 + 1) # indirect doctest """ def __init__(self, parent, base_valuation): @@ -699,7 +616,8 @@ def __init__(self, parent, base_valuation): TESTS:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, x) # indirect doctest + sage: v = K.valuation(x) # indirect doctest + sage: from sage.rings.function_field.function_field_valuation import InducedFunctionFieldValuation_base sage: isinstance(v, InducedFunctionFieldValuation_base) True @@ -719,7 +637,7 @@ def uniformizer(self): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: FunctionFieldValuation(K, x).uniformizer() + sage: K.valuation(x).uniformizer() x """ @@ -733,7 +651,7 @@ def lift(self, F): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, x) + sage: v = K.valuation(x) sage: v.lift(0) 0 sage: v.lift(1) @@ -759,7 +677,7 @@ def value_group(self): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: FunctionFieldValuation(K, x).value_group() + sage: K.valuation(x).value_group() Additive Abelian Group generated by 1 """ @@ -772,7 +690,7 @@ def reduce(self, f): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, x^2 + 1) + sage: v = K.valuation(x^2 + 1) sage: v.reduce(x) u1 @@ -804,7 +722,7 @@ def _repr_(self): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: FunctionFieldValuation(K, x^2 + 1) # indirect doctest + sage: K.valuation(x^2 + 1) # indirect doctest (x^2 + 1)-adic valuation """ @@ -827,7 +745,7 @@ def extensions(self, L): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, x^2 + 1) + sage: v = K.valuation(x^2 + 1) sage: L. = FunctionField(GaussianIntegers().fraction_field()) sage: v.extensions(L) # indirect doctest [(x - I)-adic valuation, (x + I)-adic valuation] @@ -850,7 +768,7 @@ def extensions(self, L): # We extend the underlying valuation on the polynomial ring W = self._base_valuation.extensions(L._ring) - return [FunctionFieldValuation(L, w) for w in W] + return [L.valuation(w) for w in W] return super(InducedFunctionFieldValuation_base, self).extensions(L) @@ -861,7 +779,7 @@ def _call_(self, f): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, x) # indirect doctest + sage: v = K.valuation(x) # indirect doctest sage: v((x+1)/x^2) -2 @@ -875,18 +793,18 @@ def residue_ring(self): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: FunctionFieldValuation(K, x).residue_ring() + sage: K.valuation(x).residue_ring() Rational Field sage: K. = FunctionField(QQ) - sage: v = GaussValuation(QQ['x'], pAdicValuation(QQ, 2)) - sage: w = FunctionFieldValuation(K, v) + sage: v = valuations.GaussValuation(QQ['x'], QQ.valuation(2)) + sage: w = K.valuation(v) sage: w.residue_ring() Fraction Field of Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) sage: R. = QQ[] sage: vv = v.augmentation(x, 1) - sage: w = FunctionFieldValuation(K, vv) + sage: w = K.valuation(vv) sage: w.residue_ring() Fraction Field of Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) @@ -901,24 +819,24 @@ class FiniteRationalFunctionFieldValuation(InducedFunctionFieldValuation_base, C EXAMPLES:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, x + 1); v # indirect doctest + sage: v = K.valuation(x + 1); v # indirect doctest (x + 1)-adic valuation A finite place with residual degree:: - sage: w = FunctionFieldValuation(K, x^2 + 1); w + sage: w = K.valuation(x^2 + 1); w (x^2 + 1)-adic valuation A finite place with ramification:: sage: K. = FunctionField(GF(3)) sage: L. = FunctionField(K) - sage: u = FunctionFieldValuation(L, x^3 - t); u + sage: u = L.valuation(x^3 - t); u (x^3 + 2*t)-adic valuation A finite place with residual degree and ramification:: - sage: q = FunctionFieldValuation(L, x^6 - t); q + sage: q = L.valuation(x^6 - t); q (x^6 + 2*t)-adic valuation """ @@ -927,7 +845,8 @@ def __init__(self, parent, base_valuation): TESTS:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, x + 1) + sage: v = K.valuation(x + 1) + sage: from sage.rings.function_field.function_field_valuation import FiniteRationalFunctionFieldValuation sage: isinstance(v, FiniteRationalFunctionFieldValuation) True @@ -945,8 +864,8 @@ class NonClassicalRationalFunctionFieldValuation(InducedFunctionFieldValuation_b EXAMPLES:: sage: K. = FunctionField(QQ) - sage: v = GaussValuation(QQ['x'], pAdicValuation(QQ, 2)) - sage: w = FunctionFieldValuation(K, v); w # indirect doctest + sage: v = GaussValuation(QQ['x'], QQ.valuation(2)) + sage: w = K.valuation(v); w # indirect doctest 2-adic valuation """ @@ -959,9 +878,10 @@ def __init__(self, parent, base_valuation): elments to `-\infty`, they are not supported yet:: sage: R. = QQ[] - sage: v = GaussValuation(QQ['x'], pAdicValuation(QQ, 2)).augmentation(x, infinity) + sage: v = GaussValuation(QQ['x'], QQ.valuation(2)).augmentation(x, infinity) sage: K. = FunctionField(QQ) - sage: w = FunctionFieldValuation(K, v) + sage: w = K.valuation(v) + sage: from sage.rings.function_field.function_field_valuation import NonClassicalRationalFunctionFieldValuation sage: isinstance(w, NonClassicalRationalFunctionFieldValuation) True @@ -980,15 +900,10 @@ class FunctionFieldFromLimitValuation(FiniteExtensionFromLimitValuation, Discret sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x^2 + x + 1)) - sage: v = FunctionFieldValuation(K, x - 1) # indirect doctest + sage: v = K.valuation(x - 1) # indirect doctest sage: w = v.extension(L); w (x - 1)-adic valuation - TESTS:: - - sage: isinstance(w, FunctionFieldFromLimitValuation) - True - """ def __init__(self, parent, approximant, G, approximants): r""" @@ -997,8 +912,9 @@ def __init__(self, parent, approximant, G, approximants): sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x^2 + x + 1)) - sage: v = FunctionFieldValuation(K, x - 1) # indirect doctest + sage: v = K.valuation(x - 1) # indirect doctest sage: w = v.extension(L) + sage: from sage.rings.function_field.function_field_valuation import FunctionFieldFromLimitValuation sage: isinstance(w, FunctionFieldFromLimitValuation) True @@ -1015,7 +931,7 @@ def _to_base_domain(self, f): sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x^2 + x + 1)) - sage: v = FunctionFieldValuation(K, x - 1) # indirect doctest + sage: v = K.valuation(x - 1) # indirect doctest sage: w = v.extension(L) sage: w._to_base_domain(y).parent() Univariate Polynomial Ring in y over Rational function field in x over Rational Field @@ -1032,14 +948,14 @@ def scale(self, scalar): sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - (x^2 + x + 1)) - sage: v = FunctionFieldValuation(K, x - 1) # indirect doctest + sage: v = K.valuation(x - 1) # indirect doctest sage: w = v.extension(L) sage: 3*w 3 * (x - 1)-adic valuation """ if scalar in QQ and scalar > 0 and scalar != 1: - return FunctionFieldValuation(self.domain(), self._base_valuation._initial_approximation.scale(scalar)) + return self.domain().valuation(self._base_valuation._initial_approximation.scale(scalar)) return super(FunctionFieldFromLimitValuation, self).scale(scalar) @@ -1051,7 +967,7 @@ class FunctionFieldMappedValuation_base(FunctionFieldValuation_base, MappedValua EXAMPLES:: sage: K. = FunctionField(GF(2)) - sage: v = FunctionFieldValuation(K, 1/x); v + sage: v = K.valuation(1/x); v Valuation at the infinite place """ @@ -1060,7 +976,8 @@ def __init__(self, parent, base_valuation, to_base_valuation_domain, from_base_v TESTS:: sage: K. = FunctionField(GF(2)) - sage: v = FunctionFieldValuation(K, 1/x) + sage: v = K.valuation(1/x) + sage: from sage.rings.function_field.function_field_valuation import FunctionFieldMappedValuation_base sage: isinstance(v, FunctionFieldMappedValuation_base) True @@ -1080,7 +997,7 @@ def _to_base_domain(self, f): sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) + sage: v = K.valuation(1/x) sage: w = v.extension(L) sage: w._to_base_domain(y) x^2*y @@ -1097,7 +1014,7 @@ def _from_base_domain(self, f): sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) + sage: v = K.valuation(1/x) sage: w = v.extension(L) sage: w._from_base_domain(w._to_base_domain(y)) y @@ -1114,7 +1031,7 @@ def scale(self, scalar): sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) + sage: v = K.valuation(1/x) sage: w = v.extension(L) sage: 3*w 3 * (x)-adic valuation (in Rational function field in x over Finite Field of size 2 after x |--> 1/x) @@ -1122,7 +1039,7 @@ def scale(self, scalar): """ from sage.rings.all import QQ if scalar in QQ and scalar > 0 and scalar != 1: - return FunctionFieldValuation(self.domain(), (self._base_valuation.scale(scalar), self._to_base, self._from_base)) + return self.domain().valuation((self._base_valuation.scale(scalar), self._to_base, self._from_base)) return super(FunctionFieldMappedValuation_base, self).scale(scalar) def _repr_(self): @@ -1134,7 +1051,7 @@ def _repr_(self): sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) + sage: v = K.valuation(1/x) sage: v.extension(L) # indirect doctest Valuation at the infinite place @@ -1154,9 +1071,9 @@ class RationalFunctionFieldMappedValuation(FunctionFieldMappedValuation_base, Ra sage: K. = FunctionField(QQ) sage: R. = QQ[] - sage: w = GaussValuation(R, pAdicValuation(QQ, 2)).augmentation(x, 1) - sage: w = FunctionFieldValuation(K, w) - sage: v = FunctionFieldValuation(K, (w, K.hom([~K.gen()]), K.hom([~K.gen()]))); v + sage: w = GaussValuation(R, QQ.valuation(2)).augmentation(x, 1) + sage: w = K.valuation(w) + sage: v = K.valuation((w, K.hom([~K.gen()]), K.hom([~K.gen()]))); v Valuation on rational function field induced by [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ] (in Rational function field in x over Rational Field after x |--> 1/x) """ @@ -1166,9 +1083,10 @@ def __init__(self, parent, base_valuation, to_base_valuation_doain, from_base_va sage: K. = FunctionField(QQ) sage: R. = QQ[] - sage: w = GaussValuation(R, pAdicValuation(QQ, 2)).augmentation(x, 1) - sage: w = FunctionFieldValuation(K, w) - sage: v = FunctionFieldValuation(K, (w, K.hom([~K.gen()]), K.hom([~K.gen()]))) + sage: w = GaussValuation(R, QQ.valuation(2)).augmentation(x, 1) + sage: w = K.valuation(w) + sage: v = K.valuation((w, K.hom([~K.gen()]), K.hom([~K.gen()]))) + sage: from sage.rings.function_field.function_field_valuation import RationalFunctionFieldMappedValuation sage: isinstance(v, RationalFunctionFieldMappedValuation) True @@ -1184,7 +1102,7 @@ class InfiniteRationalFunctionFieldValuation(FunctionFieldMappedValuation_base, EXAMPLES:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, 1/x) # indirect doctest + sage: v = K.valuation(1/x) # indirect doctest """ def __init__(self, parent): @@ -1192,7 +1110,8 @@ def __init__(self, parent): TESTS:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, 1/x) # indirect doctest + sage: v = K.valuation(1/x) # indirect doctest + sage: from sage.rings.function_field.function_field_valuation import InfiniteRationalFunctionFieldValuation sage: isinstance(v, InfiniteRationalFunctionFieldValuation) True @@ -1209,7 +1128,7 @@ def _repr_(self): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: FunctionFieldValuation(K, 1/x) # indirect doctest + sage: K.valuation(1/x) # indirect doctest Valuation at the infinite place """ @@ -1229,7 +1148,7 @@ class FunctionFieldExtensionMappedValuation(FunctionFieldMappedValuation_base): sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) + sage: v = K.valuation(1/x) sage: w = v.extension(L) sage: w(x) @@ -1241,6 +1160,7 @@ class FunctionFieldExtensionMappedValuation(FunctionFieldMappedValuation_base): TESTS:: + sage: from sage.rings.function_field.function_field_valuation import FunctionFieldExtensionMappedValuation sage: isinstance(w, FunctionFieldExtensionMappedValuation) True @@ -1254,14 +1174,14 @@ def _repr_(self): sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) + sage: v = K.valuation(1/x) sage: w = v.extension(L); w Valuation at the infinite place sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - 1/x^2 - 1) - sage: v = FunctionFieldValuation(K, 1/x) + sage: v = K.valuation(1/x) sage: w = v.extensions(L); w [[ Valuation at the infinite place, v(y - 1) = 2 ]-adic valuation, [ Valuation at the infinite place, v(y + 1) = 2 ]-adic valuation] @@ -1281,7 +1201,7 @@ def restriction(self, ring): sage: K. = FunctionField(GF(2)) sage: R. = K[] sage: L. = K.extension(y^2 + y + x^3) - sage: v = FunctionFieldValuation(K, 1/x) + sage: v = K.valuation(1/x) sage: w = v.extension(L) sage: w.restriction(K) is v True diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx index 84ff592fe7b..9bd847c2c06 100644 --- a/src/sage/rings/integer_ring.pyx +++ b/src/sage/rings/integer_ring.pyx @@ -1514,6 +1514,21 @@ cdef class IntegerRing_class(PrincipalIdealDomain): """ return sib.name('ZZ') + def valuation(self, p): + r""" + Return the discrete valuation with uniformizer `p`. + + EXAMPLES:: + + sage: v = ZZ.valuation(3); v + 3-adic valuation + sage: v(3) + 1 + + """ + from sage.rings.padics.padic_valuation import pAdicValuation + return pAdicValuation(self, p) + ZZ = IntegerRing_class() Z = ZZ diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index c5cfe2c731f..f6e15407364 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -6477,6 +6477,19 @@ def solve_CRT(self, reslist, Ilist, check=True): raise RuntimeError("Error in number field solve_CRT()") return self(x) + def valuation(self, prime): + r""" + Return the valuation on this field defined by ``prime``. + + EXAMPLES:: + + sage: GaussianIntegers().fraction_field().valuation(2) + 2-adic valuation + + """ + from sage.rings.padics.padic_valuation import pAdicValuation + return pAdicValuation(self, prime) + class NumberField_absolute(NumberField_generic): def __init__(self, polynomial, name, latex_name=None, check=True, embedding=None, assume_disc_small=False, maximize_at_primes=None, structure=None): diff --git a/src/sage/rings/number_field/order.py b/src/sage/rings/number_field/order.py index b68f198fb86..f6dd03425b3 100644 --- a/src/sage/rings/number_field/order.py +++ b/src/sage/rings/number_field/order.py @@ -991,6 +991,18 @@ def absolute_degree(self): """ return self.number_field().absolute_degree() + def valuation(self, p): + r""" + Return the `p`-adic valuation on this order. + + EXAMPLES:: + + sage: GaussianIntegers().valuation(2) + + """ + from sage.rings.padics.padic_valuation import pAdicValuation + return pAdicValuation(self, p) + ## def absolute_polynomial(self): ## """ ## Returns the absolute polynomial of this order, which is just the absolute polynomial of the number field. diff --git a/src/sage/rings/padics/all.py b/src/sage/rings/padics/all.py index 809b4aafe9f..3e361a9a50c 100644 --- a/src/sage/rings/padics/all.py +++ b/src/sage/rings/padics/all.py @@ -6,4 +6,3 @@ from .padic_generic import local_print_mode from .pow_computer import PowComputer from .pow_computer_ext import PowComputer_ext_maker -from .discrete_value_group import DiscreteValueGroup diff --git a/src/sage/rings/padics/discrete_value_group.py b/src/sage/rings/padics/discrete_value_group.py deleted file mode 100644 index 89efaf4757a..00000000000 --- a/src/sage/rings/padics/discrete_value_group.py +++ /dev/null @@ -1,215 +0,0 @@ -r""" -Value groups of discrete valuations - -This file defines additive subgroups of `\QQ` generated by a rational number. - -AUTHORS: - -- Julian Rueth (2013-09-06): initial version - -""" -#***************************************************************************** -# Copyright (C) 2013 Julian Rueth -# -# Distributed under the terms of the GNU General Public License (GPL) -# as published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** -from sage.rings.all import ZZ, QQ, Rational, infinity -from sage.categories.modules import Modules -from sage.structure.parent import Parent -from sage.structure.unique_representation import UniqueRepresentation - -category = Modules(ZZ) - - -class DiscreteValueGroup(UniqueRepresentation, Parent): - r""" - The value group of a discrete valuation, an additive subgroup of `\QQ` - generated by ``generator``. - - INPUT: - - - ``generator`` -- a rational number - - EXAMPLES:: - - sage: D1 = DiscreteValueGroup(0); D1 - DiscreteValueGroup(0) - sage: D2 = DiscreteValueGroup(4/3); D2 - DiscreteValueGroup(4/3) - sage: D3 = DiscreteValueGroup(-1/3); D3 - DiscreteValueGroup(1/3) - - TESTS:: - - sage: TestSuite(D1).run() - sage: TestSuite(D2).run() - sage: TestSuite(D3).run() - - """ - @staticmethod - def __classcall__(cls, generator, category=category): - r""" - Normalizes ``generator`` to a positive rational so that this is a - unique parent. - - TESTS:: - - sage: DiscreteValueGroup(1) is DiscreteValueGroup(-1) - True - - """ - from sage.misc.functional import coerce - generator = coerce(QQ, generator) - generator = generator.abs() - return super(DiscreteValueGroup, cls).__classcall__(cls, generator, category) - - def __init__(self, generator, category): - r""" - Initialization. - - TESTS:: - - sage: type(DiscreteValueGroup(0)) - - - """ - self._generator = generator - - Parent.__init__(self, facade=QQ, category=category) - - def _element_constructor_(self, x): - r""" - Create an element in this group from ``x``. - - INPUT: - - - ``x`` -- a rational number - - TESTS:: - - sage: DiscreteValueGroup(0)(0) - 0 - sage: DiscreteValueGroup(0)(1) - Traceback (most recent call last): - ... - ValueError: `1` is not in DiscreteValueGroup(0). - sage: DiscreteValueGroup(1)(1) - 1 - sage: DiscreteValueGroup(1)(1/2) - Traceback (most recent call last): - ... - ValueError: `1/2` is not in DiscreteValueGroup(1). - - """ - from sage.misc.functional import coerce - x = coerce(QQ, x) - if x == 0 or (self._generator != 0 and x/self._generator in ZZ): - return x - - raise ValueError("`{0}` is not in {1}.".format(x,self)) - - def _repr_(self): - r""" - Return a printable representation for this group. - - EXAMPLES:: - - sage: DiscreteValueGroup(0) # indirect doctest - DiscreteValueGroup(0) - - """ - return "DiscreteValueGroup({0})".format(self._generator) - - def __add__(self, other): - r""" - Return the subgroup of `\QQ` generated by this group and ``other``. - - INPUT: - - - ``other`` -- a discrete value group or a rational number - - EXAMPLES:: - - sage: D = DiscreteValueGroup(1/2) - sage: D + 1/3 - DiscreteValueGroup(1/6) - sage: D + D - DiscreteValueGroup(1/2) - sage: D + 1 - DiscreteValueGroup(1/2) - sage: DiscreteValueGroup(2/7) + DiscreteValueGroup(4/9) - DiscreteValueGroup(2/63) - - """ - if not isinstance(other, DiscreteValueGroup): - from sage.structure.element import is_Element - if is_Element(other) and QQ.has_coerce_map_from(other.parent()): - return self + DiscreteValueGroup(other, category=self.category()) - raise ValueError("`other` must be a DiscreteValueGroup or a rational number") - if self.category() is not other.category(): - raise ValueError("`other` must be in the same category") - return DiscreteValueGroup(self._generator.gcd(other._generator), category=self.category()) - - def _mul_(self, other, switch_sides=False): - r""" - Return the group generated by ``other`` times the generator of this - group. - - INPUT: - - - ``other`` -- a rational number - - EXAMPLES:: - - sage: D = DiscreteValueGroup(1/2) - sage: 1/2 * D - DiscreteValueGroup(1/4) - sage: D * (1/2) - DiscreteValueGroup(1/4) - sage: D * 0 - DiscreteValueGroup(0) - - """ - from sage.misc.functional import coerce - other = coerce(QQ, other) - return DiscreteValueGroup(self._generator*other, category=self.category()) - - def index(self, other): - r""" - Return the index of ``other`` in this group. - - INPUT: - - - ``other`` -- a subgroup of this group - - EXAMPLES:: - - sage: DiscreteValueGroup(3/8).index(DiscreteValueGroup(3)) - 8 - sage: DiscreteValueGroup(3).index(DiscreteValueGroup(3/8)) - Traceback (most recent call last): - ... - ValueError: `other` must be a subgroup of this group - sage: DiscreteValueGroup(3).index(DiscreteValueGroup(0)) - +Infinity - sage: DiscreteValueGroup(0).index(DiscreteValueGroup(0)) - 1 - sage: DiscreteValueGroup(0).index(DiscreteValueGroup(3)) - Traceback (most recent call last): - ... - ValueError: `other` must be a subgroup of this group - - """ - if not isinstance(other, DiscreteValueGroup): - raise ValueError("`other` must be a DiscreteValueGroup") - if other._generator not in self: - raise ValueError("`other` must be a subgroup of this group") - if other._generator == 0: - if self._generator == 0: - return ZZ(1) - else: - return infinity - return ZZ(other._generator / self._generator) diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index a246f3e6365..21783cd5029 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -949,6 +949,38 @@ def _test_elements_eq_transitive(self, **options): """ pass + def valuation(self): + r""" + Return the `p`-adic valuation on this ring. + + OUTPUT: + + a valuation that is normalized such that the rational prime `p` has + valuation 1. + + EXAMPLES:: + + sage: K = Qp(3) + sage: R. = K[] + sage: L. = K.extension(a^3 - 3) + sage: v = L.valuation(); v + sage: v(3) + 1 + sage: L(3).valuation() + 3 + + The normalization is chosen such that the valuation restricts to the + valuation on the base ring:: + + sage: v(3) == K.valuation()(3) + True + sage: v.restriction(K) == K.valuation() + True + + """ + from sage.rings.padics.padic_valuation import pAdicValuation + return pAdicValuation(self) + def local_print_mode(obj, print_options, pos = None, ram_name = None): r""" Context manager for safely temporarily changing the print_mode diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index c2461ca7a41..3fd1cecfc47 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -15,9 +15,9 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from valuation import DiscreteValuation -from value_group import DiscreteValueSemigroup -from mapped_valuation import FiniteExtensionFromLimitValuation +from sage.rings.valuation.valuation import DiscreteValuation +from sage.rings.valuation.value_group import DiscreteValueSemigroup +from sage.rings.valuation.mapped_valuation import FiniteExtensionFromLimitValuation from sage.structure.factory import UniqueFactory from sage.misc.cachefunc import cached_method from sage.misc.fast_methods import WithEqualityById @@ -41,23 +41,23 @@ class PadicValuationFactory(UniqueFactory): For integers and rational numbers, ``prime`` is just a prime of the integers:: - sage: pAdicValuation(ZZ, 3) + sage: valuations.pAdicValuation(ZZ, 3) 3-adic valuation - sage: pAdicValuation(QQ, 3) + sage: valuations.pAdicValuation(QQ, 3) 3-adic valuation ``prime`` may be ``None`` for local rings:: - sage: pAdicValuation(Qp(2)) + sage: valuations.pAdicValuation(Qp(2)) 2-adic valuation - sage: pAdicValuation(Zp(2)) + sage: valuations.pAdicValuation(Zp(2)) 2-adic valuation But it must be specified in all other cases:: - sage: pAdicValuation(ZZ) + sage: valuations.pAdicValuation(ZZ) Traceback (most recent call last): ... ValueError: prime must be specified for this ring @@ -65,23 +65,23 @@ class PadicValuationFactory(UniqueFactory): For number fields, ``prime`` can be an integer that is completely ramified in ``R``:: - sage: pAdicValuation(GaussianIntegers().fraction_field(), 2) + sage: GaussianIntegers().fraction_field().valuation(2) 2-adic valuation For number fields, ``prime`` can be an integer that is unramified in ``R``: - sage: pAdicValuation(GaussianIntegers().fraction_field(), 3) + sage: GaussianIntegers().fraction_field().valuation(3) 3-adic valuation The same applies if ``R`` is a subring of a number field:: - sage: pAdicValuation(GaussianIntegers(), 3) + sage: GaussianIntegers().valuation(3) 3-adic valuation However, this is only supported if ``prime`` does not factor into pairwise distinct factors:: - sage: pAdicValuation(GaussianIntegers(), 5) + sage: GaussianIntegers().valuation(5) Traceback (most recent call last): ... ValueError: The valuation Gauss valuation induced by 5-adic valuation does not approximate a unique extension of 5-adic valuation with respect to x^2 + 1 @@ -90,12 +90,12 @@ class PadicValuationFactory(UniqueFactory): ``prime`` can also be specified by providing a valuation on the base ring that has a unique extension:: - sage: pAdicValuation(CyclotomicField(5), pAdicValuation(ZZ, 5)) + sage: CyclotomicField(5).valuation(ZZ.valuation(5)) 5-adic valuation When the extension is not unique, this does not work:: - sage: pAdicValuation(GaussianIntegers(), pAdicValuation(ZZ, 5)) + sage: GaussianIntegers().valuation(ZZ.valuation(5)) Traceback (most recent call last): ... ValueError: The valuation Gauss valuation induced by 5-adic valuation does not approximate a unique extension of 5-adic valuation with respect to x^2 + 1 @@ -106,15 +106,15 @@ class PadicValuationFactory(UniqueFactory): valuation we care about in the above example:: sage: R. = QQ[] - sage: v = pAdicValuation(GaussianIntegers(), GaussValuation(R, pAdicValuation(QQ, 5)).augmentation(x + 2, infinity)) - sage: w = pAdicValuation(GaussianIntegers(), GaussValuation(R, pAdicValuation(QQ, 5)).augmentation(x + 1/2, infinity)) + sage: v = GaussianIntegers().valuation(GaussValuation(R, QQ.valuation(5)).augmentation(x + 2, infinity)) + sage: w = GaussianIntegers().valuation(GaussValuation(R, QQ.valuation(5)).augmentation(x + 1/2, infinity)) sage: v == w False Note that you get the same valuation, even if you write down the pseudo-valuation differently:: - sage: ww = pAdicValuation(GaussianIntegers(), GaussValuation(R, pAdicValuation(QQ, 5)).augmentation(x + 3, infinity)) + sage: ww = GaussianIntegers().valuation(GaussValuation(R, QQ.valuation(5)).augmentation(x + 3, infinity)) sage: w is ww True @@ -124,8 +124,8 @@ class PadicValuationFactory(UniqueFactory): completion, i.e., if it is not possible to write down one of the factors within the number field:: - sage: v = GaussValuation(R, pAdicValuation(QQ, 5)).augmentation(x + 3, 1) - sage: pAdicValuation(GaussianIntegers().fraction_field(), v) + sage: v = GaussValuation(R, QQ.valuation(5)).augmentation(x + 3, 1) + sage: GaussianIntegers().fraction_field().valuation(v) [ 5-adic valuation, v(x + 3) = 1 ]-adic valuation Finally, ``prime`` can also be a fractional ideal of a number field if it @@ -133,7 +133,7 @@ class PadicValuationFactory(UniqueFactory): sage: R = GaussianIntegers() sage: I = R.fraction_field().gen() - sage: pAdicValuation(R, R.fractional_ideal(I + 1)) + sage: R.valuation(R.fractional_ideal(I + 1)) 2-adic valuation It can sometimes be beneficial to define a number field extension as a @@ -144,7 +144,7 @@ class PadicValuationFactory(UniqueFactory): sage: K. = NumberField(x^2 + 1) sage: R. = K[] sage: L. = R.quo(x^2 + a) - sage: pAdicValuation(L, 2) + sage: valuations.pAdicValuation(L, 2) 2-adic valuation """ @@ -155,7 +155,7 @@ def create_key_and_extra_args(self, R, prime=None, approximants=None): EXAMPLES:: - sage: pAdicValuation(QQ, 2) # indirect doctest + sage: QQ.valuation(2) # indirect doctest 2-adic valuation """ @@ -184,14 +184,14 @@ def create_key_for_integers(self, R, prime): EXAMPLES:: - sage: pAdicValuation(QQ, 2) # indirect doctest + sage: QQ.valuation(2) # indirect doctest 2-adic valuation """ from sage.rings.all import ZZ if prime is None: raise ValueError("prime must be specified for this ring") - from valuation import DiscretePseudoValuation + from sage.rings.valuation.valuation import DiscretePseudoValuation if isinstance(prime, DiscretePseudoValuation): prime = prime.uniformizer() if prime not in ZZ or not ZZ(prime).is_prime(): @@ -205,7 +205,7 @@ def create_key_for_local_ring(self, R, prime): EXAMPLES:: - sage: pAdicValuation(Qp(2)) # indirect doctest + sage: Qp(2).valuation() # indirect doctest 2-adic valuation """ @@ -227,18 +227,18 @@ def create_key_and_extra_args_for_number_field(self, R, prime, approximants): EXAMPLES:: - sage: pAdicValuation(GaussianIntegers(), 2) # indirect doctest + sage: GaussianIntegers().valuation(2) # indirect doctest 2-adic valuation """ K, L, G = self._normalize_number_field_data(R) from sage.rings.number_field.number_field_ideal import NumberFieldFractionalIdeal - from valuation import DiscretePseudoValuation + from sage.rings.valuation.valuation import DiscretePseudoValuation if isinstance(prime, DiscretePseudoValuation): return self.create_key_and_extra_args_for_number_field_from_valuation(R, prime, prime, approximants=approximants) elif prime in K: - return self.create_key_and_extra_args_for_number_field_from_valuation(R, pAdicValuation(K, prime), prime, approximants=approximants) + return self.create_key_and_extra_args_for_number_field_from_valuation(R, K.valuation(prime), prime, approximants=approximants) elif prime in L or isinstance(prime, NumberFieldFractionalIdeal): return self.create_key_and_extra_args_for_number_field_from_ideal(R, L.fractional_ideal(prime), prime) else: @@ -257,7 +257,7 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime, EXAMPLES:: - sage: pAdicValuation(GaussianIntegers(), pAdicValuation(ZZ, 2)) # indirect doctest + sage: GaussianIntegers().valuation(ZZ.valuation(2)) # indirect doctest 2-adic valuation TESTS: @@ -266,10 +266,10 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime, sage: R. = ZZ[] sage: S = R.quo(x^2 + 1) - sage: v = pAdicValuation(S, 2) + sage: v = valuations.pAdicValuation(S, 2) sage: R. = QQ[] sage: S = R.quo(x^2 + 1) - sage: v = pAdicValuation(S, v) + sage: v = valuations.pAdicValuation(S, v) """ K, L, G = self._normalize_number_field_data(R) @@ -282,8 +282,8 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime, # valuations on K[x]. if v.domain().is_subring(K): if v.domain() is not K: - v = pAdicValuation(K, v) - from gauss_valuation import GaussValuation + v = K.valuation(v) + from sage.rings.valuation.gauss_valuation import GaussValuation v = GaussValuation(G.parent(), v) if v.domain() != G.parent(): # Then, we lift valuations defined on polynmial rings which are @@ -322,7 +322,7 @@ def create_key_and_extra_args_for_number_field_from_ideal(self, R, I, prime): EXAMPLES:: - sage: pAdicValuation(GaussianIntegers(), GaussianIntegers().ideal(2)) # indirect doctest + sage: GaussianIntegers().valuation(GaussianIntegers().ideal(2)) # indirect doctest 2-adic valuation """ @@ -336,7 +336,7 @@ def create_key_and_extra_args_for_number_field_from_ideal(self, R, I, prime): F = p.factor() if len(F) != 1: raise ValueError("%r does not lie over a single prime of %r"%(I, K)) - vK = pAdicValuation(K, F[0][0]) + vK = K.valuation(F[0][0]) candidates = vK.mac_lane_approximants(G) candidates_for_I = [c for c in candidates if all(c(g.polynomial()) > 0 for g in I.gens())] @@ -363,7 +363,7 @@ def _normalize_number_field_data(self, R): sage: R. = QQ[] sage: K = R.quo(x^2 + 1) - sage: pAdicValuation._normalize_number_field_data(K) + sage: valuations.pAdicValuation._normalize_number_field_data(K) (Rational Field, Univariate Quotient Polynomial Ring in xbar over Rational Field with modulus x^2 + 1, x^2 + 1) @@ -395,13 +395,13 @@ def create_object(self, version, key, **extra_args): EXAMPLES:: - sage: pAdicValuation(ZZ, 5) # indirect doctest + sage: ZZ.valuation(5) # indirect doctest 5-adic valuation """ from sage.rings.all import ZZ, QQ from sage.rings.padics.padic_generic import pAdicGeneric - from valuation_space import DiscretePseudoValuationSpace + from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace from sage.rings.polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing from sage.rings.number_field.number_field import is_NumberField R = key[0] @@ -442,31 +442,32 @@ class pAdicValuation_base(DiscreteValuation): EXAMPLES:: - sage: pAdicValuation(ZZ, 3) + sage: ZZ.valuation(3) 3-adic valuation - sage: pAdicValuation(QQ, 5) + sage: QQ.valuation(5) 5-adic valuation For `p`-adic rings, ``p`` has to match the `p` of the ring. - sage: v = pAdicValuation(Zp(3), 2); v + sage: v = valuations.pAdicValuation(Zp(3), 2); v Traceback (most recent call last): ... ValueError: prime must be an element of positive valuation TESTS:: - sage: TestSuite(pAdicValuation(ZZ, 3)).run() # long time - sage: TestSuite(pAdicValuation(QQ, 5)).run() # long time - sage: TestSuite(pAdicValuation(Zp(5), 5)).run() # long time + sage: TestSuite(ZZ.valuation(3)).run() # long time + sage: TestSuite(QQ.valuation(5)).run() # long time + sage: TestSuite(Zp(5).valuation(5)).run() # long time """ def __init__(self, parent, p): """ TESTS:: - sage: isinstance(pAdicValuation(ZZ, 3), pAdicValuation_base) + sage: from sage.rings.padics.padic_valuation import pAdicValuation_base + sage: isinstance(ZZ.valuation(3), pAdicValuation_base) True """ @@ -481,7 +482,7 @@ def p(self): EXAMPLES:: - sage: pAdicValuation(GaussianIntegers(), 2).p() + sage: GaussianIntegers().valuation(2).p() 2 """ @@ -501,7 +502,7 @@ def reduce(self, x): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 3) + sage: v = ZZ.valuation(3) sage: v.reduce(4) 1 @@ -523,7 +524,7 @@ def lift(self, x): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 3) + sage: v = ZZ.valuation(3) sage: xbar = v.reduce(4) sage: v.lift(xbar) 1 @@ -557,7 +558,7 @@ def is_unramified(self, G, include_steps=False, assume_squarefree=False): Hence, a trivial extension is unramified:: sage: R. = QQ[] - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: v.is_unramified(x) True @@ -582,7 +583,7 @@ def is_unramified(self, G, include_steps=False, assume_squarefree=False): if not assume_squarefree and not G.is_squarefree(): raise ValueError("G must be squarefree") - from gauss_valuation import GaussValuation + from sage.rings.valuation.gauss_valuation import GaussValuation steps = [ GaussValuation(R, self) ] while True: @@ -634,9 +635,9 @@ def is_totally_ramified(self, G, include_steps=False, assume_squarefree=False): EXAMPLES:: - sage: k=Qp(5,4) - sage: v = pAdicValuation(k) - sage: R.=k[] + sage: k = Qp(5,4) + sage: v = k.valuation() + sage: R. = k[] sage: G = x^2 + 1 sage: v.is_totally_ramified(G) False @@ -661,7 +662,7 @@ def is_totally_ramified(self, G, include_steps=False, assume_squarefree=False): if not assume_squarefree and not G.is_squarefree(): raise ValueError("G must be squarefree") - from gauss_valuation import GaussValuation + from sage.rings.valuation.gauss_valuation import GaussValuation steps = [ GaussValuation(R, self) ] while True: @@ -695,7 +696,7 @@ def change_domain(self, ring): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 2) + sage: v = ZZ.valuation(2) sage: v.change_domain(QQ).domain() Rational Field @@ -710,11 +711,11 @@ def _extensions_to_quotient(self, ring, approximants=None): EXAMPLES:: sage: R. = QQ[] - sage: pAdicValuation(QQ, 2)._extensions_to_quotient(R.quo(x^2 + x + 1)) + sage: QQ.valuation(2)._extensions_to_quotient(R.quo(x^2 + x + 1)) [2-adic valuation] """ - from valuation_space import DiscretePseudoValuationSpace + from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace parent = DiscretePseudoValuationSpace(ring) approximants = approximants or self.mac_lane_approximants(ring.modulus().change_ring(self.domain()), assume_squarefree=True) return [pAdicValuation(ring, approximant, approximants) for approximant in approximants] @@ -725,7 +726,7 @@ def extensions(self, ring): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 2) + sage: v = ZZ.valuation(2) sage: v.extensions(GaussianIntegers()) [2-adic valuation] @@ -735,7 +736,7 @@ def extensions(self, ring): sage: L. = QQ.extension(x^3 - 2) sage: R. = L[] sage: M. = L.extension(b^2 + 2*b + a) - sage: pAdicValuation(M, 2) + sage: M.valuation(2) 2-adic valuation Check that we can extend to a field written as a quotient:: @@ -744,7 +745,7 @@ def extensions(self, ring): sage: K. = QQ.extension(x^2 + 1) sage: R. = K[] sage: L. = R.quo(x^2 + a) - sage: pAdicValuation(QQ, 2).extensions(L) + sage: QQ.valuation(2).extensions(L) [2-adic valuation] """ @@ -753,14 +754,14 @@ def extensions(self, ring): domain_fraction_field = _fraction_field(self.domain()) if domain_fraction_field is not self.domain(): if domain_fraction_field.is_subring(ring): - return pAdicValuation(domain_fraction_field, self).extensions(ring) + return domain_fraction_field.valuation(self).extensions(ring) if self.domain().is_subring(ring): from sage.rings.polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing if is_PolynomialQuotientRing(ring): if is_PolynomialQuotientRing(self.domain()): if self.domain().modulus() == ring.modulus(): base_extensions = self._base_valuation.extensions(self._base_valuation.domain().change_ring(self._base_valuation.domain().base_ring().fraction_field())) - return [pAdicValuation(ring, base._initial_approximation) for base in base_extensions] + return [ring.valuation(base._initial_approximation) for base in base_extensions] if ring.base_ring() is self.domain(): from sage.categories.all import IntegralDomains if ring in IntegralDomains(): @@ -770,7 +771,7 @@ def extensions(self, ring): from sage.rings.number_field.number_field import is_NumberField if is_NumberField(ring.fraction_field()): if ring.base_ring().fraction_field() is self.domain().fraction_field(): - from valuation_space import DiscretePseudoValuationSpace + from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace parent = DiscretePseudoValuationSpace(ring) approximants = self.mac_lane_approximants(ring.fraction_field().relative_polynomial().change_ring(self.domain()), assume_squarefree=True) return [pAdicValuation(ring, approximant, approximants) for approximant in approximants] @@ -784,7 +785,7 @@ def restriction(self, ring): EXAMPLES:: - sage: v = pAdicValuation(GaussianIntegers(), 2) + sage: v = GaussianIntegers().valuation(2) sage: v.restriction(ZZ) 2-adic valuation @@ -795,7 +796,7 @@ def restriction(self, ring): if not ring.is_subring(self.domain()): raise ValueError("ring must be a subring of the domain of this valuation but %r is not a subring of %r"%(ring, self.domain())) - return pAdicValuation(ring, self.p()) + return ring.valuation(self.p()) @cached_method def value_semigroup(self): @@ -804,7 +805,7 @@ def value_semigroup(self): EXAMPLES:: - sage: v = pAdicValuation(GaussianIntegers(), 2) + sage: v = GaussianIntegers().valuation(2) sage: v.value_semigroup() Additive Abelian Semigroup generated by 1/2 @@ -827,7 +828,7 @@ class pAdicValuation_padic(pAdicValuation_base): EXAMPLES:: - sage: v = pAdicValuation(Qp(2)); v #indirect doctest + sage: v = Qp(2).valuation(); v #indirect doctest 2-adic valuation TESTS:: @@ -839,8 +840,8 @@ def __init__(self, parent): """ TESTS:: - sage: from sage.rings.padics.padic_valuation import padicValuation_padic # optional: integrated - sage: isinstance(pAdicValuation(Qp(2)), pAdicValuation_padic) + sage: from sage.rings.padics.padic_valuation import pAdicValuation_padic + sage: isinstance(Qp(2).valuation(), pAdicValuation_padic) True """ @@ -861,7 +862,7 @@ def reduce(self, x): EXAMPLES:: sage: R = Zp(3) - sage: pAdicValuation(Zp(3)).reduce(R(4)) + sage: Zp(3).valuation().reduce(R(4)) 1 """ @@ -880,7 +881,7 @@ def lift(self, x): EXAMPLES:: sage: R = Zp(3) - sage: v = pAdicValuation(R) + sage: v = R.valuation() sage: xbar = v.reduce(R(4)) sage: v.lift(xbar) 1 + O(3^20) @@ -895,7 +896,7 @@ def uniformizer(self): EXAMPLES:: - sage: v = pAdicValuation(Zp(3)) + sage: v = Zp(3).valuation() sage: v.uniformizer() 3 + O(3^21) @@ -913,7 +914,7 @@ def element_with_valuation(self, v): EXAMPLES:: sage: R = Zp(3) - sage: v = pAdicValuation(Zp(3)) + sage: v = R.valuation() sage: v.element_with_valuation(3) 3^3 + O(3^23) @@ -931,7 +932,7 @@ def _repr_(self): EXAMPLES:: - sage: pAdicValuation(ZZ, 3)._repr_() + sage: ZZ.valuation(3)._repr_() '3-adic valuation' """ @@ -946,7 +947,7 @@ def _call_(self, x): sage: K = Qp(3) sage: R. = K[] sage: L. = K.extension(y^2 - 3) - sage: pAdicValuation(L, 3)(3) + sage: L.valuation()(3) 1 """ @@ -958,7 +959,7 @@ def residue_ring(self): EXAMPLES:: - sage: pAdicValuation(Qq(9, names='a'), 3).residue_ring() + sage: Qq(9, names='a').valuation().residue_ring() Finite Field in a0 of size 3^2 """ @@ -979,7 +980,7 @@ def shift(self, x, s): EXAMPLES:: sage: R = ZpCA(2) - sage: v = pAdicValuation(R) + sage: v = R.valuation() sage: v.shift(R.one(), 1) 2 + O(2^20) sage: v.shift(R.one(), -1) @@ -1012,7 +1013,7 @@ def simplify(self, x, error=None, force=False): EXAMPLES:: sage: R = Zp(2) - sage: v = pAdicValuation(R, 2) + sage: v = R.valuation() sage: v.simplify(6) 2 + O(2^21) sage: v.simplify(6, error=0) @@ -1036,7 +1037,7 @@ class pAdicValuation_int(pAdicValuation_base): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 3); v + sage: v = ZZ.valuation(3); v 3-adic valuation TESTS:: @@ -1050,7 +1051,7 @@ def _repr_(self): EXAMPLES:: - sage: pAdicValuation(ZZ, 3)._repr_() + sage: ZZ.valuation(3)._repr_() '3-adic valuation' """ @@ -1066,7 +1067,7 @@ def _call_(self, x): EXAMPLES:: - sage: pAdicValuation(ZZ, 3)(9) + sage: ZZ.valuation(3)(9) 2 """ @@ -1084,7 +1085,7 @@ def uniformizer(self): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 3) + sage: v = ZZ.valuation(3) sage: v.uniformizer() 3 @@ -1097,7 +1098,7 @@ def residue_ring(self): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 3) + sage: v = ZZ.valuation(3) sage: v.residue_ring() Finite Field of size 3 @@ -1112,8 +1113,8 @@ def _ge_(self, other): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 2) - sage: w = TrivialValuation(ZZ) + sage: v = ZZ.valuation(2) + sage: w = valuations.TrivialValuation(ZZ) sage: v >= w True @@ -1138,7 +1139,7 @@ def _relative_size(self, x): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 2) + sage: v = ZZ.valuation(2) sage: v._relative_size(2) 2 sage: v._relative_size(2**20) @@ -1171,7 +1172,7 @@ def simplify(self, x, error=None, force=False, size_heuristic_bound=32): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 2) + sage: v = ZZ.valuation(2) sage: v.simplify(6, force=True) 2 sage: v.simplify(6, error=0, force=True) @@ -1217,7 +1218,7 @@ class pAdicFromLimitValuation(FiniteExtensionFromLimitValuation, pAdicValuation_ EXAMPLES:: - sage: v = pAdicValuation(GaussianIntegers(), 3); v + sage: v = GaussianIntegers().valuation(3); v 3-adic valuation TESTS:: @@ -1229,7 +1230,8 @@ def __init__(self, parent, approximant, G, approximants): r""" TESTS:: - sage: v = pAdicValuation(GaussianIntegers(), 3) + sage: v = GaussianIntegers().valuation(3) + sage: from sage.rings.padics.padic_valuation import pAdicFromLimitValuation sage: isinstance(v, pAdicFromLimitValuation) True @@ -1244,7 +1246,7 @@ def _to_base_domain(self, f): EXAMPLES:: - sage: v = pAdicValuation(GaussianIntegers(), 3) + sage: v = GaussianIntegers().valuation(3) sage: I = GaussianIntegers().fraction_field().gen() sage: v._to_base_domain(I) x @@ -1260,7 +1262,7 @@ def _from_base_domain(self, f): EXAMPLES:: - sage: v = pAdicValuation(GaussianIntegers(), 3) + sage: v = GaussianIntegers().valuation(3) sage: v._from_base_domain(v._base_valuation.domain().gen()) I @@ -1273,7 +1275,7 @@ def extensions(self, ring): EXAMPLES:: - sage: v = pAdicValuation(GaussianIntegers(), 3) + sage: v = GaussianIntegers().valuation(3) sage: v.extensions(v.domain().fraction_field()) [3-adic valuation] @@ -1300,6 +1302,7 @@ def _fraction_field(ring): sage: S.fraction_field() Fraction Field of Univariate Quotient Polynomial Ring in xbar over Integer Ring with modulus x^2 + 1 + sage: from sage.rings.padics.padic_valuation import _fraction_field sage: _fraction_field(S) Univariate Quotient Polynomial Ring in xbar over Rational Field with modulus x^2 + 1 diff --git a/src/sage/rings/rational_field.py b/src/sage/rings/rational_field.py index fe23183462b..38d63f2684c 100644 --- a/src/sage/rings/rational_field.py +++ b/src/sage/rings/rational_field.py @@ -1291,6 +1291,21 @@ def _factor_univariate_polynomial(self, f): from sage.structure.factorization import Factorization return Factorization(F, f.leading_coefficient()) + def valuation(self, p): + r""" + Return the discrete valuation with uniformizer `p`. + + EXAMPLES:: + + sage: v = QQ.valuation(3); v + 3-adic valuation + sage: v(1/3) + -1 + + """ + from sage.rings.padics.padic_valuation import pAdicValuation + return pAdicValuation(self, p) + QQ = RationalField() Q = QQ diff --git a/src/sage/rings/valuation/__init__.py b/src/sage/rings/valuation/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/rings/valuation/all.py b/src/sage/rings/valuation/all.py new file mode 100644 index 00000000000..2ca3d3ae411 --- /dev/null +++ b/src/sage/rings/valuation/all.py @@ -0,0 +1,4 @@ +from sage.misc.lazy_import import lazy_import + +lazy_import('sage.rings.valuation.gauss_valuation', 'GaussValuation') +lazy_import('sage.rings.valuation', 'valuations_catalog', 'valuations') diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index 7c345c25f85..b68d981de1d 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -8,7 +8,7 @@ polynomial rings:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x, 1); w [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ] sage: w(x) @@ -18,7 +18,7 @@ However, much of the functionality is only available over fields:: sage: R. = ZZ[] - sage: v = GaussValuation(R, pAdicValuation(ZZ, 2)) + sage: v = GaussValuation(R, ZZ.valuation(2)) sage: w = v.augmentation(x, 1); w [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ] sage: w(x) @@ -27,7 +27,7 @@ TESTS:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x, 1) sage: TestSuite(w).run() # long time @@ -37,7 +37,7 @@ Run the test suite for a valuation with a residual extension:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, 1) sage: TestSuite(w).run() # long time @@ -67,7 +67,7 @@ Run the test suite for a ramified augmentation of an unramified augmentation:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, 1) sage: TestSuite(w).run() # long time @@ -127,7 +127,7 @@ Run the test suite if the polynomial ring is not over a field:: sage: R. = ZZ[] - sage: v = GaussValuation(R, pAdicValuation(ZZ, 2)) + sage: v = GaussValuation(R, ZZ.valuation(2)) sage: w = v.augmentation(x, 1) sage: TestSuite(w).run() # long time @@ -171,7 +171,7 @@ class AugmentedValuationFactory(UniqueFactory): :meth:`augmentation` of a valuation should be called:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x, 1) # indirect doctest Note that trivial parts of the augmented valuation might be dropped, so you @@ -199,7 +199,7 @@ def create_key(self, base_valuation, phi, mu, check=True): TESTS:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x, 1) # indirect doctest sage: ww = v.augmentation(x, 1) sage: w is ww @@ -233,7 +233,7 @@ def create_object(self, version, key): TESTS:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, 1) # indirect doctest """ @@ -270,7 +270,7 @@ class AugmentedValuation_base(InductiveValuation): sage: K. = CyclotomicField(5) sage: R. = K[] - sage: v = GaussValuation(R, pAdicValuation(K, 2)) + sage: v = GaussValuation(R, K.valuation(2)) sage: w = v.augmentation(x, 1/2); w # indirect doctest [ Gauss valuation induced by 2-adic valuation, v(x) = 1/2 ] sage: ww = w.augmentation(x^4 + 2*x^2 + 4*u, 3); ww @@ -289,7 +289,9 @@ def __init__(self, parent, v, phi, mu): sage: K. = Qq(4, 5) sage: R. = K[] sage: v = GaussValuation(R) + sage: from sage.rings.valuation.augmented_valuation import AugmentedValuation sage: w = AugmentedValuation(v, x, 1/2) + sage: from sage.rings.valuation.augmented_valuation import AugmentedValuation_base sage: isinstance(w, AugmentedValuation_base) True @@ -439,7 +441,7 @@ def augmentation_chain(self): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x, 1) sage: w.augmentation_chain() [[ Gauss valuation induced by 2-adic valuation, v(x) = 1 ], @@ -542,7 +544,7 @@ def extensions(self, ring): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, 1) sage: w.extensions(GaussianIntegers().fraction_field()['x']) @@ -583,7 +585,7 @@ def restriction(self, ring): sage: K = GaussianIntegers().fraction_field() sage: R. = K[] - sage: v = GaussValuation(R, pAdicValuation(K, 2)) + sage: v = GaussValuation(R, K.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, 1) sage: w.restriction(QQ['x']) @@ -606,7 +608,7 @@ def uniformizer(self): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, 1) sage: w.uniformizer() @@ -622,7 +624,7 @@ def is_gauss_valuation(self): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, 1) sage: w.is_gauss_valuation() @@ -641,7 +643,7 @@ def monic_integral_model(self, G): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, 1) sage: w.monic_integral_model(5*x^2 + 1/2*x + 1/4) @@ -662,7 +664,7 @@ def _ge_(self, other): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, 1) sage: w >= v True @@ -696,7 +698,7 @@ def is_trivial(self): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, 1) sage: w.is_trivial() False @@ -714,7 +716,7 @@ def scale(self, scalar): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, 1) sage: 3*w # indirect doctest [ Gauss valuation induced by 3 * 2-adic valuation, v(x^2 + x + 1) = 3 ] @@ -734,7 +736,7 @@ def _residue_ring_generator_name(self): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, 1) sage: w._residue_ring_generator_name() 'u1' @@ -772,7 +774,7 @@ def _relative_size(self, f): sage: R. = QQ[] sage: K. = QQ.extension(u^2 + u+ 1) sage: S. = K[] - sage: v = GaussValuation(S, pAdicValuation(K, 2)) + sage: v = GaussValuation(S, K.valuation(2)) sage: w = v.augmentation(x^2 + x + u, 1/2) sage: w._relative_size(x^2 + x + 1) 1 @@ -792,7 +794,7 @@ def is_negative_pseudo_valuation(self): `-\infty`, so this method always returns ``False``:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) sage: w = v.augmentation(x, infinity) sage: w.is_negative_pseudo_valuation() False @@ -809,7 +811,7 @@ def change_domain(self, ring): We can change the domain of an augmented valuation even if there is no coercion between rings:: sage: R. = GaussianIntegers()[] - sage: v = GaussValuation(R, pAdicValuation(GaussianIntegers(), 2)) + sage: v = GaussValuation(R, GaussianIntegers().valuation(2)) sage: v = v.augmentation(x, 1) sage: v.change_domain(QQ['x']) [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ] @@ -829,7 +831,7 @@ class FinalAugmentedValuation(AugmentedValuation_base, FinalInductiveValuation): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) sage: w = v.augmentation(x, 1) """ @@ -838,8 +840,9 @@ def __init__(self, parent, v, phi, mu): TESTS:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) sage: w = v.augmentation(x, 1) + sage: from sage.rings.valuation.augmented_valuation import FinalAugmentedValuation sage: isinstance(w, FinalAugmentedValuation) True @@ -856,7 +859,7 @@ def residue_ring(self): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) sage: w = v.augmentation(x, 1) sage: w.residue_ring() @@ -868,7 +871,7 @@ def residue_ring(self): An example with a non-trivial base valuation:: - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, infinity) sage: w.residue_ring() Finite Field in u1 of size 2^2 @@ -886,7 +889,7 @@ def residue_ring(self): We avoid clashes in generator names:: sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, x^2 + 2) + sage: v = K.valuation(x^2 + 2) sage: R. = K[] sage: L. = K.extension(y^2 + x^2) sage: w = v.extension(L) @@ -940,7 +943,7 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) sage: w = v.augmentation(x, 1) sage: w.reduce(x^2 + x + 1) @@ -1015,7 +1018,7 @@ def _residue_field_generator(self): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) sage: w = v.augmentation(x, 1) sage: w._residue_field_generator() @@ -1062,7 +1065,7 @@ def lift(self, F): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) sage: w = v.augmentation(x, 1) sage: w.lift(1/2) @@ -1114,7 +1117,7 @@ class NonFinalAugmentedValuation(AugmentedValuation_base, NonFinalInductiveValua EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, 1) """ @@ -1123,8 +1126,9 @@ def __init__(self, parent, v, phi, mu): TESTS:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, 1) + sage: from sage.rings.valuation.augmented_valuation import NonFinalAugmentedValuation sage: isinstance(w, NonFinalAugmentedValuation) True @@ -1141,7 +1145,7 @@ def residue_ring(self): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, 1) sage: w.residue_ring() @@ -1532,7 +1536,7 @@ def _Q(self, e): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, 1) sage: w._Q(1) @@ -1552,7 +1556,7 @@ def _Q_reciprocal(self, e=1): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, 1) sage: w._Q_reciprocal() @@ -1596,6 +1600,7 @@ def __init__(self, parent, v, phi, mu): sage: S. = R[] sage: v = GaussValuation(S) sage: w = v.augmentation(x^2 + x + u, 1/2) + sage: from sage.rings.valuation.augmented_valuation import FiniteAugmentedValuation sage: isinstance(w, FiniteAugmentedValuation) True @@ -1837,7 +1842,7 @@ class FinalFiniteAugmentedValuation(FiniteAugmentedValuation, FinalAugmentedValu EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) sage: w = v.augmentation(x, 1) """ @@ -1846,8 +1851,9 @@ def __init__(self, parent, v, phi, mu): TESTS:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) sage: w = v.augmentation(x, 1) + sage: from sage.rings.valuation.augmented_valuation import FinalFiniteAugmentedValuation sage: isinstance(w, FinalFiniteAugmentedValuation) True @@ -1864,7 +1870,7 @@ class NonFinalFiniteAugmentedValuation(FiniteAugmentedValuation, NonFinalAugment EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x, 1) """ @@ -1873,8 +1879,9 @@ def __init__(self, parent, v, phi, mu): TESTS:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x, 1) + sage: from sage.rings.valuation.augmented_valuation import NonFinalFiniteAugmentedValuation sage: isinstance(w, NonFinalFiniteAugmentedValuation) True @@ -1892,7 +1899,7 @@ class InfiniteAugmentedValuation(FinalAugmentedValuation, InfiniteInductiveValua EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x, infinity) """ @@ -1901,8 +1908,9 @@ def __init__(self, parent, v, phi, mu): TESTS:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x, infinity) + sage: from sage.rings.valuation.augmented_valuation import InfiniteAugmentedValuation sage: isinstance(w, InfiniteAugmentedValuation) True diff --git a/src/sage/rings/valuation/developing_valuation.py b/src/sage/rings/valuation/developing_valuation.py index 3057139cfae..efbb1a23782 100644 --- a/src/sage/rings/valuation/developing_valuation.py +++ b/src/sage/rings/valuation/developing_valuation.py @@ -11,7 +11,7 @@ """ #***************************************************************************** -# Copyright (C) 2013-2016 Julian Rüth +# Copyright (C) 2013-2017 Julian Rüth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -31,7 +31,7 @@ class DevelopingValuation(DiscretePseudoValuation): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 7)) + sage: v = valuations.GaussValuation(R, QQ.valuation(7)) TESTS:: @@ -43,7 +43,8 @@ def __init__(self, parent, phi): TESTS:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 7)) + sage: v = valuations.GaussValuation(R, QQ.valuation(7)) + sage: from sage.rings.valuation.developing_valuation import DevelopingValuation sage: isinstance(v, DevelopingValuation) True @@ -187,7 +188,7 @@ def _quo_rem(self, f): EXAMPLES:: sage: S. = QQ[] - sage: v = GaussValuation(S, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(S, QQ.valuation(2)) sage: v._quo_rem(x^2 + 1) (x, 1) @@ -282,7 +283,7 @@ def valuations(self, f): sage: R = Qp(2,5) sage: S. = R[] - sage: v = GaussValuation(S, pAdicValuation(R)) + sage: v = valuations.GaussValuation(S, R.valuation()) sage: f = x^2 + 2*x + 16 sage: list(v.valuations(f)) [4, 1, 0] diff --git a/src/sage/rings/valuation/gauss_valuation.py b/src/sage/rings/valuation/gauss_valuation.py index 10facdf8b43..e0b92d44195 100644 --- a/src/sage/rings/valuation/gauss_valuation.py +++ b/src/sage/rings/valuation/gauss_valuation.py @@ -13,7 +13,7 @@ EXAMPLES:: sage: R. = QQ[] - sage: v0 = pAdicValuation(QQ, 2) + sage: v0 = QQ.valuation(2) sage: v = GaussValuation(R, v0); v Gauss valuation induced by 2-adic valuation sage: v(2*x + 2) @@ -34,7 +34,7 @@ """ #***************************************************************************** -# Copyright (C) 2013-2016 Julian Rüth +# Copyright (C) 2013-2017 Julian Rüth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -63,7 +63,7 @@ class GaussValuationFactory(UniqueFactory): The Gauss valuation is the minimum of the valuation of the coefficients:: - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: R. = QQ[] sage: w = GaussValuation(R, v) sage: w(2) @@ -80,7 +80,7 @@ def create_key(self, domain, v = None): TESTS:: - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: R. = ZZ[] sage: GaussValuation.create_key(R, v) Traceback (most recent call last): @@ -110,7 +110,7 @@ def create_object(self, version, key, **extra_args): TESTS:: - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: R. = QQ[] sage: GaussValuation.create_object(0, (R, v)) Gauss valuation induced by 2-adic valuation @@ -137,12 +137,12 @@ class GaussValuation_generic(NonFinalInductiveValuation): sage: R = Zp(3,5) sage: S. = R[] - sage: v0 = pAdicValuation(R) + sage: v0 = R.valuation() sage: v = GaussValuation(S, v0); v Gauss valuation induced by 3-adic valuation sage: S. = QQ[] - sage: v = GaussValuation(S, pAdicValuation(QQ, 5)); v + sage: v = GaussValuation(S, QQ.valuation(5)); v Gauss valuation induced by 5-adic valuation TESTS:: @@ -154,9 +154,9 @@ def __init__(self, parent, v): """ TESTS:: - sage: from gauss_valuation import GaussValuation_generic + sage: from sage.rings.valuation.gauss_valuation import GaussValuation_generic sage: S. = QQ[] - sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) + sage: v = GaussValuation(S, QQ.valuation(5)) sage: isinstance(v, GaussValuation_generic) True @@ -172,7 +172,7 @@ def value_group(self): EXAMPLES:: sage: S. = QQ[] - sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) + sage: v = GaussValuation(S, QQ.valuation(5)) sage: v.value_group() Additive Abelian Group generated by 1 @@ -186,7 +186,7 @@ def value_semigroup(self): EXAMPLES:: sage: S. = QQ[] - sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) + sage: v = GaussValuation(S, QQ.valuation(5)) sage: v.value_semigroup() Additive Abelian Semigroup generated by -1, 1 @@ -200,7 +200,7 @@ def _repr_(self): EXAMPLES:: sage: S. = QQ[] - sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) + sage: v = GaussValuation(S, QQ.valuation(5)) sage: v # indirect doctest Gauss valuation induced by 5-adic valuation @@ -216,7 +216,7 @@ def uniformizer(self): EXAMPLES:: sage: S. = QQ[] - sage: v = GaussValuation(S, pAdicValuation(QQ, 5)) + sage: v = GaussValuation(S, QQ.valuation(5)) sage: v.uniformizer() 5 sage: v.uniformizer().parent() is S @@ -250,7 +250,7 @@ def valuations(self, f, coefficients=None, call_error=False): sage: R = ZZ sage: S. = R[] - sage: v = GaussValuation(S, pAdicValuation(R, 2)) + sage: v = GaussValuation(S, R.valuation(2)) sage: f = x^2 + 2*x + 16 sage: list(v.valuations(f)) [4, 1, 0] @@ -414,7 +414,7 @@ def lift_to_key(self, F): sage: R. = QQ sage: S. = R[] - sage: v = GaussValuation(S, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(S, QQ.valuation(2)) sage: y = v.residue_ring().gen() sage: f = v.lift_to_key(y^2 + y + 1); f x^2 + x + 1 @@ -467,7 +467,7 @@ def element_with_valuation(self, s): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: v.element_with_valuation(-2) 1/4 @@ -514,7 +514,7 @@ def change_domain(self, ring): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 2) + sage: v = ZZ.valuation(2) sage: R. = ZZ[] sage: w = GaussValuation(R, v) sage: w.change_domain(QQ['x']) @@ -533,7 +533,7 @@ def extensions(self, ring): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 2) + sage: v = ZZ.valuation(2) sage: R. = ZZ[] sage: w = GaussValuation(R, v) sage: w.extensions(GaussianIntegers()['x']) @@ -552,7 +552,7 @@ def restriction(self, ring): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 2) + sage: v = ZZ.valuation(2) sage: R. = ZZ[] sage: w = GaussValuation(R, v) sage: w.restriction(ZZ) @@ -606,7 +606,7 @@ def is_trivial(self): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) sage: v.is_trivial() True @@ -658,8 +658,8 @@ def _ge_(self, other): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) - sage: w = GaussValuation(R, pAdicValuation(QQ, 3)) + sage: v = GaussValuation(R, QQ.valuation(2)) + sage: w = GaussValuation(R, QQ.valuation(3)) sage: v >= w False sage: w >= v @@ -682,7 +682,7 @@ def scale(self, scalar): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: 3*v # indirect doctest Gauss valuation induced by 3 * 2-adic valuation @@ -707,7 +707,7 @@ def _relative_size(self, f): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: v._relative_size(x + 1024) 11 diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index fddee29a56f..52fcb01211a 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -20,7 +20,7 @@ """ #***************************************************************************** -# Copyright (C) 2016 Julian Rüth +# Copyright (C) 2016-2017 Julian Rüth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -41,7 +41,7 @@ class InductiveValuation(DevelopingValuation): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 5)) + sage: v = GaussValuation(R, QQ.valuation(5)) TESTS:: @@ -207,7 +207,7 @@ def mu(self): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: v.mu() 0 @@ -239,7 +239,7 @@ def equivalence_unit(self, s, reciprocal=False): Note that this might fail for negative ``s`` if the domain is not defined over a field:: - sage: v = pAdicValuation(ZZ, 2) + sage: v = ZZ.valuation(2) sage: R. = ZZ[] sage: w = GaussValuation(R, v) sage: w.equivalence_unit(1) @@ -323,7 +323,7 @@ def monic_integral_model(self, G): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: v.monic_integral_model(5*x^2 + 1/2*x + 1/4) (Ring endomorphism of Univariate Polynomial Ring in x over Rational Field Defn: x |--> 1/2*x, @@ -341,7 +341,7 @@ def element_with_valuation(self, s): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: v.element_with_valuation(-2) 1/4 @@ -349,7 +349,7 @@ def element_with_valuation(self, s): exist:: sage: R. = ZZ[] - sage: v = GaussValuation(R, pAdicValuation(ZZ, 2)) + sage: v = GaussValuation(R, ZZ.valuation(2)) sage: v.element_with_valuation(-2) Traceback (most recent call last): ... @@ -364,7 +364,7 @@ def _test_element_with_valuation_inductive_valuation(self, **options): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: v._test_element_with_valuation_inductive_valuation() """ @@ -418,7 +418,7 @@ def _test_augmentation_chain(self, **options): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) sage: v._test_augmentation_chain() """ @@ -436,7 +436,7 @@ def _test_equivalence_unit(self, **options): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) sage: v._test_equivalence_unit() """ @@ -468,7 +468,7 @@ def _test_is_equivalence_unit(self, **options): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) sage: v._test_is_equivalence_unit() """ @@ -482,7 +482,7 @@ def _test_equivalence_reciprocal(self, **options): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) sage: v._test_equivalence_reciprocal() """ @@ -512,7 +512,7 @@ def _test_inductive_valuation_inheritance(self, **options): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) sage: v._test_inductive_valuation_inheritance() """ @@ -530,7 +530,7 @@ class FiniteInductiveValuation(InductiveValuation, DiscreteValuation): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) """ def __init__(self, parent, phi): @@ -538,7 +538,8 @@ def __init__(self, parent, phi): TESTS:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) + sage: from sage.rings.valuation.inductive_valuation import FiniteInductiveValuation sage: isinstance(v, FiniteInductiveValuation) True @@ -553,7 +554,7 @@ def extensions(self, other): EXAMPLES:: sage: R. = ZZ[] - sage: v = GaussValuation(R, TrivialValuation(ZZ)) + sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(ZZ)) sage: K. = FunctionField(QQ) sage: v.extensions(K) [Trivial valuation on Rational Field] @@ -564,7 +565,7 @@ def extensions(self, other): # extend to K[x] and from there to K(x) v = self.extension(self.domain().change_ring(self.domain().base().fraction_field())) from function_field_valuation import FunctionFieldValuation - return [FunctionFieldValuation(other, v)] + return [other.valuation(v)] return super(FiniteInductiveValuation, self).extensions(other) @@ -578,7 +579,7 @@ class NonFinalInductiveValuation(FiniteInductiveValuation, DiscreteValuation): sage: R. = Qq(4,5) sage: S. = R[] - sage: v = GaussValuation(S) + sage: v = valuations.GaussValuation(S) sage: v = v.augmentation(x^2 + x + u, 1) """ @@ -588,8 +589,9 @@ def __init__(self, parent, phi): sage: R. = Qq(4,5) sage: S. = R[] - sage: v = GaussValuation(S) + sage: v = valuations.GaussValuation(S) sage: v = v.augmentation(x^2 + x + u, 1) + sage: from sage.rings.valuation.inductive_valuation import NonFinalInductiveValuation sage: isinstance(v, NonFinalInductiveValuation) True @@ -618,7 +620,7 @@ def augmentation(self, phi, mu, check=True): sage: R. = Qq(4,5) sage: S. = R[] - sage: v = GaussValuation(S) + sage: v = valuations.GaussValuation(S) sage: v = v.augmentation(x^2 + x + u, 1) sage: v = v.augmentation((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) sage: v @@ -631,9 +633,9 @@ def augmentation(self, phi, mu, check=True): Make sure that we do not make the assumption that the degrees of the key polynomials are strictly increasing:: - sage: v_K = pAdicValuation(QQ,3) + sage: v_K = QQ.valuation(3) sage: A. = QQ[] - sage: v0 = GaussValuation(A,v_K) + sage: v0 = valuations.GaussValuation(A,v_K) sage: v1 = v0.augmentation(t, 1/12) sage: v2 = v1.augmentation(t^12 + 3, 7/6) @@ -690,10 +692,10 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a sage: K. = FunctionField(QQ) sage: S. = K[] sage: F = y^2 - x^2 - x^3 - 3 - sage: v0 = GaussValuation(K._ring,pAdicValuation(QQ, 3)) + sage: v0 = valuations.GaussValuation(K._ring, QQ.valuation(3)) sage: v1 = v0.augmentation(K._ring.gen(), 1/3) - sage: mu0 = FunctionFieldValuation(K, v1) - sage: eta0 = GaussValuation(S, mu0) + sage: mu0 = K.valuation(v1) + sage: eta0 = valuations.GaussValuation(S, mu0) sage: eta1 = eta0.mac_lane_step(F)[0] sage: eta2 = eta1.mac_lane_step(F)[0] sage: eta2 @@ -792,7 +794,8 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a w_valuations = islice(w_valuations, 0, principal_part_bound + 1, 1) w_valuations = list(w_valuations) - NP = w.newton_polygon(G, valuations=w_valuations).principal_part() + from sage.geometry.newton_polygon import NewtonPolygon + NP = NewtonPolygon(w.newton_polygon(G, valuations=w_valuations).vertices(), last_slope=0) verbose("Newton-Polygon for v(phi)=%s : %s"%(self(phi), NP), level=11) slopes = NP.slopes(repetition=True) @@ -867,7 +870,7 @@ def is_key(self, phi, explain=False, assume_equivalence_irreducible=False): sage: R. = Qq(4, 5) sage: S. = R[] - sage: v = GaussValuation(S) + sage: v = valuations.GaussValuation(S) sage: v.is_key(x) True sage: v.is_key(2*x, explain = True) @@ -912,7 +915,7 @@ def is_minimal(self, f, assume_equivalence_irreducible=False): sage: R. = Qq(4, 5) sage: S. = R[] - sage: v = GaussValuation(S) + sage: v = valuations.GaussValuation(S) sage: v.is_minimal(x + 1) True sage: w = v.augmentation(x, 1) @@ -923,8 +926,8 @@ def is_minimal(self, f, assume_equivalence_irreducible=False): sage: K = Qp(2, 10) sage: R. = K[] - sage: vp = pAdicValuation(K) - sage: v0 = GaussValuation(R, vp) + sage: vp = K.valuation() + sage: v0 = valuations.GaussValuation(R, vp) sage: v1 = v0.augmentation(x, 1/4) sage: v2 = v1.augmentation(x^4 + 2, 5/4) sage: v2.is_minimal(x^5 + x^4 + 2) @@ -999,7 +1002,7 @@ def _equivalence_reduction(self, f, coefficients=None, valuations=None, degree_b EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = valuations.GaussValuation(R, QQ.valuation(2)) sage: v._equivalence_reduction(2*x^6 + 4*x^5 + 2*x^4 + 8) (1, 4, x^2 + 1) @@ -1058,7 +1061,7 @@ def is_equivalence_irreducible(self, f, coefficients=None, valuations=None): sage: R. = Qq(4,5) sage: S. = R[] - sage: v = GaussValuation(S) + sage: v = valuations.GaussValuation(S) sage: v.is_equivalence_irreducible(x) True sage: v.is_equivalence_irreducible(x^2) @@ -1126,7 +1129,7 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi sage: R. = Qq(4,10) sage: S. = R[] - sage: v = GaussValuation(S) + sage: v = valuations.GaussValuation(S) sage: v.equivalence_decomposition(S.zero()) Traceback (most recent call last): ... @@ -1174,16 +1177,16 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi TESTS:: sage: R. = QQ[] - sage: K1.=NumberField(x^3-2) + sage: K1.=NumberField(x^3 - 2) sage: K.=K1.galois_closure() sage: R.=K[] - sage: vp=pAdicValuation(QQ,2) - sage: vp=vp.extension(K) - sage: v0=GaussValuation(R,vp) + sage: vp = Q.valuation(2) + sage: vp = vp.extension(K) + sage: v0 = valuations.GaussValuation(R, vp) sage: G=x^36 + 36*x^35 + 630*x^34 + 7144*x^33 + 59055*x^32 + 379688*x^31 +1978792*x^30 + 8604440*x^29 + 31895428*x^28 + 102487784*x^27 + 289310720*x^26 + 725361352*x^25 + 1629938380*x^24 + 3307417800*x^23 + 6098786184*x^22+10273444280*x^21 + 15878121214*x^20 + 22596599536*x^19 + 29695703772*x^18 +36117601976*x^17 + 40722105266*x^16 + 42608585080*x^15 + 41395961848*x^14 +37344435656*x^13 + 31267160756*x^12 + 24271543640*x^11 + 17439809008*x^10 + 11571651608*x^9 + 7066815164*x^8 + 3953912472*x^7 + 2013737432*x^6 + 925014888*x^5 + 378067657*x^4 + 134716588*x^3 + 40441790*x^2 + 9532544*x + 1584151 - sage: v1=v0.mac_lane_step(G)[0] - sage: V=v1.mac_lane_step(G) - sage: v2=V[0] + sage: v1 = v0.mac_lane_step(G)[0] + sage: V = v1.mac_lane_step(G) + sage: v2 = V[0] sage: v2.equivalence_decomposition(G) (1/387420489) * (x^4 + 2*x^2 + alpha^4 + alpha^3 + 1)^3 * (x^4 + 2*x^2 + 1/2*alpha^4 + alpha^3 + 5*alpha + 1)^3 * (x^4 + 2*x^2 + 3/2*alpha^4 + alpha^3 + 5*alpha + 1)^3 @@ -1280,7 +1283,7 @@ def minimal_representative(self, f): sage: R. = Qq(4,10) sage: S. = R[] - sage: v = GaussValuation(S) + sage: v = valuations.GaussValuation(S) sage: v.minimal_representative(x + 2) (1 + O(2^10))*x @@ -1355,7 +1358,7 @@ def lift_to_key(self, F): sage: R. = Qq(4,10) sage: S. = R[] - sage: v = GaussValuation(S) + sage: v = valuations.GaussValuation(S) sage: y = v.residue_ring().gen() sage: u0 = v.residue_ring().base_ring().gen() sage: f = v.lift_to_key(y^2 + y + u0); f @@ -1370,7 +1373,7 @@ def _test_lift_to_key(self, **options): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) sage: v._test_lift_to_key() """ @@ -1419,7 +1422,7 @@ def _test_is_equivalence_irreducible(self, **options): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) sage: v._test_is_equivalence_irreducible() """ @@ -1445,8 +1448,9 @@ class FinalInductiveValuation(InductiveValuation): TESTS:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)) + sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) sage: w = v.augmentation(x^2 + x + 1, infinity) + sage: from sage.rings.valuation.inductive_valuation import FinalInductiveValuation sage: isinstance(w, FinalInductiveValuation) True @@ -1461,7 +1465,7 @@ class InfiniteInductiveValuation(FinalInductiveValuation, InfiniteDiscretePseudo EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = valuations.GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, infinity) """ @@ -1470,8 +1474,9 @@ def __init__(self, parent, base_valuation): TESTS:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = valuations.GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, infinity) + sage: from sage.rings.valuation.inductive_valuation import InfiniteInductiveValuation sage: isinstance(w, InfiniteInductiveValuation) True @@ -1488,7 +1493,7 @@ def change_domain(self, ring): We can turn an infinite valuation into a valuation on the quotient:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: v = valuations.GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, infinity) sage: w.change_domain(R.quo(x^2 + x + 1)) 2-adic valuation @@ -1509,6 +1514,7 @@ def _lift_to_maximal_precision(c): sage: R = Zp(2,5) sage: x = R(1,2); x 1 + O(2^2) + sage: from sage.rings.valuation.inductive_valuation import _lift_to_maximal_precision sage: _lift_to_maximal_precision(x) 1 + O(2^5) diff --git a/src/sage/rings/valuation/limit_valuation.py b/src/sage/rings/valuation/limit_valuation.py index b6c76b353dc..bb14f3290e4 100644 --- a/src/sage/rings/valuation/limit_valuation.py +++ b/src/sage/rings/valuation/limit_valuation.py @@ -30,7 +30,7 @@ sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 1) + sage: v = K.valuation(1) sage: w = v.extensions(L); w [[ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation, [ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation] @@ -40,7 +40,7 @@ sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: w = v.extensions(L); w [[ 5-adic valuation, v(t + 2) = 1 ]-adic valuation, [ 5-adic valuation, v(t + 3) = 1 ]-adic valuation] @@ -55,7 +55,7 @@ sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 1/x) + sage: v = K.valuation(1/x) sage: w = v.extension(L); w Valuation at the infinite place sage: w._base_valuation._base_valuation._improve_approximation() @@ -96,8 +96,8 @@ class LimitValuationFactory(UniqueFactory): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) - sage: w = LimitValuation(v, x) + sage: v = GaussValuation(R, QQ.valuation(2)) + sage: w = valuations.LimitValuation(v, x) sage: w(x) +Infinity @@ -112,10 +112,10 @@ def create_key(self, base_valuation, G): easily possible to create the same limit in two different ways:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) - sage: w = LimitValuation(v, x) # indirect doctest + sage: v = GaussValuation(R, QQ.valuation(2)) + sage: w = valuations.LimitValuation(v, x) # indirect doctest sage: v = v.augmentation(x, infinity) - sage: u = LimitValuation(v, x) + sage: u = valuations.LimitValuation(v, x) sage: u == w False @@ -135,8 +135,8 @@ def create_object(self, version, key): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, pAdicValuation(QQ, 2)) - sage: w = LimitValuation(v, x^2 + 1) # indirect doctest + sage: v = GaussValuation(R, QQ.valuation(2)) + sage: w = valuations.LimitValuation(v, x^2 + 1) # indirect doctest """ base_valuation, G = key @@ -159,7 +159,7 @@ class LimitValuation_generic(DiscretePseudoValuation): sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 0) + sage: v = K.valuation(0) sage: w = v.extension(L) sage: w._base_valuation [ Gauss valuation induced by (x)-adic valuation, v(y) = 1/2 , … ] @@ -172,6 +172,7 @@ class LimitValuation_generic(DiscretePseudoValuation): TESTS:: + sage: from sage.rings.valuation.limit_valuation import LimitValuation_generic sage: isinstance(w._base_valuation, LimitValuation_generic) True sage: TestSuite(w._base_valuation).run() # long time @@ -183,7 +184,8 @@ def __init__(self, parent, approximation): sage: R. = QQ[] sage: K. = QQ.extension(x^2 + 1) - sage: v = pAdicValuation(K, 2) + sage: v = K.valuation(2) + sage: from sage.rings.valuation.limit_valuation import LimitValuation_generic sage: isinstance(v._base_valuation, LimitValuation_generic) True @@ -211,7 +213,7 @@ def reduce(self, f, check=True): sage: R. = K[] sage: L. = K.extension(y^2 - (x - 1)) - sage: v = FunctionFieldValuation(K, 0) + sage: v = K.valuation(0) sage: w = v.extension(L) sage: w.reduce(y) # indirect doctest u1 @@ -232,7 +234,7 @@ def _call_(self, f): sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 0) + sage: v = K.valuation(0) sage: w = v.extension(L) sage: w(y) # indirect doctest 1/2 @@ -256,7 +258,7 @@ def _improve_approximation_for_reduce(self, f): For the unique extension over the place at 1337, the initial approximation is sufficient to compute the reduction of ``y``:: - sage: v = FunctionFieldValuation(K, 1337) + sage: v = K.valuation(1337) sage: w = v.extension(L) sage: u = w._base_valuation sage: u._approximation @@ -269,7 +271,7 @@ def _improve_approximation_for_reduce(self, f): However, at a place over 1341, the initial approximation is not sufficient for some values (note that 1341-1337 is a square):: - sage: v = FunctionFieldValuation(K, 1341) + sage: v = K.valuation(1341) sage: w = v.extensions(L)[0] sage: u = w._base_valuation sage: u._approximation @@ -300,7 +302,7 @@ def _improve_approximation_for_call(self, f): For the unique extension over the place at 23, the initial approximation is sufficient to compute all valuations:: - sage: v = FunctionFieldValuation(K, 23) + sage: v = K.valuation(23) sage: w = v.extension(L) sage: u = w._base_valuation sage: u._approximation @@ -331,7 +333,7 @@ def _repr_(self): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: w = v.extension(L) sage: w._base_valuation # indirect doctest [ Gauss valuation induced by 2-adic valuation, v(t + 1) = 1/2 , … ] @@ -362,7 +364,7 @@ class MacLaneLimitValuation(LimitValuation_generic, InfiniteDiscretePseudoValuat sage: R. = QQ[] sage: K. = QQ.extension(x^2 + 1) - sage: v = pAdicValuation(K, 2) + sage: v = K.valuation(2) sage: u = v._base_valuation; u [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/2 , … ] @@ -373,8 +375,9 @@ def __init__(self, parent, approximation, G): sage: R. = QQ[] sage: K. = QQ.extension(x^2 + 1) - sage: v = pAdicValuation(K, 2) + sage: v = K.valuation(2) sage: u = v._base_valuation + sage: from sage.rings.valuation.limit_valuation import MacLaneLimitValuation sage: isinstance(u, MacLaneLimitValuation) True @@ -392,7 +395,7 @@ def extensions(self, ring): EXAMPLES:: - sage: v = pAdicValuation(GaussianIntegers(), 2) + sage: v = GaussianIntegers().valuation(2) sage: u = v._base_valuation sage: u.extensions(QQ['x']) [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/2 , … ]] @@ -421,7 +424,7 @@ def lift(self, F): sage: R. = K[] sage: L. = K.extension(y^4 - x^2 - 2*x - 1) - sage: v = FunctionFieldValuation(K, 1) + sage: v = K.valuation(1) sage: w = v.extensions(L)[0]; w [ (x - 1)-adic valuation, v(y^2 - 2) = 1 ]-adic valuation sage: s = w.reduce(y); s @@ -443,7 +446,7 @@ def uniformizer(self): sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 0) + sage: v = K.valuation(0) sage: w = v.extension(L) sage: w.uniformizer() # indirect doctest y @@ -459,20 +462,20 @@ def _call_(self, f): sage: K = QQ sage: R. = K[] - sage: vK = pAdicValuation(K, 2) + sage: vK = K.valuation(2) sage: f = (x^2 + 7) * (x^2 + 9) sage: V = vK.mac_lane_approximants(f, require_incomparability=True) sage: V = sorted(V, key=str) - sage: w = LimitValuation(V[0], f) + sage: w = valuations.LimitValuation(V[0], f) sage: w((x^2 + 7) * (x + 3)) 3/2 - sage: w = LimitValuation(V[1], f) + sage: w = valuations.LimitValuation(V[1], f) sage: w((x^2 + 7) * (x + 3)) +Infinity - sage: w = LimitValuation(V[2], f) + sage: w = valuations.LimitValuation(V[2], f) sage: w((x^2 + 7) * (x + 3)) +Infinity @@ -492,7 +495,7 @@ def _improve_approximation(self): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: w = v.extension(L) sage: u = w._base_valuation sage: u._approximation @@ -534,7 +537,7 @@ def _improve_approximation_for_call(self, f): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: w = v.extensions(L)[0] sage: u = w._base_valuation sage: u._approximation @@ -611,7 +614,7 @@ def _improve_approximation_for_reduce(self, f): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 13) + sage: v = QQ.valuation(13) sage: w = v.extensions(L)[0] sage: u = w._base_valuation sage: u._approximation @@ -640,7 +643,7 @@ def residue_ring(self): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: w = v.extension(L) sage: w.residue_ring() Finite Field of size 2 @@ -666,13 +669,13 @@ def _ge_(self, other): sage: R. = QQ[] sage: F = (x^2 + 7) * (x^2 + 9) sage: G = (x^2 + 7) - sage: V = pAdicValuation(QQ, 2).mac_lane_approximants(F, require_incomparability=True) + sage: V = QQ.valuation(2).mac_lane_approximants(F, require_incomparability=True) sage: V = sorted(V, key=str) - sage: LimitValuation(V[0], F) >= LimitValuation(V[1], F) + sage: valuations.LimitValuation(V[0], F) >= valuations.LimitValuation(V[1], F) False - sage: LimitValuation(V[1], F) >= LimitValuation(V[1], G) + sage: valuations.LimitValuation(V[1], F) >= valuations.LimitValuation(V[1], G) True - sage: LimitValuation(V[2], F) >= LimitValuation(V[2], G) + sage: valuations.LimitValuation(V[2], F) >= valuations.LimitValuation(V[2], G) True """ @@ -709,7 +712,7 @@ def restriction(self, ring): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: w = v.extension(L) sage: w._base_valuation.restriction(K) 2-adic valuation @@ -731,9 +734,9 @@ def _weakly_separating_element(self, other): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: w = v.extension(L) - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: u,uu = v.extensions(L) sage: w._base_valuation._weakly_separating_element(u._base_valuation) # long time 2 @@ -741,13 +744,13 @@ def _weakly_separating_element(self, other): t + 2 sage: K. = FunctionField(QQ) - sage: v = FunctionFieldValuation(K, 1/x) + sage: v = K.valuation(1/x) sage: R. = K[] sage: L. = K.extension(y^2 - 1/(x^2 + 1)) sage: u,uu = v.extensions(L) - sage: v = FunctionFieldValuation(K, x) + sage: v = K.valuation(x) sage: w,ww = v.extensions(L) - sage: v = FunctionFieldValuation(K, 1) + sage: v = K.valuation(1) sage: v = v.extension(L) sage: u.separating_element([uu,ww,w,v]) # long time, random output ((8*x^4 + 12*x^2 + 4)/(x^2 - x))*y + (8*x^4 + 8*x^2 + 1)/(x^3 - x^2) @@ -787,7 +790,7 @@ def value_semigroup(self): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: u,uu = v.extensions(L) sage: u.value_semigroup() Additive Abelian Semigroup generated by -1, 1 @@ -804,7 +807,7 @@ def element_with_valuation(self, s): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: u = v.extension(L) sage: u.element_with_valuation(1/2) t + 1 @@ -829,7 +832,7 @@ def _relative_size(self, f): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: u = v.extension(L) sage: u._relative_size(1024*t + 1024) 11 @@ -850,7 +853,7 @@ def simplify(self, f, error=None, force=False): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: u = v.extension(L) sage: u.simplify(t + 1024, force=True) t @@ -879,7 +882,7 @@ def lower_bound(self, f): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: u = v.extension(L) sage: u.lower_bound(1024*t + 1024) 10 @@ -902,7 +905,7 @@ def upper_bound(self, f): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: u = v.extension(L) sage: u.upper_bound(1024*t + 1024) 21/2 @@ -926,7 +929,7 @@ def is_negative_pseudo_valuation(self): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: u = v.extension(L) sage: u.is_negative_pseudo_valuation() False diff --git a/src/sage/rings/valuation/mapped_valuation.py b/src/sage/rings/valuation/mapped_valuation.py index 86e3afd9271..4f69d398e03 100644 --- a/src/sage/rings/valuation/mapped_valuation.py +++ b/src/sage/rings/valuation/mapped_valuation.py @@ -11,7 +11,7 @@ sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 0) + sage: v = K.valuation(0) sage: w = v.extension(L); w (x)-adic valuation @@ -24,7 +24,7 @@ """ #***************************************************************************** -# Copyright (C) 2016 Julian Rüth +# Copyright (C) 2016-2017 Julian Rüth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -44,7 +44,7 @@ class MappedValuation_base(DiscretePseudoValuation): sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 0) + sage: v = K.valuation(0) sage: w = v.extension(L); w (x)-adic valuation @@ -61,9 +61,10 @@ def __init__(self, parent, base_valuation): sage: R. = K[] sage: L. = K.extension(y^2 - x^2 + 1) - sage: v = FunctionFieldValuation(K, 0) + sage: v = K.valuation(0) sage: w = v.extension(L); w (x)-adic valuation + sage: from sage.rings.valuation.mapped_valuation import MappedValuation_base sage: isinstance(w, MappedValuation_base) True @@ -84,7 +85,7 @@ def _repr_(self): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) + sage: v = valuations.pAdicValuation(QQ, 2) sage: v.extension(L) # indirect doctest 2-adic valuation @@ -99,7 +100,7 @@ def residue_ring(self): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) + sage: v = valuations.pAdicValuation(QQ, 2) sage: v.extension(L).residue_ring() Finite Field of size 2 @@ -115,7 +116,7 @@ def uniformizer(self): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) + sage: v = valuations.pAdicValuation(QQ, 2) sage: v.extension(L).uniformizer() t + 1 @@ -132,7 +133,7 @@ def _to_base_domain(self, f): sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 0) + sage: v = K.valuation(0) sage: w = v.extensions(L)[0] sage: w._to_base_domain(y).parent() Univariate Polynomial Ring in y over Rational function field in x over Rational Field @@ -150,7 +151,7 @@ def _from_base_domain(self, f): sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 0) + sage: v = K.valuation(0) sage: w = v.extension(L) sage: w._from_base_domain(w._base_valuation.domain().gen()).parent() Function field in y defined by y^2 - x @@ -168,7 +169,7 @@ def _call_(self, f): sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 0) + sage: v = K.valuation(0) sage: w = v.extension(L) sage: w(y) # indirect doctest 1/2 @@ -186,7 +187,7 @@ def reduce(self, f): sage: R. = K[] sage: L. = K.extension(y^2 - (x - 2)) - sage: v = FunctionFieldValuation(K, 0) + sage: v = K.valuation(0) sage: w = v.extension(L) sage: w.reduce(y) u1 @@ -205,7 +206,7 @@ def lift(self, F): sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 2) + sage: v = K.valuation(2) sage: w = v.extension(L) sage: w.lift(w.residue_field().gen()) y @@ -227,7 +228,7 @@ def _to_base_residue_ring(self, F): sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 0) + sage: v = K.valuation(0) sage: w = v.extensions(L)[0] sage: w._to_base_residue_ring(1) 1 @@ -246,7 +247,7 @@ def _from_base_residue_ring(self, F): sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 0) + sage: v = K.valuation(0) sage: w = v.extensions(L)[0] sage: w._from_base_residue_ring(1) 1 @@ -265,7 +266,7 @@ def _test_to_from_base_domain(self, **options): sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 0) + sage: v = K.valuation(0) sage: w = v.extensions(L)[0] sage: w._test_to_from_base_domain() @@ -297,7 +298,7 @@ class FiniteExtensionFromInfiniteValuation(MappedValuation_base, DiscreteValuati sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 0) + sage: v = K.valuation(0) sage: w = v.extension(L); w (x)-adic valuation @@ -310,8 +311,9 @@ def __init__(self, parent, base_valuation): sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 0) + sage: v = K.valuation(0) sage: w = v.extension(L) + sage: from sage.rings.valuation.mapped_valuation import FiniteExtensionFromInfiniteValuation sage: isinstance(w, FiniteExtensionFromInfiniteValuation) True sage: TestSuite(w).run() # long time @@ -329,7 +331,7 @@ def _eq_(self, other): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) + sage: v = valuations.pAdicValuation(QQ, 2) sage: w = v.extension(L) sage: ww = v.extension(L) sage: w == ww # indirect doctest @@ -347,7 +349,7 @@ def restriction(self, ring): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) + sage: v = valuations.pAdicValuation(QQ, 2) sage: w = v.extension(L) sage: w.restriction(K) is v True @@ -369,9 +371,9 @@ def _weakly_separating_element(self, other): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) + sage: v = valuations.pAdicValuation(QQ, 2) sage: w = v.extension(L) - sage: v = pAdicValuation(QQ, 5) + sage: v = valuations.pAdicValuation(QQ, 5) sage: u,uu = v.extensions(L) sage: u.separating_element([w,uu]) # indirect doctest 1/20*t + 7/20 @@ -398,7 +400,7 @@ def _relative_size(self, x): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 2) + sage: v = valuations.pAdicValuation(QQ, 2) sage: w = v.extension(L) sage: w._relative_size(1024*t + 1024) 11 @@ -419,7 +421,7 @@ def simplify(self, x, error=None, force=False): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 5) + sage: v = valuations.pAdicValuation(QQ, 5) sage: u,uu = v.extensions(L) sage: f = 125*t + 1 sage: u.simplify(f, error=u(f), force=True) @@ -445,7 +447,7 @@ def lower_bound(self, x): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 5) + sage: v = valuations.pAdicValuation(QQ, 5) sage: u,uu = v.extensions(L) sage: u.lower_bound(t + 2) 0 @@ -468,7 +470,7 @@ def upper_bound(self, x): sage: K = QQ sage: R. = K[] sage: L. = K.extension(t^2 + 1) - sage: v = pAdicValuation(QQ, 5) + sage: v = valuations.pAdicValuation(QQ, 5) sage: u,uu = v.extensions(L) sage: u.upper_bound(t + 2) 3 @@ -490,7 +492,7 @@ class FiniteExtensionFromLimitValuation(FiniteExtensionFromInfiniteValuation): sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 1) + sage: v = K.valuation(1) sage: w = v.extensions(L); w [[ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation, [ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation] @@ -511,9 +513,10 @@ def __init__(self, parent, approximant, G, approximants): sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^2 - x) - sage: v = FunctionFieldValuation(K, 2) + sage: v = K.valuation(2) sage: w = v.extension(L); w (x - 2)-adic valuation + sage: from sage.rings.valuation.mapped_valuation import FiniteExtensionFromLimitValuation sage: isinstance(w, FiniteExtensionFromLimitValuation) True @@ -533,7 +536,7 @@ def _repr_(self): EXAMPLES:: - sage: pAdicValuation(GaussianIntegers().fraction_field(), 2) # indirect doctest + sage: valuations.pAdicValuation(GaussianIntegers().fraction_field(), 2) # indirect doctest 2-adic valuation """ diff --git a/src/sage/rings/valuation/scaled_valuation.py b/src/sage/rings/valuation/scaled_valuation.py index bd665ce3f7f..64aa4e71db3 100644 --- a/src/sage/rings/valuation/scaled_valuation.py +++ b/src/sage/rings/valuation/scaled_valuation.py @@ -4,7 +4,7 @@ EXAMPLES: - sage: 3*pAdicValuation(ZZ, 3) + sage: 3*ZZ.valuation(3) 3 * 3-adic valuation AUTHORS: @@ -13,7 +13,7 @@ """ #***************************************************************************** -# Copyright (C) 2016 Julian Rüth +# Copyright (C) 2016-2017 Julian Rüth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -30,7 +30,7 @@ class ScaledValuationFactory(UniqueFactory): EXAMPLES:: - sage: 3*pAdicValuation(ZZ, 2) # indirect doctest + sage: 3*ZZ.valuation(2) # indirect doctest 3 * 2-adic valuation """ @@ -40,7 +40,7 @@ def create_key(self, base, s): TESTS:: - sage: 3*pAdicValuation(ZZ, 2) is 2*(3/2*pAdicValuation(ZZ, 2)) # indirect doctest + sage: 3*ZZ.valuation(2) is 2*(3/2*ZZ.valuation(2)) # indirect doctest True """ @@ -70,7 +70,7 @@ def create_object(self, version, key): TESTS:: - sage: 3*pAdicValuation(ZZ, 2) # indirect doctest + sage: 3*ZZ.valuation(2) # indirect doctest 3 * 2-adic valuation """ @@ -91,7 +91,7 @@ class ScaledValuation_generic(DiscreteValuation): EXAMPLES:: - sage: v = 3*pAdicValuation(ZZ, 3); v + sage: v = 3*ZZ.valuation(3); v 3 * 3-adic valuation TESTS:: @@ -103,7 +103,8 @@ def __init__(self, parent, base_valuation, s): r""" TESTS:: - sage: v = 3*pAdicValuation(ZZ, 2) + sage: v = 3*ZZ.valuation(2) + sage: from sage.rings.valuation.scaled_valuation import ScaledValuation_generic sage: isinstance(v, ScaledValuation_generic) True @@ -119,7 +120,7 @@ def _repr_(self): EXAMPLES:: - sage: 3*pAdicValuation(ZZ, 2) # indirect doctest + sage: 3*ZZ.valuation(2) # indirect doctest 3 * 2-adic valuation """ @@ -131,7 +132,7 @@ def residue_ring(self): EXAMPLES:: - sage: v = 3*pAdicValuation(ZZ, 2) + sage: v = 3*ZZ.valuation(2) sage: v.residue_ring() Finite Field of size 2 @@ -144,7 +145,7 @@ def uniformizer(self): EXAMPLES:: - sage: v = 3*pAdicValuation(ZZ, 2) + sage: v = 3*ZZ.valuation(2) sage: v.uniformizer() 2 @@ -157,7 +158,7 @@ def _call_(self, f): EXAMPLES:: - sage: v = 3*pAdicValuation(ZZ, 2) + sage: v = 3*ZZ.valuation(2) sage: v(2) 3 @@ -170,7 +171,7 @@ def reduce(self, f): EXAMPLES:: - sage: v = 3*pAdicValuation(ZZ, 2) + sage: v = 3*ZZ.valuation(2) sage: v.reduce(1) 1 @@ -184,7 +185,7 @@ def lift(self, F): EXAMPLES:: - sage: v = 3*pAdicValuation(ZZ, 2) + sage: v = 3*ZZ.valuation(2) sage: v.lift(1) 1 @@ -197,7 +198,7 @@ def extensions(self, ring): EXAMPLES:: - sage: v = 3*pAdicValuation(ZZ, 5) + sage: v = 3*ZZ.valuation(5) sage: v.extensions(GaussianIntegers().fraction_field()) [3 * [ 5-adic valuation, v(x + 2) = 1 ]-adic valuation, 3 * [ 5-adic valuation, v(x + 3) = 1 ]-adic valuation] @@ -211,7 +212,7 @@ def restriction(self, ring): EXAMPLES:: - sage: v = 3*pAdicValuation(QQ, 5) + sage: v = 3*QQ.valuation(5) sage: v.restriction(ZZ) 3 * 5-adic valuation @@ -226,8 +227,8 @@ def _strictly_separating_element(self, other): EXAMPLES:: - sage: v2 = pAdicValuation(QQ, 2) - sage: v3 = 12 * pAdicValuation(QQ, 3) + sage: v2 = QQ.valuation(2) + sage: v3 = 12 * QQ.valuation(3) sage: v2._strictly_separating_element(v3) 2/3 @@ -252,8 +253,8 @@ def _weakly_separating_element(self, other): EXAMPLES:: - sage: v2 = pAdicValuation(QQ, 2) - sage: v3 = 12 * pAdicValuation(QQ, 3) + sage: v2 = QQ.valuation(2) + sage: v3 = 12 * QQ.valuation(3) sage: v2._weakly_separating_element(v3) 2 @@ -267,7 +268,7 @@ def _ge_(self, other): EXAMPLES:: - sage: v2 = pAdicValuation(QQ, 2) + sage: v2 = QQ.valuation(2) sage: 2*v2 >= v2 True sage: v2/2 >= 2*v2 @@ -302,7 +303,7 @@ def _le_(self, other): EXAMPLES:: - sage: v2 = pAdicValuation(QQ, 2) + sage: v2 = QQ.valuation(2) sage: 2*v2 <= v2 False sage: v2/2 <= 2*v2 @@ -325,7 +326,7 @@ def value_semigroup(self): EXAMPLES:: - sage: v2 = pAdicValuation(QQ, 2) + sage: v2 = QQ.valuation(2) sage: (2*v2).value_semigroup() Additive Abelian Semigroup generated by -2, 2 diff --git a/src/sage/rings/valuation/trivial_valuation.py b/src/sage/rings/valuation/trivial_valuation.py index d50078fdf78..1f21fa26ea2 100644 --- a/src/sage/rings/valuation/trivial_valuation.py +++ b/src/sage/rings/valuation/trivial_valuation.py @@ -4,7 +4,7 @@ EXAMPLES:: - sage: v = TrivialValuation(QQ); v + sage: v = valuations.TrivialValuation(QQ); v Trivial valuation on Rational Field sage: v(1) 0 @@ -15,6 +15,8 @@ directly since this gives the wrong inheritance structure on the resulting objects:: + sage: from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace + sage: from sage.rings.valuation.trivial_valuation import TrivialDiscretePseudoValuation sage: H = DiscretePseudoValuationSpace(QQ) sage: v = TrivialDiscretePseudoValuation(H) sage: v._test_category() @@ -25,13 +27,14 @@ Instead, the valuations need to be created through the ``__make_element_class__`` of the containing space:: + sage: from sage.rings.valuation.trivial_valuation import TrivialDiscretePseudoValuation sage: v = H.__make_element_class__(TrivialDiscretePseudoValuation)(H) sage: v._test_category() The factories ``TrivialValuation`` and ``TrivialPseudoValuation`` provide the right inheritance structure:: - sage: v = TrivialPseudoValuation(QQ) + sage: v = valuations.TrivialPseudoValuation(QQ) sage: v._test_category() AUTHORS: @@ -40,7 +43,7 @@ """ #***************************************************************************** -# Copyright (C) 2016 Julian Rüth +# Copyright (C) 2016-2017 Julian Rüth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -57,7 +60,7 @@ class TrivialValuationFactory(UniqueFactory): EXAMPLES:: - sage: v = TrivialValuation(QQ); v + sage: v = valuations.TrivialValuation(QQ); v Trivial valuation on Rational Field sage: v(1) 0 @@ -67,7 +70,8 @@ def __init__(self, clazz, parent, *args, **kwargs): r""" TESTS:: - sage: isinstance(TrivialValuation, TrivialValuationFactory) + sage: from sage.rings.valuation.trivial_valuation import TrivialValuationFactory + sage: isinstance(valuations.TrivialValuation, TrivialValuationFactory) True """ @@ -81,7 +85,7 @@ def create_key(self, domain): EXAMPLES:: - sage: TrivialValuation(QQ) is TrivialValuation(QQ) # indirect doctest + sage: valuations.TrivialValuation(QQ) is valuations.TrivialValuation(QQ) # indirect doctest True """ @@ -93,7 +97,7 @@ def create_object(self, version, key, **extra_args): EXAMPLES:: - sage: TrivialValuation(QQ) # indirect doctest + sage: valuations.TrivialValuation(QQ) # indirect doctest Trivial valuation on Rational Field """ @@ -107,7 +111,7 @@ class TrivialDiscretePseudoValuation_base(DiscretePseudoValuation): EXAMPLES:: - sage: v = TrivialPseudoValuation(ZZ); v + sage: v = valuations.TrivialPseudoValuation(ZZ); v Trivial pseudo-valuation on Integer Ring TESTS:: @@ -121,7 +125,7 @@ def uniformizer(self): EXAMPLES:: - sage: v = TrivialPseudoValuation(ZZ) + sage: v = valuations.TrivialPseudoValuation(ZZ) sage: v.uniformizer() Traceback (most recent call last): ... @@ -136,7 +140,7 @@ def is_trivial(self): EXAMPLES:: - sage: v = TrivialPseudoValuation(QQ) + sage: v = valuations.TrivialPseudoValuation(QQ) sage: v.is_trivial() True @@ -149,7 +153,7 @@ def is_negative_pseudo_valuation(self): EXAMPLES: - sage: v = TrivialPseudoValuation(QQ) + sage: v = valuations.TrivialPseudoValuation(QQ) sage: v.is_negative_pseudo_valuation() False @@ -162,7 +166,7 @@ class TrivialDiscretePseudoValuation(TrivialDiscretePseudoValuation_base, Infini EXAMPLES:: - sage: v = TrivialPseudoValuation(QQ); v + sage: v = valuations.TrivialPseudoValuation(QQ); v Trivial pseudo-valuation on Rational Field TESTS:: @@ -174,7 +178,8 @@ def __init__(self, parent): r""" TESTS:: - sage: v = TrivialPseudoValuation(QQ) + sage: from sage.rings.valuation.trivial_valuation import TrivialDiscretePseudoValuation + sage: v = valuations.TrivialPseudoValuation(QQ) sage: isinstance(v, TrivialDiscretePseudoValuation) True @@ -188,7 +193,7 @@ def _call_(self, x): EXAMPLES:: - sage: v = TrivialPseudoValuation(QQ) + sage: v = valuations.TrivialPseudoValuation(QQ) sage: v(0) +Infinity sage: v(1) @@ -204,7 +209,7 @@ def _repr_(self): EXAMPLES:: - sage: TrivialPseudoValuation(QQ) # indirect doctest + sage: valuations.TrivialPseudoValuation(QQ) # indirect doctest Trivial pseudo-valuation on Rational Field """ @@ -218,7 +223,7 @@ def value_group(self): A trivial discrete pseudo-valuation has no value group:: - sage: v = TrivialPseudoValuation(QQ) + sage: v = valuations.TrivialPseudoValuation(QQ) sage: v.value_group() Traceback (most recent call last): ... @@ -233,7 +238,7 @@ def residue_ring(self): EXAMPLES:: - sage: TrivialPseudoValuation(QQ).residue_ring() + sage: valuations.TrivialPseudoValuation(QQ).residue_ring() Quotient of Rational Field by the ideal (1) """ @@ -245,7 +250,7 @@ def reduce(self, x): EXAMPLES:: - sage: v = TrivialPseudoValuation(QQ) + sage: v = valuations.TrivialPseudoValuation(QQ) sage: v.reduce(1) 0 @@ -259,7 +264,7 @@ def lift(self, X): EXAMPLES:: - sage: v = TrivialPseudoValuation(QQ) + sage: v = valuations.TrivialPseudoValuation(QQ) sage: v.lift(v.residue_ring().zero()) 0 @@ -274,8 +279,8 @@ def _ge_(self, other): EXAMPLES:: - sage: v = TrivialPseudoValuation(QQ) - sage: w = TrivialValuation(QQ) + sage: v = valuations.TrivialPseudoValuation(QQ) + sage: w = valuations.TrivialValuation(QQ) sage: v >= w True @@ -289,7 +294,7 @@ class TrivialDiscreteValuation(TrivialDiscretePseudoValuation_base, DiscreteValu EXAMPLES:: - sage: v = TrivialValuation(QQ); v + sage: v = valuations.TrivialValuation(QQ); v Trivial valuation on Rational Field TESTS:: @@ -301,7 +306,8 @@ def __init__(self, parent): r""" TESTS:: - sage: v = TrivialValuation(QQ) + sage: from sage.rings.valuation.trivial_valuation import TrivialDiscreteValuation + sage: v = valuations.TrivialValuation(QQ) sage: isinstance(v, TrivialDiscreteValuation) True @@ -315,7 +321,7 @@ def _call_(self, x): EXAMPLES:: - sage: v = TrivialValuation(QQ) + sage: v = valuations.TrivialValuation(QQ) sage: v(0) +Infinity sage: v(1) @@ -331,7 +337,7 @@ def _repr_(self): EXAMPLES:: - sage: TrivialValuation(QQ) # indirect doctest + sage: valuations.TrivialValuation(QQ) # indirect doctest Trivial valuation on Rational Field """ @@ -345,7 +351,7 @@ def value_group(self): A trivial discrete valuation has a trivial value group:: - sage: v = TrivialValuation(QQ) + sage: v = valuations.TrivialValuation(QQ) sage: v.value_group() Trivial Additive Abelian Group @@ -359,7 +365,7 @@ def residue_ring(self): EXAMPLES:: - sage: TrivialValuation(QQ).residue_ring() + sage: valuations.TrivialValuation(QQ).residue_ring() Rational Field """ @@ -371,7 +377,7 @@ def reduce(self, x): EXAMPLES:: - sage: v = TrivialValuation(QQ) + sage: v = valuations.TrivialValuation(QQ) sage: v.reduce(1) 1 @@ -384,7 +390,7 @@ def lift(self, X): EXAMPLES:: - sage: v = TrivialValuation(QQ) + sage: v = valuations.TrivialValuation(QQ) sage: v.lift(v.residue_ring().zero()) 0 @@ -397,12 +403,13 @@ def extensions(self, ring): EXAMPLES:: - sage: v = TrivialValuation(ZZ) + sage: v = valuations.TrivialValuation(ZZ) sage: v.extensions(QQ) [Trivial valuation on Rational Field] """ if self.domain().is_subring(ring): + from sage.rings.valuation.trivial_valuation import TrivialValuation return [TrivialValuation(ring)] return super(DiscretePseudoValuation, self).extensions(ring) @@ -413,8 +420,8 @@ def _ge_(self, other): EXAMPLES:: - sage: v = TrivialPseudoValuation(QQ) - sage: w = TrivialValuation(QQ) + sage: v = valuations.TrivialPseudoValuation(QQ) + sage: w = valuations.TrivialValuation(QQ) sage: w >= v False diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index c852138cc51..9fc245ac927 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -10,7 +10,7 @@ """ #***************************************************************************** -# Copyright (C) 2013-2016 Julian Rüth +# Copyright (C) 2013-2017 Julian Rüth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -32,7 +32,7 @@ class DiscretePseudoValuation(Morphism): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 2); v # indirect doctest + sage: v = ZZ.valuation(2); v # indirect doctest 2-adic valuation TESTS:: @@ -44,7 +44,8 @@ def __init__(self, parent): r""" TESTS:: - sage: isinstance(pAdicValuation(ZZ, 2), DiscretePseudoValuation) + sage: from sage.rings.valuation.valuation import DiscretePseudoValuation + sage: isinstance(ZZ.valuation(2), DiscretePseudoValuation) True """ @@ -56,7 +57,7 @@ def is_equivalent(self, f, g): EXAMPLES:: - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: v.is_equivalent(2, 1) False sage: v.is_equivalent(2, -2) @@ -84,7 +85,7 @@ def __hash__(self): EXAMPLES:: - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: hash(v) == hash(v) # indirect doctest True @@ -105,7 +106,7 @@ def _hash_(self): EXAMPLES:: - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: hash(v) == hash(v) # indirect doctest True @@ -125,7 +126,7 @@ def _cmp_(self, other): when they can fall back to the implementation through ``>=`` and ``<=``:: - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: v > v False @@ -135,7 +136,7 @@ def _cmp_(self, other): not coerce into a common parent, a rather random comparison of ``id`` happens:: - sage: w = TrivialValuation(GF(2)) + sage: w = valuations.TrivialValuation(GF(2)) sage: w < v # random output True sage: v < w # random output @@ -159,12 +160,12 @@ def _richcmp_(self, other, op): EXAMPLES:: - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: v == v True sage: v != v False - sage: w = pAdicValuation(QQ, 3) + sage: w = QQ.valuation(3) sage: v == w False sage: v != w @@ -176,7 +177,7 @@ def _richcmp_(self, other, op): do not coerce into a common parent, a rather random comparison of ``id`` happens:: - sage: w = TrivialValuation(GF(2)) + sage: w = valuations.TrivialValuation(GF(2)) sage: w <= v # random output True sage: v <= w # random output @@ -214,7 +215,7 @@ def _eq_(self, other): EXAMPLES:: - sage: v = TrivialValuation(QQ) + sage: v = valuations.TrivialValuation(QQ) sage: v == v True @@ -231,8 +232,8 @@ def _le_(self, other): EXAMPLES:: - sage: v = TrivialValuation(QQ) - sage: w = pAdicValuation(QQ, 2) + sage: v = valuations.TrivialValuation(QQ) + sage: w = QQ.valuation(2) sage: v <= w True @@ -242,7 +243,7 @@ def _le_(self, other): do not coerce into a common parent, a rather random comparison of ``id`` happens:: - sage: w = TrivialValuation(GF(2)) + sage: w = valuations.TrivialValuation(GF(2)) sage: w <= v # random output True sage: v <= w # random output @@ -261,8 +262,8 @@ def _ge_(self, other): EXAMPLES:: - sage: v = TrivialValuation(QQ) - sage: w = pAdicValuation(QQ, 2) + sage: v = valuations.TrivialValuation(QQ) + sage: w = QQ.valuation(2) sage: v >= w False @@ -272,7 +273,7 @@ def _ge_(self, other): do not coerce into a common parent, a rather random comparison of ``id`` happens:: - sage: w = TrivialValuation(GF(2)) + sage: w = valuations.TrivialValuation(GF(2)) sage: w <= v # random output True sage: v <= w # random output @@ -299,7 +300,7 @@ def _test_valuation_inheritance(self, **options): EXAMPLES:: - sage: pAdicValuation(QQ, 2)._test_valuation_inheritance() + sage: QQ.valuation(2)._test_valuation_inheritance() """ tester = self._tester(**options) @@ -312,7 +313,7 @@ class InfiniteDiscretePseudoValuation(DiscretePseudoValuation): EXAMPLES:: - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: R. = QQ[] sage: v = GaussValuation(R, v) sage: w = v.augmentation(x, infinity); w # indirect doctest @@ -320,6 +321,7 @@ class InfiniteDiscretePseudoValuation(DiscretePseudoValuation): TESTS:: + sage: from sage.rings.valuation.valuation import InfiniteDiscretePseudoValuation sage: isinstance(w, InfiniteDiscretePseudoValuation) True sage: TestSuite(w).run() # long time @@ -331,7 +333,7 @@ def is_discrete_valuation(self): EXAMPLES:: - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: R. = QQ[] sage: v = GaussValuation(R, v) sage: w = v.augmentation(x, infinity) @@ -350,9 +352,9 @@ class NegativeInfiniteDiscretePseudoValuation(InfiniteDiscretePseudoValuation): EXAMPLES: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)).augmentation(x, infinity) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)).augmentation(x, infinity) sage: K. = FunctionField(QQ) - sage: w = FunctionFieldValuation(K, v) + sage: w = K.valuation(v) TESTS:: @@ -366,9 +368,9 @@ def is_negative_pseudo_valuation(self): EXAMPLES:: sage: R. = QQ[] - sage: v = GaussValuation(R, TrivialValuation(QQ)).augmentation(x, infinity) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)).augmentation(x, infinity) sage: K. = FunctionField(QQ) - sage: w = FunctionFieldValuation(K, v) + sage: w = K.valuation(v) sage: w.is_negative_pseudo_valuation() True @@ -382,7 +384,7 @@ class DiscreteValuation(DiscretePseudoValuation): EXAMPLES:: - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: R. = QQ[] sage: v = GaussValuation(R, v) sage: w = v.augmentation(x, 1337); w # indirect doctest @@ -390,6 +392,7 @@ class DiscreteValuation(DiscretePseudoValuation): TESTS:: + sage: from sage.rings.valuation.valuation import DiscreteValuation sage: isinstance(w, DiscreteValuation) True sage: TestSuite(w).run() # long time @@ -401,7 +404,7 @@ def is_discrete_valuation(self): EXAMPLES:: - sage: v = TrivialValuation(ZZ) + sage: v = valuations.TrivialValuation(ZZ) sage: v.is_discrete_valuation() True @@ -453,7 +456,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru EXAMPLES:: - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: R. = QQ[] sage: v.mac_lane_approximants(x^2 + 1) [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/2 ]] @@ -486,13 +489,13 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: R. = K[] sage: F = y^21 + x*y^20 + (x^3 + x + 1)*y^18 + (x^3 + 1)*y^17 + (x^4 + x)*y^16 + (x^7 + x^6 + x^3 + x + 1)*y^15 + x^7*y^14 + (x^8 + x^7 + x^6 + x^4 + x^3 + 1)*y^13 + (x^9 + x^8 + x^4 + 1)*y^12 + (x^11 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2)*y^11 + (x^12 + x^9 + x^8 + x^7 + x^5 + x^3 + x + 1)*y^10 + (x^14 + x^13 + x^10 + x^9 + x^8 + x^7 + x^6 + x^3 + x^2 + 1)*y^9 + (x^13 + x^9 + x^8 + x^6 + x^4 + x^3 + x)*y^8 + (x^16 + x^15 + x^13 + x^12 + x^11 + x^7 + x^3 + x)*y^7 + (x^17 + x^16 + x^13 + x^9 + x^8 + x)*y^6 + (x^17 + x^16 + x^12 + x^7 + x^5 + x^2 + x + 1)*y^5 + (x^19 + x^16 + x^15 + x^12 + x^6 + x^5 + x^3 + 1)*y^4 + (x^18 + x^15 + x^12 + x^10 + x^9 + x^7 + x^4 + x)*y^3 + (x^22 + x^21 + x^20 + x^18 + x^13 + x^12 + x^9 + x^8 + x^7 + x^5 + x^4 + x^3)*y^2 + (x^23 + x^22 + x^20 + x^17 + x^15 + x^14 + x^12 + x^9)*y + x^25 + x^23 + x^19 + x^17 + x^15 + x^13 + x^11 + x^5 sage: x = K._ring.gen() - sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).augmentation(x,1)) + sage: v0 = K.valuation(valuations.GaussValuation(K._ring, valuations.TrivialValuation(k)).augmentation(x,1)) sage: v0.mac_lane_approximants(F, assume_squarefree=True) # optional: integrated; assumes squarefree for speed [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x + 1) = 3/2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y) = 4/3, v(y^3 + x^4) = 13/3 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x) = 2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y^15 + y^13 + (x + 1)*y^12 + x*y^11 + (x + 1)*y^10 + y^9 + y^8 + x*y^6 + x*y^5 + y^4 + y^3 + y^2 + (x + 1)*y + x + 1) = 2 ]] - sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).augmentation(x+1,1)) + sage: v0 = K.valuation(valuations.GaussValuation(K._ring, valuations.TrivialValuation(k)).augmentation(x+1,1)) sage: v0.mac_lane_approximants(F, assume_squarefree=True) # optional: integrated; assumes squarefree for speed [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y) = 7/2, v(y^2 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1) = 15/2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y + x^2 + 1) = 7/2 ], @@ -512,7 +515,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: K. = FunctionField(QQ) sage: S. = K[] sage: F = y^2 - x^2 - x^3 - 3 - sage: v0 = GaussValuation(K._ring,pAdicValuation(QQ,3)) + sage: v0 = GaussValuation(K._ring, QQ.valuation(3)) sage: v1 = v0.augmentation(K._ring.gen(),1/3) sage: mu0 = FunctionFieldValuation(K, v1) sage: sorted(mu0.mac_lane_approximants(F), key=str) @@ -522,7 +525,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru Over a complete base field:: sage: k=Qp(2,10) - sage: v = pAdicValuation(k) + sage: v = k.valuation() sage: R.=k[] sage: G = x @@ -546,17 +549,17 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru The factorization of primes in the Gaussian integers can be read off the Mac Lane approximants:: - sage: v0 = pAdicValuation(QQ, 2) + sage: v0 = QQ.valuation(2) sage: R. = QQ[] sage: G = x^2 + 1 sage: v0.mac_lane_approximants(G) [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/2 ]] - sage: v0 = pAdicValuation(QQ, 3) + sage: v0 = QQ.valuation(3) sage: v0.mac_lane_approximants(G) [[ Gauss valuation induced by 3-adic valuation, v(x^2 + 1) = +Infinity ]] - sage: v0 = pAdicValuation(QQ, 5) + sage: v0 = QQ.valuation(5) sage: v0.mac_lane_approximants(G) [[ Gauss valuation induced by 5-adic valuation, v(x + 2) = 1 ], [ Gauss valuation induced by 5-adic valuation, v(x + 3) = 1 ]] @@ -569,7 +572,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru read off the Mac Lane approximants:: sage: k=Qp(5,4) - sage: v = pAdicValuation(k) + sage: v = k.valuation() sage: R.=k[] sage: G = x^2 + 1 sage: v1,v2 = v.mac_lane_approximants(G); sorted([v1,v2], key=str) @@ -595,7 +598,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru This obviously cannot happen over the rationals where we only get an approximate factorization:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: R.=QQ[] sage: G = x^2 + 1 sage: v.mac_lane_approximants(G) @@ -613,13 +616,13 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: alpha = T^3/4 sage: G = 3^3*T^3*(alpha^4 - alpha)^2 - (4*alpha^3 - 1)^3 sage: G = G/G.leading_coefficient() - sage: pAdicValuation(K).mac_lane_approximants(G) # optional: integrated + sage: K.valuation().mac_lane_approximants(G) # optional: integrated [[ Gauss valuation induced by 3-adic valuation, v((1 + O(3^20))*T + (2 + O(3^20))) = 1/9, v((1 + O(3^20))*T^9 + (2*3 + 2*3^2 + O(3^21))*T^8 + (3 + 3^5 + O(3^21))*T^7 + (2*3 + 2*3^2 + 3^3 + 2*3^4 + 2*3^5 + 3^6 + O(3^21))*T^6 + (2*3 + 2*3^2 + 3^4 + 3^6 + 2*3^7 + O(3^21))*T^5 + (3 + 3^2 + 3^3 + 2*3^6 + 2*3^7 + 3^8 + O(3^21))*T^4 + (2*3 + 2*3^2 + 3^3 + 2*3^5 + 2*3^6 + 2*3^7 + 2*3^8 + O(3^21))*T^3 + (2*3 + 2*3^2 + 3^3 + 2*3^4 + 3^5 + 2*3^6 + 2*3^7 + 2*3^8 + O(3^21))*T^2 + (3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^7 + 3^8 + O(3^21))*T + (2 + 2*3 + 2*3^2 + 2*3^4 + 2*3^5 + 3^7 + O(3^20))) = 55/27 ]] A similar example:: sage: R. = QQ[] - sage: v = pAdicValuation(QQ, 3) + sage: v = QQ.valuation(3) sage: G = (x^3 + 3)^3 - 81 sage: v.mac_lane_approximants(G) # optional: integrated [[ Gauss valuation induced by 3-adic valuation, v(x) = 1/3, v(x^3 + 3*x + 3) = 13/9 ]] @@ -631,7 +634,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: K. = NumberField(x^6 + 108) sage: K.is_galois() True - sage: vK = pAdicValuation(QQ, 2).extension(K) + sage: vK = QQ.valuation(2).extension(K) sage: vK(2) 1 sage: vK(theta) @@ -645,7 +648,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru An easy case that produced the wrong error at some point:: sage: R. = QQ[] - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: v.mac_lane_approximants(x^2 - 1/2) Traceback (most recent call last): ... @@ -691,28 +694,7 @@ def is_sufficient(leaf, others): return False return True - # Leaves in the computation of the tree of approximants. Each vertex - # consists of a tuple (v,ef,p,coeffs,vals) where v is an approximant, i.e., a - # valuation, ef is a boolean, p is the parent of this vertex, and - # coeffs and vals are cached values. (Only v and ef are relevant, - # everything else are caches/debug info.) - # The boolean ef denotes whether v already has the final ramification - # index E and residue degree F of this approximant. - # An edge V -- P represents the relation P.v ≤ V.v (pointwise on the - # polynomial ring K[x]) between the valuations. - class Node(object): - def __init__(self, valuation, parent, ef, principal_part_bound, coefficients, valuations): - self.valuation = valuation - self.parent = parent - self.ef = ef - self.principal_part_bound = principal_part_bound - self.coefficients = coefficients - self.valuations = valuations - self.forced_leaf = False - import mac_lane - mac_lane.valuation.Node = Node - - seed = Node(GaussValuation(R,self), None, G.degree() == 1, G.degree(), None, None) + seed = MacLaneApproximantNode(GaussValuation(R,self), None, G.degree() == 1, G.degree(), None, None) seed.forced_leaf = is_sufficient(seed, []) def create_children(node): @@ -722,7 +704,7 @@ def create_children(node): augmentations = node.valuation.mac_lane_step(G, report_degree_bounds_and_caches=True, coefficients=node.coefficients, valuations=node.valuations, check=False, principal_part_bound=node.principal_part_bound) for w, bound, principal_part_bound, coefficients, valuations in augmentations: ef = bound == w.E()*w.F() - new_leafs.append(Node(w, node, ef, principal_part_bound, coefficients, valuations)) + new_leafs.append(MacLaneApproximantNode(w, node, ef, principal_part_bound, coefficients, valuations)) for leaf in new_leafs: if is_sufficient(leaf, [l for l in new_leafs if l is not leaf]): leaf.forced_leaf = True @@ -771,7 +753,7 @@ def _pow(self, x, e, error): EXAMPLES:: - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: v._pow(2, 2, error=4) 4 sage: v._pow(2, 1000, error=4) @@ -794,7 +776,7 @@ def mac_lane_approximant(self, G, valuation, approximants = None): EXAMPLES:: - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: R. = QQ[] sage: G = x^2 + 1 @@ -905,7 +887,7 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No EXAMPLES:: sage: k=Qp(5,4) - sage: v = pAdicValuation(k) + sage: v = k.valuation() sage: R.=k[] sage: G = x^2 + 1 sage: v.montes_factorization(G) @@ -915,7 +897,7 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No particular because the factors can not be represented there):: sage: R. = QQ[] - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: v.montes_factorization(x^2 + 1) x^2 + 1 @@ -958,8 +940,8 @@ def _ge_(self, other): EXAMPLES:: - sage: v = TrivialValuation(QQ) - sage: w = pAdicValuation(QQ, 2) + sage: v = valuations.TrivialValuation(QQ) + sage: w = QQ.valuation(2) sage: v >= w False @@ -967,3 +949,42 @@ def _ge_(self, other): if other.is_trivial(): return other.is_discrete_valuation() return super(DiscreteValuation, self)._ge_(other) + + +class MacLaneApproximantNode(object): + r""" + A node in the tree computed by :meth:`DiscreteValuation.mac_lane_approximants` + + Leaves in the computation of the tree of approximants + :meth:`DiscreteValuation.mac_lane_approximants`. Each vertex consists of a + tuple ``(v,ef,p,coeffs,vals)`` where ``v`` is an approximant, i.e., a + valuation, ef is a boolean, ``p`` is the parent of this vertex, and + ``coeffs`` and ``vals`` are cached values. (Only ``v`` and ``ef`` are + relevant, everything else are caches/debug info.) The boolean ``ef`` + denotes whether ``v`` already has the final ramification index E and + residue degree F of this approximant. An edge V -- P represents the + relation ``P.v ≤ V.v`` (pointwise on the polynomial ring K[x]) between the + valuations. + + TESTS:: + + sage: v = pAdicValuation(ZZ, 3) + sage: v.extension(GaussianIntegers()) # indirect doctest + + """ + def __init__(self, valuation, parent, ef, principal_part_bound, coefficients, valuations): + r""" + TESTS:: + + sage: from sage.rings.valuation.valuation import MacLaneApproximantNode + sage: node = MacLaneApproximantNode(pAdicValuation(QQ, 2), None, 1, None, None, None) + sage: TestSuite(node).run() + + """ + self.valuation = valuation + self.parent = parent + self.ef = ef + self.principal_part_bound = principal_part_bound + self.coefficients = coefficients + self.valuations = valuations + self.forced_leaf = False diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index 19593a595b2..1a1931c196b 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -9,7 +9,7 @@ EXAMPLES:: - sage: pAdicValuation(QQ, 2).parent() + sage: QQ.valuation(2).parent() Discrete pseudo-valuations on Rational Field AUTHORS: @@ -18,7 +18,7 @@ """ #***************************************************************************** -# Copyright (C) 2016 Julian Rüth +# Copyright (C) 2016-2017 Julian Rüth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -37,8 +37,9 @@ class DiscretePseudoValuationSpace(UniqueRepresentation, Homset): EXAMPLES:: + sage: from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace sage: H = DiscretePseudoValuationSpace(QQ) - sage: pAdicValuation(QQ, 2) in H + sage: QQ.valuation(2) in H True .. NOTE:: @@ -75,7 +76,8 @@ def __init__(self, domain): r""" TESTS:: - sage: isinstance(pAdicValuation(QQ, 2).parent(), DiscretePseudoValuationSpace) + sage: from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace + sage: isinstance(QQ.valuation(2).parent(), DiscretePseudoValuationSpace) True """ @@ -106,7 +108,8 @@ def _abstract_element_class(self): EXAMPLES:: - sage: isinstance(pAdicValuation(QQ, 2), DiscretePseudoValuationSpace.ElementMethods) # indirect doctest + sage: from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace + sage: isinstance(QQ.valuation(2), DiscretePseudoValuationSpace.ElementMethods) # indirect doctest True """ @@ -120,7 +123,7 @@ def _get_action_(self, S, op, self_on_left): EXAMPLES:: - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: from operator import mul sage: v.parent().get_action(ZZ, mul) # indirect doctest @@ -139,6 +142,7 @@ def _an_element_(self): EXAMPLES:: + sage: from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace sage: DiscretePseudoValuationSpace(QQ).an_element() # indirect doctest Trivial pseudo-valuation on Rational Field @@ -152,6 +156,7 @@ def _repr_(self): EXAMPLES:: + sage: from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace sage: DiscretePseudoValuationSpace(QQ) # indirect doctest Discrete pseudo-valuations on Rational Field @@ -164,13 +169,14 @@ def __contains__(self, x): EXAMPLES: + sage: from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace sage: H = DiscretePseudoValuationSpace(QQ) sage: H.an_element() in H True Elements of spaces which embed into this spaces are correctly handled:: - sage: pAdicValuation(QQ, 2) in H + sage: QQ.valuation(2) in H True """ @@ -186,8 +192,9 @@ def __call__(self, x): EXAMPLES: + sage: from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace sage: H = DiscretePseudoValuationSpace(QQ) - sage: H(pAdicValuation(QQ, 2)) + sage: H(QQ.valuation(2)) 2-adic valuation """ @@ -206,9 +213,10 @@ def _element_constructor_(self, x): We try to convert valuations defined on different domains by changing their base ring:: + sage: from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace sage: Z = DiscretePseudoValuationSpace(ZZ) sage: Q = DiscretePseudoValuationSpace(QQ) - sage: v = pAdicValuation(ZZ, 2) + sage: v = ZZ.valuation(2) sage: v in Q False sage: Q(v) in Q @@ -245,13 +253,15 @@ class ElementMethods: Here is an example of a method that is automagically added to a discrete valuation:: + sage: from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace sage: H = DiscretePseudoValuationSpace(QQ) - sage: pAdicValuation(QQ, 2).is_discrete_pseudo_valuation() # indirect doctest + sage: QQ.valuation(2).is_discrete_pseudo_valuation() # indirect doctest True The methods will be provided even if the concrete types is not created with :meth:`__make_element_class__`:: + sage: from sage.rings.valuation.valuation import DiscretePseudoValuation sage: m = DiscretePseudoValuation(H) sage: m.parent() is H True @@ -278,7 +288,7 @@ def is_discrete_pseudo_valuation(self): EXAMPLES:: - sage: pAdicValuation(QQ, 2).is_discrete_pseudo_valuation() + sage: QQ.valuation(2).is_discrete_pseudo_valuation() True """ @@ -293,7 +303,7 @@ def is_discrete_valuation(self): EXAMPLES:: - sage: pAdicValuation(QQ, 2).is_discrete_valuation() + sage: QQ.valuation(2).is_discrete_valuation() True """ @@ -306,7 +316,7 @@ def is_negative_pseudo_valuation(self): EXAMPLES:: - sage: pAdicValuation(QQ, 2).is_negative_pseudo_valuation() + sage: QQ.valuation(2).is_negative_pseudo_valuation() False """ @@ -329,7 +339,7 @@ def is_trivial(self): EXAMPLES:: - sage: pAdicValuation(QQ, 7).is_trivial() + sage: QQ.valuation(7).is_trivial() False """ @@ -350,11 +360,12 @@ def uniformizer(self): EXAMPLES:: - sage: pAdicValuation(QQ, 11).uniformizer() + sage: QQ.valuation(11).uniformizer() 11 Trivial valuations have no uniformizer:: + sage: from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace sage: v = DiscretePseudoValuationSpace(QQ).an_element() sage: v.is_trivial() True @@ -374,12 +385,13 @@ def value_group(self): EXAMPLES:: - sage: pAdicValuation(QQ, 2).value_group() + sage: QQ.valuation(2).value_group() Additive Abelian Group generated by 1 A pseudo-valuation that is `\infty` everywhere, does not have a value group:: + sage: from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace sage: v = DiscretePseudoValuationSpace(QQ).an_element() sage: v.value_group() Traceback (most recent call last): @@ -401,7 +413,7 @@ def value_semigroup(self): Most commonly, in particular over fields, the semigroup is the group generated by the valuation of the uniformizer:: - sage: G = pAdicValuation(QQ, 2).value_semigroup(); G + sage: G = QQ.valuation(2).value_semigroup(); G Additive Abelian Semigroup generated by -1, 1 sage: G in AdditiveMagmas().AdditiveAssociative().AdditiveUnital().AdditiveInverse() True @@ -409,13 +421,13 @@ def value_semigroup(self): If the domain is a discrete valuation ring, then the semigroup consists of the positive elements of the :meth:`value_group`:: - sage: pAdicValuation(Zp(2), 2).value_semigroup() + sage: Zp(2).valuation().value_semigroup() Additive Abelian Semigroup generated by 1 The semigroup can have a more complicated structure when the uniformizer is not in the domain:: - sage: v = pAdicValuation(ZZ, 2) + sage: v = ZZ.valuation(2) sage: R. = ZZ[] sage: w = GaussValuation(R, v) sage: u = w.augmentation(x, 5/3) @@ -436,7 +448,7 @@ def element_with_valuation(self, s): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 2) + sage: v = ZZ.valuation(2) sage: v.element_with_valuation(10) 1024 @@ -464,19 +476,19 @@ def residue_ring(self): EXAMPLES:: - sage: pAdicValuation(QQ, 2).residue_ring() + sage: QQ.valuation(2).residue_ring() Finite Field of size 2 - sage: TrivialValuation(QQ).residue_ring() + sage: valuations.TrivialValuation(QQ).residue_ring() Rational Field Note that a residue ring always exists, even when a residue field may not:: - sage: TrivialPseudoValuation(QQ).residue_ring() + sage: valuations.TrivialPseudoValuation(QQ).residue_ring() Quotient of Rational Field by the ideal (1) sage: TrivialValuation(ZZ).residue_ring() Integer Ring - sage: GaussValuation(ZZ['x'], pAdicValuation(ZZ, 2)).residue_ring() + sage: GaussValuation(ZZ['x'], ZZ.valuation(2)).residue_ring() Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) @@ -490,14 +502,14 @@ def residue_field(self): EXAMPLES:: - sage: pAdicValuation(QQ, 2).residue_field() + sage: QQ.valuation(2).residue_field() Finite Field of size 2 - sage: TrivialValuation(QQ).residue_field() + sage: valuations.TrivialValuation(QQ).residue_field() Rational Field - sage: TrivialValuation(ZZ).residue_field() + sage: valuations.TrivialValuation(ZZ).residue_field() Rational Field - sage: GaussValuation(ZZ['x'], pAdicValuation(ZZ, 2)).residue_field() + sage: GaussValuation(ZZ['x'], ZZ.valuation(2)).residue_field() Rational function field in x over Finite Field of size 2 """ @@ -520,7 +532,7 @@ def reduce(self, x): EXAMPLES:: - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: v.reduce(2) 0 sage: v.reduce(1) @@ -542,7 +554,7 @@ def lift(self, X): EXAMPLES:: - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: v.lift(v.residue_ring().one()) 1 @@ -554,7 +566,7 @@ def extension(self, ring): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 2) + sage: v = ZZ.valuation(2) sage: w = v.extension(QQ) sage: w.domain() Rational Field @@ -572,7 +584,7 @@ def extensions(self, ring): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 2) + sage: v = ZZ.valuation(2) sage: v.extensions(QQ) [2-adic valuation] @@ -587,7 +599,7 @@ def restriction(self, ring): EXAMPLES:: - sage: v = pAdicValuation(QQ, 2) + sage: v = QQ.valuation(2) sage: w = v.restriction(ZZ) sage: w.domain() Integer Ring @@ -607,7 +619,7 @@ def change_domain(self, ring): EXAMPLES:: - sage: v = pAdicValuation(QQ, 3) + sage: v = QQ.valuation(3) sage: v.change_domain(ZZ) 3-adic valuation @@ -630,7 +642,7 @@ def scale(self, scalar): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 3) + sage: v = ZZ.valuation(3) sage: w = v.scale(3) sage: w(3) 3 @@ -687,9 +699,9 @@ def separating_element(self, others): EXAMPLES:: - sage: v2 = pAdicValuation(QQ, 2) - sage: v3 = pAdicValuation(QQ, 3) - sage: v5 = pAdicValuation(QQ, 5) + sage: v2 = QQ.valuation(2) + sage: v3 = QQ.valuation(3) + sage: v5 = QQ.valuation(5) sage: v2.separating_element([v3,v5]) 4/15 @@ -759,8 +771,8 @@ def _strictly_separating_element(self, other): EXAMPLES:: - sage: v2 = pAdicValuation(QQ, 2) - sage: v3 = pAdicValuation(QQ, 3) + sage: v2 = QQ.valuation(2) + sage: v3 = QQ.valuation(3) sage: v2._strictly_separating_element(v3) 2/3 @@ -836,8 +848,8 @@ def _weakly_separating_element(self, other): EXAMPLES:: - sage: v2 = pAdicValuation(QQ, 2) - sage: v3 = pAdicValuation(QQ, 3) + sage: v2 = QQ.valuation(2) + sage: v3 = QQ.valuation(3) sage: v2._weakly_separating_element(v3) 2 @@ -861,7 +873,7 @@ def shift(self, x, s): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 2) + sage: v = ZZ.valuation(2) sage: v.shift(1, 10) 1024 sage: v.shift(11, -1) @@ -872,7 +884,7 @@ def shift(self, x, s): the uniformizer and substraction of a lift of the reduction:: sage: R. = ZZ[] - sage: v = pAdicValuation(ZZ, 2) + sage: v = ZZ.valuation(2) sage: w = GaussValuation(R, v) sage: w.shift(x, 1) 2*x @@ -912,7 +924,7 @@ def simplify(self, x, error=None, force=False): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 2) + sage: v = ZZ.valuation(2) sage: v.simplify(6, force=True) 2 sage: v.simplify(6, error=0, force=True) @@ -934,7 +946,7 @@ def lower_bound(self, x): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 2) + sage: v = ZZ.valuation(2) sage: v.lower_bound(2^10) 10 @@ -950,7 +962,7 @@ def upper_bound(self, x): EXAMPLES:: - sage: v = pAdicValuation(ZZ, 2) + sage: v = ZZ.valuation(2) sage: v.upper_bound(2^10) 10 @@ -971,7 +983,7 @@ def _relative_size(self, x): EXAMPLES:: - sage: v = pAdicValuation(Qp(2)) + sage: v = Qp(2).valuation() sage: v._relative_size(2) 1 @@ -991,7 +1003,7 @@ def _test_is_negative_pseudo_valuation(self, **options): TESTS:: - sage: v = pAdicValuation(ZZ, 3) + sage: v = ZZ.valuation(3) sage: v._test_is_negative_pseudo_valuation() """ @@ -1014,7 +1026,7 @@ def _test_bounds(self, **options): TESTS:: - sage: v = pAdicValuation(ZZ, 3) + sage: v = ZZ.valuation(3) sage: v._test_bounds() """ @@ -1031,7 +1043,7 @@ def _test_simplify(self, **options): TESTS:: - sage: v = pAdicValuation(ZZ, 3) + sage: v = ZZ.valuation(3) sage: v._test_simplify() """ @@ -1074,7 +1086,7 @@ def _test_shift(self, **options): TESTS:: - sage: v = pAdicValuation(ZZ, 3) + sage: v = ZZ.valuation(3) sage: v._test_shift() """ @@ -1103,7 +1115,7 @@ def _test_scale(self, **options): TESTS:: - sage: v = pAdicValuation(ZZ, 3) + sage: v = ZZ.valuation(3) sage: v._test_scale() """ @@ -1139,7 +1151,7 @@ def _test_add(self, **options): TESTS:: - sage: v = pAdicValuation(ZZ, 3) + sage: v = ZZ.valuation(3) sage: v._test_add() """ @@ -1157,7 +1169,7 @@ def _test_infinite_zero(self, **options): TESTS:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: v._test_infinite_zero() """ @@ -1171,7 +1183,7 @@ def _test_mul(self, **options): TESTS:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: v._test_mul() """ @@ -1187,12 +1199,13 @@ def _test_no_infinite_units(self, **options): TESTS:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: v._test_no_infinite_units() As multiplication translates to addition, pseudo-valuations which send a unit to infinity are necessarily trivial:: + sage: from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace sage: v = DiscretePseudoValuationSpace(QQ).an_element() sage: v(1) +Infinity @@ -1217,7 +1230,7 @@ def _test_value_group(self, **options): TESTS:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: v._test_value_group() """ @@ -1246,7 +1259,7 @@ def _test_value_semigroup(self, **options): TESTS:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: v._test_value_semigroup() """ @@ -1265,7 +1278,7 @@ def _test_element_with_valuation(self, **options): TESTS:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: v._test_element_with_valuation() """ @@ -1284,7 +1297,7 @@ def _test_residue_ring(self, **options): TESTS:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: v._test_residue_ring() """ @@ -1316,7 +1329,7 @@ def _test_reduce(self, **options): TESTS:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: v._test_reduce() """ @@ -1355,7 +1368,7 @@ def _test_lift(self, **options): TESTS:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: v._test_lift() """ @@ -1385,7 +1398,7 @@ def _test_restriction(self, **options): TESTS:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: v._test_restriction() """ @@ -1399,7 +1412,7 @@ def _test_extension(self, **options): TESTS:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: v._test_extension() """ @@ -1414,7 +1427,7 @@ def _test_change_domain(self, **options): TESTS:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: v._test_change_domain() """ @@ -1428,7 +1441,7 @@ def _test_no_infinite_nonzero(self, **options): TESTS:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: v._test_no_infinite_nonzero() """ @@ -1447,7 +1460,7 @@ def _test_residue_field(self, **options): TESTS:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: v._test_residue_field() """ @@ -1481,7 +1494,7 @@ def _test_ge(self, **options): TESTS:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: v._test_ge() """ @@ -1502,7 +1515,7 @@ def _test_le(self, **options): TESTS:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: v._test_le() """ @@ -1525,7 +1538,7 @@ class ScaleAction(Action): EXAMPLES:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: from operator import mul sage: v.parent().get_action(IntegerRing, mul, self_on_left=False) @@ -1536,7 +1549,7 @@ def _call_(self, s, v): EXAMPLES:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: 3*v # indirect doctest 3 * 5-adic valuation @@ -1550,7 +1563,7 @@ class InverseScaleAction(Action): EXAMPLES:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: from operator import div sage: v.parent().get_action(IntegerRing, div, self_on_left=True) @@ -1561,7 +1574,7 @@ def _call_(self, v, s): EXAMPLES:: - sage: v = pAdicValuation(QQ, 5) + sage: v = QQ.valuation(5) sage: v/3 # indirect doctest 1/3 * 5-adic valuation diff --git a/src/sage/rings/valuation/valuations_catalog.py b/src/sage/rings/valuation/valuations_catalog.py new file mode 100644 index 00000000000..335bc54f80f --- /dev/null +++ b/src/sage/rings/valuation/valuations_catalog.py @@ -0,0 +1,6 @@ +from __future__ import absolute_import +from sage.rings.padics.padic_valuation import pAdicValuation +from sage.rings.function_field.function_field_valuation import FunctionFieldValuation +from .gauss_valuation import GaussValuation +from .trivial_valuation import TrivialDiscretePseudoValuation, TrivialPseudoValuation, TrivialValuation +from .limit_valuation import LimitValuation diff --git a/src/sage/rings/valuation/value_group.py b/src/sage/rings/valuation/value_group.py index 0ed992eb842..abcd918b708 100644 --- a/src/sage/rings/valuation/value_group.py +++ b/src/sage/rings/valuation/value_group.py @@ -6,7 +6,7 @@ EXAMPLES:: - sage: v = pAdicValuation(ZZ, 2) + sage: v = ZZ.valuation(2) sage: v.value_group() Additive Abelian Group generated by 1 sage: v.value_semigroup() @@ -18,7 +18,7 @@ """ #***************************************************************************** -# Copyright (C) 2013-2016 Julian Rüth +# Copyright (C) 2013-2017 Julian Rüth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -37,6 +37,7 @@ class DiscreteValuationCodomain(UniqueRepresentation, Parent): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValuationCodomain sage: C = DiscreteValuationCodomain(); C Codomain of Discrete Valuations @@ -49,7 +50,8 @@ def __init__(self): r""" TESTS:: - sage: isinstance(pAdicValuation(QQ, 2).codomain(), DiscreteValuationCodomain) + sage: from sage.rings.valuation.value_group import DiscreteValuationCodomain + sage: isinstance(QQ.valuation(2).codomain(), DiscreteValuationCodomain) True """ @@ -68,6 +70,7 @@ def _element_constructor_(self, x): TESTS:: + sage: from sage.rings.valuation.value_group import DiscreteValuationCodomain sage: DiscreteValuationCodomain()(0) 0 sage: DiscreteValuationCodomain()(infinity) @@ -90,6 +93,7 @@ def _repr_(self): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValuationCodomain sage: DiscreteValuationCodomain() # indirect doctest Codomain of Discrete Valuations @@ -117,6 +121,7 @@ class DiscreteValueGroup(UniqueRepresentation, Parent): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValueGroup sage: D1 = DiscreteValueGroup(0); D1 Trivial Additive Abelian Group sage: D2 = DiscreteValueGroup(4/3); D2 @@ -139,6 +144,7 @@ def __classcall__(cls, generator): TESTS:: + sage: from sage.rings.valuation.value_group import DiscreteValueGroup sage: DiscreteValueGroup(1) is DiscreteValueGroup(-1) True @@ -151,6 +157,7 @@ def __init__(self, generator): r""" TESTS:: + sage: from sage.rings.valuation.value_group import DiscreteValueGroup sage: isinstance(DiscreteValueGroup(0), DiscreteValueGroup) True @@ -173,6 +180,7 @@ def _element_constructor_(self, x): TESTS:: + sage: from sage.rings.valuation.value_group import DiscreteValueGroup sage: DiscreteValueGroup(0)(0) 0 sage: DiscreteValueGroup(0)(1) @@ -199,6 +207,7 @@ def _repr_(self): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValueGroup sage: DiscreteValueGroup(0) # indirect doctest Trivial Additive Abelian Group @@ -217,6 +226,7 @@ def __add__(self, other): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValueGroup sage: D = DiscreteValueGroup(1/2) sage: D + 1/3 Additive Abelian Group generated by 1/6 @@ -248,6 +258,7 @@ def _mul_(self, other, switch_sides=False): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValueGroup sage: D = DiscreteValueGroup(1/2) sage: 1/2 * D Additive Abelian Group generated by 1/4 @@ -270,6 +281,7 @@ def index(self, other): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValueGroup sage: DiscreteValueGroup(3/8).index(DiscreteValueGroup(3)) 8 sage: DiscreteValueGroup(3).index(DiscreteValueGroup(3/8)) @@ -305,6 +317,7 @@ def numerator(self): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValueGroup sage: DiscreteValueGroup(3/8).numerator() 3 @@ -317,6 +330,7 @@ def denominator(self): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValueGroup sage: DiscreteValueGroup(3/8).denominator() 8 @@ -329,6 +343,7 @@ def gen(self): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValueGroup sage: DiscreteValueGroup(-3/8).gen() 3/8 @@ -341,6 +356,7 @@ def some_elements(self): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValueGroup sage: DiscreteValueGroup(-3/8).some_elements() [3/8, -3/8, 0, 42, 3/2, -3/2, 9/8, -9/8] @@ -353,6 +369,7 @@ def is_trivial(self): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValueGroup sage: DiscreteValueGroup(-3/8).is_trivial() False sage: DiscreteValueGroup(0).is_trivial() @@ -372,6 +389,7 @@ class DiscreteValueSemigroup(UniqueRepresentation, Parent): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValueSemigroup sage: D1 = DiscreteValueSemigroup(0); D1 Trivial Additive Abelian Semigroup sage: D2 = DiscreteValueSemigroup(4/3); D2 @@ -397,6 +415,7 @@ def __classcall__(cls, generators): generators and drop generators that are trivially contained in the semigroup generated by the remaining generators:: + sage: from sage.rings.valuation.value_group import DiscreteValueSemigroup sage: DiscreteValueSemigroup([1,2]) is DiscreteValueSemigroup([1]) True @@ -429,6 +448,7 @@ def __init__(self, generators): r""" TESTS:: + sage: from sage.rings.valuation.value_group import DiscreteValueSemigroup sage: isinstance(DiscreteValueSemigroup([0]), DiscreteValueSemigroup) True @@ -456,6 +476,7 @@ def _solve_linear_program(self, target): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValueSemigroup sage: D = DiscreteValueSemigroup([2,3,5]) sage: D._solve_linear_program(12) {0: 1, 1: 0, 2: 2} @@ -505,6 +526,7 @@ def _element_constructor_(self, x): TESTS:: + sage: from sage.rings.valuation.value_group import DiscreteValueSemigroup sage: DiscreteValueSemigroup([])(0) 0 sage: DiscreteValueSemigroup([])(1) @@ -531,6 +553,7 @@ def _repr_(self): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValueSemigroup sage: DiscreteValueSemigroup(0) # indirect doctest Trivial Additive Abelian Semigroup @@ -549,6 +572,7 @@ def __add__(self, other): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValueSemigroup, DiscreteValueGroup sage: D = DiscreteValueSemigroup(1/2) sage: D + 1/3 Additive Abelian Semigroup generated by 1/3, 1/2 @@ -580,6 +604,7 @@ def _mul_(self, other, switch_sides=False): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValueSemigroup sage: D = DiscreteValueSemigroup(1/2) sage: 1/2 * D Additive Abelian Semigroup generated by 1/4 @@ -598,6 +623,7 @@ def gens(self): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValueSemigroup sage: DiscreteValueSemigroup(-3/8).gens() (-3/8,) @@ -610,6 +636,7 @@ def some_elements(self): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValueSemigroup sage: list(DiscreteValueSemigroup([-3/8,1/2]).some_elements()) [0, -3/8, 1/2, ...] @@ -629,6 +656,7 @@ def is_trivial(self): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValueSemigroup sage: DiscreteValueSemigroup(-3/8).is_trivial() False sage: DiscreteValueSemigroup([]).is_trivial() @@ -644,6 +672,7 @@ def is_group(self): EXAMPLES:: + sage: from sage.rings.valuation.value_group import DiscreteValueSemigroup sage: DiscreteValueSemigroup(1).is_group() False sage: D = DiscreteValueSemigroup([-1, 1]) From c0a81c8285b47f6fc89aa34bc125ac474c75f2e9 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Wed, 19 Jul 2017 07:26:34 +0000 Subject: [PATCH 253/740] fix function lookup --- src/sage/rings/valuation/valuation_space.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index 1a1931c196b..f5ea13c2885 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -486,7 +486,7 @@ def residue_ring(self): sage: valuations.TrivialPseudoValuation(QQ).residue_ring() Quotient of Rational Field by the ideal (1) - sage: TrivialValuation(ZZ).residue_ring() + sage: valuations.TrivialValuation(ZZ).residue_ring() Integer Ring sage: GaussValuation(ZZ['x'], ZZ.valuation(2)).residue_ring() Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) From 1eace2849ba3ee84bca6d9a24d64f739e194747e Mon Sep 17 00:00:00 2001 From: roed314 Date: Thu, 20 Jul 2017 16:04:59 -0400 Subject: [PATCH 254/740] Update valuation_space.py Remove comma --- src/sage/rings/valuation/valuation_space.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index f5ea13c2885..6077391b4b7 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -3,7 +3,7 @@ Spaces of valuations This module provides spaces of exponential pseudo-valuations on integral -domains. It currently, only provides support for such valuations if they are +domains. It currently only provides support for such valuations if they are discrete, i.e., their image is a discrete additive subgroup of the rational numbers extended by `\infty`. From 21b0f6820cb886b15977c8c449de17d723e5ab39 Mon Sep 17 00:00:00 2001 From: roed314 Date: Thu, 20 Jul 2017 16:44:33 -0400 Subject: [PATCH 255/740] Update valuation_space.py Allow scalar multiplication on both sides --- src/sage/rings/valuation/valuation_space.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index 6077391b4b7..a5ca66016a4 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -130,7 +130,7 @@ def _get_action_(self, S, op, self_on_left): """ from operator import mul, div from sage.rings.all import QQ, InfinityRing, ZZ - if op == mul and not self_on_left and (S is InfinityRing or S is QQ or S is ZZ): + if op == mul and (S is InfinityRing or S is QQ or S is ZZ): return ScaleAction(S, self) if op == div and self_on_left and (S is InfinityRing or S is QQ or S is ZZ): return InverseScaleAction(self, S) From 38f3815e86adf4bbadc24c4efdfee9e3debf3f15 Mon Sep 17 00:00:00 2001 From: roed314 Date: Thu, 20 Jul 2017 16:49:55 -0400 Subject: [PATCH 256/740] Update valuation_space.py Clarify __contains__ docstring --- src/sage/rings/valuation/valuation_space.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index a5ca66016a4..6cbb8a1dee0 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -173,9 +173,6 @@ def __contains__(self, x): sage: H = DiscretePseudoValuationSpace(QQ) sage: H.an_element() in H True - - Elements of spaces which embed into this spaces are correctly handled:: - sage: QQ.valuation(2) in H True From 7c57f68a73fcbea2f3a6908e7dd5e37969117727 Mon Sep 17 00:00:00 2001 From: roed314 Date: Thu, 20 Jul 2017 16:54:52 -0400 Subject: [PATCH 257/740] Update valuation_space.py Delete old comment in _element_constructor_ docstring --- src/sage/rings/valuation/valuation_space.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index 6cbb8a1dee0..38f1a18d045 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -223,12 +223,6 @@ def _element_constructor_(self, x): sage: Z(Q(v)) in Z True - We support coercions and conversions, even though they are not - implemented here:: - - sage: Z(v) - 2-adic valuation - """ if isinstance(x.parent(), DiscretePseudoValuationSpace): if x.domain() is not self.domain(): From d90afa8113018b79d891bcc5f3a54ede80775398 Mon Sep 17 00:00:00 2001 From: roed314 Date: Thu, 20 Jul 2017 17:26:06 -0400 Subject: [PATCH 258/740] Update valuation_space.py Fix typo --- src/sage/rings/valuation/valuation_space.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index 38f1a18d045..693a51831e3 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -249,7 +249,7 @@ class ElementMethods: sage: QQ.valuation(2).is_discrete_pseudo_valuation() # indirect doctest True - The methods will be provided even if the concrete types is not created + The methods will be provided even if the concrete type is not created with :meth:`__make_element_class__`:: sage: from sage.rings.valuation.valuation import DiscretePseudoValuation From 1cb817895c6bf478f09def7168ac57aadd50ac7d Mon Sep 17 00:00:00 2001 From: roed314 Date: Thu, 20 Jul 2017 17:57:50 -0400 Subject: [PATCH 259/740] Update valuation_space.py Remove incorrect statement in documentation of residue_ring --- src/sage/rings/valuation/valuation_space.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index 693a51831e3..d76440c4a6c 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -461,10 +461,6 @@ def residue_ring(self): r""" Return the residue ring of this valuation, i.e., the elements of non-negative valuation modulo the elements of positive valuation. - - This is identical to :meth:`residue_field` when a residue field - exists. - EXAMPLES:: sage: QQ.valuation(2).residue_ring() From 9fd87332c734899d860972d836d479283b5ab62f Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Thu, 20 Jul 2017 21:58:56 +0000 Subject: [PATCH 260/740] fix import --- src/sage/rings/valuation/inductive_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index 52fcb01211a..dc8b45d85be 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -564,7 +564,7 @@ def extensions(self, other): if other in FunctionFields() and other.ngens() == 1: # extend to K[x] and from there to K(x) v = self.extension(self.domain().change_ring(self.domain().base().fraction_field())) - from function_field_valuation import FunctionFieldValuation + from src.sage.rings.function_field.function_field_valuation import FunctionFieldValuation return [other.valuation(v)] return super(FiniteInductiveValuation, self).extensions(other) From e1e65a5d7514ebe51365bdf1da402d2054f02654 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Thu, 20 Jul 2017 22:00:05 +0000 Subject: [PATCH 261/740] fixed typo --- src/sage/rings/valuation/inductive_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index dc8b45d85be..d8062f3fbdc 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -564,7 +564,7 @@ def extensions(self, other): if other in FunctionFields() and other.ngens() == 1: # extend to K[x] and from there to K(x) v = self.extension(self.domain().change_ring(self.domain().base().fraction_field())) - from src.sage.rings.function_field.function_field_valuation import FunctionFieldValuation + from sage.rings.function_field.function_field_valuation import FunctionFieldValuation return [other.valuation(v)] return super(FiniteInductiveValuation, self).extensions(other) From 9c16a1bc642498daaa1c17f61a874cbd42f4063b Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Thu, 20 Jul 2017 22:07:11 +0000 Subject: [PATCH 262/740] undo .valuation() shortcut it seems to trigger a stack overflow --- src/sage/rings/valuation/inductive_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index d8062f3fbdc..9b21792db50 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -565,7 +565,7 @@ def extensions(self, other): # extend to K[x] and from there to K(x) v = self.extension(self.domain().change_ring(self.domain().base().fraction_field())) from sage.rings.function_field.function_field_valuation import FunctionFieldValuation - return [other.valuation(v)] + return [FunctionFieldValuation(other, v)] return super(FiniteInductiveValuation, self).extensions(other) From 119f2454cb4100c62300121a7df024a8b4dead9e Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Thu, 20 Jul 2017 22:32:12 +0000 Subject: [PATCH 263/740] It is not relevant whether a valuation converts into the valuation space but whether it is already in the valuation space --- src/sage/rings/function_field/function_field_valuation.py | 6 +++--- src/sage/rings/valuation/inductive_valuation.py | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/function_field/function_field_valuation.py b/src/sage/rings/function_field/function_field_valuation.py index a53234862bb..79ec971bbc8 100644 --- a/src/sage/rings/function_field/function_field_valuation.py +++ b/src/sage/rings/function_field/function_field_valuation.py @@ -197,7 +197,6 @@ def create_key_and_extra_args(self, domain, prime): """ from sage.categories.function_fields import FunctionFields - from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace if domain not in FunctionFields(): raise ValueError("Domain must be a function field.") @@ -207,7 +206,8 @@ def create_key_and_extra_args(self, domain, prime): # isomorphism information return self.create_key_and_extra_args_from_valuation_on_isomorphic_field(domain, prime[0], prime[1], prime[2]) - if prime in DiscretePseudoValuationSpace(domain): + from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace + if prime.parent() is DiscretePseudoValuationSpace(domain): # prime is already a valuation of the requested domain # if we returned (domain, prime), we would break caching # because this element has been created from a different key @@ -220,7 +220,7 @@ def create_key_and_extra_args(self, domain, prime): if prime in domain: # prime defines a place return self.create_key_and_extra_args_from_place(domain, prime) - if prime in DiscretePseudoValuationSpace(domain._ring): + if prime.parent() is DiscretePseudoValuationSpace(domain._ring): # prime is a discrete (pseudo-)valuation on the polynomial ring # that the domain is constructed from return self.create_key_and_extra_args_from_valuation(domain, prime) diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index 9b21792db50..6dd8bc0bdd0 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -564,8 +564,7 @@ def extensions(self, other): if other in FunctionFields() and other.ngens() == 1: # extend to K[x] and from there to K(x) v = self.extension(self.domain().change_ring(self.domain().base().fraction_field())) - from sage.rings.function_field.function_field_valuation import FunctionFieldValuation - return [FunctionFieldValuation(other, v)] + return [other.valuation(v)] return super(FiniteInductiveValuation, self).extensions(other) From 39cb55bc81615e142d69ee169b41183c4d2968c0 Mon Sep 17 00:00:00 2001 From: roed314 Date: Thu, 20 Jul 2017 18:37:15 -0400 Subject: [PATCH 264/740] Update valuation_space.py Add comment in _strongly_separating_element --- src/sage/rings/valuation/valuation_space.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index d76440c4a6c..d84ce6ba6f0 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -780,7 +780,9 @@ def _strictly_separating_element(self, other): assert(dd > 0) assert(d is not infinity) if d < 0: - return self.domain()(1/denominator) + # The following may fail if denominator is not inverible in the domain, + # but we don't have a better option this generically. + return self.domain()(~denominator) # We need non-negative integers a and b such that # a*n - b*d > 0 and a*nn - b*dd < 0 From 7ea987498d844fb5aa28c4023cc8f4350d48a20a Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Thu, 20 Jul 2017 22:41:13 +0000 Subject: [PATCH 265/740] fix doctests --- src/sage/rings/function_field/function_field.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 6b3c948a5cc..b6392900ae3 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -588,14 +588,14 @@ def valuation(self, prime): polynomial ring:: sage: R. = QQ[] - sage: w = GaussValuation(R, TrivialValuation(QQ)).augmentation(x - 1, 1) + sage: w = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)).augmentation(x - 1, 1) sage: v = K.valuation(w); v (x - 1)-adic valuation Note that this allows us to specify valuations which do not correspond to a place of the function field:: - sage: w = GaussValuation(R, pAdicValuation(QQ, 2)) + sage: w = valuations.GaussValuation(R, QQ.valuation(2)) sage: v = K.valuation(w); v 2-adic valuation @@ -606,7 +606,7 @@ def valuation(self, prime): applying the substitution `x \mapsto 1/x` (here, the inverse map is also `x \mapsto 1/x`):: - sage: w = GaussValuation(R, pAdicValuation(QQ, 2)).augmentation(x, 1) + sage: w = valuations.GaussValuation(R, QQ.valuation(2)).augmentation(x, 1) sage: w = K.valuation(w) sage: v = K.valuation((w, K.hom([~K.gen()]), K.hom([~K.gen()]))); v Valuation on rational function field induced by [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ] (in Rational function field in x over Rational Field after x |--> 1/x) From 3fd4140cfdc227296ba92ad5c87e5ac5d3ee03f4 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Thu, 20 Jul 2017 22:53:21 +0000 Subject: [PATCH 266/740] add doctest output --- src/sage/rings/padics/padic_generic.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index 21783cd5029..a38b98fd44c 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -964,6 +964,7 @@ def valuation(self): sage: R. = K[] sage: L. = K.extension(a^3 - 3) sage: v = L.valuation(); v + 3-adic valuation sage: v(3) 1 sage: L(3).valuation() From 0c60cb7006afcd336963fdcc7af5cab510cd796b Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Thu, 20 Jul 2017 22:53:56 +0000 Subject: [PATCH 267/740] fix valuation parameters --- src/sage/rings/padics/padic_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 3fd1cecfc47..3c0fba6ee68 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -796,7 +796,7 @@ def restriction(self, ring): if not ring.is_subring(self.domain()): raise ValueError("ring must be a subring of the domain of this valuation but %r is not a subring of %r"%(ring, self.domain())) - return ring.valuation(self.p()) + return ring.valuation() @cached_method def value_semigroup(self): From 20100145cbdac34f8f3f021f597f750541c60d11 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Thu, 20 Jul 2017 22:56:48 +0000 Subject: [PATCH 268/740] restricting to ZZ, we need to know the prime --- src/sage/rings/padics/padic_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 3c0fba6ee68..6b89af726c3 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -796,7 +796,7 @@ def restriction(self, ring): if not ring.is_subring(self.domain()): raise ValueError("ring must be a subring of the domain of this valuation but %r is not a subring of %r"%(ring, self.domain())) - return ring.valuation() + return pAdicValuation(ring, self.p()) @cached_method def value_semigroup(self): From a474ed5f97ab81f11def38e6a11076d7c80f7ed4 Mon Sep 17 00:00:00 2001 From: roed314 Date: Thu, 20 Jul 2017 20:11:34 -0400 Subject: [PATCH 269/740] Update valuation_space.py Improve implementation of _strongly_separating_element --- src/sage/rings/valuation/valuation_space.py | 39 +++++++++++---------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index d84ce6ba6f0..3b58e5adfdb 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -789,30 +789,33 @@ def _strictly_separating_element(self, other): if nn == 0: # the above becomes b != 0 and a/b > d/n b = 1 - if d/n in ZZ: - a = d/n + 1 - else: - a = (d/n).ceil() + a = (d/n + 1).floor() else: # Since n,nn,d,dd are all non-negative this is essentially equivalent to # a/b > d/n and b/a > nn/dd # which is # dd/nn > a/b > d/n assert(dd/nn > d/n) - for b in iter(NN): - # we naĩvely find the smallest b which can satisfy such an equation - # there are faster algorithms for this - # https://dl.acm.org/citation.cfm?id=1823943&CFID=864015776&CFTOKEN=26270402 - if b == 0: - continue - assert(b <= n + nn) # (a+b)/(n+nn) is a solution - if nn/dd/b in ZZ: - a = nn/dd/b + 1 - else: - a = (nn/dd/b).ceil() - assert(a/b > d/n) - if dd/nn > a/b: - break + from sage.rings.continued_fraction import continued_fraction + ab_cf = [] + dn_cf = continued_fraction(d/n) + ddnn_cf = continued_fraction(dd/nn) + for i, (x,y) in enumerate(zip(dn_cf, ddnn_cf)): + if x == y: + ab_cf.append(x) + elif x < y: + if y > x+1 or len(ddnn_cf) > i+1: + ab_cf.append(x+1) + else: + # the expansion of dd/nn is ending, so we can't append x+1 + ab_cf.extend([x,1,1]) + elif y < x: + if x > y+1 or len(dn_cf) > i+1: + ab_cf.append(y+1) + else: + ab_cf.extend([y,1,1]) + ab = continued_fraction(ab_cf).value() + a,b = ab.numerator(), ab.denominator() ret = self.domain()(numerator**a / denominator**b) assert(self(ret) > 0) From e5ef4d8cc7c76792d46f866daa628006f84c12e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 20 Jul 2017 20:45:48 -0500 Subject: [PATCH 270/740] do not catch everything --- src/sage/rings/valuation/gauss_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/gauss_valuation.py b/src/sage/rings/valuation/gauss_valuation.py index e0b92d44195..e6a288025ad 100644 --- a/src/sage/rings/valuation/gauss_valuation.py +++ b/src/sage/rings/valuation/gauss_valuation.py @@ -353,7 +353,7 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations try: return f.map_coefficients(self._base_valuation.reduce, self._base_valuation.residue_field()) - except: + except Exception: if check and not all([v>=0 for v in self.valuations(f)]): raise ValueError("reduction not defined for non-integral elements and %r is not integral over %r"%(f, self)) raise From ca2089f8ad2cb1f14c5400df39d53727ec11025f Mon Sep 17 00:00:00 2001 From: roed314 Date: Thu, 20 Jul 2017 22:18:42 -0400 Subject: [PATCH 271/740] Update valuation_space.py --- src/sage/rings/valuation/valuation_space.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index 3b58e5adfdb..6003dfb9551 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -897,11 +897,11 @@ def shift(self, x, s): return x * self.uniformizer()**s else: # s < 0 if ~self.uniformizer() in self.domain(): - return x / self.uniformizer()**(-s) + return self.domain()(x / self.uniformizer()**(-s)) else: for i in range(-s): if self(x) < 0: - raise NotImplementedError("can not compute general shifts over non-fields which do contain elements of negative valuation") + raise NotImplementedError("can not compute general shifts over non-fields which contain elements of negative valuation") x -= self.lift(self.reduce(x)) x //= self.uniformizer() return x @@ -913,6 +913,8 @@ def simplify(self, x, error=None, force=False): Produce an element which differs from ``x`` by an element of valuation strictly greater than the valuation of ``x`` (or strictly greater than ``error`` if set.) + + If ``force`` is not set, then expensive simplifications may be avoided. EXAMPLES:: @@ -1087,7 +1089,7 @@ def _test_shift(self, **options): try: self.residue_ring() - except: + except Exception: # it is not clear what a shift should be in this case return From bcb78f31511ef736da584f1e2bad8224e678d538 Mon Sep 17 00:00:00 2001 From: roed314 Date: Thu, 20 Jul 2017 23:51:40 -0400 Subject: [PATCH 272/740] Update scaled_valuation.py Remove NOTE --- src/sage/rings/valuation/scaled_valuation.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/sage/rings/valuation/scaled_valuation.py b/src/sage/rings/valuation/scaled_valuation.py index 64aa4e71db3..56db6d22965 100644 --- a/src/sage/rings/valuation/scaled_valuation.py +++ b/src/sage/rings/valuation/scaled_valuation.py @@ -242,15 +242,6 @@ def _weakly_separating_element(self, other): valuation with respect to this valuation than with respect to ``other``. - .. NOTE:: - - Overriding this method tends to be a nuissance as you need to - handle all possible types (as in Python type) of valuations. - This is essentially the same problem that you have when - implementing operators such as ``+`` or ``>=``. A sufficiently - fancy multimethod implementation could solve that here but - there is currently nothing like that in Sage/Python. - EXAMPLES:: sage: v2 = QQ.valuation(2) From be4daab09c85c24aa61b1c2f6834d15385f7fd4e Mon Sep 17 00:00:00 2001 From: roed314 Date: Fri, 21 Jul 2017 01:08:29 -0400 Subject: [PATCH 273/740] Update valuation.py Remove optional: integrated and one irrelevant assertion. --- src/sage/rings/valuation/valuation.py | 41 ++++++++++++++++++--------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 9fc245ac927..9507fff8ee1 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -97,7 +97,7 @@ def _hash_(self): Return a hash value for this valuation. We override the strange default provided by - ``sage.categories.marphism.Morphism`` here and implement equality by + :class:`sage.categories.marphism.Morphism` here and implement equality by ``id``. This works fine for objects which use unique representation. Note that the vast majority of valuations come out of a @@ -203,7 +203,7 @@ def _eq_(self, other): Return whether this valuation and ``other`` are indistinguishable. We override the strange default provided by - ``sage.categories.marphism.Morphism`` here and implement equality by + :class:`sage.categories.marphism.Morphism` here and implement equality by ``id``. This is the right behaviour in many cases. Note that the vast majority of valuations come out of a @@ -336,6 +336,8 @@ def is_discrete_valuation(self): sage: v = QQ.valuation(2) sage: R. = QQ[] sage: v = GaussValuation(R, v) + sage: v.is_discrete_valuation() + True sage: w = v.augmentation(x, infinity) sage: w.is_discrete_valuation() False @@ -369,6 +371,8 @@ def is_negative_pseudo_valuation(self): sage: R. = QQ[] sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)).augmentation(x, infinity) + sage: v.is_negative_pseudo_valuation() + False sage: K. = FunctionField(QQ) sage: w = K.valuation(v) sage: w.is_negative_pseudo_valuation() @@ -421,7 +425,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru INPUT: - - ``G`` -- a monic squarefree integral polynomial defined over a + - ``G`` -- a monic squarefree integral polynomial in a univariate polynomial ring over the :meth:`domain` of this valuation. - ``assume_squarefree`` -- a boolean (default: ``False``), whether to @@ -490,19 +494,19 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: F = y^21 + x*y^20 + (x^3 + x + 1)*y^18 + (x^3 + 1)*y^17 + (x^4 + x)*y^16 + (x^7 + x^6 + x^3 + x + 1)*y^15 + x^7*y^14 + (x^8 + x^7 + x^6 + x^4 + x^3 + 1)*y^13 + (x^9 + x^8 + x^4 + 1)*y^12 + (x^11 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2)*y^11 + (x^12 + x^9 + x^8 + x^7 + x^5 + x^3 + x + 1)*y^10 + (x^14 + x^13 + x^10 + x^9 + x^8 + x^7 + x^6 + x^3 + x^2 + 1)*y^9 + (x^13 + x^9 + x^8 + x^6 + x^4 + x^3 + x)*y^8 + (x^16 + x^15 + x^13 + x^12 + x^11 + x^7 + x^3 + x)*y^7 + (x^17 + x^16 + x^13 + x^9 + x^8 + x)*y^6 + (x^17 + x^16 + x^12 + x^7 + x^5 + x^2 + x + 1)*y^5 + (x^19 + x^16 + x^15 + x^12 + x^6 + x^5 + x^3 + 1)*y^4 + (x^18 + x^15 + x^12 + x^10 + x^9 + x^7 + x^4 + x)*y^3 + (x^22 + x^21 + x^20 + x^18 + x^13 + x^12 + x^9 + x^8 + x^7 + x^5 + x^4 + x^3)*y^2 + (x^23 + x^22 + x^20 + x^17 + x^15 + x^14 + x^12 + x^9)*y + x^25 + x^23 + x^19 + x^17 + x^15 + x^13 + x^11 + x^5 sage: x = K._ring.gen() sage: v0 = K.valuation(valuations.GaussValuation(K._ring, valuations.TrivialValuation(k)).augmentation(x,1)) - sage: v0.mac_lane_approximants(F, assume_squarefree=True) # optional: integrated; assumes squarefree for speed + sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assumes squarefree for speed [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x + 1) = 3/2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y) = 4/3, v(y^3 + x^4) = 13/3 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x) = 2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y^15 + y^13 + (x + 1)*y^12 + x*y^11 + (x + 1)*y^10 + y^9 + y^8 + x*y^6 + x*y^5 + y^4 + y^3 + y^2 + (x + 1)*y + x + 1) = 2 ]] sage: v0 = K.valuation(valuations.GaussValuation(K._ring, valuations.TrivialValuation(k)).augmentation(x+1,1)) - sage: v0.mac_lane_approximants(F, assume_squarefree=True) # optional: integrated; assumes squarefree for speed + sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assumes squarefree for speed [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y) = 7/2, v(y^2 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1) = 15/2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y + x^2 + 1) = 7/2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y) = 3/4, v(y^4 + x^3 + x^2 + x + 1) = 15/4 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y^13 + x*y^12 + y^10 + (x + 1)*y^9 + (x + 1)*y^8 + x*y^7 + x*y^6 + (x + 1)*y^4 + y^3 + (x + 1)*y^2 + 1) = 2 ]] sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).augmentation(x^3+x^2+1,1)) - sage: v0.mac_lane_approximants(F, assume_squarefree=True) # optional: integrated; assumes squarefree for speed + sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assumes squarefree for speed [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y + x^3 + x^2 + x) = 2, v(y^2 + (x^6 + x^4 + 1)*y + x^14 + x^10 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2 + x) = 5 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^2 + (x^7 + x^5 + x^4 + x^3 + x^2 + x)*y + x^7 + x^5 + x + 1) = 3 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^3 + (x^8 + x^5 + x^4 + x^3 + x + 1)*y^2 + (x^7 + x^6 + x^5)*y + x^8 + x^5 + x^4 + x^3 + 1) = 3 ], @@ -535,9 +539,9 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = +Infinity ]] sage: G = x^2 + 1 - sage: v.mac_lane_approximants(G) # optional: integrated + sage: v.mac_lane_approximants(G) [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x + (1 + O(2^10))) = 1/2 ]] - sage: v.mac_lane_approximants(G, required_precision = infinity) # optional: integrated + sage: v.mac_lane_approximants(G, required_precision = infinity) [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x + (1 + O(2^10))) = 1/2, v((1 + O(2^10))*x^2 + (1 + O(2^10))) = +Infinity ]] sage: G = x^4 + 2*x^3 + 2*x^2 - 2*x + 2 @@ -584,9 +588,9 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru Note how the latter give a better approximation to the factors of `x^2 + 1`:: - sage: v1.phi() * v2.phi() - G # optional: integrated + sage: v1.phi() * v2.phi() - G (5 + O(5^4))*x + 5 + O(5^4) - sage: w1.phi() * w2.phi() - G # optional: integrated + sage: w1.phi() * w2.phi() - G (5^3 + O(5^4))*x + 5^3 + O(5^4) In this example, the process stops with a factorization of `x^2 + 1`:: @@ -616,7 +620,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: alpha = T^3/4 sage: G = 3^3*T^3*(alpha^4 - alpha)^2 - (4*alpha^3 - 1)^3 sage: G = G/G.leading_coefficient() - sage: K.valuation().mac_lane_approximants(G) # optional: integrated + sage: K.valuation().mac_lane_approximants(G) [[ Gauss valuation induced by 3-adic valuation, v((1 + O(3^20))*T + (2 + O(3^20))) = 1/9, v((1 + O(3^20))*T^9 + (2*3 + 2*3^2 + O(3^21))*T^8 + (3 + 3^5 + O(3^21))*T^7 + (2*3 + 2*3^2 + 3^3 + 2*3^4 + 2*3^5 + 3^6 + O(3^21))*T^6 + (2*3 + 2*3^2 + 3^4 + 3^6 + 2*3^7 + O(3^21))*T^5 + (3 + 3^2 + 3^3 + 2*3^6 + 2*3^7 + 3^8 + O(3^21))*T^4 + (2*3 + 2*3^2 + 3^3 + 2*3^5 + 2*3^6 + 2*3^7 + 2*3^8 + O(3^21))*T^3 + (2*3 + 2*3^2 + 3^3 + 2*3^4 + 3^5 + 2*3^6 + 2*3^7 + 2*3^8 + O(3^21))*T^2 + (3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^7 + 3^8 + O(3^21))*T + (2 + 2*3 + 2*3^2 + 2*3^4 + 2*3^5 + 3^7 + O(3^20))) = 55/27 ]] A similar example:: @@ -624,7 +628,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: R. = QQ[] sage: v = QQ.valuation(3) sage: G = (x^3 + 3)^3 - 81 - sage: v.mac_lane_approximants(G) # optional: integrated + sage: v.mac_lane_approximants(G) [[ Gauss valuation induced by 3-adic valuation, v(x) = 1/3, v(x^3 + 3*x + 3) = 13/9 ]] Another problematic case:: @@ -663,7 +667,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru verbose("Approximants of %r on %r towards %r"%(self, self.domain(), G), level=3) from sage.rings.all import infinity - from gauss_valuation import GaussValuation + from sage.rings.valuation.gauss_valuation import GaussValuation if not all([self(c) >= 0 for c in G.coefficients()]): raise ValueError("G must be integral") @@ -774,6 +778,16 @@ def mac_lane_approximant(self, G, valuation, approximants = None): Return the approximant from :meth:`mac_lane_approximants` for ``G`` which is approximated by or approximates ``valuation``. + INPUT: + + - ``G`` -- a monic squarefree integral polynomial in a + univariate polynomial ring over the :meth:`domain` of this valuation. + + - ``valuation`` -- a valuation on the parent of ``G``. + + - ``approximants`` -- the output of :meth:`mac_lane_approximants`. + If not given, it is computed. + EXAMPLES:: sage: v = QQ.valuation(2) @@ -860,7 +874,6 @@ def mac_lane_approximant(self, G, valuation, approximants = None): raise ValueError("The valuation %r is not approximated by a unique extension of %r with respect to %r"%(valuation, self, G)) if len(smaller_approximants) == 0: raise ValueError("The valuation %r is not related to an extension of %r with respect to %r"%(valuation, self, G)) - assert len(smaller_approximants) == 1 return smaller_approximants[0] def montes_factorization(self, G, assume_squarefree=False, required_precision=None): From 7a95a9df1730d8c94d9c556beef2e0a4e3be7bf2 Mon Sep 17 00:00:00 2001 From: roed314 Date: Fri, 21 Jul 2017 02:04:23 -0400 Subject: [PATCH 274/740] Update augmented_valuation.py Fix typo in E --- src/sage/rings/valuation/augmented_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index b68d981de1d..ed3b67e2691 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -510,7 +510,7 @@ def E(self): 2 """ - if self.augmentation_chain()[-1].is_trivial(): + if self.augmentation_chain()[-1]._base_valuation.is_trivial(): raise NotImplementedError("ramification index is not defined over a trivial Gauss valuation") return self.value_group().index(self._base_valuation.value_group()) * self._base_valuation.E() From dbf779152fbce919210a9b36ca0a9ddeda98b474 Mon Sep 17 00:00:00 2001 From: roed314 Date: Fri, 21 Jul 2017 04:22:23 -0400 Subject: [PATCH 275/740] Update augmented_valuation.py --- .../rings/valuation/augmented_valuation.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index ed3b67e2691..4c2ab161cbe 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -555,7 +555,7 @@ def extensions(self, ring): return [self] from sage.rings.polynomial.polynomial_ring import is_PolynomialRing - if is_PolynomialRing(ring) and ring.ngens() == 1: + if is_PolynomialRing(ring): # univariate base_valuations = self._base_valuation.extensions(ring) phi = self.phi().change_ring(ring.base_ring()) @@ -597,8 +597,8 @@ def restriction(self, ring): if ring.is_subring(self.domain().base_ring()): return base from sage.rings.polynomial.polynomial_ring import is_PolynomialRing - if is_PolynomialRing(ring) and ring.ngens() == 1: - return base.augmentation(self.phi().change_ring(ring.base()), self._mu) + if is_PolynomialRing(ring): # univariate + return base.augmentation(self.phi().change_ring(ring.base_ring()), self._mu) return super(AugmentedValuation_base, self).restriction(ring) def uniformizer(self): @@ -818,7 +818,7 @@ def change_domain(self, ring): """ from sage.rings.polynomial.polynomial_ring import is_PolynomialRing - if is_PolynomialRing(ring) and ring.ngens() == 1 and ring.variable_name() == self.domain().variable_name(): + if is_PolynomialRing(ring) and ring.variable_name() == self.domain().variable_name(): return self._base_valuation.change_domain(ring).augmentation(self.phi().change_ring(ring.base_ring()), self._mu, check=False) return super(AugmentedValuation_base, self).change_domain(ring) @@ -1290,7 +1290,7 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations # the recursive call to reduce below # replace f_i by f_i Q^{i tau} if i//tau >= len(valuations): - # we do not the correct valuation of the coefficient, but + # we do not know the correct valuation of the coefficient, but # the computation is faster if we know that the coefficient # has positive valuation valuations.append(self._base_valuation.lower_bound(c) + i*self._mu) @@ -1435,7 +1435,7 @@ def lift(self, F, report_coefficients=False): tau = self.value_group().index(self._base_valuation.value_group()) ret = RR(coeffs)(self.phi()**tau) - ret = ret.map_coefficients(lambda c:_lift_to_maximal_precision(c)) + ret = ret.map_coefficients(_lift_to_maximal_precision) return ret def lift_to_key(self, F, check=True): @@ -1512,8 +1512,8 @@ def lift_to_key(self, F, check=True): coefficients = self.lift(F, report_coefficients=True)[:-1] coefficients = [c*self._Q(F.degree()) for i,c in enumerate(coefficients)] + [self.domain().one()] if len(coefficients) >= 2: - # The second-highest coefficient could %phi spill over into the - # highest constant (which is a constant one) so we need to mod it + # After reduction modulo phi, the second-highest coefficient could spill over into the + # highest coefficient (which is a constant one) so we need to mod it # away. # This can not happen for other coefficients because self._Q() has # degree at most the degree of phi. @@ -1522,7 +1522,7 @@ def lift_to_key(self, F, check=True): vf = self._mu * tau * F.degree() ret = self.domain().change_ring(self.domain())([c for c in coefficients])(self.phi()**tau) ret = self.simplify(ret, error=vf, force=True) - ret = ret.map_coefficients(lambda c:_lift_to_maximal_precision(c)) + ret = ret.map_coefficients(_lift_to_maximal_precision) assert (ret == self.phi()) == (F == F.parent().gen()) assert self.is_key(ret) return ret From 58bb2bcdcb5a8212d95b168fa212ce2ec4dcbb55 Mon Sep 17 00:00:00 2001 From: shivachid <30356524+shivachid@users.noreply.github.com> Date: Fri, 21 Jul 2017 19:40:42 -0400 Subject: [PATCH 276/740] Update developing_valuation.py --- src/sage/rings/valuation/developing_valuation.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/valuation/developing_valuation.py b/src/sage/rings/valuation/developing_valuation.py index efbb1a23782..5dd8b1d50d8 100644 --- a/src/sage/rings/valuation/developing_valuation.py +++ b/src/sage/rings/valuation/developing_valuation.py @@ -111,11 +111,11 @@ def effective_degree(self, f, valuations=None): return [i for i,w in enumerate(valuations) if w == v][-1] @cached_method - def _pow(self, x, e, error, effective_degree): + def _pow(self, f, e, error, effective_degree): r""" - Return `x^e`. + Return `f^e`. - This method does not compute the exact value of `x^e` but only an + This method does not compute the exact value of `f^e` but only an element that differs from the correct result by an error with valuation at least ``error``. The output is assumed to have at most ``effective_degree``. @@ -132,11 +132,11 @@ def _pow(self, x, e, error, effective_degree): if e == 0: return self.domain().one() if e == 1: - return self.simplify(x, error=error) + return self.simplify(f, error=error) if e % 2 == 0: - return self._pow(self.simplify(x*x, error=error*2/e, effective_degree=effective_degree*2/e), e//2, error=error, effective_degree=effective_degree) + return self._pow(self.simplify(f*f, error=error*2/e, effective_degree=effective_degree*2/e), e//2, error=error, effective_degree=effective_degree) else: - return self.simplify(x*self._pow(x, e-1, error=error*(e-1)/e, effective_degree=effective_degree*(e-1)/e), error=error, effective_degree=effective_degree) + return self.simplify(f*self._pow(f, e-1, error=error*(e-1)/e, effective_degree=effective_degree*(e-1)/e), error=error, effective_degree=effective_degree) def coefficients(self, f): r""" From e1c8bd523eff279ab49dda555a11dea5c208d094 Mon Sep 17 00:00:00 2001 From: roed314 Date: Fri, 21 Jul 2017 22:51:19 -0400 Subject: [PATCH 277/740] Update limit_valuation.py --- src/sage/rings/valuation/limit_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/limit_valuation.py b/src/sage/rings/valuation/limit_valuation.py index bb14f3290e4..03e1e898ab5 100644 --- a/src/sage/rings/valuation/limit_valuation.py +++ b/src/sage/rings/valuation/limit_valuation.py @@ -636,7 +636,7 @@ def _improve_approximation_for_reduce(self, f): def residue_ring(self): r""" - Return the residue field of this valuation. + Return the residue ring of this valuation, which is always a field. EXAMPLES:: From a0f6bd3d1a9639d5645f09099e5858de36c5912b Mon Sep 17 00:00:00 2001 From: roed314 Date: Fri, 21 Jul 2017 23:19:17 -0400 Subject: [PATCH 278/740] Update mapped_valuation.py --- src/sage/rings/valuation/mapped_valuation.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/valuation/mapped_valuation.py b/src/sage/rings/valuation/mapped_valuation.py index 4f69d398e03..87573aeced3 100644 --- a/src/sage/rings/valuation/mapped_valuation.py +++ b/src/sage/rings/valuation/mapped_valuation.py @@ -93,7 +93,7 @@ def _repr_(self): def residue_ring(self): r""" - Return the residue field of this valuation. + Return the residue ring of this valuation. EXAMPLES:: @@ -388,12 +388,12 @@ def _relative_size(self, x): Return an estimate on the coefficient size of ``x``. The number returned is an estimate on the factor between the number of - Bits used by ``x`` and the minimal number of bits used by an element - Congruent to ``x``. + bits used by ``x`` and the minimal number of bits used by an element + congruent to ``x``. This is used by :meth:`simplify` to decide whether simplification of - Coefficients is going to lead to a significant shrinking of the - Coefficients of ``x``. + coefficients is going to lead to a significant shrinking of the + coefficients of ``x``. EXAMPLES:: From 33aa99f1aa6c8e4478cd73c1808b3e7918a29402 Mon Sep 17 00:00:00 2001 From: roed314 Date: Sat, 22 Jul 2017 02:01:00 -0400 Subject: [PATCH 279/740] Update padic_valuation.py --- src/sage/rings/padics/padic_valuation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 6b89af726c3..8571f971a9d 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -286,7 +286,7 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime, from sage.rings.valuation.gauss_valuation import GaussValuation v = GaussValuation(G.parent(), v) if v.domain() != G.parent(): - # Then, we lift valuations defined on polynmial rings which are + # Then, we lift valuations defined on polynomial rings which are # subrings of K[x] to K[x] v = v.extension(G.parent()) elif _fraction_field(v.domain()) == L: @@ -578,7 +578,7 @@ def is_unramified(self, G, include_steps=False, assume_squarefree=False): R = G.parent() from sage.rings.polynomial.polynomial_ring import is_PolynomialRing - if not is_PolynomialRing(R) or R.base_ring() is not self.domain() or not len(R.gens()) == 1 or not G.is_monic(): + if not is_PolynomialRing(R) or R.base_ring() is not self.domain() or not G.is_monic(): raise ValueError("G must be a monic univariate polynomial over the domain of this valuation") if not assume_squarefree and not G.is_squarefree(): raise ValueError("G must be squarefree") @@ -657,7 +657,7 @@ def is_totally_ramified(self, G, include_steps=False, assume_squarefree=False): R = G.parent() from sage.rings.polynomial.polynomial_ring import is_PolynomialRing - if not is_PolynomialRing(R) or R.base_ring() is not self.domain() or not len(R.gens()) == 1 or not G.is_monic(): + if not is_PolynomialRing(R) or R.base_ring() is not self.domain() or not G.is_monic(): raise ValueError("G must be a monic univariate polynomial over the domain of this valuation") if not assume_squarefree and not G.is_squarefree(): raise ValueError("G must be squarefree") @@ -833,7 +833,7 @@ class pAdicValuation_padic(pAdicValuation_base): TESTS:: - sage: TestSuite(v).run() # optional: integrated, long time + sage: TestSuite(v).run() # long time """ def __init__(self, parent): From b90ba64d0b4d113e7d1c3548832ce4ea8d732fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 01:21:32 -0500 Subject: [PATCH 280/740] Fix doctest for left action --- src/sage/rings/valuation/valuation_space.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index 6003dfb9551..e542d9e297a 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -126,6 +126,7 @@ def _get_action_(self, S, op, self_on_left): sage: v = QQ.valuation(2) sage: from operator import mul sage: v.parent().get_action(ZZ, mul) # indirect doctest + Left action by Integer Ring on Discrete pseudo-valuations on Rational Field """ from operator import mul, div From 37d718725faa28404b3db194cc81b9eede19c01e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 01:25:06 -0500 Subject: [PATCH 281/740] Remove absolute_import otherwise it shows up if you type valuations. --- src/sage/rings/valuation/valuations_catalog.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/valuation/valuations_catalog.py b/src/sage/rings/valuation/valuations_catalog.py index 335bc54f80f..b252cd02366 100644 --- a/src/sage/rings/valuation/valuations_catalog.py +++ b/src/sage/rings/valuation/valuations_catalog.py @@ -1,6 +1,5 @@ -from __future__ import absolute_import from sage.rings.padics.padic_valuation import pAdicValuation from sage.rings.function_field.function_field_valuation import FunctionFieldValuation -from .gauss_valuation import GaussValuation -from .trivial_valuation import TrivialDiscretePseudoValuation, TrivialPseudoValuation, TrivialValuation -from .limit_valuation import LimitValuation +from sage.rings.valuation.gauss_valuation import GaussValuation +from sage.rings.valuation.trivial_valuation import TrivialDiscretePseudoValuation, TrivialPseudoValuation, TrivialValuation +from sage.rings.valuation.limit_valuation import LimitValuation From 8da1ceb17a7bbcfbf174cdb754365d3ddca05cc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 01:32:49 -0500 Subject: [PATCH 282/740] Clarify semigroup construction --- src/sage/rings/valuation/valuation_space.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index e542d9e297a..ab2e91b7f0a 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -430,6 +430,7 @@ def value_semigroup(self): from sage.categories.fields import Fields if self.domain() in Fields(): from value_group import DiscreteValueSemigroup + # return the semigroup generated by the elements of the group return DiscreteValueSemigroup([]) + self.value_group() raise NotImplementedError("can not determine value semigroup of %r"%(self,)) From 45286d752986084a57f1d6248f9bab5fd0f69094 Mon Sep 17 00:00:00 2001 From: roed314 Date: Sat, 22 Jul 2017 02:33:10 -0400 Subject: [PATCH 283/740] Update function_field.py --- src/sage/rings/function_field/function_field.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index b6392900ae3..1d70ac6aba1 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -628,10 +628,10 @@ def valuation(self, prime): sage: R. = K[] sage: L. = K.extension(w^3 - t) sage: N. = FunctionField(L) - sage: w = v.extension(N) # optional: integrated - sage: w(x^3 - t) # optional: integrated + sage: w = v.extension(N) + sage: w(x^3 - t) 1 - sage: w(x - w) # optional: integrated + sage: w(x - w) 1/3 There are several ways to create valuations on extensions of rational From 07e40b964b4ab3a9f7cdf448bcd5956065fd787f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 01:36:43 -0500 Subject: [PATCH 284/740] add shifting doctest --- src/sage/rings/valuation/valuation_space.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index ab2e91b7f0a..03da38cbf75 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -1103,7 +1103,11 @@ def _test_shift(self, **options): if self(x) < 0 and ~self.uniformizer() not in self.domain(): # it is not clear what a shift should be in this case continue - self.shift(x, s) + y = self.shift(x, s) + if s >= 0: + self(y) >= self(x) + if self.domain().is_exact(): + x == self.shift(y, -s) def _test_scale(self, **options): r""" From 021b188797aa024ae6f17f4954f679eabcaed5c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 01:49:45 -0500 Subject: [PATCH 285/740] improve error message ideals can define places, however the infrastructure for ideals in function fields is somewhat lacking so it is not implemented yet. --- src/sage/rings/function_field/function_field_valuation.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/rings/function_field/function_field_valuation.py b/src/sage/rings/function_field/function_field_valuation.py index 79ec971bbc8..85b7205dc89 100644 --- a/src/sage/rings/function_field/function_field_valuation.py +++ b/src/sage/rings/function_field/function_field_valuation.py @@ -229,6 +229,9 @@ def create_key_and_extra_args(self, domain, prime): # unique extension to domain base_valuation = domain.base_field().valuation(prime) return self.create_key_and_extra_args_from_valuation(domain, base_valuation) + from sage.rings.ideal import is_Ideal + if is_Ideal(prime): + raise NotImplementedError("a place can not be given by an ideal yet") raise NotImplementedError("argument must be a place or a pseudo-valuation on a supported subring but %r does not satisfy this for the domain %r"%(prime, domain)) From a6a798f9e713f7237e81eea92cdaca9bc455eda3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 01:56:22 -0500 Subject: [PATCH 286/740] Clarified the notion of minimality --- src/sage/rings/valuation/inductive_valuation.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index 6dd8bc0bdd0..23eb1f5a01e 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -901,10 +901,14 @@ def is_key(self, phi, explain=False, assume_equivalence_irreducible=False): def is_minimal(self, f, assume_equivalence_irreducible=False): r""" Return whether the polynomial ``f`` is minimal with respect to this - valuation, i.e., whether ``f`` is not constant any non-constant - polynomial `h` has at least the degree of ``f`` or ``f`` is not - divisible by `h` with respect to this valuation, i.e., there is no `c` - such that `c h` :meth:`is_equivalent` to `f`. + valuation. + + A polynomial `f` is minimal with respect to `v` if it is not a constant + and any non-zero polynomial `h` which is `v`-divisible by `f` has at + least the degree of `f`. + + A polynomial `h` is `v`-divisible by `f` if there is a polynomial `c` + such that `fc` :meth:`is_equivalent` to `h`. ALGORITHM: From ee0db3141cb365e8e7cdf89ec568b3b493a179e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 01:58:21 -0500 Subject: [PATCH 287/740] clarify meaning of effective_degree --- src/sage/rings/valuation/developing_valuation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/developing_valuation.py b/src/sage/rings/valuation/developing_valuation.py index 5dd8b1d50d8..6d5e8b1f3b1 100644 --- a/src/sage/rings/valuation/developing_valuation.py +++ b/src/sage/rings/valuation/developing_valuation.py @@ -118,7 +118,8 @@ def _pow(self, f, e, error, effective_degree): This method does not compute the exact value of `f^e` but only an element that differs from the correct result by an error with valuation at least ``error``. The output is assumed to have at most - ``effective_degree``. + ``effective_degree``. If the effective degree is higher than + ``effective_degree``, then the result may not be correct. EXAMPLES:: From e9243b8ea32bdaba2694bc77071b3ee41b7355ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 01:59:08 -0500 Subject: [PATCH 288/740] bring residue into the right residue ring --- src/sage/rings/valuation/mapped_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/mapped_valuation.py b/src/sage/rings/valuation/mapped_valuation.py index 87573aeced3..df7eae5cb6d 100644 --- a/src/sage/rings/valuation/mapped_valuation.py +++ b/src/sage/rings/valuation/mapped_valuation.py @@ -193,7 +193,7 @@ def reduce(self, f): u1 """ - return self._base_valuation.reduce(self._to_base_domain(f)) + return self._from_base_residue_ring(self._base_valuation.reduce(self._to_base_domain(f))) def lift(self, F): r""" From 24e68ecc6536c18d63d77d7f56ae4cb27a98ae10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 02:01:36 -0500 Subject: [PATCH 289/740] add to/from residue ring tests --- src/sage/rings/valuation/mapped_valuation.py | 23 ++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/sage/rings/valuation/mapped_valuation.py b/src/sage/rings/valuation/mapped_valuation.py index df7eae5cb6d..82761bace32 100644 --- a/src/sage/rings/valuation/mapped_valuation.py +++ b/src/sage/rings/valuation/mapped_valuation.py @@ -277,6 +277,29 @@ def _test_to_from_base_domain(self, **options): tester.assertEqual(x, self._from_base_domain(self._to_base_domain(x))) # note that the converse might not be true + def _test_to_from_base_domain(self, **options): + r""" + Check the correctness of :meth:`to_base_residue_ring` and + :meth:`from_base_residue_ring`. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + + sage: v = K.valuation(0) + sage: w = v.extensions(L)[0] + sage: w._test_to_from_base_residue_ring() + + """ + tester = self._tester(**options) + + for x in tester.some_elements(self.residue_ring().some_elements()): + tester.assertEqual(x, self._from_base_residue_ring(self._to_base_residue_ring(x))) + for x in tester.some_elements(self._base_valuation.residue_ring().some_elements()): + tester.assertEqual(x, self._to_base_residue_ring(self._from_base_residue_ring(x))) + class FiniteExtensionFromInfiniteValuation(MappedValuation_base, DiscreteValuation): r""" From 791879af5ecb5d00bad8d677f9465f1eeab11c8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 02:07:07 -0500 Subject: [PATCH 290/740] use more robust r strings --- src/sage/rings/padics/padic_valuation.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 8571f971a9d..57c1a278d96 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -""" +r""" `p`-adic valuations on number fields and their subrings and completions. AUTHORS: @@ -25,7 +25,7 @@ from sage.rings.all import infinity class PadicValuationFactory(UniqueFactory): - """ + r""" Create a ``prime``-adic valuation on ``R``. INPUT: @@ -430,7 +430,7 @@ def create_object(self, version, key, **extra_args): pAdicValuation = PadicValuationFactory("pAdicValuation") class pAdicValuation_base(DiscreteValuation): - """ + r""" Abstract base class for `p`-adic valuations. INPUT: @@ -463,7 +463,7 @@ class pAdicValuation_base(DiscreteValuation): """ def __init__(self, parent, p): - """ + r""" TESTS:: sage: from sage.rings.padics.padic_valuation import pAdicValuation_base @@ -477,7 +477,7 @@ def __init__(self, parent, p): self._p = ZZ(p) def p(self): - """ + r""" Return the `p` of this `p`-adic valuation. EXAMPLES:: @@ -489,7 +489,7 @@ def p(self): return self._p def reduce(self, x): - """ + r""" Reduce ``x`` modulo the ideal of elements of positive valuation. INPUT: @@ -515,7 +515,7 @@ def reduce(self, x): return self.residue_field()(x) def lift(self, x): - """ + r""" Lift ``x`` from the residue field to the domain of this valuation. INPUT: @@ -535,7 +535,7 @@ def lift(self, x): return self.domain()(x) def is_unramified(self, G, include_steps=False, assume_squarefree=False): - """ + r""" Return whether ``G`` defines a single unramified extension of the completion of the domain of this valuation. @@ -612,7 +612,7 @@ def is_unramified(self, G, include_steps=False, assume_squarefree=False): return ret def is_totally_ramified(self, G, include_steps=False, assume_squarefree=False): - """ + r""" Return whether ``G`` defines a single totally ramified extension of the completion of the domain of this valuation. From 2bed311a203da9a773b4cefe0bc3c6b5c038394b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 02:32:52 -0500 Subject: [PATCH 291/740] No workaround for number field equality necessary they just uses the equality given by the factory now, so this hack is not neccessary anymore --- src/sage/rings/padics/padic_valuation.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 57c1a278d96..fdb397060ea 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -307,7 +307,7 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime, approximants = [approximant.extension(v.domain()) for approximant in approximants] approximant = vK.mac_lane_approximant(G, v, approximants=tuple(approximants)) - return (R, approximant, L.construction()), {'approximants': approximants} + return (R, approximant), {'approximants': approximants} def create_key_and_extra_args_for_number_field_from_ideal(self, R, I, prime): r""" @@ -344,15 +344,7 @@ def create_key_and_extra_args_for_number_field_from_ideal(self, R, I, prime): if len(candidates_for_I) > 1: raise ValueError("%s does not single out a unique extension of %s to %s"%(prime, vK, L)) else: - # equality of number fields has it quirks since it says that two - # fields are == even if they are distinguishable (because they come - # from different constructions.) - # Including structure() into the key seems to be a way to distinguish such cases properly. - # This used to be an issue but seems to be fixed, namely, the - # absolute_field of a number field was deemed equivalent to the - # directly created absolute field, even though the absolute_field - # carried the information where it came from - return (R, candidates_for_I[0], L.construction()), {'approximants': candidates} + return (R, candidates_for_I[0]), {'approximants': candidates} def _normalize_number_field_data(self, R): r""" From c02de30b137fd6f4a8932ac6f162e083e9aa491a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 02:38:08 -0500 Subject: [PATCH 292/740] Added an example of a p-adic valuation in an order --- src/sage/rings/padics/padic_valuation.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index fdb397060ea..349a84bf915 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -147,6 +147,12 @@ class PadicValuationFactory(UniqueFactory): sage: valuations.pAdicValuation(L, 2) 2-adic valuation + Valuations can also be defined on orders in number fields:: + + sage: O = K.order(2*a) + sage: valuations.pAdicValuation(O, 2) + 2-adic valuation + """ def create_key_and_extra_args(self, R, prime=None, approximants=None): r""" From e3fb24572fe003fe2c3b176bf74656abfd31ef4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 02:41:22 -0500 Subject: [PATCH 293/740] trivial extensions are totally ramified --- src/sage/rings/padics/padic_valuation.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 349a84bf915..ba886b2f1f1 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -651,6 +651,14 @@ def is_totally_ramified(self, G, include_steps=False, assume_squarefree=False): sage: v.is_totally_ramified(G, include_steps=True) (True, [Gauss valuation induced by 5-adic valuation, [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x) = 1/2 ]]) + We consider an extension as totally ramified if its ramification index + matches the degree. Hence, a trivial extension is totally ramified:: + + sage: R. = QQ[] + sage: v = QQ.valuation(2) + sage: v.is_totally_ramified(x) + True + """ R = G.parent() From e959a8b6ec68be19b0a7a3aa2a9d93f876d7d779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 02:43:59 -0500 Subject: [PATCH 294/740] check for subrings so extensions() is not bound to fail --- src/sage/rings/padics/padic_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index ba886b2f1f1..81914d6382a 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -772,7 +772,7 @@ def extensions(self, ring): from sage.categories.all import IntegralDomains if ring in IntegralDomains(): return self._extensions_to_quotient(ring) - else: + elif self.domain().is_subring(ring.base_ring()): return sum([w.extensions(ring) for w in self.extensions(ring.base_ring())], []) from sage.rings.number_field.number_field import is_NumberField if is_NumberField(ring.fraction_field()): From 544977a3161e2f86d1e924b7403fd9709c77620a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 02:46:26 -0500 Subject: [PATCH 295/740] add doctest to element_with_valuation --- src/sage/rings/padics/padic_valuation.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 81914d6382a..1ba85ed574f 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -924,6 +924,12 @@ def element_with_valuation(self, v): sage: v.element_with_valuation(3) 3^3 + O(3^23) + sage: K = Qp(3) + sage: R. = K[] + sage: L. = K.extension(y^2 + 3*y + 3) + sage: L.valuation().element_with_valuation(3/2) + y^3 + O(y^43) + """ from sage.rings.all import QQ, ZZ v = QQ(v) From a0880bd55bb78b4c738917211adf2d0892a33038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 02:48:39 -0500 Subject: [PATCH 296/740] Fix bug in p-adic shift --- src/sage/rings/padics/padic_valuation.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 1ba85ed574f..ced8e61651e 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -998,12 +998,16 @@ def shift(self, x, s): sage: v.shift(R.one(), -1) O(2^19) + sage: S. = R[] + sage: S. = R.extension(y^3 - 2) + sage: v = S.valuation() + sage: v.shift(1, 5) + """ from sage.rings.all import ZZ x = self.domain().coerce(x) s = self.value_group()(s) - v = ZZ(s / self.domain().ramification_index()) - return x << v + return x << s def simplify(self, x, error=None, force=False): r""" From e9eddbd7dc92017f1113612dace917d562c55eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 02:54:31 -0500 Subject: [PATCH 297/740] better size estimate for large primes --- src/sage/rings/padics/padic_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index ced8e61651e..d6ada81c318 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -1163,7 +1163,7 @@ def _relative_size(self, x): """ x = self.domain().coerce(x) - return x.numerator().nbits() + x.denominator().nbits() - 1 + return (x.numerator().nbits() + x.denominator().nbits())//self.p().nbits() def simplify(self, x, error=None, force=False, size_heuristic_bound=32): r""" From 6a1e0f1e537fff7f27ac9713313c50b29f031993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 02:55:35 -0500 Subject: [PATCH 298/740] fixed docstring --- src/sage/rings/padics/padic_valuation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index d6ada81c318..f5b9d333e9f 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -1257,8 +1257,8 @@ def __init__(self, parent, approximant, G, approximants): def _to_base_domain(self, f): r""" - Return ``f``, an element of the domain of this valuation, as an element - of the domain of the underlying limit valuation. + Return ``f``, an element of the underlying limit valuation, as an + element of the domain of this valuation. EXAMPLES:: From 7c1bf020ef92e91440d97a3cc5a7354483ef36c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 03:15:10 -0500 Subject: [PATCH 299/740] Key has changed followup to 2bed311a203da9a773b4cefe0bc3c6b5c038394b --- src/sage/rings/padics/padic_valuation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index f5b9d333e9f..ce817bb3c7e 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -414,7 +414,6 @@ def create_object(self, version, key, **extra_args): return parent.__make_element_class__(pAdicValuation_int)(parent, prime) else: v = key[1] - _ = key[2] # ignored approximants = extra_args['approximants'] parent = DiscretePseudoValuationSpace(R) if is_NumberField(K): From a93ce9ae2b875091b6e3d3b2079835b69026525f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 11:02:46 -0500 Subject: [PATCH 300/740] Some examples that came up at Sage Days 87 --- src/sage/rings/padics/padic_valuation.py | 10 ++++++ src/sage/rings/valuation/valuation.py | 40 ++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index ce817bb3c7e..06a5887b4ab 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -658,6 +658,16 @@ def is_totally_ramified(self, G, include_steps=False, assume_squarefree=False): sage: v.is_totally_ramified(x) True + TESTS: + + An example that Sebastian Pauli used at Sage Days 87:: + + sage: R = ZpFM(3, 20) + sage: S. = R[] + sage: f = x^9 + 9*x^2 + 3 + sage: R.valuation().is_totally_ramified(f) + True + """ R = G.parent() diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 9507fff8ee1..96f5b8ac590 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -658,6 +658,25 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru ... ValueError: G must be integral + Some examples that Sebastian Pauli used in a talk at Sage Days 87. + + :: + + sage: R = ZpFM(3, 7, print_mode='terse') + sage: S. = R[] + sage: v = R.valuation() + sage: f = x^4 + 234 + sage: v.mac_lane_approximants(f) # is_squarefree() not implemented in this ring + sage: v.mac_lane_approximants(f, assume_squarefree=True) + + :: + + sage: R = ZpFM(2, 500, print_mode='terse') + sage: S. = R[] + sage: v = R.valuation() + sage: v.mac_lane_approximants(f) # is_squarefree() is not yet implemented on this ring + sage: v.mac_lane_approximants(f, assume_squarefree=True) + """ R = G.parent() if R.base_ring() is not self.domain(): @@ -920,6 +939,27 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No sage: v.montes_factorization(x^2 - 1, required_precision=5) (x + 1) * (x + 31) + TESTS: + + Some examples that Sebastian Pauli used in a talk at Sage Days 87. + + In this example, ``f`` factors as three factors of degree 50 over an unramified extension:: + + sage: R. = ZqFM(125, 500) + sage: S. = R[] + sage: f = (x^6+2)^25 + 5 + sage: v = R.valuation() + sage: v.montes_factorization(f) + + In this case, ``f`` factors into degrees 1, 2, and 5 over a totally ramified extension:: + + sage: R = Zp(5, 50) + sage: R. = R[] + sage: R. = R.extension(w^3 + 5) + sage: S. = R[] + sage: f = (x^3 + 5)*(x^5 + w) + 625 + sage: v.montes_factorization(f) + REFERENCES: .. [GMN2008] Jordi Guardia, Jesus Montes, Enric Nart (2008). Newton From 709e7f6354ea9420aa3d0462c412a6f4dbcd2832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 11:37:04 -0500 Subject: [PATCH 301/740] GaussValuation is available on the global scope --- .../rings/valuation/developing_valuation.py | 6 +- src/sage/rings/valuation/gauss_valuation.py | 2 +- .../rings/valuation/inductive_valuation.py | 58 +++++++++---------- src/sage/rings/valuation/valuation.py | 4 +- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/sage/rings/valuation/developing_valuation.py b/src/sage/rings/valuation/developing_valuation.py index 6d5e8b1f3b1..fbfe6d364ee 100644 --- a/src/sage/rings/valuation/developing_valuation.py +++ b/src/sage/rings/valuation/developing_valuation.py @@ -31,7 +31,7 @@ class DevelopingValuation(DiscretePseudoValuation): EXAMPLES:: sage: R. = QQ[] - sage: v = valuations.GaussValuation(R, QQ.valuation(7)) + sage: v = GaussValuation(R, QQ.valuation(7)) TESTS:: @@ -43,7 +43,7 @@ def __init__(self, parent, phi): TESTS:: sage: R. = QQ[] - sage: v = valuations.GaussValuation(R, QQ.valuation(7)) + sage: v = GaussValuation(R, QQ.valuation(7)) sage: from sage.rings.valuation.developing_valuation import DevelopingValuation sage: isinstance(v, DevelopingValuation) True @@ -284,7 +284,7 @@ def valuations(self, f): sage: R = Qp(2,5) sage: S. = R[] - sage: v = valuations.GaussValuation(S, R.valuation()) + sage: v = GaussValuation(S, R.valuation()) sage: f = x^2 + 2*x + 16 sage: list(v.valuations(f)) [4, 1, 0] diff --git a/src/sage/rings/valuation/gauss_valuation.py b/src/sage/rings/valuation/gauss_valuation.py index e6a288025ad..7e63a42f39e 100644 --- a/src/sage/rings/valuation/gauss_valuation.py +++ b/src/sage/rings/valuation/gauss_valuation.py @@ -606,7 +606,7 @@ def is_trivial(self): EXAMPLES:: sage: R. = QQ[] - sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) sage: v.is_trivial() True diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index 23eb1f5a01e..b470771021d 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -418,7 +418,7 @@ def _test_augmentation_chain(self, **options): EXAMPLES:: sage: R. = QQ[] - sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) sage: v._test_augmentation_chain() """ @@ -436,7 +436,7 @@ def _test_equivalence_unit(self, **options): EXAMPLES:: sage: R. = QQ[] - sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) sage: v._test_equivalence_unit() """ @@ -468,7 +468,7 @@ def _test_is_equivalence_unit(self, **options): EXAMPLES:: sage: R. = QQ[] - sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) sage: v._test_is_equivalence_unit() """ @@ -482,7 +482,7 @@ def _test_equivalence_reciprocal(self, **options): EXAMPLES:: sage: R. = QQ[] - sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) sage: v._test_equivalence_reciprocal() """ @@ -512,7 +512,7 @@ def _test_inductive_valuation_inheritance(self, **options): EXAMPLES:: sage: R. = QQ[] - sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) sage: v._test_inductive_valuation_inheritance() """ @@ -530,7 +530,7 @@ class FiniteInductiveValuation(InductiveValuation, DiscreteValuation): EXAMPLES:: sage: R. = QQ[] - sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) """ def __init__(self, parent, phi): @@ -538,7 +538,7 @@ def __init__(self, parent, phi): TESTS:: sage: R. = QQ[] - sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) sage: from sage.rings.valuation.inductive_valuation import FiniteInductiveValuation sage: isinstance(v, FiniteInductiveValuation) True @@ -554,7 +554,7 @@ def extensions(self, other): EXAMPLES:: sage: R. = ZZ[] - sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(ZZ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(ZZ)) sage: K. = FunctionField(QQ) sage: v.extensions(K) [Trivial valuation on Rational Field] @@ -578,7 +578,7 @@ class NonFinalInductiveValuation(FiniteInductiveValuation, DiscreteValuation): sage: R. = Qq(4,5) sage: S. = R[] - sage: v = valuations.GaussValuation(S) + sage: v = GaussValuation(S) sage: v = v.augmentation(x^2 + x + u, 1) """ @@ -588,7 +588,7 @@ def __init__(self, parent, phi): sage: R. = Qq(4,5) sage: S. = R[] - sage: v = valuations.GaussValuation(S) + sage: v = GaussValuation(S) sage: v = v.augmentation(x^2 + x + u, 1) sage: from sage.rings.valuation.inductive_valuation import NonFinalInductiveValuation sage: isinstance(v, NonFinalInductiveValuation) @@ -619,7 +619,7 @@ def augmentation(self, phi, mu, check=True): sage: R. = Qq(4,5) sage: S. = R[] - sage: v = valuations.GaussValuation(S) + sage: v = GaussValuation(S) sage: v = v.augmentation(x^2 + x + u, 1) sage: v = v.augmentation((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) sage: v @@ -634,7 +634,7 @@ def augmentation(self, phi, mu, check=True): sage: v_K = QQ.valuation(3) sage: A. = QQ[] - sage: v0 = valuations.GaussValuation(A,v_K) + sage: v0 = GaussValuation(A,v_K) sage: v1 = v0.augmentation(t, 1/12) sage: v2 = v1.augmentation(t^12 + 3, 7/6) @@ -691,10 +691,10 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a sage: K. = FunctionField(QQ) sage: S. = K[] sage: F = y^2 - x^2 - x^3 - 3 - sage: v0 = valuations.GaussValuation(K._ring, QQ.valuation(3)) + sage: v0 = GaussValuation(K._ring, QQ.valuation(3)) sage: v1 = v0.augmentation(K._ring.gen(), 1/3) sage: mu0 = K.valuation(v1) - sage: eta0 = valuations.GaussValuation(S, mu0) + sage: eta0 = GaussValuation(S, mu0) sage: eta1 = eta0.mac_lane_step(F)[0] sage: eta2 = eta1.mac_lane_step(F)[0] sage: eta2 @@ -869,7 +869,7 @@ def is_key(self, phi, explain=False, assume_equivalence_irreducible=False): sage: R. = Qq(4, 5) sage: S. = R[] - sage: v = valuations.GaussValuation(S) + sage: v = GaussValuation(S) sage: v.is_key(x) True sage: v.is_key(2*x, explain = True) @@ -918,7 +918,7 @@ def is_minimal(self, f, assume_equivalence_irreducible=False): sage: R. = Qq(4, 5) sage: S. = R[] - sage: v = valuations.GaussValuation(S) + sage: v = GaussValuation(S) sage: v.is_minimal(x + 1) True sage: w = v.augmentation(x, 1) @@ -930,7 +930,7 @@ def is_minimal(self, f, assume_equivalence_irreducible=False): sage: K = Qp(2, 10) sage: R. = K[] sage: vp = K.valuation() - sage: v0 = valuations.GaussValuation(R, vp) + sage: v0 = GaussValuation(R, vp) sage: v1 = v0.augmentation(x, 1/4) sage: v2 = v1.augmentation(x^4 + 2, 5/4) sage: v2.is_minimal(x^5 + x^4 + 2) @@ -1005,7 +1005,7 @@ def _equivalence_reduction(self, f, coefficients=None, valuations=None, degree_b EXAMPLES:: sage: R. = QQ[] - sage: v = valuations.GaussValuation(R, QQ.valuation(2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: v._equivalence_reduction(2*x^6 + 4*x^5 + 2*x^4 + 8) (1, 4, x^2 + 1) @@ -1064,7 +1064,7 @@ def is_equivalence_irreducible(self, f, coefficients=None, valuations=None): sage: R. = Qq(4,5) sage: S. = R[] - sage: v = valuations.GaussValuation(S) + sage: v = GaussValuation(S) sage: v.is_equivalence_irreducible(x) True sage: v.is_equivalence_irreducible(x^2) @@ -1132,7 +1132,7 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi sage: R. = Qq(4,10) sage: S. = R[] - sage: v = valuations.GaussValuation(S) + sage: v = GaussValuation(S) sage: v.equivalence_decomposition(S.zero()) Traceback (most recent call last): ... @@ -1185,7 +1185,7 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi sage: R.=K[] sage: vp = Q.valuation(2) sage: vp = vp.extension(K) - sage: v0 = valuations.GaussValuation(R, vp) + sage: v0 = GaussValuation(R, vp) sage: G=x^36 + 36*x^35 + 630*x^34 + 7144*x^33 + 59055*x^32 + 379688*x^31 +1978792*x^30 + 8604440*x^29 + 31895428*x^28 + 102487784*x^27 + 289310720*x^26 + 725361352*x^25 + 1629938380*x^24 + 3307417800*x^23 + 6098786184*x^22+10273444280*x^21 + 15878121214*x^20 + 22596599536*x^19 + 29695703772*x^18 +36117601976*x^17 + 40722105266*x^16 + 42608585080*x^15 + 41395961848*x^14 +37344435656*x^13 + 31267160756*x^12 + 24271543640*x^11 + 17439809008*x^10 + 11571651608*x^9 + 7066815164*x^8 + 3953912472*x^7 + 2013737432*x^6 + 925014888*x^5 + 378067657*x^4 + 134716588*x^3 + 40441790*x^2 + 9532544*x + 1584151 sage: v1 = v0.mac_lane_step(G)[0] sage: V = v1.mac_lane_step(G) @@ -1286,7 +1286,7 @@ def minimal_representative(self, f): sage: R. = Qq(4,10) sage: S. = R[] - sage: v = valuations.GaussValuation(S) + sage: v = GaussValuation(S) sage: v.minimal_representative(x + 2) (1 + O(2^10))*x @@ -1361,7 +1361,7 @@ def lift_to_key(self, F): sage: R. = Qq(4,10) sage: S. = R[] - sage: v = valuations.GaussValuation(S) + sage: v = GaussValuation(S) sage: y = v.residue_ring().gen() sage: u0 = v.residue_ring().base_ring().gen() sage: f = v.lift_to_key(y^2 + y + u0); f @@ -1376,7 +1376,7 @@ def _test_lift_to_key(self, **options): EXAMPLES:: sage: R. = QQ[] - sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) sage: v._test_lift_to_key() """ @@ -1425,7 +1425,7 @@ def _test_is_equivalence_irreducible(self, **options): EXAMPLES:: sage: R. = QQ[] - sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) sage: v._test_is_equivalence_irreducible() """ @@ -1451,7 +1451,7 @@ class FinalInductiveValuation(InductiveValuation): TESTS:: sage: R. = QQ[] - sage: v = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) sage: w = v.augmentation(x^2 + x + 1, infinity) sage: from sage.rings.valuation.inductive_valuation import FinalInductiveValuation sage: isinstance(w, FinalInductiveValuation) @@ -1468,7 +1468,7 @@ class InfiniteInductiveValuation(FinalInductiveValuation, InfiniteDiscretePseudo EXAMPLES:: sage: R. = QQ[] - sage: v = valuations.GaussValuation(R, QQ.valuation(2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, infinity) """ @@ -1477,7 +1477,7 @@ def __init__(self, parent, base_valuation): TESTS:: sage: R. = QQ[] - sage: v = valuations.GaussValuation(R, QQ.valuation(2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, infinity) sage: from sage.rings.valuation.inductive_valuation import InfiniteInductiveValuation sage: isinstance(w, InfiniteInductiveValuation) @@ -1496,7 +1496,7 @@ def change_domain(self, ring): We can turn an infinite valuation into a valuation on the quotient:: sage: R. = QQ[] - sage: v = valuations.GaussValuation(R, QQ.valuation(2)) + sage: v = GaussValuation(R, QQ.valuation(2)) sage: w = v.augmentation(x^2 + x + 1, infinity) sage: w.change_domain(R.quo(x^2 + x + 1)) 2-adic valuation diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 96f5b8ac590..0946ac5d0b4 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -493,13 +493,13 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: R. = K[] sage: F = y^21 + x*y^20 + (x^3 + x + 1)*y^18 + (x^3 + 1)*y^17 + (x^4 + x)*y^16 + (x^7 + x^6 + x^3 + x + 1)*y^15 + x^7*y^14 + (x^8 + x^7 + x^6 + x^4 + x^3 + 1)*y^13 + (x^9 + x^8 + x^4 + 1)*y^12 + (x^11 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2)*y^11 + (x^12 + x^9 + x^8 + x^7 + x^5 + x^3 + x + 1)*y^10 + (x^14 + x^13 + x^10 + x^9 + x^8 + x^7 + x^6 + x^3 + x^2 + 1)*y^9 + (x^13 + x^9 + x^8 + x^6 + x^4 + x^3 + x)*y^8 + (x^16 + x^15 + x^13 + x^12 + x^11 + x^7 + x^3 + x)*y^7 + (x^17 + x^16 + x^13 + x^9 + x^8 + x)*y^6 + (x^17 + x^16 + x^12 + x^7 + x^5 + x^2 + x + 1)*y^5 + (x^19 + x^16 + x^15 + x^12 + x^6 + x^5 + x^3 + 1)*y^4 + (x^18 + x^15 + x^12 + x^10 + x^9 + x^7 + x^4 + x)*y^3 + (x^22 + x^21 + x^20 + x^18 + x^13 + x^12 + x^9 + x^8 + x^7 + x^5 + x^4 + x^3)*y^2 + (x^23 + x^22 + x^20 + x^17 + x^15 + x^14 + x^12 + x^9)*y + x^25 + x^23 + x^19 + x^17 + x^15 + x^13 + x^11 + x^5 sage: x = K._ring.gen() - sage: v0 = K.valuation(valuations.GaussValuation(K._ring, valuations.TrivialValuation(k)).augmentation(x,1)) + sage: v0 = K.valuation(GaussValuation(K._ring, valuations.TrivialValuation(k)).augmentation(x,1)) sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assumes squarefree for speed [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x + 1) = 3/2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y) = 4/3, v(y^3 + x^4) = 13/3 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x) = 2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y^15 + y^13 + (x + 1)*y^12 + x*y^11 + (x + 1)*y^10 + y^9 + y^8 + x*y^6 + x*y^5 + y^4 + y^3 + y^2 + (x + 1)*y + x + 1) = 2 ]] - sage: v0 = K.valuation(valuations.GaussValuation(K._ring, valuations.TrivialValuation(k)).augmentation(x+1,1)) + sage: v0 = K.valuation(GaussValuation(K._ring, valuations.TrivialValuation(k)).augmentation(x+1,1)) sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assumes squarefree for speed [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y) = 7/2, v(y^2 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1) = 15/2 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y + x^2 + 1) = 7/2 ], From a86ed5e4b32c3b27fcc41eaf098c16255b6832ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 11:44:50 -0500 Subject: [PATCH 302/740] added introductory examples for developing valuations --- .../rings/valuation/developing_valuation.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/sage/rings/valuation/developing_valuation.py b/src/sage/rings/valuation/developing_valuation.py index fbfe6d364ee..3004d81e396 100644 --- a/src/sage/rings/valuation/developing_valuation.py +++ b/src/sage/rings/valuation/developing_valuation.py @@ -5,6 +5,34 @@ This file implements a base class for discrete valuations on polynomial rings, defined by a `\phi`-adic expansion. +EXAMPLES: + +The :class:`GaussValuation` is a simple example of a valuation that relies on +`\phi`-adic expansions:: + + sage: R. = QQ[] + sage: v = GaussValuation(R, QQ.valuation(2)) + +In this case, `\phi = x`, so the expansion simply lists the coefficients of the +polynomial:: + + sage: f = x^2 + 2*x + 2 + sage: list(v.coefficients(f)) + +Often only the first few coefficients are necessary in computations, so for +performance reasons, coefficients are computed lazily:: + + sage: v.coefficients(f) + +Another example of a :class:`DevelopingValuation` is an +:class:`AugmentedValuation`:: + + sage: w = v.augmentation(x^2 + 2, 3) + +Here, the expansion lists the remainders of repeated division by `x^2 + 2`:: + + sage: list(w.coefficients(f)) + AUTHORS: - Julian Rüth (2013-04-15): initial version From f305a7f0240a602ee1abf1a1cda2723de79ce782 Mon Sep 17 00:00:00 2001 From: roed314 Date: Sat, 22 Jul 2017 13:03:52 -0400 Subject: [PATCH 303/740] Update inductive_valuation.py --- src/sage/rings/valuation/inductive_valuation.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index 23eb1f5a01e..39ea97cbc7d 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -103,9 +103,9 @@ def equivalence_reciprocal(self, f, coefficients=None, valuations=None, check=Tr sage: S. = R[] sage: v = GaussValuation(S) sage: f = 3*x + 2 - sage: h = v.equivalence_reciprocal(f); h # optional: integrated (needs xgcd for polynomials with p-adic coefficients) + sage: h = v.equivalence_reciprocal(f); h # (needs xgcd for polynomials with p-adic coefficients) 2 + 3 + 3^2 + 3^3 + 3^4 + O(3^5) - sage: v.is_equivalent(f*h, 1) # optional: integrated + sage: v.is_equivalent(f*h, 1) True In an extended valuation over an extension field:: @@ -828,7 +828,6 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a continue for i, slope in enumerate(slopes): - slope = slopes[i] verbose("Slope = %s"%slope, level=12) new_mu = old_mu - slope new_valuations = [val - (j*slope if slope is not -infinity else (0 if j == 0 else -infinity)) for j,val in enumerate(w_valuations)] From 7bc7f170e970245c65c39e379a84d099234e8c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 12:40:21 -0500 Subject: [PATCH 304/740] Improved intro sections of valuation files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The style guide tells me to put AUTHORS all the way to the top…I find it a bit weird but anyway. --- src/doc/en/reference/references/index.rst | 15 +++++ .../function_field_valuation.py | 10 +++- src/sage/rings/padics/padic_valuation.py | 23 ++++++- .../rings/valuation/augmented_valuation.py | 20 +++---- .../rings/valuation/developing_valuation.py | 8 +-- src/sage/rings/valuation/gauss_valuation.py | 5 +- .../rings/valuation/inductive_valuation.py | 28 +++++---- src/sage/rings/valuation/limit_valuation.py | 9 ++- src/sage/rings/valuation/mapped_valuation.py | 10 +++- src/sage/rings/valuation/scaled_valuation.py | 10 +++- src/sage/rings/valuation/trivial_valuation.py | 60 +++++++++---------- src/sage/rings/valuation/valuation.py | 39 +++++++++++- src/sage/rings/valuation/valuation_space.py | 8 +-- .../rings/valuation/valuations_catalog.py | 2 + src/sage/rings/valuation/value_group.py | 10 ++-- 15 files changed, 180 insertions(+), 77 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index e14bc17d65e..a7759a07898 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -770,6 +770,9 @@ REFERENCES: of Hecke points. Forum Mathematicum, 15:2, pp. 165--189, De Gruyter, 2003. +.. [GMN2008] Jordi Guardia, Jesus Montes, Enric Nart. *Newton polygons of higher + order in algebraic number theory* (2008). arXiv:0807.2620 [math.NT] + .. [Go1967] Solomon Golomb, Shift register sequences, Aegean Park Press, Laguna Hills, Ca, 1967 @@ -1230,6 +1233,14 @@ REFERENCES: Atoms, Journal of Algebraic Combinatorics, Vol. 29, (2009), No. 3, p.295-313. :arXiv:`0707.4267` +.. [Mac1936] Saunders MacLane, *A construction for prime ideals as absolute + values of an algebraic field*. Duke Mathematical Journal, 2(3) + (1936), 492-510. + +.. [Mac1936'] Saunders MacLane, *A construction for absolute values in + polynomial rings*. Transactions of the American Mathematical + Society, 40(3)(1936), 363-395. + .. [Mac1915] Percy A. MacMahon, *Combinatory Analysis*, Cambridge University Press (1915--1916). (Reprinted: Chelsea, New York, 1960). @@ -1507,6 +1518,10 @@ REFERENCES: .. [Rud1958] \M. E. Rudin. *An unshellable triangulation of a tetrahedron*. Bull. Amer. Math. Soc. 64 (1958), 90-91. +.. [Rüt2014] Julian Rüth, *Models of Curves and Valuations*. Open Access + Repositorium der Universität Ulm. Dissertation (2014). + http://dx.doi.org/10.18725/OPARU-3275 + .. _ref-S: **S** diff --git a/src/sage/rings/function_field/function_field_valuation.py b/src/sage/rings/function_field/function_field_valuation.py index 85b7205dc89..ab54f2be4c3 100644 --- a/src/sage/rings/function_field/function_field_valuation.py +++ b/src/sage/rings/function_field/function_field_valuation.py @@ -2,6 +2,10 @@ r""" Discrete valuations on function fields +AUTHORS: + +- Julian Rüth (2016-10-16): initial version + EXAMPLES: We can create classical valuations that correspond to finite and infinite @@ -123,9 +127,11 @@ sage: w = K.valuation(v) sage: TestSuite(w).run() # long time -AUTHORS: +REFERENCES: -- Julian Rüth (2016-10-16): initial version +An overview of some computational tools relating to valuations on function +fields can be found in Section 4.6 of [Rüt2014]_. Most of this was originally +developed for number fields in [Mac1936]_ and [Mac1936']_. """ #***************************************************************************** diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 06a5887b4ab..6d793efec25 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -1,11 +1,32 @@ # -*- coding: utf-8 -*- r""" -`p`-adic valuations on number fields and their subrings and completions. +`p`-adic valuations on number fields and their subrings and completions + +EXAMPLES:: + + sage: ZZ.valuation(2) + sage: QQ.valuation(3) + sage: GaussianIntegers().valuation(5) + sage: CyclotomicField(5).valuation(7) + sage: Zp(11).valuation() + +These valuations can then, e.g., be used to compute factorizations in the +completion of a ring:: + + sage: v = ZZ.valuation(2) + sage: R. = ZZ[] + sage: f = x^5 + x^4 + x^3 + x^2 + x - 1 + sage: v.montes_factorization(f) AUTHORS: - Julian Rüth (2013-03-16): initial version +REFERENCES: + +The theory used here was originally developed in [Mac1936]_ and [Mac1936']_. An +overview can also be found in Chapter 4 of [Rüt2014]_. + """ #***************************************************************************** # Copyright (C) 2013-2016 Julian Rüth diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index 4c2ab161cbe..0bcb400072c 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -2,7 +2,13 @@ r""" Augmented valuations on polynomial rings -Implements augmentations of valutions as defined in [ML1936]. +Implements augmentations of (inductive) valuations. + +AUTHORS: + +- Julian Rüth (2013-04-15): initial version + +EXAMPLES: Starting from a :class:`GaussValuation`, we can create augmented valuations on polynomial rings:: @@ -133,16 +139,8 @@ REFERENCES: -.. [ML1936] Mac Lane, S. (1936). A construction for prime ideals as absolute -values of an algebraic field. Duke Mathematical Journal, 2(3), 492-510. - -.. [ML1936'] MacLane, S. (1936). A construction for absolute values in -polynomial rings. Transactions of the American Mathematical Society, 40(3), -363-395. - -AUTHORS: - -- Julian Rüth (2013-04-15): initial version +Augmentations are described originally in [ML1936]_ and [ML1936']_. An overview +can also be found in Chapter 4 of [Rüt2014]_. """ #***************************************************************************** diff --git a/src/sage/rings/valuation/developing_valuation.py b/src/sage/rings/valuation/developing_valuation.py index 3004d81e396..81958b7d67b 100644 --- a/src/sage/rings/valuation/developing_valuation.py +++ b/src/sage/rings/valuation/developing_valuation.py @@ -5,6 +5,10 @@ This file implements a base class for discrete valuations on polynomial rings, defined by a `\phi`-adic expansion. +AUTHORS: + +- Julian Rüth (2013-04-15): initial version + EXAMPLES: The :class:`GaussValuation` is a simple example of a valuation that relies on @@ -33,10 +37,6 @@ sage: list(w.coefficients(f)) -AUTHORS: - -- Julian Rüth (2013-04-15): initial version - """ #***************************************************************************** # Copyright (C) 2013-2017 Julian Rüth diff --git a/src/sage/rings/valuation/gauss_valuation.py b/src/sage/rings/valuation/gauss_valuation.py index 7e63a42f39e..fc22812f937 100644 --- a/src/sage/rings/valuation/gauss_valuation.py +++ b/src/sage/rings/valuation/gauss_valuation.py @@ -10,7 +10,10 @@ - Julian Rüth (2013-04-15): initial version -EXAMPLES:: +EXAMPLES: + +A Gauss valuation maps a polynomial to the minimal valuation of any of its +coefficients:: sage: R. = QQ[] sage: v0 = QQ.valuation(2) diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index d22e9197d5d..8b49b1c0921 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -9,14 +9,24 @@ - Julian Rüth (2016-11-01): initial version -REFERENCES: +EXAMPLES: + +A :class:`GaussValuation` is an example of an inductive valuation:: + + sage: R. = QQ[] + sage: v = GaussValuation(R, QQ.valuation(2)) + +Generally, an inductive valuation is an augmentation of an inductive valuation, +i.e., a valuation that was created from a Gauss valuation in a finite number of +augmentation steps:: -.. [ML1936] Mac Lane, S. (1936). A construction for prime ideals as absolute -values of an algebraic field. Duke Mathematical Journal, 2(3), 492-510. + sage: w = v.augmentation(x, 1) + sage: w = w.augmentation(x^2 + 2, 3) -.. [ML1936'] MacLane, S. (1936). A construction for absolute values in -polynomial rings. Transactions of the American Mathematical Society, 40(3), -363-395. +REFERENCES: + +Inductive valuations are originally discussed in [Mac1936]_ and [Mac1936']. An +introduction is also given in Chapter 4 of [Rüt2014]_. """ #***************************************************************************** @@ -1192,12 +1202,6 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi sage: v2.equivalence_decomposition(G) (1/387420489) * (x^4 + 2*x^2 + alpha^4 + alpha^3 + 1)^3 * (x^4 + 2*x^2 + 1/2*alpha^4 + alpha^3 + 5*alpha + 1)^3 * (x^4 + 2*x^2 + 3/2*alpha^4 + alpha^3 + 5*alpha + 1)^3 - REFERENCES: - - .. [ML1936'] MacLane, S. (1936). A construction for absolute values in - polynomial rings. Transactions of the American Mathematical Society, 40(3), - 363-395. - """ f = self.domain().coerce(f) diff --git a/src/sage/rings/valuation/limit_valuation.py b/src/sage/rings/valuation/limit_valuation.py index 03e1e898ab5..5b77bcb83dd 100644 --- a/src/sage/rings/valuation/limit_valuation.py +++ b/src/sage/rings/valuation/limit_valuation.py @@ -20,6 +20,10 @@ The classes in this module provide the means of writing down such limits and resulting valuations on quotients. +AUTHORS: + +- Julian Rüth (2016-10-19): initial version + EXAMPLES: In this function field, the unique place of ``K`` which corresponds to the zero @@ -62,9 +66,10 @@ sage: w._base_valuation._base_valuation._approximation [ Gauss valuation induced by Valuation at the infinite place, v(y) = 1/2, v(y^2 - 1/x) = +Infinity ] -AUTHORS: +REFERENCES: -- Julian Rüth (2016-10-19): initial version +Limits of inductive valuations are discussed in [Mac1936]_ and [Mac1936']_. An +overview can also be found in Section 4.6 of [Rüt2014]_. """ #***************************************************************************** diff --git a/src/sage/rings/valuation/mapped_valuation.py b/src/sage/rings/valuation/mapped_valuation.py index 82761bace32..253f97cd386 100644 --- a/src/sage/rings/valuation/mapped_valuation.py +++ b/src/sage/rings/valuation/mapped_valuation.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -Valuations which are implemented through a map to another valuation. +Valuations which are implemented through a map to another valuation EXAMPLES: @@ -55,6 +55,14 @@ class MappedValuation_base(DiscretePseudoValuation): """ def __init__(self, parent, base_valuation): r""" + .. TODO:: + + It is annoying that we have to wrap any possible method on + ``base_valuation`` in this class. It would be nice if this would + somehow be done automagically, e.g., by adding annotations to the + methods in ``base_valuation`` that explain which parameters and + return values need to be mapped and how. + TESTS:: sage: K. = FunctionField(QQ) diff --git a/src/sage/rings/valuation/scaled_valuation.py b/src/sage/rings/valuation/scaled_valuation.py index 56db6d22965..a8b1bf06525 100644 --- a/src/sage/rings/valuation/scaled_valuation.py +++ b/src/sage/rings/valuation/scaled_valuation.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -Valuations which are scaled versions of another valuation. +Valuations which are scaled versions of another valuation EXAMPLES: @@ -101,6 +101,14 @@ class ScaledValuation_generic(DiscreteValuation): """ def __init__(self, parent, base_valuation, s): r""" + .. TODO:: + + It is annoying that we have to wrap any possible method on + ``base_valuation`` in this class. It would be nice if this would + somehow be done automagically, e.g., by adding annotations to the + methods in ``base_valuation`` that explain which parameters and + return values need to be scaled. + TESTS:: sage: v = 3*ZZ.valuation(2) diff --git a/src/sage/rings/valuation/trivial_valuation.py b/src/sage/rings/valuation/trivial_valuation.py index 1f21fa26ea2..5433eecc4ba 100644 --- a/src/sage/rings/valuation/trivial_valuation.py +++ b/src/sage/rings/valuation/trivial_valuation.py @@ -2,6 +2,10 @@ r""" Trivial valuations +AUTHORS: + +- Julian Rüth (2016-10-14): initial version + EXAMPLES:: sage: v = valuations.TrivialValuation(QQ); v @@ -9,37 +13,33 @@ sage: v(1) 0 -.. NOTE: - -Note that the tests in this module do not create instances of valuations -directly since this gives the wrong inheritance structure on the resulting -objects:: - - sage: from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace - sage: from sage.rings.valuation.trivial_valuation import TrivialDiscretePseudoValuation - sage: H = DiscretePseudoValuationSpace(QQ) - sage: v = TrivialDiscretePseudoValuation(H) - sage: v._test_category() - Traceback (most recent call last): - ... - AssertionError: False is not true - -Instead, the valuations need to be created through the -``__make_element_class__`` of the containing space:: - - sage: from sage.rings.valuation.trivial_valuation import TrivialDiscretePseudoValuation - sage: v = H.__make_element_class__(TrivialDiscretePseudoValuation)(H) - sage: v._test_category() - -The factories ``TrivialValuation`` and ``TrivialPseudoValuation`` provide the -right inheritance structure:: +.. NOTE:: - sage: v = valuations.TrivialPseudoValuation(QQ) - sage: v._test_category() - -AUTHORS: - -- Julian Rüth (2016-10-14): initial version + Note that the tests in this module do not create instances of valuations + directly since this gives the wrong inheritance structure on the resulting + objects:: + + sage: from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace + sage: from sage.rings.valuation.trivial_valuation import TrivialDiscretePseudoValuation + sage: H = DiscretePseudoValuationSpace(QQ) + sage: v = TrivialDiscretePseudoValuation(H) + sage: v._test_category() + Traceback (most recent call last): + ... + AssertionError: False is not true + + Instead, the valuations need to be created through the + ``__make_element_class__`` of the containing space:: + + sage: from sage.rings.valuation.trivial_valuation import TrivialDiscretePseudoValuation + sage: v = H.__make_element_class__(TrivialDiscretePseudoValuation)(H) + sage: v._test_category() + + The factories ``TrivialValuation`` and ``TrivialPseudoValuation`` provide the + right inheritance structure:: + + sage: v = valuations.TrivialPseudoValuation(QQ) + sage: v._test_category() """ #***************************************************************************** diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 0946ac5d0b4..029cf2d4ea8 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -8,6 +8,40 @@ - Julian Rüth (2013-03-16): initial version +EXAMPLES: + +Discrete valuations can be created on a variety of rings:: + + sage: ZZ.valuation(2) + 2-adic valuation + sage: GaussianIntegers().valuation(3) + 3-adic valuation + sage: QQ.valuation(5) + 5-adic valuation + sage: Zp(7).valuation() + 7-adic valuation + +:: + + sage: K. = FunctionField(QQ) + sage: K.valuation(x) + x-adic valuation + sage: K.valuation(x^2 + 1) + (x^2 + 1)-adic valuation + sage: K.valuation(1/x) + +:: + + sage: R. = QQ[] + sage: v = QQ.valuation(2) + sage: w = GaussValuation(R, v) + sage: w.augmentation(x, 3) + +We can also define discrete pseudo-valuations, i.e., discrete valuations that +send more than just zero to infinity:: + + sage: w.augmentation(x, infinity) + """ #***************************************************************************** # Copyright (C) 2013-2017 Julian Rüth @@ -962,9 +996,8 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No REFERENCES: - .. [GMN2008] Jordi Guardia, Jesus Montes, Enric Nart (2008). Newton - polygons of higher order in algebraic number theory. arXiv:0807.2620 - [math.NT] + The underlying algorithm is described in [Mac1936']_and thoroughly + analyzed in [GMN2008]_. """ if required_precision is None: diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index 03da38cbf75..c7b2883c487 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -7,15 +7,15 @@ discrete, i.e., their image is a discrete additive subgroup of the rational numbers extended by `\infty`. +AUTHORS: + +- Julian Rüth (2016-10-14): initial version + EXAMPLES:: sage: QQ.valuation(2).parent() Discrete pseudo-valuations on Rational Field -AUTHORS: - -- Julian Rüth (2016-10-14): initial version - """ #***************************************************************************** # Copyright (C) 2016-2017 Julian Rüth diff --git a/src/sage/rings/valuation/valuations_catalog.py b/src/sage/rings/valuation/valuations_catalog.py index b252cd02366..ebd7c2f8290 100644 --- a/src/sage/rings/valuation/valuations_catalog.py +++ b/src/sage/rings/valuation/valuations_catalog.py @@ -1,3 +1,5 @@ +# not using absolute imports here as importing absolute_import would make +# "absolute_import" show up in -completion from sage.rings.padics.padic_valuation import pAdicValuation from sage.rings.function_field.function_field_valuation import FunctionFieldValuation from sage.rings.valuation.gauss_valuation import GaussValuation diff --git a/src/sage/rings/valuation/value_group.py b/src/sage/rings/valuation/value_group.py index abcd918b708..54fd5830295 100644 --- a/src/sage/rings/valuation/value_group.py +++ b/src/sage/rings/valuation/value_group.py @@ -2,7 +2,11 @@ r""" Value groups of discrete valuations -This file defines additive sub(semi-)groups of \QQ and related structures. +This file defines additive sub(semi-)groups of `\Q` and related structures. + +AUTHORS: + +- Julian Rüth (2013-09-06): initial version EXAMPLES:: @@ -12,10 +16,6 @@ sage: v.value_semigroup() Additive Abelian Semigroup generated by 1 -AUTHORS: - -- Julian Rüth (2013-09-06): initial version - """ #***************************************************************************** # Copyright (C) 2013-2017 Julian Rüth From 62134d89bd5b2b3f6064e6d9cf48133558bbe91f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 16:06:26 -0500 Subject: [PATCH 305/740] move README to the reference manual --- src/doc/en/reference/index.rst | 1 + src/doc/en/reference/valuations/conf.py | 1 + src/doc/en/reference/valuations/index.rst | 202 +++++++++++++++++++++ src/sage/rings/valuation/README.md | 208 ---------------------- 4 files changed, 204 insertions(+), 208 deletions(-) create mode 120000 src/doc/en/reference/valuations/conf.py create mode 100644 src/doc/en/reference/valuations/index.rst delete mode 100644 src/sage/rings/valuation/README.md diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst index 51f598b2e39..d9fa1dacc35 100644 --- a/src/doc/en/reference/index.rst +++ b/src/doc/en/reference/index.rst @@ -105,6 +105,7 @@ Number Fields and Function Fields * :doc:`Number Fields ` * :doc:`Function Fields ` +* :doc:`Discrete Valuations ` Number Theory ------------- diff --git a/src/doc/en/reference/valuations/conf.py b/src/doc/en/reference/valuations/conf.py new file mode 120000 index 00000000000..2bdf7e68470 --- /dev/null +++ b/src/doc/en/reference/valuations/conf.py @@ -0,0 +1 @@ +../conf_sub.py \ No newline at end of file diff --git a/src/doc/en/reference/valuations/index.rst b/src/doc/en/reference/valuations/index.rst new file mode 100644 index 00000000000..5ad09944c33 --- /dev/null +++ b/src/doc/en/reference/valuations/index.rst @@ -0,0 +1,202 @@ +Discrete Valuations and Discrene Pseudo-Valuations +================================================== + +High-Level Interface +==================== +Valuations can be defined conveniently on some Sage rings such as p-adic rings +and function fields. + +p-adic valuations +----------------- +Valuations on number fields can be easily specified if they uniquely extend +the valuation of a rational prime:: + + sage: v = QQ.valuation(2) + sage: v(1024) + 10 + +They are normalized such that the rational prime has valuation 1:: + + sage: K. = NumberField(x^2 + x + 1) + sage: K.valuation(2) + sage: v(1024) + 10 + +If there are multiple valuations over a prime, they can be obtained by +extending a valuation from a smaller ring:: + + sage: K. = NumberField(x^2 + x + 1) + sage: v = K.valuation(7) + sage: w,ww = v.extensions(K) + sage: w(a + 3), ww(a + 3) + (1, 0) + sage: w(a + 5), ww(a + 5) + (0, 1) + +Valuations on Function Fields +----------------------------- +Similarly, valuations can be defined on function fields:: + + sage: K. = FunctionField(QQ) + sage: v = K.valuation(x) + sage: v(1/x) + -1 + + sage: v = K.valuation(1/x) + sage: v(1/x) + 1 + +On extensions of function fields, valuations can be specified explicitly by +providing a prime on the underlying rational function field when the extension +is unique:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: v = L.valuation(x) + sage: v(x) + 1 + +Valuations can also be extended from smaller function fields:: + + sage: K. = FunctionField(QQ) + sage: v = K.valuation(x - 4) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: v.extensions(L) + [ (x - 4)-adic valuation, v(y - 2) = 1 ]-adic valuation, + [ (x - 4)-adic valuation, v(y + 2) = 1 ]-adic valuation] + +Low-Level Interface +=================== + +Mac Lane valuations +------------------- +Internally, all the above is backed by the algorithms described in +[MacLane1936]_ and [MacLane1936']_. Let us consider the extensions of +``K.valuation(x - 4)`` to the field `L` above to outline how this works +internally. + +First, the valuation on `K` is induced by a valuation on `\Q[x]`. To construct +this valuation, we start from the trivial valuation on `\Q` and consider its +induced Gauss valuation on `\Q[x]`, i.e., the valuation that assigns to a +polynomial the minimum of the coefficient valuations:: + + sage: R. = QQ[] + sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) + +The Gauss valuation can be augmented by specifying that `x - 4` has valuation 1:: + + sage: v = v.augmentation(x - 4, 1); v + [ Gauss valuation induced by Trivial valuation on Rational Field, v(x - 4) = 1 ] + +This valuation then extends uniquely to the fraction field:: + + sage: K. = FunctionField(QQ) + sage: v = v.extension(K); v + (x - 4)-adic valuation + +Over the function field we repeat the above process, i.e., we define the Gauss +valuation induced by it and augment it to approximate an extension to `L`:: + + sage: R. = K[] + sage: w = GaussValuation(R, v) + sage: w = w.augmentation(y - 2, 1); w + [ Gauss valuation induced by (x - 4)-adic valuation, v(y - 2) = 1 ] + sage: L. = K.extension(y^2 - x) + sage: ww = w.extension(L); ww + [ (x - 4)-adic valuation, v(y - 2) = 1 ]-adic valuation + +Limit valuations +---------------- +In the previous example the final valuation ``ww`` is not merely given by +evaluating ``w`` on the ring `K[y]`:: + + sage: ww(y^2 - x) + +Infinity + sage: y = R.gen() + sage: w(y^2 - x) + 1 + +Instead ``ww`` is given by a limit, i.e., an infinite sequence of +augmentations of valuations:: + + sage: ww._base_valuation + [ Gauss valuation induced by (x - 4)-adic valuation, v(y - 2) = 1 , … ] + +The terms of this infinite sequence are computed on demand:: + + sage: ww._base_valuation._approximation + [ Gauss valuation induced by (x - 4)-adic valuation, v(y - 2) = 1 ] + sage: ww(y - 1/4*x - 1) + 2 + sage: ww._base_valuation._approximation + [ Gauss valuation induced by (x - 4)-adic valuation, v(y - 1/4*x - 1) = 2 ] + +Non-classical valuations +------------------------ +Using the low-level interface we are not limited to classical valuations on +function fields that correspond to points on the corresponding projective +curves. Instead we can start with a non-trivial valuation on the field of +constants:: + + sage: v = QQ.valuation(2) + sage: R. = QQ[] + sage: w = GaussValuation(R, v) # v is not trivial + sage: K. = FunctionField(QQ) + sage: w = w.extension(K) + sage: w.residue_field() + Fraction Field of Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) + +Mac Lane Approximants +===================== +The main tool underlying this package is an algorithm by Mac Lane to compute, +starting from a Gauss valuation on a polynomial ring and a monic squarefree +polynomial G, approximations to the limit valuation which send G to infinity:: + + sage: v = QQ.valuation(2) + sage: R. = QQ[] + sage: f = x^5 + 3*x^4 + 5*x^3 + 8*x^2 + 6*x + 12 + sage: v.mac_lane_approximants(f) + [[ Gauss valuation induced by 2-adic valuation, v(x^2 + x + 1) = 3 ], + [ Gauss valuation induced by 2-adic valuation, v(x) = 1/2 ], + [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ]] + +From these approximants one can already see the residual degrees and +ramification indices of the corresponding extensions. The approximants can be +pushed to arbitrary precision:: + + sage: v.mac_lane_approximants(f, required_precision=10) + [[ Gauss valuation induced by 2-adic valuation, v(x^2 + 193*x + 13/21) = 10 ], + [ Gauss valuation induced by 2-adic valuation, v(x + 86) = 10 ], + [ Gauss valuation induced by 2-adic valuation, v(x) = 1/2, v(x^2 + 36/11*x + 2/17) = 11 ]] + +Note that in the limit they are factors of `f`. + +References +========== + +The theory was originally described in [Mac1936]_ and [Mac1936']_. A summary and some algorithmic details can also be found in Chapter 4 of [Rü2014]_. + +.. toctree:: + :maxdepth: 2 + + sage/rings/valuation/value_group.py + sage/rings/valuation/valuation.py + sage/rings/valuation/valuation_space.py + + sage/rings/valuation/trivial_valuation.py + sage/rings/valuation/gauss_valuation.py + + sage/rings/valuation/developing_valuation.py + sage/rings/valuation/inductive_valuation.py + sage/rings/valuation/augmented_valuation.py + sage/rings/valuation/limit_valuation.py + + sage/rings/valuation/mapped_valuation.py + sage/rings/valuation/scaled_valuation.py + + sage/rings/function_field/function_field_valuation.py + sage/rings/padics/padic_valuation.py + +.. include:: ../footer.txt diff --git a/src/sage/rings/valuation/README.md b/src/sage/rings/valuation/README.md deleted file mode 100644 index 584e6da39f9..00000000000 --- a/src/sage/rings/valuation/README.md +++ /dev/null @@ -1,208 +0,0 @@ -Mac Lane's Algorithms in Sage -============================= -This package implements most of Mac Lane's algorithms [1,2] and related -structures to represent discrete valuations and discrete pseudo-valuations on -rings in Sage. - -The package should run on an unmodified Sage 8.0 and can be imported with -``` -sage: from mac_lane import * -``` - -To run the included tests, execute `sage -tp --optional=sage,standalone mac_lane/`. - -High-Level Interface -==================== -Valuations can be defined conveniently on some Sage rings such as p-adic rings and function fields. - -p-adic valuations ------------------ -Valuations on number fields can be easily specified if they uniquely extend the valuation of a rational prime: -``` -sage: v = pAdicValuation(QQ, 2) -sage: v(1024) -10 -``` - -They are normalized such that the rational prime has valuation 1. -``` -sage: K. = NumberField(x^2 + x + 1) -sage: v = pAdicValuation(K, 2) -sage: v(1024) -10 -``` - -If there are multiple valuations over a prime, they can be obtained by extending a valuation from a smaller ring. -``` -sage: K. = NumberField(x^2 + x + 1) -sage: v = pAdicValuation(QQ, 7) -sage: v.extensions(K) -[[ 7-adic valuation, v(x + 3) = 1 ]-adic valuation, -[ 7-adic valuation, v(x + 5) = 1 ]-adic valuation] -sage: w,ww = _ -sage: w(a + 3), ww(a + 3) -(1, 0) -sage: w(a + 5), ww(a + 5) -(0, 1) -``` - -Function Field valuations -------------------------- -Similarly, valuations can be defined on function fields: -``` -sage: K. = FunctionField(QQ) -sage: v = FunctionFieldValuation(K, x) -sage: v(1/x) --1 - -sage: v = FunctionFieldValuation(K, 1/x) -sage: v(1/x) -1 -``` - -On extensions of function fields, valuations can be specified explicitly by -providing a prime on the underlying rational function field when the extension -is unique:: -``` -sage: K. = FunctionField(QQ) -sage: R. = K[] -sage: L. = K.extension(y^2 - x) -sage: v = FunctionFieldValuation(L, x) -sage: v(x) -1 -``` - -Valuations can also be extended from smaller function fields. -``` -sage: K. = FunctionField(QQ) -sage: v = FunctionFieldValuation(K, x - 4) -sage: R. = K[] -sage: L. = K.extension(y^2 - x) -sage: v.extensions(L) -[ (x - 4)-adic valuation, v(y - 2) = 1 ]-adic valuation, - [ (x - 4)-adic valuation, v(y + 2) = 1 ]-adic valuation] -``` - -Low-Level Interface -=================== - -Mac Lane valuations -------------------- -Internally, all the above is backed by the algorithms described in [1,2]. Let -us consider the extensions of `FunctionFieldValuation(K, x - 4)` to the field -`L` above to outline how this works internally. - -First, the valuation on K is induced by a valuation on ℚ[x]. To construct this -valuation, we start from the trivial valuation on ℚ and consider its induced -Gauss valuation on ℚ[x], i.e., the valuation that assigns to a polynomial the -minimum of the coefficient valuations. -``` -sage: R. = QQ[] -sage: v = TrivialValuation(QQ) -sage: v = GaussValuation(R, v) -``` -The Gauss valuation can be augmented by specifying that x - 4 has valuation 1. -``` -sage: v = v.augmentation(x - 4, 1); v -[ Gauss valuation induced by Trivial valuation on Rational Field, v(x - 4) = 1 ] -``` - -This valuation then extends uniquely to the fraction field. -``` -sage: K. = FunctionField(QQ) -sage: v = v.extension(K); v -(x - 4)-adic valuation -``` - -Over the function field we repeat the above process, i.e., we define the Gauss -valuation induced by it and augment it to approximate an extension to L. -``` -sage: R. = K[] -sage: w = GaussValuation(R, v) -sage: w = w.augmentation(y - 2, 1); w -[ Gauss valuation induced by (x - 4)-adic valuation, v(y - 2) = 1 ] -sage: L. = K.extension(y^2 - x) -sage: ww = w.extension(L); ww -[ (x - 4)-adic valuation, v(y - 2) = 1 ]-adic valuation -``` - -Limit valuations ----------------- -In the previous example the final valuation `ww` is not merely given by evaluating `w` on the ring `K[y]`. -``` -sage: ww(y^2 - x) -+Infinity -sage: y = R.gen() -sage: w(y^2 - x) -1 -``` - -Instead `ww` is given by a limit, i.e., an infinite sequence of augmentations of valuations. -``` -sage: ww._base_valuation -[ Gauss valuation induced by (x - 4)-adic valuation, v(y - 2) = 1 , … ] -``` - -The terms of this infinite sequence are computed on demand. -``` -sage: ww._base_valuation._approximation -[ Gauss valuation induced by (x - 4)-adic valuation, v(y - 2) = 1 ] -sage: ww(y - 1/4*x - 1) -2 -sage: ww._base_valuation._approximation -[ Gauss valuation induced by (x - 4)-adic valuation, v(y - 1/4*x - 1) = 2 ] -``` - -Non-classical valuations ------------------------- -Using the low-level interface we are not limited to classical valuations on -function fields that correspond to points on the corresponding curves. Instead -we can start with a non-trivial valuation on the field of constants. -``` -sage: v = pAdicValuation(QQ, 2) -sage: R. = QQ[] -sage: w = GaussValuation(R, v) # v is not trivial -sage: K. = FunctionField(QQ) -sage: w = w.extension(K) -sage: w.residue_field() -Fraction Field of Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) -``` - -Mac Lane Approximants -===================== -The main tool underlying this package is an algorithm by Mac Lane to compute, -starting from a Gauss valuation on a polynomial ring and a monic squarefree -polynomial G, approximations to the limit valuation which send G to infinity. -``` -sage: v = pAdicValuation(QQ, 2) -sage: R. = QQ[] -sage: f = x^5 + 3*x^4 + 5*x^3 + 8*x^2 + 6*x + 12 -sage: v.mac_lane_approximants(f) -[[ Gauss valuation induced by 2-adic valuation, v(x^2 + x + 1) = 3 ], - [ Gauss valuation induced by 2-adic valuation, v(x) = 1/2 ], - [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ]] -``` - -Note that from these approximants one can already see the residual degrees and -ramification indices of the corresponding extensions. The approximants can be -pushed to arbitrary precision. -``` -sage: sage: v.mac_lane_approximants(f, required_precision=10) -[[ Gauss valuation induced by 2-adic valuation, v(x^2 + 193*x + 13/21) = 10 ], - [ Gauss valuation induced by 2-adic valuation, v(x + 86) = 10 ], - [ Gauss valuation induced by 2-adic valuation, v(x) = 1/2, v(x^2 + 36/11*x + 2/17) = 11 ]] -``` - -Note that in the limit they are factors of `f`. - -References -========== - -[1] Mac Lane, S. (1936). A construction for prime ideals as absolute values of -an algebraic field. Duke Mathematical Journal, 2(3), 492-510. - -[2] MacLane, S. (1936). A construction for absolute values in polynomial rings. -Transactions of the American Mathematical Society, 40(3), 363-395. - -[3] Rüth, J. (2014). Models of Curves and Valuations (PhD thesis) Chapter 4 -"Mac Lane Valuations". From 39ee4e92895e44d022ecc9aebdf7a4aed2eabbfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 16:41:01 -0500 Subject: [PATCH 306/740] Move p-adic valuation factory documentation to the valuation() methods where people might actually read it. --- src/sage/rings/integer_ring.pyx | 5 ++ src/sage/rings/number_field/number_field.py | 77 ++++++++++++++++++- src/sage/rings/number_field/order.py | 51 ++++++++++++- src/sage/rings/padics/padic_generic.py | 5 ++ src/sage/rings/padics/padic_valuation.py | 85 ++------------------- src/sage/rings/rational_field.py | 5 ++ 6 files changed, 148 insertions(+), 80 deletions(-) diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx index 9bd847c2c06..e8592f5aa03 100644 --- a/src/sage/rings/integer_ring.pyx +++ b/src/sage/rings/integer_ring.pyx @@ -1525,6 +1525,11 @@ cdef class IntegerRing_class(PrincipalIdealDomain): sage: v(3) 1 + SEEALSO:: + + :meth:`sage.rings.number_field.order.Order.valuation`, + :meth:`sage.rings.rational_field.RationalField.valuation` + """ from sage.rings.padics.padic_valuation import pAdicValuation return pAdicValuation(self, p) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index f6e15407364..d15fb913a0f 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -6481,11 +6481,84 @@ def valuation(self, prime): r""" Return the valuation on this field defined by ``prime``. - EXAMPLES:: + INPUT: + + - ``prime`` -- a prime that does not split, a discrete + (pseudo-)valuation or a fractional ideal + + EXAMPLES: + + ``prime`` can be an integer that is completely ramified in ``R``:: + + sage: K. = NumberField(x^2 + 1) + sage: K.valuation(2) + 2-adic valuation + + ``prime`` can be an integer that is unramified in ``R``: + + sage: K.valuation(3) + 3-adic valuation + + This is only supported if ``prime`` does not factor into + pairwise distinct factors:: + + sage: K.valuation(5) + Traceback (most recent call last): + ... + ValueError: The valuation Gauss valuation induced by 5-adic valuation does not approximate a unique extension of 5-adic valuation with respect to x^2 + 1 + + ``prime`` can also be specified by providing a valuation on a base ring + that has a unique extension:: + + sage: CyclotomicField(5).valuation(ZZ.valuation(5)) + 5-adic valuation - sage: GaussianIntegers().fraction_field().valuation(2) + When the extension is not unique, this does not work:: + + sage: K.valuation(ZZ.valuation(5)) + Traceback (most recent call last): + ... + ValueError: The valuation Gauss valuation induced by 5-adic valuation does not approximate a unique extension of 5-adic valuation with respect to x^2 + 1 + + For a number field which is of the form `K[x]/(G)`, you can specify a + valuation by providing a discrete pseudo-valuation on `K[x]` which sends + `G` to `\infty`. This lets us specify which extension of the 5-adic + valuation we care about in the above example:: + + sage: R. = QQ[] + sage: v = K.valuation(GaussValuation(R, QQ.valuation(5)).augmentation(x + 2, infinity)) + sage: w = K.valuation(GaussValuation(R, QQ.valuation(5)).augmentation(x + 1/2, infinity)) + sage: v == w + False + + Note that you get the same valuation, even if you write down the + pseudo-valuation differently:: + + sage: ww = K.valuation(GaussValuation(R, QQ.valuation(5)).augmentation(x + 3, infinity)) + sage: w is ww + True + + The valuation ``prime`` does not need to send the defining polynomial `G` + to `\infty`. It is sufficient if it singles out one of the valuations on + the number field. This is important if the prime only factors over the + completion, i.e., if it is not possible to write down one of the factors + within the number field:: + + sage: v = GaussValuation(R, QQ.valuation(5)).augmentation(x + 3, 1) + sage: K.valuation(v) + [ 5-adic valuation, v(x + 3) = 1 ]-adic valuation + + Finally, ``prime`` can also be a fractional ideal of a number field if it + singles out an extension of a `p`-adic valuation of the base field:: + + sage: K.valuation(K.fractional_ideal(a + 1)) 2-adic valuation + SEEALSO:: + + :meth:`sage.rings.number_field.order.Order.valuation`, + :meth:`sage.rings.padics.padic_generic.pAdicGeneric.valuation` + """ from sage.rings.padics.padic_valuation import pAdicValuation return pAdicValuation(self, prime) diff --git a/src/sage/rings/number_field/order.py b/src/sage/rings/number_field/order.py index f6dd03425b3..b2169f85941 100644 --- a/src/sage/rings/number_field/order.py +++ b/src/sage/rings/number_field/order.py @@ -995,9 +995,58 @@ def valuation(self, p): r""" Return the `p`-adic valuation on this order. - EXAMPLES:: + EXAMPLES: + + ``prime`` can be an integer that is completely ramified in the number field:: + + sage: K. = NumberField(x^2 + 1) + sage: O = K.order(2*a) + sage: valuations.pAdicValuation(O, 2) + 2-adic valuation sage: GaussianIntegers().valuation(2) + 2-adic valuation + + ``prime`` can be an integer that is unramified:: + + sage: GaussianIntegers().valuation(3) + 3-adic valuation + + This is only supported if ``prime`` does not factor into + pairwise distinct factors:: + + sage: GaussianIntegers().valuation(5) + Traceback (most recent call last): + ... + ValueError: The valuation Gauss valuation induced by 5-adic valuation does not approximate a unique extension of 5-adic valuation with respect to x^2 + 1 + + ``prime`` can also be specified by providing a valuation on a base ring + that has a unique extension:: + + sage: CyclotomicField(5).ring_of_integers().valuation(ZZ.valuation(5)) + 5-adic valuation + + When the extension is not unique, this does not work:: + + sage: GaussianIntegers().valuation(ZZ.valuation(5)) + Traceback (most recent call last): + ... + ValueError: The valuation Gauss valuation induced by 5-adic valuation does not approximate a unique extension of 5-adic valuation with respect to x^2 + 1 + + If the fraction field is of the form `K[x]/(G)`, you can specify a + valuation by providing a discrete pseudo-valuation on `K[x]` which + sends `G` to `\infty`:: + + sage: R. = QQ[] + sage: v = GaussianIntegers().valuation(GaussValuation(R, QQ.valuation(5)).augmentation(x + 2, infinity)) + sage: w = GaussianIntegers().valuation(GaussValuation(R, QQ.valuation(5)).augmentation(x + 1/2, infinity)) + sage: v == w + False + + SEEALSO:: + + :meth:`sage.rings.number_field.number_field.NumberField.valuation`, + :meth:`sage.rings.padics.padic_generic.pAdicGeneric.valuation` """ from sage.rings.padics.padic_valuation import pAdicValuation diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index a38b98fd44c..0ebf942b92e 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -978,6 +978,11 @@ def valuation(self): sage: v.restriction(K) == K.valuation() True + SEEALSO:: + + :meth:`sage.rings.number_field.number_field.NumberField.valuation`, + :meth:`sage.rings.number_field.order.Order.valuation` + """ from sage.rings.padics.padic_valuation import pAdicValuation return pAdicValuation(self) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 6d793efec25..9ec6e40b871 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -83,79 +83,7 @@ class PadicValuationFactory(UniqueFactory): ... ValueError: prime must be specified for this ring - For number fields, ``prime`` can be an integer that is completely ramified - in ``R``:: - - sage: GaussianIntegers().fraction_field().valuation(2) - 2-adic valuation - - For number fields, ``prime`` can be an integer that is unramified in ``R``: - - sage: GaussianIntegers().fraction_field().valuation(3) - 3-adic valuation - - The same applies if ``R`` is a subring of a number field:: - - sage: GaussianIntegers().valuation(3) - 3-adic valuation - - However, this is only supported if ``prime`` does not factor into - pairwise distinct factors:: - - sage: GaussianIntegers().valuation(5) - Traceback (most recent call last): - ... - ValueError: The valuation Gauss valuation induced by 5-adic valuation does not approximate a unique extension of 5-adic valuation with respect to x^2 + 1 - - When ``R`` is an absolute or relative number field, or a subring thereof, - ``prime`` can also be specified by providing a valuation on the base ring - that has a unique extension:: - - sage: CyclotomicField(5).valuation(ZZ.valuation(5)) - 5-adic valuation - - When the extension is not unique, this does not work:: - - sage: GaussianIntegers().valuation(ZZ.valuation(5)) - Traceback (most recent call last): - ... - ValueError: The valuation Gauss valuation induced by 5-adic valuation does not approximate a unique extension of 5-adic valuation with respect to x^2 + 1 - - For a number field which is of the form `K[x]/(G)`, you can specify a - valuation by providing a discrete pseudo-valuation on `K[x]` which sends - `G` to `\infty`. This lets us specify which extension of the 5-adic - valuation we care about in the above example:: - - sage: R. = QQ[] - sage: v = GaussianIntegers().valuation(GaussValuation(R, QQ.valuation(5)).augmentation(x + 2, infinity)) - sage: w = GaussianIntegers().valuation(GaussValuation(R, QQ.valuation(5)).augmentation(x + 1/2, infinity)) - sage: v == w - False - - Note that you get the same valuation, even if you write down the - pseudo-valuation differently:: - - sage: ww = GaussianIntegers().valuation(GaussValuation(R, QQ.valuation(5)).augmentation(x + 3, infinity)) - sage: w is ww - True - - The valuation ``prime`` does not need to send the defining polynomial `G` - to `\infty`. It is sufficient if it singles out one of the valuations on - the number field. This is important if the prime only factors over the - completion, i.e., if it is not possible to write down one of the factors - within the number field:: - - sage: v = GaussValuation(R, QQ.valuation(5)).augmentation(x + 3, 1) - sage: GaussianIntegers().fraction_field().valuation(v) - [ 5-adic valuation, v(x + 3) = 1 ]-adic valuation - - Finally, ``prime`` can also be a fractional ideal of a number field if it - singles out an extension of a `p`-adic valuation of the base field:: - - sage: R = GaussianIntegers() - sage: I = R.fraction_field().gen() - sage: R.valuation(R.fractional_ideal(I + 1)) - 2-adic valuation + sage: TODO complete the sentence below It can sometimes be beneficial to define a number field extension as a quotient of a polynomial ring (since number field extensions always compute @@ -168,11 +96,14 @@ class PadicValuationFactory(UniqueFactory): sage: valuations.pAdicValuation(L, 2) 2-adic valuation - Valuations can also be defined on orders in number fields:: + SEEALSO:: - sage: O = K.order(2*a) - sage: valuations.pAdicValuation(O, 2) - 2-adic valuation + For more examples, see + :meth:`sage.rings.number_field.number_field.NumberField.valuation`, + :meth:`sage.rings.order.Order.valuation`, + :meth:`sage.rings.padics.padic_generic.pAdicGeneric.valuation`, + :meth:`sage.rings.rational_field.RationalField.valuation`, + :meth:`sage.rings.integer_ring.IntegerRing.valuation`. """ def create_key_and_extra_args(self, R, prime=None, approximants=None): diff --git a/src/sage/rings/rational_field.py b/src/sage/rings/rational_field.py index 38d63f2684c..24af3633178 100644 --- a/src/sage/rings/rational_field.py +++ b/src/sage/rings/rational_field.py @@ -1302,6 +1302,11 @@ def valuation(self, p): sage: v(1/3) -1 + SEEALSO:: + + :meth:`sage.rings.number_field.number_field.NumberField.valuation`, + :meth:`sage.rings.integer_ring.IntegerRing.valuation` + """ from sage.rings.padics.padic_valuation import pAdicValuation return pAdicValuation(self, p) From 48d7231e1c5874ef3b17808c3eaf774b9e082ecb Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Sat, 22 Jul 2017 21:44:13 +0000 Subject: [PATCH 307/740] Restore DiscreteValueGroup it used to be in the global namespace so I just put it back there. --- src/sage/rings/valuation/all.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/rings/valuation/all.py b/src/sage/rings/valuation/all.py index 2ca3d3ae411..3f90ef9daa0 100644 --- a/src/sage/rings/valuation/all.py +++ b/src/sage/rings/valuation/all.py @@ -2,3 +2,4 @@ lazy_import('sage.rings.valuation.gauss_valuation', 'GaussValuation') lazy_import('sage.rings.valuation', 'valuations_catalog', 'valuations') +lazy_import('sage.rings.valuation.value_group', 'DiscreteValueGroup') From d893c55a222ef01c0c281c7fc9032118003e8dcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 16:54:03 -0500 Subject: [PATCH 308/740] remove completed TODO --- src/sage/rings/padics/padic_valuation.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 9ec6e40b871..46ebd4c89b1 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -83,8 +83,6 @@ class PadicValuationFactory(UniqueFactory): ... ValueError: prime must be specified for this ring - sage: TODO complete the sentence below - It can sometimes be beneficial to define a number field extension as a quotient of a polynomial ring (since number field extensions always compute an absolute polynomial defining the extension which can be very costly):: From e318075c0420a9225010a4b57c66e68d9c052567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 17:04:26 -0500 Subject: [PATCH 309/740] value_group has moved --- src/doc/en/reference/padics/index.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/src/doc/en/reference/padics/index.rst b/src/doc/en/reference/padics/index.rst index 84c04b77952..ddc9211fb00 100644 --- a/src/doc/en/reference/padics/index.rst +++ b/src/doc/en/reference/padics/index.rst @@ -44,7 +44,6 @@ p-Adics sage/rings/padics/misc sage/rings/padics/common_conversion - sage/rings/padics/discrete_value_group sage/rings/padics/morphism .. include:: ../footer.txt From a2d10913245fc598684fe178f3eaf74bac5e5b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 17:13:18 -0500 Subject: [PATCH 310/740] fix toctree formatting --- src/doc/en/reference/valuations/index.rst | 26 +++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/doc/en/reference/valuations/index.rst b/src/doc/en/reference/valuations/index.rst index 5ad09944c33..d3a4781b8e5 100644 --- a/src/doc/en/reference/valuations/index.rst +++ b/src/doc/en/reference/valuations/index.rst @@ -181,22 +181,22 @@ The theory was originally described in [Mac1936]_ and [Mac1936']_. A summary and .. toctree:: :maxdepth: 2 - sage/rings/valuation/value_group.py - sage/rings/valuation/valuation.py - sage/rings/valuation/valuation_space.py + sage/rings/valuation/value_group + sage/rings/valuation/valuation + sage/rings/valuation/valuation_space - sage/rings/valuation/trivial_valuation.py - sage/rings/valuation/gauss_valuation.py + sage/rings/valuation/trivial_valuation + sage/rings/valuation/gauss_valuation - sage/rings/valuation/developing_valuation.py - sage/rings/valuation/inductive_valuation.py - sage/rings/valuation/augmented_valuation.py - sage/rings/valuation/limit_valuation.py + sage/rings/valuation/developing_valuation + sage/rings/valuation/inductive_valuation + sage/rings/valuation/augmented_valuation + sage/rings/valuation/limit_valuation - sage/rings/valuation/mapped_valuation.py - sage/rings/valuation/scaled_valuation.py + sage/rings/valuation/mapped_valuation + sage/rings/valuation/scaled_valuation - sage/rings/function_field/function_field_valuation.py - sage/rings/padics/padic_valuation.py + sage/rings/function_field/function_field_valuation + sage/rings/padics/padic_valuation .. include:: ../footer.txt From 9b0280042e3e1266bce9c4dee0e92ec207d0205f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 17:15:32 -0500 Subject: [PATCH 311/740] fix docbuild --- src/sage/rings/padics/padic_valuation.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 46ebd4c89b1..68168938955 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -96,12 +96,12 @@ class PadicValuationFactory(UniqueFactory): SEEALSO:: - For more examples, see - :meth:`sage.rings.number_field.number_field.NumberField.valuation`, - :meth:`sage.rings.order.Order.valuation`, - :meth:`sage.rings.padics.padic_generic.pAdicGeneric.valuation`, - :meth:`sage.rings.rational_field.RationalField.valuation`, - :meth:`sage.rings.integer_ring.IntegerRing.valuation`. + For more examples, see + :meth:`sage.rings.number_field.number_field.NumberField.valuation`, + :meth:`sage.rings.order.Order.valuation`, + :meth:`sage.rings.padics.padic_generic.pAdicGeneric.valuation`, + :meth:`sage.rings.rational_field.RationalField.valuation`, + :meth:`sage.rings.integer_ring.IntegerRing.valuation`. """ def create_key_and_extra_args(self, R, prime=None, approximants=None): From 735cd8d901ea93cf7e7d2613e16f9f0514967ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 17:22:51 -0500 Subject: [PATCH 312/740] fix typo --- src/doc/en/reference/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst index d9fa1dacc35..dcaa03c22ad 100644 --- a/src/doc/en/reference/index.rst +++ b/src/doc/en/reference/index.rst @@ -105,7 +105,7 @@ Number Fields and Function Fields * :doc:`Number Fields ` * :doc:`Function Fields ` -* :doc:`Discrete Valuations ` +* :doc:`Discrete Valuations ` Number Theory ------------- From 04dd3954fc4969dab2049e689453ddb606a5419e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 17:24:54 -0500 Subject: [PATCH 313/740] fix reference link --- src/doc/en/reference/valuations/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/reference/valuations/index.rst b/src/doc/en/reference/valuations/index.rst index d3a4781b8e5..0956f379608 100644 --- a/src/doc/en/reference/valuations/index.rst +++ b/src/doc/en/reference/valuations/index.rst @@ -73,7 +73,7 @@ Low-Level Interface Mac Lane valuations ------------------- Internally, all the above is backed by the algorithms described in -[MacLane1936]_ and [MacLane1936']_. Let us consider the extensions of +[Mac1936]_ and [Mac1936']_. Let us consider the extensions of ``K.valuation(x - 4)`` to the field `L` above to outline how this works internally. From 75ccb0e0a094f62512b51e451ba620a9a093e4c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 17:32:27 -0500 Subject: [PATCH 314/740] fix typo --- src/doc/en/reference/valuations/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/reference/valuations/index.rst b/src/doc/en/reference/valuations/index.rst index 0956f379608..f21e4156d39 100644 --- a/src/doc/en/reference/valuations/index.rst +++ b/src/doc/en/reference/valuations/index.rst @@ -1,4 +1,4 @@ -Discrete Valuations and Discrene Pseudo-Valuations +Discrete Valuations and Discrete Pseudo-Valuations ================================================== High-Level Interface From 36f33e6cbd3d8535f8c1da12af0e9f549df8f602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 17:32:35 -0500 Subject: [PATCH 315/740] remove commented out code --- src/sage/rings/valuation/augmented_valuation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index 0bcb400072c..68183c4f68d 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -358,7 +358,6 @@ def equivalence_unit(self, s, reciprocal=False): residue = self.reduce(ret*self._base_valuation.element_with_valuation(-s), check=False) assert residue.is_constant() ret *= self.lift(~(residue[0])) - #ret = self.equivalence_reciprocal(self.equivalence_unit(-s)) else: ret = self._base_valuation.element_with_valuation(s) From 13f7d6ca93fc69c1c70c14bdefe366af30ed465f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 17:33:53 -0500 Subject: [PATCH 316/740] fix grammar --- src/doc/en/reference/valuations/index.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/doc/en/reference/valuations/index.rst b/src/doc/en/reference/valuations/index.rst index f21e4156d39..d21750aa1ac 100644 --- a/src/doc/en/reference/valuations/index.rst +++ b/src/doc/en/reference/valuations/index.rst @@ -46,9 +46,8 @@ Similarly, valuations can be defined on function fields:: sage: v(1/x) 1 -On extensions of function fields, valuations can be specified explicitly by -providing a prime on the underlying rational function field when the extension -is unique:: +On extensions of function fields, valuations can be created by providing a +prime on the underlying rational function field when the extension is unique:: sage: K. = FunctionField(QQ) sage: R. = K[] From 39b0792aa9062aa9c164d7e949d777f588a70e7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 17:37:19 -0500 Subject: [PATCH 317/740] Fix MacLane references an apostrophe is not supported by sphinx --- src/doc/en/reference/references/index.rst | 4 ++-- src/doc/en/reference/valuations/index.rst | 4 ++-- src/sage/rings/function_field/function_field_valuation.py | 2 +- src/sage/rings/padics/padic_valuation.py | 2 +- src/sage/rings/valuation/inductive_valuation.py | 2 +- src/sage/rings/valuation/limit_valuation.py | 2 +- src/sage/rings/valuation/valuation.py | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index a7759a07898..2e2c838687a 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1233,11 +1233,11 @@ REFERENCES: Atoms, Journal of Algebraic Combinatorics, Vol. 29, (2009), No. 3, p.295-313. :arXiv:`0707.4267` -.. [Mac1936] Saunders MacLane, *A construction for prime ideals as absolute +.. [Mac1936I] Saunders MacLane, *A construction for prime ideals as absolute values of an algebraic field*. Duke Mathematical Journal, 2(3) (1936), 492-510. -.. [Mac1936'] Saunders MacLane, *A construction for absolute values in +.. [Mac1936II] Saunders MacLane, *A construction for absolute values in polynomial rings*. Transactions of the American Mathematical Society, 40(3)(1936), 363-395. diff --git a/src/doc/en/reference/valuations/index.rst b/src/doc/en/reference/valuations/index.rst index d21750aa1ac..b49e882cbf9 100644 --- a/src/doc/en/reference/valuations/index.rst +++ b/src/doc/en/reference/valuations/index.rst @@ -72,7 +72,7 @@ Low-Level Interface Mac Lane valuations ------------------- Internally, all the above is backed by the algorithms described in -[Mac1936]_ and [Mac1936']_. Let us consider the extensions of +[Mac1936I]_ and [Mac1936II]_. Let us consider the extensions of ``K.valuation(x - 4)`` to the field `L` above to outline how this works internally. @@ -175,7 +175,7 @@ Note that in the limit they are factors of `f`. References ========== -The theory was originally described in [Mac1936]_ and [Mac1936']_. A summary and some algorithmic details can also be found in Chapter 4 of [Rü2014]_. +The theory was originally described in [Mac1936I]_ and [Mac1936II]_. A summary and some algorithmic details can also be found in Chapter 4 of [Rü2014]_. .. toctree:: :maxdepth: 2 diff --git a/src/sage/rings/function_field/function_field_valuation.py b/src/sage/rings/function_field/function_field_valuation.py index ab54f2be4c3..15e75255c67 100644 --- a/src/sage/rings/function_field/function_field_valuation.py +++ b/src/sage/rings/function_field/function_field_valuation.py @@ -131,7 +131,7 @@ An overview of some computational tools relating to valuations on function fields can be found in Section 4.6 of [Rüt2014]_. Most of this was originally -developed for number fields in [Mac1936]_ and [Mac1936']_. +developed for number fields in [Mac1936I]_ and [Mac1936II]_. """ #***************************************************************************** diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 68168938955..b9a24952ec2 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -24,7 +24,7 @@ REFERENCES: -The theory used here was originally developed in [Mac1936]_ and [Mac1936']_. An +The theory used here was originally developed in [Mac1936I]_ and [Mac1936II]_. An overview can also be found in Chapter 4 of [Rüt2014]_. """ diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index 8b49b1c0921..bf2073fcddb 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -25,7 +25,7 @@ REFERENCES: -Inductive valuations are originally discussed in [Mac1936]_ and [Mac1936']. An +Inductive valuations are originally discussed in [Mac1936I]_ and [Mac1936II]. An introduction is also given in Chapter 4 of [Rüt2014]_. """ diff --git a/src/sage/rings/valuation/limit_valuation.py b/src/sage/rings/valuation/limit_valuation.py index 5b77bcb83dd..b203fa7d4ea 100644 --- a/src/sage/rings/valuation/limit_valuation.py +++ b/src/sage/rings/valuation/limit_valuation.py @@ -68,7 +68,7 @@ REFERENCES: -Limits of inductive valuations are discussed in [Mac1936]_ and [Mac1936']_. An +Limits of inductive valuations are discussed in [Mac1936I]_ and [Mac1936II]_. An overview can also be found in Section 4.6 of [Rüt2014]_. """ diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 029cf2d4ea8..875c8457f5d 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -996,7 +996,7 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No REFERENCES: - The underlying algorithm is described in [Mac1936']_and thoroughly + The underlying algorithm is described in [Mac1936II]_and thoroughly analyzed in [GMN2008]_. """ From 4630b93916bf0c5277c63eef537019e6ce3c28fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 17:38:19 -0500 Subject: [PATCH 318/740] fix LaTeX --- src/doc/en/reference/valuations/index.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/en/reference/valuations/index.rst b/src/doc/en/reference/valuations/index.rst index b49e882cbf9..8fa3cf3446c 100644 --- a/src/doc/en/reference/valuations/index.rst +++ b/src/doc/en/reference/valuations/index.rst @@ -76,9 +76,9 @@ Internally, all the above is backed by the algorithms described in ``K.valuation(x - 4)`` to the field `L` above to outline how this works internally. -First, the valuation on `K` is induced by a valuation on `\Q[x]`. To construct -this valuation, we start from the trivial valuation on `\Q` and consider its -induced Gauss valuation on `\Q[x]`, i.e., the valuation that assigns to a +First, the valuation on `K` is induced by a valuation on `\QQ[x]`. To construct +this valuation, we start from the trivial valuation on `\QQ` and consider its +induced Gauss valuation on `\QQ[x]`, i.e., the valuation that assigns to a polynomial the minimum of the coefficient valuations:: sage: R. = QQ[] From cc9607fbf5d5e836a20f97117991886d3d27aab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 17:39:47 -0500 Subject: [PATCH 319/740] fix docbuild --- src/sage/rings/valuation/limit_valuation.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/valuation/limit_valuation.py b/src/sage/rings/valuation/limit_valuation.py index b203fa7d4ea..5bfa78132de 100644 --- a/src/sage/rings/valuation/limit_valuation.py +++ b/src/sage/rings/valuation/limit_valuation.py @@ -55,16 +55,16 @@ valuation without using a limit. This is done to improve performance as many computations already can be done correctly with an approximation:: - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) - sage: v = K.valuation(1/x) - sage: w = v.extension(L); w - Valuation at the infinite place - sage: w._base_valuation._base_valuation._improve_approximation() - sage: w._base_valuation._base_valuation._approximation - [ Gauss valuation induced by Valuation at the infinite place, v(y) = 1/2, v(y^2 - 1/x) = +Infinity ] + sage: v = K.valuation(1/x) + sage: w = v.extension(L); w + Valuation at the infinite place + sage: w._base_valuation._base_valuation._improve_approximation() + sage: w._base_valuation._base_valuation._approximation + [ Gauss valuation induced by Valuation at the infinite place, v(y) = 1/2, v(y^2 - 1/x) = +Infinity ] REFERENCES: From 2b767b709f4cf5e11e4d1bdf946e726a0e5865df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 17:42:58 -0500 Subject: [PATCH 320/740] fix reference key --- src/doc/en/reference/valuations/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/reference/valuations/index.rst b/src/doc/en/reference/valuations/index.rst index 8fa3cf3446c..cccbb580310 100644 --- a/src/doc/en/reference/valuations/index.rst +++ b/src/doc/en/reference/valuations/index.rst @@ -175,7 +175,7 @@ Note that in the limit they are factors of `f`. References ========== -The theory was originally described in [Mac1936I]_ and [Mac1936II]_. A summary and some algorithmic details can also be found in Chapter 4 of [Rü2014]_. +The theory was originally described in [Mac1936I]_ and [Mac1936II]_. A summary and some algorithmic details can also be found in Chapter 4 of [Rüt2014]_. .. toctree:: :maxdepth: 2 From cdf89aef1bf3a1b6f7c98a650ce8669dd6c1becb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 18:08:50 -0500 Subject: [PATCH 321/740] renamed reference manual section discrete valuations are more general of course but probably not sufficiently basic to be put into the general section on rings and fields. --- src/doc/en/reference/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst index dcaa03c22ad..6b1ded414c9 100644 --- a/src/doc/en/reference/index.rst +++ b/src/doc/en/reference/index.rst @@ -100,8 +100,8 @@ Geometry and Topology * :doc:`Parametrized Surfaces ` * :doc:`Knot Theory ` -Number Fields and Function Fields ---------------------------------- +Number Fields, Function Fields, and Valuations +---------------------------------------------- * :doc:`Number Fields ` * :doc:`Function Fields ` From c011093d3b22d3826a51c28f48bf81cef79063fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 18:21:05 -0500 Subject: [PATCH 322/740] reformat headers for reference manual --- src/doc/en/reference/valuations/index.rst | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/doc/en/reference/valuations/index.rst b/src/doc/en/reference/valuations/index.rst index cccbb580310..db4b7a930ca 100644 --- a/src/doc/en/reference/valuations/index.rst +++ b/src/doc/en/reference/valuations/index.rst @@ -2,12 +2,12 @@ Discrete Valuations and Discrete Pseudo-Valuations ================================================== High-Level Interface -==================== +-------------------- Valuations can be defined conveniently on some Sage rings such as p-adic rings and function fields. p-adic valuations ------------------ +~~~~~~~~~~~~~~~~~ Valuations on number fields can be easily specified if they uniquely extend the valuation of a rational prime:: @@ -34,7 +34,7 @@ extending a valuation from a smaller ring:: (0, 1) Valuations on Function Fields ------------------------------ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Similarly, valuations can be defined on function fields:: sage: K. = FunctionField(QQ) @@ -67,10 +67,10 @@ Valuations can also be extended from smaller function fields:: [ (x - 4)-adic valuation, v(y + 2) = 1 ]-adic valuation] Low-Level Interface -=================== +------------------- Mac Lane valuations -------------------- +~~~~~~~~~~~~~~~~~~~ Internally, all the above is backed by the algorithms described in [Mac1936I]_ and [Mac1936II]_. Let us consider the extensions of ``K.valuation(x - 4)`` to the field `L` above to outline how this works @@ -107,7 +107,7 @@ valuation induced by it and augment it to approximate an extension to `L`:: [ (x - 4)-adic valuation, v(y - 2) = 1 ]-adic valuation Limit valuations ----------------- +~~~~~~~~~~~~~~~~ In the previous example the final valuation ``ww`` is not merely given by evaluating ``w`` on the ring `K[y]`:: @@ -133,7 +133,7 @@ The terms of this infinite sequence are computed on demand:: [ Gauss valuation induced by (x - 4)-adic valuation, v(y - 1/4*x - 1) = 2 ] Non-classical valuations ------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~ Using the low-level interface we are not limited to classical valuations on function fields that correspond to points on the corresponding projective curves. Instead we can start with a non-trivial valuation on the field of @@ -148,7 +148,7 @@ constants:: Fraction Field of Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) Mac Lane Approximants -===================== +--------------------- The main tool underlying this package is an algorithm by Mac Lane to compute, starting from a Gauss valuation on a polynomial ring and a monic squarefree polynomial G, approximations to the limit valuation which send G to infinity:: @@ -173,10 +173,13 @@ pushed to arbitrary precision:: Note that in the limit they are factors of `f`. References -========== +---------- The theory was originally described in [Mac1936I]_ and [Mac1936II]_. A summary and some algorithmic details can also be found in Chapter 4 of [Rüt2014]_. +More Details +============ + .. toctree:: :maxdepth: 2 From 8022ed196995b2e7b1bc93c9dcd5f0d93fd3bfd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 22:15:52 -0500 Subject: [PATCH 323/740] fix docbuild --- src/doc/en/reference/valuations/index.rst | 4 +--- src/sage/rings/function_field/function_field.py | 4 ++-- src/sage/rings/integer_ring.pyx | 4 ++-- src/sage/rings/number_field/number_field.py | 4 ++-- src/sage/rings/number_field/order.py | 6 +++--- src/sage/rings/padics/padic_generic.py | 2 +- src/sage/rings/padics/padic_valuation.py | 10 +++++----- src/sage/rings/rational_field.py | 4 ++-- src/sage/rings/valuation/augmented_valuation.py | 16 ++++++++-------- src/sage/rings/valuation/developing_valuation.py | 6 +++--- src/sage/rings/valuation/gauss_valuation.py | 10 +++++----- src/sage/rings/valuation/inductive_valuation.py | 16 ++++++++-------- src/sage/rings/valuation/limit_valuation.py | 6 +++--- src/sage/rings/valuation/mapped_valuation.py | 4 ++-- src/sage/rings/valuation/scaled_valuation.py | 2 +- src/sage/rings/valuation/trivial_valuation.py | 2 +- src/sage/rings/valuation/valuation.py | 12 ++++++------ src/sage/rings/valuation/value_group.py | 2 +- 18 files changed, 56 insertions(+), 58 deletions(-) diff --git a/src/doc/en/reference/valuations/index.rst b/src/doc/en/reference/valuations/index.rst index db4b7a930ca..c4f8f5c9bf0 100644 --- a/src/doc/en/reference/valuations/index.rst +++ b/src/doc/en/reference/valuations/index.rst @@ -163,15 +163,13 @@ polynomial G, approximations to the limit valuation which send G to infinity:: From these approximants one can already see the residual degrees and ramification indices of the corresponding extensions. The approximants can be -pushed to arbitrary precision:: +pushed to arbitrary precision, corresponding to a factorization of ``f``:: sage: v.mac_lane_approximants(f, required_precision=10) [[ Gauss valuation induced by 2-adic valuation, v(x^2 + 193*x + 13/21) = 10 ], [ Gauss valuation induced by 2-adic valuation, v(x + 86) = 10 ], [ Gauss valuation induced by 2-adic valuation, v(x) = 1/2, v(x^2 + 36/11*x + 2/17) = 11 ]] -Note that in the limit they are factors of `f`. - References ---------- diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 1d70ac6aba1..8a837c0b03b 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -622,8 +622,8 @@ def valuation(self, prime): However, if such a valuation comes out of a base change of the ground field, this is not the case anymore. In the example below, the unique - extension of ``v`` to ``L`` still has valuation 1 on ``x^3 - t`` but it has - valuation ``1/3`` on its uniformizing element ``x - w``:: + extension of ``v`` to ``L`` still has valuation 1 on `x^3 - t` but it has + valuation ``1/3`` on its uniformizing element `x - w`:: sage: R. = K[] sage: L. = K.extension(w^3 - t) diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx index e8592f5aa03..813ba50e715 100644 --- a/src/sage/rings/integer_ring.pyx +++ b/src/sage/rings/integer_ring.pyx @@ -1516,7 +1516,7 @@ cdef class IntegerRing_class(PrincipalIdealDomain): def valuation(self, p): r""" - Return the discrete valuation with uniformizer `p`. + Return the discrete valuation with uniformizer ``p``. EXAMPLES:: @@ -1525,7 +1525,7 @@ cdef class IntegerRing_class(PrincipalIdealDomain): sage: v(3) 1 - SEEALSO:: + .. SEEALSO:: :meth:`sage.rings.number_field.order.Order.valuation`, :meth:`sage.rings.rational_field.RationalField.valuation` diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index d15fb913a0f..8c779d17167 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -6494,7 +6494,7 @@ def valuation(self, prime): sage: K.valuation(2) 2-adic valuation - ``prime`` can be an integer that is unramified in ``R``: + ``prime`` can be an integer that is unramified in ``R``:: sage: K.valuation(3) 3-adic valuation @@ -6554,7 +6554,7 @@ def valuation(self, prime): sage: K.valuation(K.fractional_ideal(a + 1)) 2-adic valuation - SEEALSO:: + .. SEEALSO:: :meth:`sage.rings.number_field.order.Order.valuation`, :meth:`sage.rings.padics.padic_generic.pAdicGeneric.valuation` diff --git a/src/sage/rings/number_field/order.py b/src/sage/rings/number_field/order.py index b2169f85941..5bcbe59f52e 100644 --- a/src/sage/rings/number_field/order.py +++ b/src/sage/rings/number_field/order.py @@ -993,7 +993,7 @@ def absolute_degree(self): def valuation(self, p): r""" - Return the `p`-adic valuation on this order. + Return the ``p``-adic valuation on this order. EXAMPLES: @@ -1035,7 +1035,7 @@ def valuation(self, p): If the fraction field is of the form `K[x]/(G)`, you can specify a valuation by providing a discrete pseudo-valuation on `K[x]` which - sends `G` to `\infty`:: + sends `G` to infinity:: sage: R. = QQ[] sage: v = GaussianIntegers().valuation(GaussValuation(R, QQ.valuation(5)).augmentation(x + 2, infinity)) @@ -1043,7 +1043,7 @@ def valuation(self, p): sage: v == w False - SEEALSO:: + .. SEEALSO:: :meth:`sage.rings.number_field.number_field.NumberField.valuation`, :meth:`sage.rings.padics.padic_generic.pAdicGeneric.valuation` diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index 0ebf942b92e..afa571a6c59 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -978,7 +978,7 @@ def valuation(self): sage: v.restriction(K) == K.valuation() True - SEEALSO:: + .. SEEALSO:: :meth:`sage.rings.number_field.number_field.NumberField.valuation`, :meth:`sage.rings.number_field.order.Order.valuation` diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index b9a24952ec2..ff2796ec54c 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -52,7 +52,7 @@ class PadicValuationFactory(UniqueFactory): INPUT: - ``R`` -- a subring of a number field or a subring of a local field in - characteristic zero. + characteristic zero - ``prime`` -- a prime that does not split, a discrete (pseudo-)valuation, a fractional ideal, or ``None`` (default: ``None``) @@ -94,7 +94,7 @@ class PadicValuationFactory(UniqueFactory): sage: valuations.pAdicValuation(L, 2) 2-adic valuation - SEEALSO:: + .. SEEALSO:: For more examples, see :meth:`sage.rings.number_field.number_field.NumberField.valuation`, @@ -208,7 +208,7 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime, .. NOTE:: ``prime``, the original parameter that was passed to - :meth:`create_key_and_extra_args``, is only used to provide more + :meth:`create_key_and_extra_args`, is only used to provide more meaningful error messages EXAMPLES:: @@ -273,7 +273,7 @@ def create_key_and_extra_args_for_number_field_from_ideal(self, R, I, prime): .. NOTE:: ``prime``, the original parameter that was passed to - :meth:`create_key_and_extra_args``, is only used to provide more + :meth:`create_key_and_extra_args`, is only used to provide more meaningful error messages EXAMPLES:: @@ -1141,7 +1141,7 @@ def simplify(self, x, error=None, force=False, size_heuristic_bound=32): - ``force`` -- ignored - - ``size_heuristic_bound` -- when ``force`` is not set, the expected + - ``size_heuristic_bound`` -- when ``force`` is not set, the expected factor by which the ``x`` need to shrink to perform an actual simplification (default: 32) diff --git a/src/sage/rings/rational_field.py b/src/sage/rings/rational_field.py index 24af3633178..f596eaf4c14 100644 --- a/src/sage/rings/rational_field.py +++ b/src/sage/rings/rational_field.py @@ -1293,7 +1293,7 @@ def _factor_univariate_polynomial(self, f): def valuation(self, p): r""" - Return the discrete valuation with uniformizer `p`. + Return the discrete valuation with uniformizer ``p``. EXAMPLES:: @@ -1302,7 +1302,7 @@ def valuation(self, p): sage: v(1/3) -1 - SEEALSO:: + .. SEEALSO:: :meth:`sage.rings.number_field.number_field.NumberField.valuation`, :meth:`sage.rings.integer_ring.IntegerRing.valuation` diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index 68183c4f68d..b5e959c05e2 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -139,8 +139,8 @@ REFERENCES: -Augmentations are described originally in [ML1936]_ and [ML1936']_. An overview -can also be found in Chapter 4 of [Rüt2014]_. +Augmentations are described originally in [Mac1936I]_ and [Mac1936II]_. An +overview can also be found in Chapter 4 of [Rüt2014]_. """ #***************************************************************************** @@ -1203,7 +1203,7 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations ALGORITHM: - We follow the algorithm given in the proof of Theorem 12.1 of [ML1936]: + We follow the algorithm given in the proof of Theorem 12.1 of [Mac1936]: If ``f`` has positive valuation, the reduction is simply zero. Otherwise, let `f=\sum f_i\phi^i` be the expansion of `f`, as computed by :meth:`coefficients`. Since the valuation is zero, the exponents `i` @@ -1211,7 +1211,7 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations valuation in the value group of this valuation. Hence, there is an :meth:`equivalence_unit` `Q` with the same valuation as `\phi^\tau`. Let `Q'` be its :meth:`reciprocal_inverse`. - Now, rewrite each term `f_i\phi^{i\tau}=(f_iQ^i)(\phi^\tauQ^{-1})^i`; + Now, rewrite each term `f_i\phi^{i\tau}=(f_iQ^i)(\phi^\tau Q^{-1})^i`; it turns out that the second factor in this expression is a lift of the generator of the :meth:`residue_field`. The reduction of the first factor can be computed recursively. @@ -1455,7 +1455,7 @@ def lift_to_key(self, F, check=True): ALGORITHM: - We follow the algorithm described in Theorem 13.1 [ML1936] which, after + We follow the algorithm described in Theorem 13.1 [Mac1936] which, after a :meth:`lift` of ``F``, essentially shifts the valuations of all terms in the `\phi`-adic expansion up and then kills the leading coefficient. @@ -1662,7 +1662,7 @@ def valuations(self, f, coefficients=None, call_error=False): used to speed up the computation when the expansion of ``f`` is already known from a previous computation. - - ``call_error` -- whether or not to speed up the computation by + - ``call_error`` -- whether or not to speed up the computation by assuming that the result is only used to compute the valuation of ``f`` (default: ``False``) @@ -1729,7 +1729,7 @@ def simplify(self, f, error=None, force=False, effective_degree=None, size_heuri ``effective_degree`` in the :meth:`phi`-adic development can be safely dropped (default: ``None``) - - ``size_heuristic_bound` -- when ``force`` is not set, the expected + - ``size_heuristic_bound`` -- when ``force`` is not set, the expected factor by which the coefficients need to shrink to perform an actual simplification (default: 32) @@ -1963,7 +1963,7 @@ def valuations(self, f, coefficients=None, call_error=False): used to speed up the computation when the expansion of ``f`` is already known from a previous computation. - - ``call_error` -- whether or not to speed up the computation by + - ``call_error`` -- whether or not to speed up the computation by assuming that the result is only used to compute the valuation of ``f`` (default: ``False``) diff --git a/src/sage/rings/valuation/developing_valuation.py b/src/sage/rings/valuation/developing_valuation.py index 81958b7d67b..ebc6eaf6d33 100644 --- a/src/sage/rings/valuation/developing_valuation.py +++ b/src/sage/rings/valuation/developing_valuation.py @@ -111,7 +111,7 @@ def effective_degree(self, f, valuations=None): The effective degree of `f` is the largest `i` such that the valuation of `f` and the valuation of `f_i\phi^i` in the development `f=\sum_j - f_j\phi^j` coincide (see [ML1936'] p.497.) + f_j\phi^j` coincide (see [Mac1936II] p.497.) INPUT: @@ -177,7 +177,7 @@ def coefficients(self, f): OUTPUT: - An iterator `[f_0,f_1,\dots]` of polynomials in the domain of this + An iterator `f_0, f_1, \dots, f_n` of polynomials in the domain of this valuation such that `f=\sum_i f_i\phi^i` EXAMPLES:: @@ -228,7 +228,7 @@ def newton_polygon(self, f, valuations=None): r""" Return the newton polygon of the `\phi`-adic development of ``f``. - INPUT:: + INPUT: - ``f`` -- a polynomial in the domain of this valuation diff --git a/src/sage/rings/valuation/gauss_valuation.py b/src/sage/rings/valuation/gauss_valuation.py index fc22812f937..7257d0d012b 100644 --- a/src/sage/rings/valuation/gauss_valuation.py +++ b/src/sage/rings/valuation/gauss_valuation.py @@ -241,7 +241,7 @@ def valuations(self, f, coefficients=None, call_error=False): used to speed up the computation when the expansion of ``f`` is already known from a previous computation. - - ``call_error` -- whether or not to speed up the computation by + - ``call_error`` -- whether or not to speed up the computation by assuming that the result is only used to compute the valuation of ``f`` (default: ``False``) @@ -346,7 +346,7 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations .. SEEALSO:: - :meth: `lift` + :meth:`lift` """ f = self.domain().coerce(f) @@ -365,7 +365,7 @@ def lift(self, F): """ Return a lift of ``F``. - INPUT:: + INPUT: - ``F`` -- a polynomial over the :meth:`residue_ring` of this valuation @@ -445,7 +445,7 @@ def equivalence_unit(self, s, reciprocal=False): - ``reciprocal`` -- a boolean (default: ``False``); whether or not to return the equivalence unit as the :meth:`equivalence_reciprocal` of - the equivalence unit of valuation ``-s``. + the equivalence unit of valuation ``-s`` EXAMPLES:: @@ -746,7 +746,7 @@ def simplify(self, f, error=None, force=False, size_heuristic_bound=32, effectiv - ``effective_degree`` -- when set, assume that coefficients beyond ``effective_degree`` can be safely dropped (default: ``None``) - - ``size_heuristic_bound` -- when ``force`` is not set, the expected + - ``size_heuristic_bound`` -- when ``force`` is not set, the expected factor by which the coefficients need to shrink to perform an actual simplification (default: 32) diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index bf2073fcddb..bb94a4ef035 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -61,7 +61,7 @@ class InductiveValuation(DevelopingValuation): def is_equivalence_unit(self, f, valuations=None): r""" Return whether ``f`` is an equivalence unit, i.e., an element of - :meth:`effective_degree` zero (see [ML1936'] p.497.) + :meth:`effective_degree` zero (see [Mac1936II] p.497.) INPUT: @@ -91,7 +91,7 @@ def equivalence_reciprocal(self, f, coefficients=None, valuations=None, check=Tr Return an equivalence reciprocal of ``f``. An equivalence reciprocal of `f` is a polynomial `h` such that `f\cdot - h` is equivalent to 1 modulo this valuation (see [ML1936'] p.497.) + h` is equivalent to 1 modulo this valuation (see [Mac1936II] p.497.) INPUT: @@ -686,7 +686,7 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a - ``report_degree_bounds_and_caches`` -- whether or not to include internal state with the returned value (used by :meth:`mac_lane_approximants` to speed up sequential calls) - - ``coefficients`` -- the coefficients of ``G`` in the + - ``coefficients`` -- the coefficients of ``G`` in the :meth:`phi`-adic expansion if known (default: ``None``) - ``valauations`` -- the valuations of ``coefficients`` if known @@ -921,7 +921,7 @@ def is_minimal(self, f, assume_equivalence_irreducible=False): ALGORITHM: - Based on Theorem 9.4 of [ML1936']. + Based on Theorem 9.4 of [Mac1936II]. EXAMPLES:: @@ -994,7 +994,7 @@ def is_minimal(self, f, assume_equivalence_irreducible=False): else: tau = self.value_group().index(self._base_valuation.value_group()) - # see Theorem 9.4 of [ML1936'] + # see Theorem 9.4 of [Mac1936II] return list(self.valuations(f))[-1] == self(f) and \ list(self.coefficients(f))[-1].is_constant() and \ list(self.valuations(f))[0] == self(f) and \ @@ -1128,7 +1128,7 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi ALGORITHM: - We use the algorithm described in Theorem 4.4 of [ML1936']. After + We use the algorithm described in Theorem 4.4 of [Mac1936II]. After removing all factors `\phi` from a polynomial `f`, there is an equivalence unit `R` such that `Rf` has valuation zero. Now `Rf` can be factored as `\prod_i \alpha_i` over the :meth:`residue_field`. Lifting @@ -1172,7 +1172,7 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi sage: F.unit() 1 + O(2^10) - Examples over an iterated unramified extension: + Examples over an iterated unramified extension:: sage: v = v.augmentation(x^2 + x + u, 1) sage: v = v.augmentation((x^2 + x + u)^2 + 2*x*(x^2 + x + u) + 4*x, 3) @@ -1279,7 +1279,7 @@ def minimal_representative(self, f): ALGORITHM: - We use the algorithm described in the proof of Lemma 4.1 of [ML1936']. + We use the algorithm described in the proof of Lemma 4.1 of [Mac1936II]. In the expansion `f=\sum_i f_i\phi^i` take `e=f_i` for the largest `i` with `f_i\phi^i` minimal (see :meth:`effective_degree`). Let `h` be the :meth:`equivalence_reciprocal` of `e` and take `a` given diff --git a/src/sage/rings/valuation/limit_valuation.py b/src/sage/rings/valuation/limit_valuation.py index 5bfa78132de..b2874549960 100644 --- a/src/sage/rings/valuation/limit_valuation.py +++ b/src/sage/rings/valuation/limit_valuation.py @@ -423,7 +423,7 @@ def lift(self, F): Return a lift of ``F`` from the :meth:`residue_ring` to the :meth:`domain` of this valuatiion. - EXAMPLES;; + EXAMPLES:: sage: K. = FunctionField(QQ) sage: R. = K[] @@ -563,12 +563,12 @@ def _improve_approximation_for_call(self, f): `v(a_0) < v(a_i\phi^i)` for all `i\ne 0`. If we denote the limit valuation as `w`, then `v(a_i\phi^i)=w(a_i\phi^i)` since the valuation of key polynomials does not change during augmentations - (Theorem 6.4 in [ML1936'].) By the strict triangle inequality, + (Theorem 6.4 in [Mac1936II].) By the strict triangle inequality, `w(g)=v(g)`. Note that any `g` which is coprime to `G` is an equivalence-unit after finitely many steps of the Mac Lane algorithm. Indeed, otherwise the valuation of `g` would be infinite (follows from - Theorem 5.1 in [ML1936']) since the valuation of the key + Theorem 5.1 in [Mac1936II]) since the valuation of the key polynomials increases. When `f` is not coprime to `G`, consider `s=gcd(f,G)` and write `G=st`. Since `G` is squarefree, either `s` or `t` have finite diff --git a/src/sage/rings/valuation/mapped_valuation.py b/src/sage/rings/valuation/mapped_valuation.py index 253f97cd386..fd7d0d75722 100644 --- a/src/sage/rings/valuation/mapped_valuation.py +++ b/src/sage/rings/valuation/mapped_valuation.py @@ -205,7 +205,7 @@ def reduce(self, f): def lift(self, F): r""" - Lift ``F`` from the :meth;`residue_field` of this valuation into its + Lift ``F`` from the :meth:`residue_field` of this valuation into its domain. EXAMPLES:: @@ -321,7 +321,7 @@ class FiniteExtensionFromInfiniteValuation(MappedValuation_base, DiscreteValuati discrete valuations on `L`) - ``base_valuation`` -- an infinite valuation on `K[x]` which takes `G` to - infinity. + infinity EXAMPLES:: diff --git a/src/sage/rings/valuation/scaled_valuation.py b/src/sage/rings/valuation/scaled_valuation.py index a8b1bf06525..57bf7c39f71 100644 --- a/src/sage/rings/valuation/scaled_valuation.py +++ b/src/sage/rings/valuation/scaled_valuation.py @@ -2,7 +2,7 @@ r""" Valuations which are scaled versions of another valuation -EXAMPLES: +EXAMPLES:: sage: 3*ZZ.valuation(3) 3 * 3-adic valuation diff --git a/src/sage/rings/valuation/trivial_valuation.py b/src/sage/rings/valuation/trivial_valuation.py index 5433eecc4ba..17be6b4ab40 100644 --- a/src/sage/rings/valuation/trivial_valuation.py +++ b/src/sage/rings/valuation/trivial_valuation.py @@ -151,7 +151,7 @@ def is_negative_pseudo_valuation(self): r""" Return whether this valuatios attains the value `-\infty`. - EXAMPLES: + EXAMPLES:: sage: v = valuations.TrivialPseudoValuation(QQ) sage: v.is_negative_pseudo_valuation() diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 875c8457f5d..9e6f57f3438 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -385,7 +385,7 @@ class NegativeInfiniteDiscretePseudoValuation(InfiniteDiscretePseudoValuation): and `-\infty`, i.e., whose domain contains an element of valuation `\infty` and its inverse. - EXAMPLES: + EXAMPLES:: sage: R. = QQ[] sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)).augmentation(x, infinity) @@ -460,7 +460,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru INPUT: - ``G`` -- a monic squarefree integral polynomial in a - univariate polynomial ring over the :meth:`domain` of this valuation. + univariate polynomial ring over the :meth:`domain` of this valuation - ``assume_squarefree`` -- a boolean (default: ``False``), whether to assume that ``G`` is squarefree. If ``True``, the squafreeness of @@ -834,9 +834,9 @@ def mac_lane_approximant(self, G, valuation, approximants = None): INPUT: - ``G`` -- a monic squarefree integral polynomial in a - univariate polynomial ring over the :meth:`domain` of this valuation. + univariate polynomial ring over the :meth:`domain` of this valuation - - ``valuation`` -- a valuation on the parent of ``G``. + - ``valuation`` -- a valuation on the parent of ``G`` - ``approximants`` -- the output of :meth:`mac_lane_approximants`. If not given, it is computed. @@ -947,8 +947,8 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No ALGORITHM: - We compute :meth:`mac_lane_approximants` with ``required_precision``. - The key polynomials approximate factors of ``G``. + We compute :meth:`mac_lane_approximants` with ``required_precision``. + The key polynomials approximate factors of ``G``. EXAMPLES:: diff --git a/src/sage/rings/valuation/value_group.py b/src/sage/rings/valuation/value_group.py index 54fd5830295..7efa7517d19 100644 --- a/src/sage/rings/valuation/value_group.py +++ b/src/sage/rings/valuation/value_group.py @@ -2,7 +2,7 @@ r""" Value groups of discrete valuations -This file defines additive sub(semi-)groups of `\Q` and related structures. +This file defines additive sub(semi-)groups of `\QQ` and related structures. AUTHORS: From 14e034e46f0834f42f0ddd84f74a8b2814dbe809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 22:51:06 -0500 Subject: [PATCH 324/740] fix value_group documentation --- src/sage/rings/valuation/value_group.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/valuation/value_group.py b/src/sage/rings/valuation/value_group.py index 7efa7517d19..ccfdcf74797 100644 --- a/src/sage/rings/valuation/value_group.py +++ b/src/sage/rings/valuation/value_group.py @@ -102,7 +102,7 @@ def _repr_(self): class DiscreteValueGroup(UniqueRepresentation, Parent): r""" - The value group of a discrete valuation, an additive subgroup of \QQ + The value group of a discrete valuation, an additive subgroup of `\QQ` generated by ``generator``. INPUT: @@ -218,7 +218,7 @@ def _repr_(self): def __add__(self, other): r""" - Return the subgroup of \QQ generated by this group and ``other``. + Return the subgroup of `\QQ` generated by this group and ``other``. INPUT: @@ -381,7 +381,7 @@ def is_trivial(self): class DiscreteValueSemigroup(UniqueRepresentation, Parent): r""" The value semigroup of a discrete valuation, an additive subsemigroup of - \QQ generated by ``generators``. + `\QQ` generated by ``generators``. INPUT: @@ -564,7 +564,7 @@ def _repr_(self): def __add__(self, other): r""" - Return the subsemigroup of \QQ generated by this semigroup and ``other``. + Return the subsemigroup of `\QQ` generated by this semigroup and ``other``. INPUT: From 3bff1b961ca0104b5cb70415b2100bd4c83d1ae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 22:53:00 -0500 Subject: [PATCH 325/740] fix documentation in valuation --- src/sage/rings/valuation/valuation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 9e6f57f3438..04af00099ea 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -460,7 +460,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru INPUT: - ``G`` -- a monic squarefree integral polynomial in a - univariate polynomial ring over the :meth:`domain` of this valuation + univariate polynomial ring over the domain of this valuation - ``assume_squarefree`` -- a boolean (default: ``False``), whether to assume that ``G`` is squarefree. If ``True``, the squafreeness of @@ -833,8 +833,8 @@ def mac_lane_approximant(self, G, valuation, approximants = None): INPUT: - - ``G`` -- a monic squarefree integral polynomial in a - univariate polynomial ring over the :meth:`domain` of this valuation + - ``G`` -- a monic squarefree integral polynomial in a univariate + polynomial ring over the domain of this valuation - ``valuation`` -- a valuation on the parent of ``G`` From ed13821cbad916ef2fbb65b50f6cc770609e5e6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 22:55:53 -0500 Subject: [PATCH 326/740] fix documentation of valuation_space --- src/sage/rings/valuation/valuation_space.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index c7b2883c487..35c09b828ba 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -168,7 +168,7 @@ def __contains__(self, x): r""" Return whether ``x`` is a valuation in this space. - EXAMPLES: + EXAMPLES:: sage: from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace sage: H = DiscretePseudoValuationSpace(QQ) @@ -188,7 +188,7 @@ def __call__(self, x): r""" Create an element in this space from ``x``. - EXAMPLES: + EXAMPLES:: sage: from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace sage: H = DiscretePseudoValuationSpace(QQ) @@ -347,8 +347,8 @@ def is_trivial(self): @abstract_method def uniformizer(self): r""" - Return an element in :meth:`domain` which has positive valuation - and generates the value group of this valuation. + Return an element in the domain which has positive valuation and + generates the value group of this valuation. EXAMPLES:: @@ -602,7 +602,7 @@ def change_domain(self, ring): r""" Return this valuation over ``ring``. - Unlike :meth:`extension` or meth:`reduction`, this might not be + Unlike :meth:`extension` or :meth:`reduction`, this might not be completely sane mathematically. It is essentially a conversion of this valuation into another space of valuations. From 7db0f057258c627ac6e648cebd67536569500cd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 22:59:43 -0500 Subject: [PATCH 327/740] fix documentation of gauss_valuation --- src/sage/rings/valuation/gauss_valuation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/valuation/gauss_valuation.py b/src/sage/rings/valuation/gauss_valuation.py index 7257d0d012b..d38410c7a66 100644 --- a/src/sage/rings/valuation/gauss_valuation.py +++ b/src/sage/rings/valuation/gauss_valuation.py @@ -237,7 +237,7 @@ def valuations(self, f, coefficients=None, call_error=False): - ``f`` -- a polynomial in the domain of this valuation - ``coefficients`` -- the coefficients of ``f`` as produced by - :meth:`coefficients` or ``None`` (default: ``None``); this can be + :meth:`developing_valuation.DevelopingValuation.coefficients` or ``None`` (default: ``None``); this can be used to speed up the computation when the expansion of ``f`` is already known from a previous computation. @@ -317,7 +317,7 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations result which can speed up the computation (default: not set) - ``coefficients`` -- the coefficients of ``f`` as produced by - :meth:`coefficients` or ``None`` (default: ``None``); ignored + :meth:`developing_valuation.DevelopingValuation.coefficients` or ``None`` (default: ``None``); ignored - ``valuations`` -- the valuations of ``coefficients`` or ``None`` (default: ``None``); ignored @@ -444,7 +444,7 @@ def equivalence_unit(self, s, reciprocal=False): - ``s`` -- an element of the :meth:`value_group` - ``reciprocal`` -- a boolean (default: ``False``); whether or not to - return the equivalence unit as the :meth:`equivalence_reciprocal` of + return the equivalence unit as the :meth:`inductive_valuation.InductiveValuation.equivalence_reciprocal` of the equivalence unit of valuation ``-s`` EXAMPLES:: @@ -588,7 +588,7 @@ def is_gauss_valuation(self): def augmentation_chain(self): r""" Return a list with the chain of augmentations down to the underlying - :class:`GaussValuation`. + Gauss valuation. EXAMPLES:: From 2f6dd581cc8768f4cfa467629df2fe50773758b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 23:02:10 -0500 Subject: [PATCH 328/740] fix MacLane references --- src/sage/rings/valuation/augmented_valuation.py | 4 ++-- src/sage/rings/valuation/developing_valuation.py | 6 +++--- src/sage/rings/valuation/inductive_valuation.py | 14 +++++++------- src/sage/rings/valuation/limit_valuation.py | 4 ++-- src/sage/rings/valuation/valuation.py | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index b5e959c05e2..523feb7877c 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -1203,7 +1203,7 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations ALGORITHM: - We follow the algorithm given in the proof of Theorem 12.1 of [Mac1936]: + We follow the algorithm given in the proof of Theorem 12.1 of [Mac1936]_: If ``f`` has positive valuation, the reduction is simply zero. Otherwise, let `f=\sum f_i\phi^i` be the expansion of `f`, as computed by :meth:`coefficients`. Since the valuation is zero, the exponents `i` @@ -1455,7 +1455,7 @@ def lift_to_key(self, F, check=True): ALGORITHM: - We follow the algorithm described in Theorem 13.1 [Mac1936] which, after + We follow the algorithm described in Theorem 13.1 [Mac1936]_ which, after a :meth:`lift` of ``F``, essentially shifts the valuations of all terms in the `\phi`-adic expansion up and then kills the leading coefficient. diff --git a/src/sage/rings/valuation/developing_valuation.py b/src/sage/rings/valuation/developing_valuation.py index ebc6eaf6d33..c8365dfa443 100644 --- a/src/sage/rings/valuation/developing_valuation.py +++ b/src/sage/rings/valuation/developing_valuation.py @@ -11,7 +11,7 @@ EXAMPLES: -The :class:`GaussValuation` is a simple example of a valuation that relies on +The :class:`gauss_valuation.GaussValuation` is a simple example of a valuation that relies on `\phi`-adic expansions:: sage: R. = QQ[] @@ -29,7 +29,7 @@ sage: v.coefficients(f) Another example of a :class:`DevelopingValuation` is an -:class:`AugmentedValuation`:: +:class:`augmented_valuation.AugmentedValuation`:: sage: w = v.augmentation(x^2 + 2, 3) @@ -111,7 +111,7 @@ def effective_degree(self, f, valuations=None): The effective degree of `f` is the largest `i` such that the valuation of `f` and the valuation of `f_i\phi^i` in the development `f=\sum_j - f_j\phi^j` coincide (see [Mac1936II] p.497.) + f_j\phi^j` coincide (see [Mac1936II]_ p.497.) INPUT: diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index bb94a4ef035..3aa6e4cf4fd 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -25,8 +25,8 @@ REFERENCES: -Inductive valuations are originally discussed in [Mac1936I]_ and [Mac1936II]. An -introduction is also given in Chapter 4 of [Rüt2014]_. +Inductive valuations are originally discussed in [Mac1936I]_ and [Mac1936II]_. +An introduction is also given in Chapter 4 of [Rüt2014]_. """ #***************************************************************************** @@ -61,7 +61,7 @@ class InductiveValuation(DevelopingValuation): def is_equivalence_unit(self, f, valuations=None): r""" Return whether ``f`` is an equivalence unit, i.e., an element of - :meth:`effective_degree` zero (see [Mac1936II] p.497.) + :meth:`effective_degree` zero (see [Mac1936II]_ p.497.) INPUT: @@ -91,7 +91,7 @@ def equivalence_reciprocal(self, f, coefficients=None, valuations=None, check=Tr Return an equivalence reciprocal of ``f``. An equivalence reciprocal of `f` is a polynomial `h` such that `f\cdot - h` is equivalent to 1 modulo this valuation (see [Mac1936II] p.497.) + h` is equivalent to 1 modulo this valuation (see [Mac1936II]_ p.497.) INPUT: @@ -921,7 +921,7 @@ def is_minimal(self, f, assume_equivalence_irreducible=False): ALGORITHM: - Based on Theorem 9.4 of [Mac1936II]. + Based on Theorem 9.4 of [Mac1936II]_. EXAMPLES:: @@ -1128,7 +1128,7 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi ALGORITHM: - We use the algorithm described in Theorem 4.4 of [Mac1936II]. After + We use the algorithm described in Theorem 4.4 of [Mac1936II]_. After removing all factors `\phi` from a polynomial `f`, there is an equivalence unit `R` such that `Rf` has valuation zero. Now `Rf` can be factored as `\prod_i \alpha_i` over the :meth:`residue_field`. Lifting @@ -1279,7 +1279,7 @@ def minimal_representative(self, f): ALGORITHM: - We use the algorithm described in the proof of Lemma 4.1 of [Mac1936II]. + We use the algorithm described in the proof of Lemma 4.1 of [Mac1936II]_. In the expansion `f=\sum_i f_i\phi^i` take `e=f_i` for the largest `i` with `f_i\phi^i` minimal (see :meth:`effective_degree`). Let `h` be the :meth:`equivalence_reciprocal` of `e` and take `a` given diff --git a/src/sage/rings/valuation/limit_valuation.py b/src/sage/rings/valuation/limit_valuation.py index b2874549960..7871901d36e 100644 --- a/src/sage/rings/valuation/limit_valuation.py +++ b/src/sage/rings/valuation/limit_valuation.py @@ -563,12 +563,12 @@ def _improve_approximation_for_call(self, f): `v(a_0) < v(a_i\phi^i)` for all `i\ne 0`. If we denote the limit valuation as `w`, then `v(a_i\phi^i)=w(a_i\phi^i)` since the valuation of key polynomials does not change during augmentations - (Theorem 6.4 in [Mac1936II].) By the strict triangle inequality, + (Theorem 6.4 in [Mac1936II]_.) By the strict triangle inequality, `w(g)=v(g)`. Note that any `g` which is coprime to `G` is an equivalence-unit after finitely many steps of the Mac Lane algorithm. Indeed, otherwise the valuation of `g` would be infinite (follows from - Theorem 5.1 in [Mac1936II]) since the valuation of the key + Theorem 5.1 in [Mac1936II]_) since the valuation of the key polynomials increases. When `f` is not coprime to `G`, consider `s=gcd(f,G)` and write `G=st`. Since `G` is squarefree, either `s` or `t` have finite diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 04af00099ea..f3ca179a807 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -996,7 +996,7 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No REFERENCES: - The underlying algorithm is described in [Mac1936II]_and thoroughly + The underlying algorithm is described in [Mac1936II]_ and thoroughly analyzed in [GMN2008]_. """ From 738904bb0adc2742026c55954efbfbb8d15eab82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 23:08:02 -0500 Subject: [PATCH 329/740] trying to fix augmented valuation links --- src/sage/rings/valuation/developing_valuation.py | 4 ++-- src/sage/rings/valuation/inductive_valuation.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/valuation/developing_valuation.py b/src/sage/rings/valuation/developing_valuation.py index c8365dfa443..6e55f0fa5e9 100644 --- a/src/sage/rings/valuation/developing_valuation.py +++ b/src/sage/rings/valuation/developing_valuation.py @@ -28,8 +28,8 @@ sage: v.coefficients(f) -Another example of a :class:`DevelopingValuation` is an -:class:`augmented_valuation.AugmentedValuation`:: +Another example of a :class:`DevelopingValuation` is an :mod:`augmented +valuation `:: sage: w = v.augmentation(x^2 + 2, 3) diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index 3aa6e4cf4fd..061951d7232 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -3,7 +3,7 @@ Inductive valuations on polynomial rings This module provides functionality for inductive valuations, i.e., finite -chains of :class:`AugmentedValuation`s on top of a :class:`GaussValuation`. +chains of :mod:`augmented_valuation augmented valuation` on top of a :class:`gauss_valuation.GaussValuation`. AUTHORS: @@ -45,7 +45,7 @@ class InductiveValuation(DevelopingValuation): r""" - Abstract base class for iterated :class:`AugmentedValuation` on top of a + Abstract base class for iterated :mod:`augmented valuations ` on top of a :class:`GaussValuation`. EXAMPLES:: From 007499ebc0532f8c9fb63767d359b9dcbf582cac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 23:11:50 -0500 Subject: [PATCH 330/740] shorten links --- src/sage/rings/valuation/developing_valuation.py | 2 +- src/sage/rings/valuation/gauss_valuation.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/valuation/developing_valuation.py b/src/sage/rings/valuation/developing_valuation.py index 6e55f0fa5e9..584616f4b06 100644 --- a/src/sage/rings/valuation/developing_valuation.py +++ b/src/sage/rings/valuation/developing_valuation.py @@ -29,7 +29,7 @@ sage: v.coefficients(f) Another example of a :class:`DevelopingValuation` is an :mod:`augmented -valuation `:: +valuation `:: sage: w = v.augmentation(x^2 + 2, 3) diff --git a/src/sage/rings/valuation/gauss_valuation.py b/src/sage/rings/valuation/gauss_valuation.py index d38410c7a66..c88b7395bf9 100644 --- a/src/sage/rings/valuation/gauss_valuation.py +++ b/src/sage/rings/valuation/gauss_valuation.py @@ -237,7 +237,7 @@ def valuations(self, f, coefficients=None, call_error=False): - ``f`` -- a polynomial in the domain of this valuation - ``coefficients`` -- the coefficients of ``f`` as produced by - :meth:`developing_valuation.DevelopingValuation.coefficients` or ``None`` (default: ``None``); this can be + :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.coefficients` or ``None`` (default: ``None``); this can be used to speed up the computation when the expansion of ``f`` is already known from a previous computation. @@ -317,7 +317,7 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations result which can speed up the computation (default: not set) - ``coefficients`` -- the coefficients of ``f`` as produced by - :meth:`developing_valuation.DevelopingValuation.coefficients` or ``None`` (default: ``None``); ignored + :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.coefficients` or ``None`` (default: ``None``); ignored - ``valuations`` -- the valuations of ``coefficients`` or ``None`` (default: ``None``); ignored From a105a8f43dc3bce318bb373d9fccf0a0ec34948b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 23:23:16 -0500 Subject: [PATCH 331/740] fixed links in documentation --- .../rings/valuation/developing_valuation.py | 2 +- src/sage/rings/valuation/gauss_valuation.py | 2 +- .../rings/valuation/inductive_valuation.py | 25 ++++++++++--------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/sage/rings/valuation/developing_valuation.py b/src/sage/rings/valuation/developing_valuation.py index 584616f4b06..30617351e97 100644 --- a/src/sage/rings/valuation/developing_valuation.py +++ b/src/sage/rings/valuation/developing_valuation.py @@ -11,7 +11,7 @@ EXAMPLES: -The :class:`gauss_valuation.GaussValuation` is a simple example of a valuation that relies on +The :class:`^sage.rings.valuation.gauss_valuation.GaussValuation` is a simple example of a valuation that relies on `\phi`-adic expansions:: sage: R. = QQ[] diff --git a/src/sage/rings/valuation/gauss_valuation.py b/src/sage/rings/valuation/gauss_valuation.py index c88b7395bf9..7183b188dfd 100644 --- a/src/sage/rings/valuation/gauss_valuation.py +++ b/src/sage/rings/valuation/gauss_valuation.py @@ -444,7 +444,7 @@ def equivalence_unit(self, s, reciprocal=False): - ``s`` -- an element of the :meth:`value_group` - ``reciprocal`` -- a boolean (default: ``False``); whether or not to - return the equivalence unit as the :meth:`inductive_valuation.InductiveValuation.equivalence_reciprocal` of + return the equivalence unit as the :meth:`~sage.rings.valuation.inductive_valuation.InductiveValuation.equivalence_reciprocal` of the equivalence unit of valuation ``-s`` EXAMPLES:: diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index 061951d7232..31f8ad10f0e 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -3,7 +3,7 @@ Inductive valuations on polynomial rings This module provides functionality for inductive valuations, i.e., finite -chains of :mod:`augmented_valuation augmented valuation` on top of a :class:`gauss_valuation.GaussValuation`. +chains of :mod:`sage.rings.valuation.augmented_valuation ` on top of a :class:`~sage.rings.valuation.gauss_valuation.GaussValuation`. AUTHORS: @@ -45,8 +45,7 @@ class InductiveValuation(DevelopingValuation): r""" - Abstract base class for iterated :mod:`augmented valuations ` on top of a - :class:`GaussValuation`. + Abstract base class for iterated :mod:`sage.rings.valuation.augmented_valuation ` on top of a :class:`~sage.rings.valuation.gauss_valuation.GaussValuation`. EXAMPLES:: @@ -231,7 +230,7 @@ def equivalence_unit(self, s, reciprocal=False): INPUT: - - ``s`` -- an element of the :meth:`value_group` + - ``s`` -- an element of the :meth:`~sage.rings.valuation.valuation.DiscreteValuation.value_group` - ``reciprocal`` -- a boolean (default: ``False``); whether or not to return the equivalence unit as the :meth:`equivalence_reciprocal` of @@ -265,7 +264,7 @@ def equivalence_unit(self, s, reciprocal=False): def augmentation_chain(self): r""" Return a list with the chain of augmentations down to the underlying - :class:`GaussValuation`. + :class:`~sage.rings.valuation.gauss_valuation.GaussValuation`. EXAMPLES:: @@ -655,7 +654,7 @@ def augmentation(self, phi, mu, check=True): .. SEEALSO:: - :meth:`AugmentedValuation` + :mod:`~sage.rings.valuation.augmented_valuation` """ from augmented_valuation import AugmentedValuation @@ -664,7 +663,7 @@ def augmentation(self, phi, mu, check=True): def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, assume_equivalence_irreducible=False, report_degree_bounds_and_caches=False, coefficients=None, valuations=None, check=True): r""" Perform an approximation step towards the squarefree monic non-constant - integral polynomial ``G`` which is not an :meth:`equivalence_unit`. + integral polynomial ``G`` which is not an :meth:`is_equivalence_unit `. This performs the individual steps that are used in :meth:`mac_lane_approximants`. @@ -672,7 +671,7 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a INPUT: - ``G`` -- a sqaurefree monic non-constant integral polynomial ``G`` - which is not an :meth:`equivalence_unit` + which is not an :meth:`is_equivalence_unit ` - ``principal_part_bound`` -- an integer or ``None`` (default: ``None``), a bound on the length of the principal part, i.e., the @@ -693,7 +692,7 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a (default: ``None``) - ``check`` -- whether to check that ``G`` is a squarefree monic - non-constant integral polynomial and not an :meth:`equivalence_unit` + non-constant integral polynomial and not an :meth:`is_equivalence_unit ` (default: ``True``) TESTS:: @@ -1104,8 +1103,10 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi r""" Return an equivalence decomposition of ``f``, i.e., a polynomial `g(x)=e(x)\prod_i \phi_i(x)` with `e(x)` an equivalence unit (see - :meth:`is_equivalence_unit()`) and the `\phi_i` key polynomials (see - :meth:`is_key`) such that ``f`` :meth:`is_equivalent` to `g`. + :meth:`is_equivalence_unit`) and the `\phi_i` key polynomials (see + :meth:`is_key`) such that ``f`` + :meth:`~sage.rings.valuation.valuation.DiscretePseudoValuation.is_equivalent` + to `g`. INPUT: @@ -1281,7 +1282,7 @@ def minimal_representative(self, f): We use the algorithm described in the proof of Lemma 4.1 of [Mac1936II]_. In the expansion `f=\sum_i f_i\phi^i` take `e=f_i` for the largest `i` - with `f_i\phi^i` minimal (see :meth:`effective_degree`). + with `f_i\phi^i` minimal (see :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.effective_degree`). Let `h` be the :meth:`equivalence_reciprocal` of `e` and take `a` given by the terms of minimal valuation in the expansion of `e f`. From 3b91735e9718a4b000a627a451ddc6aedef61ac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 23:26:42 -0500 Subject: [PATCH 332/740] fixing links in developing valuation --- src/sage/rings/valuation/developing_valuation.py | 2 +- src/sage/rings/valuation/inductive_valuation.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/valuation/developing_valuation.py b/src/sage/rings/valuation/developing_valuation.py index 30617351e97..b6e5fc3e370 100644 --- a/src/sage/rings/valuation/developing_valuation.py +++ b/src/sage/rings/valuation/developing_valuation.py @@ -11,7 +11,7 @@ EXAMPLES: -The :class:`^sage.rings.valuation.gauss_valuation.GaussValuation` is a simple example of a valuation that relies on +The :class:`~sage.rings.valuation.gauss_valuation.GaussValuation` is a simple example of a valuation that relies on `\phi`-adic expansions:: sage: R. = QQ[] diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index 31f8ad10f0e..c92d1591a7a 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -1155,7 +1155,7 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi ((1 + O(2^10))*x + 1 + O(2^10))^2 A polynomial that is an equivalence unit, is returned as the unit part - of a :class:`sage.structure.factorization.Factorization`, leading to a unit + of a :class:`~sage.structure.factorization.Factorization`, leading to a unit non-minimal degree:: sage: w = v.augmentation(x, 1) From ee5abb213abfaa78d2ebe82f839c5fe312b54146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 22 Jul 2017 23:34:20 -0500 Subject: [PATCH 333/740] Fix links in developing valuation --- src/sage/rings/valuation/developing_valuation.py | 2 +- src/sage/rings/valuation/inductive_valuation.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/valuation/developing_valuation.py b/src/sage/rings/valuation/developing_valuation.py index b6e5fc3e370..1c4fcab78ce 100644 --- a/src/sage/rings/valuation/developing_valuation.py +++ b/src/sage/rings/valuation/developing_valuation.py @@ -11,7 +11,7 @@ EXAMPLES: -The :class:`~sage.rings.valuation.gauss_valuation.GaussValuation` is a simple example of a valuation that relies on +The :mod:`Gauss valuation ` is a simple example of a valuation that relies on `\phi`-adic expansions:: sage: R. = QQ[] diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index c92d1591a7a..8c795e0ef9d 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -45,7 +45,7 @@ class InductiveValuation(DevelopingValuation): r""" - Abstract base class for iterated :mod:`sage.rings.valuation.augmented_valuation ` on top of a :class:`~sage.rings.valuation.gauss_valuation.GaussValuation`. + Abstract base class for iterated :mod:`augmented valuations ` on top of a :class:`~sage.rings.valuation.gauss_valuation.GaussValuation`. EXAMPLES:: From 6da75364b1989cfb8a773556081ca580c4b76873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 23 Jul 2017 06:22:23 +0000 Subject: [PATCH 334/740] fix broken documentation links --- .../function_field_valuation.py | 4 +- src/sage/rings/padics/padic_valuation.py | 11 +- .../rings/valuation/augmented_valuation.py | 118 ++++++++++-------- .../rings/valuation/inductive_valuation.py | 82 ++++++------ src/sage/rings/valuation/limit_valuation.py | 8 +- src/sage/rings/valuation/mapped_valuation.py | 10 +- src/sage/rings/valuation/scaled_valuation.py | 3 +- src/sage/rings/valuation/trivial_valuation.py | 28 ----- src/sage/rings/valuation/valuation.py | 2 +- src/sage/rings/valuation/valuation_space.py | 50 ++++++-- 10 files changed, 164 insertions(+), 152 deletions(-) diff --git a/src/sage/rings/function_field/function_field_valuation.py b/src/sage/rings/function_field/function_field_valuation.py index 15e75255c67..f890ae07e3d 100644 --- a/src/sage/rings/function_field/function_field_valuation.py +++ b/src/sage/rings/function_field/function_field_valuation.py @@ -654,7 +654,7 @@ def uniformizer(self): def lift(self, F): r""" - Return a lift of ``F`` to the :meth:`domain` of this valuation such + Return a lift of ``F`` to the domain of this valuation such that :meth:`reduce` returns the original element. EXAMPLES:: @@ -749,7 +749,7 @@ def _repr_(self): def extensions(self, L): r""" Return all extensions of this valuation to ``L`` which has a larger - constant field than the :meth:`domain` of this valuation. + constant field than the domain of this valuation. EXAMPLES:: diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index ff2796ec54c..d10654ff082 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -96,12 +96,11 @@ class PadicValuationFactory(UniqueFactory): .. SEEALSO:: - For more examples, see - :meth:`sage.rings.number_field.number_field.NumberField.valuation`, - :meth:`sage.rings.order.Order.valuation`, - :meth:`sage.rings.padics.padic_generic.pAdicGeneric.valuation`, - :meth:`sage.rings.rational_field.RationalField.valuation`, - :meth:`sage.rings.integer_ring.IntegerRing.valuation`. + :meth:`NumberField_generic.valuation() `, + :meth:`Order.valuation() `, + :meth:`pAdicGeneric.valuation() `, + :meth:`RationalField.valuation() `, + :meth:`IntegerRing_class.valuation() `. """ def create_key_and_extra_args(self, R, prime=None, approximants=None): diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index 523feb7877c..6c2ce99fe50 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -10,7 +10,7 @@ EXAMPLES: -Starting from a :class:`GaussValuation`, we can create augmented valuations on +Starting from a :mod:`Gauss valuation `, we can create augmented valuations on polynomial rings:: sage: R. = QQ[] @@ -166,7 +166,8 @@ class AugmentedValuationFactory(UniqueFactory): EXAMPLES: This factory is not meant to be called directly. Instead, - :meth:`augmentation` of a valuation should be called:: + :meth:`~sage.rings.valuation.inductive_valuation.NonFinalInductiveValuation.augmentation` + of a valuation should be called:: sage: R. = QQ[] sage: v = GaussValuation(R, QQ.valuation(2)) @@ -250,7 +251,7 @@ def create_object(self, version, key): AugmentedValuation = AugmentedValuationFactory("AugmentedValuation") class AugmentedValuation_base(InductiveValuation): - """ + r""" An augmented valuation is a discrete valuation on a polynomial ring. It extends another discrete valuation `v` by setting the valuation of a polynomial `f` to the minumum of `v(f_i)i\mu` when writing `f=\sum_i @@ -258,9 +259,9 @@ class AugmentedValuation_base(InductiveValuation): INPUT: - - ``v`` -- a :class:`InductiveValuation` on a polynomial ring + - ``v`` -- a :class:`~sage.rings.valuation.inductive_valuation.InductiveValuation` on a polynomial ring - - ``phi`` -- a key polynomial over ``v`` (see :meth:`is_key`) + - ``phi`` -- a :meth:`key polynomial ` over ``v`` - ``mu`` -- a rational number such that ``mu > v(phi)`` or ``infinity`` @@ -281,7 +282,7 @@ class AugmentedValuation_base(InductiveValuation): """ def __init__(self, parent, v, phi, mu): - """ + r""" TESTS:: sage: K. = Qq(4, 5) @@ -303,7 +304,7 @@ def __init__(self, parent, v, phi, mu): @cached_method def equivalence_unit(self, s, reciprocal=False): - """ + r""" Return an equivalence unit of minimal degree and valuation ``s``. INPUT: @@ -311,13 +312,13 @@ def equivalence_unit(self, s, reciprocal=False): - ``s`` -- a rational number - ``reciprocal`` -- a boolean (default: ``False``); whether or not to - return the equivalence unit as the :meth:`equivalence_reciprocal` of - the equivalence unit of valuation ``-s``. + return the equivalence unit as the :meth:`~sage.rings.valuation.inductive_valuation.InductiveValuation.equivalence_reciprocal` + of the equivalence unit of valuation ``-s``. OUTPUT: A polynomial in the domain of this valuation which - :meth:`is_equivalence_unit` for this valuation. + :meth:`~sage.rings.valuation.inductive_valuation.InductiveValuation.is_equivalence_unit` for this valuation. EXAMPLES:: @@ -367,7 +368,7 @@ def equivalence_unit(self, s, reciprocal=False): @cached_method def element_with_valuation(self, s): - """ + r""" Create an element of minimal degree and of valuation ``s``. INPUT: @@ -412,7 +413,7 @@ def element_with_valuation(self, s): return self.simplify(ret, error=error) def _repr_(self): - """ + r""" Return a printable representation of this valuation. EXAMPLES:: @@ -432,8 +433,7 @@ def _repr_(self): def augmentation_chain(self): r""" - Return a list with the chain of augmentations down to the underlying - :class:`GaussValuation`. + Return a list with the chain of augmentations down to the underlying :mod:`Gauss valuation `. EXAMPLES:: @@ -459,7 +459,7 @@ def augmentation_chain(self): @cached_method def psi(self): - """ + r""" Return the minimal polynomial of the residue field extension of this valuation. OUTPUT: @@ -488,7 +488,7 @@ def psi(self): @cached_method def E(self): - """ + r""" Return the ramification index of this valuation over its underlying Gauss valuation. @@ -513,7 +513,7 @@ def E(self): @cached_method def F(self): - """ + r""" Return the degree of the residue field extension of this valuation over the underlying Gauss valuation. @@ -925,9 +925,10 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations result which can speed up the computation (default: not set) - ``coefficients`` -- the coefficients of ``f`` as produced by - :meth:`coefficients` or ``None`` (default: ``None``); this can be - used to speed up the computation when the expansion of ``f`` is - already known from a previous computation. + :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.coefficients` + or ``None`` (default: ``None``); this can be used to speed up the + computation when the expansion of ``f`` is already known from a + previous computation. - ``valuations`` -- the valuations of ``coefficients`` or ``None`` (default: ``None``); ignored @@ -1044,8 +1045,8 @@ def _residue_field_generator(self): return ret def lift(self, F): - """ - Return a polynomial which :meth:`reduce`s to ``F``. + r""" + Return a polynomial which reduces to ``F``. INPUT: @@ -1189,9 +1190,10 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations result which can speed up the computation (default: not set) - ``coefficients`` -- the coefficients of ``f`` as produced by - :meth:`coefficients` or ``None`` (default: ``None``); this can be - used to speed up the computation when the expansion of ``f`` is - already known from a previous computation. + :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.coefficients` + or ``None`` (default: ``None``); this can be used to speed up the + computation when the expansion of ``f`` is already known from a + previous computation. - ``valuations`` -- the valuations of ``coefficients`` or ``None`` (default: ``None``) @@ -1206,15 +1208,18 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations We follow the algorithm given in the proof of Theorem 12.1 of [Mac1936]_: If ``f`` has positive valuation, the reduction is simply zero. Otherwise, let `f=\sum f_i\phi^i` be the expansion of `f`, as computed - by :meth:`coefficients`. Since the valuation is zero, the exponents `i` - must all be multiples of `\tau`, the index the value group of the base - valuation in the value group of this valuation. - Hence, there is an :meth:`equivalence_unit` `Q` with the same valuation - as `\phi^\tau`. Let `Q'` be its :meth:`reciprocal_inverse`. + by + :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.coefficients`. + Since the valuation is zero, the exponents `i` must all be multiples of + `\tau`, the index the value group of the base valuation in the value + group of this valuation. Hence, there is an + :meth:`~sage.rings.valuation.inductive_valuation.InductiveValuation.equivalence_unit` + `Q` with the same valuation as `\phi^\tau`. Let `Q'` be its + :meth:`~sage.rings.valuation.inductive_valuation.InductiveValuation.equivalence_reciprocal`. Now, rewrite each term `f_i\phi^{i\tau}=(f_iQ^i)(\phi^\tau Q^{-1})^i`; it turns out that the second factor in this expression is a lift of the - generator of the :meth:`residue_field`. The reduction of the first - factor can be computed recursively. + generator of the :meth:`~sage.rings.valuation.valuation_space.DiscretePseudoValuationSpace.ElementMethods.residue_field`. + The reduction of the first factor can be computed recursively. EXAMPLES:: @@ -1333,15 +1338,15 @@ def _residue_field_generator(self): return ret def lift(self, F, report_coefficients=False): - """ - Return a polynomial which :meth:`reduce`s to ``F``. + r""" + Return a polynomial which reduces to ``F``. INPUT: - ``F`` -- an element of the :meth:`residue_ring` - ``report_coefficients`` -- whether to return the coefficients of the - :meth:`phi`-adic expansion or the actual polynomial (default: + :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.phi`-adic expansion or the actual polynomial (default: ``False``, i.e., return the polynomial) OUTPUT: @@ -1436,7 +1441,7 @@ def lift(self, F, report_coefficients=False): return ret def lift_to_key(self, F, check=True): - """ + r""" Lift the irreducible polynomial ``F`` to a key polynomial. INPUT: @@ -1607,7 +1612,7 @@ def __init__(self, parent, v, phi, mu): @cached_method def value_group(self): - """ + r""" Return the value group of this valuation. EXAMPLES:: @@ -1649,7 +1654,7 @@ def value_semigroup(self): return self._base_valuation.value_semigroup() + self._mu def valuations(self, f, coefficients=None, call_error=False): - """ + r""" Return the valuations of the `f_i\phi^i` in the expansion `f=\sum_i f_i\phi^i`. @@ -1658,9 +1663,10 @@ def valuations(self, f, coefficients=None, call_error=False): - ``f`` -- a polynomial in the domain of this valuation - ``coefficients`` -- the coefficients of ``f`` as produced by - :meth:`coefficients` or ``None`` (default: ``None``); this can be - used to speed up the computation when the expansion of ``f`` is - already known from a previous computation. + :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.coefficients` + or ``None`` (default: ``None``); this can be used to speed up the + computation when the expansion of ``f`` is already known from a + previous computation. - ``call_error`` -- whether or not to speed up the computation by assuming that the result is only used to compute the valuation of @@ -1726,7 +1732,7 @@ def simplify(self, f, error=None, force=False, effective_degree=None, size_heuri (default: ``False``) - ``effective_degree`` -- when set, assume that coefficients beyond - ``effective_degree`` in the :meth:`phi`-adic development can be + ``effective_degree`` in the :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.phi`-adic development can be safely dropped (default: ``None``) - ``size_heuristic_bound`` -- when ``force`` is not set, the expected @@ -1768,10 +1774,12 @@ def lower_bound(self, f): ALGORITHM: The main cost of evaluation is the computation of the - :meth:`coefficients` of the :meth:`phi`-adic expansion of ``f`` (which - often leads to coefficient bloat.) So unless :meth:`phi` is trivial, we - fall back to valuation which this valuation augments since it is - guaranteed to be smaller everywhere. + :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.coefficients` + of the :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.phi`-adic + expansion of ``f`` (which often leads to coefficient bloat.) So unless + :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.phi` + is trivial, we fall back to valuation which this valuation augments + since it is guaranteed to be smaller everywhere. EXAMPLES:: @@ -1809,10 +1817,11 @@ def upper_bound(self, f): ALGORITHM: Any entry of :meth:`valuations` serves as an upper bound. However, - computation of the :meth:`phi`-adic expansion of ``f`` is quite costly. + computation of the :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.phi`-adic + expansion of ``f`` is quite costly. Therefore, we produce an upper bound on the last entry of :meth:`valuations`, namely the valuation of the leading coefficient of - ``f`` plus the valuation of the appropriate power of :meth:`phi`. + ``f`` plus the valuation of the appropriate power of :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.phi`. EXAMPLES:: @@ -1917,7 +1926,7 @@ def __init__(self, parent, v, phi, mu): @cached_method def value_group(self): - """ + r""" Return the value group of this valuation. EXAMPLES:: @@ -1934,7 +1943,7 @@ def value_group(self): @cached_method def value_semigroup(self): - """ + r""" Return the value semigroup of this valuation. EXAMPLES:: @@ -1950,7 +1959,7 @@ def value_semigroup(self): return self._base_valuation.value_semigroup() def valuations(self, f, coefficients=None, call_error=False): - """ + r""" Return the valuations of the `f_i\phi^i` in the expansion `f=\sum_i f_i\phi^i`. @@ -1959,9 +1968,10 @@ def valuations(self, f, coefficients=None, call_error=False): - ``f`` -- a polynomial in the domain of this valuation - ``coefficients`` -- the coefficients of ``f`` as produced by - :meth:`coefficients` or ``None`` (default: ``None``); this can be - used to speed up the computation when the expansion of ``f`` is - already known from a previous computation. + :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.coefficients` + or ``None`` (default: ``None``); this can be used to speed up the + computation when the expansion of ``f`` is already known from a + previous computation. - ``call_error`` -- whether or not to speed up the computation by assuming that the result is only used to compute the valuation of diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index 8c795e0ef9d..6f12c862f96 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -3,7 +3,7 @@ Inductive valuations on polynomial rings This module provides functionality for inductive valuations, i.e., finite -chains of :mod:`sage.rings.valuation.augmented_valuation ` on top of a :class:`~sage.rings.valuation.gauss_valuation.GaussValuation`. +chains of :mod:`augmented valuations ` on top of a :mod:`Gauss valuation `. AUTHORS: @@ -11,7 +11,7 @@ EXAMPLES: -A :class:`GaussValuation` is an example of an inductive valuation:: +A :mod:`Gauss valuation ` is an example of an inductive valuation:: sage: R. = QQ[] sage: v = GaussValuation(R, QQ.valuation(2)) @@ -45,7 +45,7 @@ class InductiveValuation(DevelopingValuation): r""" - Abstract base class for iterated :mod:`augmented valuations ` on top of a :class:`~sage.rings.valuation.gauss_valuation.GaussValuation`. + Abstract base class for iterated :mod:`augmented valuations ` on top of a :mod:`Gauss valuation `. EXAMPLES:: @@ -59,8 +59,9 @@ class InductiveValuation(DevelopingValuation): """ def is_equivalence_unit(self, f, valuations=None): r""" - Return whether ``f`` is an equivalence unit, i.e., an element of - :meth:`effective_degree` zero (see [Mac1936II]_ p.497.) + Return whether the polynomial ``f`` is an equivalence unit, i.e., an + element of :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.effective_degree` + zero (see [Mac1936II]_ p.497.) INPUT: @@ -97,7 +98,7 @@ def equivalence_reciprocal(self, f, coefficients=None, valuations=None, check=Tr - ``f`` -- a polynomial in the domain of this valuation which is an :meth:`equivalence_unit` - - ``coefficients`` -- the coefficients of ``f`` in the :meth:`phi`-adic + - ``coefficients`` -- the coefficients of ``f`` in the :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.phi`-adic expansion if known (default: ``None``) - ``valuations`` -- the valuations of ``coefficients`` if known @@ -230,7 +231,7 @@ def equivalence_unit(self, s, reciprocal=False): INPUT: - - ``s`` -- an element of the :meth:`~sage.rings.valuation.valuation.DiscreteValuation.value_group` + - ``s`` -- an element of the :meth:`~sage.rings.valuation.valuation_space.DiscretePseudoValuationSpace.ElementMethods.value_group` - ``reciprocal`` -- a boolean (default: ``False``); whether or not to return the equivalence unit as the :meth:`equivalence_reciprocal` of @@ -264,7 +265,7 @@ def equivalence_unit(self, s, reciprocal=False): def augmentation_chain(self): r""" Return a list with the chain of augmentations down to the underlying - :class:`~sage.rings.valuation.gauss_valuation.GaussValuation`. + :mod:`Gauss valuation `. EXAMPLES:: @@ -532,9 +533,9 @@ def _test_inductive_valuation_inheritance(self, **options): class FiniteInductiveValuation(InductiveValuation, DiscreteValuation): r""" - Abstract base class for iterated :class:`AugmentedValuation` on top of a - :class:`GaussValuation` which is a discrete valuation, i.e., the last key - polynomial has finite valuation. + Abstract base class for iterated :mod:`augmented valuations ` + on top of a :mod:`Gauss valuation ` which is a discrete valuation, + i.e., the last key polynomial has finite valuation. EXAMPLES:: @@ -579,9 +580,9 @@ def extensions(self, other): class NonFinalInductiveValuation(FiniteInductiveValuation, DiscreteValuation): r""" - Abstract base class for iterated :class:`AugmentedValuation` on top of a - :class:`GaussValuation` which can be extended further through - :meth:`augmentation`. + Abstract base class for iterated :mod:`augmented valuations ` + on top of a :mod:`Gauss valuation ` which can be extended further + through :meth:`augmentation`. EXAMPLES:: @@ -663,15 +664,15 @@ def augmentation(self, phi, mu, check=True): def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, assume_equivalence_irreducible=False, report_degree_bounds_and_caches=False, coefficients=None, valuations=None, check=True): r""" Perform an approximation step towards the squarefree monic non-constant - integral polynomial ``G`` which is not an :meth:`is_equivalence_unit `. + integral polynomial ``G`` which is not an :meth:`equivalence unit `. This performs the individual steps that are used in - :meth:`mac_lane_approximants`. + :meth:`~sage.rings.valuation.valuation.DiscreteValuation.mac_lane_approximants`. INPUT: - ``G`` -- a sqaurefree monic non-constant integral polynomial ``G`` - which is not an :meth:`is_equivalence_unit ` + which is not an :meth:`equivalence unit ` - ``principal_part_bound`` -- an integer or ``None`` (default: ``None``), a bound on the length of the principal part, i.e., the @@ -683,16 +684,15 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a - ``assume_equivalence_irreducible`` -- whether or not to assume that ``G`` is equivalence irreducible (default: ``False``) - - ``report_degree_bounds_and_caches`` -- whether or not to include internal state with the returned value (used by :meth:`mac_lane_approximants` to speed up sequential calls) + - ``report_degree_bounds_and_caches`` -- whether or not to include internal state with the returned value (used by :meth:`~sage.rings.valuation.valuation.DiscreteValuation.mac_lane_approximants` to speed up sequential calls) - - ``coefficients`` -- the coefficients of ``G`` in the - :meth:`phi`-adic expansion if known (default: ``None``) + - ``coefficients`` -- the coefficients of ``G`` in the :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.phi`-adic expansion if known (default: ``None``) - ``valauations`` -- the valuations of ``coefficients`` if known (default: ``None``) - ``check`` -- whether to check that ``G`` is a squarefree monic - non-constant integral polynomial and not an :meth:`is_equivalence_unit ` + non-constant integral polynomial and not an :meth:`equivalence unit ` (default: ``True``) TESTS:: @@ -916,7 +916,7 @@ def is_minimal(self, f, assume_equivalence_irreducible=False): least the degree of `f`. A polynomial `h` is `v`-divisible by `f` if there is a polynomial `c` - such that `fc` :meth:`is_equivalent` to `h`. + such that `fc` :meth:`~sage.rings.valuation.valuation.DiscretePseudoValuation.is_equivalent` to `h`. ALGORITHM: @@ -1102,20 +1102,20 @@ def is_equivalence_irreducible(self, f, coefficients=None, valuations=None): def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coefficients=None, valuations=None, compute_unit=True, degree_bound=None): r""" Return an equivalence decomposition of ``f``, i.e., a polynomial - `g(x)=e(x)\prod_i \phi_i(x)` with `e(x)` an equivalence unit (see - :meth:`is_equivalence_unit`) and the `\phi_i` key polynomials (see - :meth:`is_key`) such that ``f`` - :meth:`~sage.rings.valuation.valuation.DiscretePseudoValuation.is_equivalent` - to `g`. + `g(x)=e(x)\prod_i \phi_i(x)` with `e(x)` an :meth:`equivalence unit + ` and the `\phi_i` :meth:`key + polynomials ` such that ``f`` :meth:`~sage.rings.valuation.valuation.DiscretePseudoValuation.is_equivalent` to `g`. INPUT: - ``f`` -- a non-zero polynomial in the domain of this valuation - ``assume_not_equivalence_unit`` -- whether or not to assume that - ``f`` is not an :meth:`equivalence_unit` (default: ``False``) + ``f`` is not an :meth:`equivalence unit ` + (default: ``False``) - - ``coefficients`` -- the coefficients of ``f`` in the :meth:`phi`-adic + - ``coefficients`` -- the coefficients of ``f`` in the + :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.phi`-adic expansion if known (default: ``None``) - ``valuations`` -- the valuations of ``coefficients`` if known @@ -1132,10 +1132,10 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi We use the algorithm described in Theorem 4.4 of [Mac1936II]_. After removing all factors `\phi` from a polynomial `f`, there is an equivalence unit `R` such that `Rf` has valuation zero. Now `Rf` can be - factored as `\prod_i \alpha_i` over the :meth:`residue_field`. Lifting + factored as `\prod_i \alpha_i` over the :meth:`~sage.rings.valuation.valuation_space.DiscretePseudoValuationSpace.ElementMethods.residue_field`. Lifting all `\alpha_i` to key polynomials `\phi_i` gives `Rf=\prod_i R_i f_i` for suitable equivalence units `R_i` (see :meth:`lift_to_key`). Taking - `R'` an :meth:`equivalence_reciprocal` of `R`, we have `f` equivalent + `R'` an :meth:`~InductiveValuation.equivalence_reciprocal` of `R`, we have `f` equivalent to `(R'\prod_i R_i)\prod_i\phi_i`. EXAMPLES:: @@ -1267,8 +1267,8 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi def minimal_representative(self, f): r""" Return a minimal representative for ``f``, i.e., a pair `e, a` such - that ``f`` :meth:`is_equivalent` to `e a`, `e` is an - :meth:`equivalence_unit`, and `a` :meth:`is_minimal` and monic. + that ``f`` :meth:`~sage.rings.valuation.valuation.DiscretePseudoValuation.is_equivalent` to `e a`, `e` is an + :meth:`equivalence unit `, and `a` :meth:`is_minimal` and monic. INPUT: @@ -1283,7 +1283,7 @@ def minimal_representative(self, f): We use the algorithm described in the proof of Lemma 4.1 of [Mac1936II]_. In the expansion `f=\sum_i f_i\phi^i` take `e=f_i` for the largest `i` with `f_i\phi^i` minimal (see :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.effective_degree`). - Let `h` be the :meth:`equivalence_reciprocal` of `e` and take `a` given + Let `h` be the :meth:`~InductiveValuation.equivalence_reciprocal` of `e` and take `a` given by the terms of minimal valuation in the expansion of `e f`. EXAMPLES:: @@ -1341,24 +1341,26 @@ def minimal_representative(self, f): @abstract_method def lift_to_key(self, F): """ - Lift the irreducible polynomial ``F`` from the :meth:`residue_ring` to - a key polynomial over this valuation. + Lift the irreducible polynomial ``F`` from the + :meth:`~sage.rings.valuation.valuation_space.DiscretePseudoValuationSpace.ElementMethods.residue_ring` + to a key polynomial over this valuation. INPUT: - ``F`` -- an irreducible non-constant monic polynomial in - :meth:`residue_ring` of this valuation + :meth:`~sage.rings.valuation.valuation_space.DiscretePseudoValuationSpace.ElementMethods.residue_ring` + of this valuation OUTPUT: A polynomial `f` in the domain of this valuation which is a key polynomial for this valuation and which is such that an :meth:`augmentation` with this polynomial adjoins a root of ``F`` to - the resulting :meth:`residue_ring`. + the resulting :meth:`~sage.rings.valuation.valuation_space.DiscretePseudoValuationSpace.ElementMethods.residue_ring`. More specifically, if ``F`` is not the generator of the residue ring, - then multiplying ``f`` with the :meth:`equivalence_reciprocal` of the - :meth:`equivalence_unit` of the valuation of ``f``, produces a unit + then multiplying ``f`` with the :meth:`~InductiveValuation.equivalence_reciprocal` of the + :meth:`~InductiveValuation.equivalence_unit` of the valuation of ``f``, produces a unit which reduces to ``F``. EXAMPLES:: diff --git a/src/sage/rings/valuation/limit_valuation.py b/src/sage/rings/valuation/limit_valuation.py index 7871901d36e..61f80bd7404 100644 --- a/src/sage/rings/valuation/limit_valuation.py +++ b/src/sage/rings/valuation/limit_valuation.py @@ -13,8 +13,8 @@ More specifically, let `v` be a discrete valuation on `K` and let `L=K[x]/(G)` a finite extension thereof. An extension of `v` to `L` can be represented as a discrete pseudo-valuation `w'` on `K[x]` which sends `G` to infinity. -However, such `w'` might not be described by an :class:`AugmentedValuation` -over a :class:`GaussValuation` anymore. Instead, we may need to write is as a +However, such `w'` might not be described by an :mod:`augmented valuation ` +over a :mod:`Gauss valuation ` anymore. Instead, we may need to write is as a limit of augmented valuations. The classes in this module provide the means of writing down such limits and @@ -420,8 +420,8 @@ def extensions(self, ring): def lift(self, F): r""" - Return a lift of ``F`` from the :meth:`residue_ring` to the - :meth:`domain` of this valuatiion. + Return a lift of ``F`` from the :meth:`residue_ring` to the domain of + this valuatiion. EXAMPLES:: diff --git a/src/sage/rings/valuation/mapped_valuation.py b/src/sage/rings/valuation/mapped_valuation.py index fd7d0d75722..a767dc6a6ba 100644 --- a/src/sage/rings/valuation/mapped_valuation.py +++ b/src/sage/rings/valuation/mapped_valuation.py @@ -205,8 +205,8 @@ def reduce(self, f): def lift(self, F): r""" - Lift ``F`` from the :meth:`residue_field` of this valuation into its - domain. + Lift ``F`` from the :meth:`~sage.rings.valuation.valuation_space.DiscretePseudoValuationSpace.ElementMethods.residue_field` + of this valuation into its domain. EXAMPLES:: @@ -227,8 +227,8 @@ def lift(self, F): def _to_base_residue_ring(self, F): r""" - Return ``F``, an element of :meth:`residue_ring`, as an element of the - residue ring of the ``_base_valuation``. + Return ``F``, an element of :meth:`~sage.rings.valuation.valuation_space.DiscretePseudoValuationSpace.ElementMethods.residue_ring`, + as an element of the residue ring of the ``_base_valuation``. EXAMPLES:: @@ -247,7 +247,7 @@ def _to_base_residue_ring(self, F): def _from_base_residue_ring(self, F): r""" Return ``F``, an element of the residue ring of ``_base_valuation``, as - an element of this valuation's :meth:`residue_ring`. + an element of this valuation's :meth:`~sage.rings.valuation.valuation_space.DiscretePseudoValuationSpace.ElementMethods.residue_ring`. EXAMPLES:: diff --git a/src/sage/rings/valuation/scaled_valuation.py b/src/sage/rings/valuation/scaled_valuation.py index 57bf7c39f71..2987ff6f3f5 100644 --- a/src/sage/rings/valuation/scaled_valuation.py +++ b/src/sage/rings/valuation/scaled_valuation.py @@ -188,7 +188,8 @@ def reduce(self, f): def lift(self, F): r""" - Lift ``F`` from the :meth;`residue_field` of this valuation into its + Lift ``F`` from the :meth:`~sage.rings.valuation.valuation_space.DiscretePseudoValuationSpace.ElementMethods.residue_field` + of this valuation into its domain. EXAMPLES:: diff --git a/src/sage/rings/valuation/trivial_valuation.py b/src/sage/rings/valuation/trivial_valuation.py index 17be6b4ab40..b4c876baa9b 100644 --- a/src/sage/rings/valuation/trivial_valuation.py +++ b/src/sage/rings/valuation/trivial_valuation.py @@ -13,34 +13,6 @@ sage: v(1) 0 -.. NOTE:: - - Note that the tests in this module do not create instances of valuations - directly since this gives the wrong inheritance structure on the resulting - objects:: - - sage: from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace - sage: from sage.rings.valuation.trivial_valuation import TrivialDiscretePseudoValuation - sage: H = DiscretePseudoValuationSpace(QQ) - sage: v = TrivialDiscretePseudoValuation(H) - sage: v._test_category() - Traceback (most recent call last): - ... - AssertionError: False is not true - - Instead, the valuations need to be created through the - ``__make_element_class__`` of the containing space:: - - sage: from sage.rings.valuation.trivial_valuation import TrivialDiscretePseudoValuation - sage: v = H.__make_element_class__(TrivialDiscretePseudoValuation)(H) - sage: v._test_category() - - The factories ``TrivialValuation`` and ``TrivialPseudoValuation`` provide the - right inheritance structure:: - - sage: v = valuations.TrivialPseudoValuation(QQ) - sage: v._test_category() - """ #***************************************************************************** # Copyright (C) 2016-2017 Julian Rüth diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index f3ca179a807..deef54181a0 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -1042,7 +1042,7 @@ class MacLaneApproximantNode(object): A node in the tree computed by :meth:`DiscreteValuation.mac_lane_approximants` Leaves in the computation of the tree of approximants - :meth:`DiscreteValuation.mac_lane_approximants`. Each vertex consists of a + :meth:`~DiscreteValuation.mac_lane_approximants`. Each vertex consists of a tuple ``(v,ef,p,coeffs,vals)`` where ``v`` is an approximant, i.e., a valuation, ef is a boolean, ``p`` is the parent of this vertex, and ``coeffs`` and ``vals`` are cached values. (Only ``v`` and ``ef`` are diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index 35c09b828ba..97486bac47d 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -16,6 +16,34 @@ sage: QQ.valuation(2).parent() Discrete pseudo-valuations on Rational Field +.. NOTE:: + + Note that many tests not only in this module do not create instances of + valuations directly since this gives the wrong inheritance structure on + the resulting objects:: + + sage: from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace + sage: from sage.rings.valuation.trivial_valuation import TrivialDiscretePseudoValuation + sage: H = DiscretePseudoValuationSpace(QQ) + sage: v = TrivialDiscretePseudoValuation(H) + sage: v._test_category() + Traceback (most recent call last): + ... + AssertionError: False is not true + + Instead, the valuations need to be created through the + ``__make_element_class__`` of the containing space:: + + sage: from sage.rings.valuation.trivial_valuation import TrivialDiscretePseudoValuation + sage: v = H.__make_element_class__(TrivialDiscretePseudoValuation)(H) + sage: v._test_category() + + The factories such as ``TrivialPseudoValuation`` provide the right + inheritance structure:: + + sage: v = valuations.TrivialPseudoValuation(QQ) + sage: v._test_category() + """ #***************************************************************************** # Copyright (C) 2016-2017 Julian Rüth @@ -251,7 +279,7 @@ class ElementMethods: True The methods will be provided even if the concrete type is not created - with :meth:`__make_element_class__`:: + with ``__make_element_class__``:: sage: from sage.rings.valuation.valuation import DiscretePseudoValuation sage: m = DiscretePseudoValuation(H) @@ -267,8 +295,8 @@ class ElementMethods: ... AssertionError: False is not true - Using :meth:`__make_element_class__`, makes your concrete valuation - inherit from this class:: + Using ``__make_element_class__``, makes your concrete valuation inherit + from this class:: sage: m = H.__make_element_class__(DiscretePseudoValuation)(H) sage: m._test_category() @@ -290,8 +318,8 @@ def is_discrete_pseudo_valuation(self): def is_discrete_valuation(self): r""" Return whether this valuation is a discrete valuation, i.e., - whether it is a :meth:`is_discrete_pseudo_valuation` that only - sends zero to `\infty`. + whether it is a :meth:`discrete pseudo valuation + ` that only sends zero to `\infty`. EXAMPLES:: @@ -398,7 +426,7 @@ def value_semigroup(self): r""" Return the value semigroup of this discrete pseudo-valuation, the additive subsemigroup of the rational numbers which is generated by - the valuations of the elements in :meth:`domain`. + the valuations of the elements in the domain. EXAMPLES: @@ -436,8 +464,8 @@ def value_semigroup(self): def element_with_valuation(self, s): r""" - Return an element in the :meth:`domain` of this valuation with - valuation ``s``. + Return an element in the domain of this valuation with valuation + ``s``. EXAMPLES:: @@ -538,8 +566,8 @@ def reduce(self, x): @abstract_method def lift(self, X): r""" - Return a lift of ``X`` in :meth:`domain` which reduces down to - ``X`` again via :meth:`reduce`. + Return a lift of ``X`` in the domain which reduces down to ``X`` + again via :meth:`reduce`. EXAMPLES:: @@ -602,7 +630,7 @@ def change_domain(self, ring): r""" Return this valuation over ``ring``. - Unlike :meth:`extension` or :meth:`reduction`, this might not be + Unlike :meth:`extension` or :meth:`restriction`, this might not be completely sane mathematically. It is essentially a conversion of this valuation into another space of valuations. From 6aa9f65cc0a6ceb579c5e7c95729e5d5b7ec4df2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 23 Jul 2017 06:37:14 +0000 Subject: [PATCH 335/740] Fixed documentation fixed broken links and reworded some confusing valuation() comments --- .../rings/function_field/function_field.py | 3 +-- src/sage/rings/integer_ring.pyx | 4 ++-- src/sage/rings/number_field/number_field.py | 20 +++++++++---------- src/sage/rings/number_field/order.py | 16 +++++++-------- src/sage/rings/padics/padic_generic.py | 4 ++-- src/sage/rings/rational_field.py | 4 ++-- 6 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 871d8e4bbc5..98244f2c7e3 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -550,11 +550,10 @@ def valuation(self, prime): EXAMPLES:: - sage: K. = FunctionField(QQ) - We create a valuation that correspond to a finite rational place of a function field:: + sage: K. = FunctionField(QQ) sage: v = K.valuation(1); v (x - 1)-adic valuation sage: v(x) diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx index 813ba50e715..a7b1826e73a 100644 --- a/src/sage/rings/integer_ring.pyx +++ b/src/sage/rings/integer_ring.pyx @@ -1527,8 +1527,8 @@ cdef class IntegerRing_class(PrincipalIdealDomain): .. SEEALSO:: - :meth:`sage.rings.number_field.order.Order.valuation`, - :meth:`sage.rings.rational_field.RationalField.valuation` + :meth:`Order.valuation() `, + :meth:`RationalField.valuation() ` """ from sage.rings.padics.padic_valuation import pAdicValuation diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index d7ad60ff402..6f3ffdef59c 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -6488,27 +6488,27 @@ def valuation(self, prime): EXAMPLES: - ``prime`` can be an integer that is completely ramified in ``R``:: + The valuation can be specified with an integer ``prime`` that is + completely ramified in ``R``:: sage: K. = NumberField(x^2 + 1) sage: K.valuation(2) 2-adic valuation - ``prime`` can be an integer that is unramified in ``R``:: + It can also be unramified in ``R``:: sage: K.valuation(3) 3-adic valuation - This is only supported if ``prime`` does not factor into - pairwise distinct factors:: + A ``prime`` that factors into pairwise distinct factors, results in an error:: sage: K.valuation(5) Traceback (most recent call last): ... ValueError: The valuation Gauss valuation induced by 5-adic valuation does not approximate a unique extension of 5-adic valuation with respect to x^2 + 1 - ``prime`` can also be specified by providing a valuation on a base ring - that has a unique extension:: + The valuation can also be selected by giving a valuation on the base + ring that extends uniquely:: sage: CyclotomicField(5).valuation(ZZ.valuation(5)) 5-adic valuation @@ -6522,7 +6522,7 @@ def valuation(self, prime): For a number field which is of the form `K[x]/(G)`, you can specify a valuation by providing a discrete pseudo-valuation on `K[x]` which sends - `G` to `\infty`. This lets us specify which extension of the 5-adic + `G` to infinity. This lets us specify which extension of the 5-adic valuation we care about in the above example:: sage: R. = QQ[] @@ -6539,7 +6539,7 @@ def valuation(self, prime): True The valuation ``prime`` does not need to send the defining polynomial `G` - to `\infty`. It is sufficient if it singles out one of the valuations on + to infinity. It is sufficient if it singles out one of the valuations on the number field. This is important if the prime only factors over the completion, i.e., if it is not possible to write down one of the factors within the number field:: @@ -6556,8 +6556,8 @@ def valuation(self, prime): .. SEEALSO:: - :meth:`sage.rings.number_field.order.Order.valuation`, - :meth:`sage.rings.padics.padic_generic.pAdicGeneric.valuation` + :meth:`Order.valuation() `, + :meth:`pAdicGeneric.valuation() ` """ from sage.rings.padics.padic_valuation import pAdicValuation diff --git a/src/sage/rings/number_field/order.py b/src/sage/rings/number_field/order.py index 5bcbe59f52e..b83a1ed5d7e 100644 --- a/src/sage/rings/number_field/order.py +++ b/src/sage/rings/number_field/order.py @@ -997,7 +997,8 @@ def valuation(self, p): EXAMPLES: - ``prime`` can be an integer that is completely ramified in the number field:: + The valuation can be specified with an integer ``prime`` that is + completely ramified or unramified:: sage: K. = NumberField(x^2 + 1) sage: O = K.order(2*a) @@ -1007,21 +1008,20 @@ def valuation(self, p): sage: GaussianIntegers().valuation(2) 2-adic valuation - ``prime`` can be an integer that is unramified:: + :: sage: GaussianIntegers().valuation(3) 3-adic valuation - This is only supported if ``prime`` does not factor into - pairwise distinct factors:: + A ``prime`` that factors into pairwise distinct factors, results in an error:: sage: GaussianIntegers().valuation(5) Traceback (most recent call last): ... ValueError: The valuation Gauss valuation induced by 5-adic valuation does not approximate a unique extension of 5-adic valuation with respect to x^2 + 1 - ``prime`` can also be specified by providing a valuation on a base ring - that has a unique extension:: + The valuation can also be selected by giving a valuation on the base + ring that extends uniquely:: sage: CyclotomicField(5).ring_of_integers().valuation(ZZ.valuation(5)) 5-adic valuation @@ -1045,8 +1045,8 @@ def valuation(self, p): .. SEEALSO:: - :meth:`sage.rings.number_field.number_field.NumberField.valuation`, - :meth:`sage.rings.padics.padic_generic.pAdicGeneric.valuation` + :meth:`NumberField_generic.valuation() `, + :meth:`pAdicGeneric.valuation() ` """ from sage.rings.padics.padic_valuation import pAdicValuation diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index afa571a6c59..1a1957b5b77 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -980,8 +980,8 @@ def valuation(self): .. SEEALSO:: - :meth:`sage.rings.number_field.number_field.NumberField.valuation`, - :meth:`sage.rings.number_field.order.Order.valuation` + :meth:`NumberField_generic.valuation() `, + :meth:`Order.valuation() ` """ from sage.rings.padics.padic_valuation import pAdicValuation diff --git a/src/sage/rings/rational_field.py b/src/sage/rings/rational_field.py index 46b5df255b0..51f866509e9 100644 --- a/src/sage/rings/rational_field.py +++ b/src/sage/rings/rational_field.py @@ -1304,8 +1304,8 @@ def valuation(self, p): .. SEEALSO:: - :meth:`sage.rings.number_field.number_field.NumberField.valuation`, - :meth:`sage.rings.integer_ring.IntegerRing.valuation` + :meth:`NumberField_generic.valuation() `, + :meth:`IntegerRing_class.valuation() ` """ from sage.rings.padics.padic_valuation import pAdicValuation From 2ca7a7c934463219690fd31d5ff4f4c3ee341c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 23 Jul 2017 06:59:18 +0000 Subject: [PATCH 336/740] fix pickling of valuations --- src/sage/rings/function_field/function_field_valuation.py | 2 +- src/sage/rings/padics/padic_valuation.py | 2 +- src/sage/rings/valuation/augmented_valuation.py | 2 +- src/sage/rings/valuation/gauss_valuation.py | 2 +- src/sage/rings/valuation/limit_valuation.py | 2 +- src/sage/rings/valuation/scaled_valuation.py | 2 +- src/sage/rings/valuation/trivial_valuation.py | 4 ++-- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/rings/function_field/function_field_valuation.py b/src/sage/rings/function_field/function_field_valuation.py index f890ae07e3d..cc56cb023cb 100644 --- a/src/sage/rings/function_field/function_field_valuation.py +++ b/src/sage/rings/function_field/function_field_valuation.py @@ -437,7 +437,7 @@ def create_object(self, version, key, **extra_args): raise NotImplementedError("valuation on %r from %r on %r"%(domain, valuation, valuation.domain())) -FunctionFieldValuation = FunctionFieldValuationFactory("FunctionFieldValuation") +FunctionFieldValuation = FunctionFieldValuationFactory("sage.rings.function_field.function_field_valuation.FunctionFieldValuation") class FunctionFieldValuation_base(DiscretePseudoValuation): diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index d10654ff082..ca19c9b96ac 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -373,7 +373,7 @@ def create_object(self, version, key, **extra_args): raise NotImplementedError return parent.__make_element_class__(pAdicFromLimitValuation)(parent, v, G.change_ring(R.base_ring()), approximants) -pAdicValuation = PadicValuationFactory("pAdicValuation") +pAdicValuation = PadicValuationFactory("sage.rings.padics.padic_valuation.pAdicValuation") class pAdicValuation_base(DiscreteValuation): r""" diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index 6c2ce99fe50..c2a2c91c466 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -248,7 +248,7 @@ def create_object(self, version, key): else: return parent.__make_element_class__(InfiniteAugmentedValuation)(parent, base_valuation, phi, mu) -AugmentedValuation = AugmentedValuationFactory("AugmentedValuation") +AugmentedValuation = AugmentedValuationFactory("sage.rings.valuation.augmented_valuation.AugmentedValuation") class AugmentedValuation_base(InductiveValuation): r""" diff --git a/src/sage/rings/valuation/gauss_valuation.py b/src/sage/rings/valuation/gauss_valuation.py index 7183b188dfd..2a5d48e0c69 100644 --- a/src/sage/rings/valuation/gauss_valuation.py +++ b/src/sage/rings/valuation/gauss_valuation.py @@ -124,7 +124,7 @@ def create_object(self, version, key, **extra_args): parent = DiscretePseudoValuationSpace(domain) return parent.__make_element_class__(GaussValuation_generic)(parent, v) -GaussValuation = GaussValuationFactory("GaussValuation") +GaussValuation = GaussValuationFactory("sage.rings.valuation.gauss_valuation.GaussValuation") class GaussValuation_generic(NonFinalInductiveValuation): """ diff --git a/src/sage/rings/valuation/limit_valuation.py b/src/sage/rings/valuation/limit_valuation.py index 61f80bd7404..8e5a34334a4 100644 --- a/src/sage/rings/valuation/limit_valuation.py +++ b/src/sage/rings/valuation/limit_valuation.py @@ -149,7 +149,7 @@ def create_object(self, version, key): parent = DiscretePseudoValuationSpace(base_valuation.domain()) return parent.__make_element_class__(MacLaneLimitValuation)(parent, base_valuation, G) -LimitValuation = LimitValuationFactory("LimitValuation") +LimitValuation = LimitValuationFactory("sage.rings.valuation.limit_valuation.LimitValuation") class LimitValuation_generic(DiscretePseudoValuation): r""" diff --git a/src/sage/rings/valuation/scaled_valuation.py b/src/sage/rings/valuation/scaled_valuation.py index 2987ff6f3f5..1341d59360a 100644 --- a/src/sage/rings/valuation/scaled_valuation.py +++ b/src/sage/rings/valuation/scaled_valuation.py @@ -83,7 +83,7 @@ def create_object(self, version, key): return parent.__make_element_class__(ScaledValuation_generic)(parent, base, s) -ScaledValuation = ScaledValuationFactory("ScaledValuation") +ScaledValuation = ScaledValuationFactory("sage.rings.valuation.scaled_valuation.ScaledValuation") class ScaledValuation_generic(DiscreteValuation): r""" diff --git a/src/sage/rings/valuation/trivial_valuation.py b/src/sage/rings/valuation/trivial_valuation.py index b4c876baa9b..1c348ebfe7d 100644 --- a/src/sage/rings/valuation/trivial_valuation.py +++ b/src/sage/rings/valuation/trivial_valuation.py @@ -403,6 +403,6 @@ def _ge_(self, other): return True return False -TrivialValuation = TrivialValuationFactory(TrivialDiscreteValuation, DiscretePseudoValuationSpace, "TrivialValuation") -TrivialPseudoValuation = TrivialValuationFactory(TrivialDiscretePseudoValuation, DiscretePseudoValuationSpace, "TrivialPseudoValuation") +TrivialValuation = TrivialValuationFactory(TrivialDiscreteValuation, DiscretePseudoValuationSpace, "sage.rings.valuation.trivial_valuation.TrivialValuation") +TrivialPseudoValuation = TrivialValuationFactory(TrivialDiscretePseudoValuation, DiscretePseudoValuationSpace, "sage.rings.valuation.trivial_valuation.TrivialPseudoValuation") From b105f147ecea40c3d76894df64f469bd8e93c002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 23 Jul 2017 07:29:26 +0000 Subject: [PATCH 337/740] Disable function field test does not work because there is no factorization in towers of function fields. Of course this is unneccesary. This is the defining polynomiala of the extension, so it better be irreducible... --- src/sage/rings/function_field/function_field.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 98244f2c7e3..7d4e092c917 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -627,10 +627,13 @@ def valuation(self, prime): sage: R. = K[] sage: L. = K.extension(w^3 - t) sage: N. = FunctionField(L) - sage: w = v.extension(N) - sage: w(x^3 - t) + sage: w = v.extension(N) # missing factorization, :trac:`16572` + Traceback (most recent call last): + ... + NotImplementedError + sage: w(x^3 - t) # not tested 1 - sage: w(x - w) + sage: w(x - w) # not tested 1/3 There are several ways to create valuations on extensions of rational From 50d609039556187be5a2d6e4e703bf6f199f9662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 23 Jul 2017 07:31:59 +0000 Subject: [PATCH 338/740] fix _relative_size output the old values were ignoring that we actually need two bits to store "2" not just one --- src/sage/rings/valuation/gauss_valuation.py | 2 +- src/sage/rings/valuation/limit_valuation.py | 2 +- src/sage/rings/valuation/mapped_valuation.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/valuation/gauss_valuation.py b/src/sage/rings/valuation/gauss_valuation.py index 2a5d48e0c69..5b705052ba4 100644 --- a/src/sage/rings/valuation/gauss_valuation.py +++ b/src/sage/rings/valuation/gauss_valuation.py @@ -712,7 +712,7 @@ def _relative_size(self, f): sage: R. = QQ[] sage: v = GaussValuation(R, QQ.valuation(2)) sage: v._relative_size(x + 1024) - 11 + 6 For performance reasons, only the constant coefficient is considered. (In common appplications, the constant coefficient shows the most diff --git a/src/sage/rings/valuation/limit_valuation.py b/src/sage/rings/valuation/limit_valuation.py index 8e5a34334a4..92c01ef0b42 100644 --- a/src/sage/rings/valuation/limit_valuation.py +++ b/src/sage/rings/valuation/limit_valuation.py @@ -840,7 +840,7 @@ def _relative_size(self, f): sage: v = QQ.valuation(2) sage: u = v.extension(L) sage: u._relative_size(1024*t + 1024) - 11 + 6 """ return self._initial_approximation._relative_size(f) diff --git a/src/sage/rings/valuation/mapped_valuation.py b/src/sage/rings/valuation/mapped_valuation.py index a767dc6a6ba..a6322919cc5 100644 --- a/src/sage/rings/valuation/mapped_valuation.py +++ b/src/sage/rings/valuation/mapped_valuation.py @@ -434,7 +434,7 @@ def _relative_size(self, x): sage: v = valuations.pAdicValuation(QQ, 2) sage: w = v.extension(L) sage: w._relative_size(1024*t + 1024) - 11 + 6 """ return self._base_valuation._relative_size(self._to_base_domain(x)) From 9bf9e7527f4702cf79cc59bb5cfb9ef924ef6ada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 23 Jul 2017 07:40:11 +0000 Subject: [PATCH 339/740] fill in missing doctest output --- src/sage/rings/valuation/developing_valuation.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/valuation/developing_valuation.py b/src/sage/rings/valuation/developing_valuation.py index 1c4fcab78ce..f585f748ee7 100644 --- a/src/sage/rings/valuation/developing_valuation.py +++ b/src/sage/rings/valuation/developing_valuation.py @@ -22,20 +22,23 @@ sage: f = x^2 + 2*x + 2 sage: list(v.coefficients(f)) + [2, 2, 1] Often only the first few coefficients are necessary in computations, so for performance reasons, coefficients are computed lazily:: sage: v.coefficients(f) + Another example of a :class:`DevelopingValuation` is an :mod:`augmented valuation `:: - sage: w = v.augmentation(x^2 + 2, 3) + sage: w = v.augmentation(x^2 + x + 1, 3) -Here, the expansion lists the remainders of repeated division by `x^2 + 2`:: +Here, the expansion lists the remainders of repeated division by `x^2 + x + 1`:: sage: list(w.coefficients(f)) + [x + 1, 1] """ #***************************************************************************** From 45e0e8f7cb9b0b6b8aaa1d3af8e4c4a761fb0cca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 23 Jul 2017 07:40:22 +0000 Subject: [PATCH 340/740] fix copy&paste error --- src/sage/rings/valuation/mapped_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/mapped_valuation.py b/src/sage/rings/valuation/mapped_valuation.py index a6322919cc5..e3f4f4f13a1 100644 --- a/src/sage/rings/valuation/mapped_valuation.py +++ b/src/sage/rings/valuation/mapped_valuation.py @@ -285,7 +285,7 @@ def _test_to_from_base_domain(self, **options): tester.assertEqual(x, self._from_base_domain(self._to_base_domain(x))) # note that the converse might not be true - def _test_to_from_base_domain(self, **options): + def _test_to_from_base_residue_ring(self, **options): r""" Check the correctness of :meth:`to_base_residue_ring` and :meth:`from_base_residue_ring`. From a4ac152e53b790477666998bacd0ed5951a436a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 23 Jul 2017 07:44:13 +0000 Subject: [PATCH 341/740] Call pAdicValuation instead of .valuation so it always accepts a paremeter (also for p-adics) --- src/sage/rings/padics/padic_valuation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index ca19c9b96ac..a27873f723d 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -718,14 +718,14 @@ def extensions(self, ring): domain_fraction_field = _fraction_field(self.domain()) if domain_fraction_field is not self.domain(): if domain_fraction_field.is_subring(ring): - return domain_fraction_field.valuation(self).extensions(ring) + return pAdicValuation(domain_fraction_field, self).extensions(ring) if self.domain().is_subring(ring): from sage.rings.polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing if is_PolynomialQuotientRing(ring): if is_PolynomialQuotientRing(self.domain()): if self.domain().modulus() == ring.modulus(): base_extensions = self._base_valuation.extensions(self._base_valuation.domain().change_ring(self._base_valuation.domain().base_ring().fraction_field())) - return [ring.valuation(base._initial_approximation) for base in base_extensions] + return [pAdicValuation(ring, base._initial_approximation) for base in base_extensions] if ring.base_ring() is self.domain(): from sage.categories.all import IntegralDomains if ring in IntegralDomains(): From 6de28680e68ec96f826efca092dc39b8e032158c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 23 Jul 2017 07:45:25 +0000 Subject: [PATCH 342/740] fix _relative_size output the old one did not take the bits of p into account --- src/sage/rings/valuation/augmented_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index c2a2c91c466..dae09e8877d 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -776,7 +776,7 @@ def _relative_size(self, f): sage: w._relative_size(x^2 + x + 1) 1 sage: w._relative_size(1048576*x^2 + 1048576*x + 1048576) - 21 + 11 """ return self._base_valuation._relative_size(f) From cf61932c0bd6aa4c94ddabd762b9889dbd0a8f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 23 Jul 2017 08:00:50 +0000 Subject: [PATCH 343/740] Fix doctest the old test did not make sense --- src/sage/rings/valuation/inductive_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index 6f12c862f96..7f96ea0db65 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -21,7 +21,7 @@ augmentation steps:: sage: w = v.augmentation(x, 1) - sage: w = w.augmentation(x^2 + 2, 3) + sage: w = w.augmentation(x^2 + 2*x + 4, 3) REFERENCES: From 13297381f160880706fb7d132165131a86a18890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 23 Jul 2017 08:01:05 +0000 Subject: [PATCH 344/740] Allow p-adic rings apparently there are problems but I could not find an example immediately where it doese not work. --- src/sage/rings/valuation/inductive_valuation.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index 7f96ea0db65..a816271e303 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -107,14 +107,19 @@ def equivalence_reciprocal(self, f, coefficients=None, valuations=None, check=Tr - ``check`` -- whether or not to check the validity of ``f`` (default: ``True``) + .. WARNING:: + + This method may not work over `p`-adic rings due to problems with + the xgcd implementation there. + EXAMPLES:: sage: R = Zp(3,5) sage: S. = R[] sage: v = GaussValuation(S) sage: f = 3*x + 2 - sage: h = v.equivalence_reciprocal(f); h # (needs xgcd for polynomials with p-adic coefficients) - 2 + 3 + 3^2 + 3^3 + 3^4 + O(3^5) + sage: h = v.equivalence_reciprocal(f); h + (2 + O(3^5)) sage: v.is_equivalent(f*h, 1) True @@ -161,11 +166,6 @@ def equivalence_reciprocal(self, f, coefficients=None, valuations=None, check=Tr """ f = self.domain().coerce(f) - from sage.categories.fields import Fields - if not self.domain().base_ring() in Fields(): - # the xgcd does in general not work, i.e., return 1, unless over a field - raise NotImplementedError("only implemented for polynomial rings over fields") - if check: if coefficients is None: coefficients = list(self.coefficients(f)) From 249bdc0743c0c79ffd6cec510db0d9c7bbec49e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 23 Jul 2017 08:01:34 +0000 Subject: [PATCH 345/740] Update doctest output the output has changed. It is still valid though. --- src/sage/rings/valuation/inductive_valuation.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index a816271e303..11296032ce0 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -1193,15 +1193,17 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi sage: K1.=NumberField(x^3 - 2) sage: K.=K1.galois_closure() sage: R.=K[] - sage: vp = Q.valuation(2) + sage: vp = QQ.valuation(2) sage: vp = vp.extension(K) sage: v0 = GaussValuation(R, vp) sage: G=x^36 + 36*x^35 + 630*x^34 + 7144*x^33 + 59055*x^32 + 379688*x^31 +1978792*x^30 + 8604440*x^29 + 31895428*x^28 + 102487784*x^27 + 289310720*x^26 + 725361352*x^25 + 1629938380*x^24 + 3307417800*x^23 + 6098786184*x^22+10273444280*x^21 + 15878121214*x^20 + 22596599536*x^19 + 29695703772*x^18 +36117601976*x^17 + 40722105266*x^16 + 42608585080*x^15 + 41395961848*x^14 +37344435656*x^13 + 31267160756*x^12 + 24271543640*x^11 + 17439809008*x^10 + 11571651608*x^9 + 7066815164*x^8 + 3953912472*x^7 + 2013737432*x^6 + 925014888*x^5 + 378067657*x^4 + 134716588*x^3 + 40441790*x^2 + 9532544*x + 1584151 sage: v1 = v0.mac_lane_step(G)[0] sage: V = v1.mac_lane_step(G) sage: v2 = V[0] - sage: v2.equivalence_decomposition(G) - (1/387420489) * (x^4 + 2*x^2 + alpha^4 + alpha^3 + 1)^3 * (x^4 + 2*x^2 + 1/2*alpha^4 + alpha^3 + 5*alpha + 1)^3 * (x^4 + 2*x^2 + 3/2*alpha^4 + alpha^3 + 5*alpha + 1)^3 + sage: F = v2.equivalence_decomposition(G); F + (x^4 + 2*x^2 + alpha^4 + alpha^3 + 1)^3 * (x^4 + 2*x^2 + 1/2*alpha^4 + alpha^3 + 5*alpha + 1)^3 * (x^4 + 2*x^2 + 3/2*alpha^4 + alpha^3 + 5*alpha + 1)^3 + sage: v2.is_equivalent(F.prod(), G) + True """ f = self.domain().coerce(f) From c1504712bc5da19d640de518ff81481eb2f80fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 23 Jul 2017 08:04:27 +0000 Subject: [PATCH 346/740] pAdicValuation is not in the global namespace anymore --- src/sage/rings/valuation/valuation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index deef54181a0..6a58adb6f70 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -1054,7 +1054,7 @@ class MacLaneApproximantNode(object): TESTS:: - sage: v = pAdicValuation(ZZ, 3) + sage: v = ZZ.valuation(3) sage: v.extension(GaussianIntegers()) # indirect doctest """ @@ -1063,7 +1063,7 @@ def __init__(self, valuation, parent, ef, principal_part_bound, coefficients, va TESTS:: sage: from sage.rings.valuation.valuation import MacLaneApproximantNode - sage: node = MacLaneApproximantNode(pAdicValuation(QQ, 2), None, 1, None, None, None) + sage: node = MacLaneApproximantNode(QQ.valuation(2), None, 1, None, None, None) sage: TestSuite(node).run() """ From a92f47fc79bd567b274421c9ede06b8d56538308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 24 Jul 2017 08:43:44 -0500 Subject: [PATCH 347/740] make sure that arguments to add_bigoh are correctly normalized --- src/sage/rings/padics/padic_valuation.py | 2 ++ src/sage/rings/valuation/inductive_valuation.py | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index ff2796ec54c..1f2a6eac325 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -1002,6 +1002,8 @@ def simplify(self, x, error=None, force=False): from sage.rings.all import infinity if error is infinity: return x + # we need to scale by the ramification index because p-adics use a + # different normalization normalized_error = (error / self.value_group().gen()).ceil() return x.add_bigoh(normalized_error + 1).lift_to_precision() diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index 8c795e0ef9d..d570655af98 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -828,8 +828,9 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a for j in range(i): if w(q[j]) < w(q[best]): best = j - # now add the right O() to phi in degree i - best - phi[i-best] = phi[i-best].add_bigoh(w(c)-w(q[best])) + # now add the right O() to phi in degree i - best (note that p-adics use a different normalization) + precision_absolute = (w(c) - w(q[best]))/w.value_group().gen() + phi[i-best] = phi[i-best].add_bigoh(precision_absolute) phi = G.parent()(phi) w = self._base_valuation.augmentation(phi, infinity, check=False) From aa1709d0153a3ad4377dabae0a339ae82f469dc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 24 Jul 2017 08:46:33 -0500 Subject: [PATCH 348/740] comment on stagnating degrees --- src/sage/rings/valuation/inductive_valuation.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index d570655af98..9e9ac2541e1 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -843,6 +843,12 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a new_valuations = [val - (j*slope if slope is not -infinity else (0 if j == 0 else -infinity)) for j,val in enumerate(w_valuations)] base = self if phi.degree() == base.phi().degree(): + # very frequently, the degree of the key polynomials + # stagnate for a bit while the valuation of the key + # polynomial is slowly increased. + # In this case, we can drop previous key polynomials + # of the same degree. (They have no influence on the + # phi-adic expansion.) assert new_mu > self(phi) if not base.is_gauss_valuation(): base = base._base_valuation From 276e4f72bea3e9ff3f59954fb22493d5d18dc7af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 24 Jul 2017 13:50:34 +0000 Subject: [PATCH 349/740] fix and fill in doctest output --- src/sage/rings/padics/padic_valuation.py | 14 ++++-- src/sage/rings/valuation/valuation.py | 54 +++++++++++++----------- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index a27873f723d..f4ab5779e2c 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -5,10 +5,15 @@ EXAMPLES:: sage: ZZ.valuation(2) + 2-adic valuation sage: QQ.valuation(3) - sage: GaussianIntegers().valuation(5) - sage: CyclotomicField(5).valuation(7) + 3-adic valuation + sage: CyclotomicField(5).valuation(5) + 5-adic valuation + sage: GaussianIntegers().valuation(7) + 7-adic valuation sage: Zp(11).valuation() + 11-adic valuation These valuations can then, e.g., be used to compute factorizations in the completion of a ring:: @@ -960,6 +965,7 @@ def shift(self, x, s): sage: S. = R.extension(y^3 - 2) sage: v = S.valuation() sage: v.shift(1, 5) + y^5 + O(y^60) """ from sage.rings.all import ZZ @@ -1115,9 +1121,9 @@ def _relative_size(self, x): sage: v = ZZ.valuation(2) sage: v._relative_size(2) - 2 + 1 sage: v._relative_size(2**20) - 21 + 11 """ x = self.domain().coerce(x) diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 6a58adb6f70..32ab4e9f72b 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -25,10 +25,11 @@ sage: K. = FunctionField(QQ) sage: K.valuation(x) - x-adic valuation + (x)-adic valuation sage: K.valuation(x^2 + 1) (x^2 + 1)-adic valuation sage: K.valuation(1/x) + Valuation at the infinite place :: @@ -36,11 +37,13 @@ sage: v = QQ.valuation(2) sage: w = GaussValuation(R, v) sage: w.augmentation(x, 3) + [ Gauss valuation induced by 2-adic valuation, v(x) = 3 ] We can also define discrete pseudo-valuations, i.e., discrete valuations that send more than just zero to infinity:: sage: w.augmentation(x, infinity) + [ Gauss valuation induced by 2-adic valuation, v(x) = +Infinity ] """ #***************************************************************************** @@ -528,25 +531,25 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: F = y^21 + x*y^20 + (x^3 + x + 1)*y^18 + (x^3 + 1)*y^17 + (x^4 + x)*y^16 + (x^7 + x^6 + x^3 + x + 1)*y^15 + x^7*y^14 + (x^8 + x^7 + x^6 + x^4 + x^3 + 1)*y^13 + (x^9 + x^8 + x^4 + 1)*y^12 + (x^11 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2)*y^11 + (x^12 + x^9 + x^8 + x^7 + x^5 + x^3 + x + 1)*y^10 + (x^14 + x^13 + x^10 + x^9 + x^8 + x^7 + x^6 + x^3 + x^2 + 1)*y^9 + (x^13 + x^9 + x^8 + x^6 + x^4 + x^3 + x)*y^8 + (x^16 + x^15 + x^13 + x^12 + x^11 + x^7 + x^3 + x)*y^7 + (x^17 + x^16 + x^13 + x^9 + x^8 + x)*y^6 + (x^17 + x^16 + x^12 + x^7 + x^5 + x^2 + x + 1)*y^5 + (x^19 + x^16 + x^15 + x^12 + x^6 + x^5 + x^3 + 1)*y^4 + (x^18 + x^15 + x^12 + x^10 + x^9 + x^7 + x^4 + x)*y^3 + (x^22 + x^21 + x^20 + x^18 + x^13 + x^12 + x^9 + x^8 + x^7 + x^5 + x^4 + x^3)*y^2 + (x^23 + x^22 + x^20 + x^17 + x^15 + x^14 + x^12 + x^9)*y + x^25 + x^23 + x^19 + x^17 + x^15 + x^13 + x^11 + x^5 sage: x = K._ring.gen() sage: v0 = K.valuation(GaussValuation(K._ring, valuations.TrivialValuation(k)).augmentation(x,1)) - sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assumes squarefree for speed - [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x + 1) = 3/2 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y) = 4/3, v(y^3 + x^4) = 13/3 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y + x) = 2 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x) = 1 ], v(y^15 + y^13 + (x + 1)*y^12 + x*y^11 + (x + 1)*y^10 + y^9 + y^8 + x*y^6 + x*y^5 + y^4 + y^3 + y^2 + (x + 1)*y + x + 1) = 2 ]] + sage: sorted(v0.mac_lane_approximants(F, assume_squarefree=True), key=str) # assumes squarefree for speed + [[ Gauss valuation induced by (x)-adic valuation, v(y + x + 1) = 3/2 ], + [ Gauss valuation induced by (x)-adic valuation, v(y) = 1 ], + [ Gauss valuation induced by (x)-adic valuation, v(y) = 4/3 ], + [ Gauss valuation induced by (x)-adic valuation, v(y^15 + y^13 + y^12 + y^10 + y^9 + y^8 + y^4 + y^3 + y^2 + y + 1) = 1 ]] sage: v0 = K.valuation(GaussValuation(K._ring, valuations.TrivialValuation(k)).augmentation(x+1,1)) - sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assumes squarefree for speed - [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y) = 7/2, v(y^2 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1) = 15/2 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y + x^2 + 1) = 7/2 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y) = 3/4, v(y^4 + x^3 + x^2 + x + 1) = 15/4 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x + 1) = 1 ], v(y^13 + x*y^12 + y^10 + (x + 1)*y^9 + (x + 1)*y^8 + x*y^7 + x*y^6 + (x + 1)*y^4 + y^3 + (x + 1)*y^2 + 1) = 2 ]] - sage: v0 = FunctionFieldValuation(K, GaussValuation(K._ring, TrivialValuation(k)).augmentation(x^3+x^2+1,1)) - sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assumes squarefree for speed - [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y + x^3 + x^2 + x) = 2, v(y^2 + (x^6 + x^4 + 1)*y + x^14 + x^10 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2 + x) = 5 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^2 + (x^7 + x^5 + x^4 + x^3 + x^2 + x)*y + x^7 + x^5 + x + 1) = 3 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^3 + (x^8 + x^5 + x^4 + x^3 + x + 1)*y^2 + (x^7 + x^6 + x^5)*y + x^8 + x^5 + x^4 + x^3 + 1) = 3 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^3 + (x^8 + x^4 + x^3 + x + 1)*y^2 + (x^4 + x^3 + 1)*y + x^8 + x^7 + x^4 + x + 1) = 3 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^4 + (x^8 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1)*y^3 + (x^8 + x^5 + x^4 + x^3 + x^2 + x + 1)*y^2 + (x^8 + x^7 + x^6 + x^5 + x^3 + x^2 + 1)*y + x^8 + x^7 + x^6 + x^5 + x^3 + 1) = 3 ], - [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by Trivial valuation, v(x^3 + x^2 + 1) = 1 ], v(y^7 + (x^8 + x^5 + x^4 + x)*y^6 + (x^7 + 1)*y^5 + (x^4 + x^2)*y^4 + (x^8 + x^3 + x + 1)*y^3 + (x^7 + x^6 + x^4 + x^2 + x + 1)*y^2 + (x^8 + x^7 + x^5 + x^3 + 1)*y + x^7 + x^6 + x^5 + x^4 + x^3 + x^2) = 3 ]] + sage: sorted(v0.mac_lane_approximants(F, assume_squarefree=True), key=str) # assumes squarefree for speed + [[ Gauss valuation induced by (x + 1)-adic valuation, v(y + x^2 + 1) = 7/2 ], + [ Gauss valuation induced by (x + 1)-adic valuation, v(y) = 3/4 ], + [ Gauss valuation induced by (x + 1)-adic valuation, v(y) = 7/2 ], + [ Gauss valuation induced by (x + 1)-adic valuation, v(y^13 + y^12 + y^10 + y^7 + y^6 + y^3 + 1) = 1 ]] + sage: v0 = valuations.FunctionFieldValuation(K, GaussValuation(K._ring, valuations.TrivialValuation(k)).augmentation(x^3+x^2+1,1)) + sage: sorted(v0.mac_lane_approximants(F, assume_squarefree=True), key=str) # assumes squarefree for speed + [[ Gauss valuation induced by (x^3 + x^2 + 1)-adic valuation, v(y + x^3 + x^2 + x) = 2, v(y^2 + (x^6 + x^4 + 1)*y + x^14 + x^10 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2 + x) = 5 ], + [ Gauss valuation induced by (x^3 + x^2 + 1)-adic valuation, v(y^2 + (x^2 + x)*y + 1) = 1 ], + [ Gauss valuation induced by (x^3 + x^2 + 1)-adic valuation, v(y^3 + (x + 1)*y^2 + (x + 1)*y + x^2 + x + 1) = 1 ], + [ Gauss valuation induced by (x^3 + x^2 + 1)-adic valuation, v(y^3 + x^2*y + x) = 1 ], + [ Gauss valuation induced by (x^3 + x^2 + 1)-adic valuation, v(y^4 + (x + 1)*y^3 + x^2*y^2 + (x^2 + x)*y + x) = 1 ], + [ Gauss valuation induced by (x^3 + x^2 + 1)-adic valuation, v(y^7 + x^2*y^6 + (x + 1)*y^4 + x^2*y^3 + (x^2 + x + 1)*y^2 + x^2*y + x) = 1 ]] Cases with trivial residue field extensions:: @@ -555,7 +558,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: F = y^2 - x^2 - x^3 - 3 sage: v0 = GaussValuation(K._ring, QQ.valuation(3)) sage: v1 = v0.augmentation(K._ring.gen(),1/3) - sage: mu0 = FunctionFieldValuation(K, v1) + sage: mu0 = valuations.FunctionFieldValuation(K, v1) sage: sorted(mu0.mac_lane_approximants(F), key=str) [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + 2*x) = 2/3 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + x) = 2/3 ]] @@ -623,9 +626,9 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru Note how the latter give a better approximation to the factors of `x^2 + 1`:: sage: v1.phi() * v2.phi() - G - (5 + O(5^4))*x + 5 + O(5^4) + (O(5^4))*x^2 + (5 + O(5^4))*x + (5 + O(5^4)) sage: w1.phi() * w2.phi() - G - (5^3 + O(5^4))*x + 5^3 + O(5^4) + (O(5^4))*x^2 + (5^2 + O(5^4))*x + (5^3 + O(5^4)) In this example, the process stops with a factorization of `x^2 + 1`:: @@ -648,14 +651,14 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru Initial versions ran into problems with the trivial residue field extensions in this case:: - sage: K = Qp(3,20) + sage: K = Qp(3, 20, print_mode='digits') sage: R. = K[] sage: alpha = T^3/4 sage: G = 3^3*T^3*(alpha^4 - alpha)^2 - (4*alpha^3 - 1)^3 sage: G = G/G.leading_coefficient() sage: K.valuation().mac_lane_approximants(G) - [[ Gauss valuation induced by 3-adic valuation, v((1 + O(3^20))*T + (2 + O(3^20))) = 1/9, v((1 + O(3^20))*T^9 + (2*3 + 2*3^2 + O(3^21))*T^8 + (3 + 3^5 + O(3^21))*T^7 + (2*3 + 2*3^2 + 3^3 + 2*3^4 + 2*3^5 + 3^6 + O(3^21))*T^6 + (2*3 + 2*3^2 + 3^4 + 3^6 + 2*3^7 + O(3^21))*T^5 + (3 + 3^2 + 3^3 + 2*3^6 + 2*3^7 + 3^8 + O(3^21))*T^4 + (2*3 + 2*3^2 + 3^3 + 2*3^5 + 2*3^6 + 2*3^7 + 2*3^8 + O(3^21))*T^3 + (2*3 + 2*3^2 + 3^3 + 2*3^4 + 3^5 + 2*3^6 + 2*3^7 + 2*3^8 + O(3^21))*T^2 + (3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^7 + 3^8 + O(3^21))*T + (2 + 2*3 + 2*3^2 + 2*3^4 + 2*3^5 + 3^7 + O(3^20))) = 55/27 ]] + [[ Gauss valuation induced by 3-adic valuation, v((...1)*T + (...2)) = 1/9, v((...1)*T^9 + (...20)*T^8 + (...210)*T^7 + (...20)*T^6 + (...20)*T^5 + (...10)*T^4 + (...220)*T^3 + (...20)*T^2 + (...110)*T + (...122)) = 55/27 ]] A similar example:: @@ -988,7 +991,7 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No In this case, ``f`` factors into degrees 1, 2, and 5 over a totally ramified extension:: sage: R = Zp(5, 50) - sage: R. = R[] + sage: S. = R[] sage: R. = R.extension(w^3 + 5) sage: S. = R[] sage: f = (x^3 + 5)*(x^5 + w) + 625 @@ -1056,6 +1059,7 @@ class MacLaneApproximantNode(object): sage: v = ZZ.valuation(3) sage: v.extension(GaussianIntegers()) # indirect doctest + 3-adic valuation """ def __init__(self, valuation, parent, ef, principal_part_bound, coefficients, valuations): From 9b03cf8e3beae7c4585bec5e6f074b9f8c4fab51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 24 Jul 2017 13:50:44 +0000 Subject: [PATCH 350/740] compute fraction field only when necessary --- src/sage/rings/padics/padic_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index f4ab5779e2c..057d647ffa4 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -357,7 +357,6 @@ def create_object(self, version, key, **extra_args): from sage.rings.polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing from sage.rings.number_field.number_field import is_NumberField R = key[0] - K = R.fraction_field() parent = DiscretePseudoValuationSpace(R) if isinstance(R, pAdicGeneric): assert(len(key)==1) @@ -370,6 +369,7 @@ def create_object(self, version, key, **extra_args): v = key[1] approximants = extra_args['approximants'] parent = DiscretePseudoValuationSpace(R) + K = R.fraction_field() if is_NumberField(K): G = K.relative_polynomial() elif is_PolynomialQuotientRing(R): From a886c4214deda809cf97f294ffdab82b9c2bcb4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 24 Jul 2017 13:50:52 +0000 Subject: [PATCH 351/740] fix pickling of nodes --- src/sage/rings/valuation/valuation.py | 39 +++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 32ab4e9f72b..bfaf47d5a33 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -1078,3 +1078,42 @@ def __init__(self, valuation, parent, ef, principal_part_bound, coefficients, va self.coefficients = coefficients self.valuations = valuations self.forced_leaf = False + + def __eq__(self, other): + r""" + Return whether this node is equal to ``other``. + + EXAMPLES:: + + sage: from sage.rings.valuation.valuation import MacLaneApproximantNode + sage: n = MacLaneApproximantNode(QQ.valuation(2), None, 1, None, None, None) + sage: m = MacLaneApproximantNode(QQ.valuation(3), None, 1, None, None, None) + sage: n == m + False + sage: n == n + True + + """ + if type(self) != type(other): + return False + return (self.valuation, self.parent, self.ef, self.principal_part_bound, self.coefficients, self.valuations, self.forced_leaf) == (other.valuation, other.parent, other.ef, other.principal_part_bound, other.coefficients, other.valuations, other.forced_leaf) + + def __ne__(self, other): + r""" + Return whether this node is not equal to ``other``. + + EXAMPLES:: + + sage: from sage.rings.valuation.valuation import MacLaneApproximantNode + sage: n = MacLaneApproximantNode(QQ.valuation(2), None, 1, None, None, None) + sage: m = MacLaneApproximantNode(QQ.valuation(3), None, 1, None, None, None) + sage: n != m + True + sage: n != n + False + + """ + return not (self == other) + + # mutable object - not hashable + __hash__ = None From 282c95ea845009b91e5db57e9b6f76de562f64ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 24 Jul 2017 13:53:21 +0000 Subject: [PATCH 352/740] Remove "not slopes" I believe that this was a hack to work around some precision problems. It should not be in the code I think. --- .../rings/valuation/inductive_valuation.py | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index 05809fb57d7..afe8f4ad73b 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -813,30 +813,6 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a slopes = [-infinity] + slopes multiplicities[-infinity] = 1 - if not slopes: - q,r = G.quo_rem(phi) - assert not r.is_zero() - phi = phi.coefficients(sparse=False) - for i,c in enumerate(r.coefficients(sparse=False)): - if not c.is_zero(): - v = w(c) - # for a correct result we need to add O(pi^v) in degree i - # we try to find the coefficient of phi where such an - # error can be introduced without losing much absolute - # precision on phi - best = i - for j in range(i): - if w(q[j]) < w(q[best]): - best = j - # now add the right O() to phi in degree i - best (note that p-adics use a different normalization) - precision_absolute = (w(c) - w(q[best]))/w.value_group().gen() - phi[i-best] = phi[i-best].add_bigoh(precision_absolute) - - phi = G.parent()(phi) - w = self._base_valuation.augmentation(phi, infinity, check=False) - ret.append((w, phi.degree(), principal_part_bound, None, None)) - continue - for i, slope in enumerate(slopes): verbose("Slope = %s"%slope, level=12) new_mu = old_mu - slope From 7b44fb315dca1289d6a9a856dc2dfc7210f2704e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 5 Aug 2017 01:14:27 +0800 Subject: [PATCH 353/740] A test case that failed in an older version reported by Stefan Wewers --- src/sage/rings/padics/padic_valuation.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 951c4d78b73..f09bf934127 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -712,6 +712,13 @@ def extensions(self, ring): sage: QQ.valuation(2).extensions(L) [2-adic valuation] + A case where there was at some point an internal error in the + approximants code:: + + sage: R. = QQ[] + sage: L. = NumberField(x^4 + 2*x^3 + 2*x^2 + 8) + sage: QQ.valuation(2).extensions(L) + """ if self.domain() is ring: return [self] From 28fbf356a3d48dd93c88be70dfcaf5092e064519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 6 Aug 2017 21:10:49 +0800 Subject: [PATCH 354/740] Check that extensions are computed correctly in towers of number fields A problem reported by Stefan Wewers. --- src/sage/rings/padics/padic_valuation.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 6c6c61cd750..b3169dd63f9 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -724,6 +724,15 @@ def extensions(self, ring): sage: L. = NumberField(x^4 + 2*x^3 + 2*x^2 + 8) sage: QQ.valuation(2).extensions(L) + A case where the extension was incorrect at some point:: + + sage: v = QQ.valuation(2) + sage: L. = NumberField(x^2 + 2) + sage: M. = L.extension(x^2 + 1) + sage: w = v.extension(L).extension(M) + sage: w(w.uniformizer()) + 1/2 + """ if self.domain() is ring: return [self] From 12211251d3efd3bc5d54d4812792f1b41663b6ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 10 Aug 2017 17:14:36 +0800 Subject: [PATCH 355/740] remove TABs --- src/sage/rings/padics/padic_valuation.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index b3169dd63f9..a252e28c05a 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -720,18 +720,18 @@ def extensions(self, ring): A case where there was at some point an internal error in the approximants code:: - sage: R. = QQ[] - sage: L. = NumberField(x^4 + 2*x^3 + 2*x^2 + 8) - sage: QQ.valuation(2).extensions(L) + sage: R. = QQ[] + sage: L. = NumberField(x^4 + 2*x^3 + 2*x^2 + 8) + sage: QQ.valuation(2).extensions(L) A case where the extension was incorrect at some point:: - sage: v = QQ.valuation(2) - sage: L. = NumberField(x^2 + 2) - sage: M. = L.extension(x^2 + 1) - sage: w = v.extension(L).extension(M) - sage: w(w.uniformizer()) - 1/2 + sage: v = QQ.valuation(2) + sage: L. = NumberField(x^2 + 2) + sage: M. = L.extension(x^2 + 1) + sage: w = v.extension(L).extension(M) + sage: w(w.uniformizer()) + 1/2 """ if self.domain() is ring: From 7660eab0134f488f18572d2b4a2a856d43641f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 12 Aug 2017 23:19:43 +0800 Subject: [PATCH 356/740] Mapped valuations should know whether they are discrete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit a bug reported by Stefan Wewers. It would be really nice to do this wrapping more automagically… --- .../function_field/function_field_valuation.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/sage/rings/function_field/function_field_valuation.py b/src/sage/rings/function_field/function_field_valuation.py index cc56cb023cb..e58a96311e5 100644 --- a/src/sage/rings/function_field/function_field_valuation.py +++ b/src/sage/rings/function_field/function_field_valuation.py @@ -1070,6 +1070,23 @@ def _repr_(self): to_base = self._to_base._repr_defn().replace('\n', ', ') return "%r (in %r after %s)"%(self._base_valuation, self._base_valuation.domain(), to_base) + def is_discrete_valuation(self): + r""" + Return whether this is a discrete valuation. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x^4 - 1) + sage: v = K.valuation(1/x) + sage: w0,w1 = v.extensions(L) + sage: w0.is_discrete_valuation() + True + + """ + return self._base_valuation.is_discrete_valuation() + class RationalFunctionFieldMappedValuation(FunctionFieldMappedValuation_base, RationalFunctionFieldValuation_base): r""" From 4e46d34ceafa80a69c1d7a1e94d64b3a8b253982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 14 Aug 2017 21:01:59 +0800 Subject: [PATCH 357/740] Added another failing case reported by Stefan Wewers --- .../rings/function_field/function_field_valuation.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sage/rings/function_field/function_field_valuation.py b/src/sage/rings/function_field/function_field_valuation.py index e58a96311e5..78cdd34bf49 100644 --- a/src/sage/rings/function_field/function_field_valuation.py +++ b/src/sage/rings/function_field/function_field_valuation.py @@ -507,6 +507,18 @@ def extensions(self, L): ... NotImplementedError + A case that caused some trouble at some point:: + + sage: R. = QQ[] + sage: v = GaussValuation(R, QQ.valuation(2)) + + sage: K. = FunctionField(QQ) + sage: v = K.valuation(v) + + sage: R. = K[] + sage: L. = K.extension(y^3 - x^4 - 1) + sage: v.extensions(L) + """ K = self.domain() from sage.categories.function_fields import FunctionFields From 51f7ed09ea118b0d05c6bc124c159b321c89a90c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 21 Aug 2017 12:52:20 +0800 Subject: [PATCH 358/740] Add test case provided by Stefan Wewers --- src/sage/rings/valuation/valuation.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index bfaf47d5a33..ccc16d81fee 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -714,6 +714,13 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: v.mac_lane_approximants(f) # is_squarefree() is not yet implemented on this ring sage: v.mac_lane_approximants(f, assume_squarefree=True) + A case that triggered an assertion at some point:: + + sage: v = QQ.valuation(3) + sage: R. = QQ[] + sage: f = x^36 + 60552000*x^33 + 268157412*x^30 + 173881701*x^27 + 266324841*x^24 + 83125683*x^21 + 111803814*x^18 + 31925826*x^15 + 205726716*x^12 +17990262*x^9 + 351459648*x^6 + 127014399*x^3 + 359254116 + sage: v.mac_lane_approximants(f) + """ R = G.parent() if R.base_ring() is not self.domain(): From 84093c4bc19d2aa7e353dfa2b2436a5bf56b96b8 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 19 Sep 2017 23:41:28 +0200 Subject: [PATCH 359/740] fix minor formatting issues --- src/sage/combinat/tableau_shifted_primed.py | 382 ++++++++++---------- 1 file changed, 201 insertions(+), 181 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index d7f9f9c12b0..75311456ca3 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -20,15 +20,15 @@ @add_metaclass(InheritComparisonClasscallMetaclass) class ShiftedPrimedTableau(ClonableArray): r""" - A shifted primed tableau with primed elements stored as half-integers + A shifted primed tableau with primed elements stored as half-integers. - A primed tableau is a shifted tableau on the alphabet - X' = {1' < 1 < 2' < 2 <...< n' < n} such that - 1). the entries are weakly increasing along rows and columns - 2). a row can't have two repeated primed elements, and a column - can't have two repeated non-primed elements - 3). there are only non-primed elements on the main diagonal + A primed tableau is a shifted tableau on the alphabet + X' = {1' < 1 < 2' < 2 <...< n' < n} such that + 1. the entries are weakly increasing along rows and columns + 2. a row can't have two repeated primed elements, and a column + can't have two repeated non-primed elements + 3. there are only non-primed elements on the main diagonal EXAMPLES:: @@ -46,12 +46,12 @@ class ShiftedPrimedTableau(ClonableArray): ... ValueError: [[1, 2, 2.50000000000000, 3], [0, 2, 2.50000000000000]] is not an element of Shifted Primed Tableaux """ - + @staticmethod def __classcall_private__(cls, T): r""" - This ensures that a shifted tableau is only ever constructed - as an ``element_class`` call of an appropriate parent. + Ensure that a shifted tableau is only ever constructed as an + ``element_class`` call of an appropriate parent. EXAMPLES:: @@ -65,16 +65,16 @@ def __classcall_private__(cls, T): True """ - + if isinstance(T, cls): return T - + return ShiftedPrimedTableaux()(T) - + def __init__(self,parent, T): r""" - Preprocessing of list t and initializing a shifted tableau. + Initialize a shifted tableau. TESTS:: @@ -101,28 +101,28 @@ def __init__(self,parent, T): ... TypeError: 'tuple' object does not support item assignment """ - + if isinstance(T,ShiftedPrimedTableau): ClonableArray.__init__(self, parent, T) return - + if not isinstance(T, list): try: t_ = list(T) except TypeError: t_ = [T] else: - t_ = T - + t_ = T + if not all(isinstance(row, (list,tuple,np.ndarray)) for row in t_): t_ = [t_] - + t = [] # Preprocessing list t for primes and other symbols for row in t_: row_new = [] for element in row: - + if isinstance(element,str): if element[-1] == "'" and element[:-1].isdigit() == True: # Check if an element has "'" at the end row_new.append(float(float(element[:-1]) - .5)) @@ -136,10 +136,10 @@ def __init__(self,parent, T): continue else: raise ValueError("all numbers must be half-integers") - + except (TypeError,ValueError): raise ValueError("primed elements should be half-integers or have symbols p or ' at the end") - + t.append(row_new) @@ -168,7 +168,7 @@ def __eq__(self, other): INPUT: - ``other`` -- the element that ``self`` is compared to + - ``other`` -- the element that ``self`` is compared to OUTPUT: @@ -187,7 +187,7 @@ def __eq__(self, other): return (list(self) == list(Tab)) - + def to_matrix(self): """ Return a 2-dimensional numpy.array representation of a shifted tableau @@ -209,7 +209,7 @@ def to_matrix(self): array.append([0]*i + list(self[i]) + [0]*(m-i-len(self[i]))) array = np.array(array, dtype = 'float') return array - + def check(self): """ Check that ``self`` is a valid primed tableaux. @@ -246,20 +246,20 @@ def _repr_(self): [(1.0, 1.5, 2.0, 2.0), (2.0, 2.5)] """ return repr([tuple(_) for _ in self]) - + def pp(self): """ Print out a nice version of ``self``. - + EXAMPLES:: sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t.pp() - 1 2' 2 2 + 1 2' 2 2 2 3' sage: t = ShiftedPrimedTableau([[10,'11p',11,11],[11,'12']]) sage: t.pp() - 10 11' 11 11 + 10 11' 11 11 11 12 """ if [item for sublist in self for item in sublist] == []: @@ -306,7 +306,7 @@ def _latex_(self): else: num_list.append(str(int(let+0.5))+"'") L.append(num_list) - + return tex_from_array(L) def max_element(self): @@ -314,7 +314,7 @@ def max_element(self): Return the maximum element in the primed tableaux ``self``, rounded up. EXAMPLE:: - + sage: Tab = ShiftedPrimedTableau([(1,1,1.5,2.5),(2,2)]) sage: Tab.max_element() 3 @@ -325,19 +325,19 @@ def max_element(self): flat = [item for sublist in self for item in sublist] return int(round(max(flat))) - + def shape(self): """ Return the shape of the underlying partition of ``self`` in list format. - + EXAMPLES:: - + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t.shape() [4, 2] """ return ([len(row) for row in self]) - + def __call__(self,*cell): """ Function call of ``self``. @@ -349,8 +349,8 @@ def __call__(self,*cell): OUTPUT: - - the element in the corresponding cell. if the element is primed, - returnes half-integer value. + - the element in the corresponding cell. if the element is primed, + returns half-integer value. EXAMPLES:: @@ -360,12 +360,12 @@ def __call__(self,*cell): sage: t((1,2)) Traceback (most recent call last): ... - IndexError: the cell (1,2) is not contained in the shifted tableau + IndexError: the cell (1,2) is not contained in the shifted tableau [(1.0, 1.5, 2.0, 2.0), (2.0, 2.5)] sage: t((1,1)) 2.5 """ - + try: i,j = cell except ValueError: @@ -376,15 +376,16 @@ def __call__(self,*cell): except IndexError: raise IndexError("the cell (%d,%d) is not contained in the shifted tableau \n%s"%(i, j, repr(self))) - + def weight(self): """ - Returnes the weight of ``self`` in tuple format. + Return the weight of ``self`` as a tuple. + The weight of a shifted primed tableau is defined to be the vector with i-th component equal to the number of letters i and i' in the tableau. - + EXAMPLE:: - + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t.weight() (1, 4, 1) @@ -399,20 +400,27 @@ def weight(self): def reading_word_with_positions(self): """ - Returnes the reading word of ``self`` together with positions of the corresponding + Return the reading word of ``self`` together with positions of the corresponding letters in ``self``. + The reading word of a shifted primed tableau is constructed as follows: - (1) List all primed letters in the tableau, column by column, in decreasing order - within each column, moving from the rightmost column to the left, and with all the - primes removed (i.e. all letters are increased by half a unit). - (2) Then list all unprimed elements, row by row, in increasing order within each row, - moving from the bottommost row to the top. + + 1. List all primed letters in the tableau, column by + column, in decreasing order within each column, moving + from the rightmost column to the left, and with all + the primes removed (i.e. all letters are increased by + half a unit). + + 2. Then list all unprimed elements, row by row, in + increasing order within each row, moving from the + bottommost row to the top. EXAMPLE:: sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t.reading_word_with_positions() [((1, 2), 3), ((0, 1), 2), ((1, 1), 2), ((0, 0), 1), ((0, 2), 2), ((0, 3), 2)] + """ mat = self.to_matrix() list_with_positions = [] @@ -426,13 +434,19 @@ def reading_word_with_positions(self): def reading_word(self): """ - Returnes the reading word of ``self``. + Return the reading word of ``self``. + The reading word of a shifted primed tableau is constructed as follows: - (1) List all primed letters in the tableau, column by column, in decreasing order - within each column, moving from the rightmost column to the left, and with all the - primes removed (i.e. all letters are increased by half a unit). - (2) Then list all unprimed elements, row by row, in increasing order within each row, - moving from the bottommost row to the top. + + 1. List all primed letters in the tableau, column by + column, in decreasing order within each column, moving + from the rightmost column to the left, and with all + the primes removed (i.e. all letters are increased by + half a unit). + + 2. Then list all unprimed elements, row by row, in + increasing order within each row, moving from the + bottommost row to the top. EXAMPLE:: @@ -445,51 +459,51 @@ def reading_word(self): def f(self, ind): """ - A function to compute the action of the crystal operator $f_i$ on a Shifted Primed Tableau + Compute the action of the crystal operator `f_i` on a Shifted Primed Tableau using cases from the paper [GPS.17]. - INPUT: + INPUT:: + + - ``self`` -- shifted primed tableau + - ``ind`` -- index of the crystal operator `f_i`. - self -- shifted primed tableau - ind -- index of the crystal operator $f_i$. - OUTPUT: - + Primed tableau or 'None'. EXAMPLES:: - + sage: t = ShiftedPrimedTableau([[1,1,1,1,2.5],[2,2,2,2.5],[3,3]]) sage: t.pp() - 1 1 1 1 3' - 2 2 2 3' - 3 3 + 1 1 1 1 3' + 2 2 2 3' + 3 3 sage: s = t.f(2) sage: print(s) None sage: t = ShiftedPrimedTableau([[1,1,1,1.5,2.5],[0,2,2,3,3],[0,0,3,4]]) sage: t.pp() - 1 1 1 2' 3' - 2 2 3 3 - 3 4 + 1 1 1 2' 3' + 2 2 3 3 + 3 4 sage: s = t.f(2) sage: s.pp() - 1 1 1 2' 3' - 2 3' 3 3 - 3 4 + 1 1 1 2' 3' + 2 3' 3 3 + 3 4 """ if self is None: return None - + T = self.to_matrix() - + read_word = self.reading_word_with_positions() read_word = [num for num in read_word if num[1]==ind or num[1]==ind+1] element_to_change = None count=0 - + for element in read_word: if element[1] == ind+1: count += 1 @@ -546,44 +560,44 @@ def f(self, ind): def e(self,ind): """ - A function to compute an action of the crystal operator $e_i$ on a Primed Tableau + Compute the action of the crystal operator `e_i` on a Shifted Primed Tableau using cases from the paper [GPS.17]. INPUT: - self -- shifted primed tableau - ind -- index of the crystal operator $e_i$. - + - ``self`` -- shifted primed tableau + - ``ind`` -- index of the crystal operator `e_i`. + OUTPUT: - + Primed tableau or 'None'. - + EXAMPLES:: - + sage: t = ShiftedPrimedTableau([[1,1,1,1.5,2.5],[0,2,"3p",3,3],[0,0,3,4]]) sage: t.pp() - 1 1 1 2' 3' - 2 3' 3 3 - 3 4 + 1 1 1 2' 3' + 2 3' 3 3 + 3 4 sage: s = t.e(2) sage: s.pp() - 1 1 1 2' 3' - 2 2 3 3 - 3 4 + 1 1 1 2' 3' + 2 2 3 3 + 3 4 sage: t == s.f(2) True - + """ if self is None: return None T = self.to_matrix() - + read_word = self.reading_word_with_positions() read_word = [num for num in read_word if num[1]==ind or num[1]==ind+1] element_to_change = None count=0 - + for element in read_word[::-1]: if element[1] == ind: count += 1 @@ -635,21 +649,22 @@ def e(self,ind): def epsilon(self,i): r""" - Compute value of the crystal function $\varepsilon_i$ applied to a shifted primed tableau ``self``. - The function $\varepsilon_i$ is defined to be the maximal number of operators $e_i$ one can apply to - ``self`` before vanishing into ``None``. + Compute the value of the crystal function `\epsilon_i` applied to + a shifted primed tableau ``self``. The function `\epsilon_i` + is defined to be the maximal number of operators `e_i` one + can apply to ``self`` before vanishing into ``None``. INPUT: - - self -- shifted primed tableau - ind -- index of the function $\varepsilon_i$ associated with a crystal operator $e_i$. - + + - ``self`` -- shifted primed tableau + - ``ind`` -- index of the function `\epsilon_i` associated with a crystal operator `e_i`. + OUTPUT: - - Value of the function $\varepsilon_i$. - + + Value of the function `\epsilon_i`. + EXAMPLES:: - + sage: t = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.5, 2.5), (2.0, 2.5, 3.0, 3.0), (3.0, 4.0)]) sage: t.epsilon(2) 3 @@ -666,21 +681,22 @@ def epsilon(self,i): def phi(self,i): r""" - Compute value of the crystal function $\phi_i$ applied to a shifted primed tableau ``self``. - The function $\phi_i$ is defined to be the maximal number of operators $f_i$ one can apply to - ``self`` before vanishing into ``None``. + Compute value of the crystal function `\phi_i` applied to a + shifted primed tableau ``self``. The function `\phi_i` is + defined to be the maximal number of operators `f_i` one can + apply to ``self`` before vanishing into ``None``. INPUT: - - self -- shifted primed tableau - ind -- index of the function $\varepsilon_i$ associated with a crystal operator $f_i$. - + + - ``self`` -- shifted primed tableau + - ``ind`` -- index of the function `\epsilon_i` associated with a crystal operator `f_i`. + OUTPUT: - - Value of the function $\varepsilon_i$. - + + Value of the function `\epsilon_i`. + EXAMPLES:: - + sage: t = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.5, 2.0), (2.0, 2.0, 2.0, 2.5), (3.0, 4.0)]) sage: t.phi(2) 3 @@ -698,7 +714,7 @@ def phi(self,i): def is_highest_weight(self): """ Check wether the shifted primed tableau ``self`` is a highest weight element of the crystal. - Highest weight element defined to be vanishing under any crystal operator $e_i$. + Highest weight element defined to be vanishing under any crystal operator `e_i`. EXAMPLE:: @@ -725,9 +741,9 @@ def is_highest_weight(self): except KeyError: return False return True - - - + + + class ShiftedPrimedTableaux(UniqueRepresentation, Parent): """ @@ -741,27 +757,31 @@ class ShiftedPrimedTableaux(UniqueRepresentation, Parent): - with no argument, the class of all primed tableaux - - with a list or partition argument, the class of all primed tableaux of that - shape (infinite set if we don't specify the weight or maximum element) - - with an additional integer argument ``max_element``, the class of all primed - tableaux of a given shape and a given maximum element - + - with a list or partition argument, the class of all primed + tableaux of that shape (infinite set if we don't specify the + weight or maximum element) + + - with an additional integer argument ``max_element``, the class + of all primed tableaux of a given shape and a given maximum + element + - with a tuple argument, the class of all primed tableaux of that - weight (finite set) - - A primed tableau is a shifted tableau on the alphabet - X' = {1' < 1 < 2' < 2 <...< n' < n} such that - 1). the entries are weakly increasing along rows and columns - 2). a row can't have two repeated primed elements, and a column - can't have two repeated non-primed elements - 3). there are only non-primed elements along the main diagonal - - The weight of a tableau is defined to be the vector with i-th component - equal to the number of letters i and i' in the tableau. - The sum of the entries in the weight vector must be equal to the number - of boxes in the partition. + weight (finite set) + + A primed tableau is a shifted tableau on the alphabet + X' = {1' < 1 < 2' < 2 <...< n' < n} such that - All classes of Shifted Primed Tableaux are not iterateble. + 1. the entries are weakly increasing along rows and columns + 2. a row can't have two repeated primed elements, and a column + can't have two repeated non-primed elements + 3. there are only non-primed elements along the main diagonal + + The weight of a tableau is defined to be the vector with i-th + component equal to the number of letters i and i' in the tableau. + The sum of the entries in the weight vector must be equal to the + number of boxes in the partition. + + None of the Shifted Primed Tableaux classes can be iterated over. EXAMPLES:: @@ -838,10 +858,10 @@ def __classcall_private__(cls, *args, **kwargs): if 'max' in kwargs: max_element = int(kwargs['max']) - + if 'size' in kwargs and isinstance(kwargs['size'],(list,tuple,Partition)): shape = list(kwargs['size']) - + if 'shape' in kwargs: shape = list(kwargs['shape']) @@ -856,7 +876,7 @@ def __classcall_private__(cls, *args, **kwargs): shape = args[1] else: raise ValueError('weight argument must be a tuple and shape argument must be a strictly increasing partition') - + elif isinstance(args[0],(list,Partition)) and shape==None: shape = args[0] if len(args)>1: @@ -872,7 +892,7 @@ def __classcall_private__(cls, *args, **kwargs): shape = Partition(shape) except ValueError: raise ValueError('shape {} is not a partition'.format(shape)) - + if weight is not None: while weight[-1]==0: weight = weight[:-1] @@ -885,21 +905,21 @@ def __classcall_private__(cls, *args, **kwargs): if max_elementshape[i+1] for i in range(len(shape)-1)): return ShiftedPrimedTableaux_shape(Partition(shape), max_element) - + elif shape is None: return ShiftedPrimedTableaux_weight(weight) if not all(shape[i]>shape[i+1] for i in range(len(shape)-1)): raise ValueError("shape {} is not a strict partition".format(shape)) - + if sum(shape) != sum(weight): raise ValueError("the sum of weights should match the sum of parts of the shape") @@ -921,7 +941,7 @@ def __contains__(self, T): False sage: [1,1,1] in ShiftedPrimedTableaux() True - + TESTS:: sage: 1 in ShiftedPrimedTableaux() @@ -947,7 +967,7 @@ class ShiftedPrimedTableaux_all(ShiftedPrimedTableaux): def __init__(self): """ - Initializes the class of all shifted tableaux. + Initialize the class of all shifted tableaux. TESTS:: @@ -967,7 +987,7 @@ def _repr_(self): Return a string representation of ``self``. TESTS:: - + sage: ShiftedPrimedTableaux() Shifted Primed Tableaux """ @@ -975,7 +995,7 @@ def _repr_(self): def _element_constructor_(self, T): """ - Constructs an object from ``T`` as an element of ``self``, if + Construct an object from ``T`` as an element of ``self``, if possible. INPUT: @@ -1019,9 +1039,9 @@ def __contains__(self, T): return True except ValueError: return False - - + + class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): """ Shifted Primed Tableaux of a fixed shape. @@ -1030,7 +1050,7 @@ class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): def __init__(self, shape, max_elt): """ - Initializes the class of Shifted Primed Tableaux of a given shape. + Initialize the class of Shifted Primed Tableaux of a given shape. If ``max_elt`` is specified, a finite set with entries smaller or equal to ``max_elt``. """ @@ -1044,7 +1064,7 @@ def _repr_(self): TESTS:: - sage: ShiftedPrimedTableaux([3,2,1]) + sage: ShiftedPrimedTableaux([3,2,1]) Shifted Primed Tableaux of shape [3, 2, 1] """ if self._max_elt is None: @@ -1060,7 +1080,7 @@ def __contains__(self, T): sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux([4,2]) True """ - + try: Tab = self.element_class(self, T) except ValueError: @@ -1068,14 +1088,14 @@ def __contains__(self, T): if self._max_elt is None: return (Tab.shape() == self._shape) - + return (Tab.shape() == self._shape and Tab.max_element() <= self._max_elt) - + def _element_constructor_(self, T): """ - Constructs an object from ``T`` as an element of ``self``, if + Construct an object from ``T`` as an element of ``self``, if possible. INPUT: @@ -1128,7 +1148,7 @@ def __iter__(self): Iterate over ``self``, if ``max_element`` is specified. EXAMPLES:: - + sage: Tabs = ShiftedPrimedTableaux([3,2], max_element=3) sage: Tabs[:4] [[(1.0, 1.0, 1.0), (2.0, 2.0)], @@ -1137,9 +1157,9 @@ def __iter__(self): [(1.0, 1.0, 1.0), (3.0, 3.0)]] sage: len(list(Tabs)) 24 - + TEST:: - + sage: Tabs = ShiftedPrimedTableaux([3,2]) sage: Tabs[:3] Traceback (most recent call last): @@ -1158,7 +1178,7 @@ def list_decreasing_weight(self): List elements of ``self`` with weakly decreasing weight. EXAMPLE:: - + sage: Tabs = ShiftedPrimedTableaux([2,1]) sage: Tabs.list_decreasing_weight() [[(1.0, 1.0), (2.0,)], [(1.0, 2.0), (3.0,)], [(1.0, 1.5), (3.0,)]] @@ -1178,7 +1198,7 @@ def list_highest_weight(self): List elements of ``self`` that are highest weight elements in the crystal. EXAMPLE:: - + sage: Tabs = ShiftedPrimedTableaux([3,1]) sage: Tabs.list_highest_weight() [[(1.0, 1.0, 1.0), (2.0,)], @@ -1197,8 +1217,8 @@ class ShiftedPrimedTableaux_weight(ShiftedPrimedTableaux): def __init__(self, weight): """ - Initializes the class of Shifted Primed Tableaux of a given weight. - + Initialize the class of Shifted Primed Tableaux of a given weight. + TESTS:: sage: TestSuite( ShiftedPrimedTableaux((3,2,1)) ).run() @@ -1229,10 +1249,10 @@ def __contains__(self, T): except ValueError: return False return Tab.weight()==self._weight - + def _element_constructor_(self, T): """ - Constructs an object from ``T`` as an element of ``self``, if + Construct an object from ``T`` as an element of ``self``, if possible. INPUT: @@ -1265,7 +1285,7 @@ def __iter__(self): Iterate over ``self``. EXAMPLES:: - + sage: Tabs = ShiftedPrimedTableaux((2,3)) sage: Tabs[:4] [[(1.0, 1.0, 2.0, 2.0, 2.0)], @@ -1279,7 +1299,7 @@ def __iter__(self): if all(shape_[i] > shape_[i+1] for i in range(len(shape_)-1)) for tab in ShiftedPrimedTableaux(shape = shape_, weight = self._weight)) - + class ShiftedPrimedTableaux_weight_shape(ShiftedPrimedTableaux): @@ -1290,7 +1310,7 @@ class ShiftedPrimedTableaux_weight_shape(ShiftedPrimedTableaux): def __init__(self, weight, shape): """ - Initializes the class of Shifted Primed Tableaux of the given weight and shape. + Initialize the class of Shifted Primed Tableaux of the given weight and shape. """ Parent.__init__(self, category=FiniteEnumeratedSets()) self._weight = weight @@ -1302,7 +1322,7 @@ def _repr_(self): TESTS:: - sage: ShiftedPrimedTableaux([3,2,1],(4,2)) + sage: ShiftedPrimedTableaux([3,2,1],(4,2)) Shifted Primed Tableaux of weight (4, 2) and shape [3, 2, 1] """ return ("Shifted Primed Tableaux of weight {} and shape {}" .format(self._weight,self._shape)) @@ -1324,7 +1344,7 @@ def __contains__(self, T): def _element_constructor_(self, T): """ - Constructs an object from ``T`` as an element of ``self``, if + Construct an object from ``T`` as an element of ``self``, if possible. TESTS:: @@ -1343,12 +1363,12 @@ def _element_constructor_(self, T): except ValueError: raise ValueError ("{} is not an element of Shifted Primed Tableaux" .format(T)) - + if Tab.shape() == self._shape and Tab.weight() == self._weight: return Tab raise ValueError("{} is not an element of {}".format(T, self)) - + def __iter__(self): """ Iterate over ``self``. @@ -1374,8 +1394,8 @@ def __iter__(self): for i,w in enumerate(self._weight): tab_list_old = tab_list_new tab_list_new = [] - for sub_tab in tab_list_old: - sub_shape = [len(sub_tab[r]) for r in range(len(sub_tab))] + for sub_tab in tab_list_old: + sub_shape = [len(sub_tab[r]) for r in range(len(sub_tab))] for strip in add_strip(sub_shape, full_shape, w): l = int(len(strip)/2) if len(sub_shape) Date: Wed, 20 Sep 2017 00:16:48 +0200 Subject: [PATCH 360/740] more trivial fixes --- src/sage/combinat/tableau_shifted_primed.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index 75311456ca3..d1cb32dd0ce 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -650,9 +650,11 @@ def e(self,ind): def epsilon(self,i): r""" Compute the value of the crystal function `\epsilon_i` applied to - a shifted primed tableau ``self``. The function `\epsilon_i` - is defined to be the maximal number of operators `e_i` one - can apply to ``self`` before vanishing into ``None``. + a shifted primed tableau ``self``. + + The function `\epsilon_i` is defined to be the maximal number + of operators `e_i` one can apply to ``self`` before vanishing + into ``None``. INPUT: @@ -682,9 +684,11 @@ def epsilon(self,i): def phi(self,i): r""" Compute value of the crystal function `\phi_i` applied to a - shifted primed tableau ``self``. The function `\phi_i` is - defined to be the maximal number of operators `f_i` one can - apply to ``self`` before vanishing into ``None``. + shifted primed tableau ``self``. + + The function `\phi_i` is defined to be the maximal number of + operators `f_i` one can apply to ``self`` before vanishing + into ``None``. INPUT: @@ -714,7 +718,9 @@ def phi(self,i): def is_highest_weight(self): """ Check wether the shifted primed tableau ``self`` is a highest weight element of the crystal. - Highest weight element defined to be vanishing under any crystal operator `e_i`. + + + An element is highest weight if it vanishes under any crystal operator `e_i`. EXAMPLE:: @@ -1051,6 +1057,7 @@ class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): def __init__(self, shape, max_elt): """ Initialize the class of Shifted Primed Tableaux of a given shape. + If ``max_elt`` is specified, a finite set with entries smaller or equal to ``max_elt``. """ From 608c5c3dda3ae4560bc0e88eeb440233ab5aa4bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 20 Sep 2017 23:47:17 +0700 Subject: [PATCH 361/740] Use names for richcmp operators --- src/sage/rings/valuation/valuation.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index ccc16d81fee..a303adb7281 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -55,6 +55,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from sage.categories.morphism import Morphism +from sage.structure.richcmp import op_EQ, op_NE, op_LE, op_LT, op_GE, op_GT from sage.misc.cachefunc import cached_method @@ -221,17 +222,17 @@ def _richcmp_(self, other, op): False """ - if op == 0: # < + if op == op_LT: return self <= other and not (self >= other) - if op == 1: # <= + if op == op_LE: return self._le_(other) - if op == 2: # == + if op == op_EQ: return self._eq_(other) - if op == 3: # != + if op == op_NE: return not self == other - if op == 4: # > + if op == op_GT: return self >= other and not (self <= other) - if op == 5: # >= + if op == op_GE: return self._ge_(other) raise NotImplementedError("Operator not implemented for this valuation") From 28b36f83023a95933d65679f74cf7d1f59a7a3f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 21 Sep 2017 03:14:01 +0700 Subject: [PATCH 362/740] Fix ScaleAction the InverseScale action comes for free. I am not entirely sure how it works but v/2 gets rewritten to v*(1/2). --- src/sage/rings/valuation/valuation_space.py | 37 ++++----------------- 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index 97486bac47d..92c3a5f309b 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -154,15 +154,13 @@ def _get_action_(self, S, op, self_on_left): sage: v = QQ.valuation(2) sage: from operator import mul sage: v.parent().get_action(ZZ, mul) # indirect doctest - Left action by Integer Ring on Discrete pseudo-valuations on Rational Field + Right action by Integer Ring on Discrete pseudo-valuations on Rational Field """ from operator import mul, div from sage.rings.all import QQ, InfinityRing, ZZ if op == mul and (S is InfinityRing or S is QQ or S is ZZ): - return ScaleAction(S, self) - if op == div and self_on_left and (S is InfinityRing or S is QQ or S is ZZ): - return InverseScaleAction(self, S) + return ScaleAction(S, self, not self_on_left, op) return None def _an_element_(self): @@ -1568,7 +1566,8 @@ class ScaleAction(Action): sage: v = QQ.valuation(5) sage: from operator import mul - sage: v.parent().get_action(IntegerRing, mul, self_on_left=False) + sage: v.parent().get_action(ZZ, mul, self_on_left=False) + Left action by Integer Ring on Discrete pseudo-valuations on Rational Field """ def _call_(self, s, v): @@ -1582,29 +1581,7 @@ def _call_(self, s, v): 3 * 5-adic valuation """ + if not self.is_left(): + # for a right action, the parameters are swapped + s,v = v,s return v.scale(s) - -class InverseScaleAction(Action): - r""" - Action of integers, rationals and the infinity ring on valuations by - scaling it (with the inverse of the scalar.) - - EXAMPLES:: - - sage: v = QQ.valuation(5) - sage: from operator import div - sage: v.parent().get_action(IntegerRing, div, self_on_left=True) - - """ - def _call_(self, v, s): - r""" - Let ``s`` act on ``v`` (by division.) - - EXAMPLES:: - - sage: v = QQ.valuation(5) - sage: v/3 # indirect doctest - 1/3 * 5-adic valuation - - """ - return v.scale(1/s) From ef251e17f4b118b02d5d0acf5e8b489309e50b01 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Mon, 2 Oct 2017 18:32:37 -0700 Subject: [PATCH 363/740] improved style and doctests --- src/sage/combinat/tableau_shifted_primed.py | 592 +++++++++++--------- 1 file changed, 322 insertions(+), 270 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index d1cb32dd0ce..5a6bd5187d7 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -44,7 +44,8 @@ class ShiftedPrimedTableau(ClonableArray): sage: t = ShiftedPrimedTableau([[1,2,2.5,3],[0,2,2.5]]) Traceback (most recent call last): ... - ValueError: [[1, 2, 2.50000000000000, 3], [0, 2, 2.50000000000000]] is not an element of Shifted Primed Tableaux + ValueError: [[1, 2, 2.50000000000000, 3], [0, 2, 2.50000000000000]] + is not an element of Shifted Primed Tableaux """ @staticmethod @@ -71,15 +72,15 @@ def __classcall_private__(cls, T): return ShiftedPrimedTableaux()(T) - - def __init__(self,parent, T): + def __init__(self, parent, T): r""" Initialize a shifted tableau. TESTS:: sage: s = ShiftedPrimedTableau([[1,"2'","3'",3],[2,"3'"]]) - sage: t = ShiftedPrimedTableaux([4,2])([[1,"2p","3p",3],[0, 2,"3p"]]) + sage: t = ShiftedPrimedTableaux([4,2])([[1,"2p","3p",3], + ....: [0, 2,"3p"]]) sage: s==t True sage: t.parent() @@ -91,7 +92,8 @@ def __init__(self,parent, T): sage: s is t # identical shifted tableaux are distinct objects False - A shifted primed tableau is shallowly immutable, the rows are represented as tuples. + A shifted primed tableau is shallowly immutable, the rows are + represented as tuples. :: @@ -102,7 +104,7 @@ def __init__(self,parent, T): TypeError: 'tuple' object does not support item assignment """ - if isinstance(T,ShiftedPrimedTableau): + if isinstance(T, ShiftedPrimedTableau): ClonableArray.__init__(self, parent, T) return @@ -114,7 +116,7 @@ def __init__(self,parent, T): else: t_ = T - if not all(isinstance(row, (list,tuple,np.ndarray)) for row in t_): + if not all(isinstance(row, (list, tuple, np.ndarray)) for row in t_): t_ = [t_] t = [] @@ -123,35 +125,36 @@ def __init__(self,parent, T): row_new = [] for element in row: - if isinstance(element,str): - if element[-1] == "'" and element[:-1].isdigit() == True: # Check if an element has "'" at the end + if isinstance(element, str): + if element[-1] == "'" and element[:-1].isdigit() is True: + # Check if an element has "'" at the end row_new.append(float(float(element[:-1]) - .5)) continue - if element[-1] == "p" and element[:-1].isdigit() == True: # Check if an element has "p" at the end + if element[-1] == "p" and element[:-1].isdigit() is True: + # Check if an element has "p" at the end row_new.append(float(float(element[:-1]) - .5)) continue try: - if int(float(element)*2) == float(element)*2: # Check if an element is a half-integer + if int(float(element)*2) == float(element)*2: + # Check if an element is a half-integer row_new.append(float(element)) continue else: raise ValueError("all numbers must be half-integers") - except (TypeError,ValueError): - raise ValueError("primed elements should be half-integers or have symbols p or ' at the end") + except (TypeError, ValueError): + raise ValueError("primed elements have wrong format") t.append(row_new) - - #Accounting for zeros at the beginning and at the end of a row' - # TODO: one line - i=0 + # Accounting for zeros at the beginning and at the end of a row' + i = 0 while i < len(t): row = t[i] try: - while row[0]==0: + while row[0] == 0: row.pop(0) - while row[-1]==0: + while row[-1] == 0: row.pop(-1) except IndexError: t.remove(t[i]) @@ -174,7 +177,7 @@ def __eq__(self, other): Boolean. - EXAMPLES:: + EXAMPLE:: sage: t = ShiftedPrimedTableau([[1,"2p"]]) sage: t == ShiftedPrimedTableaux([2])([1,1.5]) @@ -186,13 +189,11 @@ def __eq__(self, other): return False return (list(self) == list(Tab)) - - def to_matrix(self): """ Return a 2-dimensional numpy.array representation of a shifted tableau - EXAMPLES:: + EXAMPLE:: sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p'],[3]]) sage: mat = t.to_matrix() @@ -207,14 +208,14 @@ def to_matrix(self): m = len(self[0]) for i in range(len(self)): array.append([0]*i + list(self[i]) + [0]*(m-i-len(self[i]))) - array = np.array(array, dtype = 'float') + array = np.array(array, dtype='float') return array def check(self): """ Check that ``self`` is a valid primed tableaux. - EXAMPLES:: + EXAMPLE:: sage: T = ShiftedPrimedTableaux([4,2]) sage: t = T([[1,'2p',2,2],[2,'3p']]) @@ -222,24 +223,31 @@ def check(self): """ if not all(len(self[i]) > len(self[i+1]) for i in range(len(self)-1)): raise ValueError('shape must be a strict partition') - for i,row in enumerate(self): + for i, row in enumerate(self): if i > 0: - if not all(val > self[i-1][j+1] for j,val in enumerate(row) if int(val)==val): - raise ValueError('column is not strictly increasing in non-primes') - if not all(val >= self[i-1][j+1] for j,val in enumerate(row) if int(val)!=val): - raise ValueError('column is not weakly increasing in primes') - if not all(row[j] <= row[j+1] for j in range(len(row)-1) if int(row[j])==row[j]): + if not all(val > self[i-1][j+1] + for j, val in enumerate(row) if int(val) == val): + raise ValueError( + 'column is not strictly increasing in non-primes') + if not all(val >= self[i-1][j+1] + for j, val in enumerate(row) if int(val) != val): + raise ValueError( + 'column is not weakly increasing in primes') + if not all(row[j] <= row[j+1] + for j in range(len(row)-1) if int(row[j]) == row[j]): raise ValueError('row is not weakly increasing in non-primes') - if not all(row[j] < row[j+1] for j in range(len(row)-1) if int(row[j])!=row[j]): + if not all(row[j] < row[j+1] + for j in range(len(row)-1) if int(row[j]) != row[j]): raise ValueError('row is not strictly increasing in primes') - if not all(int(row[0])==row[0] for row in self): + if not all(int(row[0]) == row[0] for row in self): raise ValueError('diagonal elements must be non-primes') def _repr_(self): """ - Represent Shifted Primed Tableau as a list of rows, rows are represented as tuples of half-integers. + Represent Shifted Primed Tableau as a list of rows, + rows are represented as tuples of half-integers. - EXAMPLES:: + EXAMPLE:: sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t @@ -268,14 +276,16 @@ def pp(self): max_ind = max([item for sublist in self for item in sublist]) max_len = len(str(round(max_ind))) string_list = '' - for i,row in enumerate(self): + for i, row in enumerate(self): string_list += ' ' string_list += ' ' * i * (max_len) for val in row: - if int(val)==val: - string_list += str(int(val)) + ' ' * (max_len-len(str(int(val)))) + if int(val) == val: + string_list += (str(int(val)) + + ' ' * (max_len-len(str(int(val))))) else: - string_list += str(int(val+.5)) + "'" + ' '*(max_len-1- len(str(int(val+.5)))) + string_list += (str(int(val+.5)) + "'" + + ' '*(max_len-1 - len(str(int(val+.5))))) string_list += '\n' string_list = string_list[:-2] print(string_list) @@ -285,7 +295,7 @@ def _latex_(self): r""" Return LaTex code for ``self``. - EXAMPLES:: + EXAMPLE:: sage: T = ShiftedPrimedTableaux([4,2]) sage: latex(T([[1,"2p",2,"3p"],[2,3]])) @@ -298,10 +308,10 @@ def _latex_(self): """ from sage.combinat.output import tex_from_array L = list() - for i,row in enumerate(self): + for i, row in enumerate(self): num_list = [None]*i for let in row: - if int(let)==let: + if int(let) == let: num_list.append(int(let)) else: num_list.append(str(int(let+0.5))+"'") @@ -319,16 +329,16 @@ def max_element(self): sage: Tab.max_element() 3 """ - if self==[]: + if self == []: return 0 else: flat = [item for sublist in self for item in sublist] return int(round(max(flat))) - def shape(self): """ - Return the shape of the underlying partition of ``self`` in list format. + Return the shape of the underlying partition of ``self`` in list + format. EXAMPLES:: @@ -338,7 +348,7 @@ def shape(self): """ return ([len(row) for row in self]) - def __call__(self,*cell): + def __call__(self, *cell): """ Function call of ``self``. @@ -360,29 +370,28 @@ def __call__(self,*cell): sage: t((1,2)) Traceback (most recent call last): ... - IndexError: the cell (1,2) is not contained in the shifted tableau - [(1.0, 1.5, 2.0, 2.0), (2.0, 2.5)] + IndexError: invalid cell sage: t((1,1)) 2.5 """ try: - i,j = cell + i, j = cell except ValueError: - i,j = cell[0] + i, j = cell[0] try: return self[i][j] except IndexError: - raise IndexError("the cell (%d,%d) is not contained in the shifted tableau \n%s"%(i, j, repr(self))) - + raise IndexError("invalid cell") def weight(self): """ Return the weight of ``self`` as a tuple. - The weight of a shifted primed tableau is defined to be the vector with i-th component - equal to the number of letters i and i' in the tableau. + The weight of a shifted primed tableau is defined to be the vector + with i-th component equal to the number of letters i and i' in the + tableau. EXAMPLE:: @@ -400,8 +409,8 @@ def weight(self): def reading_word_with_positions(self): """ - Return the reading word of ``self`` together with positions of the corresponding - letters in ``self``. + Return the reading word of ``self`` together with positions of the + corresponding letters in ``self``. The reading word of a shifted primed tableau is constructed as follows: @@ -419,17 +428,18 @@ def reading_word_with_positions(self): sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t.reading_word_with_positions() - [((1, 2), 3), ((0, 1), 2), ((1, 1), 2), ((0, 0), 1), ((0, 2), 2), ((0, 3), 2)] + [((1, 2), 3), ((0, 1), 2), ((1, 1), 2), ((0, 0), 1), + ((0, 2), 2), ((0, 3), 2)] """ mat = self.to_matrix() list_with_positions = [] - for (i,j), x in np.ndenumerate(mat[:,::-1].T): + for (i, j), x in np.ndenumerate(mat[:, ::-1].T): if int(x) != x: - list_with_positions.append(((j,mat.shape[1]-i-1),int(x+0.5))) - for (i,j), x in np.ndenumerate(mat[::-1,:]): - if int(x) == x and int(x) != 0: - list_with_positions.append(((mat.shape[0]-i-1,j),int(x))) + list_with_positions.append(((j, mat.shape[1]-i-1), int(x+0.5))) + for (i, j), x in np.ndenumerate(mat[::-1, :]): + if int(x) == x and int(x) != 0: + list_with_positions.append(((mat.shape[0]-i-1, j), int(x))) return list_with_positions def reading_word(self): @@ -456,11 +466,10 @@ def reading_word(self): """ return [tup[1] for tup in self.reading_word_with_positions()] - def f(self, ind): """ - Compute the action of the crystal operator `f_i` on a Shifted Primed Tableau - using cases from the paper [GPS.17]. + Compute the action of the crystal operator `f_i` on a Shifted Primed + Tableau using cases from the paper [GPS.17]. INPUT:: @@ -481,7 +490,8 @@ def f(self, ind): sage: s = t.f(2) sage: print(s) None - sage: t = ShiftedPrimedTableau([[1,1,1,1.5,2.5],[0,2,2,3,3],[0,0,3,4]]) + sage: t = ShiftedPrimedTableau([[1,1,1,1.5,2.5],[0,2,2,3,3], + ....: [0,0,3,4]]) sage: t.pp() 1 1 1 2' 3' 2 2 3 3 @@ -499,10 +509,12 @@ def f(self, ind): T = self.to_matrix() read_word = self.reading_word_with_positions() - read_word = [num for num in read_word if num[1]==ind or num[1]==ind+1] + read_word = [num + for num in read_word + if num[1] == ind or num[1] == ind+1] element_to_change = None - count=0 + count = 0 for element in read_word: if element[1] == ind+1: @@ -512,56 +524,57 @@ def f(self, ind): else: count -= 1 - if element_to_change == None: + if element_to_change is None: return None - (r,c), elt = element_to_change + (r, c), elt = element_to_change - if T[r,c] == ind- .5: + if T[r, c] == ind - .5: T = T.T + .5 - r,c = c,r - h,l = T.shape + r, c = c, r + h, l = T.shape - if (c+1==l or T[r,c+1]>=ind+1 or T[r,c+1]<1): + if (c+1 == l or T[r, c+1] >= ind+1 or T[r, c+1] < 1): - (tp_r,tp_c)=(r,c) + (tp_r, tp_c) = (r, c) while True: - if tp_r+1==h or T[tp_r+1,tp_c]>ind+1 or T[tp_r+1,tp_c]<1: + if (tp_r+1 == h or + T[tp_r+1, tp_c] > ind+1 or + T[tp_r+1, tp_c] < 1): break - if tp_r<=tp_c and T[tp_r+1,tp_r+1]==ind+1: + if tp_r <= tp_c and T[tp_r+1, tp_r+1] == ind+1: tp_r += 1 tp_c = tp_r break - if (ind+.5 not in T[tp_r+1]): + if (ind+.5 not in T[tp_r+1]): break tp_r += 1 - tp_c = np.where(T[tp_r]==ind+.5)[0] - + tp_c = np.where(T[tp_r] == ind+.5)[0] if tp_r == r: - T[r,c] += 1 + T[r, c] += 1 elif tp_r == tp_c: - T[r,c] += .5 + T[r, c] += .5 else: - T[r,c] += .5 - T[tp_r,tp_c] += .5 + T[r, c] += .5 + T[tp_r, tp_c] += .5 - elif T[r,c+1]==ind+.5: - T[r,c+1] += .5 - T[r,c] += .5 + elif T[r, c+1] == ind+.5: + T[r, c+1] += .5 + T[r, c] += .5 - if r>c: + if r > c: T = T.T - .5 return(ShiftedPrimedTableau(T)) - def e(self,ind): + def e(self, ind): """ - Compute the action of the crystal operator `e_i` on a Shifted Primed Tableau - using cases from the paper [GPS.17]. + Compute the action of the crystal operator `e_i` on a Shifted Primed + Tableau using cases from the paper [GPS.17]. INPUT: @@ -574,7 +587,8 @@ def e(self,ind): EXAMPLES:: - sage: t = ShiftedPrimedTableau([[1,1,1,1.5,2.5],[0,2,"3p",3,3],[0,0,3,4]]) + sage: t = ShiftedPrimedTableau([[1,1,1,1.5,2.5], + ....: [0,2,"3p",3,3],[0,0,3,4]]) sage: t.pp() 1 1 1 2' 3' 2 3' 3 3 @@ -593,10 +607,12 @@ def e(self,ind): T = self.to_matrix() read_word = self.reading_word_with_positions() - read_word = [num for num in read_word if num[1]==ind or num[1]==ind+1] + read_word = [num + for num in read_word + if num[1] == ind or num[1] == ind+1] element_to_change = None - count=0 + count = 0 for element in read_word[::-1]: if element[1] == ind: @@ -606,48 +622,47 @@ def e(self,ind): else: count -= 1 - if element_to_change == None: + if element_to_change is None: return None - (r,c), elt = element_to_change + (r, c), elt = element_to_change - if T[r,c] == ind + .5: + if T[r, c] == ind + .5: T = T.T + .5 - r,c = c,r - h,l = T.shape + r, c = c, r + h, l = T.shape - if (c==0 or T[r,c-1]<=ind or T[r,c-1]<1): + if (c == 0 or T[r, c-1] <= ind or T[r, c-1] < 1): - (tp_r,tp_c)=(r,c) + (tp_r, tp_c) = (r, c) while True: - if tp_r==0 or T[tp_r-1,tp_c]c: + if r > c: T = T.T - .5 return(ShiftedPrimedTableau(T)) - def epsilon(self,i): + def epsilon(self, i): r""" Compute the value of the crystal function `\epsilon_i` applied to a shifted primed tableau ``self``. @@ -659,7 +674,8 @@ def epsilon(self,i): INPUT: - ``self`` -- shifted primed tableau - - ``ind`` -- index of the function `\epsilon_i` associated with a crystal operator `e_i`. + - ``ind`` -- index of the function `\epsilon_i` associated with a + crystal operator `e_i`. OUTPUT: @@ -667,7 +683,8 @@ def epsilon(self,i): EXAMPLES:: - sage: t = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.5, 2.5), (2.0, 2.5, 3.0, 3.0), (3.0, 4.0)]) + sage: t = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.5, 2.5), + ....: (2.0, 2.5, 3.0, 3.0), (3.0, 4.0)]) sage: t.epsilon(2) 3 sage: s = t.e(2).e(2).e(2) @@ -678,10 +695,10 @@ def epsilon(self,i): count = -1 while b is not None: b = b.e(i) - count +=1 + count += 1 return count - def phi(self,i): + def phi(self, i): r""" Compute value of the crystal function `\phi_i` applied to a shifted primed tableau ``self``. @@ -693,7 +710,8 @@ def phi(self,i): INPUT: - ``self`` -- shifted primed tableau - - ``ind`` -- index of the function `\epsilon_i` associated with a crystal operator `f_i`. + - ``ind`` -- index of the function `\epsilon_i` associated with a + crystal operator `f_i`. OUTPUT: @@ -701,7 +719,8 @@ def phi(self,i): EXAMPLES:: - sage: t = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.5, 2.0), (2.0, 2.0, 2.0, 2.5), (3.0, 4.0)]) + sage: t = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.5, 2.0), + ....: (2.0, 2.0, 2.0, 2.5), (3.0, 4.0)]) sage: t.phi(2) 3 sage: s = t.f(2).f(2).f(2) @@ -712,19 +731,22 @@ def phi(self,i): count = -1 while b is not None: b = b.f(i) - count +=1 + count += 1 return count def is_highest_weight(self): """ - Check wether the shifted primed tableau ``self`` is a highest weight element of the crystal. + Check wether the shifted primed tableau ``self`` is a highest weight + element of the crystal. - An element is highest weight if it vanishes under any crystal operator `e_i`. + An element is highest weight if it vanishes under any crystal operator + `e_i`. - EXAMPLE:: + EXAMPLES:: - sage: t = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.0, 1.0), (2.0, 2.0, 2.0, 2.5), (3.0, 3.0)]) + sage: t = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.0, 1.0), + ....: (2.0, 2.0, 2.0, 2.5), (3.0, 3.0)]) sage: print(t.e(1)) None sage: print(t.e(2)) @@ -737,20 +759,18 @@ def is_highest_weight(self): count = {} for l in read_w[::-1]: try: - count[l] +=1 + count[l] += 1 except KeyError: count[l] = 1 - if l>1: + if l > 1: try: - if count[l]>count[l-1]: + if count[l] > count[l-1]: return False except KeyError: return False return True - - class ShiftedPrimedTableaux(UniqueRepresentation, Parent): """ A factory for the various classes of shifted standard tableaux. @@ -822,8 +842,8 @@ class ShiftedPrimedTableaux(UniqueRepresentation, Parent): def __classcall_private__(cls, *args, **kwargs): r""" This is a factory class which returns the appropriate parent based on - arguments. See the documentation for :class:`ShiftedPrimedTableaux` for - more information. + arguments. See the documentation for :class:`ShiftedPrimedTableaux` + for more information. TESTS:: @@ -834,12 +854,12 @@ def __classcall_private__(cls, *args, **kwargs): sage: ShiftedPrimedTableaux(3) Traceback (most recent call last): ... - ValueError: weight argument must be a tuple and shape argument must be a strictly increasing partition + ValueError: invalid argument for weight or shape sage: ShiftedPrimedTableaux(weight=(2,2,2), shape=[3,2]) Traceback (most recent call last): ... - ValueError: the sum of weights should match the sum of parts of the shape + ValueError: weight and shape are incompatible sage: ShiftedPrimedTableaux([[1]]) Traceback (most recent call last): @@ -849,8 +869,7 @@ def __classcall_private__(cls, *args, **kwargs): sage: ShiftedPrimedTableaux(weight=(2,2,2), max_element=2) Traceback (most recent call last): ... - ValueError: maximum element can not be smaller then the length of the weight vector - + ValueError: maximum element is incompatible with the weight """ weight = None shape = None @@ -865,7 +884,8 @@ def __classcall_private__(cls, *args, **kwargs): if 'max' in kwargs: max_element = int(kwargs['max']) - if 'size' in kwargs and isinstance(kwargs['size'],(list,tuple,Partition)): + if 'size' in kwargs and isinstance(kwargs['size'], + (list, tuple, Partition)): shape = list(kwargs['size']) if 'shape' in kwargs: @@ -875,23 +895,27 @@ def __classcall_private__(cls, *args, **kwargs): weight = tuple(kwargs['weight']) if args: - if isinstance(args[0],tuple) and weight==None: + if isinstance(args[0], tuple) and weight is None: weight = args[0] - if len(args)>1: - if (isinstance(args[1],(list,Partition)) or args[1]==None) and shape==None: + if len(args) > 1: + if ((isinstance(args[1], (list, Partition)) or + args[1] is None) and shape is None): shape = args[1] else: - raise ValueError('weight argument must be a tuple and shape argument must be a strictly increasing partition') + raise ValueError( + 'invalid argument for weight or shape') - elif isinstance(args[0],(list,Partition)) and shape==None: + elif (isinstance(args[0], (list, Partition)) and shape is None): shape = args[0] - if len(args)>1: - if (isinstance(args[1],tuple) or args[1]==None) and weight==None: + if len(args) > 1: + if ((isinstance(args[1], tuple) or args[1] is None) and + weight is None): weight = args[1] else: - raise ValueError('weight argument must be a tuple and shape argument must be a strictly increasing partition') + raise ValueError( + 'invalid argument for weight or shape') else: - raise ValueError('weight argument must be a tuple and shape argument must be a strictly increasing partition') + raise ValueError('invalid argument for weight or shape') if shape is not None: try: @@ -900,36 +924,40 @@ def __classcall_private__(cls, *args, **kwargs): raise ValueError('shape {} is not a partition'.format(shape)) if weight is not None: - while weight[-1]==0: + while weight[-1] == 0: weight = weight[:-1] if max_element is not None and weight is not None: - if len(weight)!=max_element: - raise ValueError("maximum element can not be smaller then the length of the weight vector") + if len(weight) != max_element: + raise ValueError( + "maximum element is incompatible with the weight") if max_element is not None and shape is not None: - if max_elementshape[i+1] for i in range(len(shape)-1)): + elif weight is None and all(shape[i] > shape[i+1] + for i in range(len(shape)-1)): return ShiftedPrimedTableaux_shape(Partition(shape), max_element) elif shape is None: return ShiftedPrimedTableaux_weight(weight) - if not all(shape[i]>shape[i+1] for i in range(len(shape)-1)): - raise ValueError("shape {} is not a strict partition".format(shape)) + if not all(shape[i] > shape[i+1] for i in range(len(shape)-1)): + raise ValueError( + "shape {} is not a strict partition".format(shape)) if sum(shape) != sum(weight): - raise ValueError("the sum of weights should match the sum of parts of the shape") + raise ValueError( + "weight and shape are incompatible") - return ShiftedPrimedTableaux_weight_shape(weight,shape) + return ShiftedPrimedTableaux_weight_shape(weight, shape) def __contains__(self, T): """ @@ -956,7 +984,7 @@ def __contains__(self, T): True """ try: - self.element_class(self,T) + self.element_class(self, T) return True except ValueError: return False @@ -964,7 +992,6 @@ def __contains__(self, T): _is_a = __contains__ - class ShiftedPrimedTableaux_all(ShiftedPrimedTableaux): """ The class of all Shifted Primed Tableaux. @@ -992,7 +1019,7 @@ def _repr_(self): """ Return a string representation of ``self``. - TESTS:: + TEST:: sage: ShiftedPrimedTableaux() Shifted Primed Tableaux @@ -1021,15 +1048,15 @@ def _element_constructor_(self, T): sage: Tab=ShiftedPrimedTableaux()([[1,1,2],[2,2]]) Traceback (most recent call last): ... - ValueError: [[1, 1, 2], [2, 2]] is not an element of Shifted Primed Tableaux + ValueError: [[1, 1, 2], [2, 2]] is not an element of + Shifted Primed Tableaux """ try: - return self.element_class(self,T) + return self.element_class(self, T) except ValueError: - raise ValueError ("{} is not an element of Shifted Primed Tableaux" - .format(T)) - + raise ValueError( + "{} is not an element of Shifted Primed Tableaux".format(T)) def __contains__(self, T): """ @@ -1041,13 +1068,12 @@ def __contains__(self, T): False """ try: - self.element_class(self,T) + self.element_class(self, T) return True except ValueError: return False - class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): """ Shifted Primed Tableaux of a fixed shape. @@ -1058,7 +1084,8 @@ def __init__(self, shape, max_elt): """ Initialize the class of Shifted Primed Tableaux of a given shape. - If ``max_elt`` is specified, a finite set with entries smaller or equal to ``max_elt``. + If ``max_elt`` is specified, a finite set with entries smaller + or equal to ``max_elt``. """ Parent.__init__(self, category=FiniteEnumeratedSets()) @@ -1069,7 +1096,7 @@ def _repr_(self): """ Return a string representation of ``self``. - TESTS:: + TEST:: sage: ShiftedPrimedTableaux([3,2,1]) Shifted Primed Tableaux of shape [3, 2, 1] @@ -1077,12 +1104,13 @@ def _repr_(self): if self._max_elt is None: return "Shifted Primed Tableaux of shape {}".format(self._shape) if self._max_elt is not None: - return ("Shifted Primed Tableaux of shape {} and maximum element {}" - .format(self._shape, self._max_elt)) + return ( + "Shifted Primed Tableaux of shape {} and maximum element {}" + .format(self._shape, self._max_elt)) def __contains__(self, T): """ - TESTS:: + TEST:: sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux([4,2]) True @@ -1096,9 +1124,8 @@ def __contains__(self, T): if self._max_elt is None: return (Tab.shape() == self._shape) - return (Tab.shape() == self._shape - and Tab.max_element() <= self._max_elt) - + return (Tab.shape() == self._shape and + Tab.max_element() <= self._max_elt) def _element_constructor_(self, T): """ @@ -1122,22 +1149,24 @@ def _element_constructor_(self, T): sage: ShiftedPrimedTableaux([3])([1,1]) Traceback (most recent call last): ... - ValueError: [1, 1] is not an element of Shifted Primed Tableaux of shape [3] + ValueError: [1, 1] is not an element of Shifted Primed Tableaux + of shape [3] """ try: Tab = self.element_class(self, T) except ValueError: - raise ValueError ("{} is not an element of Shifted Primed Tableaux" - .format(T)) + raise ValueError( + "{} is not an element of Shifted Primed Tableaux".format(T)) if self._max_elt is None: if Tab.shape() == self._shape: return Tab else: - if Tab.shape() == self._shape and Tab.max_element() <= self._max_elt: + if (Tab.shape() == self._shape and + Tab.max_element() <= self._max_elt): return Tab - raise ValueError ("{} is not an element of {}".format(T, self)) + raise ValueError("{} is not an element of {}".format(T, self)) def shape(self): """ @@ -1154,7 +1183,7 @@ def __iter__(self): """ Iterate over ``self``, if ``max_element`` is specified. - EXAMPLES:: + EXAMPLE:: sage: Tabs = ShiftedPrimedTableaux([3,2], max_element=3) sage: Tabs[:4] @@ -1175,9 +1204,11 @@ def __iter__(self): """ if self._max_elt is None: raise ValueError("set is infinite") - for weight in OrderedPartitions(sum(self._shape)+self._max_elt,k=self._max_elt): + for weight in OrderedPartitions(sum(self._shape)+self._max_elt, + k=self._max_elt): weight_n = tuple([w-1 for w in weight]) - for tab in ShiftedPrimedTableaux(shape = self._shape, weight = weight_n): + for tab in ShiftedPrimedTableaux(shape=self._shape, + weight=weight_n): yield (tab) def list_decreasing_weight(self): @@ -1195,14 +1226,16 @@ def list_decreasing_weight(self): max_element = sum(self._shape) else: max_element = self._max_elt - for weight in Partition(self._shape).dominated_partitions(rows=max_element): + for weight in Partition(self._shape).dominated_partitions( + rows=max_element): list_dw.extend(list(ShiftedPrimedTableaux(weight=tuple(weight), - shape = self._shape))) + shape=self._shape))) return list_dw def list_highest_weight(self): """ - List elements of ``self`` that are highest weight elements in the crystal. + List elements of ``self`` that are highest weight elements + in the crystal. EXAMPLE:: @@ -1212,8 +1245,9 @@ def list_highest_weight(self): [(1.0, 1.0, 1.5), (2.0,)], [(1.0, 1.0, 2.5), (2.0,)]] """ - return [tab for tab in self.list_decreasing_weight() if tab.is_highest_weight()] - + return [tab + for tab in self.list_decreasing_weight() + if tab.is_highest_weight()] class ShiftedPrimedTableaux_weight(ShiftedPrimedTableaux): @@ -1226,7 +1260,7 @@ def __init__(self, weight): """ Initialize the class of Shifted Primed Tableaux of a given weight. - TESTS:: + TEST:: sage: TestSuite( ShiftedPrimedTableaux((3,2,1)) ).run() """ @@ -1246,7 +1280,7 @@ def _repr_(self): def __contains__(self, T): """ - TESTS:: + TEST:: sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux((1,4,1)) True @@ -1255,7 +1289,7 @@ def __contains__(self, T): Tab = self.element_class(self, T) except ValueError: return False - return Tab.weight()==self._weight + return Tab.weight() == self._weight def _element_constructor_(self, T): """ @@ -1270,7 +1304,7 @@ def _element_constructor_(self, T): - the corresponding primed tableau object - TESTS:: + TEST:: sage: tab= ShiftedPrimedTableaux((2,1))([1,1,1.5]); tab [(1.0, 1.0, 1.5)] @@ -1280,18 +1314,17 @@ def _element_constructor_(self, T): try: Tab = self.element_class(self, T) except ValueError: - raise ValueError ("{} is not an element of Shifted Primed Tableaux" - .format(T)) + raise ValueError( + "{} is not an element of Shifted Primed Tableaux".format(T)) if Tab.weight() == self._weight: return Tab - raise ValueError ("{} is not an element of {}".format(T, self)) - + raise ValueError("{} is not an element of {}".format(T, self)) def __iter__(self): """ Iterate over ``self``. - EXAMPLES:: + EXAMPLE:: sage: Tabs = ShiftedPrimedTableaux((2,3)) sage: Tabs[:4] @@ -1304,9 +1337,8 @@ def __iter__(self): """ return (tab for shape_ in Partitions(sum(self._weight)) if all(shape_[i] > shape_[i+1] for i in range(len(shape_)-1)) - for tab in - ShiftedPrimedTableaux(shape = shape_, weight = self._weight)) - + for tab in ShiftedPrimedTableaux(shape=shape_, + weight=self._weight)) class ShiftedPrimedTableaux_weight_shape(ShiftedPrimedTableaux): @@ -1317,7 +1349,8 @@ class ShiftedPrimedTableaux_weight_shape(ShiftedPrimedTableaux): def __init__(self, weight, shape): """ - Initialize the class of Shifted Primed Tableaux of the given weight and shape. + Initialize the class of Shifted Primed Tableaux of the given weight + and shape. """ Parent.__init__(self, category=FiniteEnumeratedSets()) self._weight = weight @@ -1327,19 +1360,22 @@ def _repr_(self): """ Return a string representation of ``self``. - TESTS:: + TEST:: sage: ShiftedPrimedTableaux([3,2,1],(4,2)) Shifted Primed Tableaux of weight (4, 2) and shape [3, 2, 1] """ - return ("Shifted Primed Tableaux of weight {} and shape {}" .format(self._weight,self._shape)) + return ( + "Shifted Primed Tableaux of weight {} and shape {}" .format( + self._weight, self._shape)) def __contains__(self, T): """ - TESTS:: + TEST:: - sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux([4,2],(1,4,1)) - True + sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux( + ....: [4,2],(1,4,1)) + True """ try: Tab = self.element_class(self, T) @@ -1363,13 +1399,14 @@ def _element_constructor_(self, T): sage: ShiftedPrimedTableaux([3],(2,1))([1,1]) Traceback (most recent call last): ... - ValueError: [1, 1] is not an element of Shifted Primed Tableaux of weight (2, 1) and shape [3] + ValueError: [1, 1] is not an element of Shifted Primed Tableaux + of weight (2, 1) and shape [3] """ try: Tab = self.element_class(self, T) except ValueError: - raise ValueError ("{} is not an element of Shifted Primed Tableaux" - .format(T)) + raise ValueError( + "{} is not an element of Shifted Primed Tableaux".format(T)) if Tab.shape() == self._shape and Tab.weight() == self._weight: return Tab @@ -1380,7 +1417,7 @@ def __iter__(self): """ Iterate over ``self``. - EXAMPLES:: + EXAMPLE:: sage: Tabs = ShiftedPrimedTableaux([3,2], (1,2,2)) sage: Tabs[:4] @@ -1391,56 +1428,62 @@ def __iter__(self): sage: len(list(Tabs)) 4 """ - if not self._shape.dominates(Partition(sorted(list(self._weight), key=int, reverse=True))): + if not self._shape.dominates(Partition(sorted(list(self._weight), + key=int, + reverse=True))): return yield - #TODO: More efficient algorithm with generators full_shape = self._shape sub_tab = [] tab_list_new = [[]] - for i,w in enumerate(self._weight): + for i, w in enumerate(self._weight): tab_list_old = tab_list_new tab_list_new = [] for sub_tab in tab_list_old: sub_shape = [len(sub_tab[r]) for r in range(len(sub_tab))] for strip in add_strip(sub_shape, full_shape, w): l = int(len(strip)/2) - if len(sub_shape)1 and isinstance(args[1],int): + if len(args) > 1 and isinstance(args[1], int): n = args[1] else: - if isinstance(args[0],int): + if isinstance(args[0], int): n = args[0] - if len(args)>1 and isinstance(args[1],(list,tuple,Partition)): + if (len(args) > 1 and + isinstance(args[1], (list, tuple, Partition))): shape = tuple(args[1]) if shape is None: raise ValueError('input size of tableaux as a list or a tuple') @@ -1449,35 +1492,39 @@ def __classcall_private__(cls,*args,**kwargs): n = sum(shape)-1 if n+1 < len(shape): - raise ValueError('crystal index should be greater or equal to the number of rows minus one') - return ShiftedPrimedTableauxCrystal(shape =shape, n=n) + raise ValueError('invalid crystal index') + return ShiftedPrimedTableauxCrystal(shape=shape, n=n) class ShiftedPrimedTableauxCrystal(SPTCrystal): - def __init__(self, shape=[4,2],n=2): - Parent.__init__(self, category = ClassicalCrystals()) + def __init__(self, shape=[4, 2], n=2): + Parent.__init__(self, category=ClassicalCrystals()) self.n = n self._shape = shape - self._cartan_type = CartanType(['A',n]) - self.module_generators = ShiftedPrimedTableaux(shape=shape,max_element=n+1).list_decreasing_weight() + self._cartan_type = CartanType(['A', n]) + self.module_generators = ShiftedPrimedTableaux( + shape=shape, + max_element=n+1).list_decreasing_weight() - def _repr_(self): - return ("Crystal of Shifted Primed Tableaux of type A_%s of shape "%(self.n) + str(self._shape)) + def _repr_(self): + return ("Crystal of Shifted Primed Tableaux of type A_%s of shape " + % (self.n) + str(self._shape)) #################### # Helper functions # #################### + def add_strip(sub_tab, full_tab, length): if sum(sub_tab)+length > sum(full_tab): raise ValueError("strip does not fit") - if len(sub_tab)==0: + if len(sub_tab) == 0: cliff_list = [] else: - cliff_list = [int(sub_tab[0]!=full_tab[0])] + cliff_list = [int(sub_tab[0] != full_tab[0])] - for row in range(1,len(sub_tab)): + for row in range(1, len(sub_tab)): if sub_tab[row] == full_tab[row]: cliff_list.append(0) elif sub_tab[row-1]-1 == sub_tab[row]: @@ -1485,56 +1532,61 @@ def add_strip(sub_tab, full_tab, length): else: cliff_list.append(1) - if len(sub_tab) j) for j in range(cliff)]) + primed_strip.extend([int(primed_list[i] > j) + for j in range(cliff)]) row += cliff plat_list = list() - if len(sub_tab)0: - plat_list.append(full_tab[0] - sub_tab[0]- primed_strip[0]) + if len(sub_tab) < len(full_tab) and len(sub_tab) != 0: + plat_list.append(min(sub_tab[-1] + primed_strip[-2] - 1, + full_tab[len(sub_tab)])) + for row in range(1, len(sub_tab))[::-1]: + plat_list.append( + min(sub_tab[row-1]+primed_strip[row-1]-1, full_tab[row]) + - sub_tab[row] - primed_strip[row]) + if len(sub_tab) > 0: + plat_list.append(full_tab[0] - sub_tab[0] - primed_strip[0]) else: plat_list.append(full_tab[0]) - if sum(plat_list) Date: Tue, 3 Oct 2017 18:16:00 -0700 Subject: [PATCH 364/740] Added Ascii and unicode --- src/sage/combinat/tableau_shifted_primed.py | 186 ++++++++++++++++++-- 1 file changed, 173 insertions(+), 13 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index 5a6bd5187d7..18c7defc00f 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -255,27 +255,48 @@ def _repr_(self): """ return repr([tuple(_) for _ in self]) - def pp(self): + def _repr_tab(self): """ - Print out a nice version of ``self``. + Return a nested list of strings representing the elements. + + TEST:: + + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t._repr_tab() + [[' 1 ', " 2'", ' 2 ', ' 2 '], [' 2 ', " 3'"]] + """ + max_len = len(str(self.max_element()))+1 + string_tab = [] + for i, row in enumerate(self): + string_row = [] + for val in row: + if int(val) == val: + string_row.append(' '*(max_len-len(str(int(val)))) + + str(int(val)) + ' ') + else: + string_row.append(' '*(max_len-len(str(int(val+.5)))) + + str(int(val+.5)) + "'") + string_tab.append(string_row) + return string_tab + + def _repr_diagram(self): + """ + Return a string representation of ``self`` as an array. EXAMPLES:: sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) - sage: t.pp() + sage: print(t._repr_diagram()) 1 2' 2 2 2 3' sage: t = ShiftedPrimedTableau([[10,'11p',11,11],[11,'12']]) - sage: t.pp() + sage: print(t._repr_diagram()) 10 11' 11 11 11 12 + """ - if [item for sublist in self for item in sublist] == []: - max_ind = 0 - else: - max_ind = max([item for sublist in self for item in sublist]) - max_len = len(str(round(max_ind))) - string_list = '' + max_len = len(str(self.max_element()))+2 + string_list = "" for i, row in enumerate(self): string_list += ' ' string_list += ' ' * i * (max_len) @@ -288,8 +309,148 @@ def pp(self): + ' '*(max_len-1 - len(str(int(val+.5))))) string_list += '\n' string_list = string_list[:-2] - print(string_list) - return + return string_list + + def _ascii_art_(self): + """ + Return ASCII representaion of a tableau. + + EXAMPLE:: + + sage: ascii_art(ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']])) + +---+---+---+---+ + | 1 | 2'| 2 | 2 | + +---+---+---+---+ + | 2 | 3'| + +---+---+ + + TEST:: + + sage: ascii_art(ShiftedPrimedTableau([])) + ++ + ++ + """ + from sage.typeset.ascii_art import AsciiArt + return AsciiArt(self._ascii_art_table(unicode=False).splitlines()) + + def _unicode_art_(self): + """ + Return a Unicode representation of a tableau. + + EXAMPLE:: + + sage: unicode_art(ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']])) + ┌───┬───┬───┬───┐ + │ 1 │ 2'│ 2 │ 2 │ + └───┼───┼───┼───┘ + │ 2 │ 3'│ + └───┴───┘ + + TEST:: + sage: unicode_art(ShiftedPrimedTableau([])) + ┌┐ + └┘ + """ + from sage.typeset.unicode_art import UnicodeArt + return UnicodeArt(self._ascii_art_table(unicode=True).splitlines()) + + def _ascii_art_table(self, unicode=False): + """ + TESTS:: + + sage: t = ShiftedPrimedTableau([[1,'2p',2],[2,'3p']]) + sage: print(t._ascii_art_table(unicode=True)) + ┌───┬───┬───┐ + │ 1 │ 2'│ 2 │ + └───┼───┼───┤ + │ 2 │ 3'│ + └───┴───┘ + sage: print(t._ascii_art_table()) + +---+---+---+ + | 1 | 2'| 2 | + +---+---+---+ + | 2 | 3'| + +---+---+ + sage: s = ShiftedPrimedTableau([[1,'2p',2, 23],[2,'30p']]) + sage: print(s._ascii_art_table(unicode=True)) + ┌────┬────┬────┬────┐ + │ 1 │ 2'│ 2 │ 23 │ + └────┼────┼────┼────┘ + │ 2 │ 30'│ + └────┴────┘ + sage: print(s._ascii_art_table(unicode=False)) + +----+----+----+----+ + | 1 | 2'| 2 | 23 | + +----+----+----+----+ + | 2 | 30'| + +----+----+ + + """ + if unicode: + import unicodedata + v = unicodedata.lookup('BOX DRAWINGS LIGHT VERTICAL') + h = unicodedata.lookup('BOX DRAWINGS LIGHT HORIZONTAL') + dl = unicodedata.lookup('BOX DRAWINGS LIGHT DOWN AND LEFT') + dr = unicodedata.lookup('BOX DRAWINGS LIGHT DOWN AND RIGHT') + ul = unicodedata.lookup('BOX DRAWINGS LIGHT UP AND LEFT') + ur = unicodedata.lookup('BOX DRAWINGS LIGHT UP AND RIGHT') + vl = unicodedata.lookup('BOX DRAWINGS LIGHT VERTICAL AND LEFT') + uh = unicodedata.lookup('BOX DRAWINGS LIGHT UP AND HORIZONTAL') + dh = unicodedata.lookup('BOX DRAWINGS LIGHT DOWN AND HORIZONTAL') + vh = unicodedata.lookup( + 'BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL') + else: + v = '|' + h = '-' + dl = dr = ul = ur = vl = uh = dh = vh = '+' + + if self.shape() == []: + return dr + dl + '\n' + ur + ul + + # Get the widths of the columns + str_tab = self._repr_tab() + width = len(str_tab[0][0]) + str_list = [dr + (h*width + dh)*(len(str_tab[0])-1) + h*width + dl] + for nrow, row in enumerate(str_tab): + l1 = " " * (width+1) * nrow + l2 = " " * (width+1) * nrow + n = len(str_tab[nrow+1]) if nrow+1 < len(str_tab) else -1 + for i, e in enumerate(row): + if i == 0: + l1 += ur + h*width + elif i <= n+1: + l1 += vh + h*width + else: + l1 += uh + h*width + if unicode: + l2 += u"{}{:^{width}}".format(v, e, width=width) + else: + l2 += "{}{:^{width}}".format(v, e, width=width) + if i <= n: + l1 += vl + else: + l1 += ul + l2 += v + str_list.append(l2) + str_list.append(l1) + return "\n".join(str_list) + + def pp(self): + """ + Print out a nice version of ``self``. + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t.pp() + 1 2' 2 2 + 2 3' + sage: t = ShiftedPrimedTableau([[10,'11p',11,11],[11,'12']]) + sage: t.pp() + 10 11' 11 11 + 11 12 + """ + print(self._repr_diagram()) def _latex_(self): r""" @@ -316,7 +477,6 @@ def _latex_(self): else: num_list.append(str(int(let+0.5))+"'") L.append(num_list) - return tex_from_array(L) def max_element(self): From 271f6ec79d1dd5e700816a0a43dd3ed3751ecf92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 4 Oct 2017 15:25:00 +0800 Subject: [PATCH 365/740] Use a more specialized class for non-classical discrete valuations on function fields so we get extensions() to work for free --- src/sage/rings/function_field/function_field_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/function_field_valuation.py b/src/sage/rings/function_field/function_field_valuation.py index 78cdd34bf49..1f0fec332af 100644 --- a/src/sage/rings/function_field/function_field_valuation.py +++ b/src/sage/rings/function_field/function_field_valuation.py @@ -427,7 +427,7 @@ def create_object(self, version, key, **extra_args): from sage.structure.dynamic_class import dynamic_class clazz = NonClassicalRationalFunctionFieldValuation if valuation.is_discrete_valuation(): - clazz = dynamic_class("NonClassicalRationalFunctionFieldValuation_discrete", (clazz, DiscreteValuation)) + clazz = dynamic_class("NonClassicalRationalFunctionFieldValuation_discrete", (clazz, DiscreteFunctionFieldValuation_base)) else: clazz = dynamic_class("NonClassicalRationalFunctionFieldValuation_negative_infinite", (clazz, NegativeInfiniteDiscretePseudoValuation)) return parent.__make_element_class__(clazz)(parent, valuation) From bbca3ce8d2f732530695e2ca1c99338e17078aab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 4 Oct 2017 15:30:50 +0800 Subject: [PATCH 366/740] fill in doctest output --- src/sage/rings/function_field/function_field_valuation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/rings/function_field/function_field_valuation.py b/src/sage/rings/function_field/function_field_valuation.py index 1f0fec332af..a65bb66fb03 100644 --- a/src/sage/rings/function_field/function_field_valuation.py +++ b/src/sage/rings/function_field/function_field_valuation.py @@ -518,6 +518,7 @@ def extensions(self, L): sage: R. = K[] sage: L. = K.extension(y^3 - x^4 - 1) sage: v.extensions(L) + [2-adic valuation] """ K = self.domain() From 20868e9de4fbaa180a0581f1552c02e199ee4b49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 5 Oct 2017 13:57:43 +0800 Subject: [PATCH 367/740] bring equivalence_decompositions back to non-fields --- src/sage/rings/padics/padic_valuation.py | 56 ++++++++++- .../rings/valuation/augmented_valuation.py | 17 +++- src/sage/rings/valuation/gauss_valuation.py | 8 +- .../rings/valuation/inductive_valuation.py | 94 ++++++++++++++++++- src/sage/rings/valuation/valuation_space.py | 68 ++++++++++++++ 5 files changed, 232 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index a252e28c05a..32cc26a81c8 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -15,13 +15,14 @@ sage: Zp(11).valuation() 11-adic valuation -These valuations can then, e.g., be used to compute factorizations in the -completion of a ring:: +These valuations can then, e.g., be used to compute approximate factorizations +in the completion of a ring:: sage: v = ZZ.valuation(2) sage: R. = ZZ[] sage: f = x^5 + x^4 + x^3 + x^2 + x - 1 - sage: v.montes_factorization(f) + sage: v.montes_factorization(f, required_precision=20) + (x + 676027) * (x^4 + 372550*x^3 + 464863*x^2 + 385052*x + 297869) AUTHORS: @@ -1208,6 +1209,55 @@ def simplify(self, x, error=None, force=False, size_heuristic_bound=32): return self.domain()(reduced.lift()) + def inverse(self, x, precision): + r""" + Return an approximate inverse of ``x``. + + The element returned is such that the product differs from 1 by an + element of valuation at least ``precision``. + + INPUT: + + - ``x`` -- an element in the domain of this valuation + + - ``precision`` -- a rational or infinity + + EXAMPLES:: + + sage: v = ZZ.valuation(2) + sage: x = 3 + sage: y = v.inverse(3, 2); y + 3 + sage: x*y - 1 + 8 + + This might not be possible for elements of positive valuation:: + + sage: v.inverse(2, 2) + Traceback (most recent call last): + ... + ValueError: element has no approximate inverse in this ring + + Unless the precision is very small:: + + sage: v.inverse(2, 0) + 1 + + """ + if not x.is_zero(): + y = ~x + if y in self.domain(): + return y + if precision <= 0: + return self.domain().one() + + from sage.rings.all import infinity + if self(x) > 0 or precision is infinity: + raise ValueError("element has no approximate inverse in this ring") + + from sage.rings.all import ZpFM + return self.domain()(~ZpFM(self.p(), precision.ceil())(x)) + class pAdicFromLimitValuation(FiniteExtensionFromLimitValuation, pAdicValuation_base): r""" diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index dae09e8877d..ddcada1f3d2 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -1712,7 +1712,7 @@ def valuations(self, f, coefficients=None, call_error=False): lowest_valuation = ret yield ret - def simplify(self, f, error=None, force=False, effective_degree=None, size_heuristic_bound=32): + def simplify(self, f, error=None, force=False, effective_degree=None, size_heuristic_bound=32, phiadic=False): r""" Return a simplified version of ``f``. @@ -1739,6 +1739,11 @@ def simplify(self, f, error=None, force=False, effective_degree=None, size_heuri factor by which the coefficients need to shrink to perform an actual simplification (default: 32) + - ``phiadic`` -- whether to simplify the coefficients in the + `\phi`-adic expansion recursively. This tends to be slower and + sometimes leads to very huge coefficients in the `x`-adic + expansion (default: ``False``.) + EXAMPLES:: sage: R. = Qq(4, 5) @@ -1760,9 +1765,15 @@ def simplify(self, f, error=None, force=False, effective_degree=None, size_heuri return f if error is None: - error = self.upper_bound(f) + # if the caller was sure that we should simplify, then we should try to do the best simplification possible + error = self(f) if force else self.upper_bound(f) - return self._base_valuation.simplify(f, error=error, force=force) + if phiadic: + coefficients = list(self.coefficients(f)) + valuations = list(self.valuations(f, coefficients=coefficients)) + return self.domain().change_ring(self.domain())([0 if valuations[i] > error else self._base_valuation.simplify(c, error=error-i*self._mu, force=force, phiadic=True) for (i,c) in enumerate(coefficients)])(self.phi()) + else: + return self._base_valuation.simplify(f, error=error, force=force) def lower_bound(self, f): r""" diff --git a/src/sage/rings/valuation/gauss_valuation.py b/src/sage/rings/valuation/gauss_valuation.py index 5b705052ba4..5f6cd5b6b9b 100644 --- a/src/sage/rings/valuation/gauss_valuation.py +++ b/src/sage/rings/valuation/gauss_valuation.py @@ -724,7 +724,7 @@ def _relative_size(self, f): """ return self._base_valuation._relative_size(f[0]) - def simplify(self, f, error=None, force=False, size_heuristic_bound=32, effective_degree=None): + def simplify(self, f, error=None, force=False, size_heuristic_bound=32, effective_degree=None, phiadic=True): r""" Return a simplified version of ``f``. @@ -750,6 +750,9 @@ def simplify(self, f, error=None, force=False, size_heuristic_bound=32, effectiv factor by which the coefficients need to shrink to perform an actual simplification (default: 32) + - ``phiadic`` -- whether to simplify in the `x`-adic expansion; the + parameter is ignored as no other simplification is implemented + EXAMPLES:: sage: R. = Qq(4, 5) @@ -770,7 +773,8 @@ def simplify(self, f, error=None, force=False, size_heuristic_bound=32, effectiv return f if error is None: - error = self.upper_bound(f) + # if the caller was sure that we should simplify, then we should try to do the best simplification possible + error = self(f) if force else self.uppper_bound(f) return f.map_coefficients(lambda c: self._base_valuation.simplify(c, error=error, force=force)) diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index afe8f4ad73b..13fc8b9c3d2 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -1199,10 +1199,11 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi return Factorization([], unit=f, sort=False) if not self.domain().base_ring().is_field(): - domain = self.domain().change_ring(self.domain().base_ring().fraction_field()) + nonfractions = self.domain().base_ring() + domain = self.domain().change_ring(nonfractions.fraction_field()) v = self.extension(domain) ret = v.equivalence_decomposition(v.domain()(f)) - return Factorization([(g.change_ring(self.domain().base_ring()),e) for g,e in ret], unit=ret.unit().change_ring(self.domain().base_ring()), sort=False) + return Factorization([(self._eliminate_denominators(g), e) for (g,e) in ret], unit=self._eliminate_denominators(ret.unit())) valuation, phi_divides, F = self._equivalence_reduction(f, coefficients=coefficients, valuations=valuations, degree_bound=degree_bound) F = F.factor() @@ -1231,7 +1232,7 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi for g,e in F: v_g = self(g) unit *= self._pow(self.equivalence_unit(-v_g, reciprocal=True), e, error=-v_g*e, effective_degree=0) - unit = self.simplify(unit) + unit = self.simplify(unit, effective_degree=0) if phi_divides: for i,(g,e) in enumerate(F): @@ -1360,6 +1361,93 @@ def lift_to_key(self, F): """ + def _eliminate_denominators(self, f): + r""" + Return a polynomial in the domain of this valuation that + :meth:`is_equivalent` to ``f``. + + INPUT: + + - ``f`` -- a polynomial with coefficients in the fraction field of the + base ring of the domain of this valuation. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: v = GaussValuation(R, ZZ.valuation(2)) + sage: v._eliminate_denominators(x/3) + x + + In general such a polynomial may not exist:: + + sage: w = v.augmentation(x, 1) + sage: w._eliminate_denominators(x/2) + Traceback (most recent call last): + ... + ValueError: element has no approximate inverse in this ring + + In general it exists iff the coefficients of minimal valuation in the + `\phi`-adic expansion of ``f`` do not have denominators of positive + valuation and if the same is true for these coefficients in their + expansion; at least if the coefficient ring's residue ring is already a + field:: + + sage: w._eliminate_denominators(x^3/2 + x) + x + + """ + if f in self.domain(): + return self.domain()(f) + + nonfractions = self.domain().base_ring() + fractions = nonfractions.fraction_field() + + extended_domain = self.domain().change_ring(fractions) + + g = extended_domain.coerce(f) + + w = self.extension(extended_domain) + # drop coefficients whose valuation is not minimal (recursively) + valuation = w(g) + g = w.simplify(g, error=valuation, force=True, phiadic=True) + + nonfraction_valuation = self.restriction(nonfractions) + # if this fails then there is no equivalent polynomial in the domain of this valuation + ret = g.map_coefficients( + lambda c: c.numerator()*nonfraction_valuation.inverse(c.denominator(), + valuation + nonfraction_valuation(c.denominator()) - nonfraction_valuation(c.numerator()) + nonfraction_valuation.value_group().gen()), + nonfractions) + assert w.is_equivalent(f, ret) + return ret + + + def _test_eliminate_denominators(self, **options): + r""" + Test the correctness of :meth:`_eliminate_denominators`. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: v = GaussValuation(R, ZZ.valuation(2)) + sage: v._test_eliminate_denominators() + + """ + tester = self._tester(**options) + + nonfractions = self.domain().base_ring() + fractions = nonfractions.fraction_field() + extended_domain = self.domain().change_ring(fractions) + w = self.extension(extended_domain) + + S = tester.some_elements(w.domain().some_elements()) + for f in S: + try: + g = self._eliminate_denominators(f) + except ValueError: + continue + tester.assertTrue(g.parent() is self.domain()) + tester.assertTrue(w.is_equivalent(f, g)) + def _test_lift_to_key(self, **options): r""" Test the correctness of :meth:`lift_to_key`. diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index 92c3a5f309b..8a3c4788b80 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -991,6 +991,47 @@ def upper_bound(self, x): """ return self(x) + def inverse(self, x, precision): + r""" + Return an approximate inverse of ``x``. + + The element returned is such that the product differs from 1 by an + element of valuation at least ``precision``. + + INPUT: + + - ``x`` -- an element in the domain of this valuation + + - ``precision`` -- a rational or infinity + + EXAMPLES:: + + sage: v = ZZ.valuation(2) + sage: x = 3 + sage: y = v.inverse(3, 2); y + 3 + sage: x*y - 1 + 8 + + This might not be possible for elements of positive valuation:: + + sage: v.inverse(2, 2) + Traceback (most recent call last): + ... + ValueError: element has no approximate inverse in this ring + + Of course this always works over fields:: + + sage: v = QQ.valuation(2) + sage: v.inverse(2, 2) + 1/2 + + """ + try: + return x.inverse_of_unit() + except: + raise NotImplementedError("can not compute approximate inverse with respect to this valuation") + def _relative_size(self, x): r""" Return an estimate on the coefficient size of ``x``. @@ -1556,6 +1597,33 @@ def _test_le(self, **options): tester.assertLessEqual(TrivialValuation(self.domain()), self) tester.assertGreaterEqual(TrivialPseudoValuation(self.domain()), self) + def _test_inverse(self, **options): + r""" + Check the correctness of :meth:`inverse`. + + TESTS:: + + sage: v = QQ.valuation(5) + sage: v._test_inverse() + + """ + tester = self._tester(**options) + + for x in tester.some_elements(self.domain().some_elements()): + from sage.rings.all import infinity + for prec in (0, 1, 42, infinity): + try: + y = self.inverse(x, prec) + except ValueError, NotImplementedError: + tester.assertNotEqual(self(x), 0) + tester.assertFalse(x.is_unit()) + continue + + tester.assertTrue(y.parent() is self.domain()) + if self.domain().is_exact(): + tester.assertGreaterEqual(self(x*y - 1), prec) + + from sage.categories.action import Action class ScaleAction(Action): r""" From 27351d9440667c18e64c5baa7e26c26e24e07918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 5 Oct 2017 14:19:22 +0800 Subject: [PATCH 368/740] Bring back equivalence_decompositions into local rings where there is no concept of a denominator. Note that we need assume_squarefree in the example because sage fails to detect the squarefreeness in that case. --- src/sage/rings/valuation/inductive_valuation.py | 3 +++ src/sage/rings/valuation/valuation.py | 8 +++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index 13fc8b9c3d2..4d4dc7d2033 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -1411,6 +1411,9 @@ def _eliminate_denominators(self, f): valuation = w(g) g = w.simplify(g, error=valuation, force=True, phiadic=True) + if g in self.domain(): + return self.domain()(g) + nonfraction_valuation = self.restriction(nonfractions) # if this fails then there is no equivalent polynomial in the domain of this valuation ret = g.map_coefficients( diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index a303adb7281..d156db34240 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -990,11 +990,12 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No In this example, ``f`` factors as three factors of degree 50 over an unramified extension:: - sage: R. = ZqFM(125, 500) + sage: R. = ZqFM(125) sage: S. = R[] sage: f = (x^6+2)^25 + 5 sage: v = R.valuation() - sage: v.montes_factorization(f) + sage: v.montes_factorization(f, assume_squarefree=True, required_precision=0) + ((1 + O(5^20))*x^50 + (3*5 + O(5^20))*x^45 + (5 + O(5^20))*x^40 + (5 + O(5^20))*x^30 + (3 + 4*5 + O(5^20))*x^25 + (3*5 + O(5^20))*x^20 + (2*5 + O(5^20))*x^10 + (3*5 + O(5^20))*x^5 + (4*5 + O(5^20))*x + 3 + 5 + O(5^20)) * ((1 + O(5^20))*x^50 + (2*5 + O(5^20))*x^45 + (5 + O(5^20))*x^40 + (5 + O(5^20))*x^30 + (2 + O(5^20))*x^25 + (3*5 + O(5^20))*x^20 + (2*5 + O(5^20))*x^10 + (2*5 + O(5^20))*x^5 + (5 + O(5^20))*x + 3 + 5 + O(5^20)) * ((1 + O(5^20))*x^50 + (3*5 + O(5^20))*x^40 + (3*5 + O(5^20))*x^30 + (4*5 + O(5^20))*x^20 + (5 + O(5^20))*x^10 + 3 + 5 + O(5^20)) In this case, ``f`` factors into degrees 1, 2, and 5 over a totally ramified extension:: @@ -1003,7 +1004,8 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No sage: R. = R.extension(w^3 + 5) sage: S. = R[] sage: f = (x^3 + 5)*(x^5 + w) + 625 - sage: v.montes_factorization(f) + sage: v = R.valuation() + sage: v.montes_factorization(f, assume_squarefree=True, required_precision=0) REFERENCES: From 76df6901142e9484c6969d3b4d5afeed81482982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 5 Oct 2017 15:16:33 +0800 Subject: [PATCH 369/740] Fill in doctest output works with #23965 merged in --- src/sage/rings/valuation/valuation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index d156db34240..e0e4054de19 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -999,13 +999,14 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No In this case, ``f`` factors into degrees 1, 2, and 5 over a totally ramified extension:: - sage: R = Zp(5, 50) + sage: R = Zp(5) sage: S. = R[] sage: R. = R.extension(w^3 + 5) sage: S. = R[] sage: f = (x^3 + 5)*(x^5 + w) + 625 sage: v = R.valuation() sage: v.montes_factorization(f, assume_squarefree=True, required_precision=0) + ((1 + O(w^60))*x + 4*w + O(w^60)) * ((1 + O(w^60))*x^2 + (w + O(w^60))*x + w^2 + O(w^60)) * ((1 + O(w^60))*x^5 + w + O(w^60)) REFERENCES: From a29d30a7cf66a7b42edc166272e39c2db71f2407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 5 Oct 2017 16:09:52 +0800 Subject: [PATCH 370/740] Fill in correct doctest output 2 factors in this number field, so there should be an exception - just a meaningful one. That's what this test is about. --- src/sage/rings/padics/padic_valuation.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 32cc26a81c8..97c5b117a6e 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -724,6 +724,9 @@ def extensions(self, ring): sage: R. = QQ[] sage: L. = NumberField(x^4 + 2*x^3 + 2*x^2 + 8) sage: QQ.valuation(2).extensions(L) + Traceback (most recent call last): + ... + ValueError: The valuation [ Gauss valuation induced by 2-adic valuation, v(x) = 1/2 ] does not approximate a unique extension of 2-adic valuation with respect to x^4 + 2*x^3 + 2*x^2 + 8 A case where the extension was incorrect at some point:: From d9b94870dad0e5079a6eef07533af6514cdc0ca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 5 Oct 2017 16:23:25 +0800 Subject: [PATCH 371/740] Fix computations in iterated extensions of number fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This used to return -1/2…which was wrong as well as 2 is totally ramified here. --- src/sage/rings/padics/padic_valuation.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 97c5b117a6e..fe07e7ba4cb 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -735,7 +735,7 @@ def extensions(self, ring): sage: M. = L.extension(x^2 + 1) sage: w = v.extension(L).extension(M) sage: w(w.uniformizer()) - 1/2 + 1/4 """ if self.domain() is ring: @@ -1302,8 +1302,19 @@ def _to_base_domain(self, f): sage: v._to_base_domain(I) x + TESTS: + + Check that this also works for relative extensions:: + + sage: v = QQ.valuation(2) + sage: L. = NumberField(x^2 + 2) + sage: M. = L.extension(x^2 + 1) + sage: w = v.extension(L).extension(M) + sage: w._to_base_domain(b) + x + """ - polynomial = f.polynomial() if hasattr(f,'polynomial') else f.lift() + polynomial = f.lift() return polynomial(self._base_valuation.domain().gen()) def _from_base_domain(self, f): From 6c5c12b4e18253522eb6ea52f106f7601de27b0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 5 Oct 2017 16:33:35 +0800 Subject: [PATCH 372/740] fill in doctest output --- src/sage/rings/valuation/valuation.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index e0e4054de19..82c8c0069d0 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -706,6 +706,8 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: f = x^4 + 234 sage: v.mac_lane_approximants(f) # is_squarefree() not implemented in this ring sage: v.mac_lane_approximants(f, assume_squarefree=True) + [[ Gauss valuation induced by 3-adic valuation, v((1 + O(3^7))*x + (0 + O(3^7))) = 1/2, v((1 + O(3^7))*x^2 + (0 + O(3^7))*x + (6 + O(3^7))) = 2 ], + [ Gauss valuation induced by 3-adic valuation, v((1 + O(3^7))*x + (0 + O(3^7))) = 1/2, v((1 + O(3^7))*x^2 + (0 + O(3^7))*x + (3 + O(3^7))) = 4 ]] :: From 41277b3f9070fe51be20cdc05f15f4c852e12205 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 5 Oct 2017 16:46:13 +0800 Subject: [PATCH 373/740] Fix factorization doctests with the values that I found in the OM code --- src/sage/rings/valuation/valuation.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 82c8c0069d0..896e98eed40 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -704,18 +704,17 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: S. = R[] sage: v = R.valuation() sage: f = x^4 + 234 - sage: v.mac_lane_approximants(f) # is_squarefree() not implemented in this ring - sage: v.mac_lane_approximants(f, assume_squarefree=True) - [[ Gauss valuation induced by 3-adic valuation, v((1 + O(3^7))*x + (0 + O(3^7))) = 1/2, v((1 + O(3^7))*x^2 + (0 + O(3^7))*x + (6 + O(3^7))) = 2 ], - [ Gauss valuation induced by 3-adic valuation, v((1 + O(3^7))*x + (0 + O(3^7))) = 1/2, v((1 + O(3^7))*x^2 + (0 + O(3^7))*x + (3 + O(3^7))) = 4 ]] + sage: len(v.mac_lane_approximants(f, assume_squarefree=True)) # is_squarefree() is not properly implemented yet + 2 :: - sage: R = ZpFM(2, 500, print_mode='terse') + sage: R = ZpFM(2, 50, print_mode='terse') sage: S. = R[] + sage: f = (x^32 + 16)*(x^32 + 16 + 2^16*x^2) + 2^34 sage: v = R.valuation() - sage: v.mac_lane_approximants(f) # is_squarefree() is not yet implemented on this ring - sage: v.mac_lane_approximants(f, assume_squarefree=True) + sage: len(v.mac_lane_approximants(f, assume_squarefree=True)) # is_squarefree() is not properly implemented yet + 2 A case that triggered an assertion at some point:: From 09b64022a765aa6116f10aaf846f410a074d4821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 5 Oct 2017 16:53:55 +0800 Subject: [PATCH 374/740] stable factorization output --- src/sage/rings/valuation/valuation.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 896e98eed40..a2088299463 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -995,8 +995,11 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No sage: S. = R[] sage: f = (x^6+2)^25 + 5 sage: v = R.valuation() - sage: v.montes_factorization(f, assume_squarefree=True, required_precision=0) - ((1 + O(5^20))*x^50 + (3*5 + O(5^20))*x^45 + (5 + O(5^20))*x^40 + (5 + O(5^20))*x^30 + (3 + 4*5 + O(5^20))*x^25 + (3*5 + O(5^20))*x^20 + (2*5 + O(5^20))*x^10 + (3*5 + O(5^20))*x^5 + (4*5 + O(5^20))*x + 3 + 5 + O(5^20)) * ((1 + O(5^20))*x^50 + (2*5 + O(5^20))*x^45 + (5 + O(5^20))*x^40 + (5 + O(5^20))*x^30 + (2 + O(5^20))*x^25 + (3*5 + O(5^20))*x^20 + (2*5 + O(5^20))*x^10 + (2*5 + O(5^20))*x^5 + (5 + O(5^20))*x + 3 + 5 + O(5^20)) * ((1 + O(5^20))*x^50 + (3*5 + O(5^20))*x^40 + (3*5 + O(5^20))*x^30 + (4*5 + O(5^20))*x^20 + (5 + O(5^20))*x^10 + 3 + 5 + O(5^20)) + sage: sorted(v.montes_factorization(f, assume_squarefree=True, required_precision=0), key=str) + [((1 + O(5^20))*x^50 + (2*5 + O(5^20))*x^45 + (5 + O(5^20))*x^40 + (5 + O(5^20))*x^30 + (2 + O(5^20))*x^25 + (3*5 + O(5^20))*x^20 + (2*5 + O(5^20))*x^10 + (2*5 + O(5^20))*x^5 + (5 + O(5^20))*x + 3 + 5 + O(5^20), 1), + ((1 + O(5^20))*x^50 + (3*5 + O(5^20))*x^40 + (3*5 + O(5^20))*x^30 + (4*5 + O(5^20))*x^20 + (5 + O(5^20))*x^10 + 3 + 5 + O(5^20), 1), + ((1 + O(5^20))*x^50 + (3*5 + O(5^20))*x^45 + (5 + O(5^20))*x^40 + (5 + O(5^20))*x^30 + (3 + 4*5 + O(5^20))*x^25 + (3*5 + O(5^20))*x^20 + (2*5 + O(5^20))*x^10 + (3*5 + O(5^20))*x^5 + (4*5 + O(5^20))*x + 3 + 5 + O(5^20), 1)] + In this case, ``f`` factors into degrees 1, 2, and 5 over a totally ramified extension:: @@ -1006,8 +1009,10 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No sage: S. = R[] sage: f = (x^3 + 5)*(x^5 + w) + 625 sage: v = R.valuation() - sage: v.montes_factorization(f, assume_squarefree=True, required_precision=0) - ((1 + O(w^60))*x + 4*w + O(w^60)) * ((1 + O(w^60))*x^2 + (w + O(w^60))*x + w^2 + O(w^60)) * ((1 + O(w^60))*x^5 + w + O(w^60)) + sage: sorted(v.montes_factorization(f, assume_squarefree=True, required_precision=0), key=str) + [((1 + O(w^60))*x + 4*w + O(w^60), 1), + ((1 + O(w^60))*x^2 + (w + O(w^60))*x + w^2 + O(w^60), 1), + ((1 + O(w^60))*x^5 + w + O(w^60), 1)] REFERENCES: From dfad6f47d2e8edf2e7cb694f29cc0c4316a4dc1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 5 Oct 2017 19:05:06 +0800 Subject: [PATCH 375/740] Fix valuation.lift(report_coefficients=True) so it actually reports the coefficients in the phi-adic development and not something of possibly higher degree --- src/sage/rings/valuation/augmented_valuation.py | 13 ++++++++----- src/sage/rings/valuation/valuation.py | 1 + 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index ddcada1f3d2..19c4693575a 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -1346,8 +1346,9 @@ def lift(self, F, report_coefficients=False): - ``F`` -- an element of the :meth:`residue_ring` - ``report_coefficients`` -- whether to return the coefficients of the - :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.phi`-adic expansion or the actual polynomial (default: - ``False``, i.e., return the polynomial) + :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.phi`-adic + expansion or the actual polynomial (default: ``False``, i.e., return + the polynomial) OUTPUT: @@ -1429,6 +1430,8 @@ def lift(self, F, report_coefficients=False): # now we undo the factors of Q^i (the if else is necessary to handle the case when mu is infinity, i.e., when _Q_reciprocal() is undefined) coeffs = [ (c if i == 0 else c*self._Q_reciprocal(i)).map_coefficients(_lift_to_maximal_precision) for i,c in enumerate(coeffs) ] + # reduce the coefficients mod phi; the part that exceeds phi has no effect on the reduction of the coefficient + coeffs = [ self.coefficients(c).next() for c in coeffs ] if report_coefficients: return coeffs @@ -1514,9 +1517,9 @@ def lift_to_key(self, F, check=True): coefficients = self.lift(F, report_coefficients=True)[:-1] coefficients = [c*self._Q(F.degree()) for i,c in enumerate(coefficients)] + [self.domain().one()] if len(coefficients) >= 2: - # After reduction modulo phi, the second-highest coefficient could spill over into the - # highest coefficient (which is a constant one) so we need to mod it - # away. + # In the phi-adic development, the second-highest coefficient could + # spill over into the highest coefficient (which is a constant one) + # so we need to mod it away. # This can not happen for other coefficients because self._Q() has # degree at most the degree of phi. coefficients[-2] %= self.phi() diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index a2088299463..2f2f0394e69 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -722,6 +722,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: R. = QQ[] sage: f = x^36 + 60552000*x^33 + 268157412*x^30 + 173881701*x^27 + 266324841*x^24 + 83125683*x^21 + 111803814*x^18 + 31925826*x^15 + 205726716*x^12 +17990262*x^9 + 351459648*x^6 + 127014399*x^3 + 359254116 sage: v.mac_lane_approximants(f) + [[ Gauss valuation induced by 3-adic valuation, v(x) = 1/3, v(x^3 + 6) = 3/2, v(x^12 + 24*x^9 + 216*x^6 + 864*x^3 + 2025) = 13/2, v(x^36 + 60552000*x^33 + 268157412*x^30 + 173881701*x^27 + 266324841*x^24 + 83125683*x^21 + 111803814*x^18 + 31925826*x^15 + 205726716*x^12 + 17990262*x^9 + 351459648*x^6 + 127014399*x^3 + 359254116) = +Infinity ]] """ R = G.parent() From 8767fdc5d81f83172e6daac6fd1aa143fa7cb41c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 5 Oct 2017 19:20:38 +0800 Subject: [PATCH 376/740] fix docbuild --- src/sage/rings/function_field/function_field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 0c63f56f423..afd282317e2 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -584,7 +584,7 @@ def valuation(self, prime): or a valuation on another function field together with information for isomorphisms to and from that function field - EXAMPLES:: + EXAMPLES: We create a valuation that correspond to a finite rational place of a function field:: From ecfe1bc9d6b021d0066269a9c21d7022e9e28e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 5 Oct 2017 20:19:14 +0800 Subject: [PATCH 377/740] fix doc references --- .../function_field/function_field_valuation.py | 2 +- src/sage/rings/padics/padic_valuation.py | 14 +++++++------- src/sage/rings/valuation/augmented_valuation.py | 6 +++--- src/sage/rings/valuation/inductive_valuation.py | 2 +- src/sage/rings/valuation/limit_valuation.py | 4 ++-- src/sage/rings/valuation/mapped_valuation.py | 2 +- src/sage/rings/valuation/scaled_valuation.py | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/sage/rings/function_field/function_field_valuation.py b/src/sage/rings/function_field/function_field_valuation.py index a65bb66fb03..31e87e07f87 100644 --- a/src/sage/rings/function_field/function_field_valuation.py +++ b/src/sage/rings/function_field/function_field_valuation.py @@ -172,7 +172,7 @@ class FunctionFieldValuationFactory(UniqueFactory): sage: v(x - 1) 1 - See :meth:`function_field.FunctionField.valuation` for further examples. + See :meth:`sage.rings.function_field.function_field.FunctionField.valuation` for further examples. """ def create_key_and_extra_args(self, domain, prime): diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index fe07e7ba4cb..b15aad5799c 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -450,7 +450,7 @@ def reduce(self, x): OUTPUT: - An element of the :meth:`residue_field`. + An element of the :meth:`~sage.rings.valuation.valuation_space.DiscretePseudoValuationSpace.ElementMethods.residue_field`. EXAMPLES:: @@ -472,7 +472,7 @@ def lift(self, x): INPUT: - - ``x`` -- an element of the :meth:`residue_field` + - ``x`` -- an element of the :meth:`~sage.rings.valuation.valuation_space.DiscretePseudoValuationSpace.ElementMethods.residue_field` EXAMPLES:: @@ -583,7 +583,7 @@ def is_totally_ramified(self, G, include_steps=False, assume_squarefree=False): ALGORITHM: - This is a simplified version of :meth:`mac_lane_approximants`. + This is a simplified version of :meth:`sage.rings.valuation.valuation.DiscreteValuation.mac_lane_approximants`. EXAMPLES:: @@ -846,7 +846,7 @@ def reduce(self, x): OUTPUT: - An element of the :meth:`residue_field`. + An element of the :meth:`~sage.rings.valuation.valuation_space.DiscretePseudoValuationSpace.ElementMethods.residue_field`. EXAMPLES:: @@ -860,12 +860,12 @@ def reduce(self, x): def lift(self, x): """ - Lift ``x`` from the :meth:`residue_field` to the :meth:`domain` of this + Lift ``x`` from the :meth:`~sage.rings.valuation.valuation_space.DiscretePseudoValuationSpace.ElementMethods.residue_field` to the domain of this valuation. INPUT: - - ``x`` -- an element of the :meth:`residue_field` + - ``x`` -- an element of the residue field of this valuation EXAMPLES:: @@ -898,7 +898,7 @@ def element_with_valuation(self, v): INPUT: - - ``v`` -- an element of the :meth:`value_semigroup` of this valuation + - ``v`` -- an element of the :meth:`pAdicValuation_base.value_semigroup` of this valuation EXAMPLES:: diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index 19c4693575a..2e94b6a3bbb 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -261,7 +261,7 @@ class AugmentedValuation_base(InductiveValuation): - ``v`` -- a :class:`~sage.rings.valuation.inductive_valuation.InductiveValuation` on a polynomial ring - - ``phi`` -- a :meth:`key polynomial ` over ``v`` + - ``phi`` -- a :meth:`key polynomial ` over ``v`` - ``mu`` -- a rational number such that ``mu > v(phi)`` or ``infinity`` @@ -1205,7 +1205,7 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations ALGORITHM: - We follow the algorithm given in the proof of Theorem 12.1 of [Mac1936]_: + We follow the algorithm given in the proof of Theorem 12.1 of [Mac1936I]_: If ``f`` has positive valuation, the reduction is simply zero. Otherwise, let `f=\sum f_i\phi^i` be the expansion of `f`, as computed by @@ -1463,7 +1463,7 @@ def lift_to_key(self, F, check=True): ALGORITHM: - We follow the algorithm described in Theorem 13.1 [Mac1936]_ which, after + We follow the algorithm described in Theorem 13.1 [Mac1936I]_ which, after a :meth:`lift` of ``F``, essentially shifts the valuations of all terms in the `\phi`-adic expansion up and then kills the leading coefficient. diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index 4d4dc7d2033..94382dea37c 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -212,7 +212,7 @@ def equivalence_reciprocal(self, f, coefficients=None, valuations=None, check=Tr @cached_method def mu(self): r""" - Return the valuation of :meth:`phi`. + Return the valuation of :meth:`~sage.rings.valuation.developing_valuation.DevelopingValuation.phi`. EXAMPLES:: diff --git a/src/sage/rings/valuation/limit_valuation.py b/src/sage/rings/valuation/limit_valuation.py index 92c01ef0b42..509c89d5119 100644 --- a/src/sage/rings/valuation/limit_valuation.py +++ b/src/sage/rings/valuation/limit_valuation.py @@ -202,7 +202,7 @@ def __init__(self, parent, approximation): def reduce(self, f, check=True): r""" - Return the reduction of ``f`` as an element of :meth:`residue_ring`. + Return the reduction of ``f`` as an element of the :meth:`~sage.rings.valuation.valuation_space.DiscretePseudoValuationSpace.ElementMethods.residue_ring`. INPUT: @@ -420,7 +420,7 @@ def extensions(self, ring): def lift(self, F): r""" - Return a lift of ``F`` from the :meth:`residue_ring` to the domain of + Return a lift of ``F`` from the :meth:`~sage.rings.valuation.valuation_space.DiscretePseudoValuationSpace.ElementMethods.residue_ring` to the domain of this valuatiion. EXAMPLES:: diff --git a/src/sage/rings/valuation/mapped_valuation.py b/src/sage/rings/valuation/mapped_valuation.py index e3f4f4f13a1..7149b540cfc 100644 --- a/src/sage/rings/valuation/mapped_valuation.py +++ b/src/sage/rings/valuation/mapped_valuation.py @@ -187,7 +187,7 @@ def _call_(self, f): def reduce(self, f): r""" - Return the reduction of ``f`` in the :meth:`residue_field` of this valuation. + Return the reduction of ``f`` in the :meth:`~sage.rings.valuation.valuation_space.DiscretePseudoValuationSpace.ElementMethods.residue_field` of this valuation. EXAMPLES:: diff --git a/src/sage/rings/valuation/scaled_valuation.py b/src/sage/rings/valuation/scaled_valuation.py index 1341d59360a..2b499cf1d54 100644 --- a/src/sage/rings/valuation/scaled_valuation.py +++ b/src/sage/rings/valuation/scaled_valuation.py @@ -175,7 +175,7 @@ def _call_(self, f): def reduce(self, f): r""" - Return the reduction of ``f`` in the :meth:`residue_field` of this valuation. + Return the reduction of ``f`` in the :meth:`~sage.rings.valuation.valuation_space.DiscretePseudoValuationSpace.ElementMethods.residue_field` of this valuation. EXAMPLES:: From 775e093eb3076c1ebf44bd2867340605700c2a35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 6 Oct 2017 07:54:22 +0800 Subject: [PATCH 378/740] Remove Python2 code from valuations --- src/sage/rings/valuation/augmented_valuation.py | 12 ++++++------ src/sage/rings/valuation/developing_valuation.py | 5 ++--- src/sage/rings/valuation/inductive_valuation.py | 6 +++--- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index 2e94b6a3bbb..609d12d5829 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -1002,7 +1002,7 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations return self.residue_ring().zero() if coefficients is None: - constant_term = self.coefficients(f).next() + constant_term = next(self.coefficients(f)) else: constant_term = coefficients[0] constant_term_reduced = self._base_valuation.reduce(constant_term) @@ -1431,7 +1431,7 @@ def lift(self, F, report_coefficients=False): # now we undo the factors of Q^i (the if else is necessary to handle the case when mu is infinity, i.e., when _Q_reciprocal() is undefined) coeffs = [ (c if i == 0 else c*self._Q_reciprocal(i)).map_coefficients(_lift_to_maximal_precision) for i,c in enumerate(coeffs) ] # reduce the coefficients mod phi; the part that exceeds phi has no effect on the reduction of the coefficient - coeffs = [ self.coefficients(c).next() for c in coeffs ] + coeffs = [ next(self.coefficients(c)) for c in coeffs ] if report_coefficients: return coeffs @@ -2011,7 +2011,7 @@ def valuations(self, f, coefficients=None, call_error=False): if coefficients is not None: constant_coefficient = coefficients[0] else: - constant_coefficient = self.coefficients(f).next() + constant_coefficient = next(self.coefficients(f)) yield self._base_valuation(constant_coefficient) for i in range(num_infty_coefficients): yield infinity @@ -2056,7 +2056,7 @@ def simplify(self, f, error=None, force=False, effective_degree=None): if error is infinity: return f - return self.domain()(self._base_valuation.simplify(self.coefficients(f).next(), error, force=force)) + return self.domain()(self._base_valuation.simplify(next(self.coefficients(f)), error, force=force)) def lower_bound(self, f): r""" @@ -2075,7 +2075,7 @@ def lower_bound(self, f): +Infinity """ - return self._base_valuation.lower_bound(self.coefficients(f).next()) + return self._base_valuation.lower_bound(next(self.coefficients(f))) def upper_bound(self, f): r""" @@ -2094,4 +2094,4 @@ def upper_bound(self, f): +Infinity """ - return self._base_valuation.upper_bound(self.coefficients(f).next()) + return self._base_valuation.upper_bound(next(self.coefficients(f))) diff --git a/src/sage/rings/valuation/developing_valuation.py b/src/sage/rings/valuation/developing_valuation.py index f585f748ee7..2f964843fc7 100644 --- a/src/sage/rings/valuation/developing_valuation.py +++ b/src/sage/rings/valuation/developing_valuation.py @@ -202,11 +202,10 @@ def coefficients(self, f): if f.degree() < self.phi().degree(): yield f elif self.phi().degree() == 1: - from itertools import imap if self.phi() != domain.gen() or not domain.is_exact(): f = f(domain.gen() - self.phi()[0]) - for c in imap(domain, f.coefficients(sparse=False)): - yield c + for c in f.coefficients(sparse=False): + yield domain(c) else: while f.degree() >= 0: f,r = self._quo_rem(f) diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index 94382dea37c..c8f04b8d121 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -175,7 +175,7 @@ def equivalence_reciprocal(self, f, coefficients=None, valuations=None, check=Tr raise ValueError("f must be an equivalence unit but %r is not"%(f,)) if coefficients is None: - e0 = self.coefficients(f).next() + e0 = next(self.coefficients(f)) else: e0 = coefficients[0] @@ -1030,10 +1030,10 @@ def _equivalence_reduction(self, f, coefficients=None, valuations=None, degree_b valuation = min(valuations) R = self.equivalence_unit(-valuation) - R = self.coefficients(R).next() + R = next(self.coefficients(R)) fR_valuations = [v-valuation for v in valuations] from sage.rings.all import infinity - fR_coefficients = [self.coefficients(c*R).next() if v is not infinity and v == 0 else 0 for c,v in zip(coefficients,fR_valuations)] + fR_coefficients = [next(self.coefficients(c*R)) if v is not infinity and v == 0 else 0 for c,v in zip(coefficients,fR_valuations)] return valuation, phi_divides, self.reduce(f*R, check=False, degree_bound=degree_bound, coefficients=fR_coefficients, valuations=fR_valuations) From 32c4528c6b8f5633546f6a7f0e861f1a8d784683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 6 Oct 2017 07:55:29 +0800 Subject: [PATCH 379/740] Fix INPUT:: blocks --- src/sage/rings/padics/padic_valuation.py | 2 +- src/sage/rings/valuation/developing_valuation.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index b15aad5799c..b6ad3bec3b9 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -1063,7 +1063,7 @@ def _call_(self, x): """ Evaluate this valuation at ``x``. - INPUT:: + INPUT: - ``x`` -- an element in the domain of this valuation diff --git a/src/sage/rings/valuation/developing_valuation.py b/src/sage/rings/valuation/developing_valuation.py index 2f964843fc7..e1b11db7c12 100644 --- a/src/sage/rings/valuation/developing_valuation.py +++ b/src/sage/rings/valuation/developing_valuation.py @@ -261,7 +261,7 @@ def _call_(self, f): r""" Evaluate this valuation at ``f``. - INPUT:: + INPUT: - ``f`` -- a polynomial in the domain of this valuation From 5a555ada7232c5a9ea28f073b8f6d0f26024de83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 6 Oct 2017 08:05:26 +0800 Subject: [PATCH 380/740] Catch multiple exceptions in _test_inverse and don't assign the ValueError to NotImplementedError --- src/sage/rings/valuation/valuation_space.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index 8a3c4788b80..cceac0715d9 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -1614,7 +1614,7 @@ def _test_inverse(self, **options): for prec in (0, 1, 42, infinity): try: y = self.inverse(x, prec) - except ValueError, NotImplementedError: + except (ValueError, NotImplementedError): tester.assertNotEqual(self(x), 0) tester.assertFalse(x.is_unit()) continue From 46b8c2ea13cf0e0d1288076b2d6345c71d812ab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 6 Oct 2017 08:07:47 +0800 Subject: [PATCH 381/740] Fix _test_inverse handling of NotImplementedError you never know what happened, so don't expect anything --- src/sage/rings/valuation/valuation_space.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index cceac0715d9..f0fbba80586 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -1614,7 +1614,9 @@ def _test_inverse(self, **options): for prec in (0, 1, 42, infinity): try: y = self.inverse(x, prec) - except (ValueError, NotImplementedError): + except NotImplementedError: + continue + except ValueError: tester.assertNotEqual(self(x), 0) tester.assertFalse(x.is_unit()) continue From 0a87df303dd758fd3bb3485b8577f582632dac9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 6 Oct 2017 08:26:41 +0800 Subject: [PATCH 382/740] Fix inverse() checks for ZZ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and make sure the result is in the domain. How many mistakes can you probably make in such a trivial method… --- src/sage/rings/padics/padic_valuation.py | 6 +++--- src/sage/rings/valuation/valuation_space.py | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index b6ad3bec3b9..9e689332a7b 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -1250,7 +1250,7 @@ def inverse(self, x, precision): if not x.is_zero(): y = ~x if y in self.domain(): - return y + return self.domain()(y) if precision <= 0: return self.domain().one() @@ -1258,8 +1258,8 @@ def inverse(self, x, precision): if self(x) > 0 or precision is infinity: raise ValueError("element has no approximate inverse in this ring") - from sage.rings.all import ZpFM - return self.domain()(~ZpFM(self.p(), precision.ceil())(x)) + from sage.rings.all import ZpFM, ZZ + return self.domain()(~ZpFM(self.p(), ZZ(precision).ceil())(x)) class pAdicFromLimitValuation(FiniteExtensionFromLimitValuation, pAdicValuation_base): diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index f0fbba80586..1e034b4c3d0 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -1617,7 +1617,8 @@ def _test_inverse(self, **options): except NotImplementedError: continue except ValueError: - tester.assertNotEqual(self(x), 0) + if prec is not infinity: + tester.assertNotEqual(self(x), 0) tester.assertFalse(x.is_unit()) continue From 14e8f281c7edafae3e1f81f27425463769a76383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 6 Oct 2017 08:33:28 +0800 Subject: [PATCH 383/740] Fix _test_mul for valuations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As we allow -infinity as the value of an element the formula v(xy)=v(x)+v(y) does not make easily sense if this is -∞+∞. --- src/sage/rings/valuation/valuation_space.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index 1e034b4c3d0..f61b92a5313 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -1258,6 +1258,9 @@ def _test_mul(self, **options): S = self.domain().some_elements() from itertools import product for x,y in tester.some_elements(product(S,S)): + from sage.rings.all import infinity + if set([self(x), self(y)]) == set([infinity, -infinity]): + continue tester.assertEqual(self(x*y),self(x)+self(y)) def _test_no_infinite_units(self, **options): From a1d7cd97194041e76f5c42734b2a39ee244b3114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 6 Oct 2017 08:35:11 +0800 Subject: [PATCH 384/740] There's only one valuation implemented on Zp(5) --- src/sage/rings/padics/padic_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 9e689332a7b..e89563054ba 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -411,7 +411,7 @@ class pAdicValuation_base(DiscreteValuation): sage: TestSuite(ZZ.valuation(3)).run() # long time sage: TestSuite(QQ.valuation(5)).run() # long time - sage: TestSuite(Zp(5).valuation(5)).run() # long time + sage: TestSuite(Zp(5).valuation()).run() # long time """ def __init__(self, parent, p): From 377d87a308efdf7e7084658bfe408b0f5bd6a366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 6 Oct 2017 08:39:36 +0800 Subject: [PATCH 385/740] Fix _test_residue() An element with precision O(p^0) has valuation zero but still we can not compute its residue. --- src/sage/rings/valuation/valuation_space.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index f61b92a5313..6b23ac17ab3 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -1418,7 +1418,7 @@ def _test_reduce(self, **options): for x in tester.some_elements(self.domain().some_elements()): if self(x) < 0: - with tester.assertRaises(ValueError): + with tester.assertRaises((ValueError, ArithmeticError)): self.reduce(x) continue if self(x) == 0: From 716d66bd30adab7b25fb85a5dfa3e78014b243b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 6 Oct 2017 09:05:10 +0800 Subject: [PATCH 386/740] Do not test shifts for broken orders --- src/sage/rings/padics/padic_valuation.py | 8 +++++++- src/sage/rings/valuation/valuation_space.py | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index e89563054ba..e53b4507de7 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -1274,7 +1274,13 @@ class pAdicFromLimitValuation(FiniteExtensionFromLimitValuation, pAdicValuation_ TESTS:: - sage: TestSuite(v).run() # long time + sage: TestSuite(v).run(skip='_test_shift') # long time + + The ``_test_shift`` test fails because the parent of the shift is + incorrect, see :trac:`23971`:: + + sage: v.shift(1, -1).parent() + Number Field in I with defining polynomial x^2 + 1 """ def __init__(self, parent, approximant, G, approximants): diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index 6b23ac17ab3..c35d6356cbc 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -1174,6 +1174,8 @@ def _test_shift(self, **options): if s >= 0: self(y) >= self(x) if self.domain().is_exact(): + # the shift here sometimes fails if elements implement + # __floordiv__ incorrectly, see #23971 x == self.shift(y, -s) def _test_scale(self, **options): From cc35bcd871fc90b50df222f7dbfab66d605e3ce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 6 Oct 2017 09:07:30 +0800 Subject: [PATCH 387/740] there is a new section in the reference manual --- src/sage_setup/docbuild/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index fada71126cc..b8f79c2269a 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -633,7 +633,7 @@ def get_all_documents(self, refdir): ['reference/algebras', 'reference/arithgroup', ..., - 'reference/tensor_free_modules'] + 'reference/valuations'] """ documents = [] From 5578cae3051b2637aa31b06a28958b8a5d739a14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 6 Oct 2017 09:36:07 +0800 Subject: [PATCH 388/740] fix doctests in valuation/index.rst --- src/doc/en/reference/valuations/index.rst | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/doc/en/reference/valuations/index.rst b/src/doc/en/reference/valuations/index.rst index c4f8f5c9bf0..398163c4774 100644 --- a/src/doc/en/reference/valuations/index.rst +++ b/src/doc/en/reference/valuations/index.rst @@ -1,6 +1,8 @@ Discrete Valuations and Discrete Pseudo-Valuations ================================================== +.. linkall + High-Level Interface -------------------- Valuations can be defined conveniently on some Sage rings such as p-adic rings @@ -18,7 +20,7 @@ the valuation of a rational prime:: They are normalized such that the rational prime has valuation 1:: sage: K. = NumberField(x^2 + x + 1) - sage: K.valuation(2) + sage: v = K.valuation(2) sage: v(1024) 10 @@ -26,8 +28,11 @@ If there are multiple valuations over a prime, they can be obtained by extending a valuation from a smaller ring:: sage: K. = NumberField(x^2 + x + 1) - sage: v = K.valuation(7) - sage: w,ww = v.extensions(K) + sage: K.valuation(7) + Traceback (most recent call last): + ... + ValueError: The valuation Gauss valuation induced by 7-adic valuation does not approximate a unique extension of 7-adic valuation with respect to x^2 + x + 1 + sage: w,ww = QQ.valuation(7).extensions(K) sage: w(a + 3), ww(a + 3) (1, 0) sage: w(a + 5), ww(a + 5) @@ -63,7 +68,7 @@ Valuations can also be extended from smaller function fields:: sage: R. = K[] sage: L. = K.extension(y^2 - x) sage: v.extensions(L) - [ (x - 4)-adic valuation, v(y - 2) = 1 ]-adic valuation, + [[ (x - 4)-adic valuation, v(y - 2) = 1 ]-adic valuation, [ (x - 4)-adic valuation, v(y + 2) = 1 ]-adic valuation] Low-Level Interface @@ -130,7 +135,7 @@ The terms of this infinite sequence are computed on demand:: sage: ww(y - 1/4*x - 1) 2 sage: ww._base_valuation._approximation - [ Gauss valuation induced by (x - 4)-adic valuation, v(y - 1/4*x - 1) = 2 ] + [ Gauss valuation induced by (x - 4)-adic valuation, v(y + 1/64*x^2 - 3/8*x - 3/4) = 3 ] Non-classical valuations ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -156,7 +161,7 @@ polynomial G, approximations to the limit valuation which send G to infinity:: sage: v = QQ.valuation(2) sage: R. = QQ[] sage: f = x^5 + 3*x^4 + 5*x^3 + 8*x^2 + 6*x + 12 - sage: v.mac_lane_approximants(f) + sage: v.mac_lane_approximants(f) # random output (order may vary) [[ Gauss valuation induced by 2-adic valuation, v(x^2 + x + 1) = 3 ], [ Gauss valuation induced by 2-adic valuation, v(x) = 1/2 ], [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ]] @@ -165,7 +170,7 @@ From these approximants one can already see the residual degrees and ramification indices of the corresponding extensions. The approximants can be pushed to arbitrary precision, corresponding to a factorization of ``f``:: - sage: v.mac_lane_approximants(f, required_precision=10) + sage: v.mac_lane_approximants(f, required_precision=10) # random output [[ Gauss valuation induced by 2-adic valuation, v(x^2 + 193*x + 13/21) = 10 ], [ Gauss valuation induced by 2-adic valuation, v(x + 86) = 10 ], [ Gauss valuation induced by 2-adic valuation, v(x) = 1/2, v(x^2 + 36/11*x + 2/17) = 11 ]] From 175d9e1d145f91c1860e21194728c0bbc7e1a864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 6 Oct 2017 14:39:35 +0800 Subject: [PATCH 389/740] fix LaTeX errors in reference manual --- src/doc/en/reference/valuations/index.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/en/reference/valuations/index.rst b/src/doc/en/reference/valuations/index.rst index 398163c4774..e939d7c4873 100644 --- a/src/doc/en/reference/valuations/index.rst +++ b/src/doc/en/reference/valuations/index.rst @@ -81,9 +81,9 @@ Internally, all the above is backed by the algorithms described in ``K.valuation(x - 4)`` to the field `L` above to outline how this works internally. -First, the valuation on `K` is induced by a valuation on `\QQ[x]`. To construct -this valuation, we start from the trivial valuation on `\QQ` and consider its -induced Gauss valuation on `\QQ[x]`, i.e., the valuation that assigns to a +First, the valuation on `K` is induced by a valuation on `\Q[x]`. To construct +this valuation, we start from the trivial valuation on `\Q` and consider its +induced Gauss valuation on `\Q[x]`, i.e., the valuation that assigns to a polynomial the minimum of the coefficient valuations:: sage: R. = QQ[] From 0e9fa0c3ac71268730b266cea0c25edb174552a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 6 Oct 2017 14:39:46 +0800 Subject: [PATCH 390/740] fix grammar in docstring --- src/sage/rings/function_field/function_field.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index afd282317e2..ddca8452d78 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -586,8 +586,8 @@ def valuation(self, prime): EXAMPLES: - We create a valuation that correspond to a finite rational place of a function - field:: + We create valuations that correspond to finite rational places of a + function field:: sage: K. = FunctionField(QQ) sage: v = K.valuation(1); v From 3afa634e7268e8f4a564e509d8b21ed8dadc094d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 6 Oct 2017 14:40:35 +0800 Subject: [PATCH 391/740] Fix handling of rationals in inverse() and do not create new parents --- src/sage/rings/padics/padic_valuation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index e53b4507de7..0b4bd3b835f 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -1258,8 +1258,8 @@ def inverse(self, x, precision): if self(x) > 0 or precision is infinity: raise ValueError("element has no approximate inverse in this ring") - from sage.rings.all import ZpFM, ZZ - return self.domain()(~ZpFM(self.p(), ZZ(precision).ceil())(x)) + from sage.rings.all import ZZ, QQ + return self.domain()(ZZ(x).inverse_mod(self.p() ** QQ(precision).ceil())) class pAdicFromLimitValuation(FiniteExtensionFromLimitValuation, pAdicValuation_base): From b457126dabd8a71fdc04a914d424dc7a60d2c545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 6 Oct 2017 14:42:17 +0800 Subject: [PATCH 392/740] comment on required_precision=0 --- src/sage/rings/valuation/valuation.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 2f2f0394e69..77ab3a600fb 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -961,7 +961,9 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No ALGORITHM: We compute :meth:`mac_lane_approximants` with ``required_precision``. - The key polynomials approximate factors of ``G``. + The key polynomials approximate factors of ``G``. This can be very + slow unless ``required_precision`` is set to zero. Single factor + lifting could improve this significantly. EXAMPLES:: @@ -990,7 +992,8 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No Some examples that Sebastian Pauli used in a talk at Sage Days 87. - In this example, ``f`` factors as three factors of degree 50 over an unramified extension:: + In this example, ``f`` factors as three factors of degree 50 over an + unramified extension:: sage: R. = ZqFM(125) sage: S. = R[] @@ -1001,7 +1004,6 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No ((1 + O(5^20))*x^50 + (3*5 + O(5^20))*x^40 + (3*5 + O(5^20))*x^30 + (4*5 + O(5^20))*x^20 + (5 + O(5^20))*x^10 + 3 + 5 + O(5^20), 1), ((1 + O(5^20))*x^50 + (3*5 + O(5^20))*x^45 + (5 + O(5^20))*x^40 + (5 + O(5^20))*x^30 + (3 + 4*5 + O(5^20))*x^25 + (3*5 + O(5^20))*x^20 + (2*5 + O(5^20))*x^10 + (3*5 + O(5^20))*x^5 + (4*5 + O(5^20))*x + 3 + 5 + O(5^20), 1)] - In this case, ``f`` factors into degrees 1, 2, and 5 over a totally ramified extension:: sage: R = Zp(5) From 6b1f708e21e1c1e1e42bf3e679e734a0c09b2ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 6 Oct 2017 14:42:45 +0800 Subject: [PATCH 393/740] assert something in _test_shift --- src/sage/rings/valuation/valuation_space.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index c35d6356cbc..1c3e842dbbd 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -1172,11 +1172,11 @@ def _test_shift(self, **options): continue y = self.shift(x, s) if s >= 0: - self(y) >= self(x) + tester.assertGreaterEqual(self(y),self(x)) if self.domain().is_exact(): # the shift here sometimes fails if elements implement # __floordiv__ incorrectly, see #23971 - x == self.shift(y, -s) + tester.assertEqual(x, self.shift(y, -s)) def _test_scale(self, **options): r""" From 159491957d15df472d93f9b4505d237fbb2d43c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 6 Oct 2017 14:56:33 +0800 Subject: [PATCH 394/740] Fix _test_shift() in rings you can not expect to be able to shift back as digits might have been dropped --- src/sage/rings/valuation/valuation_space.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index 1c3e842dbbd..258f8cc7f4f 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -1173,7 +1173,8 @@ def _test_shift(self, **options): y = self.shift(x, s) if s >= 0: tester.assertGreaterEqual(self(y),self(x)) - if self.domain().is_exact(): + from sage.categories.all import Fields + if self.domain().is_exact() and self.domain() in Fields(): # the shift here sometimes fails if elements implement # __floordiv__ incorrectly, see #23971 tester.assertEqual(x, self.shift(y, -s)) From 20d3ad9f62502bb77b54d3b468128ba1f2ff8269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 6 Oct 2017 15:04:48 +0800 Subject: [PATCH 395/740] broke some long lines where it seems to improve readability --- src/sage/rings/valuation/augmented_valuation.py | 16 ++++++++++++---- src/sage/rings/valuation/developing_valuation.py | 6 ++++-- src/sage/rings/valuation/inductive_valuation.py | 14 ++++++++++---- src/sage/rings/valuation/limit_valuation.py | 13 ++++++++++--- src/sage/rings/valuation/mapped_valuation.py | 3 ++- src/sage/rings/valuation/valuation.py | 7 ++++++- 6 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index 609d12d5829..e3290d750a6 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -1307,7 +1307,10 @@ def reduce(self, f, check=True, degree_bound=None, coefficients=None, valuations coefficients = coefficients[::tau] # recursively reduce the f_i Q^{i tau} - C = [self._base_valuation.reduce(c, check=False)(self._residue_field_generator()) if valuations[i] is not infinity else self._base_valuation.residue_ring().zero() for i,c in enumerate(coefficients)] + C = [self._base_valuation.reduce(c, check=False)(self._residue_field_generator()) + if valuations[i] is not infinity + else self._base_valuation.residue_ring().zero() + for i,c in enumerate(coefficients)] # reduce the Q'^i phi^i return self.residue_ring()(C) @@ -1424,12 +1427,14 @@ def lift(self, F, report_coefficients=False): # in the last step of reduce, the f_iQ^i are reduced, and evaluated at # the generator of the residue field # here, we undo this: - coeffs = [ R0(c if self.psi().degree()==1 else list(c._vector_() if hasattr(c, '_vector_') else c.list())) for c in F.coefficients(sparse=False) ] + coeffs = [ R0(c if self.psi().degree()==1 else list(c._vector_() if hasattr(c, '_vector_') else c.list())) + for c in F.coefficients(sparse=False) ] coeffs = [ self._base_valuation.lift(c) for c in coeffs ] # now the coefficients correspond to the expansion with (f_iQ^i)(Q^{-1} phi)^i # now we undo the factors of Q^i (the if else is necessary to handle the case when mu is infinity, i.e., when _Q_reciprocal() is undefined) - coeffs = [ (c if i == 0 else c*self._Q_reciprocal(i)).map_coefficients(_lift_to_maximal_precision) for i,c in enumerate(coeffs) ] + coeffs = [ (c if i == 0 else c*self._Q_reciprocal(i)).map_coefficients(_lift_to_maximal_precision) + for i,c in enumerate(coeffs) ] # reduce the coefficients mod phi; the part that exceeds phi has no effect on the reduction of the coefficient coeffs = [ next(self.coefficients(c)) for c in coeffs ] @@ -1774,7 +1779,10 @@ def simplify(self, f, error=None, force=False, effective_degree=None, size_heuri if phiadic: coefficients = list(self.coefficients(f)) valuations = list(self.valuations(f, coefficients=coefficients)) - return self.domain().change_ring(self.domain())([0 if valuations[i] > error else self._base_valuation.simplify(c, error=error-i*self._mu, force=force, phiadic=True) for (i,c) in enumerate(coefficients)])(self.phi()) + return self.domain().change_ring(self.domain())([ + 0 if valuations[i] > error + else self._base_valuation.simplify(c, error=error-i*self._mu, force=force, phiadic=True) + for (i,c) in enumerate(coefficients)])(self.phi()) else: return self._base_valuation.simplify(f, error=error, force=force) diff --git a/src/sage/rings/valuation/developing_valuation.py b/src/sage/rings/valuation/developing_valuation.py index e1b11db7c12..982f696a8ab 100644 --- a/src/sage/rings/valuation/developing_valuation.py +++ b/src/sage/rings/valuation/developing_valuation.py @@ -166,9 +166,11 @@ def _pow(self, f, e, error, effective_degree): if e == 1: return self.simplify(f, error=error) if e % 2 == 0: - return self._pow(self.simplify(f*f, error=error*2/e, effective_degree=effective_degree*2/e), e//2, error=error, effective_degree=effective_degree) + return self._pow(self.simplify(f*f, error=error*2/e, effective_degree=effective_degree*2/e), + e//2, error=error, effective_degree=effective_degree) else: - return self.simplify(f*self._pow(f, e-1, error=error*(e-1)/e, effective_degree=effective_degree*(e-1)/e), error=error, effective_degree=effective_degree) + return self.simplify(f*self._pow(f, e-1, error=error*(e-1)/e, effective_degree=effective_degree*(e-1)/e), + error=error, effective_degree=effective_degree) def coefficients(self, f): r""" diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index c8f04b8d121..a8b1ca79773 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -816,7 +816,8 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a for i, slope in enumerate(slopes): verbose("Slope = %s"%slope, level=12) new_mu = old_mu - slope - new_valuations = [val - (j*slope if slope is not -infinity else (0 if j == 0 else -infinity)) for j,val in enumerate(w_valuations)] + new_valuations = [val - (j*slope if slope is not -infinity else (0 if j == 0 else -infinity)) + for j,val in enumerate(w_valuations)] base = self if phi.degree() == base.phi().degree(): # very frequently, the degree of the key polynomials @@ -1033,7 +1034,8 @@ def _equivalence_reduction(self, f, coefficients=None, valuations=None, degree_b R = next(self.coefficients(R)) fR_valuations = [v-valuation for v in valuations] from sage.rings.all import infinity - fR_coefficients = [next(self.coefficients(c*R)) if v is not infinity and v == 0 else 0 for c,v in zip(coefficients,fR_valuations)] + fR_coefficients = [next(self.coefficients(c*R)) if v is not infinity and v == 0 else 0 + for c,v in zip(coefficients,fR_valuations)] return valuation, phi_divides, self.reduce(f*R, check=False, degree_bound=degree_bound, coefficients=fR_coefficients, valuations=fR_valuations) @@ -1203,7 +1205,8 @@ def equivalence_decomposition(self, f, assume_not_equivalence_unit=False, coeffi domain = self.domain().change_ring(nonfractions.fraction_field()) v = self.extension(domain) ret = v.equivalence_decomposition(v.domain()(f)) - return Factorization([(self._eliminate_denominators(g), e) for (g,e) in ret], unit=self._eliminate_denominators(ret.unit())) + return Factorization([(self._eliminate_denominators(g), e) + for (g,e) in ret], unit=self._eliminate_denominators(ret.unit())) valuation, phi_divides, F = self._equivalence_reduction(f, coefficients=coefficients, valuations=valuations, degree_bound=degree_bound) F = F.factor() @@ -1418,7 +1421,10 @@ def _eliminate_denominators(self, f): # if this fails then there is no equivalent polynomial in the domain of this valuation ret = g.map_coefficients( lambda c: c.numerator()*nonfraction_valuation.inverse(c.denominator(), - valuation + nonfraction_valuation(c.denominator()) - nonfraction_valuation(c.numerator()) + nonfraction_valuation.value_group().gen()), + valuation + + nonfraction_valuation(c.denominator()) + - nonfraction_valuation(c.numerator()) + + nonfraction_valuation.value_group().gen()), nonfractions) assert w.is_equivalent(f, ret) return ret diff --git a/src/sage/rings/valuation/limit_valuation.py b/src/sage/rings/valuation/limit_valuation.py index 509c89d5119..f425d85714d 100644 --- a/src/sage/rings/valuation/limit_valuation.py +++ b/src/sage/rings/valuation/limit_valuation.py @@ -411,7 +411,8 @@ def extensions(self, ring): from sage.rings.polynomial.polynomial_ring import is_PolynomialRing if is_PolynomialRing(ring) and self.domain().base_ring().is_subring(ring.base_ring()): if self.domain().base_ring().fraction_field() is ring.base_ring(): - return [LimitValuation(self._initial_approximation.change_domain(ring), self._G.change_ring(ring.base_ring()))] + return [LimitValuation(self._initial_approximation.change_domain(ring), + self._G.change_ring(ring.base_ring()))] else: # we need to recompute the mac lane approximants over this base # ring because it could split differently @@ -522,7 +523,12 @@ def _improve_approximation(self): # an infinite valuation can not be improved further return - approximations = self._approximation.mac_lane_step(self._G, assume_squarefree=True, assume_equivalence_irreducible=True, check=False, principal_part_bound=1 if self._approximation.E()*self._approximation.F() == self._approximation.phi().degree() else None, report_degree_bounds_and_caches=True) + approximations = self._approximation.mac_lane_step(self._G, + assume_squarefree=True, + assume_equivalence_irreducible=True, + check=False, + principal_part_bound=1 if self._approximation.E()*self._approximation.F() == self._approximation.phi().degree() else None, + report_degree_bounds_and_caches=True) assert(len(approximations)==1) self._approximation, _, _, self._next_coefficients, self._next_valuations = approximations[0] @@ -704,7 +710,8 @@ def _ge_(self, other): # If the valuations are comparable, they must approximate the # same factor of G (see the documentation of LimitValuation: # the approximation must *uniquely* single out a valuation.) - return self._initial_approximation >= other._initial_approximation or self._initial_approximation <= other._initial_approximation + return (self._initial_approximation >= other._initial_approximation + or self._initial_approximation <= other._initial_approximation) return super(MacLaneLimitValuation, self)._ge_(other) diff --git a/src/sage/rings/valuation/mapped_valuation.py b/src/sage/rings/valuation/mapped_valuation.py index 7149b540cfc..e6afa136540 100644 --- a/src/sage/rings/valuation/mapped_valuation.py +++ b/src/sage/rings/valuation/mapped_valuation.py @@ -369,7 +369,8 @@ def _eq_(self, other): True """ - return isinstance(other, FiniteExtensionFromInfiniteValuation) and self._base_valuation == other._base_valuation + return (isinstance(other, FiniteExtensionFromInfiniteValuation) + and self._base_valuation == other._base_valuation) def restriction(self, ring): r""" diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 77ab3a600fb..28bb1891e55 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -771,7 +771,12 @@ def create_children(node): new_leafs = [] if node.forced_leaf: return new_leafs - augmentations = node.valuation.mac_lane_step(G, report_degree_bounds_and_caches=True, coefficients=node.coefficients, valuations=node.valuations, check=False, principal_part_bound=node.principal_part_bound) + augmentations = node.valuation.mac_lane_step(G, + report_degree_bounds_and_caches=True, + coefficients=node.coefficients, + valuations=node.valuations, + check=False, + principal_part_bound=node.principal_part_bound) for w, bound, principal_part_bound, coefficients, valuations in augmentations: ef = bound == w.E()*w.F() new_leafs.append(MacLaneApproximantNode(w, node, ef, principal_part_bound, coefficients, valuations)) From bf9567d6df5adeddc1218e9c5a513e0f1681281e Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Fri, 6 Oct 2017 14:50:38 -0700 Subject: [PATCH 396/740] Added crystal documentation, put in the crystal catalog --- src/sage/combinat/crystals/catalog.py | 2 + src/sage/combinat/tableau_shifted_primed.py | 183 ++++++++++++-------- 2 files changed, 113 insertions(+), 72 deletions(-) diff --git a/src/sage/combinat/crystals/catalog.py b/src/sage/combinat/crystals/catalog.py index 410967f90c3..39566ee2043 100644 --- a/src/sage/combinat/crystals/catalog.py +++ b/src/sage/combinat/crystals/catalog.py @@ -32,6 +32,7 @@ ` * :class:`RiggedConfigurations ` +* :class:`ShiftedPrimedTableaux ` * :class:`Spins ` * :class:`SpinsPlus ` * :class:`SpinsMinus ` @@ -75,6 +76,7 @@ from sage.combinat.rigged_configurations.tensor_product_kr_tableaux import TensorProductOfKirillovReshetikhinTableaux from sage.combinat.crystals.kirillov_reshetikhin import KirillovReshetikhinCrystal as KirillovReshetikhin from sage.combinat.rigged_configurations.rc_crystal import CrystalOfRiggedConfigurations as RiggedConfigurations +from sage.combinat.tableau_shifted_primed import SPTCrystal as ShiftedPrimedTableaux from sage.combinat.crystals.induced_structure import InducedCrystal as Induced diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index 18c7defc00f..394d9fe8c8c 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -3,6 +3,7 @@ from sage.combinat.partition import Partition, Partitions, OrderedPartitions from sage.combinat.integer_vector import IntegerVectors +from sage.rings.integer import Integer from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass @@ -46,7 +47,7 @@ class ShiftedPrimedTableau(ClonableArray): ... ValueError: [[1, 2, 2.50000000000000, 3], [0, 2, 2.50000000000000]] is not an element of Shifted Primed Tableaux - """ + """ @staticmethod def __classcall_private__(cls, T): @@ -734,7 +735,7 @@ def f(self, ind): def e(self, ind): """ Compute the action of the crystal operator `e_i` on a Shifted Primed - Tableau using cases from the paper [GPS.17]. + Tableau using cases from the paper [HPS.17]. INPUT: @@ -1626,38 +1627,128 @@ def __iter__(self): class SPTCrystal(UniqueRepresentation, Parent): + r""" + The class of crystals generated by Shifted Primed Tableaux of fixed shape. + + INPUT: + + -``n`` or ``rank`` -- a nonnegative integer + -``shape`` -- a strictly decreasing partition of length at most ``n`` plus + one + + This constructs a classical crystal of type `A_n` with highest weights + corresponding to a given shape. + + If ``n`` is not given, the rank of the crystal is assumed to be the size of + the partition ``shape`` minus one. + + The list of module generators consists of all elements of the crystal with + nonincreasing weight. + + Crystal is constructed following operations described in [HPS17]_. + + .. SEEALSO:: + + :class:`ShiftedPrimedTableaux` + :class:`ShiftedPrimedTableau` + + EXAMPLES:: + + sage: SPTC = SPTCrystal([3,2], 2) + sage: T = SPTC.module_generators[-1] + sage: T + [(1.0, 1.0, 1.5), (2.0, 2.5)] + sage: T.f(2) + [(1.0, 1.0, 2.5), (2.0, 2.5)] + sage: len(SPTC.module_generators) + 7 + sage: SPTC[0] + [(1.0, 1.0, 1.0), (2.0, 2.0)] + sage: SPTC.cardinality() + 24 + """ @staticmethod def __classcall_private__(cls, *args, **kwargs): + """ + Normalize the input. + + EXAMPLES:: + + sage: SPTCrystal() + Crystal of Shifted Primed Tableaux of type A_2 of shape (4, 2) + sage: SPTCrystal(n=2, shape=[4,2]) + Crystal of Shifted Primed Tableaux of type A_2 of shape (4, 2) + sage: SPTCrystal([4,2], rank=2) + Crystal of Shifted Primed Tableaux of type A_2 of shape (4, 2) + sage: SPTCrystal([4,2]) + Crystal of Shifted Primed Tableaux of type A_5 of shape (4, 2) + + TESTS:: + + sage: SPTCrystal([4,4], 3) + Traceback (most recent call last): + ... + ValueError: shape [4, 4] is not a strict partition + sage: SPTCrystal([3,2,1], 1) + Traceback (most recent call last): + ... + ValueError: invalid crystal rank + sage: SPTCrystal([3,2,1]) + Crystal of Shifted Primed Tableaux of type A_5 of shape (3, 2, 1) + """ + shape = None n = None if 'shape' in kwargs: shape = tuple(kwargs['shape']) + if 'rank' in kwargs: + n = int(kwargs['rank']) if 'n' in kwargs: n = int(kwargs['n']) if args: if isinstance(args[0], (list, tuple, Partition)): shape = tuple(args[0]) - if len(args) > 1 and isinstance(args[1], int): + if len(args) > 1 and isinstance(args[1], (int, Integer)): n = args[1] else: - if isinstance(args[0], int): + if isinstance(args[0], (int, Integer)): n = args[0] if (len(args) > 1 and isinstance(args[1], (list, tuple, Partition))): shape = tuple(args[1]) if shape is None: - raise ValueError('input size of tableaux as a list or a tuple') - + shape = (4, 2) + n = 2 if n is None: n = sum(shape)-1 if n+1 < len(shape): - raise ValueError('invalid crystal index') + raise ValueError('invalid crystal rank') return ShiftedPrimedTableauxCrystal(shape=shape, n=n) class ShiftedPrimedTableauxCrystal(SPTCrystal): - def __init__(self, shape=[4, 2], n=2): + """ + A factory class generating a classical crystal of Shifted Primed Tableaux. + + INPUT: + + -``n``-- a nonnegative integer + -``shape``-- a strictly decreasing partition of length at most ``n`` plus + one + """ + def __init__(self, shape=(4, 2), n=2): + """ + Initialize the crystal of Shifted Primed Tableaux. + + TESTS:: + + sage: SPTC = SPTCrystal([4,2], 2) + sage: SPTC._cartan_type + ['A', 2] + sage: len(SPTC.module_generators) + 21 + """ Parent.__init__(self, category=ClassicalCrystals()) self.n = n self._shape = shape @@ -1667,6 +1758,14 @@ def __init__(self, shape=[4, 2], n=2): max_element=n+1).list_decreasing_weight() def _repr_(self): + """ + Return a string representation of ``self``. + + TEST:: + + sage: SPTCrystal() + Crystal of Shifted Primed Tableaux of type A_2 of shape (4, 2) + """ return ("Crystal of Shifted Primed Tableaux of type A_%s of shape " % (self.n) + str(self._shape)) @@ -1676,6 +1775,10 @@ def _repr_(self): def add_strip(sub_tab, full_tab, length): + """ + Helper function used in the algorithm to generate all Shifted Primed + Tableaux of the fixed weight and shape. + """ if sum(sub_tab)+length > sum(full_tab): raise ValueError("strip does not fit") @@ -1728,67 +1831,3 @@ def add_strip(sub_tab, full_tab, length): k=len(plat_list), outer=plat_list): yield (list(primed_strip) + list(non_primed_strip)) - - -# Helper function, preprocessing of the input array to fit -# Shifted Primed Tableau format. - -def preprocessing(T): - - if isinstance(T, ShiftedPrimedTableau): - return T - elif isinstance(T, np.ndarray): - t = T.tolist() - elif not isinstance(T, (list, tuple)): - try: - return list(T) - except TypeError: - return T - else: - if not all(isinstance(row, (list, tuple)) for row in T): - T = [T] - t = [] - # Preprocessing list T for primes and other symbols - for row in T: - row_new = [] - for element in row: - try: - if int(element*2) == element*2: - row_new.append(float(element)) - continue - else: - raise ValueError("all numbers must be half-integers") - except (TypeError, ValueError): - pass - if isinstance(element, str): - if element[-1] == "'" and (element[:-1].isdigit() is True): - row_new.append(float(float(element[:-1]) - .5)) - continue - if element[-1] == "p" and (element[:-1].isdigit() is True): - row_new.append(float(float(element[:-1]) - .5)) - continue - try: - row_new.append(float(element)) - except ValueError: - raise ValueError( - "invalid input for primed elements") - t.append(row_new) - - # Accounting for zeros at the beginning and at the end of a row - i = 0 - while i < len(t): - row = t[i] - try: - while row[0] == 0: - row.pop(0) - while row[-1] == 0: - row.pop(-1) - except IndexError: - t.remove(t[i]) - continue - t[i] = row - i += 1 - - # Normalize t to be a list of tuples. - t = [tuple(_) for _ in t] - return t From 9fa940624649de1c9bdcefc3f6e279be037cca29 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Fri, 6 Oct 2017 15:17:41 -0700 Subject: [PATCH 397/740] No default value for the shape of the crystal --- src/sage/combinat/tableau_shifted_primed.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index 394d9fe8c8c..b16df7b7b3c 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -1674,8 +1674,6 @@ def __classcall_private__(cls, *args, **kwargs): EXAMPLES:: - sage: SPTCrystal() - Crystal of Shifted Primed Tableaux of type A_2 of shape (4, 2) sage: SPTCrystal(n=2, shape=[4,2]) Crystal of Shifted Primed Tableaux of type A_2 of shape (4, 2) sage: SPTCrystal([4,2], rank=2) @@ -1684,7 +1682,10 @@ def __classcall_private__(cls, *args, **kwargs): Crystal of Shifted Primed Tableaux of type A_5 of shape (4, 2) TESTS:: - + sage: SPTCrystal() + Traceback (most recent call last): + ... + ValueError: shape argument is not specified sage: SPTCrystal([4,4], 3) Traceback (most recent call last): ... @@ -1717,8 +1718,7 @@ def __classcall_private__(cls, *args, **kwargs): isinstance(args[1], (list, tuple, Partition))): shape = tuple(args[1]) if shape is None: - shape = (4, 2) - n = 2 + raise ValueError('shape argument is not specified') if n is None: n = sum(shape)-1 @@ -1763,7 +1763,7 @@ def _repr_(self): TEST:: - sage: SPTCrystal() + sage: SPTCrystal([4,2], 2) Crystal of Shifted Primed Tableaux of type A_2 of shape (4, 2) """ return ("Crystal of Shifted Primed Tableaux of type A_%s of shape " From 3fe8dcc0cbbcf8cfd9a029def18be37e18375d13 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sat, 7 Oct 2017 12:05:37 -0700 Subject: [PATCH 398/740] Initial skew implementation --- src/sage/combinat/tableau_shifted_primed.py | 119 ++++++++++++-------- 1 file changed, 73 insertions(+), 46 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index b16df7b7b3c..7a70202ff8f 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -50,7 +50,7 @@ class ShiftedPrimedTableau(ClonableArray): """ @staticmethod - def __classcall_private__(cls, T): + def __classcall_private__(cls, T, skew=[]): r""" Ensure that a shifted tableau is only ever constructed as an ``element_class`` call of an appropriate parent. @@ -68,12 +68,13 @@ def __classcall_private__(cls, T): """ - if isinstance(T, cls): + if (isinstance(T, cls) and T._skew == skew): return T - return ShiftedPrimedTableaux()(T) + return ShiftedPrimedTableaux(skew=skew)(T) - def __init__(self, parent, T): + +def __init__(self, parent, T, skew=[]): r""" Initialize a shifted tableau. @@ -104,6 +105,7 @@ def __init__(self, parent, T): ... TypeError: 'tuple' object does not support item assignment """ + self._skew = skew if isinstance(T, ShiftedPrimedTableau): ClonableArray.__init__(self, parent, T) @@ -188,7 +190,7 @@ def __eq__(self, other): Tab = ShiftedPrimedTableau(other) except ValueError: return False - return (list(self) == list(Tab)) + return (self._skew == Tab._skew and list(self) == list(Tab)) def to_matrix(self): """ @@ -979,12 +981,10 @@ class ShiftedPrimedTableaux(UniqueRepresentation, Parent): [(1.0, 1.5, 2.5), (2.0, 3.0)], [(1.0, 1.5, 2.5), (2.0, 2.5)], [(1.0, 1.5, 2.0), (3.0, 3.0)]] - sage: SPT = ShiftedPrimedTableaux((1,2)); SPT Shifted Primed Tableaux of weight (1, 2) sage: list(SPT) [[(1.0, 2.0, 2.0)], [(1.0, 1.5, 2.0)], [(1.0, 1.5), (2.0,)]] - sage: SPT = ShiftedPrimedTableaux([3,2], max_element = 2); SPT Shifted Primed Tableaux of shape [3, 2] and maximum element 2 sage: list(SPT) @@ -1011,7 +1011,6 @@ def __classcall_private__(cls, *args, **kwargs): sage: ShiftedPrimedTableaux([]) Shifted Primed Tableaux of shape [] - sage: ShiftedPrimedTableaux(3) Traceback (most recent call last): ... @@ -1025,7 +1024,7 @@ def __classcall_private__(cls, *args, **kwargs): sage: ShiftedPrimedTableaux([[1]]) Traceback (most recent call last): ... - ValueError: shape [[1]] is not a partition + ValueError: invalid shape argument sage: ShiftedPrimedTableaux(weight=(2,2,2), max_element=2) Traceback (most recent call last): @@ -1035,6 +1034,15 @@ def __classcall_private__(cls, *args, **kwargs): weight = None shape = None max_element = None + skew = [] + + if 'skew' in kwargs: + try: + skew = Partition(kwargs['skew']) + except ValueError: + raise ValueError('invalid skew argument') + if not all(skew[i] > skew[i+1] for i in range(len(skew)-1)): + raise ValueError('skew shape must be a strict partition') if 'max_elt' in kwargs: max_element = int(kwargs['max_elt']) @@ -1082,43 +1090,39 @@ def __classcall_private__(cls, *args, **kwargs): try: shape = Partition(shape) except ValueError: - raise ValueError('shape {} is not a partition'.format(shape)) + raise ValueError('invalid shape argument') + if not all(shape[i] > shape[i+1] for i in range(len(shape)-1)): + raise ValueError( + "shape {} is not a strict partition".format(shape)) + if not all(skew[i] <= shape[i] for i in range(len(skew))): + raise ValueError( + 'skew shape must be inside the given tableau shape') if weight is not None: while weight[-1] == 0: weight = weight[:-1] if max_element is not None and weight is not None: - if len(weight) != max_element: + if len(weight) > max_element: raise ValueError( "maximum element is incompatible with the weight") - if max_element is not None and shape is not None: - if max_element < len(shape): - raise ValueError( - "maximum element is incompatible with the shape") - if shape is None and weight is None: if max_element is not None: raise ValueError("specify shape or weight argument") - return ShiftedPrimedTableaux_all() + return ShiftedPrimedTableaux_all(skew=skew) - elif weight is None and all(shape[i] > shape[i+1] - for i in range(len(shape)-1)): - return ShiftedPrimedTableaux_shape(Partition(shape), max_element) + elif weight is None: + return ShiftedPrimedTableaux_shape(shape, max_element, skew=skew) elif shape is None: - return ShiftedPrimedTableaux_weight(weight) + return ShiftedPrimedTableaux_weight(weight, skew=skew) - if not all(shape[i] > shape[i+1] for i in range(len(shape)-1)): - raise ValueError( - "shape {} is not a strict partition".format(shape)) - - if sum(shape) != sum(weight): + if sum(shape) - sum(skew) != sum(weight): raise ValueError( "weight and shape are incompatible") - return ShiftedPrimedTableaux_weight_shape(weight, shape) + return ShiftedPrimedTableaux_weight_shape(weight, shape, skew=skew) def __contains__(self, T): """ @@ -1159,7 +1163,7 @@ class ShiftedPrimedTableaux_all(ShiftedPrimedTableaux): """ Element = ShiftedPrimedTableau - def __init__(self): + def __init__(self, skew=[]): """ Initialize the class of all shifted tableaux. @@ -1175,6 +1179,7 @@ def __init__(self): False """ Parent.__init__(self, category=FiniteEnumeratedSets()) + self._skew = skew def _repr_(self): """ @@ -1185,7 +1190,9 @@ def _repr_(self): sage: ShiftedPrimedTableaux() Shifted Primed Tableaux """ - return "Shifted Primed Tableaux" + if self._skew==[]: + return "Shifted Primed Tableaux" + return "Shifted Primed Tableaux Skewed by {}".format(self._skew) def _element_constructor_(self, T): """ @@ -1214,7 +1221,7 @@ def _element_constructor_(self, T): """ try: - return self.element_class(self, T) + return self.element_class(self, T, skew=self._skew) except ValueError: raise ValueError( "{} is not an element of Shifted Primed Tableaux".format(T)) @@ -1229,7 +1236,7 @@ def __contains__(self, T): False """ try: - self.element_class(self, T) + self.element_class(self, T, skew=self._skew) return True except ValueError: return False @@ -1241,7 +1248,7 @@ class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): """ Element = ShiftedPrimedTableau - def __init__(self, shape, max_elt): + def __init__(self, shape, max_elt, skew = []): """ Initialize the class of Shifted Primed Tableaux of a given shape. @@ -1252,6 +1259,7 @@ def __init__(self, shape, max_elt): Parent.__init__(self, category=FiniteEnumeratedSets()) self._max_elt = max_elt self._shape = shape + self._skew = skew def _repr_(self): """ @@ -1262,12 +1270,20 @@ def _repr_(self): sage: ShiftedPrimedTableaux([3,2,1]) Shifted Primed Tableaux of shape [3, 2, 1] """ - if self._max_elt is None: + if self._max_elt is None and self._skew == []: return "Shifted Primed Tableaux of shape {}".format(self._shape) - if self._max_elt is not None: + if self._max_elt is not None and self._skew == []: return ( "Shifted Primed Tableaux of shape {} and maximum element {}" .format(self._shape, self._max_elt)) + if self._max_elt is None and self._skew != []: + return ("Shifted Primed Tableaux skewed by {} of shape {}" + .format(self.skew, self._shape)) + if self._max_elt is not None and self._skew != []: + return ( + "Shifted Primed Tableaux skewed by {}".format(self._skew) + + " of shape {} and maximum element {}" + .format(self._shape, self._max_elt)) def __contains__(self, T): """ @@ -1278,7 +1294,7 @@ def __contains__(self, T): """ try: - Tab = self.element_class(self, T) + Tab = self.element_class(self, T, skew=self._skew) except ValueError: return False @@ -1315,7 +1331,7 @@ def _element_constructor_(self, T): """ try: - Tab = self.element_class(self, T) + Tab = self.element_class(self, T, skew=self._skew) except ValueError: raise ValueError( "{} is not an element of Shifted Primed Tableaux".format(T)) @@ -1363,6 +1379,8 @@ def __iter__(self): ... ValueError: set is infinite """ + if self._skew != []: + raise NotImplementedError('skew tableau must be empty') if self._max_elt is None: raise ValueError("set is infinite") for weight in OrderedPartitions(sum(self._shape)+self._max_elt, @@ -1382,6 +1400,8 @@ def list_decreasing_weight(self): sage: Tabs.list_decreasing_weight() [[(1.0, 1.0), (2.0,)], [(1.0, 2.0), (3.0,)], [(1.0, 1.5), (3.0,)]] """ + if self._skew != []: + raise NotImplementedError('skew tableau must be empty') list_dw = [] if self._max_elt is None: max_element = sum(self._shape) @@ -1406,6 +1426,8 @@ def list_highest_weight(self): [(1.0, 1.0, 1.5), (2.0,)], [(1.0, 1.0, 2.5), (2.0,)]] """ + if self._skew != []: + raise NotImplementedError('skew tableau must be empty') return [tab for tab in self.list_decreasing_weight() if tab.is_highest_weight()] @@ -1417,7 +1439,7 @@ class ShiftedPrimedTableaux_weight(ShiftedPrimedTableaux): """ Element = ShiftedPrimedTableau - def __init__(self, weight): + def __init__(self, weight, skew=[]): """ Initialize the class of Shifted Primed Tableaux of a given weight. @@ -1427,6 +1449,7 @@ def __init__(self, weight): """ Parent.__init__(self, category=FiniteEnumeratedSets()) self._weight = weight + self._skew = skew def _repr_(self): """ @@ -1437,7 +1460,10 @@ def _repr_(self): sage: ShiftedPrimedTableaux((3,2,1)) Shifted Primed Tableaux of weight (3, 2, 1) """ - return "Shifted Primed Tableaux of weight {}".format(self._weight) + if self._skew == []: + return "Shifted Primed Tableaux of weight {}".format(self._weight) + return "Shifted Primed Tableaux Skewed by {} of weight {}".format( + self._skew, self._weight) def __contains__(self, T): """ @@ -1447,7 +1473,7 @@ def __contains__(self, T): True """ try: - Tab = self.element_class(self, T) + Tab = self.element_class(self, T, skew=self._skew) except ValueError: return False return Tab.weight() == self._weight @@ -1473,7 +1499,7 @@ def _element_constructor_(self, T): Shifted Primed Tableaux of weight (2, 1) """ try: - Tab = self.element_class(self, T) + Tab = self.element_class(self, T, skew=self._skew) except ValueError: raise ValueError( "{} is not an element of Shifted Primed Tableaux".format(T)) @@ -1543,8 +1569,7 @@ def __contains__(self, T): except ValueError: return False - return (Tab.weight() == self._weight - and Tab.shape() == self._shape) + return (Tab.weight() == self._weight and Tab.shape() == self._shape) def _element_constructor_(self, T): """ @@ -1564,7 +1589,7 @@ def _element_constructor_(self, T): of weight (2, 1) and shape [3] """ try: - Tab = self.element_class(self, T) + Tab = self.element_class(self, T, skew=self._skew) except ValueError: raise ValueError( "{} is not an element of Shifted Primed Tableaux".format(T)) @@ -1589,9 +1614,11 @@ def __iter__(self): sage: len(list(Tabs)) 4 """ - if not self._shape.dominates(Partition(sorted(list(self._weight), - key=int, - reverse=True))): + if self._skew != []: + raise NotImplementedError('skew tableau must be empty') + + if not self._shape.dominates( + Partition(sorted(list(self._weight), key=int, reverse=True))): return yield full_shape = self._shape From 2cc0e9b0183adb4f74661c847325f2c309e7a79c Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sat, 7 Oct 2017 14:03:18 -0700 Subject: [PATCH 399/740] Fixed minor issues --- src/sage/combinat/all.py | 3 +- src/sage/combinat/crystals/catalog.py | 5 +- src/sage/combinat/tableau_shifted_primed.py | 101 ++++---------------- 3 files changed, 22 insertions(+), 87 deletions(-) diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index dc6b9dddf05..50cde969294 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -95,7 +95,8 @@ from .k_tableau import WeakTableau, WeakTableaux, StrongTableau, StrongTableaux lazy_import('sage.combinat.lr_tableau', ['LittlewoodRichardsonTableau', 'LittlewoodRichardsonTableaux']) -lazy_import('sage.combinat.tableau_shifted_primed', ['ShiftedPrimedTableaux', 'ShiftedPrimedTableau','SPTCrystal']) +lazy_import('sage.combinat.tableau_shifted_primed', ['ShiftedPrimedTableaux', + 'ShiftedPrimedTableau']) #Words from .words.all import * diff --git a/src/sage/combinat/crystals/catalog.py b/src/sage/combinat/crystals/catalog.py index 39566ee2043..46887ec37b7 100644 --- a/src/sage/combinat/crystals/catalog.py +++ b/src/sage/combinat/crystals/catalog.py @@ -32,7 +32,8 @@ ` * :class:`RiggedConfigurations ` -* :class:`ShiftedPrimedTableaux ` +* :class:`ShiftedPrimedTableaux +` * :class:`Spins ` * :class:`SpinsPlus ` * :class:`SpinsMinus ` @@ -76,7 +77,7 @@ from sage.combinat.rigged_configurations.tensor_product_kr_tableaux import TensorProductOfKirillovReshetikhinTableaux from sage.combinat.crystals.kirillov_reshetikhin import KirillovReshetikhinCrystal as KirillovReshetikhin from sage.combinat.rigged_configurations.rc_crystal import CrystalOfRiggedConfigurations as RiggedConfigurations -from sage.combinat.tableau_shifted_primed import SPTCrystal as ShiftedPrimedTableaux +from sage.combinat.tableau_shifted_primed import ShiftedPrimedTableauxCrystal as ShiftedPrimedTableaux from sage.combinat.crystals.induced_structure import InducedCrystal as Induced diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index b16df7b7b3c..87c7b1d62cc 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -823,78 +823,6 @@ def e(self, ind): return(ShiftedPrimedTableau(T)) - def epsilon(self, i): - r""" - Compute the value of the crystal function `\epsilon_i` applied to - a shifted primed tableau ``self``. - - The function `\epsilon_i` is defined to be the maximal number - of operators `e_i` one can apply to ``self`` before vanishing - into ``None``. - - INPUT: - - - ``self`` -- shifted primed tableau - - ``ind`` -- index of the function `\epsilon_i` associated with a - crystal operator `e_i`. - - OUTPUT: - - Value of the function `\epsilon_i`. - - EXAMPLES:: - - sage: t = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.5, 2.5), - ....: (2.0, 2.5, 3.0, 3.0), (3.0, 4.0)]) - sage: t.epsilon(2) - 3 - sage: s = t.e(2).e(2).e(2) - sage: s.epsilon(2) - 0 - """ - b = self - count = -1 - while b is not None: - b = b.e(i) - count += 1 - return count - - def phi(self, i): - r""" - Compute value of the crystal function `\phi_i` applied to a - shifted primed tableau ``self``. - - The function `\phi_i` is defined to be the maximal number of - operators `f_i` one can apply to ``self`` before vanishing - into ``None``. - - INPUT: - - - ``self`` -- shifted primed tableau - - ``ind`` -- index of the function `\epsilon_i` associated with a - crystal operator `f_i`. - - OUTPUT: - - Value of the function `\epsilon_i`. - - EXAMPLES:: - - sage: t = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.5, 2.0), - ....: (2.0, 2.0, 2.0, 2.5), (3.0, 4.0)]) - sage: t.phi(2) - 3 - sage: s = t.f(2).f(2).f(2) - sage: s.phi(2) - 0 - """ - b = self - count = -1 - while b is not None: - b = b.f(i) - count += 1 - return count - def is_highest_weight(self): """ Check wether the shifted primed tableau ``self`` is a highest weight @@ -1626,7 +1554,7 @@ def __iter__(self): ########### -class SPTCrystal(UniqueRepresentation, Parent): +class ShiftedPrimedTableauxCrystal(UniqueRepresentation, Parent): r""" The class of crystals generated by Shifted Primed Tableaux of fixed shape. @@ -1674,27 +1602,27 @@ def __classcall_private__(cls, *args, **kwargs): EXAMPLES:: - sage: SPTCrystal(n=2, shape=[4,2]) + sage: ShiftedPrimedTableauxCrystal(n=2, shape=[4,2]) Crystal of Shifted Primed Tableaux of type A_2 of shape (4, 2) - sage: SPTCrystal([4,2], rank=2) + sage: ShiftedPrimedTableauxCrystal([4,2], rank=2) Crystal of Shifted Primed Tableaux of type A_2 of shape (4, 2) - sage: SPTCrystal([4,2]) + sage: ShiftedPrimedTableauxCrystal([4,2]) Crystal of Shifted Primed Tableaux of type A_5 of shape (4, 2) TESTS:: - sage: SPTCrystal() + sage: ShiftedPrimedTableauxCrystal() Traceback (most recent call last): ... ValueError: shape argument is not specified - sage: SPTCrystal([4,4], 3) + sage: ShiftedPrimedTableauxCrystal([4,4], 3) Traceback (most recent call last): ... ValueError: shape [4, 4] is not a strict partition - sage: SPTCrystal([3,2,1], 1) + sage: ShiftedPrimedTableauxCrystal([3,2,1], 1) Traceback (most recent call last): ... ValueError: invalid crystal rank - sage: SPTCrystal([3,2,1]) + sage: ShiftedPrimedTableauxCrystal([3,2,1]) Crystal of Shifted Primed Tableaux of type A_5 of shape (3, 2, 1) """ @@ -1724,10 +1652,11 @@ def __classcall_private__(cls, *args, **kwargs): if n+1 < len(shape): raise ValueError('invalid crystal rank') - return ShiftedPrimedTableauxCrystal(shape=shape, n=n) + return SPTCrystal(shape=shape, n=n) -class ShiftedPrimedTableauxCrystal(SPTCrystal): +class SPTCrystal(ShiftedPrimedTableauxCrystal): + """ A factory class generating a classical crystal of Shifted Primed Tableaux. @@ -1737,7 +1666,9 @@ class ShiftedPrimedTableauxCrystal(SPTCrystal): -``shape``-- a strictly decreasing partition of length at most ``n`` plus one """ - def __init__(self, shape=(4, 2), n=2): + Element = ShiftedPrimedTableau + + def __init__(self, shape, n): """ Initialize the crystal of Shifted Primed Tableaux. @@ -1749,6 +1680,8 @@ def __init__(self, shape=(4, 2), n=2): sage: len(SPTC.module_generators) 21 """ + if shape is None or n is None: + raise ValueError('shape and n must be specified') Parent.__init__(self, category=ClassicalCrystals()) self.n = n self._shape = shape @@ -1763,7 +1696,7 @@ def _repr_(self): TEST:: - sage: SPTCrystal([4,2], 2) + sage: ShiftedPrimedTableauxCrystal([4,2], 2) Crystal of Shifted Primed Tableaux of type A_2 of shape (4, 2) """ return ("Crystal of Shifted Primed Tableaux of type A_%s of shape " From a9a52ca23dec0bc018c8d7d264d2fa263b3e17e1 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Mon, 9 Oct 2017 10:36:26 -0700 Subject: [PATCH 400/740] Fixed doctests --- src/sage/combinat/tableau_shifted_primed.py | 44 ++++++++++----------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index 87c7b1d62cc..244d2e5c20a 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -190,14 +190,14 @@ def __eq__(self, other): return False return (list(self) == list(Tab)) - def to_matrix(self): + def _to_matrix(self): """ Return a 2-dimensional numpy.array representation of a shifted tableau EXAMPLE:: sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p'],[3]]) - sage: mat = t.to_matrix() + sage: mat = t._to_matrix() sage: mat array([[ 1. , 1.5, 2. , 2. ], [ 0. , 2. , 2.5, 0. ], @@ -568,7 +568,7 @@ def weight(self): weight = tuple([flat.count(i+1) for i in range(max_ind)]) return tuple(weight) - def reading_word_with_positions(self): + def _reading_word_with_positions(self): """ Return the reading word of ``self`` together with positions of the corresponding letters in ``self``. @@ -588,12 +588,12 @@ def reading_word_with_positions(self): EXAMPLE:: sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) - sage: t.reading_word_with_positions() + sage: t._reading_word_with_positions() [((1, 2), 3), ((0, 1), 2), ((1, 1), 2), ((0, 0), 1), ((0, 2), 2), ((0, 3), 2)] """ - mat = self.to_matrix() + mat = self._to_matrix() list_with_positions = [] for (i, j), x in np.ndenumerate(mat[:, ::-1].T): if int(x) != x: @@ -625,7 +625,7 @@ def reading_word(self): sage: t.reading_word() [3, 2, 2, 1, 2, 2] """ - return [tup[1] for tup in self.reading_word_with_positions()] + return [tup[1] for tup in self._reading_word_with_positions()] def f(self, ind): """ @@ -667,9 +667,9 @@ def f(self, ind): if self is None: return None - T = self.to_matrix() + T = self._to_matrix() - read_word = self.reading_word_with_positions() + read_word = self._reading_word_with_positions() read_word = [num for num in read_word if num[1] == ind or num[1] == ind+1] @@ -765,9 +765,9 @@ def e(self, ind): """ if self is None: return None - T = self.to_matrix() + T = self._to_matrix() - read_word = self.reading_word_with_positions() + read_word = self._reading_word_with_positions() read_word = [num for num in read_word if num[1] == ind or num[1] == ind+1] @@ -1582,7 +1582,7 @@ class ShiftedPrimedTableauxCrystal(UniqueRepresentation, Parent): EXAMPLES:: - sage: SPTC = SPTCrystal([3,2], 2) + sage: SPTC = crystals.ShiftedPrimedTableaux([3,2], 2) sage: T = SPTC.module_generators[-1] sage: T [(1.0, 1.0, 1.5), (2.0, 2.5)] @@ -1602,27 +1602,27 @@ def __classcall_private__(cls, *args, **kwargs): EXAMPLES:: - sage: ShiftedPrimedTableauxCrystal(n=2, shape=[4,2]) + sage: crystals.ShiftedPrimedTableaux(n=2, shape=[4,2]) Crystal of Shifted Primed Tableaux of type A_2 of shape (4, 2) - sage: ShiftedPrimedTableauxCrystal([4,2], rank=2) + sage: crystals.ShiftedPrimedTableaux([4,2], rank=2) Crystal of Shifted Primed Tableaux of type A_2 of shape (4, 2) - sage: ShiftedPrimedTableauxCrystal([4,2]) + sage: crystals.ShiftedPrimedTableaux([4,2]) Crystal of Shifted Primed Tableaux of type A_5 of shape (4, 2) TESTS:: - sage: ShiftedPrimedTableauxCrystal() + sage: crystals.ShiftedPrimedTableaux() Traceback (most recent call last): ... - ValueError: shape argument is not specified - sage: ShiftedPrimedTableauxCrystal([4,4], 3) + ValueError: shape argument must be specified + sage: crystals.ShiftedPrimedTableaux([4,4], 3) Traceback (most recent call last): ... ValueError: shape [4, 4] is not a strict partition - sage: ShiftedPrimedTableauxCrystal([3,2,1], 1) + sage: crystals.ShiftedPrimedTableaux([3,2,1], 1) Traceback (most recent call last): ... ValueError: invalid crystal rank - sage: ShiftedPrimedTableauxCrystal([3,2,1]) + sage: crystals.ShiftedPrimedTableaux([3,2,1]) Crystal of Shifted Primed Tableaux of type A_5 of shape (3, 2, 1) """ @@ -1646,7 +1646,7 @@ def __classcall_private__(cls, *args, **kwargs): isinstance(args[1], (list, tuple, Partition))): shape = tuple(args[1]) if shape is None: - raise ValueError('shape argument is not specified') + raise ValueError('shape argument must be specified') if n is None: n = sum(shape)-1 @@ -1674,7 +1674,7 @@ def __init__(self, shape, n): TESTS:: - sage: SPTC = SPTCrystal([4,2], 2) + sage: SPTC = crystals.ShiftedPrimedTableaux([4,2], 2) sage: SPTC._cartan_type ['A', 2] sage: len(SPTC.module_generators) @@ -1696,7 +1696,7 @@ def _repr_(self): TEST:: - sage: ShiftedPrimedTableauxCrystal([4,2], 2) + sage: crystals.ShiftedPrimedTableaux([4,2], 2) Crystal of Shifted Primed Tableaux of type A_2 of shape (4, 2) """ return ("Crystal of Shifted Primed Tableaux of type A_%s of shape " From f6e95408910a26d45f6370edfbbb300a9ee3bd2b Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Mon, 9 Oct 2017 10:59:57 -0700 Subject: [PATCH 401/740] Fixed documentation --- src/sage/combinat/tableau_shifted_primed.py | 33 +++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index 244d2e5c20a..f8bd94c7a15 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -1166,6 +1166,13 @@ def __contains__(self, T): class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): """ Shifted Primed Tableaux of a fixed shape. + + TESTS:: + + sage: ShiftedPrimedTableaux([4,3,1], max_elt=4) + Shifted Primed Tableaux of shape [4, 3, 1] and maximum element 4 + sage: ShiftedPrimedTableaux([4,3,1], max_elt=4).cardinality() + 384 """ Element = ShiftedPrimedTableau @@ -1176,6 +1183,9 @@ def __init__(self, shape, max_elt): If ``max_elt`` is specified, a finite set with entries smaller or equal to ``max_elt``. + TEST:: + + sage: TestSuite( ShiftedPrimedTableaux([4,2,1], max_elt=4)).run() """ Parent.__init__(self, category=FiniteEnumeratedSets()) self._max_elt = max_elt @@ -1342,6 +1352,13 @@ def list_highest_weight(self): class ShiftedPrimedTableaux_weight(ShiftedPrimedTableaux): """ Shifted Primed Tableaux of fixed weight. + + TESTS:: + + sage: ShiftedPrimedTableaux((2,3,1)) + Shifted Primed Tableaux of weight (2, 3, 1) + sage: ShiftedPrimedTableaux((2,3,1)).cardinality() + 17 """ Element = ShiftedPrimedTableau @@ -1433,6 +1450,13 @@ def __iter__(self): class ShiftedPrimedTableaux_weight_shape(ShiftedPrimedTableaux): """ Shifted Primed Tableaux of the fixed weight and shape. + + TESTS:: + + sage: ShiftedPrimedTableaux((2,3,2),[4,2,1]) + Shifted Primed Tableaux of weight (2, 3, 2) and shape [4, 2, 1] + sage: ShiftedPrimedTableaux((2,3,2), [4,2,1]).cardinality() + 4 """ Element = ShiftedPrimedTableau @@ -1440,6 +1464,10 @@ def __init__(self, weight, shape): """ Initialize the class of Shifted Primed Tableaux of the given weight and shape. + + TEST:: + + sage: TestSuite( ShiftedPrimedTableaux((3,2,1)) ).run() """ Parent.__init__(self, category=FiniteEnumeratedSets()) self._weight = weight @@ -1711,6 +1739,11 @@ def add_strip(sub_tab, full_tab, length): """ Helper function used in the algorithm to generate all Shifted Primed Tableaux of the fixed weight and shape. + + TEST:: + + sage: list(ShiftedPrimedTableaux([3,1],(2,2))) + [[(1.0, 1.0, 2.0), (2.0,)], [(1.0, 1.0, 1.5), (2.0,)]] """ if sum(sub_tab)+length > sum(full_tab): raise ValueError("strip does not fit") From bb86ca0fd179a17464c240ff703827355b1b2d27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 13 Oct 2017 13:51:06 +0800 Subject: [PATCH 402/740] Do not implement Python2 _cmp_ --- src/sage/rings/valuation/valuation.py | 32 --------------------------- 1 file changed, 32 deletions(-) diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 28bb1891e55..86f3bf6e43b 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -151,38 +151,6 @@ def _hash_(self): """ return id(self) - def _cmp_(self, other): - r""" - Compare this element to ``other``. - - Since there is no reasonable total order on valuations, this method - just throws an exception. - - EXAMPLES: - - However, comparison with the operators ``>`` and ``<`` might still work - when they can fall back to the implementation through ``>=`` and - ``<=``:: - - sage: v = QQ.valuation(2) - sage: v > v - False - - Note that this does not affect comparison of valuations which do not - coerce into a common parent. This is by design in Sage, see - :meth:`sage.structure.element.Element.__cmp__`. When the valuations do - not coerce into a common parent, a rather random comparison of ``id`` - happens:: - - sage: w = valuations.TrivialValuation(GF(2)) - sage: w < v # random output - True - sage: v < w # random output - False - - """ - raise NotImplementedError("No total order for these valuations") - def _richcmp_(self, other, op): r""" Compare this element to ``other``. From 3b6fb54a72be70c61f6baca28dab70d6607841a3 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sun, 15 Oct 2017 20:08:56 -0700 Subject: [PATCH 403/740] Skew --- src/sage/combinat/tableau_shifted_primed.py | 204 +++++++------------- 1 file changed, 74 insertions(+), 130 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index 7a70202ff8f..7bef3f47fe2 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -73,8 +73,7 @@ def __classcall_private__(cls, T, skew=[]): return ShiftedPrimedTableaux(skew=skew)(T) - -def __init__(self, parent, T, skew=[]): + def __init__(self, parent, T, skew=[]): r""" Initialize a shifted tableau. @@ -208,8 +207,12 @@ def to_matrix(self): True """ array = [] - m = len(self[0]) - for i in range(len(self)): + m = self.shape()[0] + for i in range(len(self._skew)): + array.append([0]*(i+self._skew[i]) + + list(self[i]) + + [0]*(m-i-self._skew[i]-len(self[i]))) + for i in range(len(self._skew), len(self)): array.append([0]*i + list(self[i]) + [0]*(m-i-len(self[i]))) array = np.array(array, dtype='float') return array @@ -224,16 +227,25 @@ def check(self): sage: t = T([[1,'2p',2,2],[2,'3p']]) sage: t.check() """ - if not all(len(self[i]) > len(self[i+1]) for i in range(len(self)-1)): + skew = self._skew + [0]*(len(self)-len(self._skew)) + if not all(self._skew[i] > self._skew[i+1] + for i in range(len(self._skew)-1)): + raise ValueError('skew shape must be a strict partition') + if not all(len(self[i]) + skew[i] > len(self[i+1]) + skew[i+1] + for i in range(len(self)-1)): raise ValueError('shape must be a strict partition') for i, row in enumerate(self): if i > 0: - if not all(val > self[i-1][j+1] - for j, val in enumerate(row) if int(val) == val): + if not all(val > self[i-1][j+1-skew[i-1]+skew[i]] + for j, val in enumerate(row) + if int(val) == val + if j+1 >= skew[i-1]-skew[i]): raise ValueError( 'column is not strictly increasing in non-primes') - if not all(val >= self[i-1][j+1] - for j, val in enumerate(row) if int(val) != val): + if not all(val >= self[i-1][j+1-skew[i-1]+skew[i]] + for j, val in enumerate(row) + if int(val) != val + if j+1 >= skew[i-1]-skew[i]): raise ValueError( 'column is not weakly increasing in primes') if not all(row[j] <= row[j+1] @@ -242,7 +254,9 @@ def check(self): if not all(row[j] < row[j+1] for j in range(len(row)-1) if int(row[j]) != row[j]): raise ValueError('row is not strictly increasing in primes') - if not all(int(row[0]) == row[0] for row in self): + if not all(int(row[0]) == row[0] + for i, row in enumerate(self) + if skew[i] == 0): raise ValueError('diagonal elements must be non-primes') def _repr_(self): @@ -256,7 +270,10 @@ def _repr_(self): sage: t [(1.0, 1.5, 2.0, 2.0), (2.0, 2.5)] """ - return repr([tuple(_) for _ in self]) + if self._skew == []: + return repr([tuple(_) for _ in self]) + return (repr([tuple(_) for _ in self]) + + " skewed by {}".format(self._skew)) def _repr_tab(self): """ @@ -268,10 +285,11 @@ def _repr_tab(self): sage: t._repr_tab() [[' 1 ', " 2'", ' 2 ', ' 2 '], [' 2 ', " 3'"]] """ + skew = self._skew + [0]*(len(self)-len(self._skew)) max_len = len(str(self.max_element()))+1 string_tab = [] for i, row in enumerate(self): - string_row = [] + string_row = [' '*(max_len-1) + '. ']*(skew[i]) for val in row: if int(val) == val: string_row.append(' '*(max_len-len(str(int(val)))) @@ -299,20 +317,8 @@ def _repr_diagram(self): """ max_len = len(str(self.max_element()))+2 - string_list = "" - for i, row in enumerate(self): - string_list += ' ' - string_list += ' ' * i * (max_len) - for val in row: - if int(val) == val: - string_list += (str(int(val)) - + ' ' * (max_len-len(str(int(val))))) - else: - string_list += (str(int(val+.5)) + "'" - + ' '*(max_len-1 - len(str(int(val+.5))))) - string_list += '\n' - string_list = string_list[:-2] - return string_list + return "\n".join([" "*max_len*i + "".join(_) + for i, _ in enumerate(self._repr_tab())]) def _ascii_art_(self): """ @@ -471,15 +477,7 @@ def _latex_(self): } """ from sage.combinat.output import tex_from_array - L = list() - for i, row in enumerate(self): - num_list = [None]*i - for let in row: - if int(let) == let: - num_list.append(int(let)) - else: - num_list.append(str(int(let+0.5))+"'") - L.append(num_list) + L = [[None]*i + row for i, row in enumerate(self._repr_tab())] return tex_from_array(L) def max_element(self): @@ -509,7 +507,10 @@ def shape(self): sage: t.shape() [4, 2] """ - return ([len(row) for row in self]) + return ([len(self[i])+self._skew[i] + for i in range(len(self._skew))] + + [len(self[i]) + for i in range(len(self._skew), len(self))]) def __call__(self, *cell): """ @@ -542,9 +543,9 @@ def __call__(self, *cell): i, j = cell except ValueError: i, j = cell[0] - + skew = self._skew[i] if i < len(self._skew) else 0 try: - return self[i][j] + return self[i][j-skew] except IndexError: raise IndexError("invalid cell") @@ -595,6 +596,8 @@ def reading_word_with_positions(self): ((0, 2), 2), ((0, 3), 2)] """ + if self._skew != []: + raise NotImplementedError('skew tableau must be empty') mat = self.to_matrix() list_with_positions = [] for (i, j), x in np.ndenumerate(mat[:, ::-1].T): @@ -627,6 +630,8 @@ def reading_word(self): sage: t.reading_word() [3, 2, 2, 1, 2, 2] """ + if self._skew != []: + raise NotImplementedError('skew tableau must be empty') return [tup[1] for tup in self.reading_word_with_positions()] def f(self, ind): @@ -668,7 +673,8 @@ def f(self, ind): """ if self is None: return None - + if self._skew != []: + raise NotImplementedError('skew tableau must be empty') T = self.to_matrix() read_word = self.reading_word_with_positions() @@ -767,6 +773,8 @@ def e(self, ind): """ if self is None: return None + if self._skew != []: + raise NotImplementedError('skew tableau must be empty') T = self.to_matrix() read_word = self.reading_word_with_positions() @@ -825,78 +833,6 @@ def e(self, ind): return(ShiftedPrimedTableau(T)) - def epsilon(self, i): - r""" - Compute the value of the crystal function `\epsilon_i` applied to - a shifted primed tableau ``self``. - - The function `\epsilon_i` is defined to be the maximal number - of operators `e_i` one can apply to ``self`` before vanishing - into ``None``. - - INPUT: - - - ``self`` -- shifted primed tableau - - ``ind`` -- index of the function `\epsilon_i` associated with a - crystal operator `e_i`. - - OUTPUT: - - Value of the function `\epsilon_i`. - - EXAMPLES:: - - sage: t = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.5, 2.5), - ....: (2.0, 2.5, 3.0, 3.0), (3.0, 4.0)]) - sage: t.epsilon(2) - 3 - sage: s = t.e(2).e(2).e(2) - sage: s.epsilon(2) - 0 - """ - b = self - count = -1 - while b is not None: - b = b.e(i) - count += 1 - return count - - def phi(self, i): - r""" - Compute value of the crystal function `\phi_i` applied to a - shifted primed tableau ``self``. - - The function `\phi_i` is defined to be the maximal number of - operators `f_i` one can apply to ``self`` before vanishing - into ``None``. - - INPUT: - - - ``self`` -- shifted primed tableau - - ``ind`` -- index of the function `\epsilon_i` associated with a - crystal operator `f_i`. - - OUTPUT: - - Value of the function `\epsilon_i`. - - EXAMPLES:: - - sage: t = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.5, 2.0), - ....: (2.0, 2.0, 2.0, 2.5), (3.0, 4.0)]) - sage: t.phi(2) - 3 - sage: s = t.f(2).f(2).f(2) - sage: s.phi(2) - 0 - """ - b = self - count = -1 - while b is not None: - b = b.f(i) - count += 1 - return count - def is_highest_weight(self): """ Check wether the shifted primed tableau ``self`` is a highest weight @@ -918,6 +854,8 @@ def is_highest_weight(self): True """ + if self._skew != []: + raise NotImplementedError('skew tableau must be empty') read_w = self.reading_word() count = {} for l in read_w[::-1]: @@ -1190,9 +1128,9 @@ def _repr_(self): sage: ShiftedPrimedTableaux() Shifted Primed Tableaux """ - if self._skew==[]: + if self._skew == []: return "Shifted Primed Tableaux" - return "Shifted Primed Tableaux Skewed by {}".format(self._skew) + return "Shifted Primed Tableaux skewed by {}".format(self._skew) def _element_constructor_(self, T): """ @@ -1224,7 +1162,7 @@ def _element_constructor_(self, T): return self.element_class(self, T, skew=self._skew) except ValueError: raise ValueError( - "{} is not an element of Shifted Primed Tableaux".format(T)) + "{} is not an element of {}".format(T,self)) def __contains__(self, T): """ @@ -1248,7 +1186,7 @@ class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): """ Element = ShiftedPrimedTableau - def __init__(self, shape, max_elt, skew = []): + def __init__(self, shape, max_elt, skew=[]): """ Initialize the class of Shifted Primed Tableaux of a given shape. @@ -1277,13 +1215,13 @@ def _repr_(self): "Shifted Primed Tableaux of shape {} and maximum element {}" .format(self._shape, self._max_elt)) if self._max_elt is None and self._skew != []: - return ("Shifted Primed Tableaux skewed by {} of shape {}" - .format(self.skew, self._shape)) + return ("Shifted Primed Tableaux of shape {} skewed by {}" + .format(self._shape, self._skew)) if self._max_elt is not None and self._skew != []: return ( - "Shifted Primed Tableaux skewed by {}".format(self._skew) - + " of shape {} and maximum element {}" - .format(self._shape, self._max_elt)) + "Shifted Primed Tableaux of shape {} and maximum element {}" + .format(self._shape, self._max_elt) + + "skewed by {}".format(self._skew)) def __contains__(self, T): """ @@ -1334,7 +1272,7 @@ def _element_constructor_(self, T): Tab = self.element_class(self, T, skew=self._skew) except ValueError: raise ValueError( - "{} is not an element of Shifted Primed Tableaux".format(T)) + "{} is not an element of {}".format(T,self)) if self._max_elt is None: if Tab.shape() == self._shape: @@ -1462,8 +1400,8 @@ def _repr_(self): """ if self._skew == []: return "Shifted Primed Tableaux of weight {}".format(self._weight) - return "Shifted Primed Tableaux Skewed by {} of weight {}".format( - self._skew, self._weight) + return ("Shifted Primed Tableaux of weight {} skewed by {}" + .format(self._weight, self._skew)) def __contains__(self, T): """ @@ -1502,7 +1440,7 @@ def _element_constructor_(self, T): Tab = self.element_class(self, T, skew=self._skew) except ValueError: raise ValueError( - "{} is not an element of Shifted Primed Tableaux".format(T)) + "{} is not an element of {}".format(T,self)) if Tab.weight() == self._weight: return Tab raise ValueError("{} is not an element of {}".format(T, self)) @@ -1525,7 +1463,8 @@ def __iter__(self): return (tab for shape_ in Partitions(sum(self._weight)) if all(shape_[i] > shape_[i+1] for i in range(len(shape_)-1)) for tab in ShiftedPrimedTableaux(shape=shape_, - weight=self._weight)) + weight=self._weight, + skew=self._skew)) class ShiftedPrimedTableaux_weight_shape(ShiftedPrimedTableaux): @@ -1534,7 +1473,7 @@ class ShiftedPrimedTableaux_weight_shape(ShiftedPrimedTableaux): """ Element = ShiftedPrimedTableau - def __init__(self, weight, shape): + def __init__(self, weight, shape, skew=[]): """ Initialize the class of Shifted Primed Tableaux of the given weight and shape. @@ -1542,6 +1481,7 @@ def __init__(self, weight, shape): Parent.__init__(self, category=FiniteEnumeratedSets()) self._weight = weight self._shape = shape + self._skew = skew def _repr_(self): """ @@ -1552,9 +1492,13 @@ def _repr_(self): sage: ShiftedPrimedTableaux([3,2,1],(4,2)) Shifted Primed Tableaux of weight (4, 2) and shape [3, 2, 1] """ + if self._skew == []: + return ( + "Shifted Primed Tableaux of weight {} and shape {}" + .format(self._weight, self._shape)) return ( - "Shifted Primed Tableaux of weight {} and shape {}" .format( - self._weight, self._shape)) + "Shifted Primed Tableaux of weight {} and shape {} skewed by {}" + .format(self._weight, self._shape, self._skew)) def __contains__(self, T): """ @@ -1565,7 +1509,7 @@ def __contains__(self, T): True """ try: - Tab = self.element_class(self, T) + Tab = self.element_class(self, T, skew=self._skew) except ValueError: return False @@ -1592,7 +1536,7 @@ def _element_constructor_(self, T): Tab = self.element_class(self, T, skew=self._skew) except ValueError: raise ValueError( - "{} is not an element of Shifted Primed Tableaux".format(T)) + "{} is not an element of {}".format(T,self)) if Tab.shape() == self._shape and Tab.weight() == self._weight: return Tab From 0ddb89e423d43abd0a74554640662ec78e3ad23b Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Mon, 16 Oct 2017 14:17:31 +0200 Subject: [PATCH 404/740] Deleted __richcmp__ methods added __eq__ this does not work. --- src/sage/modules/free_module.py | 192 +------------------------------- 1 file changed, 4 insertions(+), 188 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 05bb3729b37..5215d553223 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -759,6 +759,9 @@ def __init__(self, base_ring, rank, degree, sparse=False, self.__is_sparse = sparse self._gram_matrix = None + def __eq__(self,other): + return self is other + def construction(self): """ The construction functor and base ring for self. @@ -4314,113 +4317,7 @@ def echelonized_basis_matrix(self): """ return self.basis_matrix() - def __richcmp__(self, other, op): - r""" - Compare the free module self with other. - - Modules are ordered by their ambient spaces, then by dimension, - then in order by their echelon matrices. However, if - other is a sub-module or is a quotient module then its - comparison method is used instead of generic comparison. - - EXAMPLES: - - We compare rank three free modules over the integers and - rationals:: - - sage: QQ^3 < CC^3 - True - sage: CC^3 < QQ^3 - False - sage: CC^3 > QQ^3 - True - - :: - - sage: Q = QQ; Z = ZZ - sage: Q^3 > Z^3 - True - sage: Q^3 < Z^3 - False - sage: Z^3 < Q^3 - True - sage: Z^3 > Q^3 - False - sage: Q^3 == Z^3 - False - sage: Q^3 == Q^3 - True - - Comparison with a sub-module:: - - sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) - sage: V - Vector space of degree 3 and dimension 2 over Rational Field - Basis matrix: - [ 1 0 -1] - [ 0 1 2] - sage: A = QQ^3 - sage: V < A - True - sage: A < V - False - - Comparison with a quotient module (see :trac:`10513`):: - - sage: M = QQ^3 / [[1,2,3]] - sage: V = QQ^2 - sage: V == M - False - sage: M == V - False - We create the module `\ZZ^3`, and the submodule generated by - one vector `(1,1,0)`, and check whether certain elements are - in the submodule:: - - sage: R = FreeModule(ZZ, 3) - sage: V = R.submodule([R.gen(0) + R.gen(1)]) - sage: R.gen(0) + R.gen(1) in V - True - sage: R.gen(0) + 2*R.gen(1) in V - False - - sage: w = (1/2)*(R.gen(0) + R.gen(1)) - sage: w - (1/2, 1/2, 0) - sage: w.parent() - Vector space of dimension 3 over Rational Field - sage: w in V - False - sage: V.coordinates(w) - [1/2] - """ - if self is other: - return rich_to_bool(op, 0) - if not isinstance(other, FreeModule_generic): - return NotImplemented - from sage.modules.quotient_module import FreeModule_ambient_field_quotient - if isinstance(other, FreeModule_ambient) and not isinstance(other, FreeModule_ambient_field_quotient): - lx = self.rank() - rx = other.rank() - if lx != rx: - return richcmp_not_equal(lx, rx, op) - lx = self.base_ring() - rx = other.base_ring() - if lx == rx: - return rich_to_bool(op, 0) - try: - if self.base_ring().is_subring(other.base_ring()): - return rich_to_bool(op, -1) - elif other.base_ring().is_subring(self.base_ring()): - return rich_to_bool(op, 1) - except NotImplementedError: - pass - return richcmp_not_equal(lx, rx, op) - else: - # now other is not ambient or is a quotient; - # it knows how to do the comparison. - return richcmp(other, self, revop(op)) def _repr_(self): """ @@ -5423,88 +5320,7 @@ def _echelonized_basis(self, ambient, basis): self.__echelonized_basis_matrix = E return E.rows() - def __richcmp__(self, other, op): - r""" - Compare the free module self with other. - - Modules are ordered by their ambient spaces, then by dimension, - then in order by their echelon matrices. - - .. note:: - - Use :meth:`is_submodule` to determine if one - module is a submodule of another. - - EXAMPLES: - - First we compare two equal vector spaces. - - :: - - sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) - sage: W = span([[5,6,7], [8,9,10]], QQ) - sage: V == W - True - - Next we compare a one dimensional space to the two dimensional - space defined above. - - :: - - sage: M = span([[5,6,7]], QQ) - sage: V == M - False - sage: M < V - True - sage: V < M - False - - We compare a `\ZZ`-module to the one-dimensional - space above. - - :: - - sage: V = span([[5,6,7]], ZZ).scale(1/11); V - Free module of degree 3 and rank 1 over Integer Ring - Echelon basis matrix: - [5/11 6/11 7/11] - sage: V < M - True - sage: M < V - False - - We test that :trac:`5525` is fixed:: - - sage: A = (QQ^1).span([[1/3]],ZZ); B = (QQ^1).span([[1]],ZZ); - sage: A.intersection(B) - Free module of degree 1 and rank 1 over Integer Ring - Echelon basis matrix: - [1] - - """ - if self is other: - return rich_to_bool(op, 0) - if not isinstance(other, FreeModule_generic): - return NotImplemented - lx = self.ambient_vector_space() - rx = other.ambient_vector_space() - if lx != rx: - return richcmp_not_equal(lx, rx, op) - - lx = self.dimension() - rx = other.dimension() - if lx != rx: - return richcmp_not_equal(lx, rx, op) - - lx = self.base_ring() - rx = other.base_ring() - if lx != rx: - return richcmp_not_equal(lx, rx, op) - - # We use self.echelonized_basis_matrix() == other.echelonized_basis_matrix() - # with the matrix to avoid a circular reference. - return richcmp(self.echelonized_basis_matrix(), - other.echelonized_basis_matrix(), op) + def _denominator(self, B): """ From a48e875a017f8449ea1b220aebf8f11421c06be9 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Mon, 16 Oct 2017 14:59:51 +0200 Subject: [PATCH 405/740] Removed the @richcmp_method decorator. --- src/sage/modules/free_module.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 5215d553223..e70229afea8 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -179,8 +179,6 @@ from sage.categories.principal_ideal_domains import PrincipalIdealDomains from sage.misc.randstate import current_randstate from sage.structure.sequence import Sequence -from sage.structure.richcmp import (richcmp_method, rich_to_bool, richcmp, - richcmp_not_equal, revop) from sage.misc.cachefunc import cached_method @@ -4180,7 +4178,6 @@ def quotient_abstract(self, sub, check=True): # ############################################################################### -@richcmp_method class FreeModule_ambient(FreeModule_generic): """ Ambient free module over a commutative ring. @@ -5103,7 +5100,6 @@ def _element_constructor_(self, e, *args, **kwds): # ############################################################################### -@richcmp_method class FreeModule_submodule_with_basis_pid(FreeModule_generic_pid): r""" Construct a submodule of a free module over PID with a distinguished basis. From 51e7afaf5f1027f10baa146a390271c7391b2400 Mon Sep 17 00:00:00 2001 From: pmenegat Date: Mon, 16 Oct 2017 19:51:30 +0200 Subject: [PATCH 406/740] Replaced `__richcmp__` operator by comparison methods (`__le__`,`__lt__`, etc.) using `is_submodule` Minor modification to method `is_submodule` for comparing modules over different ambient fields --- src/sage/modules/free_module.py | 521 +++++++++++++++++++------------- 1 file changed, 317 insertions(+), 204 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 05bb3729b37..624855bd2b5 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -179,8 +179,6 @@ from sage.categories.principal_ideal_domains import PrincipalIdealDomains from sage.misc.randstate import current_randstate from sage.structure.sequence import Sequence -from sage.structure.richcmp import (richcmp_method, rich_to_bool, richcmp, - richcmp_not_equal, revop) from sage.misc.cachefunc import cached_method @@ -1079,16 +1077,10 @@ def is_submodule(self, other): sage: vector(ZZ['x']['y'],[1,2,3,4]) * QQ(1/2) (1/2, 1, 3/2, 2) """ + if self is other: + return True if not isinstance(other, FreeModule_generic): return False - try: - if self.ambient_vector_space() != other.ambient_vector_space(): - return False - if other == other.ambient_vector_space(): - return True - except AttributeError: - # Not all free modules have an ambient_vector_space. - pass if other.rank() < self.rank(): return False R = self.base_ring() @@ -1099,12 +1091,326 @@ def is_submodule(self, other): return False except NotImplementedError: return False + try: + if other is other.ambient_vector_space(): + return True + except AttributeError: + # Not all free modules have an ambient_vector_space. + pass try: M = other.basis_matrix().solve_left(self.basis_matrix()) except ValueError: return False return all(x in S for x in M.list()) + def __le__(self,other): + r""" + Return ``True`` if ``self`` is contained in ``other``. + + See :meth:`is_submodule`. + + EXAMPLES: + + We compare rank three free modules over the integers and + rationals:: + + sage: QQ^3 <= CC^3 + True + sage: CC^3 <= QQ^3 + False + sage: QQ^3 <= QQ^3 + True + + :: + + sage: Q = QQ; Z = ZZ; C=CC + sage: Q^3 <= Z^3 + False + sage: Z^3 <= Q^3 + True + sage: Z^3 <= C^3 + True + sage: C^3 <= Z^3 + False + + Comparison with a sub-module:: + + sage: A = QQ^3 + sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) + sage: V + Vector space of degree 3 and dimension 2 over Rational Field + Basis matrix: + [ 1 0 -1] + [ 0 1 2] + sage: V <= A + True + sage: A <= V + False + sage: L1 = span([[1,2,3], [5,6,7], [8,9,10]], ZZ) + sage: L2 = span([[2,4,6], [10,12,14], [16,18,20]], ZZ) + sage: 2*L1 <= L2 + True + sage: L2 <= L1 + True + sage: L1 <= L2 + False + """ + return self.is_submodule(other) + + def __lt__(self,other): + r""" + Return ``True`` if ``self`` is costrictly ntained in ``other``. + + See :meth:`is_submodule`. + + EXAMPLES: + + We compare rank three free modules over the integers and + rationals:: + + sage: QQ^3 < CC^3 + True + sage: CC^3 < QQ^3 + False + sage: QQ^3 < QQ^3 + False + + :: + + sage: Q = QQ; Z = ZZ; C=CC + sage: Q^3 < Z^3 + False + sage: Z^3 < Q^3 + True + sage: Z^3 < C^3 + True + sage: C^3 < Z^3 + False + + Comparison with a sub-module:: + + sage: A = QQ^3 + sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) + sage: V + Vector space of degree 3 and dimension 2 over Rational Field + Basis matrix: + [ 1 0 -1] + [ 0 1 2] + sage: V < A + True + sage: A < V + False + sage: L1 = span([[1,2,3], [5,6,7], [8,9,10]], ZZ) + sage: L2 = span([[2,4,6], [10,12,14], [16,18,20]], ZZ) + sage: 2*L1 < L2 + False + sage: L2 < L1 + True + sage: L1 < L2 + False + """ + if self <= other: + R = self.base_ring() + S = other.base_ring() + if R!=S: + return True + try: + M2 = self.basis_matrix().solve_left(other.basis_matrix()) + except ValueError: + return True + return not all(y in R for y in M2.list()) + else: + return False + + def __ge__(self,other): + r""" + Return ``True`` if ``self`` contain ``other``. + + See :meth:`is_submodule`. + + EXAMPLES: + + We compare rank three free modules over the integers and + rationals:: + + sage: QQ^3 >= CC^3 + False + sage: CC^3 >= QQ^3 + True + sage: QQ^3 >= QQ^3 + True + + :: + + Comparison with a sub-module:: + + sage: A = QQ^3 + sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) + sage: V + Vector space of degree 3 and dimension 2 over Rational Field + Basis matrix: + [ 1 0 -1] + [ 0 1 2] + sage: V >= A + False + sage: A >= V + True + sage: L1 = span([[1,2,3], [5,6,7], [8,9,10]], ZZ) + sage: L2 = span([[2,4,6], [10,12,14], [16,18,20]], ZZ) + sage: 2*L1 >= L2 + True + sage: L2 >= L1 + False + sage: L1 >= L2 + True + """ + return other.is_submodule(self) + + def __gr__(self,other): + r""" + Return ``True`` if ``self`` strictly contain ``other``. + + See :meth:`is_submodule`. + + EXAMPLES: + + We compare rank three free modules over the integers and + rationals:: + + sage: QQ^3 > CC^3 + False + sage: CC^3 > QQ^3 + True + sage: QQ^3 > QQ^3 + False + + :: + + Comparison with a sub-module:: + + sage: A = QQ^3 + sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) + sage: V + Vector space of degree 3 and dimension 2 over Rational Field + Basis matrix: + [ 1 0 -1] + [ 0 1 2] + sage: V > A + False + sage: A > V + True + sage: L1 = span([[1,2,3], [5,6,7], [8,9,10]], ZZ) + sage: L2 = span([[2,4,6], [10,12,14], [16,18,20]], ZZ) + sage: 2*L1 > L2 + False + sage: L2 > L1 + False + sage: L1 > L2 + True + """ + return other < self + + def __eq__(self,other): + r""" + Return ``True`` if ``self`` and ``other`` are equals. + + See :meth:`is_submodule`. + + EXAMPLES: + + We compare rank three free modules over the integers and + rationals:: + + sage: QQ^3 == CC^3 + False + sage: QQ^3 == QQ^3 + True + + :: + + Comparison with a sub-module:: + + sage: A = QQ^3 + sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) + sage: V + Vector space of degree 3 and dimension 2 over Rational Field + Basis matrix: + [ 1 0 -1] + [ 0 1 2] + sage: V == A + False + sage: L1 = span([[1,2,3], [5,6,7], [8,9,10]], ZZ) + sage: L2 = span([[2,4,6], [10,12,14], [16,18,20]], ZZ) + sage: 2*L1 == L2 + True + sage: L2 == L1 + False + sage: L1 == L2 + False + """ + if self is other: + return True + if not isinstance(other, FreeModule_generic): + return False + if other.rank() != self.rank(): + return False + R = self.base_ring() + S = other.base_ring() + if R != S: + return False + try: + M1 = other.basis_matrix().solve_left(self.basis_matrix()) + except ValueError: + return False + if all(x in S for x in M1.list()): + try: + M2 = self.basis_matrix().solve_left(other.basis_matrix()) + except ValueError: + return False + return all(y in R for y in M2.list()) + else: + return False + + def __ne__(self,other): + r""" + Return ``True`` if ``self`` and ``other`` are not equals. + + See :meth:`is_submodule`. + + EXAMPLES: + + We compare rank three free modules over the integers and + rationals:: + + sage: QQ^3 != CC^3 + True + sage: QQ^3 != QQ^3 + False + + :: + + Comparison with a sub-module:: + + sage: A = QQ^3 + sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) + sage: V + Vector space of degree 3 and dimension 2 over Rational Field + Basis matrix: + [ 1 0 -1] + [ 0 1 2] + sage: V != A + True + sage: L1 = span([[1,2,3], [5,6,7], [8,9,10]], ZZ) + sage: L2 = span([[2,4,6], [10,12,14], [16,18,20]], ZZ) + sage: 2*L1 != L2 + False + sage: L2 != L1 + True + sage: L1 != L2 + True + """ + return not (self == other) + def __iter__(self): """ Return iterator over the elements of this free module. @@ -4177,7 +4483,6 @@ def quotient_abstract(self, sub, check=True): # ############################################################################### -@richcmp_method class FreeModule_ambient(FreeModule_generic): """ Ambient free module over a commutative ring. @@ -4313,115 +4618,7 @@ def echelonized_basis_matrix(self): [ 0 0 1 -1] """ return self.basis_matrix() - - def __richcmp__(self, other, op): - r""" - Compare the free module self with other. - - Modules are ordered by their ambient spaces, then by dimension, - then in order by their echelon matrices. However, if - other is a sub-module or is a quotient module then its - comparison method is used instead of generic comparison. - - EXAMPLES: - - We compare rank three free modules over the integers and - rationals:: - - sage: QQ^3 < CC^3 - True - sage: CC^3 < QQ^3 - False - sage: CC^3 > QQ^3 - True - - :: - - sage: Q = QQ; Z = ZZ - sage: Q^3 > Z^3 - True - sage: Q^3 < Z^3 - False - sage: Z^3 < Q^3 - True - sage: Z^3 > Q^3 - False - sage: Q^3 == Z^3 - False - sage: Q^3 == Q^3 - True - - Comparison with a sub-module:: - - sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) - sage: V - Vector space of degree 3 and dimension 2 over Rational Field - Basis matrix: - [ 1 0 -1] - [ 0 1 2] - sage: A = QQ^3 - sage: V < A - True - sage: A < V - False - - Comparison with a quotient module (see :trac:`10513`):: - - sage: M = QQ^3 / [[1,2,3]] - sage: V = QQ^2 - sage: V == M - False - sage: M == V - False - - We create the module `\ZZ^3`, and the submodule generated by - one vector `(1,1,0)`, and check whether certain elements are - in the submodule:: - - sage: R = FreeModule(ZZ, 3) - sage: V = R.submodule([R.gen(0) + R.gen(1)]) - sage: R.gen(0) + R.gen(1) in V - True - sage: R.gen(0) + 2*R.gen(1) in V - False - - sage: w = (1/2)*(R.gen(0) + R.gen(1)) - sage: w - (1/2, 1/2, 0) - sage: w.parent() - Vector space of dimension 3 over Rational Field - sage: w in V - False - sage: V.coordinates(w) - [1/2] - """ - if self is other: - return rich_to_bool(op, 0) - if not isinstance(other, FreeModule_generic): - return NotImplemented - from sage.modules.quotient_module import FreeModule_ambient_field_quotient - if isinstance(other, FreeModule_ambient) and not isinstance(other, FreeModule_ambient_field_quotient): - lx = self.rank() - rx = other.rank() - if lx != rx: - return richcmp_not_equal(lx, rx, op) - lx = self.base_ring() - rx = other.base_ring() - if lx == rx: - return rich_to_bool(op, 0) - try: - if self.base_ring().is_subring(other.base_ring()): - return rich_to_bool(op, -1) - elif other.base_ring().is_subring(self.base_ring()): - return rich_to_bool(op, 1) - except NotImplementedError: - pass - return richcmp_not_equal(lx, rx, op) - else: - # now other is not ambient or is a quotient; - # it knows how to do the comparison. - return richcmp(other, self, revop(op)) - + def _repr_(self): """ The printing representation of self. @@ -5206,7 +5403,6 @@ def _element_constructor_(self, e, *args, **kwds): # ############################################################################### -@richcmp_method class FreeModule_submodule_with_basis_pid(FreeModule_generic_pid): r""" Construct a submodule of a free module over PID with a distinguished basis. @@ -5423,89 +5619,6 @@ def _echelonized_basis(self, ambient, basis): self.__echelonized_basis_matrix = E return E.rows() - def __richcmp__(self, other, op): - r""" - Compare the free module self with other. - - Modules are ordered by their ambient spaces, then by dimension, - then in order by their echelon matrices. - - .. note:: - - Use :meth:`is_submodule` to determine if one - module is a submodule of another. - - EXAMPLES: - - First we compare two equal vector spaces. - - :: - - sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) - sage: W = span([[5,6,7], [8,9,10]], QQ) - sage: V == W - True - - Next we compare a one dimensional space to the two dimensional - space defined above. - - :: - - sage: M = span([[5,6,7]], QQ) - sage: V == M - False - sage: M < V - True - sage: V < M - False - - We compare a `\ZZ`-module to the one-dimensional - space above. - - :: - - sage: V = span([[5,6,7]], ZZ).scale(1/11); V - Free module of degree 3 and rank 1 over Integer Ring - Echelon basis matrix: - [5/11 6/11 7/11] - sage: V < M - True - sage: M < V - False - - We test that :trac:`5525` is fixed:: - - sage: A = (QQ^1).span([[1/3]],ZZ); B = (QQ^1).span([[1]],ZZ); - sage: A.intersection(B) - Free module of degree 1 and rank 1 over Integer Ring - Echelon basis matrix: - [1] - - """ - if self is other: - return rich_to_bool(op, 0) - if not isinstance(other, FreeModule_generic): - return NotImplemented - lx = self.ambient_vector_space() - rx = other.ambient_vector_space() - if lx != rx: - return richcmp_not_equal(lx, rx, op) - - lx = self.dimension() - rx = other.dimension() - if lx != rx: - return richcmp_not_equal(lx, rx, op) - - lx = self.base_ring() - rx = other.base_ring() - if lx != rx: - return richcmp_not_equal(lx, rx, op) - - # We use self.echelonized_basis_matrix() == other.echelonized_basis_matrix() - # with the matrix to avoid a circular reference. - return richcmp(self.echelonized_basis_matrix(), - other.echelonized_basis_matrix(), op) - def _denominator(self, B): """ The LCM of the denominators of the given list B. From f4b7371acd0483303db093dca6af0eb126cc8085 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Mon, 16 Oct 2017 12:03:55 -0700 Subject: [PATCH 407/740] More skew --- src/sage/combinat/tableau_shifted_primed.py | 76 +++++++++++++++++---- 1 file changed, 61 insertions(+), 15 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index 7bef3f47fe2..0329c2c5975 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -39,6 +39,8 @@ class ShiftedPrimedTableau(ClonableArray): sage: t = ShiftedPrimedTableau([[1,"2p",2.5,3],[0,2,2.5]]) sage: t[1] (2.0, 2.5) + sage: ShiftedPrimedTableau([["2p",2,3],["2p","3p"],[2]], skew=[2,1]) + [(1.5, 2.0, 3.0), (1.5, 2.5), (2.0,)] skewed by [2, 1] TEST:: @@ -65,7 +67,9 @@ def __classcall_private__(cls, T, skew=[]): sage: S = ShiftedPrimedTableaux(shape=[4,2]) sage: t == S(data) True - + sage: t = ShiftedPrimedTableau([["2p",2,3],["2p"],skew=[2,1]) + sage: t.parent() + Shifted Primed Tableaux skewed by [2, 1] """ if (isinstance(T, cls) and T._skew == skew): @@ -185,6 +189,8 @@ def __eq__(self, other): sage: t == ShiftedPrimedTableaux([2])([1,1.5]) True """ + if isinstance(other, ShiftedPrimedTableau): + return (self._skew == other._skew and list(self) == list(other)) try: Tab = ShiftedPrimedTableau(other) except ValueError: @@ -221,11 +227,13 @@ def check(self): """ Check that ``self`` is a valid primed tableaux. - EXAMPLE:: + EXAMPLES:: sage: T = ShiftedPrimedTableaux([4,2]) sage: t = T([[1,'2p',2,2],[2,'3p']]) sage: t.check() + sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + sage: s.check() """ skew = self._skew + [0]*(len(self)-len(self._skew)) if not all(self._skew[i] > self._skew[i+1] @@ -264,11 +272,13 @@ def _repr_(self): Represent Shifted Primed Tableau as a list of rows, rows are represented as tuples of half-integers. - EXAMPLE:: + EXAMPLES:: sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t [(1.0, 1.5, 2.0, 2.0), (2.0, 2.5)] + sage: ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + [(1.5, 2.0, 3.0), (1.5,)] skewed by [2, 1] """ if self._skew == []: return repr([tuple(_) for _ in self]) @@ -279,11 +289,14 @@ def _repr_tab(self): """ Return a nested list of strings representing the elements. - TEST:: + TESTS:: sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t._repr_tab() [[' 1 ', " 2'", ' 2 ', ' 2 '], [' 2 ', " 3'"]] + sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + sage: s._repr_tab() + [[' . ', ' . ', " 2'", ' 2 ', ' 3 '], [' . ', " 2'"]] """ skew = self._skew + [0]*(len(self)-len(self._skew)) max_len = len(str(self.max_element()))+1 @@ -308,13 +321,16 @@ def _repr_diagram(self): sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: print(t._repr_diagram()) - 1 2' 2 2 - 2 3' + 1 2' 2 2 + 2 3' sage: t = ShiftedPrimedTableau([[10,'11p',11,11],[11,'12']]) sage: print(t._repr_diagram()) - 10 11' 11 11 - 11 12 - + 10 11' 11 11 + 11 12 + sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + sage: print(s._repr_diagram()) + . . 2' 2 3 + . 2' """ max_len = len(str(self.max_element()))+2 return "\n".join([" "*max_len*i + "".join(_) @@ -324,7 +340,7 @@ def _ascii_art_(self): """ Return ASCII representaion of a tableau. - EXAMPLE:: + EXAMPLES:: sage: ascii_art(ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']])) +---+---+---+---+ @@ -332,6 +348,13 @@ def _ascii_art_(self): +---+---+---+---+ | 2 | 3'| +---+---+ + sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + sage: ascii_art(s) + +---+---+---+---+---+ + | . | . | 2'| 2 | 3 | + +---+---+---+---+---+ + | . | 2'| + +---+---+ TEST:: @@ -354,6 +377,13 @@ def _unicode_art_(self): └───┼───┼───┼───┘ │ 2 │ 3'│ └───┴───┘ + sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + sage: unicode_art(s) + ┌───┬───┬───┬───┬───┐ + │ . │ . │ 2'│ 2 │ 3 │ + └───┼───┼───┼───┴───┘ + │ . │ 2'│ + └───┴───┘ TEST:: sage: unicode_art(ShiftedPrimedTableau([])) @@ -393,7 +423,13 @@ def _ascii_art_table(self, unicode=False): +----+----+----+----+ | 2 | 30'| +----+----+ - + sage: s = ShiftedPrimedTableau([["2p",2,10],["2p"]],skew=[2,1]) + sage: print(s._ascii_art_table(unicode=True)) + ┌────┬────┬────┬────┬────┐ + │ . │ . │ 2'│ 2 │ 10 │ + └────┼────┼────┼────┴────┘ + │ . │ 2'│ + └────┴────┘ """ if unicode: import unicodedata @@ -458,6 +494,9 @@ def pp(self): sage: t.pp() 10 11' 11 11 11 12 + sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + . . 2' 2 3 + . 2' """ print(self._repr_diagram()) @@ -506,6 +545,9 @@ def shape(self): sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t.shape() [4, 2] + sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + sage: s.shape() + [5, 2] """ return ([len(self[i])+self._skew[i] for i in range(len(self._skew))] + @@ -537,6 +579,9 @@ def __call__(self, *cell): IndexError: invalid cell sage: t((1,1)) 2.5 + sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + sage: s(0,2) + 1.5 """ try: @@ -953,21 +998,22 @@ def __classcall_private__(cls, *args, **kwargs): Traceback (most recent call last): ... ValueError: invalid argument for weight or shape - sage: ShiftedPrimedTableaux(weight=(2,2,2), shape=[3,2]) Traceback (most recent call last): ... ValueError: weight and shape are incompatible - sage: ShiftedPrimedTableaux([[1]]) Traceback (most recent call last): ... ValueError: invalid shape argument - sage: ShiftedPrimedTableaux(weight=(2,2,2), max_element=2) Traceback (most recent call last): ... ValueError: maximum element is incompatible with the weight + sage: ShiftedPrimedTableaux(shape=[4,1],skew=[3,2]) + Traceback (most recent call last): + ... + ValueError: skew shape must be inside the given tableau shape """ weight = None shape = None @@ -1162,7 +1208,7 @@ def _element_constructor_(self, T): return self.element_class(self, T, skew=self._skew) except ValueError: raise ValueError( - "{} is not an element of {}".format(T,self)) + "{} is not an element of {}".format(T, self)) def __contains__(self, T): """ From 841c08d8edaf4ddcebedccf9081e05355db06f5d Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 17 Oct 2017 23:54:03 -0500 Subject: [PATCH 408/740] Some simple doc formatting things. --- src/sage/combinat/tableau_shifted_primed.py | 156 ++++++++++---------- 1 file changed, 74 insertions(+), 82 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index f8bd94c7a15..c5e5debd73a 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -24,12 +24,12 @@ class ShiftedPrimedTableau(ClonableArray): A shifted primed tableau with primed elements stored as half-integers. A primed tableau is a shifted tableau on the alphabet - X' = {1' < 1 < 2' < 2 <...< n' < n} such that + `X' = \{1' < 1 < 2' < 2 < \cdots < n' < n\}` such that - 1. the entries are weakly increasing along rows and columns - 2. a row can't have two repeated primed elements, and a column - can't have two repeated non-primed elements - 3. there are only non-primed elements on the main diagonal + 1. the entries are weakly increasing along rows and columns + 2. a row can't have two repeated primed elements, and a column + can't have two repeated non-primed elements + 3. there are only non-primed elements on the main diagonal EXAMPLES:: @@ -40,15 +40,14 @@ class ShiftedPrimedTableau(ClonableArray): sage: t[1] (2.0, 2.5) - TEST:: + TESTS:: sage: t = ShiftedPrimedTableau([[1,2,2.5,3],[0,2,2.5]]) Traceback (most recent call last): ... ValueError: [[1, 2, 2.50000000000000, 3], [0, 2, 2.50000000000000]] - is not an element of Shifted Primed Tableaux + is not an element of Shifted Primed Tableaux """ - @staticmethod def __classcall_private__(cls, T): r""" @@ -65,12 +64,9 @@ def __classcall_private__(cls, T): sage: S = ShiftedPrimedTableaux(shape=[4,2]) sage: t == S(data) True - """ - if isinstance(T, cls): return T - return ShiftedPrimedTableaux()(T) def __init__(self, parent, T): @@ -178,7 +174,7 @@ def __eq__(self, other): Boolean. - EXAMPLE:: + EXAMPLES:: sage: t = ShiftedPrimedTableau([[1,"2p"]]) sage: t == ShiftedPrimedTableaux([2])([1,1.5]) @@ -194,7 +190,7 @@ def _to_matrix(self): """ Return a 2-dimensional numpy.array representation of a shifted tableau - EXAMPLE:: + EXAMPLES:: sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p'],[3]]) sage: mat = t._to_matrix() @@ -216,7 +212,7 @@ def check(self): """ Check that ``self`` is a valid primed tableaux. - EXAMPLE:: + EXAMPLES:: sage: T = ShiftedPrimedTableaux([4,2]) sage: t = T([[1,'2p',2,2],[2,'3p']]) @@ -248,7 +244,7 @@ def _repr_(self): Represent Shifted Primed Tableau as a list of rows, rows are represented as tuples of half-integers. - EXAMPLE:: + EXAMPLES:: sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t @@ -260,7 +256,7 @@ def _repr_tab(self): """ Return a nested list of strings representing the elements. - TEST:: + TESTS:: sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t._repr_tab() @@ -316,7 +312,7 @@ def _ascii_art_(self): """ Return ASCII representaion of a tableau. - EXAMPLE:: + EXAMPLES:: sage: ascii_art(ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']])) +---+---+---+---+ @@ -325,7 +321,7 @@ def _ascii_art_(self): | 2 | 3'| +---+---+ - TEST:: + TESTS:: sage: ascii_art(ShiftedPrimedTableau([])) ++ @@ -338,7 +334,7 @@ def _unicode_art_(self): """ Return a Unicode representation of a tableau. - EXAMPLE:: + EXAMPLES:: sage: unicode_art(ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']])) ┌───┬───┬───┬───┐ @@ -347,7 +343,8 @@ def _unicode_art_(self): │ 2 │ 3'│ └───┴───┘ - TEST:: + TESTS:: + sage: unicode_art(ShiftedPrimedTableau([])) ┌┐ └┘ @@ -385,7 +382,6 @@ def _ascii_art_table(self, unicode=False): +----+----+----+----+ | 2 | 30'| +----+----+ - """ if unicode: import unicodedata @@ -457,7 +453,7 @@ def _latex_(self): r""" Return LaTex code for ``self``. - EXAMPLE:: + EXAMPLES:: sage: T = ShiftedPrimedTableaux([4,2]) sage: latex(T([[1,"2p",2,"3p"],[2,3]])) @@ -484,7 +480,7 @@ def max_element(self): """ Return the maximum element in the primed tableaux ``self``, rounded up. - EXAMPLE:: + EXAMPLES:: sage: Tab = ShiftedPrimedTableau([(1,1,1.5,2.5),(2,2)]) sage: Tab.max_element() @@ -554,7 +550,7 @@ def weight(self): with i-th component equal to the number of letters i and i' in the tableau. - EXAMPLE:: + EXAMPLES:: sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t.weight() @@ -573,19 +569,20 @@ def _reading_word_with_positions(self): Return the reading word of ``self`` together with positions of the corresponding letters in ``self``. - The reading word of a shifted primed tableau is constructed as follows: + The reading word of a shifted primed tableau is constructed + as follows: - 1. List all primed letters in the tableau, column by - column, in decreasing order within each column, moving - from the rightmost column to the left, and with all - the primes removed (i.e. all letters are increased by - half a unit). + 1. List all primed letters in the tableau, column by + column, in decreasing order within each column, moving + from the rightmost column to the left, and with all + the primes removed (i.e. all letters are increased by + half a unit). - 2. Then list all unprimed elements, row by row, in - increasing order within each row, moving from the - bottommost row to the top. + 2. Then list all unprimed elements, row by row, in + increasing order within each row, moving from the + bottommost row to the top. - EXAMPLE:: + EXAMPLES:: sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t._reading_word_with_positions() @@ -607,19 +604,20 @@ def reading_word(self): """ Return the reading word of ``self``. - The reading word of a shifted primed tableau is constructed as follows: + The reading word of a shifted primed tableau is constructed + as follows: - 1. List all primed letters in the tableau, column by - column, in decreasing order within each column, moving - from the rightmost column to the left, and with all - the primes removed (i.e. all letters are increased by - half a unit). + 1. List all primed letters in the tableau, column by + column, in decreasing order within each column, moving + from the rightmost column to the left, and with all + the primes removed (i.e. all letters are increased by + half a unit). - 2. Then list all unprimed elements, row by row, in - increasing order within each row, moving from the - bottommost row to the top. + 2. Then list all unprimed elements, row by row, in + increasing order within each row, moving from the + bottommost row to the top. - EXAMPLE:: + EXAMPLES:: sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t.reading_word() @@ -634,12 +632,11 @@ def f(self, ind): INPUT:: - - ``self`` -- shifted primed tableau - - ``ind`` -- index of the crystal operator `f_i`. + - ``ind`` -- index of the crystal operator `f_i` OUTPUT: - Primed tableau or 'None'. + Primed tableau or ``None``. EXAMPLES:: @@ -739,8 +736,7 @@ def e(self, ind): INPUT: - - ``self`` -- shifted primed tableau - - ``ind`` -- index of the crystal operator `e_i`. + - ``ind`` -- index of the crystal operator `e_i` OUTPUT: @@ -828,7 +824,6 @@ def is_highest_weight(self): Check wether the shifted primed tableau ``self`` is a highest weight element of the crystal. - An element is highest weight if it vanishes under any crystal operator `e_i`. @@ -884,12 +879,12 @@ class ShiftedPrimedTableaux(UniqueRepresentation, Parent): weight (finite set) A primed tableau is a shifted tableau on the alphabet - X' = {1' < 1 < 2' < 2 <...< n' < n} such that + `X' = \{1' < 1 < 2' < 2 < \cdots < n' < n\}` such that - 1. the entries are weakly increasing along rows and columns - 2. a row can't have two repeated primed elements, and a column - can't have two repeated non-primed elements - 3. there are only non-primed elements along the main diagonal + 1. the entries are weakly increasing along rows and columns + 2. a row can't have two repeated primed elements, and a column + can't have two repeated non-primed elements + 3. there are only non-primed elements along the main diagonal The weight of a tableau is defined to be the vector with i-th component equal to the number of letters i and i' in the tableau. @@ -934,7 +929,6 @@ def __classcall_private__(cls, *args, **kwargs): arguments. See the documentation for :class:`ShiftedPrimedTableaux` for more information. - TESTS:: sage: ShiftedPrimedTableaux([]) @@ -1108,7 +1102,7 @@ def _repr_(self): """ Return a string representation of ``self``. - TEST:: + TESTS:: sage: ShiftedPrimedTableaux() Shifted Primed Tableaux @@ -1183,7 +1177,7 @@ def __init__(self, shape, max_elt): If ``max_elt`` is specified, a finite set with entries smaller or equal to ``max_elt``. - TEST:: + TESTS:: sage: TestSuite( ShiftedPrimedTableaux([4,2,1], max_elt=4)).run() """ @@ -1195,7 +1189,7 @@ def _repr_(self): """ Return a string representation of ``self``. - TEST:: + TESTS:: sage: ShiftedPrimedTableaux([3,2,1]) Shifted Primed Tableaux of shape [3, 2, 1] @@ -1209,12 +1203,11 @@ def _repr_(self): def __contains__(self, T): """ - TEST:: + TESTS:: sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux([4,2]) True """ - try: Tab = self.element_class(self, T) except ValueError: @@ -1271,7 +1264,7 @@ def shape(self): """ Return the shape of the shifted tableaux ``self``. - TEST:: + TESTS:: sage: ShiftedPrimedTableaux([6,4,3,1]).shape() [6, 4, 3, 1] @@ -1282,7 +1275,7 @@ def __iter__(self): """ Iterate over ``self``, if ``max_element`` is specified. - EXAMPLE:: + EXAMPLES:: sage: Tabs = ShiftedPrimedTableaux([3,2], max_element=3) sage: Tabs[:4] @@ -1293,7 +1286,7 @@ def __iter__(self): sage: len(list(Tabs)) 24 - TEST:: + TESTS:: sage: Tabs = ShiftedPrimedTableaux([3,2]) sage: Tabs[:3] @@ -1314,7 +1307,7 @@ def list_decreasing_weight(self): """ List elements of ``self`` with weakly decreasing weight. - EXAMPLE:: + EXAMPLES:: sage: Tabs = ShiftedPrimedTableaux([2,1]) sage: Tabs.list_decreasing_weight() @@ -1336,7 +1329,7 @@ def list_highest_weight(self): List elements of ``self`` that are highest weight elements in the crystal. - EXAMPLE:: + EXAMPLES:: sage: Tabs = ShiftedPrimedTableaux([3,1]) sage: Tabs.list_highest_weight() @@ -1366,7 +1359,7 @@ def __init__(self, weight): """ Initialize the class of Shifted Primed Tableaux of a given weight. - TEST:: + TESTS:: sage: TestSuite( ShiftedPrimedTableaux((3,2,1)) ).run() """ @@ -1377,7 +1370,7 @@ def _repr_(self): """ Return a string representation of ``self``. - TEST:: + TESTS:: sage: ShiftedPrimedTableaux((3,2,1)) Shifted Primed Tableaux of weight (3, 2, 1) @@ -1386,7 +1379,7 @@ def _repr_(self): def __contains__(self, T): """ - TEST:: + TESTS:: sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux((1,4,1)) True @@ -1410,7 +1403,7 @@ def _element_constructor_(self, T): - the corresponding primed tableau object - TEST:: + TESTS:: sage: tab= ShiftedPrimedTableaux((2,1))([1,1,1.5]); tab [(1.0, 1.0, 1.5)] @@ -1430,7 +1423,7 @@ def __iter__(self): """ Iterate over ``self``. - EXAMPLE:: + EXAMPLES:: sage: Tabs = ShiftedPrimedTableaux((2,3)) sage: Tabs[:4] @@ -1465,7 +1458,7 @@ def __init__(self, weight, shape): Initialize the class of Shifted Primed Tableaux of the given weight and shape. - TEST:: + TESTS:: sage: TestSuite( ShiftedPrimedTableaux((3,2,1)) ).run() """ @@ -1477,7 +1470,7 @@ def _repr_(self): """ Return a string representation of ``self``. - TEST:: + TESTS:: sage: ShiftedPrimedTableaux([3,2,1],(4,2)) Shifted Primed Tableaux of weight (4, 2) and shape [3, 2, 1] @@ -1488,7 +1481,7 @@ def _repr_(self): def __contains__(self, T): """ - TEST:: + TESTS:: sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux( ....: [4,2],(1,4,1)) @@ -1534,7 +1527,7 @@ def __iter__(self): """ Iterate over ``self``. - EXAMPLE:: + EXAMPLES:: sage: Tabs = ShiftedPrimedTableaux([3,2], (1,2,2)) sage: Tabs[:4] @@ -1653,7 +1646,6 @@ def __classcall_private__(cls, *args, **kwargs): sage: crystals.ShiftedPrimedTableaux([3,2,1]) Crystal of Shifted Primed Tableaux of type A_5 of shape (3, 2, 1) """ - shape = None n = None if 'shape' in kwargs: @@ -1684,7 +1676,6 @@ def __classcall_private__(cls, *args, **kwargs): class SPTCrystal(ShiftedPrimedTableauxCrystal): - """ A factory class generating a classical crystal of Shifted Primed Tableaux. @@ -1707,6 +1698,7 @@ def __init__(self, shape, n): ['A', 2] sage: len(SPTC.module_generators) 21 + sage: TestSuite(SPTC).run() """ if shape is None or n is None: raise ValueError('shape and n must be specified') @@ -1714,15 +1706,14 @@ def __init__(self, shape, n): self.n = n self._shape = shape self._cartan_type = CartanType(['A', n]) - self.module_generators = ShiftedPrimedTableaux( - shape=shape, - max_element=n+1).list_decreasing_weight() + T = ShiftedPrimedTableaux(shape=shape, max_element=n+1) + self.module_generators = tuple(T.list_decreasing_weight()) def _repr_(self): """ Return a string representation of ``self``. - TEST:: + TESTS:: sage: crystals.ShiftedPrimedTableaux([4,2], 2) Crystal of Shifted Primed Tableaux of type A_2 of shape (4, 2) @@ -1740,7 +1731,7 @@ def add_strip(sub_tab, full_tab, length): Helper function used in the algorithm to generate all Shifted Primed Tableaux of the fixed weight and shape. - TEST:: + TESTS:: sage: list(ShiftedPrimedTableaux([3,1],(2,2))) [[(1.0, 1.0, 2.0), (2.0,)], [(1.0, 1.0, 1.5), (2.0,)]] @@ -1797,3 +1788,4 @@ def add_strip(sub_tab, full_tab, length): k=len(plat_list), outer=plat_list): yield (list(primed_strip) + list(non_primed_strip)) + From 7c4e4ca1cc2e2d486eb389dc660fabfc8b3ad432 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Wed, 18 Oct 2017 10:43:58 +0200 Subject: [PATCH 409/740] Cleanup a leftover __eq__ --- src/sage/modules/free_module.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index b302cd974a1..900367ce80b 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -756,9 +756,6 @@ def __init__(self, base_ring, rank, degree, sparse=False, self.__degree = degree self.__is_sparse = sparse self._gram_matrix = None - - def __eq__(self,other): - return self is other def construction(self): """ From 64a4cce47d8d954148181c3d9baf0bfb67ef4a04 Mon Sep 17 00:00:00 2001 From: pmenegat Date: Wed, 18 Oct 2017 16:40:50 +0200 Subject: [PATCH 410/740] Corrected error related to solve_left method in is_submodule --- src/sage/modules/free_module.py | 51 +++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 624855bd2b5..2624789018b 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -181,7 +181,7 @@ from sage.structure.sequence import Sequence from sage.misc.cachefunc import cached_method - +from sage.misc.flatten import flatten from warnings import warn ############################################################################### @@ -1098,10 +1098,11 @@ def is_submodule(self, other): # Not all free modules have an ambient_vector_space. pass try: - M = other.basis_matrix().solve_left(self.basis_matrix()) + M=[list(other.basis_matrix().solve_left(self.basis_matrix()[i])) for i in range(self.rank())] except ValueError: return False - return all(x in S for x in M.list()) + return all(x in S for x in flatten(M)) + def __le__(self,other): r""" @@ -1132,7 +1133,7 @@ def __le__(self,other): True sage: C^3 <= Z^3 False - + Comparison with a sub-module:: sage: A = QQ^3 @@ -1153,7 +1154,27 @@ def __le__(self,other): sage: L2 <= L1 True sage: L1 <= L2 - False + False + + More exotical comparisons:: + + sage: R1=ZZ[sqrt(2)] + sage: F1=R1^3 + sage: V1=F1.span([[sqrt(2),sqrt(2),0]]) + sage: F2=ZZ^3 + sage: V2=F2.span([[2,2,0]]) + sage: V2<=V1 + True + sage: V1<=V2 + False + sage: R2=GF(5)[x] + sage: F3=R2^3 + sage: V3=F3.span([[x^5-1,1+x+x^2+x^3+x^4,0]]) + sage: W3=F3.span([[1,1,0],[0,4,0]]) + sage: V3<=W3 + True + sage: W3<=V3 + False """ return self.is_submodule(other) @@ -1213,15 +1234,15 @@ def __lt__(self,other): R = self.base_ring() S = other.base_ring() if R!=S: - return True + return True try: - M2 = self.basis_matrix().solve_left(other.basis_matrix()) + M2=[list(self.basis_matrix().solve_left(other.basis_matrix()[i])) for i in range(self.rank())] except ValueError: return True - return not all(y in R for y in M2.list()) - else: - return False - + return not all(y in R for y in flatten(M2)) + else: + return False + def __ge__(self,other): r""" Return ``True`` if ``self`` contain ``other``. @@ -1359,15 +1380,15 @@ def __eq__(self,other): if R != S: return False try: - M1 = other.basis_matrix().solve_left(self.basis_matrix()) + M1=[list(other.basis_matrix().solve_left(self.basis_matrix()[i])) for i in range(self.rank())] except ValueError: return False - if all(x in S for x in M1.list()): + if all(x in S for x in flatten(M1)): try: - M2 = self.basis_matrix().solve_left(other.basis_matrix()) + M2=[list(self.basis_matrix().solve_left(other.basis_matrix()[i])) for i in range(other.rank())] except ValueError: return False - return all(y in R for y in M2.list()) + return all(y in R for y in flatten(M2)) else: return False From 02a3458c6ddc64b225239f46b4c01ec7393d6527 Mon Sep 17 00:00:00 2001 From: pmenegat Date: Wed, 18 Oct 2017 20:01:15 +0200 Subject: [PATCH 411/740] added method `is_contained` and some bug corected --- src/sage/modules/free_module.py | 83 +++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 30 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 7ad1d96aa1d..6e0acfb9133 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -756,7 +756,7 @@ def __init__(self, base_ring, rank, degree, sparse=False, self.__degree = degree self.__is_sparse = sparse self._gram_matrix = None - + def construction(self): """ The construction functor and base ring for self. @@ -1098,19 +1098,46 @@ def is_submodule(self, other): # Not all free modules have an ambient_vector_space. pass try: - M=[list(other.basis_matrix().solve_left(self.basis_matrix()[i])) for i in range(self.rank())] + M=[list(other.basis_matrix().solve_left(self.basis_matrix()[i])) for i in range(self.basis_matrix().nrows())] except ValueError: return False return all(x in S for x in flatten(M)) + def is_contained(self,other): + r""" + Return ``True`` if ``self`` is contained in ``other``. + + See :meth:`is_submodule`. + + + EXAMPLES:: + sage: M = FreeModule(ZZ,3) + sage: V = M.ambient_vector_space() + sage: X = V.span([[1/2,1/2,0],[1/2,0,1/2]], ZZ) + sage: Y = V.span([[1,1,1]], ZZ) + sage: N = X + Y + sage: M.is_contained(X) + False + sage: M.is_contained(Y) + False + sage: Y.is_contained(M) + True + sage: N.is_contained(M) + False + sage: M.is_contained(N) + True + """ + return self.is_submodule(other) + + def __le__(self,other): r""" Return ``True`` if ``self`` is contained in ``other``. See :meth:`is_submodule`. - EXAMPLES: + EXAMPLES:: We compare rank three free modules over the integers and rationals:: @@ -1176,7 +1203,7 @@ def __le__(self,other): sage: W3<=V3 False """ - return self.is_submodule(other) + return self.is_contained(other) def __lt__(self,other): r""" @@ -1184,7 +1211,7 @@ def __lt__(self,other): See :meth:`is_submodule`. - EXAMPLES: + EXAMPLES:: We compare rank three free modules over the integers and rationals:: @@ -1236,7 +1263,7 @@ def __lt__(self,other): if R!=S: return True try: - M2=[list(self.basis_matrix().solve_left(other.basis_matrix()[i])) for i in range(self.rank())] + M2=[list(self.basis_matrix().solve_left(other.basis_matrix()[i])) for i in range(other.basis_matrix().nrows())] except ValueError: return True return not all(y in R for y in flatten(M2)) @@ -1249,7 +1276,7 @@ def __ge__(self,other): See :meth:`is_submodule`. - EXAMPLES: + EXAMPLES:: We compare rank three free modules over the integers and rationals:: @@ -1293,7 +1320,7 @@ def __gr__(self,other): See :meth:`is_submodule`. - EXAMPLES: + EXAMPLES:: We compare rank three free modules over the integers and rationals:: @@ -1337,7 +1364,7 @@ def __eq__(self,other): See :meth:`is_submodule`. - EXAMPLES: + EXAMPLES:: We compare rank three free modules over the integers and rationals:: @@ -1368,28 +1395,25 @@ def __eq__(self,other): False sage: L1 == L2 False - """ + """ + if not isinstance(other, FreeModule_generic): + return False if self is other: return True - if not isinstance(other, FreeModule_generic): - return False if other.rank() != self.rank(): return False - R = self.base_ring() - S = other.base_ring() - if R != S: - return False - try: - M1=[list(other.basis_matrix().solve_left(self.basis_matrix()[i])) for i in range(self.rank())] - except ValueError: - return False - if all(x in S for x in flatten(M1)): - try: - M2=[list(self.basis_matrix().solve_left(other.basis_matrix()[i])) for i in range(other.rank())] - except ValueError: - return False - return all(y in R for y in flatten(M2)) - else: + if self.is_contained(other): + R = self.base_ring() + S = other.base_ring() + if R!=S: + return False + else: + try: + M2=[list(self.basis_matrix().solve_left(other.basis_matrix()[i])) for i in range(other.basis_matrix().nrows())] + except ValueError: + return False + return all(y in R for y in flatten(M2)) + else: return False def __ne__(self,other): @@ -1398,7 +1422,7 @@ def __ne__(self,other): See :meth:`is_submodule`. - EXAMPLES: + EXAMPLES:: We compare rank three free modules over the integers and rationals:: @@ -4370,7 +4394,6 @@ def __quotient_matrices(self, sub): sage: A = GF(5)^2; B = A.span([[1,3]]); A / B Vector space quotient V/W of dimension 1 over Finite Field of size 5 where - V: Vector space of dimension 2 over Finite Field of size 5 W: Vector space of degree 2 and dimension 1 over Finite Field of size 5 Basis matrix: [1 3] @@ -4639,7 +4662,7 @@ def echelonized_basis_matrix(self): [ 0 0 1 -1] """ return self.basis_matrix() - + def _repr_(self): """ The printing representation of self. From 86ca57b716f3c67b9e74416d7a028db8e463cc25 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Wed, 18 Oct 2017 11:33:34 -0700 Subject: [PATCH 412/740] Skewed version --- src/sage/combinat/tableau_shifted_primed.py | 105 ++++++++++++-------- 1 file changed, 62 insertions(+), 43 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index c41baa0dc50..e974b396f85 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -52,7 +52,7 @@ class ShiftedPrimedTableau(ClonableArray): """ @staticmethod - def __classcall_private__(cls, T, skew=[]): + def __classcall_private__(cls, T, skew=None): r""" Ensure that a shifted tableau is only ever constructed as an ``element_class`` call of an appropriate parent. @@ -67,7 +67,7 @@ def __classcall_private__(cls, T, skew=[]): sage: S = ShiftedPrimedTableaux(shape=[4,2]) sage: t == S(data) True - sage: t = ShiftedPrimedTableau([["2p",2,3],["2p"],skew=[2,1]) + sage: t = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) sage: t.parent() Shifted Primed Tableaux skewed by [2, 1] """ @@ -77,7 +77,7 @@ def __classcall_private__(cls, T, skew=[]): return ShiftedPrimedTableaux(skew=skew)(T) - def __init__(self, parent, T, skew=[]): + def __init__(self, parent, T, skew=None): r""" Initialize a shifted tableau. @@ -214,11 +214,12 @@ def _to_matrix(self): """ array = [] m = self.shape()[0] - for i in range(len(self._skew)): + sk_len = 0 if self._skew is None else len(self._skew) + for i in range(sk_len): array.append([0]*(i+self._skew[i]) + list(self[i]) + [0]*(m-i-self._skew[i]-len(self[i]))) - for i in range(len(self._skew), len(self)): + for i in range(sk_len, len(self)): array.append([0]*i + list(self[i]) + [0]*(m-i-len(self[i]))) array = np.array(array, dtype='float') return array @@ -235,10 +236,13 @@ def check(self): sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) sage: s.check() """ - skew = self._skew + [0]*(len(self)-len(self._skew)) - if not all(self._skew[i] > self._skew[i+1] - for i in range(len(self._skew)-1)): - raise ValueError('skew shape must be a strict partition') + if self._skew is not None: + if not all(self._skew[i] > self._skew[i+1] + for i in range(len(self._skew)-1)): + raise ValueError('skew shape must be a strict partition') + skew = self._skew + [0]*(len(self)-len(self._skew)) + else: + skew = [0]*len(self) if not all(len(self[i]) + skew[i] > len(self[i+1]) + skew[i+1] for i in range(len(self)-1)): raise ValueError('shape must be a strict partition') @@ -280,7 +284,7 @@ def _repr_(self): sage: ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) [(1.5, 2.0, 3.0), (1.5,)] skewed by [2, 1] """ - if self._skew == []: + if self._skew is None: return repr([tuple(_) for _ in self]) return (repr([tuple(_) for _ in self]) + " skewed by {}".format(self._skew)) @@ -298,7 +302,10 @@ def _repr_tab(self): sage: s._repr_tab() [[' . ', ' . ', " 2'", ' 2 ', ' 3 '], [' . ', " 2'"]] """ - skew = self._skew + [0]*(len(self)-len(self._skew)) + if self._skew is not None: + skew = self._skew + [0]*(len(self)-len(self._skew)) + else: + skew = [0]*len(self) max_len = len(str(self.max_element()))+1 string_tab = [] for i, row in enumerate(self): @@ -495,6 +502,7 @@ def pp(self): 10 11' 11 11 11 12 sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + sage: s.pp() . . 2' 2 3 . 2' """ @@ -510,8 +518,8 @@ def _latex_(self): sage: latex(T([[1,"2p",2,"3p"],[2,3]])) {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} \raisebox{-.6ex}{$\begin{array}[b]{*{4}c}\cline{1-4} - \lr{1}&\lr{2'}&\lr{2}&\lr{3'}\\\cline{1-4} - &\lr{2}&\lr{3}\\\cline{2-3} + \lr{ 1 }&\lr{ 2'}&\lr{ 2 }&\lr{ 3'}\\\cline{1-4} + &\lr{ 2 }&\lr{ 3 }\\\cline{2-3} \end{array}$} } """ @@ -549,6 +557,8 @@ def shape(self): sage: s.shape() [5, 2] """ + if self._skew is None: + return ([len(_) for _ in self]) return ([len(self[i])+self._skew[i] for i in range(len(self._skew))] + [len(self[i]) @@ -588,7 +598,10 @@ def __call__(self, *cell): i, j = cell except ValueError: i, j = cell[0] - skew = self._skew[i] if i < len(self._skew) else 0 + if self._skew is not None: + skew = self._skew[i] if i < len(self._skew) else 0 + else: + skew = 0 try: return self[i][j-skew] except IndexError: @@ -641,9 +654,9 @@ def _reading_word_with_positions(self): ((0, 2), 2), ((0, 3), 2)] """ - if self._skew != []: + if self._skew is not None: raise NotImplementedError('skew tableau must be empty') - mat = self.to_matrix() + mat = self._to_matrix() list_with_positions = [] for (i, j), x in np.ndenumerate(mat[:, ::-1].T): if int(x) != x: @@ -675,9 +688,9 @@ def reading_word(self): sage: t.reading_word() [3, 2, 2, 1, 2, 2] """ - if self._skew != []: + if self._skew is not None: raise NotImplementedError('skew tableau must be empty') - return [tup[1] for tup in self.reading_word_with_positions()] + return [tup[1] for tup in self._reading_word_with_positions()] def f(self, ind): """ @@ -719,9 +732,10 @@ def f(self, ind): if self is None: return None - if self._skew != []: + if self._skew is not None: raise NotImplementedError('skew tableau must be empty') - T = self.to_matrix() + + T = self._to_matrix() read_word = self._reading_word_with_positions() read_word = [num @@ -820,9 +834,10 @@ def e(self, ind): if self is None: return None - if self._skew != []: + if self._skew is not None: raise NotImplementedError('skew tableau must be empty') - T = self.to_matrix() + + T = self._to_matrix() read_word = self._reading_word_with_positions() read_word = [num @@ -901,7 +916,7 @@ def is_highest_weight(self): True """ - if self._skew != []: + if self._skew is not None: raise NotImplementedError('skew tableau must be empty') read_w = self.reading_word() count = {} @@ -1020,9 +1035,9 @@ def __classcall_private__(cls, *args, **kwargs): weight = None shape = None max_element = None - skew = [] + skew = None - if 'skew' in kwargs: + if ('skew' in kwargs and kwargs['skew'] is not None): try: skew = Partition(kwargs['skew']) except ValueError: @@ -1080,7 +1095,9 @@ def __classcall_private__(cls, *args, **kwargs): if not all(shape[i] > shape[i+1] for i in range(len(shape)-1)): raise ValueError( "shape {} is not a strict partition".format(shape)) - if not all(skew[i] <= shape[i] for i in range(len(skew))): + if (skew is not None + and not all(skew[i] <= shape[i] + for i in range(len(skew)))): raise ValueError( 'skew shape must be inside the given tableau shape') @@ -1104,7 +1121,8 @@ def __classcall_private__(cls, *args, **kwargs): elif shape is None: return ShiftedPrimedTableaux_weight(weight, skew=skew) - if sum(shape) - sum(skew) != sum(weight): + if (skew is not None and sum(shape) - sum(skew) != sum(weight) + or skew is None and sum(shape) != sum(weight)): raise ValueError( "weight and shape are incompatible") @@ -1149,7 +1167,7 @@ class ShiftedPrimedTableaux_all(ShiftedPrimedTableaux): """ Element = ShiftedPrimedTableau - def __init__(self, skew=[]): + def __init__(self, skew=None): """ Initialize the class of all shifted tableaux. @@ -1176,7 +1194,7 @@ def _repr_(self): sage: ShiftedPrimedTableaux() Shifted Primed Tableaux """ - if self._skew == []: + if self._skew is None: return "Shifted Primed Tableaux" return "Shifted Primed Tableaux skewed by {}".format(self._skew) @@ -1241,7 +1259,7 @@ class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): """ Element = ShiftedPrimedTableau - def __init__(self, shape, max_elt, skew=[]): + def __init__(self, shape, max_elt, skew=None): """ Initialize the class of Shifted Primed Tableaux of a given shape. @@ -1266,16 +1284,16 @@ def _repr_(self): sage: ShiftedPrimedTableaux([3,2,1]) Shifted Primed Tableaux of shape [3, 2, 1] """ - if self._max_elt is None and self._skew == []: + if self._max_elt is None and self._skew is None: return "Shifted Primed Tableaux of shape {}".format(self._shape) - if self._max_elt is not None and self._skew == []: + if self._max_elt is not None and self._skew is None: return ( "Shifted Primed Tableaux of shape {} and maximum element {}" .format(self._shape, self._max_elt)) - if self._max_elt is None and self._skew != []: + if self._max_elt is None and self._skew is not None: return ("Shifted Primed Tableaux of shape {} skewed by {}" .format(self._shape, self._skew)) - if self._max_elt is not None and self._skew != []: + if self._max_elt is not None and self._skew is not None: return ( "Shifted Primed Tableaux of shape {} and maximum element {}" .format(self._shape, self._max_elt) + @@ -1375,7 +1393,7 @@ def __iter__(self): ... ValueError: set is infinite """ - if self._skew != []: + if self._skew is not None: raise NotImplementedError('skew tableau must be empty') if self._max_elt is None: raise ValueError("set is infinite") @@ -1396,7 +1414,7 @@ def list_decreasing_weight(self): sage: Tabs.list_decreasing_weight() [[(1.0, 1.0), (2.0,)], [(1.0, 2.0), (3.0,)], [(1.0, 1.5), (3.0,)]] """ - if self._skew != []: + if self._skew is not None: raise NotImplementedError('skew tableau must be empty') list_dw = [] if self._max_elt is None: @@ -1422,7 +1440,7 @@ def list_highest_weight(self): [(1.0, 1.0, 1.5), (2.0,)], [(1.0, 1.0, 2.5), (2.0,)]] """ - if self._skew != []: + if self._skew is not None: raise NotImplementedError('skew tableau must be empty') return [tab for tab in self.list_decreasing_weight() @@ -1442,7 +1460,7 @@ class ShiftedPrimedTableaux_weight(ShiftedPrimedTableaux): """ Element = ShiftedPrimedTableau - def __init__(self, weight, skew=[]): + def __init__(self, weight, skew=None): """ Initialize the class of Shifted Primed Tableaux of a given weight. @@ -1463,7 +1481,7 @@ def _repr_(self): sage: ShiftedPrimedTableaux((3,2,1)) Shifted Primed Tableaux of weight (3, 2, 1) """ - if self._skew == []: + if self._skew is None: return "Shifted Primed Tableaux of weight {}".format(self._weight) return ("Shifted Primed Tableaux of weight {} skewed by {}" .format(self._weight, self._skew)) @@ -1545,7 +1563,7 @@ class ShiftedPrimedTableaux_weight_shape(ShiftedPrimedTableaux): """ Element = ShiftedPrimedTableau - def __init__(self, weight, shape, skew=[]): + def __init__(self, weight, shape, skew=None): """ Initialize the class of Shifted Primed Tableaux of the given weight and shape. @@ -1568,7 +1586,7 @@ def _repr_(self): sage: ShiftedPrimedTableaux([3,2,1],(4,2)) Shifted Primed Tableaux of weight (4, 2) and shape [3, 2, 1] """ - if self._skew == []: + if self._skew is None: return ( "Shifted Primed Tableaux of weight {} and shape {}" .format(self._weight, self._shape)) @@ -1634,7 +1652,7 @@ def __iter__(self): sage: len(list(Tabs)) 4 """ - if self._skew != []: + if self._skew is not None: raise NotImplementedError('skew tableau must be empty') if not self._shape.dominates( @@ -1807,7 +1825,8 @@ def __init__(self, shape, n): self._cartan_type = CartanType(['A', n]) self.module_generators = ShiftedPrimedTableaux( shape=shape, - max_element=n+1).list_decreasing_weight() + max_element=n+1, + skew=None).list_decreasing_weight() def _repr_(self): """ From 0efbae46f953d923d9c18e30e47615b3ad4b2665 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 18 Oct 2017 16:27:30 -0500 Subject: [PATCH 413/740] Some cleanup and simplification of the class structure. --- src/doc/en/reference/references/index.rst | 4 + src/sage/combinat/crystals/catalog.py | 4 +- src/sage/combinat/tableau_shifted_primed.py | 291 +++++++------------- 3 files changed, 108 insertions(+), 191 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index ab4fa99e785..6cfebbbfe8a 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1154,6 +1154,10 @@ REFERENCES: Introduction to Mathematical Cryptography*. Springer, 2008. +.. [HPS2017] Graham Hawkes, Kirill Paramonov, and Anne Schilling. + *Crystal analysis of type* `C` *Stanley symmetric functions*. + Preprint, :arxiv:`1704.00889`. + .. [HOLM2016] Tristan Holmes and \J. \B. Nation, *Inflation of finite lattices along all-or-nothing sets*. http://www.math.hawaii.edu/~jb/inflation.pdf diff --git a/src/sage/combinat/crystals/catalog.py b/src/sage/combinat/crystals/catalog.py index 46887ec37b7..3f9b3a693c5 100644 --- a/src/sage/combinat/crystals/catalog.py +++ b/src/sage/combinat/crystals/catalog.py @@ -33,7 +33,7 @@ * :class:`RiggedConfigurations ` * :class:`ShiftedPrimedTableaux -` + ` * :class:`Spins ` * :class:`SpinsPlus ` * :class:`SpinsMinus ` @@ -77,7 +77,7 @@ from sage.combinat.rigged_configurations.tensor_product_kr_tableaux import TensorProductOfKirillovReshetikhinTableaux from sage.combinat.crystals.kirillov_reshetikhin import KirillovReshetikhinCrystal as KirillovReshetikhin from sage.combinat.rigged_configurations.rc_crystal import CrystalOfRiggedConfigurations as RiggedConfigurations -from sage.combinat.tableau_shifted_primed import ShiftedPrimedTableauxCrystal as ShiftedPrimedTableaux +from sage.combinat.tableau_shifted_primed import ShiftedPrimedTableaux_shape as ShiftedPrimedTableaux from sage.combinat.crystals.induced_structure import InducedCrystal as Induced diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index 0ec6a2526ee..7b9b31f9ad4 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -1,20 +1,40 @@ +# -*- coding: utf-8 -*- +""" +Shifted primed tableaux + +AUTHORS: + +- Kirill Paramonov (2017-08-18): initial implementation +""" + +#***************************************************************************** +# Copyright (C) 2017 Kirill Paramonov , +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from __future__ import print_function, absolute_import from six import add_metaclass -import numpy as np -from sage.combinat.partition import Partition, Partitions, OrderedPartitions +from sage.combinat.partition import Partition, Partitions, _Partitions, OrderedPartitions from sage.combinat.integer_vector import IntegerVectors from sage.rings.integer import Integer -from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass +from sage.misc.lazy_attribute import lazy_attribute from sage.structure.list_clone import ClonableArray from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation -# Imports for the crystal - +from sage.categories.regular_crystals import RegularCrystals from sage.categories.classical_crystals import ClassicalCrystals +from sage.categories.sets_cat import Sets +from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.combinat.root_system.cartan_type import CartanType @@ -121,6 +141,8 @@ def __init__(self, parent, T, skew=None): else: t_ = T + # FIXME: Remove numpy imports!!! + import numpy as np if not all(isinstance(row, (list, tuple, np.ndarray)) for row in t_): t_ = [t_] @@ -220,6 +242,7 @@ def _to_matrix(self): + [0]*(m-i-self._skew[i]-len(self[i]))) for i in range(sk_len, len(self)): array.append([0]*i + list(self[i]) + [0]*(m-i-len(self[i]))) + import numpy as np array = np.array(array, dtype='float') return array @@ -593,7 +616,6 @@ def __call__(self, *cell): sage: s(0,2) 1.5 """ - try: i, j = cell except ValueError: @@ -653,12 +675,12 @@ def _reading_word_with_positions(self): sage: t._reading_word_with_positions() [((1, 2), 3), ((0, 1), 2), ((1, 1), 2), ((0, 0), 1), ((0, 2), 2), ((0, 3), 2)] - """ if self._skew is not None: raise NotImplementedError('skew tableau must be empty') mat = self._to_matrix() list_with_positions = [] + import numpy as np for (i, j), x in np.ndenumerate(mat[:, ::-1].T): if int(x) != x: list_with_positions.append(((j, mat.shape[1]-i-1), int(x+0.5))) @@ -697,9 +719,9 @@ def reading_word(self): def f(self, ind): """ Compute the action of the crystal operator `f_i` on a Shifted Primed - Tableau using cases from the paper [GPS.17]. + Tableau using cases from the paper [HPS2017]_. - INPUT:: + INPUT: - ``ind`` -- index of the crystal operator `f_i` @@ -746,6 +768,8 @@ def f(self, ind): element_to_change = None count = 0 + import numpy as np + for element in read_word: if element[1] == ind+1: count += 1 @@ -765,10 +789,8 @@ def f(self, ind): h, l = T.shape if (c+1 == l or T[r, c+1] >= ind+1 or T[r, c+1] < 1): - (tp_r, tp_c) = (r, c) while True: - if (tp_r+1 == h or T[tp_r+1, tp_c] > ind+1 or T[tp_r+1, tp_c] < 1): @@ -799,7 +821,7 @@ def f(self, ind): if r > c: T = T.T - .5 - return(ShiftedPrimedTableau(T)) + return self.parent()(T) # FIXME: Generically this is not true def e(self, ind): """ @@ -846,6 +868,7 @@ def e(self, ind): element_to_change = None count = 0 + import numpy as np for element in read_word[::-1]: if element[1] == ind: @@ -893,7 +916,7 @@ def e(self, ind): if r > c: T = T.T - .5 - return(ShiftedPrimedTableau(T)) + return self.parent()(T) # FIXME: Generically this is not true def is_highest_weight(self): """ @@ -1248,16 +1271,44 @@ class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): """ Shifted Primed Tableaux of a fixed shape. - TESTS:: + Shifted prime tableaux admit a type `A_n` classical crystal structure + with highest weights corresponding to a given shape. + + The list of module generators consists of all elements of the + crystal with nonincreasing weight. + + Crystal is constructed following operations described in [HPS17]_. + + EXAMPLES:: sage: ShiftedPrimedTableaux([4,3,1], max_elt=4) Shifted Primed Tableaux of shape [4, 3, 1] and maximum element 4 sage: ShiftedPrimedTableaux([4,3,1], max_elt=4).cardinality() 384 + + We compute some of the crystal structure:: + + sage: SPTC = crystals.ShiftedPrimedTableaux([3,2], 3) + sage: T = SPTC.module_generators[-1] + sage: T + [(1.0, 1.0, 1.5), (2.0, 2.5)] + sage: T.f(2) + [(1.0, 1.0, 2.5), (2.0, 2.5)] + sage: len(SPTC.module_generators) + 7 + sage: SPTC[0] + [(1.0, 1.0, 1.0), (2.0, 2.0)] + sage: SPTC.cardinality() + 24 """ + @staticmethod + def __classcall_private__(cls, shape, max_elt=None, skew=None): + shape = _Partitions(shape) + return super(ShiftedPrimedTableaux_shape, cls).__classcall__(cls, shape, max_elt, skew) + Element = ShiftedPrimedTableau - def __init__(self, shape, max_elt, skew=None): + def __init__(self, shape, max_elt, skew): """ Initialize the class of Shifted Primed Tableaux of a given shape. @@ -1266,9 +1317,23 @@ def __init__(self, shape, max_elt, skew=None): TESTS:: - sage: TestSuite( ShiftedPrimedTableaux([4,2,1], max_elt=4)).run() + sage: TestSuite(ShiftedPrimedTableaux([4,2,1], max_elt=4)).run() """ - Parent.__init__(self, category=FiniteEnumeratedSets()) + # Determine the correct category + if max_elt is None: + if skew is None: + category = RegularCrystals().Infinite() + self._cartan_type = CartanType(['A+oo']) + else: + category = Sets().Infinite() + else: + if skew is None: + category = ClassicalCrystals() + self._cartan_type = CartanType(['A', max_elt-1]) + else: + category = Sets().Finite() + + ShiftedPrimedTableaux.__init__(self, category=category) self._max_elt = max_elt self._shape = shape self._skew = skew @@ -1282,20 +1347,12 @@ def _repr_(self): sage: ShiftedPrimedTableaux([3,2,1]) Shifted Primed Tableaux of shape [3, 2, 1] """ - if self._max_elt is None and self._skew is None: - return "Shifted Primed Tableaux of shape {}".format(self._shape) - if self._max_elt is not None and self._skew is None: - return ( - "Shifted Primed Tableaux of shape {} and maximum element {}" - .format(self._shape, self._max_elt)) - if self._max_elt is None and self._skew is not None: - return ("Shifted Primed Tableaux of shape {} skewed by {}" - .format(self._shape, self._skew)) - if self._max_elt is not None and self._skew is not None: - return ( - "Shifted Primed Tableaux of shape {} and maximum element {}" - .format(self._shape, self._max_elt) + - "skewed by {}".format(self._skew)) + base = "Shifted Primed Tableaux of shape {}".format(self._shape) + if self._max_elt is not None: + base += " and maximum element {}".format(self._max_elt) + if self._skew is not None: + base += " skewed by {}".format(self._shape, self._skew) + return base def __contains__(self, T): """ @@ -1338,9 +1395,8 @@ def _element_constructor_(self, T): Traceback (most recent call last): ... ValueError: [1, 1] is not an element of Shifted Primed Tableaux - of shape [3] + of shape [3] """ - try: Tab = self.element_class(self, T, skew=self._skew) except ValueError: @@ -1356,6 +1412,15 @@ def _element_constructor_(self, T): return Tab raise ValueError("{} is not an element of {}".format(T, self)) + @lazy_attribute + def module_generators(self): + """ + Return the generators of ``self`` as a crystal. + """ + if self._skew is not None: + raise NotImplementedError("only for non-skew shapes") + return tuple(self.list_decreasing_weight()) + def shape(self): """ Return the shape of the shifted tableaux ``self``. @@ -1399,7 +1464,7 @@ def __iter__(self): weight_n = tuple([w-1 for w in weight]) for tab in ShiftedPrimedTableaux(shape=self._shape, weight=weight_n): - yield (tab) + yield self(tab) def list_decreasing_weight(self): """ @@ -1418,10 +1483,9 @@ def list_decreasing_weight(self): max_element = sum(self._shape) else: max_element = self._max_elt - for weight in Partition(self._shape).dominated_partitions( - rows=max_element): - list_dw.extend(list(ShiftedPrimedTableaux(weight=tuple(weight), - shape=self._shape))) + for weight in Partition(self._shape).dominated_partitions(rows=max_element): + list_dw.extend([self(T) for T in ShiftedPrimedTableaux(weight=tuple(weight), + shape=self._shape)]) return list_dw def list_highest_weight(self): @@ -1683,157 +1747,6 @@ def __iter__(self): yield(ShiftedPrimedTableau(tab)) -########### -# Crystal # -########### - - -class ShiftedPrimedTableauxCrystal(UniqueRepresentation, Parent): - r""" - The class of crystals generated by Shifted Primed Tableaux of fixed shape. - - INPUT: - - -``n`` or ``rank`` -- a nonnegative integer - -``shape`` -- a strictly decreasing partition of length at most ``n`` plus - one - - This constructs a classical crystal of type `A_n` with highest weights - corresponding to a given shape. - - If ``n`` is not given, the rank of the crystal is assumed to be the size of - the partition ``shape`` minus one. - - The list of module generators consists of all elements of the crystal with - nonincreasing weight. - - Crystal is constructed following operations described in [HPS17]_. - - .. SEEALSO:: - - :class:`ShiftedPrimedTableaux` - :class:`ShiftedPrimedTableau` - - EXAMPLES:: - - sage: SPTC = crystals.ShiftedPrimedTableaux([3,2], 2) - sage: T = SPTC.module_generators[-1] - sage: T - [(1.0, 1.0, 1.5), (2.0, 2.5)] - sage: T.f(2) - [(1.0, 1.0, 2.5), (2.0, 2.5)] - sage: len(SPTC.module_generators) - 7 - sage: SPTC[0] - [(1.0, 1.0, 1.0), (2.0, 2.0)] - sage: SPTC.cardinality() - 24 - """ - @staticmethod - def __classcall_private__(cls, *args, **kwargs): - """ - Normalize the input. - - EXAMPLES:: - - sage: crystals.ShiftedPrimedTableaux(n=2, shape=[4,2]) - Crystal of Shifted Primed Tableaux of type A_2 of shape (4, 2) - sage: crystals.ShiftedPrimedTableaux([4,2], rank=2) - Crystal of Shifted Primed Tableaux of type A_2 of shape (4, 2) - sage: crystals.ShiftedPrimedTableaux([4,2]) - Crystal of Shifted Primed Tableaux of type A_5 of shape (4, 2) - - TESTS:: - sage: crystals.ShiftedPrimedTableaux() - Traceback (most recent call last): - ... - ValueError: shape argument must be specified - sage: crystals.ShiftedPrimedTableaux([4,4], 3) - Traceback (most recent call last): - ... - ValueError: shape [4, 4] is not a strict partition - sage: crystals.ShiftedPrimedTableaux([3,2,1], 1) - Traceback (most recent call last): - ... - ValueError: invalid crystal rank - sage: crystals.ShiftedPrimedTableaux([3,2,1]) - Crystal of Shifted Primed Tableaux of type A_5 of shape (3, 2, 1) - """ - shape = None - n = None - if 'shape' in kwargs: - shape = tuple(kwargs['shape']) - if 'rank' in kwargs: - n = int(kwargs['rank']) - if 'n' in kwargs: - n = int(kwargs['n']) - if args: - if isinstance(args[0], (list, tuple, Partition)): - shape = tuple(args[0]) - if len(args) > 1 and isinstance(args[1], (int, Integer)): - n = args[1] - else: - if isinstance(args[0], (int, Integer)): - n = args[0] - if (len(args) > 1 and - isinstance(args[1], (list, tuple, Partition))): - shape = tuple(args[1]) - if shape is None: - raise ValueError('shape argument must be specified') - if n is None: - n = sum(shape)-1 - - if n+1 < len(shape): - raise ValueError('invalid crystal rank') - return SPTCrystal(shape=shape, n=n) - - -class SPTCrystal(ShiftedPrimedTableauxCrystal): - """ - A factory class generating a classical crystal of Shifted Primed Tableaux. - - INPUT: - - -``n``-- a nonnegative integer - -``shape``-- a strictly decreasing partition of length at most ``n`` plus - one - """ - Element = ShiftedPrimedTableau - - def __init__(self, shape, n): - """ - Initialize the crystal of Shifted Primed Tableaux. - - TESTS:: - - sage: SPTC = crystals.ShiftedPrimedTableaux([4,2], 2) - sage: SPTC._cartan_type - ['A', 2] - sage: len(SPTC.module_generators) - 21 - sage: TestSuite(SPTC).run() - """ - if shape is None or n is None: - raise ValueError('shape and n must be specified') - Parent.__init__(self, category=ClassicalCrystals()) - self.n = n - self._shape = shape - self._cartan_type = CartanType(['A', n]) - T = ShiftedPrimedTableaux(shape=shape, max_element=n+1, skew=None) - self.module_generators = tuple(T.list_decreasing_weight()) - - def _repr_(self): - """ - Return a string representation of ``self``. - - TESTS:: - - sage: crystals.ShiftedPrimedTableaux([4,2], 2) - Crystal of Shifted Primed Tableaux of type A_2 of shape (4, 2) - """ - return ("Crystal of Shifted Primed Tableaux of type A_%s of shape " - % (self.n) + str(self._shape)) - #################### # Helper functions # #################### From c689609651f7e466461262f25f9da483b781ecb7 Mon Sep 17 00:00:00 2001 From: pmenegat Date: Thu, 19 Oct 2017 10:49:57 +0200 Subject: [PATCH 414/740] Doctest added and some correction relative quotient module --- src/sage/modules/free_module.py | 98 +++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 6 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 6e0acfb9133..792e24bd634 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -1081,8 +1081,13 @@ def is_submodule(self, other): return True if not isinstance(other, FreeModule_generic): return False + from sage.modules.quotient_module import FreeModule_ambient_field_quotient + if isinstance(other, FreeModule_ambient_field_quotient): + return False if other.rank() < self.rank(): return False + if other.degree() != self.degree(): + return False R = self.base_ring() S = other.base_ring() if R != S: @@ -1202,6 +1207,47 @@ def __le__(self,other): True sage: W3<=V3 False + + :: + + We compare a one dimensional space to a two dimensional + space: + + sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) + sage: M = span([[5,6,7]], QQ) + sage: M <= V + True + sage: V <= M + False + + We test that :trac:`5525` is fixed:: + + sage: A = (QQ^1).span([[1/3]],ZZ); B = (QQ^1).span([[1]],ZZ); + sage: A.intersection(B) + Free module of degree 1 and rank 1 over Integer Ring + Echelon basis matrix: + [1] + + We create the module `\ZZ^3`, and the submodule generated by + one vector `(1,1,0)`, and check whether certain elements are + in the submodule:: + + sage: R = FreeModule(ZZ, 3) + sage: V = R.submodule([R.gen(0) + R.gen(1)]) + sage: R.gen(0) + R.gen(1) in V + True + sage: R.gen(0) + 2*R.gen(1) in V + False + + sage: w = (1/2)*(R.gen(0) + R.gen(1)) + sage: w + (1/2, 1/2, 0) + sage: w.parent() + Vector space of dimension 3 over Rational Field + sage: w in V + False + sage: V.coordinates(w) + [1/2] """ return self.is_contained(other) @@ -1255,7 +1301,23 @@ def __lt__(self,other): sage: L2 < L1 True sage: L1 < L2 - False + False + + :: + + We compare a `\ZZ`-module to a one-dimensional + space. + + sage: V = span([[5,6,7]], ZZ).scale(1/11); V + Free module of degree 3 and rank 1 over Integer Ring + Echelon basis matrix: + [5/11 6/11 7/11] + sage: M = span([[5,6,7]], QQ) + sage: V < M + True + sage: M < V + False + """ if self <= other: R = self.base_ring() @@ -1374,10 +1436,27 @@ def __eq__(self,other): sage: QQ^3 == QQ^3 True + :: + + We compare two equal vector spaces. + + :: + + sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) + sage: W = span([[5,6,7], [8,9,10]], QQ) + sage: V == W + True + + Next we compare a one dimensional space to the two dimensional + space defined above. + :: + sage: M = span([[5,6,7]], QQ) + sage: V == M + False + Comparison with a sub-module:: - sage: A = QQ^3 sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) sage: V @@ -1394,14 +1473,21 @@ def __eq__(self,other): sage: L2 == L1 False sage: L1 == L2 - False - """ + False + + Comparison with a quotient module (see :trac:`10513`):: + + sage: M = QQ^3 / [[1,2,3]] + sage: V = QQ^2 + sage: V == M + False + sage: M == V + False + """ if not isinstance(other, FreeModule_generic): return False if self is other: return True - if other.rank() != self.rank(): - return False if self.is_contained(other): R = self.base_ring() S = other.base_ring() From 1b52cd873a2cb5f7d897036e786b80f16c01f7dd Mon Sep 17 00:00:00 2001 From: pmenegat Date: Thu, 19 Oct 2017 11:31:14 +0200 Subject: [PATCH 415/740] Added method `is_contained`, used for comparing, for free_quadratic_modules --- src/sage/modules/free_quadratic_module.py | 63 +++++++++++++++++++---- 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/src/sage/modules/free_quadratic_module.py b/src/sage/modules/free_quadratic_module.py index 6f5aa673b1e..c2a7c1855be 100644 --- a/src/sage/modules/free_quadratic_module.py +++ b/src/sage/modules/free_quadratic_module.py @@ -113,17 +113,7 @@ def FreeQuadraticModule( inner product matrix. EXAMPLES:: - - sage: M1 = FreeQuadraticModule(ZZ,2,inner_product_matrix=1) - sage: M1 is FreeModule(ZZ,2,inner_product_matrix=1) - True - sage: M1.inner_product_matrix() - [1 0] - [0 1] - sage: M1 == ZZ^2 - True - sage: M1 is ZZ^2 - False + sage: M2 = FreeQuadraticModule(ZZ,2,inner_product_matrix=[1,2,3,4]) sage: M2 is FreeQuadraticModule(ZZ,2,inner_product_matrix=[1,2,3,4]) True @@ -565,6 +555,57 @@ def _inner_product_is_diagonal(self): A = self.inner_product_matrix() D = sage.matrix.constructor.diagonal_matrix([ A[i,i] for i in range(A.nrows()) ]) return A == D + + def is_contained(self,other): + r""" + Return ``True`` if ``self`` is a submodule of ``other`` and the inner product is preserved. + + It overrides the method for the free modules, asking also for the same inner product matrix. + This method is used by comparison operators. + + EXAMPLES:: + + We compare free modules and free quadratic modules:: + + sage: M1 = FreeQuadraticModule(ZZ,2,inner_product_matrix=1) + sage: M1 is FreeModule(ZZ,2,inner_product_matrix=1) + True + sage: M1.inner_product_matrix() + [1 0] + [0 1] + sage: M1 == ZZ^2 + True + sage: M1 is ZZ^2 + False + + We compare free quadratic modules with a different inner product:: + + sage: V1 = FreeQuadraticModule(QQ,2,inner_product_matrix=1) + sage: V2 = FreeQuadraticModule(QQ,2,inner_product_matrix=[[-1,0],[0,1]]) + sage: V1.is_submodule(V2) + True + sage: V1.is_contained(V2) + False + sage: V1 == V2 + False + + We compare isomorphic free quadratic submodules embedded in a different way:: + + sage: F = FreeQuadraticModule(RR,3,inner_product_matrix=2) + sage: W1 = F.submodule([[0,1,1]]) + sage: W2 = F.span([[1,1,0]]) + sage: W1.gram_matrix() == W2.gram_matrix() + True + sage: W1 == W2 + False + """ + if not (isinstance(self, FreeQuadraticModule_generic) and isinstance(other, FreeQuadraticModule_generic)): + return self.is_submodule(other) + else: + if self.inner_product_matrix()==other.inner_product_matrix() and self.is_submodule(other): + return True + else: + return False class FreeQuadraticModule_generic_pid( free_module.FreeModule_generic_pid, FreeQuadraticModule_generic): From 17e22531a77e094b099892fcabb18f1dcb2a60fd Mon Sep 17 00:00:00 2001 From: Luca De Feo Date: Fri, 20 Oct 2017 00:19:45 +0200 Subject: [PATCH 416/740] Improved documentation of MPolynomial_libsingular.reduce() --- .../multi_polynomial_libsingular.pyx | 48 +++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index c606aa5191a..4ec4000e146 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -4421,19 +4421,36 @@ cdef class MPolynomial_libsingular(MPolynomial): def reduce(self,I): """ - Return the normal form of self w.r.t. ``I``, i.e. return the - remainder of this polynomial with respect to the polynomials - in ``I``. If the polynomial set/list ``I`` is not a (strong) - Groebner basis the result is not canonical. + Return a remainder of this polynomial modulo the + polynomials in ``I``. + + INPUT: - A strong Groebner basis ``G`` of ``I`` implies that for every - leading term ``t`` of ``I`` there exists an element ``g`` of ``G``, - such that the leading term of ``g`` divides ``t``. + - ``I`` - an ideal or a list/set/iterable of polynomials. - INPUT: + OUTPUT: + + A polynomial ``r`` such that: + + - ``self`` - ``r`` is in the ideal generated by ``I``. + + - No term in ``r`` is divisible by any of the leading monomials + of ``I``. + + The result ``r`` is canonical if: - - ``I`` - a list/set of polynomials. If ``I`` is an ideal, the - generators are used. + - ``I`` is an ideal, and Sage can compute a Groebner basis of it. + + - ``I`` is a list/set/iterable that is a (strong) Groebner basis + for the term order of ``self``. (A strong Groebner basis is + such that for every leading term ``t`` of the ideal generated + by ``I``, there exists an element ``g`` of ``I`` such that the + leading term of ``g`` divides ``t``.) + + The result ``r`` is implementation-dependent (and possibly + order-dependent) otherwise. If ``I`` is an ideal and no Groebner + basis can be computed, its list of generators ``I.gens()`` is + used for the reduction. EXAMPLES:: @@ -4464,6 +4481,17 @@ cdef class MPolynomial_libsingular(MPolynomial): sage: f = 3*x sage: f.reduce([2*x,y]) 3*x + + The reduction is not canonical when ``I`` is not a Groebner + basis:: + + sage: A. = QQ[] + sage: (x+y).reduce([x+y, x-y]) + 2*y + sage: (x+y).reduce([x-y, x+y]) + 0 + + """ cdef ideal *_I cdef MPolynomialRing_libsingular parent = self._parent From b59062520b9b8e99e8ee126cbf209feb241a596a Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Fri, 20 Oct 2017 03:15:34 +0200 Subject: [PATCH 417/740] A first version with richcmp_by_eq_and_lt ... doctests explode... --- src/sage/modules/free_module.py | 576 +++++----------------- src/sage/modules/free_quadratic_module.py | 56 +-- 2 files changed, 138 insertions(+), 494 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 792e24bd634..54d99a29d85 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -179,9 +179,11 @@ from sage.categories.principal_ideal_domains import PrincipalIdealDomains from sage.misc.randstate import current_randstate from sage.structure.sequence import Sequence +from sage.structure.richcmp import (richcmp_method,richcmp_by_eq_and_lt) + from sage.misc.cachefunc import cached_method -from sage.misc.flatten import flatten + from warnings import warn ############################################################################### @@ -641,6 +643,7 @@ def is_FreeModule(M): """ return isinstance(M, FreeModule_generic) +@richcmp_method class FreeModule_generic(Module): """ Base class for all free modules. @@ -1032,202 +1035,62 @@ def _element_constructor_(self, x, coerce=True, copy=True, check=True): raise TypeError("element {!r} is not in free module".format(x)) return self.element_class(self, x, coerce, copy) - def is_submodule(self, other): - """ - Return ``True`` if ``self`` is a submodule of ``other``. - - EXAMPLES:: - - sage: M = FreeModule(ZZ,3) - sage: V = M.ambient_vector_space() - sage: X = V.span([[1/2,1/2,0],[1/2,0,1/2]], ZZ) - sage: Y = V.span([[1,1,1]], ZZ) - sage: N = X + Y - sage: M.is_submodule(X) - False - sage: M.is_submodule(Y) - False - sage: Y.is_submodule(M) - True - sage: N.is_submodule(M) - False - sage: M.is_submodule(N) - True - - sage: M = FreeModule(ZZ,2) - sage: M.is_submodule(M) - True - sage: N = M.scale(2) - sage: N.is_submodule(M) - True - sage: M.is_submodule(N) - False - sage: N = M.scale(1/2) - sage: N.is_submodule(M) - False - sage: M.is_submodule(N) - True - - Since :meth:`basis` is not implemented in general, submodule - testing does not work for all PID's. However, trivial cases are - already used (and useful) for coercion, e.g. :: - - sage: QQ(1/2) * vector(ZZ['x']['y'],[1,2,3,4]) - (1/2, 1, 3/2, 2) - sage: vector(ZZ['x']['y'],[1,2,3,4]) * QQ(1/2) - (1/2, 1, 3/2, 2) - """ - if self is other: - return True - if not isinstance(other, FreeModule_generic): - return False - from sage.modules.quotient_module import FreeModule_ambient_field_quotient - if isinstance(other, FreeModule_ambient_field_quotient): - return False - if other.rank() < self.rank(): - return False - if other.degree() != self.degree(): - return False - R = self.base_ring() - S = other.base_ring() - if R != S: - try: - if not R.is_subring(S): - return False - except NotImplementedError: - return False - try: - if other is other.ambient_vector_space(): - return True - except AttributeError: - # Not all free modules have an ambient_vector_space. - pass - try: - M=[list(other.basis_matrix().solve_left(self.basis_matrix()[i])) for i in range(self.basis_matrix().nrows())] - except ValueError: - return False - return all(x in S for x in flatten(M)) - - def is_contained(self,other): - r""" - Return ``True`` if ``self`` is contained in ``other``. - - See :meth:`is_submodule`. - - - EXAMPLES:: - - sage: M = FreeModule(ZZ,3) - sage: V = M.ambient_vector_space() - sage: X = V.span([[1/2,1/2,0],[1/2,0,1/2]], ZZ) - sage: Y = V.span([[1,1,1]], ZZ) - sage: N = X + Y - sage: M.is_contained(X) - False - sage: M.is_contained(Y) - False - sage: Y.is_contained(M) - True - sage: N.is_contained(M) - False - sage: M.is_contained(N) - True - """ - return self.is_submodule(other) + __richcmp__ = richcmp_by_eq_and_lt("_eq_","_lt_") - - def __le__(self,other): + def _eq_(self,other): r""" - Return ``True`` if ``self`` is contained in ``other``. + Return if this free module is equal to other. - See :meth:`is_submodule`. + Ambient spaces are considered equal if they have the same + rank, basering and inner product matrix. + + Modules in the same ambient space are partially ordered by inclusion. - EXAMPLES:: + EXAMPLES: We compare rank three free modules over the integers and rationals:: - sage: QQ^3 <= CC^3 + sage: QQ^3 < CC^3 True - sage: CC^3 <= QQ^3 + sage: CC^3 < QQ^3 False - sage: QQ^3 <= QQ^3 + sage: CC^3 > QQ^3 True :: - sage: Q = QQ; Z = ZZ; C=CC - sage: Q^3 <= Z^3 + sage: Q = QQ; Z = ZZ + sage: Q^3 == Z^3 False - sage: Z^3 <= Q^3 - True - sage: Z^3 <= C^3 + sage: Q^3 == Q^3 True - sage: C^3 <= Z^3 + sage: Z^3 > Z^3 False - + Comparison with a sub-module:: - - sage: A = QQ^3 + sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) sage: V Vector space of degree 3 and dimension 2 over Rational Field Basis matrix: [ 1 0 -1] [ 0 1 2] - sage: V <= A - True - sage: A <= V - False - sage: L1 = span([[1,2,3], [5,6,7], [8,9,10]], ZZ) - sage: L2 = span([[2,4,6], [10,12,14], [16,18,20]], ZZ) - sage: 2*L1 <= L2 - True - sage: L2 <= L1 - True - sage: L1 <= L2 - False - - More exotical comparisons:: - - sage: R1=ZZ[sqrt(2)] - sage: F1=R1^3 - sage: V1=F1.span([[sqrt(2),sqrt(2),0]]) - sage: F2=ZZ^3 - sage: V2=F2.span([[2,2,0]]) - sage: V2<=V1 - True - sage: V1<=V2 - False - sage: R2=GF(5)[x] - sage: F3=R2^3 - sage: V3=F3.span([[x^5-1,1+x+x^2+x^3+x^4,0]]) - sage: W3=F3.span([[1,1,0],[0,4,0]]) - sage: V3<=W3 + sage: A = QQ^3 + sage: V < A True - sage: W3<=V3 + sage: A < V False - - :: - - We compare a one dimensional space to a two dimensional - space: - sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) - sage: M = span([[5,6,7]], QQ) - sage: M <= V - True - sage: V <= M - False + Comparison with a quotient module (see :trac:`10513`):: - We test that :trac:`5525` is fixed:: + sage: M = QQ^3 / [[1,2,3]] + sage: V = QQ^2 + sage: V == M + False + sage: M == V + False - sage: A = (QQ^1).span([[1/3]],ZZ); B = (QQ^1).span([[1]],ZZ); - sage: A.intersection(B) - Free module of degree 1 and rank 1 over Integer Ring - Echelon basis matrix: - [1] - We create the module `\ZZ^3`, and the submodule generated by one vector `(1,1,0)`, and check whether certain elements are in the submodule:: @@ -1247,301 +1110,127 @@ def __le__(self,other): sage: w in V False sage: V.coordinates(w) - [1/2] + [1/2] """ - return self.is_contained(other) - def __lt__(self,other): - r""" - Return ``True`` if ``self`` is costrictly ntained in ``other``. - - See :meth:`is_submodule`. - - EXAMPLES:: - - We compare rank three free modules over the integers and - rationals:: - - sage: QQ^3 < CC^3 - True - sage: CC^3 < QQ^3 - False - sage: QQ^3 < QQ^3 - False - - :: - - sage: Q = QQ; Z = ZZ; C=CC - sage: Q^3 < Z^3 - False - sage: Z^3 < Q^3 - True - sage: Z^3 < C^3 - True - sage: C^3 < Z^3 - False - - Comparison with a sub-module:: - - sage: A = QQ^3 - sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) - sage: V - Vector space of degree 3 and dimension 2 over Rational Field - Basis matrix: - [ 1 0 -1] - [ 0 1 2] - sage: V < A - True - sage: A < V - False - sage: L1 = span([[1,2,3], [5,6,7], [8,9,10]], ZZ) - sage: L2 = span([[2,4,6], [10,12,14], [16,18,20]], ZZ) - sage: 2*L1 < L2 - False - sage: L2 < L1 - True - sage: L1 < L2 - False - - :: + if self is other: + return True + if not isinstance(other, FreeModule_generic): + return NotImplemented + lx = self.rank() + rx = other.rank() + if lx != rx: + return False + lx = self.base_ring() + rx = other.base_ring() + if lx != rx: + return False + #We do not want to create an inner product matrix in memory if + #self and other use the dot product + if self._inner_product_is_dot_product() and other._inner_product_is_dot_product(): + pass + else: + #this only affects free_quadratic_modules + lx = self.inner_product_matrix() + rx = other.inner_product_matrix() + if lx != rx: + return False + from sage.modules.quotient_module import FreeModule_ambient_field_quotient + if isinstance(self, FreeModule_ambient_field_quotient): + return False + lx = isinstance(self, FreeModule_ambient) + rx = isinstance(other, FreeModule_ambient) + if lx and rx: + return True + #self and other are not ambient. + #but they are contained in the same ambient space - We compare a `\ZZ`-module to a one-dimensional - space. - - sage: V = span([[5,6,7]], ZZ).scale(1/11); V - Free module of degree 3 and rank 1 over Integer Ring - Echelon basis matrix: - [5/11 6/11 7/11] - sage: M = span([[5,6,7]], QQ) - sage: V < M - True - sage: M < V - False - + # We use self.echelonized_basis_matrix() == other.echelonized_basis_matrix() + # with the matrix to avoid a circular reference. + from sage.rings.integer_ring import IntegerRing + if self.base_ring().is_field() or self.base_ring() is IntegerRing: + #We know that the Hermite normal form is unique here. + lx = self.echelonized_basis_matrix() + rx = other.echelonized_basis_matrix() + return lx == rx + return self.is_submodule(other) and other.is_submodule(self) + + def _lt_(self,other): + return self.is_submodule(other) + + def is_submodule(self, other): """ - if self <= other: - R = self.base_ring() - S = other.base_ring() - if R!=S: - return True - try: - M2=[list(self.basis_matrix().solve_left(other.basis_matrix()[i])) for i in range(other.basis_matrix().nrows())] - except ValueError: - return True - return not all(y in R for y in flatten(M2)) - else: - return False - - def __ge__(self,other): - r""" - Return ``True`` if ``self`` contain ``other``. - - See :meth:`is_submodule`. + Return ``True`` if ``self`` is a submodule of ``other``. EXAMPLES:: - We compare rank three free modules over the integers and - rationals:: - - sage: QQ^3 >= CC^3 - False - sage: CC^3 >= QQ^3 - True - sage: QQ^3 >= QQ^3 - True - - :: - - Comparison with a sub-module:: - - sage: A = QQ^3 - sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) - sage: V - Vector space of degree 3 and dimension 2 over Rational Field - Basis matrix: - [ 1 0 -1] - [ 0 1 2] - sage: V >= A - False - sage: A >= V - True - sage: L1 = span([[1,2,3], [5,6,7], [8,9,10]], ZZ) - sage: L2 = span([[2,4,6], [10,12,14], [16,18,20]], ZZ) - sage: 2*L1 >= L2 - True - sage: L2 >= L1 + sage: M = FreeModule(ZZ,3) + sage: V = M.ambient_vector_space() + sage: X = V.span([[1/2,1/2,0],[1/2,0,1/2]], ZZ) + sage: Y = V.span([[1,1,1]], ZZ) + sage: N = X + Y + sage: M.is_submodule(X) False - sage: L1 >= L2 - True - """ - return other.is_submodule(self) - - def __gr__(self,other): - r""" - Return ``True`` if ``self`` strictly contain ``other``. - - See :meth:`is_submodule`. - - EXAMPLES:: - - We compare rank three free modules over the integers and - rationals:: - - sage: QQ^3 > CC^3 + sage: M.is_submodule(Y) False - sage: CC^3 > QQ^3 + sage: Y.is_submodule(M) True - sage: QQ^3 > QQ^3 - False - - :: - - Comparison with a sub-module:: - - sage: A = QQ^3 - sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) - sage: V - Vector space of degree 3 and dimension 2 over Rational Field - Basis matrix: - [ 1 0 -1] - [ 0 1 2] - sage: V > A + sage: N.is_submodule(M) False - sage: A > V + sage: M.is_submodule(N) True - sage: L1 = span([[1,2,3], [5,6,7], [8,9,10]], ZZ) - sage: L2 = span([[2,4,6], [10,12,14], [16,18,20]], ZZ) - sage: 2*L1 > L2 - False - sage: L2 > L1 - False - sage: L1 > L2 - True - """ - return other < self - - def __eq__(self,other): - r""" - Return ``True`` if ``self`` and ``other`` are equals. - - See :meth:`is_submodule`. - - EXAMPLES:: - We compare rank three free modules over the integers and - rationals:: - - sage: QQ^3 == CC^3 - False - sage: QQ^3 == QQ^3 + sage: M = FreeModule(ZZ,2) + sage: M.is_submodule(M) True - - :: - - We compare two equal vector spaces. - - :: - - sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) - sage: W = span([[5,6,7], [8,9,10]], QQ) - sage: V == W + sage: N = M.scale(2) + sage: N.is_submodule(M) True - - Next we compare a one dimensional space to the two dimensional - space defined above. - - :: - - sage: M = span([[5,6,7]], QQ) - sage: V == M + sage: M.is_submodule(N) False - - Comparison with a sub-module:: - sage: A = QQ^3 - sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) - sage: V - Vector space of degree 3 and dimension 2 over Rational Field - Basis matrix: - [ 1 0 -1] - [ 0 1 2] - sage: V == A + sage: N = M.scale(1/2) + sage: N.is_submodule(M) False - sage: L1 = span([[1,2,3], [5,6,7], [8,9,10]], ZZ) - sage: L2 = span([[2,4,6], [10,12,14], [16,18,20]], ZZ) - sage: 2*L1 == L2 + sage: M.is_submodule(N) True - sage: L2 == L1 - False - sage: L1 == L2 - False - - Comparison with a quotient module (see :trac:`10513`):: - sage: M = QQ^3 / [[1,2,3]] - sage: V = QQ^2 - sage: V == M - False - sage: M == V - False + Since :meth:`basis` is not implemented in general, submodule + testing does not work for all PID's. However, trivial cases are + already used (and useful) for coercion, e.g. :: + + sage: QQ(1/2) * vector(ZZ['x']['y'],[1,2,3,4]) + (1/2, 1, 3/2, 2) + sage: vector(ZZ['x']['y'],[1,2,3,4]) * QQ(1/2) + (1/2, 1, 3/2, 2) """ - if not isinstance(other, FreeModule_generic): - return False if self is other: return True - if self.is_contained(other): - R = self.base_ring() - S = other.base_ring() - if R!=S: - return False - else: - try: - M2=[list(self.basis_matrix().solve_left(other.basis_matrix()[i])) for i in range(other.basis_matrix().nrows())] - except ValueError: + if not isinstance(other, FreeModule_generic): + return False + if other.rank() < self.rank(): + return False + if other.degree() != self.degree(): + return False + if self._inner_product_is_dot_product() and other._inner_product_is_dot_product(): + pass + else: + if self.inner_product_matrix() != other.inner_product_matrix(): + return False + R = self.base_ring() + S = other.base_ring() + if R != S: + try: + if not R.is_subring(S): return False - return all(y in R for y in flatten(M2)) - else: + except NotImplementedError: + return False + try: + M=[list(other.basis_matrix().solve_left(self.basis_matrix()[i])) for i in range(self.basis_matrix().nrows())] + except ValueError: return False - - def __ne__(self,other): - r""" - Return ``True`` if ``self`` and ``other`` are not equals. - - See :meth:`is_submodule`. - - EXAMPLES:: - - We compare rank three free modules over the integers and - rationals:: - - sage: QQ^3 != CC^3 - True - sage: QQ^3 != QQ^3 - False - - :: + from sage.misc.flatten import flatten + return all(x in S for x in flatten(M)) - Comparison with a sub-module:: - - sage: A = QQ^3 - sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) - sage: V - Vector space of degree 3 and dimension 2 over Rational Field - Basis matrix: - [ 1 0 -1] - [ 0 1 2] - sage: V != A - True - sage: L1 = span([[1,2,3], [5,6,7], [8,9,10]], ZZ) - sage: L2 = span([[2,4,6], [10,12,14], [16,18,20]], ZZ) - sage: 2*L1 != L2 - False - sage: L2 != L1 - True - sage: L1 != L2 - True - """ - return not (self == other) - def __iter__(self): """ Return iterator over the elements of this free module. @@ -4480,6 +4169,7 @@ def __quotient_matrices(self, sub): sage: A = GF(5)^2; B = A.span([[1,3]]); A / B Vector space quotient V/W of dimension 1 over Finite Field of size 5 where + V: Vector space of dimension 2 over Finite Field of size 5 W: Vector space of degree 2 and dimension 1 over Finite Field of size 5 Basis matrix: [1 3] @@ -4720,7 +4410,7 @@ def _sparse_module(self): True """ return FreeModule(base_ring=self.base_ring(), rank = self.rank(), sparse=True) - + def echelonized_basis_matrix(self): """ The echelonized basis matrix of self. @@ -4748,7 +4438,9 @@ def echelonized_basis_matrix(self): [ 0 0 1 -1] """ return self.basis_matrix() - + + + def _repr_(self): """ The printing representation of self. @@ -5669,7 +5361,7 @@ def __hash__(self): True """ return hash(self.__basis) - + def construction(self): """ Returns the functorial construction of self, namely, the subspace @@ -5749,6 +5441,8 @@ def _echelonized_basis(self, ambient, basis): self.__echelonized_basis_matrix = E return E.rows() + + def _denominator(self, B): """ The LCM of the denominators of the given list B. diff --git a/src/sage/modules/free_quadratic_module.py b/src/sage/modules/free_quadratic_module.py index c2a7c1855be..937fd0bfb65 100644 --- a/src/sage/modules/free_quadratic_module.py +++ b/src/sage/modules/free_quadratic_module.py @@ -555,57 +555,6 @@ def _inner_product_is_diagonal(self): A = self.inner_product_matrix() D = sage.matrix.constructor.diagonal_matrix([ A[i,i] for i in range(A.nrows()) ]) return A == D - - def is_contained(self,other): - r""" - Return ``True`` if ``self`` is a submodule of ``other`` and the inner product is preserved. - - It overrides the method for the free modules, asking also for the same inner product matrix. - This method is used by comparison operators. - - EXAMPLES:: - - We compare free modules and free quadratic modules:: - - sage: M1 = FreeQuadraticModule(ZZ,2,inner_product_matrix=1) - sage: M1 is FreeModule(ZZ,2,inner_product_matrix=1) - True - sage: M1.inner_product_matrix() - [1 0] - [0 1] - sage: M1 == ZZ^2 - True - sage: M1 is ZZ^2 - False - - We compare free quadratic modules with a different inner product:: - - sage: V1 = FreeQuadraticModule(QQ,2,inner_product_matrix=1) - sage: V2 = FreeQuadraticModule(QQ,2,inner_product_matrix=[[-1,0],[0,1]]) - sage: V1.is_submodule(V2) - True - sage: V1.is_contained(V2) - False - sage: V1 == V2 - False - - We compare isomorphic free quadratic submodules embedded in a different way:: - - sage: F = FreeQuadraticModule(RR,3,inner_product_matrix=2) - sage: W1 = F.submodule([[0,1,1]]) - sage: W2 = F.span([[1,1,0]]) - sage: W1.gram_matrix() == W2.gram_matrix() - True - sage: W1 == W2 - False - """ - if not (isinstance(self, FreeQuadraticModule_generic) and isinstance(other, FreeQuadraticModule_generic)): - return self.is_submodule(other) - else: - if self.inner_product_matrix()==other.inner_product_matrix() and self.is_submodule(other): - return True - else: - return False class FreeQuadraticModule_generic_pid( free_module.FreeModule_generic_pid, FreeQuadraticModule_generic): @@ -1239,8 +1188,9 @@ class FreeQuadraticModule_submodule_with_basis_pid( False We compare a `\ZZ`-module to the one-dimensional space above:: - - sage: V = span([[5,6,7]], ZZ).scale(1/11); + + sage: V = A.span([[5,6,7]]) + sage: V = V.change_ring(ZZ).scale(1/11); sage: V < M True sage: M < V From c30ab64d1d9fc288a26e56c305e3236131b7797a Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Fri, 20 Oct 2017 03:29:47 +0200 Subject: [PATCH 418/740] Replaced richcmp_by_eq_and_lt by __eq__ __lt__ __neq__ __geq__ etc. doctests pass now. --- src/sage/modules/free_module.py | 307 +++++++++++++++++++++++++++- src/sage/modules/quotient_module.py | 24 --- 2 files changed, 301 insertions(+), 30 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 54d99a29d85..3fd8889e930 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -643,7 +643,6 @@ def is_FreeModule(M): """ return isinstance(M, FreeModule_generic) -@richcmp_method class FreeModule_generic(Module): """ Base class for all free modules. @@ -1034,10 +1033,8 @@ def _element_constructor_(self, x, coerce=True, copy=True, check=True): except ArithmeticError: raise TypeError("element {!r} is not in free module".format(x)) return self.element_class(self, x, coerce, copy) - - __richcmp__ = richcmp_by_eq_and_lt("_eq_","_lt_") - def _eq_(self,other): + def __eq__(self,other): r""" Return if this free module is equal to other. @@ -1136,7 +1133,7 @@ def _eq_(self,other): if lx != rx: return False from sage.modules.quotient_module import FreeModule_ambient_field_quotient - if isinstance(self, FreeModule_ambient_field_quotient): + if isinstance(other, FreeModule_ambient_field_quotient): return False lx = isinstance(self, FreeModule_ambient) rx = isinstance(other, FreeModule_ambient) @@ -1155,9 +1152,307 @@ def _eq_(self,other): return lx == rx return self.is_submodule(other) and other.is_submodule(self) - def _lt_(self,other): + def __le__(self,other): + r""" + Return ``True`` if ``self`` is contained in ``other``. + + See :meth:`is_submodule`. + + EXAMPLES:: + + We compare rank three free modules over the integers and + rationals:: + + sage: QQ^3 <= CC^3 + True + sage: CC^3 <= QQ^3 + False + sage: QQ^3 <= QQ^3 + True + + :: + + sage: Q = QQ; Z = ZZ; C=CC + sage: Q^3 <= Z^3 + False + sage: Z^3 <= Q^3 + True + sage: Z^3 <= C^3 + True + sage: C^3 <= Z^3 + False + + Comparison with a sub-module:: + + sage: A = QQ^3 + sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) + sage: V + Vector space of degree 3 and dimension 2 over Rational Field + Basis matrix: + [ 1 0 -1] + [ 0 1 2] + sage: V <= A + True + sage: A <= V + False + sage: L1 = span([[1,2,3], [5,6,7], [8,9,10]], ZZ) + sage: L2 = span([[2,4,6], [10,12,14], [16,18,20]], ZZ) + sage: 2*L1 <= L2 + True + sage: L2 <= L1 + True + sage: L1 <= L2 + False + + More exotical comparisons:: + + sage: R1=ZZ[sqrt(2)] + sage: F1=R1^3 + sage: V1=F1.span([[sqrt(2),sqrt(2),0]]) + sage: F2=ZZ^3 + sage: V2=F2.span([[2,2,0]]) + sage: V2<=V1 + True + sage: V1<=V2 + False + sage: R2=GF(5)[x] + sage: F3=R2^3 + sage: V3=F3.span([[x^5-1,1+x+x^2+x^3+x^4,0]]) + sage: W3=F3.span([[1,1,0],[0,4,0]]) + sage: V3<=W3 + True + sage: W3<=V3 + False + + :: + + We compare a one dimensional space to a two dimensional + space: + + sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) + sage: M = span([[5,6,7]], QQ) + sage: M <= V + True + sage: V <= M + False + + We test that :trac:`5525` is fixed:: + + sage: A = (QQ^1).span([[1/3]],ZZ); B = (QQ^1).span([[1]],ZZ); + sage: A.intersection(B) + Free module of degree 1 and rank 1 over Integer Ring + Echelon basis matrix: + [1] + + We create the module `\ZZ^3`, and the submodule generated by + one vector `(1,1,0)`, and check whether certain elements are + in the submodule:: + + sage: R = FreeModule(ZZ, 3) + sage: V = R.submodule([R.gen(0) + R.gen(1)]) + sage: R.gen(0) + R.gen(1) in V + True + sage: R.gen(0) + 2*R.gen(1) in V + False + + sage: w = (1/2)*(R.gen(0) + R.gen(1)) + sage: w + (1/2, 1/2, 0) + sage: w.parent() + Vector space of dimension 3 over Rational Field + sage: w in V + False + sage: V.coordinates(w) + [1/2] + """ return self.is_submodule(other) + def __lt__(self,other): + r""" + Return ``True`` if ``self`` is strictly contained in ``other``. + + See :meth:`is_submodule`. + + EXAMPLES:: + + We compare rank three free modules over the integers and + rationals:: + + sage: QQ^3 < CC^3 + True + sage: CC^3 < QQ^3 + False + sage: QQ^3 < QQ^3 + False + + :: + + sage: Q = QQ; Z = ZZ; C=CC + sage: Q^3 < Z^3 + False + sage: Z^3 < Q^3 + True + sage: Z^3 < C^3 + True + sage: C^3 < Z^3 + False + + Comparison with a sub-module:: + + sage: A = QQ^3 + sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) + sage: V + Vector space of degree 3 and dimension 2 over Rational Field + Basis matrix: + [ 1 0 -1] + [ 0 1 2] + sage: V < A + True + sage: A < V + False + sage: L1 = span([[1,2,3], [5,6,7], [8,9,10]], ZZ) + sage: L2 = span([[2,4,6], [10,12,14], [16,18,20]], ZZ) + sage: 2*L1 < L2 + False + sage: L2 < L1 + True + sage: L1 < L2 + False + + :: + + We compare a `\ZZ`-module to a one-dimensional + space. + + sage: V = span([[5,6,7]], ZZ).scale(1/11); V + Free module of degree 3 and rank 1 over Integer Ring + Echelon basis matrix: + [5/11 6/11 7/11] + sage: M = span([[5,6,7]], QQ) + sage: V < M + True + sage: M < V + False + + """ + return (not self == other) and self.is_submodule(other) + + def __neq__(self,other): + return not self == other + + def __ne__(self, other): + """ + Check not-equality of ``self`` and ``other``. + + EXAMPLES: + + We create three quotient spaces and compare them:: + + sage: A = QQ^2 + sage: V = A.span_of_basis([[1,0], [1,1]]) + sage: W0 = V.span([V.1, V.0]) + sage: W1 = V.span([V.1]) + sage: W2 = V.span([V.1]) + sage: Q0 = V/W0 + sage: Q1 = V/W1 + sage: Q2 = V/W2 + + sage: Q0 != Q1 + True + sage: Q1 != Q2 + False + """ + return not (self == other) + + def __ge__(self,other): + r""" + Return ``True`` if ``self`` contain ``other``. + + See :meth:`is_submodule`. + + EXAMPLES:: + + We compare rank three free modules over the integers and + rationals:: + + sage: QQ^3 >= CC^3 + False + sage: CC^3 >= QQ^3 + True + sage: QQ^3 >= QQ^3 + True + + :: + + Comparison with a sub-module:: + + sage: A = QQ^3 + sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) + sage: V + Vector space of degree 3 and dimension 2 over Rational Field + Basis matrix: + [ 1 0 -1] + [ 0 1 2] + sage: V >= A + False + sage: A >= V + True + sage: L1 = span([[1,2,3], [5,6,7], [8,9,10]], ZZ) + sage: L2 = span([[2,4,6], [10,12,14], [16,18,20]], ZZ) + sage: 2*L1 >= L2 + True + sage: L2 >= L1 + False + sage: L1 >= L2 + True + """ + return other.is_submodule(self) + + def __gr__(self,other): + r""" + Return ``True`` if ``self`` strictly contain ``other``. + + See :meth:`is_submodule`. + + EXAMPLES:: + + We compare rank three free modules over the integers and + rationals:: + + sage: QQ^3 > CC^3 + False + sage: CC^3 > QQ^3 + True + sage: QQ^3 > QQ^3 + False + + :: + + Comparison with a sub-module:: + + sage: A = QQ^3 + sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) + sage: V + Vector space of degree 3 and dimension 2 over Rational Field + Basis matrix: + [ 1 0 -1] + [ 0 1 2] + sage: V > A + False + sage: A > V + True + sage: L1 = span([[1,2,3], [5,6,7], [8,9,10]], ZZ) + sage: L2 = span([[2,4,6], [10,12,14], [16,18,20]], ZZ) + sage: 2*L1 > L2 + False + sage: L2 > L1 + False + sage: L1 > L2 + True + """ + return other < self + + def is_submodule(self, other): """ Return ``True`` if ``self`` is a submodule of ``other``. diff --git a/src/sage/modules/quotient_module.py b/src/sage/modules/quotient_module.py index 1611eabddb8..aa3a3d08935 100644 --- a/src/sage/modules/quotient_module.py +++ b/src/sage/modules/quotient_module.py @@ -213,30 +213,6 @@ def __eq__(self, other): return False return (self.V(), self.W()) == (other.V(), other.W()) - def __ne__(self, other): - """ - Check not-equality of ``self`` and ``other``. - - EXAMPLES: - - We create three quotient spaces and compare them:: - - sage: A = QQ^2 - sage: V = A.span_of_basis([[1,0], [1,1]]) - sage: W0 = V.span([V.1, V.0]) - sage: W1 = V.span([V.1]) - sage: W2 = V.span([V.1]) - sage: Q0 = V/W0 - sage: Q1 = V/W1 - sage: Q2 = V/W2 - - sage: Q0 != Q1 - True - sage: Q1 != Q2 - False - """ - return not (self == other) - def _element_constructor_(self, x): """ Convert an element into this quotient space `V/W` if there is From d357e794bc35bacde5a009ae7bee788bfced3f2e Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Fri, 20 Oct 2017 03:39:23 +0200 Subject: [PATCH 419/740] Removed unnecessary imports --- src/sage/modules/free_module.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 3fd8889e930..bce90fb499a 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -179,7 +179,6 @@ from sage.categories.principal_ideal_domains import PrincipalIdealDomains from sage.misc.randstate import current_randstate from sage.structure.sequence import Sequence -from sage.structure.richcmp import (richcmp_method,richcmp_by_eq_and_lt) from sage.misc.cachefunc import cached_method From 4407c8a0670abf12af0622dbc31d348b35ee9b0c Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Tue, 24 Oct 2017 09:14:21 +0200 Subject: [PATCH 420/740] Fixed comparison with FreeQuotient modules --- src/sage/modules/free_module.py | 35 ++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 4f1b0ff1cc0..7e2b4a90cd1 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -1133,7 +1133,15 @@ def __eq__(self,other): return False from sage.modules.quotient_module import FreeModule_ambient_field_quotient if isinstance(other, FreeModule_ambient_field_quotient): - return False + #if the relations agree we continue with the covers. + if isinstance(self, FreeModule_ambient_field_quotient): + if other.relations()!=self.relations(): + return False + self = self.cover() + else: + if other.relations()!= 0: + return False + other = other.cover() lx = isinstance(self, FreeModule_ambient) rx = isinstance(other, FreeModule_ambient) if lx and rx: @@ -1496,11 +1504,36 @@ def is_submodule(self, other): (1/2, 1, 3/2, 2) sage: vector(ZZ['x']['y'],[1,2,3,4]) * QQ(1/2) (1/2, 1, 3/2, 2) + + TESTS:: + + M = QQ^3 / [[1,2,3]] + V = QQ^2 + V.is_submodule(M) + False + + sage: M1 = QQ^3 / [[1,2,3]] + sage: V1 = span(QQ,[(1,0,0)])+ M1.relations() + sage: M2 = V1 / M1.relations() + sage: M2.is_submodule(M1) + True """ if self is other: return True if not isinstance(other, FreeModule_generic): return False + from sage.modules.quotient_module import FreeModule_ambient_field_quotient + if isinstance(other, FreeModule_ambient_field_quotient): + #if the relations agree we continue with the covers. + if isinstance(self, FreeModule_ambient_field_quotient): + if other.relations()!=self.relations(): + return False + self = self.cover() + else: + if other.relations()!= 0: + return False + other = other.cover() + if other.rank() < self.rank(): return False if other.degree() != self.degree(): From 97e2324619344ab6f4b2034c3aba003953f64aab Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Wed, 25 Oct 2017 11:57:39 +0200 Subject: [PATCH 421/740] Removed __eq__ from quotient_module.py. The comparison is all done in free_modules use __richcmp__ and @richcmp_method --- src/sage/modules/free_module.py | 355 ++++++++++++---------------- src/sage/modules/quotient_module.py | 47 ++-- 2 files changed, 173 insertions(+), 229 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 7e2b4a90cd1..12849036a37 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -179,8 +179,7 @@ from sage.categories.principal_ideal_domains import PrincipalIdealDomains from sage.misc.randstate import current_randstate from sage.structure.sequence import Sequence - - +from sage.structure.richcmp import richcmp_method,rich_to_bool,op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE from sage.misc.cachefunc import cached_method from warnings import warn @@ -642,6 +641,7 @@ def is_FreeModule(M): """ return isinstance(M, FreeModule_generic) +@richcmp_method class FreeModule_generic(Module): """ Base class for all free modules. @@ -1033,140 +1033,14 @@ def _element_constructor_(self, x, coerce=True, copy=True, check=True): raise TypeError("element {!r} is not in free module".format(x)) return self.element_class(self, x, coerce, copy) - def __eq__(self,other): - r""" - Return if this free module is equal to other. - - Ambient spaces are considered equal if they have the same - rank, basering and inner product matrix. - - Modules in the same ambient space are partially ordered by inclusion. - - EXAMPLES: - - We compare rank three free modules over the integers and - rationals:: - - sage: QQ^3 < CC^3 - True - sage: CC^3 < QQ^3 - False - sage: CC^3 > QQ^3 - True - - :: - - sage: Q = QQ; Z = ZZ - sage: Q^3 == Z^3 - False - sage: Q^3 == Q^3 - True - sage: Z^3 > Z^3 - False - - Comparison with a sub-module:: - - sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) - sage: V - Vector space of degree 3 and dimension 2 over Rational Field - Basis matrix: - [ 1 0 -1] - [ 0 1 2] - sage: A = QQ^3 - sage: V < A - True - sage: A < V - False - - Comparison with a quotient module (see :trac:`10513`):: - - sage: M = QQ^3 / [[1,2,3]] - sage: V = QQ^2 - sage: V == M - False - sage: M == V - False - - We create the module `\ZZ^3`, and the submodule generated by - one vector `(1,1,0)`, and check whether certain elements are - in the submodule:: - - sage: R = FreeModule(ZZ, 3) - sage: V = R.submodule([R.gen(0) + R.gen(1)]) - sage: R.gen(0) + R.gen(1) in V - True - sage: R.gen(0) + 2*R.gen(1) in V - False - - sage: w = (1/2)*(R.gen(0) + R.gen(1)) - sage: w - (1/2, 1/2, 0) - sage: w.parent() - Vector space of dimension 3 over Rational Field - sage: w in V - False - sage: V.coordinates(w) - [1/2] + def __richcmp__(self,other,op): """ + Rich comparison via containment in the same ambient space. - if self is other: - return True - if not isinstance(other, FreeModule_generic): - return NotImplemented - lx = self.rank() - rx = other.rank() - if lx != rx: - return False - lx = self.base_ring() - rx = other.base_ring() - if lx != rx: - return False - #We do not want to create an inner product matrix in memory if - #self and other use the dot product - if self._inner_product_is_dot_product() and other._inner_product_is_dot_product(): - pass - else: - #this only affects free_quadratic_modules - lx = self.inner_product_matrix() - rx = other.inner_product_matrix() - if lx != rx: - return False - from sage.modules.quotient_module import FreeModule_ambient_field_quotient - if isinstance(other, FreeModule_ambient_field_quotient): - #if the relations agree we continue with the covers. - if isinstance(self, FreeModule_ambient_field_quotient): - if other.relations()!=self.relations(): - return False - self = self.cover() - else: - if other.relations()!= 0: - return False - other = other.cover() - lx = isinstance(self, FreeModule_ambient) - rx = isinstance(other, FreeModule_ambient) - if lx and rx: - return True - #self and other are not ambient. - #but they are contained in the same ambient space + Two modules compare if their ambient module/space is equal. + Ambient spaces are equal if they have the same + base ring, rank and inner product matrix. - # We use self.echelonized_basis_matrix() == other.echelonized_basis_matrix() - # with the matrix to avoid a circular reference. - from sage.rings.integer_ring import IntegerRing - if self.base_ring().is_field() or self.base_ring() is IntegerRing: - #We know that the Hermite normal form is unique here. - lx = self.echelonized_basis_matrix() - rx = other.echelonized_basis_matrix() - return lx == rx - return self.is_submodule(other) and other.is_submodule(self) - - def __le__(self,other): - r""" - Return ``True`` if ``self`` is contained in ``other``. - - See :meth:`is_submodule`. - - EXAMPLES:: - We compare rank three free modules over the integers and rationals:: @@ -1211,7 +1085,7 @@ def __le__(self,other): sage: L1 <= L2 False - More exotical comparisons:: + More exotic comparisons:: sage: R1=ZZ[sqrt(2)] sage: F1=R1^3 @@ -1271,19 +1145,11 @@ def __le__(self,other): False sage: V.coordinates(w) [1/2] - """ - return self.is_submodule(other) - - def __lt__(self,other): - r""" - Return ``True`` if ``self`` is strictly contained in ``other``. - - See :meth:`is_submodule`. - - EXAMPLES:: - - We compare rank three free modules over the integers and - rationals:: + + TESTS:: + + We compare rank three free modules over the integers and + rationals:: sage: QQ^3 < CC^3 True @@ -1341,44 +1207,6 @@ def __lt__(self,other): sage: M < V False - """ - return (not self == other) and self.is_submodule(other) - - def __neq__(self,other): - return not self == other - - def __ne__(self, other): - """ - Check not-equality of ``self`` and ``other``. - - EXAMPLES: - - We create three quotient spaces and compare them:: - - sage: A = QQ^2 - sage: V = A.span_of_basis([[1,0], [1,1]]) - sage: W0 = V.span([V.1, V.0]) - sage: W1 = V.span([V.1]) - sage: W2 = V.span([V.1]) - sage: Q0 = V/W0 - sage: Q1 = V/W1 - sage: Q2 = V/W2 - - sage: Q0 != Q1 - True - sage: Q1 != Q2 - False - """ - return not (self == other) - - def __ge__(self,other): - r""" - Return ``True`` if ``self`` contain ``other``. - - See :meth:`is_submodule`. - - EXAMPLES:: - We compare rank three free modules over the integers and rationals:: @@ -1411,18 +1239,8 @@ def __ge__(self,other): sage: L2 >= L1 False sage: L1 >= L2 - True - """ - return other.is_submodule(self) - - def __gr__(self,other): - r""" - Return ``True`` if ``self`` strictly contain ``other``. - - See :meth:`is_submodule`. - - EXAMPLES:: - + True + We compare rank three free modules over the integers and rationals:: @@ -1457,8 +1275,149 @@ def __gr__(self,other): sage: L1 > L2 True """ - return other < self + if self is other: + return rich_to_bool(op, 0) + if not isinstance(other, FreeModule_generic): + return NotImplemented + + # Check equality first if needed + if op == op_EQ: + return self._eq(other) + if op == op_NE: + return not self._eq(other) + if op == op_LE: + return self.is_submodule(other) + if op == op_GE: + return other.is_submodule(self) + if op == op_LT: + return (not self._eq(other)) and self.is_submodule(other) + if op == op_GT: + return (not self._eq(other)) and other.is_submodule(self) + def _eq(self,other): + r""" + Return if this free module is equal to other. + + Ambient spaces are considered equal if they have the same + rank, basering and inner product matrix. + + Modules in the same ambient space are partially ordered by inclusion. + + EXAMPLES: + + We compare rank three free modules over the integers and + rationals:: + + sage: QQ^3 < CC^3 + True + sage: CC^3 < QQ^3 + False + sage: CC^3 > QQ^3 + True + + :: + + sage: Q = QQ; Z = ZZ + sage: Q^3 == Z^3 + False + sage: Q^3 == Q^3 + True + sage: Z^3 > Z^3 + False + + Comparison with a sub-module:: + + sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) + sage: V + Vector space of degree 3 and dimension 2 over Rational Field + Basis matrix: + [ 1 0 -1] + [ 0 1 2] + sage: A = QQ^3 + sage: V < A + True + sage: A < V + False + + Comparison with a quotient module (see :trac:`10513`):: + + sage: M = QQ^3 / [[1,2,3]] + sage: V = QQ^2 + sage: V == M + False + sage: M == V + False + + We create the module `\ZZ^3`, and the submodule generated by + one vector `(1,1,0)`, and check whether certain elements are + in the submodule:: + + sage: R = FreeModule(ZZ, 3) + sage: V = R.submodule([R.gen(0) + R.gen(1)]) + sage: R.gen(0) + R.gen(1) in V + True + sage: R.gen(0) + 2*R.gen(1) in V + False + + sage: w = (1/2)*(R.gen(0) + R.gen(1)) + sage: w + (1/2, 1/2, 0) + sage: w.parent() + Vector space of dimension 3 over Rational Field + sage: w in V + False + sage: V.coordinates(w) + [1/2] + """ + lx = self.rank() + rx = other.rank() + if lx != rx: + return False + lx = self.base_ring() + rx = other.base_ring() + if lx != rx: + return False + #We do not want to create an inner product matrix in memory if + #self and other use the dot product + if not (self._inner_product_is_dot_product() and other._inner_product_is_dot_product()): + #this only affects free_quadratic_modules + lx = self.inner_product_matrix() + rx = other.inner_product_matrix() + if lx != rx: + return False + from sage.modules.quotient_module import FreeModule_ambient_field_quotient + lq = isinstance(self, FreeModule_ambient_field_quotient) + rq = isinstance(other, FreeModule_ambient_field_quotient) + if lq or rq: + #if the relations agree we continue with the covers. + if lq: + lx = self.relations() + self = self.cover() + else: + lx = self.zero_submodule() + if rq: + rx = other.relations() + other = other.cover() + else: + rx = other.zero_submodule() + if lx != rx: + return False + lx = isinstance(self, FreeModule_ambient) + rx = isinstance(other, FreeModule_ambient) + if lx and rx: + return True + #self and other are not ambient. + #but they are contained in the same ambient space + + # We use self.echelonized_basis_matrix() == other.echelonized_basis_matrix() + # with the matrix to avoid a circular reference. + from sage.rings.integer_ring import IntegerRing + if self.base_ring().is_field() or self.base_ring() is IntegerRing: + #We know that the Hermite normal form is unique here. + lx = self.echelonized_basis_matrix() + rx = other.echelonized_basis_matrix() + return lx == rx + return self.is_submodule(other) and other.is_submodule(self) def is_submodule(self, other): """ diff --git a/src/sage/modules/quotient_module.py b/src/sage/modules/quotient_module.py index aa3a3d08935..470a59ca4d5 100644 --- a/src/sage/modules/quotient_module.py +++ b/src/sage/modules/quotient_module.py @@ -69,6 +69,22 @@ class FreeModule_ambient_field_quotient(FreeModule_ambient_field): sage: Z = V.quotient(W) sage: Z == U True + + We create three quotient spaces and compare them:: + + sage: A = QQ^2 + sage: V = A.span_of_basis([[1,0], [1,1]]) + sage: W0 = V.span([V.1, V.0]) + sage: W1 = V.span([V.1]) + sage: W2 = V.span([V.1]) + sage: Q0 = V/W0 + sage: Q1 = V/W1 + sage: Q2 = V/W2 + + sage: Q0 == Q1 + False + sage: Q1 == Q2 + True TESTS:: @@ -182,37 +198,6 @@ def __hash__(self): """ return self.__hash - def __eq__(self, other): - """ - Compare ``self`` and ``other``. - - If ``other`` is not a quotient of vector spaces, return ``False``. - - If it is, return comparison of the pair `(V, W)` so that ``self`` is - `V/W` for each of ``self`` and ``other``. - - EXAMPLES: - - We create three quotient spaces and compare them:: - - sage: A = QQ^2 - sage: V = A.span_of_basis([[1,0], [1,1]]) - sage: W0 = V.span([V.1, V.0]) - sage: W1 = V.span([V.1]) - sage: W2 = V.span([V.1]) - sage: Q0 = V/W0 - sage: Q1 = V/W1 - sage: Q2 = V/W2 - - sage: Q0 == Q1 - False - sage: Q1 == Q2 - True - """ - if not isinstance(other, FreeModule_ambient_field_quotient): - return False - return (self.V(), self.W()) == (other.V(), other.W()) - def _element_constructor_(self, x): """ Convert an element into this quotient space `V/W` if there is From 04d5347d4650e3d90d81fb45af724ad6f79dafdf Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 1 Nov 2017 07:57:02 -0500 Subject: [PATCH 422/740] Allow baseline to be passed as part of the API of *_art. --- src/sage/typeset/ascii_art.py | 33 +++- src/sage/typeset/character_art_factory.py | 182 ++++++++++++++++------ src/sage/typeset/unicode_art.py | 18 ++- 3 files changed, 174 insertions(+), 59 deletions(-) diff --git a/src/sage/typeset/ascii_art.py b/src/sage/typeset/ascii_art.py index 51a6e896166..031e15c7172 100644 --- a/src/sage/typeset/ascii_art.py +++ b/src/sage/typeset/ascii_art.py @@ -202,11 +202,15 @@ def ascii_art(*obj, **kwds): - ``*obj`` -- any number of positional arguments, of arbitrary type. The objects whose ascii art representation we want. - - ``sep`` -- optional ``'sep=...'`` keyword argument. Anything - that can be converted to ascii art (default: empty ascii + - ``sep`` -- optional ``'sep=...'`` keyword argument (or ``'separator'``). + Anything that can be converted to ascii art (default: empty ascii art). The separator in-between a list of objects. Only used if more than one object given. + - ``baseline`` -- (default: 0) the baseline for the object + + - ``sep_baseline`` -- (default: 0) the baseline for the separator + OUTPUT: :class:`AsciiArt` instance. @@ -224,12 +228,28 @@ def ascii_art(*obj, **kwds): | / + We can specify a separator object:: + sage: ident = lambda n: identity_matrix(ZZ, n) sage: ascii_art(ident(1), ident(2), ident(3), sep=' : ') [1 0 0] [1 0] [0 1 0] [1] : [0 1] : [0 0 1] + We can specify the baseline:: + + sage: ascii_art(ident(2), baseline=-1) + ascii_art(ident(3)) + [1 0][1 0 0] + [0 1][0 1 0] + [0 0 1] + + We can determine the baseline of the separator:: + + sage: ascii_art(ident(1), ident(2), ident(3), sep=' -- ', sep_baseline=-1) + [1 0 0] + -- [1 0] -- [0 1 0] + [1] [0 1] [0 0 1] + TESTS:: sage: n = var('n') @@ -246,12 +266,13 @@ def ascii_art(*obj, **kwds): sage: ascii_art(1) 1 """ - separator = kwds.pop('sep', empty_ascii_art) + separator, baseline, sep_base = _ascii_art_factory.parse_keywords(kwds) if kwds: raise ValueError('unknown keyword arguments: {0}'.format(list(kwds))) if len(obj) == 1: - return _ascii_art_factory.build(obj[0]) + return _ascii_art_factory.build(obj[0], baseline=baseline) if not isinstance(separator, AsciiArt): - separator = _ascii_art_factory.build(separator) + separator = _ascii_art_factory.build(separator, baseline=sep_base) obj = map(_ascii_art_factory.build, obj) - return _ascii_art_factory.concatenate(obj, separator, empty_ascii_art) + return _ascii_art_factory.concatenate(obj, separator, empty_ascii_art, baseline=baseline) + diff --git a/src/sage/typeset/character_art_factory.py b/src/sage/typeset/character_art_factory.py index e70288f0382..066e4c9258a 100644 --- a/src/sage/typeset/character_art_factory.py +++ b/src/sage/typeset/character_art_factory.py @@ -69,14 +69,15 @@ def __init__(self, self.left_square_bracket, self.right_square_bracket = square_bracet self.left_curly_brace, self.right_curly_brace = curly_brace - def build(self, obj): + def build(self, obj, baseline=None): r""" - Construct a character art representation + Construct a character art representation. INPUT: - - ``obj`` -- anything. The object whose ascii art representation - we want. + - ``obj`` -- anything; the object whose ascii art representation + we want + - ``baseline`` -- (optional) the baseline of the object OUTPUT: @@ -112,20 +113,24 @@ def build(self, obj): 1 """ if isinstance(obj, self.art_type): + if baseline is not None: + obj._baseline = baseline return obj - elif isinstance(obj, (tuple, list, dict, set)): + if baseline is None: + baseline = 0 + if isinstance(obj, (tuple, list, dict, set)): if obj.__class__ is tuple: - return self.build_tuple(obj) + return self.build_tuple(obj, baseline) elif obj.__class__ is dict: - return self.build_dict(obj) + return self.build_dict(obj, baseline) elif obj.__class__ is list: - return self.build_list(obj) + return self.build_list(obj, baseline) else: - return self.build_set(obj) + return self.build_set(obj, baseline) elif isinstance(obj, SageObject): - return self.build_from_magic_method(obj) + return self.build_from_magic_method(obj, baseline) else: - return self.build_from_string(obj) + return self.build_from_string(obj, baseline) def build_empty(self): """ @@ -134,10 +139,12 @@ def build_empty(self): OUTPUT: Character art instance. + + EXAMPLES:: """ return self.art_type.empty() - def build_from_magic_method(self, obj): + def build_from_magic_method(self, obj, baseline=0): """ Return the character art object created by the object's magic method @@ -155,15 +162,19 @@ def build_from_magic_method(self, obj): """ magic_method = getattr(obj, self.magic_method_name) - return magic_method() + ret = magic_method() + ret._baseline = baseline + return ret - def build_from_string(self, obj): + def build_from_string(self, obj, baseline=0): r""" - Return the character art object created from splitting the object's string representation + Return the character art object created from splitting + the object's string representation. INPUT: - - ``obj`` -- utf-8 encoded byte string or unicode. + - ``obj`` -- utf-8 encoded byte string or unicode + - ``baseline`` -- (default: 0) the baseline of the object OUTPUT: @@ -182,38 +193,50 @@ def build_from_string(self, obj): TESTS:: + sage: from sage.typeset.ascii_art import _ascii_art_factory as factory sage: factory.build_from_string(u'a\nbb\nccc') # same with unicode a bb ccc + + :: + + sage: a = factory.build_from_string('a\nbb\nccc', baseline=2) + sage: a + ascii_art('<-') + a <- + bb + ccc """ if self.string_type is text_type and not isinstance(obj, text_type): obj = binary_type(obj).decode('utf-8') if self.string_type is binary_type and not isinstance(obj, binary_type): obj = text_type(obj).encode('utf-8') - return self.art_type(obj.splitlines()) + return self.art_type(obj.splitlines(), baseline=baseline) - def build_container(self, content, left_border, right_border): + def build_container(self, content, left_border, right_border, baseline=0): r""" - Return character art for a container + Return character art for a container. INPUT: - ``content`` -- - :class:`~sage.typeset.character_art.CharacterArt`. The - content of the container, usually comma-separated entries. + :class:`~sage.typeset.character_art.CharacterArt`; the + content of the container, usually comma-separated entries - ``left_border`` -- - :class:`~sage.typeset.symbols.CompoundSymbol`. The left - border of the container. + :class:`~sage.typeset.symbols.CompoundSymbol`; the left + border of the container - ``right_border`` -- - :class:`~sage.typeset.symbols.CompoundSymbol`. The right - border of the container. + :class:`~sage.typeset.symbols.CompoundSymbol`; the right + border of the container + + - ``baseline`` -- (default: 0) the baseline of the object TESTS:: - sage: l = ascii_art(list(DyckWords(3))); l + sage: l = ascii_art(list(DyckWords(3))) # indirect doctest + sage: l [ /\ ] [ /\ /\ /\/\ / \ ] [ /\/\/\, /\/ \, / \/\, / \, / \ ] @@ -230,15 +253,15 @@ def build_container(self, content, left_border, right_border): lines.append(left + pad + line.ljust(w) + pad + right) shift = len(left_border) + len(pad) basepoints = [bp + shift for bp in content.get_breakpoints()] - return self.art_type(lines, basepoints, baseline=0) + return self.art_type(lines, basepoints, baseline=baseline) - def build_set(self, s): + def build_set(self, s, baseline=0): r""" Return an character art output of a set. TESTS:: - sage: ascii_art(set(DyckWords(3))) + sage: ascii_art(set(DyckWords(3))) # indirect doctest { /\ } { /\ /\/\ /\ / \ } { / \/\, / \, /\/\/\, /\/ \, / \ } @@ -246,15 +269,16 @@ def build_set(self, s): comma = self.art_type([self.string_type(', ')], baseline=0) repr_elems = self.concatenate(s, comma) return self.build_container( - repr_elems, self.left_curly_brace, self.right_curly_brace) + repr_elems, self.left_curly_brace, self.right_curly_brace, + baseline) - def build_dict(self, d): + def build_dict(self, d, baseline=0): r""" Return an character art output of a dictionary. TESTS:: - sage: d = ascii_art({i:dw for i,dw in enumerate(DyckWords(3))}) + sage: d = ascii_art({i:dw for i,dw in enumerate(DyckWords(3))}) # indirect doctest sage: d { /\ } { /\ /\ /\/\ / \ } @@ -276,16 +300,18 @@ def concat_no_breakpoint(k,v): repr_elems = self.concatenate( (concat_no_breakpoint(k, v) for k, v in iteritems(d)), comma) - return self.build_container(repr_elems, - self.left_curly_brace, self.right_curly_brace) + return self.build_container( + repr_elems, self.left_curly_brace, self.right_curly_brace, + baseline) - def build_list(self, l): + def build_list(self, l, baseline=0): r""" Return an character art output of a list. TESTS:: - sage: l = ascii_art(list(DyckWords(3))); l + sage: l = ascii_art(list(DyckWords(3))) # indirect doctest + sage: l [ /\ ] [ /\ /\ /\/\ / \ ] [ /\/\/\, /\/ \, / \/\, / \, / \ ] @@ -303,15 +329,16 @@ def build_list(self, l): breakpoints=[1]) repr_elems = self.concatenate(l, comma) return self.build_container( - repr_elems, self.left_square_bracket, self.right_square_bracket) + repr_elems, self.left_square_bracket, self.right_square_bracket, + baseline) - def build_tuple(self, t): + def build_tuple(self, t, baseline=0): r""" Return an character art output of a tuple. TESTS:: - sage: ascii_art(tuple(DyckWords(3))) + sage: ascii_art(tuple(DyckWords(3))) # indirect doctest ( /\ ) ( /\ /\ /\/\ / \ ) ( /\/\/\, /\/ \, / \/\, / \, / \ ) @@ -321,26 +348,29 @@ def build_tuple(self, t): breakpoints=[1]) repr_elems = self.concatenate(t, comma) return self.build_container( - repr_elems, self.left_parenthesis, self.right_parenthesis) + repr_elems, self.left_parenthesis, self.right_parenthesis, + baseline) - def concatenate(self, iterable, separator, empty=None): + def concatenate(self, iterable, separator, empty=None, baseline=0): """ Concatenate multiple character art instances - The breakpoints are set as the breakpoints of the ``separator`` together - with the breakpoints of the objects in ``iterable``. If there is - ``None``, the end of the separator is used. + The breakpoints are set as the breakpoints of the ``separator`` + together with the breakpoints of the objects in ``iterable``. + If there is ``None``, the end of the separator is used. INPUT: - - ``iterable`` -- iterable of character art. + - ``iterable`` -- iterable of character art - - ``separable`` -- character art. The separator in-between the - iterable. + - ``separable`` -- character art; the separator in-between the + iterable - ``empty`` -- an optional character art which is returned if ``iterable`` is empty + - ``baseline`` -- (default: 0) the baseline of the object + EXAMPLES:: sage: i2 = identity_matrix(2) @@ -367,4 +397,62 @@ def concatenate(self, iterable, separator, empty=None): result += self.build(obj) breakpoints.extend([l+x for x in obj.get_breakpoints()]) result._breakpoints = breakpoints + result._baseline = baseline return result + + def parse_keywords(self, kwds): + """ + Parse the keyword input given by the dict ``kwds``. + + INPUT: + + - ``kwds`` -- a dict + + OUTPUT: + + A triple: + + - the separator + - the baseline + - the baseline of the separator + + .. WARNING:: + + The input is a dict, not a list of keyword arguments. + + .. NOTE:: + + This will remove ``sep``/``separator`` and ``baseline`` + from ``kwds`` if they are specified. + + TESTS:: + + sage: from sage.typeset.ascii_art import _ascii_art_factory as factory + sage: d = {'sep': '2', 'baseline': 5} + ('2', 5, 0) + sage: factory.parse_keywords(d) + sage: d + {} + + :: + + sage: d = {'foo': '2', 'baseline': 5} + sage: factory.parse_keywords(d) + (, 5, 0) + sage: d + {'foo': '2'} + + sage: d = {'sep': '2', 'separator': '2'} + sage: factory.parse_keywords(d) + Traceback (most recent call last): + ... + ValueError: cannot specify both 'sep' and 'separator' + """ + empty = self.build_empty() + sep = kwds.pop("sep", empty) + if sep == empty: + sep = kwds.pop("separator", empty) + elif "separator" in kwds: + raise ValueError("cannot specify both 'sep' and 'separator'") + return sep, kwds.pop("baseline", 0), kwds.pop("sep_baseline", 0) + diff --git a/src/sage/typeset/unicode_art.py b/src/sage/typeset/unicode_art.py index 8b463db7db9..dbdd0289900 100644 --- a/src/sage/typeset/unicode_art.py +++ b/src/sage/typeset/unicode_art.py @@ -74,11 +74,15 @@ def unicode_art(*obj, **kwds): - ``*obj`` -- any number of positional arguments, of arbitrary type. The objects whose ascii art representation we want. - - ``sep`` -- optional ``'sep=...'`` keyword argument. Anything - that can be converted to unicode art (default: empty unicode + - ``sep`` -- optional ``'sep=...'`` keyword argument (or ``'separator'``). + Anything that can be converted to unicode art (default: empty unicode art). The separator in-between a list of objects. Only used if more than one object given. + - ``baseline`` -- (default: 0) the baseline for the object + + - ``sep_baseline`` -- (default: 0) the baseline for the separator + OUTPUT: :class:`UnicodeArt` instance. @@ -114,12 +118,14 @@ def unicode_art(*obj, **kwds): sage: unicode_art(1) 1 """ - separator = kwds.pop('sep', empty_unicode_art) + separator, baseline, sep_baseline = _unicode_art_factory.parse_keywords(kwds) if kwds: raise ValueError('unknown keyword arguments: {0}'.format(list(kwds))) if len(obj) == 1: - return _unicode_art_factory.build(obj[0]) + return _unicode_art_factory.build(obj[0], baseline=baseline) if not isinstance(separator, UnicodeArt): - separator = _unicode_art_factory.build(separator) + separator = _unicode_art_factory.build(separator, baseline=sep_baseline) obj = map(_unicode_art_factory.build, obj) - return _unicode_art_factory.concatenate(obj, separator, empty_unicode_art) + return _unicode_art_factory.concatenate(obj, separator, empty_unicode_art, + baseline=baseline) + From b4756f5a04be2b56735972078238283b1372c322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 1 Nov 2017 23:07:14 +0700 Subject: [PATCH 423/740] Make mac_lane_approximants output more stable --- src/sage/rings/valuation/limit_valuation.py | 2 -- src/sage/rings/valuation/valuation.py | 37 +++++++++++++-------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/sage/rings/valuation/limit_valuation.py b/src/sage/rings/valuation/limit_valuation.py index f425d85714d..888824910cb 100644 --- a/src/sage/rings/valuation/limit_valuation.py +++ b/src/sage/rings/valuation/limit_valuation.py @@ -471,7 +471,6 @@ def _call_(self, f): sage: vK = K.valuation(2) sage: f = (x^2 + 7) * (x^2 + 9) sage: V = vK.mac_lane_approximants(f, require_incomparability=True) - sage: V = sorted(V, key=str) sage: w = valuations.LimitValuation(V[0], f) sage: w((x^2 + 7) * (x + 3)) @@ -681,7 +680,6 @@ def _ge_(self, other): sage: F = (x^2 + 7) * (x^2 + 9) sage: G = (x^2 + 7) sage: V = QQ.valuation(2).mac_lane_approximants(F, require_incomparability=True) - sage: V = sorted(V, key=str) sage: valuations.LimitValuation(V[0], F) >= valuations.LimitValuation(V[1], F) False sage: valuations.LimitValuation(V[1], F) >= valuations.LimitValuation(V[1], G) diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 86f3bf6e43b..7f9a1c19523 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -500,19 +500,19 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: F = y^21 + x*y^20 + (x^3 + x + 1)*y^18 + (x^3 + 1)*y^17 + (x^4 + x)*y^16 + (x^7 + x^6 + x^3 + x + 1)*y^15 + x^7*y^14 + (x^8 + x^7 + x^6 + x^4 + x^3 + 1)*y^13 + (x^9 + x^8 + x^4 + 1)*y^12 + (x^11 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2)*y^11 + (x^12 + x^9 + x^8 + x^7 + x^5 + x^3 + x + 1)*y^10 + (x^14 + x^13 + x^10 + x^9 + x^8 + x^7 + x^6 + x^3 + x^2 + 1)*y^9 + (x^13 + x^9 + x^8 + x^6 + x^4 + x^3 + x)*y^8 + (x^16 + x^15 + x^13 + x^12 + x^11 + x^7 + x^3 + x)*y^7 + (x^17 + x^16 + x^13 + x^9 + x^8 + x)*y^6 + (x^17 + x^16 + x^12 + x^7 + x^5 + x^2 + x + 1)*y^5 + (x^19 + x^16 + x^15 + x^12 + x^6 + x^5 + x^3 + 1)*y^4 + (x^18 + x^15 + x^12 + x^10 + x^9 + x^7 + x^4 + x)*y^3 + (x^22 + x^21 + x^20 + x^18 + x^13 + x^12 + x^9 + x^8 + x^7 + x^5 + x^4 + x^3)*y^2 + (x^23 + x^22 + x^20 + x^17 + x^15 + x^14 + x^12 + x^9)*y + x^25 + x^23 + x^19 + x^17 + x^15 + x^13 + x^11 + x^5 sage: x = K._ring.gen() sage: v0 = K.valuation(GaussValuation(K._ring, valuations.TrivialValuation(k)).augmentation(x,1)) - sage: sorted(v0.mac_lane_approximants(F, assume_squarefree=True), key=str) # assumes squarefree for speed + sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assumes squarefree for speed [[ Gauss valuation induced by (x)-adic valuation, v(y + x + 1) = 3/2 ], [ Gauss valuation induced by (x)-adic valuation, v(y) = 1 ], [ Gauss valuation induced by (x)-adic valuation, v(y) = 4/3 ], [ Gauss valuation induced by (x)-adic valuation, v(y^15 + y^13 + y^12 + y^10 + y^9 + y^8 + y^4 + y^3 + y^2 + y + 1) = 1 ]] sage: v0 = K.valuation(GaussValuation(K._ring, valuations.TrivialValuation(k)).augmentation(x+1,1)) - sage: sorted(v0.mac_lane_approximants(F, assume_squarefree=True), key=str) # assumes squarefree for speed + sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assumes squarefree for speed [[ Gauss valuation induced by (x + 1)-adic valuation, v(y + x^2 + 1) = 7/2 ], [ Gauss valuation induced by (x + 1)-adic valuation, v(y) = 3/4 ], [ Gauss valuation induced by (x + 1)-adic valuation, v(y) = 7/2 ], [ Gauss valuation induced by (x + 1)-adic valuation, v(y^13 + y^12 + y^10 + y^7 + y^6 + y^3 + 1) = 1 ]] sage: v0 = valuations.FunctionFieldValuation(K, GaussValuation(K._ring, valuations.TrivialValuation(k)).augmentation(x^3+x^2+1,1)) - sage: sorted(v0.mac_lane_approximants(F, assume_squarefree=True), key=str) # assumes squarefree for speed + sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assumes squarefree for speed [[ Gauss valuation induced by (x^3 + x^2 + 1)-adic valuation, v(y + x^3 + x^2 + x) = 2, v(y^2 + (x^6 + x^4 + 1)*y + x^14 + x^10 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2 + x) = 5 ], [ Gauss valuation induced by (x^3 + x^2 + 1)-adic valuation, v(y^2 + (x^2 + x)*y + 1) = 1 ], [ Gauss valuation induced by (x^3 + x^2 + 1)-adic valuation, v(y^3 + (x + 1)*y^2 + (x + 1)*y + x^2 + x + 1) = 1 ], @@ -528,7 +528,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: v0 = GaussValuation(K._ring, QQ.valuation(3)) sage: v1 = v0.augmentation(K._ring.gen(),1/3) sage: mu0 = valuations.FunctionFieldValuation(K, v1) - sage: sorted(mu0.mac_lane_approximants(F), key=str) + sage: mu0.mac_lane_approximants(F) [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + 2*x) = 2/3 ], [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + x) = 2/3 ]] @@ -573,7 +573,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: v0.mac_lane_approximants(G) [[ Gauss valuation induced by 5-adic valuation, v(x + 2) = 1 ], [ Gauss valuation induced by 5-adic valuation, v(x + 3) = 1 ]] - sage: sorted(v0.mac_lane_approximants(G, required_precision = 10), key=str) + sage: v0.mac_lane_approximants(G, required_precision = 10) [[ Gauss valuation induced by 5-adic valuation, v(x + 3626068) = 10 ], [ Gauss valuation induced by 5-adic valuation, v(x + 6139557) = 10 ]] @@ -585,10 +585,10 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: v = k.valuation() sage: R.=k[] sage: G = x^2 + 1 - sage: v1,v2 = v.mac_lane_approximants(G); sorted([v1,v2], key=str) + sage: v1,v2 = v.mac_lane_approximants(G); v1,v2 [[ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + O(5^4))) = 1 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + O(5^4))) = 1 ]] - sage: w1, w2 = v.mac_lane_approximants(G, required_precision = 2); sorted([w1,w2], key=str) + sage: w1, w2 = v.mac_lane_approximants(G, required_precision = 2); w1,w2 [[ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + O(5^4))) = 2 ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + 3*5 + O(5^4))) = 2 ]] @@ -601,7 +601,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru In this example, the process stops with a factorization of `x^2 + 1`:: - sage: sorted(v.mac_lane_approximants(G, required_precision=infinity), key=str) + sage: v.mac_lane_approximants(G, required_precision=infinity) [[ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + 2*5^2 + 5^3 + O(5^4))) = +Infinity ], [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + 3*5 + 2*5^2 + 3*5^3 + O(5^4))) = +Infinity ]] @@ -613,7 +613,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: G = x^2 + 1 sage: v.mac_lane_approximants(G) [[ Gauss valuation induced by 5-adic valuation, v(x + 2) = 1 ], [ Gauss valuation induced by 5-adic valuation, v(x + 3) = 1 ]] - sage: sorted(v.mac_lane_approximants(G, required_precision=5), key=str) + sage: v.mac_lane_approximants(G, required_precision=5) [[ Gauss valuation induced by 5-adic valuation, v(x + 1068) = 6 ], [ Gauss valuation induced by 5-adic valuation, v(x + 2057) = 5 ]] @@ -650,7 +650,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: vK(theta) 1/3 sage: G=Delta.change_ring(K) - sage: sorted(vK.mac_lane_approximants(G), key=str) # long time + sage: vK.mac_lane_approximants(G) [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 2*x^2 + 1/2*theta^4 + theta^3 + 5*theta + 1) = 5/3 ], [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 2*x^2 + 3/2*theta^4 + theta^3 + 5*theta + 1) = 5/3 ], [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 2*x^2 + theta^4 + theta^3 + 1) = 5/3 ]] @@ -782,8 +782,19 @@ def reduce_tree(v, w): if v in leafs: leafs.remove(v) + # The order of the leafs is not predictable in parallel mode and in + # serial mode it depends on the hash functions and so on the underlying + # archictecture (32/64 bit). There is no natural ordering on these + # valuations but it is very convenient for doctesting to return them in + # some stable order, so we just order them by their string + # representation which should be very fast. + try: + ret = sorted(leafs, key=lambda v: str(v)) + except: + # if for some reason the valuation can not be printed, we leave them unsorted + ret = list(leafs) - return list(leafs) + return ret @cached_method def _pow(self, x, e, error): @@ -972,7 +983,7 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No sage: S. = R[] sage: f = (x^6+2)^25 + 5 sage: v = R.valuation() - sage: sorted(v.montes_factorization(f, assume_squarefree=True, required_precision=0), key=str) + sage: v.montes_factorization(f, assume_squarefree=True, required_precision=0) [((1 + O(5^20))*x^50 + (2*5 + O(5^20))*x^45 + (5 + O(5^20))*x^40 + (5 + O(5^20))*x^30 + (2 + O(5^20))*x^25 + (3*5 + O(5^20))*x^20 + (2*5 + O(5^20))*x^10 + (2*5 + O(5^20))*x^5 + (5 + O(5^20))*x + 3 + 5 + O(5^20), 1), ((1 + O(5^20))*x^50 + (3*5 + O(5^20))*x^40 + (3*5 + O(5^20))*x^30 + (4*5 + O(5^20))*x^20 + (5 + O(5^20))*x^10 + 3 + 5 + O(5^20), 1), ((1 + O(5^20))*x^50 + (3*5 + O(5^20))*x^45 + (5 + O(5^20))*x^40 + (5 + O(5^20))*x^30 + (3 + 4*5 + O(5^20))*x^25 + (3*5 + O(5^20))*x^20 + (2*5 + O(5^20))*x^10 + (3*5 + O(5^20))*x^5 + (4*5 + O(5^20))*x + 3 + 5 + O(5^20), 1)] @@ -985,7 +996,7 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No sage: S. = R[] sage: f = (x^3 + 5)*(x^5 + w) + 625 sage: v = R.valuation() - sage: sorted(v.montes_factorization(f, assume_squarefree=True, required_precision=0), key=str) + sage: v.montes_factorization(f, assume_squarefree=True, required_precision=0) [((1 + O(w^60))*x + 4*w + O(w^60), 1), ((1 + O(w^60))*x^2 + (w + O(w^60))*x + w^2 + O(w^60), 1), ((1 + O(w^60))*x^5 + w + O(w^60), 1)] From 82bc1fb522d3bedabdb639debf88f75d73d60d09 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 2 Nov 2017 18:55:05 +1000 Subject: [PATCH 424/740] Initial optimization to avoid polytopes for small number of possible choices. --- .../rigged_configurations/kleber_tree.py | 51 +++++++++++-------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/src/sage/combinat/rigged_configurations/kleber_tree.py b/src/sage/combinat/rigged_configurations/kleber_tree.py index abb7dfe554e..d6f46b19d97 100644 --- a/src/sage/combinat/rigged_configurations/kleber_tree.py +++ b/src/sage/combinat/rigged_configurations/kleber_tree.py @@ -71,6 +71,7 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.misc.cachefunc import cached_method from sage.misc.latex import latex +from sage.misc.misc_c import prod from sage.arith.all import binomial from sage.rings.integer import Integer from sage.rings.all import ZZ @@ -621,6 +622,10 @@ def __init__(self, cartan_type, B, classical_ct): self._cartan_type = cartan_type self.B = B self._classical_ct = classical_ct + # Our computations in _children_iter_vector use dense vectors. + # Moreover, ranks are relatively small, so just use the dense + # version of the Cartan matrix. + self._CM = self._classical_ct.cartan_matrix().dense_matrix() self._build_tree() self._latex_options = dict(edge_labels=True, use_vector_notation=False, hspace=2.5, vspace=min(-2.5, -0.75*self._classical_ct.rank())) @@ -732,24 +737,19 @@ def _build_tree(self): growth = False depth += 1 leaves = new_children - new_children = [] if depth <= len(L[0]): + new_children = [] for x in full_list: growth = True for a in range(n): for i in range(depth - 1, len(L[a])): # Subtract 1 for indexing x.weight += L[a][i] * weight_basis[I[a]] - if x in leaves: - for new_child in child_itr(x): - if not self._prune(new_child, depth): - new_children.append(new_child) - else: - for x in leaves: - for new_child in child_itr(x): - if not self._prune(new_child, depth): - new_children.append(new_child) + new_children = [new_child + for x in leaves + for new_child in child_itr(x) + if not self._prune(new_child, depth)] # Connect the new children into the tree if new_children: @@ -797,6 +797,16 @@ def _children_iter(self, node): sage: for x in KT._children_iter(KT[1]): x Kleber tree node with weight [0, 0, 0, 0] and upwards edge root [1, 2, 1, 1] """ + # It is faster to just cycle through than build the polytope and its + # lattice points when we are sufficiently small + # The number 500 comes from testing on my machine about where the + # tradeoff occurs between the methods. However, this may grow as + # the _children_iter_vector is further optimized. + if node != self.root and prod(val+1 for val in node.up_root.coefficients()) < 1000: + for x in self._children_iter_vector(node): + yield x + return + n = self._classical_ct.rank() I = self._classical_ct.index_set() Q = self._classical_ct.root_system().root_lattice() @@ -807,7 +817,7 @@ def _children_iter(self, node): # Construct the shifted weight cone root_weight = node.weight.to_vector() ieqs = [[root_weight[i]] + list(col) - for i,col in enumerate(self._classical_ct.cartan_matrix())] + for i,col in enumerate(self._CM.columns())] # Construct the negative weight cone for i in range(n): v = [0] * (n+1) @@ -867,8 +877,7 @@ def _children_iter_vector(self, node): P = self._classical_ct.root_system().weight_lattice() I = self._classical_ct.index_set() wt = node.weight.to_vector() - # Everything is dense at this point; moreover, ranks are relatively small - CM = self._classical_ct.cartan_matrix().dense_matrix() + cols = self._CM.columns() F = FreeModule(ZZ, self._classical_ct.rank()) L = [range(val + 1) for val in node.up_root.to_vector()] @@ -877,12 +886,15 @@ def _children_iter_vector(self, node): next(it) # First element is the zero element for root in it: # Convert the list to the weight lattice - converted_root = CM * F(root) + converted_root = sum(cols[i] * c for i,c in enumerate(root) if c != 0) if all(wt[i] >= val for i,val in enumerate(converted_root)): - wd = {I[i]: wt[i] - val for i, val in enumerate(converted_root)} - rd = {I[i]: val for i, val in enumerate(root)} - yield KleberTreeNode(self, P._from_dict(wd), Q._from_dict(rd), node) + wd = {I[i]: wt[i] - val for i,val in enumerate(converted_root)} + rd = {I[i]: val for i,val in enumerate(root) if val != 0} + yield KleberTreeNode(self, + P._from_dict(wd), + Q._from_dict(rd, remove_zeros=False), + node) def _prune(self, new_child, depth): r""" @@ -1206,9 +1218,8 @@ def _prune(self, new_child, depth): sigma = self._folded_ct._orbit for orbit in sigma[1:]: start = new_child.weight[orbit[0]] - for i in orbit[1:]: - if new_child.weight[i] != start: - return True + if any(new_child.weight[i] != start for i in orbit[1:]): + return True gamma = self._folded_ct.scaling_factors() for a in range(1, len(gamma)): if (depth - 1) % gamma[a] != 0 and new_child.up_root[sigma[a][0]] \ From 51a96ef49993a63ed56aae02a9b00a6599a2b1a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 3 Nov 2017 12:50:29 +0700 Subject: [PATCH 425/740] do not catch bare exceptions --- src/sage/rings/valuation/valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 7f9a1c19523..ca6f8e70ab7 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -790,7 +790,7 @@ def reduce_tree(v, w): # representation which should be very fast. try: ret = sorted(leafs, key=lambda v: str(v)) - except: + except Exception: # if for some reason the valuation can not be printed, we leave them unsorted ret = list(leafs) From c51fb436cf39153f5bd8c5abae857ef013dccd8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 3 Nov 2017 12:50:52 +0700 Subject: [PATCH 426/740] shorten code --- src/sage/rings/valuation/valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index ca6f8e70ab7..deb098749e0 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -789,7 +789,7 @@ def reduce_tree(v, w): # some stable order, so we just order them by their string # representation which should be very fast. try: - ret = sorted(leafs, key=lambda v: str(v)) + ret = sorted(leafs, key=str) except Exception: # if for some reason the valuation can not be printed, we leave them unsorted ret = list(leafs) From 62cb0c0d78563b1062bb43e5b88f838d4ec012b0 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 3 Nov 2017 06:36:54 -0500 Subject: [PATCH 427/740] Fixing bugs with unnecessary overwriting. --- src/sage/typeset/character_art_factory.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/sage/typeset/character_art_factory.py b/src/sage/typeset/character_art_factory.py index 066e4c9258a..f51898d0b8f 100644 --- a/src/sage/typeset/character_art_factory.py +++ b/src/sage/typeset/character_art_factory.py @@ -116,6 +116,8 @@ def build(self, obj, baseline=None): if baseline is not None: obj._baseline = baseline return obj + if isinstance(obj, SageObject): + return self.build_from_magic_method(obj, baseline) if baseline is None: baseline = 0 if isinstance(obj, (tuple, list, dict, set)): @@ -127,8 +129,6 @@ def build(self, obj, baseline=None): return self.build_list(obj, baseline) else: return self.build_set(obj, baseline) - elif isinstance(obj, SageObject): - return self.build_from_magic_method(obj, baseline) else: return self.build_from_string(obj, baseline) @@ -141,10 +141,14 @@ def build_empty(self): Character art instance. EXAMPLES:: + + sage: from sage.typeset.ascii_art import _ascii_art_factory as factory + sage: str(factory.build_empty()) + '' """ return self.art_type.empty() - def build_from_magic_method(self, obj, baseline=0): + def build_from_magic_method(self, obj, baseline=None): """ Return the character art object created by the object's magic method @@ -163,7 +167,8 @@ def build_from_magic_method(self, obj, baseline=0): """ magic_method = getattr(obj, self.magic_method_name) ret = magic_method() - ret._baseline = baseline + if baseline is not None: + ret._baseline = baseline return ret def build_from_string(self, obj, baseline=0): @@ -429,8 +434,8 @@ def parse_keywords(self, kwds): sage: from sage.typeset.ascii_art import _ascii_art_factory as factory sage: d = {'sep': '2', 'baseline': 5} - ('2', 5, 0) sage: factory.parse_keywords(d) + ('2', 5, None) sage: d {} @@ -438,7 +443,7 @@ def parse_keywords(self, kwds): sage: d = {'foo': '2', 'baseline': 5} sage: factory.parse_keywords(d) - (, 5, 0) + (, 5, None) sage: d {'foo': '2'} @@ -454,5 +459,5 @@ def parse_keywords(self, kwds): sep = kwds.pop("separator", empty) elif "separator" in kwds: raise ValueError("cannot specify both 'sep' and 'separator'") - return sep, kwds.pop("baseline", 0), kwds.pop("sep_baseline", 0) + return sep, kwds.pop("baseline", None), kwds.pop("sep_baseline", None) From c4a9900c04eeffeb083ec59e6556a354981acaa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 5 Nov 2017 09:53:17 +0700 Subject: [PATCH 428/740] Adapt doctests to sorting of mac_lane_approximants() --- src/doc/en/reference/valuations/index.rst | 4 ++-- .../function_field/function_field_valuation.py | 10 +++++----- src/sage/rings/valuation/limit_valuation.py | 4 ++-- src/sage/rings/valuation/mapped_valuation.py | 4 ++-- src/sage/rings/valuation/valuation.py | 16 ++++++---------- 5 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/doc/en/reference/valuations/index.rst b/src/doc/en/reference/valuations/index.rst index e939d7c4873..20375496faf 100644 --- a/src/doc/en/reference/valuations/index.rst +++ b/src/doc/en/reference/valuations/index.rst @@ -68,8 +68,8 @@ Valuations can also be extended from smaller function fields:: sage: R. = K[] sage: L. = K.extension(y^2 - x) sage: v.extensions(L) - [[ (x - 4)-adic valuation, v(y - 2) = 1 ]-adic valuation, - [ (x - 4)-adic valuation, v(y + 2) = 1 ]-adic valuation] + [[ (x - 4)-adic valuation, v(y + 2) = 1 ]-adic valuation, + [ (x - 4)-adic valuation, v(y - 2) = 1 ]-adic valuation] Low-Level Interface ------------------- diff --git a/src/sage/rings/function_field/function_field_valuation.py b/src/sage/rings/function_field/function_field_valuation.py index 31e87e07f87..8a9d686545b 100644 --- a/src/sage/rings/function_field/function_field_valuation.py +++ b/src/sage/rings/function_field/function_field_valuation.py @@ -35,8 +35,8 @@ sage: R. = K[] sage: L. = K.extension(y^2 - x) sage: w = v.extensions(L); w - [[ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation, - [ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation] + [[ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation, + [ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation] TESTS: @@ -722,7 +722,7 @@ def reduce(self, f): if self(f) > 0: return self.residue_field().zero() if self(f) < 0: - raise ValueError + raise ValueError("can not reduce element of negative valuation") base = self._base_valuation @@ -1222,8 +1222,8 @@ def _repr_(self): sage: L. = K.extension(y^2 - 1/x^2 - 1) sage: v = K.valuation(1/x) sage: w = v.extensions(L); w - [[ Valuation at the infinite place, v(y - 1) = 2 ]-adic valuation, - [ Valuation at the infinite place, v(y + 1) = 2 ]-adic valuation] + [[ Valuation at the infinite place, v(y + 1) = 2 ]-adic valuation, + [ Valuation at the infinite place, v(y - 1) = 2 ]-adic valuation] """ assert(self.domain().base() is not self.domain()) diff --git a/src/sage/rings/valuation/limit_valuation.py b/src/sage/rings/valuation/limit_valuation.py index 888824910cb..a86a9707e2e 100644 --- a/src/sage/rings/valuation/limit_valuation.py +++ b/src/sage/rings/valuation/limit_valuation.py @@ -277,7 +277,7 @@ def _improve_approximation_for_reduce(self, f): for some values (note that 1341-1337 is a square):: sage: v = K.valuation(1341) - sage: w = v.extensions(L)[0] + sage: w = v.extensions(L)[1] sage: u = w._base_valuation sage: u._approximation [ Gauss valuation induced by (x - 1341)-adic valuation, v(y - 2) = 1 ] @@ -431,7 +431,7 @@ def lift(self, F): sage: L. = K.extension(y^4 - x^2 - 2*x - 1) sage: v = K.valuation(1) - sage: w = v.extensions(L)[0]; w + sage: w = v.extensions(L)[1]; w [ (x - 1)-adic valuation, v(y^2 - 2) = 1 ]-adic valuation sage: s = w.reduce(y); s u1 diff --git a/src/sage/rings/valuation/mapped_valuation.py b/src/sage/rings/valuation/mapped_valuation.py index e6afa136540..a91c8da0a87 100644 --- a/src/sage/rings/valuation/mapped_valuation.py +++ b/src/sage/rings/valuation/mapped_valuation.py @@ -526,8 +526,8 @@ class FiniteExtensionFromLimitValuation(FiniteExtensionFromInfiniteValuation): sage: L. = K.extension(y^2 - x) sage: v = K.valuation(1) sage: w = v.extensions(L); w - [[ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation, - [ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation] + [[ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation, + [ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation] TESTS:: diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index deb098749e0..302db445b57 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -586,11 +586,11 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru sage: R.=k[] sage: G = x^2 + 1 sage: v1,v2 = v.mac_lane_approximants(G); v1,v2 - [[ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + O(5^4))) = 1 ], - [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + O(5^4))) = 1 ]] + ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + O(5^4))) = 1 ], + [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + O(5^4))) = 1 ]) sage: w1, w2 = v.mac_lane_approximants(G, required_precision = 2); w1,w2 - [[ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + O(5^4))) = 2 ], - [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + 3*5 + O(5^4))) = 2 ]] + ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (2 + 5 + O(5^4))) = 2 ], + [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + (3 + 3*5 + O(5^4))) = 2 ]) Note how the latter give a better approximation to the factors of `x^2 + 1`:: @@ -984,9 +984,7 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No sage: f = (x^6+2)^25 + 5 sage: v = R.valuation() sage: v.montes_factorization(f, assume_squarefree=True, required_precision=0) - [((1 + O(5^20))*x^50 + (2*5 + O(5^20))*x^45 + (5 + O(5^20))*x^40 + (5 + O(5^20))*x^30 + (2 + O(5^20))*x^25 + (3*5 + O(5^20))*x^20 + (2*5 + O(5^20))*x^10 + (2*5 + O(5^20))*x^5 + (5 + O(5^20))*x + 3 + 5 + O(5^20), 1), - ((1 + O(5^20))*x^50 + (3*5 + O(5^20))*x^40 + (3*5 + O(5^20))*x^30 + (4*5 + O(5^20))*x^20 + (5 + O(5^20))*x^10 + 3 + 5 + O(5^20), 1), - ((1 + O(5^20))*x^50 + (3*5 + O(5^20))*x^45 + (5 + O(5^20))*x^40 + (5 + O(5^20))*x^30 + (3 + 4*5 + O(5^20))*x^25 + (3*5 + O(5^20))*x^20 + (2*5 + O(5^20))*x^10 + (3*5 + O(5^20))*x^5 + (4*5 + O(5^20))*x + 3 + 5 + O(5^20), 1)] + ((1 + O(5^20))*x^50 + (2*5 + O(5^20))*x^45 + (5 + O(5^20))*x^40 + (5 + O(5^20))*x^30 + (2 + O(5^20))*x^25 + (3*5 + O(5^20))*x^20 + (2*5 + O(5^20))*x^10 + (2*5 + O(5^20))*x^5 + (5 + O(5^20))*x + 3 + 5 + O(5^20)) * ((1 + O(5^20))*x^50 + (3*5 + O(5^20))*x^45 + (5 + O(5^20))*x^40 + (5 + O(5^20))*x^30 + (3 + 4*5 + O(5^20))*x^25 + (3*5 + O(5^20))*x^20 + (2*5 + O(5^20))*x^10 + (3*5 + O(5^20))*x^5 + (4*5 + O(5^20))*x + 3 + 5 + O(5^20)) * ((1 + O(5^20))*x^50 + (3*5 + O(5^20))*x^40 + (3*5 + O(5^20))*x^30 + (4*5 + O(5^20))*x^20 + (5 + O(5^20))*x^10 + 3 + 5 + O(5^20)) In this case, ``f`` factors into degrees 1, 2, and 5 over a totally ramified extension:: @@ -997,9 +995,7 @@ def montes_factorization(self, G, assume_squarefree=False, required_precision=No sage: f = (x^3 + 5)*(x^5 + w) + 625 sage: v = R.valuation() sage: v.montes_factorization(f, assume_squarefree=True, required_precision=0) - [((1 + O(w^60))*x + 4*w + O(w^60), 1), - ((1 + O(w^60))*x^2 + (w + O(w^60))*x + w^2 + O(w^60), 1), - ((1 + O(w^60))*x^5 + w + O(w^60), 1)] + ((1 + O(w^60))*x + 4*w + O(w^60)) * ((1 + O(w^60))*x^2 + (w + O(w^60))*x + w^2 + O(w^60)) * ((1 + O(w^60))*x^5 + w + O(w^60)) REFERENCES: From 65df5a1a720fb949838ad045aa4a526a3631f047 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 8 Nov 2017 15:41:15 +1000 Subject: [PATCH 429/740] Faster _calc_vacancy_number and phi. Cythonized RiggedPartition. --- .../rigged_configurations/rc_crystal.py | 15 +- .../rigged_configurations/rc_infinity.py | 14 +- .../rigged_configurations.py | 38 +++- .../rigged_partition.pxd | 15 ++ ...gged_partition.py => rigged_partition.pyx} | 214 +++++++++++++----- 5 files changed, 216 insertions(+), 80 deletions(-) create mode 100644 src/sage/combinat/rigged_configurations/rigged_partition.pxd rename src/sage/combinat/rigged_configurations/{rigged_partition.py => rigged_partition.pyx} (76%) diff --git a/src/sage/combinat/rigged_configurations/rc_crystal.py b/src/sage/combinat/rigged_configurations/rc_crystal.py index 8e36864d3e1..1b5983b7d56 100644 --- a/src/sage/combinat/rigged_configurations/rc_crystal.py +++ b/src/sage/combinat/rigged_configurations/rc_crystal.py @@ -270,10 +270,13 @@ def _calc_vacancy_number(self, partitions, a, i, **options): """ vac_num = self._wt[self.index_set()[a]] - for b in range(self._cartan_matrix.ncols()): + for b,nu in enumerate(partitions): val = self._cartan_matrix[a,b] if val: - vac_num -= val * partitions[b].get_num_cells_to_column(i) + if i == float('inf'): + vac_num -= val * sum(nu) + else: + vac_num -= val * nu.get_num_cells_to_column(i) return vac_num @@ -358,11 +361,15 @@ def _calc_vacancy_number(self, partitions, a, i, **options): ia = I[a] vac_num = self._wt[ia] + if i == float('inf'): + return vac_num - sum(self._cartan_matrix[a,b] * sum(nu) + for b,nu in enumerate(partitions)) + gamma = self._folded_ct.scaling_factors() g = gamma[ia] - for b in range(self._cartan_matrix.ncols()): + for b, nu in enumerate(partitions): ib = I[b] - q = partitions[b].get_num_cells_to_column(g*i, gamma[ib]) + q = nu.get_num_cells_to_column(g*i, gamma[ib]) vac_num -= self._cartan_matrix[a,b] * q / gamma[ib] return vac_num diff --git a/src/sage/combinat/rigged_configurations/rc_infinity.py b/src/sage/combinat/rigged_configurations/rc_infinity.py index d1afd84bdbc..1839acddac0 100644 --- a/src/sage/combinat/rigged_configurations/rc_infinity.py +++ b/src/sage/combinat/rigged_configurations/rc_infinity.py @@ -261,12 +261,12 @@ def _calc_vacancy_number(self, partitions, a, i, **options): sage: RC._calc_vacancy_number(elt.nu(), 0, 1) -1 """ - vac_num = 0 + if i == float('inf'): + return -sum(self._cartan_matrix[a,b] * sum(nu) + for b,nu in enumerate(partitions)) - for b in range(self._cartan_matrix.ncols()): - vac_num -= self._cartan_matrix[a,b] * partitions[b].get_num_cells_to_column(i) - - return vac_num + return -sum(self._cartan_matrix[a,b] * nu.get_num_cells_to_column(i) + for b,nu in enumerate(partitions)) # FIXME: Remove this method!!! def weight_lattice_realization(self): @@ -380,6 +380,10 @@ def _calc_vacancy_number(self, partitions, a, i): ia = I[a] vac_num = 0 + if i == float('inf'): + return -sum(self._cartan_matrix[a,b] * sum(nu) + for b,nu in enumerate(partitions)) + gamma = self._folded_ct.scaling_factors() g = gamma[ia] for b in range(self._cartan_matrix.ncols()): diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index 9e99f3e29b4..52960c32a25 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -810,8 +810,12 @@ def _calc_vacancy_number(self, partitions, a, i, **options): if dim[0] == self._rc_index[a]: vac_num += min(dim[1], i) - for b in range(self._cartan_matrix.ncols()): - vac_num -= self._cartan_matrix[a,b] * partitions[b].get_num_cells_to_column(i) + if i == float('inf'): + vac_num -= sum(self._cartan_matrix[a,b] * sum(nu) + for b,nu in enumerate(partitions)) + else: + vac_num -= sum(self._cartan_matrix[a,b] * nu.get_num_cells_to_column(i) + for b,nu in enumerate(partitions)) return vac_num @@ -1146,11 +1150,15 @@ def _calc_vacancy_number(self, partitions, a, i, **options): if dim[0] == self._rc_index[a]: vac_num += min(dim[1], i) - gamma = self._folded_ct.scaling_factors() - for b in range(self._cartan_matrix.ncols()): - vac_num -= (self._cartan_matrix[a,b] - * partitions[b].get_num_cells_to_column(gamma[a+1]*i, gamma[b+1]) - // gamma[b+1]) + if i == float('inf'): + vac_num -= sum(self._cartan_matrix[a,b] * sum(nu) + for b,nu in enumerate(partitions)) + else: + gamma = self._folded_ct.scaling_factors() + vac_num -= sum(self._cartan_matrix[a,b] + * nu.get_num_cells_to_column(gamma[a+1]*i, gamma[b+1]) + // gamma[b+1] + for b,nu in enumerate(partitions)) return vac_num @@ -1492,8 +1500,12 @@ def _calc_vacancy_number(self, partitions, a, i, **options): vac_num += min(dim[1], i) gamma = self._folded_ct.scaling_factors() - for b in range(self._cartan_matrix.ncols()): - vac_num -= self._cartan_matrix[a,b] * partitions[b].get_num_cells_to_column(i) // gamma[b+1] + if i == float('inf'): + vac_num -= sum(self._cartan_matrix[a,b] * sum(nu) // gamma[b+1] + for b, nu in enumerate(partitions)) + else: + vac_num -= sum(self._cartan_matrix[a,b] * nu.get_num_cells_to_column(i) // gamma[b+1] + for b, nu in enumerate(partitions)) return vac_num @@ -1640,8 +1652,12 @@ def _calc_vacancy_number(self, partitions, a, i, **options): if dim[0] == self._rc_index[a]: vac_num += min(dim[1], i) - for b in range(self._cartan_matrix.ncols()): - vac_num -= self._cartan_matrix[a,b] * partitions[b].get_num_cells_to_column(i) / 2 + if i == float('inf'): + vac_num -= sum(self._cartan_matrix[a,b] * sum(nu) / 2 + for b,nu in enumerate(partitions)) + else: + vac_num -= sum(self._cartan_matrix[a,b] * nu.get_num_cells_to_column(i) / 2 + for b,nu in enumerate(partitions)) return vac_num diff --git a/src/sage/combinat/rigged_configurations/rigged_partition.pxd b/src/sage/combinat/rigged_configurations/rigged_partition.pxd new file mode 100644 index 00000000000..e99258f33b2 --- /dev/null +++ b/src/sage/combinat/rigged_configurations/rigged_partition.pxd @@ -0,0 +1,15 @@ +from sage.structure.sage_object cimport SageObject + +cdef class RiggedPartition(SageObject): + cdef public list _list + cdef public list vacancy_numbers + cdef public list rigging + cdef long _hash + + cpdef get_num_cells_to_column(self, int end_column, t=*) + cpdef insert_cell(self, int max_width) + cpdef remove_cell(self, row, int num_cells=*) + +cdef class RiggedPartitionTypeB(RiggedPartition): + pass + diff --git a/src/sage/combinat/rigged_configurations/rigged_partition.py b/src/sage/combinat/rigged_configurations/rigged_partition.pyx similarity index 76% rename from src/sage/combinat/rigged_configurations/rigged_partition.py rename to src/sage/combinat/rigged_configurations/rigged_partition.pyx index 4094cec20c7..47a91f3f174 100644 --- a/src/sage/combinat/rigged_configurations/rigged_partition.py +++ b/src/sage/combinat/rigged_configurations/rigged_partition.pyx @@ -38,10 +38,10 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.combinat.combinat import CombinatorialObject from sage.misc.latex import latex +from sage.structure.richcmp cimport richcmp -class RiggedPartition(CombinatorialObject): +cdef class RiggedPartition(SageObject): r""" The RiggedPartition class which is the data structure of a rigged (i.e. marked or decorated) Young diagram of a partition. @@ -58,8 +58,6 @@ class RiggedPartition(CombinatorialObject): 0[ ][ ]0 -1[ ]-1 - sage: type(RP) - """ def __init__(self, shape=None, rigging_list=None, vacancy_nums=None): @@ -70,47 +68,49 @@ def __init__(self, shape=None, rigging_list=None, vacancy_nums=None): INPUT: - - ``shape`` -- (Default: ``None``) The shape - - - ``rigging_list`` -- (Default: ``None``) The riggings - - - ``vacancy_nums`` -- (Default: ``None``) The vacancy numbers + - ``shape`` -- (default: ``None``) the shape + - ``rigging_list`` -- (default: ``None``) the riggings + - ``vacancy_nums`` -- (default: ``None``) the vacancy numbers TESTS:: - sage: sage.combinat.rigged_configurations.rigged_partition.RiggedPartition() + sage: from sage.combinat.rigged_configurations.rigged_partition import RiggedPartition + sage: RiggedPartition() (/) - sage: RP = sage.combinat.rigged_configurations.rigged_partition.RiggedPartition([2,1], [0,0], [1, 0]) + sage: RP = RiggedPartition([2,1], [0,0], [1, 0]) sage: RP 1[ ][ ]0 0[ ]0 sage: TestSuite(RP).run() """ + self._hash = 0 + if shape is None: - CombinatorialObject.__init__(self, []) + self._list = [] self.vacancy_numbers = [] self.rigging = [] - else: - CombinatorialObject.__init__(self, shape) + return - if vacancy_nums is not None: - if len(shape) != len(vacancy_nums): - raise ValueError("Mismatch between shape and vacancy numbers") + self._list = list(shape) - self.vacancy_numbers = list(vacancy_nums) - else: - self.vacancy_numbers = [None] * len(shape) + if vacancy_nums is not None: + if len(shape) != len(vacancy_nums): + raise ValueError("mismatch between shape and vacancy numbers") - if rigging_list is not None: + self.vacancy_numbers = list(vacancy_nums) + else: + self.vacancy_numbers = [None] * len(shape) + + if rigging_list is not None: - if len(shape) != len(rigging_list): - raise ValueError("Mismatch between shape and rigging list") + if len(shape) != len(rigging_list): + raise ValueError("mismatch between shape and rigging list") - self.rigging = list(rigging_list) - else: - self.rigging = [None] * len(shape) + self.rigging = list(rigging_list) + else: + self.rigging = [None] * len(shape) def _repr_(self): """ @@ -132,7 +132,7 @@ def _repr_(self): sage: Partitions.options._reset() """ # If it is empty, return saying so - if len(self._list) == 0: + if not self._list: return("(/)\n") from sage.combinat.partition import Partitions @@ -243,9 +243,18 @@ def _clone(self): sage: RP is RP2 False """ - return self.__class__(self._list[:], self.rigging[:], self.vacancy_numbers[:]) - - def __eq__(self, rhs): + # TODO: Perhaps we can be better by not copying data as much and do it + # more on-demand + cdef RiggedPartition res + cdef type t = type(self) + res = t.__new__(t) + res._list = self._list[:] + res.rigging = self.rigging[:] + res.vacancy_numbers = self.vacancy_numbers[:] + res._hash = self._hash + return res + + def __richcmp__(self, other, int op): r""" Return true if ``self`` equals ``rhs``. @@ -257,14 +266,94 @@ def __eq__(self, rhs): sage: x == y False """ - if isinstance(rhs, RiggedPartition): - return self._list == rhs._list and self.rigging == rhs.rigging + if not (isinstance(self, RiggedPartition) and isinstance(other, RiggedPartition)): + return False + + cdef left = self + cdef right = other + return richcmp((left._list, left.rigging), (right._list, right.rigging), op) + + # TODO: Cythonize CombinatorialObject? + + def __hash__(self): + """ + TESTS:: + + sage: from sage.combinat.rigged_configurations.rigged_partition import RiggedPartition + sage: nu = RiggedPartition() + sage: h = hash(nu) + sage: _ = nu.insert_cell(2) + sage: h == hash(nu) + False + """ + if self._hash == 0: + self._hash = hash(tuple(self._list)) + return self._hash + + def __nonzero__(self): + """ + TESTS:: - return False + sage: from sage.combinat.rigged_configurations.rigged_partition import RiggedPartition + sage: nu = RiggedPartition() + sage: bool(nu) + False + sage: nu = RiggedPartition([1]) + sage: bool(nu) + True + """ + return bool(self._list) + + def __len__(self): + """ + TESTS:: + + sage: from sage.combinat.rigged_configurations.rigged_partition import RiggedPartition + sage: nu = RiggedPartition() + sage: len(nu) + 0 + sage: nu = RiggedPartition([3,2,2,1]) + sage: len(nu) + 4 + """ + return len(self._list) + + def __getitem__(self, key): + """ + TESTS:: + + sage: from sage.combinat.rigged_configurations.rigged_partition import RiggedPartition + sage: nu = RiggedPartition([3,2,1]) + sage: nu[2] + 1 + """ + return self._list[key] + + def __iter__(self): + """ + TESTS:: + + sage: from sage.combinat.rigged_configurations.rigged_partition import RiggedPartition + sage: nu = RiggedPartition([3,2,1]) + sage: list(nu) + [3, 2, 1] + """ + return iter(self._list) + + def __reduce__(self): + """ + TESTS:: + + sage: from sage.combinat.rigged_configurations.rigged_partition import RiggedPartition + sage: nu = RiggedPartition([3,2,1]) + sage: loads(dumps(nu)) == nu + True + """ + return type(self), (self._list, self.rigging, self.vacancy_numbers) # Should we move these functions to the CP -> RC bijections? - def get_num_cells_to_column(self, end_column, t=1): + cpdef get_num_cells_to_column(self, int end_column, t=1): r""" Get the number of cells in all columns before the ``end_column``. @@ -291,9 +380,9 @@ def get_num_cells_to_column(self, end_column, t=1): sage: RP.get_num_cells_to_column(3, 2) 5 """ - sum_cells = 0 + cdef Py_ssize_t sum_cells = 0 # Sum up from the reverse (the smallest row sizes) - i = len(self._list) - 1 + cdef Py_ssize_t i = len(self._list) - 1 while i >= 0 and self._list[i]*t < end_column: sum_cells += self._list[i]*t i -= 1 @@ -304,7 +393,7 @@ def get_num_cells_to_column(self, end_column, t=1): return sum_cells - def insert_cell(self, max_width): + cpdef insert_cell(self, int max_width): r""" Insert a cell given at a singular value as long as its less than the specified width. @@ -333,7 +422,9 @@ def insert_cell(self, max_width): -1[ ]-1 """ - max_pos = -1 + cdef Py_ssize_t max_pos = -1 + cdef Py_ssize_t i + self._hash = 0 # Reset the cached hash value if max_width > 0: for i, vac_num in enumerate(self.vacancy_numbers): if self._list[i] <= max_width and vac_num == self.rigging[i]: @@ -354,7 +445,7 @@ def insert_cell(self, max_width): self.rigging[max_pos] = None # State that we've changed this row return self._list[max_pos] - 1 - def remove_cell(self, row, num_cells=1): + cpdef remove_cell(self, row, int num_cells=1): r""" Removes a cell at the specified ``row``. @@ -384,31 +475,34 @@ def remove_cell(self, row, num_cells=1): -1[ ]-1 """ + self._hash = 0 # Reset the cached hash value if row is None: return None - if self._list[row] <= num_cells: - self._list.pop(row) - self.vacancy_numbers.pop(row) - self.rigging.pop(row) + cdef Py_ssize_t r = row + if self._list[r] <= num_cells: + self._list.pop(r) + self.vacancy_numbers.pop(r) + self.rigging.pop(r) return None # Find the beginning of the next block we want - block_len = self._list[row] - num_cells # The length of the desired block + cdef Py_ssize_t block_len = self._list[r] - num_cells # The length of the desired block if row + 1 == len(self._list): # If we are at the end, just do a simple remove - self._list[row] = block_len - self.vacancy_numbers[row] = None - self.rigging[row] = None - return row + self._list[r] = block_len + self.vacancy_numbers[r] = None + self.rigging[r] = None + return r - for i in range(row + 1, len(self._list)): + cdef Py_ssize_t i + for i in range(r + 1, len(self._list)): if self._list[i] <= block_len: - if i == row + 1: + if i == r + 1: # If the next row is a block change, just reduce by num_cells - self._list[row] = block_len - self.vacancy_numbers[row] = None - self.rigging[row] = None + self._list[r] = block_len + self.vacancy_numbers[r] = None + self.rigging[r] = None return row # Otherwise we need to "move" the row @@ -417,15 +511,15 @@ def remove_cell(self, row, num_cells=1): self.vacancy_numbers.insert(i, None) self.rigging.insert(i, None) - self._list.pop(row) - self.vacancy_numbers.pop(row) - self.rigging.pop(row) + self._list.pop(r) + self.vacancy_numbers.pop(r) + self.rigging.pop(r) return i - 1 # We need to "move" the row to the end of the partition - self._list.pop(row) - self.vacancy_numbers.pop(row) - self.rigging.pop(row) + self._list.pop(r) + self.vacancy_numbers.pop(r) + self.rigging.pop(r) self._list.append(block_len) # Placeholders as above @@ -433,7 +527,7 @@ def remove_cell(self, row, num_cells=1): self.rigging.append(None) return len(self._list) - 1 -class RiggedPartitionTypeB(RiggedPartition): +cdef class RiggedPartitionTypeB(RiggedPartition): r""" Rigged partitions for type `B_n^{(1)}` which has special printing rules which comes from the fact that the `n`-th partition can have columns of From 17655fd0c08f69032a7caaa57b43595945064b8e Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Sat, 11 Nov 2017 15:23:34 +0100 Subject: [PATCH 430/740] extra effort if is_subring is not implemented. --- src/sage/modules/free_module.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 12849036a37..95b5f7438cb 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -1502,6 +1502,7 @@ def is_submodule(self, other): else: if self.inner_product_matrix() != other.inner_product_matrix(): return False + # self and other lie in a common ambient space. R = self.base_ring() S = other.base_ring() if R != S: @@ -1509,9 +1510,12 @@ def is_submodule(self, other): if not R.is_subring(S): return False except NotImplementedError: - return False + if not R.fraction_field().is_subring(S): + raise NotImplementedError("could not determine if %s is a " + "subring of %s" %(R, S)) + # now R is a subring of S try: - M=[list(other.basis_matrix().solve_left(self.basis_matrix()[i])) for i in range(self.basis_matrix().nrows())] + M = [list(other.basis_matrix().solve_left(self.basis_matrix()[i])) for i in range(self.basis_matrix().nrows())] except ValueError: return False from sage.misc.flatten import flatten From 4583f6557566fea5c01f62fab6e8d05a993da832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 13 Nov 2017 12:32:38 +0700 Subject: [PATCH 431/740] fix output order --- src/sage/rings/valuation/limit_valuation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/valuation/limit_valuation.py b/src/sage/rings/valuation/limit_valuation.py index a86a9707e2e..9a240749ab4 100644 --- a/src/sage/rings/valuation/limit_valuation.py +++ b/src/sage/rings/valuation/limit_valuation.py @@ -36,8 +36,8 @@ sage: v = K.valuation(1) sage: w = v.extensions(L); w - [[ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation, - [ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation] + [[ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation, + [ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation] The same phenomenon can be observed for valuations on number fields:: From 8aed383f9ff4804e9bd886e06edd0002f4d5a031 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 1 Jul 2017 20:53:07 -0500 Subject: [PATCH 432/740] Implementing soliton cellular automata using rigged configurations. --- .../reference/dynamics/cellular_automata.rst | 8 + src/doc/en/reference/dynamics/index.rst | 5 +- src/doc/en/reference/references/index.rst | 8 + src/sage/dynamics/all.py | 1 + .../dynamics/cellular_automata/__init__.py | 3 + src/sage/dynamics/cellular_automata/all.py | 3 + .../dynamics/cellular_automata/solitons.py | 1156 +++++++++++++++++ 7 files changed, 1182 insertions(+), 2 deletions(-) create mode 100644 src/doc/en/reference/dynamics/cellular_automata.rst create mode 100644 src/sage/dynamics/cellular_automata/__init__.py create mode 100644 src/sage/dynamics/cellular_automata/all.py create mode 100644 src/sage/dynamics/cellular_automata/solitons.py diff --git a/src/doc/en/reference/dynamics/cellular_automata.rst b/src/doc/en/reference/dynamics/cellular_automata.rst new file mode 100644 index 00000000000..8aeefcf2947 --- /dev/null +++ b/src/doc/en/reference/dynamics/cellular_automata.rst @@ -0,0 +1,8 @@ +Cellular Automata +================= + +.. toctree:: + :maxdepth: 2 + + ../sage/dynamics/cellular_automata/solitons + diff --git a/src/doc/en/reference/dynamics/index.rst b/src/doc/en/reference/dynamics/index.rst index 31ea9ffede6..c79ca52eae8 100644 --- a/src/doc/en/reference/dynamics/index.rst +++ b/src/doc/en/reference/dynamics/index.rst @@ -6,9 +6,10 @@ Discrete dynamics .. toctree:: :maxdepth: 2 - interval_exchanges - flat_surfaces + cellular_automata complex_dynamics + flat_surfaces + interval_exchanges sage/sandpiles/sandpile .. SEEALSO:: diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 5c4bdf55a3e..6d282b175a7 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1583,6 +1583,10 @@ REFERENCES: group*. Mathematische Zeitschrift 264(4) (2010) 765-811 (:arxiv:`0710.2720`) +.. [LS2017] Xuan Liu and Travis Scrimshaw. *A uniform approach to soliton + cellular automata using rigged configurations*. + Preprint (2017) :arxiv:`1706.02443` + .. [LT1998] \B. Leclerc, J.-Y. Thibon, Littlewood-Richardson coefficients and Kazhdan-Lusztig polynomials, http://front.math.ucdavis.edu/9809.5122 @@ -2359,6 +2363,10 @@ REFERENCES: **Y** +.. [Yamada2007] Daisuke Yamada. *Scattering rule in soliton cellular automaton + associated with crystal base of* `U_q(D_4^{(3)})`. + \J. Math. Phys., **48** (4):043509, 28, (2007). + .. [Yoc2005] Jean-Christophe Yoccoz "Echange d'Intervalles", Cours au college de France diff --git a/src/sage/dynamics/all.py b/src/sage/dynamics/all.py index bee180cf0d1..e84f68ee18b 100644 --- a/src/sage/dynamics/all.py +++ b/src/sage/dynamics/all.py @@ -2,3 +2,4 @@ from sage.dynamics.flat_surfaces.all import * from sage.dynamics.arithmetic_dynamics.all import * from sage.dynamics.complex_dynamics.all import * +from sage.dynamics.cellular_automata.all import * diff --git a/src/sage/dynamics/cellular_automata/__init__.py b/src/sage/dynamics/cellular_automata/__init__.py new file mode 100644 index 00000000000..bd469e79134 --- /dev/null +++ b/src/sage/dynamics/cellular_automata/__init__.py @@ -0,0 +1,3 @@ +r""" +Cellular Automata +""" diff --git a/src/sage/dynamics/cellular_automata/all.py b/src/sage/dynamics/cellular_automata/all.py new file mode 100644 index 00000000000..d0a32e749dc --- /dev/null +++ b/src/sage/dynamics/cellular_automata/all.py @@ -0,0 +1,3 @@ +from sage.misc.lazy_import import lazy_import +lazy_import("sage.dynamics.cellular_automata.solitons", + ["SolitonCellularAutomata"]) diff --git a/src/sage/dynamics/cellular_automata/solitons.py b/src/sage/dynamics/cellular_automata/solitons.py new file mode 100644 index 00000000000..c3c34d0dca5 --- /dev/null +++ b/src/sage/dynamics/cellular_automata/solitons.py @@ -0,0 +1,1156 @@ +""" +Soliton Cellular Automata + +AUTHORS: + +- Travis Scrimshaw (2017-06-30): Initial version +""" + +#***************************************************************************** +# Copyright (C) 2017 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.structure.sage_object import SageObject +from sage.combinat.rigged_configurations.tensor_product_kr_tableaux import TensorProductOfKirillovReshetikhinTableaux +from sage.combinat.rigged_configurations.kr_tableaux import KirillovReshetikhinTableaux +from sage.combinat.rigged_configurations.rigged_configurations import RiggedConfigurations +from sage.combinat.root_system.cartan_type import CartanType +from sage.typeset.ascii_art import ascii_art +from sage.rings.integer_ring import ZZ + +class SolitonCellularAutomata(SageObject): + r""" + Soliton cellular automata. + + Fix some an affine Lie algebra `\mathfrak{g}` with index `I` and + classical index set `I_0`. Fix some `r \in I_0`. A *soliton + cellular automaton* (SCA) is a discrete (non-linear) dynamical + system is given as follows. The *states* are given by elements of + a semi-infinite tensor product of Kirillov-Reshetihkin crystals + `B^{r,1}`, where only a finite number of factors are not the + maximal element `u`, which we will call the *vacuum*. The *time + evolution* `T_s` is defined by + + .. MATH:: + + R(p \otimes u_s) = u_s \otimes T_s(p), + + where `p = \cdots \otimes p_3 \otimes p_2 \otimes p_1 \otimes _0` + is a state and `u_s` is the maximal element of `B^{r,s}`. + In more detail, we have `R(p_i \otimes u^{(i)}) = + u^{(i+1)} \otimes \widetilde{p}_i` with u^{(0)} = u_s` and + `T_s(p) = \cdots \otimes \widetilde{p}_1 \otimes \widetilde{p}_0`. + This is well-defined since `R(u \otimes u_s) = u_s \otimes u` + and `u^{(k)} = u_s` for all `k \gg 1`. + + INPUT: + + - ``initial_state`` -- the list of elements, can also be a string + when ``vacuum`` is 1 and ``n`` is `\mathfrak{sl}_n` + - ``cartan_type`` -- (default: 2) the value `sl_n` or a Cartan type + - ``r`` -- (default: 1) the node index `r`; typically this + corresponds to the height of the vacuum element + + EXAMPLES: + + We first create an example in `\mathfrak{sl}_4` (type `A_3`):: + + sage: B = SolitonCellularAutomata('3411111122411112223', 4) + sage: B + Soliton cellular automata of type ['A', 3, 1] and vacuum = 1 + initial state: + 34......224....2223 + evoltuions: [] + current state: + 34......224....2223 + + We then apply an standard evolution:: + + sage: B.evolve() + sage: B + Soliton cellular automata of type ['A', 3, 1] and vacuum = 1 + initial state: + 34......224....2223 + evoltuions: [(1, 19)] + current state: + .................34.....224...2223.... + + Next, we apply a smaller carrier evolution. Note that the soliton + of size 4 moves only 3 steps:: + + sage: B.evolve(3) + sage: B + Soliton cellular automata of type ['A', 3, 1] and vacuum = 1 + initial state: + 34......224....2223 + evoltuions: [(1, 19), (1, 3)] + current state: + ...............34....224...2223....... + + We can also use carriers corresponding to non-vacuum indices. + In these cases, the carrier might not return to its initial + state, which results in a message being displayed about + the resulting state of the carrier:: + + sage: B.evolve(carrier_capacity=7, carrier_index=3) + Last carrier: + 1 1 1 1 1 1 1 + 2 2 2 2 2 3 3 + 3 3 3 3 3 4 4 + sage: B + Soliton cellular automata of type ['A', 3, 1] and vacuum = 1 + initial state: + 34......224....2223 + evoltuions: [(1, 19), (1, 3), (3, 7)] + current state: + .....................23....222....2223....... + + sage: B.evolve(carrier_capacity=3, carrier_index=2) + Last carrier: + 1 1 1 + 2 2 3 + sage: B + Soliton cellular automata of type ['A', 3, 1] and vacuum = 1 + initial state: + 34......224....2223 + evoltuions: [(1, 19), (1, 3), (3, 7), (2, 3)] + current state: + .......................22.....223...2222........ + + To summarize our current evolutions, we can use :meth:`print_states`:: + + sage: B.print_states(5) + t: 0 + .............................34......224....2223 + t: 1 + ...........................34.....224...2223.... + t: 2 + .........................34....224...2223....... + t: 3 + ........................23....222....2223....... + t: 4 + .......................22.....223...2222........ + + To run the SCA further under the standard evolutions, one can use + :meth:`print_states` or :meth:`latex_states`:: + + sage: B.print_states(15) + t: 0 + ................................................34......224....2223 + t: 1 + ..............................................34.....224...2223.... + t: 2 + ............................................34....224...2223....... + t: 3 + ...........................................23....222....2223....... + t: 4 + ..........................................22.....223...2222........ + t: 5 + ........................................22....223..2222............ + t: 6 + ......................................22..2223..222................ + t: 7 + ..................................2222..23...222................... + t: 8 + ..............................2222....23..222...................... + t: 9 + ..........................2222......23.222......................... + t: 10 + ......................2222.......223.22............................ + t: 11 + ..................2222........223..22.............................. + t: 12 + ..............2222.........223...22................................ + t: 13 + ..........2222..........223....22.................................. + t: 14 + ......2222...........223.....22.................................... + + Next, we use `r = 2` in type `A_3`. Here, we give the data as lists of + values corresponding to the entries of the column of height 2 from + the largest entry to smallest. Our columns are drawn in French + convention:: + + sage: B = SolitonCellularAutomata([[4,1],[4,1],[2,1],[2,1],[2,1],[2,1],[3,1],[3,1],[3,2]], 4, 2) + + We perform 3 evolutions and obtain the following:: + + sage: B.evolve(number=3) + sage: B + Soliton cellular automata of type ['A', 3, 1] and vacuum = 2 + initial state: + 44 333 + 11....112 + evoltuions: [(2, 9), (2, 9), (2, 9)] + current state: + 44 333 + ...11.112......... + + We construct Example 2.9 from [LS2017]_:: + + sage: B = SolitonCellularAutomata([[2],[-3],[1],[1],[1],[4],[0],[-2], + ....: [1],[1],[1],[1],[3],[-4],[-3],[-3],[1]], ['D',5,2]) + sage: B.print_states(10) + t: 0 _ _ ___ + ..................................23...402....3433. + t: 1 _ _ ___ + ................................23..402...3433..... + t: 2 _ _ ___ + ..............................23.402..3433......... + t: 3 _ _ ___ + ...........................243.02.3433............. + t: 4 _ __ __ + .......................2403..42333................. + t: 5 _ ___ _ + ...................2403...44243.................... + t: 6 _ ___ _ + ...............2403....442.43...................... + t: 7 _ ___ _ + ...........2403.....442..43........................ + t: 8 _ ___ _ + .......2403......442...43.......................... + t: 9 _ ___ _ + ...2403.......442....43............................ + + Example 3.12 from [LS2017]_:: + + sage: B = SolitonCellularAutomata([[-1,3,2],[3,2,1],[3,2,1],[-3,2,1], + ....: [-2,-3,1]], ['B',3,1], 3) + sage: B.print_states(6) + -1 -3-2 + t: 0 3 2-3 + . . . . . . . . . . . . . . . 2 . . 1 1 + -1-3-2 + t: 1 3 2-3 + . . . . . . . . . . . . . . 2 1 1 . . . + -3-1 + t: 2 2-2 + . . . . . . . . . . . . 1-3 . . . . . . + -3-1 -3 + t: 3 2-2 2 + . . . . . . . . . 1 3 . 1 . . . . . . . + -3-1 -3 + t: 4 2-2 2 + . . . . . . 1 3 . . . 1 . . . . . . . . + -3-1 -3 + t: 5 2-2 2 + . . . 1 3 . . . . . 1 . . . . . . . . . + + Example 4.12 from [LS2017]_:: + + sage: K = crystals.KirillovReshetikhin(['E',6,1], 1,1, 'KR') + sage: u = K.module_generators[0] + sage: x = u.f_string([1,3,4,5]) + sage: y = u.f_string([1,3,4,2,5,6]) + sage: a = u.f_string([1,3,4,2]) + sage: B = SolitonCellularAutomata([a, u,u,u, x,y], ['E',6,1], 1) + sage: B + Soliton cellular automata of type ['E', 6, 1] and vacuum = 1 + initial state: + (-2, 5) . . . (-5, 2, 6)(-2, -6, 4) + evoltuions: [] + current state: + (-2, 5) . . . (-5, 2, 6)(-2, -6, 4) + sage: B.print_states(8) + t: 0 ... + t: 7 + . (-2, 5)(-2, -5, 4, 6) ... (-6, 2) ... + """ + def __init__(self, initial_state, cartan_type=2, vacuum=1): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: B = SolitonCellularAutomata('3411111122411112223', 4) + sage: TestSuite(B).run() + """ + if cartan_type in ZZ: + cartan_type = CartanType(['A',cartan_type-1,1]) + else: + cartan_type = CartanType(cartan_type) + self._cartan_type = cartan_type + self._vacuum = vacuum + K = KirillovReshetikhinTableaux(self._cartan_type, self._vacuum, 1) + try: + # FIXME: the maximal_vector() does not work in type E and F + self._vacuum_elt = K.maximal_vector() + except (ValueError, TypeError, AttributeError): + self._vacuum_elt = K.module_generators[0] + + if isinstance(initial_state, str): + # We consider things 1-9 + initial_state = [[ZZ(x) if x != '.' else ZZ.one()] for x in initial_state] + try: + KRT = TensorProductOfKirillovReshetikhinTableaux(self._cartan_type, + [[vacuum, len(st)//vacuum] + for st in initial_state]) + self._states = [KRT(pathlist=initial_state)] + except TypeError: + KRT = TensorProductOfKirillovReshetikhinTableaux(self._cartan_type, + [[vacuum, 1] + for st in initial_state]) + self._states = [KRT(*initial_state)] + + self._evolutions = [] + self._nballs = len(self._states[0]) + + def __eq__(self, other): + """ + Check equality. + + Two SCAs are equal when they have the same initial state + and evolutions. + + TESTS:: + + sage: B1 = SolitonCellularAutomata('34112223', 4) + sage: B2 = SolitonCellularAutomata('34112223', 4) + sage: B1 == B2 + True + sage: B1.evolve() + sage: B1 == B2 + False + sage: B2.evolve() + sage: B1 == B2 + True + sage: B1.evolve(5) + sage: B2.evolve(6) + sage: B1 == B2 + False + """ + return (isinstance(other, SolitonCellularAutomata) + and self._states[0] == other._states[0] + and self._evolutions == other._evolutions) + + def __ne__(self, other): + """ + Check non equality. + + TESTS:: + + sage: B1 = SolitonCellularAutomata('34112223', 4) + sage: B2 = SolitonCellularAutomata('34112223', 4) + sage: B1 != B2 + False + sage: B1.evolve() + sage: B1 != B2 + True + sage: B2.evolve() + sage: B1 != B2 + False + sage: B1.evolve(5) + sage: B2.evolve(6) + sage: B1 != B2 + True + """ + return not (self == other) + + # Evolution functions + # ------------------- + + def evolve(self, carrier_capacity=None, carrier_index=None, number=None): + """ + Evolve ``self``. + + Time evolution `T_s` of a SCA state `p` is determined by + + .. MATH:: + + u_{r,s} \otimes T_s(p) = R(p \otimes u_{r,s}), + + where `u_{r,s}` is the maximal element of `B^{r,s}`. + + INPUT: + + - ``carrier_capacity`` -- (default: the number of balls in + the system) the size `s` of carrier + + - ``carrier_index`` -- (default: the vacuum index) the index `r` + of the carrier + + - ``number`` -- (optional) the number of times to perform + the evolutions + + To perform multiple evolutions of the SCA, ``carrier_capacity`` + and ``carrier_index`` may be lists of the same length. + + EXAMPLES:: + + sage: B = SolitonCellularAutomata('3411111122411112223', 4) + sage: for k in range(10): + ....: B.evolve() + sage: B + Soliton cellular automata of type ['A', 3, 1] and vacuum = 1 + initial state: + 34......224....2223 + evoltuions: [(1, 19), (1, 19), (1, 19), (1, 19), (1, 19), + (1, 19), (1, 19), (1, 19), (1, 19), (1, 19)] + current state: + ......2344.......222....23............................... + + sage: B.reset() + sage: B.evolve(number=10); B + Soliton cellular automata of type ['A', 3, 1] and vacuum = 1 + initial state: + 34......224....2223 + evoltuions: [(1, 19), (1, 19), (1, 19), (1, 19), (1, 19), + (1, 19), (1, 19), (1, 19), (1, 19), (1, 19)] + current state: + ......2344.......222....23............................... + + sage: B.reset() + sage: B.evolve(carrier_capacity=[1,2,3,4,5,6,7,8,9,10]); B + Soliton cellular automata of type ['A', 3, 1] and vacuum = 1 + initial state: + 34......224....2223 + evoltuions: [(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), + (1, 6), (1, 7), (1, 8), (1, 9), (1, 10)] + current state: + ........2344....222..23.............................. + + sage: B.reset() + sage: B.evolve(carrier_index=[1,2,3]) + Last carrier: + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 4 4 + sage: B + Soliton cellular automata of type ['A', 3, 1] and vacuum = 1 + initial state: + 34......224....2223 + evoltuions: [(1, 19), (2, 19), (3, 19)] + current state: + ..................................22......223...2222..... + + sage: B.reset() + sage: B.evolve(carrier_capacity=[1,2,3], carrier_index=[1,2,3]) + Last carrier: + 1 1 + 3 4 + Last carrier: + 1 1 1 + 2 2 3 + 3 3 4 + sage: B + Soliton cellular automata of type ['A', 3, 1] and vacuum = 1 + initial state: + 34......224....2223 + evoltuions: [(1, 1), (2, 2), (3, 3)] + current state: + .....22.......223....2222.. + + sage: B.reset() + sage: B.evolve(1, 2, number=3) + Last carrier: + 1 + 3 + Last carrier: + 1 + 4 + Last carrier: + 1 + 3 + sage: B + Soliton cellular automata of type ['A', 3, 1] and vacuum = 1 + initial state: + 34......224....2223 + evoltuions: [(2, 1), (2, 1), (2, 1)] + current state: + .24......222.....2222. + """ + if isinstance(carrier_capacity, (list, tuple)): + if not isinstance(carrier_index, (list, tuple)): + carrier_index = [carrier_index] * len(carrier_capacity) + if len(carrier_index) != len(carrier_capacity): + raise ValueError("carrier_index and carrier_capacity" + " must have the same length") + for i,r in zip(carrier_capacity, carrier_index): + self.evolve(i, r) + return + if isinstance(carrier_index, (list, tuple)): + # carrier_capacity must be not be a list/tuple if given + for r in carrier_index: + self.evolve(carrier_capacity, r) + return + + if carrier_capacity is None: + carrier_capacity = self._nballs + if carrier_index is None: + carrier_index = self._vacuum + + if number is not None: + for k in range(number): + self.evolve(carrier_capacity, carrier_index) + return + + passed = False + K = KirillovReshetikhinTableaux(self._cartan_type, carrier_index, carrier_capacity) + try: + # FIXME: the maximal_vector() does not work in type E and F + empty_carrier = K.maximal_vector() + except (ValueError, TypeError, AttributeError): + empty_carrier = K.module_generators[0] + carrier_factor = (carrier_index, carrier_capacity) + last_final_carrier = empty_carrier + state = self._states[-1] + dims = state.parent().dims + while not passed: + KRT = TensorProductOfKirillovReshetikhinTableaux(self._cartan_type, + dims + (carrier_factor,)) + elt = KRT(*(list(state) + [empty_carrier])) + RC = RiggedConfigurations(self._cartan_type, (carrier_factor,) + dims) + elt2 = RC(*elt.to_rigged_configuration()).to_tensor_product_of_kirillov_reshetikhin_tableaux() + # Back to an empty carrier or we are not getting any better + if elt2[0] == empty_carrier or elt2[0] == last_final_carrier: + passed = True + KRT = TensorProductOfKirillovReshetikhinTableaux(self._cartan_type, dims) + self._states.append(KRT(*elt2[1:])) + self._evolutions.append(carrier_factor) + if elt2[0] != empty_carrier: + print("Last carrier:") + print(ascii_art(last_final_carrier)) + else: + # We need to add more vacuum states + last_final_carrier = elt2[0] + dims = tuple([(self._vacuum, 1)]*carrier_capacity) + dims + + def state_evolution(self, num): + """ + Return a list of the carrier values at state ``num`` evolving to + the next state. + + If ``num`` is greater than the number of states, this performs + the standard evolution `T_k`, where `k` is the number of balls + in the system. + + .. SEEALSO:: + + :meth:`print_state_evolution`, :meth:`latex_state_evolution` + + EXAMPLES:: + + sage: B = SolitonCellularAutomata('1113123', 3) + sage: B.evolve(3) + sage: B.state_evolution(0) + [[[1, 1, 1]], + [[1, 1, 1]], + [[1, 1, 1]], + [[1, 1, 3]], + [[1, 1, 2]], + [[1, 2, 3]], + [[1, 1, 3]], + [[1, 1, 1]]] + sage: B.state_evolution(2) + [[[1, 1, 1, 1, 1, 1, 1]], + [[1, 1, 1, 1, 1, 1, 1]], + [[1, 1, 1, 1, 1, 1, 1]], + [[1, 1, 1, 1, 1, 1, 1]], + [[1, 1, 1, 1, 1, 1, 1]], + [[1, 1, 1, 1, 1, 1, 1]], + [[1, 1, 1, 1, 1, 1, 3]], + [[1, 1, 1, 1, 1, 3, 3]], + [[1, 1, 1, 1, 1, 1, 3]], + [[1, 1, 1, 1, 1, 1, 2]], + [[1, 1, 1, 1, 1, 1, 1]], + [[1, 1, 1, 1, 1, 1, 1]], + [[1, 1, 1, 1, 1, 1, 1]], + [[1, 1, 1, 1, 1, 1, 1]], + [[1, 1, 1, 1, 1, 1, 1]]] + """ + if num + 2 > len(self._states): + for _ in range(num + 2 - len(self._states)): + self.evolve() + + carrier = KirillovReshetikhinTableaux(self._cartan_type, *self._evolutions[num]) + num_factors = len(self._states[num+1]) + vacuum = self._vacuum_elt + state = [vacuum]*(num_factors - len(self._states[num])) + list(self._states[num]) + final = [] + try: + # FIXME: the maximal_vector() does not work in type E and F + u = [carrier.maximal_vector()] + except (ValueError, TypeError, AttributeError): + u = [carrier.module_generators[0]] + # Assume every element has the same parent + R = state[0].parent().R_matrix(carrier) + for elt in reversed(state): + up, eltp = R(R.domain()(elt, u[0])) + u.insert(0, up) + final.insert(0, eltp) + return u + + def reset(self): + r""" + Reset ``self`` back to the initial state. + + EXAMPLES:: + + sage: B = SolitonCellularAutomata('34111111224', 4) + sage: B + Soliton cellular automata of type ['A', 3, 1] and vacuum = 1 + initial state: + 34......224 + evoltuions: [] + current state: + 34......224 + sage: B.evolve() + sage: B.evolve() + sage: B.evolve() + sage: B.evolve() + sage: B + Soliton cellular automata of type ['A', 3, 1] and vacuum = 1 + initial state: + 34......224 + evoltuions: [(1, 11), (1, 11), (1, 11), (1, 11)] + current state: + ...34..224............ + sage: B.reset() + sage: B + Soliton cellular automata of type ['A', 3, 1] and vacuum = 1 + initial state: + 34......224 + evoltuions: [] + current state: + 34......224 + """ + self._states = [self._states[0]] + self._evolutions = [] + + # Output functions + # ---------------- + + def _column_repr(self, b, vacuum_letter=None): + """ + Return a string representation of the column ``b``. + + EXAMPLES:: + + sage: B = SolitonCellularAutomata([[-2,1],[2,1],[-3,1],[-3,2]], ['D',4,2], 2) + sage: K = crystals.KirillovReshetikhin(['D',4,2], 2,1, 'KR') + sage: B._column_repr(K(-2,1)) + -2 + 1 + sage: B._column_repr(K.module_generator()) + 2 + 1 + sage: B._column_repr(K.module_generator(), 'x') + x + """ + if vacuum_letter is not None and b == self._vacuum_elt: + return ascii_art(vacuum_letter) + if self._vacuum_elt.parent()._tableau_height == 1: + s = str(b[0]) + return ascii_art(s if s[0] != '-' else '_\n' + s[1:]) + letter_str = [str(letter) for letter in b] + max_width = max(len(s) for s in letter_str) + return ascii_art('\n'.join(' '*(max_width-len(s)) + s for s in letter_str)) + + def _repr_state(self, state, vacuum_letter='.'): + """ + Return a string representation of ``state``. + + EXAMPLES:: + + sage: B = SolitonCellularAutomata('3411111122411112223', 4) + sage: B.evolve(number=10) + sage: print(B._repr_state(B._states[0])) + 34......224....2223 + sage: print(B._repr_state(B._states[-1], '_')) + ______2344_______222____23_______________________________ + """ + output = [self._column_repr(b, vacuum_letter) for b in state] + max_width = max(cell.width() for cell in output) + return sum((ascii_art(' '*(max_width-b.width())) + b for b in output), + ascii_art('')) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: SolitonCellularAutomata('3411111122411112223', 4) + Soliton cellular automata of type ['A', 3, 1] and vacuum = 1 + initial state: + 34......224....2223 + evoltuions: [] + current state: + 34......224....2223 + sage: SolitonCellularAutomata([[4,1],[2,1],[2,1],[3,1],[3,2]], 4, 2) + Soliton cellular automata of type ['A', 3, 1] and vacuum = 2 + initial state: + 4 33 + 1..12 + evoltuions: [] + current state: + 4 33 + 1..12 + sage: SolitonCellularAutomata([[4,1],[2,1],[2,1],[3,1],[3,2]], ['C',4,1], 2) + Soliton cellular automata of type ['C', 4, 1] and vacuum = 2 + initial state: + 4 33 + 1..12 + evoltuions: [] + current state: + 4 33 + 1..12 + sage: SolitonCellularAutomata([[4,3],[2,1],[-3,1],[-3,2]], ['B',4,1], 2) + Soliton cellular automata of type ['B', 4, 1] and vacuum = 2 + initial state: + 4 -3-3 + 3 . 1 2 + evoltuions: [] + current state: + 4 -3-3 + 3 . 1 2 + """ + ret = "Soliton cellular automata of type {} and vacuum = {}\n".format(self._cartan_type, self._vacuum) + ret += " initial state:\n{}\n evoltuions: {}\n current state:\n{}".format( + ascii_art(' ') + self._repr_state(self._states[0]), + self._evolutions, + ascii_art(' ') + self._repr_state(self._states[-1]) + ) + return ret + + def print_state(self, num=None, vacuum_letter='.', remove_trailing_vacuums=False): + """ + Print the state ``num``. + + INPUT: + + - ``num`` -- (default: the current state) the state to print + - ``vacuum_letter`` -- (default: ``'.'``) the letter to print + for the vacuum + - ``remove_trailing_vacuums`` -- (default: ``False``) if ``True`` + then this does not print the vacuum letters at the right end + of the state + + EXAMPLES:: + + sage: B = SolitonCellularAutomata('3411111122411112223', 4) + sage: B.print_state() + 34......224....2223 + sage: B.evolve(number=2) + sage: B.print_state(vacuum_letter=',') + ,,,,,,,,,,,,,,,34,,,,224,,2223,,,,,,,, + sage: B.print_state(10, '_') + ______2344_______222____23_______________________________ + sage: B.print_state(10, '_', True) + ______2344_______222____23 + """ + if num is None: + num = len(self._states) - 1 + if num + 1 > len(self._states): + for _ in range(num + 1 - len(self._states)): + self.evolve() + state = self._states[num] + if remove_trailing_vacuums: + pos = len(state) - 1 + # The pos goes negative if and only if the state consists + # entirely of vacuum elements. + while pos >= 0 and state[pos] == self._vacuum_elt: + pos -= 1 + state = state[:pos+1] + print(self._repr_state(state, vacuum_letter)) + + def print_states(self, num=None, vacuum_letter='.'): + r""" + Print the first ``num`` states of ``self``. + + .. NOTE:: + + If the number of states computed for ``self`` is less than + ``num``, then this evolves the system using the default + time evolution. + + INPUT: + + - ``num`` -- the number of states to print + + EXAMPLES:: + + sage: B = SolitonCellularAutomata([[2],[-1],[1],[1],[1],[1],[2],[2],[3], + ....: [-2],[1],[1],[2],[-1],[1],[1],[1],[1],[1],[1],[2],[3],[3],[-3],[-2]], + ....: ['C',3,1]) + sage: B.print_states(7) + t: 0 _ _ _ __ + .........................21....2232..21......23332 + t: 1 _ _ _ __ + ......................21...2232...21....23332..... + t: 2 _ _ _ __ + ...................21..2232....21..23332.......... + t: 3 _ _ _ __ + ...............221..232...2231..332............... + t: 4 _ _ _ __ + ...........221...232.2231....332.................. + t: 5 _ __ __ + .......221...2321223......332..................... + t: 6 _ __ __ + ..2221...321..223......332........................ + + sage: B = SolitonCellularAutomata([[2],[1],[1],[1],[3],[-2],[1],[1], + ....: [1],[2],[2],[-3],[1],[1],[1],[1],[1],[1],[2],[3],[3],[-3]], + ....: ['B',3,1]) + sage: B.print_states(9, ' ') + t: 0 _ _ _ + 2 32 223 2333 + t: 1 _ _ _ + 2 32 223 2333 + t: 2 _ _ _ + 2 32 223 2333 + t: 3 _ _ _ + 23 2223 2333 + t: 4 __ _ + 23 213 2333 + t: 5 _ _ _ + 2233 222 333 + t: 6 _ _ _ + 2233 23223 3 + t: 7 _ _ _ + 2233 232 23 3 + t: 8 _ _ _ + 2233 232 23 3 + + sage: B = SolitonCellularAutomata([[2],[-2],[1],[1],[1],[1],[2],[0],[-3], + ....: [1],[1],[1],[1],[1],[2],[2],[3],[-3],], ['D',4,2]) + sage: B.print_states(10) + t: 0 _ _ _ + ....................................22....203.....2233 + t: 1 _ _ _ + ..................................22...203....2233.... + t: 2 _ _ _ + ................................22..203...2233........ + t: 3 _ _ _ + ..............................22.203..2233............ + t: 4 _ _ _ + ............................22203.2233................ + t: 5 _ _ _ + ........................220223.233.................... + t: 6 _ _ _ + ....................2202.223.33....................... + t: 7 _ _ _ + ................2202..223..33......................... + t: 8 _ _ _ + ............2202...223...33........................... + t: 9 _ _ _ + ........2202....223....33............................. + + Example 4.13 from [Yamada2007]_:: + + sage: B = SolitonCellularAutomata([[3],[3],[1],[1],[1],[1],[2],[2],[2]], ['D',4,3]) + sage: B.print_states(15) + t: 0 + ....................................33....222 + t: 1 + ..................................33...222... + t: 2 + ................................33..222...... + t: 3 + ..............................33.222......... + t: 4 + ............................33222............ + t: 5 + ..........................3022............... + t: 6 _ + ........................332.................. + t: 7 _ + ......................03..................... + t: 8 _ + ....................3E....................... + t: 9 _ + .................21.......................... + t: 10 + ..............20E............................ + t: 11 _ + ...........233............................... + t: 12 + ........2302................................. + t: 13 + .....23322................................... + t: 14 + ..233.22..................................... + + Example 4.14 from [Yamada2007]_:: + + sage: B = SolitonCellularAutomata([[3],[1],[1],[1],[2],[3],[1],[1],[1],[2],[3],[3]], ['D',4,3]) + sage: B.print_states(15) + t: 0 + ....................................3...23...233 + t: 1 + ...................................3..23..233... + t: 2 + ..................................3.23.233...... + t: 3 + .................................323233......... + t: 4 + ................................0033............ + t: 5 _ + ..............................313............... + t: 6 + ...........................30E.3................ + t: 7 _ + ........................333...3................. + t: 8 + .....................3302....3.................. + t: 9 + ..................33322.....3................... + t: 10 + ...............333.22......3.................... + t: 11 + ............333..22.......3..................... + t: 12 + .........333...22........3...................... + t: 13 + ......333....22.........3....................... + t: 14 + ...333.....22..........3........................ + """ + if num is None: + num = len(self._states) + if num > len(self._states): + for _ in range(num - len(self._states)): + self.evolve() + + vacuum = self._vacuum_elt + num_factors = len(self._states[num-1]) + for i,state in enumerate(self._states[:num]): + state = [vacuum]*(num_factors - len(state)) + list(state) + output = [self._column_repr(b, vacuum_letter) for b in state] + max_width = max(b.width() for b in output) + start = ascii_art("t: %s \n"%i) + start._baseline = -1 + print(start + + sum((ascii_art(' '*(max_width-b.width())) + b for b in output), + ascii_art(''))) + + def latex_states(self, num=None, as_array=True, box_width='5pt'): + r""" + Return a latex verion of the states. + + INPUT: + + - ``num`` -- the number of states + - ``as_array`` (default: ``True``) if ``True``, then the states are + placed inside of an array; if ``False``, then the states are + given as a word + - ``box_width`` -- (default: ``'5pt'``) the width of the ``.`` used + to represent the vacuum state when ``as_array`` is ``True`` + + If ``as_array`` is ``False``, then the vacuum element is printed + in a gray color. If ``as_array`` is ``True``, then the vacuum + is given as ``.`` + + Use the ``box_width`` to help create more even spacing when + a column in the output contains only vacuum elements. + + EXAMPLES:: + + sage: B = SolitonCellularAutomata('411122', 4) + sage: B.latex_states(8) + {\arraycolsep=0.5pt \begin{array}{c|ccccccccccccccccccc} + t = 0 & \cdots & ... & \makebox[5pt]{.} & 4 & \makebox[5pt]{.} + & \makebox[5pt]{.} & \makebox[5pt]{.} & 2 & 2 \\ + t = 1 & \cdots & ... & 4 & \makebox[5pt]{.} & \makebox[5pt]{.} & 2 & 2 & ... \\ + t = 2 & \cdots & ... & 4 & \makebox[5pt]{.} & 2 & 2 & ... \\ + t = 3 & \cdots & ... & 4 & 2 & 2 & ... \\ + t = 4 & \cdots & ... & 2 & 4 & 2 & ... \\ + t = 5 & \cdots & ... & 2 & 4 & \makebox[5pt]{.} & 2 & ... \\ + t = 6 & \cdots & ... & 2 & 4 & \makebox[5pt]{.} & \makebox[5pt]{.} + & 2 & ... \\ + t = 7 & \cdots & \makebox[5pt]{.} & 2 & 4 & \makebox[5pt]{.} + & \makebox[5pt]{.} & \makebox[5pt]{.} & 2 & ... \\ + \end{array}} + + sage: B = SolitonCellularAutomata('511122', 5) + sage: B.latex_states(8, as_array=False) + {\begin{array}{c|c} + t = 0 & \cdots ... {\color{gray} 1} 5 {\color{gray} 1} + {\color{gray} 1} {\color{gray} 1} 2 2 \\ + t = 1 & \cdots ... 5 {\color{gray} 1} {\color{gray} 1} 2 2 ... \\ + t = 2 & \cdots ... 5 {\color{gray} 1} 2 2 ... \\ + t = 3 & \cdots ... 5 2 2 ... \\ + t = 4 & \cdots ... 2 5 2 ... \\ + t = 5 & \cdots ... 2 5 {\color{gray} 1} 2 ... \\ + t = 6 & \cdots ... 2 5 {\color{gray} 1} {\color{gray} 1} 2 ... \\ + t = 7 & \cdots {\color{gray} 1} 2 5 {\color{gray} 1} + {\color{gray} 1} {\color{gray} 1} 2 ... \\ + \end{array}} + """ + from sage.misc.latex import latex, LatexExpr + if not as_array: + latex.add_package_to_preamble_if_available('xcolor') + + if num is None: + num = len(self._states) + if num > len(self._states): + for _ in range(num - len(self._states)): + self.evolve() + + vacuum = self._vacuum_elt + def compact_repr(b): + if as_array and b == vacuum: + return "\\makebox[%s]{.}"%box_width + + if b.parent()._tableau_height == 1: + temp = latex(b[0]) + else: + temp = "\\begin{array}{@{}c@{}}" # No padding around columns + temp += r"\\".join(latex(letter) for letter in reversed(b)) + temp += "\\end{array}" + + if b == vacuum: + return "{\\color{gray} %s}"%temp + return temp # "\\makebox[%s]{$%s$}"%(box_width, temp) + + num_factors = len(self._states[num-1]) + if as_array: + ret = "{\\arraycolsep=0.5pt \\begin{array}" + ret += "{c|c%s}\n"%('c'*num_factors) + else: + ret = "{\\begin{array}" + ret += "{c|c}\n" + for i,state in enumerate(self._states[:num]): + state = [vacuum]*(num_factors-len(state)) + list(state) + if as_array: + ret += "t = %s & \\cdots & %s \\\\\n"%(i, r" & ".join(compact_repr(b) for b in state)) + else: + ret += "t = %s & \\cdots %s \\\\\n"%(i, r" ".join(compact_repr(b) for b in state)) + ret += "\\end{array}}\n" + return LatexExpr(ret) + + def print_state_evolution(self, num): + r""" + Print the evolution process of the state ``num``. + + .. SEEALSO:: + + :meth:`state_evolution`, :meth:`latex_state_evolution` + + EXAMPLES:: + + sage: B = SolitonCellularAutomata('1113123', 3) + sage: B.evolve(3) + sage: B.evolve(3) + sage: B.print_state_evolution(0) + 1 1 1 3 1 2 3 + | | | | | | | + 111 --+-- 111 --+-- 111 --+-- 113 --+-- 112 --+-- 123 --+-- 113 --+-- 111 + | | | | | | | + 1 1 3 2 3 1 1 + sage: B.print_state_evolution(1) + 1 1 3 2 3 1 1 + | | | | | | | + 111 --+-- 113 --+-- 133 --+-- 123 --+-- 113 --+-- 111 --+-- 111 --+-- 111 + | | | | | | | + 3 3 2 1 1 1 1 + """ + u = self.state_evolution(num) # Also evolves as necessary + final = self._states[num+1] + vacuum = self._vacuum_elt + state = [vacuum]*(len(final) - len(self._states[num])) + list(self._states[num]) + carrier = KirillovReshetikhinTableaux(self._cartan_type, *self._evolutions[num]) + def simple_repr(x): + return ''.join(repr(x).strip('[]').split(', ')) + def carrier_repr(x): + if carrier._tableau_height == 1: + return sum((ascii_art(repr(b)) if repr(b)[0] != '-' + else ascii_art("_" + '\n' + repr(b)[1:]) + for b in x), + ascii_art('')) + return ascii_art(''.join(repr(x).strip('[]').split(', '))) + def cross_repr(i): + ret = ascii_art( +""" +{!s:^7} + | + --+-- + | +{!s:^7} +""".format(simple_repr(state[i]), simple_repr(final[i]))) + ret._baseline = 2 + return ret + art = sum((cross_repr(i) + + carrier_repr(u[i+1]) + for i in range(len(state))), ascii_art('')) + print(ascii_art(carrier_repr(u[0])) + art) + + def latex_state_evolution(self, num, scale=1): + r""" + Return a latex version of the evolution process of + the state ``num``. + + .. SEEALSO:: + + :meth:`state_evolution`, :meth:`print_state_evolution` + + EXAMPLES:: + + sage: B = SolitonCellularAutomata('113123', 3) + sage: B.evolve(3) + sage: B.latex_state_evolution(0) + \begin{tikzpicture}[scale=1] + \node (i0) at (0,0.9) {$1$}; + \node (i1) at (-2,0.9) {$1$}; + \node (i2) at (-4,0.9) {$3$}; + \node (i3) at (-6,0.9) {$1$}; + \node (i4) at (-8,0.9) {$2$}; + \node (i5) at (-10,0.9) {$3$}; + \node (t0) at (0,-1) {$1$}; + \node (t1) at (-2,-1) {$3$}; + \node (t2) at (-4,-1) {$2$}; + \node (t3) at (-6,-1) {$3$}; + \node (t4) at (-8,-1) {$1$}; + \node (t5) at (-10,-1) {$1$}; + \node (u0) at (1,0) {$111$}; + \node (u1) at (-1,0) {$111$}; + \node (u2) at (-3,0) {$113$}; + \node (u3) at (-5,0) {$112$}; + \node (u4) at (-7,0) {$123$}; + \node (u5) at (-9,0) {$113$}; + \node (u6) at (-11,0) {$111$}; + \draw[->] (i0) -- (t0); + \draw[->] (u0) -- (u1); + \draw[->] (i1) -- (t1); + \draw[->] (u1) -- (u2); + \draw[->] (i2) -- (t2); + \draw[->] (u2) -- (u3); + \draw[->] (i3) -- (t3); + \draw[->] (u3) -- (u4); + \draw[->] (i4) -- (t4); + \draw[->] (u4) -- (u5); + \draw[->] (i5) -- (t5); + \draw[->] (u5) -- (u6); + \end{tikzpicture} + sage: B.latex_state_evolution(1) + \begin{tikzpicture}[scale=1] + ... + \end{tikzpicture} + """ + from sage.graphs.graph_latex import setup_latex_preamble + from sage.misc.latex import latex, LatexExpr + setup_latex_preamble() + u = self.state_evolution(num) # Also evolves as necessary + final = self._states[num+1] + vacuum = self._vacuum_elt + initial = [vacuum]*(len(final) - len(self._states[num])) + list(self._states[num]) + def simple_repr(x): + return ''.join(repr(x).strip('[]').split(', ')) + ret = '\\begin{{tikzpicture}}[scale={}]\n'.format(scale) + for i,val in enumerate(initial): + ret += '\\node (i{}) at ({},0.9) {{${}$}};\n'.format(i, -2*i, simple_repr(val)) + for i,val in enumerate(final): + ret += '\\node (t{}) at ({},-1) {{${}$}};\n'.format(i, -2*i, simple_repr(val)) + for i,val in enumerate(u): + ret += '\\node (u{}) at ({},0) {{${}$}};\n'.format(i, -2*i+1, simple_repr(val)) + for i in range(len(initial)): + ret += '\\draw[->] (i{}) -- (t{});\n'.format(i, i) + ret += '\\draw[->] (u{}) -- (u{});\n'.format(i, i+1) + ret += '\\end{tikzpicture}' + return LatexExpr(ret) + From 38f76db03a6cea14bd1531209d7f2f019ce1e1da Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Fri, 24 Nov 2017 11:16:02 +0100 Subject: [PATCH 433/740] No doubling anymore. --- src/sage/quadratic_forms/quadratic_form.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/sage/quadratic_forms/quadratic_form.py b/src/sage/quadratic_forms/quadratic_form.py index b77a202bed8..396bc8b080b 100644 --- a/src/sage/quadratic_forms/quadratic_form.py +++ b/src/sage/quadratic_forms/quadratic_form.py @@ -1174,7 +1174,7 @@ def polynomial(self,names='x'): sage: Q = DiagonalQuadraticForm(QQ,[1, 3, 5, 7]) sage: P = Q.polynomial(); P - 2*x0^2 + 6*x1^2 + 10*x2^2 + 14*x3^2 + x0^2 + 3*x1^2 + 5*x2^2 + 7*x3^2 :: @@ -1182,23 +1182,25 @@ def polynomial(self,names='x'): sage: Z = F.ring_of_integers() sage: Q = QuadraticForm(Z,3,[2*a, 3*a, 0 , 1 - a, 0, 2*a + 4]) sage: P = Q.polynomial(names='y'); P - 4*a*y0^2 + 6*a*y0*y1 + (-2*a + 2)*y1^2 + (4*a + 8)*y2^2 + 2*a*y0^2 + 3*a*y0*y1 + (-a + 1)*y1^2 + (2*a + 4)*y2^2 sage: Q = QuadraticForm(F,4,[a, 3*a, 0, 1 - a, a - 3, 0, 2*a + 4, 4 + a, 0, 1]) sage: Q.polynomial(names='z') - (2*a)*z0^2 + (6*a)*z0*z1 + (2*a - 6)*z1^2 + (2*a + 8)*z2^2 + (-2*a + 2)*z0*z3 + (4*a + 8)*z1*z3 + 2*z3^2 + (a)*z0^2 + (3*a)*z0*z1 + (a - 3)*z1^2 + (a + 4)*z2^2 + (-a + 1)*z0*z3 + (2*a + 4)*z1*z3 + z3^2 sage: B. = QuaternionAlgebra(F,-1,-1) sage: Q = QuadraticForm(B, 3, [2*a, 3*a, i, 1 - a, 0, 2*a + 4]) sage: Q.polynomial() Traceback (most recent call last): ... ValueError: Can only create polynomial rings over commutative rings. - """ - M = self.matrix() - n = self.dim() B = self.base_ring() + n = self.dim() + M = matrix(B, n) + for i in range(n): + for j in range(i, n): + M[i,j] = self[i,j] try: - R = PolynomialRing(self.base_ring(),names,n) + R = PolynomialRing(self.base_ring(), names, n) except Exception: raise ValueError('Can only create polynomial rings over commutative rings.') V = vector(R.gens()) From ae0be0bfb4316f50153452cfacdafebee3782451 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Wed, 29 Nov 2017 22:29:37 +0100 Subject: [PATCH 434/740] Added alternative total ordering key function --- src/sage/modules/free_module.py | 218 +++++++++++++++++++++++++++++++- 1 file changed, 215 insertions(+), 3 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 95b5f7438cb..5d0e516feb2 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -179,7 +179,10 @@ from sage.categories.principal_ideal_domains import PrincipalIdealDomains from sage.misc.randstate import current_randstate from sage.structure.sequence import Sequence -from sage.structure.richcmp import richcmp_method,rich_to_bool,op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE +from sage.structure.richcmp import ( + richcmp_method,rich_to_bool,op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE) +from sage.structure.richcmp import (rich_to_bool, richcmp, + richcmp_not_equal, revop) from sage.misc.cachefunc import cached_method from warnings import warn @@ -1418,7 +1421,7 @@ def _eq(self,other): rx = other.echelonized_basis_matrix() return lx == rx return self.is_submodule(other) and other.is_submodule(self) - + def is_submodule(self, other): """ Return ``True`` if ``self`` is a submodule of ``other``. @@ -4738,7 +4741,106 @@ def _sparse_module(self): True """ return FreeModule(base_ring=self.base_ring(), rank = self.rank(), sparse=True) - + + def _total_ordering_cmp(self, other, op): + r""" + Compare the free module self with other. + + Modules are ordered by their ambient spaces, then by dimension, + then in order by their echelon matrices. However, if + other is a sub-module or is a quotient module then its + comparison method is used instead of generic comparison. + + EXAMPLES: + + We compare rank three free modules over the integers and + rationals:: + + sage: from sage.structure.richcmp import op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE + sage: (QQ^3)._total_ordering_cmp(CC^3, op_LT) + True + sage: (CC^3)._total_ordering_cmp(QQ^3, op_LT) + False + sage: (CC^3)._total_ordering_cmp(QQ^3, op_GT) + True + + :: + + sage: from sage.structure.richcmp import op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE + sage: Q = QQ; Z = ZZ + sage: (Q^3)._total_ordering_cmp(Z^3, op_GT) + True + sage: (Q^3)._total_ordering_cmp(Z^3, op_LT) + False + sage: (Z^3)._total_ordering_cmp(Q^3, op_LT) + True + sage: (Z^3)._total_ordering_cmp(Q^3, op_GT) + False + sage: (Q^3)._total_ordering_cmp(Z^3, op_EQ) + False + sage: (Q^3)._total_ordering_cmp(Q^3, op_EQ) + True + + Comparison with a sub-module:: + + sage: from sage.structure.richcmp import op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE + sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) + sage: V + Vector space of degree 3 and dimension 2 over Rational Field + Basis matrix: + [ 1 0 -1] + [ 0 1 2] + sage: A = QQ^3 + sage: V._total_ordering_cmp(A, op_LT) + True + sage: A._total_ordering_cmp(V, op_LT) + False + + Comparison with a quotient module (see :trac:`10513`):: + + sage: from sage.structure.richcmp import op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE + sage: M = QQ^3 / [[1,2,3]] + sage: V = QQ^2 + sage: V._total_ordering_cmp(M, op_EQ) + False + sage: M._total_ordering_cmp(V, op_EQ) + False + """ + if self is other: + return rich_to_bool(op, 0) + if not isinstance(other, FreeModule_generic): + return NotImplemented + from sage.modules.quotient_module import FreeModule_ambient_field_quotient + if isinstance(other, FreeModule_ambient) and not isinstance(other, FreeModule_ambient_field_quotient): + lx = self.rank() + rx = other.rank() + if lx != rx: + return richcmp_not_equal(lx, rx, op) + lx = self.base_ring() + rx = other.base_ring() + if lx == rx: + #We do not want to create an inner product matrix in memory if + #self and other use the dot product + if self._inner_product_is_dot_product() and other._inner_product_is_dot_product(): + return rich_to_bool(op, 0) + else: + #this only affects free_quadratic_modules + lx = self.inner_product_matrix() + rx = other.inner_product_matrix() + return richcmp(lx,rx,op) + try: + if self.base_ring().is_subring(other.base_ring()): + return rich_to_bool(op, -1) + elif other.base_ring().is_subring(self.base_ring()): + return rich_to_bool(op, 1) + except NotImplementedError: + pass + return richcmp_not_equal(lx, rx, op) + else: + # now other is not ambient or is a quotient; + # it knows how to do the comparison. + return other._total_ordering_cmp( self, revop(op)) + def echelonized_basis_matrix(self): """ The echelonized basis matrix of self. @@ -5690,6 +5792,81 @@ def __hash__(self): """ return hash(self.__basis) + def _total_ordering_cmp(self, other, op): + r""" + Compare the free module self with other. + + Modules are ordered by their ambient spaces, then by dimension, + then in order by their echelon matrices. + + .. note:: + + Use :meth:`is_submodule` to determine if one + module is a submodule of another. + + EXAMPLES: + + First we compare two equal vector spaces. + + :: + + sage: from sage.structure.richcmp import op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE + sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) + sage: W = span([[5,6,7], [8,9,10]], QQ) + sage: V._total_ordering_cmp(W,op_EQ) + True + + Next we compare a one dimensional space to the two dimensional + space defined above. + + :: + + sage: from sage.structure.richcmp import op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE + sage: M = span([[5,6,7]], QQ) + sage: V._total_ordering_cmp(M,op_EQ) + False + sage: M._total_ordering_cmp(V, op_LT) + True + sage: V._total_ordering_cmp(M, op_LT) + False + + We compare a `\ZZ`-module to the one-dimensional + space above.:: + + sage: from sage.structure.richcmp import op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE + sage: V = span([[5,6,7]], ZZ).scale(1/11); V + Free module of degree 3 and rank 1 over Integer Ring + Echelon basis matrix: + [5/11 6/11 7/11] + sage: V._total_ordering_cmp(M, op_LT) + True + sage: M._total_ordering_cmp(V, op_LT) + False + """ + if self is other: + return rich_to_bool(op, 0) + if not isinstance(other, FreeModule_generic): + return NotImplemented + lx = self.ambient_vector_space() + rx = other.ambient_vector_space() + if lx != rx: + return lx._total_ordering_cmp( rx, op) + + lx = self.dimension() + rx = other.dimension() + if lx != rx: + return richcmp_not_equal(lx, rx, op) + + lx = self.base_ring() + rx = other.base_ring() + if lx != rx: + return richcmp_not_equal(lx, rx, op) + + # We use self.echelonized_basis_matrix() == other.echelonized_basis_matrix() + # with the matrix to avoid a circular reference. + return richcmp(self.echelonized_basis_matrix(), + other.echelonized_basis_matrix(), op) + def construction(self): """ Returns the functorial construction of self, namely, the subspace @@ -7165,3 +7342,38 @@ def element_class(R, is_sparse): else: return free_module_element.FreeModuleElement_generic_dense raise NotImplementedError + +def total_module_ordering(): + """ + A total ordering on free modules for sorting. + + EXAMPLES:: + + sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) + sage: W = span([[5,6,7], [8,9,10]], QQ) + sage: X = span([[5,6,7]], ZZ).scale(1/11) + sage: Y = CC^3 + sage: Z = ZZ^2 + sage: modules = [V,W,X,Y,Z] + sage: modules_sorted = [Z,X,V,W,Y] + sage: from sage.modules.free_module import total_module_ordering + sage: modules.sort(key=total_module_ordering()) + sage: modules == modules_sorted + True + """ + class K: + def __init__(self, obj, *args): + self.obj = obj + def __lt__(self, other): + return self.obj._total_ordering_cmp(other.obj,op_LT) + def __gt__(self, other): + return self.obj._total_ordering_cmp(other.obj,op_GT) + def __eq__(self, other): + return self.obj._total_ordering_cmp(other.obj,op_EQ) + def __le__(self, other): + return self.obj._total_ordering_cmp(other.obj,op_LE) + def __ge__(self, other): + return self.obj._total_ordering_cmp(other.obj,op_GE) + def __ne__(self, other): + return self.obj._total_ordering_cmp(other.obj,op_NE) + return K From b0f6fc434453ca23961060c9bc73c508e2574af5 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Thu, 30 Nov 2017 10:13:11 +0100 Subject: [PATCH 435/740] Use the old total ordering in /src/sage/modular to fix doctest failures --- src/sage/modular/abvar/abvar.py | 7 +++-- src/sage/modular/abvar/finite_subgroup.py | 6 ++-- src/sage/modular/hecke/module.py | 5 ++-- src/sage/modular/hecke/submodule.py | 7 +++-- src/sage/modular/modform/space.py | 5 +++- src/sage/modular/modsym/space.py | 7 +++-- src/sage/modules/free_module.py | 36 +++++++++++------------ 7 files changed, 40 insertions(+), 33 deletions(-) diff --git a/src/sage/modular/abvar/abvar.py b/src/sage/modular/abvar/abvar.py index 2288b349b41..f61de484dfd 100644 --- a/src/sage/modular/abvar/abvar.py +++ b/src/sage/modular/abvar/abvar.py @@ -48,7 +48,7 @@ from sage.rings.polynomial.polynomial_element import Polynomial from sage.rings.infinity import infinity from sage.rings.fraction_field import FractionField -from sage.modules.free_module import is_FreeModule +from sage.modules.free_module import is_FreeModule, total_module_order from sage.modular.arithgroup.all import is_CongruenceSubgroup, is_Gamma0, is_Gamma1, is_GammaH from sage.modular.modsym.all import ModularSymbols from sage.modular.modsym.space import ModularSymbolsSpace @@ -409,8 +409,9 @@ def __richcmp__(self, other, op): # NOTE!! having the same newform level, isogeny class number, # and degen_t does not imply two abelian varieties are equal. # See the docstring for self.label. - - return richcmp(self.lattice(), other.lattice(), op) + lx = total_module_order(self.lattice()) + rx = total_module_order(other.lattice()) + return richcmp(lx, rx, op) def __radd__(self,other): """ diff --git a/src/sage/modular/abvar/finite_subgroup.py b/src/sage/modular/abvar/finite_subgroup.py index 0fce5573e34..b63deb3b808 100644 --- a/src/sage/modular/abvar/finite_subgroup.py +++ b/src/sage/modular/abvar/finite_subgroup.py @@ -101,7 +101,7 @@ from sage.modular.abvar.torsion_point import TorsionPoint from sage.modules.module import Module -from sage.modules.free_module import is_FreeModule +from sage.modules.free_module import is_FreeModule, total_module_order from sage.structure.element import ModuleElement from sage.structure.gens_py import abelian_iterator from sage.structure.sequence import Sequence @@ -253,8 +253,10 @@ def __richcmp__(self, other, op): if not A.in_same_ambient_variety(B): return richcmp(A.ambient_variety(), B.ambient_variety(), op) L = A.lattice() + B.lattice() + lx = total_module_order(other.lattice() + L) + rx = total_module_order(self.lattice() + L) # order gets reversed in passing to lattices. - return richcmp(other.lattice() + L, self.lattice() + L, op) + return richcmp(lx, rx, op) def is_subgroup(self, other): """ diff --git a/src/sage/modular/hecke/module.py b/src/sage/modular/hecke/module.py index 9d31e5a4e6a..5745c490b12 100644 --- a/src/sage/modular/hecke/module.py +++ b/src/sage/modular/hecke/module.py @@ -1026,8 +1026,9 @@ def decomposition(self, bound=None, anemic=True, height_guess=1, sort_by_basis = self.__is_splittable = len(D) > 1 if anemic: self.__is_splittable_anemic = len(D) > 1 - - D.sort(key = None if not sort_by_basis else lambda ss: ss.free_module()) + from sage.modules.free_module import total_module_order + D.sort(key = None if not sort_by_basis + else lambda ss: total_module_order(ss.free_module())) D.set_immutable() self.__decomposition[key] = D for i in range(len(D)): diff --git a/src/sage/modular/hecke/submodule.py b/src/sage/modular/hecke/submodule.py index 8661ff6853b..0707e715ea2 100644 --- a/src/sage/modular/hecke/submodule.py +++ b/src/sage/modular/hecke/submodule.py @@ -23,7 +23,7 @@ import sage.misc.misc as misc from sage.misc.cachefunc import cached_method from sage.structure.richcmp import richcmp_method, richcmp_not_equal, richcmp - +from sage.modules.free_module import total_module_order import sage.modules.all from . import module @@ -190,8 +190,9 @@ def __richcmp__(self, other, op): rx = other.ambient() if lx != rx: return richcmp_not_equal(lx, rx, op) - else: - return richcmp(self.free_module(), other.free_module(), op) + lx = total_module_order(self.free_module()) + rx = total_module_order(other.free_module()) + return richcmp(lx, rx, op) ################################ # Semi-Private functions diff --git a/src/sage/modular/modform/space.py b/src/sage/modular/modform/space.py index 03dc897cbfc..13e18d74746 100644 --- a/src/sage/modular/modform/space.py +++ b/src/sage/modular/modform/space.py @@ -1166,7 +1166,10 @@ def __richcmp__(self, x, op): if self.is_ambient() or x.is_ambient(): return richcmp(self.dimension(), x.dimension(), op) else: - return richcmp(self.free_module(), x.free_module(), op) + from sage.modules.free_module import total_module_order + lx = total_module_order(self.free_module()) + rx = total_module_order(x.free_module()) + return richcmp(lx, rx, op) def span_of_basis(self, B): """ diff --git a/src/sage/modular/modsym/space.py b/src/sage/modular/modsym/space.py index f5946cdab1d..b2d3655ccd3 100644 --- a/src/sage/modular/modsym/space.py +++ b/src/sage/modular/modsym/space.py @@ -27,6 +27,7 @@ import sage.modules.free_module as free_module import sage.matrix.matrix_space as matrix_space from sage.modules.free_module_element import is_FreeModuleElement +from sage.modules.free_module import total_module_order import sage.misc.all as misc import sage.modular.hecke.all as hecke import sage.arith.all as arith @@ -144,9 +145,9 @@ def __richcmp__(self, other, op): rx = other.dimension() if lx != rx: return richcmp_not_equal(lx, rx, op) - - lx = self.free_module() - rx = other.free_module() + + lx = total_module_order(self.free_module()) + rx = total_module_order(other.free_module()) if lx == rx: return rich_to_bool(op, 0) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 5d0e516feb2..dc579b7b26e 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -7343,7 +7343,7 @@ def element_class(R, is_sparse): return free_module_element.FreeModuleElement_generic_dense raise NotImplementedError -def total_module_ordering(): +class total_module_order: """ A total ordering on free modules for sorting. @@ -7356,24 +7356,22 @@ def total_module_ordering(): sage: Z = ZZ^2 sage: modules = [V,W,X,Y,Z] sage: modules_sorted = [Z,X,V,W,Y] - sage: from sage.modules.free_module import total_module_ordering - sage: modules.sort(key=total_module_ordering()) + sage: from sage.modules.free_module import total_module_order + sage: modules.sort(key=total_module_order) sage: modules == modules_sorted True """ - class K: - def __init__(self, obj, *args): - self.obj = obj - def __lt__(self, other): - return self.obj._total_ordering_cmp(other.obj,op_LT) - def __gt__(self, other): - return self.obj._total_ordering_cmp(other.obj,op_GT) - def __eq__(self, other): - return self.obj._total_ordering_cmp(other.obj,op_EQ) - def __le__(self, other): - return self.obj._total_ordering_cmp(other.obj,op_LE) - def __ge__(self, other): - return self.obj._total_ordering_cmp(other.obj,op_GE) - def __ne__(self, other): - return self.obj._total_ordering_cmp(other.obj,op_NE) - return K + def __init__(self, obj, *args): + self.obj = obj + def __lt__(self, other): + return self.obj._total_ordering_cmp(other.obj,op_LT) + def __gt__(self, other): + return self.obj._total_ordering_cmp(other.obj,op_GT) + def __eq__(self, other): + return self.obj._total_ordering_cmp(other.obj,op_EQ) + def __le__(self, other): + return self.obj._total_ordering_cmp(other.obj,op_LE) + def __ge__(self, other): + return self.obj._total_ordering_cmp(other.obj,op_GE) + def __ne__(self, other): + return self.obj._total_ordering_cmp(other.obj,op_NE) From 11d57fbf016dac9997b23246ad3c642ec494ed65 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Fri, 1 Dec 2017 09:49:17 +0100 Subject: [PATCH 436/740] Now with @richcmp_method decorator --- src/sage/modules/free_module.py | 90 +++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 38 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index dc579b7b26e..3afd549c098 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -4742,14 +4742,14 @@ def _sparse_module(self): """ return FreeModule(base_ring=self.base_ring(), rank = self.rank(), sparse=True) - def _total_ordering_cmp(self, other, op): + def _total_order_cmp(self, other, op): r""" Compare the free module self with other. Modules are ordered by their ambient spaces, then by dimension, then in order by their echelon matrices. However, if other is a sub-module or is a quotient module then its - comparison method is used instead of generic comparison. + total comparison method is used instead of generic comparison. EXAMPLES: @@ -4757,28 +4757,28 @@ def _total_ordering_cmp(self, other, op): rationals:: sage: from sage.structure.richcmp import op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE - sage: (QQ^3)._total_ordering_cmp(CC^3, op_LT) + sage: (QQ^3)._total_order_cmp(CC^3, op_LT) True - sage: (CC^3)._total_ordering_cmp(QQ^3, op_LT) + sage: (CC^3)._total_order_cmp(QQ^3, op_LT) False - sage: (CC^3)._total_ordering_cmp(QQ^3, op_GT) + sage: (CC^3)._total_order_cmp(QQ^3, op_GT) True :: sage: from sage.structure.richcmp import op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE sage: Q = QQ; Z = ZZ - sage: (Q^3)._total_ordering_cmp(Z^3, op_GT) + sage: (Q^3)._total_order_cmp(Z^3, op_GT) True - sage: (Q^3)._total_ordering_cmp(Z^3, op_LT) + sage: (Q^3)._total_order_cmp(Z^3, op_LT) False - sage: (Z^3)._total_ordering_cmp(Q^3, op_LT) + sage: (Z^3)._total_order_cmp(Q^3, op_LT) True - sage: (Z^3)._total_ordering_cmp(Q^3, op_GT) + sage: (Z^3)._total_order_cmp(Q^3, op_GT) False - sage: (Q^3)._total_ordering_cmp(Z^3, op_EQ) + sage: (Q^3)._total_order_cmp(Z^3, op_EQ) False - sage: (Q^3)._total_ordering_cmp(Q^3, op_EQ) + sage: (Q^3)._total_order_cmp(Q^3, op_EQ) True Comparison with a sub-module:: @@ -4791,9 +4791,9 @@ def _total_ordering_cmp(self, other, op): [ 1 0 -1] [ 0 1 2] sage: A = QQ^3 - sage: V._total_ordering_cmp(A, op_LT) + sage: V._total_order_cmp(A, op_LT) True - sage: A._total_ordering_cmp(V, op_LT) + sage: A._total_order_cmp(V, op_LT) False Comparison with a quotient module (see :trac:`10513`):: @@ -4801,9 +4801,9 @@ def _total_ordering_cmp(self, other, op): sage: from sage.structure.richcmp import op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE sage: M = QQ^3 / [[1,2,3]] sage: V = QQ^2 - sage: V._total_ordering_cmp(M, op_EQ) + sage: V._total_order_cmp(M, op_EQ) False - sage: M._total_ordering_cmp(V, op_EQ) + sage: M._total_order_cmp(V, op_EQ) False """ if self is other: @@ -4836,10 +4836,11 @@ def _total_ordering_cmp(self, other, op): except NotImplementedError: pass return richcmp_not_equal(lx, rx, op) + else: # now other is not ambient or is a quotient; # it knows how to do the comparison. - return other._total_ordering_cmp( self, revop(op)) + return other._total_order_cmp( self, revop(op)) def echelonized_basis_matrix(self): """ @@ -5792,7 +5793,7 @@ def __hash__(self): """ return hash(self.__basis) - def _total_ordering_cmp(self, other, op): + def _total_order_cmp(self, other, op): r""" Compare the free module self with other. @@ -5813,7 +5814,7 @@ def _total_ordering_cmp(self, other, op): sage: from sage.structure.richcmp import op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) sage: W = span([[5,6,7], [8,9,10]], QQ) - sage: V._total_ordering_cmp(W,op_EQ) + sage: V._total_order_cmp(W,op_EQ) True Next we compare a one dimensional space to the two dimensional @@ -5823,11 +5824,11 @@ def _total_ordering_cmp(self, other, op): sage: from sage.structure.richcmp import op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE sage: M = span([[5,6,7]], QQ) - sage: V._total_ordering_cmp(M,op_EQ) + sage: V._total_order_cmp(M,op_EQ) False - sage: M._total_ordering_cmp(V, op_LT) + sage: M._total_order_cmp(V, op_LT) True - sage: V._total_ordering_cmp(M, op_LT) + sage: V._total_order_cmp(M, op_LT) False We compare a `\ZZ`-module to the one-dimensional @@ -5838,9 +5839,9 @@ def _total_ordering_cmp(self, other, op): Free module of degree 3 and rank 1 over Integer Ring Echelon basis matrix: [5/11 6/11 7/11] - sage: V._total_ordering_cmp(M, op_LT) + sage: V._total_order_cmp(M, op_LT) True - sage: M._total_ordering_cmp(V, op_LT) + sage: M._total_order_cmp(V, op_LT) False """ if self is other: @@ -5850,7 +5851,7 @@ def _total_ordering_cmp(self, other, op): lx = self.ambient_vector_space() rx = other.ambient_vector_space() if lx != rx: - return lx._total_ordering_cmp( rx, op) + return lx._total_order_cmp( rx, op) lx = self.dimension() rx = other.dimension() @@ -7343,10 +7344,18 @@ def element_class(R, is_sparse): return free_module_element.FreeModuleElement_generic_dense raise NotImplementedError -class total_module_order: +@richcmp_method +class total_module_order(object): """ A total ordering on free modules for sorting. + Modules are ordered by their ambient spaces, then by dimension, + then in order by their echelon matrices. + + INPUT: + + - a free module + EXAMPLES:: sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) @@ -7361,17 +7370,22 @@ class total_module_order: sage: modules == modules_sorted True """ - def __init__(self, obj, *args): + def __init__(self, obj): + """ + Initialization + """ self.obj = obj - def __lt__(self, other): - return self.obj._total_ordering_cmp(other.obj,op_LT) - def __gt__(self, other): - return self.obj._total_ordering_cmp(other.obj,op_GT) - def __eq__(self, other): - return self.obj._total_ordering_cmp(other.obj,op_EQ) - def __le__(self, other): - return self.obj._total_ordering_cmp(other.obj,op_LE) - def __ge__(self, other): - return self.obj._total_ordering_cmp(other.obj,op_GE) - def __ne__(self, other): - return self.obj._total_ordering_cmp(other.obj,op_NE) + + def __richcmp__(self,other,op): + """ + A total ordering on free modules. + + TESTS:: + + sage: from sage.modules.free_module import total_module_order + sage: Y = total_module_order(CC^3) + sage: Z = total_module_order(ZZ^2) + sage: Z < Y + True + """ + return self.obj._total_order_cmp(other.obj,op) From 219478c836dce0c8a40c84ab35ff1492eb2ec090 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Fri, 1 Dec 2017 11:34:38 +0100 Subject: [PATCH 437/740] Reverting to solve(other.basis_matrix()) which is much faster. Vector wise solving is now a slow fallback option. --- src/sage/modules/free_module.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 3afd549c098..b2d3907d67d 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -1505,7 +1505,6 @@ def is_submodule(self, other): else: if self.inner_product_matrix() != other.inner_product_matrix(): return False - # self and other lie in a common ambient space. R = self.base_ring() S = other.base_ring() if R != S: @@ -1517,12 +1516,22 @@ def is_submodule(self, other): raise NotImplementedError("could not determine if %s is a " "subring of %s" %(R, S)) # now R is a subring of S - try: - M = [list(other.basis_matrix().solve_left(self.basis_matrix()[i])) for i in range(self.basis_matrix().nrows())] + if other.is_ambient() and S.is_field(): + return True + try: + M = other.basis_matrix().solve_left(self.basis_matrix()) except ValueError: return False - from sage.misc.flatten import flatten - return all(x in S for x in flatten(M)) + except TypeError: + # only if solve_left does not eat a matrix + # else this is far to inefficient + try: + M = [list(other.basis_matrix().solve_left(self.basis_matrix()[i])) for i in range(self.basis_matrix().nrows())] + except ValueError: + return False + from sage.misc.flatten import flatten + return all(x in S for x in flatten(M)) + return all(x in S for x in M.list()) def __iter__(self): """ From d54fcfaaf3fa488b594723eeb91bb0ac96bb5a18 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Fri, 1 Dec 2017 11:52:35 +0100 Subject: [PATCH 438/740] Total ordering works for quotient modules now. --- src/sage/modules/free_module.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index b2d3907d67d..14d54cba001 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -4820,7 +4820,9 @@ def _total_order_cmp(self, other, op): if not isinstance(other, FreeModule_generic): return NotImplemented from sage.modules.quotient_module import FreeModule_ambient_field_quotient - if isinstance(other, FreeModule_ambient) and not isinstance(other, FreeModule_ambient_field_quotient): + if isinstance(other, FreeModule_ambient): + if isinstance(other, FreeModule_ambient_field_quotient) or isinstance(self, FreeModule_ambient_field_quotient): + return richcmp(self,other,op) lx = self.rank() rx = other.rank() if lx != rx: From 72aeed1cfe0ed2dadf0b2026ad326adc1cfc482c Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 30 Nov 2017 13:47:31 +0000 Subject: [PATCH 439/740] Change atomic_write to support text mode vs. binary mode writing. On Python 2 there's no difference, and the encoding argument is ignored. On Python 3 the new default is text mode, so that atomic_write writes and reads `str` objects (as opposed to `bytes`). The explicit `binary` argument exists so that if no encoding is specified (`encoding=None`) the default encoding can be used. In other words, just setting `encoding=None` is not sufficient to imply binary mode. --- src/sage/misc/temporary_file.py | 34 ++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/sage/misc/temporary_file.py b/src/sage/misc/temporary_file.py index 697d000c8f2..6418cb8c86f 100644 --- a/src/sage/misc/temporary_file.py +++ b/src/sage/misc/temporary_file.py @@ -21,10 +21,14 @@ #***************************************************************************** from __future__ import print_function +import locale import os import tempfile import atexit +import six + + def delete_tmpfiles(): """ Remove the directory ``SAGE_TMP``. @@ -232,6 +236,14 @@ class atomic_write: resulting file will also have these permissions (unless the mode bits of the file were changed manually). + - ``binary`` -- (boolean, default: False) the underlying file is opened + in binary mode. If False then it is opened in text mode and an encoding + with which to write the file may be supplied. + + - ``encoding`` -- (str, default: ``locale.getpreferredencoding(False)``) + the encoding with which to write text data when ``binary=False``. + Implies ``binary=False`` if given. + EXAMPLES:: sage: from sage.misc.temporary_file import atomic_write @@ -322,7 +334,8 @@ class atomic_write: sage: open(target_file, "r").read() '>>> AAA' """ - def __init__(self, target_filename, append=False, mode=0o666): + def __init__(self, target_filename, append=False, mode=0o666, + binary=False, encoding=None): """ TESTS:: @@ -341,6 +354,10 @@ def __init__(self, target_filename, append=False, mode=0o666): # Remove umask bits from mode umask = os.umask(0); os.umask(umask) self.mode = mode & (~umask) + self.binary = binary if encoding is None else False + if not self.binary and encoding is None: + encoding = locale.getpreferredencoding(False) + self.encoding = encoding def __enter__(self): """ @@ -360,12 +377,23 @@ def __enter__(self): ....: os.path.dirname(aw.target) == os.path.dirname(f.name) True """ - self.tempfile = tempfile.NamedTemporaryFile(dir=self.tmpdir, delete=False) + + rmode = 'r' + ('b' if self.binary else '') + wmode = 'w+' + ('b' if self.binary else '') + if six.PY2: + encoding_kwargs = {} + else: + encoding_kwargs = {'encoding': self.encoding} + + self.tempfile = tempfile.NamedTemporaryFile( + wmode, dir=self.tmpdir, delete=False, **encoding_kwargs) + self.tempname = self.tempfile.name os.chmod(self.tempname, self.mode) if self.append: try: - r = open(self.target).read() + with open(self.target, rmode, **encoding_kwargs) as f: + r = f.read() except IOError: pass else: From bccdf943830707bf200e6ddd6a8a00da54205744 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 30 Nov 2017 13:51:20 +0000 Subject: [PATCH 440/740] Minor fixes to the sage.misc.temporary_file doctests. Most of these are to ensure that all files opened by the tests are closed before the end of the test. Also, avoid using oct() since it outputs in a different format on Python 3. --- src/sage/misc/temporary_file.py | 52 +++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/src/sage/misc/temporary_file.py b/src/sage/misc/temporary_file.py index 6418cb8c86f..ad6f0235083 100644 --- a/src/sage/misc/temporary_file.py +++ b/src/sage/misc/temporary_file.py @@ -88,12 +88,13 @@ def tmp_dir(name="dir_", ext=""): sage: d # random output '/home/username/.sage/temp/hostname/7961/dir_testing_XgRu4p.extension/' sage: os.chdir(d) - sage: _ = open('file_inside_d', 'w') + sage: f = open('file_inside_d', 'w') Temporary directories are unaccessible by other users:: sage: os.stat(d).st_mode & 0o077 0 + sage: f.close() """ from sage.misc.misc import SAGE_TMP tmp = tempfile.mkdtemp(prefix=name, suffix=ext, dir=str(SAGE_TMP)) @@ -137,12 +138,13 @@ def tmp_filename(name="tmp_", ext=""): sage: fn = tmp_filename('just_for_testing_', '.extension') sage: fn # random '/home/username/.sage/temp/hostname/8044/just_for_testing_tVVHsn.extension' - sage: _ = open(fn, 'w') + sage: f = open(fn, 'w') Temporary files are unaccessible by other users:: sage: os.stat(fn).st_mode & 0o077 0 + sage: f.close() """ from sage.misc.misc import SAGE_TMP handle, tmp = tempfile.mkstemp(prefix=name, suffix=ext, dir=str(SAGE_TMP)) @@ -248,13 +250,16 @@ class atomic_write: sage: from sage.misc.temporary_file import atomic_write sage: target_file = tmp_filename() - sage: _ = open(target_file, "w").write("Old contents") + sage: with open(target_file, 'w') as f: + ....: _ = f.write("Old contents") sage: with atomic_write(target_file) as f: ....: _ = f.write("New contents") ....: f.flush() - ....: open(target_file, "r").read() + ....: with open(target_file, 'r') as f2: + ....: f2.read() 'Old contents' - sage: open(target_file, "r").read() + sage: with open(target_file, 'r') as f: + ....: f.read() 'New contents' The name of the temporary file can be accessed using ``f.name``. @@ -262,11 +267,14 @@ class atomic_write: sage: from sage.misc.temporary_file import atomic_write sage: target_file = tmp_filename() - sage: _ = open(target_file, "w").write("Old contents") + sage: with open(target_file, 'w') as f: + ....: _ = f.write("Old contents") sage: with atomic_write(target_file) as f: ....: f.close() - ....: _ = open(f.name, "w").write("Newer contents") - sage: open(target_file, "r").read() + ....: with open(f.name, 'w') as f2: + ....: _ = f2.write("Newer contents") + sage: with open(target_file, 'r') as f: + ....: f.read() 'Newer contents' If an exception occurs while writing the file, the target file is @@ -278,7 +286,8 @@ class atomic_write: Traceback (most recent call last): ... RuntimeError - sage: open(target_file, "r").read() + sage: with open(target_file, 'r') as f: + ....: f.read() 'Newer contents' Some examples of using the ``append`` option. Note that the file @@ -290,12 +299,14 @@ class atomic_write: ....: _ = f.write("Hello") sage: with atomic_write(target_file, append=True) as f: ....: _ = f.write(" World") - sage: open(target_file, "r").read() + sage: with open(target_file, 'r') as f: + ....: f.read() 'Hello World' sage: with atomic_write(target_file, append=True) as f: - ....: f.seek(0) + ....: _ = f.seek(0) ....: _ = f.write("HELLO") - sage: open(target_file, "r").read() + sage: with open(target_file, 'r') as f: + ....: f.read() 'HELLO World' If the target file is a symbolic link, the link is kept and the @@ -305,7 +316,8 @@ class atomic_write: sage: os.symlink(target_file, link_to_target) sage: with atomic_write(link_to_target) as f: ....: _ = f.write("Newest contents") - sage: open(target_file, "r").read() + sage: with open(target_file, 'r') as f: + ....: f.read() 'Newest contents' We check the permission bits of the new file. Note that the old @@ -315,23 +327,25 @@ class atomic_write: sage: _ = os.umask(0o022) sage: with atomic_write(target_file) as f: ....: pass - sage: oct(os.stat(target_file).st_mode & 0o777) - '644' + sage: '{:#o}'.format(os.stat(target_file).st_mode & 0o777) + '0o644' sage: _ = os.umask(0o077) sage: with atomic_write(target_file, mode=0o777) as f: ....: pass - sage: oct(os.stat(target_file).st_mode & 0o777) - '700' + sage: '{:#o}'.format(os.stat(target_file).st_mode & 0o777) + '0o700' Test writing twice to the same target file. The outermost ``with`` "wins":: - sage: _ = open(target_file, "w").write(">>> ") + sage: with open(target_file, 'w') as f: + ....: _ = f.write('>>> ') sage: with atomic_write(target_file, append=True) as f, \ ....: atomic_write(target_file, append=True) as g: ....: _ = f.write("AAA"); f.close() ....: _ = g.write("BBB"); g.close() - sage: open(target_file, "r").read() + sage: with open(target_file, 'r') as f: + ....: f.read() '>>> AAA' """ def __init__(self, target_filename, append=False, mode=0o666, From f75818036ed31474d76a0d0f60637dc037b87f79 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Sat, 2 Dec 2017 11:35:48 +0000 Subject: [PATCH 441/740] #21092 work in progress --- .../schemes/elliptic_curves/constructor.py | 80 +++++++++++-------- 1 file changed, 47 insertions(+), 33 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index 3afb853c361..5791d904e1c 100644 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -511,7 +511,7 @@ def EllipticCurve_from_Weierstrass_polynomial(f): def coefficients_from_Weierstrass_polynomial(f): """ - Return the coefficients `(a_1, a_2, a_3, a_4, a_5)` for a cubic in + Return the coefficients `[a_1, a_2, a_3, a_4, a_6]` of a cubic in Weierstrass form. EXAMPLES:: @@ -723,36 +723,49 @@ def coefficients_from_j(j, minimal_twist=True): return Sequence([0, 0, 0, -3*j*k, -2*j*k**2], universe=K) -def EllipticCurve_from_cubic(F, P, morphism=True): +def EllipticCurve_from_cubic(F, P=None, morphism=True): r""" - Construct an elliptic curve from a ternary cubic with a rational point. - - If you just want the Weierstrass form and are not interested in - the morphism then it is easier to use - :func:`~sage.schemes.elliptic_curves.jacobian.Jacobian` - instead. This will construct the same elliptic curve but you don't - have to supply the point ``P``. + Construct an elliptic curve from a smooth ternary cubic with a rational point. INPUT: - ``F`` -- a homogeneous cubic in three variables with rational coefficients, as a polynomial ring element, defining a smooth - plane cubic curve. + plane cubic curve `C`. - - ``P`` -- a 3-tuple `(x,y,z)` defining a projective point on the - curve `F=0`. Need not be a flex, but see caveat on output. + - ``P`` -- a 3-tuple `(x,y,z)` defining a projective point on `C`, + or ``None``. If ``None`` then a rational flex will be used as a + base point if one exists, otherwise an error will be raised. - - ``morphism`` -- boolean (default: ``True``). Whether to return - the morphism or just the elliptic curve. + - ``morphism`` -- boolean (default: ``True``). If ``True`` + returns a birational isomorphism from `C` to a Weierstrass + elliptic curve `E`, otherwise just returns `E`. OUTPUT: - An elliptic curve in long Weierstrass form isomorphic to the curve - `F=0`. + Either (when ``morphism``=``False``) an elliptic curve `E` in long + Weierstrass form isomorphic to the plane cubic curve `C` defined + by the equation `F=0`. + + Or (when ``morphism=True``), a birational isomorphism from `C` to + the elliptic curve `E`. If the given point is a flex, this is a + linear isomorphism. + + .. note:: + + The function + :func:`~sage.schemes.elliptic_curves.jacobian.Jacobian` may be + used instead. It constructs the same elliptic curve (which is in + all cases the Jacobian of `(F=0)`) and needs no base point to be + provided, but also returns no isomorphism since in general there + is none: the plane cubic is only isomorphic to its Jacobian when + it has a rational point. - If ``morphism=True`` is passed, then a birational equivalence - between F and the Weierstrass curve is returned. If the point - happens to be a flex, then this is an isomorphism. + .. note:: + + When ``morphism=True``, the morphism does not necessarily take + the given point `P` to the point at infinity on `E`, since we + always use a rational flex on `C` as base-point when one exists. EXAMPLES: @@ -898,25 +911,26 @@ def EllipticCurve_from_cubic(F, P, morphism=True): if not F.is_homogeneous(): raise TypeError('equation must be a homogeneous polynomial') K = F.parent().base_ring() + try: - P = [K(c) for c in P] - except TypeError: - raise TypeError('cannot convert %s into %s'%(P,K)) - if F(P) != 0: - raise ValueError('%s is not a point on %s'%(P,F)) - if len(P) != 3: - raise TypeError('%s is not a projective point'%P) + C = Curve(F) + CP = C(P) + except TypeError, ValueError: + raise TypeError('{} does not define a point on a projective curve over {} defined by {}'.format(P,K,F)) + x, y, z = R.gens() - # First case: if P = P2 then P is a flex, or if P2 = P3 then P2 is a flex - P2 = chord_and_tangent(F, P) - flex_point = None - if are_projectively_equivalent(P, P2, base_ring=K): + # Test whether P is a flex; if not test whether there are any rational flexes: + + hessian = Matrix([[F.derivative(v1, v2) for v1 in R.gens()] for v2 in R.gens()]).det() + if hessian(P)==0: flex_point = P else: - P3 = chord_and_tangent(F, P2) - if are_projectively_equivalent(P2, P3, base_ring=K): - flex_point = P2 + flexes = C.intersection(Curve(hessian)).rational_points() + if flexes: + flex_point = list(flexes[0]) + else: + flex_point = None if flex_point is not None: P = flex_point From 800e98b16e583a070f8d90b44ae89f99d54e1bc3 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Mon, 4 Dec 2017 09:03:42 +0100 Subject: [PATCH 442/740] Added a few doctests --- src/sage/modules/free_module.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 14d54cba001..b3e9c6e0db2 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -7360,8 +7360,10 @@ class total_module_order(object): """ A total ordering on free modules for sorting. - Modules are ordered by their ambient spaces, then by dimension, - then in order by their echelon matrices. + This class orders modules by their ambient spaces, then by dimension, + then in order by their echelon matrices. If a function returns a list of + free modules, this can be used to sort the output and thus render + it deterministic. INPUT: @@ -7383,7 +7385,18 @@ class total_module_order(object): """ def __init__(self, obj): """ - Initialization + Create a container for a free module with a total ordering. + + EXAMPLES:: + + sage: R. = QQ[] + sage: V = span(R,[[x,1+x],[x^2,2+x]]) + sage: W = RR^2 + sage: from sage.modules.free_module import total_module_order + sage: V = total_module_order(V) + sage: W = total_module_order(W) + sage: V < W + True """ self.obj = obj From c79313811851584c5b6732ea12da8b40d04b0377 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 11 Dec 2017 23:58:06 +0000 Subject: [PATCH 443/740] update arb to 2.12.0, doctest fixes, and #24369 --- build/pkgs/arb/checksums.ini | 6 +++--- build/pkgs/arb/package-version.txt | 2 +- src/sage/rings/complex_arb.pyx | 14 +++++++------- src/sage/rings/real_arb.pyx | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/build/pkgs/arb/checksums.ini b/build/pkgs/arb/checksums.ini index 78909d583ff..1924ee03c3d 100644 --- a/build/pkgs/arb/checksums.ini +++ b/build/pkgs/arb/checksums.ini @@ -1,4 +1,4 @@ tarball=arb-VERSION.tar.gz -sha1=2f06bfb433cdaecde0e824c5e638094fd666a0d1 -md5=d63cdd1147104790826c93bc8651104f -cksum=2745482665 +sha1=27476d0529e48a07d92da90bd0fb80dd18f443e3 +md5=733285d9705d10b8024e551ffa81952f +cksum=2391183744 diff --git a/build/pkgs/arb/package-version.txt b/build/pkgs/arb/package-version.txt index 99993ffae4c..c8810e9bdb8 100644 --- a/build/pkgs/arb/package-version.txt +++ b/build/pkgs/arb/package-version.txt @@ -1 +1 @@ -2.11.1.p0 +2.12.0.p0 diff --git a/src/sage/rings/complex_arb.pyx b/src/sage/rings/complex_arb.pyx index ee048fb28cf..71fad24b327 100644 --- a/src/sage/rings/complex_arb.pyx +++ b/src/sage/rings/complex_arb.pyx @@ -2603,7 +2603,7 @@ cdef class ComplexBall(RingElement): EXAMPLES:: sage: CBF(0, -1).agm1() - [0.5990701173678 +/- 1.15e-14] + [-0.5990701173678 +/- 1.19e-14]*I + [0.599070117367796 +/- 3.9...e-16] + [-0.599070117367796 +/- 5.5...e-16]*I """ cdef ComplexBall res = self._new() if _do_sig(prec(self)): sig_on() @@ -3318,9 +3318,9 @@ cdef class ComplexBall(RingElement): [0.002473055794309 +/- 5.01e-16] + [0.003859554040267 +/- 4.45e-16]*I, [-0.01299087561709 +/- 4.72e-15] + [0.00725027521915 +/- 4.32e-15]*I] sage: (z + 3 + 4*tau).elliptic_p(tau, 3) - [[-3.2892099677271 +/- 2.29e-14] + [-0.00036737673029 +/- 8.58e-15]*I, - [0.002473055794 +/- 6.59e-13] + [0.003859554040 +/- 6.17e-13]*I, - [-0.0129908756 +/- 3.39e-11] + [0.0072502752 +/- 3.60e-11]*I] + [[-3.28920996772709 +/- 8.4...e-15] + [-0.00036737673029 +/- 4.1...e-15]*I, + [0.0024730557943 +/- 6.6...e-14] + [0.0038595540403 +/- 8.8...e-14]*I, + [-0.01299087562 +/- 5.6...e-12] + [0.00725027522 +/- 3.5...e-12]*I] """ cdef ComplexBall my_tau = self._parent.coerce(tau) @@ -3356,7 +3356,7 @@ cdef class ComplexBall(RingElement): EXAMPLES:: sage: CBF(2,3).elliptic_k() - [1.0429132919285 +/- 3.65e-14] + [0.6296824723086 +/- 6.15e-14]*I + [1.04291329192852 +/- 5.9...e-15] + [0.62968247230864 +/- 3.4...e-15]*I """ cdef ComplexBall result = self._new() @@ -3373,7 +3373,7 @@ cdef class ComplexBall(RingElement): EXAMPLES:: sage: CBF(2,3).elliptic_e() - [1.472797144959 +/- 4.82e-13] + [-1.231604783936 +/- 1.25e-13]*I + [1.472797144959 +/- 4.5...e-13] + [-1.231604783936 +/- 9.5...e-14]*I """ cdef ComplexBall result = self._new() @@ -3520,7 +3520,7 @@ cdef class ComplexBall(RingElement): EXAMPLES:: sage: CBF(1/2).legendre_P(5) - 0.08984375000000000 + [0.08984375000000000 +/- 4.5...e-18] sage: CBF(1,2).legendre_P(CBF(2,3), CBF(0,1)) [0.10996180744364 +/- 7.45e-15] + [0.14312767804055 +/- 8.38e-15]*I sage: CBF(-10).legendre_P(5, 325/100) diff --git a/src/sage/rings/real_arb.pyx b/src/sage/rings/real_arb.pyx index aa12ae0a043..9aad425387e 100644 --- a/src/sage/rings/real_arb.pyx +++ b/src/sage/rings/real_arb.pyx @@ -3451,7 +3451,7 @@ cdef class RealBall(RingElement): sage: RBF(1/2).polylog(1) [0.6931471805599 +/- 5.02e-14] sage: RBF(1/3).polylog(1/2) - [0.44210883528067 +/- 6.75e-15] + [0.44210883528067 +/- 6.7...e-15] sage: RBF(1/3).polylog(RLF(pi)) [0.34728895057225 +/- 5.51e-15] @@ -3551,7 +3551,7 @@ cdef class RealBall(RingElement): sage: RBF(1).agm(1) 1.000000000000000 sage: RBF(sqrt(2)).agm(1)^(-1) - [0.83462684167407 +/- 4.31e-15] + [0.83462684167407 +/- 3.9...e-15] """ cdef RealBall other_as_ball cdef RealBall res = self._new() From 12e50b9303568f18b8e1f28cf3609321f17bb6db Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Thu, 14 Dec 2017 08:20:11 +0100 Subject: [PATCH 444/740] Fixed a derived inconsistency in sage.rings.invariant_theory --- src/sage/rings/invariant_theory.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/invariant_theory.py b/src/sage/rings/invariant_theory.py index b43b3ce58c2..a051d86353f 100644 --- a/src/sage/rings/invariant_theory.py +++ b/src/sage/rings/invariant_theory.py @@ -1079,14 +1079,14 @@ def as_QuadraticForm(self): sage: quadratic.as_QuadraticForm() Quadratic form in 3 variables over Multivariate Polynomial Ring in x, y, z over Rational Field with coefficients: - [ 1/2 1 3/2 ] - [ * 1/2 0 ] - [ * * 1/2 ] + [ 1 2 3 ] + [ * 1 0 ] + [ * * 1 ] sage: _.polynomial('X,Y,Z') X^2 + 2*X*Y + Y^2 + 3*X*Z + Z^2 """ R = self._ring - B = self._matrix_() + B = 2*self._matrix_() import sage.quadratic_forms.quadratic_form return sage.quadratic_forms.quadratic_form.QuadraticForm(R, B) From b49ea7634d85dde58ec9557fee4cdaf78fc9c02f Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Thu, 21 Dec 2017 16:09:10 +0100 Subject: [PATCH 445/740] 24411: move gamma functions to gamma.py --- src/sage/functions/airy.py | 5 +- src/sage/functions/all.py | 8 +- src/sage/functions/bessel.py | 6 +- src/sage/functions/gamma.py | 973 +++++++++ src/sage/functions/hypergeometric.py | 11 +- src/sage/functions/log.py | 2 +- src/sage/functions/orthogonal_polys.py | 17 +- src/sage/functions/other.py | 1863 ++++------------- src/sage/functions/special.py | 3 +- src/sage/functions/transcendental.py | 4 +- src/sage/interfaces/maxima_lib.py | 11 +- .../elliptic_curves/ell_rational_field.py | 2 +- src/sage/symbolic/expression.pyx | 2 +- 13 files changed, 1469 insertions(+), 1438 deletions(-) create mode 100644 src/sage/functions/gamma.py diff --git a/src/sage/functions/airy.py b/src/sage/functions/airy.py index da3b614abe5..f4a6b9565b9 100644 --- a/src/sage/functions/airy.py +++ b/src/sage/functions/airy.py @@ -48,7 +48,6 @@ from sage.symbolic.function import BuiltinFunction from sage.symbolic.expression import Expression from sage.symbolic.ring import SR -from sage.functions.other import gamma from sage.rings.integer_ring import ZZ from sage.rings.real_double import RDF from sage.rings.rational import Rational as R @@ -193,6 +192,7 @@ def _eval_(self, x): sage: airy_ai_simple(1.0 * I) 0.331493305432141 - 0.317449858968444*I """ + from .gamma import gamma if x == 0: r = ZZ(2) / 3 return 1 / (3 ** (r) * gamma(r)) @@ -293,6 +293,7 @@ def _eval_(self, x): sage: airy_ai_prime(0.0) -0.258819403792807 """ + from .gamma import gamma if x == 0: r = ZZ(1) / 3 return -1 / (3 ** (r) * gamma(r)) @@ -625,6 +626,7 @@ def _eval_(self, x): sage: airy_bi_simple(1.0 * I) 0.648858208330395 + 0.344958634768048*I """ + from .gamma import gamma if x == 0: one_sixth = ZZ(1) / 6 return 1 / (3 ** (one_sixth) * gamma(4 * one_sixth)) @@ -725,6 +727,7 @@ def _eval_(self, x): sage: airy_bi_prime(0.0) 0.448288357353826 """ + from .gamma import gamma if x == 0: one_sixth = ZZ(1) / 6 return 3 ** (one_sixth) / gamma(2 * one_sixth) diff --git a/src/sage/functions/all.py b/src/sage/functions/all.py index e7f1ea0efad..b3c4dd6774e 100644 --- a/src/sage/functions/all.py +++ b/src/sage/functions/all.py @@ -21,13 +21,11 @@ -from .other import ( ceil, floor, gamma, psi, factorial, beta, binomial, - abs_symbolic, sqrt, log_gamma, - gamma_inc, incomplete_gamma, gamma_inc_lower, +from .other import ( ceil, floor, abs_symbolic, sqrt, arg, real_part, real, frac, + factorial, binomial, imag_part, imag, imaginary, conjugate, cases, complex_root_of) - from .log import (exp, exp_polar, log, ln, polylog, dilog, lambert_w, harmonic_number) @@ -86,3 +84,5 @@ from .hypergeometric import hypergeometric, hypergeometric_M, hypergeometric_U +from .gamma import (gamma, psi, beta, log_gamma, + gamma_inc, incomplete_gamma, gamma_inc_lower) diff --git a/src/sage/functions/bessel.py b/src/sage/functions/bessel.py index 11181ac6534..a82fa942c6d 100644 --- a/src/sage/functions/bessel.py +++ b/src/sage/functions/bessel.py @@ -1320,7 +1320,8 @@ def _derivative_(self, a, z, diff_param=None): if diff_param == 0: raise ValueError("cannot differentiate struve_H in the first parameter") - from sage.functions.other import sqrt, gamma + from .gamma import gamma + from .other import sqrt return (z**a/(sqrt(pi)*2**a*gamma(a+Integer(3)/Integer(2)))-struve_H(a+1,z)+struve_H(a-1,z))/2 def _print_latex_(self, a, z): @@ -1434,7 +1435,8 @@ def _derivative_(self, a, z, diff_param=None): if diff_param == 0: raise ValueError("cannot differentiate struve_L in the first parameter") - from sage.functions.other import sqrt, gamma + from .gamma import gamma + from .other import sqrt return (z**a/(sqrt(pi)*2**a*gamma(a+Integer(3)/Integer(2)))-struve_L(a+1,z)+struve_L(a-1,z))/2 def _print_latex_(self, a, z): diff --git a/src/sage/functions/gamma.py b/src/sage/functions/gamma.py new file mode 100644 index 00000000000..fe25c31d2ae --- /dev/null +++ b/src/sage/functions/gamma.py @@ -0,0 +1,973 @@ +""" +Gamma and related functions +""" +from __future__ import print_function +from six.moves import range +from six import integer_types + +from sage.symbolic.function import GinacFunction, BuiltinFunction +from sage.libs.pynac.pynac import (register_symbol, symbol_table, + py_factorial_py, I) +from sage.structure.element import coercion_model +from sage.structure.all import parent as s_parent +from sage.symbolic.expression import Expression +from sage.rings.all import Integer, Rational, RealField, ZZ, ComplexField +from sage.functions.exp_integral import Ei +from sage.libs.mpmath import utils as mpmath_utils +from sage.arith.all import binomial as arith_binomial +from .log import exp +from .other import sqrt +from sage.symbolic.constants import pi + +class Function_gamma(GinacFunction): + def __init__(self): + r""" + The Gamma function. This is defined by + + .. MATH:: + + \Gamma(z) = \int_0^\infty t^{z-1}e^{-t} dt + + for complex input `z` with real part greater than zero, and by + analytic continuation on the rest of the complex plane (except + for negative integers, which are poles). + + It is computed by various libraries within Sage, depending on + the input type. + + EXAMPLES:: + + sage: from sage.functions.gamma import gamma1 + sage: gamma1(CDF(0.5,14)) + -4.0537030780372815e-10 - 5.773299834553605e-10*I + sage: gamma1(CDF(I)) + -0.15494982830181067 - 0.49801566811835607*I + + Recall that `\Gamma(n)` is `n-1` factorial:: + + sage: gamma1(11) == factorial(10) + True + sage: gamma1(6) + 120 + sage: gamma1(1/2) + sqrt(pi) + sage: gamma1(-1) + Infinity + sage: gamma1(I) + gamma(I) + sage: gamma1(x/2)(x=5) + 3/4*sqrt(pi) + + sage: gamma1(float(6)) # For ARM: rel tol 3e-16 + 120.0 + sage: gamma(6.) + 120.000000000000 + sage: gamma1(x) + gamma(x) + + :: + + sage: gamma1(pi) + gamma(pi) + sage: gamma1(i) + gamma(I) + sage: gamma1(i).n() + -0.154949828301811 - 0.498015668118356*I + sage: gamma1(int(5)) + 24 + + :: + + sage: conjugate(gamma(x)) + gamma(conjugate(x)) + + :: + + sage: plot(gamma1(x),(x,1,5)) + Graphics object consisting of 1 graphics primitive + + To prevent automatic evaluation use the ``hold`` argument:: + + sage: gamma1(1/2,hold=True) + gamma(1/2) + + To then evaluate again, we currently must use Maxima via + :meth:`sage.symbolic.expression.Expression.simplify`:: + + sage: gamma1(1/2,hold=True).simplify() + sqrt(pi) + + TESTS: + + sage: gamma(x)._sympy_() + gamma(x) + + We verify that we can convert this function to Maxima and + convert back to Sage:: + + sage: z = var('z') + sage: maxima(gamma1(z)).sage() + gamma(z) + sage: latex(gamma1(z)) + \Gamma\left(z\right) + + Test that :trac:`5556` is fixed:: + + sage: gamma1(3/4) + gamma(3/4) + + sage: gamma1(3/4).n(100) + 1.2254167024651776451290983034 + + Check that negative integer input works:: + + sage: (-1).gamma() + Infinity + sage: (-1.).gamma() + NaN + sage: CC(-1).gamma() + Infinity + sage: RDF(-1).gamma() + NaN + sage: CDF(-1).gamma() + Infinity + + Check if :trac:`8297` is fixed:: + + sage: latex(gamma(1/4)) + \Gamma\left(\frac{1}{4}\right) + + Test pickling:: + + sage: loads(dumps(gamma(x))) + gamma(x) + + Check that the implementations roughly agrees (note there might be + difference of several ulp on more complicated entries):: + + sage: import mpmath + sage: float(gamma(10.)) == gamma(10.r) == float(gamma(mpmath.mpf(10))) + True + sage: float(gamma(8.5)) == gamma(8.5r) == float(gamma(mpmath.mpf(8.5))) + True + + Check that ``QQbar`` half integers work with the ``pi`` formula:: + + sage: gamma(QQbar(1/2)) + sqrt(pi) + sage: gamma(QQbar(-9/2)) + -32/945*sqrt(pi) + + .. SEEALSO:: + + :meth:`gamma` + """ + GinacFunction.__init__(self, 'gamma', latex_name=r"\Gamma", + ginac_name='tgamma', + conversions={'mathematica':'Gamma', + 'maple':'GAMMA', + 'sympy':'gamma', + 'fricas':'Gamma', + 'giac':'Gamma'}) + +gamma1 = Function_gamma() + +class Function_log_gamma(GinacFunction): + def __init__(self): + r""" + The principal branch of the log gamma function. Note that for + `x < 0`, ``log(gamma(x))`` is not, in general, equal to + ``log_gamma(x)``. + + It is computed by the ``log_gamma`` function for the number type, + or by ``lgamma`` in Ginac, failing that. + + Gamma is defined for complex input `z` with real part greater + than zero, and by analytic continuation on the rest of the + complex plane (except for negative integers, which are poles). + + EXAMPLES: + + Numerical evaluation happens when appropriate, to the + appropriate accuracy (see :trac:`10072`):: + + sage: log_gamma(6) + log(120) + sage: log_gamma(6.) + 4.78749174278205 + sage: log_gamma(6).n() + 4.78749174278205 + sage: log_gamma(RealField(100)(6)) + 4.7874917427820459942477009345 + sage: log_gamma(2.4 + I) + -0.0308566579348816 + 0.693427705955790*I + sage: log_gamma(-3.1) + 0.400311696703985 - 12.5663706143592*I + sage: log_gamma(-1.1) == log(gamma(-1.1)) + False + + Symbolic input works (see :trac:`10075`):: + + sage: log_gamma(3*x) + log_gamma(3*x) + sage: log_gamma(3 + I) + log_gamma(I + 3) + sage: log_gamma(3 + I + x) + log_gamma(x + I + 3) + + Check that :trac:`12521` is fixed:: + + sage: log_gamma(-2.1) + 1.53171380819509 - 9.42477796076938*I + sage: log_gamma(CC(-2.1)) + 1.53171380819509 - 9.42477796076938*I + sage: log_gamma(-21/10).n() + 1.53171380819509 - 9.42477796076938*I + sage: exp(log_gamma(-1.3) + log_gamma(-0.4) - + ....: log_gamma(-1.3 - 0.4)).real_part() # beta(-1.3, -0.4) + -4.92909641669610 + + In order to prevent evaluation, use the ``hold`` argument; + to evaluate a held expression, use the ``n()`` numerical + evaluation method:: + + sage: log_gamma(SR(5), hold=True) + log_gamma(5) + sage: log_gamma(SR(5), hold=True).n() + 3.17805383034795 + + TESTS:: + + sage: log_gamma(-2.1 + I) + -1.90373724496982 - 7.18482377077183*I + sage: log_gamma(pari(6)) + 4.78749174278205 + sage: log_gamma(x)._sympy_() + loggamma(x) + sage: log_gamma(CC(6)) + 4.78749174278205 + sage: log_gamma(CC(-2.5)) + -0.0562437164976741 - 9.42477796076938*I + sage: log_gamma(RDF(-2.5)) + -0.056243716497674054 - 9.42477796076938*I + sage: log_gamma(CDF(-2.5)) + -0.056243716497674054 - 9.42477796076938*I + sage: log_gamma(float(-2.5)) + (-0.056243716497674054-9.42477796076938j) + sage: log_gamma(complex(-2.5)) + (-0.056243716497674054-9.42477796076938j) + + ``conjugate(log_gamma(x)) == log_gamma(conjugate(x))`` unless on the + branch cut, which runs along the negative real axis.:: + + sage: conjugate(log_gamma(x)) + conjugate(log_gamma(x)) + sage: var('y', domain='positive') + y + sage: conjugate(log_gamma(y)) + log_gamma(y) + sage: conjugate(log_gamma(y + I)) + conjugate(log_gamma(y + I)) + sage: log_gamma(-2) + +Infinity + sage: conjugate(log_gamma(-2)) + +Infinity + """ + GinacFunction.__init__(self, "log_gamma", latex_name=r'\log\Gamma', + conversions=dict(mathematica='LogGamma', + maxima='log_gamma', + sympy='loggamma', + fricas='logGamma')) + +log_gamma = Function_log_gamma() + +class Function_gamma_inc(BuiltinFunction): + def __init__(self): + r""" + The upper incomplete gamma function. + + It is defined by the integral + + .. MATH:: + + \Gamma(a,z)=\int_z^\infty t^{a-1}e^{-t}\,\mathrm{d}t + + EXAMPLES:: + + sage: gamma_inc(CDF(0,1), 3) + 0.0032085749933691158 + 0.012406185811871568*I + sage: gamma_inc(RDF(1), 3) + 0.049787068367863944 + sage: gamma_inc(3,2) + gamma(3, 2) + sage: gamma_inc(x,0) + gamma(x) + sage: latex(gamma_inc(3,2)) + \Gamma\left(3, 2\right) + sage: loads(dumps((gamma_inc(3,2)))) + gamma(3, 2) + sage: i = ComplexField(30).0; gamma_inc(2, 1 + i) + 0.70709210 - 0.42035364*I + sage: gamma_inc(2., 5) + 0.0404276819945128 + sage: x,y=var('x,y') + sage: gamma_inc(x,y).diff(x) + diff(gamma(x, y), x) + sage: (gamma_inc(x,x+1).diff(x)).simplify() + -(x + 1)^(x - 1)*e^(-x - 1) + D[0](gamma)(x, x + 1) + + TESTS: + + Check that :trac:`21407` is fixed:: + + sage: gamma(-1,5)._sympy_() + expint(2, 5)/5 + sage: gamma(-3/2,5)._sympy_() + -6*sqrt(5)*exp(-5)/25 + 4*sqrt(pi)*erfc(sqrt(5))/3 + + .. SEEALSO:: + + :meth:`gamma` + """ + BuiltinFunction.__init__(self, "gamma", nargs=2, latex_name=r"\Gamma", + conversions={'maxima':'gamma_incomplete', 'mathematica':'Gamma', + 'maple':'GAMMA', 'sympy':'uppergamma', 'giac':'ugamma'}) + + def _eval_(self, x, y): + """ + EXAMPLES:: + + sage: gamma_inc(2.,0) + 1.00000000000000 + sage: gamma_inc(2,0) + 1 + sage: gamma_inc(1/2,2) + -sqrt(pi)*(erf(sqrt(2)) - 1) + sage: gamma_inc(1/2,1) + -sqrt(pi)*(erf(1) - 1) + sage: gamma_inc(1/2,0) + sqrt(pi) + sage: gamma_inc(x,0) + gamma(x) + sage: gamma_inc(1,2) + e^(-2) + sage: gamma_inc(0,2) + -Ei(-2) + """ + if y == 0: + return gamma(x) + if x == 1: + return exp(-y) + if x == 0: + return -Ei(-y) + if x == Rational(1)/2: #only for x>0 + from sage.functions.error import erf + return sqrt(pi)*(1-erf(sqrt(y))) + return None + + def _evalf_(self, x, y, parent=None, algorithm='pari'): + """ + EXAMPLES:: + + sage: gamma_inc(0,2) + -Ei(-2) + sage: gamma_inc(0,2.) + 0.0489005107080611 + sage: gamma_inc(0,2).n(algorithm='pari') + 0.0489005107080611 + sage: gamma_inc(0,2).n(200) + 0.048900510708061119567239835228... + sage: gamma_inc(3,2).n() + 1.35335283236613 + + TESTS: + + Check that :trac:`7099` is fixed:: + + sage: R = RealField(1024) + sage: gamma(R(9), R(10^-3)) # rel tol 1e-308 + 40319.99999999999999999999999999988898884344822911869926361916294165058203634104838326009191542490601781777105678829520585311300510347676330951251563007679436243294653538925717144381702105700908686088851362675381239820118402497959018315224423868693918493033078310647199219674433536605771315869983788442389633 + sage: numerical_approx(gamma(9, 10^(-3)) - gamma(9), digits=40) # abs tol 1e-36 + -1.110111598370794007949063502542063148294e-28 + + Check that :trac:`17328` is fixed:: + + sage: gamma_inc(float(-1), float(-1)) + (-0.8231640121031085+3.141592653589793j) + sage: gamma_inc(RR(-1), RR(-1)) + -0.823164012103109 + 3.14159265358979*I + sage: gamma_inc(-1, float(-log(3))) - gamma_inc(-1, float(-log(2))) # abs tol 1e-15 + (1.2730972164471142+0j) + + Check that :trac:`17130` is fixed:: + + sage: r = gamma_inc(float(0), float(1)); r + 0.21938393439552029 + sage: type(r) + <... 'float'> + """ + R = parent or s_parent(x) + # C is the complex version of R + # prec is the precision of R + if R is float: + prec = 53 + C = complex + else: + try: + prec = R.precision() + except AttributeError: + prec = 53 + try: + C = R.complex_field() + except AttributeError: + C = R + + if algorithm == 'pari': + v = ComplexField(prec)(x).gamma_inc(y) + else: + import mpmath + v = ComplexField(prec)(mpmath_utils.call(mpmath.gammainc, x, y, parent=R)) + if v.is_real(): + return R(v) + else: + return C(v) + +# synonym. +gamma_inc = Function_gamma_inc() + +class Function_gamma_inc_lower(BuiltinFunction): + def __init__(self): + r""" + The lower incomplete gamma function. + + It is defined by the integral + + .. MATH:: + + \Gamma(a,z)=\int_0^z t^{a-1}e^{-t}\,\mathrm{d}t + + EXAMPLES:: + + sage: gamma_inc_lower(CDF(0,1), 3) + -0.1581584032951798 - 0.5104218539302277*I + sage: gamma_inc_lower(RDF(1), 3) + 0.950212931632136 + sage: gamma_inc_lower(3, 2, hold=True) + gamma_inc_lower(3, 2) + sage: gamma_inc_lower(3, 2) + -10*e^(-2) + 2 + sage: gamma_inc_lower(x, 0) + 0 + sage: latex(gamma_inc_lower(x, x)) + \gamma\left(x, x\right) + sage: loads(dumps((gamma_inc_lower(x, x)))) + gamma_inc_lower(x, x) + sage: i = ComplexField(30).0; gamma_inc_lower(2, 1 + i) + 0.29290790 + 0.42035364*I + sage: gamma_inc_lower(2., 5) + 0.959572318005487 + + Interfaces to other software:: + + sage: gamma_inc_lower(x,x)._sympy_() + lowergamma(x, x) + sage: maxima(gamma_inc_lower(x,x)) + gamma_greek(_SAGE_VAR_x,_SAGE_VAR_x) + + .. SEEALSO:: + + :class:`Function_gamma_inc` + """ + BuiltinFunction.__init__(self, "gamma_inc_lower", nargs=2, latex_name=r"\gamma", + conversions={'maxima':'gamma_greek', 'mathematica':'Gamma', + 'maple':'GAMMA', 'sympy':'lowergamma', 'giac':'igamma'}) + + def _eval_(self, x, y): + """ + EXAMPLES:: + + sage: gamma_inc_lower(2.,0) + 0.000000000000000 + sage: gamma_inc_lower(2,0) + 0 + sage: gamma_inc_lower(1/2,2) + sqrt(pi)*erf(sqrt(2)) + sage: gamma_inc_lower(1/2,1) + sqrt(pi)*erf(1) + sage: gamma_inc_lower(1/2,0) + 0 + sage: gamma_inc_lower(x,0) + 0 + sage: gamma_inc_lower(1,2) + -e^(-2) + 1 + sage: gamma_inc_lower(0,2) + +Infinity + sage: gamma_inc_lower(2,377/79) + -456/79*e^(-377/79) + 1 + sage: gamma_inc_lower(3,x) + -x^2*e^(-x) - 2*x*e^(-x) - 2*e^(-x) + 2 + sage: gamma_inc_lower(9/2,37/7) + 105/16*sqrt(pi)*erf(1/7*sqrt(259)) - 836473/19208*sqrt(259)*e^(-37/7) + """ + if y == 0: + return 0 + if x == 0: + from sage.rings.infinity import Infinity + return Infinity + elif x == 1: + return 1-exp(-y) + elif (2*x).is_integer(): + return self(x,y,hold=True)._sympy_() + else: + return None + + def _evalf_(self, x, y, parent=None, algorithm='mpmath'): + """ + EXAMPLES:: + + sage: gamma_inc_lower(3,2.) + 0.646647167633873 + sage: gamma_inc_lower(3,2).n(200) + 0.646647167633873081060005050275155... + sage: gamma_inc_lower(0,2.) + +infinity + """ + R = parent or s_parent(x) + # C is the complex version of R + # prec is the precision of R + if R is float: + prec = 53 + C = complex + else: + try: + prec = R.precision() + except AttributeError: + prec = 53 + try: + C = R.complex_field() + except AttributeError: + C = R + if algorithm == 'pari': + try: + v = ComplexField(prec)(x).gamma() - ComplexField(prec)(x).gamma_inc(y) + except AttributeError: + if not (is_ComplexNumber(x)): + if is_ComplexNumber(y): + C = y.parent() + else: + C = ComplexField() + x = C(x) + v = ComplexField(prec)(x).gamma() - ComplexField(prec)(x).gamma_inc(y) + else: + import mpmath + v = ComplexField(prec)(mpmath_utils.call(mpmath.gammainc, x, 0, y, parent=R)) + if v.is_real(): + return R(v) + else: + return C(v) + + def _derivative_(self, x, y, diff_param=None): + """ + EXAMPLES:: + + sage: x,y = var('x,y') + sage: gamma_inc_lower(x,y).diff(y) + y^(x - 1)*e^(-y) + sage: gamma_inc_lower(x,y).diff(x) + Traceback (most recent call last): + ... + NotImplementedError: cannot differentiate gamma_inc_lower in the first parameter + """ + if diff_param == 0: + raise NotImplementedError("cannot differentiate gamma_inc_lower in the" + " first parameter") + else: + return exp(-y)*y**(x - 1) + +# synonym. +gamma_inc_lower = Function_gamma_inc_lower() + +def gamma(a, *args, **kwds): + r""" + Gamma and upper incomplete gamma functions in one symbol. + + Recall that `\Gamma(n)` is `n-1` factorial:: + + sage: gamma(11) == factorial(10) + True + sage: gamma(6) + 120 + sage: gamma(1/2) + sqrt(pi) + sage: gamma(-4/3) + gamma(-4/3) + sage: gamma(-1) + Infinity + sage: gamma(0) + Infinity + + :: + + sage: gamma_inc(3,2) + gamma(3, 2) + sage: gamma_inc(x,0) + gamma(x) + + :: + + sage: gamma(5, hold=True) + gamma(5) + sage: gamma(x, 0, hold=True) + gamma(x, 0) + + :: + + sage: gamma(CDF(I)) + -0.15494982830181067 - 0.49801566811835607*I + sage: gamma(CDF(0.5,14)) + -4.0537030780372815e-10 - 5.773299834553605e-10*I + + Use ``numerical_approx`` to get higher precision from + symbolic expressions:: + + sage: gamma(pi).n(100) + 2.2880377953400324179595889091 + sage: gamma(3/4).n(100) + 1.2254167024651776451290983034 + + The precision for the result is also deduced from the precision of the + input. Convert the input to a higher precision explicitly if a result + with higher precision is desired.:: + + sage: t = gamma(RealField(100)(2.5)); t + 1.3293403881791370204736256125 + sage: t.prec() + 100 + + The gamma function only works with input that can be coerced to the + Symbolic Ring:: + + sage: Q. = NumberField(x^2+1) + sage: gamma(i) + Traceback (most recent call last): + ... + TypeError: cannot coerce arguments: no canonical coercion from Number Field in i with defining polynomial x^2 + 1 to Symbolic Ring + + .. SEEALSO:: + + :class:`Function_gamma` + """ + if not args: + return gamma1(a, **kwds) + if len(args) > 1: + raise TypeError("Symbolic function gamma takes at most 2 arguments (%s given)"%(len(args)+1)) + return gamma_inc(a,args[0],**kwds) + +def incomplete_gamma(*args, **kwds): + """ + Deprecated name for :class:`Function_gamma_inc`. + + TESTS:: + + sage: incomplete_gamma(1,1) + doctest:...: DeprecationWarning: Please use gamma_inc(). + See http://trac.sagemath.org/16697 for details. + e^(-1) + """ + from sage.misc.superseded import deprecation + deprecation(16697, 'Please use gamma_inc().') + return gamma_inc(*args, **kwds) + +# We have to add the wrapper function manually to the symbol_table when we have +# two functions with different number of arguments and the same name +symbol_table['functions']['gamma'] = gamma + +class Function_psi1(GinacFunction): + def __init__(self): + r""" + The digamma function, `\psi(x)`, is the logarithmic derivative of the + gamma function. + + .. MATH:: + + \psi(x) = \frac{d}{dx} \log(\Gamma(x)) = \frac{\Gamma'(x)}{\Gamma(x)} + + EXAMPLES:: + + sage: from sage.functions.gamma import psi1 + sage: psi1(x) + psi(x) + sage: psi1(x).derivative(x) + psi(1, x) + + :: + + sage: psi1(3) + -euler_gamma + 3/2 + + :: + + sage: psi(.5) + -1.96351002602142 + sage: psi(RealField(100)(.5)) + -1.9635100260214234794409763330 + + TESTS:: + + sage: latex(psi1(x)) + \psi\left(x\right) + sage: loads(dumps(psi1(x)+1)) + psi(x) + 1 + + sage: t = psi1(x); t + psi(x) + sage: t.subs(x=.2) + -5.28903989659219 + sage: psi(x)._sympy_() + polygamma(0, x) + """ + GinacFunction.__init__(self, "psi", nargs=1, latex_name='\psi', + conversions=dict(mathematica='PolyGamma', + maxima='psi[0]', + sympy='digamma')) + +class Function_psi2(GinacFunction): + def __init__(self): + r""" + Derivatives of the digamma function `\psi(x)`. T + + EXAMPLES:: + + sage: from sage.functions.gamma import psi2 + sage: psi2(2, x) + psi(2, x) + sage: psi2(2, x).derivative(x) + psi(3, x) + sage: n = var('n') + sage: psi2(n, x).derivative(x) + psi(n + 1, x) + + :: + + sage: psi2(0, x) + psi(x) + sage: psi2(-1, x) + log(gamma(x)) + sage: psi2(3, 1) + 1/15*pi^4 + + :: + + sage: psi2(2, .5).n() + -16.8287966442343 + sage: psi2(2, .5).n(100) + -16.828796644234319995596334261 + + TESTS:: + + sage: psi2(n, x).derivative(n) + Traceback (most recent call last): + ... + RuntimeError: cannot diff psi(n,x) with respect to n + + sage: latex(psi2(2,x)) + \psi\left(2, x\right) + sage: loads(dumps(psi2(2,x)+1)) + psi(2, x) + 1 + sage: psi(2, x)._sympy_() + polygamma(2, x) + """ + GinacFunction.__init__(self, "psi", nargs=2, latex_name='\psi', + conversions=dict(mathematica='PolyGamma', + sympy='polygamma', + giac='Psi')) + + def _maxima_init_evaled_(self, *args): + """ + EXAMPLES: + + These are indirect doctests for this function.:: + + sage: from sage.functions.gamma import psi2 + sage: psi2(2, x)._maxima_() + psi[2](_SAGE_VAR_x) + sage: psi2(4, x)._maxima_() + psi[4](_SAGE_VAR_x) + """ + args_maxima = [] + for a in args: + if isinstance(a, str): + args_maxima.append(a) + elif hasattr(a, '_maxima_init_'): + args_maxima.append(a._maxima_init_()) + else: + args_maxima.append(str(a)) + n, x = args_maxima + return "psi[%s](%s)"%(n, x) + +psi1 = Function_psi1() +psi2 = Function_psi2() + +def psi(x, *args, **kwds): + r""" + The digamma function, `\psi(x)`, is the logarithmic derivative of the + gamma function. + + .. MATH:: + + \psi(x) = \frac{d}{dx} \log(\Gamma(x)) = \frac{\Gamma'(x)}{\Gamma(x)} + + We represent the `n`-th derivative of the digamma function with + `\psi(n, x)` or `psi(n, x)`. + + EXAMPLES:: + + sage: psi(x) + psi(x) + sage: psi(.5) + -1.96351002602142 + sage: psi(3) + -euler_gamma + 3/2 + sage: psi(1, 5) + 1/6*pi^2 - 205/144 + sage: psi(1, x) + psi(1, x) + sage: psi(1, x).derivative(x) + psi(2, x) + + :: + + sage: psi(3, hold=True) + psi(3) + sage: psi(1, 5, hold=True) + psi(1, 5) + + TESTS:: + + sage: psi(2, x, 3) + Traceback (most recent call last): + ... + TypeError: Symbolic function psi takes at most 2 arguments (3 given) + """ + if not args: + return psi1(x, **kwds) + if len(args) > 1: + raise TypeError("Symbolic function psi takes at most 2 arguments (%s given)"%(len(args)+1)) + return psi2(x,args[0],**kwds) + +# We have to add the wrapper function manually to the symbol_table when we have +# two functions with different number of arguments and the same name +symbol_table['functions']['psi'] = psi + +def _swap_psi(a, b): return psi(b, a) +register_symbol(_swap_psi, {'giac':'Psi'}) + +class Function_beta(GinacFunction): + def __init__(self): + r""" + Return the beta function. This is defined by + + .. MATH:: + + \operatorname{B}(p,q) = \int_0^1 t^{p-1}(1-t)^{q-1} dt + + for complex or symbolic input `p` and `q`. + Note that the order of inputs does not matter: + `\operatorname{B}(p,q)=\operatorname{B}(q,p)`. + + GiNaC is used to compute `\operatorname{B}(p,q)`. However, complex inputs + are not yet handled in general. When GiNaC raises an error on + such inputs, we raise a NotImplementedError. + + If either input is 1, GiNaC returns the reciprocal of the + other. In other cases, GiNaC uses one of the following + formulas: + + .. MATH:: + + \operatorname{B}(p,q) = \frac{\Gamma(p)\Gamma(q)}{\Gamma(p+q)} + + or + + .. MATH:: + + \operatorname{B}(p,q) = (-1)^q \operatorname{B}(1-p-q, q). + + + For numerical inputs, GiNaC uses the formula + + .. MATH:: + + \operatorname{B}(p,q) = \exp[\log\Gamma(p)+\log\Gamma(q)-\log\Gamma(p+q)] + + + INPUT: + + - ``p`` - number or symbolic expression + + - ``q`` - number or symbolic expression + + + OUTPUT: number or symbolic expression (if input is symbolic) + + EXAMPLES:: + + sage: beta(3,2) + 1/12 + sage: beta(3,1) + 1/3 + sage: beta(1/2,1/2) + beta(1/2, 1/2) + sage: beta(-1,1) + -1 + sage: beta(-1/2,-1/2) + 0 + sage: ex = beta(x/2,3) + sage: set(ex.operands()) == set([1/2*x, 3]) + True + sage: beta(.5,.5) + 3.14159265358979 + sage: beta(1,2.0+I) + 0.400000000000000 - 0.200000000000000*I + sage: ex = beta(3,x+I) + sage: set(ex.operands()) == set([x+I, 3]) + True + + The result is symbolic if exact input is given:: + + sage: ex = beta(2,1+5*I); ex + beta(... + sage: set(ex.operands()) == set([1+5*I, 2]) + True + sage: beta(2, 2.) + 0.166666666666667 + sage: beta(I, 2.) + -0.500000000000000 - 0.500000000000000*I + sage: beta(2., 2) + 0.166666666666667 + sage: beta(2., I) + -0.500000000000000 - 0.500000000000000*I + + sage: beta(x, x)._sympy_() + beta(x, x) + + Test pickling:: + + sage: loads(dumps(beta)) + beta + + Check that :trac:`15196` is fixed:: + + sage: beta(-1.3,-0.4) + -4.92909641669610 + """ + GinacFunction.__init__(self, 'beta', nargs=2, + latex_name=r"\operatorname{B}", + conversions=dict(maxima='beta', + mathematica='Beta', + sympy='beta', + fricas='Beta', + giac='Beta')) + +beta = Function_beta() + diff --git a/src/sage/functions/hypergeometric.py b/src/sage/functions/hypergeometric.py index 6fd2ab8d283..fea6e98962f 100644 --- a/src/sage/functions/hypergeometric.py +++ b/src/sage/functions/hypergeometric.py @@ -168,11 +168,6 @@ from sage.rings.rational_field import QQ from sage.rings.infinity import Infinity from sage.arith.all import binomial, rising_factorial, factorial -from sage.functions.other import sqrt, gamma, real_part -from sage.functions.log import exp, log -from sage.functions.trig import sin -from sage.functions.hyperbolic import cosh, sinh -from sage.functions.error import erf from sage.symbolic.constants import pi from sage.symbolic.all import I from sage.symbolic.function import BuiltinFunction @@ -185,6 +180,12 @@ from sage.calculus.functional import derivative from functools import reduce +from .gamma import gamma +from .other import sqrt, real_part +from .log import exp, log +from .trig import sin +from .hyperbolic import cosh, sinh +from .error import erf def rational_param_as_tuple(x): r""" diff --git a/src/sage/functions/log.py b/src/sage/functions/log.py index 6beef456b2f..0cfd77954b6 100644 --- a/src/sage/functions/log.py +++ b/src/sage/functions/log.py @@ -1384,7 +1384,7 @@ def _eval_(self, z, **kwds): import sage.libs.flint.arith as flint_arith return flint_arith.harmonic_number(z) elif z in QQ: - from sage.functions.other import psi1 + from .gamma import psi1 return psi1(z+1) - psi1(1) def _evalf_(self, z, parent=None, algorithm='mpmath'): diff --git a/src/sage/functions/orthogonal_polys.py b/src/sage/functions/orthogonal_polys.py index c877454997a..a98dcf28347 100644 --- a/src/sage/functions/orthogonal_polys.py +++ b/src/sage/functions/orthogonal_polys.py @@ -1354,8 +1354,9 @@ def _eval_special_values_(self, n, x): return SR(unsigned_infinity) if x == 0: - from sage.functions.other import gamma, sqrt - from sage.functions.trig import sin + from .gamma import gamma + from .other import sqrt + from .trig import sin try: gam = gamma((n+1)/2)/gamma(n/2 + 1) if gam.is_infinity(): @@ -1566,8 +1567,9 @@ def _eval_special_values_(self, n, m, x): if n == m: return factorial(2*m)/2**m/factorial(m) * (x**2-1)**(m/2) if x == 0: - from sage.functions.other import gamma, sqrt - from sage.functions.trig import cos + from .gamma import gamma + from .other import sqrt + from .trig import cos if m in QQ and n in QQ: return 2**m/sqrt(SR.pi())*cos((n+m)/2*SR.pi())*(gamma(QQ(n+m+1)/2)/gamma(QQ(n-m)/2+1)) elif isinstance(n, Expression) or isinstance(m, Expression): @@ -1689,8 +1691,9 @@ def _eval_special_values_(self, n, m, x): if m == 0: return legendre_Q(n, x) if x.is_zero(): - from sage.functions.other import gamma, sqrt - from sage.functions.trig import sin + from .gamma import gamma + from .other import sqrt + from .trig import sin if m in QQ and n in QQ: return -(sqrt(SR.pi()))*sin(SR.pi()/2*(m+n))*gamma(QQ(m+n+1)/2)/gamma(QQ(n-m)/2 + 1)*2**(m-1) elif isinstance(n, Expression) or isinstance(m, Expression): @@ -1923,7 +1926,7 @@ def _eval_(self, n, a, b, x): raise ValueError("n must be greater than -1, got n = {0}".format(n)) if not n in ZZ: return - from sage.functions.other import gamma + from .gamma import gamma s = sum(binomial(n,m) * gamma(a+b+n+m+1) / gamma(a+m+1) * ((x-1)/2)**m for m in range(n+1)) r = gamma(a+n+1) / factorial(n) / gamma(n+a+b+1) * s return r.to_gamma().gamma_normalize().normalize() diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 78c6cb7f978..b5c532fff0f 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -690,862 +690,565 @@ def _eval_(self, x): frac = Function_frac() - -class Function_gamma(GinacFunction): - def __init__(self): +def _do_sqrt(x, prec=None, extend=True, all=False): r""" - The Gamma function. This is defined by - - .. MATH:: - - \Gamma(z) = \int_0^\infty t^{z-1}e^{-t} dt - - for complex input `z` with real part greater than zero, and by - analytic continuation on the rest of the complex plane (except - for negative integers, which are poles). - - It is computed by various libraries within Sage, depending on - the input type. - - EXAMPLES:: - - sage: from sage.functions.other import gamma1 - sage: gamma1(CDF(0.5,14)) - -4.0537030780372815e-10 - 5.773299834553605e-10*I - sage: gamma1(CDF(I)) - -0.15494982830181067 - 0.49801566811835607*I - - Recall that `\Gamma(n)` is `n-1` factorial:: - - sage: gamma1(11) == factorial(10) - True - sage: gamma1(6) - 120 - sage: gamma1(1/2) - sqrt(pi) - sage: gamma1(-1) - Infinity - sage: gamma1(I) - gamma(I) - sage: gamma1(x/2)(x=5) - 3/4*sqrt(pi) - - sage: gamma1(float(6)) # For ARM: rel tol 3e-16 - 120.0 - sage: gamma(6.) - 120.000000000000 - sage: gamma1(x) - gamma(x) - - :: - - sage: gamma1(pi) - gamma(pi) - sage: gamma1(i) - gamma(I) - sage: gamma1(i).n() - -0.154949828301811 - 0.498015668118356*I - sage: gamma1(int(5)) - 24 - - :: - - sage: conjugate(gamma(x)) - gamma(conjugate(x)) - - :: - - sage: plot(gamma1(x),(x,1,5)) - Graphics object consisting of 1 graphics primitive - - To prevent automatic evaluation use the ``hold`` argument:: - - sage: gamma1(1/2,hold=True) - gamma(1/2) - - To then evaluate again, we currently must use Maxima via - :meth:`sage.symbolic.expression.Expression.simplify`:: - - sage: gamma1(1/2,hold=True).simplify() - sqrt(pi) - - TESTS: - - sage: gamma(x)._sympy_() - gamma(x) - - We verify that we can convert this function to Maxima and - convert back to Sage:: - - sage: z = var('z') - sage: maxima(gamma1(z)).sage() - gamma(z) - sage: latex(gamma1(z)) - \Gamma\left(z\right) - - Test that :trac:`5556` is fixed:: - - sage: gamma1(3/4) - gamma(3/4) - - sage: gamma1(3/4).n(100) - 1.2254167024651776451290983034 - - Check that negative integer input works:: + Used internally to compute the square root of x. - sage: (-1).gamma() - Infinity - sage: (-1.).gamma() - NaN - sage: CC(-1).gamma() - Infinity - sage: RDF(-1).gamma() - NaN - sage: CDF(-1).gamma() - Infinity + INPUT: - Check if :trac:`8297` is fixed:: + - ``x`` - a number - sage: latex(gamma(1/4)) - \Gamma\left(\frac{1}{4}\right) + - ``prec`` - None (default) or a positive integer + (bits of precision) If not None, then compute the square root + numerically to prec bits of precision. - Test pickling:: + - ``extend`` - bool (default: True); this is a place + holder, and is always ignored since in the symbolic ring everything + has a square root. - sage: loads(dumps(gamma(x))) - gamma(x) + - ``extend`` - bool (default: True); whether to extend + the base ring to find roots. The extend parameter is ignored if + prec is a positive integer. - Check that the implementations roughly agrees (note there might be - difference of several ulp on more complicated entries):: + - ``all`` - bool (default: False); whether to return + a list of all the square roots of x. - sage: import mpmath - sage: float(gamma(10.)) == gamma(10.r) == float(gamma(mpmath.mpf(10))) - True - sage: float(gamma(8.5)) == gamma(8.5r) == float(gamma(mpmath.mpf(8.5))) - True - Check that ``QQbar`` half integers work with the ``pi`` formula:: + EXAMPLES:: - sage: gamma(QQbar(1/2)) - sqrt(pi) - sage: gamma(QQbar(-9/2)) - -32/945*sqrt(pi) + sage: from sage.functions.other import _do_sqrt + sage: _do_sqrt(3) + sqrt(3) + sage: _do_sqrt(3,prec=10) + 1.7 + sage: _do_sqrt(3,prec=100) + 1.7320508075688772935274463415 + sage: _do_sqrt(3,all=True) + [sqrt(3), -sqrt(3)] - .. SEEALSO:: + Note that the extend parameter is ignored in the symbolic ring:: - :meth:`sage.functions.other.gamma` + sage: _do_sqrt(3,extend=False) + sqrt(3) """ - GinacFunction.__init__(self, 'gamma', latex_name=r"\Gamma", - ginac_name='tgamma', - conversions={'mathematica':'Gamma', - 'maple':'GAMMA', - 'sympy':'gamma', - 'fricas':'Gamma', - 'giac':'Gamma'}) + if prec: + if x >= 0: + return RealField(prec)(x).sqrt(all=all) + else: + return ComplexField(prec)(x).sqrt(all=all) + if x == -1: + z = I + else: + z = SR(x) ** one_half -gamma1 = Function_gamma() + if all: + if z: + return [z, -z] + else: + return [z] + return z -class Function_log_gamma(GinacFunction): - def __init__(self): +def sqrt(x, *args, **kwds): r""" - The principal branch of the log gamma function. Note that for - `x < 0`, ``log(gamma(x))`` is not, in general, equal to - ``log_gamma(x)``. - - It is computed by the ``log_gamma`` function for the number type, - or by ``lgamma`` in Ginac, failing that. - - Gamma is defined for complex input `z` with real part greater - than zero, and by analytic continuation on the rest of the - complex plane (except for negative integers, which are poles). - - EXAMPLES: - - Numerical evaluation happens when appropriate, to the - appropriate accuracy (see :trac:`10072`):: - - sage: log_gamma(6) - log(120) - sage: log_gamma(6.) - 4.78749174278205 - sage: log_gamma(6).n() - 4.78749174278205 - sage: log_gamma(RealField(100)(6)) - 4.7874917427820459942477009345 - sage: log_gamma(2.4 + I) - -0.0308566579348816 + 0.693427705955790*I - sage: log_gamma(-3.1) - 0.400311696703985 - 12.5663706143592*I - sage: log_gamma(-1.1) == log(gamma(-1.1)) - False - - Symbolic input works (see :trac:`10075`):: - - sage: log_gamma(3*x) - log_gamma(3*x) - sage: log_gamma(3 + I) - log_gamma(I + 3) - sage: log_gamma(3 + I + x) - log_gamma(x + I + 3) - - Check that :trac:`12521` is fixed:: - - sage: log_gamma(-2.1) - 1.53171380819509 - 9.42477796076938*I - sage: log_gamma(CC(-2.1)) - 1.53171380819509 - 9.42477796076938*I - sage: log_gamma(-21/10).n() - 1.53171380819509 - 9.42477796076938*I - sage: exp(log_gamma(-1.3) + log_gamma(-0.4) - - ....: log_gamma(-1.3 - 0.4)).real_part() # beta(-1.3, -0.4) - -4.92909641669610 - - In order to prevent evaluation, use the ``hold`` argument; - to evaluate a held expression, use the ``n()`` numerical - evaluation method:: - - sage: log_gamma(SR(5), hold=True) - log_gamma(5) - sage: log_gamma(SR(5), hold=True).n() - 3.17805383034795 - - TESTS:: + INPUT: - sage: log_gamma(-2.1 + I) - -1.90373724496982 - 7.18482377077183*I - sage: log_gamma(pari(6)) - 4.78749174278205 - sage: log_gamma(x)._sympy_() - loggamma(x) - sage: log_gamma(CC(6)) - 4.78749174278205 - sage: log_gamma(CC(-2.5)) - -0.0562437164976741 - 9.42477796076938*I - sage: log_gamma(RDF(-2.5)) - -0.056243716497674054 - 9.42477796076938*I - sage: log_gamma(CDF(-2.5)) - -0.056243716497674054 - 9.42477796076938*I - sage: log_gamma(float(-2.5)) - (-0.056243716497674054-9.42477796076938j) - sage: log_gamma(complex(-2.5)) - (-0.056243716497674054-9.42477796076938j) - - ``conjugate(log_gamma(x)) == log_gamma(conjugate(x))`` unless on the - branch cut, which runs along the negative real axis.:: - - sage: conjugate(log_gamma(x)) - conjugate(log_gamma(x)) - sage: var('y', domain='positive') - y - sage: conjugate(log_gamma(y)) - log_gamma(y) - sage: conjugate(log_gamma(y + I)) - conjugate(log_gamma(y + I)) - sage: log_gamma(-2) - +Infinity - sage: conjugate(log_gamma(-2)) - +Infinity - """ - GinacFunction.__init__(self, "log_gamma", latex_name=r'\log\Gamma', - conversions=dict(mathematica='LogGamma', - maxima='log_gamma', - sympy='loggamma', - fricas='logGamma')) - -log_gamma = Function_log_gamma() - -class Function_gamma_inc(BuiltinFunction): - def __init__(self): - r""" - The upper incomplete gamma function. + - ``x`` - a number - It is defined by the integral + - ``prec`` - integer (default: None): if None, returns + an exact square root; otherwise returns a numerical square root if + necessary, to the given bits of precision. - .. MATH:: + - ``extend`` - bool (default: True); this is a place + holder, and is always ignored or passed to the sqrt function for x, + since in the symbolic ring everything has a square root. - \Gamma(a,z)=\int_z^\infty t^{a-1}e^{-t}\,\mathrm{d}t + - ``all`` - bool (default: False); if True, return all + square roots of self, instead of just one. EXAMPLES:: - sage: gamma_inc(CDF(0,1), 3) - 0.0032085749933691158 + 0.012406185811871568*I - sage: gamma_inc(RDF(1), 3) - 0.049787068367863944 - sage: gamma_inc(3,2) - gamma(3, 2) - sage: gamma_inc(x,0) - gamma(x) - sage: latex(gamma_inc(3,2)) - \Gamma\left(3, 2\right) - sage: loads(dumps((gamma_inc(3,2)))) - gamma(3, 2) - sage: i = ComplexField(30).0; gamma_inc(2, 1 + i) - 0.70709210 - 0.42035364*I - sage: gamma_inc(2., 5) - 0.0404276819945128 - sage: x,y=var('x,y') - sage: gamma_inc(x,y).diff(x) - diff(gamma(x, y), x) - sage: (gamma_inc(x,x+1).diff(x)).simplify() - -(x + 1)^(x - 1)*e^(-x - 1) + D[0](gamma)(x, x + 1) - - TESTS: - - Check that :trac:`21407` is fixed:: - - sage: gamma(-1,5)._sympy_() - expint(2, 5)/5 - sage: gamma(-3/2,5)._sympy_() - -6*sqrt(5)*exp(-5)/25 + 4*sqrt(pi)*erfc(sqrt(5))/3 - - .. SEEALSO:: - - :meth:`sage.functions.other.gamma` - """ - BuiltinFunction.__init__(self, "gamma", nargs=2, latex_name=r"\Gamma", - conversions={'maxima':'gamma_incomplete', 'mathematica':'Gamma', - 'maple':'GAMMA', 'sympy':'uppergamma', 'giac':'ugamma'}) - - def _eval_(self, x, y): - """ - EXAMPLES:: + sage: sqrt(-1) + I + sage: sqrt(2) + sqrt(2) + sage: sqrt(2)^2 + 2 + sage: sqrt(4) + 2 + sage: sqrt(4,all=True) + [2, -2] + sage: sqrt(x^2) + sqrt(x^2) - sage: gamma_inc(2.,0) - 1.00000000000000 - sage: gamma_inc(2,0) - 1 - sage: gamma_inc(1/2,2) - -sqrt(pi)*(erf(sqrt(2)) - 1) - sage: gamma_inc(1/2,1) - -sqrt(pi)*(erf(1) - 1) - sage: gamma_inc(1/2,0) - sqrt(pi) - sage: gamma_inc(x,0) - gamma(x) - sage: gamma_inc(1,2) - e^(-2) - sage: gamma_inc(0,2) - -Ei(-2) - """ - if y == 0: - return gamma(x) - if x == 1: - return exp(-y) - if x == 0: - return -Ei(-y) - if x == Rational(1)/2: #only for x>0 - from sage.functions.error import erf - return sqrt(pi)*(1-erf(sqrt(y))) - return None + For a non-symbolic square root, there are a few options. + The best is to numerically approximate afterward:: - def _evalf_(self, x, y, parent=None, algorithm='pari'): - """ - EXAMPLES:: + sage: sqrt(2).n() + 1.41421356237310 + sage: sqrt(2).n(prec=100) + 1.4142135623730950488016887242 - sage: gamma_inc(0,2) - -Ei(-2) - sage: gamma_inc(0,2.) - 0.0489005107080611 - sage: gamma_inc(0,2).n(algorithm='pari') - 0.0489005107080611 - sage: gamma_inc(0,2).n(200) - 0.048900510708061119567239835228... - sage: gamma_inc(3,2).n() - 1.35335283236613 + Or one can input a numerical type. - TESTS: + sage: sqrt(2.) + 1.41421356237310 + sage: sqrt(2.000000000000000000000000) + 1.41421356237309504880169 + sage: sqrt(4.0) + 2.00000000000000 - Check that :trac:`7099` is fixed:: + To prevent automatic evaluation, one can use the ``hold`` parameter + after coercing to the symbolic ring:: - sage: R = RealField(1024) - sage: gamma(R(9), R(10^-3)) # rel tol 1e-308 - 40319.99999999999999999999999999988898884344822911869926361916294165058203634104838326009191542490601781777105678829520585311300510347676330951251563007679436243294653538925717144381702105700908686088851362675381239820118402497959018315224423868693918493033078310647199219674433536605771315869983788442389633 - sage: numerical_approx(gamma(9, 10^(-3)) - gamma(9), digits=40) # abs tol 1e-36 - -1.110111598370794007949063502542063148294e-28 + sage: sqrt(SR(4),hold=True) + sqrt(4) + sage: sqrt(4,hold=True) + Traceback (most recent call last): + ... + TypeError: _do_sqrt() got an unexpected keyword argument 'hold' - Check that :trac:`17328` is fixed:: + This illustrates that the bug reported in :trac:`6171` has been fixed:: - sage: gamma_inc(float(-1), float(-1)) - (-0.8231640121031085+3.141592653589793j) - sage: gamma_inc(RR(-1), RR(-1)) - -0.823164012103109 + 3.14159265358979*I - sage: gamma_inc(-1, float(-log(3))) - gamma_inc(-1, float(-log(2))) # abs tol 1e-15 - (1.2730972164471142+0j) + sage: a = 1.1 + sage: a.sqrt(prec=100) # this is supposed to fail + Traceback (most recent call last): + ... + TypeError: sqrt() got an unexpected keyword argument 'prec' + sage: sqrt(a, prec=100) + 1.0488088481701515469914535137 + sage: sqrt(4.00, prec=250) + 2.0000000000000000000000000000000000000000000000000000000000000000000000000 - Check that :trac:`17130` is fixed:: + One can use numpy input as well:: - sage: r = gamma_inc(float(0), float(1)); r - 0.21938393439552029 - sage: type(r) - <... 'float'> + sage: import numpy + sage: a = numpy.arange(2,5) + sage: sqrt(a) + array([ 1.41421356, 1.73205081, 2. ]) """ - R = parent or s_parent(x) - # C is the complex version of R - # prec is the precision of R - if R is float: - prec = 53 - C = complex - else: - try: - prec = R.precision() - except AttributeError: - prec = 53 - try: - C = R.complex_field() - except AttributeError: - C = R + if isinstance(x, float): + return math.sqrt(x) + elif type(x).__module__ == 'numpy': + from numpy import sqrt + return sqrt(x) + try: + return x.sqrt(*args, **kwds) + # The following includes TypeError to catch cases where sqrt + # is called with a "prec" keyword, for example, but the sqrt + # method for x doesn't accept such a keyword. + except (AttributeError, TypeError): + pass + return _do_sqrt(x, *args, **kwds) - if algorithm == 'pari': - v = ComplexField(prec)(x).gamma_inc(y) - else: - import mpmath - v = ComplexField(prec)(mpmath_utils.call(mpmath.gammainc, x, y, parent=R)) - if v.is_real(): - return R(v) - else: - return C(v) +# register sqrt in pynac symbol_table for conversion back from other systems +register_symbol(sqrt, dict(mathematica='Sqrt')) +symbol_table['functions']['sqrt'] = sqrt -# synonym. -gamma_inc = Function_gamma_inc() +Function_sqrt = type('deprecated_sqrt', (), + {'__call__': staticmethod(sqrt), + '__setstate__': lambda x, y: None}) -class Function_gamma_inc_lower(BuiltinFunction): +class Function_arg(BuiltinFunction): def __init__(self): r""" - The lower incomplete gamma function. - - It is defined by the integral - - .. MATH:: - - \Gamma(a,z)=\int_0^z t^{a-1}e^{-t}\,\mathrm{d}t + The argument function for complex numbers. EXAMPLES:: - sage: gamma_inc_lower(CDF(0,1), 3) - -0.1581584032951798 - 0.5104218539302277*I - sage: gamma_inc_lower(RDF(1), 3) - 0.950212931632136 - sage: gamma_inc_lower(3, 2, hold=True) - gamma_inc_lower(3, 2) - sage: gamma_inc_lower(3, 2) - -10*e^(-2) + 2 - sage: gamma_inc_lower(x, 0) + sage: arg(3+i) + arctan(1/3) + sage: arg(-1+i) + 3/4*pi + sage: arg(2+2*i) + 1/4*pi + sage: arg(2+x) + arg(x + 2) + sage: arg(2.0+i+x) + arg(x + 2.00000000000000 + 1.00000000000000*I) + sage: arg(-3) + pi + sage: arg(3) + 0 + sage: arg(0) 0 - sage: latex(gamma_inc_lower(x, x)) - \gamma\left(x, x\right) - sage: loads(dumps((gamma_inc_lower(x, x)))) - gamma_inc_lower(x, x) - sage: i = ComplexField(30).0; gamma_inc_lower(2, 1 + i) - 0.29290790 + 0.42035364*I - sage: gamma_inc_lower(2., 5) - 0.959572318005487 - Interfaces to other software:: + sage: latex(arg(x)) + {\rm arg}\left(x\right) + sage: maxima(arg(x)) + atan2(0,_SAGE_VAR_x) + sage: maxima(arg(2+i)) + atan(1/2) + sage: maxima(arg(sqrt(2)+i)) + atan(1/sqrt(2)) + sage: arg(x)._sympy_() + arg(x) - sage: gamma_inc_lower(x,x)._sympy_() - lowergamma(x, x) - sage: maxima(gamma_inc_lower(x,x)) - gamma_greek(_SAGE_VAR_x,_SAGE_VAR_x) + sage: arg(2+i) + arctan(1/2) + sage: arg(sqrt(2)+i) + arg(sqrt(2) + I) + sage: arg(sqrt(2)+i).simplify() + arctan(1/2*sqrt(2)) - .. SEEALSO:: + TESTS:: - :meth:`sage.functions.other.Function_gamma_inc` + sage: arg(0.0) + 0.000000000000000 + sage: arg(3.0) + 0.000000000000000 + sage: arg(-2.5) + 3.14159265358979 + sage: arg(2.0+3*i) + 0.982793723247329 """ - BuiltinFunction.__init__(self, "gamma_inc_lower", nargs=2, latex_name=r"\gamma", - conversions={'maxima':'gamma_greek', 'mathematica':'Gamma', - 'maple':'GAMMA', 'sympy':'lowergamma', 'giac':'igamma'}) + BuiltinFunction.__init__(self, "arg", + conversions=dict(maxima='carg', + mathematica='Arg', + sympy='arg', + giac='arg')) - def _eval_(self, x, y): + def _eval_(self, x): """ EXAMPLES:: - sage: gamma_inc_lower(2.,0) - 0.000000000000000 - sage: gamma_inc_lower(2,0) - 0 - sage: gamma_inc_lower(1/2,2) - sqrt(pi)*erf(sqrt(2)) - sage: gamma_inc_lower(1/2,1) - sqrt(pi)*erf(1) - sage: gamma_inc_lower(1/2,0) - 0 - sage: gamma_inc_lower(x,0) - 0 - sage: gamma_inc_lower(1,2) - -e^(-2) + 1 - sage: gamma_inc_lower(0,2) - +Infinity - sage: gamma_inc_lower(2,377/79) - -456/79*e^(-377/79) + 1 - sage: gamma_inc_lower(3,x) - -x^2*e^(-x) - 2*x*e^(-x) - 2*e^(-x) + 2 - sage: gamma_inc_lower(9/2,37/7) - 105/16*sqrt(pi)*erf(1/7*sqrt(259)) - 836473/19208*sqrt(259)*e^(-37/7) - """ - if y == 0: - return 0 - if x == 0: - from sage.rings.infinity import Infinity - return Infinity - elif x == 1: - return 1-exp(-y) - elif (2*x).is_integer(): - return self(x,y,hold=True)._sympy_() - else: - return None - - def _evalf_(self, x, y, parent=None, algorithm='mpmath'): - """ - EXAMPLES:: + sage: arg(3+i) + arctan(1/3) + sage: arg(-1+i) + 3/4*pi + sage: arg(2+2*i) + 1/4*pi + sage: arg(2+x) + arg(x + 2) + sage: arg(2.0+i+x) + arg(x + 2.00000000000000 + 1.00000000000000*I) + sage: arg(-3) + pi + sage: arg(3) + 0 + sage: arg(0) + 0 + sage: arg(sqrt(2)+i) + arg(sqrt(2) + I) - sage: gamma_inc_lower(3,2.) - 0.646647167633873 - sage: gamma_inc_lower(3,2).n(200) - 0.646647167633873081060005050275155... - sage: gamma_inc_lower(0,2.) - +infinity - """ - R = parent or s_parent(x) - # C is the complex version of R - # prec is the precision of R - if R is float: - prec = 53 - C = complex - else: - try: - prec = R.precision() - except AttributeError: - prec = 53 - try: - C = R.complex_field() - except AttributeError: - C = R - if algorithm == 'pari': - try: - v = ComplexField(prec)(x).gamma() - ComplexField(prec)(x).gamma_inc(y) - except AttributeError: - if not (is_ComplexNumber(x)): - if is_ComplexNumber(y): - C = y.parent() - else: - C = ComplexField() - x = C(x) - v = ComplexField(prec)(x).gamma() - ComplexField(prec)(x).gamma_inc(y) - else: - import mpmath - v = ComplexField(prec)(mpmath_utils.call(mpmath.gammainc, x, 0, y, parent=R)) - if v.is_real(): - return R(v) + """ + if isinstance(x,Expression): + if x.is_trivial_zero(): + return x else: - return C(v) + if not x: + return x + else: + return arctan2(imag_part(x),real_part(x)) - def _derivative_(self, x, y, diff_param=None): + def _evalf_(self, x, parent=None, algorithm=None): """ EXAMPLES:: - sage: x,y = var('x,y') - sage: gamma_inc_lower(x,y).diff(y) - y^(x - 1)*e^(-y) - sage: gamma_inc_lower(x,y).diff(x) - Traceback (most recent call last): - ... - NotImplementedError: cannot differentiate gamma_inc_lower in the first parameter - """ - if diff_param == 0: - raise NotImplementedError("cannot differentiate gamma_inc_lower in the" - " first parameter") - else: - return exp(-y)*y**(x - 1) - -# synonym. -gamma_inc_lower = Function_gamma_inc_lower() - -def gamma(a, *args, **kwds): - r""" - Gamma and upper incomplete gamma functions in one symbol. - - Recall that `\Gamma(n)` is `n-1` factorial:: + sage: arg(0.0) + 0.000000000000000 + sage: arg(3.0) + 0.000000000000000 + sage: arg(3.00000000000000000000000000) + 0.00000000000000000000000000 + sage: arg(3.00000000000000000000000000).prec() + 90 + sage: arg(ComplexIntervalField(90)(3)).prec() + 90 + sage: arg(ComplexIntervalField(90)(3)).parent() + Real Interval Field with 90 bits of precision + sage: arg(3.0r) + 0.0 + sage: arg(RDF(3)) + 0.0 + sage: arg(RDF(3)).parent() + Real Double Field + sage: arg(-2.5) + 3.14159265358979 + sage: arg(2.0+3*i) + 0.982793723247329 - sage: gamma(11) == factorial(10) - True - sage: gamma(6) - 120 - sage: gamma(1/2) - sqrt(pi) - sage: gamma(-4/3) - gamma(-4/3) - sage: gamma(-1) - Infinity - sage: gamma(0) - Infinity - - :: - - sage: gamma_inc(3,2) - gamma(3, 2) - sage: gamma_inc(x,0) - gamma(x) - - :: - - sage: gamma(5, hold=True) - gamma(5) - sage: gamma(x, 0, hold=True) - gamma(x, 0) - - :: - - sage: gamma(CDF(I)) - -0.15494982830181067 - 0.49801566811835607*I - sage: gamma(CDF(0.5,14)) - -4.0537030780372815e-10 - 5.773299834553605e-10*I - - Use ``numerical_approx`` to get higher precision from - symbolic expressions:: - - sage: gamma(pi).n(100) - 2.2880377953400324179595889091 - sage: gamma(3/4).n(100) - 1.2254167024651776451290983034 - - The precision for the result is also deduced from the precision of the - input. Convert the input to a higher precision explicitly if a result - with higher precision is desired.:: - - sage: t = gamma(RealField(100)(2.5)); t - 1.3293403881791370204736256125 - sage: t.prec() - 100 - - The gamma function only works with input that can be coerced to the - Symbolic Ring:: - - sage: Q. = NumberField(x^2+1) - sage: gamma(i) - Traceback (most recent call last): - ... - TypeError: cannot coerce arguments: no canonical coercion from Number Field in i with defining polynomial x^2 + 1 to Symbolic Ring + TESTS: - .. SEEALSO:: + Make sure that the ``_evalf_`` method works when it receives a + keyword argument ``parent`` :trac:`12289`:: - :meth:`sage.functions.other.Function_gamma` + sage: arg(5+I, hold=True).n() + 0.197395559849881 """ - if not args: - return gamma1(a, **kwds) - if len(args) > 1: - raise TypeError("Symbolic function gamma takes at most 2 arguments (%s given)"%(len(args)+1)) - return gamma_inc(a,args[0],**kwds) - -def incomplete_gamma(*args, **kwds): - """ - Deprecated name for :meth:`sage.functions.other.Function_gamma_inc`. + try: + return x.arg() + except AttributeError: + pass + # try to find a parent that support .arg() + if parent is None: + parent = s_parent(x) + try: + parent = parent.complex_field() + except AttributeError: + try: + parent = ComplexField(x.prec()) + except AttributeError: + parent = ComplexField() - TESTS:: + return parent(x).arg() - sage: incomplete_gamma(1,1) - doctest:...: DeprecationWarning: Please use gamma_inc(). - See http://trac.sagemath.org/16697 for details. - e^(-1) - """ - from sage.misc.superseded import deprecation - deprecation(16697, 'Please use gamma_inc().') - return gamma_inc(*args, **kwds) +arg=Function_arg() -# We have to add the wrapper function manually to the symbol_table when we have -# two functions with different number of arguments and the same name -symbol_table['functions']['gamma'] = gamma -class Function_psi1(GinacFunction): +############################ +# Real and Imaginary Parts # +############################ +class Function_real_part(GinacFunction): def __init__(self): r""" - The digamma function, `\psi(x)`, is the logarithmic derivative of the - gamma function. + Returns the real part of the (possibly complex) input. - .. MATH:: + It is possible to prevent automatic evaluation using the + ``hold`` parameter:: - \psi(x) = \frac{d}{dx} \log(\Gamma(x)) = \frac{\Gamma'(x)}{\Gamma(x)} + sage: real_part(I,hold=True) + real_part(I) - EXAMPLES:: + To then evaluate again, we currently must use Maxima via + :meth:`sage.symbolic.expression.Expression.simplify`:: - sage: from sage.functions.other import psi1 - sage: psi1(x) - psi(x) - sage: psi1(x).derivative(x) - psi(1, x) + sage: real_part(I,hold=True).simplify() + 0 - :: + EXAMPLES:: - sage: psi1(3) - -euler_gamma + 3/2 + sage: z = 1+2*I + sage: real(z) + 1 + sage: real(5/3) + 5/3 + sage: a = 2.5 + sage: real(a) + 2.50000000000000 + sage: type(real(a)) + + sage: real(1.0r) + 1.0 + sage: real(complex(3, 4)) + 3.0 - :: + Sage can recognize some expressions as real and accordingly + return the identical argument:: - sage: psi(.5) - -1.96351002602142 - sage: psi(RealField(100)(.5)) - -1.9635100260214234794409763330 + sage: SR.var('x', domain='integer').real_part() + x + sage: SR.var('x', domain='integer').imag_part() + 0 + sage: real_part(sin(x)+x) + x + sin(x) + sage: real_part(x*exp(x)) + x*e^x + sage: imag_part(sin(x)+x) + 0 + sage: real_part(real_part(x)) + x + sage: forget() TESTS:: - sage: latex(psi1(x)) - \psi\left(x\right) - sage: loads(dumps(psi1(x)+1)) - psi(x) + 1 + sage: loads(dumps(real_part)) + real_part + sage: real_part(x)._sympy_() + re(x) - sage: t = psi1(x); t - psi(x) - sage: t.subs(x=.2) - -5.28903989659219 - sage: psi(x)._sympy_() - polygamma(0, x) - """ - GinacFunction.__init__(self, "psi", nargs=1, latex_name='\psi', - conversions=dict(mathematica='PolyGamma', - maxima='psi[0]', - sympy='digamma')) + Check if :trac:`6401` is fixed:: -class Function_psi2(GinacFunction): - def __init__(self): - r""" - Derivatives of the digamma function `\psi(x)`. T + sage: latex(x.real()) + \Re \left( x \right) - EXAMPLES:: + sage: f(x) = function('f')(x) + sage: latex( f(x).real()) + \Re \left( f\left(x\right) \right) - sage: from sage.functions.other import psi2 - sage: psi2(2, x) - psi(2, x) - sage: psi2(2, x).derivative(x) - psi(3, x) - sage: n = var('n') - sage: psi2(n, x).derivative(x) - psi(n + 1, x) + Check that some real part expansions evaluate correctly + (:trac:`21614`):: - :: + sage: real(sqrt(sin(x))).subs(x==0) + 0 + """ + GinacFunction.__init__(self, "real_part", + conversions=dict(maxima='realpart', + sympy='re', + giac='re'), + alt_name="real") - sage: psi2(0, x) - psi(x) - sage: psi2(-1, x) - log(gamma(x)) - sage: psi2(3, 1) - 1/15*pi^4 + def __call__(self, x, **kwargs): + r""" + TESTS:: - :: + sage: type(real(complex(3, 4))) + <... 'float'> + """ + if isinstance(x, complex): + return x.real + else: + return GinacFunction.__call__(self, x, **kwargs) - sage: psi2(2, .5).n() - -16.8287966442343 - sage: psi2(2, .5).n(100) - -16.828796644234319995596334261 +real = real_part = Function_real_part() - TESTS:: +class Function_imag_part(GinacFunction): + def __init__(self): + r""" + Returns the imaginary part of the (possibly complex) input. - sage: psi2(n, x).derivative(n) - Traceback (most recent call last): - ... - RuntimeError: cannot diff psi(n,x) with respect to n - - sage: latex(psi2(2,x)) - \psi\left(2, x\right) - sage: loads(dumps(psi2(2,x)+1)) - psi(2, x) + 1 - sage: psi(2, x)._sympy_() - polygamma(2, x) - """ - GinacFunction.__init__(self, "psi", nargs=2, latex_name='\psi', - conversions=dict(mathematica='PolyGamma', - sympy='polygamma', - giac='Psi')) - - def _maxima_init_evaled_(self, *args): - """ - EXAMPLES: - - These are indirect doctests for this function.:: - - sage: from sage.functions.other import psi2 - sage: psi2(2, x)._maxima_() - psi[2](_SAGE_VAR_x) - sage: psi2(4, x)._maxima_() - psi[4](_SAGE_VAR_x) - """ - args_maxima = [] - for a in args: - if isinstance(a, str): - args_maxima.append(a) - elif hasattr(a, '_maxima_init_'): - args_maxima.append(a._maxima_init_()) - else: - args_maxima.append(str(a)) - n, x = args_maxima - return "psi[%s](%s)"%(n, x) + It is possible to prevent automatic evaluation using the + ``hold`` parameter:: + + sage: imag_part(I,hold=True) + imag_part(I) -psi1 = Function_psi1() -psi2 = Function_psi2() + To then evaluate again, we currently must use Maxima via + :meth:`sage.symbolic.expression.Expression.simplify`:: -def psi(x, *args, **kwds): - r""" - The digamma function, `\psi(x)`, is the logarithmic derivative of the - gamma function. + sage: imag_part(I,hold=True).simplify() + 1 - .. MATH:: + TESTS:: - \psi(x) = \frac{d}{dx} \log(\Gamma(x)) = \frac{\Gamma'(x)}{\Gamma(x)} + sage: z = 1+2*I + sage: imaginary(z) + 2 + sage: imag(z) + 2 + sage: imag(complex(3, 4)) + 4.0 + sage: loads(dumps(imag_part)) + imag_part + sage: imag_part(x)._sympy_() + im(x) - We represent the `n`-th derivative of the digamma function with - `\psi(n, x)` or `psi(n, x)`. + Check if :trac:`6401` is fixed:: - EXAMPLES:: + sage: latex(x.imag()) + \Im \left( x \right) - sage: psi(x) - psi(x) - sage: psi(.5) - -1.96351002602142 - sage: psi(3) - -euler_gamma + 3/2 - sage: psi(1, 5) - 1/6*pi^2 - 205/144 - sage: psi(1, x) - psi(1, x) - sage: psi(1, x).derivative(x) - psi(2, x) - - :: - - sage: psi(3, hold=True) - psi(3) - sage: psi(1, 5, hold=True) - psi(1, 5) + sage: f(x) = function('f')(x) + sage: latex( f(x).imag()) + \Im \left( f\left(x\right) \right) + """ + GinacFunction.__init__(self, "imag_part", + conversions=dict(maxima='imagpart', + sympy='im', + giac='im'), + alt_name="imag") - TESTS:: + def __call__(self, x, **kwargs): + r""" + TESTS:: - sage: psi(2, x, 3) - Traceback (most recent call last): - ... - TypeError: Symbolic function psi takes at most 2 arguments (3 given) - """ - if not args: - return psi1(x, **kwds) - if len(args) > 1: - raise TypeError("Symbolic function psi takes at most 2 arguments (%s given)"%(len(args)+1)) - return psi2(x,args[0],**kwds) + sage: type(imag(complex(3, 4))) + <... 'float'> + """ + if isinstance(x, complex): + return x.imag + else: + return GinacFunction.__call__(self, x, **kwargs) -# We have to add the wrapper function manually to the symbol_table when we have -# two functions with different number of arguments and the same name -symbol_table['functions']['psi'] = psi +imag = imag_part = imaginary = Function_imag_part() -def _swap_psi(a, b): return psi(b, a) -register_symbol(_swap_psi, {'giac':'Psi'}) -class Function_factorial(GinacFunction): +############################ +# Complex Conjugate # +############################ +class Function_conjugate(GinacFunction): def __init__(self): r""" - Returns the factorial of `n`. - - INPUT: + Returns the complex conjugate of the input. - - ``n`` - any complex argument (except negative - integers) or any symbolic expression + It is possible to prevent automatic evaluation using the + ``hold`` parameter:: + sage: conjugate(I,hold=True) + conjugate(I) - OUTPUT: an integer or symbolic expression + To then evaluate again, we currently must use Maxima via + :meth:`sage.symbolic.expression.Expression.simplify`:: + + sage: conjugate(I,hold=True).simplify() + -I + + TESTS:: + + sage: x,y = var('x,y') + sage: x.conjugate() + conjugate(x) + sage: _._sympy_() + conjugate(x) + sage: latex(conjugate(x)) + \overline{x} + sage: f = function('f') + sage: latex(f(x).conjugate()) + \overline{f\left(x\right)} + sage: f = function('psi')(x,y) + sage: latex(f.conjugate()) + \overline{\psi\left(x, y\right)} + sage: x.conjugate().conjugate() + x + sage: x.conjugate().operator() + conjugate + sage: x.conjugate().operator() == conjugate + True + + Check if :trac:`8755` is fixed:: + + sage: conjugate(sqrt(-3)) + conjugate(sqrt(-3)) + sage: conjugate(sqrt(3)) + sqrt(3) + sage: conjugate(sqrt(x)) + conjugate(sqrt(x)) + sage: conjugate(x^2) + conjugate(x)^2 + sage: var('y',domain='positive') + y + sage: conjugate(sqrt(y)) + sqrt(y) + + Check if :trac:`10964` is fixed:: + + sage: z= I*sqrt(-3); z + I*sqrt(-3) + sage: conjugate(z) + -I*conjugate(sqrt(-3)) + sage: var('a') + a + sage: conjugate(a*sqrt(-2)*sqrt(-3)) + conjugate(sqrt(-2))*conjugate(sqrt(-3))*conjugate(a) + + Check that sums are handled correctly:: + + sage: y = var('y', domain='real') + sage: conjugate(y + I) + y - I + + Test pickling:: + + sage: loads(dumps(conjugate)) + conjugate + """ + GinacFunction.__init__(self, "conjugate", + conversions=dict(sympy='conjugate', + giac='conj')) + +conjugate = Function_conjugate() + +class Function_factorial(GinacFunction): + def __init__(self): + r""" + Returns the factorial of `n`. + + INPUT: + + - ``n`` - any complex argument (except negative + integers) or any symbolic expression + + + OUTPUT: an integer or symbolic expression EXAMPLES:: @@ -1577,7 +1280,8 @@ def __init__(self): 120 We can also give input other than nonnegative integers. For - other nonnegative numbers, the :func:`gamma` function is used:: + other nonnegative numbers, the :func:`sage.functions.gamma.gamma` + function is used:: sage: factorial(1/2) 1/2*sqrt(pi) @@ -1684,6 +1388,7 @@ def _eval_(self, x): sage: SR(3245908723049857203948572398475).factorial() factorial(3245908723049857203948572398475) """ + from sage.functions.gamma import gamma if isinstance(x, Rational): return gamma(x+1) elif isinstance(x, (Integer, int)) or self._is_numerical(x): @@ -1890,662 +1595,6 @@ def _evalf_(self, n, k, parent=None, algorithm=None): binomial = Function_binomial() -class Function_beta(GinacFunction): - def __init__(self): - r""" - Return the beta function. This is defined by - - .. MATH:: - - \operatorname{B}(p,q) = \int_0^1 t^{p-1}(1-t)^{q-1} dt - - for complex or symbolic input `p` and `q`. - Note that the order of inputs does not matter: - `\operatorname{B}(p,q)=\operatorname{B}(q,p)`. - - GiNaC is used to compute `\operatorname{B}(p,q)`. However, complex inputs - are not yet handled in general. When GiNaC raises an error on - such inputs, we raise a NotImplementedError. - - If either input is 1, GiNaC returns the reciprocal of the - other. In other cases, GiNaC uses one of the following - formulas: - - .. MATH:: - - \operatorname{B}(p,q) = \frac{\Gamma(p)\Gamma(q)}{\Gamma(p+q)} - - or - - .. MATH:: - - \operatorname{B}(p,q) = (-1)^q \operatorname{B}(1-p-q, q). - - - For numerical inputs, GiNaC uses the formula - - .. MATH:: - - \operatorname{B}(p,q) = \exp[\log\Gamma(p)+\log\Gamma(q)-\log\Gamma(p+q)] - - - INPUT: - - - ``p`` - number or symbolic expression - - - ``q`` - number or symbolic expression - - - OUTPUT: number or symbolic expression (if input is symbolic) - - EXAMPLES:: - - sage: beta(3,2) - 1/12 - sage: beta(3,1) - 1/3 - sage: beta(1/2,1/2) - beta(1/2, 1/2) - sage: beta(-1,1) - -1 - sage: beta(-1/2,-1/2) - 0 - sage: ex = beta(x/2,3) - sage: set(ex.operands()) == set([1/2*x, 3]) - True - sage: beta(.5,.5) - 3.14159265358979 - sage: beta(1,2.0+I) - 0.400000000000000 - 0.200000000000000*I - sage: ex = beta(3,x+I) - sage: set(ex.operands()) == set([x+I, 3]) - True - - The result is symbolic if exact input is given:: - - sage: ex = beta(2,1+5*I); ex - beta(... - sage: set(ex.operands()) == set([1+5*I, 2]) - True - sage: beta(2, 2.) - 0.166666666666667 - sage: beta(I, 2.) - -0.500000000000000 - 0.500000000000000*I - sage: beta(2., 2) - 0.166666666666667 - sage: beta(2., I) - -0.500000000000000 - 0.500000000000000*I - - sage: beta(x, x)._sympy_() - beta(x, x) - - Test pickling:: - - sage: loads(dumps(beta)) - beta - - Check that :trac:`15196` is fixed:: - - sage: beta(-1.3,-0.4) - -4.92909641669610 - """ - GinacFunction.__init__(self, 'beta', nargs=2, - latex_name=r"\operatorname{B}", - conversions=dict(maxima='beta', - mathematica='Beta', - sympy='beta', - fricas='Beta', - giac='Beta')) - -beta = Function_beta() - -def _do_sqrt(x, prec=None, extend=True, all=False): - r""" - Used internally to compute the square root of x. - - INPUT: - - - ``x`` - a number - - - ``prec`` - None (default) or a positive integer - (bits of precision) If not None, then compute the square root - numerically to prec bits of precision. - - - ``extend`` - bool (default: True); this is a place - holder, and is always ignored since in the symbolic ring everything - has a square root. - - - ``extend`` - bool (default: True); whether to extend - the base ring to find roots. The extend parameter is ignored if - prec is a positive integer. - - - ``all`` - bool (default: False); whether to return - a list of all the square roots of x. - - - EXAMPLES:: - - sage: from sage.functions.other import _do_sqrt - sage: _do_sqrt(3) - sqrt(3) - sage: _do_sqrt(3,prec=10) - 1.7 - sage: _do_sqrt(3,prec=100) - 1.7320508075688772935274463415 - sage: _do_sqrt(3,all=True) - [sqrt(3), -sqrt(3)] - - Note that the extend parameter is ignored in the symbolic ring:: - - sage: _do_sqrt(3,extend=False) - sqrt(3) - """ - if prec: - if x >= 0: - return RealField(prec)(x).sqrt(all=all) - else: - return ComplexField(prec)(x).sqrt(all=all) - if x == -1: - z = I - else: - z = SR(x) ** one_half - - if all: - if z: - return [z, -z] - else: - return [z] - return z - -def sqrt(x, *args, **kwds): - r""" - INPUT: - - - ``x`` - a number - - - ``prec`` - integer (default: None): if None, returns - an exact square root; otherwise returns a numerical square root if - necessary, to the given bits of precision. - - - ``extend`` - bool (default: True); this is a place - holder, and is always ignored or passed to the sqrt function for x, - since in the symbolic ring everything has a square root. - - - ``all`` - bool (default: False); if True, return all - square roots of self, instead of just one. - - EXAMPLES:: - - sage: sqrt(-1) - I - sage: sqrt(2) - sqrt(2) - sage: sqrt(2)^2 - 2 - sage: sqrt(4) - 2 - sage: sqrt(4,all=True) - [2, -2] - sage: sqrt(x^2) - sqrt(x^2) - - For a non-symbolic square root, there are a few options. - The best is to numerically approximate afterward:: - - sage: sqrt(2).n() - 1.41421356237310 - sage: sqrt(2).n(prec=100) - 1.4142135623730950488016887242 - - Or one can input a numerical type. - - sage: sqrt(2.) - 1.41421356237310 - sage: sqrt(2.000000000000000000000000) - 1.41421356237309504880169 - sage: sqrt(4.0) - 2.00000000000000 - - To prevent automatic evaluation, one can use the ``hold`` parameter - after coercing to the symbolic ring:: - - sage: sqrt(SR(4),hold=True) - sqrt(4) - sage: sqrt(4,hold=True) - Traceback (most recent call last): - ... - TypeError: _do_sqrt() got an unexpected keyword argument 'hold' - - This illustrates that the bug reported in :trac:`6171` has been fixed:: - - sage: a = 1.1 - sage: a.sqrt(prec=100) # this is supposed to fail - Traceback (most recent call last): - ... - TypeError: sqrt() got an unexpected keyword argument 'prec' - sage: sqrt(a, prec=100) - 1.0488088481701515469914535137 - sage: sqrt(4.00, prec=250) - 2.0000000000000000000000000000000000000000000000000000000000000000000000000 - - One can use numpy input as well:: - - sage: import numpy - sage: a = numpy.arange(2,5) - sage: sqrt(a) - array([ 1.41421356, 1.73205081, 2. ]) - """ - if isinstance(x, float): - return math.sqrt(x) - elif type(x).__module__ == 'numpy': - from numpy import sqrt - return sqrt(x) - try: - return x.sqrt(*args, **kwds) - # The following includes TypeError to catch cases where sqrt - # is called with a "prec" keyword, for example, but the sqrt - # method for x doesn't accept such a keyword. - except (AttributeError, TypeError): - pass - return _do_sqrt(x, *args, **kwds) - -# register sqrt in pynac symbol_table for conversion back from other systems -register_symbol(sqrt, dict(mathematica='Sqrt')) -symbol_table['functions']['sqrt'] = sqrt - -Function_sqrt = type('deprecated_sqrt', (), - {'__call__': staticmethod(sqrt), - '__setstate__': lambda x, y: None}) - -class Function_arg(BuiltinFunction): - def __init__(self): - r""" - The argument function for complex numbers. - - EXAMPLES:: - - sage: arg(3+i) - arctan(1/3) - sage: arg(-1+i) - 3/4*pi - sage: arg(2+2*i) - 1/4*pi - sage: arg(2+x) - arg(x + 2) - sage: arg(2.0+i+x) - arg(x + 2.00000000000000 + 1.00000000000000*I) - sage: arg(-3) - pi - sage: arg(3) - 0 - sage: arg(0) - 0 - - sage: latex(arg(x)) - {\rm arg}\left(x\right) - sage: maxima(arg(x)) - atan2(0,_SAGE_VAR_x) - sage: maxima(arg(2+i)) - atan(1/2) - sage: maxima(arg(sqrt(2)+i)) - atan(1/sqrt(2)) - sage: arg(x)._sympy_() - arg(x) - - sage: arg(2+i) - arctan(1/2) - sage: arg(sqrt(2)+i) - arg(sqrt(2) + I) - sage: arg(sqrt(2)+i).simplify() - arctan(1/2*sqrt(2)) - - TESTS:: - - sage: arg(0.0) - 0.000000000000000 - sage: arg(3.0) - 0.000000000000000 - sage: arg(-2.5) - 3.14159265358979 - sage: arg(2.0+3*i) - 0.982793723247329 - """ - BuiltinFunction.__init__(self, "arg", - conversions=dict(maxima='carg', - mathematica='Arg', - sympy='arg', - giac='arg')) - - def _eval_(self, x): - """ - EXAMPLES:: - - sage: arg(3+i) - arctan(1/3) - sage: arg(-1+i) - 3/4*pi - sage: arg(2+2*i) - 1/4*pi - sage: arg(2+x) - arg(x + 2) - sage: arg(2.0+i+x) - arg(x + 2.00000000000000 + 1.00000000000000*I) - sage: arg(-3) - pi - sage: arg(3) - 0 - sage: arg(0) - 0 - sage: arg(sqrt(2)+i) - arg(sqrt(2) + I) - - """ - if isinstance(x,Expression): - if x.is_trivial_zero(): - return x - else: - if not x: - return x - else: - return arctan2(imag_part(x),real_part(x)) - - def _evalf_(self, x, parent=None, algorithm=None): - """ - EXAMPLES:: - - sage: arg(0.0) - 0.000000000000000 - sage: arg(3.0) - 0.000000000000000 - sage: arg(3.00000000000000000000000000) - 0.00000000000000000000000000 - sage: arg(3.00000000000000000000000000).prec() - 90 - sage: arg(ComplexIntervalField(90)(3)).prec() - 90 - sage: arg(ComplexIntervalField(90)(3)).parent() - Real Interval Field with 90 bits of precision - sage: arg(3.0r) - 0.0 - sage: arg(RDF(3)) - 0.0 - sage: arg(RDF(3)).parent() - Real Double Field - sage: arg(-2.5) - 3.14159265358979 - sage: arg(2.0+3*i) - 0.982793723247329 - - TESTS: - - Make sure that the ``_evalf_`` method works when it receives a - keyword argument ``parent`` :trac:`12289`:: - - sage: arg(5+I, hold=True).n() - 0.197395559849881 - """ - try: - return x.arg() - except AttributeError: - pass - # try to find a parent that support .arg() - if parent is None: - parent = s_parent(x) - try: - parent = parent.complex_field() - except AttributeError: - try: - parent = ComplexField(x.prec()) - except AttributeError: - parent = ComplexField() - - return parent(x).arg() - -arg=Function_arg() - - -############################ -# Real and Imaginary Parts # -############################ -class Function_real_part(GinacFunction): - def __init__(self): - r""" - Returns the real part of the (possibly complex) input. - - It is possible to prevent automatic evaluation using the - ``hold`` parameter:: - - sage: real_part(I,hold=True) - real_part(I) - - To then evaluate again, we currently must use Maxima via - :meth:`sage.symbolic.expression.Expression.simplify`:: - - sage: real_part(I,hold=True).simplify() - 0 - - EXAMPLES:: - - sage: z = 1+2*I - sage: real(z) - 1 - sage: real(5/3) - 5/3 - sage: a = 2.5 - sage: real(a) - 2.50000000000000 - sage: type(real(a)) - - sage: real(1.0r) - 1.0 - sage: real(complex(3, 4)) - 3.0 - - Sage can recognize some expressions as real and accordingly - return the identical argument:: - - sage: SR.var('x', domain='integer').real_part() - x - sage: SR.var('x', domain='integer').imag_part() - 0 - sage: real_part(sin(x)+x) - x + sin(x) - sage: real_part(x*exp(x)) - x*e^x - sage: imag_part(sin(x)+x) - 0 - sage: real_part(real_part(x)) - x - sage: forget() - - TESTS:: - - sage: loads(dumps(real_part)) - real_part - sage: real_part(x)._sympy_() - re(x) - - Check if :trac:`6401` is fixed:: - - sage: latex(x.real()) - \Re \left( x \right) - - sage: f(x) = function('f')(x) - sage: latex( f(x).real()) - \Re \left( f\left(x\right) \right) - - Check that some real part expansions evaluate correctly - (:trac:`21614`):: - - sage: real(sqrt(sin(x))).subs(x==0) - 0 - """ - GinacFunction.__init__(self, "real_part", - conversions=dict(maxima='realpart', - sympy='re', - giac='re'), - alt_name="real") - - def __call__(self, x, **kwargs): - r""" - TESTS:: - - sage: type(real(complex(3, 4))) - <... 'float'> - """ - if isinstance(x, complex): - return x.real - else: - return GinacFunction.__call__(self, x, **kwargs) - -real = real_part = Function_real_part() - -class Function_imag_part(GinacFunction): - def __init__(self): - r""" - Returns the imaginary part of the (possibly complex) input. - - It is possible to prevent automatic evaluation using the - ``hold`` parameter:: - - sage: imag_part(I,hold=True) - imag_part(I) - - To then evaluate again, we currently must use Maxima via - :meth:`sage.symbolic.expression.Expression.simplify`:: - - sage: imag_part(I,hold=True).simplify() - 1 - - TESTS:: - - sage: z = 1+2*I - sage: imaginary(z) - 2 - sage: imag(z) - 2 - sage: imag(complex(3, 4)) - 4.0 - sage: loads(dumps(imag_part)) - imag_part - sage: imag_part(x)._sympy_() - im(x) - - Check if :trac:`6401` is fixed:: - - sage: latex(x.imag()) - \Im \left( x \right) - - sage: f(x) = function('f')(x) - sage: latex( f(x).imag()) - \Im \left( f\left(x\right) \right) - """ - GinacFunction.__init__(self, "imag_part", - conversions=dict(maxima='imagpart', - sympy='im', - giac='im'), - alt_name="imag") - - def __call__(self, x, **kwargs): - r""" - TESTS:: - - sage: type(imag(complex(3, 4))) - <... 'float'> - """ - if isinstance(x, complex): - return x.imag - else: - return GinacFunction.__call__(self, x, **kwargs) - -imag = imag_part = imaginary = Function_imag_part() - - -############################ -# Complex Conjugate # -############################ -class Function_conjugate(GinacFunction): - def __init__(self): - r""" - Returns the complex conjugate of the input. - - It is possible to prevent automatic evaluation using the - ``hold`` parameter:: - - sage: conjugate(I,hold=True) - conjugate(I) - - To then evaluate again, we currently must use Maxima via - :meth:`sage.symbolic.expression.Expression.simplify`:: - - sage: conjugate(I,hold=True).simplify() - -I - - TESTS:: - - sage: x,y = var('x,y') - sage: x.conjugate() - conjugate(x) - sage: _._sympy_() - conjugate(x) - sage: latex(conjugate(x)) - \overline{x} - sage: f = function('f') - sage: latex(f(x).conjugate()) - \overline{f\left(x\right)} - sage: f = function('psi')(x,y) - sage: latex(f.conjugate()) - \overline{\psi\left(x, y\right)} - sage: x.conjugate().conjugate() - x - sage: x.conjugate().operator() - conjugate - sage: x.conjugate().operator() == conjugate - True - - Check if :trac:`8755` is fixed:: - - sage: conjugate(sqrt(-3)) - conjugate(sqrt(-3)) - sage: conjugate(sqrt(3)) - sqrt(3) - sage: conjugate(sqrt(x)) - conjugate(sqrt(x)) - sage: conjugate(x^2) - conjugate(x)^2 - sage: var('y',domain='positive') - y - sage: conjugate(sqrt(y)) - sqrt(y) - - Check if :trac:`10964` is fixed:: - - sage: z= I*sqrt(-3); z - I*sqrt(-3) - sage: conjugate(z) - -I*conjugate(sqrt(-3)) - sage: var('a') - a - sage: conjugate(a*sqrt(-2)*sqrt(-3)) - conjugate(sqrt(-2))*conjugate(sqrt(-3))*conjugate(a) - - Check that sums are handled correctly:: - - sage: y = var('y', domain='real') - sage: conjugate(y + I) - y - I - - Test pickling:: - - sage: loads(dumps(conjugate)) - conjugate - """ - GinacFunction.__init__(self, "conjugate", - conversions=dict(sympy='conjugate', - giac='conj')) - -conjugate = Function_conjugate() - class Function_sum(BuiltinFunction): """ diff --git a/src/sage/functions/special.py b/src/sage/functions/special.py index c94db77082f..1288a3cfc4a 100644 --- a/src/sage/functions/special.py +++ b/src/sage/functions/special.py @@ -163,7 +163,8 @@ from sage.rings.complex_field import ComplexField from sage.misc.latex import latex from sage.rings.all import ZZ, RR, RDF, CDF -from sage.functions.other import real, imag, log_gamma +from .gamma import log_gamma +from .other import real, imag from sage.symbolic.constants import pi from sage.symbolic.function import BuiltinFunction from sage.symbolic.expression import Expression diff --git a/src/sage/functions/transcendental.py b/src/sage/functions/transcendental.py index 769651f8af9..15791fceb50 100644 --- a/src/sage/functions/transcendental.py +++ b/src/sage/functions/transcendental.py @@ -19,7 +19,6 @@ import sys import sage.rings.complex_field as complex_field -from sage.functions.other import factorial, psi from sage.rings.all import (ComplexField, ZZ, RR, RDF) from sage.rings.complex_number import is_ComplexNumber @@ -31,6 +30,9 @@ from sage.misc.superseded import deprecation from sage.combinat.combinat import bernoulli_polynomial +from .gamma import psi +from .other import factorial + CC = complex_field.ComplexField() I = CC.gen(0) diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 763b17f5c7b..874ea23483b 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -1199,9 +1199,6 @@ def reduce_load_MaximaLib(): import sage.rings.real_double import sage.symbolic.expression -import sage.functions.trig -import sage.functions.log -import sage.functions.other import sage.symbolic.integration.integral from sage.symbolic.operators import FDerivativeOperator, add_vararg, mul_vararg @@ -1238,7 +1235,7 @@ def reduce_load_MaximaLib(): sage.functions.log.lambert_w : "%LAMBERT_W", sage.functions.other.factorial : "MFACTORIAL", sage.functions.error.erf : "%ERF", - sage.functions.other.gamma_inc : "%GAMMA_INCOMPLETE", + sage.functions.gamma.gamma_inc : "%GAMMA_INCOMPLETE", } #we compile the dictionary sage_op_dict = dict([(k,EclObject(sage_op_dict[k])) for k in sage_op_dict]) @@ -1343,7 +1340,7 @@ def mqapply_to_sage(expr): return sage.functions.log.polylog(max_to_sr(cadadr(expr)), max_to_sr(caddr(expr))) if caaadr(expr) == max_psi: - return sage.functions.other.psi(max_to_sr(cadadr(expr)), + return sage.functions.gamma.psi(max_to_sr(cadadr(expr)), max_to_sr(caddr(expr))) if caaadr(expr) == max_hyper: return sage.functions.hypergeometric.hypergeometric(mlist_to_sage(car(cdr(cdr(expr)))), @@ -1494,8 +1491,8 @@ def max_harmonic_to_sage(expr): special_sage_to_max={ sage.functions.log.polylog : lambda N,X : [[mqapply],[[max_li, max_array],N],X], - sage.functions.other.psi1 : lambda X : [[mqapply],[[max_psi, max_array],0],X], - sage.functions.other.psi2 : lambda N,X : [[mqapply],[[max_psi, max_array],N],X], + sage.functions.gamma.psi1 : lambda X : [[mqapply],[[max_psi, max_array],0],X], + sage.functions.gamma.psi2 : lambda N,X : [[mqapply],[[max_psi, max_array],N],X], sage.functions.log.lambert_w : lambda N,X : [[max_lambert_w], X] if N==EclObject(0) else [[mqapply],[[max_lambert_w, max_array],N],X], sage.functions.log.harmonic_number : lambda N,X : [[max_harmo],X,N], sage.functions.hypergeometric.hypergeometric : lambda A, B, X : [[mqapply],[[max_hyper, max_array],lisp_length(A.cdr()),lisp_length(B.cdr())],A,B,X] diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index d23cc08fa2f..b10c416bdce 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -96,7 +96,7 @@ import sage.matrix.all as matrix from sage.libs.pari.all import pari, PariError -from sage.functions.other import gamma_inc +from sage.functions.gamma import gamma_inc from math import sqrt from sage.interfaces.all import gp from sage.misc.cachefunc import cached_method diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index a7a11c3e45e..1e7d79dfa52 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -5548,7 +5548,7 @@ cdef class Expression(CommutativeRingElement): sage: abs(x).operator() abs sage: r = gamma(x).operator(); type(r) - + sage: psi = function('psi', nargs=1) sage: psi(x).operator() From 488f9eec1a61e9adaba813749e9a96546b346264 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Fri, 22 Dec 2017 09:33:20 +0100 Subject: [PATCH 446/740] Changed name of total_module_order to EchelonMatrixKey and general cleanup --- src/sage/modular/abvar/abvar.py | 6 +- src/sage/modular/abvar/finite_subgroup.py | 6 +- src/sage/modular/abvar/homology.py | 4 + src/sage/modular/hecke/module.py | 4 +- src/sage/modular/hecke/submodule.py | 6 +- src/sage/modular/modform/space.py | 6 +- src/sage/modular/modsym/space.py | 6 +- src/sage/modules/free_module.py | 330 +++++++++++----------- src/sage/modules/free_quadratic_module.py | 35 +-- 9 files changed, 204 insertions(+), 199 deletions(-) diff --git a/src/sage/modular/abvar/abvar.py b/src/sage/modular/abvar/abvar.py index f61de484dfd..8c0caab1d1c 100644 --- a/src/sage/modular/abvar/abvar.py +++ b/src/sage/modular/abvar/abvar.py @@ -48,7 +48,7 @@ from sage.rings.polynomial.polynomial_element import Polynomial from sage.rings.infinity import infinity from sage.rings.fraction_field import FractionField -from sage.modules.free_module import is_FreeModule, total_module_order +from sage.modules.free_module import is_FreeModule, EchelonMatrixKey from sage.modular.arithgroup.all import is_CongruenceSubgroup, is_Gamma0, is_Gamma1, is_GammaH from sage.modular.modsym.all import ModularSymbols from sage.modular.modsym.space import ModularSymbolsSpace @@ -409,8 +409,8 @@ def __richcmp__(self, other, op): # NOTE!! having the same newform level, isogeny class number, # and degen_t does not imply two abelian varieties are equal. # See the docstring for self.label. - lx = total_module_order(self.lattice()) - rx = total_module_order(other.lattice()) + lx = EchelonMatrixKey(self.lattice()) + rx = EchelonMatrixKey(other.lattice()) return richcmp(lx, rx, op) def __radd__(self,other): diff --git a/src/sage/modular/abvar/finite_subgroup.py b/src/sage/modular/abvar/finite_subgroup.py index b63deb3b808..a3a389e2bcb 100644 --- a/src/sage/modular/abvar/finite_subgroup.py +++ b/src/sage/modular/abvar/finite_subgroup.py @@ -101,7 +101,7 @@ from sage.modular.abvar.torsion_point import TorsionPoint from sage.modules.module import Module -from sage.modules.free_module import is_FreeModule, total_module_order +from sage.modules.free_module import is_FreeModule, EchelonMatrixKey from sage.structure.element import ModuleElement from sage.structure.gens_py import abelian_iterator from sage.structure.sequence import Sequence @@ -253,8 +253,8 @@ def __richcmp__(self, other, op): if not A.in_same_ambient_variety(B): return richcmp(A.ambient_variety(), B.ambient_variety(), op) L = A.lattice() + B.lattice() - lx = total_module_order(other.lattice() + L) - rx = total_module_order(self.lattice() + L) + lx = EchelonMatrixKey(other.lattice() + L) + rx = EchelonMatrixKey(self.lattice() + L) # order gets reversed in passing to lattices. return richcmp(lx, rx, op) diff --git a/src/sage/modular/abvar/homology.py b/src/sage/modular/abvar/homology.py index 3d80880e499..9e40ed42a8c 100644 --- a/src/sage/modular/abvar/homology.py +++ b/src/sage/modular/abvar/homology.py @@ -28,6 +28,10 @@ sage: H.base_ring() Integer Ring sage: d = H.decomposition(); d + doctest:warning + ... + DeprecationWarning: The default order on free modules has changed. The old ordering is in sage.modules.free_module.EchelonMatrixKey + See http://trac.sagemath.org/23878 for details. [ Submodule of rank 2 of Integral Homology of Abelian variety J0(43) of dimension 3, Submodule of rank 4 of Integral Homology of Abelian variety J0(43) of dimension 3 diff --git a/src/sage/modular/hecke/module.py b/src/sage/modular/hecke/module.py index b1f89f4d13c..bcc7a55d72b 100644 --- a/src/sage/modular/hecke/module.py +++ b/src/sage/modular/hecke/module.py @@ -1026,9 +1026,9 @@ def decomposition(self, bound=None, anemic=True, height_guess=1, sort_by_basis = self.__is_splittable = len(D) > 1 if anemic: self.__is_splittable_anemic = len(D) > 1 - from sage.modules.free_module import total_module_order + from sage.modules.free_module import EchelonMatrixKey D.sort(key = None if not sort_by_basis - else lambda ss: total_module_order(ss.free_module())) + else lambda ss: EchelonMatrixKey(ss.free_module())) D.set_immutable() self.__decomposition[key] = D for i in range(len(D)): diff --git a/src/sage/modular/hecke/submodule.py b/src/sage/modular/hecke/submodule.py index 0707e715ea2..c34bcb68ec2 100644 --- a/src/sage/modular/hecke/submodule.py +++ b/src/sage/modular/hecke/submodule.py @@ -23,7 +23,7 @@ import sage.misc.misc as misc from sage.misc.cachefunc import cached_method from sage.structure.richcmp import richcmp_method, richcmp_not_equal, richcmp -from sage.modules.free_module import total_module_order +from sage.modules.free_module import EchelonMatrixKey import sage.modules.all from . import module @@ -190,8 +190,8 @@ def __richcmp__(self, other, op): rx = other.ambient() if lx != rx: return richcmp_not_equal(lx, rx, op) - lx = total_module_order(self.free_module()) - rx = total_module_order(other.free_module()) + lx = EchelonMatrixKey(self.free_module()) + rx = EchelonMatrixKey(other.free_module()) return richcmp(lx, rx, op) ################################ diff --git a/src/sage/modular/modform/space.py b/src/sage/modular/modform/space.py index 13e18d74746..3a0127a528b 100644 --- a/src/sage/modular/modform/space.py +++ b/src/sage/modular/modform/space.py @@ -1166,9 +1166,9 @@ def __richcmp__(self, x, op): if self.is_ambient() or x.is_ambient(): return richcmp(self.dimension(), x.dimension(), op) else: - from sage.modules.free_module import total_module_order - lx = total_module_order(self.free_module()) - rx = total_module_order(x.free_module()) + from sage.modules.free_module import EchelonMatrixKey + lx = EchelonMatrixKey(self.free_module()) + rx = EchelonMatrixKey(x.free_module()) return richcmp(lx, rx, op) def span_of_basis(self, B): diff --git a/src/sage/modular/modsym/space.py b/src/sage/modular/modsym/space.py index f8b1c08adec..3eaf2e63b4d 100644 --- a/src/sage/modular/modsym/space.py +++ b/src/sage/modular/modsym/space.py @@ -27,7 +27,7 @@ import sage.modules.free_module as free_module import sage.matrix.matrix_space as matrix_space from sage.modules.free_module_element import is_FreeModuleElement -from sage.modules.free_module import total_module_order +from sage.modules.free_module import EchelonMatrixKey from sage.misc.all import verbose, prod import sage.modular.hecke.all as hecke import sage.arith.all as arith @@ -146,8 +146,8 @@ def __richcmp__(self, other, op): if lx != rx: return richcmp_not_equal(lx, rx, op) - lx = total_module_order(self.free_module()) - rx = total_module_order(other.free_module()) + lx = EchelonMatrixKey(self.free_module()) + rx = EchelonMatrixKey(other.free_module()) if lx == rx: return rich_to_bool(op, 0) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 0b527e65165..6feb1ff5dc1 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -184,6 +184,7 @@ from sage.structure.richcmp import (rich_to_bool, richcmp, richcmp_not_equal, revop) from sage.misc.cachefunc import cached_method +from sage.misc.superseded import deprecation from warnings import warn @@ -1048,6 +1049,10 @@ def __richcmp__(self,other,op): rationals:: sage: QQ^3 <= CC^3 + doctest:warning + ... + DeprecationWarning: The default order on free modules has changed. The old ordering is in sage.modules.free_module.EchelonMatrixKey + See http://trac.sagemath.org/23878 for details. True sage: CC^3 <= QQ^3 False @@ -1087,9 +1092,9 @@ def __richcmp__(self,other,op): True sage: L1 <= L2 False - + More exotic comparisons:: - + sage: R1=ZZ[sqrt(2)] sage: F1=R1^3 sage: V1=F1.span([[sqrt(2),sqrt(2),0]]) @@ -1107,11 +1112,9 @@ def __richcmp__(self,other,op): True sage: W3<=V3 False - - :: - + We compare a one dimensional space to a two dimensional - space: + space:: sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) sage: M = span([[5,6,7]], QQ) @@ -1127,7 +1130,7 @@ def __richcmp__(self,other,op): Free module of degree 1 and rank 1 over Integer Ring Echelon basis matrix: [1] - + We create the module `\ZZ^3`, and the submodule generated by one vector `(1,1,0)`, and check whether certain elements are in the submodule:: @@ -1147,10 +1150,10 @@ def __richcmp__(self,other,op): sage: w in V False sage: V.coordinates(w) - [1/2] - + [1/2] + TESTS:: - + We compare rank three free modules over the integers and rationals:: @@ -1174,7 +1177,7 @@ def __richcmp__(self,other,op): False Comparison with a sub-module:: - + sage: A = QQ^3 sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) sage: V @@ -1194,11 +1197,9 @@ def __richcmp__(self,other,op): True sage: L1 < L2 False - - :: - + We compare a `\ZZ`-module to a one-dimensional - space. + space:: sage: V = span([[5,6,7]], ZZ).scale(1/11); V Free module of degree 3 and rank 1 over Integer Ring @@ -1209,7 +1210,7 @@ def __richcmp__(self,other,op): True sage: M < V False - + We compare rank three free modules over the integers and rationals:: @@ -1220,10 +1221,8 @@ def __richcmp__(self,other,op): sage: QQ^3 >= QQ^3 True - :: - Comparison with a sub-module:: - + sage: A = QQ^3 sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) sage: V @@ -1243,7 +1242,7 @@ def __richcmp__(self,other,op): False sage: L1 >= L2 True - + We compare rank three free modules over the integers and rationals:: @@ -1254,10 +1253,8 @@ def __richcmp__(self,other,op): sage: QQ^3 > QQ^3 False - :: - Comparison with a sub-module:: - + sage: A = QQ^3 sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) sage: V @@ -1276,18 +1273,19 @@ def __richcmp__(self,other,op): sage: L2 > L1 False sage: L1 > L2 - True + True """ if self is other: return rich_to_bool(op, 0) if not isinstance(other, FreeModule_generic): return NotImplemented - # Check equality first if needed if op == op_EQ: return self._eq(other) if op == op_NE: return not self._eq(other) + deprecation(23878,"The default order on free modules has changed. " + "The old ordering is in sage.modules.free_module.EchelonMatrixKey") if op == op_LE: return self.is_submodule(other) if op == op_GE: @@ -1296,14 +1294,14 @@ def __richcmp__(self,other,op): return (not self._eq(other)) and self.is_submodule(other) if op == op_GT: return (not self._eq(other)) and other.is_submodule(self) - + def _eq(self,other): r""" Return if this free module is equal to other. Ambient spaces are considered equal if they have the same rank, basering and inner product matrix. - + Modules in the same ambient space are partially ordered by inclusion. EXAMPLES: @@ -1312,6 +1310,10 @@ def _eq(self,other): rationals:: sage: QQ^3 < CC^3 + doctest:warning + ... + DeprecationWarning: The default order on free modules has changed. The old ordering is in sage.modules.free_module.EchelonMatrixKey + See http://trac.sagemath.org/23878 for details. True sage: CC^3 < QQ^3 False @@ -1380,8 +1382,8 @@ def _eq(self,other): rx = other.base_ring() if lx != rx: return False - #We do not want to create an inner product matrix in memory if - #self and other use the dot product + # We do not want to create an inner product matrix in memory if + # self and other use the dot product if not (self._inner_product_is_dot_product() and other._inner_product_is_dot_product()): #this only affects free_quadratic_modules lx = self.inner_product_matrix() @@ -1392,7 +1394,7 @@ def _eq(self,other): lq = isinstance(self, FreeModule_ambient_field_quotient) rq = isinstance(other, FreeModule_ambient_field_quotient) if lq or rq: - #if the relations agree we continue with the covers. + # if the relations agree we continue with the covers. if lq: lx = self.relations() self = self.cover() @@ -1409,14 +1411,14 @@ def _eq(self,other): rx = isinstance(other, FreeModule_ambient) if lx and rx: return True - #self and other are not ambient. - #but they are contained in the same ambient space - + # self and other are not ambient. + # but they are contained in the same ambient space + # We use self.echelonized_basis_matrix() == other.echelonized_basis_matrix() # with the matrix to avoid a circular reference. from sage.rings.integer_ring import IntegerRing if self.base_ring().is_field() or self.base_ring() is IntegerRing: - #We know that the Hermite normal form is unique here. + # We know that the Hermite normal form is unique here. lx = self.echelonized_basis_matrix() rx = other.echelonized_basis_matrix() return lx == rx @@ -1466,14 +1468,14 @@ def is_submodule(self, other): (1/2, 1, 3/2, 2) sage: vector(ZZ['x']['y'],[1,2,3,4]) * QQ(1/2) (1/2, 1, 3/2, 2) - + TESTS:: - + M = QQ^3 / [[1,2,3]] V = QQ^2 V.is_submodule(M) False - + sage: M1 = QQ^3 / [[1,2,3]] sage: V1 = span(QQ,[(1,0,0)])+ M1.relations() sage: M2 = V1 / M1.relations() @@ -1799,7 +1801,7 @@ def matrix(self): def direct_sum(self, other): """ - Return the direct sum of self and other as a free module. + Return the direct sum of ``self`` and ``other`` as a free module. EXAMPLES:: @@ -1881,10 +1883,10 @@ def coordinate_vector(self, v, check=True): def coordinate_module(self, V): r""" - Suppose V is a submodule of self (or a module commensurable with - self), and that self is a free module over `R` of rank - `n`. Let `\phi` be the map from self to - `R^n` that sends the basis vectors of self in order to the + Suppose V is a submodule of ``self`` (or a module commensurable with + self), and that ``self`` is a free module over `R` of rank + `n`. Let `\phi` be the map from ``self`` to + `R^n` that sends the basis vectors of ``self`` in order to the standard basis of `R^n`. This function returns the image `\phi(V)`. @@ -2704,7 +2706,7 @@ def __radd__(self, other): def __add__(self, other): r""" - Return the sum of self and other, where both self and other must be + Return the sum of ``self`` and other, where both ``self`` and ``other`` must be submodules of the ambient vector space. EXAMPLES: @@ -2830,8 +2832,8 @@ def _mul_(self, other, switch_sides=False): def index_in(self, other): """ - Return the lattice index [other:self] of self in other, as an - element of the base field. When self is contained in other, the + Return the lattice index [other:self] of ``self`` in other, as an + element of the base field. When ``self`` is contained in other, the lattice index is the usual index. If the index is infinite, then this function returns infinity. @@ -2896,7 +2898,7 @@ def index_in(self, other): def intersection(self, other): r""" - Return the intersection of self and other. + Return the intersection of ``self`` and other. EXAMPLES: @@ -3009,7 +3011,7 @@ def intersection(self, other): def __and__(self, other): r""" - Return the intersection of self and other. + Return the intersection of ``self`` and other. See :meth:`intersection`. @@ -3061,7 +3063,7 @@ def zero_submodule(self): def denominator(self): """ - The denominator of the basis matrix of self (i.e. the LCM of the + The denominator of the basis matrix of ``self`` (i.e. the LCM of the coordinate entries with respect to the basis of the ambient space). @@ -3309,7 +3311,7 @@ def submodule(self, gens, check=True, already_echelonized=False): def span_of_basis(self, basis, base_ring=None, check=True, already_echelonized=False): r""" Return the free R-module with the given basis, where R is the base - ring of self or user specified base_ring. + ring of ``self`` or user specified base_ring. Note that this R-module need not be a submodule of self, nor even of the ambient space. It must, however, be contained in the ambient @@ -3540,7 +3542,7 @@ def vector_space_span_of_basis(self, basis, check=True): def quotient(self, sub, check=True): """ - Return the quotient of self by the given submodule sub. + Return the quotient of ``self`` by the given submodule sub. INPUT: @@ -3572,7 +3574,7 @@ def quotient(self, sub, check=True): def __truediv__(self, sub): """ - Return the quotient of self by the given submodule sub. + Return the quotient of ``self`` by the given submodule sub. EXAMPLES:: @@ -3663,10 +3665,10 @@ def _Hom_(self, Y, category): def scale(self, other): """ - Return the product of self by the number other, which is the module - spanned by other times each basis vector. Since self is a vector - space this product equals self if other is nonzero, and is the zero - vector space if other is 0. + Return the product of ``self`` by the number other, which is the module + spanned by ``other`` times each basis vector. Since ``self`` is a vector + space this product equals ``self`` if ``other`` is nonzero, and is the zero + vector space if ``other`` is 0. EXAMPLES:: @@ -3707,7 +3709,7 @@ def scale(self, other): def __add__(self, other): """ - Return the sum of self and other. + Return the sum of ``self`` and other. EXAMPLES:: @@ -3733,7 +3735,7 @@ def __add__(self, other): def echelonized_basis_matrix(self): """ - Return basis matrix for self in row echelon form. + Return basis matrix for ``self`` in row echelon form. EXAMPLES:: @@ -3749,7 +3751,7 @@ def echelonized_basis_matrix(self): def intersection(self, other): """ - Return the intersection of self and other, which must be + Return the intersection of ``self`` and other, which must be R-submodules of a common ambient vector space. EXAMPLES:: @@ -3867,7 +3869,7 @@ def is_subspace(self, other): def span(self, gens, base_ring=None, check=True, already_echelonized=False): """ Return the K-span of the given list of gens, where K is the - base field of self or the user-specified base_ring. Note that + base field of ``self`` or the user-specified base_ring. Note that this span is a subspace of the ambient vector space, but need not be a subspace of self. @@ -3933,7 +3935,7 @@ def span(self, gens, base_ring=None, check=True, already_echelonized=False): def span_of_basis(self, basis, base_ring=None, check=True, already_echelonized=False): r""" Return the free K-module with the given basis, where K is the base - field of self or user specified base_ring. + field of ``self`` or user specified base_ring. Note that this span is a subspace of the ambient vector space, but need not be a subspace of self. @@ -3989,7 +3991,7 @@ def span_of_basis(self, basis, base_ring=None, check=True, already_echelonized=F def subspace(self, gens, check=True, already_echelonized=False): """ - Return the subspace of self spanned by the elements of gens. + Return the subspace of ``self`` spanned by the elements of gens. INPUT: @@ -4196,7 +4198,7 @@ def complement(self): def vector_space(self, base_field=None): """ - Return the vector space associated to self. Since self is a vector + Return the vector space associated to self. Since ``self`` is a vector space this function simply returns self, unless the base field is different. @@ -4406,7 +4408,7 @@ def linear_dependence(self, vectors, zeros='left', check=True): def __truediv__(self, sub): """ - Return the quotient of self by the given subspace sub. + Return the quotient of ``self`` by the given subspace sub. EXAMPLES:: @@ -4431,7 +4433,7 @@ def __truediv__(self, sub): def quotient(self, sub, check=True): """ - Return the quotient of self by the given subspace sub. + Return the quotient of ``self`` by the given subspace sub. INPUT: @@ -4542,7 +4544,7 @@ def __quotient_matrices(self, sub): # Compute the matrix D "change of basis from S to A" # that writes each element of the basis - # for self in terms of the basis of rows of A, i.e., + # for ``self`` in terms of the basis of rows of A, i.e., # want to find D such that # D * A = S # where D is a square n x n matrix. @@ -4554,8 +4556,8 @@ def __quotient_matrices(self, sub): SS = S.matrix_from_columns(P) D = SS * AA**(-1) - # Compute the image of each basis vector for self under the - # map "write an element of self in terms of the basis A" then + # Compute the image of each basis vector for ``self`` under the + # map "write an element of ``self`` in terms of the basis A" then # take the last n-m components. Q = D.matrix_from_columns(range(n - m, n)) @@ -4569,7 +4571,7 @@ def __quotient_matrices(self, sub): def quotient_abstract(self, sub, check=True): r""" Returns an ambient free module isomorphic to the quotient space of - self modulo sub, together with maps from self to the quotient, and + ``self`` modulo sub, together with maps from ``self`` to the quotient, and a lifting map in the other direction. Use ``self.quotient(sub)`` to obtain the quotient @@ -4751,13 +4753,41 @@ def _sparse_module(self): """ return FreeModule(base_ring=self.base_ring(), rank = self.rank(), sparse=True) - def _total_order_cmp(self, other, op): + def echelonized_basis_matrix(self): + """ + The echelonized basis matrix of self. + + EXAMPLES:: + + sage: V = ZZ^4 + sage: W = V.submodule([ V.gen(i)-V.gen(0) for i in range(1,4) ]) + sage: W.basis_matrix() + [ 1 0 0 -1] + [ 0 1 0 -1] + [ 0 0 1 -1] + sage: W.echelonized_basis_matrix() + [ 1 0 0 -1] + [ 0 1 0 -1] + [ 0 0 1 -1] + sage: U = V.submodule_with_basis([ V.gen(i)-V.gen(0) for i in range(1,4) ]) + sage: U.basis_matrix() + [-1 1 0 0] + [-1 0 1 0] + [-1 0 0 1] + sage: U.echelonized_basis_matrix() + [ 1 0 0 -1] + [ 0 1 0 -1] + [ 0 0 1 -1] + """ + return self.basis_matrix() + + def _echelon_matrix_richcmp(self, other, op): r""" - Compare the free module self with other. + Compare the free module ``self`` with ``other`. - Modules are ordered by their ambient spaces, then by dimension, + This compares modules by their ambient spaces, then by dimension, then in order by their echelon matrices. However, if - other is a sub-module or is a quotient module then its + ``other`` is a sub-module or is a quotient module then its total comparison method is used instead of generic comparison. EXAMPLES: @@ -4766,28 +4796,28 @@ def _total_order_cmp(self, other, op): rationals:: sage: from sage.structure.richcmp import op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE - sage: (QQ^3)._total_order_cmp(CC^3, op_LT) + sage: (QQ^3)._echelon_matrix_richcmp(CC^3, op_LT) True - sage: (CC^3)._total_order_cmp(QQ^3, op_LT) + sage: (CC^3)._echelon_matrix_richcmp(QQ^3, op_LT) False - sage: (CC^3)._total_order_cmp(QQ^3, op_GT) + sage: (CC^3)._echelon_matrix_richcmp(QQ^3, op_GT) True :: sage: from sage.structure.richcmp import op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE sage: Q = QQ; Z = ZZ - sage: (Q^3)._total_order_cmp(Z^3, op_GT) + sage: (Q^3)._echelon_matrix_richcmp(Z^3, op_GT) True - sage: (Q^3)._total_order_cmp(Z^3, op_LT) + sage: (Q^3)._echelon_matrix_richcmp(Z^3, op_LT) False - sage: (Z^3)._total_order_cmp(Q^3, op_LT) + sage: (Z^3)._echelon_matrix_richcmp(Q^3, op_LT) True - sage: (Z^3)._total_order_cmp(Q^3, op_GT) + sage: (Z^3)._echelon_matrix_richcmp(Q^3, op_GT) False - sage: (Q^3)._total_order_cmp(Z^3, op_EQ) + sage: (Q^3)._echelon_matrix_richcmp(Z^3, op_EQ) False - sage: (Q^3)._total_order_cmp(Q^3, op_EQ) + sage: (Q^3)._echelon_matrix_richcmp(Q^3, op_EQ) True Comparison with a sub-module:: @@ -4800,9 +4830,9 @@ def _total_order_cmp(self, other, op): [ 1 0 -1] [ 0 1 2] sage: A = QQ^3 - sage: V._total_order_cmp(A, op_LT) + sage: V._echelon_matrix_richcmp(A, op_LT) True - sage: A._total_order_cmp(V, op_LT) + sage: A._echelon_matrix_richcmp(V, op_LT) False Comparison with a quotient module (see :trac:`10513`):: @@ -4810,9 +4840,9 @@ def _total_order_cmp(self, other, op): sage: from sage.structure.richcmp import op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE sage: M = QQ^3 / [[1,2,3]] sage: V = QQ^2 - sage: V._total_order_cmp(M, op_EQ) + sage: V._echelon_matrix_richcmp(M, op_EQ) False - sage: M._total_order_cmp(V, op_EQ) + sage: M._echelon_matrix_richcmp(V, op_EQ) False """ if self is other: @@ -4830,8 +4860,8 @@ def _total_order_cmp(self, other, op): lx = self.base_ring() rx = other.base_ring() if lx == rx: - #We do not want to create an inner product matrix in memory if - #self and other use the dot product + # We do not want to create an inner product matrix in memory if + # self and other use the dot product if self._inner_product_is_dot_product() and other._inner_product_is_dot_product(): return rich_to_bool(op, 0) else: @@ -4851,37 +4881,7 @@ def _total_order_cmp(self, other, op): else: # now other is not ambient or is a quotient; # it knows how to do the comparison. - return other._total_order_cmp( self, revop(op)) - - def echelonized_basis_matrix(self): - """ - The echelonized basis matrix of self. - - EXAMPLES:: - - sage: V = ZZ^4 - sage: W = V.submodule([ V.gen(i)-V.gen(0) for i in range(1,4) ]) - sage: W.basis_matrix() - [ 1 0 0 -1] - [ 0 1 0 -1] - [ 0 0 1 -1] - sage: W.echelonized_basis_matrix() - [ 1 0 0 -1] - [ 0 1 0 -1] - [ 0 0 1 -1] - sage: U = V.submodule_with_basis([ V.gen(i)-V.gen(0) for i in range(1,4) ]) - sage: U.basis_matrix() - [-1 1 0 0] - [-1 0 1 0] - [-1 0 0 1] - sage: U.echelonized_basis_matrix() - [ 1 0 0 -1] - [ 0 1 0 -1] - [ 0 0 1 -1] - """ - return self.basis_matrix() - - + return other._echelon_matrix_richcmp( self, revop(op)) def _repr_(self): """ @@ -4970,7 +4970,7 @@ def is_ambient(self): def ambient_module(self): """ - Return self, since self is ambient. + Return ``self``, since ``self`` is ambient. EXAMPLES:: @@ -5060,7 +5060,7 @@ def change_ring(self, R): def linear_combination_of_basis(self, v): """ - Return the linear combination of the basis for self obtained from + Return the linear combination of the basis for ``self`` obtained from the elements of the list v. INPUT: @@ -5090,7 +5090,7 @@ def linear_combination_of_basis(self, v): def coordinate_vector(self, v, check=True): """ - Write `v` in terms of the standard basis for self and + Write `v` in terms of the standard basis for ``self`` and return the resulting coefficients in a vector over the fraction field of the base ring. @@ -5114,7 +5114,7 @@ def coordinate_vector(self, v, check=True): def echelon_coordinate_vector(self, v, check=True): r""" - Same as ``self.coordinate_vector(v)``, since self is + Same as ``self.coordinate_vector(v)``, since ``self`` is an ambient free module. INPUT: @@ -5394,7 +5394,7 @@ def ambient_vector_space(self): def coordinate_vector(self, v, check=True): """ - Write `v` in terms of the standard basis for self and + Write `v` in terms of the standard basis for ``self`` and return the resulting coefficients in a vector over the fraction field of the base ring. @@ -5436,7 +5436,7 @@ def coordinate_vector(self, v, check=True): def vector_space(self, base_field=None): """ - Returns the vector space obtained from self by tensoring with the + Returns the vector space obtained from ``self`` by tensoring with the fraction field of the base ring and extending to the field. EXAMPLES:: @@ -5620,7 +5620,7 @@ def _repr_(self): def ambient_vector_space(self): """ - Returns self as the ambient vector space. + Returns ``self`` as the ambient vector space. EXAMPLES:: @@ -5804,14 +5804,14 @@ def __hash__(self): """ return hash(self.__basis) - def _total_order_cmp(self, other, op): + def _echelon_matrix_richcmp(self, other, op): r""" - Compare the free module self with other. + Compare the free module ``self`` with other. Modules are ordered by their ambient spaces, then by dimension, then in order by their echelon matrices. - .. note:: + .. NOTE:: Use :meth:`is_submodule` to determine if one module is a submodule of another. @@ -5825,7 +5825,7 @@ def _total_order_cmp(self, other, op): sage: from sage.structure.richcmp import op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) sage: W = span([[5,6,7], [8,9,10]], QQ) - sage: V._total_order_cmp(W,op_EQ) + sage: V._echelon_matrix_richcmp(W,op_EQ) True Next we compare a one dimensional space to the two dimensional @@ -5835,11 +5835,11 @@ def _total_order_cmp(self, other, op): sage: from sage.structure.richcmp import op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE sage: M = span([[5,6,7]], QQ) - sage: V._total_order_cmp(M,op_EQ) + sage: V._echelon_matrix_richcmp(M,op_EQ) False - sage: M._total_order_cmp(V, op_LT) + sage: M._echelon_matrix_richcmp(V, op_LT) True - sage: V._total_order_cmp(M, op_LT) + sage: V._echelon_matrix_richcmp(M, op_LT) False We compare a `\ZZ`-module to the one-dimensional @@ -5850,9 +5850,9 @@ def _total_order_cmp(self, other, op): Free module of degree 3 and rank 1 over Integer Ring Echelon basis matrix: [5/11 6/11 7/11] - sage: V._total_order_cmp(M, op_LT) + sage: V._echelon_matrix_richcmp(M, op_LT) True - sage: M._total_order_cmp(V, op_LT) + sage: M._echelon_matrix_richcmp(V, op_LT) False """ if self is other: @@ -5862,7 +5862,7 @@ def _total_order_cmp(self, other, op): lx = self.ambient_vector_space() rx = other.ambient_vector_space() if lx != rx: - return lx._total_order_cmp( rx, op) + return lx._echelon_matrix_richcmp( rx, op) lx = self.dimension() rx = other.dimension() @@ -5901,7 +5901,7 @@ def construction(self): def echelonized_basis_matrix(self): """ - Return basis matrix for self in row echelon form. + Return basis matrix for ``self`` in row echelon form. EXAMPLES:: @@ -6048,8 +6048,8 @@ def ambient_module(self): """ Return the ambient module related to the `R`-module self, which was used when creating this module, and is of the form - `R^n`. Note that self need not be contained in the ambient - module, though self will be contained in the ambient vector space. + `R^n`. Note that ``self`` need not be contained in the ambient + module, though ``self`` will be contained in the ambient vector space. EXAMPLES:: @@ -6143,7 +6143,7 @@ def echelon_coordinates(self, v, check=True): def user_to_echelon_matrix(self): """ Return matrix that transforms a vector written with respect to the - user basis of self to one written with respect to the echelon + user basis of ``self`` to one written with respect to the echelon basis. The matrix acts from the right, as is usual in Sage. EXAMPLES:: @@ -6191,7 +6191,7 @@ def echelon_to_user_matrix(self): """ Return matrix that transforms the echelon basis to the user basis of self. This is a matrix `A` such that if `v` is a - vector written with respect to the echelon basis for self then + vector written with respect to the echelon basis for ``self`` then `vA` is that vector written with respect to the user basis of self. @@ -6548,7 +6548,7 @@ def coordinate_vector(self, v, check=True): def echelonized_basis(self): """ - Return the basis for self in echelon form. + Return the basis for ``self`` in echelon form. EXAMPLES:: @@ -6623,7 +6623,7 @@ def has_user_basis(self): def linear_combination_of_basis(self, v): """ - Return the linear combination of the basis for self obtained from + Return the linear combination of the basis for ``self`` obtained from the coordinates of v. INPUT: @@ -7249,7 +7249,7 @@ def basis_seq(V, vecs): This converts a list vecs of vectors in V to an Sequence of immutable vectors. - Should it? I.e. in most other parts of the system the return type + Should it? I.e. in most ``other`` parts of the system the return type of basis or generators is a tuple. EXAMPLES:: @@ -7356,8 +7356,8 @@ def element_class(R, is_sparse): raise NotImplementedError @richcmp_method -class total_module_order(object): - """ +class EchelonMatrixKey(object): + r""" A total ordering on free modules for sorting. This class orders modules by their ambient spaces, then by dimension, @@ -7371,20 +7371,20 @@ class total_module_order(object): EXAMPLES:: - sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) - sage: W = span([[5,6,7], [8,9,10]], QQ) - sage: X = span([[5,6,7]], ZZ).scale(1/11) - sage: Y = CC^3 - sage: Z = ZZ^2 - sage: modules = [V,W,X,Y,Z] - sage: modules_sorted = [Z,X,V,W,Y] - sage: from sage.modules.free_module import total_module_order - sage: modules.sort(key=total_module_order) - sage: modules == modules_sorted - True + sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) + sage: W = span([[5,6,7], [8,9,10]], QQ) + sage: X = span([[5,6,7]], ZZ).scale(1/11) + sage: Y = CC^3 + sage: Z = ZZ^2 + sage: modules = [V,W,X,Y,Z] + sage: modules_sorted = [Z,X,V,W,Y] + sage: from sage.modules.free_module import EchelonMatrixKey + sage: modules.sort(key=EchelonMatrixKey) + sage: modules == modules_sorted + True """ def __init__(self, obj): - """ + r""" Create a container for a free module with a total ordering. EXAMPLES:: @@ -7392,24 +7392,24 @@ def __init__(self, obj): sage: R. = QQ[] sage: V = span(R,[[x,1+x],[x^2,2+x]]) sage: W = RR^2 - sage: from sage.modules.free_module import total_module_order - sage: V = total_module_order(V) - sage: W = total_module_order(W) + sage: from sage.modules.free_module import EchelonMatrixKey + sage: V = EchelonMatrixKey(V) + sage: W = EchelonMatrixKey(W) sage: V < W True """ self.obj = obj def __richcmp__(self,other,op): - """ + r""" A total ordering on free modules. TESTS:: - sage: from sage.modules.free_module import total_module_order - sage: Y = total_module_order(CC^3) - sage: Z = total_module_order(ZZ^2) + sage: from sage.modules.free_module import EchelonMatrixKey + sage: Y = EchelonMatrixKey(CC^3) + sage: Z = EchelonMatrixKey(ZZ^2) sage: Z < Y True """ - return self.obj._total_order_cmp(other.obj,op) + return self.obj._echelon_matrix_richcmp(other.obj,op) diff --git a/src/sage/modules/free_quadratic_module.py b/src/sage/modules/free_quadratic_module.py index 937fd0bfb65..6a4b17b3a25 100644 --- a/src/sage/modules/free_quadratic_module.py +++ b/src/sage/modules/free_quadratic_module.py @@ -237,19 +237,22 @@ def is_FreeQuadraticModule(M): class FreeQuadraticModule_generic(free_module.FreeModule_generic): """ Base class for all free quadratic modules. - - Modules are ordered by their ambient spaces, then by - dimension, then in order by their echelon matrices. + + Modules are ordered by inclusion in the same ambient space. TESTS: We compare rank three free modules over the integers and rationals:: - + sage: Q3 = FreeQuadraticModule(QQ,3,matrix.identity(3)) sage: C3 = FreeQuadraticModule(CC,3,matrix.identity(3)) sage: Z3 = FreeQuadraticModule(ZZ,3,matrix.identity(3)) sage: Q3 < C3 + doctest:warning + ... + DeprecationWarning: The default order on free modules has changed. The old ordering is in sage.modules.free_module.EchelonMatrixKey + See http://trac.sagemath.org/23878 for details. True sage: C3 < Q3 False @@ -275,13 +278,13 @@ class FreeQuadraticModule_generic(free_module.FreeModule_generic): False The inner_product_matrix is part of the comparison:: - + sage: Q3zero = FreeQuadraticModule(QQ,3,matrix.zero(3)) sage: Q3zero == Q3 False - + We test that :trac:`23915` is fixed:: - + sage: M1 = FreeQuadraticModule(ZZ,1,matrix.identity(1)) sage: M2 = FreeQuadraticModule(ZZ,1,matrix.identity(1)*2) sage: M1 == M2 @@ -1157,14 +1160,8 @@ class FreeQuadraticModule_submodule_with_basis_pid( """ An `R`-submodule of `K^n` with distinguished basis, where `K` is the fraction field of a principal ideal domain `R`. - - Modules are ordered by their ambient spaces, then by - dimension, then in order by their echelon matrices. - .. NOTE:: - - Use the \code{is_submodule} to determine if one module - is a submodule of another. + Modules are ordered by inclusion. EXAMPLES: @@ -1183,12 +1180,16 @@ class FreeQuadraticModule_submodule_with_basis_pid( sage: V == M False sage: M < V + doctest:warning + ... + DeprecationWarning: The default order on free modules has changed. The old ordering is in sage.modules.free_module.EchelonMatrixKey + See http://trac.sagemath.org/23878 for details. True sage: V < M False We compare a `\ZZ`-module to the one-dimensional space above:: - + sage: V = A.span([[5,6,7]]) sage: V = V.change_ring(ZZ).scale(1/11); sage: V < M @@ -1224,9 +1225,9 @@ def __init__(self, ambient, basis, inner_product_matrix, [1 0 0] [0 2 0] [0 0 2] - + TESTS: - + We test that :trac:`23703` is fixed:: sage: A=FreeQuadraticModule(ZZ,1,matrix.identity(1)) From 5c2f4f5c78c3b3a09451c601b64f4c536191b25b Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Fri, 15 Dec 2017 12:04:10 +0100 Subject: [PATCH 447/740] New Parent method _convert_method_map --- src/sage/categories/finite_fields.py | 15 ---- src/sage/rings/padics/padic_generic.py | 3 - src/sage/rings/real_mpfr.pyx | 4 +- src/sage/structure/coerce_maps.pxd | 14 +++- src/sage/structure/coerce_maps.pyx | 22 +---- src/sage/structure/parent.pxd | 1 + src/sage/structure/parent.pyx | 107 ++++++++++++++----------- 7 files changed, 75 insertions(+), 91 deletions(-) diff --git a/src/sage/categories/finite_fields.py b/src/sage/categories/finite_fields.py index 00574e15b9a..59106b8bbdc 100644 --- a/src/sage/categories/finite_fields.py +++ b/src/sage/categories/finite_fields.py @@ -91,21 +91,6 @@ def _call_(self, x): raise TypeError("unable to canonically associate a finite field to %s"%x) # TODO: local dvr ring? - #@lazy_attribute - #def element_class(self): - # """ - # A common super class for all elements of finite fields - # - # EXAMPLES:: - # - # sage: C = FiniteFields().element_class; C - # - # sage: type(C) - # <... 'type'> - # """ - # from sage.rings.finite_rings.element_base import FiniteFieldElement - # return FiniteFieldElement - class ParentMethods: pass diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index 3a8177fac1b..e6bfb8e1311 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -211,9 +211,6 @@ def print_mode(self): """ return self._printer._print_mode() - #def element_class(self): - # return self._element_class - def characteristic(self): r""" Returns the characteristic of self, which is always 0. diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 681ac845b17..1ff74da5acc 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -710,8 +710,8 @@ cdef class RealField_class(sage.rings.ring.Field): return QQtoRR(QQ, self) * QQ._internal_coerce_map_from(S) from sage.rings.qqbar import AA from sage.rings.real_lazy import RLF - if S == AA or S is RLF: - return self._generic_coerce_map(S) + if S is AA or S is RLF: + return self.convert_method_map(S, "_mpfr_") return self._coerce_map_via([RLF], S) def __cmp__(self, other): diff --git a/src/sage/structure/coerce_maps.pxd b/src/sage/structure/coerce_maps.pxd index 0e6575080b3..0afc1a96adb 100644 --- a/src/sage/structure/coerce_maps.pxd +++ b/src/sage/structure/coerce_maps.pxd @@ -1,19 +1,27 @@ -from sage.categories.action cimport Action from sage.categories.map cimport Map + cdef class DefaultConvertMap(Map): - cdef public bint _force_use + pass + cdef class DefaultConvertMap_unique(DefaultConvertMap): pass + cdef class NamedConvertMap(Map): cdef readonly method_name - cdef public bint _force_use + cdef class TryMap(Map): cdef Map _map_p cdef Map _map_b cdef _error_types + +cdef class CallableConvertMap(Map): + cdef bint _parent_as_first_arg + cdef _func + + cdef Map CCallableConvertMap(domain, codomain, void* func, name) diff --git a/src/sage/structure/coerce_maps.pyx b/src/sage/structure/coerce_maps.pyx index 911a1dc46f9..4d3a60c6f7e 100644 --- a/src/sage/structure/coerce_maps.pyx +++ b/src/sage/structure/coerce_maps.pyx @@ -28,7 +28,7 @@ cdef class DefaultConvertMap(Map): From: Rational Field To: Power Series Ring in x over Rational Field """ - def __init__(self, domain, codomain, category=None, force_use=False): + def __init__(self, domain, codomain, category=None): """ TESTS: @@ -57,7 +57,6 @@ cdef class DefaultConvertMap(Map): parent = domain.Hom(codomain, category=category) Map.__init__(self, parent) self._coerce_cost = 100 - self._force_use = force_use if (codomain)._element_constructor is None: raise RuntimeError("BUG in coercion model, no element constructor for {}".format(type(codomain))) @@ -74,15 +73,6 @@ cdef class DefaultConvertMap(Map): """ return self._repr_type_str or ("Coercion" if self._is_coercion else "Conversion") - cdef dict _extra_slots(self): - slots = Map._extra_slots(self) - slots['_force_use'] = self._force_use - return slots - - cdef _update_slots(self, dict _slots): - self._force_use = _slots['_force_use'] - Map._update_slots(self, _slots) - cpdef Element _call_(self, x): """ Create an element of the codomain from a single element of the domain. @@ -182,7 +172,7 @@ cdef class NamedConvertMap(Map): convert to ZZ, or a _rational_ method to convert to QQ. """ - def __init__(self, domain, codomain, method_name, force_use=False): + def __init__(self, domain, codomain, method_name): """ EXAMPLES:: @@ -200,7 +190,6 @@ cdef class NamedConvertMap(Map): domain = Set_PythonType(domain) Map.__init__(self, domain, codomain) self._coerce_cost = 400 - self._force_use = force_use self.method_name = method_name self._repr_type_str = "Conversion via %s method" % self.method_name @@ -227,8 +216,7 @@ cdef class NamedConvertMap(Map): True """ slots = Map._extra_slots(self) - slots['method_name'] = method_name=self.method_name - slots['_force_use'] = self._force_use + slots['method_name'] = self.method_name return slots cdef _update_slots(self, dict _slots): @@ -254,7 +242,6 @@ cdef class NamedConvertMap(Map): True """ self.method_name = _slots['method_name'] - self._force_use = _slots['_force_use'] Map._update_slots(self, _slots) cpdef Element _call_(self, x): @@ -308,9 +295,6 @@ cdef class NamedConvertMap(Map): # and constructing a CCallableConvertMap_class if it is bound to the codomain. cdef class CallableConvertMap(Map): - cdef bint _parent_as_first_arg - cdef _func - def __init__(self, domain, codomain, func, parent_as_first_arg=None): """ This lets one easily create maps from any callable object. diff --git a/src/sage/structure/parent.pxd b/src/sage/structure/parent.pxd index c9dc1ceacb8..2e2226b493b 100644 --- a/src/sage/structure/parent.pxd +++ b/src/sage/structure/parent.pxd @@ -48,6 +48,7 @@ cdef class Parent(sage.structure.category_object.CategoryObject): cpdef convert_map_from(self, S) cpdef _internal_convert_map_from(self, S) cpdef _convert_map_from_(self, S) + cdef convert_method_map(self, S, method_name) # returns the Action by/on self on/by S # corresponding to op and self_on_left diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index 3393a768be8..ee33eea6918 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -126,6 +126,8 @@ from sage.structure.misc import is_extension_type from sage.misc.lazy_attribute import lazy_attribute from sage.categories.sets_cat import Sets, EmptySetError from sage.misc.lazy_format import LazyFormat +from . coerce_maps cimport (NamedConvertMap, DefaultConvertMap, + DefaultConvertMap_unique, CallableConvertMap) cdef _record_exception(): @@ -1873,7 +1875,6 @@ cdef class Parent(sage.structure.category_object.CategoryObject): From: Integer Ring To: Univariate Polynomial Ring in x over Rational Field - TESTS: We check that :trac:`23184` has been resolved:: @@ -1882,7 +1883,6 @@ cdef class Parent(sage.structure.category_object.CategoryObject): Category of unique factorization domains sage: QQ[['x']].coerce_map_from(QQ).category_for() Category of euclidean domains - """ if isinstance(S, type): category = None @@ -1912,6 +1912,10 @@ cdef class Parent(sage.structure.category_object.CategoryObject): Conversion map: From: Finite Field of size 7 To: Finite Field of size 11 + sage: ZZ._generic_convert_map(RDF) + Conversion via _integer_ method map: + From: Real Double Field + To: Integer Ring TESTS: @@ -1919,27 +1923,45 @@ cdef class Parent(sage.structure.category_object.CategoryObject): sage: QQ[['x']].coerce_map_from(QQ).category_for() Category of euclidean domains + """ + m = self._convert_method_name + if m is not None: + f = self.convert_method_map(S, m) + if f is not None: + return f + if self._element_init_pass_parent: + return DefaultConvertMap(S, self, category=category) + else: + return DefaultConvertMap_unique(S, self, category=category) + def _convert_method_map(self, S, method_name=None): """ - from . import coerce_maps - if self._convert_method_name is not None: - # handle methods like _integer_ - if isinstance(S, type): - element_constructor = S - elif isinstance(S, Parent): - element_constructor = (S)._element_constructor - if not isinstance(element_constructor, type): - # if element_constructor is not an actual class, get the element class - element_constructor = type(S.an_element()) - else: - element_constructor = None - if element_constructor is not None and hasattr(element_constructor, self._convert_method_name): - return coerce_maps.NamedConvertMap(S, self, self._convert_method_name) + Return a map to convert from ``S`` to ``self`` using a convert + method like ``_integer_`` on elements of ``S``. - if self._element_init_pass_parent: - return coerce_maps.DefaultConvertMap(S, self, category=category) + OUTPUT: either an instance of :class:`NamedConvertMap` or + ``None`` if ``S`` does not have the method. + """ + # NOTE: in Cython code, call convert_method_map() directly + if method_name is None: + method_name = self._convert_method_name + return self.convert_method_map(S, method_name) + + cdef convert_method_map(self, S, method_name): + # Cython implementation of _convert_method_map() + cdef Parent P + if isinstance(S, Parent): + P = S + try: + element_cls = P.Element + except AttributeError: + element_cls = type(P.an_element()) else: - return coerce_maps.DefaultConvertMap_unique(S, self, category=category) + element_cls = S + if hasattr(element_cls, method_name): + return NamedConvertMap(S, self, method_name) + else: + return None def _coerce_map_via(self, v, S): """ @@ -2300,43 +2322,32 @@ cdef class Parent(sage.structure.category_object.CategoryObject): From: Number Field in b with defining polynomial x^2 + 2 To: Power Series Ring in x over Number Field in b with defining polynomial x^2 + 2 """ - best_mor = None if isinstance(S, Parent) and (S)._embedding is not None: if (S)._embedding.codomain() is self: return (S)._embedding - cdef map.Map mor user_provided_mor = self._coerce_map_from_(S) - if user_provided_mor is False: - user_provided_mor = None - - elif user_provided_mor is not None: - - from sage.categories.map import Map - from .coerce_maps import DefaultConvertMap, DefaultConvertMap_unique, NamedConvertMap, CallableConvertMap - - if user_provided_mor is True: - mor = self._generic_coerce_map(S) - elif isinstance(user_provided_mor, Map): - mor = user_provided_mor - elif callable(user_provided_mor): - mor = CallableConvertMap(S, self, user_provided_mor) - else: - raise TypeError("_coerce_map_from_ must return None, a boolean, a callable, or an explicit Map (called on %s, got %s)" % (type(self), type(user_provided_mor))) - - if (type(mor) is DefaultConvertMap or - type(mor) is DefaultConvertMap_unique or - type(mor) is NamedConvertMap) and not mor._force_use: - # If there is something better in the list, try to return that instead - # This is so, for example, _coerce_map_from_ can return True but still - # take advantage of the _populate_coercion_lists_ data. - best_mor = mor - else: - return mor + if user_provided_mor is None or user_provided_mor is False: + best_mor = None + elif user_provided_mor is True: + best_mor = self._generic_coerce_map(S) + if not isinstance(best_mor, DefaultConvertMap): + return best_mor + # Continue searching for better maps. If there is something + # better in the list, return that instead. This is so, for + # example, _coerce_map_from_ can return True but still take + # advantage of the _populate_coercion_lists_ data. + elif isinstance(user_provided_mor, map.Map): + return user_provided_mor + elif callable(user_provided_mor): + return CallableConvertMap(S, self, user_provided_mor) + else: + raise TypeError("_coerce_map_from_ must return None, a boolean, a callable, or an explicit Map (called on %s, got %s)" % (type(self), type(user_provided_mor))) from sage.categories.homset import Hom + cdef map.Map mor cdef int num_paths = 1 # this is the number of paths we find before settling on the best (the one with lowest coerce_cost). # setting this to 1 will make it return the first path found. cdef int mor_found = 0 @@ -2399,7 +2410,6 @@ cdef class Parent(sage.structure.category_object.CategoryObject): """ return copy(self._internal_convert_map_from(S)) - cpdef _internal_convert_map_from(self, S): """ This function returns a :class:`Map` from `S` to `self`, @@ -2465,7 +2475,6 @@ cdef class Parent(sage.structure.category_object.CategoryObject): if isinstance(user_provided_mor, map.Map): return user_provided_mor elif callable(user_provided_mor): - from .coerce_maps import CallableConvertMap return CallableConvertMap(S, self, user_provided_mor) else: raise TypeError("_convert_map_from_ must return a map or callable (called on %s, got %s)" % (type(self), type(user_provided_mor))) From b8b49c4a04529794cc4402e5cc2a4160d1131b3d Mon Sep 17 00:00:00 2001 From: Anne Schilling Date: Wed, 27 Dec 2017 17:31:00 -0800 Subject: [PATCH 448/740] fixed some typos in 22921 --- src/doc/en/reference/references/index.rst | 2 +- src/sage/combinat/partition.py | 2 -- src/sage/combinat/tableau.py | 1 + src/sage/combinat/tableau_shifted_primed.py | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 156159dd87b..1223a83684c 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1180,7 +1180,7 @@ REFERENCES: .. [HPS2017] Graham Hawkes, Kirill Paramonov, and Anne Schilling. *Crystal analysis of type* `C` *Stanley symmetric functions*. - Preprint, :arxiv:`1704.00889`. + Electronic J. Comb. 24(3) (2017) #P3.51. :arxiv:`1704.00889`. .. [HOLM2016] Tristan Holmes and \J. \B. Nation, *Inflation of finite lattices along all-or-nothing sets*. diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index e069a0d0f99..653363002ff 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -305,8 +305,6 @@ from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.sets.non_negative_integers import NonNegativeIntegers -from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets -from sage.sets.family import Family from sage.rings.all import QQ, ZZ, NN, IntegerModRing from sage.arith.all import factorial, gcd diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index f0cca08b583..05142187aa6 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -1446,6 +1446,7 @@ def reading_word_permutation(self): bottommost row (in English notation). EXAMPLES:: + sage: StandardTableau([[1,2],[3,4]]).reading_word_permutation() [3, 4, 1, 2] diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index 7b9b31f9ad4..34f81c4671b 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -367,7 +367,7 @@ def _repr_diagram(self): def _ascii_art_(self): """ - Return ASCII representaion of a tableau. + Return ASCII representation of a tableau. EXAMPLES:: From 64cb74d9fb91952dfa504193376ca8c8be9e2718 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Fri, 29 Dec 2017 23:16:26 +0500 Subject: [PATCH 449/740] Fixed numpy issues and missing docs. --- src/sage/combinat/tableau_shifted_primed.py | 144 ++++++++++++-------- 1 file changed, 89 insertions(+), 55 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index 34f81c4671b..5edf2a89110 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -141,9 +141,7 @@ def __init__(self, parent, T, skew=None): else: t_ = T - # FIXME: Remove numpy imports!!! - import numpy as np - if not all(isinstance(row, (list, tuple, np.ndarray)) for row in t_): + if not all(isinstance(row, (list, tuple)) for row in t_): t_ = [t_] t = [] @@ -220,16 +218,14 @@ def __eq__(self, other): def _to_matrix(self): """ - Return a 2-dimensional numpy.array representation of a shifted tableau + Return a 2-dimensional array representation of a shifted tableau EXAMPLES:: sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p'],[3]]) sage: mat = t._to_matrix() sage: mat - array([[ 1. , 1.5, 2. , 2. ], - [ 0. , 2. , 2.5, 0. ], - [ 0. , 0. , 3. , 0. ]]) + [[1.0, 1.5, 2.0, 2.0], [0, 2.0, 2.5, 0], [0, 0, 3.0, 0]] sage: t == ShiftedPrimedTableau(mat) True """ @@ -242,8 +238,6 @@ def _to_matrix(self): + [0]*(m-i-self._skew[i]-len(self[i]))) for i in range(sk_len, len(self)): array.append([0]*i + list(self[i]) + [0]*(m-i-len(self[i]))) - import numpy as np - array = np.array(array, dtype='float') return array def check(self): @@ -679,14 +673,18 @@ def _reading_word_with_positions(self): if self._skew is not None: raise NotImplementedError('skew tableau must be empty') mat = self._to_matrix() + ndim, mdim = len(mat), len(mat[0]) list_with_positions = [] - import numpy as np - for (i, j), x in np.ndenumerate(mat[:, ::-1].T): - if int(x) != x: - list_with_positions.append(((j, mat.shape[1]-i-1), int(x+0.5))) - for (i, j), x in np.ndenumerate(mat[::-1, :]): - if int(x) == x and int(x) != 0: - list_with_positions.append(((mat.shape[0]-i-1, j), int(x))) + for j in reversed(range(mdim)): + for i in range(ndim): + x = mat[i][j] + if int(x) != x: + list_with_positions.append(((i, j), int(x+0.5))) + for i in reversed(range(ndim)): + for j in range(mdim): + x = mat[i][j] + if int(x) == x and int(x) != 0: + list_with_positions.append(((i, j), int(x))) return list_with_positions def reading_word(self): @@ -752,12 +750,20 @@ def f(self, ind): 3 4 """ + if self is None: return None if self._skew is not None: raise NotImplementedError('skew tableau must be empty') + try: + self.parent()._weight + raise TypeError('operator generates an element outside of {}' + .format(self.parent())) + except AttributeError: + pass + T = self._to_matrix() read_word = self._reading_word_with_positions() @@ -768,8 +774,6 @@ def f(self, ind): element_to_change = None count = 0 - import numpy as np - for element in read_word: if element[1] == ind+1: count += 1 @@ -783,45 +787,47 @@ def f(self, ind): (r, c), elt = element_to_change - if T[r, c] == ind - .5: - T = T.T + .5 + if T[r][c] == ind - .5: + T = [[elt+.5 if elt != 0 else elt for elt in row] for row in T] + T = map(list, zip(*T)) r, c = c, r - h, l = T.shape + h, l = len(T), len(T[0]) - if (c+1 == l or T[r, c+1] >= ind+1 or T[r, c+1] < 1): + if (c+1 == l or T[r][c+1] >= ind+1 or T[r][c+1] < 1): (tp_r, tp_c) = (r, c) while True: if (tp_r+1 == h or - T[tp_r+1, tp_c] > ind+1 or - T[tp_r+1, tp_c] < 1): + T[tp_r+1][tp_c] > ind+1 or + T[tp_r+1][tp_c] < 1): break - if tp_r <= tp_c and T[tp_r+1, tp_r+1] == ind+1: + if tp_r <= tp_c and T[tp_r+1][tp_r+1] == ind+1: tp_r += 1 tp_c = tp_r break if (ind+.5 not in T[tp_r+1]): break tp_r += 1 - tp_c = np.where(T[tp_r] == ind+.5)[0] + tp_c = T[tp_r].index(ind+.5) if tp_r == r: - T[r, c] += 1 + T[r][c] += 1 elif tp_r == tp_c: - T[r, c] += .5 + T[r][c] += .5 else: - T[r, c] += .5 - T[tp_r, tp_c] += .5 + T[r][c] += .5 + T[tp_r][tp_c] += .5 - elif T[r, c+1] == ind+.5: - T[r, c+1] += .5 - T[r, c] += .5 + elif T[r][c+1] == ind+.5: + T[r][c+1] += .5 + T[r][c] += .5 if r > c: - T = T.T - .5 + T = [[elt-.5 if elt != 0 else elt for elt in row] for row in T] + T = map(list, zip(*T)) - return self.parent()(T) # FIXME: Generically this is not true + return self.parent()(T) def e(self, ind): """ @@ -859,6 +865,13 @@ def e(self, ind): if self._skew is not None: raise NotImplementedError('skew tableau must be empty') + try: + self.parent()._weight + raise TypeError('operator generates an element outside of {}' + .format(self.parent())) + except AttributeError: + pass + T = self._to_matrix() read_word = self._reading_word_with_positions() @@ -868,7 +881,6 @@ def e(self, ind): element_to_change = None count = 0 - import numpy as np for element in read_word[::-1]: if element[1] == ind: @@ -882,41 +894,42 @@ def e(self, ind): return None (r, c), elt = element_to_change - if T[r, c] == ind + .5: - T = T.T + .5 + if T[r][c] == ind + .5: + T = [[elt+.5 if elt != 0 else elt for elt in row] for row in T] + T = map(list, zip(*T)) r, c = c, r - h, l = T.shape - if (c == 0 or T[r, c-1] <= ind or T[r, c-1] < 1): + if (c == 0 or T[r][c-1] <= ind or T[r][c-1] < 1): (tp_r, tp_c) = (r, c) while True: - if tp_r == 0 or T[tp_r-1, tp_c] < ind or T[tp_r-1, tp_c] < 1: + if tp_r == 0 or T[tp_r-1][tp_c] < ind or T[tp_r-1][tp_c] < 1: break if (ind+.5 not in T[tp_r-1]): break tp_r -= 1 - tp_c = np.where(T[tp_r] == ind+.5)[0] + tp_c = T[tp_r].index(ind+.5) if tp_r == r: - T[r, c] -= 1 + T[r][c] -= 1 elif tp_r == tp_c: - T[r, c] -= .5 + T[r][c] -= .5 else: - T[r, c] -= .5 - T[tp_r, tp_c] -= .5 + T[r][c] -= .5 + T[tp_r][tp_c] -= .5 - elif T[r, c-1] == ind+.5: - T[r, c-1] -= .5 - T[r, c] -= .5 + elif T[r][c-1] == ind+.5: + T[r][c-1] -= .5 + T[r][c] -= .5 if r > c: - T = T.T - .5 + T = [[elt-.5 if elt != 0 else elt for elt in row] for row in T] + T = map(list, zip(*T)) - return self.parent()(T) # FIXME: Generically this is not true + return self.parent()(T) def is_highest_weight(self): """ @@ -1303,8 +1316,18 @@ class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): """ @staticmethod def __classcall_private__(cls, shape, max_elt=None, skew=None): + """ + Normalilize the attributes for the class. + + TEST:: + + sage: SPT = ShiftedPrimedTableaux(shape=[2,1]) + sage: SPT._shape.parent() + Partitions + """ shape = _Partitions(shape) - return super(ShiftedPrimedTableaux_shape, cls).__classcall__(cls, shape, max_elt, skew) + return (super(ShiftedPrimedTableaux_shape, cls) + .__classcall__(cls, shape, max_elt, skew)) Element = ShiftedPrimedTableau @@ -1416,6 +1439,14 @@ def _element_constructor_(self, T): def module_generators(self): """ Return the generators of ``self`` as a crystal. + + TEST:: + + sage: SPT = ShiftedPrimedTableaux(shape=[2,1]) + sage: SPT.module_generators + ([(1.0, 1.0), (2.0,)], + [(1.0, 2.0), (3.0,)], + [(1.0, 1.5), (3.0,)]) """ if self._skew is not None: raise NotImplementedError("only for non-skew shapes") @@ -1483,9 +1514,12 @@ def list_decreasing_weight(self): max_element = sum(self._shape) else: max_element = self._max_elt - for weight in Partition(self._shape).dominated_partitions(rows=max_element): - list_dw.extend([self(T) for T in ShiftedPrimedTableaux(weight=tuple(weight), - shape=self._shape)]) + for weight in (Partition(self._shape) + .dominated_partitions(rows=max_element)): + list_dw.extend([self(T) + for T in ShiftedPrimedTableaux( + weight=tuple(weight), + shape=self._shape)]) return list_dw def list_highest_weight(self): From b1d7d2e55e04a298a3a611c041dfa6bfa74adf2c Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Mon, 1 Jan 2018 14:36:42 +0500 Subject: [PATCH 450/740] Fixed reference to the paper --- src/sage/combinat/tableau_shifted_primed.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index 5edf2a89110..4d503154196 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -832,7 +832,7 @@ def f(self, ind): def e(self, ind): """ Compute the action of the crystal operator `e_i` on a Shifted Primed - Tableau using cases from the paper [HPS.17]. + Tableau using cases from the paper [HPS2017]_. INPUT: @@ -1290,7 +1290,7 @@ class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): The list of module generators consists of all elements of the crystal with nonincreasing weight. - Crystal is constructed following operations described in [HPS17]_. + Crystal is constructed following operations described in [HPS2017]_. EXAMPLES:: From 8aaf077b7c3fd4183a9c2b41f6e46ef83cd4a93d Mon Sep 17 00:00:00 2001 From: Anne Schilling Date: Mon, 1 Jan 2018 17:28:42 -0800 Subject: [PATCH 451/740] Fix some documentation --- src/sage/combinat/tableau_shifted_primed.py | 101 ++++++++++---------- 1 file changed, 50 insertions(+), 51 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index 4d503154196..2b8c318dbb3 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -43,13 +43,13 @@ class ShiftedPrimedTableau(ClonableArray): r""" A shifted primed tableau with primed elements stored as half-integers. - A primed tableau is a shifted tableau on the alphabet + A primed tableau is a tableau of shifted shape in the alphabet `X' = \{1' < 1 < 2' < 2 < \cdots < n' < n\}` such that - 1. the entries are weakly increasing along rows and columns - 2. a row can't have two repeated primed elements, and a column - can't have two repeated non-primed elements - 3. there are only non-primed elements on the main diagonal + 1. The entries are weakly increasing along rows and columns; + 2. A row cannot have two repeated primed elements, and a column + cannot have two repeated non-primed elements; + 3. There are only non-primed elements on the main diagonal. EXAMPLES:: @@ -90,10 +90,8 @@ def __classcall_private__(cls, T, skew=None): sage: t.parent() Shifted Primed Tableaux skewed by [2, 1] """ - if (isinstance(T, cls) and T._skew == skew): return T - return ShiftedPrimedTableaux(skew=skew)(T) def __init__(self, parent, T, skew=None): @@ -198,9 +196,7 @@ def __eq__(self, other): - ``other`` -- the element that ``self`` is compared to - OUTPUT: - - Boolean. + OUTPUT: Boolean EXAMPLES:: @@ -218,7 +214,7 @@ def __eq__(self, other): def _to_matrix(self): """ - Return a 2-dimensional array representation of a shifted tableau + Return a 2-dimensional array representation of a shifted tableau. EXAMPLES:: @@ -242,7 +238,7 @@ def _to_matrix(self): def check(self): """ - Check that ``self`` is a valid primed tableaux. + Check that ``self`` is a valid primed tableau. EXAMPLES:: @@ -251,6 +247,10 @@ def check(self): sage: t.check() sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) sage: s.check() + sage: t = T([['1p','2p',2,2],[2,'3p']]) + Traceback (most recent call last): + .... + ValueError: [['1p', '2p', 2, 2], [2, '3p']] is not an element of Shifted Primed Tableaux of shape [4, 2] """ if self._skew is not None: if not all(self._skew[i] > self._skew[i+1] @@ -289,8 +289,7 @@ def check(self): def _repr_(self): """ - Represent Shifted Primed Tableau as a list of rows, - rows are represented as tuples of half-integers. + Represent ``self`` as a list of rows with rows represented as tuples of half-integers. EXAMPLES:: @@ -361,7 +360,7 @@ def _repr_diagram(self): def _ascii_art_(self): """ - Return ASCII representation of a tableau. + Return ASCII representation of ``self``. EXAMPLES:: @@ -390,7 +389,7 @@ def _ascii_art_(self): def _unicode_art_(self): """ - Return a Unicode representation of a tableau. + Return a Unicode representation of ``self``. EXAMPLES:: @@ -587,12 +586,12 @@ def __call__(self, *cell): INPUT: - - ``cell`` -- a pair of integers, tuple, or list specifying a cell in + - ``cell`` -- A pair of integers, tuple, or list specifying a cell in the tableau. OUTPUT: - - the element in the corresponding cell. if the element is primed, + - The element in the corresponding cell. If the element is primed, returns half-integer value. EXAMPLES:: @@ -653,15 +652,15 @@ def _reading_word_with_positions(self): The reading word of a shifted primed tableau is constructed as follows: - 1. List all primed letters in the tableau, column by - column, in decreasing order within each column, moving - from the rightmost column to the left, and with all - the primes removed (i.e. all letters are increased by - half a unit). + 1. List all primed letters in the tableau, column by + column, in decreasing order within each column, moving + from the rightmost column to the left, and with all + the primes removed (i.e. all letters are increased by + half a unit). - 2. Then list all unprimed elements, row by row, in - increasing order within each row, moving from the - bottommost row to the top. + 2. Then list all unprimed elements, row by row, in + increasing order within each row, moving from the + bottommost row to the top. EXAMPLES:: @@ -694,15 +693,15 @@ def reading_word(self): The reading word of a shifted primed tableau is constructed as follows: - 1. List all primed letters in the tableau, column by - column, in decreasing order within each column, moving - from the rightmost column to the left, and with all - the primes removed (i.e. all letters are increased by - half a unit). + 1. List all primed letters in the tableau, column by + column, in decreasing order within each column, moving + from the rightmost column to the left, and with all + the primes removed (i.e. all letters are increased by + half a unit). - 2. Then list all unprimed elements, row by row, in - increasing order within each row, moving from the - bottommost row to the top. + 2. Then list all unprimed elements, row by row, in + increasing order within each row, moving from the + bottommost row to the top. EXAMPLES:: @@ -721,7 +720,7 @@ def f(self, ind): INPUT: - - ``ind`` -- index of the crystal operator `f_i` + - ``ind`` -- element in the index set of the crystal OUTPUT: @@ -836,7 +835,7 @@ def e(self, ind): INPUT: - - ``ind`` -- index of the crystal operator `e_i` + - ``ind`` -- an element in the index set of the crystal. OUTPUT: @@ -933,10 +932,9 @@ def e(self, ind): def is_highest_weight(self): """ - Check wether the shifted primed tableau ``self`` is a highest weight - element of the crystal. + Return whether ``self`` is a highest weight element of the crystal. - An element is highest weight if it vanishes under any crystal operator + An element is highest weight if it vanishes under all crystal operators `e_i`. EXAMPLES:: @@ -992,16 +990,18 @@ class ShiftedPrimedTableaux(UniqueRepresentation, Parent): - with a tuple argument, the class of all primed tableaux of that weight (finite set) - A primed tableau is a shifted tableau on the alphabet + A primed tableau is a tableau of shifted shape on the alphabet `X' = \{1' < 1 < 2' < 2 < \cdots < n' < n\}` such that - 1. the entries are weakly increasing along rows and columns - 2. a row can't have two repeated primed elements, and a column - can't have two repeated non-primed elements - 3. there are only non-primed elements along the main diagonal + 1. the entries are weakly increasing along rows and columns + + 2. a row cannot have two repeated primed elements, and a column + cannot have two repeated non-primed elements + + 3. there are only non-primed elements along the main diagonal - The weight of a tableau is defined to be the vector with i-th - component equal to the number of letters i and i' in the tableau. + The weight of a tableau is defined to be the vector with `i`-th + component equal to the number of letters `i` and `i'` in the tableau. The sum of the entries in the weight vector must be equal to the number of boxes in the partition. @@ -1284,13 +1284,13 @@ class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): """ Shifted Primed Tableaux of a fixed shape. - Shifted prime tableaux admit a type `A_n` classical crystal structure + Shifted primed tableaux admit a type `A_n` classical crystal structure with highest weights corresponding to a given shape. The list of module generators consists of all elements of the crystal with nonincreasing weight. - Crystal is constructed following operations described in [HPS2017]_. + The crystal is constructed following operations described in [HPS2017]_. EXAMPLES:: @@ -1317,7 +1317,7 @@ class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): @staticmethod def __classcall_private__(cls, shape, max_elt=None, skew=None): """ - Normalilize the attributes for the class. + Normalize the attributes for the class. TEST:: @@ -1524,8 +1524,7 @@ def list_decreasing_weight(self): def list_highest_weight(self): """ - List elements of ``self`` that are highest weight elements - in the crystal. + List the highest weight elements of ``self``. EXAMPLES:: From df0f1192d57d634290cad6612257bbed2e6a84ba Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Tue, 2 Jan 2018 12:41:33 +0500 Subject: [PATCH 452/740] Removed call, changed shape type --- src/sage/combinat/tableau_shifted_primed.py | 66 +++++---------------- 1 file changed, 14 insertions(+), 52 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index 2b8c318dbb3..2e4c54cca61 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -21,6 +21,7 @@ from six import add_metaclass from sage.combinat.partition import Partition, Partitions, _Partitions, OrderedPartitions +from sage.combinat.skew_partition import SkewPartition from sage.combinat.integer_vector import IntegerVectors from sage.rings.integer import Integer @@ -250,7 +251,8 @@ def check(self): sage: t = T([['1p','2p',2,2],[2,'3p']]) Traceback (most recent call last): .... - ValueError: [['1p', '2p', 2, 2], [2, '3p']] is not an element of Shifted Primed Tableaux of shape [4, 2] + ValueError: [['1p', '2p', 2, 2], [2, '3p']] is not an element of + Shifted Primed Tableaux of shape [4, 2] """ if self._skew is not None: if not all(self._skew[i] > self._skew[i+1] @@ -289,7 +291,8 @@ def check(self): def _repr_(self): """ - Represent ``self`` as a list of rows with rows represented as tuples of half-integers. + Represent ``self`` as a list of rows with rows represented as tuples of + half-integers. EXAMPLES:: @@ -571,56 +574,15 @@ def shape(self): [4, 2] sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) sage: s.shape() - [5, 2] - """ + [5, 2] / [2, 1] + """ if self._skew is None: - return ([len(_) for _ in self]) - return ([len(self[i])+self._skew[i] - for i in range(len(self._skew))] + - [len(self[i]) - for i in range(len(self._skew), len(self))]) - - def __call__(self, *cell): - """ - Function call of ``self``. - - INPUT: - - - ``cell`` -- A pair of integers, tuple, or list specifying a cell in - the tableau. - - OUTPUT: - - - The element in the corresponding cell. If the element is primed, - returns half-integer value. - - EXAMPLES:: - - sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) - sage: t(1,0) - 2.0 - sage: t((1,2)) - Traceback (most recent call last): - ... - IndexError: invalid cell - sage: t((1,1)) - 2.5 - sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) - sage: s(0,2) - 1.5 - """ - try: - i, j = cell - except ValueError: - i, j = cell[0] - if self._skew is not None: - skew = self._skew[i] if i < len(self._skew) else 0 - else: - skew = 0 - try: - return self[i][j-skew] - except IndexError: - raise IndexError("invalid cell") + return Partition([len(_) for _ in self]) + return SkewPartition(([len(self[i])+self._skew[i] + for i in range(len(self._skew))] + + [len(self[i]) + for i in range(len(self._skew), len(self))], + self._skew)) def weight(self): """ @@ -997,7 +959,7 @@ class ShiftedPrimedTableaux(UniqueRepresentation, Parent): 2. a row cannot have two repeated primed elements, and a column cannot have two repeated non-primed elements - + 3. there are only non-primed elements along the main diagonal The weight of a tableau is defined to be the vector with `i`-th From 4606309005099356eda39142a9abc425d341337e Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Fri, 5 Jan 2018 15:35:38 +0100 Subject: [PATCH 453/740] LinearFunctionOrConstraint.__richcmp__ should replace before converting --- src/sage/numerical/linear_functions.pxd | 2 +- src/sage/numerical/linear_functions.pyx | 130 ++++++++++++++---------- 2 files changed, 77 insertions(+), 55 deletions(-) diff --git a/src/sage/numerical/linear_functions.pxd b/src/sage/numerical/linear_functions.pxd index 284a5b34aa7..568f04b75af 100644 --- a/src/sage/numerical/linear_functions.pxd +++ b/src/sage/numerical/linear_functions.pxd @@ -4,7 +4,7 @@ from sage.structure.element cimport ModuleElement, RingElement, Element cpdef is_LinearFunction(x) cdef class LinearFunctionOrConstraint(ModuleElement): - cdef void chained_comparator_hack_nonzero(self) + pass cdef class LinearFunctionsParent_class(Parent): cpdef _element_constructor_(self, x) diff --git a/src/sage/numerical/linear_functions.pyx b/src/sage/numerical/linear_functions.pyx index de1bf534ff9..e11744e984f 100644 --- a/src/sage/numerical/linear_functions.pyx +++ b/src/sage/numerical/linear_functions.pyx @@ -62,11 +62,19 @@ inequalities as less or equal:: sage: from sage.numerical.linear_functions import LinearFunctionsParent sage: LF = LinearFunctionsParent(QQ) - sage: x[0] <= LF(3) <= LF(4) - x_0 <= 3 <= 4 + sage: x[1] <= LF(3) <= LF(4) + x_1 <= 3 <= 4 TESTS: +This was fixed in :trac:`24423`:: + + sage: p. = MixedIntegerLinearProgram() + sage: from sage.numerical.linear_functions import LinearFunctionsParent + sage: LF = LinearFunctionsParent(QQ) + sage: 3 <= x[0] <= LF(4) + 3 <= x_0 <= 4 + See :trac:`12091`:: sage: p = MixedIntegerLinearProgram() @@ -339,25 +347,20 @@ cdef class LinearFunctionOrConstraint(ModuleElement): sage: float(0) <= b[0] <= int(1) <= b[1] <= float(2) 0 <= x_0 <= 1 <= x_1 <= 2 """ + # Store the chaining variables and set them to None for now in + # order to have a sane state for any conversions below or when + # an exception is raised. global chained_comparator_left global chained_comparator_right global chained_comparator_replace - # Ensure that we use chained_comparator_replace only for this - # call to __richcmp__. - cdef LinearConstraint replace = chained_comparator_replace - chained_comparator_replace = None + chain_left = chained_comparator_left + chain_right = chained_comparator_right + chain_replace = chained_comparator_replace - # At least one of py_left or py_right must be of type - # LinearFunctionOrConstraint: figure out its parent - try: - LC = (py_left)._parent - except TypeError: - LC = (py_right)._parent - - # We want the parent to be a LinearConstraintsParent - if not isinstance(LC, LinearConstraintsParent_class): - LC = LinearConstraintsParent(LC) + chained_comparator_left = None + chained_comparator_right = None + chained_comparator_replace = None # Check op and change >= to <= cdef bint equality = False @@ -374,25 +377,10 @@ cdef class LinearFunctionOrConstraint(ModuleElement): else: raise ValueError("inequality != is not allowed, use one of <=, ==, >=") - # Convert py_left and py_right to constraints left and right - cdef LinearConstraint left - cdef LinearConstraint right - - try: - left = py_left - except TypeError: - left = LC(py_left, equality=equality) - else: - if left._parent is not LC: - left = LC(left.constraints, equality=left.equality) - - try: - right = py_right - except TypeError: - right = LC(py_right, equality=equality) - else: - if right._parent is not LC: - right = LC(right.constraints, equality=right.equality) + # Convert py_left and py_right to constraints left and right, + # possibly replacing them to implement the hack below. + cdef LinearConstraint left = None + cdef LinearConstraint right = None # HACK to allow chained constraints: Python translates # x <= y <= z into: @@ -416,39 +404,72 @@ cdef class LinearFunctionOrConstraint(ModuleElement): # work is to store x and y in the first call to __richcmp__ # and temp in the call to __nonzero__. Then we can replace x # or y by x <= y in the second call to __richcmp__. - if replace is not None: + if chain_replace is not None: # First, check for correctly-chained inequalities # x <= y <= z or z <= y <= x. - if py_left is chained_comparator_right: - left = replace - elif py_right is chained_comparator_left: - right = replace + if py_left is chain_right: + left = chain_replace + elif py_right is chain_left: + right = chain_replace # Next, check for wrongly-chained inequalities like # x <= y >= z. In case of equality, these are accepted # anyway. - elif py_left is chained_comparator_left: + elif py_left is chain_left: if not equality: raise ValueError("incorrectly chained inequality") - left = replace - elif py_right is chained_comparator_right: + left = chain_replace + elif py_right is chain_right: if not equality: raise ValueError("incorrectly chained inequality") - right = replace + right = chain_replace + + # Figure out the correct parent to work with: if we did a + # replacement, its parent takes priority. + if left is not None: + LC = left._parent + elif right is not None: + LC = right._parent + else: + # At least one of the inputs must be of type + # LinearFunctionOrConstraint + try: + LC = (py_left)._parent + except TypeError: + LC = (py_right)._parent + + # We want the parent to be a LinearConstraintsParent + if not isinstance(LC, LinearConstraintsParent_class): + LC = LinearConstraintsParent(LC) + + if left is None: + try: + left = py_left + except TypeError: + left = LC(py_left, equality=equality) + else: + if left._parent is not LC: + left = LC(left.constraints, equality=left.equality) - chained_comparator_left = py_left - chained_comparator_right = py_right + if right is None: + try: + right = py_right + except TypeError: + right = LC(py_right, equality=equality) + else: + if right._parent is not LC: + right = LC(right.constraints, equality=right.equality) # Finally, chain the (in)equalities if left.equality != equality or right.equality != equality: raise ValueError("cannot mix equations and inequalities") - return LC(left.constraints + right.constraints, equality=equality) + ret = LC(left.constraints + right.constraints, equality=equality) - cdef void chained_comparator_hack_nonzero(self): - """ - Hack to allow chained (in)equalities, see ``__richcmp__``. - """ - global chained_comparator_replace - chained_comparator_replace = self + # Everything done, so we set the chaining variables + chained_comparator_left = py_left + chained_comparator_right = py_right + chained_comparator_replace = None + + return ret def __hash__(self): r""" @@ -1651,5 +1672,6 @@ cdef class LinearConstraint(LinearFunctionOrConstraint): sage: ieq <= ieq <= ieq x_0 <= 9 + x_1 <= x_0 <= 9 + x_1 <= x_0 <= 9 + x_1 """ - self.chained_comparator_hack_nonzero() + global chained_comparator_replace + chained_comparator_replace = self return True From 8850224e15f67795770e75a9762a9c129991ffbf Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Fri, 5 Jan 2018 14:48:27 -0800 Subject: [PATCH 454/740] Fixed weight type for crystals, minor skew tableaux fixes. --- src/sage/combinat/tableau_shifted_primed.py | 48 ++++++++++++--------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index 2e4c54cca61..828701759f1 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -604,7 +604,9 @@ def weight(self): else: max_ind = int(max(flat)) weight = tuple([flat.count(i+1) for i in range(max_ind)]) - return tuple(weight) + if isinstance(self.parent(), ShiftedPrimedTableaux_shape): + return self.parent().weight_lattice_realization()(weight) + return weight def _reading_word_with_positions(self): """ @@ -1050,12 +1052,8 @@ def __classcall_private__(cls, *args, **kwargs): if 'max' in kwargs: max_element = int(kwargs['max']) - if 'size' in kwargs and isinstance(kwargs['size'], - (list, tuple, Partition)): - shape = list(kwargs['size']) - if 'shape' in kwargs: - shape = list(kwargs['shape']) + shape = kwargs['shape'] if 'weight' in kwargs: weight = tuple(kwargs['weight']) @@ -1064,14 +1062,16 @@ def __classcall_private__(cls, *args, **kwargs): if isinstance(args[0], tuple) and weight is None: weight = args[0] if len(args) > 1: - if ((isinstance(args[1], (list, Partition)) or + if ((isinstance(args[1], + (list, Partition, SkewPartition)) or args[1] is None) and shape is None): shape = args[1] else: raise ValueError( 'invalid argument for weight or shape') - elif (isinstance(args[0], (list, Partition)) and shape is None): + elif (isinstance(args[0], (list, Partition, SkewPartition)) + and shape is None): shape = args[0] if len(args) > 1: if ((isinstance(args[1], tuple) or args[1] is None) and @@ -1084,6 +1084,9 @@ def __classcall_private__(cls, *args, **kwargs): raise ValueError('invalid argument for weight or shape') if shape is not None: + if isinstance(shape, SkewPartition): + skew = shape.inner() + shape = shape.outer() try: shape = Partition(shape) except ValueError: @@ -1289,7 +1292,8 @@ def __classcall_private__(cls, shape, max_elt=None, skew=None): """ shape = _Partitions(shape) return (super(ShiftedPrimedTableaux_shape, cls) - .__classcall__(cls, shape, max_elt, skew)) + .__classcall__(cls, shape=shape, max_elt=max_elt, + skew=skew)) Element = ShiftedPrimedTableau @@ -1320,8 +1324,11 @@ def __init__(self, shape, max_elt, skew): ShiftedPrimedTableaux.__init__(self, category=category) self._max_elt = max_elt - self._shape = shape self._skew = skew + if skew is None: + self._shape = Partition(shape) + else: + self._shape = SkewPartition((shape, skew)) def _repr_(self): """ @@ -1332,11 +1339,9 @@ def _repr_(self): sage: ShiftedPrimedTableaux([3,2,1]) Shifted Primed Tableaux of shape [3, 2, 1] """ - base = "Shifted Primed Tableaux of shape {}".format(self._shape) + base = "Shifted Primed Tableaux of shape " + self._shape._repr_() if self._max_elt is not None: base += " and maximum element {}".format(self._max_elt) - if self._skew is not None: - base += " skewed by {}".format(self._shape, self._skew) return base def __contains__(self, T): @@ -1553,6 +1558,7 @@ def __contains__(self, T): Tab = self.element_class(self, T, skew=self._skew) except ValueError: return False + return Tab.weight() == self._weight def _element_constructor_(self, T): @@ -1582,6 +1588,7 @@ def _element_constructor_(self, T): "{} is not an element of {}".format(T, self)) if Tab.weight() == self._weight: return Tab + raise ValueError("{} is not an element of {}".format(T, self)) def __iter__(self): @@ -1630,8 +1637,11 @@ def __init__(self, weight, shape, skew=None): """ Parent.__init__(self, category=FiniteEnumeratedSets()) self._weight = weight - self._shape = shape self._skew = skew + if skew is None: + self._shape = Partition(shape) + else: + self._shape = SkewPartition((shape, skew)) def _repr_(self): """ @@ -1642,13 +1652,9 @@ def _repr_(self): sage: ShiftedPrimedTableaux([3,2,1],(4,2)) Shifted Primed Tableaux of weight (4, 2) and shape [3, 2, 1] """ - if self._skew is None: - return ( - "Shifted Primed Tableaux of weight {} and shape {}" - .format(self._weight, self._shape)) return ( - "Shifted Primed Tableaux of weight {} and shape {} skewed by {}" - .format(self._weight, self._shape, self._skew)) + "Shifted Primed Tableaux of weight {} and shape {}" + .format(self._weight, self._shape)) def __contains__(self, T): """ @@ -1663,7 +1669,7 @@ def __contains__(self, T): except ValueError: return False - return (Tab.weight() == self._weight and Tab.shape() == self._shape) + return Tab.weight() == self._weight and Tab.shape() == self._shape def _element_constructor_(self, T): """ From 1da4b48716cc80492cce88ddfa5739d8fe0ff34c Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Fri, 5 Jan 2018 15:48:53 -0800 Subject: [PATCH 455/740] Removed list_highest_weight --- src/sage/combinat/tableau_shifted_primed.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index 828701759f1..770ef061df5 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -988,8 +988,6 @@ class ShiftedPrimedTableaux(UniqueRepresentation, Parent): Shifted Primed Tableaux of shape [3, 2] and maximum element 2 sage: list(SPT) [[(1.0, 1.0, 1.0), (2.0, 2.0)], [(1.0, 1.0, 1.5), (2.0, 2.0)]] - sage: SPT.list_highest_weight() - [[(1.0, 1.0, 1.0), (2.0, 2.0)]] .. SEEALSO:: @@ -1489,24 +1487,6 @@ def list_decreasing_weight(self): shape=self._shape)]) return list_dw - def list_highest_weight(self): - """ - List the highest weight elements of ``self``. - - EXAMPLES:: - - sage: Tabs = ShiftedPrimedTableaux([3,1]) - sage: Tabs.list_highest_weight() - [[(1.0, 1.0, 1.0), (2.0,)], - [(1.0, 1.0, 1.5), (2.0,)], - [(1.0, 1.0, 2.5), (2.0,)]] - """ - if self._skew is not None: - raise NotImplementedError('skew tableau must be empty') - return [tab - for tab in self.list_decreasing_weight() - if tab.is_highest_weight()] - class ShiftedPrimedTableaux_weight(ShiftedPrimedTableaux): """ From c53f45c7fe243ba5ef2e53dd54b428d3d8218ee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 6 Jan 2018 00:51:58 +0100 Subject: [PATCH 456/740] Fix doctests residue field implementation has changed apparently. I don't understand why we print that information but anyway, we certainly don't care here. --- src/doc/en/reference/valuations/index.rst | 2 +- src/sage/rings/function_field/function_field_valuation.py | 4 ++-- src/sage/rings/valuation/augmented_valuation.py | 2 +- src/sage/rings/valuation/gauss_valuation.py | 2 +- src/sage/rings/valuation/valuation_space.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/doc/en/reference/valuations/index.rst b/src/doc/en/reference/valuations/index.rst index 20375496faf..ed674037962 100644 --- a/src/doc/en/reference/valuations/index.rst +++ b/src/doc/en/reference/valuations/index.rst @@ -150,7 +150,7 @@ constants:: sage: K. = FunctionField(QQ) sage: w = w.extension(K) sage: w.residue_field() - Fraction Field of Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) + Fraction Field of Univariate Polynomial Ring in x over Finite Field of size 2 (using ...) Mac Lane Approximants --------------------- diff --git a/src/sage/rings/function_field/function_field_valuation.py b/src/sage/rings/function_field/function_field_valuation.py index 8a9d686545b..64d3ca3ac71 100644 --- a/src/sage/rings/function_field/function_field_valuation.py +++ b/src/sage/rings/function_field/function_field_valuation.py @@ -822,13 +822,13 @@ def residue_ring(self): sage: v = valuations.GaussValuation(QQ['x'], QQ.valuation(2)) sage: w = K.valuation(v) sage: w.residue_ring() - Fraction Field of Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) + Fraction Field of Univariate Polynomial Ring in x over Finite Field of size 2 (using ...) sage: R. = QQ[] sage: vv = v.augmentation(x, 1) sage: w = K.valuation(vv) sage: w.residue_ring() - Fraction Field of Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) + Fraction Field of Univariate Polynomial Ring in x over Finite Field of size 2 (using ...) """ return self._base_valuation.residue_ring().fraction_field() diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index e3290d750a6..eca54abf6af 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -1155,7 +1155,7 @@ def residue_ring(self): sage: w = v.augmentation(x, 1) sage: w.residue_ring() - Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) + Univariate Polynomial Ring in x over Finite Field of size 2 (using ...) """ from sage.categories.fields import Fields diff --git a/src/sage/rings/valuation/gauss_valuation.py b/src/sage/rings/valuation/gauss_valuation.py index 5f6cd5b6b9b..935361460e5 100644 --- a/src/sage/rings/valuation/gauss_valuation.py +++ b/src/sage/rings/valuation/gauss_valuation.py @@ -297,7 +297,7 @@ def residue_ring(self): sage: S. = Qp(2,5)[] sage: v = GaussValuation(S) sage: v.residue_ring() - Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) + Univariate Polynomial Ring in x over Finite Field of size 2 (using ...) """ return self.domain().change_ring(self._base_valuation.residue_ring()) diff --git a/src/sage/rings/valuation/valuation_space.py b/src/sage/rings/valuation/valuation_space.py index 258f8cc7f4f..e1958fd1cf7 100644 --- a/src/sage/rings/valuation/valuation_space.py +++ b/src/sage/rings/valuation/valuation_space.py @@ -504,7 +504,7 @@ def residue_ring(self): sage: valuations.TrivialValuation(ZZ).residue_ring() Integer Ring sage: GaussValuation(ZZ['x'], ZZ.valuation(2)).residue_ring() - Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL) + Univariate Polynomial Ring in x over Finite Field of size 2 (using ...) """ From 1a03e0b7947ad9ed06a1e21b81cd5660880972ff Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Fri, 5 Jan 2018 17:10:58 -0800 Subject: [PATCH 457/740] Removed list_decreasing_weight --- src/sage/combinat/tableau_shifted_primed.py | 38 +++++++-------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py index 770ef061df5..ec97e0b4a75 100644 --- a/src/sage/combinat/tableau_shifted_primed.py +++ b/src/sage/combinat/tableau_shifted_primed.py @@ -1415,7 +1415,18 @@ def module_generators(self): """ if self._skew is not None: raise NotImplementedError("only for non-skew shapes") - return tuple(self.list_decreasing_weight()) + list_dw = [] + if self._max_elt is None: + max_element = sum(self._shape) + else: + max_element = self._max_elt + for weight in (Partition(self._shape) + .dominated_partitions(rows=max_element)): + list_dw.extend([self(T) + for T in ShiftedPrimedTableaux( + weight=tuple(weight), + shape=self._shape)]) + return tuple(list_dw) def shape(self): """ @@ -1462,31 +1473,6 @@ def __iter__(self): weight=weight_n): yield self(tab) - def list_decreasing_weight(self): - """ - List elements of ``self`` with weakly decreasing weight. - - EXAMPLES:: - - sage: Tabs = ShiftedPrimedTableaux([2,1]) - sage: Tabs.list_decreasing_weight() - [[(1.0, 1.0), (2.0,)], [(1.0, 2.0), (3.0,)], [(1.0, 1.5), (3.0,)]] - """ - if self._skew is not None: - raise NotImplementedError('skew tableau must be empty') - list_dw = [] - if self._max_elt is None: - max_element = sum(self._shape) - else: - max_element = self._max_elt - for weight in (Partition(self._shape) - .dominated_partitions(rows=max_element)): - list_dw.extend([self(T) - for T in ShiftedPrimedTableaux( - weight=tuple(weight), - shape=self._shape)]) - return list_dw - class ShiftedPrimedTableaux_weight(ShiftedPrimedTableaux): """ From eab9d4f6b3011898d8ce515f696cef7ff082e0b4 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sat, 6 Jan 2018 10:00:39 -0800 Subject: [PATCH 458/740] Minor fixes --- src/sage/combinat/all.py | 2 +- src/sage/combinat/crystals/catalog.py | 4 +- src/sage/combinat/tableau_shifted_primed.py | 1784 ------------------- 3 files changed, 3 insertions(+), 1787 deletions(-) delete mode 100644 src/sage/combinat/tableau_shifted_primed.py diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index d3efd95b0f7..cecdc91814c 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -95,7 +95,7 @@ from .k_tableau import WeakTableau, WeakTableaux, StrongTableau, StrongTableaux lazy_import('sage.combinat.lr_tableau', ['LittlewoodRichardsonTableau', 'LittlewoodRichardsonTableaux']) -lazy_import('sage.combinat.tableau_shifted_primed', ['ShiftedPrimedTableaux', +lazy_import('sage.combinat.shifted_primed_tableau', ['ShiftedPrimedTableaux', 'ShiftedPrimedTableau']) #Words diff --git a/src/sage/combinat/crystals/catalog.py b/src/sage/combinat/crystals/catalog.py index 3f9b3a693c5..bcea2c4e7ca 100644 --- a/src/sage/combinat/crystals/catalog.py +++ b/src/sage/combinat/crystals/catalog.py @@ -33,7 +33,7 @@ * :class:`RiggedConfigurations ` * :class:`ShiftedPrimedTableaux - ` + ` * :class:`Spins ` * :class:`SpinsPlus ` * :class:`SpinsMinus ` @@ -77,7 +77,7 @@ from sage.combinat.rigged_configurations.tensor_product_kr_tableaux import TensorProductOfKirillovReshetikhinTableaux from sage.combinat.crystals.kirillov_reshetikhin import KirillovReshetikhinCrystal as KirillovReshetikhin from sage.combinat.rigged_configurations.rc_crystal import CrystalOfRiggedConfigurations as RiggedConfigurations -from sage.combinat.tableau_shifted_primed import ShiftedPrimedTableaux_shape as ShiftedPrimedTableaux +from sage.combinat.shifted_primed_tableau import ShiftedPrimedTableaux_shape as ShiftedPrimedTableaux from sage.combinat.crystals.induced_structure import InducedCrystal as Induced diff --git a/src/sage/combinat/tableau_shifted_primed.py b/src/sage/combinat/tableau_shifted_primed.py deleted file mode 100644 index ec97e0b4a75..00000000000 --- a/src/sage/combinat/tableau_shifted_primed.py +++ /dev/null @@ -1,1784 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Shifted primed tableaux - -AUTHORS: - -- Kirill Paramonov (2017-08-18): initial implementation -""" - -#***************************************************************************** -# Copyright (C) 2017 Kirill Paramonov , -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - -from __future__ import print_function, absolute_import -from six import add_metaclass - -from sage.combinat.partition import Partition, Partitions, _Partitions, OrderedPartitions -from sage.combinat.skew_partition import SkewPartition -from sage.combinat.integer_vector import IntegerVectors -from sage.rings.integer import Integer - -from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass -from sage.misc.lazy_attribute import lazy_attribute - -from sage.structure.list_clone import ClonableArray -from sage.structure.parent import Parent -from sage.structure.unique_representation import UniqueRepresentation - -from sage.categories.regular_crystals import RegularCrystals -from sage.categories.classical_crystals import ClassicalCrystals -from sage.categories.sets_cat import Sets -from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets -from sage.combinat.root_system.cartan_type import CartanType - - -@add_metaclass(InheritComparisonClasscallMetaclass) -class ShiftedPrimedTableau(ClonableArray): - r""" - A shifted primed tableau with primed elements stored as half-integers. - - A primed tableau is a tableau of shifted shape in the alphabet - `X' = \{1' < 1 < 2' < 2 < \cdots < n' < n\}` such that - - 1. The entries are weakly increasing along rows and columns; - 2. A row cannot have two repeated primed elements, and a column - cannot have two repeated non-primed elements; - 3. There are only non-primed elements on the main diagonal. - - EXAMPLES:: - - sage: T = ShiftedPrimedTableaux([4,2]) - sage: T([[1,"2'","3'",3],[2,"3'"]])[1] - (2.0, 2.5) - sage: t = ShiftedPrimedTableau([[1,"2p",2.5,3],[0,2,2.5]]) - sage: t[1] - (2.0, 2.5) - sage: ShiftedPrimedTableau([["2p",2,3],["2p","3p"],[2]], skew=[2,1]) - [(1.5, 2.0, 3.0), (1.5, 2.5), (2.0,)] skewed by [2, 1] - - TESTS:: - - sage: t = ShiftedPrimedTableau([[1,2,2.5,3],[0,2,2.5]]) - Traceback (most recent call last): - ... - ValueError: [[1, 2, 2.50000000000000, 3], [0, 2, 2.50000000000000]] - is not an element of Shifted Primed Tableaux - """ - @staticmethod - def __classcall_private__(cls, T, skew=None): - r""" - Ensure that a shifted tableau is only ever constructed as an - ``element_class`` call of an appropriate parent. - - EXAMPLES:: - - sage: data = [[1,"2'","2",3],[2,"3'"]] - sage: t = ShiftedPrimedTableau(data) - sage: T = ShiftedPrimedTableaux(shape=[4,2],weight=(1,3,2)) - sage: t == T(data) - True - sage: S = ShiftedPrimedTableaux(shape=[4,2]) - sage: t == S(data) - True - sage: t = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) - sage: t.parent() - Shifted Primed Tableaux skewed by [2, 1] - """ - if (isinstance(T, cls) and T._skew == skew): - return T - return ShiftedPrimedTableaux(skew=skew)(T) - - def __init__(self, parent, T, skew=None): - r""" - Initialize a shifted tableau. - - TESTS:: - - sage: s = ShiftedPrimedTableau([[1,"2'","3'",3],[2,"3'"]]) - sage: t = ShiftedPrimedTableaux([4,2])([[1,"2p","3p",3], - ....: [0, 2,"3p"]]) - sage: s==t - True - sage: t.parent() - Shifted Primed Tableaux of shape [4, 2] - sage: s.parent() - Shifted Primed Tableaux - sage: r = ShiftedPrimedTableaux([4, 2])(s); r.parent() - Shifted Primed Tableaux of shape [4, 2] - sage: s is t # identical shifted tableaux are distinct objects - False - - A shifted primed tableau is shallowly immutable, the rows are - represented as tuples. - - :: - - sage: t = ShiftedPrimedTableau([[1,"2p","3p",3],[2,"3p"]]) - sage: t[0][1] = 3 - Traceback (most recent call last): - ... - TypeError: 'tuple' object does not support item assignment - """ - self._skew = skew - - if isinstance(T, ShiftedPrimedTableau): - ClonableArray.__init__(self, parent, T) - return - - if not isinstance(T, list): - try: - t_ = list(T) - except TypeError: - t_ = [T] - else: - t_ = T - - if not all(isinstance(row, (list, tuple)) for row in t_): - t_ = [t_] - - t = [] - # Preprocessing list t for primes and other symbols - for row in t_: - row_new = [] - for element in row: - - if isinstance(element, str): - if element[-1] == "'" and element[:-1].isdigit() is True: - # Check if an element has "'" at the end - row_new.append(float(float(element[:-1]) - .5)) - continue - if element[-1] == "p" and element[:-1].isdigit() is True: - # Check if an element has "p" at the end - row_new.append(float(float(element[:-1]) - .5)) - continue - try: - if int(float(element)*2) == float(element)*2: - # Check if an element is a half-integer - row_new.append(float(element)) - continue - else: - raise ValueError("all numbers must be half-integers") - - except (TypeError, ValueError): - raise ValueError("primed elements have wrong format") - - t.append(row_new) - - # Accounting for zeros at the beginning and at the end of a row' - i = 0 - while i < len(t): - row = t[i] - try: - while row[0] == 0: - row.pop(0) - while row[-1] == 0: - row.pop(-1) - except IndexError: - t.remove(t[i]) - continue - t[i] = row - i += 1 - - t = [tuple(_) for _ in t] - ClonableArray.__init__(self, parent, t) - - def __eq__(self, other): - """ - Check whether ``self`` is equal to ``other``. - - INPUT: - - - ``other`` -- the element that ``self`` is compared to - - OUTPUT: Boolean - - EXAMPLES:: - - sage: t = ShiftedPrimedTableau([[1,"2p"]]) - sage: t == ShiftedPrimedTableaux([2])([1,1.5]) - True - """ - if isinstance(other, ShiftedPrimedTableau): - return (self._skew == other._skew and list(self) == list(other)) - try: - Tab = ShiftedPrimedTableau(other) - except ValueError: - return False - return (self._skew == Tab._skew and list(self) == list(Tab)) - - def _to_matrix(self): - """ - Return a 2-dimensional array representation of a shifted tableau. - - EXAMPLES:: - - sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p'],[3]]) - sage: mat = t._to_matrix() - sage: mat - [[1.0, 1.5, 2.0, 2.0], [0, 2.0, 2.5, 0], [0, 0, 3.0, 0]] - sage: t == ShiftedPrimedTableau(mat) - True - """ - array = [] - m = self.shape()[0] - sk_len = 0 if self._skew is None else len(self._skew) - for i in range(sk_len): - array.append([0]*(i+self._skew[i]) - + list(self[i]) - + [0]*(m-i-self._skew[i]-len(self[i]))) - for i in range(sk_len, len(self)): - array.append([0]*i + list(self[i]) + [0]*(m-i-len(self[i]))) - return array - - def check(self): - """ - Check that ``self`` is a valid primed tableau. - - EXAMPLES:: - - sage: T = ShiftedPrimedTableaux([4,2]) - sage: t = T([[1,'2p',2,2],[2,'3p']]) - sage: t.check() - sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) - sage: s.check() - sage: t = T([['1p','2p',2,2],[2,'3p']]) - Traceback (most recent call last): - .... - ValueError: [['1p', '2p', 2, 2], [2, '3p']] is not an element of - Shifted Primed Tableaux of shape [4, 2] - """ - if self._skew is not None: - if not all(self._skew[i] > self._skew[i+1] - for i in range(len(self._skew)-1)): - raise ValueError('skew shape must be a strict partition') - skew = self._skew + [0]*(len(self)-len(self._skew)) - else: - skew = [0]*len(self) - if not all(len(self[i]) + skew[i] > len(self[i+1]) + skew[i+1] - for i in range(len(self)-1)): - raise ValueError('shape must be a strict partition') - for i, row in enumerate(self): - if i > 0: - if not all(val > self[i-1][j+1-skew[i-1]+skew[i]] - for j, val in enumerate(row) - if int(val) == val - if j+1 >= skew[i-1]-skew[i]): - raise ValueError( - 'column is not strictly increasing in non-primes') - if not all(val >= self[i-1][j+1-skew[i-1]+skew[i]] - for j, val in enumerate(row) - if int(val) != val - if j+1 >= skew[i-1]-skew[i]): - raise ValueError( - 'column is not weakly increasing in primes') - if not all(row[j] <= row[j+1] - for j in range(len(row)-1) if int(row[j]) == row[j]): - raise ValueError('row is not weakly increasing in non-primes') - if not all(row[j] < row[j+1] - for j in range(len(row)-1) if int(row[j]) != row[j]): - raise ValueError('row is not strictly increasing in primes') - if not all(int(row[0]) == row[0] - for i, row in enumerate(self) - if skew[i] == 0): - raise ValueError('diagonal elements must be non-primes') - - def _repr_(self): - """ - Represent ``self`` as a list of rows with rows represented as tuples of - half-integers. - - EXAMPLES:: - - sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) - sage: t - [(1.0, 1.5, 2.0, 2.0), (2.0, 2.5)] - sage: ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) - [(1.5, 2.0, 3.0), (1.5,)] skewed by [2, 1] - """ - if self._skew is None: - return repr([tuple(_) for _ in self]) - return (repr([tuple(_) for _ in self]) + - " skewed by {}".format(self._skew)) - - def _repr_tab(self): - """ - Return a nested list of strings representing the elements. - - TESTS:: - - sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) - sage: t._repr_tab() - [[' 1 ', " 2'", ' 2 ', ' 2 '], [' 2 ', " 3'"]] - sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) - sage: s._repr_tab() - [[' . ', ' . ', " 2'", ' 2 ', ' 3 '], [' . ', " 2'"]] - """ - if self._skew is not None: - skew = self._skew + [0]*(len(self)-len(self._skew)) - else: - skew = [0]*len(self) - max_len = len(str(self.max_element()))+1 - string_tab = [] - for i, row in enumerate(self): - string_row = [' '*(max_len-1) + '. ']*(skew[i]) - for val in row: - if int(val) == val: - string_row.append(' '*(max_len-len(str(int(val)))) - + str(int(val)) + ' ') - else: - string_row.append(' '*(max_len-len(str(int(val+.5)))) - + str(int(val+.5)) + "'") - string_tab.append(string_row) - return string_tab - - def _repr_diagram(self): - """ - Return a string representation of ``self`` as an array. - - EXAMPLES:: - - sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) - sage: print(t._repr_diagram()) - 1 2' 2 2 - 2 3' - sage: t = ShiftedPrimedTableau([[10,'11p',11,11],[11,'12']]) - sage: print(t._repr_diagram()) - 10 11' 11 11 - 11 12 - sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) - sage: print(s._repr_diagram()) - . . 2' 2 3 - . 2' - """ - max_len = len(str(self.max_element()))+2 - return "\n".join([" "*max_len*i + "".join(_) - for i, _ in enumerate(self._repr_tab())]) - - def _ascii_art_(self): - """ - Return ASCII representation of ``self``. - - EXAMPLES:: - - sage: ascii_art(ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']])) - +---+---+---+---+ - | 1 | 2'| 2 | 2 | - +---+---+---+---+ - | 2 | 3'| - +---+---+ - sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) - sage: ascii_art(s) - +---+---+---+---+---+ - | . | . | 2'| 2 | 3 | - +---+---+---+---+---+ - | . | 2'| - +---+---+ - - TESTS:: - - sage: ascii_art(ShiftedPrimedTableau([])) - ++ - ++ - """ - from sage.typeset.ascii_art import AsciiArt - return AsciiArt(self._ascii_art_table(unicode=False).splitlines()) - - def _unicode_art_(self): - """ - Return a Unicode representation of ``self``. - - EXAMPLES:: - - sage: unicode_art(ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']])) - ┌───┬───┬───┬───┐ - │ 1 │ 2'│ 2 │ 2 │ - └───┼───┼───┼───┘ - │ 2 │ 3'│ - └───┴───┘ - sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) - sage: unicode_art(s) - ┌───┬───┬───┬───┬───┐ - │ . │ . │ 2'│ 2 │ 3 │ - └───┼───┼───┼───┴───┘ - │ . │ 2'│ - └───┴───┘ - - TESTS:: - - sage: unicode_art(ShiftedPrimedTableau([])) - ┌┐ - └┘ - """ - from sage.typeset.unicode_art import UnicodeArt - return UnicodeArt(self._ascii_art_table(unicode=True).splitlines()) - - def _ascii_art_table(self, unicode=False): - """ - TESTS:: - - sage: t = ShiftedPrimedTableau([[1,'2p',2],[2,'3p']]) - sage: print(t._ascii_art_table(unicode=True)) - ┌───┬───┬───┐ - │ 1 │ 2'│ 2 │ - └───┼───┼───┤ - │ 2 │ 3'│ - └───┴───┘ - sage: print(t._ascii_art_table()) - +---+---+---+ - | 1 | 2'| 2 | - +---+---+---+ - | 2 | 3'| - +---+---+ - sage: s = ShiftedPrimedTableau([[1,'2p',2, 23],[2,'30p']]) - sage: print(s._ascii_art_table(unicode=True)) - ┌────┬────┬────┬────┐ - │ 1 │ 2'│ 2 │ 23 │ - └────┼────┼────┼────┘ - │ 2 │ 30'│ - └────┴────┘ - sage: print(s._ascii_art_table(unicode=False)) - +----+----+----+----+ - | 1 | 2'| 2 | 23 | - +----+----+----+----+ - | 2 | 30'| - +----+----+ - sage: s = ShiftedPrimedTableau([["2p",2,10],["2p"]],skew=[2,1]) - sage: print(s._ascii_art_table(unicode=True)) - ┌────┬────┬────┬────┬────┐ - │ . │ . │ 2'│ 2 │ 10 │ - └────┼────┼────┼────┴────┘ - │ . │ 2'│ - └────┴────┘ - """ - if unicode: - import unicodedata - v = unicodedata.lookup('BOX DRAWINGS LIGHT VERTICAL') - h = unicodedata.lookup('BOX DRAWINGS LIGHT HORIZONTAL') - dl = unicodedata.lookup('BOX DRAWINGS LIGHT DOWN AND LEFT') - dr = unicodedata.lookup('BOX DRAWINGS LIGHT DOWN AND RIGHT') - ul = unicodedata.lookup('BOX DRAWINGS LIGHT UP AND LEFT') - ur = unicodedata.lookup('BOX DRAWINGS LIGHT UP AND RIGHT') - vl = unicodedata.lookup('BOX DRAWINGS LIGHT VERTICAL AND LEFT') - uh = unicodedata.lookup('BOX DRAWINGS LIGHT UP AND HORIZONTAL') - dh = unicodedata.lookup('BOX DRAWINGS LIGHT DOWN AND HORIZONTAL') - vh = unicodedata.lookup( - 'BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL') - else: - v = '|' - h = '-' - dl = dr = ul = ur = vl = uh = dh = vh = '+' - - if self.shape() == []: - return dr + dl + '\n' + ur + ul - - # Get the widths of the columns - str_tab = self._repr_tab() - width = len(str_tab[0][0]) - str_list = [dr + (h*width + dh)*(len(str_tab[0])-1) + h*width + dl] - for nrow, row in enumerate(str_tab): - l1 = " " * (width+1) * nrow - l2 = " " * (width+1) * nrow - n = len(str_tab[nrow+1]) if nrow+1 < len(str_tab) else -1 - for i, e in enumerate(row): - if i == 0: - l1 += ur + h*width - elif i <= n+1: - l1 += vh + h*width - else: - l1 += uh + h*width - if unicode: - l2 += u"{}{:^{width}}".format(v, e, width=width) - else: - l2 += "{}{:^{width}}".format(v, e, width=width) - if i <= n: - l1 += vl - else: - l1 += ul - l2 += v - str_list.append(l2) - str_list.append(l1) - return "\n".join(str_list) - - def pp(self): - """ - Print out a nice version of ``self``. - - EXAMPLES:: - - sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) - sage: t.pp() - 1 2' 2 2 - 2 3' - sage: t = ShiftedPrimedTableau([[10,'11p',11,11],[11,'12']]) - sage: t.pp() - 10 11' 11 11 - 11 12 - sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) - sage: s.pp() - . . 2' 2 3 - . 2' - """ - print(self._repr_diagram()) - - def _latex_(self): - r""" - Return LaTex code for ``self``. - - EXAMPLES:: - - sage: T = ShiftedPrimedTableaux([4,2]) - sage: latex(T([[1,"2p",2,"3p"],[2,3]])) - {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} - \raisebox{-.6ex}{$\begin{array}[b]{*{4}c}\cline{1-4} - \lr{ 1 }&\lr{ 2'}&\lr{ 2 }&\lr{ 3'}\\\cline{1-4} - &\lr{ 2 }&\lr{ 3 }\\\cline{2-3} - \end{array}$} - } - """ - from sage.combinat.output import tex_from_array - L = [[None]*i + row for i, row in enumerate(self._repr_tab())] - return tex_from_array(L) - - def max_element(self): - """ - Return the maximum element in the primed tableaux ``self``, rounded up. - - EXAMPLES:: - - sage: Tab = ShiftedPrimedTableau([(1,1,1.5,2.5),(2,2)]) - sage: Tab.max_element() - 3 - """ - if self == []: - return 0 - else: - flat = [item for sublist in self for item in sublist] - return int(round(max(flat))) - - def shape(self): - """ - Return the shape of the underlying partition of ``self`` in list - format. - - EXAMPLES:: - - sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) - sage: t.shape() - [4, 2] - sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) - sage: s.shape() - [5, 2] / [2, 1] - """ - if self._skew is None: - return Partition([len(_) for _ in self]) - return SkewPartition(([len(self[i])+self._skew[i] - for i in range(len(self._skew))] + - [len(self[i]) - for i in range(len(self._skew), len(self))], - self._skew)) - - def weight(self): - """ - Return the weight of ``self`` as a tuple. - - The weight of a shifted primed tableau is defined to be the vector - with i-th component equal to the number of letters i and i' in the - tableau. - - EXAMPLES:: - - sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) - sage: t.weight() - (1, 4, 1) - """ - flat = [round(item) for sublist in self for item in sublist] - if flat == []: - max_ind = 0 - else: - max_ind = int(max(flat)) - weight = tuple([flat.count(i+1) for i in range(max_ind)]) - if isinstance(self.parent(), ShiftedPrimedTableaux_shape): - return self.parent().weight_lattice_realization()(weight) - return weight - - def _reading_word_with_positions(self): - """ - Return the reading word of ``self`` together with positions of the - corresponding letters in ``self``. - - The reading word of a shifted primed tableau is constructed - as follows: - - 1. List all primed letters in the tableau, column by - column, in decreasing order within each column, moving - from the rightmost column to the left, and with all - the primes removed (i.e. all letters are increased by - half a unit). - - 2. Then list all unprimed elements, row by row, in - increasing order within each row, moving from the - bottommost row to the top. - - EXAMPLES:: - - sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) - sage: t._reading_word_with_positions() - [((1, 2), 3), ((0, 1), 2), ((1, 1), 2), ((0, 0), 1), - ((0, 2), 2), ((0, 3), 2)] - """ - if self._skew is not None: - raise NotImplementedError('skew tableau must be empty') - mat = self._to_matrix() - ndim, mdim = len(mat), len(mat[0]) - list_with_positions = [] - for j in reversed(range(mdim)): - for i in range(ndim): - x = mat[i][j] - if int(x) != x: - list_with_positions.append(((i, j), int(x+0.5))) - for i in reversed(range(ndim)): - for j in range(mdim): - x = mat[i][j] - if int(x) == x and int(x) != 0: - list_with_positions.append(((i, j), int(x))) - return list_with_positions - - def reading_word(self): - """ - Return the reading word of ``self``. - - The reading word of a shifted primed tableau is constructed - as follows: - - 1. List all primed letters in the tableau, column by - column, in decreasing order within each column, moving - from the rightmost column to the left, and with all - the primes removed (i.e. all letters are increased by - half a unit). - - 2. Then list all unprimed elements, row by row, in - increasing order within each row, moving from the - bottommost row to the top. - - EXAMPLES:: - - sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) - sage: t.reading_word() - [3, 2, 2, 1, 2, 2] - """ - if self._skew is not None: - raise NotImplementedError('skew tableau must be empty') - return [tup[1] for tup in self._reading_word_with_positions()] - - def f(self, ind): - """ - Compute the action of the crystal operator `f_i` on a Shifted Primed - Tableau using cases from the paper [HPS2017]_. - - INPUT: - - - ``ind`` -- element in the index set of the crystal - - OUTPUT: - - Primed tableau or ``None``. - - EXAMPLES:: - - sage: t = ShiftedPrimedTableau([[1,1,1,1,2.5],[2,2,2,2.5],[3,3]]) - sage: t.pp() - 1 1 1 1 3' - 2 2 2 3' - 3 3 - sage: s = t.f(2) - sage: print(s) - None - sage: t = ShiftedPrimedTableau([[1,1,1,1.5,2.5],[0,2,2,3,3], - ....: [0,0,3,4]]) - sage: t.pp() - 1 1 1 2' 3' - 2 2 3 3 - 3 4 - sage: s = t.f(2) - sage: s.pp() - 1 1 1 2' 3' - 2 3' 3 3 - 3 4 - - """ - - if self is None: - return None - - if self._skew is not None: - raise NotImplementedError('skew tableau must be empty') - - try: - self.parent()._weight - raise TypeError('operator generates an element outside of {}' - .format(self.parent())) - except AttributeError: - pass - - T = self._to_matrix() - - read_word = self._reading_word_with_positions() - read_word = [num - for num in read_word - if num[1] == ind or num[1] == ind+1] - - element_to_change = None - count = 0 - - for element in read_word: - if element[1] == ind+1: - count += 1 - elif count == 0: - element_to_change = element - else: - count -= 1 - - if element_to_change is None: - return None - - (r, c), elt = element_to_change - - if T[r][c] == ind - .5: - T = [[elt+.5 if elt != 0 else elt for elt in row] for row in T] - T = map(list, zip(*T)) - r, c = c, r - h, l = len(T), len(T[0]) - - if (c+1 == l or T[r][c+1] >= ind+1 or T[r][c+1] < 1): - (tp_r, tp_c) = (r, c) - while True: - if (tp_r+1 == h or - T[tp_r+1][tp_c] > ind+1 or - T[tp_r+1][tp_c] < 1): - break - if tp_r <= tp_c and T[tp_r+1][tp_r+1] == ind+1: - tp_r += 1 - tp_c = tp_r - break - if (ind+.5 not in T[tp_r+1]): - break - tp_r += 1 - tp_c = T[tp_r].index(ind+.5) - - if tp_r == r: - T[r][c] += 1 - - elif tp_r == tp_c: - T[r][c] += .5 - - else: - T[r][c] += .5 - T[tp_r][tp_c] += .5 - - elif T[r][c+1] == ind+.5: - T[r][c+1] += .5 - T[r][c] += .5 - - if r > c: - T = [[elt-.5 if elt != 0 else elt for elt in row] for row in T] - T = map(list, zip(*T)) - - return self.parent()(T) - - def e(self, ind): - """ - Compute the action of the crystal operator `e_i` on a Shifted Primed - Tableau using cases from the paper [HPS2017]_. - - INPUT: - - - ``ind`` -- an element in the index set of the crystal. - - OUTPUT: - - Primed tableau or 'None'. - - EXAMPLES:: - - sage: t = ShiftedPrimedTableau([[1,1,1,1.5,2.5], - ....: [0,2,"3p",3,3],[0,0,3,4]]) - sage: t.pp() - 1 1 1 2' 3' - 2 3' 3 3 - 3 4 - sage: s = t.e(2) - sage: s.pp() - 1 1 1 2' 3' - 2 2 3 3 - 3 4 - sage: t == s.f(2) - True - - """ - if self is None: - return None - - if self._skew is not None: - raise NotImplementedError('skew tableau must be empty') - - try: - self.parent()._weight - raise TypeError('operator generates an element outside of {}' - .format(self.parent())) - except AttributeError: - pass - - T = self._to_matrix() - - read_word = self._reading_word_with_positions() - read_word = [num - for num in read_word - if num[1] == ind or num[1] == ind+1] - - element_to_change = None - count = 0 - - for element in read_word[::-1]: - if element[1] == ind: - count += 1 - elif count == 0: - element_to_change = element - else: - count -= 1 - - if element_to_change is None: - return None - (r, c), elt = element_to_change - - if T[r][c] == ind + .5: - T = [[elt+.5 if elt != 0 else elt for elt in row] for row in T] - T = map(list, zip(*T)) - r, c = c, r - - if (c == 0 or T[r][c-1] <= ind or T[r][c-1] < 1): - - (tp_r, tp_c) = (r, c) - while True: - - if tp_r == 0 or T[tp_r-1][tp_c] < ind or T[tp_r-1][tp_c] < 1: - break - if (ind+.5 not in T[tp_r-1]): - break - tp_r -= 1 - tp_c = T[tp_r].index(ind+.5) - - if tp_r == r: - T[r][c] -= 1 - - elif tp_r == tp_c: - T[r][c] -= .5 - - else: - T[r][c] -= .5 - T[tp_r][tp_c] -= .5 - - elif T[r][c-1] == ind+.5: - T[r][c-1] -= .5 - T[r][c] -= .5 - - if r > c: - T = [[elt-.5 if elt != 0 else elt for elt in row] for row in T] - T = map(list, zip(*T)) - - return self.parent()(T) - - def is_highest_weight(self): - """ - Return whether ``self`` is a highest weight element of the crystal. - - An element is highest weight if it vanishes under all crystal operators - `e_i`. - - EXAMPLES:: - - sage: t = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.0, 1.0), - ....: (2.0, 2.0, 2.0, 2.5), (3.0, 3.0)]) - sage: print(t.e(1)) - None - sage: print(t.e(2)) - None - sage: t.is_highest_weight() - True - - """ - if self._skew is not None: - raise NotImplementedError('skew tableau must be empty') - read_w = self.reading_word() - count = {} - for l in read_w[::-1]: - try: - count[l] += 1 - except KeyError: - count[l] = 1 - if l > 1: - try: - if count[l] > count[l-1]: - return False - except KeyError: - return False - return True - - -class ShiftedPrimedTableaux(UniqueRepresentation, Parent): - """ - A factory for the various classes of shifted standard tableaux. - - INPUT: - - - a weight and/or a partition - - OUTPUT: - - - with no argument, the class of all primed tableaux - - - with a list or partition argument, the class of all primed - tableaux of that shape (infinite set if we don't specify the - weight or maximum element) - - - with an additional integer argument ``max_element``, the class - of all primed tableaux of a given shape and a given maximum - element - - - with a tuple argument, the class of all primed tableaux of that - weight (finite set) - - A primed tableau is a tableau of shifted shape on the alphabet - `X' = \{1' < 1 < 2' < 2 < \cdots < n' < n\}` such that - - 1. the entries are weakly increasing along rows and columns - - 2. a row cannot have two repeated primed elements, and a column - cannot have two repeated non-primed elements - - 3. there are only non-primed elements along the main diagonal - - The weight of a tableau is defined to be the vector with `i`-th - component equal to the number of letters `i` and `i'` in the tableau. - The sum of the entries in the weight vector must be equal to the - number of boxes in the partition. - - None of the Shifted Primed Tableaux classes can be iterated over. - - EXAMPLES:: - - sage: SPT = ShiftedPrimedTableaux(weight=(1,2,2), shape=[3,2]); SPT - Shifted Primed Tableaux of weight (1, 2, 2) and shape [3, 2] - sage: SPT.list() - [[(1.0, 2.0, 2.0), (3.0, 3.0)], - [(1.0, 1.5, 2.5), (2.0, 3.0)], - [(1.0, 1.5, 2.5), (2.0, 2.5)], - [(1.0, 1.5, 2.0), (3.0, 3.0)]] - sage: SPT = ShiftedPrimedTableaux((1,2)); SPT - Shifted Primed Tableaux of weight (1, 2) - sage: list(SPT) - [[(1.0, 2.0, 2.0)], [(1.0, 1.5, 2.0)], [(1.0, 1.5), (2.0,)]] - sage: SPT = ShiftedPrimedTableaux([3,2], max_element = 2); SPT - Shifted Primed Tableaux of shape [3, 2] and maximum element 2 - sage: list(SPT) - [[(1.0, 1.0, 1.0), (2.0, 2.0)], [(1.0, 1.0, 1.5), (2.0, 2.0)]] - - .. SEEALSO:: - - - :class:`ShiftedPrimedTableau` - """ - - Element = ShiftedPrimedTableau - - @staticmethod - def __classcall_private__(cls, *args, **kwargs): - r""" - This is a factory class which returns the appropriate parent based on - arguments. See the documentation for :class:`ShiftedPrimedTableaux` - for more information. - - TESTS:: - - sage: ShiftedPrimedTableaux([]) - Shifted Primed Tableaux of shape [] - sage: ShiftedPrimedTableaux(3) - Traceback (most recent call last): - ... - ValueError: invalid argument for weight or shape - sage: ShiftedPrimedTableaux(weight=(2,2,2), shape=[3,2]) - Traceback (most recent call last): - ... - ValueError: weight and shape are incompatible - sage: ShiftedPrimedTableaux([[1]]) - Traceback (most recent call last): - ... - ValueError: invalid shape argument - sage: ShiftedPrimedTableaux(weight=(2,2,2), max_element=2) - Traceback (most recent call last): - ... - ValueError: maximum element is incompatible with the weight - sage: ShiftedPrimedTableaux(shape=[4,1],skew=[3,2]) - Traceback (most recent call last): - ... - ValueError: skew shape must be inside the given tableau shape - """ - weight = None - shape = None - max_element = None - skew = None - - if ('skew' in kwargs and kwargs['skew'] is not None): - try: - skew = Partition(kwargs['skew']) - except ValueError: - raise ValueError('invalid skew argument') - if not all(skew[i] > skew[i+1] for i in range(len(skew)-1)): - raise ValueError('skew shape must be a strict partition') - - if 'max_elt' in kwargs: - max_element = int(kwargs['max_elt']) - - if 'max_element' in kwargs: - max_element = int(kwargs['max_element']) - - if 'max' in kwargs: - max_element = int(kwargs['max']) - - if 'shape' in kwargs: - shape = kwargs['shape'] - - if 'weight' in kwargs: - weight = tuple(kwargs['weight']) - - if args: - if isinstance(args[0], tuple) and weight is None: - weight = args[0] - if len(args) > 1: - if ((isinstance(args[1], - (list, Partition, SkewPartition)) or - args[1] is None) and shape is None): - shape = args[1] - else: - raise ValueError( - 'invalid argument for weight or shape') - - elif (isinstance(args[0], (list, Partition, SkewPartition)) - and shape is None): - shape = args[0] - if len(args) > 1: - if ((isinstance(args[1], tuple) or args[1] is None) and - weight is None): - weight = args[1] - else: - raise ValueError( - 'invalid argument for weight or shape') - else: - raise ValueError('invalid argument for weight or shape') - - if shape is not None: - if isinstance(shape, SkewPartition): - skew = shape.inner() - shape = shape.outer() - try: - shape = Partition(shape) - except ValueError: - raise ValueError('invalid shape argument') - if not all(shape[i] > shape[i+1] for i in range(len(shape)-1)): - raise ValueError( - "shape {} is not a strict partition".format(shape)) - if (skew is not None - and not all(skew[i] <= shape[i] - for i in range(len(skew)))): - raise ValueError( - 'skew shape must be inside the given tableau shape') - - if weight is not None: - while weight[-1] == 0: - weight = weight[:-1] - - if max_element is not None and weight is not None: - if len(weight) > max_element: - raise ValueError( - "maximum element is incompatible with the weight") - - if shape is None and weight is None: - if max_element is not None: - raise ValueError("specify shape or weight argument") - return ShiftedPrimedTableaux_all(skew=skew) - - elif weight is None: - return ShiftedPrimedTableaux_shape(shape, max_element, skew=skew) - - elif shape is None: - return ShiftedPrimedTableaux_weight(weight, skew=skew) - - if (skew is not None and sum(shape) - sum(skew) != sum(weight) - or skew is None and sum(shape) != sum(weight)): - raise ValueError( - "weight and shape are incompatible") - - return ShiftedPrimedTableaux_weight_shape(weight, shape, skew=skew) - - def __contains__(self, T): - """ - EXAMPLES:: - - sage: [(1,'2p',2,2),(2,'3p')] in ShiftedPrimedTableaux() - True - sage: [(1,1),(2,2)] in ShiftedPrimedTableaux() - False - sage: [(1,1),('2p',)] in ShiftedPrimedTableaux() - False - sage: [(1,1,'3p'),(2,'3p')] in ShiftedPrimedTableaux() - True - sage: [(1,1,2),(2,2)] in ShiftedPrimedTableaux() - False - sage: [1,1,1] in ShiftedPrimedTableaux() - True - - TESTS:: - - sage: 1 in ShiftedPrimedTableaux() - True - sage: [] in ShiftedPrimedTableaux() - True - """ - try: - self.element_class(self, T) - return True - except ValueError: - return False - - _is_a = __contains__ - - -class ShiftedPrimedTableaux_all(ShiftedPrimedTableaux): - """ - The class of all Shifted Primed Tableaux. - """ - Element = ShiftedPrimedTableau - - def __init__(self, skew=None): - """ - Initialize the class of all shifted tableaux. - - TESTS:: - - sage: [[1,1.5],[2]] in ShiftedPrimedTableaux() - True - sage: [[1,1.5],[1.5]] in ShiftedPrimedTableaux() - False - sage: [[1,1],[1]] in ShiftedPrimedTableaux() - False - sage: [[1,1],[2,2]] in ShiftedPrimedTableaux() - False - """ - Parent.__init__(self, category=FiniteEnumeratedSets()) - self._skew = skew - - def _repr_(self): - """ - Return a string representation of ``self``. - - TESTS:: - - sage: ShiftedPrimedTableaux() - Shifted Primed Tableaux - """ - if self._skew is None: - return "Shifted Primed Tableaux" - return "Shifted Primed Tableaux skewed by {}".format(self._skew) - - def _element_constructor_(self, T): - """ - Construct an object from ``T`` as an element of ``self``, if - possible. - - INPUT: - - - ``T`` -- data which can be interpreted as a tableau - - OUTPUT: - - - the corresponding tableau object - - TESTS:: - - sage: Tab=ShiftedPrimedTableaux()([1,1,"2p"]); Tab - [(1.0, 1.0, 1.5)] - sage: Tab.parent() - Shifted Primed Tableaux - sage: Tab=ShiftedPrimedTableaux()([[1,1,2],[2,2]]) - Traceback (most recent call last): - ... - ValueError: [[1, 1, 2], [2, 2]] is not an element of - Shifted Primed Tableaux - - """ - try: - return self.element_class(self, T, skew=self._skew) - except ValueError: - raise ValueError( - "{} is not an element of {}".format(T, self)) - - def __contains__(self, T): - """ - TESTS:: - - sage: [1,1,2] in ShiftedPrimedTableaux() - True - sage: (2,1) in ShiftedPrimedTableaux() - False - """ - try: - self.element_class(self, T, skew=self._skew) - return True - except ValueError: - return False - - -class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): - """ - Shifted Primed Tableaux of a fixed shape. - - Shifted primed tableaux admit a type `A_n` classical crystal structure - with highest weights corresponding to a given shape. - - The list of module generators consists of all elements of the - crystal with nonincreasing weight. - - The crystal is constructed following operations described in [HPS2017]_. - - EXAMPLES:: - - sage: ShiftedPrimedTableaux([4,3,1], max_elt=4) - Shifted Primed Tableaux of shape [4, 3, 1] and maximum element 4 - sage: ShiftedPrimedTableaux([4,3,1], max_elt=4).cardinality() - 384 - - We compute some of the crystal structure:: - - sage: SPTC = crystals.ShiftedPrimedTableaux([3,2], 3) - sage: T = SPTC.module_generators[-1] - sage: T - [(1.0, 1.0, 1.5), (2.0, 2.5)] - sage: T.f(2) - [(1.0, 1.0, 2.5), (2.0, 2.5)] - sage: len(SPTC.module_generators) - 7 - sage: SPTC[0] - [(1.0, 1.0, 1.0), (2.0, 2.0)] - sage: SPTC.cardinality() - 24 - """ - @staticmethod - def __classcall_private__(cls, shape, max_elt=None, skew=None): - """ - Normalize the attributes for the class. - - TEST:: - - sage: SPT = ShiftedPrimedTableaux(shape=[2,1]) - sage: SPT._shape.parent() - Partitions - """ - shape = _Partitions(shape) - return (super(ShiftedPrimedTableaux_shape, cls) - .__classcall__(cls, shape=shape, max_elt=max_elt, - skew=skew)) - - Element = ShiftedPrimedTableau - - def __init__(self, shape, max_elt, skew): - """ - Initialize the class of Shifted Primed Tableaux of a given shape. - - If ``max_elt`` is specified, a finite set with entries smaller - or equal to ``max_elt``. - - TESTS:: - - sage: TestSuite(ShiftedPrimedTableaux([4,2,1], max_elt=4)).run() - """ - # Determine the correct category - if max_elt is None: - if skew is None: - category = RegularCrystals().Infinite() - self._cartan_type = CartanType(['A+oo']) - else: - category = Sets().Infinite() - else: - if skew is None: - category = ClassicalCrystals() - self._cartan_type = CartanType(['A', max_elt-1]) - else: - category = Sets().Finite() - - ShiftedPrimedTableaux.__init__(self, category=category) - self._max_elt = max_elt - self._skew = skew - if skew is None: - self._shape = Partition(shape) - else: - self._shape = SkewPartition((shape, skew)) - - def _repr_(self): - """ - Return a string representation of ``self``. - - TESTS:: - - sage: ShiftedPrimedTableaux([3,2,1]) - Shifted Primed Tableaux of shape [3, 2, 1] - """ - base = "Shifted Primed Tableaux of shape " + self._shape._repr_() - if self._max_elt is not None: - base += " and maximum element {}".format(self._max_elt) - return base - - def __contains__(self, T): - """ - TESTS:: - - sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux([4,2]) - True - """ - try: - Tab = self.element_class(self, T, skew=self._skew) - except ValueError: - return False - - if self._max_elt is None: - return (Tab.shape() == self._shape) - - return (Tab.shape() == self._shape and - Tab.max_element() <= self._max_elt) - - def _element_constructor_(self, T): - """ - Construct an object from ``T`` as an element of ``self``, if - possible. - - INPUT: - - - ``T`` -- data which can be interpreted as a primed tableau - - OUTPUT: - - - the corresponding primed tableau object - - TESTS:: - - sage: tab= ShiftedPrimedTableaux([3])([1,1,1.5]); tab - [(1.0, 1.0, 1.5)] - sage: tab.parent() - Shifted Primed Tableaux of shape [3] - sage: ShiftedPrimedTableaux([3])([1,1]) - Traceback (most recent call last): - ... - ValueError: [1, 1] is not an element of Shifted Primed Tableaux - of shape [3] - """ - try: - Tab = self.element_class(self, T, skew=self._skew) - except ValueError: - raise ValueError( - "{} is not an element of {}".format(T, self)) - - if self._max_elt is None: - if Tab.shape() == self._shape: - return Tab - else: - if (Tab.shape() == self._shape and - Tab.max_element() <= self._max_elt): - return Tab - raise ValueError("{} is not an element of {}".format(T, self)) - - @lazy_attribute - def module_generators(self): - """ - Return the generators of ``self`` as a crystal. - - TEST:: - - sage: SPT = ShiftedPrimedTableaux(shape=[2,1]) - sage: SPT.module_generators - ([(1.0, 1.0), (2.0,)], - [(1.0, 2.0), (3.0,)], - [(1.0, 1.5), (3.0,)]) - """ - if self._skew is not None: - raise NotImplementedError("only for non-skew shapes") - list_dw = [] - if self._max_elt is None: - max_element = sum(self._shape) - else: - max_element = self._max_elt - for weight in (Partition(self._shape) - .dominated_partitions(rows=max_element)): - list_dw.extend([self(T) - for T in ShiftedPrimedTableaux( - weight=tuple(weight), - shape=self._shape)]) - return tuple(list_dw) - - def shape(self): - """ - Return the shape of the shifted tableaux ``self``. - - TESTS:: - - sage: ShiftedPrimedTableaux([6,4,3,1]).shape() - [6, 4, 3, 1] - """ - return self._shape - - def __iter__(self): - """ - Iterate over ``self``, if ``max_element`` is specified. - - EXAMPLES:: - - sage: Tabs = ShiftedPrimedTableaux([3,2], max_element=3) - sage: Tabs[:4] - [[(1.0, 1.0, 1.0), (2.0, 2.0)], - [(1.0, 1.0, 1.0), (2.0, 3.0)], - [(1.0, 1.0, 1.0), (2.0, 2.5)], - [(1.0, 1.0, 1.0), (3.0, 3.0)]] - sage: len(list(Tabs)) - 24 - - TESTS:: - - sage: Tabs = ShiftedPrimedTableaux([3,2]) - sage: Tabs[:3] - Traceback (most recent call last): - ... - ValueError: set is infinite - """ - if self._skew is not None: - raise NotImplementedError('skew tableau must be empty') - if self._max_elt is None: - raise ValueError("set is infinite") - for weight in OrderedPartitions(sum(self._shape)+self._max_elt, - k=self._max_elt): - weight_n = tuple([w-1 for w in weight]) - for tab in ShiftedPrimedTableaux(shape=self._shape, - weight=weight_n): - yield self(tab) - - -class ShiftedPrimedTableaux_weight(ShiftedPrimedTableaux): - """ - Shifted Primed Tableaux of fixed weight. - - TESTS:: - - sage: ShiftedPrimedTableaux((2,3,1)) - Shifted Primed Tableaux of weight (2, 3, 1) - sage: ShiftedPrimedTableaux((2,3,1)).cardinality() - 17 - """ - Element = ShiftedPrimedTableau - - def __init__(self, weight, skew=None): - """ - Initialize the class of Shifted Primed Tableaux of a given weight. - - TESTS:: - - sage: TestSuite( ShiftedPrimedTableaux((3,2,1)) ).run() - """ - Parent.__init__(self, category=FiniteEnumeratedSets()) - self._weight = weight - self._skew = skew - - def _repr_(self): - """ - Return a string representation of ``self``. - - TESTS:: - - sage: ShiftedPrimedTableaux((3,2,1)) - Shifted Primed Tableaux of weight (3, 2, 1) - """ - if self._skew is None: - return "Shifted Primed Tableaux of weight {}".format(self._weight) - return ("Shifted Primed Tableaux of weight {} skewed by {}" - .format(self._weight, self._skew)) - - def __contains__(self, T): - """ - TESTS:: - - sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux((1,4,1)) - True - """ - try: - Tab = self.element_class(self, T, skew=self._skew) - except ValueError: - return False - - return Tab.weight() == self._weight - - def _element_constructor_(self, T): - """ - Construct an object from ``T`` as an element of ``self``, if - possible. - - INPUT: - - - ``T`` -- data which can be interpreted as a primed tableau - - OUTPUT: - - - the corresponding primed tableau object - - TESTS:: - - sage: tab= ShiftedPrimedTableaux((2,1))([1,1,1.5]); tab - [(1.0, 1.0, 1.5)] - sage: tab.parent() - Shifted Primed Tableaux of weight (2, 1) - """ - try: - Tab = self.element_class(self, T, skew=self._skew) - except ValueError: - raise ValueError( - "{} is not an element of {}".format(T, self)) - if Tab.weight() == self._weight: - return Tab - - raise ValueError("{} is not an element of {}".format(T, self)) - - def __iter__(self): - """ - Iterate over ``self``. - - EXAMPLES:: - - sage: Tabs = ShiftedPrimedTableaux((2,3)) - sage: Tabs[:4] - [[(1.0, 1.0, 2.0, 2.0, 2.0)], - [(1.0, 1.0, 1.5, 2.0, 2.0)], - [(1.0, 1.0, 2.0, 2.0), (2.0,)], - [(1.0, 1.0, 1.5, 2.0), (2.0,)]] - sage: len(list(Tabs)) - 5 - """ - return (tab for shape_ in Partitions(sum(self._weight)) - if all(shape_[i] > shape_[i+1] for i in range(len(shape_)-1)) - for tab in ShiftedPrimedTableaux(shape=shape_, - weight=self._weight, - skew=self._skew)) - - -class ShiftedPrimedTableaux_weight_shape(ShiftedPrimedTableaux): - """ - Shifted Primed Tableaux of the fixed weight and shape. - - TESTS:: - - sage: ShiftedPrimedTableaux((2,3,2),[4,2,1]) - Shifted Primed Tableaux of weight (2, 3, 2) and shape [4, 2, 1] - sage: ShiftedPrimedTableaux((2,3,2), [4,2,1]).cardinality() - 4 - """ - Element = ShiftedPrimedTableau - - def __init__(self, weight, shape, skew=None): - """ - Initialize the class of Shifted Primed Tableaux of the given weight - and shape. - - TESTS:: - - sage: TestSuite( ShiftedPrimedTableaux((3,2,1)) ).run() - """ - Parent.__init__(self, category=FiniteEnumeratedSets()) - self._weight = weight - self._skew = skew - if skew is None: - self._shape = Partition(shape) - else: - self._shape = SkewPartition((shape, skew)) - - def _repr_(self): - """ - Return a string representation of ``self``. - - TESTS:: - - sage: ShiftedPrimedTableaux([3,2,1],(4,2)) - Shifted Primed Tableaux of weight (4, 2) and shape [3, 2, 1] - """ - return ( - "Shifted Primed Tableaux of weight {} and shape {}" - .format(self._weight, self._shape)) - - def __contains__(self, T): - """ - TESTS:: - - sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux( - ....: [4,2],(1,4,1)) - True - """ - try: - Tab = self.element_class(self, T, skew=self._skew) - except ValueError: - return False - - return Tab.weight() == self._weight and Tab.shape() == self._shape - - def _element_constructor_(self, T): - """ - Construct an object from ``T`` as an element of ``self``, if - possible. - - TESTS:: - - sage: tab= ShiftedPrimedTableaux([3],(2,1))([1,1,1.5]); tab - [(1.0, 1.0, 1.5)] - sage: tab.parent() - Shifted Primed Tableaux of weight (2, 1) and shape [3] - sage: ShiftedPrimedTableaux([3],(2,1))([1,1]) - Traceback (most recent call last): - ... - ValueError: [1, 1] is not an element of Shifted Primed Tableaux - of weight (2, 1) and shape [3] - """ - try: - Tab = self.element_class(self, T, skew=self._skew) - except ValueError: - raise ValueError( - "{} is not an element of {}".format(T, self)) - - if Tab.shape() == self._shape and Tab.weight() == self._weight: - return Tab - - raise ValueError("{} is not an element of {}".format(T, self)) - - def __iter__(self): - """ - Iterate over ``self``. - - EXAMPLES:: - - sage: Tabs = ShiftedPrimedTableaux([3,2], (1,2,2)) - sage: Tabs[:4] - [[(1.0, 2.0, 2.0), (3.0, 3.0)], - [(1.0, 1.5, 2.5), (2.0, 3.0)], - [(1.0, 1.5, 2.5), (2.0, 2.5)], - [(1.0, 1.5, 2.0), (3.0, 3.0)]] - sage: len(list(Tabs)) - 4 - """ - if self._skew is not None: - raise NotImplementedError('skew tableau must be empty') - - if not self._shape.dominates( - Partition(sorted(list(self._weight), key=int, reverse=True))): - return - yield - full_shape = self._shape - sub_tab = [] - tab_list_new = [[]] - for i, w in enumerate(self._weight): - tab_list_old = tab_list_new - tab_list_new = [] - for sub_tab in tab_list_old: - sub_shape = [len(sub_tab[r]) for r in range(len(sub_tab))] - for strip in add_strip(sub_shape, full_shape, w): - l = int(len(strip)/2) - if len(sub_shape) < len(full_shape): - new_tab = [sub_tab[r] - + [float(i+.5)]*int(strip[r]) - + [float(i+1)]*int(strip[-r-1]) - for r in range(l-1)] - if strip[l] != 0: - new_tab.append([float(i+1)]*int(strip[l])) - else: - new_tab = [sub_tab[r] - + [float(i+.5)]*int(strip[r]) - + [float(i+1)]*int(strip[-r-1]) - for r in range(l)] - tab_list_new.append(new_tab) - for tab in tab_list_new: - yield(ShiftedPrimedTableau(tab)) - - -#################### -# Helper functions # -#################### - - -def add_strip(sub_tab, full_tab, length): - """ - Helper function used in the algorithm to generate all Shifted Primed - Tableaux of the fixed weight and shape. - - TESTS:: - - sage: list(ShiftedPrimedTableaux([3,1],(2,2))) - [[(1.0, 1.0, 2.0), (2.0,)], [(1.0, 1.0, 1.5), (2.0,)]] - """ - if sum(sub_tab)+length > sum(full_tab): - raise ValueError("strip does not fit") - - if len(sub_tab) == 0: - cliff_list = [] - else: - cliff_list = [int(sub_tab[0] != full_tab[0])] - - for row in range(1, len(sub_tab)): - if sub_tab[row] == full_tab[row]: - cliff_list.append(0) - elif sub_tab[row-1]-1 == sub_tab[row]: - cliff_list[-1] += 1 - else: - cliff_list.append(1) - - if len(sub_tab) < len(full_tab): - cliff_list.append(0) - - for primes_num in range(min(sum(cliff_list), length)+1): - for primed_list in IntegerVectors(n=primes_num, k=len(cliff_list), - outer=cliff_list): - row = 0 - primed_strip = list() - for i, cliff in enumerate(cliff_list): - if cliff == 0: - row += 1 - primed_strip.append(0) - pass - primed_strip.extend([int(primed_list[i] > j) - for j in range(cliff)]) - row += cliff - plat_list = list() - - if len(sub_tab) < len(full_tab) and len(sub_tab) != 0: - plat_list.append(min(sub_tab[-1] + primed_strip[-2] - 1, - full_tab[len(sub_tab)])) - for row in range(1, len(sub_tab))[::-1]: - plat_list.append( - min(sub_tab[row-1]+primed_strip[row-1]-1, full_tab[row]) - - sub_tab[row] - primed_strip[row]) - if len(sub_tab) > 0: - plat_list.append(full_tab[0] - sub_tab[0] - primed_strip[0]) - else: - plat_list.append(full_tab[0]) - - if sum(plat_list) < length - primes_num: - pass - for non_primed_strip in IntegerVectors(n=length-primes_num, - k=len(plat_list), - outer=plat_list): - yield (list(primed_strip) + list(non_primed_strip)) - From 9e2a43d054b4d9fab9f5ee33e51433223f7c0d99 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sat, 6 Jan 2018 10:01:15 -0800 Subject: [PATCH 459/740] Minor fixes --- src/sage/combinat/shifted_primed_tableau.py | 1776 +++++++++++++++++++ 1 file changed, 1776 insertions(+) create mode 100644 src/sage/combinat/shifted_primed_tableau.py diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py new file mode 100644 index 00000000000..39270f2d546 --- /dev/null +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -0,0 +1,1776 @@ +# -*- coding: utf-8 -*- +""" +Shifted primed tableaux + +AUTHORS: + +- Kirill Paramonov (2017-08-18): initial implementation +""" + +#***************************************************************************** +# Copyright (C) 2017 Kirill Paramonov , +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from __future__ import print_function, absolute_import +from six import add_metaclass + +from sage.combinat.partition import Partition, Partitions, _Partitions, OrderedPartitions +from sage.combinat.skew_partition import SkewPartition +from sage.combinat.integer_vector import IntegerVectors +from sage.rings.integer import Integer + +from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass +from sage.misc.lazy_attribute import lazy_attribute + +from sage.structure.list_clone import ClonableArray +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation + +from sage.categories.regular_crystals import RegularCrystals +from sage.categories.classical_crystals import ClassicalCrystals +from sage.categories.sets_cat import Sets +from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from sage.combinat.root_system.cartan_type import CartanType + + +@add_metaclass(InheritComparisonClasscallMetaclass) +class ShiftedPrimedTableau(ClonableArray): + r""" + A shifted primed tableau with primed elements stored as half-integers. + + A primed tableau is a tableau of shifted shape in the alphabet + `X' = \{1' < 1 < 2' < 2 < \cdots < n' < n\}` such that + + 1. The entries are weakly increasing along rows and columns; + 2. A row cannot have two repeated primed elements, and a column + cannot have two repeated non-primed elements; + 3. There are only non-primed elements on the main diagonal. + + EXAMPLES:: + + sage: T = ShiftedPrimedTableaux([4,2]) + sage: T([[1,"2'","3'",3],[2,"3'"]])[1] + (2.0, 2.5) + sage: t = ShiftedPrimedTableau([[1,"2p",2.5,3],[0,2,2.5]]) + sage: t[1] + (2.0, 2.5) + sage: ShiftedPrimedTableau([["2p",2,3],["2p","3p"],[2]], skew=[2,1]) + [(1.5, 2.0, 3.0), (1.5, 2.5), (2.0,)] skewed by [2, 1] + + TESTS:: + + sage: t = ShiftedPrimedTableau([[1,2,2.5,3],[0,2,2.5]]) + Traceback (most recent call last): + ... + ValueError: [[1, 2, 2.50000000000000, 3], [0, 2, 2.50000000000000]] + is not an element of Shifted Primed Tableaux + """ + @staticmethod + def __classcall_private__(cls, T, skew=None): + r""" + Ensure that a shifted tableau is only ever constructed as an + ``element_class`` call of an appropriate parent. + + EXAMPLES:: + + sage: data = [[1,"2'","2",3],[2,"3'"]] + sage: t = ShiftedPrimedTableau(data) + sage: T = ShiftedPrimedTableaux(shape=[4,2],weight=(1,3,2)) + sage: t == T(data) + True + sage: S = ShiftedPrimedTableaux(shape=[4,2]) + sage: t == S(data) + True + sage: t = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + sage: t.parent() + Shifted Primed Tableaux skewed by [2, 1] + """ + if (isinstance(T, cls) and T._skew == skew): + return T + return ShiftedPrimedTableaux(skew=skew)(T) + + def __init__(self, parent, T, skew=None): + r""" + Initialize a shifted tableau. + + TESTS:: + + sage: s = ShiftedPrimedTableau([[1,"2'","3'",3],[2,"3'"]]) + sage: t = ShiftedPrimedTableaux([4,2])([[1,"2p","3p",3], + ....: [0, 2,"3p"]]) + sage: s==t + True + sage: t.parent() + Shifted Primed Tableaux of shape [4, 2] + sage: s.parent() + Shifted Primed Tableaux + sage: r = ShiftedPrimedTableaux([4, 2])(s); r.parent() + Shifted Primed Tableaux of shape [4, 2] + sage: s is t # identical shifted tableaux are distinct objects + False + + A shifted primed tableau is shallowly immutable, the rows are + represented as tuples. + + :: + + sage: t = ShiftedPrimedTableau([[1,"2p","3p",3],[2,"3p"]]) + sage: t[0][1] = 3 + Traceback (most recent call last): + ... + TypeError: 'tuple' object does not support item assignment + """ + self._skew = skew + + if isinstance(T, ShiftedPrimedTableau): + ClonableArray.__init__(self, parent, T) + return + + if not isinstance(T, list): + try: + t_ = list(T) + except TypeError: + t_ = [T] + else: + t_ = T + + if not all(isinstance(row, (list, tuple)) for row in t_): + t_ = [t_] + + t = [] + # Preprocessing list t for primes and other symbols + for row in t_: + row_new = [] + for element in row: + + if isinstance(element, str): + if element[-1] == "'" and element[:-1].isdigit() is True: + # Check if an element has "'" at the end + row_new.append(float(float(element[:-1]) - .5)) + continue + if element[-1] == "p" and element[:-1].isdigit() is True: + # Check if an element has "p" at the end + row_new.append(float(float(element[:-1]) - .5)) + continue + try: + if int(float(element)*2) == float(element)*2: + # Check if an element is a half-integer + row_new.append(float(element)) + continue + else: + raise ValueError("all numbers must be half-integers") + + except (TypeError, ValueError): + raise ValueError("primed elements have wrong format") + + t.append(row_new) + + # Accounting for zeros at the beginning and at the end of a row' + i = 0 + while i < len(t): + row = t[i] + try: + while row[0] == 0: + row.pop(0) + while row[-1] == 0: + row.pop(-1) + except IndexError: + t.remove(t[i]) + continue + t[i] = row + i += 1 + + t = [tuple(_) for _ in t] + ClonableArray.__init__(self, parent, t) + + def __eq__(self, other): + """ + Check whether ``self`` is equal to ``other``. + + INPUT: + + - ``other`` -- the element that ``self`` is compared to + + OUTPUT: Boolean + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,"2p"]]) + sage: t == ShiftedPrimedTableaux([2])([1,1.5]) + True + """ + if isinstance(other, ShiftedPrimedTableau): + return (self._skew == other._skew and list(self) == list(other)) + try: + Tab = ShiftedPrimedTableau(other) + except ValueError: + return False + return (self._skew == Tab._skew and list(self) == list(Tab)) + + def _to_matrix(self): + """ + Return a 2-dimensional array representation of a shifted tableau. + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p'],[3]]) + sage: mat = t._to_matrix() + sage: mat + [[1.0, 1.5, 2.0, 2.0], [0, 2.0, 2.5, 0], [0, 0, 3.0, 0]] + sage: t == ShiftedPrimedTableau(mat) + True + """ + array = [] + m = self.shape()[0] + sk_len = 0 if self._skew is None else len(self._skew) + for i in range(sk_len): + array.append([0]*(i+self._skew[i]) + + list(self[i]) + + [0]*(m-i-self._skew[i]-len(self[i]))) + for i in range(sk_len, len(self)): + array.append([0]*i + list(self[i]) + [0]*(m-i-len(self[i]))) + return array + + def check(self): + """ + Check that ``self`` is a valid primed tableau. + + EXAMPLES:: + + sage: T = ShiftedPrimedTableaux([4,2]) + sage: t = T([[1,'2p',2,2],[2,'3p']]) + sage: t.check() + sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + sage: s.check() + sage: t = T([['1p','2p',2,2],[2,'3p']]) + Traceback (most recent call last): + .... + ValueError: [['1p', '2p', 2, 2], [2, '3p']] is not an element of + Shifted Primed Tableaux of shape [4, 2] + """ + if self._skew is not None: + if not all(self._skew[i] > self._skew[i+1] + for i in range(len(self._skew)-1)): + raise ValueError('skew shape must be a strict partition') + skew = self._skew + [0]*(len(self)-len(self._skew)) + else: + skew = [0]*len(self) + if not all(len(self[i]) + skew[i] > len(self[i+1]) + skew[i+1] + for i in range(len(self)-1)): + raise ValueError('shape must be a strict partition') + for i, row in enumerate(self): + if i > 0: + if not all(val > self[i-1][j+1-skew[i-1]+skew[i]] + for j, val in enumerate(row) + if int(val) == val + if j+1 >= skew[i-1]-skew[i]): + raise ValueError( + 'column is not strictly increasing in non-primes') + if not all(val >= self[i-1][j+1-skew[i-1]+skew[i]] + for j, val in enumerate(row) + if int(val) != val + if j+1 >= skew[i-1]-skew[i]): + raise ValueError( + 'column is not weakly increasing in primes') + if not all(row[j] <= row[j+1] + for j in range(len(row)-1) if int(row[j]) == row[j]): + raise ValueError('row is not weakly increasing in non-primes') + if not all(row[j] < row[j+1] + for j in range(len(row)-1) if int(row[j]) != row[j]): + raise ValueError('row is not strictly increasing in primes') + if not all(int(row[0]) == row[0] + for i, row in enumerate(self) + if skew[i] == 0): + raise ValueError('diagonal elements must be non-primes') + + def _repr_(self): + """ + Represent ``self`` as a list of rows with rows represented as tuples of + half-integers. + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t + [(1.0, 1.5, 2.0, 2.0), (2.0, 2.5)] + sage: ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + [(1.5, 2.0, 3.0), (1.5,)] skewed by [2, 1] + """ + if self._skew is None: + return repr([tuple(_) for _ in self]) + return (repr([tuple(_) for _ in self]) + + " skewed by {}".format(self._skew)) + + def _repr_tab(self): + """ + Return a nested list of strings representing the elements. + + TESTS:: + + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t._repr_tab() + [[' 1 ', " 2'", ' 2 ', ' 2 '], [' 2 ', " 3'"]] + sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + sage: s._repr_tab() + [[' . ', ' . ', " 2'", ' 2 ', ' 3 '], [' . ', " 2'"]] + """ + if self._skew is not None: + skew = self._skew + [0]*(len(self)-len(self._skew)) + else: + skew = [0]*len(self) + max_len = len(str(self.max_entry()))+1 + string_tab = [] + for i, row in enumerate(self): + string_row = [' '*(max_len-1) + '. ']*(skew[i]) + for val in row: + if int(val) == val: + string_row.append(' '*(max_len-len(str(int(val)))) + + str(int(val)) + ' ') + else: + string_row.append(' '*(max_len-len(str(int(val+.5)))) + + str(int(val+.5)) + "'") + string_tab.append(string_row) + return string_tab + + def _repr_diagram(self): + """ + Return a string representation of ``self`` as an array. + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: print(t._repr_diagram()) + 1 2' 2 2 + 2 3' + sage: t = ShiftedPrimedTableau([[10,'11p',11,11],[11,'12']]) + sage: print(t._repr_diagram()) + 10 11' 11 11 + 11 12 + sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + sage: print(s._repr_diagram()) + . . 2' 2 3 + . 2' + """ + max_len = len(str(self.max_entry()))+2 + return "\n".join([" "*max_len*i + "".join(_) + for i, _ in enumerate(self._repr_tab())]) + + def _ascii_art_(self): + """ + Return ASCII representation of ``self``. + + EXAMPLES:: + + sage: ascii_art(ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']])) + +---+---+---+---+ + | 1 | 2'| 2 | 2 | + +---+---+---+---+ + | 2 | 3'| + +---+---+ + sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + sage: ascii_art(s) + +---+---+---+---+---+ + | . | . | 2'| 2 | 3 | + +---+---+---+---+---+ + | . | 2'| + +---+---+ + + TESTS:: + + sage: ascii_art(ShiftedPrimedTableau([])) + ++ + ++ + """ + from sage.typeset.ascii_art import AsciiArt + return AsciiArt(self._ascii_art_table(unicode=False).splitlines()) + + def _unicode_art_(self): + """ + Return a Unicode representation of ``self``. + + EXAMPLES:: + + sage: unicode_art(ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']])) + ┌───┬───┬───┬───┐ + │ 1 │ 2'│ 2 │ 2 │ + └───┼───┼───┼───┘ + │ 2 │ 3'│ + └───┴───┘ + sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + sage: unicode_art(s) + ┌───┬───┬───┬───┬───┐ + │ . │ . │ 2'│ 2 │ 3 │ + └───┼───┼───┼───┴───┘ + │ . │ 2'│ + └───┴───┘ + + TESTS:: + + sage: unicode_art(ShiftedPrimedTableau([])) + ┌┐ + └┘ + """ + from sage.typeset.unicode_art import UnicodeArt + return UnicodeArt(self._ascii_art_table(unicode=True).splitlines()) + + def _ascii_art_table(self, unicode=False): + """ + TESTS:: + + sage: t = ShiftedPrimedTableau([[1,'2p',2],[2,'3p']]) + sage: print(t._ascii_art_table(unicode=True)) + ┌───┬───┬───┐ + │ 1 │ 2'│ 2 │ + └───┼───┼───┤ + │ 2 │ 3'│ + └───┴───┘ + sage: print(t._ascii_art_table()) + +---+---+---+ + | 1 | 2'| 2 | + +---+---+---+ + | 2 | 3'| + +---+---+ + sage: s = ShiftedPrimedTableau([[1,'2p',2, 23],[2,'30p']]) + sage: print(s._ascii_art_table(unicode=True)) + ┌────┬────┬────┬────┐ + │ 1 │ 2'│ 2 │ 23 │ + └────┼────┼────┼────┘ + │ 2 │ 30'│ + └────┴────┘ + sage: print(s._ascii_art_table(unicode=False)) + +----+----+----+----+ + | 1 | 2'| 2 | 23 | + +----+----+----+----+ + | 2 | 30'| + +----+----+ + sage: s = ShiftedPrimedTableau([["2p",2,10],["2p"]],skew=[2,1]) + sage: print(s._ascii_art_table(unicode=True)) + ┌────┬────┬────┬────┬────┐ + │ . │ . │ 2'│ 2 │ 10 │ + └────┼────┼────┼────┴────┘ + │ . │ 2'│ + └────┴────┘ + """ + if unicode: + import unicodedata + v = unicodedata.lookup('BOX DRAWINGS LIGHT VERTICAL') + h = unicodedata.lookup('BOX DRAWINGS LIGHT HORIZONTAL') + dl = unicodedata.lookup('BOX DRAWINGS LIGHT DOWN AND LEFT') + dr = unicodedata.lookup('BOX DRAWINGS LIGHT DOWN AND RIGHT') + ul = unicodedata.lookup('BOX DRAWINGS LIGHT UP AND LEFT') + ur = unicodedata.lookup('BOX DRAWINGS LIGHT UP AND RIGHT') + vl = unicodedata.lookup('BOX DRAWINGS LIGHT VERTICAL AND LEFT') + uh = unicodedata.lookup('BOX DRAWINGS LIGHT UP AND HORIZONTAL') + dh = unicodedata.lookup('BOX DRAWINGS LIGHT DOWN AND HORIZONTAL') + vh = unicodedata.lookup( + 'BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL') + else: + v = '|' + h = '-' + dl = dr = ul = ur = vl = uh = dh = vh = '+' + + if self.shape() == []: + return dr + dl + '\n' + ur + ul + + # Get the widths of the columns + str_tab = self._repr_tab() + width = len(str_tab[0][0]) + str_list = [dr + (h*width + dh)*(len(str_tab[0])-1) + h*width + dl] + for nrow, row in enumerate(str_tab): + l1 = " " * (width+1) * nrow + l2 = " " * (width+1) * nrow + n = len(str_tab[nrow+1]) if nrow+1 < len(str_tab) else -1 + for i, e in enumerate(row): + if i == 0: + l1 += ur + h*width + elif i <= n+1: + l1 += vh + h*width + else: + l1 += uh + h*width + if unicode: + l2 += u"{}{:^{width}}".format(v, e, width=width) + else: + l2 += "{}{:^{width}}".format(v, e, width=width) + if i <= n: + l1 += vl + else: + l1 += ul + l2 += v + str_list.append(l2) + str_list.append(l1) + return "\n".join(str_list) + + def pp(self): + """ + Pretty print ``self``. + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t.pp() + 1 2' 2 2 + 2 3' + sage: t = ShiftedPrimedTableau([[10,'11p',11,11],[11,'12']]) + sage: t.pp() + 10 11' 11 11 + 11 12 + sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + sage: s.pp() + . . 2' 2 3 + . 2' + """ + print(self._repr_diagram()) + + def _latex_(self): + r""" + Return LaTex code for ``self``. + + EXAMPLES:: + + sage: T = ShiftedPrimedTableaux([4,2]) + sage: latex(T([[1,"2p",2,"3p"],[2,3]])) + {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} + \raisebox{-.6ex}{$\begin{array}[b]{*{4}c}\cline{1-4} + \lr{ 1 }&\lr{ 2'}&\lr{ 2 }&\lr{ 3'}\\\cline{1-4} + &\lr{ 2 }&\lr{ 3 }\\\cline{2-3} + \end{array}$} + } + """ + from sage.combinat.output import tex_from_array + L = [[None]*i + row for i, row in enumerate(self._repr_tab())] + return tex_from_array(L) + + def max_entry(self): + """ + Return the maximum entry in the primed tableaux ``self``, rounded up. + + EXAMPLES:: + + sage: Tab = ShiftedPrimedTableau([(1,1,1.5,2.5),(2,2)]) + sage: Tab.max_entry() + 3 + """ + if self == []: + return 0 + else: + flat = [item for sublist in self for item in sublist] + return int(round(max(flat))) + + def shape(self): + """ + Return the shape of the underlying partition of ``self`` in list + format. + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t.shape() + [4, 2] + sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + sage: s.shape() + [5, 2] / [2, 1] + """ + if self._skew is None: + return Partition([len(_) for _ in self]) + return SkewPartition(([len(self[i])+self._skew[i] + for i in range(len(self._skew))] + + [len(self[i]) + for i in range(len(self._skew), len(self))], + self._skew)) + + def weight(self): + """ + Return the weight of ``self``. + + The weight of a shifted primed tableau is defined to be the vector + with i-th component equal to the number of entries i and i' in the + tableau. + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t.weight() + (1, 4, 1) + """ + flat = [round(item) for sublist in self for item in sublist] + if flat == []: + max_ind = 0 + else: + max_ind = int(max(flat)) + weight = tuple([flat.count(i+1) for i in range(max_ind)]) + if isinstance(self.parent(), ShiftedPrimedTableaux_shape): + return self.parent().weight_lattice_realization()(weight) + return weight + + def _reading_word_with_positions(self): + """ + Return the reading word of ``self`` together with positions of the + corresponding letters in ``self``. + + The reading word of a shifted primed tableau is constructed + as follows: + + 1. List all primed entries in the tableau, column by + column, in decreasing order within each column, moving + from the rightmost column to the left, and with all + the primes removed (i.e. all entries are increased by + half a unit). + + 2. Then list all unprimed entries, row by row, in + increasing order within each row, moving from the + bottommost row to the top. + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t._reading_word_with_positions() + [((1, 2), 3), ((0, 1), 2), ((1, 1), 2), ((0, 0), 1), + ((0, 2), 2), ((0, 3), 2)] + """ + if self._skew is not None: + raise NotImplementedError('skew tableau must be empty') + mat = self._to_matrix() + ndim, mdim = len(mat), len(mat[0]) + list_with_positions = [] + for j in reversed(range(mdim)): + for i in range(ndim): + x = mat[i][j] + if int(x) != x: + list_with_positions.append(((i, j), int(x+0.5))) + for i in reversed(range(ndim)): + for j in range(mdim): + x = mat[i][j] + if int(x) == x and int(x) != 0: + list_with_positions.append(((i, j), int(x))) + return list_with_positions + + def reading_word(self): + """ + Return the reading word of ``self``. + + The reading word of a shifted primed tableau is constructed + as follows: + + 1. List all primed entries in the tableau, column by + column, in decreasing order within each column, moving + from the rightmost column to the left, and with all + the primes removed (i.e. all entries are increased by + half a unit). + + 2. Then list all unprimed entries, row by row, in + increasing order within each row, moving from the + bottommost row to the top. + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t.reading_word() + [3, 2, 2, 1, 2, 2] + """ + if self._skew is not None: + raise NotImplementedError('skew tableau must be empty') + return [tup[1] for tup in self._reading_word_with_positions()] + + def f(self, ind): + """ + Compute the action of the crystal operator `f_i` on a Shifted Primed + Tableau using cases from the paper [HPS2017]_. + + INPUT: + + - ``ind`` -- element in the index set of the crystal + + OUTPUT: + + Primed tableau or ``None``. + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,1,1,1,2.5],[2,2,2,2.5],[3,3]]) + sage: t.pp() + 1 1 1 1 3' + 2 2 2 3' + 3 3 + sage: s = t.f(2) + sage: print(s) + None + sage: t = ShiftedPrimedTableau([[1,1,1,1.5,2.5],[0,2,2,3,3], + ....: [0,0,3,4]]) + sage: t.pp() + 1 1 1 2' 3' + 2 2 3 3 + 3 4 + sage: s = t.f(2) + sage: s.pp() + 1 1 1 2' 3' + 2 3' 3 3 + 3 4 + + """ + + if self is None: + return None + + if self._skew is not None: + raise NotImplementedError('skew tableau must be empty') + + try: + self.parent()._weight + raise TypeError('operator generates an element outside of {}' + .format(self.parent())) + except AttributeError: + pass + + T = self._to_matrix() + + read_word = self._reading_word_with_positions() + read_word = [num + for num in read_word + if num[1] == ind or num[1] == ind+1] + + element_to_change = None + count = 0 + + for element in read_word: + if element[1] == ind+1: + count += 1 + elif count == 0: + element_to_change = element + else: + count -= 1 + + if element_to_change is None: + return None + + (r, c), elt = element_to_change + + if T[r][c] == ind - .5: + T = [[elt+.5 if elt != 0 else elt for elt in row] for row in T] + T = map(list, zip(*T)) + r, c = c, r + h, l = len(T), len(T[0]) + + if (c+1 == l or T[r][c+1] >= ind+1 or T[r][c+1] < 1): + (tp_r, tp_c) = (r, c) + while True: + if (tp_r+1 == h or + T[tp_r+1][tp_c] > ind+1 or + T[tp_r+1][tp_c] < 1): + break + if tp_r <= tp_c and T[tp_r+1][tp_r+1] == ind+1: + tp_r += 1 + tp_c = tp_r + break + if (ind+.5 not in T[tp_r+1]): + break + tp_r += 1 + tp_c = T[tp_r].index(ind+.5) + + if tp_r == r: + T[r][c] += 1 + + elif tp_r == tp_c: + T[r][c] += .5 + + else: + T[r][c] += .5 + T[tp_r][tp_c] += .5 + + elif T[r][c+1] == ind+.5: + T[r][c+1] += .5 + T[r][c] += .5 + + if r > c: + T = [[elt-.5 if elt != 0 else elt for elt in row] for row in T] + T = map(list, zip(*T)) + + return self.parent()(T) + + def e(self, ind): + """ + Compute the action of the crystal operator `e_i` on a Shifted Primed + Tableau using cases from the paper [HPS2017]_. + + INPUT: + + - ``ind`` -- an element in the index set of the crystal. + + OUTPUT: + + Primed tableau or 'None'. + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,1,1,1.5,2.5], + ....: [0,2,"3p",3,3],[0,0,3,4]]) + sage: t.pp() + 1 1 1 2' 3' + 2 3' 3 3 + 3 4 + sage: s = t.e(2) + sage: s.pp() + 1 1 1 2' 3' + 2 2 3 3 + 3 4 + sage: t == s.f(2) + True + + """ + if self is None: + return None + + if self._skew is not None: + raise NotImplementedError('skew tableau must be empty') + + try: + self.parent()._weight + raise TypeError('operator generates an element outside of {}' + .format(self.parent())) + except AttributeError: + pass + + T = self._to_matrix() + + read_word = self._reading_word_with_positions() + read_word = [num + for num in read_word + if num[1] == ind or num[1] == ind+1] + + element_to_change = None + count = 0 + + for element in read_word[::-1]: + if element[1] == ind: + count += 1 + elif count == 0: + element_to_change = element + else: + count -= 1 + + if element_to_change is None: + return None + (r, c), elt = element_to_change + + if T[r][c] == ind + .5: + T = [[elt+.5 if elt != 0 else elt for elt in row] for row in T] + T = map(list, zip(*T)) + r, c = c, r + + if (c == 0 or T[r][c-1] <= ind or T[r][c-1] < 1): + + (tp_r, tp_c) = (r, c) + while True: + + if tp_r == 0 or T[tp_r-1][tp_c] < ind or T[tp_r-1][tp_c] < 1: + break + if (ind+.5 not in T[tp_r-1]): + break + tp_r -= 1 + tp_c = T[tp_r].index(ind+.5) + + if tp_r == r: + T[r][c] -= 1 + + elif tp_r == tp_c: + T[r][c] -= .5 + + else: + T[r][c] -= .5 + T[tp_r][tp_c] -= .5 + + elif T[r][c-1] == ind+.5: + T[r][c-1] -= .5 + T[r][c] -= .5 + + if r > c: + T = [[elt-.5 if elt != 0 else elt for elt in row] for row in T] + T = map(list, zip(*T)) + + return self.parent()(T) + + def is_highest_weight(self): + """ + Return whether ``self`` is a highest weight element of the crystal. + + An element is highest weight if it vanishes under all crystal operators + `e_i`. + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.0, 1.0), + ....: (2.0, 2.0, 2.0, 2.5), (3.0, 3.0)]) + sage: print(t.e(1)) + None + sage: print(t.e(2)) + None + sage: t.is_highest_weight() + True + + """ + if self._skew is not None: + raise NotImplementedError('skew tableau must be empty') + read_w = self.reading_word() + count = {} + for l in read_w[::-1]: + try: + count[l] += 1 + except KeyError: + count[l] = 1 + if l > 1: + try: + if count[l] > count[l-1]: + return False + except KeyError: + return False + return True + + +class ShiftedPrimedTableaux(UniqueRepresentation, Parent): + """ + A factory for the various classes of shifted standard tableaux. + + INPUT: + + - a weight and/or a partition + + OUTPUT: + + - with no argument, the class of all primed tableaux + + - with a list or partition argument, the class of all primed + tableaux of that shape (infinite set if we don't specify the + weight or maximum entry) + + - with an additional integer argument ``max_entry``, the class + of all primed tableaux of a given shape and a given maximum + entry + + - with a tuple argument, the class of all primed tableaux of that + weight (finite set) + + A primed tableau is a tableau of shifted shape on the alphabet + `X' = \{1' < 1 < 2' < 2 < \cdots < n' < n\}` such that + + 1. the entries are weakly increasing along rows and columns + + 2. a row cannot have two repeated primed entries, and a column + cannot have two repeated non-primed entries + + 3. there are only non-primed entries along the main diagonal + + The weight of a tableau is defined to be the vector with `i`-th + component equal to the number of entries `i` and `i'` in the tableau. + The sum of the coordinates in the weight vector must be equal to the + number of entries in the partition. + + None of the Shifted Primed Tableaux classes can be iterated over. + + EXAMPLES:: + + sage: SPT = ShiftedPrimedTableaux(weight=(1,2,2), shape=[3,2]); SPT + Shifted Primed Tableaux of weight (1, 2, 2) and shape [3, 2] + sage: SPT.list() + [[(1.0, 2.0, 2.0), (3.0, 3.0)], + [(1.0, 1.5, 2.5), (2.0, 3.0)], + [(1.0, 1.5, 2.5), (2.0, 2.5)], + [(1.0, 1.5, 2.0), (3.0, 3.0)]] + sage: SPT = ShiftedPrimedTableaux((1,2)); SPT + Shifted Primed Tableaux of weight (1, 2) + sage: list(SPT) + [[(1.0, 2.0, 2.0)], [(1.0, 1.5, 2.0)], [(1.0, 1.5), (2.0,)]] + sage: SPT = ShiftedPrimedTableaux([3,2], max_entry = 2); SPT + Shifted Primed Tableaux of shape [3, 2] and maximum entry 2 + sage: list(SPT) + [[(1.0, 1.0, 1.0), (2.0, 2.0)], [(1.0, 1.0, 1.5), (2.0, 2.0)]] + + .. SEEALSO:: + + - :class:`ShiftedPrimedTableau` + """ + + Element = ShiftedPrimedTableau + + @staticmethod + def __classcall_private__(cls, *args, **kwargs): + r""" + This is a factory class which returns the appropriate parent based on + arguments. See the documentation for :class:`ShiftedPrimedTableaux` + for more information. + + TESTS:: + + sage: ShiftedPrimedTableaux([]) + Shifted Primed Tableaux of shape [] + sage: ShiftedPrimedTableaux(3) + Traceback (most recent call last): + ... + ValueError: invalid argument for weight or shape + sage: ShiftedPrimedTableaux(weight=(2,2,2), shape=[3,2]) + Traceback (most recent call last): + ... + ValueError: weight and shape are incompatible + sage: ShiftedPrimedTableaux([[1]]) + Traceback (most recent call last): + ... + ValueError: invalid shape argument + sage: ShiftedPrimedTableaux(weight=(2,2,2), max_entry=2) + Traceback (most recent call last): + ... + ValueError: maximum entry is incompatible with the weight + sage: ShiftedPrimedTableaux(shape=[4,1],skew=[3,2]) + Traceback (most recent call last): + ... + ValueError: skew shape must be inside the given tableau shape + """ + weight = None + shape = None + max_entry = None + skew = None + + if ('skew' in kwargs and kwargs['skew'] is not None): + try: + skew = Partition(kwargs['skew']) + except ValueError: + raise ValueError('invalid skew argument') + if not all(skew[i] > skew[i+1] for i in range(len(skew)-1)): + raise ValueError('skew shape must be a strict partition') + + if 'max_entry' in kwargs: + max_entry = int(kwargs['max_entry']) + + if 'shape' in kwargs: + shape = kwargs['shape'] + + if 'weight' in kwargs: + weight = tuple(kwargs['weight']) + + if args: + if isinstance(args[0], tuple) and weight is None: + weight = args[0] + if len(args) > 1: + if ((isinstance(args[1], + (list, Partition, SkewPartition)) or + args[1] is None) and shape is None): + shape = args[1] + else: + raise ValueError( + 'invalid argument for weight or shape') + + elif (isinstance(args[0], (list, Partition, SkewPartition)) + and shape is None): + shape = args[0] + if len(args) > 1: + if ((isinstance(args[1], tuple) or args[1] is None) and + weight is None): + weight = args[1] + else: + raise ValueError( + 'invalid argument for weight or shape') + else: + raise ValueError('invalid argument for weight or shape') + + if shape is not None: + if isinstance(shape, SkewPartition): + skew = shape.inner() + shape = shape.outer() + try: + shape = Partition(shape) + except ValueError: + raise ValueError('invalid shape argument') + if not all(shape[i] > shape[i+1] for i in range(len(shape)-1)): + raise ValueError( + "shape {} is not a strict partition".format(shape)) + if (skew is not None + and not all(skew[i] <= shape[i] + for i in range(len(skew)))): + raise ValueError( + 'skew shape must be inside the given tableau shape') + + if weight is not None: + while weight[-1] == 0: + weight = weight[:-1] + + if max_entry is not None and weight is not None: + if len(weight) > max_entry: + raise ValueError( + "maximum entry is incompatible with the weight") + + if shape is None and weight is None: + if max_entry is not None: + raise ValueError("specify shape or weight argument") + return ShiftedPrimedTableaux_all(skew=skew) + + elif weight is None: + return ShiftedPrimedTableaux_shape(shape, max_entry, skew=skew) + + elif shape is None: + return ShiftedPrimedTableaux_weight(weight, skew=skew) + + if (skew is not None and sum(shape) - sum(skew) != sum(weight) + or skew is None and sum(shape) != sum(weight)): + raise ValueError( + "weight and shape are incompatible") + + return ShiftedPrimedTableaux_weight_shape(weight, shape, skew=skew) + + def __contains__(self, T): + """ + EXAMPLES:: + + sage: [(1,'2p',2,2),(2,'3p')] in ShiftedPrimedTableaux() + True + sage: [(1,1),(2,2)] in ShiftedPrimedTableaux() + False + sage: [(1,1),('2p',)] in ShiftedPrimedTableaux() + False + sage: [(1,1,'3p'),(2,'3p')] in ShiftedPrimedTableaux() + True + sage: [(1,1,2),(2,2)] in ShiftedPrimedTableaux() + False + sage: [1,1,1] in ShiftedPrimedTableaux() + True + + TESTS:: + + sage: 1 in ShiftedPrimedTableaux() + True + sage: [] in ShiftedPrimedTableaux() + True + """ + try: + self.element_class(self, T) + return True + except ValueError: + return False + + +class ShiftedPrimedTableaux_all(ShiftedPrimedTableaux): + """ + The class of all Shifted Primed Tableaux. + """ + Element = ShiftedPrimedTableau + + def __init__(self, skew=None): + """ + Initialize the class of all shifted tableaux. + + TESTS:: + + sage: [[1,1.5],[2]] in ShiftedPrimedTableaux() + True + sage: [[1,1.5],[1.5]] in ShiftedPrimedTableaux() + False + sage: [[1,1],[1]] in ShiftedPrimedTableaux() + False + sage: [[1,1],[2,2]] in ShiftedPrimedTableaux() + False + """ + Parent.__init__(self, category=FiniteEnumeratedSets()) + self._skew = skew + + def _repr_(self): + """ + Return a string representation of ``self``. + + TESTS:: + + sage: ShiftedPrimedTableaux() + Shifted Primed Tableaux + """ + if self._skew is None: + return "Shifted Primed Tableaux" + return "Shifted Primed Tableaux skewed by {}".format(self._skew) + + def _element_constructor_(self, T): + """ + Construct an object from ``T`` as an element of ``self``, if + possible. + + INPUT: + + - ``T`` -- data which can be interpreted as a tableau + + OUTPUT: + + - the corresponding tableau object + + TESTS:: + + sage: Tab=ShiftedPrimedTableaux()([1,1,"2p"]); Tab + [(1.0, 1.0, 1.5)] + sage: Tab.parent() + Shifted Primed Tableaux + sage: Tab=ShiftedPrimedTableaux()([[1,1,2],[2,2]]) + Traceback (most recent call last): + ... + ValueError: [[1, 1, 2], [2, 2]] is not an element of + Shifted Primed Tableaux + + """ + try: + return self.element_class(self, T, skew=self._skew) + except ValueError: + raise ValueError( + "{} is not an element of {}".format(T, self)) + + def __contains__(self, T): + """ + TESTS:: + + sage: [1,1,2] in ShiftedPrimedTableaux() + True + sage: (2,1) in ShiftedPrimedTableaux() + False + """ + try: + self.element_class(self, T, skew=self._skew) + return True + except ValueError: + return False + + +class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): + """ + Shifted Primed Tableaux of a fixed shape. + + Shifted primed tableaux admit a type `A_n` classical crystal structure + with highest weights corresponding to a given shape. + + The list of module generators consists of all elements of the + crystal with nonincreasing weight. + + The crystal is constructed following operations described in [HPS2017]_. + + EXAMPLES:: + + sage: ShiftedPrimedTableaux([4,3,1], max_entry=4) + Shifted Primed Tableaux of shape [4, 3, 1] and maximum entry 4 + sage: ShiftedPrimedTableaux([4,3,1], max_entry=4).cardinality() + 384 + + We compute some of the crystal structure:: + + sage: SPTC = crystals.ShiftedPrimedTableaux([3,2], 3) + sage: T = SPTC.module_generators[-1] + sage: T + [(1.0, 1.0, 1.5), (2.0, 2.5)] + sage: T.f(2) + [(1.0, 1.0, 2.5), (2.0, 2.5)] + sage: len(SPTC.module_generators) + 7 + sage: SPTC[0] + [(1.0, 1.0, 1.0), (2.0, 2.0)] + sage: SPTC.cardinality() + 24 + """ + @staticmethod + def __classcall_private__(cls, shape, max_entry=None, skew=None): + """ + Normalize the attributes for the class. + + TEST:: + + sage: SPT = ShiftedPrimedTableaux(shape=[2,1]) + sage: SPT._shape.parent() + Partitions + """ + shape = _Partitions(shape) + return (super(ShiftedPrimedTableaux_shape, cls) + .__classcall__(cls, shape=shape, max_entry=max_entry, + skew=skew)) + + Element = ShiftedPrimedTableau + + def __init__(self, shape, max_entry, skew): + """ + Initialize the class of Shifted Primed Tableaux of a given shape. + + If ``max_elt`` is specified, a finite set with entries smaller + or equal to ``max_elt``. + + TESTS:: + + sage: TestSuite(ShiftedPrimedTableaux([4,2,1], max_entry=4)).run() + """ + # Determine the correct category + if max_entry is None: + if skew is None: + category = RegularCrystals().Infinite() + self._cartan_type = CartanType(['A+oo']) + else: + category = Sets().Infinite() + else: + if skew is None: + category = ClassicalCrystals() + self._cartan_type = CartanType(['A', max_entry-1]) + else: + category = Sets().Finite() + + ShiftedPrimedTableaux.__init__(self, category=category) + self._max_entry = max_entry + self._skew = skew + if skew is None: + self._shape = Partition(shape) + else: + self._shape = SkewPartition((shape, skew)) + + def _repr_(self): + """ + Return a string representation of ``self``. + + TESTS:: + + sage: ShiftedPrimedTableaux([3,2,1]) + Shifted Primed Tableaux of shape [3, 2, 1] + """ + base = "Shifted Primed Tableaux of shape " + self._shape._repr_() + if self._max_entry is not None: + base += " and maximum entry {}".format(self._max_entry) + return base + + def __contains__(self, T): + """ + TESTS:: + + sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux([4,2]) + True + """ + try: + Tab = self.element_class(self, T, skew=self._skew) + except ValueError: + return False + + if self._max_entry is None: + return (Tab.shape() == self._shape) + + return (Tab.shape() == self._shape and + Tab.max_entry() <= self._max_entry) + + def _element_constructor_(self, T): + """ + Construct an object from ``T`` as an element of ``self``, if + possible. + + INPUT: + + - ``T`` -- data which can be interpreted as a primed tableau + + OUTPUT: + + - the corresponding primed tableau object + + TESTS:: + + sage: tab= ShiftedPrimedTableaux([3])([1,1,1.5]); tab + [(1.0, 1.0, 1.5)] + sage: tab.parent() + Shifted Primed Tableaux of shape [3] + sage: ShiftedPrimedTableaux([3])([1,1]) + Traceback (most recent call last): + ... + ValueError: [1, 1] is not an element of Shifted Primed Tableaux + of shape [3] + """ + try: + Tab = self.element_class(self, T, skew=self._skew) + except ValueError: + raise ValueError( + "{} is not an element of {}".format(T, self)) + + if self._max_entry is None: + if Tab.shape() == self._shape: + return Tab + else: + if (Tab.shape() == self._shape and + Tab.max_entry() <= self._max_entry): + return Tab + raise ValueError("{} is not an element of {}".format(T, self)) + + @lazy_attribute + def module_generators(self): + """ + Return the generators of ``self`` as a crystal. + + TEST:: + + sage: SPT = ShiftedPrimedTableaux(shape=[2,1]) + sage: SPT.module_generators + ([(1.0, 1.0), (2.0,)], + [(1.0, 2.0), (3.0,)], + [(1.0, 1.5), (3.0,)]) + """ + if self._skew is not None: + raise NotImplementedError("only for non-skew shapes") + list_dw = [] + if self._max_entry is None: + max_entry = sum(self._shape) + else: + max_entry = self._max_entry + for weight in (Partition(self._shape) + .dominated_partitions(rows=max_entry)): + list_dw.extend([self(T) + for T in ShiftedPrimedTableaux( + weight=tuple(weight), + shape=self._shape)]) + return tuple(list_dw) + + def shape(self): + """ + Return the shape of the shifted tableaux ``self``. + + TESTS:: + + sage: ShiftedPrimedTableaux([6,4,3,1]).shape() + [6, 4, 3, 1] + """ + return self._shape + + def __iter__(self): + """ + Iterate over ``self``, if ``max_entry`` is specified. + + EXAMPLES:: + + sage: Tabs = ShiftedPrimedTableaux([3,2], max_entry=3) + sage: Tabs[:4] + [[(1.0, 1.0, 1.0), (2.0, 2.0)], + [(1.0, 1.0, 1.0), (2.0, 3.0)], + [(1.0, 1.0, 1.0), (2.0, 2.5)], + [(1.0, 1.0, 1.0), (3.0, 3.0)]] + sage: len(list(Tabs)) + 24 + + TESTS:: + + sage: Tabs = ShiftedPrimedTableaux([3,2]) + sage: Tabs[:3] + Traceback (most recent call last): + ... + ValueError: set is infinite + """ + if self._skew is not None: + raise NotImplementedError('skew tableau must be empty') + if self._max_entry is None: + raise ValueError("set is infinite") + for weight in OrderedPartitions(sum(self._shape)+self._max_entry, + k=self._max_entry): + weight_n = tuple([w-1 for w in weight]) + for tab in ShiftedPrimedTableaux(shape=self._shape, + weight=weight_n): + yield self(tab) + + +class ShiftedPrimedTableaux_weight(ShiftedPrimedTableaux): + """ + Shifted Primed Tableaux of fixed weight. + + TESTS:: + + sage: ShiftedPrimedTableaux((2,3,1)) + Shifted Primed Tableaux of weight (2, 3, 1) + sage: ShiftedPrimedTableaux((2,3,1)).cardinality() + 17 + """ + Element = ShiftedPrimedTableau + + def __init__(self, weight, skew=None): + """ + Initialize the class of Shifted Primed Tableaux of a given weight. + + TESTS:: + + sage: TestSuite( ShiftedPrimedTableaux((3,2,1)) ).run() + """ + Parent.__init__(self, category=FiniteEnumeratedSets()) + self._weight = weight + self._skew = skew + + def _repr_(self): + """ + Return a string representation of ``self``. + + TESTS:: + + sage: ShiftedPrimedTableaux((3,2,1)) + Shifted Primed Tableaux of weight (3, 2, 1) + """ + if self._skew is None: + return "Shifted Primed Tableaux of weight {}".format(self._weight) + return ("Shifted Primed Tableaux of weight {} skewed by {}" + .format(self._weight, self._skew)) + + def __contains__(self, T): + """ + TESTS:: + + sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux((1,4,1)) + True + """ + try: + Tab = self.element_class(self, T, skew=self._skew) + except ValueError: + return False + + return Tab.weight() == self._weight + + def _element_constructor_(self, T): + """ + Construct an object from ``T`` as an element of ``self``, if + possible. + + INPUT: + + - ``T`` -- data which can be interpreted as a primed tableau + + OUTPUT: + + - the corresponding primed tableau object + + TESTS:: + + sage: tab= ShiftedPrimedTableaux((2,1))([1,1,1.5]); tab + [(1.0, 1.0, 1.5)] + sage: tab.parent() + Shifted Primed Tableaux of weight (2, 1) + """ + try: + Tab = self.element_class(self, T, skew=self._skew) + except ValueError: + raise ValueError( + "{} is not an element of {}".format(T, self)) + if Tab.weight() == self._weight: + return Tab + + raise ValueError("{} is not an element of {}".format(T, self)) + + def __iter__(self): + """ + Iterate over ``self``. + + EXAMPLES:: + + sage: Tabs = ShiftedPrimedTableaux((2,3)) + sage: Tabs[:4] + [[(1.0, 1.0, 2.0, 2.0, 2.0)], + [(1.0, 1.0, 1.5, 2.0, 2.0)], + [(1.0, 1.0, 2.0, 2.0), (2.0,)], + [(1.0, 1.0, 1.5, 2.0), (2.0,)]] + sage: len(list(Tabs)) + 5 + """ + return (tab for shape_ in Partitions(sum(self._weight)) + if all(shape_[i] > shape_[i+1] for i in range(len(shape_)-1)) + for tab in ShiftedPrimedTableaux(shape=shape_, + weight=self._weight, + skew=self._skew)) + + +class ShiftedPrimedTableaux_weight_shape(ShiftedPrimedTableaux): + """ + Shifted Primed Tableaux of the fixed weight and shape. + + TESTS:: + + sage: ShiftedPrimedTableaux((2,3,2),[4,2,1]) + Shifted Primed Tableaux of weight (2, 3, 2) and shape [4, 2, 1] + sage: ShiftedPrimedTableaux((2,3,2), [4,2,1]).cardinality() + 4 + """ + Element = ShiftedPrimedTableau + + def __init__(self, weight, shape, skew=None): + """ + Initialize the class of Shifted Primed Tableaux of the given weight + and shape. + + TESTS:: + + sage: TestSuite( ShiftedPrimedTableaux((3,2,1)) ).run() + """ + Parent.__init__(self, category=FiniteEnumeratedSets()) + self._weight = weight + self._skew = skew + if skew is None: + self._shape = Partition(shape) + else: + self._shape = SkewPartition((shape, skew)) + + def _repr_(self): + """ + Return a string representation of ``self``. + + TESTS:: + + sage: ShiftedPrimedTableaux([3,2,1],(4,2)) + Shifted Primed Tableaux of weight (4, 2) and shape [3, 2, 1] + """ + return ( + "Shifted Primed Tableaux of weight {} and shape {}" + .format(self._weight, self._shape)) + + def __contains__(self, T): + """ + TESTS:: + + sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux( + ....: [4,2],(1,4,1)) + True + """ + try: + Tab = self.element_class(self, T, skew=self._skew) + except ValueError: + return False + + return Tab.weight() == self._weight and Tab.shape() == self._shape + + def _element_constructor_(self, T): + """ + Construct an object from ``T`` as an element of ``self``, if + possible. + + TESTS:: + + sage: tab= ShiftedPrimedTableaux([3],(2,1))([1,1,1.5]); tab + [(1.0, 1.0, 1.5)] + sage: tab.parent() + Shifted Primed Tableaux of weight (2, 1) and shape [3] + sage: ShiftedPrimedTableaux([3],(2,1))([1,1]) + Traceback (most recent call last): + ... + ValueError: [1, 1] is not an element of Shifted Primed Tableaux + of weight (2, 1) and shape [3] + """ + try: + Tab = self.element_class(self, T, skew=self._skew) + except ValueError: + raise ValueError( + "{} is not an element of {}".format(T, self)) + + if Tab.shape() == self._shape and Tab.weight() == self._weight: + return Tab + + raise ValueError("{} is not an element of {}".format(T, self)) + + def __iter__(self): + """ + Iterate over ``self``. + + EXAMPLES:: + + sage: Tabs = ShiftedPrimedTableaux([3,2], (1,2,2)) + sage: Tabs[:4] + [[(1.0, 2.0, 2.0), (3.0, 3.0)], + [(1.0, 1.5, 2.5), (2.0, 3.0)], + [(1.0, 1.5, 2.5), (2.0, 2.5)], + [(1.0, 1.5, 2.0), (3.0, 3.0)]] + sage: len(list(Tabs)) + 4 + """ + if self._skew is not None: + raise NotImplementedError('skew tableau must be empty') + + if not self._shape.dominates( + Partition(sorted(list(self._weight), key=int, reverse=True))): + return + yield + full_shape = self._shape + sub_tab = [] + tab_list_new = [[]] + for i, w in enumerate(self._weight): + tab_list_old = tab_list_new + tab_list_new = [] + for sub_tab in tab_list_old: + sub_shape = [len(sub_tab[r]) for r in range(len(sub_tab))] + for strip in _add_strip(sub_shape, full_shape, w): + l = int(len(strip)/2) + if len(sub_shape) < len(full_shape): + new_tab = [sub_tab[r] + + [float(i+.5)]*int(strip[r]) + + [float(i+1)]*int(strip[-r-1]) + for r in range(l-1)] + if strip[l] != 0: + new_tab.append([float(i+1)]*int(strip[l])) + else: + new_tab = [sub_tab[r] + + [float(i+.5)]*int(strip[r]) + + [float(i+1)]*int(strip[-r-1]) + for r in range(l)] + tab_list_new.append(new_tab) + for tab in tab_list_new: + yield(ShiftedPrimedTableau(tab)) + + +#################### +# Helper functions # +#################### + + +def _add_strip(sub_tab, full_tab, length): + """ + Helper function used in the algorithm to generate all Shifted Primed + Tableaux of the fixed weight and shape. + + TESTS:: + + sage: list(ShiftedPrimedTableaux([3,1],(2,2))) # indirect doctest + [[(1.0, 1.0, 2.0), (2.0,)], [(1.0, 1.0, 1.5), (2.0,)]] + """ + if sum(sub_tab)+length > sum(full_tab): + raise ValueError("strip does not fit") + + if len(sub_tab) == 0: + cliff_list = [] + else: + cliff_list = [int(sub_tab[0] != full_tab[0])] + + for row in range(1, len(sub_tab)): + if sub_tab[row] == full_tab[row]: + cliff_list.append(0) + elif sub_tab[row-1]-1 == sub_tab[row]: + cliff_list[-1] += 1 + else: + cliff_list.append(1) + + if len(sub_tab) < len(full_tab): + cliff_list.append(0) + + for primes_num in range(min(sum(cliff_list), length)+1): + for primed_list in IntegerVectors(n=primes_num, k=len(cliff_list), + outer=cliff_list): + row = 0 + primed_strip = list() + for i, cliff in enumerate(cliff_list): + if cliff == 0: + row += 1 + primed_strip.append(0) + pass + primed_strip.extend([int(primed_list[i] > j) + for j in range(cliff)]) + row += cliff + plat_list = list() + + if len(sub_tab) < len(full_tab) and len(sub_tab) != 0: + plat_list.append(min(sub_tab[-1] + primed_strip[-2] - 1, + full_tab[len(sub_tab)])) + for row in range(1, len(sub_tab))[::-1]: + plat_list.append( + min(sub_tab[row-1]+primed_strip[row-1]-1, full_tab[row]) + - sub_tab[row] - primed_strip[row]) + if len(sub_tab) > 0: + plat_list.append(full_tab[0] - sub_tab[0] - primed_strip[0]) + else: + plat_list.append(full_tab[0]) + + if sum(plat_list) < length - primes_num: + pass + for non_primed_strip in IntegerVectors(n=length-primes_num, + k=len(plat_list), + outer=plat_list): + yield (list(primed_strip) + list(non_primed_strip)) + From 560deb4c7f727446eac7192180188d2adc411015 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sun, 7 Jan 2018 09:39:50 -0800 Subject: [PATCH 460/740] Corrected the name to properly generate docs --- src/doc/en/reference/combinat/module_list.rst | 2 +- src/sage/combinat/catalog_partitions.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 2bb6dc6f91b..f92df8278dc 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -301,6 +301,7 @@ Comprehensive Module list sage/combinat/sf/sfa sage/combinat/sf/witt sage/combinat/shard_order + sage/combinat/shifted_primed_tableau sage/combinat/shuffle sage/combinat/sidon_sets sage/combinat/similarity_class_type @@ -343,7 +344,6 @@ Comprehensive Module list sage/combinat/tableau sage/combinat/tableau_residues sage/combinat/tableau_tuple - sage/combinat/tableau_shifted_primed sage/combinat/tamari_lattices sage/combinat/tiling sage/combinat/tools diff --git a/src/sage/combinat/catalog_partitions.py b/src/sage/combinat/catalog_partitions.py index e3b43947183..1040a0ea410 100644 --- a/src/sage/combinat/catalog_partitions.py +++ b/src/sage/combinat/catalog_partitions.py @@ -21,5 +21,5 @@ - :ref:`sage.combinat.rsk` - :ref:`sage.combinat.growth` - :ref:`sage.combinat.tableau_residues` -- :ref:`sage.combinat.tableau_shifted_primed` +- :ref:`sage.combinat.shifted_primed_tableau` """ From 28561f117e7982506af5bb812c90a6d437e669a8 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sun, 7 Jan 2018 10:32:08 -0800 Subject: [PATCH 461/740] Revert changes to partition.py --- src/sage/combinat/partition.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 653363002ff..64fd34ea3c7 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -62,7 +62,6 @@ all in the category framework except ``PartitionsRestricted`` (which will eventually be removed). Cleaned up documentation. - EXAMPLES: There are `5` partitions of the integer `4`:: @@ -305,7 +304,6 @@ from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.sets.non_negative_integers import NonNegativeIntegers - from sage.rings.all import QQ, ZZ, NN, IntegerModRing from sage.arith.all import factorial, gcd from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing From 6892bdda7b4df40403dea51a1ff31f47dba0fc0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 10 Jan 2018 19:24:45 +0100 Subject: [PATCH 462/740] Workaround a noisy doctest we had seen issues with this test at some point (https://trac.sagemath.org/ticket/21869#comment:274.) We are not sure what's the issue and probably it is not even caused by something random happening in the valuations code. Anyway, we relax the check here to make sure that others don't see rarely failing doctests (in the patchbots) for an area they do not at all care about. --- src/sage/rings/valuation/mapped_valuation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/valuation/mapped_valuation.py b/src/sage/rings/valuation/mapped_valuation.py index a91c8da0a87..d6dc170056d 100644 --- a/src/sage/rings/valuation/mapped_valuation.py +++ b/src/sage/rings/valuation/mapped_valuation.py @@ -504,8 +504,8 @@ def upper_bound(self, x): sage: L. = K.extension(t^2 + 1) sage: v = valuations.pAdicValuation(QQ, 5) sage: u,uu = v.extensions(L) - sage: u.upper_bound(t + 2) - 3 + sage: u.upper_bound(t + 2) >= 1 + True sage: u(t + 2) 1 From f9af766f54b7bf3200c7fe55f7da2304bd56a856 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Thu, 11 Jan 2018 18:03:58 +0100 Subject: [PATCH 463/740] First undocumented prototype --- src/sage/groups/abelian_gps/abelian_group.py | 13 ++ .../groups/abelian_gps/abelian_group_gap.py | 152 ++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 src/sage/groups/abelian_gps/abelian_group_gap.py diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py index 5e2dfc327db..1e473feff46 100644 --- a/src/sage/groups/abelian_gps/abelian_group.py +++ b/src/sage/groups/abelian_gps/abelian_group.py @@ -901,6 +901,19 @@ def _gap_init_(self): raise TypeError('abelian groups in GAP are finite, but self is infinite') return 'AbelianGroup(%s)'%list(self.gens_orders()) + @cached_method + def gap(self): + r""" + Return this abelian group a libgap group + + EXAMPLES:: + + sage: A = AbelianGroup([2,3,0,6]) + sage: A.gap() + """ + from sage.groups.abelian_gps.abelian_group_gap import AbelianGroup_gap + return AbelianGroup_gap(self) + def gen(self, i=0): """ The `i`-th generator of the abelian group. diff --git a/src/sage/groups/abelian_gps/abelian_group_gap.py b/src/sage/groups/abelian_gps/abelian_group_gap.py new file mode 100644 index 00000000000..edfdc74e219 --- /dev/null +++ b/src/sage/groups/abelian_gps/abelian_group_gap.py @@ -0,0 +1,152 @@ +from sage.groups.libgap_wrapper import ParentLibGAP, ElementLibGAP +from sage.groups.libgap_mixin import GroupMixinLibGAP +from sage.groups.group import AbelianGroup as AbelianGroupBase +from sage.libs.gap.element import GapElement +from sage.libs.gap.libgap import libgap +from sage.misc.cachefunc import cached_method +from sage.rings.integer_ring import ZZ +from sage.arith.all import GCD, LCM + + + +class AbelianGroupElement_gap(ElementLibGAP): + r""" + An element of an abelian group via libgap + """ + def __init__(self, parent, x, check=False): + """ + The Python constructor. + + See :class:`AbelianGroupElement_gap` for details. + + TESTS:: + + sage: A = AbelianGroup([3,6]) + sage: G = A.gap() + + """ + if not isinstance(x, GapElement): + A = parent._A + x = A(x) + # turn this into a gap element + gens_gap = parent.gens() + exp = x.exponents() + x = gens_gap[0]**0 + for i in range(len(exp)): + x *= gens_gap[i]**exp[i] + if check: + x in parent.gap() + ElementLibGAP.__init__(self, parent, x) + + def __hash__(self): + r""" + """ + return hash(self.parent()) ^ hash(self._exponents) + + def exponents(self): + r""" + """ + P = self.parent() + x = libgap.Factorization(P.gap(), self.gap()) + L = x.ExtRepOfObj() + Lgens = L[::2] + Lexpo = L[1::2] + exp = [] + for k in range(len(P.gens())): + if k in Lgens: + exp.append(0) + else: + exp.append(Lexpo[k]) + return exp + + def order(self): + r""" + """ + if self.parent().is_finite(): + return self.gap().Order() + else: + # is there a way to do this in gap? + P = self.parent() + order = P.gens_orders() + L = self.exponents() + N = LCM([order[i]/GCD(order[i],L[i]) for i in range(len(order)) if L[i]!=0]) + return N + + def sage(self): + r""" + """ + P = self.parent() + x = libgap.Factorization(P, self.gap()) + L = x.ExtRepOfObj() + gens_sage = P._A.gens() + e = P._A.identity() + n = len(L) + for k in range(0,n,2): + e *= gens_sage[L[k]-1]**L[k+1] + return e + +class AbelianGroup_gap(GroupMixinLibGAP, ParentLibGAP, AbelianGroupBase): + r""" + """ + def __init__(self, A): + r""" + """ + AbelianGroupBase.__init__(self, category=A.category()) + ParentLibGAP.__init__(self, libgap(A)) + + self._A = A + + Element = AbelianGroupElement_gap + + def _latex_(self): + return self._A.latex() + + def _gap_init(self): + return self._A._gap_init() + + def _repr_(self): + r""" + Return a string representation + + EXAMPLES:: + + sage: A = AbelianGroup([2,6]) + sage: G = A.gap() + sage: G._repr_() + """ + s = self._A._repr_() + s += " implemented with gap" + return s + + def dual_group(self): + return self._A.dual_group() + + def is_trivial(self): + return 1 == self.order() + + def identity(self): + return self(self.gap().Identity()) + + @cached_method + def elementary_divisors(self): + ediv = self.gap().AbelianInvariants() + from sage.matrix.constructor import diagonal_matrix + ed = diagonal_matrix(ZZ, ediv).elementary_divisors() + return tuple(d for d in ed if d!=1) + + @cached_method + def exponent(self): + r""" + """ + return self.gap().Exponent() + + + def gens_orders(self): + r""" + """ + return (g.order() for g in self.gens()) + + def sage(self): + r""" + """ + return self._A From 0b13ad7861cddf55f6a5de067ef7541f7d2cac52 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Fri, 12 Jan 2018 17:04:56 +0100 Subject: [PATCH 464/740] Documentation an bugfixes. Use Polycyclic package for infinite groups. First working version. --- src/sage/groups/abelian_gps/abelian_group.py | 35 +- .../abelian_gps/abelian_group_element.py | 17 + .../groups/abelian_gps/abelian_group_gap.py | 358 +++++++++++++++--- 3 files changed, 336 insertions(+), 74 deletions(-) diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py index 1e473feff46..726803935bd 100644 --- a/src/sage/groups/abelian_gps/abelian_group.py +++ b/src/sage/groups/abelian_gps/abelian_group.py @@ -700,7 +700,7 @@ def __bool__(self): @cached_method def dual_group(self, names="X", base_ring=None): """ - Returns the dual group. + Return the dual group. INPUT: @@ -889,31 +889,40 @@ def _gap_init_(self): sage: G = AbelianGroup(3,[0,3,4],names="abc"); G Multiplicative Abelian group isomorphic to Z x C3 x C4 sage: G._gap_init_() - Traceback (most recent call last): - ... - TypeError: abelian groups in GAP are finite, but self is infinite + 'AbelianPcpGroup([0, 3, 4])' """ # TODO: Use the package polycyclic has AbelianPcpGroup, which can handle # the infinite case but it is a GAP package not GPL'd. # Use this when the group is infinite... - # return 'AbelianPcpGroup(%s)'%list(self.invariants()) - if not self.is_finite(): - raise TypeError('abelian groups in GAP are finite, but self is infinite') - return 'AbelianGroup(%s)'%list(self.gens_orders()) + if self.is_finite(): + return 'AbelianGroup(%s)'%list(self.gens_orders()) + from sage.misc.package import is_package_installed, PackageNotFoundError + if is_package_installed('gap_packages'): + # Make sure to LoadPackage("Polycyclic") in gap + return 'AbelianPcpGroup(%s)'%list(self.gens_orders()) + raise PackageNotFoundError("gap_packages") @cached_method def gap(self): r""" - Return this abelian group a libgap group - + Return this abelian group as a libgap group + EXAMPLES:: - - sage: A = AbelianGroup([2,3,0,6]) + + sage: A = AbelianGroup([2,3,6]) sage: A.gap() + Multiplicative Abelian group isomorphic to C2 x C3 x C6 with gap + + If the gap package "Polycyclic" is installed, it can handle + infinite cyclic groups as well:: + + sage: A = AbelianGroup([2,0,6]) # optional - gap_packages + sage: A.gap() + Multiplicative Abelian group isomorphic to C2 x Z x C6 with gap """ from sage.groups.abelian_gps.abelian_group_gap import AbelianGroup_gap return AbelianGroup_gap(self) - + def gen(self, i=0): """ The `i`-th generator of the abelian group. diff --git a/src/sage/groups/abelian_gps/abelian_group_element.py b/src/sage/groups/abelian_gps/abelian_group_element.py index 75d3c18a14f..38209961fb4 100644 --- a/src/sage/groups/abelian_gps/abelian_group_element.py +++ b/src/sage/groups/abelian_gps/abelian_group_element.py @@ -164,3 +164,20 @@ def word_problem(self, words): """ from sage.groups.abelian_gps.abelian_group import AbelianGroup, word_problem return word_problem(words,self) + + def gap(self): + r""" + Push this element to the corresponding gap based group. + + Note that the notation of elements might change. + + EXAMPLES:: + + sage: A = AbelianGroup([2,5]) + sage: a = A.an_element() + sage: a.gap() + g1*g2 + sage: a == a.gap().sage() + True + """ + return self.parent().gap()(self) diff --git a/src/sage/groups/abelian_gps/abelian_group_gap.py b/src/sage/groups/abelian_gps/abelian_group_gap.py index edfdc74e219..e4720d255f9 100644 --- a/src/sage/groups/abelian_gps/abelian_group_gap.py +++ b/src/sage/groups/abelian_gps/abelian_group_gap.py @@ -6,6 +6,7 @@ from sage.misc.cachefunc import cached_method from sage.rings.integer_ring import ZZ from sage.arith.all import GCD, LCM +from sage.structure.unique_representation import UniqueRepresentation @@ -13,18 +14,21 @@ class AbelianGroupElement_gap(ElementLibGAP): r""" An element of an abelian group via libgap """ - def __init__(self, parent, x, check=False): + def __init__(self, parent, x, check=True): """ The Python constructor. See :class:`AbelianGroupElement_gap` for details. TESTS:: - + sage: A = AbelianGroup([3,6]) sage: G = A.gap() - + sage: g = G.an_element() + sage: TestSuite(g).run() """ + if isinstance(x, AbelianGroupElement_gap): + x = x.gap() if not isinstance(x, GapElement): A = parent._A x = A(x) @@ -34,119 +38,351 @@ def __init__(self, parent, x, check=False): x = gens_gap[0]**0 for i in range(len(exp)): x *= gens_gap[i]**exp[i] + x = x.gap() if check: - x in parent.gap() + if not x in parent.gap(): + raise ValueError("%s is not in the group %s" %(x, parent)) ElementLibGAP.__init__(self, parent, x) - + def __hash__(self): r""" + Return the hash of this element + + EXAMPLES:: + + sage: A = AbelianGroup([3,2,4]) + sage: G = A.gap() + sage: g = G.an_element() + sage: g.__hash__() # random + 1693277541873681615 """ - return hash(self.parent()) ^ hash(self._exponents) - - def exponents(self): + return hash(self.parent()) ^ hash(self.exponents()) + + def __reduce__(self): r""" + Implement pickling + + sage: A = AbelianGroup([3,2,4]) + sage: G = A.gap() + sage: g = G.an_element() + sage: g == loads(dumps(g)) + True + sage: g.__reduce__() + (Multiplicative Abelian group isomorphic to C3 x C2 x C4 with gap, (f0*f1*f2,)) """ - P = self.parent() - x = libgap.Factorization(P.gap(), self.gap()) - L = x.ExtRepOfObj() - Lgens = L[::2] - Lexpo = L[1::2] - exp = [] - for k in range(len(P.gens())): - if k in Lgens: - exp.append(0) - else: - exp.append(Lexpo[k]) - return exp - - def order(self): + return self.parent(), (self.sage(),) + + def exponents(self): r""" + Return the tuple of exponents. + + EXAMPLES:: + + sage: A = AbelianGroup([4,7,9]) + sage: G = A.gap() + sage: gens = G.gens() + sage: g = gens[0]^2 * gens[1]^4 * gens[2]^8 + sage: g.exponents() + (2, 4, 8) + sage: A = AbelianGroup([4,7,0]) # optional - gap_packages + sage: G = A.gap() + sage: gens = G.gens() + sage: g = gens[0]^2 * gens[1]^4 * gens[2]^8 + sage: g.exponents() + (2, 4, 8) """ - if self.parent().is_finite(): - return self.gap().Order() + if self.parent()._with_pc: + exp = self.gap().Exponents().sage() else: - # is there a way to do this in gap? + # works only for small groups + # as gap has problems to solve the word problem P = self.parent() - order = P.gens_orders() - L = self.exponents() - N = LCM([order[i]/GCD(order[i],L[i]) for i in range(len(order)) if L[i]!=0]) - return N - + x = libgap.Factorization(P.gap(), self.gap()) + L = x.ExtRepOfObj().sage() + Lgens = L[::2] + Lexpo = L[1::2] + exp = [] + orders = P.gens_orders() + i = 0 + for k in range(len(P.gens())): + if not k+1 in Lgens: + exp.append(0) + else: + i = Lgens.index(k+1) + exp.append(Lexpo[i] % orders[k]) + return tuple(exp) + + def order(self): + r""" + Return the order of this element. + + EXAMPLES:: + + sage: G = AbelianGroup([4]).gap() + sage: g = G.gens()[0] + sage: g.order() + 4 + sage: G = AbelianGroup([0]).gap() # optional - gap_packages + sage: g = G.gens()[0] + sage: g.order() + +Infinity + """ + return self.gap().Order().sage() + def sage(self): r""" + Convert this element to the corresponding abelian group in sage. + + EXAMPLES:: + + sage: A = AbelianGroup([2,3,7,4]) + sage: G = A.gap() + sage: all([a == a.gap().sage() for a in A]) + True + sage: all([g == g.sage().gap() for g in G]) + True """ P = self.parent() - x = libgap.Factorization(P, self.gap()) - L = x.ExtRepOfObj() gens_sage = P._A.gens() e = P._A.identity() - n = len(L) - for k in range(0,n,2): - e *= gens_sage[L[k]-1]**L[k+1] + exp = self.exponents() + for i in range(len(exp)): + e *= gens_sage[i]**exp[i] return e -class AbelianGroup_gap(GroupMixinLibGAP, ParentLibGAP, AbelianGroupBase): +class AbelianGroup_gap(UniqueRepresentation, GroupMixinLibGAP, ParentLibGAP, AbelianGroupBase): r""" + Class for finitely generated abelian groups implemented with gap. + + Needs the gap package "Polycyclic" in case the group is infinite. + + INPUT: + + - ``A`` -- an `AbelianGroup` + - ``G`` -- (default:``None``) a gap group + - ``ambient`` -- (default:``None``) an AbelianGroup_gap + + EXAMPLES:: + + sage: A = AbelianGroup([3,2,5]) + sage: G = A.gap() + sage: TestSuite(G).run() """ - def __init__(self, A): + def __init__(self, A, G=None, ambient=None): r""" """ AbelianGroupBase.__init__(self, category=A.category()) - ParentLibGAP.__init__(self, libgap(A)) - + self._with_pc = libgap.LoadPackage("Polycyclic") + if G is None: + if self._with_pc: + G = libgap.eval("AbelianPcpGroup(%s)"%list(A.gens_orders())) + else: + G = libgap(A) + ParentLibGAP.__init__(self, G, ambient=ambient) self._A = A - + Element = AbelianGroupElement_gap - + def _latex_(self): - return self._A.latex() - - def _gap_init(self): - return self._A._gap_init() - + """ + Return the latex representation of this group. + + EXAMPLES:: + + sage: A = AbelianGroup([2,6]) + sage: G = A.gap() + sage: G._latex_() + '$\\mathrm{AbelianGroup}( 2, (2, 6) )$ with gap' + """ + return self._A._latex_() + " with gap" + def _repr_(self): r""" - Return a string representation - + Return the string representation of this group. + EXAMPLES:: - + sage: A = AbelianGroup([2,6]) sage: G = A.gap() sage: G._repr_() + 'Multiplicative Abelian group isomorphic to C2 x C6 with gap' """ s = self._A._repr_() - s += " implemented with gap" + s += " with gap" return s - def dual_group(self): - return self._A.dual_group() - + def __hash__(self): + r""" + A hash function. + + EXAMPLES:: + + sage: A = AbelianGroup([2,6]) + sage: G = A.gap() + sage: G.__hash__() # random + -9223363266866470866 + """ + return hash(self._A) ^ hash(type(self)) + + def _coerce_map_from_(self, S): + r""" + """ + try: + if S.ambient() is self: + return True + except AttributeError: + pass + if self._A is S: + return True + def is_trivial(self): + r""" + Return if this group is the trivial group. + + EXAMPLES:: + + sage: A = AbelianGroup([]).gap() + sage: A.is_trivial() + True + """ return 1 == self.order() - + def identity(self): + r""" + Return the identity element of this group. + + EXAMPLES: + + sage: G = AbelianGroup([4,10]).gap() + sage: G.identity() + 1 + """ return self(self.gap().Identity()) - + @cached_method def elementary_divisors(self): - ediv = self.gap().AbelianInvariants() + r""" + Return the elementary divisors of the group. + + See :meth:`sage.groups.abelian_gps.abelian_group_gap.elementary_divisors` + """ + ediv = self.gap().AbelianInvariants().sage() from sage.matrix.constructor import diagonal_matrix ed = diagonal_matrix(ZZ, ediv).elementary_divisors() return tuple(d for d in ed if d!=1) - + @cached_method def exponent(self): r""" + Return the exponent of this abelian group. + + EXAMPLES:: + + sage: G = AbelianGroup([2,3,7]).gap() + sage: G + Multiplicative Abelian group isomorphic to C2 x C3 x C7 with gap + sage: G.exponent() + 42 + sage: G = AbelianGroup([2,4,6]).gap() + sage: G + Multiplicative Abelian group isomorphic to C2 x C4 x C6 with gap + sage: G.exponent() + 12 """ - return self.gap().Exponent() + return self.gap().Exponent().sage() - + @cached_method def gens_orders(self): r""" + Return the orders of the cyclic factors that this group has + been defined with. + + Use :meth:`elementary_divisors` if you are looking for an + invariant of the group. + + OUTPUT: + + - A tuple of integers. + + EXAMPLES:: + + sage: Z2xZ3 = AbelianGroup([2,3]).gap() + sage: Z2xZ3.gens_orders() + (2, 3) + sage: Z2xZ3.elementary_divisors() + (6,) + sage: Z6 = AbelianGroup([6]).gap() + sage: Z6.gens_orders() + (6,) + sage: Z6.elementary_divisors() + (6,) + sage: Z2xZ3.is_isomorphic(Z6) + True + sage: Z2xZ3 is Z6 + False """ - return (g.order() for g in self.gens()) - + return tuple(g.order() for g in self.gens()) + def sage(self): r""" + Return the sage pendant of this abelian group. + + EXAMPLES:: + + sage: A = AbelianGroup([]) + sage: G = A.gap() + sage: A is G.sage() + True """ return self._A + + def subgroup(self, gens): + r""" + Return the subgroup of this group generated by ``gens``. + + INPUT: + + - ``gens`` -- a list of elements coercible into this group + + OUTPUT: + + - a subgroup which remembers that it is a subgroup + + EXAMPLES:: + + sage: A = AbelianGroup([2,3,4,5]) + sage: G = A.gap() + sage: gen = G.gens()[:2] + sage: S = G.subgroup(gen) + sage: S + Multiplicative Abelian subgroup isomorphic to C2 x C3 generated by {f0, f1} with gap + sage: g = G.an_element() + sage: s = S.an_element() + sage: a = A.an_element() + sage: g*s + g2^2*g3*g4 + sage: g*a + g2^2*g3^2*g4^2 + sage: A = AbelianGroup([3,4,0,2]) + sage: G = A.gap() + sage: gen = G.gens()[:2] + sage: S = G.subgroup(gen) + sage: g = G.an_element() + sage: s = S.an_element() + sage: a = A.an_element() + sage: g*s + g1^2*g2^2*g3*g4 + sage: g*a + g1^2*g2^2*g3^2 + + TESTS:: + + sage: h = G.gens()[3] + sage: h in S + False + """ + gens_gap = [self(g).gap() for g in gens] + gens_sage = [g.sage() for g in gens] + G = self.gap().Subgroup(gens_gap) + A = self._A.subgroup(gens_sage) + return AbelianGroup_gap(A, G=G, ambient=self) From 74234d4de9bf109ded91c74c0657678317de7bb3 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Fri, 12 Jan 2018 22:00:42 +0100 Subject: [PATCH 465/740] Documentation and removed hash method for AbelianGroup_gap --- .../groups/abelian_gps/abelian_group_gap.py | 68 +++++++++++++------ 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/src/sage/groups/abelian_gps/abelian_group_gap.py b/src/sage/groups/abelian_gps/abelian_group_gap.py index e4720d255f9..4ef372e3b1b 100644 --- a/src/sage/groups/abelian_gps/abelian_group_gap.py +++ b/src/sage/groups/abelian_gps/abelian_group_gap.py @@ -46,7 +46,7 @@ def __init__(self, parent, x, check=True): def __hash__(self): r""" - Return the hash of this element + Return the hash of this element. EXAMPLES:: @@ -60,7 +60,9 @@ def __hash__(self): def __reduce__(self): r""" - Implement pickling + Implement pickling. + + EXAMPLES:: sage: A = AbelianGroup([3,2,4]) sage: G = A.gap() @@ -158,18 +160,28 @@ class AbelianGroup_gap(UniqueRepresentation, GroupMixinLibGAP, ParentLibGAP, Abe INPUT: - - ``A`` -- an `AbelianGroup` - - ``G`` -- (default:``None``) a gap group - - ``ambient`` -- (default:``None``) an AbelianGroup_gap + - ``A`` -- :class:`sage.groups.abelian_gps.abelian_group.AbelianGroup_class` + - ``G`` -- (default:``None``) a gap group + - ``ambient`` -- (default:``None``) an :class:`AbelianGroup_gap` EXAMPLES:: sage: A = AbelianGroup([3,2,5]) sage: G = A.gap() - sage: TestSuite(G).run() + sage: G + Multiplicative Abelian group isomorphic to C3 x C2 x C5 with gap """ def __init__(self, A, G=None, ambient=None): r""" + Create an instance of this class. + + See :class:`AbelianGroup_gap` for details + + TESTS:: + + sage: A = AbelianGroup([3,2,5]) + sage: G = A.gap() + sage: TestSuite(G).run() """ AbelianGroupBase.__init__(self, category=A.category()) self._with_pc = libgap.LoadPackage("Polycyclic") @@ -211,21 +223,29 @@ def _repr_(self): s += " with gap" return s - def __hash__(self): + def _coerce_map_from_(self, S): r""" - A hash function. + Return whether ``S`` canonically coerces to ``self``. + + INPUT: + + - ``S`` -- anything. + + OUTPUT: + + Boolean. EXAMPLES:: - sage: A = AbelianGroup([2,6]) + sage: A = AbelianGroup([2,3,4,5]) sage: G = A.gap() - sage: G.__hash__() # random - -9223363266866470866 - """ - return hash(self._A) ^ hash(type(self)) - - def _coerce_map_from_(self, S): - r""" + sage: gen = G.gens()[:2] + sage: S = G.subgroup(gen) + sage: G._coerce_map_from_(S) + True + sage: S._coerce_map_from_(G) + sage: G._coerce_map_from_(A) + True """ try: if S.ambient() is self: @@ -265,6 +285,13 @@ def elementary_divisors(self): Return the elementary divisors of the group. See :meth:`sage.groups.abelian_gps.abelian_group_gap.elementary_divisors` + + EXAMPLES:: + + sage: A = AbelianGroup([2,3,4,5]) + sage: G = A.gap() + sage: G.elementary_divisors() + (2, 60) """ ediv = self.gap().AbelianInvariants().sage() from sage.matrix.constructor import diagonal_matrix @@ -294,15 +321,14 @@ def exponent(self): @cached_method def gens_orders(self): r""" - Return the orders of the cyclic factors that this group has - been defined with. + Return the orders of the generators. Use :meth:`elementary_divisors` if you are looking for an invariant of the group. OUTPUT: - - A tuple of integers. + - a tuple of integers EXAMPLES:: @@ -342,11 +368,11 @@ def subgroup(self, gens): INPUT: - - ``gens`` -- a list of elements coercible into this group + - ``gens`` -- a list of elements coercible into this group OUTPUT: - - a subgroup which remembers that it is a subgroup + - a subgroup EXAMPLES:: From e78174232093e0931514f6fc6038be22a2bc4e0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 14 Jan 2018 02:43:24 +0100 Subject: [PATCH 466/740] Fix documentation build issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit when building PDF manuals. I am not sure why I need to protect ≤ in a math environment but I checked that this looks good in the PDF output like that. --- src/doc/en/reference/valuations/index.rst | 6 +++--- src/sage/rings/valuation/valuation.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/en/reference/valuations/index.rst b/src/doc/en/reference/valuations/index.rst index ed674037962..391a7e58162 100644 --- a/src/doc/en/reference/valuations/index.rst +++ b/src/doc/en/reference/valuations/index.rst @@ -81,9 +81,9 @@ Internally, all the above is backed by the algorithms described in ``K.valuation(x - 4)`` to the field `L` above to outline how this works internally. -First, the valuation on `K` is induced by a valuation on `\Q[x]`. To construct -this valuation, we start from the trivial valuation on `\Q` and consider its -induced Gauss valuation on `\Q[x]`, i.e., the valuation that assigns to a +First, the valuation on `K` is induced by a valuation on `\QQ[x]`. To construct +this valuation, we start from the trivial valuation on `\\Q` and consider its +induced Gauss valuation on `\\Q[x]`, i.e., the valuation that assigns to a polynomial the minimum of the coefficient valuations:: sage: R. = QQ[] diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 302db445b57..a55bb5bc332 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -1052,7 +1052,7 @@ class MacLaneApproximantNode(object): relevant, everything else are caches/debug info.) The boolean ``ef`` denotes whether ``v`` already has the final ramification index E and residue degree F of this approximant. An edge V -- P represents the - relation ``P.v ≤ V.v`` (pointwise on the polynomial ring K[x]) between the + relation ``P.v`` `≤` ``V.v`` (pointwise on the polynomial ring K[x]) between the valuations. TESTS:: From f041576f199f6d6c5933730d463fa666d7dbab31 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sun, 14 Jan 2018 13:05:42 -0800 Subject: [PATCH 467/740] Various fixes --- src/sage/combinat/shifted_primed_tableau.py | 448 ++++++++++---------- 1 file changed, 214 insertions(+), 234 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index 39270f2d546..34918ab0768 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -21,6 +21,7 @@ from six import add_metaclass from sage.combinat.partition import Partition, Partitions, _Partitions, OrderedPartitions +from sage.combinat.tableau import Tableaux from sage.combinat.skew_partition import SkewPartition from sage.combinat.integer_vector import IntegerVectors from sage.rings.integer import Integer @@ -61,7 +62,7 @@ class ShiftedPrimedTableau(ClonableArray): sage: t[1] (2.0, 2.5) sage: ShiftedPrimedTableau([["2p",2,3],["2p","3p"],[2]], skew=[2,1]) - [(1.5, 2.0, 3.0), (1.5, 2.5), (2.0,)] skewed by [2, 1] + [(None, None, 1.5, 2.0, 3.0), (None, 1.5, 2.5), (2.0,)] TESTS:: @@ -126,6 +127,10 @@ def __init__(self, parent, T, skew=None): ... TypeError: 'tuple' object does not support item assignment """ + if skew is not None: + if not all(skew[i] > skew[i+1] for i in range(len(skew)-1)): + raise ValueError('skew shape must be a strict partition') + self._skew = skew if isinstance(T, ShiftedPrimedTableau): @@ -186,8 +191,16 @@ def __init__(self, parent, T, skew=None): t[i] = row i += 1 - t = [tuple(_) for _ in t] - ClonableArray.__init__(self, parent, t) + if skew is not None: + t_ = ([(None,)*skew[i] + tuple(t[i]) for i in range(len(skew))] + + [tuple(t[i]) for i in range(len(skew), len(t))]) + else: + t_ = [tuple(row) for row in t] + + if not all(len(t_[i]) > len(t_[i+1]) for i in range(len(t_)-1)): + raise ValueError('shape must be a strict partition') + + ClonableArray.__init__(self, parent, t_) def __eq__(self, other): """ @@ -213,6 +226,30 @@ def __eq__(self, other): return False return (self._skew == Tab._skew and list(self) == list(Tab)) + def __ne__(self, other): + """ + Check whether ``self`` is not equal to ``other``. + + INPUT: + + - ``other`` -- the element that ``self`` is compared to + + OUTPUT: Boolean + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,"2p"]]) + sage: t != ShiftedPrimedTableaux([2])([1,1]) + True + """ + if isinstance(other, ShiftedPrimedTableau): + return (self._skew != other._skew or list(self) != list(other)) + try: + Tab = ShiftedPrimedTableau(other) + except ValueError: + return True + return (self._skew != Tab._skew or list(self) != list(Tab)) + def _to_matrix(self): """ Return a 2-dimensional array representation of a shifted tableau. @@ -226,6 +263,8 @@ def _to_matrix(self): sage: t == ShiftedPrimedTableau(mat) True """ + if self._skew is not None: + raise NotImplementedError('skew tableau must be empty') array = [] m = self.shape()[0] sk_len = 0 if self._skew is None else len(self._skew) @@ -246,7 +285,7 @@ def check(self): sage: T = ShiftedPrimedTableaux([4,2]) sage: t = T([[1,'2p',2,2],[2,'3p']]) sage: t.check() - sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"],[2]],skew=[2,1]) sage: s.check() sage: t = T([['1p','2p',2,2],[2,'3p']]) Traceback (most recent call last): @@ -255,34 +294,30 @@ def check(self): Shifted Primed Tableaux of shape [4, 2] """ if self._skew is not None: - if not all(self._skew[i] > self._skew[i+1] - for i in range(len(self._skew)-1)): - raise ValueError('skew shape must be a strict partition') skew = self._skew + [0]*(len(self)-len(self._skew)) else: skew = [0]*len(self) - if not all(len(self[i]) + skew[i] > len(self[i+1]) + skew[i+1] - for i in range(len(self)-1)): - raise ValueError('shape must be a strict partition') for i, row in enumerate(self): if i > 0: - if not all(val > self[i-1][j+1-skew[i-1]+skew[i]] + if not all(val > self[i-1][j+1] for j, val in enumerate(row) - if int(val) == val - if j+1 >= skew[i-1]-skew[i]): + if j+1 >= skew[i-1] + if int(val) == val): raise ValueError( 'column is not strictly increasing in non-primes') - if not all(val >= self[i-1][j+1-skew[i-1]+skew[i]] + if not all(val >= self[i-1][j+1] for j, val in enumerate(row) - if int(val) != val - if j+1 >= skew[i-1]-skew[i]): + if j+1 >= skew[i-1] + if int(val) != val): raise ValueError( 'column is not weakly increasing in primes') if not all(row[j] <= row[j+1] - for j in range(len(row)-1) if int(row[j]) == row[j]): + for j in range(skew[i], len(row)-1) + if int(row[j]) == row[j]): raise ValueError('row is not weakly increasing in non-primes') if not all(row[j] < row[j+1] - for j in range(len(row)-1) if int(row[j]) != row[j]): + for j in range(skew[i], len(row)-1) + if int(row[j]) != row[j]): raise ValueError('row is not strictly increasing in primes') if not all(int(row[0]) == row[0] for i, row in enumerate(self) @@ -291,8 +326,7 @@ def check(self): def _repr_(self): """ - Represent ``self`` as a list of rows with rows represented as tuples of - half-integers. + Return a string representation of ``self``. EXAMPLES:: @@ -300,12 +334,20 @@ def _repr_(self): sage: t [(1.0, 1.5, 2.0, 2.0), (2.0, 2.5)] sage: ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) - [(1.5, 2.0, 3.0), (1.5,)] skewed by [2, 1] + [(None, None, 1.5, 2.0, 3.0), (None, 1.5)] + """ + return self.parent().options._dispatch(self, '_repr_', 'display') + + def _repr_list(self): """ - if self._skew is None: - return repr([tuple(_) for _ in self]) - return (repr([tuple(_) for _ in self]) + - " skewed by {}".format(self._skew)) + Return a string representation of ``self`` as a list of tuples. + + EXAMPLES:: + + sage: print(ShiftedPrimedTableau([['2p',3],[2,2]], skew=[2])) + [(None, None, 1.5, 3.0), (2.0, 2.0)] + """ + return (repr([row for row in self])) def _repr_tab(self): """ @@ -327,14 +369,12 @@ def _repr_tab(self): max_len = len(str(self.max_entry()))+1 string_tab = [] for i, row in enumerate(self): - string_row = [' '*(max_len-1) + '. ']*(skew[i]) - for val in row: - if int(val) == val: - string_row.append(' '*(max_len-len(str(int(val)))) - + str(int(val)) + ' ') + string_row = ['.'.rjust(max_len) + ' ']*(skew[i]) + for j in range(skew[i], len(row)): + if int(row[j]) == row[j]: + string_row.append(str(int(row[j])).rjust(max_len) + ' ') else: - string_row.append(' '*(max_len-len(str(int(val+.5)))) - + str(int(val+.5)) + "'") + string_row.append(str(int(row[j]+.5)).rjust(max_len) + "'") string_tab.append(string_row) return string_tab @@ -520,7 +560,7 @@ def pp(self): sage: t.pp() 10 11' 11 11 11 12 - sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) + sage: s = ShiftedPrimedTableau([['2p',2,3],['2p']],skew=[2,1]) sage: s.pp() . . 2' 2 3 . 2' @@ -552,7 +592,7 @@ def max_entry(self): EXAMPLES:: - sage: Tab = ShiftedPrimedTableau([(1,1,1.5,2.5),(2,2)]) + sage: Tab = ShiftedPrimedTableau([(1,1,'2p','3p'),(2,2)]) sage: Tab.max_entry() 3 """ @@ -577,19 +617,15 @@ def shape(self): [5, 2] / [2, 1] """ if self._skew is None: - return Partition([len(_) for _ in self]) - return SkewPartition(([len(self[i])+self._skew[i] - for i in range(len(self._skew))] + - [len(self[i]) - for i in range(len(self._skew), len(self))], - self._skew)) + return Partition([len(row) for row in self]) + return SkewPartition(([len(row) for row in self], self._skew)) def weight(self): - """ + r""" Return the weight of ``self``. The weight of a shifted primed tableau is defined to be the vector - with i-th component equal to the number of entries i and i' in the + with `i`-th component equal to the number of entries i and i' in the tableau. EXAMPLES:: @@ -616,15 +652,15 @@ def _reading_word_with_positions(self): The reading word of a shifted primed tableau is constructed as follows: - 1. List all primed entries in the tableau, column by - column, in decreasing order within each column, moving - from the rightmost column to the left, and with all - the primes removed (i.e. all entries are increased by - half a unit). + 1. List all primed entries in the tableau, column by + column, in decreasing order within each column, moving + from the rightmost column to the left, and with all + the primes removed (i.e. all entries are increased by + half a unit). - 2. Then list all unprimed entries, row by row, in - increasing order within each row, moving from the - bottommost row to the top. + 2. Then list all unprimed entries, row by row, in + increasing order within each row, moving from the + bottommost row to the top. EXAMPLES:: @@ -657,15 +693,15 @@ def reading_word(self): The reading word of a shifted primed tableau is constructed as follows: - 1. List all primed entries in the tableau, column by - column, in decreasing order within each column, moving - from the rightmost column to the left, and with all - the primes removed (i.e. all entries are increased by - half a unit). + 1. List all primed entries in the tableau, column by + column, in decreasing order within each column, moving + from the rightmost column to the left, and with all + the primes removed (i.e. all entries are increased by + half a unit). - 2. Then list all unprimed entries, row by row, in - increasing order within each row, moving from the - bottommost row to the top. + 2. Then list all unprimed entries, row by row, in + increasing order within each row, moving from the + bottommost row to the top. EXAMPLES:: @@ -678,9 +714,9 @@ def reading_word(self): return [tup[1] for tup in self._reading_word_with_positions()] def f(self, ind): - """ - Compute the action of the crystal operator `f_i` on a Shifted Primed - Tableau using cases from the paper [HPS2017]_. + r""" + Compute the action of the crystal operator `f_i` on a shifted primed + tableau using cases from the paper [HPS2017]_. INPUT: @@ -714,9 +750,6 @@ def f(self, ind): """ - if self is None: - return None - if self._skew is not None: raise NotImplementedError('skew tableau must be empty') @@ -790,12 +823,12 @@ def f(self, ind): T = [[elt-.5 if elt != 0 else elt for elt in row] for row in T] T = map(list, zip(*T)) - return self.parent()(T) + return type(self)(self.parent(), T) def e(self, ind): - """ - Compute the action of the crystal operator `e_i` on a Shifted Primed - Tableau using cases from the paper [HPS2017]_. + r""" + Compute the action of the crystal operator `e_i` on a shifted primed + tableau using cases from the paper [HPS2017]_. INPUT: @@ -822,9 +855,6 @@ def e(self, ind): True """ - if self is None: - return None - if self._skew is not None: raise NotImplementedError('skew tableau must be empty') @@ -892,10 +922,10 @@ def e(self, ind): T = [[elt-.5 if elt != 0 else elt for elt in row] for row in T] T = map(list, zip(*T)) - return self.parent()(T) + return type(self)(self.parent(), T) - def is_highest_weight(self): - """ + def is_highest_weight(self, index_set=None): + r""" Return whether ``self`` is a highest weight element of the crystal. An element is highest weight if it vanishes under all crystal operators @@ -905,71 +935,61 @@ def is_highest_weight(self): sage: t = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.0, 1.0), ....: (2.0, 2.0, 2.0, 2.5), (3.0, 3.0)]) - sage: print(t.e(1)) - None - sage: print(t.e(2)) - None sage: t.is_highest_weight() True + sage: s = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.0, 1.0), + ....: (2.0, 2.0, 2.5, 3.0)]) + sage: s.is_highest_weight(index_set = [1]) + True """ if self._skew is not None: raise NotImplementedError('skew tableau must be empty') read_w = self.reading_word() - count = {} + max_entry = max(read_w) + count = {i: 0 for i in range(max_entry+1)} + if index_set is None: + index_set = list(range(1, max_entry)) for l in read_w[::-1]: - try: - count[l] += 1 - except KeyError: - count[l] = 1 - if l > 1: - try: - if count[l] > count[l-1]: - return False - except KeyError: - return False + count[l] += 1 + if (l-1 in index_set) and (count[l] > count[l-1]): + return False return True class ShiftedPrimedTableaux(UniqueRepresentation, Parent): - """ - A factory for the various classes of shifted standard tableaux. - - INPUT: + r""" + Returns the combinatorial class of shifted primed tableaux subject + ro the constraints given by the arguments. - - a weight and/or a partition + A primed tableau is a tableau of shifted shape on the alphabet + `X' = \{1' < 1 < 2' < 2 < \cdots < n' < n\}` such that - OUTPUT: + 1. the entries are weakly increasing along rows and columns - - with no argument, the class of all primed tableaux + 2. a row cannot have two repeated primed entries, and a column + cannot have two repeated non-primed entries - - with a list or partition argument, the class of all primed - tableaux of that shape (infinite set if we don't specify the - weight or maximum entry) + 3. there are only non-primed entries along the main diagonal - - with an additional integer argument ``max_entry``, the class - of all primed tableaux of a given shape and a given maximum - entry + Valid input: shape partition. - - with a tuple argument, the class of all primed tableaux of that - weight (finite set) + Valid input keywords: ``shape``, ``weight``, ``max_entry``, ``skew``. - A primed tableau is a tableau of shifted shape on the alphabet - `X' = \{1' < 1 < 2' < 2 < \cdots < n' < n\}` such that + -``shape`` specifies the (outer skew) shape of tableaux - 1. the entries are weakly increasing along rows and columns + -``weight`` specifies the weight of tableaux - 2. a row cannot have two repeated primed entries, and a column - cannot have two repeated non-primed entries + -``max_entry`` specifies the maximum entry of tableaux - 3. there are only non-primed entries along the main diagonal + -``skew`` specifies the inner skew shape of tableaux The weight of a tableau is defined to be the vector with `i`-th component equal to the number of entries `i` and `i'` in the tableau. The sum of the coordinates in the weight vector must be equal to the number of entries in the partition. - None of the Shifted Primed Tableaux classes can be iterated over. + Shape and skew arguments must be strictly decreasing partitions. EXAMPLES:: @@ -980,7 +1000,7 @@ class ShiftedPrimedTableaux(UniqueRepresentation, Parent): [(1.0, 1.5, 2.5), (2.0, 3.0)], [(1.0, 1.5, 2.5), (2.0, 2.5)], [(1.0, 1.5, 2.0), (3.0, 3.0)]] - sage: SPT = ShiftedPrimedTableaux((1,2)); SPT + sage: SPT = ShiftedPrimedTableaux(weight=(1,2)); SPT Shifted Primed Tableaux of weight (1, 2) sage: list(SPT) [[(1.0, 2.0, 2.0)], [(1.0, 1.5, 2.0)], [(1.0, 1.5), (2.0,)]] @@ -989,19 +1009,30 @@ class ShiftedPrimedTableaux(UniqueRepresentation, Parent): sage: list(SPT) [[(1.0, 1.0, 1.0), (2.0, 2.0)], [(1.0, 1.0, 1.5), (2.0, 2.0)]] + TESTS:: + + sage: [(1,'2p',2,2),(2,'3p')] in ShiftedPrimedTableaux() + True + sage: [(1,1),(2,2)] in ShiftedPrimedTableaux() + False + sage: 1 in ShiftedPrimedTableaux(skew=[1]) + True + sage: [] in ShiftedPrimedTableaux() + True + .. SEEALSO:: - :class:`ShiftedPrimedTableau` """ - Element = ShiftedPrimedTableau + options = Tableaux.options @staticmethod def __classcall_private__(cls, *args, **kwargs): r""" - This is a factory class which returns the appropriate parent based on - arguments. See the documentation for :class:`ShiftedPrimedTableaux` - for more information. + Normalize and process input to return the correct parent and ensure a + unique representation. See the documentation for + :class:`ShiftedPrimedTableaux` for more information. TESTS:: @@ -1010,7 +1041,7 @@ def __classcall_private__(cls, *args, **kwargs): sage: ShiftedPrimedTableaux(3) Traceback (most recent call last): ... - ValueError: invalid argument for weight or shape + ValueError: invalid shape argument sage: ShiftedPrimedTableaux(weight=(2,2,2), shape=[3,2]) Traceback (most recent call last): ... @@ -1051,29 +1082,9 @@ def __classcall_private__(cls, *args, **kwargs): weight = tuple(kwargs['weight']) if args: - if isinstance(args[0], tuple) and weight is None: - weight = args[0] - if len(args) > 1: - if ((isinstance(args[1], - (list, Partition, SkewPartition)) or - args[1] is None) and shape is None): - shape = args[1] - else: - raise ValueError( - 'invalid argument for weight or shape') - - elif (isinstance(args[0], (list, Partition, SkewPartition)) - and shape is None): - shape = args[0] - if len(args) > 1: - if ((isinstance(args[1], tuple) or args[1] is None) and - weight is None): - weight = args[1] - else: - raise ValueError( - 'invalid argument for weight or shape') - else: - raise ValueError('invalid argument for weight or shape') + if shape is not None: + raise ValueError('shape argument was specified twice') + shape = args[0] if shape is not None: if isinstance(shape, SkewPartition): @@ -1081,16 +1092,15 @@ def __classcall_private__(cls, *args, **kwargs): shape = shape.outer() try: shape = Partition(shape) - except ValueError: + except (ValueError, TypeError): raise ValueError('invalid shape argument') + if not all(shape[i] > shape[i+1] for i in range(len(shape)-1)): - raise ValueError( - "shape {} is not a strict partition".format(shape)) - if (skew is not None - and not all(skew[i] <= shape[i] - for i in range(len(skew)))): - raise ValueError( - 'skew shape must be inside the given tableau shape') + raise ValueError("shape {} is not a strict partition".format(shape)) + + if (skew is not None and not all(skew[i] <= shape[i] + for i in range(len(skew)))): + raise ValueError('skew shape must be inside the given tableau shape') if weight is not None: while weight[-1] == 0: @@ -1098,53 +1108,36 @@ def __classcall_private__(cls, *args, **kwargs): if max_entry is not None and weight is not None: if len(weight) > max_entry: - raise ValueError( - "maximum entry is incompatible with the weight") + raise ValueError("maximum entry is incompatible with the weight") if shape is None and weight is None: if max_entry is not None: raise ValueError("specify shape or weight argument") return ShiftedPrimedTableaux_all(skew=skew) - elif weight is None: - return ShiftedPrimedTableaux_shape(shape, max_entry, skew=skew) + if weight is None: + return ShiftedPrimedTableaux_shape(shape, max_entry=max_entry, skew=skew) - elif shape is None: + if shape is None: return ShiftedPrimedTableaux_weight(weight, skew=skew) if (skew is not None and sum(shape) - sum(skew) != sum(weight) or skew is None and sum(shape) != sum(weight)): - raise ValueError( - "weight and shape are incompatible") + raise ValueError("weight and shape are incompatible") return ShiftedPrimedTableaux_weight_shape(weight, shape, skew=skew) def __contains__(self, T): """ - EXAMPLES:: - - sage: [(1,'2p',2,2),(2,'3p')] in ShiftedPrimedTableaux() - True - sage: [(1,1),(2,2)] in ShiftedPrimedTableaux() - False - sage: [(1,1),('2p',)] in ShiftedPrimedTableaux() - False - sage: [(1,1,'3p'),(2,'3p')] in ShiftedPrimedTableaux() - True - sage: [(1,1,2),(2,2)] in ShiftedPrimedTableaux() - False - sage: [1,1,1] in ShiftedPrimedTableaux() - True - TESTS:: - sage: 1 in ShiftedPrimedTableaux() - True - sage: [] in ShiftedPrimedTableaux() + sage: [1,1,2] in ShiftedPrimedTableaux() True + sage: (2,1) in ShiftedPrimedTableaux() + False """ try: - self.element_class(self, T) + self.element_class(self, T, skew=self._skew) return True except ValueError: return False @@ -1152,10 +1145,8 @@ def __contains__(self, T): class ShiftedPrimedTableaux_all(ShiftedPrimedTableaux): """ - The class of all Shifted Primed Tableaux. + The class of all shifted primed tableaux. """ - Element = ShiftedPrimedTableau - def __init__(self, skew=None): """ Initialize the class of all shifted tableaux. @@ -1216,8 +1207,7 @@ def _element_constructor_(self, T): try: return self.element_class(self, T, skew=self._skew) except ValueError: - raise ValueError( - "{} is not an element of {}".format(T, self)) + raise ValueError("{} is not an element of {}".format(T, self)) def __contains__(self, T): """ @@ -1236,14 +1226,14 @@ def __contains__(self, T): class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): - """ - Shifted Primed Tableaux of a fixed shape. + r""" + Shifted primed tableaux of a fixed shape. Shifted primed tableaux admit a type `A_n` classical crystal structure with highest weights corresponding to a given shape. The list of module generators consists of all elements of the - crystal with nonincreasing weight. + crystal with strictly decreasing weight entries. The crystal is constructed following operations described in [HPS2017]_. @@ -1282,14 +1272,11 @@ def __classcall_private__(cls, shape, max_entry=None, skew=None): """ shape = _Partitions(shape) return (super(ShiftedPrimedTableaux_shape, cls) - .__classcall__(cls, shape=shape, max_entry=max_entry, - skew=skew)) - - Element = ShiftedPrimedTableau + .__classcall__(cls, shape=shape, max_entry=max_entry, skew=skew)) def __init__(self, shape, max_entry, skew): """ - Initialize the class of Shifted Primed Tableaux of a given shape. + Initialize the class of shifted primed tableaux of a given shape. If ``max_elt`` is specified, a finite set with entries smaller or equal to ``max_elt``. @@ -1349,8 +1336,7 @@ def __contains__(self, T): if self._max_entry is None: return (Tab.shape() == self._shape) - return (Tab.shape() == self._shape and - Tab.max_entry() <= self._max_entry) + return (Tab.shape() == self._shape and Tab.max_entry() <= self._max_entry) def _element_constructor_(self, T): """ @@ -1380,15 +1366,13 @@ def _element_constructor_(self, T): try: Tab = self.element_class(self, T, skew=self._skew) except ValueError: - raise ValueError( - "{} is not an element of {}".format(T, self)) + raise ValueError("{} is not an element of {}".format(T, self)) if self._max_entry is None: if Tab.shape() == self._shape: return Tab else: - if (Tab.shape() == self._shape and - Tab.max_entry() <= self._max_entry): + if (Tab.shape() == self._shape and Tab.max_entry() <= self._max_entry): return Tab raise ValueError("{} is not an element of {}".format(T, self)) @@ -1412,12 +1396,10 @@ def module_generators(self): max_entry = sum(self._shape) else: max_entry = self._max_entry - for weight in (Partition(self._shape) - .dominated_partitions(rows=max_entry)): + for weight in (Partition(self._shape).dominated_partitions(rows=max_entry)): list_dw.extend([self(T) - for T in ShiftedPrimedTableaux( - weight=tuple(weight), - shape=self._shape)]) + for T in ShiftedPrimedTableaux(weight=tuple(weight), + shape=self._shape)]) return tuple(list_dw) def shape(self): @@ -1461,31 +1443,29 @@ def __iter__(self): for weight in OrderedPartitions(sum(self._shape)+self._max_entry, k=self._max_entry): weight_n = tuple([w-1 for w in weight]) - for tab in ShiftedPrimedTableaux(shape=self._shape, - weight=weight_n): + for tab in ShiftedPrimedTableaux(shape=self._shape, weight=weight_n): yield self(tab) class ShiftedPrimedTableaux_weight(ShiftedPrimedTableaux): """ - Shifted Primed Tableaux of fixed weight. + Shifted primed tableaux of fixed weight. TESTS:: - sage: ShiftedPrimedTableaux((2,3,1)) + sage: ShiftedPrimedTableaux(weight=(2,3,1)) Shifted Primed Tableaux of weight (2, 3, 1) - sage: ShiftedPrimedTableaux((2,3,1)).cardinality() + sage: ShiftedPrimedTableaux(weight=(2,3,1)).cardinality() 17 """ - Element = ShiftedPrimedTableau def __init__(self, weight, skew=None): """ - Initialize the class of Shifted Primed Tableaux of a given weight. + Initialize the class of shifted primed tableaux of a given weight. TESTS:: - sage: TestSuite( ShiftedPrimedTableaux((3,2,1)) ).run() + sage: TestSuite( ShiftedPrimedTableaux(weight=(3,2,1)) ).run() """ Parent.__init__(self, category=FiniteEnumeratedSets()) self._weight = weight @@ -1497,7 +1477,7 @@ def _repr_(self): TESTS:: - sage: ShiftedPrimedTableaux((3,2,1)) + sage: ShiftedPrimedTableaux(weight=(3,2,1)) Shifted Primed Tableaux of weight (3, 2, 1) """ if self._skew is None: @@ -1509,7 +1489,7 @@ def __contains__(self, T): """ TESTS:: - sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux((1,4,1)) + sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux(weight=(1,4,1)) True """ try: @@ -1534,7 +1514,7 @@ def _element_constructor_(self, T): TESTS:: - sage: tab= ShiftedPrimedTableaux((2,1))([1,1,1.5]); tab + sage: tab= ShiftedPrimedTableaux(weight=(2,1))([1,1,1.5]); tab [(1.0, 1.0, 1.5)] sage: tab.parent() Shifted Primed Tableaux of weight (2, 1) @@ -1542,8 +1522,7 @@ def _element_constructor_(self, T): try: Tab = self.element_class(self, T, skew=self._skew) except ValueError: - raise ValueError( - "{} is not an element of {}".format(T, self)) + raise ValueError("{} is not an element of {}".format(T, self)) if Tab.weight() == self._weight: return Tab @@ -1555,7 +1534,7 @@ def __iter__(self): EXAMPLES:: - sage: Tabs = ShiftedPrimedTableaux((2,3)) + sage: Tabs = ShiftedPrimedTableaux(weight=(2,3)) sage: Tabs[:4] [[(1.0, 1.0, 2.0, 2.0, 2.0)], [(1.0, 1.0, 1.5, 2.0, 2.0)], @@ -1573,25 +1552,23 @@ def __iter__(self): class ShiftedPrimedTableaux_weight_shape(ShiftedPrimedTableaux): """ - Shifted Primed Tableaux of the fixed weight and shape. + Shifted primed tableaux of the fixed weight and shape. TESTS:: - sage: ShiftedPrimedTableaux((2,3,2),[4,2,1]) + sage: ShiftedPrimedTableaux([4,2,1], weight=(2,3,2)) Shifted Primed Tableaux of weight (2, 3, 2) and shape [4, 2, 1] - sage: ShiftedPrimedTableaux((2,3,2), [4,2,1]).cardinality() + sage: ShiftedPrimedTableaux([4,2,1], weight=(2,3,2)).cardinality() 4 """ - Element = ShiftedPrimedTableau - def __init__(self, weight, shape, skew=None): """ - Initialize the class of Shifted Primed Tableaux of the given weight + Initialize the class of shifted primed tableaux of the given weight and shape. TESTS:: - sage: TestSuite( ShiftedPrimedTableaux((3,2,1)) ).run() + sage: TestSuite( ShiftedPrimedTableaux([4,2,1], weight=(3,2,2)) ).run() """ Parent.__init__(self, category=FiniteEnumeratedSets()) self._weight = weight @@ -1607,19 +1584,18 @@ def _repr_(self): TESTS:: - sage: ShiftedPrimedTableaux([3,2,1],(4,2)) + sage: ShiftedPrimedTableaux([3,2,1], weight=(4,2)) Shifted Primed Tableaux of weight (4, 2) and shape [3, 2, 1] """ - return ( - "Shifted Primed Tableaux of weight {} and shape {}" - .format(self._weight, self._shape)) + return ("Shifted Primed Tableaux of weight {} and shape {}" + .format(self._weight, self._shape)) def __contains__(self, T): """ TESTS:: sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux( - ....: [4,2],(1,4,1)) + ....: [4,2], weight=(1,4,1)) True """ try: @@ -1636,11 +1612,11 @@ def _element_constructor_(self, T): TESTS:: - sage: tab= ShiftedPrimedTableaux([3],(2,1))([1,1,1.5]); tab + sage: tab= ShiftedPrimedTableaux([3], weight=(2,1))([1,1,1.5]); tab [(1.0, 1.0, 1.5)] sage: tab.parent() Shifted Primed Tableaux of weight (2, 1) and shape [3] - sage: ShiftedPrimedTableaux([3],(2,1))([1,1]) + sage: ShiftedPrimedTableaux([3], weight=(2,1))([1,1]) Traceback (most recent call last): ... ValueError: [1, 1] is not an element of Shifted Primed Tableaux @@ -1649,8 +1625,7 @@ def _element_constructor_(self, T): try: Tab = self.element_class(self, T, skew=self._skew) except ValueError: - raise ValueError( - "{} is not an element of {}".format(T, self)) + raise ValueError("{} is not an element of {}".format(T, self)) if Tab.shape() == self._shape and Tab.weight() == self._weight: return Tab @@ -1663,7 +1638,7 @@ def __iter__(self): EXAMPLES:: - sage: Tabs = ShiftedPrimedTableaux([3,2], (1,2,2)) + sage: Tabs = ShiftedPrimedTableaux([3,2], weight=(1,2,2)) sage: Tabs[:4] [[(1.0, 2.0, 2.0), (3.0, 3.0)], [(1.0, 1.5, 2.5), (2.0, 3.0)], @@ -1671,14 +1646,19 @@ def __iter__(self): [(1.0, 1.5, 2.0), (3.0, 3.0)]] sage: len(list(Tabs)) 4 + + TEST:: + + sage: Tabs = ShiftedPrimedTableaux([3,2], weight=(1,4)) + sage: list(Tabs) + [] """ if self._skew is not None: raise NotImplementedError('skew tableau must be empty') if not self._shape.dominates( - Partition(sorted(list(self._weight), key=int, reverse=True))): + Partition(sorted(list(self._weight), reverse=True))): return - yield full_shape = self._shape sub_tab = [] tab_list_new = [[]] @@ -1713,12 +1693,12 @@ def __iter__(self): def _add_strip(sub_tab, full_tab, length): """ - Helper function used in the algorithm to generate all Shifted Primed - Tableaux of the fixed weight and shape. + Helper function used in the algorithm to generate all shifted primed + tableaux of the fixed weight and shape. TESTS:: - sage: list(ShiftedPrimedTableaux([3,1],(2,2))) # indirect doctest + sage: list(ShiftedPrimedTableaux([3,1], weight=(2,2))) # indirect doctest [[(1.0, 1.0, 2.0), (2.0,)], [(1.0, 1.0, 1.5), (2.0,)]] """ if sum(sub_tab)+length > sum(full_tab): From 3c5d53c1f537acf545a0c6579feef1cff7cd541a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Mon, 15 Jan 2018 12:57:34 +1300 Subject: [PATCH 468/740] Rebase MPL-2.1.0 upgrade branch --- .../backports_functools_lru_cache/SPKG.txt | 5 ++ .../checksums.ini | 4 ++ .../dependencies | 5 ++ .../package-version.txt | 1 + .../spkg-install | 3 + build/pkgs/backports_functools_lru_cache/type | 1 + build/pkgs/matplotlib/checksums.ini | 6 +- build/pkgs/matplotlib/dependencies | 2 +- build/pkgs/matplotlib/package-version.txt | 2 +- build/pkgs/matplotlib/spkg-src | 6 +- build/pkgs/setuptools_scm/checksums.ini | 6 +- build/pkgs/setuptools_scm/package-version.txt | 2 +- build/pkgs/subprocess32/SPKG.txt | 5 ++ build/pkgs/subprocess32/checksums.ini | 4 ++ build/pkgs/subprocess32/dependencies | 5 ++ build/pkgs/subprocess32/package-version.txt | 1 + build/pkgs/subprocess32/spkg-install | 3 + build/pkgs/subprocess32/type | 1 + .../introduction.rst | 2 +- src/sage/all.py | 2 - src/sage/plot/arc.py | 18 +++++- src/sage/plot/arrow.py | 12 ++-- src/sage/plot/contour_plot.py | 12 ++-- src/sage/plot/plot.py | 12 +++- src/sage/plot/plot3d/list_plot3d.py | 55 +++++++++---------- .../probability/probability_distribution.pyx | 2 +- 26 files changed, 111 insertions(+), 66 deletions(-) create mode 100644 build/pkgs/backports_functools_lru_cache/SPKG.txt create mode 100644 build/pkgs/backports_functools_lru_cache/checksums.ini create mode 100644 build/pkgs/backports_functools_lru_cache/dependencies create mode 100644 build/pkgs/backports_functools_lru_cache/package-version.txt create mode 100644 build/pkgs/backports_functools_lru_cache/spkg-install create mode 100644 build/pkgs/backports_functools_lru_cache/type create mode 100644 build/pkgs/subprocess32/SPKG.txt create mode 100644 build/pkgs/subprocess32/checksums.ini create mode 100644 build/pkgs/subprocess32/dependencies create mode 100644 build/pkgs/subprocess32/package-version.txt create mode 100644 build/pkgs/subprocess32/spkg-install create mode 100644 build/pkgs/subprocess32/type diff --git a/build/pkgs/backports_functools_lru_cache/SPKG.txt b/build/pkgs/backports_functools_lru_cache/SPKG.txt new file mode 100644 index 00000000000..ecee62e1137 --- /dev/null +++ b/build/pkgs/backports_functools_lru_cache/SPKG.txt @@ -0,0 +1,5 @@ += backports.functools_lru_cache = + +== Description == + +A backport of functools.lru_cache from Python 3.3 as published at ActiveState. diff --git a/build/pkgs/backports_functools_lru_cache/checksums.ini b/build/pkgs/backports_functools_lru_cache/checksums.ini new file mode 100644 index 00000000000..9b49ffbe93d --- /dev/null +++ b/build/pkgs/backports_functools_lru_cache/checksums.ini @@ -0,0 +1,4 @@ +tarball=backports.functools_lru_cache-VERSION.tar.gz +sha1=8a546e7887e961c2873c9b053f4e2cd2a96bd71d +md5=b954e7d5e2ca0f0f66ad2ed12ba800e5 +cksum=2013684405 diff --git a/build/pkgs/backports_functools_lru_cache/dependencies b/build/pkgs/backports_functools_lru_cache/dependencies new file mode 100644 index 00000000000..84580f94937 --- /dev/null +++ b/build/pkgs/backports_functools_lru_cache/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | setuptools_scm pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/backports_functools_lru_cache/package-version.txt b/build/pkgs/backports_functools_lru_cache/package-version.txt new file mode 100644 index 00000000000..c068b2447cc --- /dev/null +++ b/build/pkgs/backports_functools_lru_cache/package-version.txt @@ -0,0 +1 @@ +1.4 diff --git a/build/pkgs/backports_functools_lru_cache/spkg-install b/build/pkgs/backports_functools_lru_cache/spkg-install new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/backports_functools_lru_cache/spkg-install @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/backports_functools_lru_cache/type b/build/pkgs/backports_functools_lru_cache/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/backports_functools_lru_cache/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/matplotlib/checksums.ini b/build/pkgs/matplotlib/checksums.ini index a1da207683e..ff8a8f84834 100644 --- a/build/pkgs/matplotlib/checksums.ini +++ b/build/pkgs/matplotlib/checksums.ini @@ -1,4 +1,4 @@ tarball=matplotlib-VERSION.tar.bz2 -sha1=6c266bb2c5b3f895677b9bd2041245befe44b4e8 -md5=c09cd4c3b07662908e634ffe560fa259 -cksum=1900854975 +sha1=4fa346a0ea9a9c2b7e59045c5947230c07067e03 +md5=9583848157456284620f78277b531f9f +cksum=1698365803 diff --git a/build/pkgs/matplotlib/dependencies b/build/pkgs/matplotlib/dependencies index b9b70d80b49..d5db24571a0 100644 --- a/build/pkgs/matplotlib/dependencies +++ b/build/pkgs/matplotlib/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) numpy freetype libpng dateutil pyparsing tornado six cycler | setuptools pip pytz +$(PYTHON) numpy freetype libpng dateutil pyparsing tornado six cycler | setuptools pip pytz functools32 backports_functools_lru_cache subprocess32 ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/matplotlib/package-version.txt b/build/pkgs/matplotlib/package-version.txt index 343372e95da..7ec1d6db408 100644 --- a/build/pkgs/matplotlib/package-version.txt +++ b/build/pkgs/matplotlib/package-version.txt @@ -1 +1 @@ -1.5.1.p0 +2.1.0 diff --git a/build/pkgs/matplotlib/spkg-src b/build/pkgs/matplotlib/spkg-src index 1f6e094160a..8ad76b7c268 100755 --- a/build/pkgs/matplotlib/spkg-src +++ b/build/pkgs/matplotlib/spkg-src @@ -7,8 +7,8 @@ set -e # Set the version and its download URL. -VERSION=1.5.1 -DOWNLOAD_URL="https://pypi.python.org/packages/source/m/matplotlib/matplotlib-${VERSION}.tar.gz" +VERSION=2.1.0 +DOWNLOAD_URL="https://github.com/matplotlib/matplotlib/archive/v${VERSION}.tar.gz" # fetch and unpack latest version tar xzf <( curl -L ${DOWNLOAD_URL} ) @@ -25,5 +25,5 @@ rm -rf matplotlib-${VERSION} # update package info echo "${VERSION}" > "${SAGE_ROOT}/build/pkgs/matplotlib/package-version.txt" -"$SAGE_ROOT"/sage -sh 'sage-fix-pkg-checksums' +"$SAGE_ROOT"/sage --package fix-checksum matplotlib diff --git a/build/pkgs/setuptools_scm/checksums.ini b/build/pkgs/setuptools_scm/checksums.ini index c53fb7935c6..77d0a199425 100644 --- a/build/pkgs/setuptools_scm/checksums.ini +++ b/build/pkgs/setuptools_scm/checksums.ini @@ -1,4 +1,4 @@ tarball=setuptools_scm-VERSION.tar.gz -sha1=3caea1245d704727ce8a651cb25fbf1b64da5a0b -md5=4d19b2bc9580016d991f665ac20e2e8f -cksum=1244743244 +sha1=cce778e7e9aa2c2f2bc96ae621f671e6d21b019d +md5=f17493d53f0d842bb0152f214775640b +cksum=1736410138 diff --git a/build/pkgs/setuptools_scm/package-version.txt b/build/pkgs/setuptools_scm/package-version.txt index 34abf6a9292..04cc99945d2 100644 --- a/build/pkgs/setuptools_scm/package-version.txt +++ b/build/pkgs/setuptools_scm/package-version.txt @@ -1 +1 @@ -1.11.1.p0 +1.15.6 diff --git a/build/pkgs/subprocess32/SPKG.txt b/build/pkgs/subprocess32/SPKG.txt new file mode 100644 index 00000000000..0bacc96c8a1 --- /dev/null +++ b/build/pkgs/subprocess32/SPKG.txt @@ -0,0 +1,5 @@ += subprocess32 = + +== Description == + +A backport of the subprocess module from Python 3 for use on 2.x diff --git a/build/pkgs/subprocess32/checksums.ini b/build/pkgs/subprocess32/checksums.ini new file mode 100644 index 00000000000..b01fa9d0f86 --- /dev/null +++ b/build/pkgs/subprocess32/checksums.ini @@ -0,0 +1,4 @@ +tarball=subprocess32-VERSION.tar.gz +sha1=75a8664ba54663016315dae17510af97c5a96953 +md5=824c801e479d3e916879aae3e9c15e16 +cksum=270770299 diff --git a/build/pkgs/subprocess32/dependencies b/build/pkgs/subprocess32/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/subprocess32/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/subprocess32/package-version.txt b/build/pkgs/subprocess32/package-version.txt new file mode 100644 index 00000000000..406ebcbd95f --- /dev/null +++ b/build/pkgs/subprocess32/package-version.txt @@ -0,0 +1 @@ +3.2.7 diff --git a/build/pkgs/subprocess32/spkg-install b/build/pkgs/subprocess32/spkg-install new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/subprocess32/spkg-install @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/subprocess32/type b/build/pkgs/subprocess32/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/subprocess32/type @@ -0,0 +1 @@ +standard diff --git a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/introduction.rst b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/introduction.rst index 840cf6cec7d..28157494b86 100644 --- a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/introduction.rst +++ b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/introduction.rst @@ -74,7 +74,7 @@ And, this example draws a similar 3d plot:: sage: import warnings sage: warnings.simplefilter('ignore', UserWarning) sage: v = [[len(factor(n*m)) for n in [1..15]] for m in [1..15]] - sage: list_plot3d(v, interpolation_type='nn') + sage: list_plot3d(v, interpolation_type='clough') Graphics3d Object diff --git a/src/sage/all.py b/src/sage/all.py index 300f5194dd5..19bc8ecba69 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -313,8 +313,6 @@ def _write_started_file(): # Ignore all deprecations from IPython etc. warnings.filterwarnings('ignore', module='.*(IPython|ipykernel|jupyter_client|jupyter_core|nbformat|notebook|ipywidgets|storemagic)') -# Ignore warnings due to matplotlib-1.5 together with numpy-1.13 -warnings.filterwarnings('ignore', module='matplotlib[.]contour|numpy[.]ma[.]core') # However, be sure to keep OUR deprecation warnings warnings.filterwarnings('default', '[\s\S]*See http://trac.sagemath.org/[0-9]* for details.') diff --git a/src/sage/plot/arc.py b/src/sage/plot/arc.py index d18e7a7b90c..3b9834ef5f6 100644 --- a/src/sage/plot/arc.py +++ b/src/sage/plot/arc.py @@ -297,16 +297,28 @@ def bezier_path(self): sage: a = arc((0,0),2,1,0,(pi/5,pi/2+pi/12), linestyle="--", color="red") sage: b = a[0].bezier_path() sage: b[0] - Bezier path from (1.618..., 0.5877...) to (-0.5176..., 0.9659...) + Bezier path from (1.133..., 0.8237...) to (-0.2655..., 0.9911...) """ from sage.plot.bezier_path import BezierPath from sage.plot.graphics import Graphics + from matplotlib.path import Path + import numpy as np ma = self._matplotlib_arc() - transform = ma.get_transform().get_matrix() + def theta_stretch(theta, scale): + theta = np.deg2rad(theta) + x = np.cos(theta) + y = np.sin(theta) + return np.rad2deg(np.arctan2(scale * y, x)) + theta1 = theta_stretch(ma.theta1, ma.width / ma.height) + theta2 = theta_stretch(ma.theta2, ma.width / ma.height) + + pa = ma + pa._path = Path.arc(theta1, theta2) + transform = pa.get_transform().get_matrix() cA, cC, cE = transform[0] cB, cD, cF = transform[1] points = [] - for u in ma._path.vertices: + for u in pa._path.vertices: x, y = list(u) points += [(cA * x + cC * y + cE, cB * x + cD * y + cF)] cutlist = [points[0: 4]] diff --git a/src/sage/plot/arrow.py b/src/sage/plot/arrow.py index 98d6224accb..f8d1e033f7f 100644 --- a/src/sage/plot/arrow.py +++ b/src/sage/plot/arrow.py @@ -147,8 +147,8 @@ def _render_on_subplot(self, subplot): bpath = Path(self.vertices, self.codes) p = FancyArrowPatch(path=bpath, lw=width, arrowstyle='%s,head_width=%s,head_length=%s' % (style, head_width, head_length), - fc=color, ec=color) - p.set_linestyle(get_matplotlib_linestyle(options['linestyle'], return_type='long')) + fc=color, ec=color, + linestyle=get_matplotlib_linestyle(options['linestyle'], return_type='long')) p.set_zorder(options['zorder']) p.set_label(options['legend_label']) subplot.add_patch(p) @@ -336,7 +336,7 @@ def _render_on_subplot(self, subplot): this into account. See :trac:`12836`:: sage: fig = Graphics().matplotlib() - sage: sp = fig.add_subplot(1,1,1) + sage: sp = fig.add_subplot(1,1,1, label='axis1') sage: a = arrow((0,0), (1,1)) sage: b = arrow((0,0), (1,1), width=20) sage: p1 = a[0]._render_on_subplot(sp) @@ -383,8 +383,8 @@ def _render_on_subplot(self, subplot): lw=width, arrowstyle='%s,head_width=%s,head_length=%s' % (style, head_width, head_length), shrinkA=arrowshorten_end, shrinkB=arrowshorten_end, - fc=color, ec=color) - p.set_linestyle(get_matplotlib_linestyle(options['linestyle'], return_type='long')) + fc=color, ec=color, + linestyle=get_matplotlib_linestyle(options['linestyle'], return_type='long')) p.set_zorder(options['zorder']) p.set_label(options['legend_label']) @@ -437,7 +437,7 @@ def draw_path(self, renderer, gc, tpath, affine, rgbFace): pe1.draw_path(renderer, gc, tpath, affine, rgbFace) pe1 = ConditionalStroke(CheckNthSubPath(p, 0), [pe.Stroke()]) - pe2 = ConditionalStroke(CheckNthSubPath(p, 1), [pe.Stroke(linestyle="solid")]) + pe2 = ConditionalStroke(CheckNthSubPath(p, 1), [pe.Stroke(dashes={'dash_offset': 0, 'dash_list': None})]) p.set_path_effects([pe1, pe2]) subplot.add_patch(p) diff --git a/src/sage/plot/contour_plot.py b/src/sage/plot/contour_plot.py index 76077e323b8..efc1f2b2ee9 100644 --- a/src/sage/plot/contour_plot.py +++ b/src/sage/plot/contour_plot.py @@ -178,12 +178,10 @@ def _render_on_subplot(self, subplot): if fill: if contours is None: CSF = subplot.contourf(self.xy_data_array, cmap=cmap, - extent=(x0, x1, y0, y1), - label=options['legend_label']) + extent=(x0, x1, y0, y1)) else: CSF = subplot.contourf(self.xy_data_array, contours, cmap=cmap, - extent=(x0, x1, y0, y1), extend='both', - label=options['legend_label']) + extent=(x0, x1, y0, y1), extend='both') linewidths = options.get('linewidths', None) if isinstance(linewidths, (int, Integer)): @@ -200,13 +198,11 @@ def _render_on_subplot(self, subplot): if contours is None: CS = subplot.contour(self.xy_data_array, cmap=cmap, extent=(x0, x1, y0, y1), - linewidths=linewidths, linestyles=linestyles, - label=options['legend_label']) + linewidths=linewidths, linestyles=linestyles) else: CS = subplot.contour(self.xy_data_array, contours, cmap=cmap, extent=(x0, x1, y0, y1), - linewidths=linewidths, linestyles=linestyles, - label=options['legend_label']) + linewidths=linewidths, linestyles=linestyles) if options.get('labels', False): label_options = options['label_options'] label_options['fontsize'] = int(label_options['fontsize']) diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index 43229875ace..a599f2d3533 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -478,7 +478,7 @@ def f(x): return (x-3)*(x-5)*(x-7)+40 :: - sage: plt.imshow([[(0,0,0)]]) + sage: plt.imshow([[(0.0,0.0,0.0)]]) sage: plt.savefig(os.path.join(SAGE_TMP, 'foo.png')) @@ -2908,11 +2908,14 @@ def list_plot(data, plotjoined=False, **kwargs): If ``plotjoined`` is ``False`` then the axis that is in log scale must have all points strictly positive. For instance, the following plot will show no points in the figure since the points in the - horizontal axis starts from `(0,1)`. + horizontal axis starts from `(0,1)`. Further, matplotlib will display + a user warning. :: sage: list_plot(yl, scale='loglog') # both axes are log + doctest:warning + ... Graphics object consisting of 1 graphics primitive Instead this will work. We drop the point `(0,1)`.:: @@ -3288,12 +3291,15 @@ def list_plot_semilogy(data, plotjoined=False, **kwds): If ``plotjoined`` is ``False`` then the vertical axis must have all points strictly positive. Otherwise the plot will come up empty. - For instance the following plot contains a point at `(1,0)`. + For instance the following plot contains a point at `(1,0)`. Further, + matplotlib will display a user warning. :: sage: xl = [2**k for k in range(12)]; yl = range(len(xl)) sage: list_plot_semilogy(list(zip(xl,yl))) # plot empty due to (1,0) + doctest:warning + ... Graphics object consisting of 1 graphics primitive We remove `(1,0)` to fix this.:: diff --git a/src/sage/plot/plot3d/list_plot3d.py b/src/sage/plot/plot3d/list_plot3d.py index 50a4b9196ea..b1000db3c17 100644 --- a/src/sage/plot/plot3d/list_plot3d.py +++ b/src/sage/plot/plot3d/list_plot3d.py @@ -29,21 +29,20 @@ def list_plot3d(v, interpolation_type='default', texture="automatic", point_list OPTIONAL KEYWORDS: - - ``interpolation_type`` - 'linear', 'nn' (natural neighbor), 'spline' + - ``interpolation_type`` - 'linear', 'clough' (CloughTocher2D), 'spline' 'linear' will perform linear interpolation - The option 'nn' An interpolation method for multivariate data in a - Delaunay triangulation. The value for an interpolation point is - estimated using weighted values of the closest surrounding points in - the triangulation. These points, the natural neighbors, are the ones - the interpolation point would connect to if inserted into the - triangulation. + The option 'clough' will interpolate by using a piecewise cubic interpolating + Bezier polynomial on each triangle, using a Clough-Tocher scheme. + The interpolant is guaranteed to be continuously differentiable. + The gradients of the interpolant are chosen so that the curvature of the + interpolating surface is approximatively minimized. The option 'spline' interpolates using a bivariate B-spline. When v is a matrix the default is to use linear interpolation, when - v is a list of points the default is nearest neighbor. + v is a list of points the default is 'clough'. - ``degree`` - an integer between 1 and 5, controls the degree of spline used for spline interpolation. For data that is highly oscillatory @@ -93,14 +92,14 @@ def list_plot3d(v, interpolation_type='default', texture="automatic", point_list sage: import warnings sage: warnings.simplefilter('ignore', UserWarning) - sage: list_plot3d(m, texture='yellow', interpolation_type='nn', frame_aspect_ratio=[1, 1, 1/3]) + sage: list_plot3d(m, texture='yellow', interpolation_type='clough', frame_aspect_ratio=[1, 1, 1/3]) Graphics3d Object We can make this look better by increasing the number of samples. :: - sage: list_plot3d(m, texture='yellow', interpolation_type='nn', frame_aspect_ratio=[1, 1, 1/3], num_points=40) + sage: list_plot3d(m, texture='yellow', interpolation_type='clough', frame_aspect_ratio=[1, 1, 1/3], num_points=40) Graphics3d Object Let's try a spline. @@ -141,7 +140,7 @@ def list_plot3d(v, interpolation_type='default', texture="automatic", point_list sage: for i in range(-5, 5): ....: for j in range(-5, 5): ....: l.append((normalvariate(0, 1), normalvariate(0, 1), normalvariate(0, 1))) - sage: list_plot3d(l, interpolation_type='nn', texture='yellow', num_points=100) + sage: list_plot3d(l, interpolation_type='clough', texture='yellow', num_points=100) Graphics3d Object TESTS: @@ -165,7 +164,7 @@ def list_plot3d(v, interpolation_type='default', texture="automatic", point_list different z coordinates, an exception will be raised:: sage: pts =[(-4/5, -2/5, -2/5), (-4/5, -2/5, 2/5), (-4/5, 2/5, -2/5), (-4/5, 2/5, 2/5), (-2/5, -4/5, -2/5), (-2/5, -4/5, 2/5), (-2/5, -2/5, -4/5), (-2/5, -2/5, 4/5), (-2/5, 2/5, -4/5), (-2/5, 2/5, 4/5), (-2/5, 4/5, -2/5), (-2/5, 4/5, 2/5), (2/5, -4/5, -2/5), (2/5, -4/5, 2/5), (2/5, -2/5, -4/5), (2/5, -2/5, 4/5), (2/5, 2/5, -4/5), (2/5, 2/5, 4/5), (2/5, 4/5, -2/5), (2/5, 4/5, 2/5), (4/5, -2/5, -2/5), (4/5, -2/5, 2/5), (4/5, 2/5, -2/5), (4/5, 2/5, 2/5)] - sage: show(list_plot3d(pts, interpolation_type='nn')) + sage: show(list_plot3d(pts, interpolation_type='clough')) Traceback (most recent call last): ... ValueError: Two points with same x,y coordinates and different z coordinates were given. Interpolation cannot handle this. @@ -173,7 +172,7 @@ def list_plot3d(v, interpolation_type='default', texture="automatic", point_list Additionally we need at least 3 points to do the interpolation:: sage: mat = matrix(RDF, 1, 2, [3.2, 1.550]) - sage: show(list_plot3d(mat, interpolation_type='nn')) + sage: show(list_plot3d(mat, interpolation_type='clough')) Traceback (most recent call last): ... ValueError: We need at least 3 points to perform the interpolation @@ -320,18 +319,18 @@ def list_plot3d_tuples(v, interpolation_type, texture, **kwds): OPTIONAL KEYWORDS: - - ``interpolation_type`` - 'linear', 'nn' (natural neighbor), 'spline' + - ``interpolation_type`` - 'linear', 'clough' (CloughTocher2D), 'spline' 'linear' will perform linear interpolation - The option 'nn' will interpolate by using natural neighbors. The - value for an interpolation point is estimated using weighted values - of the closest surrounding points in the triangulation. + The option 'clough' will interpolate by using a piecewise cubic interpolating + Bezier polynomial on each triangle, using a Clough-Tocher scheme. + The interpolant is guaranteed to be continuously differentiable. The option 'spline' interpolates using a bivariate B-spline. When v is a matrix the default is to use linear interpolation, when - v is a list of points the default is nearest neighbor. + v is a list of points the default is 'clough'. - ``degree`` - an integer between 1 and 5, controls the degree of spline used for spline interpolation. For data that is highly oscillatory @@ -373,7 +372,7 @@ def list_plot3d_tuples(v, interpolation_type, texture, **kwds): sage: list_plot3d([(1, 2, 3), (0, 1, 3), (2, 1, 4), (1, 0, -2)], texture='yellow', num_points=50) Graphics3d Object """ - from matplotlib import tri, delaunay + from matplotlib import tri import numpy import scipy from random import random @@ -439,19 +438,15 @@ def g(x, y): G._set_extra_kwds(kwds) return G - if interpolation_type == 'nn' or interpolation_type =='default': + if interpolation_type == 'clough' or interpolation_type =='default': - T=delaunay.Triangulation(x,y) - f=T.nn_interpolator(z) - f.default_value=0.0 - j=numpy.complex(0,1) - vals=f[ymin:ymax:j*num_points,xmin:xmax:j*num_points] + points=[[x[i],y[i]] for i in range(len(x))] + j = numpy.complex(0, 1) + f = interpolate.CloughTocher2DInterpolator(points,z) from .parametric_surface import ParametricSurface - def g(x,y): - i=round( (x-xmin)/(xmax-xmin)*(num_points-1) ) - j=round( (y-ymin)/(ymax-ymin)*(num_points-1) ) - z=vals[int(j),int(i)] - return (x,y,z) + def g(x, y): + z = f([x, y]) + return (x, y, z) G = ParametricSurface(g, (list(numpy.r_[xmin:xmax:num_points*j]), list(numpy.r_[ymin:ymax:num_points*j])), texture=texture, **kwds) G._set_extra_kwds(kwds) return G diff --git a/src/sage/probability/probability_distribution.pyx b/src/sage/probability/probability_distribution.pyx index 3ce464202a5..f66cd898b90 100644 --- a/src/sage/probability/probability_distribution.pyx +++ b/src/sage/probability/probability_distribution.pyx @@ -118,7 +118,7 @@ cdef class ProbabilityDistribution: sage: P = [0.3, 0.4, 0.3] sage: X = GeneralDiscreteDistribution(P) sage: h, b = X.generate_histogram_data(bins = 10) - sage: h + sage: h # rel tol 1e-08 [1.6299999999999999, 0.0, 0.0, From d109b7593ef7d1e8d3e234d554524e99c7e92b5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Mon, 15 Jan 2018 15:44:38 +1300 Subject: [PATCH 469/740] WIP on inserting matplotlib style sheet as a plot option and defaulting it to classic --- src/sage/plot/graphics.py | 6 ++++++ src/sage/plot/plot.py | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/sage/plot/graphics.py b/src/sage/plot/graphics.py index 3e3c53b31e4..a04a067bc3c 100644 --- a/src/sage/plot/graphics.py +++ b/src/sage/plot/graphics.py @@ -2457,6 +2457,7 @@ def matplotlib(self, filename=None, axes_pad=None, ticks_integer=None, tick_formatter=None, ticks=None, title=None, title_pos=None, base=None, scale=None, + stylesheet='classic', typeset='default'): r""" Return a matplotlib figure object representing the graphic @@ -2526,6 +2527,11 @@ def matplotlib(self, filename=None, if not isinstance(ticks, (list, tuple)): ticks = (ticks, None) + import matplotlib.pyplot as plt + if stylesheet not in plt.style.available: + stylesheet = 'classic' + plt.style.use(stylesheet) + from sage.symbolic.ring import SR if not isinstance(tick_formatter, (list, tuple)): # make sure both formatters typeset or both don't if tick_formatter == "latex" or tick_formatter in SR: diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index a599f2d3533..d782fcfcc3d 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -1011,6 +1011,12 @@ def plot(funcs, *args, **kwds): - ``fillalpha`` - (default: 0.5) How transparent the fill is. A number between 0 and 1. + + MATPLOTLIB STYLE SHEET OPTION: + + - ``stylesheet`` - (Default: classic) Support for loading a full matplotlib style sheet. + Any style sheet listed in ``matplotlib.pyplot.style.available`` is acceptable. If a + non-existing style is provided the default classic is applied. EXAMPLES: From f87cf6c523f38b4b4d1d4a6c467a67db37a2b75f Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Mon, 15 Jan 2018 09:00:51 +0100 Subject: [PATCH 470/740] 24411: adapt to pynac-0.7.14 --- src/sage/functions/gamma.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/functions/gamma.py b/src/sage/functions/gamma.py index fe25c31d2ae..635cc77d6e4 100644 --- a/src/sage/functions/gamma.py +++ b/src/sage/functions/gamma.py @@ -163,7 +163,7 @@ def __init__(self): :meth:`gamma` """ GinacFunction.__init__(self, 'gamma', latex_name=r"\Gamma", - ginac_name='tgamma', + ginac_name='gamma', conversions={'mathematica':'Gamma', 'maple':'GAMMA', 'sympy':'gamma', From 9b90efadd3a8aa3dfc2ecd77c3f8e538f20cf424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Tue, 16 Jan 2018 10:30:02 +1300 Subject: [PATCH 471/740] Set matplotlib options in sphinx_plot so that 3D plots look nice again. --- src/doc/common/conf.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/doc/common/conf.py b/src/doc/common/conf.py index c4dfbf53d77..5acdcdadff8 100644 --- a/src/doc/common/conf.py +++ b/src/doc/common/conf.py @@ -25,6 +25,9 @@ def sphinx_plot(plot): import matplotlib.image as mpimg from sage.misc.temporary_file import tmp_filename import matplotlib.pyplot as plt + import matplotlib as mpl + mpl.rcParams['image.interpolation'] = 'bilinear' + mpl.rcParams['image.resample'] = False if os.environ.get('SAGE_SKIP_PLOT_DIRECTIVE', 'no') != 'yes': fn = tmp_filename(ext=".png") plot.plot().save(fn) From 4005ec3dae5ab697d215d084d1e7bddea7ff110e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Tue, 16 Jan 2018 14:48:26 +1300 Subject: [PATCH 472/740] Fix the arrow matching pattern --- src/sage/plot/arrow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/plot/arrow.py b/src/sage/plot/arrow.py index f8d1e033f7f..e5abceeb9c9 100644 --- a/src/sage/plot/arrow.py +++ b/src/sage/plot/arrow.py @@ -358,7 +358,7 @@ def _render_on_subplot(self, subplot): sage: a.save(filename=filename) sage: with open(filename, 'r') as f: ....: contents = f.read().replace('\n', ' ') - sage: two_stroke_pattern = r'setdash.*stroke.*stroke.*setdash' + sage: two_stroke_pattern = r'setdash.*stroke.*stroke.*setdash.*setdash' sage: import re sage: two_stroke_re = re.compile(two_stroke_pattern) sage: two_stroke_re.search(contents) is None From 3ea7b1646ea138caf8e6b3651509c4711089cea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Tue, 16 Jan 2018 20:12:59 +1300 Subject: [PATCH 473/740] build qhull with cmake instead of custom script. This install can then be used by matplotlib. --- build/pkgs/qhull/dependencies | 6 +++++- build/pkgs/qhull/package-version.txt | 2 +- build/pkgs/qhull/spkg-install | 12 +++++++++--- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/build/pkgs/qhull/dependencies b/build/pkgs/qhull/dependencies index 2f9f3849682..66d6773d907 100644 --- a/build/pkgs/qhull/dependencies +++ b/build/pkgs/qhull/dependencies @@ -1 +1,5 @@ -# no dependencies +| cmake + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/qhull/package-version.txt b/build/pkgs/qhull/package-version.txt index 8f95c627f19..f3d5a78f033 100644 --- a/build/pkgs/qhull/package-version.txt +++ b/build/pkgs/qhull/package-version.txt @@ -1 +1 @@ -2015-src-7.2.0 +2015-src-7.2.0.p1 diff --git a/build/pkgs/qhull/spkg-install b/build/pkgs/qhull/spkg-install index e7d4448452d..ca124506d2e 100644 --- a/build/pkgs/qhull/spkg-install +++ b/build/pkgs/qhull/spkg-install @@ -6,14 +6,20 @@ fi cd src/ -$MAKE -j1 +cmake -DCMAKE_INSTALL_PREFIX="${SAGE_LOCAL}" -DCMAKE_VERBOSE_MAKEFILE=ON + +$MAKE if [ $? -ne 0 ]; then echo "Error building qhull" exit 1 fi -# Note that the Makefile uses DESTDIR for what otherwise would be called "prefix". -$MAKE -j1 install DESTDIR="$SAGE_LOCAL" +# clean old install +rm -rf "${SAGE_LOCAL}"/include/libqhull +rm -rf "${SAGE_LOCAL}"/include/qhull +rm -rf "${SAGE_LOCAL}"/lib/libqhull* + +$MAKE install if [ $? -ne 0 ]; then echo "Error installing qhull" exit 1 From 3a6792ea300002114fc701acfb14ea52f72fe040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Tue, 16 Jan 2018 23:18:59 +1300 Subject: [PATCH 474/740] Setting more parameters in sphinx_plot for better 3D documentation --- src/doc/common/conf.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/doc/common/conf.py b/src/doc/common/conf.py index 5acdcdadff8..97491ad8e1d 100644 --- a/src/doc/common/conf.py +++ b/src/doc/common/conf.py @@ -25,10 +25,13 @@ def sphinx_plot(plot): import matplotlib.image as mpimg from sage.misc.temporary_file import tmp_filename import matplotlib.pyplot as plt - import matplotlib as mpl - mpl.rcParams['image.interpolation'] = 'bilinear' - mpl.rcParams['image.resample'] = False if os.environ.get('SAGE_SKIP_PLOT_DIRECTIVE', 'no') != 'yes': + import matplotlib as mpl + mpl.rcParams['image.interpolation'] = 'bilinear' + mpl.rcParams['image.resample'] = False + mpl.rcParams['figure.figsize'] = [8.0, 6.0] + mpl.rcParams['figure.dpi'] = 80 + mpl.rcParams['savefig.dpi'] = 100 fn = tmp_filename(ext=".png") plot.plot().save(fn) img = mpimg.imread(fn) From 929509edcfe5278337087f280222fc9b7bb10bbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Tue, 16 Jan 2018 23:59:11 +1300 Subject: [PATCH 475/740] use helper scripts in qhull --- build/pkgs/qhull/spkg-install | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/build/pkgs/qhull/spkg-install b/build/pkgs/qhull/spkg-install index ca124506d2e..bd6ad0d7fbf 100644 --- a/build/pkgs/qhull/spkg-install +++ b/build/pkgs/qhull/spkg-install @@ -8,19 +8,11 @@ cd src/ cmake -DCMAKE_INSTALL_PREFIX="${SAGE_LOCAL}" -DCMAKE_VERBOSE_MAKEFILE=ON -$MAKE -if [ $? -ne 0 ]; then - echo "Error building qhull" - exit 1 -fi +sdh_make # clean old install rm -rf "${SAGE_LOCAL}"/include/libqhull rm -rf "${SAGE_LOCAL}"/include/qhull rm -rf "${SAGE_LOCAL}"/lib/libqhull* -$MAKE install -if [ $? -ne 0 ]; then - echo "Error installing qhull" - exit 1 -fi +sdh_make_install From 589e7a2b6012f99542760fac2ddedb034cfbbd13 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Tue, 16 Jan 2018 16:16:48 +0000 Subject: [PATCH 476/740] Slight reworking of the arguments to atomic_write: * Instead of using tempfile.Tempfile we use tmp_filename() (which has the added benefit (?) of creating the temp file in SAGE_TMP) and we use io.open directly. Any additional keywork arguments given are passed through to io.open (this includes encoding, as well as encoding error handling which can now be supported). * Binary mode is on by default in Python 2 and off by default in Python 3-- this enable consistent handling of the 'str' type on both Pythons with the default arguments (naturally there will still be cases where we'll want to specify one or the other explicitly, especially for binary=True on Python 3). --- src/sage/misc/temporary_file.py | 41 ++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/sage/misc/temporary_file.py b/src/sage/misc/temporary_file.py index ad6f0235083..da9d9c1381b 100644 --- a/src/sage/misc/temporary_file.py +++ b/src/sage/misc/temporary_file.py @@ -21,6 +21,7 @@ #***************************************************************************** from __future__ import print_function +import io import locale import os import tempfile @@ -209,7 +210,7 @@ def graphics_filename(ext='.png'): ################################################################# # write to a temporary file and move it in place ################################################################# -class atomic_write: +class atomic_write(object): """ Write to a given file using a temporary file and then rename it to the target file. This renaming should be atomic on modern @@ -236,15 +237,15 @@ class atomic_write: - ``mode`` -- (default: ``0o666``) mode bits for the file. The temporary file is created with mode ``mode & ~umask`` and the resulting file will also have these permissions (unless the - mode bits of the file were changed manually). + mode bits of the file were changed manually). (Not to be confused with + the file opening mode.) - ``binary`` -- (boolean, default: False) the underlying file is opened in binary mode. If False then it is opened in text mode and an encoding with which to write the file may be supplied. - - ``encoding`` -- (str, default: ``locale.getpreferredencoding(False)``) - the encoding with which to write text data when ``binary=False``. - Implies ``binary=False`` if given. + - ``kwargs`` -- additional keyword arguments passed to the underlying + `io.open` call. EXAMPLES:: @@ -349,7 +350,7 @@ class atomic_write: '>>> AAA' """ def __init__(self, target_filename, append=False, mode=0o666, - binary=False, encoding=None): + binary=None, **kwargs): """ TESTS:: @@ -368,10 +369,12 @@ def __init__(self, target_filename, append=False, mode=0o666, # Remove umask bits from mode umask = os.umask(0); os.umask(umask) self.mode = mode & (~umask) - self.binary = binary if encoding is None else False - if not self.binary and encoding is None: - encoding = locale.getpreferredencoding(False) - self.encoding = encoding + + # 'binary' mode is the default on Python 2, whereas 'text' mode is the + # default on Python 3--this reflects consistent handling of the default + # str type on the two platforms + self.binary = six.PY2 if binary is None else binary + self.kwargs = kwargs def __enter__(self): """ @@ -392,21 +395,23 @@ def __enter__(self): True """ + name = tmp_filename() + rmode = 'r' + ('b' if self.binary else '') wmode = 'w+' + ('b' if self.binary else '') - if six.PY2: - encoding_kwargs = {} - else: - encoding_kwargs = {'encoding': self.encoding} - self.tempfile = tempfile.NamedTemporaryFile( - wmode, dir=self.tmpdir, delete=False, **encoding_kwargs) + try: + self.tempfile = io.open(name, wmode, **self.kwargs) + except (TypeError, ValueError): + # Some invalid arguments were passed to io.open + os.unlink(name) + raise - self.tempname = self.tempfile.name + self.tempname = name os.chmod(self.tempname, self.mode) if self.append: try: - with open(self.target, rmode, **encoding_kwargs) as f: + with io.open(self.target, rmode, **self.kwargs) as f: r = f.read() except IOError: pass From e01a3ab0fbd7f40532395063e2cc7e9defd579a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 16 Jan 2018 17:40:01 +0100 Subject: [PATCH 477/740] Require incomparability to fix some issues where extensions could not be separated --- src/sage/rings/padics/padic_valuation.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 0b4bd3b835f..a7e15471873 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -264,7 +264,7 @@ def create_key_and_extra_args_for_number_field_from_valuation(self, R, v, prime, # the one approximated by v. vK = v.restriction(v.domain().base_ring()).extension(K) if approximants is None: - approximants = vK.mac_lane_approximants(G) + approximants = vK.mac_lane_approximants(G, require_incomparability=True) approximants = [approximant.extension(v.domain()) for approximant in approximants] approximant = vK.mac_lane_approximant(G, v, approximants=tuple(approximants)) @@ -298,7 +298,7 @@ def create_key_and_extra_args_for_number_field_from_ideal(self, R, I, prime): if len(F) != 1: raise ValueError("%r does not lie over a single prime of %r"%(I, K)) vK = K.valuation(F[0][0]) - candidates = vK.mac_lane_approximants(G) + candidates = vK.mac_lane_approximants(G, require_incomparability=True) candidates_for_I = [c for c in candidates if all(c(g.polynomial()) > 0 for g in I.gens())] assert(len(candidates_for_I) > 0) # This should not be possible, unless I contains a unit @@ -687,7 +687,7 @@ def _extensions_to_quotient(self, ring, approximants=None): """ from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace parent = DiscretePseudoValuationSpace(ring) - approximants = approximants or self.mac_lane_approximants(ring.modulus().change_ring(self.domain()), assume_squarefree=True) + approximants = approximants or self.mac_lane_approximants(ring.modulus().change_ring(self.domain()), assume_squarefree=True, require_incomparability=True) return [pAdicValuation(ring, approximant, approximants) for approximant in approximants] def extensions(self, ring): @@ -737,6 +737,16 @@ def extensions(self, ring): sage: w(w.uniformizer()) 1/4 + A case where the extensions could not be separated at some point:: + + sage: v = QQ.valuation(2) + sage: R. = QQ[] + sage: F = x^48 + 120*x^45 + 56*x^42 + 108*x^36 + 32*x^33 + 40*x^30 + 48*x^27 + 80*x^24 + 112*x^21 + 96*x^18 + 96*x^15 + 24*x^12 + 96*x^9 + 16*x^6 + 96*x^3 + 68 + sage: L. = QQ.extension(F) + sage: v.extensions(L) + [[ 2-adic valuation, v(x) = 1/24, v(x^24 + 4*x^18 + 10*x^1 2 + 12*x^6 + 8*x^3 + 6) = 29/8 ]-adic valuation, + [ 2-adic valuation, v(x) = 1/24, v(x^24 + 4*x^18 + 2*x^12 + 12*x^6 + 8*x^3 + 6) = 29/8 ]-adic valuation] + """ if self.domain() is ring: return [self] @@ -762,7 +772,7 @@ def extensions(self, ring): if ring.base_ring().fraction_field() is self.domain().fraction_field(): from sage.rings.valuation.valuation_space import DiscretePseudoValuationSpace parent = DiscretePseudoValuationSpace(ring) - approximants = self.mac_lane_approximants(ring.fraction_field().relative_polynomial().change_ring(self.domain()), assume_squarefree=True) + approximants = self.mac_lane_approximants(ring.fraction_field().relative_polynomial().change_ring(self.domain()), assume_squarefree=True, require_incomparability=True) return [pAdicValuation(ring, approximant, approximants) for approximant in approximants] if ring.base_ring() is not ring and self.domain().is_subring(ring.base_ring()): return sum([w.extensions(ring) for w in self.extensions(ring.base_ring())], []) From 21428911816e42073feb06eaa3867b55bfd2e31a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 16 Jan 2018 18:24:43 +0100 Subject: [PATCH 478/740] fix copy & paste mistake --- src/sage/rings/padics/padic_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index a7e15471873..7023a21dcc2 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -744,7 +744,7 @@ def extensions(self, ring): sage: F = x^48 + 120*x^45 + 56*x^42 + 108*x^36 + 32*x^33 + 40*x^30 + 48*x^27 + 80*x^24 + 112*x^21 + 96*x^18 + 96*x^15 + 24*x^12 + 96*x^9 + 16*x^6 + 96*x^3 + 68 sage: L. = QQ.extension(F) sage: v.extensions(L) - [[ 2-adic valuation, v(x) = 1/24, v(x^24 + 4*x^18 + 10*x^1 2 + 12*x^6 + 8*x^3 + 6) = 29/8 ]-adic valuation, + [[ 2-adic valuation, v(x) = 1/24, v(x^24 + 4*x^18 + 10*x^12 + 12*x^6 + 8*x^3 + 6) = 29/8 ]-adic valuation, [ 2-adic valuation, v(x) = 1/24, v(x^24 + 4*x^18 + 2*x^12 + 12*x^6 + 8*x^3 + 6) = 29/8 ]-adic valuation] """ From 7ad95eaececd29f7198a7be267f85312e788c25f Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Tue, 16 Jan 2018 18:50:03 +0100 Subject: [PATCH 479/740] Abelian groups with gap are not independent beings of the sage abelian groups --- src/sage/groups/abelian_gps/abelian_group.py | 21 - .../abelian_gps/abelian_group_element.py | 19 +- .../groups/abelian_gps/abelian_group_gap.py | 424 ++++++++++++------ 3 files changed, 278 insertions(+), 186 deletions(-) diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py index 726803935bd..20ad4f88e97 100644 --- a/src/sage/groups/abelian_gps/abelian_group.py +++ b/src/sage/groups/abelian_gps/abelian_group.py @@ -902,27 +902,6 @@ def _gap_init_(self): return 'AbelianPcpGroup(%s)'%list(self.gens_orders()) raise PackageNotFoundError("gap_packages") - @cached_method - def gap(self): - r""" - Return this abelian group as a libgap group - - EXAMPLES:: - - sage: A = AbelianGroup([2,3,6]) - sage: A.gap() - Multiplicative Abelian group isomorphic to C2 x C3 x C6 with gap - - If the gap package "Polycyclic" is installed, it can handle - infinite cyclic groups as well:: - - sage: A = AbelianGroup([2,0,6]) # optional - gap_packages - sage: A.gap() - Multiplicative Abelian group isomorphic to C2 x Z x C6 with gap - """ - from sage.groups.abelian_gps.abelian_group_gap import AbelianGroup_gap - return AbelianGroup_gap(self) - def gen(self, i=0): """ The `i`-th generator of the abelian group. diff --git a/src/sage/groups/abelian_gps/abelian_group_element.py b/src/sage/groups/abelian_gps/abelian_group_element.py index 38209961fb4..81ef643c9f9 100644 --- a/src/sage/groups/abelian_gps/abelian_group_element.py +++ b/src/sage/groups/abelian_gps/abelian_group_element.py @@ -163,21 +163,4 @@ def word_problem(self, words): True """ from sage.groups.abelian_gps.abelian_group import AbelianGroup, word_problem - return word_problem(words,self) - - def gap(self): - r""" - Push this element to the corresponding gap based group. - - Note that the notation of elements might change. - - EXAMPLES:: - - sage: A = AbelianGroup([2,5]) - sage: a = A.an_element() - sage: a.gap() - g1*g2 - sage: a == a.gap().sage() - True - """ - return self.parent().gap()(self) + return word_problem(words,self) \ No newline at end of file diff --git a/src/sage/groups/abelian_gps/abelian_group_gap.py b/src/sage/groups/abelian_gps/abelian_group_gap.py index 4ef372e3b1b..7dab5aac8db 100644 --- a/src/sage/groups/abelian_gps/abelian_group_gap.py +++ b/src/sage/groups/abelian_gps/abelian_group_gap.py @@ -5,10 +5,41 @@ from sage.libs.gap.libgap import libgap from sage.misc.cachefunc import cached_method from sage.rings.integer_ring import ZZ -from sage.arith.all import GCD, LCM from sage.structure.unique_representation import UniqueRepresentation +from sage.categories.groups import Groups - +def AbelianGroupGap(generator_orders): + r""" + Create the multiplicative abelian group with given orders of generators. + + INPUT: + + - ``generator_orders`` -- a list of nonnegative integers where `0` will give a factor isomorphic to `\ZZ`. + + OUTPUT: + + - an abelian group + + EXAMPLES:: + + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: AbelianGroupGap([3,6]) + Abelian group with gap, generator orders (3, 6) + sage: AbelianGroupGap([3,6,5]) + Abelian group with gap, generator orders (3, 6, 5) + sage: AbelianGroupGap([3,6,0]) # optional gap_packages + Abelian group with gap, generator orders (3, 6, 0) + """ + generator_orders = tuple(ZZ(e) for e in generator_orders) + if not all([e >= 0 for e in generator_orders]): + return ValueError("Generator orders must be nonnegative") + category = Groups().Commutative() + if 0 in generator_orders: + category = category.Finite().Enumerated() + else: + category = category.Infinite() + polycyclic_package = libgap.LoadPackage("Polycyclic") + return AbelianGroupAmbient_gap(generator_orders, polycyclic_package=polycyclic_package, category=None) class AbelianGroupElement_gap(ElementLibGAP): r""" @@ -19,26 +50,21 @@ def __init__(self, parent, x, check=True): The Python constructor. See :class:`AbelianGroupElement_gap` for details. + + INPUT: + + - ``parent`` -- an instance of :class:`AbelianGroup_gap` + - ``x`` -- an instance of :class:`sage.libs.gap.element.GapElement` + - ``check`` -- boolean (default: ``True``) check + if ``x`` is an element of the group TESTS:: - sage: A = AbelianGroup([3,6]) - sage: G = A.gap() + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([3,6]) sage: g = G.an_element() sage: TestSuite(g).run() """ - if isinstance(x, AbelianGroupElement_gap): - x = x.gap() - if not isinstance(x, GapElement): - A = parent._A - x = A(x) - # turn this into a gap element - gens_gap = parent.gens() - exp = x.exponents() - x = gens_gap[0]**0 - for i in range(len(exp)): - x *= gens_gap[i]**exp[i] - x = x.gap() if check: if not x in parent.gap(): raise ValueError("%s is not in the group %s" %(x, parent)) @@ -50,8 +76,8 @@ def __hash__(self): EXAMPLES:: - sage: A = AbelianGroup([3,2,4]) - sage: G = A.gap() + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([3,2,4]) sage: g = G.an_element() sage: g.__hash__() # random 1693277541873681615 @@ -64,15 +90,30 @@ def __reduce__(self): EXAMPLES:: - sage: A = AbelianGroup([3,2,4]) - sage: G = A.gap() + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([3,2,4]) sage: g = G.an_element() sage: g == loads(dumps(g)) True sage: g.__reduce__() - (Multiplicative Abelian group isomorphic to C3 x C2 x C4 with gap, (f0*f1*f2,)) + (Abelian group with gap, generator orders (3, 2, 4), ((1, 1, 1),)) + """ + return self.parent(), (self.exponents(),) + + def _repr_(self): """ - return self.parent(), (self.sage(),) + The string representation of this element. + + EXAMPLES:: + + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([3,2,4]) + sage: g = G.an_element() + sage: g._repr_() + 'g1*g2*g3' + """ + rep = self.gap()._repr_() + return rep.replace('f','g') def exponents(self): r""" @@ -80,14 +121,13 @@ def exponents(self): EXAMPLES:: - sage: A = AbelianGroup([4,7,9]) - sage: G = A.gap() + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([4,7,9]) sage: gens = G.gens() sage: g = gens[0]^2 * gens[1]^4 * gens[2]^8 sage: g.exponents() (2, 4, 8) - sage: A = AbelianGroup([4,7,0]) # optional - gap_packages - sage: G = A.gap() + sage: G = AbelianGroupGap([4,7,0]) # optional - gap_packages sage: gens = G.gens() sage: g = gens[0]^2 * gens[1]^4 * gens[2]^8 sage: g.exponents() @@ -120,58 +160,61 @@ def order(self): EXAMPLES:: - sage: G = AbelianGroup([4]).gap() + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([4]) sage: g = G.gens()[0] sage: g.order() 4 - sage: G = AbelianGroup([0]).gap() # optional - gap_packages + sage: G = AbelianGroupGap([0]) # optional - gap_packages sage: g = G.gens()[0] - sage: g.order() + sage: g.order() # optional - gap_packages +Infinity """ return self.gap().Order().sage() - def sage(self): - r""" - Convert this element to the corresponding abelian group in sage. - - EXAMPLES:: - - sage: A = AbelianGroup([2,3,7,4]) - sage: G = A.gap() - sage: all([a == a.gap().sage() for a in A]) - True - sage: all([g == g.sage().gap() for g in G]) - True - """ - P = self.parent() - gens_sage = P._A.gens() - e = P._A.identity() - exp = self.exponents() - for i in range(len(exp)): - e *= gens_sage[i]**exp[i] - return e - -class AbelianGroup_gap(UniqueRepresentation, GroupMixinLibGAP, ParentLibGAP, AbelianGroupBase): +# def sage(self): +# r""" +# Convert this element to the corresponding abelian group in sage. +# +# EXAMPLES:: +# +# sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap +# sage: G = AbelianGroupGap([2,3,7,4]) +# sage: all([a == a.gap().sage() for a in A]) +# True +# sage: all([g == g.sage().gap() for g in G]) +# True +# """ +# P = self.parent() +# gens_sage = P._A.gens() +# e = P._A.identity() +# exp = self.exponents() +# for i in range(len(exp)): +# e *= gens_sage[i]**exp[i] +# return e + +class AbelianGroup_gap(UniqueRepresentation,GroupMixinLibGAP, ParentLibGAP, AbelianGroupBase): r""" - Class for finitely generated abelian groups implemented with gap. + Python wrapper for finitely generated abelian groups in gap. Needs the gap package "Polycyclic" in case the group is infinite. INPUT: - - ``A`` -- :class:`sage.groups.abelian_gps.abelian_group.AbelianGroup_class` - ``G`` -- (default:``None``) a gap group - ``ambient`` -- (default:``None``) an :class:`AbelianGroup_gap` + - ``polycyclic_package`` -- (default: ``False``) boolean + - ``category` -- a category` EXAMPLES:: - sage: A = AbelianGroup([3,2,5]) - sage: G = A.gap() - sage: G - Multiplicative Abelian group isomorphic to C3 x C2 x C5 with gap + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([3,2,5]) + sage: G + Abelian group with gap, generator orders (3, 2, 5) + """ - def __init__(self, A, G=None, ambient=None): + def __init__(self, G, ambient=None, polycyclic_package=False, category=None): r""" Create an instance of this class. @@ -179,49 +222,23 @@ def __init__(self, A, G=None, ambient=None): TESTS:: - sage: A = AbelianGroup([3,2,5]) - sage: G = A.gap() + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([3,2,5]) sage: TestSuite(G).run() """ - AbelianGroupBase.__init__(self, category=A.category()) - self._with_pc = libgap.LoadPackage("Polycyclic") - if G is None: - if self._with_pc: - G = libgap.eval("AbelianPcpGroup(%s)"%list(A.gens_orders())) - else: - G = libgap(A) + self._with_pc = polycyclic_package + AbelianGroupBase.__init__(self, category=category) ParentLibGAP.__init__(self, G, ambient=ambient) - self._A = A Element = AbelianGroupElement_gap - def _latex_(self): - """ - Return the latex representation of this group. - - EXAMPLES:: - - sage: A = AbelianGroup([2,6]) - sage: G = A.gap() - sage: G._latex_() - '$\\mathrm{AbelianGroup}( 2, (2, 6) )$ with gap' - """ - return self._A._latex_() + " with gap" - - def _repr_(self): + def __reduce__(self): r""" - Return the string representation of this group. - - EXAMPLES:: - - sage: A = AbelianGroup([2,6]) - sage: G = A.gap() - sage: G._repr_() - 'Multiplicative Abelian group isomorphic to C2 x C6 with gap' """ - s = self._A._repr_() - s += " with gap" - return s + if self is self.ambient(): + return AbelianGroupGap, (self.gens_orders(),) + else: + return self.ambient().subgroup, (self.gens(),) def _coerce_map_from_(self, S): r""" @@ -237,23 +254,46 @@ def _coerce_map_from_(self, S): EXAMPLES:: - sage: A = AbelianGroup([2,3,4,5]) - sage: G = A.gap() + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([2,3,4,5]) sage: gen = G.gens()[:2] sage: S = G.subgroup(gen) sage: G._coerce_map_from_(S) True sage: S._coerce_map_from_(G) - sage: G._coerce_map_from_(A) - True """ try: if S.ambient() is self: return True except AttributeError: pass - if self._A is S: - return True + + def _element_constructor_(self,x,check=True): + r""" + Defines coercions and conversions. + + INPUT: + + - ``x` -- an element of this group, a gap element ` + """ + if isinstance(x, AbelianGroupElement_gap): + x = x.gap() + elif x == 1: + x = self.gap().Identity() + elif not isinstance(x, GapElement): + try: + exp = x.exponents() + except AttributeError: + exp = tuple(x) + # turn the exponents into a gap element + gens_gap = self.gens() + if len(exp) != len(gens_gap): + raise ValueError("Input does not match the number of generators.") + x = gens_gap[0]**0 + for i in range(len(exp)): + x *= gens_gap[i]**exp[i] + x = x.gap() + return self.element_class(self, x, check=check) def is_trivial(self): r""" @@ -261,9 +301,10 @@ def is_trivial(self): EXAMPLES:: - sage: A = AbelianGroup([]).gap() - sage: A.is_trivial() - True + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([]) + sage: G + Abelian group with gap, generator orders () """ return 1 == self.order() @@ -273,9 +314,10 @@ def identity(self): EXAMPLES: - sage: G = AbelianGroup([4,10]).gap() + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([4,10]) sage: G.identity() - 1 + id """ return self(self.gap().Identity()) @@ -288,8 +330,8 @@ def elementary_divisors(self): EXAMPLES:: - sage: A = AbelianGroup([2,3,4,5]) - sage: G = A.gap() + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([2,3,4,5]) sage: G.elementary_divisors() (2, 60) """ @@ -305,14 +347,13 @@ def exponent(self): EXAMPLES:: - sage: G = AbelianGroup([2,3,7]).gap() - sage: G - Multiplicative Abelian group isomorphic to C2 x C3 x C7 with gap - sage: G.exponent() - 42 - sage: G = AbelianGroup([2,4,6]).gap() + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([2,3,7]) sage: G - Multiplicative Abelian group isomorphic to C2 x C4 x C6 with gap + Abelian group with gap, generator orders (2, 3, 7) + sage: G = AbelianGroupGap([2,4,6]) + sage: G + Abelian group with gap, generator orders (2, 4, 6) sage: G.exponent() 12 """ @@ -332,12 +373,13 @@ def gens_orders(self): EXAMPLES:: - sage: Z2xZ3 = AbelianGroup([2,3]).gap() + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: Z2xZ3 = AbelianGroupGap([2,3]) sage: Z2xZ3.gens_orders() (2, 3) sage: Z2xZ3.elementary_divisors() (6,) - sage: Z6 = AbelianGroup([6]).gap() + sage: Z6 = AbelianGroupGap([6]) sage: Z6.gens_orders() (6,) sage: Z6.elementary_divisors() @@ -347,20 +389,14 @@ def gens_orders(self): sage: Z2xZ3 is Z6 False """ - return tuple(g.order() for g in self.gens()) - - def sage(self): - r""" - Return the sage pendant of this abelian group. - - EXAMPLES:: - - sage: A = AbelianGroup([]) - sage: G = A.gap() - sage: A is G.sage() - True - """ - return self._A + from sage.rings.infinity import Infinity + orders = [] + for g in self.gens(): + order = g.order() + if order == Infinity: + order = 0 + orders.append(order) + return tuple(orders) def subgroup(self, gens): r""" @@ -376,30 +412,23 @@ def subgroup(self, gens): EXAMPLES:: - sage: A = AbelianGroup([2,3,4,5]) - sage: G = A.gap() + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([2,3,4,5]) sage: gen = G.gens()[:2] sage: S = G.subgroup(gen) - sage: S - Multiplicative Abelian subgroup isomorphic to C2 x C3 generated by {f0, f1} with gap + sage: S + Subgroup of Abelian group with gap, generator orders (2, 3, 4, 5) generated by (g1, g2) sage: g = G.an_element() sage: s = S.an_element() - sage: a = A.an_element() sage: g*s g2^2*g3*g4 - sage: g*a - g2^2*g3^2*g4^2 - sage: A = AbelianGroup([3,4,0,2]) - sage: G = A.gap() + sage: G = AbelianGroupGap([3,4,0,2]) # optional - gap_packages sage: gen = G.gens()[:2] sage: S = G.subgroup(gen) sage: g = G.an_element() sage: s = S.an_element() - sage: a = A.an_element() - sage: g*s + sage: g*s # optional - gap_packages g1^2*g2^2*g3*g4 - sage: g*a - g1^2*g2^2*g3^2 TESTS:: @@ -407,8 +436,109 @@ def subgroup(self, gens): sage: h in S False """ - gens_gap = [self(g).gap() for g in gens] - gens_sage = [g.sage() for g in gens] - G = self.gap().Subgroup(gens_gap) - A = self._A.subgroup(gens_sage) - return AbelianGroup_gap(A, G=G, ambient=self) + gens = tuple(self(g) for g in gens) + return AbelianGroupSubgroup_gap(self, gens) + +class AbelianGroupAmbient_gap(AbelianGroup_gap): + r""" + Ambient abelian groups with gap. + + Do not use this class directly. Instead use :function:`AbelianGroupGap`. + Needs the gap package "Polycyclic" in case the group is infinite. + + INPUT: + + - ``generator_orders`` - a tuple of nonnegative integers + - ``polycyclic_package`` -- (default: ``False``) boolean + - ``category` -- a category` + + EXAMPLES:: + + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupAmbient_gap + sage: AbelianGroupAmbient_gap((2,3,4)) + Abelian group with gap, generator orders (2, 3, 4) + """ + def __init__(self, generator_orders, polycyclic_package=False, category=None): + if polycyclic_package: + G = libgap.eval("AbelianPcpGroup(%s)"%list(generator_orders)) + else: + G = libgap.AbelianGroup(generator_orders) + AbelianGroup_gap.__init__(self, G, ambient=None, polycyclic_package=polycyclic_package, category=category) + + def _latex_(self): + """ + Return the latex representation of this group. + + EXAMPLES:: + + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([2,6]) + sage: G._latex_() + 'Abelian group with gap, generator orders $(2, 6)$' + """ + return "Abelian group with gap, generator orders $" + str(self.gens_orders()) + "$" + + def _repr_(self): + r""" + Return the string representation of this group. + + EXAMPLES:: + + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([2,6]) + sage: G._repr_() + 'Abelian group with gap, generator orders (2, 6)' + """ + return "Abelian group with gap, generator orders " + str(self.gens_orders()) + +class AbelianGroupSubgroup_gap(AbelianGroup_gap): + r""" + Subgroups of abelian groups with gap. + + Do not use this class directly. Instead use :meth:`subgroup`. + + INPUT: + + - ``ambient`` -- the ambient group + - ``gens`` -- generators of the subgroup + + EXAMPLES:: + + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([2,3,4,5]) + sage: gen = G.gens()[:2] + sage: S = G.subgroup(gen) # indirect doctest + """ + def __init__(self, ambient, gens): + r""" + Initialize this module + + TESTS:: + + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap,AbelianGroupSubgroup_gap + sage: G = AbelianGroupGap([]) + sage: gen = G.gens() + sage: AbelianGroupSubgroup_gap(G, gen) + Subgroup of Abelian group with gap, generator orders () generated by () + """ + polycyclic_package = ambient._with_pc + category = ambient.category() + gens_gap = tuple(g.gap() for g in gens) + G = ambient.gap().Subgroup(gens_gap) + AbelianGroup_gap.__init__(self, G, ambient=ambient, polycyclic_package=polycyclic_package, category=category) + + def __repr__(self): + r""" + Return the string representation of this subgroup. + + EXAMPLES:: + + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([2,3,4,5]) + sage: gen = G.gens()[:2] + sage: S = G.subgroup(gen) # indirect doctest + sage: S.__repr__() + 'Subgroup of Abelian group with gap, generator orders (2, 3, 4, 5) generated by (g1, g2)' + """ + s = "Subgroup of %s generated by %s"%(self.ambient(),self.gens()) + return s From de04f86ca1fcbe96a14d6b3fc802a9f86d29d197 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Tue, 16 Jan 2018 15:25:05 -0800 Subject: [PATCH 480/740] Added iter --- src/sage/combinat/shifted_primed_tableau.py | 580 +++++++++++--------- 1 file changed, 314 insertions(+), 266 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index 34918ab0768..38fcd2c21bc 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -36,6 +36,7 @@ from sage.categories.regular_crystals import RegularCrystals from sage.categories.classical_crystals import ClassicalCrystals from sage.categories.sets_cat import Sets +from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.combinat.root_system.cartan_type import CartanType @@ -96,7 +97,7 @@ def __classcall_private__(cls, T, skew=None): return T return ShiftedPrimedTableaux(skew=skew)(T) - def __init__(self, parent, T, skew=None): + def __init__(self, parent, T, skew=None, check=True, preprocessed=False): r""" Initialize a shifted tableau. @@ -127,30 +128,22 @@ def __init__(self, parent, T, skew=None): ... TypeError: 'tuple' object does not support item assignment """ - if skew is not None: - if not all(skew[i] > skew[i+1] for i in range(len(skew)-1)): - raise ValueError('skew shape must be a strict partition') - + if not preprocessed: + T = self._preprocess_(T, skew=skew) self._skew = skew + ClonableArray.__init__(self, parent, T, check=check) + @staticmethod + def _preprocess_(T, skew=None): + """ + Preprocessing list ``T`` to initialize the tableau. + """ if isinstance(T, ShiftedPrimedTableau): - ClonableArray.__init__(self, parent, T) - return - - if not isinstance(T, list): - try: - t_ = list(T) - except TypeError: - t_ = [T] - else: - t_ = T - - if not all(isinstance(row, (list, tuple)) for row in t_): - t_ = [t_] + return T t = [] # Preprocessing list t for primes and other symbols - for row in t_: + for row in T: row_new = [] for element in row: @@ -170,10 +163,8 @@ def __init__(self, parent, T, skew=None): continue else: raise ValueError("all numbers must be half-integers") - except (TypeError, ValueError): raise ValueError("primed elements have wrong format") - t.append(row_new) # Accounting for zeros at the beginning and at the end of a row' @@ -192,15 +183,33 @@ def __init__(self, parent, T, skew=None): i += 1 if skew is not None: - t_ = ([(None,)*skew[i] + tuple(t[i]) for i in range(len(skew))] - + [tuple(t[i]) for i in range(len(skew), len(t))]) + T = ([(None,)*skew[i] + tuple(t[i]) + for i in range(len(skew))] + + [tuple(t[i]) for i in range(len(skew), len(t))]) else: - t_ = [tuple(row) for row in t] + T = [tuple(row) for row in t] + return T + + def check(self): + """ + Check that ``self`` is a valid primed tableau. - if not all(len(t_[i]) > len(t_[i+1]) for i in range(len(t_)-1)): - raise ValueError('shape must be a strict partition') + EXAMPLES:: - ClonableArray.__init__(self, parent, t_) + sage: T = ShiftedPrimedTableaux([4,2]) + sage: t = T([[1,'2p',2,2],[2,'3p']]) + sage: t.check() + sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"],[2]],skew=[2,1]) + sage: s.check() + sage: t = T([['1p','2p',2,2],[2,'3p']]) + Traceback (most recent call last): + .... + ValueError: [['1p', '2p', 2, 2], [2, '3p']] is not an element of + Shifted Primed Tableaux of shape [4, 2] + """ + if not self.parent()._contains_tableau_(self): + raise ValueError("{} is not an element of Shifted Primed Tableaux" + .format(self)) def __eq__(self, other): """ @@ -215,14 +224,14 @@ def __eq__(self, other): EXAMPLES:: sage: t = ShiftedPrimedTableau([[1,"2p"]]) - sage: t == ShiftedPrimedTableaux([2])([1,1.5]) + sage: t == ShiftedPrimedTableaux([2])([[1,1.5]]) True """ if isinstance(other, ShiftedPrimedTableau): return (self._skew == other._skew and list(self) == list(other)) try: Tab = ShiftedPrimedTableau(other) - except ValueError: + except (ValueError, TypeError): return False return (self._skew == Tab._skew and list(self) == list(Tab)) @@ -239,14 +248,14 @@ def __ne__(self, other): EXAMPLES:: sage: t = ShiftedPrimedTableau([[1,"2p"]]) - sage: t != ShiftedPrimedTableaux([2])([1,1]) + sage: t != ShiftedPrimedTableaux([2])([[1,1]]) True """ if isinstance(other, ShiftedPrimedTableau): return (self._skew != other._skew or list(self) != list(other)) try: Tab = ShiftedPrimedTableau(other) - except ValueError: + except (ValueError, TypeError): return True return (self._skew != Tab._skew or list(self) != list(Tab)) @@ -276,54 +285,6 @@ def _to_matrix(self): array.append([0]*i + list(self[i]) + [0]*(m-i-len(self[i]))) return array - def check(self): - """ - Check that ``self`` is a valid primed tableau. - - EXAMPLES:: - - sage: T = ShiftedPrimedTableaux([4,2]) - sage: t = T([[1,'2p',2,2],[2,'3p']]) - sage: t.check() - sage: s = ShiftedPrimedTableau([["2p",2,3],["2p"],[2]],skew=[2,1]) - sage: s.check() - sage: t = T([['1p','2p',2,2],[2,'3p']]) - Traceback (most recent call last): - .... - ValueError: [['1p', '2p', 2, 2], [2, '3p']] is not an element of - Shifted Primed Tableaux of shape [4, 2] - """ - if self._skew is not None: - skew = self._skew + [0]*(len(self)-len(self._skew)) - else: - skew = [0]*len(self) - for i, row in enumerate(self): - if i > 0: - if not all(val > self[i-1][j+1] - for j, val in enumerate(row) - if j+1 >= skew[i-1] - if int(val) == val): - raise ValueError( - 'column is not strictly increasing in non-primes') - if not all(val >= self[i-1][j+1] - for j, val in enumerate(row) - if j+1 >= skew[i-1] - if int(val) != val): - raise ValueError( - 'column is not weakly increasing in primes') - if not all(row[j] <= row[j+1] - for j in range(skew[i], len(row)-1) - if int(row[j]) == row[j]): - raise ValueError('row is not weakly increasing in non-primes') - if not all(row[j] < row[j+1] - for j in range(skew[i], len(row)-1) - if int(row[j]) != row[j]): - raise ValueError('row is not strictly increasing in primes') - if not all(int(row[0]) == row[0] - for i, row in enumerate(self) - if skew[i] == 0): - raise ValueError('diagonal elements must be non-primes') - def _repr_(self): """ Return a string representation of ``self``. @@ -401,6 +362,8 @@ def _repr_diagram(self): return "\n".join([" "*max_len*i + "".join(_) for i, _ in enumerate(self._repr_tab())]) + _repr_compact = _repr_diagram + def _ascii_art_(self): """ Return ASCII representation of ``self``. @@ -640,8 +603,6 @@ def weight(self): else: max_ind = int(max(flat)) weight = tuple([flat.count(i+1) for i in range(max_ind)]) - if isinstance(self.parent(), ShiftedPrimedTableaux_shape): - return self.parent().weight_lattice_realization()(weight) return weight def _reading_word_with_positions(self): @@ -713,6 +674,11 @@ def reading_word(self): raise NotImplementedError('skew tableau must be empty') return [tup[1] for tup in self._reading_word_with_positions()] + +class CrystalElementShiftedPrimedTableau(ShiftedPrimedTableau): + """ + Documentation here + """ def f(self, ind): r""" Compute the action of the crystal operator `f_i` on a shifted primed @@ -728,7 +694,7 @@ def f(self, ind): EXAMPLES:: - sage: t = ShiftedPrimedTableau([[1,1,1,1,2.5],[2,2,2,2.5],[3,3]]) + sage: t = ShiftedPrimedTableaux([5,4,2])([[1,1,1,1,'3p'],[2,2,2,'3p'],[3,3]]) sage: t.pp() 1 1 1 1 3' 2 2 2 3' @@ -736,8 +702,7 @@ def f(self, ind): sage: s = t.f(2) sage: print(s) None - sage: t = ShiftedPrimedTableau([[1,1,1,1.5,2.5],[0,2,2,3,3], - ....: [0,0,3,4]]) + sage: t = ShiftedPrimedTableaux([5,4,2])([[1,1,1,'2p','3p'],[2,2,3,3],[3,4]]) sage: t.pp() 1 1 1 2' 3' 2 2 3 3 @@ -749,17 +714,6 @@ def f(self, ind): 3 4 """ - - if self._skew is not None: - raise NotImplementedError('skew tableau must be empty') - - try: - self.parent()._weight - raise TypeError('operator generates an element outside of {}' - .format(self.parent())) - except AttributeError: - pass - T = self._to_matrix() read_word = self._reading_word_with_positions() @@ -823,7 +777,7 @@ def f(self, ind): T = [[elt-.5 if elt != 0 else elt for elt in row] for row in T] T = map(list, zip(*T)) - return type(self)(self.parent(), T) + return type(self)(self.parent(), T, check=False) def e(self, ind): r""" @@ -836,12 +790,12 @@ def e(self, ind): OUTPUT: - Primed tableau or 'None'. + Primed tableau or ``None``. EXAMPLES:: - sage: t = ShiftedPrimedTableau([[1,1,1,1.5,2.5], - ....: [0,2,"3p",3,3],[0,0,3,4]]) + sage: t = ShiftedPrimedTableaux([5,4,2])([[1,1,1,'2p','3p'], + ....: [2,'3p',3,3],[3,4]]) sage: t.pp() 1 1 1 2' 3' 2 3' 3 3 @@ -855,16 +809,6 @@ def e(self, ind): True """ - if self._skew is not None: - raise NotImplementedError('skew tableau must be empty') - - try: - self.parent()._weight - raise TypeError('operator generates an element outside of {}' - .format(self.parent())) - except AttributeError: - pass - T = self._to_matrix() read_word = self._reading_word_with_positions() @@ -922,7 +866,7 @@ def e(self, ind): T = [[elt-.5 if elt != 0 else elt for elt in row] for row in T] T = map(list, zip(*T)) - return type(self)(self.parent(), T) + return type(self)(self.parent(), T, check=False) def is_highest_weight(self, index_set=None): r""" @@ -933,29 +877,48 @@ def is_highest_weight(self, index_set=None): EXAMPLES:: - sage: t = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.0, 1.0), + sage: t = ShiftedPrimedTableaux([5,4,2])([(1.0, 1.0, 1.0, 1.0, 1.0), ....: (2.0, 2.0, 2.0, 2.5), (3.0, 3.0)]) sage: t.is_highest_weight() True - sage: s = ShiftedPrimedTableau([(1.0, 1.0, 1.0, 1.0, 1.0), + sage: s = ShiftedPrimedTableaux([5,4])([(1.0, 1.0, 1.0, 1.0, 1.0), ....: (2.0, 2.0, 2.5, 3.0)]) sage: s.is_highest_weight(index_set = [1]) True - """ - if self._skew is not None: - raise NotImplementedError('skew tableau must be empty') read_w = self.reading_word() max_entry = max(read_w) count = {i: 0 for i in range(max_entry+1)} if index_set is None: - index_set = list(range(1, max_entry)) + index_set = self.parent().index_set() for l in read_w[::-1]: count[l] += 1 - if (l-1 in index_set) and (count[l] > count[l-1]): + if (l-1 in index_set) and (l > 1) and (count[l] > count[l-1]): return False return True + def weight(self): + r""" + Return the weight of ``self``. + + The weight of a shifted primed tableau is defined to be the vector + with `i`-th component equal to the number of entries i and i' in the + tableau. + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t.weight() + (1, 4, 1) + """ + flat = [round(item) for sublist in self for item in sublist] + if flat == []: + max_ind = 0 + else: + max_ind = int(max(flat)) + weight = tuple([flat.count(i+1) for i in range(max_ind)]) + return self.parent().weight_lattice_realization()(weight) + class ShiftedPrimedTableaux(UniqueRepresentation, Parent): r""" @@ -972,24 +935,24 @@ class ShiftedPrimedTableaux(UniqueRepresentation, Parent): 3. there are only non-primed entries along the main diagonal - Valid input: shape partition. + INPUT: - Valid input keywords: ``shape``, ``weight``, ``max_entry``, ``skew``. + Valid optional keywords: - -``shape`` specifies the (outer skew) shape of tableaux + - ``shape`` -- the (outer skew) shape of tableaux - -``weight`` specifies the weight of tableaux + - ``weight`` -- the weight of tableaux - -``max_entry`` specifies the maximum entry of tableaux + - ``max_entry`` -- the maximum entry of tableaux - -``skew`` specifies the inner skew shape of tableaux + - ``skew`` -- the inner skew shape of tableaux The weight of a tableau is defined to be the vector with `i`-th component equal to the number of entries `i` and `i'` in the tableau. The sum of the coordinates in the weight vector must be equal to the number of entries in the partition. - Shape and skew arguments must be strictly decreasing partitions. + The ``shape`` and ``skew`` must be strictly decreasing partitions. EXAMPLES:: @@ -1015,8 +978,6 @@ class ShiftedPrimedTableaux(UniqueRepresentation, Parent): True sage: [(1,1),(2,2)] in ShiftedPrimedTableaux() False - sage: 1 in ShiftedPrimedTableaux(skew=[1]) - True sage: [] in ShiftedPrimedTableaux() True @@ -1028,7 +989,8 @@ class ShiftedPrimedTableaux(UniqueRepresentation, Parent): options = Tableaux.options @staticmethod - def __classcall_private__(cls, *args, **kwargs): + def __classcall_private__(cls, shape=None, weight=None, + max_entry=None, skew=None): r""" Normalize and process input to return the correct parent and ensure a unique representation. See the documentation for @@ -1059,32 +1021,16 @@ def __classcall_private__(cls, *args, **kwargs): ... ValueError: skew shape must be inside the given tableau shape """ - weight = None - shape = None - max_entry = None - skew = None - - if ('skew' in kwargs and kwargs['skew'] is not None): + if skew is not None: try: - skew = Partition(kwargs['skew']) + skew = Partition(skew) except ValueError: raise ValueError('invalid skew argument') if not all(skew[i] > skew[i+1] for i in range(len(skew)-1)): raise ValueError('skew shape must be a strict partition') - if 'max_entry' in kwargs: - max_entry = int(kwargs['max_entry']) - - if 'shape' in kwargs: - shape = kwargs['shape'] - - if 'weight' in kwargs: - weight = tuple(kwargs['weight']) - - if args: - if shape is not None: - raise ValueError('shape argument was specified twice') - shape = args[0] + if weight is not None: + weight = tuple(weight) if shape is not None: if isinstance(shape, SkewPartition): @@ -1110,37 +1056,83 @@ def __classcall_private__(cls, *args, **kwargs): if len(weight) > max_entry: raise ValueError("maximum entry is incompatible with the weight") - if shape is None and weight is None: - if max_entry is not None: - raise ValueError("specify shape or weight argument") - return ShiftedPrimedTableaux_all(skew=skew) - - if weight is None: - return ShiftedPrimedTableaux_shape(shape, max_entry=max_entry, skew=skew) - if shape is None: - return ShiftedPrimedTableaux_weight(weight, skew=skew) + if weight is None: + if max_entry is not None: + raise ValueError("specify shape or weight argument") + return ShiftedPrimedTableaux_all(skew=skew) + else: + return ShiftedPrimedTableaux_weight(weight, skew=skew) + else: + if weight is None: + return ShiftedPrimedTableaux_shape(shape, max_entry=max_entry, skew=skew) - if (skew is not None and sum(shape) - sum(skew) != sum(weight) - or skew is None and sum(shape) != sum(weight)): - raise ValueError("weight and shape are incompatible") + if (skew is not None and sum(shape) - sum(skew) != sum(weight) + or skew is None and sum(shape) != sum(weight)): + raise ValueError("weight and shape are incompatible") - return ShiftedPrimedTableaux_weight_shape(weight, shape, skew=skew) + return ShiftedPrimedTableaux_weight_shape(weight, shape, skew=skew) - def __contains__(self, T): + def __init__(self, skew=None): """ - TESTS:: + Initialization of the parent class with given skew shape. + """ + self._skew = skew + + def _element_constructor_(self, T): + """ + Construct an object from ``T`` as an element of shifted primed + tableaux, if possible. + + INPUT: + + - ``T`` -- data which can be interpreted as a primed tableau + + OUTPUT: + + - the corresponding primed tableau object - sage: [1,1,2] in ShiftedPrimedTableaux() - True - sage: (2,1) in ShiftedPrimedTableaux() - False """ try: - self.element_class(self, T, skew=self._skew) - return True + return self.element_class(self, T, skew=self._skew) except ValueError: + raise ValueError("{} is not an element of {}".format(T, self)) + + def _contains_tableau_(self, T): + """ + Check if ``self`` contains preprocessed tableau ``T``. + """ + if not all(len(T[i]) > len(T[i+1]) for i in range(len(T)-1)): return False + if self._skew is not None: + skew = self._skew + [0]*(len(T)-len(self._skew)) + else: + skew = [0]*len(T) + for i, row in enumerate(T): + if i > 0: + if not all(val > T[i-1][j+1] + for j, val in enumerate(row) + if j+1 >= skew[i-1] + if int(val) == val): + return False + if not all(val >= T[i-1][j+1] + for j, val in enumerate(row) + if j+1 >= skew[i-1] + if int(val) != val): + return False + if not all(row[j] <= row[j+1] + for j in range(skew[i], len(row)-1) + if int(row[j]) == row[j]): + return False + if not all(row[j] < row[j+1] + for j in range(skew[i], len(row)-1) + if int(row[j]) != row[j]): + return False + if not all(int(row[0]) == row[0] + for i, row in enumerate(T) + if skew[i] == 0): + return False + return True class ShiftedPrimedTableaux_all(ShiftedPrimedTableaux): @@ -1162,7 +1154,8 @@ def __init__(self, skew=None): sage: [[1,1],[2,2]] in ShiftedPrimedTableaux() False """ - Parent.__init__(self, category=FiniteEnumeratedSets()) + Parent.__init__(self, category=InfiniteEnumeratedSets()) + ShiftedPrimedTableaux.__init__(self, skew=skew) self._skew = skew def _repr_(self): @@ -1193,7 +1186,7 @@ def _element_constructor_(self, T): TESTS:: - sage: Tab=ShiftedPrimedTableaux()([1,1,"2p"]); Tab + sage: Tab=ShiftedPrimedTableaux()([[1,1,"2p"]]); Tab [(1.0, 1.0, 1.5)] sage: Tab.parent() Shifted Primed Tableaux @@ -1204,25 +1197,35 @@ def _element_constructor_(self, T): Shifted Primed Tableaux """ - try: - return self.element_class(self, T, skew=self._skew) - except ValueError: - raise ValueError("{} is not an element of {}".format(T, self)) + return (super(ShiftedPrimedTableaux_all, self)._element_constructor_(T)) - def __contains__(self, T): + def __iter__(self): """ - TESTS:: + Iterate over ``self``. - sage: [1,1,2] in ShiftedPrimedTableaux() - True - sage: (2,1) in ShiftedPrimedTableaux() - False + EXAMPLES:: + + sage: Tabs = ShiftedPrimedTableaux() + sage: Tabs[:5] + [[], [(1.0,)], [(2.0,)], [(1.0, 2.0)], [(1.0, 1.5)]] """ - try: - self.element_class(self, T, skew=self._skew) - return True - except ValueError: - return False + if self._skew is not None: + raise NotImplementedError('skew tableau must be empty') + yield self.element_class(self, []) + + max_entry = 1 + while(True): + for size in range(1, max_entry+1): + for shape in Partitions(size, max_slope=-1): + for weight in OrderedPartitions(size+max_entry-1, + k=max_entry): + weight = [weight[i]-1 for i in range(max_entry)] + weight[-1] += 1 + for tab in ShiftedPrimedTableaux(shape=shape, + weight=weight): + yield self.element_class(self, tab, check=False, + preprocessed=True) + max_entry += 1 class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): @@ -1233,7 +1236,7 @@ class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): with highest weights corresponding to a given shape. The list of module generators consists of all elements of the - crystal with strictly decreasing weight entries. + crystal with nonincreasing weight entries. The crystal is constructed following operations described in [HPS2017]_. @@ -1290,16 +1293,19 @@ def __init__(self, shape, max_entry, skew): if skew is None: category = RegularCrystals().Infinite() self._cartan_type = CartanType(['A+oo']) + self.Element = CrystalElementShiftedPrimedTableau else: category = Sets().Infinite() else: if skew is None: category = ClassicalCrystals() self._cartan_type = CartanType(['A', max_entry-1]) + self.Element = CrystalElementShiftedPrimedTableau else: category = Sets().Finite() - ShiftedPrimedTableaux.__init__(self, category=category) + ShiftedPrimedTableaux.__init__(self, skew=skew) + Parent.__init__(self, category=category) self._max_entry = max_entry self._skew = skew if skew is None: @@ -1321,22 +1327,38 @@ def _repr_(self): base += " and maximum entry {}".format(self._max_entry) return base - def __contains__(self, T): + def _contains_tableau_(self, T): """ TESTS:: - sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux([4,2]) + sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux([4,2], + ....: max_entry=4) True + sage: [[1,'2p',2],[2,'3p']] in ShiftedPrimedTableaux([4,2]) + False """ - try: - Tab = self.element_class(self, T, skew=self._skew) - except ValueError: + if not super(ShiftedPrimedTableaux_shape, self)._contains_tableau_(T): return False - if self._max_entry is None: - return (Tab.shape() == self._shape) + shape = [len(row) for row in T] + skew = [row.count(None) for row in T] + if sum(skew) == 0: + shape = Partition(shape) + else: + shape = SkewPartition((shape, skew)) + if self._shape != shape: + return False + + if self._max_entry is not None: + flat = [round(item) for sublist in T for item in sublist] + if flat == []: + max_entry = 0 + else: + max_entry = int(max(flat)) + if max_entry > self._max_entry: + return False - return (Tab.shape() == self._shape and Tab.max_entry() <= self._max_entry) + return True def _element_constructor_(self, T): """ @@ -1353,29 +1375,22 @@ def _element_constructor_(self, T): TESTS:: - sage: tab= ShiftedPrimedTableaux([3])([1,1,1.5]); tab + sage: tab= ShiftedPrimedTableaux([3])([[1,1,1.5]]); tab [(1.0, 1.0, 1.5)] sage: tab.parent() Shifted Primed Tableaux of shape [3] - sage: ShiftedPrimedTableaux([3])([1,1]) + sage: ShiftedPrimedTableaux([3])([[1,1]]) Traceback (most recent call last): ... - ValueError: [1, 1] is not an element of Shifted Primed Tableaux + ValueError: [[1, 1]] is not an element of Shifted Primed Tableaux of shape [3] """ try: - Tab = self.element_class(self, T, skew=self._skew) + return (super(ShiftedPrimedTableaux_shape, self) + ._element_constructor_(T)) except ValueError: raise ValueError("{} is not an element of {}".format(T, self)) - if self._max_entry is None: - if Tab.shape() == self._shape: - return Tab - else: - if (Tab.shape() == self._shape and Tab.max_entry() <= self._max_entry): - return Tab - raise ValueError("{} is not an element of {}".format(T, self)) - @lazy_attribute def module_generators(self): """ @@ -1397,7 +1412,8 @@ def module_generators(self): else: max_entry = self._max_entry for weight in (Partition(self._shape).dominated_partitions(rows=max_entry)): - list_dw.extend([self(T) + list_dw.extend([self.element_class(self, T, check=False, + preprocessed=True) for T in ShiftedPrimedTableaux(weight=tuple(weight), shape=self._shape)]) return tuple(list_dw) @@ -1415,36 +1431,42 @@ def shape(self): def __iter__(self): """ - Iterate over ``self``, if ``max_entry`` is specified. + Iterate over ``self``. EXAMPLES:: sage: Tabs = ShiftedPrimedTableaux([3,2], max_entry=3) - sage: Tabs[:4] + sage: Tabs[:3] [[(1.0, 1.0, 1.0), (2.0, 2.0)], [(1.0, 1.0, 1.0), (2.0, 3.0)], - [(1.0, 1.0, 1.0), (2.0, 2.5)], - [(1.0, 1.0, 1.0), (3.0, 3.0)]] + [(1.0, 1.0, 1.0), (2.0, 2.5)]] sage: len(list(Tabs)) 24 - - TESTS:: - sage: Tabs = ShiftedPrimedTableaux([3,2]) sage: Tabs[:3] - Traceback (most recent call last): - ... - ValueError: set is infinite + [[(1.0, 1.0, 1.0), (2.0, 2.0)], + [(1.0, 1.0, 1.5), (2.0, 2.0)], + [(1.0, 1.0, 1.0), (2.0, 2.0)]] """ if self._skew is not None: raise NotImplementedError('skew tableau must be empty') - if self._max_entry is None: - raise ValueError("set is infinite") - for weight in OrderedPartitions(sum(self._shape)+self._max_entry, - k=self._max_entry): - weight_n = tuple([w-1 for w in weight]) - for tab in ShiftedPrimedTableaux(shape=self._shape, weight=weight_n): - yield self(tab) + if self._max_entry is not None: + for weight in OrderedPartitions(sum(self._shape)+self._max_entry, + k=self._max_entry): + weight_n = tuple([w-1 for w in weight]) + for tab in ShiftedPrimedTableaux(shape=self._shape, weight=weight_n): + yield self.element_class(self, tab, check=False, + preprocessed=True) + else: + max_entry = 1 + while(True): + for weight in OrderedPartitions(sum(self._shape)+max_entry, k=max_entry): + weight_n = tuple([w-1 for w in weight]) + for tab in ShiftedPrimedTableaux(shape=self._shape, weight=weight_n): + yield self.element_class(self, tab, check=False, + preprocessed=True) + max_entry += 1 + class ShiftedPrimedTableaux_weight(ShiftedPrimedTableaux): @@ -1467,6 +1489,7 @@ def __init__(self, weight, skew=None): sage: TestSuite( ShiftedPrimedTableaux(weight=(3,2,1)) ).run() """ + ShiftedPrimedTableaux.__init__(self, skew=skew) Parent.__init__(self, category=FiniteEnumeratedSets()) self._weight = weight self._skew = skew @@ -1485,20 +1508,6 @@ def _repr_(self): return ("Shifted Primed Tableaux of weight {} skewed by {}" .format(self._weight, self._skew)) - def __contains__(self, T): - """ - TESTS:: - - sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux(weight=(1,4,1)) - True - """ - try: - Tab = self.element_class(self, T, skew=self._skew) - except ValueError: - return False - - return Tab.weight() == self._weight - def _element_constructor_(self, T): """ Construct an object from ``T`` as an element of ``self``, if @@ -1514,19 +1523,38 @@ def _element_constructor_(self, T): TESTS:: - sage: tab= ShiftedPrimedTableaux(weight=(2,1))([1,1,1.5]); tab + sage: tab= ShiftedPrimedTableaux(weight=(2,1))([[1,1,1.5]]); tab [(1.0, 1.0, 1.5)] sage: tab.parent() Shifted Primed Tableaux of weight (2, 1) """ try: - Tab = self.element_class(self, T, skew=self._skew) + return (super(ShiftedPrimedTableaux_weight, self) + ._element_constructor_(T)) except ValueError: raise ValueError("{} is not an element of {}".format(T, self)) - if Tab.weight() == self._weight: - return Tab - raise ValueError("{} is not an element of {}".format(T, self)) + def _contains_tableau_(self, T): + """ + Check if ``self`` contains preprocessed tableau ``T``. + + TESTS:: + + sage: [[1,1.5],[2]] in ShiftedPrimedTableaux(weight=(1,2)) + True + sage: [[1,1.5],[3]] in ShiftedPrimedTableaux(weight=(1,2)) + False + """ + if not super(ShiftedPrimedTableaux_weight, self)._contains_tableau_(T): + return False + + flat = [round(item) for sublist in T for item in sublist] + if flat == []: + max_ind = 0 + else: + max_ind = int(max(flat)) + weight = tuple([flat.count(i+1) for i in range(max_ind)]) + return self._weight == weight def __iter__(self): """ @@ -1543,11 +1571,12 @@ def __iter__(self): sage: len(list(Tabs)) 5 """ - return (tab for shape_ in Partitions(sum(self._weight)) - if all(shape_[i] > shape_[i+1] for i in range(len(shape_)-1)) - for tab in ShiftedPrimedTableaux(shape=shape_, - weight=self._weight, - skew=self._skew)) + for shape_ in Partitions(sum(self._weight)): + if all(shape_[i] > shape_[i+1] for i in range(len(shape_)-1)): + for tab in ShiftedPrimedTableaux(shape=shape_, weight=self._weight, + skew=self._skew): + yield self.element_class(self, tab, check=False, + preprocessed=True) class ShiftedPrimedTableaux_weight_shape(ShiftedPrimedTableaux): @@ -1570,6 +1599,7 @@ def __init__(self, weight, shape, skew=None): sage: TestSuite( ShiftedPrimedTableaux([4,2,1], weight=(3,2,2)) ).run() """ + ShiftedPrimedTableaux.__init__(self, skew=skew) Parent.__init__(self, category=FiniteEnumeratedSets()) self._weight = weight self._skew = skew @@ -1590,20 +1620,42 @@ def _repr_(self): return ("Shifted Primed Tableaux of weight {} and shape {}" .format(self._weight, self._shape)) - def __contains__(self, T): + def _contains_tableau_(self, T): """ + Check if ``self`` contains preprocessed tableau ``T``. + TESTS:: - sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux( - ....: [4,2], weight=(1,4,1)) + sage: [[1,1.5],[2]] in ShiftedPrimedTableaux([2,1], weight=(1,2)) True + sage: [[1,1.5],[3]] in ShiftedPrimedTableaux([2,1], weight=(1,2)) + False + sage: [[1,1.5,2,3],[3]] in ShiftedPrimedTableaux([3,2], weight=(1,2,2)) + False """ - try: - Tab = self.element_class(self, T, skew=self._skew) - except ValueError: + + if not super(ShiftedPrimedTableaux_weight_shape, self)._contains_tableau_(T): return False - return Tab.weight() == self._weight and Tab.shape() == self._shape + flat = [round(item) for sublist in T for item in sublist] + if flat == []: + max_ind = 0 + else: + max_ind = int(max(flat)) + weight = tuple([flat.count(i+1) for i in range(max_ind)]) + if self._weight!=weight: + return False + + shape = [len(row) for row in T] + skew = [row.count(None) for row in T] + if sum(skew) == 0: + shape = Partition(shape) + else: + shape = SkewPartition((shape, skew)) + if self._shape != shape: + return False + + return True def _element_constructor_(self, T): """ @@ -1612,26 +1664,22 @@ def _element_constructor_(self, T): TESTS:: - sage: tab= ShiftedPrimedTableaux([3], weight=(2,1))([1,1,1.5]); tab + sage: tab = ShiftedPrimedTableaux([3], weight=(2,1))([[1,1,1.5]]); tab [(1.0, 1.0, 1.5)] sage: tab.parent() Shifted Primed Tableaux of weight (2, 1) and shape [3] - sage: ShiftedPrimedTableaux([3], weight=(2,1))([1,1]) + sage: ShiftedPrimedTableaux([3], weight=(2,1))([[1,1]]) Traceback (most recent call last): ... - ValueError: [1, 1] is not an element of Shifted Primed Tableaux + ValueError: [[1, 1]] is not an element of Shifted Primed Tableaux of weight (2, 1) and shape [3] """ try: - Tab = self.element_class(self, T, skew=self._skew) + return (super(ShiftedPrimedTableaux_weight_shape, self) + ._element_constructor_(T)) except ValueError: raise ValueError("{} is not an element of {}".format(T, self)) - if Tab.shape() == self._shape and Tab.weight() == self._weight: - return Tab - - raise ValueError("{} is not an element of {}".format(T, self)) - def __iter__(self): """ Iterate over ``self``. @@ -1683,7 +1731,7 @@ def __iter__(self): for r in range(l)] tab_list_new.append(new_tab) for tab in tab_list_new: - yield(ShiftedPrimedTableau(tab)) + yield self.element_class(self, tab) #################### From 1bc73c4a1cbb03f866889a40d4f7958c0328017b Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Wed, 17 Jan 2018 08:13:38 +0100 Subject: [PATCH 481/740] 24411: deprecation, imports --- src/sage/functions/gamma.py | 2 +- src/sage/functions/other.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/sage/functions/gamma.py b/src/sage/functions/gamma.py index 635cc77d6e4..b4167ec9d14 100644 --- a/src/sage/functions/gamma.py +++ b/src/sage/functions/gamma.py @@ -1,7 +1,7 @@ """ Gamma and related functions """ -from __future__ import print_function +from __future__ import print_function, absolute_import from six.moves import range from six import integer_types diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index d8627db8e45..834b28de369 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -1,10 +1,26 @@ """ Other functions + +TESTS: + +Check that gamma function imports are deprecated (:trac:`24411`):: + + sage: from sage.functions.other import beta + sage: beta(x, x) + doctest:...: DeprecationWarning: + Importing beta from here is deprecated. If you need to use it, please import it directly from sage.functions.gamma + See http://trac.sagemath.org/24411 for details. + beta(x, x) """ from __future__ import print_function from six.moves import range from six import integer_types +from sage.misc.lazy_import import lazy_import +lazy_import('sage.functions.gamma', + ('gamma', 'log_gamma', 'gamma_inc', 'incomplete_gamma', + 'gamma_inc_lower', 'psi', 'beta'), deprecation=24411) + from sage.symbolic.function import GinacFunction, BuiltinFunction from sage.symbolic.expression import Expression from sage.libs.pynac.pynac import (register_symbol, symbol_table, From 9a4d5481aa418e4c8d273c4f919b64491ac892ad Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Wed, 17 Jan 2018 09:38:38 +0000 Subject: [PATCH 482/740] Slight correction to this docstring. --- src/sage/misc/temporary_file.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/misc/temporary_file.py b/src/sage/misc/temporary_file.py index da9d9c1381b..41214ba68c1 100644 --- a/src/sage/misc/temporary_file.py +++ b/src/sage/misc/temporary_file.py @@ -240,9 +240,9 @@ class atomic_write(object): mode bits of the file were changed manually). (Not to be confused with the file opening mode.) - - ``binary`` -- (boolean, default: False) the underlying file is opened - in binary mode. If False then it is opened in text mode and an encoding - with which to write the file may be supplied. + - ``binary`` -- (boolean, default: True on Python 2, False on Python 3) the + underlying file is opened in binary mode. If False then it is opened in + text mode and an encoding with which to write the file may be supplied. - ``kwargs`` -- additional keyword arguments passed to the underlying `io.open` call. From 99296dc97b86b476760e19f7acabbac2c992c6ed Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Wed, 17 Jan 2018 11:41:39 +0100 Subject: [PATCH 483/740] Reworked pickling. Reference builds now. --- src/doc/en/reference/groups/index.rst | 1 + .../groups/abelian_gps/abelian_group_gap.py | 200 ++++++++++++++---- 2 files changed, 157 insertions(+), 44 deletions(-) diff --git a/src/doc/en/reference/groups/index.rst b/src/doc/en/reference/groups/index.rst index 1d28cc2bc00..582b1293566 100644 --- a/src/doc/en/reference/groups/index.rst +++ b/src/doc/en/reference/groups/index.rst @@ -33,6 +33,7 @@ Abelian Groups :maxdepth: 2 sage/groups/abelian_gps/abelian_group + sage/groups/abelian_gps/abelian_group_gap sage/groups/abelian_gps/values sage/groups/abelian_gps/dual_abelian_group sage/groups/abelian_gps/element_base diff --git a/src/sage/groups/abelian_gps/abelian_group_gap.py b/src/sage/groups/abelian_gps/abelian_group_gap.py index 7dab5aac8db..9ae7178babc 100644 --- a/src/sage/groups/abelian_gps/abelian_group_gap.py +++ b/src/sage/groups/abelian_gps/abelian_group_gap.py @@ -1,3 +1,27 @@ +r""" +Finitely generated abelian groups with gap. + +This module provides a python wrapper for abelian groups in gap. + +EXAMPLES:: + + sage: + +AUTHORS: + +- Simon Brandhorst (2018-01-17): initial version + +""" + +# **************************************************************************** +# Copyright (C) 2018 SIMON BRANDHORST +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +# **************************************************************************** from sage.groups.libgap_wrapper import ParentLibGAP, ElementLibGAP from sage.groups.libgap_mixin import GroupMixinLibGAP from sage.groups.group import AbelianGroup as AbelianGroupBase @@ -8,13 +32,14 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.categories.groups import Groups + def AbelianGroupGap(generator_orders): r""" Create the multiplicative abelian group with given orders of generators. INPUT: - - ``generator_orders`` -- a list of nonnegative integers where `0` will give a factor isomorphic to `\ZZ`. + - ``generator_orders`` -- a list of nonnegative integers where `0` gives a factor isomorphic to `\ZZ`. OUTPUT: @@ -43,7 +68,14 @@ def AbelianGroupGap(generator_orders): class AbelianGroupElement_gap(ElementLibGAP): r""" - An element of an abelian group via libgap + An element of an abelian group via libgap. + + EXAMPLES:: + + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([3,6]) + sage: G.gens() + (g1, g2) """ def __init__(self, parent, x, check=True): """ @@ -87,6 +119,10 @@ def __hash__(self): def __reduce__(self): r""" Implement pickling. + + OUTPUT: + + - a tuple ``f`` such that this element is ``f[0](*f[1])`` EXAMPLES:: @@ -104,6 +140,10 @@ def _repr_(self): """ The string representation of this element. + OUTPUT: + + - a string + EXAMPLES:: sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap @@ -118,6 +158,10 @@ def _repr_(self): def exponents(self): r""" Return the tuple of exponents. + + OUTPUT: + + - a tuple of sage integers EXAMPLES:: @@ -157,6 +201,10 @@ def exponents(self): def order(self): r""" Return the order of this element. + + OUTPUT: + + - an integer or infinity EXAMPLES:: @@ -172,27 +220,6 @@ def order(self): """ return self.gap().Order().sage() -# def sage(self): -# r""" -# Convert this element to the corresponding abelian group in sage. -# -# EXAMPLES:: -# -# sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap -# sage: G = AbelianGroupGap([2,3,7,4]) -# sage: all([a == a.gap().sage() for a in A]) -# True -# sage: all([g == g.sage().gap() for g in G]) -# True -# """ -# P = self.parent() -# gens_sage = P._A.gens() -# e = P._A.identity() -# exp = self.exponents() -# for i in range(len(exp)): -# e *= gens_sage[i]**exp[i] -# return e - class AbelianGroup_gap(UniqueRepresentation,GroupMixinLibGAP, ParentLibGAP, AbelianGroupBase): r""" Python wrapper for finitely generated abelian groups in gap. @@ -204,7 +231,7 @@ class AbelianGroup_gap(UniqueRepresentation,GroupMixinLibGAP, ParentLibGAP, Abel - ``G`` -- (default:``None``) a gap group - ``ambient`` -- (default:``None``) an :class:`AbelianGroup_gap` - ``polycyclic_package`` -- (default: ``False``) boolean - - ``category` -- a category` + - ``category`` -- a category EXAMPLES:: @@ -212,7 +239,6 @@ class AbelianGroup_gap(UniqueRepresentation,GroupMixinLibGAP, ParentLibGAP, Abel sage: G = AbelianGroupGap([3,2,5]) sage: G Abelian group with gap, generator orders (3, 2, 5) - """ def __init__(self, G, ambient=None, polycyclic_package=False, category=None): r""" @@ -223,7 +249,7 @@ def __init__(self, G, ambient=None, polycyclic_package=False, category=None): TESTS:: sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap - sage: G = AbelianGroupGap([3,2,5]) + sage: G = AbelianGroupGap([3,2,5]) # indirect doctest sage: TestSuite(G).run() """ self._with_pc = polycyclic_package @@ -232,14 +258,6 @@ def __init__(self, G, ambient=None, polycyclic_package=False, category=None): Element = AbelianGroupElement_gap - def __reduce__(self): - r""" - """ - if self is self.ambient(): - return AbelianGroupGap, (self.gens_orders(),) - else: - return self.ambient().subgroup, (self.gens(),) - def _coerce_map_from_(self, S): r""" Return whether ``S`` canonically coerces to ``self``. @@ -274,17 +292,53 @@ def _element_constructor_(self,x,check=True): INPUT: - - ``x` -- an element of this group, a gap element ` - """ + - ``x`` -- an element of this group, a gap element + + EXAMPLES:: + + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([2,3]) + sage: A = AbelianGroup([2,3]) + sage: a = A.an_element() + sage: a + f0*f1 + sage: G(a) + g1*g2 + sage: A = AdditiveAbelianGroup([2,3]) + sage: a = A.an_element() + sage: a + (1, 0) + sage: G(a) + g1 + + For general fgp_modules conversion is implemented if our group is in smith form:: + + sage: G = AbelianGroupGap([6]) + sage: A = ZZ^2 + sage: A = A / A.submodule([2*A.0, 3*A.1]) + sage: a = 2 * A.an_element() + sage: a + (2) + sage: G(a) + g1^2 + """ if isinstance(x, AbelianGroupElement_gap): x = x.gap() elif x == 1: x = self.gap().Identity() elif not isinstance(x, GapElement): - try: + from sage.groups.abelian_gps.abelian_group_element import AbelianGroupElement + from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroupElement + from sage.modules.fg_pid.fgp_element import FGP_Element + if isinstance(x, AbelianGroupElement): exp = x.exponents() - except AttributeError: - exp = tuple(x) + elif isinstance(x, AdditiveAbelianGroupElement): + exp = x._hermite_lift() + elif isinstance(x, FGP_Element): + exp = x.vector() + else: + from sage.modules.free_module_element import vector + exp = vector(ZZ,x) # turn the exponents into a gap element gens_gap = self.gens() if len(exp) != len(gens_gap): @@ -295,6 +349,28 @@ def _element_constructor_(self,x,check=True): x = x.gap() return self.element_class(self, x, check=check) + def all_subgroups(self): + r""" + Return the list of all subgroups of this group. + + EXAMPLES:: + + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([2,3]) + sage: G.all_subgroups() + [Subgroup of Abelian group with gap, generator orders (2, 3) generated by (), + Subgroup of Abelian group with gap, generator orders (2, 3) generated by (g1,), + Subgroup of Abelian group with gap, generator orders (2, 3) generated by (g2,), + Subgroup of Abelian group with gap, generator orders (2, 3) generated by (g2, g1)] + """ + subgroups_gap = self.gap().AllSubgroups() + subgroups_sage = [] + for G in subgroups_gap: + S = self.subgroup(G.GeneratorsOfGroup()) + subgroups_sage.append(S) + del G + return subgroups_sage + def is_trivial(self): r""" Return if this group is the trivial group. @@ -312,7 +388,7 @@ def identity(self): r""" Return the identity element of this group. - EXAMPLES: + EXAMPLES:: sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: G = AbelianGroupGap([4,10]) @@ -443,14 +519,14 @@ class AbelianGroupAmbient_gap(AbelianGroup_gap): r""" Ambient abelian groups with gap. - Do not use this class directly. Instead use :function:`AbelianGroupGap`. + Do not use this class directly. Instead use :meth:`AbelianGroupGap`. Needs the gap package "Polycyclic" in case the group is infinite. INPUT: - ``generator_orders`` - a tuple of nonnegative integers - ``polycyclic_package`` -- (default: ``False``) boolean - - ``category` -- a category` + - ``category`` -- a category EXAMPLES:: @@ -490,7 +566,22 @@ def _repr_(self): 'Abelian group with gap, generator orders (2, 6)' """ return "Abelian group with gap, generator orders " + str(self.gens_orders()) - + + def __reduce__(self): + r""" + Implements pickling. + + We have to work around the fact that gap does not provide pickling. + + EXAMPLES:: + + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([3,2,5]) + sage: G == loads(dumps(G)) + True + """ + return AbelianGroupGap, (self.gens_orders(),) + class AbelianGroupSubgroup_gap(AbelianGroup_gap): r""" Subgroups of abelian groups with gap. @@ -536,9 +627,30 @@ def __repr__(self): sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: G = AbelianGroupGap([2,3,4,5]) sage: gen = G.gens()[:2] - sage: S = G.subgroup(gen) # indirect doctest + sage: S = G.subgroup(gen) sage: S.__repr__() 'Subgroup of Abelian group with gap, generator orders (2, 3, 4, 5) generated by (g1, g2)' """ s = "Subgroup of %s generated by %s"%(self.ambient(),self.gens()) return s + + + def __reduce__(self): + r""" + Implements pickling. + + We have to work around the fact that gap does not provide pickling. + + EXAMPLES:: + + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([2,3,4,5]) + sage: gen = G.gens()[:2] + sage: S = G.subgroup(gen) + sage: S == loads(dumps(S)) + True + """ + amb = self.ambient() + # avoid infinite loop + gens = tuple(amb(g) for g in self.gens()) + return amb.subgroup, (gens,) From c3bd7a1652a0022efcf999a84ace4282019cb5f2 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Wed, 17 Jan 2018 11:45:24 +0100 Subject: [PATCH 484/740] Added an example --- src/sage/groups/abelian_gps/abelian_group_gap.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sage/groups/abelian_gps/abelian_group_gap.py b/src/sage/groups/abelian_gps/abelian_group_gap.py index 9ae7178babc..45df4bc9a5a 100644 --- a/src/sage/groups/abelian_gps/abelian_group_gap.py +++ b/src/sage/groups/abelian_gps/abelian_group_gap.py @@ -5,7 +5,14 @@ EXAMPLES:: - sage: + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: AbelianGroupGap([3,5]) + Abelian group with gap, generator orders (3, 5) + +For infinite abelian groups we use the gap package Polycyclic:: + + sage: AbelianGroupGap([3,0]) # optional gap_packages + Abelian group with gap, generator orders (3, 0) AUTHORS: From 4d2fbe04419a9eed0da4f2a1ccc8ecb5fc7b9256 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Wed, 17 Jan 2018 12:20:44 +0100 Subject: [PATCH 485/740] Added method is_subgroup_of. ambient() now allways returns the full automorphism group. --- .../groups/abelian_gps/abelian_group_gap.py | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/sage/groups/abelian_gps/abelian_group_gap.py b/src/sage/groups/abelian_gps/abelian_group_gap.py index 45df4bc9a5a..1a81f523237 100644 --- a/src/sage/groups/abelian_gps/abelian_group_gap.py +++ b/src/sage/groups/abelian_gps/abelian_group_gap.py @@ -286,12 +286,10 @@ def _coerce_map_from_(self, S): sage: G._coerce_map_from_(S) True sage: S._coerce_map_from_(G) + False """ - try: - if S.ambient() is self: - return True - except AttributeError: - pass + if isinstance(S,AbelianGroup_gap): + return S.is_subgroup_of(self) def _element_constructor_(self,x,check=True): r""" @@ -481,6 +479,28 @@ def gens_orders(self): orders.append(order) return tuple(orders) + def is_subgroup_of(self, G): + r""" + Return if ``self`` is a subgroup of ``G`` considered in the same ambient group. + + EXAMPLES:: + + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([2,3,4,5]) + sage: gen = G.gens()[:2] + sage: S1 = G.subgroup(gen) + sage: S1.is_subgroup_of(G) + True + sage: S2 = G.subgroup(G.gens()[1:]) + sage: S2.is_subgroup_of(S1) + False + """ + if not isinstance(G,AbelianGroup_gap): + raise ValueError("Input must be an instance of AbelianGroup_gap.") + if not self.ambient() is G.ambient(): + return False + return G.gap().IsSubsemigroup(self).sage() + def subgroup(self, gens): r""" Return the subgroup of this group generated by ``gens``. @@ -520,7 +540,7 @@ def subgroup(self, gens): False """ gens = tuple(self(g) for g in gens) - return AbelianGroupSubgroup_gap(self, gens) + return AbelianGroupSubgroup_gap(self.ambient(), gens) class AbelianGroupAmbient_gap(AbelianGroup_gap): r""" From d9308b29558240d8100e8b7d72bfd00a1d83a589 Mon Sep 17 00:00:00 2001 From: vinklein Date: Wed, 3 May 2017 09:54:15 +0200 Subject: [PATCH 486/740] Conversion between gmpy2 and Sage objects --- src/module_list.py | 1 - src/sage/rings/integer.pyx | 34 ++++++++++++++++++ src/sage/rings/rational.pyx | 64 ++++++++++++++++++++++++++++++++++ src/sage/rings/real_double.pyx | 25 +++++++++++++ src/sage/rings/real_mpfr.pyx | 46 ++++++++++++++++++++++++ 5 files changed, 169 insertions(+), 1 deletion(-) diff --git a/src/module_list.py b/src/module_list.py index 911d598ecfd..f93646692e1 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -494,7 +494,6 @@ def uname_specific(name, value, alternative): package="libbraiding", language = 'c++'), - OptionalExtension('sage.libs.homfly', sources = ["sage/libs/homfly.pyx"], libraries = ["homfly", "gc"], diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index db564b1951d..dd47878ccf8 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -116,6 +116,9 @@ AUTHORS: in favour of :meth:`~sage.rings.integer.Integer.is_perfect_power` (see :trac:`12116`) +- Vincent Klein (2017-05-11): add __mpz__() to class Integer + +- Vincent Klein (2017-05-22): Integer constructor support gmpy2.mpz parameter """ #***************************************************************************** # Copyright (C) 2004,2006 William Stein @@ -187,6 +190,9 @@ from sage.structure.element import coerce_binop from sage.libs.gmp.binop cimport mpq_add_z, mpq_mul_z, mpq_div_zz +from gmpy2 cimport MPZ, MPZ_Object, GMPy_MPZ_From_mpz, import_gmpy2, MPZ_Check +import_gmpy2() + cdef extern from *: int unlikely(int) nogil # Defined by Cython @@ -466,6 +472,12 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): sage: Integer(pari('Pol([-3])')) -3 + Conversion from gmpy2:: + + sage: from gmpy2 import mpz + sage: Integer(mpz(3)) + 3 + .. automethod:: __pow__ """ @@ -522,6 +534,9 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): 0 sage: ZZ('+10') 10 + sage: from gmpy2 import mpz + sage: ZZ(mpz(42)) + 42 :: @@ -717,6 +732,9 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): if isinstance(x, numpy.integer): mpz_set_pylong(self.value, x.__long__()) return + elif MPZ_Check(x): + mpz_set(self.value, MPZ( x)) + return raise TypeError("unable to coerce %s to an integer" % type(x)) @@ -1039,6 +1057,22 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): """ return '%s'%self + def __mpz__(self): + """ + Return a gmpy2 integer + + EXAMPLES:: + + sage: a = 5 + sage: a.__mpz__() + mpz(5) + sage: from gmpy2 import mpz + sage: sz = ZZ(42) + sage: mpz(sz) + mpz(42) + """ + return GMPy_MPZ_From_mpz(self.value) + def str(self, int base=10): r""" Return the string representation of ``self`` in the diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index 9e8c39d602f..4965828abf6 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -28,6 +28,11 @@ AUTHORS: - Vincent Delecroix (2017-05-03): faster integer-rational comparison +- Vincent Klein (2017-05-11): add __mpq__() to class Rational + +- Vincent Klein (2017-05-22): Rational constructor support gmpy2.mpq + or gmpy2.mpz parameter. Add __mpz__ to class Rational. + TESTS:: sage: a = -2/3 @@ -106,6 +111,9 @@ cdef object numpy_double_interface = {'typestr': '=f8'} from libc.math cimport ldexp from sage.libs.gmp.all cimport * +from gmpy2 cimport MPQ, MPQ_Object, GMPy_MPQ_From_mpq, import_gmpy2, \ + MPZ_Check, MPQ_Check, MPZ, MPZ_Object +import_gmpy2() cdef class Rational(sage.structure.element.FieldElement) @@ -419,6 +427,18 @@ cdef class Rational(sage.structure.element.FieldElement): sage: QQ(np.float16('12')) 12 + + Conversions from gmpy2:: + + sage: from gmpy2 import * + sage: QQ(mpq('3/4')) + 3/4 + sage: QQ(mpz(42)) + 42 + sage: Rational(mpq(2/3)) + 2/3 + sage: Rational(mpz(5)) + 5 """ def __cinit__(self): r""" @@ -455,6 +475,9 @@ cdef class Rational(sage.structure.element.FieldElement): 2/3 sage: a.__init__('-h/3ki', 32); a -17/3730 + sage: from gmpy2 import mpq + sage: a.__init__(mpq('3/5')); a + 3/5 TESTS: @@ -503,6 +526,24 @@ cdef class Rational(sage.structure.element.FieldElement): return int(self) raise TypeError("rational is not an integer") + def __mpz__(self): + """ + Return a gmpy2 ``mpz`` if this Rational is an integer. + + EXAMPLES:: + sage: q = Rational(6/2) + sage: q.__mpz__() + mpz(3) + sage: q = Rational(1/4) + sage: q.__mpz__() + Traceback (most recent call last): + ... + TypeError: rational is not an integer + """ + if self.denominator() == 1: + return self.numerator().__mpz__() + raise TypeError("rational is not an integer") + def _reduce_set(self, s): """ Used in setting a rational number when unpickling. Do not call this @@ -636,6 +677,12 @@ cdef class Rational(sage.structure.element.FieldElement): mpz_set(mpq_numref(self.value), ( integer.Integer(x.numerator)).value) mpz_set(mpq_denref(self.value), ( integer.Integer(x.denominator)).value) + elif MPQ_Check( x): + mpq_set(self.value, MPQ( x)) + + elif MPZ_Check( x): + mpq_set_z(self.value, MPZ( x)) + else: raise TypeError("unable to convert {!r} to a rational".format(x)) @@ -938,6 +985,23 @@ cdef class Rational(sage.structure.element.FieldElement): import sympy return sympy.Rational(int(self.numerator()), int(self.denominator())) + def __mpq__(self): + """ + Convert Sage ``Rational`` to gmpy2 ``Rational``. + + EXAMPLES:: + + sage: r = 5/3 + sage: r.__mpq__() + mpq(5,3) + sage: from gmpy2 import mpq + sage: r=3/4 + sage: mpq(r) + mpq(3,4) + + """ + return GMPy_MPQ_From_mpq(self.value) + def _magma_init_(self, magma): """ Return the magma representation of ``self``. diff --git a/src/sage/rings/real_double.pyx b/src/sage/rings/real_double.pyx index b08b10ac236..9ce84750d20 100644 --- a/src/sage/rings/real_double.pyx +++ b/src/sage/rings/real_double.pyx @@ -71,6 +71,7 @@ from sage.structure.coerce cimport is_numpy_type from sage.misc.randstate cimport randstate, current_randstate from sage.structure.richcmp cimport rich_to_bool from sage.arith.constants cimport * +from gmpy2 import mpfr def is_RealDoubleField(x): @@ -705,6 +706,16 @@ cdef class RealDoubleElement(FieldElement): sage: RDF(10^100) 1e+100 + + TESTS:: + + sage: from gmpy2 import mpz, mpq, mpfr + sage: RDF(mpz(42)) + 42.0 + sage: RDF(mpq(3/4)) + 0.75 + sage: RDF(mpq('4.1')) + 4.1 """ self._value = float(x) @@ -921,6 +932,20 @@ cdef class RealDoubleElement(FieldElement): """ return Integer(self._value) + def __mpfr__(self): + """ + Convert Sage ``RealDoubleElement`` to gmpy2 ``mpfr``. + + EXAMPLES:: + + sage: RDF(42.2).__mpfr__() + mpfr('42.200000000000003') + sage: from gmpy2 import mpfr + sage: mpfr(RDF(5.1)) + mpfr('5.0999999999999996') + """ + return mpfr(self._value) + def _interface_init_(self, I=None): """ Return ``self`` formatted as a string, suitable as input to another diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index df9d736c5a4..9e561368f02 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -31,6 +31,10 @@ AUTHORS: - Eviatar Bach (2013-06): Fixing numerical evaluation of log_gamma +- Vincent Klein (2017-06): RealNumber constructor support gmpy2.mpfr + , gmpy2.mpq or gmpy2.mpz parameter. + Add __mpfr__ to class RealNumber. + This is a binding for the MPFR arbitrary-precision floating point library. @@ -120,6 +124,7 @@ import sys import re from cpython.object cimport Py_NE +from cpython cimport PyObject from cysignals.signals cimport sig_on, sig_off from sage.ext.stdsage cimport PY_NEW @@ -159,6 +164,9 @@ import sage.rings.infinity from sage.structure.parent_gens cimport ParentWithGens from sage.arith.numerical_approx cimport digits_to_bits +from gmpy2 cimport MPQ, MPQ_Object, import_gmpy2, MPZ_Check, MPQ_Check, \ + MPZ, MPZ_Object, MPFR, MPFR_Object, MPFR_Check, GMPy_MPFR_From_mpfr +import_gmpy2() #***************************************************************************** # @@ -1343,6 +1351,19 @@ cdef class RealNumber(sage.structure.element.RingElement): sage: R('1.2456') 1.2 + EXAMPLES: gmpy2 numbers + + :: + + sage: from gmpy2 import * + sage: rf = RealField() + sage: rf(mpz(5)) + 5.00000000000000 + sage: rf(mpq(1/2)) + 0.500000000000000 + sage: rf(mpfr('42.1')) + 42.1000000000000 + EXAMPLES: Rounding Modes :: @@ -1445,6 +1466,12 @@ cdef class RealNumber(sage.structure.element.RingElement): mpfr_set_d(self.value, x, parent.rnd) elif isinstance(x, RealDoubleElement): mpfr_set_d(self.value, (x)._value, parent.rnd) + elif MPFR_Check( x): + mpfr_set(self.value, MPFR( x),parent.rnd) + elif MPQ_Check( x): + mpfr_set_q(self.value, MPQ( x), parent.rnd) + elif MPZ_Check( x): + mpfr_set_z(self.value, MPZ( x), parent.rnd) else: s = str(x).replace(' ','') s_lower = s.lower() @@ -3702,7 +3729,26 @@ cdef class RealNumber(sage.structure.element.RingElement): return -result return result + def __mpfr__(self): + """ + Convert Sage ``RealNumber`` to gmpy2 ``mpfr``. + EXAMPLES:: + + sage: rf = RealField() + sage: r = rf(4.12) + sage: r.__mpfr__() + mpfr('4.1200000000000001') + sage: from gmpy2 import mpfr + sage: r= rf(4.5) + sage: mpfr(r) + mpfr('4.5') + sage: R = RealField(256) + sage: r = mpfr(R.pi()) + sage: r.precision + 256 + """ + return GMPy_MPFR_From_mpfr(self.value) ########################################### # Comparisons: ==, !=, <, <=, >, >= ########################################### From 074acd61e20ed39a1147edf2de8c6efb1cfc1c53 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 14 Nov 2017 12:50:17 +0100 Subject: [PATCH 487/740] Cleaner and optional gmpy2 interface --- src/sage/rings/integer.pyx | 40 ++++++++++----- src/sage/rings/rational.pyx | 94 ++++++++++++++++++++-------------- src/sage/rings/real_double.pyx | 30 +++++++---- src/sage/rings/real_mpfr.pyx | 71 +++++++++++++------------ 4 files changed, 142 insertions(+), 93 deletions(-) diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index dd47878ccf8..a71ee179a3c 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -190,8 +190,10 @@ from sage.structure.element import coerce_binop from sage.libs.gmp.binop cimport mpq_add_z, mpq_mul_z, mpq_div_zz -from gmpy2 cimport MPZ, MPZ_Object, GMPy_MPZ_From_mpz, import_gmpy2, MPZ_Check -import_gmpy2() +IF HAVE_GMPY2: + cimport gmpy2 + gmpy2.import_gmpy2() + cdef extern from *: int unlikely(int) nogil # Defined by Cython @@ -474,8 +476,8 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): Conversion from gmpy2:: - sage: from gmpy2 import mpz - sage: Integer(mpz(3)) + sage: from gmpy2 import mpz # optional - gmpy2 + sage: Integer(mpz(3)) # optional - gmpy2 3 .. automethod:: __pow__ @@ -534,8 +536,8 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): 0 sage: ZZ('+10') 10 - sage: from gmpy2 import mpz - sage: ZZ(mpz(42)) + sage: from gmpy2 import mpz # optional - gmpy2 + sage: ZZ(mpz(42)) # optional - gmpy2 42 :: @@ -732,8 +734,9 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): if isinstance(x, numpy.integer): mpz_set_pylong(self.value, x.__long__()) return - elif MPZ_Check(x): - mpz_set(self.value, MPZ( x)) + + elif HAVE_GMPY2 and type(x) is gmpy2.mpz: + mpz_set(self.value, (x).z) return raise TypeError("unable to coerce %s to an integer" % type(x)) @@ -1064,14 +1067,23 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): EXAMPLES:: sage: a = 5 - sage: a.__mpz__() + sage: a.__mpz__() # optional - gmpy2 mpz(5) - sage: from gmpy2 import mpz - sage: sz = ZZ(42) - sage: mpz(sz) - mpz(42) + sage: from gmpy2 import mpz # optional - gmpy2 + sage: mpz(a) # optional - gmpy2 + mpz(5) + + TESTS:: + + sage: a.__mpz__(); raise NotImplementedError("gmpy2 is not installed") + Traceback (most recent call last): + ... + NotImplementedError: gmpy2 is not installed """ - return GMPy_MPZ_From_mpz(self.value) + IF HAVE_GMPY2: + return gmpy2.GMPy_MPZ_From_mpz(self.value) + ELSE: + raise NotImplementedError("gmpy2 is not installed") def str(self, int base=10): r""" diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index 4965828abf6..7360076f04f 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -111,9 +111,11 @@ cdef object numpy_double_interface = {'typestr': '=f8'} from libc.math cimport ldexp from sage.libs.gmp.all cimport * -from gmpy2 cimport MPQ, MPQ_Object, GMPy_MPQ_From_mpq, import_gmpy2, \ - MPZ_Check, MPQ_Check, MPZ, MPZ_Object -import_gmpy2() + +IF HAVE_GMPY2: + cimport gmpy2 + gmpy2.import_gmpy2() + cdef class Rational(sage.structure.element.FieldElement) @@ -430,14 +432,14 @@ cdef class Rational(sage.structure.element.FieldElement): Conversions from gmpy2:: - sage: from gmpy2 import * - sage: QQ(mpq('3/4')) + sage: from gmpy2 import * # optional - gmpy2 + sage: QQ(mpq('3/4')) # optional - gmpy2 3/4 - sage: QQ(mpz(42)) + sage: QQ(mpz(42)) # optional - gmpy2 42 - sage: Rational(mpq(2/3)) + sage: Rational(mpq(2/3)) # optional - gmpy2 2/3 - sage: Rational(mpz(5)) + sage: Rational(mpz(5)) # optional - gmpy2 5 """ def __cinit__(self): @@ -475,8 +477,8 @@ cdef class Rational(sage.structure.element.FieldElement): 2/3 sage: a.__init__('-h/3ki', 32); a -17/3730 - sage: from gmpy2 import mpq - sage: a.__init__(mpq('3/5')); a + sage: from gmpy2 import mpq # optional - gmpy2 + sage: a.__init__(mpq('3/5')); a # optional - gmpy2 3/5 TESTS: @@ -526,24 +528,6 @@ cdef class Rational(sage.structure.element.FieldElement): return int(self) raise TypeError("rational is not an integer") - def __mpz__(self): - """ - Return a gmpy2 ``mpz`` if this Rational is an integer. - - EXAMPLES:: - sage: q = Rational(6/2) - sage: q.__mpz__() - mpz(3) - sage: q = Rational(1/4) - sage: q.__mpz__() - Traceback (most recent call last): - ... - TypeError: rational is not an integer - """ - if self.denominator() == 1: - return self.numerator().__mpz__() - raise TypeError("rational is not an integer") - def _reduce_set(self, s): """ Used in setting a rational number when unpickling. Do not call this @@ -677,11 +661,11 @@ cdef class Rational(sage.structure.element.FieldElement): mpz_set(mpq_numref(self.value), ( integer.Integer(x.numerator)).value) mpz_set(mpq_denref(self.value), ( integer.Integer(x.denominator)).value) - elif MPQ_Check( x): - mpq_set(self.value, MPQ( x)) + elif HAVE_GMPY2 and type(x) is gmpy2.mpq: + mpq_set(self.value, (x).q) - elif MPZ_Check( x): - mpq_set_z(self.value, MPZ( x)) + elif HAVE_GMPY2 and type(x) is gmpy2.mpz: + mpq_set_z(self.value, (x).z) else: raise TypeError("unable to convert {!r} to a rational".format(x)) @@ -985,6 +969,32 @@ cdef class Rational(sage.structure.element.FieldElement): import sympy return sympy.Rational(int(self.numerator()), int(self.denominator())) + def __mpz__(self): + """ + Return a gmpy2 ``mpz`` if this Rational is an integer. + + EXAMPLES:: + + sage: q = 6/2 + sage: q.__mpz__() # optional - gmpy2 + mpz(3) + sage: q = 1/4 + sage: q.__mpz__() # optional - gmpy2 + Traceback (most recent call last): + ... + TypeError: rational is not an integer + + TESTS:: + + sage: QQ().__mpz__(); raise NotImplementedError("gmpy2 is not installed") + Traceback (most recent call last): + ... + NotImplementedError: gmpy2 is not installed + """ + if self.denominator() != 1: + raise TypeError("rational is not an integer") + return self.numerator().__mpz__() + def __mpq__(self): """ Convert Sage ``Rational`` to gmpy2 ``Rational``. @@ -992,15 +1002,23 @@ cdef class Rational(sage.structure.element.FieldElement): EXAMPLES:: sage: r = 5/3 - sage: r.__mpq__() + sage: r.__mpq__() # optional - gmpy2 + mpq(5,3) + sage: from gmpy2 import mpq # optional - gmpy2 + sage: mpq(r) # optional - gmpy2 mpq(5,3) - sage: from gmpy2 import mpq - sage: r=3/4 - sage: mpq(r) - mpq(3,4) + TESTS:: + + sage: r.__mpq__(); raise NotImplementedError("gmpy2 is not installed") + Traceback (most recent call last): + ... + NotImplementedError: gmpy2 is not installed """ - return GMPy_MPQ_From_mpq(self.value) + IF HAVE_GMPY2: + return gmpy2.GMPy_MPQ_From_mpq(self.value) + ELSE: + raise NotImplementedError("gmpy2 is not installed") def _magma_init_(self, magma): """ diff --git a/src/sage/rings/real_double.pyx b/src/sage/rings/real_double.pyx index 9ce84750d20..f11628c2bd9 100644 --- a/src/sage/rings/real_double.pyx +++ b/src/sage/rings/real_double.pyx @@ -71,7 +71,9 @@ from sage.structure.coerce cimport is_numpy_type from sage.misc.randstate cimport randstate, current_randstate from sage.structure.richcmp cimport rich_to_bool from sage.arith.constants cimport * -from gmpy2 import mpfr + +IF HAVE_GMPY2: + cimport gmpy2 def is_RealDoubleField(x): @@ -709,12 +711,12 @@ cdef class RealDoubleElement(FieldElement): TESTS:: - sage: from gmpy2 import mpz, mpq, mpfr - sage: RDF(mpz(42)) + sage: from gmpy2 import * # optional - gmpy2 + sage: RDF(mpz(42)) # optional - gmpy2 42.0 - sage: RDF(mpq(3/4)) + sage: RDF(mpq(3/4)) # optional - gmpy2 0.75 - sage: RDF(mpq('4.1')) + sage: RDF(mpq('4.1')) # optional - gmpy2 4.1 """ self._value = float(x) @@ -938,13 +940,23 @@ cdef class RealDoubleElement(FieldElement): EXAMPLES:: - sage: RDF(42.2).__mpfr__() + sage: RDF(42.2).__mpfr__() # optional - gmpy2 mpfr('42.200000000000003') - sage: from gmpy2 import mpfr - sage: mpfr(RDF(5.1)) + sage: from gmpy2 import mpfr # optional - gmpy2 + sage: mpfr(RDF(5.1)) # optional - gmpy2 mpfr('5.0999999999999996') + + TESTS:: + + sage: RDF().__mpfr__(); raise NotImplementedError("gmpy2 is not installed") + Traceback (most recent call last): + ... + NotImplementedError: gmpy2 is not installed """ - return mpfr(self._value) + IF HAVE_GMPY2: + return gmpy2.mpfr(self._value) + ELSE: + raise NotImplementedError("gmpy2 is not installed") def _interface_init_(self, I=None): """ diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 9e561368f02..4042ddc2b5d 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -124,7 +124,6 @@ import sys import re from cpython.object cimport Py_NE -from cpython cimport PyObject from cysignals.signals cimport sig_on, sig_off from sage.ext.stdsage cimport PY_NEW @@ -164,9 +163,11 @@ import sage.rings.infinity from sage.structure.parent_gens cimport ParentWithGens from sage.arith.numerical_approx cimport digits_to_bits -from gmpy2 cimport MPQ, MPQ_Object, import_gmpy2, MPZ_Check, MPQ_Check, \ - MPZ, MPZ_Object, MPFR, MPFR_Object, MPFR_Check, GMPy_MPFR_From_mpfr -import_gmpy2() + +IF HAVE_GMPY2: + cimport gmpy2 + gmpy2.import_gmpy2() + #***************************************************************************** # @@ -1351,19 +1352,6 @@ cdef class RealNumber(sage.structure.element.RingElement): sage: R('1.2456') 1.2 - EXAMPLES: gmpy2 numbers - - :: - - sage: from gmpy2 import * - sage: rf = RealField() - sage: rf(mpz(5)) - 5.00000000000000 - sage: rf(mpq(1/2)) - 0.500000000000000 - sage: rf(mpfr('42.1')) - 42.1000000000000 - EXAMPLES: Rounding Modes :: @@ -1378,6 +1366,16 @@ cdef class RealNumber(sage.structure.element.RingElement): sage: RealField(2, rnd="RNDN")(w).str(2) '10.' + Conversion from gmpy2 numbers:: + + sage: from gmpy2 import * # optional - gmpy2 + sage: RR(mpz(5)) # optional - gmpy2 + 5.00000000000000 + sage: RR(mpq(1/2)) # optional - gmpy2 + 0.500000000000000 + sage: RR(mpfr('42.1')) # optional - gmpy2 + 42.1000000000000 + .. NOTE:: A real number is an arbitrary precision mantissa with a @@ -1466,12 +1464,12 @@ cdef class RealNumber(sage.structure.element.RingElement): mpfr_set_d(self.value, x, parent.rnd) elif isinstance(x, RealDoubleElement): mpfr_set_d(self.value, (x)._value, parent.rnd) - elif MPFR_Check( x): - mpfr_set(self.value, MPFR( x),parent.rnd) - elif MPQ_Check( x): - mpfr_set_q(self.value, MPQ( x), parent.rnd) - elif MPZ_Check( x): - mpfr_set_z(self.value, MPZ( x), parent.rnd) + elif HAVE_GMPY2 and type(x) is gmpy2.mpfr: + mpfr_set(self.value, (x).f, parent.rnd) + elif HAVE_GMPY2 and type(x) is gmpy2.mpq: + mpfr_set_q(self.value, (x).q, parent.rnd) + elif HAVE_GMPY2 and type(x) is gmpy2.mpz: + mpfr_set_z(self.value, (x).z, parent.rnd) else: s = str(x).replace(' ','') s_lower = s.lower() @@ -3735,20 +3733,29 @@ cdef class RealNumber(sage.structure.element.RingElement): EXAMPLES:: - sage: rf = RealField() - sage: r = rf(4.12) - sage: r.__mpfr__() + sage: r = RR(4.12) + sage: r.__mpfr__() # optional - gmpy2 mpfr('4.1200000000000001') - sage: from gmpy2 import mpfr - sage: r= rf(4.5) - sage: mpfr(r) + sage: from gmpy2 import mpfr # optional - gmpy2 + sage: mpfr(RR(4.5)) # optional - gmpy2 mpfr('4.5') sage: R = RealField(256) - sage: r = mpfr(R.pi()) - sage: r.precision + sage: x = mpfr(R.pi()) # optional - gmpy2 + sage: x.precision # optional - gmpy2 256 + + TESTS:: + + sage: r.__mpfr__(); raise NotImplementedError("gmpy2 is not installed") + Traceback (most recent call last): + ... + NotImplementedError: gmpy2 is not installed """ - return GMPy_MPFR_From_mpfr(self.value) + IF HAVE_GMPY2: + return gmpy2.GMPy_MPFR_From_mpfr(self.value) + ELSE: + raise NotImplementedError("gmpy2 is not installed") + ########################################### # Comparisons: ==, !=, <, <=, >, >= ########################################### From 2604a3745b9636c5de4bb9a84ee88055c1681c90 Mon Sep 17 00:00:00 2001 From: Vincent Klein Date: Thu, 16 Nov 2017 17:31:29 +0100 Subject: [PATCH 488/740] trac 22928 : add __mpc__ function to Sage complex numbers Sage complex numbers constructors now accept gmpy number parameters : MPComplexNumber constructor accept gmpy2's mpc, mpfr, mpq and mpz. ComplexDoubleElement and ComplexNumber accept gmpy2.mpc parameter. --- src/sage/rings/complex_double.pyx | 37 ++++++++++++++++++ src/sage/rings/complex_mpc.pyx | 62 +++++++++++++++++++++++++++++++ src/sage/rings/complex_number.pyx | 42 +++++++++++++++++++++ 3 files changed, 141 insertions(+) diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index cc9fd7b4906..a47af748358 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -50,6 +50,10 @@ AUTHORS: - Travis Scrimshaw (2012-10-18): Added doctests to get full coverage - Jeroen Demeyer (2013-02-27): fixed all PARI calls (:trac:`14082`) + +- Vincent Klein (2017-11-15) : add __mpc__() to class ComplexDoubleElement. + ComplexDoubleElement constructor support and gmpy2.mpc parameter. + """ #***************************************************************************** @@ -103,6 +107,9 @@ from .real_double cimport RealDoubleElement, double_repr, double_str from .real_double import RDF from sage.rings.integer_ring import ZZ +IF HAVE_GMPY2: + cimport gmpy2 + gmpy2.import_gmpy2() def is_ComplexDoubleField(x): """ @@ -293,6 +300,9 @@ cdef class ComplexDoubleField_class(sage.rings.ring.Field): 1.0*I sage: CDF(pari("x^2 + x + 1").polroots()[0]) -0.5 - 0.8660254037844386*I + sage: from gmpy2 import mpc # optional - gmpy2 + sage: CDF(mpc('2.0+1.0j')) # optional - gmpy2 + 2.0 + 1.0*I A ``TypeError`` is raised if the coercion doesn't make sense:: @@ -342,6 +352,8 @@ cdef class ComplexDoubleField_class(sage.rings.ring.Field): return ComplexDoubleElement(x.real(), x.imag()) elif isinstance(x, pari_gen): return pari_to_cdf(x) + elif HAVE_GMPY2 and type(x) is gmpy2.mpc: + return ComplexDoubleElement((x).real, (x).imag) elif isinstance(x, str): t = cdf_parser.parse_expression(x) if isinstance(t, float): @@ -1123,6 +1135,31 @@ cdef class ComplexDoubleElement(FieldElement): else: return new_t_COMPLEX_from_double(self._complex.dat[0], self._complex.dat[1]) + def __mpc__(self): + """ + Convert Sage ``ComplexDoubleElement`` to gmpy2 ``mpc``. + + EXAMPLES:: + + sage: c = CDF(2,1) + sage: c.__mpc__() # optional - gmpy2 + mpc('2.0+1.0j') + sage: from gmpy2 import mpc # optional - gmpy2 + sage: mpc(c) # optional - gmpy2 + mpc('2.0+1.0j') + + TESTS:: + + sage: c.__mpc__(); raise NotImplementedError("gmpy2 is not installed") + Traceback (most recent call last): + ... + NotImplementedError: gmpy2 is not installed + """ + IF HAVE_GMPY2: + return gmpy2.mpc(self._complex.dat[0], self._complex.dat[1]) + ELSE: + raise NotImplementedError("gmpy2 is not installed") + ####################################################################### # Arithmetic ####################################################################### diff --git a/src/sage/rings/complex_mpc.pyx b/src/sage/rings/complex_mpc.pyx index 824fe55a382..eb499aad01a 100644 --- a/src/sage/rings/complex_mpc.pyx +++ b/src/sage/rings/complex_mpc.pyx @@ -28,6 +28,10 @@ AUTHORS: - Travis Scrimshaw (2012-10-18): Added doctests for full coverage. +- Vincent Klein (2017-11-15) : add __mpc__() to class MPComplexNumber. + MPComplexNumber constructor support gmpy2.mpz, gmpy2.mpq, gmpy2.mpfr + and gmpy2.mpc parameters. + EXAMPLES:: sage: MPC = MPComplexField(42) @@ -82,6 +86,10 @@ from .real_mpfr import mpfr_prec_min, mpfr_prec_max from sage.structure.richcmp cimport rich_to_bool, richcmp from sage.categories.fields import Fields +IF HAVE_GMPY2: + cimport gmpy2 + gmpy2.import_gmpy2() + NumberFieldElement_quadratic = None AlgebraicNumber_base = None AlgebraicNumber = None @@ -811,6 +819,22 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): 3.1415926535897932384626433833*I sage: MPC('0.625e-26', '0.0000001') 6.2500000000000000000000000000e-27 + 1.0000000000000000000000000000e-7*I + + Conversion from gmpy2 numbers:: + + sage: from gmpy2 import * # optional - gmpy2 + sage: MPC(mpc(int(2),int(1))) # optional - gmpy2 + 2.0000000000000000000000000000 + 1.0000000000000000000000000000*I + sage: MPC(mpfr(2.5)) # optional - gmpy2 + 2.5000000000000000000000000000 + sage: MPC(mpq('3/2')) # optional - gmpy2 + 1.5000000000000000000000000000 + sage: MPC(mpz(int(5))) # optional - gmpy2 + 5.0000000000000000000000000000 + sage: re = mpfr('2.5') # optional - gmpy2 + sage: im = mpz(int(2)) # optional - gmpy2 + sage: MPC(re, im) # optional - gmpy2 + 2.5000000000000000000000000000 + 2.0000000000000000000000000000*I """ # This should not be called except when the number is being created. # Complex Numbers are supposed to be immutable. @@ -849,7 +873,19 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): zz = sage.rings.complex_field.ComplexField(self._parent.prec())(z) self._set(zz) return + elif HAVE_GMPY2 and type(z) is gmpy2.mpc: + mpc_set(self.value, (z).c, rnd) + return # then, no imaginary part + elif HAVE_GMPY2 and type(z) is gmpy2.mpfr: + mpc_set_fr(self.value, (z).f, rnd) + return + elif HAVE_GMPY2 and type(z) is gmpy2.mpq: + mpc_set_q(self.value, (z).q, rnd) + return + elif HAVE_GMPY2 and type(z) is gmpy2.mpz: + mpc_set_z(self.value, (z).z, rnd) + return elif isinstance(z, RealNumber): zz = sage.rings.real_mpfr.RealField(self._parent.prec())(z) mpc_set_fr(self.value, (zz).value, rnd) @@ -1223,6 +1259,32 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): im = self.imag().__pari__() return pari.complex(re, im) + def __mpc__(self): + """ + Convert Sage ``MPComplexNumber`` to gmpy2 ``mpc``. + + EXAMPLES:: + + sage: MPC = MPComplexField() + sage: c = MPC(2,1) + sage: c.__mpc__() # optional - gmpy2 + mpc('2.0+1.0j') + sage: from gmpy2 import mpc # optional - gmpy2 + sage: mpc(c) # optional - gmpy2 + mpc('2.0+1.0j') + + TESTS:: + + sage: c.__mpc__(); raise NotImplementedError("gmpy2 is not installed") + Traceback (most recent call last): + ... + NotImplementedError: gmpy2 is not installed + """ + IF HAVE_GMPY2: + return gmpy2.GMPy_MPC_From_mpfr(self.value.re, self.value.im) + ELSE: + raise NotImplementedError("gmpy2 is not installed") + cpdef int _cmp_(self, other) except -2: r""" Compare ``self`` to ``other``. diff --git a/src/sage/rings/complex_number.pyx b/src/sage/rings/complex_number.pyx index 16604510037..c3ede9393c9 100644 --- a/src/sage/rings/complex_number.pyx +++ b/src/sage/rings/complex_number.pyx @@ -12,6 +12,9 @@ AUTHORS: - Vincent Delecroix (2010-01): plot function - Travis Scrimshaw (2012-10-18): Added documentation for full coverage + +- Vincent Klein (2017-11-14) : add __mpc__() to class ComplexNumber. + ComplexNumber constructor support gmpy2.mpc parameter. """ #***************************************************************************** @@ -28,6 +31,7 @@ from __future__ import absolute_import, print_function import math import operator +from sage.libs.mpc cimport * from sage.libs.mpfr cimport * from sage.structure.element cimport FieldElement, RingElement, Element, ModuleElement from sage.categories.map cimport Map @@ -43,6 +47,9 @@ import sage.rings.infinity as infinity from sage.libs.mpmath.utils cimport mpfr_to_mpfval from sage.rings.integer_ring import ZZ +IF HAVE_GMPY2: + cimport gmpy2 + gmpy2.import_gmpy2() cdef object numpy_complex_interface = {'typestr': '=c16'} cdef object numpy_object_interface = {'typestr': '|O'} @@ -157,6 +164,13 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): 2.00000000000000 sage: imag(a) 1.00000000000000 + + Conversion from gmpy2 numbers:: + + sage: from gmpy2 import * # optional - gmpy2 + sage: c = mpc('2.0+1.0j') # optional - gmpy2 + sage: CC(c) # optional - gmpy2 + 2.00000000000000 + 1.00000000000000*I """ cdef RealNumber rr, ii self._parent = parent @@ -178,6 +192,8 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): real = re elif isinstance(real, complex): real, imag = real.real, real.imag + elif HAVE_GMPY2 and type(real) is gmpy2.mpc: + real, imag = (real).real, (real).imag else: imag = 0 try: @@ -579,6 +595,32 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): return self.real().__pari__() return sage.libs.pari.all.pari.complex(self.real() or 0, self.imag()) + def __mpc__(self): + """ + Convert Sage ``ComplexNumber`` to gmpy2 ``mpc``. + + EXAMPLES:: + + sage: c = ComplexNumber(2,1) + sage: c.__mpc__() # optional - gmpy2 + mpc('2.0+1.0j') + sage: from gmpy2 import mpc # optional - gmpy2 + sage: mpc(c) # optional - gmpy2 + mpc('2.0+1.0j') + + TESTS:: + + sage: c.__mpc__(); raise NotImplementedError("gmpy2 is not installed") + Traceback (most recent call last): + ... + NotImplementedError: gmpy2 is not installed + """ + IF HAVE_GMPY2: + return gmpy2.GMPy_MPC_From_mpfr(self.__re, self.__im) + ELSE: + raise NotImplementedError("gmpy2 is not installed") + + def _mpmath_(self, prec=None, rounding=None): """ Returns an mpmath version of ``self``. From 1695bd8153a5e00107d3bccdeb5e09c7d2b14660 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Wed, 17 Jan 2018 18:48:56 +0100 Subject: [PATCH 489/740] bugfixes --- .../groups/abelian_gps/abelian_group_gap.py | 175 ++++++++++-------- src/sage/groups/libgap_wrapper.pyx | 8 +- 2 files changed, 101 insertions(+), 82 deletions(-) diff --git a/src/sage/groups/abelian_gps/abelian_group_gap.py b/src/sage/groups/abelian_gps/abelian_group_gap.py index 1a81f523237..d037572e45a 100644 --- a/src/sage/groups/abelian_gps/abelian_group_gap.py +++ b/src/sage/groups/abelian_gps/abelian_group_gap.py @@ -8,7 +8,7 @@ sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: AbelianGroupGap([3,5]) Abelian group with gap, generator orders (3, 5) - + For infinite abelian groups we use the gap package Polycyclic:: sage: AbelianGroupGap([3,0]) # optional gap_packages @@ -43,19 +43,19 @@ def AbelianGroupGap(generator_orders): r""" Create the multiplicative abelian group with given orders of generators. - + INPUT: - + - ``generator_orders`` -- a list of nonnegative integers where `0` gives a factor isomorphic to `\ZZ`. - + OUTPUT: - - - an abelian group - + + - an abelian group + EXAMPLES:: - + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap - sage: AbelianGroupGap([3,6]) + sage: AbelianGroupGap([3,6]) Abelian group with gap, generator orders (3, 6) sage: AbelianGroupGap([3,6,5]) Abelian group with gap, generator orders (3, 6, 5) @@ -67,18 +67,18 @@ def AbelianGroupGap(generator_orders): return ValueError("Generator orders must be nonnegative") category = Groups().Commutative() if 0 in generator_orders: - category = category.Finite().Enumerated() - else: category = category.Infinite() + else: + category = category.Finite().Enumerated() polycyclic_package = libgap.LoadPackage("Polycyclic") - return AbelianGroupAmbient_gap(generator_orders, polycyclic_package=polycyclic_package, category=None) + return AbelianGroupAmbient_gap(generator_orders, polycyclic_package=polycyclic_package, category=category) class AbelianGroupElement_gap(ElementLibGAP): r""" An element of an abelian group via libgap. - + EXAMPLES:: - + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: G = AbelianGroupGap([3,6]) sage: G.gens() @@ -89,12 +89,12 @@ def __init__(self, parent, x, check=True): The Python constructor. See :class:`AbelianGroupElement_gap` for details. - + INPUT: - + - ``parent`` -- an instance of :class:`AbelianGroup_gap` - ``x`` -- an instance of :class:`sage.libs.gap.element.GapElement` - - ``check`` -- boolean (default: ``True``) check + - ``check`` -- boolean (default: ``True``) check if ``x`` is an element of the group TESTS:: @@ -126,9 +126,9 @@ def __hash__(self): def __reduce__(self): r""" Implement pickling. - + OUTPUT: - + - a tuple ``f`` such that this element is ``f[0](*f[1])`` EXAMPLES:: @@ -146,17 +146,17 @@ def __reduce__(self): def _repr_(self): """ The string representation of this element. - + OUTPUT: - + - a string - + EXAMPLES:: sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: G = AbelianGroupGap([3,2,4]) sage: g = G.an_element() - sage: g._repr_() + sage: g._repr_() 'g1*g2*g3' """ rep = self.gap()._repr_() @@ -164,10 +164,10 @@ def _repr_(self): def exponents(self): r""" - Return the tuple of exponents. - + Return the tuple of exponents of this element. + OUTPUT: - + - a tuple of sage integers EXAMPLES:: @@ -178,13 +178,20 @@ def exponents(self): sage: g = gens[0]^2 * gens[1]^4 * gens[2]^8 sage: g.exponents() (2, 4, 8) + sage: S = G.subgroup(G.gens()[:1]) + sage: s = S.gens()[0] + sage: s + g1 + sage: s.exponents() + (1,) sage: G = AbelianGroupGap([4,7,0]) # optional - gap_packages sage: gens = G.gens() sage: g = gens[0]^2 * gens[1]^4 * gens[2]^8 sage: g.exponents() (2, 4, 8) """ - if self.parent()._with_pc: + P = self.parent() + if (not P.is_subgroup()) and P._with_pc: exp = self.gap().Exponents().sage() else: # works only for small groups @@ -208,9 +215,9 @@ def exponents(self): def order(self): r""" Return the order of this element. - + OUTPUT: - + - an integer or infinity EXAMPLES:: @@ -227,7 +234,7 @@ def order(self): """ return self.gap().Order().sage() -class AbelianGroup_gap(UniqueRepresentation,GroupMixinLibGAP, ParentLibGAP, AbelianGroupBase): +class AbelianGroup_gap(UniqueRepresentation, GroupMixinLibGAP, ParentLibGAP, AbelianGroupBase): r""" Python wrapper for finitely generated abelian groups in gap. @@ -244,7 +251,7 @@ class AbelianGroup_gap(UniqueRepresentation,GroupMixinLibGAP, ParentLibGAP, Abel sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: G = AbelianGroupGap([3,2,5]) - sage: G + sage: G Abelian group with gap, generator orders (3, 2, 5) """ def __init__(self, G, ambient=None, polycyclic_package=False, category=None): @@ -294,13 +301,13 @@ def _coerce_map_from_(self, S): def _element_constructor_(self,x,check=True): r""" Defines coercions and conversions. - + INPUT: - + - ``x`` -- an element of this group, a gap element - + EXAMPLES:: - + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: G = AbelianGroupGap([2,3]) sage: A = AbelianGroup([2,3]) @@ -315,27 +322,27 @@ def _element_constructor_(self,x,check=True): (1, 0) sage: G(a) g1 - + For general fgp_modules conversion is implemented if our group is in smith form:: - + sage: G = AbelianGroupGap([6]) sage: A = ZZ^2 sage: A = A / A.submodule([2*A.0, 3*A.1]) sage: a = 2 * A.an_element() - sage: a + sage: a (2) sage: G(a) g1^2 """ if isinstance(x, AbelianGroupElement_gap): x = x.gap() - elif x == 1: + elif x==1 or x==(): x = self.gap().Identity() elif not isinstance(x, GapElement): from sage.groups.abelian_gps.abelian_group_element import AbelianGroupElement from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroupElement from sage.modules.fg_pid.fgp_element import FGP_Element - if isinstance(x, AbelianGroupElement): + if isinstance(x, AbelianGroupElement): exp = x.exponents() elif isinstance(x, AdditiveAbelianGroupElement): exp = x._hermite_lift() @@ -357,9 +364,9 @@ def _element_constructor_(self,x,check=True): def all_subgroups(self): r""" Return the list of all subgroups of this group. - + EXAMPLES:: - + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: G = AbelianGroupGap([2,3]) sage: G.all_subgroups() @@ -400,7 +407,7 @@ def identity(self): sage: G.identity() id """ - return self(self.gap().Identity()) + return self.one() @cached_method def elementary_divisors(self): @@ -429,11 +436,11 @@ def exponent(self): EXAMPLES:: sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap - sage: G = AbelianGroupGap([2,3,7]) + sage: G = AbelianGroupGap([2,3,7]) sage: G Abelian group with gap, generator orders (2, 3, 7) sage: G = AbelianGroupGap([2,4,6]) - sage: G + sage: G Abelian group with gap, generator orders (2, 4, 6) sage: G.exponent() 12 @@ -482,9 +489,9 @@ def gens_orders(self): def is_subgroup_of(self, G): r""" Return if ``self`` is a subgroup of ``G`` considered in the same ambient group. - + EXAMPLES:: - + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: G = AbelianGroupGap([2,3,4,5]) sage: gen = G.gens()[:2] @@ -519,7 +526,7 @@ def subgroup(self, gens): sage: G = AbelianGroupGap([2,3,4,5]) sage: gen = G.gens()[:2] sage: S = G.subgroup(gen) - sage: S + sage: S Subgroup of Abelian group with gap, generator orders (2, 3, 4, 5) generated by (g1, g2) sage: g = G.an_element() sage: s = S.an_element() @@ -541,11 +548,11 @@ def subgroup(self, gens): """ gens = tuple(self(g) for g in gens) return AbelianGroupSubgroup_gap(self.ambient(), gens) - + class AbelianGroupAmbient_gap(AbelianGroup_gap): r""" Ambient abelian groups with gap. - + Do not use this class directly. Instead use :meth:`AbelianGroupGap`. Needs the gap package "Polycyclic" in case the group is infinite. @@ -554,20 +561,31 @@ class AbelianGroupAmbient_gap(AbelianGroup_gap): - ``generator_orders`` - a tuple of nonnegative integers - ``polycyclic_package`` -- (default: ``False``) boolean - ``category`` -- a category - + EXAMPLES:: - + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupAmbient_gap sage: AbelianGroupAmbient_gap((2,3,4)) Abelian group with gap, generator orders (2, 3, 4) """ def __init__(self, generator_orders, polycyclic_package=False, category=None): + r""" + Constructor. + + See :class:`AbelianGroupAmbient_gap` for documentation. + + TESTS:: + + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupAmbient_gap + sage: A = AbelianGroup((2,3,4)) + sage: TestSuite(A).run() + """ if polycyclic_package: G = libgap.eval("AbelianPcpGroup(%s)"%list(generator_orders)) else: G = libgap.AbelianGroup(generator_orders) AbelianGroup_gap.__init__(self, G, ambient=None, polycyclic_package=polycyclic_package, category=category) - + def _latex_(self): """ Return the latex representation of this group. @@ -576,7 +594,7 @@ def _latex_(self): sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: G = AbelianGroupGap([2,6]) - sage: G._latex_() + sage: G._latex_() 'Abelian group with gap, generator orders $(2, 6)$' """ return "Abelian group with gap, generator orders $" + str(self.gens_orders()) + "$" @@ -589,39 +607,39 @@ def _repr_(self): sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: G = AbelianGroupGap([2,6]) - sage: G._repr_() + sage: G._repr_() 'Abelian group with gap, generator orders (2, 6)' """ return "Abelian group with gap, generator orders " + str(self.gens_orders()) - + def __reduce__(self): r""" Implements pickling. - + We have to work around the fact that gap does not provide pickling. - + EXAMPLES:: - + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: G = AbelianGroupGap([3,2,5]) sage: G == loads(dumps(G)) True """ return AbelianGroupGap, (self.gens_orders(),) - + class AbelianGroupSubgroup_gap(AbelianGroup_gap): r""" Subgroups of abelian groups with gap. - + Do not use this class directly. Instead use :meth:`subgroup`. - + INPUT: - + - ``ambient`` -- the ambient group - ``gens`` -- generators of the subgroup - + EXAMPLES:: - + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: G = AbelianGroupGap([2,3,4,5]) sage: gen = G.gens()[:2] @@ -629,47 +647,46 @@ class AbelianGroupSubgroup_gap(AbelianGroup_gap): """ def __init__(self, ambient, gens): r""" - Initialize this module - + Initialize this subgroup. + TESTS:: - + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap,AbelianGroupSubgroup_gap sage: G = AbelianGroupGap([]) sage: gen = G.gens() - sage: AbelianGroupSubgroup_gap(G, gen) - Subgroup of Abelian group with gap, generator orders () generated by () + sage: A = AbelianGroupSubgroup_gap(G, gen) + sage: TestSuite(A).run() """ polycyclic_package = ambient._with_pc category = ambient.category() gens_gap = tuple(g.gap() for g in gens) G = ambient.gap().Subgroup(gens_gap) AbelianGroup_gap.__init__(self, G, ambient=ambient, polycyclic_package=polycyclic_package, category=category) - + def __repr__(self): r""" Return the string representation of this subgroup. - + EXAMPLES:: - + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: G = AbelianGroupGap([2,3,4,5]) sage: gen = G.gens()[:2] sage: S = G.subgroup(gen) - sage: S.__repr__() + sage: S.__repr__() 'Subgroup of Abelian group with gap, generator orders (2, 3, 4, 5) generated by (g1, g2)' """ s = "Subgroup of %s generated by %s"%(self.ambient(),self.gens()) return s - - + def __reduce__(self): r""" Implements pickling. - + We have to work around the fact that gap does not provide pickling. - + EXAMPLES:: - + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: G = AbelianGroupGap([2,3,4,5]) sage: gen = G.gens()[:2] diff --git a/src/sage/groups/libgap_wrapper.pyx b/src/sage/groups/libgap_wrapper.pyx index 8f76df86089..fe3457d0304 100644 --- a/src/sage/groups/libgap_wrapper.pyx +++ b/src/sage/groups/libgap_wrapper.pyx @@ -393,9 +393,11 @@ class ParentLibGAP(SageObject): a*b """ from sage.misc.all import prod - return prod(self.gens()) - - + gens = self.gens() + if gens: + return prod(gens) + else: + return self.one() cdef class ElementLibGAP(MultiplicativeGroupElement): """ From ea347f74dd96d9955622a930f9658560e0ead65d Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Wed, 17 Jan 2018 17:06:43 -0800 Subject: [PATCH 490/740] Added class of entries --- src/sage/combinat/shifted_primed_tableau.py | 405 +++++++++++--------- 1 file changed, 225 insertions(+), 180 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index 38fcd2c21bc..e58503eca16 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -25,6 +25,9 @@ from sage.combinat.skew_partition import SkewPartition from sage.combinat.integer_vector import IntegerVectors from sage.rings.integer import Integer +from sage.rings.rational_field import QQ +from sage.rings.rational import Rational +from sage.rings.integer_ring import ZZ from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.misc.lazy_attribute import lazy_attribute @@ -58,19 +61,19 @@ class ShiftedPrimedTableau(ClonableArray): sage: T = ShiftedPrimedTableaux([4,2]) sage: T([[1,"2'","3'",3],[2,"3'"]])[1] - (2.0, 2.5) - sage: t = ShiftedPrimedTableau([[1,"2p",2.5,3],[0,2,2.5]]) + (2, 3') + sage: t = ShiftedPrimedTableau([[1,"2p",2.5,3],[2,2.5]]) sage: t[1] - (2.0, 2.5) + (2, 3') sage: ShiftedPrimedTableau([["2p",2,3],["2p","3p"],[2]], skew=[2,1]) - [(None, None, 1.5, 2.0, 3.0), (None, 1.5, 2.5), (2.0,)] + [(None, None, 2', 2, 3), (None, 2', 3'), (2,)] TESTS:: - sage: t = ShiftedPrimedTableau([[1,2,2.5,3],[0,2,2.5]]) + sage: t = ShiftedPrimedTableau([[1,2,2.5,3],[2,2.5]]) Traceback (most recent call last): ... - ValueError: [[1, 2, 2.50000000000000, 3], [0, 2, 2.50000000000000]] + ValueError: [[1, 2, 2.50000000000000, 3], [2, 2.50000000000000]] is not an element of Shifted Primed Tableaux """ @staticmethod @@ -105,7 +108,7 @@ def __init__(self, parent, T, skew=None, check=True, preprocessed=False): sage: s = ShiftedPrimedTableau([[1,"2'","3'",3],[2,"3'"]]) sage: t = ShiftedPrimedTableaux([4,2])([[1,"2p","3p",3], - ....: [0, 2,"3p"]]) + ....: [2,"3p"]]) sage: s==t True sage: t.parent() @@ -141,53 +144,15 @@ def _preprocess_(T, skew=None): if isinstance(T, ShiftedPrimedTableau): return T - t = [] # Preprocessing list t for primes and other symbols - for row in T: - row_new = [] - for element in row: - - if isinstance(element, str): - if element[-1] == "'" and element[:-1].isdigit() is True: - # Check if an element has "'" at the end - row_new.append(float(float(element[:-1]) - .5)) - continue - if element[-1] == "p" and element[:-1].isdigit() is True: - # Check if an element has "p" at the end - row_new.append(float(float(element[:-1]) - .5)) - continue - try: - if int(float(element)*2) == float(element)*2: - # Check if an element is a half-integer - row_new.append(float(element)) - continue - else: - raise ValueError("all numbers must be half-integers") - except (TypeError, ValueError): - raise ValueError("primed elements have wrong format") - t.append(row_new) - - # Accounting for zeros at the beginning and at the end of a row' - i = 0 - while i < len(t): - row = t[i] - try: - while row[0] == 0: - row.pop(0) - while row[-1] == 0: - row.pop(-1) - except IndexError: - t.remove(t[i]) - continue - t[i] = row - i += 1 + T = [[ShiftedPrimedTableauEntry(entry) for entry in row] for row in T] if skew is not None: - T = ([(None,)*skew[i] + tuple(t[i]) + T = ([(None,)*skew[i] + tuple(T[i]) for i in range(len(skew))] - + [tuple(t[i]) for i in range(len(skew), len(t))]) + + [tuple(T[i]) for i in range(len(skew), len(T))]) else: - T = [tuple(row) for row in t] + T = [tuple(row) for row in T] return T def check(self): @@ -259,32 +224,6 @@ def __ne__(self, other): return True return (self._skew != Tab._skew or list(self) != list(Tab)) - def _to_matrix(self): - """ - Return a 2-dimensional array representation of a shifted tableau. - - EXAMPLES:: - - sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p'],[3]]) - sage: mat = t._to_matrix() - sage: mat - [[1.0, 1.5, 2.0, 2.0], [0, 2.0, 2.5, 0], [0, 0, 3.0, 0]] - sage: t == ShiftedPrimedTableau(mat) - True - """ - if self._skew is not None: - raise NotImplementedError('skew tableau must be empty') - array = [] - m = self.shape()[0] - sk_len = 0 if self._skew is None else len(self._skew) - for i in range(sk_len): - array.append([0]*(i+self._skew[i]) - + list(self[i]) - + [0]*(m-i-self._skew[i]-len(self[i]))) - for i in range(sk_len, len(self)): - array.append([0]*i + list(self[i]) + [0]*(m-i-len(self[i]))) - return array - def _repr_(self): """ Return a string representation of ``self``. @@ -293,9 +232,9 @@ def _repr_(self): sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t - [(1.0, 1.5, 2.0, 2.0), (2.0, 2.5)] + [(1, 2', 2, 2), (2, 3')] sage: ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) - [(None, None, 1.5, 2.0, 3.0), (None, 1.5)] + [(None, None, 2', 2, 3), (None, 2')] """ return self.parent().options._dispatch(self, '_repr_', 'display') @@ -306,7 +245,7 @@ def _repr_list(self): EXAMPLES:: sage: print(ShiftedPrimedTableau([['2p',3],[2,2]], skew=[2])) - [(None, None, 1.5, 3.0), (2.0, 2.0)] + [(None, None, 2', 3), (2, 2)] """ return (repr([row for row in self])) @@ -323,21 +262,19 @@ def _repr_tab(self): sage: s._repr_tab() [[' . ', ' . ', " 2'", ' 2 ', ' 3 '], [' . ', " 2'"]] """ - if self._skew is not None: - skew = self._skew + [0]*(len(self)-len(self._skew)) - else: - skew = [0]*len(self) - max_len = len(str(self.max_entry()))+1 - string_tab = [] - for i, row in enumerate(self): - string_row = ['.'.rjust(max_len) + ' ']*(skew[i]) - for j in range(skew[i], len(row)): - if int(row[j]) == row[j]: - string_row.append(str(int(row[j])).rjust(max_len) + ' ') - else: - string_row.append(str(int(row[j]+.5)).rjust(max_len) + "'") - string_tab.append(string_row) - return string_tab + max_len = len(str(self.max_entry()))+2 + repr_tab = [] + for row in self: + repr_row = [] + for entry in row: + if entry is None: + repr_row.append('. '.rjust(max_len)) + elif entry.is_primed(): + repr_row.append(repr(entry).rjust(max_len)) + elif entry.is_unprimed(): + repr_row.append(repr(entry).rjust(max_len-1)+" ") + repr_tab.append(repr_row) + return repr_tab def _repr_diagram(self): """ @@ -562,8 +499,9 @@ def max_entry(self): if self == []: return 0 else: - flat = [item for sublist in self for item in sublist] - return int(round(max(flat))) + flat = [entry.unprime() for row in self + for entry in row if entry is not None] + return max(flat) def shape(self): """ @@ -597,14 +535,34 @@ def weight(self): sage: t.weight() (1, 4, 1) """ - flat = [round(item) for sublist in self for item in sublist] + flat = [entry.unprime() for row in self for entry in row] if flat == []: max_ind = 0 else: - max_ind = int(max(flat)) + max_ind = max(flat) weight = tuple([flat.count(i+1) for i in range(max_ind)]) return weight + +class CrystalElementShiftedPrimedTableau(ShiftedPrimedTableau): + """ + Class for elements of ``crystals.ShiftedPrimedTableau``. + """ + def _to_matrix(self): + """ + Return a 2-dimensional array representation of a shifted tableau. + + EXAMPLES:: + + sage: t = ShiftedPrimedTableaux([4,2,1])([[1,'2p',2,2],[2,'3p'],[3]]) + sage: mat = t._to_matrix() + sage: mat + [[1, 2', 2, 2], [None, 2, 3', None], [None, None, 3, None]] + """ + m = self.shape()[0] + return [[None]*i + list(row) + [None]*(m-i-len(row)) + for i, row in enumerate(self)] + def _reading_word_with_positions(self): """ Return the reading word of ``self`` together with positions of the @@ -625,26 +583,24 @@ def _reading_word_with_positions(self): EXAMPLES:: - sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t = ShiftedPrimedTableaux([4,2])([[1,'2p',2,2],[2,'3p']]) sage: t._reading_word_with_positions() [((1, 2), 3), ((0, 1), 2), ((1, 1), 2), ((0, 0), 1), ((0, 2), 2), ((0, 3), 2)] """ - if self._skew is not None: - raise NotImplementedError('skew tableau must be empty') mat = self._to_matrix() ndim, mdim = len(mat), len(mat[0]) list_with_positions = [] for j in reversed(range(mdim)): for i in range(ndim): x = mat[i][j] - if int(x) != x: - list_with_positions.append(((i, j), int(x+0.5))) + if x is not None and x.is_primed(): + list_with_positions.append(((i, j), x.unprime())) for i in reversed(range(ndim)): for j in range(mdim): x = mat[i][j] - if int(x) == x and int(x) != 0: - list_with_positions.append(((i, j), int(x))) + if x is not None and x.is_unprimed(): + list_with_positions.append(((i, j), x.unprime())) return list_with_positions def reading_word(self): @@ -666,7 +622,7 @@ def reading_word(self): EXAMPLES:: - sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t = ShiftedPrimedTableaux([4,2])([[1,'2p',2,2],[2,'3p']]) sage: t.reading_word() [3, 2, 2, 1, 2, 2] """ @@ -674,11 +630,6 @@ def reading_word(self): raise NotImplementedError('skew tableau must be empty') return [tup[1] for tup in self._reading_word_with_positions()] - -class CrystalElementShiftedPrimedTableau(ShiftedPrimedTableau): - """ - Documentation here - """ def f(self, ind): r""" Compute the action of the crystal operator `f_i` on a shifted primed @@ -737,18 +688,17 @@ def f(self, ind): (r, c), elt = element_to_change - if T[r][c] == ind - .5: - T = [[elt+.5 if elt != 0 else elt for elt in row] for row in T] + if T[r][c].is_primed(): + T = [[elt+.5 if elt is not None else elt + for elt in row] for row in T] T = map(list, zip(*T)) r, c = c, r h, l = len(T), len(T[0]) - if (c+1 == l or T[r][c+1] >= ind+1 or T[r][c+1] < 1): + if (c+1 == l or T[r][c+1] is None or T[r][c+1] >= ind+1): (tp_r, tp_c) = (r, c) while True: - if (tp_r+1 == h or - T[tp_r+1][tp_c] > ind+1 or - T[tp_r+1][tp_c] < 1): + if (tp_r+1 == h or T[tp_r+1][tp_c] is None or T[tp_r+1][tp_c] > ind+1): break if tp_r <= tp_c and T[tp_r+1][tp_r+1] == ind+1: tp_r += 1 @@ -760,24 +710,24 @@ def f(self, ind): tp_c = T[tp_r].index(ind+.5) if tp_r == r: - T[r][c] += 1 - + T[r][c] = T[r][c]+1 elif tp_r == tp_c: - T[r][c] += .5 - + T[r][c] = T[r][c]+.5 else: - T[r][c] += .5 - T[tp_r][tp_c] += .5 + T[r][c] = T[r][c]+.5 + T[tp_r][tp_c] = T[tp_r][tp_c]+.5 elif T[r][c+1] == ind+.5: - T[r][c+1] += .5 - T[r][c] += .5 + T[r][c+1] = T[r][c+1]+.5 + T[r][c] = T[r][c]+.5 if r > c: - T = [[elt-.5 if elt != 0 else elt for elt in row] for row in T] + T = [[elt-.5 if elt is not None else elt + for elt in row] for row in T] T = map(list, zip(*T)) - return type(self)(self.parent(), T, check=False) + T = [tuple(elt for elt in row if elt is not None) for row in T] + return type(self)(self.parent(), T, check=False, preprocessed=True) def e(self, ind): r""" @@ -812,8 +762,7 @@ def e(self, ind): T = self._to_matrix() read_word = self._reading_word_with_positions() - read_word = [num - for num in read_word + read_word = [num for num in read_word if num[1] == ind or num[1] == ind+1] element_to_change = None @@ -831,17 +780,16 @@ def e(self, ind): return None (r, c), elt = element_to_change - if T[r][c] == ind + .5: - T = [[elt+.5 if elt != 0 else elt for elt in row] for row in T] + if T[r][c].is_primed(): + T = [[elt+.5 if elt is not None else elt + for elt in row] for row in T] T = map(list, zip(*T)) r, c = c, r - if (c == 0 or T[r][c-1] <= ind or T[r][c-1] < 1): - + if (c == 0 or T[r][c-1] is None or T[r][c-1] <= ind): (tp_r, tp_c) = (r, c) while True: - - if tp_r == 0 or T[tp_r-1][tp_c] < ind or T[tp_r-1][tp_c] < 1: + if (tp_r == 0 or T[tp_r-1][tp_c] is None or T[tp_r-1][tp_c] < ind): break if (ind+.5 not in T[tp_r-1]): break @@ -849,24 +797,23 @@ def e(self, ind): tp_c = T[tp_r].index(ind+.5) if tp_r == r: - T[r][c] -= 1 - + T[r][c] = T[r][c]-1.0 elif tp_r == tp_c: - T[r][c] -= .5 - + T[r][c] = T[r][c]-.5 else: - T[r][c] -= .5 - T[tp_r][tp_c] -= .5 + T[r][c] = T[r][c]-.5 + T[tp_r][tp_c] = T[tp_r][tp_c]-.5 elif T[r][c-1] == ind+.5: - T[r][c-1] -= .5 - T[r][c] -= .5 - + T[r][c-1] = T[r][c-1]-.5 + T[r][c] = T[r][c]-.5 if r > c: - T = [[elt-.5 if elt != 0 else elt for elt in row] for row in T] + T = [[elt-.5 if elt is not None else elt + for elt in row] for row in T] T = map(list, zip(*T)) - return type(self)(self.parent(), T, check=False) + T = [tuple(elt for elt in row if elt is not None) for row in T] + return type(self)(self.parent(), T, check=False, preprocessed=True) def is_highest_weight(self, index_set=None): r""" @@ -911,15 +858,120 @@ def weight(self): sage: t.weight() (1, 4, 1) """ - flat = [round(item) for sublist in self for item in sublist] + flat = [entry.unprime() for row in self for entry in row] if flat == []: max_ind = 0 else: - max_ind = int(max(flat)) + max_ind = max(flat) weight = tuple([flat.count(i+1) for i in range(max_ind)]) return self.parent().weight_lattice_realization()(weight) +class ShiftedPrimedTableauEntry(Rational): + """ + The class of entries in shifted primed tableaux. + """ + def __init__(self, entry): + """ + Normalize the entry to be a half-integer in ``QQ``. + + TEST:: + sage: ShiftedPrimedTableau([[1,"2p"]])[0][1] + 2' + sage: ShiftedPrimedTableau([[1,"2'"]])[0][1] + 2' + sage: ShiftedPrimedTableau([[1,1.5]])[0][1] + 2' + """ + if isinstance(entry, str): + if (entry[-1] == "'" or entry[-1] == "p") and entry[:-1].isdigit() is True: + # Check if an element has "'" or "p" at the end + entry = QQ(entry[:-1]) - .5 + try: + if (QQ(entry)+.5 in ZZ) or (QQ(entry) in ZZ): + # Check if an element is a half-integer + entry = QQ(entry) + else: + raise ValueError("all numbers must be half-integers") + except (TypeError, ValueError): + raise ValueError("primed elements have wrong format") + Rational.__init__(self, entry) + + def __repr__(self): + """ + Represent ``self`` as primed or unprimed integer. + + TEST:: + sage: ShiftedPrimedTableau([[1,"2p"]])[0][1] + 2' + """ + if self.is_primed(): + return Integer(self.unprime()).__repr__() + "'" + else: + return Integer(self.unprime()).__repr__() + + def __add__(self, other): + """ + Sum of ``self`` with rational number ``other``. + + TEST:: + sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] + sage: b = a+.5 + sage: type(b) + + """ + return ShiftedPrimedTableauEntry(Rational.__add__(self, float(other))) + + def __sub__(self, other): + """ + Sum of ``self`` with rational number ``other``. + + TEST:: + sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] + sage: b = a-.5 + sage: type(b) + + """ + return ShiftedPrimedTableauEntry(Rational.__sub__(self, float(other))) + + + def is_unprimed(self): + """ + Checks if ``self`` is an unprimed element. + + TEST:: + sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] + sage: a.is_unprimed() + False + """ + return (self in ZZ) + + def is_primed(self): + """ + Checks if ``self`` is a primed element. + + TEST:: + sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] + sage: a.is_primed() + True + """ + return (self not in ZZ) + + def unprime(self): + """ + Unprime ``self`` if it was a prime element. + + TEST:: + sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] + sage: a.unprime() + 2 + """ + if self.is_primed(): + return Integer(self + .5) + else: + return Integer(self) + + class ShiftedPrimedTableaux(UniqueRepresentation, Parent): r""" Returns the combinatorial class of shifted primed tableaux subject @@ -959,18 +1011,18 @@ class ShiftedPrimedTableaux(UniqueRepresentation, Parent): sage: SPT = ShiftedPrimedTableaux(weight=(1,2,2), shape=[3,2]); SPT Shifted Primed Tableaux of weight (1, 2, 2) and shape [3, 2] sage: SPT.list() - [[(1.0, 2.0, 2.0), (3.0, 3.0)], - [(1.0, 1.5, 2.5), (2.0, 3.0)], - [(1.0, 1.5, 2.5), (2.0, 2.5)], - [(1.0, 1.5, 2.0), (3.0, 3.0)]] + [[(1, 2, 2), (3, 3)], + [(1, 2', 3'), (2, 3)], + [(1, 2', 3'), (2, 3')], + [(1, 2', 2), (3, 3)]] sage: SPT = ShiftedPrimedTableaux(weight=(1,2)); SPT Shifted Primed Tableaux of weight (1, 2) sage: list(SPT) - [[(1.0, 2.0, 2.0)], [(1.0, 1.5, 2.0)], [(1.0, 1.5), (2.0,)]] + [[(1, 2, 2)], [(1, 2', 2)], [(1, 2'), (2,)]] sage: SPT = ShiftedPrimedTableaux([3,2], max_entry = 2); SPT Shifted Primed Tableaux of shape [3, 2] and maximum entry 2 sage: list(SPT) - [[(1.0, 1.0, 1.0), (2.0, 2.0)], [(1.0, 1.0, 1.5), (2.0, 2.0)]] + [[(1, 1, 1), (2, 2)], [(1, 1, 2'), (2, 2)]] TESTS:: @@ -1187,7 +1239,7 @@ def _element_constructor_(self, T): TESTS:: sage: Tab=ShiftedPrimedTableaux()([[1,1,"2p"]]); Tab - [(1.0, 1.0, 1.5)] + [(1, 1, 2')] sage: Tab.parent() Shifted Primed Tableaux sage: Tab=ShiftedPrimedTableaux()([[1,1,2],[2,2]]) @@ -1207,7 +1259,7 @@ def __iter__(self): sage: Tabs = ShiftedPrimedTableaux() sage: Tabs[:5] - [[], [(1.0,)], [(2.0,)], [(1.0, 2.0)], [(1.0, 1.5)]] + [[], [(1,)], [(2,)], [(1, 2)], [(1, 2')]] """ if self._skew is not None: raise NotImplementedError('skew tableau must be empty') @@ -1252,13 +1304,13 @@ class ShiftedPrimedTableaux_shape(ShiftedPrimedTableaux): sage: SPTC = crystals.ShiftedPrimedTableaux([3,2], 3) sage: T = SPTC.module_generators[-1] sage: T - [(1.0, 1.0, 1.5), (2.0, 2.5)] + [(1, 1, 2'), (2, 3')] sage: T.f(2) - [(1.0, 1.0, 2.5), (2.0, 2.5)] + [(1, 1, 3'), (2, 3')] sage: len(SPTC.module_generators) 7 sage: SPTC[0] - [(1.0, 1.0, 1.0), (2.0, 2.0)] + [(1, 1, 1), (2, 2)] sage: SPTC.cardinality() 24 """ @@ -1376,7 +1428,7 @@ def _element_constructor_(self, T): TESTS:: sage: tab= ShiftedPrimedTableaux([3])([[1,1,1.5]]); tab - [(1.0, 1.0, 1.5)] + [(1, 1, 2')] sage: tab.parent() Shifted Primed Tableaux of shape [3] sage: ShiftedPrimedTableaux([3])([[1,1]]) @@ -1400,9 +1452,7 @@ def module_generators(self): sage: SPT = ShiftedPrimedTableaux(shape=[2,1]) sage: SPT.module_generators - ([(1.0, 1.0), (2.0,)], - [(1.0, 2.0), (3.0,)], - [(1.0, 1.5), (3.0,)]) + ([(1, 1), (2,)], [(1, 2), (3,)], [(1, 2'), (3,)]) """ if self._skew is not None: raise NotImplementedError("only for non-skew shapes") @@ -1437,16 +1487,12 @@ def __iter__(self): sage: Tabs = ShiftedPrimedTableaux([3,2], max_entry=3) sage: Tabs[:3] - [[(1.0, 1.0, 1.0), (2.0, 2.0)], - [(1.0, 1.0, 1.0), (2.0, 3.0)], - [(1.0, 1.0, 1.0), (2.0, 2.5)]] + [[(1, 1, 1), (2, 2)], [(1, 1, 1), (2, 3)], [(1, 1, 1), (2, 3')]] sage: len(list(Tabs)) 24 sage: Tabs = ShiftedPrimedTableaux([3,2]) sage: Tabs[:3] - [[(1.0, 1.0, 1.0), (2.0, 2.0)], - [(1.0, 1.0, 1.5), (2.0, 2.0)], - [(1.0, 1.0, 1.0), (2.0, 2.0)]] + [[(1, 1, 1), (2, 2)], [(1, 1, 2'), (2, 2)], [(1, 1, 1), (2, 2)]] """ if self._skew is not None: raise NotImplementedError('skew tableau must be empty') @@ -1468,7 +1514,6 @@ def __iter__(self): max_entry += 1 - class ShiftedPrimedTableaux_weight(ShiftedPrimedTableaux): """ Shifted primed tableaux of fixed weight. @@ -1524,7 +1569,7 @@ def _element_constructor_(self, T): TESTS:: sage: tab= ShiftedPrimedTableaux(weight=(2,1))([[1,1,1.5]]); tab - [(1.0, 1.0, 1.5)] + [(1, 1, 2')] sage: tab.parent() Shifted Primed Tableaux of weight (2, 1) """ @@ -1564,10 +1609,10 @@ def __iter__(self): sage: Tabs = ShiftedPrimedTableaux(weight=(2,3)) sage: Tabs[:4] - [[(1.0, 1.0, 2.0, 2.0, 2.0)], - [(1.0, 1.0, 1.5, 2.0, 2.0)], - [(1.0, 1.0, 2.0, 2.0), (2.0,)], - [(1.0, 1.0, 1.5, 2.0), (2.0,)]] + [[(1, 1, 2, 2, 2)], + [(1, 1, 2', 2, 2)], + [(1, 1, 2, 2), (2,)], + [(1, 1, 2', 2), (2,)]] sage: len(list(Tabs)) 5 """ @@ -1665,7 +1710,7 @@ def _element_constructor_(self, T): TESTS:: sage: tab = ShiftedPrimedTableaux([3], weight=(2,1))([[1,1,1.5]]); tab - [(1.0, 1.0, 1.5)] + [(1, 1, 2')] sage: tab.parent() Shifted Primed Tableaux of weight (2, 1) and shape [3] sage: ShiftedPrimedTableaux([3], weight=(2,1))([[1,1]]) @@ -1688,10 +1733,10 @@ def __iter__(self): sage: Tabs = ShiftedPrimedTableaux([3,2], weight=(1,2,2)) sage: Tabs[:4] - [[(1.0, 2.0, 2.0), (3.0, 3.0)], - [(1.0, 1.5, 2.5), (2.0, 3.0)], - [(1.0, 1.5, 2.5), (2.0, 2.5)], - [(1.0, 1.5, 2.0), (3.0, 3.0)]] + [[(1, 2, 2), (3, 3)], + [(1, 2', 3'), (2, 3)], + [(1, 2', 3'), (2, 3')], + [(1, 2', 2), (3, 3)]] sage: len(list(Tabs)) 4 @@ -1747,7 +1792,7 @@ def _add_strip(sub_tab, full_tab, length): TESTS:: sage: list(ShiftedPrimedTableaux([3,1], weight=(2,2))) # indirect doctest - [[(1.0, 1.0, 2.0), (2.0,)], [(1.0, 1.0, 1.5), (2.0,)]] + [[(1, 1, 2), (2,)], [(1, 1, 2'), (2,)]] """ if sum(sub_tab)+length > sum(full_tab): raise ValueError("strip does not fit") From 5bfd1698795bc83fe838d36039a13c253f99e5ef Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 9 Jan 2018 11:28:55 +0100 Subject: [PATCH 491/740] Implement QQbar^QQ as action --- src/sage/rings/qqbar.py | 504 ++++++++++++++++++++++------------------ 1 file changed, 276 insertions(+), 228 deletions(-) diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index bc381219aa1..f24754d4a95 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -521,8 +521,7 @@ from sage.structure.sage_object import SageObject from sage.structure.richcmp import (richcmp, richcmp_method, rich_to_bool, richcmp_not_equal, - op_EQ, op_NE, op_LE, op_LT, - op_GE, op_GT) + op_EQ, op_NE) from sage.rings.real_mpfr import RR from sage.rings.real_mpfi import RealIntervalField, RIF, is_RealIntervalFieldElement, RealIntervalField_class from sage.rings.complex_field import ComplexField @@ -535,14 +534,13 @@ from sage.rings.number_field.number_field import NumberField, QuadraticField, CyclotomicField from sage.rings.number_field.number_field_element_quadratic import NumberFieldElement_quadratic from sage.arith.all import factor -from sage.structure.element import generic_power, canonical_coercion from . import infinity -from sage.misc.functional import cyclotomic_polynomial +from sage.categories.action import Action + CC = ComplexField() CIF = ComplexIntervalField() -is_SymbolicExpressionRing = None class AlgebraicField_common(sage.rings.ring.Field): r""" @@ -640,6 +638,23 @@ def common_polynomial(self, poly): """ return AlgebraicPolynomialTracker(poly) + def _get_action_(self, G, op, self_on_left): + """ + EXAMPLES:: + + sage: QQbar.get_action(QQ, operator.pow) + Right Rational Powering by Rational Field on Algebraic Field + sage: print(QQbar.get_action(QQ, operator.pow, self_on_left=False)) + None + sage: print(QQbar.get_action(QQ, operator.mul)) + None + sage: QQbar.get_action(ZZ, operator.pow) + Right Integer Powering by Integer Ring on Algebraic Field + """ + if self_on_left and G is QQ and op is operator.pow: + return AlgebraicNumberPowQQAction(G, self) + + class AlgebraicRealField(Singleton, AlgebraicField_common): r""" The field of algebraic reals. @@ -2139,7 +2154,7 @@ def cmp_elements_with_same_minpoly(a, b, p): ar = a._value.real() br = b._value.real() if not ar.overlaps(br): - return -1 if richcmp_not_equal(ar, br, op_LT) else 1 + return -1 if (ar < br) else 1 ai = a._value.imag() bi = b._value.imag() @@ -2170,7 +2185,7 @@ def cmp_elements_with_same_minpoly(a, b, p): bi = b._value.imag() if ai.overlaps(bi): return 0 - return -1 if richcmp_not_equal(ai, bi, op_LT) else 1 + return -1 if (ai < bi) else 1 return None @@ -4048,137 +4063,6 @@ def _richcmp_(self, other, op): return richcmp_not_equal(srp, orp, op) return richcmp(self.imag(), other.imag(), op) - def __pow__(self, e): - r""" ``self**p`` returns the `p`'th power of self (where `p` can - be an arbitrary rational). If `p` is `(a/b)`, takes the principal - `b`'th root of self, then takes that to the `a`'th power. (Note - that this differs from ``__pow__`` on algebraic reals, where real - roots are preferred over principal roots if they exist.) - - EXAMPLES:: - - sage: QQbar(2)^(1/2) - 1.414213562373095? - sage: QQbar(8)^(2/3) - 4 - sage: QQbar(8)^(2/3) == 4 - True - sage: x = polygen(QQbar) - sage: phi = QQbar.polynomial_root(x^2 - x - 1, RIF(1, 2)) - sage: tau = QQbar.polynomial_root(x^2 - x - 1, RIF(-1, 0)) - sage: rt5 = QQbar(5)^(1/2) - sage: phi^10 / rt5 - 55.00363612324742? - sage: tau^10 / rt5 - 0.003636123247413266? - sage: (phi^10 - tau^10) / rt5 - 55.00000000000000? - sage: (phi^10 - tau^10) / rt5 == fibonacci(10) - True - sage: (phi^50 - tau^50) / rt5 == fibonacci(50) - True - sage: QQbar(-8)^(1/3) - 1.000000000000000? + 1.732050807568878?*I - sage: (QQbar(-8)^(1/3))^3 - -8 - sage: QQbar(32)^(1/5) - 2 - sage: a = QQbar.zeta(7)^(1/3); a - 0.9555728057861407? + 0.2947551744109043?*I - sage: a == QQbar.zeta(21) - True - sage: QQbar.zeta(7)^6 - 0.6234898018587335? - 0.7818314824680299?*I - sage: (QQbar.zeta(7)^6)^(1/3) * QQbar.zeta(21) - 1.000000000000000? + 0.?e-17*I - - TESTS: - - :trac:`22120`:: - - sage: QQbar(1)^QQbar(sqrt(2)) - 1 - sage: QQ(1)^QQbar(sqrt(3)) - 1 - sage: ZZ(1)^QQbar(sqrt(5)) - 1 - """ - if self == self.parent().one(): - return self.parent().one() - e = QQ._coerce_(e) - n = e.numerator() - d = e.denominator() - if d == 1: - return generic_power(self, n) - - # First, check for exact roots. - if isinstance(self._descr, ANRational): - rt = rational_exact_root(abs(self._descr._value), d) - if rt is not None: - if self._descr._value < 0: - z = QQbar.zeta(2*d)**n - return z * AlgebraicNumber(ANRational(rt**n)) - else: - return AlgebraicNumber(ANRational(rt**n)) - - # Without this special case, we do not know the multiplicity - # of the desired root - if self.is_zero(): - return QQbar.zero() - argument_is_pi = False - for prec in short_prec_seq(): - if prec is None: - # We know that self.real() < 0, since self._value - # crosses the negative real line and self._value - # is known to be non-zero. - isgn = self.imag().sign() - val = self._value - argument = val.argument() - if isgn == 0: - argument = argument.parent().pi() - argument_is_pi = True - elif isgn > 0: - if argument < 0: - argument = argument + 2 * argument.parent().pi() - else: - if argument > 0: - argument = argument - 2 * argument.parent().pi() - else: - val = self._interval_fast(prec) - if not val.crosses_log_branch_cut(): - argument = val.argument() - if val.imag().is_zero() and val.real() < 0: - argument_is_pi = True - break - - target_abs = abs(val) ** e - target_arg = argument * e - - for prec in tail_prec_seq(): - if target_abs.relative_diameter() < RR_1_10 and (target_arg * d).absolute_diameter() < RR_1_10: - break - - val = self._interval_fast(prec) - - target_abs = abs(val) ** e - argument = val.argument() - if argument_is_pi: - argument = argument.parent().pi() - target_arg = argument * e - - pow_n = self**n - poly = QQbarPoly.gen()**d - pow_n - - prec = target_abs.prec() - if argument_is_pi and d == 2: - target_real = 0 - else: - target_real = target_arg.cos() * target_abs - target = ComplexIntervalField(prec)(target_real, - target_arg.sin() * target_abs) - - return AlgebraicNumber(ANRoot(poly, target)) - def _mpfr_(self, field): r""" Given a ``RealField``, compute a good approximation to self in @@ -4521,6 +4405,32 @@ def rational_argument(self): self.exactify() return self._descr.rational_argument(self) + def _pow_(self, other): + """ + Powering for ``QQbar(1)``. + + EXAMPLES:: + + sage: QQbar(1) ^ QQbar(sqrt(2)) + 1 + sage: 1 ^ QQbar(sqrt(2)) + 1 + sage: QQbar(2) ^ QQbar(2) + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s) for ^: 'Algebraic Field' and 'Algebraic Field' + sage: AA(1) ^ AA(1) + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s) for ^: 'Algebraic Real Field' and 'Algebraic Real Field' + """ + # For some crazy unspecified reason, we must allow this if the + # base is QQbar(1). See Trac #22120 and #24490. + if self == 1: + return self + raise TypeError("unsupported operand parent(s) for ^: '{0}' and '{0}'".format(self.parent())) + + class AlgebraicReal(AlgebraicNumber_base): r""" A real algebraic number. @@ -4667,96 +4577,6 @@ def _richcmp_(self, other, op): return rich_to_bool(op, (self-other).sign()) - def __pow__(self, e): - """ - ``self**p`` returns the `p`'th power of self (where `p` can be an - arbitrary rational). If `p` is `(a/b)`, takes the `b`'th root of - self, then takes that to the `a`'th power. If self is negative - and `b` is odd, it takes the real `b`'th root; if self is odd and - `b` is even, this takes a complex root. Note that the behavior - when self is negative and `b` is odd differs from the complex - case; algebraic numbers select the principal complex `b`'th - root, but algebraic reals select the real root. - - EXAMPLES:: - - sage: AA(2)^(1/2) - 1.414213562373095? - sage: AA(8)^(2/3) - 4 - sage: AA(8)^(2/3) == 4 - True - sage: x = polygen(AA) - sage: phi = AA.polynomial_root(x^2 - x - 1, RIF(0, 2)) - sage: tau = AA.polynomial_root(x^2 - x - 1, RIF(-2, 0)) - sage: rt5 = AA(5)^(1/2) - sage: phi^10 / rt5 - 55.00363612324742? - sage: tau^10 / rt5 - 0.003636123247413266? - sage: (phi^10 - tau^10) / rt5 - 55.00000000000000? - sage: (phi^10 - tau^10) / rt5 == fibonacci(10) - True - sage: (phi^50 - tau^50) / rt5 == fibonacci(50) - True - - TESTS:: - - sage: AA(-8)^(1/3) - -2 - sage: AA(-8)^(2/3) - 4 - sage: AA(32)^(3/5) - 8 - sage: AA(-16)^(1/2) - 4*I - sage: AA(-16)^(1/4) - 1.414213562373095? + 1.414213562373095?*I - sage: AA(-16)^(1/4)/QQbar.zeta(8) - 2 - - We check that :trac:`7859` is fixed:: - - sage: (AA(2)^(1/2)-AA(2)^(1/2))^(1/2) - 0 - """ - e = QQ._coerce_(e) - n = e.numerator() - d = e.denominator() - if d == 1: - return generic_power(self, n) - - # First, check for exact roots. - if isinstance(self._descr, ANRational): - rt = rational_exact_root(abs(self._descr._value), d) - if rt is not None: - if self._descr._value < 0: - if d % 2 == 0: - z = QQbar.zeta(2*d)**n - return z * AlgebraicNumber(ANRational(rt**n)) - else: - return AlgebraicReal(ANRational((-rt)**n)) - else: - return AlgebraicReal(ANRational(rt**n)) - - # Without this special case, we do not know the multiplicity - # of the desired root - if self.sign() == 0: - return AA.zero() - if d % 2 == 0: - if self.sign() < 0: - return QQbar(self) ** e - pow_n = self**n - poly = AAPoly.gen()**d - pow_n - range = pow_n.interval_fast(RIF) - if d % 2 == 0: - result_min = 0 - else: - result_min = min(range.lower(), -1) - result_max = max(range.upper(), 1) - return AlgebraicReal(ANRoot(poly, RIF(result_min, result_max))) - def _integer_(self, Z=None): """ Return self as an Integer. @@ -5300,6 +5120,234 @@ def real_exact(self, field): return field(mid) + +class AlgebraicNumberPowQQAction(Action): + """ + Implement powering of an algebraic number (an element of ``QQbar`` + or ``AA``) by a rational. + + This is always a right action. + + INPUT: + + - ``G`` -- must be ``QQ`` + + - ``S`` -- the parent on which to act, either ``AA`` or ``QQbar``. + + .. NOTE:: + + To compute ``x ^ (a/b)``, we take the `b`'th root of `x`; then + we take that to the `a`'th power. If `x` is a negative algebraic + real and `b` is odd, take the real `b`'th root; otherwise take + the principal `b`'th root. + + EXAMPLES in ``QQbar``:: + + sage: QQbar(2)^(1/2) + 1.414213562373095? + sage: QQbar(8)^(2/3) + 4 + sage: QQbar(8)^(2/3) == 4 + True + sage: x = polygen(QQbar) + sage: phi = QQbar.polynomial_root(x^2 - x - 1, RIF(1, 2)) + sage: tau = QQbar.polynomial_root(x^2 - x - 1, RIF(-1, 0)) + sage: rt5 = QQbar(5)^(1/2) + sage: phi^10 / rt5 + 55.00363612324742? + sage: tau^10 / rt5 + 0.003636123247413266? + sage: (phi^10 - tau^10) / rt5 + 55.00000000000000? + sage: (phi^10 - tau^10) / rt5 == fibonacci(10) + True + sage: (phi^50 - tau^50) / rt5 == fibonacci(50) + True + sage: QQbar(-8)^(1/3) + 1.000000000000000? + 1.732050807568878?*I + sage: (QQbar(-8)^(1/3))^3 + -8 + sage: QQbar(32)^(1/5) + 2 + sage: a = QQbar.zeta(7)^(1/3); a + 0.9555728057861407? + 0.2947551744109043?*I + sage: a == QQbar.zeta(21) + True + sage: QQbar.zeta(7)^6 + 0.6234898018587335? - 0.7818314824680299?*I + sage: (QQbar.zeta(7)^6)^(1/3) * QQbar.zeta(21) + 1.000000000000000? + 0.?e-17*I + + EXAMPLES in ``AA``:: + + sage: AA(2)^(1/2) + 1.414213562373095? + sage: AA(8)^(2/3) + 4 + sage: AA(8)^(2/3) == 4 + True + sage: x = polygen(AA) + sage: phi = AA.polynomial_root(x^2 - x - 1, RIF(0, 2)) + sage: tau = AA.polynomial_root(x^2 - x - 1, RIF(-2, 0)) + sage: rt5 = AA(5)^(1/2) + sage: phi^10 / rt5 + 55.00363612324742? + sage: tau^10 / rt5 + 0.003636123247413266? + sage: (phi^10 - tau^10) / rt5 + 55.00000000000000? + sage: (phi^10 - tau^10) / rt5 == fibonacci(10) + True + sage: (phi^50 - tau^50) / rt5 == fibonacci(50) + True + + TESTS:: + + sage: AA(-8)^(1/3) + -2 + sage: AA(-8)^(2/3) + 4 + sage: AA(32)^(3/5) + 8 + sage: AA(-16)^(1/2) + 4*I + sage: AA(-16)^(1/4) + 1.414213562373095? + 1.414213562373095?*I + sage: AA(-16)^(1/4)/QQbar.zeta(8) + 2 + + We check that :trac:`7859` is fixed:: + + sage: (AA(2)^(1/2)-AA(2)^(1/2))^(1/2) + 0 + """ + def __init__(self, G, S): + """ + EXAMPLES:: + + sage: from sage.rings.qqbar import AlgebraicNumberPowQQAction + sage: act = AlgebraicNumberPowQQAction(QQ, AA); act + Right Rational Powering by Rational Field on Algebraic Real Field + sage: act(AA(-2), 1/3) + -1.259921049894873? + + :: + + sage: act = AlgebraicNumberPowQQAction(QQ, QQbar); act + Right Rational Powering by Rational Field on Algebraic Field + sage: act(QQbar(-2), 1/3) + 0.6299605249474365? + 1.091123635971722?*I + """ + Action.__init__(self, G, S, False, operator.pow) + + def _call_(self, x, e): + r""" + Return the power ``x ^ e``. + + INPUT: + + - ``x`` -- an algebraic number + + - ``e`` -- a rational number + """ + if not x: + return x + + n = e.numerator() + d = e.denominator() + if d == 1: + return x._pow_int(n) + + # Parent of the result + S = self.codomain() + if S is AA and d % 2 == 0 and x.sign() < 0: + S = QQbar + + # First, check for exact roots. + if isinstance(x._descr, ANRational): + rt = rational_exact_root(abs(x._descr._value), d) + if rt is not None: + if x._descr._value < 0: + if S is AA: + return AlgebraicReal(ANRational((-rt)**n)) + else: + z = QQbar.zeta(2*d)._pow_int(n) + return z * AlgebraicNumber(ANRational(rt**n)) + return S(ANRational(rt**n)) + + if S is AA: + # Result lies in AA + pow_n = x._pow_int(n) + poly = AAPoly.gen()**d - pow_n + range = pow_n.interval_fast(RIF) + if d % 2 == 0: + result_min = 0 + else: + result_min = min(range.lower(), -1) + result_max = max(range.upper(), 1) + return AlgebraicReal(ANRoot(poly, RIF(result_min, result_max))) + + # Result lies in QQbar + + # Determine whether arg(x) equals pi. + argument_is_pi = False + for prec in short_prec_seq(): + if prec is None: + # We know that x.real() < 0, since x._value + # crosses the negative real line and x._value + # is known to be non-zero. + isgn = x.imag().sign() + val = x._value + argument = val.argument() + if isgn == 0: + argument = argument.parent().pi() + argument_is_pi = True + elif isgn > 0: + if argument < 0: + argument = argument + 2 * argument.parent().pi() + else: + if argument > 0: + argument = argument - 2 * argument.parent().pi() + else: + val = x._interval_fast(prec) + if is_RealIntervalFieldElement(val) or not val.crosses_log_branch_cut(): + argument = val.argument() + if val.imag().is_zero() and val.real() < 0: + argument_is_pi = True + break + + target_abs = abs(val) ** e + target_arg = argument * e + + for prec in tail_prec_seq(): + if target_abs.relative_diameter() < RR_1_10 and (target_arg * d).absolute_diameter() < RR_1_10: + break + + val = x._interval_fast(prec) + + target_abs = abs(val) ** e + argument = val.argument() + if argument_is_pi: + argument = argument.parent().pi() + target_arg = argument * e + + pow_n = x**n + poly = QQbarPoly.gen()**d - pow_n + + prec = target_abs.prec() + if argument_is_pi and d == 2: + target_real = 0 + else: + target_real = target_arg.cos() * target_abs + target = ComplexIntervalField(prec)(target_real, + target_arg.sin() * target_abs) + + return AlgebraicNumber(ANRoot(poly, target)) + + def _repr_name_(self): + return "Rational Powering" + + class ANRational(ANDescr): r""" The subclass of ``ANDescr`` that represents an arbitrary From c846a0ebcfb29f9bcabc8a333e201e474b983950 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Thu, 18 Jan 2018 19:04:27 +0100 Subject: [PATCH 492/740] Changed printing of elements and fixed doctests. --- .../groups/abelian_gps/abelian_group_gap.py | 46 ++++++------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/src/sage/groups/abelian_gps/abelian_group_gap.py b/src/sage/groups/abelian_gps/abelian_group_gap.py index d037572e45a..bb42526d873 100644 --- a/src/sage/groups/abelian_gps/abelian_group_gap.py +++ b/src/sage/groups/abelian_gps/abelian_group_gap.py @@ -82,7 +82,7 @@ class AbelianGroupElement_gap(ElementLibGAP): sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: G = AbelianGroupGap([3,6]) sage: G.gens() - (g1, g2) + (f1, f2) """ def __init__(self, parent, x, check=True): """ @@ -143,25 +143,6 @@ def __reduce__(self): """ return self.parent(), (self.exponents(),) - def _repr_(self): - """ - The string representation of this element. - - OUTPUT: - - - a string - - EXAMPLES:: - - sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap - sage: G = AbelianGroupGap([3,2,4]) - sage: g = G.an_element() - sage: g._repr_() - 'g1*g2*g3' - """ - rep = self.gap()._repr_() - return rep.replace('f','g') - def exponents(self): r""" Return the tuple of exponents of this element. @@ -181,7 +162,7 @@ def exponents(self): sage: S = G.subgroup(G.gens()[:1]) sage: s = S.gens()[0] sage: s - g1 + f1 sage: s.exponents() (1,) sage: G = AbelianGroupGap([4,7,0]) # optional - gap_packages @@ -315,13 +296,13 @@ def _element_constructor_(self,x,check=True): sage: a f0*f1 sage: G(a) - g1*g2 + f1*f2 sage: A = AdditiveAbelianGroup([2,3]) sage: a = A.an_element() sage: a (1, 0) sage: G(a) - g1 + f1 For general fgp_modules conversion is implemented if our group is in smith form:: @@ -332,7 +313,7 @@ def _element_constructor_(self,x,check=True): sage: a (2) sage: G(a) - g1^2 + f2 """ if isinstance(x, AbelianGroupElement_gap): x = x.gap() @@ -370,10 +351,10 @@ def all_subgroups(self): sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: G = AbelianGroupGap([2,3]) sage: G.all_subgroups() - [Subgroup of Abelian group with gap, generator orders (2, 3) generated by (), - Subgroup of Abelian group with gap, generator orders (2, 3) generated by (g1,), - Subgroup of Abelian group with gap, generator orders (2, 3) generated by (g2,), - Subgroup of Abelian group with gap, generator orders (2, 3) generated by (g2, g1)] + [Subgroup of Abelian group with gap, generator orders (2, 3) generated by (1,), + Subgroup of Abelian group with gap, generator orders (2, 3) generated by (f1,), + Subgroup of Abelian group with gap, generator orders (2, 3) generated by (f2,), + Subgroup of Abelian group with gap, generator orders (2, 3) generated by (f1, f2)] """ subgroups_gap = self.gap().AllSubgroups() subgroups_sage = [] @@ -405,7 +386,7 @@ def identity(self): sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: G = AbelianGroupGap([4,10]) sage: G.identity() - id + 1 """ return self.one() @@ -527,11 +508,11 @@ def subgroup(self, gens): sage: gen = G.gens()[:2] sage: S = G.subgroup(gen) sage: S - Subgroup of Abelian group with gap, generator orders (2, 3, 4, 5) generated by (g1, g2) + Subgroup of Abelian group with gap, generator orders (2, 3, 4, 5) generated by (f1, f2) sage: g = G.an_element() sage: s = S.an_element() sage: g*s - g2^2*g3*g4 + f2^2*f3*f5 sage: G = AbelianGroupGap([3,4,0,2]) # optional - gap_packages sage: gen = G.gens()[:2] sage: S = G.subgroup(gen) @@ -674,7 +655,7 @@ def __repr__(self): sage: gen = G.gens()[:2] sage: S = G.subgroup(gen) sage: S.__repr__() - 'Subgroup of Abelian group with gap, generator orders (2, 3, 4, 5) generated by (g1, g2)' + 'Subgroup of Abelian group with gap, generator orders (2, 3, 4, 5) generated by (f1, f2)' """ s = "Subgroup of %s generated by %s"%(self.ambient(),self.gens()) return s @@ -698,3 +679,4 @@ def __reduce__(self): # avoid infinite loop gens = tuple(amb(g) for g in self.gens()) return amb.subgroup, (gens,) + From ec2c0116c41fc4755f4ecd2376f96e8db1e98d49 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Thu, 18 Jan 2018 19:18:35 +0100 Subject: [PATCH 493/740] Doctest _gap_init_ is now optional gap_packages --- src/sage/groups/abelian_gps/abelian_group.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py index 20ad4f88e97..08f92658893 100644 --- a/src/sage/groups/abelian_gps/abelian_group.py +++ b/src/sage/groups/abelian_gps/abelian_group.py @@ -884,16 +884,13 @@ def _gap_init_(self): sage: gap(G) Group( [ f1, f2, f3 ] ) - Only works for finite groups:: + Requires the optional `gap_packages` for infinite groups:: sage: G = AbelianGroup(3,[0,3,4],names="abc"); G Multiplicative Abelian group isomorphic to Z x C3 x C4 - sage: G._gap_init_() + sage: G._gap_init_() # optional gap_packages 'AbelianPcpGroup([0, 3, 4])' """ - # TODO: Use the package polycyclic has AbelianPcpGroup, which can handle - # the infinite case but it is a GAP package not GPL'd. - # Use this when the group is infinite... if self.is_finite(): return 'AbelianGroup(%s)'%list(self.gens_orders()) from sage.misc.package import is_package_installed, PackageNotFoundError From 8e4506ea84eb812b66a2e7bd0bbf5432da4ddb46 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Fri, 19 Jan 2018 10:12:23 +0100 Subject: [PATCH 494/740] Fix possible race condition that can crash the parallel doctest runner. This is especially likely on Cygwin where process shutdown just takes longer. --- src/sage/doctest/forker.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 5a2f443d04f..ccf81fd7f46 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -38,6 +38,7 @@ import __future__ import hashlib, multiprocessing, os, sys, time, warnings, signal, linecache +import errno import doctest, traceback import tempfile import six @@ -2110,11 +2111,19 @@ def kill(self): if self.rmessages is not None: os.close(self.rmessages) self.rmessages = None - if not self.killed: - self.killed = True - os.killpg(self.pid, signal.SIGQUIT) - else: - os.killpg(self.pid, signal.SIGKILL) + + try: + if not self.killed: + self.killed = True + os.killpg(self.pid, signal.SIGQUIT) + else: + os.killpg(self.pid, signal.SIGKILL) + except OSError as exc: + # Handle a race condition where the process has exited on + # its own by the time we get here, and ESRCH is returned + # indicating no processes in the specified process group + if exc.errno != errno.ESRCH: + raise class DocTestTask(object): From e9188bae6e2e5407101d1ef46f3343b7250ae375 Mon Sep 17 00:00:00 2001 From: Vincent Klein Date: Fri, 19 Jan 2018 10:27:40 +0100 Subject: [PATCH 495/740] Trac #22928: Test different precisions for each possible type --- src/sage/rings/complex_mpc.pyx | 15 ++++++++++++--- src/sage/rings/complex_number.pyx | 15 ++++++++++++--- src/sage/rings/real_mpfr.pyx | 11 ++++++++--- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/complex_mpc.pyx b/src/sage/rings/complex_mpc.pyx index eb499aad01a..bf4befef518 100644 --- a/src/sage/rings/complex_mpc.pyx +++ b/src/sage/rings/complex_mpc.pyx @@ -1267,11 +1267,20 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): sage: MPC = MPComplexField() sage: c = MPC(2,1) - sage: c.__mpc__() # optional - gmpy2 + sage: c.__mpc__() # optional - gmpy2 mpc('2.0+1.0j') - sage: from gmpy2 import mpc # optional - gmpy2 - sage: mpc(c) # optional - gmpy2 + sage: from gmpy2 import mpc # optional - gmpy2 + sage: mpc(c) # optional - gmpy2 mpc('2.0+1.0j') + sage: MPCF = MPComplexField(42) # optional - gmpy2 + sage: mpc(MPCF(12, 12)).precision # optional - gmpy2 + (42, 42) + sage: MPCF = MPComplexField(236) # optional - gmpy2 + sage: mpc(MPCF(12, 12)).precision # optional - gmpy2 + (236, 236) + sage: MPCF = MPComplexField(63) # optional - gmpy2 + sage: mpc(MPCF('15.64E+128', '15.64E+128')).precision # optional - gmpy2 + (63, 63) TESTS:: diff --git a/src/sage/rings/complex_number.pyx b/src/sage/rings/complex_number.pyx index c3ede9393c9..ccf1d330128 100644 --- a/src/sage/rings/complex_number.pyx +++ b/src/sage/rings/complex_number.pyx @@ -602,11 +602,20 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): EXAMPLES:: sage: c = ComplexNumber(2,1) - sage: c.__mpc__() # optional - gmpy2 + sage: c.__mpc__() # optional - gmpy2 mpc('2.0+1.0j') - sage: from gmpy2 import mpc # optional - gmpy2 - sage: mpc(c) # optional - gmpy2 + sage: from gmpy2 import mpc # optional - gmpy2 + sage: mpc(c) # optional - gmpy2 mpc('2.0+1.0j') + sage: cf = ComplexField(134) # optional - gmpy2 + sage: mpc(cf.pi()).precision # optional - gmpy2 + (134, 134) + sage: cf = ComplexField(45) # optional - gmpy2 + sage: mpc(cf.zeta(5)).precision # optional - gmpy2 + (45, 45) + sage: cf = ComplexField(255) # optional - gmpy2 + sage: mpc(cf.zeta(5)).precision # optional - gmpy2 + (255, 255) TESTS:: diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 4042ddc2b5d..658d830a0dd 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -3739,10 +3739,15 @@ cdef class RealNumber(sage.structure.element.RingElement): sage: from gmpy2 import mpfr # optional - gmpy2 sage: mpfr(RR(4.5)) # optional - gmpy2 mpfr('4.5') - sage: R = RealField(256) - sage: x = mpfr(R.pi()) # optional - gmpy2 - sage: x.precision # optional - gmpy2 + sage: R = RealField(256) # optional - gmpy2 + sage: mpfr(R.pi()).precision # optional - gmpy2 256 + sage: R = RealField(127) # optional - gmpy2 + sage: mpfr(R.pi()).precision # optional - gmpy2 + 127 + sage: R = RealField(42) # optional - gmpy2 + sage: mpfr(R.pi()).precision # optional - gmpy2 + 42 TESTS:: From 483c04e6a58b3c75cd323d64bca5782a0f213b68 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Fri, 19 Jan 2018 13:25:09 +0000 Subject: [PATCH 496/740] Apply the user's umask to the top-level directory extracted from a tarball in case it wasn't already. --- build/sage_bootstrap/uncompress/action.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build/sage_bootstrap/uncompress/action.py b/build/sage_bootstrap/uncompress/action.py index bc265edafa6..1191682775e 100644 --- a/build/sage_bootstrap/uncompress/action.py +++ b/build/sage_bootstrap/uncompress/action.py @@ -72,5 +72,11 @@ def unpack_archive(archive, dirname=None): # inspecting the newly extracted files rename = lambda: os.rename(top_level, dirname) retry(rename, OSError) + + # Apply the user's umask to the top-level directory in case it + # wasn't already; see https://trac.sagemath.org/ticket/24567 + umask = os.umask(0o777) + os.umask(umask) + os.chmod(dirname, os.stat(dirname).st_mode & ~umask) finally: os.chdir(prev_cwd) From 963ceb7a37a8ccfccf0e5b0442ea3a5fa38774f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 19 Jan 2018 15:22:05 +0100 Subject: [PATCH 497/740] Fix incorrect doctest output the ValueError did actually not make sense. --- src/sage/rings/padics/padic_valuation.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 0b4bd3b835f..8c326e6edee 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -724,9 +724,8 @@ def extensions(self, ring): sage: R. = QQ[] sage: L. = NumberField(x^4 + 2*x^3 + 2*x^2 + 8) sage: QQ.valuation(2).extensions(L) - Traceback (most recent call last): - ... - ValueError: The valuation [ Gauss valuation induced by 2-adic valuation, v(x) = 1/2 ] does not approximate a unique extension of 2-adic valuation with respect to x^4 + 2*x^3 + 2*x^2 + 8 + [[ 2-adic valuation, v(x + 2) = 3/2 ]-adic valuation, + [ 2-adic valuation, v(x) = 1/2 ]-adic valuation] A case where the extension was incorrect at some point:: From 5097c404493bbf331ca8dd4e3c3ebd135b639a56 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Sat, 20 Jan 2018 17:27:23 +0100 Subject: [PATCH 498/740] Precision bugfix. --- src/sage/modules/torsion_quadratic_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modules/torsion_quadratic_module.py b/src/sage/modules/torsion_quadratic_module.py index 6a8a44411c3..7b11e2ea040 100644 --- a/src/sage/modules/torsion_quadratic_module.py +++ b/src/sage/modules/torsion_quadratic_module.py @@ -475,7 +475,7 @@ def normal_form(self, partial=False): D_p = self.primary_part(p) q_p = D_p.gram_matrix_quadratic() q_p = q_p / D_p._modulus_qf - prec = self.annihilator().gen().valuation(p) + 1 + prec = self.annihilator().gen().valuation(p) + 5 D, U = p_adic_normal_form(q_p, p, precision=prec, partial=False) #apply U to the generators n = U.ncols() From 464c2a913f0c6cc1f2451f5324fd69a4b129a857 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sat, 20 Jan 2018 10:01:23 -0800 Subject: [PATCH 499/740] Fixed indentation --- src/sage/combinat/shifted_primed_tableau.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index e58503eca16..2080bde5992 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -614,7 +614,7 @@ def reading_word(self): column, in decreasing order within each column, moving from the rightmost column to the left, and with all the primes removed (i.e. all entries are increased by - half a unit). + half a unit). 2. Then list all unprimed entries, row by row, in increasing order within each row, moving from the @@ -934,7 +934,6 @@ def __sub__(self, other): """ return ShiftedPrimedTableauEntry(Rational.__sub__(self, float(other))) - def is_unprimed(self): """ Checks if ``self`` is an unprimed element. From ac7816d0be8d72884422d107e361889ca3400fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Fri, 5 Jan 2018 12:13:11 +0100 Subject: [PATCH 500/740] Add a new gfortran spkg and use it when gfortran is not found --- build/pkgs/atlas/dependencies | 2 +- build/pkgs/gfortran/SPKG.txt | 25 ++++++++ build/pkgs/gfortran/checksums.ini | 1 + build/pkgs/gfortran/dependencies | 1 + build/pkgs/gfortran/package-version.txt | 1 + build/pkgs/gfortran/spkg-install | 77 +++++++++++++++++++++++++ build/pkgs/gfortran/type | 1 + build/pkgs/numpy/dependencies | 2 +- build/pkgs/openblas/dependencies | 2 +- build/pkgs/r/dependencies | 2 +- build/pkgs/scipy/dependencies | 2 +- configure.ac | 66 ++++++--------------- 12 files changed, 130 insertions(+), 52 deletions(-) create mode 100644 build/pkgs/gfortran/SPKG.txt create mode 120000 build/pkgs/gfortran/checksums.ini create mode 120000 build/pkgs/gfortran/dependencies create mode 120000 build/pkgs/gfortran/package-version.txt create mode 100644 build/pkgs/gfortran/spkg-install create mode 100644 build/pkgs/gfortran/type diff --git a/build/pkgs/atlas/dependencies b/build/pkgs/atlas/dependencies index 1a36cc52351..46757bc2b61 100644 --- a/build/pkgs/atlas/dependencies +++ b/build/pkgs/atlas/dependencies @@ -1,4 +1,4 @@ -| $(PYTHON) +gfortran | $(PYTHON) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/gfortran/SPKG.txt b/build/pkgs/gfortran/SPKG.txt new file mode 100644 index 00000000000..0f9c79bf9dc --- /dev/null +++ b/build/pkgs/gfortran/SPKG.txt @@ -0,0 +1,25 @@ += gfortran = + +== Description == + +The GNU Compiler Collection, including the C, C++ and Fortran compiler. +This particular package is meant to only make gfortran available. + +== License == + +GPL version 2 or version 3 + +== Upstream Contact == + +http://gcc.gnu.org/ + +== Dependencies == + + * zlib + * MPIR + * MPFR + * MPC + +== Special Update/Build Instructions == + +None. diff --git a/build/pkgs/gfortran/checksums.ini b/build/pkgs/gfortran/checksums.ini new file mode 120000 index 00000000000..af5c6bc0835 --- /dev/null +++ b/build/pkgs/gfortran/checksums.ini @@ -0,0 +1 @@ +../gcc/checksums.ini \ No newline at end of file diff --git a/build/pkgs/gfortran/dependencies b/build/pkgs/gfortran/dependencies new file mode 120000 index 00000000000..2824aff6f86 --- /dev/null +++ b/build/pkgs/gfortran/dependencies @@ -0,0 +1 @@ +../gcc/dependencies \ No newline at end of file diff --git a/build/pkgs/gfortran/package-version.txt b/build/pkgs/gfortran/package-version.txt new file mode 120000 index 00000000000..ff59fe7635c --- /dev/null +++ b/build/pkgs/gfortran/package-version.txt @@ -0,0 +1 @@ +../gcc/package-version.txt \ No newline at end of file diff --git a/build/pkgs/gfortran/spkg-install b/build/pkgs/gfortran/spkg-install new file mode 100644 index 00000000000..e506125be5d --- /dev/null +++ b/build/pkgs/gfortran/spkg-install @@ -0,0 +1,77 @@ +if [ -z "$SAGE_LOCAL" ]; then + echo >&2 "SAGE_LOCAL undefined ... exiting" + echo >&2 "Maybe run 'sage --sh'?" + exit 1 +fi + +# This spkg shouldn't be installed if gcc is already installed +if [ -a "$SAGE_LOCAL"/bin/gcc ]; then + echo >&2 "Error: gcc is already installed" + exit 1 +fi + +# Exit on error +set -e + +# Build in a separate directory, not in src/ (a.k.a. a VPATH build). +# This is the recommended way to build GCC. +mkdir gcc-build +cd gcc-build + + +# On OSX: +if [ "$UNAME" = "Darwin" ]; then + # isl/cloog libraries are almost certainly from Homebrew and won't work + GCC_CONFIGURE="--without-isl --without-cloog $GCC_CONFIGURE" + + # Use 'bootstrap-debug' build configuration to force stripping of object + # files prior to comparison during bootstrap (broken by Xcode 6.3). + # See #18156 + GCC_CONFIGURE="--with-build-config=bootstrap-debug $GCC_CONFIGURE" +fi + +# Let Gfortran build on Raspberry Pi using hard floats. +if [ `uname -m` = "armv6l" ]; then + GCC_CONFIGURE="--with-arch=armv6 --with-fpu=vfp --with-float=hard $GCC_CONFIGURE" +fi + +# Let Gfortran build on more recent ARM boards using hard floats. +if [ `uname -m` = "armv7l" ]; then + GCC_CONFIGURE="--with-arch=armv7-a --with-fpu=vfpv3-d16 --with-float=hard $GCC_CONFIGURE" +fi + +if [ "$SAGE_CHECK" = yes ]; then + # Enable internal checks in GCC. These checks do not affect the + # binaries produced by GCC, but they do increase the compile time + # of everything compiled with GCC. + GCC_CONFIGURE="$GCC_CONFIGURE --enable-checking=yes" +fi + +# Use the assembler/linker specified by $AS/$LD if they differ from the +# default. +if [ -n "$AS" -a "$AS" != "as" ]; then + CONFIGURE_AS="--with-as=$AS" +fi +if [ -n "$LD" -a "$LD" != "ld" ]; then + CONFIGURE_LD="--with-ld=$LD" +fi + +../src/configure \ + --prefix="$SAGE_LOCAL" \ + --with-local-prefix="$SAGE_LOCAL" \ + --with-gmp="$SAGE_LOCAL" --with-mpfr="$SAGE_LOCAL" --with-mpc="$SAGE_LOCAL" \ + --with-system-zlib \ + --disable-multilib \ + --disable-nls \ + --enable-languages=fortran \ + --disable-bootstrap \ + --disable-libitm \ + $GCC_CONFIGURE "$CONFIGURE_AS" "$CONFIGURE_LD" + +$MAKE BOOT_LDFLAGS="-Wl,-rpath,$SAGE_LOCAL/lib" + +$MAKE install + +# The spkg still installs a minimal C compiler that needs to be removed +# so it doesn't conflict with the configured C compiler. +rm -f "$SAGE_LOCAL"/bin/gcc "$SAGE_LOCAL"/bin/cpp diff --git a/build/pkgs/gfortran/type b/build/pkgs/gfortran/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/gfortran/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/numpy/dependencies b/build/pkgs/numpy/dependencies index a918e9080c5..a55efc64db5 100644 --- a/build/pkgs/numpy/dependencies +++ b/build/pkgs/numpy/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) $(BLAS) | setuptools pip pkgconfig +$(PYTHON) $(BLAS) gfortran | setuptools pip pkgconfig ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/openblas/dependencies b/build/pkgs/openblas/dependencies index 1a36cc52351..46757bc2b61 100644 --- a/build/pkgs/openblas/dependencies +++ b/build/pkgs/openblas/dependencies @@ -1,4 +1,4 @@ -| $(PYTHON) +gfortran | $(PYTHON) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/r/dependencies b/build/pkgs/r/dependencies index da0552acdb7..ceb34c5e8c2 100644 --- a/build/pkgs/r/dependencies +++ b/build/pkgs/r/dependencies @@ -1,4 +1,4 @@ -$(BLAS) iconv readline bzip2 xz pcre curl | pkgconf +$(BLAS) gfortran iconv readline bzip2 xz pcre curl | pkgconf ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/scipy/dependencies b/build/pkgs/scipy/dependencies index 5f5d5cecc38..51d2af0a0d7 100644 --- a/build/pkgs/scipy/dependencies +++ b/build/pkgs/scipy/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) $(BLAS) numpy | pip +$(PYTHON) $(BLAS) gfortran numpy | pip ---------- All lines of this file are ignored except the first. diff --git a/configure.ac b/configure.ac index fbe94eb4ff7..ad84d694a0a 100644 --- a/configure.ac +++ b/configure.ac @@ -473,7 +473,7 @@ fi # not a working Fortran compiler. AC_LANG(Fortran) if test -z "$FC"; then - SAGE_MUST_INSTALL_GCC([a Fortran compiler is missing]) + need_to_install_gfortran=yes else # see http://www.gnu.org/software/hello/manual/autoconf/Fortran-Compiler.html AC_FC_FREEFORM([], @@ -481,7 +481,7 @@ else AC_MSG_NOTICE([Your Fortran compiler does not accept free-format source code]) AC_MSG_NOTICE([which means the compiler is either seriously broken, or]) AC_MSG_NOTICE([is too old to build Sage.]) - SAGE_MUST_INSTALL_GCC([the Fortran compiler is not suitable]) + need_to_install_gfortran=yes ]) fi @@ -497,7 +497,7 @@ elif test $need_to_install_gcc = yes; then elif test x$GCC != xyes; then SAGE_SHOULD_INSTALL_GCC([your C compiler isn't GCC (GNU C)]) elif test x$GFC != xyes; then - SAGE_SHOULD_INSTALL_GCC([your Fortran compiler isn't GCC (GNU Fortran)]) + need_to_install_gfortran=yes else # Since need_to_install_gcc is "no", we know that # at least C, C++ and Fortran compilers are available. @@ -507,13 +507,15 @@ else AX_GCC_VERSION() AX_GXX_VERSION() - # Add the .0 because Debian/Ubuntu gives version numbers like - # 4.6 instead of 4.6.4 (Trac #18885) - case "$GXX_VERSION.0" in - [[0-3]].*|4.[[0-7]].*) - # Install our own GCC if the system-provided one is older than gcc-4.8. - SAGE_SHOULD_INSTALL_GCC([you have $CXX version $GXX_VERSION, which is quite old]);; - esac + if test $IS_REALLY_GCC = yes ; then + # Add the .0 because Debian/Ubuntu gives version numbers like + # 4.6 instead of 4.6.4 (Trac #18885) + case "$GXX_VERSION.0" in + [[0-3]].*|4.[[0-7]].*) + # Install our own GCC if the system-provided one is older than gcc-4.8. + SAGE_SHOULD_INSTALL_GCC([you have $CXX version $GXX_VERSION, which is quite old]);; + esac + fi # The following tests check that the version of the compilers # are all the same. @@ -521,43 +523,7 @@ else SAGE_SHOULD_INSTALL_GCC([$CC ($GCC_VERSION) and $CXX ($GXX_VERSION) are not the same version]) fi - # In the paragraph below, 'gfortran' is used to indicate the GNU Fortran - # compiler, though it might be called something else. - - # It's not easily possible to determine the Fortran version, as - # gfortran -dumpversion did not until GCC 4.5 return just the - # the version number, but the same as gfortran --version - # for example: - - # drkirkby@hawk:~$ gcc -dumpversion - # 4.3.4 - - # drkirkby@hawk:~$ g++ -dumpversion - # 4.3.4 - - # drkirkby@hawk:~$ gfortran -dumpversion - # GNU Fortran (GCC) 4.3.4 - # Copyright (C) 2008 Free Software Foundation, Inc. - # GNU Fortran comes with NO WARRANTY, to the extent permitted by law. - # You may redistribute copies of GNU Fortran - # under the terms of the GNU General Public License. - # For more information about these matters, see the file named COPYING - - # This behaviour is fixed in the gcc 4.5 branch. Since we need to - # support older versions of the compiler, we can't change this. - - # But I would expect that the version will be on the output - # of the compiler followed by -dumpversion (e.g. fortran -dumpversion) - - # So we grep for the known gcc version on the output of gfortran -dumpversion. - # and hope we find the same string. If so, they are almost certainly - # the same version. - fortran_version_string="`$FC -dumpversion | grep $GCC_VERSION 2>&1`" - - if test "x$fortran_version_string" = x - then - SAGE_SHOULD_INSTALL_GCC([the Fortran compiler is not the same version as the C compiler]) - fi + # assuming gfortran does work. fi @@ -681,6 +647,12 @@ AC_MSG_ERROR([["found Fink in $FINK_PATH. Either: fi fi +# if GCC needs to be installed GFORTRAN shouldn't be installed +# GCC will provide GFORTRAN in that case +if test $need_to_install_gcc = yes ; then + need_to_install_gfortran=no +fi + ############################################################################### # Create $SAGE_ROOT/build/make/Makefile starting from build/make/deps ############################################################################### From 6428f276ba874e86931357ebde1d1f813764d8ac Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sun, 21 Jan 2018 09:27:53 -0800 Subject: [PATCH 501/740] Fixed weight for skew tableaux --- src/sage/combinat/shifted_primed_tableau.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index 2080bde5992..d7f6be1c35e 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -531,11 +531,12 @@ def weight(self): EXAMPLES:: - sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) + sage: t = ShiftedPrimedTableau([['2p',2,2],[2,'3p']], skew=[1]) sage: t.weight() - (1, 4, 1) + (0, 4, 1) """ - flat = [entry.unprime() for row in self for entry in row] + flat = [entry.unprime() for row in self + for entry in row if entry is not None] if flat == []: max_ind = 0 else: From 7f73b362bcc7ec335dc92c690def9abc2837a3e9 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sun, 21 Jan 2018 09:47:09 -0800 Subject: [PATCH 502/740] Minor documentation fix --- src/sage/combinat/shifted_primed_tableau.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index d7f6be1c35e..266e5567035 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -850,7 +850,7 @@ def weight(self): Return the weight of ``self``. The weight of a shifted primed tableau is defined to be the vector - with `i`-th component equal to the number of entries i and i' in the + with `i`-th component equal to the number of entries `i` and `i'` in the tableau. EXAMPLES:: From e312dfe11f392ccd5c67675a7ee31481aa8e888d Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sun, 21 Jan 2018 11:42:21 -0800 Subject: [PATCH 503/740] More doctests --- src/sage/combinat/shifted_primed_tableau.py | 96 +++++++++++++++++++-- 1 file changed, 87 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index 266e5567035..66c2b6ee5d2 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -47,7 +47,8 @@ @add_metaclass(InheritComparisonClasscallMetaclass) class ShiftedPrimedTableau(ClonableArray): r""" - A shifted primed tableau with primed elements stored as half-integers. + A shifted primed tableau with primed elements stored as half-integers from + ``QQ``. A primed tableau is a tableau of shifted shape in the alphabet `X' = \{1' < 1 < 2' < 2 < \cdots < n' < n\}` such that @@ -57,6 +58,9 @@ class ShiftedPrimedTableau(ClonableArray): cannot have two repeated non-primed elements; 3. There are only non-primed elements on the main diagonal. + Skew shape of the shifted primed tableaux is specified either with an optional + argument ``skew`` or with ``None`` entries. + EXAMPLES:: sage: T = ShiftedPrimedTableaux([4,2]) @@ -67,6 +71,8 @@ class ShiftedPrimedTableau(ClonableArray): (2, 3') sage: ShiftedPrimedTableau([["2p",2,3],["2p","3p"],[2]], skew=[2,1]) [(None, None, 2', 2, 3), (None, 2', 3'), (2,)] + sage: ShiftedPrimedTableau([[None,None,"2p"],[None,"2p"]]) + [(None, None, 2'), (None, 2')] TESTS:: @@ -95,9 +101,28 @@ def __classcall_private__(cls, T, skew=None): sage: t = ShiftedPrimedTableau([["2p",2,3],["2p"]],skew=[2,1]) sage: t.parent() Shifted Primed Tableaux skewed by [2, 1] + sage: s = ShiftedPrimedTableau([[None, None,"2p",2,3],[None,"2p"]]) + sage: s.parent() + Shifted Primed Tableaux skewed by [2, 1] """ + if T == [] or T == [[]]: + return ShiftedPrimedTableaux(skew=skew)([]) if (isinstance(T, cls) and T._skew == skew): return T + + try: + entry = T[0][0] + except TypeError: + raise ValueError("input tableau must be a list of lists") + + if entry is None: + skew_ = [row.count(None) for row in T if row[0] is None] + if skew is not None: + if skew != skew_: + raise ValueError("skew shape does not agree with None entries") + else: + skew = skew_ + return ShiftedPrimedTableaux(skew=skew)(T) def __init__(self, parent, T, skew=None, check=True, preprocessed=False): @@ -140,12 +165,18 @@ def __init__(self, parent, T, skew=None, check=True, preprocessed=False): def _preprocess_(T, skew=None): """ Preprocessing list ``T`` to initialize the tableau. + + TESTS:: + sage: t = ShiftedPrimedTableau([[None, "2'", "3p", 3.5]]) #indirect doctest + sage: t + [(None, 2', 3', 4')] """ if isinstance(T, ShiftedPrimedTableau): return T # Preprocessing list t for primes and other symbols - T = [[ShiftedPrimedTableauEntry(entry) for entry in row] for row in T] + T = [[ShiftedPrimedTableauEntry(entry) for entry in row if entry is not None] + for row in T] if skew is not None: T = ([(None,)*skew[i] + tuple(T[i]) @@ -191,6 +222,9 @@ def __eq__(self, other): sage: t = ShiftedPrimedTableau([[1,"2p"]]) sage: t == ShiftedPrimedTableaux([2])([[1,1.5]]) True + sage: s = ShiftedPrimedTableau([["2p",3]], skew=[1]) + sage: s == [[None, "2p", 3]] + True """ if isinstance(other, ShiftedPrimedTableau): return (self._skew == other._skew and list(self) == list(other)) @@ -215,6 +249,10 @@ def __ne__(self, other): sage: t = ShiftedPrimedTableau([[1,"2p"]]) sage: t != ShiftedPrimedTableaux([2])([[1,1]]) True + sage: s = ShiftedPrimedTableau([["2p",3]], skew=[1]) + sage: s != [[None, "2p", 3]] + False + """ if isinstance(other, ShiftedPrimedTableau): return (self._skew != other._skew or list(self) != list(other)) @@ -242,10 +280,10 @@ def _repr_list(self): """ Return a string representation of ``self`` as a list of tuples. - EXAMPLES:: + EXAMPLE:: - sage: print(ShiftedPrimedTableau([['2p',3],[2,2]], skew=[2])) - [(None, None, 2', 3), (2, 2)] + sage: ShiftedPrimedTableau([['2p',3],[2,2]], skew=[2])._repr_list() + "[(None, None, 2', 3), (2, 2)]" """ return (repr([row for row in self])) @@ -877,6 +915,7 @@ def __init__(self, entry): Normalize the entry to be a half-integer in ``QQ``. TEST:: + sage: ShiftedPrimedTableau([[1,"2p"]])[0][1] 2' sage: ShiftedPrimedTableau([[1,"2'"]])[0][1] @@ -903,6 +942,7 @@ def __repr__(self): Represent ``self`` as primed or unprimed integer. TEST:: + sage: ShiftedPrimedTableau([[1,"2p"]])[0][1] 2' """ @@ -916,6 +956,7 @@ def __add__(self, other): Sum of ``self`` with rational number ``other``. TEST:: + sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] sage: b = a+.5 sage: type(b) @@ -928,6 +969,7 @@ def __sub__(self, other): Sum of ``self`` with rational number ``other``. TEST:: + sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] sage: b = a-.5 sage: type(b) @@ -940,6 +982,7 @@ def is_unprimed(self): Checks if ``self`` is an unprimed element. TEST:: + sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] sage: a.is_unprimed() False @@ -951,6 +994,7 @@ def is_primed(self): Checks if ``self`` is a primed element. TEST:: + sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] sage: a.is_primed() True @@ -962,6 +1006,7 @@ def unprime(self): Unprime ``self`` if it was a prime element. TEST:: + sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] sage: a.unprime() 2 @@ -1128,6 +1173,12 @@ def __classcall_private__(cls, shape=None, weight=None, def __init__(self, skew=None): """ Initialization of the parent class with given skew shape. + + TEST:: + + sage: T = ShiftedPrimedTableau([[None, 2]]) + sage: T.parent()._skew + [1] """ self._skew = skew @@ -1144,6 +1195,16 @@ def _element_constructor_(self, T): - the corresponding primed tableau object + TESTS:: + + sage: Tabs = ShiftedPrimedTableaux() + sage: Tabs([[1,"2p","2p"]]) + Traceback (most recent call last): + ... + ValueError: [[1, '2p', '2p']] is not an element of Shifted Primed Tableaux + sage: Tabs = ShiftedPrimedTableaux(skew=[1]) + sage: Tabs([["2p",2]]) + [(None, 2', 2)] """ try: return self.element_class(self, T, skew=self._skew) @@ -1153,6 +1214,23 @@ def _element_constructor_(self, T): def _contains_tableau_(self, T): """ Check if ``self`` contains preprocessed tableau ``T``. + + TESTS:: + + sage: Tabs = ShiftedPrimedTableaux() + sage: tab = ShiftedPrimedTableau([])._preprocess_( + ....: [[1,"2p","3p","3p"]]) + sage: tab + [(1, 2', 3', 3')] + sage: Tabs._contains_tableau_(tab) + False + sage: Tabs = ShiftedPrimedTableaux(skew = [1]) + sage: tab = ShiftedPrimedTableau([])._preprocess_( + ....: [[None,"2p","3p",3]], skew=[1]) + sage: tab + [(None, 2', 3', 3)] + sage: Tabs._contains_tableau_(tab) + True """ if not all(len(T[i]) > len(T[i+1]) for i in range(len(T)-1)): return False @@ -1165,20 +1243,20 @@ def _contains_tableau_(self, T): if not all(val > T[i-1][j+1] for j, val in enumerate(row) if j+1 >= skew[i-1] - if int(val) == val): + if val.is_unprimed()): return False if not all(val >= T[i-1][j+1] for j, val in enumerate(row) if j+1 >= skew[i-1] - if int(val) != val): + if val.is_primed()): return False if not all(row[j] <= row[j+1] for j in range(skew[i], len(row)-1) - if int(row[j]) == row[j]): + if row[j].is_unprimed()): return False if not all(row[j] < row[j+1] for j in range(skew[i], len(row)-1) - if int(row[j]) != row[j]): + if row[j].is_primed()): return False if not all(int(row[0]) == row[0] for i, row in enumerate(T) From 89a9fce92e10534dce169f8bf7b60d0e2b1f89ec Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sun, 21 Jan 2018 11:59:31 -0800 Subject: [PATCH 504/740] Speedup init for entry class --- src/sage/combinat/shifted_primed_tableau.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index 66c2b6ee5d2..ee693f31118 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -284,7 +284,7 @@ def _repr_list(self): sage: ShiftedPrimedTableau([['2p',3],[2,2]], skew=[2])._repr_list() "[(None, None, 2', 3), (2, 2)]" - """ + """ return (repr([row for row in self])) def _repr_tab(self): @@ -926,15 +926,18 @@ def __init__(self, entry): if isinstance(entry, str): if (entry[-1] == "'" or entry[-1] == "p") and entry[:-1].isdigit() is True: # Check if an element has "'" or "p" at the end - entry = QQ(entry[:-1]) - .5 + entry = Rational(entry[:-1]) - Rational(.5) + Rational.__init__(self, entry) + return try: - if (QQ(entry)+.5 in ZZ) or (QQ(entry) in ZZ): - # Check if an element is a half-integer - entry = QQ(entry) - else: - raise ValueError("all numbers must be half-integers") + entry = Rational(entry) except (TypeError, ValueError): raise ValueError("primed elements have wrong format") + + if (entry+.5 not in ZZ) and (entry not in ZZ): + # Check if an element is a half-integer + raise ValueError("all numbers must be half-integers") + Rational.__init__(self, entry) def __repr__(self): From f21e876ad790c8d8ae7c1ed9d7dbbf16be5951a2 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sun, 21 Jan 2018 12:05:25 -0800 Subject: [PATCH 505/740] More speedup init for entry class --- src/sage/combinat/shifted_primed_tableau.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index ee693f31118..abf09215354 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -923,10 +923,11 @@ def __init__(self, entry): sage: ShiftedPrimedTableau([[1,1.5]])[0][1] 2' """ + half = Integer(1)/Integer(2) if isinstance(entry, str): if (entry[-1] == "'" or entry[-1] == "p") and entry[:-1].isdigit() is True: # Check if an element has "'" or "p" at the end - entry = Rational(entry[:-1]) - Rational(.5) + entry = Rational(entry[:-1]) - half Rational.__init__(self, entry) return try: @@ -934,7 +935,7 @@ def __init__(self, entry): except (TypeError, ValueError): raise ValueError("primed elements have wrong format") - if (entry+.5 not in ZZ) and (entry not in ZZ): + if (entry + half not in ZZ) and (entry not in ZZ): # Check if an element is a half-integer raise ValueError("all numbers must be half-integers") @@ -1015,7 +1016,7 @@ def unprime(self): 2 """ if self.is_primed(): - return Integer(self + .5) + return Integer(self + Integer(1)/Integer(2)) else: return Integer(self) From e0534a8c68bc60cf2495acf32598096d64579184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Mon, 22 Jan 2018 11:55:18 +1300 Subject: [PATCH 506/740] Make all doctests set agg as the backend for matplotlib. --- src/sage/doctest/control.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 8dbbc3c138e..f35e68bb169 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -42,6 +42,11 @@ # Optional tags which are always automatically added auto_optional_tags = set(['py2' if six.PY2 else 'py3']) +# Make sure the agg backend is selected during doctesting +# This needs to be done before any other matplotlib calls. +import matplotlib +matplotlib.use('agg') + class DocTestDefaults(SageObject): """ From 582099c36863459524e292afab9d33c271b941c9 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sun, 21 Jan 2018 15:04:07 -0800 Subject: [PATCH 507/740] Fixed addition on primed entries, speedup of crystal operators --- src/sage/combinat/shifted_primed_tableau.py | 74 ++++++++++++--------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index abf09215354..d89f41bffad 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -175,7 +175,7 @@ def _preprocess_(T, skew=None): return T # Preprocessing list t for primes and other symbols - T = [[ShiftedPrimedTableauEntry(entry) for entry in row if entry is not None] + T = [[PrimedEntry(entry) for entry in row if entry is not None] for row in T] if skew is not None: @@ -713,6 +713,8 @@ def f(self, ind): element_to_change = None count = 0 + half = Integer(1)/Integer(2) + one = Integer(1) for element in read_word: if element[1] == ind+1: @@ -728,40 +730,40 @@ def f(self, ind): (r, c), elt = element_to_change if T[r][c].is_primed(): - T = [[elt+.5 if elt is not None else elt + T = [[elt+half if elt is not None else elt for elt in row] for row in T] T = map(list, zip(*T)) r, c = c, r h, l = len(T), len(T[0]) - if (c+1 == l or T[r][c+1] is None or T[r][c+1] >= ind+1): + if (c+1 == l or T[r][c+1] is None or T[r][c+1] >= ind+one): (tp_r, tp_c) = (r, c) while True: - if (tp_r+1 == h or T[tp_r+1][tp_c] is None or T[tp_r+1][tp_c] > ind+1): + if (tp_r+1 == h or T[tp_r+1][tp_c] is None or T[tp_r+1][tp_c] > ind+one): break - if tp_r <= tp_c and T[tp_r+1][tp_r+1] == ind+1: + if tp_r <= tp_c and T[tp_r+1][tp_r+1] == ind+one: tp_r += 1 tp_c = tp_r break - if (ind+.5 not in T[tp_r+1]): + if (ind+half not in T[tp_r+1]): break tp_r += 1 - tp_c = T[tp_r].index(ind+.5) + tp_c = T[tp_r].index(ind+half) if tp_r == r: - T[r][c] = T[r][c]+1 + T[r][c] = T[r][c]+one elif tp_r == tp_c: - T[r][c] = T[r][c]+.5 + T[r][c] = T[r][c]+half else: - T[r][c] = T[r][c]+.5 - T[tp_r][tp_c] = T[tp_r][tp_c]+.5 + T[r][c] = T[r][c]+half + T[tp_r][tp_c] = T[tp_r][tp_c]+half - elif T[r][c+1] == ind+.5: - T[r][c+1] = T[r][c+1]+.5 - T[r][c] = T[r][c]+.5 + elif T[r][c+1] == ind+half: + T[r][c+1] = T[r][c+1]+half + T[r][c] = T[r][c]+half if r > c: - T = [[elt-.5 if elt is not None else elt + T = [[elt-half if elt is not None else elt for elt in row] for row in T] T = map(list, zip(*T)) @@ -806,6 +808,8 @@ def e(self, ind): element_to_change = None count = 0 + half = Integer(1)/Integer(2) + one = Integer(1) for element in read_word[::-1]: if element[1] == ind: @@ -820,7 +824,7 @@ def e(self, ind): (r, c), elt = element_to_change if T[r][c].is_primed(): - T = [[elt+.5 if elt is not None else elt + T = [[elt+half if elt is not None else elt for elt in row] for row in T] T = map(list, zip(*T)) r, c = c, r @@ -830,24 +834,24 @@ def e(self, ind): while True: if (tp_r == 0 or T[tp_r-1][tp_c] is None or T[tp_r-1][tp_c] < ind): break - if (ind+.5 not in T[tp_r-1]): + if (ind+half not in T[tp_r-1]): break tp_r -= 1 - tp_c = T[tp_r].index(ind+.5) + tp_c = T[tp_r].index(ind+half) if tp_r == r: - T[r][c] = T[r][c]-1.0 + T[r][c] = T[r][c]-one elif tp_r == tp_c: - T[r][c] = T[r][c]-.5 + T[r][c] = T[r][c]-half else: - T[r][c] = T[r][c]-.5 - T[tp_r][tp_c] = T[tp_r][tp_c]-.5 + T[r][c] = T[r][c]-half + T[tp_r][tp_c] = T[tp_r][tp_c]-half - elif T[r][c-1] == ind+.5: - T[r][c-1] = T[r][c-1]-.5 - T[r][c] = T[r][c]-.5 + elif T[r][c-1] == ind+half: + T[r][c-1] = T[r][c-1]-half + T[r][c] = T[r][c]-half if r > c: - T = [[elt-.5 if elt is not None else elt + T = [[elt-half if elt is not None else elt for elt in row] for row in T] T = map(list, zip(*T)) @@ -906,7 +910,7 @@ def weight(self): return self.parent().weight_lattice_realization()(weight) -class ShiftedPrimedTableauEntry(Rational): +class PrimedEntry(Rational): """ The class of entries in shifted primed tableaux. """ @@ -923,7 +927,11 @@ def __init__(self, entry): sage: ShiftedPrimedTableau([[1,1.5]])[0][1] 2' """ + if isinstance(entry, self.__class__): + Rational.__init__(self, entry) + return half = Integer(1)/Integer(2) + if isinstance(entry, str): if (entry[-1] == "'" or entry[-1] == "p") and entry[:-1].isdigit() is True: # Check if an element has "'" or "p" at the end @@ -934,7 +942,6 @@ def __init__(self, entry): entry = Rational(entry) except (TypeError, ValueError): raise ValueError("primed elements have wrong format") - if (entry + half not in ZZ) and (entry not in ZZ): # Check if an element is a half-integer raise ValueError("all numbers must be half-integers") @@ -964,9 +971,9 @@ def __add__(self, other): sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] sage: b = a+.5 sage: type(b) - + """ - return ShiftedPrimedTableauEntry(Rational.__add__(self, float(other))) + return self.__class__(Rational(self) + Rational(other)) def __sub__(self, other): """ @@ -977,9 +984,9 @@ def __sub__(self, other): sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] sage: b = a-.5 sage: type(b) - + """ - return ShiftedPrimedTableauEntry(Rational.__sub__(self, float(other))) + return self.__class__(Rational(self) - Rational(other)) def is_unprimed(self): """ @@ -1015,8 +1022,9 @@ def unprime(self): sage: a.unprime() 2 """ + half = Integer(1)/Integer(2) if self.is_primed(): - return Integer(self + Integer(1)/Integer(2)) + return Integer(self + half) else: return Integer(self) From ed56f4b50be919d5be7c89a8a0238560861f3f01 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sun, 21 Jan 2018 15:08:31 -0800 Subject: [PATCH 508/740] Minor fix for checking half-integer --- src/sage/combinat/shifted_primed_tableau.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index d89f41bffad..4c831273b37 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -942,7 +942,7 @@ def __init__(self, entry): entry = Rational(entry) except (TypeError, ValueError): raise ValueError("primed elements have wrong format") - if (entry + half not in ZZ) and (entry not in ZZ): + if entry.denominator() > 2: # Check if an element is a half-integer raise ValueError("all numbers must be half-integers") From fe58704b1a69db9856d397cec4a4a726fbda74c1 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Sun, 21 Jan 2018 15:11:52 -0800 Subject: [PATCH 509/740] Minor fix --- src/sage/combinat/shifted_primed_tableau.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index 4c831273b37..7f00f56fd6b 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -930,19 +930,18 @@ def __init__(self, entry): if isinstance(entry, self.__class__): Rational.__init__(self, entry) return - half = Integer(1)/Integer(2) if isinstance(entry, str): if (entry[-1] == "'" or entry[-1] == "p") and entry[:-1].isdigit() is True: # Check if an element has "'" or "p" at the end - entry = Rational(entry[:-1]) - half + entry = Rational(entry[:-1]) - Integer(1)/Integer(2) Rational.__init__(self, entry) return try: entry = Rational(entry) except (TypeError, ValueError): raise ValueError("primed elements have wrong format") - if entry.denominator() > 2: + if entry.denominator() not in (1, 2): # Check if an element is a half-integer raise ValueError("all numbers must be half-integers") From 041a5fef45d6d69d36aab458f86e9e379acef94f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 21 Jan 2018 19:13:22 -0600 Subject: [PATCH 510/740] Have sep_baseline override the baseline of the separator. --- src/sage/typeset/ascii_art.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sage/typeset/ascii_art.py b/src/sage/typeset/ascii_art.py index 031e15c7172..f6191d57613 100644 --- a/src/sage/typeset/ascii_art.py +++ b/src/sage/typeset/ascii_art.py @@ -250,6 +250,18 @@ def ascii_art(*obj, **kwds): -- [1 0] -- [0 1 0] [1] [0 1] [0 0 1] + If specified, the ``sep_baseline`` overrides the baseline of + an ascii art separator:: + + sage: sep_line = ascii_art('\n'.join(' | ' for _ in range(6))) + sage: ascii_art(*Partitions(6), separator=sep_line, baseline=6) + | | | | | | | | | | * + | | | | | | | | | ** | * + | | | | | | *** | | ** | * | * + | | | **** | | *** | * | ** | ** | * | * + | ***** | **** | * | *** | ** | * | ** | * | * | * + ****** | * | ** | * | *** | * | * | ** | * | * | * + TESTS:: sage: n = var('n') From 0b72ed55a5261fe632e296b4677faa43f79e5611 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Mon, 22 Jan 2018 14:58:38 +0100 Subject: [PATCH 511/740] Change DocTestWorker.kill to return a value whether or not it actually signalled the process. Removed some of the try/excepts around it. ESRCH errors are already handled by kill() itself. I don't know what other exceptions are likely to be raised here if any but I think we'd want to know about them if they occur at all rather than ignore them. --- src/sage/doctest/forker.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index ccf81fd7f46..36b2116b26a 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -1749,12 +1749,8 @@ def sel_exit(): # Kill all remaining workers (in case we got interrupted) for w in workers: - try: - w.kill() - except Exception: - pass - else: - log("Killing test %s"%w.source.printpath) + if w.kill(): + log("Killing test %s" % w.source.printpath) # Fork a child process with the specific purpose of # killing the remaining workers. if len(workers) > 0 and os.fork() == 0: @@ -1764,10 +1760,7 @@ def sel_exit(): from time import sleep sleep(die_timeout) for w in workers: - try: - w.kill() - except Exception: - pass + w.kill() finally: os._exit(0) @@ -2062,7 +2055,8 @@ def save_result_output(self): def kill(self): """ - Kill this worker + Kill this worker. Returns ``True`` if the signal(s) are sent + successfully or ``False`` if the worker process no longer exists. This method is only called if there is something wrong with the worker. Under normal circumstances, the worker is supposed to @@ -2100,10 +2094,12 @@ def kill(self): sage: W.killed False sage: W.kill() + True sage: W.killed True sage: time.sleep(0.2) # Worker doesn't die sage: W.kill() # Worker dies now + True sage: time.sleep(0.2) sage: W.is_alive() False @@ -2125,6 +2121,10 @@ def kill(self): if exc.errno != errno.ESRCH: raise + return False + + return True + class DocTestTask(object): """ From 25ddbc9a720ee11513a2c7002650d8ec8a3b3364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Mon, 22 Jan 2018 21:17:08 +0200 Subject: [PATCH 512/740] Add the power poset. --- src/doc/en/reference/references/index.rst | 6 +++ src/sage/combinat/posets/poset_examples.py | 53 ++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 8769b5aa7f1..59d1e0db894 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -434,6 +434,12 @@ REFERENCES: .. [Br2016] *Bresenham's Line Algorithm*, Python, 26 December 2016. http://www.roguebasin.com/index.php?title=Bresenham%27s_Line_Algorithm +.. [Bru1994] Richard A. Brualdi, Hyung Chan Jung, William T.Trotter Jr + *On the poset of all posets on `n` elements* + Volume 50, Issue 2, 6 May 1994, Pages 111-123 + Discrete Applied Mathematics + http://www.sciencedirect.com/science/article/pii/0166218X9200169M + .. [Bruin-Molnar] \N. Bruin and A. Molnar, *Minimal models for rational functions in a dynamical setting*, LMS Journal of Computation and Mathematics, Volume 15 (2012), diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index 732cdeb2004..88c2b622d5c 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -37,6 +37,7 @@ :meth:`~posets.IntegerPartitionsDominanceOrder` | Return the lattice of integer partitions on the integer `n` ordered by dominance. :meth:`~posets.NoncrossingPartitions` | Return the poset of noncrossing partitions of a finite Coxeter group ``W``. :meth:`~posets.PentagonPoset` | Return the Pentagon poset. + :meth:`~posets.PowerPoset` | Return a power poset. :meth:`~posets.RandomLattice` | Return a random lattice on `n` elements. :meth:`~posets.RandomPoset` | Return a random poset on `n` elements. :meth:`~posets.RestrictedIntegerPartitions` | Return the poset of integer partitions of `n`, ordered by restricted refinement. @@ -561,6 +562,58 @@ def IntegerPartitionsDominanceOrder(n): from sage.combinat.partition import Partitions, Partition return LatticePoset((Partitions(n), Partition.dominates)).dual() + @staticmethod + def PowerPoset(n): + """ + Return the power poset on `n` element posets. + + Elements of the power poset are all posets on + the set `\{0, 1, \ldots, n-1\}` ordered by extension. + That is, the antichain of `n` elements is the bottom and + `P_a \le P_b` in the power poset if `P_b` is an extension + of `P_a`. + + These were studied in [Bru1994]_. + + EXAMPLES:: + + sage: P3 = posets.PowerPoset(3); P3 + Finite poset containing 19 elements + sage: all(P.is_chain() for P in P3.maximal_elements()) + True + + TESTS:: + + sage: P0 = posets.PowerPoset(0); P0 + Finite poset containing 1 elements + sage: P0[0] + Finite poset containing 0 elements + sage: P1 = posets.PowerPoset(1); P1 + Finite poset containing 1 elements + sage: P1[0] + Finite poset containing 1 elements + sage: P1[0][0] + 0 + """ + # Todo: Make this faster. + + try: + n = Integer(n) + except TypeError: + raise TypeError("parameter n must be an integer, not {0}".format(n)) + if n < 0: + raise ValueError("parameter n must be non-negative, not {0}".format(n)) + + all_pos_n = set() + Pn = list(Posets(n)) + for P in Pn: + for r in Permutations(P): + all_pos_n.add(P.relabel(list(r))) + + return Poset((all_pos_n, + lambda A, B: all(B.is_lequal(x, y) for x,y in A.cover_relations_iterator()) + )) + @staticmethod def RandomPoset(n, p): r""" From 5740814745244aa50e3844cb89f1063529934202 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 18 Jan 2018 11:23:50 -0500 Subject: [PATCH 513/740] Trac #24517: improve random_cone() performance when max_rays is None. The random_cone() function essentially works by generating a starting set of random rays, and then adding one random ray at a time until the current cone meets the user's specification. Often, the whole process will "reset" when it becomes obvious that this procedure will never succeed for the current cone. There is an existing test case that elicits a bug in that approach, K = random_cone(min_ambient_dim=3, max_ambient_dim=3, min_rays=7) Since max_rays is not specified, the random cone algorithm will often start with an enormous number of rays, most of which will get thrown out before the process resets and chooses a more reasonable number of starting rays. That takes far more time and space than is desirable. This commit addresses the problem by using min_rays as the starting number of rays when max_rays is unspecified (that is, None). That won't prevent the algorithm from "resetting," but it won't waste time and space generating a huge number of rays first. A second improvement (thanks to Jeroen Demeyer) to that test case was made, setting max_rays=9. Independent of the first issue, having max_rays=None forces more resets than are necessary for this test case to do its job. The algorithm is less likely to terminate (succeed) when it wants more rays, so limiting the number to [7, 9] should be faster in general than [7, infinity]. An unscientific test using the timeit() function shows about a 5s improvement on average. --- src/sage/geometry/cone.py | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index d10fee81dfc..bd24087ff17 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -6168,12 +6168,15 @@ def random_cone(lattice=None, min_ambient_dim=0, max_ambient_dim=None, generators. Please decrease min_rays. Ensure that we can obtain a cone in three dimensions with a large - number (in particular, more than 2*dim) rays:: + number (in particular, more than 2*dim) rays. The ``max_rays`` is + not strictly necessary, but it minimizes the number of times that + we will loop with an absurd, unattainable, number of rays:: sage: set_random_seed() # long time sage: K = random_cone(min_ambient_dim=3, # long time ....: max_ambient_dim=3, # long time - ....: min_rays=7) # long time + ....: min_rays=7, # long time + ....: max_rays=9) # long time sage: K.nrays() >= 7 # long time True sage: K.lattice_dim() # long time @@ -6449,14 +6452,32 @@ def is_valid(K): d = random_min_max(min_ambient_dim, max_ambient_dim) L = ToricLattice(d) + # The number of rays that we will try to attain in this iteration. r = random_min_max(min_rays, max_rays) # The rays are trickier to generate, since we could generate v and # 2*v as our "two rays." In that case, the resulting cone would - # have one generating ray. To avoid such a situation, we start by - # generating ``r`` rays where ``r`` is the number we want to end - # up with... - rays = [L.random_element() for i in range(r)] + # have only one generating ray -- not what we want. + # + # Let's begin with an easier question: how many rays should we + # start with? If we want to attain ``r`` rays in this iteration, + # then surely ``r`` is a good number to start with, even if some + # of them will be redundant? + if max_rays is None: + # Not quite... if the maximum number of rays is unbounded, + # then ``r`` could be enormous. Ultimately that wouldn't + # be a problem, because almost all of those rays would be + # thrown out. However, as we discovered in :trac:`24517`, + # simply generating all of those random rays in the first + # place (and storing them in a list) is problematic. + # + # So, when the maximum number of rays is unbounded, we use + # the safer ``min_rays`` as the number to start with. + rays = [L.random_element() for i in range(min_rays)] + else: + # But yeah, if ``max_rays`` was specified, then ``r`` is a + # better guess. + rays = [L.random_element() for i in range(r)] # The lattice parameter is required when no rays are given, so # we pass it in case ``r == 0`` or ``d == 0`` (or ``d == 1`` From ca2b0f925e62e03d011b55a5003ae2ae2cc3e342 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 18 Jan 2018 14:42:06 -0500 Subject: [PATCH 514/740] Trac #24517: simplify the recent random_cone() performance fix. A recent commit improves the performance of random_cone() when max_rays is None by adding a special case: when max_rays is None (that is, unbounded), we choose a smaller number of starting rays. The conditional is used because there is another performance issue to keep in mind: we don't want to start with too few rays, and have to add them one-at-a-time in a larger space, where the rays won't be redundant. The point of diminishing returns is when the number of rays is around twice the ambient dimension, because, for example, six rays can generate all of three-dimensional space, making any additional rays redundant. Since it is extremely unlikely that more than 2*d rays will be useful, we can unconditionally cap the number of starting rays at 2*d, rather than worry about whether or not max_rays was specified. This should still fix the issue reported in Trac #24517, but performs better when the user specifies a large number for max_rays in a small ambient space. For example, with the original fix in 6f9be50, K = random_cone(min_ambient_dim=1,max_ambient_dim=1,max_rays=10000000) could crash if we tried to construct r=10000000 initial rays in some iteration. Now that number would be capped to min(2*d,r) = 2. This has a side effect, namely that if you ask for more than 2*d rays, then you'll probably get the minimum amount, because we'll start with 2*d and add them one-at-a-time. --- src/sage/geometry/cone.py | 41 ++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index bd24087ff17..179d725b12e 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -6451,6 +6451,8 @@ def is_valid(K): # No lattice given, make our own. d = random_min_max(min_ambient_dim, max_ambient_dim) L = ToricLattice(d) + else: + d = L.dimension() # The number of rays that we will try to attain in this iteration. r = random_min_max(min_rays, max_rays) @@ -6460,24 +6462,27 @@ def is_valid(K): # have only one generating ray -- not what we want. # # Let's begin with an easier question: how many rays should we - # start with? If we want to attain ``r`` rays in this iteration, - # then surely ``r`` is a good number to start with, even if some - # of them will be redundant? - if max_rays is None: - # Not quite... if the maximum number of rays is unbounded, - # then ``r`` could be enormous. Ultimately that wouldn't - # be a problem, because almost all of those rays would be - # thrown out. However, as we discovered in :trac:`24517`, - # simply generating all of those random rays in the first - # place (and storing them in a list) is problematic. - # - # So, when the maximum number of rays is unbounded, we use - # the safer ``min_rays`` as the number to start with. - rays = [L.random_element() for i in range(min_rays)] - else: - # But yeah, if ``max_rays`` was specified, then ``r`` is a - # better guess. - rays = [L.random_element() for i in range(r)] + # start with? If we want to attain ``r`` rays in this + # iteration, then surely ``r`` is a good number to start with, + # even if some of them will be redundant? + # + # Not quite, because after ``2*d`` rays, there is a greater + # tendency for them to be redundant. If, for example, the + # maximum number of rays is unbounded, then ``r`` could be + # enormous. Ultimately that won't be a problem, because + # almost all of those rays will be thrown out. However, as we + # discovered in :trac:`24517`, simply generating all of those + # random rays in the first place (and storing them in a list) + # is problematic. + # + # Since the returns fall off around ``2*d``, we start with the + # smaller of the two numbers ``2*d`` or ``r`` to ensure that + # we don't pay a huge performance penalty for things we're + # going to throw out anyway. This has a side effect, namely + # that if you ask for more than ``2*d`` rays, then you'll + # probably get the minimum amount, because we'll start with + # ``2*d`` and add them one-at-a-time (see below). + rays = [L.random_element() for i in range(min(r,2*d))] # The lattice parameter is required when no rays are given, so # we pass it in case ``r == 0`` or ``d == 0`` (or ``d == 1`` From 2683d071d904469a01ce3949dacff8768f8df711 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 22 Jan 2018 15:43:59 -0500 Subject: [PATCH 515/740] Trac #24517: remove RST markup from non-docstring random_cone() comments. --- src/sage/geometry/cone.py | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 179d725b12e..0b559ff494a 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -6462,35 +6462,34 @@ def is_valid(K): # have only one generating ray -- not what we want. # # Let's begin with an easier question: how many rays should we - # start with? If we want to attain ``r`` rays in this - # iteration, then surely ``r`` is a good number to start with, - # even if some of them will be redundant? + # start with? If we want to attain r rays in this iteration, + # then surely r is a good number to start with, even if some + # of them will be redundant? # - # Not quite, because after ``2*d`` rays, there is a greater + # Not quite, because after 2*d rays, there is a greater # tendency for them to be redundant. If, for example, the - # maximum number of rays is unbounded, then ``r`` could be - # enormous. Ultimately that won't be a problem, because - # almost all of those rays will be thrown out. However, as we - # discovered in :trac:`24517`, simply generating all of those - # random rays in the first place (and storing them in a list) - # is problematic. + # maximum number of rays is unbounded, then r could be enormous + # Ultimately that won't be a problem, because almost all of + # those rays will be thrown out. However, as we discovered in + # Trac #24517, simply generating the random rays in the first + # place (and storing them in a list) is problematic. # - # Since the returns fall off around ``2*d``, we start with the - # smaller of the two numbers ``2*d`` or ``r`` to ensure that - # we don't pay a huge performance penalty for things we're - # going to throw out anyway. This has a side effect, namely - # that if you ask for more than ``2*d`` rays, then you'll - # probably get the minimum amount, because we'll start with - # ``2*d`` and add them one-at-a-time (see below). + # Since the returns fall off around 2*d, we start with the + # smaller of the two numbers 2*d or r to ensure that we don't + # pay a huge performance penalty for things we're going to + # throw out anyway. This has a side effect, namely that if you + # ask for more than 2*d rays, then you'll probably get the + # minimum amount, because we'll start with 2*d and add them + # one-at-a-time (see below). rays = [L.random_element() for i in range(min(r,2*d))] # The lattice parameter is required when no rays are given, so - # we pass it in case ``r == 0`` or ``d == 0`` (or ``d == 1`` - # but we're making a strictly convex cone). + # we pass it in case r == 0 or d == 0 (or d == 1 but we're + # making a strictly convex cone). K = Cone(rays, lattice=L) # Now, some of the rays that we generated were probably redundant, - # so we need to come up with more. We can obviously stop if ``K`` + # so we need to come up with more. We can obviously stop if K # becomes the entire ambient vector space. # # We're still not guaranteed to have the correct number of From c2bf3b9585787fa1cbc40982d6fa4dfa9cfa32a9 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Tue, 23 Jan 2018 12:27:13 +0100 Subject: [PATCH 516/740] Actually use mkstemp directly instead of tmp_filename; create the tempfile in the same directory as the target file --- src/sage/misc/temporary_file.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/misc/temporary_file.py b/src/sage/misc/temporary_file.py index 41214ba68c1..e98a66d912b 100644 --- a/src/sage/misc/temporary_file.py +++ b/src/sage/misc/temporary_file.py @@ -395,15 +395,17 @@ def __enter__(self): True """ - name = tmp_filename() + fd, name = tempfile.mkstemp(dir=self.tmpdir) + name = os.path.abspath(name) rmode = 'r' + ('b' if self.binary else '') wmode = 'w+' + ('b' if self.binary else '') try: - self.tempfile = io.open(name, wmode, **self.kwargs) + self.tempfile = io.open(fd, wmode, **self.kwargs) except (TypeError, ValueError): # Some invalid arguments were passed to io.open + os.close(fd) os.unlink(name) raise From 52ddbf9ef5252a0d8c4729d23b94d3b1a58fee54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Wed, 27 Jul 2016 10:15:31 +0200 Subject: [PATCH 517/740] 21092 Unhandled case in EllipticCurve_from_cubic --- .../schemes/elliptic_curves/constructor.py | 61 +++++++++++++------ 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index 363d9144b1c..dc608ac6722 100644 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -728,10 +728,11 @@ def EllipticCurve_from_cubic(F, P, morphism=True): Construct an elliptic curve from a ternary cubic with a rational point. If you just want the Weierstrass form and are not interested in - the morphism then it is easier to use + the morphism then it is easier to use the function :func:`~sage.schemes.elliptic_curves.jacobian.Jacobian` - instead. This will construct the same elliptic curve but you don't - have to supply the point ``P``. + instead. If there is a rational point on the given cubic, this + function will construct the same elliptic curve but you do not have to + supply the point ``P``. INPUT: @@ -750,9 +751,9 @@ def EllipticCurve_from_cubic(F, P, morphism=True): An elliptic curve in long Weierstrass form isomorphic to the curve `F=0`. - If ``morphism=True`` is passed, then a birational equivalence - between F and the Weierstrass curve is returned. If the point - happens to be a flex, then this is an isomorphism. + If ``morphism=True`` is passed, then a birational isomorphism + between the curve `F=0` and the Weierstrass curve is returned. If + the point happens to be a flex, then this is a linear isomorphism. EXAMPLES: @@ -874,6 +875,18 @@ def EllipticCurve_from_cubic(F, P, morphism=True): sage: cubic = x^2*y + 4*x*y^2 + x^2*z + 8*x*y*z + 4*y^2*z + 9*x*z^2 + 9*y*z^2 sage: EllipticCurve_from_cubic(cubic, [1,-1,1], morphism=False) Elliptic Curve defined by y^2 - 882*x*y - 2560000*y = x^3 - 127281*x^2 over Rational Field + + Here is a test for :trac:`21092`:: + + sage: R. = QQ[] + sage: cubic = -3*x^2*y + 3*x*y^2 + 4*x^2*z + 4*y^2*z - 3*x*z^2 + 3*y*z^2 - 8*z^3 + sage: EllipticCurve_from_cubic(cubic, (-4/5, 4/5, 3/5) ) + Scheme morphism: + From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: + -3*x^2*y + 3*x*y^2 + 4*x^2*z + 4*y^2*z - 3*x*z^2 + 3*y*z^2 - 8*z^3 + To: Elliptic Curve defined by y^2 - 6*x*y - 112*y = x^3 + 62*x^2 + 560*x over Rational Field + Defn: Defined on coordinates by sending (x : y : z) to + (1/3*z : -y - 1/3*z : 1/112*x - 1/112*y - 1/42*z) """ import sage.matrix.all as matrix @@ -881,24 +894,38 @@ def EllipticCurve_from_cubic(F, P, morphism=True): R = F.parent() if not is_MPolynomialRing(R): raise TypeError('equation must be a polynomial') - if R.ngens() != 3: + if R.ngens() != 3 or F.nvariables() != 3: raise TypeError('equation must be a polynomial in three variables') + x, y, z = R.gens() if not F.is_homogeneous(): raise TypeError('equation must be a homogeneous polynomial') + + if len(P) != 3: + raise TypeError('%s is not a projective point' % P) K = F.parent().base_ring() try: P = [K(c) for c in P] except TypeError: - raise TypeError('cannot convert %s into %s'%(P,K)) + raise TypeError('cannot convert %s into %s' % (P, K)) if F(P) != 0: - raise ValueError('%s is not a point on %s'%(P,F)) - if len(P) != 3: - raise TypeError('%s is not a projective point'%P) - x, y, z = R.gens() + raise ValueError('%s is not a point on %s' % (P, F)) + + hessian_pol = matrix.Matrix(R, 3, 3, [[F.derivative(v1, v2) + for v1 in R.gens()] + for v2 in R.gens()]).det() + flex_point = None + if hessian_pol(P) == 0: + # case 1 : P itself is a flex + flex_point = P + else: + # case 2 : if P2 = P3 then P2 is a flex + P2 = chord_and_tangent(F, P) + P3 = chord_and_tangent(F, P2) + if are_projectively_equivalent(P2, P3, base_ring=K): + flex_point = P2 - # First case: if P = P2 then P is a flex - P2 = chord_and_tangent(F, P) - if are_projectively_equivalent(P, P2, base_ring=K): + if flex_point is not None: + P = flex_point # find the tangent to F in P dx = K(F.derivative(x)(P)) dy = K(F.derivative(y)(P)) @@ -910,7 +937,6 @@ def EllipticCurve_from_cubic(F, P, morphism=True): F_Q = F(Q) if F_Q != 0: # At most one further point may accidentally be on the cubic break - assert F_Q != 0 # pick linearly independent third point for third_point in [(1,0,0), (0,1,0), (0,0,1)]: M = matrix.matrix(K, [Q, P, third_point]).transpose() @@ -935,9 +961,8 @@ def EllipticCurve_from_cubic(F, P, morphism=True): fwd_defining_poly = [trans_x, trans_y, -b*trans_z] fwd_post = -a - # Second case: P is not a flex, then P, P2, P3 are different + # Second case: P, P2, P3 are different else: - P3 = chord_and_tangent(F, P2) # send P, P2, P3 to (1:0:0), (0:1:0), (0:0:1) respectively M = matrix.matrix(K, [P, P2, P3]).transpose() F2 = M.act_on_polynomial(F) From 4a6e589acfaae3c31245fc866356982db33f420a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 23 Jan 2018 15:55:05 +0100 Subject: [PATCH 518/740] 21092 better ? --- src/sage/schemes/elliptic_curves/constructor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index dc608ac6722..437b8194375 100644 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -918,10 +918,9 @@ def EllipticCurve_from_cubic(F, P, morphism=True): # case 1 : P itself is a flex flex_point = P else: - # case 2 : if P2 = P3 then P2 is a flex + # case 2 : P2 is a flex P2 = chord_and_tangent(F, P) - P3 = chord_and_tangent(F, P2) - if are_projectively_equivalent(P2, P3, base_ring=K): + if hessian_pol(P2) == 0: flex_point = P2 if flex_point is not None: @@ -963,6 +962,7 @@ def EllipticCurve_from_cubic(F, P, morphism=True): # Second case: P, P2, P3 are different else: + P3 = chord_and_tangent(F, P2) # send P, P2, P3 to (1:0:0), (0:1:0), (0:0:1) respectively M = matrix.matrix(K, [P, P2, P3]).transpose() F2 = M.act_on_polynomial(F) From 2c6f7d5cca8039363968cb286583e0095607a1c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 23 Jan 2018 15:58:05 +0100 Subject: [PATCH 519/740] trac 21092 details of doc --- src/sage/schemes/elliptic_curves/constructor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index 437b8194375..7395b09089d 100644 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -785,9 +785,9 @@ def EllipticCurve_from_cubic(F, P, morphism=True): sage: E.conductor() 24300 - We can also get the birational equivalence to and from the + We can also get the birational isomorphism to and from the Weierstrass form. We start with an example where ``P`` is a flex - and the equivalence is an isomorphism:: + and the equivalence is a linear isomorphism:: sage: f = EllipticCurve_from_cubic(cubic, P, morphism=True) sage: f From 5d044bfb4c8ada79db6e8b0147a1f1120cf49a9d Mon Sep 17 00:00:00 2001 From: Vincent Klein Date: Tue, 23 Jan 2018 18:17:29 +0100 Subject: [PATCH 520/740] Trac #22928: Add tests and fix a bug with precision compex_number : Avoid using .real and .imag of gmpy.mpc each one carrying their own precision. Add precision tests for MPComplexField ComplexField and RealField --- src/sage/rings/complex_mpc.pyx | 26 +++++++++++++++++--------- src/sage/rings/complex_number.pyx | 25 ++++++++++++++++++------- src/sage/rings/real_mpfr.pyx | 18 +++++++++++++----- 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/src/sage/rings/complex_mpc.pyx b/src/sage/rings/complex_mpc.pyx index bf4befef518..bf767b4d339 100644 --- a/src/sage/rings/complex_mpc.pyx +++ b/src/sage/rings/complex_mpc.pyx @@ -1267,20 +1267,28 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): sage: MPC = MPComplexField() sage: c = MPC(2,1) - sage: c.__mpc__() # optional - gmpy2 + sage: c.__mpc__() # optional - gmpy2 mpc('2.0+1.0j') - sage: from gmpy2 import mpc # optional - gmpy2 - sage: mpc(c) # optional - gmpy2 + sage: from gmpy2 import mpc # optional - gmpy2 + sage: mpc(c) # optional - gmpy2 mpc('2.0+1.0j') - sage: MPCF = MPComplexField(42) # optional - gmpy2 - sage: mpc(MPCF(12, 12)).precision # optional - gmpy2 + sage: MPCF = MPComplexField(42) + sage: mpc(MPCF(12, 12)).precision # optional - gmpy2 (42, 42) - sage: MPCF = MPComplexField(236) # optional - gmpy2 - sage: mpc(MPCF(12, 12)).precision # optional - gmpy2 + sage: MPCF = MPComplexField(236) + sage: mpc(MPCF(12, 12)).precision # optional - gmpy2 (236, 236) - sage: MPCF = MPComplexField(63) # optional - gmpy2 - sage: mpc(MPCF('15.64E+128', '15.64E+128')).precision # optional - gmpy2 + sage: MPCF = MPComplexField(63) + sage: x = MPCF('15.64E+128', '15.64E+128') + sage: y = mpc(x) # optional - gmpy2 + sage: y.precision # optional - gmpy2 (63, 63) + sage: MPCF(y) == x # optional - gmpy2 + True + sage: x = mpc('1.324+4e50j', precision=(70,70)) # optional - gmpy2 + sage: y = MPComplexField(70)(x) # optional - gmpy2 + sage: mpc(y) == x # optional - gmpy2 + True TESTS:: diff --git a/src/sage/rings/complex_number.pyx b/src/sage/rings/complex_number.pyx index ccf1d330128..8cdf7e934fb 100644 --- a/src/sage/rings/complex_number.pyx +++ b/src/sage/rings/complex_number.pyx @@ -193,7 +193,9 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): elif isinstance(real, complex): real, imag = real.real, real.imag elif HAVE_GMPY2 and type(real) is gmpy2.mpc: - real, imag = (real).real, (real).imag + mpfr_set(self.__re, ((real).c).re, rnd) + mpfr_set(self.__im, ((real).c).im, rnd) + return else: imag = 0 try: @@ -607,15 +609,24 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): sage: from gmpy2 import mpc # optional - gmpy2 sage: mpc(c) # optional - gmpy2 mpc('2.0+1.0j') - sage: cf = ComplexField(134) # optional - gmpy2 - sage: mpc(cf.pi()).precision # optional - gmpy2 + sage: CF = ComplexField(134) + sage: mpc(CF.pi()).precision # optional - gmpy2 (134, 134) - sage: cf = ComplexField(45) # optional - gmpy2 - sage: mpc(cf.zeta(5)).precision # optional - gmpy2 + sage: CF = ComplexField(45) + sage: mpc(CF.zeta(5)).precision # optional - gmpy2 (45, 45) - sage: cf = ComplexField(255) # optional - gmpy2 - sage: mpc(cf.zeta(5)).precision # optional - gmpy2 + sage: CF = ComplexField(255) + sage: x = CF(5, 8) + sage: y = mpc(x) # optional - gmpy2 + sage: y.precision # optional - gmpy2 (255, 255) + sage: CF(y) == x # optional - gmpy2 + True + sage: x = mpc('1.324+4e50j', precision=(70,70)) # optional - gmpy2 + sage: CF = ComplexField(70) + sage: y = CF(x) # optional - gmpy2 + sage: x == mpc(y) # optional - gmpy2 + True TESTS:: diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 658d830a0dd..9a3ea524f75 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -3739,15 +3739,23 @@ cdef class RealNumber(sage.structure.element.RingElement): sage: from gmpy2 import mpfr # optional - gmpy2 sage: mpfr(RR(4.5)) # optional - gmpy2 mpfr('4.5') - sage: R = RealField(256) # optional - gmpy2 - sage: mpfr(R.pi()).precision # optional - gmpy2 - 256 - sage: R = RealField(127) # optional - gmpy2 + sage: R = RealField(127) sage: mpfr(R.pi()).precision # optional - gmpy2 127 - sage: R = RealField(42) # optional - gmpy2 + sage: R = RealField(42) sage: mpfr(R.pi()).precision # optional - gmpy2 42 + sage: R = RealField(256) + sage: x = mpfr(R.pi()) + sage: x.precision # optional - gmpy2 + 256 + sage: y = R(x) # optional - gmpy2 + sage: mpfr(y) == x # optional - gmpy2 + True + sage: x = mpfr('2.567e42', precision=128) # optional - gmpy2 + sage: y = RealField(128)(x) + sage: mpfr(y) == x + True TESTS:: From e1c1fa74803213b4d596a83edd198d66ff02211b Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 8 Jun 2017 13:59:03 +0200 Subject: [PATCH 521/740] Upgrade to ipywidgets 7 --- build/pkgs/ipywidgets/checksums.ini | 6 +- build/pkgs/ipywidgets/package-version.txt | 2 +- .../pkgs/ipywidgets/patches/widget_repr.patch | 45 ----- build/pkgs/widgetsnbextension/checksums.ini | 6 +- .../widgetsnbextension/package-version.txt | 2 +- src/sage/interacts/test_jupyter.rst | 171 +++++++++--------- src/sage/repl/ipython_kernel/interact.py | 34 ++-- src/sage/repl/ipython_kernel/widgets.py | 20 +- .../repl/ipython_kernel/widgets_sagenb.py | 68 +++---- 9 files changed, 155 insertions(+), 199 deletions(-) delete mode 100644 build/pkgs/ipywidgets/patches/widget_repr.patch diff --git a/build/pkgs/ipywidgets/checksums.ini b/build/pkgs/ipywidgets/checksums.ini index 14417ad5174..d86ccfbe1c1 100644 --- a/build/pkgs/ipywidgets/checksums.ini +++ b/build/pkgs/ipywidgets/checksums.ini @@ -1,4 +1,4 @@ tarball=ipywidgets-VERSION.tar.gz -sha1=3ec713b82cc415ebe63e2971cc7acae28e89d657 -md5=245c978e0ca64b9e686b716c71efb675 -cksum=3038350627 +sha1=8b32ce2f82665ab500b7f359bf93bbf803731cff +md5=4538e533c99ac950d9bc39fdb8d07610 +cksum=475923855 diff --git a/build/pkgs/ipywidgets/package-version.txt b/build/pkgs/ipywidgets/package-version.txt index 09b254e90c6..21c8c7b46b8 100644 --- a/build/pkgs/ipywidgets/package-version.txt +++ b/build/pkgs/ipywidgets/package-version.txt @@ -1 +1 @@ -6.0.0 +7.1.1 diff --git a/build/pkgs/ipywidgets/patches/widget_repr.patch b/build/pkgs/ipywidgets/patches/widget_repr.patch deleted file mode 100644 index 2611eb278a2..00000000000 --- a/build/pkgs/ipywidgets/patches/widget_repr.patch +++ /dev/null @@ -1,45 +0,0 @@ -commit aadd75cb60d73638a24c8f69e5fbd0c5c8a44780 -Author: Jeroen Demeyer -Date: Thu Jan 5 17:34:52 2017 +0100 - - Add __repr__ to Widget for testing/debugging - -diff --git a/ipywidgets/widgets/widget.py b/ipywidgets/widgets/widget.py -index 8f4abd3..db474ac 100644 ---- a/ipywidgets/widgets/widget.py -+++ b/ipywidgets/widgets/widget.py -@@ -123,6 +123,34 @@ class Widget(LoggingConfigurable): - widgets = {} - widget_types = {} - -+ def __repr__(self): -+ """ -+ Textual representation of this widget, mainly used for testing -+ and debugging. -+ """ -+ # List of attributes to show with default values. The attribute -+ # is printed if it exists and its value is not equal to this -+ # default value. -+ attributes = [("value", None), -+ ("min", None), -+ ("max", None), -+ ("step", None), -+ ("options", None), -+ ("description", ""), -+ ("children", [])] -+ r = "" -+ for (attr, default) in attributes: -+ try: -+ v = getattr(self, attr) -+ except AttributeError: -+ continue -+ if default == v: -+ continue -+ if r: -+ r += ", " -+ r += attr + "=" + repr(v) -+ return "%s(%s)" % (type(self).__name__, r) -+ - @staticmethod - def on_widget_constructed(callback): - """Registers a callback to be called when a widget is constructed. diff --git a/build/pkgs/widgetsnbextension/checksums.ini b/build/pkgs/widgetsnbextension/checksums.ini index 0caa7094e8e..f78d5c55ef8 100644 --- a/build/pkgs/widgetsnbextension/checksums.ini +++ b/build/pkgs/widgetsnbextension/checksums.ini @@ -1,4 +1,4 @@ tarball=widgetsnbextension-VERSION.tar.gz -sha1=9a616f9c4832bfda289bad1fef8766e03664be95 -md5=4a20d3c3fee43dba15da2e7c1ad6bbf3 -cksum=2004624864 +sha1=91b389948f7af499834d8d12c8da74c93daa44e1 +md5=f5896c30dbc170d7a637b437370a1a7a +cksum=2904759160 diff --git a/build/pkgs/widgetsnbextension/package-version.txt b/build/pkgs/widgetsnbextension/package-version.txt index 227cea21564..94ff29cc4de 100644 --- a/build/pkgs/widgetsnbextension/package-version.txt +++ b/build/pkgs/widgetsnbextension/package-version.txt @@ -1 +1 @@ -2.0.0 +3.1.1 diff --git a/src/sage/interacts/test_jupyter.rst b/src/sage/interacts/test_jupyter.rst index 36f8abeddc6..47b1eb2a847 100644 --- a/src/sage/interacts/test_jupyter.rst +++ b/src/sage/interacts/test_jupyter.rst @@ -6,7 +6,7 @@ Test the Sage interact library in Jupyter We need to setup a proper test environment for widgets:: - sage: from ipywidgets.widgets.tests import setup_test_comm + sage: from ipywidgets.widgets.tests.utils import setup_test_comm sage: setup_test_comm() Make sure that we use the Jupyter interacts:: @@ -39,12 +39,12 @@ Test all interacts from the Sage interact library:: sage: test(interacts.algebra.polar_prime_spiral) # long time Interactive function with 6 widgets - interval: IntRangeSlider(value=(1, 1000), min=1, max=4000, step=10, description=u'range') + interval: IntRangeSlider(value=(1, 1000), description=u'range', max=4000, min=1, step=10) show_factors: Checkbox(value=True, description=u'show_factors') highlight_primes: Checkbox(value=True, description=u'highlight_primes') show_curves: Checkbox(value=True, description=u'show_curves') - n: IntSlider(value=89, min=1, max=200, step=1, description=u'number $n$') - dpi: IntSlider(value=100, min=10, max=300, step=10, description=u'dpi') + n: IntSlider(value=89, description=u'number $n$', max=200, min=1) + dpi: IntSlider(value=100, description=u'dpi', max=300, min=10, step=10)

Polar Prime Spiral

Pink Curve: @@ -53,27 +53,27 @@ Test all interacts from the Sage interact library:: sage: test(interacts.calculus.taylor_polynomial) Interactive function with 3 widgets title: HTMLText(value=u'

Taylor polynomial

') - f: EvalText(value=u'e^(-x)*sin(x)', description=u'$f(x)=$') - order: SelectionSlider(value=1, options=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], description=u'order') + f: EvalText(value=u'e^(-x)*sin(x)', description=u'$f(x)=$', layout=Layout(max_width=u'81em')) + order: SelectionSlider(description=u'order', options=(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), value=1) sage: test(interacts.calculus.definite_integral) Interactive function with 6 widgets title: HTMLText(value=u'

Definite integral

') - f: EvalText(value=u'3*x', description=u'$f(x)=$') - g: EvalText(value=u'x^2', description=u'$g(x)=$') - interval: IntRangeSlider(value=(0, 3), min=-10, max=10, step=1, description=u'Interval') - x_range: IntRangeSlider(value=(0, 3), min=-10, max=10, step=1, description=u'plot range (x)') - selection: Dropdown(value='f and g', options=['f', 'g', 'f and g', 'f - g'], description=u'Select') + f: EvalText(value=u'3*x', description=u'$f(x)=$', layout=Layout(max_width=u'81em')) + g: EvalText(value=u'x^2', description=u'$g(x)=$', layout=Layout(max_width=u'81em')) + interval: IntRangeSlider(value=(0, 3), description=u'Interval', max=10, min=-10) + x_range: IntRangeSlider(value=(0, 3), description=u'plot range (x)', max=10, min=-10) + selection: Dropdown(description=u'Select', index=2, options=('f', 'g', 'f and g', 'f - g'), value='f and g')
sage: test(interacts.calculus.function_derivative) Interactive function with 4 widgets title: HTMLText(value=u'

Derivative grapher

') - function: EvalText(value=u'x^5-3*x^3+1', description=u'Function:') - x_range: FloatRangeSlider(value=(-2.0, 2.0), min=-15.0, max=15.0, step=0.1, description=u'Range (x)') - y_range: FloatRangeSlider(value=(-8.0, 6.0), min=-15.0, max=15.0, step=0.1, description=u'Range (y)') + function: EvalText(value=u'x^5-3*x^3+1', description=u'Function:', layout=Layout(max_width=u'81em')) + x_range: FloatRangeSlider(value=(-2.0, 2.0), description=u'Range (x)', max=15.0, min=-15.0) + y_range: FloatRangeSlider(value=(-8.0, 6.0), description=u'Range (y)', max=15.0, min=-15.0)
@@ -81,10 +81,10 @@ Test all interacts from the Sage interact library:: sage: test(interacts.calculus.difference_quotient) Interactive function with 5 widgets title: HTMLText(value=u'

Difference quotient

') - f: EvalText(value=u'sin(x)', description=u'f(x)') - interval: FloatRangeSlider(value=(0.0, 10.0), min=0.0, max=10.0, step=0.1, description=u'Range') - a: IntSlider(value=5, min=0, max=10, step=1, description=u'$a$') - x0: IntSlider(value=2, min=0, max=10, step=1, description=u'$x_0$ (start point)') + f: EvalText(value=u'sin(x)', description=u'f(x)', layout=Layout(max_width=u'81em')) + interval: FloatRangeSlider(value=(0.0, 10.0), description=u'Range', max=10.0) + a: IntSlider(value=5, description=u'$a$', max=10) + x0: IntSlider(value=2, description=u'$x_0$ (start point)', max=10)

Difference Quotient

Difference Quotient

@@ -95,9 +95,9 @@ Test all interacts from the Sage interact library:: sage: test(interacts.calculus.quadratic_equation) Interactive function with 3 widgets - A: IntSlider(value=1, min=-7, max=7, step=1, description=u'A') - B: IntSlider(value=1, min=-7, max=7, step=1, description=u'B') - C: IntSlider(value=-2, min=-7, max=7, step=1, description=u'C') + A: IntSlider(value=1, description=u'A', max=7, min=-7) + B: IntSlider(value=1, description=u'B', max=7, min=-7) + C: IntSlider(value=-2, description=u'C', max=7, min=-7)

The Solutions of the Quadratic Equation

@@ -105,9 +105,9 @@ Test all interacts from the Sage interact library:: sage: test(interacts.calculus.trigonometric_properties_triangle) Interactive function with 3 widgets - a0: IntSlider(value=30, min=0, max=360, step=1, description=u'A') - a1: IntSlider(value=180, min=0, max=360, step=1, description=u'B') - a2: IntSlider(value=300, min=0, max=360, step=1, description=u'C') + a0: IntSlider(value=30, description=u'A', max=360) + a1: IntSlider(value=180, description=u'B', max=360) + a2: IntSlider(value=300, description=u'C', max=360)

Trigonometric Properties of a Triangle

@@ -116,10 +116,10 @@ Test all interacts from the Sage interact library:: sage: test(interacts.calculus.secant_method) Interactive function with 5 widgets title: HTMLText(value=u'

Secant method for numerical root finding

') - f: EvalText(value=u'x^2-2', description=u'f(x)') - interval: IntRangeSlider(value=(0, 4), min=-5, max=5, step=1, description=u'range') - d: IntSlider(value=3, min=1, max=16, step=1, description=u'10^-d precision') - maxn: IntSlider(value=10, min=0, max=15, step=1, description=u'max iterations') + f: EvalText(value=u'x^2-2', description=u'f(x)', layout=Layout(max_width=u'81em')) + interval: IntRangeSlider(value=(0, 4), description=u'range', max=5, min=-5) + d: IntSlider(value=3, description=u'10^-d precision', max=16, min=1) + maxn: IntSlider(value=10, description=u'max iterations', max=15) @@ -128,11 +128,11 @@ Test all interacts from the Sage interact library:: sage: test(interacts.calculus.newton_method) Interactive function with 7 widgets title: HTMLText(value=u'

Newton method

') - f: EvalText(value=u'x^2 - 2', description=u'f') - c: IntSlider(value=6, min=-10, max=10, step=1, description=u'Start ($x$)') - d: IntSlider(value=3, min=1, max=16, step=1, description=u'$10^{-d}$ precision') - maxn: IntSlider(value=10, min=0, max=15, step=1, description=u'max iterations') - interval: IntRangeSlider(value=(0, 6), min=-10, max=10, step=1, description=u'Interval') + f: EvalText(value=u'x^2 - 2', description=u'f', layout=Layout(max_width=u'81em')) + c: IntSlider(value=6, description=u'Start ($x$)', max=10, min=-10) + d: IntSlider(value=3, description=u'$10^{-d}$ precision', max=16, min=1) + maxn: IntSlider(value=10, description=u'max iterations', max=15) + interval: IntRangeSlider(value=(0, 6), description=u'Interval', max=10, min=-10) list_steps: Checkbox(value=False, description=u'List steps') @@ -142,12 +142,12 @@ Test all interacts from the Sage interact library:: sage: test(interacts.calculus.trapezoid_integration) Interactive function with 7 widgets title: HTMLText(value=u'

Trapezoid integration

') - f: EvalText(value=u'x^2-5*x + 10', description=u'$f(x)=$') - n: IntSlider(value=5, min=1, max=100, step=1, description=u'# divisions') - interval_input: ToggleButtons(value='from slider', options=['from slider', 'from keyboard'], description=u'Integration interval') - interval_s: IntRangeSlider(value=(0, 8), min=-10, max=10, step=1, description=u'slider: ') - interval_g: Grid(value=[[0, 8]], description=u'keyboard: ', children=(Label(value=u'keyboard: '), VBox(children=(EvalText(value=u'0'),)), VBox(children=(EvalText(value=u'8'),)))) - output_form: ToggleButtons(value='traditional', options=['traditional', 'table', 'none'], description=u'Computations form') + f: EvalText(value=u'x^2-5*x + 10', description=u'$f(x)=$', layout=Layout(max_width=u'81em')) + n: IntSlider(value=5, description=u'# divisions', min=1) + interval_input: ToggleButtons(description=u'Integration interval', options=('from slider', 'from keyboard'), value='from slider') + interval_s: IntRangeSlider(value=(0, 8), description=u'slider: ', max=10, min=-10) + interval_g: Grid(value=[[0, 8]], children=(Label(value=u'keyboard: '), VBox(children=(EvalText(value=u'0', layout=Layout(max_width=u'5em')),)), VBox(children=(EvalText(value=u'8', layout=Layout(max_width=u'5em')),)))) + output_form: ToggleButtons(description=u'Computations form', options=('traditional', 'table', 'none'), value='traditional') Function Integral value to seven decimal places is: @@ -165,12 +165,12 @@ Test all interacts from the Sage interact library:: sage: test(interacts.calculus.simpson_integration) Interactive function with 7 widgets title: HTMLText(value=u'

Simpson integration

') - f: EvalText(value=u'x*sin(x)+x+1', description=u'$f(x)=$') - n: IntSlider(value=6, min=2, max=100, step=2, description=u'# divisions') - interval_input: ToggleButtons(value='from slider', options=['from slider', 'from keyboard'], description=u'Integration interval') - interval_s: IntRangeSlider(value=(0, 10), min=-10, max=10, step=1, description=u'slider: ') - interval_g: Grid(value=[[0, 10]], description=u'keyboard: ', children=(Label(value=u'keyboard: '), VBox(children=(EvalText(value=u'0'),)), VBox(children=(EvalText(value=u'10'),)))) - output_form: ToggleButtons(value='traditional', options=['traditional', 'table', 'none'], description=u'Computations form') + f: EvalText(value=u'x*sin(x)+x+1', description=u'$f(x)=$', layout=Layout(max_width=u'81em')) + n: IntSlider(value=6, description=u'# divisions', min=2, step=2) + interval_input: ToggleButtons(description=u'Integration interval', options=('from slider', 'from keyboard'), value='from slider') + interval_s: IntRangeSlider(value=(0, 10), description=u'slider: ', max=10, min=-10) + interval_g: Grid(value=[[0, 10]], children=(Label(value=u'keyboard: '), VBox(children=(EvalText(value=u'0', layout=Layout(max_width=u'5em')),)), VBox(children=(EvalText(value=u'10', layout=Layout(max_width=u'5em')),)))) + output_form: ToggleButtons(description=u'Computations form', options=('traditional', 'table', 'none'), value='traditional') Function Integral value to seven decimal places is: @@ -188,10 +188,10 @@ Test all interacts from the Sage interact library:: sage: test(interacts.calculus.bisection_method) Interactive function with 5 widgets title: HTMLText(value=u'

Bisection method

') - f: EvalText(value=u'x^2-2', description=u'f(x)') - interval: IntRangeSlider(value=(0, 4), min=-5, max=5, step=1, description=u'range') - d: IntSlider(value=3, min=1, max=8, step=1, description=u'$10^{-d}$ precision') - maxn: IntSlider(value=10, min=0, max=50, step=1, description=u'max iterations') + f: EvalText(value=u'x^2-2', description=u'f(x)', layout=Layout(max_width=u'81em')) + interval: IntRangeSlider(value=(0, 4), description=u'range', max=5, min=-5) + d: IntSlider(value=3, description=u'$10^{-d}$ precision', max=8, min=1) + maxn: IntSlider(value=10, description=u'max iterations', max=50) @@ -200,12 +200,12 @@ Test all interacts from the Sage interact library:: sage: test(interacts.calculus.riemann_sum) Manual interactive function with 9 widgets title: HTMLText(value=u'

Riemann integral with random sampling

') - f: EvalText(value=u'x^2+1', description=u'$f(x)=$') - n: IntSlider(value=5, min=1, max=30, step=1, description=u'# divisions') + f: EvalText(value=u'x^2+1', description=u'$f(x)=$', layout=Layout(max_width=u'41em')) + n: IntSlider(value=5, description=u'# divisions', max=30, min=1) hr1: HTMLText(value=u'
') - interval_input: ToggleButtons(value='from slider', options=['from slider', 'from keyboard'], description=u'Integration interval') - interval_s: IntRangeSlider(value=(0, 2), min=-5, max=10, step=1, description=u'slider: ') - interval_g: Grid(value=[[0, 2]], description=u'keyboard: ', children=(Label(value=u'keyboard: '), VBox(children=(EvalText(value=u'0'),)), VBox(children=(EvalText(value=u'2'),)))) + interval_input: ToggleButtons(description=u'Integration interval', options=('from slider', 'from keyboard'), value='from slider') + interval_s: IntRangeSlider(value=(0, 2), description=u'slider: ', max=10, min=-5) + interval_g: Grid(value=[[0, 2]], children=(Label(value=u'keyboard: '), VBox(children=(EvalText(value=u'0', layout=Layout(max_width=u'5em')),)), VBox(children=(EvalText(value=u'2', layout=Layout(max_width=u'5em')),)))) hr2: HTMLText(value=u'
') list_table: Checkbox(value=False, description=u'List table') Adjust your data and click Update button. Click repeatedly for another random values. @@ -216,10 +216,10 @@ Test all interacts from the Sage interact library:: Interactive function with 7 widgets f: EvalText(value=u'sin(x)', description=u'f') g: EvalText(value=u'cos(x)', description=u'g') - xrange: IntRangeSlider(value=(0, 1), min=-3, max=3, step=1, description=u'x-range') + xrange: IntRangeSlider(value=(0, 1), description=u'x-range', max=3, min=-3) yrange: Text(value=u'auto', description=u'yrange') - a: IntSlider(value=1, min=-1, max=3, step=1, description=u'a') - action: ToggleButtons(value='f', options=['f', 'df/dx', 'int f', 'num f', 'den f', '1/f', 'finv', 'f+a', 'f-a', 'f*a', 'f/a', 'f^a', 'f(x+a)', 'f(x*a)', 'f+g', 'f-g', 'f*g', 'f/g', 'f(g)'], description=u'h = ') + a: IntSlider(value=1, description=u'a', max=3, min=-1) + action: ToggleButtons(description=u'h = ', options=('f', 'df/dx', 'int f', 'num f', 'den f', '1/f', 'finv', 'f+a', 'f-a', 'f*a', 'f/a', 'f^a', 'f(x+a)', 'f(x*a)', 'f+g', 'f-g', 'f*g', 'f/g', 'f(g)'), value='f') do_plot: Checkbox(value=True, description=u'Draw Plots')
@@ -227,46 +227,46 @@ Test all interacts from the Sage interact library:: sage: test(interacts.fractals.mandelbrot) Interactive function with 6 widgets - expo: FloatSlider(value=2.0, min=-10.0, max=10.0, step=0.1, description=u'expo') - iterations: IntSlider(value=20, min=1, max=100, step=1, description=u'# iterations') - zoom_x: FloatRangeSlider(value=(-2.0, 1.0), min=-2.0, max=2.0, step=0.01, description=u'Zoom X') - zoom_y: FloatRangeSlider(value=(-1.5, 1.5), min=-2.0, max=2.0, step=0.01, description=u'Zoom Y') - plot_points: IntSlider(value=150, min=20, max=400, step=20, description=u'plot points') - dpi: IntSlider(value=80, min=20, max=200, step=10, description=u'dpi') + expo: FloatSlider(value=2.0, description=u'expo', max=10.0, min=-10.0) + iterations: IntSlider(value=20, description=u'# iterations', min=1) + zoom_x: FloatRangeSlider(value=(-2.0, 1.0), description=u'Zoom X', max=2.0, min=-2.0, step=0.01) + zoom_y: FloatRangeSlider(value=(-1.5, 1.5), description=u'Zoom Y', max=2.0, min=-2.0, step=0.01) + plot_points: IntSlider(value=150, description=u'plot points', max=400, min=20, step=20) + dpi: IntSlider(value=80, description=u'dpi', max=200, min=20, step=10)

Mandelbrot Fractal

Recursive Formula: for sage: test(interacts.fractals.julia) Interactive function with 8 widgets - expo: FloatSlider(value=2.0, min=-10.0, max=10.0, step=0.1, description=u'expo') - c_real: FloatSlider(value=0.5, min=-2.0, max=2.0, step=0.01, description=u'real part const.') - c_imag: FloatSlider(value=0.5, min=-2.0, max=2.0, step=0.01, description=u'imag part const.') - iterations: IntSlider(value=20, min=1, max=100, step=1, description=u'# iterations') - zoom_x: FloatRangeSlider(value=(-1.5, 1.5), min=-2.0, max=2.0, step=0.01, description=u'Zoom X') - zoom_y: FloatRangeSlider(value=(-1.5, 1.5), min=-2.0, max=2.0, step=0.01, description=u'Zoom Y') - plot_points: IntSlider(value=150, min=20, max=400, step=20, description=u'plot points') - dpi: IntSlider(value=80, min=20, max=200, step=10, description=u'dpi') + expo: FloatSlider(value=2.0, description=u'expo', max=10.0, min=-10.0) + c_real: FloatSlider(value=0.5, description=u'real part const.', max=2.0, min=-2.0, step=0.01) + c_imag: FloatSlider(value=0.5, description=u'imag part const.', max=2.0, min=-2.0, step=0.01) + iterations: IntSlider(value=20, description=u'# iterations', min=1) + zoom_x: FloatRangeSlider(value=(-1.5, 1.5), description=u'Zoom X', max=2.0, min=-2.0, step=0.01) + zoom_y: FloatRangeSlider(value=(-1.5, 1.5), description=u'Zoom Y', max=2.0, min=-2.0, step=0.01) + plot_points: IntSlider(value=150, description=u'plot points', max=400, min=20, step=20) + dpi: IntSlider(value=80, description=u'dpi', max=200, min=20, step=10)

Julia Fractal

Recursive Formula: sage: test(interacts.fractals.cellular_automaton) Interactive function with 3 widgets - N: IntSlider(value=100, min=1, max=500, step=1, description=u'Number of iterations') - rule_number: IntSlider(value=110, min=0, max=255, step=1, description=u'Rule number') - size: IntSlider(value=6, min=1, max=11, step=1, description=u'size of graphic') + N: IntSlider(value=100, description=u'Number of iterations', max=500, min=1) + rule_number: IntSlider(value=110, description=u'Rule number', max=255) + size: IntSlider(value=6, description=u'size of graphic', max=11, min=1)

Cellular Automaton

"A cellular automaton is a collection of "colored" cells on a grid of specified shape that evolves through a number of discrete time steps according to a set of rules based on the states of neighboring cells." — Mathworld, Cellular Automaton
Rule 110 expands to 01110110
sage: test(interacts.geometry.unit_circle) Interactive function with 2 widgets - function: Dropdown(value=0, options=[('sin(x)', 0), ('cos(x)', 1), ('tan(x)', 2)], description=u'function') - x: TransformFloatSlider(value=0.0, min=0.0, max=6.283185307179586, step=0.015707963267948967, description=u'x') + function: Dropdown(description=u'function', options=(('sin(x)', 0), ('cos(x)', 1), ('tan(x)', 2)), value=0) + x: TransformFloatSlider(value=0.0, description=u'x', max=6.283185307179586, step=0.015707963267948967)
Lines of the same color have the same length
sage: test(interacts.geometry.trigonometric_properties_triangle) Interactive function with 3 widgets - a0: IntSlider(value=30, min=0, max=360, step=1, description=u'A') - a1: IntSlider(value=180, min=0, max=360, step=1, description=u'B') - a2: IntSlider(value=300, min=0, max=360, step=1, description=u'C') + a0: IntSlider(value=30, description=u'A', max=360) + a1: IntSlider(value=180, description=u'B', max=360) + a2: IntSlider(value=300, description=u'C', max=360)

Trigonometric Properties of a Triangle

@@ -275,9 +275,9 @@ Test all interacts from the Sage interact library:: sage: test(interacts.geometry.special_points) Interactive function with 10 widgets title: HTMLText(value=u'

Special points in triangle

') - a0: IntSlider(value=30, min=0, max=360, step=1, description=u'A') - a1: IntSlider(value=180, min=0, max=360, step=1, description=u'B') - a2: IntSlider(value=300, min=0, max=360, step=1, description=u'C') + a0: IntSlider(value=30, description=u'A', max=360) + a1: IntSlider(value=180, description=u'B', max=360) + a2: IntSlider(value=300, description=u'C', max=360) show_median: Checkbox(value=False, description=u'Medians') show_pb: Checkbox(value=False, description=u'Perpendicular Bisectors') show_alt: Checkbox(value=False, description=u'Altitudes') @@ -287,7 +287,8 @@ Test all interacts from the Sage interact library:: sage: test(interacts.statistics.coin) Interactive function with 2 widgets - n: IntSlider(value=1000, min=2, max=10000, step=100, description=u'Number of Tosses') - interval: IntRangeSlider(value=(0, 0), min=0, max=1, step=1, description=u'Plotting range (y)') - doctest:...: UserWarning: Attempting to set identical bottom==top results in singular transformations; automatically expanding. + n: IntSlider(value=1000, description=u'Number of Tosses', max=10000, min=2, step=100) + interval: IntRangeSlider(value=(0, 0), description=u'Plotting range (y)', max=1) + doctest:...: UserWarning: Attempting to set identical bottom==top results + in singular transformations; automatically expanding. bottom=0.0, top=0.0 diff --git a/src/sage/repl/ipython_kernel/interact.py b/src/sage/repl/ipython_kernel/interact.py index ff2c38ccd90..66a41b4ed6c 100644 --- a/src/sage/repl/ipython_kernel/interact.py +++ b/src/sage/repl/ipython_kernel/interact.py @@ -8,7 +8,7 @@ We need to setup a proper test environment for widgets:: - sage: from ipywidgets.widgets.tests import setup_test_comm + sage: from ipywidgets.widgets.tests.utils import setup_test_comm sage: setup_test_comm() EXAMPLES:: @@ -17,10 +17,10 @@ sage: @interact ....: def f(x=(0,10)): ....: pass - Interactive function with 1 widget - x: IntSlider(value=5, min=0, max=10, step=1, description=u'x') + Interactive function with 1 widget + x: IntSlider(value=5, description=u'x', max=10) sage: f.widget.children - (IntSlider(value=5, min=0, max=10, step=1, description=u'x'), Output()) + (IntSlider(value=5, description=u'x', max=10), Output()) """ #***************************************************************************** @@ -53,10 +53,10 @@ class sage_interactive(interactive): sage: from sage.repl.ipython_kernel.interact import sage_interactive sage: def myfunc(x=10, y="hello", z=None): pass sage: sage_interactive(myfunc, x=(0,100), z=["one", "two", "three"]) - Interactive function with 3 widgets - x: IntSlider(value=10, min=0, max=100, step=1, description=u'x') + Interactive function with 3 widgets + x: IntSlider(value=10, description=u'x') y: Text(value=u'hello', description=u'y') - z: Dropdown(value='one', options=['one', 'two', 'three'], description=u'z') + z: Dropdown(description=u'z', options=('one', 'two', 'three'), value=None) """ def __init__(*args, **kwds): """ @@ -182,15 +182,15 @@ def widget_from_tuple(cls, abbrev, *args, **kwds): sage: from sage.repl.ipython_kernel.interact import sage_interactive sage: sage_interactive.widget_from_tuple( (0, 10) ) - IntSlider(value=5, min=0, max=10, step=1) + IntSlider(value=5, max=10) sage: sage_interactive.widget_from_tuple( ("number", (0, 10)) ) - IntSlider(value=5, min=0, max=10, step=1, description=u'number') + IntSlider(value=5, description=u'number', max=10) sage: sage_interactive.widget_from_tuple( (3, (0, 10)) ) - IntSlider(value=3, min=0, max=10, step=1) + IntSlider(value=3, max=10) sage: sage_interactive.widget_from_tuple( (2, dict(one=1, two=2, three=3)) ) - Dropdown(value=2, options={'three': 3, 'two': 2, 'one': 1}) + Dropdown(index=1, options={'three': 3, 'two': 2, 'one': 1}, value=2) sage: sage_interactive.widget_from_tuple( (sqrt(2), pi) ) - FloatSlider(value=2.277903107981444, min=1.4142135623730951, max=3.141592653589793, step=0.1) + FloatSlider(value=2.277903107981444, max=3.141592653589793, min=1.4142135623730951) """ # Support (description, abbrev) if len(abbrev) == 2 and isinstance(abbrev[0], str): @@ -223,17 +223,17 @@ def widget_from_iterable(cls, abbrev, *args, **kwds): sage: from sage.repl.ipython_kernel.interact import sage_interactive sage: sage_interactive.widget_from_iterable([1..5]) - Dropdown(value=1, options=[1, 2, 3, 4, 5]) + Dropdown(options=(1, 2, 3, 4, 5), value=1) sage: sage_interactive.widget_from_iterable(iter([1..5])) - SelectionSlider(value=1, options=[1, 2, 3, 4, 5]) + SelectionSlider(options=(1, 2, 3, 4, 5), value=1) sage: sage_interactive.widget_from_iterable((1..5)) - SelectionSlider(value=1, options=[1, 2, 3, 4, 5]) + SelectionSlider(options=(1, 2, 3, 4, 5), value=1) sage: sage_interactive.widget_from_iterable(x for x in [1..5]) - SelectionSlider(value=1, options=[1, 2, 3, 4, 5]) + SelectionSlider(options=(1, 2, 3, 4, 5), value=1) sage: def gen(): ....: yield 1; yield 2; yield 3; yield 4; yield 5 sage: sage_interactive.widget_from_iterable(gen()) - SelectionSlider(value=1, options=[1, 2, 3, 4, 5]) + SelectionSlider(options=(1, 2, 3, 4, 5), value=1) """ if isinstance(abbrev, Iterator): return SelectionSlider(options=list(abbrev)) diff --git a/src/sage/repl/ipython_kernel/widgets.py b/src/sage/repl/ipython_kernel/widgets.py index 6a7565f5127..bd24f893366 100644 --- a/src/sage/repl/ipython_kernel/widgets.py +++ b/src/sage/repl/ipython_kernel/widgets.py @@ -8,7 +8,7 @@ We need to setup a proper test environment for widgets:: - sage: from ipywidgets.widgets.tests import setup_test_comm + sage: from ipywidgets.widgets.tests.utils import setup_test_comm sage: setup_test_comm() """ @@ -100,7 +100,7 @@ class TransformWidget(object): sage: class TransformToggleButtons(TransformWidget, ToggleButtons): pass sage: w = TransformToggleButtons(options=["pi", "e"], transform=lambda x: x+x) sage: w - TransformToggleButtons(value='pi', options=['pi', 'e']) + TransformToggleButtons(options=('pi', 'e'), value='pi') sage: w.get_interact_value() 'pipi' """ @@ -170,7 +170,7 @@ class EvalWidget(TransformWidget): sage: class EvalToggleButtons(EvalWidget, ToggleButtons): pass sage: w = EvalToggleButtons(options=["pi", "e"], transform=lambda x: x+x) sage: w - EvalToggleButtons(value='pi', options=['pi', 'e']) + EvalToggleButtons(options=('pi', 'e'), value='pi') sage: w.get_interact_value() 2*pi """ @@ -185,7 +185,7 @@ def get_value(self): sage: class EvalDropdown(EvalWidget, Dropdown): pass sage: w = EvalDropdown(options=["the_answer"], transform=RR) sage: w - EvalDropdown(value='the_answer', options=['the_answer']) + EvalDropdown(options=('the_answer',), value='the_answer') sage: the_answer = 42 sage: w.get_value() 42 @@ -205,7 +205,7 @@ class TransformIntSlider(TransformWidget, IntSlider): sage: from sage.repl.ipython_kernel.widgets import TransformIntSlider sage: w = TransformIntSlider(min=0, max=100, value=7, transform=lambda x: x^2) sage: w - TransformIntSlider(value=7, min=0, max=100, step=1) + TransformIntSlider(value=7) sage: w.get_interact_value() 49 """ @@ -222,7 +222,7 @@ class TransformFloatSlider(TransformWidget, FloatSlider): sage: from sage.repl.ipython_kernel.widgets import TransformFloatSlider sage: w = TransformFloatSlider(min=0, max=100, value=7, transform=lambda x: sqrt(x)) sage: w - TransformFloatSlider(value=7.0, min=0.0, max=100.0, step=0.1) + TransformFloatSlider(value=7.0) sage: w.get_interact_value() 2.6457513110645907 """ @@ -239,7 +239,7 @@ class TransformIntRangeSlider(TransformWidget, IntRangeSlider): sage: from sage.repl.ipython_kernel.widgets import TransformIntRangeSlider sage: w = TransformIntRangeSlider(min=0, max=100, value=(7,9), transform=lambda x: x[1]-x[0]) sage: w - TransformIntRangeSlider(value=(7, 9), min=0, max=100, step=1) + TransformIntRangeSlider(value=(7, 9)) sage: w.get_interact_value() 2 """ @@ -256,7 +256,7 @@ class TransformFloatRangeSlider(TransformWidget, FloatRangeSlider): sage: from sage.repl.ipython_kernel.widgets import TransformFloatRangeSlider sage: w = TransformFloatRangeSlider(min=0, max=100, value=(7,9), transform=lambda x: x[1]-x[0]) sage: w - TransformFloatRangeSlider(value=(7.0, 9.0), min=0.0, max=100.0, step=1.0) + TransformFloatRangeSlider(value=(7.0, 9.0)) sage: w.get_interact_value() 2.0 """ @@ -333,7 +333,7 @@ class EvalTextarea(EvalWidget, Textarea): class SageColorPicker(ColorPicker): """ - A color picker widget return a Sage :class:`Color`. + A color picker widget returning a Sage :class:`Color`. EXAMPLES:: @@ -396,7 +396,7 @@ def __init__(self, nrows, ncols, make_widget, description=u"", transform=None): sage: w = Grid(2, 2, lambda i,j: EvalText(str(j+4*i)), ....: description="2x2 matrix", transform=matrix) sage: w - Grid(value=[[0, 1], [4, 5]], description=u'2x2 matrix', children=(Label(value=u'2x2 matrix'), VBox(children=(EvalText(value=u'0'), EvalText(value=u'4'))), VBox(children=(EvalText(value=u'1'), EvalText(value=u'5'))))) + Grid(value=[[0, 1], [4, 5]], children=(Label(value=u'2x2 matrix'), VBox(children=(EvalText(value=u'0'), EvalText(value=u'4'))), VBox(children=(EvalText(value=u'1'), EvalText(value=u'5'))))) sage: w.get_interact_value() [0 1] [4 5] diff --git a/src/sage/repl/ipython_kernel/widgets_sagenb.py b/src/sage/repl/ipython_kernel/widgets_sagenb.py index e73107b4dc1..e5e3623857d 100644 --- a/src/sage/repl/ipython_kernel/widgets_sagenb.py +++ b/src/sage/repl/ipython_kernel/widgets_sagenb.py @@ -7,7 +7,7 @@ We need to setup a proper test environment for widgets:: - sage: from ipywidgets.widgets.tests import setup_test_comm + sage: from ipywidgets.widgets.tests.utils import setup_test_comm sage: setup_test_comm() EXAMPLES:: @@ -79,7 +79,7 @@ def input_box(default=None, label=None, type=None, width=80, height=1): sage: w = input_box("4+5", type=str, label="enter a string") sage: w - TransformText(value=u'4+5', description=u'enter a string') + TransformText(value=u'4+5', description=u'enter a string', layout=Layout(max_width=u'81em')) sage: w.get_interact_value() '4+5' @@ -87,7 +87,7 @@ def input_box(default=None, label=None, type=None, width=80, height=1): sage: w = input_box("4+5") sage: w - EvalText(value=u'4+5') + EvalText(value=u'4+5', layout=Layout(max_width=u'81em')) sage: w.get_interact_value() 9 @@ -96,7 +96,7 @@ def input_box(default=None, label=None, type=None, width=80, height=1): sage: w = input_box("4+5", type=float) sage: w - EvalText(value=u'4+5') + EvalText(value=u'4+5', layout=Layout(max_width=u'81em')) sage: w.get_interact_value() 9.0 @@ -104,7 +104,7 @@ def input_box(default=None, label=None, type=None, width=80, height=1): sage: w = input_box("4+5", type=sqrt) sage: w - EvalText(value=u'4+5') + EvalText(value=u'4+5', layout=Layout(max_width=u'81em')) sage: w.get_interact_value() 3 @@ -112,10 +112,10 @@ def input_box(default=None, label=None, type=None, width=80, height=1): sage: w = input_box("4+5", width=100, height=1) sage: w - EvalText(value=u'4+5') + EvalText(value=u'4+5', layout=Layout(max_width=u'101em')) sage: w = input_box("4+5", width=100, height=5) sage: w - EvalTextarea(value=u'4+5') + EvalTextarea(value=u'4+5', layout=Layout(max_width=u'101em')) TESTS:: @@ -180,37 +180,37 @@ def slider(vmin, vmax=None, step_size=None, default=None, label=None, display_va sage: from sage.repl.ipython_kernel.all_jupyter import slider sage: slider(5, label="slide me") - TransformIntSlider(value=5, min=5, max=100, step=1, description=u'slide me') + TransformIntSlider(value=5, description=u'slide me', min=5) sage: slider(5, 20) - TransformIntSlider(value=5, min=5, max=20, step=1) + TransformIntSlider(value=5, max=20, min=5) sage: slider(5, 20, 0.5) - TransformFloatSlider(value=5.0, min=5.0, max=20.0, step=0.5) + TransformFloatSlider(value=5.0, max=20.0, min=5.0, step=0.5) sage: slider(5, 20, default=12) - TransformIntSlider(value=12, min=5, max=20, step=1) + TransformIntSlider(value=12, max=20, min=5) The parent of the inputs determines the parent of the value:: sage: w = slider(5); w - TransformIntSlider(value=5, min=5, max=100, step=1) + TransformIntSlider(value=5, min=5) sage: parent(w.get_interact_value()) Integer Ring sage: w = slider(int(5)); w - IntSlider(value=5, min=5, max=100, step=1) + IntSlider(value=5, min=5) sage: parent(w.get_interact_value()) <... 'int'> sage: w = slider(5, 20, step_size=RDF("0.1")); w - TransformFloatSlider(value=5.0, min=5.0, max=20.0, step=0.1) + TransformFloatSlider(value=5.0, max=20.0, min=5.0) sage: parent(w.get_interact_value()) Real Double Field sage: w = slider(5, 20, step_size=10/3); w - SelectionSlider(value=35/3, options=[5, 25/3, 35/3, 15, 55/3]) + SelectionSlider(index=2, options=(5, 25/3, 35/3, 15, 55/3), value=35/3) sage: parent(w.get_interact_value()) Rational Field Symbolic input is evaluated numerically:: sage: w = slider(e, pi); w - TransformFloatSlider(value=2.718281828459045, min=2.718281828459045, max=3.141592653589793, step=0.1) + TransformFloatSlider(value=2.718281828459045, max=3.141592653589793, min=2.718281828459045) sage: parent(w.get_interact_value()) Real Field with 53 bits of precision @@ -218,7 +218,7 @@ def slider(vmin, vmax=None, step_size=None, default=None, label=None, display_va possible values:: sage: slider(range(10), default=17/10) - SelectionSlider(value=2, options=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + SelectionSlider(index=2, options=(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), value=2) TESTS:: @@ -354,26 +354,26 @@ def range_slider(*args, **kwds): sage: from sage.repl.ipython_kernel.all_jupyter import range_slider sage: range_slider(5, label="slide me") - TransformIntRangeSlider(value=(28, 76), min=5, max=100, step=1, description=u'slide me') + TransformIntRangeSlider(value=(28, 76), description=u'slide me', min=5) sage: range_slider(5, 20) - TransformIntRangeSlider(value=(8, 16), min=5, max=20, step=1) + TransformIntRangeSlider(value=(8, 16), max=20, min=5) sage: range_slider(5, 20, 0.5) - TransformFloatRangeSlider(value=(8.75, 16.25), min=5.0, max=20.0, step=0.5) + TransformFloatRangeSlider(value=(8.75, 16.25), max=20.0, min=5.0, step=0.5) sage: range_slider(5, 20, default=(12,15)) - TransformIntRangeSlider(value=(12, 15), min=5, max=20, step=1) + TransformIntRangeSlider(value=(12, 15), max=20, min=5) The parent of the inputs determines the parent of the value:: sage: w = range_slider(5); w - TransformIntRangeSlider(value=(28, 76), min=5, max=100, step=1) + TransformIntRangeSlider(value=(28, 76), min=5) sage: [parent(x) for x in w.get_interact_value()] [Integer Ring, Integer Ring] sage: w = range_slider(int(5)); w - IntRangeSlider(value=(28, 76), min=5, max=100, step=1) + IntRangeSlider(value=(28, 76), min=5) sage: [parent(x) for x in w.get_interact_value()] [<... 'int'>, <... 'int'>] sage: w = range_slider(5, 20, step_size=RDF("0.1")); w - TransformFloatRangeSlider(value=(8.75, 16.25), min=5.0, max=20.0, step=0.1) + TransformFloatRangeSlider(value=(8.75, 16.25), max=20.0, min=5.0) sage: [parent(x) for x in w.get_interact_value()] [Real Double Field, Real Double Field] @@ -442,31 +442,31 @@ def selector(values, label=None, default=None, nrows=None, ncols=None, width=Non sage: from sage.repl.ipython_kernel.all_jupyter import selector sage: selector(range(5), label="choose one") - Dropdown(value=0, options=[0, 1, 2, 3, 4], description=u'choose one') + Dropdown(description=u'choose one', options=(0, 1, 2, 3, 4), value=0) sage: selector(range(5), buttons=True, default=4) - ToggleButtons(value=4, options=[0, 1, 2, 3, 4]) + ToggleButtons(index=4, options=(0, 1, 2, 3, 4), value=4) Apart from a simple list, ``values`` can be given as a list of 2-tuples ``(value, label)``:: sage: selector([(1,"one"), (2,"two"), (3,"three")]) - Dropdown(value=1, options=[('one', 1), ('two', 2), ('three', 3)]) + Dropdown(options=(('one', 1), ('two', 2), ('three', 3)), value=1) sage: selector([(1,"one"), (2,"two"), (3,"three")], buttons=True) - ToggleButtons(value=1, options=[('one', 1), ('two', 2), ('three', 3)]) + ToggleButtons(options=(('one', 1), ('two', 2), ('three', 3)), value=1) A dict of ``label:value`` pairs is also allowed. Since a ``dict`` is not ordered, it is better to use an :class:`OrderedDict`:: sage: from collections import OrderedDict sage: selector(OrderedDict(one=1, two=2, three=3)) - Dropdown(value=1, options=OrderedDict([('one', 1), ('three', 3), ('two', 2)])) + Dropdown(options=OrderedDict([('one', 1), ('three', 3), ('two', 2)]), value=1) sage: selector(OrderedDict(one=1, two=2, three=3), buttons=True) - ToggleButtons(value=1, options=OrderedDict([('one', 1), ('three', 3), ('two', 2)])) + ToggleButtons(options=OrderedDict([('one', 1), ('three', 3), ('two', 2)]), value=1) The values can be any kind of object: sage: selector([sin(x^2), GF(29), EllipticCurve('37a1')]) - Dropdown(value=sin(x^2), options=[sin(x^2), Finite Field of size 29, Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field]) + Dropdown(options=(sin(x^2), Finite Field of size 29, Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field), value=sin(x^2)) """ if isinstance(values, Sequence): values = list(values) @@ -519,14 +519,14 @@ def input_grid(nrows, ncols, default=None, label=None, to_value=None, width=4): sage: from sage.repl.ipython_kernel.all_jupyter import input_grid sage: input_grid(2, 2, default=42, label="answers") - Grid(value=[[42, 42], [42, 42]], description=u'answers', children=(Label(value=u'answers'), VBox(children=(EvalText(value=u'42'), EvalText(value=u'42'))), VBox(children=(EvalText(value=u'42'), EvalText(value=u'42'))))) + Grid(value=[[42, 42], [42, 42]], children=(Label(value=u'answers'), VBox(children=(EvalText(value=u'42', layout=Layout(max_width=u'5em')), EvalText(value=u'42', layout=Layout(max_width=u'5em')))), VBox(children=(EvalText(value=u'42', layout=Layout(max_width=u'5em')), EvalText(value=u'42', layout=Layout(max_width=u'5em')))))) sage: w = input_grid(2, 2, default=[[cos(x), sin(x)], [-sin(x), cos(x)]], to_value=matrix); w - Grid(value=[[cos(x), sin(x)], [-sin(x), cos(x)]], children=(Label(value=u''), VBox(children=(EvalText(value=u'cos(x)'), EvalText(value=u'-sin(x)'))), VBox(children=(EvalText(value=u'sin(x)'), EvalText(value=u'cos(x)'))))) + Grid(value=[[cos(x), sin(x)], [-sin(x), cos(x)]], children=(Label(value=u''), VBox(children=(EvalText(value=u'cos(x)', layout=Layout(max_width=u'5em')), EvalText(value=u'-sin(x)', layout=Layout(max_width=u'5em')))), VBox(children=(EvalText(value=u'sin(x)', layout=Layout(max_width=u'5em')), EvalText(value=u'cos(x)', layout=Layout(max_width=u'5em')))))) sage: w.get_interact_value() [ cos(x) sin(x)] [-sin(x) cos(x)] sage: w = input_grid(2, 2, default=[1, x, x^2, x^3], to_value=lambda x: x[1][1]); w - Grid(value=[[1, x], [x^2, x^3]], children=(Label(value=u''), VBox(children=(EvalText(value=u'1'), EvalText(value=u'x^2'))), VBox(children=(EvalText(value=u'x'), EvalText(value=u'x^3'))))) + Grid(value=[[1, x], [x^2, x^3]], children=(Label(value=u''), VBox(children=(EvalText(value=u'1', layout=Layout(max_width=u'5em')), EvalText(value=u'x^2', layout=Layout(max_width=u'5em')))), VBox(children=(EvalText(value=u'x', layout=Layout(max_width=u'5em')), EvalText(value=u'x^3', layout=Layout(max_width=u'5em')))))) sage: w.get_interact_value() x^3 """ From d7fa6ec8008f47c9420131756c168aaeeac0f1ba Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Wed, 24 Jan 2018 14:44:05 +0100 Subject: [PATCH 522/740] use domain.submodule instead of row_module --- src/sage/modules/free_module_morphism.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/sage/modules/free_module_morphism.py b/src/sage/modules/free_module_morphism.py index 3a934ac2068..5c998f65227 100644 --- a/src/sage/modules/free_module_morphism.py +++ b/src/sage/modules/free_module_morphism.py @@ -268,6 +268,15 @@ def inverse_image(self, V): [ 6 0 8/3] sage: phi(Y) == Z True + + We test that :trac:`24590` is resolved:: + + sage: A = FreeQuadraticModule(ZZ,1,matrix([2])) + sage: f = A.Hom(A).an_element() + sage: f.inverse_image(A) + Free module of degree 1 and rank 1 over Integer Ring + Echelon basis matrix: + [1] """ if self.rank() == 0: # Special case -- if this is the 0 map, then the only possibility @@ -318,11 +327,10 @@ def inverse_image(self, V): # Finally take the linear combinations of the basis for the # domain defined by C. Together with the kernel K, this spans # the inverse image of V. - if self.domain().is_ambient(): - L = C.row_module(R) - else: - L = (C*self.domain().basis_matrix()).row_module(R) - + dom = self.domain() + if not dom.is_ambient(): + C = C * dom.basis_matrix() + L = dom.submodule(C.rows()) return K + L def lift(self, x): From b6e2c05634879e77912848a89a45a145c1df526f Mon Sep 17 00:00:00 2001 From: Vincent Klein Date: Wed, 24 Jan 2018 19:56:02 +0100 Subject: [PATCH 523/740] Trac #22928: replace cimport * by cimport mpc_t in complex_number.pyx --- src/sage/rings/complex_number.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/complex_number.pyx b/src/sage/rings/complex_number.pyx index 8cdf7e934fb..bf0e4a3f988 100644 --- a/src/sage/rings/complex_number.pyx +++ b/src/sage/rings/complex_number.pyx @@ -31,7 +31,7 @@ from __future__ import absolute_import, print_function import math import operator -from sage.libs.mpc cimport * +from sage.libs.mpc cimport mpc_t from sage.libs.mpfr cimport * from sage.structure.element cimport FieldElement, RingElement, Element, ModuleElement from sage.categories.map cimport Map From 72d67d79064f7653729e712ef20f25abb6c8a22b Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Wed, 24 Jan 2018 15:48:35 -0800 Subject: [PATCH 524/740] Changed Entry class --- src/sage/combinat/shifted_primed_tableau.py | 273 ++++++++++++++------ 1 file changed, 197 insertions(+), 76 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index 7f00f56fd6b..a80a6acaaeb 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -35,6 +35,7 @@ from sage.structure.list_clone import ClonableArray from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.sage_object import SageObject from sage.categories.regular_crystals import RegularCrystals from sage.categories.classical_crystals import ClassicalCrystals @@ -537,7 +538,7 @@ def max_entry(self): if self == []: return 0 else: - flat = [entry.unprime() for row in self + flat = [entry.unprimed() for row in self for entry in row if entry is not None] return max(flat) @@ -573,7 +574,7 @@ def weight(self): sage: t.weight() (0, 4, 1) """ - flat = [entry.unprime() for row in self + flat = [entry.integer() for row in self for entry in row if entry is not None] if flat == []: max_ind = 0 @@ -634,12 +635,12 @@ def _reading_word_with_positions(self): for i in range(ndim): x = mat[i][j] if x is not None and x.is_primed(): - list_with_positions.append(((i, j), x.unprime())) + list_with_positions.append(((i, j), x.integer())) for i in reversed(range(ndim)): for j in range(mdim): x = mat[i][j] if x is not None and x.is_unprimed(): - list_with_positions.append(((i, j), x.unprime())) + list_with_positions.append(((i, j), x.integer())) return list_with_positions def reading_word(self): @@ -705,7 +706,6 @@ def f(self, ind): """ T = self._to_matrix() - read_word = self._reading_word_with_positions() read_word = [num for num in read_word @@ -713,8 +713,6 @@ def f(self, ind): element_to_change = None count = 0 - half = Integer(1)/Integer(2) - one = Integer(1) for element in read_word: if element[1] == ind+1: @@ -728,42 +726,45 @@ def f(self, ind): return None (r, c), elt = element_to_change + ind_e = PrimedEntry(ind) + ind_plus_one = ind_e.increase_one() + ind_plus_half = ind_e.increase_half() if T[r][c].is_primed(): - T = [[elt+half if elt is not None else elt + T = [[elt.increase_half() if elt is not None else elt for elt in row] for row in T] T = map(list, zip(*T)) r, c = c, r h, l = len(T), len(T[0]) - if (c+1 == l or T[r][c+1] is None or T[r][c+1] >= ind+one): + if (c+1 == l or T[r][c+1] is None or T[r][c+1] >= ind_plus_one): (tp_r, tp_c) = (r, c) while True: - if (tp_r+1 == h or T[tp_r+1][tp_c] is None or T[tp_r+1][tp_c] > ind+one): + if (tp_r+1 == h or T[tp_r+1][tp_c] is None or T[tp_r+1][tp_c] > ind_plus_one): break - if tp_r <= tp_c and T[tp_r+1][tp_r+1] == ind+one: + if tp_r <= tp_c and T[tp_r+1][tp_r+1] == ind_plus_one: tp_r += 1 tp_c = tp_r break - if (ind+half not in T[tp_r+1]): + if (ind_plus_half not in T[tp_r+1]): break tp_r += 1 - tp_c = T[tp_r].index(ind+half) + tp_c = T[tp_r].index(ind_plus_half) if tp_r == r: - T[r][c] = T[r][c]+one + T[r][c] = T[r][c].increase_one() elif tp_r == tp_c: - T[r][c] = T[r][c]+half + T[r][c] = T[r][c].increase_half() else: - T[r][c] = T[r][c]+half - T[tp_r][tp_c] = T[tp_r][tp_c]+half + T[r][c] = T[r][c].increase_half() + T[tp_r][tp_c] = T[tp_r][tp_c].increase_half() - elif T[r][c+1] == ind+half: - T[r][c+1] = T[r][c+1]+half - T[r][c] = T[r][c]+half + elif T[r][c+1] == ind_plus_half: + T[r][c+1] = T[r][c+1].increase_half() + T[r][c] = T[r][c].increase_half() if r > c: - T = [[elt-half if elt is not None else elt + T = [[elt.decrease_half() if elt is not None else elt for elt in row] for row in T] T = map(list, zip(*T)) @@ -801,15 +802,12 @@ def e(self, ind): """ T = self._to_matrix() - read_word = self._reading_word_with_positions() read_word = [num for num in read_word if num[1] == ind or num[1] == ind+1] element_to_change = None count = 0 - half = Integer(1)/Integer(2) - one = Integer(1) for element in read_word[::-1]: if element[1] == ind: @@ -823,35 +821,38 @@ def e(self, ind): return None (r, c), elt = element_to_change + ind_e = PrimedEntry(ind) + ind_plus_half = ind_e.increase_half() + if T[r][c].is_primed(): - T = [[elt+half if elt is not None else elt + T = [[elt.increase_half() if elt is not None else elt for elt in row] for row in T] T = map(list, zip(*T)) r, c = c, r - if (c == 0 or T[r][c-1] is None or T[r][c-1] <= ind): + if (c == 0 or T[r][c-1] is None or T[r][c-1] <= ind_e): (tp_r, tp_c) = (r, c) while True: - if (tp_r == 0 or T[tp_r-1][tp_c] is None or T[tp_r-1][tp_c] < ind): + if (tp_r == 0 or T[tp_r-1][tp_c] is None or T[tp_r-1][tp_c] < ind_e): break - if (ind+half not in T[tp_r-1]): + if (ind_plus_half not in T[tp_r-1]): break tp_r -= 1 - tp_c = T[tp_r].index(ind+half) + tp_c = T[tp_r].index(ind_plus_half) if tp_r == r: - T[r][c] = T[r][c]-one + T[r][c] = T[r][c].decrease_one() elif tp_r == tp_c: - T[r][c] = T[r][c]-half + T[r][c] = T[r][c].decrease_half() else: - T[r][c] = T[r][c]-half - T[tp_r][tp_c] = T[tp_r][tp_c]-half + T[r][c] = T[r][c].decrease_half() + T[tp_r][tp_c] = T[tp_r][tp_c].decrease_half() - elif T[r][c-1] == ind+half: - T[r][c-1] = T[r][c-1]-half - T[r][c] = T[r][c]-half + elif T[r][c-1] == ind_plus_half: + T[r][c-1] = T[r][c-1].decrease_half() + T[r][c] = T[r][c].decrease_half() if r > c: - T = [[elt-half if elt is not None else elt + T = [[elt.decrease_half() if elt is not None else elt for elt in row] for row in T] T = map(list, zip(*T)) @@ -901,7 +902,7 @@ def weight(self): sage: t.weight() (1, 4, 1) """ - flat = [entry.unprime() for row in self for entry in row] + flat = [entry.integer() for row in self for entry in row] if flat == []: max_ind = 0 else: @@ -910,13 +911,13 @@ def weight(self): return self.parent().weight_lattice_realization()(weight) -class PrimedEntry(Rational): +class PrimedEntry(SageObject): """ The class of entries in shifted primed tableaux. """ - def __init__(self, entry): + def __init__(self, entry=None, double=None): """ - Normalize the entry to be a half-integer in ``QQ``. + Normalize the entry. TEST:: @@ -927,16 +928,21 @@ def __init__(self, entry): sage: ShiftedPrimedTableau([[1,1.5]])[0][1] 2' """ + # store primed numbers as odd, unprimed numbers as even integers if isinstance(entry, self.__class__): - Rational.__init__(self, entry) + self._entry = entry._entry + return + + if double is not None: + self._entry = Integer(double) return if isinstance(entry, str): if (entry[-1] == "'" or entry[-1] == "p") and entry[:-1].isdigit() is True: # Check if an element has "'" or "p" at the end - entry = Rational(entry[:-1]) - Integer(1)/Integer(2) - Rational.__init__(self, entry) + self._entry = 2*Integer(entry[:-1]) - 1 return + try: entry = Rational(entry) except (TypeError, ValueError): @@ -944,8 +950,18 @@ def __init__(self, entry): if entry.denominator() not in (1, 2): # Check if an element is a half-integer raise ValueError("all numbers must be half-integers") + self._entry = Integer(2*entry) - Rational.__init__(self, entry) + def __hash__(self): + """ + TEST:: + + sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] + sage: b = ShiftedPrimedTableau([[1,1,"2p"]])[0][2] + sage: a == b + True + """ + return self._entry def __repr__(self): """ @@ -956,36 +972,87 @@ def __repr__(self): sage: ShiftedPrimedTableau([[1,"2p"]])[0][1] 2' """ - if self.is_primed(): - return Integer(self.unprime()).__repr__() + "'" + if self.is_unprimed(): + return str(self._entry//2) else: - return Integer(self.unprime()).__repr__() + return str((self._entry+1)//2) + "'" + + def integer(self): + """ + TEST:: - def __add__(self, other): + sage: ShiftedPrimedTableau([[1,"2p"]])[0][1].integer() + 2 """ - Sum of ``self`` with rational number ``other``. + return (self._entry+1)//2 + def __eq__(self, other): + """ TEST:: - sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] - sage: b = a+.5 - sage: type(b) - + sage: a = ShiftedPrimedTableau([[1,1]])[0][1] + sage: b = ShiftedPrimedTableau([[1,1]])[0][0] + sage: a == b + True """ - return self.__class__(Rational(self) + Rational(other)) - def __sub__(self, other): + return self._entry == PrimedEntry(other)._entry + + def __ne__(self, other): + """ + TEST:: + + sage: a = ShiftedPrimedTableau([[1,1]])[0][1] + sage: b = ShiftedPrimedTableau([[1,1]])[0][0] + sage: a != b + False """ - Sum of ``self`` with rational number ``other``. + return self._entry != PrimedEntry(other)._entry + def __lt__(self, other): + """ TEST:: - sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] - sage: b = a-.5 - sage: type(b) - + sage: a = ShiftedPrimedTableau([[1,"2p",2]])[0][1] + sage: b = ShiftedPrimedTableau([[1,"2p",2]])[0][2] + sage: a < b + True + """ + + return self._entry < PrimedEntry(other)._entry + + def __le__(self, other): + """ + TEST:: + + sage: a = ShiftedPrimedTableau([[1,2,2]])[0][1] + sage: b = ShiftedPrimedTableau([[1,2,2]])[0][2] + sage: a <= b + True + """ + return self._entry <= PrimedEntry(other)._entry + + def __gt__(self, other): + """ + TEST:: + + sage: a = ShiftedPrimedTableau([[1,"2p",2]])[0][1] + sage: b = ShiftedPrimedTableau([[1,"2p",2]])[0][2] + sage: b > a + True + """ + return self._entry > PrimedEntry(other)._entry + + def __ge__(self, other): """ - return self.__class__(Rational(self) - Rational(other)) + TEST:: + + sage: a = ShiftedPrimedTableau([[1,"2p",2]])[0][1] + sage: b = ShiftedPrimedTableau([[1,"2p",2]])[0][2] + sage: a >= b + False + """ + return self._entry >= PrimedEntry(other)._entry def is_unprimed(self): """ @@ -997,7 +1064,7 @@ def is_unprimed(self): sage: a.is_unprimed() False """ - return (self in ZZ) + return self._entry % 2 == 0 def is_primed(self): """ @@ -1009,23 +1076,77 @@ def is_primed(self): sage: a.is_primed() True """ - return (self not in ZZ) + return self._entry % 2 == 1 - def unprime(self): + def unprimed(self): """ - Unprime ``self`` if it was a prime element. + Unprime ``self`` if it is a primed element. TEST:: sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] - sage: a.unprime() + sage: a.unprimed() 2 """ - half = Integer(1)/Integer(2) - if self.is_primed(): - return Integer(self + half) + if self.is_unprimed(): + return self + else: + return PrimedEntry(double=self._entry + 1) + + def primed(self): + """ + Prime ``self`` if it is an unprimed element. + + TEST:: + + sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][0] + sage: a.primed() + 1' + """ + if self.is_unprimed(): + return PrimedEntry(double=self._entry - 1) else: - return Integer(self) + return self + + def increase_half(self): + """ + TEST:: + + sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][0] + sage: a.increase_half() + 2' + """ + return PrimedEntry(double=self._entry + 1) + + def decrease_half(self): + """ + TEST:: + + sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][0] + sage: a.decrease_half() + 1' + """ + return PrimedEntry(double=self._entry - 1) + + def increase_one(self): + """ + TEST:: + + sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] + sage: a.increase_one() + 3' + """ + return PrimedEntry(double=self._entry + 2) + + def decrease_one(self): + """ + TEST:: + + sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] + sage: a.decrease_one() + 1' + """ + return PrimedEntry(double=self._entry - 2) class ShiftedPrimedTableaux(UniqueRepresentation, Parent): @@ -1269,7 +1390,7 @@ def _contains_tableau_(self, T): for j in range(skew[i], len(row)-1) if row[j].is_primed()): return False - if not all(int(row[0]) == row[0] + if not all(row[0].is_unprimed() for i, row in enumerate(T) if skew[i] == 0): return False @@ -1491,7 +1612,7 @@ def _contains_tableau_(self, T): return False if self._max_entry is not None: - flat = [round(item) for sublist in T for item in sublist] + flat = [item.integer() for sublist in T for item in sublist] if flat == []: max_entry = 0 else: @@ -1682,7 +1803,7 @@ def _contains_tableau_(self, T): if not super(ShiftedPrimedTableaux_weight, self)._contains_tableau_(T): return False - flat = [round(item) for sublist in T for item in sublist] + flat = [item.integer() for sublist in T for item in sublist] if flat == []: max_ind = 0 else: @@ -1771,13 +1892,13 @@ def _contains_tableau_(self, T): if not super(ShiftedPrimedTableaux_weight_shape, self)._contains_tableau_(T): return False - flat = [round(item) for sublist in T for item in sublist] + flat = [item.integer() for sublist in T for item in sublist] if flat == []: max_ind = 0 else: max_ind = int(max(flat)) weight = tuple([flat.count(i+1) for i in range(max_ind)]) - if self._weight!=weight: + if self._weight != weight: return False shape = [len(row) for row in T] From f24133bc0da12435b387bc828991e4b83ee9ce33 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 25 Jan 2018 10:09:33 -0600 Subject: [PATCH 525/740] Adding promised changes. --- src/sage/typeset/ascii_art.py | 12 ++++++++---- src/sage/typeset/character_art_factory.py | 2 ++ src/sage/typeset/unicode_art.py | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/sage/typeset/ascii_art.py b/src/sage/typeset/ascii_art.py index f6191d57613..5dd3a7f1531 100644 --- a/src/sage/typeset/ascii_art.py +++ b/src/sage/typeset/ascii_art.py @@ -253,8 +253,8 @@ def ascii_art(*obj, **kwds): If specified, the ``sep_baseline`` overrides the baseline of an ascii art separator:: - sage: sep_line = ascii_art('\n'.join(' | ' for _ in range(6))) - sage: ascii_art(*Partitions(6), separator=sep_line, baseline=6) + sage: sep_line = ascii_art('\n'.join(' | ' for _ in range(6)), baseline=6) + sage: ascii_art(*Partitions(6), separator=sep_line, sep_baseline=0) | | | | | | | | | | * | | | | | | | | | ** | * | | | | | | *** | | ** | * | * @@ -278,13 +278,17 @@ def ascii_art(*obj, **kwds): sage: ascii_art(1) 1 """ - separator, baseline, sep_base = _ascii_art_factory.parse_keywords(kwds) + separator, baseline, sep_baseline = _ascii_art_factory.parse_keywords(kwds) if kwds: raise ValueError('unknown keyword arguments: {0}'.format(list(kwds))) if len(obj) == 1: return _ascii_art_factory.build(obj[0], baseline=baseline) if not isinstance(separator, AsciiArt): - separator = _ascii_art_factory.build(separator, baseline=sep_base) + separator = _ascii_art_factory.build(separator, baseline=sep_baseline) + elif sep_baseline is not None: + from copy import copy + separator = copy(separator) + separator._baseline = sep_baseline obj = map(_ascii_art_factory.build, obj) return _ascii_art_factory.concatenate(obj, separator, empty_ascii_art, baseline=baseline) diff --git a/src/sage/typeset/character_art_factory.py b/src/sage/typeset/character_art_factory.py index f51898d0b8f..4905a3f5235 100644 --- a/src/sage/typeset/character_art_factory.py +++ b/src/sage/typeset/character_art_factory.py @@ -114,6 +114,8 @@ def build(self, obj, baseline=None): """ if isinstance(obj, self.art_type): if baseline is not None: + from copy import copy + obj = copy(obj) obj._baseline = baseline return obj if isinstance(obj, SageObject): diff --git a/src/sage/typeset/unicode_art.py b/src/sage/typeset/unicode_art.py index dbdd0289900..504d7a4da3a 100644 --- a/src/sage/typeset/unicode_art.py +++ b/src/sage/typeset/unicode_art.py @@ -102,6 +102,17 @@ def unicode_art(*obj, **kwds): ⎛1 0⎞ ⎜0 1 0⎟ (1) : ⎝0 1⎠ : ⎝0 0 1⎠ + If specified, the ``sep_baseline`` overrides the baseline of + an unicode art separator:: + + sage: sep_line = unicode_art('\n'.join(u' ⎟ ' for _ in range(5)), baseline=5) + sage: unicode_art(*Partitions(4), separator=sep_line, sep_baseline=0) + ⎟ ⎟ ⎟ ⎟ ┌┐ + ⎟ ⎟ ⎟ ┌┬┐ ⎟ ├┤ + ⎟ ┌┬┬┐ ⎟ ┌┬┐ ⎟ ├┼┘ ⎟ ├┤ + ┌┬┬┬┐ ⎟ ├┼┴┘ ⎟ ├┼┤ ⎟ ├┤ ⎟ ├┤ + └┴┴┴┘ ⎟ └┘ ⎟ └┴┘ ⎟ └┘ ⎟ └┘ + TESTS:: sage: n = var('n') @@ -125,6 +136,10 @@ def unicode_art(*obj, **kwds): return _unicode_art_factory.build(obj[0], baseline=baseline) if not isinstance(separator, UnicodeArt): separator = _unicode_art_factory.build(separator, baseline=sep_baseline) + elif sep_baseline is not None: + from copy import copy + separator = copy(separator) + separator._baseline = sep_baseline obj = map(_unicode_art_factory.build, obj) return _unicode_art_factory.concatenate(obj, separator, empty_unicode_art, baseline=baseline) From e30fb3d02298bf032e2980485cba15cdd8441800 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Thu, 25 Jan 2018 18:10:42 +0000 Subject: [PATCH 526/740] #21092: rewrite of plane cubic to Weierstrass --- .../schemes/elliptic_curves/constructor.py | 368 ++++++++++-------- .../elliptic_curves/weierstrass_transform.py | 43 +- 2 files changed, 233 insertions(+), 178 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index 5903d6a0f1c..54fb7b8cd0a 100644 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -28,7 +28,6 @@ import sage.rings.all as rings from sage.rings.finite_rings.integer_mod_ring import is_IntegerModRing -from sage.rings.rational_field import is_RationalField from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.rings.number_field.number_field import is_NumberField @@ -401,6 +400,7 @@ def create_key_and_extra_args(self, x=None, y=None, j=None, minimal_twist=True, if y is None: x = coefficients_from_Weierstrass_polynomial(x) else: + # TODO: This function coefficients_from_cubic() is not defined anywhere! x = coefficients_from_cubic(x, y, morphism=False) if isinstance(x, string_types): @@ -786,7 +786,7 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): sage: cubic = x^3+y^3+z^3 sage: P = [1,-1,0] sage: E = EllipticCurve_from_cubic(cubic, P, morphism=False); E - Elliptic Curve defined by y^2 + 2*x*y + 1/3*y = x^3 - x^2 - 1/3*x - 1/27 over Rational Field + Elliptic Curve defined by y^2 - 9*y = x^3 - 27 over Rational Field sage: E.cremona_label() '27a1' sage: EllipticCurve_from_cubic(cubic, [0,1,-1], morphism=False).cremona_label() @@ -801,7 +801,7 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): sage: cubic = a^3+b^3+60*c^3 sage: P = [1,-1,0] sage: E = EllipticCurve_from_cubic(cubic, P, morphism=False); E - Elliptic Curve defined by y^2 + 2*x*y + 20*y = x^3 - x^2 - 20*x - 400/3 over Rational Field + Elliptic Curve defined by y^2 - 540*y = x^3 - 97200 over Rational Field sage: E.minimal_model() Elliptic Curve defined by y^2 = x^3 - 24300 over Rational Field sage: E.conductor() @@ -814,14 +814,18 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): sage: f = EllipticCurve_from_cubic(cubic, P, morphism=True) sage: f Scheme morphism: - From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: - a^3 + b^3 + 60*c^3 - To: Elliptic Curve defined by y^2 + 2*x*y + 20*y = x^3 - x^2 - 20*x - 400/3 - over Rational Field + From: Projective Plane Curve over Rational Field defined by a^3 + b^3 + 60*c^3 + To: Elliptic Curve defined by y^2 - 540*y = x^3 - 97200 over Rational Field Defn: Defined on coordinates by sending (a : b : c) to - (-c : -b + c : 1/20*a + 1/20*b) + (-c : 3*a : 1/180*a + 1/180*b) sage: finv = f.inverse(); finv + Scheme morphism: + From: Elliptic Curve defined by y^2 - 540*y = x^3 - 97200 over Rational Field + To: Projective Plane Curve over Rational Field defined by a^3 + b^3 + 60*c^3 + Defn: Defined on coordinates by sending (x : y : z) to + (1/3*y : -1/3*y + 180*z : -x) + Scheme morphism: From: Elliptic Curve defined by y^2 + 2*x*y + 20*y = x^3 - x^2 - 20*x - 400/3 over Rational Field @@ -842,61 +846,110 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): this indeed transforms the cubic into Weierstrass form:: sage: cubic(finv.defining_polynomials()) * finv.post_rescaling() - -x^3 + x^2*z + 2*x*y*z + y^2*z + 20*x*z^2 + 20*y*z^2 + 400/3*z^3 + -x^3 + y^2*z - 540*y*z^2 + 97200*z^3 sage: E.defining_polynomial()(f.defining_polynomials()) * f.post_rescaling() a^3 + b^3 + 60*c^3 - If the point is not a flex then the cubic can not be transformed - to a Weierstrass equation by a linear transformation. The general - birational transformation is quadratic:: + If the given point is not a flex and the cubic has no rational + flexes, then the cubic can not be transformed to a Weierstrass + equation by a linear transformation. The general birational + transformation is still a birational isomorphism, but is + quadratic:: + + sage: R. = QQ[] + sage: cubic = x^2*y + 4*x*y^2 + x^2*z + 8*x*y*z + 4*y^2*z + 9*x*z^2 + 9*y*z^2 + sage: f = EllipticCurve_from_cubic(cubic, [1,-1,1], morphism=True); f + Scheme morphism: + From: Projective Plane Curve over Rational Field defined by x^2*y + 4*x*y^2 + x^2*z + 8*x*y*z + 4*y^2*z + 9*x*z^2 + 9*y*z^2 + To: Elliptic Curve defined by y^2 + 7560/19*x*y + 552960000000/2352637*y = x^3 - 3445200/133*x^2 over Rational Field + Defn: Defined on coordinates by sending (x : y : z) to + (2527/17280*x^2 + 133/2160*x*y + 133/108000*y^2 + 133/2880*x*z + 931/18000*y*z - 3857/48000*z^2 : -6859/288*x^2 + 323/36*x*y + 359/1800*y^2 + 551/48*x*z + 2813/300*y*z + 24389/800*z^2 : -2352637/99532800000*x^2 - 2352637/124416000000*x*y - 2352637/622080000000*y^2 + 2352637/82944000000*x*z + 2352637/207360000000*y*z - 2352637/276480000000*z^2) + + Note that the morphism returned cannor be evaluated directly at + the given point ``P=(1:-1:1)`` since the polynomials defining it + all vanish there:: + + sage: f([1,-1,1]) + Traceback (most recent call last): + ... + ValueError: [0, 0, 0] does not define a valid point since all entries are 0 + Using the group law on the codomain elliptic curve, which has rank + 1 and full 2-torsion, anf the inverse morphism, we can find many + points on the cubic. First we find the preimages of multiples of + the generator:: + + sage: E = f.codomain() + sage: E.label() + '720e2' + sage: E.rank() + 1 + sage: R = E.gens()[0]; R + (-17280000/2527 : 9331200000/6859 : 1) + sage: finv = f.inverse() + sage: [finv(k*R) for k in range(1,10)] + [(-4 : 1 : 0), + (-1 : 4 : 1), + (-20 : -55/76 : 1), + (319/399 : -11339/7539 : 1), + (159919/14360 : -4078139/1327840 : 1), + (-27809119/63578639 : 1856146436/3425378659 : 1), + (-510646582340/56909753439 : 424000923715/30153806197284 : 1), + (-56686114363679/4050436059492161 : -2433034816977728281/1072927821085503881 : 1), + (650589589099815846721/72056273157352822480 : -347376189546061993109881/194127383495944026752320 : 1)] + + The elliptic curve also has torsion, which we can map back:: + + sage: E.torsion_points() + [(-144000000/17689 : 3533760000000/2352637 : 1), + (-92160000/17689 : 2162073600000/2352637 : 1), + (-5760000/17689 : -124070400000/2352637 : 1), + (0 : 1 : 0)] + sage: [finv(Q) for Q in E.torsion_points() if Q] + [(9 : -9/4 : 1), (-9 : 0 : 1), (0 : 1 : 0)] + + + In this example, the given point ``P`` is not a flex but the cubic + does have a rational flex, ``(-4:0:1)``. We return a linear + isomorphism which maps this flex to the point at infinity on the + Weierstrass model:: + + sage: R. = QQ[] sage: cubic = a^3+7*b^3+64*c^3 sage: P = [2,2,-1] sage: f = EllipticCurve_from_cubic(cubic, P, morphism=True) sage: E = f.codomain(); E - Elliptic Curve defined by y^2 - 722*x*y - 21870000*y = x^3 - + 23579*x^2 over Rational Field + Elliptic Curve defined by y^2 - 258048*y = x^3 - 22196256768 over Rational Field sage: E.minimal_model() Elliptic Curve defined by y^2 + y = x^3 - 331 over Rational Field sage: f Scheme morphism: - From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: - a^3 + 7*b^3 + 64*c^3 - To: Elliptic Curve defined by y^2 - 722*x*y - 21870000*y = - x^3 + 23579*x^2 over Rational Field + From: Projective Plane Curve over Rational Field defined by a^3 + 7*b^3 + 64*c^3 + To: Elliptic Curve defined by y^2 - 258048*y = x^3 - 22196256768 over Rational Field Defn: Defined on coordinates by sending (a : b : c) to - (-5/112896*a^2 - 17/40320*a*b - 1/1280*b^2 - 29/35280*a*c - - 13/5040*b*c - 4/2205*c^2 : - -4055/112896*a^2 - 4787/40320*a*b - 91/1280*b^2 - 7769/35280*a*c - - 1993/5040*b*c - 724/2205*c^2 : - 1/4572288000*a^2 + 1/326592000*a*b + 1/93312000*b^2 + 1/142884000*a*c - + 1/20412000*b*c + 1/17860500*c^2) + (b : -48*a : -1/5376*a - 1/1344*c) sage: finv = f.inverse(); finv Scheme morphism: - From: Elliptic Curve defined by y^2 - 722*x*y - 21870000*y = - x^3 + 23579*x^2 over Rational Field - To: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: - a^3 + 7*b^3 + 64*c^3 + From: Elliptic Curve defined by y^2 - 258048*y = x^3 - 22196256768 over Rational Field + To: Projective Plane Curve over Rational Field defined by a^3 + 7*b^3 + 64*c^3 Defn: Defined on coordinates by sending (x : y : z) to - (2*x^2 + 227700*x*z - 900*y*z : - 2*x^2 - 32940*x*z + 540*y*z : - -x^2 - 56520*x*z - 180*y*z) + (-1/48*y : x : 1/192*y - 1344*z) sage: cubic(finv.defining_polynomials()) * finv.post_rescaling() - -x^3 - 23579*x^2*z - 722*x*y*z + y^2*z - 21870000*y*z^2 + -x^3 + y^2*z - 258048*y*z^2 + 22196256768*z^3 sage: E.defining_polynomial()(f.defining_polynomials()) * f.post_rescaling() a^3 + 7*b^3 + 64*c^3 - TESTS:: + sage: f(P) + (5376 : -258048 : 1) + sage: f([-4,0,1]) + (0 : 1 : 0) - sage: R. = QQ[] - sage: cubic = x^2*y + 4*x*y^2 + x^2*z + 8*x*y*z + 4*y^2*z + 9*x*z^2 + 9*y*z^2 - sage: EllipticCurve_from_cubic(cubic, [1,-1,1], morphism=False) - Elliptic Curve defined by y^2 - 882*x*y - 2560000*y = x^3 - 127281*x^2 over Rational Field + TESTS: Here is a test for :trac:`21092`:: @@ -904,13 +957,16 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): sage: cubic = -3*x^2*y + 3*x*y^2 + 4*x^2*z + 4*y^2*z - 3*x*z^2 + 3*y*z^2 - 8*z^3 sage: EllipticCurve_from_cubic(cubic, (-4/5, 4/5, 3/5) ) Scheme morphism: - From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: - -3*x^2*y + 3*x*y^2 + 4*x^2*z + 4*y^2*z - 3*x*z^2 + 3*y*z^2 - 8*z^3 - To: Elliptic Curve defined by y^2 - 6*x*y - 112*y = x^3 + 62*x^2 + 560*x over Rational Field + From: Projective Plane Curve over Rational Field defined by -3*x^2*y + 3*x*y^2 + 4*x^2*z + 4*y^2*z - 3*x*z^2 + 3*y*z^2 - 8*z^3 + To: Elliptic Curve defined by y^2 + 24*x*y + 3024*y = x^3 + 495*x^2 + 36288*x over Rational Field Defn: Defined on coordinates by sending (x : y : z) to - (1/3*z : -y - 1/3*z : 1/112*x - 1/112*y - 1/42*z) + (-1/3*z : 3*x : -1/1008*x + 1/1008*y + 1/378*z) """ - import sage.matrix.all as matrix + from sage.schemes.curves.constructor import Curve + from sage.matrix.all import Matrix + from sage.modules.all import vector + from sage.schemes.elliptic_curves.weierstrass_transform import \ + WeierstrassTransformationWithInverse # check the input R = F.parent() @@ -918,18 +974,17 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): raise TypeError('equation must be a polynomial') if R.ngens() != 3 or F.nvariables() != 3: raise TypeError('equation must be a polynomial in three variables') - x, y, z = R.gens() if not F.is_homogeneous(): raise TypeError('equation must be a homogeneous polynomial') if len(P) != 3: raise TypeError('%s is not a projective point' % P) - K = F.parent().base_ring() + K = R.base_ring() try: C = Curve(F) CP = C(P) - except TypeError, ValueError: + except (TypeError, ValueError): raise TypeError('{} does not define a point on a projective curve over {} defined by {}'.format(P,K,F)) x, y, z = R.gens() @@ -946,83 +1001,120 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): else: flex_point = None - if flex_point is not None: + if flex_point is not None: # first case: base point is a flex P = flex_point - # find the tangent to F in P - dx = K(F.derivative(x)(P)) - dy = K(F.derivative(y)(P)) - dz = K(F.derivative(z)(P)) - # find a second point Q on the tangent line but not on the cubic - for tangent in [[dy, -dx, K.zero()], [dz, K.zero(), -dx], [K.zero(), -dz, dx]]: - tangent = projective_point(tangent) - Q = [tangent[0]+P[0], tangent[1]+P[1], tangent[2]+P[2]] - F_Q = F(Q) - if F_Q != 0: # At most one further point may accidentally be on the cubic - break - # pick linearly independent third point - for third_point in [(1,0,0), (0,1,0), (0,0,1)]: - M = matrix.matrix(K, [Q, P, third_point]).transpose() - if M.is_invertible(): - break + L = C.tangents(P)[0] + dx, dy, dz = [L.coefficient(v) for v in R.gens()] + + # find an invertible matrix M such that (0,1,0)M=P and + # ML'=(0,0,1)' where L=[dx,dy,dx]. Then the linea transform + # by M takes P to [0,1,0] and L to Z=0: + + if P[0]: + Q1 = [0,-dz,dy] + Q2 = [0,1,0] if dy else [0,0,1] + elif P[1]: + Q1 = [dz,0,-dx] + Q2 = [1,0,0] if dx else [0,0,1] + else: + Q1 = [-dy,dx,0] + Q2 = [1,0,0] if dx else [0,1,0] + + M = Matrix(K,[Q1,P,Q2]) + # assert M.is_invertible() + # assert list(vector([0,1,0])*M) == P + # assert list(M*vector([dx,dy,dz]))[:2] == [0,0] + + M = M.transpose() F2 = R(M.act_on_polynomial(F)) + # scale and dehomogenise a = K(F2.coefficient(x**3)) - F3 = F2/a - b = K(F3.coefficient(y*y*z)) - S = rings.PolynomialRing(K, 'x,y,z') - # elliptic curve coordinates - X, Y, Z = S.gen(0), S.gen(1), S(-1/b)*S.gen(2) - F4 = F3(X, Y, Z) - E = EllipticCurve(F4.subs(z=1)) + b = K(F2.coefficient(y*y*z)) + + F3 = F2([-x, y/b, z*a*b]) / a + # assert F3.coefficient(x**3) == -1 + # assert F3.coefficient(y*y*z) == 1 + E = EllipticCurve(F3([x,y,1])) if not morphism: return E - inv_defining_poly = [ M[i,0]*X + M[i,1]*Y + M[i,2]*Z for i in range(3) ] - inv_post = -1/a + + # Construct the (linear) morphism + M = M * Matrix(K,[[-1,0,0],[0,1/b,0],[0,0,a*b]]) + inv_defining_poly = [ M[i,0]*x + M[i,1]*y + M[i,2]*z for i in range(3) ] + inv_post = 1/a M = M.inverse() - trans_x, trans_y, trans_z = [ M[i,0]*x + M[i,1]*y + M[i,2]*z for i in range(3) ] - fwd_defining_poly = [trans_x, trans_y, -b*trans_z] - fwd_post = -a + fwd_defining_poly = [ M[i,0]*x + M[i,1]*y + M[i,2]*z for i in range(3) ] + fwd_post = a + + else: # Second case: no flexes + L = C.tangents(P)[0] + Qlist = [Q for Q in C.intersection(Curve(L)).rational_points() if C(Q)!=CP] + # assert Qlist + P2 = C(Qlist[0]) + L2 = C.tangents(P2)[0] + Qlist = [Q for Q in C.intersection(Curve(L2)).rational_points() if C(Q)!=P2] + # assert Qlist + P3 = C(Qlist[0]) + + # NB This construction of P3 relies on P2 not being a flex. + # If we want to use a non-flex as P when there are rational + # flexes this would be a problem. However, the only condition + # which P3 must satisfy is that it is on the tangent at P2, it + # need not lie on the cubic. - # Second case: P, P2, P3 are different - else: # send P, P2, P3 to (1:0:0), (0:1:0), (0:0:1) respectively - M = matrix.matrix(K, [P, P2, P3]).transpose() + M = Matrix(K, [P, list(P2), list(P3)]).transpose() F2 = M.act_on_polynomial(F) + xyzM = [ M[i,0]*x + M[i,1]*y + M[i,2]*z for i in range(3) ] + # assert F(xyzM)==F2 + # substitute x = U^2, y = V*W, z = U*W, and rename (x,y,z)=(U,V,W) - F3 = F2.substitute({x:x**2, y:y*z, z:x*z}) // (x**2*z) + T1 = [x*x,y*z,x*z] + S1 = x**2*z + F3 = F2(T1) // S1 + xyzC = [ t(T1) for t in xyzM ] + # assert F3 == F(xyzC) // S1 + # scale and dehomogenise a = K(F3.coefficient(x**3)) - F4 = F3/a - b = K(F4.coefficient(y*y*z)) - # change to a polynomial in only two variables - S = rings.PolynomialRing(K, 'x,y,z') - # elliptic curve coordinates - X, Y, Z = S.gen(0), S.gen(1), S(-1/b)*S.gen(2) - F5 = F4(X, Y, Z) - E = EllipticCurve(F5.subs(z=1)) + b = K(F3.coefficient(y*y*z)) + ab = a*b + + T2 = [-x, y/b, ab*z] + F4 = F3(T2) / a + # assert F4.coefficient(x**3) == -1 + # assert F4.coefficient(y*y*z) == 1 + xyzW = [ t(T2) for t in xyzC ] + S2 = a*S1(T2) + # assert F4 == F(xyzW) // S2 + + E = EllipticCurve(F4([x,y,1])) if not morphism: return E - inv_defining_poly = [ M[i,0]*X*X + M[i,1]*Y*Z + M[i,2]*X*Z for i in range(3) ] - inv_post = -1/a/(X**2)/Z - M = M.inverse() - trans_x, trans_y, trans_z = [ - (M[i,0]*x + M[i,1]*y + M[i,2]*z) for i in range(3) ] - fwd_defining_poly = [ trans_x*trans_z, trans_x*trans_y, -b*trans_z*trans_z ] - fwd_post = -a/(trans_x*trans_z*trans_z) + + inv_defining_poly = xyzW + inv_post = 1/S2 + # assert F4==F(inv_defining_poly)*inv_post + MI = M.inverse() + xyzI = [ (MI[i,0]*x + MI[i,1]*y + MI[i,2]*z) for i in range(3) ] + T1I = [x*z,x*y,z*z] # inverse of T1 + xyzIC = [ t(xyzI) for t in T1I ] + T2I = [-x, b*y, z/ab] # inverse of T2 + xyzIW = [ t(xyzIC) for t in T2I ] + fwd_defining_poly = xyzIW + fwd_post = a/(x*z*z)(xyzI) + # assert F4(fwd_defining_poly)*fwd_post == F # Construct the morphism - from sage.schemes.projective.projective_space import ProjectiveSpace - P2 = ProjectiveSpace(2, K, names=[str(_) for _ in R.gens()]) - cubic = P2.subscheme(F) - from sage.schemes.elliptic_curves.weierstrass_transform import \ - WeierstrassTransformationWithInverse + return WeierstrassTransformationWithInverse( - cubic, E, fwd_defining_poly, fwd_post, inv_defining_poly, inv_post) + C, E, fwd_defining_poly, fwd_post, inv_defining_poly, inv_post) def chord_and_tangent(F, P): """ - Use the chord and tangent method to get another point on a cubic. + Return the third point of intersection of a cubic with the tangent at one point. INPUT: @@ -1035,7 +1127,7 @@ def chord_and_tangent(F, P): OUTPUT: - Another point satisfying the equation ``F``. + A point ``Q`` such that ``F(Q)=0``, namely the third point of intersection of the tangent at ``P`` with the curve ``F=0``, so ``Q=P`` if and only if ``P`` is a flex. EXAMPLES:: @@ -1043,18 +1135,18 @@ def chord_and_tangent(F, P): sage: from sage.schemes.elliptic_curves.constructor import chord_and_tangent sage: F = x^3+y^3+60*z^3 sage: chord_and_tangent(F, [1,-1,0]) - [1, -1, 0] + (-1 : 1 : 0) sage: F = x^3+7*y^3+64*z^3 sage: p0 = [2,2,-1] sage: p1 = chord_and_tangent(F, p0); p1 - [-5, 3, -1] + (5 : -3 : 1) sage: p2 = chord_and_tangent(F, p1); p2 - [1265, -183, -314] + (-1265/314 : 183/314 : 1) TESTS:: - sage: F(p2) + sage: F(list(p2)) 0 sage: list(map(type, p2)) [<... 'sage.rings.rational.Rational'>, @@ -1065,68 +1157,32 @@ def chord_and_tangent(F, P): sage: F = x**3 - 4*x**2*y - 65*x*y**2 + 3*x*y*z - 76*y*z**2 sage: chord_and_tangent(F, [0, 1, 0]) - [0, 0, -1] + (0 : 0 : 1) """ + from sage.schemes.curves.constructor import Curve # check the input R = F.parent() if not is_MPolynomialRing(R): raise TypeError('equation must be a polynomial') if R.ngens() != 3: - raise TypeError('%s is not a polynomial in three variables'%F) + raise TypeError('{} is not a polynomial in three variables'.format(F)) if not F.is_homogeneous(): - raise TypeError('%s is not a homogeneous polynomial'%F) + raise TypeError('{} is not a homogeneous polynomial'.format(F)) x, y, z = R.gens() if len(P) != 3: - raise TypeError('%s is not a projective point'%P) + raise TypeError('{} is not a projective point'.format(P)) K = R.base_ring() try: - P = [K(c) for c in P] - except TypeError: - raise TypeError('cannot coerce %s into %s'%(P,K)) - if F(P) != 0: - raise ValueError('%s is not a point on %s'%(P,F)) - - # find the tangent to F in P - dx = K(F.derivative(x)(P)) - dy = K(F.derivative(y)(P)) - dz = K(F.derivative(z)(P)) - # if dF/dy(P) = 0, change variables so that dF/dy != 0 - if dy == 0: - if dx != 0: - g = F.substitute({x:y, y:x}) - Q = [P[1], P[0], P[2]] - R = chord_and_tangent(g, Q) - return [R[1], R[0], R[2]] - elif dz != 0: - g = F.substitute({y:z, z:y}) - Q = [P[0], P[2], P[1]] - R = chord_and_tangent(g, Q) - return [R[0], R[2], R[1]] - else: - raise ValueError('%s is singular at %s'%(F, P)) - - # t will be our choice of parmeter of the tangent plane - # dx*(x-P[0]) + dy*(y-P[1]) + dz*(z-P[2]) - # through the point P - t = rings.PolynomialRing(K, 't').gen(0) - Ft = F(dy*t+P[0], -dx*t+P[1], P[2]) - if Ft == 0: # (dy, -dx, 0) is projectively equivalent to P - # then (0, -dz, dy) is not projectively equivalent to P - g = F.substitute({x:z, z:x}) - Q = [P[2], P[1], P[0]] - R = chord_and_tangent(g, Q) - return [R[2], R[1], R[0]] - # Ft has a double zero at t=0 by construction, which we now remove - Ft = Ft // t**2 - - # first case: the third point is at t=infinity - if Ft.is_constant(): - return projective_point([dy, -dx, K(0)]) - # second case: the third point is at finite t - else: - assert Ft.degree() == 1 - t0 = Ft.roots()[0][0] - return projective_point([dy*t0+P[0], -dx*t0+P[1], P[2]]) + C = Curve(F) + P = C(P) + except (TypeError, ValueError): + raise TypeError('{} does not define a point on a projective curve over {} defined by {}'.format(P,K,F)) + + L = Curve(C.tangents(P)[0]) + Qlist = [Q for Q in C.intersection(L).rational_points() if Q!=P] + if Qlist: + return Qlist[0] + return P def projective_point(p): diff --git a/src/sage/schemes/elliptic_curves/weierstrass_transform.py b/src/sage/schemes/elliptic_curves/weierstrass_transform.py index 909327bec0c..2a3cf0c91ba 100644 --- a/src/sage/schemes/elliptic_curves/weierstrass_transform.py +++ b/src/sage/schemes/elliptic_curves/weierstrass_transform.py @@ -13,24 +13,20 @@ sage: R. = QQ[] sage: f = EllipticCurve_from_cubic(u^3 + v^3 + w^3, [1,-1,0], morphism=True); f Scheme morphism: - From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: - u^3 + v^3 + w^3 - To: Elliptic Curve defined by y^2 + 2*x*y + 1/3*y - = x^3 - x^2 - 1/3*x - 1/27 over Rational Field + From: Projective Plane Curve over Rational Field defined by u^3 + v^3 + w^3 + To: Elliptic Curve defined by y^2 - 9*y = x^3 - 27 over Rational Field Defn: Defined on coordinates by sending (u : v : w) to - (-w : -v + w : 3*u + 3*v) + (-w : 3*u : 1/3*u + 1/3*v) sage: finv = f.inverse(); finv Scheme morphism: - From: Elliptic Curve defined by y^2 + 2*x*y + 1/3*y - = x^3 - x^2 - 1/3*x - 1/27 over Rational Field - To: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: - u^3 + v^3 + w^3 + From: Elliptic Curve defined by y^2 - 9*y = x^3 - 27 over Rational Field + To: Projective Plane Curve over Rational Field defined by u^3 + v^3 + w^3 Defn: Defined on coordinates by sending (x : y : z) to - (x + y + 1/3*z : -x - y : -x) + (1/3*y : -1/3*y + 3*z : -x) sage: (u^3 + v^3 + w^3)(f.inverse().defining_polynomials()) * f.inverse().post_rescaling() - -x^3 + x^2*z + 2*x*y*z + y^2*z + 1/3*x*z^2 + 1/3*y*z^2 + 1/27*z^3 + -x^3 + y^2*z - 9*y*z^2 + 27*z^3 sage: E = finv.domain() sage: E.defining_polynomial()(f.defining_polynomials()) * f.post_rescaling() @@ -39,9 +35,9 @@ sage: f([1,-1,0]) (0 : 1 : 0) sage: f([1,0,-1]) - (1/3 : -1/3 : 1) + (3 : 9 : 1) sage: f([0,1,-1]) - (1/3 : -2/3 : 1) + (3 : 0 : 1) """ from __future__ import absolute_import @@ -137,7 +133,7 @@ def post_rescaling(self): sage: P = [2,2,-1] sage: f = EllipticCurve_from_cubic(cubic, P, morphism=True).inverse() sage: f.post_rescaling() - 1/60480/(180*x^2*z) + -1/7 So here is what it does. If we just plug in the coordinate transformation, we get the defining polynomial up to @@ -145,10 +141,9 @@ def post_rescaling(self): equation to bring the result into the standard form:: sage: cubic(f.defining_polynomials()) - -10886400*x^5*z - 256690425600*x^4*z^2 - 7859980800*x^3*y*z^2 - + 10886400*x^2*y^2*z^2 - 238085568000000*x^2*y*z^3 + 7*x^3 - 7*y^2*z + 1806336*y*z^2 - 155373797376*z^3 sage: cubic(f.defining_polynomials()) * f.post_rescaling() - -x^3 - 23579*x^2*z - 722*x*y*z + y^2*z - 21870000*y*z^2 + -x^3 + y^2*z - 258048*y*z^2 + 22196256768*z^3 """ return self._post @@ -164,6 +159,12 @@ def WeierstrassTransformationWithInverse(domain, codomain, sage: R. = QQ[] sage: f = EllipticCurve_from_cubic(u^3 + v^3 + w^3, [1,-1,0], morphism=True); f + Scheme morphism: + From: Projective Plane Curve over Rational Field defined by u^3 + v^3 + w^3 + To: Elliptic Curve defined by y^2 - 9*y = x^3 - 27 over Rational Field + Defn: Defined on coordinates by sending (u : v : w) to + (-w : 3*u : 1/3*u + 1/3*v) + Scheme morphism: From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: u^3 + v^3 + w^3 @@ -198,12 +199,10 @@ def inverse(self): sage: f = EllipticCurve_from_cubic(u^3 + v^3 + w^3, [1,-1,0], morphism=True) sage: f.inverse() Scheme morphism: - From: Elliptic Curve defined by y^2 + 2*x*y + 1/3*y - = x^3 - x^2 - 1/3*x - 1/27 over Rational Field - To: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: - u^3 + v^3 + w^3 + From: Elliptic Curve defined by y^2 - 9*y = x^3 - 27 over Rational Field + To: Projective Plane Curve over Rational Field defined by u^3 + v^3 + w^3 Defn: Defined on coordinates by sending (x : y : z) to - (x + y + 1/3*z : -x - y : -x) + (1/3*y : -1/3*y + 3*z : -x) """ return self._inverse From 7ac6cfb7ce78610bd2fe4f05051b8e3f87e971e1 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Thu, 25 Jan 2018 10:15:07 -0800 Subject: [PATCH 527/740] Minor fixes --- src/sage/combinat/shifted_primed_tableau.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index a80a6acaaeb..be701a06bb5 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -599,7 +599,7 @@ def _to_matrix(self): sage: mat [[1, 2', 2, 2], [None, 2, 3', None], [None, None, 3, None]] """ - m = self.shape()[0] + m = len(self[0]) return [[None]*i + list(row) + [None]*(m-i-len(row)) for i, row in enumerate(self)] @@ -809,7 +809,7 @@ def e(self, ind): element_to_change = None count = 0 - for element in read_word[::-1]: + for element in reversed(read_word): if element[1] == ind: count += 1 elif count == 0: @@ -882,7 +882,7 @@ def is_highest_weight(self, index_set=None): count = {i: 0 for i in range(max_entry+1)} if index_set is None: index_set = self.parent().index_set() - for l in read_w[::-1]: + for l in reversed(read_w): count[l] += 1 if (l-1 in index_set) and (l > 1) and (count[l] > count[l-1]): return False @@ -2041,7 +2041,7 @@ def _add_strip(sub_tab, full_tab, length): if len(sub_tab) < len(full_tab) and len(sub_tab) != 0: plat_list.append(min(sub_tab[-1] + primed_strip[-2] - 1, full_tab[len(sub_tab)])) - for row in range(1, len(sub_tab))[::-1]: + for row in reversed(range(1, len(sub_tab))): plat_list.append( min(sub_tab[row-1]+primed_strip[row-1]-1, full_tab[row]) - sub_tab[row] - primed_strip[row]) From 84483fe5a41f3465f25ab7461bafb7929df7a8cc Mon Sep 17 00:00:00 2001 From: John Cremona Date: Thu, 25 Jan 2018 19:56:08 +0000 Subject: [PATCH 528/740] #21092: added support for giving no point when there is a rational flex --- .../schemes/elliptic_curves/constructor.py | 46 +++++++++++++++---- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index 54fb7b8cd0a..7297dacc75f 100644 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -949,6 +949,29 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): sage: f([-4,0,1]) (0 : 1 : 0) + It is possible to not provide a base point ``P`` provided that the + cubic has a rational flex. In this case the flexes will be found + and one will beused as a base point:: + + sage: R. = QQ[] + sage: cubic = x^3+y^3+z^3 + sage: f = EllipticCurve_from_cubic(cubic, morphism=True) + sage: f + Scheme morphism: + From: Projective Plane Curve over Rational Field defined by x^3 + y^3 + z^3 + To: Elliptic Curve defined by y^2 - 9*y = x^3 - 27 over Rational Field + Defn: Defined on coordinates by sending (x : y : z) to + (y : -3*x : -1/3*x - 1/3*z) + + An error will be raised if not point is given and there are not rational flexes:: + + sage: R. = QQ[] + sage: cubic = 3*x^3+4*y^3+5*z^3 + sage: EllipticCurve_from_cubic(cubic) + Traceback (most recent call last): + ... + ValueError: A point must be given when the cubic has no rational flexes + TESTS: Here is a test for :trac:`21092`:: @@ -970,6 +993,7 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): # check the input R = F.parent() + K = R.base_ring() if not is_MPolynomialRing(R): raise TypeError('equation must be a polynomial') if R.ngens() != 3 or F.nvariables() != 3: @@ -977,27 +1001,27 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): if not F.is_homogeneous(): raise TypeError('equation must be a homogeneous polynomial') - if len(P) != 3: - raise TypeError('%s is not a projective point' % P) - K = R.base_ring() - - try: - C = Curve(F) - CP = C(P) - except (TypeError, ValueError): - raise TypeError('{} does not define a point on a projective curve over {} defined by {}'.format(P,K,F)) + C = Curve(F) + if P: + try: + CP = C(P) + except (TypeError, ValueError): + raise TypeError('{} does not define a point on a projective curve over {} defined by {}'.format(P,K,F)) x, y, z = R.gens() # Test whether P is a flex; if not test whether there are any rational flexes: hessian = Matrix([[F.derivative(v1, v2) for v1 in R.gens()] for v2 in R.gens()]).det() - if hessian(P)==0: + if P and hessian(P)==0: flex_point = P else: flexes = C.intersection(Curve(hessian)).rational_points() if flexes: flex_point = list(flexes[0]) + if not P: + P = flex_point + CP = C(P) else: flex_point = None @@ -1048,6 +1072,8 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): fwd_post = a else: # Second case: no flexes + if not P: + raise ValueError('A point must be given when the cubic has no rational flexes') L = C.tangents(P)[0] Qlist = [Q for Q in C.intersection(Curve(L)).rational_points() if C(Q)!=CP] # assert Qlist From 5a986593b4d655c88bb703f837a043ed423c3ff5 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Fri, 26 Jan 2018 11:30:39 +0100 Subject: [PATCH 529/740] SEEALSO: --> SEEALSO:: --- src/sage/quadratic_forms/genera/normal_form.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/quadratic_forms/genera/normal_form.py b/src/sage/quadratic_forms/genera/normal_form.py index 631aa2cbfce..c0fe4ba52ad 100644 --- a/src/sage/quadratic_forms/genera/normal_form.py +++ b/src/sage/quadratic_forms/genera/normal_form.py @@ -66,11 +66,11 @@ sage: q1.is_locally_equivalent_to(q2,2) True -SEEALSO: +SEEALSO:: -- :mod:`~sage.quadratic_forms.genera.genus` -- :meth:`~sage.quadratic_forms.quadratic_form.QuadraticForm.is_locally_equivalent_to` -- :meth:`~sage.modules.torsion_quadratic_module.TorsionQuadraticModule.normal_form` + :mod:`~sage.quadratic_forms.genera.genus` + :meth:`~sage.quadratic_forms.quadratic_form.QuadraticForm.is_locally_equivalent_to` + :meth:`~sage.modules.torsion_quadratic_module.TorsionQuadraticModule.normal_form` AUTHORS: From 987417dddffad67d0226ff151a6c5b0367b6968f Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Fri, 26 Jan 2018 11:38:22 +0100 Subject: [PATCH 530/740] Doc formating in torsion_quadratic_module.py --- src/sage/modules/torsion_quadratic_module.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sage/modules/torsion_quadratic_module.py b/src/sage/modules/torsion_quadratic_module.py index 7b11e2ea040..11086373e7f 100644 --- a/src/sage/modules/torsion_quadratic_module.py +++ b/src/sage/modules/torsion_quadratic_module.py @@ -428,11 +428,13 @@ def normal_form(self, partial=False): If `p = 2` is even, then the normal form consists of 1 x 1 blocks of the form - `0`, `[2^n]`, `[3*2^n]`, `[5*2^n]`, `[7*2^n]` - or of 2 x 2 blocks of the form - [2 1] [0 1] - [1 2] * 2^n or [1 0] * 2^n - The entries are ordered by their valuation. + ``[0]``, ``[2^n]`, ``[3*2^n]``, ``[5*2^n]``, ``[7*2^n]`` + or of `2 \times 2` blocks of the form:: + + [2 1] [0 1] + [1 2] * 2^n, [1 0] * 2^n + + The entries are ordered by their valuation. INPUT: From dcc7e9567a7df1f37aa795bb7f58ba184985745c Mon Sep 17 00:00:00 2001 From: Vincent Klein Date: Fri, 26 Jan 2018 11:31:05 +0100 Subject: [PATCH 531/740] Trac #22928: Patchs gmpy2 and update complex_number - PR-180-add_data_struc.patch by jdemeyer https://github.com/aleaxit/gmpy/pull/180 Replace mpc_set_fr_fr by two mpfr_set calls Add data structures for the various types - PR-181-Fix_mpc_precision_issue.patch by vinklein https://github.com/aleaxit/gmpy/pull/181 ``.real`` and ``.imag`` now return the same precision as their ``gmpy2.mpc`` - update complex_number.pyx : Use of ``gmpy2.mpc``'s ``.real`` and ``.imag`` getter --- .../patches/PR-180-add_data_struct.patch | 80 +++++++++++++ .../PR-181-Fix_mpc_precision_issue.patch | 105 ++++++++++++++++++ src/sage/rings/complex_number.pyx | 5 +- 3 files changed, 186 insertions(+), 4 deletions(-) create mode 100644 build/pkgs/gmpy2/patches/PR-180-add_data_struct.patch create mode 100644 build/pkgs/gmpy2/patches/PR-181-Fix_mpc_precision_issue.patch diff --git a/build/pkgs/gmpy2/patches/PR-180-add_data_struct.patch b/build/pkgs/gmpy2/patches/PR-180-add_data_struct.patch new file mode 100644 index 00000000000..d0ccab0e107 --- /dev/null +++ b/build/pkgs/gmpy2/patches/PR-180-add_data_struct.patch @@ -0,0 +1,80 @@ +commit 916b4eaefa2cbd09defbd57b1f89b3fcced4c9fe +Author: Jeroen Demeyer +Date: Thu Jan 25 10:25:15 2018 +0100 + + Replace mpc_set_fr_fr by two mpfr_set calls + +commit 82cfcac492b69ce3741d036e1741f58896ad1e48 +Author: Jeroen Demeyer +Date: Thu Jan 25 10:23:49 2018 +0100 + + Add data structures for the various types + +diff --git a/src/gmpy2.pxd b/src/gmpy2.pxd +index 7cdcc72..6d7d092 100644 +--- a/src/gmpy2.pxd ++++ b/src/gmpy2.pxd +@@ -1,14 +1,21 @@ + cdef extern from "gmp.h": + # gmp integers ++ ctypedef long mp_limb_t ++ + ctypedef struct __mpz_struct: +- pass ++ int _mp_alloc ++ int _mp_size ++ mp_limb_t* _mp_d ++ + ctypedef __mpz_struct mpz_t[1] + ctypedef __mpz_struct *mpz_ptr + ctypedef const __mpz_struct *mpz_srcptr + + # gmp rationals + ctypedef struct __mpq_struct: +- pass ++ __mpz_struct _mp_num ++ __mpz_struct _mp_den ++ + ctypedef __mpq_struct mpq_t[1] + ctypedef __mpq_struct *mpq_ptr + ctypedef const __mpq_struct *mpq_srcptr +@@ -21,8 +28,16 @@ cdef extern from "gmp.h": + + cdef extern from "mpfr.h": + # mpfr reals ++ ctypedef int mpfr_sign_t ++ ctypedef long mpfr_prec_t ++ ctypedef long mpfr_exp_t ++ + ctypedef struct __mpfr_struct: +- pass ++ mpfr_prec_t _mpfr_prec ++ mpfr_sign_t _mpfr_sign ++ mpfr_exp_t _mpfr_exp ++ mp_limb_t* _mpfr_d ++ + ctypedef __mpfr_struct mpfr_t[1] + ctypedef __mpfr_struct *mpfr_ptr + ctypedef const __mpfr_struct *mpfr_srcptr +@@ -45,7 +60,9 @@ cdef extern from "mpfr.h": + cdef extern from "mpc.h": + # mpc complexes + ctypedef struct __mpc_struct: +- pass ++ mpfr_t re ++ mpfr_t im ++ + ctypedef __mpc_struct mpc_t[1]; + ctypedef __mpc_struct *mpc_ptr; + ctypedef const __mpc_struct *mpc_srcptr; +@@ -147,5 +164,9 @@ cdef inline mpc GMPy_MPC_From_mpc(mpc_srcptr c): + # Build a gmpy2 mpc from a real part mpfr and an imaginary part mpfr + cdef inline mpc GMPy_MPC_From_mpfr(mpfr_srcptr re, mpfr_srcptr im): + cdef mpc res = GMPy_MPC_New(mpfr_get_prec(re), mpfr_get_prec(im), NULL) +- mpc_set_fr_fr(res.c, re, im, MPC_RNDNN) ++ # We intentionally use MPFR funtions instead of MPC functions here ++ # in order not to add an unneeded dependency on MPC. It's probably ++ # faster too this way. ++ mpfr_set(res.c.re, re, MPFR_RNDN) ++ mpfr_set(res.c.im, im, MPFR_RNDN) + return res diff --git a/build/pkgs/gmpy2/patches/PR-181-Fix_mpc_precision_issue.patch b/build/pkgs/gmpy2/patches/PR-181-Fix_mpc_precision_issue.patch new file mode 100644 index 00000000000..4f44205be44 --- /dev/null +++ b/build/pkgs/gmpy2/patches/PR-181-Fix_mpc_precision_issue.patch @@ -0,0 +1,105 @@ +commit a8c5d96e5821d8afec235a24669b93cd24c03380 +Author: Vincent Klein +Date: Thu Jan 25 15:49:13 2018 +0100 + + gmpy2_mpc_misc.c : Fix issue #179 + + - .real and .imag now return the same precision as their mpc + + - add doctests for these cases. + +commit f2a4f8b9ddfd53e66c6cfeee5f5e7f5d638cf755 +Author: Case Van Horsen +Date: Mon Jan 8 22:57:05 2018 -0800 + + Update copyright year. + +commit 76d96272c4f00a7894a218ca0dfabe325859de0d +Author: Case Van Horsen +Date: Mon Jan 8 22:24:27 2018 -0800 + + Remove unused macros. + +diff --git a/src/gmpy2_mpc_misc.c b/src/gmpy2_mpc_misc.c +index 12fb254..90aa99c 100644 +--- a/src/gmpy2_mpc_misc.c ++++ b/src/gmpy2_mpc_misc.c +@@ -8,7 +8,7 @@ + * 2008, 2009 Alex Martelli * + * * + * Copyright 2008, 2009, 2010, 2011, 2012, 2013, 2014, * +- * 2015, 2016, 2017 Case Van Horsen * ++ * 2015, 2016, 2017, 2018 Case Van Horsen * + * * + * This file is part of GMPY2. * + * * +@@ -48,8 +48,6 @@ GMPy_Complex_Phase(PyObject *x, CTXT_Object *context) + return NULL; + } + +- SET_MPC_WAS_NAN(context, tempx); +- + result->rc = mpc_arg(result->f, tempx->c, GET_MPFR_ROUND(context)); + Py_DECREF((PyObject*)tempx); + +@@ -112,7 +110,6 @@ GMPy_Complex_Norm(PyObject *x, CTXT_Object *context) + } + + mpfr_clear_flags(); +- SET_MPC_WAS_NAN(context, tempx); + + result->rc = mpc_norm(result->f, tempx->c, GET_MPFR_ROUND(context)); + Py_DECREF((PyObject*)tempx); +@@ -245,8 +242,6 @@ GMPy_Complex_Rect(PyObject *x, PyObject *y, CTXT_Object *context) + return NULL; + } + +- SET_MPFR_MPFR_WAS_NAN(context, tempx, tempy); +- + mpfr_cos(mpc_realref(result->c), tempy->f, GET_REAL_ROUND(context)); + mpfr_mul(mpc_realref(result->c), mpc_realref(result->c), tempx->f, GET_REAL_ROUND(context)); + mpfr_sin(mpc_imagref(result->c), tempy->f, GET_IMAG_ROUND(context)); +@@ -309,8 +304,6 @@ GMPy_Complex_Proj(PyObject *x, CTXT_Object *context) + return NULL; + } + +- SET_MPC_WAS_NAN(context, tempx); +- + result->rc = mpc_proj(result->c, tempx->c, GET_MPC_ROUND(context)); + Py_DECREF((PyObject*)tempx); + +@@ -365,8 +358,6 @@ GMPy_MPC_Conjugate_Method(PyObject *self, PyObject *args) + return NULL; + } + +- SET_MPC_WAS_NAN(context, self); +- + result->rc = mpc_conj(result->c, MPC(self), GET_MPC_ROUND(context)); + + _GMPy_MPC_Cleanup(&result, context); +@@ -402,8 +393,10 @@ GMPy_MPC_GetImag_Attrib(MPC_Object *self, void *closure) + + CHECK_CONTEXT(context); + +- if ((result = GMPy_MPFR_New(0, context))) { +- SET_MPC_WAS_NAN(context, self); ++ mpfr_prec_t rprec = 0, iprec = 0; ++ mpc_get_prec2(&rprec, &iprec, self->c); ++ ++ if ((result = GMPy_MPFR_New(iprec, context))) { + result->rc = mpc_imag(result->f, self->c, GET_MPFR_ROUND(context)); + _GMPy_MPFR_Cleanup(&result, context); + } +@@ -420,8 +413,10 @@ GMPy_MPC_GetReal_Attrib(MPC_Object *self, void *closure) + + CHECK_CONTEXT(context); + +- if ((result = GMPy_MPFR_New(0, context))) { +- SET_MPC_WAS_NAN(context, self); ++ mpfr_prec_t rprec = 0, iprec = 0; ++ mpc_get_prec2(&rprec, &iprec, self->c); ++ ++ if ((result = GMPy_MPFR_New(rprec, context))) { + result->rc = mpc_real(result->f, self->c, context->ctx.mpfr_round); + _GMPy_MPFR_Cleanup(&result, context); + } diff --git a/src/sage/rings/complex_number.pyx b/src/sage/rings/complex_number.pyx index bf0e4a3f988..1a0f06b0841 100644 --- a/src/sage/rings/complex_number.pyx +++ b/src/sage/rings/complex_number.pyx @@ -31,7 +31,6 @@ from __future__ import absolute_import, print_function import math import operator -from sage.libs.mpc cimport mpc_t from sage.libs.mpfr cimport * from sage.structure.element cimport FieldElement, RingElement, Element, ModuleElement from sage.categories.map cimport Map @@ -193,9 +192,7 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): elif isinstance(real, complex): real, imag = real.real, real.imag elif HAVE_GMPY2 and type(real) is gmpy2.mpc: - mpfr_set(self.__re, ((real).c).re, rnd) - mpfr_set(self.__im, ((real).c).im, rnd) - return + real, imag = (real).real, (real).imag else: imag = 0 try: From e3a3bf49b965b6f81814efe36d9b0bbe49a43156 Mon Sep 17 00:00:00 2001 From: Vincent Klein Date: Fri, 26 Jan 2018 11:57:52 +0100 Subject: [PATCH 532/740] Trac #22928 : update version patch p0 to p1 --- build/pkgs/gmpy2/package-version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/gmpy2/package-version.txt b/build/pkgs/gmpy2/package-version.txt index 6a98f53d6fd..bee4f7e8826 100644 --- a/build/pkgs/gmpy2/package-version.txt +++ b/build/pkgs/gmpy2/package-version.txt @@ -1 +1 @@ -2.1.0a1.p0 +2.1.0a1.p1 From 6eabcf445af462b6fffeedf8f8ce8393cb25defe Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 26 Jan 2018 10:09:47 -0600 Subject: [PATCH 533/740] Whitespace, trivial doc fixes, some optimizations, and removing floats. --- src/sage/combinat/shifted_primed_tableau.py | 200 ++++++++++---------- 1 file changed, 98 insertions(+), 102 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index be701a06bb5..ef79ba2112c 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -17,10 +17,11 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function, absolute_import +from __future__ import print_function, absolute_import, division from six import add_metaclass from sage.combinat.partition import Partition, Partitions, _Partitions, OrderedPartitions +from sage.combinat.partitions import ZS1_iterator from sage.combinat.tableau import Tableaux from sage.combinat.skew_partition import SkewPartition from sage.combinat.integer_vector import IntegerVectors @@ -168,7 +169,8 @@ def _preprocess_(T, skew=None): Preprocessing list ``T`` to initialize the tableau. TESTS:: - sage: t = ShiftedPrimedTableau([[None, "2'", "3p", 3.5]]) #indirect doctest + + sage: t = ShiftedPrimedTableau([[None, "2'", "3p", 3.5]]) # indirect doctest sage: t [(None, 2', 3', 4')] """ @@ -221,7 +223,7 @@ def __eq__(self, other): EXAMPLES:: sage: t = ShiftedPrimedTableau([[1,"2p"]]) - sage: t == ShiftedPrimedTableaux([2])([[1,1.5]]) + sage: t == ShiftedPrimedTableaux([2])([[1,3/2]]) True sage: s = ShiftedPrimedTableau([["2p",3]], skew=[1]) sage: s == [[None, "2p", 3]] @@ -253,15 +255,8 @@ def __ne__(self, other): sage: s = ShiftedPrimedTableau([["2p",3]], skew=[1]) sage: s != [[None, "2p", 3]] False - """ - if isinstance(other, ShiftedPrimedTableau): - return (self._skew != other._skew or list(self) != list(other)) - try: - Tab = ShiftedPrimedTableau(other) - except (ValueError, TypeError): - return True - return (self._skew != Tab._skew or list(self) != list(Tab)) + return not (self == other) def _repr_(self): """ @@ -281,7 +276,7 @@ def _repr_list(self): """ Return a string representation of ``self`` as a list of tuples. - EXAMPLE:: + EXAMPLES:: sage: ShiftedPrimedTableau([['2p',3],[2,2]], skew=[2])._repr_list() "[(None, None, 2', 3), (2, 2)]" @@ -565,8 +560,8 @@ def weight(self): Return the weight of ``self``. The weight of a shifted primed tableau is defined to be the vector - with `i`-th component equal to the number of entries i and i' in the - tableau. + with `i`-th component equal to the number of entries `i` and `i'` + in the tableau. EXAMPLES:: @@ -594,7 +589,8 @@ def _to_matrix(self): EXAMPLES:: - sage: t = ShiftedPrimedTableaux([4,2,1])([[1,'2p',2,2],[2,'3p'],[3]]) + sage: SPT = ShiftedPrimedTableaux([4,2,1]) + sage: t = SPT([[1,'2p',2,2],[2,'3p'],[3]]) sage: mat = t._to_matrix() sage: mat [[1, 2', 2, 2], [None, 2, 3', None], [None, None, 3, None]] @@ -623,7 +619,8 @@ def _reading_word_with_positions(self): EXAMPLES:: - sage: t = ShiftedPrimedTableaux([4,2])([[1,'2p',2,2],[2,'3p']]) + sage: SPT = ShiftedPrimedTableaux([4,2]) + sage: t = SPT([[1,'2p',2,2],[2,'3p']]) sage: t._reading_word_with_positions() [((1, 2), 3), ((0, 1), 2), ((1, 1), 2), ((0, 0), 1), ((0, 2), 2), ((0, 3), 2)] @@ -662,7 +659,8 @@ def reading_word(self): EXAMPLES:: - sage: t = ShiftedPrimedTableaux([4,2])([[1,'2p',2,2],[2,'3p']]) + sage: SPT = ShiftedPrimedTableaux([4,2]) + sage: t = SPT([[1,'2p',2,2],[2,'3p']]) sage: t.reading_word() [3, 2, 2, 1, 2, 2] """ @@ -685,15 +683,17 @@ def f(self, ind): EXAMPLES:: - sage: t = ShiftedPrimedTableaux([5,4,2])([[1,1,1,1,'3p'],[2,2,2,'3p'],[3,3]]) + sage: SPT = ShiftedPrimedTableaux([5,4,2]) + sage: t = SPT([[1,1,1,1,'3p'],[2,2,2,'3p'],[3,3]]) sage: t.pp() 1 1 1 1 3' 2 2 2 3' 3 3 sage: s = t.f(2) - sage: print(s) - None - sage: t = ShiftedPrimedTableaux([5,4,2])([[1,1,1,'2p','3p'],[2,2,3,3],[3,4]]) + sage: s is None + True + + sage: t = SPT([[1,1,1,'2p','3p'],[2,2,3,3],[3,4]]) sage: t.pp() 1 1 1 2' 3' 2 2 3 3 @@ -778,7 +778,7 @@ def e(self, ind): INPUT: - - ``ind`` -- an element in the index set of the crystal. + - ``ind`` -- an element in the index set of the crystal OUTPUT: @@ -786,8 +786,8 @@ def e(self, ind): EXAMPLES:: - sage: t = ShiftedPrimedTableaux([5,4,2])([[1,1,1,'2p','3p'], - ....: [2,'3p',3,3],[3,4]]) + sage: SPT = ShiftedPrimedTableaux([5,4,2]) + sage: t = SPT([[1,1,1,'2p','3p'], [2,'3p',3,3],[3,4]]) sage: t.pp() 1 1 1 2' 3' 2 3' 3 3 @@ -799,7 +799,6 @@ def e(self, ind): 3 4 sage: t == s.f(2) True - """ T = self._to_matrix() read_word = self._reading_word_with_positions() @@ -863,18 +862,19 @@ def is_highest_weight(self, index_set=None): r""" Return whether ``self`` is a highest weight element of the crystal. - An element is highest weight if it vanishes under all crystal operators - `e_i`. + An element is highest weight if it vanishes under all crystal + operators `e_i`. EXAMPLES:: - sage: t = ShiftedPrimedTableaux([5,4,2])([(1.0, 1.0, 1.0, 1.0, 1.0), - ....: (2.0, 2.0, 2.0, 2.5), (3.0, 3.0)]) + sage: SPT = ShiftedPrimedTableaux([5,4,2]) + sage: t = SPT([(1.0, 1.0, 1.0, 1.0, 1.0), (2.0, 2.0, 2.0, 2.5), (3.0, 3.0)]) sage: t.is_highest_weight() True - sage: s = ShiftedPrimedTableaux([5,4])([(1.0, 1.0, 1.0, 1.0, 1.0), - ....: (2.0, 2.0, 2.5, 3.0)]) - sage: s.is_highest_weight(index_set = [1]) + + sage: SPT = ShiftedPrimedTableaux([5,4]) + sage: s = SPT([(1.0, 1.0, 1.0, 1.0, 1.0), (2.0, 2.0, 2.5, 3.0)]) + sage: s.is_highest_weight(index_set=[1]) True """ read_w = self.reading_word() @@ -893,8 +893,8 @@ def weight(self): Return the weight of ``self``. The weight of a shifted primed tableau is defined to be the vector - with `i`-th component equal to the number of entries `i` and `i'` in the - tableau. + with `i`-th component equal to the number of entries `i` and `i'` + in the tableau. EXAMPLES:: @@ -912,14 +912,17 @@ def weight(self): class PrimedEntry(SageObject): - """ + r""" The class of entries in shifted primed tableaux. + + An entry in a shifted primed tableau is an element in + the alphabet `\{1' < 1 < 2' < 2 < \cdots < n' < n\}`. """ def __init__(self, entry=None, double=None): """ Normalize the entry. - TEST:: + TESTS:: sage: ShiftedPrimedTableau([[1,"2p"]])[0][1] 2' @@ -954,7 +957,7 @@ def __init__(self, entry=None, double=None): def __hash__(self): """ - TEST:: + TESTS:: sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] sage: b = ShiftedPrimedTableau([[1,1,"2p"]])[0][2] @@ -967,7 +970,7 @@ def __repr__(self): """ Represent ``self`` as primed or unprimed integer. - TEST:: + TESTS:: sage: ShiftedPrimedTableau([[1,"2p"]])[0][1] 2' @@ -979,7 +982,7 @@ def __repr__(self): def integer(self): """ - TEST:: + TESTS:: sage: ShiftedPrimedTableau([[1,"2p"]])[0][1].integer() 2 @@ -988,19 +991,18 @@ def integer(self): def __eq__(self, other): """ - TEST:: + TESTS:: sage: a = ShiftedPrimedTableau([[1,1]])[0][1] sage: b = ShiftedPrimedTableau([[1,1]])[0][0] sage: a == b True """ - return self._entry == PrimedEntry(other)._entry def __ne__(self, other): """ - TEST:: + TESTS:: sage: a = ShiftedPrimedTableau([[1,1]])[0][1] sage: b = ShiftedPrimedTableau([[1,1]])[0][0] @@ -1011,19 +1013,18 @@ def __ne__(self, other): def __lt__(self, other): """ - TEST:: + TESTS:: sage: a = ShiftedPrimedTableau([[1,"2p",2]])[0][1] sage: b = ShiftedPrimedTableau([[1,"2p",2]])[0][2] sage: a < b True """ - return self._entry < PrimedEntry(other)._entry def __le__(self, other): """ - TEST:: + TESTS:: sage: a = ShiftedPrimedTableau([[1,2,2]])[0][1] sage: b = ShiftedPrimedTableau([[1,2,2]])[0][2] @@ -1034,7 +1035,7 @@ def __le__(self, other): def __gt__(self, other): """ - TEST:: + TESTS:: sage: a = ShiftedPrimedTableau([[1,"2p",2]])[0][1] sage: b = ShiftedPrimedTableau([[1,"2p",2]])[0][2] @@ -1045,7 +1046,7 @@ def __gt__(self, other): def __ge__(self, other): """ - TEST:: + TESTS:: sage: a = ShiftedPrimedTableau([[1,"2p",2]])[0][1] sage: b = ShiftedPrimedTableau([[1,"2p",2]])[0][2] @@ -1058,7 +1059,7 @@ def is_unprimed(self): """ Checks if ``self`` is an unprimed element. - TEST:: + TESTS:: sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] sage: a.is_unprimed() @@ -1070,7 +1071,7 @@ def is_primed(self): """ Checks if ``self`` is a primed element. - TEST:: + TESTS:: sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] sage: a.is_primed() @@ -1082,7 +1083,7 @@ def unprimed(self): """ Unprime ``self`` if it is a primed element. - TEST:: + TESTS:: sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] sage: a.unprimed() @@ -1097,7 +1098,7 @@ def primed(self): """ Prime ``self`` if it is an unprimed element. - TEST:: + TESTS:: sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][0] sage: a.primed() @@ -1110,7 +1111,7 @@ def primed(self): def increase_half(self): """ - TEST:: + TESTS:: sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][0] sage: a.increase_half() @@ -1120,7 +1121,7 @@ def increase_half(self): def decrease_half(self): """ - TEST:: + TESTS:: sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][0] sage: a.decrease_half() @@ -1130,7 +1131,7 @@ def decrease_half(self): def increase_one(self): """ - TEST:: + TESTS:: sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] sage: a.increase_one() @@ -1140,7 +1141,7 @@ def increase_one(self): def decrease_one(self): """ - TEST:: + TESTS:: sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] sage: a.decrease_one() @@ -1152,7 +1153,7 @@ def decrease_one(self): class ShiftedPrimedTableaux(UniqueRepresentation, Parent): r""" Returns the combinatorial class of shifted primed tableaux subject - ro the constraints given by the arguments. + to the constraints given by the arguments. A primed tableau is a tableau of shifted shape on the alphabet `X' = \{1' < 1 < 2' < 2 < \cdots < n' < n\}` such that @@ -1221,9 +1222,8 @@ class ShiftedPrimedTableaux(UniqueRepresentation, Parent): def __classcall_private__(cls, shape=None, weight=None, max_entry=None, skew=None): r""" - Normalize and process input to return the correct parent and ensure a - unique representation. See the documentation for - :class:`ShiftedPrimedTableaux` for more information. + Normalize and process input to return the correct parent and + ensure a unique representation. TESTS:: @@ -1306,7 +1306,7 @@ def __init__(self, skew=None): """ Initialization of the parent class with given skew shape. - TEST:: + TESTS:: sage: T = ShiftedPrimedTableau([[None, 2]]) sage: T.parent()._skew @@ -1369,18 +1369,16 @@ def _contains_tableau_(self, T): if self._skew is not None: skew = self._skew + [0]*(len(T)-len(self._skew)) else: - skew = [0]*len(T) + skew = [0] * len(T) for i, row in enumerate(T): if i > 0: if not all(val > T[i-1][j+1] for j, val in enumerate(row) - if j+1 >= skew[i-1] - if val.is_unprimed()): + if j+1 >= skew[i-1] and val.is_unprimed()): return False if not all(val >= T[i-1][j+1] for j, val in enumerate(row) - if j+1 >= skew[i-1] - if val.is_primed()): + if j+1 >= skew[i-1] and val.is_primed()): return False if not all(row[j] <= row[j+1] for j in range(skew[i], len(row)-1) @@ -1407,14 +1405,16 @@ def __init__(self, skew=None): TESTS:: - sage: [[1,1.5],[2]] in ShiftedPrimedTableaux() + sage: SPT = ShiftedPrimedTableaux() + sage: [[1,1.5],[2]] in SPT True - sage: [[1,1.5],[1.5]] in ShiftedPrimedTableaux() + sage: [[1,1.5],[1.5]] in SPT False - sage: [[1,1],[1]] in ShiftedPrimedTableaux() + sage: [[1,1],[1]] in SPT False - sage: [[1,1],[2,2]] in ShiftedPrimedTableaux() + sage: [[1,1],[2,2]] in SPT False + sage: TestSuite(SPT).run() # long time """ Parent.__init__(self, category=InfiniteEnumeratedSets()) ShiftedPrimedTableaux.__init__(self, skew=skew) @@ -1435,8 +1435,7 @@ def _repr_(self): def _element_constructor_(self, T): """ - Construct an object from ``T`` as an element of ``self``, if - possible. + Construct an object from ``T`` as an element of ``self``, if possible. INPUT: @@ -1457,7 +1456,6 @@ def _element_constructor_(self, T): ... ValueError: [[1, 1, 2], [2, 2]] is not an element of Shifted Primed Tableaux - """ return (super(ShiftedPrimedTableaux_all, self)._element_constructor_(T)) @@ -1529,11 +1527,16 @@ def __classcall_private__(cls, shape, max_entry=None, skew=None): """ Normalize the attributes for the class. - TEST:: + TESTS:: sage: SPT = ShiftedPrimedTableaux(shape=[2,1]) sage: SPT._shape.parent() Partitions + + sage: SPT1 = ShiftedPrimedTableaux(shape=(2,1), max_entry=3) + sage: SPT2 = ShiftedPrimedTableaux(shape=[2,1], max_entry=3) + sage: SPT1 is SPT2 + True """ shape = _Partitions(shape) return (super(ShiftedPrimedTableaux_shape, cls) @@ -1548,7 +1551,8 @@ def __init__(self, shape, max_entry, skew): TESTS:: - sage: TestSuite(ShiftedPrimedTableaux([4,2,1], max_entry=4)).run() + sage: SPT = ShiftedPrimedTableaux([4,2,1], max_entry=4) + sage: TestSuite(SPT).run() # long time """ # Determine the correct category if max_entry is None: @@ -1658,7 +1662,7 @@ def module_generators(self): """ Return the generators of ``self`` as a crystal. - TEST:: + TESTS:: sage: SPT = ShiftedPrimedTableaux(shape=[2,1]) sage: SPT.module_generators @@ -1728,14 +1732,13 @@ class ShiftedPrimedTableaux_weight(ShiftedPrimedTableaux): """ Shifted primed tableaux of fixed weight. - TESTS:: + EXAMPLES:: sage: ShiftedPrimedTableaux(weight=(2,3,1)) Shifted Primed Tableaux of weight (2, 3, 1) sage: ShiftedPrimedTableaux(weight=(2,3,1)).cardinality() 17 """ - def __init__(self, weight, skew=None): """ Initialize the class of shifted primed tableaux of a given weight. @@ -1804,10 +1807,10 @@ def _contains_tableau_(self, T): return False flat = [item.integer() for sublist in T for item in sublist] - if flat == []: + if not flat: max_ind = 0 else: - max_ind = int(max(flat)) + max_ind = max(flat) weight = tuple([flat.count(i+1) for i in range(max_ind)]) return self._weight == weight @@ -1826,7 +1829,7 @@ def __iter__(self): sage: len(list(Tabs)) 5 """ - for shape_ in Partitions(sum(self._weight)): + for shape_ in ZS1_iterator(sum(self._weight)): if all(shape_[i] > shape_[i+1] for i in range(len(shape_)-1)): for tab in ShiftedPrimedTableaux(shape=shape_, weight=self._weight, skew=self._skew): @@ -1838,7 +1841,7 @@ class ShiftedPrimedTableaux_weight_shape(ShiftedPrimedTableaux): """ Shifted primed tableaux of the fixed weight and shape. - TESTS:: + EXAMPLES:: sage: ShiftedPrimedTableaux([4,2,1], weight=(2,3,2)) Shifted Primed Tableaux of weight (2, 3, 2) and shape [4, 2, 1] @@ -1859,7 +1862,7 @@ def __init__(self, weight, shape, skew=None): self._weight = weight self._skew = skew if skew is None: - self._shape = Partition(shape) + self._shape = _Partitions(shape) else: self._shape = SkewPartition((shape, skew)) @@ -1888,7 +1891,6 @@ def _contains_tableau_(self, T): sage: [[1,1.5,2,3],[3]] in ShiftedPrimedTableaux([3,2], weight=(1,2,2)) False """ - if not super(ShiftedPrimedTableaux_weight_shape, self)._contains_tableau_(T): return False @@ -1950,7 +1952,7 @@ def __iter__(self): sage: len(list(Tabs)) 4 - TEST:: + TESTS:: sage: Tabs = ShiftedPrimedTableaux([3,2], weight=(1,4)) sage: list(Tabs) @@ -1959,30 +1961,26 @@ def __iter__(self): if self._skew is not None: raise NotImplementedError('skew tableau must be empty') - if not self._shape.dominates( - Partition(sorted(list(self._weight), reverse=True))): + if not self._shape.dominates(sorted(self._weight, reverse=True)): return full_shape = self._shape sub_tab = [] tab_list_new = [[]] + half = ~QQ(2) for i, w in enumerate(self._weight): tab_list_old = tab_list_new tab_list_new = [] for sub_tab in tab_list_old: - sub_shape = [len(sub_tab[r]) for r in range(len(sub_tab))] + sub_shape = [len(row) for row in sub_tab] for strip in _add_strip(sub_shape, full_shape, w): - l = int(len(strip)/2) + l = len(strip) // 2 if len(sub_shape) < len(full_shape): - new_tab = [sub_tab[r] - + [float(i+.5)]*int(strip[r]) - + [float(i+1)]*int(strip[-r-1]) + new_tab = [sub_tab[r] + [i+half]*strip[r] + [i+1]*strip[-r-1] for r in range(l-1)] if strip[l] != 0: - new_tab.append([float(i+1)]*int(strip[l])) + new_tab.append([i+1] * strip[l]) else: - new_tab = [sub_tab[r] - + [float(i+.5)]*int(strip[r]) - + [float(i+1)]*int(strip[-r-1]) + new_tab = [sub_tab[r] + [i+half]*strip[r] + [i+1]*strip[-r-1] for r in range(l)] tab_list_new.append(new_tab) for tab in tab_list_new: @@ -2004,10 +2002,10 @@ def _add_strip(sub_tab, full_tab, length): sage: list(ShiftedPrimedTableaux([3,1], weight=(2,2))) # indirect doctest [[(1, 1, 2), (2,)], [(1, 1, 2'), (2,)]] """ - if sum(sub_tab)+length > sum(full_tab): + if sum(sub_tab) + length > sum(full_tab): raise ValueError("strip does not fit") - if len(sub_tab) == 0: + if not sub_tab: cliff_list = [] else: cliff_list = [int(sub_tab[0] != full_tab[0])] @@ -2023,11 +2021,11 @@ def _add_strip(sub_tab, full_tab, length): if len(sub_tab) < len(full_tab): cliff_list.append(0) - for primes_num in range(min(sum(cliff_list), length)+1): + for primes_num in range(min(sum(cliff_list), length) + 1): for primed_list in IntegerVectors(n=primes_num, k=len(cliff_list), outer=cliff_list): row = 0 - primed_strip = list() + primed_strip = [] for i, cliff in enumerate(cliff_list): if cliff == 0: row += 1 @@ -2036,7 +2034,7 @@ def _add_strip(sub_tab, full_tab, length): primed_strip.extend([int(primed_list[i] > j) for j in range(cliff)]) row += cliff - plat_list = list() + plat_list = [] if len(sub_tab) < len(full_tab) and len(sub_tab) != 0: plat_list.append(min(sub_tab[-1] + primed_strip[-2] - 1, @@ -2050,8 +2048,6 @@ def _add_strip(sub_tab, full_tab, length): else: plat_list.append(full_tab[0]) - if sum(plat_list) < length - primes_num: - pass for non_primed_strip in IntegerVectors(n=length-primes_num, k=len(plat_list), outer=plat_list): From e9a6fdaf5e130e826929c4d9a6617a2128afe3d0 Mon Sep 17 00:00:00 2001 From: Vincent Klein Date: Fri, 26 Jan 2018 17:16:40 +0100 Subject: [PATCH 534/740] Trac #22928 : add some missings # optional - gmpy2 --- src/sage/rings/real_mpfr.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 9a3ea524f75..24f67bf1c13 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -3746,15 +3746,15 @@ cdef class RealNumber(sage.structure.element.RingElement): sage: mpfr(R.pi()).precision # optional - gmpy2 42 sage: R = RealField(256) - sage: x = mpfr(R.pi()) + sage: x = mpfr(R.pi()) # optional - gmpy2 sage: x.precision # optional - gmpy2 256 sage: y = R(x) # optional - gmpy2 sage: mpfr(y) == x # optional - gmpy2 True sage: x = mpfr('2.567e42', precision=128) # optional - gmpy2 - sage: y = RealField(128)(x) - sage: mpfr(y) == x + sage: y = RealField(128)(x) # optional - gmpy2 + sage: mpfr(y) == x # optional - gmpy2 True TESTS:: From 9ca063484badf58b31e081cca575ad1a21a8dab2 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 26 Jan 2018 10:31:00 -0600 Subject: [PATCH 535/740] Fixing bug with 0 weight tableaux. --- src/sage/combinat/shifted_primed_tableau.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index ef79ba2112c..3483cca7b1b 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -1249,6 +1249,11 @@ def __classcall_private__(cls, shape=None, weight=None, Traceback (most recent call last): ... ValueError: skew shape must be inside the given tableau shape + + sage: SPT1 = ShiftedPrimedTableaux(weight=()) + sage: SPT2 = ShiftedPrimedTableaux(weight=(0,0,0)) + sage: SPT1 is SPT2 + True """ if skew is not None: try: @@ -1278,7 +1283,7 @@ def __classcall_private__(cls, shape=None, weight=None, raise ValueError('skew shape must be inside the given tableau shape') if weight is not None: - while weight[-1] == 0: + while weight and weight[-1] == 0: weight = weight[:-1] if max_entry is not None and weight is not None: @@ -1802,15 +1807,19 @@ def _contains_tableau_(self, T): True sage: [[1,1.5],[3]] in ShiftedPrimedTableaux(weight=(1,2)) False + + sage: [] in ShiftedPrimedTableaux(weight=()) + True + sage: [] in ShiftedPrimedTableaux(weight=(1,2)) + False """ if not super(ShiftedPrimedTableaux_weight, self)._contains_tableau_(T): return False flat = [item.integer() for sublist in T for item in sublist] if not flat: - max_ind = 0 - else: - max_ind = max(flat) + return not self._weight + max_ind = max(flat) weight = tuple([flat.count(i+1) for i in range(max_ind)]) return self._weight == weight From 364ec35f1cbdab920e52c44341b8c19c8183b818 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Fri, 26 Jan 2018 16:33:54 +0000 Subject: [PATCH 536/740] LD_LIBRARY_PATH="." everywhere except OSX --- build/pkgs/python3/spkg-build | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/python3/spkg-build b/build/pkgs/python3/spkg-build index 56db087ae5a..51daef31a31 100644 --- a/build/pkgs/python3/spkg-build +++ b/build/pkgs/python3/spkg-build @@ -95,10 +95,10 @@ if [ $? -ne 0 ]; then exit 1 fi -if [ "$UNAME" = "Linux" ]; then - export LD_LIBRARY_PATH="." -elif [ "$UNAME" = "Darwin" ]; then +if [ "$UNAME" = "Darwin" ]; then export DYLD_LIBRARY_PATH="." +else + export LD_LIBRARY_PATH="." fi # When building on a case-insensitive filesystem (on any OS, not just Windows) From 40433dd77e2732e8869d6582d7031ae368598afc Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 26 Jan 2018 10:43:09 -0600 Subject: [PATCH 537/740] A little more cleanup. --- src/sage/combinat/shifted_primed_tableau.py | 53 +++++++++++---------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index 3483cca7b1b..736794472f4 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -1462,7 +1462,7 @@ def _element_constructor_(self, T): ValueError: [[1, 1, 2], [2, 2]] is not an element of Shifted Primed Tableaux """ - return (super(ShiftedPrimedTableaux_all, self)._element_constructor_(T)) + return super(ShiftedPrimedTableaux_all, self)._element_constructor_(T) def __iter__(self): """ @@ -1657,8 +1657,7 @@ def _element_constructor_(self, T): of shape [3] """ try: - return (super(ShiftedPrimedTableaux_shape, self) - ._element_constructor_(T)) + return super(ShiftedPrimedTableaux_shape, self)._element_constructor_(T) except ValueError: raise ValueError("{} is not an element of {}".format(T, self)) @@ -1786,14 +1785,13 @@ def _element_constructor_(self, T): TESTS:: - sage: tab= ShiftedPrimedTableaux(weight=(2,1))([[1,1,1.5]]); tab + sage: tab = ShiftedPrimedTableaux(weight=(2,1))([[1,1,1.5]]); tab [(1, 1, 2')] sage: tab.parent() Shifted Primed Tableaux of weight (2, 1) """ try: - return (super(ShiftedPrimedTableaux_weight, self) - ._element_constructor_(T)) + return super(ShiftedPrimedTableaux_weight, self)._element_constructor_(T) except ValueError: raise ValueError("{} is not an element of {}".format(T, self)) @@ -1899,15 +1897,22 @@ def _contains_tableau_(self, T): False sage: [[1,1.5,2,3],[3]] in ShiftedPrimedTableaux([3,2], weight=(1,2,2)) False + + sage: [] in ShiftedPrimedTableaux([3,2], weight=(1,2,2)) + False + sage: [] in ShiftedPrimedTableaux([], weight=()) + True """ if not super(ShiftedPrimedTableaux_weight_shape, self)._contains_tableau_(T): return False flat = [item.integer() for sublist in T for item in sublist] - if flat == []: - max_ind = 0 - else: - max_ind = int(max(flat)) + if not flat: + # It is sufficient only to check this because the weight + # and shape must be compatible + return not self._weight + + max_ind = max(flat) weight = tuple([flat.count(i+1) for i in range(max_ind)]) if self._weight != weight: return False @@ -1915,34 +1920,30 @@ def _contains_tableau_(self, T): shape = [len(row) for row in T] skew = [row.count(None) for row in T] if sum(skew) == 0: - shape = Partition(shape) + shape = _Partitions(shape) else: shape = SkewPartition((shape, skew)) - if self._shape != shape: - return False - - return True + return self._shape == shape: def _element_constructor_(self, T): """ - Construct an object from ``T`` as an element of ``self``, if - possible. + Construct an object from ``T`` as an element of ``self``, if possible. TESTS:: - sage: tab = ShiftedPrimedTableaux([3], weight=(2,1))([[1,1,1.5]]); tab + sage: SPT = ShiftedPrimedTableaux([3], weight=(2,1)) + sage: tab = SPT([[1,1,1.5]]); tab [(1, 1, 2')] - sage: tab.parent() - Shifted Primed Tableaux of weight (2, 1) and shape [3] - sage: ShiftedPrimedTableaux([3], weight=(2,1))([[1,1]]) + sage: tab.parent() is SPT + True + sage: SPT([[1,1]]) Traceback (most recent call last): ... ValueError: [[1, 1]] is not an element of Shifted Primed Tableaux - of weight (2, 1) and shape [3] + of weight (2, 1) and shape [3] """ try: - return (super(ShiftedPrimedTableaux_weight_shape, self) - ._element_constructor_(T)) + return super(ShiftedPrimedTableaux_weight_shape, self)._element_constructor_(T) except ValueError: raise ValueError("{} is not an element of {}".format(T, self)) @@ -2045,14 +2046,14 @@ def _add_strip(sub_tab, full_tab, length): row += cliff plat_list = [] - if len(sub_tab) < len(full_tab) and len(sub_tab) != 0: + if sub_tab and len(sub_tab) < len(full_tab): plat_list.append(min(sub_tab[-1] + primed_strip[-2] - 1, full_tab[len(sub_tab)])) for row in reversed(range(1, len(sub_tab))): plat_list.append( min(sub_tab[row-1]+primed_strip[row-1]-1, full_tab[row]) - sub_tab[row] - primed_strip[row]) - if len(sub_tab) > 0: + if sub_tab: plat_list.append(full_tab[0] - sub_tab[0] - primed_strip[0]) else: plat_list.append(full_tab[0]) From 3466f5c61edb21921a4a476e20c69b6410c4e9a4 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 26 Jan 2018 10:43:24 -0600 Subject: [PATCH 538/740] Removing redundant _element_constructor_'s. These had the same error message copied and otherwise were using the inherited method. --- src/sage/combinat/shifted_primed_tableau.py | 153 ++++++-------------- 1 file changed, 44 insertions(+), 109 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index 736794472f4..6f8b7d27f51 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -1316,6 +1316,7 @@ def __init__(self, skew=None): sage: T = ShiftedPrimedTableau([[None, 2]]) sage: T.parent()._skew [1] + sage: TestSuite(T).run() """ self._skew = skew @@ -1332,16 +1333,53 @@ def _element_constructor_(self, T): - the corresponding primed tableau object - TESTS:: + EXAMPLES:: - sage: Tabs = ShiftedPrimedTableaux() - sage: Tabs([[1,"2p","2p"]]) + sage: SPT = ShiftedPrimedTableaux() + sage: tab = SPT([[1,1,"2p"]]); tab + [(1, 1, 2')] + sage: tab.parent() is SPT + True + sage: tab = SPT([[1,1,2],[2,2]]) + Traceback (most recent call last): + ... + ValueError: [[1, 1, 2], [2, 2]] is not an element of Shifted Primed Tableaux + sage: SPT([[1,"2p","2p"]]) Traceback (most recent call last): ... ValueError: [[1, '2p', '2p']] is not an element of Shifted Primed Tableaux - sage: Tabs = ShiftedPrimedTableaux(skew=[1]) - sage: Tabs([["2p",2]]) + + sage: SPT = ShiftedPrimedTableaux(skew=[1]) + sage: SPT([["2p",2]]) [(None, 2', 2)] + + sage: SPT = ShiftedPrimedTableaux(weight=(2,1)) + sage: tab = SPT([[1,1,1.5]]); tab + [(1, 1, 2')] + sage: tab.parent() is SPT + True + + sage: SPT = ShiftedPrimedTableaux([3]) + sage: tab = SPT([[1,1,1.5]]); tab + [(1, 1, 2')] + sage: tab.parent() is SPT + True + sage: SPT([[1,1]]) + Traceback (most recent call last): + ... + ValueError: [[1, 1]] is not an element of Shifted Primed Tableaux + of shape [3] + + sage: SPT = ShiftedPrimedTableaux([3], weight=(2,1)) + sage: tab = SPT([[1,1,1.5]]); tab + [(1, 1, 2')] + sage: tab.parent() is SPT + True + sage: SPT([[1,1]]) + Traceback (most recent call last): + ... + ValueError: [[1, 1]] is not an element of Shifted Primed Tableaux + of weight (2, 1) and shape [3] """ try: return self.element_class(self, T, skew=self._skew) @@ -1438,32 +1476,6 @@ def _repr_(self): return "Shifted Primed Tableaux" return "Shifted Primed Tableaux skewed by {}".format(self._skew) - def _element_constructor_(self, T): - """ - Construct an object from ``T`` as an element of ``self``, if possible. - - INPUT: - - - ``T`` -- data which can be interpreted as a tableau - - OUTPUT: - - - the corresponding tableau object - - TESTS:: - - sage: Tab=ShiftedPrimedTableaux()([[1,1,"2p"]]); Tab - [(1, 1, 2')] - sage: Tab.parent() - Shifted Primed Tableaux - sage: Tab=ShiftedPrimedTableaux()([[1,1,2],[2,2]]) - Traceback (most recent call last): - ... - ValueError: [[1, 1, 2], [2, 2]] is not an element of - Shifted Primed Tableaux - """ - return super(ShiftedPrimedTableaux_all, self)._element_constructor_(T) - def __iter__(self): """ Iterate over ``self``. @@ -1631,36 +1643,6 @@ def _contains_tableau_(self, T): return True - def _element_constructor_(self, T): - """ - Construct an object from ``T`` as an element of ``self``, if - possible. - - INPUT: - - - ``T`` -- data which can be interpreted as a primed tableau - - OUTPUT: - - - the corresponding primed tableau object - - TESTS:: - - sage: tab= ShiftedPrimedTableaux([3])([[1,1,1.5]]); tab - [(1, 1, 2')] - sage: tab.parent() - Shifted Primed Tableaux of shape [3] - sage: ShiftedPrimedTableaux([3])([[1,1]]) - Traceback (most recent call last): - ... - ValueError: [[1, 1]] is not an element of Shifted Primed Tableaux - of shape [3] - """ - try: - return super(ShiftedPrimedTableaux_shape, self)._element_constructor_(T) - except ValueError: - raise ValueError("{} is not an element of {}".format(T, self)) - @lazy_attribute def module_generators(self): """ @@ -1770,31 +1752,6 @@ def _repr_(self): return ("Shifted Primed Tableaux of weight {} skewed by {}" .format(self._weight, self._skew)) - def _element_constructor_(self, T): - """ - Construct an object from ``T`` as an element of ``self``, if - possible. - - INPUT: - - - ``T`` -- data which can be interpreted as a primed tableau - - OUTPUT: - - - the corresponding primed tableau object - - TESTS:: - - sage: tab = ShiftedPrimedTableaux(weight=(2,1))([[1,1,1.5]]); tab - [(1, 1, 2')] - sage: tab.parent() - Shifted Primed Tableaux of weight (2, 1) - """ - try: - return super(ShiftedPrimedTableaux_weight, self)._element_constructor_(T) - except ValueError: - raise ValueError("{} is not an element of {}".format(T, self)) - def _contains_tableau_(self, T): """ Check if ``self`` contains preprocessed tableau ``T``. @@ -1923,29 +1880,7 @@ def _contains_tableau_(self, T): shape = _Partitions(shape) else: shape = SkewPartition((shape, skew)) - return self._shape == shape: - - def _element_constructor_(self, T): - """ - Construct an object from ``T`` as an element of ``self``, if possible. - - TESTS:: - - sage: SPT = ShiftedPrimedTableaux([3], weight=(2,1)) - sage: tab = SPT([[1,1,1.5]]); tab - [(1, 1, 2')] - sage: tab.parent() is SPT - True - sage: SPT([[1,1]]) - Traceback (most recent call last): - ... - ValueError: [[1, 1]] is not an element of Shifted Primed Tableaux - of weight (2, 1) and shape [3] - """ - try: - return super(ShiftedPrimedTableaux_weight_shape, self)._element_constructor_(T) - except ValueError: - raise ValueError("{} is not an element of {}".format(T, self)) + return self._shape == shape def __iter__(self): """ From 3771ec97a75d224bdea18de963e9129bbcd4b86f Mon Sep 17 00:00:00 2001 From: Sebastian Oehms Date: Sat, 27 Jan 2018 00:15:34 +0100 Subject: [PATCH 539/740] missing blanks in docstring inserted --- src/sage/typeset/unicode_art.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/typeset/unicode_art.py b/src/sage/typeset/unicode_art.py index 504d7a4da3a..9407b39dfe4 100644 --- a/src/sage/typeset/unicode_art.py +++ b/src/sage/typeset/unicode_art.py @@ -107,9 +107,9 @@ def unicode_art(*obj, **kwds): sage: sep_line = unicode_art('\n'.join(u' ⎟ ' for _ in range(5)), baseline=5) sage: unicode_art(*Partitions(4), separator=sep_line, sep_baseline=0) - ⎟ ⎟ ⎟ ⎟ ┌┐ - ⎟ ⎟ ⎟ ┌┬┐ ⎟ ├┤ - ⎟ ┌┬┬┐ ⎟ ┌┬┐ ⎟ ├┼┘ ⎟ ├┤ + ⎟ ⎟ ⎟ ⎟ ┌┐ + ⎟ ⎟ ⎟ ┌┬┐ ⎟ ├┤ + ⎟ ┌┬┬┐ ⎟ ┌┬┐ ⎟ ├┼┘ ⎟ ├┤ ┌┬┬┬┐ ⎟ ├┼┴┘ ⎟ ├┼┤ ⎟ ├┤ ⎟ ├┤ └┴┴┴┘ ⎟ └┘ ⎟ └┴┘ ⎟ └┘ ⎟ └┘ From b4a34e6a6c51a6fe086f3e68cc47755b06bf2154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 28 Jan 2018 10:42:19 +0100 Subject: [PATCH 540/740] some typos again --- build/pkgs/coxeter3/SPKG.txt | 2 +- src/sage/calculus/riemann.pyx | 2 +- src/sage/categories/modules_with_basis.py | 4 ++-- src/sage/coding/delsarte_bounds.py | 2 +- src/sage/coding/linear_code.py | 2 +- src/sage/coding/reed_muller_code.py | 2 +- .../cluster_algebra_quiver/cluster_seed.py | 2 +- .../combinat/crystals/kirillov_reshetikhin.py | 2 +- src/sage/combinat/designs/orthogonal_arrays.py | 4 ++-- src/sage/combinat/dyck_word.py | 2 +- src/sage/combinat/integer_lists/invlex.pyx | 2 +- src/sage/combinat/k_tableau.py | 2 +- .../combinat/species/composition_species.py | 2 +- .../product_projective_ds.py | 2 +- .../arithmetic_dynamics/projective_ds.py | 12 ++++++------ .../dynamics/arithmetic_dynamics/wehlerK3.py | 2 +- .../dynamics/complex_dynamics/mandel_julia.py | 2 +- .../hyperbolic_space/hyperbolic_geodesic.py | 4 ++-- src/sage/geometry/polyhedron/base.py | 6 +++--- src/sage/graphs/hypergraph_generators.py | 4 ++-- src/sage/graphs/line_graph.py | 15 ++++++++------- src/sage/graphs/modular_decomposition.py | 2 +- src/sage/groups/braid.py | 2 +- .../perm_gps/partn_ref/refinement_graphs.pyx | 2 +- src/sage/interfaces/tachyon.py | 2 +- src/sage/manifolds/chart_func.py | 6 +++--- .../differentiable/manifold_homset.py | 4 ++-- src/sage/manifolds/differentiable/real_line.py | 2 +- src/sage/manifolds/scalarfield.py | 18 +++++++++--------- src/sage/matrix/matrix2.pyx | 2 +- src/sage/matrix/matrix_double_dense.pyx | 2 +- src/sage/misc/converting_dict.py | 2 +- .../modform/l_series_gross_zagier_coeffs.pyx | 2 +- .../modform_hecketriangle/abstract_space.py | 2 +- .../modular/modform_hecketriangle/functors.py | 2 +- .../modular/modform_hecketriangle/readme.py | 2 +- src/sage/modular/pollack_stevens/manin_map.py | 2 +- src/sage/modular/pollack_stevens/modsym.py | 2 +- src/sage/modular/pollack_stevens/space.py | 2 +- .../rings/function_field/function_field.py | 2 +- src/sage/rings/integer.pyx | 2 +- .../rings/padics/padic_template_element.pxi | 2 +- .../rings/polynomial/polynomial_element.pyx | 2 +- src/sage/schemes/toric/chow_group.py | 2 +- .../distributions/discrete_gaussian_lattice.py | 2 +- 45 files changed, 74 insertions(+), 73 deletions(-) diff --git a/build/pkgs/coxeter3/SPKG.txt b/build/pkgs/coxeter3/SPKG.txt index 4ae292eea1c..d5bd21c6a2b 100644 --- a/build/pkgs/coxeter3/SPKG.txt +++ b/build/pkgs/coxeter3/SPKG.txt @@ -44,6 +44,6 @@ by spkg-install: its data and messages from the Sage installation tree - makefile.patch: handle compilation flags for Darwin and Cygwin - warning.patch: remove deprecation warnings -- sage.cpp: add a function for Bruhat invervals which is simpler to +- sage.cpp: add a function for Bruhat intervals which is simpler to code in C++ than Cython diff --git a/src/sage/calculus/riemann.pyx b/src/sage/calculus/riemann.pyx index 01248935392..9f9ebfc7a02 100644 --- a/src/sage/calculus/riemann.pyx +++ b/src/sage/calculus/riemann.pyx @@ -579,7 +579,7 @@ cdef class Riemann_Map: the interior of the mapped region, ``riemann_map`` will return the point on the unit disk that ``pt`` maps to. Note that this method only works for interior points; accuracy breaks down very close - to the boundary. To get boundary corrospondance, use + to the boundary. To get boundary correspondance, use :meth:`get_theta_points`. INPUT: diff --git a/src/sage/categories/modules_with_basis.py b/src/sage/categories/modules_with_basis.py index 241eb3e2424..d13b27bf6d0 100644 --- a/src/sage/categories/modules_with_basis.py +++ b/src/sage/categories/modules_with_basis.py @@ -675,8 +675,8 @@ def submodule(self, gens, check=True, already_echelonized=False, the elements of ``gens`` are already in (not necessarily reduced) echelon form - - ``unitrangular`` -- (default: ``False``) whether - the lift morphism is unitrangular + - ``unitriangular`` -- (default: ``False``) whether + the lift morphism is unitriangular If ``already_echelonized`` is ``False``, then the generators are put in reduced echelon form using diff --git a/src/sage/coding/delsarte_bounds.py b/src/sage/coding/delsarte_bounds.py index 8d7ccd557e2..270aae3fb61 100644 --- a/src/sage/coding/delsarte_bounds.py +++ b/src/sage/coding/delsarte_bounds.py @@ -197,7 +197,7 @@ def delsarte_bound_hamming_space(n, d, q, return_data=False, solver="PPL", isint [1, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0] The bound on the size of the `F_2`-codes of length 24 and minimal distance - 8, i.e. parameters of the extened binary Golay code:: + 8, i.e. parameters of the extended binary Golay code:: sage: a,p,x = codes.bounds.delsarte_bound_hamming_space(24,8,2,return_data=True) sage: x diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index edb8ca3d500..888cca33f66 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -1318,7 +1318,7 @@ def parity_check_matrix(self): @cached_method def covering_radius(self): r""" - Return the minimimal integer `r` such that any element in the ambient space of ``self`` has distance at most `r` to a codeword of ``self``. + Return the minimal integer `r` such that any element in the ambient space of ``self`` has distance at most `r` to a codeword of ``self``. This method requires the optional GAP package Guava. diff --git a/src/sage/coding/reed_muller_code.py b/src/sage/coding/reed_muller_code.py index f3975ec23e6..0a74a03038f 100644 --- a/src/sage/coding/reed_muller_code.py +++ b/src/sage/coding/reed_muller_code.py @@ -685,7 +685,7 @@ def points(self): class ReedMullerPolynomialEncoder(Encoder): r""" - Encoder for Reed-Muller codes which encodes appropiate multivariate polynomials into codewords. + Encoder for Reed-Muller codes which encodes appropriate multivariate polynomials into codewords. Consider a Reed-Muller code of order `r`, number of variables `m`, length `n`, dimension `k` over some finite field `F`. diff --git a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py index b4fd1b000e7..796fd220d3a 100644 --- a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py +++ b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py @@ -4674,7 +4674,7 @@ def _compute_compatible_vectors(self,vd): for s in psetvect: pass1 = True #The first possible failure for compatibility is if any entry in s is larger than the corresponding entry of a. - #Only checks for the mutable verices since all entries in a_i i>num_cols are zero. + #Only checks for the mutable vertices since all entries in a_i i>num_cols are zero. for k in range(num_cols): if s[k] > a[0][k]: pass1 = False diff --git a/src/sage/combinat/crystals/kirillov_reshetikhin.py b/src/sage/combinat/crystals/kirillov_reshetikhin.py index dd09ec349dc..38249a65824 100644 --- a/src/sage/combinat/crystals/kirillov_reshetikhin.py +++ b/src/sage/combinat/crystals/kirillov_reshetikhin.py @@ -853,7 +853,7 @@ def classical_decomposition(self): It is given by `B^{r,s} \cong \bigoplus_\Lambda B(\Lambda)`, where `\Lambda` are weights obtained from a rectangle of width `s` - and height `r` by removing verticle dominoes. Here we identify + and height `r` by removing vertical dominoes. Here we identify the fundamental weight `\Lambda_i` with a column of height `i`. EXAMPLES:: diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 6f80263ee2a..414a3114268 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -134,7 +134,7 @@ def transversal_design(k,n,resolvable=False,check=True,existence=False): .. SEEALSO:: - :func:`orthogonal_array` -- a tranversal design `TD(k,n)` is equivalent to an + :func:`orthogonal_array` -- a transversal design `TD(k,n)` is equivalent to an orthogonal array `OA(k,n,2)`. EXAMPLES:: @@ -469,7 +469,7 @@ def is_transversal_design(B,k,n, verbose=False): .. NOTE:: - The tranversal design must have `\{0, \ldots, kn-1\}` as a ground set, + The transversal design must have `\{0, \ldots, kn-1\}` as a ground set, partitioned as `k` sets of size `n`: `\{0, \ldots, k-1\} \sqcup \{k, \ldots, 2k-1\} \sqcup \cdots \sqcup \{k(n-1), \ldots, kn-1\}`. diff --git a/src/sage/combinat/dyck_word.py b/src/sage/combinat/dyck_word.py index 36d2da07161..f90bb6476c8 100644 --- a/src/sage/combinat/dyck_word.py +++ b/src/sage/combinat/dyck_word.py @@ -2068,7 +2068,7 @@ def to_321_avoiding_permutation(self): It is shown in [EP2004]_ that it sends the number of centered tunnels to the number of fixed points, the number of right tunnels to the - number of exceedences, and the semilength plus the height of the middle + number of excedences, and the semilength plus the height of the middle point to 2 times the length of the longest increasing subsequence. REFERENCES: diff --git a/src/sage/combinat/integer_lists/invlex.pyx b/src/sage/combinat/integer_lists/invlex.pyx index 04a201c2b1c..dd8200670cf 100644 --- a/src/sage/combinat/integer_lists/invlex.pyx +++ b/src/sage/combinat/integer_lists/invlex.pyx @@ -1547,7 +1547,7 @@ class IntegerListsLexIter(object): ``self._current_list``. The current algorithm computes, for `k = j, j+1, \ldots`, a lower bound `l_k` and an upper bound `u_k` for `v_0+\dots+v_k`, and stops if none of the - invervals `[l_k, u_k]` intersect ``[min_sum, max_sum]``. + intervals `[l_k, u_k]` intersect ``[min_sum, max_sum]``. The lower bound `l_k` is given by the area below `v_0,\dots,v_{j-1}` prolongated by the lower envelope diff --git a/src/sage/combinat/k_tableau.py b/src/sage/combinat/k_tableau.py index 4ea1ddd0cab..1e9a447f1af 100644 --- a/src/sage/combinat/k_tableau.py +++ b/src/sage/combinat/k_tableau.py @@ -4549,7 +4549,7 @@ def marked_CST_to_transposition_sequence( self, T, k ): @classmethod def transpositions_to_standard_strong( self, transeq, k, emptyTableau=[] ): """ - Return a strong tableau correponding to a sequence of transpositions. + Return a strong tableau corresponding to a sequence of transpositions. This method returns the action by left multiplication on the empty strong tableau by transpositions specified by ``transeq``. diff --git a/src/sage/combinat/species/composition_species.py b/src/sage/combinat/species/composition_species.py index c97a93fb7cf..645bea1e6e0 100644 --- a/src/sage/combinat/species/composition_species.py +++ b/src/sage/combinat/species/composition_species.py @@ -238,7 +238,7 @@ def _cis(self, series_ring, base_ring): sage: S.isotype_generating_series().coefficients(5) #indirect [1, t, t^2 + t, t^3 + t^2 + t, t^4 + t^3 + 2*t^2 + t] - We do the same thing with set partitions weighed by the number of + We do the same thing with set partitions weighted by the number of blocks. :: diff --git a/src/sage/dynamics/arithmetic_dynamics/product_projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/product_projective_ds.py index 830ccfdbd34..99b23158b9a 100644 --- a/src/sage/dynamics/arithmetic_dynamics/product_projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/product_projective_ds.py @@ -167,7 +167,7 @@ def nth_iterate(self, P, n, normalize=False): def orbit(self, P, N, **kwds): r""" - Return the orbit of `P` by this dynamcial system. + Return the orbit of `P` by this dynamical system. Let `F` be this dynamical system. If `N` is an integer return `[P,F(P),\ldots,F^N(P)]`. diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index d6df92ceeab..4c643b4c3cf 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -1029,7 +1029,7 @@ def nth_iterate(self, P, n, **kwds): def degree_sequence(self, iterates=2): r""" Return sequence of degrees of normalized iterates starting with - the degree of this dynamcial system. + the degree of this dynamical system. INPUT: ``iterates`` -- (default: 2) positive integer @@ -2701,7 +2701,7 @@ def critical_subscheme(self): def critical_points(self, R=None): r""" - Return the critical points of this dynamcial system defined over + Return the critical points of this dynamical system defined over the ring ``R`` or the base ring of this map. Must be dimension 1. @@ -3539,7 +3539,7 @@ def sigma_invariants(self, n, formal=False, embedding=None, type='point'): #evaluate the resultant fix_poly = psi(fix_poly) res = fix_poly.resultant(mult_poly, S.gen(0)) - #take infinty into consideration + #take infinity into consideration if inf_per.divides(n): res *= (S.gen(1) - self.multiplier(inf, n)[0,0])**e_inf res = res.univariate_polynomial() @@ -3569,7 +3569,7 @@ def sigma_invariants(self, n, formal=False, embedding=None, type='point'): #evaluate the resultant fix_poly = psi(fix_poly) res = fix_poly.resultant(mult_poly, S.gen(0)) - #take infinty into consideration + #take infinity into consideration if inf_per.divides(n): res *= (S.gen(1) - self.multiplier(inf, n)[0,0])**e_inf res = res.univariate_polynomial() @@ -4851,7 +4851,7 @@ def conjugating_set(self, other): def is_conjugate(self, other): r""" - Return whether or not two dynamcial systems are conjugate. + Return whether or not two dynamical systems are conjugate. ALGORITHM: @@ -4971,7 +4971,7 @@ def is_conjugate(self, other): def is_polynomial(self): r""" - Check to see if the dynamcial system has a totally ramified + Check to see if the dynamical system has a totally ramified fixed point. The function must be defined over an absolute number field or a diff --git a/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py b/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py index 83842fc5c86..0bd5a7af2a3 100644 --- a/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py +++ b/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py @@ -2189,7 +2189,7 @@ def nth_iterate_phi(self, P, n, **kwds): n = ZZ(n) except TypeError: raise TypeError("iterate number must be an integer") - #Since phi and psi and inveerses and automorphism + #Since phi and psi are inverses and automorphisms if n < 0: return(self.nth_iterate_psi(P, abs(n), **kwds)) if n == 0: diff --git a/src/sage/dynamics/complex_dynamics/mandel_julia.py b/src/sage/dynamics/complex_dynamics/mandel_julia.py index c04270625e4..1299b9cde24 100644 --- a/src/sage/dynamics/complex_dynamics/mandel_julia.py +++ b/src/sage/dynamics/complex_dynamics/mandel_julia.py @@ -253,7 +253,7 @@ def external_ray(theta, **kwds): if type(theta) != list: theta = [theta] - # Check if theta is in the invterval [0,1] + # Check if theta is in the interval [0,1] for angle in theta: if angle < 0 or angle > 1: raise \ diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py index 1ea867ad453..3b5c8d17295 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py @@ -1552,8 +1552,8 @@ def angle(self, other): # UHP @staticmethod def _get_B(a): r""" - Helper function to get an appropiate matrix transforming - (0,1,inf)->(0,I,inf) based on the type of a + Helper function to get an appropriate matrix transforming + (0,1,inf) -> (0,I,inf) based on the type of a INPUT: diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 3f47e629064..a1a25faf8e1 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6120,7 +6120,7 @@ def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, sage: A = L.affine_hull(orthonormal=True) Traceback (most recent call last): ... - ValueError: The base ring needs to be extented; try with "extend=True" + ValueError: The base ring needs to be extended; try with "extend=True" sage: A = L.affine_hull(orthonormal=True, extend=True); A A 1-dimensional polyhedron in AA^1 defined as the convex hull of 2 vertices sage: A.vertices() @@ -6185,7 +6185,7 @@ def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, sage: A = P.affine_hull(orthonormal=True) Traceback (most recent call last): ... - ValueError: The base ring needs to be extented; try with "extend=True" + ValueError: The base ring needs to be extended; try with "extend=True" sage: A = P.affine_hull(orthonormal=True, extend=True); A A 1-dimensional polyhedron in AA^1 defined as the convex hull of 2 vertices sage: A.vertices() @@ -6340,7 +6340,7 @@ def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, A = M.gram_schmidt(orthonormal=orthonormal)[0] except TypeError: if not extend: - raise ValueError('The base ring needs to be extented; try with "extend=True"') + raise ValueError('The base ring needs to be extended; try with "extend=True"') M = matrix(AA, M) A = M.gram_schmidt(orthonormal=orthonormal)[0] if as_affine_map: diff --git a/src/sage/graphs/hypergraph_generators.py b/src/sage/graphs/hypergraph_generators.py index 658c5c0bb90..1f07c8f23fc 100644 --- a/src/sage/graphs/hypergraph_generators.py +++ b/src/sage/graphs/hypergraph_generators.py @@ -39,11 +39,11 @@ def nauty(self, number_of_sets, number_of_vertices, - ``regular`` (integer) -- if set to an integer value `k`, requires the hypergraphs to be `k`-regular. It is actually a shortcut for the - corresponing min/max values. + corresponding min/max values. - ``uniform`` (integer) -- if set to an integer value `k`, requires the hypergraphs to be `k`-uniform. It is actually a shortcut for the - corresponing min/max values. + corresponding min/max values. - ``max_intersection`` (integer) -- constraints the maximum cardinality of the intersection of two sets fro the hypergraphs. Set to ``None`` diff --git a/src/sage/graphs/line_graph.py b/src/sage/graphs/line_graph.py index 75378e2929e..8e7f57253d2 100644 --- a/src/sage/graphs/line_graph.py +++ b/src/sage/graphs/line_graph.py @@ -574,13 +574,14 @@ def root_graph(g, verbose = False): print("Added clique", S) # Deal with even triangles - for u,v,w in even_triangles: - - # According to Beineke, we must go through all even triangles, and for - # each triangle uvw consider its three pairs of adjacent verties uv, vw, - # wu. For all pairs xy among those such that xy do not appear together - # in any clique we have found so far, we add xy to the list of cliques - # describing our covering. + for u, v, w in even_triangles: + + # According to Beineke, we must go through all even triangles, + # and for each triangle uvw consider its three pairs of + # adjacent vertices uv, vw, wu. For all pairs xy among those + # such that xy do not appear together in any clique we have + # found so far, we add xy to the list of cliques describing + # our covering. for x,y in [(u,v), (v,w), (w,u)]: diff --git a/src/sage/graphs/modular_decomposition.py b/src/sage/graphs/modular_decomposition.py index 021ce5d9802..0f03204a401 100644 --- a/src/sage/graphs/modular_decomposition.py +++ b/src/sage/graphs/modular_decomposition.py @@ -2622,7 +2622,7 @@ def form_module(index, other_index, tree_root, graph): OUTPUT: ``[module_formed, vertices]`` where ``module_formed`` is ``True`` if - module is formed else ``False`` and ``vertices`` is a list of verices + module is formed else ``False`` and ``vertices`` is a list of vertices included in the formed module EXAMPLES:: diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index 7ca7df76c47..a24310d46d7 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -1901,7 +1901,7 @@ def TL_basis_with_drain(self, drain_size): [3, 2, 1, 2, 1, 0], [3, 2, 1, 0, 1, 0]] - The number of basis elements hopefully correponds to the general + The number of basis elements hopefully corresponds to the general formula for the dimension of the representation spaces:: sage: B = BraidGroup(10) diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx index 72d1d97e1ce..b2ead61682d 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx @@ -1403,7 +1403,7 @@ def generate_dense_graphs_edge_addition(int n, bint loops, G = None, depth = Non # Dense graphs: adding vertices # This implements an augmentation scheme as follows: -# * Seed objects are graphs with one verticex and no edges. +# * Seed objects are graphs with one vertex and no edges. # * Augmentations consist of adding a single vertex connected to some subset of # the previous vertices. diff --git a/src/sage/interfaces/tachyon.py b/src/sage/interfaces/tachyon.py index bb1cfbf03ef..a3ce76bb54e 100644 --- a/src/sage/interfaces/tachyon.py +++ b/src/sage/interfaces/tachyon.py @@ -510,7 +510,7 @@ def help(self, use_pager=True): \subsubsection{Smoothed Triangles} Smoothed triangles are just like regular triangles, except that the - surface normal for each of the three vertexes is used to determine the + surface normal for each of the three vertices is used to determine the surface normal across the triangle by linear interpolation. Smoothed triangles yield curved looking objects and have nice reflections. diff --git a/src/sage/manifolds/chart_func.py b/src/sage/manifolds/chart_func.py index c39450cdeb5..18e92da6c3e 100644 --- a/src/sage/manifolds/chart_func.py +++ b/src/sage/manifolds/chart_func.py @@ -1964,7 +1964,7 @@ def arccosh(self): OUTPUT: - - chart function `\mathrm{arcosh}(f)`, where `f` is the current + - chart function `\mathrm{arccosh}(f)`, where `f` is the current chart function EXAMPLES:: @@ -2007,7 +2007,7 @@ def arcsinh(self): OUTPUT: - - chart function `\mathrm{arsinh}(f)`, where `f` is the current + - chart function `\mathrm{arcsinh}(f)`, where `f` is the current chart function EXAMPLES:: @@ -2050,7 +2050,7 @@ def arctanh(self): OUTPUT: - - chart function `\mathrm{artanh}(f)`, where `f` is the + - chart function `\mathrm{arctanh}(f)`, where `f` is the current chart function EXAMPLES:: diff --git a/src/sage/manifolds/differentiable/manifold_homset.py b/src/sage/manifolds/differentiable/manifold_homset.py index 4a85bb8e9a4..0ead19a7f97 100644 --- a/src/sage/manifolds/differentiable/manifold_homset.py +++ b/src/sage/manifolds/differentiable/manifold_homset.py @@ -492,7 +492,7 @@ def _an_element_(self): target_point = chart2.domain().an_element() target_coord = list(target_point.coord(chart2)) bounds = chart2._bounds[0] # bounds of first coordinate - # Determination of an interval (x1, x2) arround target_point: + # Determination of an interval (x1, x2) around target_point: xmin = bounds[0][0] xmax = bounds[1][0] one_half = QQ(1) / QQ(2) @@ -1411,7 +1411,7 @@ def _an_element_(self): nab.set_coef()[i0,i0,i0+1] = 1 y_bounds = chart2._bounds[1] # bounds of second coordinate - # Determination of an interval (y_A, y_B) arround target_point: + # Determination of an interval (y_A, y_B) around target_point: y_min = y_bounds[0][0] y_max = y_bounds[1][0] one_half = QQ(1) / QQ(2) diff --git a/src/sage/manifolds/differentiable/real_line.py b/src/sage/manifolds/differentiable/real_line.py index dd5b37fe524..0c7c2c4d4ab 100644 --- a/src/sage/manifolds/differentiable/real_line.py +++ b/src/sage/manifolds/differentiable/real_line.py @@ -294,7 +294,7 @@ def __init__(self, lower, upper, ambient=None, name=None, latex_name=None, coordinate=None, names=None, start_index=0): r""" - Construct an open inverval. + Construct an open interval. TESTS:: diff --git a/src/sage/manifolds/scalarfield.py b/src/sage/manifolds/scalarfield.py index 37956eafbe0..7d7a5af68bf 100644 --- a/src/sage/manifolds/scalarfield.py +++ b/src/sage/manifolds/scalarfield.py @@ -3112,7 +3112,7 @@ def arccosh(self): OUTPUT: - - the scalar field `\mathrm{arcosh}\, f`, where `f` is the current + - the scalar field `\mathrm{arccosh}\, f`, where `f` is the current scalar field EXAMPLES:: @@ -3123,7 +3123,7 @@ def arccosh(self): sage: g = arccosh(f) ; g Scalar field arccosh(f) on the 2-dimensional topological manifold M sage: latex(g) - \,\mathrm{arcosh}\left(\Phi\right) + \,\mathrm{arccosh}\left(\Phi\right) sage: g.display() arccosh(f): M --> R (x, y) |--> arccosh(x*y) @@ -3143,7 +3143,7 @@ def arccosh(self): True """ - name, latex_name = self._function_name("arccosh", r"\,\mathrm{arcosh}") + name, latex_name = self._function_name("arccosh", r"\,\mathrm{arccosh}") resu = type(self)(self.parent(), name=name, latex_name=latex_name) for chart, func in self._express.items(): resu._express[chart] = func.arccosh() @@ -3155,7 +3155,7 @@ def arcsinh(self): OUTPUT: - - the scalar field `\mathrm{arsinh}\, f`, where `f` is the current + - the scalar field `\mathrm{arcsinh}\, f`, where `f` is the current scalar field EXAMPLES:: @@ -3166,7 +3166,7 @@ def arcsinh(self): sage: g = arcsinh(f) ; g Scalar field arcsinh(f) on the 2-dimensional topological manifold M sage: latex(g) - \,\mathrm{arsinh}\left(\Phi\right) + \,\mathrm{arcsinh}\left(\Phi\right) sage: g.display() arcsinh(f): M --> R (x, y) |--> arcsinh(x*y) @@ -3186,7 +3186,7 @@ def arcsinh(self): True """ - name, latex_name = self._function_name("arcsinh", r"\,\mathrm{arsinh}") + name, latex_name = self._function_name("arcsinh", r"\,\mathrm{arcsinh}") resu = type(self)(self.parent(), name=name, latex_name=latex_name) for chart, func in self._express.items(): resu._express[chart] = func.arcsinh() @@ -3198,7 +3198,7 @@ def arctanh(self): OUTPUT: - - the scalar field `\mathrm{artanh}\, f`, where `f` is the current + - the scalar field `\mathrm{arctanh}\, f`, where `f` is the current scalar field EXAMPLES:: @@ -3209,7 +3209,7 @@ def arctanh(self): sage: g = arctanh(f) ; g Scalar field arctanh(f) on the 2-dimensional topological manifold M sage: latex(g) - \,\mathrm{artanh}\left(\Phi\right) + \,\mathrm{arctanh}\left(\Phi\right) sage: g.display() arctanh(f): M --> R (x, y) |--> arctanh(x*y) @@ -3231,7 +3231,7 @@ def arctanh(self): True """ - name, latex_name = self._function_name("arctanh", r"\,\mathrm{artanh}") + name, latex_name = self._function_name("arctanh", r"\,\mathrm{arctanh}") resu = type(self)(self.parent(), name=name, latex_name=latex_name) for chart, func in self._express.items(): resu._express[chart] = func.arctanh() diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 7dd03cd1c14..2bbab6e2e97 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -8002,7 +8002,7 @@ cdef class Matrix(Matrix1): ``None``. A new subdivision is created between ``top`` and ``bottom`` for ``self``. If possible, column subdivisions are - preserved in ``self``, but if the two sets of solumn subdivisions + preserved in ``self``, but if the two sets of column subdivisions are incompatible, they are removed. EXAMPLES:: diff --git a/src/sage/matrix/matrix_double_dense.pyx b/src/sage/matrix/matrix_double_dense.pyx index ff911f4e936..398d5dbdf5c 100644 --- a/src/sage/matrix/matrix_double_dense.pyx +++ b/src/sage/matrix/matrix_double_dense.pyx @@ -2685,7 +2685,7 @@ cdef class Matrix_double_dense(Matrix_dense): ALGORITHMS: - The naive algorithm simply compares corresponing entries on either + The naive algorithm simply compares corresponding entries on either side of the diagonal (and on the diagonal itself) to see if they are conjugates, with equality controlled by the tolerance parameter. diff --git a/src/sage/misc/converting_dict.py b/src/sage/misc/converting_dict.py index 78338478bf3..a7260153469 100644 --- a/src/sage/misc/converting_dict.py +++ b/src/sage/misc/converting_dict.py @@ -54,7 +54,7 @@ class KeyConvertingDict(dict): r""" - A dictionary which automatically applys a conversions to its keys. + A dictionary which automatically applies a conversions to its keys. The most common application is the case where the conversion function is the object representing some category, so that key diff --git a/src/sage/modular/modform/l_series_gross_zagier_coeffs.pyx b/src/sage/modular/modform/l_series_gross_zagier_coeffs.pyx index a104f19b21e..25d36c8fd52 100644 --- a/src/sage/modular/modform/l_series_gross_zagier_coeffs.pyx +++ b/src/sage/modular/modform/l_series_gross_zagier_coeffs.pyx @@ -41,7 +41,7 @@ def bqf_theta_series(Q, long bound, var=None): .. MATH:: - \sum_{(x,y) \in \Z^2} q^{f(x,y)} = \sum_{n=-infty}^{\infy} r(n)q^n + \sum_{(x,y) \in \Z^2} q^{f(x,y)} = \sum_{n=-\infty}^{\infty} r(n)q^n where `r(n)` give the number of way `n` is represented by `f`. diff --git a/src/sage/modular/modform_hecketriangle/abstract_space.py b/src/sage/modular/modform_hecketriangle/abstract_space.py index 4d3e29d19bc..08c5b0109ca 100644 --- a/src/sage/modular/modform_hecketriangle/abstract_space.py +++ b/src/sage/modular/modform_hecketriangle/abstract_space.py @@ -1155,7 +1155,7 @@ def faber_pol(self, m, order_1=ZZ(0), fix_d = False, d_num_prec = None): def F_basis_pol(self, m, order_1=ZZ(0)): r""" Returns a polynomial corresponding to the basis element of - the correponding space of weakly holomorphic forms of + the corresponding space of weakly holomorphic forms of the same degree as ``self``. The basis element is determined by the property that the Fourier expansion is of the form ``q^m + O(q^(order_inf + 1))``, where ``order_inf = self._l1 - order_1``. diff --git a/src/sage/modular/modform_hecketriangle/functors.py b/src/sage/modular/modform_hecketriangle/functors.py index cbae4f3bf96..735a30f0530 100644 --- a/src/sage/modular/modform_hecketriangle/functors.py +++ b/src/sage/modular/modform_hecketriangle/functors.py @@ -46,7 +46,7 @@ def _get_base_ring(ring, var_name="d"): Otherwise return ``ring``. - The base ring is used in the construction of the correponding + The base ring is used in the construction of the corresponding ``FormsRing`` or ``FormsSpace``. In particular in the construction of holomorphic forms of degree (0, 1). For (binary) operations a general ring element is considered (coerced to) diff --git a/src/sage/modular/modform_hecketriangle/readme.py b/src/sage/modular/modform_hecketriangle/readme.py index c3df334fa6d..e182b07d884 100644 --- a/src/sage/modular/modform_hecketriangle/readme.py +++ b/src/sage/modular/modform_hecketriangle/readme.py @@ -248,7 +248,7 @@ Note that for hyperbolic (and parabolic) fixed points there is a 1-1 correspondence with primitive hyperbolic/parabolic group elements (at least if ``n < infinity``). The group action on - fixed points resp. on matrices is compatible with this correpondence. + fixed points resp. on matrices is compatible with this correspondence. EXAMPLES:: diff --git a/src/sage/modular/pollack_stevens/manin_map.py b/src/sage/modular/pollack_stevens/manin_map.py index bb9e223f204..9ae5af4c081 100644 --- a/src/sage/modular/pollack_stevens/manin_map.py +++ b/src/sage/modular/pollack_stevens/manin_map.py @@ -822,7 +822,7 @@ def hecke(self, ell, algorithm = 'prep'): def p_stabilize(self, p, alpha, V): r""" - Return the `p`-stablization of self to level `N*p` on which + Return the `p`-stabilization of self to level `N*p` on which `U_p` acts by `\alpha`. INPUT: diff --git a/src/sage/modular/pollack_stevens/modsym.py b/src/sage/modular/pollack_stevens/modsym.py index 9707f6e5a14..563f50c505a 100644 --- a/src/sage/modular/pollack_stevens/modsym.py +++ b/src/sage/modular/pollack_stevens/modsym.py @@ -951,7 +951,7 @@ def _find_alpha(self, p, k, M=None, ap=None, new_base_ring=None, ordinary=True, def p_stabilize(self, p=None, M=20, alpha=None, ap=None, new_base_ring=None, ordinary=True, check=True): r""" - Return the `p`-stablization of self to level `N p` on which `U_p` acts by `\alpha`. + Return the `p`-stabilization of self to level `N p` on which `U_p` acts by `\alpha`. Note that since `\alpha` is `p`-adic, the resulting symbol is just an approximation to the true `p`-stabilization diff --git a/src/sage/modular/pollack_stevens/space.py b/src/sage/modular/pollack_stevens/space.py index 64f9becee21..f2fe13ba5e5 100644 --- a/src/sage/modular/pollack_stevens/space.py +++ b/src/sage/modular/pollack_stevens/space.py @@ -769,7 +769,7 @@ def random_element(self, M=None): ## If k = 0, then t has total measure zero. However, this is not true when k != 0 ## (unlike Prop 5.1 of [PS1] this is not a lift of classical symbol). ## So instead we simply add (const)*mu_1 to some (non-torsion) v[j] to fix this - ## here since (mu_1 |_k ([a,b,c,d]-1))(trival char) = chi(a) k a^{k-1} c , + ## here since (mu_1 |_k ([a,b,c,d]-1))(trivial char) = chi(a) k a^{k-1} c , ## we take the constant to be minus the total measure of t divided by (chi(a) k a^{k-1} c) if k != 0: diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index e79308952d0..1a01fc8217f 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -2451,7 +2451,7 @@ def hom(self, im_gens, base_morphism=None): INPUT: - - ``im_gens`` -- exactly one element of some ring. It must be invertible and trascendental over + - ``im_gens`` -- exactly one element of some ring. It must be invertible and transcendental over the image of ``base_morphism``; this is not checked. - ``base_morphism`` -- a homomorphism from the base field into the other ring. If ``None``, try to use a coercion map. diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index f0cc57c1a32..0dd95693d5c 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -6103,7 +6103,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): def _xgcd(self, Integer n, bint minimal=0): r""" - Return the exteded gcd of ``self`` and ``n``. + Return the extended gcd of ``self`` and ``n``. INPUT: diff --git a/src/sage/rings/padics/padic_template_element.pxi b/src/sage/rings/padics/padic_template_element.pxi index 07e0729ca5d..13f06a13581 100644 --- a/src/sage/rings/padics/padic_template_element.pxi +++ b/src/sage/rings/padics/padic_template_element.pxi @@ -1142,7 +1142,7 @@ cdef class ExpansionIterable(object): Returns an iterator, based on a corresponding :class:`ExpansionIter`. If ``val_shift`` is positive, will first emit that many zeros - (of the approrpiate type: ``[]`` instead when the inertia degree + (of the appropriate type: ``[]`` instead when the inertia degree is larger than one. If ``val_shift`` is negative, will truncate that many terms at diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index dd420918180..90e764b08c4 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2441,7 +2441,7 @@ cdef class Polynomial(CommutativeAlgebraElement): We verify that :trac:`23020` has been resolved. (There are no elements in the Sage library yet that do not implement ``__nonzero__``, so we - have to create one artifically.):: + have to create one artificially.):: sage: class PatchedAlgebraicNumber(sage.rings.qqbar.AlgebraicNumber): ....: def __nonzero__(self): raise NotImplementedError() diff --git a/src/sage/schemes/toric/chow_group.py b/src/sage/schemes/toric/chow_group.py index 86ccf17b310..87541ee0872 100644 --- a/src/sage/schemes/toric/chow_group.py +++ b/src/sage/schemes/toric/chow_group.py @@ -152,7 +152,7 @@ class ChowCycle(FGP_Element): Do not construct :class:`ChowCycle` objects manually. Instead, use the parent :class:`ChowGroup` to obtain - generators or Chow cycles correspondig to cones of the fan. + generators or Chow cycles corresponding to cones of the fan. EXAMPLES:: diff --git a/src/sage/stats/distributions/discrete_gaussian_lattice.py b/src/sage/stats/distributions/discrete_gaussian_lattice.py index 0031f4fb0c8..8ed6a77cead 100644 --- a/src/sage/stats/distributions/discrete_gaussian_lattice.py +++ b/src/sage/stats/distributions/discrete_gaussian_lattice.py @@ -87,7 +87,7 @@ def _iter_vectors(n, lower, upper, step=None): """ if step is None: if ZZ(lower) >= ZZ(upper): - raise ValueError("Expected lower < uppper, but got %d >= %d" % (lower, upper)) + raise ValueError("Expected lower < upper, but got %d >= %d" % (lower, upper)) if ZZ(n) <= 0: raise ValueError("Expected n>0 but got %d <= 0" % n) step = n From bd39a1ff082cb5f0ceb2f9a93f736434e24597ab Mon Sep 17 00:00:00 2001 From: John Cremona Date: Sun, 28 Jan 2018 15:09:08 +0000 Subject: [PATCH 541/740] #21092: now works over function fields, extra examples included over finite and number fields --- .../schemes/elliptic_curves/constructor.py | 115 +++++++++++++++--- 1 file changed, 101 insertions(+), 14 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index 7297dacc75f..1d8aa42a749 100644 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -758,7 +758,7 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): the elliptic curve `E`. If the given point is a flex, this is a linear isomorphism. - .. note:: + .. NOTE:: The function :func:`~sage.schemes.elliptic_curves.jacobian.Jacobian` may be @@ -768,7 +768,7 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): is none: the plane cubic is only isomorphic to its Jacobian when it has a rational point. - .. note:: + .. NOTE:: When ``morphism=True``, a birational isomorphism between the curve `F=0` and the Weierstrass curve is returned. If the point @@ -866,7 +866,7 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): Defn: Defined on coordinates by sending (x : y : z) to (2527/17280*x^2 + 133/2160*x*y + 133/108000*y^2 + 133/2880*x*z + 931/18000*y*z - 3857/48000*z^2 : -6859/288*x^2 + 323/36*x*y + 359/1800*y^2 + 551/48*x*z + 2813/300*y*z + 24389/800*z^2 : -2352637/99532800000*x^2 - 2352637/124416000000*x*y - 2352637/622080000000*y^2 + 2352637/82944000000*x*z + 2352637/207360000000*y*z - 2352637/276480000000*z^2) - Note that the morphism returned cannor be evaluated directly at + Note that the morphism returned cannot be evaluated directly at the given point ``P=(1:-1:1)`` since the polynomials defining it all vanish there:: @@ -876,7 +876,7 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): ValueError: [0, 0, 0] does not define a valid point since all entries are 0 Using the group law on the codomain elliptic curve, which has rank - 1 and full 2-torsion, anf the inverse morphism, we can find many + 1 and full 2-torsion, and the inverse morphism, we can find many points on the cubic. First we find the preimages of multiples of the generator:: @@ -951,7 +951,7 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): It is possible to not provide a base point ``P`` provided that the cubic has a rational flex. In this case the flexes will be found - and one will beused as a base point:: + and one will be used as a base point:: sage: R. = QQ[] sage: cubic = x^3+y^3+z^3 @@ -963,7 +963,7 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): Defn: Defined on coordinates by sending (x : y : z) to (y : -3*x : -1/3*x - 1/3*z) - An error will be raised if not point is given and there are not rational flexes:: + An error will be raised if no point is given and there are no rational flexes:: sage: R. = QQ[] sage: cubic = 3*x^3+4*y^3+5*z^3 @@ -972,6 +972,51 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): ... ValueError: A point must be given when the cubic has no rational flexes + An example over a finite field, using a flex:: + + sage: K = GF(17) + sage: R. = K[] + sage: cubic = 2*x^3+3*y^3+4*z^3 + sage: EllipticCurve_from_cubic(cubic,[0,3,1]) + Scheme morphism: + From: Projective Plane Curve over Finite Field of size 17 defined by 2*x^3 + 3*y^3 + 4*z^3 + To: Elliptic Curve defined by y^2 + 16*y = x^3 + 11 over Finite Field of size 17 + Defn: Defined on coordinates by sending (x : y : z) to + (-x : 4*y : 4*y + 5*z) + + An example in characteristic 3:: + + sage: K = GF(3) + sage: R. = K[] + sage: cubic = x^3+y^3+z^3+x*y*z + sage: EllipticCurve_from_cubic(cubic,[0,1,-1]) + Scheme morphism: + From: Projective Plane Curve over Finite Field of size 3 defined by x^3 + y^3 + x*y*z + z^3 + To: Elliptic Curve defined by y^2 + x*y = x^3 + 1 over Finite Field of size 3 + Defn: Defined on coordinates by sending (x : y : z) to + (y + z : -y : x) + + An example over a number field, using a non-flex and where there are no rational flexes:: + + sage: K. = QuadraticField(-3) + sage: R. = K[] + sage: cubic = 2*x^3+3*y^3+5*z^3 + sage: EllipticCurve_from_cubic(cubic,[1,1,-1]) + Scheme morphism: + From: Projective Plane Curve over Number Field in a with defining polynomial x^2 + 3 defined by 2*x^3 + 3*y^3 + 5*z^3 + To: Elliptic Curve defined by y^2 + 1754460/2053*x*y + 5226454388736000/8653002877*y = x^3 + (-652253285700/4214809)*x^2 over Number Field in a with defining polynomial x^2 + 3 + Defn: Defined on coordinates by sending (x : y : z) to + (-16424/127575*x^2 - 231989/680400*x*y - 14371/64800*y^2 - 26689/81648*x*z - 10265/27216*y*z - 2053/163296*z^2 : 24496/315*x^2 + 119243/840*x*y + 4837/80*y^2 + 67259/504*x*z + 25507/168*y*z + 5135/1008*z^2 : 8653002877/2099914709760000*x^2 + 8653002877/699971569920000*x*y + 8653002877/933295426560000*y^2 + 8653002877/419982941952000*x*z + 8653002877/279988627968000*y*z + 8653002877/335986353561600*z^2) + + An example over a function field, using a non-flex:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: cubic = x^3+t*y^3+(1+t)*z^3 + sage: EllipticCurve_from_cubic(cubic,[1,1,-1], morphism=False) + Elliptic Curve defined by y^2 + ((-236196*t^6-708588*t^5-1180980*t^4-1180980*t^3-708588*t^2-236196*t)/(-1458*t^6-17496*t^5+4374*t^4+29160*t^3+4374*t^2-17496*t-1458))*x*y + ((-459165024*t^14-5969145312*t^13-34207794288*t^12-113872925952*t^11-244304490582*t^10-354331909458*t^9-354331909458*t^8-244304490582*t^7-113872925952*t^6-34207794288*t^5-5969145312*t^4-459165024*t^3)/(-1458*t^14-58320*t^13-841266*t^12-5137992*t^11-11773350*t^10-7709904*t^9+12627738*t^8+25789104*t^7+12627738*t^6-7709904*t^5-11773350*t^4-5137992*t^3-841266*t^2-58320*t-1458))*y = x^3 + ((-118098*t^12-708588*t^11+944784*t^10+11219310*t^9+27871128*t^8+36374184*t^7+27871128*t^6+11219310*t^5+944784*t^4-708588*t^3-118098*t^2)/(-54*t^12-1296*t^11-7452*t^10+6048*t^9+25758*t^8-3888*t^7-38232*t^6-3888*t^5+25758*t^4+6048*t^3-7452*t^2-1296*t-54))*x^2 over Rational function field in t over Rational Field + + TESTS: Here is a test for :trac:`21092`:: @@ -1027,7 +1072,7 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): if flex_point is not None: # first case: base point is a flex P = flex_point - L = C.tangents(P)[0] + L = tangent_at_smooth_point(C,P) dx, dy, dz = [L.coefficient(v) for v in R.gens()] # find an invertible matrix M such that (0,1,0)M=P and @@ -1074,11 +1119,11 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): else: # Second case: no flexes if not P: raise ValueError('A point must be given when the cubic has no rational flexes') - L = C.tangents(P)[0] + L = tangent_at_smooth_point(C,P) Qlist = [Q for Q in C.intersection(Curve(L)).rational_points() if C(Q)!=CP] # assert Qlist P2 = C(Qlist[0]) - L2 = C.tangents(P2)[0] + L2 = tangent_at_smooth_point(C,P2) Qlist = [Q for Q in C.intersection(Curve(L2)).rational_points() if C(Q)!=P2] # assert Qlist P3 = C(Qlist[0]) @@ -1138,9 +1183,48 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): C, E, fwd_defining_poly, fwd_post, inv_defining_poly, inv_post) -def chord_and_tangent(F, P): +def tangent_at_smooth_point(C,P): + """Return the tangent at the smooth point `P` of projective curve `C`. + + INPUT: + + - ``C`` -- a projective plane curve. + + - ``P`` -- a 3-tuple `(x,y,z)` defining a projective point on `C`. + + OUTPUT: + + The linear form defining the tangent at `P` to `C`. + + EXAMPLES:: + + sage: R. = QQ[] + sage: from sage.schemes.elliptic_curves.constructor import tangent_at_smooth_point + sage: C = Curve(x^3+y^3+60*z^3) + sage: tangent_at_smooth_point(C, [1,-1,0]) + x + y + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: C = Curve(x^3+2*y^3+3*z^3) + sage: from sage.schemes.elliptic_curves.constructor import tangent_at_smooth_point + sage: tangent_at_smooth_point(C,[1,1,-1]) + 3*x + 6*y + 9*z """ - Return the third point of intersection of a cubic with the tangent at one point. + # Over function fields such as QQ(t) an error is raised with the + # default (factor=True). Note that factor=False returns the + # product of the tangents in case of a multiple point, while here + # `P` is assumed smooth so factorization is unnecessary, but over + # QQ (for example) including the factorization gives better + # results, for example returning x+y instead of 3x+3y in the + # doctest. + try: + return C.tangents(P)[0] + except NotImplementedError: + return C.tangents(P,factor=False)[0] + +def chord_and_tangent(F, P): + """Return the third point of intersection of a cubic with the tangent at one point. INPUT: @@ -1153,7 +1237,9 @@ def chord_and_tangent(F, P): OUTPUT: - A point ``Q`` such that ``F(Q)=0``, namely the third point of intersection of the tangent at ``P`` with the curve ``F=0``, so ``Q=P`` if and only if ``P`` is a flex. + A point ``Q`` such that ``F(Q)=0``, namely the third point of + intersection of the tangent at ``P`` with the curve ``F=0``, so + ``Q=P`` if and only if ``P`` is a flex. EXAMPLES:: @@ -1184,6 +1270,7 @@ def chord_and_tangent(F, P): sage: F = x**3 - 4*x**2*y - 65*x*y**2 + 3*x*y*z - 76*y*z**2 sage: chord_and_tangent(F, [0, 1, 0]) (0 : 0 : 1) + """ from sage.schemes.curves.constructor import Curve # check the input @@ -1204,7 +1291,7 @@ def chord_and_tangent(F, P): except (TypeError, ValueError): raise TypeError('{} does not define a point on a projective curve over {} defined by {}'.format(P,K,F)) - L = Curve(C.tangents(P)[0]) + L = Curve(tangent_at_smooth_point(C,P)) Qlist = [Q for Q in C.intersection(L).rational_points() if Q!=P] if Qlist: return Qlist[0] @@ -1307,7 +1394,7 @@ def EllipticCurves_with_good_reduction_outside_S(S=[], proof=None, verbose=False - ``verbose`` - True/False (default False): if True, some details of the computation will be output. - .. note:: + .. NOTE:: Proof flag: The algorithm used requires determining all S-integral points on several auxiliary curves, which in turn From e6237fbfe56870290b8524458d2740ee85b2668a Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 29 Jan 2018 00:11:25 +0000 Subject: [PATCH 542/740] update iconv to 1.15 --- build/pkgs/iconv/checksums.ini | 8 ++++---- build/pkgs/iconv/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/pkgs/iconv/checksums.ini b/build/pkgs/iconv/checksums.ini index eff7ae3bfa6..d45d8b0fb87 100644 --- a/build/pkgs/iconv/checksums.ini +++ b/build/pkgs/iconv/checksums.ini @@ -1,4 +1,4 @@ -tarball=iconv-VERSION.tar.bz2 -sha1=5b5e732cd1eaa01bcfa2b47903ce6ea041a0fae3 -md5=cd09c5497b1eeac5f560768081c46654 -cksum=1041145550 +tarball=libiconv-VERSION.tar.gz +sha1=7af3149fa7c4bd1d3a36a30f02e95a0ebfd6b18f +md5=ace8b5f2db42f7b3b3057585e80d9808 +cksum=538361871 diff --git a/build/pkgs/iconv/package-version.txt b/build/pkgs/iconv/package-version.txt index 328f4a15495..d40acaaea09 100644 --- a/build/pkgs/iconv/package-version.txt +++ b/build/pkgs/iconv/package-version.txt @@ -1 +1 @@ -1.14.p0 +1.15 From 6d80f28933716117c1671e96c3eb3b3928ed8630 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 29 Jan 2018 10:10:31 +0100 Subject: [PATCH 543/740] Clean up in maxima spkg-install --- build/pkgs/maxima/package-version.txt | 2 +- build/pkgs/maxima/spkg-install | 17 +++-------------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/build/pkgs/maxima/package-version.txt b/build/pkgs/maxima/package-version.txt index 81b86f55f6b..326e0939f62 100644 --- a/build/pkgs/maxima/package-version.txt +++ b/build/pkgs/maxima/package-version.txt @@ -1 +1 @@ -5.39.0.p1 +5.39.0.p2 diff --git a/build/pkgs/maxima/spkg-install b/build/pkgs/maxima/spkg-install index 045ac5b6a05..9c0e5ed4677 100644 --- a/build/pkgs/maxima/spkg-install +++ b/build/pkgs/maxima/spkg-install @@ -12,8 +12,6 @@ exec Date: Mon, 29 Jan 2018 15:44:23 +0100 Subject: [PATCH 544/740] Check for broken g++ in $SAGE_LOCAL --- configure.ac | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/configure.ac b/configure.ac index fbe94eb4ff7..bb9fb26788d 100644 --- a/configure.ac +++ b/configure.ac @@ -361,6 +361,19 @@ need_to_install_gfortran=no if test -f "$SAGE_LOCAL/bin/gcc"; then # Special value for SAGE_INSTALL_GCC if GCC is already installed SAGE_INSTALL_GCC=exists + + # Check whether it actually works... + # See https://trac.sagemath.org/ticket/24599 + if test -x "$SAGE_LOCAL/bin/g++"; then + echo '#include ' >conftest.cpp + echo 'auto inf = 1.0 / std::complex();' >>conftest.cpp + + if ! bash -c "source '$SAGE_LOCAL/bin/sage-env' && g++ -O3 -c -o conftest.o conftest.cpp"; then + SAGE_INSTALL_GCC=yes + SAGE_MUST_INSTALL_GCC([installed g++ is broken]) + fi + rm -f conftest.* + fi elif test -n "$SAGE_INSTALL_GCC"; then # Check the value of the environment variable SAGE_INSTALL_GCC case "$SAGE_INSTALL_GCC" in From 821f7d9f3568316bc0b8b1f5619bcef46cd3bfe1 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Mon, 29 Jan 2018 16:37:50 +0100 Subject: [PATCH 545/740] 24418: Doctest --- src/sage/symbolic/expression.pyx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 7130898086c..aa04ae4a75b 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -5815,6 +5815,11 @@ cdef class Expression(CommutativeRingElement): 162.000000000000 sage: (ex+1).n() 163.000000000000 + + Check if :trac:`24418` is fixed:: + + sage: numerical_approx(2^(450232897/4888643760)) + 1.06591892580915 """ if prec is None: prec = digits_to_bits(digits) From 8820e7b2e6cac3b56f2bc5b3d334057eee583373 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 29 Jan 2018 16:51:16 +0100 Subject: [PATCH 546/740] Remove deprecated PowerSeries._floordiv_ --- src/sage/rings/power_series_ring_element.pyx | 26 -------------------- 1 file changed, 26 deletions(-) diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index c961aa5168a..8f9269853a5 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -1035,32 +1035,6 @@ cdef class PowerSeries(AlgebraElement): num = self return num*inv - cpdef _floordiv_(self, denom): - """ - Euclidean division (over fields) or ordinary division (over - other rings; deprecated). - - EXAMPLES:: - - sage: A. = GF(7)[[]] - sage: (q^2 - 1) // (q + 1) - doctest:...: UserWarning: the operator // now returns the Euclidean quotient for power series over fields, use / for the true quotient - 6 + q + O(q^20) - - sage: R. = ZZ[[]] - sage: (t**10 - 1) // (1 + t + t^7) - doctest:...: DeprecationWarning: the operator // is deprecated for power series over non-fields, use / instead - See http://trac.sagemath.org/20062 for details. - -1 + t - t^2 + t^3 - t^4 + t^5 - t^6 + 2*t^7 - 3*t^8 + 4*t^9 - 4*t^10 + 5*t^11 - 6*t^12 + 7*t^13 - 9*t^14 + 12*t^15 - 16*t^16 + 20*t^17 - 25*t^18 + 31*t^19 + O(t^20) - """ - try: - q, r = self.quo_rem(denom) - warn("the operator // now returns the Euclidean quotient for power series over fields, use / for the true quotient") - return q - except (AttributeError, NotImplementedError): - deprecation(20062, "the operator // is deprecated for power series over non-fields, use / instead") - return self._div_(denom) - def __mod__(self, other): """ EXAMPLES:: From c7b0dac707e329540d2d8983d8c103534c62dab9 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 29 Jan 2018 16:53:37 +0100 Subject: [PATCH 547/740] Libraries should be real dependencies --- build/pkgs/gcc/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/gcc/dependencies b/build/pkgs/gcc/dependencies index bd5a05742b6..d22d8d3c7c2 100644 --- a/build/pkgs/gcc/dependencies +++ b/build/pkgs/gcc/dependencies @@ -1,4 +1,4 @@ -| $(MP_LIBRARY) mpfr mpc zlib xz +$(MP_LIBRARY) mpfr mpc zlib | xz ---------- All lines of this file are ignored except the first. From f65afd020ae53bccda63e19e76939dd844e161b4 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 29 Jan 2018 17:06:07 +0100 Subject: [PATCH 548/740] Change docstring formatting --- src/sage/rings/qqbar.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index f24754d4a95..79813c81c9e 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -5141,7 +5141,9 @@ class AlgebraicNumberPowQQAction(Action): real and `b` is odd, take the real `b`'th root; otherwise take the principal `b`'th root. - EXAMPLES in ``QQbar``:: + EXAMPLES: + + In ``QQbar``:: sage: QQbar(2)^(1/2) 1.414213562373095? @@ -5178,7 +5180,7 @@ class AlgebraicNumberPowQQAction(Action): sage: (QQbar.zeta(7)^6)^(1/3) * QQbar.zeta(21) 1.000000000000000? + 0.?e-17*I - EXAMPLES in ``AA``:: + In ``AA``:: sage: AA(2)^(1/2) 1.414213562373095? From 4715809a8aaf336a92775902e747a69bb1505a67 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 29 Jan 2018 15:55:30 +0100 Subject: [PATCH 549/740] Add abstract _add_ and _mul_ methods --- src/sage/rings/finite_rings/element_base.pxd | 3 +- src/sage/rings/finite_rings/element_base.pyx | 8 ----- src/sage/structure/element.pxd | 14 +++++---- src/sage/structure/element.pyx | 31 ++++++++++++++++++++ 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/sage/rings/finite_rings/element_base.pxd b/src/sage/rings/finite_rings/element_base.pxd index 8121c800535..94ca634d3a6 100644 --- a/src/sage/rings/finite_rings/element_base.pxd +++ b/src/sage/rings/finite_rings/element_base.pxd @@ -1,8 +1,7 @@ from sage.structure.element cimport CommutativeRingElement cdef class FiniteRingElement(CommutativeRingElement): - cpdef _add_(self, other) - cpdef _mul_(self, other) + pass cdef class FinitePolyExtElement(FiniteRingElement): pass diff --git a/src/sage/rings/finite_rings/element_base.pyx b/src/sage/rings/finite_rings/element_base.pyx index b6c40ddf3fc..757e9ad0e6e 100644 --- a/src/sage/rings/finite_rings/element_base.pyx +++ b/src/sage/rings/finite_rings/element_base.pyx @@ -100,14 +100,6 @@ cdef class FiniteRingElement(CommutativeRingElement): else: raise ValueError("unknown algorithm") - cpdef _add_(self, other): - """Abstract addition method""" - raise NotImplementedError - - cpdef _mul_(self, other): - """Abstract multiplication method""" - raise NotImplementedError - cdef class FinitePolyExtElement(FiniteRingElement): """ diff --git a/src/sage/structure/element.pxd b/src/sage/structure/element.pxd index 98fd1865cb9..0280b7e4cd8 100644 --- a/src/sage/structure/element.pxd +++ b/src/sage/structure/element.pxd @@ -204,7 +204,8 @@ cdef class ModuleElement(Element) # forward declaration cdef class RingElement(ModuleElement) # forward declaration cdef class ModuleElement(Element): - cpdef _sub_(self, right) + cpdef _add_(self, other) + cpdef _sub_(self, other) cpdef _neg_(self) # self._rmul_(x) is x * self @@ -216,13 +217,14 @@ cdef class MonoidElement(Element): cpdef _pow_int(self, n) cdef class MultiplicativeGroupElement(MonoidElement): - cpdef _div_(self, right) + cpdef _div_(self, other) cdef class AdditiveGroupElement(ModuleElement): pass cdef class RingElement(ModuleElement): - cpdef _div_(self, right) + cpdef _mul_(self, other) + cpdef _div_(self, other) cpdef _pow_int(self, n) cdef class CommutativeRingElement(RingElement): @@ -238,11 +240,11 @@ cdef class PrincipalIdealDomainElement(DedekindDomainElement): pass cdef class EuclideanDomainElement(PrincipalIdealDomainElement): - cpdef _floordiv_(self, right) - cpdef _mod_(self, right) + cpdef _floordiv_(self, other) + cpdef _mod_(self, other) cdef class FieldElement(CommutativeRingElement): - cpdef _floordiv_(self, right) + cpdef _floordiv_(self, other) cdef class AlgebraElement(RingElement): pass diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index 405080f1be5..2d6c17eb281 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -2337,10 +2337,26 @@ cdef class ElementWithCachedMethod(Element): self.__cached_methods = {name : attr} return attr + cdef class ModuleElement(Element): """ Generic element of a module. """ + cpdef _add_(self, other): + """ + Abstract addition method + + TESTS:: + + sage: from sage.structure.element import ModuleElement + sage: e = ModuleElement(Parent()) + sage: e + e + Traceback (most recent call last): + ... + NotImplementedError: addition not implemented for + """ + raise NotImplementedError(f"addition not implemented for {self._parent}") + cdef _add_long(self, long n): """ Generic path for adding a C long, assumed to commute. @@ -2526,6 +2542,21 @@ def is_RingElement(x): return isinstance(x, RingElement) cdef class RingElement(ModuleElement): + cpdef _mul_(self, other): + """ + Abstract multiplication method + + TESTS:: + + sage: from sage.structure.element import RingElement + sage: e = RingElement(Parent()) + sage: e * e + Traceback (most recent call last): + ... + NotImplementedError: multiplication not implemented for + """ + raise NotImplementedError(f"multiplication not implemented for {self._parent}") + def is_one(self): return self == self._parent.one() From 8c155c0e93c4dd1817245509be45695a2f426f7d Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 29 Jan 2018 11:55:34 -0600 Subject: [PATCH 550/740] Some further simplifications and tweaks. --- src/sage/combinat/shifted_primed_tableau.py | 146 ++++++++++---------- 1 file changed, 72 insertions(+), 74 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index 6f8b7d27f51..52fd8996cf6 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -49,19 +49,18 @@ @add_metaclass(InheritComparisonClasscallMetaclass) class ShiftedPrimedTableau(ClonableArray): r""" - A shifted primed tableau with primed elements stored as half-integers from - ``QQ``. + A shifted primed tableau. A primed tableau is a tableau of shifted shape in the alphabet `X' = \{1' < 1 < 2' < 2 < \cdots < n' < n\}` such that - 1. The entries are weakly increasing along rows and columns; - 2. A row cannot have two repeated primed elements, and a column - cannot have two repeated non-primed elements; - 3. There are only non-primed elements on the main diagonal. + 1. the entries are weakly increasing along rows and columns; + 2. a row cannot have two repeated primed elements, and a column + cannot have two repeated non-primed elements; + 3. there are only non-primed elements on the main diagonal. - Skew shape of the shifted primed tableaux is specified either with an optional - argument ``skew`` or with ``None`` entries. + Skew shape of the shifted primed tableaux is specified either + with an optional argument ``skew`` or with ``None`` entries. EXAMPLES:: @@ -107,10 +106,10 @@ def __classcall_private__(cls, T, skew=None): sage: s.parent() Shifted Primed Tableaux skewed by [2, 1] """ - if T == [] or T == [[]]: - return ShiftedPrimedTableaux(skew=skew)([]) - if (isinstance(T, cls) and T._skew == skew): + if isinstance(T, ShiftedPrimedTableau) and T._skew == skew: return T + if not T or T == [[]]: + return ShiftedPrimedTableaux(skew=skew)([]) try: entry = T[0][0] @@ -133,10 +132,9 @@ def __init__(self, parent, T, skew=None, check=True, preprocessed=False): TESTS:: - sage: s = ShiftedPrimedTableau([[1,"2'","3'",3],[2,"3'"]]) - sage: t = ShiftedPrimedTableaux([4,2])([[1,"2p","3p",3], - ....: [2,"3p"]]) - sage: s==t + sage: s = ShiftedPrimedTableau([[1,"2'","3'",3], [2,"3'"]]) + sage: t = ShiftedPrimedTableaux([4,2])([[1,"2p","3p",3], [2,"3p"]]) + sage: s == t True sage: t.parent() Shifted Primed Tableaux of shape [4, 2] @@ -144,13 +142,11 @@ def __init__(self, parent, T, skew=None, check=True, preprocessed=False): Shifted Primed Tableaux sage: r = ShiftedPrimedTableaux([4, 2])(s); r.parent() Shifted Primed Tableaux of shape [4, 2] - sage: s is t # identical shifted tableaux are distinct objects + sage: s is t # identical shifted tableaux are distinct objects False - A shifted primed tableau is shallowly immutable, the rows are - represented as tuples. - - :: + A shifted primed tableau is deeply immutable as the rows are + stored as tuples:: sage: t = ShiftedPrimedTableau([[1,"2p","3p",3],[2,"3p"]]) sage: t[0][1] = 3 @@ -207,8 +203,7 @@ def check(self): Shifted Primed Tableaux of shape [4, 2] """ if not self.parent()._contains_tableau_(self): - raise ValueError("{} is not an element of Shifted Primed Tableaux" - .format(self)) + raise ValueError("{} is not an element of Shifted Primed Tableaux".format(self)) def __eq__(self, other): """ @@ -230,12 +225,12 @@ def __eq__(self, other): True """ if isinstance(other, ShiftedPrimedTableau): - return (self._skew == other._skew and list(self) == list(other)) + return self._skew == other._skew and list(self) == list(other) try: Tab = ShiftedPrimedTableau(other) except (ValueError, TypeError): return False - return (self._skew == Tab._skew and list(self) == list(Tab)) + return self._skew == Tab._skew and list(self) == list(Tab) def __ne__(self, other): """ @@ -281,13 +276,13 @@ def _repr_list(self): sage: ShiftedPrimedTableau([['2p',3],[2,2]], skew=[2])._repr_list() "[(None, None, 2', 3), (2, 2)]" """ - return (repr([row for row in self])) + return repr([row for row in self]) def _repr_tab(self): """ Return a nested list of strings representing the elements. - TESTS:: + EXAMPLES:: sage: t = ShiftedPrimedTableau([[1,'2p',2,2],[2,'3p']]) sage: t._repr_tab() @@ -296,7 +291,7 @@ def _repr_tab(self): sage: s._repr_tab() [[' . ', ' . ', " 2'", ' 2 ', ' 3 '], [' . ', " 2'"]] """ - max_len = len(str(self.max_entry()))+2 + max_len = len(str(self.max_entry())) + 2 repr_tab = [] for row in self: repr_row = [] @@ -330,8 +325,8 @@ def _repr_diagram(self): . 2' """ max_len = len(str(self.max_entry()))+2 - return "\n".join([" "*max_len*i + "".join(_) - for i, _ in enumerate(self._repr_tab())]) + return "\n".join([" "*max_len*i + "".join(val) + for i, val in enumerate(self._repr_tab())]) _repr_compact = _repr_diagram @@ -449,7 +444,7 @@ def _ascii_art_table(self, unicode=False): h = '-' dl = dr = ul = ur = vl = uh = dh = vh = '+' - if self.shape() == []: + if not self.shape(): return dr + dl + '\n' + ur + ul # Get the widths of the columns @@ -521,8 +516,8 @@ def _latex_(self): return tex_from_array(L) def max_entry(self): - """ - Return the maximum entry in the primed tableaux ``self``, rounded up. + r""" + Return the minimum unprimed letter `x > y` for all `y` in ``self``. EXAMPLES:: @@ -530,17 +525,15 @@ def max_entry(self): sage: Tab.max_entry() 3 """ - if self == []: + if not self: return 0 - else: - flat = [entry.unprimed() for row in self - for entry in row if entry is not None] - return max(flat) + + return max(entry.unprimed() for row in self + for entry in row if entry is not None) def shape(self): - """ - Return the shape of the underlying partition of ``self`` in list - format. + r""" + Return the shape of the underlying partition of ``self``. EXAMPLES:: @@ -571,11 +564,10 @@ def weight(self): """ flat = [entry.integer() for row in self for entry in row if entry is not None] - if flat == []: - max_ind = 0 - else: - max_ind = max(flat) - weight = tuple([flat.count(i+1) for i in range(max_ind)]) + if not flat: + return () + + weight = tuple([flat.count(i+1) for i in range(max(flat))]) return weight @@ -623,7 +615,7 @@ def _reading_word_with_positions(self): sage: t = SPT([[1,'2p',2,2],[2,'3p']]) sage: t._reading_word_with_positions() [((1, 2), 3), ((0, 1), 2), ((1, 1), 2), ((0, 0), 1), - ((0, 2), 2), ((0, 3), 2)] + ((0, 2), 2), ((0, 3), 2)] """ mat = self._to_matrix() ndim, mdim = len(mat), len(mat[0]) @@ -706,9 +698,7 @@ def f(self, ind): """ T = self._to_matrix() - read_word = self._reading_word_with_positions() - read_word = [num - for num in read_word + read_word = [num for num in self._reading_word_with_positions() if num[1] == ind or num[1] == ind+1] element_to_change = None @@ -801,8 +791,7 @@ def e(self, ind): True """ T = self._to_matrix() - read_word = self._reading_word_with_positions() - read_word = [num for num in read_word + read_word = [num for num in self._reading_word_with_positions() if num[1] == ind or num[1] == ind+1] element_to_change = None @@ -917,6 +906,15 @@ class PrimedEntry(SageObject): An entry in a shifted primed tableau is an element in the alphabet `\{1' < 1 < 2' < 2 < \cdots < n' < n\}`. + Internally, we represent an unprimed element `x` as `2x` + and the primed elements as the corresponding odd integer + that respects the total order. + + INPUT: + + - ``entry`` -- a half integer or a string of an integer + possibly ending in ``p`` or ``'`` + - ``double`` -- the doubled value """ def __init__(self, entry=None, double=None): """ @@ -930,6 +928,8 @@ def __init__(self, entry=None, double=None): 2' sage: ShiftedPrimedTableau([[1,1.5]])[0][1] 2' + sage: ShiftedPrimedTableau([[1,3/2]])[0][1] + 2' """ # store primed numbers as odd, unprimed numbers as even integers if isinstance(entry, self.__class__): @@ -941,19 +941,20 @@ def __init__(self, entry=None, double=None): return if isinstance(entry, str): - if (entry[-1] == "'" or entry[-1] == "p") and entry[:-1].isdigit() is True: + if (entry[-1] == "'" or entry[-1] == "p") and entry[:-1].isdigit(): # Check if an element has "'" or "p" at the end self._entry = 2*Integer(entry[:-1]) - 1 - return + else: + self._entry = 2 * Integer(entry) + return + # FIXME: This should not happen! Something is not right. + if entry is None: + entry = 0 try: - entry = Rational(entry) + self._entry = Integer(2*entry) except (TypeError, ValueError): - raise ValueError("primed elements have wrong format") - if entry.denominator() not in (1, 2): - # Check if an element is a half-integer - raise ValueError("all numbers must be half-integers") - self._entry = Integer(2*entry) + raise ValueError("primed entries must be half-integers") def __hash__(self): """ @@ -976,9 +977,9 @@ def __repr__(self): 2' """ if self.is_unprimed(): - return str(self._entry//2) + return repr(self._entry // 2) else: - return str((self._entry+1)//2) + "'" + return repr((self._entry+1) // 2) + "'" def integer(self): """ @@ -987,7 +988,7 @@ def integer(self): sage: ShiftedPrimedTableau([[1,"2p"]])[0][1].integer() 2 """ - return (self._entry+1)//2 + return (self._entry + 1) // 2 def __eq__(self, other): """ @@ -1313,10 +1314,8 @@ def __init__(self, skew=None): TESTS:: - sage: T = ShiftedPrimedTableau([[None, 2]]) - sage: T.parent()._skew - [1] - sage: TestSuite(T).run() + sage: SPT = ShiftedPrimedTableaux(skew=[1]) + sage: TestSuite(SPT).run() # known bug """ self._skew = skew @@ -1399,7 +1398,7 @@ def _contains_tableau_(self, T): [(1, 2', 3', 3')] sage: Tabs._contains_tableau_(tab) False - sage: Tabs = ShiftedPrimedTableaux(skew = [1]) + sage: Tabs = ShiftedPrimedTableaux(skew=[1]) sage: tab = ShiftedPrimedTableau([])._preprocess_( ....: [[None,"2p","3p",3]], skew=[1]) sage: tab @@ -1491,7 +1490,7 @@ def __iter__(self): yield self.element_class(self, []) max_entry = 1 - while(True): + while True: for size in range(1, max_entry+1): for shape in Partitions(size, max_slope=-1): for weight in OrderedPartitions(size+max_entry-1, @@ -1556,8 +1555,8 @@ def __classcall_private__(cls, shape, max_entry=None, skew=None): True """ shape = _Partitions(shape) - return (super(ShiftedPrimedTableaux_shape, cls) - .__classcall__(cls, shape=shape, max_entry=max_entry, skew=skew)) + return super(ShiftedPrimedTableaux_shape, cls).__classcall__(cls, + shape=shape, max_entry=max_entry, skew=skew) def __init__(self, shape, max_entry, skew): """ @@ -1705,7 +1704,7 @@ def __iter__(self): preprocessed=True) else: max_entry = 1 - while(True): + while True: for weight in OrderedPartitions(sum(self._shape)+max_entry, k=max_entry): weight_n = tuple([w-1 for w in weight]) for tab in ShiftedPrimedTableaux(shape=self._shape, weight=weight_n): @@ -1749,8 +1748,7 @@ def _repr_(self): """ if self._skew is None: return "Shifted Primed Tableaux of weight {}".format(self._weight) - return ("Shifted Primed Tableaux of weight {} skewed by {}" - .format(self._weight, self._skew)) + return "Shifted Primed Tableaux of weight {} skewed by {}".format(self._weight, self._skew) def _contains_tableau_(self, T): """ @@ -1996,5 +1994,5 @@ def _add_strip(sub_tab, full_tab, length): for non_primed_strip in IntegerVectors(n=length-primes_num, k=len(plat_list), outer=plat_list): - yield (list(primed_strip) + list(non_primed_strip)) + yield list(primed_strip) + list(non_primed_strip) From b60c7fcc5a8d450328a421e7e52a4b2705683a4f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 29 Jan 2018 11:58:37 -0600 Subject: [PATCH 551/740] Convert _reading_word_with_positions to an iterator. --- src/sage/combinat/shifted_primed_tableau.py | 24 ++++++++++----------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index 52fd8996cf6..dda6c9f812a 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -593,8 +593,8 @@ def _to_matrix(self): def _reading_word_with_positions(self): """ - Return the reading word of ``self`` together with positions of the - corresponding letters in ``self``. + Iterate over the reading word of ``self`` together with positions + of the corresponding letters in ``self``. The reading word of a shifted primed tableau is constructed as follows: @@ -603,7 +603,7 @@ def _reading_word_with_positions(self): column, in decreasing order within each column, moving from the rightmost column to the left, and with all the primes removed (i.e. all entries are increased by - half a unit). + half a unit). 2. Then list all unprimed entries, row by row, in increasing order within each row, moving from the @@ -613,24 +613,22 @@ def _reading_word_with_positions(self): sage: SPT = ShiftedPrimedTableaux([4,2]) sage: t = SPT([[1,'2p',2,2],[2,'3p']]) - sage: t._reading_word_with_positions() + sage: list(t._reading_word_with_positions()) [((1, 2), 3), ((0, 1), 2), ((1, 1), 2), ((0, 0), 1), ((0, 2), 2), ((0, 3), 2)] """ mat = self._to_matrix() ndim, mdim = len(mat), len(mat[0]) - list_with_positions = [] for j in reversed(range(mdim)): for i in range(ndim): x = mat[i][j] if x is not None and x.is_primed(): - list_with_positions.append(((i, j), x.integer())) + yield ((i, j), x.integer()) for i in reversed(range(ndim)): for j in range(mdim): x = mat[i][j] if x is not None and x.is_unprimed(): - list_with_positions.append(((i, j), x.integer())) - return list_with_positions + yield ((i, j), x.integer()) def reading_word(self): """ @@ -730,13 +728,13 @@ def f(self, ind): if (c+1 == l or T[r][c+1] is None or T[r][c+1] >= ind_plus_one): (tp_r, tp_c) = (r, c) while True: - if (tp_r+1 == h or T[tp_r+1][tp_c] is None or T[tp_r+1][tp_c] > ind_plus_one): + if tp_r+1 == h or T[tp_r+1][tp_c] is None or T[tp_r+1][tp_c] > ind_plus_one: break if tp_r <= tp_c and T[tp_r+1][tp_r+1] == ind_plus_one: tp_r += 1 tp_c = tp_r break - if (ind_plus_half not in T[tp_r+1]): + if ind_plus_half not in T[tp_r+1]: break tp_r += 1 tp_c = T[tp_r].index(ind_plus_half) @@ -821,9 +819,9 @@ def e(self, ind): if (c == 0 or T[r][c-1] is None or T[r][c-1] <= ind_e): (tp_r, tp_c) = (r, c) while True: - if (tp_r == 0 or T[tp_r-1][tp_c] is None or T[tp_r-1][tp_c] < ind_e): + if tp_r == 0 or T[tp_r-1][tp_c] is None or T[tp_r-1][tp_c] < ind_e: break - if (ind_plus_half not in T[tp_r-1]): + if ind_plus_half not in T[tp_r-1]: break tp_r -= 1 tp_c = T[tp_r].index(ind_plus_half) @@ -873,7 +871,7 @@ def is_highest_weight(self, index_set=None): index_set = self.parent().index_set() for l in reversed(read_w): count[l] += 1 - if (l-1 in index_set) and (l > 1) and (count[l] > count[l-1]): + if l-1 in index_set and l > 1 and count[l] > count[l-1]: return False return True From d82d038c42d4e785c6c90ebfeb1b382565a7000f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 29 Jan 2018 12:04:02 -0600 Subject: [PATCH 552/740] Use the iterator from the crystals from the category (over 10x faster). --- src/sage/combinat/shifted_primed_tableau.py | 34 --------------------- 1 file changed, 34 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index dda6c9f812a..d6ae62c92e1 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -1676,40 +1676,6 @@ def shape(self): """ return self._shape - def __iter__(self): - """ - Iterate over ``self``. - - EXAMPLES:: - - sage: Tabs = ShiftedPrimedTableaux([3,2], max_entry=3) - sage: Tabs[:3] - [[(1, 1, 1), (2, 2)], [(1, 1, 1), (2, 3)], [(1, 1, 1), (2, 3')]] - sage: len(list(Tabs)) - 24 - sage: Tabs = ShiftedPrimedTableaux([3,2]) - sage: Tabs[:3] - [[(1, 1, 1), (2, 2)], [(1, 1, 2'), (2, 2)], [(1, 1, 1), (2, 2)]] - """ - if self._skew is not None: - raise NotImplementedError('skew tableau must be empty') - if self._max_entry is not None: - for weight in OrderedPartitions(sum(self._shape)+self._max_entry, - k=self._max_entry): - weight_n = tuple([w-1 for w in weight]) - for tab in ShiftedPrimedTableaux(shape=self._shape, weight=weight_n): - yield self.element_class(self, tab, check=False, - preprocessed=True) - else: - max_entry = 1 - while True: - for weight in OrderedPartitions(sum(self._shape)+max_entry, k=max_entry): - weight_n = tuple([w-1 for w in weight]) - for tab in ShiftedPrimedTableaux(shape=self._shape, weight=weight_n): - yield self.element_class(self, tab, check=False, - preprocessed=True) - max_entry += 1 - class ShiftedPrimedTableaux_weight(ShiftedPrimedTableaux): """ From d984df507be6b352f87bafbc388e23d7e5995031 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 29 Jan 2018 21:33:49 +0100 Subject: [PATCH 553/740] Fix GapElement_generic._add_ --- src/sage/interfaces/gap.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index 434ac77f884..6881ba5fbb9 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -966,7 +966,8 @@ def get_record_element(self, record, name): # We need to inherit from ModuleElement to support -# sage.structure.coerce_actions.ModuleAction +# sage.structure.coerce_actions.ModuleAction and it needs to be first +# in the MRO because extension types should always come first. @instancedoc class GapElement_generic(ModuleElement, ExtraTabCompletion, ExpectElement): r""" @@ -978,8 +979,19 @@ class GapElement_generic(ModuleElement, ExtraTabCompletion, ExpectElement): - Franco Saliola (Feb 2010): refactored to separate out the generic code - """ + def _add_(self, other): + """ + EXAMPLES:: + + sage: a = gap(1) + sage: a + a + 2 + """ + # This is just a copy of ExpectElement._add_ to fix the fact + # that the abtract method ModuleElement._add_ comes first in + # the MRO. + return self._operation("+", other) def bool(self): """ From 9e3e33874326f119479346c20edac973d891e31f Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 29 Jan 2018 22:12:02 +0100 Subject: [PATCH 554/740] Change richcmp() to a cpdef inline function --- src/sage/structure/richcmp.pxd | 52 ++++++++++++++++++++++++++++-- src/sage/structure/richcmp.pyx | 59 +++++++--------------------------- 2 files changed, 61 insertions(+), 50 deletions(-) diff --git a/src/sage/structure/richcmp.pxd b/src/sage/structure/richcmp.pxd index a5730ebbeea..a1df316b523 100644 --- a/src/sage/structure/richcmp.pxd +++ b/src/sage/structure/richcmp.pxd @@ -1,10 +1,58 @@ from libc.stdint cimport uint32_t -from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE -from cpython.object cimport PyObject_RichCompare as richcmp +from cpython.object cimport (Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE, + PyObject_RichCompare) + + +cpdef inline richcmp(x, y, int op): + """ + Return the result of the rich comparison of ``x`` and ``y`` with + operator ``op``. + + INPUT: + + - ``x``, ``y`` -- arbitrary Python objects + + - ``op`` -- comparison operator (one of ``op_LT`, ``op_LE``, + ``op_EQ``, ``op_NE``, ``op_GT``, ``op_GE``). + + EXAMPLES:: + + sage: from sage.structure.richcmp import * + sage: richcmp(3, 4, op_LT) + True + sage: richcmp(x, x^2, op_EQ) + x == x^2 + + The two examples above are completely equivalent to ``3 < 4`` + and ``x == x^2``. For this reason, it only makes sense in practice + to call ``richcmp`` with a non-constant value for ``op``. + + We can write a custom ``Element`` class which shows a more + realistic example of how to use this:: + + sage: from sage.structure.element import Element + sage: class MyElement(Element): + ....: def __init__(self, parent, value): + ....: Element.__init__(self, parent) + ....: self.v = value + ....: def _richcmp_(self, other, op): + ....: return richcmp(self.v, other.v, op) + sage: P = Parent() + sage: x = MyElement(P, 3) + sage: y = MyElement(P, 3) + sage: x < y + False + sage: x == y + True + sage: x > y + False + """ + return PyObject_RichCompare(x, y, op) cpdef richcmp_item(x, y, int op) + cpdef inline richcmp_not_equal(x, y, int op): """ Like ``richcmp(x, y, op)`` but assuming that `x` is not equal to `y`. diff --git a/src/sage/structure/richcmp.pyx b/src/sage/structure/richcmp.pyx index d5de65a7f20..f4a52c3e87f 100644 --- a/src/sage/structure/richcmp.pyx +++ b/src/sage/structure/richcmp.pyx @@ -32,7 +32,17 @@ AUTHORS: - Jeroen Demeyer """ -from cpython.object cimport PyObject_RichCompare, Py_TYPE, PyTypeObject +#***************************************************************************** +# Copyright (C) 2017-2018 Jeroen Demeyer +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from cpython.object cimport Py_TYPE, PyTypeObject from sage.cpython.wrapperdescr cimport get_slotdef, wrapperbase, PyDescr_NewWrapper cdef extern from *: @@ -58,53 +68,6 @@ richcmp_slotdef[Py_LE] = get_slotdef(bytes.__le__) richcmp_slotdef[Py_GE] = get_slotdef(bytes.__ge__) -def richcmp(x, y, int op): - """ - Return the result of the rich comparison of ``x`` and ``y`` with - operator ``op``. - - INPUT: - - - ``x``, ``y`` -- arbitrary Python objects - - - ``op`` -- comparison operator (one of ``op_LT`, ``op_LE``, - ``op_EQ``, ``op_NE``, ``op_GT``, ``op_GE``). - - EXAMPLES:: - - sage: from sage.structure.richcmp import * - sage: richcmp(3, 4, op_LT) - True - sage: richcmp(x, x^2, op_EQ) - x == x^2 - - The two examples above are completely equivalent to ``3 < 4`` - and ``x == x^2``. For this reason, it only makes sense in practice - to call ``richcmp`` with a non-constant value for ``op``. - - We can write a custom ``Element`` class which shows a more - realistic example of how to use this:: - - sage: from sage.structure.element import Element - sage: class MyElement(Element): - ....: def __init__(self, parent, value): - ....: Element.__init__(self, parent) - ....: self.v = value - ....: def _richcmp_(self, other, op): - ....: return richcmp(self.v, other.v, op) - sage: P = Parent() - sage: x = MyElement(P, 3) - sage: y = MyElement(P, 3) - sage: x < y - False - sage: x == y - True - sage: x > y - False - """ - return PyObject_RichCompare(x, y, op) - - cpdef richcmp_item(x, y, int op): """ This function is meant to implement lexicographic rich comparison From 8f944ccb9b950d7baa7b48b622870afb3abe33db Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Tue, 30 Jan 2018 09:37:45 +0100 Subject: [PATCH 555/740] Doc formatting. --- src/sage/modules/torsion_quadratic_module.py | 34 +++++++++++++------ .../quadratic_forms/genera/normal_form.py | 18 +++++----- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/sage/modules/torsion_quadratic_module.py b/src/sage/modules/torsion_quadratic_module.py index 11086373e7f..12cc491553b 100644 --- a/src/sage/modules/torsion_quadratic_module.py +++ b/src/sage/modules/torsion_quadratic_module.py @@ -62,7 +62,7 @@ def __init__(self, parent, x, check=DEBUG): def _mul_(self, other): r""" - Compute the inner product of two elements + Compute the inner product of two elements. OUTPUT: @@ -282,7 +282,7 @@ def _module_constructor(self, V, W, check=True): @cached_method def gram_matrix_bilinear(self): r""" - The gram matrix with respect to the generators + Return the gram matrix with respect to the generators. OUTPUT: @@ -410,8 +410,8 @@ def orthogonal_submodule_to(self, S): @cached_method def normal_form(self, partial=False): - """ - Return the normal form of this torsion quadratic module + r""" + Return the normal form of this torsion quadratic module. Two torsion quadratic modules are isomorphic if and only if they have the same value modules and the same normal form. @@ -424,17 +424,31 @@ def normal_form(self, partial=False): Below are some of its properties. Let `p` be odd and `u` be the smallest non-square modulo `p`. The normal form is a diagonal matrix with diagonal entries either `p^n` - or `u*p^n`. + or `u p^n`. If `p = 2` is even, then the normal form consists of 1 x 1 blocks of the form - ``[0]``, ``[2^n]`, ``[3*2^n]``, ``[5*2^n]``, ``[7*2^n]`` - or of `2 \times 2` blocks of the form:: - [2 1] [0 1] - [1 2] * 2^n, [1 0] * 2^n + .. MATH:: + + (0), \quad 2^n(1),\quad 2^n(3),\quad 2^n(5) ,\quad 2^n(7) + + or of `2 \times 2` blocks of the form + + .. MATH:: + + 2^n + \left(\begin{matrix} + 2 & 1\\ + 1 & 2 + \end{matrix}\right), \quad + 2^n + \left(\begin{matrix} + 0 & 1\\ + 1 & 0 + \end{matrix}\right). - The entries are ordered by their valuation. + The blocks are ordered by their valuation. INPUT: diff --git a/src/sage/quadratic_forms/genera/normal_form.py b/src/sage/quadratic_forms/genera/normal_form.py index c0fe4ba52ad..65d27faf848 100644 --- a/src/sage/quadratic_forms/genera/normal_form.py +++ b/src/sage/quadratic_forms/genera/normal_form.py @@ -353,7 +353,7 @@ def _get_small_block_indices(G): r""" Return the indices of the blocks. - For internal use in :meth:`collect_small_blocks` + For internal use in :meth:`collect_small_blocks`. INPUT: @@ -739,7 +739,7 @@ def _min_nonsquare(p): OUTPUT: - - ``a`` -- the minimal nonsquare mod `p` as an element of `\ZZ`. + - ``a`` -- the minimal nonsquare mod `p` EXAMPLES:: @@ -759,9 +759,9 @@ def _min_nonsquare(p): def _normalize(G): r""" - Return the transformation to sums of forms of types `U`, `V` and `W` + Return the transformation to sums of forms of types `U`, `V` and `W`. - See also :meth:``p_adic_normal_form``. + Part of the algorithm :meth:`p_adic_normal_form`. INPUT: @@ -849,7 +849,7 @@ def _normalize(G): def _normalize_2x2(G): r""" - normalize this indecomposable `2` by `2` block + Normalize this indecomposable `2` by `2` block. INPUT: @@ -985,7 +985,7 @@ def _normalize_2x2(G): def _normalize_odd_2x2(G): r""" - normalize this 2x2 block + Normalize this `2` by `2` block. INPUT: @@ -1116,7 +1116,7 @@ def _partial_normal_form_of_block(G): def _relations(G,n): r""" - Return relations of `2`-adic quadratic forms + Return relations of `2`-adic quadratic forms. See [MirMor2009]_ IV Prop. 3.2. This function is for internal use only. @@ -1508,14 +1508,14 @@ def _two_adic_normal_forms(G, partial=False): # condition a) - stay in homogneneous normal form R = UV + W Dk = D[R,R] - Bk = _homogeneous_normal_form(Dk,len(W))[1] + Bk = _homogeneous_normal_form(Dk, len(W))[1] B[R,:] = Bk * B[R,:] D = B * G * B.T # we need to restore the homogeneous normal form of k-1 if len(Wm)>0: R = UVm + Wm Dkm = D[R,R] - Bkm = _homogeneous_normal_form(Dkm,len(Wm))[1] + Bkm = _homogeneous_normal_form(Dkm, len(Wm))[1] B[R,:] = Bkm * B[R,:] D = B * G * B.T return D, B From 93a79bc514205b2369476905876e52a124538dce Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 30 Jan 2018 10:46:10 +0100 Subject: [PATCH 556/740] Use _get_action_ in MatrixSpace --- src/sage/matrix/matrix_space.py | 41 +++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index d621c106c6a..7f9234d5e1d 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -919,17 +919,45 @@ def construction(self): from sage.categories.pushout import MatrixFunctor return MatrixFunctor(self.__nrows, self.__ncols, is_sparse=self.is_sparse()), self.base_ring() - def get_action_impl(self, S, op, self_on_left): + def _get_action_(self, S, op, self_on_left): r""" Return the action of S on self INPUT: - - ``S`` - a parent + - ``S`` -- a parent - - ``op`` - an operator + - ``op`` -- an operator - - ``self_on_left`` - whether the operation is on left or on right + - ``self_on_left`` -- whether the operation is on left or on right + + EXAMPLES:: + + sage: V = QQ^(2,3) + sage: W1 = QQ^(3,4); W2 = QQ^(2,2) + sage: V.get_action(W1, operator.mul) + Left action by Full MatrixSpace of 2 by 3 dense matrices over Rational Field on Full MatrixSpace of 3 by 4 dense matrices over Rational Field + sage: V.get_action(W2, operator.mul) + sage: V.get_action(W1, operator.mul, self_on_left=False) + sage: V.get_action(W2, operator.mul, self_on_left=False) + Left action by Full MatrixSpace of 2 by 2 dense matrices over Rational Field on Full MatrixSpace of 2 by 3 dense matrices over Rational Field + + :: + + sage: V2 = QQ^2; V3 = QQ^3 + sage: V.get_action(V3, operator.mul) + Left action by Full MatrixSpace of 2 by 3 dense matrices over Rational Field on Vector space of dimension 3 over Rational Field + sage: V.get_action(V2, operator.mul) + sage: V.get_action(V3, operator.mul, self_on_left=False) + sage: V.get_action(V2, operator.mul, self_on_left=False) + Right action by Full MatrixSpace of 2 by 3 dense matrices over Rational Field on Vector space of dimension 2 over Rational Field + + :: + + sage: V.get_action(ZZ, operator.mul) + Right scalar multiplication by Integer Ring on Full MatrixSpace of 2 by 3 dense matrices over Rational Field + sage: V.get_action(ZZ, operator.mul, self_on_left=False) + Left scalar multiplication by Integer Ring on Full MatrixSpace of 2 by 3 dense matrices over Rational Field """ if op is operator.mul: try: @@ -944,7 +972,10 @@ def get_action_impl(self, S, op, self_on_left): # action of base ring return sage.structure.coerce.RightModuleAction(S, self) else: - if sage.modules.free_module.is_FreeModule(S): + if is_MatrixSpace(S): + # matrix multiplications + return matrix_action.MatrixMatrixAction(S, self) + elif sage.modules.free_module.is_FreeModule(S): return matrix_action.VectorMatrixAction(self, S) else: # action of base ring From 2249ad7d61c1c880ebc23452373aecdceea0fcce Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 30 Jan 2018 10:53:19 +0100 Subject: [PATCH 557/740] Remove actions from old coercion model --- .../coercion_and_categories.rst | 2 -- src/sage/structure/parent.pyx | 2 +- src/sage/structure/parent_old.pxd | 7 ---- src/sage/structure/parent_old.pyx | 33 ------------------- 4 files changed, 1 insertion(+), 43 deletions(-) diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst index 6f54d8b55a1..614c16393b2 100644 --- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst +++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst @@ -147,8 +147,6 @@ This base class provides a lot more methods than a general parent:: 'gcd', 'gen', 'gens', - 'get_action_c', - 'get_action_impl', 'has_coerce_map_from_c', 'ideal', 'ideal_monoid', diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index 19229698f89..531ce3cfc3b 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -2487,7 +2487,7 @@ cdef class Parent(sage.structure.category_object.CategoryObject): if action is not None: from sage.categories.action import Action if not isinstance(action, Action): - raise TypeError("get_action_impl must return None or an Action") + raise TypeError("_get_action_ must return None or an Action") # We do NOT add to the list, as this would lead to errors as in # the example above. diff --git a/src/sage/structure/parent_old.pxd b/src/sage/structure/parent_old.pxd index be4eb7d7715..8c68378154d 100644 --- a/src/sage/structure/parent_old.pxd +++ b/src/sage/structure/parent_old.pxd @@ -18,13 +18,6 @@ cdef class Parent(parent.Parent): cpdef coerce_map_from_c(self, S) cdef coerce_map_from_c_impl(self, S) - # returns the Action by/on self on/by S - # corresponding to op and self_on_left - cpdef get_action_c(self, S, op, bint self_on_left) - cdef get_action_c_impl(self, S, op, bint self_on_left) - - - cdef public MonoDict _has_coerce_map_from ######################################### diff --git a/src/sage/structure/parent_old.pyx b/src/sage/structure/parent_old.pyx index a74f0d28ff9..98b86780b77 100644 --- a/src/sage/structure/parent_old.pyx +++ b/src/sage/structure/parent_old.pyx @@ -193,33 +193,6 @@ cdef class Parent(parent.Parent): else: return None - cpdef get_action_c(self, S, op, bint self_on_left): - check_old_coerce(self) - try: - if self._action_hash is None: # this is because parent.__init__() does not always get called - self.init_coerce() - return self._action_hash.get(S, op, self_on_left) - except KeyError: - pass - if HAS_DICTIONARY(self): - action = self.get_action_impl(S, op, self_on_left) - else: - action = self.get_action_c_impl(S, op, self_on_left) - if action is not None: - from sage.categories.action import Action - if not isinstance(action, Action): - raise TypeError("get_action_impl must return None or an Action") - self._action_hash.set(S, op, self_on_left, action) - return action - - def get_action_impl(self, S, op, self_on_left): - check_old_coerce(self) - return self.get_action_c_impl(S, op, self_on_left) - - cdef get_action_c_impl(self, S, op, bint self_on_left): - check_old_coerce(self) - return self.discover_action(S, op, self_on_left, None, None) - ################################################################################# # Coercion support functionality ################################################################################# @@ -367,12 +340,6 @@ cdef class Parent(parent.Parent): else: return parent.Parent._coerce_map_from_(self, S) - cpdef _get_action_(self, other, op, bint self_on_left): - if self._element_constructor is None: - return self.get_action_c(other, op, self_on_left) - else: - return parent.Parent._get_action_(self, other, op, self_on_left) - def _an_element_(self): if self._element_constructor is None: return self._an_element_c() From 35137daa48adf4adde24ce1790ac0e6f1fb29b13 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 30 Jan 2018 11:17:16 +0100 Subject: [PATCH 558/740] Deprecate args of Parent_old.__init__ --- src/sage/structure/parent_base.pyx | 4 ++-- src/sage/structure/parent_old.pyx | 34 ++++++++++++++++-------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/sage/structure/parent_base.pyx b/src/sage/structure/parent_base.pyx index c5a3cbd7a35..81758edb532 100644 --- a/src/sage/structure/parent_base.pyx +++ b/src/sage/structure/parent_base.pyx @@ -33,8 +33,8 @@ cdef class ParentWithBase(Parent_old): """ This class is being deprecated, see parent.Parent for the new model. """ - def __init__(self, base, coerce_from=[], actions=[], embeddings=[], category=None): - Parent_old.__init__(self, coerce_from=coerce_from, actions=actions, embeddings=embeddings, category=category) + def __init__(self, base, *args, **kwds): + Parent_old.__init__(self, *args, **kwds) self._base = base cdef _coerce_c_impl(self,x): diff --git a/src/sage/structure/parent_old.pyx b/src/sage/structure/parent_old.pyx index a74f0d28ff9..8505bac3af7 100644 --- a/src/sage/structure/parent_old.pyx +++ b/src/sage/structure/parent_old.pyx @@ -66,23 +66,29 @@ cdef class Parent(parent.Parent): [0, a, a + 1, 1] """ - def __init__(self, coerce_from=[], actions=[], embeddings=[], category=None): - # TODO: many classes don't call this at all, but __new__ crashes Sage -# if len(coerce_from): -# print(type(self), coerce_from) + def __init__(self, coerce_from=None, actions=None, embeddings=None, *, category=None): self.init_coerce(False) - self._coerce_from_list = list(coerce_from) + if coerce_from is not None: + from sage.misc.superseded import deprecation + deprecation(24614, "the 'coerce_from' keyword is deprecated") + self._coerce_from_list = list(coerce_from) self._coerce_from_hash = MonoDict() - self._action_list = list(actions) + if actions is not None: + from sage.misc.superseded import deprecation + deprecation(24614, "the 'actions' keyword is deprecated") + self._action_list = list(actions) self._action_hash = TripleDict() cdef parent.Parent other - for mor in embeddings: - other = mor.domain() - print("embedding", self, " --> ", other) - print(mor) - other.init_coerce() # TODO remove when we can - other._coerce_from_list.append(mor) + if embeddings is not None: + from sage.misc.superseded import deprecation + deprecation(24614, "the 'embeddings' keyword is deprecated") + for mor in embeddings: + other = mor.domain() + print("embedding", self, " --> ", other) + print(mor) + other.init_coerce() # TODO remove when we can + other._coerce_from_list.append(mor) self._set_element_constructor() @@ -91,10 +97,6 @@ cdef class Parent(parent.Parent): if category is not None: self._init_category_(category) - cdef int init_coerce(self, bint warn=False) except -1: - parent.Parent.init_coerce(self, warn) - - ################################################################################# # New Coercion support functionality ################################################################################# From d192688b4d490aedb779934e72386d5531f7dcd6 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 30 Jan 2018 11:27:06 +0100 Subject: [PATCH 559/740] Reduce memory footprint of lcalc --- build/pkgs/lcalc/package-version.txt | 2 +- build/pkgs/lcalc/patches/pari-mem.patch | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/lcalc/patches/pari-mem.patch diff --git a/build/pkgs/lcalc/package-version.txt b/build/pkgs/lcalc/package-version.txt index 4eaf70cf6d8..0299e490029 100644 --- a/build/pkgs/lcalc/package-version.txt +++ b/build/pkgs/lcalc/package-version.txt @@ -1 +1 @@ -1.23.p15 +1.23.p16 diff --git a/build/pkgs/lcalc/patches/pari-mem.patch b/build/pkgs/lcalc/patches/pari-mem.patch new file mode 100644 index 00000000000..1566681a909 --- /dev/null +++ b/build/pkgs/lcalc/patches/pari-mem.patch @@ -0,0 +1,24 @@ +Use a much smaller PARI stack size for starting up + +Actually 1MB is sufficient, so there is plenty of margin +with the 16MB in this patch + +See https://trac.sagemath.org/ticket/24516 + +diff -ru lcalc-1.23/src/Lcommandline.cc lcalc-1.23-patched//src/Lcommandline.cc +--- lcalc-1.23/src/Lcommandline.cc 2012-08-08 23:21:56.000000000 +0200 ++++ lcalc-1.23-patched//src/Lcommandline.cc 2018-01-30 11:23:06.975418539 +0100 +@@ -412,12 +412,7 @@ + + t2=.5; //t2=.5 because of the GAMMA(s+1/2) + +- pari_init(1000000000,2); +- //pari_init_opts(400000000,2,INIT_DFTm); // the last option is to prevent +- //pari from giving its interrupt signal when its elliptic curve a_p +- //algorithm is called and interrupted with ctrl-c. Requires a more current +- //version of pari, so use pari_init above until I have a configure set up +- //that detects which pari, if any, is installed. ++ pari_init_opts(16000000, 2, INIT_DFTm); + + coeff = new Double[3]; + //compute the conductor which is copied to coeff[1] From 66a1e6c2317cb58fa97fb0c40a3433e11b816a22 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Sun, 10 Dec 2017 10:01:58 +0100 Subject: [PATCH 560/740] Fix two bugs in NumberField.composite_fields() --- src/sage/rings/number_field/number_field.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 85064c88916..3a8bc9e56af 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -135,6 +135,7 @@ from sage.structure.category_object import normalize_names import sage.structure.parent_gens +import sage.structure.coerce_exceptions from sage.structure.proof.proof import get_flag from . import maps @@ -4705,6 +4706,20 @@ def composite_fields(self, other, names=None, both_maps=False, preserve_embeddin 32 sage: F.gen() == map2(F2.gen()) + k*map1(F1.gen()) True + + Check that the bugs reported at :trac:`24357` are fixed:: + + sage: A. = NumberField(x^9 - 7) + sage: B. = NumberField(x^3-7, embedding=a^3) + sage: C. = QuadraticField(-1) + sage: B.composite_fields(C) + [Number Field in bc with defining polynomial x^6 + 3*x^4 + 14*x^3 + 3*x^2 - 42*x + 50] + + sage: y = polygen(QQ, 'y') + sage: A. = NumberField(x^3 - 7, embedding=CC(-0.95+1.65*I)) + sage: B. = NumberField(y^9 - 7, embedding=CC(-1.16+0.42*I)) + sage: A.composite_fields(B) + [Number Field in b with defining polynomial y^9 - 7] """ if not isinstance(other, NumberField_generic): raise TypeError("other must be a number field.") @@ -4724,13 +4739,13 @@ def composite_fields(self, other, names=None, both_maps=False, preserve_embeddin try: from sage.categories.pushout import pushout ambient_field = pushout(self.coerce_embedding().codomain(), other.coerce_embedding().codomain()) - except CoercionException: + except sage.structure.coerce_exceptions.CoercionException: ambient_field = None if ambient_field is None: subfields_have_embeddings = False f = self.absolute_polynomial() - g = other.absolute_polynomial() + g = other.absolute_polynomial().change_variable_name(f.variable_name()) R = f.parent() f = f.__pari__(); f /= f.content() g = g.__pari__(); g /= g.content() From f4eb6e0a018779c75c162efdabc9c2cf51c173d5 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Sun, 10 Dec 2017 10:23:54 +0100 Subject: [PATCH 561/740] NumberField.composite_fields(): warn about misfeature --- src/sage/rings/number_field/number_field.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 3a8bc9e56af..21de6b1ae48 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -4621,6 +4621,16 @@ def composite_fields(self, other, names=None, both_maps=False, preserve_embeddin sage: Hom(Q1, F).order() 4 + Note that even with ``preserve_embedding=True``, this method may fail + to recognize that the two number fields have compatible embeddings, and + hence return several composite number fields:: + + sage: x = polygen(ZZ) + sage: A. = NumberField(x^3 - 7, embedding=CC(-0.95+1.65*I)) + sage: B. = NumberField(x^9 - 7, embedding=QQbar.polynomial_root(x^9 - 7, RIF(1.2, 1.3))) + sage: len(A.composite_fields(B, preserve_embedding=True)) + 2 + TESTS: Let's check that embeddings are being respected:: From cc214fdcf00443531ade6e2fa650426de7debefb Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 16 Nov 2017 11:30:49 +0000 Subject: [PATCH 562/740] Numerous string conversions from bytes to str and from str to bytes for Python 3 --- src/sage/libs/ecl.pyx | 33 +++++++++++++++-------------- src/sage/libs/pynac/constant.pyx | 4 +++- src/sage/libs/pynac/pynac.pyx | 6 ++++++ src/sage/libs/singular/singular.pyx | 5 ++++- src/sage/rings/integer.pyx | 10 +++++++-- src/sage/rings/rational.pyx | 3 ++- src/sage/rings/real_arb.pyx | 4 ++-- src/sage/rings/real_mpfi.pyx | 5 +++-- src/sage/rings/real_mpfr.pyx | 3 ++- src/sage/symbolic/function.pyx | 11 +++++----- src/sage/symbolic/ring.pyx | 4 +++- 11 files changed, 56 insertions(+), 32 deletions(-) diff --git a/src/sage/libs/ecl.pyx b/src/sage/libs/ecl.pyx index 77ac7674d11..165e9253f4a 100644 --- a/src/sage/libs/ecl.pyx +++ b/src/sage/libs/ecl.pyx @@ -23,6 +23,7 @@ cimport cysignals.signals from sage.libs.gmp.types cimport mpz_t from sage.misc.misc import ECL_TMP +from sage.cpython.string cimport str_to_bytes, char_to_str from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational from cpython.object cimport Py_EQ, Py_NE @@ -279,30 +280,30 @@ def init_ecl(): #initialise list of objects and bind to global variable # *SAGE-LIST-OF-OBJECTS* to make it rooted in the reachable tree for the GC list_of_objects=cl_cons(Cnil,cl_cons(Cnil,Cnil)) - cl_set(string_to_object("*SAGE-LIST-OF-OBJECTS*"),list_of_objects) + cl_set(string_to_object(b"*SAGE-LIST-OF-OBJECTS*"), list_of_objects) - cl_eval(string_to_object(""" + cl_eval(string_to_object(b""" (setf (logical-pathname-translations "TMP") '(("**;*.*" "%s/**/*.*"))) - """ % ECL_TMP)) + """ % str_to_bytes(str(ECL_TMP)))) # We define our own error catching eval, apply and funcall/ # Presently these routines are only converted to byte-code. If they # ever turn out to be a bottle neck, it should be easy to properly # compile them. - read_from_string_clobj=cl_eval(string_to_object("(symbol-function 'read-from-string)")) + read_from_string_clobj=cl_eval(string_to_object(b"(symbol-function 'read-from-string)")) - cl_eval(string_to_object(""" + cl_eval(string_to_object(b""" (defun sage-safe-eval (form) (handler-case (values (eval form)) (serious-condition (cnd) (values nil (princ-to-string cnd))))) """)) - safe_eval_clobj=cl_eval(string_to_object("(symbol-function 'sage-safe-eval)")) + safe_eval_clobj=cl_eval(string_to_object(b"(symbol-function 'sage-safe-eval)")) - cl_eval(string_to_object(""" + cl_eval(string_to_object(b""" (defun sage-safe-apply (func args) (handler-case (values (apply func args)) @@ -310,15 +311,15 @@ def init_ecl(): (values nil (princ-to-string cnd))))) """)) - safe_apply_clobj=cl_eval(string_to_object("(symbol-function 'sage-safe-apply)")) - cl_eval(string_to_object(""" + safe_apply_clobj=cl_eval(string_to_object(b"(symbol-function 'sage-safe-apply)")) + cl_eval(string_to_object(b""" (defun sage-safe-funcall (func arg) (handler-case (values (funcall func arg)) (serious-condition (cnd) (values nil (princ-to-string cnd))))) """)) - safe_funcall_clobj=cl_eval(string_to_object("(symbol-function 'sage-safe-funcall)")) + safe_funcall_clobj=cl_eval(string_to_object(b"(symbol-function 'sage-safe-funcall)")) ecl_has_booted = 1 @@ -426,7 +427,7 @@ def print_objects(): c = list_of_objects while True: s = si_coerce_to_base_string(cl_write_to_string(1,cl_car(c))) - print(ecl_base_string_pointer_safe(s)) + print(char_to_str(ecl_base_string_pointer_safe(s))) c = cl_cadr(c) if c == Cnil: break @@ -458,7 +459,7 @@ cdef cl_object python_to_ecl(pyobj) except NULL: elif isinstance(pyobj,float): return ecl_make_doublefloat(pyobj) elif isinstance(pyobj,unicode): - s=(str(pyobj)) + s=str_to_bytes(pyobj) return ecl_safe_read_string(s) elif isinstance(pyobj,bytes): s=pyobj @@ -537,7 +538,7 @@ cdef ecl_to_python(cl_object o): return L else: s = si_coerce_to_base_string(cl_write_to_string(1,o)) - return ecl_base_string_pointer_safe(s) + return char_to_str(ecl_base_string_pointer_safe(s)) #Maxima's BFLOAT multiprecision float type can be read with: #def bfloat_to_python(e): @@ -754,7 +755,7 @@ cdef class EclObject: """ cdef cl_object s s = si_coerce_to_base_string(cl_write_to_string(1,self.obj)) - return ecl_base_string_pointer_safe(s) + return char_to_str(ecl_base_string_pointer_safe(s)) def __hash__(self): r""" @@ -1319,7 +1320,7 @@ cdef EclObject ecl_wrap(cl_object o): return obj #convenience routine to more easily evaluate strings -cpdef EclObject ecl_eval(bytes s): +cpdef EclObject ecl_eval(str s): """ Read and evaluate string in Lisp and return the result @@ -1333,7 +1334,7 @@ cpdef EclObject ecl_eval(bytes s): """ cdef cl_object o - o=ecl_safe_read_string(s) + o=ecl_safe_read_string(str_to_bytes(s)) o=ecl_safe_eval(o) return ecl_wrap(o) diff --git a/src/sage/libs/pynac/constant.pyx b/src/sage/libs/pynac/constant.pyx index 949ad43c6e6..49dfde0397a 100644 --- a/src/sage/libs/pynac/constant.pyx +++ b/src/sage/libs/pynac/constant.pyx @@ -19,6 +19,7 @@ from __future__ import absolute_import, division, print_function from .pynac cimport * from sage.symbolic.expression cimport new_Expression_from_GEx from sage.symbolic.ring import SR +from sage.cpython.string import str_to_bytes cdef class PynacConstant: @@ -65,7 +66,8 @@ cdef class PynacConstant: elif self._name == "NaN": self.pointer = &g_NaN else: - self._object = new GConstant(name, ConstantEvalf, texname, domain) + self._object = new GConstant(str_to_bytes(name), ConstantEvalf, + str_to_bytes(texname), domain) self.pointer = self._object def __dealloc__(self): diff --git a/src/sage/libs/pynac/pynac.pyx b/src/sage/libs/pynac/pynac.pyx index 529386766c7..517a47d3119 100644 --- a/src/sage/libs/pynac/pynac.pyx +++ b/src/sage/libs/pynac/pynac.pyx @@ -27,6 +27,8 @@ from sage.libs.gsl.gamma cimport gsl_sf_lngamma_complex_e from sage.libs.mpmath import utils as mpmath_utils from sage.libs.pari.all import pari +from sage.cpython.string import str_to_bytes + from sage.arith.all import gcd, lcm, is_prime, factorial, bernoulli from sage.structure.element cimport Element, parent, coercion_model @@ -370,6 +372,10 @@ cdef stdstring* string_from_pystr(py_str) except NULL: cdef bytes s if isinstance(py_str, bytes): s = py_str + elif isinstance(py_str, str): + # Note: This should only by the case on Python 3 since on Python 2 + # bytes is str + s = str_to_bytes(py_str) else: s = b"(INVALID)" # Avoid segfaults for invalid input return new stdstring(s) diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index 27123898cb3..e693eb088af 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -37,6 +37,7 @@ from sage.rings.finite_rings.finite_field_givaro import FiniteField_givaro from sage.rings.finite_rings.finite_field_ntl_gf2e import FiniteField_ntl_gf2e from sage.libs.pari.all import pari from sage.libs.gmp.all cimport * +from sage.cpython.string import FS_ENCODING, str_to_bytes from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular @@ -774,7 +775,9 @@ cdef init_libsingular(): if not os.path.exists(lib): raise ImportError("cannot locate Singular library ({})".format(lib)) - handle = dlopen(lib, RTLD_GLOBAL|RTLD_LAZY) + lib = str_to_bytes(lib, FS_ENCODING, "surrogateescape") + + handle = dlopen(lib, RTLD_GLOBAL|RTLD_LAZY) if not handle: err = dlerror() raise ImportError("cannot load Singular library ({})".format(err)) diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index f0cc57c1a32..3da210d3fc2 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -158,6 +158,7 @@ from sage.libs.gmp.mpz cimport * from sage.libs.gmp.mpq cimport * from sage.misc.superseded import deprecated_function_alias from sage.arith.long cimport pyobject_to_long, integer_check_long +from sage.cpython.string cimport char_to_str, str_to_bytes from cpython.list cimport * from cpython.number cimport * @@ -687,9 +688,14 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): except AttributeError: pass - elif isinstance(x, basestring): + elif isinstance(x, bytes): mpz_set_str_python(self.value, x, base) return + elif isinstance(x, str): + # Note: This should only be executed on Python 3 since on + # Python 2 str is bytes + mpz_set_str_python(self.value, str_to_bytes(x), base) + return elif (isinstance(x, list) or isinstance(x, tuple)) and base > 1: b = the_integer_ring(base) @@ -1084,7 +1090,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): sig_on() mpz_get_str(s, base, self.value) sig_off() - k = s + k = char_to_str(s) sig_free(s) return k diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index 9e8c39d602f..9da3f731f9b 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -58,6 +58,7 @@ import fractions from sage.misc.mathml import mathml from sage.arith.long cimport pyobject_to_long +from sage.cpython.string cimport char_to_str import sage.misc.misc as misc from sage.structure.sage_object cimport SageObject @@ -2073,7 +2074,7 @@ cdef class Rational(sage.structure.element.FieldElement): sig_on() mpq_get_str(s, base, self.value) sig_off() - k = str(s) + k = char_to_str(s) PyMem_Free(s) return k diff --git a/src/sage/rings/real_arb.pyx b/src/sage/rings/real_arb.pyx index 19da7f88f1e..1c91c113068 100644 --- a/src/sage/rings/real_arb.pyx +++ b/src/sage/rings/real_arb.pyx @@ -225,6 +225,7 @@ from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.real_mpfi import RealIntervalField, RealIntervalField_class from sage.structure.unique_representation import UniqueRepresentation +from sage.cpython.string cimport char_to_str cdef void mpfi_to_arb(arb_t target, const mpfi_t source, const long precision): """ @@ -1412,11 +1413,10 @@ cdef class RealBall(RingElement): [2e+0 +/- 0.101] """ cdef char* c_result - cdef bytes py_string c_result = arb_get_str(self.value, (prec(self) * 31) // 100, 0) try: - py_string = c_result + py_string = char_to_str(c_result) finally: flint_free(c_result) diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index 9cf4f16daab..38278e7ea4c 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -267,6 +267,7 @@ cimport sage.rings.real_mpfr as real_mpfr import math # for log import sys import operator +from sage.cpython.string cimport char_to_str import sage.rings.complex_field import sage.rings.infinity @@ -1908,7 +1909,7 @@ cdef class RealIntervalFieldElement(RingElement): sig_on() mpz_get_str(zz_str, base, self_zz) sig_off() - v = str(zz_str) + v = char_to_str(zz_str) PyMem_Free(zz_str) return v @@ -2141,7 +2142,7 @@ cdef class RealIntervalFieldElement(RingElement): if tmp_cstr == NULL: raise MemoryError("Unable to allocate memory for the error of an interval") mpz_get_str(tmp_cstr, 10, cur_error) - error_string = str(tmp_cstr) + error_string = char_to_str(tmp_cstr) PyMem_Free(tmp_cstr) mpz_clear(lower_mpz) diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index d892c0985aa..ee3c7450ba1 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -126,6 +126,7 @@ from sage.ext.stdsage cimport PY_NEW from sage.libs.gmp.mpz cimport * from sage.libs.mpfr cimport * from sage.misc.randstate cimport randstate, current_randstate +from sage.cpython.string cimport char_to_str from sage.structure.element cimport RingElement, Element, ModuleElement from sage.structure.richcmp cimport rich_to_bool_sgn @@ -1974,7 +1975,7 @@ cdef class RealNumber(sage.structure.element.RingElement): sig_off() if s is NULL: raise RuntimeError("unable to convert an mpfr number to a string") - t = str(s) + t = char_to_str(s) mpfr_free_str(s) if skip_zeroes: diff --git a/src/sage/symbolic/function.pyx b/src/sage/symbolic/function.pyx index 57013434e39..64af0fd8e8c 100644 --- a/src/sage/symbolic/function.pyx +++ b/src/sage/symbolic/function.pyx @@ -31,6 +31,7 @@ from sage.structure.richcmp cimport richcmp cdef dict sfunction_serial_dict = {} from sage.misc.fpickle import pickle_function, unpickle_function +from sage.cpython.string cimport str_to_bytes from sage.ext.fast_eval import FastDoubleFunc # List of functions which ginac allows us to define custom behavior for. @@ -146,7 +147,7 @@ cdef class Function(SageObject): """ cdef GFunctionOpt opt - opt = g_function_options_args(self._name, self._nargs) + opt = g_function_options_args(str_to_bytes(self._name), self._nargs) if hasattr(self, '_eval_'): opt.eval_func(self) @@ -186,7 +187,7 @@ cdef class Function(SageObject): # so we don't register them with the ginac function_options object if self._latex_name: - opt.latex_name(self._latex_name) + opt.latex_name(str_to_bytes(self._latex_name)) self._serial = g_register_new(opt) g_foptions_assign(g_registered_functions().index(self._serial), opt) @@ -803,7 +804,7 @@ cdef class GinacFunction(BuiltinFunction): fname = self._ginac_name if self._ginac_name is not None else self._name # get serial try: - self._serial = find_function(fname, self._nargs) + self._serial = find_function(str_to_bytes(fname), self._nargs) except RuntimeError as err: raise ValueError("cannot find GiNaC function with name %s and %s arguments" % (fname, self._nargs)) @@ -851,7 +852,7 @@ cdef class GinacFunction(BuiltinFunction): # overriding print functions is not supported if self._latex_name: - opt.latex_name(self._latex_name) + opt.latex_name(str_to_bytes(self._latex_name)) g_foptions_assign(g_registered_functions().index(self._serial), opt) @@ -1057,7 +1058,7 @@ cdef class BuiltinFunction(Function): # search ginac registry for name and nargs try: - serial = find_function(self._name, self._nargs) + serial = find_function(str_to_bytes(self._name), self._nargs) except RuntimeError as err: pass diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index e0a50640f64..9de834c042c 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -24,6 +24,7 @@ from sage.rings.real_mpfr cimport RealNumber from sage.symbolic.expression cimport Expression, new_Expression_from_GEx, new_Expression_from_pyobject, is_Expression from sage.misc.latex import latex_variable_name +from sage.cpython.string cimport str_to_bytes from sage.structure.element cimport RingElement, Element, Matrix from sage.categories.morphism cimport Morphism from sage.structure.coerce cimport is_numpy_type @@ -709,7 +710,8 @@ cdef class SymbolicRing(CommutativeRing): ginac_domain = sage_domain_to_ginac_domain(domain) else: ginac_domain = domain_complex - symb = ginac_symbol(name, latex_name, ginac_domain) + symb = ginac_symbol(str_to_bytes(name), + str_to_bytes(latex_name), ginac_domain) self.symbols[name] = e e._gobj = GEx(symb) From f7e1153918d1da2c451e95fbb9d4f00a393d1752 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Tue, 9 Jan 2018 13:05:38 +0000 Subject: [PATCH 563/740] Use str_to_bytes for 'unicode' (which will be str on Python 3 and unicode on Python 2) --- src/sage/rings/integer.pyx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 3da210d3fc2..deb348b8f58 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -691,9 +691,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): elif isinstance(x, bytes): mpz_set_str_python(self.value, x, base) return - elif isinstance(x, str): - # Note: This should only be executed on Python 3 since on - # Python 2 str is bytes + elif isinstance(x, unicode): mpz_set_str_python(self.value, str_to_bytes(x), base) return From a4d6fe3074a6df02eb2fda49fe47767727479294 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Wed, 17 Jan 2018 15:28:59 +0000 Subject: [PATCH 564/740] A few additional string conversion fixes for sage.libs.ecl --- src/sage/libs/ecl.pyx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/sage/libs/ecl.pyx b/src/sage/libs/ecl.pyx index 165e9253f4a..997b45047e6 100644 --- a/src/sage/libs/ecl.pyx +++ b/src/sage/libs/ecl.pyx @@ -1,4 +1,4 @@ -r""" +""" Library interface to Embeddable Common Lisp (ECL) """ #***************************************************************************** @@ -347,7 +347,8 @@ cdef cl_object ecl_safe_eval(cl_object form) except NULL: if ecl_nvalues > 1: s = si_coerce_to_base_string(ecl_values(1)) - raise RuntimeError("ECL says: "+ecl_base_string_pointer_safe(s)) + raise RuntimeError("ECL says: {}".format( + char_to_str(ecl_base_string_pointer_safe(s)))) else: return ecl_values(0) @@ -361,7 +362,8 @@ cdef cl_object ecl_safe_funcall(cl_object func, cl_object arg) except NULL: if ecl_nvalues > 1: s = si_coerce_to_base_string(ecl_values(1)) - raise RuntimeError("ECL says: "+ecl_base_string_pointer_safe(s)) + raise RuntimeError("ECL says: {}".format( + char_to_str(ecl_base_string_pointer_safe(s)))) else: return ecl_values(0) @@ -373,7 +375,8 @@ cdef cl_object ecl_safe_apply(cl_object func, cl_object args) except NULL: if ecl_nvalues > 1: s = si_coerce_to_base_string(ecl_values(1)) - raise RuntimeError("ECL says: "+ecl_base_string_pointer_safe(s)) + raise RuntimeError("ECL says: {}".format( + char_to_str(ecl_base_string_pointer_safe(s)))) else: return ecl_values(0) From d03a8d450e577e5c1cad95ef825409e6141367c4 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Fri, 19 Jan 2018 15:35:25 +0000 Subject: [PATCH 565/740] py3: fix pickling of integers and other minor bytes/str fixes --- src/sage/rings/integer.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index deb348b8f58..4ad18eb0c69 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -756,7 +756,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): Integers are supposed to be immutable, so you should not use this function. """ - mpz_set_str(self.value, s, 32) + mpz_set_str(self.value, str_to_bytes(s), 32) def __index__(self): """ @@ -6900,7 +6900,7 @@ cdef int mpz_set_str_python(mpz_ptr z, char* s, int base) except -1: assert base >= 2 if mpz_set_str(z, x, base) != 0: - raise TypeError("unable to convert %r to an integer" % s) + raise TypeError("unable to convert %r to an integer" % char_to_str(s)) if sign < 0: mpz_neg(z, z) if warnoctal and mpz_sgn(z) != 0: @@ -6987,7 +6987,7 @@ def make_integer(s): sage: make_integer(29) Traceback (most recent call last): ... - TypeError: expected string or Unicode object, sage.rings.integer.Integer found + TypeError: expected str...Integer found """ cdef Integer r = PY_NEW(Integer) r._reduce_set(s) From e9a582e0a3a5fe777ea34475200f18288fc3db2c Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Tue, 30 Jan 2018 14:37:15 +0000 Subject: [PATCH 566/740] import -> cimport where appropriate --- src/sage/libs/pynac/constant.pyx | 2 +- src/sage/libs/pynac/pynac.pyx | 2 +- src/sage/libs/singular/singular.pyx | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/libs/pynac/constant.pyx b/src/sage/libs/pynac/constant.pyx index 49dfde0397a..f05941e6f08 100644 --- a/src/sage/libs/pynac/constant.pyx +++ b/src/sage/libs/pynac/constant.pyx @@ -19,7 +19,7 @@ from __future__ import absolute_import, division, print_function from .pynac cimport * from sage.symbolic.expression cimport new_Expression_from_GEx from sage.symbolic.ring import SR -from sage.cpython.string import str_to_bytes +from sage.cpython.string cimport str_to_bytes cdef class PynacConstant: diff --git a/src/sage/libs/pynac/pynac.pyx b/src/sage/libs/pynac/pynac.pyx index 517a47d3119..829e2b24412 100644 --- a/src/sage/libs/pynac/pynac.pyx +++ b/src/sage/libs/pynac/pynac.pyx @@ -27,7 +27,7 @@ from sage.libs.gsl.gamma cimport gsl_sf_lngamma_complex_e from sage.libs.mpmath import utils as mpmath_utils from sage.libs.pari.all import pari -from sage.cpython.string import str_to_bytes +from sage.cpython.string cimport str_to_bytes from sage.arith.all import gcd, lcm, is_prime, factorial, bernoulli diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index e693eb088af..d73b55ed44b 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -37,7 +37,8 @@ from sage.rings.finite_rings.finite_field_givaro import FiniteField_givaro from sage.rings.finite_rings.finite_field_ntl_gf2e import FiniteField_ntl_gf2e from sage.libs.pari.all import pari from sage.libs.gmp.all cimport * -from sage.cpython.string import FS_ENCODING, str_to_bytes +from sage.cpython.string import FS_ENCODING +from sage.cpython.string cimport str_to_bytes from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular From 35a8fb25475ef19b38f81b128f4ae1f92db3293e Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Tue, 30 Jan 2018 16:20:10 +0100 Subject: [PATCH 567/740] 24615: libgap floating point numbers --- src/sage/libs/gap/element.pyx | 78 +++++++++++++++++++++++++++++- src/sage/libs/gap/gap_includes.pxd | 4 ++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/sage/libs/gap/element.pyx b/src/sage/libs/gap/element.pyx index e6f1e483925..de328e75346 100644 --- a/src/sage/libs/gap/element.pyx +++ b/src/sage/libs/gap/element.pyx @@ -24,7 +24,7 @@ from cysignals.signals cimport sig_on, sig_off from sage.misc.cachefunc import cached_method from sage.structure.sage_object cimport SageObject from sage.structure.parent import Parent -from sage.rings.all import ZZ, QQ +from sage.rings.all import ZZ, QQ, RDF decode_type_number = { libGAP_T_INT: 'T_INT (integer)', @@ -197,6 +197,8 @@ cdef GapElement make_any_gap_element(parent, libGAP_Obj obj): cdef int num = libGAP_TNUM_OBJ(obj) if num == libGAP_T_INT or num == libGAP_T_INTPOS or num == libGAP_T_INTNEG: return make_GapElement_Integer(parent, obj) + elif num == libGAP_T_MACFLOAT: + return make_GapElement_Float(parent, obj) elif num == libGAP_T_CYC: return make_GapElement_Cyclotomic(parent, obj) elif num == libGAP_T_FFE: @@ -1320,6 +1322,80 @@ cdef class GapElement_Integer(GapElement): """ return int(self) + +########################################################################## +### GapElement_Float ##################################################### +########################################################################## + +cdef GapElement_Float make_GapElement_Float(parent, libGAP_Obj obj): + r""" + Turn a Gap integer object into a GapElement_Float Sage object + + EXAMPLES:: + + sage: libgap(123.5) + 123.5 + sage: type(_) + + """ + cdef GapElement_Float r = GapElement_Float.__new__(GapElement_Float) + r._initialize(parent, obj) + return r + +cdef class GapElement_Float(GapElement): + r""" + Derived class of GapElement for GAP floating point numbers. + + EXAMPLES:: + + sage: i = libgap(123.5) + sage: type(i) + + sage: RDF(i) + 123.5 + sage: float(i) + 123.5 + + TESTS:: + + sage: a = RDF.random_element() + sage: libgap(a).sage() == a + True + """ + def sage(self, ring=None): + r""" + Return the Sage equivalent of the :class:`GapElement_Float` + + - ``ring`` -- a floating point field or ``None`` (default). If not + specified, the default Sage ``RDF`` is used. + + OUTPUT: + + A Sage double precision floating point number + + EXAMPLES:: + + sage: a = libgap.eval("Float(3.25)").sage() + sage: a + 3.25 + sage: parent(a) + Real Double Field + """ + if ring is None: + ring = RDF + return ring(libGAP_VAL_MACFLOAT(self.value)) + + def __float__(self): + r""" + TESTS:: + + sage: float(libgap.eval("Float(3.5)")) + 3.5 + """ + return libGAP_VAL_MACFLOAT(self.value) + + + ############################################################################ ### GapElement_IntegerMod ##################################################### ############################################################################ diff --git a/src/sage/libs/gap/gap_includes.pxd b/src/sage/libs/gap/gap_includes.pxd index 7ee08480c37..94480e8c026 100644 --- a/src/sage/libs/gap/gap_includes.pxd +++ b/src/sage/libs/gap/gap_includes.pxd @@ -302,6 +302,10 @@ cdef extern from "": void libGAP_AddList(libGAP_Obj list, libGAP_Obj obj) void libGAP_AddPlist(libGAP_Obj list, libGAP_Obj obj) +cdef extern from "": + bint libGAP_IS_MACFLOAT(libGAP_Obj obj) + double libGAP_VAL_MACFLOAT(libGAP_Obj obj) + cdef extern from "": char* libGAP_NAME_RNAM(libGAP_UInt rnam) libGAP_UInt libGAP_RNamIntg(int i) From 554fe61c0e5240a96c6a096f8b2b5a7ce30349d3 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Tue, 30 Jan 2018 17:14:25 +0100 Subject: [PATCH 568/740] 24615: typo --- src/sage/libs/gap/element.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/libs/gap/element.pyx b/src/sage/libs/gap/element.pyx index de328e75346..396b2d1beb2 100644 --- a/src/sage/libs/gap/element.pyx +++ b/src/sage/libs/gap/element.pyx @@ -1329,7 +1329,7 @@ cdef class GapElement_Integer(GapElement): cdef GapElement_Float make_GapElement_Float(parent, libGAP_Obj obj): r""" - Turn a Gap integer object into a GapElement_Float Sage object + Turn a Gap macfloat object into a GapElement_Float Sage object EXAMPLES:: From 1f58e87b674e45fc372fdd62451e8ebf4f6cd8bd Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 30 Jan 2018 17:24:54 +0100 Subject: [PATCH 569/740] Force GNU make --- Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9d1210395ab..92457458fe5 100644 --- a/Makefile +++ b/Makefile @@ -11,10 +11,14 @@ default: all build: all-build +# The --stop flag below is just a random flag to induce graceful +# breakage with non-GNU versions of make. +# See https://trac.sagemath.org/ticket/24617 + # Defer unknown targets to build/make/Makefile %:: @if [ -x relocate-once.py ]; then ./relocate-once.py; fi - $(MAKE) build/make/Makefile + $(MAKE) build/make/Makefile --stop +build/bin/sage-logger \ "cd build/make && ./install '$@'" logs/install.log From 8abc33b3dd7001c7022e9c114563251d7e668304 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Tue, 26 Dec 2017 16:40:20 +0100 Subject: [PATCH 570/740] 24431: Laurent series pushout and coercions --- src/sage/rings/laurent_series_ring.py | 110 ++++++++++++++++-- .../polynomial/laurent_polynomial_ring.py | 22 +++- .../hyperelliptic_generic.py | 13 ++- 3 files changed, 131 insertions(+), 14 deletions(-) diff --git a/src/sage/rings/laurent_series_ring.py b/src/sage/rings/laurent_series_ring.py index a6caea08d17..8067e1f8316 100644 --- a/src/sage/rings/laurent_series_ring.py +++ b/src/sage/rings/laurent_series_ring.py @@ -17,6 +17,20 @@ * :func:`sage.misc.defaults.set_series_precision` """ +#***************************************************************************** +# Copyright (C) 2006 William Stein +# 2007 Robert Bradshaw +# 2012 David Roe +# 2014 Peter Bruin +# 2017 Vincent Delecroix <20100.delecroix@gmail.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + from __future__ import print_function, absolute_import from sage.categories.rings import Rings @@ -135,6 +149,28 @@ class LaurentSeriesRing(UniqueRepresentation, CommutativeRing): Category of infinite complete discrete valuation fields sage: LaurentSeriesRing(Zmod(4), 'x').category() Category of infinite commutative rings + + Check coercions (:trac:`24431`):: + + sage: pts = [LaurentSeriesRing, + ....: PolynomialRing, + ....: PowerSeriesRing, + ....: LaurentPolynomialRing] + sage: LS = LaurentSeriesRing(QQ, 'x') + sage: LSx = LS.gen() + + sage: for P in pts: + ....: x = P(QQ, 'x').gen() + ....: assert parent(LSx * x) is LS, "wrong parent for {}".format(P) + + sage: for P in pts: + ....: y = P(QQ, 'y').gen() + ....: try: + ....: LSx * y + ....: except TypeError: + ....: pass + ....: else: + ....: print("wrong coercion {}".format(P)) """ Element = LaurentSeries @@ -148,6 +184,11 @@ def __classcall__(cls, *args, **kwds): True sage: loads(dumps(L)) is L True + + sage: L.variable_names() + ('q',) + sage: L.variable_name() + 'q' """ from .power_series_ring import PowerSeriesRing, is_PowerSeriesRing @@ -446,6 +487,40 @@ def _element_constructor_(self, x, n=0): return (x << n) return self.element_class(self, x, n) + def construction(self): + r""" + Return the functorial construction of this Laurent power series ring. + + The construction is given as the completion of the Laurent polynomials. + + EXAMPLES:: + + sage: L. = LaurentSeriesRing(ZZ, default_prec=42) + sage: phi, arg = L.construction() + sage: phi + Completion[t, prec=42] + sage: arg + Univariate Laurent Polynomial Ring in t over Integer Ring + sage: phi(arg) is L + True + + Because of this construction, pushout is automatically available:: + + sage: 1/2 * t + 1/2*t + sage: parent(1/2 * t) + Laurent Series Ring in t over Rational Field + + sage: QQbar.gen() * t + I*t + sage: parent(QQbar.gen() * t) + Laurent Series Ring in t over Algebraic Field + """ + from sage.categories.pushout import CompletionFunctor + from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing + L = LaurentPolynomialRing(self.base_ring(), self._names[0]) + return CompletionFunctor(self._names[0], self.default_prec()), L + def _coerce_map_from_(self, P): """ Return a coercion map from `P` to ``self``, or True, or None. @@ -462,14 +537,31 @@ def _coerce_map_from_(self, P): EXAMPLES:: - sage: R. = LaurentSeriesRing(ZZ) - sage: S. = PowerSeriesRing(QQ) - sage: R.has_coerce_map_from(S) # indirect doctest - False - sage: R.has_coerce_map_from(R) + sage: S. = LaurentSeriesRing(ZZ) + sage: S.has_coerce_map_from(ZZ) + True + sage: S.has_coerce_map_from(PolynomialRing(ZZ, 't')) + True + sage: S.has_coerce_map_from(LaurentPolynomialRing(ZZ, 't')) + True + sage: S.has_coerce_map_from(PowerSeriesRing(ZZ, 't')) + True + sage: S.has_coerce_map_from(S) True + + sage: S.has_coerce_map_from(QQ) + False + sage: S.has_coerce_map_from(PolynomialRing(QQ, 't')) + False + sage: S.has_coerce_map_from(LaurentPolynomialRing(QQ, 't')) + False + sage: S.has_coerce_map_from(PowerSeriesRing(QQ, 't')) + False + sage: S.has_coerce_map_from(LaurentSeriesRing(QQ, 't')) + False + sage: R. = LaurentSeriesRing(QQ['x']) - sage: R.has_coerce_map_from(S) + sage: R.has_coerce_map_from(QQ[['t']]) True sage: R.has_coerce_map_from(QQ['t']) True @@ -489,7 +581,11 @@ def _coerce_map_from_(self, P): from sage.rings.polynomial.polynomial_ring import is_PolynomialRing from sage.rings.power_series_ring import is_PowerSeriesRing - if ((is_LaurentSeriesRing(P) or is_PowerSeriesRing(P) or is_PolynomialRing(P)) + from sage.rings.polynomial.laurent_polynomial_ring import is_LaurentPolynomialRing + if ((is_LaurentSeriesRing(P) or + is_LaurentPolynomialRing(P) or + is_PowerSeriesRing(P) or + is_PolynomialRing(P)) and P.variable_name() == self.variable_name() and A.has_coerce_map_from(P.base_ring())): return True diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring.py b/src/sage/rings/polynomial/laurent_polynomial_ring.py index 9ff67302e3c..8b1c4bfce1e 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_ring.py +++ b/src/sage/rings/polynomial/laurent_polynomial_ring.py @@ -555,10 +555,21 @@ def completion(self, p, prec=20, extras=None): -x^-1 + 1 sage: 1/PP(f) -x - x^2 - x^3 - x^4 - x^5 - x^6 - x^7 - x^8 - x^9 - x^10 - x^11 - x^12 - x^13 - x^14 - x^15 - x^16 - x^17 - x^18 - x^19 - x^20 + O(x^21) + + TESTS: + + Check that the precision is taken into account (:trac:`24431`):: + + sage: L = LaurentPolynomialRing(QQ, 'x') + sage: L.completion('x', 100).default_prec() + 100 + sage: L.completion('x', 20).default_prec() + 20 """ if str(p) == self._names[0] and self._n == 1: from sage.rings.laurent_series_ring import LaurentSeriesRing - return LaurentSeriesRing(self.base_ring(), name=self._names[0]) + R = self.polynomial_ring().completion(self._names[0], prec) + return LaurentSeriesRing(R) else: raise TypeError("Cannot complete %s with respect to %s" % (self, p)) @@ -849,11 +860,16 @@ def __init__(self, R): sage: L == loads(dumps(L)) True + + + TESTS:: + + sage: TestSuite(LaurentPolynomialRing(Zmod(4), 'y')).run() + sage: TestSuite(LaurentPolynomialRing(ZZ, 'u')).run() + sage: TestSuite(LaurentPolynomialRing(Zmod(4)['T'], 'u')).run() """ if R.ngens() != 1: raise ValueError("must be 1 generator") - if not R.base_ring().is_integral_domain(): - raise ValueError("base ring must be an integral domain") LaurentPolynomialRing_generic.__init__(self, R) def _repr_(self): diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py index cdca282cc59..9d820ffab0e 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py @@ -33,7 +33,11 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.rings.all import PolynomialRing, RR, PowerSeriesRing, LaurentSeriesRing, O +from sage.rings.polynomial.all import PolynomialRing +from sage.rings.big_oh import O +from sage.rings.power_series_ring import PowerSeriesRing +from sage.rings.laurent_series_ring import LaurentSeriesRing +from sage.rings.real_mpfr import RR from sage.functions.all import log from sage.structure.category_object import normalize_names @@ -550,20 +554,21 @@ def local_coordinates_at_infinity(self, prec = 20, name = 't'): t^-3 + t - t^3 - t^5 + 3*t^7 - 10*t^11 + O(t^12) AUTHOR: - - Jennifer Balakrishnan (2007-12) + + - Jennifer Balakrishnan (2007-12) """ g = self.genus() pol = self.hyperelliptic_polynomials()[0] K = LaurentSeriesRing(self.base_ring(), name, default_prec=prec+2) t = K.gen() - L = PolynomialRing(self.base_ring(),'x') + L = PolynomialRing(K,'x') x = L.gen() i = 0 w = (x**g/t)**2-pol wprime = w.derivative(x) x = t**-2 for i in range((RR(log(prec+2)/log(2))).ceil()): - x = x-w(x)/wprime(x) + x = x - w(x)/wprime(x) y = x**g/t return x+O(t**(prec+2)) , y+O(t**(prec+2)) From 18d98778238982fd01f7cc9a1f027aff51460305 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 30 Jan 2018 20:20:49 +0100 Subject: [PATCH 571/740] Fix cimport --- src/sage/structure/parent.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index ee33eea6918..815ad3d1335 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -126,7 +126,7 @@ from sage.structure.misc import is_extension_type from sage.misc.lazy_attribute import lazy_attribute from sage.categories.sets_cat import Sets, EmptySetError from sage.misc.lazy_format import LazyFormat -from . coerce_maps cimport (NamedConvertMap, DefaultConvertMap, +from .coerce_maps cimport (NamedConvertMap, DefaultConvertMap, DefaultConvertMap_unique, CallableConvertMap) From 2af9f709afacd0d2abad80b2722eacf1f90d5d76 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Tue, 30 Jan 2018 12:58:04 -0800 Subject: [PATCH 572/740] Minor fixes --- src/sage/combinat/shifted_primed_tableau.py | 172 +++++++++++++------- 1 file changed, 112 insertions(+), 60 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index d6ae62c92e1..c7f127e9212 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -110,7 +110,6 @@ def __classcall_private__(cls, T, skew=None): return T if not T or T == [[]]: return ShiftedPrimedTableaux(skew=skew)([]) - try: entry = T[0][0] except TypeError: @@ -155,12 +154,12 @@ def __init__(self, parent, T, skew=None, check=True, preprocessed=False): TypeError: 'tuple' object does not support item assignment """ if not preprocessed: - T = self._preprocess_(T, skew=skew) + T = self._preprocess(T, skew=skew) self._skew = skew ClonableArray.__init__(self, parent, T, check=check) @staticmethod - def _preprocess_(T, skew=None): + def _preprocess(T, skew=None): """ Preprocessing list ``T`` to initialize the tableau. @@ -202,7 +201,7 @@ def check(self): ValueError: [['1p', '2p', 2, 2], [2, '3p']] is not an element of Shifted Primed Tableaux of shape [4, 2] """ - if not self.parent()._contains_tableau_(self): + if not self.parent()._contains_tableau(self): raise ValueError("{} is not an element of Shifted Primed Tableaux".format(self)) def __eq__(self, other): @@ -855,12 +854,12 @@ def is_highest_weight(self, index_set=None): EXAMPLES:: sage: SPT = ShiftedPrimedTableaux([5,4,2]) - sage: t = SPT([(1.0, 1.0, 1.0, 1.0, 1.0), (2.0, 2.0, 2.0, 2.5), (3.0, 3.0)]) + sage: t = SPT([(1, 1, 1, 1, 1), (2, 2, 2, "3p"), (3, 3)]) sage: t.is_highest_weight() True sage: SPT = ShiftedPrimedTableaux([5,4]) - sage: s = SPT([(1.0, 1.0, 1.0, 1.0, 1.0), (2.0, 2.0, 2.5, 3.0)]) + sage: s = SPT([(1, 1, 1, 1, 1), (2, 2, "3p", 3)]) sage: s.is_highest_weight(index_set=[1]) True """ @@ -920,14 +919,16 @@ def __init__(self, entry=None, double=None): TESTS:: - sage: ShiftedPrimedTableau([[1,"2p"]])[0][1] - 2' - sage: ShiftedPrimedTableau([[1,"2'"]])[0][1] - 2' - sage: ShiftedPrimedTableau([[1,1.5]])[0][1] + sage: from sage.combinat.shifted_primed_tableau import PrimedEntry + sage: PrimedEntry(2) + 2 + sage: PrimedEntry("2p") 2' - sage: ShiftedPrimedTableau([[1,3/2]])[0][1] + sage: PrimedEntry("2'") 2' + sage: a = PrimedEntry(2.5) + sage: PrimedEntry(a) + 3' """ # store primed numbers as odd, unprimed numbers as even integers if isinstance(entry, self.__class__): @@ -941,14 +942,13 @@ def __init__(self, entry=None, double=None): if isinstance(entry, str): if (entry[-1] == "'" or entry[-1] == "p") and entry[:-1].isdigit(): # Check if an element has "'" or "p" at the end - self._entry = 2*Integer(entry[:-1]) - 1 + self._entry = 2 * Integer(entry[:-1]) - 1 else: self._entry = 2 * Integer(entry) return - # FIXME: This should not happen! Something is not right. if entry is None: - entry = 0 + raise ValueError("primed entry must not be None") try: self._entry = Integer(2*entry) except (TypeError, ValueError): @@ -958,8 +958,9 @@ def __hash__(self): """ TESTS:: - sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] - sage: b = ShiftedPrimedTableau([[1,1,"2p"]])[0][2] + sage: from sage.combinat.shifted_primed_tableau import PrimedEntry + sage: a = PrimedEntry("2p") + sage: b = PrimedEntry("2'") sage: a == b True """ @@ -983,8 +984,12 @@ def integer(self): """ TESTS:: - sage: ShiftedPrimedTableau([[1,"2p"]])[0][1].integer() + sage: from sage.combinat.shifted_primed_tableau import PrimedEntry + sage: b = PrimedEntry("2p").integer() + sage: b 2 + sage: b.category() + Category of elements of Integer Ring """ return (self._entry + 1) // 2 @@ -992,67 +997,97 @@ def __eq__(self, other): """ TESTS:: - sage: a = ShiftedPrimedTableau([[1,1]])[0][1] - sage: b = ShiftedPrimedTableau([[1,1]])[0][0] + sage: from sage.combinat.shifted_primed_tableau import PrimedEntry + sage: a = PrimedEntry("2p") + sage: b = PrimedEntry("2'") sage: a == b True """ - return self._entry == PrimedEntry(other)._entry + try: + other = PrimedEntry(other) + except ValueError: + return False + return self._entry == other._entry def __ne__(self, other): """ TESTS:: - sage: a = ShiftedPrimedTableau([[1,1]])[0][1] - sage: b = ShiftedPrimedTableau([[1,1]])[0][0] + sage: from sage.combinat.shifted_primed_tableau import PrimedEntry + sage: a = PrimedEntry("1") + sage: b = PrimedEntry(1) sage: a != b False """ - return self._entry != PrimedEntry(other)._entry + try: + other = PrimedEntry(other) + except ValueError: + return True + return self._entry != other._entry def __lt__(self, other): """ TESTS:: - sage: a = ShiftedPrimedTableau([[1,"2p",2]])[0][1] - sage: b = ShiftedPrimedTableau([[1,"2p",2]])[0][2] + sage: from sage.combinat.shifted_primed_tableau import PrimedEntry + sage: a = PrimedEntry("2p") + sage: b = PrimedEntry(2) sage: a < b True """ - return self._entry < PrimedEntry(other)._entry + try: + other = PrimedEntry(other) + except ValueError: + raise ValueError("both elements must be primed entries") + return self._entry < other._entry def __le__(self, other): """ TESTS:: - sage: a = ShiftedPrimedTableau([[1,2,2]])[0][1] - sage: b = ShiftedPrimedTableau([[1,2,2]])[0][2] + sage: from sage.combinat.shifted_primed_tableau import PrimedEntry + sage: a = PrimedEntry(2) + sage: b = PrimedEntry("3p") sage: a <= b True """ - return self._entry <= PrimedEntry(other)._entry + try: + other = PrimedEntry(other) + except ValueError: + raise ValueError("both elements must be primed entries") + return self._entry <= other._entry def __gt__(self, other): """ TESTS:: - sage: a = ShiftedPrimedTableau([[1,"2p",2]])[0][1] - sage: b = ShiftedPrimedTableau([[1,"2p",2]])[0][2] + sage: from sage.combinat.shifted_primed_tableau import PrimedEntry + sage: a = PrimedEntry("2p") + sage: b = PrimedEntry(2) sage: b > a True """ - return self._entry > PrimedEntry(other)._entry + try: + other = PrimedEntry(other) + except ValueError: + raise ValueError("both elements must be primed entries") + return self._entry > other._entry def __ge__(self, other): """ TESTS:: - sage: a = ShiftedPrimedTableau([[1,"2p",2]])[0][1] - sage: b = ShiftedPrimedTableau([[1,"2p",2]])[0][2] - sage: a >= b - False + sage: from sage.combinat.shifted_primed_tableau import PrimedEntry + sage: a = PrimedEntry(2) + sage: b = PrimedEntry("3p") + sage: a <= b + True """ - return self._entry >= PrimedEntry(other)._entry + try: + other = PrimedEntry(other) + except ValueError: + raise ValueError("both elements must be primed entries") + return self._entry >= other._entry def is_unprimed(self): """ @@ -1060,7 +1095,8 @@ def is_unprimed(self): TESTS:: - sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] + sage: from sage.combinat.shifted_primed_tableau import PrimedEntry + sage: a = PrimedEntry("2p") sage: a.is_unprimed() False """ @@ -1072,7 +1108,8 @@ def is_primed(self): TESTS:: - sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] + sage: from sage.combinat.shifted_primed_tableau import PrimedEntry + sage: a = PrimedEntry("3p") sage: a.is_primed() True """ @@ -1084,7 +1121,8 @@ def unprimed(self): TESTS:: - sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] + sage: from sage.combinat.shifted_primed_tableau import PrimedEntry + sage: a = PrimedEntry("2p") sage: a.unprimed() 2 """ @@ -1099,7 +1137,8 @@ def primed(self): TESTS:: - sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][0] + sage: from sage.combinat.shifted_primed_tableau import PrimedEntry + sage: a = PrimedEntry(1) sage: a.primed() 1' """ @@ -1112,7 +1151,8 @@ def increase_half(self): """ TESTS:: - sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][0] + sage: from sage.combinat.shifted_primed_tableau import PrimedEntry + sage: a = PrimedEntry(1) sage: a.increase_half() 2' """ @@ -1122,7 +1162,8 @@ def decrease_half(self): """ TESTS:: - sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][0] + sage: from sage.combinat.shifted_primed_tableau import PrimedEntry + sage: a = PrimedEntry(1) sage: a.decrease_half() 1' """ @@ -1132,7 +1173,8 @@ def increase_one(self): """ TESTS:: - sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] + sage: from sage.combinat.shifted_primed_tableau import PrimedEntry + sage: a = PrimedEntry("2p") sage: a.increase_one() 3' """ @@ -1142,7 +1184,8 @@ def decrease_one(self): """ TESTS:: - sage: a = ShiftedPrimedTableau([[1,"2p"]])[0][1] + sage: from sage.combinat.shifted_primed_tableau import PrimedEntry + sage: a = PrimedEntry("2p") sage: a.decrease_one() 1' """ @@ -1383,25 +1426,25 @@ def _element_constructor_(self, T): except ValueError: raise ValueError("{} is not an element of {}".format(T, self)) - def _contains_tableau_(self, T): + def _contains_tableau(self, T): """ Check if ``self`` contains preprocessed tableau ``T``. TESTS:: sage: Tabs = ShiftedPrimedTableaux() - sage: tab = ShiftedPrimedTableau([])._preprocess_( + sage: tab = ShiftedPrimedTableau([])._preprocess( ....: [[1,"2p","3p","3p"]]) sage: tab [(1, 2', 3', 3')] - sage: Tabs._contains_tableau_(tab) + sage: Tabs._contains_tableau(tab) False sage: Tabs = ShiftedPrimedTableaux(skew=[1]) - sage: tab = ShiftedPrimedTableau([])._preprocess_( + sage: tab = ShiftedPrimedTableau([])._preprocess( ....: [[None,"2p","3p",3]], skew=[1]) sage: tab [(None, 2', 3', 3)] - sage: Tabs._contains_tableau_(tab) + sage: Tabs._contains_tableau(tab) True """ if not all(len(T[i]) > len(T[i+1]) for i in range(len(T)-1)): @@ -1456,7 +1499,10 @@ def __init__(self, skew=None): False sage: TestSuite(SPT).run() # long time """ - Parent.__init__(self, category=InfiniteEnumeratedSets()) + if skew is None: + Parent.__init__(self, category=InfiniteEnumeratedSets()) + else: + Parent.__init__(self, category=Sets().Infinite()) ShiftedPrimedTableaux.__init__(self, skew=skew) self._skew = skew @@ -1607,7 +1653,7 @@ def _repr_(self): base += " and maximum entry {}".format(self._max_entry) return base - def _contains_tableau_(self, T): + def _contains_tableau(self, T): """ TESTS:: @@ -1617,7 +1663,7 @@ def _contains_tableau_(self, T): sage: [[1,'2p',2],[2,'3p']] in ShiftedPrimedTableaux([4,2]) False """ - if not super(ShiftedPrimedTableaux_shape, self)._contains_tableau_(T): + if not super(ShiftedPrimedTableaux_shape, self)._contains_tableau(T): return False shape = [len(row) for row in T] @@ -1697,7 +1743,10 @@ def __init__(self, weight, skew=None): sage: TestSuite( ShiftedPrimedTableaux(weight=(3,2,1)) ).run() """ ShiftedPrimedTableaux.__init__(self, skew=skew) - Parent.__init__(self, category=FiniteEnumeratedSets()) + if skew is None: + Parent.__init__(self, category=FiniteEnumeratedSets()) + else: + Parent.__init__(self, category=Sets().Finite()) self._weight = weight self._skew = skew @@ -1714,7 +1763,7 @@ def _repr_(self): return "Shifted Primed Tableaux of weight {}".format(self._weight) return "Shifted Primed Tableaux of weight {} skewed by {}".format(self._weight, self._skew) - def _contains_tableau_(self, T): + def _contains_tableau(self, T): """ Check if ``self`` contains preprocessed tableau ``T``. @@ -1730,7 +1779,7 @@ def _contains_tableau_(self, T): sage: [] in ShiftedPrimedTableaux(weight=(1,2)) False """ - if not super(ShiftedPrimedTableaux_weight, self)._contains_tableau_(T): + if not super(ShiftedPrimedTableaux_weight, self)._contains_tableau(T): return False flat = [item.integer() for sublist in T for item in sublist] @@ -1784,7 +1833,10 @@ def __init__(self, weight, shape, skew=None): sage: TestSuite( ShiftedPrimedTableaux([4,2,1], weight=(3,2,2)) ).run() """ ShiftedPrimedTableaux.__init__(self, skew=skew) - Parent.__init__(self, category=FiniteEnumeratedSets()) + if skew is None: + Parent.__init__(self, category=FiniteEnumeratedSets()) + else: + Parent.__init__(self, category=Sets().Finite()) self._weight = weight self._skew = skew if skew is None: @@ -1804,7 +1856,7 @@ def _repr_(self): return ("Shifted Primed Tableaux of weight {} and shape {}" .format(self._weight, self._shape)) - def _contains_tableau_(self, T): + def _contains_tableau(self, T): """ Check if ``self`` contains preprocessed tableau ``T``. @@ -1822,7 +1874,7 @@ def _contains_tableau_(self, T): sage: [] in ShiftedPrimedTableaux([], weight=()) True """ - if not super(ShiftedPrimedTableaux_weight_shape, self)._contains_tableau_(T): + if not super(ShiftedPrimedTableaux_weight_shape, self)._contains_tableau(T): return False flat = [item.integer() for sublist in T for item in sublist] From 20170dfefdf380b1481748b3ffe2307b599c70c5 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 3 Sep 2017 10:53:51 -0700 Subject: [PATCH 573/740] 23714: Matrix_gap --- src/module_list.py | 3 + src/sage/matrix/action.pyx | 16 +- src/sage/matrix/matrix_gap.pxd | 10 + src/sage/matrix/matrix_gap.pyx | 395 ++++++++++++++++++++++++++++++++ src/sage/matrix/matrix_space.py | 6 + 5 files changed, 423 insertions(+), 7 deletions(-) create mode 100644 src/sage/matrix/matrix_gap.pxd create mode 100644 src/sage/matrix/matrix_gap.pyx diff --git a/src/module_list.py b/src/module_list.py index 911d598ecfd..ea809f241f0 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -757,6 +757,9 @@ def uname_specific(name, value, alternative): language = "c++", libraries=['ntl']), + Extension('sage.matrix.matrix_gap', + sources = ['sage/matrix/matrix_gap.pyx']), + Extension('sage.matrix.matrix_dense', sources = ['sage/matrix/matrix_dense.pyx']), diff --git a/src/sage/matrix/action.pyx b/src/sage/matrix/action.pyx index 2b07075d979..6abe4438015 100644 --- a/src/sage/matrix/action.pyx +++ b/src/sage/matrix/action.pyx @@ -157,22 +157,24 @@ cdef class MatrixMatrixAction(MatrixMulAction): sage: M1 = MatrixSpace(ZZ, 2, implementation='flint') sage: M2 = MatrixSpace(ZZ, 2, implementation='generic') - sage: M3 = MatrixSpace(ZZ, 2, sparse=True) - sage: M = [M1, M2, M3] + sage: M3 = MatrixSpace(ZZ, 2, implementation='gap') + sage: M4 = MatrixSpace(ZZ, 2, sparse=True) + sage: M = [M1, M2, M3, M4] sage: coercions = '' - sage: for i in range(3): - ....: for j in range(3): + sage: for M1 in M: + ....: for M2 in M: ....: try: - ....: s = M[i].an_element() * M[j].an_element() + ....: s = M1.an_element() * M2.an_element() ....: coercions += 'X' ....: except TypeError: ....: coercions += ' ' ....: coercions += '\n' sage: print(coercions) - X X + X X X - X X + X + X X """ if not is_MatrixSpace(S): raise TypeError("Not a matrix space: %s" % S) diff --git a/src/sage/matrix/matrix_gap.pxd b/src/sage/matrix/matrix_gap.pxd new file mode 100644 index 00000000000..d61f6918f7e --- /dev/null +++ b/src/sage/matrix/matrix_gap.pxd @@ -0,0 +1,10 @@ +from .matrix_dense cimport Matrix_dense +from sage.libs.gap.element cimport GapElement +from sage.groups.libgap_wrapper cimport ElementLibGAP + +cdef class Matrix_gap(Matrix_dense): + cdef GapElement _libgap + + cpdef GapElement gap(self) + cdef Matrix_gap _new(self, Py_ssize_t nrows, Py_ssize_t ncols) + diff --git a/src/sage/matrix/matrix_gap.pyx b/src/sage/matrix/matrix_gap.pyx new file mode 100644 index 00000000000..211b0f52aad --- /dev/null +++ b/src/sage/matrix/matrix_gap.pyx @@ -0,0 +1,395 @@ +r""" +Wrappers on GAP matrices +""" +#***************************************************************************** +# Copyright (C) 2017 Vincent Delecroix <20100.delecroix@gmail.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from __future__ import print_function, absolute_import + +from sage.libs.gap.libgap import libgap +from . import matrix_space +cimport sage.structure.element + +cdef class Matrix_gap(Matrix_dense): + r""" + A Sage matrix wrapper over a GAP matrix. + + EXAMPLES:: + + sage: M = MatrixSpace(ZZ, 2, implementation='gap') + sage: m1 = M([1, 0, 2, -3]) + sage: m2 = M([2, 2, 5, -1]) + sage: type(m1) + + + sage: m1 * m2 + [ 2 2] + [-11 7] + sage: type(m1 * m2) + + + sage: M = MatrixSpace(QQ, 5, 3, implementation='gap') + sage: m = M(range(15)) + sage: m.left_kernel() + Vector space of degree 5 and dimension 3 over Rational Field + Basis matrix: + [ 1 0 0 -4 3] + [ 0 1 0 -3 2] + [ 0 0 1 -2 1] + + sage: M = MatrixSpace(ZZ, 10, implementation='gap') + sage: m = M(range(100)) + sage: m.transpose().parent() is M + True + + TESTS:: + + sage: M = MatrixSpace(ZZ, 2, implementation='gap') + sage: TestSuite(M).run() + + sage: M = MatrixSpace(ZZ, 2, 3, implementation='gap') + sage: TestSuite(M).run() + + sage: M = MatrixSpace(QQ, 3, implementation='gap') + sage: TestSuite(M).run() + + sage: M = MatrixSpace(QQ, 3, 2, implementation='gap') + sage: TestSuite(M).run() + """ + def __init__(self, parent, entries, coerce, copy): + r""" + TESTS:: + + sage: M = MatrixSpace(ZZ, 2, implementation='gap') + sage: M(0) + [0 0] + [0 0] + sage: M(1) + [1 0] + [0 1] + sage: M(2) + [2 0] + [0 2] + sage: type(M(0)) + + sage: type(M(1)) + + sage: type(M(2)) + + + sage: M = MatrixSpace(QQ, 2, 3, implementation='gap') + sage: M(0) + [0 0 0] + [0 0 0] + sage: M(1) + Traceback (most recent call last): + ... + TypeError: identity matrix must be square + + sage: MatrixSpace(QQ, 1, 2)(0) + [0 0] + sage: MatrixSpace(QQ, 2, 1)(0) + [0] + [0] + """ + Matrix_dense.__init__(self, parent) + + R = self._base_ring + + if isinstance(entries, (tuple, list)): + entries = [[R(x) for x in entries[i * self._ncols: (i+1) * self._ncols]] for i in range(self._nrows)] + else: + zero = R.zero() + if entries is None: + elt = zero + else: + elt = R(entries) + entries = [[zero] * self._ncols for i in range(self._nrows)] + if not elt.is_zero(): + if self._nrows != self._ncols: + raise TypeError('non diagonal matrices can not be initialized from a scalar') + for i in range(self._nrows): + entries[i][i] = elt + + self._libgap = libgap(entries) + + cdef Matrix_gap _new(self, Py_ssize_t nrows, Py_ssize_t ncols): + if nrows == self._nrows and ncols == self._ncols: + P = self._parent + else: + P = self.matrix_space(nrows, ncols) + + cdef Matrix_gap M = Matrix_gap.__new__(Matrix_gap, P, None, None, None) + Matrix_dense.__init__(M, P) + return M + + def __copy__(self): + r""" + TESTS:: + + sage: M = MatrixSpace(QQ, 2, implementation='gap') + sage: m1 = M([1,2,0,3]) + sage: m2 = m1.__copy__() + sage: m2 + [1 2] + [0 3] + sage: m1[0,1] = -2 + sage: m1 + [ 1 -2] + [ 0 3] + sage: m2 + [1 2] + [0 3] + """ + cdef Matrix_gap M = self._new(self._nrows, self._ncols) + M._libgap = self._libgap.deepcopy(1) + return M + + def __reduce__(self): + r""" + TESTS:: + + sage: M = MatrixSpace(ZZ, 2, implementation='gap') + sage: m = M([1,2,1,2]) + sage: loads(dumps(m)) == m + True + """ + return self._parent, (self.list(),) + + cpdef GapElement gap(self): + r""" + Return the underlying gap object. + + EXAMPLES:: + + sage: M = MatrixSpace(ZZ, 2, implementation='gap') + sage: m = M([1,2,2,1]).gap() + sage: m + [ [ 1, 2 ], [ 2, 1 ] ] + sage: type(m) + + + sage: m.MatrixAutomorphisms() + Group([ (1,2) ]) + """ + return self._libgap + + cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j): + return self._base_ring(self._libgap[i,j]) + + cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, object x): + r""" + TESTS:: + + sage: M = MatrixSpace(ZZ, 2, implementation='gap') + sage: m = M(0) + sage: m[0,1] = 13 + sage: m + [ 0 13] + [ 0 0] + sage: m[1,0] = -1/2 + Traceback (most recent call last): + ... + TypeError: no conversion of this rational to integer + """ + self._libgap[i,j] = x + + cpdef _richcmp_(self, other, int op): + r""" + Compare ``self`` and ``right``. + + EXAMPLES:: + + sage: M = MatrixSpace(ZZ, 2, implementation='gap') + sage: m1 = M([1,2,3,4]) + sage: m2 = M([1,2,3,4]) + sage: m3 = M([1,2,0,4]) + sage: m1 == m2 + True + sage: m1 != m2 + False + sage: m1 == m3 + False + sage: m1 != m3 + True + + sage: M = MatrixSpace(QQ, 2, implementation='gap') + sage: m1 = M([1/2, 1/3, 2, -5]) + sage: m2 = M([1/2, 1/3, 2, -5]) + sage: m3 = M([1/2, 0, 2, -5]) + sage: m1 == m2 + True + sage: m1 != m2 + False + sage: m1 == m3 + False + sage: m1 != m3 + True + + sage: UCF = UniversalCyclotomicField() + sage: M = MatrixSpace(UCF, 2, implementation='gap') + sage: m1 = M([E(2), E(3), 0, E(4)]) + sage: m2 = M([E(2), E(3), 0, E(4)]) + sage: m3 = M([E(2), E(3), 0, E(5)]) + sage: m1 == m2 + True + sage: m1 != m2 + False + sage: m1 == m3 + False + sage: m1 != m3 + True + """ + return ( self)._libgap._richcmp_(( other)._libgap, op) + + def __neg__(self): + r""" + TESTS:: + + sage: M = MatrixSpace(ZZ, 2, 3, implementation='gap') + sage: m = M([1, -1, 3, 2, -5, 1]) + sage: -m + [-1 1 -3] + [-2 5 -1] + """ + cdef Matrix_gap M = self._new(self._nrows, self._ncols) + M._libgap = self._libgap.AdditiveInverse() + return M + + def __invert__(self): + r""" + TESTS:: + + sage: M = MatrixSpace(QQ, 2) + sage: ~M([4,2,2,2]) + [ 1/2 -1/2] + [-1/2 1] + """ + cdef Matrix_gap M + if self._base_ring.is_field(): + M = self._new(self._nrows, self._ncols) + M._libgap = self._libgap.Inverse() + return M + else: + return Matrix_dense.__invert__(self) + + + cpdef _add_(left, right): + r""" + TESTS:: + + sage: M = MatrixSpace(ZZ, 2, 3, implementation='gap') + sage: M([1,2,3,4,3,2]) + M([1,1,1,1,1,1]) == M([2,3,4,5,4,3]) + True + """ + cdef Matrix_gap cleft = left + cdef Matrix_gap ans = cleft._new(cleft._nrows, cleft._ncols) + ans._libgap = left._libgap + ( right)._libgap + return ans + + cpdef _sub_(left, right): + r""" + TESTS:: + + sage: M = MatrixSpace(ZZ, 2, 3, implementation='gap') + sage: M([1,2,3,4,3,2]) - M([1,1,1,1,1,1]) == M([0,1,2,3,2,1]) + True + """ + cdef Matrix_gap cleft = left + cdef Matrix_gap ans = cleft._new(cleft._nrows, cleft._ncols) + ans._libgap = left._libgap - ( right)._libgap + return ans + + cdef sage.structure.element.Matrix _matrix_times_matrix_(left, sage.structure.element.Matrix right): + r""" + TESTS:: + + sage: M = MatrixSpace(QQ, 2, implementation='gap') + sage: m1 = M([1,2,-4,3]) + sage: m2 = M([-1,1,1,-1]) + sage: m1 * m2 + [ 1 -1] + [ 7 -7] + """ + if left._ncols != right._nrows: + raise IndexError("Number of columns of self must equal number of rows of right.") + cdef Matrix_gap M = left._new(left._nrows, right._ncols) + M._libgap = (( left)._libgap * ( right)._libgap) + return M + + def determinant(self): + r""" + Return the determinant of this matrix. + + EXAMPLES:: + + sage: M = MatrixSpace(ZZ, 2, implementation='gap') + sage: M([2, 1, 1, 1]).determinant() + 1 + sage: M([2, 1, 3, 3]).determinant() + 3 + + TESTS:: + + sage: M = MatrixSpace(ZZ, 1, implementation='gap') + sage: parent(M(1).determinant()) + Integer Ring + + sage: M = MatrixSpace(QQ, 1, implementation='gap') + sage: parent(M(1).determinant()) + Rational Field + + sage: M = MatrixSpace(UniversalCyclotomicField(), 1, implementation='gap') + sage: parent(M(1).determinant()) + Universal Cyclotomic Field + """ + return self._base_ring(self._libgap.Determinant()) + + def trace(self): + r""" + Return the trace of this matrix. + + EXAMPLES:: + + sage: M = MatrixSpace(ZZ, 2, implementation='gap') + sage: M([2, 1, 1, 1]).trace() + 3 + sage: M([2, 1, 3, 3]).trace() + 5 + + TESTS:: + + sage: M = MatrixSpace(ZZ, 1, implementation='gap') + sage: parent(M(1).trace()) + Integer Ring + + sage: M = MatrixSpace(QQ, 1, implementation='gap') + sage: parent(M(1).trace()) + Rational Field + + sage: M = MatrixSpace(UniversalCyclotomicField(), 1, implementation='gap') + sage: parent(M(1).trace()) + Universal Cyclotomic Field + """ + return self._base_ring(self._libgap.Trace()) + + def rank(self): + r""" + Return the rank of this matrix. + + EXAMPLES:: + + sage: M = MatrixSpace(ZZ, 2, implementation='gap') + sage: M([2, 1, 1, 1]).rank() + 2 + sage: M([2, 1, 4, 2]).rank() + 1 + """ + return int(self._libgap.Rank()) diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index d621c106c6a..9c549eb58d0 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -133,6 +133,8 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation): sage: get_matrix_class(ZZ, 3, 3, False, 'flint') + sage: get_matrix_class(ZZ, 3, 3, False, 'gap') + sage: get_matrix_class(ZZ, 3, 3, False, 'generic') @@ -189,6 +191,10 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation): if implementation == 'generic': return matrix_generic_dense.Matrix_generic_dense + elif implementation == 'gap': + from .matrix_gap import Matrix_gap + return Matrix_gap + if R is sage.rings.integer_ring.ZZ: if implementation is None or implementation == 'flint': return matrix_integer_dense.Matrix_integer_dense From ac406955c714a58efe4b6a4c4a6c425769e6c987 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 31 Jan 2018 00:11:51 +0100 Subject: [PATCH 574/740] 23714: gap matrices over finite fields --- src/sage/libs/gap/element.pyx | 18 ++++++++++++++++++ src/sage/matrix/matrix_space.py | 14 ++++++++++++++ .../rings/finite_rings/integer_mod_ring.py | 7 +++++++ 3 files changed, 39 insertions(+) diff --git a/src/sage/libs/gap/element.pyx b/src/sage/libs/gap/element.pyx index e6f1e483925..bd6074fb0fa 100644 --- a/src/sage/libs/gap/element.pyx +++ b/src/sage/libs/gap/element.pyx @@ -1549,6 +1549,24 @@ cdef class GapElement_FiniteField(GapElement): exp = self.LogFFE(gap_field.PrimitiveRoot()) return ring.multiplicative_generator() ** exp.sage() + def __int__(self): + r""" + TESTS:: + + sage: int(libgap.eval("Z(53)")) + 2 + """ + return int(self.Int()) + + def _integer_(self, R): + r""" + TESTS:: + + sage: ZZ(libgap.eval("Z(53)")) + 2 + """ + return R(self.Int()) + ############################################################################ ### GapElement_Cyclotomic ##################################################### diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index 9c549eb58d0..da08edf1b08 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -417,6 +417,20 @@ class MatrixSpace(UniqueRepresentation, parent_gens.ParentWithGens): sage: all(((A.get_action(B) is not None) == (A is B)) for A in [M1,M2] for B in [M1,M2]) True + + Check that libgap matrices over finite fields are working properly:: + + sage: M2 = MatrixSpace(GF(2), 5, implementation='gap') + sage: M2.one() + [1 0 0 0 0] + [0 1 0 0 0] + [0 0 1 0 0] + [0 0 0 1 0] + [0 0 0 0 1] + sage: m = M2.random_element() + sage: M1 = MatrixSpace(GF(2), 5) + sage: M1(m * m) == M1(m) * M1(m) + True """ _no_generic_basering_coercion = True diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py index a10da08dc06..06b827a593f 100644 --- a/src/sage/rings/finite_rings/integer_mod_ring.py +++ b/src/sage/rings/finite_rings/integer_mod_ring.py @@ -1156,6 +1156,13 @@ def _element_constructor_(self, x): sage: a == R(gap(a)) True + libgap interface (:trac:`23714`):: + + sage: a = libgap.eval("Z(13)^2") + sage: a.sage() + 4 + sage: libgap(a.sage()) == a + True """ try: return integer_mod.IntegerMod(self, x) From 6cc7824743549205e46d358cf5b1045ed38eae79 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Tue, 30 Jan 2018 15:33:12 -0800 Subject: [PATCH 575/740] Minor changes in PrimedEntry docs --- src/sage/combinat/shifted_primed_tableau.py | 52 +++++++++++---------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index c7f127e9212..a0c1ac875d0 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -165,8 +165,8 @@ def _preprocess(T, skew=None): TESTS:: - sage: t = ShiftedPrimedTableau([[None, "2'", "3p", 3.5]]) # indirect doctest - sage: t + sage: ShiftedPrimedTableau._preprocess([["2'", "3p", 3.5]], + ....: skew=[1]) [(None, 2', 3', 4')] """ if isinstance(T, ShiftedPrimedTableau): @@ -903,6 +903,9 @@ class PrimedEntry(SageObject): An entry in a shifted primed tableau is an element in the alphabet `\{1' < 1 < 2' < 2 < \cdots < n' < n\}`. + The difference between two elements `i` and `i-1` counts as a + whole unit, whereas the difference between `i` and `i'` counts + as half a unit. Internally, we represent an unprimed element `x` as `2x` and the primed elements as the corresponding odd integer that respects the total order. @@ -929,6 +932,10 @@ def __init__(self, entry=None, double=None): sage: a = PrimedEntry(2.5) sage: PrimedEntry(a) 3' + sage: PrimedEntry(None) + Traceback (most recent call last): + .... + ValueError: primed entry must not be None """ # store primed numbers as odd, unprimed numbers as even integers if isinstance(entry, self.__class__): @@ -982,6 +989,9 @@ def __repr__(self): def integer(self): """ + Return the corresponding integer `i` for primed entries + of the form `i` or `i'`. + TESTS:: sage: from sage.combinat.shifted_primed_tableau import PrimedEntry @@ -1035,11 +1045,7 @@ def __lt__(self, other): sage: a < b True """ - try: - other = PrimedEntry(other) - except ValueError: - raise ValueError("both elements must be primed entries") - return self._entry < other._entry + return self._entry < PrimedEntry(other)._entry def __le__(self, other): """ @@ -1051,11 +1057,7 @@ def __le__(self, other): sage: a <= b True """ - try: - other = PrimedEntry(other) - except ValueError: - raise ValueError("both elements must be primed entries") - return self._entry <= other._entry + return self._entry <= PrimedEntry(other)._entry def __gt__(self, other): """ @@ -1067,11 +1069,7 @@ def __gt__(self, other): sage: b > a True """ - try: - other = PrimedEntry(other) - except ValueError: - raise ValueError("both elements must be primed entries") - return self._entry > other._entry + return self._entry > PrimedEntry(other)._entry def __ge__(self, other): """ @@ -1083,11 +1081,7 @@ def __ge__(self, other): sage: a <= b True """ - try: - other = PrimedEntry(other) - except ValueError: - raise ValueError("both elements must be primed entries") - return self._entry >= other._entry + return self._entry >= PrimedEntry(other)._entry def is_unprimed(self): """ @@ -1149,6 +1143,8 @@ def primed(self): def increase_half(self): """ + Increase ``self`` by half a unit. + TESTS:: sage: from sage.combinat.shifted_primed_tableau import PrimedEntry @@ -1160,6 +1156,8 @@ def increase_half(self): def decrease_half(self): """ + Decrease ``self`` by half a unit. + TESTS:: sage: from sage.combinat.shifted_primed_tableau import PrimedEntry @@ -1171,6 +1169,8 @@ def decrease_half(self): def increase_one(self): """ + Increase ``self`` by one unit. + TESTS:: sage: from sage.combinat.shifted_primed_tableau import PrimedEntry @@ -1182,6 +1182,8 @@ def increase_one(self): def decrease_one(self): """ + Decrease ``self`` by one unit. + TESTS:: sage: from sage.combinat.shifted_primed_tableau import PrimedEntry @@ -1433,15 +1435,15 @@ def _contains_tableau(self, T): TESTS:: sage: Tabs = ShiftedPrimedTableaux() - sage: tab = ShiftedPrimedTableau([])._preprocess( + sage: tab = ShiftedPrimedTableau._preprocess( ....: [[1,"2p","3p","3p"]]) sage: tab [(1, 2', 3', 3')] sage: Tabs._contains_tableau(tab) False sage: Tabs = ShiftedPrimedTableaux(skew=[1]) - sage: tab = ShiftedPrimedTableau([])._preprocess( - ....: [[None,"2p","3p",3]], skew=[1]) + sage: tab = ShiftedPrimedTableau._preprocess( + ....: [["2p","3p",3]], skew=[1]) sage: tab [(None, 2', 3', 3)] sage: Tabs._contains_tableau(tab) From 85353ab927b2b9b01b91aa4a26d5b16936360b9e Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Tue, 30 Jan 2018 17:52:27 -0800 Subject: [PATCH 576/740] More minor fixes --- src/sage/combinat/shifted_primed_tableau.py | 36 ++++++++++++--------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index a0c1ac875d0..5fd0c09dab2 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -1078,8 +1078,8 @@ def __ge__(self, other): sage: from sage.combinat.shifted_primed_tableau import PrimedEntry sage: a = PrimedEntry(2) sage: b = PrimedEntry("3p") - sage: a <= b - True + sage: a >= b + False """ return self._entry >= PrimedEntry(other)._entry @@ -1659,10 +1659,11 @@ def _contains_tableau(self, T): """ TESTS:: - sage: [[1,'2p',2,2],[2,'3p']] in ShiftedPrimedTableaux([4,2], - ....: max_entry=4) + sage: t = ShiftedPrimedTableau._preprocess([[1,'2p',2,2],[2,'3p']]) + sage: ShiftedPrimedTableaux([4,2],max_entry=4)._contains_tableau(t) True - sage: [[1,'2p',2],[2,'3p']] in ShiftedPrimedTableaux([4,2]) + sage: s = ShiftedPrimedTableau._preprocess([[1,'2p',2],[2,'3p']]) + sage: ShiftedPrimedTableaux([4,2])._contains_tableau(s) False """ if not super(ShiftedPrimedTableaux_shape, self)._contains_tableau(T): @@ -1770,15 +1771,17 @@ def _contains_tableau(self, T): Check if ``self`` contains preprocessed tableau ``T``. TESTS:: - - sage: [[1,1.5],[2]] in ShiftedPrimedTableaux(weight=(1,2)) + sage: t = ShiftedPrimedTableau._preprocess([[1,1.5],[2]]) + sage: ShiftedPrimedTableaux(weight=(1,2))._contains_tableau(t) True - sage: [[1,1.5],[3]] in ShiftedPrimedTableaux(weight=(1,2)) + sage: s = ShiftedPrimedTableau._preprocess([[1,1.5],[3]]) + sage: ShiftedPrimedTableaux(weight=(1,2))._contains_tableau(s) False - sage: [] in ShiftedPrimedTableaux(weight=()) + sage: u = ShiftedPrimedTableau._preprocess([]) + sage: ShiftedPrimedTableaux(weight=())._contains_tableau(u) True - sage: [] in ShiftedPrimedTableaux(weight=(1,2)) + sage: ShiftedPrimedTableaux(weight=(1,2))._contains_tableau(u) False """ if not super(ShiftedPrimedTableaux_weight, self)._contains_tableau(T): @@ -1864,16 +1867,19 @@ def _contains_tableau(self, T): TESTS:: - sage: [[1,1.5],[2]] in ShiftedPrimedTableaux([2,1], weight=(1,2)) + sage: t = ShiftedPrimedTableau._preprocess([[1,1.5],[2]]) + sage: ShiftedPrimedTableaux([2,1], weight=(1,2))._contains_tableau(t) True - sage: [[1,1.5],[3]] in ShiftedPrimedTableaux([2,1], weight=(1,2)) + sage: ShiftedPrimedTableaux([2,1], weight=(2,1))._contains_tableau(t) False - sage: [[1,1.5,2,3],[3]] in ShiftedPrimedTableaux([3,2], weight=(1,2,2)) + sage: s = ShiftedPrimedTableau._preprocess([[1,1.5,2,3],[3]]) + sage: ShiftedPrimedTableaux([3,2], weight=(1,2,2))._contains_tableau(s) False - sage: [] in ShiftedPrimedTableaux([3,2], weight=(1,2,2)) + sage: u = ShiftedPrimedTableau._preprocess([]) + sage: ShiftedPrimedTableaux([3,2], weight=(1,2,2))._contains_tableau(u) False - sage: [] in ShiftedPrimedTableaux([], weight=()) + sage: ShiftedPrimedTableaux([], weight=())._contains_tableau(u) True """ if not super(ShiftedPrimedTableaux_weight_shape, self)._contains_tableau(T): From a5edbfe57746cc1ea0b876aeb9e13b07231955b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Wed, 31 Jan 2018 15:14:00 +1300 Subject: [PATCH 577/740] Change the startup test for numpy and pyparsing now that they are imported in the doctesting framework via matplotlib. --- src/sage/tests/startup.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/sage/tests/startup.py b/src/sage/tests/startup.py index 0d0ddd87321..a7053b63b72 100644 --- a/src/sage/tests/startup.py +++ b/src/sage/tests/startup.py @@ -3,10 +3,6 @@ EXAMPLES:: - sage: 'numpy' in sys.modules - False - sage: 'pyparsing' in sys.modules - False sage: 'sage.libs.gap.libgap' in sys.modules False @@ -18,4 +14,17 @@ sage: cmd = "from sage.all import *\nprint('IPython' in sys.modules)\n" sage: print(test_executable(["sage", "--python"], cmd)[0]) # long time False + +Check that numpy (:trac:`11714`) and pyparsing are not imported on startup +as they increase the startup time. Since :trac:`23696` those are imported +by the doctest framework via a matplotlib import. Again the simple test would +not work, but this time we don't have to avoid to load ipython. + + sage: from sage.tests.cmdline import test_executable + sage: cmd = "print('numpy' in sys.modules)\n" + sage: print(test_executable(["sage", "-c"], cmd)[0]) # long time + False + sage: cmd = "print('pyparsing' in sys.modules)\n" + sage: print(test_executable(["sage", "-c"], cmd)[0]) # long time + False """ From 8474ea3c302e87aceb0b6362ce9e183e3ebf6b1a Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Wed, 31 Jan 2018 10:36:10 +0100 Subject: [PATCH 578/740] Fix up 75c75e9 (#24285) --- src/sage/rings/complex_arb.pyx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/complex_arb.pyx b/src/sage/rings/complex_arb.pyx index df7b9bbae78..dac707058c9 100644 --- a/src/sage/rings/complex_arb.pyx +++ b/src/sage/rings/complex_arb.pyx @@ -117,6 +117,11 @@ Check that :trac:`19839` is fixed:: sage: log(SR(CBF(0.42))).pyobject().parent() Complex ball field with 53 bits of precision +:trac:`24621`:: + + sage: CBF(NumberField(polygen(QQ, 'y')^3 + 20, 'a', embedding=CC(1.35,2.35)).gen()) + [1.357208808297453 +/- 4.96e-16] + [2.350754612451197 +/- 7.67e-16]*I + Classes and Methods =================== """ @@ -135,6 +140,7 @@ import operator from cysignals.signals cimport sig_on, sig_str, sig_off, sig_error import sage.categories.fields +import sage.rings.number_field.number_field as number_field cimport sage.rings.integer cimport sage.rings.rational @@ -457,16 +463,14 @@ class ComplexBallField(UniqueRepresentation, Field): return other._prec >= self._prec elif isinstance(other, ComplexBallField): return other._prec >= self._prec + elif isinstance(other, number_field.NumberField_quadratic): + emb = other.coerce_embedding() + return emb is not None and self.has_coerce_map_from(emb.codomain()) from sage.rings.all import QQ, AA, QQbar, RLF, CLF if other in [AA, QQbar, RLF, CLF]: return True - from sage.rings.number_field.number_field_base import is_NumberField - if is_NumberField(other): - emb = other.coerce_embedding() - return emb is not None and self.has_coerce_map_from(emb.codomain()) - def _element_constructor_(self, x=None, y=None): r""" Convert (x, y) to an element of this complex ball field, perhaps From 9440ff4f3e83f14cbf7f58a78837e5292c94e69c Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 31 Jan 2018 14:55:21 +0100 Subject: [PATCH 579/740] Move new_t_POL_from_int_star from cypari2 to Sage --- src/sage/libs/pari/misc.pxd | 3 +++ src/sage/libs/pari/misc.pyx | 25 +++++++++++++++++++++ src/sage/rings/number_field/totallyreal.pyx | 3 ++- 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/sage/libs/pari/misc.pxd create mode 100644 src/sage/libs/pari/misc.pyx diff --git a/src/sage/libs/pari/misc.pxd b/src/sage/libs/pari/misc.pxd new file mode 100644 index 00000000000..ae89aff0b84 --- /dev/null +++ b/src/sage/libs/pari/misc.pxd @@ -0,0 +1,3 @@ +from cypari2.gen cimport Gen + +cdef Gen new_t_POL_from_int_star(int* vals, unsigned long length, long varnum) diff --git a/src/sage/libs/pari/misc.pyx b/src/sage/libs/pari/misc.pyx new file mode 100644 index 00000000000..1ed774d417c --- /dev/null +++ b/src/sage/libs/pari/misc.pyx @@ -0,0 +1,25 @@ +from cysignals.signals cimport sig_on +from cypari2.paridecl cimport * +from cypari2.stack cimport new_gen + + +cdef Gen new_t_POL_from_int_star(int* vals, unsigned long length, long varnum): + """ + Convert an array of ints to a PARI polynomial. + + Note that ``length = degree + 1``, so that recognizing 0 is easier. + """ + cdef GEN z + cdef unsigned long i + + sig_on() + z = cgetg(length + 2, t_POL) + if length == 0: + # Polynomial is zero + z[1] = evalvarn(varnum) + evalsigne(0) + else: + z[1] = evalvarn(varnum) + evalsigne(1) + for i in range(length): + set_gel(z, i+2, stoi(vals[i])) + + return new_gen(z) diff --git a/src/sage/rings/number_field/totallyreal.pyx b/src/sage/rings/number_field/totallyreal.pyx index 9414c9923d2..2576937e7ef 100644 --- a/src/sage/rings/number_field/totallyreal.pyx +++ b/src/sage/rings/number_field/totallyreal.pyx @@ -116,7 +116,7 @@ import sys from sage.libs.gmp.mpz cimport * from sage.libs.pari.all import pari from cypari2.gen cimport Gen as pari_gen -from cypari2.convert cimport new_t_POL_from_int_star +from sage.libs.pari.misc cimport new_t_POL_from_int_star from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.integer import Integer @@ -173,6 +173,7 @@ cpdef double odlyzko_bound_totallyreal(int n): dB = 33.9508 return dB + def enumerate_totallyreal_fields_prim(n, B, a = [], verbose=0, return_seqs=False, phc=False, keep_fields=False, t_2=False, just_print=False, From c6e6f3d6e08dc66a3922d64cf96a18eaf55e1d5d Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Fri, 1 Dec 2017 14:55:11 +0100 Subject: [PATCH 580/740] polynomial_complex_arb: use acb_poly_taylor_shift in compose_trunc --- .../rings/polynomial/polynomial_complex_arb.pyx | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_complex_arb.pyx b/src/sage/rings/polynomial/polynomial_complex_arb.pyx index 0aee01b3d9f..6e086d40165 100644 --- a/src/sage/rings/polynomial/polynomial_complex_arb.pyx +++ b/src/sage/rings/polynomial/polynomial_complex_arb.pyx @@ -674,7 +674,7 @@ cdef class Polynomial_complex_arb(Polynomial): return self(other).truncate(n) cdef Polynomial_complex_arb other1 = other cdef Polynomial_complex_arb res = self._new() - cdef acb_poly_t self_ts, other_ts, lin + cdef acb_poly_t self_ts, other_ts cdef acb_ptr cc if acb_poly_length(other1.__poly) > 0: cc = acb_poly_get_coeff_ptr(other1.__poly, 0) @@ -683,20 +683,11 @@ cdef class Polynomial_complex_arb(Polynomial): try: acb_poly_init(self_ts) acb_poly_init(other_ts) - ### Not yet supported in sage's version of arb - #acb_poly_taylor_shift(self_ts, self.__poly, cc, prec(self)) - acb_poly_init(lin) - acb_poly_set_coeff_acb(lin, 0, cc) - acb_poly_set_coeff_si(lin, 1, 1) - acb_poly_compose(self_ts, self.__poly, lin, prec(self)) - ### + acb_poly_taylor_shift(self_ts, self.__poly, cc, prec(self)) acb_poly_set(other_ts, other1.__poly) acb_zero(acb_poly_get_coeff_ptr(other_ts, 0)) acb_poly_compose_series(res.__poly, self_ts, other_ts, n, prec(self)) finally: - ### - acb_poly_clear(lin) - ### acb_poly_clear(other_ts) acb_poly_clear(self_ts) sig_off() From 65e1c3b6b2bad5075b5cf432c4846850ad945ba7 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Wed, 31 Jan 2018 17:28:40 +0100 Subject: [PATCH 581/740] polynomial_complex_arb: scalar multiplication --- .../polynomial/polynomial_complex_arb.pyx | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/sage/rings/polynomial/polynomial_complex_arb.pyx b/src/sage/rings/polynomial/polynomial_complex_arb.pyx index 6e086d40165..2ae92a1b9e4 100644 --- a/src/sage/rings/polynomial/polynomial_complex_arb.pyx +++ b/src/sage/rings/polynomial/polynomial_complex_arb.pyx @@ -28,6 +28,7 @@ from cysignals.signals cimport sig_on, sig_off from sage.libs.arb.acb cimport * from sage.rings.integer cimport Integer, smallInteger from sage.rings.complex_arb cimport ComplexBall +from sage.structure.element cimport Element from sage.rings.complex_arb import ComplexBallField from sage.structure.element import coerce_binop @@ -320,6 +321,30 @@ cdef class Polynomial_complex_arb(Polynomial): sig_off() return res + cpdef _lmul_(self, Element a): + r""" + TESTS:: + + sage: Pol. = CBF[] + sage: (x + 1)._lmul_(CBF(3)) + 3.000000000000000*x + 3.000000000000000 + """ + cdef Polynomial_complex_arb res = self._new() + sig_on() + acb_poly_scalar_mul(res.__poly, self.__poly, ( a).value, prec(self)) + sig_off() + return res + + cpdef _rmul_(self, Element a): + r""" + TESTS:: + + sage: Pol. = CBF[] + sage: (x + 1)._rmul_(CBF(3)) + 3.000000000000000*x + 3.000000000000000 + """ + return self._lmul_(a) + @coerce_binop def quo_rem(self, divisor): r""" From 5df54d44777b6855ed6c127c6eb7b65211bc9242 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Wed, 31 Jan 2018 17:26:58 +0000 Subject: [PATCH 582/740] Address some review comments: * Fix the documentation formatting a bit. * Add some examples of using the encoding argument, as well as testing the code path where an invalid argument is passed to io.open() * Also changed io.open() to pass the filename instead of the file descriptor (this was a minor regression, since the existing code expected to be able to get the name of the tempfile). Once the io.open() call succeeds we can close the FD returned by mkstemp(). --- src/sage/misc/temporary_file.py | 39 ++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/sage/misc/temporary_file.py b/src/sage/misc/temporary_file.py index e98a66d912b..25478823a31 100644 --- a/src/sage/misc/temporary_file.py +++ b/src/sage/misc/temporary_file.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Temporary file handling @@ -244,7 +245,7 @@ class atomic_write(object): underlying file is opened in binary mode. If False then it is opened in text mode and an encoding with which to write the file may be supplied. - - ``kwargs`` -- additional keyword arguments passed to the underlying + - ``**kwargs`` -- additional keyword arguments passed to the underlying `io.open` call. EXAMPLES:: @@ -348,6 +349,31 @@ class atomic_write(object): sage: with open(target_file, 'r') as f: ....: f.read() '>>> AAA' + + Supplying an encoding means we're writing the file in "text mode" (in the + same sense as `io.open`) and so we must write unicode strings:: + + sage: target_file = tmp_filename() + sage: with atomic_write(target_file, binary=False, + ....: encoding='utf-8') as f: + ....: _ = f.write(u'Hélas') + sage: import io + sage: with io.open(target_file, encoding='utf-8') as f: + ....: print(f.read()) + Hélas + + Supplying an encoding in binary mode (or other arguments that don't + make sense to `io.open` in binary mode) is an error:: + + sage: writer = atomic_write(target_file, binary=True, + ....: encoding='utf-8') + sage: with writer as f: + ....: _ = f.write(u'Hello') + Traceback (most recent call last): + ... + ValueError: binary mode doesn't take an encoding argument + sage: os.path.exists(writer.tempname) + False """ def __init__(self, target_filename, append=False, mode=0o666, binary=None, **kwargs): @@ -396,21 +422,21 @@ def __enter__(self): """ fd, name = tempfile.mkstemp(dir=self.tmpdir) - name = os.path.abspath(name) + self.tempname = os.path.abspath(name) rmode = 'r' + ('b' if self.binary else '') wmode = 'w+' + ('b' if self.binary else '') try: - self.tempfile = io.open(fd, wmode, **self.kwargs) + self.tempfile = io.open(name, wmode, **self.kwargs) except (TypeError, ValueError): # Some invalid arguments were passed to io.open - os.close(fd) os.unlink(name) raise + finally: + os.close(fd) - self.tempname = name - os.chmod(self.tempname, self.mode) + os.chmod(name, self.mode) if self.append: try: with io.open(self.target, rmode, **self.kwargs) as f: @@ -419,6 +445,7 @@ def __enter__(self): pass else: self.tempfile.write(r) + return self.tempfile def __exit__(self, exc_type, exc_val, exc_tb): From d6dd6a0cc8bc231dacc8eb98c935db56f8fea459 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Wed, 31 Jan 2018 17:29:05 +0100 Subject: [PATCH 583/740] polynomial_complex_arb: revert_series() Also add a stub method to the root Polynomial class for documentation purposes. --- .../polynomial/polynomial_complex_arb.pyx | 45 +++++++++++++++++++ .../rings/polynomial/polynomial_element.pyx | 23 ++++++++++ 2 files changed, 68 insertions(+) diff --git a/src/sage/rings/polynomial/polynomial_complex_arb.pyx b/src/sage/rings/polynomial/polynomial_complex_arb.pyx index 2ae92a1b9e4..21ffe1c553f 100644 --- a/src/sage/rings/polynomial/polynomial_complex_arb.pyx +++ b/src/sage/rings/polynomial/polynomial_complex_arb.pyx @@ -328,6 +328,12 @@ cdef class Polynomial_complex_arb(Polynomial): sage: Pol. = CBF[] sage: (x + 1)._lmul_(CBF(3)) 3.000000000000000*x + 3.000000000000000 + sage: (1 + x)*(1/3) + ([0.3333333333333333 +/- 7.04e-17])*x + [0.3333333333333333 +/- 7.04e-17] + sage: (1 + x)*GF(2)(1) + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s)... """ cdef Polynomial_complex_arb res = self._new() sig_on() @@ -342,6 +348,8 @@ cdef class Polynomial_complex_arb(Polynomial): sage: Pol. = CBF[] sage: (x + 1)._rmul_(CBF(3)) 3.000000000000000*x + 3.000000000000000 + sage: (1/3)*(1 + x) + ([0.3333333333333333 +/- 7.04e-17])*x + [0.3333333333333333 +/- 7.04e-17] """ return self._lmul_(a) @@ -722,6 +730,43 @@ cdef class Polynomial_complex_arb(Polynomial): sig_off() return res + def revert_series(self, long n): + r""" + Return a polynomial ``f`` such that + ``f(self(x)) = self(f(x)) = x mod x^n``. + + EXAMPLES:: + + sage: Pol. = CBF[] + + sage: (2*x).revert_series(5) + 0.5000000000000000*x + + sage: (x + x^3/6 + x^5/120).revert_series(6) + ([0.075000000000000 +/- 9.75e-17])*x^5 + ([-0.166666666666667 +/- 4.45e-16])*x^3 + x + + sage: (1 + x).revert_series(6) + Traceback (most recent call last): + ... + ValueError: the constant coefficient must be zero + + sage: (x^2).revert_series(6) + Traceback (most recent call last): + ... + ValueError: the linear term must be nonzero + """ + cdef Polynomial_complex_arb res = self._new() + if n < 0: + n = 0 + if not acb_is_zero(acb_poly_get_coeff_ptr(self.__poly, 0)): + raise ValueError("the constant coefficient must be zero") + if acb_contains_zero(acb_poly_get_coeff_ptr(self.__poly, 1)): + raise ValueError("the linear term must be nonzero") + sig_on() + acb_poly_revert_series(res.__poly, self.__poly, n, prec(self)) + sig_off() + return res + # Evaluation def __call__(self, *x, **kwds): diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index dd420918180..c11a548d5b3 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -1559,6 +1559,29 @@ cdef class Polynomial(CommutativeAlgebraElement): current = current + current - z return current + def revert_series(self, n): + r""" + Return a polynomial ``f`` such that + ``f(self(x)) = self(f(x)) = x mod x^n``. + + Currently, this is only implemented over some coefficient rings. + + EXAMPLES:: + + sage: Pol. = QQ[] + sage: (x + x^3/6 + x^5/120).revert_series(6) + 3/40*x^5 - 1/6*x^3 + x + sage: Pol. = CBF[] + sage: (x + x^3/6 + x^5/120).revert_series(6) + ([0.075000000000000 +/- 9.75e-17])*x^5 + ([-0.166666666666667 +/- 4.45e-16])*x^3 + x + sage: Pol. = SR[] + sage: x.revert_series(6) + Traceback (most recent call last): + ... + NotImplementedError: only implemented for certain base rings + """ + raise NotImplementedError("only implemented for certain base rings") + def __long__(self): """ EXAMPLES:: From f65ac29ed2d3e47ed4360e073ecd7ffa4810ba79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 1 Feb 2018 10:03:05 +1300 Subject: [PATCH 584/740] Merge with develop --- VERSION.txt | 2 +- build/bin/sage-spkg | 64 +- build/pkgs/configure/checksums.ini | 6 +- build/pkgs/configure/package-version.txt | 2 +- build/pkgs/ecl/spkg-install | 4 +- build/pkgs/gcc/spkg-install | 3 +- build/pkgs/giac/checksums.ini | 6 +- build/pkgs/giac/package-version.txt | 2 +- build/pkgs/giac/patches/README.txt | 3 - .../pkgs/giac/patches/cSolveorder-check.patch | 11 - build/pkgs/giac/patches/libpng16.patch | 62 -- build/pkgs/giac/spkg-src | 5 +- build/pkgs/jupyter_client/checksums.ini | 6 +- build/pkgs/jupyter_client/package-version.txt | 2 +- build/pkgs/mpc/checksums.ini | 6 +- build/pkgs/mpc/package-version.txt | 2 +- build/pkgs/mpc/patches/mpc_mul_faster.patch | 124 ---- build/pkgs/mpfi/checksums.ini | 6 +- build/pkgs/mpfi/package-version.txt | 2 +- build/pkgs/mpfi/spkg-install | 2 +- build/pkgs/mpfr/checksums.ini | 6 +- build/pkgs/mpfr/package-version.txt | 2 +- .../pkgs/mpfr/patches/clang_workaround.patch | 20 - build/pkgs/notebook/checksums.ini | 6 +- build/pkgs/notebook/dependencies | 2 +- .../pkgs/notebook/jupyter_notebook_config.py | 5 + build/pkgs/notebook/package-version.txt | 2 +- build/pkgs/pari_jupyter/SPKG.txt | 10 +- build/pkgs/pari_jupyter/checksums.ini | 8 +- build/pkgs/pari_jupyter/dependencies | 2 +- build/pkgs/pari_jupyter/package-version.txt | 2 +- build/pkgs/patch/spkg-install | 9 +- build/pkgs/pynac/checksums.ini | 6 +- build/pkgs/pynac/package-version.txt | 2 +- build/pkgs/readline/spkg-install | 6 +- build/pkgs/send2trash/SPKG.txt | 17 + build/pkgs/send2trash/checksums.ini | 4 + build/pkgs/send2trash/dependencies | 5 + build/pkgs/send2trash/package-version.txt | 1 + build/pkgs/send2trash/spkg-install | 1 + build/pkgs/send2trash/type | 1 + build/pkgs/singular_jupyter/checksums.ini | 6 +- .../pkgs/singular_jupyter/package-version.txt | 2 +- build/pkgs/texlive/spkg-install | 0 configure.ac | 13 +- m4/ax_compiler_vendor.m4 | 88 +++ src/bin/sage | 56 +- src/bin/sage-banner | 2 +- src/bin/sage-dist-helpers | 7 +- src/bin/sage-eval | 2 +- src/bin/sage-fixdoctests | 2 +- src/bin/sage-ipython | 2 +- src/bin/sage-list-packages | 2 +- src/bin/sage-preparse | 2 +- src/bin/sage-run-cython | 2 +- src/bin/sage-runtests | 6 +- src/bin/sage-version.sh | 4 +- src/doc/de/tutorial/afterword.rst | 2 +- src/doc/de/tutorial/tour_help.rst | 4 +- src/doc/en/reference/references/index.rst | 17 +- .../algebraic_combinatorics/rsk.rst | 2 +- .../coercion_and_categories.rst | 3 +- src/doc/en/tutorial/programming.rst | 2 +- src/doc/en/tutorial/tour_help.rst | 4 +- src/doc/es/tutorial/tour_help.rst | 4 +- src/doc/fr/tutorial/afterword.rst | 2 +- src/doc/fr/tutorial/tour_help.rst | 4 +- src/doc/ja/tutorial/afterword.rst | 2 +- src/doc/ja/tutorial/programming.rst | 2 +- src/doc/ja/tutorial/tour_help.rst | 4 +- src/doc/pt/tutorial/afterword.rst | 2 +- src/doc/pt/tutorial/programming.rst | 2 +- src/doc/pt/tutorial/tour_help.rst | 4 +- src/doc/ru/tutorial/afterword.rst | 2 +- src/doc/ru/tutorial/tour_help.rst | 4 +- src/module_list.py | 3 +- src/sage/algebras/lie_algebras/examples.py | 9 + .../lie_algebras/lie_algebra_element.pyx | 10 +- .../lie_algebras/poincare_birkhoff_witt.py | 4 +- src/sage/algebras/lie_algebras/virasoro.py | 564 +++++++++++++++++- src/sage/algebras/orlik_solomon.py | 2 +- .../steenrod/steenrod_algebra_bases.py | 22 +- src/sage/calculus/desolvers.py | 2 +- src/sage/categories/category.py | 2 +- src/sage/categories/homset.py | 69 ++- src/sage/categories/number_fields.py | 9 +- src/sage/coding/guruswami_sudan/gs_decoder.py | 4 +- src/sage/combinat/abstract_tree.py | 55 +- .../combinat/binary_recurrence_sequences.py | 2 +- src/sage/combinat/cartesian_product.py | 22 +- .../cluster_algebra_quiver/cluster_seed.py | 5 +- src/sage/combinat/composition.py | 2 +- src/sage/combinat/composition_tableau.py | 18 +- src/sage/combinat/finite_state_machine.py | 2 +- src/sage/combinat/free_module.py | 15 + src/sage/combinat/integer_matrices.py | 2 +- src/sage/combinat/q_analogues.py | 90 ++- src/sage/combinat/root_system/cartan_type.py | 10 +- .../root_system/root_lattice_realizations.py | 2 +- src/sage/combinat/similarity_class_type.py | 2 +- src/sage/combinat/species/series.py | 8 +- src/sage/combinat/species/stream.py | 1 - src/sage/combinat/tamari_lattices.py | 28 +- src/sage/combinat/tiling.py | 5 +- src/sage/combinat/tutorial.py | 17 +- src/sage/combinat/words/abstract_word.py | 169 ++---- src/sage/combinat/words/alphabet.py | 2 +- src/sage/combinat/words/word_datatypes.pyx | 69 +-- src/sage/cpython/string.pxd | 78 ++- src/sage/cpython/string.pyx | 2 +- src/sage/crypto/boolean_function.pyx | 4 +- src/sage/data_structures/blas_dict.pyx | 2 +- src/sage/databases/sql_db.py | 2 +- src/sage/databases/stein_watkins.py | 2 +- src/sage/doctest/parsing.py | 83 ++- .../arithmetic_dynamics/projective_ds.py | 2 +- .../interval_exchanges/constructors.py | 2 +- src/sage/finance/markov_multifractal.py | 12 + src/sage/finance/stock.py | 16 + src/sage/functions/transcendental.py | 2 +- src/sage/game_theory/normal_form_game.py | 2 +- src/sage/games/sudoku.py | 12 + src/sage/geometry/cone.py | 2 +- .../hyperplane_arrangement/arrangement.py | 2 +- src/sage/geometry/newton_polygon.py | 6 +- src/sage/geometry/polyhedron/base.py | 156 ++++- src/sage/geometry/polyhedron/plot.py | 22 +- .../polyhedron/ppl_lattice_polygon.py | 2 +- src/sage/geometry/pseudolines.py | 2 +- src/sage/geometry/toric_plotter.py | 6 +- .../triangulation/point_configuration.py | 2 +- src/sage/graphs/centrality.pyx | 6 +- src/sage/graphs/digraph_generators.py | 9 +- src/sage/graphs/generators/basic.py | 64 +- src/sage/graphs/generators/chessboard.py | 2 +- .../graphs/generators/classical_geometries.py | 6 +- src/sage/graphs/generators/families.py | 45 +- src/sage/graphs/generators/smallgraphs.py | 2 +- src/sage/graphs/generators/world_map.py | 77 +++ src/sage/graphs/generic_graph.py | 22 +- src/sage/graphs/generic_graph_pyx.pyx | 8 +- src/sage/graphs/graph.py | 2 +- src/sage/graphs/graph_generators.py | 14 +- src/sage/graphs/hypergraph_generators.py | 3 +- src/sage/graphs/linearextensions.py | 14 +- src/sage/graphs/strongly_regular_db.pyx | 8 +- .../abelian_gps/abelian_group_morphism.py | 94 +-- src/sage/groups/finitely_presented.py | 2 +- .../groups/matrix_gps/finitely_generated.py | 4 +- .../perm_gps/partn_ref/data_structures.pyx | 6 +- .../groups/perm_gps/symgp_conjugacy_class.py | 7 +- src/sage/homology/simplicial_complex.py | 2 +- src/sage/interfaces/gap.py | 3 +- src/sage/interfaces/giac.py | 4 +- src/sage/interfaces/gp.py | 3 +- src/sage/interfaces/magma.py | 3 +- src/sage/interfaces/r.py | 5 +- src/sage/interfaces/scilab.py | 3 +- src/sage/interfaces/singular.py | 3 +- src/sage/interfaces/tests.py | 3 +- src/sage/knots/link.py | 12 + src/sage/lfunctions/all.py | 13 +- src/sage/libs/flint/fmpz_poly.pyx | 5 +- src/sage/libs/gap/element.pyx | 2 +- src/sage/libs/gap/util.pyx | 2 +- src/sage/libs/homfly.pyx | 16 +- src/sage/libs/mpc.pxd | 133 ----- src/sage/libs/mpc/__init__.pxd | 113 ++++ src/sage/libs/mpc/types.pxd | 27 + src/sage/libs/mpfr/__init__.pxd | 1 + src/sage/libs/ntl/ntl_GF2X.pyx | 130 ++-- src/sage/libs/pynac/pynac.pyx | 2 +- .../differentiable/automorphismfield.py | 2 +- .../differentiable/integrated_curve.py | 2 +- src/sage/matrix/matrix_integer_dense.pyx | 3 +- src/sage/matrix/matrix_rational_dense.pyx | 2 +- src/sage/matrix/matrix_space.py | 6 +- src/sage/matrix/special.py | 25 +- src/sage/matroids/graphic_matroid.py | 14 +- src/sage/matroids/set_system.pyx | 2 +- src/sage/misc/c3_controlled.pyx | 3 +- src/sage/misc/gperftools.py | 2 +- src/sage/misc/mrange.py | 2 +- src/sage/misc/sageinspect.py | 2 +- .../modular/btquotients/pautomorphicform.py | 14 +- src/sage/modular/hecke/module.py | 2 +- src/sage/modular/local_comp/liftings.py | 153 ++++- src/sage/modular/modform/element.py | 6 +- .../graded_ring_element.py | 4 +- src/sage/monoids/automatic_semigroup.py | 7 +- src/sage/monoids/string_monoid_element.py | 5 +- src/sage/numerical/backends/coin_backend.pyx | 2 +- .../numerical/backends/cvxopt_backend.pyx | 2 +- src/sage/numerical/backends/glpk_backend.pyx | 4 +- .../numerical/backends/glpk_graph_backend.pyx | 2 +- src/sage/numerical/backends/ppl_backend.pyx | 2 +- .../numerical/interactive_simplex_method.py | 2 +- src/sage/numerical/linear_functions.pyx | 4 +- src/sage/numerical/sdp.pyx | 3 +- src/sage/plot/plot.py | 2 +- src/sage/plot/plot3d/list_plot3d.py | 19 + src/sage/plot/plot3d/platonic.py | 4 +- src/sage/plot/plot3d/shapes2.py | 2 +- src/sage/plot/plot3d/tachyon.py | 2 +- src/sage/quivers/morphism.py | 19 + src/sage/repl/display/jsmol_iframe.py | 17 +- src/sage/repl/load.py | 4 +- src/sage/rings/asymptotic/growth_group.py | 3 - src/sage/rings/complex_mpc.pxd | 2 +- src/sage/rings/complex_mpc.pyx | 4 +- src/sage/rings/complex_number.pyx | 2 +- .../rings/finite_rings/element_givaro.pyx | 2 +- src/sage/rings/fraction_field.py | 23 +- .../rings/function_field/function_field.py | 8 +- src/sage/rings/invariant_theory.py | 3 +- src/sage/rings/morphism.pyx | 2 +- src/sage/rings/number_field/number_field.py | 95 +-- src/sage/rings/number_field/order.py | 18 + .../rings/padics/padic_generic_element.pyx | 4 +- src/sage/rings/polynomial/pbori.pyx | 3 +- src/sage/rings/polynomial/polydict.pyx | 2 +- .../rings/polynomial/polynomial_element.pyx | 51 +- .../polynomial/polynomial_element_generic.py | 7 +- src/sage/rings/power_series_pari.pyx | 4 +- src/sage/rings/rational_field.py | 6 +- src/sage/rings/real_mpfr.pyx | 26 +- .../elliptic_curves/ell_number_field.py | 2 +- .../elliptic_curves/gal_reps_number_field.py | 2 +- src/sage/schemes/elliptic_curves/height.py | 2 +- .../schemes/elliptic_curves/isogeny_class.py | 2 +- .../schemes/hyperelliptic_curves/mestre.py | 2 +- src/sage/schemes/toric/divisor.py | 2 +- src/sage/schemes/toric/points.py | 2 +- src/sage/schemes/toric/sheaf/klyachko.py | 2 +- src/sage/sets/recursively_enumerated_set.pyx | 5 +- src/sage/structure/element.pyx | 2 +- src/sage/structure/parent.pyx | 3 +- src/sage/structure/richcmp.pxd | 2 + src/sage/structure/richcmp.pyx | 198 ++++++ src/sage/symbolic/expression.pyx | 2 +- src/sage/symbolic/series.pyx | 4 +- .../modules/free_module_linear_group.py | 4 +- src/sage/version.py | 4 +- .../autogen/interpreters/__init__.py | 2 +- 244 files changed, 2810 insertions(+), 1269 deletions(-) delete mode 100644 build/pkgs/giac/patches/cSolveorder-check.patch delete mode 100644 build/pkgs/giac/patches/libpng16.patch delete mode 100644 build/pkgs/mpc/patches/mpc_mul_faster.patch delete mode 100644 build/pkgs/mpfr/patches/clang_workaround.patch create mode 100644 build/pkgs/send2trash/SPKG.txt create mode 100644 build/pkgs/send2trash/checksums.ini create mode 100644 build/pkgs/send2trash/dependencies create mode 100644 build/pkgs/send2trash/package-version.txt create mode 100644 build/pkgs/send2trash/spkg-install create mode 100644 build/pkgs/send2trash/type mode change 100644 => 100755 build/pkgs/texlive/spkg-install create mode 100644 m4/ax_compiler_vendor.m4 delete mode 100644 src/sage/libs/mpc.pxd create mode 100644 src/sage/libs/mpc/__init__.pxd create mode 100644 src/sage/libs/mpc/types.pxd diff --git a/VERSION.txt b/VERSION.txt index 20494d07023..b503aeef942 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 8.2.beta3, Release Date: 2018-01-17 +SageMath version 8.2.beta4, Release Date: 2018-01-27 diff --git a/build/bin/sage-spkg b/build/bin/sage-spkg index 80f6370a8ec..b86336cdc35 100755 --- a/build/bin/sage-spkg +++ b/build/bin/sage-spkg @@ -21,6 +21,7 @@ # SAGE_ROOT -- root directory of sage install # SAGE_LOCAL -- $SAGE_ROOT/local # SAGE_DISTFILES -- directory that stores upstream tarballs +# SAGE_DESTDIR -- temporary root the package will be installed to # PKG_BASE -- the base name of the package itself (e.g. 'patch') # PKG_VER -- the version number of the package # PKG_NAME -- $PKG_BASE-$PKG_VER @@ -804,6 +805,10 @@ export rsync_proxy=$http_proxy # Actually install ################################################################## +# Set the $SAGE_DESTDIR variable to be passed to the spkg-install +# script (the script itself could set this, but better to standardize +# this in one place) +export SAGE_DESTDIR="${SAGE_BUILD_DIR}/${PKG_NAME}/inst" if [ -f spkg-build ]; then # Package has both spkg-build and spkg-install; execute the latter with SAGE_SUDO @@ -826,6 +831,46 @@ else fi fi +# All spkgs should eventually support this, but fall back on old behavior in +# case DESTDIR=$SAGE_DESTDIR installation was not used +echo "Copying package files from temporary location $SAGE_DESTDIR to $SAGE_LOCAL" +if [ -d "$SAGE_DESTDIR" ]; then + PREFIX="${SAGE_DESTDIR}${SAGE_LOCAL%/}/" + + rm -f "$PREFIX"lib/*.la + if [ $? -ne 0 ]; then + error_msg "Error deleting unnecessary libtool archive files" + exit 1 + fi + + # Generate installed file manifest + FILE_LIST="" + FIRST=1 + IFS=$'\n' + for filename in $(find "$PREFIX" -type f -o -type l | sort); do + filename="${filename#$PREFIX}" + if [ $FIRST -eq 1 ]; then + FILE_LIST="\"$filename\"" + FIRST=0 + else + FILE_LIST="${FILE_LIST},"$'\n '"\"${filename}\"" + fi + # Copy file from the temp install path into $SAGE_LOCAL + if [ ! -d "$SAGE_LOCAL/$(dirname "$filename")" ]; then + mkdir -p "$SAGE_LOCAL/$(dirname "$filename")" + fi + mv "$PREFIX$filename" "${SAGE_LOCAL%/}/$filename" + if [ $? -ne 0 ]; then + error_msg "Error moving files for $PKG_BASE." + exit 1 + fi + done + + # Remove the $SAGE_DESTDIR entirely once all files have been moved to their + # final location. + rm -rf "$SAGE_DESTDIR" +fi + if [ "$UNAME" = "CYGWIN" ]; then # Rebase after installing each package--in case any packages load this # package at build time we need to ensure during the build that no binaries @@ -834,7 +879,11 @@ if [ "$UNAME" = "CYGWIN" ]; then sage-rebase.sh "$SAGE_LOCAL" 2>/dev/null fi -echo "Successfully installed $PKG_NAME" + +# Note: spkg-check tests are run after the package has been copied into +# SAGE_LOCAL. It might make more sense to run the tests before, but the +# spkg-check scripts were written before use of DESTDIR installs, and so +# fail in many cases. This might be good to change later. if [ "$SAGE_CHECK" = "yes" ]; then if [ -f spkg-check ]; then @@ -851,11 +900,6 @@ if [ "$SAGE_CHECK" = "yes" ]; then fi fi -rm -f "$SAGE_LOCAL"/lib/*.la -if [ $? -ne 0 ]; then - error_msg "Error deleting unnecessary libtool archive files" - exit 1 -fi # Mark that the new package has been installed (and tested, if # applicable). @@ -867,11 +911,17 @@ cat > "$PKG_NAME_INSTALLED" << __EOF__ "install_date": "$(date)", "system_uname": "$(uname -a)", "sage_version": "$(cat "${SAGE_ROOT}/VERSION.txt")", - "test_result": "$TEST_SUITE_RESULT" + "test_result": "$TEST_SUITE_RESULT", + "files": [ + $FILE_LIST + ] } __EOF__ +echo "Successfully installed $PKG_NAME" + + ################################################################## # Delete the temporary build directory if required ################################################################## diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 680bb78988f..41fb4d74819 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=fce3f8df178645228d91b6b65e3a03af0b2b3641 -md5=587c7e18518428f0f30138eeb7743bad -cksum=144653522 +sha1=499badf9321c53c458652ca71580585463e574a3 +md5=e483eb587a1be5886b718e24f54a84a5 +cksum=3889320352 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 1f7e0d6eac6..2197544d04c 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -251 +252 diff --git a/build/pkgs/ecl/spkg-install b/build/pkgs/ecl/spkg-install index 226af5333e8..7ee84ec5bd8 100644 --- a/build/pkgs/ecl/spkg-install +++ b/build/pkgs/ecl/spkg-install @@ -81,13 +81,13 @@ sdh_make_install # Create symbolic link to lib/ecl-version directory. # Also create a symbolic link lib/ecl/ecl -> include/ecl. # This is important when the Sage install is moved, see Trac #14662. -cd "$SAGE_LOCAL/lib/" && rm -f ecl && ln -s ecl-* ecl +cd "$SAGE_DESTDIR$SAGE_LOCAL/lib/" && rm -f ecl && ln -s ecl-* ecl if [ $? -ne 0 ]; then echo >&2 "Error - Failed to create symbolic link to ECL library" echo >&2 "directory ... exiting" exit 1 fi -cd "$SAGE_LOCAL/lib/ecl" && rm -f ecl && ln -s ../../include/ecl ecl +cd "$SAGE_DESTDIR$SAGE_LOCAL/lib/ecl" && rm -f ecl && ln -s ../../include/ecl ecl if [ $? -ne 0 ]; then echo >&2 "Error - Failed to create symbolic link to ECL include" echo >&2 "directory ... exiting" diff --git a/build/pkgs/gcc/spkg-install b/build/pkgs/gcc/spkg-install index e9a068f8675..a346ae73af0 100644 --- a/build/pkgs/gcc/spkg-install +++ b/build/pkgs/gcc/spkg-install @@ -123,8 +123,9 @@ $MAKE install # Force re-installation of gmp, mpir, mpfr and mpc with the GCC we just built. +# Also mark gfortran as not installed. cd "$SAGE_SPKG_INST" -rm -f gmp-* mpir-* mpfr-* mpc-* +rm -f gmp-* mpir-* mpfr-* mpc-* gfortran-* # Force re-configuration: the next time that "make" is run, we don't # want GCC to be built again, see Trac #19324 diff --git a/build/pkgs/giac/checksums.ini b/build/pkgs/giac/checksums.ini index 27861689d59..ae2283791d5 100644 --- a/build/pkgs/giac/checksums.ini +++ b/build/pkgs/giac/checksums.ini @@ -1,4 +1,4 @@ tarball=giac-VERSION.tar.bz2 -sha1=79503a2c2ee1ffe996727d09ebe6c6be3554434c -md5=ad15340533a7308f30b739bc36991a56 -cksum=2233538356 +sha1=3b61dd5fe79a76c2e0d4ef60ad1ad90dc3c04bd3 +md5=55308225758c547b061b52d699fa8b13 +cksum=2762392051 diff --git a/build/pkgs/giac/package-version.txt b/build/pkgs/giac/package-version.txt index ad6d053bf90..f7272f4bb5c 100644 --- a/build/pkgs/giac/package-version.txt +++ b/build/pkgs/giac/package-version.txt @@ -1 +1 @@ -1.2.3.47.p1 +1.4.9.45 diff --git a/build/pkgs/giac/patches/README.txt b/build/pkgs/giac/patches/README.txt index 691aca83157..93e759e6cc5 100644 --- a/build/pkgs/giac/patches/README.txt +++ b/build/pkgs/giac/patches/README.txt @@ -6,6 +6,3 @@ (xcas, qcas). http://xcas.e.ujf-grenoble.fr/XCAS/viewtopic.php?f=4&t=1555. The macos-ifactor patch enables pari in ifactor under osx because the problem can't be reproduced with the spkg library. - -* the pari2.8 patch is because some outputs in the check suite differs when giac is built - with pari 2.8 vs 2.7. (One more keyword and a different ordering in a sum) diff --git a/build/pkgs/giac/patches/cSolveorder-check.patch b/build/pkgs/giac/patches/cSolveorder-check.patch deleted file mode 100644 index 08a155138b7..00000000000 --- a/build/pkgs/giac/patches/cSolveorder-check.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/check/TP16-sol.cas.out1 2016-07-16 14:21:09.000000000 +0200 -+++ b/check/TP16-sol.cas.out1 2016-09-14 14:08:07.110527423 +0200 -@@ -48,7 +48,7 @@ - "Done", - [-sqrt(13)-1,sqrt(13)-1,4], - y^2+6*sqrt(13)+18,y^2-6*sqrt(13)+18,y^2, ---sqrt(6)*I*sqrt(sqrt(13)+3),sqrt(6)*I*sqrt(sqrt(13)+3),-sqrt(6)*sqrt(sqrt(13)-3),sqrt(6)*sqrt(sqrt(13)-3),0, -+sqrt(6)*I*sqrt(sqrt(13)+3),-sqrt(6)*I*sqrt(sqrt(13)+3),-sqrt(6)*sqrt(sqrt(13)-3),sqrt(6)*sqrt(sqrt(13)-3),0, - "No such variable u", - x^2+1/4*y^2+1/9*z^2-1, - x^2+y^2+z^2-u, diff --git a/build/pkgs/giac/patches/libpng16.patch b/build/pkgs/giac/patches/libpng16.patch deleted file mode 100644 index b6ba599c4e5..00000000000 --- a/build/pkgs/giac/patches/libpng16.patch +++ /dev/null @@ -1,62 +0,0 @@ -Make sure libpng 1.6x as provided by sage is used. ---- giac/configure.in 2017-06-19 13:50:43.676339211 +0000 -+++ giac.patched/configure.in 2017-06-19 13:51:38.986548183 +0000 -@@ -100,7 +100,7 @@ - [ if test "x$enableval" = "xno"; then CONFIG_PNG="no"; fi], []) - - if test "x$CONFIG_PNG" = "xyes"; then -- AC_CHECK_HEADERS(png.h, AC_CHECK_LIB(png,main)) -+ AC_CHECK_HEADERS(png.h, AC_CHECK_LIB(png16,main)) - fi - - ---- giac/configure 2017-05-28 12:54:29.000000000 +0000 -+++ giac.patched/configure 2017-06-19 13:51:54.826611543 +0000 -@@ -15482,13 +15550,13 @@ - cat >>confdefs.h <<_ACEOF - #define HAVE_PNG_H 1 - _ACEOF -- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lpng" >&5 --$as_echo_n "checking for main in -lpng... " >&6; } --if ${ac_cv_lib_png_main+:} false; then : -+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lpng16" >&5 -+$as_echo_n "checking for main in -lpng16... " >&6; } -+if ${ac_cv_lib_png16_main+:} false; then : - $as_echo_n "(cached) " >&6 - else - ac_check_lib_save_LIBS=$LIBS --LIBS="-lpng $LIBS" -+LIBS="-lpng16 $LIBS" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext - /* end confdefs.h. */ - -@@ -15502,22 +15570,22 @@ - } - _ACEOF - if ac_fn_cxx_try_link "$LINENO"; then : -- ac_cv_lib_png_main=yes -+ ac_cv_lib_png16_main=yes - else -- ac_cv_lib_png_main=no -+ ac_cv_lib_png16_main=no - fi - rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - LIBS=$ac_check_lib_save_LIBS - fi --{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_png_main" >&5 --$as_echo "$ac_cv_lib_png_main" >&6; } --if test "x$ac_cv_lib_png_main" = xyes; then : -+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_png16_main" >&5 -+$as_echo "$ac_cv_lib_png16_main" >&6; } -+if test "x$ac_cv_lib_png16_main" = xyes; then : - cat >>confdefs.h <<_ACEOF --#define HAVE_LIBPNG 1 -+#define HAVE_LIBPNG16 1 - _ACEOF - -- LIBS="-lpng $LIBS" -+ LIBS="-lpng16 $LIBS" - - fi - diff --git a/build/pkgs/giac/spkg-src b/build/pkgs/giac/spkg-src index 6706f87ed38..d82372402ef 100755 --- a/build/pkgs/giac/spkg-src +++ b/build/pkgs/giac/spkg-src @@ -13,9 +13,8 @@ fi # Exit on failure set -e -# TODO on the next update: l71 switch from gz to bz2 as wished in #18826 -VERSION="1.2.3" -VERSIONREV="47" +VERSION="1.4.9" +VERSIONREV="45" # The upstream tarball name is: giac"$SOURCEORIG".tar.gz SOURCEORIG=_"$VERSION"-"$VERSIONREV" diff --git a/build/pkgs/jupyter_client/checksums.ini b/build/pkgs/jupyter_client/checksums.ini index 84292b0761e..ab30f0430b2 100644 --- a/build/pkgs/jupyter_client/checksums.ini +++ b/build/pkgs/jupyter_client/checksums.ini @@ -1,4 +1,4 @@ tarball=jupyter_client-VERSION.tar.gz -sha1=1f226cd2d437eed5f9c45d572c0c9efe53437ea9 -md5=990ab28f63861cc29f90585d3f725b10 -cksum=1587833857 +sha1=2290704671547049ee20b034135a7a191fbc26f1 +md5=03293d63b59a3f71c417ee907852b460 +cksum=3064225526 diff --git a/build/pkgs/jupyter_client/package-version.txt b/build/pkgs/jupyter_client/package-version.txt index 831446cbd27..ce7f2b425b5 100644 --- a/build/pkgs/jupyter_client/package-version.txt +++ b/build/pkgs/jupyter_client/package-version.txt @@ -1 +1 @@ -5.1.0 +5.2.2 diff --git a/build/pkgs/mpc/checksums.ini b/build/pkgs/mpc/checksums.ini index c97e6b75889..70fbea4b5be 100644 --- a/build/pkgs/mpc/checksums.ini +++ b/build/pkgs/mpc/checksums.ini @@ -1,4 +1,4 @@ tarball=mpc-VERSION.tar.gz -sha1=b8be66396c726fdc36ebb0f692ed8a8cca3bcc66 -md5=d6a1d5f8ddea3abd2cc3e98f58352d26 -cksum=1941630434 +sha1=b019d9e1d27ec5fb99497159d43a3164995de2d0 +md5=4125404e41e482ec68282a2e687f6c73 +cksum=3987925640 diff --git a/build/pkgs/mpc/package-version.txt b/build/pkgs/mpc/package-version.txt index 3b69baff860..9084fa2f716 100644 --- a/build/pkgs/mpc/package-version.txt +++ b/build/pkgs/mpc/package-version.txt @@ -1 +1 @@ -1.0.3.p1 +1.1.0 diff --git a/build/pkgs/mpc/patches/mpc_mul_faster.patch b/build/pkgs/mpc/patches/mpc_mul_faster.patch deleted file mode 100644 index 5fc59a1bffc..00000000000 --- a/build/pkgs/mpc/patches/mpc_mul_faster.patch +++ /dev/null @@ -1,124 +0,0 @@ -diff -ru src/src/mul.c b/src/mul.c ---- src/src/mul.c 2012-07-22 17:40:22.000000000 +0200 -+++ b/src/mul.c 2014-01-27 15:12:37.449617411 +0100 -@@ -170,6 +170,10 @@ - return MPC_INEX (inex_re, inex_im); - } - -+#define MPFR_MANT(x) ((x)->_mpfr_d) -+#define MPFR_PREC(x) ((x)->_mpfr_prec) -+#define MPFR_EXP(x) ((x)->_mpfr_exp) -+#define MPFR_LIMB_SIZE(x) ((MPFR_PREC (x) - 1) / GMP_NUMB_BITS + 1) - - static int - mpfr_fmma (mpfr_ptr z, mpfr_srcptr a, mpfr_srcptr b, mpfr_srcptr c, -@@ -183,33 +187,68 @@ - - int inex; - mpfr_t u, v; -+ mp_size_t an, bn, cn, dn; - - /* u=a*b, v=sign*c*d exactly */ -- mpfr_init2 (u, mpfr_get_prec (a) + mpfr_get_prec (b)); -- mpfr_init2 (v, mpfr_get_prec (c) + mpfr_get_prec (d)); -- mpfr_mul (u, a, b, GMP_RNDN); -- mpfr_mul (v, c, d, GMP_RNDN); -- if (sign < 0) -- mpfr_neg (v, v, GMP_RNDN); -+ an = MPFR_LIMB_SIZE(a); -+ bn = MPFR_LIMB_SIZE(b); -+ cn = MPFR_LIMB_SIZE(c); -+ dn = MPFR_LIMB_SIZE(d); -+ MPFR_MANT(u) = malloc ((an + bn) * sizeof (mp_limb_t)); -+ MPFR_MANT(v) = malloc ((cn + dn) * sizeof (mp_limb_t)); -+ if (an >= bn) -+ mpn_mul (MPFR_MANT(u), MPFR_MANT(a), an, MPFR_MANT(b), bn); -+ else -+ mpn_mul (MPFR_MANT(u), MPFR_MANT(b), bn, MPFR_MANT(a), an); -+ if ((MPFR_MANT(u)[an + bn - 1] >> (GMP_NUMB_BITS - 1)) == 0) -+ { -+ mpn_lshift (MPFR_MANT(u), MPFR_MANT(u), an + bn, 1); -+ MPFR_EXP(u) = MPFR_EXP(a) + MPFR_EXP(b) - 1; -+ } -+ else -+ MPFR_EXP(u) = MPFR_EXP(a) + MPFR_EXP(b); -+ if (cn >= dn) -+ mpn_mul (MPFR_MANT(v), MPFR_MANT(c), cn, MPFR_MANT(d), dn); -+ else -+ mpn_mul (MPFR_MANT(v), MPFR_MANT(d), dn, MPFR_MANT(c), cn); -+ if ((MPFR_MANT(v)[cn + dn - 1] >> (GMP_NUMB_BITS - 1)) == 0) -+ { -+ mpn_lshift (MPFR_MANT(v), MPFR_MANT(v), cn + dn, 1); -+ MPFR_EXP(v) = MPFR_EXP(c) + MPFR_EXP(d) - 1; -+ } -+ else -+ MPFR_EXP(v) = MPFR_EXP(c) + MPFR_EXP(d); -+ MPFR_PREC(u) = (an + bn) * GMP_NUMB_BITS; -+ MPFR_PREC(v) = (cn + dn) * GMP_NUMB_BITS; -+ MPFR_SIGN(u) = MPFR_SIGN(a) * MPFR_SIGN(b); -+ if (sign > 0) -+ MPFR_SIGN(v) = MPFR_SIGN(c) * MPFR_SIGN(d); -+ else -+ MPFR_SIGN(v) = -MPFR_SIGN(c) * MPFR_SIGN(d); -+ -+ mpfr_check_range (u, 0, GMP_RNDN); -+ mpfr_check_range (v, 0, GMP_RNDN); - - /* tentatively compute z as u+v; here we need z to be distinct - from a, b, c, d to not lose the latter */ - inex = mpfr_add (z, u, v, rnd); -- -- if (mpfr_inf_p (z)) { -- /* replace by "correctly rounded overflow" */ -- mpfr_set_si (z, (mpfr_signbit (z) ? -1 : 1), GMP_RNDN); -- inex = mpfr_mul_2ui (z, z, mpfr_get_emax (), rnd); -- } -- else if (mpfr_zero_p (u) && !mpfr_zero_p (v)) { -- /* exactly u underflowed, determine inexact flag */ -- inex = (mpfr_signbit (u) ? 1 : -1); -- } -- else if (mpfr_zero_p (v) && !mpfr_zero_p (u)) { -- /* exactly v underflowed, determine inexact flag */ -- inex = (mpfr_signbit (v) ? 1 : -1); -- } -- else if (mpfr_nan_p (z) || (mpfr_zero_p (u) && mpfr_zero_p (v))) { -+ -+ if (!mpfr_regular_p(z) || !mpfr_regular_p(u) || !mpfr_regular_p(v)) -+ { -+ if (mpfr_inf_p (z)) { -+ /* replace by "correctly rounded overflow" */ -+ mpfr_set_si (z, (mpfr_signbit (z) ? -1 : 1), GMP_RNDN); -+ inex = mpfr_mul_2ui (z, z, mpfr_get_emax (), rnd); -+ } -+ else if (mpfr_zero_p (u) && !mpfr_zero_p (v)) { -+ /* exactly u underflowed, determine inexact flag */ -+ inex = (mpfr_signbit (u) ? 1 : -1); -+ } -+ else if (mpfr_zero_p (v) && !mpfr_zero_p (u)) { -+ /* exactly v underflowed, determine inexact flag */ -+ inex = (mpfr_signbit (v) ? 1 : -1); -+ } -+ else if (mpfr_nan_p (z) || (mpfr_zero_p (u) && mpfr_zero_p (v))) { - /* In the first case, u and v are infinities with opposite signs. - In the second case, u and v are zeroes; their sum may be 0 or the - least representable number, with a sign to be determined. -@@ -312,15 +351,14 @@ - mpfr_set_exp ((mpfr_ptr) c, ec); - mpfr_set_exp ((mpfr_ptr) d, ed); - /* works also when some of a, b, c, d are not all distinct */ -- } -- -- mpfr_clear (u); -- mpfr_clear (v); -+ } -+ } - -+ free (MPFR_MANT(u)); -+ free (MPFR_MANT(v)); - return inex; - } - -- - int - mpc_mul_naive (mpc_ptr z, mpc_srcptr x, mpc_srcptr y, mpc_rnd_t rnd) - { diff --git a/build/pkgs/mpfi/checksums.ini b/build/pkgs/mpfi/checksums.ini index 108e255e173..6d6940e0237 100644 --- a/build/pkgs/mpfi/checksums.ini +++ b/build/pkgs/mpfi/checksums.ini @@ -1,4 +1,4 @@ tarball=mpfi-VERSION.tar.bz2 -sha1=68773ce05e89e0f968687dd82d402f80ccc4c31f -md5=50d05e9d6f20a07c043b3b038927a456 -cksum=2405993671 +sha1=6a44e59f69fa72b4fe1c67b9c9ccf678f321ef80 +md5=1c233f3646bfb8a363aee0cc653c8baa +cksum=4291278873 diff --git a/build/pkgs/mpfi/package-version.txt b/build/pkgs/mpfi/package-version.txt index 343372e95da..4cda8f19edc 100644 --- a/build/pkgs/mpfi/package-version.txt +++ b/build/pkgs/mpfi/package-version.txt @@ -1 +1 @@ -1.5.1.p0 +1.5.2 diff --git a/build/pkgs/mpfi/spkg-install b/build/pkgs/mpfi/spkg-install index 7c5aaf3ac45..fad7f89a9eb 100644 --- a/build/pkgs/mpfi/spkg-install +++ b/build/pkgs/mpfi/spkg-install @@ -19,7 +19,7 @@ sdh_configure --with-mpfr="$SAGE_LOCAL" --with-gmp="$SAGE_LOCAL" sdh_make sdh_make_install -if ! [ $? -eq 0 -a -f $SAGE_LOCAL/include/mpfi.h ]; then +if ! [ $? -eq 0 -a -f $SAGE_DESTDIR$SAGE_LOCAL/include/mpfi.h ]; then echo "An error occurred while building MPFI." exit 1 fi diff --git a/build/pkgs/mpfr/checksums.ini b/build/pkgs/mpfr/checksums.ini index cd5a5a629d9..2728dda9c04 100644 --- a/build/pkgs/mpfr/checksums.ini +++ b/build/pkgs/mpfr/checksums.ini @@ -1,4 +1,4 @@ tarball=mpfr-VERSION.tar.bz2 -sha1=874e84bb5959fd5a19c032cfb5d673dded4b5cff -md5=b1d23a55588e3b2a13e3be66bc69fd8d -cksum=3347469031 +sha1=799245347044c8f0da9e513f86bb5e4c07974931 +md5=ef619f3bb68039e35c4a219e06be72d0 +cksum=3469661192 diff --git a/build/pkgs/mpfr/package-version.txt b/build/pkgs/mpfr/package-version.txt index fc261c4280b..fcdb2e109f6 100644 --- a/build/pkgs/mpfr/package-version.txt +++ b/build/pkgs/mpfr/package-version.txt @@ -1 +1 @@ -3.1.5.p0 +4.0.0 diff --git a/build/pkgs/mpfr/patches/clang_workaround.patch b/build/pkgs/mpfr/patches/clang_workaround.patch deleted file mode 100644 index 845ee1accef..00000000000 --- a/build/pkgs/mpfr/patches/clang_workaround.patch +++ /dev/null @@ -1,20 +0,0 @@ -diff --git a/src/set_d.c b/src/set_d.c -index c076ccf..5fc9548 100644 ---- a/src/set_d.c -+++ b/src/set_d.c -@@ -118,8 +118,15 @@ __gmpfr_extract_double (mpfr_limb_ptr rp, double d) - - d *= MP_BASE_AS_DOUBLE; - #if GMP_NUMB_BITS >= 64 -+#ifndef __clang__ - manl = d; - #else -+ /* clang produces an invalid exception when d >= 2^63, -+ see https://bugs.llvm.org//show_bug.cgi?id=17686. -+ Since this is always the case, here, we use the following patch. */ -+ manl = 0x8000000000000000 + (mp_limb_t) (d - 0x8000000000000000); -+#endif /* __clang__ */ -+#else - manh = (mp_limb_t) d; - manl = (mp_limb_t) ((d - manh) * MP_BASE_AS_DOUBLE); - #endif diff --git a/build/pkgs/notebook/checksums.ini b/build/pkgs/notebook/checksums.ini index ce2119db22a..214164b6dd1 100644 --- a/build/pkgs/notebook/checksums.ini +++ b/build/pkgs/notebook/checksums.ini @@ -1,4 +1,4 @@ tarball=notebook-VERSION.tar.gz -sha1=ebf8ac32b9ca6c981b8936f987d19a5689d0b298 -md5=61a3f3c8d97fe9ce8affd359733717e9 -cksum=2566272680 +sha1=5ef18cf1731383860efd944526454e5e9653b3db +md5=ea79ad977a3ab25ef5f506065017e1ed +cksum=2023952192 diff --git a/build/pkgs/notebook/dependencies b/build/pkgs/notebook/dependencies index 1f206344809..97c7dede39f 100644 --- a/build/pkgs/notebook/dependencies +++ b/build/pkgs/notebook/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip ipython jupyter_client ipykernel nbconvert nbformat jinja2 tornado terminado +$(PYTHON) | pip ipython jupyter_client ipykernel nbconvert nbformat jinja2 tornado terminado send2trash ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/notebook/jupyter_notebook_config.py b/build/pkgs/notebook/jupyter_notebook_config.py index 1b0566a32ac..1ea31ce125f 100644 --- a/build/pkgs/notebook/jupyter_notebook_config.py +++ b/build/pkgs/notebook/jupyter_notebook_config.py @@ -11,3 +11,8 @@ # * https://github.com/oreillymedia/thebe/issues/93 c.NotebookApp.disable_check_xsrf = True + +# send2trash sometimes doesn't work, so disable that for now +# See https://github.com/jupyter/notebook/issues/3249 + +c.FileContentsManager.delete_to_trash = False diff --git a/build/pkgs/notebook/package-version.txt b/build/pkgs/notebook/package-version.txt index ce7f2b425b5..c7cb1311a64 100644 --- a/build/pkgs/notebook/package-version.txt +++ b/build/pkgs/notebook/package-version.txt @@ -1 +1 @@ -5.2.2 +5.3.1 diff --git a/build/pkgs/pari_jupyter/SPKG.txt b/build/pkgs/pari_jupyter/SPKG.txt index 681cdc57f23..3a6ad00273c 100644 --- a/build/pkgs/pari_jupyter/SPKG.txt +++ b/build/pkgs/pari_jupyter/SPKG.txt @@ -11,12 +11,12 @@ GPL version 3 or later == Upstream Contact == * https://github.com/jdemeyer/pari_jupyter -* Jeroen Demeyer +* Jeroen Demeyer == Dependencies == +* Python (tested with version 2.7.14 and 3.6.1) * Jupyter 4 -* Python (tested with 2.7.9) -* Cython (git master) -* PARI (git master) -* GMP or MPIR (any version which works with PARI) +* PARI version 2.8.0 or later +* Readline (any version which works with PARI) +* Optional: Cython version 0.25 or later diff --git a/build/pkgs/pari_jupyter/checksums.ini b/build/pkgs/pari_jupyter/checksums.ini index 8c4503ffb22..0bcc161a9b1 100644 --- a/build/pkgs/pari_jupyter/checksums.ini +++ b/build/pkgs/pari_jupyter/checksums.ini @@ -1,4 +1,4 @@ -tarball=pari_jupyter-VERSION.tar.bz2 -sha1=285acc6650ad177b092605938582e16d67e910bb -md5=ab0dc1d09303d33e861088155b82c965 -cksum=1154883481 +tarball=pari_jupyter-VERSION.tar.gz +sha1=6eac8e4466e4753fd07a38d16538222bec9b0a53 +md5=4d6e4b12afdb26c53e38b3b7aadfa18f +cksum=109015385 diff --git a/build/pkgs/pari_jupyter/dependencies b/build/pkgs/pari_jupyter/dependencies index 516d7089387..9e6b05089de 100644 --- a/build/pkgs/pari_jupyter/dependencies +++ b/build/pkgs/pari_jupyter/dependencies @@ -1 +1 @@ -$(PYTHON) pari jupyter_core | pip cython +$(PYTHON) pari | pip cython notebook jupyter_core diff --git a/build/pkgs/pari_jupyter/package-version.txt b/build/pkgs/pari_jupyter/package-version.txt index 23aa8390630..1892b926767 100644 --- a/build/pkgs/pari_jupyter/package-version.txt +++ b/build/pkgs/pari_jupyter/package-version.txt @@ -1 +1 @@ -1.2.2 +1.3.2 diff --git a/build/pkgs/patch/spkg-install b/build/pkgs/patch/spkg-install index f5d5a6e92da..d9de2282753 100644 --- a/build/pkgs/patch/spkg-install +++ b/build/pkgs/patch/spkg-install @@ -31,12 +31,5 @@ sdh_make sdh_make_install if [ "$UNAME" = "CYGWIN" ] ; then - cp ../patch.exe.manifest "$SAGE_LOCAL/bin/" -fi - -# Sanity check that we have the correct version of patch -# in our PATH. -if ! patch --version | grep >/dev/null 'patch 2\.7\.5'; then - echo >&2 "Cannot find the patch program we just installed" - exit 1 + cp ../patch.exe.manifest "$SAGE_DESTDIR$SAGE_LOCAL/bin/" fi diff --git a/build/pkgs/pynac/checksums.ini b/build/pkgs/pynac/checksums.ini index e35983b473e..0b2fe41225c 100644 --- a/build/pkgs/pynac/checksums.ini +++ b/build/pkgs/pynac/checksums.ini @@ -1,4 +1,4 @@ tarball=pynac-VERSION.tar.bz2 -sha1=33e2990a1f3a90ced00c2d87709a851682892e7f -md5=5faf705c049ca4cf09f1de436363c3a3 -cksum=3009897742 +sha1=ec322d219a9c02b95a29789ca221b28c6cca7a14 +md5=10f21726733209ae6f641b5736c4c9fb +cksum=2341458065 diff --git a/build/pkgs/pynac/package-version.txt b/build/pkgs/pynac/package-version.txt index a597e4f317c..def4250351c 100644 --- a/build/pkgs/pynac/package-version.txt +++ b/build/pkgs/pynac/package-version.txt @@ -1 +1 @@ -0.7.14 +0.7.15 diff --git a/build/pkgs/readline/spkg-install b/build/pkgs/readline/spkg-install index b8c2663871e..e17b66fdcff 100644 --- a/build/pkgs/readline/spkg-install +++ b/build/pkgs/readline/spkg-install @@ -69,10 +69,10 @@ esac # Make sure that the install worked, despite whatever the # exit code of 'make' or 'make install' was: -if [[ -f "$SAGE_LOCAL/lib/$DYLIB_NAME" ]]; then +if [[ -f "$SAGE_DESTDIR$SAGE_LOCAL/lib/$DYLIB_NAME" ]]; then echo "Fixing permissions of libraries..." - chmod 755 "$SAGE_LOCAL"/lib/libreadline.* - chmod 755 "$SAGE_LOCAL"/lib/libhistory.* + chmod 755 "$SAGE_DESTDIR$SAGE_LOCAL"/lib/libreadline.* + chmod 755 "$SAGE_DESTDIR$SAGE_LOCAL"/lib/libhistory.* else # One or both of the readline libraries are missing, i.e. # haven't been installed. diff --git a/build/pkgs/send2trash/SPKG.txt b/build/pkgs/send2trash/SPKG.txt new file mode 100644 index 00000000000..d829cf1573b --- /dev/null +++ b/build/pkgs/send2trash/SPKG.txt @@ -0,0 +1,17 @@ += Send2Trash = + +== Description == + +Send file to trash natively under Mac OS X, Windows and Linux. + +Send2Trash is a small package that sends files to the Trash (or Recycle +Bin) natively and on all platforms. On OS X, it uses native +FSMoveObjectToTrashSync Cocoa calls, on Windows, it uses native (and +ugly) SHFileOperation win32 calls. On other platforms, if PyGObject and +GIO are available, it will use this. Otherwise, it will fallback to its +own implementation of the trash specifications from freedesktop.org. + +ctypes is used to access native libraries, so no compilation is +necessary. + +Send2Trash supports Python 2.7 and up (Python 3 is supported). diff --git a/build/pkgs/send2trash/checksums.ini b/build/pkgs/send2trash/checksums.ini new file mode 100644 index 00000000000..f8312979034 --- /dev/null +++ b/build/pkgs/send2trash/checksums.ini @@ -0,0 +1,4 @@ +tarball=Send2Trash-VERSION.tar.gz +sha1=34a01a1b0c25180e8f04c476f2a7cdaaf46e52ae +md5=3aa42844d608370c9e19d108fdf3782d +cksum=995505276 diff --git a/build/pkgs/send2trash/dependencies b/build/pkgs/send2trash/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/send2trash/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/send2trash/package-version.txt b/build/pkgs/send2trash/package-version.txt new file mode 100644 index 00000000000..9df886c42a1 --- /dev/null +++ b/build/pkgs/send2trash/package-version.txt @@ -0,0 +1 @@ +1.4.2 diff --git a/build/pkgs/send2trash/spkg-install b/build/pkgs/send2trash/spkg-install new file mode 100644 index 00000000000..deba1bb42bb --- /dev/null +++ b/build/pkgs/send2trash/spkg-install @@ -0,0 +1 @@ +cd src && sdh_pip_install . diff --git a/build/pkgs/send2trash/type b/build/pkgs/send2trash/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/send2trash/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/singular_jupyter/checksums.ini b/build/pkgs/singular_jupyter/checksums.ini index 9610c182c79..7def85d733f 100644 --- a/build/pkgs/singular_jupyter/checksums.ini +++ b/build/pkgs/singular_jupyter/checksums.ini @@ -1,4 +1,4 @@ tarball=jupyter_kernel_singular-VERSION.tar.gz -sha1=381319a1a49c4565078392bcd65b306f2dc22707 -md5=915e2e89b759297a8aebcfe8ca400cbb -cksum=3440281746 +sha1=e10d0894cac9ee111e34c6d6d6e409b26edf108f +md5=c2acfe07c1094200dac521d2e8dcb2a9 +cksum=970360836 diff --git a/build/pkgs/singular_jupyter/package-version.txt b/build/pkgs/singular_jupyter/package-version.txt index 2003b639c40..c81aa44afbf 100644 --- a/build/pkgs/singular_jupyter/package-version.txt +++ b/build/pkgs/singular_jupyter/package-version.txt @@ -1 +1 @@ -0.9.2 +0.9.7 diff --git a/build/pkgs/texlive/spkg-install b/build/pkgs/texlive/spkg-install old mode 100644 new mode 100755 diff --git a/configure.ac b/configure.ac index a6900c297ad..fbe94eb4ff7 100644 --- a/configure.ac +++ b/configure.ac @@ -70,7 +70,7 @@ AM_INIT_AUTOMAKE([1.9.6 foreign]) # Allow "configure --disable-maintainer-mode" to disable timestamp checking AM_MAINTAINER_MODE([enable]) -dnl Make sure the path to our own m4 macros is always properly set +dnl Make sure the path to our own m4 macros is always properly set dnl and doesn't depend on how autoconf is called. AC_CONFIG_MACRO_DIR([m4]) @@ -212,7 +212,7 @@ then AC_MSG_NOTICE([On some systems it can be found in /usr/ccs/bin]) AC_MSG_NOTICE([See also http://www.gnu.org/software/m4/]) AC_MSG_ERROR([Exiting, as the macro processor 'm4' can not be found.]) -fi +fi AC_CHECK_PROG(found_ranlib, ranlib, yes, no) if test x$found_ranlib != xyes @@ -355,6 +355,8 @@ AC_DEFUN([SAGE_MUST_INSTALL_GCC], [ # By default, do not install GCC need_to_install_gcc=no +# or GFORTRAN +need_to_install_gfortran=no if test -f "$SAGE_LOCAL/bin/gcc"; then # Special value for SAGE_INSTALL_GCC if GCC is already installed @@ -381,6 +383,13 @@ AC_SUBST(CC) AC_SUBST(CXX) AC_SUBST(FC) +# Figuring out if we are using clang instead of gcc. +AX_COMPILER_VENDOR() +IS_REALLY_GCC=no +if test "x$ax_cv_c_compiler_vendor" = xgnu ; then + IS_REALLY_GCC=yes +fi + # On darwin, also set the objective C/C++ compilers # Checking on all platforms doesn't hurt and stops # configure from sending an error when run on non-darwin. diff --git a/m4/ax_compiler_vendor.m4 b/m4/ax_compiler_vendor.m4 new file mode 100644 index 00000000000..0256bbaf82c --- /dev/null +++ b/m4/ax_compiler_vendor.m4 @@ -0,0 +1,88 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_compiler_vendor.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_COMPILER_VENDOR +# +# DESCRIPTION +# +# Determine the vendor of the C/C++ compiler, e.g., gnu, intel, ibm, sun, +# hp, borland, comeau, dec, cray, kai, lcc, metrowerks, sgi, microsoft, +# watcom, etc. The vendor is returned in the cache variable +# $ax_cv_c_compiler_vendor for C and $ax_cv_cxx_compiler_vendor for C++. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2008 Matteo Frigo +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 16 + +AC_DEFUN([AX_COMPILER_VENDOR], +[AC_CACHE_CHECK([for _AC_LANG compiler vendor], ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor, + dnl Please add if possible support to ax_compiler_version.m4 + [# note: don't check for gcc first since some other compilers define __GNUC__ + vendors="intel: __ICC,__ECC,__INTEL_COMPILER + ibm: __xlc__,__xlC__,__IBMC__,__IBMCPP__ + pathscale: __PATHCC__,__PATHSCALE__ + clang: __clang__ + cray: _CRAYC + fujitsu: __FUJITSU + gnu: __GNUC__ + sun: __SUNPRO_C,__SUNPRO_CC + hp: __HP_cc,__HP_aCC + dec: __DECC,__DECCXX,__DECC_VER,__DECCXX_VER + borland: __BORLANDC__,__CODEGEARC__,__TURBOC__ + comeau: __COMO__ + kai: __KCC + lcc: __LCC__ + sgi: __sgi,sgi + microsoft: _MSC_VER + metrowerks: __MWERKS__ + watcom: __WATCOMC__ + portland: __PGI + tcc: __TINYC__ + unknown: UNKNOWN" + for ventest in $vendors; do + case $ventest in + *:) vendor=$ventest; continue ;; + *) vencpp="defined("`echo $ventest | sed 's/,/) || defined(/g'`")" ;; + esac + AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,[ + #if !($vencpp) + thisisanerror; + #endif + ])], [break]) + done + ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor=`echo $vendor | cut -d: -f1` + ]) +]) + diff --git a/src/bin/sage b/src/bin/sage index d9fd2897e50..4cfd8c076d6 100755 --- a/src/bin/sage +++ b/src/bin/sage @@ -324,40 +324,44 @@ if [ "$1" = '-i' ]; then fi set -e - cd "$SAGE_ROOT" - # First of all, make sure that the toolchain is up-to-date - # (which is a dependency of every package) - ./sage --location - $MAKE all-toolchain - echo - + # Parse options + PACKAGES="" # Packages to install INSTALL_OPTIONS="" # Options to sage-spkg - for PKG in "$@" - do - case "$PKG" in + for OPT in "$@"; do + case "$OPT" in -info|--info) - echo >&2 "Error: 'sage -i $PKG ' is no longer supported, use 'sage --info ' instead." + echo >&2 "Error: 'sage -i $OPT ' is no longer supported, use 'sage --info ' instead." exit 2;; -f) FORCE_INSTALL=yes;; - -*) INSTALL_OPTIONS="$INSTALL_OPTIONS $PKG";; - *) - # First check that $PKG is actually a Makefile target - if ! grep "^$PKG: " build/make/Makefile >/dev/null; then - echo >&2 "Error: package '$PKG' not found" - echo >&2 "Assuming it is an old-style package... (this is deprecated: use -p instead of -i to install old-style packages)" - echo >&2 - sleep 5 - ./sage -p $INSTALL_OPTIONS "$PKG" - else - if [ x$FORCE_INSTALL = xyes ]; then - $MAKE "$PKG-clean" - fi - $MAKE SAGE_SPKG="sage-spkg $INSTALL_OPTIONS" "$PKG" - fi;; + -*) INSTALL_OPTIONS="$INSTALL_OPTIONS $OPT";; + *) PACKAGES="$PACKAGES $OPT";; esac done + + # First, uninstall the packages if -f was given + if [ "$FORCE_INSTALL" = yes ]; then + for PKG in $PACKAGES; do + $MAKE "$PKG-clean" || true # Ignore errors + done + fi + + # Make sure that the toolchain is up-to-date + # (which is a dependency of every package) + $MAKE all-toolchain + + # Now install the packages + for PKG in $PACKAGES; do + echo + # First check that $PKG is actually a Makefile target + if ! grep "^$PKG: " build/make/Makefile >/dev/null; then + echo >&2 "Error: package '$PKG' not found" + echo >&2 "Note: if it is an old-style package, use -p instead of -i to install it" + exit 1 + fi + $MAKE SAGE_SPKG="sage-spkg $INSTALL_OPTIONS" "$PKG" + done exit 0 fi diff --git a/src/bin/sage-banner b/src/bin/sage-banner index 5116d986fde..d2e95e4428d 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ SageMath version 8.2.beta3, Release Date: 2018-01-17 │ +│ SageMath version 8.2.beta4, Release Date: 2018-01-27 │ │ Type "notebook()" for the browser-based notebook interface. │ │ Type "help()" for help. │ └────────────────────────────────────────────────────────────────────┘ diff --git a/src/bin/sage-dist-helpers b/src/bin/sage-dist-helpers index d5b966a422a..63a4b5ebcfe 100644 --- a/src/bin/sage-dist-helpers +++ b/src/bin/sage-dist-helpers @@ -45,8 +45,9 @@ # # - sdh_make_install [...] # -# Runs `$SAGE_SUDO $MAKE install`. Additional arguments to `make` may be -# given as arguments. +# Runs `$SAGE_SUDO $MAKE install` with DESTDIR correctly set to a temporary +# install directory, for staged installations. Additional arguments to +# `make` may be given as arguments. # # - sdh_pip_install [...] # @@ -137,7 +138,7 @@ sdh_make() { sdh_make_install() { echo "Installing $PKG_NAME" - $SAGE_SUDO ${MAKE:-make} install "$@" || \ + $SAGE_SUDO ${MAKE:-make} install DESTDIR="$SAGE_DESTDIR" "$@" || \ sdh_die "Error installing $PKG_NAME" } diff --git a/src/bin/sage-eval b/src/bin/sage-eval index 3011bff7b6e..781c36150f8 100755 --- a/src/bin/sage-eval +++ b/src/bin/sage-eval @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env sage-python23 import sys from sage.all import * diff --git a/src/bin/sage-fixdoctests b/src/bin/sage-fixdoctests index 92d6cebd748..0debd28d544 100755 --- a/src/bin/sage-fixdoctests +++ b/src/bin/sage-fixdoctests @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env sage-python23 """ Given the output of doctest and a file, adjust the doctests so they won't fail. diff --git a/src/bin/sage-ipython b/src/bin/sage-ipython index d83ba020be8..d2ca96b1a87 100755 --- a/src/bin/sage-ipython +++ b/src/bin/sage-ipython @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env sage-python23 # -*- coding: utf-8 -*- """ Sage IPython startup script. diff --git a/src/bin/sage-list-packages b/src/bin/sage-list-packages index 1988cb86aec..bf35f8d8df9 100755 --- a/src/bin/sage-list-packages +++ b/src/bin/sage-list-packages @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env sage-python23 r""" Script to list the Sage packages diff --git a/src/bin/sage-preparse b/src/bin/sage-preparse index 58a26eb7d47..2d87e73dca7 100755 --- a/src/bin/sage-preparse +++ b/src/bin/sage-preparse @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env sage-python23 """ Preparse .sage files and save the result to .sage.py files. diff --git a/src/bin/sage-run-cython b/src/bin/sage-run-cython index feb91fff1b3..c6dbe00e9f5 100755 --- a/src/bin/sage-run-cython +++ b/src/bin/sage-run-cython @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env sage-python23 import sys from sage.repl.load import load_cython diff --git a/src/bin/sage-runtests b/src/bin/sage-runtests index 405c3f637d4..754081f3015 100755 --- a/src/bin/sage-runtests +++ b/src/bin/sage-runtests @@ -1,6 +1,8 @@ -#!/usr/bin/env python +#!/usr/bin/env sage-python23 -import optparse, os, sys +import optparse +import os +import sys # Note: the DOT_SAGE and SAGE_STARTUP_FILE environment variables have already been set by sage-env DOT_SAGE = os.environ['DOT_SAGE'] diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index b95e9d65275..270e83c3a8a 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,4 +1,4 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='8.2.beta3' -SAGE_RELEASE_DATE='2018-01-17' +SAGE_VERSION='8.2.beta4' +SAGE_RELEASE_DATE='2018-01-27' diff --git a/src/doc/de/tutorial/afterword.rst b/src/doc/de/tutorial/afterword.rst index 271ac92d543..cc38bcdef05 100644 --- a/src/doc/de/tutorial/afterword.rst +++ b/src/doc/de/tutorial/afterword.rst @@ -121,7 +121,7 @@ sein, also verhält sich Sage an manchen Stellen anders als Python. Rational Field sage: 2//3 0 - sage: int(2)/int(3) # optional - python2 + sage: int(2)/int(3) # py2 0 - **Große ganze Zahlen:** Python besitzt von Hause aus Unterstützung diff --git a/src/doc/de/tutorial/tour_help.rst b/src/doc/de/tutorial/tour_help.rst index 5e0884ad5e5..f15eb9e8ee3 100644 --- a/src/doc/de/tutorial/tour_help.rst +++ b/src/doc/de/tutorial/tour_help.rst @@ -262,9 +262,9 @@ verwendet haben, eine Liste (python 2): :: - sage: range(2,10) # optional - python2 + sage: range(2,10) # py2 [2, 3, 4, 5, 6, 7, 8, 9] - sage: list(range(2,10)) # optional - python3 + sage: list(range(2,10)) # py3 [2, 3, 4, 5, 6, 7, 8, 9] Hier ist eine etwas kompliziertere Liste: diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 8769b5aa7f1..90bd9e4b861 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1221,6 +1221,10 @@ REFERENCES: **I** +.. [IK2010] Kenji Iohara and Yoshiyuki Koga. + *Representation Theory of the Virasoro Algebra*. + Springer, (2010). + .. [ILS2012] Giuseppe F. Italiano, Luigi Laura, and Federico Santaroni. *Finding strong bridges and strong articulation points in linear time*. Theoretical Computer @@ -1657,6 +1661,10 @@ REFERENCES: FSE’93, Volume 809 of LNCS, pages 1-17. Springer, Heidelberg, December 1994. +.. [Mat1992] \O. Mathieu. *Classification of Harish-Chandra + modules over the Virasoro Lie algebra*. + Invent. Math. **107(2)** (1992), pp. 225-234. + .. [Mat2002] Jiří Matousek, "Lectures on Discrete Geometry", Springer, 2002 @@ -2057,11 +2065,12 @@ REFERENCES: Reduction*. Advances in Cryptology - EUROCRYPT '95. LNCS Volume 921, 1995, pp 1-12. -.. [Shi1971] Shimura, Goro. *Introduction to the arithmetic theory of - automorphic functions*. Princeton University Press, 1971. +.. [Shi1971] Goro Shimura, *Introduction to the arithmetic theory of + automorphic functions*. Publications of the Mathematical + Society of Japan and Princeton University Press, 1971. -.. [Shr2004] Shreve, S. Stochastic Calculus for Finance II: - Continuous-Time Models. New York: Springer, 2004 +.. [Shr2004] \S. Shreve, *Stochastic Calculus for Finance II: + Continuous-Time Models*. New York: Springer, 2004 .. [SIHMAS2011] \K. Shibutani, T. Isobe, H. Hiwatari, A. Mitsuda, T. Akishita, and T. Shirai, *Piccolo: An ultra-lightweight block-cipher*; in diff --git a/src/doc/en/thematic_tutorials/algebraic_combinatorics/rsk.rst b/src/doc/en/thematic_tutorials/algebraic_combinatorics/rsk.rst index cbc10938634..cbbf6a1d89a 100644 --- a/src/doc/en/thematic_tutorials/algebraic_combinatorics/rsk.rst +++ b/src/doc/en/thematic_tutorials/algebraic_combinatorics/rsk.rst @@ -88,7 +88,7 @@ We can also check this against the hook length formula (Theorem 8.1):: sage: def hook_length_formula(p): ....: n = p.size() - ....: return factorial(n) / prod(p.hook_length(*c) for c in p.cells()) + ....: return factorial(n) // prod(p.hook_length(*c) for c in p.cells()) sage: for la in u.support(): ....: assert u[la] == hook_length_formula(la) diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst index cf7b312707f..6f54d8b55a1 100644 --- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst +++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst @@ -875,7 +875,8 @@ The four axioms requested for coercions rational field is a homomorphism of euclidean domains:: sage: QQ.coerce_map_from(ZZ).category_for() - Join of Category of euclidean domains and Category of metric spaces + Join of Category of euclidean domains and Category of infinite sets + and Category of metric spaces .. end of output diff --git a/src/doc/en/tutorial/programming.rst b/src/doc/en/tutorial/programming.rst index a2e8147f695..febe20200f1 100644 --- a/src/doc/en/tutorial/programming.rst +++ b/src/doc/en/tutorial/programming.rst @@ -672,7 +672,7 @@ the Python int ``1`` is unique, but the Sage Integer ``1`` is not: sage: 1 is 2/2 False - sage: int(1) is int(2)/int(2) # optional - python2 + sage: int(1) is int(2)/int(2) # py2 True sage: 1 is 1 False diff --git a/src/doc/en/tutorial/tour_help.rst b/src/doc/en/tutorial/tour_help.rst index b290d3e949d..800777882dc 100644 --- a/src/doc/en/tutorial/tour_help.rst +++ b/src/doc/en/tutorial/tour_help.rst @@ -259,9 +259,9 @@ the ``range`` command that we used creates a list (in python 2): :: - sage: range(2,10) # optional - python2 + sage: range(2,10) # py2 [2, 3, 4, 5, 6, 7, 8, 9] - sage: list(range(2,10)) # optional - python3 + sage: list(range(2,10)) # py3 [2, 3, 4, 5, 6, 7, 8, 9] Here is a more complicated list: diff --git a/src/doc/es/tutorial/tour_help.rst b/src/doc/es/tutorial/tour_help.rst index c759d1b0fb6..efbb2880940 100644 --- a/src/doc/es/tutorial/tour_help.rst +++ b/src/doc/es/tutorial/tour_help.rst @@ -258,9 +258,9 @@ Por ejemplo, el comando ``range`` que hemos usado crea una lista (python 2): :: - sage: range(2,10) # optional - python2 + sage: range(2,10) # py2 [2, 3, 4, 5, 6, 7, 8, 9] - sage: list(range(2,10)) # optional - python3 + sage: list(range(2,10)) # py3 [2, 3, 4, 5, 6, 7, 8, 9] He aquí una lista más complicada: diff --git a/src/doc/fr/tutorial/afterword.rst b/src/doc/fr/tutorial/afterword.rst index 4355559abf2..1a470f08cfe 100644 --- a/src/doc/fr/tutorial/afterword.rst +++ b/src/doc/fr/tutorial/afterword.rst @@ -132,7 +132,7 @@ Aussi, Sage se comporte différemment de Python à plusieurs égards. Rational Field sage: 2//3 0 - sage: int(2)/int(3) # optional - python2 + sage: int(2)/int(3) # py2 0 - **Entiers longs :** Python possède nativement un support pour les entiers de diff --git a/src/doc/fr/tutorial/tour_help.rst b/src/doc/fr/tutorial/tour_help.rst index 9e853c11659..3ad0435ec8c 100644 --- a/src/doc/fr/tutorial/tour_help.rst +++ b/src/doc/fr/tutorial/tour_help.rst @@ -264,9 +264,9 @@ liste (en python 2): :: - sage: range(2,10) # optional - python2 + sage: range(2,10) # py2 [2, 3, 4, 5, 6, 7, 8, 9] - sage: list(range(2,10)) # optional - python3 + sage: list(range(2,10)) # py3 [2, 3, 4, 5, 6, 7, 8, 9] Voici un exemple plus compliqué de liste : diff --git a/src/doc/ja/tutorial/afterword.rst b/src/doc/ja/tutorial/afterword.rst index 47371fecdda..68403ea6216 100644 --- a/src/doc/ja/tutorial/afterword.rst +++ b/src/doc/ja/tutorial/afterword.rst @@ -96,7 +96,7 @@ Pythonの数学機能には混乱を招きがちな面があり,SageにはPyth Rational Field sage: 2//3 0 - sage: int(2)/int(3) # optional - python2 + sage: int(2)/int(3) # py2 0 - **長整数:** Python本体は,C言語由来のint型だけではなく任意精度整数をサポートしている. diff --git a/src/doc/ja/tutorial/programming.rst b/src/doc/ja/tutorial/programming.rst index 082853703c3..f390e5744d9 100644 --- a/src/doc/ja/tutorial/programming.rst +++ b/src/doc/ja/tutorial/programming.rst @@ -647,7 +647,7 @@ Sageにおける異種オブジェクト間の比較演算では,まず対象 sage: 1 is 2/2 False - sage: int(1) is int(2)/int(2) # optional - python2 + sage: int(1) is int(2)/int(2) # py2 True sage: 1 is 1 False diff --git a/src/doc/ja/tutorial/tour_help.rst b/src/doc/ja/tutorial/tour_help.rst index a319a189e06..7c3994930e5 100644 --- a/src/doc/ja/tutorial/tour_help.rst +++ b/src/doc/ja/tutorial/tour_help.rst @@ -254,9 +254,9 @@ Sageにおける最も基本的なデータ構造はリストで,名前の示 :: - sage: range(2,10) # optional - python2 + sage: range(2,10) # py2 [2, 3, 4, 5, 6, 7, 8, 9] - sage: list(range(2,10)) # optional - python3 + sage: list(range(2,10)) # py3 [2, 3, 4, 5, 6, 7, 8, 9] もう少し複雑なリストの例として: diff --git a/src/doc/pt/tutorial/afterword.rst b/src/doc/pt/tutorial/afterword.rst index a218562c045..5318a424766 100644 --- a/src/doc/pt/tutorial/afterword.rst +++ b/src/doc/pt/tutorial/afterword.rst @@ -113,7 +113,7 @@ se comporta diferentemente do Python em diversas situações. Rational Field sage: 2//3 0 - sage: int(2)/int(3) # optional - python2 + sage: int(2)/int(3) # py2 0 - **Inteiros longos:** O Python possui suporte nativo para inteiros diff --git a/src/doc/pt/tutorial/programming.rst b/src/doc/pt/tutorial/programming.rst index 71ef68e996b..82ba2c538f8 100644 --- a/src/doc/pt/tutorial/programming.rst +++ b/src/doc/pt/tutorial/programming.rst @@ -693,7 +693,7 @@ o int ``1`` do Python é único, mas o Inteiro ``1`` do Sage não é. sage: 1 is 2/2 False - sage: int(1) is int(2)/int(2) # optional - python2 + sage: int(1) is int(2)/int(2) # py2 True sage: 1 is 1 False diff --git a/src/doc/pt/tutorial/tour_help.rst b/src/doc/pt/tutorial/tour_help.rst index e31190a4e8c..7b4f5243ddb 100644 --- a/src/doc/pt/tutorial/tour_help.rst +++ b/src/doc/pt/tutorial/tour_help.rst @@ -260,9 +260,9 @@ exemplo, o comando ``range`` que usamos acima cria uma lista: :: - sage: range(2,10) # optional - python2 + sage: range(2,10) # py2 [2, 3, 4, 5, 6, 7, 8, 9] - sage: list(range(2,10)) # optional - python3 + sage: list(range(2,10)) # py3 [2, 3, 4, 5, 6, 7, 8, 9] Abaixo segue uma lista mais complicada: diff --git a/src/doc/ru/tutorial/afterword.rst b/src/doc/ru/tutorial/afterword.rst index 6b59856ea52..6465ec04a8f 100644 --- a/src/doc/ru/tutorial/afterword.rst +++ b/src/doc/ru/tutorial/afterword.rst @@ -106,7 +106,7 @@ Sage ведет себя немного другим образом. Rational Field sage: 2//3 0 - sage: int(2)/int(3) # optional - python2 + sage: int(2)/int(3) # py2 0 - **Большие целые числа:** Python имеет встроенную поддержку целых чисел diff --git a/src/doc/ru/tutorial/tour_help.rst b/src/doc/ru/tutorial/tour_help.rst index 5650c9d70f9..57d4da60df4 100644 --- a/src/doc/ru/tutorial/tour_help.rst +++ b/src/doc/ru/tutorial/tour_help.rst @@ -246,9 +246,9 @@ tanh, taylor``. Данная функция является хорошим сп :: - sage: range(2,10) # optional - python2 + sage: range(2,10) # py2 [2, 3, 4, 5, 6, 7, 8, 9] - sage: list(range(2,10)) # optional - python3 + sage: list(range(2,10)) # py3 [2, 3, 4, 5, 6, 7, 8, 9] Далее показан пример более сложного списка: diff --git a/src/module_list.py b/src/module_list.py index a066fca2391..911d598ecfd 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -1154,8 +1154,7 @@ def uname_specific(name, value, alternative): sources = ['sage/rings/morphism.pyx']), Extension('sage.rings.complex_mpc', - sources = ['sage/rings/complex_mpc.pyx'], - libraries = ['mpc']), + sources = ['sage/rings/complex_mpc.pyx']), Extension('sage.rings.noncommutative_ideals', sources = ['sage/rings/noncommutative_ideals.pyx']), diff --git a/src/sage/algebras/lie_algebras/examples.py b/src/sage/algebras/lie_algebras/examples.py index 88a364c0d2a..9b98295d1cf 100644 --- a/src/sage/algebras/lie_algebras/examples.py +++ b/src/sage/algebras/lie_algebras/examples.py @@ -274,6 +274,8 @@ def regular_vector_fields(R): r""" Return the Lie algebra of regular vector fields on `\CC^{\times}`. + This is also known as the Witt (Lie) algebra. + .. SEEALSO:: :class:`~sage.algebras.lie_algebras.virasoro.LieAlgebraRegularVectorFields` @@ -286,10 +288,17 @@ def regular_vector_fields(R): from sage.algebras.lie_algebras.virasoro import LieAlgebraRegularVectorFields return LieAlgebraRegularVectorFields(R) +witt = regular_vector_fields + def pwitt(R, p): r""" Return the `p`-Witt Lie algebra over `R`. + INPUT: + + - ``R`` -- the base ring + - ``p`` -- a positive integer that is `0` in ``R`` + EXAMPLES:: sage: lie_algebras.pwitt(GF(5), 5) diff --git a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx index 8110996d00f..46758624192 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx +++ b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx @@ -45,6 +45,14 @@ cdef class LieAlgebraElement(IndexedFreeModuleElement): sage: y*x x*y - z + Check that actions work:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: d = L.basis() + sage: M = L.chargeless_representation(1/2, 3/4) + sage: d[-5] * M.basis()[10] + -47/4*v[5] + TESTS:: sage: L. = LieAlgebra(QQ, {('x','y'): {'z':1}}) @@ -96,7 +104,7 @@ cdef class LieAlgebraElement(IndexedFreeModuleElement): sage: x * y b2*b3 sage: y * x - b2*b3 - b0 + b2*b3 + b0 sage: L = lie_algebras.regular_vector_fields(QQ) sage: L.an_element() diff --git a/src/sage/algebras/lie_algebras/poincare_birkhoff_witt.py b/src/sage/algebras/lie_algebras/poincare_birkhoff_witt.py index 51ede7639c2..0b2014a97fe 100644 --- a/src/sage/algebras/lie_algebras/poincare_birkhoff_witt.py +++ b/src/sage/algebras/lie_algebras/poincare_birkhoff_witt.py @@ -92,9 +92,9 @@ class PoincareBirkhoffWittBasis(CombinatorialFreeModule): sage: G[2] * G[3] PBW[2]*PBW[3] sage: G[3] * G[2] - PBW[2]*PBW[3] - PBW[5] + PBW[2]*PBW[3] + PBW[5] sage: G[-2] * G[3] * G[2] - PBW[-2]*PBW[2]*PBW[3] - PBW[-2]*PBW[5] + PBW[-2]*PBW[2]*PBW[3] + PBW[-2]*PBW[5] """ @staticmethod def __classcall_private__(cls, g, basis_key=None, prefix='PBW', **kwds): diff --git a/src/sage/algebras/lie_algebras/virasoro.py b/src/sage/algebras/lie_algebras/virasoro.py index d217d51471a..6a2b0814150 100644 --- a/src/sage/algebras/lie_algebras/virasoro.py +++ b/src/sage/algebras/lie_algebras/virasoro.py @@ -26,6 +26,7 @@ from sage.algebras.lie_algebras.lie_algebra_element import LieAlgebraElement from sage.algebras.lie_algebras.lie_algebra import (InfinitelyGeneratedLieAlgebra, FinitelyGeneratedLieAlgebra) +from sage.combinat.free_module import CombinatorialFreeModule class LieAlgebraRegularVectorFields(InfinitelyGeneratedLieAlgebra, IndexedGenerators): r""" @@ -36,10 +37,15 @@ class LieAlgebraRegularVectorFields(InfinitelyGeneratedLieAlgebra, IndexedGenera .. MATH:: - [d_i, d_j] = (j - i) d_{i+j}. + [d_i, d_j] = (i - j) d_{i+j}. This is also known as the Witt (Lie) algebra. + .. NOTE:: + + This differs from some conventions (e.g., [Ka1990]_), where + we have `d'_i \mapsto -d_i`. + REFERENCES: - :wikipedia:`Witt_algebra` @@ -101,13 +107,13 @@ def bracket_on_basis(self, i, j): sage: L = lie_algebras.regular_vector_fields(QQ) sage: L.bracket_on_basis(2, -2) - -4*d[0] + 4*d[0] sage: L.bracket_on_basis(2, 4) - 2*d[6] + -2*d[6] sage: L.bracket_on_basis(4, 4) 0 """ - return self.term(i + j, j - i) + return self.term(i + j, i - j) def _an_element_(self): """ @@ -148,7 +154,7 @@ class WittLieAlgebra_charp(FinitelyGeneratedLieAlgebra, IndexedGenerators): .. MATH:: - [d_i, d_j] = (j - i) d_{i+j}, + [d_i, d_j] = (i - j) d_{i+j}, where the `i+j` on the right hand side is identified with its remainder modulo `p`. @@ -170,6 +176,8 @@ def __init__(self, R, p): sage: TestSuite(L).run() # not tested -- universal envelope doesn't work sage: L._test_jacobi_identity() """ + if R(p) != 0: + raise ValueError("{} is not 0 in {}".format(p, R)) cat = LieAlgebras(R).FiniteDimensional().WithBasis() FinitelyGeneratedLieAlgebra.__init__(self, R, index_set=range(p), category=cat) IndexedGenerators.__init__(self, range(p), prefix='d', bracket='[') @@ -217,15 +225,15 @@ def bracket_on_basis(self, i, j): sage: L = lie_algebras.pwitt(Zmod(5), 5) sage: L.bracket_on_basis(2, 3) - d[0] - sage: L.bracket_on_basis(3, 2) 4*d[0] + sage: L.bracket_on_basis(3, 2) + d[0] sage: L.bracket_on_basis(2, 2) 0 sage: L.bracket_on_basis(1, 3) - 2*d[4] + 3*d[4] """ - return self.term((i + j) % self._p, j - i) + return self.term((i + j) % self._p, i - j) def _an_element_(self): """ @@ -283,7 +291,7 @@ class VirasoroAlgebra(InfinitelyGeneratedLieAlgebra, IndexedGenerators): .. MATH:: - [d_i, d_j] = (j - i) d_{i+j} + \frac{1}{12}(j^3 - j) \delta_{i,-j} c + [d_i, d_j] = (i - j) d_{i+j} + \frac{1}{12}(i^3 - i) \delta_{i,-j} c and @@ -437,14 +445,14 @@ def bracket_on_basis(self, i, j): sage: d.bracket_on_basis('c', 2) 0 sage: d.bracket_on_basis(2, -2) - -4*d[0] - 1/2*c + 4*d[0] + 1/2*c """ if i == 'c' or j == 'c': return self.zero() - ret = self._from_dict({i + j: j-i}) + ret = self._from_dict({i + j: i-j}) R = self.base_ring() if i == -j: - ret += R(j ** 3 - j) / R(12) * self.c() + ret += R(i ** 3 - i) / R(12) * self.c() return ret def _an_element_(self): @@ -473,6 +481,536 @@ def some_elements(self): d = self.monomial return [d(0), d(2), d(-2), d('c'), self.an_element()] + def chargeless_representation(self, a, b): + """ + Return the chargeless representation of ``self`` with + parameters ``a`` and ``b``. + + .. SEEALSO:: + + :class:`~sage.algebras.lie_algebras.virasoro.ChargelessRepresentation` + + EXAMPLES:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: L.chargeless_representation(3, 2) + Chargeless representation (3, 2) of + The Virasoro algebra over Rational Field + """ + return ChargelessRepresentation(self, a, b) + + def verma_module(self, c, h): + """ + Return the Verma module with central charge ``c`` and + conformal (or highest) weight ``h``. + + .. SEEALSO:: + + :class:`~sage.algebras.lie_algebras.virasoro.VermaModule` + + EXAMPLES:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: L.verma_module(3, 2) + Verma module with charge 3 and confromal weight 2 of + The Virasoro algebra over Rational Field + """ + return VermaModule(self, c, h) + class Element(LieAlgebraElement): pass +##################################################################### +## Representations + +class ChargelessRepresentation(CombinatorialFreeModule): + r""" + A chargeless representation of the Virasoro algebra. + + Let `L` be the Virasoro algebra over the field `F` of + characteristic `0`. For `\alpha, \beta \in R`, we denote `V_{a,b}` + as the `(a, b)`-*chargeless representation* of `L`, which is the + `F`-span of `\{v_k \mid k \in \ZZ\}` with `L` action + + .. MATH:: + + \begin{aligned} + d_n \cdot v_k & = (a n + b - k) v_{n+k}, + \\ c \cdot v_k & = 0, + \end{aligned} + + This comes from the action of `d_n = -t^{n+1} \frac{d}{dt}` on + `F[t, t^{-1}]` (recall that `L` is the central extension of the + :class:`algebra of derivations ` + of `F[t, t^{-1}]`), where + + .. MATH:: + + V_{a,b} = F[t, t^{-1}] t^{a-b} (dt)^{-a} + + and `v_k = t^{a-b+k} (dz)^{-a}`. + + The chargeless representations are either irreducible or + contains exactly two simple subquotients, one of which is the + trivial representation and the other is `F[t, t^{-1}] / F`. + The non-trivial simple subquotients are called the + *intermediate series*. + + The module `V_{a,b}` is irreducible if and only if + `a \neq 0, -1` or `b \notin \ZZ`. When `a = 0` and `b \in \ZZ`, + then there exists a subrepresentation isomorphic to the trivial + representation. If `a = -1` and `b \in \ZZ`, then there exists + a subrepresentation `V` such that `V_{a,b} / V` is isomorphic + to `K \frac{dt}{t}` and `V` is irreducible. + + In characteristic `p`, the non-trivial simple subquotient + is isomorphic to `F[t, t^{-1}] / F[t^p, t^{-p}]`. For + `p \neq 2,3`, then the action is given as above. + + EXAMPLES: + + We first construct the irreducible `V_{1/2, 3/4}` and do some + basic computations:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: M = L.chargeless_representation(1/2, 3/4) + sage: d = L.basis() + sage: v = M.basis() + sage: d[3] * v[2] + 1/4*v[5] + sage: d[3] * v[-1] + 13/4*v[2] + sage: (d[3] - d[-2]) * (v[-1] + 1/2*v[0] - v[4]) + -3/4*v[-3] + 1/8*v[-2] - v[2] + 9/8*v[3] + 7/4*v[7] + + We construct the reducible `V_{0,2}` and the trivial + subrepresentation given by the span of `v_2`. We verify + this for `\{d_i \mid -10 \leq i < 10\}`:: + + sage: M = L.chargeless_representation(0, 2) + sage: v = M.basis() + sage: all(d[i] * v[2] == M.zero() for i in range(-10, 10)) + True + + REFERENCES: + + - [Mat1992]_ + - [IK2010]_ + """ + def __init__(self, V, a, b): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: M = L.chargeless_representation(1/2, 3/4) + sage: TestSuite(M).run() + """ + self._a = a + self._b = b + self._V = V + if V.base_ring().characteristic() in [2,3]: + raise NotImplementedError("not implemented for characteristic 2,3") + CombinatorialFreeModule.__init__(self, V.base_ring(), ZZ, + prefix='v') + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: L.chargeless_representation(1/2, 3/4) + Chargeless representation (1/2, 3/4) of + The Virasoro algebra over Rational Field + """ + return "Chargeless representation ({}, {}) of {}".format( + self._a, self._b, self._V) + + def parameters(self): + """ + Return the parameters `(a, b)` of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: M = L.chargeless_representation(1/2, 3/4) + sage: M.parameters() + (1/2, 3/4) + """ + return (self._a, self._b) + + def virasoro_algebra(self): + """ + Return the Virasoro algebra ``self`` is a representation of. + + EXAMPLES:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: M = L.chargeless_representation(1/2, 3/4) + sage: M.virasoro_algebra() is L + True + """ + return self._V + + class Element(CombinatorialFreeModule.Element): + def _acted_upon_(self, scalar, self_on_left=False): + """ + Return the action of ``scalar`` on ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: d = L.basis() + sage: M = L.chargeless_representation(1/2, 3/4) + sage: x = d[-5] * M.an_element() + M.basis()[10]; x + -9/4*v[-6] - 7/4*v[-5] - 33/4*v[-4] + v[10] + sage: d[2] * x + -279/16*v[-4] - 189/16*v[-3] - 759/16*v[-2] - 33/4*v[12] + + sage: v = M.basis() + sage: all(d[i]*(d[j]*v[k]) - d[j]*(d[i]*v[k]) == d[i].bracket(d[j])*v[k] + ....: for i in range(-5, 5) for j in range(-5, 5) for k in range(-5, 5)) + True + """ + P = self.parent() + # We implement only a left action + if not self_on_left and scalar in P._V: + scalar = P._V(scalar) + return P.sum_of_terms((n+k, (P._a * n + P._b - k) * cv * cm) + for n,cv in scalar.monomial_coefficients(copy=False).items() if n != 'c' + for k,cm in self.monomial_coefficients(copy=False).items()) + return CombinatorialFreeModule.Element._acted_upon_(self, scalar, self_on_left) + + _rmul_ = _lmul_ = _acted_upon_ + +class VermaModule(CombinatorialFreeModule): + """ + A Verma module of the Virasoro algebra. + + The Virasoro algebra admits a triangular decomposition + + .. MATH:: + + V_- \oplus R d_0 \oplus R \hat{c} \oplus V_+, + + where `V_-` (resp. `V_+`) is the span of `\{d_i \mid i < 0\}` + (resp. `\{d_i \mid i > 0\}`). We can construct the *Verma module* + `M_{c,h}` as the induced representation of the `R d_0 \oplus + R \hat{c} \oplus V_+` representation `R_{c,H} = Rv`, where + + .. MATH:: + + V_+ v = 0, \qquad \hat{c} v = c v, \qquad d_0 v = h v. + + Therefore, we have a basis of `M_{c,h}` + + .. MATH:: + + \{ L_{i_1} \cdots L_{i_k} v \mid i_1 \leq \cdots \leq i_k < 0 \}. + + Moreover, the Verma modules are the free objects in the category of + highest weight representations of `V` and are indecomposable. + The Verma module `M_{c,h}` is irreducible for generic values of `c` + and `h` and when it is reducible, the quotient by the maximal + submodule is the unique irreducible highest weight representation + `V_{c,h}`. + + EXAMPLES: + + We construct a Verma module and do some basic computations:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: M = L.verma_module(3, 0) + sage: d = L.basis() + sage: v = M.highest_weight_vector() + sage: d[3] * v + 0 + sage: d[-3] * v + d[-3]*v + sage: d[-1] * (d[-3] * v) + 2*d[-4]*v + d[-3]*d[-1]*v + sage: d[2] * (d[-1] * (d[-3] * v)) + 12*d[-2]*v + 5*d[-1]*d[-1]*v + + We verify that `d_{-1} v` is a singular vector for + `\{d_i \mid 1 \leq i < 20\}`:: + + sage: w = M.basis()[-1]; w + d[-1]*v + sage: all(d[i] * w == M.zero() for i in range(1,20)) + True + + We also verify a singular vector for `V_{-2,1}`:: + + sage: M = L.verma_module(-2, 1) + sage: B = M.basis() + sage: w = B[-1,-1] - 2 * B[-2] + sage: d = L.basis() + sage: all(d[i] * w == M.zero() for i in range(1,20)) + True + + REFERENCES: + + - :wikipedia:`Virasoro_algebra#Representation_theory` + """ + @staticmethod + def __classcall_private__(cls, V, c, h): + """ + Normalize input to ensure a unique representation. + + EXAMPLES:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: M = L.verma_module(3, 1/2) + sage: M2 = L.verma_module(int(3), 1/2) + sage: M is M2 + True + """ + R = V.base_ring() + return super(VermaModule, cls).__classcall__(cls, V, R(c), R(h)) + + @staticmethod + def _partition_to_neg_tuple(x): + """ + Helper function to convert a partition to an increasing + sequence of negative numbers. + + EXAMPLES:: + + sage: from sage.algebras.lie_algebras.virasoro import VermaModule + sage: VermaModule._partition_to_neg_tuple([3,2,2,1]) + (-3, -2, -2, -1) + """ + # The entries of the partition are likely ints, but we need to + # make sure they are Integers. + return tuple([ZZ(-i) for i in x]) + + def __init__(self, V, c, h): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: M = L.verma_module(3, 1/2) + sage: TestSuite(M).run() + """ + self._c = c + self._h = h + self._V = V + from sage.combinat.partition import _Partitions + indices = _Partitions.map(VermaModule._partition_to_neg_tuple) + CombinatorialFreeModule.__init__(self, V.base_ring(), + indices, prefix='v') + + def _repr_term(self, k): + """ + Return a string representation for the term indexed by ``k``. + + EXAMPLES:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: M = L.verma_module(1, -2) + sage: M._repr_term((-3,-2,-2,-1)) + 'd[-3]*d[-2]*d[-2]*d[-1]*v' + """ + if not k: + return 'v' + d = self._V.basis() + return '*'.join(repr(d[i]) for i in k) + '*v' + + def _latex_term(self, k): + """ + Return a latex representation for the term indexed by ``k``. + + EXAMPLES:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: M = L.verma_module(1, -2) + sage: M._latex_term((-3,-2,-2,-1)) + 'd_{-3} d_{-2} d_{-2} d_{-1} v' + """ + if not k: + return 'v' + d = self._V.basis() + from sage.misc.latex import latex + return ' '.join(latex(d[i]) for i in k) + ' v' + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: M = L.verma_module(3, 0) + sage: M + Verma module with charge 3 and confromal weight 0 of + The Virasoro algebra over Rational Field + """ + return "Verma module with charge {} and confromal weight {} of {}".format( + self._c, self._h, self._V) + + def _monomial(self, index): + """ + TESTS:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: M = L.verma_module(3, 0) + sage: v = M.basis() + sage: v[-3] # indirect doctest + d[-3]*v + sage: v[-3,-2,-2] # indirect doctest + d[-3]*d[-2]*d[-2]*v + """ + if index in ZZ: + if index >= 0: + raise ValueError("sequence must have non-positive entries") + index = (index,) + return super(VermaModule, self)._monomial(index) + + def central_charge(self): + """ + Return the central charge of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: M = L.verma_module(3, 0) + sage: M.central_charge() + 3 + """ + return self._c + + def conformal_weight(self): + """ + Return the conformal weight of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: M = L.verma_module(3, 0) + sage: M.conformal_weight() + 3 + """ + return self._c + + def virasoro_algebra(self): + """ + Return the Virasoro algebra ``self`` is a representation of. + + EXAMPLES:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: M = L.verma_module(1/2, 3/4) + sage: M.virasoro_algebra() is L + True + """ + return self._V + + @cached_method + def highest_weight_vector(self): + """ + Return the highest weight vector of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: M = L.verma_module(-2/7, 3) + sage: M.highest_weight_vector() + v + """ + return self.monomial(()) + + def _d_action_on_basis(self, n, k): + """ + Return the action of `d_n` on `v_k`. + + EXAMPLES:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: M = L.verma_module(-2/7, 3) + sage: M._d_action_on_basis(-3, ()) + d[-3]*v + sage: M._d_action_on_basis(0, ()) + 3*v + sage: M._d_action_on_basis('c', ()) + -2/7*v + sage: M._d_action_on_basis('c', (-4,-2,-2,-1)) + -2/7*d[-4]*d[-2]*d[-2]*d[-1]*v + sage: M._d_action_on_basis(3, (-4,-2,-2,-1)) + 7*d[-5]*d[-1]*v + 60*d[-4]*d[-2]*v + 15*d[-4]*d[-1]*d[-1]*v + + 14*d[-3]*d[-2]*d[-1]*v + 7*d[-2]*d[-2]*d[-1]*d[-1]*v + sage: M._d_action_on_basis(-1, (-4,-2,-2,-1)) + d[-9]*d[-1]*v + d[-5]*d[-4]*d[-1]*v + 3*d[-5]*d[-2]*d[-2]*d[-1]*v + + 2*d[-4]*d[-3]*d[-2]*d[-1]*v + d[-4]*d[-2]*d[-2]*d[-1]*d[-1]*v + """ + # c acts my multiplication by self._c on all elements + if n == 'c': + return self.term(k, self._c) + + # when k corresponds to the highest weight vector + if not k: + if n > 0: + return self.zero() + if n == 0: + return self.term(k, self._h) + return self.monomial((n,)) + + # The basis are eigenvectors for d_0 + if n == 0: + return self.term(k, self._h - sum(k)) + + # We keep things in order + if n <= k[0]: + return self.monomial((n,) + k) + + # [L_n, L_m] v = L_n L_m v - L_m L_n v + # L_n L_m v = L_m L_n v + [L_n, L_m] v + d = self._V.basis() + m = k[0] + k = k[1:] + # We need to explicitly call the action as this method is + # used in discovering the action + return (self._d_action_on_basis(n, k)._acted_upon_(d[m], False) + + self.monomial(k)._acted_upon_(d[n].bracket(d[m]), False)) + + class Element(CombinatorialFreeModule.Element): + def _acted_upon_(self, scalar, self_on_left=False): + """ + Return the action of ``scalar`` on ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: d = L.basis() + sage: M = L.verma_module(1/2, 3/4) + sage: x = d[-5] * M.an_element() + M.basis()[-10]; x + d[-10]*v + 2*d[-5]*v + 3*d[-5]*d[-2]*v + 2*d[-5]*d[-1]*v + sage: d[2] * x + 12*d[-8]*v + 39/4*d[-5]*v + 14*d[-3]*v + 21*d[-3]*d[-2]*v + + 14*d[-3]*d[-1]*v + sage: v = M.highest_weight_vector() + sage: d[2] * (d[-2] * v) + 13/4*v + + sage: it = iter(M.basis()) + sage: B = [next(it) for _ in range(10)] + sage: all(d[i]*(d[j]*v) - d[j]*(d[i]*v) == d[i].bracket(d[j])*v + ....: for i in range(-5, 5) for j in range(-5, 5) for v in B) + True + """ + P = self.parent() + # We implement only a left action + if not self_on_left and scalar in P._V: + scalar = P._V(scalar) + return P.linear_combination((P._d_action_on_basis(n, k), cv * cm) + for n,cv in scalar.monomial_coefficients(copy=False).items() + for k,cm in self.monomial_coefficients(copy=False).items()) + return CombinatorialFreeModule.Element._acted_upon_(self, scalar, self_on_left) + + _rmul_ = _lmul_ = _acted_upon_ + diff --git a/src/sage/algebras/orlik_solomon.py b/src/sage/algebras/orlik_solomon.py index 1a692846c46..5ee9915d980 100644 --- a/src/sage/algebras/orlik_solomon.py +++ b/src/sage/algebras/orlik_solomon.py @@ -284,7 +284,7 @@ def product_on_basis(self, a, b): return R(coeff) * self.subset_image(ns) - # r is the accumalator + # r is the accumulator # we reverse a in the product, so add a sign # note that l>=2 here if len(a) % 4 < 2: diff --git a/src/sage/algebras/steenrod/steenrod_algebra_bases.py b/src/sage/algebras/steenrod/steenrod_algebra_bases.py index bf04407ada0..8b59bee8d3b 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra_bases.py +++ b/src/sage/algebras/steenrod/steenrod_algebra_bases.py @@ -105,7 +105,7 @@ ` in :file:`steenrod_algebra.py`. """ -from __future__ import absolute_import +from __future__ import absolute_import, division #***************************************************************************** # Copyright (C) 2008-2010 John H. Palmieri @@ -484,12 +484,12 @@ def xi_degrees(n,p=2, reverse=True): [307, 18, 1] """ from sage.rings.all import Integer - if n <= 0: return [] + if n <= 0: + return [] N = Integer(n*(p-1) + 1) - l = [int((p**d-1)/(p-1)) for d in range(1,N.exact_log(p)+1)] - if not reverse: - return l - l.reverse() + l = [(p**d-1)//(p-1) for d in range(1, N.exact_log(p)+1)] + if reverse: + l.reverse() return l ######################################################## @@ -609,7 +609,7 @@ def milnor_basis(n, p=2, **kwds): # first find the P part of each basis element. # in this part of the code (the P part), all dimensions are # divided by 2(p-1). - for dim in range(n/(2*(p-1)) + 1): + for dim in range(n//(2*(p-1)) + 1): if dim == 0: P_result = [[0]] else: @@ -625,7 +625,7 @@ def milnor_basis(n, p=2, **kwds): for p_mono in P_result: deg = n - 2*dim*(p-1) q_degrees = [1+2*(p-1)*d for d in - xi_degrees(int((deg - 1)/(2*(p-1))), p)] + [1] + xi_degrees(int((deg - 1)//(2*(p-1))), p)] + [1] q_degrees_decrease = q_degrees q_degrees.reverse() if deg % (2*(p-1)) <= len(q_degrees): @@ -721,8 +721,8 @@ def serre_cartan_basis(n, p=2, bound=1, **kwds): new = vec + (last,) result.append(new) else: # p odd - if n % (2 * (p-1)) == 0 and n/(2 * (p-1)) >= bound: - result = [(0, int(n/(2 * (p-1))), 0)] + if n % (2 * (p-1)) == 0 and n//(2 * (p-1)) >= bound: + result = [(0, int(n//(2 * (p-1))), 0)] elif n == 1: result = [(1,)] else: @@ -1041,7 +1041,7 @@ def sorting_pair(s,t,basis): # pair used for sorting the basis trunc = kwds.get("truncation_type", 0) result = [] - for dim in range(n/(2*p-2) + 1): + for dim in range(n//(2*p-2) + 1): P_result = [] for v in WeightedIntegerVectors(dim, xi_degrees(dim, p=p, reverse=False)): mono = [] diff --git a/src/sage/calculus/desolvers.py b/src/sage/calculus/desolvers.py index 192c39a9209..24bc544690b 100644 --- a/src/sage/calculus/desolvers.py +++ b/src/sage/calculus/desolvers.py @@ -1178,7 +1178,7 @@ def desolve_rk4(de, dvar, ics=None, ivar=None, end_points=None, step=0.1, output [[0, 1], [0.5, 1.12419127424558], [1.0, 1.461590162288825]] Variant 1 for input - we can pass ODE in the form used by - desolve function In this example we integrate bakwards, since + desolve function In this example we integrate backwards, since ``end_points < ics[0]``:: sage: y = function('y')(x) diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py index 78248bb4362..ab7559ea797 100644 --- a/src/sage/categories/category.py +++ b/src/sage/categories/category.py @@ -1738,7 +1738,7 @@ def morphism_class(self): This class contains the methods defined in the nested class ``self.MorphismMethods`` (if it exists), and has as bases the - morphims classes of the super categories of ``self``. + morphism classes of the super categories of ``self``. .. SEEALSO:: diff --git a/src/sage/categories/homset.py b/src/sage/categories/homset.py index eb57aee1198..a3595bd0fa8 100644 --- a/src/sage/categories/homset.py +++ b/src/sage/categories/homset.py @@ -47,8 +47,6 @@ - Simon King (2013-02): added examples """ -from __future__ import absolute_import - #***************************************************************************** # Copyright (C) 2005 David Kohel , William Stein # @@ -64,7 +62,9 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.categories.category import Category +from __future__ import absolute_import, print_function + +from sage.categories.category import Category, JoinCategory from . import morphism from sage.structure.parent import Parent, Set_generic from sage.misc.fast_methods import WithEqualityById @@ -343,6 +343,34 @@ def Hom(X, Y, category=None, check=True): si... = ... pg_Hom(si..., si..., ...) ... sage: Q == loads(dumps(Q)) True + + Check that the ``_Hom_`` method of the ``category`` input is used:: + + sage: from sage.categories.category_types import Category_over_base_ring + sage: class ModulesWithHom(Category_over_base_ring): + ....: def super_categories(self): + ....: return [Modules(self.base_ring())] + ....: class ParentMethods: + ....: def _Hom_(self, Y, category=None): + ....: print("Modules") + ....: raise TypeError + sage: class AlgebrasWithHom(Category_over_base_ring): + ....: def super_categories(self): + ....: return [Algebras(self.base_ring()), ModulesWithHom(self.base_ring())] + ....: class ParentMethods: + ....: def _Hom_(self, Y, category=None): + ....: R = self.base_ring() + ....: if category is not None and category.is_subcategory(Algebras(R)): + ....: print("Algebras") + ....: raise TypeError + sage: from sage.structure.element import Element + sage: class Foo(Parent): + ....: _no_generic_basering_coercion = True + ....: class Element(Element): + ....: pass + sage: X = Foo(base=QQ, category=AlgebrasWithHom(QQ)) + sage: H = Hom(X, X, ModulesWithHom(QQ)) + Modules """ # This should use cache_function instead # However some special handling is currently needed for @@ -394,18 +422,31 @@ def Hom(X, Y, category=None, check=True): try: # _Hom_ hook from the parent H = X._Hom_(Y, category) except (AttributeError, TypeError): - try: - # Workaround in case the above fails, but the category - # also provides a _Hom_ hook. - # FIXME: - # - If X._Hom_ actually comes from category and fails, it - # will be called twice. - # - This is bound to fail if X is an extension type and - # does not actually inherit from category.parent_class - H = category.parent_class._Hom_(X, Y, category = category) - except (AttributeError, TypeError): + # Workaround in case the above fails, but the category + # also provides a _Hom_ hook. + # FIXME: + # - If X._Hom_ actually comes from category and fails, it + # will be called twice. + # - This is bound to fail if X is an extension type and + # does not actually inherit from category.parent_class + # For join categories, we check all of the direct super + # categories as the parent_class of the join category is + # not (necessarily) inherited and join categories do not + # implement a _Hom_ (see trac #23418). + if not isinstance(category, JoinCategory): + cats = [category] + else: + cats = category.super_categories() + H = None + for C in cats: + try: + H = C.parent_class._Hom_(X, Y, category=category) + break + except (AttributeError, TypeError): + pass + if H is None: # By default, construct a plain homset. - H = Homset(X, Y, category = category, check=check) + H = Homset(X, Y, category=category, check=check) _cache[key] = H if isinstance(X, UniqueRepresentation) and isinstance(Y, UniqueRepresentation): if not isinstance(H, WithEqualityById): diff --git a/src/sage/categories/number_fields.py b/src/sage/categories/number_fields.py index a1c6336e656..0317f520a5e 100644 --- a/src/sage/categories/number_fields.py +++ b/src/sage/categories/number_fields.py @@ -26,6 +26,11 @@ class NumberFields(Category_singleton): sage: C Category of number fields + By definition, it is infinite:: + + sage: NumberFields().Infinite() is NumberFields() + True + Notice that the rational numbers `\QQ` *are* considered as an object in this category:: @@ -58,9 +63,9 @@ def super_categories(self): EXAMPLES:: sage: NumberFields().super_categories() - [Category of fields] + [Category of infinite fields] """ - return[Fields()] + return [Fields().Infinite()] def __contains__(self, x): r""" diff --git a/src/sage/coding/guruswami_sudan/gs_decoder.py b/src/sage/coding/guruswami_sudan/gs_decoder.py index ae566604ab4..e731b41c29c 100644 --- a/src/sage/coding/guruswami_sudan/gs_decoder.py +++ b/src/sage/coding/guruswami_sudan/gs_decoder.py @@ -390,7 +390,7 @@ def find_integral_max(real_max, f): lmax = sqrt(n*s*(s+1.)/(k-1.)) - 1. #the best integral value will be (l,tau) = find_integral_max(lmax, lambda l: get_tau(s,l)) - #Note that we have not proven that this ell is minimial in integral + #Note that we have not proven that this ell is minimal in integral #sense! It just seems that this most often happens return (tau,(s,l)) if l is not None: @@ -481,7 +481,7 @@ def gs_satisfactory(tau, s, l, C = None, n_k = None): INPUT: - - ``tau`` -- an integer, number of errrors one expects Guruswami-Sudan algorithm + - ``tau`` -- an integer, number of errors one expects Guruswami-Sudan algorithm to correct - ``s`` -- an integer, multiplicity parameter of Guruswami-Sudan algorithm - ``l`` -- an integer, list size parameter diff --git a/src/sage/combinat/abstract_tree.py b/src/sage/combinat/abstract_tree.py index 1bdede02f5c..4ced7fca867 100644 --- a/src/sage/combinat/abstract_tree.py +++ b/src/sage/combinat/abstract_tree.py @@ -72,6 +72,8 @@ # Unfortunately Cython forbids multiple inheritance. Therefore, we do not # inherit from SageObject to be able to inherit from Element or a subclass # of it later. + + class AbstractTree(object): """ Abstract Tree. @@ -1049,7 +1051,7 @@ def node_number(self): 5 """ if self.is_empty(): - return 0 + return Integer(0) else: return sum((i.node_number() for i in self), Integer(1)) @@ -1151,7 +1153,8 @@ def _ascii_art_(self): / / 14 15 """ - node_to_str = lambda t: str(t.label()) if hasattr(t, "label") else "o" + def node_to_str(t): + return str(t.label()) if hasattr(t, "label") else "o" if self.is_empty(): from sage.typeset.ascii_art import empty_ascii_art @@ -1159,15 +1162,15 @@ def _ascii_art_(self): from sage.typeset.ascii_art import AsciiArt if len(self) == 0: - t_repr = AsciiArt( [node_to_str(self)] ) + t_repr = AsciiArt([node_to_str(self)]) t_repr._root = 1 return t_repr if len(self) == 1: repr_child = self[0]._ascii_art_() - sep = AsciiArt( [" "*(repr_child._root-1)] ) - t_repr = AsciiArt( [node_to_str(self)] ) + sep = AsciiArt([" "*(repr_child._root-1)]) + t_repr = AsciiArt([node_to_str(self)]) t_repr._root = 1 - repr_root = (sep + t_repr)*(sep + AsciiArt( ["|"] )) + repr_root = (sep + t_repr)*(sep + AsciiArt(["|"])) t_repr = repr_root * repr_child t_repr._root = repr_child._root t_repr._baseline = t_repr._h - 1 @@ -1182,12 +1185,12 @@ def _ascii_art_(self): t_repr = l_repr.pop(0) acc += AsciiArt([" "]) + t_repr if len(l_repr) == 0: - lf_sep += "_"*(t_repr._root+1) + lf_sep += "_" * (t_repr._root + 1) else: - lf_sep += "_"*(t_repr._l+1) + lf_sep += "_" * (t_repr._l + 1) ls_sep += " "*(t_repr._root) + "/" + " "*(t_repr._l-t_repr._root) mid = whitesep + (len(lf_sep) - whitesep) // 2 - node = node_to_str( self ) + node = node_to_str(self) t_repr = AsciiArt([lf_sep[:mid-1] + node + lf_sep[mid+len(node)-1:], ls_sep]) * acc t_repr._root = mid t_repr._baseline = t_repr._h - 1 @@ -1302,8 +1305,8 @@ def node_to_str(t): tr = l_repr.pop(0) acc += UnicodeArt([u" "]) + tr if not len(l_repr): - lf_sep += u"─" * (tr._root) + u"╮" # + u" " * (tr._l - tr._root) - ls_sep += u" " * (tr._root) + u"│" # + u" " * (tr._l - tr._root) + lf_sep += u"─" * (tr._root) + u"╮" + ls_sep += u" " * (tr._root) + u"│" else: lf_sep += u"─" * (tr._root) + u"┬" + u"─" * (tr._l - tr._root) ls_sep += u" " * (tr._root) + u"│" + u" " * (tr._l - tr._root) @@ -1316,7 +1319,7 @@ def node_to_str(t): t_repr._baseline = t_repr._h - 1 return t_repr - def canonical_labelling(self,shift=1): + def canonical_labelling(self, shift=1): """ Returns a labelled version of ``self``. @@ -1345,9 +1348,9 @@ def canonical_labelling(self,shift=1): liste = [] deca = 1 for subtree in self: - liste += [subtree.canonical_labelling(shift+deca)] + liste += [subtree.canonical_labelling(shift + deca)] deca += subtree.node_number() - return LTR._element_constructor_(liste,label=shift) + return LTR._element_constructor_(liste, label=shift) def to_hexacode(self): r""" @@ -1419,7 +1422,7 @@ def tree_factorial(self): """ nb = self.node_number() if nb <= 1: - return 1 + return Integer(1) return nb * prod(s.tree_factorial() for s in self) def _latex_(self): @@ -1474,12 +1477,16 @@ def _latex_(self): space = " "*9 sepspace = sep + space spacesep = space + sep - node_to_str = lambda node: " " + node + " " * (len(space) - 1 - len(node)) + + def node_to_str(node): + return " " + node + " " * (len(space) - 1 - len(node)) # # TODO:: modify how to create nodes --> new_cmd : \\node[...] in create_node num = [0] def resolve(self): - nodes = []; matrix = []; edges = [] + nodes = [] + matrix = [] + edges = [] def create_node(self): r""" @@ -2034,7 +2041,7 @@ class AbstractLabelledTree(AbstractTree): .. SEEALSO:: :class:`AbstractTree` """ - def __init__(self, parent, children, label = None, check = True): + def __init__(self, parent, children, label=None, check=True): """ TESTS:: @@ -2100,7 +2107,7 @@ def _repr_(self): sage: LabelledOrderedTree([[],LabelledOrderedTree([[]], label=2)], label=3) 3[None[], 2[None[]]] """ - return "%s%s"%(self._label, self[:]) + return "%s%s" % (self._label, self[:]) def label(self, path=None): """ @@ -2177,7 +2184,7 @@ def leaf_labels(self): sage: LBT(None).leaf_labels() [] """ - return [t.label() for t in self.subtrees() if t.node_number()==1] + return [t.label() for t in self.subtrees() if t.node_number() == 1] def __eq__(self, other): """ @@ -2201,8 +2208,8 @@ def __eq__(self, other): sage: t1 == t2 False """ - return ( super(AbstractLabelledTree, self).__eq__(other) and - self._label == other._label ) + return (super(AbstractLabelledTree, self).__eq__(other) and + self._label == other._label) def _hash_(self): """ @@ -2292,8 +2299,8 @@ def as_digraph(self): Digraph on 4 vertices """ from sage.graphs.digraph import DiGraph - resu = dict([[self.label(), - [t.label() for t in self if not t.is_empty()]]]) + resu = {self.label(): + [t.label() for t in self if not t.is_empty()]} resu = DiGraph(resu, format="dict_of_lists") for t in self: if not t.is_empty(): diff --git a/src/sage/combinat/binary_recurrence_sequences.py b/src/sage/combinat/binary_recurrence_sequences.py index d8ecf4383c9..6ace53b8e96 100644 --- a/src/sage/combinat/binary_recurrence_sequences.py +++ b/src/sage/combinat/binary_recurrence_sequences.py @@ -374,7 +374,7 @@ def period(self, m): Return the period of the binary recurrence sequence modulo an integer ``m``. - If `n_1` is congruent to `n_2` modulu ``period(m)``, then `u_{n_1}` is + If `n_1` is congruent to `n_2` modulo ``period(m)``, then `u_{n_1}` is is congruent to `u_{n_2}` modulo ``m``. INPUT: diff --git a/src/sage/combinat/cartesian_product.py b/src/sage/combinat/cartesian_product.py index 0082a28710d..1dfb80c4473 100644 --- a/src/sage/combinat/cartesian_product.py +++ b/src/sage/combinat/cartesian_product.py @@ -19,8 +19,7 @@ from six.moves import range -from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets -from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from sage.categories.enumerated_sets import EnumeratedSets from sage.sets.set_from_iterator import EnumeratedSetFromIterator from inspect import isgenerator @@ -92,7 +91,6 @@ def CartesianProduct(*iters): deprecation(18411, "CartesianProduct is deprecated. Use cartesian_product instead") from sage.combinat.misc import IterableFunctionCall - from sage.sets.set_from_iterator import EnumeratedSetFromIterator deprecate_ifc = False iiters = [] for a in iters: @@ -163,16 +161,28 @@ def __init__(self, *iters): """ TESTS:: - sage: import sage.combinat.cartesian_product as cartesian_product - sage: cp = cartesian_product.CartesianProduct_iters([1,2],[3,4]); cp + sage: from sage.combinat.cartesian_product import CartesianProduct_iters + sage: cp = CartesianProduct_iters([1,2],[3,4]); cp Cartesian product of [1, 2], [3, 4] sage: loads(dumps(cp)) == cp True sage: TestSuite(cp).run(skip='_test_an_element') + + Check that :trac:`24558` is fixed:: + + sage: from sage.combinat.cartesian_product import CartesianProduct_iters + sage: from sage.sets.set_from_iterator import EnumeratedSetFromIterator + sage: I = EnumeratedSetFromIterator(Integers) + sage: CartesianProduct_iters(I, I) + Cartesian product of {0, 1, -1, 2, -2, ...}, {0, 1, -1, 2, -2, ...} """ self.iters = iters self._mrange = xmrange_iter(iters) - category = FiniteEnumeratedSets() if self.is_finite() else InfiniteEnumeratedSets() + category = EnumeratedSets() + try: + category = category.Finite() if self.is_finite() else category.Infinite() + except ValueError: # Unable to determine if it is finite or not + pass def iterfunc(): # we can not use self.__iterate__ directly because # that leads to an infinite recursion in __eq__ diff --git a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py index 5f96100876b..b4fd1b000e7 100644 --- a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py +++ b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py @@ -2547,7 +2547,8 @@ def mutate(self, sequence, inplace=True, input_type=None): is_vertices = set(seqq).issubset(set(seed._nlist)) is_indices = set(seqq).issubset(set(range(n))) - # Note - this does not guarantee that the sequence consists of cluster variables, it only rules out some posibilities. + # Note - this does not guarantee that the sequence consists of + # cluster variables, it only rules out some possibilities. is_cluster_vars = reduce(lambda x, y: isinstance(y, str), seqq, 1) and seed._use_fpolys # Ensures the sequence has elements of type input_type. @@ -4921,7 +4922,7 @@ def is_LeeLiZel_allowable(T,n,m,b,c): def get_green_vertices(C): r""" - Get the green vertices from a matrix. Will go through each clumn and return + Get the green vertices from a matrix. Will go through each column and return the ones where no entry is greater than 0. INPUT: diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index f236b90c536..ab6436b3fa1 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -1226,7 +1226,7 @@ def shuffle_product(self, other, overlap=False): OUTPUT: - An enumerated set (allowing for mutliplicities) + An enumerated set (allowing for multiplicities) EXAMPLES: diff --git a/src/sage/combinat/composition_tableau.py b/src/sage/combinat/composition_tableau.py index b2f9593a830..1eba224737d 100644 --- a/src/sage/combinat/composition_tableau.py +++ b/src/sage/combinat/composition_tableau.py @@ -318,7 +318,7 @@ class CompositionTableaux(UniqueRepresentation, Parent): [[3, 3, 3]]] sage: CT = CompositionTableaux([1,2,1]); CT - Composition tableaux of shape [1, 2, 1] and maximun entry 4 + Composition tableaux of shape [1, 2, 1] and maximum entry 4 sage: list(CT) [[[1], [2, 2], [3]], [[1], [2, 2], [4]], @@ -327,7 +327,7 @@ class CompositionTableaux(UniqueRepresentation, Parent): [[2], [3, 3], [4]]] sage: CT = CompositionTableaux(shape=[1,2,1],max_entry=3); CT - Composition tableaux of shape [1, 2, 1] and maximun entry 3 + Composition tableaux of shape [1, 2, 1] and maximum entry 3 sage: list(CT) [[[1], [2, 2], [3]]] @@ -363,17 +363,17 @@ def __classcall_private__(cls, *args, **kwargs): sage: CT = CompositionTableaux(size=3); CT Composition Tableaux of size 3 and maximum entry 3 sage: CT = CompositionTableaux([1,2]); CT - Composition tableaux of shape [1, 2] and maximun entry 3 + Composition tableaux of shape [1, 2] and maximum entry 3 sage: CT = CompositionTableaux(shape=[1,2]); CT - Composition tableaux of shape [1, 2] and maximun entry 3 + Composition tableaux of shape [1, 2] and maximum entry 3 sage: CT = CompositionTableaux(shape=[]); CT - Composition tableaux of shape [] and maximun entry 0 + Composition tableaux of shape [] and maximum entry 0 sage: CT = CompositionTableaux(0); CT Composition Tableaux of size 0 and maximum entry 0 sage: CT = CompositionTableaux(max_entry=3); CT Composition tableaux with maximum entry 3 sage: CT = CompositionTableaux([1,2],max_entry=3); CT - Composition tableaux of shape [1, 2] and maximun entry 3 + Composition tableaux of shape [1, 2] and maximum entry 3 sage: CT = CompositionTableaux(size=2,shape=[1,2]); CT Traceback (most recent call last): ... @@ -737,11 +737,11 @@ def _repr_(self): TESTS:: sage: CompositionTableaux([1,2,1]) - Composition tableaux of shape [1, 2, 1] and maximun entry 4 + Composition tableaux of shape [1, 2, 1] and maximum entry 4 sage: CompositionTableaux([1,2,1],max_entry=3) - Composition tableaux of shape [1, 2, 1] and maximun entry 3 + Composition tableaux of shape [1, 2, 1] and maximum entry 3 """ - return "Composition tableaux of shape %s and maximun entry %s"%(str(self.shape), str(self.max_entry)) + return "Composition tableaux of shape %s and maximum entry %s" % (str(self.shape), str(self.max_entry)) def an_element(self): r""" diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 68a5ae7da85..9eb981769ca 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -2706,7 +2706,7 @@ class FiniteStateMachine(sage.structure.sage_object.SageObject): process. - ``store_states_dict`` -- If ``True``, then additionally the states - are stored in an interal dictionary for speed up. + are stored in an internal dictionary for speed up. - ``on_duplicate_transition`` -- A function which is called when a transition is inserted into ``self`` which already existed (same diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index 447da288ae1..a7579527bf2 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -31,9 +31,11 @@ import sage.data_structures.blas_dict as blas from sage.typeset.ascii_art import AsciiArt from sage.typeset.unicode_art import UnicodeArt +from sage.misc.superseded import deprecation import six + class CombinatorialFreeModule(UniqueRepresentation, Module, IndexedGenerators): r""" Class for free modules with a named basis @@ -851,10 +853,16 @@ def get_order_cmp(self): Return a comparison function on the basis indices that is compatible with the current term order. + DEPRECATED by :trac:`24548`. + EXAMPLES:: sage: A = FiniteDimensionalAlgebrasWithBasis(QQ).example() sage: Acmp = A.get_order_cmp() + doctest:warning...: + DeprecationWarning: comparison should use keys + See http://trac.sagemath.org/24548 for details. + sage: sorted(A.basis().keys(), Acmp) ['x', 'y', 'a', 'b'] sage: A.set_order(list(reversed(A.basis().keys()))) @@ -862,6 +870,7 @@ def get_order_cmp(self): sage: sorted(A.basis().keys(), Acmp) ['b', 'a', 'y', 'x'] """ + deprecation(24548, 'comparison should use keys') self.get_order() return self._order_cmp @@ -869,6 +878,8 @@ def _order_cmp(self, x, y): """ Compare `x` and `y` w.r.t. the term order. + DEPRECATED by :trac:`24548`. + INPUT: - ``x``, ``y`` -- indices of the basis of ``self`` @@ -883,12 +894,16 @@ def _order_cmp(self, x, y): sage: A = CombinatorialFreeModule(QQ, ['x','y','a','b']) sage: A.set_order(['x', 'y', 'a', 'b']) sage: A._order_cmp('x', 'y') + doctest:warning...: + DeprecationWarning: comparison should use keys + See http://trac.sagemath.org/24548 for details. -1 sage: A._order_cmp('y', 'y') 0 sage: A._order_cmp('a', 'y') 1 """ + deprecation(24548, 'comparison should use keys') ix = self._rank_basis(x) iy = self._rank_basis(y) if ix < iy: diff --git a/src/sage/combinat/integer_matrices.py b/src/sage/combinat/integer_matrices.py index cd8b2236c2b..ec35259808a 100644 --- a/src/sage/combinat/integer_matrices.py +++ b/src/sage/combinat/integer_matrices.py @@ -323,7 +323,7 @@ def integer_matrices_generator(row_sums, column_sums): """ column_sums = list(column_sums) if sum(row_sums) != sum(column_sums): - raise StopIteration + return if not row_sums: yield [] elif len(row_sums) == 1: diff --git a/src/sage/combinat/q_analogues.py b/src/sage/combinat/q_analogues.py index f15a58caba1..5610576bbe1 100644 --- a/src/sage/combinat/q_analogues.py +++ b/src/sage/combinat/q_analogues.py @@ -2,7 +2,7 @@ r""" `q`-Analogues """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 Mike Hansen , # # Distributed under the terms of the GNU General Public License (GPL) @@ -15,7 +15,7 @@ # The full text of the GPL is available at: # # http://www.gnu.org/licenses/ -#***************************************************************************** +# **************************************************************************** # python3 from __future__ import division @@ -69,7 +69,7 @@ def q_int(n, q=None): sage: q_int(0).parent() Univariate Polynomial Ring in q over Integer Ring """ - if not n in ZZ: + if n not in ZZ: raise ValueError('%s must be an integer' % n) if q is None: @@ -80,6 +80,7 @@ def q_int(n, q=None): return sum(q**i for i in range(n)) return -q**n*sum(q**i for i in range(-n)) + def q_factorial(n, q=None): """ Returns the `q`-analogue of the factorial `n!`. @@ -105,9 +106,10 @@ def q_factorial(n, q=None): ValueError: Argument (-2) must be a nonnegative integer. """ if n in ZZ and n >= 0: - return prod([q_int(i, q) for i in range(1, n+1)]) + return prod(q_int(i, q) for i in range(1, n + 1)) else: - raise ValueError("Argument (%s) must be a nonnegative integer." %n) + raise ValueError("Argument (%s) must be a nonnegative integer." % n) + def q_binomial(n, k, q=None, algorithm='auto'): r""" @@ -297,7 +299,7 @@ def q_binomial(n, k, q=None, algorithm='auto'): if not(0 <= k and k <= n): return zero - k = min(n-k,k) # Pick the smallest k + k = min(n - k, k) # Pick the smallest k # heuristic choice of the fastest algorithm if algorithm == 'auto': @@ -322,7 +324,7 @@ def q_binomial(n, k, q=None, algorithm='auto'): # the algorithms if algorithm == 'naive': denom = prod(one - q**i for i in range(1, k+1)) - if not denom: # q is a root of unity, use the cyclotomic algorithm + if not denom: # q is a root of unity, use the cyclotomic algorithm from sage.rings.polynomial.cyclotomic import cyclotomic_value return cyclotomic_value(n, k, q, algorithm='cyclotomic') else: @@ -359,6 +361,7 @@ def gaussian_binomial(n, k, q=None, algorithm='auto'): """ return q_binomial(n, k, q, algorithm) + def q_multinomial(seq, q=None, binomial_algorithm='auto'): r""" Return the `q`-multinomial coefficient. @@ -485,11 +488,12 @@ def qt_catalan_number(n): ZZqt = ZZ['q','t'] d = {} for dw in DyckWords(n): - tup = (dw.area(),dw.bounce()) - d[tup] = d.get(tup,0)+1 + tup = (dw.area(), dw.bounce()) + d[tup] = d.get(tup, 0) + 1 return ZZqt(d) else: - raise ValueError("Argument (%s) must be a nonnegative integer." %n) + raise ValueError("Argument (%s) must be a nonnegative integer." % n) + def q_pochhammer(n, a, q=None): r""" @@ -556,6 +560,7 @@ def q_pochhammer(n, a, q=None): return R.prod(one / (one - a/q**-k) for k in range(1,-n+1)) return R.prod((one - a*q**k) for k in range(n)) + @cached_function def q_jordan(t, q): r""" @@ -614,7 +619,6 @@ def q_jordan(t, q): - Xavier Caruso (2012-06-29) """ - if q == 1: raise ValueError("q must not be equal to 1") @@ -631,6 +635,7 @@ def q_jordan(t, q): tj = ti return res + def q_subgroups_of_abelian_group(la, mu, q=None, algorithm='birkhoff'): r""" Return the `q`-number of subgroups of type ``mu`` in a finite abelian @@ -756,7 +761,7 @@ def q_subgroups_of_abelian_group(la, mu, q=None, algorithm='birkhoff'): - Tomer Bauer (2013-09-26): Implemented the Birkhoff algorithm """ if q is None: - q = ZZ['q'].gens()[0] + q = ZZ['q'].gen() la_c = Partition(la).conjugate() mu_c = Partition(mu).conjugate() k = mu_c.length() @@ -780,3 +785,64 @@ def F(args): raise ValueError("invalid algorithm choice") + +@cached_function +def q_stirling_number1(n, k, q=None): + r""" + Return the (unsigned) `q`-Stirling number of the first kind. + + This is a `q`-analogue of :func:`sage.combinat.combinat.stirling_number1` . + + INPUT: + + - ``n``, ``k`` -- integers with ``1 <= k <= n`` + + - ``q`` -- optional variable (default `q`) + + OUTPUT: a polynomial in the variable `q` + + These polynomials satisfy the recurrence + + .. MATH:: + + s_{n,k} = s_{n-1,k-1} + [n-1]_q s_{n-1, k}. + + EXAMPLES:: + + sage: from sage.combinat.q_analogues import q_stirling_number1 + sage: q_stirling_number1(4,2) + q^3 + 3*q^2 + 4*q + 3 + + sage: all(stirling_number1(6,k) == q_stirling_number1(6,k)(1) + ....: for k in range(1,7)) + True + + sage: x = polygen(QQ['q'],'x') + sage: S = sum(q_stirling_number1(5,k)*x**k for k in range(1, 6)) + sage: factor(S) + x * (x + 1) * (x + q + 1) * (x + q^2 + q + 1) * (x + q^3 + q^2 + q + 1) + + TESTS:: + + sage: q_stirling_number1(-1,2) + Traceback (most recent call last): + ... + ValueError: q-Stirling numbers are not defined for n < 0 + + REFERENCES: + + - [Ca1948]_ + + - [Ca1954]_ + """ + if q is None: + q = ZZ['q'].gen() + A = q.parent() + if n < 0: + raise ValueError('q-Stirling numbers are not defined for n < 0') + if n == 0 == k: + return A.one() + if k > n or k < 1: + return A.zero() + return (q_stirling_number1(n - 1, k - 1, q=q) + + q_int(n - 1) * q_stirling_number1(n - 1, k, q=q)) diff --git a/src/sage/combinat/root_system/cartan_type.py b/src/sage/combinat/root_system/cartan_type.py index c4231a6af0a..f3515a39f1e 100644 --- a/src/sage/combinat/root_system/cartan_type.py +++ b/src/sage/combinat/root_system/cartan_type.py @@ -473,7 +473,7 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function, absolute_import +from __future__ import print_function, absolute_import, division from six.moves import range from six.moves.builtins import sorted @@ -708,17 +708,17 @@ def __call__(self, *args): from . import type_BC_affine return type_BC_affine.CartanType(n) if letter == "A" and t[2] == 2: - if n%2 == 0: # Kac' A_2n^(2) - return CartanType(["BC", ZZ(n/2), 2]) + if n % 2 == 0: # Kac' A_2n^(2) + return CartanType(["BC", ZZ(n//2), 2]) else: # Kac' A_2n-1^(2) - return CartanType(["B", ZZ((n+1)/2), 1]).dual() + return CartanType(["B", ZZ((n+1)//2), 1]).dual() if letter == "D" and t[2] == 2: return CartanType(["C", n-1, 1]).dual() if letter == "D" and t[2] == 3 and n == 4: return CartanType(["G", 2, 1]).dual().relabel([0,2,1]) if letter == "E" and t[2] == 2 and n == 6: return CartanType(["F", 4, 1]).dual() - raise ValueError("%s is not a valid Cartan type"%t) + raise ValueError("%s is not a valid Cartan type" % t) if isinstance(t[0], string_types) and isinstance(t[1], (list, tuple)): letter, n = t[0], t[1] diff --git a/src/sage/combinat/root_system/root_lattice_realizations.py b/src/sage/combinat/root_system/root_lattice_realizations.py index 6797d671797..6d484917311 100644 --- a/src/sage/combinat/root_system/root_lattice_realizations.py +++ b/src/sage/combinat/root_system/root_lattice_realizations.py @@ -2526,7 +2526,7 @@ def plot_hedron(self, **options): sage: RootSystem(["D",3]).ambient_space().plot_hedron() Graphics3d Object - Surprise: polyhedrons of large dimension know how to + Surprise: polyhedra of large dimension know how to project themselves nicely:: sage: RootSystem(["F",4]).ambient_space().plot_hedron() # long time diff --git a/src/sage/combinat/similarity_class_type.py b/src/sage/combinat/similarity_class_type.py index 4e49bbcecd0..2110f05a97f 100644 --- a/src/sage/combinat/similarity_class_type.py +++ b/src/sage/combinat/similarity_class_type.py @@ -2,7 +2,7 @@ Similarity class types of matrices with entries in a finite field The notion of a matrix conjugacy class type was introduced by J. A. Green in -[Green55]_, in the context of computing the irreducible charcaters of finite +[Green55]_, in the context of computing the irreducible characters of finite general linear groups. The class types are equivalence classes of similarity classes of square matrices with entries in a finite field which, roughly speaking, have the same qualitative properties. diff --git a/src/sage/combinat/species/series.py b/src/sage/combinat/species/series.py index bdad43ef40f..2a5f966432d 100644 --- a/src/sage/combinat/species/series.py +++ b/src/sage/combinat/species/series.py @@ -406,10 +406,9 @@ def _product_generator_gen(self, g): """ EXAMPLES:: - sage: from builtins import map sage: from sage.combinat.species.stream import _integers_from sage: L = LazyPowerSeriesRing(QQ) - sage: g = map(lambda i: L([1]+[0]*i+[1]), _integers_from(0)) + sage: g = (L([1]+[0]*i+[1]) for i in _integers_from(0)) sage: g2 = L._product_generator_gen(g) sage: [next(g2) for i in range(10)] [1, 1, 2, 4, 7, 12, 20, 33, 53, 84] @@ -1488,7 +1487,7 @@ def _integral_zero_gen(self, ao): #Check to see if the stream is finite if self.is_finite(n-1): yield self._stream[n-1] - raise StopIteration + break else: yield (Integer(1)/Integer(n))*self._stream[n-1] n += 1 @@ -1511,7 +1510,6 @@ def _integral_nonzero_gen(self, integration_constant): if ao == inf: yield self._zero - raise StopIteration else: for _ in range(ao-1): yield self._zero @@ -1523,7 +1521,7 @@ def _integral_nonzero_gen(self, integration_constant): #Check to see if the stream is finite if self.is_finite(n-1): yield self.coefficient(n-1) - raise StopIteration + break else: yield (Integer(1)/Integer(n))*self.coefficient(n-1) n += 1 diff --git a/src/sage/combinat/species/stream.py b/src/sage/combinat/species/stream.py index 4b1335975e8..1040411f1b2 100644 --- a/src/sage/combinat/species/stream.py +++ b/src/sage/combinat/species/stream.py @@ -358,7 +358,6 @@ def __iter__(self): except IndexError: break i += 1 - raise StopIteration def __len__(self): """ diff --git a/src/sage/combinat/tamari_lattices.py b/src/sage/combinat/tamari_lattices.py index 2dfba4b6b6b..39aabcee00b 100644 --- a/src/sage/combinat/tamari_lattices.py +++ b/src/sage/combinat/tamari_lattices.py @@ -163,7 +163,7 @@ def GeneralizedTamariLattice(a, b, m=1): - `a` and `b` coprime integers with `a \geq b` - - `m` a nonnegative integer such that `a \geq b \times m` + - `m` a nonnegative integer such that `a \geq b m` OUTPUT: @@ -222,21 +222,26 @@ def covers(p): for p in paths_in_triangle(a, b, a, b)])) -def TamariLattice(n): +def TamariLattice(n, m=1): r""" Return the `n`-th Tamari lattice. + Using the slope parameter `m`, one can also get the `m`-Tamari lattices. + INPUT: - - `n` a nonnegative integer + - `n` -- a nonnegative integer (the index) + + - `m` -- an optional nonnegative integer (the slope, default to 1) OUTPUT: - - a finite lattice + a finite lattice - The elements of the lattice are - :func:`Dyck paths` - in the `(n+1 \times n)`-rectangle. + In the usual case, the elements of the lattice are :func:`Dyck + paths` in the `(n+1 \times + n)`-rectangle. For a general slope `m`, the elements are Dyck + paths in the `(m n+1 \times n)`-rectangle. See :wikipedia:`Tamari lattice` for mathematical background. @@ -245,5 +250,12 @@ def TamariLattice(n): sage: posets.TamariLattice(3) Finite lattice containing 5 elements + + sage: posets.TamariLattice(3, 2) + Finite lattice containing 12 elements + + REFERENCES: + + - [BMFPR]_ """ - return GeneralizedTamariLattice(n + 1, n, 1) + return GeneralizedTamariLattice(m * n + 1, n, m) diff --git a/src/sage/combinat/tiling.py b/src/sage/combinat/tiling.py index 206d7f6b639..f761f8f2c97 100644 --- a/src/sage/combinat/tiling.py +++ b/src/sage/combinat/tiling.py @@ -1834,7 +1834,8 @@ def _dlx_solutions_iterator(self): [[0, 7, 14], [0, 12, 10], [6, 13, 5], [6, 14, 2], [11, 9, 5], [11, 10, 3]] """ if len(self.rows()) == 0: - raise StopIteration + return + x = self.dlx_solver() while x.search() == 1: yield x.get_solution() @@ -2047,7 +2048,7 @@ def solve(self, partial=None): """ if not self.is_suitable(): - raise StopIteration + return if partial is None: it = self._dlx_solutions_iterator() elif partial == 'common_prefix': diff --git a/src/sage/combinat/tutorial.py b/src/sage/combinat/tutorial.py index 95f07084e97..cf479065cfd 100644 --- a/src/sage/combinat/tutorial.py +++ b/src/sage/combinat/tutorial.py @@ -1270,27 +1270,18 @@ sage: from builtins import map, filter +but they should rather be avoided, using list comprehension instead. + To apply a function to all the elements, one can do:: - sage: from builtins import map - sage: list(map(lambda z: z.cycle_type(), Permutations(3))) + sage: list(z.cycle_type() for z in Permutations(3)) [[1, 1, 1], [2, 1], [2, 1], [3], [3], [2, 1]] and similarly to select the elements satisfying a certain condition:: - sage: from builtins import filter - sage: list(filter(lambda z: z.has_pattern([1,2]), Permutations(3))) + sage: list(z for z in Permutations(3) if z.has_pattern([1,2])) [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2]] -In all these situations, ``attrcall`` can be an advantageous alternative -to creating an anonymous function:: - - sage: from builtins import map - sage: list(map(lambda z: z.cycle_type(), Permutations(3))) - [[1, 1, 1], [2, 1], [2, 1], [3], [3], [2, 1]] - sage: list(map(attrcall("cycle_type"), Permutations(3))) - [[1, 1, 1], [2, 1], [2, 1], [3], [3], [2, 1]] - Implementation of new iterators ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/sage/combinat/words/abstract_word.py b/src/sage/combinat/words/abstract_word.py index 4dd0b0f092e..23ed90125f4 100644 --- a/src/sage/combinat/words/abstract_word.py +++ b/src/sage/combinat/words/abstract_word.py @@ -39,8 +39,11 @@ from sage.combinat.words.word_options import word_options from itertools import islice, groupby from sage.rings.all import Integers, ZZ, Infinity +from sage.structure.richcmp import (richcmp_method, rich_to_bool, + richcmp, op_LT, op_GT) +@richcmp_method class Word_class(SageObject): def parent(self): r""" @@ -258,93 +261,43 @@ def __len__(self): raise TypeError(msg) return int(L) - def __cmp__(self, other): + def __richcmp__(self, other, op): r""" - Compares two words lexicographically according to the ordering - defined by the parent of self. This corresponds to Python's built-in + Compare two words lexicographically according to the ordering + defined by the parent of ``self``. + + This corresponds to Python's built-in ordering when no parent nor alphabet was used to defined the word. Provides for all normal comparison operators. .. NOTE:: - This function will not terminate if self and other are equal - infinite words! + This function will not terminate if ``self`` and ``other`` + are equal infinite words! EXAMPLES:: sage: W = Word sage: from itertools import count - sage: W(range(1,10)).__cmp__(W(range(10))) > 0 + sage: W(range(1,10)) > W(range(10)) True - sage: W(range(10)).__cmp__(W(range(1,10))) < 0 + sage: W(range(10)) < W(range(1,10)) True - sage: W(range(10)).__cmp__(W(range(10))) == 0 + sage: W(range(10)) == W(range(10)) True - sage: W(range(10)).__cmp__(W(count())) < 0 + sage: W(range(10)) < W(count()) True - sage: W(count()).__cmp__(W(range(10))) > 0 + sage: W(count()) > W(range(10)) True :: sage: W = Words(['a', 'b', 'c']) - sage: W('a').__cmp__(W([])) - 1 - sage: W([]).__cmp__(W('a')) - -1 - """ - if not isinstance(other, Word_class): - return NotImplemented - self_it, other_it = iter(self), iter(other) - cmp_key = self._parent.sortkey_letters - while True: - try: - cs = next(self_it) - except StopIteration: - try: - co = next(other_it) - except StopIteration: - # If both self_it and other_it are exhausted then - # self == other. Return 0. - return 0 - else: - # If self_it is exhausted, but not other_it, then - # self is a proper prefix of other: return -1 - return -1 - else: - try: - co = next(other_it) - except StopIteration: - # If self_it is not exhausted but other_it is, then - # other is a proper prefix of self: return 1. - return 1 - else: - key_cs = cmp_key(cs) - key_co = cmp_key(co) - if key_cs < key_co: - return -1 - elif key_cs > key_co: - return 1 - - def __eq__(self, other): - r""" - Returns True if self is equal to other and False otherwise. - - INPUT: - - - ``other`` - a word - - OUTPUT: - - boolean - - .. NOTE:: - - This function will not terminate if self and other are equal - infinite words! - - EXAMPLES:: + sage: W('a') > W([]) + True + sage: W([]) < W('a') + True sage: Word('abc') == Word(['a','b','c']) True @@ -369,11 +322,21 @@ def __eq__(self, other): sage: Word(lambda n:n) == Word(range(20)) False - Beware the following does not halt!:: + Beware the following does not halt! :: sage: from itertools import count sage: Word(lambda n:n) == Word(count()) #not tested + Examples for unequality:: + + sage: w = Word(range(10)) + sage: z = Word(range(10)) + sage: w != z + False + sage: u = Word(range(12)) + sage: u != w + True + TESTS:: sage: Word(count())[:20] == Word(range(20)) @@ -388,6 +351,7 @@ def __eq__(self, other): if not isinstance(other, Word_class): return NotImplemented self_it, other_it = iter(self), iter(other) + cmp_key = self._parent.sortkey_letters while True: try: cs = next(self_it) @@ -397,50 +361,25 @@ def __eq__(self, other): except StopIteration: # If both self_it and other_it are exhausted then # self == other. Return 0. - return True + return rich_to_bool(op, 0) else: # If self_it is exhausted, but not other_it, then # self is a proper prefix of other: return -1 - return False + return rich_to_bool(op, -1) else: try: co = next(other_it) except StopIteration: # If self_it is not exhausted but other_it is, then # other is a proper prefix of self: return 1. - return False + return rich_to_bool(op, 1) else: - if cs != co: - return False - - def __ne__(self, other): - r""" - Returns True if self is not equal to other and False otherwise. - - INPUT: - - - ``other`` - a word - - OUTPUT: - - boolean - - .. NOTE:: - - This function will not terminate if self and other are equal - infinite words! - - EXAMPLES:: - - sage: w = Word(range(10)) - sage: z = Word(range(10)) - sage: w != z - False - sage: u = Word(range(12)) - sage: u != w - True - """ - return not self == other + key_cs = cmp_key(cs) + key_co = cmp_key(co) + if key_cs < key_co: + return rich_to_bool(op, -1) + elif key_cs > key_co: + return rich_to_bool(op, 1) def _longest_common_prefix_iterator(self, other): r""" @@ -470,9 +409,7 @@ def _longest_common_prefix_iterator(self, other): if b == c: yield b else: - raise StopIteration - else: - raise StopIteration + break def longest_common_prefix(self, other, length='unknown'): r""" @@ -600,11 +537,11 @@ def _longest_periodic_prefix_iterator(self, period=1): sage: list(Word([1,2,1,2,1,3])._longest_periodic_prefix_iterator(2)) [1, 2, 1, 2, 1] """ - for i,l in enumerate(self): - if self[i%period] == l: + for i, l in enumerate(self): + if self[i % period] == l: yield l else: - raise StopIteration + break def longest_periodic_prefix(self, period=1): r""" @@ -770,7 +707,7 @@ def lex_less(self, other): sage: t[:10].lex_less(t) True """ - return self < other + return richcmp(self, other, op_LT) def lex_greater(self, other): r""" @@ -800,9 +737,9 @@ def lex_greater(self, other): sage: t.lex_greater(t[:10]) True """ - return self > other + return richcmp(self, other, op_GT) - def apply_morphism(self,morphism): + def apply_morphism(self, morphism): r""" Returns the word obtained by applying the morphism to self. @@ -874,13 +811,14 @@ def _delta_iterator(self): def delta(self): r""" - Returns the image of self under the delta morphism. This is the - word composed of the length of consecutive runs of the same letter - in a given word. + Returns the image of self under the delta morphism. + + This is the word composed of the length of consecutive runs of + the same letter in a given word. OUTPUT: - Word over integers + Word over integers EXAMPLES: @@ -903,7 +841,8 @@ def delta(self): word: 1211222112112112221122211222112112112221... """ from sage.combinat.words.word import Word - return Word(self._delta_iterator()) + from sage.rings.semirings.non_negative_integer_semiring import NN + return Word(self._delta_iterator(), alphabet=NN) def _iterated_right_palindromic_closure_iterator(self, f=None): r""" diff --git a/src/sage/combinat/words/alphabet.py b/src/sage/combinat/words/alphabet.py index fd7d97ac78a..9e84451f6af 100644 --- a/src/sage/combinat/words/alphabet.py +++ b/src/sage/combinat/words/alphabet.py @@ -142,7 +142,7 @@ def build_alphabet(data=None, names=None, name=None): The other families for the option ``name`` are among 'lower', 'upper', 'space', 'underscore', 'punctuation', 'printable', 'binary', 'octal', 'decimal', 'hexadecimal', 'radix64' which refer to standard set of - charaters. Theses names may be combined by separating them by a space:: + characters. Theses names may be combined by separating them by a space:: sage: build_alphabet(name="lower") {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'} diff --git a/src/sage/combinat/words/word_datatypes.pyx b/src/sage/combinat/words/word_datatypes.pyx index 9d9654f7270..5ba87408f43 100644 --- a/src/sage/combinat/words/word_datatypes.pyx +++ b/src/sage/combinat/words/word_datatypes.pyx @@ -174,24 +174,15 @@ cdef class WordDatatype_list(WordDatatype): http://docs.cython.org/docs/special_methods.html """ - if op == Py_EQ: - if isinstance(other, WordDatatype_list): + if isinstance(other, WordDatatype_list): + if op == Py_EQ: return self._data == other._data - else: - # Otherwise, force FiniteWord_class.__eq__ to do it - # (if we don't force it, then __cmp__ is called before) - from sage.combinat.words.word import FiniteWord_class - return FiniteWord_class.__eq__(self, other) - elif op == Py_NE: - if isinstance(other, WordDatatype_list): + elif op == Py_NE: return self._data != other._data - else: - # Otherwise, force FiniteWord_class.__eq__ to do it - # (if we don't force it, then __cmp__ is called before) - from sage.combinat.words.word import FiniteWord_class - return not FiniteWord_class.__eq__(self,other) - else: - return NotImplemented + + # Otherwise, force FiniteWord_class.__richcmp__ to do it + from sage.combinat.words.word import FiniteWord_class + return FiniteWord_class.__richcmp__(self, other, op) def __len__(self): r""" @@ -385,24 +376,15 @@ cdef class WordDatatype_str(WordDatatype): http://docs.cython.org/docs/special_methods.html """ - if op == Py_EQ: - if isinstance(other, WordDatatype_str): + if isinstance(other, WordDatatype_str): + if op == Py_EQ: return self._data == other._data - else: - # Otherwise, force FiniteWord_class.__eq__ to do it - # (if we don't force it, then __cmp__ is called before) - from sage.combinat.words.word import FiniteWord_class - return FiniteWord_class.__eq__(self,other) - elif op == Py_NE: - if isinstance(other, WordDatatype_str): + elif op == Py_NE: return self._data != other._data - else: - # Otherwise, force FiniteWord_class.__eq__ to do it - # (if we don't force it, then __cmp__ is called before) - from sage.combinat.words.word import FiniteWord_class - return not FiniteWord_class.__eq__(self,other) - else: - return NotImplemented + + # Otherwise, force FiniteWord_class.__richcmp__ to do it + from sage.combinat.words.word import FiniteWord_class + return FiniteWord_class.__richcmp__(self, other, op) def __contains__(self, a): r""" @@ -998,24 +980,15 @@ cdef class WordDatatype_tuple(WordDatatype): http://docs.cython.org/docs/special_methods.html """ - if op == Py_EQ: - if isinstance(other, WordDatatype_tuple): + if isinstance(other, WordDatatype_tuple): + if op == Py_EQ: return self._data == other._data - else: - # Otherwise, force FiniteWord_class.__eq__ to do it - # (if we don't force it, then __cmp__ is called before) - from sage.combinat.words.word import FiniteWord_class - return FiniteWord_class.__eq__(self,other) - elif op == Py_NE: - if isinstance(other, WordDatatype_tuple): + elif op == Py_NE: return self._data != other._data - else: - # Otherwise, force FiniteWord_class.__eq__ to do it - # (if we don't force it, then __cmp__ is called before) - from sage.combinat.words.word import FiniteWord_class - return not FiniteWord_class.__eq__(self,other) - else: - return NotImplemented + + # Otherwise, force FiniteWord_class.__richcmp__ to do it + from sage.combinat.words.word import FiniteWord_class + return FiniteWord_class.__richcmp__(self, other, op) def __len__(self): r""" diff --git a/src/sage/cpython/string.pxd b/src/sage/cpython/string.pxd index d8157669932..eefc2d685ea 100644 --- a/src/sage/cpython/string.pxd +++ b/src/sage/cpython/string.pxd @@ -15,31 +15,33 @@ from libc.string cimport strlen from cpython.bytes cimport PyBytes_AS_STRING, PyBytes_FromString from cpython.unicode cimport PyUnicode_Decode, PyUnicode_AsEncodedString -IF PY_MAJOR_VERSION >= 3: - cdef extern from "Python.h": - # Missing from cpython.unicode in Cython 0.27.3 - char* PyUnicode_AsUTF8(object s) - str PyUnicode_DecodeLocale(const char* s, const char* errors) - bytes PyUnicode_EncodeLocale(object s, const char* errors) + +cdef extern from "Python.h": + # Missing from cpython.unicode in Cython 0.27.3 + char* PyUnicode_AsUTF8(object s) cdef inline str char_to_str(const char* c, encoding=None, errors=None): IF PY_MAJOR_VERSION <= 2: return PyBytes_FromString(c) ELSE: - cdef char* err + cdef const char* err + cdef const char* enc + if errors is None: err = NULL # implies "strict" else: err = PyUnicode_AsUTF8(errors) if encoding is None: - return PyUnicode_DecodeLocale(c, err) + enc = NULL # default to utf-8 + else: + enc = PyUnicode_AsUTF8(encoding) - return PyUnicode_Decode(c, strlen(c), PyUnicode_AsUTF8(encoding), err) + return PyUnicode_Decode(c, strlen(c), enc, err) -cpdef inline bytes_to_str(b, encoding=None, errors=None): +cpdef inline str bytes_to_str(b, encoding=None, errors=None): r""" Convert ``bytes`` to ``str``. @@ -66,49 +68,71 @@ cpdef inline bytes_to_str(b, encoding=None, errors=None): raise TypeError(f"expected bytes, {type(b).__name__} found") IF PY_MAJOR_VERSION <= 2: - return b + return b ELSE: return char_to_str(PyBytes_AS_STRING(b), encoding=encoding, errors=errors) -cpdef inline str_to_bytes(s, encoding=None, errors=None): +cpdef inline bytes str_to_bytes(s, encoding=None, errors=None): r""" - Convert ``str`` to ``bytes``. + Convert ``str`` or ``unicode`` to ``bytes``. + + On Python 3 this encodes the given ``str`` to a Python 3 ``bytes`` + using the specified encoding. - On Python 2 this is a no-op since ``str is bytes``. On Python 3 - this encodes the given ``str`` to a Python 3 ``bytes`` using the - specified encoding. + On Python 2 this is a no-op on ``str`` input since ``str is bytes``. + However, this function also accepts Python 2 ``unicode`` objects and + treats them the same as Python 3 unicode ``str`` objects. EXAMPLES:: sage: import six sage: from sage.cpython.string import str_to_bytes sage: if six.PY2: - ....: b = str_to_bytes('\xcf\x80') + ....: bs = [str_to_bytes('\xcf\x80'), str_to_bytes(u'π')] ....: else: - ....: b = str_to_bytes(u'π') - sage: b == b'\xcf\x80' + ....: bs = [str_to_bytes(u'π')] + sage: all(b == b'\xcf\x80' for b in bs) True sage: str_to_bytes([]) Traceback (most recent call last): ... - TypeError: expected str, list found + TypeError: expected str ... list found """ - # Make this check explicit to avoid obscure error message below - if not isinstance(s, str): - raise TypeError(f"expected str, {type(s).__name__} found") + cdef const char* err + cdef const char* enc IF PY_MAJOR_VERSION <= 2: - return s + # Make this check explicit to avoid obscure error message below + if isinstance(s, str): + # On Python 2 str is already bytes so this should be a no-op + return s + elif not isinstance(s, unicode): + raise TypeError( + f"expected str or unicode, {type(s).__name__} found") + + if errors is None: + err = NULL # implies "strict" + else: + err = errors + + if encoding is None: + enc = 'utf-8' + else: + enc = encoding ELSE: - cdef char* err + if not isinstance(s, str): + raise TypeError(f"expected str, {type(s).__name__} found") + if errors is None: err = NULL # implies "strict" else: err = PyUnicode_AsUTF8(errors) if encoding is None: - return PyUnicode_EncodeLocale(s, err) + enc = NULL # default to utf-8 + else: + enc = PyUnicode_AsUTF8(encoding) - return PyUnicode_AsEncodedString(s, PyUnicode_AsUTF8(encoding), err) + return PyUnicode_AsEncodedString(s, enc, err) diff --git a/src/sage/cpython/string.pyx b/src/sage/cpython/string.pyx index db7164126a7..0cc5e57d68e 100644 --- a/src/sage/cpython/string.pyx +++ b/src/sage/cpython/string.pyx @@ -19,7 +19,7 @@ import sys # Provide this as a shortcut to calling sys.getfilesystemencoding(), which -# after interpeter initialization is constant. +# after interpreter initialization is constant. FS_ENCODING = sys.getfilesystemencoding() # Functions in this module are implemented in the .pxd file for inlining. diff --git a/src/sage/crypto/boolean_function.pyx b/src/sage/crypto/boolean_function.pyx index f3ab09ffd5e..35a1fcc1790 100644 --- a/src/sage/crypto/boolean_function.pyx +++ b/src/sage/crypto/boolean_function.pyx @@ -636,14 +636,14 @@ cdef class BooleanFunction(SageObject): 1 sage: B([1,0]) 1 - sage: B(7) + sage: B(4) Traceback (most recent call last): ... IndexError: index out of bound """ if isinstance(x, (int,long,Integer)): - if x > self._truth_table.size: + if x >= self._truth_table.size: raise IndexError("index out of bound") return bitset_in(self._truth_table,x) elif isinstance(x, list): diff --git a/src/sage/data_structures/blas_dict.pyx b/src/sage/data_structures/blas_dict.pyx index ca2355731e6..5dfa06dbd86 100644 --- a/src/sage/data_structures/blas_dict.pyx +++ b/src/sage/data_structures/blas_dict.pyx @@ -132,7 +132,7 @@ cpdef int iaxpy(a, dict X, dict Y, bint remove_zeros=True, bint factor_on_left=T else: value = value*a if not value: - continue # a is a zero divizor + continue # a is a zero divisor # assert value if key in Y: Y[key] += value diff --git a/src/sage/databases/sql_db.py b/src/sage/databases/sql_db.py index db2b7317d31..a95b3502b92 100644 --- a/src/sage/databases/sql_db.py +++ b/src/sage/databases/sql_db.py @@ -772,7 +772,7 @@ def _merge_queries(self, other, ret, join_table, join_dict, operator): if re.search(pattern, self.__query_string__) \ or re.search(pattern, other.__query_string__): raise TypeError('Input queries have joins but join ' \ - + 'paramaters are NoneType') + + 'parameters are NoneType') s = ((self.__query_string__).upper()).split('FROM ') o = ((other.__query_string__).upper()).split('FROM ') s = s[1].split(' WHERE ') diff --git a/src/sage/databases/stein_watkins.py b/src/sage/databases/stein_watkins.py index 3bf373a1d14..eeb2bcb5981 100644 --- a/src/sage/databases/stein_watkins.py +++ b/src/sage/databases/stein_watkins.py @@ -300,7 +300,7 @@ def iter_levels(self): except StopIteration: if C != []: yield C - raise StopIteration + return if E.conductor != N: if C != []: yield C diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index 71088fc9102..8d880c3dd74 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -113,6 +113,51 @@ def remove_unicode_u(string): return string +_type_repr_re = re.compile(r"[^']+)'>") + +def normalize_type_repr(s): + r""" + Convert the repr of type objects (e.g. ``int``, ``float``) from their + Python 2 representation to their Python 3 representation. + + In Python 2, the repr of built-in types like ``int`` is like + ````, whereas user-defined pure Python classes are displayed + as ````. On Python 3 this was normalized so that + built-in types are represented the same as user-defined classes (e.g. + ````. + + This simply normalizes all class/type reprs to the Python 3 convention for + the sake of output checking. + + EXAMPLES:: + + sage: from sage.doctest.parsing import normalize_type_repr + sage: s = "" + sage: normalize_type_repr(s) + "" + sage: normalize_type_repr(repr(float)) + "" + + This can work on multi-line output as well:: + + sage: s = "The desired output was \n" + sage: s += "The received output was " + sage: print(normalize_type_repr(s)) + The desired output was + The received output was + + And should work when types are embedded in other nested expressions:: + + sage: normalize_type_repr(repr([Integer, float])) + "[, ]" + """ + + def subst(m): + return "".format(m.group('name')) + + return _type_repr_re.sub(subst, s) + + def parse_optional_tags(string): """ Returns a set consisting of the optional tags from the following @@ -879,6 +924,14 @@ def check_output(self, want, got, optionflags): [u'Fermat', u'Euler'] sage: c = u'you'; c u'you' + + Also allowance for the difference in reprs of ``type`` instances (i.e. + classes) between Python 2 and Python 3:: + + sage: int + + sage: float + """ got = self.human_readable_escape_sequences(got) if isinstance(want, MarkedOutput): @@ -904,13 +957,33 @@ def check_output(self, want, got, optionflags): return all(a.overlaps(b) for a, b in zip(want_intervals, got_values)) ok = doctest.OutputChecker.check_output(self, want, got, optionflags) - if ok or ('u"' not in want and "u'" not in want): + + if ok: return ok - # accept the same answer where strings have unicode prefix u - # for smoother transition to python3 - if not six.PY2: - want = remove_unicode_u(want) + # Possibly fix up the desired output to account for the difference in + # reprs of some objects between Python 2 and Python 3 + # Since most of the tests are currently written for Python 2 the only + # fixups we perform right now are on Python 3 + if six.PY2: + repr_fixups = [] + else: + repr_fixups = [ + (lambda g, w: 'u"' in w or "u'" in w, remove_unicode_u), + (lambda g, w: ' vertices[-1][0]: return vertices[-1][1] + lastslope * (x - vertices[-1][0]) - a = 0; b = len(vertices) + a = 0 + b = len(vertices) while b - a > 1: - c = floor((a+b)/2) + c = (a + b) // 2 if vertices[c][0] < x: a = c else: diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index f94b3ffaf6a..3f47e629064 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -23,6 +23,7 @@ from sage.misc.all import cached_method, prod from sage.misc.package import is_package_installed, PackageNotFoundError +from sage.misc.randstate import current_randstate from sage.rings.all import QQ, ZZ, AA from sage.rings.real_double import RDF @@ -38,6 +39,8 @@ from sage.misc.superseded import deprecated_function_alias +from sage.categories.sets_cat import EmptySetError + ######################################################################### # Notes if you want to implement your own backend: # @@ -3085,7 +3088,7 @@ def dilation(self, scalar): TESTS: - Dilation of empty polyhedrons works, see :trac:`14987`:: + Dilation of empty polyhedra works, see :trac:`14987`:: sage: p = Polyhedron(ambient_dim=2); p The empty polyhedron in ZZ^2 @@ -5315,6 +5318,157 @@ def integral_points(self, threshold=100000): # assert all(self.contains(p) for p in points) # slow return tuple(points) + def get_integral_point(self, index, **kwds): + r""" + Return the ``index``-th integral point in this polyhedron. + + This is equivalent to ``sorted(self.integral_points())[index]``. + However, so long as self.integral_points_count() does not need to + enumerate all integral points, neither does this method. Hence it can + be significantly faster. If the polyhedron is not compact, a + ``ValueError`` is raised. + + INPUT: + + - ``index`` -- integer. The index of the integral point to be found. If + this is not in [0, ``self.integral_point_count()``), an ``IndexError`` + is raised. + + - ``**kwds`` -- optional keyword parameters that are passed to + :meth:`self.integral_points_count`. + + ALGORITHM: + + The function computes each of the components of the requested point in + turn. To compute x_i, the ith component, it bisects the upper and lower + bounds on x_i given by the bounding box. At each bisection, it uses + :meth:`integral_points_count` to determine on which side of the + bisecting hyperplane the requested point lies. + + .. SEEALSO:: + + :meth:`integral_points_count`. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[(-1,-1),(1,0),(1,1),(0,1)]) + sage: P.get_integral_point(1) + (0, 0) + sage: P.get_integral_point(4) + (1, 1) + sage: sorted(P.integral_points()) + [(-1, -1), (0, 0), (0, 1), (1, 0), (1, 1)] + sage: P.get_integral_point(5) + Traceback (most recent call last): + ... + IndexError: ... + + sage: Q = Polyhedron([(1,3), (2, 7), (9, 77)]) + sage: [Q.get_integral_point(i) for i in range(Q.integral_points_count())] == sorted(Q.integral_points()) + True + sage: Q.get_integral_point(0, explicit_enumeration_threshold=0, triangulation='cddlib') # optional - latte_int + (1, 3) + sage: Q.get_integral_point(0, explicit_enumeration_threshold=0, triangulation='cddlib', foo=True) # optional - latte_int + Traceback (most recent call last): + ... + RuntimeError: ... + + sage: R = Polyhedron(vertices=[[1/2, 1/3]], rays=[[1, 1]]) + sage: R.get_integral_point(0) + Traceback (most recent call last): + ... + ValueError: ... + """ + + if not self.is_compact(): + raise ValueError('Can only enumerate points in a compact polyhedron.') + + if not 0 <= index < self.integral_points_count(**kwds): + raise IndexError('polytope index out of range') + + D = self.ambient_dim() + lower_bounds, upper_bounds = self.bounding_box() + coordinate = [] + P = self + S = self.parent() + for i in range(D): # Now compute x_i, the ith component of coordinate. + lower, upper = ceil(lower_bounds[i]), floor(upper_bounds[i]) + 1 # So lower <= x_i < upper. + while lower < upper-1: + guess = (lower + upper) // 2 # > lower. + # Build new polyhedron by intersecting P with the halfspace {x_i < guess}. + P_lt_guess = P.intersection(S(None, ([[guess-1] + [0] * i + [-1] + [0] * (D - i - 1)], []))) + # Avoid computing P_geq_guess = P.intersection({x_i >= guess}) right now, it might not be needed. + P_lt_guess_count = P_lt_guess.integral_points_count(**kwds) + if P_lt_guess_count > index: # Move upper down to guess. + upper = guess + index -= 0 + P = P_lt_guess + else: # P_lt_guess_count <= index: # Move lower up to guess. + lower = guess + index -= P_lt_guess_count + P_geq_guess = P.intersection(S(None, ([[-guess] + [0] * i + [1] + [0] * (D - i - 1)], []))) + P = P_geq_guess + coordinate.append(lower) # Record the new component that we have found. + point = vector(ZZ, coordinate) + point.set_immutable() + return point + + def random_integral_point(self, **kwds): + r""" + Return an integral point in this polyhedron chosen uniformly at random. + + INPUT: + + - ``**kwds`` -- optional keyword parameters that are passed to + :meth:`self.get_integral_point`. + + OUTPUT: + + The integral point in the polyhedron chosen uniformly at random. If the + polyhedron is not compact, a ``ValueError`` is raised. If the + polyhedron does not contain any integral points, an ``EmptySetError`` is + raised. + + .. SEEALSO:: + + :meth:`get_integral_point`. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[(-1,-1),(1,0),(1,1),(0,1)]) + sage: P.random_integral_point() # random + (0, 0) + sage: P.random_integral_point() in P.integral_points() + True + sage: P.random_integral_point(explicit_enumeration_threshold=0, triangulation='cddlib') # random, optional - latte_int + (1, 1) + sage: P.random_integral_point(explicit_enumeration_threshold=0, triangulation='cddlib', foo=7) # optional - latte_int + Traceback (most recent call last): + ... + RuntimeError: ... + + sage: Q = Polyhedron(vertices=[(2, 1/3)], rays=[(1, 2)]) + sage: Q.random_integral_point() + Traceback (most recent call last): + ... + ValueError: ... + + sage: R = Polyhedron(vertices=[(1/2, 0), (1, 1/2), (0, 1/2)]) + sage: R.random_integral_point() + Traceback (most recent call last): + ... + EmptySetError: ... + """ + + if not self.is_compact(): + raise ValueError('Can only sample integral points in a compact polyhedron.') + + count = self.integral_points_count() + if count == 0: + raise EmptySetError('Polyhedron does not contain any integral points.') + + return self.get_integral_point(current_randstate().python_random().randint(0, count-1), **kwds) + @cached_method def combinatorial_automorphism_group(self, vertex_graph_only=False): """ diff --git a/src/sage/geometry/polyhedron/plot.py b/src/sage/geometry/polyhedron/plot.py index 2a51bf6540e..03092097093 100644 --- a/src/sage/geometry/polyhedron/plot.py +++ b/src/sage/geometry/polyhedron/plot.py @@ -1243,18 +1243,28 @@ def render_3d(self, point_opts={}, line_opts={}, polygon_opts={}): Graphics3d Object sage: Polyhedron(vertices=[[1,1,1]]).plot() # point in R^3 Graphics3d Object + + The origin is not included, if it is not in the polyhedron (:trac:`23555`):: + + sage: Q = Polyhedron([[100],[101]]) + sage: P = Q*Q*Q; P + A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 8 vertices + sage: p = P.plot() + sage: p.bounding_box() + ((100.0, 100.0, 100.0), (101.0, 101.0, 101.0)) """ - from sage.plot.plot3d.base import Graphics3d - plt = Graphics3d() + pplt = None + lplt = None + pgplt = None if isinstance(point_opts, dict): point_opts.setdefault('width', 3) - plt += self.render_vertices_3d(**point_opts) + pplt = self.render_vertices_3d(**point_opts) if isinstance(line_opts, dict): line_opts.setdefault('width', 3) - plt += self.render_wireframe_3d(**line_opts) + lplt = self.render_wireframe_3d(**line_opts) if isinstance(polygon_opts, dict): - plt += self.render_solid_3d(**polygon_opts) - return plt + pgplt = self.render_solid_3d(**polygon_opts) + return sum(_ for _ in [pplt, lplt, pgplt] if _ != None) def tikz(self, view=[0, 0, 1], angle=0, scale=2, edge_color='blue!95!black', facet_color='blue!95!black', diff --git a/src/sage/geometry/polyhedron/ppl_lattice_polygon.py b/src/sage/geometry/polyhedron/ppl_lattice_polygon.py index 019bff803e2..e8084b221b3 100644 --- a/src/sage/geometry/polyhedron/ppl_lattice_polygon.py +++ b/src/sage/geometry/polyhedron/ppl_lattice_polygon.py @@ -363,7 +363,7 @@ def is_isomorphic(self, polytope): def sub_polytopes(self): """ - Returns a list of all lattice sub-polygons up to isomorphsm. + Return a list of all lattice sub-polygons up to isomorphism. OUTPUT: diff --git a/src/sage/geometry/pseudolines.py b/src/sage/geometry/pseudolines.py index 27a6dcb2f74..9e69bdba70c 100644 --- a/src/sage/geometry/pseudolines.py +++ b/src/sage/geometry/pseudolines.py @@ -265,7 +265,7 @@ def __init__(self, seq, encoding = "auto"): seq = deepcopy(seq) self._n = len(seq) - ordering = range(self._n) + ordering = list(range(self._n)) self._permutations = [[] for i in range(self._n)] diff --git a/src/sage/geometry/toric_plotter.py b/src/sage/geometry/toric_plotter.py index 867b3b0b419..8c9204a2dba 100644 --- a/src/sage/geometry/toric_plotter.py +++ b/src/sage/geometry/toric_plotter.py @@ -979,9 +979,11 @@ def options(option=None, **kwds): - ``font_size`` -- an integer, the size of font used for labels; - - ``ray_label`` -- a string or a list of strings used for ray labels; + - ``ray_label`` -- a string or a list of strings used for ray labels; use + ``None`` to hide labels; - - ``wall_label`` -- a string or a list of strings used for wall labels; + - ``wall_label`` -- a string or a list of strings used for wall labels; use + ``None`` to hide labels; - ``radius`` -- a positive number, the radius of the cut-off region for "round" mode; diff --git a/src/sage/geometry/triangulation/point_configuration.py b/src/sage/geometry/triangulation/point_configuration.py index 74c4518d079..f84d2d42d93 100644 --- a/src/sage/geometry/triangulation/point_configuration.py +++ b/src/sage/geometry/triangulation/point_configuration.py @@ -661,7 +661,7 @@ def _TOPCOM_exec(cls, executable, input_string, verbose=True): yield line.strip() except GeneratorExit: proc.close(force=True) - raise StopIteration + return if verbose: print("#######################") diff --git a/src/sage/graphs/centrality.pyx b/src/sage/graphs/centrality.pyx index c5c9d355eff..67db9819d8d 100644 --- a/src/sage/graphs/centrality.pyx +++ b/src/sage/graphs/centrality.pyx @@ -233,7 +233,7 @@ cdef dict centrality_betweenness_C(G, numerical_type _, normalize=True): for j in range(layer_current_beginning,layer_current_end): u = queue[j] - # List the neighors of u + # List the neighbors of u p_tmp = g.neighbors[u] while p_tmp n*n: good_input = False - elif m > n*n/2: + elif 2*m > n*n: is_dense = True m = n*n - m else: if m > n*(n-1): good_input = False - elif m > n*(n-1)/2: + elif m > (n * (n - 1)) // 2: is_dense = True m = n*(n-1) - m diff --git a/src/sage/graphs/generators/basic.py b/src/sage/graphs/generators/basic.py index 8777b95c4be..e108f2d57f9 100644 --- a/src/sage/graphs/generators/basic.py +++ b/src/sage/graphs/generators/basic.py @@ -237,24 +237,22 @@ def CycleGraph(n): r""" Returns a cycle graph with n nodes. - A cycle graph is a basic structure which is also typically called - an n-gon. + A cycle graph is a basic structure which is also typically called an + `n`-gon. - PLOTTING: Upon construction, the position dictionary is filled to - override the spring-layout algorithm. By convention, each cycle - graph will be displayed with the first (0) node at the top, with - the rest following in a counterclockwise manner. + PLOTTING: Upon construction, the position dictionary is filled to override + the spring-layout algorithm. By convention, each cycle graph will be + displayed with the first (0) node at the top, with the rest following in a + counterclockwise manner. - The cycle graph is a good opportunity to compare efficiency of - filling a position dictionary vs. using the spring-layout algorithm - for plotting. Because the cycle graph is very symmetric, the - resulting plots should be similar (in cases of small n). + The cycle graph is a good opportunity to compare efficiency of filling a + position dictionary vs. using the spring-layout algorithm for + plotting. Because the cycle graph is very symmetric, the resulting plots + should be similar (in cases of small `n`). - Filling the position dictionary in advance adds O(n) to the - constructor. + Filling the position dictionary in advance adds `O(n)` to the constructor. - EXAMPLES: Compare plotting using the predefined layout and - networkx:: + EXAMPLES: Compare plotting using the predefined layout and networkx:: sage: import networkx sage: n = networkx.cycle_graph(23) @@ -263,9 +261,8 @@ def CycleGraph(n): sage: spring23.show() # long time sage: posdict23.show() # long time - We next view many cycle graphs as a Sage graphics array. First we - use the ``CycleGraph`` constructor, which fills in the - position dictionary:: + We next view many cycle graphs as a Sage graphics array. First we use the + ``CycleGraph`` constructor, which fills in the position dictionary:: sage: g = [] sage: j = [] @@ -295,14 +292,33 @@ def CycleGraph(n): ....: j.append(n) sage: G = sage.plot.graphics.GraphicsArray(j) sage: G.show() # long time + + TESTS: + + The input parameter must be a positive integer:: + + sage: G = graphs.CycleGraph(-1) + Traceback (most recent call last): + ... + ValueError: parameter n must be a positive integer """ - pos_dict = {} - for i in range(n): - x = float(cos((pi/2) + ((2*pi)/n)*i)) - y = float(sin((pi/2) + ((2*pi)/n)*i)) - pos_dict[i] = (x, y) - G = graph.Graph(n, pos=pos_dict, name="Cycle graph") - G.add_cycle(list(range(n))) + if n < 0: + raise ValueError("parameter n must be a positive integer") + + G = Graph(n, name="Cycle graph") + if n == 1: + G.set_pos({0:(0, 0)}) + elif n == 2: + G.add_edge(0, 1) + G.set_pos({0:(0, 1), 1:(0, -1)}) + else: + pos_dict = {} + for i in range(n): + x = float(cos((pi/2) + ((2*pi)/n)*i)) + y = float(sin((pi/2) + ((2*pi)/n)*i)) + pos_dict[i] = (x, y) + G.set_pos(pos_dict) + G.add_cycle(list(range(n))) return G def CompleteGraph(n): diff --git a/src/sage/graphs/generators/chessboard.py b/src/sage/graphs/generators/chessboard.py index c902f369e61..4d4b9e3c6be 100644 --- a/src/sage/graphs/generators/chessboard.py +++ b/src/sage/graphs/generators/chessboard.py @@ -84,7 +84,7 @@ def ChessboardGraphGenerator(dim_list, sage: G.is_isomorphic( graphs.CompleteGraph(4) ) True - A Rook's Graph in 2 dimensions is isomporphic to the Cartesian product of 2 + A Rook's Graph in 2 dimensions is isomorphic to the Cartesian product of 2 complete graphs:: sage: G, _ = graphs.ChessboardGraphGenerator( [3,4], rook=True, rook_radius=None, bishop=False, knight=False ) diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index 8a3f67170ce..0c036ac22ee 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -49,9 +49,7 @@ def SymplecticPolarGraph(d, q, algorithm=None): Computation of the spectrum of `Sp(6,2)`:: - sage: g = graphs.SymplecticGraph(6,2) - doctest:...: DeprecationWarning: SymplecticGraph is deprecated. Please use sage.graphs.generators.classical_geometries.SymplecticPolarGraph instead. - See http://trac.sagemath.org/19136 for details. + sage: g = graphs.SymplecticPolarGraph(6,2) sage: g.is_strongly_regular(parameters=True) (63, 30, 13, 15) sage: set(g.spectrum()) == {-5, 3, 30} @@ -112,8 +110,6 @@ def SymplecticPolarGraph(d, q, algorithm=None): G.relabel() return G -from sage.misc.superseded import deprecated_function_alias -SymplecticGraph = deprecated_function_alias(19136, SymplecticPolarGraph) def AffineOrthogonalPolarGraph(d,q,sign="+"): r""" diff --git a/src/sage/graphs/generators/families.py b/src/sage/graphs/generators/families.py index 103f487933d..131ef8f5028 100644 --- a/src/sage/graphs/generators/families.py +++ b/src/sage/graphs/generators/families.py @@ -2478,24 +2478,22 @@ def WheelGraph(n): """ Returns a Wheel graph with n nodes. - A Wheel graph is a basic structure where one node is connected to - all other nodes and those (outer) nodes are connected cyclically. + A Wheel graph is a basic structure where one node is connected to all other + nodes and those (outer) nodes are connected cyclically. - This constructor depends on NetworkX numeric labels. + PLOTTING: Upon construction, the position dictionary is filled to override + the spring-layout algorithm. By convention, each wheel graph will be + displayed with the first (0) node in the center, the second node at the top, + and the rest following in a counterclockwise manner. - PLOTTING: Upon construction, the position dictionary is filled to - override the spring-layout algorithm. By convention, each wheel - graph will be displayed with the first (0) node in the center, the - second node at the top, and the rest following in a - counterclockwise manner. + With the wheel graph, we see that it doesn't take a very large n at all for + the spring-layout to give a counter-intuitive display. (See Graphics Array + examples below). - With the wheel graph, we see that it doesn't take a very large n at - all for the spring-layout to give a counter-intuitive display. (See - Graphics Array examples below). + EXAMPLES: - EXAMPLES: We view many wheel graphs with a Sage Graphics Array, - first with this constructor (i.e., the position dictionary - filled):: + We view many wheel graphs with a Sage Graphics Array, first with this + constructor (i.e., the position dictionary filled):: sage: g = [] sage: j = [] @@ -2539,15 +2537,16 @@ def WheelGraph(n): sage: spring23.show() # long time sage: posdict23.show() # long time """ - pos_dict = {} - pos_dict[0] = (0,0) - for i in range(1,n): - x = float(cos((pi/2) + ((2*pi)/(n-1))*(i-1))) - y = float(sin((pi/2) + ((2*pi)/(n-1))*(i-1))) - pos_dict[i] = (x,y) - import networkx - G = networkx.wheel_graph(n) - return Graph(G, pos=pos_dict, name="Wheel graph") + from sage.graphs.generators.basic import CycleGraph + if n < 4: + G = CycleGraph(n) + else: + G = CycleGraph(n-1) + G.relabel(perm=list(range(1, n)), inplace=True) + G.add_edges([(0, i) for i in range(1, n)]) + G._pos[0] = (0, 0) + G.name("Wheel graph") + return G def WindmillGraph(k, n): r""" diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index 0428f69ee5b..bf86674d765 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -4902,7 +4902,7 @@ def JankoKharaghaniGraph(v): q = m*t+1 K = GF(q,'alpha') a = K.primitive_element() - Ci= [[K(0)]] + map(set,[[a**(k*j+i) for j in range(t)] for i in range(m)]) + Ci= [[K(0)]] + [set(a**(k*j+i) for j in range(t)) for i in range(m)] Kelem_to_Ci = {v:i for i,s in enumerate(Ci) for v in s} # maps v to [0,...,12] W = ([[0]+ [1]*(len(K))] + diff --git a/src/sage/graphs/generators/world_map.py b/src/sage/graphs/generators/world_map.py index 39e48d50d16..3dc1b6977f5 100644 --- a/src/sage/graphs/generators/world_map.py +++ b/src/sage/graphs/generators/world_map.py @@ -18,6 +18,83 @@ # import from Sage library from sage.graphs.graph import Graph +def AfricaMap(continental=False, year=2018): + """ + Return African states as a graph of common border. + + "African state" here is defined as an independent + state having the capital city in Africa. The graph + has an edge between those countries that have common + *land* border. + + INPUT: + + - ``continental``, a Boolean -- if set, only return states in + the continental Africa + - ``year`` -- reserved for future use + + EXAMPLES:: + + sage: Africa = graphs.AfricaMap(); Africa + Africa Map: Graph on 54 vertices + sage: sorted(Africa.neighbors('Libya')) + ['Algeria', 'Chad', 'Egypt', 'Niger', 'Sudan', 'Tunisia'] + + sage: cont_Africa = graphs.AfricaMap(continental=True) + sage: cont_Africa.order() + 48 + sage: 'Madagaskar' in cont_Africa + False + """ + if year != 2018: + raise ValueError("currently only year 2018 is implemented") + + common_border = { + 'Algeria': ['Libya', 'Mali', 'Mauritania', 'Morocco', 'Niger', 'Tunisia'], + 'Angola': ['Namibia', 'Zambia'], + 'Benin': ['Burkina Faso', 'Niger', 'Nigeria', 'Togo'], + 'Botswana': ['Namibia', 'South Africa', 'Zimbabwe'], + 'Burkina Faso': ['Ghana', 'Ivory Coast', 'Mali', 'Niger', 'Togo'], + 'Cameroon': ['Central Africa', 'Chad', 'Equatorial Guinea', 'Gabon', 'Nigeria'], + 'Central Africa': ['Chad', 'South Sudan', 'Sudan'], + 'Chad': ['Libya', 'Niger', 'Nigeria', 'Sudan'], + 'Republic of the Congo': ['Gabon', 'Cameroon', 'Central Africa', 'Angola', + 'Democratic Republic of the Congo'], + 'Democratic Republic of the Congo': ['Zambia', 'South Sudan', 'Tanzania', 'Burundi', + 'Rwanda', 'Uganda', 'Central Africa', 'Angola'], + 'Djibouti': ['Eritrea', 'Ethiopia', 'Somalia'], + 'Ethiopia': ['Eritrea', 'Kenya', 'Somalia', 'South Sudan', 'Sudan'], + 'Gabon': ['Equatorial Guinea'], + 'Ghana': ['Ivory Coast', 'Togo'], + 'Guinea': ['Guinea-Bissau', 'Ivory Coast', 'Liberia', 'Sierra Leone'], + 'Kenya': ['Somalia', 'South Sudan', 'Tanzania', 'Uganda'], + 'Liberia': ['Ivory Coast', 'Sierra Leone'], + 'Libya': ['Egypt', 'Niger', 'Sudan', 'Tunisia'], + 'Mali': ['Guinea', 'Ivory Coast', 'Mauritania', 'Niger', 'Senegal'], + 'Mozambique': ['Malawi', 'South Africa', 'Swaziland', 'Zimbabwe'], + 'Niger': ['Nigeria'], + 'Rwanda': ['Burundi', 'Tanzania', 'Uganda'], + 'Senegal': ['Guinea', 'Guinea-Bissau', 'Mauritania', 'Gambia'], + 'South Africa': ['Lesotho', 'Namibia', 'Swaziland', 'Zimbabwe'], + 'South Sudan': ['Uganda', 'Sudan', 'Democratic Republic of the Congo'], + 'Sudan': ['Egypt', 'Eritrea'], + 'Tanzania': ['Burundi', 'Malawi', 'Mozambique', 'Uganda', 'Zambia'], + 'Zambia': ['Malawi', 'Mozambique', 'Namibia', 'Zimbabwe'] + } + + no_land_border = ['Cape Verde', 'Seychelles', 'Mauritius', 'S\xc3\xa3o Tom\xc3\xa9 and Pr\xc3\xadncipe', 'Madagascar', 'Comoros'] + + G = Graph(common_border, format='dict_of_lists') + + if continental: + G = G.subgraph(G.connected_component_containing_vertex('Central Africa')) + G.name(new="Continental Africa Map") + else: + G.add_vertices(no_land_border) + G.name(new="Africa Map") + + return G + def EuropeMap(continental=False, year=2018): """ Return European states as a graph of common border. diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 6cfe42a0952..70b38e2c45c 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -312,7 +312,7 @@ :meth:`~GenericGraph.subgraph_search_iterator` | Return an iterator over the labelled copies of ``G`` in ``self``. :meth:`~GenericGraph.characteristic_polynomial` | Return the characteristic polynomial of the adjacency matrix of the (di)graph. :meth:`~GenericGraph.genus` | Return the minimal genus of the graph. - :meth:`~GenericGraph.crossing_number` | Return the minimun number of edge crossings needed to draw the graph. + :meth:`~GenericGraph.crossing_number` | Return the minimum number of edge crossings needed to draw the graph. Methods ------- @@ -1532,7 +1532,7 @@ def to_dictionary(self, edge_labels=False, multiple_edges=False): .. NOTE:: When used on directed graphs, the explanations above can be understood - by replacing the word "neigbours" by "out-neighbors" + by replacing the word "neighbors" by "out-neighbors" EXAMPLES:: @@ -4892,7 +4892,7 @@ def crossing_number(self): """ Return the crossing number of the graph. - The crossing number of a graph is the minimun number of + The crossing number of a graph is the minimum number of edge crossings needed to draw the graph on a plane. It can be seen as a measure of non-planarity; a planar graph has crossing number zero. @@ -9125,7 +9125,7 @@ def disjoint_routed_paths(self,pairs, solver=None, verbose=0): Given a set of pairs `(s_i,t_i)`, a set of disjoint routed paths is a set of - `s_i-t_i` paths which can interset at their endpoints + `s_i-t_i` paths which can intersect at their endpoints and are vertex-disjoint otherwise. INPUT: @@ -14743,7 +14743,16 @@ def center(self, by_weight=False, algorithm=None, weight_function=None, - ``check_weight`` (boolean) - if ``True``, we check that the weight_function outputs a number for each edge. - EXAMPLES:: + EXAMPLES: + + Is Central African Republic in the center of Africa in graph theoretic + sense? Yes:: + + sage: A = graphs.AfricaMap(continental=True) + sage: sorted(A.center()) + ['Cameroon', 'Central Africa'] + + Some other graphs. Center can be the whole graph:: sage: G = graphs.DiamondGraph() sage: G.center() @@ -14754,6 +14763,9 @@ def center(self, by_weight=False, algorithm=None, weight_function=None, sage: S = graphs.StarGraph(19) sage: S.center() [0] + + TESTS:: + sage: G = Graph() sage: G.center() [] diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index cce996ed280..f31e6e2b743 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -31,6 +31,7 @@ include "sage/data_structures/binary_matrix.pxi" from libc.math cimport sqrt, fabs from libc.string cimport memset +from sage.cpython.string cimport char_to_str from sage.libs.gmp.mpz cimport * from sage.misc.prandom import random from sage.ext.memory_allocator cimport MemoryAllocator @@ -398,10 +399,11 @@ def int_to_binary_string(n): '11111010111' """ cdef mpz_t i + cdef char* s mpz_init(i) - mpz_set_ui(i,n) - cdef char* s=mpz_get_str(NULL, 2, i) - t=str(s) + mpz_set_ui(i, n) + s = mpz_get_str(NULL, 2, i) + t = char_to_str(s) sig_free(s) mpz_clear(i) return t diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index ebcfa88dd7d..852e8a06dc9 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -3379,7 +3379,7 @@ def is_semi_symmetric(self): """ Returns true if self is semi-symmetric. - A graph is semi-symmetric if it is regular, edge-transitve but not + A graph is semi-symmetric if it is regular, edge-transitive but not vertex-transitive. See :wikipedia:`the wikipedia article on semi-symmetric graphs diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 751d19d1833..b3ec5ac8ef1 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -15,7 +15,7 @@ More interestingly, one can get the list of all graphs that Sage knows how to build by typing ``graphs.`` in Sage and then hitting tab. """ -from __future__ import print_function, absolute_import +from __future__ import print_function, absolute_import, division from six.moves import range # This method appends a list of methods to the doc as a 3xN table. @@ -336,7 +336,8 @@ def __append_to_doc(methods): __append_to_doc( ["WorldMap", - "EuropeMap"] + "EuropeMap", + "AfricaMap"] ) __doc__ += """ @@ -858,7 +859,7 @@ def nauty_geng(self, options="", debug=False): sage: next(gen) Traceback (most recent call last): ... - StopIteration: Exhausted list of graphs from nauty geng + StopIteration A list of all graphs on 7 vertices. This agrees with :oeis:`A000088`. :: @@ -895,7 +896,8 @@ def nauty_geng(self, options="", debug=False): try: s = next(gen) except StopIteration: - raise StopIteration("Exhausted list of graphs from nauty geng") + # Exhausted list of graphs from nauty geng + return G = graph.Graph(s[:-1], format='graph6') yield G @@ -2045,7 +2047,6 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None NonisotropicUnitaryPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.NonisotropicUnitaryPolarGraph) OrthogonalPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.OrthogonalPolarGraph) SymplecticDualPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.SymplecticDualPolarGraph) - SymplecticGraph = staticmethod(sage.graphs.generators.classical_geometries.SymplecticGraph) SymplecticPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.SymplecticPolarGraph) TaylorTwographDescendantSRG = \ staticmethod(sage.graphs.generators.classical_geometries.TaylorTwographDescendantSRG) @@ -2106,6 +2107,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None import sage.graphs.generators.world_map WorldMap = staticmethod(sage.graphs.generators.world_map.WorldMap) EuropeMap = staticmethod(sage.graphs.generators.world_map.EuropeMap) + AfricaMap = staticmethod(sage.graphs.generators.world_map.AfricaMap) ########################################################################### # Degree Sequence @@ -2231,7 +2233,7 @@ def canaug_traverse_vert(g, aut_gens, max_verts, property, dig=False, loops=Fals edges = [] if dig: index = 0 - while index < possibilities/2: + while 2 * index < possibilities: if (1 << index)&i: edges.append((index,n)) index += 1 diff --git a/src/sage/graphs/hypergraph_generators.py b/src/sage/graphs/hypergraph_generators.py index d131b56a1c8..658c5c0bb90 100644 --- a/src/sage/graphs/hypergraph_generators.py +++ b/src/sage/graphs/hypergraph_generators.py @@ -150,7 +150,8 @@ def nauty(self, number_of_sets, number_of_vertices, try: s = next(gen) except StopIteration: - raise StopIteration("Exhausted list of graphs from nauty geng") + # Exhausted list of graphs from nauty geng + return from sage.graphs.graph import Graph G = Graph(s[:-1], format='graph6') diff --git a/src/sage/graphs/linearextensions.py b/src/sage/graphs/linearextensions.py index f5368aeb1bb..113a2cd731d 100644 --- a/src/sage/graphs/linearextensions.py +++ b/src/sage/graphs/linearextensions.py @@ -93,16 +93,16 @@ def __init__(self, dag): #Pruesse and Ruskey while dag_copy.num_verts() != 0: #Find all the minimal elements of dag_copy - minimial_elements = [] + minimal_elements = [] for node in dag_copy.vertices(): if len(dag_copy.incoming_edges(node)) == 0: - minimial_elements.append(node) - if len(minimial_elements) == 1: - le.append(minimial_elements[0]) - dag_copy.delete_vertex(minimial_elements[0]) + minimal_elements.append(node) + if len(minimal_elements) == 1: + le.append(minimal_elements[0]) + dag_copy.delete_vertex(minimal_elements[0]) else: - ap = minimial_elements[0] - bp = minimial_elements[1] + ap = minimal_elements[0] + bp = minimal_elements[1] a.append(ap) b.append(bp) le.append(ap) diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index 7da68c8977a..f7dea6add46 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -1649,9 +1649,8 @@ def is_switch_OA_srg(int v, int k, int l, int mu): not orthogonal_array(c+1,n,existence=True,resolvable=True)): return None - def switch_OA_srg(c,n): - from builtins import zip - OA = map(tuple,orthogonal_array(c+1,n,resolvable=True)) + def switch_OA_srg(c, n): + OA = map(tuple, orthogonal_array(c+1, n, resolvable=True)) g = Graph([OA, lambda x,y: any(xx==yy for xx,yy in zip(x,y))], loops=False) g.add_vertex(0) @@ -1660,6 +1659,7 @@ def is_switch_OA_srg(int v, int k, int l, int mu): return (switch_OA_srg,c,n) + def is_nowhere0_twoweight(int v, int k, int l, int mu): r""" Test whether some graph of nowhere 0 words is `(v,k,\lambda,\mu)`-strongly regular. @@ -2343,7 +2343,7 @@ def SRG_280_117_44_52(): """ from sage.graphs.hypergraph_generators import hypergraphs - # V is the set of partions {{a,b,c},{d,e,f},{g,h,i}} of {0,...,8} + # V is the set of partitions {{a,b,c},{d,e,f},{g,h,i}} of {0,...,8} H = hypergraphs.CompleteUniform(9,3) g = H.intersection_graph() V = g.complement().cliques_maximal() diff --git a/src/sage/groups/abelian_gps/abelian_group_morphism.py b/src/sage/groups/abelian_gps/abelian_group_morphism.py index c7d4688bc05..725adaf9c75 100644 --- a/src/sage/groups/abelian_gps/abelian_group_morphism.py +++ b/src/sage/groups/abelian_gps/abelian_group_morphism.py @@ -1,11 +1,11 @@ """ Homomorphisms of abelian groups -TODO: +.. TODO:: -- there must be a homspace first + - there must be a homspace first -- there should be hom and Hom methods in abelian group + - there should be hom and Hom methods in abelian group AUTHORS: @@ -28,8 +28,10 @@ from sage.misc.all import prod + def is_AbelianGroupMorphism(f): - return isinstance(f, AbelianGroupMorphism); + return isinstance(f, AbelianGroupMorphism) + class AbelianGroupMap(Morphism): """ @@ -44,6 +46,7 @@ def __init__(self, parent): def _repr_type(self): return "AbelianGroup" + class AbelianGroupMorphism_id(AbelianGroupMap): """ Return the identity homomorphism from X to itself. @@ -54,7 +57,8 @@ def __init__(self, X): AbelianGroupMorphism.__init__(self, X.Hom(X)) def _repr_defn(self): - return "Identity map of "+str(X) + return "Identity map of " + str(X) + class AbelianGroupMorphism(Morphism): """ @@ -72,11 +76,17 @@ class AbelianGroupMorphism(Morphism): Multiplicative Abelian group isomorphic to C2 x C3 sage: x,y = H.gens() - :: - sage: from sage.groups.abelian_gps.abelian_group_morphism import AbelianGroupMorphism sage: phi = AbelianGroupMorphism(H,G,[x,y],[a,b]) + TESTS:: + + sage: G. = AbelianGroup(2,[2,3]) + sage: H. = AbelianGroup(3,[2,3,4]) + sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,b]) + sage: Hom(G,H) == phi.parent() + True + AUTHORS: - David Joyner (2006-02) @@ -100,11 +110,11 @@ class AbelianGroupMorphism(Morphism): # raise TypeError, "Sorry, the list %s must generate G."%genss #TypeError: Sorry, the list [a*b, c^2] must generate G. - def __init__(self, G, H, genss, imgss ): + def __init__(self, G, H, genss, imgss): from sage.categories.homset import Hom Morphism.__init__(self, Hom(G, H)) if len(genss) != len(imgss): - raise TypeError("Sorry, the lengths of %s, %s must be equal."%(genss,imgss)) + raise TypeError("Sorry, the lengths of %s, %s must be equal." % (genss, imgss)) self._domain = G self._codomain = H if not(G.is_abelian()): @@ -113,14 +123,14 @@ def __init__(self, G, H, genss, imgss ): raise TypeError("Sorry, the groups must be abelian groups.") G_domain = G.subgroup(genss) if G_domain.order() != G.order(): - raise TypeError("Sorry, the list %s must generate G."%genss) + raise TypeError("Sorry, the list %s must generate G." % genss) # self.domain_invs = G.gens_orders() # self.codomaininvs = H.gens_orders() self.domaingens = genss self.codomaingens = imgss for i in range(len(self.domaingens)): if (self.domaingens[i]).order() != (self.codomaingens[i]).order(): - raise TypeError("Sorry, the orders of the corresponding elements in %s, %s must be equal."%(genss,imgss)) + raise TypeError("Sorry, the orders of the corresponding elements in %s, %s must be equal." % (genss, imgss)) def _gap_init_(self): """ @@ -140,22 +150,22 @@ def _gap_init_(self): """ G = (self.domain())._gap_init_() H = (self.codomain())._gap_init_() - s3 = 'G:=%s; H:=%s'%(G,H) + s3 = 'G:=%s; H:=%s' % (G, H) gap.eval(s3) - gensG = self.domain().variable_names() ## the Sage group generators + gensG = self.domain().variable_names() # the Sage group generators gensH = self.codomain().variable_names() - s1 = "gensG := GeneratorsOfGroup(G)" ## the GAP group generators + s1 = "gensG := GeneratorsOfGroup(G)" # the GAP group generators gap.eval(s1) s2 = "gensH := GeneratorsOfGroup(H)" gap.eval(s2) - for i in range(len(gensG)): ## making the Sage group gens - cmd = ("%s := gensG["+str(i+1)+"]")%gensG[i] ## correspond to the Sage group gens + for i in range(len(gensG)): # making the Sage group gens + cmd = ("%s := gensG["+str(i+1)+"]") % gensG[i] # correspond to the Sage group gens gap.eval(cmd) for i in range(len(gensH)): - cmd = ("%s := gensH["+str(i+1)+"]")%gensH[i] + cmd = ("%s := gensH[" + str(i + 1) + "]") % gensH[i] gap.eval(cmd) - args = str(self.domaingens)+","+ str(self.codomaingens) - gap.eval("phi := GroupHomomorphismByImages(G,H,"+args+")") + args = str(self.domaingens) + "," + str(self.codomaingens) + gap.eval("phi := GroupHomomorphismByImages(G,H," + args + ")") self.gap_hom_string = "phi := GroupHomomorphismByImages(G,H,"+args+")" return self.gap_hom_string @@ -166,8 +176,9 @@ def kernel(self): """ Only works for finite groups. - TODO: not done yet; returns a gap object but should return a Sage - group. + .. TODO:: + + not done yet; returns a gap object but should return a Sage group. EXAMPLES:: @@ -180,35 +191,43 @@ def kernel(self): sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,b]) sage: phi.kernel() 'Group([ ])' + + sage: H = AbelianGroup(3,[2,2,2],names="abc") + sage: a,b,c = H.gens() + sage: G = AbelianGroup(2,[2,2],names="x") + sage: x,y = G.gens() + sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,a]) + sage: phi.kernel() + 'Group([ f1*f2 ])' """ cmd = self._gap_init_() gap.eval(cmd) return gap.eval("Kernel(phi)") - def image(self, J): + def image(self, S): """ - Only works for finite groups. + Return the image of the subgroup ``S`` by the morphism. + + This only works for finite groups. - J must be a subgroup of G. Computes the subgroup of H which is the - image of J. + INPUT: + + - ``S`` -- a subgroup of the domain group ``G`` EXAMPLES:: sage: G = AbelianGroup(2,[2,3],names="xy") sage: x,y = G.gens() + sage: subG = G.subgroup([x]) sage: H = AbelianGroup(3,[2,3,4],names="abc") sage: a,b,c = H.gens() sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,b]) + sage: phi.image(subG) + Multiplicative Abelian subgroup isomorphic to C2 generated by {a} """ - G = self.domain() - gensJ = J.gens() - for g in gensJ: - print(g) - print(self(g), "\n") - L = [self(g) for g in gensJ] - return G.subgroup(L) - - def _call_( self, g ): + return self.codomain().subgroup([self(g) for g in S.gens()]) + + def _call_(self, g): """ Some python code for wrapping GAP's Images function but only for permutation groups. Returns an error if g is not in G. @@ -225,10 +244,7 @@ def _call_( self, g ): sage: phi(y^2) b^2 """ - G = g.parent() - w = g.word_problem(self.domaingens) - n = len(w) # g.word_problem is faster in general than word_problem(g) gens = self.codomaingens - h = prod([gens[(self.domaingens).index(w[i][0])]**(w[i][1]) for i in range(n)]) - return h + return prod(gens[(self.domaingens).index(wi[0])]**wi[1] + for wi in g.word_problem(self.domaingens)) diff --git a/src/sage/groups/finitely_presented.py b/src/sage/groups/finitely_presented.py index 1c76552994f..59fe82e7523 100644 --- a/src/sage/groups/finitely_presented.py +++ b/src/sage/groups/finitely_presented.py @@ -429,7 +429,7 @@ class RewritingSystem(object): A rewriting system is a set of rules that allow to transform one word in the group to an equivalent one. - If the rewriting system is confluent, then the transformated + If the rewriting system is confluent, then the transformed word is a unique reduced form of the element of the group. .. WARNING:: diff --git a/src/sage/groups/matrix_gps/finitely_generated.py b/src/sage/groups/matrix_gps/finitely_generated.py index ae24e75385d..4c4837f03f0 100644 --- a/src/sage/groups/matrix_gps/finitely_generated.py +++ b/src/sage/groups/matrix_gps/finitely_generated.py @@ -1110,7 +1110,7 @@ def reynolds_operator(self, poly, chi=None): sage: G.reynolds_operator(f, chi) Traceback (most recent call last): ... - NotImplementedError: nontrivial characters not implemented for charateristic > 0 + NotImplementedError: nontrivial characters not implemented for characteristic > 0 sage: G.reynolds_operator(f) x^6 @@ -1194,7 +1194,7 @@ def reynolds_operator(self, poly, chi=None): L1 = fields[0].composite_fields(fields[1])[0] L = L1.composite_fields(fields[2])[0] else: - raise NotImplementedError("nontrivial characters not implemented for charateristic > 0") + raise NotImplementedError("nontrivial characters not implemented for characteristic > 0") poly = poly.change_ring(L) poly_gens = vector(poly.parent().gens()) F = L.zero() diff --git a/src/sage/groups/perm_gps/partn_ref/data_structures.pyx b/src/sage/groups/perm_gps/partn_ref/data_structures.pyx index 0db8f9efe4f..9a1c29923c7 100644 --- a/src/sage/groups/perm_gps/partn_ref/data_structures.pyx +++ b/src/sage/groups/perm_gps/partn_ref/data_structures.pyx @@ -1287,10 +1287,10 @@ def SC_test_list_perms(list L, int n, int limit, bint gap, bint limit_complain, ....: m = n//3 ....: perm1 = list(range(2*m)) ....: shuffle(perm1) - ....: perm1 += range(2*m,n) - ....: perm2 = range(m,n) + ....: perm1 += list(range(2*m,n)) + ....: perm2 = list(range(m,n)) ....: shuffle(perm2) - ....: perm2 = range(m) + perm2 + ....: perm2 = list(range(m)) + perm2 ....: SC_test_list_perms([perm1, perm2], n, limit, gap, 0, contains) sage: for n in [4..9]: # long time ....: for _ in range(2): # long time diff --git a/src/sage/groups/perm_gps/symgp_conjugacy_class.py b/src/sage/groups/perm_gps/symgp_conjugacy_class.py index 7bd1a42e0f4..3b55bcf178b 100644 --- a/src/sage/groups/perm_gps/symgp_conjugacy_class.py +++ b/src/sage/groups/perm_gps/symgp_conjugacy_class.py @@ -292,6 +292,7 @@ def default_representative(part, G): # once SymmetricGroup is a proper parent. return PermutationGroupElement(cycles, G, check=False) + def conjugacy_class_iterator(part, S=None): r""" Return an iterator over the conjugacy class associated to @@ -319,12 +320,10 @@ def conjugacy_class_iterator(part, S=None): [(1, 3), (2, 4)] [(1, 4), (2, 3)] - In order to get permutations, one can use ``map`` - from the Python module ``builtins`` (works with Python 2 and Python 3):: + In order to get permutations, one just has to wrap:: - sage: from builtins import map sage: S = SymmetricGroup(5) - sage: for p in map(S, conjugacy_class_iterator([3,2])): print(p) + sage: for p in conjugacy_class_iterator([3,2]): print(S(p)) (1,2)(3,4,5) (1,2)(3,5,4) (1,3)(2,4,5) diff --git a/src/sage/homology/simplicial_complex.py b/src/sage/homology/simplicial_complex.py index 007402a0d89..6462d2fe9d6 100644 --- a/src/sage/homology/simplicial_complex.py +++ b/src/sage/homology/simplicial_complex.py @@ -839,7 +839,7 @@ class SimplicialComplex(Parent, GenericCellComplex): (or tuples, etc.) of vertices. Maximal faces are also known as 'facets'. - Alternatively, the maximal faces can be defined from a monotome boolean + Alternatively, the maximal faces can be defined from a monotone boolean function on the subsets of a set `X`. While defining ``maximal_faces=None``, you can thus set ``from_characteristic_function=(f,X)`` where ``X`` is the set of points and ``f`` a boolean monotone hereditary function that accepts diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index 2202f45964b..434ac77f884 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -1129,7 +1129,8 @@ def __init__(self, max_workspace_size=None, def set_seed(self,seed=None): """ - Sets the seed for gap interpeter. + Set the seed for gap interpreter. + The seed should be an integer. EXAMPLES:: diff --git a/src/sage/interfaces/giac.py b/src/sage/interfaces/giac.py index 0742cf896e6..0649a26d991 100644 --- a/src/sage/interfaces/giac.py +++ b/src/sage/interfaces/giac.py @@ -714,14 +714,14 @@ def _true_symbol(self): EXAMPLES:: sage: giac._true_symbol() - '1' + 'true' :: sage: giac(2) == giac(2) True """ - return '1' + return 'true' def _assign_symbol(self): """ diff --git a/src/sage/interfaces/gp.py b/src/sage/interfaces/gp.py index ad5bf3d9c73..53381d96308 100644 --- a/src/sage/interfaces/gp.py +++ b/src/sage/interfaces/gp.py @@ -221,7 +221,8 @@ def __init__(self, stacksize=10000000, # 10MB def set_seed(self, seed=None): """ - Sets the seed for gp interpeter. + Set the seed for gp interpreter. + The seed should be an integer. EXAMPLES:: diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index 78f87d27998..7d8f9131e83 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -366,7 +366,8 @@ def __init__(self, script_subdirectory=None, def set_seed(self, seed=None): """ - Sets the seed for the Magma interpeter. + Set the seed for the Magma interpreter. + The seed should be an integer. EXAMPLES:: diff --git a/src/sage/interfaces/r.py b/src/sage/interfaces/r.py index 8c6d589babc..8b54d985aaa 100644 --- a/src/sage/interfaces/r.py +++ b/src/sage/interfaces/r.py @@ -362,7 +362,8 @@ def __init__(self, def set_seed(self, seed=None): """ - Sets the seed for R interpeter. + Set the seed for R interpreter. + The seed should be an integer. EXAMPLES:: @@ -1889,7 +1890,7 @@ def _sage_(self): # c is an ordered list # list is a dictionary (where _Names give the entries names. # map entries in names to (value, name) in each entry? - # structure is .. see above .. strucuture(DATA,**kw) + # structure is .. see above .. structure(DATA,**kw) # TODO: thinking of just replacing c( with ( to get a long tuple? diff --git a/src/sage/interfaces/scilab.py b/src/sage/interfaces/scilab.py index 8d9e9605cf4..466347bd9a5 100644 --- a/src/sage/interfaces/scilab.py +++ b/src/sage/interfaces/scilab.py @@ -236,7 +236,8 @@ def __init__(self, maxread=None, script_subdirectory=None, def set_seed(self, seed=None): """ - Sets the seed for gp interpeter. + Set the seed for gp interpreter. + The seed should be an integer. EXAMPLES:: diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index 8a02c1b9db0..18f8384c508 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -400,7 +400,8 @@ def __init__(self, maxread=None, script_subdirectory=None, def set_seed(self,seed=None): """ - Sets the seed for singular interpeter. + Set the seed for singular interpreter. + The seed should be an integer at least 1 and not more than 30 bits. See diff --git a/src/sage/interfaces/tests.py b/src/sage/interfaces/tests.py index af9797abcdf..6bcbfef37b2 100644 --- a/src/sage/interfaces/tests.py +++ b/src/sage/interfaces/tests.py @@ -37,10 +37,11 @@ 0 sage: subprocess.call("echo syntax error | gp", **kwds) 0 - sage: subprocess.call("echo syntax error | ipython", **kwds) in (0,1) + sage: subprocess.call("echo syntax error | ipython", **kwds) in (0, 1, 120) True sage: subprocess.call("echo syntax error | Singular", **kwds) 0 + sage: f.close() """ from __future__ import print_function from __future__ import absolute_import diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index f60eff48e48..5fa23cd3f15 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -559,6 +559,18 @@ def __eq__(self, other): return True return self.braid() == other.braid() + def __hash__(self): + """ + Return the hash of ``self``. + + EXAMPLES:: + + sage: B = BraidGroup(8) + sage: L1 = Link(B([-1, -1, -1, -2, 1, -2, 3, -2, 5, 4])) + sage: H = hash(L1) + """ + return hash(self.braid()) + def __ne__(self, other): """ Check inequality. diff --git a/src/sage/lfunctions/all.py b/src/sage/lfunctions/all.py index d7d4d065841..9281f97ebbe 100644 --- a/src/sage/lfunctions/all.py +++ b/src/sage/lfunctions/all.py @@ -1,9 +1,6 @@ -from __future__ import absolute_import +from sage.misc.lazy_import import lazy_import as _lazy_import_ -from .dokchitser import Dokchitser - -from .lcalc import lcalc - -from .sympow import sympow - -from .zero_sums import LFunctionZeroSum +_lazy_import_("sage.lfunctions.dokchitser", "Dokchitser") +_lazy_import_("sage.lfunctions.sympow", "sympow") +_lazy_import_("sage.lfunctions.zero_sums", "LFunctionZeroSum") +_lazy_import_("sage.lfunctions.lcalc", "lcalc") diff --git a/src/sage/libs/flint/fmpz_poly.pyx b/src/sage/libs/flint/fmpz_poly.pyx index fee3b2cf5b7..3d83acd4505 100644 --- a/src/sage/libs/flint/fmpz_poly.pyx +++ b/src/sage/libs/flint/fmpz_poly.pyx @@ -23,6 +23,7 @@ from cpython.sequence cimport * from cysignals.memory cimport sig_free from sage.arith.long cimport pyobject_to_long +from sage.cpython.string cimport char_to_str, str_to_bytes from sage.structure.sage_object cimport SageObject from sage.rings.integer cimport Integer from sage.libs.flint.fmpz_poly cimport * @@ -51,7 +52,7 @@ cdef class Fmpz_poly(SageObject): cdef long c cdef Integer w if isinstance(v, str): - if not fmpz_poly_set_str(self.poly, v): + if not fmpz_poly_set_str(self.poly, str_to_bytes(v)): return else: raise ValueError("Unable to create Fmpz_poly from that string.") @@ -117,7 +118,7 @@ cdef class Fmpz_poly(SageObject): 8 0 0 0 0 0 0 0 1 """ cdef char* ss = fmpz_poly_get_str(self.poly) - cdef object s = ss + cdef object s = char_to_str(ss) sig_free(ss) return s diff --git a/src/sage/libs/gap/element.pyx b/src/sage/libs/gap/element.pyx index c7ca311ad52..e6f1e483925 100644 --- a/src/sage/libs/gap/element.pyx +++ b/src/sage/libs/gap/element.pyx @@ -2617,7 +2617,7 @@ cdef class GapElement_List(GapElement): """ Return the list as a vector. - GAP does not have a special vetor data type, they are just + GAP does not have a special vector data type, they are just lists. This function converts a GAP list to a Sage vector. OUTPUT: diff --git a/src/sage/libs/gap/util.pyx b/src/sage/libs/gap/util.pyx index abc04d331e4..7bffcfc00d7 100644 --- a/src/sage/libs/gap/util.pyx +++ b/src/sage/libs/gap/util.pyx @@ -391,7 +391,7 @@ cdef inline void DEBUG_CHECK(libGAP_Obj obj): cpdef memory_usage(): """ - Return information about the memory useage. + Return information about the memory usage. See :meth:`~sage.libs.gap.libgap.Gap.mem` for details. """ diff --git a/src/sage/libs/homfly.pyx b/src/sage/libs/homfly.pyx index a10e2c59760..172783f60c4 100644 --- a/src/sage/libs/homfly.pyx +++ b/src/sage/libs/homfly.pyx @@ -32,8 +32,10 @@ AUTHORS: from cysignals.signals cimport sig_on, sig_off +from sage.cpython.string cimport str_to_bytes, char_to_str + cdef extern from "homfly.h": - ctypedef int word; + ctypedef int word; ctypedef signed long int sb4; ctypedef unsigned short int ub2; ctypedef signed short int sb2; @@ -67,12 +69,12 @@ def homfly_polynomial_string(link): sage: homfly_polynomial_string(trefoil) # optional - libhomfly ' - L^-4 - 2L^-2 + M^2L^-2' """ - cdef char* c_string = link + link = str_to_bytes(link) sig_on() - cdef char* c_output = homfly_str(c_string) + cdef char* c_output = homfly_str(link) sig_off() - output = c_output - return output + return char_to_str(c_output) + def homfly_polynomial_dict(link): """ @@ -93,10 +95,10 @@ def homfly_polynomial_dict(link): sage: homfly_polynomial_dict(trefoil) # optional - libhomfly {(-4, 0): -1, (-2, 0): -2, (-2, 2): 1} """ - cdef char* c_string = link + link = str_to_bytes(link) cdef Term ter sig_on() - cdef Poly* c_output = homfly(c_string) + cdef Poly* c_output = homfly(link) sig_off() cdef int l = c_output.len d = dict() diff --git a/src/sage/libs/mpc.pxd b/src/sage/libs/mpc.pxd deleted file mode 100644 index 24fb0f82816..00000000000 --- a/src/sage/libs/mpc.pxd +++ /dev/null @@ -1,133 +0,0 @@ -from sage.libs.gmp.types cimport * -from sage.libs.mpfr.types cimport * - -cdef extern from "mpc.h": - ctypedef struct __mpc_struct: - mpfr_t re - mpfr_t im - ctypedef __mpc_struct mpc_t[1] - ctypedef __mpc_struct* mpc_ptr - ctypedef __mpc_struct* mpc_srcptr - - ctypedef enum mpc_rnd_t: - MPC_RNDNN = 0 - MPC_RNDZN = 1 - MPC_RNDUN = 2 - MPC_RNDDN = 3 - MPC_RNDNZ = 16 - MPC_RNDZZ = 17 - MPC_RNDUZ = 18 - MPC_RNDDZ = 19 - MPC_RNDNU = 32 - MPC_RNDZU = 33 - MPC_RNDUU = 34 - MPC_RNDDU = 35 - MPC_RNDND = 48 - MPC_RNDZD = 49 - MPC_RNDUD = 50 - MPC_RNDDD = 51 - - # Memory management - void mpc_init (mpc_t) - void mpc_init2 (mpc_t, mpfr_prec_t) - void mpc_clear (mpc_t) - - # Precision accessors - mpfr_prec_t mpc_get_prec (mpc_t) - void mpc_set_prec (mpc_t, mpfr_prec_t) - - # Set real part to given value and imaginary part to +0 - int mpc_set_ui (mpc_t, unsigned long int, mpc_rnd_t) - int mpc_set_si (mpc_t, long int, mpc_rnd_t) - int mpc_set_z (mpc_t, mpz_t, mpc_rnd_t) - int mpc_set_d (mpc_t, double, mpc_rnd_t) - int mpc_set_fr (mpc_t, mpfr_t, mpc_rnd_t) - # Set value - int mpc_set (mpc_t, mpc_t, mpc_rnd_t) - int mpc_set_ui_ui (mpc_t, unsigned long int, unsigned long int, mpc_rnd_t) - int mpc_set_si_si (mpc_t, long int, long int, mpc_rnd_t) - int mpc_set_d_d (mpc_t, double, double, mpc_rnd_t) - int mpc_set_fr_fr (mpc_t, mpfr_t, mpfr_t, mpc_rnd_t) - void mpc_set_nan(mpc_t) - void mpc_swap(mpc_t, mpc_t) - - # Comparisons - int mpc_cmp (mpc_t, mpc_t) - int mpc_cmp_si_si (mpc_t, long int, long int) - int mpc_cmp_si (mpc_t, long int) - - # Projection - int mpc_real (mpfr_t rop, mpc_t op, mpfr_rnd_t rnd) - int mpc_imag (mpfr_t rop, mpc_t op, mpfr_rnd_t rnd) - mpfr_t mpc_realref (mpc_t op) - mpfr_t mpc_imagref (mpc_t op) - int mpc_arg (mpfr_t rop, mpc_t op, mpfr_rnd_t rnd) - int mpc_proj (mpc_t rop, mpc_t op, mpc_rnd_t rnd) - - # Arithmetic - int mpc_neg (mpc_t, mpc_t, mpc_rnd_t) - int mpc_conj (mpc_t, mpc_t, mpc_rnd_t) - - int mpc_add (mpc_t, mpc_t, mpc_t, mpc_rnd_t) - int mpc_sub (mpc_t, mpc_t, mpc_t, mpc_rnd_t) - int mpc_mul (mpc_t, mpc_t, mpc_t, mpc_rnd_t) - int mpc_div (mpc_t, mpc_t, mpc_t, mpc_rnd_t) - int mpc_sqr (mpc_t, mpc_t, mpc_rnd_t) - - int mpc_add_fr (mpc_t, mpc_t, mpfr_t, mpc_rnd_t) - int mpc_add_ui (mpc_t, mpc_t, unsigned long int, mpc_rnd_t) - int mpc_add_si (mpc_t, mpc_t, unsigned long, mpc_rnd_t) - int mpc_sub_fr (mpc_t, mpc_t, mpfr_t, mpc_rnd_t) - int mpc_fr_sub (mpc_t, mpfr_t, mpc_t, mpc_rnd_t) - int mpc_sub_ui (mpc_t, mpc_t, unsigned long int, mpc_rnd_t) - int mpc_ui_sub (mpc_t, unsigned long int, mpc_t, mpc_rnd_t) - int mpc_ui_ui_sub (mpc_t, unsigned long int, unsigned long int, mpc_t, mpc_rnd_t) - int mpc_mul_fr (mpc_t, mpc_t, mpfr_t, mpc_rnd_t) - int mpc_mul_ui (mpc_t, mpc_t, unsigned long int, mpc_rnd_t) - int mpc_mul_si (mpc_t, mpc_t, long int, mpc_rnd_t) - int mpc_mul_i (mpc_t, mpc_t, int, mpc_rnd_t) - int mpc_ui_div (mpc_t, unsigned long int, mpc_t, mpc_rnd_t) - int mpc_div_fr (mpc_t, mpc_t, mpfr_t, mpc_rnd_t) - int mpc_fr_div (mpc_t, mpfr_t, mpc_t, mpc_rnd_t) - int mpc_div_ui (mpc_t, mpc_t, unsigned long int, mpc_rnd_t) - int mpc_ui_div (mpc_t, unsigned long int, mpc_t, mpc_rnd_t) - int mpc_div_2ui (mpc_t, mpc_t, unsigned long int, mpc_rnd_t) - int mpc_div_2si (mpc_t, mpc_t, long int, mpc_rnd_t) - int mpc_mul_2ui (mpc_t, mpc_t, unsigned long int, mpc_rnd_t) - int mpc_mul_2si (mpc_t, mpc_t, long int, mpc_rnd_t) - # - int mpc_abs (mpfr_t, mpc_t, mpfr_rnd_t) - int mpc_norm (mpfr_t, mpc_t, mpfr_rnd_t) - - - # Power functions and logarithm - int mpc_sqrt (mpc_t, mpc_t, mpc_rnd_t) - int mpc_exp (mpc_t, mpc_t, mpc_rnd_t) - int mpc_log (mpc_t, mpc_t, mpc_rnd_t) - int mpc_pow (mpc_t rop, mpc_t op1, mpc_t op2, mpc_rnd_t rnd) - int mpc_pow_si (mpc_t rop, mpc_t op1, long op2, mpc_rnd_t rnd) - int mpc_pow_ui (mpc_t rop, mpc_t op1, unsigned long op2, mpc_rnd_t rnd) - int mpc_pow_z (mpc_t rop, mpc_t op1, mpz_t, mpc_rnd_t rnd) - int mpc_pow_d (mpc_t rop, mpc_t op1, double, mpc_rnd_t rnd) - int mpc_pow_fr (mpc_t rop, mpc_t op1, mpfr_t op2, mpc_rnd_t rnd) - - # Trigonometric functions - void mpc_sin (mpc_t, mpc_t, mpc_rnd_t) - void mpc_cos (mpc_t, mpc_t, mpc_rnd_t) - void mpc_tan (mpc_t, mpc_t, mpc_rnd_t) - void mpc_sinh (mpc_t, mpc_t, mpc_rnd_t) - void mpc_cosh (mpc_t, mpc_t, mpc_rnd_t) - void mpc_tanh (mpc_t, mpc_t, mpc_rnd_t) - void mpc_asin (mpc_t, mpc_t, mpc_rnd_t) - void mpc_acos (mpc_t, mpc_t, mpc_rnd_t) - void mpc_atan (mpc_t, mpc_t, mpc_rnd_t) - void mpc_asinh (mpc_t, mpc_t, mpc_rnd_t) - void mpc_acosh (mpc_t, mpc_t, mpc_rnd_t) - void mpc_atanh (mpc_t, mpc_t, mpc_rnd_t) - - # Random Function - int mpc_urandom (mpc_t, gmp_randstate_t) - - # utility - int MPC_INEX_RE(int) - int MPC_INEX_IM(int) diff --git a/src/sage/libs/mpc/__init__.pxd b/src/sage/libs/mpc/__init__.pxd new file mode 100644 index 00000000000..4930a636c41 --- /dev/null +++ b/src/sage/libs/mpc/__init__.pxd @@ -0,0 +1,113 @@ +# distutils: libraries = gmp mpfr mpc + +from sage.libs.gmp.types cimport * +from sage.libs.mpfr.types cimport * +from sage.libs.mpc.types cimport * + +cdef extern from "mpc.h": + int mpc_add(mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t) + int mpc_add_fr(mpc_ptr, mpc_srcptr, mpfr_srcptr, mpc_rnd_t) + int mpc_add_si(mpc_ptr, mpc_srcptr, long int, mpc_rnd_t) + int mpc_add_ui(mpc_ptr, mpc_srcptr, unsigned long int, mpc_rnd_t) + int mpc_sub(mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t) + int mpc_sub_fr(mpc_ptr, mpc_srcptr, mpfr_srcptr, mpc_rnd_t) + int mpc_fr_sub(mpc_ptr, mpfr_srcptr, mpc_srcptr, mpc_rnd_t) + int mpc_sub_ui(mpc_ptr, mpc_srcptr, unsigned long int, mpc_rnd_t) + int mpc_ui_sub(mpc_ptr, unsigned long int, mpc_srcptr, mpc_rnd_t) + int mpc_ui_ui_sub(mpc_ptr, unsigned long int, unsigned long int, mpc_srcptr, mpc_rnd_t) + int mpc_mul(mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t) + int mpc_mul_fr(mpc_ptr, mpc_srcptr, mpfr_srcptr, mpc_rnd_t) + int mpc_mul_ui(mpc_ptr, mpc_srcptr, unsigned long int, mpc_rnd_t) + int mpc_mul_si(mpc_ptr, mpc_srcptr, long int, mpc_rnd_t) + int mpc_mul_i(mpc_ptr, mpc_srcptr, int, mpc_rnd_t) + int mpc_sqr(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_div(mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t) + int mpc_pow(mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t) + int mpc_pow_fr(mpc_ptr, mpc_srcptr, mpfr_srcptr, mpc_rnd_t) + int mpc_pow_ld(mpc_ptr, mpc_srcptr, long double, mpc_rnd_t) + int mpc_pow_d(mpc_ptr, mpc_srcptr, double, mpc_rnd_t) + int mpc_pow_si(mpc_ptr, mpc_srcptr, long, mpc_rnd_t) + int mpc_pow_ui(mpc_ptr, mpc_srcptr, unsigned long, mpc_rnd_t) + int mpc_pow_z(mpc_ptr, mpc_srcptr, mpz_srcptr, mpc_rnd_t) + int mpc_div_fr(mpc_ptr, mpc_srcptr, mpfr_srcptr, mpc_rnd_t) + int mpc_fr_div(mpc_ptr, mpfr_srcptr, mpc_srcptr, mpc_rnd_t) + int mpc_div_ui(mpc_ptr, mpc_srcptr, unsigned long int, mpc_rnd_t) + int mpc_ui_div(mpc_ptr, unsigned long int, mpc_srcptr, mpc_rnd_t) + int mpc_div_2ui(mpc_ptr, mpc_srcptr, unsigned long int, mpc_rnd_t) + int mpc_mul_2ui(mpc_ptr, mpc_srcptr, unsigned long int, mpc_rnd_t) + int mpc_div_2si(mpc_ptr, mpc_srcptr, long int, mpc_rnd_t) + int mpc_mul_2si(mpc_ptr, mpc_srcptr, long int, mpc_rnd_t) + int mpc_conj(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_neg(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_norm(mpfr_ptr, mpc_srcptr, mpfr_rnd_t) + int mpc_abs(mpfr_ptr, mpc_srcptr, mpfr_rnd_t) + int mpc_sqrt(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_set(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_set_d(mpc_ptr, double, mpc_rnd_t) + int mpc_set_d_d(mpc_ptr, double, double, mpc_rnd_t) + int mpc_set_ld(mpc_ptr, long double, mpc_rnd_t) + int mpc_set_ld_ld(mpc_ptr, long double, long double, mpc_rnd_t) + int mpc_set_f(mpc_ptr, mpf_srcptr, mpc_rnd_t) + int mpc_set_f_f(mpc_ptr, mpf_srcptr, mpf_srcptr, mpc_rnd_t) + int mpc_set_fr(mpc_ptr, mpfr_srcptr, mpc_rnd_t) + int mpc_set_fr_fr(mpc_ptr, mpfr_srcptr, mpfr_srcptr, mpc_rnd_t) + int mpc_set_q(mpc_ptr, mpq_srcptr, mpc_rnd_t) + int mpc_set_q_q(mpc_ptr, mpq_srcptr, mpq_srcptr, mpc_rnd_t) + int mpc_set_si(mpc_ptr, long int, mpc_rnd_t) + int mpc_set_si_si(mpc_ptr, long int, long int, mpc_rnd_t) + int mpc_set_ui(mpc_ptr, unsigned long int, mpc_rnd_t) + int mpc_set_ui_ui(mpc_ptr, unsigned long int, unsigned long int, mpc_rnd_t) + int mpc_set_z(mpc_ptr, mpz_srcptr, mpc_rnd_t) + int mpc_set_z_z(mpc_ptr, mpz_srcptr, mpz_srcptr, mpc_rnd_t) + void mpc_swap(mpc_ptr, mpc_ptr) + int mpc_fma(mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t) + + void mpc_set_nan(mpc_ptr) + + int mpc_real(mpfr_ptr, mpc_srcptr, mpfr_rnd_t) + int mpc_imag(mpfr_ptr, mpc_srcptr, mpfr_rnd_t) + int mpc_arg(mpfr_ptr, mpc_srcptr, mpfr_rnd_t) + int mpc_proj(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_cmp(mpc_srcptr, mpc_srcptr) + int mpc_cmp_si(mpc_srcptr, long int) + int mpc_cmp_si_si(mpc_srcptr, long int, long int) + int mpc_cmp_abs(mpc_srcptr, mpc_srcptr) + int mpc_exp(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_log(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_log10(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_sin(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_cos(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_sin_cos(mpc_ptr, mpc_ptr, mpc_srcptr, mpc_rnd_t, mpc_rnd_t) + int mpc_tan(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_sinh(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_cosh(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_tanh(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_asin(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_acos(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_atan(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_asinh(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_acosh(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_atanh(mpc_ptr, mpc_srcptr, mpc_rnd_t) + int mpc_rootofunity(mpc_ptr, unsigned long int, unsigned long int, mpc_rnd_t) + void mpc_clear(mpc_ptr) + int mpc_urandom(mpc_ptr, gmp_randstate_t) + void mpc_init2(mpc_ptr, mpfr_prec_t) + void mpc_init3(mpc_ptr, mpfr_prec_t, mpfr_prec_t) + mpfr_prec_t mpc_get_prec(mpc_srcptr x) + void mpc_get_prec2(mpfr_prec_t *pr, mpfr_prec_t *pi, mpc_srcptr x) + void mpc_set_prec(mpc_ptr, mpfr_prec_t) + const char * mpc_get_version() + + int mpc_strtoc(mpc_ptr, const char *, char **, int, mpc_rnd_t) + int mpc_set_str(mpc_ptr, const char *, int, mpc_rnd_t) + char * mpc_get_str(int, size_t, mpc_srcptr, mpc_rnd_t) + void mpc_free_str(char *) + + int mpc_inp_str(mpc_ptr, FILE *, size_t *, int, mpc_rnd_t) + size_t mpc_out_str(FILE *, int, size_t, mpc_srcptr, mpc_rnd_t) + + mpfr_t mpc_realref(mpc_t op) + mpfr_t mpc_imagref(mpc_t op) + + int MPC_INEX_RE(int) + int MPC_INEX_IM(int) diff --git a/src/sage/libs/mpc/types.pxd b/src/sage/libs/mpc/types.pxd new file mode 100644 index 00000000000..72565c08630 --- /dev/null +++ b/src/sage/libs/mpc/types.pxd @@ -0,0 +1,27 @@ +from sage.libs.mpfr.types cimport mpfr_t + +cdef extern from "mpc.h": + ctypedef struct __mpc_struct: + mpfr_t re + mpfr_t im + ctypedef __mpc_struct mpc_t[1] + ctypedef __mpc_struct* mpc_ptr + ctypedef __mpc_struct* mpc_srcptr + + ctypedef enum mpc_rnd_t: + MPC_RNDNN + MPC_RNDZN + MPC_RNDUN + MPC_RNDDN + MPC_RNDNZ + MPC_RNDZZ + MPC_RNDUZ + MPC_RNDDZ + MPC_RNDNU + MPC_RNDZU + MPC_RNDUU + MPC_RNDDU + MPC_RNDND + MPC_RNDZD + MPC_RNDUD + MPC_RNDDD diff --git a/src/sage/libs/mpfr/__init__.pxd b/src/sage/libs/mpfr/__init__.pxd index 12c7a655864..facac9aa6c7 100644 --- a/src/sage/libs/mpfr/__init__.pxd +++ b/src/sage/libs/mpfr/__init__.pxd @@ -96,6 +96,7 @@ cdef extern from "mpfr.h": int mpfr_sqrt(mpfr_t rop, mpfr_t op, mpfr_rnd_t rnd) int mpfr_sqrt_ui(mpfr_t rop, unsigned long int op, mpfr_rnd_t rnd) int mpfr_cbrt(mpfr_t rop, mpfr_t op, mpfr_rnd_t rnd) + int mpfr_rootn_ui(mpfr_t rop, mpfr_t op, unsigned long int k, mpfr_rnd_t rnd) int mpfr_root(mpfr_t rop, mpfr_t op, unsigned long int k, mpfr_rnd_t rnd) int mpfr_pow(mpfr_t rop, mpfr_t op1, mpfr_t op2, mpfr_rnd_t rnd) int mpfr_pow_ui(mpfr_t rop, mpfr_t op1, unsigned long int op2, mpfr_rnd_t rnd) diff --git a/src/sage/libs/ntl/ntl_GF2X.pyx b/src/sage/libs/ntl/ntl_GF2X.pyx index 5c3fc79d689..3b4892a132f 100644 --- a/src/sage/libs/ntl/ntl_GF2X.pyx +++ b/src/sage/libs/ntl/ntl_GF2X.pyx @@ -24,6 +24,7 @@ include 'decl.pxi' from cpython.object cimport Py_EQ, Py_NE from sage.rings.integer cimport Integer +from sage.misc.superseded import deprecation from .ntl_ZZ import unpickle_class_value from .ntl_GF2 cimport ntl_GF2 @@ -52,7 +53,8 @@ def GF2XHexOutput(have_hex=None): INPUT: have_hex if True hex representation will be used - EXAMPLES: + EXAMPLES:: + sage: m = ntl.GF2EContext(ntl.GF2X([1,1,0,1,1,0,0,0,1])) sage: x = ntl.GF2E([1,0,1,0,1], m) ; x [1 0 1 0 1] @@ -100,7 +102,8 @@ cdef class ntl_GF2X(object): OUTPUT: a new ntl.GF2X element - EXAMPLES: + EXAMPLES:: + sage: ntl.GF2X(ntl.ZZ_pX([1,1,3],2)) [1 1 1] sage: ntl.GF2X('0x1c') @@ -148,7 +151,7 @@ cdef class ntl_GF2X(object): x = x.list() # this is slow but cimport leads to circular imports elif isinstance(x, FiniteField): if x.characteristic() == 2: - x= list(x.modulus()) + x = list(x.modulus()) elif isinstance(x, FiniteField_givaroElement): x = "0x"+hex(x.integer_representation())[2:][::-1] elif isinstance(x, FiniteField_ntl_gf2eElement): @@ -159,7 +162,8 @@ cdef class ntl_GF2X(object): def __reduce__(self): """ - EXAMPLES: + EXAMPLES:: + sage: f = ntl.GF2X(ntl.ZZ_pX([1,1,3],2)) sage: loads(dumps(f)) == f True @@ -167,13 +171,14 @@ cdef class ntl_GF2X(object): sage: loads(dumps(f)) == f True """ - return unpickle_class_value, (ntl_GF2X, hex(self)) + return unpickle_class_value, (ntl_GF2X, self.hex()) def __repr__(self): """ Return the string representation of self. - EXAMPLES: + EXAMPLES:: + sage: ntl.GF2X(ntl.ZZ_pX([1,1,3],2)).__repr__() '[1 1 1]' """ @@ -181,7 +186,8 @@ cdef class ntl_GF2X(object): def __mul__(ntl_GF2X self, other): """ - EXAMPLES: + EXAMPLES:: + sage: f = ntl.GF2X([1,0,1,1]) ; g = ntl.GF2X([0,1]) sage: f*g ## indirect doctest [0 1 0 1 1] @@ -194,7 +200,8 @@ cdef class ntl_GF2X(object): def __truediv__(ntl_GF2X self, b): """ - EXAMPLES: + EXAMPLES:: + sage: a = ntl.GF2X(4) sage: a / ntl.GF2X(2) [0 1] @@ -219,7 +226,8 @@ cdef class ntl_GF2X(object): def DivRem(ntl_GF2X self, b): """ - EXAMPLES: + EXAMPLES:: + sage: a = ntl.GF2X(4) sage: a.DivRem( ntl.GF2X(2) ) ([0 1], []) @@ -237,7 +245,8 @@ cdef class ntl_GF2X(object): def __floordiv__(ntl_GF2X self, b): """ - EXAMPLES: + EXAMPLES:: + sage: a = ntl.GF2X(4) sage: a // ntl.GF2X(2) [0 1] @@ -254,7 +263,8 @@ cdef class ntl_GF2X(object): def __mod__(ntl_GF2X self, b): """ - EXAMPLES: + EXAMPLES:: + sage: a = ntl.GF2X(4) sage: a % ntl.GF2X(2) [] @@ -271,7 +281,8 @@ cdef class ntl_GF2X(object): def __sub__(ntl_GF2X self, other): """ - EXAMPLES: + EXAMPLES:: + sage: f = ntl.GF2X([1,0,1,1]) ; g = ntl.GF2X([0,1]) sage: f - g ## indirect doctest [1 1 1 1] @@ -286,7 +297,8 @@ cdef class ntl_GF2X(object): def __add__(ntl_GF2X self, other): """ - EXAMPLES: + EXAMPLES:: + sage: f = ntl.GF2X([1,0,1,1]) ; g = ntl.GF2X([0,1,0]) sage: f + g ## indirect doctest [1 1 1 1] @@ -299,7 +311,8 @@ cdef class ntl_GF2X(object): def __neg__(ntl_GF2X self): """ - EXAMPLES: + EXAMPLES:: + sage: f = ntl.GF2X([1,0,1,1]) sage: -f ## indirect doctest [1 0 1 1] @@ -312,7 +325,8 @@ cdef class ntl_GF2X(object): def __pow__(ntl_GF2X self, long e, ignored): """ - EXAMPLES: + EXAMPLES:: + sage: f = ntl.GF2X([1,0,1,1]) ; g = ntl.GF2X([0,1,0]) sage: f**3 ## indirect doctest [1 0 1 1 1 0 0 1 1 1] @@ -355,7 +369,8 @@ cdef class ntl_GF2X(object): INPUT: i -- offset/power of X - EXAMPLES: + EXAMPLES:: + sage: a = ntl.GF2X(4); a [0 0 1] sage: a << 2 @@ -373,7 +388,8 @@ cdef class ntl_GF2X(object): INPUT: i -- offset/power of X - EXAMPLES: + EXAMPLES:: + sage: a = ntl.GF2X(4); a [0 0 1] sage: a >> 1 @@ -390,7 +406,8 @@ cdef class ntl_GF2X(object): INPUT: other -- ntl.GF2X - EXAMPLES: + EXAMPLES:: + sage: a = ntl.GF2X(10) sage: b = ntl.GF2X(4) sage: a.GCD(b) @@ -413,7 +430,8 @@ cdef class ntl_GF2X(object): INPUT: other -- ntl.GF2X - EXAMPLES: + EXAMPLES:: + sage: a = ntl.GF2X(10) sage: b = ntl.GF2X(4) sage: r,s,t = a.XGCD(b) @@ -435,7 +453,8 @@ cdef class ntl_GF2X(object): """ Returns the degree of this polynomial - EXAMPLES: + EXAMPLES:: + sage: ntl.GF2X([1,0,1,1]).deg() 3 """ @@ -445,7 +464,8 @@ cdef class ntl_GF2X(object): """ Represents this element as a list of binary digits. - EXAMPLES: + EXAMPLES:: + sage: e=ntl.GF2X([0,1,1]) sage: e.list() [0, 1, 1] @@ -467,7 +487,8 @@ cdef class ntl_GF2X(object): faster and preserves the HexOutput state as opposed to the above code. - EXAMPLES: + EXAMPLES:: + sage: e=ntl.GF2X([1,1,0,1,1,1,0,0,1]) sage: e.bin() '[1 1 0 1 1 1 0 0 1]' @@ -481,22 +502,32 @@ cdef class ntl_GF2X(object): GF2XHexOutput_c[0] = _hex return s - def __hex__(ntl_GF2X self): + def hex(ntl_GF2X self): """ - Returns hexadecimal representation of this element. It is - the same as setting \code{ntl.GF2XHexOutput(True)} and - representing this element afterwards. However it should be - faster and preserves the HexOutput state as opposed to - the above code. + Return an hexadecimal representation of this element. - EXAMPLES: - sage: e=ntl.GF2X([1,1,0,1,1,1,0,0,1]) - sage: hex(e) - '0xb31' + It is the same as setting \code{ntl.GF2XHexOutput(True)} and + representing this element afterwards. However it should be + faster and preserves the HexOutput state as opposed to the + above code. OUTPUT: - string representing this element in hexadecimal + string representing this element in hexadecimal + + EXAMPLES:: + + sage: e = ntl.GF2X([1,1,0,1,1,1,0,0,1]) + sage: e.hex() + '0xb31' + + TESTS:: + + sage: hex(e) + doctest:warning...: + DeprecationWarning: use the method .hex instead + See http://trac.sagemath.org/24514 for details. + '0xb31' """ cdef long _hex = GF2XHexOutput_c[0] GF2XHexOutput_c[0] = 1 @@ -504,8 +535,12 @@ cdef class ntl_GF2X(object): GF2XHexOutput_c[0] = _hex return s + def __hex__(self): + deprecation(24514, 'use the method .hex instead') + return self.hex() + def __hash__(self): - return hash(hex(self)) + return hash(self.hex()) def _sage_(ntl_GF2X self, R=None): """ @@ -521,7 +556,8 @@ cdef class ntl_GF2X(object): OUTPUT: polynomial in R - EXAMPLES: + EXAMPLES:: + sage: f = ntl.GF2X([1,0,1,1,0,1]) sage: f._sage_() x^5 + x^3 + x^2 + 1 @@ -542,7 +578,8 @@ cdef class ntl_GF2X(object): INPUT: i -- degree of X - EXAMPLES: + EXAMPLES:: + sage: e = ntl.GF2X([0,1,0,1]) sage: e.coeff(0) 0 @@ -570,7 +607,8 @@ cdef class ntl_GF2X(object): Return the leading coefficient of self. This is always 1 except when self == 0. - EXAMPLES: + EXAMPLES:: + sage: e = ntl.GF2X([0,1]) sage: e.LeadCoeff() 1 @@ -586,7 +624,8 @@ cdef class ntl_GF2X(object): """ Return the constant term of self. - EXAMPLES: + EXAMPLES:: + sage: e = ntl.GF2X([1,0,1]) sage: e.ConstTerm() 1 @@ -602,7 +641,8 @@ cdef class ntl_GF2X(object): """ Return the constant term of self. - EXAMPLES: + EXAMPLES:: + sage: e = ntl.GF2X([1,0,1]); e [1 0 1] sage: e.SetCoeff(1,1) @@ -643,7 +683,8 @@ cdef class ntl_GF2X(object): INPUT: hi -- bit position until which reverse is requested - EXAMPLES: + EXAMPLES:: + sage: e = ntl.GF2X([1,0,1,1,0]) sage: e.reverse() [1 1 0 1] @@ -658,7 +699,8 @@ cdef class ntl_GF2X(object): """ Return the number of nonzero coefficients in self. - EXAMPLES: + EXAMPLES:: + sage: e = ntl.GF2X([1,0,1,1,0]) sage: e.weight() 3 @@ -685,7 +727,8 @@ cdef class ntl_GF2X(object): """ returns number of bits of self, i.e., deg(self) + 1. - EXAMPLES: + EXAMPLES:: + sage: e = ntl.GF2X([1,0,1,1,0]) sage: e.NumBits() 4 @@ -704,7 +747,8 @@ cdef class ntl_GF2X(object): """ Returns number of bytes of self, i.e., floor((NumBits(self)+7)/8) - EXAMPLES: + EXAMPLES:: + sage: e = ntl.GF2X([1,0,1,1,0,0,0,0,1,1,1,0,0,1,1,0,1,1]) sage: e.NumBytes() 3 diff --git a/src/sage/libs/pynac/pynac.pyx b/src/sage/libs/pynac/pynac.pyx index 419cc47829c..529386766c7 100644 --- a/src/sage/libs/pynac/pynac.pyx +++ b/src/sage/libs/pynac/pynac.pyx @@ -340,7 +340,7 @@ cdef stdstring* py_repr(o, int level): else: t = s # Python complexes are always printed with parentheses - # we try to avoid double parantheses + # we try to avoid double parentheses if type(o) is not complex and \ (' ' in t or '/' in t or '+' in t or '-' in t or '*' in t \ or '^' in t): diff --git a/src/sage/manifolds/differentiable/automorphismfield.py b/src/sage/manifolds/differentiable/automorphismfield.py index 62af1f3b2e3..94956dc89f8 100644 --- a/src/sage/manifolds/differentiable/automorphismfield.py +++ b/src/sage/manifolds/differentiable/automorphismfield.py @@ -106,7 +106,7 @@ class AutomorphismField(TensorField): sage: W = U.intersection(V) sage: a.add_comp_by_continuation(eV, W, c_uv) - At this stage, the automorphims field `a` is fully defined:: + At this stage, the automorphism field `a` is fully defined:: sage: a.display(eU) a = d/dx*dx + x d/dx*dy + 2 d/dy*dy diff --git a/src/sage/manifolds/differentiable/integrated_curve.py b/src/sage/manifolds/differentiable/integrated_curve.py index e8cf9507090..7b5011d702a 100644 --- a/src/sage/manifolds/differentiable/integrated_curve.py +++ b/src/sage/manifolds/differentiable/integrated_curve.py @@ -2378,7 +2378,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): the usual embedding of `\mathbb{S}^{2}` in `\mathbb{R}^{3}` thanks to using an orthonormal frame, since providing the components with respect to the coordinate basis - would require mutliplying the second component (i.e. the `\phi` + would require multiplying the second component (i.e. the `\phi` component) in order to picture the vector in the same way. This subtlety will need to be taken into account later when the numerical curve will be compared to the analytical solution. diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index 125c82a392e..36b1e180342 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -305,8 +305,7 @@ cdef class Matrix_integer_dense(Matrix_dense): Actually it is only necessary that the input can be coerced to a list, so the following also works:: - sage: v = reversed(range(4)); type(v) - <... 'listreverseiterator'> + sage: v = reversed(range(4)) sage: A(v) [3 2] [1 0] diff --git a/src/sage/matrix/matrix_rational_dense.pyx b/src/sage/matrix/matrix_rational_dense.pyx index 5c3ad109868..7d79509451a 100644 --- a/src/sage/matrix/matrix_rational_dense.pyx +++ b/src/sage/matrix/matrix_rational_dense.pyx @@ -1786,7 +1786,7 @@ cdef class Matrix_rational_dense(Matrix_dense): def _echelonize_multimodular(self, height_guess=None, proof=None): """ - Echelonize self using mutlimodular recomposition + Echelonize ``self`` using multimodular recomposition. REFERENCE: diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index e34a9ba9506..d621c106c6a 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -157,7 +157,7 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation): sage: get_matrix_class(GF(3), 2, 2, False, 'm4ri') Traceback (most recent call last): ... - ValueError: m4ri matrices are only available in characterstic 2 + ValueError: m4ri matrices are only available in characteristic 2 sage: get_matrix_class(Zmod(2**30), 2, 2, False, 'linbox-float') Traceback (most recent call last): ... @@ -227,7 +227,7 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation): if implementation == 'm4ri': if R.order() != 2: - raise ValueError('m4ri matrices are only available in characterstic 2') + raise ValueError('m4ri matrices are only available in characteristic 2') else: return matrix_mod2_dense.Matrix_mod2_dense elif implementation == 'linbox-float': @@ -1322,7 +1322,7 @@ def __iter__(self): #yield the empty matrix in that case and return if number_of_entries == 0: yield self(0) - raise StopIteration + return import sage.combinat.integer_vector diff --git a/src/sage/matrix/special.py b/src/sage/matrix/special.py index ad7309185e8..c698ba5e97e 100644 --- a/src/sage/matrix/special.py +++ b/src/sage/matrix/special.py @@ -750,12 +750,12 @@ def diagonal_matrix(arg0=None, arg1=None, arg2=None, sparse=True): sage: A.parent() Full MatrixSpace of 0 by 0 sparse matrices over Integer Ring - Giving the entries improperly may first complain about not having a length. :: + Giving the entries improperly may first complain about not being iterable:: sage: diagonal_matrix(QQ, 5, 10) Traceback (most recent call last): ... - TypeError: unable to determine number of entries for diagonal matrix construction + TypeError: 'sage.rings.integer.Integer' object is not iterable Giving too many entries will raise an error. :: @@ -771,16 +771,21 @@ def diagonal_matrix(arg0=None, arg1=None, arg2=None, sparse=True): ... ValueError: number of diagonal matrix entries (1) exceeds the requested matrix size (-2) - Types for the entries are limited, even though they may have a length. :: + Types for the entries need to be iterable (tuple, list, vector, NumPy array, + etc):: sage: diagonal_matrix(x^2) Traceback (most recent call last): ... - TypeError: diagonal matrix entries are not a supported type (list, tuple, vector, or NumPy array) + TypeError: 'sage.symbolic.expression.Expression' object is not iterable + + TESTS:: + + sage: A = diagonal_matrix(reversed(range(4))) AUTHOR: - - Rob Beezer (2011-01-11): total rewrite + - Rob Beezer (2011-01-11): total rewrite """ # Roll arguments leftward # @@ -801,6 +806,11 @@ def diagonal_matrix(arg0=None, arg1=None, arg2=None, sparse=True): # Formats 1, 2, 3, 4 entries = arg0 + # sanity check for entries + from numpy import ndarray + if not isinstance(entries, (list, tuple, ndarray)): + entries = list(entries) + # Reconcile matrix size and number of entries try: nentries = len(entries) @@ -816,11 +826,6 @@ def diagonal_matrix(arg0=None, arg1=None, arg2=None, sparse=True): if len(entries) == 0 and ring is None: ring = rings.ZZ - # Sanity check on entries (partially, e.g. a list of lists will survive this check) - from numpy import ndarray - if not any([isinstance(entries, (list, tuple)), isinstance(entries, ndarray), is_Vector(entries)]): - raise TypeError('diagonal matrix entries are not a supported type (list, tuple, vector, or NumPy array)') - # Convert entries to a list v over a common ring from sage.modules.free_module_element import prepare v, ring = prepare(entries, ring) diff --git a/src/sage/matroids/graphic_matroid.py b/src/sage/matroids/graphic_matroid.py index e4667b27ecc..46ccea8ffd1 100644 --- a/src/sage/matroids/graphic_matroid.py +++ b/src/sage/matroids/graphic_matroid.py @@ -561,7 +561,7 @@ def _minor(self, contractions=frozenset([]), deletions=frozenset([])): def _has_minor(self, N, certificate=False): """ - Check if the matroid has a minor isomoprhic to M(H). + Check if the matroid has a minor isomorphic to M(H). INPUT: @@ -1079,9 +1079,9 @@ def _is_isomorphic(self, other, certificate=False): sage: M = Matroid(Graph(edgelist)) sage: N = Matroid(range(6), graphs.WheelGraph(4)) sage: M._is_isomorphic(N, certificate=True) - (True, {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 5}) + (True, {'a': 2, 'b': 4, 'c': 5, 'd': 0, 'e': 1, 'f': 3}) sage: N._is_isomorphic(M, certificate=True) - (True, {0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f'}) + (True, {0: 'd', 1: 'e', 2: 'a', 3: 'f', 4: 'b', 5: 'c'}) sage: O = Matroid(range(6), graphs.CycleGraph(6)) sage: M._is_isomorphic(O) False @@ -1145,7 +1145,7 @@ def _isomorphism(self, other): sage: M = Matroid(Graph(edgelist)) sage: N = Matroid(range(6), graphs.WheelGraph(4)) sage: M._isomorphism(N) - {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 5} + {'a': 2, 'b': 4, 'c': 5, 'd': 0, 'e': 1, 'f': 3} sage: O = Matroid(Graph(edgelist), regular=True) sage: M._isomorphism(O) {'a': 'a', 'b': 'c', 'c': 'b', 'd': 'e', 'e': 'd', 'f': 'f'} @@ -1497,7 +1497,8 @@ def graphic_coextension(self, u, v=None, X=None, element=None): EXAMPLES:: - sage: M = Matroid(range(8), graphs.WheelGraph(5)) + sage: G = Graph([(0, 1, 0), (0, 2, 1), (0, 3, 2), (0, 4, 3), (1, 2, 4), (1, 4, 5), (2, 3, 6), (3, 4, 7)]) + sage: M = Matroid(G) sage: M1 = M.graphic_coextension(0, X=[1,2], element='a') sage: M1.graph().edges() [(0, 1, 0), @@ -1625,7 +1626,8 @@ def graphic_coextensions(self, vertices=None, v=None, element=None, cosimple=Fal EXAMPLES:: - sage: M = Matroid(range(8), graphs.WheelGraph(5)) + sage: G = Graph([(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 4), (2, 3), (3, 4)]) + sage: M = Matroid(range(8), G) sage: I = M.graphic_coextensions(vertices=[0], element='a') sage: for N in I: ....: N.graph().edges_incident(0) diff --git a/src/sage/matroids/set_system.pyx b/src/sage/matroids/set_system.pyx index 0256f042470..62cfc4d359a 100644 --- a/src/sage/matroids/set_system.pyx +++ b/src/sage/matroids/set_system.pyx @@ -600,7 +600,7 @@ cdef class SetSystem: partition ``P``, and while ``P`` has a partition element ``p`` with more than one element, select an arbitrary ``e`` from the first such ``p`` and split ``p`` into ``p-e``. Then replace ``P`` with - the equitabele refinement of this partition. + the equitable refinement of this partition. INPUT: diff --git a/src/sage/misc/c3_controlled.pyx b/src/sage/misc/c3_controlled.pyx index 38b3b7577f7..831b20f6829 100644 --- a/src/sage/misc/c3_controlled.pyx +++ b/src/sage/misc/c3_controlled.pyx @@ -346,7 +346,8 @@ doctest:: Category of finite dimensional hopf algebras with basis over Rational Field, Category of finite enumerated permutation groups, Category of finite weyl groups, - Category of group algebras over Rational Field] + Category of group algebras over Rational Field, + Category of number fields] AUTHOR: diff --git a/src/sage/misc/gperftools.py b/src/sage/misc/gperftools.py index 142308c24d2..36cd36f1c39 100644 --- a/src/sage/misc/gperftools.py +++ b/src/sage/misc/gperftools.py @@ -242,7 +242,7 @@ def _executable(self): sage: from sage.misc.gperftools import Profiler sage: prof = Profiler() sage: prof._executable() - '.../python' + '.../python...' """ import sys return sys.executable diff --git a/src/sage/misc/mrange.py b/src/sage/misc/mrange.py index 08567bf5fd8..36cbf6c2232 100644 --- a/src/sage/misc/mrange.py +++ b/src/sage/misc/mrange.py @@ -82,7 +82,7 @@ def _is_finite(L, fallback=True): try: n = _len(L) - except (TypeError, AttributeError): + except (TypeError, AttributeError, NotImplementedError): # We usually assume L is finite for speed reasons return fallback diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index 9bde19dc187..61791093840 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -680,7 +680,7 @@ def visit_BinOp(self, node): sage: import ast, sage.misc.sageinspect as sms sage: visitor = sms.SageArgSpecVisitor() sage: vis = lambda x: visitor.visit(ast.parse(x).body[0].value) - sage: [vis(d) for d in ['(3+(2*4))', '7|8', '5^3', '7/3', '7//3', '3<<4']] #indirect doctest # optional - python2 + sage: [vis(d) for d in ['(3+(2*4))', '7|8', '5^3', '7/3', '7//3', '3<<4']] #indirect doctest # py2 [11, 15, 6, 2, 2, 48] """ op = node.op.__class__.__name__ diff --git a/src/sage/modular/btquotients/pautomorphicform.py b/src/sage/modular/btquotients/pautomorphicform.py index 98ccf4ddc18..9daf1e3f49c 100644 --- a/src/sage/modular/btquotients/pautomorphicform.py +++ b/src/sage/modular/btquotients/pautomorphicform.py @@ -45,7 +45,7 @@ Cameron Franc Ph.D. thesis, McGill University, 2011. """ -from __future__ import print_function +from __future__ import print_function, division from builtins import zip @@ -751,7 +751,7 @@ def __init__(self, X, k, prec=None, basis_matrix=None, base_field=None): self._U = Symk(self._k - 2, base=self._R, act_on_left=True, adjuster=_btquot_adjuster(), - dettwist=-ZZ((self._k - 2) / 2), act_padic=True) + dettwist=-ZZ((self._k - 2) // 2), act_padic=True) if basis_matrix is None: self.__rank = self._X.dimension_harmonic_cocycles(self._k) @@ -1302,9 +1302,9 @@ def __apply_hecke_operator(self, l, f): """ HeckeData, alpha = self._X._get_hecke_data(l) if self.level() % l == 0: - factor = QQ(l ** (Integer((self._k - 2) / 2)) / (l + 1)) + factor = QQ(l ** (Integer((self._k - 2) // 2)) / (l + 1)) else: - factor = QQ(l ** (Integer((self._k - 2) / 2))) + factor = QQ(l ** (Integer((self._k - 2) // 2))) p = self._X._p alphamat = self.embed_quaternion(alpha) tmp = [self._U(0) for jj in range(len(self._E))] @@ -1925,7 +1925,7 @@ def integrate(self, f, center=1, level=0, method='moments'): delta = e.determinant() verbose('%s' % (R2([e[0, 1], e[0, 0]]) / R2([e[1, 1], e[1, 0]]))) - tmp = ((c * x + d) ** n * delta ** -ZZ(n / 2)) * f((a * x + b) / (c * x + d)) + tmp = ((c * x + d) ** n * delta ** -ZZ(n // 2)) * f((a * x + b) / (c * x + d)) exp = R1(tmp.numerator()) / R1(tmp.denominator()) new = eval_dist_at_powseries(self.evaluate(e), exp) @@ -2283,12 +2283,12 @@ def __init__(self, domain, U, prec=None, t=None, R=None, prec_cap=U - 1 + t, act_on_left=True, adjuster=_btquot_adjuster(), - dettwist=-ZZ((U - 2) / 2), + dettwist=-ZZ((U - 2) // 2), act_padic=True) else: self._U = Symk(U - 2, base=self._R, act_on_left=True, adjuster=_btquot_adjuster(), - dettwist=-ZZ((U - 2) / 2), + dettwist=-ZZ((U - 2) // 2), act_padic=True) else: self._U = U diff --git a/src/sage/modular/hecke/module.py b/src/sage/modular/hecke/module.py index 98231db856d..113b5e19fed 100644 --- a/src/sage/modular/hecke/module.py +++ b/src/sage/modular/hecke/module.py @@ -154,7 +154,7 @@ def _compute_hecke_matrix_prime_power(self, p, r, **kwds): if eps is None: raise NotImplementedError("either character or _compute_hecke_matrix_prime_power must be overloaded in a derived class") k = self.weight() - Tpr2 = self._hecke_matrices[pow/p] + Tpr2 = self._hecke_matrices[pow // p] return Tp*Tpr1 - eps(p)*(p**(k-1)) * Tpr2 def _compute_hecke_matrix_general_product(self, F, **kwds): diff --git a/src/sage/modular/local_comp/liftings.py b/src/sage/modular/local_comp/liftings.py index d5f59f97ffb..10fcde9fb39 100644 --- a/src/sage/modular/local_comp/liftings.py +++ b/src/sage/modular/local_comp/liftings.py @@ -6,9 +6,11 @@ problems. """ -from sage.rings.all import ZZ, Zmod +from sage.rings.all import ZZ from sage.arith.all import crt, inverse_mod from sage.modular.modsym.p1list import lift_to_sl2z +from copy import copy + def lift_to_gamma1(g, m, n): r""" @@ -18,6 +20,12 @@ def lift_to_gamma1(g, m, n): `\begin{pmatrix} 1 & * \\ 0 & 1 \end{pmatrix} \pmod n`. Here `m` and `n` must be coprime. + INPUT: + + - ``g`` -- list of 4 integers defining a `2 \times 2` matrix + + - `m`, `n` -- coprime positive integers + Here `m` and `n` should be coprime positive integers. Either of `m` and `n` can be `1`. If `n = 1`, this still makes perfect sense; this is what is called by the function :func:`~lift_matrix_to_sl2z`. If `m = 1` this is a @@ -56,15 +64,17 @@ def lift_to_gamma1(g, m, n): [1, 0, 0, 1] """ if m == 1: - return [ZZ(1),ZZ(0),ZZ(0),ZZ(1)] - a,b,c,d = [ZZ(x) for x in g] - if not (a*d - b*c) % m == 1: - raise ValueError( "Determinant is {0} mod {1}, should be 1".format((a*d - b*c) % m, m) ) + return [ZZ.one(), ZZ.zero(), ZZ.zero(), ZZ.one()] + a, b, c, d = [ZZ(x) for x in g] + det = (a * d - b * c) % m + if det != 1: + raise ValueError("Determinant is {0} mod {1}, should be 1".format(det, m)) c2 = crt(c, 0, m, n) d2 = crt(d, 1, m, n) - a3,b3,c3,d3 = [ZZ(_) for _ in lift_to_sl2z(c2,d2,m*n)] + a3,b3,c3,d3 = [ZZ(_) for _ in lift_to_sl2z(c2, d2, m * n)] r = (a3*b - b3*a) % m - return [a3 + r*c3, b3 + r*d3, c3, d3] + return [a3 + r * c3, b3 + r * d3, c3, d3] + def lift_matrix_to_sl2z(A, N): r""" @@ -74,7 +84,13 @@ def lift_matrix_to_sl2z(A, N): This is a special case of :func:`~lift_to_gamma1`, and is coded as such. - TESTS:: + INPUT: + + - ``A`` -- list of 4 integers defining a `2 \times 2` matrix + + - `N` -- positive integer + + EXAMPLES:: sage: from sage.modular.local_comp.liftings import lift_matrix_to_sl2z sage: lift_matrix_to_sl2z([10, 11, 3, 11], 19) @@ -88,6 +104,7 @@ def lift_matrix_to_sl2z(A, N): """ return lift_to_gamma1(A, N, 1) + def lift_gen_to_gamma1(m, n): r""" Return four integers defining a matrix in `\mathrm{SL}_2(\ZZ)` which is @@ -97,6 +114,10 @@ def lift_gen_to_gamma1(m, n): This is a special case of :func:`~lift_to_gamma1`, and is coded as such. + INPUT: + + - `m`, `n` -- coprime positive integers + EXAMPLES:: sage: from sage.modular.local_comp.liftings import lift_gen_to_gamma1 @@ -114,6 +135,7 @@ def lift_gen_to_gamma1(m, n): """ return lift_to_gamma1([0,-1,1,0], m, n) + def lift_uniformiser_odd(p, u, n): r""" Construct a matrix over `\ZZ` whose determinant is `p`, and which is @@ -132,7 +154,7 @@ def lift_uniformiser_odd(p, u, n): """ g = lift_gen_to_gamma1(p**u, n) - return [p*g[0], g[1], p*g[2], g[3]] + return [p * g[0], g[1], p * g[2], g[3]] def lift_ramified(g, p, u, n): @@ -156,9 +178,116 @@ def lift_ramified(g, p, u, n): sage: type(lift_ramified([8,2,12,2], 3, 2, 23)[0]) """ - a,b,c,d = lift_to_gamma1(g, p**u, n) - r = crt( (c - g[2]) / p**u * inverse_mod(a, p), 0, p, n) + a, b, c, d = lift_to_gamma1(g, p**u, n) + r = crt((c - g[2]) / p**u * inverse_mod(a, p), 0, p, n) c = c - p**u * r * a d = d - p**u * r * b # assert (c - g[2]) % p**(u+1) == 0 - return [a,b,c,d] + return [a, b, c, d] + + +def lift_for_SL(A, N=None): + r""" + Lift a matrix `A` from `SL_m(\ZZ / N\ZZ)` to `SL_m(\ZZ)`. + + This follows [Shi1971]_, Lemma 1.38, p. 21. + + INPUT: + + - ``A`` -- a square matrix with coefficients in `\ZZ / N\ZZ` (or `\ZZ`) + + - ``N`` -- the modulus (optional) required only if the matrix ``A`` + has coefficients in `\ZZ` + + EXAMPLES:: + + sage: from sage.modular.local_comp.liftings import lift_for_SL + sage: A = matrix(Zmod(11), 4, 4, [6, 0, 0, 9, 1, 6, 9, 4, 4, 4, 8, 0, 4, 0, 0, 8]) + sage: A.det() + 1 + sage: L = lift_for_SL(A) + sage: L.det() + 1 + sage: (L - A) == 0 + True + + sage: B = matrix(Zmod(19), 4, 4, [1, 6, 10, 4, 4, 14, 15, 4, 13, 0, 1, 15, 15, 15, 17, 10]) + sage: B.det() + 1 + sage: L = lift_for_SL(B) + sage: L.det() + 1 + sage: (L - B) == 0 + True + + TESTS:: + + sage: lift_for_SL(matrix(3,3,[1,2,0,3,4,0,0,0,1]),3) + [10 14 3] + [ 9 10 3] + [ 3 3 1] + + sage: A = matrix(Zmod(7), 2, [1,0,0,1]) + sage: L = lift_for_SL(A) + sage: L.parent() + Full MatrixSpace of 2 by 2 dense matrices over Integer Ring + + sage: A = matrix(Zmod(7), 1, [1]) + sage: L = lift_for_SL(A); L + [1] + + sage: A = matrix(ZZ, 2, [1,0,0,1]) + sage: lift_for_SL(A) + Traceback (most recent call last): + ... + ValueError: you must choose the modulus + + sage: for _ in range(100): + ....: d = randint(0, 10) + ....: p = choice([2,3,5,7,11]) + ....: M = random_matrix(Zmod(p), d, algorithm='unimodular') + ....: assert lift_for_SL(M).det() == 1 + """ + from sage.matrix.constructor import matrix + from sage.matrix.special import (identity_matrix, diagonal_matrix, + block_diagonal_matrix) + from sage.misc.misc_c import prod + + ring = A.parent().base_ring() + if N is None: + if ring is ZZ: + raise ValueError('you must choose the modulus') + else: + N = ring.characteristic() + + m = A.nrows() + if m <= 1: + return identity_matrix(ZZ, m) + + AZZ = A .change_ring(ZZ) + D, U, V = AZZ.smith_form() + diag = diagonal_matrix([-1] + [1] * (m - 1)) + if U.det() == -1: + U = diag * U + if V.det() == -1: + V = V * diag + + a = [U.row(i) * AZZ * V.column(i) for i in range(m)] + b = prod(a[1:]) + + Winv = identity_matrix(m) + Winv[1, 0] = 1 - b + Winv[0, 1] = -1 + Winv[1, 1] = b + + Xinv = identity_matrix(m) + Xinv[0, 1] = a[1] + + Cp = diagonal_matrix(a[1:]) + Cp[0, 0] *= a[0] + C = lift_for_SL(Cp, N) + + Cpp = block_diagonal_matrix(identity_matrix(1), C) + Cpp[1, 0] = 1 - a[0] + + return (~U * Winv * Cpp * Xinv * ~V).change_ring(ZZ) diff --git a/src/sage/modular/modform/element.py b/src/sage/modular/modform/element.py index 67aa2e90c46..0d103f49d64 100644 --- a/src/sage/modular/modform/element.py +++ b/src/sage/modular/modform/element.py @@ -24,7 +24,7 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import absolute_import +from __future__ import absolute_import, division from six.moves import range import sage.modular.hecke.element as element @@ -2661,8 +2661,8 @@ def __compute_weight2_trivial_character(self, X): v.append(F(t-1)/F(24)) else: an = sigma(n,1) - if n%t == 0: - an -= t * sigma(n/t,1) + if n % t == 0: + an -= t * sigma(n//t,1) v.append(an) return v diff --git a/src/sage/modular/modform_hecketriangle/graded_ring_element.py b/src/sage/modular/modform_hecketriangle/graded_ring_element.py index a23d922460f..ca15bf9e13a 100644 --- a/src/sage/modular/modform_hecketriangle/graded_ring_element.py +++ b/src/sage/modular/modform_hecketriangle/graded_ring_element.py @@ -1944,8 +1944,8 @@ def evaluate(self, tau, prec = None, num_prec = None, check=False): 2.525...e-10 - 3.884...e-6*I sage: f_i(i) 0 - sage: f_i(i + 1e-1000) - -6.084...e-14 - 4.101...e-1000*I + sage: f_i(i + 1e-1000) # rel tol 5e-2 + -6.08402217494586e-14 - 4.10147008296517e-1000*I sage: f_inf(infinity) 0 diff --git a/src/sage/monoids/automatic_semigroup.py b/src/sage/monoids/automatic_semigroup.py index 2de54a47b73..3f7e745b776 100644 --- a/src/sage/monoids/automatic_semigroup.py +++ b/src/sage/monoids/automatic_semigroup.py @@ -680,7 +680,12 @@ def _iter_concurrent(self): # been called before we move on to the next line i += 1 if i == len(self._elements) and not self._constructed: - next(self._iter) + try: + next(self._iter) + except StopIteration: + # Don't allow StopIteration to bubble up from generator + # see PEP-479 + break def cardinality(self): """ diff --git a/src/sage/monoids/string_monoid_element.py b/src/sage/monoids/string_monoid_element.py index e4bf95f1314..f1ac8335740 100644 --- a/src/sage/monoids/string_monoid_element.py +++ b/src/sage/monoids/string_monoid_element.py @@ -231,11 +231,10 @@ def __iter__(self): [S, H, R, U, B, B, E, R, Y] """ l = len(self._element_list) - i=0 + i = 0 while i < l: yield self[i] - i+=1 - raise StopIteration + i += 1 def __getitem__(self, n): """ diff --git a/src/sage/numerical/backends/coin_backend.pyx b/src/sage/numerical/backends/coin_backend.pyx index da527137d21..0e95c7fd02b 100644 --- a/src/sage/numerical/backends/coin_backend.pyx +++ b/src/sage/numerical/backends/coin_backend.pyx @@ -445,7 +445,7 @@ cdef class CoinBackend(GenericBackend): INPUT: - - ``constraints`` -- an interable containing the indices of the rows to remove + - ``constraints`` -- an iterable containing the indices of the rows to remove EXAMPLES:: diff --git a/src/sage/numerical/backends/cvxopt_backend.pyx b/src/sage/numerical/backends/cvxopt_backend.pyx index 6194549c1a2..cd41098557f 100644 --- a/src/sage/numerical/backends/cvxopt_backend.pyx +++ b/src/sage/numerical/backends/cvxopt_backend.pyx @@ -322,7 +322,7 @@ cdef class CVXOPTBackend(GenericBackend): INPUT: - - ``indices`` (list of integers) -- this list constains the + - ``indices`` (list of integers) -- this list contains the indices of the constraints in which the variable's coefficient is nonzero diff --git a/src/sage/numerical/backends/glpk_backend.pyx b/src/sage/numerical/backends/glpk_backend.pyx index 64781bd3a8a..0e81c13dad5 100644 --- a/src/sage/numerical/backends/glpk_backend.pyx +++ b/src/sage/numerical/backends/glpk_backend.pyx @@ -154,7 +154,7 @@ cdef class GLPKBackend(GenericBackend): Add ``number`` new variables. This amounts to adding new columns to the matrix. By default, - the variables are both positive, real and theor coefficient in + the variables are both positive, real and their coefficient in the objective function is 0.0. INPUT: @@ -786,7 +786,7 @@ cdef class GLPKBackend(GenericBackend): INPUT: - - ``indices`` (list of integers) -- this list constains the + - ``indices`` (list of integers) -- this list contains the indices of the constraints in which the variable's coefficient is nonzero diff --git a/src/sage/numerical/backends/glpk_graph_backend.pyx b/src/sage/numerical/backends/glpk_graph_backend.pyx index 52a4b6e8e97..65ca73706f6 100644 --- a/src/sage/numerical/backends/glpk_graph_backend.pyx +++ b/src/sage/numerical/backends/glpk_graph_backend.pyx @@ -626,7 +626,7 @@ cdef class GLPKGraphBackend(object): - ``edges`` -- An iterable container of pairs of the form ``(u, v)``, where ``u`` is name (as ``str``) of the tail vertex and ``v`` is the - name (as ``str``) of the head vertex or an interable container of + name (as ``str``) of the head vertex or an iterable container of triples of the form ``(u, v, params)`` where params is a ``dict`` as described in ``add_edge``. diff --git a/src/sage/numerical/backends/ppl_backend.pyx b/src/sage/numerical/backends/ppl_backend.pyx index 4e4b5707af6..1d089baa2be 100644 --- a/src/sage/numerical/backends/ppl_backend.pyx +++ b/src/sage/numerical/backends/ppl_backend.pyx @@ -562,7 +562,7 @@ cdef class PPLBackend(GenericBackend): INPUT: - - ``indices`` (list of integers) -- this list constains the + - ``indices`` (list of integers) -- this list contains the indices of the constraints in which the variable's coefficient is nonzero diff --git a/src/sage/numerical/interactive_simplex_method.py b/src/sage/numerical/interactive_simplex_method.py index 9ead04316ed..852ff329193 100644 --- a/src/sage/numerical/interactive_simplex_method.py +++ b/src/sage/numerical/interactive_simplex_method.py @@ -1631,7 +1631,7 @@ def plot_feasible_set(self, xmin=None, xmax=None, ymin=None, ymax=None, b = b.n().change_ring(QQ) F = self.feasible_set() if ymax is None: - ymax = max(map(abs, b) + [v[1] for v in F.vertices()]) + ymax = max([abs(bb) for bb in b] + [v[1] for v in F.vertices()]) if ymin is None: ymin = min([-ymax/4.0] + [v[1] for v in F.vertices()]) if xmax is None: diff --git a/src/sage/numerical/linear_functions.pyx b/src/sage/numerical/linear_functions.pyx index de1bf534ff9..4120cf67fdf 100644 --- a/src/sage/numerical/linear_functions.pyx +++ b/src/sage/numerical/linear_functions.pyx @@ -1570,7 +1570,7 @@ cdef class LinearConstraint(LinearFunctionOrConstraint): 3 == x_2 """ if not self.is_equation() or self.is_trivial(): - raise StopIteration + return term_iter = iter(self) lhs = next(term_iter) rhs = next(term_iter) @@ -1603,7 +1603,7 @@ cdef class LinearConstraint(LinearFunctionOrConstraint): 3 <= x_2 """ if not self.is_less_or_equal() or self.is_trivial(): - raise StopIteration + return term_iter = iter(self) lhs = next(term_iter) rhs = next(term_iter) diff --git a/src/sage/numerical/sdp.pyx b/src/sage/numerical/sdp.pyx index 9075d9bbf19..30b40d3081a 100644 --- a/src/sage/numerical/sdp.pyx +++ b/src/sage/numerical/sdp.pyx @@ -740,8 +740,7 @@ cdef class SemidefiniteProgram(SageObject): for l in lists: if isinstance(l, SDPVariable): c = {} - for (k,v) in l.items(): - #c[k] = self._values[v] if self._values.has_key(v) else None + for k, v in l.items(): c[k] = self._backend.get_variable_value(self._variables[v]) val.append(c) elif isinstance(l, list): diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index d782fcfcc3d..77cdeb8c468 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -3313,7 +3313,6 @@ def list_plot_semilogy(data, plotjoined=False, **kwds): sage: list_plot_semilogy(list(zip(xl[1:],yl[1:]))) Graphics object consisting of 1 graphics primitive - :: sage: list_plot_semilogy([2, 4, 6, 8, 16, 31], base=2) # with base 2 @@ -3327,6 +3326,7 @@ def list_plot_semilogy(data, plotjoined=False, **kwds): """ return list_plot(data, plotjoined=plotjoined, scale='semilogy', **kwds) + def to_float_list(v): """ Given a list or tuple or iterable v, coerce each element of v to a diff --git a/src/sage/plot/plot3d/list_plot3d.py b/src/sage/plot/plot3d/list_plot3d.py index b1000db3c17..c0c578b99f6 100644 --- a/src/sage/plot/plot3d/list_plot3d.py +++ b/src/sage/plot/plot3d/list_plot3d.py @@ -212,6 +212,7 @@ def list_plot3d(v, interpolation_type='default', texture="automatic", point_list return list_plot3d_array_of_arrays(v, interpolation_type, texture, **kwds) raise TypeError("v must be a matrix or list") + def list_plot3d_matrix(m, texture, **kwds): """ A 3-dimensional plot of a surface defined by a matrix ``M`` @@ -248,6 +249,23 @@ def list_plot3d_matrix(m, texture, **kwds): Graphics3d Object sage: list_plot3d(m, texture='yellow', interpolation_type='linear') # indirect doctest Graphics3d Object + + Here is a colored example, using a colormap and a coloring function + which must take values in (0, 1):: + + sage: cm = colormaps.rainbow + sage: n = 20 + sage: cf = lambda x, y: ((2*(x-y)/n)**2) % 1 + sage: list_plot3d(matrix(RDF, n, [cos(pi*(i+j)/n) for i in [1..n] + ....: for j in [1..n]]), color=(cf,cm)) + Graphics3d Object + + .. PLOT:: + + cm = colormaps.rainbow + cf = lambda x, y: ((2*(x-y)/20)**2) % 1 + expl = list_plot3d(matrix(RDF,20,20,[cos(pi*(i+j)/20) for i in range(1,21) for j in range(1,21)]),color=(cf,cm)) + sphinx_plot(expl) """ from .parametric_surface import ParametricSurface f = lambda i,j: (i, j, float(m[int(i), int(j)])) @@ -256,6 +274,7 @@ def list_plot3d_matrix(m, texture, **kwds): G._set_extra_kwds(kwds) return G + def list_plot3d_array_of_arrays(v, interpolation_type, texture, **kwds): """ A 3-dimensional plot of a surface defined by a list of lists ``v`` diff --git a/src/sage/plot/plot3d/platonic.py b/src/sage/plot/plot3d/platonic.py index 4744ee3abe2..c15fdc3af32 100644 --- a/src/sage/plot/plot3d/platonic.py +++ b/src/sage/plot/plot3d/platonic.py @@ -222,7 +222,7 @@ def tetrahedron(center=(0, 0, 0), size=1, **kwds): sphinx_plot(tetrahedron(color='red') + tetrahedron((0,0,-2)).scale([1,1,-1])) - A Dodecahedral complex of 5 tetrahedrons (a more elaborate example + A Dodecahedral complex of 5 tetrahedra (a more elaborate example from Peter Jipsen):: sage: v=(sqrt(5.)/2-5/6, 5/6*sqrt(3.)-sqrt(15.)/2, sqrt(5.)/3) @@ -579,7 +579,7 @@ def icosahedron(center=(0, 0, 0), size=1, **kwds): sphinx_plot(icosahedron()) - Two icosahedrons at different positions of different sizes. :: + Two icosahedra at different positions of different sizes. :: sage: p = icosahedron((-1/2,0,1), color='orange') sage: p += icosahedron((2,0,1), size=1/2, color='red', aspect_ratio=[1,1,1]) diff --git a/src/sage/plot/plot3d/shapes2.py b/src/sage/plot/plot3d/shapes2.py index 1dde57433ae..f42658d1dfa 100644 --- a/src/sage/plot/plot3d/shapes2.py +++ b/src/sage/plot/plot3d/shapes2.py @@ -93,7 +93,7 @@ def line3d(points, thickness=1, radius=None, arrow_head=False, **kwds): ....: color='green') + line3d([(0,1,0), (1,0,2)]) Graphics3d Object - A Dodecahedral complex of 5 tetrahedrons (a more elaborate example + A Dodecahedral complex of 5 tetrahedra (a more elaborate example from Peter Jipsen):: sage: def tetra(col): diff --git a/src/sage/plot/plot3d/tachyon.py b/src/sage/plot/plot3d/tachyon.py index cdee141efa1..81ef81e7a61 100644 --- a/src/sage/plot/plot3d/tachyon.py +++ b/src/sage/plot/plot3d/tachyon.py @@ -698,7 +698,7 @@ def texfunc(self, type=0, center=(0,0,0), rotate=(0,0,0), scale=(1,1,1), - ``scale`` - (default: (1,1,1)) - EXAMPLES: We draw an infinite checkboard:: + EXAMPLES: We draw an infinite checkerboard:: sage: t = Tachyon(camera_center=(2,7,4), look_at=(2,0,0)) sage: t.texture('black', color=(0,0,0), texfunc=1) diff --git a/src/sage/quivers/morphism.py b/src/sage/quivers/morphism.py index b6cd04e27bd..18232b2413f 100644 --- a/src/sage/quivers/morphism.py +++ b/src/sage/quivers/morphism.py @@ -515,6 +515,25 @@ def __eq__(self, other): # If all that holds just check the vectors return self._vector == other._vector + def __hash__(self): + """ + Return the hash of ``self``. + + EXAMPLES:: + + sage: Q = DiGraph({1:{2:['a', 'b']}, 2:{3:['c']}}).path_semigroup() + sage: spaces = {1: QQ^2, 2: QQ^2, 3:QQ^1} + sage: maps = {(1, 2, 'a'): [[1, 0], [0, 0]], (1, 2, 'b'): [[0, 0], [0, 1]], (2, 3, 'c'): [[1], [1]]} + sage: M = Q.representation(QQ, spaces, maps) + sage: spaces2 = {2: QQ^1, 3: QQ^1} + sage: S = Q.representation(QQ, spaces2) + sage: x = M({2: (1, -1)}) + sage: y = M({3: (1,)}) + sage: g = S.hom([x, y], M) + sage: H = hash(g) + """ + return hash(tuple(self._vector)) + def __ne__(self, other): """ This function overrides the ``!=`` operator diff --git a/src/sage/repl/display/jsmol_iframe.py b/src/sage/repl/display/jsmol_iframe.py index 48831ca6611..b664bc6897b 100644 --- a/src/sage/repl/display/jsmol_iframe.py +++ b/src/sage/repl/display/jsmol_iframe.py @@ -155,13 +155,16 @@ def script(self): if line.startswith('pmesh'): command, obj, meshfile = line.split(' ', 3) assert command == 'pmesh' - assert meshfile.startswith('"') and meshfile.endswith('"\n') - meshfile = meshfile[1:-2] # strip quotes - script += [ - 'pmesh {0} inline "'.format(obj), - self._zip.open(meshfile).read(), - '"\n' - ] + if meshfile not in ['dots\n', 'mesh\n']: + assert meshfile.startswith('"') and meshfile.endswith('"\n') + meshfile = meshfile[1:-2] # strip quotes + script += [ + 'pmesh {0} inline "'.format(obj), + self._zip.open(meshfile).read(), + '"\n' + ] + else: + script += [line] else: script += [line] return ''.join(script) diff --git a/src/sage/repl/load.py b/src/sage/repl/load.py index e54e14cf005..e6028426aef 100644 --- a/src/sage/repl/load.py +++ b/src/sage/repl/load.py @@ -98,9 +98,9 @@ def load(filename, globals, attach=False): sage: t = tmp_filename(ext='.py') sage: _ = open(t,'w').write("print 'hi', 2/3; z = -2/7") sage: z = 1 - sage: sage.repl.load.load(t, globals()) # optional - python2 + sage: sage.repl.load.load(t, globals()) # py2 hi 0 - sage: z # optional - python2 + sage: z # py2 -1 A ``.sage`` file *is* preparsed:: diff --git a/src/sage/rings/asymptotic/growth_group.py b/src/sage/rings/asymptotic/growth_group.py index acddb7a7e60..44ab16e2d1a 100644 --- a/src/sage/rings/asymptotic/growth_group.py +++ b/src/sage/rings/asymptotic/growth_group.py @@ -1463,9 +1463,6 @@ def __classcall__(cls, base, var=None, category=None, ignore_variables=None): sage: P3 = MonomialGrowthGroup(ZZ, SR.var('x')) sage: P1 is P2 and P2 is P3 True - sage: P4 = MonomialGrowthGroup(ZZ, buffer('xylophone', 0, 1)) - sage: P1 is P4 - True sage: P5 = MonomialGrowthGroup(ZZ, 'x ') sage: P1 is P5 True diff --git a/src/sage/rings/complex_mpc.pxd b/src/sage/rings/complex_mpc.pxd index 11e1c162b59..762c38afbdc 100644 --- a/src/sage/rings/complex_mpc.pxd +++ b/src/sage/rings/complex_mpc.pxd @@ -1,4 +1,4 @@ -from sage.libs.mpc cimport mpc_t, mpc_rnd_t +from sage.libs.mpc.types cimport mpc_t, mpc_rnd_t cimport sage.structure.element cimport sage.rings.ring diff --git a/src/sage/rings/complex_mpc.pyx b/src/sage/rings/complex_mpc.pyx index 824fe55a382..014c7efdf6d 100644 --- a/src/sage/rings/complex_mpc.pyx +++ b/src/sage/rings/complex_mpc.pyx @@ -2142,7 +2142,7 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): cdef RealNumber a,r a = self.argument()/n r = self.abs() - mpfr_root(r.value, r.value, n, rrnd) + mpfr_rootn_ui(r.value, r.value, n, rrnd) cdef MPComplexNumber z z = self._new() @@ -2297,7 +2297,7 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): def agm(self, right, algorithm="optimal"): """ - Returns the algebraic geometrc mean of ``self`` and ``right``. + Return the algebro-geometric mean of ``self`` and ``right``. EXAMPLES:: diff --git a/src/sage/rings/complex_number.pyx b/src/sage/rings/complex_number.pyx index 16604510037..6df884420a0 100644 --- a/src/sage/rings/complex_number.pyx +++ b/src/sage/rings/complex_number.pyx @@ -2217,7 +2217,7 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): rho = abs(self) arg = self.argument() / n mpfr_init2(r, self._prec) - mpfr_root(r, rho.value, n, rnd) + mpfr_rootn_ui(r, rho.value, n, rnd) mpfr_sin_cos(z.__im, z.__re, arg.value, rnd) mpfr_mul(z.__re, z.__re, r, rnd) diff --git a/src/sage/rings/finite_rings/element_givaro.pyx b/src/sage/rings/finite_rings/element_givaro.pyx index 90e2aabc69a..1c2d5facece 100644 --- a/src/sage/rings/finite_rings/element_givaro.pyx +++ b/src/sage/rings/finite_rings/element_givaro.pyx @@ -918,7 +918,7 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement): def _element(self): """ - Returns the int interally representing this element. + Return the int internally representing this element. EXAMPLES:: diff --git a/src/sage/rings/fraction_field.py b/src/sage/rings/fraction_field.py index fca808361c8..67e72a18e73 100644 --- a/src/sage/rings/fraction_field.py +++ b/src/sage/rings/fraction_field.py @@ -574,6 +574,17 @@ def _element_constructor_(self, x, y=None, coerce=True): sage: R. = PolynomialRing(B,'x') sage: (a*d*x^2+a+e+1).resultant(-4*c^2*x+1) a*d + 16*c^4*e + 16*a*c^4 + 16*c^4 + + Check that :trac:`24539` is fixed:: + + sage: tau = polygen(QQ, 'tau') + sage: R = PolynomialRing(CyclotomicField(2), 'z').fraction_field()( + ....: tau/(1+tau)) + Traceback (most recent call last): + ... + TypeError: cannot convert tau/(tau + 1)/1 to an element of Fraction + Field of Univariate Polynomial Ring in z over Cyclotomic Field of + order 2 and degree 1 """ if y is None: if isinstance(x, Element) and x.parent() is self: @@ -619,8 +630,8 @@ def _element_constructor_(self, x, y=None, coerce=True): x = x0.numerator()*y0.denominator() y = y0.numerator()*x0.denominator() except AttributeError: - raise TypeError("cannot convert {!r}/{!r} to an element of {}", - x0, y0, self) + raise TypeError("cannot convert {!r}/{!r} to an element of {}".format( + x0, y0, self)) try: return self._element_class(self, x, y, coerce=coerce) except TypeError: @@ -933,7 +944,7 @@ class FractionFieldEmbedding(DefaultConvertMap_unique): Coercion map: From: Univariate Polynomial Ring in x over Rational Field To: Fraction Field of Univariate Polynomial Ring in x over Rational Field - + TESTS:: sage: from sage.rings.fraction_field import FractionFieldEmbedding @@ -1008,7 +1019,7 @@ def _richcmp_(self, other, op): sage: f = R.fraction_field().coerce_map_from(R) sage: S. = GF(2)[] sage: g = S.fraction_field().coerce_map_from(S) - + sage: f == g # indirect doctest False sage: f == f @@ -1045,7 +1056,7 @@ class FractionFieldEmbeddingSection(Section): Section map: From: Fraction Field of Univariate Polynomial Ring in x over Rational Field To: Univariate Polynomial Ring in x over Rational Field - + TESTS:: sage: from sage.rings.fraction_field import FractionFieldEmbeddingSection @@ -1123,7 +1134,7 @@ def _richcmp_(self, other, op): sage: f = R.fraction_field().coerce_map_from(R).section() sage: S. = GF(2)[] sage: g = S.fraction_field().coerce_map_from(S).section() - + sage: f == g # indirect doctest False sage: f == f diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index ca90682b5e4..e79308952d0 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -1483,9 +1483,11 @@ def genus(self): sage: L.genus() 3 """ - # unfortunately singular can not compute the genus with the polynomial_ring()._singular_ - # object because genus method only accepts a ring of transdental degree 2 over a prime field - # not a ring of transdental degree 1 over a rational function field of one variable + # unfortunately singular can not compute the genus with the + # polynomial_ring()._singular_ object because genus method + # only accepts a ring of transcendental degree 2 over a prime + # field not a ring of transcendental degree 1 over a rational + # function field of one variable if is_RationalFunctionField(self._base_field) and self._base_field.constant_field().is_prime_field(): diff --git a/src/sage/rings/invariant_theory.py b/src/sage/rings/invariant_theory.py index 63ad78ae3bb..d4d202a4ee8 100644 --- a/src/sage/rings/invariant_theory.py +++ b/src/sage/rings/invariant_theory.py @@ -3318,7 +3318,8 @@ def quaternary_biquadratic(self, quadratic1, quadratic2, *args, **kwds): INPUT: - - ``quadratic1``, ``quadratic2`` -- two polynomias. Either homogeneous quadratic + - ``quadratic1``, ``quadratic2`` -- two polynomials. + Either homogeneous quadratic in 4 homogeneous variables, or inhomogeneous quadratic in 3 variables. diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index bc059af3404..3959e80ba67 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -1913,7 +1913,7 @@ cdef class FrobeniusEndomorphism_generic(RingHomomorphism): raise TypeError("The base ring must be a commutative ring") self._p = domain.characteristic() if not self._p.is_prime(): - raise TypeError("The caracteristic of the base ring must be prime") + raise TypeError("the characteristic of the base ring must be prime") try: n = Integer(n) except TypeError: diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 5d782490360..85064c88916 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -120,6 +120,7 @@ from sage.misc.misc_c import prod from sage.categories.homset import End from sage.rings.all import Infinity +from sage.categories.number_fields import NumberFields import sage.rings.ring from sage.misc.latex import latex_variable_name @@ -143,6 +144,7 @@ from builtins import zip from sage.misc.superseded import deprecated_function_alias +_NumberFields = NumberFields() def is_NumberFieldHomsetCodomain(codomain): """ @@ -521,6 +523,18 @@ def NumberField(polynomial, name=None, check=True, names=None, embedding=None, l ... TypeError: You must specify the name of the generator. + Check that we can construct morphisms to matrix space (:trac:`23418`):: + + sage: t = polygen(QQ) + sage: K = NumberField(t^4 - 2, 'a') + sage: K.hom([K.gen().matrix()]) + Ring morphism: + From: Number Field in a with defining polynomial x^4 - 2 + To: Full MatrixSpace of 4 by 4 dense matrices over Rational Field + Defn: a |--> [0 1 0 0] + [0 0 1 0] + [0 0 0 1] + [2 0 0 0] """ if names is not None: name = names @@ -1202,7 +1216,7 @@ class NumberField_generic(WithEqualityById, number_field_base.NumberField): sage: NumberField(QQ['x'].0^4 + 23, 'a') == NumberField(QQ['y'].0^4 + 23, 'a') False - sage: x = var('x'); y = ZZ['y'].gen() + sage: x = polygen(QQ); y = ZZ['y'].gen() sage: NumberField(x^3 + x + 5, 'a') == NumberField(y^3 + y + 5, 'a') False sage: NumberField(x^3 + x + 5, 'a') == NumberField(y^4 + y + 5, 'a') @@ -1328,8 +1342,7 @@ def __init__(self, polynomial, name, latex_name, self._assume_disc_small = assume_disc_small self._maximize_at_primes = maximize_at_primes self._structure = structure - from sage.categories.number_fields import NumberFields - default_category = NumberFields() + default_category = _NumberFields if category is None: category = default_category else: @@ -3470,8 +3483,8 @@ def primes_of_bounded_norm_iter(self, B): except (TypeError, AttributeError): raise TypeError("%s is not valid bound on prime ideals" % B) - if B<2: - raise StopIteration + if B < 2: + return if self is QQ: for p in arith.primes(B+1): @@ -5050,7 +5063,7 @@ def extension(self, poly, name=None, names=None, *args, **kwds): sage: k. = NumberField(x^2 + 1); k Number Field in a with defining polynomial x^2 + 1 - sage: y = var('y') + sage: y = polygen(QQ,'y') sage: m. = k.extension(y^2 + 2); m Number Field in b with defining polynomial y^2 + 2 over its base field @@ -5589,45 +5602,40 @@ def reduced_basis(self, prec=None): [-1, -1/2*t^5 + 1/2*t^4 + 3*t^3 - 3/2*t^2 - 4*t - 1/2, t, 1/2*t^5 + 1/2*t^4 - 4*t^3 - 5/2*t^2 + 7*t + 1/2, 1/2*t^5 - 1/2*t^4 - 2*t^3 + 3/2*t^2 - 1/2, 1/2*t^5 - 1/2*t^4 - 3*t^3 + 5/2*t^2 + 4*t - 5/2] sage: CyclotomicField(12).reduced_basis() [1, zeta12^2, zeta12, zeta12^3] - """ - if self.is_totally_real(): - try: - return self.__reduced_basis - except AttributeError: - pass - else: - try: - if self.__reduced_basis_precision >= prec: - return self.__reduced_basis - except AttributeError: - pass - from sage.matrix.constructor import matrix + TESTS: + Check that the bug reported at :trac:`10017` is fixed:: + + sage: x = polygen(QQ) + sage: k. = NumberField(x^6 + 2218926655879913714112*x^4 - 32507675650290949030789018433536*x^3 + 4923635504174417014460581055002374467948544*x^2 - 36066074010564497464129951249279114076897746988630016*x + 264187244046129768986806800244258952598300346857154900812365824) + sage: new_basis = k.reduced_basis(prec=120) + sage: [c.minpoly() for c in new_basis] + [x - 1, + x^2 - x + 1, + x^6 + 3*x^5 - 102*x^4 - 103*x^3 + 10572*x^2 - 59919*x + 127657, + x^6 - 3*x^5 - 102*x^4 + 315*x^3 + 10254*x^2 - 80955*x + 198147, + x^3 - 171*x + 848, + x^6 + 171*x^4 + 1696*x^3 + 29241*x^2 + 145008*x + 719104] + sage: R = k.order(new_basis) + sage: R.discriminant()==k.discriminant() + True + """ + ZK = self.integral_basis() d = self.absolute_degree() - Z_basis = self.integral_basis() - ## If self is totally real, then we can use (x*y).trace() as - ## the inner product on the Minkowski embedding, which is - ## faster than computing all the conjugates, etc ... + # If self is totally real, then we can use (x*y).trace() as + # the inner product on the Minkowski embedding, which is + # faster than computing all the conjugates, etc ... + if self.is_totally_real(): - T = pari(matrix(ZZ, d, d, [[(x*y).trace() for x in Z_basis] - for y in Z_basis])).qflllgram() - self.__reduced_basis = [ sum([ ZZ(T[i][j]) * Z_basis[j] - for j in range(d)]) - for i in range(d)] + from sage.matrix.constructor import matrix + T = pari(matrix(ZZ, d, d, [[(x*y).trace() for x in ZK] for y in ZK])).qflllgram() else: - M = self.minkowski_embedding(self.integral_basis(), prec=prec) - T = pari(M).qflll().sage() - self.__reduced_basis = [ self(v.list()) for v in T.columns() ] - if prec is None: - ## this is the default choice for minkowski_embedding - self.__reduced_basis_prec = 53 - else: - self.__reduced_basis_prec = prec - - return self.__reduced_basis + M = self.minkowski_embedding(ZK, prec=prec) + T = pari(M).qflll() + return [ sum([ ZZ(T[i][j]) * ZK[j] for j in range(d)]) for i in range(d)] def reduced_gram_matrix(self, prec=None): r""" @@ -5684,14 +5692,13 @@ def reduced_gram_matrix(self, prec=None): :: - sage: var('x') - x + sage: x = polygen(QQ) sage: F. = NumberField(x^4+x^2+712312*x+131001238) sage: F.reduced_gram_matrix(prec=128) - [ 4.0000000000000000000000000000000000000 0.00000000000000000000000000000000000000 -2.1369360000000000000000000000000000000e6 -3.3122478000000000000000000000000000000e7] - [ 0.00000000000000000000000000000000000000 46721.539331563218381658483353092335550 -2.2467769057394530109094755223395819322e7 -3.4807276041138450473611629088647496430e8] - [-2.1369360000000000000000000000000000000e6 -2.2467769057394530109094755223395819322e7 7.0704285924714907491782135494859351061e12 1.1256639928034037006027526953641297995e14] - [-3.3122478000000000000000000000000000000e7 -3.4807276041138450473611629088647496430e8 1.1256639928034037006027526953641297995e14 1.7923838231014970520503146603069479547e15] + [ 4.0000000000000000000000000000000000000 0.00000000000000000000000000000000000000 -1.9999999999999999999999999999999999037 -0.99999999999999999999999999999999383702] + [ 0.00000000000000000000000000000000000000 46721.539331563218381658483353092335550 -11488.910026551724275122749703614966768 -418.12718083977141198754424579680468382] + [ -1.9999999999999999999999999999999999037 -11488.910026551724275122749703614966768 5.5658915310500611768713076521847709187e8 1.4179092271494070050433368847682152174e8] + [ -0.99999999999999999999999999999999383702 -418.12718083977141198754424579680468382 1.4179092271494070050433368847682152174e8 1.3665897267919181137884111201405279175e12] """ if self.is_totally_real(): try: diff --git a/src/sage/rings/number_field/order.py b/src/sage/rings/number_field/order.py index dcbe495df62..d717eb7618d 100644 --- a/src/sage/rings/number_field/order.py +++ b/src/sage/rings/number_field/order.py @@ -1129,6 +1129,7 @@ def _element_constructor_(self, x): EXAMPLES:: + sage: x = polygen(QQ) sage: k. = NumberField(x^2 - 389) sage: m = k.order(3*z); m Order in Number Field in z with defining polynomial x^2 - 389 @@ -1136,9 +1137,26 @@ def _element_constructor_(self, x): 6*z sage: k(m(6*z)) 6*z + + If ``x`` is a list or tuple the element constructed is the + linear combination of the generators with these coefficients + (see :trac:`10017`):: + + sage: x = polygen(QQ) + sage: K. = NumberField(x^3-10) + sage: ZK = K.ring_of_integers() + sage: ZK.basis() + [1/3*a^2 + 1/3*a + 1/3, a, a^2] + sage: ZK([1,2,3]) + 10/3*a^2 + 7/3*a + 1/3 + sage: K([1,2,3]) + 3*a^2 + 2*a + 1 + """ if is_Element(x) and x.parent() is self: return x + if isinstance(x, (tuple, list)): + x = sum(xi*gi for xi,gi in zip(x,self.gens())) if not is_Element(x) or x.parent() is not self._K: x = self._K(x) V, _, embedding = self._K.vector_space() diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index 334e544bba2..fa5277899d7 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -852,7 +852,7 @@ cdef class pAdicGenericElement(LocalGenericElement): their greatest common divisor is in general not unique (not even up to units). For example `O(3)` is a representative for the elements 0 and 3 in the 3-adic ring `\ZZ_3`. The greatest common - divisior of `O(3)` and `O(3)` could be (among others) 3 or 0 which + divisor of `O(3)` and `O(3)` could be (among others) 3 or 0 which have different valuation. The algorithm implemented here, will return an element of minimal valuation among the possible greatest common divisors. @@ -1005,7 +1005,7 @@ cdef class pAdicGenericElement(LocalGenericElement): greatest common divisor is in general not unique (not even up to units). For example `O(3)` is a representative for the elements 0 and 3 in the 3-adic ring `\ZZ_3`. The greatest common - divisior of `O(3)` and `O(3)` could be (among others) 3 or 0 which + divisor of `O(3)` and `O(3)` could be (among others) 3 or 0 which have different valuation. The algorithm implemented here, will return an element of minimal valuation among the possible greatest common divisors. diff --git a/src/sage/rings/polynomial/pbori.pyx b/src/sage/rings/polynomial/pbori.pyx index 318ff62af75..89bf06a18d8 100644 --- a/src/sage/rings/polynomial/pbori.pyx +++ b/src/sage/rings/polynomial/pbori.pyx @@ -3144,7 +3144,6 @@ cdef class BooleanPolynomial(MPolynomial): sage: x != False True """ - from builtins import zip for lm, rm in zip(left, right): if lm != rm: return richcmp_not_equal(lm, rm, op) @@ -6416,7 +6415,7 @@ cdef class ReductionStrategy: - ``leading_terms`` - all leading terms of generators - - ``minimial_leading_terms`` - the reduced set of leading terms + - ``minimal_leading_terms`` - the reduced set of leading terms - ``monomials`` - diff --git a/src/sage/rings/polynomial/polydict.pyx b/src/sage/rings/polynomial/polydict.pyx index 3ce87c8862b..b045e6bc5e5 100644 --- a/src/sage/rings/polynomial/polydict.pyx +++ b/src/sage/rings/polynomial/polydict.pyx @@ -303,7 +303,7 @@ cdef class PolyDict: return min(_min) def total_degree(PolyDict self): - return max([-1] + map(sum, self.__repn.keys())) + return max([-1] + [sum(k) for k in self.__repn.keys()]) def monomial_coefficient(PolyDict self, mon): """ diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index afa54f54ac0..dd420918180 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -73,7 +73,7 @@ from sage.misc.abstract_method import abstract_method from sage.misc.latex import latex from sage.arith.long cimport pyobject_to_long from sage.structure.factorization import Factorization -from sage.structure.richcmp cimport (richcmp, richcmp_not_equal, +from sage.structure.richcmp cimport (richcmp, richcmp_item, rich_to_bool, rich_to_bool_sgn) from sage.interfaces.singular import singular as singular_default, is_SingularElement @@ -964,13 +964,49 @@ cdef class Polynomial(CommutativeAlgebraElement): False sage: R(0) == R(0) True + + TESTS:: + + Test that comparisons are consistent when using interval + coefficients:: + + sage: R. = RIF[] + sage: a = RIF(0,1) * x + sage: b = RIF(1,2) * x + sage: a == a + False + sage: a != a + False + sage: a == b + False + sage: a < b + False + sage: a > b + False + sage: a <= b + True + sage: a >= b + False + sage: a != b + False + + For ``RBF``, identical elements are considered equal:: + + sage: R. = RBF[] + sage: pol = RBF(1.0, 0.1) + sage: pol == pol + True + sage: pol == copy(pol) + False """ + cdef Polynomial pol = other + cdef Py_ssize_t d1 = self.degree() - cdef Py_ssize_t d2 = other.degree() + cdef Py_ssize_t d2 = pol.degree() # Special case constant polynomials if d1 <= 0 and d2 <= 0: - return richcmp(self[0], other[0], op) + return richcmp(self[0], pol[0], op) # For different degrees, compare the degree if d1 != d2: @@ -979,9 +1015,10 @@ cdef class Polynomial(CommutativeAlgebraElement): cdef Py_ssize_t i for i in reversed(range(d1+1)): x = self.get_unsafe(i) - y = other[i] - if x != y: - return richcmp_not_equal(x, y, op) + y = pol.get_unsafe(i) + res = richcmp_item(x, y, op) + if res is not NotImplemented: + return res return rich_to_bool(op, 0) def __nonzero__(self): @@ -9451,7 +9488,7 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: f.homogenize('x') 3*x^2 - In positive characterstic, the degree can drop in this case:: + In positive characteristic, the degree can drop in this case:: sage: R. = GF(2)[] sage: f = x + 1 diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index a5e76ea7f10..e71d85309ca 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -39,7 +39,7 @@ from sage.rings.polynomial.polynomial_singular_interface import Polynomial_singular_repr from sage.libs.pari.all import pari_gen -from sage.structure.richcmp import richcmp, richcmp_not_equal, rich_to_bool, rich_to_bool_sgn +from sage.structure.richcmp import richcmp, richcmp_item, rich_to_bool, rich_to_bool_sgn from sage.structure.element import coerce_binop from sage.rings.infinity import infinity, Infinity @@ -687,8 +687,9 @@ def _richcmp_(self, other, op): for i in sorted(degs, reverse=True): x = self[i] y = other[i] - if x != y: - return richcmp_not_equal(x, y, op) + res = richcmp_item(x, y, op) + if res is not NotImplemented: + return res return rich_to_bool(op, 0) def shift(self, n): diff --git a/src/sage/rings/power_series_pari.pyx b/src/sage/rings/power_series_pari.pyx index 5a242b1a5fe..e37e0a37186 100644 --- a/src/sage/rings/power_series_pari.pyx +++ b/src/sage/rings/power_series_pari.pyx @@ -400,8 +400,8 @@ cdef class PowerSeries_pari(PowerSeries): """ if len(kwds) >= 1: name = self._parent.variable_name() - if kwds.has_key(name): # the series variable is specified by a keyword - if len(x) > 0: + if name in kwds: # the series variable is specified by a keyword + if len(x): raise ValueError("must not specify %s keyword and positional argument" % name) x = [kwds[name]] del kwds[name] diff --git a/src/sage/rings/rational_field.py b/src/sage/rings/rational_field.py index 64be36a52bc..3e66bf4df67 100644 --- a/src/sage/rings/rational_field.py +++ b/src/sage/rings/rational_field.py @@ -39,6 +39,8 @@ True sage: RationalField() is RationalField() True + sage: Q in Fields().Infinite() + True AUTHORS: @@ -531,8 +533,8 @@ def primes_of_bounded_norm_iter(self, B): except (TypeError, AttributeError): raise TypeError("%s is not valid bound on prime ideals" % B) - if B<2: - raise StopIteration + if B < 2: + return from sage.arith.all import primes for p in primes(B+1): diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index df9d736c5a4..d892c0985aa 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -190,16 +190,16 @@ def mpfr_prec_min(): sage: from sage.rings.real_mpfr import mpfr_prec_min sage: mpfr_prec_min() - 2 + 1 sage: R = RealField(2) sage: R(2) + R(1) 3.0 sage: R(4) + R(1) 4.0 - sage: R = RealField(1) + sage: R = RealField(0) Traceback (most recent call last): ... - ValueError: prec (=1) must be >= 2 and <= 2147483391 + ValueError: prec (=0) must be >= 1 and <= 2147483391 """ return MPFR_PREC_MIN @@ -218,7 +218,7 @@ def mpfr_prec_max(): sage: R = RealField(2^31-256) Traceback (most recent call last): ... - ValueError: prec (=2147483392) must be >= 2 and <= 2147483391 + ValueError: prec (=2147483392) must be >= 1 and <= 2147483391 """ global MY_MPFR_PREC_MAX return MY_MPFR_PREC_MAX @@ -353,7 +353,7 @@ mpfr_set_exp_max(mpfr_get_emax_max()) from sage.arith.long cimport pyobject_to_long cdef dict rounding_modes = dict(RNDN=MPFR_RNDN, RNDZ=MPFR_RNDZ, - RNDD=MPFR_RNDD, RNDU=MPFR_RNDU, RNDA=MPFR_RNDA) + RNDD=MPFR_RNDD, RNDU=MPFR_RNDU, RNDA=MPFR_RNDA, RNDF=MPFR_RNDF) cdef double LOG_TEN_TWO_PLUS_EPSILON = 3.321928094887363 # a small overestimate of log(10,2) @@ -384,6 +384,8 @@ cpdef RealField(int prec=53, int sci_not=0, rnd=MPFR_RNDN): - ``'RNDZ'`` -- round towards zero - ``'RNDU'`` -- round towards plus infinity - ``'RNDA'`` -- round away from zero + - ``'RNDF'`` -- faithful rounding (currently experimental; not + guaranteed correct for every operation) - for specialized applications, the rounding mode can also be given as an integer value of type ``mpfr_rnd_t``. However, the exact values are unspecified. @@ -472,6 +474,8 @@ cdef class RealField_class(sage.rings.ring.Field): Real Field with 100 bits of precision and rounding RNDD sage: RealField(100, rnd="RNDA") Real Field with 100 bits of precision and rounding RNDA + sage: RealField(100, rnd="RNDF") + Real Field with 100 bits of precision and rounding RNDF sage: RealField(100, rnd=0) Real Field with 100 bits of precision sage: RealField(100, rnd=1) @@ -482,14 +486,16 @@ cdef class RealField_class(sage.rings.ring.Field): Real Field with 100 bits of precision and rounding RNDD sage: RealField(100, rnd=4) Real Field with 100 bits of precision and rounding RNDA + sage: RealField(100, rnd=5) + Real Field with 100 bits of precision and rounding RNDF sage: RealField(100, rnd=3.14) Traceback (most recent call last): ... - ValueError: rounding mode (=3.14000000000000) must be one of ['RNDA', 'RNDZ', 'RNDD', 'RNDU', 'RNDN'] - sage: RealField(100, rnd=5) + ValueError: rounding mode (=3.14000000000000) must be one of ['RNDA', 'RNDD', 'RNDF', 'RNDN', 'RNDU', 'RNDZ'] + sage: RealField(100, rnd=6) Traceback (most recent call last): ... - ValueError: unknown rounding mode 5 + ValueError: unknown rounding mode 6 sage: RealField(100, rnd=10^100) Traceback (most recent call last): ... @@ -4419,7 +4425,7 @@ cdef class RealNumber(sage.structure.element.RingElement): sage: r = -1.0 sage: r.eint() - NaN + -0.219383934395520 """ cdef RealNumber x = self._new() if (self._parent).__prec > SIG_PREC_THRESHOLD: sig_on() @@ -5275,7 +5281,7 @@ cdef class RealNumber(sage.structure.element.RingElement): if algorithm == 1: x = self._new() sig_on() - mpfr_root(x.value, self.value, n, (self._parent).rnd) + mpfr_rootn_ui(x.value, self.value, n, (self._parent).rnd) sig_off() return x diff --git a/src/sage/schemes/elliptic_curves/ell_number_field.py b/src/sage/schemes/elliptic_curves/ell_number_field.py index 022cf77ccae..9764498d6d9 100644 --- a/src/sage/schemes/elliptic_curves/ell_number_field.py +++ b/src/sage/schemes/elliptic_curves/ell_number_field.py @@ -2628,7 +2628,7 @@ def isogeny_class(self): The array of isogenies themselves is not filled out but only contains those used to construct the class, the other entries - containing the interger 0. This will be changed when the + containing the integer 0. This will be changed when the class :class:`EllipticCurveIsogeny` allowed composition. In this case we used `2`-isogenies to go from 0 to 2 and from 1 to 3, and `3`-isogenies to go from 0 to 1 and from 2 to 3:: diff --git a/src/sage/schemes/elliptic_curves/gal_reps_number_field.py b/src/sage/schemes/elliptic_curves/gal_reps_number_field.py index 9004ce09745..ca268e1dd34 100644 --- a/src/sage/schemes/elliptic_curves/gal_reps_number_field.py +++ b/src/sage/schemes/elliptic_curves/gal_reps_number_field.py @@ -532,7 +532,7 @@ def _maybe_borels(E, L, patience=100): sage: sage.schemes.elliptic_curves.gal_reps_number_field._maybe_borels(E, primes(20)) [2, 3] - Here the curve really does possess isognies of degrees 2 and 3:: + Here the curve really does possess isogenies of degrees 2 and 3:: sage: [len(E.isogenies_prime_degree(l)) for l in [2,3]] [1, 1] diff --git a/src/sage/schemes/elliptic_curves/height.py b/src/sage/schemes/elliptic_curves/height.py index 10669a2794f..3d270ebf9d7 100644 --- a/src/sage/schemes/elliptic_curves/height.py +++ b/src/sage/schemes/elliptic_curves/height.py @@ -535,7 +535,7 @@ def inf_max_abs(f, g, D): INPUT: - - ``f``, ``g`` (polynomials) -- real univariate polynomaials + - ``f``, ``g`` (polynomials) -- real univariate polynomials - ``D`` (UnionOfIntervals) -- a subset of `\RR` diff --git a/src/sage/schemes/elliptic_curves/isogeny_class.py b/src/sage/schemes/elliptic_curves/isogeny_class.py index ffe6553fe88..677d5b74ff6 100644 --- a/src/sage/schemes/elliptic_curves/isogeny_class.py +++ b/src/sage/schemes/elliptic_curves/isogeny_class.py @@ -619,7 +619,7 @@ def __init__(self, E): The array of isogenies themselves is not filled out but only contains those used to construct the class, the other entries - containing the interger 0. This will be changed when the + containing the integer 0. This will be changed when the class :class:`EllipticCurveIsogeny` allowed composition. In this case we used `2`-isogenies to go from 0 to 2 and from 1 to 3, and `3`-isogenies to go from 0 to 1 and from 2 to 3:: diff --git a/src/sage/schemes/hyperelliptic_curves/mestre.py b/src/sage/schemes/hyperelliptic_curves/mestre.py index 6107c01c562..e916364e38d 100644 --- a/src/sage/schemes/hyperelliptic_curves/mestre.py +++ b/src/sage/schemes/hyperelliptic_curves/mestre.py @@ -108,7 +108,7 @@ def HyperellipticCurve_from_invariants(i, reduced=True, precision=None, TypeError: F (=0) must have degree 2 - Igusa-Clebsch invariants also only work over fields of charateristic + Igusa-Clebsch invariants also only work over fields of characteristic different from 2, 3, and 5, so another algorithm will be needed for fields of those characteristics. See also :trac:`12200`:: diff --git a/src/sage/schemes/toric/divisor.py b/src/sage/schemes/toric/divisor.py index 6de57b970fc..cb7802a3e57 100644 --- a/src/sage/schemes/toric/divisor.py +++ b/src/sage/schemes/toric/divisor.py @@ -742,7 +742,7 @@ def function_value(self, point): OUTPUT: - - an interger or a rational number. + - an integer or a rational number. EXAMPLES:: diff --git a/src/sage/schemes/toric/points.py b/src/sage/schemes/toric/points.py index 6d83d0af779..8ba5a8d876e 100644 --- a/src/sage/schemes/toric/points.py +++ b/src/sage/schemes/toric/points.py @@ -915,7 +915,7 @@ def solutions(self, inhomogeneous_equations, log_range): if len(log_range) <= 2: for log_t in self.solutions_serial(inhomogeneous_equations, log_range): yield log_t - raise StopIteration + return # Parallelize the outermost loop of the Cartesian product work = [([[r]] + log_range[1:],) for r in log_range[0]] from sage.parallel.decorate import Parallel diff --git a/src/sage/schemes/toric/sheaf/klyachko.py b/src/sage/schemes/toric/sheaf/klyachko.py index fcfe90901c9..572cc3cb9d5 100644 --- a/src/sage/schemes/toric/sheaf/klyachko.py +++ b/src/sage/schemes/toric/sheaf/klyachko.py @@ -948,7 +948,7 @@ def random_deformation(self, epsilon=None): OUTPUT: - A new Klyachko bundle with randomly perturbed moduly. In + A new Klyachko bundle with randomly perturbed moduli. In particular, the same Chern classes. EXAMPLES:: diff --git a/src/sage/sets/recursively_enumerated_set.pyx b/src/sage/sets/recursively_enumerated_set.pyx index d592ec0f7a6..27facd9fc13 100644 --- a/src/sage/sets/recursively_enumerated_set.pyx +++ b/src/sage/sets/recursively_enumerated_set.pyx @@ -281,7 +281,7 @@ def RecursivelyEnumeratedSet(seeds, successors, structure=None, TESTS: - The succesors method is an attribute:: + The successors method is an attribute:: sage: R = RecursivelyEnumeratedSet([1], lambda x: [x+1, x-1]) sage: R.successors(4) @@ -296,7 +296,8 @@ def RecursivelyEnumeratedSet(seeds, successors, structure=None, (1, 2, 3) """ if structure is None: - if enumeration is None: enumeration = 'breadth' + if enumeration is None: + enumeration = 'breadth' return RecursivelyEnumeratedSet_generic(seeds, successors, enumeration, max_depth, facade=facade, category=category) if structure == 'symmetric': diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index af800710aab..405080f1be5 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -3086,7 +3086,7 @@ cdef class CommutativeRingElement(RingElement): if not isinstance(P, IntegralDomain): raise NotImplementedError('sqrt() with all=True is only implemented for integral domains, not for %s' % P) if P.characteristic()==2 or sq_rt==0: - #0 has only one square root, and in charasteristic 2 everything also has only 1 root + #0 has only one square root, and in characteristic 2 everything also has only 1 root return [ sq_rt ] return [ sq_rt, -sq_rt ] return sq_rt diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index 41f7421a12e..19229698f89 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -1853,10 +1853,9 @@ cdef class Parent(sage.structure.category_object.CategoryObject): We check that :trac:`23184` has been resolved:: sage: QQ['x', 'y']._generic_coerce_map(QQ).category_for() - Category of unique factorization domains + Category of infinite unique factorization domains sage: QQ[['x']].coerce_map_from(QQ).category_for() Category of euclidean domains - """ if isinstance(S, type): category = None diff --git a/src/sage/structure/richcmp.pxd b/src/sage/structure/richcmp.pxd index aa6115a1d37..a5730ebbeea 100644 --- a/src/sage/structure/richcmp.pxd +++ b/src/sage/structure/richcmp.pxd @@ -3,6 +3,8 @@ from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE from cpython.object cimport PyObject_RichCompare as richcmp +cpdef richcmp_item(x, y, int op) + cpdef inline richcmp_not_equal(x, y, int op): """ Like ``richcmp(x, y, op)`` but assuming that `x` is not equal to `y`. diff --git a/src/sage/structure/richcmp.pyx b/src/sage/structure/richcmp.pyx index 26015c52ab4..d5de65a7f20 100644 --- a/src/sage/structure/richcmp.pyx +++ b/src/sage/structure/richcmp.pyx @@ -105,6 +105,204 @@ def richcmp(x, y, int op): return PyObject_RichCompare(x, y, op) +cpdef richcmp_item(x, y, int op): + """ + This function is meant to implement lexicographic rich comparison + of sequences (lists, vectors, polynomials, ...). + The inputs ``x`` and ``y`` are corresponding items of such lists + which should compared. + + INPUT: + + - ``x``, ``y`` -- arbitrary Python objects. Typically, these are + ``X[i]`` and ``Y[i]`` for sequences ``X`` and ``Y``. + + - ``op`` -- comparison operator (one of ``op_LT`, ``op_LE``, + ``op_EQ``, ``op_NE``, ``op_GT``, ``op_GE``) + + OUTPUT: + + Assuming that ``x = X[i]`` and ``y = Y[i]``: + + - if the comparison ``X {op} Y`` (where ``op`` is the given + operation) could not be decided yet (i.e. we should compare the + next items in the list): return ``NotImplemented`` + + - otherwise, if the comparison ``X {op} Y`` could be decided: + return ``x {op} y``, which should then also be the result for + ``X {op} Y``. + + .. NOTE:: + + Since ``x {op} y`` cannot return ``NotImplemented``, the two + cases above are mutually exclusive. + + The semantics of the comparison is different from Python lists or + tuples in the case that the order is not total. Assume that ``A`` + and ``B`` are lists whose rich comparison is implemented using + ``richcmp_item`` (as in EXAMPLES below). Then + + - ``A == B`` iff ``A[i] == B[i]`` for all indices `i`. + + - ``A != B`` iff ``A[i] != B[i]`` for some index `i`. + + - ``A < B`` iff ``A[i] < B[i]`` for some index `i` and + for all `j < i`, ``A[j] <= B[j]``. + + - ``A <= B`` iff ``A < B`` or ``A[i] <= B[i]`` for all `i`. + + - ``A > B`` iff ``A[i] > B[i]`` for some index `i` and + for all `j < i`, ``A[j] >= B[j]``. + + - ``A >= B`` iff ``A > B`` or ``A[i] >= B[i]`` for all `i`. + + See below for a detailed description of the exact semantics of + ``richcmp_item`` in general. + + EXAMPLES:: + + sage: from sage.structure.richcmp import * + sage: @richcmp_method + ....: class Listcmp(list): + ....: def __richcmp__(self, other, op): + ....: for i in range(len(self)): # Assume equal lengths + ....: res = richcmp_item(self[i], other[i], op) + ....: if res is not NotImplemented: + ....: return res + ....: return rich_to_bool(op, 0) # Consider the lists to be equal + sage: a = Listcmp([0, 1, 3]) + sage: b = Listcmp([0, 2, 1]) + sage: a == a + True + sage: a != a + False + sage: a < a + False + sage: a <= a + True + sage: a > a + False + sage: a >= a + True + sage: a == b, b == a + (False, False) + sage: a != b, b != a + (True, True) + sage: a < b, b > a + (True, True) + sage: a <= b, b >= a + (True, True) + sage: a > b, b < a + (False, False) + sage: a >= b, b <= a + (False, False) + + The above tests used a list of integers, where the result of + comparisons are the same as for Python lists. + + If we want to see the difference, we need more general entries in + the list. The comparison rules are made to be consistent with + setwise operations. If `A` and `B` are sets, we define ``A {op} B`` + to be true if ``a {op} B`` is true for every `a` in `A` and + `b` in `B`. Interval comparisons are a special case of this. For + lists of non-empty(!) sets, we want that ``[A1, A2] {op} [B1, B2]`` + is true if and only if ``[a1, a2] {op} [b1, b2]`` is true for all + elements. We verify this:: + + sage: @richcmp_method + ....: class Setcmp(tuple): + ....: def __richcmp__(self, other, op): + ....: return all(richcmp(x, y, op) for x in self for y in other) + sage: sym = {op_EQ: "==", op_NE: "!=", op_LT: "<", op_GT: ">", op_LE: "<=", op_GE: ">="} + sage: for A1 in Set(range(4)).subsets(): # long time + ....: if not A1: continue + ....: for B1 in Set(range(4)).subsets(): + ....: if not B1: continue + ....: for A2 in Set(range(4)).subsets(): + ....: if not A2: continue + ....: for B2 in Set(range(3)).subsets(): + ....: if not B2: continue + ....: L1 = Listcmp([Setcmp(A1), Setcmp(A2)]) + ....: L2 = Listcmp([Setcmp(B1), Setcmp(B2)]) + ....: for op in range(6): + ....: reslist = richcmp(L1, L2, op) + ....: reselt = all(richcmp([a1, a2], [b1, b2], op) for a1 in A1 for a2 in A2 for b1 in B1 for b2 in B2) + ....: assert reslist is reselt + + EXACT SEMANTICS: + + Above, we only described how ``richcmp_item`` behaves when it is + used to compare sequences. Here, we specify the exact semantics. + First of all, recall that the result of ``richcmp_item(x, y, op)`` + is either ``NotImplemented`` or ``x {op} y``. + + - if ``op`` is ``==``: return ``NotImplemented`` if ``x == y``. + If ``x == y`` is false, then return ``x == y``. + + - if ``op`` is ``!=``: return ``NotImplemented`` if not ``x != y``. + If ``x != y`` is true, then return ``x != y``. + + - if ``op`` is ``<``: return ``NotImplemented`` if ``x == y``. + If ``x < y`` or not ``x <= y``, return ``x < y``. + Otherwise (if both ``x == y`` and ``x < y`` are false but + ``x <= y`` is true), return ``NotImplemented``. + + - if ``op`` is ``<=``: return ``NotImplemented`` if ``x == y``. + If ``x < y`` or not ``x <= y``, return ``x <= y``. + Otherwise (if both ``x == y`` and ``x < y`` are false but + ``x <= y`` is true), return ``NotImplemented``. + + - the ``>`` and ``>=`` operators are analogous to ``<`` and ``<=``. + """ + if op == Py_NE: + res = (x != y) + if not res: + return NotImplemented + return res # (x != y) --> True + + # If x and y are equal, we cannot decide + res = (x == y) + if res: + return NotImplemented + + if op == Py_EQ: + return res # not (x == y) --> False + + # At this point, {op} is < or <= or > or >=. In the comments below, + # we always refer to < and <= but > and >= are obviously analogous. + + # Compute x {op} y and convert to boolean (0 or 1) + res = PyObject_RichCompare(x, y, op) + cdef bint bres = res + + # true (1) for <= and >= and false (0) for < and > + cdef bint op_is_not_strict = op & 1 + + if bres != op_is_not_strict: + # If we are asked to compute (x < y) and (x < y) is true, + # return (x < y) which is true. + # If we are asked to compute (x <= y) and (x <= y) is false, + # return (x <= y) which is false. + return res + + # Finally, check the inequality with the other strictness + # (< becomes <= and vice versa; this corresponds to replacing op by + # op ^ 1). This check is redundant in the typical case that (x <= y) + # is equivalent to (x < y or x == y): since (x == y) returned false, + # we expect that this PyObject_RichCompare() call returns the same + # boolean result as the previous one. + cdef bint xres = PyObject_RichCompare(x, y, op ^ 1) + if xres == bres: # As expected + return res + + # OK, we are in a special case now. We checked that (x == y) is + # false, that (x < y) is false but (x <= y) is true. + # Since we want to give more importance to the < and <= results than + # the == result, we treat this case as equality. Therefore, we + # cannot decide. + return NotImplemented + + cdef slot_tp_richcompare(self, other, int op): """ Function to put in the ``tp_richcompare`` slot. diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index dfc65cc5f73..7130898086c 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -2208,7 +2208,7 @@ cdef class Expression(CommutativeRingElement): def _is_registered_constant_(self): """ - Return True if this symbolic expression is interally represented as + Return True if this symbolic expression is internally represented as a constant. This function is intended to provide an interface to query the internal diff --git a/src/sage/symbolic/series.pyx b/src/sage/symbolic/series.pyx index 6ebc12357f7..a0a5a510d3a 100644 --- a/src/sage/symbolic/series.pyx +++ b/src/sage/symbolic/series.pyx @@ -88,7 +88,7 @@ TESTS: Check that :trac:`20088` is fixed:: sage: ((1+x).series(x)^pi).series(x,3) - 1 + (pi)*x + (-1/2*pi + 1/2*pi^2)*x^2 + Order(x^3) + 1 + pi*x + (-1/2*pi + 1/2*pi^2)*x^2 + Order(x^3) Check that :trac:`14878` is fixed, this should take only microseconds:: @@ -281,7 +281,7 @@ cdef class SymbolicSeries(Expression): EXAMPLES:: sage: ex=(gamma(1-x)).series(x,3); ex - 1 + (euler_gamma)*x + (1/2*euler_gamma^2 + 1/12*pi^2)*x^2 + Order(x^3) + 1 + euler_gamma*x + (1/2*euler_gamma^2 + 1/12*pi^2)*x^2 + Order(x^3) sage: g=ex.power_series(SR); g 1 + euler_gamma*x + (1/2*euler_gamma^2 + 1/12*pi^2)*x^2 + O(x^3) sage: g.parent() diff --git a/src/sage/tensor/modules/free_module_linear_group.py b/src/sage/tensor/modules/free_module_linear_group.py index 156446b0ebb..efdcf021dff 100644 --- a/src/sage/tensor/modules/free_module_linear_group.py +++ b/src/sage/tensor/modules/free_module_linear_group.py @@ -1,7 +1,7 @@ r""" General linear group of a free module -The set `\mathrm{GL}(M)` of automorphisms (i.e. invertible endomorphims) of a +The set `\mathrm{GL}(M)` of automorphisms (i.e. invertible endomorphisms) of a free module of finite rank `M` is a group under composition of automorphisms, named the *general linear group* of `M`. In other words, `\mathrm{GL}(M)` is the group of units (i.e. invertible elements) of `\mathrm{End}(M)`, the @@ -41,7 +41,7 @@ class FreeModuleLinearGroup(UniqueRepresentation, Parent): Given a free module of finite rank `M` over a commutative ring `R`, the *general linear group* of `M` is the group `\mathrm{GL}(M)` of - automorphisms (i.e. invertible endomorphims) of `M`. It is the group of + automorphisms (i.e. invertible endomorphisms) of `M`. It is the group of units (i.e. invertible elements) of `\mathrm{End}(M)`, the endomorphism ring of `M`. diff --git a/src/sage/version.py b/src/sage/version.py index 4d100301684..1d98722b54f 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,4 +1,4 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '8.2.beta3' -date = '2018-01-17' +version = '8.2.beta4' +date = '2018-01-27' diff --git a/src/sage_setup/autogen/interpreters/__init__.py b/src/sage_setup/autogen/interpreters/__init__.py index 8535d0c8095..8dbdae26295 100644 --- a/src/sage_setup/autogen/interpreters/__init__.py +++ b/src/sage_setup/autogen/interpreters/__init__.py @@ -139,7 +139,7 @@ key=lambda c: c.name) # Tuple of (filename_root, extension, method) where filename_root is the -# root of the filename to be joined with "_".ext and +# root of the filename to be joined with "_".ext and # method is the name of a get_ method on InterpreterGenerator that returns # the contents of that file _INTERPRETER_SOURCES = [ From ca3b242e6d7ca2ce22d8d23ef62d0cecd95b585f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 1 Feb 2018 10:05:43 +1300 Subject: [PATCH 585/740] correct test in startup.py --- src/sage/tests/startup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/tests/startup.py b/src/sage/tests/startup.py index a7053b63b72..90e6b549b28 100644 --- a/src/sage/tests/startup.py +++ b/src/sage/tests/startup.py @@ -22,9 +22,9 @@ sage: from sage.tests.cmdline import test_executable sage: cmd = "print('numpy' in sys.modules)\n" - sage: print(test_executable(["sage", "-c"], cmd)[0]) # long time + sage: print(test_executable(["sage", "-c", cmd])[0]) # long time False sage: cmd = "print('pyparsing' in sys.modules)\n" - sage: print(test_executable(["sage", "-c"], cmd)[0]) # long time + sage: print(test_executable(["sage", "-c", cmd])[0]) # long time False """ From aaf8be7b2197ac5a6cd2504ad6dc834f359cced3 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 1 Feb 2018 09:01:48 +0100 Subject: [PATCH 586/740] 23714: simpler imports --- src/sage/matrix/matrix_gap.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/matrix/matrix_gap.pyx b/src/sage/matrix/matrix_gap.pyx index 211b0f52aad..f7b64d2c0e2 100644 --- a/src/sage/matrix/matrix_gap.pyx +++ b/src/sage/matrix/matrix_gap.pyx @@ -15,7 +15,7 @@ from __future__ import print_function, absolute_import from sage.libs.gap.libgap import libgap from . import matrix_space -cimport sage.structure.element +from sage.structure.element cimport Matrix cdef class Matrix_gap(Matrix_dense): r""" @@ -307,7 +307,7 @@ cdef class Matrix_gap(Matrix_dense): ans._libgap = left._libgap - ( right)._libgap return ans - cdef sage.structure.element.Matrix _matrix_times_matrix_(left, sage.structure.element.Matrix right): + cdef Matrix _matrix_times_matrix_(left, Matrix right): r""" TESTS:: From a66e1d70ae87b9bbdb0b86d79e553747c365c176 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 1 Feb 2018 09:02:01 +0100 Subject: [PATCH 587/740] 23714: more doctests --- src/sage/matrix/matrix_gap.pyx | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/sage/matrix/matrix_gap.pyx b/src/sage/matrix/matrix_gap.pyx index f7b64d2c0e2..36465b43f6b 100644 --- a/src/sage/matrix/matrix_gap.pyx +++ b/src/sage/matrix/matrix_gap.pyx @@ -49,19 +49,23 @@ cdef class Matrix_gap(Matrix_dense): sage: m.transpose().parent() is M True - TESTS:: - - sage: M = MatrixSpace(ZZ, 2, implementation='gap') - sage: TestSuite(M).run() + sage: UCF = UniversalCyclotomicField() + sage: M = MatrixSpace(UCF, 3, implementation='gap') + sage: m = M([UCF.zeta(i) for i in range(1,10)]) + sage: m + [ 1 -1 E(3)] + [ E(4) E(5) -E(3)^2] + [ E(7) E(8) -E(9)^4 - E(9)^7] + sage: (m^2)[1,2] + E(180)^32 - E(180)^33 + E(180)^68 - E(180)^69 + E(180)^104 - E(180)^141 - E(180)^156 + E(180)^176 - E(180)^177 - sage: M = MatrixSpace(ZZ, 2, 3, implementation='gap') - sage: TestSuite(M).run() - - sage: M = MatrixSpace(QQ, 3, implementation='gap') - sage: TestSuite(M).run() + TESTS:: - sage: M = MatrixSpace(QQ, 3, 2, implementation='gap') - sage: TestSuite(M).run() + sage: for ring in [ZZ, QQ, UniversalCyclotomicField(), GF(2), GF(3)]: + ....: M = MatrixSpace(ring, 2, implementation='gap') + ....: TestSuite(M).run() + ....: M = MatrixSpace(ring, 2, 3, implementation='gap') + ....: TestSuite(M).run() """ def __init__(self, parent, entries, coerce, copy): r""" From 0600a540a2e43da79c0a0fa53968832323677019 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Thu, 1 Feb 2018 09:04:15 +0100 Subject: [PATCH 588/740] Documentation only Use print_mode='terse' and show_prec=False instead of .change_ring(ZZ) for printing p-adic matrices. --- .../quadratic_forms/genera/normal_form.py | 191 +++++++++--------- 1 file changed, 90 insertions(+), 101 deletions(-) diff --git a/src/sage/quadratic_forms/genera/normal_form.py b/src/sage/quadratic_forms/genera/normal_form.py index 65d27faf848..1cbe3b9dd63 100644 --- a/src/sage/quadratic_forms/genera/normal_form.py +++ b/src/sage/quadratic_forms/genera/normal_form.py @@ -7,7 +7,7 @@ This module allows the computation of a normal form. This means that two `p`-adic forms are integrally equivalent if and only if they have the same -normal form. Further, we can compute the transformation into normal form +normal form. Further, we can compute a transformation into normal form (up to finite precision). EXAMPLES:: @@ -307,20 +307,18 @@ def _find_min_p(G, cnt, lower_bound=0): EXAMPLES:: sage: from sage.quadratic_forms.genera.normal_form import _find_min_p - sage: G = matrix(Qp(2), 3, 3, [4,0,1,0,4,2,1,2,1]) + sage: G = matrix(Qp(2, show_prec=False), 3, 3, [4,0,1,0,4,2,1,2,1]) sage: G - [2^2 + O(2^22) 0 1 + O(2^20)] - [ 0 2^2 + O(2^22) 2 + O(2^21)] - [ 1 + O(2^20) 2 + O(2^21) 1 + O(2^20)] - + [2^2 0 1] + [ 0 2^2 2] + [ 1 2 1] sage: _find_min_p(G, 0) (0, 2, 2) - - sage: G = matrix(Qp(3), 3, 3, [4,0,1,0,4,2,1,2,1]) + sage: G = matrix(Qp(3, show_prec=False), 3, 3, [4,0,1,0,4,2,1,2,1]) sage: G - [1 + 3 + O(3^20) 0 1 + O(3^20)] - [ 0 1 + 3 + O(3^20) 2 + O(3^20)] - [ 1 + O(3^20) 2 + O(3^20) 1 + O(3^20)] + [1 + 3 0 1] + [ 0 1 + 3 2] + [ 1 2 1] sage: _find_min_p(G, 0) (0, 0, 0) """ @@ -455,7 +453,7 @@ def _homogeneous_normal_form(G, w): EXAMPLES:: sage: from sage.quadratic_forms.genera.normal_form import _homogeneous_normal_form - sage: R = Zp(2, type = 'fixed-mod') + sage: R = Zp(2, type = 'fixed-mod', print_mode='terse', show_prec=False) sage: U = Matrix(R, 2, [0,1,1,0]) sage: V = Matrix(R, 2, [2,1,1,2]) sage: W1 = Matrix(R, 1, [1]) @@ -464,20 +462,20 @@ def _homogeneous_normal_form(G, w): sage: W7 = Matrix(R, 1, [7]) sage: G = Matrix.block_diagonal([V, W1]) sage: B = _homogeneous_normal_form(G, 1)[1] - sage: (B * G * B.T).change_ring(ZZ) + sage: B * G * B.T [2 1 0] [1 2 0] [0 0 1] sage: G = Matrix.block_diagonal([V, W1, W3]) sage: B = _homogeneous_normal_form(G, 2)[1] - sage: (B * G * B.T).change_ring(ZZ) + sage: B * G * B.T [2 1 0 0] [1 2 0 0] [0 0 1 0] [0 0 0 3] sage: G = Matrix.block_diagonal([U, V, W1, W5]) sage: B = _homogeneous_normal_form(G, 2)[1] - sage: (B * G * B.T).change_ring(ZZ) + sage: B * G * B.T [0 1 0 0 0 0] [1 0 0 0 0 0] [0 0 0 1 0 0] @@ -486,35 +484,35 @@ def _homogeneous_normal_form(G, w): [0 0 0 0 0 7] sage: G = Matrix.block_diagonal([U, W7, W3]) sage: B = _homogeneous_normal_form(G, 2)[1] - sage: (B * G * B.T).change_ring(ZZ) + sage: B * G * B.T [0 1 0 0] [1 0 0 0] [0 0 3 0] [0 0 0 7] sage: G = Matrix.block_diagonal([V, W5, W5]) sage: B = _homogeneous_normal_form(G, 2)[1] - sage: (B * G * B.T).change_ring(ZZ) + sage: B * G * B.T [0 1 0 0] [1 0 0 0] [0 0 3 0] [0 0 0 7] sage: G = Matrix.block_diagonal([V, W3, W3]) sage: B = _homogeneous_normal_form(G, 2)[1] - sage: (B * G * B.T).change_ring(ZZ) + sage: B * G * B.T [0 1 0 0] [1 0 0 0] [0 0 1 0] [0 0 0 5] sage: G = Matrix.block_diagonal([V, W1, W3]) sage: B = _homogeneous_normal_form(G, 2)[1] - sage: (B * G * B.T).change_ring(ZZ) + sage: B * G * B.T [2 1 0 0] [1 2 0 0] [0 0 1 0] [0 0 0 3] sage: G = Matrix.block_diagonal([W3, W3]) sage: B = _homogeneous_normal_form(G, 2)[1] - sage: (B * G * B.T).change_ring(ZZ) + sage: B * G * B.T [7 0] [0 7] """ @@ -566,15 +564,15 @@ def _jordan_odd_adic(G): EXAMPLES:: sage: from sage.quadratic_forms.genera.normal_form import _jordan_odd_adic - sage: R = Zp(3,prec=2,print_mode='series') + sage: R = Zp(3, prec=2, print_mode='terse', show_prec=False) sage: A4 = Matrix(R,4,[2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2]) sage: A4 - [ 2 + O(3^2) 2 + 2*3 + O(3^2) 0 0] - [2 + 2*3 + O(3^2) 2 + O(3^2) 2 + 2*3 + O(3^2) 0] - [ 0 2 + 2*3 + O(3^2) 2 + O(3^2) 2 + 2*3 + O(3^2)] - [ 0 0 2 + 2*3 + O(3^2) 2 + O(3^2)] + [2 8 0 0] + [8 2 8 0] + [0 8 2 8] + [0 0 8 2] sage: D, B = _jordan_odd_adic(A4) - sage: D.change_ring(ZZ) # just for pretty printing. + sage: D [2 0 0 0] [0 2 0 0] [0 0 1 0] @@ -650,15 +648,15 @@ def _jordan_2_adic(G): EXAMPLES:: sage: from sage.quadratic_forms.genera.normal_form import _jordan_2_adic - sage: R = Zp(2,prec=3,print_mode='series') + sage: R = Zp(2, prec=3, print_mode='terse', show_prec=False) sage: A4 = Matrix(R,4,[2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2]) - sage: A4.change_ring(ZZ) # for pretty printing + sage: A4 [2 7 0 0] [7 2 7 0] [0 7 2 7] [0 0 7 2] sage: D, B = _jordan_2_adic(A4) - sage: D.change_ring(ZZ) # for pretty printing. + sage: D [ 2 7 0 0] [ 7 2 0 0] [ 0 0 12 7] @@ -778,18 +776,18 @@ def _normalize(G): EXAMPLES:: sage: from sage.quadratic_forms.genera.normal_form import _normalize - sage: R = Zp(3, prec = 5, type = 'fixed-mod', print_mode = 'digits') + sage: R = Zp(3, prec = 5, type = 'fixed-mod', print_mode='series', show_prec=False) sage: G = matrix.diagonal(R, [1,7,3,3*5,3,9,-9,27*13]) sage: D, B =_normalize(G) sage: D - [...00001 0 0 0 0 0 0 0] - [ 0 ...00001 0 0 0 0 0 0] - [ 0 0 ...00010 0 0 0 0 0] - [ 0 0 0 ...00010 0 0 0 0] - [ 0 0 0 0 ...00020 0 0 0] - [ 0 0 0 0 0 ...00100 0 0] - [ 0 0 0 0 0 0 ...00200 0] - [ 0 0 0 0 0 0 0 ...01000] + [ 1 0 0 0 0 0 0 0] + [ 0 1 0 0 0 0 0 0] + [ 0 0 3 0 0 0 0 0] + [ 0 0 0 3 0 0 0 0] + [ 0 0 0 0 2*3 0 0 0] + [ 0 0 0 0 0 3^2 0 0] + [ 0 0 0 0 0 0 2*3^2 0] + [ 0 0 0 0 0 0 0 3^3] """ R = G.base_ring() D = copy(G) @@ -859,7 +857,7 @@ def _normalize_2x2(G): [2a b] [ b 2c] * 2^n - with b of valuation 1. + with `b` of valuation 1. OUTPUT: @@ -873,34 +871,24 @@ def _normalize_2x2(G): EXAMPLES:: sage: from sage.quadratic_forms.genera.normal_form import _normalize_2x2 - sage: R = Zp(2, prec = 10, type = 'fixed-mod', print_mode = 'digits') + sage: R = Zp(2, prec = 15, type = 'fixed-mod', print_mode='series', show_prec=False) sage: G = Matrix(R, 2, [-17*2,3,3,23*2]) sage: B =_normalize_2x2(G) sage: B * G * B.T - [...0000000010 ...0000000001] - [...0000000001 ...0000000010] + [2 1] + [1 2] sage: G = Matrix(R,2,[-17*4,3,3,23*2]) sage: B = _normalize_2x2(G) sage: B*G*B.T - [ 0 ...0000000001] - [...0000000001 0] + [0 1] + [1 0] - sage: G = Matrix(R, 2, [-17*2,3,3,23*2]) - sage: B = _normalize_2x2(8*G) + sage: G = 2^3 * Matrix(R, 2, [-17*2,3,3,23*2]) + sage: B = _normalize_2x2(G) sage: B * G * B.T - [...1110000010 ...1010000001] - [...1010000001 ...1110000010] - - TESTS:: - - sage: from sage.quadratic_forms.genera.normal_form import _normalize_2x2 - sage: R = Zp(2, prec = 10, type = 'fixed-mod') - sage: ref1 = Matrix(R,2,[2,1,1,2]) - sage: ref2 = Matrix(R,2,[0,1,1,0]) - sage: N = _normalize_2x2(G) - sage: (N*G*N.T == ref1) or (N*G*N.T == ref2) - True + [2^4 2^3] + [2^3 2^4] """ from sage.rings.all import PolynomialRing from sage.modules.free_module_element import vector @@ -1000,11 +988,12 @@ def _normalize_odd_2x2(G): EXAMPLES:: sage: from sage.quadratic_forms.genera.normal_form import _normalize_odd_2x2 - sage: G = 2 * Matrix.identity(Qp(5), 2) + sage: R = Zp(5, type='fixed-mod', print_mode='terse', show_prec=False) + sage: G = 2 * Matrix.identity(R, 2) sage: B = _normalize_odd_2x2(G) sage: B*G*B.T - [1 + O(5^20) O(5^20)] - [ O(5^20) 1 + O(5^20)] + [1 0] + [0 1] """ assert G[0,0]==G[1,1] u = G[0,0] @@ -1038,7 +1027,7 @@ def _partial_normal_form_of_block(G): EXAMPLES:: sage: from sage.quadratic_forms.genera.normal_form import _partial_normal_form_of_block - sage: R = Zp(2,prec=4, type = 'fixed-mod') + sage: R = Zp(2,prec=4, type = 'fixed-mod',print_mode='terse', show_prec=False) sage: U = Matrix(R, 2, [0,1,1,0]) sage: V = Matrix(R, 2, [2,1,1,2]) sage: W1 = Matrix(R, 1, [1]) @@ -1047,7 +1036,7 @@ def _partial_normal_form_of_block(G): sage: W7 = Matrix(R, 1, [7]) sage: G = Matrix.block_diagonal([W1, U, V, W5, V, W3, V, W7]) sage: B = _partial_normal_form_of_block(G)[1] - sage: (B * G * B.T).change_ring(ZZ) + sage: B * G * B.T [0 1 0 0 0 0 0 0 0 0 0 0] [1 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 1 0 0 0 0 0 0 0 0] @@ -1062,7 +1051,7 @@ def _partial_normal_form_of_block(G): [0 0 0 0 0 0 0 0 0 0 0 7] sage: G = Matrix.block_diagonal([W1, U, V, W1, V, W1, V, W7]) sage: B = _partial_normal_form_of_block(G)[1] - sage: (B * G * B.T).change_ring(ZZ) + sage: B * G * B.T [0 1 0 0 0 0 0 0 0 0 0 0] [1 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 1 0 0 0 0 0 0 0 0] @@ -1135,7 +1124,7 @@ def _relations(G,n): EXAMPLES:: sage: from sage.quadratic_forms.genera.normal_form import _relations - sage: R = Zp(2, type = 'fixed-mod') + sage: R = Zp(2, type = 'fixed-mod',print_mode='terse', show_prec=False) sage: U = Matrix(R,2,[0,1,1,0]) sage: V = Matrix(R,2,[2,1,1,2]) sage: W1 = Matrix(R,1,[1]) @@ -1144,179 +1133,179 @@ def _relations(G,n): sage: W7 = Matrix(R,1,[7]) sage: G = Matrix.block_diagonal(W1,W1) sage: b = _relations(G,1) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [5 0] [0 5] sage: G = Matrix.block_diagonal(W1,W3) sage: b = _relations(G,1) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [5 0] [0 7] sage: G = Matrix.block_diagonal(W1,W5) sage: b = _relations(G,1) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [5 0] [0 1] sage: G = Matrix.block_diagonal(W1,W7) sage: b = _relations(G,1) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [5 0] [0 3] sage: G = Matrix.block_diagonal(W3,W3) sage: b = _relations(G,1) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [7 0] [0 7] sage: G = Matrix.block_diagonal(W3,W5) sage: b = _relations(G,1) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [7 0] [0 1] sage: G = Matrix.block_diagonal(W3,W7) sage: b = _relations(G,1) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [7 0] [0 3] sage: G = Matrix.block_diagonal(W5,W5) sage: b = _relations(G,1) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [1 0] [0 1] sage: G = Matrix.block_diagonal(W5,W7) sage: b = _relations(G,1) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [1 0] [0 3] sage: G = Matrix.block_diagonal(W7,W7) sage: b = _relations(G,1) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [3 0] [0 3] sage: G = Matrix.block_diagonal([V,V]) sage: b = _relations(G,3) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [0 1 0 0] [1 0 0 0] [0 0 0 1] [0 0 1 0] sage: G = Matrix.block_diagonal([V,W1,W1]) sage: b = _relations(G,5) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [0 1 0 0] [1 0 0 0] [0 0 7 0] [0 0 0 3] sage: G = Matrix.block_diagonal([V,W1,W5]) sage: b = _relations(G,5) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [0 1 0 0] [1 0 0 0] [0 0 3 0] [0 0 0 3] sage: G = Matrix.block_diagonal([V,W3,W7]) sage: b = _relations(G,5) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [0 1 0 0] [1 0 0 0] [0 0 5 0] [0 0 0 5] sage: G = Matrix.block_diagonal([W1,2*W1]) sage: b = _relations(G,6) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [3 0] [0 6] sage: G = Matrix.block_diagonal([W1,2*W3]) sage: b = _relations(G,6) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [ 7 0] [ 0 10] sage: G = Matrix.block_diagonal([W1,2*W5]) sage: b = _relations(G,6) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [ 3 0] [ 0 14] sage: G = Matrix.block_diagonal([W1,2*W7]) sage: b = _relations(G,6) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [7 0] [0 2] sage: G = Matrix.block_diagonal([W3,2*W5]) sage: b = _relations(G,6) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [5 0] [0 6] sage: G = Matrix.block_diagonal([W3,2*W3]) sage: b = _relations(G,6) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [1 0] [0 2] sage: G = Matrix.block_diagonal([2*W5,4*W7]) sage: b = _relations(G,6) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [6 0] [0 4] sage: G = Matrix.block_diagonal([W3,2*V]) sage: b = _relations(G,7) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [7 0 0] [0 0 2] [0 2 0] sage: G = Matrix.block_diagonal([W7,2*V]) sage: b = _relations(G,7) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [3 0 0] [0 0 2] [0 2 0] sage: G = Matrix.block_diagonal([U,2*W1]) sage: b = _relations(G,8) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [ 2 1 0] [ 1 2 0] [ 0 0 10] sage: G = Matrix.block_diagonal([U,2*W5]) sage: b = _relations(G,8) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [2 1 0] [1 2 0] [0 0 2] sage: G = Matrix.block_diagonal([V,2*W1]) sage: b = _relations(G,8) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [ 0 1 0] [ 1 0 0] [ 0 0 10] sage: G = Matrix.block_diagonal([V,2*W7]) sage: b = _relations(G,8) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [0 1 0] [1 0 0] [0 0 6] sage: G = Matrix.block_diagonal([W1,W5,2*W5]) sage: b = _relations(G,9) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [3 0 0] [0 3 0] [0 0 2] sage: G = Matrix.block_diagonal([W3,W3,2*W5]) sage: b = _relations(G,9) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [5 0 0] [0 1 0] [0 0 2] sage: G = Matrix.block_diagonal([W3,W3,2*W1]) sage: b = _relations(G,9) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [ 5 0 0] [ 0 1 0] [ 0 0 10] sage: G = Matrix.block_diagonal([W3,4*W1]) sage: b = _relations(G,10) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [ 7 0] [ 0 20] sage: G = Matrix.block_diagonal([W5,4*W5]) sage: b = _relations(G,10) - sage: (b*G*b.T).change_ring(ZZ) + sage: b * G * b.T [1 0] [0 4] """ @@ -1395,7 +1384,7 @@ def _two_adic_normal_forms(G, partial=False): EXAMPLES:: sage: from sage.quadratic_forms.genera.normal_form import _two_adic_normal_forms - sage: R = Zp(2, type = 'fixed-mod') + sage: R = Zp(2, type = 'fixed-mod', print_mode='terse', show_prec=False) sage: U = Matrix(R,2,[0,1,1,0]) sage: V = Matrix(R,2,[2,1,1,2]) sage: W1 = Matrix(R,1,[1]) @@ -1404,14 +1393,14 @@ def _two_adic_normal_forms(G, partial=False): sage: W7 = Matrix(R,1,[7]) sage: G = Matrix.block_diagonal([2*W1,2*W1,4*V]) sage: B = _two_adic_normal_forms(G)[1] - sage: (B * G * B.T).change_ring(ZZ) + sage: B * G * B.T [ 2 0 0 0] [ 0 10 0 0] [ 0 0 0 4] [ 0 0 4 0] sage: G = Matrix.block_diagonal([W1,2*V,2*W3,2*W5]) sage: B = _two_adic_normal_forms(G)[1] - sage: (B * G * B.T).change_ring(ZZ) + sage: B * G * B.T [3 0 0 0 0] [0 0 2 0 0] [0 2 0 0 0] @@ -1419,7 +1408,7 @@ def _two_adic_normal_forms(G, partial=False): [0 0 0 0 2] sage: G = Matrix.block_diagonal([U,2*V,2*W3,2*W5]) sage: B = _two_adic_normal_forms(G)[1] - sage: (B * G * B.T).change_ring(ZZ) + sage: B * G * B.T [2 1 0 0 0 0] [1 2 0 0 0 0] [0 0 4 2 0 0] From 5cf25a976cf2c0cd74140e770cc6856b0367f80c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Thu, 1 Feb 2018 10:06:26 +0200 Subject: [PATCH 589/740] Faster algorithm. --- src/sage/combinat/posets/posets.py | 55 ++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 06bac0c4be5..4c79e1dd938 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -3139,23 +3139,48 @@ def jump_number(self, certificate=False): sage: A4.jump_number() 3 """ - H = self._hasse_diagram - jumps_min = H.order() # = "Infinity" - - for lin_ext in H.topological_sort_generator(): - jumpcount = 0 - for a, b in zip(lin_ext, lin_ext[1:]): - if not H.has_edge(a, b): - jumpcount += 1 - if jumpcount >= jumps_min: - break - else: - jumps_min = jumpcount - best_le = lin_ext + N = self.cardinality() + if N == 0: + return (0, []) if certificate else 0 + self_as_set = set(self) + # No 'nonlocal' in Python 2. Workaround: + # nonlocals[0] is the best jump number found so far, + # nonlocals[1] is the linear extension giving it. + nonlocals = [N, []] + + def greedy_rec(self, linext, jumpcount): + """ + Recursively extend beginning of a linear extension by one element, + unless we see that an extension with smaller jump number already + has been found. + """ + if len(linext) == N: + nonlocals[0] = jumpcount + nonlocals[1] = linext[:] + return + + S = [] + if linext: + # S is elements where we can grow the chain without a jump. + S = [x for x in self.upper_covers(linext[-1]) if + all(low in linext for low in self.lower_covers(x))] + if not S: + if jumpcount >= nonlocals[0]-1: + return + jumpcount += 1 + # S is minimal elements of the poset without elements in linext + S_ = self_as_set.difference(set(linext)) + S = [x for x in S_ if + not any(low in S_ for low in self.lower_covers(x))] + + for e in S: + greedy_rec(self, linext+[e], jumpcount) + + greedy_rec(self, [], -1) if certificate: - return (jumps_min, [self._vertex_to_element(v) for v in best_le]) - return jumps_min + return (nonlocals[0], nonlocals[1]) + return nonlocals[0] def rank_function(self): r""" From bcd3f046b7589b54a336ac737d4309bd4f4bd252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Thu, 1 Feb 2018 10:09:56 +0200 Subject: [PATCH 590/740] Explain algoirthm. --- src/sage/combinat/posets/posets.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 4c79e1dd938..3a83075a1cb 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -3124,7 +3124,8 @@ def jump_number(self, certificate=False): ALGORITHM: - See [BIANCO]_ + Basically we test every greedy linear extension of the poset. + The problem is shown to be NP-hard. TESTS:: From 478aee42b882a74a9b440e5543dc9cf867240e81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Thu, 1 Feb 2018 11:48:17 +0200 Subject: [PATCH 591/740] Add is_greedy() to linear extension. --- src/sage/combinat/posets/linear_extensions.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/sage/combinat/posets/linear_extensions.py b/src/sage/combinat/posets/linear_extensions.py index e22ed3fe722..d242711c404 100644 --- a/src/sage/combinat/posets/linear_extensions.py +++ b/src/sage/combinat/posets/linear_extensions.py @@ -213,6 +213,40 @@ def to_poset(self): relabelling = dict(zip(old,new)) return P.relabel(relabelling).with_linear_extension(new) + def is_greedy(self): + """ + Return ``True`` if the linear extension is greedy. + + A linear extension `[e_1, e_2, \ldots, e_n]` is *greedy* if for + every `i` either `e_{i+1}` covers `e_i` or all upper covers + of `e_i` have at least one lower cover that is not in + `[e_1, e_2, \ldots, e_i]`. + + Informally said a linear extension is greedy if it "always + goes up when possible" and so has no unnecessary jumps. + + EXAMPLES:: + + sage: P = posets.PentagonPoset() + sage: for l in P.linear_extensions(): + ....: if not l.is_greedy(): + ....: print(l) + [0, 2, 1, 3, 4] + + TESTS:: + + sage: E = Poset() + sage: E.linear_extensions()[0].is_greedy() + True + """ + P = self.poset() + for i in range(len(self)-1): + if not P.covers(self[i], self[i+1]): + for u in P.upper_covers(self[i]): + if all(l in self[:i+1] for l in P.lower_covers(u)): + return False + return True + def tau(self, i): r""" Returns the operator `\tau_i` on linear extensions ``self`` of a poset. From 0381eb23c9489c6fcbc2f7d07515469704dd1a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Thu, 1 Feb 2018 12:16:43 +0200 Subject: [PATCH 592/740] Add jump number of a linear extension. --- src/sage/combinat/posets/linear_extensions.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/sage/combinat/posets/linear_extensions.py b/src/sage/combinat/posets/linear_extensions.py index e22ed3fe722..f024cf3b8ee 100644 --- a/src/sage/combinat/posets/linear_extensions.py +++ b/src/sage/combinat/posets/linear_extensions.py @@ -325,6 +325,47 @@ def evacuation(self): self = self.tau(j) return self + def jump_count(self): + """ + Return the number of jumps in the linear extension. + + A *jump* in a linear extension `[e_1, e_2, \ldots, e_n]` + is a pair `(e_i, e_{i+1})` such that `e_{i+1}` does not + cover `e_i`. + + .. SEEALSO:: + + - :meth:`sage.combinat.posets.posets.FinitePoset.jump_number()` + + EXAMPLES:: + + sage: B3 = posets.BooleanLattice(3) + sage: l1 = B3.linear_extension((0, 1, 2, 3, 4, 5, 6, 7)) + sage: l1.jump_count() + 3 + sage: l2 = B3.linear_extension((0, 1, 2, 4, 3, 5, 6, 7)) + sage: l2.jump_count() + 5 + + TESTS:: + + sage: E = Poset() + sage: E.linear_extensions()[0].jump_count() + 0 + sage: C4 = posets.ChainPoset(4) + sage: C4.linear_extensions()[0].jump_count() + 0 + sage: A4 = posets.AntichainPoset(4) + sage: A4.linear_extensions()[0].jump_count() + 3 + """ + P = self.poset() + n = 0 + for i in range(len(self)-1): + if not P.covers(self[i], self[i+1]): + n += 1 + return n + class LinearExtensionsOfPoset(UniqueRepresentation, Parent): """ The set of all linear extensions of a finite poset From 2074b35d1e1c596063d70ebc7962fd709cddf0d5 Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Thu, 1 Feb 2018 12:10:54 +0100 Subject: [PATCH 593/740] added 'is_polyhedral' method to graphs --- src/doc/en/reference/references/index.rst | 5 +++ src/sage/graphs/generic_graph.py | 48 +++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 90bd9e4b861..14e69fd3c93 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -2233,6 +2233,11 @@ REFERENCES: 2369, Springer, 2002, p267--275. http://modular.math.washington.edu/papers/stein-watkins/ +.. [St1922] Ernst Steinitz, *Polyeder und Raumeinteilungen*. + In *Encyclopädie der Mathematischen Wissenschaften*, Franz Meyer + and Hand Mohrmann, eds., volume 3, *Geometrie, erster Teil, zweite Hälfte*, + pp. 1--139, Teubner, Leipzig, 1922 + .. [Swe1969] Moss Sweedler. Hopf algebras. W.A. Benjamin, Math Lec Note Ser., 1969. diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 70b38e2c45c..1ccc9e6791c 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -173,6 +173,7 @@ :meth:`~GenericGraph.is_eulerian` | Return ``True`` if the graph has a (closed) tour that visits each edge exactly once. :meth:`~GenericGraph.is_planar` | Test whether the graph is planar. :meth:`~GenericGraph.is_circular_planar` | Test whether the graph is circular planar (outerplanar) + :meth:`~GenericGraph.is_planar` | Test whether the graph is the graph of the polyhedron. :meth:`~GenericGraph.is_regular` | Return ``True`` if this graph is (`k`-)regular. :meth:`~GenericGraph.is_chordal` | Test whether the given graph is chordal. :meth:`~GenericGraph.is_circulant` | Test whether the graph is a circulant graph. @@ -4124,6 +4125,7 @@ def is_planar(self, on_embedding=None, kuratowski=False, set_embedding=False, se - "Measuring non-planarity": :meth:`~genus`, :meth:`~crossing_number` - :meth:`planar_dual` - :meth:`faces` + - :meth:`is_polyhedral` INPUT: @@ -5201,6 +5203,52 @@ def planar_dual(self, embedding=None): from sage.graphs.graph import Graph return Graph([[tuple(_) for _ in self.faces()], lambda f, g: not set([tuple(reversed(e)) for e in f]).isdisjoint(g)], loops=False) + def is_polyhedral(self): + """ + Test whether the graph is the graph of the polyhedron. + + By a theorem of Steinitz (Satz 43, p. 77 of [St1922]_), + graphs of three-dimensional polyhedra are exactly + the simple 3-vertex-connected planar graphs. + + EXAMPLES:: + + sage: C = graphs.CubeGraph(3) + sage: C.is_polyhedral() + True + sage: K33=graphs.CompleteBipartiteGraph(3, 3) + sage: K33.is_polyhedral() + False + sage: graphs.CycleGraph(17).is_polyhedral() + False + sage: [i for i in range(9) if graphs.CompleteGraph(i).is_polyhedral()] + [4] + + + .. SEEALSO:: + + * :meth:`vertex_connectivity` + * :meth:`is_planar` + + TESTS:: + + sage: G = Graph([[1, 2, 3, 4], [[1, 2], [1,1]]], loops=True) + sage: G.is_polyhedral() + False + + sage: G = Graph([[1, 2, 3], [[1, 2], [3, 1], [1, 2], [2, 3]]], multiedges=True) + sage: G.is_polyhedral() + False + + .. TODO:: + + Implement a faster 3-vertex-connectivity test. + """ + + return (not (self.has_loops() or self.has_multiple_edges()) + and (self.vertex_connectivity() >= 3) + and self.is_planar()) + ### Connectivity def is_connected(self): From a88fbf6fa68aae05afa6e251aac1c612b056a6b9 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Thu, 1 Feb 2018 13:27:51 +0100 Subject: [PATCH 594/740] Change in convention for the normal form of a p-torsion module. --- src/sage/modules/torsion_quadratic_module.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/sage/modules/torsion_quadratic_module.py b/src/sage/modules/torsion_quadratic_module.py index 12cc491553b..71b3d3cb6e1 100644 --- a/src/sage/modules/torsion_quadratic_module.py +++ b/src/sage/modules/torsion_quadratic_module.py @@ -476,10 +476,10 @@ def normal_form(self, partial=False): sage: T.normal_form() Finite quadratic module over Integer Ring with invariants (6, 6, 12, 12) Gram matrix of the quadratic form with values in Q/(1/3)Z: - [1/12 1/24 0 0 0 0 0 0] - [1/24 1/12 0 0 0 0 0 0] - [ 0 0 1/6 1/12 0 0 0 0] - [ 0 0 1/12 1/6 0 0 0 0] + [ 1/6 1/12 0 0 0 0 0 0] + [1/12 1/6 0 0 0 0 0 0] + [ 0 0 1/12 1/24 0 0 0 0] + [ 0 0 1/24 1/12 0 0 0 0] [ 0 0 0 0 1/9 0 0 0] [ 0 0 0 0 0 1/9 0 0] [ 0 0 0 0 0 0 1/9 0] @@ -502,6 +502,9 @@ def normal_form(self, partial=False): for j in range(n): g += D_p.gens()[j].lift() * U[i,j] gens_p.append(g) + # we want the order of the generators of the p-primary part + # to be ascending + gens_p.reverse() gens += gens_p return self.submodule_with_gens(gens) From 9ae1c1369b171d417e7a291055fa747335244abe Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Thu, 1 Feb 2018 13:52:15 +0100 Subject: [PATCH 595/740] Revert "Change in convention for the normal form of a p-torsion module." This reverts commit a88fbf6fa68aae05afa6e251aac1c612b056a6b9. --- src/sage/modules/torsion_quadratic_module.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/sage/modules/torsion_quadratic_module.py b/src/sage/modules/torsion_quadratic_module.py index 71b3d3cb6e1..12cc491553b 100644 --- a/src/sage/modules/torsion_quadratic_module.py +++ b/src/sage/modules/torsion_quadratic_module.py @@ -476,10 +476,10 @@ def normal_form(self, partial=False): sage: T.normal_form() Finite quadratic module over Integer Ring with invariants (6, 6, 12, 12) Gram matrix of the quadratic form with values in Q/(1/3)Z: - [ 1/6 1/12 0 0 0 0 0 0] - [1/12 1/6 0 0 0 0 0 0] - [ 0 0 1/12 1/24 0 0 0 0] - [ 0 0 1/24 1/12 0 0 0 0] + [1/12 1/24 0 0 0 0 0 0] + [1/24 1/12 0 0 0 0 0 0] + [ 0 0 1/6 1/12 0 0 0 0] + [ 0 0 1/12 1/6 0 0 0 0] [ 0 0 0 0 1/9 0 0 0] [ 0 0 0 0 0 1/9 0 0] [ 0 0 0 0 0 0 1/9 0] @@ -502,9 +502,6 @@ def normal_form(self, partial=False): for j in range(n): g += D_p.gens()[j].lift() * U[i,j] gens_p.append(g) - # we want the order of the generators of the p-primary part - # to be ascending - gens_p.reverse() gens += gens_p return self.submodule_with_gens(gens) From e10754d0b36d1262300b1ac618da5e7f48e28bc7 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Thu, 1 Feb 2018 11:16:06 -0800 Subject: [PATCH 596/740] Corner cases --- src/sage/combinat/shifted_primed_tableau.py | 82 ++++++++++++++------- 1 file changed, 57 insertions(+), 25 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index 5fd0c09dab2..c9259263aea 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -108,21 +108,13 @@ def __classcall_private__(cls, T, skew=None): """ if isinstance(T, ShiftedPrimedTableau) and T._skew == skew: return T - if not T or T == [[]]: + if T == [] or T == [[]]: return ShiftedPrimedTableaux(skew=skew)([]) - try: - entry = T[0][0] - except TypeError: - raise ValueError("input tableau must be a list of lists") - - if entry is None: - skew_ = [row.count(None) for row in T if row[0] is None] - if skew is not None: - if skew != skew_: - raise ValueError("skew shape does not agree with None entries") - else: - skew = skew_ - + skew_ = Partition([row.count(None) for row in T]) + if skew_: + if skew and Partition(skew) != skew_: + raise ValueError("skew shape does not agree with None entries") + skew = skew_ return ShiftedPrimedTableaux(skew=skew)(T) def __init__(self, parent, T, skew=None, check=True, preprocessed=False): @@ -162,12 +154,26 @@ def __init__(self, parent, T, skew=None, check=True, preprocessed=False): def _preprocess(T, skew=None): """ Preprocessing list ``T`` to initialize the tableau. + The output is a list of rows as tuples, with explicit + ``None``'s to indicate the skew shape, and entries being + ``PrimedEntry``s. + + Trailing empty rows are removed. TESTS:: sage: ShiftedPrimedTableau._preprocess([["2'", "3p", 3.5]], ....: skew=[1]) [(None, 2', 3', 4')] + sage: ShiftedPrimedTableau._preprocess([["2'", "3p", 3.5]], skew=[1]) + [(None, 2', 3', 4')] + + sage: ShiftedPrimedTableau._preprocess([[None]], skew=[1]) + [(None,)] + sage: ShiftedPrimedTableau._preprocess([], skew=[2,1]) + [(None, None), (None,)] + sage: ShiftedPrimedTableau._preprocess([], skew=[]) + [] """ if isinstance(T, ShiftedPrimedTableau): return T @@ -175,14 +181,14 @@ def _preprocess(T, skew=None): # Preprocessing list t for primes and other symbols T = [[PrimedEntry(entry) for entry in row if entry is not None] for row in T] + row_min = min(len(skew), len(T)) if skew else 0 + T_ = [(None,)*skew[i] + tuple(T[i]) for i in range(row_min)] - if skew is not None: - T = ([(None,)*skew[i] + tuple(T[i]) - for i in range(len(skew))] - + [tuple(T[i]) for i in range(len(skew), len(T))]) - else: - T = [tuple(row) for row in T] - return T + if row_min < len(T): + T_ += [tuple(T[i]) for i in range(row_min, len(T))] + elif skew: + T_ += [(None,)*skew[i] for i in range(row_min, len(skew))] + return T_ def check(self): """ @@ -354,6 +360,12 @@ def _ascii_art_(self): sage: ascii_art(ShiftedPrimedTableau([])) ++ ++ + + sage: ascii_art(ShiftedPrimedTableau([], skew=[1])) + +---+ + | . | + +---+ + """ from sage.typeset.ascii_art import AsciiArt return AsciiArt(self._ascii_art_table(unicode=False).splitlines()) @@ -383,6 +395,10 @@ def _unicode_art_(self): sage: unicode_art(ShiftedPrimedTableau([])) ┌┐ └┘ + sage: unicode_art(ShiftedPrimedTableau([], skew=[1])) + ┌───┐ + │ . │ + └───┘ """ from sage.typeset.unicode_art import UnicodeArt return UnicodeArt(self._ascii_art_table(unicode=True).splitlines()) @@ -492,6 +508,13 @@ def pp(self): sage: s.pp() . . 2' 2 3 . 2' + + TESTS:: + + sage: ShiftedPrimedTableau([],skew=[1]).pp() + . + sage: ShiftedPrimedTableau([]).pp() + """ print(self._repr_diagram()) @@ -523,12 +546,21 @@ def max_entry(self): sage: Tab = ShiftedPrimedTableau([(1,1,'2p','3p'),(2,2)]) sage: Tab.max_entry() 3 + + TESTS:: + + sage: Tab = ShiftedPrimedTableau([], skew=[2,1]) + sage: Tab.max_entry() + 0 + sage: Tab = ShiftedPrimedTableau([["1p"]], skew=[2,1]) + sage: Tab.max_entry() + 1 """ - if not self: + flat = [entry.unprimed() for row in self + for entry in row if entry is not None] + if len(flat) == 0: return 0 - - return max(entry.unprimed() for row in self - for entry in row if entry is not None) + return max(flat) def shape(self): r""" From a4d6731c28f45d32464363925fae1152b9ec5233 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Thu, 1 Feb 2018 12:04:02 -0800 Subject: [PATCH 597/740] more input processing --- src/sage/combinat/shifted_primed_tableau.py | 28 +++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index c9259263aea..cead214dbea 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -105,11 +105,35 @@ def __classcall_private__(cls, T, skew=None): sage: s = ShiftedPrimedTableau([[None, None,"2p",2,3],[None,"2p"]]) sage: s.parent() Shifted Primed Tableaux skewed by [2, 1] + + TESTS:: + + sage: ShiftedPrimedTableau([]) + [] + sage: ShiftedPrimedTableau([tuple([])]) + [] + sage: ShiftedPrimedTableau(1) + Traceback (most recent call last): + ... + ValueError: input must be an iterable + sage: ShiftedPrimedTableau([1,2]) + Traceback (most recent call last): + ... + ValueError: rows in the tableau must be iterables """ if isinstance(T, ShiftedPrimedTableau) and T._skew == skew: return T - if T == [] or T == [[]]: - return ShiftedPrimedTableaux(skew=skew)([]) + try: + if len(T) == 0: + return ShiftedPrimedTableaux(skew=skew)([]) + except TypeError: + raise ValueError("input must be an iterable") + try: + if sum(len(row) for row in T) == 0: + return ShiftedPrimedTableaux(skew=skew)([]) + except TypeError: + raise ValueError("rows in the tableau must be iterables") + skew_ = Partition([row.count(None) for row in T]) if skew_: if skew and Partition(skew) != skew_: From 8d93e0c2b69163c82762b5680e8ef615930f202c Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 31 Jan 2018 10:38:31 +0100 Subject: [PATCH 598/740] Fix giac build on SunOS --- build/pkgs/giac/SPKG.txt | 15 +++++----- build/pkgs/giac/package-version.txt | 2 +- build/pkgs/giac/patches/alloca.patch | 38 +++++++++++++++++++++++ build/pkgs/giac/patches/dirent.patch | 40 +++++++++++++++++++++++++ build/pkgs/giac/patches/sunos_abs.patch | 18 +++++++++++ build/pkgs/giac/spkg-src | 2 +- 6 files changed, 105 insertions(+), 10 deletions(-) create mode 100644 build/pkgs/giac/patches/alloca.patch create mode 100644 build/pkgs/giac/patches/dirent.patch create mode 100644 build/pkgs/giac/patches/sunos_abs.patch diff --git a/build/pkgs/giac/SPKG.txt b/build/pkgs/giac/SPKG.txt index 1a8624b99c1..d4d0a35ba5b 100644 --- a/build/pkgs/giac/SPKG.txt +++ b/build/pkgs/giac/SPKG.txt @@ -16,8 +16,11 @@ == Licence == -GPLv3+ except the french html documentation which is freely redistributable - for non commercial only purposes. This doc has been removed in the spkg) +GPLv3+ + +Note: except the french html documentation which is freely +redistributable for non commercial only purposes. This doc has been +removed in the Sage package, see spkg-src == Upstream Contact == @@ -30,12 +33,8 @@ GPLv3+ except the french html documentation which is freely redistributable * gettext, readline * giac will benefit of ntl, pari, mpfr, gsl, lapack but they should be already installed by sage. * giac can also benefit of mpfi for arithmetic on intervals. - * The Documentation is pre-built, hevea or latex or ... are not needed to install the package. + * The Documentation is pre-built, hevea or latex or ... are not needed to install the package. == Special Update/Build Instructions == - * The script_subdirectory of the pexpect giac interface is create in spkg-install. - * Some gcc 4.7.2 gives a seg fault when compiling giac, but earlier or later version - should be fine. - http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54986 - * pari support has been enabled in spkg-install. To allow this, giac have been patched to not always init pari. (See the comments in patches/README.txt for details) + * Use spkg-src to update this package diff --git a/build/pkgs/giac/package-version.txt b/build/pkgs/giac/package-version.txt index f7272f4bb5c..becbcd212a0 100644 --- a/build/pkgs/giac/package-version.txt +++ b/build/pkgs/giac/package-version.txt @@ -1 +1 @@ -1.4.9.45 +1.4.9.45.p1 diff --git a/build/pkgs/giac/patches/alloca.patch b/build/pkgs/giac/patches/alloca.patch new file mode 100644 index 00000000000..5dec185c225 --- /dev/null +++ b/build/pkgs/giac/patches/alloca.patch @@ -0,0 +1,38 @@ +Always use ALLOCA macro instead of alloca() + +diff -ru a/src/gen.cc b/src/gen.cc +--- a/src/gen.cc 2018-01-10 14:16:00.000000000 +0100 ++++ b/src/gen.cc 2018-02-01 19:36:02.128020823 +0100 +@@ -11153,7 +11153,7 @@ + + /* I/O: Input routines */ + +- gen chartab2gen(char * & s,GIAC_CONTEXT){ ++ gen chartab2gen(char * s,GIAC_CONTEXT){ + gen res; + // subtype=0; + // initialize as a null _INT_ +@@ -11200,11 +11200,7 @@ + if (l>0 && s[l-1]=='.'){ + // make a copy of s, call chartab2gen recursivly, + // because some implementations of strtod do not like a . at the end +-#ifdef FREERTOS + ALLOCA(char, scopy, l+2); +-#else +- char * scopy=(char *)alloca(l+2); +-#endif + strcpy(scopy,s); + scopy[l]='0'; + scopy[l+1]=0; +diff -ru a/src/gen.h b/src/gen.h +--- a/src/gen.h 2017-12-19 21:15:00.000000000 +0100 ++++ b/src/gen.h 2018-02-01 19:35:56.090020807 +0100 +@@ -930,7 +930,7 @@ + void gen_sort_f(iterateur it,iterateur itend,bool (*f)(const gen &a,const gen &b)); + void gen_sort_f_context(iterateur it,iterateur itend,bool (*f)(const gen &a,const gen &b,GIAC_CONTEXT),GIAC_CONTEXT); + gen makemap(); // make a new map +- gen chartab2gen(char * & s,GIAC_CONTEXT); ++ gen chartab2gen(char * s,GIAC_CONTEXT); + + + bool is_zero(const gen & a,GIAC_CONTEXT0); diff --git a/build/pkgs/giac/patches/dirent.patch b/build/pkgs/giac/patches/dirent.patch new file mode 100644 index 00000000000..306f3a90a27 --- /dev/null +++ b/build/pkgs/giac/patches/dirent.patch @@ -0,0 +1,40 @@ +Disable checking for directories on systems not supporting that + +diff -ru a/src/help.cc b/src/help.cc +--- a/src/help.cc 2017-11-28 07:55:06.000000000 +0100 ++++ b/src/help.cc 2018-02-01 21:53:24.350042354 +0100 +@@ -52,6 +52,15 @@ + + #include "input_lexer.h" + ++#ifdef DT_DIR ++/* This should be supported on Linux and BSD */ ++#define dirent_is_dir(d) ((d)->d_type == DT_DIR) ++#else ++/* This functionality simply will not work on other systems not ++ * supporting the d_type field. */ ++#define dirent_is_dir(d) (0) ++#endif ++ + #ifndef NO_NAMESPACE_GIAC + namespace giac { + #endif // ndef NO_NAMESPACE_GIAC +@@ -933,7 +942,7 @@ + static int dir_select (const struct dirent *d){ + #endif + string s(d->d_name); +- if (d->d_type==DT_DIR || equalposcomp(subdir_strings,s)){ ++ if (dirent_is_dir(d) || equalposcomp(subdir_strings,s)){ + return s!="." && s!=".."; + } + int t=s.size(); +@@ -997,8 +1006,7 @@ + index_done=find_index(subdir,s,mtt,mall); + } + #else +- unsigned char type=cnt>=0?eps[cnt]->d_type:0; +- if (type==DT_DIR || equalposcomp(subdir_strings,s)) ++ if ((cnt>=0 && dirent_is_dir(eps[cnt])) || equalposcomp(subdir_strings,s)) + find_all_index(s+"/",mtt,mall); + else { + if (!index_done) diff --git a/build/pkgs/giac/patches/sunos_abs.patch b/build/pkgs/giac/patches/sunos_abs.patch new file mode 100644 index 00000000000..0e1cd76ed2c --- /dev/null +++ b/build/pkgs/giac/patches/sunos_abs.patch @@ -0,0 +1,18 @@ +Work around _ABS macro on SunOS + +See https://trac.sagemath.org/ticket/24619 + +diff -ru a/src/rpn.h b/src/rpn.h +--- a/src/rpn.h 2016-01-03 09:12:13.000000000 +0100 ++++ b/src/rpn.h 2018-01-31 10:36:43.049921736 +0100 +@@ -24,6 +24,10 @@ + #include + #include + ++/* SunOS defines this as macro */ ++#undef _ABS ++ ++ + #ifndef NO_NAMESPACE_GIAC + namespace giac { + #endif // ndef NO_NAMESPACE_GIAC diff --git a/build/pkgs/giac/spkg-src b/build/pkgs/giac/spkg-src index d82372402ef..e8baa2584f2 100755 --- a/build/pkgs/giac/spkg-src +++ b/build/pkgs/giac/spkg-src @@ -46,7 +46,7 @@ mv giac-"$VERSION" src # removing french html doc, but keep keywords, and working makefiles. # NB: the french html doc is huge and not GPL. -# it is freely redistibuable only for non commercial purposes. +# it is freely redistributable only for non commercial purposes. cd src/doc/fr rm -rf [^Mkx]* rm -rf *.pdf *.eps *.pdf *.png *.cxx *.cas *.jpg *.tex *.stamp *html* cas* *.fig fig* From bf7e707c6764952aeca7f2b81331cb6b6eed1738 Mon Sep 17 00:00:00 2001 From: KirillP23 Date: Thu, 1 Feb 2018 13:42:08 -0800 Subject: [PATCH 599/740] preprocessing for corner cases --- src/sage/combinat/shifted_primed_tableau.py | 24 ++------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index cead214dbea..f5028ab8daf 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -112,27 +112,9 @@ def __classcall_private__(cls, T, skew=None): [] sage: ShiftedPrimedTableau([tuple([])]) [] - sage: ShiftedPrimedTableau(1) - Traceback (most recent call last): - ... - ValueError: input must be an iterable - sage: ShiftedPrimedTableau([1,2]) - Traceback (most recent call last): - ... - ValueError: rows in the tableau must be iterables """ if isinstance(T, ShiftedPrimedTableau) and T._skew == skew: return T - try: - if len(T) == 0: - return ShiftedPrimedTableaux(skew=skew)([]) - except TypeError: - raise ValueError("input must be an iterable") - try: - if sum(len(row) for row in T) == 0: - return ShiftedPrimedTableaux(skew=skew)([]) - except TypeError: - raise ValueError("rows in the tableau must be iterables") skew_ = Partition([row.count(None) for row in T]) if skew_: @@ -189,9 +171,6 @@ def _preprocess(T, skew=None): sage: ShiftedPrimedTableau._preprocess([["2'", "3p", 3.5]], ....: skew=[1]) [(None, 2', 3', 4')] - sage: ShiftedPrimedTableau._preprocess([["2'", "3p", 3.5]], skew=[1]) - [(None, 2', 3', 4')] - sage: ShiftedPrimedTableau._preprocess([[None]], skew=[1]) [(None,)] sage: ShiftedPrimedTableau._preprocess([], skew=[2,1]) @@ -201,10 +180,11 @@ def _preprocess(T, skew=None): """ if isinstance(T, ShiftedPrimedTableau): return T - # Preprocessing list t for primes and other symbols T = [[PrimedEntry(entry) for entry in row if entry is not None] for row in T] + while len(T) > 0 and len(T[-1]) == 0: + T = T[:-1] row_min = min(len(skew), len(T)) if skew else 0 T_ = [(None,)*skew[i] + tuple(T[i]) for i in range(row_min)] From b5f42599cdfe89d45d1200e83eddf26822946409 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 1 Feb 2018 20:23:03 -0600 Subject: [PATCH 600/740] Adding _unicode_art_ to ASMs. --- src/sage/combinat/alternating_sign_matrix.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sage/combinat/alternating_sign_matrix.py b/src/sage/combinat/alternating_sign_matrix.py index 44917260f4a..4203fac43ff 100644 --- a/src/sage/combinat/alternating_sign_matrix.py +++ b/src/sage/combinat/alternating_sign_matrix.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Alternating Sign Matrices @@ -175,6 +176,21 @@ def _repr_(self): """ return repr(self._matrix) + def _unicode_art_(self): + """ + Unicode art representation of ``self``. + + TESTS:: + + sage: A = AlternatingSignMatrices(3) + sage: M = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) + sage: M._unicode_art_() + ⎛1 0 0⎞ + ⎜0 1 0⎟ + ⎝0 0 1⎠ + """ + return self._matrix._unicode_art_() + def _richcmp_(self, other, op): """ Do the comparison. From bc49eaa679fd84382f2a299efe5565e37608cc92 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 1 Feb 2018 20:23:14 -0600 Subject: [PATCH 601/740] Changing test for unicode_art separator baseline. --- src/sage/typeset/unicode_art.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/sage/typeset/unicode_art.py b/src/sage/typeset/unicode_art.py index 9407b39dfe4..fb847943a40 100644 --- a/src/sage/typeset/unicode_art.py +++ b/src/sage/typeset/unicode_art.py @@ -106,12 +106,13 @@ def unicode_art(*obj, **kwds): an unicode art separator:: sage: sep_line = unicode_art('\n'.join(u' ⎟ ' for _ in range(5)), baseline=5) - sage: unicode_art(*Partitions(4), separator=sep_line, sep_baseline=0) - ⎟ ⎟ ⎟ ⎟ ┌┐ - ⎟ ⎟ ⎟ ┌┬┐ ⎟ ├┤ - ⎟ ┌┬┬┐ ⎟ ┌┬┐ ⎟ ├┼┘ ⎟ ├┤ - ┌┬┬┬┐ ⎟ ├┼┴┘ ⎟ ├┼┤ ⎟ ├┤ ⎟ ├┤ - └┴┴┴┘ ⎟ └┘ ⎟ └┴┘ ⎟ └┘ ⎟ └┘ + sage: unicode_art(*AlternatingSignMatrices(3), + ....: separator=sep_line, sep_baseline=1) + ⎟ ⎟ ⎟ ⎟ ⎟ ⎟ + ⎛1 0 0⎞ ⎟ ⎛0 1 0⎞ ⎟ ⎛1 0 0⎞ ⎟ ⎛ 0 1 0⎞ ⎟ ⎛0 0 1⎞ ⎟ ⎛0 1 0⎞ ⎟ ⎛0 0 1⎞ + ⎜0 1 0⎟ ⎟ ⎜1 0 0⎟ ⎟ ⎜0 0 1⎟ ⎟ ⎜ 1 -1 1⎟ ⎟ ⎜1 0 0⎟ ⎟ ⎜0 0 1⎟ ⎟ ⎜0 1 0⎟ + ⎝0 0 1⎠ ⎟ ⎝0 0 1⎠ ⎟ ⎝0 1 0⎠ ⎟ ⎝ 0 1 0⎠ ⎟ ⎝0 1 0⎠ ⎟ ⎝1 0 0⎠ ⎟ ⎝1 0 0⎠ + ⎟ ⎟ ⎟ ⎟ ⎟ ⎟ TESTS:: From 1306d1fa55326ba106f578c1a77311afa770fd6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Fri, 2 Feb 2018 09:18:06 +0200 Subject: [PATCH 602/740] Explain algorithm. --- src/doc/en/reference/references/index.rst | 6 ++++++ src/sage/combinat/posets/posets.py | 10 ++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 90bd9e4b861..3005613d2c1 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1653,6 +1653,12 @@ REFERENCES: **M** +.. [Mac1987] Maciej M. SysŁo, + *Minimizing the jump number for partially-ordered sets: a + graph-theoretic approach, II*. + Discrete Mathematics, + Volume 63, Issues 2-3, 1987, Pages 279-295. + .. [MagmaHGM] *Hypergeometric motives* in Magma, http://magma.maths.usyd.edu.au/~watkins/papers/HGM-chapter.pdf diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 3a83075a1cb..3bb9ddd5afb 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -3124,8 +3124,14 @@ def jump_number(self, certificate=False): ALGORITHM: - Basically we test every greedy linear extension of the poset. - The problem is shown to be NP-hard. + It is known that every poset has a greedy linear extension -- + an extension `[e_1, e_2, \ldots, e_n]` where every `e_{i+1}` is + an upper cover of `e_i` if that is possible -- with the smallest + possible number of jumps; see [Mac1987]_. + + Hence it suffices to test only those. We do that by backtracking. + + The problem is proven to be NP-complete. TESTS:: From db535dc8a50256e8b70708e15d4508f07d8e217a Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Fri, 2 Feb 2018 09:08:47 +0100 Subject: [PATCH 603/740] 24411: more imports changed --- src/sage/interfaces/sympy.py | 4 ++-- .../modular/modform_hecketriangle/hecke_triangle_groups.py | 2 +- src/sage/rings/asymptotic/asymptotic_expansion_generators.py | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/interfaces/sympy.py b/src/sage/interfaces/sympy.py index 178830b0e9f..10f966b11b0 100644 --- a/src/sage/interfaces/sympy.py +++ b/src/sage/interfaces/sympy.py @@ -377,7 +377,7 @@ def _sympysage_lgamma(self): sage: assert log_gamma(x)._sympy_() == loggamma(Symbol('x')) sage: assert log_gamma(x) == loggamma(Symbol('x'))._sage_() """ - from sage.functions.other import log_gamma + from sage.functions.gamma import log_gamma return log_gamma(self.args[0]._sage_()) def _sympysage_polygamma(self): @@ -394,7 +394,7 @@ def _sympysage_polygamma(self): sage: integrate(psi(x), x, algorithm='sympy') integrate(psi(x), x) """ - from sage.functions.other import psi + from sage.functions.gamma import psi return psi(self.args[0]._sage_(),self.args[1]._sage_()) def _sympysage_dirac_delta(self): diff --git a/src/sage/modular/modform_hecketriangle/hecke_triangle_groups.py b/src/sage/modular/modform_hecketriangle/hecke_triangle_groups.py index 64bca727b79..196c863c8c8 100644 --- a/src/sage/modular/modform_hecketriangle/hecke_triangle_groups.py +++ b/src/sage/modular/modform_hecketriangle/hecke_triangle_groups.py @@ -20,7 +20,7 @@ from sage.rings.all import ZZ, QQ, AA, AlgebraicField, infinity, PolynomialRing, NumberField from sage.functions.all import cos,exp,sec -from sage.functions.other import psi1 +from sage.functions.gamma import psi1 from sage.symbolic.all import pi,i from sage.matrix.constructor import matrix from sage.misc.latex import latex diff --git a/src/sage/rings/asymptotic/asymptotic_expansion_generators.py b/src/sage/rings/asymptotic/asymptotic_expansion_generators.py index c52c8696327..756ea743a46 100644 --- a/src/sage/rings/asymptotic/asymptotic_expansion_generators.py +++ b/src/sage/rings/asymptotic/asymptotic_expansion_generators.py @@ -883,7 +883,8 @@ def SingularityAnalysis(var, zeta=1, alpha=0, beta=0, delta=0, MonomialGrowthGroup from sage.arith.all import falling_factorial from sage.categories.cartesian_product import cartesian_product - from sage.functions.other import binomial, gamma + from sage.functions.other import binomial + from sage.functions.gamma import gamma from sage.calculus.calculus import limit from sage.misc.cachefunc import cached_function from sage.arith.srange import srange From b93ba19f800d2328088733dc9ec5d16ea07dfc07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Fri, 2 Feb 2018 10:25:18 +0200 Subject: [PATCH 604/740] Add iterator over greedy linear extensions. --- src/sage/combinat/posets/hasse_diagram.py | 50 +++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index fac70e990c0..23bd941344c 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -122,6 +122,56 @@ def linear_extensions(self): """ return self.topological_sort_generator() + def greedy_linear_extensions_iterator(self): + """ + Return an iterator over greedy linear extensions of the Hasse diagram. + + A linear extension `[e_1, e_2, \ldots, e_n]` is *greedy* if for + every `i` either `e_{i+1}` covers `e_i` or all upper covers + of `e_i` have at least one lower cover that is not in + `[e_1, e_2, \ldots, e_i]`. + + Informally said a linear extension is greedy if it "always + goes up when possible" and so has no unnecessary jumps. + + EXAMPLES:: + + sage: from sage.combinat.posets.hasse_diagram import HasseDiagram + sage: N5 = HasseDiagram({0: [1, 2], 2: [3], 1: [4], 3: [4]}) + sage: for l in N5.greedy_linear_extensions_iterator(): + ....: print(l) + [0, 1, 2, 3, 4] + [0, 2, 3, 1, 4] + + TESTS: + + sage: from sage.combinat.posets.hasse_diagram import HasseDiagram + sage: list(HasseDiagram({}).greedy_linear_extensions_iterator()) + [[]] + sage: H = HasseDiagram({42: []}) + sage: list(H.greedy_linear_extensions_iterator()) + [[42]] + """ + N = self.order() + + def greedy_rec(H, linext): + if len(linext) == N: + yield linext + + S = [] + if linext: + S = [x for x in H.neighbors_out(linext[-1]) if all(low in linext for low in H.neighbors_in(x))] + if not S: + S_ = set(self).difference(set(linext)) + S = [x for x in S_ if + not any(low in S_ for low in self.neighbors_in(x))] + + for e in S: + for i_want_python_3 in greedy_rec(H, linext+[e]): + yield i_want_python_3 + + return greedy_rec(self, []) + def is_linear_extension(self, lin_ext=None): r""" Test if an ordering is a linear extension. From 814fe06c809bf1a14961551605420c12a494c7b7 Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Fri, 2 Feb 2018 10:27:03 +0100 Subject: [PATCH 605/740] add to isgci --- src/sage/graphs/isgci.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/graphs/isgci.py b/src/sage/graphs/isgci.py index 23a07671e67..a861b6dfcd2 100644 --- a/src/sage/graphs/isgci.py +++ b/src/sage/graphs/isgci.py @@ -1074,6 +1074,7 @@ def _XML_to_dict(root): graph_classes.Outerplanar = GraphClass("Outerplanar", "gc_110") graph_classes.Perfect = GraphClass("Perfect", "gc_56", recognition_function = lambda x:x.is_perfect()) graph_classes.Planar = GraphClass("Planar", "gc_43", recognition_function = lambda x:x.is_planar()) +graph_classes.Polyhedral = GraphClass("Polyhedral", "gc_986", recognition_function = lambda x:x.is_polyhedral()) graph_classes.Split = GraphClass("Split", "gc_39", recognition_function = lambda x:x.is_split()) graph_classes.Tree = GraphClass("Tree", "gc_342", recognition_function = lambda x:x.is_tree()) graph_classes.UnitDisk = GraphClass("UnitDisk", "gc_389") From d3ff9df99c969661367204f158d8d7d3126b124d Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Fri, 2 Feb 2018 11:02:27 +0100 Subject: [PATCH 606/740] typo --- src/sage/graphs/generic_graph.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 1ccc9e6791c..11173f086ce 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -173,7 +173,7 @@ :meth:`~GenericGraph.is_eulerian` | Return ``True`` if the graph has a (closed) tour that visits each edge exactly once. :meth:`~GenericGraph.is_planar` | Test whether the graph is planar. :meth:`~GenericGraph.is_circular_planar` | Test whether the graph is circular planar (outerplanar) - :meth:`~GenericGraph.is_planar` | Test whether the graph is the graph of the polyhedron. + :meth:`~GenericGraph.is_polyhedral` | Test whether the graph is the graph of the polyhedron. :meth:`~GenericGraph.is_regular` | Return ``True`` if this graph is (`k`-)regular. :meth:`~GenericGraph.is_chordal` | Test whether the given graph is chordal. :meth:`~GenericGraph.is_circulant` | Test whether the graph is a circulant graph. @@ -8156,7 +8156,7 @@ def feedback_vertex_set(self, value_only=False, solver=None, verbose=0, constrai # There is a circuit left. Let's add the corresponding # constraint ! while not isok: - + p.add_constraint(p.sum(b[v] for v in certificate), min=1) if verbose: print("Adding a constraint on circuit: ", certificate) From 8c1b2ef430bbacdbb6047bd2525f3906c578eb79 Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Fri, 2 Feb 2018 11:07:48 +0100 Subject: [PATCH 607/740] another typo --- src/sage/graphs/generic_graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 11173f086ce..d851422b99e 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -173,7 +173,7 @@ :meth:`~GenericGraph.is_eulerian` | Return ``True`` if the graph has a (closed) tour that visits each edge exactly once. :meth:`~GenericGraph.is_planar` | Test whether the graph is planar. :meth:`~GenericGraph.is_circular_planar` | Test whether the graph is circular planar (outerplanar) - :meth:`~GenericGraph.is_polyhedral` | Test whether the graph is the graph of the polyhedron. + :meth:`~GenericGraph.is_polyhedral` | Test whether the graph is the graph of a polyhedron. :meth:`~GenericGraph.is_regular` | Return ``True`` if this graph is (`k`-)regular. :meth:`~GenericGraph.is_chordal` | Test whether the given graph is chordal. :meth:`~GenericGraph.is_circulant` | Test whether the graph is a circulant graph. From c21d398803952e643a76a85b1467444e71d26cb0 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Fri, 2 Feb 2018 11:45:04 +0000 Subject: [PATCH 608/740] Just force a strict umask in general, instead of using the user's umask. Also don't apply the timestamps from the tarball; just use the extraction time instead. --- build/sage_bootstrap/uncompress/action.py | 8 ++--- build/sage_bootstrap/uncompress/tar_file.py | 39 +++++++++++++++++---- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/build/sage_bootstrap/uncompress/action.py b/build/sage_bootstrap/uncompress/action.py index 1191682775e..8b8216660de 100644 --- a/build/sage_bootstrap/uncompress/action.py +++ b/build/sage_bootstrap/uncompress/action.py @@ -73,10 +73,8 @@ def unpack_archive(archive, dirname=None): rename = lambda: os.rename(top_level, dirname) retry(rename, OSError) - # Apply the user's umask to the top-level directory in case it - # wasn't already; see https://trac.sagemath.org/ticket/24567 - umask = os.umask(0o777) - os.umask(umask) - os.chmod(dirname, os.stat(dirname).st_mode & ~umask) + # Apply strict umask to the top-level directory in case it wasn't + # already; see https://trac.sagemath.org/ticket/24567 + os.chmod(dirname, os.stat(dirname).st_mode & ~0o077) finally: os.chdir(prev_cwd) diff --git a/build/sage_bootstrap/uncompress/tar_file.py b/build/sage_bootstrap/uncompress/tar_file.py index e4fc6eb1b22..952ca0e24da 100644 --- a/build/sage_bootstrap/uncompress/tar_file.py +++ b/build/sage_bootstrap/uncompress/tar_file.py @@ -26,19 +26,26 @@ class SageBaseTarFile(tarfile.TarFile): """ - Sage as tarfile.TarFile, but applies the user's current umask to the + Sage as tarfile.TarFile, but applies a strict umask (0077) to the permissions of all extracted files and directories. - This mimics the default behavior of the ``tar`` utility. + Previously this applied the user's current umask per the default behavior + of the ``tar`` utility, but this did not provide sufficiently reliable + behavior in all cases, such as when the user's umask is not strict enough. - See http://trac.sagemath.org/ticket/20218#comment:16 for more background. + This also sets the modified timestamps on all extracted files to the time + of their extraction, not the timestamps stored in the tarball. + + See http://trac.sagemath.org/ticket/20218#comment:16 and + https://trac.sagemath.org/ticket/24567 for more background. """ + + umask = 0o077 + def __init__(self, *args, **kwargs): # Unfortunately the only way to get the current umask is to set it # and then restore it super(SageBaseTarFile, self).__init__(*args, **kwargs) - self.umask = os.umask(0o777) - os.umask(self.umask) @property def names(self): @@ -52,11 +59,17 @@ def names(self): return filter_os_files(self.getnames()) def chmod(self, tarinfo, target): + """Apply ``self.umask`` instead of the permissions in the TarInfo.""" tarinfo = copy.copy(tarinfo) tarinfo.mode &= ~self.umask tarinfo.mode &= ~(stat.S_ISUID | stat.S_ISGID) return super(SageBaseTarFile, self).chmod(tarinfo, target) + def utime(self, tarinfo, target): + """Override to keep the extraction time as the file's timestamp.""" + + return + def extractall(self, path='.', members=None): """ Same as tarfile.TarFile.extractall but allows filenames for @@ -67,7 +80,8 @@ def extractall(self, path='.', members=None): members = [m if isinstance(m, tarfile.TarInfo) else name_to_member[m] for m in members] - return super(SageBaseTarFile, self).extractall(path=path, members=members) + return super(SageBaseTarFile, self).extractall(path=path, + members=members) def extractbytes(self, member): """ @@ -80,6 +94,19 @@ def extractbytes(self, member): reader = self.extractfile(member) return reader.read() + def _extract_member(self, tarinfo, targetpath): + """ + Override to ensure that our custom umask is applied over the entire + directory tree, even for directories that are not explicitly listed in + the tarball. + """ + + old_umask = os.umask(self.umask) + try: + super(SageBaseTarFile, self)._extract_member(tarinfo, targetpath) + finally: + os.umask(old_umask) + class SageTarFile(SageBaseTarFile): """ From ee3e900a3bc309d774de2c191d49b1f853f3ed90 Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Fri, 2 Feb 2018 21:35:48 +0100 Subject: [PATCH 609/740] move function from 'generic_graph' to 'graph' --- src/sage/graphs/generic_graph.py | 49 +------------------------- src/sage/graphs/graph.py | 59 +++++++++++++++++++++++++++++--- 2 files changed, 55 insertions(+), 53 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index d851422b99e..acb5818df5e 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -173,7 +173,6 @@ :meth:`~GenericGraph.is_eulerian` | Return ``True`` if the graph has a (closed) tour that visits each edge exactly once. :meth:`~GenericGraph.is_planar` | Test whether the graph is planar. :meth:`~GenericGraph.is_circular_planar` | Test whether the graph is circular planar (outerplanar) - :meth:`~GenericGraph.is_polyhedral` | Test whether the graph is the graph of a polyhedron. :meth:`~GenericGraph.is_regular` | Return ``True`` if this graph is (`k`-)regular. :meth:`~GenericGraph.is_chordal` | Test whether the given graph is chordal. :meth:`~GenericGraph.is_circulant` | Test whether the graph is a circulant graph. @@ -4125,7 +4124,7 @@ def is_planar(self, on_embedding=None, kuratowski=False, set_embedding=False, se - "Measuring non-planarity": :meth:`~genus`, :meth:`~crossing_number` - :meth:`planar_dual` - :meth:`faces` - - :meth:`is_polyhedral` + - :meth:`~sage.graphs.graph.Graph.is_polyhedral` INPUT: @@ -5203,52 +5202,6 @@ def planar_dual(self, embedding=None): from sage.graphs.graph import Graph return Graph([[tuple(_) for _ in self.faces()], lambda f, g: not set([tuple(reversed(e)) for e in f]).isdisjoint(g)], loops=False) - def is_polyhedral(self): - """ - Test whether the graph is the graph of the polyhedron. - - By a theorem of Steinitz (Satz 43, p. 77 of [St1922]_), - graphs of three-dimensional polyhedra are exactly - the simple 3-vertex-connected planar graphs. - - EXAMPLES:: - - sage: C = graphs.CubeGraph(3) - sage: C.is_polyhedral() - True - sage: K33=graphs.CompleteBipartiteGraph(3, 3) - sage: K33.is_polyhedral() - False - sage: graphs.CycleGraph(17).is_polyhedral() - False - sage: [i for i in range(9) if graphs.CompleteGraph(i).is_polyhedral()] - [4] - - - .. SEEALSO:: - - * :meth:`vertex_connectivity` - * :meth:`is_planar` - - TESTS:: - - sage: G = Graph([[1, 2, 3, 4], [[1, 2], [1,1]]], loops=True) - sage: G.is_polyhedral() - False - - sage: G = Graph([[1, 2, 3], [[1, 2], [3, 1], [1, 2], [2, 3]]], multiedges=True) - sage: G.is_polyhedral() - False - - .. TODO:: - - Implement a faster 3-vertex-connectivity test. - """ - - return (not (self.has_loops() or self.has_multiple_edges()) - and (self.vertex_connectivity() >= 3) - and self.is_planar()) - ### Connectivity def is_connected(self): diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 852e8a06dc9..1b012f33bc4 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -2953,7 +2953,7 @@ def treewidth(self,k=None,certificate=False,algorithm=None): v = next(T[0].vertex_iterator()) for t in T[1:]: tree.add_edge(next(t.vertex_iterator()),v) - return tree + return tree # Forcing k to be defined if k is None: @@ -4131,7 +4131,7 @@ def chromatic_index(self, solver=None, verbose=0): """ if self.order() == 0 or self.size() == 0: return 0 - + from sage.graphs.graph_coloring import edge_coloring return edge_coloring(self, value_only=True, solver=solver, verbose=verbose) @@ -7186,6 +7186,55 @@ def modular_decomposition(self): return relabel(D) + @doc_index("Graph properties") + def is_polyhedral(self): + """ + Test whether the graph is the graph of the polyhedron. + + By a theorem of Steinitz (Satz 43, p. 77 of [St1922]_), + graphs of three-dimensional polyhedra are exactly + the simple 3-vertex-connected planar graphs. + + EXAMPLES:: + + sage: C = graphs.CubeGraph(3) + sage: C.is_polyhedral() + True + sage: K33=graphs.CompleteBipartiteGraph(3, 3) + sage: K33.is_polyhedral() + False + sage: graphs.CycleGraph(17).is_polyhedral() + False + sage: [i for i in range(9) if graphs.CompleteGraph(i).is_polyhedral()] + [4] + + + .. SEEALSO:: + + * :meth:`~sage.graphs.generic_graph.GenericGraph.vertex_connectivity` + * :meth:`~sage.graphs.generic_graph.GenericGraph.is_planar` + * :wikipedia:`Polyhedral_graph` + + TESTS:: + + sage: G = Graph([[1, 2, 3, 4], [[1, 2], [1,1]]], loops=True) + sage: G.is_polyhedral() + False + + sage: G = Graph([[1, 2, 3], [[1, 2], [3, 1], [1, 2], [2, 3]]], multiedges=True) + sage: G.is_polyhedral() + False + + .. TODO:: + + Implement a faster 3-vertex-connectivity test: :trac:`24635`. + """ + + return (not self.has_loops() + and not self.has_multiple_edges() + and (self.vertex_connectivity() >= 3) + and self.is_planar()) + @doc_index("Graph properties") def is_prime(self): r""" @@ -7832,12 +7881,12 @@ def has_perfect_matching(self, algorithm="Edmonds", solver=None, verbose=0): problem: put a binary variable ``b[e]`` on each edge ``e``, and for each vertex ``v``, require that the sum of the values of the edges incident to ``v`` is 1. - + - ``solver`` -- (default: ``None``) specify a Linear Program (LP) solver to be used; if set to ``None``, the default one is used - ``verbose`` -- integer (default: ``0``); sets the level of verbosity: - set to 0 by default, which means quiet (only useful when + set to 0 by default, which means quiet (only useful when ``algorithm == "LP_matching"`` or ``algorithm == "LP"``) For more information on LP solvers and which default solver is @@ -7872,7 +7921,7 @@ def has_perfect_matching(self, algorithm="Edmonds", solver=None, verbose=0): False TESTS:: - + sage: G = graphs.EmptyGraph() sage: all(G.has_perfect_matching(algorithm=algo) for algo in ['Edmonds', 'LP_matching', 'LP']) True From 504dbaf654efa16170bfbc8cf4c76568d35c5caa Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Fri, 2 Feb 2018 12:45:16 +0100 Subject: [PATCH 610/740] Fix building Singular on SunOS --- build/pkgs/singular/package-version.txt | 2 +- build/pkgs/singular/patches/floor.patch | 33 +++++ build/pkgs/singular/patches/floor2.patch | 37 +++++ build/pkgs/singular/patches/pow.patch | 28 ++++ build/pkgs/singular/patches/sunos.diff | 93 ++++++++++++ build/pkgs/singular/patches/sunos.patch | 180 +++++++++++++++++++++++ 6 files changed, 372 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/singular/patches/floor.patch create mode 100644 build/pkgs/singular/patches/floor2.patch create mode 100644 build/pkgs/singular/patches/pow.patch create mode 100644 build/pkgs/singular/patches/sunos.diff create mode 100644 build/pkgs/singular/patches/sunos.patch diff --git a/build/pkgs/singular/package-version.txt b/build/pkgs/singular/package-version.txt index 036600a1cb3..8dfe4bffa7f 100644 --- a/build/pkgs/singular/package-version.txt +++ b/build/pkgs/singular/package-version.txt @@ -1 +1 @@ -4.1.0p3.p1 +4.1.0p3.p2 diff --git a/build/pkgs/singular/patches/floor.patch b/build/pkgs/singular/patches/floor.patch new file mode 100644 index 00000000000..f3b7230c5d5 --- /dev/null +++ b/build/pkgs/singular/patches/floor.patch @@ -0,0 +1,33 @@ +commit 08fd9112fa9a9f938b637cbc4f50aa3f12a7013e +Author: Dima Pasechnik +Date: Sat Feb 3 04:08:10 2018 +0000 + + don't do floor(int) + + This is another catch from an attempt to build Singular on Solaris 11, + this time using gcc 5.4.0. It errors out saying + + walk.cc:4570:103: error: call of overloaded 'floor(int)' is ambiguous + (*next_weight2)[i] = 1 + (*curr_weight)[i] + floor(weight_rad*(*next_weight2)[i]/weight_norm); + + walk.cc:4574:99: error: call of overloaded 'floor(int)' is ambiguous + (*next_weight2)[i] = (*curr_weight)[i] + floor(weight_rad*(*next_weight2)[i]/weight_norm); + +diff --git a/Singular/walk.cc b/Singular/walk.cc +index 08b7658..0ece307 100644 +--- a/Singular/walk.cc ++++ b/Singular/walk.cc +@@ -4566,11 +4566,11 @@ static intvec* MWalkRandomNextWeight(ideal G, intvec* orig_M, intvec* target_wei + { + if((*next_weight2)[i] < 0) + { +- (*next_weight2)[i] = 1 + (*curr_weight)[i] + floor(weight_rad*(*next_weight2)[i]/weight_norm); ++ (*next_weight2)[i] = 1 + (*curr_weight)[i] + weight_rad*(*next_weight2)[i]/weight_norm; + } + else + { +- (*next_weight2)[i] = (*curr_weight)[i] + floor(weight_rad*(*next_weight2)[i]/weight_norm); ++ (*next_weight2)[i] = (*curr_weight)[i] + weight_rad*(*next_weight2)[i]/weight_norm; + } + } + if(test_w_in_ConeCC(G,next_weight2) == 1) diff --git a/build/pkgs/singular/patches/floor2.patch b/build/pkgs/singular/patches/floor2.patch new file mode 100644 index 00000000000..cc32ef1b146 --- /dev/null +++ b/build/pkgs/singular/patches/floor2.patch @@ -0,0 +1,37 @@ +commit 1a13c7c54e8a386019b0bbf897243b395e01ebe5 +Author: Jeroen Demeyer +Date: Sat Feb 3 09:21:08 2018 +0100 + + Avoid some floor() calls + +diff --git a/Singular/walk.cc b/Singular/walk.cc +index 08b7658..bf692d9 100644 +--- a/Singular/walk.cc ++++ b/Singular/walk.cc +@@ -2302,7 +2302,7 @@ static intvec* MwalkNextWeightCC(intvec* curr_weight, intvec* target_weight, + } + for(j=0; j(sqrt(double(weight_norm))); + } + for(i=0; i +Date: Fri Feb 2 12:24:48 2018 +0100 + + fix: use pow(double,double) by explicit casting + +diff --git a/libpolys/coeffs/ffields.cc b/libpolys/coeffs/ffields.cc +index 61d8eff..612ed2d 100644 +--- a/libpolys/coeffs/ffields.cc ++++ b/libpolys/coeffs/ffields.cc +@@ -806,7 +806,7 @@ static BOOLEAN nfCoeffIsEqual (const coeffs r, n_coeffType n, void * parameter) + { + if (n==n_GF) { + GFInfo* p = (GFInfo *)(parameter); +- int c = pow (p->GFChar, p->GFDegree); ++ int c = (int)pow ((double)p->GFChar, (double)p->GFDegree); + if ((c == r->m_nfCharQ) && (strcmp(n_ParameterNames(r)[0], p->GFPar_name) == 0)) + return TRUE; + } +@@ -927,7 +927,7 @@ BOOLEAN nfInitChar(coeffs r, void * parameter) + return TRUE; + } + +- int c = pow (p->GFChar, p->GFDegree); ++ int c = (int)pow ((double)p->GFChar, (double)p->GFDegree); + + nfReadTable(c, r); + diff --git a/build/pkgs/singular/patches/sunos.diff b/build/pkgs/singular/patches/sunos.diff new file mode 100644 index 00000000000..fc9db417a1c --- /dev/null +++ b/build/pkgs/singular/patches/sunos.diff @@ -0,0 +1,93 @@ +Changes to source files to compile Singular on SunOS. + +This patch is not actually applied, only the changes to auto-generated +files in sunos.patch should be applied. + + +commit 6712e907767e5540613b217f792b5f42d2b39ac1 +Author: Dima Pasechnik +Date: Wed Jan 31 12:50:27 2018 +0000 + + link libraries sometimes needed by gethostbyname etc + + On Solaris, in particular, one needs -lsocket -lnsl in the libs + to be linked against. See https://trac.sagemath.org/ticket/24611 + This is taken care by this autoconf macro call. + +diff --git a/configure.ac b/configure.ac +index ecda2a4..a4a21a2 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -155,6 +155,7 @@ m4_ifval([$3], + fi[]dnl + ])]) + ++AX_LIB_SOCKET_NSL + + AC_DEFINE_UNQUOTED([CC],"$CC",[CC]) + AC_DEFINE_UNQUOTED([CXX],"$CXX",[CXX]) +diff --git a/m4/ax_lib_socket_nsl.m4 b/m4/ax_lib_socket_nsl.m4 +new file mode 100644 +index 0000000..54cad68 +--- /dev/null ++++ b/m4/ax_lib_socket_nsl.m4 +@@ -0,0 +1,40 @@ ++# =========================================================================== ++# https://www.gnu.org/software/autoconf-archive/ax_lib_socket_nsl.html ++# =========================================================================== ++# ++# SYNOPSIS ++# ++# AX_LIB_SOCKET_NSL ++# ++# DESCRIPTION ++# ++# This macro figures out what libraries are required on this platform to ++# link sockets programs. ++# ++# The common cases are not to need any extra libraries, or to need ++# -lsocket and -lnsl. We need to avoid linking with libnsl unless we need ++# it, though, since on some OSes where it isn't necessary it will totally ++# break networking. Unisys also includes gethostbyname() in libsocket but ++# needs libnsl for socket(). ++# ++# LICENSE ++# ++# Copyright (c) 2008 Russ Allbery ++# Copyright (c) 2008 Stepan Kasal ++# Copyright (c) 2008 Warren Young ++# ++# Copying and distribution of this file, with or without modification, are ++# permitted in any medium without royalty provided the copyright notice ++# and this notice are preserved. This file is offered as-is, without any ++# warranty. ++ ++#serial 7 ++ ++AU_ALIAS([LIB_SOCKET_NSL], [AX_LIB_SOCKET_NSL]) ++AC_DEFUN([AX_LIB_SOCKET_NSL], ++[ ++ AC_SEARCH_LIBS([gethostbyname], [nsl]) ++ AC_SEARCH_LIBS([socket], [socket], [], [ ++ AC_CHECK_LIB([socket], [socket], [LIBS="-lsocket -lnsl $LIBS"], ++ [], [-lnsl])]) ++]) +commit facbf5bceffaab92df81e58b805ceee5cdc1788e +Author: Hans Schoenemann +Date: Tue Jan 30 17:38:13 2018 +0100 + + fix: tr. #816 (P_PROCS_MODULE_LDFLAGS) + +diff --git a/libpolys/polys/Makefile.am b/libpolys/polys/Makefile.am +index 23d4cae..b222a7f 100644 +--- a/libpolys/polys/Makefile.am ++++ b/libpolys/polys/Makefile.am +@@ -65,7 +65,7 @@ p_Procs_FieldIndep_la_CPPFLAGS = -Dp_Procs_FieldIndep ${P_PROCS_CPPFLAGS_COMMON} + p_Procs_FieldQ_la_CPPFLAGS = -Dp_Procs_FieldQ ${P_PROCS_CPPFLAGS_COMMON} + p_Procs_FieldZp_la_CPPFLAGS = -Dp_Procs_FieldZp ${P_PROCS_CPPFLAGS_COMMON} + +-P_PROCS_MODULE_LDFLAGS = -shared -module -dynamic -export-dynamic -avoid-version -weak_reference_mismatches weak -undefined dynamic_lookup -Wl,-undefined -Wl,dynamic_lookup -flat_namespace $(SINGULAR_LDFLAGS) ++P_PROCS_MODULE_LDFLAGS = -shared -module -dynamic -export-dynamic -avoid-version -weak_reference_mismatches weak -undefined dynamic_lookup -flat_namespace + + p_Procs_FieldGeneral_la_LDFLAGS = ${P_PROCS_MODULE_LDFLAGS} + p_Procs_FieldIndep_la_LDFLAGS = ${P_PROCS_MODULE_LDFLAGS} diff --git a/build/pkgs/singular/patches/sunos.patch b/build/pkgs/singular/patches/sunos.patch new file mode 100644 index 00000000000..42c84d3713f --- /dev/null +++ b/build/pkgs/singular/patches/sunos.patch @@ -0,0 +1,180 @@ +Changes to relevant autogenerated-files as consequence of sunos.diff + +diff -ru singular-4.1.0//configure b/configure +--- singular-4.1.0//configure 2017-04-18 16:55:37.000000000 +0200 ++++ b/configure 2018-02-02 12:57:16.588312504 +0100 +@@ -23526,6 +23533,162 @@ + + + ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gethostbyname" >&5 ++$as_echo_n "checking for library containing gethostbyname... " >&6; } ++if ${ac_cv_search_gethostbyname+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ ac_func_search_save_LIBS=$LIBS ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char gethostbyname (); ++int ++main () ++{ ++return gethostbyname (); ++ ; ++ return 0; ++} ++_ACEOF ++for ac_lib in '' nsl; do ++ if test -z "$ac_lib"; then ++ ac_res="none required" ++ else ++ ac_res=-l$ac_lib ++ LIBS="-l$ac_lib $ac_func_search_save_LIBS" ++ fi ++ if ac_fn_c_try_link "$LINENO"; then : ++ ac_cv_search_gethostbyname=$ac_res ++fi ++rm -f core conftest.err conftest.$ac_objext \ ++ conftest$ac_exeext ++ if ${ac_cv_search_gethostbyname+:} false; then : ++ break ++fi ++done ++if ${ac_cv_search_gethostbyname+:} false; then : ++ ++else ++ ac_cv_search_gethostbyname=no ++fi ++rm conftest.$ac_ext ++LIBS=$ac_func_search_save_LIBS ++fi ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gethostbyname" >&5 ++$as_echo "$ac_cv_search_gethostbyname" >&6; } ++ac_res=$ac_cv_search_gethostbyname ++if test "$ac_res" != no; then : ++ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" ++ ++fi ++ ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing socket" >&5 ++$as_echo_n "checking for library containing socket... " >&6; } ++if ${ac_cv_search_socket+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ ac_func_search_save_LIBS=$LIBS ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char socket (); ++int ++main () ++{ ++return socket (); ++ ; ++ return 0; ++} ++_ACEOF ++for ac_lib in '' socket; do ++ if test -z "$ac_lib"; then ++ ac_res="none required" ++ else ++ ac_res=-l$ac_lib ++ LIBS="-l$ac_lib $ac_func_search_save_LIBS" ++ fi ++ if ac_fn_c_try_link "$LINENO"; then : ++ ac_cv_search_socket=$ac_res ++fi ++rm -f core conftest.err conftest.$ac_objext \ ++ conftest$ac_exeext ++ if ${ac_cv_search_socket+:} false; then : ++ break ++fi ++done ++if ${ac_cv_search_socket+:} false; then : ++ ++else ++ ac_cv_search_socket=no ++fi ++rm conftest.$ac_ext ++LIBS=$ac_func_search_save_LIBS ++fi ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_socket" >&5 ++$as_echo "$ac_cv_search_socket" >&6; } ++ac_res=$ac_cv_search_socket ++if test "$ac_res" != no; then : ++ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" ++ ++else ++ ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for socket in -lsocket" >&5 ++$as_echo_n "checking for socket in -lsocket... " >&6; } ++if ${ac_cv_lib_socket_socket+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ ac_check_lib_save_LIBS=$LIBS ++LIBS="-lsocket -lnsl $LIBS" ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char socket (); ++int ++main () ++{ ++return socket (); ++ ; ++ return 0; ++} ++_ACEOF ++if ac_fn_c_try_link "$LINENO"; then : ++ ac_cv_lib_socket_socket=yes ++else ++ ac_cv_lib_socket_socket=no ++fi ++rm -f core conftest.err conftest.$ac_objext \ ++ conftest$ac_exeext conftest.$ac_ext ++LIBS=$ac_check_lib_save_LIBS ++fi ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_socket" >&5 ++$as_echo "$ac_cv_lib_socket_socket" >&6; } ++if test "x$ac_cv_lib_socket_socket" = xyes; then : ++ LIBS="-lsocket -lnsl $LIBS" ++fi ++ ++fi ++ ++ ++ + + cat >>confdefs.h <<_ACEOF + #define CC "$CC" +diff -ru singular-4.1.0//libpolys/polys/Makefile.in b/libpolys/polys/Makefile.in +--- singular-4.1.0//libpolys/polys/Makefile.in 2017-04-18 16:55:19.000000000 +0200 ++++ b/libpolys/polys/Makefile.in 2018-02-02 12:57:15.961312272 +0100 +@@ -709,7 +709,7 @@ + p_Procs_FieldIndep_la_CPPFLAGS = -Dp_Procs_FieldIndep ${P_PROCS_CPPFLAGS_COMMON} + p_Procs_FieldQ_la_CPPFLAGS = -Dp_Procs_FieldQ ${P_PROCS_CPPFLAGS_COMMON} + p_Procs_FieldZp_la_CPPFLAGS = -Dp_Procs_FieldZp ${P_PROCS_CPPFLAGS_COMMON} +-P_PROCS_MODULE_LDFLAGS = -shared -module -dynamic -export-dynamic -avoid-version -weak_reference_mismatches weak -undefined dynamic_lookup -Wl,-undefined -Wl,dynamic_lookup -flat_namespace $(SINGULAR_LDFLAGS) ++P_PROCS_MODULE_LDFLAGS = -shared -module -dynamic -export-dynamic -avoid-version -weak_reference_mismatches weak -undefined dynamic_lookup -flat_namespace + p_Procs_FieldGeneral_la_LDFLAGS = ${P_PROCS_MODULE_LDFLAGS} + p_Procs_FieldIndep_la_LDFLAGS = ${P_PROCS_MODULE_LDFLAGS} + p_Procs_FieldQ_la_LDFLAGS = ${P_PROCS_MODULE_LDFLAGS} From 81d87e14f98ff5f5c053b97bc0bff4d915e9d0f2 Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Sat, 3 Feb 2018 10:59:04 +0100 Subject: [PATCH 611/740] added 'Predefined Classes' --- src/sage/graphs/isgci.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/graphs/isgci.py b/src/sage/graphs/isgci.py index a861b6dfcd2..f037b8a8a27 100644 --- a/src/sage/graphs/isgci.py +++ b/src/sage/graphs/isgci.py @@ -226,6 +226,10 @@ - :meth:`~sage.graphs.generic_graph.GenericGraph.is_planar` + * - Polyhedral + + - :meth:`~sage.graphs.generic_graph.GenericGraph.is_polyhedral` + * - Split - :meth:`~sage.graphs.graph.Graph.is_split` From 74a45df4482d866ce00634e651ae9b0cd18a86d1 Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Sat, 3 Feb 2018 11:27:11 +0100 Subject: [PATCH 612/740] 'Graph' not 'GenericGraph --- src/sage/graphs/isgci.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/isgci.py b/src/sage/graphs/isgci.py index f037b8a8a27..05e204a09f3 100644 --- a/src/sage/graphs/isgci.py +++ b/src/sage/graphs/isgci.py @@ -228,7 +228,7 @@ * - Polyhedral - - :meth:`~sage.graphs.generic_graph.GenericGraph.is_polyhedral` + - :meth:`~sage.graphs.generic_graph.Graph.is_polyhedral` * - Split From f329b20431ab41788e5b3c4aef793e04a9d67cf9 Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Fri, 2 Feb 2018 10:19:19 +0100 Subject: [PATCH 613/740] 'is_circumscribable' and 'is_inscribable' --- src/doc/en/reference/references/index.rst | 12 +- src/sage/graphs/generic_graph.py | 142 +++++++++++++++++++++- 2 files changed, 148 insertions(+), 6 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 14e69fd3c93..8396e6a6732 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -435,7 +435,7 @@ REFERENCES: http://www.roguebasin.com/index.php?title=Bresenham%27s_Line_Algorithm .. [Bruin-Molnar] \N. Bruin and A. Molnar, *Minimal models for rational - functions in a dynamical setting*, + functions in a dynamical setting*, LMS Journal of Computation and Mathematics, Volume 15 (2012), pp 400-417. @@ -1191,6 +1191,12 @@ REFERENCES: `J`-Ideals of a Matrix Over a Principal Ideal Domain", :arxiv:`1611.10308`, 2016. +.. [HRS1993] ] Craig D. Hodgson, Igor Rivin and Warren D. Smith, \ + *A characterization of convex hyperbolic polyhedra + and of convex polyhedra inscribed in the sphere.* + Bulletin of the American Mathematical Society + 27.2 (1992): 246-251. + .. [HRT2000] \R.B. Howlett, L.J. Rylands, and D.E. Taylor. *Matrix generators for exceptional groups of Lie type*. J. Symbolic Computation. **11** (2000). @@ -1222,7 +1228,7 @@ REFERENCES: **I** .. [IK2010] Kenji Iohara and Yoshiyuki Koga. - *Representation Theory of the Virasoro Algebra*. + *Representation Theory of the Virasoro Algebra*. Springer, (2010). .. [ILS2012] Giuseppe F. Italiano, Luigi Laura, and Federico @@ -2234,7 +2240,7 @@ REFERENCES: p267--275. http://modular.math.washington.edu/papers/stein-watkins/ .. [St1922] Ernst Steinitz, *Polyeder und Raumeinteilungen*. - In *Encyclopädie der Mathematischen Wissenschaften*, Franz Meyer + In *Encyclopädie der Mathematischen Wissenschaften*, Franz Meyer and Hand Mohrmann, eds., volume 3, *Geometrie, erster Teil, zweite Hälfte*, pp. 1--139, Teubner, Leipzig, 1922 diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index acb5818df5e..0c890cb59bb 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -4557,7 +4557,6 @@ def layout_planar(self, set_embedding=False, on_embedding=None, external_face=No raise ValueError('provided embedding is not a valid embedding for %s. Try putting set_embedding=True'%self) else: G.is_planar(set_embedding=True) - # The following is what was breaking the code. It is where we were specifying the external # face ahead of time. This is definitely a TODO: # @@ -5190,8 +5189,8 @@ def planar_dual(self, embedding=None): .. TODO:: - Implement the method for graphs that are not 3-vertex-connected - (or at least have a faster 3-vertex-connectivity test). + Implement the method for graphs that are not 3-vertex-connected, + or at least have a faster 3-vertex-connectivity test (:trac:`24635`). """ self._scream_if_not_simple() @@ -5202,6 +5201,143 @@ def planar_dual(self, embedding=None): from sage.graphs.graph import Graph return Graph([[tuple(_) for _ in self.faces()], lambda f, g: not set([tuple(reversed(e)) for e in f]).isdisjoint(g)], loops=False) + def is_circumscribable(self): + """ + Test whether the graph is the graph of a circumscrbed polyhedron. + + A polyhedron is circumscribed is all of its facets are tangent to a sphere. + By a theorem of Rivin ([HRS1993]_), this can be checked by solving a + linear program. + + EXAMPLES:: + + sage: C = graphs.CubeGraph(3) + sage: C.is_circumscribable() + True + sage: O = graphs.OctahedralGraph() + sage: f = set(flatten(O.faces()[0])) + sage: O.add_edges([[6, i] for i in f]) + sage: O.is_circumscribable() + False + + .. SEEALSO:: + + * :meth:`is_polyhedral` + * :meth:`is_inscribable` + + TESTS:: + + sage: G = graphs.CompleteGraph(5) + sage: G.is_circumscribable() + Traceback (most recent call last): + ... + NotImplementedError: Complete graph is not polyhedral. This method only works for polyhedral graphs. + + .. TODO:: + + Allow the use of other, inexact but faster solvers. + """ + if not self.is_polyhedral(): + raise NotImplementedError('%s is not polyhedral. This method only works for polyhedral graphs.' % str(self)) + G = self.to_undirected() + from sage.numerical.mip import MixedIntegerLinearProgram + # In order to simulate strict inequalities in the following LP, we + # introduce a variable c[0] and maximize it. If it is positive, then + # the LP has a solution, such that all inequalities are strict + # after removing the auxiliary variable c[0]. + M = MixedIntegerLinearProgram(maximization=True, solver="ppl") + e = M.new_variable() + c = M.new_variable() + M.set_min(c[0], -1) + M.set_max(c[0], 1) + M.set_objective(c[0]) + + vertices_dict = {} + for i, v in enumerate(G): + vertices_dict[v] = i + for edge in G.edges(): + sorted_edge = sorted([vertices_dict[edge[0]], vertices_dict[edge[1]]]) + angle = e[sorted_edge[0], sorted_edge[1]] + M.set_min(angle, 0) + M.set_max(angle, 1/2) + M.add_constraint(angle-c[0], min=0) + M.add_constraint(angle+c[0], max=1/2) + + from sage.misc.flatten import flatten + # The faces are completely determinend by the graph structure: + # for polyhedral graph, there is only one way to choose the faces. + faces = [flatten([[vertices_dict[_[0]], vertices_dict[_[1]]] for _ in face]) for face in G.faces()] + D = G.to_directed() + # In order to generate all simple cycles of G, we use the "all_simple_cycles" + # method of directed graphs, generating each cycle twice (in both directions) + # The two sets below make sure only one direction gives rise to an (in)equality + equality_constraints = set([]) + inequality_constraints = set([]) + for scycle in D.all_simple_cycles(): + cycle = [vertices_dict[_] for _ in scycle] + if len(set(cycle)) > 2: + edges = [sorted([cycle[i], cycle[i+1]]) for i in range(len(cycle)-1)] + if any(set(cycle).issubset(_) for _ in faces): + eq = tuple([e[_[0], _[1]] for _ in sorted(edges)]) + equality_constraints.add(eq) + else: + ieq = tuple([e[_[0], _[1]] for _ in sorted(edges)]) + inequality_constraints.add(ieq) + for eq in equality_constraints: + symb_eq = M.sum(eq) + M.add_constraint(symb_eq, min=1, max=1) + for ieq in inequality_constraints: + symb_ieq = M.sum(ieq)-1-c[0] + M.add_constraint(symb_ieq, min=0) + from sage.numerical.mip import MIPSolverException + try: + solution = M.solve() + except MIPSolverException as e: + if str(e) == "PPL : There is no feasible solution": + return False + return solution > 0 + + def is_inscribable(self): + """ + Test whether the graph is the graph of a inscribed polyhedron. + + A polyhedron is inscribed if all of its vertices are on a sphere. + This is dual to the notion of circumscribed polyhedron: + A Polyhedron is inscribed if and only if its polar dual is circumscribed + and hence a graph is inscribable if and only if its planar dual is + circumscribable. + + EXAMPLES:: + + sage: graphs.CubeGraph(3).is_inscribable() + True + sage: C=graphs.CubeGraph(3) + sage: v = C.vertices()[0] + sage: triangle=[_ + v for _ in C.neighbors(v)] + sage: C.add_edges(Combinations(triangle, 2)) + sage: C.add_edges(zip(triangle, C.neighbors(v))) + sage: C.delete_vertex(v) + sage: C.is_inscribable() + False + + .. SEEALSO:: + + * :meth:`is_polyhedral` + * :meth:`is_circumscribable` + + TESTS:: + + sage: G = graphs.CompleteBipartiteGraph(3,3) + sage: G.is_inscribable() + Traceback (most recent call last): + ... + NotImplementedError: Complete bipartite graph is not polyhedral. This method only works for polyhedral graphs. + """ + if not self.is_polyhedral(): + raise NotImplementedError('%s is not polyhedral. This method only works for polyhedral graphs.' % str(self)) + return self.planar_dual().is_circumscribable() + + ### Connectivity def is_connected(self): From ade7514687db9be50d3b3f81e57e2d3a4228bc23 Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Fri, 2 Feb 2018 11:05:50 +0100 Subject: [PATCH 614/740] added to list of methods in the beginning --- src/sage/graphs/generic_graph.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 0c890cb59bb..70cdd220405 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -173,6 +173,8 @@ :meth:`~GenericGraph.is_eulerian` | Return ``True`` if the graph has a (closed) tour that visits each edge exactly once. :meth:`~GenericGraph.is_planar` | Test whether the graph is planar. :meth:`~GenericGraph.is_circular_planar` | Test whether the graph is circular planar (outerplanar) + :meth:`~GenericGraph.is_circumscribable` | Test whether the graph is the graph of a circumscribed polyhedron. + :meth:`~GenericGraph.is_inscribable` | Test whether the graph is the graph of an inscribed polyhedron. :meth:`~GenericGraph.is_regular` | Return ``True`` if this graph is (`k`-)regular. :meth:`~GenericGraph.is_chordal` | Test whether the given graph is chordal. :meth:`~GenericGraph.is_circulant` | Test whether the graph is a circulant graph. @@ -5203,7 +5205,7 @@ def planar_dual(self, embedding=None): def is_circumscribable(self): """ - Test whether the graph is the graph of a circumscrbed polyhedron. + Test whether the graph is the graph of a circumscribed polyhedron. A polyhedron is circumscribed is all of its facets are tangent to a sphere. By a theorem of Rivin ([HRS1993]_), this can be checked by solving a @@ -5299,7 +5301,7 @@ def is_circumscribable(self): def is_inscribable(self): """ - Test whether the graph is the graph of a inscribed polyhedron. + Test whether the graph is the graph of an inscribed polyhedron. A polyhedron is inscribed if all of its vertices are on a sphere. This is dual to the notion of circumscribed polyhedron: From 9db46ad100d7ac80087f99fadf3f909901cd6cbd Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Fri, 2 Feb 2018 22:29:01 +0100 Subject: [PATCH 615/740] move circumscribable and inscribable methods to graph --- src/sage/graphs/generic_graph.py | 138 ------------------------------ src/sage/graphs/graph.py | 141 +++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+), 138 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 70cdd220405..dc5cdd6018d 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -173,8 +173,6 @@ :meth:`~GenericGraph.is_eulerian` | Return ``True`` if the graph has a (closed) tour that visits each edge exactly once. :meth:`~GenericGraph.is_planar` | Test whether the graph is planar. :meth:`~GenericGraph.is_circular_planar` | Test whether the graph is circular planar (outerplanar) - :meth:`~GenericGraph.is_circumscribable` | Test whether the graph is the graph of a circumscribed polyhedron. - :meth:`~GenericGraph.is_inscribable` | Test whether the graph is the graph of an inscribed polyhedron. :meth:`~GenericGraph.is_regular` | Return ``True`` if this graph is (`k`-)regular. :meth:`~GenericGraph.is_chordal` | Test whether the given graph is chordal. :meth:`~GenericGraph.is_circulant` | Test whether the graph is a circulant graph. @@ -5203,142 +5201,6 @@ def planar_dual(self, embedding=None): from sage.graphs.graph import Graph return Graph([[tuple(_) for _ in self.faces()], lambda f, g: not set([tuple(reversed(e)) for e in f]).isdisjoint(g)], loops=False) - def is_circumscribable(self): - """ - Test whether the graph is the graph of a circumscribed polyhedron. - - A polyhedron is circumscribed is all of its facets are tangent to a sphere. - By a theorem of Rivin ([HRS1993]_), this can be checked by solving a - linear program. - - EXAMPLES:: - - sage: C = graphs.CubeGraph(3) - sage: C.is_circumscribable() - True - sage: O = graphs.OctahedralGraph() - sage: f = set(flatten(O.faces()[0])) - sage: O.add_edges([[6, i] for i in f]) - sage: O.is_circumscribable() - False - - .. SEEALSO:: - - * :meth:`is_polyhedral` - * :meth:`is_inscribable` - - TESTS:: - - sage: G = graphs.CompleteGraph(5) - sage: G.is_circumscribable() - Traceback (most recent call last): - ... - NotImplementedError: Complete graph is not polyhedral. This method only works for polyhedral graphs. - - .. TODO:: - - Allow the use of other, inexact but faster solvers. - """ - if not self.is_polyhedral(): - raise NotImplementedError('%s is not polyhedral. This method only works for polyhedral graphs.' % str(self)) - G = self.to_undirected() - from sage.numerical.mip import MixedIntegerLinearProgram - # In order to simulate strict inequalities in the following LP, we - # introduce a variable c[0] and maximize it. If it is positive, then - # the LP has a solution, such that all inequalities are strict - # after removing the auxiliary variable c[0]. - M = MixedIntegerLinearProgram(maximization=True, solver="ppl") - e = M.new_variable() - c = M.new_variable() - M.set_min(c[0], -1) - M.set_max(c[0], 1) - M.set_objective(c[0]) - - vertices_dict = {} - for i, v in enumerate(G): - vertices_dict[v] = i - for edge in G.edges(): - sorted_edge = sorted([vertices_dict[edge[0]], vertices_dict[edge[1]]]) - angle = e[sorted_edge[0], sorted_edge[1]] - M.set_min(angle, 0) - M.set_max(angle, 1/2) - M.add_constraint(angle-c[0], min=0) - M.add_constraint(angle+c[0], max=1/2) - - from sage.misc.flatten import flatten - # The faces are completely determinend by the graph structure: - # for polyhedral graph, there is only one way to choose the faces. - faces = [flatten([[vertices_dict[_[0]], vertices_dict[_[1]]] for _ in face]) for face in G.faces()] - D = G.to_directed() - # In order to generate all simple cycles of G, we use the "all_simple_cycles" - # method of directed graphs, generating each cycle twice (in both directions) - # The two sets below make sure only one direction gives rise to an (in)equality - equality_constraints = set([]) - inequality_constraints = set([]) - for scycle in D.all_simple_cycles(): - cycle = [vertices_dict[_] for _ in scycle] - if len(set(cycle)) > 2: - edges = [sorted([cycle[i], cycle[i+1]]) for i in range(len(cycle)-1)] - if any(set(cycle).issubset(_) for _ in faces): - eq = tuple([e[_[0], _[1]] for _ in sorted(edges)]) - equality_constraints.add(eq) - else: - ieq = tuple([e[_[0], _[1]] for _ in sorted(edges)]) - inequality_constraints.add(ieq) - for eq in equality_constraints: - symb_eq = M.sum(eq) - M.add_constraint(symb_eq, min=1, max=1) - for ieq in inequality_constraints: - symb_ieq = M.sum(ieq)-1-c[0] - M.add_constraint(symb_ieq, min=0) - from sage.numerical.mip import MIPSolverException - try: - solution = M.solve() - except MIPSolverException as e: - if str(e) == "PPL : There is no feasible solution": - return False - return solution > 0 - - def is_inscribable(self): - """ - Test whether the graph is the graph of an inscribed polyhedron. - - A polyhedron is inscribed if all of its vertices are on a sphere. - This is dual to the notion of circumscribed polyhedron: - A Polyhedron is inscribed if and only if its polar dual is circumscribed - and hence a graph is inscribable if and only if its planar dual is - circumscribable. - - EXAMPLES:: - - sage: graphs.CubeGraph(3).is_inscribable() - True - sage: C=graphs.CubeGraph(3) - sage: v = C.vertices()[0] - sage: triangle=[_ + v for _ in C.neighbors(v)] - sage: C.add_edges(Combinations(triangle, 2)) - sage: C.add_edges(zip(triangle, C.neighbors(v))) - sage: C.delete_vertex(v) - sage: C.is_inscribable() - False - - .. SEEALSO:: - - * :meth:`is_polyhedral` - * :meth:`is_circumscribable` - - TESTS:: - - sage: G = graphs.CompleteBipartiteGraph(3,3) - sage: G.is_inscribable() - Traceback (most recent call last): - ... - NotImplementedError: Complete bipartite graph is not polyhedral. This method only works for polyhedral graphs. - """ - if not self.is_polyhedral(): - raise NotImplementedError('%s is not polyhedral. This method only works for polyhedral graphs.' % str(self)) - return self.planar_dual().is_circumscribable() - ### Connectivity diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 1b012f33bc4..1868ca150c0 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -7213,6 +7213,8 @@ def is_polyhedral(self): * :meth:`~sage.graphs.generic_graph.GenericGraph.vertex_connectivity` * :meth:`~sage.graphs.generic_graph.GenericGraph.is_planar` + * :meth:`is_circumscribable` + * :meth:`is_inscribable` * :wikipedia:`Polyhedral_graph` TESTS:: @@ -7235,6 +7237,145 @@ def is_polyhedral(self): and (self.vertex_connectivity() >= 3) and self.is_planar()) + @doc_index("Graph properties") + def is_circumscribable(self): + """ + Test whether the graph is the graph of a circumscribed polyhedron. + + A polyhedron is circumscribed is all of its facets are tangent to a sphere. + By a theorem of Rivin ([HRS1993]_), this can be checked by solving a + linear program. + + EXAMPLES:: + + sage: C = graphs.CubeGraph(3) + sage: C.is_circumscribable() + True + sage: O = graphs.OctahedralGraph() + sage: f = set(flatten(O.faces()[0])) + sage: O.add_edges([[6, i] for i in f]) + sage: O.is_circumscribable() + False + + .. SEEALSO:: + + * :meth:`is_polyhedral` + * :meth:`is_inscribable` + + TESTS:: + + sage: G = graphs.CompleteGraph(5) + sage: G.is_circumscribable() + Traceback (most recent call last): + ... + NotImplementedError: Complete graph is not polyhedral. This method only works for polyhedral graphs. + + .. TODO:: + + Allow the use of other, inexact but faster solvers. + """ + if not self.is_polyhedral(): + raise NotImplementedError('%s is not polyhedral. This method only works for polyhedral graphs.' % str(self)) + G = self.to_undirected() + from sage.numerical.mip import MixedIntegerLinearProgram + # In order to simulate strict inequalities in the following LP, we + # introduce a variable c[0] and maximize it. If it is positive, then + # the LP has a solution, such that all inequalities are strict + # after removing the auxiliary variable c[0]. + M = MixedIntegerLinearProgram(maximization=True, solver="ppl") + e = M.new_variable() + c = M.new_variable() + M.set_min(c[0], -1) + M.set_max(c[0], 1) + M.set_objective(c[0]) + + vertices_dict = {} + for i, v in enumerate(self): + vertices_dict[v] = i + for edge in self.edges(): + sorted_edge = sorted([vertices_dict[edge[0]], vertices_dict[edge[1]]]) + angle = e[sorted_edge[0], sorted_edge[1]] + M.set_min(angle, 0) + M.set_max(angle, ZZ(1)/ZZ(2)) + M.add_constraint(angle-c[0], min=0) + M.add_constraint(angle+c[0], max=ZZ(1)/ZZ(2)) + + from sage.misc.flatten import flatten + # The faces are completely determinend by the graph structure: + # for polyhedral graph, there is only one way to choose the faces. + faces = [flatten([[vertices_dict[_[0]], vertices_dict[_[1]]] for _ in face]) for face in self.faces()] + D = self.to_directed() + # In order to generate all simple cycles of G, we use the "all_simple_cycles" + # method of directed graphs, generating each cycle twice (in both directions) + # The two sets below make sure only one direction gives rise to an (in)equality + equality_constraints = set([]) + inequality_constraints = set([]) + for scycle in D.all_simple_cycles(): + cycle = [vertices_dict[_] for _ in scycle] + if len(set(cycle)) > 2: + edges = [sorted([cycle[i], cycle[i+1]]) for i in range(len(cycle)-1)] + if any(set(cycle).issubset(_) for _ in faces): + eq = tuple([e[_[0], _[1]] for _ in sorted(edges)]) + equality_constraints.add(eq) + else: + ieq = tuple([e[_[0], _[1]] for _ in sorted(edges)]) + inequality_constraints.add(ieq) + for eq in equality_constraints: + symb_eq = M.sum(eq) + M.add_constraint(symb_eq, min=1, max=1) + for ieq in inequality_constraints: + symb_ieq = M.sum(ieq)-1-c[0] + M.add_constraint(symb_ieq, min=0) + from sage.numerical.mip import MIPSolverException + try: + solution = M.solve() + except MIPSolverException as e: + if str(e) == "PPL : There is no feasible solution": + return False + return solution > 0 + + @doc_index("Graph properties") + def is_inscribable(self): + """ + Test whether the graph is the graph of an inscribed polyhedron. + + A polyhedron is inscribed if all of its vertices are on a sphere. + This is dual to the notion of circumscribed polyhedron: + A Polyhedron is inscribed if and only if its polar dual is circumscribed + and hence a graph is inscribable if and only if its planar dual is + circumscribable. + + EXAMPLES:: + + sage: graphs.CubeGraph(3).is_inscribable() + True + sage: C=graphs.CubeGraph(3) + sage: v = C.vertices()[0] + sage: triangle=[_ + v for _ in C.neighbors(v)] + sage: C.add_edges(Combinations(triangle, 2)) + sage: C.add_edges(zip(triangle, C.neighbors(v))) + sage: C.delete_vertex(v) + sage: C.is_inscribable() + False + + .. SEEALSO:: + + * :meth:`is_polyhedral` + * :meth:`is_circumscribable` + + TESTS:: + + sage: G = graphs.CompleteBipartiteGraph(3,3) + sage: G.is_inscribable() + Traceback (most recent call last): + ... + NotImplementedError: Complete bipartite graph is not polyhedral. This method only works for polyhedral graphs. + """ + if not self.is_polyhedral(): + raise NotImplementedError('%s is not polyhedral. This method only works for polyhedral graphs.' % str(self)) + return self.planar_dual().is_circumscribable() + + @doc_index("Graph properties") def is_prime(self): r""" From 7a22aa341c8cac36a320e78dbac985c7db06b404 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 18 Jan 2018 10:02:02 -0600 Subject: [PATCH 616/740] Initial round of reviewer changes and reverting is_submodule behavior. --- src/sage/modular/abvar/abvar.py | 6 +- src/sage/modular/abvar/finite_subgroup.py | 8 +- src/sage/modular/hecke/module.py | 4 +- src/sage/modular/hecke/submodule.py | 5 +- src/sage/modular/modform/space.py | 5 +- src/sage/modular/modsym/space.py | 2 +- src/sage/modules/free_module.py | 361 ++++++++-------------- src/sage/modules/free_quadratic_module.py | 12 +- src/sage/modules/quotient_module.py | 62 ++-- 9 files changed, 182 insertions(+), 283 deletions(-) diff --git a/src/sage/modular/abvar/abvar.py b/src/sage/modular/abvar/abvar.py index 8c0caab1d1c..3b66d9a083f 100644 --- a/src/sage/modular/abvar/abvar.py +++ b/src/sage/modular/abvar/abvar.py @@ -48,7 +48,7 @@ from sage.rings.polynomial.polynomial_element import Polynomial from sage.rings.infinity import infinity from sage.rings.fraction_field import FractionField -from sage.modules.free_module import is_FreeModule, EchelonMatrixKey +from sage.modules.free_module import is_FreeModule from sage.modular.arithgroup.all import is_CongruenceSubgroup, is_Gamma0, is_Gamma1, is_GammaH from sage.modular.modsym.all import ModularSymbols from sage.modular.modsym.space import ModularSymbolsSpace @@ -409,9 +409,7 @@ def __richcmp__(self, other, op): # NOTE!! having the same newform level, isogeny class number, # and degen_t does not imply two abelian varieties are equal. # See the docstring for self.label. - lx = EchelonMatrixKey(self.lattice()) - rx = EchelonMatrixKey(other.lattice()) - return richcmp(lx, rx, op) + return self.lattice()._echelon_matrix_richcmp(other.lattice(), op) def __radd__(self,other): """ diff --git a/src/sage/modular/abvar/finite_subgroup.py b/src/sage/modular/abvar/finite_subgroup.py index a3a389e2bcb..d32889b07ef 100644 --- a/src/sage/modular/abvar/finite_subgroup.py +++ b/src/sage/modular/abvar/finite_subgroup.py @@ -101,7 +101,7 @@ from sage.modular.abvar.torsion_point import TorsionPoint from sage.modules.module import Module -from sage.modules.free_module import is_FreeModule, EchelonMatrixKey +from sage.modules.free_module import is_FreeModule from sage.structure.element import ModuleElement from sage.structure.gens_py import abelian_iterator from sage.structure.sequence import Sequence @@ -253,10 +253,10 @@ def __richcmp__(self, other, op): if not A.in_same_ambient_variety(B): return richcmp(A.ambient_variety(), B.ambient_variety(), op) L = A.lattice() + B.lattice() - lx = EchelonMatrixKey(other.lattice() + L) - rx = EchelonMatrixKey(self.lattice() + L) + lx = other.lattice() + L + rx = self.lattice() + L # order gets reversed in passing to lattices. - return richcmp(lx, rx, op) + return lx._echelon_matrix_richcmp(rx, op) def is_subgroup(self, other): """ diff --git a/src/sage/modular/hecke/module.py b/src/sage/modular/hecke/module.py index c2711917306..5f467cb69b5 100644 --- a/src/sage/modular/hecke/module.py +++ b/src/sage/modular/hecke/module.py @@ -1027,8 +1027,8 @@ def decomposition(self, bound=None, anemic=True, height_guess=1, sort_by_basis = if anemic: self.__is_splittable_anemic = len(D) > 1 from sage.modules.free_module import EchelonMatrixKey - D.sort(key = None if not sort_by_basis - else lambda ss: EchelonMatrixKey(ss.free_module())) + D.sort(key=None if not sort_by_basis + else lambda ss: EchelonMatrixKey(ss.free_module())) D.set_immutable() self.__decomposition[key] = D for i in range(len(D)): diff --git a/src/sage/modular/hecke/submodule.py b/src/sage/modular/hecke/submodule.py index c34bcb68ec2..e3276453b11 100644 --- a/src/sage/modular/hecke/submodule.py +++ b/src/sage/modular/hecke/submodule.py @@ -23,7 +23,6 @@ import sage.misc.misc as misc from sage.misc.cachefunc import cached_method from sage.structure.richcmp import richcmp_method, richcmp_not_equal, richcmp -from sage.modules.free_module import EchelonMatrixKey import sage.modules.all from . import module @@ -190,9 +189,7 @@ def __richcmp__(self, other, op): rx = other.ambient() if lx != rx: return richcmp_not_equal(lx, rx, op) - lx = EchelonMatrixKey(self.free_module()) - rx = EchelonMatrixKey(other.free_module()) - return richcmp(lx, rx, op) + return self.free_module()._echelon_matrix_richcmp(other.free_module(), op) ################################ # Semi-Private functions diff --git a/src/sage/modular/modform/space.py b/src/sage/modular/modform/space.py index 3a0127a528b..eb704447386 100644 --- a/src/sage/modular/modform/space.py +++ b/src/sage/modular/modform/space.py @@ -1166,10 +1166,7 @@ def __richcmp__(self, x, op): if self.is_ambient() or x.is_ambient(): return richcmp(self.dimension(), x.dimension(), op) else: - from sage.modules.free_module import EchelonMatrixKey - lx = EchelonMatrixKey(self.free_module()) - rx = EchelonMatrixKey(x.free_module()) - return richcmp(lx, rx, op) + return self.free_module()._echelon_matrix_richcmp(x.free_module(), op) def span_of_basis(self, B): """ diff --git a/src/sage/modular/modsym/space.py b/src/sage/modular/modsym/space.py index 3eaf2e63b4d..86f6995d5f3 100644 --- a/src/sage/modular/modsym/space.py +++ b/src/sage/modular/modsym/space.py @@ -145,7 +145,7 @@ def __richcmp__(self, other, op): rx = other.dimension() if lx != rx: return richcmp_not_equal(lx, rx, op) - + lx = EchelonMatrixKey(self.free_module()) rx = EchelonMatrixKey(other.free_module()) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 6feb1ff5dc1..3e2d4b0759f 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -179,10 +179,9 @@ from sage.categories.principal_ideal_domains import PrincipalIdealDomains from sage.misc.randstate import current_randstate from sage.structure.sequence import Sequence -from sage.structure.richcmp import ( - richcmp_method,rich_to_bool,op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE) -from sage.structure.richcmp import (rich_to_bool, richcmp, - richcmp_not_equal, revop) +from sage.structure.richcmp import (richcmp_method, rich_to_bool, richcmp, + richcmp_not_equal, revop, + op_LT,op_LE,op_EQ,op_NE,op_GT,op_GE) from sage.misc.cachefunc import cached_method from sage.misc.superseded import deprecation @@ -1036,43 +1035,44 @@ def _element_constructor_(self, x, coerce=True, copy=True, check=True): except ArithmeticError: raise TypeError("element {!r} is not in free module".format(x)) return self.element_class(self, x, coerce, copy) - - def __richcmp__(self,other,op): + + def __richcmp__(self, other, op): """ Rich comparison via containment in the same ambient space. - + Two modules compare if their ambient module/space is equal. Ambient spaces are equal if they have the same base ring, rank and inner product matrix. - - We compare rank three free modules over the integers and - rationals:: + + EXAMPLES: + + We compare rank three free modules over the integers, + rationals, and complex numbers. Note the free modules + ``QQ^3`` (and hence ``ZZ^3``) and ``CC^3`` are incomparable + because of the different ambient vector spaces:: sage: QQ^3 <= CC^3 doctest:warning ... DeprecationWarning: The default order on free modules has changed. The old ordering is in sage.modules.free_module.EchelonMatrixKey See http://trac.sagemath.org/23878 for details. - True + False sage: CC^3 <= QQ^3 False sage: QQ^3 <= QQ^3 True - :: - - sage: Q = QQ; Z = ZZ; C=CC - sage: Q^3 <= Z^3 + sage: QQ^3 <= ZZ^3 False - sage: Z^3 <= Q^3 - True - sage: Z^3 <= C^3 + sage: ZZ^3 <= QQ^3 True - sage: C^3 <= Z^3 + sage: ZZ^3 <= CC^3 False - - Comparison with a sub-module:: - + sage: CC^3 <= ZZ^3 + False + + Comparison with a submodule:: + sage: A = QQ^3 sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) sage: V @@ -1091,30 +1091,29 @@ def __richcmp__(self,other,op): sage: L2 <= L1 True sage: L1 <= L2 - False + False - More exotic comparisons:: + More exotic comparisons:: - sage: R1=ZZ[sqrt(2)] - sage: F1=R1^3 - sage: V1=F1.span([[sqrt(2),sqrt(2),0]]) - sage: F2=ZZ^3 - sage: V2=F2.span([[2,2,0]]) - sage: V2<=V1 - True - sage: V1<=V2 + sage: R1 = ZZ[sqrt(2)] + sage: F1 = R1^3 + sage: V1 = F1.span([[sqrt(2),sqrt(2),0]]) + sage: F2 = ZZ^3 + sage: V2 = F2.span([[2,2,0]]) + sage: V2 <= V1 # Different ambient vector spaces + False + sage: V1 <= V2 False - sage: R2=GF(5)[x] - sage: F3=R2^3 - sage: V3=F3.span([[x^5-1,1+x+x^2+x^3+x^4,0]]) - sage: W3=F3.span([[1,1,0],[0,4,0]]) - sage: V3<=W3 + sage: R2 = GF(5)[x] + sage: F3 = R2^3 + sage: V3 = F3.span([[x^5-1,1+x+x^2+x^3+x^4,0]]) + sage: W3 = F3.span([[1,1,0],[0,4,0]]) + sage: V3 <= W3 True - sage: W3<=V3 + sage: W3 <= V3 False - We compare a one dimensional space to a two dimensional - space:: + We compare a one dimensional space to a two dimensional space:: sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) sage: M = span([[5,6,7]], QQ) @@ -1154,29 +1153,16 @@ def __richcmp__(self,other,op): TESTS:: - We compare rank three free modules over the integers and - rationals:: - - sage: QQ^3 < CC^3 - True - sage: CC^3 < QQ^3 - False - sage: QQ^3 < QQ^3 - False - - :: - - sage: Q = QQ; Z = ZZ; C=CC - sage: Q^3 < Z^3 + sage: QQ^3 < ZZ^3 False - sage: Z^3 < Q^3 - True - sage: Z^3 < C^3 + sage: ZZ^3 < QQ^3 True - sage: C^3 < Z^3 + sage: ZZ^3 < CC^3 + False + sage: CC^3 < ZZ^3 False - Comparison with a sub-module:: + Comparison with a submodule:: sage: A = QQ^3 sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) @@ -1196,10 +1182,9 @@ def __richcmp__(self,other,op): sage: L2 < L1 True sage: L1 < L2 - False + False - We compare a `\ZZ`-module to a one-dimensional - space:: + We compare a `\ZZ`-module to a one-dimensional space:: sage: V = span([[5,6,7]], ZZ).scale(1/11); V Free module of degree 3 and rank 1 over Integer Ring @@ -1211,17 +1196,17 @@ def __richcmp__(self,other,op): sage: M < V False - We compare rank three free modules over the integers and - rationals:: + We compare rank three free modules over the rationals and + complex numbers:: sage: QQ^3 >= CC^3 False sage: CC^3 >= QQ^3 - True + False sage: QQ^3 >= QQ^3 True - Comparison with a sub-module:: + Comparison with a submodule:: sage: A = QQ^3 sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) @@ -1243,17 +1228,17 @@ def __richcmp__(self,other,op): sage: L1 >= L2 True - We compare rank three free modules over the integers and - rationals:: + We compare rank three free modules over the rationals and + complex numbers:: sage: QQ^3 > CC^3 False sage: CC^3 > QQ^3 - True + False sage: QQ^3 > QQ^3 False - Comparison with a sub-module:: + Comparison with a submodule:: sage: A = QQ^3 sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) @@ -1278,7 +1263,7 @@ def __richcmp__(self,other,op): if self is other: return rich_to_bool(op, 0) if not isinstance(other, FreeModule_generic): - return NotImplemented + return NotImplemented # Check equality first if needed if op == op_EQ: return self._eq(other) @@ -1295,100 +1280,33 @@ def __richcmp__(self,other,op): if op == op_GT: return (not self._eq(other)) and other.is_submodule(self) - def _eq(self,other): + def _eq(self, other): r""" - Return if this free module is equal to other. + Return if ``self`` is equal to ``other``. Ambient spaces are considered equal if they have the same rank, basering and inner product matrix. Modules in the same ambient space are partially ordered by inclusion. - EXAMPLES: - - We compare rank three free modules over the integers and - rationals:: - - sage: QQ^3 < CC^3 - doctest:warning - ... - DeprecationWarning: The default order on free modules has changed. The old ordering is in sage.modules.free_module.EchelonMatrixKey - See http://trac.sagemath.org/23878 for details. - True - sage: CC^3 < QQ^3 - False - sage: CC^3 > QQ^3 - True - - :: - - sage: Q = QQ; Z = ZZ - sage: Q^3 == Z^3 - False - sage: Q^3 == Q^3 - True - sage: Z^3 > Z^3 - False - - Comparison with a sub-module:: - - sage: V = span([[1,2,3], [5,6,7], [8,9,10]], QQ) - sage: V - Vector space of degree 3 and dimension 2 over Rational Field - Basis matrix: - [ 1 0 -1] - [ 0 1 2] - sage: A = QQ^3 - sage: V < A - True - sage: A < V - False - - Comparison with a quotient module (see :trac:`10513`):: + EXAMPLES:: - sage: M = QQ^3 / [[1,2,3]] - sage: V = QQ^2 - sage: V == M - False - sage: M == V + sage: L = IntegralLattice("U") + sage: L is IntegralLattice("U") False - - We create the module `\ZZ^3`, and the submodule generated by - one vector `(1,1,0)`, and check whether certain elements are - in the submodule:: - - sage: R = FreeModule(ZZ, 3) - sage: V = R.submodule([R.gen(0) + R.gen(1)]) - sage: R.gen(0) + R.gen(1) in V + sage: L._eq(IntegralLattice("U")) True - sage: R.gen(0) + 2*R.gen(1) in V - False - - sage: w = (1/2)*(R.gen(0) + R.gen(1)) - sage: w - (1/2, 1/2, 0) - sage: w.parent() - Vector space of dimension 3 over Rational Field - sage: w in V - False - sage: V.coordinates(w) - [1/2] """ - lx = self.rank() - rx = other.rank() - if lx != rx: + if self.rank() != other.rank(): return False - lx = self.base_ring() - rx = other.base_ring() - if lx != rx: + if self.base_ring() != other.base_ring(): return False # We do not want to create an inner product matrix in memory if # self and other use the dot product - if not (self._inner_product_is_dot_product() and other._inner_product_is_dot_product()): - #this only affects free_quadratic_modules - lx = self.inner_product_matrix() - rx = other.inner_product_matrix() - if lx != rx: + if not (self._inner_product_is_dot_product() + and other._inner_product_is_dot_product()): + # This only affects free_quadratic_modules + if self.inner_product_matrix() != other.inner_product_matrix(): return False from sage.modules.quotient_module import FreeModule_ambient_field_quotient lq = isinstance(self, FreeModule_ambient_field_quotient) @@ -1407,9 +1325,7 @@ def _eq(self,other): rx = other.zero_submodule() if lx != rx: return False - lx = isinstance(self, FreeModule_ambient) - rx = isinstance(other, FreeModule_ambient) - if lx and rx: + if isinstance(self, FreeModule_ambient) and isinstance(other, FreeModule_ambient): return True # self and other are not ambient. # but they are contained in the same ambient space @@ -1417,15 +1333,13 @@ def _eq(self,other): # We use self.echelonized_basis_matrix() == other.echelonized_basis_matrix() # with the matrix to avoid a circular reference. from sage.rings.integer_ring import IntegerRing - if self.base_ring().is_field() or self.base_ring() is IntegerRing: + if self.base_ring().is_field() or self.base_ring() is IntegerRing(): # We know that the Hermite normal form is unique here. - lx = self.echelonized_basis_matrix() - rx = other.echelonized_basis_matrix() - return lx == rx + return self.echelonized_basis_matrix() == other.echelonized_basis_matrix() return self.is_submodule(other) and other.is_submodule(self) def is_submodule(self, other): - """ + r""" Return ``True`` if ``self`` is a submodule of ``other``. EXAMPLES:: @@ -1462,7 +1376,7 @@ def is_submodule(self, other): Since :meth:`basis` is not implemented in general, submodule testing does not work for all PID's. However, trivial cases are - already used (and useful) for coercion, e.g. :: + already used (and useful) for coercion, e.g.:: sage: QQ(1/2) * vector(ZZ['x']['y'],[1,2,3,4]) (1/2, 1, 3/2, 2) @@ -1477,24 +1391,34 @@ def is_submodule(self, other): False sage: M1 = QQ^3 / [[1,2,3]] - sage: V1 = span(QQ,[(1,0,0)])+ M1.relations() + sage: V1 = span(QQ,[(1,0,0)]) + M1.relations() sage: M2 = V1 / M1.relations() - sage: M2.is_submodule(M1) - True + sage: M2.is_submodule(M1) # Different ambient vector spaces + False """ if self is other: return True if not isinstance(other, FreeModule_generic): return False + # Removing this try-except block changes the behavior of + # is_submodule for (QQ^2).is_submodule(CC^2) + try: + if self.ambient_vector_space() != other.ambient_vector_space(): + return False + if other is other.ambient_vector_space(): + return True + except AttributeError: + # Not all free modules have an ambient_vector_space. + pass from sage.modules.quotient_module import FreeModule_ambient_field_quotient if isinstance(other, FreeModule_ambient_field_quotient): #if the relations agree we continue with the covers. if isinstance(self, FreeModule_ambient_field_quotient): - if other.relations()!=self.relations(): + if other.relations() != self.relations(): return False self = self.cover() else: - if other.relations()!= 0: + if other.relations() != 0: return False other = other.cover() @@ -1516,11 +1440,11 @@ def is_submodule(self, other): except NotImplementedError: if not R.fraction_field().is_subring(S): raise NotImplementedError("could not determine if %s is a " - "subring of %s" %(R, S)) + "subring of %s" % (R, S)) # now R is a subring of S if other.is_ambient() and S.is_field(): return True - try: + try: M = other.basis_matrix().solve_left(self.basis_matrix()) except ValueError: return False @@ -1883,25 +1807,22 @@ def coordinate_vector(self, v, check=True): def coordinate_module(self, V): r""" - Suppose V is a submodule of ``self`` (or a module commensurable with - self), and that ``self`` is a free module over `R` of rank + Suppose ``V`` is a submodule of ``self`` (or a module commensurable + with ``self``), and that ``self`` is a free module over `R` of rank `n`. Let `\phi` be the map from ``self`` to `R^n` that sends the basis vectors of ``self`` in order to the standard basis of `R^n`. This function returns the image `\phi(V)`. - .. warning:: + .. WARNING:: - If there is no integer `d` such that `dV` is a - submodule of self, then this function will give total - nonsense. + If there is no integer `d` such that `dV` is a submodule + of ``self``, then this function will give total nonsense. EXAMPLES: We illustrate this function with some - `\ZZ`-submodules of `\QQ^3`. - - :: + `\ZZ`-submodules of `\QQ^3`:: sage: V = (ZZ^3).span([[1/2,3,5], [0,1,-3]]) sage: W = (ZZ^3).span([[1/2,4,2]]) @@ -1912,10 +1833,7 @@ def coordinate_module(self, V): sage: V.0 + 4*V.1 (1/2, 4, 2) - In this example, the coordinate module isn't even in - `\ZZ^3`. - - :: + In this example, the coordinate module isn't even in `\ZZ^3`:: sage: W = (ZZ^3).span([[1/4,2,1]]) sage: V.coordinate_module(W) @@ -2898,14 +2816,12 @@ def index_in(self, other): def intersection(self, other): r""" - Return the intersection of ``self`` and other. + Return the intersection of ``self`` and ``other``. EXAMPLES: We intersect two submodules one of which is clearly - contained in the other. - - :: + contained in the other:: sage: A = ZZ^2 sage: M1 = A.span([[1,1]]) @@ -2918,9 +2834,7 @@ def intersection(self, other): True We intersection two submodules of `\ZZ^3` of rank - `2`, whose intersection has rank `1`. - - :: + `2`, whose intersection has rank `1`:: sage: A = ZZ^3 sage: M1 = A.span([[1,1,1], [1,2,3]]) @@ -2931,9 +2845,7 @@ def intersection(self, other): [2 2 2] We compute an intersection of two `\ZZ`-modules that - are not submodules of `\ZZ^2`. - - :: + are not submodules of `\ZZ^2`:: sage: A = ZZ^2 sage: M1 = A.span([[1,2]]).scale(1/6) @@ -2943,10 +2855,7 @@ def intersection(self, other): Echelon basis matrix: [1/3 2/3] - We intersect a `\ZZ`-module with a - `\QQ`-vector space. - - :: + We intersect a `\ZZ`-module with a `\QQ`-vector space:: sage: A = ZZ^3 sage: L = ZZ^3 @@ -2966,9 +2875,12 @@ def intersection(self, other): sage: L. = NumberField(x^2 - x + 2) sage: OL = L.ring_of_integers() - sage: V = L**3; W1 = V.span([[0,w/5,0], [1,0,-1/17]], OL); W2 = V.span([[0,(1-w)/5,0]], OL) + sage: V = L**3 + sage: W1 = V.span([[0,w/5,0], [1,0,-1/17]], OL) + sage: W2 = V.span([[0,(1-w)/5,0]], OL) sage: W1.intersection(W2) - Free module of degree 3 and rank 1 over Maximal Order in Number Field in w with defining polynomial x^2 - x + 2 + Free module of degree 3 and rank 1 over Maximal Order in + Number Field in w with defining polynomial x^2 - x + 2 Echelon basis matrix: [ 0 2/5 0] @@ -3011,16 +2923,14 @@ def intersection(self, other): def __and__(self, other): r""" - Return the intersection of ``self`` and other. - + Return the intersection of ``self`` and ``other``. + See :meth:`intersection`. - + EXAMPLES: We intersect two submodules one of which is clearly - contained in the other. - - :: + contained in the other:: sage: A = ZZ^2 sage: M1 = A.span([[1,1]]) @@ -3033,9 +2943,7 @@ def __and__(self, other): True We intersection two submodules of `\ZZ^3` of rank - `2`, whose intersection has rank `1`. - - :: + `2`, whose intersection has rank `1`:: sage: A = ZZ^3 sage: M1 = A.span([[1,1,1], [1,2,3]]) @@ -4570,9 +4478,9 @@ def __quotient_matrices(self, sub): def quotient_abstract(self, sub, check=True): r""" - Returns an ambient free module isomorphic to the quotient space of - ``self`` modulo sub, together with maps from ``self`` to the quotient, and - a lifting map in the other direction. + Return an ambient free module isomorphic to the quotient space + of ``self`` modulo ``sub``, together with maps from ``self`` to + the quotient, and a lifting map in the other direction. Use ``self.quotient(sub)`` to obtain the quotient module as an object equipped with natural maps in both directions, @@ -4580,23 +4488,19 @@ def quotient_abstract(self, sub, check=True): INPUT: + - ``sub`` -- a submodule of ``self`` or something that can + be turned into one via ``self.submodule(sub)`` - - ``sub`` - a submodule of self, or something that can - be turned into one via self.submodule(sub). - - - ``check`` - (default: True) whether or not to check - that sub is a submodule. - + - ``check`` -- (default: ``True``) whether or not to check + that sub is a submodule OUTPUT: + - ``U`` -- the quotient as an abstract *ambient* free module - - ``U`` - the quotient as an abstract *ambient* free - module + - ``pi`` -- projection map to the quotient - - ``pi`` - projection map to the quotient - - - ``lift`` - lifting map back from quotient + - ``lift`` -- lifting map back from quotient EXAMPLES:: @@ -4611,9 +4515,7 @@ def quotient_abstract(self, sub, check=True): sage: pi(V.0 + V.2) (0) - Another example involving a quotient of one subspace by another. - - :: + Another example involving a quotient of one subspace by another:: sage: A = matrix(QQ,4,4,[0,1,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0]) sage: V = (A^3).kernel() @@ -5803,7 +5705,7 @@ def __hash__(self): True """ return hash(self.__basis) - + def _echelon_matrix_richcmp(self, other, op): r""" Compare the free module ``self`` with other. @@ -5878,7 +5780,7 @@ def _echelon_matrix_richcmp(self, other, op): # with the matrix to avoid a circular reference. return richcmp(self.echelonized_basis_matrix(), other.echelonized_basis_matrix(), op) - + def construction(self): """ Returns the functorial construction of self, namely, the subspace @@ -7361,13 +7263,13 @@ class EchelonMatrixKey(object): A total ordering on free modules for sorting. This class orders modules by their ambient spaces, then by dimension, - then in order by their echelon matrices. If a function returns a list of - free modules, this can be used to sort the output and thus render + then in order by their echelon matrices. If a function returns a list + of free modules, this can be used to sort the output and thus render it deterministic. INPUT: - - a free module + - ``obj`` -- a free module EXAMPLES:: @@ -7400,16 +7302,17 @@ def __init__(self, obj): """ self.obj = obj - def __richcmp__(self,other,op): + def __richcmp__(self, other, op): r""" A total ordering on free modules. TESTS:: - sage: from sage.modules.free_module import EchelonMatrixKey + sage: from sage.modules.free_module import EchelonMatrixKey sage: Y = EchelonMatrixKey(CC^3) sage: Z = EchelonMatrixKey(ZZ^2) sage: Z < Y True """ - return self.obj._echelon_matrix_richcmp(other.obj,op) + return self.obj._echelon_matrix_richcmp(other.obj, op) + diff --git a/src/sage/modules/free_quadratic_module.py b/src/sage/modules/free_quadratic_module.py index 6a4b17b3a25..08ca0c1eb9c 100644 --- a/src/sage/modules/free_quadratic_module.py +++ b/src/sage/modules/free_quadratic_module.py @@ -113,7 +113,7 @@ def FreeQuadraticModule( inner product matrix. EXAMPLES:: - + sage: M2 = FreeQuadraticModule(ZZ,2,inner_product_matrix=[1,2,3,4]) sage: M2 is FreeQuadraticModule(ZZ,2,inner_product_matrix=[1,2,3,4]) True @@ -242,8 +242,8 @@ class FreeQuadraticModule_generic(free_module.FreeModule_generic): TESTS: - We compare rank three free modules over the integers and - rationals:: + We compare rank three free modules over the integers, + rationals, and complex numbers:: sage: Q3 = FreeQuadraticModule(QQ,3,matrix.identity(3)) sage: C3 = FreeQuadraticModule(CC,3,matrix.identity(3)) @@ -253,11 +253,11 @@ class FreeQuadraticModule_generic(free_module.FreeModule_generic): ... DeprecationWarning: The default order on free modules has changed. The old ordering is in sage.modules.free_module.EchelonMatrixKey See http://trac.sagemath.org/23878 for details. - True + False sage: C3 < Q3 False sage: C3 > Q3 - True + False sage: Q3 > Z3 True sage: Q3 < Z3 @@ -277,7 +277,7 @@ class FreeQuadraticModule_generic(free_module.FreeModule_generic): sage: Q3 < V False - The inner_product_matrix is part of the comparison:: + The :meth:`inner_product_matrix` is part of the comparison:: sage: Q3zero = FreeQuadraticModule(QQ,3,matrix.zero(3)) sage: Q3zero == Q3 diff --git a/src/sage/modules/quotient_module.py b/src/sage/modules/quotient_module.py index 470a59ca4d5..5c170f60a67 100644 --- a/src/sage/modules/quotient_module.py +++ b/src/sage/modules/quotient_module.py @@ -69,22 +69,22 @@ class FreeModule_ambient_field_quotient(FreeModule_ambient_field): sage: Z = V.quotient(W) sage: Z == U True - - We create three quotient spaces and compare them:: - - sage: A = QQ^2 - sage: V = A.span_of_basis([[1,0], [1,1]]) - sage: W0 = V.span([V.1, V.0]) - sage: W1 = V.span([V.1]) - sage: W2 = V.span([V.1]) - sage: Q0 = V/W0 - sage: Q1 = V/W1 - sage: Q2 = V/W2 - - sage: Q0 == Q1 - False - sage: Q1 == Q2 - True + + We create three quotient spaces and compare them:: + + sage: A = QQ^2 + sage: V = A.span_of_basis([[1,0], [1,1]]) + sage: W0 = V.span([V.1, V.0]) + sage: W1 = V.span([V.1]) + sage: W2 = V.span([V.1]) + sage: Q0 = V/W0 + sage: Q1 = V/W1 + sage: Q2 = V/W2 + + sage: Q0 == Q1 + False + sage: Q1 == Q2 + True TESTS:: @@ -97,9 +97,10 @@ class FreeModule_ambient_field_quotient(FreeModule_ambient_field): sage: type(loads(dumps(U)) ) """ - def __init__(self, domain, sub, quotient_matrix, lift_matrix, inner_product_matrix = None): + def __init__(self, domain, sub, quotient_matrix, lift_matrix, inner_product_matrix=None): """ - Create this quotient space, from the given domain, sub-module, and quotient_matrix. + Create this quotient space, from the given domain, submodule, + and quotient_matrix. EXAMPLES:: @@ -113,9 +114,9 @@ def __init__(self, domain, sub, quotient_matrix, lift_matrix, inner_product_matr User basis matrix: [1/3 2/3 -1 5/9 1/2] - This creates a quotient vector space, which calls the init method:: + This creates a quotient vector space:: - sage: Q = V / W #indirect doctest + sage: Q = V / W Behold the type of Q:: @@ -175,8 +176,8 @@ def _repr_(self): def __hash__(self): """ - Return hash of this quotient space V/W, which is by definition the hash of - the tuple (V,W). + Return hash of this quotient space `V/W`, which is, by definition, + the hash of the tuple `(V, W)`. EXAMPLES: @@ -190,7 +191,7 @@ def __hash__(self): 954887582 # 32-bit -5856620741060301410 # 64-bit - The hash is just got by hashing both V and W:: + The hash is just got by hashing both `V` and `W`:: sage: hash((V, W)) 954887582 # 32-bit @@ -309,7 +310,8 @@ def _coerce_map_from_(self, M): def quotient_map(self): """ - Given this quotient space `Q = V/W`, return the natural quotient map from V to Q. + Given this quotient space `Q = V / W`, return the natural quotient + map from `V` to `Q`. EXAMPLES:: @@ -332,9 +334,9 @@ def quotient_map(self): return self.__quo_map def lift_map(self): - """ - Given this quotient space `Q = V/W`, return a fixed choice of linear homomorphism - (a section) from Q to V. + r""" + Given this quotient space `Q = V / W`, return a fixed choice of + linear homomorphism (a section) from `Q` to `V`. EXAMPLES:: @@ -353,8 +355,9 @@ def lift_map(self): return self.__lift_map def lift(self, x): - """ - Lift element of this quotient V/W to V by applying the fixed lift homomorphism. + r""" + Lift element of this quotient `V / W` to `V` by applying + the fixed lift homomorphism. The lift is a fixed homomorphism. @@ -427,3 +430,4 @@ def relations(self): [ 0 1 2 3 4 5 6 7 8 9] """ return self.W() + From 307b007422f49466f6238883c4880334c82f12ba Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Sat, 3 Feb 2018 15:57:34 +0100 Subject: [PATCH 617/740] improvements suggested by dcoudert --- src/sage/graphs/graph.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 1868ca150c0..afa2ac376dc 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -7242,7 +7242,7 @@ def is_circumscribable(self): """ Test whether the graph is the graph of a circumscribed polyhedron. - A polyhedron is circumscribed is all of its facets are tangent to a sphere. + A polyhedron is circumscribed if all of its facets are tangent to a sphere. By a theorem of Rivin ([HRS1993]_), this can be checked by solving a linear program. @@ -7268,16 +7268,18 @@ def is_circumscribable(self): sage: G.is_circumscribable() Traceback (most recent call last): ... - NotImplementedError: Complete graph is not polyhedral. This method only works for polyhedral graphs. + NotImplementedError: this method only works for polyhedral graphs .. TODO:: Allow the use of other, inexact but faster solvers. """ if not self.is_polyhedral(): - raise NotImplementedError('%s is not polyhedral. This method only works for polyhedral graphs.' % str(self)) + raise NotImplementedError('this method only works for polyhedral graphs') G = self.to_undirected() from sage.numerical.mip import MixedIntegerLinearProgram + # For a description of the algorithm see paper by Rivin and: + # https://www.ics.uci.edu/~eppstein/junkyard/uninscribable/ # In order to simulate strict inequalities in the following LP, we # introduce a variable c[0] and maximize it. If it is positive, then # the LP has a solution, such that all inequalities are strict @@ -7292,6 +7294,7 @@ def is_circumscribable(self): vertices_dict = {} for i, v in enumerate(self): vertices_dict[v] = i + for edge in self.edges(): sorted_edge = sorted([vertices_dict[edge[0]], vertices_dict[edge[1]]]) angle = e[sorted_edge[0], sorted_edge[1]] @@ -7301,18 +7304,18 @@ def is_circumscribable(self): M.add_constraint(angle+c[0], max=ZZ(1)/ZZ(2)) from sage.misc.flatten import flatten - # The faces are completely determinend by the graph structure: + # The faces are completely determined by the graph structure: # for polyhedral graph, there is only one way to choose the faces. faces = [flatten([[vertices_dict[_[0]], vertices_dict[_[1]]] for _ in face]) for face in self.faces()] D = self.to_directed() # In order to generate all simple cycles of G, we use the "all_simple_cycles" # method of directed graphs, generating each cycle twice (in both directions) # The two sets below make sure only one direction gives rise to an (in)equality - equality_constraints = set([]) - inequality_constraints = set([]) + equality_constraints = set() + inequality_constraints = set() for scycle in D.all_simple_cycles(): cycle = [vertices_dict[_] for _ in scycle] - if len(set(cycle)) > 2: + if len(cycle) > 3: edges = [sorted([cycle[i], cycle[i+1]]) for i in range(len(cycle)-1)] if any(set(cycle).issubset(_) for _ in faces): eq = tuple([e[_[0], _[1]] for _ in sorted(edges)]) @@ -7320,12 +7323,14 @@ def is_circumscribable(self): else: ieq = tuple([e[_[0], _[1]] for _ in sorted(edges)]) inequality_constraints.add(ieq) + for eq in equality_constraints: symb_eq = M.sum(eq) M.add_constraint(symb_eq, min=1, max=1) for ieq in inequality_constraints: symb_ieq = M.sum(ieq)-1-c[0] M.add_constraint(symb_ieq, min=0) + from sage.numerical.mip import MIPSolverException try: solution = M.solve() @@ -7358,6 +7363,10 @@ def is_inscribable(self): sage: C.is_inscribable() False + sage: H = graphs.HerschelGraph() + sage: H.is_inscribable() # long time (1 second) + False + .. SEEALSO:: * :meth:`is_polyhedral` @@ -7369,10 +7378,10 @@ def is_inscribable(self): sage: G.is_inscribable() Traceback (most recent call last): ... - NotImplementedError: Complete bipartite graph is not polyhedral. This method only works for polyhedral graphs. + NotImplementedError: this method only works for polyhedral graphs """ if not self.is_polyhedral(): - raise NotImplementedError('%s is not polyhedral. This method only works for polyhedral graphs.' % str(self)) + raise NotImplementedError('this method only works for polyhedral graphs') return self.planar_dual().is_circumscribable() From 0cfbf5373530841f0f49e45421e610c2a15b0533 Mon Sep 17 00:00:00 2001 From: Ben Salisbury Date: Sat, 3 Feb 2018 15:27:17 -0500 Subject: [PATCH 618/740] Fixed typos and added an example --- .../dynamics/cellular_automata/solitons.py | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/sage/dynamics/cellular_automata/solitons.py b/src/sage/dynamics/cellular_automata/solitons.py index c3c34d0dca5..2c26f045e22 100644 --- a/src/sage/dynamics/cellular_automata/solitons.py +++ b/src/sage/dynamics/cellular_automata/solitons.py @@ -28,10 +28,10 @@ class SolitonCellularAutomata(SageObject): r""" Soliton cellular automata. - Fix some an affine Lie algebra `\mathfrak{g}` with index `I` and + Fix an affine Lie algebra `\mathfrak{g}` with index `I` and classical index set `I_0`. Fix some `r \in I_0`. A *soliton cellular automaton* (SCA) is a discrete (non-linear) dynamical - system is given as follows. The *states* are given by elements of + system given as follows. The *states* are given by elements of a semi-infinite tensor product of Kirillov-Reshetihkin crystals `B^{r,1}`, where only a finite number of factors are not the maximal element `u`, which we will call the *vacuum*. The *time @@ -41,10 +41,10 @@ class SolitonCellularAutomata(SageObject): R(p \otimes u_s) = u_s \otimes T_s(p), - where `p = \cdots \otimes p_3 \otimes p_2 \otimes p_1 \otimes _0` + where `p = \cdots \otimes p_3 \otimes p_2 \otimes p_1 \otimes p_0` is a state and `u_s` is the maximal element of `B^{r,s}`. In more detail, we have `R(p_i \otimes u^{(i)}) = - u^{(i+1)} \otimes \widetilde{p}_i` with u^{(0)} = u_s` and + u^{(i+1)} \otimes \widetilde{p}_i` with `u^{(0)} = u_s` and `T_s(p) = \cdots \otimes \widetilde{p}_1 \otimes \widetilde{p}_0`. This is well-defined since `R(u \otimes u_s) = u_s \otimes u` and `u^{(k)} = u_s` for all `k \gg 1`. @@ -53,7 +53,8 @@ class SolitonCellularAutomata(SageObject): - ``initial_state`` -- the list of elements, can also be a string when ``vacuum`` is 1 and ``n`` is `\mathfrak{sl}_n` - - ``cartan_type`` -- (default: 2) the value `sl_n` or a Cartan type + - ``cartan_type`` -- (default: 2) the value ``n``, for `\mathfrak{sl}_n`, + or a Cartan type - ``r`` -- (default: 1) the node index `r`; typically this corresponds to the height of the vacuum element @@ -189,7 +190,7 @@ class SolitonCellularAutomata(SageObject): 11....112 evoltuions: [(2, 9), (2, 9), (2, 9)] current state: - 44 333 + 44 333 ...11.112......... We construct Example 2.9 from [LS2017]_:: @@ -218,6 +219,32 @@ class SolitonCellularAutomata(SageObject): t: 9 _ ___ _ ...2403.......442....43............................ + Example 3.4 from [LS2017]_:: + + sage: B = SolitonCellularAutomata([['E'],[1],[1],[1],[3],[0], + ....: [1],[1],[1],[1],[2],[-3],[-1],[1]], ['D',4,2]) + sage: B.print_states(10) + t: 0 __ + ..........................................E...30....231. + t: 1 __ + .........................................E..30..231..... + t: 2 _ _ + ........................................E303.21......... + t: 3 _ _ + ....................................303E2.22............ + t: 4 _ _ + ................................303E...222.............. + t: 5 _ _ + ............................303E......12................ + t: 6 _ _ + ........................303E........1.2................. + t: 7 _ _ + ....................303E..........1..2.................. + t: 8 _ _ + ................303E............1...2................... + t: 9 _ _ + ............303E..............1....2.................... + Example 3.12 from [LS2017]_:: sage: B = SolitonCellularAutomata([[-1,3,2],[3,2,1],[3,2,1],[-3,2,1], @@ -900,7 +927,7 @@ def print_states(self, num=None, vacuum_letter='.'): .....................3302....3.................. t: 9 ..................33322.....3................... - t: 10 + t: 10 ...............333.22......3.................... t: 11 ............333..22.......3..................... @@ -1069,7 +1096,7 @@ def cross_repr(i): """ {!s:^7} | - --+-- + --+-- | {!s:^7} """.format(simple_repr(state[i]), simple_repr(final[i]))) From 3b7916bd96e98a848c4ea3133cca191a84661f8b Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Sat, 3 Feb 2018 21:43:53 +0100 Subject: [PATCH 619/740] cleaner rewrite suggested by dcoudert --- src/sage/graphs/graph.py | 55 +++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index afa2ac376dc..78bbc599496 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -7276,7 +7276,7 @@ def is_circumscribable(self): """ if not self.is_polyhedral(): raise NotImplementedError('this method only works for polyhedral graphs') - G = self.to_undirected() + from sage.numerical.mip import MixedIntegerLinearProgram # For a description of the algorithm see paper by Rivin and: # https://www.ics.uci.edu/~eppstein/junkyard/uninscribable/ @@ -7285,51 +7285,42 @@ def is_circumscribable(self): # the LP has a solution, such that all inequalities are strict # after removing the auxiliary variable c[0]. M = MixedIntegerLinearProgram(maximization=True, solver="ppl") - e = M.new_variable() + e = M.new_variable(nonnegative=True) c = M.new_variable() M.set_min(c[0], -1) M.set_max(c[0], 1) M.set_objective(c[0]) - vertices_dict = {} - for i, v in enumerate(self): - vertices_dict[v] = i - - for edge in self.edges(): - sorted_edge = sorted([vertices_dict[edge[0]], vertices_dict[edge[1]]]) - angle = e[sorted_edge[0], sorted_edge[1]] - M.set_min(angle, 0) - M.set_max(angle, ZZ(1)/ZZ(2)) - M.add_constraint(angle-c[0], min=0) - M.add_constraint(angle+c[0], max=ZZ(1)/ZZ(2)) + for u,v in self.edge_iterator(labels=0): + if u > v: + u,v = v,u + M.set_max(e[u,v], ZZ(1)/ZZ(2)) + M.add_constraint(e[u,v] - c[0], min=0) + M.add_constraint(e[u,v] + c[0], max=ZZ(1)/ZZ(2)) from sage.misc.flatten import flatten # The faces are completely determined by the graph structure: # for polyhedral graph, there is only one way to choose the faces. - faces = [flatten([[vertices_dict[_[0]], vertices_dict[_[1]]] for _ in face]) for face in self.faces()] + # We add an equality constraint for each face. + efaces = self.faces() + vfaces = [set(flatten(face)) for face in efaces] + for edges in efaces: + M.add_constraint(M.sum(e[tuple(sorted(_))] for _ in edges) == 1) + # In order to generate all simple cycles of G, which are not faces, + # we use the "all_simple_cycles" method of directed graphs, generating + # each cycle twice (in both directions). The set below make sure only + # one direction gives rise to an (in)equality D = self.to_directed() - # In order to generate all simple cycles of G, we use the "all_simple_cycles" - # method of directed graphs, generating each cycle twice (in both directions) - # The two sets below make sure only one direction gives rise to an (in)equality - equality_constraints = set() inequality_constraints = set() - for scycle in D.all_simple_cycles(): - cycle = [vertices_dict[_] for _ in scycle] + for cycle in D.all_simple_cycles(): if len(cycle) > 3: - edges = [sorted([cycle[i], cycle[i+1]]) for i in range(len(cycle)-1)] - if any(set(cycle).issubset(_) for _ in faces): - eq = tuple([e[_[0], _[1]] for _ in sorted(edges)]) - equality_constraints.add(eq) - else: - ieq = tuple([e[_[0], _[1]] for _ in sorted(edges)]) - inequality_constraints.add(ieq) + edges = (tuple(sorted([cycle[i], cycle[i+1]])) for i in range(len(cycle)-1)) + scycle = set(flatten(cycle)) + if scycle not in vfaces: + inequality_constraints.add(frozenset(edges)) - for eq in equality_constraints: - symb_eq = M.sum(eq) - M.add_constraint(symb_eq, min=1, max=1) for ieq in inequality_constraints: - symb_ieq = M.sum(ieq)-1-c[0] - M.add_constraint(symb_ieq, min=0) + M.add_constraint(M.sum(e[_] for _ in ieq) - c[0] >= 1) from sage.numerical.mip import MIPSolverException try: From e5b0c0f6b7c5b2f3f9d7e7f046d4fab349fce631 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 3 Feb 2018 15:26:31 -0600 Subject: [PATCH 620/740] Added unicode output for UnicodeArt. --- src/sage/typeset/unicode_art.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sage/typeset/unicode_art.py b/src/sage/typeset/unicode_art.py index 8b463db7db9..57199bfe04c 100644 --- a/src/sage/typeset/unicode_art.py +++ b/src/sage/typeset/unicode_art.py @@ -52,6 +52,18 @@ class UnicodeArt(CharacterArt): """ _string_type = text_type + def __unicode__(self): + r""" + Return a unicode representation of ``self``. + + EXAMPLES:: + + sage: i = var('i') + sage: ua = unicode_art(sum(pi^i/factorial(i)*x^i, i, 0, oo)) + sage: unicode(ua) + u' \u03c0\u22c5x\n\u212f ' + """ + return repr(self).decode("utf-8") _unicode_art_factory = CharacterArtFactory( UnicodeArt, text_type, '_unicode_art_', From 68d207256768747f46b52e3b5d3f5b93ffdd1447 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 3 Feb 2018 14:52:27 -0600 Subject: [PATCH 621/740] Add unicode art to CrystalOfLetters. --- src/sage/combinat/crystals/letters.pyx | 78 +++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/crystals/letters.pyx b/src/sage/combinat/crystals/letters.pyx index 26365fe976f..6a92d39cbd2 100644 --- a/src/sage/combinat/crystals/letters.pyx +++ b/src/sage/combinat/crystals/letters.pyx @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Crystals of letters """ @@ -432,6 +433,29 @@ cdef class Letter(Element): return "\\overline{" + repr(-self.value) + "}" return repr(self.value) + def _unicode_art_(self): + r""" + A unicode art representation of ``self``. + + EXAMPLES:: + + sage: C = crystals.Letters(['D', 4]) + sage: unicode_art(C(2)) + 2 + sage: unicode_art(C(-3)) + 3̄ + + sage: C = crystals.Letters(['D',12]) + sage: unicode_art(C(12)) + 12 + sage: unicode_art(C(-11)) + 1̄1̄ + """ + from sage.typeset.unicode_art import UnicodeArt + if self.value < 0: + return UnicodeArt(["".join(let + u"̄" for let in unicode(-self.value))]) + return UnicodeArt([unicode(self.value)]) + def __hash__(self): """ Return the hash value of ``self``. @@ -1384,6 +1408,20 @@ cdef class LetterTuple(Element): """ return repr(self.value) + def _unicode_art_(self): + r""" + A unicode art representation of ``self``. + + EXAMPLES:: + + sage: C = crystals.Letters(['E',6]) + sage: unicode_art(C.list()[:5]) + [ (1), (1̄, 3), (3̄, 4), (4̄, 2, 5), (2̄, 5) ] + """ + from sage.typeset.unicode_art import UnicodeArt + return UnicodeArt([u"({})".format(u", ".join(unicode(x) if x > 0 else unicode(-x) + u"̄" + for x in self.value))]) + def _latex_(self): r""" A latex representation of ``self``. @@ -2317,6 +2355,30 @@ cdef class BKKLetter(Letter): ret = ret + '*' return ret + def _unicode_art_(self): + r""" + A unicode art representation of ``self``. + + EXAMPLES:: + + sage: C = crystals.Letters(['A', [2, 1]]) + sage: unicode_art(C(2)) + 2 + sage: unicode_art(C(-3)) + 3̄ + + sage: C = crystals.Letters(['A', [2, 1]], dual=True) + sage: unicode_art(C(2)) + 2˅ + sage: unicode_art(C(-3)) + 3̄˅ + """ + ret = Letter._unicode_art_(self) + from sage.typeset.unicode_art import UnicodeArt + if self._parent._dual: + ret = ret + UnicodeArt([u'˅']) + return ret + def _latex_(self): r""" A latex representation of ``self``. @@ -2628,6 +2690,20 @@ cdef class LetterWrapped(Element): """ return repr(self._to_tuple()) + def _unicode_art_(self): + r""" + A unicode art representation of ``self``. + + EXAMPLES:: + + sage: C = crystals.Letters(['E', 8]) + sage: unicode_art(C((1,-4,5))) + (1, 4̄, 5) + """ + from sage.typeset.unicode_art import UnicodeArt + return UnicodeArt([u"({})".format(u", ".join(unicode(x) if x > 0 else unicode(-x) + u"̄" + for x in self._to_tuple()))]) + def _latex_(self): r""" A latex representation of ``self``. @@ -2731,7 +2807,7 @@ class ClassicalCrystalOfLettersWrapped(ClassicalCrystalOfLetters): EXAMPLES:: sage: C = crystals.Letters(['E', 8]) - sage: TestSuite(C).run() + sage: TestSuite(C).run() # long time sage: C = crystals.Letters(['F', 4]) sage: TestSuite(C).run() From 251a665884b2b25e508b3d6f76372f384c6366da Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 3 Feb 2018 15:53:41 -0600 Subject: [PATCH 622/740] Convert each entry of a tableau to an ascii/unicode art object. --- src/sage/combinat/tableau.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index 9eb4f088995..4eba89928c3 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -479,11 +479,11 @@ def _unicode_art_(self): └┘ """ from sage.typeset.unicode_art import UnicodeArt - return UnicodeArt(self._ascii_art_table(unicode=True).splitlines()) + return UnicodeArt(self._ascii_art_table(use_unicode=True).splitlines()) _ascii_art_repr = _repr_diagram - def _ascii_art_table(self, unicode=False): + def _ascii_art_table(self, use_unicode=False): """ TESTS: @@ -544,7 +544,7 @@ def _ascii_art_table(self, unicode=False): Unicode version:: sage: t = Tableau([[1,2,15,7],[12,5],[8,10],[9]]) - sage: print(t._ascii_art_table(unicode=True)) + sage: print(t._ascii_art_table(use_unicode=True)) ┌────┬────┬────┬───┐ │ 1 │ 2 │ 15 │ 7 │ ├────┼────┼────┴───┘ @@ -556,7 +556,7 @@ def _ascii_art_table(self, unicode=False): └────┘ sage: Tableaux().options.convention='french' sage: t = Tableau([[1,2,15,7],[12,5],[8,10],[9]]) - sage: print(t._ascii_art_table(unicode=True)) + sage: print(t._ascii_art_table(use_unicode=True)) ┌────┐ │ 9 │ ├────┼────┐ @@ -568,7 +568,7 @@ def _ascii_art_table(self, unicode=False): └────┴────┴────┴───┘ sage: Tableaux.options._reset() """ - if unicode: + if use_unicode: import unicodedata v = unicodedata.lookup('BOX DRAWINGS LIGHT VERTICAL') h = unicodedata.lookup('BOX DRAWINGS LIGHT HORIZONTAL') @@ -581,20 +581,28 @@ def _ascii_art_table(self, unicode=False): uh = unicodedata.lookup('BOX DRAWINGS LIGHT UP AND HORIZONTAL') dh = unicodedata.lookup('BOX DRAWINGS LIGHT DOWN AND HORIZONTAL') vh = unicodedata.lookup('BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL') + from sage.typeset.unicode_art import unicode_art as art else: v = '|' h = '-' dl = dr = ul = ur = vr = vl = uh = dh = vh = '+' + from sage.typeset.ascii_art import ascii_art as art if not self: return dr + dl + '\n' + ur + ul # Get the widths of the columns - str_tab = [[str(_) for _ in row] for row in self] + str_tab = [[art(_) for _ in row] for row in self] col_widths = [1]*len(str_tab[0]) + if use_unicode: + # Special handling of overline not adding to printed length + def get_len(e): + return len(e) - list(unicode(e)).count(u"\u0304") + else: + get_len = len for row in str_tab: for i,e in enumerate(row): - col_widths[i] = max(col_widths[i], len(e)) + col_widths[i] = max(col_widths[i], get_len(e)) matr = [] # just the list of lines l1 = "" @@ -615,7 +623,7 @@ def _ascii_art_table(self, unicode=False): l1 += vh + h*(2+w) else: l1 += uh + h*(2+w) - if unicode: + if use_unicode: l2 += u"{} {:^{width}} ".format(v, e, width=w) else: l2 += "{} {:^{width}} ".format(v, e, width=w) @@ -631,7 +639,7 @@ def _ascii_art_table(self, unicode=False): return "\n".join(matr) else: output = "\n".join(reversed(matr)) - if unicode: + if use_unicode: tr = { ord(dl): ul, ord(dr): ur, ord(ul): dl, ord(ur): dr, From 3a7df827869ce65308e5a3e359959c5cfadbaedb Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 3 Feb 2018 15:54:03 -0600 Subject: [PATCH 623/740] Adding unicode art for tableaux crystals and tensor products. --- .../crystals/tensor_product_element.pyx | 88 +++++++++++++++++++ .../rigged_configurations/kr_tableaux.py | 17 ++++ 2 files changed, 105 insertions(+) diff --git a/src/sage/combinat/crystals/tensor_product_element.pyx b/src/sage/combinat/crystals/tensor_product_element.pyx index a1ffb148842..55b4bb5a784 100644 --- a/src/sage/combinat/crystals/tensor_product_element.pyx +++ b/src/sage/combinat/crystals/tensor_product_element.pyx @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Tensor Products of Crystal Elements @@ -213,6 +214,38 @@ cdef class TensorProductOfCrystalsElement(ImmutableListWithParent): ret += AsciiArt([" # "]) + s return ret + def _unicode_art_(self): + """ + Return a unicode art representation of ``self``. + + EXAMPLES:: + + sage: KT = crystals.TensorProductOfKirillovReshetikhinTableaux(['D',4,1],[[3,3],[2,1],[1,2]]) + sage: unicode_art(KT.module_generators[0]) + ┌───┬───┬───┐ + │ 1 │ 1 │ 1 │ + ├───┼───┼───┤ ┌───┐ + │ 2 │ 2 │ 2 │ │ 1 │ ┌───┬───┐ + ├───┼───┼───┤ ⊗ ├───┤ ⊗ │ 1 │ 1 │ + │ 3 │ 3 │ 3 │ │ 2 │ └───┴───┘ + ├───┼───┼───┤ └───┘ + │ 4̄ │ 4̄ │ 4̄ │ + └───┴───┴───┘ + """ + if self._parent.options.convention == "Kashiwara": + lst = list(reversed(self)) + else: + lst = self + from sage.typeset.unicode_art import unicode_art, UnicodeArt + s = unicode_art(lst[0]) + s._baseline = s._h // 2 + ret = s + for tableau in lst[1:]: + s = unicode_art(tableau) + s._baseline = s._h // 2 + ret += UnicodeArt([u" ⊗ "]) + s + return ret + def _repr_diagram(self): r""" Return a string representation of ``self`` as a diagram. @@ -840,6 +873,44 @@ cdef class CrystalOfTableauxElement(TensorProductOfRegularCrystalsElement): """ return self.to_tableau()._ascii_art_() + def _unicode_art_(self): + """ + Return a unicode art version of ``self``. + + EXAMPLES:: + + sage: T = crystals.Tableaux(['B',4], shape=[1]*3) + sage: unicode_art(T.module_generators[0]) + ┌───┐ + │ 1 │ + ├───┤ + │ 2 │ + ├───┤ + │ 3 │ + └───┘ + sage: T = crystals.Tableaux(['D',4], shape=[2,1]) + sage: t = T.module_generators[0].f_string([1,2,3,4,2,2,3,4]) + sage: unicode_art(t) + ┌───┬───┐ + │ 1 │ 2̄ │ + ├───┼───┘ + │ 3̄ │ + └───┘ + """ + if not self._list: + return Tableau([])._unicode_art_() + cdef list lst = self._list + cdef list tab = [ [lst[0]] ] + cdef int i + for i in range(1,len(self)): + if lst[i-1] < lst[i] or (lst[i-1].value != 0 and lst[i-1] == lst[i]): + tab.append([lst[i]]) + else: + tab[len(tab)-1].append(lst[i]) + for x in tab: + x.reverse() + return Tableau(tab).conjugate()._unicode_art_() + def _latex_(self): r""" EXAMPLES:: @@ -1322,6 +1393,23 @@ cdef class CrystalOfBKKTableauxElement(TensorProductOfSuperCrystalsElement): """ return self.to_tableau()._ascii_art_() + def _unicode_art_(self): + """ + Return a unicode art version of ``self``. + + EXAMPLES:: + + sage: C = crystals.Tableaux(['A',[1,2]], shape=[1,1]) + sage: c = C.an_element() + sage: unicode_art(c) + ┌───┐ + │ 2̄ │ + ├───┤ + │ 1̄ │ + └───┘ + """ + return self.to_tableau()._unicode_art_() + def _latex_(self): r""" Return the latex code of ``self``. diff --git a/src/sage/combinat/rigged_configurations/kr_tableaux.py b/src/sage/combinat/rigged_configurations/kr_tableaux.py index ec1cf32c291..d6e6dcb8063 100644 --- a/src/sage/combinat/rigged_configurations/kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/kr_tableaux.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Kirillov-Reshetikhin Tableaux @@ -1122,6 +1123,22 @@ def _ascii_art_(self): from sage.typeset.ascii_art import AsciiArt return AsciiArt(self._repr_diagram().splitlines()) + def _unicode_art_(self): + r""" + Return a unicode art representation of ``self``. + + EXAMPLES:: + + sage: KRT = crystals.KirillovReshetikhin(['D',4,1], 2, 2, model='KR') + sage: unicode_art(KRT(2,1,-4,3)) + ┌───┬───┐ + │ 1 │ 3 │ + ├───┼───┤ + │ 2 │ 4̄ │ + └───┴───┘ + """ + return self.to_tableau()._unicode_art_() + def to_kirillov_reshetikhin_crystal(self): r""" Construct a From f7e27b888a4e3bc6cfe9f879e52f4ed23db9da9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Sun, 4 Feb 2018 09:27:51 +0200 Subject: [PATCH 624/740] A correction. --- src/sage/combinat/posets/hasse_diagram.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index 23bd941344c..42b057e24e9 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -148,9 +148,9 @@ def greedy_linear_extensions_iterator(self): sage: from sage.combinat.posets.hasse_diagram import HasseDiagram sage: list(HasseDiagram({}).greedy_linear_extensions_iterator()) [[]] - sage: H = HasseDiagram({42: []}) + sage: H = HasseDiagram({0: []}) sage: list(H.greedy_linear_extensions_iterator()) - [[42]] + [[0]] """ N = self.order() From 4e2897ad539a42bbc0a60281ef43f3b1bdd82683 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 4 Feb 2018 03:10:40 -0600 Subject: [PATCH 625/740] Fixing bugs and doing some cleanup. --- src/sage/groups/abelian_gps/abelian_group.py | 6 +- .../abelian_gps/abelian_group_element.py | 2 +- .../groups/abelian_gps/abelian_group_gap.py | 352 ++++++++++-------- 3 files changed, 206 insertions(+), 154 deletions(-) diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py index 08f92658893..e19199b949f 100644 --- a/src/sage/groups/abelian_gps/abelian_group.py +++ b/src/sage/groups/abelian_gps/abelian_group.py @@ -884,11 +884,11 @@ def _gap_init_(self): sage: gap(G) Group( [ f1, f2, f3 ] ) - Requires the optional `gap_packages` for infinite groups:: + Requires the optional ``gap_packages`` for infinite groups:: - sage: G = AbelianGroup(3,[0,3,4],names="abc"); G + sage: G = AbelianGroup(3,[0,3,4], names="abc"); G Multiplicative Abelian group isomorphic to Z x C3 x C4 - sage: G._gap_init_() # optional gap_packages + sage: G._gap_init_() # optional - gap_packages 'AbelianPcpGroup([0, 3, 4])' """ if self.is_finite(): diff --git a/src/sage/groups/abelian_gps/abelian_group_element.py b/src/sage/groups/abelian_gps/abelian_group_element.py index 81ef643c9f9..75d3c18a14f 100644 --- a/src/sage/groups/abelian_gps/abelian_group_element.py +++ b/src/sage/groups/abelian_gps/abelian_group_element.py @@ -163,4 +163,4 @@ def word_problem(self, words): True """ from sage.groups.abelian_gps.abelian_group import AbelianGroup, word_problem - return word_problem(words,self) \ No newline at end of file + return word_problem(words,self) diff --git a/src/sage/groups/abelian_gps/abelian_group_gap.py b/src/sage/groups/abelian_gps/abelian_group_gap.py index bb42526d873..542d40c9758 100644 --- a/src/sage/groups/abelian_gps/abelian_group_gap.py +++ b/src/sage/groups/abelian_gps/abelian_group_gap.py @@ -1,7 +1,7 @@ r""" -Finitely generated abelian groups with gap. +Finitely generated abelian groups with GAP. -This module provides a python wrapper for abelian groups in gap. +This module provides a python wrapper for abelian groups in GAP. EXAMPLES:: @@ -9,19 +9,18 @@ sage: AbelianGroupGap([3,5]) Abelian group with gap, generator orders (3, 5) -For infinite abelian groups we use the gap package Polycyclic:: +For infinite abelian groups we use the GAP package ``Polycyclic``:: - sage: AbelianGroupGap([3,0]) # optional gap_packages + sage: AbelianGroupGap([3,0]) # optional - gap_packages Abelian group with gap, generator orders (3, 0) AUTHORS: - Simon Brandhorst (2018-01-17): initial version - """ # **************************************************************************** -# Copyright (C) 2018 SIMON BRANDHORST +# Copyright (C) 2018 Simon Brandhorst # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -29,6 +28,7 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ # **************************************************************************** + from sage.groups.libgap_wrapper import ParentLibGAP, ElementLibGAP from sage.groups.libgap_mixin import GroupMixinLibGAP from sage.groups.group import AbelianGroup as AbelianGroupBase @@ -39,50 +39,16 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.categories.groups import Groups - -def AbelianGroupGap(generator_orders): - r""" - Create the multiplicative abelian group with given orders of generators. - - INPUT: - - - ``generator_orders`` -- a list of nonnegative integers where `0` gives a factor isomorphic to `\ZZ`. - - OUTPUT: - - - an abelian group - - EXAMPLES:: - - sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap - sage: AbelianGroupGap([3,6]) - Abelian group with gap, generator orders (3, 6) - sage: AbelianGroupGap([3,6,5]) - Abelian group with gap, generator orders (3, 6, 5) - sage: AbelianGroupGap([3,6,0]) # optional gap_packages - Abelian group with gap, generator orders (3, 6, 0) - """ - generator_orders = tuple(ZZ(e) for e in generator_orders) - if not all([e >= 0 for e in generator_orders]): - return ValueError("Generator orders must be nonnegative") - category = Groups().Commutative() - if 0 in generator_orders: - category = category.Infinite() - else: - category = category.Finite().Enumerated() - polycyclic_package = libgap.LoadPackage("Polycyclic") - return AbelianGroupAmbient_gap(generator_orders, polycyclic_package=polycyclic_package, category=category) - class AbelianGroupElement_gap(ElementLibGAP): r""" An element of an abelian group via libgap. EXAMPLES:: - sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap - sage: G = AbelianGroupGap([3,6]) - sage: G.gens() - (f1, f2) + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([3,6]) + sage: G.gens() + (f1, f2) """ def __init__(self, parent, x, check=True): """ @@ -94,7 +60,7 @@ def __init__(self, parent, x, check=True): - ``parent`` -- an instance of :class:`AbelianGroup_gap` - ``x`` -- an instance of :class:`sage.libs.gap.element.GapElement` - - ``check`` -- boolean (default: ``True``) check + - ``check`` -- boolean (default: ``True``); check if ``x`` is an element of the group TESTS:: @@ -104,9 +70,8 @@ def __init__(self, parent, x, check=True): sage: g = G.an_element() sage: TestSuite(g).run() """ - if check: - if not x in parent.gap(): - raise ValueError("%s is not in the group %s" %(x, parent)) + if check and x not in parent.gap(): + raise ValueError("%s is not in the group %s" % (x, parent)) ElementLibGAP.__init__(self, parent, x) def __hash__(self): @@ -149,7 +114,7 @@ def exponents(self): OUTPUT: - - a tuple of sage integers + - a tuple of integers EXAMPLES:: @@ -165,32 +130,24 @@ def exponents(self): f1 sage: s.exponents() (1,) - sage: G = AbelianGroupGap([4,7,0]) # optional - gap_packages - sage: gens = G.gens() - sage: g = gens[0]^2 * gens[1]^4 * gens[2]^8 - sage: g.exponents() - (2, 4, 8) """ P = self.parent() - if (not P.is_subgroup()) and P._with_pc: - exp = self.gap().Exponents().sage() - else: - # works only for small groups - # as gap has problems to solve the word problem - P = self.parent() - x = libgap.Factorization(P.gap(), self.gap()) - L = x.ExtRepOfObj().sage() - Lgens = L[::2] - Lexpo = L[1::2] - exp = [] - orders = P.gens_orders() - i = 0 - for k in range(len(P.gens())): - if not k+1 in Lgens: - exp.append(0) - else: - i = Lgens.index(k+1) - exp.append(Lexpo[i] % orders[k]) + # works only for small groups + # as gap has problems to solve the word problem + P = self.parent() + x = libgap.Factorization(P.gap(), self.gap()) + L = x.ExtRepOfObj().sage() + Lgens = L[::2] + Lexpo = L[1::2] + exp = [] + orders = P.gens_orders() + i = 0 + for k in range(len(P.gens())): + if not k+1 in Lgens: + exp.append(0) + else: + i = Lgens.index(k+1) + exp.append(Lexpo[i] % orders[k]) return tuple(exp) def order(self): @@ -209,33 +166,61 @@ def order(self): sage: g.order() 4 sage: G = AbelianGroupGap([0]) # optional - gap_packages - sage: g = G.gens()[0] + sage: g = G.gens()[0] # optional - gap_packages sage: g.order() # optional - gap_packages +Infinity """ return self.gap().Order().sage() +class AbelianGroupElement_polycyclic(AbelianGroupElement_gap): + r""" + An element of an abelian group using the GAP package ``Polycyclic``. + + TESTS:: + + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([4,7,0]) # optional - gap_packages + sage: TestSuite(G.an_element()).run() # optional - gap_packages + """ + def exponents(self): + r""" + Return the tuple of exponents of ``self``. + + OUTPUT: + + - a tuple of integers + + EXAMPLES:: + + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([4,7,0]) # optional - gap_packages + sage: gens = G.gens() # optional - gap_packages + sage: g = gens[0]^2 * gens[1]^4 * gens[2]^8 # optional - gap_packages + sage: g.exponents() # optional - gap_packages + (2, 4, 8) + """ + return tuple(self.gap().Exponents().sage()) + class AbelianGroup_gap(UniqueRepresentation, GroupMixinLibGAP, ParentLibGAP, AbelianGroupBase): r""" - Python wrapper for finitely generated abelian groups in gap. + Finitely generated abelian groups implemented in GAP. - Needs the gap package "Polycyclic" in case the group is infinite. + Needs the gap package ``Polycyclic`` in case the group is infinite. INPUT: - - ``G`` -- (default:``None``) a gap group - - ``ambient`` -- (default:``None``) an :class:`AbelianGroup_gap` - - ``polycyclic_package`` -- (default: ``False``) boolean + - ``G`` -- a GAP group - ``category`` -- a category + - ``ambient`` -- (optional) an :class:`AbelianGroupGap` EXAMPLES:: sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap - sage: G = AbelianGroupGap([3,2,5]) + sage: G = AbelianGroupGap([3, 2, 5]) sage: G Abelian group with gap, generator orders (3, 2, 5) """ - def __init__(self, G, ambient=None, polycyclic_package=False, category=None): + def __init__(self, G, category, ambient=None): r""" Create an instance of this class. @@ -244,10 +229,9 @@ def __init__(self, G, ambient=None, polycyclic_package=False, category=None): TESTS:: sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap - sage: G = AbelianGroupGap([3,2,5]) # indirect doctest + sage: G = AbelianGroupGap([3,2,5]) sage: TestSuite(G).run() """ - self._with_pc = polycyclic_package AbelianGroupBase.__init__(self, category=category) ParentLibGAP.__init__(self, G, ambient=ambient) @@ -259,7 +243,7 @@ def _coerce_map_from_(self, S): INPUT: - - ``S`` -- anything. + - ``S`` -- anything OUTPUT: @@ -276,16 +260,17 @@ def _coerce_map_from_(self, S): sage: S._coerce_map_from_(G) False """ - if isinstance(S,AbelianGroup_gap): + if isinstance(S, AbelianGroup_gap): return S.is_subgroup_of(self) + return super(AbelianGroup_gap, self)._coerce_map_from_(S) - def _element_constructor_(self,x,check=True): + def _element_constructor_(self, x, check=True): r""" Defines coercions and conversions. INPUT: - - ``x`` -- an element of this group, a gap element + - ``x`` -- an element of this group, a GAP element EXAMPLES:: @@ -304,20 +289,22 @@ def _element_constructor_(self,x,check=True): sage: G(a) f1 - For general fgp_modules conversion is implemented if our group is in smith form:: + For general ``fgp_modules`` conversion is implemented if our + group is in Smith form:: sage: G = AbelianGroupGap([6]) sage: A = ZZ^2 - sage: A = A / A.submodule([2*A.0, 3*A.1]) + sage: e0,e1 = A.gens() + sage: A = A / A.submodule([2*e0, 3*e1]) sage: a = 2 * A.an_element() sage: a (2) sage: G(a) f2 - """ + """ if isinstance(x, AbelianGroupElement_gap): x = x.gap() - elif x==1 or x==(): + elif x == 1 or x == (): x = self.gap().Identity() elif not isinstance(x, GapElement): from sage.groups.abelian_gps.abelian_group_element import AbelianGroupElement @@ -331,11 +318,11 @@ def _element_constructor_(self,x,check=True): exp = x.vector() else: from sage.modules.free_module_element import vector - exp = vector(ZZ,x) + exp = vector(ZZ, x) # turn the exponents into a gap element gens_gap = self.gens() if len(exp) != len(gens_gap): - raise ValueError("Input does not match the number of generators.") + raise ValueError("input does not match the number of generators") x = gens_gap[0]**0 for i in range(len(exp)): x *= gens_gap[i]**exp[i] @@ -349,7 +336,7 @@ def all_subgroups(self): EXAMPLES:: sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap - sage: G = AbelianGroupGap([2,3]) + sage: G = AbelianGroupGap([2, 3]) sage: G.all_subgroups() [Subgroup of Abelian group with gap, generator orders (2, 3) generated by (1,), Subgroup of Abelian group with gap, generator orders (2, 3) generated by (f1,), @@ -361,12 +348,11 @@ def all_subgroups(self): for G in subgroups_gap: S = self.subgroup(G.GeneratorsOfGroup()) subgroups_sage.append(S) - del G return subgroups_sage def is_trivial(self): r""" - Return if this group is the trivial group. + Return ``True`` if this group is the trivial group. EXAMPLES:: @@ -374,6 +360,16 @@ def is_trivial(self): sage: G = AbelianGroupGap([]) sage: G Abelian group with gap, generator orders () + sage: G.is_trivial() + True + sage: AbelianGroupGap([1]).is_trivial() + True + sage: AbelianGroupGap([1,1,1]).is_trivial() + True + sage: AbelianGroupGap([2]).is_trivial() + False + sage: AbelianGroupGap([2,1]).is_trivial() + False """ return 1 == self.order() @@ -393,9 +389,9 @@ def identity(self): @cached_method def elementary_divisors(self): r""" - Return the elementary divisors of the group. + Return the elementary divisors of this group. - See :meth:`sage.groups.abelian_gps.abelian_group_gap.elementary_divisors` + See :meth:`sage.groups.abelian_gps.abelian_group_gap.elementary_divisors`. EXAMPLES:: @@ -407,7 +403,7 @@ def elementary_divisors(self): ediv = self.gap().AbelianInvariants().sage() from sage.matrix.constructor import diagonal_matrix ed = diagonal_matrix(ZZ, ediv).elementary_divisors() - return tuple(d for d in ed if d!=1) + return tuple(d for d in ed if d != 1) @cached_method def exponent(self): @@ -469,7 +465,8 @@ def gens_orders(self): def is_subgroup_of(self, G): r""" - Return if ``self`` is a subgroup of ``G`` considered in the same ambient group. + Return if ``self`` is a subgroup of ``G`` considered in + the same ambient group. EXAMPLES:: @@ -483,8 +480,8 @@ def is_subgroup_of(self, G): sage: S2.is_subgroup_of(S1) False """ - if not isinstance(G,AbelianGroup_gap): - raise ValueError("Input must be an instance of AbelianGroup_gap.") + if not isinstance(G, AbelianGroup_gap): + raise ValueError("input must be an instance of AbelianGroup_gap") if not self.ambient() is G.ambient(): return False return G.gap().IsSubsemigroup(self).sage() @@ -508,17 +505,18 @@ def subgroup(self, gens): sage: gen = G.gens()[:2] sage: S = G.subgroup(gen) sage: S - Subgroup of Abelian group with gap, generator orders (2, 3, 4, 5) generated by (f1, f2) + Subgroup of Abelian group with gap, generator orders (2, 3, 4, 5) + generated by (f1, f2) sage: g = G.an_element() sage: s = S.an_element() - sage: g*s + sage: g * s f2^2*f3*f5 sage: G = AbelianGroupGap([3,4,0,2]) # optional - gap_packages - sage: gen = G.gens()[:2] - sage: S = G.subgroup(gen) - sage: g = G.an_element() - sage: s = S.an_element() - sage: g*s # optional - gap_packages + sage: gen = G.gens()[:2] # optional - gap_packages + sage: S = G.subgroup(gen) # optional - gap_packages + sage: g = G.an_element() # optional - gap_packages + sage: s = S.an_element() # optional - gap_packages + sage: g * s # optional - gap_packages g1^2*g2^2*g3*g4 TESTS:: @@ -527,49 +525,79 @@ def subgroup(self, gens): sage: h in S False """ - gens = tuple(self(g) for g in gens) + gens = tuple([self(g) for g in gens]) return AbelianGroupSubgroup_gap(self.ambient(), gens) -class AbelianGroupAmbient_gap(AbelianGroup_gap): +class AbelianGroupGap(AbelianGroup_gap): r""" - Ambient abelian groups with gap. - - Do not use this class directly. Instead use :meth:`AbelianGroupGap`. - Needs the gap package "Polycyclic" in case the group is infinite. + Abelian groups implemented using GAP. INPUT: - - ``generator_orders`` - a tuple of nonnegative integers - - ``polycyclic_package`` -- (default: ``False``) boolean - - ``category`` -- a category + - ``generator_orders`` -- a list of nonnegative integers where `0` + gives a factor isomorphic to `\ZZ` + + OUTPUT: + + - an abelian group EXAMPLES:: - sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupAmbient_gap - sage: AbelianGroupAmbient_gap((2,3,4)) - Abelian group with gap, generator orders (2, 3, 4) + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: AbelianGroupGap([3,6]) + Abelian group with gap, generator orders (3, 6) + sage: AbelianGroupGap([3,6,5]) + Abelian group with gap, generator orders (3, 6, 5) + sage: AbelianGroupGap([3,6,0]) # optional - gap_packages + Abelian group with gap, generator orders (3, 6, 0) + + .. WARNING:: + + Needs the GAP package ``Polycyclic`` in case the group is infinite. """ - def __init__(self, generator_orders, polycyclic_package=False, category=None): + @staticmethod + def __classcall_private__(cls, generator_orders): r""" - Constructor. + Normalize input to ensure a unique representation. - See :class:`AbelianGroupAmbient_gap` for documentation. + EXAMPLES:: + + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: A1 = AbelianGroupGap((2,3,4)) + sage: A2 = AbelianGroupGap([4/2,3,4]) + sage: A1 is A2 + True + """ + generator_orders = tuple([ZZ(e) for e in generator_orders]) + if any(e < 0 for e in generator_orders): + return ValueError("generator orders must be nonnegative") + return super(AbelianGroupGap, cls).__classcall__(cls, generator_orders) + + def __init__(self, generator_orders): + r""" + Constructor. TESTS:: - sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupAmbient_gap + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: A = AbelianGroup((2,3,4)) sage: TestSuite(A).run() """ - if polycyclic_package: - G = libgap.eval("AbelianPcpGroup(%s)"%list(generator_orders)) + category = Groups().Commutative() + if 0 in generator_orders: + if not libgap.LoadPackage("Polycyclic"): + raise ImportError("unable to import polycyclic package") + G = libgap.eval("AbelianPcpGroup(%s)" % list(generator_orders)) + category = category.Infinite() + self.Element = AbelianGroupElement_polycyclic else: G = libgap.AbelianGroup(generator_orders) - AbelianGroup_gap.__init__(self, G, ambient=None, polycyclic_package=polycyclic_package, category=category) + category = category.Finite().Enumerated() + AbelianGroup_gap.__init__(self, G, category=category) def _latex_(self): """ - Return the latex representation of this group. + Return a latex representation of this group. EXAMPLES:: @@ -578,11 +606,11 @@ def _latex_(self): sage: G._latex_() 'Abelian group with gap, generator orders $(2, 6)$' """ - return "Abelian group with gap, generator orders $" + str(self.gens_orders()) + "$" + return "Abelian group with gap, generator orders ${}$".format(self.gens_orders()) def _repr_(self): r""" - Return the string representation of this group. + Return a string representation of this group. EXAMPLES:: @@ -605,26 +633,31 @@ def __reduce__(self): sage: G = AbelianGroupGap([3,2,5]) sage: G == loads(dumps(G)) True + sage: G is loads(dumps(G)) + True """ return AbelianGroupGap, (self.gens_orders(),) class AbelianGroupSubgroup_gap(AbelianGroup_gap): r""" - Subgroups of abelian groups with gap. - - Do not use this class directly. Instead use :meth:`subgroup`. + Subgroups of abelian groups with GAP. INPUT: - ``ambient`` -- the ambient group - ``gens`` -- generators of the subgroup + .. NOTE:: + + Do not construct this class directly. Instead use + :meth:`~sage.groups.abelian_groups.AbelianGroupGap.subgroup`. + EXAMPLES:: - sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap - sage: G = AbelianGroupGap([2,3,4,5]) - sage: gen = G.gens()[:2] - sage: S = G.subgroup(gen) # indirect doctest + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + sage: G = AbelianGroupGap([2,3,4,5]) + sage: gen = G.gens()[:2] + sage: S = G.subgroup(gen) """ def __init__(self, ambient, gens): r""" @@ -632,21 +665,38 @@ def __init__(self, ambient, gens): TESTS:: - sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap,AbelianGroupSubgroup_gap + sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap, AbelianGroupSubgroup_gap sage: G = AbelianGroupGap([]) sage: gen = G.gens() sage: A = AbelianGroupSubgroup_gap(G, gen) sage: TestSuite(A).run() + + Check that we are in the correct category:: + + sage: G = AbelianGroupGap([2,3,0]) + sage: g = G.gens() + sage: H1 = G.subgroup([g[0],g[1]]) + sage: H1 in Groups().Finite() + True + sage: H2 = G.subgroup([g[0],g[2]]) + sage: H2 in Groups().Infinite() + True """ - polycyclic_package = ambient._with_pc - category = ambient.category() - gens_gap = tuple(g.gap() for g in gens) + gens_gap = tuple([g.gap() for g in gens]) G = ambient.gap().Subgroup(gens_gap) - AbelianGroup_gap.__init__(self, G, ambient=ambient, polycyclic_package=polycyclic_package, category=category) + from sage.rings.infinity import Infinity + category = Groups().Commutative() + if G.Size().sage() < Infinity: + category = category.Finite() + else: + category = category.Infinite() + # FIXME: Tell the category that it is a Subobjects() category + # category = category.Subobjects() + AbelianGroup_gap.__init__(self, G, ambient=ambient, category=category) - def __repr__(self): + def _repr_(self): r""" - Return the string representation of this subgroup. + Return a string representation of this subgroup. EXAMPLES:: @@ -654,11 +704,11 @@ def __repr__(self): sage: G = AbelianGroupGap([2,3,4,5]) sage: gen = G.gens()[:2] sage: S = G.subgroup(gen) - sage: S.__repr__() - 'Subgroup of Abelian group with gap, generator orders (2, 3, 4, 5) generated by (f1, f2)' + sage: S + Subgroup of Abelian group with gap, generator orders (2, 3, 4, 5) + generated by (f1, f2) """ - s = "Subgroup of %s generated by %s"%(self.ambient(),self.gens()) - return s + return "Subgroup of %s generated by %s"%(self.ambient(),self.gens()) def __reduce__(self): r""" @@ -674,9 +724,11 @@ def __reduce__(self): sage: S = G.subgroup(gen) sage: S == loads(dumps(S)) True + sage: S is loads(dumps(S)) + True """ amb = self.ambient() # avoid infinite loop - gens = tuple(amb(g) for g in self.gens()) + gens = tuple([amb(g) for g in self.gens()]) return amb.subgroup, (gens,) From 45c8b46555c3869a21665cb2563c7f866efd20f5 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Sun, 4 Feb 2018 14:43:30 +0100 Subject: [PATCH 626/740] Fix a latex issue with // and rawstrings --- src/sage/modules/free_module.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 3e2d4b0759f..44c5dcafefd 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -1752,7 +1752,7 @@ def direct_sum(self, other): return self.basis_matrix().block_sum(other.basis_matrix()).row_module(self.base_ring()) def coordinates(self, v, check=True): - r""" + """ Write `v` in terms of the basis for self. INPUT: @@ -2920,7 +2920,7 @@ def intersection(self, other): K = K.matrix_from_columns(range(n)) B = K*A1 return B.row_module(self.base_ring()) - + def __and__(self, other): r""" Return the intersection of ``self`` and ``other``. @@ -2953,8 +2953,8 @@ def __and__(self, other): Echelon basis matrix: [2 2 2] """ - return self.intersection(other) - + return self.intersection(other) + def zero_submodule(self): """ Return the zero submodule of this module. @@ -5989,7 +5989,7 @@ def echelon_coordinates(self, v, check=True): .. MATH:: - \\sum c_i B_i = v. + \sum c_i B_i = v. If `v` is not in self, raise an ``ArithmeticError`` exception. From c2549b58d7d1d1d3152654b38428ba4f20e673d6 Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Sun, 4 Feb 2018 14:50:28 +0100 Subject: [PATCH 627/740] make vfaces set of frozensets --- src/sage/graphs/graph.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 78bbc599496..ec6a26146d5 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -7244,7 +7244,10 @@ def is_circumscribable(self): A polyhedron is circumscribed if all of its facets are tangent to a sphere. By a theorem of Rivin ([HRS1993]_), this can be checked by solving a - linear program. + linear program that assigns weights between 0 and 1/2 on each edge of + the polyhedron, so that the weights on any face add to exactly one + and the weights on any non-facial cycle add to more than one. + If and only if this can be done, the polyhedron can be circumscribed. EXAMPLES:: @@ -7298,14 +7301,14 @@ def is_circumscribable(self): M.add_constraint(e[u,v] - c[0], min=0) M.add_constraint(e[u,v] + c[0], max=ZZ(1)/ZZ(2)) - from sage.misc.flatten import flatten # The faces are completely determined by the graph structure: # for polyhedral graph, there is only one way to choose the faces. # We add an equality constraint for each face. efaces = self.faces() - vfaces = [set(flatten(face)) for face in efaces] + vfaces = set(frozenset([_[0] for _ in face]) for face in efaces) for edges in efaces: M.add_constraint(M.sum(e[tuple(sorted(_))] for _ in edges) == 1) + # In order to generate all simple cycles of G, which are not faces, # we use the "all_simple_cycles" method of directed graphs, generating # each cycle twice (in both directions). The set below make sure only @@ -7314,9 +7317,9 @@ def is_circumscribable(self): inequality_constraints = set() for cycle in D.all_simple_cycles(): if len(cycle) > 3: - edges = (tuple(sorted([cycle[i], cycle[i+1]])) for i in range(len(cycle)-1)) - scycle = set(flatten(cycle)) + scycle = frozenset(cycle) if scycle not in vfaces: + edges = (tuple(sorted([cycle[i], cycle[i+1]])) for i in range(len(cycle)-1)) inequality_constraints.add(frozenset(edges)) for ieq in inequality_constraints: From 23f489a3a09fd2b94edd994f93bd941ecdaa3db6 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Sun, 4 Feb 2018 16:24:46 +0100 Subject: [PATCH 628/740] Improved exponents() --- .../groups/abelian_gps/abelian_group_gap.py | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/sage/groups/abelian_gps/abelian_group_gap.py b/src/sage/groups/abelian_gps/abelian_group_gap.py index 542d40c9758..90177292e57 100644 --- a/src/sage/groups/abelian_gps/abelian_group_gap.py +++ b/src/sage/groups/abelian_gps/abelian_group_gap.py @@ -130,12 +130,30 @@ def exponents(self): f1 sage: s.exponents() (1,) + + It can handle quite large groups too:: + + sage: G = AbelianGroupGap([2^10, 5^10]) + sage: f1, f2 = G.gens() + sage: g = f1^123*f2^789 + sage: g.exponents() + (123, 789) + + .. WARNING:: + + Crashes for very large groups. + + .. TODO:: + + Make exponents work for very large groups. + This could be done by using Pcgs in gap. """ + P = self.parent() - # works only for small groups - # as gap has problems to solve the word problem - P = self.parent() - x = libgap.Factorization(P.gap(), self.gap()) + # better than Factorization as this does not create the + # whole group in memory. + f = P.gap().EpimorphismFromFreeGroup() + x = f.PreImagesRepresentative(self.gap()) L = x.ExtRepOfObj().sage() Lgens = L[::2] Lexpo = L[1::2] @@ -198,6 +216,13 @@ def exponents(self): sage: g = gens[0]^2 * gens[1]^4 * gens[2]^8 # optional - gap_packages sage: g.exponents() # optional - gap_packages (2, 4, 8) + + Efficiently handles very large groups:: + + sage: G = AbelianGroupGap([2^30,5^30,0]) # optional - gap_packages + sage: f1, f2, f3 = G.gens() # optional - gap_packages + sage: (f1^12345*f2^123456789).exponents() # optional - gap_packages + (12345, 123456789, 0) """ return tuple(self.gap().Exponents().sage()) From 67c987924cffff9b416cf5ced05a8b682b0037bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Sun, 4 Feb 2018 18:17:33 +0200 Subject: [PATCH 629/740] Add supergreedy extensions. --- src/sage/combinat/posets/hasse_diagram.py | 62 +++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index 42b057e24e9..3fe992c0950 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -172,6 +172,68 @@ def greedy_rec(H, linext): return greedy_rec(self, []) + def supergreedy_linear_extensions_iterator(self): + """ + Return an iterator over supergreedy linear extensions of the Hasse diagram. + + A linear extension `[e_1, e_2, \ldots, e_n]` is *supergreedy* if, + for every `i` and `j` where `i > j`, `e_i` covers `e_j` if for + every `i > k > j` at least one lower cover of `e_k` is not in + `[e_1, e_2, \ldots, e_k]`. + + Informally said a linear extension is supergreedy if it "always + goes as high possible, and withdraw so less as possible". + These are also called depth-first linear extensions. + + EXAMPLES: + + We show the difference between "only greedy" and supergreedy + extensions:: + + sage: from sage.combinat.posets.hasse_diagram import HasseDiagram + sage: H = HasseDiagram({0: [1, 2], 2: [3, 4]}) + sage: G_ext = list(H.greedy_linear_extensions_iterator()) + sage: SG_ext = list(H.supergreedy_linear_extensions_iterator()) + sage: [0, 2, 3, 1, 4] in G_ext + True + sage: [0, 2, 3, 1, 4] in SG_ext + False + + sage: len(SG_ext) + 4 + + TESTS:: + + sage: from sage.combinat.posets.hasse_diagram import HasseDiagram + sage: list(HasseDiagram({}).supergreedy_linear_extensions_iterator()) + [[]] + sage: list(HasseDiagram({0: [], 1: []}).supergreedy_linear_extensions_iterator()) + [[0, 1], [1, 0]] + """ + N = self.order() + self_as_set = set(self) + + def supergreedy_rec(H, linext): + k = len(linext) + + if k == N: + yield linext + + else: + S = [] + while not S: + if not k: # Start from new minimal element + S = [x for x in self.sources() if x not in linext] + else: + S = [x for x in self.neighbors_out(linext[k-1]) if x not in linext and all(low in linext for low in self.neighbors_in(x))] + k -= 1 + + for e in S: + for i_want_python_3 in supergreedy_rec(H, linext+[e]): + yield i_want_python_3 + + return supergreedy_rec(self, []) + def is_linear_extension(self, lin_ext=None): r""" Test if an ordering is a linear extension. From dacc52aa7439fd0c9ceb629776b532586768a8a0 Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Sun, 4 Feb 2018 18:01:48 +0100 Subject: [PATCH 630/740] more examples and some pep8 improvements --- src/sage/graphs/graph.py | 69 +++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index ec6a26146d5..17d5a5f77a7 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -7242,11 +7242,11 @@ def is_circumscribable(self): """ Test whether the graph is the graph of a circumscribed polyhedron. - A polyhedron is circumscribed if all of its facets are tangent to a sphere. - By a theorem of Rivin ([HRS1993]_), this can be checked by solving a - linear program that assigns weights between 0 and 1/2 on each edge of - the polyhedron, so that the weights on any face add to exactly one - and the weights on any non-facial cycle add to more than one. + A polyhedron is circumscribed if all of its facets are tangent to a + sphere. By a theorem of Rivin ([HRS1993]_), this can be checked by + solving a linear program that assigns weights between 0 and 1/2 on each + edge of the polyhedron, so that the weights on any face add to exactly + one and the weights on any non-facial cycle add to more than one. If and only if this can be done, the polyhedron can be circumscribed. EXAMPLES:: @@ -7254,12 +7254,24 @@ def is_circumscribable(self): sage: C = graphs.CubeGraph(3) sage: C.is_circumscribable() True + sage: O = graphs.OctahedralGraph() - sage: f = set(flatten(O.faces()[0])) + sage: O.is_circumscribable() + True + + sage: TT = polytopes.truncated_tetrahedron().graph() + sage: TT.is_circumscribable() + False + + + Stellating in a face of the octahedral graph is not circumscribable:: + + sage: f = set(flatten(choice(O.faces()))) sage: O.add_edges([[6, i] for i in f]) sage: O.is_circumscribable() False + .. SEEALSO:: * :meth:`is_polyhedral` @@ -7294,12 +7306,12 @@ def is_circumscribable(self): M.set_max(c[0], 1) M.set_objective(c[0]) - for u,v in self.edge_iterator(labels=0): + for u, v in self.edge_iterator(labels=0): if u > v: - u,v = v,u - M.set_max(e[u,v], ZZ(1)/ZZ(2)) - M.add_constraint(e[u,v] - c[0], min=0) - M.add_constraint(e[u,v] + c[0], max=ZZ(1)/ZZ(2)) + u, v = v, u + M.set_max(e[u, v], ZZ(1)/ZZ(2)) + M.add_constraint(e[u, v] - c[0], min=0) + M.add_constraint(e[u, v] + c[0], max=ZZ(1)/ZZ(2)) # The faces are completely determined by the graph structure: # for polyhedral graph, there is only one way to choose the faces. @@ -7339,28 +7351,42 @@ def is_inscribable(self): Test whether the graph is the graph of an inscribed polyhedron. A polyhedron is inscribed if all of its vertices are on a sphere. - This is dual to the notion of circumscribed polyhedron: - A Polyhedron is inscribed if and only if its polar dual is circumscribed - and hence a graph is inscribable if and only if its planar dual is - circumscribable. + This is dual to the notion of circumscribed polyhedron: A Polyhedron is + inscribed if and only if its polar dual is circumscribed and hence a + graph is inscribable if and only if its planar dual is circumscribable. EXAMPLES:: - sage: graphs.CubeGraph(3).is_inscribable() + sage: H = graphs.HerschelGraph() + sage: H.is_inscribable() # long time (> 1 sec) + False + sage: H.planar_dual().is_inscribable() # long time (> 1 sec) + True + + sage: C = graphs.CubeGraph(3) + sage: C.is_inscribable() True - sage: C=graphs.CubeGraph(3) - sage: v = C.vertices()[0] - sage: triangle=[_ + v for _ in C.neighbors(v)] + + Cutting off a vertex from the cube yields an uninscribable graph:: + + sage: C = graphs.CubeGraph(3) + sage: v = next(C.vertex_iterator()) + sage: triangle = [_ + v for _ in C.neighbors(v)] sage: C.add_edges(Combinations(triangle, 2)) sage: C.add_edges(zip(triangle, C.neighbors(v))) sage: C.delete_vertex(v) sage: C.is_inscribable() False - sage: H = graphs.HerschelGraph() - sage: H.is_inscribable() # long time (1 second) + Breaking a face of the cube yields an uninscribable graph:: + + sage: C = graphs.CubeGraph(3) + sage: face = choice(C.faces()) + sage: C.add_edge([face[0][0], face[2][0]]) + sage: C.is_inscribable() False + .. SEEALSO:: * :meth:`is_polyhedral` @@ -7378,7 +7404,6 @@ def is_inscribable(self): raise NotImplementedError('this method only works for polyhedral graphs') return self.planar_dual().is_circumscribable() - @doc_index("Graph properties") def is_prime(self): r""" From dbadf625cac960b8a0fefcf117c2ca1f0872a5fa Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Sun, 4 Feb 2018 18:44:18 +0100 Subject: [PATCH 631/740] fixed reference --- src/doc/en/reference/references/index.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 8396e6a6732..8af051d4aab 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1191,11 +1191,11 @@ REFERENCES: `J`-Ideals of a Matrix Over a Principal Ideal Domain", :arxiv:`1611.10308`, 2016. -.. [HRS1993] ] Craig D. Hodgson, Igor Rivin and Warren D. Smith, \ - *A characterization of convex hyperbolic polyhedra - and of convex polyhedra inscribed in the sphere.* - Bulletin of the American Mathematical Society - 27.2 (1992): 246-251. +.. [HRS1993] \C. D. Hodgson, I. Rivin and W. D. Smith. + *A characterization of convex hyperbolic polyhedra + and of convex polyhedra inscribed in the sphere.* + Bulletin of the American Mathematical Society + 27.2 (1992): 246-251. .. [HRT2000] \R.B. Howlett, L.J. Rylands, and D.E. Taylor. *Matrix generators for exceptional groups of Lie type*. From b9ababe78120a3104d2ddb53e481bac66106ca40 Mon Sep 17 00:00:00 2001 From: Madison Van Dyk Date: Sun, 4 Feb 2018 14:28:56 -0500 Subject: [PATCH 632/740] update Isogeny function to address slow order calcuaiton --- .../schemes/elliptic_curves/ell_curve_isogeny.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index 3457641499b..33ced7d9e0c 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -1833,7 +1833,6 @@ def __setup_post_isomorphism(self, codomain, model): return - ########################### # Velu's Formula Functions ########################### @@ -1870,8 +1869,15 @@ def __init_from_kernel_list(self, kernel_gens): kernel_set = Set([self.__E1(0)]) from sage.misc.all import flatten from sage.groups.generic import multiples + def all_multiples(itr, terminal): + mult_list = [terminal] + R = terminal + itr + while R != terminal: + mult_list.append(R) + R = R + itr + return mult_list for P in kernel_gens: - kernel_set += Set(flatten([list(multiples(P,P.order(),Q)) + kernel_set += Set(flatten([list(all_multiples(P,Q)) for Q in kernel_set])) self.__kernel_list = kernel_set.list() @@ -1879,7 +1885,7 @@ def __init_from_kernel_list(self, kernel_gens): self.__kernel_non2tor = {} self.__degree = Integer(len(kernel_set)) self.__sort_kernel_list() - + # # Precompute the values in Velu's Formula. # From 516935cb4134e2df950e97cf2f9936d6ce34c59e Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 4 Feb 2018 14:34:12 -0600 Subject: [PATCH 633/740] Fixing bug in type E RC bijection and added a better test. --- src/sage/combinat/rigged_configurations/bij_type_E67.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/rigged_configurations/bij_type_E67.py b/src/sage/combinat/rigged_configurations/bij_type_E67.py index b727eaa37d7..d9d635540e6 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_E67.py +++ b/src/sage/combinat/rigged_configurations/bij_type_E67.py @@ -57,8 +57,8 @@ def next_state(self, val): sage: bijection = KRTToRCBijectionTypeE67(KRT.module_generators[0]) sage: bijection.cur_path.insert(0, []) sage: bijection.cur_dims.insert(0, [1, 1]) - sage: bijection.cur_path[0].insert(0, [(1,)]) - sage: bijection.next_state((1,)) + sage: bijection.cur_path[0].insert(0, [(-3,4)]) + sage: bijection.next_state((-3,4)) """ def find_singular_string(p, max_width): max_pos = -1 @@ -80,7 +80,7 @@ def find_singular_string(p, max_width): self._update_vacancy_nums(a) return - max_width = float("inf") + max_width = max(nu[0] if nu else 0 for nu in self.ret_rig_con) + 1 found = True while found: found = False From 6db34e48fca477d03b80bb820987f5542359eee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Mon, 5 Feb 2018 07:34:12 +0200 Subject: [PATCH 634/740] Unfunnying variable name. --- src/sage/combinat/posets/hasse_diagram.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index 3fe992c0950..7986dd8bb85 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -167,8 +167,9 @@ def greedy_rec(H, linext): not any(low in S_ for low in self.neighbors_in(x))] for e in S: - for i_want_python_3 in greedy_rec(H, linext+[e]): - yield i_want_python_3 + # Python3-todo: use yield from + for tmp in greedy_rec(H, linext+[e]): + yield tmp return greedy_rec(self, []) @@ -229,8 +230,9 @@ def supergreedy_rec(H, linext): k -= 1 for e in S: - for i_want_python_3 in supergreedy_rec(H, linext+[e]): - yield i_want_python_3 + # Python3-todo: use yield from + for tmp in supergreedy_rec(H, linext+[e]): + yield tmp return supergreedy_rec(self, []) From d8170e6038b71ff7e75060cfda5a2558bf1c6523 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 5 Feb 2018 10:42:57 +0100 Subject: [PATCH 635/740] Don't import locale --- src/sage/misc/temporary_file.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/misc/temporary_file.py b/src/sage/misc/temporary_file.py index 25478823a31..7378123dd67 100644 --- a/src/sage/misc/temporary_file.py +++ b/src/sage/misc/temporary_file.py @@ -23,11 +23,9 @@ from __future__ import print_function import io -import locale import os import tempfile import atexit - import six From d7d7614c73854be5bf155f789485d4fae90edff4 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 5 Feb 2018 10:45:45 +0100 Subject: [PATCH 636/740] Catch all exceptions from io.open() --- src/sage/misc/temporary_file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/misc/temporary_file.py b/src/sage/misc/temporary_file.py index 7378123dd67..da8e199eb88 100644 --- a/src/sage/misc/temporary_file.py +++ b/src/sage/misc/temporary_file.py @@ -427,7 +427,7 @@ def __enter__(self): try: self.tempfile = io.open(name, wmode, **self.kwargs) - except (TypeError, ValueError): + except Exception: # Some invalid arguments were passed to io.open os.unlink(name) raise From db16bc954c742ea82878da606e8737e57230865b Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 5 Feb 2018 10:54:51 +0100 Subject: [PATCH 637/740] Build PCRE without JIT if it does not work --- build/pkgs/pcre/spkg-check | 2 -- build/pkgs/pcre/spkg-install | 19 ++++++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) delete mode 100644 build/pkgs/pcre/spkg-check diff --git a/build/pkgs/pcre/spkg-check b/build/pkgs/pcre/spkg-check deleted file mode 100644 index 27cd9419538..00000000000 --- a/build/pkgs/pcre/spkg-check +++ /dev/null @@ -1,2 +0,0 @@ -cd src -$MAKE check diff --git a/build/pkgs/pcre/spkg-install b/build/pkgs/pcre/spkg-install index 6fd6a6e294c..b449084248c 100644 --- a/build/pkgs/pcre/spkg-install +++ b/build/pkgs/pcre/spkg-install @@ -1,5 +1,22 @@ cd src -sdh_configure --enable-utf --enable-unicode-properties --enable-jit +PCRE_CONFIGURE="--enable-utf --enable-unicode-properties $PCRE_CONFIGURE" + +sdh_configure $PCRE_CONFIGURE --enable-jit sdh_make + +# The JIT feature of pcre is known to be broken on some systems, in +# particular on Solaris. We run the testsuite of pcre (this takes only +# a few seconds). It the testsuite fails, we rebuild pcre without JIT +# support. See https://trac.sagemath.org/ticket/24628 +if ! $MAKE check; then + echo >&2 "*** Rebuilding pcre without JIT support ***" + $MAKE clean + + sdh_configure $PCRE_CONFIGURE --disable-jit + sdh_make + + $MAKE check || sdh_die "Error checking $PKG_NAME" +fi + sdh_make_install From b1dfae4a5f358e0f4be47aaedd5f659dd2c44ed7 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Mon, 5 Feb 2018 10:15:43 +0000 Subject: [PATCH 638/740] Use slightly less strict umask, but still should be good enough. Ensure that all files are applied the ''same'' mtime. --- build/sage_bootstrap/uncompress/tar_file.py | 23 ++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/build/sage_bootstrap/uncompress/tar_file.py b/build/sage_bootstrap/uncompress/tar_file.py index 952ca0e24da..b1fb93350ff 100644 --- a/build/sage_bootstrap/uncompress/tar_file.py +++ b/build/sage_bootstrap/uncompress/tar_file.py @@ -19,6 +19,8 @@ import tarfile import stat import subprocess +import time + from io import BytesIO from sage_bootstrap.uncompress.filter_os_files import filter_os_files @@ -26,27 +28,31 @@ class SageBaseTarFile(tarfile.TarFile): """ - Sage as tarfile.TarFile, but applies a strict umask (0077) to the + Sage as tarfile.TarFile, but applies a reasonable umask (0022) to the permissions of all extracted files and directories. Previously this applied the user's current umask per the default behavior of the ``tar`` utility, but this did not provide sufficiently reliable behavior in all cases, such as when the user's umask is not strict enough. - This also sets the modified timestamps on all extracted files to the time - of their extraction, not the timestamps stored in the tarball. + This also sets the modified timestamps on all extracted files to the same + time, not the timestamps stored in the tarball. See http://trac.sagemath.org/ticket/20218#comment:16 and https://trac.sagemath.org/ticket/24567 for more background. """ - umask = 0o077 + umask = 0o022 def __init__(self, *args, **kwargs): # Unfortunately the only way to get the current umask is to set it # and then restore it super(SageBaseTarFile, self).__init__(*args, **kwargs) + # When extracting files, this will be set to the time of the first + # file extracted, so that all files can be set to the same mtime + self._extracted_mtime = None + @property def names(self): """ @@ -68,13 +74,17 @@ def chmod(self, tarinfo, target): def utime(self, tarinfo, target): """Override to keep the extraction time as the file's timestamp.""" - return + tarinfo.mtime = self._extracted_mtime + return super(SageBaseTarFile, self).utime(tarinfo, target) def extractall(self, path='.', members=None): """ Same as tarfile.TarFile.extractall but allows filenames for the members argument (like zipfile.ZipFile). """ + + self._extracted_mtime = None + if members: name_to_member = dict([member.name, member] for member in self.getmembers()) members = [m if isinstance(m, tarfile.TarInfo) @@ -101,6 +111,9 @@ def _extract_member(self, tarinfo, targetpath): the tarball. """ + if self._extracted_mtime is None: + self._extracted_mtime = time.time() + old_umask = os.umask(self.umask) try: super(SageBaseTarFile, self)._extract_member(tarinfo, targetpath) From 3e34cd3b08480d42761cad93b2db51650e54d2cc Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 5 Feb 2018 11:31:48 +0100 Subject: [PATCH 639/740] Fix building fplll on Solaris --- build/pkgs/fplll/package-version.txt | 2 +- build/pkgs/fplll/patches/GCC-PR10200.patch | 74 ++++++++++++++++++++++ build/pkgs/fplll/patches/pow.patch | 27 ++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/fplll/patches/GCC-PR10200.patch create mode 100644 build/pkgs/fplll/patches/pow.patch diff --git a/build/pkgs/fplll/package-version.txt b/build/pkgs/fplll/package-version.txt index def2490a89c..17b5de986f3 100644 --- a/build/pkgs/fplll/package-version.txt +++ b/build/pkgs/fplll/package-version.txt @@ -1 +1 @@ -5.2.0.p0 +5.2.0.p1 diff --git a/build/pkgs/fplll/patches/GCC-PR10200.patch b/build/pkgs/fplll/patches/GCC-PR10200.patch new file mode 100644 index 00000000000..89da1ce3e55 --- /dev/null +++ b/build/pkgs/fplll/patches/GCC-PR10200.patch @@ -0,0 +1,74 @@ +commit f9ea48c350feb9054c7176c2adec9c8e4a06f503 +Author: Jeroen Demeyer +Date: Mon Feb 5 11:24:14 2018 +0100 + + Work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=10200 + + This shows up in particular on Solaris systems with GCC-5.4.0 + +diff --git a/fplll/sieve/sieve_gauss_2sieve.cpp b/fplll/sieve/sieve_gauss_2sieve.cpp +index 83eb817..625c6f0 100644 +--- a/fplll/sieve/sieve_gauss_2sieve.cpp ++++ b/fplll/sieve/sieve_gauss_2sieve.cpp +@@ -33,7 +33,7 @@ template Z_NR GaussSieve::update_p_2reduce(ListPo + for (lp_it = List.begin(); lp_it != List.end(); ++lp_it) + { + v = *lp_it; +- if (p->norm < v->norm) ++ if ((p->norm) < v->norm) + break; + + /* if there is one reduction the vector should re-pass the list */ +diff --git a/fplll/sieve/sieve_gauss_3sieve.cpp b/fplll/sieve/sieve_gauss_3sieve.cpp +index 3c3716c..3fdbca6 100644 +--- a/fplll/sieve/sieve_gauss_3sieve.cpp ++++ b/fplll/sieve/sieve_gauss_3sieve.cpp +@@ -27,7 +27,7 @@ GaussSieve::update_p_3reduce_2reduce(ListPoint *p, + for (lp_it = List.begin(); lp_it != List.end(); ++lp_it) + { + v = *lp_it; +- if (p->norm < v->norm) ++ if ((p->norm) < v->norm) + break; + if (half_2reduce(p, v)) + { +@@ -166,7 +166,7 @@ template Z_NR GaussSieve::update_p_3reduce(ListPo + continue; + } + ++lp_it2; +- if (v1->norm < p->norm) ++ if ((v1->norm) < p->norm) + { + vnew2 = new_listpoint(nc); + if (check_3reduce(v1, p, v2, vnew2) != 1) +diff --git a/fplll/sieve/sieve_gauss_4sieve.cpp b/fplll/sieve/sieve_gauss_4sieve.cpp +index 9eb586e..f2758a4 100644 +--- a/fplll/sieve/sieve_gauss_4sieve.cpp ++++ b/fplll/sieve/sieve_gauss_4sieve.cpp +@@ -15,7 +15,7 @@ void GaussSieve::update_p_4reduce_aux(ListPoint *p, + for (lp_it = List.begin(); lp_it != List.end(); ++lp_it) + { + v = *lp_it; +- if (p->norm < v->norm) ++ if ((p->norm) < v->norm) + break; + } + lp_it_k = lp_it; +@@ -119,7 +119,7 @@ template Z_NR GaussSieve::update_p_4reduce_3reduc + continue; + } + ++lp_it2; +- if (v1->norm < p->norm) ++ if ((v1->norm) < p->norm) + { + /*cout << "# --- here 1 " << endl; + cout << v1->norm << endl; +@@ -274,7 +274,7 @@ template Z_NR GaussSieve::update_p_4reduce(ListPo + } + ++lp_it3; + /* (v1, p, v2, v3) or (v1, v2, p, v3) */ +- if (v1->norm < p->norm) ++ if ((v1->norm) < p->norm) + { + /* (v1, p, v2, v3) */ + if (v2->norm > p->norm) diff --git a/build/pkgs/fplll/patches/pow.patch b/build/pkgs/fplll/patches/pow.patch new file mode 100644 index 00000000000..14d1453935d --- /dev/null +++ b/build/pkgs/fplll/patches/pow.patch @@ -0,0 +1,27 @@ +commit 848a55ec1eaa425d6843303fcc0fac9eda397a83 +Author: Jeroen Demeyer +Date: Mon Feb 5 11:22:54 2018 +0100 + + Fix pow() calls on Solaris + +diff --git a/fplll/nr/dpe.h b/fplll/nr/dpe.h +index 1ad6562..c132378 100644 +--- a/fplll/nr/dpe.h ++++ b/fplll/nr/dpe.h +@@ -708,14 +708,14 @@ DPE_INLINE void dpe_ugly_log(dpe_t x, const dpe_t y) + DPE_INLINE void dpe_ugly_exp(dpe_t x, const dpe_t y) + { + // printf ("## exp is %ld\n", DPE_EXP(y)); +- dpe_set_d(x, exp(((double)DPE_MANT(y)) * pow(2, ((double)DPE_EXP(y))))); ++ dpe_set_d(x, exp(((double)DPE_MANT(y)) * pow(2.0, ((double)DPE_EXP(y))))); + } + + /* More hacks */ + /* x = y^k */ + DPE_INLINE void dpe_pow_si(dpe_t x, const dpe_t y, const unsigned int k) + { +- DPE_MANT(x) = pow(DPE_MANT(y), k); ++ DPE_MANT(x) = pow(DPE_MANT(y), (double)k); + DPE_EXP(x) = DPE_EXP(y) * k; + dpe_normalize(x); + } From 77b84909751cc78971a686854266b8b371415dc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konrad=20K=2E=20D=C4=85browski?= Date: Mon, 5 Feb 2018 10:33:31 +0000 Subject: [PATCH 640/740] Add missing free in SubgraphSearch. --- src/sage/graphs/generic_graph_pyx.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index f31e6e2b743..e9edd71a2ce 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -915,6 +915,7 @@ cdef class SubgraphSearch: # Free the memory sig_free(self.busy) sig_free(self.stack) + sig_free(self.tmp_array) sig_free(self.vertices) sig_free(self.line_h_out) sig_free(self.line_h_in) From 13a581e5c5c1567f54a93082202f8a9073440490 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 5 Feb 2018 17:07:24 +0100 Subject: [PATCH 641/740] Set _extracted_mtime once in __init__() --- build/sage_bootstrap/uncompress/tar_file.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/build/sage_bootstrap/uncompress/tar_file.py b/build/sage_bootstrap/uncompress/tar_file.py index b1fb93350ff..18f8212bade 100644 --- a/build/sage_bootstrap/uncompress/tar_file.py +++ b/build/sage_bootstrap/uncompress/tar_file.py @@ -36,7 +36,8 @@ class SageBaseTarFile(tarfile.TarFile): behavior in all cases, such as when the user's umask is not strict enough. This also sets the modified timestamps on all extracted files to the same - time, not the timestamps stored in the tarball. + time (the current time), not the timestamps stored in the tarball. This + is meant to work around https://bugs.python.org/issue32773 See http://trac.sagemath.org/ticket/20218#comment:16 and https://trac.sagemath.org/ticket/24567 for more background. @@ -49,9 +50,8 @@ def __init__(self, *args, **kwargs): # and then restore it super(SageBaseTarFile, self).__init__(*args, **kwargs) - # When extracting files, this will be set to the time of the first - # file extracted, so that all files can be set to the same mtime - self._extracted_mtime = None + # Extracted files will have this timestamp + self._extracted_mtime = time.time() @property def names(self): @@ -73,7 +73,6 @@ def chmod(self, tarinfo, target): def utime(self, tarinfo, target): """Override to keep the extraction time as the file's timestamp.""" - tarinfo.mtime = self._extracted_mtime return super(SageBaseTarFile, self).utime(tarinfo, target) @@ -82,9 +81,6 @@ def extractall(self, path='.', members=None): Same as tarfile.TarFile.extractall but allows filenames for the members argument (like zipfile.ZipFile). """ - - self._extracted_mtime = None - if members: name_to_member = dict([member.name, member] for member in self.getmembers()) members = [m if isinstance(m, tarfile.TarInfo) @@ -110,10 +106,6 @@ def _extract_member(self, tarinfo, targetpath): directory tree, even for directories that are not explicitly listed in the tarball. """ - - if self._extracted_mtime is None: - self._extracted_mtime = time.time() - old_umask = os.umask(self.umask) try: super(SageBaseTarFile, self)._extract_member(tarinfo, targetpath) From 97ac0ac61e926e78482d0006f5c3ca6ce4f486a4 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 5 Feb 2018 17:19:36 +0100 Subject: [PATCH 642/740] Fix R build on Solaris --- build/pkgs/r/package-version.txt | 2 +- build/pkgs/r/patches/link.patch | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/r/patches/link.patch diff --git a/build/pkgs/r/package-version.txt b/build/pkgs/r/package-version.txt index 6cb9d3dd0d6..0f0755f68a7 100644 --- a/build/pkgs/r/package-version.txt +++ b/build/pkgs/r/package-version.txt @@ -1 +1 @@ -3.4.3 +3.4.3.p0 diff --git a/build/pkgs/r/patches/link.patch b/build/pkgs/r/patches/link.patch new file mode 100644 index 00000000000..b96130ca9e5 --- /dev/null +++ b/build/pkgs/r/patches/link.patch @@ -0,0 +1,15 @@ +baseRegisterIndex should be declared extern + +Otherwise linking breaks at least on Solaris + +diff -ru R-3.4.3//src/include/GraphicsBase.h b/src/include/GraphicsBase.h +--- R-3.4.3//src/include/GraphicsBase.h 2017-03-24 00:03:58.000000000 +0100 ++++ b/src/include/GraphicsBase.h 2018-02-05 16:26:05.656947056 +0100 +@@ -41,6 +41,6 @@ + + void Rf_setBaseDevice(Rboolean val, pGEDevDesc dd); /* used in graphics.c */ + +-int baseRegisterIndex; ++extern int baseRegisterIndex; + + #endif /* R_GRAPHICSBASE_ */ From 0fd77fc43b37c71dcb8542451d0da8b48610f03f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Mon, 5 Feb 2018 20:38:47 +0200 Subject: [PATCH 643/740] Add is_greedy(). --- src/sage/combinat/posets/posets.py | 65 ++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 06bac0c4be5..8895a015dd4 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -73,6 +73,7 @@ :meth:`~FinitePoset.is_ranked` | Return ``True`` if the poset has a rank function. :meth:`~FinitePoset.is_rank_symmetric` | Return ``True`` if the poset is rank symmetric. :meth:`~FinitePoset.is_series_parallel` | Return ``True`` if the poset can be built by ordinal sums and disjoint unions. + :meth:`~FinitePoset.is_greedy` | Return ``True`` if all greedy linear extensions have equal number of jumps. :meth:`~FinitePoset.is_eulerian` | Return ``True`` if the poset is Eulerian. :meth:`~FinitePoset.is_incomparable_chain_free` | Return ``True`` if the poset is (m+n)-free. :meth:`~FinitePoset.is_slender` | Return ``True`` if the poset is slender. @@ -6714,6 +6715,70 @@ def is_eulerian(self, k=None, certificate=False): return False return (True, None) if certificate else True + def is_greedy(self, certificate=False): + """ + Return ``True`` if the poset is greedy, and ``False`` otherwise. + + A poset is ''greedy'' if every greedy linear extension + has the same number of jumps. + + INPUT: + + - ``certificate`` -- (default: ``False``) whether to return + a certificate + + OUTPUT: + + - If ``certificate=True`` return either ``(True, None)`` or + ``(False, (A, B))`` where `A` and `B` are greedy linear extension + so that `B` has more jumps. If ``certificate=False`` return + ``True`` or ``False``. + + EXAMPLES: + + This is not a self-dual property:: + + sage: W = Poset({1: [3, 4], 2: [4, 5]}) + sage: M = W.dual() + sage: W.is_greedy() + True + sage: M.is_greedy() + False + + Getting a certificate:: + + sage: N = Poset({1: [3], 2: [3, 4]}) + sage: L.is_greedy(certificate=True) + (False, ([1, 2, 4, 3], [2, 4, 1, 3])) + + TESTS:: + + sage: Poset().is_greedy() + True + sage: posets.AntichainPoset(3).is_greedy() + True + sage: posets.ChainPoset(3).is_greedy() + True + """ + H = P._hasse_diagram + N1 = H.order()-1 + it = H.greedy_linear_extensions_iterator() + A = next(it) + A_jumps = sum(1 for i in range(N1) if H.has_edge(A[i], A[i+1])) + + for B in it: + B_jumps = sum(1 for i in range(N1) if H.has_edge(B[i], B[i+1])) + if A_jumps != B_jumps: + if certificate: + if A_jumps > B_jumps: + tmp = A; A = B; B = tmp + return (False, + (self.linear_extension([self[v] for v in A]), + self.linear_extension([self[v] for v in B]))) + return False + + return (True, None) if certificate else True + def frank_network(self): r""" Return Frank's network of the poset. From b461d389b5df0c982ac94778f33686673951172d Mon Sep 17 00:00:00 2001 From: Madison Van Dyk Date: Mon, 5 Feb 2018 14:27:49 -0500 Subject: [PATCH 644/740] Add doctest for order-free isogeny computation --- src/sage/schemes/elliptic_curves/ell_curve_isogeny.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index 33ced7d9e0c..ab3807561d3 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -1855,6 +1855,14 @@ def __init_from_kernel_list(self, kernel_gens): Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 4*x over Finite Field of size 7 sage: phi._EllipticCurveIsogeny__init_from_kernel_list([E(0), E((0,0))]) + The following example demonstrates the necessity of avoiding any calls to P.order(), since such calls involve factoring the group order which could take a long time. + + sage: p=12*next_prime(2**516)*next_prime(2**543)-1 + sage: E=EllipticCurve([GF(p)(1),GF(p)(0)]) + sage: P=E(0).division_points(3)[1] + sage: EllipticCurveIsogeny(E,P) + Isogeny of degree 3 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 74121918935782697150572026386322255999593499669485639046716805918248415369138920980942523328163009458686674022576957487081228785663742728344853540017360335968220603279754173597895493608709042384746570991683531266851688876306667526140964490813793422758339760047934425286567991491819082225635623850227154445853927797025147 to Elliptic Curve defined by y^2 = x^3 + 50980532732167944637647016106708857869144958686070887399211900429833015413233148839807050055231319912356312345664008416716445294630236741746525650487701703923321188716120734056225687969070865447975585851125632334975602253072854988951094096051754342218511208877842573773109101461209491421209013134031964257825134320425762*x + 20123325562060579887439486218415531037934433846849708678890785454945560104043719299121967091612317185340446850679666894934881575657808661438571327808333127323560952150611972136419014507945141707672086018167956291485257506710279043521951524501649360994275020881682315300417777303863402326130971448587985197336271065135371 over Finite Field of size 74121918935782697150572026386322255999593499669485639046716805918248415369138920980942523328163009458686674022576957487081228785663742728344853540017360335968220603279754173597895493608709042384746570991683531266851688876306667526140964490813793422758339760047934425286567991491819082225635623850227154445853927797025147 + """ if self.__check : for P in kernel_gens: From 7ca51b43ffdafc0585b01cfe8e15906b3d957743 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 5 Feb 2018 15:43:07 -0600 Subject: [PATCH 645/740] Initial implementation of Artin groups. --- src/doc/en/reference/references/index.rst | 6 + src/sage/groups/all.py | 1 + src/sage/groups/artin.py | 633 ++++++++++++++++++ .../groups/misc_gps/misc_groups_catalog.py | 1 + 4 files changed, 641 insertions(+) create mode 100644 src/sage/groups/artin.py diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 90bd9e4b861..3ed619f58e8 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1064,6 +1064,12 @@ REFERENCES: fundamental domains for the subgroups of the modular group", preprint :arxiv:`0901.1340` +.. [GP2012] Eddy Godelle and Luis Paris. + *Basic questions on Artin-Tits groups*. A. Bjorner et al. (eds) + Configuration spaces, CRM series. (2012) pp. 299--311. + Edizioni della Normale, Pisa. + :doi:`10.1007/978-88-7642-431-1_13` + .. [GPV2008] Craig Gentry, Chris Peikert, Vinod Vaikuntanathan. *How to Use a Short Basis: Trapdoors for Hard Lattices and New Cryptographic diff --git a/src/sage/groups/all.py b/src/sage/groups/all.py index 8cab8eeb571..d14af3f31ae 100644 --- a/src/sage/groups/all.py +++ b/src/sage/groups/all.py @@ -22,6 +22,7 @@ lazy_import('sage.groups.affine_gps.affine_group', 'AffineGroup') lazy_import('sage.groups.affine_gps.euclidean_group', 'EuclideanGroup') +lazy_import('sage.groups.artin', 'ArtinGroup') lazy_import('sage.groups.raag', 'RightAngledArtinGroup') lazy_import('sage.groups', 'groups_catalog', 'groups') diff --git a/src/sage/groups/artin.py b/src/sage/groups/artin.py new file mode 100644 index 00000000000..206198960b9 --- /dev/null +++ b/src/sage/groups/artin.py @@ -0,0 +1,633 @@ +# -*- coding: utf-8 -*- +""" +Artin Groups + +Artin groups are implemented as a particular case of finitely presented +groups. For finite-type Artin groups, there is a specific left normal +form using the Garside structure associated to the lift the long element +of the corresponding Coxeter group. + +AUTHORS: + +- Travis Scrimshaw (2018-02-05): Initial version +""" + +#**************************************************************************** +# Copyright (C) 2018 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** +from __future__ import division, absolute_import, print_function +import six + +from sage.misc.lazy_attribute import lazy_attribute +from sage.misc.cachefunc import cached_method +from sage.groups.free_group import FreeGroup +from sage.groups.finitely_presented import FinitelyPresentedGroup, FinitelyPresentedGroupElement +from sage.combinat.root_system.coxeter_matrix import CoxeterMatrix +from sage.combinat.root_system.coxeter_group import CoxeterGroup +from sage.rings.infinity import Infinity +from sage.structure.richcmp import richcmp, rich_to_bool + + +class ArtinGroupElement(FinitelyPresentedGroupElement): + """ + An element of an Artin group. + + It is a particular case of element of a finitely presented group. + + EXAMPLES:: + + sage: A. = ArtinGroup(['B',3]) + sage: A + Artin group of type ['B', 3] + sage: s1 * s2 / s3 / s2 + s1*s2*s3^-1*s2^-1 + sage: A((1, 2, -3, -2)) + s1*s2*s3^-1*s2^-1 + """ + def _latex_(self): + r""" + Return a LaTeX representation of ``self``. + + OUTPUT: + + String. A valid LaTeX math command sequence. + + TESTS:: + + sage: A = ArtinGroup(['B',3]) + sage: b = A([1, 2, 3, -1, 2, -3]) + sage: b._latex_() + '\\sigma_{1}\\sigma_{2}\\sigma_{3}\\sigma_{1}^{-1}\\sigma_{2}\\sigma_{3}^{-1}' + """ + return ''.join("\sigma_{%s}^{-1}" % (-i) if i < 0 else "\sigma_{%s}" % i + for i in self.Tietze()) + + def exponent_sum(self): + """ + Return the exponent sum of ``self``. + + OUTPUT: + + Integer. + + EXAMPLES:: + + sage: A = ArtinGroup(['E',6]) + sage: b = A([1, 4, -3, 2]) + sage: b.exponent_sum() + 2 + sage: b = A([]) + sage: b.exponent_sum() + 0 + """ + return sum(s.sign() for s in self.Tietze()) + + def coxeter_group_element(self): + """ + Return the corresponding Coxeter group element under the natural + projection. + + OUTPUT: + + A permutation. + + EXAMPLES:: + + sage: A. = ArtinGroup(['B',3]) + sage: b = s1 * s2 / s3 / s2 + sage: b.coxeter_group_element() + [ 1 -1 0] + [ 2 -1 0] + [ a -a 1] + sage: b.coxeter_group_element().reduced_word() + [1, 2, 3, 2] + """ + W = self.parent().coxeter_group() + s = W.simple_reflections() + I = W.index_set() + return W.prod(s[I[abs(i)-1]] for i in self.Tietze()) + +class FiniteTypeArtinGroupElement(ArtinGroupElement): + """ + An element of a finite-type Artin group. + """ + def _richcmp_(self, other, op): + """ + Compare ``self`` and ``other``. + + TESTS:: + + sage: A = ArtinGroup(['B',3]) + sage: x = A([1, 2, 1]) + sage: y = A([2, 1, 2]) + sage: x == y + True + sage: x < y^(-1) + True + sage: A([]) == A.one() + True + sage: x = A([2, 3, 2, 3]) + sage: y = A([3, 2, 3, 2]) + sage: x == y + True + sage: x < y^(-1) + True + """ + if self.Tietze() == other.Tietze(): + return rich_to_bool(op, 0) + nfself = [i.Tietze() for i in self.left_normal_form()] + nfother = [i.Tietze() for i in other.left_normal_form()] + return richcmp(nfself, nfother, op) + + def __hash__(self): + r""" + Return a hash value for ``self``. + + EXAMPLES:: + + sage: B. = ArtinGroup(['B',3]) + sage: hash(s1*s3) == hash(s3*s1) + True + sage: hash(s1*s2) == hash(s2*s1) + False + sage: hash(s1*s2*s1) == hash(s2*s1*s2) + True + sage: hash(s2*s3*s2) == hash(s3*s2*s3) + False + sage: hash(s2*s3*s2*s3) == hash(s3*s2*s3*s2) + True + """ + return hash(tuple(i.Tietze() for i in self.left_normal_form())) + + @cached_method + def left_normal_form(self): + """ + Return the left normal form of ``self``. + + OUTPUT: + + A tuple of simple generators in the left normal form. The first + element is a power of `\Delta`, and the rest are elements of the + natural section lift from the corresponding Coxeter group. + + EXAMPLES:: + + sage: A = ArtinGroup(['B',3]) + sage: A([1]).left_normal_form() + (1, s1) + sage: A([-1]).left_normal_form() + (s1^-1*(s2^-1*s1^-1*s3^-1)^2*s2^-1*s3^-1, s3*(s2*s3*s1)^2*s2) + sage: A([1, 2, 2, 1, 2]).left_normal_form() + (1, s1*s2*s1, s2*s1) + sage: A([3, 3, -2]).left_normal_form() + (s1^-1*(s2^-1*s1^-1*s3^-1)^2*s2^-1*s3^-1, + s3*s1*s2*s3*s2*s1, s3, s3*s2*s3) + sage: A([1, 2, 3, -1, 2, -3]).left_normal_form() + (s1^-1*(s2^-1*s1^-1*s3^-1)^2*s2^-1*s3^-1, + (s3*s1*s2)^2*s1, s1*s2*s3*s2) + sage: A([1,2,1,3,2,1,3,2,3,3,2,3,1,2,3,1,2,3,1,2]).left_normal_form() + ((s3*(s2*s3*s1)^2*s2*s1)^2, s3*s2) + """ + lnfp = self._left_normal_form_coxeter() + P = self.parent() + return tuple([P.delta() ** lnfp[0]] + + [P(w.reduced_word()) for w in lnfp[1:]]) + + def _left_normal_form_coxeter(self): + """ + Return the left normal form of the element, in the `\Delta` + exponent and Coxeter group element form. + + OUTPUT: + + A tuple whose first element is the power of `\Delta`, and the rest + are the Coxeter elements corresponding to the simple factors. + + EXAMPLES:: + + sage: A = ArtinGroup(['E',6]) + sage: A([2, -4, 2, 3, 1, 3, 2, 1, -2])._left_normal_form_coxeter() + ( + [ 0 0 0 0 0 -1] [ 0 0 -1 1 0 0] [-1 0 1 0 0 0] + [ 0 1 0 -1 0 0] [ 0 -1 0 1 0 0] [ 0 -1 0 1 0 0] + [ 0 0 0 0 -1 0] [-1 0 0 1 0 0] [ 0 0 1 0 0 0] + [ 0 1 -1 0 -1 0] [-1 -1 0 1 1 0] [ 0 0 0 1 0 0] + [ 0 0 -1 0 0 0] [ 0 0 0 0 1 0] [ 0 0 0 0 1 0] + -1, [-1 0 0 0 0 0], [ 0 0 0 0 0 1], [ 0 0 0 0 0 1] + ) + sage: A = ArtinGroup(['F',4]) + sage: A([2, 3, -4, 2, 3, -2, 1, -2, 3, 4, 1, -2])._left_normal_form_coxeter() + ( + [-1 0 0 0] [ -1 1 0 0] [-1 0 0 0] + [-1 -1 a -a] [ -2 3 -a 0] [-1 1 -a 0] + [-a 0 1 -2] [ -a 2*a -1 -1] [ 0 0 -1 0] + -3, [-a 0 1 -1], [ -a a 0 -1], [ 0 0 0 -1], + + [ -1 1 0 -a] [ -1 0 a -a] + [ 0 1 0 -2*a] [ -1 1 a -2*a] + [ 0 a -1 -2] [ 0 0 2 -3] + [ 0 a -1 -1], [ 0 0 1 -1] + ) + """ + delta = 0 + Delta = self.parent().coxeter_group().long_element() + sr = self.parent().coxeter_group().simple_reflections() + l = self.Tietze() + if l == (): + return (0,) + form = [] + for i in l: + if i > 0: + form.append(sr[i]) + else: + delta += 1 + form = [Delta * a * Delta for a in form] + form.append(Delta * sr[-i]) + i = j = 0 + while j < len(form): + while i < len(form) - j - 1: + e = form[i].descents(side='right') + s = form[i + 1].descents(side='left') + S = set(s).difference(set(e)) + while S: + a = list(S)[0] + form[i] = form[i] * sr[a] + form[i + 1] = sr[a] * form[i+1] + e = form[i].descents(side='right') + s = form[i + 1].descents(side='left') + S = set(s).difference(set(e)) + if form[i+1].length() == 0: + form.pop(i+1) + i = 0 + else: + i += 1 + j += 1 + i = 0 + form = [elt for elt in form if elt.length()] + while form and form[0] == Delta: + form.pop(0) + delta -= 1 + return tuple([-delta] + form) + +class ArtinGroup(FinitelyPresentedGroup): + r""" + An Artin group. + + Fix an index set `I`. Let `M = (m_{ij})_{i,j \in I}` be a + :class:`Coxeter matrix + `. + An *Artin group* is a group `A_M` that has a presentation + given by generators `\{ s_i \mid i \in I \}` and relations + + .. MATH:: + + \underbrace{s_i s_j s_i \cdots}_{m_{ij}} + = \underbrace{s_j s_i s_j \cdots}_{\text{$m_{ji}$ factors}} + + for all `i,j \in I` with the usual convention that `m_{ij} = \infty` + implies no relation between `s_i` and `s_j`. There is a natural + corresponding Coxeter group `W_M` by imposing the additional + relations `s_i^2 = 1` for all `i \in I`. Furthermore, there is + a natural section of `W_M` by sending a reduced word + `s_{i_1} \cdots s_{i_{\ell}} \mapsto s_{i_1} \cdots s_{i_{\ell}}`. + + Artin groups `A_M` are classified based on the Coxeter data: + + - `A_M` is of *finite type* or *spherical* if `W_M` is finite; + - `A_M` is of *affine type* if `W_M` is of affine type; + - `A_M` is of *large type* if `m_{ij} \geq 4` for all `i,j \in I`; + - `A_M` is of *extra-large type* if `m_{ij} \geq 5` for all `i,j \in I`; + - `A_M` is *right-angled* if `m_{ij} \in \{2,\infty\}` for all `i,j \in I`. + + Artin groups are conjectured to have many nice properties: + + - Artin groups are torsion free. + - Finite type Artin groups have `Z(A_M) = \ZZ` and infinite type + Artin groups have trivial center. + - Artin groups have solvable word problems. + - `H_{W_M} / W_M` is a `K(A_M, 1)`-space, where `H_W` is the + hyperplane complement of the Coxeter group `W` acting on `\CC^n`. + + These conjectures are known when the Artin group is finite type and a + number of other cases. See, e.g., [GP2012]_ and references therein. + + INPUT: + + - ``coxeter_data`` -- data defining a Coxeter matrix + + - ``names`` -- string or list/tuple/iterable of strings + (default: ``'s'``); the generator names or name prefix + + EXAMPLES:: + + sage: A. = ArtinGroup(['B',3]); A + Artin group of type ['B', 3] + sage: ArtinGroup(['B',3]) + Artin group of type ['B', 3] + + The input must always include the Coxeter data, but the ``names`` + can either be a string representing the prefix of the names or + the explicit names of the generators. Otherwise the default prefix + of ``'s'`` is used:: + + sage: ArtinGroup(['B',2]).generators() + (s1, s2) + sage: ArtinGroup(['B',2], 'g').generators() + (g1, g2) + sage: ArtinGroup(['B',2], 'x,y').generators() + (x, y) + + REFERENCES: + + - :wikipedia:`Artin_group` + + .. SEEALSO:: + + :class:`~sage.groups.raag.RightAngledArtinGroup` + """ + @staticmethod + def __classcall_private__(cls, coxeter_data, names=None): + """ + Normalize input to ensure a unique representation. + + TESTS:: + + sage: A1 = ArtinGroup(['B',3]) + sage: A2 = ArtinGroup(['B',3], 's') + sage: A3 = ArtinGroup(['B',3], ['s1','s2','s3']) + sage: A1 is A2 and A2 is A3 + True + + sage: A1 = ArtinGroup(['B',2], 'a,b') + sage: A2 = ArtinGroup([[1,4],[4,1]], 'a,b') + sage: A3. = ArtinGroup('B2') + sage: A1 is A2 and A2 is A3 + True + + sage: ArtinGroup(['A',3]) is BraidGroup(4, 's1,s2,s3') + True + """ + coxeter_data = CoxeterMatrix(coxeter_data) + if names is None: + names = 's' + if isinstance(names, six.string_types): + if ',' in names: + names = [x.strip() for x in names.split(',')] + else: + names = [names + str(i) for i in coxeter_data.index_set()] + names = tuple(names) + if len(names) != coxeter_data.rank(): + raise ValueError("the number of generators must match" + " the rank of the Coxeter type") + if all(m == Infinity for m in coxeter_data.coxeter_graph().edge_labels()): + from sage.groups.raag import RightAngledArtinGroup + return RightAngledArtinGroup(coxeter_data.coxeter_graph()) + if not coxeter_data.is_finite(): + raise NotImplementedError + if coxeter_data.coxeter_type().cartan_type().type() == 'A': + from sage.groups.braid import BraidGroup + return BraidGroup(coxeter_data.rank()+1, names) + return FiniteTypeArtinGroup(coxeter_data, names) + + def __init__(self, coxeter_matrix, names): + """ + Initialize ``self``. + + TESTS:: + + sage: A = ArtinGroup(['D',4]) + sage: TestSuite(A).run() + sage: A = ArtinGroup(['B',3], ['x','y','z']) + sage: TestSuite(A).run() + """ + self._coxeter_group = CoxeterGroup(coxeter_matrix) + free_group = FreeGroup(names) + rels = [] + # Generate the relations based on the Coxeter graph + I = coxeter_matrix.index_set() + for ii,i in enumerate(I): + for j in I[ii+1:]: + m = coxeter_matrix[i,j] + if m == Infinity: # no relation + continue + elt = [i,j]*m + for ind in range(m, 2*m): + elt[ind] = -elt[ind] + rels.append(free_group(elt)) + FinitelyPresentedGroup.__init__(self, free_group, tuple(rels)) + + def _repr_(self): + """ + Return a string representation of ``self``. + + TESTS:: + + sage: ArtinGroup(['B',3]) + Artin group of type ['B', 3] + sage: ArtinGroup(['D',4], 'g') + Artin group of type ['D', 4] + """ + try: + data = self.coxeter_type().cartan_type() + return "Artin group of type {}".format(data) + except AttributeError: + pass + return "Artin group with Coxeter matrix:\n{}".format(self.coxeter_matrix()) + + def cardinality(self): + """ + Return the number of group elements. + + OUTPUT: + + Infinity. + + TESTS:: + + sage: A = ArtinGroup(['A',1]) + sage: A.cardinality() + +Infinity + """ + from sage.rings.infinity import Infinity + return Infinity + + order = cardinality + + def as_permutation_group(self): + """ + Return an isomorphic permutation group. + + OUTPUT: + + Raises a ``ValueError`` error since Artin groups are infinite. + + TESTS:: + + sage: A = ArtinGroup(['D',4], 'g') + sage: A.as_permutation_group() + Traceback (most recent call last): + ... + ValueError: the group is infinite + """ + raise ValueError("the group is infinite") + + def coxeter_type(self): + """ + Return the Coxeter type of ``self``. + + EXAMPLES:: + + sage: A = ArtinGroup(['D',4]) + sage: A.coxeter_type() + Coxeter type of ['D', 4] + """ + return self._coxeter_group.coxeter_type() + + def coxeter_matrix(self): + """ + Return the Coxeter type of ``self``. + + EXAMPLES:: + + sage: A = ArtinGroup(['B',3]) + sage: A.coxeter_matrix() + [1 3 2] + [3 1 4] + [2 4 1] + """ + return self._coxeter_group.coxeter_matrix() + + def coxeter_group(self): + """ + Return the Coxeter group of ``self``. + + EXAMPLES:: + + sage: A = ArtinGroup(['D',4]) + sage: A.coxeter_group() + Finite Coxeter group over Integer Ring with Coxeter matrix: + [1 3 2 2] + [3 1 3 3] + [2 3 1 2] + [2 3 2 1] + """ + return self._coxeter_group + + def index_set(self): + """ + Return the index set of ``self``. + + OUTPUT: + + A tuple. + + EXAMPLES:: + + sage: A = ArtinGroup(['E',7]) + sage: A.index_set() + (1, 2, 3, 4, 5, 6, 7) + """ + return self._coxeter_group.index_set() + + def _element_constructor_(self, x): + """ + TESTS:: + + sage: A = ArtinGroup(['B',3]) + sage: A([2,1,-2,3,3,3,1]) + s2*s1*s2^-1*s3^3*s1 + """ + return self.element_class(self, x) + + @cached_method + def an_element(self): + """ + Return an element of ``self``. + + EXAMPLES:: + + sage: A = ArtinGroup(['B',2]) + sage: A.an_element() + s1 + """ + return self.gen(0) + + def some_elements(self): + """ + Return a list of some elements of ``self``. + + EXAMPLES:: + + sage: A = ArtinGroup(['B',3]) + sage: A.some_elements() + [s1, s1*s2*s3, (s1*s2*s3)^3] + """ + rank = self.coxeter_matrix().rank() + elements_list = [self.gen(0)] + elements_list.append(self.prod(self.gens())) + elements_list.append(elements_list[-1] ** rank) + return elements_list + + Element = ArtinGroupElement + +class FiniteTypeArtinGroup(ArtinGroup): + """ + A finite-type Artin group. + + An Artin group is *finite-type* or *spherical* if the + corresponding Coxeter group is finite. + + .. SEEALSO:: + + :class:`ArtinGroup` + + EXAMPLES:: + + sage: ArtinGroup(['E',7]) + Artin group of type ['E', 7] + + Since the word problem for finite-type Artin groups is solvable, their + Cayley graph can be locally obtained as follows (see :trac:`16059`):: + + sage: def ball(group, radius): + ....: ret = set() + ....: ret.add(group.one()) + ....: for length in range(1, radius): + ....: for w in Words(alphabet=group.gens(), length=length): + ....: ret.add(prod(w)) + ....: return ret + sage: A = ArtinGroup(['B',3]) + sage: GA = A.cayley_graph(elements=ball(A, 4), generators=A.gens()); GA + Digraph on 32 vertices + + Since the Artin group has nontrivial relations, this graph contains less + vertices than the one associated to the free group (which is a tree):: + + sage: F = FreeGroup(3) + sage: GF = F.cayley_graph(elements=ball(F, 4), generators=F.gens()); GF + Digraph on 40 vertices + """ + def delta(self): + r""" + Return the `\Delta` element of ``self``. + + EXAMPLES:: + + sage: A = ArtinGroup(['B',3]) + sage: A.delta() + s3*(s2*s3*s1)^2*s2*s1 + + sage: A = ArtinGroup(['G',2]) + sage: A.delta() + (s2*s1)^3 + """ + return self(self._coxeter_group.long_element().reduced_word()) + + Element = FiniteTypeArtinGroupElement + diff --git a/src/sage/groups/misc_gps/misc_groups_catalog.py b/src/sage/groups/misc_gps/misc_groups_catalog.py index 34428cc125a..44399554a26 100644 --- a/src/sage/groups/misc_gps/misc_groups_catalog.py +++ b/src/sage/groups/misc_gps/misc_groups_catalog.py @@ -17,6 +17,7 @@ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing as AdditiveCyclic from sage.groups.free_group import FreeGroup as Free from sage.groups.braid import BraidGroup as Braid +from sage.groups.braid import ArtinGroup as Artin from sage.groups.semimonomial_transformations.semimonomial_transformation_group import SemimonomialTransformationGroup as SemimonomialTransformation from sage.combinat.root_system.coxeter_group import CoxeterGroup from sage.combinat.root_system.weyl_group import WeylGroup From 3df9f9ffabb2b7ec588646160c337521089c30bd Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 5 Feb 2018 12:24:09 +0000 Subject: [PATCH 646/740] correctly linking Flint on SunOS and FreeBSD backported from https://github.com/wbhart/flint2/commit/6a9fb5340828918707ab9aadb5f842fb1a1e5952 --- build/pkgs/flint/package-version.txt | 2 +- .../flint/patches/linking_SunOS_FreeBSD.patch | 42 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/flint/patches/linking_SunOS_FreeBSD.patch diff --git a/build/pkgs/flint/package-version.txt b/build/pkgs/flint/package-version.txt index 5a8449ee660..2b89f38efd6 100644 --- a/build/pkgs/flint/package-version.txt +++ b/build/pkgs/flint/package-version.txt @@ -1 +1 @@ -2.5.2.p1 +2.5.2.p2 diff --git a/build/pkgs/flint/patches/linking_SunOS_FreeBSD.patch b/build/pkgs/flint/patches/linking_SunOS_FreeBSD.patch new file mode 100644 index 00000000000..de53ccf2bf6 --- /dev/null +++ b/build/pkgs/flint/patches/linking_SunOS_FreeBSD.patch @@ -0,0 +1,42 @@ +diff --git a/Makefile.in b/Makefile.in +index 2d0475d..a8b2b44 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -118,6 +118,7 @@ $(FLINT_LIB): $(LOBJS) $(LIB_SOURCES) $(EXT_SOURCES) $(HEADERS) $(EXT_HEADERS) | + $(LDCONFIG) -n "$(CURDIR)"; \ + fi + ln -sf "$(FLINT_LIB)" "$(FLINT_LIBNAME)"; \ ++ ln -sf "$(FLINT_LIB)" "$(FLINT_LIBNAME).$(FLINT_MAJOR)"; \ + + libflint.a: $(OBJS) $(LIB_SOURCES) $(EXT_SOURCES) $(HEADERS) $(EXT_HEADERS) | build build/interfaces + $(AT)$(foreach ext, $(EXTENSIONS), $(foreach dir, $(filter-out %templates, $(patsubst $(ext)/%.h, %, $(wildcard $(ext)/*.h))), mkdir -p build/$(dir); BUILD_DIR=$(CURDIR)/build/$(dir); export BUILD_DIR; MOD_DIR=$(dir); export MOD_DIR; $(MAKE) -f $(CURDIR)/Makefile.subdirs -C $(ext)/$(dir) static || exit $$?;)) +diff --git a/configure b/configure +index 424ab0a..959a650 100755 +--- a/configure ++++ b/configure +@@ -450,7 +450,7 @@ fi + + # sometimes LDCONFIG is not to be found in the path. Look at some common places. + case "$OS" in +- MINGW*|CYGWIN*|Darwin) ++ MINGW*|CYGWIN*|Darwin|FreeBSD) + LDCONFIG="true";; + *) + if [ -z "$LDCONFIG" ]; then +@@ -551,6 +551,8 @@ if [ -z "$CFLAGS" ]; then + CFLAGS="-O2 -funroll-loops -g -D _WIN64 $POPCNT_FLAG $ABI_FLAG" + elif [ "$MACHINE" = "mips64" ]; then + CFLAGS="-O2 -funroll-loops -g $POPCNT_FLAG $ABI_FLAG" ++ elif [ "$KERNEL" = FreeBSD ]; then ++ CFLAGS="-std=c99 -pedantic -Wall -O2 -funroll-loops -g $POPCNT_FLAG $ABI_FLAG $OPENMP_FLAG" + else + CFLAGS="-ansi -pedantic -Wall -O2 -funroll-loops -g $POPCNT_FLAG $ABI_FLAG" + fi +@@ -715,6 +717,7 @@ echo "FLINT_SHARED=$SHARED" >> Makefile + echo "FLINT_LIB=$FLINT_LIB" >> Makefile + echo "FLINT_LIBNAME=$FLINT_LIBNAME" >> Makefile + echo "FLINT_SOLIB=$FLINT_SOLIB" >> Makefile ++echo "FLINT_MAJOR=$FLINT_MAJOR" >> Makefile + echo "EXEEXT=$EXEEXT" >> Makefile + echo "PREFIX=$PREFIX" >> Makefile + echo "" >> Makefile From 365e0a03056902f5361c835a05308ce8b9224c16 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 5 Feb 2018 20:04:41 -0600 Subject: [PATCH 647/740] Making RAAG's compatible and fixing some bugs. --- src/sage/groups/artin.py | 39 ++++++--- src/sage/groups/raag.py | 167 +++++++++++++++++++++++---------------- 2 files changed, 127 insertions(+), 79 deletions(-) diff --git a/src/sage/groups/artin.py b/src/sage/groups/artin.py index 206198960b9..9d6d602bb16 100644 --- a/src/sage/groups/artin.py +++ b/src/sage/groups/artin.py @@ -24,7 +24,6 @@ from __future__ import division, absolute_import, print_function import six -from sage.misc.lazy_attribute import lazy_attribute from sage.misc.cachefunc import cached_method from sage.groups.free_group import FreeGroup from sage.groups.finitely_presented import FinitelyPresentedGroup, FinitelyPresentedGroupElement @@ -372,6 +371,13 @@ def __classcall_private__(cls, coxeter_data, names=None): sage: ArtinGroup(['A',3]) is BraidGroup(4, 's1,s2,s3') True + + sage: G = graphs.Path(3) + sage: CM = CoxeterMatrix([[1,-1,2],[-1,1,-1],[2,-1,1]], index_set=G.vertices()) + sage: A = groups.Artin(CM) + sage: Ap = groups.RightAngledArtin(G, 's') + sage: A is Ap + True """ coxeter_data = CoxeterMatrix(coxeter_data) if names is None: @@ -387,7 +393,7 @@ def __classcall_private__(cls, coxeter_data, names=None): " the rank of the Coxeter type") if all(m == Infinity for m in coxeter_data.coxeter_graph().edge_labels()): from sage.groups.raag import RightAngledArtinGroup - return RightAngledArtinGroup(coxeter_data.coxeter_graph()) + return RightAngledArtinGroup(coxeter_data.coxeter_graph(), names) if not coxeter_data.is_finite(): raise NotImplementedError if coxeter_data.coxeter_type().cartan_type().type() == 'A': @@ -442,13 +448,18 @@ def _repr_(self): def cardinality(self): """ - Return the number of group elements. + Return the number of elements of ``self``. OUTPUT: Infinity. - TESTS:: + EXAMPLES:: + + sage: Gamma = graphs.CycleGraph(5) + sage: G = RightAngledArtinGroup(Gamma) + sage: G.cardinality() + +Infinity sage: A = ArtinGroup(['A',1]) sage: A.cardinality() @@ -463,11 +474,17 @@ def as_permutation_group(self): """ Return an isomorphic permutation group. - OUTPUT: + Raises a ``ValueError`` error since Artin groups are infinite + and have no corresponding permutation group. - Raises a ``ValueError`` error since Artin groups are infinite. + EXAMPLES:: - TESTS:: + sage: Gamma = graphs.CycleGraph(5) + sage: G = RightAngledArtinGroup(Gamma) + sage: G.as_permutation_group() + Traceback (most recent call last): + ... + ValueError: the group is infinite sage: A = ArtinGroup(['D',4], 'g') sage: A.as_permutation_group() @@ -571,7 +588,7 @@ def some_elements(self): rank = self.coxeter_matrix().rank() elements_list = [self.gen(0)] elements_list.append(self.prod(self.gens())) - elements_list.append(elements_list[-1] ** rank) + elements_list.append(elements_list[-1] ** min(rank,3)) return elements_list Element = ArtinGroupElement @@ -580,8 +597,10 @@ class FiniteTypeArtinGroup(ArtinGroup): """ A finite-type Artin group. - An Artin group is *finite-type* or *spherical* if the - corresponding Coxeter group is finite. + An Artin group is *finite-type* or *spherical* if the corresponding + Coxeter group is finite. Finite type Artin groups are known to be + torsion free, have a Garside structure given by `\Delta` (see + :meth:`delta`) and have a center generated by `\Delta`. .. SEEALSO:: diff --git a/src/sage/groups/raag.py b/src/sage/groups/raag.py index b3f434ba94b..dbe06b18494 100644 --- a/src/sage/groups/raag.py +++ b/src/sage/groups/raag.py @@ -9,25 +9,33 @@ AUTHORS: - Travis Scrimshaw (2013-09-01): Initial version +- Travis Scrimshaw (2018-02-05): Made compatible with + :class:`~sage.groups.artin.ArtinGroup` """ -############################################################################## -# Copyright (C) 2013 Travis Scrimshaw -# -# Distributed under the terms of the GNU General Public License (GPL) -# -# The full text of the GPL is available at: +#**************************************************************************** +# Copyright (C) 2013,2018 Travis Scrimshaw # +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ -############################################################################## +#***************************************************************************** + +from __future__ import division, absolute_import, print_function +import six from sage.misc.cachefunc import cached_method +from sage.structure.richcmp import richcmp from sage.groups.finitely_presented import FinitelyPresentedGroup, FinitelyPresentedGroupElement from sage.groups.free_group import FreeGroup +from sage.groups.artin import ArtinGroup, ArtinGroupElement from sage.graphs.graph import Graph +from sage.combinat.root_system.coxeter_matrix import CoxeterMatrix +from sage.combinat.root_system.coxeter_group import CoxeterGroup - -class RightAngledArtinGroup(FinitelyPresentedGroup): +class RightAngledArtinGroup(ArtinGroup): r""" The right-angled Artin group defined by a graph `G`. @@ -72,6 +80,11 @@ class RightAngledArtinGroup(FinitelyPresentedGroup): The normal forms for RAAG's in Sage are those described in [VW1994]_ and gathers commuting groups together. + INPUT: + + - ``G`` -- a graph + - ``names`` -- a string or a list of generator names + EXAMPLES:: sage: Gamma = Graph(4) @@ -91,25 +104,33 @@ class RightAngledArtinGroup(FinitelyPresentedGroup): sage: G Right-angled Artin group of Cycle graph sage: a,b,c,d,e = G.gens() + sage: d*b*a*d + v1*v3^2*v0 sage: e^-1*c*b*e*b^-1*c^-4 v2^-3 + We create the previous example but with different variable names:: + + sage: G. = RightAngledArtinGroup(Gamma) + sage: G + Right-angled Artin group of Cycle graph + sage: d*b*a*d + b*d^2*a + sage: e^-1*c*b*e*b^-1*c^-4 + c^-3 + REFERENCES: - [Cha2006]_ - - [BB1997]_ - - [Dro1987]_ - - [CP2001]_ - - [VW1994]_ - :wikipedia:`Artin_group#Right-angled_Artin_groups` """ @staticmethod - def __classcall_private__(cls, G): + def __classcall_private__(cls, G, names=None): """ Normalize input to ensure a unique representation. @@ -119,7 +140,8 @@ def __classcall_private__(cls, G): sage: Gamma = Graph([(0,1),(1,2),(2,3),(3,4),(4,0)]) sage: G2 = RightAngledArtinGroup(Gamma) sage: G3 = RightAngledArtinGroup([(0,1),(1,2),(2,3),(3,4),(4,0)]) - sage: G1 is G2 and G2 is G3 + sage: G4 = RightAngledArtinGroup(Gamma, 'v') + sage: G1 is G2 and G2 is G3 and G3 is G4 True Handle the empty graph:: @@ -135,27 +157,41 @@ def __classcall_private__(cls, G): G = G.copy(immutable=True) if G.num_verts() == 0: raise ValueError("the graph must not be empty") - return super(RightAngledArtinGroup, cls).__classcall__(cls, G) - - def __init__(self, G): + if names is None: + names = 'v' + if isinstance(names, six.string_types): + if ',' in names: + names = [x.strip() for x in names.split(',')] + else: + names = [names + str(v) for v in G.vertices()] + names = tuple(names) + if len(names) != G.num_verts(): + raise ValueError("the number of generators must match the" + " number of vertices of the defining graph") + return super(RightAngledArtinGroup, cls).__classcall__(cls, G, names) + + def __init__(self, G, names): """ Initialize ``self``. - INPUT: - - - ``G`` -- a graph - TESTS:: sage: G = RightAngledArtinGroup(graphs.CycleGraph(5)) sage: TestSuite(G).run() """ self._graph = G - F = FreeGroup(names=['v{}'.format(v) for v in self._graph.vertices()]) + F = FreeGroup(names=names) CG = Graph(G).complement() # Make sure it's mutable CG.relabel() # Standardize the labels + cm = [[-1]*CG.num_verts() for _ in range(CG.num_verts())] + for i in range(CG.num_verts()): + cm[i][i] = 1 + for u,v in CG.edge_iterator(labels=False): + cm[u][v] = 2 + cm[v][u] = 2 + self._coxeter_group = CoxeterGroup(CoxeterMatrix(cm, index_set=G.vertices())) rels = tuple(F([i + 1, j + 1, -i - 1, -j - 1]) - for i, j in CG.edges(False)) # +/- 1 for indexing + for i, j in CG.edge_iterator(labels=False)) # +/- 1 for indexing FinitelyPresentedGroup.__init__(self, F, rels) def _repr_(self): @@ -180,7 +216,7 @@ def gen(self, i): sage: G.gen(2) v2 """ - return self.element_class(self, ((i, 1),)) + return self.element_class(self, ([i, 1],)) def gens(self): """ @@ -212,42 +248,6 @@ def ngens(self): """ return self._graph.num_verts() - def cardinality(self): - r""" - Return the number of group elements. - - OUTPUT: - - Infinity. - - EXAMPLES:: - - sage: Gamma = graphs.CycleGraph(5) - sage: G = RightAngledArtinGroup(Gamma) - sage: G.cardinality() - +Infinity - """ - from sage.rings.infinity import Infinity - return Infinity - - order = cardinality - - def as_permutation_group(self): - r""" - Raise a ``ValueError`` error since right-angled Artin groups - are infinite, so they have no isomorphic permutation group. - - EXAMPLES:: - - sage: Gamma = graphs.CycleGraph(5) - sage: G = RightAngledArtinGroup(Gamma) - sage: G.as_permutation_group() - Traceback (most recent call last): - ... - ValueError: the group is infinite - """ - raise ValueError("the group is infinite") - def graph(self): """ Return the defining graph of ``self``. @@ -326,7 +326,7 @@ def _normal_form(self, word): pos = 0 G = self._graph v = G.vertices() - w = [list(_) for _ in word] # Make a (2 level) deep copy + w = [list(x) for x in word] # Make a (2 level) deep copy while pos < len(w): comm_set = [w[pos][0]] # The current set of totally commuting elements @@ -337,7 +337,8 @@ def _normal_form(self, word): # Check if this could fit in the commuting set if letter in comm_set: # Try to move it in - if any(G.has_edge(v[w[j][0]], v[letter]) for j in range(pos + len(comm_set), i)): + if any(G.has_edge(v[w[j][0]], v[letter]) + for j in range(pos + len(comm_set), i)): # We can't, so go onto the next letter i += 1 continue @@ -368,7 +369,7 @@ def _normal_form(self, word): pos += len(comm_set) return tuple(w) - class Element(FinitelyPresentedGroupElement): + class Element(ArtinGroupElement): """ An element of a right-angled Artin group (RAAG). @@ -428,16 +429,19 @@ def _repr_(self): sage: x,y,z = G.gens() sage: z * y^-2 * x^3 vzeta*vy^-2*vx^3 + sage: G. = RightAngledArtinGroup(Gamma) + sage: c * b^-2 * a^3 + c*b^-2*a^3 """ if not self._data: return '1' - v = self.parent()._graph.vertices() + v = self.parent().variable_names() def to_str(name, p): if p == 1: - return "v{}".format(name) + return "{}".format(name) else: - return "v{}^{}".format(name, p) + return "{}^{}".format(name, p) return '*'.join(to_str(v[i], p) for i, p in self._data) @@ -517,11 +521,11 @@ def __pow__(self, n): return P.one() if n < 0: - lst = sum([self._data for i in range(-n)], ()) # Positive product + lst = sum((self._data for i in range(-n)), ()) # Positive product lst = [[x[0], -x[1]] for x in reversed(lst)] # Now invert return self.__class__(P, P._normal_form(lst)) - lst = sum([self._data for i in range(n)], ()) + lst = sum((self._data for i in range(n)), ()) return self.__class__(self.parent(), P._normal_form(lst)) def __invert__(self): @@ -539,3 +543,28 @@ def __invert__(self): P = self.parent() lst = [[x[0], -x[1]] for x in reversed(self._data)] return self.__class__(P, P._normal_form(lst)) + + def _richcmp_(self, other, op): + """ + Compare ``self`` and ``other``. + + TESTS:: + + sage: A = ArtinGroup(['B',3]) + sage: x = A([1, 2, 1]) + sage: y = A([2, 1, 2]) + sage: x == y + True + sage: x < y^(-1) + True + sage: A([]) == A.one() + True + sage: x = A([2, 3, 2, 3]) + sage: y = A([3, 2, 3, 2]) + sage: x == y + True + sage: x < y^(-1) + True + """ + return richcmp(self._data, other._data, op) + From 43ea78e6b51bf93154b0425a3869739546750f24 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 5 Feb 2018 20:44:43 -0600 Subject: [PATCH 648/740] Subclass braid group (elements) from Artin group (elements). Fix some bugs. --- src/sage/groups/artin.py | 77 +++++- src/sage/groups/braid.py | 229 +++++------------- .../groups/misc_gps/misc_groups_catalog.py | 2 +- 3 files changed, 137 insertions(+), 171 deletions(-) diff --git a/src/sage/groups/artin.py b/src/sage/groups/artin.py index 9d6d602bb16..64bdd9c2585 100644 --- a/src/sage/groups/artin.py +++ b/src/sage/groups/artin.py @@ -63,6 +63,11 @@ def _latex_(self): sage: b = A([1, 2, 3, -1, 2, -3]) sage: b._latex_() '\\sigma_{1}\\sigma_{2}\\sigma_{3}\\sigma_{1}^{-1}\\sigma_{2}\\sigma_{3}^{-1}' + + sage: B = BraidGroup(4) + sage: b = B([1, 2, 3, -1, 2, -3]) + sage: b._latex_() + '\\sigma_{1}\\sigma_{2}\\sigma_{3}\\sigma_{1}^{-1}\\sigma_{2}\\sigma_{3}^{-1}' """ return ''.join("\sigma_{%s}^{-1}" % (-i) if i < 0 else "\sigma_{%s}" % i for i in self.Tietze()) @@ -84,6 +89,14 @@ def exponent_sum(self): sage: b = A([]) sage: b.exponent_sum() 0 + + sage: B = BraidGroup(5) + sage: b = B([1, 4, -3, 2]) + sage: b.exponent_sum() + 2 + sage: b = B([]) + sage: b.exponent_sum() + 0 """ return sum(s.sign() for s in self.Tietze()) @@ -192,11 +205,19 @@ def left_normal_form(self): (s3*s1*s2)^2*s1, s1*s2*s3*s2) sage: A([1,2,1,3,2,1,3,2,3,3,2,3,1,2,3,1,2,3,1,2]).left_normal_form() ((s3*(s2*s3*s1)^2*s2*s1)^2, s3*s2) + + sage: B = BraidGroup(4) + sage: b = B([1, 2, 3, -1, 2, -3]) + sage: b.left_normal_form() + (s0^-1*s1^-1*s2^-1*s0^-1*s1^-1*s0^-1, s0*s1*s2*s1*s0, s0*s2*s1) + sage: c = B([1]) + sage: c.left_normal_form() + (1, s0) """ lnfp = self._left_normal_form_coxeter() P = self.parent() return tuple([P.delta() ** lnfp[0]] + - [P(w.reduced_word()) for w in lnfp[1:]]) + [P._standard_lift(w) for w in lnfp[1:]]) def _left_normal_form_coxeter(self): """ @@ -372,10 +393,10 @@ def __classcall_private__(cls, coxeter_data, names=None): sage: ArtinGroup(['A',3]) is BraidGroup(4, 's1,s2,s3') True - sage: G = graphs.Path(3) + sage: G = graphs.PathGraph(3) sage: CM = CoxeterMatrix([[1,-1,2],[-1,1,-1],[2,-1,1]], index_set=G.vertices()) - sage: A = groups.Artin(CM) - sage: Ap = groups.RightAngledArtin(G, 's') + sage: A = groups.misc.Artin(CM) + sage: Ap = groups.misc.RightAngledArtin(G, 's') sage: A is Ap True """ @@ -560,6 +581,8 @@ def _element_constructor_(self, x): sage: A([2,1,-2,3,3,3,1]) s2*s1*s2^-1*s3^3*s1 """ + if x in self._coxeter_group: + return self._standard_lift(x) return self.element_class(self, x) @cached_method @@ -591,6 +614,46 @@ def some_elements(self): elements_list.append(elements_list[-1] ** min(rank,3)) return elements_list + def _standard_lift_Tietze(self, w): + """ + Return a Tietze word representing the Coxeter element ``w`` + under the natural section. + + INPUT: + + - ``w`` -- an element of the Coxeter group of ``self``. + + EXAMPLES:: + + sage: A = ArtinGroup(['B',3]) + sage: A._standard_lift_Tietze(A.coxeter_group().long_element()) + [3, 2, 3, 1, 2, 3, 1, 2, 1] + """ + return w.reduced_word() + + @cached_method + def _standard_lift(self, w): + """ + Return the element of ``self`` that corresponds to the given + Coxeter element ``w`` under the natural section. + + INPUT: + + - ``w`` -- an element of the Coxeter group of ``self``. + + EXAMPLES:: + + sage: A = ArtinGroup(['B',3]) + sage: A._standard_lift(A.coxeter_group().long_element()) + s3*(s2*s3*s1)^2*s2*s1 + + sage: B = BraidGroup(5) + sage: P = Permutation([5, 3, 1, 2, 4]) + sage: B._standard_lift(P) + s0*s1*s0*s2*s1*s3 + """ + return self(self._standard_lift_Tietze(w)) + Element = ArtinGroupElement class FiniteTypeArtinGroup(ArtinGroup): @@ -645,8 +708,12 @@ def delta(self): sage: A = ArtinGroup(['G',2]) sage: A.delta() (s2*s1)^3 + + sage: B = BraidGroup(5) + sage: B.delta() + s0*s1*s0*s2*s1*s0*s3*s2*s1*s0 """ - return self(self._coxeter_group.long_element().reduced_word()) + return self._standard_lift(self._coxeter_group.long_element()) Element = FiniteTypeArtinGroupElement diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index 7ca7df76c47..7faa3312fe1 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -71,17 +71,18 @@ from sage.groups.free_group import FreeGroup, is_FreeGroup from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing from sage.matrix.constructor import identity_matrix, matrix -from sage.combinat.permutation import Permutation +from sage.combinat.permutation import Permutations from sage.categories.action import Action from sage.sets.set import Set from sage.groups.finitely_presented import FinitelyPresentedGroup, FinitelyPresentedGroupElement +from sage.groups.artin import FiniteTypeArtinGroup, FiniteTypeArtinGroupElement from sage.misc.package import PackageNotFoundError from sage.structure.richcmp import richcmp, rich_to_bool +from sage.misc.superseded import deprecated_function_alias - -class Braid(FinitelyPresentedGroupElement): +class Braid(FiniteTypeArtinGroupElement): """ - Class that models elements of the braid group. + An element of a braid group. It is a particular case of element of a finitely presented group. @@ -131,29 +132,6 @@ def __hash__(self): """ return hash(tuple(i.Tietze() for i in self.left_normal_form())) - def _latex_(self): - """ - Return a LaTeX representation - - OUTPUT: - - String. A valid LaTeX math command sequence. - - TESTS:: - - sage: B = BraidGroup(4) - sage: b = B([1, 2, 3, -1, 2, -3]) - sage: b._latex_() - '\\sigma_{1}\\sigma_{2}\\sigma_{3}\\sigma_{1}^{-1}\\sigma_{2}\\sigma_{3}^{-1}' - """ - latexrepr = '' - for i in self.Tietze(): - if i > 0: - latexrepr = latexrepr+"\sigma_{%s}" % i - if i < 0: - latexrepr = latexrepr+"\sigma_{%s}^{-1}" % (-i) - return latexrepr - def strands(self): """ Return the number of strands in the braid. @@ -167,26 +145,6 @@ def strands(self): """ return self.parent().strands() - def exponent_sum(self): - """ - Return the exponent sum of the braid. - - OUTPUT: - - Integer. - - EXAMPLES:: - - sage: B = BraidGroup(5) - sage: b = B([1, 4, -3, 2]) - sage: b.exponent_sum() - 2 - sage: b = B([]) - sage: b.exponent_sum() - 0 - """ - return sum(s.sign() for s in self.Tietze()) - def components_in_closure(self): """ Return the number of components of the trace closure of the braid. @@ -385,11 +343,7 @@ def permutation(self): sage: b.permutation().cycle_string() '(1,4,2)' """ - per = Permutation((())) - for i in self.Tietze(): - j = abs(i) - per = per*Permutation(((j, j+1))) - return per + return self.coxeter_group_element() def plot(self, color='rainbow', orientation='bottom-top', gap=0.05, aspect_ratio=1, axes=False, **kwds): """ @@ -950,37 +904,7 @@ def jones_polynomial(self, variab=None, skein_normalization=False): # We force the result to be in the symbolic ring because of the expand return self._jones_polynomial(SR(variab)**(ZZ(1)/ZZ(4))).expand() - @cached_method - def left_normal_form(self): - """ - Return the left normal form of the braid. - - OUTPUT: - - A tuple of braid generators in the left normal form. The first - element is a power of $\Delta$, and the rest are permutation - braids. - - EXAMPLES:: - - sage: B = BraidGroup(4) - sage: b = B([1, 2, 3, -1, 2, -3]) - sage: b.left_normal_form() - (s0^-1*s1^-1*s2^-1*s0^-1*s1^-1*s0^-1, s0*s1*s2*s1*s0, s0*s2*s1) - sage: c = B([1]) - sage: c.left_normal_form() - (1, s0) - """ - lnfp = self._left_normal_form_perm_() - a = lnfp[0] - l = lnfp[1:] - n = self.strands() - delta = Permutation([n - i for i in range(n)]) - P = self.parent() - return tuple([P._permutation_braid(delta) ** a] + - [P._permutation_braid(i) for i in l]) - - def _left_normal_form_perm_(self): + def _left_normal_form_coxeter(self): """ Return the left normal form of the braid, in permutation form. @@ -992,46 +916,52 @@ def _left_normal_form_perm_(self): EXAMPLES:: sage: B = BraidGroup(12) - sage: B([2, 2, 2, 3, 1, 2, 3, 2, 1, -2])._left_normal_form_perm_() + sage: B([2, 2, 2, 3, 1, 2, 3, 2, 1, -2])._left_normal_form_coxeter() (-1, [12, 11, 10, 9, 8, 7, 6, 5, 2, 4, 3, 1], [4, 1, 3, 2, 5, 6, 7, 8, 9, 10, 11, 12], [2, 3, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12], [3, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12], [2, 3, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12]) - sage: C=BraidGroup(6) - sage: C([2, 3, -4, 2, 3, -5, 1, -2, 3, 4, 1, -2])._left_normal_form_perm_() + sage: C = BraidGroup(6) + sage: C([2, 3, -4, 2, 3, -5, 1, -2, 3, 4, 1, -2])._left_normal_form_coxeter() (-2, [3, 5, 4, 2, 6, 1], [1, 6, 3, 5, 2, 4], [5, 6, 2, 4, 1, 3], [3, 2, 4, 1, 5, 6], [1, 5, 2, 3, 4, 6]) + + .. TODO:: + + Remove this method and use the default one from + :meth:`sage.groups.artin.FiniteTypeArtinGroupElement.left_normal_form`. """ n = self.parent().strands() delta = 0 - Delta = Permutation([n-i for i in range(n)]) + Delta = self.parent()._coxeter_group.long_element() + sr = self.parent()._coxeter_group.simple_reflections() l = self.Tietze() - if l==(): + if l == (): return (0,) form = [] for i in l: - if i>0: - form.append(Permutation((i, i+1))) + if i > 0: + form.append(sr[i]) else: - delta = delta+1 - form = [Delta*a*Delta for a in form] - form.append(Delta*Permutation((-i, -i+1))) + delta += 1 + form = [Delta * a * Delta for a in form] + form.append(Delta * sr[-i]) i = j = 0 - while jpl(i+1): + while i < max(pl): + if pl(i) > pl(i+1): l.append(i) - pl = Permutation([(i, i+1)])*pl + pl = self._coxeter_group.simple_reflection(i) * pl i = 1 else: i += 1 return tuple(l) - @cached_method - def _permutation_braid(self, p): - """ - Return the braid that corresponds to the given permutation. - - It is the only braid with the following properties: - - - The braid induces the given permutation. - - - The braid is positive (that is, it can be written without - using the inverses of the generators). - - - Every two strands cross each other at most once. - - INPUT: - - - ``p`` -- a permutation. - - OUTPUT: - - The braid that corresponds to the permutation. - - EXAMPLES:: - - sage: B = BraidGroup(5) - sage: P = Permutation([5, 3, 1, 2, 4]) - sage: B._permutation_braid(P) - s0*s1*s0*s2*s1*s3 - """ - return self(self._permutation_braid_Tietze(p)) - @cached_method def _LKB_matrix_(self, braid, variab): """ @@ -1846,7 +1756,7 @@ def dimension_of_TL_space(self, drain_size): sage: B = BraidGroup(6) sage: dimensions = [B.dimension_of_TL_space(d)**2 for d in [0, 2, 4, 6]] sage: total_dim = sum(dimensions) - sage: total_dim == len(list(da.temperley_lieb_diagrams(6))) + sage: total_dim == len(list(da.temperley_lieb_diagrams(6))) # long time True """ n = self.strands() @@ -2215,19 +2125,7 @@ def _get_action_(self, S, op, self_on_left): return self.mapping_class_action(S) return None - def Delta(self): - r""" - Return the `\Delta` element of the braid group. - - EXAMPLES:: - - sage: B = BraidGroup(5) - sage: B.Delta() - s0*s1*s0*s2*s1*s0*s3*s2*s1*s0 - """ - n = self.strands() - delta = Permutation([n-i for i in range(n)]) - return self._permutation_braid(delta) + Delta = deprecated_function_alias(24664, FiniteTypeArtinGroup.delta) def _element_from_libbraiding(self, nf): """ @@ -2247,9 +2145,9 @@ def _element_from_libbraiding(self, nf): 1 """ if len(nf) == 1: - return self.Delta() ** nf[0][0] + return self.delta() ** nf[0][0] from sage.misc.misc_c import prod - return self.Delta() ** nf[0][0] * prod(self(i) for i in nf[1:]) + return self.delta() ** nf[0][0] * prod(self(i) for i in nf[1:]) def BraidGroup(n=None, names='s'): @@ -2440,3 +2338,4 @@ def _call_(self, x, b): s += [i] t = s return self.codomain()(t) + diff --git a/src/sage/groups/misc_gps/misc_groups_catalog.py b/src/sage/groups/misc_gps/misc_groups_catalog.py index 44399554a26..40d9d66d15d 100644 --- a/src/sage/groups/misc_gps/misc_groups_catalog.py +++ b/src/sage/groups/misc_gps/misc_groups_catalog.py @@ -16,8 +16,8 @@ from sage.groups.abelian_gps.abelian_group import AbelianGroup as MultiplicativeAbelian from sage.rings.finite_rings.integer_mod_ring import IntegerModRing as AdditiveCyclic from sage.groups.free_group import FreeGroup as Free +from sage.groups.artin import ArtinGroup as Artin from sage.groups.braid import BraidGroup as Braid -from sage.groups.braid import ArtinGroup as Artin from sage.groups.semimonomial_transformations.semimonomial_transformation_group import SemimonomialTransformationGroup as SemimonomialTransformation from sage.combinat.root_system.coxeter_group import CoxeterGroup from sage.combinat.root_system.weyl_group import WeylGroup From 2f93e12b88d200d7c5adbce97b95823df868231d Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 5 Feb 2018 20:51:29 -0600 Subject: [PATCH 649/740] Adding test specifically for Python3 regression testing. --- src/sage/typeset/unicode_art.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/typeset/unicode_art.py b/src/sage/typeset/unicode_art.py index 57199bfe04c..68e01c54cfe 100644 --- a/src/sage/typeset/unicode_art.py +++ b/src/sage/typeset/unicode_art.py @@ -60,8 +60,10 @@ def __unicode__(self): sage: i = var('i') sage: ua = unicode_art(sum(pi^i/factorial(i)*x^i, i, 0, oo)) - sage: unicode(ua) + sage: unicode(ua) # py2 u' \u03c0\u22c5x\n\u212f ' + sage: str(ua) # py3 + ' \u03c0\u22c5x\n\u212f ' """ return repr(self).decode("utf-8") From 250f15ab7d9f3c8759afe55f687ecc5e49d8fb32 Mon Sep 17 00:00:00 2001 From: Kevin Lui Date: Tue, 6 Feb 2018 04:59:11 +0000 Subject: [PATCH 650/740] fixed documentation, removed an import that's not longer needed, removed a list function --- src/sage/schemes/elliptic_curves/ell_curve_isogeny.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index ab3807561d3..bfb4e7db69d 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -1855,7 +1855,9 @@ def __init_from_kernel_list(self, kernel_gens): Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 4*x over Finite Field of size 7 sage: phi._EllipticCurveIsogeny__init_from_kernel_list([E(0), E((0,0))]) - The following example demonstrates the necessity of avoiding any calls to P.order(), since such calls involve factoring the group order which could take a long time. + The following example demonstrates the necessity of avoiding any calls + to P.order(), since such calls involve factoring the group order which + could take a long time. :: sage: p=12*next_prime(2**516)*next_prime(2**543)-1 sage: E=EllipticCurve([GF(p)(1),GF(p)(0)]) @@ -1876,7 +1878,7 @@ def __init_from_kernel_list(self, kernel_gens): kernel_set = Set([self.__E1(0)]) from sage.misc.all import flatten - from sage.groups.generic import multiples + def all_multiples(itr, terminal): mult_list = [terminal] R = terminal + itr @@ -1885,15 +1887,14 @@ def all_multiples(itr, terminal): R = R + itr return mult_list for P in kernel_gens: - kernel_set += Set(flatten([list(all_multiples(P,Q)) + kernel_set += Set(flatten([all_multiples(P,Q) for Q in kernel_set])) - self.__kernel_list = kernel_set.list() self.__kernel_2tor = {} self.__kernel_non2tor = {} self.__degree = Integer(len(kernel_set)) self.__sort_kernel_list() - + # # Precompute the values in Velu's Formula. # From 5ef7a25adfbe29cd7eb5c9c0924761828a36df7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Tue, 6 Feb 2018 08:58:43 +0200 Subject: [PATCH 651/740] Few StopIterations to return. --- src/sage/combinat/posets/hasse_diagram.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index fac70e990c0..1541e2b386d 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -1704,16 +1704,16 @@ def orthocomplementations_iterator(self): # Special cases first if n == 0: yield [] - raise(StopIteration) + return if n == 1: yield [0] - raise(StopIteration) + return if n % 2 == 1: - raise(StopIteration) + return dual_isomorphism = self.is_isomorphic(self.reverse(), certificate=True)[1] if dual_isomorphism is None: # i.e. if the lattice is not self-dual. - raise(StopIteration) + return # We compute possible orthocomplements, i.e. elements # with "dual position" and complement to each other. @@ -1762,7 +1762,7 @@ def recursive_fit(orthocomplements, unbinded): # A little optimization for e in range(n): if len(comps[e]) == 0: # Not any possible orthocomplement - raise(StopIteration) + return if len(comps[e]) == 1: # Do not re-fit this every time e_ = comps[e][0] # Every element might have one possible orthocomplement, @@ -1770,7 +1770,7 @@ def recursive_fit(orthocomplements, unbinded): for lc in self.lower_covers_iterator(e): if start[lc] is not None: if not self.has_edge(e_, start[lc]): - raise(StopIteration) + return if start[e_] is None: start[e] = e_ start[e_] = e From 59b11eda5656ffb0fb61c99f493718521a6fcea6 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Tue, 6 Feb 2018 09:20:42 +0100 Subject: [PATCH 652/740] added # optional gap_packages to fix doctest --- src/sage/groups/abelian_gps/abelian_group_gap.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/groups/abelian_gps/abelian_group_gap.py b/src/sage/groups/abelian_gps/abelian_group_gap.py index 90177292e57..9563b472346 100644 --- a/src/sage/groups/abelian_gps/abelian_group_gap.py +++ b/src/sage/groups/abelian_gps/abelian_group_gap.py @@ -698,13 +698,13 @@ def __init__(self, ambient, gens): Check that we are in the correct category:: - sage: G = AbelianGroupGap([2,3,0]) - sage: g = G.gens() - sage: H1 = G.subgroup([g[0],g[1]]) - sage: H1 in Groups().Finite() + sage: G = AbelianGroupGap([2,3,0]) # optional - gap_packages + sage: g = G.gens() # optional - gap_packages + sage: H1 = G.subgroup([g[0],g[1]]) # optional - gap_packages + sage: H1 in Groups().Finite() # optional - gap_packages True - sage: H2 = G.subgroup([g[0],g[2]]) - sage: H2 in Groups().Infinite() + sage: H2 = G.subgroup([g[0],g[2]]) # optional - gap_packages + sage: H2 in Groups().Infinite() # optional - gap_packages True """ gens_gap = tuple([g.gap() for g in gens]) From f5b3de4f629e0c1c66489c63d2fba4b5cb30f94a Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Tue, 6 Feb 2018 10:16:10 +0100 Subject: [PATCH 653/740] 24668: version/chzksum --- build/pkgs/pynac/checksums.ini | 6 +++--- build/pkgs/pynac/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pynac/checksums.ini b/build/pkgs/pynac/checksums.ini index 0b2fe41225c..0fd2a656ad1 100644 --- a/build/pkgs/pynac/checksums.ini +++ b/build/pkgs/pynac/checksums.ini @@ -1,4 +1,4 @@ tarball=pynac-VERSION.tar.bz2 -sha1=ec322d219a9c02b95a29789ca221b28c6cca7a14 -md5=10f21726733209ae6f641b5736c4c9fb -cksum=2341458065 +sha1=6884e38508746afc3bb35ed544a2b6605b7d1c13 +md5=ce413448e8adbda7239a59f326128341 +cksum=1898847628 diff --git a/build/pkgs/pynac/package-version.txt b/build/pkgs/pynac/package-version.txt index def4250351c..bf7b715d275 100644 --- a/build/pkgs/pynac/package-version.txt +++ b/build/pkgs/pynac/package-version.txt @@ -1 +1 @@ -0.7.15 +0.7.16 From 67f9ddc48ffe5258b610e1af60ef0ca9fcb7c3db Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Tue, 6 Feb 2018 10:17:19 +0100 Subject: [PATCH 654/740] 24668: doctest fixes --- src/sage/functions/hyperbolic.py | 8 ++++---- src/sage/functions/other.py | 2 +- src/sage/symbolic/expression.pyx | 18 +++++++++--------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sage/functions/hyperbolic.py b/src/sage/functions/hyperbolic.py index a79940a342b..f46bdca6bb5 100644 --- a/src/sage/functions/hyperbolic.py +++ b/src/sage/functions/hyperbolic.py @@ -536,7 +536,7 @@ def __init__(self): sage: arctanh(0.5) 0.549306144334055 sage: arctanh(1/2) - arctanh(1/2) + 1/2*log(3) sage: arctanh(1 + I*1.0) 0.402359478108525 + 1.01722196789785*I @@ -549,7 +549,7 @@ def __init__(self): :meth:`sage.symbolic.expression.Expression.simplify`:: sage: arctanh(-1/2,hold=True).simplify() - -arctanh(1/2) + -1/2*log(3) ``conjugate(arctanh(x))==arctanh(conjugate(x))`` unless on the branch cuts which run along the real axis outside the interval [-1, +1].:: @@ -563,7 +563,7 @@ def __init__(self): sage: conjugate(arctanh(y+I)) conjugate(arctanh(y + I)) sage: conjugate(arctanh(1/16)) - arctanh(1/16) + 1/2*log(17/15) sage: conjugate(arctanh(I/2)) arctanh(-1/2*I) sage: conjugate(arctanh(-2*I)) @@ -595,7 +595,7 @@ def __init__(self): sage: arccoth(2.0) 0.549306144334055 sage: arccoth(2) - arccoth(2) + 1/2*log(3) sage: arccoth(1 + I*1.0) 0.402359478108525 - 0.553574358897045*I sage: arccoth(2).n(200) diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 0ee11427e2a..eb7d3b605d1 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -1679,7 +1679,7 @@ def _eval_(self, x): sage: SR(5).factorial() 120 sage: SR(3245908723049857203948572398475r).factorial() - factorial(3245908723049857203948572398475L) + factorial(3245908723049857203948572398475) sage: SR(3245908723049857203948572398475).factorial() factorial(3245908723049857203948572398475) """ diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 7130898086c..068af9df15f 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -3295,7 +3295,7 @@ cdef class Expression(CommutativeRingElement): Check if Pynac can compute inverses of Python longs (:trac:`13107`):: sage: SR(4L)*SR(2L)^(-1) - 2.0 + 2 Check for simplifications when multiplying instances of exp:: @@ -3496,7 +3496,7 @@ cdef class Expression(CommutativeRingElement): Check if Pynac can compute divisions of Python longs (:trac:`13107`):: sage: SR(1L)/SR(2L) - 0.5 + 1/2 """ cdef GEx x cdef Expression _right = right @@ -3915,7 +3915,7 @@ cdef class Expression(CommutativeRingElement): Test if we can compute inverses of Python longs (:trac:`13107`):: sage: SR(2L)^(-1) - 0.5 + 1/2 Symbolic powers with ``None`` shouldn't crash (:trac:`17523`):: @@ -8322,9 +8322,9 @@ cdef class Expression(CommutativeRingElement): sage: SR(I).arctan2(1) arctan2(I, 1) sage: SR(CDF(0,1)).arctan2(1) - arctan2(1.0*I, 1) + NaN + +infinity*I sage: SR(1).arctan2(CDF(0,1)) - arctan2(1, 1.0*I) + 0.7853981633974484 - 19.012501686914433*I sage: arctan2(0,oo) 0 @@ -8633,7 +8633,7 @@ cdef class Expression(CommutativeRingElement): sage: SR(0).arctanh() 0 sage: SR(1/2).arctanh() - arctanh(1/2) + 1/2*log(3) sage: SR(0.5).arctanh() 0.549306144334055 sage: SR(0.5).arctanh().tanh() @@ -8644,7 +8644,7 @@ cdef class Expression(CommutativeRingElement): To prevent automatic evaluation use the ``hold`` argument:: sage: SR(-1/2).arctanh() - -arctanh(1/2) + -1/2*log(3) sage: SR(-1/2).arctanh(hold=True) arctanh(-1/2) @@ -8653,12 +8653,12 @@ cdef class Expression(CommutativeRingElement): sage: arctanh(-1/2,hold=True) arctanh(-1/2) sage: arctanh(-1/2) - -arctanh(1/2) + -1/2*log(3) To then evaluate again, we use :meth:`unhold`:: sage: a = SR(-1/2).arctanh(hold=True); a.unhold() - -arctanh(1/2) + -1/2*log(3) TESTS:: From 93de16e381c300c875604494092027d086a83f50 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 6 Feb 2018 10:01:41 +0100 Subject: [PATCH 655/740] Clean up partitions_c.cc --- src/sage/combinat/partitions.pyx | 16 +- src/sage/combinat/partitions_c.cc | 991 +++++++++++------------------- src/sage/combinat/partitions_c.h | 10 - 3 files changed, 377 insertions(+), 640 deletions(-) delete mode 100644 src/sage/combinat/partitions_c.h diff --git a/src/sage/combinat/partitions.pyx b/src/sage/combinat/partitions.pyx index 90fadba5564..cfe9f421d01 100644 --- a/src/sage/combinat/partitions.pyx +++ b/src/sage/combinat/partitions.pyx @@ -1,7 +1,6 @@ # The actual algorithm is implemented in the C++ file partitions_c.cc # which requires the gmp and mpfr libraries. # -# distutils: sources = sage/combinat/partitions_c.cc # distutils: libraries = gmp mpfr # distutils: language = c++ """ @@ -32,8 +31,8 @@ from cysignals.signals cimport sig_on, sig_off from sage.libs.gmp.types cimport mpz_t -cdef extern from "partitions_c.h": - int part(mpz_t answer, unsigned int n) +cdef extern from "partitions_c.cc": + void part(mpz_t answer, unsigned int n) int test(bint longtest, bint forever) from sage.rings.integer cimport Integer @@ -45,6 +44,12 @@ def number_of_partitions(n): EXAMPLES:: sage: from sage.combinat.partitions import number_of_partitions + sage: number_of_partitions(0) + 1 + sage: number_of_partitions(1) + 1 + sage: number_of_partitions(2) + 2 sage: number_of_partitions(3) 3 sage: number_of_partitions(10) @@ -90,14 +95,11 @@ def number_of_partitions(n): sage: len([n for n in [1..500] if number_of_partitions(n) != Partitions(n).cardinality(algorithm='pari')]) 0 - """ n = Integer(n) if n < 0: raise ValueError("n (=%s) must be a nonnegative integer"%n) - elif n <= 1: - return Integer(1) # part hangs on n=1 as input. - if n >= Integer('4294967296'): + if n >= Integer(4294967296): raise ValueError("input must be a nonnegative integer less than 4294967296.") cdef unsigned int nn = n diff --git a/src/sage/combinat/partitions_c.cc b/src/sage/combinat/partitions_c.cc index 8bf8ba13d77..44d2c51b9c5 100644 --- a/src/sage/combinat/partitions_c.cc +++ b/src/sage/combinat/partitions_c.cc @@ -51,9 +51,9 @@ * * http://www.ark.in-berlin.de/part.c * - * while writing this code, but didn't really make use of it, except for the - * function cospi(), currently included in the source (slightly modified), but not - * used. + * while writing this code, but didn't really make use of it. + * Maybe we could use the function cospi() instead of mpfr_cos() + * but there seems to be no gain in doing that. * * More useful were notes currently available at * @@ -69,6 +69,9 @@ * Also, Bill Hart made some comments about ways to speed up this computation on the SAGE * mailing list. * + * A big clean up of this file was done by Jeroen Demeyer in + * https://trac.sagemath.org/ticket/24667 + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -83,16 +86,6 @@ * along with this program. If not, see . */ - -#if defined(__sun) || defined(__CYGWIN__) -extern "C" long double fabsl (long double); -extern "C" long double sinl (long double); -extern "C" long double cosl (long double); -extern "C" long double sqrtl (long double); -extern "C" long double coshl (long double); -extern "C" long double sinhl (long double); -#endif - #include #include #include @@ -110,8 +103,6 @@ extern "C" long double sinhl (long double); using namespace std; -using std::cout; -using std::endl; /***************************************************************************** * @@ -127,24 +118,22 @@ const bool debug = false; // If true, outp const bool debug_precision = false; // If true, output information that might be useful for // debugging the precision setting code. -const bool debugs = false; // If true, output information that might be useful for - // debugging the various s() functions. const bool debugf = false; // Same for the f() functions. const bool debuga = false; // Same for the a() functions. const bool debugt = false; // Same for the t() functions. #if FLT_RADIX != 2 -#error I don't know what to do when the float radix is not 2. +#error "I don't know what to do when the float radix is not 2" #endif // Note - it might be unreasonable to not support a float radix other than -// 2, but apparently gcc doesn't either. See http://gcc.gnu.org/ml/fortran/2006-12/msg00032.html. +// 2, but apparently gcc doesn't either. See http://gcc.gnu.org/ml/fortran/2006-12/msg00032.html const unsigned int min_precision = DBL_MANT_DIG; // The minimum precision that we will ever use. const unsigned int double_precision = DBL_MANT_DIG; // The assumed precision of a double. -#if defined(__sparc) || defined(__CYGWIN__) || defined(__FreeBSD__) +#if defined(__sparc) || defined(__CYGWIN__) || defined(__FreeBSD__) // On sparc solaris long double is bad/broken/different, etc. E.g., // LDBL_MANT_DIG is 113 rather than 106, which causes all kinds of trouble. // So we only use double_precision. @@ -155,9 +144,6 @@ const unsigned int long_double_precision = (LDBL_MANT_DIG == 106) ? double_preci // The assumed precision of a long double. // Note: On many systems double_precision = long_double_precision. This is OK, as // the long double stage of the computation will just be skipped. - // - // NOTE: If long_double_precision > dd_precision, then, again, long doubles - // will not ever be used. It would be nice if this were fixed. // Second, some constants that control the precision at which we compute. @@ -167,17 +153,9 @@ const unsigned int long_double_precision = (LDBL_MANT_DIG == 106) ? double_preci // we don't need as much precision. These constants control the various // precision levels at which we switch to different special // purpose functions. - -// const unsigned int level_one_precision = infinity // We don't actually use this, but if we did it would be infinite. const unsigned int level_two_precision = long_double_precision; -const unsigned int level_three_precision = long_double_precision; -const unsigned int level_four_precision = long_double_precision; const unsigned int level_five_precision = double_precision; - -const long double ld_pi = 3.141592653589793238462643L; -const double d_pi = ld_pi; - // Third, the rounding mode for mpfr. const mpfr_rnd_t round_mode = MPFR_RNDN; @@ -186,18 +164,18 @@ const mpfr_rnd_t round_mode = MPFR_RNDN; * We are finished declaring constants, and next declare semi-constant * variables. These are all set just once per call to part(n). * - * All of these are set in initialize_constants(), and the mpfr_t variables - * are cleared in clear_constants(). + * All of these are set in initialize_globals(), and + * cleared in clear_globals(). * ****************************************************************************/ -mpfr_t mp_one_over_12, mp_one_over_24, mp_sqrt2, mp_sqrt3, mp_pi, half, fourth; // These need to be set at run time - // because we don't know how much precision we will need - // until we know for what n we are computing p(n). +mpfr_t mp_sqrt2, mp_sqrt3, mp_pi; // These need to be set at run time + // because we don't know how much precision we will need + // until we know for what n we are computing p(n). -mpfr_t mp_A, mp_B, mp_C, mp_D; // These "constants" all depend on n, and -double d_A, d_B, d_C, d_D; // -long double ld_A, ld_B, ld_C, ld_D; // +mpfr_t mp_A, mp_B, mp_C, mp_D; // These "constants" all depend on n +double d_pi, d_A, d_B, d_C, d_D; +long double ld_pi, ld_A, ld_B, ld_C, ld_D; /***************************************************************************** @@ -209,26 +187,10 @@ long double ld_A, ld_B, ld_C, ld_D; * ****************************************************************************/ -mpz_t ztemp1; // These are initialized in the function initialize_mpz_and_mpq_variables(), and -mpq_t qtemps, qtempa, qtempa2; // cleared in the function clear_mpz_and_mpq_variables(). - // qtemps is used by q_s() and qtempa and qtempa2() are used by mp_a(). - // - // ztemp1 is only used in one function, and the code where is it used is actually unlikely - // to be used for any input, so it should be erased. - -mpfr_t tempa1, tempa2, tempf1, tempf2, temps1, temps2; // These are all initialized by initialize_mpfr_variables(), and cleared by - // clear_mpfr_variables(). - // NAMING CONVENTIONS: - // - // -tempa1 and tempa2 are two variables available for use - // in the function mp_a() - // - // -tempf1 and tempf2 are two variables available for use - // in the function mp_f() - // - // -etc... - -mpfr_t tempc1, tempc2; // temp variables used by cospi() +// These are initialized in the function initialize_globals() +// and cleared in clear_globals(). +mpq_t qtemps, qtempa, qtempa2; +mpfr_t mptemp; /***************************************************************************** @@ -237,18 +199,18 @@ mpfr_t tempc1, tempc2; * ****************************************************************************/ -// + // A listing of the main functions, in "conceptual" order. -// -int part(mpz_t answer, unsigned int n); + +void part(mpz_t answer, unsigned int n); static void mp_t(mpfr_t result, unsigned int n); // Compute t(n,N) for an N large enough, and with a high enough precision, so that // |p(n) - t(n,N)| < .5 static unsigned int compute_initial_precision(unsigned int n); // computes the precision required to accurately compute p(n) -static void initialize_constants(unsigned int prec, unsigned int n); // Once we know the precision that we will need, we precompute +static void initialize_globals(mp_prec_t prec, unsigned int n); // Once we know the precision that we will need, we precompute // some commonly used constants. static unsigned int compute_current_precision(unsigned int n, unsigned int N, unsigned int extra); // Computed the precision required to @@ -256,71 +218,53 @@ static unsigned int compute_current_precision(unsigned int n, unsigned int N, un // assuming that N terms have already been computed. // This is called after computing each summand, unless // we have already reached the minimum precision. - // + // Reducing the precision as fast as possible is key to // fast performance. static double compute_remainder(unsigned int n, unsigned int N); // Gives an upper bound on the error that occurs // when only N terms of the Rademacher series have been // computed. - // + // This should only be called when we know that compute_current_precision // will return min_precision. Otherwise the error will be too large for // it to compute. - -// -// The following functions are all called in exactly one place, so -// we could ask the compiler to inline everything. Actually making this -// work properly requires changing some compiler options, and doesn't -// provide much improvement, however. -// - static void mp_f(mpfr_t result, unsigned int k); // See introduction for an explanation of these functions. -static void q_s(mpq_t result, unsigned int h, unsigned int k); // -static void mp_a(mpfr_t result, unsigned int n, unsigned int k); // +static void q_s(mpq_t result, unsigned int h, unsigned int k); +static void mp_a(mpfr_t result, unsigned int n, unsigned int k); template static inline T a(unsigned int n, unsigned int k); // Template versions of the above functions for computing with -template static inline T f(unsigned int k); // low precision. Currently used for computing with qd_real, dd_real, +template static inline T f(unsigned int k); // low precision. Currently used for computing with template static inline T s(unsigned int h, unsigned int k); // long double, and double. -// // The following are a bunch of "fancy macros" designed so that in the templated code // for a, for example, when we need to use pi, we just call pi() to get pi to // the proper precision. The compiler should inline these, so using one of these // functions is just as good as using a constant, and since these functions are static // they shouldn't even appear in the object code generated. +template static inline T sqrt2() {return sqrt(T(2));} +template static inline T sqrt3() {return sqrt(T(3));} -template static inline T pi(){ return ld_pi; } +template static inline T pi() {return T(d_pi);} +template <> inline long double pi() {return ld_pi;} -template static inline T sqrt2() {return sqrt(2.0L);} - -template static inline T sqrt3() {return sqrt(3.0L);} - -template static inline T A() {return 1;} -template <> inline double A() {return d_A;} +template static inline T A() {return T(d_A);} template <> inline long double A() {return ld_A;} -template static inline T B() {return 1;} -template <> inline double B() {return d_B;} +template static inline T B() {return T(d_B);} template <> inline long double B() {return ld_B;} -template static inline T C() {return 1;} -template <> inline double C() {return d_C;} +template static inline T C() {return T(d_C);} template <> inline long double C() {return ld_C;} -template static inline T D() {return 1;} -template <> inline double D() {return d_D;} +template static inline T D() {return T(d_D);} template <> inline long double D() {return ld_D;} -template static inline T pi_sqrt2() {return 1;} -template <> inline double pi_sqrt2() {return d_pi * sqrt(2.0);} -template <> inline long double pi_sqrt2() {return ld_pi * sqrt(2.0L);} +template static inline T pi_sqrt2() {return pi() * sqrt(T(2));} -template static inline T one_over_12() {return 1;} -template <> inline double one_over_12() {return 1.0/12.0;} -template <> inline long double one_over_12() {return 1.0L/12.0L;} +template static inline T one_over_12() {return T(1)/T(12);} // A few utility functions... @@ -331,24 +275,10 @@ int test(bool longtest = false, bool forever = false); // have been verified by other programs, and // also on known congruences for p(n) -static void cospi (mpfr_t res, mpfr_t x); // Puts cos(pi x) in result. This is not currently - // used, but should be faster than using mpfr_cos, - // according to comments in the file part.c - // mentioned in the introduction. - // test - static int grab_last_digits(char * output, int n, mpfr_t x); // Might be useful for debugging, but // don't use it for anything else. // (See function definition for more information.) - -int main(int argc, char *argv[]); // This program is mainly meant for inclusion - // in SAGE (or any other program, if anyone - // feels like it). We include a main() function - // anyway, because it is useful to compile - // this as a standalone program - // for testing purposes. - /*********************************************************************** * * That should be the end of both function and variable definitions. @@ -357,12 +287,12 @@ int main(int argc, char *argv[]); // The following function can be useful for debugging in come circumstances, but should not be used for anything else // unless it is rewritten. -int grab_last_digits(char * output, int n, mpfr_t x) { +static int grab_last_digits(char * output, int n, mpfr_t x) { // fill output with the n digits of x that occur // just before the decimal point // Note: this assumes that x has enough digits and enough // precision -- otherwise bad things can happen - // + // returns: the number of digits to the right of the decimal point char * temp; @@ -372,12 +302,12 @@ int grab_last_digits(char * output, int n, mpfr_t x) { int retval; - if(e > 0) { + if (e > 0) { strncpy(output, temp + e - n, n); retval = strlen(temp + e); } else { - for(int i = 0; i < n; i++) + for (int i = 0; i < n; i++) output[i] = '0'; retval = strlen(temp); } @@ -389,50 +319,48 @@ int grab_last_digits(char * output, int n, mpfr_t x) { } - -unsigned int compute_initial_precision(unsigned int n) { +static unsigned int compute_initial_precision(unsigned int n) { // We just want to know how many bits we will need to // compute to get an accurate answer. // We know that - // + // p(n) ~ exp(pi * sqrt(2n/3))/(4n sqrt(3)), - // + // so for now we are assuming that p(n) < exp(pi * sqrt(2n/3))/n, // so we need pi*sqrt(2n/3)/log(2) - log(n)/log(2) + EXTRA bits. - // + // EXTRA should depend on n, and should be something that ensures // that the TOTAL ERROR in all computations is < (something small). // This needs to be worked out carefully. EXTRA = log(n)/log(2) + 3 // is probably good enough, and is convenient... - // + // but we really need: - // + // p(n) < something - // + // to be sure that we compute the correct answer unsigned int result = (unsigned int)(ceil(3.1415926535897931 * sqrt(2.0 * double(n)/ 3.0) / log(2))) + 3; - if(debug) cout << "Using initial precision of " << result << " bits." << endl; - - if(result > min_precision) { - return result; - } + if (debug) cout << "Using initial precision of " << result << " bits." << endl; - else return min_precision; + if (result <= min_precision) + result = min_precision; + return result; } -unsigned int compute_current_precision(unsigned int n, unsigned int N, unsigned int extra = 0) { + +static unsigned int compute_current_precision(unsigned int n, unsigned int N, unsigned int extra = 0) { // Roughly, we compute - // + // log(A/sqrt(N) + B*sqrt(N/(n-1))*sinh(C * sqrt(n) / N) / log(2) - // + // where A, B, and C are the constants listed below. These error bounds // are given in the paper by Rademacher listed at the top of this file. // We then return this + extra, if extra != 0. If extra == 0, return with // what is probably way more extra precision than is needed. - // + // extra should probably have been set by a call to compute_extra_precision() // before this function was called. @@ -442,59 +370,53 @@ unsigned int compute_current_precision(unsigned int n, unsigned int N, unsigned // if N is 0, then we can't use the above formula (because we would be // dividing by 0). - if(N == 0) return compute_initial_precision(n) + extra; + if (N == 0) return compute_initial_precision(n) + extra; mpfr_t A, B, C; mpfr_init2(A, 32); mpfr_init2(B, 32); mpfr_init2(C, 32); - mpfr_set_d(A,1.11431833485164,round_mode); - mpfr_set_d(B,0.059238439175445,round_mode); - mpfr_set_d(C,2.5650996603238,round_mode); + mpfr_set_d(A, 1.11431833485164, MPFR_RNDN); + mpfr_set_d(B, 0.059238439175445, MPFR_RNDN); + mpfr_set_d(C, 2.5650996603238, MPFR_RNDN); mpfr_t error, t1, t2; mpfr_init2(error, 32); // we shouldn't need much precision here since we just need the most significant bit mpfr_init2(t1, 32); mpfr_init2(t2, 32); - mpfr_set(error, A, round_mode); // error = A - mpfr_sqrt_ui(t1, N, round_mode); // t1 = sqrt(N) - mpfr_div(error, error, t1, round_mode); // error = A/sqrt(N) - + mpfr_set(error, A, MPFR_RNDF); // error = A + mpfr_sqrt_ui(t1, N, MPFR_RNDF); // t1 = sqrt(N) + mpfr_div(error, error, t1, MPFR_RNDF); // error = A/sqrt(N) - mpfr_sqrt_ui(t1, n, round_mode); // t1 = sqrt(n) - mpfr_mul(t1, t1, C, round_mode); // t1 = C * sqrt(n) - mpfr_div_ui(t1, t1, N, round_mode); // t1 = C * sqrt(n) / N - mpfr_sinh(t1, t1, round_mode); // t1 = sinh( ditto ) - mpfr_mul(t1, t1, B, round_mode); // t1 = B * sinh( ditto ) - mpfr_set_ui(t2, N, round_mode); // t2 = N - mpfr_div_ui(t2, t2, n-1, round_mode); // t2 = N/(n-1) - mpfr_sqrt(t2, t2, round_mode); // t2 = sqrt( ditto ) + mpfr_sqrt_ui(t1, n, MPFR_RNDF); // t1 = sqrt(n) + mpfr_mul(t1, t1, C, MPFR_RNDF); // t1 = C * sqrt(n) + mpfr_div_ui(t1, t1, N, MPFR_RNDF); // t1 = C * sqrt(n) / N + mpfr_sinh(t1, t1, MPFR_RNDF); // t1 = sinh( ditto ) + mpfr_mul(t1, t1, B, MPFR_RNDF); // t1 = B * sinh( ditto ) - mpfr_mul(t1, t1, t2, round_mode); // t1 = B * sqrt(N/(n-1)) * sinh(C * sqrt(n)/N) + mpfr_set_ui(t2, N, MPFR_RNDF); // t2 = N + mpfr_div_ui(t2, t2, n-1, MPFR_RNDF); // t2 = N/(n-1) + mpfr_sqrt(t2, t2, MPFR_RNDF); // t2 = sqrt( ditto ) - mpfr_add(error, error, t1, round_mode); // error = (ERROR ESTIMATE) + mpfr_fma(error, t1, t2, error, MPFR_RNDF); // error += t1 * t2 (= ERROR ESTIMATE) - unsigned int p = mpfr_get_exp(error); // I am not 100% certain that this always does the right thing. - // (It should be the case that p is now the number of bits - // required to hold the integer part of the error.) + // the number of bits required to hold the integer part of the error + unsigned int p = mpfr_get_exp(error); - - if(extra == 0) { - p = p + (unsigned int)ceil(log(n)/log(2)); // This is a stupid case to fall back on, - // left over from earlier versions of this code. - // It really should never be used. + if (extra == 0) { + // Stupid fall back case + extra = ceil(log(n)/log(2)); } - else { - p = p + extra; // Recall that the extra precision should be + + p += extra; // Recall that the extra precision should be // large enough so that the accumulated errors // in all of the computations that we make // are not big enough to matter. - } - if(debug) { + if (debug) { cout << "Error seems to be: "; mpfr_out_str(stdout, 10, 0, error, round_mode); cout << endl; @@ -509,27 +431,25 @@ unsigned int compute_current_precision(unsigned int n, unsigned int N, unsigned mpfr_clear(B); mpfr_clear(C); - - if(p > min_precision) { - return p; // We don't want to return < min_precision. - // Note that when the code that calls this + if (p <= min_precision) // We don't want to return < min_precision. + p = min_precision; // Note that when the code that calls this // function finds that the returned result // is min_precision, it should stop calling // this function, since the result won't change // after that. Also, it should probably switch // to computations with doubles, and should // start calling compute_remainder(). - } - return min_precision; + return p; } -int compute_extra_precision(unsigned int n, double error = .25) { + +static int compute_extra_precision(unsigned int n, double error = .25) { // Return the number of terms of the Rachemacher series that // we will need to compute to get a remainder of less than error // in absolute value, and then return the extra precision // that will guarantee that the accumulated error after computing // that number of steps will be less than .5 - error. - // + // How this works: // We first need to figure out how many terms of the series we are going to // need to compute. That is, we need to know how large k needs to be @@ -538,7 +458,7 @@ int compute_extra_precision(unsigned int n, double error = .25) { // compute_current_precision() until we know that the error will be // small enough to call compute_remainder(). Then we just call compute_remainder() // until the error is small enough. - // + // Now that we know how many terms we will need to compute, k, we compute // the number of bits required to accurately store (.5 - error)/k. This ensures // that the total error introduced when we add up all k terms of the sum @@ -546,12 +466,12 @@ int compute_extra_precision(unsigned int n, double error = .25) { // then the sum will be within .5 of the correct (integer) answer, and we // can correctly find it by rounding. unsigned int k = 1; - for( ; compute_current_precision(n,k,0) > double_precision; k += 100) { + for ( ; compute_current_precision(n,k,0) > double_precision; k += 100) { } - for( ; compute_remainder(n, k) > error ; k += 100) { + for ( ; compute_remainder(n, k) > error ; k += 100) { } - if(debug_precision) { + if (debug_precision) { cout << "To compute p(" << n << ") we will add up approximately " << k << " terms from the Rachemacher series." << endl; } int bits = (int)((log(k/(.5 - error)))/log(2)) + 5; // NOTE: reducing the number of bits by 3 here is known to cause errors @@ -574,12 +494,13 @@ int compute_extra_precision(unsigned int n, double error = .25) { return bits; } -double compute_remainder(unsigned int n, unsigned int N) { + +static double compute_remainder(unsigned int n, unsigned int N) { // This computes the remainer left after N terms have been computed. // The formula is exactly the same as the one used to compute the required // precision, but once we know the necessary precision is small, we can // call this function to determine the actual error (rather than the precision). - // + // Generally, this is only called once we know that the necessary // precision is <= min_precision, because then the error is small // enough to fit into a double, and also, we know that we are @@ -594,214 +515,176 @@ double compute_remainder(unsigned int n, unsigned int N) { } -void initialize_mpfr_variables(unsigned int prec) { - // - // Clear and initialize some "temp" variables that are used in the computation of various functions. - // - // We do this in this auxilliary function (and endure the pain of - // the extra global variables) so that we only have to initialize/clear - // these variables once every time the precision changes. - // - // NAMING CONVENTIONS: - // - // -tempa1 and tempa2 are two variables available for use - // in the function mp_a() - // - // -tempf1 and tempf2 are two variables available for use - // in the function mp_f() - // - // -etc... - // - // NOTE: Calls to this function must be paired with calls to clear_mpfr_variables() - - mpfr_init2(tempa1, prec); - mpfr_init2(tempa2, prec); - mpfr_init2(tempf1, prec); - mpfr_init2(tempf2, prec); - mpfr_init2(temps1, prec); - mpfr_init2(temps2, prec); - mpfr_init2(tempc1, prec); - mpfr_init2(tempc2, prec); -} - -void clear_mpfr_variables() { - mpfr_clear(tempa1); - mpfr_clear(tempa2); - mpfr_clear(tempf1); - mpfr_clear(tempf2); - mpfr_clear(temps1); - mpfr_clear(temps2); - - mpfr_clear(tempc1); - mpfr_clear(tempc2); -} - - -void initialize_constants(unsigned int prec, unsigned int n) { +static void initialize_globals(mp_prec_t prec, unsigned int n) { // The variables mp_A, mp_B, mp_C, and mp_D are used for // A_n, B_n, C_n, and D_n listed at the top of this file. - // + // They depend only on n, so we compute them just once in this function, // and then use them many times elsewhere. - // - // Also, we precompute some extra constants that we use a lot, such as - // sqrt2, sqrt3, pi, 1/24, 1/12, etc. - // - // NOTE: Calls to this function must be paired with calls to clear_constants() - static bool init = false; - mpfr_prec_t p = prec; - - mpfr_init2(mp_one_over_12,p); mpfr_init2(mp_one_over_24,p); mpfr_init2(mp_sqrt2,p); mpfr_init2(mp_sqrt3,p); mpfr_init2(mp_pi,p); - mpfr_init2(mp_A,p); mpfr_init2(mp_B,p); mpfr_init2(mp_C,p); mpfr_init2(mp_D,p); mpfr_init2(fourth, p); mpfr_init2(half, p); - - init = true; - - mpfr_set_ui(mp_one_over_12, 1, round_mode); // mp_one_over_12 = 1/12 - mpfr_div_ui(mp_one_over_12, mp_one_over_12, 12, round_mode); // - - mpfr_set_ui(mp_one_over_24, 1, round_mode); // mp_one_over_24 = 1/24 - mpfr_div_ui(mp_one_over_24, mp_one_over_24, 24, round_mode); // - - mpfr_set_ui(half, 1, round_mode); // - mpfr_div_ui(half, half, 2, round_mode); // half = 1/2 - mpfr_div_ui(fourth, half, 2, round_mode); // fourth = 1/4 - - mpfr_t n_minus; // - mpfr_init2(n_minus, p); // - mpfr_set_ui(n_minus, n, round_mode); // n_minus = n - mpfr_sub(n_minus, n_minus, mp_one_over_24,round_mode); // n_minus = n - 1/24 - - mpfr_t sqrt_n_minus; // - mpfr_init2(sqrt_n_minus, p); // - mpfr_sqrt(sqrt_n_minus, n_minus, round_mode); // n_minus = sqrt(n-1/24) - - - mpfr_sqrt_ui(mp_sqrt2, 2, round_mode); // mp_sqrt2 = sqrt(2) - mpfr_sqrt_ui(mp_sqrt3, 3, round_mode); // mp_sqrt3 = sqrt(3) - mpfr_const_pi(mp_pi, round_mode); // mp_pi = pi - - //mp_A = sqrt(2) * 3.1415926535897931 * sqrt(n - 1.0/24.0);--------------- - mpfr_set(mp_A, mp_sqrt2, round_mode); // mp_A = sqrt(2) - mpfr_mul(mp_A, mp_A, mp_pi, round_mode); // mp_A = sqrt(2) * pi - mpfr_mul(mp_A, mp_A, sqrt_n_minus, round_mode); // mp_A = sqrt(2) * pi * sqrt(n - 1/24) - //------------------------------------------------------------------------ - - - //mp_B = 2.0 * sqrt(3) * (n - 1.0/24.0);---------------------------------- - mpfr_set_ui(mp_B, 2, round_mode); // mp_A = 2 - mpfr_mul(mp_B, mp_B, mp_sqrt3, round_mode); // mp_A = 2*sqrt(3) - mpfr_mul(mp_B, mp_B, n_minus, round_mode); // mp_A = 2*sqrt(3)*(n-1/24) - //------------------------------------------------------------------------ - - - //mp_C = sqrt(2) * pi * sqrt(n - 1.0/24.0) / sqrt(3);--------------------- - mpfr_set(mp_C, mp_sqrt2, round_mode); // mp_C = sqrt(2) - mpfr_mul(mp_C, mp_C, mp_pi, round_mode); // mp_C = sqrt(2) * pi - mpfr_mul(mp_C, mp_C, sqrt_n_minus, round_mode); // mp_C = sqrt(2) * pi * sqrt(n - 1/24) - mpfr_div(mp_C, mp_C, mp_sqrt3, round_mode); // mp_C = sqrt(2) * pi * sqrt(n - 1/24) / sqrt3 - //------------------------------------------------------------------------ + // Also, we precompute some extra constants that we use a lot, such as + // sqrt2, sqrt3, pi - //mp_D = 2.0 * (n - 1.0/24.0) * sqrt(n - 1.0/24.0);----------------------- - mpfr_set_ui(mp_D, 2, round_mode); // mp_D = 2 - mpfr_mul(mp_D, mp_D, n_minus, round_mode); // mp_D = 2 * (n - 1/24) - mpfr_mul(mp_D, mp_D, sqrt_n_minus, round_mode); // mp_D = 2 * (n - 1/24) * sqrt(n - 1/24) - //------------------------------------------------------------------------ - - mpfr_clear(n_minus); - mpfr_clear(sqrt_n_minus); - - - // some double precision versions of the above values - - d_A = sqrt(2) * d_pi * sqrt(n - 1.0/24.0); - d_B = 2.0 * sqrt(3) * (n - 1.0/24.0); - d_C = sqrt(2) * d_pi * sqrt(n - 1.0/24.0) / sqrt(3); - d_D = 2.0 * (n - 1.0/24.0) * sqrt(n - 1.0/24.0); - - ld_A = sqrtl(2) * ld_pi * sqrtl(n - 1.0L/24.0L); - ld_B = 2.0L * sqrtl(3) * (n - 1.0/24.0); - ld_C = sqrtl(2) * ld_pi * sqrtl(n - 1.0L/24.0L) / sqrtl(3); - ld_D = 2.0L * (n - 1.0L/24.0L) * sqrtl(n - 1.0L/24.0L); - -} - -void clear_constants() { - mpfr_clear(mp_one_over_12); mpfr_clear(mp_one_over_24); mpfr_clear(mp_sqrt2); mpfr_clear(mp_sqrt3); mpfr_clear(mp_pi); - mpfr_clear(mp_A); mpfr_clear(mp_B); mpfr_clear(mp_C); mpfr_clear(mp_D); mpfr_clear(half); mpfr_clear(fourth); -} - -void initialize_mpz_and_mpq_variables() { - /* - * We use a few mpz_t and mpq_t variables which need to be initialized - * before they can be used. Initialization and clearing take some - * time, so we initialize just once in this function, and clear in another. - */ - mpz_init(ztemp1); + // NOTE: Calls to this function must be paired with calls to clear_globals() + // To ensure that we compute a good approximation of these + // constants, we use a larger precision than needed and then round + // to a lower precision after we are done. Working at a larger + // precision also means that we can use MPFR_RNDF safely (this + // corresponds roughly to losing 1 bit of precision). + mpfr_prec_t p = prec; + if (p < long_double_precision) p = long_double_precision; + p += 20; + + // Global constants + mpfr_init2(mp_sqrt2, p); + mpfr_init2(mp_sqrt3, p); + mpfr_init2(mp_pi, p); + mpfr_init2(mp_A, p); + mpfr_init2(mp_B, p); + mpfr_init2(mp_C, p); + mpfr_init2(mp_D, p); + + // Global temporary variables mpq_init(qtemps); mpq_init(qtempa); mpq_init(qtempa2); + mpfr_init2(mptemp, prec); + + // Temporary variables only used in this function to compute + // the constants + mpfr_t n_minus; + mpfr_t sqrt_n_minus; + mpfr_init2(n_minus, p); + mpfr_init2(sqrt_n_minus, p); + + mpfr_set_si(n_minus, -1, MPFR_RNDF); + mpfr_div_ui(n_minus, n_minus, 24, MPFR_RNDF); // n_minus = -1/24 + mpfr_add_ui(n_minus, n_minus, n, MPFR_RNDF); // n_minus = n - 1/24 + + mpfr_sqrt(sqrt_n_minus, n_minus, MPFR_RNDF); // sqrt_n_minus = sqrt(n - 1/24) + + mpfr_sqrt_ui(mp_sqrt2, 2, MPFR_RNDF); // mp_sqrt2 = sqrt(2) + mpfr_sqrt_ui(mp_sqrt3, 3, MPFR_RNDF); // mp_sqrt3 = sqrt(3) + mpfr_const_pi(mp_pi, MPFR_RNDF); // mp_pi = π + + // mp_A = sqrt(2) * π * sqrt(n - 1/24) + mpfr_set(mp_A, mp_sqrt2, MPFR_RNDF); // mp_A = sqrt(2) + mpfr_mul(mp_A, mp_A, mp_pi, MPFR_RNDF); // mp_A = sqrt(2) * π + mpfr_mul(mp_A, mp_A, sqrt_n_minus, MPFR_RNDF); // mp_A = sqrt(2) * π * sqrt(n - 1/24) + + // mp_B = 2 * sqrt(3) * (n - 1/24) + mpfr_set_ui(mp_B, 2, MPFR_RNDF); // mp_A = 2 + mpfr_mul(mp_B, mp_B, mp_sqrt3, MPFR_RNDF); // mp_A = 2 * sqrt(3) + mpfr_mul(mp_B, mp_B, n_minus, MPFR_RNDF); // mp_A = 2 * sqrt(3) * (n - 1/24) + + // mp_C = sqrt(2) * π * sqrt(n - 1/24) / sqrt(3) + mpfr_set(mp_C, mp_sqrt2, MPFR_RNDF); // mp_C = sqrt(2) + mpfr_mul(mp_C, mp_C, mp_pi, MPFR_RNDF); // mp_C = sqrt(2) * π + mpfr_mul(mp_C, mp_C, sqrt_n_minus, MPFR_RNDF); // mp_C = sqrt(2) * π * sqrt(n - 1/24) + mpfr_div(mp_C, mp_C, mp_sqrt3, MPFR_RNDF); // mp_C = sqrt(2) * π * sqrt(n - 1/24) / sqrt3 + + // mp_D = 2 * (n - 1/24) * sqrt(n - 1/24) + mpfr_set_ui(mp_D, 2, MPFR_RNDF); // mp_D = 2 + mpfr_mul(mp_D, mp_D, n_minus, MPFR_RNDF); // mp_D = 2 * (n - 1/24) + mpfr_mul(mp_D, mp_D, sqrt_n_minus, MPFR_RNDF); // mp_D = 2 * (n - 1/24) * sqrt(n - 1/24) + + // Convert these to double and long double + d_pi = mpfr_get_d(mp_pi, MPFR_RNDN); + d_A = mpfr_get_d(mp_A, MPFR_RNDN); + d_B = mpfr_get_d(mp_B, MPFR_RNDN); + d_C = mpfr_get_d(mp_C, MPFR_RNDN); + d_D = mpfr_get_d(mp_D, MPFR_RNDN); + + ld_pi = mpfr_get_ld(mp_pi, MPFR_RNDN); + ld_A = mpfr_get_ld(mp_A, MPFR_RNDN); + ld_B = mpfr_get_ld(mp_B, MPFR_RNDN); + ld_C = mpfr_get_ld(mp_C, MPFR_RNDN); + ld_D = mpfr_get_ld(mp_D, MPFR_RNDN); + + // Drop the precision of the computed constants + mpfr_prec_round(mp_pi, prec, MPFR_RNDN); + mpfr_prec_round(mp_sqrt2, prec, MPFR_RNDN); + mpfr_prec_round(mp_sqrt3, prec, MPFR_RNDN); + mpfr_prec_round(mp_A, prec, MPFR_RNDN); + mpfr_prec_round(mp_B, prec, MPFR_RNDN); + mpfr_prec_round(mp_C, prec, MPFR_RNDN); + mpfr_prec_round(mp_D, prec, MPFR_RNDN); + + mpfr_clear(n_minus); + mpfr_clear(sqrt_n_minus); } -void clear_mpz_and_mpq_variables() { - mpz_clear(ztemp1); +static void clear_globals() { mpq_clear(qtemps); mpq_clear(qtempa); mpq_clear(qtempa2); + mpfr_clear(mptemp); + mpfr_clear(mp_sqrt2); + mpfr_clear(mp_sqrt3); + mpfr_clear(mp_pi); + mpfr_clear(mp_A); + mpfr_clear(mp_B); + mpfr_clear(mp_C); + mpfr_clear(mp_D); } -void mp_f(mpfr_t result, unsigned int k) { + +static void set_temp_precision(mp_prec_t prec) { + // Set the precision of the temporary MPFR variables to "prec". + // This requires that the temporary variables have already been + // initialized by initialize_globals(). + mpfr_set_prec(mptemp, prec); +} + + +static void mp_f(mpfr_t result, unsigned int k) { // compute f_n(k) as described in the introduction - // + // notice that this doesn't use n - the "constants" // A, B, C, and D depend on n, but they are precomputed // once before this function is called. //result = pi * sqrt(2) * cosh(A/(sqrt(3)*k))/(B*k) - sinh(C/k)/D; - // mpfr_set(result, mp_pi, round_mode); // result = pi mpfr_mul(result, result, mp_sqrt2, round_mode); // result = sqrt(2) * pi - // - mpfr_div(tempf1, mp_A, mp_sqrt3, round_mode); // temp1 = mp_A/sqrt(3) - mpfr_div_ui(tempf1, tempf1, k, round_mode); // temp1 = mp_A/(sqrt(3) * k) - mpfr_cosh(tempf1, tempf1, round_mode); // temp1 = cosh(mp_A/(sqrt(3) * k)) - mpfr_div(tempf1, tempf1, mp_B, round_mode); // temp1 = cosh(mp_A/(sqrt(3) * k))/mp_B - mpfr_div_ui(tempf1, tempf1, k, round_mode); // temp1 = cosh(mp_A/(sqrt(3) * k))/(mp_B*k) - // - mpfr_mul(result, result, tempf1, round_mode); // result = sqrt(2) * pi * cosh(mp_A/(sqrt(3) * k))/(mp_B*k) - // - mpfr_div_ui(tempf1, mp_C, k, round_mode); // temp1 = mp_C/k - mpfr_sinh(tempf1, tempf1, round_mode); // temp1 = sinh(mp_C/k) - mpfr_div(tempf1, tempf1, mp_D, round_mode); // temp1 = sinh(mp_C/k)/D - // - mpfr_sub(result, result, tempf1, round_mode); // result = RESULT! - // - //return pi * sqrt(2) * cosh(A/(sqrt(3)*k))/(B*k) - sinh(C/k)/D; + + mpfr_div(mptemp, mp_A, mp_sqrt3, round_mode); // temp = mp_A/sqrt(3) + mpfr_div_ui(mptemp, mptemp, k, round_mode); // temp = mp_A/(sqrt(3) * k) + mpfr_cosh(mptemp, mptemp, round_mode); // temp = cosh(mp_A/(sqrt(3) * k)) + mpfr_div(mptemp, mptemp, mp_B, round_mode); // temp = cosh(mp_A/(sqrt(3) * k))/mp_B + mpfr_div_ui(mptemp, mptemp, k, round_mode); // temp = cosh(mp_A/(sqrt(3) * k))/(mp_B*k) + + mpfr_mul(result, result, mptemp, round_mode); // result = sqrt(2) * pi * cosh(mp_A/(sqrt(3) * k))/(mp_B*k) + + mpfr_div_ui(mptemp, mp_C, k, round_mode); // temp = mp_C/k + mpfr_sinh(mptemp, mptemp, round_mode); // temp = sinh(mp_C/k) + mpfr_div(mptemp, mptemp, mp_D, round_mode); // temp = sinh(mp_C/k)/D + + mpfr_sub(result, result, mptemp, round_mode); // result = RESULT! } // call the following when 4k < sqrt(UINT_MAX) -// + // TODO: maybe a faster version of this can be written without using mpq_t, // or maybe this version can be smarter. -// + // The actual value of the cosine that we compute using s(h,k) // only depends on {s(h,k)/2}, that is, the fractional // part of s(h,k)/2. It may be possible to make use of this somehow. -// -void q_s(mpq_t result, unsigned int h, unsigned int k) { - if(k < 3) { +static void q_s(mpq_t result, unsigned int h, unsigned int k) { + if (k < 3) { mpq_set_ui(result, 0, 1); return; } if (h == 1) { unsigned int d = GCD( (k-1)*(k-2), 12*k); - if(d > 1) { + if (d > 1) { mpq_set_ui(result, ((k-1)*(k-2))/d, (12*k)/d); } else { @@ -814,7 +697,7 @@ void q_s(mpq_t result, unsigned int h, unsigned int k) { // and also some more listed in some of the links mentioned in the introduction, but // it seems like there might not be much speed benefit to this, and putting in too // many seems to slow things down a little. - // + // if h = 2 and k is odd, we have // s(h,k) = (k-1)*(k-5)/24k @@ -832,9 +715,9 @@ void q_s(mpq_t result, unsigned int h, unsigned int k) { /* // if k % h == 1, then - // + // s(h,k) = (k-1)(k - h^2 - 1)/(12hk) - // + // We need to be a little careful here because k - h^2 - 1 can be negative. if(k % h == 1) { @@ -851,9 +734,9 @@ void q_s(mpq_t result, unsigned int h, unsigned int k) { } // if k % h == 2, then - // + // s(h,k) = (k-2)[k - .5(h^2 + 1)]/(12hk) - // + //if(k % h == 2) { //} @@ -871,39 +754,36 @@ void q_s(mpq_t result, unsigned int h, unsigned int k) { int temp3; while(r1 > 0 && r2 > 0) { unsigned int d = GCD(r1 * r1 + r2 * r2 + 1, r1 * r2); - if(d > 1) { + if (d > 1) { mpq_set_ui(qtemps, (r1 * r1 + r2 * r2 + 1)/d, (r1 * r2)/d); } else{ mpq_set_ui(qtemps, r1 * r1 + r2 * r2 + 1, r1 * r2); } - //mpq_canonicalize(qtemps); - - if(n % 2 == 0){ // - mpq_add(result, result, qtemps); // result += temp1; - } // - else { // - mpq_sub(result, result, qtemps); // result -= temp1; - } // - temp3 = r1 % r2; // - r1 = r2; // - r2 = temp3; // - n++; // + + if (n % 2 == 0){ + mpq_add(result, result, qtemps); // result += temp; + } + else { + mpq_sub(result, result, qtemps); // result -= temp; + } + temp3 = r1 % r2; + r1 = r2; + r2 = temp3; + n++; } mpq_set_ui(qtemps, 1, 12); mpq_mul(result, result, qtemps); // result = result * 1.0/12.0; - - if(n % 2 == 1) { + if (n % 2 == 1) { mpq_set_ui(qtemps, 1, 4); mpq_sub(result, result, qtemps); // result = result - .25; } - } -void mp_a(mpfr_t result, unsigned int n, unsigned int k) { +static void mp_a(mpfr_t result, unsigned int n, unsigned int k) { // compute a(n,k) if (k == 1) { @@ -913,13 +793,13 @@ void mp_a(mpfr_t result, unsigned int n, unsigned int k) { mpfr_set_ui(result, 0, round_mode); - unsigned int h = 0; - for(h = 1; h < k+1; h++) { - if(GCD(h,k) == 1) { + unsigned int h; + for (h = 1; h < k+1; h++) { + if (GCD(h,k) == 1) { // Note that we compute each term of the summand as // result += cos(pi * ( s(h,k) - (2.0 * h * n)/k) ); - // + // This is the real part of the exponential that was written // down in the introduction, and we don't need to compute // the imaginary part because we know that, in the end, the @@ -927,21 +807,17 @@ void mp_a(mpfr_t result, unsigned int n, unsigned int k) { q_s(qtempa, h, k); - //mpfr_mul_q(tempa1, mp_pi, qtempa, round_mode); - //mpfr_mul_ui(tempa1, tempa1, k * k, round_mode); - - //mpfr_set_q(tempa1, qtempa, round_mode); unsigned int r = n % k; // here we make use of the fact that the unsigned int d = GCD(r,k); // cos() term written above only depends unsigned int K; // on {hn/k}. - if(d > 1) { + if (d > 1) { r = r/d; K = k/d; } else { K = k; } - if(K % 2 == 0) { + if (K % 2 == 0) { K = K/2; } else { @@ -950,29 +826,21 @@ void mp_a(mpfr_t result, unsigned int n, unsigned int k) { mpq_set_ui(qtempa2, h*r, K); mpq_sub(qtempa, qtempa, qtempa2); - //mpfr_set_q(tempa2, qtempa, round_mode); // This might be faster, according to - //cospi(tempa1, tempa2); // the comments in Ralf Stephan's part.c, but - // I haven't noticed a significant speed up. - // (Perhaps a different version that takes an mpq_t - // as an input might be faster.) - - mpfr_mul_q(tempa1, mp_pi, qtempa, round_mode); - mpfr_cos(tempa1, tempa1, round_mode); - mpfr_add(result, result, tempa1, round_mode); - + mpfr_mul_q(mptemp, mp_pi, qtempa, round_mode); + mpfr_cos(mptemp, mptemp, round_mode); + mpfr_add(result, result, mptemp, round_mode); } - } - } + template inline T partial_sum_of_t(unsigned int n, unsigned int &k, unsigned int exit_precision, unsigned int extra_precision, double error = 0) { unsigned int current_precision = compute_current_precision(n, k - 1, extra_precision); T result = 0; - if(error == 0) { - for(; current_precision > exit_precision; k++) { // (don't change k -- it is already the right value) - result += sqrt(T(int(k))) * a(n,k) * f(k); // + if (error == 0) { + for (; current_precision > exit_precision; k++) { // (don't change k -- it is already the right value) + result += sqrt(T(k)) * a(n,k) * f(k); current_precision = compute_current_precision(n,k,extra_precision); // The only reason that we compute the new precision // now is so that we know when we can change to using just doubles. // (There should be a 'long double' version of the compute_current_precision function. @@ -980,8 +848,8 @@ inline T partial_sum_of_t(unsigned int n, unsigned int &k, unsigned int exit_pre } else { double remainder = 1; - for(; remainder > error; k++) { // (don't change k -- it is already the right value) - result += sqrt(T(int(k))) * a(n,k) * f(k); // + for (; remainder > error; k++) { // (don't change k -- it is already the right value) + result += sqrt(T(k)) * a(n,k) * f(k); remainder = compute_remainder(n,k); } } @@ -989,13 +857,13 @@ inline T partial_sum_of_t(unsigned int n, unsigned int &k, unsigned int exit_pre } -void mp_t(mpfr_t result, unsigned int n) { +static void mp_t(mpfr_t result, unsigned int n) { // This is the function that actually computes p(n). - // + // More specifically, it computes t(n,N) to within 1/2 of p(n), and then // we can find p(n) by rounding. - // - // + + // NOTE: result should NOT have been initialized when this is called, // as we initialize it to the proper precision in this function. @@ -1005,17 +873,15 @@ void mp_t(mpfr_t result, unsigned int n) { unsigned int initial_precision = compute_initial_precision(n); // We begin by computing the precision necessary to hold the final answer. // and then initialize both the result and some temporary variables to that // precision. - mpfr_t t1, t2; // - mpfr_init2(t1, initial_precision); // - mpfr_init2(t2, initial_precision); // - mpfr_init2(result, initial_precision); // - mpfr_set_ui(result, 0, round_mode); // - - initialize_mpz_and_mpq_variables(); - initialize_constants(initial_precision, n); // Now that we have the precision information, we initialize some constants - // that will be used throughout, and also - // - initialize_mpfr_variables(initial_precision); // set the precision of the "temp" variables that are used in individual functions. + mpfr_t t1, t2; + mpfr_init2(t1, initial_precision); + mpfr_init2(t2, initial_precision); + mpfr_init2(result, initial_precision); + mpfr_set_ui(result, 0, round_mode); + + initialize_globals(initial_precision, n); // Now that we have the precision information, we initialize some constants + // that will be used throughout, and also set the precision of the "temp" + // variables that are used in individual functions. unsigned int current_precision = initial_precision; unsigned int new_precision; @@ -1025,37 +891,34 @@ void mp_t(mpfr_t result, unsigned int n) { // anymore. Once current_precision == min_precision, we drop // out of this loop and switch to a computation // that only involves doubles. - - - unsigned int k = 1; // (k holds the index of the summand that we are computing.) - for(k = 1; current_precision > level_two_precision; k++) { // + for (k = 1; current_precision > level_two_precision; k++) { mpfr_sqrt_ui(t1, k, round_mode); // t1 = sqrt(k) - // + mp_a(t2, n, k); // t2 = A_k(n) - if(debuga) { + if (debuga) { cout << "a(" << k << ") = "; mpfr_out_str(stdout, 10, 10, t2, round_mode); cout << endl; } mpfr_mul(t1, t1, t2, round_mode); // t1 = sqrt(k)*A_k(n) - // + mp_f(t2, k); // t2 = f_k(n) - if(debugf) { + if (debugf) { cout << "f(" << n << "," << k << ") = "; mpfr_out_str(stdout, 10, 0, t2, round_mode); cout << endl; } mpfr_mul(t1, t1, t2, round_mode); // t1 = sqrt(k)*A_k(n)*f_k(n) - // + mpfr_add(result, result, t1, round_mode); // result += summand - if(debugt) { + if (debugt) { int num_digits = 20; int num_extra_digits; char digits[num_digits + 1]; @@ -1071,122 +934,84 @@ void mp_t(mpfr_t result, unsigned int n) { } new_precision = compute_current_precision(n,k,extra); // After computing one summand, check what the new precision should be. - if(new_precision != current_precision) { // If the precision changes, we need to clear - current_precision = new_precision; // and reinitialize all "temp" variables to - clear_mpfr_variables(); // use lower precision. - initialize_mpfr_variables(current_precision); // - mpfr_clear(t1); mpfr_clear(t2); // - mpfr_init2(t1, current_precision); // - mpfr_init2(t2, current_precision); // + if (new_precision != current_precision) { // If the precision changes, we need to fix the + current_precision = new_precision; // precision of all "temp" variables. + set_temp_precision(current_precision); + mpfr_set_prec(t1, current_precision); + mpfr_set_prec(t2, current_precision); } } - mpfr_clear(t1); mpfr_clear(t2); - - mpfr_init2(t1, 200); - mpfr_init2(t2, 200); - - long double ld_partial_sum = partial_sum_of_t(n,k,level_five_precision, extra, 0); + long double ld_partial_sum = partial_sum_of_t(n, k, level_five_precision, extra, 0); + mpfr_set_prec(t1, long_double_precision); mpfr_set_ld(t1, ld_partial_sum, round_mode); mpfr_add(result, result, t1, round_mode); - double d_partial_sum = partial_sum_of_t(n,k,0,extra,error); - - - mpfr_set_d(t1, d_partial_sum, round_mode); // - mpfr_add(result, result, t1, round_mode); // We add together the main result and the tail ends' - + double d_partial_sum = partial_sum_of_t(n, k, 0, extra, error); + mpfr_add_d(result, result, d_partial_sum, round_mode); // We add together the main result and the tail ends' mpfr_div(result, result, mp_pi, round_mode); // The actual result is the sum that we have computed mpfr_div(result, result, mp_sqrt2, round_mode); // divided by pi*sqrt(2). - clear_constants(); - clear_mpz_and_mpq_variables(); - clear_mpfr_variables(); + clear_globals(); mpfr_clear(t1); mpfr_clear(t2); } + template T f(unsigned int k) { return pi_sqrt2() * cosh(A()/(sqrt3()*k))/(B() * k) - sinh(C()/k)/D(); } + template T a(unsigned int n, unsigned int k) { - if(k == 1) { + if (k == 1) { return 1; } T result = 0; unsigned int h = 0; - for(h = 1; h < k+1; h++) { - if(GCD(h,k) == 1) { - result += cos( pi() * ( s(h,k) - T(2.0 * double(h) * n)/T(int(k))) ); // be careful to promote 2 and h to Ts - // because the result 2 * h * n could - // be too large. + for (h = 1; h < k+1; h++) { + if (GCD(h,k) == 1) { + result += cos( pi() * ( s(h,k) - T(2.0 * double(h) * n)/T(k)) ); // be careful to promote 2 and h to Ts + // because the result 2 * h * n could + // be too large. } } return result; } + template T s(unsigned int h, unsigned int k) { - //mpq_set_ui(result, 1, 1); - //return; - - //return T(0); // Uncommenting this line saves 25% or more time in the computation of p(10^9) in the current version (.51) - // but, of course, gives wrong results. (This is just to give an idea of how much time could - // possibly be saved by optimizing this function. - - if(k < 3) { + if (k < 3) { return T(0); } if (h == 1) { - return T( int((k-1)*(k-2)) )/T(int(12 * k)); + return T((k-1) * (k-2))/T(12 * k); } // TODO: In the function mp_s() there are a few special cases for special forms of h and k. // (And there are more special cases listed in one of the references listed in the introduction.) - // + // It may be advantageous to implement some here, but I'm not sure // if there is any real speed benefit to this. - // + // In the mpfr_t version of this function, the speedups didn't seem to help too much, but // they might make more of a difference here. - // + // Update to the above comments: // Actually, a few tests seem to indicate that putting in too many special // cases slows things down a little bit. // if h = 2 and k is odd, we have // s(h,k) = (k-1)*(k-5)/24k - if(h == 2 && k > 5 && k % 2 == 1) { - return T( int((k-1)*(k-5)) )/ T(int(24*k)); + if (h == 2 && k > 5 && k % 2 == 1) { + return T((k-1)*(k-5))/ T(24 * k); } - - // if k % h == 1, then - // - // s(h,k) = (k-1)(k - h^2 - 1)/(12hk) - // - - // We might need to be a little careful here because k - h^2 - 1 can be negative. - // THIS CODE DOESN'T WORK. - //if(k % h == 1) { - // int num = (k-1)*(k - h*h - 1); - // int den = 12*k*h; - // return T(num)/T(den); - //} - - // if k % h == 2, then - // - // s(h,k) = (k-2)[k - .5(h^2 + 1)]/(12hk) - // - - //if(k % h == 2) { - //} - int r1 = k; int r2 = h; @@ -1198,26 +1023,26 @@ T s(unsigned int h, unsigned int k) { while(r2 > 0) // Note that we maintain the invariant r1 >= r2, so // we only need to make sure that r2 > 0 { - temp = T(int(r1 * r1 + r2 * r2 + 1))/(n * r1 * r2); + temp = T(r1 * r1 + r2 * r2 + 1)/(n * r1 * r2); temp3 = r1 % r2; r1 = r2; r2 = temp3; - result += temp; // result += temp1; + result += temp; n = -n; } result *= one_over_12(); - if(n < 0) { + if (n < 0) { result -= T(.25); } return result; } -long GCD(long a, long b) +static long GCD(long a, long b) { long u, v, t, x; @@ -1248,109 +1073,33 @@ long GCD(long a, long b) } - -// The following function was copied from Ralf Stephan's code, -// mentioned in the introduction, and then some variable names -// were changed. The function is not currently used, however. -void cospi (mpfr_t res, - mpfr_t x) -{ -// mpfr_t t, tt, half, fourth; - -// mpfr_init2 (t, prec); -// mpfr_init2 (tt, prec); -// mpfr_init2 (half, prec); -// mpfr_init2 (fourth, prec); - -// mpfr_set_ui (half, 1, r); -// mpfr_div_2ui (half, half, 1, r); -// mpfr_div_2ui (fourth, half, 1, r); - - // NOTE: switched t to tempc2 - // and tt to tempc1 - - - mpfr_rnd_t r = round_mode; - - - mpfr_div_2ui (tempc1, x, 1, r); - mpfr_floor (tempc1, tempc1); - mpfr_mul_2ui (tempc1, tempc1, 1, r); - mpfr_sub (tempc2, x, tempc1, r); - if (mpfr_cmp_ui (tempc2, 1) > 0) - mpfr_sub_ui (tempc2, tempc2, 2, r); - mpfr_abs (tempc1, tempc2, r); - if (mpfr_cmp (tempc1, half) > 0) - { - mpfr_ui_sub (tempc2, 1, tempc1, r); - mpfr_abs (tempc1, tempc2, r); - if (mpfr_cmp (tempc1, fourth) > 0) - { - if (mpfr_sgn (tempc2) > 0) - mpfr_sub (tempc2, half, tempc2, r); - else - mpfr_add (tempc2, tempc2, half, r); - mpfr_mul (tempc2, tempc2, mp_pi, r); - mpfr_sin (tempc2, tempc2, r); - } - else - { - mpfr_mul (tempc2, tempc2, mp_pi, r); - mpfr_cos (tempc2, tempc2, r); - } - mpfr_neg (res, tempc2, r); - } - else - { - mpfr_abs (tempc1, tempc2, r); - if (mpfr_cmp (tempc1, fourth) > 0) - { - if (mpfr_sgn (tempc2) > 0) - mpfr_sub (tempc2, half, tempc2, r); - else - mpfr_add (tempc2, tempc2, half, r); - mpfr_mul (tempc2, tempc2, mp_pi, r); - mpfr_sin (res, tempc2, r); - } - else - { - mpfr_mul (tempc2, tempc2, mp_pi, r); - mpfr_cos (res, tempc2, r); - } - } - -// mpfr_clear (half); -// mpfr_clear (fourth); -// mpfr_clear (t); -// mpfr_clear (tt); -} - -/* answer must have already been mpz_init'd. */ -int part(mpz_t answer, unsigned int n){ - if(n == 1) { - mpz_set_str(answer, "1", 10); - return 0; +// answer must have already been mpz_init'd. +void part(mpz_t answer, unsigned int n){ + if (n <= 1) { + mpz_set_ui(answer, 1); + return; } mpfr_t result; mp_t(result, n); - mpfr_get_z(answer, result, round_mode); + mpfr_get_z(answer, result, MPFR_RNDN); mpfr_clear(result); - return 0; } +// This program is mainly meant for inclusion in SageMath (or any other +// program, if anyone feels like it). We include a main() function +// anyway, because it is useful to compile this as a standalone program +// for testing purposes. int main(int argc, char *argv[]){ - //init(); - unsigned int n = 10; - if(argc > 1) - if(strcmp(argv[1], "test") == 0) { + if (argc > 1) + if (strcmp(argv[1], "test") == 0) { n = test(true); - if(n == 0) { + if (n == 0) { cout << "All Tests Passed" << endl; } else { @@ -1358,9 +1107,9 @@ int main(int argc, char *argv[]){ } return 0; } - else if(strcmp(argv[1], "testforever") == 0) { + else if (strcmp(argv[1], "testforever") == 0) { n = test(false, true); - if(n == 0) { + if (n == 0) { cout << "All Tests Passed" << endl; } else { @@ -1373,7 +1122,7 @@ int main(int argc, char *argv[]){ } else { n = test(false); - if(n == 0) { + if (n == 0) { cout << "All short tests passed. Run '" << argv[0] << " test' to run all tests. (This may take some time, but it gives updates as it progresses, and can be interrupted.)" << endl; cout << "Run with the argument 'testforever' to run tests until a failure is found (or, hopefully, to run tests forever.)" << endl; } @@ -1422,7 +1171,7 @@ int test(bool longtest, bool forever) { mpz_set_str(expected_value, "1", 10); part(actual_value, n); - if(mpz_cmp(expected_value, actual_value) != 0) + if (mpz_cmp(expected_value, actual_value) != 0) return n; cout << " OK." << endl; @@ -1433,7 +1182,7 @@ int test(bool longtest, bool forever) { mpz_set_str(expected_value, "42", 10); part(actual_value, n); - if(mpz_cmp(expected_value, actual_value) != 0) + if (mpz_cmp(expected_value, actual_value) != 0) return n; cout << " OK." << endl; @@ -1444,7 +1193,7 @@ int test(bool longtest, bool forever) { mpz_set_str(expected_value, "190569292", 10); part(actual_value, n); - if(mpz_cmp(expected_value, actual_value) != 0) + if (mpz_cmp(expected_value, actual_value) != 0) return n; cout << " OK." << endl; @@ -1455,7 +1204,7 @@ int test(bool longtest, bool forever) { mpz_set_str(expected_value, "24061467864032622473692149727991", 10); part(actual_value, n); - if(mpz_cmp(expected_value, actual_value) != 0) + if (mpz_cmp(expected_value, actual_value) != 0) return n; cout << " OK." << endl; @@ -1466,7 +1215,7 @@ int test(bool longtest, bool forever) { mpz_set_str(expected_value, "36167251325636293988820471890953695495016030339315650422081868605887952568754066420592310556052906916435144", 10); part(actual_value, n); - if(mpz_cmp(expected_value, actual_value) != 0) + if (mpz_cmp(expected_value, actual_value) != 0) return n; cout << " OK." << endl; @@ -1477,7 +1226,7 @@ int test(bool longtest, bool forever) { mpz_set_str(expected_value, "27493510569775696512677516320986352688173429315980054758203125984302147328114964173055050741660736621590157844774296248940493063070200461792764493033510116079342457190155718943509725312466108452006369558934464248716828789832182345009262853831404597021307130674510624419227311238999702284408609370935531629697851569569892196108480158600569421098519", 10); part(actual_value, n); - if(mpz_cmp(expected_value, actual_value) != 0) + if (mpz_cmp(expected_value, actual_value) != 0) return n; cout << " OK." << endl; @@ -1488,7 +1237,7 @@ int test(bool longtest, bool forever) { mpz_set_str(expected_value, "1471684986358223398631004760609895943484030484439142125334612747351666117418918618276330148873983597555842015374130600288095929387347128232270327849578001932784396072064228659048713020170971840761025676479860846908142829356706929785991290519899445490672219997823452874982974022288229850136767566294781887494687879003824699988197729200632068668735996662273816798266213482417208446631027428001918132198177180646511234542595026728424452592296781193448139994664730105742564359154794989181485285351370551399476719981691459022015599101959601417474075715430750022184895815209339012481734469448319323280150665384042994054179587751761294916248142479998802936507195257074485047571662771763903391442495113823298195263008336489826045837712202455304996382144601028531832004519046591968302787537418118486000612016852593542741980215046267245473237321845833427512524227465399130174076941280847400831542217999286071108336303316298289102444649696805395416791875480010852636774022023128467646919775022348562520747741843343657801534130704761975530375169707999287040285677841619347472368171772154046664303121315630003467104673818", 10); part(actual_value, n); - if(mpz_cmp(expected_value, actual_value) != 0) + if (mpz_cmp(expected_value, actual_value) != 0) return n; cout << " OK." << endl; @@ -1500,7 +1249,7 @@ int test(bool longtest, bool forever) { cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) + if (mpz_divisible_ui_p(actual_value, 385) == 0) return n; cout << " OK." << endl; @@ -1509,7 +1258,7 @@ int test(bool longtest, bool forever) { cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) + if (mpz_divisible_ui_p(actual_value, 385) == 0) return n; cout << " OK." << endl; @@ -1518,7 +1267,7 @@ int test(bool longtest, bool forever) { cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) + if (mpz_divisible_ui_p(actual_value, 385) == 0) return n; cout << " OK." << endl; @@ -1527,7 +1276,7 @@ int test(bool longtest, bool forever) { cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) + if (mpz_divisible_ui_p(actual_value, 385) == 0) return n; cout << " OK." << endl; @@ -1536,7 +1285,7 @@ int test(bool longtest, bool forever) { cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) + if (mpz_divisible_ui_p(actual_value, 385) == 0) return n; cout << " OK." << endl; @@ -1545,7 +1294,7 @@ int test(bool longtest, bool forever) { cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) + if (mpz_divisible_ui_p(actual_value, 385) == 0) return n; cout << " OK." << endl; @@ -1554,25 +1303,25 @@ int test(bool longtest, bool forever) { srand( time(NULL) ); - for(int i = 0; i < 100; i++) { + for (int i = 0; i < 100; i++) { n = int(100000 * double(rand())/double(RAND_MAX) + 1); n = n - (n % 385) + 369; cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) { + if (mpz_divisible_ui_p(actual_value, 385) == 0) { return n; } cout << " OK." << endl; } - if(longtest) { + if (longtest) { n = 369 + 1000*385; cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) + if (mpz_divisible_ui_p(actual_value, 385) == 0) return n; cout << " OK." << endl; @@ -1581,7 +1330,7 @@ int test(bool longtest, bool forever) { cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) + if (mpz_divisible_ui_p(actual_value, 385) == 0) return n; cout << " OK." << endl; @@ -1590,54 +1339,54 @@ int test(bool longtest, bool forever) { cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) + if (mpz_divisible_ui_p(actual_value, 385) == 0) return n; cout << " OK." << endl; - for(int i = 0; i < 20; i++) { + for (int i = 0; i < 20; i++) { n = int(100000 * double(rand())/double(RAND_MAX) + 1) + 100000; n = n - (n % 385) + 369; cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) { + if (mpz_divisible_ui_p(actual_value, 385) == 0) { return n; } cout << " OK." << endl; } - for(int i = 0; i < 20; i++) { + for (int i = 0; i < 20; i++) { n = int(100000 * double(rand())/double(RAND_MAX) + 1) + 500000; n = n - (n % 385) + 369; cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) { + if (mpz_divisible_ui_p(actual_value, 385) == 0) { return n; } cout << " OK." << endl; } - for(int i = 0; i < 20; i++) { + for (int i = 0; i < 20; i++) { n = int(100000 * double(rand())/double(RAND_MAX) + 1) + 1000000; n = n - (n % 385) + 369; cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) { + if (mpz_divisible_ui_p(actual_value, 385) == 0) { return n; } cout << " OK." << endl; } - for(int i = 0; i < 10; i++) { + for (int i = 0; i < 10; i++) { n = int(100000 * double(rand())/double(RAND_MAX) + 1) + 10000000; n = n - (n % 385) + 369; cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) { + if (mpz_divisible_ui_p(actual_value, 385) == 0) { return n; } cout << " OK." << endl; @@ -1647,18 +1396,18 @@ int test(bool longtest, bool forever) { cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) + if (mpz_divisible_ui_p(actual_value, 385) == 0) return n; cout << " OK." << endl; - for(int i = 0; i < 10; i++) { + for (int i = 0; i < 10; i++) { n = int(100000000 * double(rand())/double(RAND_MAX) + 1) + 100000000; n = n - (n % 385) + 369; cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) { + if (mpz_divisible_ui_p(actual_value, 385) == 0) { return n; } cout << " OK." << endl; @@ -1668,19 +1417,19 @@ int test(bool longtest, bool forever) { cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) + if (mpz_divisible_ui_p(actual_value, 385) == 0) return n; cout << " OK." << endl; - for(int i = 0; i < 10; i++) { + for (int i = 0; i < 10; i++) { n = int(100000000 * double(rand())/double(RAND_MAX) + 1) + 1000000000; n = n - (n % 385) + 369; cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) { + if (mpz_divisible_ui_p(actual_value, 385) == 0) { return n; } cout << " OK." << endl; @@ -1688,75 +1437,71 @@ int test(bool longtest, bool forever) { } - if(forever) { + if (forever) { while(1) { - for(int i = 0; i < 100; i++) { + for (int i = 0; i < 100; i++) { n = int(900000 * double(rand())/double(RAND_MAX) + 1) + 100000; n = n - (n % 385) + 369; cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) { + if (mpz_divisible_ui_p(actual_value, 385) == 0) { return n; } cout << " OK." << endl; } - for(int i = 0; i < 50; i++) { + for (int i = 0; i < 50; i++) { n = int(9000000 * double(rand())/double(RAND_MAX) + 1) + 1000000; n = n - (n % 385) + 369; cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) { + if (mpz_divisible_ui_p(actual_value, 385) == 0) { return n; } cout << " OK." << endl; } - for(int i = 0; i < 50; i++) { + for (int i = 0; i < 50; i++) { n = int(90000000 * double(rand())/double(RAND_MAX) + 1) + 10000000; n = n - (n % 385) + 369; cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) { + if (mpz_divisible_ui_p(actual_value, 385) == 0) { return n; } cout << " OK." << endl; } - for(int i = 0; i < 10; i++) { + for (int i = 0; i < 10; i++) { n = int(900000000 * double(rand())/double(RAND_MAX) + 1) + 100000000; n = n - (n % 385) + 369; cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) { + if (mpz_divisible_ui_p(actual_value, 385) == 0) { return n; } cout << " OK." << endl; } - for(int i = 0; i < 5; i++) { + for (int i = 0; i < 5; i++) { n = int(100000000 * double(rand())/double(RAND_MAX) + 1) + 1000000000; n = n - (n % 385) + 369; cout << "Computing p(" << n << ")..."; cout.flush(); part(actual_value, n); - if(mpz_divisible_ui_p(actual_value, 385) == 0) { + if (mpz_divisible_ui_p(actual_value, 385) == 0) { return n; } cout << " OK." << endl; } } - - } mpz_clear(expected_value); mpz_clear(actual_value); return 0; } - - diff --git a/src/sage/combinat/partitions_c.h b/src/sage/combinat/partitions_c.h deleted file mode 100644 index e82ac77ea10..00000000000 --- a/src/sage/combinat/partitions_c.h +++ /dev/null @@ -1,10 +0,0 @@ -#if defined(__sun) -#include -int isinf(double x) { return !finite(x) && x==x; } -#endif - -#include - -int part(mpz_t answer, unsigned int n); -int test(bool longtest, bool forever); - From 6bedfb7472f4a86819bc54b97c81b63083f08b73 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 6 Feb 2018 06:32:38 -0600 Subject: [PATCH 656/740] Use six.text_type. --- src/sage/combinat/tableau.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index 4eba89928c3..1487a657d35 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -68,7 +68,7 @@ #***************************************************************************** from __future__ import print_function, absolute_import from six.moves import range, zip -from six import add_metaclass +from six import add_metaclass, text_type from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets from sage.sets.family import Family @@ -597,7 +597,7 @@ def _ascii_art_table(self, use_unicode=False): if use_unicode: # Special handling of overline not adding to printed length def get_len(e): - return len(e) - list(unicode(e)).count(u"\u0304") + return len(e) - list(text_type(e)).count(u"\u0304") else: get_len = len for row in str_tab: From c7471ccee89cd5ffdc6c729815c7bd9d347b959e Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 6 Feb 2018 14:32:01 +0100 Subject: [PATCH 657/740] Move the matplotlib import for doctests to init_sage() --- src/sage/doctest/control.py | 5 ----- src/sage/doctest/forker.py | 19 ++++++------------- src/sage/tests/startup.py | 4 ++-- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index f35e68bb169..8dbbc3c138e 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -42,11 +42,6 @@ # Optional tags which are always automatically added auto_optional_tags = set(['py2' if six.PY2 else 'py3']) -# Make sure the agg backend is selected during doctesting -# This needs to be done before any other matplotlib calls. -import matplotlib -matplotlib.use('agg') - class DocTestDefaults(SageObject): """ diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index b15221a9e33..817cbf086c0 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -49,7 +49,6 @@ from sage.structure.sage_object import SageObject from .parsing import SageOutputChecker, pre_hash, get_source from sage.repl.user_globals import set_globals -from sage.interfaces.process import ContainChildren # All doctests run as if the following future imports are present @@ -109,15 +108,12 @@ def init_sage(): {'a': 23, 'aaa': 234, 'au': 56, 'b': 34, 'bbf': 234} """ # We need to ensure that the Matplotlib font cache is built to - # avoid spurious warnings (see Trac #20222). We don't want to - # import matplotlib in the main process because we want the - # doctesting environment to be as close to a real Sage session - # as possible. - with ContainChildren(): - pid = os.fork() - if not pid: - # Child process - from matplotlib.font_manager import fontManager + # avoid spurious warnings (see Trac #20222). + import matplotlib.font_manager + + # Make sure that the agg backend is selected during doctesting. + # This needs to be done before any other matplotlib calls. + matplotlib.use('agg') # Do this once before forking off child processes running the tests. # This is more efficient because we only need to wait once for the @@ -150,9 +146,6 @@ def init_sage(): from sympy.printing.pretty.stringpict import stringPict stringPict.terminal_width = lambda self:0 - # Wait for the child process created above - os.waitpid(pid, 0) - def showwarning_with_traceback(message, category, filename, lineno, file=sys.stdout, line=None): r""" diff --git a/src/sage/tests/startup.py b/src/sage/tests/startup.py index 90e6b549b28..164ba7be005 100644 --- a/src/sage/tests/startup.py +++ b/src/sage/tests/startup.py @@ -17,8 +17,8 @@ Check that numpy (:trac:`11714`) and pyparsing are not imported on startup as they increase the startup time. Since :trac:`23696` those are imported -by the doctest framework via a matplotlib import. Again the simple test would -not work, but this time we don't have to avoid to load ipython. +by the doctest framework via a matplotlib import. Again the simple test +would not work (but we don't have to avoid loading IPython):: sage: from sage.tests.cmdline import test_executable sage: cmd = "print('numpy' in sys.modules)\n" From e6ec1d657fec57c311292fbef96832b8302833c4 Mon Sep 17 00:00:00 2001 From: Madison Van Dyk Date: Tue, 6 Feb 2018 14:06:21 -0500 Subject: [PATCH 658/740] Smaller and faster doctest for order-free isogenies --- src/sage/schemes/elliptic_curves/ell_curve_isogeny.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index bfb4e7db69d..677d4ad3ef3 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -1859,11 +1859,12 @@ def __init_from_kernel_list(self, kernel_gens): to P.order(), since such calls involve factoring the group order which could take a long time. :: - sage: p=12*next_prime(2**516)*next_prime(2**543)-1 - sage: E=EllipticCurve([GF(p)(1),GF(p)(0)]) - sage: P=E(0).division_points(3)[1] - sage: EllipticCurveIsogeny(E,P) - Isogeny of degree 3 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 74121918935782697150572026386322255999593499669485639046716805918248415369138920980942523328163009458686674022576957487081228785663742728344853540017360335968220603279754173597895493608709042384746570991683531266851688876306667526140964490813793422758339760047934425286567991491819082225635623850227154445853927797025147 to Elliptic Curve defined by y^2 = x^3 + 50980532732167944637647016106708857869144958686070887399211900429833015413233148839807050055231319912356312345664008416716445294630236741746525650487701703923321188716120734056225687969070865447975585851125632334975602253072854988951094096051754342218511208877842573773109101461209491421209013134031964257825134320425762*x + 20123325562060579887439486218415531037934433846849708678890785454945560104043719299121967091612317185340446850679666894934881575657808661438571327808333127323560952150611972136419014507945141707672086018167956291485257506710279043521951524501649360994275020881682315300417777303863402326130971448587985197336271065135371 over Finite Field of size 74121918935782697150572026386322255999593499669485639046716805918248415369138920980942523328163009458686674022576957487081228785663742728344853540017360335968220603279754173597895493608709042384746570991683531266851688876306667526140964490813793422758339760047934425286567991491819082225635623850227154445853927797025147 + sage: p = 12 * next_prime(2^246) * next_prime(2^247) - 1 + sage: F = FiniteField(p, proof=False) + sage: E = EllipticCurve([F(1), F(0)]) + sage: P = E(0).division_points(3)[1] + sage: EllipticCurveIsogeny(E, P) + Isogeny of degree 3 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 306880369490263300313736534077587420520310191816537324014058565759012544413001947737908555848973232913399393291567882031064458001335811229593024848691 to Elliptic Curve defined by y^2 = x^3 + 138339744732679989316868434850977055074939617746881563766525552777337080950210098011490359468980566166703728781690013462872744031856023553225684698214*x + 144492060973761695990422770559924271262401033407979769478362459183553396187521975467910654355724411241272805279344391492601859775119243705193259368622 over Finite Field of size 306880369490263300313736534077587420520310191816537324014058565759012544413001947737908555848973232913399393291567882031064458001335811229593024848691 """ if self.__check : From e7e68c7434425b3da8f3ca3bb9670dd61649da7d Mon Sep 17 00:00:00 2001 From: Simon King Date: Tue, 6 Feb 2018 23:42:34 +0100 Subject: [PATCH 659/740] Add method to remove some coercions/conversions from cache --- src/sage/structure/parent.pyx | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index 19229698f89..eedd07a0dde 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -1552,6 +1552,34 @@ cdef class Parent(sage.structure.category_object.CategoryObject): """ return domain in self._convert_from_hash + def _remove_from_coerce_cache(self, domain): + r""" + Remove the coercion and the conversion from ``domain`` to self from the cache. + + EXAMPLES:: + + sage: R. = QQ + sage: R._remove_from_coerce_cache(QQ) + sage: R._is_coercion_cached(QQ) + False + sage: _ = R.coerce_map_from(QQ) + sage: R._is_coercion_cached(QQ) + True + sage: R._remove_from_coerce_cache(QQ) + sage: R._is_coercion_cached(QQ) + False + sage: R._is_conversion_cached(QQ) + False + """ + try: + del self._coerce_from_hash[domain] + except KeyError: + pass + try: + del self._convert_from_hash[domain] + except KeyError: + pass + cpdef register_coercion(self, mor): r""" Update the coercion model to use `mor : P \to \text{self}` to coerce From 6f5ca14f5df97fb77c95786ffe0ae13e4f82db4c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 7 Feb 2018 10:21:22 +1000 Subject: [PATCH 660/740] Adding __format__ for py2/3 support. --- src/sage/typeset/character_art.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sage/typeset/character_art.py b/src/sage/typeset/character_art.py index 97687871604..1d01193ac05 100644 --- a/src/sage/typeset/character_art.py +++ b/src/sage/typeset/character_art.py @@ -150,6 +150,22 @@ def _repr_(self): return output + self._matrix[len(self._matrix) - 1] return output + def __format__(self, fmt): + r""" + Format ``self``. + + EXAMPLES:: + + sage: M = matrix([[1,2],[3,4]]) + sage: format(ascii_art(M)) + '[1 2]\n[3 4]' + sage: format(unicode_art(M)) # py2 + u'\u239b1 2\u239e\n\u239d3 4\u23a0' + sage: format(unicode_art(M)) # py3 + '\u239b1 2\u239e\n\u239d3 4\u23a0' + """ + return format(self._string_type(self), fmt) + def get_baseline(self): r""" Return the line where the baseline is, for example:: From f82683c27fda13c9fb6b19f19ca8b26ca04fbae4 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 6 Feb 2018 14:54:00 +0100 Subject: [PATCH 661/740] Use pyconfig.h from Python 3 to build subprocess32 --- build/pkgs/subprocess32/dependencies | 2 +- build/pkgs/subprocess32/fix_config.py | 48 +++++++++++++++++++++ build/pkgs/subprocess32/package-version.txt | 2 +- build/pkgs/subprocess32/spkg-install | 8 ++++ 4 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 build/pkgs/subprocess32/fix_config.py diff --git a/build/pkgs/subprocess32/dependencies b/build/pkgs/subprocess32/dependencies index d5dab729e18..3750e1891b6 100644 --- a/build/pkgs/subprocess32/dependencies +++ b/build/pkgs/subprocess32/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | pip python3 ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/subprocess32/fix_config.py b/build/pkgs/subprocess32/fix_config.py new file mode 100644 index 00000000000..e73c52f3140 --- /dev/null +++ b/build/pkgs/subprocess32/fix_config.py @@ -0,0 +1,48 @@ +# Fix build of subprocess32 +# +# subprocess32 is a backport from Python 3 to Python 2. But only the C +# sources were backported, not the configure script. This way, macros +# like HAVE_DIRFD which come from the Python 3 configure script are +# always undefined. This causes breakage on certain platforms. +# See upstream bug +# https://github.com/google/python-subprocess32/issues/40 +# +# In Sage, we fix this by using the actual pyconfig.h file from our +# Python 3 installation. +# +# This Python script should be run with the Python version where +# subprocess32 will eventually be installed. + + +import os +from sysconfig import get_path +from subprocess import check_output + + +# Path to the Python 3 includes +cmd = "from sysconfig import get_path; print(get_path('include'), end='')" +py3incdir = check_output(["python3", "-c", cmd]) + +# Path to the includes of the Python installation where subprocess32 +# will be installed +incdir = get_path("include") + + +# Create a fake "Python.h" file which includes "pyconfig.h" from +# Python 3 and then includes the real Python.h header +header = ''' +/* Include pyconfig.h from Python 3 */ +#include "{}/pyconfig.h" + +/* Make sure that the Python 2 version of pyconfig.h can also be included */ +#undef Py_PYCONFIG_H + +/* Include the real Python.h file */ +#include "{}/Python.h" +'''.format(py3incdir, incdir) + + +print("NOTE: Using Python 3 configuration to build subprocess32 for Python 2") + +with open("Python.h", "w") as f: + f.write(header) diff --git a/build/pkgs/subprocess32/package-version.txt b/build/pkgs/subprocess32/package-version.txt index 406ebcbd95f..e00541606dd 100644 --- a/build/pkgs/subprocess32/package-version.txt +++ b/build/pkgs/subprocess32/package-version.txt @@ -1 +1 @@ -3.2.7 +3.2.7.p0 diff --git a/build/pkgs/subprocess32/spkg-install b/build/pkgs/subprocess32/spkg-install index 058b1344dc2..d1734178f3a 100644 --- a/build/pkgs/subprocess32/spkg-install +++ b/build/pkgs/subprocess32/spkg-install @@ -1,3 +1,11 @@ +if [ "$SAGE_PYTHON3" = yes ]; then + echo "Skipping subprocess32 since it is not necessary on Python 3" + exit 0 +fi + cd src +# See fix_config.py for an explanation +python2 ../fix_config.py || exit $? + sdh_pip_install . From 4eef48755ba995a5084647f58fb0b0b2aaff22b7 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Wed, 7 Feb 2018 08:02:00 +0100 Subject: [PATCH 662/740] 24649: Clean up and documentation improvements with hyperbolic functions --- src/sage/functions/hyperbolic.py | 309 +++++++++++++------------------ 1 file changed, 129 insertions(+), 180 deletions(-) diff --git a/src/sage/functions/hyperbolic.py b/src/sage/functions/hyperbolic.py index f46bdca6bb5..931a4b41e44 100644 --- a/src/sage/functions/hyperbolic.py +++ b/src/sage/functions/hyperbolic.py @@ -1,78 +1,33 @@ """ Hyperbolic Functions -""" -from __future__ import division -from sage.symbolic.function import GinacFunction, BuiltinFunction -from sage.rings.infinity import Infinity -from sage.symbolic.expression import Expression -from sage.symbolic.constants import pi, I -from sage.rings.integer_ring import ZZ - -import math - - -class HyperbolicFunction(BuiltinFunction): - r""" - Abstract base class for the functions defined in this file. - - EXAMPLES:: - - sage: from sage.functions.hyperbolic import HyperbolicFunction - sage: f = HyperbolicFunction('foo', latex_name='\\foo', conversions={'mathematica':'Foo'},evalf_float=lambda x: 2*x) - sage: f(x) - foo(x) - sage: f(0.5r) - 1.0 - sage: latex(f(x)) - \foo\left(x\right) - sage: f(x)._mathematica_init_() - 'Foo[x]' - """ - def __init__(self, name, latex_name=None, conversions=None, - evalf_float=None): - """ - Note that subclasses of HyperbolicFunction should be instantiated only - once, since they inherit from BuiltinFunction which only uses the - name and class to check if a function was already registered. +The full set of hyperbolic and inverse hyperbolic functions is +available: - EXAMPLES:: + - hyperbolic sine: :class:`sinh() ` + - hyperbolic cosine: :class:`cosh() ` + - hyperbolic tangent: :class:`tanh() ` + - hyperbolic cotangent: :class:`coth() ` + - hyperbolic secant: :class:`sech() ` + - hyperbolic cosecant: :class:`csch() ` + - inverse hyperbolic sine: :class:`asinh() ` + - inverse hyperbolic cosine: :class:`acosh() ` + - inverse hyperbolic tangent: :class:`atanh() ` + - inverse hyperbolic cotangent: :class:`acoth() ` + - inverse hyperbolic secant: :class:`asech() ` + - inverse hyperbolic cosecant: :class:`acsch() ` - sage: from sage.functions.hyperbolic import HyperbolicFunction - sage: class Barh(HyperbolicFunction): - ....: def __init__(self): - ....: HyperbolicFunction.__init__(self, 'barh') - sage: barh = Barh() - sage: barh(x) - barh(x) - """ - self._evalf_float = evalf_float - BuiltinFunction.__init__(self, name, latex_name=latex_name, - conversions=conversions) +REFERENCES: - def _evalf_(self, x, parent=None, algorithm=None): - """ - EXAMPLES:: +- :wikipedia:`Hyperbolic function` - sage: from sage.functions.hyperbolic import HyperbolicFunction - sage: class Fooh(HyperbolicFunction): - ....: def __init__(self): - ....: HyperbolicFunction.__init__(self, 'fooh',evalf_float=lambda x: 2*x) - sage: fooh = Fooh() - sage: fooh(float(5)) - 10.0 - sage: fooh(0.5r) - 1.0 - sage: fooh(x).subs(x=.5r) - 1.0 - sage: fooh(x).n() - Traceback (most recent call last): - ... - AttributeError: 'sage.symbolic.expression.Expression' object has no attribute 'fooh' - """ - if parent is float: - return self._evalf_float(x) - return getattr(x, self.name())() +- :wikipedia:`Inverse hyperbolic functions` + +- R. Roy, F. W. J. Olver, Elementary Functions, http://dlmf.nist.gov/4 +""" +from __future__ import division + +from sage.symbolic.function import GinacFunction class Function_sinh(GinacFunction): @@ -101,12 +56,10 @@ def __init__(self): sage: sinh(arccosh(x),hold=True) sinh(arccosh(x)) - To then evaluate again, we currently must use Maxima via - :meth:`sage.symbolic.expression.Expression.simplify`:: + To then evaluate again, use the ``unhold`` method:: - sage: sinh(arccosh(x),hold=True).simplify() + sage: sinh(arccosh(x),hold=True).unhold() sqrt(x + 1)*sqrt(x - 1) - """ GinacFunction.__init__(self, "sinh", latex_name=r"\sinh") @@ -139,12 +92,10 @@ def __init__(self): sage: cosh(arcsinh(x),hold=True) cosh(arcsinh(x)) - To then evaluate again, we currently must use Maxima via - :meth:`sage.symbolic.expression.Expression.simplify`:: + To then evaluate again, use the ``unhold`` method:: - sage: cosh(arcsinh(x),hold=True).simplify() + sage: cosh(arcsinh(x),hold=True).unhold() sqrt(x^2 + 1) - """ GinacFunction.__init__(self, "cosh", latex_name=r"\cosh") @@ -185,10 +136,9 @@ def __init__(self): sage: tanh(arcsinh(x),hold=True) tanh(arcsinh(x)) - To then evaluate again, we currently must use Maxima via - :meth:`sage.symbolic.expression.Expression.simplify`:: + To then evaluate again, use the ``unhold`` method:: - sage: tanh(arcsinh(x),hold=True).simplify() + sage: tanh(arcsinh(x),hold=True).unhold() x/sqrt(x^2 + 1) TESTS:: @@ -251,11 +201,11 @@ def __init__(self): sage: diff(coth(x), x) -1/sinh(x)^2 sage: latex(coth(x)) - \operatorname{coth}\left(x\right) + \coth\left(x\right) sage: coth(x)._sympy_() coth(x) """ - GinacFunction.__init__(self, "coth", latex_name=r"\operatorname{coth}") + GinacFunction.__init__(self, "coth", latex_name=r"\coth") def _eval_numpy_(self, x): """ @@ -355,11 +305,11 @@ def __init__(self): sage: diff(csch(x), x) -coth(x)*csch(x) sage: latex(csch(x)) - {\rm csch}\left(x\right) + \operatorname{csch}\left(x\right) sage: csch(x)._sympy_() csch(x) """ - GinacFunction.__init__(self, "csch", latex_name=r"{\rm csch}") + GinacFunction.__init__(self, "csch", latex_name=r"\operatorname{csch}") def _eval_numpy_(self, x): """ @@ -387,54 +337,54 @@ def __init__(self): EXAMPLES:: - sage: arcsinh + sage: asinh arcsinh - sage: arcsinh(0.5) + sage: asinh(0.5) 0.481211825059603 - sage: arcsinh(1/2) + sage: asinh(1/2) arcsinh(1/2) - sage: arcsinh(1 + I*1.0) + sage: asinh(1 + I*1.0) 1.06127506190504 + 0.666239432492515*I To prevent automatic evaluation use the ``hold`` argument:: - sage: arcsinh(-2,hold=True) + sage: asinh(-2,hold=True) arcsinh(-2) - To then evaluate again, we currently must use Maxima via - :meth:`sage.symbolic.expression.Expression.simplify`:: + To then evaluate again, use the ``unhold`` method:: - sage: arcsinh(-2,hold=True).simplify() + sage: asinh(-2,hold=True).unhold() -arcsinh(2) - ``conjugate(arcsinh(x))==arcsinh(conjugate(x))`` unless on the branch + ``conjugate(asinh(x))==asinh(conjugate(x))`` unless on the branch cuts which run along the imaginary axis outside the interval [-I, +I].:: - sage: conjugate(arcsinh(x)) + sage: conjugate(asinh(x)) conjugate(arcsinh(x)) sage: var('y', domain='positive') y - sage: conjugate(arcsinh(y)) + sage: conjugate(asinh(y)) arcsinh(y) - sage: conjugate(arcsinh(y+I)) + sage: conjugate(asinh(y+I)) conjugate(arcsinh(y + I)) - sage: conjugate(arcsinh(1/16)) + sage: conjugate(asinh(1/16)) arcsinh(1/16) - sage: conjugate(arcsinh(I/2)) + sage: conjugate(asinh(I/2)) arcsinh(-1/2*I) - sage: conjugate(arcsinh(2*I)) + sage: conjugate(asinh(2*I)) conjugate(arcsinh(2*I)) TESTS:: - sage: arcsinh(x).operator() + sage: asinh(x).operator() arcsinh - sage: latex(arcsinh(x)) - {\rm arcsinh}\left(x\right) + sage: latex(asinh(x)) + \operatorname{arsinh}\left(x\right) sage: asinh(x)._sympy_() asinh(x) """ - GinacFunction.__init__(self, "arcsinh", latex_name=r"{\rm arcsinh}", + GinacFunction.__init__(self, "arcsinh", + latex_name=r"\operatorname{arsinh}", conversions=dict(maxima='asinh', sympy='asinh', fricas='asinh', giac='asinh')) @@ -448,15 +398,15 @@ def __init__(self): EXAMPLES:: - sage: arccosh(1/2) + sage: acosh(1/2) arccosh(1/2) - sage: arccosh(1 + I*1.0) + sage: acosh(1 + I*1.0) 1.06127506190504 + 0.904556894302381*I - sage: float(arccosh(2)) + sage: float(acosh(2)) 1.3169578969248168 - sage: cosh(float(arccosh(2))) + sage: cosh(float(acosh(2))) 2.0 - sage: arccosh(complex(1, 2)) # abs tol 1e-15 + sage: acosh(complex(1, 2)) # abs tol 1e-15 (1.5285709194809982+1.1437177404024204j) .. warning:: @@ -468,58 +418,58 @@ def __init__(self): :: - sage: arccosh(0.5) + sage: acosh(0.5) NaN - sage: arccosh(1/2) + sage: acosh(1/2) arccosh(1/2) - sage: arccosh(1/2).n() + sage: acosh(1/2).n() NaN - sage: arccosh(CC(0.5)) + sage: acosh(CC(0.5)) 1.04719755119660*I - sage: arccosh(0) + sage: acosh(0) 1/2*I*pi - sage: arccosh(-1) + sage: acosh(-1) I*pi To prevent automatic evaluation use the ``hold`` argument:: - sage: arccosh(-1,hold=True) + sage: acosh(-1,hold=True) arccosh(-1) - To then evaluate again, we currently must use Maxima via - :meth:`sage.symbolic.expression.Expression.simplify`:: + To then evaluate again, use the ``unhold`` method:: - sage: arccosh(-1,hold=True).simplify() + sage: acosh(-1,hold=True).unhold() I*pi ``conjugate(arccosh(x))==arccosh(conjugate(x))`` unless on the branch cut which runs along the real axis from +1 to -inf.:: - sage: conjugate(arccosh(x)) + sage: conjugate(acosh(x)) conjugate(arccosh(x)) sage: var('y', domain='positive') y - sage: conjugate(arccosh(y)) + sage: conjugate(acosh(y)) conjugate(arccosh(y)) - sage: conjugate(arccosh(y+I)) + sage: conjugate(acosh(y+I)) conjugate(arccosh(y + I)) - sage: conjugate(arccosh(1/16)) + sage: conjugate(acosh(1/16)) conjugate(arccosh(1/16)) - sage: conjugate(arccosh(2)) + sage: conjugate(acosh(2)) arccosh(2) - sage: conjugate(arccosh(I/2)) + sage: conjugate(acosh(I/2)) arccosh(-1/2*I) TESTS:: - sage: arccosh(x).operator() + sage: acosh(x).operator() arccosh - sage: latex(arccosh(x)) - {\rm arccosh}\left(x\right) + sage: latex(acosh(x)) + \operatorname{arcosh}\left(x\right) sage: acosh(x)._sympy_() acosh(x) """ - GinacFunction.__init__(self, "arccosh", latex_name=r"{\rm arccosh}", + GinacFunction.__init__(self, "arccosh", + latex_name=r"\operatorname{arcosh}", conversions=dict(maxima='acosh', sympy='acosh', fricas='acosh', giac='acosh')) @@ -533,52 +483,52 @@ def __init__(self): EXAMPLES:: - sage: arctanh(0.5) + sage: atanh(0.5) 0.549306144334055 - sage: arctanh(1/2) + sage: atanh(1/2) 1/2*log(3) - sage: arctanh(1 + I*1.0) + sage: atanh(1 + I*1.0) 0.402359478108525 + 1.01722196789785*I To prevent automatic evaluation use the ``hold`` argument:: - sage: arctanh(-1/2,hold=True) + sage: atanh(-1/2,hold=True) arctanh(-1/2) - To then evaluate again, we currently must use Maxima via - :meth:`sage.symbolic.expression.Expression.simplify`:: + To then evaluate again, use the ``unhold`` method:: - sage: arctanh(-1/2,hold=True).simplify() + sage: atanh(-1/2,hold=True).unhold() -1/2*log(3) ``conjugate(arctanh(x))==arctanh(conjugate(x))`` unless on the branch cuts which run along the real axis outside the interval [-1, +1].:: - sage: conjugate(arctanh(x)) + sage: conjugate(atanh(x)) conjugate(arctanh(x)) sage: var('y', domain='positive') y - sage: conjugate(arctanh(y)) + sage: conjugate(atanh(y)) conjugate(arctanh(y)) - sage: conjugate(arctanh(y+I)) + sage: conjugate(atanh(y+I)) conjugate(arctanh(y + I)) - sage: conjugate(arctanh(1/16)) + sage: conjugate(atanh(1/16)) 1/2*log(17/15) - sage: conjugate(arctanh(I/2)) + sage: conjugate(atanh(I/2)) arctanh(-1/2*I) - sage: conjugate(arctanh(-2*I)) + sage: conjugate(atanh(-2*I)) arctanh(2*I) TESTS:: - sage: arctanh(x).operator() + sage: atanh(x).operator() arctanh - sage: latex(arctanh(x)) - {\rm arctanh}\left(x\right) + sage: latex(atanh(x)) + \operatorname{artanh}\left(x\right) sage: atanh(x)._sympy_() atanh(x) """ - GinacFunction.__init__(self, "arctanh", latex_name=r"{\rm arctanh}", + GinacFunction.__init__(self, "arctanh", + latex_name=r"\operatorname{artanh}", conversions=dict(maxima='atanh', sympy='atanh', fricas='atanh', giac='atanh')) @@ -592,13 +542,13 @@ def __init__(self): EXAMPLES:: - sage: arccoth(2.0) + sage: acoth(2.0) 0.549306144334055 - sage: arccoth(2) + sage: acoth(2) 1/2*log(3) - sage: arccoth(1 + I*1.0) + sage: acoth(1 + I*1.0) 0.402359478108525 - 0.553574358897045*I - sage: arccoth(2).n(200) + sage: acoth(2).n(200) 0.54930614433405484569762261846126285232374527891137472586735 sage: bool(diff(acoth(x), x) == diff(atanh(x), x)) @@ -606,30 +556,27 @@ def __init__(self): sage: diff(acoth(x), x) -1/(x^2 - 1) - Using first the `.n(53)` method is slightly more precise than - converting directly to a ``float``:: - - sage: float(arccoth(2)) # abs tol 1e-16 - 0.5493061443340548 - sage: float(arccoth(2).n(53)) # Correct result to 53 bits + sage: float(acoth(2)) + 0.5493061443340549 + sage: float(acoth(2).n(53)) # Correct result to 53 bits 0.5493061443340549 - sage: float(arccoth(2).n(100)) # Compute 100 bits and then round to 53 + sage: float(acoth(2).n(100)) # Compute 100 bits and then round to 53 0.5493061443340549 TESTS:: - sage: latex(arccoth(x)) - \operatorname{arccoth}\left(x\right) + sage: latex(acoth(x)) + \operatorname{arcoth}\left(x\right) sage: acoth(x)._sympy_() acoth(x) Check if :trac:`23636` is fixed:: - sage: arccoth(float(1.1)) + sage: acoth(float(1.1)) 1.5222612188617113 """ GinacFunction.__init__(self, "arccoth", - latex_name=r"\operatorname{arccoth}", + latex_name=r"\operatorname{arcoth}", conversions=dict(maxima='acoth', sympy='acoth', fricas='acoth')) def _eval_numpy_(self, x): @@ -638,7 +585,7 @@ def _eval_numpy_(self, x): sage: import numpy sage: a = numpy.arange(2,5) - sage: arccoth(a) + sage: acoth(a) array([ 0.54930614, 0.34657359, 0.25541281]) """ return arctanh(1.0 / x) @@ -653,26 +600,26 @@ def __init__(self): EXAMPLES:: - sage: arcsech(0.5) + sage: asech(0.5) 1.31695789692482 - sage: arcsech(1/2) + sage: asech(1/2) arcsech(1/2) - sage: arcsech(1 + I*1.0) + sage: asech(1 + I*1.0) 0.530637530952518 - 1.11851787964371*I - sage: arcsech(1/2).n(200) + sage: asech(1/2).n(200) 1.3169578969248167086250463473079684440269819714675164797685 - sage: float(arcsech(1/2)) + sage: float(asech(1/2)) 1.3169578969248168 sage: diff(asech(x), x) -1/(sqrt(-x^2 + 1)*x) - sage: latex(arcsech(x)) - \operatorname{arcsech}\left(x\right) + sage: latex(asech(x)) + \operatorname{arsech}\left(x\right) sage: asech(x)._sympy_() asech(x) """ GinacFunction.__init__(self, "arcsech", - latex_name=r"\operatorname{arcsech}", + latex_name=r"\operatorname{arsech}", conversions=dict(maxima='asech', sympy='asech', fricas='asech')) def _eval_numpy_(self, x): @@ -681,7 +628,7 @@ def _eval_numpy_(self, x): sage: import numpy sage: a = numpy.linspace(0,1,3) - sage: arcsech(a) + sage: asech(a) doctest:...: RuntimeWarning: divide by zero encountered in ...divide array([ inf, 1.3169579, 0. ]) """ @@ -697,32 +644,34 @@ def __init__(self): EXAMPLES:: - sage: arccsch(2.0) + sage: acsch(2.0) 0.481211825059603 - sage: arccsch(2) + sage: acsch(2) arccsch(2) - sage: arccsch(1 + I*1.0) + sage: acsch(1 + I*1.0) 0.530637530952518 - 0.452278447151191*I - sage: arccsch(1).n(200) + sage: acsch(1).n(200) 0.88137358701954302523260932497979230902816032826163541075330 - sage: float(arccsch(1)) + sage: float(acsch(1)) 0.881373587019543 sage: diff(acsch(x), x) -1/(sqrt(x^2 + 1)*x) - sage: latex(arccsch(x)) - \operatorname{arccsch}\left(x\right) + sage: latex(acsch(x)) + \operatorname{arcsch}\left(x\right) TESTS: Check if :trac:`20818` is fixed:: - sage: arccsch(float(0.1)) + sage: acsch(float(0.1)) 2.99822295029797 + sage: acsch(x)._sympy_() + acsch(x) """ GinacFunction.__init__(self, "arccsch", - latex_name=r"\operatorname{arccsch}", - conversions=dict(maxima='acsch', sympy='asech', fricas='acsch')) + latex_name=r"\operatorname{arcsch}", + conversions=dict(maxima='acsch', sympy='acsch', fricas='acsch')) def _eval_numpy_(self, x): """ @@ -730,7 +679,7 @@ def _eval_numpy_(self, x): sage: import numpy sage: a = numpy.linspace(0,1,3) - sage: arccsch(a) + sage: acsch(a) doctest:...: RuntimeWarning: divide by zero encountered in ...divide array([ inf, 1.44363548, 0.88137359]) """ From 350fdd62150ec152a413b09514f1f2fe52b60e58 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 7 Feb 2018 10:36:06 +0100 Subject: [PATCH 663/740] Disable pexpect echo when starting the interface --- src/sage/interfaces/expect.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/sage/interfaces/expect.py b/src/sage/interfaces/expect.py index 3960b6eed00..a1a9c0607ff 100644 --- a/src/sage/interfaces/expect.py +++ b/src/sage/interfaces/expect.py @@ -482,7 +482,8 @@ def _start(self, alt_message=None, block_during_init=True): timeout=None, # no timeout env=pexpect_env, name=self._repr_(), - # work around python #1652 + echo=self._terminal_echo, + # Work around https://bugs.python.org/issue1652 preexec_fn=lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL), quit_string=self._quit_string()) except (ExceptionPexpect, pexpect.EOF) as e: @@ -507,13 +508,6 @@ def _start(self, alt_message=None, block_during_init=True): raise RuntimeError("unable to start %s" % self.name()) self._expect.timeout = None - # Calling tcsetattr earlier exposes bugs in various pty - # implementations, see trac #16474. Since we haven't - # **written** anything so far it is safe to wait with - # switching echo off until now. - if not self._terminal_echo: - self._expect.setecho(0) - with gc_disabled(): if block_during_init: for X in self.__init_code: From 63a5abe737d0d5446a7322c41c306fcafcaf43be Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 7 Feb 2018 11:02:20 +0100 Subject: [PATCH 664/740] Use py_scalar_to_element() to factor non-Sage numbers --- src/sage/arith/misc.py | 57 +++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 4438ec3955b..d2693c747ee 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -25,7 +25,7 @@ from sage.libs.pari.all import pari import sage.libs.flint.arith as flint_arith -from sage.structure.element import parent +from sage.structure.element import parent, Element from sage.structure.coerce import py_scalar_to_element from sage.rings.rational_field import QQ @@ -2279,25 +2279,48 @@ def factor(n, proof=None, int_=False, algorithm='pari', verbose=0, **kwds): sage: [p^e for p,e in f] [4, 3, 5, 7] + We can factor Python and numpy numbers:: + + sage: factor(math.pi) + 3.141592653589793 + sage: import numpy + sage: factor(numpy.int8(30)) + 2 * 3 * 5 + + TESTS:: + + sage: factor(Mod(4, 100)) + Traceback (most recent call last): + ... + TypeError: unable to factor 4 + sage: factor("xyz") + Traceback (most recent call last): + ... + TypeError: unable to factor 'xyz' """ - if isinstance(n, integer_types): - n = ZZ(n) + try: + m = n.factor + except AttributeError: + # Maybe n is not a Sage Element, try to convert it + e = py_scalar_to_element(n) + if e is n: + # Either n was a Sage Element without a factor() method + # or we cannot it convert it to Sage + raise TypeError("unable to factor {!r}".format(n)) + n = e + m = n.factor if isinstance(n, Integer): - return n.factor(proof=proof, algorithm=algorithm, - int_ = int_, verbose=verbose) - else: - # e.g. n = x**2 + y**2 + 2*x*y - try: - return n.factor(proof=proof, **kwds) - except AttributeError: - raise TypeError("unable to factor n") - except TypeError: - # Just in case factor method doesn't have a proof option. - try: - return n.factor(**kwds) - except AttributeError: - raise TypeError("unable to factor n") + return m(proof=proof, algorithm=algorithm, int_=int_, + verbose=verbose, **kwds) + + # Polynomial or other factorable object + try: + return m(proof=proof, **kwds) + except TypeError: + # Maybe the factor() method doesn't have a proof option + return m(**kwds) + def radical(n, *args, **kwds): """ From 185d13695b0180cd483c4e67e26b43dccc6e1d66 Mon Sep 17 00:00:00 2001 From: Madison Van Dyk Date: Wed, 7 Feb 2018 09:01:15 -0500 Subject: [PATCH 665/740] Faster doctest for order-free isogenies that is under one second --- src/sage/schemes/elliptic_curves/ell_curve_isogeny.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index 677d4ad3ef3..18a408d7960 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -1859,12 +1859,12 @@ def __init_from_kernel_list(self, kernel_gens): to P.order(), since such calls involve factoring the group order which could take a long time. :: - sage: p = 12 * next_prime(2^246) * next_prime(2^247) - 1 + sage: p = 12 * next_prime(2^180) * next_prime(2^194) - 1 sage: F = FiniteField(p, proof=False) sage: E = EllipticCurve([F(1), F(0)]) sage: P = E(0).division_points(3)[1] sage: EllipticCurveIsogeny(E, P) - Isogeny of degree 3 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 306880369490263300313736534077587420520310191816537324014058565759012544413001947737908555848973232913399393291567882031064458001335811229593024848691 to Elliptic Curve defined by y^2 = x^3 + 138339744732679989316868434850977055074939617746881563766525552777337080950210098011490359468980566166703728781690013462872744031856023553225684698214*x + 144492060973761695990422770559924271262401033407979769478362459183553396187521975467910654355724411241272805279344391492601859775119243705193259368622 over Finite Field of size 306880369490263300313736534077587420520310191816537324014058565759012544413001947737908555848973232913399393291567882031064458001335811229593024848691 + Isogeny of degree 3 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 461742260113997803268895001173557974278278194575766957660028841364655249961609425998827452443620996655395008156411 to Elliptic Curve defined by y^2 = x^3 + 80816485163488178037199320944019099858815874115367810482828676054000067654558381377552245721755005198633191074893*x + 301497584865165444049833326660609767433467459033532853758006118022998267706948164646650354324860226263546558337993 over Finite Field of size 461742260113997803268895001173557974278278194575766957660028841364655249961609425998827452443620996655395008156411 """ if self.__check : From b393ac0ad5172cc8dfe28fdb315a8ef1ca95ed6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 7 Feb 2018 15:48:39 +0100 Subject: [PATCH 666/740] a little more code in Tamari interval-posets --- src/sage/combinat/interval_posets.py | 62 +++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/interval_posets.py b/src/sage/combinat/interval_posets.py index 6777c160d43..6f09e97c0d9 100644 --- a/src/sage/combinat/interval_posets.py +++ b/src/sage/combinat/interval_posets.py @@ -2290,10 +2290,11 @@ def extract_tree(x, y, tilt, common): def is_new(self): """ - Return ``True`` if ``self`` is a new Tamari interval. + Return whether ``self`` is a new Tamari interval. Here 'new' means that the interval is not contained in any facet of the associahedron. + This condition is invariant under complementation. They have been considered in section 9 of [ChapTamari08]_. @@ -2315,7 +2316,7 @@ def is_new(self): def is_simple(self): """ - Return ``True`` if ``self`` is a simple Tamari interval. + Return whether ``self`` is a simple Tamari interval. Here 'simple' means that the interval contains a unique binary tree. @@ -2338,9 +2339,10 @@ def is_simple(self): def is_synchronized(self): """ - Return ``True`` if ``self`` is a synchronized Tamari interval. + Return whether ``self`` is a synchronized Tamari interval. This means that the upper and lower binary trees have the same canopee. + This condition is invariant under complementation. This has been considered in [FPR15]_. The numbers of synchronized intervals are given by the sequence :oeis:`A000139`. @@ -2357,12 +2359,14 @@ def is_synchronized(self): def is_modern(self): r""" - Return ``True`` if ``self`` is a modern Tamari interval. + Return whether ``self`` is a modern Tamari interval. This is defined by exclusion of a simple pattern in the Hasse diagram, namely there is no configuration ``y --> x <-- z`` with `1 \leq y < x < z \leq n`. + This condition is invariant under complementation. + .. SEEALSO:: :meth:`is_new` EXAMPLES:: @@ -2380,12 +2384,14 @@ def is_modern(self): def is_exceptional(self): r""" - Return ``True`` if ``self`` is an exceptional Tamari interval. + Return whether ``self`` is an exceptional Tamari interval. This is defined by exclusion of a simple pattern in the Hasse diagram, namely there is no configuration ``y <-- x --> z`` with `1 \leq y < x < z \leq n`. + This condition is invariant under complementation. + EXAMPLES:: sage: len([T for T in TamariIntervalPosets(3) @@ -2400,6 +2406,42 @@ def is_exceptional(self): return False return True + def is_dexter(self): + r""" + Return whether ``self`` is a dexter Tamari interval. + + This is defined by an exclusion pattern in the Hasse diagram. + See the code for the exact description. + + This condition is not invariant under complementation. + + EXAMPLES:: + + sage: len([T for T in TamariIntervalPosets(3) if T.is_dexter()]) + 12 + """ + G = self.poset().hasse_diagram() + for x in G: + nx = list(G.neighbors_out(x)) + nx.append(x) + y = min(nx) + if y < x and any(z > x for z in G.neighbors_out(y)): + return False + return True + + def is_connected(self): + """ + Return whether ``self`` is a connected Tamari interval. + + This means that the Hasse diagram is connected. + + EXAMPLES:: + + sage: len([T for T in TamariIntervalPosets(3) if T.is_connected()]) + 8 + """ + return self.poset().is_connected() + # Abstract class to serve as a Factory ; no instances are created. class TamariIntervalPosets(UniqueRepresentation, Parent): @@ -3230,23 +3272,23 @@ def __iter__(self): """ n = self._size if n <= 1: - yield TamariIntervalPoset(n, []) + yield TamariIntervalPoset(n, [], check=False) return for tip in TamariIntervalPosets(n - 1): - new_tip = TamariIntervalPoset(n, tip._cover_relations) + new_tip = TamariIntervalPoset(n, tip._cover_relations, check=False) yield new_tip # we have added an extra vertex but no relations # adding a decreasing relation n>>m2 with m2>n if not new_tip.le(m, n): - new_tip = TamariIntervalPoset(n, new_tip._cover_relations + ((m, n),)) + new_tip = TamariIntervalPoset(n, new_tip._cover_relations + ((m, n),), check=False) yield new_tip else: continue @@ -3254,7 +3296,7 @@ def __iter__(self): # further adding a decreasing relation n>>m2 with m2 Date: Wed, 7 Feb 2018 20:47:28 +0100 Subject: [PATCH 667/740] Minor fixes --- src/sage/combinat/partitions_c.cc | 36 +++++-------------------------- 1 file changed, 5 insertions(+), 31 deletions(-) diff --git a/src/sage/combinat/partitions_c.cc b/src/sage/combinat/partitions_c.cc index 44d2c51b9c5..7b0ee5bd1ae 100644 --- a/src/sage/combinat/partitions_c.cc +++ b/src/sage/combinat/partitions_c.cc @@ -145,7 +145,6 @@ const unsigned int long_double_precision = (LDBL_MANT_DIG == 106) ? double_preci // Note: On many systems double_precision = long_double_precision. This is OK, as // the long double stage of the computation will just be skipped. - // Second, some constants that control the precision at which we compute. // When we compute the terms of the Rademacher series, we start by computing with @@ -199,10 +198,7 @@ mpfr_t mptemp; * ****************************************************************************/ - // A listing of the main functions, in "conceptual" order. - - void part(mpz_t answer, unsigned int n); static void mp_t(mpfr_t result, unsigned int n); // Compute t(n,N) for an N large enough, and with a high enough precision, so that @@ -364,7 +360,6 @@ static unsigned int compute_current_precision(unsigned int n, unsigned int N, un // extra should probably have been set by a call to compute_extra_precision() // before this function was called. - // n = the number for which we are computing p(n) // N = the number of terms that have been computed so far @@ -390,7 +385,6 @@ static unsigned int compute_current_precision(unsigned int n, unsigned int N, un mpfr_sqrt_ui(t1, N, MPFR_RNDF); // t1 = sqrt(N) mpfr_div(error, error, t1, MPFR_RNDF); // error = A/sqrt(N) - mpfr_sqrt_ui(t1, n, MPFR_RNDF); // t1 = sqrt(n) mpfr_mul(t1, t1, C, MPFR_RNDF); // t1 = C * sqrt(n) mpfr_div_ui(t1, t1, N, MPFR_RNDF); // t1 = C * sqrt(n) / N @@ -415,7 +409,6 @@ static unsigned int compute_current_precision(unsigned int n, unsigned int N, un // large enough so that the accumulated errors // in all of the computations that we make // are not big enough to matter. - if (debug) { cout << "Error seems to be: "; mpfr_out_str(stdout, 10, 0, error, round_mode); @@ -466,11 +459,9 @@ static int compute_extra_precision(unsigned int n, double error = .25) { // then the sum will be within .5 of the correct (integer) answer, and we // can correctly find it by rounding. unsigned int k = 1; - for ( ; compute_current_precision(n,k,0) > double_precision; k += 100) { - } + while (compute_current_precision(n, k, 0) > double_precision) k += 100; + while (compute_remainder(n, k) > error) k += 100; - for ( ; compute_remainder(n, k) > error ; k += 100) { - } if (debug_precision) { cout << "To compute p(" << n << ") we will add up approximately " << k << " terms from the Rachemacher series." << endl; } @@ -487,10 +478,6 @@ static int compute_extra_precision(unsigned int n, double error = .25) { // be safe, we use 5 extra bits. // (Extensive trial and error means compiling this file to get // a.out and then running './a.out testforever' for a few hours.) - - - - return bits; } @@ -534,7 +521,7 @@ static void initialize_globals(mp_prec_t prec, unsigned int n) { // corresponds roughly to losing 1 bit of precision). mpfr_prec_t p = prec; if (p < long_double_precision) p = long_double_precision; - p += 20; + p += 10; // Global constants mpfr_init2(mp_sqrt2, p); @@ -667,6 +654,7 @@ static void mp_f(mpfr_t result, unsigned int k) { mpfr_sub(result, result, mptemp, round_mode); // result = RESULT! } + // call the following when 4k < sqrt(UINT_MAX) // TODO: maybe a faster version of this can be written without using mpq_t, @@ -675,7 +663,6 @@ static void mp_f(mpfr_t result, unsigned int k) { // The actual value of the cosine that we compute using s(h,k) // only depends on {s(h,k)/2}, that is, the fractional // part of s(h,k)/2. It may be possible to make use of this somehow. - static void q_s(mpq_t result, unsigned int h, unsigned int k) { if (k < 3) { mpq_set_ui(result, 0, 1); @@ -698,7 +685,6 @@ static void q_s(mpq_t result, unsigned int h, unsigned int k) { // it seems like there might not be much speed benefit to this, and putting in too // many seems to slow things down a little. - // if h = 2 and k is odd, we have // s(h,k) = (k-1)*(k-5)/24k //if(h == 2 && k > 5 && k % 2 == 1) { @@ -742,9 +728,6 @@ static void q_s(mpq_t result, unsigned int h, unsigned int k) { //} */ - - - mpq_set_ui(result, 0, 1); // result = 0 int r1 = k; @@ -863,10 +846,8 @@ static void mp_t(mpfr_t result, unsigned int n) { // More specifically, it computes t(n,N) to within 1/2 of p(n), and then // we can find p(n) by rounding. - // NOTE: result should NOT have been initialized when this is called, // as we initialize it to the proper precision in this function. - double error = .25; int extra = compute_extra_precision(n, error); @@ -882,7 +863,6 @@ static void mp_t(mpfr_t result, unsigned int n) { initialize_globals(initial_precision, n); // Now that we have the precision information, we initialize some constants // that will be used throughout, and also set the precision of the "temp" // variables that are used in individual functions. - unsigned int current_precision = initial_precision; unsigned int new_precision; @@ -893,7 +873,6 @@ static void mp_t(mpfr_t result, unsigned int n) { // that only involves doubles. unsigned int k = 1; // (k holds the index of the summand that we are computing.) for (k = 1; current_precision > level_two_precision; k++) { - mpfr_sqrt_ui(t1, k, round_mode); // t1 = sqrt(k) mp_a(t2, n, k); // t2 = A_k(n) @@ -1422,7 +1401,6 @@ int test(bool longtest, bool forever) { cout << " OK." << endl; - for (int i = 0; i < 10; i++) { n = int(100000000 * double(rand())/double(RAND_MAX) + 1) + 1000000000; n = n - (n % 385) + 369; @@ -1434,12 +1412,9 @@ int test(bool longtest, bool forever) { } cout << " OK." << endl; } - } - if (forever) { - while(1) { - + while (forever) { for (int i = 0; i < 100; i++) { n = int(900000 * double(rand())/double(RAND_MAX) + 1) + 100000; n = n - (n % 385) + 369; @@ -1498,7 +1473,6 @@ int test(bool longtest, bool forever) { } cout << " OK." << endl; } - } } mpz_clear(expected_value); From 1e5c7027942edc9573dbbb550a83a8c012cb7825 Mon Sep 17 00:00:00 2001 From: Florent Hivert Date: Wed, 7 Feb 2018 21:21:03 +0100 Subject: [PATCH 668/740] Fix wrong Peirce summand --- .../finite_dimensional_algebras_with_basis.py | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/sage/categories/finite_dimensional_algebras_with_basis.py b/src/sage/categories/finite_dimensional_algebras_with_basis.py index 03430b14930..368c47fea2c 100644 --- a/src/sage/categories/finite_dimensional_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_algebras_with_basis.py @@ -793,11 +793,29 @@ def peirce_summand(self, ei, ej): sage: e = A4.central_orthogonal_idempotents()[2] sage: A4.peirce_summand(e, e) Free module generated by {0, 1, 2, 3} over Rational Field + + We check that idempotent is the basis for some 1-dimensional + peirce summand:: + + sage: from sage.monoids.hecke_monoid import HeckeMonoid + sage: M = HeckeMonoid(SymmetricGroup(4)) + sage: A = M.algebra(QQ) + sage: e = A.orthogonal_idempotents_central_mod_radical()[0] + sage: len(e) + 1 + sage: e.coefficient(M.one()) + 0 + sage: S = A.peirce_summand(e, e); S + Free module generated by {0} over Rational Field + sage: f = S.basis()[0].lift() + sage: e == f + True """ B = self.basis() phi = self.module_morphism(on_basis=lambda k: ei * B[k] * ej, codomain=self, triangular='lower') - ideal = phi.matrix().image() + ideal = phi.matrix(side='right').image() + return self.submodule([self.from_vector(v) for v in ideal.basis()], already_echelonized=True) From 97d235a4c973ef20cea57a1766dec2200610e2f3 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 7 Feb 2018 16:40:40 -0600 Subject: [PATCH 669/740] Fixing typo type -> matrix in coxeter_matrix(). --- src/sage/groups/artin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/groups/artin.py b/src/sage/groups/artin.py index 64bdd9c2585..5d7ef4d21d1 100644 --- a/src/sage/groups/artin.py +++ b/src/sage/groups/artin.py @@ -529,7 +529,7 @@ def coxeter_type(self): def coxeter_matrix(self): """ - Return the Coxeter type of ``self``. + Return the Coxeter matrix of ``self``. EXAMPLES:: From 7e8e805bdd8fa19aa94ccbc11cab6292640a39d2 Mon Sep 17 00:00:00 2001 From: Florent Hivert Date: Thu, 8 Feb 2018 00:35:24 +0100 Subject: [PATCH 670/740] Travis fagility comment --- .../categories/finite_dimensional_algebras_with_basis.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/sage/categories/finite_dimensional_algebras_with_basis.py b/src/sage/categories/finite_dimensional_algebras_with_basis.py index 368c47fea2c..910c07f29ba 100644 --- a/src/sage/categories/finite_dimensional_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_algebras_with_basis.py @@ -801,14 +801,10 @@ def peirce_summand(self, ei, ej): sage: M = HeckeMonoid(SymmetricGroup(4)) sage: A = M.algebra(QQ) sage: e = A.orthogonal_idempotents_central_mod_radical()[0] - sage: len(e) - 1 - sage: e.coefficient(M.one()) - 0 sage: S = A.peirce_summand(e, e); S Free module generated by {0} over Rational Field sage: f = S.basis()[0].lift() - sage: e == f + sage: e / e.leading_coefficient() == f / f.leading_coefficient() True """ B = self.basis() From 14faeb489346c222af3722f2db3dd796c29abffa Mon Sep 17 00:00:00 2001 From: Kevin Lui Date: Thu, 8 Feb 2018 01:22:36 +0000 Subject: [PATCH 671/740] implemented rational_torsion_order as an alias to rational_torsion_subgroup().order --- src/sage/modular/abvar/abvar.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/sage/modular/abvar/abvar.py b/src/sage/modular/abvar/abvar.py index 2288b349b41..4229308603a 100644 --- a/src/sage/modular/abvar/abvar.py +++ b/src/sage/modular/abvar/abvar.py @@ -2231,6 +2231,35 @@ def _ambient_hecke_matrix_on_modular_symbols(self, n): self.__ambient_hecke_matrix_on_modular_symbols[n] = T return T + def rational_torsion_order(self, proof=True): + """ + Return the order of the rational torsion subgroup of this modular + abelian variety. + + This function is really an alias for + :meth:`~sage.modular.abvar.torsion_subgroup.RationalTorsionSubgroup.order` + See the docstring there for a more in-depth reference and more + interesting examples. + + INPUT: + + - ``proof`` -- a boolean (default: True) + + OUTPUT: + + The order of the rational torsion subgroup of this modular abelian + variety. + + + EXAMPLES:: + + sage: J0(11).rational_torsion_subgroup().order() + 5 + sage: J0(11).rational_torsion_order() + 5 + """ + return self.rational_torsion_subgroup().order(proof=proof) + def number_of_rational_points(self): """ Return the number of rational points of this modular abelian variety. From 926ff9b5a81a114433221c2ac9e9d4be6e70d0d9 Mon Sep 17 00:00:00 2001 From: Kevin Lui Date: Thu, 8 Feb 2018 01:56:04 +0000 Subject: [PATCH 672/740] fixed trailing whitespace --- src/sage/schemes/elliptic_curves/ell_curve_isogeny.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index 18a408d7960..450856f8d29 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -1859,7 +1859,7 @@ def __init_from_kernel_list(self, kernel_gens): to P.order(), since such calls involve factoring the group order which could take a long time. :: - sage: p = 12 * next_prime(2^180) * next_prime(2^194) - 1 + sage: p = 12 * next_prime(2^180) * next_prime(2^194) - 1 sage: F = FiniteField(p, proof=False) sage: E = EllipticCurve([F(1), F(0)]) sage: P = E(0).division_points(3)[1] From b7464ad071c3c7fef44beea071f7414800794997 Mon Sep 17 00:00:00 2001 From: Florent Hivert Date: Thu, 8 Feb 2018 13:45:01 +0100 Subject: [PATCH 673/740] Simpler, wider and more robust test --- .../finite_dimensional_algebras_with_basis.py | 14 +++++++------- .../categories/unique_factorization_domains.py | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/categories/finite_dimensional_algebras_with_basis.py b/src/sage/categories/finite_dimensional_algebras_with_basis.py index 910c07f29ba..268a3d29d90 100644 --- a/src/sage/categories/finite_dimensional_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_algebras_with_basis.py @@ -794,17 +794,17 @@ def peirce_summand(self, ei, ej): sage: A4.peirce_summand(e, e) Free module generated by {0, 1, 2, 3} over Rational Field - We check that idempotent is the basis for some 1-dimensional - peirce summand:: + TESTS:: + + We check each idempotent belong to its own peirce summand + (see :trac:`24687`):: sage: from sage.monoids.hecke_monoid import HeckeMonoid sage: M = HeckeMonoid(SymmetricGroup(4)) sage: A = M.algebra(QQ) - sage: e = A.orthogonal_idempotents_central_mod_radical()[0] - sage: S = A.peirce_summand(e, e); S - Free module generated by {0} over Rational Field - sage: f = S.basis()[0].lift() - sage: e / e.leading_coefficient() == f / f.leading_coefficient() + sage: Idms = A.orthogonal_idempotents_central_mod_radical() + sage: all(A.peirce_summand(e, e).retract(e) + ....: in A.peirce_summand(e, e) for e in Idms) True """ B = self.basis() diff --git a/src/sage/categories/unique_factorization_domains.py b/src/sage/categories/unique_factorization_domains.py index 63af8895b55..f299c1cb07f 100644 --- a/src/sage/categories/unique_factorization_domains.py +++ b/src/sage/categories/unique_factorization_domains.py @@ -153,7 +153,7 @@ def _gcd_univariate_polynomial(self, f, g): sage: (-x^2 - 4*x - 5)^(3-2+1) * p == quo*q + rem True - Check that :trac;`23620` has been resolved:: + Check that :trac:`23620` has been resolved:: sage: R. = ZpFM(2)[] sage: f = 2*x + 2 From 1bf128ec5f0540d7ec81f4df9ff106fe48f88cba Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Wed, 7 Feb 2018 17:51:07 +0000 Subject: [PATCH 674/740] Install the .pyx files --- src/sage_setup/find.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage_setup/find.py b/src/sage_setup/find.py index aef5e8f1f13..09f8a15b24c 100644 --- a/src/sage_setup/find.py +++ b/src/sage_setup/find.py @@ -114,6 +114,7 @@ def find_extra_files(packages, src_dir, cythonized_dir, special_filenames=[]): ['.../src/sage/ext/interpreters/wrapper_cdf.pxd', ...wrapper_cdf.h...])] """ data_files = [] + cy_exts = ('.pxd', '.pxi', '.pyx') for package in packages: dir = package.replace('.', os.path.sep) @@ -121,7 +122,7 @@ def find_extra_files(packages, src_dir, cythonized_dir, special_filenames=[]): cydir = os.path.join(cythonized_dir, dir) files = [os.path.join(sdir, f) for f in os.listdir(sdir) - if f.endswith((".pxd", ".pxi")) or f in special_filenames] + if f.endswith(cy_exts) or f in special_filenames] if os.path.isdir(cydir): # Not every directory contains Cython files files += [os.path.join(cydir, f) for f in os.listdir(cydir) if f.endswith(".h")] From 996759c97d6dd747e2d61fc5b2bb71f00119e597 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 8 Feb 2018 14:47:25 +0100 Subject: [PATCH 675/740] No longer add SAGE_SRC to sys.path --- src/sage/all.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/sage/all.py b/src/sage/all.py index 300f5194dd5..a766326cb0f 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -70,14 +70,10 @@ from sage.env import SAGE_ROOT, SAGE_SRC, SAGE_DOC_SRC, SAGE_LOCAL, DOT_SAGE, SAGE_ENV -# Add SAGE_SRC at the end of sys.path to enable Cython tracebacks -# (which use paths relative to SAGE_SRC) -sys.path.append(SAGE_SRC) - ################################################################### -# This import also setups the interrupt handler +# This import also sets up the interrupt handler from cysignals.signals import (AlarmInterrupt, SignalError, sig_on_reset as sig_on_count) From e000281b09bbafcc4477924e9810a8b538b6da53 Mon Sep 17 00:00:00 2001 From: Florent Hivert Date: Thu, 8 Feb 2018 14:58:54 +0100 Subject: [PATCH 676/740] Typo --- src/sage/categories/finite_dimensional_algebras_with_basis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/finite_dimensional_algebras_with_basis.py b/src/sage/categories/finite_dimensional_algebras_with_basis.py index 268a3d29d90..5eb7037e6d1 100644 --- a/src/sage/categories/finite_dimensional_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_algebras_with_basis.py @@ -794,7 +794,7 @@ def peirce_summand(self, ei, ej): sage: A4.peirce_summand(e, e) Free module generated by {0, 1, 2, 3} over Rational Field - TESTS:: + TESTS: We check each idempotent belong to its own peirce summand (see :trac:`24687`):: From cdea81bed4cdc96888ca4d2abc62d3c8a56b6f03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= Date: Thu, 8 Feb 2018 17:38:22 +0100 Subject: [PATCH 677/740] 23362: upgrade pandocfilters to 1.4.2 --- build/pkgs/pandocfilters/checksums.ini | 6 +++--- build/pkgs/pandocfilters/package-version.txt | 2 +- build/pkgs/pandocfilters/spkg-install | 16 +--------------- 3 files changed, 5 insertions(+), 19 deletions(-) diff --git a/build/pkgs/pandocfilters/checksums.ini b/build/pkgs/pandocfilters/checksums.ini index d750f58aa68..4846a23a217 100644 --- a/build/pkgs/pandocfilters/checksums.ini +++ b/build/pkgs/pandocfilters/checksums.ini @@ -1,4 +1,4 @@ tarball=pandocfilters-VERSION.tar.gz -sha1=fa54e5e61b896fe9bcffaf39035e1a7d282953ec -md5=54301b11175a57120168d7695fe958e4 -cksum=1528146878 +sha1=f2d402891a22353be2c641aa742cc939af8b2301 +md5=632e1dac599c5798d27e96ad47f2ebe4 +cksum=1089820352 diff --git a/build/pkgs/pandocfilters/package-version.txt b/build/pkgs/pandocfilters/package-version.txt index f0bb29e7638..9df886c42a1 100644 --- a/build/pkgs/pandocfilters/package-version.txt +++ b/build/pkgs/pandocfilters/package-version.txt @@ -1 +1 @@ -1.3.0 +1.4.2 diff --git a/build/pkgs/pandocfilters/spkg-install b/build/pkgs/pandocfilters/spkg-install index 921ee7f1700..deba1bb42bb 100644 --- a/build/pkgs/pandocfilters/spkg-install +++ b/build/pkgs/pandocfilters/spkg-install @@ -1,15 +1 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo "Error installing pandocfilters ... exiting" - exit 1 -fi - +cd src && sdh_pip_install . From 12327e477763bc013aabc28c2364608140b58865 Mon Sep 17 00:00:00 2001 From: Florent Hivert Date: Thu, 8 Feb 2018 20:07:08 +0100 Subject: [PATCH 678/740] typo (2) --- src/sage/categories/finite_dimensional_algebras_with_basis.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/finite_dimensional_algebras_with_basis.py b/src/sage/categories/finite_dimensional_algebras_with_basis.py index 5eb7037e6d1..10affde68a8 100644 --- a/src/sage/categories/finite_dimensional_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_algebras_with_basis.py @@ -631,7 +631,7 @@ def cartan_invariants_matrix(self): ALGORITHM: The Cartan invariant matrix of `A` is computed from the - dimension of the summands of its peirce decomposition. + dimension of the summands of its Peirce decomposition. .. SEEALSO:: @@ -796,7 +796,7 @@ def peirce_summand(self, ei, ej): TESTS: - We check each idempotent belong to its own peirce summand + We check each idempotent belong to its own Peirce summand (see :trac:`24687`):: sage: from sage.monoids.hecke_monoid import HeckeMonoid From e6cae6d88dc018f6421a91fc4d9e073ab138644e Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Fri, 9 Feb 2018 09:05:46 +0100 Subject: [PATCH 679/740] Updated SageMath version to 8.2.beta5 --- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- src/bin/sage-banner | 2 +- src/bin/sage-version.sh | 4 ++-- src/sage/version.py | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index b503aeef942..3ad408445a9 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 8.2.beta4, Release Date: 2018-01-27 +SageMath version 8.2.beta5, Release Date: 2018-02-09 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 41fb4d74819..cd2c5db52ae 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=499badf9321c53c458652ca71580585463e574a3 -md5=e483eb587a1be5886b718e24f54a84a5 -cksum=3889320352 +sha1=ade416eb97804aa5dbc97afe9e55f63de0986270 +md5=a8b02b91f6650ecaf26ef7b2287a5e7f +cksum=334528818 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 2197544d04c..63fe24a5cdd 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -252 +253 diff --git a/src/bin/sage-banner b/src/bin/sage-banner index d2e95e4428d..720359c08dd 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ SageMath version 8.2.beta4, Release Date: 2018-01-27 │ +│ SageMath version 8.2.beta5, Release Date: 2018-02-09 │ │ Type "notebook()" for the browser-based notebook interface. │ │ Type "help()" for help. │ └────────────────────────────────────────────────────────────────────┘ diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 270e83c3a8a..b892de5bdcc 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,4 +1,4 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='8.2.beta4' -SAGE_RELEASE_DATE='2018-01-27' +SAGE_VERSION='8.2.beta5' +SAGE_RELEASE_DATE='2018-02-09' diff --git a/src/sage/version.py b/src/sage/version.py index 1d98722b54f..c018102e652 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,4 +1,4 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '8.2.beta4' -date = '2018-01-27' +version = '8.2.beta5' +date = '2018-02-09' From 177ff6f796f690b7387299c01a48e41837aec019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= Date: Fri, 9 Feb 2018 14:54:36 +0100 Subject: [PATCH 680/740] 23362: use pypi rather than github to fetch the tarball from --- build/pkgs/pandocfilters/SPKG.txt | 3 +-- build/pkgs/pandocfilters/checksums.ini | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/build/pkgs/pandocfilters/SPKG.txt b/build/pkgs/pandocfilters/SPKG.txt index 9dd5c13f66a..d35dfd575e1 100644 --- a/build/pkgs/pandocfilters/SPKG.txt +++ b/build/pkgs/pandocfilters/SPKG.txt @@ -20,5 +20,4 @@ Home page: https://github.com/jgm/pandocfilters == Special Update/Build Instructions == Download the last release from -https://github.com/jgm/pandocfilters/releases - +https://pypi.python.org/pypi/pandocfilters diff --git a/build/pkgs/pandocfilters/checksums.ini b/build/pkgs/pandocfilters/checksums.ini index 4846a23a217..d629a37586a 100644 --- a/build/pkgs/pandocfilters/checksums.ini +++ b/build/pkgs/pandocfilters/checksums.ini @@ -1,4 +1,4 @@ tarball=pandocfilters-VERSION.tar.gz -sha1=f2d402891a22353be2c641aa742cc939af8b2301 -md5=632e1dac599c5798d27e96ad47f2ebe4 -cksum=1089820352 +sha1=46695990e2a2fedbab0d24b0f49fae8f1850d95a +md5=dc391791ef54c7de1572d7b46b63361f +cksum=2264149273 From 14a09db6032ab5aa87921f9b2fa86183a0600740 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Fri, 9 Feb 2018 15:15:04 +0100 Subject: [PATCH 681/740] Fix ComplexIntervalFieldElement.log() --- src/sage/rings/complex_interval.pyx | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx index 6d2fe22dae9..5473618a61c 100644 --- a/src/sage/rings/complex_interval.pyx +++ b/src/sage/rings/complex_interval.pyx @@ -1748,7 +1748,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): im = theta.sin() * mag return ComplexIntervalFieldElement(self._parent, re, im) - def log(self,base=None): + def log(self, base=None): """ Complex logarithm of `z`. @@ -1790,17 +1790,21 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): If a base is passed from another function, we can accommodate this:: sage: CIF(-1,1).log(2) - 0.500000000000000? + 3.399270106370396?*I + 0.500000000000000? + 3.39927010637040?*I """ - if self == 0: + if not self: from .real_mpfi import RIF return RIF(0).log() - theta = self.argument() - rho = abs(self) - if base is None or base == 'e': - return ComplexIntervalFieldElement(self._parent, rho.log(), theta) - else: - return ComplexIntervalFieldElement(self._parent, rho.log()/RealNumber(RealField(self.prec()),base).log(), theta/RealNumber(RealField(self.prec()),base).log()) + re = abs(self).log() + im = self.argument() + if base == 'e': + base = None + if base is not None: + base = self._parent._real_field()(base) + f = base.log() + re /= f + im /= f + return ComplexIntervalFieldElement(self._parent, re, im) def sqrt(self, bint all=False, **kwds): """ From 0c38a837152312c3b2c79f287812805dd91673df Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Fri, 9 Feb 2018 17:05:13 +0100 Subject: [PATCH 682/740] 24696: fix hash_map logic --- build/pkgs/giac/package-version.txt | 2 +- build/pkgs/giac/patches/hashmap.patch | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/giac/patches/hashmap.patch diff --git a/build/pkgs/giac/package-version.txt b/build/pkgs/giac/package-version.txt index becbcd212a0..ed1aa46887e 100644 --- a/build/pkgs/giac/package-version.txt +++ b/build/pkgs/giac/package-version.txt @@ -1 +1 @@ -1.4.9.45.p1 +1.4.9.45.p2 diff --git a/build/pkgs/giac/patches/hashmap.patch b/build/pkgs/giac/patches/hashmap.patch new file mode 100644 index 00000000000..6f538396855 --- /dev/null +++ b/build/pkgs/giac/patches/hashmap.patch @@ -0,0 +1,13 @@ +diff --git a/giac-1.4.9.45-orig/src/src/index.h b/giac-1.4.9.45/src/src/index.h +index e74be63..8ed12f9 100755 +--- a/src/index.h ++++ b/src/index.h +@@ -42,7 +42,7 @@ + #include + #endif + +-#if defined UNORDERED_MAP && !defined(__clang__) && !defined(VISUALC) // && !defined(__APPLE__) ++#if defined UNORDERED_MAP && !defined(VISUALC) // && !defined(__APPLE__) + #include + #define HASH_MAP_NAMESPACE std::tr1 + #define hash_map unordered_map From 1195b3a4b098a4e5d32e482603ff9d0c3e86e9c8 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 13 Dec 2017 15:37:27 +0100 Subject: [PATCH 683/740] Move real/complex interval fields to new coercion model --- src/module_list.py | 2 + src/sage/arith/constants.pxd | 2 + src/sage/categories/rings.py | 3 - src/sage/rings/complex_interval.pyx | 62 ++-- src/sage/rings/complex_interval_field.py | 197 ++++++------ src/sage/rings/convert/__init__.py | 0 src/sage/rings/convert/mpfi.pxd | 5 + src/sage/rings/convert/mpfi.pyx | 291 ++++++++++++++++++ .../number_field_element_quadratic.pyx | 80 ++++- src/sage/rings/polynomial/real_roots.pyx | 6 +- src/sage/rings/qqbar.py | 19 +- src/sage/rings/real_lazy.pyx | 8 +- src/sage/rings/real_mpfi.pyx | 251 ++++++++------- 13 files changed, 644 insertions(+), 282 deletions(-) create mode 100644 src/sage/rings/convert/__init__.py create mode 100644 src/sage/rings/convert/mpfi.pxd create mode 100644 src/sage/rings/convert/mpfi.pyx diff --git a/src/module_list.py b/src/module_list.py index 23d8f2ca81e..c661ee06212 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -1201,6 +1201,8 @@ def uname_specific(name, value, alternative): Extension('sage.rings.ring', sources = ['sage/rings/ring.pyx']), + Extension('*', ['sage/rings/convert/*.pyx']), + ################################ ## ## sage.rings.finite_rings diff --git a/src/sage/arith/constants.pxd b/src/sage/arith/constants.pxd index dec1b7b33b3..84ade1f956a 100644 --- a/src/sage/arith/constants.pxd +++ b/src/sage/arith/constants.pxd @@ -21,3 +21,5 @@ cdef extern from *: double M_1_LN2 "0x1.71547652b82fep+0" # 1/log(2) double M_1_LN10 "0x6.f2dec549b9438p-4" # 1/log(10) double M_1_LNPI "0xd.fa22fdd8cfd98p-4" # 1/log(π) + + double LOG_TEN_TWO_PLUS_EPSILON "0x3.5269e12f346e4p+0" # log(10,2) rounded up diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index ba0f70d1a8f..5c1ce2936f3 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -946,9 +946,6 @@ def __getitem__(self, arg): From: Number Field in i with defining polynomial x^2 + 1 To: Complex Lazy Field Defn: i -> 1*I - sage: QQi. = QuadraticField(-1, embedding=None) - sage: QQ[i].coerce_embedding() is None - True TESTS: diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx index 6d2fe22dae9..cd12eec28f1 100644 --- a/src/sage/rings/complex_interval.pyx +++ b/src/sage/rings/complex_interval.pyx @@ -41,16 +41,16 @@ heavily modified: from __future__ import absolute_import, print_function +from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE from cysignals.signals cimport sig_on, sig_off from sage.libs.gmp.mpz cimport mpz_sgn, mpz_cmpabs_ui from sage.libs.mpfr cimport * from sage.libs.mpfi cimport * from sage.libs.flint.fmpz cimport * -from cypari2.gen cimport Gen as pari_gen from sage.libs.mpfr cimport MPFR_RNDU, MPFR_RNDD +from sage.arith.constants cimport LOG_TEN_TWO_PLUS_EPSILON -from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE from sage.structure.element cimport FieldElement, RingElement, Element, ModuleElement from sage.structure.parent cimport Parent from .complex_number cimport ComplexNumber @@ -58,10 +58,9 @@ from .complex_field import ComplexField from sage.rings.integer cimport Integer cimport sage.rings.real_mpfi as real_mpfi from .real_mpfr cimport RealNumber, RealField +from .convert.mpfi cimport mpfi_set_sage -cdef double LOG_TEN_TWO_PLUS_EPSILON = 3.321928094887363 # a small overestimate of log(10,2) - def is_ComplexIntervalFieldElement(x): """ Check if ``x`` is a :class:`ComplexIntervalFieldElement`. @@ -76,6 +75,7 @@ def is_ComplexIntervalFieldElement(x): """ return isinstance(x, ComplexIntervalFieldElement) + cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): """ A complex interval. @@ -86,7 +86,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: b = 1.5 + 2.5*I sage: TestSuite(b).run() """ - def __cinit__(self, parent, real=None, imag=None): + def __cinit__(self, parent, *args): """ TESTS:: @@ -98,13 +98,12 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: ComplexIntervalFieldElement.__new__(ComplexIntervalFieldElement, CIF) [.. NaN ..] + [.. NaN ..]*I """ - cdef Parent p = parent - self._prec = p._prec + self._parent = parent + self._prec = parent._prec mpfi_init2(self.__re, self._prec) mpfi_init2(self.__im, self._prec) - self._parent = p - def __init__(self, parent, real, imag=None): + def __init__(self, parent, real, imag=None, int base=10): """ Initialize a complex number (interval). @@ -117,35 +116,16 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: CIF(1.5 + 2.5*I) 1.5000000000000000? + 2.5000000000000000?*I """ - cdef real_mpfi.RealIntervalFieldElement rr, ii - self._parent = parent - self._prec = self._parent._prec - mpfi_init2(self.__re, self._prec) - mpfi_init2(self.__im, self._prec) - - if imag is None: - if isinstance(real, ComplexNumber): - real, imag = (real).real(), (real).imag() - elif isinstance(real, ComplexIntervalFieldElement): - real, imag = (real).real(), (real).imag() - elif isinstance(real, pari_gen): - real, imag = real.real(), real.imag() - elif isinstance(real, list) or isinstance(real, tuple): - re, imag = real - real = re - elif isinstance(real, complex): - real, imag = real.real, real.imag - else: - imag = 0 - try: - R = parent._real_field() - rr = R(real) - ii = R(imag) - mpfi_set(self.__re, rr.value) - mpfi_set(self.__im, ii.value) - except TypeError: - raise TypeError("unable to coerce to a ComplexIntervalFieldElement") - + if real is None: + mpfi_set_ui(self.__re, 0) + mpfi_set_ui(self.__im, 0) + elif imag is None: + # "real" may be real or complex + mpfi_set_sage(self.__re, self.__im, real, parent, base) + else: + # Set real and imaginary parts separately + mpfi_set_sage(self.__re, NULL, real, parent, base) + mpfi_set_sage(self.__im, NULL, imag, parent, base) def __dealloc__(self): if self._parent is not None: @@ -418,7 +398,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): """ left, right = self.real().endpoints() lower, upper = self.imag().endpoints() - CC = self._parent._middle_field() + CC = self._parent.middle_field() return (CC(left, lower), CC(right, upper), CC(left, upper), CC(right, lower)) @@ -491,7 +471,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): 2.00000000000000 """ cdef RealNumber diam - diam = RealNumber(self._parent._real_field()._middle_field(), None) + diam = RealNumber(self._parent._real_field().middle_field(), None) cdef mpfr_t tmp mpfr_init2(tmp, self.prec()) mpfi_diam(diam.value, self.__re) @@ -629,7 +609,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): 1.50000000000000 + 3.50000000000000*I """ cdef ComplexNumber center - center = ComplexNumber(self._parent._middle_field(), None) + center = ComplexNumber(self._parent.middle_field(), None) mpfi_mid(center.__re, self.__re) mpfi_mid(center.__im, self.__im) diff --git a/src/sage/rings/complex_interval_field.py b/src/sage/rings/complex_interval_field.py index c52a86b5805..dcfd48fa93b 100644 --- a/src/sage/rings/complex_interval_field.py +++ b/src/sage/rings/complex_interval_field.py @@ -25,25 +25,32 @@ ``+/-1``. """ -################################################################################# +#***************************************************************************** # Copyright (C) 2006 William Stein # -# Distributed under the terms of the GNU General Public License (GPL) -# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** + from __future__ import absolute_import +from six import integer_types + +from sage.structure.parent import Parent +from .integer_ring import ZZ +from .rational_field import QQ from . import complex_double -from . import ring +from .ring import Field from . import integer -import weakref -from . import real_mpfi from . import complex_interval -from . import complex_field +import weakref +from .real_mpfi import RealIntervalField, RealIntervalField_class +from .complex_field import ComplexField, ComplexField_class from sage.misc.sage_eval import sage_eval -from sage.structure.parent_gens import ParentWithGens NumberFieldElement_quadratic = None def late_import(): @@ -104,7 +111,7 @@ def ComplexIntervalField(prec=53, names=None): return C -class ComplexIntervalField_class(ring.Field): +class ComplexIntervalField_class(Field): """ The field of complex (interval) numbers. @@ -124,11 +131,11 @@ class ComplexIntervalField_class(ring.Field): sage: Q = RationalField() sage: C(1/3) 0.3333333333333334? - sage: S = PolynomialRing(Q, 'x') - sage: C(S.gen()) + sage: S. = PolynomialRing(Q) + sage: C(x) Traceback (most recent call last): ... - TypeError: unable to coerce to a ComplexIntervalFieldElement + TypeError: unable to convert x to real interval This illustrates precision:: @@ -172,7 +179,7 @@ class ComplexIntervalField_class(ring.Field): sage: CIF = ComplexIntervalField(53) sage: CIF.category() - Category of fields + Category of infinite fields sage: TestSuite(CIF).run() TESTS: @@ -192,6 +199,8 @@ class ComplexIntervalField_class(ring.Field): sage: x + CIF(RIF(3.14,3.15), 0) x + 3.15? """ + Element = complex_interval.ComplexIntervalFieldElement + def __init__(self, prec=53): """ Initialize ``self``. @@ -205,7 +214,9 @@ def __init__(self, prec=53): """ self._prec = int(prec) from sage.categories.fields import Fields - ParentWithGens.__init__(self, self._real_field(), ('I',), False, category = Fields()) + Field.__init__(self, self._real_field(), ("I",), False, + category=Fields().Infinite()) + self._populate_coercion_lists_(convert_method_name="_complex_mpfi_") def __reduce__(self): """ @@ -338,42 +349,43 @@ def _sage_input_(self, sib, coerce): precision = prec - # very useful to cache this. - def _real_field(self): + def real_field(self): """ Return the underlying :class:`RealIntervalField`. EXAMPLES:: - sage: R = CIF._real_field(); R + sage: R = CIF.real_field(); R Real Interval Field with 53 bits of precision - sage: ComplexIntervalField(200)._real_field() + sage: ComplexIntervalField(200).real_field() Real Interval Field with 200 bits of precision - sage: CIF._real_field() is R + sage: CIF.real_field() is R True """ try: return self.__real_field except AttributeError: - self.__real_field = real_mpfi.RealIntervalField(self._prec) + self.__real_field = RealIntervalField(self._prec) return self.__real_field - def _middle_field(self): + _real_field = real_field + + def middle_field(self): """ Return the corresponding :class:`ComplexField` with the same precision as ``self``. EXAMPLES:: - sage: CIF._middle_field() + sage: CIF.middle_field() Complex Field with 53 bits of precision - sage: ComplexIntervalField(200)._middle_field() + sage: ComplexIntervalField(200).middle_field() Complex Field with 200 bits of precision """ try: return self.__middle_field except AttributeError: - self.__middle_field = complex_field.ComplexField(self._prec) + self.__middle_field = ComplexField(self._prec) return self.__middle_field def __eq__(self, other): @@ -426,7 +438,7 @@ def __ne__(self, other): """ return not (self == other) - def __call__(self, x, im=None): + def __call__(self, x=None, im=None, **kwds): """ Construct an element. @@ -437,7 +449,9 @@ def __call__(self, x, im=None): sage: CIF(CIF.0) 1*I sage: CIF('1+I') - 1 + 1*I + Traceback (most recent call last): + ... + TypeError: unable to convert '1+I' to real interval sage: CIF(2,3) 2 + 3*I sage: CIF(pi, e) @@ -453,80 +467,85 @@ def __call__(self, x, im=None): -1*I sage: QQi. = QuadraticField(-1, embedding=None) sage: CIF(i) - Traceback (most recent call last): - ... - ValueError: can not convert complex algebraic number to real interval - """ - if im is None: - if isinstance(x, complex_interval.ComplexIntervalFieldElement): - if x.parent() is self: - return x - else: - return complex_interval.ComplexIntervalFieldElement(self, x) - elif isinstance(x, complex_double.ComplexDoubleElement): - return complex_interval.ComplexIntervalFieldElement(self, x.real(), x.imag()) - elif isinstance(x, str): - # TODO: this is probably not the best and most - # efficient way to do this. -- Martin Albrecht - return complex_interval.ComplexIntervalFieldElement(self, - sage_eval(x.replace(' ',''), locals={"I":self.gen(),"i":self.gen()})) - - late_import() - if isinstance(x, NumberFieldElement_quadratic): - parent = x.parent() - if (list(parent.polynomial()) == [1, 0, 1] and - parent.coerce_embedding() is not None): - (re, im) = list(x) - if not parent._standard_embedding: - im = -im - return complex_interval.ComplexIntervalFieldElement(self, re, im) - - try: - return x._complex_mpfi_( self ) - except AttributeError: - pass - try: - return x._complex_mpfr_field_( self ) - except AttributeError: - pass - return complex_interval.ComplexIntervalFieldElement(self, x, im) - - def _coerce_impl(self, x): - """ - Return the canonical coerce of ``x`` into this complex field, if it is - defined, otherwise raise a ``TypeError``. + 1*I + + :: + + sage: R. = CIF[] + sage: a = R(CIF(0,1)); a + I + sage: CIF(a) + 1*I + """ + # Note: we override Parent.__call__ because we want to support + # CIF(a, b) and that is hard to do using coerce maps. + if im is not None or kwds: + return complex_interval.ComplexIntervalFieldElement(self, x, im, **kwds) + return Parent.__call__(self, x) + + def _coerce_map_from_(self, S): + """ + Canonical coercion from ``S`` to this complex interval field. The rings that canonically coerce to the MPFI complex field are: - * this MPFI complex field, or any other of higher precision + - this MPFI complex field, or any other of higher precision - * anything that canonically coerces to the mpfi real field with this - precision + - anything that canonically coerces to the real interval field + with this precision EXAMPLES:: sage: CIF((2,1)) + 2 + I # indirect doctest 4 + 2*I - sage: x = ComplexField(25)(2) - sage: x - 2.000000 - sage: CIF((2,1)) + x + sage: CIF((2,1)) + RR.pi() + 5.1415926535897932? + 1*I + sage: CIF((2,1)) + CC.pi() Traceback (most recent call last): ... - TypeError: unsupported operand parent(s) for +: 'Complex Interval - Field with 53 bits of precision' and 'Complex Field with 25 bits of precision' - """ - try: - K = x.parent() - if is_ComplexIntervalField(K) and K._prec >= self._prec: - return self(x) -# elif complex_field.is_ComplexField(K) and K.prec() >= self._prec: -# return self(x) - except AttributeError: - pass - if hasattr(x, '_complex_mpfr_field_') or hasattr(x, '_complex_mpfi_'): - return self(x) - return self._coerce_try(x, self._real_field()) + TypeError: unsupported operand parent(s) for +: 'Complex Interval Field with 53 bits of precision' and 'Complex Field with 53 bits of precision' + + sage: CIF.coerce_map_from(QQ) + Coercion map: + From: Rational Field + To: Complex Interval Field with 53 bits of precision + sage: CIF.coerce_map_from(int) + Coercion map: + From: Set of Python objects of class 'int' + To: Complex Interval Field with 53 bits of precision + sage: CIF.coerce_map_from(GaussianIntegers()) + Conversion via _complex_mpfi_ method map: + From: Gaussian Integers in Number Field in I with defining polynomial x^2 + 1 + To: Complex Interval Field with 53 bits of precision + sage: CIF.coerce_map_from(QQbar) + Conversion via _complex_mpfi_ method map: + From: Algebraic Field + To: Complex Interval Field with 53 bits of precision + sage: CIF.coerce_map_from(AA) + Conversion via _complex_mpfi_ method map: + From: Algebraic Real Field + To: Complex Interval Field with 53 bits of precision + sage: CIF.coerce_map_from(UniversalCyclotomicField()) + Conversion via _complex_mpfi_ method map: + From: Universal Cyclotomic Field + To: Complex Interval Field with 53 bits of precision + """ + # Direct and efficient conversions + if S is ZZ or S is QQ or S is float: + return True + if any(S is T for T in integer_types): + return True + if isinstance(S, (ComplexIntervalField_class, + RealIntervalField_class)): + return S.precision() >= self._prec + + # Assume that a _complex_mpfi_ method always defines a + # coercion (as opposed to only a conversion). + f = self._convert_method_map(S) + if f is not None: + return f + + return self._coerce_map_via( (self.real_field(),), S) def _repr_(self): """ diff --git a/src/sage/rings/convert/__init__.py b/src/sage/rings/convert/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/rings/convert/mpfi.pxd b/src/sage/rings/convert/mpfi.pxd new file mode 100644 index 00000000000..ea43d9383f2 --- /dev/null +++ b/src/sage/rings/convert/mpfi.pxd @@ -0,0 +1,5 @@ +from sage.libs.mpfi.types cimport mpfi_ptr + +cdef int mpfi_set_sage(mpfi_ptr re, mpfi_ptr im, x, field, int base) except -1 +cdef int mpfi_interv_sage(mpfi_ptr re, mpfi_ptr im, x, y, field, int base) except -1 +cdef int mpfi_set_via_RR(mpfi_ptr re, x, field) except -1 diff --git a/src/sage/rings/convert/mpfi.pyx b/src/sage/rings/convert/mpfi.pyx new file mode 100644 index 00000000000..a93b810ea01 --- /dev/null +++ b/src/sage/rings/convert/mpfi.pyx @@ -0,0 +1,291 @@ +""" +Convert Sage/Python objects to real/complex intervals +""" +#***************************************************************************** +# Copyright (C) 2018 Jeroen Demeyer +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from cpython.float cimport PyFloat_AS_DOUBLE +from cpython.complex cimport PyComplex_RealAsDouble, PyComplex_ImagAsDouble + +from sage.libs.mpfr cimport * +from sage.libs.mpfi cimport * + +from sage.arith.long cimport integer_check_long +from sage.structure.element cimport Element, parent +from ..integer cimport Integer +from ..rational cimport Rational +from ..real_mpfi cimport RealIntervalFieldElement, RealIntervalField_class +from ..complex_interval_field import ComplexIntervalField_class +from ..real_mpfr cimport RealNumber +from ..real_double cimport RealDoubleElement +from ..complex_number cimport ComplexNumber +from ..complex_interval cimport ComplexIntervalFieldElement +from ..complex_double cimport ComplexDoubleElement + +from cypari2.gen cimport Gen + + +cdef inline int return_real(mpfi_ptr im): + """ + Called by ``mpfi_set_sage`` on the imaginary part when converting + a real number. + """ + if im is not NULL: + mpfi_set_ui(im, 0) + return 0 + + +cdef int mpfi_set_sage(mpfi_ptr re, mpfi_ptr im, x, field, int base) except -1: + """ + Convert any object ``x`` to an MPFI interval or a pair of + real and complex intervals. + + INPUT: + + - ``re`` -- a pre-initialized MPFI interval. + + - ``im`` -- a pre-initialized MPFI interval or NULL. + + - ``x`` -- any Sage or Python object to be converted to an interval. + + - ``field`` -- a ``RealIntervalField`` or ``ComplexIntervalField`` + of the right precision (real or complex doesn't matter). + + - ``base`` -- base to use for string conversion. + + OUTPUT: + + - if conversion is possible: set ``re`` and ``im`` (if applicable) + and return 0. + + - if ``x`` is complex but ``im`` is ``NULL``: convert only if the + imaginary component is 0. + + - in all other cases: raise an exception. + """ + cdef RealIntervalFieldElement ri + cdef ComplexIntervalFieldElement zi + cdef ComplexNumber zn + cdef ComplexDoubleElement zd + cdef bytes s + + if im is not NULL and isinstance(x, tuple): + # For complex numbers, interpret tuples as real/imag parts + if len(x) != 2: + raise TypeError("tuple defining a complex number must have length 2") + mpfi_set_sage(re, NULL, x[0], field, base) + mpfi_set_sage(im, NULL, x[1], field, base) + return 0 + if isinstance(x, list) or isinstance(x, tuple): + # Interpret entries in x as endpoints of interval + if len(x) != 2: + raise TypeError("list defining an interval must have length 2") + return mpfi_interv_sage(re, im, x[0], x[1], field, base) + + cdef long value + cdef int err + + # Check for known types. First check for Element to reduce the + # number of checks below. + if isinstance(x, Element): + # Real + if isinstance(x, RealIntervalFieldElement): + mpfi_set(re, (x).value) + return return_real(im) + if isinstance(x, RealNumber): + mpfi_set_fr(re, (x).value) + return return_real(im) + if isinstance(x, Rational): + mpfi_set_q(re, (x).value) + return return_real(im) + if isinstance(x, Integer): + mpfi_set_z(re, (x).value) + return return_real(im) + if isinstance(x, RealDoubleElement): + mpfi_set_d(re, (x)._value) + return return_real(im) + + # Complex + if isinstance(x, ComplexIntervalFieldElement): + zi = x + if im is NULL: + if not mpfi_is_zero(zi.__im): + raise TypeError(f"unable to convert complex interval {x!r} to real interval") + else: + mpfi_set(im, zi.__im) + mpfi_set(re, zi.__re) + return 0 + if isinstance(x, ComplexNumber): + zn = x + if im is NULL: + if mpfr_sgn(zn.__im) != 0: + raise TypeError(f"unable to convert complex number {x!r} to real interval") + else: + mpfi_set_fr(im, zn.__im) + mpfi_set_fr(re, zn.__re) + return 0 + if isinstance(x, ComplexDoubleElement): + zd = x + if im is NULL: + if zd._complex.dat[1] != 0: + raise TypeError(f"unable to convert complex number {x!r} to real interval") + else: + mpfi_set_d(im, zd._complex.dat[1]) + mpfi_set_d(re, zd._complex.dat[0]) + return 0 + else: # not a Sage Element + # Real + if isinstance(x, float): + mpfi_set_d(re, PyFloat_AS_DOUBLE(x)) + return return_real(im) + if integer_check_long(x, &value, &err): + if err == 0: + mpfi_set_si(re, value) + else: + mpfi_set_via_RR(re, x, field) + return return_real(im) + if isinstance(x, unicode): + x = x.encode("ascii") + if isinstance(x, bytes): + s = (x).replace(b'..', b',').replace(b' ', b'').replace(b'+infinity', b'@inf@').replace(b'-infinity', b'-@inf@') + if mpfi_set_str(re, s, base): + raise TypeError(f"unable to convert {x!r} to real interval") + return return_real(im) + + # Complex + if isinstance(x, Gen): + imag = x.imag() + if im is NULL: + if imag: + raise TypeError(f"unable to convert complex PARI/GP element {x!r} to real interval") + else: + mpfi_set_via_RR(im, imag, field) + mpfi_set_via_RR(re, x.real(), field) + return 0 + if isinstance(x, complex): + imag = PyComplex_ImagAsDouble(x) + if im is NULL: + if imag: + raise TypeError(f"unable to convert complex number {x!r} to real interval") + else: + mpfi_set_d(im, imag) + mpfi_set_d(re, PyComplex_RealAsDouble(x)) + return 0 + + # No known type, try _real_mpfi_ or _complex_mpfi_ methods + if im is not NULL: + try: + m = x._complex_mpfi_ + except AttributeError: + pass + else: + if not isinstance(field, ComplexIntervalField_class): + field = field.complex_field() + e = m(field) + mpfi_swap(re, e.__re) + mpfi_swap(im, e.__im) + return 0 + + try: + m = x._real_mpfi_ + except AttributeError: + pass + else: + if not isinstance(field, RealIntervalField_class): + field = field.real_field() + ri = m(field) + mpfi_swap(re, ri.value) + return return_real(im) + + # Finally, try converting via the corresponding RealField + mpfi_set_via_RR(re, x, field) + return return_real(im) + + +cdef int mpfi_interv_sage(mpfi_ptr re, mpfi_ptr im, x, y, field, int base) except -1: + """ + Like ``mpfi_set_sage`` but construct the interval around ``x`` and + ``y``. It is not required that ``x <= y`` or that ``x`` and ``y`` + are of the same type. + + INPUT: see ``mpfi_set_sage`` + """ + cdef long valx, valy + cdef int err + if type(x) is type(y): + if isinstance(x, RealNumber): + mpfi_interv_fr(re, (x).value, (y).value) + return return_real(im) + if isinstance(x, RealDoubleElement): + mpfi_interv_d(re, (x)._value, (y)._value) + return return_real(im) + if isinstance(x, Integer): + mpfi_interv_z(re, (x).value, (y).value) + return return_real(im) + if isinstance(x, Rational): + mpfi_interv_q(re, (x).value, (y).value) + return return_real(im) + if isinstance(x, float): + mpfi_interv_d(re, PyFloat_AS_DOUBLE(x), PyFloat_AS_DOUBLE(y)) + return return_real(im) + # General check for C long + integer_check_long(x, &valx, &err) + if err == 0: + integer_check_long(y, &valy, &err) + if err == 0: + mpfi_interv_si(re, valx, valy) + return return_real(im) + + # General case: convert both x and y to an interval and take the + # union + + # First handle x + mpfi_set_sage(re, im, x, field, base) + + # Now handle y, which requires temporary mpfi variables. + cdef mpfi_t tmp1, tmp2 + cdef mpfr_prec_t prec = mpfi_get_prec(re) + + mpfi_init2(tmp1, prec) + cdef mpfi_ptr tmpim = NULL + if im is not NULL: + mpfi_init2(tmp2, prec) + tmpim = tmp2 + + try: + mpfi_set_sage(tmp1, tmpim, y, field, base) + mpfi_union(re, re, tmp1) + if im is not NULL: + mpfi_union(im, im, tmpim) + finally: + mpfi_clear(tmp1) + if tmpim is not NULL: + mpfi_clear(tmpim) + + +cdef int mpfi_set_via_RR(mpfi_ptr re, x, field) except -1: + """ + Convert ``x`` to an MPFI interval by converting ``x`` to the + appropriate real fields. + + INPUT: see ``mpfi_set_sage`` + """ + cdef RealIntervalField_class RIF + if isinstance(field, RealIntervalField_class): + RIF = field + else: + RIF = field.real_field() + + try: + ra = RIF.__lower_field(x) + rb = RIF.__upper_field(x) + except TypeError: + raise TypeError(f"unable to convert {x!r} to real interval") + mpfi_interv_fr(re, (ra).value, (rb).value) diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pyx b/src/sage/rings/number_field/number_field_element_quadratic.pyx index d2f90afe1ea..9ad624f42c9 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -55,7 +55,8 @@ from sage.rings.real_double import RDF from sage.rings.complex_double import CDF from sage.categories.morphism cimport Morphism from sage.rings.number_field.number_field_element import _inverse_mod_generic -from sage.rings.real_mpfi cimport RealIntervalFieldElement, RealIntervalField_class +from sage.rings.real_mpfi cimport RealIntervalFieldElement +from sage.rings.complex_interval cimport ComplexIntervalFieldElement from sage.rings.real_arb cimport RealBall from sage.rings.complex_arb cimport ComplexBall @@ -533,7 +534,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): (x)._reduce_c_() return x - def _real_mpfi_(self, RealIntervalField_class R): + def _real_mpfi_(self, R): r""" Conversion to a real interval field @@ -555,7 +556,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): sage: RIF(K3.gen()) Traceback (most recent call last): ... - ValueError: can not convert complex algebraic number to real interval + ValueError: unable to convert complex algebraic number a to real interval sage: RIF(K3(2)) 2 @@ -568,34 +569,91 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): sage: RealIntervalField(128)(u).is_zero() False - The following is a bug due to the fact that ``RIF`` does not play - nicely with coercions (it should have been a conversion map - via _real_mpfi_ method map):: + This was fixed in :trac:`24371`:: sage: RIF.convert_map_from(QuadraticField(5)) - Call morphism: + Conversion via _real_mpfi_ method map: From: Number Field in a with defining polynomial x^2 - 5 To: Real Interval Field with 53 bits of precision """ - cdef RealIntervalFieldElement ans = R._new() + ans = RealIntervalFieldElement.__new__(RealIntervalFieldElement, R) if mpz_cmp_ui(self.b, 0): if mpz_cmp_ui(self.D.value, 0) < 0: - raise ValueError("can not convert complex algebraic number to real interval") + raise ValueError(f"unable to convert complex algebraic number {self!r} to real interval") mpfi_set_z(ans.value, self.D.value) mpfi_sqrt(ans.value, ans.value) if not self.standard_embedding: mpfi_neg(ans.value, ans.value) mpfi_mul_z(ans.value, ans.value, self.b) - mpfi_add_z(ans.value, ans.value, self.a) - else: mpfi_set_z(ans.value, self.a) mpfi_div_z(ans.value, ans.value, self.denom) return ans + def _complex_mpfi_(self, R): + r""" + Conversion to a complex interval field + + TESTS:: + + sage: K. = QuadraticField(2) + sage: CIF(a) + 1.414213562373095? + sage: CIF(K(1/5)) + 0.2000000000000000? + sage: CIF(1/5 + 3/5*a) + 1.048528137423857? + + sage: K. = QuadraticField(-2) + sage: CIF(a) + 1.414213562373095?*I + sage: CIF(K(1/5)) + 0.2000000000000000? + sage: CIF(1/5 + 3/5*a) + 0.2000000000000000? + 0.848528137423857?*I + + sage: K. = QuadraticField(-2, embedding=-CLF(-2).sqrt()) + sage: CIF(a) + -1.414213562373095?*I + + This was fixed in :trac:`24371`:: + + sage: CIF.convert_map_from(QuadraticField(-5)) + Conversion via _complex_mpfi_ method map: + From: Number Field in a with defining polynomial x^2 + 5 + To: Complex Interval Field with 53 bits of precision + """ + ans = ComplexIntervalFieldElement.__new__(ComplexIntervalFieldElement, R) + + if mpz_cmp_ui(self.b, 0): + mpfi_set_z(ans.__re, self.D.value) + if mpfi_is_neg(ans.__re): + # Imaginary quadratic + mpfi_neg(ans.__re, ans.__re) + mpfi_sqrt(ans.__im, ans.__re) + if not self.standard_embedding: + mpfi_neg(ans.__im, ans.__im) + mpfi_set_z(ans.__re, self.a) + mpfi_mul_z(ans.__im, ans.__im, self.b) + mpfi_div_z(ans.__im, ans.__im, self.denom) + else: + # Real quadratic + mpfi_sqrt(ans.__re, ans.__re) + if not self.standard_embedding: + mpfi_neg(ans.__re, ans.__re) + mpfi_mul_z(ans.__re, ans.__re, self.b) + mpfi_add_z(ans.__re, ans.__re, self.a) + mpfi_set_ui(ans.__im, 0) + else: + mpfi_set_z(ans.__re, self.a) + mpfi_set_ui(ans.__im, 0) + + mpfi_div_z(ans.__re, ans.__re, self.denom) + return ans + cdef int arb_set_real(self, arb_t x, long prec) except -1: "Set x to the real part of this element" cdef fmpz_t tmpz diff --git a/src/sage/rings/polynomial/real_roots.pyx b/src/sage/rings/polynomial/real_roots.pyx index 0d0b73aafa8..bd5d9f90b9a 100644 --- a/src/sage/rings/polynomial/real_roots.pyx +++ b/src/sage/rings/polynomial/real_roots.pyx @@ -2217,8 +2217,8 @@ def cl_maximum_root_first_lambda(cl): pending_pos_exp = j posCounter = posCounter+1 - if len(neg) == 0: - return RIF._upper_field().zero() + if not neg: + return RIF.upper_field().zero() max_ub_log = RIF('-infinity') for j in xrange(len(neg)): @@ -2347,7 +2347,7 @@ def root_bounds(p): if n == 0: # not RIF.zero().endpoints() because of MPFI's convention that the # upper bound is -0. - return RIF._lower_field().zero(), RIF._upper_field().zero() + return RIF.lower_field().zero(), RIF.upper_field().zero() ub = cl_maximum_root(cl) diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 79813c81c9e..72252e899fc 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -3722,7 +3722,7 @@ def interval_fast(self, field): sage: x.interval_fast(RIF) Traceback (most recent call last): ... - TypeError: unable to convert 0.7071067811865475244? + 0.7071067811865475244?*I to real interval + TypeError: unable to convert complex interval 0.7071067811865475244? + 0.7071067811865475244?*I to real interval """ while self._value.prec() < field.prec(): self._more_precision() @@ -3818,7 +3818,7 @@ def interval(self, field): else: return field(val) - _real_mpfi_ = interval + _complex_mpfi_ = _real_mpfi_ = interval def radical_expression(self): r""" @@ -4275,27 +4275,18 @@ def interval_exact(self, field): def _complex_mpfr_field_(self, field): r""" - Compute an approximation to self in the given field, which may be - either an interval field (in which case ``self.interval()`` is called) - or any other complex field (in which case ``self.complex_number()`` is - called). + Compute an approximation to self in the given field, which must + be a complex field. EXAMPLES:: sage: a = QQbar(1 + I).sqrt() - sage: t = a._complex_mpfr_field_(CIF); t - 1.098684113467810? + 0.4550898605622274?*I - sage: parent(t) - Complex Interval Field with 53 bits of precision sage: t = a._complex_mpfr_field_(ComplexField(100)); t 1.0986841134678099660398011952 + 0.45508986056222734130435775782*I sage: parent(t) Complex Field with 100 bits of precision """ - if is_ComplexIntervalField(field): - return self.interval(field) - else: - return self.complex_number(field) + return self.complex_number(field) def complex_number(self, field): r""" diff --git a/src/sage/rings/real_lazy.pyx b/src/sage/rings/real_lazy.pyx index 34e68260d08..71cb68246bb 100644 --- a/src/sage/rings/real_lazy.pyx +++ b/src/sage/rings/real_lazy.pyx @@ -167,7 +167,7 @@ cdef class LazyField(Field): # discovery algorithm and when trying to convert LazyWrappers, # so we only consider direct coercions. if mor is not None and not isinstance(mor, sage.categories.map.FormalCompositeMap): - mor = ivf._middle_field().coerce_map_from(R) + mor = ivf.middle_field().coerce_map_from(R) if mor is not None and not isinstance(mor, sage.categories.map.FormalCompositeMap): return LazyWrapperMorphism(R, self) # We can skip the test for a coercion to RDF/CDF since RR/CC @@ -805,7 +805,7 @@ cdef class LazyFieldElement(FieldElement): """ return self.eval(R) - _mpfi_ = _mpfr_ = _complex_mpfr_field_ = _complex_mpfi_field_ = _generic_ + _real_mpfi_ = _complex_mpfi_ =_mpfr_ = _complex_mpfr_field_ = _generic_ def __complex__(self): """ @@ -1379,7 +1379,7 @@ cdef class LazyNamedUnop(LazyUnop): # not everything defined on interval fields # this is less info though, but mostly just want to print it interval_field = self._parent.interval_field() - return self.eval(interval_field._middle_field()) + return self.eval(interval_field.middle_field()) def __hash__(self): """ @@ -1530,7 +1530,7 @@ cdef class LazyConstant(LazyFieldElement): 3.141592653589793 """ interval_field = self._parent.interval_field() - return self.eval(interval_field._middle_field()) + return self.eval(interval_field.middle_field()) def __reduce__(self): """ diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index 38278e7ea4c..0cada5d3e48 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -253,14 +253,21 @@ from cysignals.signals cimport sig_on, sig_off from sage.libs.gmp.mpz cimport * from sage.libs.mpfr cimport * from sage.libs.mpfi cimport * +from sage.arith.constants cimport LOG_TEN_TWO_PLUS_EPSILON cimport sage.structure.element from sage.structure.element cimport RingElement, Element, ModuleElement +from sage.structure.parent cimport Parent from sage.structure.richcmp cimport richcmp +from .convert.mpfi cimport mpfi_set_sage from .real_mpfr cimport RealField_class, RealNumber, RealField from .integer cimport Integer from .real_double cimport RealDoubleElement +from .real_double import RDF +from .integer_ring import ZZ +from .rational_field import QQ +from sage.categories.morphism cimport Map cimport sage.rings.real_mpfr as real_mpfr @@ -282,11 +289,9 @@ import sage.rings.infinity printing_style = 'question' printing_error_digits = 0 -cdef double LOG_TEN_TWO_PLUS_EPSILON = 3.321928094887363 # a small overestimate of log(10,2) - #***************************************************************************** # -# Real Field +# Real Interval Field # #***************************************************************************** @@ -329,6 +334,7 @@ cpdef RealIntervalField_class RealIntervalField(prec=53, sci_not=False): RealIntervalField_cache[prec, sci_not] = R = RealIntervalField_class(prec, sci_not) return R + cdef class RealIntervalField_class(Field): """ Class of the real interval field. @@ -391,6 +397,8 @@ cdef class RealIntervalField_class(Field): sage: R200 = RealField(200) sage: RIF(R200.pi()) 3.141592653589794? + sage: RIF(10^100) + 1.000000000000000?e100 The base must be explicitly specified as a named parameter:: @@ -401,6 +409,11 @@ cdef class RealIntervalField_class(Field): sage: RIF('[1..3]').str(style='brackets') '[1.0000000000000000 .. 3.0000000000000000]' + All string-like types are accepted:: + + sage: RIF(b"100", u"100") + 100 + Next we coerce some 2-tuples, which define intervals:: sage: RIF((-1.5, -1.3)) @@ -419,7 +432,7 @@ cdef class RealIntervalField_class(Field): sage: RIF((1r,2r)).str(style='brackets') '[1.0000000000000000 .. 2.0000000000000000]' sage: RIF((pi, e)).str(style='brackets') - '[2.7182818284590455 .. 3.1415926535897932]' + '[2.7182818284590450 .. 3.1415926535897936]' Values which can be represented as an exact floating-point number (of the precision of this ``RealIntervalField``) result in a precise @@ -475,16 +488,21 @@ cdef class RealIntervalField_class(Field): TESTS:: - sage: RIF._lower_field() is RealField(53, rnd='RNDD') + sage: RIF(0, 10^200) + 1.?e200 + sage: RIF(10^100, 10^200) + 1.?e200 + sage: RIF.lower_field() is RealField(53, rnd='RNDD') True - sage: RIF._upper_field() is RealField(53, rnd='RNDU') + sage: RIF.upper_field() is RealField(53, rnd='RNDU') True - sage: RIF._middle_field() is RR + sage: RIF.middle_field() is RR True sage: TestSuite(RIF).run() """ + Element = RealIntervalFieldElement - def __init__(self, int prec=53, int sci_not=0): + def __init__(self, mpfr_prec_t prec=53, int sci_not=0): """ Initialize ``self``. @@ -505,45 +523,46 @@ cdef class RealIntervalField_class(Field): self.__upper_field = RealField(prec, sci_not, "RNDU") from sage.categories.fields import Fields Field.__init__(self, self, category=Fields().Infinite()) + self._populate_coercion_lists_(convert_method_name="_real_mpfi_") - def _lower_field(self): + def lower_field(self): """ Return the :class:`RealField_class` with rounding mode ``'RNDD'`` (rounding towards minus infinity). EXAMPLES:: - sage: RIF._lower_field() + sage: RIF.lower_field() Real Field with 53 bits of precision and rounding RNDD - sage: RealIntervalField(200)._lower_field() + sage: RealIntervalField(200).lower_field() Real Field with 200 bits of precision and rounding RNDD """ return self.__lower_field - def _middle_field(self): + def middle_field(self): """ Return the :class:`RealField_class` with rounding mode ``'RNDN'`` (rounding towards nearest). EXAMPLES:: - sage: RIF._middle_field() + sage: RIF.middle_field() Real Field with 53 bits of precision - sage: RealIntervalField(200)._middle_field() + sage: RealIntervalField(200).middle_field() Real Field with 200 bits of precision """ return self.__middle_field - def _upper_field(self): + def upper_field(self): """ Return the :class:`RealField_class` with rounding mode ``'RNDU'`` (rounding towards plus infinity). EXAMPLES:: - sage: RIF._upper_field() + sage: RIF.upper_field() Real Field with 53 bits of precision and rounding RNDU - sage: RealIntervalField(200)._upper_field() + sage: RealIntervalField(200).upper_field() Real Field with 200 bits of precision and rounding RNDU """ return self.__upper_field @@ -562,11 +581,11 @@ cdef class RealIntervalField_class(Field): Real Field with 200 bits of precision and rounding RNDD """ if rnd == "RNDD": - return self._lower_field() + return self.lower_field() elif rnd == "RNDN": - return self._middle_field() + return self.middle_field() elif rnd == "RNDU": - return self._upper_field() + return self.upper_field() else: return RealField(self.__prec, self.sci_not, rnd) @@ -639,18 +658,18 @@ cdef class RealIntervalField_class(Field): """ return False - def __call__(self, x, y=None, int base=10): + def __call__(self, x=None, y=None, **kwds): """ Create an element in this real interval field. INPUT: - - ``x`` - a number, string, or 2-tuple + - ``x`` -- a number, string, or 2-tuple - - ``y`` - (default: ``None``); if given ``x`` is set to ``(x,y)``; - this is so you can write ``R(2,3)`` to make the interval from 2 to 3 + - ``y`` -- (default: ``None``); if given ``x`` is set to ``(x,y)``; + this is so you can write ``R(2,3)`` to make the interval from 2 to 3 - - ``base`` - integer (default: 10) - only used if ``x`` is a string + - ``base`` -- integer (default: 10) - only used if ``x`` is a string OUTPUT: an element of this real interval field. @@ -670,9 +689,13 @@ cdef class RealIntervalField_class(Field): Type: RealIntervalField? for more information. """ - if not y is None: - x = (x, y) - return RealIntervalFieldElement(self, x, base) + # Note: we override Parent.__call__ because we want to support + # RIF(a, b) and that is hard to do using coerce maps. + if y is not None: + return RealIntervalFieldElement(self, [x, y], **kwds) + if kwds: + return RealIntervalFieldElement(self, x, **kwds) + return Parent.__call__(self, x) def algebraic_closure(self): """ @@ -713,9 +736,9 @@ cdef class RealIntervalField_class(Field): {'sci_not': self.scientific_notation(), 'type': 'Interval'}), sage.rings.rational_field.QQ) - cdef _coerce_c_impl(self, x): + cpdef _coerce_map_from_(self, S): """ - Canonical coercion of ``x`` to this mpfi real field. + Canonical coercion from ``S`` to this real interval field. The rings that canonically coerce to this mpfi real field are: @@ -734,28 +757,66 @@ cdef class RealIntervalField_class(Field): are coerced to a precise interval, with upper and lower bounds equal; otherwise, the upper and lower bounds will typically be adjacent floating-point numbers that surround the given value. + + EXAMPLES:: + + sage: phi = RIF.coerce_map_from(ZZ); phi + Coercion map: + From: Integer Ring + To: Real Interval Field with 53 bits of precision + sage: phi(3^100) + 5.153775207320114?e47 + sage: phi = RIF.coerce_map_from(float); phi + Coercion map: + From: Set of Python objects of class 'float' + To: Real Interval Field with 53 bits of precision + sage: phi(math.pi) + 3.1415926535897932? + + Coercion can decrease precision, but not increase it:: + + sage: phi = RIF.coerce_map_from(RealIntervalField(100)); phi + Coercion map: + From: Real Interval Field with 100 bits of precision + To: Real Interval Field with 53 bits of precision + sage: phi = RIF.coerce_map_from(RealField(100)); phi + Coercion map: + From: Real Field with 100 bits of precision + To: Real Interval Field with 53 bits of precision + sage: print(RIF.coerce_map_from(RealIntervalField(20))) + None + sage: print(RIF.coerce_map_from(RealField(20))) + None + + :: + + sage: phi = RIF.coerce_map_from(AA); phi + Conversion via _real_mpfi_ method map: + From: Algebraic Real Field + To: Real Interval Field with 53 bits of precision """ - if isinstance(x, real_mpfr.RealNumber): - P = x.parent() - if ( P).__prec >= self.__prec: - return self(x) - else: - raise TypeError("Canonical coercion from lower to higher precision not defined") - if isinstance(x, RealIntervalFieldElement): - P = x.parent() - if ( P).__prec >= self.__prec: - return self(x) - else: - raise TypeError("Canonical coercion from lower to higher precision not defined") - if isinstance(x, (Integer, Rational)): - return self(x) - cdef RealNumber lower, upper - try: - lower = self.__lower_field._coerce_(x) - upper = self.__upper_field._coerce_(x) - return self(lower, upper) - except TypeError as msg: - raise TypeError("no canonical coercion of element into self") + prec = self.__prec + + # Direct and efficient conversions + if S is ZZ or S is QQ: + return True + if S is float or S is int or S is long: + return True + if isinstance(S, RealIntervalField_class): + return (S).__prec >= prec + if isinstance(S, RealField_class): + return (S).__prec >= prec + if S is RDF: + return 53 >= prec + from .number_field.number_field import NumberField_quadratic + if isinstance(S, NumberField_quadratic): + return S.discriminant() > 0 + + # If coercion to RR is possible and there is a _real_mpfi_ + # method, assume that it defines a coercion to RIF + if self.__middle_field.has_coerce_map_from(S): + return self._convert_method_map(S, "_real_mpfi_") + return None def __richcmp__(self, other, int op): """ @@ -818,7 +879,7 @@ cdef class RealIntervalField_class(Field): sage: RIF.random_element(min=-100, max=0) -1.5803457307118123? """ - return self(self._middle_field().random_element(*args, **kwds)) + return self(self.middle_field().random_element(*args, **kwds)) def gen(self, i=0): """ @@ -1122,14 +1183,14 @@ cdef class RealIntervalField_class(Field): #***************************************************************************** # -# RealIntervalFieldElement -- element of Real Field +# RealIntervalFieldElement -- element of Real Interval Field # #***************************************************************************** cdef class RealIntervalFieldElement(RingElement): """ A real number interval. """ - def __cinit__(self, parent, x=None, base=None): + def __cinit__(self, parent, *args, **kwds): """ Initialize the parent of this element and allocate memory @@ -1156,12 +1217,18 @@ cdef class RealIntervalFieldElement(RingElement): mpfi_init2(self.value, p.__prec) self._parent = p - def __init__(self, parent, x=0, int base=10): + def __init__(self, parent, x, int base=10): """ Initialize a real interval element. Should be called by first creating a :class:`RealIntervalField`, as illustrated in the examples. + INPUT: + + - ``x`` -- a number, string, or 2-tuple + + - ``base`` -- integer (default: 10) - only used if ``x`` is a string + EXAMPLES:: sage: R = RealIntervalField() @@ -1199,62 +1266,9 @@ cdef class RealIntervalFieldElement(RingElement): Type: ``RealIntervalField?`` for many more examples. """ if x is None: - return - - cdef RealNumber ra, rb - cdef RealIntervalFieldElement d - - if isinstance(x, RealIntervalFieldElement): - mpfi_set(self.value, (x).value) - elif isinstance(x, RealNumber): - mpfi_set_fr(self.value, (x).value) - elif isinstance(x, Rational): - mpfi_set_q(self.value, (x).value) - elif isinstance(x, Integer): - mpfi_set_z(self.value, (x).value) - elif isinstance(x, RealDoubleElement): - mpfi_set_d(self.value, (x)._value) - elif isinstance(x, int): - mpfi_set_si(self.value, x) - elif isinstance(x, float): - mpfi_set_d(self.value, x) - elif hasattr(x, '_real_mpfi_'): - # TODO: this is a stupid useless copy! - # this case should be handled by coercion once - # sage.rings.ring.Ring get rid of old parent inheritance - d = x._real_mpfi_(self._parent) - mpfi_set(self.value, d.value) - elif isinstance(x, tuple): - try: - a, b = x - except ValueError: - raise TypeError("tuple defining an interval must have length 2") - if isinstance(a, RealNumber) and isinstance(b, RealNumber): - mpfi_interv_fr(self.value, (a).value, (b).value) - elif isinstance(a, RealDoubleElement) and isinstance(b, RealDoubleElement): - mpfi_interv_d(self.value, (a)._value, (b)._value) - elif isinstance(a, Rational) and isinstance(b, Rational): - mpfi_interv_q(self.value, (a).value, (b).value) - elif isinstance(a, Integer) and isinstance(b, Integer): - mpfi_interv_z(self.value, (a).value, (b).value) - elif isinstance(a, int) and isinstance(b, int): - mpfi_interv_si(self.value, a, b) - else: # generic fallback - ra = self._parent(a).lower() - rb = self._parent(b).upper() - mpfi_interv_fr(self.value, ra.value, rb.value) - elif isinstance(x, basestring): - s = str(x).replace('..', ',').replace(' ','').replace('+infinity', '@inf@').replace('-infinity','-@inf@') - if mpfi_set_str(self.value, s, base): - raise TypeError("unable to convert {!r} to real interval".format(x)) + mpfi_set_ui(self.value, 0) else: - # try coercing to real - try: - ra = self._parent._lower_field()(x) - rb = self._parent._upper_field()(x) - except TypeError: - raise TypeError("unable to convert {!r} to real interval".format(x)) - mpfi_interv_fr(self.value, ra.value, rb.value) + mpfi_set_sage(self.value, NULL, x, parent, base) def __reduce__(self): """ @@ -1877,7 +1891,7 @@ cdef class RealIntervalFieldElement(RingElement): cdef mp_exp_t self_exp cdef mpz_t self_zz - cdef int prec = (self._parent).__prec + cdef mpfr_prec_t prec = (self._parent).__prec cdef char *zz_str cdef size_t zz_str_maxlen @@ -3234,13 +3248,13 @@ cdef class RealIntervalFieldElement(RingElement): r""" The argument of this interval, if it is well-defined, in the complex sense. Otherwise raises a ``ValueError``. - + OUTPUT: - + - an element of the parent of this interval (0 or pi) - + EXAMPLES:: - + sage: RIF(1).argument() 0 sage: RIF(-1).argument() @@ -3268,7 +3282,7 @@ cdef class RealIntervalFieldElement(RingElement): return k.pi() else: raise ValueError("Can't take the argument of interval strictly containing zero") - + def unique_floor(self): """ Returns the unique floor of this interval, if it is well defined, @@ -5319,7 +5333,10 @@ def RealInterval(s, upper=None, int base=10, int pad=0, min_prec=53): else: bits = int(math.log(base,2)*1.00001*len(s)) R = RealIntervalField(prec=max(bits+pad, min_prec)) - return R(s, upper, base) + if upper is not None: + s = (s, upper) + return RealIntervalFieldElement(R, s, base) + # The default real interval field, with precision 53 bits RIF = RealIntervalField() From a09e8a63476b3595b65308afb0acb704cb77bedc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 9 Feb 2018 17:51:24 +0100 Subject: [PATCH 684/740] fixing one plantri doctest --- src/sage/graphs/graph_generators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index b3ec5ac8ef1..fa4a83f6b01 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -1119,7 +1119,7 @@ def _read_planar_code(self, code_input): for i, di in enumerate(g): Ni = di.count(i + 1) if Ni > 1: - edges_g[i + 1] += [i + 1] * (Ni / 2) + edges_g[i + 1] += [i + 1] * (Ni // 2) has_loops = True G = graph.Graph(edges_g, loops=has_loops) From 998935ee29856a07344a48388daf033f16a05875 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Fri, 9 Feb 2018 11:43:34 +0100 Subject: [PATCH 685/740] Various Cython fixes --- src/sage/combinat/integer_lists/invlex.pyx | 5 +++-- src/sage/misc/lazy_string.pyx | 5 ++--- .../rings/number_field/number_field_element_quadratic.pyx | 2 -- src/sage/sets/recursively_enumerated_set.pyx | 4 ++-- src/sage/structure/sage_object.pyx | 4 ++-- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/integer_lists/invlex.pyx b/src/sage/combinat/integer_lists/invlex.pyx index dd8200670cf..a3c9599b263 100644 --- a/src/sage/combinat/integer_lists/invlex.pyx +++ b/src/sage/combinat/integer_lists/invlex.pyx @@ -27,9 +27,10 @@ limitations and lack of robustness w.r.t. input. # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** + from __future__ import print_function, absolute_import -from builtins import object +import builtins from sage.misc.classcall_metaclass import ClasscallMetaclass, typecall from sage.misc.cachefunc import cached_method @@ -1088,7 +1089,7 @@ DECREASE = 2 POP = 1 STOP = 0 -class IntegerListsLexIter(object): +class IntegerListsLexIter(builtins.object): r""" Iterator class for IntegerListsLex. diff --git a/src/sage/misc/lazy_string.pyx b/src/sage/misc/lazy_string.pyx index cd849d89e8e..1d2ef92727e 100644 --- a/src/sage/misc/lazy_string.pyx +++ b/src/sage/misc/lazy_string.pyx @@ -405,7 +405,7 @@ cdef class _LazyString(object): else: return self * (<_LazyString>other).val() - def __richcmp__(self, other, int op): + def __richcmp__(_LazyString self, other, int op): """ EXAMPLES:: @@ -449,8 +449,7 @@ cdef class _LazyString(object): sage: s >= s True """ - self = (<_LazyString?>self).val() - return PyObject_RichCompare(self, other, op) + return PyObject_RichCompare(self.val(), other, op) def __getattr__(self, name): """ diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pyx b/src/sage/rings/number_field/number_field_element_quadratic.pyx index d2f90afe1ea..551c8e52cf4 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -1105,8 +1105,6 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): sage: K(123/567).continued_fraction_list() ((0, 4, 1, 1, 1, 1, 3, 2), ()) """ - cdef NumberFieldElement_quadratic x - if mpz_sgn(self.b) == 0: return tuple(Rational(self).continued_fraction_list()),() diff --git a/src/sage/sets/recursively_enumerated_set.pyx b/src/sage/sets/recursively_enumerated_set.pyx index 27facd9fc13..19bb20ca9af 100644 --- a/src/sage/sets/recursively_enumerated_set.pyx +++ b/src/sage/sets/recursively_enumerated_set.pyx @@ -447,9 +447,9 @@ cdef class RecursivelyEnumeratedSet_generic(Parent): sage: len(C) Traceback (most recent call last): ... - TypeError: 'NoneType' object cannot be interpreted as an index + TypeError: cannot compute length of A recursively enumerated set (breadth first search) """ - return None + raise TypeError(f"cannot compute length of {self}") def __iter__(self): r""" diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index 8eeeaaf7bf7..6a93a1330ce 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -1599,9 +1599,9 @@ def unpickle_all(dir, debug=False, run_test_suite=False): for A in sorted(os.listdir(dir)): if A.endswith('.sobj'): try: - object = load(os.path.join(dir,A)) + obj = load(os.path.join(dir,A)) if run_test_suite: - TestSuite(object).run(catch = False) + TestSuite(obj).run(catch = False) i += 1 except Exception: j += 1 From 240f0a9d81059208adf3ba74c25a87522f244aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Sat, 10 Feb 2018 10:09:43 +0200 Subject: [PATCH 686/740] Two corrections. --- src/sage/combinat/posets/posets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 94ec5fa6fc6..3db84851b88 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -6780,7 +6780,7 @@ def is_greedy(self, certificate=False): Getting a certificate:: sage: N = Poset({1: [3], 2: [3, 4]}) - sage: L.is_greedy(certificate=True) + sage: N.is_greedy(certificate=True) (False, ([1, 2, 4, 3], [2, 4, 1, 3])) TESTS:: @@ -6792,7 +6792,7 @@ def is_greedy(self, certificate=False): sage: posets.ChainPoset(3).is_greedy() True """ - H = P._hasse_diagram + H = self._hasse_diagram N1 = H.order()-1 it = H.greedy_linear_extensions_iterator() A = next(it) From 222025a4bb1358890bbc39b7ba645b1b94f12dd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Sat, 10 Feb 2018 10:53:45 +0200 Subject: [PATCH 687/740] Doc formatting. --- src/sage/combinat/posets/posets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 3db84851b88..c192d34d99d 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -6751,7 +6751,7 @@ def is_greedy(self, certificate=False): """ Return ``True`` if the poset is greedy, and ``False`` otherwise. - A poset is ''greedy'' if every greedy linear extension + A poset is *greedy* if every greedy linear extension has the same number of jumps. INPUT: From f9772bc2aa7b6603b551cc49250f9335201456e5 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Sat, 10 Feb 2018 10:38:25 +0100 Subject: [PATCH 688/740] Upgrade to MPFR-4.0.1 --- build/pkgs/mpfr/checksums.ini | 6 +++--- build/pkgs/mpfr/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/mpfr/checksums.ini b/build/pkgs/mpfr/checksums.ini index 2728dda9c04..508290ace26 100644 --- a/build/pkgs/mpfr/checksums.ini +++ b/build/pkgs/mpfr/checksums.ini @@ -1,4 +1,4 @@ tarball=mpfr-VERSION.tar.bz2 -sha1=799245347044c8f0da9e513f86bb5e4c07974931 -md5=ef619f3bb68039e35c4a219e06be72d0 -cksum=3469661192 +sha1=fcbbafb37c683898e585b926608d540ed037609e +md5=8c21d8ac7460493b2b9f3ef3cc610454 +cksum=3260550035 diff --git a/build/pkgs/mpfr/package-version.txt b/build/pkgs/mpfr/package-version.txt index fcdb2e109f6..1454f6ed4b7 100644 --- a/build/pkgs/mpfr/package-version.txt +++ b/build/pkgs/mpfr/package-version.txt @@ -1 +1 @@ -4.0.0 +4.0.1 From 68022a4d114951c39f63962c4b35b3feed405f93 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Sat, 10 Feb 2018 13:42:30 +0100 Subject: [PATCH 689/740] Sources in traceback are no longer in src --- src/sage/repl/interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/repl/interpreter.py b/src/sage/repl/interpreter.py index 9d401029ae2..21a6e527d62 100644 --- a/src/sage/repl/interpreter.py +++ b/src/sage/repl/interpreter.py @@ -80,7 +80,7 @@ in () ----> 1 Integer(1)/Integer(0) - .../src/sage/rings/integer.pyx in sage.rings.integer.Integer.__div__ (.../cythonized/sage/rings/integer.c:...)() + .../sage/rings/integer.pyx in sage.rings.integer.Integer.__div__ (.../cythonized/sage/rings/integer.c:...)() ....: if type(left) is type(right): ....: if mpz_sgn((right).value) == 0: -> ... raise ZeroDivisionError("rational division by zero") From 9e58f1f8f6fb0db84fec3fdc01b32e32dcf8700b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Sun, 11 Feb 2018 15:29:00 +1300 Subject: [PATCH 690/740] check if gcc is installed before setting need_to_install_gfortran --- configure.ac | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 919b5c2326e..5c61a89704e 100644 --- a/configure.ac +++ b/configure.ac @@ -660,9 +660,10 @@ AC_MSG_ERROR([["found Fink in $FINK_PATH. Either: fi fi -# if GCC needs to be installed GFORTRAN shouldn't be installed -# GCC will provide GFORTRAN in that case -if test $need_to_install_gcc = yes ; then +# if GCC needs to be installed GFORTRAN shouldn't be installed. +# if GCC is already installed GFORTRAN shouldn't be installed either. +# GCC will provide GFORTRAN in both cases. +if test x$need_to_install_gcc = xyes -o x$SAGE_INSTALL_GCC = xexists ; then need_to_install_gfortran=no fi From 2cfbc99decb479132d0a4ce3f9f079fac8e990fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Sun, 11 Feb 2018 07:44:06 +0200 Subject: [PATCH 691/740] Cleaner swap. --- src/sage/combinat/posets/posets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index c192d34d99d..d03190342a3 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -6803,7 +6803,7 @@ def is_greedy(self, certificate=False): if A_jumps != B_jumps: if certificate: if A_jumps > B_jumps: - tmp = A; A = B; B = tmp + A, B = B, A return (False, (self.linear_extension([self[v] for v in A]), self.linear_extension([self[v] for v in B]))) From 7f404f96714d4707878696b5515cbb13e34437cf Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sun, 11 Feb 2018 14:08:08 +0100 Subject: [PATCH 692/740] Hold reference to avoid garbage collection action of domain --- src/sage/rings/real_mpfr.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 9b5da88a595..e8b04a9ae4b 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -710,7 +710,8 @@ cdef class RealField_class(sage.rings.ring.Field): sage: 1.0 - ZZ(1) - int(1) - long(1) - QQ(1) - RealField(100)(1) - AA(1) - RLF(1) -6.00000000000000 - sage: RR['x'].get_action(ZZ) + sage: R = RR['x'] # Hold reference to avoid garbage collection, see Trac #24709 + sage: R.get_action(ZZ) Right scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Real Field with 53 bits of precision """ if S is ZZ: From 6f42457a8c14573156dae50aff0965d5f767a982 Mon Sep 17 00:00:00 2001 From: David Coudert Date: Sun, 11 Feb 2018 19:04:03 +0100 Subject: [PATCH 693/740] trac #24683: add parameter maximize to TSP and clean the method --- src/sage/graphs/generic_graph.py | 110 ++++++++++++++++--------------- 1 file changed, 57 insertions(+), 53 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index dc5cdd6018d..484d2d20bea 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -7298,7 +7298,9 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, return path if path[1].order() == self.order() else (0, None) return path if path.order() == self.order() else None - def traveling_salesman_problem(self, use_edge_labels = False, solver = None, constraint_generation = None, verbose = 0, verbose_constraints = False): + def traveling_salesman_problem(self, use_edge_labels=False, maximize=False, + solver=None, constraint_generation=None, + verbose=0, verbose_constraints=False): r""" Solves the traveling salesman problem (TSP) @@ -7321,6 +7323,11 @@ def traveling_salesman_problem(self, use_edge_labels = False, solver = None, con circuit returned is the one minimizing the sum of the weights (an edge with no label is assumed to have weight `1`). + - ``maximize`` -- boolean (default: ``False``). When set to ``True`` + search for a Hamiltonian cycle (res. circuit) of maximum weight + instead of minimum weight. This paramter is used only when + ``use_edge_labels`` is ``True``. + - ``solver`` -- (default: ``None``) Specify a Linear Program (LP) solver to be used. If set to ``None``, the default one is used. For more information on LP solvers and which default solver is used, see @@ -7417,6 +7424,16 @@ def traveling_salesman_problem(self, use_edge_labels = False, solver = None, con sage: sum( tsp.edge_labels() ) == (1/2)*10 True + Search for a minimum and a maximum weight Hamiltonian cycle:: + + sage: G = Graph([(0, 1, 1), (0, 2, 2), (0, 3, 1), (1, 2, 1), (1, 3, 2), (2, 3, 1)]) + sage: tsp = G.traveling_salesman_problem(use_edge_labels=True, maximize=False) + sage: print(sum(tsp.edge_labels())) + 4 + sage: tsp = G.traveling_salesman_problem(use_edge_labels=True, maximize=True) + sage: print(sum(tsp.edge_labels())) + 6 + TESTS: Comparing the results returned according to the value of @@ -7518,7 +7535,15 @@ def traveling_salesman_problem(self, use_edge_labels = False, solver = None, con """ from sage.categories.sets_cat import EmptySetError - weight = lambda l : 1 if l is None else l + # Associating a weight to a label + if use_edge_labels: + M = 1 + L = [l for l in self.edge_labels() if l is not None] + if L: + M = min(L) if maximize else max(L) + weight = lambda l: M if l is None else l + else: + weight = lambda l: 1 ######################## # 0 or 1 vertex graphs # @@ -7536,8 +7561,12 @@ def traveling_salesman_problem(self, use_edge_labels = False, solver = None, con if self.is_directed(): if self.has_edge(u,v) and self.has_edge(v,u): if self.has_multiple_edges(): - edges = [(u,v,min(self.edge_label(u,v), key=weight)), - (v,u,min(self.edge_label(v,u), key=weight))] + if maximize: + edges = [(u,v,max(self.edge_label(u,v), key=weight)), + (v,u,max(self.edge_label(v,u), key=weight))] + else: + edges = [(u,v,min(self.edge_label(u,v), key=weight)), + (v,u,min(self.edge_label(v,u), key=weight))] else: edges = [(u,v,self.edge_label(u,v)), (v,u,self.edge_label(v,u))] @@ -7578,37 +7607,16 @@ def traveling_salesman_problem(self, use_edge_labels = False, solver = None, con if not self.strong_orientation().is_strongly_connected(): raise EmptySetError("the given graph is not Hamiltonian") - ############################ - # Deal with multiple edges # - ############################ + ###################################### + # Deal with loops and multiple edges # + ###################################### if self.has_loops() or self.has_multiple_edges(): - g = copy(self) + keep_label = 'max' if (use_edge_labels and maximize) else 'min' + g = self.to_simple(to_undirected=False, keep_label=keep_label, immutable=False) else: g = self - if g.has_multiple_edges(): - multi = g.multiple_edges() - g.delete_edges(multi) - g.allow_multiple_edges(False) - if use_edge_labels: - e = {} - - # The weight of an edge is the minimum over the weights of the parallel edges - for u,v,l in multi: - if (u,v) in e: - e[u,v] = weight(l) if weight(l) < e[u,v] else e[u,v] - else: - e[u,v] = l - - g.add_edges([(u,v) for (u,v),l in iteritems(e)]) - - else: - g.add_edges(multi) - - if g.has_loops(): - g.remove_loops() - g.allow_loops(False) if constraint_generation is None: if g.density() > .7: @@ -7625,10 +7633,9 @@ def traveling_salesman_problem(self, use_edge_labels = False, solver = None, con if constraint_generation: - p = MixedIntegerLinearProgram(maximization = False, - solver = solver, - constraint_generation = True) - + p = MixedIntegerLinearProgram(maximization=maximize, + solver=solver, + constraint_generation=True) # Directed Case # ################# @@ -7640,14 +7647,14 @@ def traveling_salesman_problem(self, use_edge_labels = False, solver = None, con # Objective function if use_edge_labels: p.set_objective(p.sum([ weight(l)*b[u,v] - for u,v,l in g.edges()])) + for u,v,l in g.edge_iterator()])) # All the vertices have in-degree 1 and out-degree 1 for v in g: - p.add_constraint(p.sum([b[u,v] for u in g.neighbors_in(v)]), + p.add_constraint(p.sum([b[u,v] for u in g.neighbor_in_iterator(v)]), min = 1, max = 1) - p.add_constraint(p.sum([b[v,u] for u in g.neighbors_out(v)]), + p.add_constraint(p.sum([b[v,u] for u in g.neighbor_out_iterator(v)]), min = 1, max = 1) @@ -7660,7 +7667,7 @@ def traveling_salesman_problem(self, use_edge_labels = False, solver = None, con while True: # We build the DiGraph representing the current solution h = DiGraph() - for u,v,l in g.edges(): + for u,v,l in g.edge_iterator(): if p.get_values(b[u,v]) == 1: h.add_edge(u,v,l) @@ -7692,12 +7699,11 @@ def traveling_salesman_problem(self, use_edge_labels = False, solver = None, con # Objective function if use_edge_labels: - p.set_objective(p.sum([ weight(l)*B(u,v) - for u,v,l in g.edges()]) ) + p.set_objective(p.sum( weight(l)*B(u,v) for u,v,l in g.edge_iterator()) ) # All the vertices have degree 2 for v in g: - p.add_constraint(p.sum([ B(u,v) for u in g.neighbors(v)]), + p.add_constraint(p.sum( B(u,v) for u in g.neighbor_iterator(v)), min = 2, max = 2) @@ -7710,7 +7716,7 @@ def traveling_salesman_problem(self, use_edge_labels = False, solver = None, con while True: # We build the DiGraph representing the current solution h = Graph() - for u,v,l in g.edges(): + for u,v,l in g.edge_iterator(): if p.get_values(B(u,v)) == 1: h.add_edge(u,v,l) @@ -7744,7 +7750,7 @@ def traveling_salesman_problem(self, use_edge_labels = False, solver = None, con # ILP formulation without constraint generation # ################################################# - p = MixedIntegerLinearProgram(maximization = False, solver = solver) + p = MixedIntegerLinearProgram(maximization=maximize, solver=solver) f = p.new_variable(binary=True) r = p.new_variable(nonnegative=True) @@ -7760,11 +7766,11 @@ def traveling_salesman_problem(self, use_edge_labels = False, solver = None, con # All the vertices have in-degree 1 and out-degree 1 for v in g: - p.add_constraint(p.sum([ f[(u,v)] for u in g.neighbors_in(v)]), + p.add_constraint(p.sum(f[(u,v)] for u in g.neighbor_in_iterator(v)), min = 1, max = 1) - p.add_constraint(p.sum([ f[(v,u)] for u in g.neighbors_out(v)]), + p.add_constraint(p.sum(f[(v,u)] for u in g.neighbor_out_iterator(v)), min = 1, max = 1) @@ -7793,12 +7799,12 @@ def traveling_salesman_problem(self, use_edge_labels = False, solver = None, con # All the vertices have degree 2 for v in g: - p.add_constraint(p.sum([ E(u,v) for u in g.neighbors(v)]), + p.add_constraint(p.sum( E(u,v) for u in g.neighbor_iterator(v)), min = 2, max = 2) # r is greater than f - for u,v in g.edges(labels = None): + for u,v in g.edge_iterator(labels = None): p.add_constraint( r[(u,v)] + r[(v,u)] - E(u,v), min = 0) from sage.graphs.all import Graph @@ -7809,20 +7815,18 @@ def traveling_salesman_problem(self, use_edge_labels = False, solver = None, con # no cycle which does not contain x for v in g: if v != x: - p.add_constraint(p.sum([ r[(u,v)] for u in g.neighbors(v)]),max = 1-eps) + p.add_constraint(p.sum( r[(u,v)] for u in g.neighbor_iterator(v)),max = 1-eps) if use_edge_labels: - p.set_objective(p.sum([ weight(l)*E(u,v) for u,v,l in g.edges()]) ) - else: - p.set_objective(None) + p.set_objective(p.sum( weight(l)*E(u,v) for u,v,l in g.edge_iterator()) ) try: - obj = p.solve(log = verbose) + obj = p.solve(log=verbose) f = p.get_values(f) tsp.add_vertices(g.vertices()) tsp.set_pos(g.get_pos()) tsp.name("TSP from "+g.name()) - tsp.add_edges([(u,v,l) for u,v,l in g.edges() if E(u,v) == 1]) + tsp.add_edges([(u,v,l) for u,v,l in g.edge_iterator() if E(u,v) == 1]) return tsp From 02bab0f65a3c2b53ca1184460ef811f1d44b347e Mon Sep 17 00:00:00 2001 From: David Coudert Date: Sun, 11 Feb 2018 20:02:39 +0100 Subject: [PATCH 694/740] trac #24683: new implementation of hamiltonian path --- src/sage/graphs/generic_graph.py | 145 +++++++++++++++++++++++++++---- 1 file changed, 128 insertions(+), 17 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 484d2d20bea..cf679b74bdc 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -7183,7 +7183,7 @@ def longest_path(self, s=None, t=None, use_edge_labels=False, algorithm="MILP", return g def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, - algorithm='MILP', solver=None, verbose=0): + maximize=False, algorithm='MILP', solver=None, verbose=0): r""" Return a Hamiltonian path of the current graph/digraph. @@ -7191,8 +7191,9 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, once. Computing a Hamiltonian path being NP-Complete, this algorithm could run for some time depending on the instance. - When ``use_edge_labels == True``, this method returns a minimum - weight hamiltonian path. + When ``use_edge_labels == True``, this method returns either a minimum + weight hamiltonian path or a maximum weight Hamiltonian path (if + ``maximize == True``). .. SEEALSO:: @@ -7213,6 +7214,11 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, labels on the edges are to be considered as weights (a label set to ``None`` or ``{}`` being considered as a weight of `1`) + - ``maximize`` -- boolean (default: ``False``); whether to compute a + minimum (default) of a maximum (when ``maximize == True``) weight + hamiltonian path. This parameter is considered only if + ``use_edge_labels == True``. + - ``algorithm`` -- one of ``"MILP"`` (default) or ``"backtrack"``; two remarks on this respect: @@ -7246,11 +7252,11 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, sage: g = graphs.Grid2dGraph(3, 3) sage: g.hamiltonian_path() - Subgraph of (2D Grid Graph for [3, 3]): Graph on 9 vertices + Hamiltonian path from 2D Grid Graph for [3, 3]: Graph on 9 vertices sage: g.hamiltonian_path(s=(0,0), t=(2,2)) - Subgraph of (2D Grid Graph for [3, 3]): Graph on 9 vertices + Hamiltonian path from 2D Grid Graph for [3, 3]: Graph on 9 vertices sage: g.hamiltonian_path(s=(0,0), t=(2,2), use_edge_labels=True) - (8, Subgraph of (2D Grid Graph for [3, 3]): Graph on 9 vertices) + (8, Hamiltonian path from 2D Grid Graph for [3, 3]: Graph on 9 vertices) sage: g.hamiltonian_path(s=(0,0), t=(0,1)) is None True sage: g.hamiltonian_path(s=(0,0), t=(0,1), use_edge_labels=True) @@ -7283,6 +7289,14 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, sage: g = DiGraph(2) sage: g.hamiltonian_path() is None True + + Asking for a minimum (resp., maximum) weight Hamiltonian path:: + + sage: G = Graph([(0, 1, 1), (0, 2, 2), (0, 3, 1), (1, 2, 1), (1, 3, 2), (2, 3, 1)]) + sage: print(G.hamiltonian_path(s=0, t=1, use_edge_labels=True, maximize=False)[0]) + 3 + sage: print(G.hamiltonian_path(s=0, t=1, use_edge_labels=True, maximize=True)[0]) + 5 """ if self.order() < 2: raise ValueError('the Hamiltonian path problem is not well ' + @@ -7291,12 +7305,113 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, if not self.is_connected(): return (0, None) if use_edge_labels else None - path = self.longest_path(s=s, t=t, use_edge_labels=use_edge_labels, - algorithm=algorithm, solver=solver, - verbose=verbose) - if use_edge_labels: - return path if path[1].order() == self.order() else (0, None) - return path if path.order() == self.order() else None + # + # Deal with loops and multiple edges + # + if self.has_loops() or self.has_multiple_edges(): + keep_label = 'max' if (use_edge_label and maximize) else 'min' + g = self.to_simple(to_undirected=False, keep_label=keep_label, immutable=False) + else: + g = copy(self) + + + new_s, new_t = s, t + if g.is_directed(): + # + # Deal with vertices with no in|out-neighbors + # + zeros = [u for u in g if g.in_degree(u)==0] + if len(zeros) > 1: + return (0, None) if use_edge_labels else None + elif len(zeros) == 1: + if new_s is None: + new_s = zeros.pop() + elif not new_s in zeros: + return (0, None) if use_edge_labels else None + + zeros = [u for u in g if g.out_degree(u)==0] + if len(zeros) > 1: + return (0, None) if use_edge_labels else None + elif len(zeros) == 1: + if new_t is None: + new_t = zeros.pop() + elif not new_t in zeros: + return (0, None) if use_edge_labels else None + + else: + # + # Deal with vertices of degree one + # + ones = [u for u in g if g.degree(u)==1] + if len(ones) > 2: + return (0, None) if use_edge_labels else None + + elif len(ones) == 2: + if not new_s is None and not new_s in ones: + return (0, None) if use_edge_labels else None + if not new_t is None and not new_t in ones: + return (0, None) if use_edge_labels else None + + # Set new_s and new_t if possible + if new_s is None and new_t is None: + new_s,new_t = ones + elif not new_s is None and new_t is None: + new_t = ones[1] if new_s == ones[0] else ones[0] + elif new_s is None and not new_t is None: + new_s = ones[1] if new_t == ones[0] else ones[0] + + elif len(ones) == 1: + if not new_s is None and not new_t is None and not (new_s in ones or new_t in ones): + return (0, None) if use_edge_labels else None + elif new_s is None and (new_t is None or (not new_t is None and not new_t in ones)): + new_s = ones.pop() + elif new_t is None and not new_s is None and not new_s in ones: + new_t = ones.pop() + + + if not use_edge_labels and algorithm == "backtrack": + path = g.longest_path(s=new_s, t=new_t, algorithm="backtrack") + return path if path.order() == g.order() else None + + + # We modify the graph to search for a Hamiltonian Cycle starting from + # new_s (if determined) and ending at new_t (if determined) + extra_vertices = [] + if new_s is None: + new_s = g.add_vertex() + extra_vertices.append(new_s) + for u in self: # in original set of vertices + g.add_edge(new_s, u, 0) + + if new_t is None: + new_t = g.add_vertex() + extra_vertices.append(new_t) + for u in self: + g.add_edge(u, new_t, 0) + + # We force the Hamiltonian cycle to visit new_s right after new_t by + # inserting an intermediate vertex + v = g.add_vertex() + extra_vertices.append(v) + g.add_edge(new_t, v, 0) + g.add_edge(v, new_s, 0) + + # + # We now search for an Hamiltonian Cycle in g + # + from sage.categories.sets_cat import EmptySetError + try: + tsp = g.traveling_salesman_problem(use_edge_labels=use_edge_labels, + maximize=maximize, + solver=solver, verbose=verbose) + except EmptySetError: + return (0, None) if use_edge_labels else None + + tsp.delete_vertices(extra_vertices) + tsp.name("Hamiltonian path from {}".format(self.name())) + weight = lambda l: 1 if l is None else l + return (sum(map(weight,tsp.edge_labels())), tsp) if use_edge_labels else tsp + def traveling_salesman_problem(self, use_edge_labels=False, maximize=False, solver=None, constraint_generation=None, @@ -7537,11 +7652,7 @@ def traveling_salesman_problem(self, use_edge_labels=False, maximize=False, # Associating a weight to a label if use_edge_labels: - M = 1 - L = [l for l in self.edge_labels() if l is not None] - if L: - M = min(L) if maximize else max(L) - weight = lambda l: M if l is None else l + weight = lambda l: 1 if l is None else l else: weight = lambda l: 1 From 2014769fc55baaaf47dc5d9f775c2659b2f6955a Mon Sep 17 00:00:00 2001 From: David Coudert Date: Sun, 11 Feb 2018 20:08:59 +0100 Subject: [PATCH 695/740] trac #24683: remove trailing spaces --- src/sage/graphs/generic_graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index cf679b74bdc..41dfcb85c79 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -7399,7 +7399,7 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, # # We now search for an Hamiltonian Cycle in g # - from sage.categories.sets_cat import EmptySetError + from sage.categories.sets_cat import EmptySetError try: tsp = g.traveling_salesman_problem(use_edge_labels=use_edge_labels, maximize=maximize, From 7ed2aa632eab67f632b861dabae17b4728982f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Mon, 12 Feb 2018 13:58:47 +1300 Subject: [PATCH 696/740] Add upstream patch to ppl to support clang-5 --- build/pkgs/ppl/package-version.txt | 2 +- build/pkgs/ppl/patches/clang5-support.patch | 37 +++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/ppl/patches/clang5-support.patch diff --git a/build/pkgs/ppl/package-version.txt b/build/pkgs/ppl/package-version.txt index 5625e59da88..92ab3427e80 100644 --- a/build/pkgs/ppl/package-version.txt +++ b/build/pkgs/ppl/package-version.txt @@ -1 +1 @@ -1.2 +1.2.p1 diff --git a/build/pkgs/ppl/patches/clang5-support.patch b/build/pkgs/ppl/patches/clang5-support.patch new file mode 100644 index 00000000000..648ff59aa30 --- /dev/null +++ b/build/pkgs/ppl/patches/clang5-support.patch @@ -0,0 +1,37 @@ +From: Roberto Bagnara +Date: Sun, 11 Feb 2018 08:11:09 +0000 (+0100) +Subject: Added missing "template" and "typename" keywords. +X-Git-Url: http://www.cs.unipr.it/git/gitweb.cgi?p=ppl%2Fppl.git;a=commitdiff_plain;h=c39f6a07b51f89e365b05ba4147aa2aa448febd7;hp=3a5e1e20a94cffb830182f22132b153d6691b7c5 + +Added missing "template" and "typename" keywords. +(Thanks to Dmitrii Pasechnik.) +--- + +diff --git a/src/Determinate_inlines.hh b/src/Determinate_inlines.hh +index 2749953..5b47275 100644 +--- a/src/Determinate_inlines.hh ++++ b/src/Determinate_inlines.hh +@@ -289,8 +289,8 @@ operator()(Determinate& x, const Determinate& y) const { + + template + template +-inline +-Determinate::Binary_Operator_Assign_Lifter ++inline typename ++Determinate::template Binary_Operator_Assign_Lifter + Determinate::lift_op_assign(Binary_Operator_Assign op_assign) { + return Binary_Operator_Assign_Lifter(op_assign); + } +diff --git a/src/OR_Matrix_inlines.hh b/src/OR_Matrix_inlines.hh +index b20b697..8124b7f 100644 +--- a/src/OR_Matrix_inlines.hh ++++ b/src/OR_Matrix_inlines.hh +@@ -97,7 +97,7 @@ OR_Matrix::Pseudo_Row::Pseudo_Row(const Pseudo_Row& y) + + template + template +-inline OR_Matrix::Pseudo_Row& ++inline typename OR_Matrix::template Pseudo_Row& + OR_Matrix::Pseudo_Row::operator=(const Pseudo_Row& y) { + first = y.first; + #if PPL_OR_MATRIX_EXTRA_DEBUG From 2e7b61fef9cdebed5a29a815fe8b1fcdb919b84a Mon Sep 17 00:00:00 2001 From: David Coudert Date: Mon, 12 Feb 2018 09:08:42 +0100 Subject: [PATCH 697/740] trac #24683: add s to use_edge_labels --- src/sage/graphs/generic_graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 41dfcb85c79..ac91a47999c 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -7309,7 +7309,7 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, # Deal with loops and multiple edges # if self.has_loops() or self.has_multiple_edges(): - keep_label = 'max' if (use_edge_label and maximize) else 'min' + keep_label = 'max' if (use_edge_labels and maximize) else 'min' g = self.to_simple(to_undirected=False, keep_label=keep_label, immutable=False) else: g = copy(self) From 1cdff6319ca437b745d5d2bf0fd8dca2a2ea3232 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 12 Feb 2018 10:37:28 +0100 Subject: [PATCH 698/740] Upgrade cypari2 --- build/pkgs/cypari/checksums.ini | 6 +++--- build/pkgs/cypari/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/cypari/checksums.ini b/build/pkgs/cypari/checksums.ini index fa58aa5691e..98475cd6032 100644 --- a/build/pkgs/cypari/checksums.ini +++ b/build/pkgs/cypari/checksums.ini @@ -1,4 +1,4 @@ tarball=cypari2-VERSION.tar.gz -sha1=9e1848b5dc566133edad79437fd39e298e336642 -md5=f268f870d255bb98bd4e917f0d74a463 -cksum=3159551499 +sha1=d27b8a80470514471a59e38f3e7248c677c58abb +md5=38149046fbdbecc390ce45e28fc10f8a +cksum=296167081 diff --git a/build/pkgs/cypari/package-version.txt b/build/pkgs/cypari/package-version.txt index 781dcb07cd8..65087b4f5ec 100644 --- a/build/pkgs/cypari/package-version.txt +++ b/build/pkgs/cypari/package-version.txt @@ -1 +1 @@ -1.1.3 +1.1.4 From be956dfdae833dd193396c8d45c2c3869ff15277 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Mon, 27 Nov 2017 14:03:35 +0000 Subject: [PATCH 699/740] =?UTF-8?q?#=20Ceci=20est=20la=20combinaison=20de?= =?UTF-8?q?=202=20commits.=20#=20Ceci=20est=20le=20premier=20message=20de?= =?UTF-8?q?=20validation=C2=A0:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update these tests to make sense on Python 3: * Remove some "# optional - python2" tags since they don't really help to skip tests on Python 3 * Rewrite the load() tests to work on Python 3: 1) replace print statements with print function 2) the main point of some of these tests is to demonstrate when .sage format parsing is or is not taking place; to demonstrate this the tests used division as an example, but since the default '/' operator has different behavior on Python 3 this did not work, so instead we use interpretation of the '^' for our examples * Replaced more examples of 'open(...)' with 'with open(...)' to prevent ResourceWarnings from causing the tests to fail # Ceci est le message de validation numéro 2 : Fixed this code to account for how the base64 module works differently on Python 3 --- src/sage/repl/load.py | 57 ++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/src/sage/repl/load.py b/src/sage/repl/load.py index e6028426aef..f1591e0d64a 100644 --- a/src/sage/repl/load.py +++ b/src/sage/repl/load.py @@ -16,6 +16,8 @@ import os import base64 +from sage.cpython.string import str_to_bytes, bytes_to_str, FS_ENCODING + def is_loadable_filename(filename): """ Returns whether a file can be loaded into Sage. This checks only @@ -96,33 +98,36 @@ def load(filename, globals, attach=False): Note that ``.py`` files are *not* preparsed:: sage: t = tmp_filename(ext='.py') - sage: _ = open(t,'w').write("print 'hi', 2/3; z = -2/7") + sage: with open(t, 'w') as f: + ....: _ = f.write("print(('hi', 2^3)); z = -2^7") sage: z = 1 - sage: sage.repl.load.load(t, globals()) # py2 - hi 0 - sage: z # py2 - -1 + sage: sage.repl.load.load(t, globals()) + ('hi', 1) + sage: z + -7 A ``.sage`` file *is* preparsed:: sage: t = tmp_filename(ext='.sage') - sage: _ = open(t,'w').write("print 'hi', 2/3; z = -2/7") + sage: with open(t, 'w') as f: + ....: _ = f.write("print(('hi', 2^3)); z = -2^7") sage: z = 1 sage: sage.repl.load.load(t, globals()) - hi 2/3 + ('hi', 8) sage: z - -2/7 + -128 Cython files are *not* preparsed:: sage: t = tmp_filename(ext='.pyx') - sage: _ = open(t,'w').write("print 'hi', 2/3; z = -2/7") + sage: with open(t, 'w') as f: + ....: _ = f.write("print(('hi', 2^3)); z = -2^7") sage: z = 1 sage: sage.repl.load.load(t, globals()) Compiling ... - hi 0 + ('hi', 1) sage: z - -1 + -7 If the file isn't a Cython, Python, or a Sage file, a ValueError is raised:: @@ -145,7 +150,8 @@ def load(filename, globals, attach=False): We attach a file:: sage: t = tmp_filename(ext='.py') - sage: _ = open(t,'w').write("print 'hello world'") + sage: with open(t, 'w') as f: + ....: _ = f.write("print('hello world')") sage: sage.repl.load.load(t, globals(), attach=True) hello world sage: t in attached_files() @@ -166,10 +172,12 @@ def load(filename, globals, attach=False): sage: load_attach_path() ['.'] sage: t_dir = tmp_dir() - sage: fullpath = os.path.join(t_dir, 'test.py') - sage: _ = open(fullpath, 'w').write("print 37 * 3") + sage: fname = tmp_filename(ext='.py') + sage: fullpath = os.path.join(t_dir, fname) + sage: with open(fullpath, 'w') as f: + ....: _ = f.write("print(37 * 3)") sage: load_attach_path(t_dir) - sage: attach('test.py') + sage: attach(fname) 111 sage: sage.repl.attach.reset(); reset_load_attach_path() # clean up @@ -185,7 +193,9 @@ def load(filename, globals, attach=False): Make sure that load handles filenames with spaces in the name or path:: - sage: t = tmp_filename(ext=' b.sage'); _ = open(t,'w').write("print 2") + sage: t = tmp_filename(ext=' b.sage') + sage: with open(t, 'w') as f: + ....: _ = f.write("print(2)") sage: sage.repl.load.load(t, globals()) 2 @@ -244,7 +254,8 @@ def load(filename, globals, attach=False): # Preparse in memory only for speed. if attach: add_attached_file(fpath) - exec(preparse_file(open(fpath).read()) + "\n", globals) + with open(fpath) as f: + exec(preparse_file(f.read()) + "\n", globals) elif ext == '.spyx' or ext == '.pyx': if attach: add_attached_file(fpath) @@ -286,8 +297,14 @@ def load_wrap(filename, attach=False): 'sage.repl.load.load(sage.repl.load.base64.b64decode("Zm9vLnB5"),globals(),True)' sage: sage.repl.load.load_wrap('foo.sage') 'sage.repl.load.load(sage.repl.load.base64.b64decode("Zm9vLnNhZ2U="),globals(),False)' - sage: sage.repl.load.base64.b64decode("Zm9vLnNhZ2U=") - 'foo.sage' + sage: m = sage.repl.load.base64.b64decode("Zm9vLnNhZ2U=") + sage: m == b'foo.sage' + True """ + + # Note: On Python 3 b64encode only accepts bytes, and returns bytes (yet + # b64decode does accept str, but always returns bytes) + b64 = base64.b64encode(str_to_bytes(filename, FS_ENCODING, + "surrogateescape")) return 'sage.repl.load.load(sage.repl.load.base64.b64decode("{}"),globals(),{})'.format( - base64.b64encode(filename), attach) + bytes_to_str(b64, 'ascii'), attach) From e3ee2fc0d5b6f40abd093011af03bc02293c4098 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Wed, 10 Jan 2018 13:00:38 +0000 Subject: [PATCH 700/740] Minor Python 3 fixes for sage.libs.pynac --- src/sage/libs/pynac/pynac.pyx | 53 ++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/src/sage/libs/pynac/pynac.pyx b/src/sage/libs/pynac/pynac.pyx index 829e2b24412..69672a34514 100644 --- a/src/sage/libs/pynac/pynac.pyx +++ b/src/sage/libs/pynac/pynac.pyx @@ -16,6 +16,8 @@ Pynac interface from __future__ import absolute_import, division, print_function +from six import integer_types + from cpython cimport * from libc cimport math @@ -27,7 +29,7 @@ from sage.libs.gsl.gamma cimport gsl_sf_lngamma_complex_e from sage.libs.mpmath import utils as mpmath_utils from sage.libs.pari.all import pari -from sage.cpython.string cimport str_to_bytes +from sage.cpython.string cimport str_to_bytes, char_to_str from sage.arith.all import gcd, lcm, is_prime, factorial, bernoulli @@ -290,7 +292,7 @@ cdef subs_args_to_PyTuple(const GExMap& map, unsigned options, const GExVector& sage: tfunc = TFunc() sage: tfunc(x).subs(x=1) len(args): 3, types: [, - <... 'int'>, # 64-bit + , # 64-bit , # 32-bit ] x @@ -380,7 +382,7 @@ cdef stdstring* string_from_pystr(py_str) except NULL: s = b"(INVALID)" # Avoid segfaults for invalid input return new stdstring(s) -cdef stdstring* py_latex_variable(char* var_name): +cdef stdstring* py_latex_variable(var_name): """ Returns a c++ string containing the latex representation of the given variable name. @@ -405,7 +407,6 @@ cdef stdstring* py_latex_variable(char* var_name): sage: py_latex_variable('beta_00') \beta_{00} """ - cdef Py_ssize_t slen from sage.misc.latex import latex_variable_name py_vlatex = latex_variable_name(var_name) return string_from_pystr(py_vlatex) @@ -422,7 +423,7 @@ def py_latex_variable_for_doctests(x): \sigma """ cdef stdstring* ostr = py_latex_variable(x) - print(ostr.c_str()) + print(char_to_str(ostr.c_str())) del ostr def py_print_function_pystring(id, args, fname_paren=False): @@ -661,7 +662,7 @@ def py_print_fderivative_for_doctests(id, params, args): """ cdef stdstring* ostr = py_print_fderivative(id, params, args) - print(ostr.c_str()) + print(char_to_str(ostr.c_str())) del ostr cdef stdstring* py_latex_fderivative(unsigned id, params, @@ -747,7 +748,7 @@ def py_latex_fderivative_for_doctests(id, params, args): \mathrm{D}_{0, 1, 0, 1}func_with_args(x, y^z) """ cdef stdstring* ostr = py_latex_fderivative(id, params, args) - print(ostr.c_str()) + print(char_to_str(ostr.c_str())) del ostr ################################################################# @@ -985,8 +986,7 @@ cdef py_real(x): sage: py_real(complex(2,2)) 2.0 """ - if type(x) is float or type(x) is int or \ - type(x) is long: + if type(x) is float or type(x) in integer_types: return x elif type(x) is complex: return x.real @@ -1081,9 +1081,9 @@ cdef py_conjugate(x): return x # assume is real since it doesn't have an imag attribute. cdef bint py_is_rational(x): - return type(x) is Rational or \ - type(x) is Integer or\ - isinstance(x, int) or isinstance(x, long) + return (type(x) is Rational or + type(x) is Integer or + isinstance(x, integer_types)) cdef bint py_is_equal(x, y): """ @@ -1117,10 +1117,10 @@ cdef bint py_is_integer(x): sage: py_is_integer(3.0r) False """ - return isinstance(x, int) or isinstance(x, long) or isinstance(x, Integer) or \ - (isinstance(x, Element) and - ((x)._parent.is_exact() or (x)._parent == ring.SR) and - (x in ZZ)) + return (isinstance(x, integer_types + (Integer,)) or + (isinstance(x, Element) and + ((x)._parent.is_exact() or + (x)._parent == ring.SR) and (x in ZZ))) def py_is_integer_for_doctests(x): """ @@ -1177,8 +1177,8 @@ def py_is_crational_for_doctest(x): return py_is_crational(x) cdef bint py_is_real(a): - if type(a) is int or isinstance(a, Integer) or\ - type(a) is long or type(a) is float: + if (type(a) in integer_types or isinstance(a, Integer) or + type(a) is float): return True try: P = parent(a) @@ -1201,10 +1201,13 @@ cdef bint py_is_prime(n): pass return False + cdef bint py_is_exact(x): - return isinstance(x, int) or isinstance(x, long) or isinstance(x, Integer) or \ - (isinstance(x, Element) and - ((x)._parent.is_exact() or (x)._parent == ring.SR)) + return (isinstance(x, integer_types + (Integer,)) or + (isinstance(x, Element) and + ((x)._parent.is_exact() or + (x)._parent == ring.SR))) + cdef py_numer(n): """ @@ -1232,7 +1235,7 @@ cdef py_numer(n): sage: py_numer(no_numer()) 42 """ - if isinstance(n, (int, long, Integer)): + if isinstance(n, integer_types + (Integer,)): return n try: return n.numerator() @@ -1270,7 +1273,7 @@ cdef py_denom(n): sage: py_denom(2/3*i) 3 """ - if isinstance(n, (int, long, Integer)): + if isinstance(n, integer_types + (Integer,)): return 1 try: return n.denominator() @@ -1391,7 +1394,7 @@ cdef py_tgamma(x): sage: py_tgamma(1/2) 1.77245385090552 """ - if type(x) is int or type(x) is long: + if type(x) in integer_types: x = float(x) if type(x) is float: return math.tgamma(PyFloat_AS_DOUBLE(x)) @@ -1712,7 +1715,7 @@ cdef py_log(x): """ cdef gsl_complex res cdef double real, imag - if type(x) is int or type(x) is long: + if type(x) in integer_types: x = float(x) if type(x) is float: real = PyFloat_AS_DOUBLE(x) From 2568492aea64d516c2216e5d05e2548c23c66215 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Mon, 12 Feb 2018 17:23:23 +0100 Subject: [PATCH 701/740] 24668: fix doctest --- src/sage/symbolic/expression.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 068af9df15f..809c0e1de58 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -8323,7 +8323,7 @@ cdef class Expression(CommutativeRingElement): arctan2(I, 1) sage: SR(CDF(0,1)).arctan2(1) NaN + +infinity*I - sage: SR(1).arctan2(CDF(0,1)) + sage: SR(1).arctan2(CDF(0,1)) # known bug 0.7853981633974484 - 19.012501686914433*I sage: arctan2(0,oo) From 040cc3ff694bfc05ee9a7dff0b14025642335204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 12 Feb 2018 17:56:28 +0100 Subject: [PATCH 702/740] trac 24286 fixing doctest --- src/sage/repl/attach.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/repl/attach.py b/src/sage/repl/attach.py index f3a33c47e42..f8935a42189 100644 --- a/src/sage/repl/attach.py +++ b/src/sage/repl/attach.py @@ -39,7 +39,7 @@ ....: traceback.print_exc() Traceback (most recent call last): ... - exec(preparse_file(open(fpath).read()) + "\n", globals) + exec(preparse_file(f.read()) + "\n", globals) File "", line 3, in ValueError: third sage: detach(src) From 6e4c5be1bd5742518acc62af7b98347d63931d5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 12 Feb 2018 21:04:11 +0100 Subject: [PATCH 703/740] fix wrong import of gamma --- .../asymptotics_multivariate_generating_functions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py index 9b4b950017f..e029712c139 100644 --- a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +++ b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py @@ -1785,7 +1785,8 @@ def asymptotics_smooth(self, p, alpha, N, asy_var, coordinate=None, """ from sage.calculus.functions import jacobian from sage.calculus.var import function - from sage.functions.other import factorial, gamma, sqrt + from sage.functions.other import factorial, sqrt + from sage.functions.gamma import gamma from sage.functions.log import exp, log from sage.matrix.constructor import matrix from sage.modules.free_module_element import vector From e0b296e5acdda871085f813406105fa4adb42176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Tue, 13 Feb 2018 11:40:25 +1300 Subject: [PATCH 704/740] Update sqlite to 3.22 may solve issue with xcode 7 --- build/pkgs/sqlite/checksums.ini | 6 +++--- build/pkgs/sqlite/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/sqlite/checksums.ini b/build/pkgs/sqlite/checksums.ini index ed3c939efef..c81f76dd9e8 100644 --- a/build/pkgs/sqlite/checksums.ini +++ b/build/pkgs/sqlite/checksums.ini @@ -1,4 +1,4 @@ tarball=sqlite-VERSION.tar.gz -sha1=7bcff1c158ed9e2c0e159c1b4b6c36d4d65dff8c -md5=450a95a7bde697c9fe4de9ae2fffdcca -cksum=2662976810 +sha1=2fb24ec12001926d5209d2da90d252b9825366ac +md5=96b5648d542e8afa6ab7ffb8db8ddc3d +cksum=1556846983 diff --git a/build/pkgs/sqlite/package-version.txt b/build/pkgs/sqlite/package-version.txt index b90c4601d25..b3793d498a2 100644 --- a/build/pkgs/sqlite/package-version.txt +++ b/build/pkgs/sqlite/package-version.txt @@ -1 +1 @@ -autoconf-3170000.p0 +autoconf-3220000.p0 From 9ac1612c342c66a5d574a6c824b064e2a2ec076c Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 12 Feb 2018 13:22:01 +0100 Subject: [PATCH 705/740] Cleanup for real/complex interval fields --- .../en/reference/rings_numerical/index.rst | 1 - src/sage/rings/complex_interval.pxd | 15 +- src/sage/rings/complex_interval.pyx | 121 +++++------- src/sage/rings/complex_interval_field.py | 49 +++-- .../number_field_element_quadratic.pyx | 4 +- src/sage/rings/real_interval_field.py | 5 + src/sage/rings/real_mpfi.pxd | 7 +- src/sage/rings/real_mpfi.pyx | 177 ++---------------- 8 files changed, 98 insertions(+), 281 deletions(-) diff --git a/src/doc/en/reference/rings_numerical/index.rst b/src/doc/en/reference/rings_numerical/index.rst index 9d396fe1559..b8bcfe8fd6f 100644 --- a/src/doc/en/reference/rings_numerical/index.rst +++ b/src/doc/en/reference/rings_numerical/index.rst @@ -36,7 +36,6 @@ ComplexBallField). :maxdepth: 2 sage/rings/real_mpfi - sage/rings/real_interval_field sage/rings/real_interval_absolute sage/rings/complex_interval_field sage/rings/complex_interval diff --git a/src/sage/rings/complex_interval.pxd b/src/sage/rings/complex_interval.pxd index 0b9d8a55464..eebcb2acbfa 100644 --- a/src/sage/rings/complex_interval.pxd +++ b/src/sage/rings/complex_interval.pxd @@ -2,7 +2,7 @@ from sage.libs.mpfr.types cimport mpfr_prec_t from sage.libs.mpfi.types cimport mpfi_t cimport sage.structure.element -from .real_mpfi cimport RealIntervalFieldElement +from .real_mpfi cimport RealIntervalFieldElement, RealIntervalField_class cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): @@ -10,11 +10,6 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): cdef mpfi_t __im cdef mpfr_prec_t _prec - cpdef _add_(self, other) - cpdef _mul_(self, other) - cdef RealIntervalFieldElement abs_c(ComplexIntervalFieldElement self) - cdef RealIntervalFieldElement norm_c(ComplexIntervalFieldElement self) - cdef inline ComplexIntervalFieldElement _new(self): """ Quickly create a new complex interval with the same parent as @@ -22,3 +17,11 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): """ cdef type t = type(self) return t.__new__(t, self._parent) + + cdef inline RealIntervalFieldElement _new_real(self): + """ + Quickly create a new real interval with the same precision as + ``self``. + """ + P = (self._parent.real_field()) + return P._new() diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx index cd12eec28f1..1c9c5f5b4a0 100644 --- a/src/sage/rings/complex_interval.pyx +++ b/src/sage/rings/complex_interval.pyx @@ -322,23 +322,23 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: zz.imag().endpoints() == z.imag().endpoints() True """ - cdef ComplexIntervalFieldElement a00 = self._new() + a00 = self._new() mpfr_set(&a00.__re.left, &self.__re.left, MPFR_RNDN) mpfi_mid(&a00.__re.right, self.__re) mpfr_set(&a00.__im.left, &self.__im.left, MPFR_RNDN) mpfi_mid(&a00.__im.right, self.__im) - cdef ComplexIntervalFieldElement a01 = self._new() + a01 = self._new() mpfr_set(&a01.__re.left, &a00.__re.right, MPFR_RNDN) mpfr_set(&a01.__re.right, &self.__re.right, MPFR_RNDN) mpfi_set(a01.__im, a00.__im) - cdef ComplexIntervalFieldElement a10 = self._new() + a10 = self._new() mpfi_set(a10.__re, a00.__re) mpfi_mid(&a10.__im.left, self.__im) mpfr_set(&a10.__im.right, &self.__im.right, MPFR_RNDN) - cdef ComplexIntervalFieldElement a11 = self._new() + a11 = self._new() mpfi_set(a11.__re, a01.__re) mpfi_set(a11.__im, a10.__im) @@ -425,10 +425,10 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): 0.693147? + 3.1415902?*I, 0.693147? + 3.1415940?*I) """ - cdef ComplexIntervalFieldElement left = self._new() - cdef ComplexIntervalFieldElement right = self._new() - cdef ComplexIntervalFieldElement lower = self._new() - cdef ComplexIntervalFieldElement upper = self._new() + left = self._new() + right = self._new() + lower = self._new() + upper = self._new() cdef mpfr_t x mpfr_init2(x, self.prec()) @@ -471,7 +471,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): 2.00000000000000 """ cdef RealNumber diam - diam = RealNumber(self._parent._real_field().middle_field(), None) + diam = RealNumber(self._parent.real_field().middle_field(), None) cdef mpfr_t tmp mpfr_init2(tmp, self.prec()) mpfi_diam(diam.value, self.__re) @@ -511,8 +511,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): ... ValueError: intersection of non-overlapping intervals """ - - cdef ComplexIntervalFieldElement x = self._new() + x = self._new() cdef ComplexIntervalFieldElement other_intv if isinstance(other, ComplexIntervalFieldElement): other_intv = other @@ -538,7 +537,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: CIF(0).union(CIF(5, 5)).str(style='brackets') '[0.00000000000000000 .. 5.0000000000000000] + [0.00000000000000000 .. 5.0000000000000000]*I' """ - cdef ComplexIntervalFieldElement x = self._new() + x = self._new() cdef ComplexIntervalFieldElement other_intv if isinstance(other, ComplexIntervalFieldElement): other_intv = other @@ -566,7 +565,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: parent(CIF(1).magnitude()) Real Field with 53 bits of precision and rounding RNDU """ - cdef real_mpfi.RealIntervalField_class RIF = self._parent._real_field() + cdef real_mpfi.RealIntervalField_class RIF = self._parent.real_field() cdef RealNumber x = RIF.__upper_field._new() cdef RealNumber y = RIF.__upper_field._new() mpfi_mag(x.value, self.__re) @@ -590,7 +589,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: parent(CIF(1).mignitude()) Real Field with 53 bits of precision and rounding RNDD """ - cdef real_mpfi.RealIntervalField_class RIF = self._parent._real_field() + cdef real_mpfi.RealIntervalField_class RIF = self._parent.real_field() cdef RealNumber x = RIF.__lower_field._new() cdef RealNumber y = RIF.__lower_field._new() mpfi_mig(x.value, self.__re) @@ -649,7 +648,6 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: CIF(2,-3)._add_(CIF(1,-2)) 3 - 5*I """ - cdef ComplexIntervalFieldElement x x = self._new() mpfi_add(x.__re, self.__re, (right).__re) mpfi_add(x.__im, self.__im, (right).__im) @@ -664,7 +662,6 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: CIF(2,-3)._sub_(CIF(1,-2)) 1 - 1*I """ - cdef ComplexIntervalFieldElement x x = self._new() mpfi_sub(x.__re, self.__re, (right).__re) mpfi_sub(x.__im, self.__im, (right).__im) @@ -679,7 +676,6 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: CIF(2,-3)._mul_(CIF(1,-2)) -4 - 7*I """ - cdef ComplexIntervalFieldElement x x = self._new() cdef mpfi_t t0, t1 mpfi_init2(t0, self._prec) @@ -728,41 +724,17 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: CIF(1, -2).norm() 5 """ - return self.norm_c() - - cdef real_mpfi.RealIntervalFieldElement norm_c(ComplexIntervalFieldElement self): - cdef real_mpfi.RealIntervalFieldElement x - x = real_mpfi.RealIntervalFieldElement(self._parent._real_field(), None) - - cdef mpfi_t t0, t1 - mpfi_init2(t0, self._prec) - mpfi_init2(t1, self._prec) - - mpfi_sqr(t0, self.__re) - mpfi_sqr(t1, self.__im) + x = self._new_real() - mpfi_add(x.value, t0, t1) - - mpfi_clear(t0) - mpfi_clear(t1) - return x - - cdef real_mpfi.RealIntervalFieldElement abs_c(ComplexIntervalFieldElement self): - cdef real_mpfi.RealIntervalFieldElement x - x = real_mpfi.RealIntervalFieldElement(self._parent._real_field(), None) - - cdef mpfi_t t0, t1 - mpfi_init2(t0, self._prec) - mpfi_init2(t1, self._prec) + cdef mpfi_t t + mpfi_init2(t, self._prec) - mpfi_sqr(t0, self.__re) - mpfi_sqr(t1, self.__im) + mpfi_sqr(x.value, self.__re) + mpfi_sqr(t, self.__im) - mpfi_add(x.value, t0, t1) - mpfi_sqrt(x.value, x.value) + mpfi_add(x.value, x.value, t) - mpfi_clear(t0) - mpfi_clear(t1) + mpfi_clear(t) return x cpdef _div_(self, right): @@ -790,17 +762,6 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): """ return self * right.__invert__() - def __rdiv__(self, left): - """ - Divide ``left`` by ``self``. - - EXAMPLES:: - - sage: CIF(2,-3).__rdiv__(CIF(1,-2)) - 0.6153846153846154? - 0.0769230769230770?*I - """ - return ComplexIntervalFieldElement(self._parent, left)/self - def __pow__(self, right, modulus): r""" Compute `x^y`. @@ -1040,8 +1001,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: x.parent() Real Interval Field with 100 bits of precision """ - cdef real_mpfi.RealIntervalFieldElement x - x = real_mpfi.RealIntervalFieldElement(self._parent._real_field(), None) + x = self._new_real() mpfi_set(x.value, self.__re) return x @@ -1058,8 +1018,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: x.parent() Real Interval Field with 100 bits of precision """ - cdef real_mpfi.RealIntervalFieldElement x - x = real_mpfi.RealIntervalFieldElement(self._parent._real_field(), None) + x = self._new_real() mpfi_set(x.value, self.__im) return x @@ -1072,7 +1031,6 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: CIF(1.5, 2.5).__neg__() -1.5000000000000000? - 2.5000000000000000?*I """ - cdef ComplexIntervalFieldElement x x = self._new() mpfi_neg(x.__re, self.__re) mpfi_neg(x.__im, self.__im) @@ -1095,10 +1053,24 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): EXAMPLES:: + sage: abs(CIF(1.5, 2.5)) + 2.915475947422650? sage: CIF(1.5, 2.5).__abs__() 2.915475947422650? """ - return self.abs_c() + x = self._new_real() + + cdef mpfi_t t + mpfi_init2(t, self._prec) + + mpfi_sqr(x.value, self.__re) + mpfi_sqr(t, self.__im) + + mpfi_add(x.value, x.value, t) + mpfi_sqrt(x.value, x.value) + + mpfi_clear(t) + return x def __invert__(self): """ @@ -1135,8 +1107,6 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): .. [RL] \J. Rokne, P. Lancaster. Complex interval arithmetic. Communications of the ACM 14. 1971. """ - - cdef ComplexIntervalFieldElement x x = self._new() cdef mpfr_t a, b, c, d @@ -1631,7 +1601,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): upper = i break - fld = self.parent()._real_field() + fld = self._parent.real_field() return fld.pi() * fld(lower, upper) * fld(0.5) else: @@ -1641,7 +1611,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): # We'll handle the "bounded away in the imaginary direction" # case first. - fld = self.parent()._real_field() + fld = self._parent.real_field() if mpfi_is_strictly_pos(self.__im): return (-self.real() / self.imag()).arctan() + fld.pi()/2 @@ -1704,7 +1674,6 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: (1+i).conjugate() 1 - 1*I """ - cdef ComplexIntervalFieldElement x x = self._new() mpfi_set(x.__re, self.__re) @@ -1888,7 +1857,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): \cos(x + iy) = \cos(x) \cosh(y) - i \sin(x) \sinh(y) """ - cdef ComplexIntervalFieldElement res = self._new() + res = self._new() cdef mpfi_t tmp mpfi_init2(tmp, self._parent.prec()) sig_on() @@ -1930,7 +1899,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): \sin(x + iy) = \sin(x) \cosh(y) + i \cos (x) \sinh(y) """ - cdef ComplexIntervalFieldElement res = self._new() + res = self._new() cdef mpfi_t tmp mpfi_init2(tmp, self._parent.prec()) sig_on() @@ -1981,7 +1950,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): \cosh(x+iy) = \cos(y) \cosh(x) + i \sin(y) \sinh(x) """ - cdef ComplexIntervalFieldElement res = self._new() + res = self._new() cdef mpfi_t tmp mpfi_init2(tmp, self._parent.prec()) sig_on() @@ -2017,7 +1986,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): \sinh(x+iy) = \cos(y) \sinh(x) + i \sin(y) \cosh(x) """ - cdef ComplexIntervalFieldElement res = self._new() + res = self._new() cdef mpfi_t tmp mpfi_init2(tmp, self._parent.prec()) sig_on() @@ -2077,9 +2046,7 @@ def make_ComplexIntervalFieldElement0( fld, re, im ): sage: loads(dumps(a)) == a # indirect doctest True """ - x = ComplexIntervalFieldElement( fld, re, im ) - return x - + return fld(re, im) def create_ComplexIntervalFieldElement(s_real, s_imag=None, int pad=0, min_prec=53): diff --git a/src/sage/rings/complex_interval_field.py b/src/sage/rings/complex_interval_field.py index dcfd48fa93b..dffe4c7b78b 100644 --- a/src/sage/rings/complex_interval_field.py +++ b/src/sage/rings/complex_interval_field.py @@ -42,14 +42,13 @@ from sage.structure.parent import Parent from .integer_ring import ZZ from .rational_field import QQ -from . import complex_double from .ring import Field from . import integer from . import complex_interval import weakref from .real_mpfi import RealIntervalField, RealIntervalField_class -from .complex_field import ComplexField, ComplexField_class -from sage.misc.sage_eval import sage_eval +from .complex_field import ComplexField +from sage.misc.cachefunc import cached_method NumberFieldElement_quadratic = None @@ -214,7 +213,7 @@ def __init__(self, prec=53): """ self._prec = int(prec) from sage.categories.fields import Fields - Field.__init__(self, self._real_field(), ("I",), False, + Field.__init__(self, self.real_field(), ("I",), False, category=Fields().Infinite()) self._populate_coercion_lists_(convert_method_name="_complex_mpfi_") @@ -260,7 +259,7 @@ def construction(self): Univariate Polynomial Ring in x over Complex Interval Field with 53 bits of precision """ from sage.categories.pushout import AlgebraicClosureFunctor - return (AlgebraicClosureFunctor(), self._real_field()) + return (AlgebraicClosureFunctor(), self.real_field()) def is_exact(self): """ @@ -349,6 +348,7 @@ def _sage_input_(self, sib, coerce): precision = prec + @cached_method def real_field(self): """ Return the underlying :class:`RealIntervalField`. @@ -362,14 +362,13 @@ def real_field(self): sage: CIF.real_field() is R True """ - try: - return self.__real_field - except AttributeError: - self.__real_field = RealIntervalField(self._prec) - return self.__real_field + return RealIntervalField(self._prec) + # For compatibility with with other complex number implementations + # such as CC. _real_field = real_field + @cached_method def middle_field(self): """ Return the corresponding :class:`ComplexField` with the same precision @@ -382,11 +381,7 @@ def middle_field(self): sage: ComplexIntervalField(200).middle_field() Complex Field with 200 bits of precision """ - try: - return self.__middle_field - except AttributeError: - self.__middle_field = ComplexField(self._prec) - return self.__middle_field + return ComplexField(self._prec) def __eq__(self, other): """ @@ -480,7 +475,7 @@ def __call__(self, x=None, im=None, **kwds): # Note: we override Parent.__call__ because we want to support # CIF(a, b) and that is hard to do using coerce maps. if im is not None or kwds: - return complex_interval.ComplexIntervalFieldElement(self, x, im, **kwds) + return self.element_class(self, x, im, **kwds) return Parent.__call__(self, x) def _coerce_map_from_(self, S): @@ -595,7 +590,7 @@ def gen(self, n=0): """ if n != 0: raise IndexError("n must be 0") - return complex_interval.ComplexIntervalFieldElement(self, 0, 1) + return self.element_class(self, 0, 1) def random_element(self, *args, **kwds): """ @@ -616,8 +611,10 @@ def random_element(self, *args, **kwds): sage: CIF.random_element(max=0, min=-5) -0.079017286535590259? - 2.8712089896087117?*I """ - return self._real_field().random_element(*args, **kwds) \ - + self.gen() * self._real_field().random_element(*args, **kwds) + rand = self.real_field().random_element + re = rand(*args, **kwds) + im = rand(*args, **kwds) + return self.element_class(self, re, im) def is_field(self, proof = True): """ @@ -650,7 +647,7 @@ def pi(self): sage: ComplexIntervalField(100).pi() 3.14159265358979323846264338328? """ - return self(self._real_field().pi()) + return self.element_class(self, self.real_field().pi()) def ngens(self): r""" @@ -693,16 +690,16 @@ def zeta(self, n=2): from .integer import Integer n = Integer(n) if n == 1: - x = self(1) + x = self.element_class(self, 1) elif n == 2: - x = self(-1) + x = self.element_class(self, -1) elif n >= 3: # Use De Moivre # e^(2*pi*i/n) = cos(2pi/n) + i *sin(2pi/n) - RR = self._real_field() - pi = RR.pi() + RIF = self.real_field() + pi = RIF.pi() z = 2*pi/n - x = complex_interval.ComplexIntervalFieldElement(self, z.cos(), z.sin()) + x = self.element_class(self, z.cos(), z.sin()) # Uncomment after implemented #x._set_multiplicative_order( n ) return x @@ -725,6 +722,6 @@ def scientific_notation(self, status=None): sage: CIF((0.025, 2)) 0.025000000000000002? + 2*I """ - return self._real_field().scientific_notation(status) + return self.real_field().scientific_notation(status) diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pyx b/src/sage/rings/number_field/number_field_element_quadratic.pyx index 9ad624f42c9..37f1b18828d 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -55,7 +55,7 @@ from sage.rings.real_double import RDF from sage.rings.complex_double import CDF from sage.categories.morphism cimport Morphism from sage.rings.number_field.number_field_element import _inverse_mod_generic -from sage.rings.real_mpfi cimport RealIntervalFieldElement +from sage.rings.real_mpfi cimport RealIntervalField_class from sage.rings.complex_interval cimport ComplexIntervalFieldElement from sage.rings.real_arb cimport RealBall from sage.rings.complex_arb cimport ComplexBall @@ -576,7 +576,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): From: Number Field in a with defining polynomial x^2 - 5 To: Real Interval Field with 53 bits of precision """ - ans = RealIntervalFieldElement.__new__(RealIntervalFieldElement, R) + ans = (R)._new() if mpz_cmp_ui(self.b, 0): if mpz_cmp_ui(self.D.value, 0) < 0: diff --git a/src/sage/rings/real_interval_field.py b/src/sage/rings/real_interval_field.py index 2c7df5581a7..97a32ae67cc 100644 --- a/src/sage/rings/real_interval_field.py +++ b/src/sage/rings/real_interval_field.py @@ -2,6 +2,9 @@ Field of Arbitrary Precision Real Number Intervals """ +from sage.misc.superseded import deprecation +deprecation(24371, "sage.rings.real_interval_field is deprecated") + from sage.rings.real_mpfi import RealIntervalField_class, RealIntervalFieldElement def is_RealIntervalField(x): @@ -11,6 +14,8 @@ def is_RealIntervalField(x): EXAMPLES:: sage: from sage.rings.real_interval_field import is_RealIntervalField as is_RIF + doctest:...: DeprecationWarning: sage.rings.real_interval_field is deprecated + See http://trac.sagemath.org/24371 for details. sage: is_RIF(RIF) True """ diff --git a/src/sage/rings/real_mpfi.pxd b/src/sage/rings/real_mpfi.pxd index fc54c3c7a51..c32dcc959d0 100644 --- a/src/sage/rings/real_mpfi.pxd +++ b/src/sage/rings/real_mpfi.pxd @@ -33,7 +33,8 @@ cdef class RealIntervalField_class(Field): cdef inline RealIntervalFieldElement _new(self): """Return a new real interval with parent ``self``.""" - return RealIntervalFieldElement.__new__(RealIntervalFieldElement, self) + t = self.element_class + return (t.__new__(t, self)) cdef class RealIntervalFieldElement(RingElement): @@ -41,9 +42,7 @@ cdef class RealIntervalFieldElement(RingElement): cdef inline RealIntervalFieldElement _new(self): """Return a new real interval with same parent as ``self``.""" - return RealIntervalFieldElement.__new__(RealIntervalFieldElement, self._parent) - cpdef _add_(self, other) - cpdef _mul_(self, other) + return (self._parent)._new() cdef RealIntervalFieldElement abs(RealIntervalFieldElement self) cdef Rational _simplest_rational_helper(self) cpdef _str_question_style(self, int base, int error_digits, e, bint prefer_sci) diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index 0cada5d3e48..0c1bcac566d 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -692,9 +692,9 @@ cdef class RealIntervalField_class(Field): # Note: we override Parent.__call__ because we want to support # RIF(a, b) and that is hard to do using coerce maps. if y is not None: - return RealIntervalFieldElement(self, [x, y], **kwds) + return self.element_class(self, [x, y], **kwds) if kwds: - return RealIntervalFieldElement(self, x, **kwds) + return self.element_class(self, x, **kwds) return Parent.__call__(self, x) def algebraic_closure(self): @@ -1081,7 +1081,6 @@ cdef class RealIntervalField_class(Field): sage: R.pi().sqrt()/2 0.886226925452758013649083741670572591398774728? """ - cdef RealIntervalFieldElement x x = self._new() mpfi_const_pi(x.value) return x @@ -1095,24 +1094,10 @@ cdef class RealIntervalField_class(Field): sage: RealIntervalField(100).euler_constant() 0.577215664901532860606512090083? """ - cdef RealIntervalFieldElement x x = self._new() mpfi_const_euler(x.value) return x -# def catalan_constant(self): -# """ -# Returns Catalan's constant to the precision of this field. - -# EXAMPLES: -# sage: RealIntervalField(100).catalan_constant() -# 0.91596559417721901505460351493 -# """ -# cdef RealIntervalFieldElement x -# x = self._new() -# mpfr_const_catalan(x.value, self.rnd) -# return x - def log2(self): r""" Returns `\log(2)` to the precision of this field. @@ -1125,7 +1110,6 @@ cdef class RealIntervalField_class(Field): sage: R(2).log() 0.693147180559945309417232121458? """ - cdef RealIntervalFieldElement x x = self._new() mpfi_const_log2(x.value) return x @@ -2362,8 +2346,8 @@ cdef class RealIntervalFieldElement(RingElement): sage: RIF(pi).edges() (3.1415926535897932?, 3.1415926535897936?) """ - cdef RealIntervalFieldElement left = self._new() - cdef RealIntervalFieldElement right = self._new() + left = self._new() + right = self._new() cdef mpfr_t x mpfr_init2(x, self.prec()) mpfi_get_left(x, self.value) @@ -2567,8 +2551,8 @@ cdef class RealIntervalFieldElement(RingElement): sage: a.union(b).endpoints() == I.endpoints() True """ - cdef RealIntervalFieldElement left = self._new() - cdef RealIntervalFieldElement right = self._new() + left = self._new() + right = self._new() mpfr_set(&left.value.left, &self.value.left, MPFR_RNDN) mpfi_mid(&left.value.right, self.value) mpfi_interv_fr(right.value, &left.value.right, &self.value.right) @@ -2606,7 +2590,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: (R(1, 2) + R(3, 4)).str(style='brackets') '[4.0000000000000000 .. 6.0000000000000000]' """ - cdef RealIntervalFieldElement x x = self._new() mpfi_add(x.value, self.value, (other).value) return x @@ -2633,7 +2616,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: ~RIF(-1, 1) [-infinity .. +infinity] """ - cdef RealIntervalFieldElement x x = self._new() mpfi_inv(x.value, self.value) return x @@ -2652,7 +2634,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: (R(1, 2) - R(3, 4)).str(style='brackets') '[-3.0000000000000000 .. -1.0000000000000000]' """ - cdef RealIntervalFieldElement x x = self._new() mpfi_sub(x.value, self.value, (right).value) return x @@ -2689,7 +2670,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: parent(b*a) Real Interval Field with 20 bits of precision """ - cdef RealIntervalFieldElement x x = self._new() mpfi_mul(x.value, self.value, (right).value) return x @@ -2717,9 +2697,8 @@ cdef class RealIntervalFieldElement(RingElement): sage: (R(1, 2) / R(3, 4)).str(style='brackets') '[0.25000000000000000 .. 0.66666666666666675]' """ - cdef RealIntervalFieldElement x x = self._new() - mpfi_div((x).value, self.value, + mpfi_div(x.value, self.value, (right).value) return x @@ -2743,8 +2722,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: (v + -v).str(style='brackets') '[-1.0000000000000000 .. 1.0000000000000000]' """ - - cdef RealIntervalFieldElement x x = self._new() mpfi_neg(x.value, self.value) return x @@ -2764,7 +2741,7 @@ cdef class RealIntervalFieldElement(RingElement): """ return self.abs() - cdef RealIntervalFieldElement abs(RealIntervalFieldElement self): + cdef RealIntervalFieldElement abs(self): """ Return the absolute value of ``self``. @@ -2777,7 +2754,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: RIF(-2.1).abs() 2.1000000000000001? """ - cdef RealIntervalFieldElement x x = self._new() mpfi_abs(x.value, self.value) return x @@ -2800,14 +2776,12 @@ cdef class RealIntervalFieldElement(RingElement): sage: (RIF(-1, 1) * RIF(-1, 1)).str(style='brackets') '[-1.0000000000000000 .. 1.0000000000000000]' """ - - cdef RealIntervalFieldElement x x = self._new() mpfi_sqr(x.value, self.value) return x # Bit shifting - def _lshift_(RealIntervalFieldElement self, n): + def _lshift_(self, n): """ Return ``self*(2^n)`` for an integer ``n``. @@ -2818,7 +2792,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: RIF(1.5)._lshift_(2) 6 """ - cdef RealIntervalFieldElement x if n > sys.maxsize: raise OverflowError("n (=%s) must be <= %s" % (n, sys.maxsize)) x = self._new() @@ -2839,7 +2812,7 @@ cdef class RealIntervalFieldElement(RingElement): return x._lshift_(y) return sage.structure.element.bin_op(x, y, operator.lshift) - def _rshift_(RealIntervalFieldElement self, n): + def _rshift_(self, n): """ Return ``self/(2^n)`` for an integer ``n``. @@ -2852,7 +2825,6 @@ cdef class RealIntervalFieldElement(RingElement): """ if n > sys.maxsize: raise OverflowError("n (=%s) must be <= %s" % (n, sys.maxsize)) - cdef RealIntervalFieldElement x x = self._new() mpfi_div_2exp(x.value, self.value, n) return x @@ -3962,7 +3934,6 @@ cdef class RealIntervalFieldElement(RingElement): ... ValueError: intersection of non-overlapping intervals """ - cdef RealIntervalFieldElement x x = self._new() cdef RealIntervalFieldElement other_intv if isinstance(other, RealIntervalFieldElement): @@ -3992,8 +3963,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: RIF(1).union(RIF(-1)).str(style='brackets') '[-1.0000000000000000 .. 1.0000000000000000]' """ - - cdef RealIntervalFieldElement x x = self._new() cdef RealIntervalFieldElement other_intv cdef RealNumber other_rn @@ -4293,7 +4262,6 @@ cdef class RealIntervalFieldElement(RingElement): ... ValueError: self (=-2) is not >= 0 """ - cdef RealIntervalFieldElement x x = self._new() sig_on() mpfi_sqrt(x.value, self.value) @@ -4324,7 +4292,6 @@ cdef class RealIntervalFieldElement(RingElement): return RingElement.__pow__(self, exponent) return (self.log() * exponent).exp() - def log(self, base='e'): """ Return the logarithm of ``self`` to the given ``base``. @@ -4337,7 +4304,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: r = R(-2); r.log() 0.6931471805599453? + 3.141592653589794?*I """ - cdef RealIntervalFieldElement x if self < 0: return self.parent().complex_field()(self).log(base) if base == 'e': @@ -4374,7 +4340,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: r.log2() [-infinity .. 1.0000000000000000] """ - cdef RealIntervalFieldElement x if self < 0: return self.parent().complex_field()(self).log(2) x = self._new() @@ -4411,7 +4376,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: r.log10() 1.364376353841841?*I """ - cdef RealIntervalFieldElement x if self < 0: return self.parent().complex_field()(self).log(10) x = self._new() @@ -4444,7 +4408,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: r.exp() 9.38184458849869?e-15 """ - cdef RealIntervalFieldElement x x = self._new() sig_on() mpfi_exp(x.value, self.value) @@ -4473,7 +4436,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: r.exp2() 1.891172482530207?e-10 """ - cdef RealIntervalFieldElement x x = self._new() sig_on() mpfi_exp2(x.value, self.value) @@ -4523,31 +4485,6 @@ cdef class RealIntervalFieldElement(RingElement): else: return False, None -# MPFI does not have exp10. (Could easily be synthesized if anybody cares.) -# def exp10(self): -# r""" -# Returns $10^\code{self}$ - -# EXAMPLES: -# sage: r = 0.0 -# sage: r.exp10() -# 1.00000000000000 - -# sage: r = 32.0 -# sage: r.exp10() -# 100000000000000000000000000000000 - -# sage: r = -32.3 -# sage: r.exp10() -# 0.00000000000000000000000000000000501187233627275 -# """ -# cdef RealIntervalFieldElement x -# x = self._new() -# sig_on() -# mpfr_exp10(x.value, self.value, (self._parent).rnd) -# sig_off() -# return x - def cos(self): """ Return the cosine of ``self``. @@ -4570,7 +4507,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: RIF(-1, 1).cos().str(style='brackets') '[0.54030230586813965 .. 1.0000000000000000]' """ - cdef RealIntervalFieldElement x x = self._new() sig_on() mpfi_cos(x.value, self.value) @@ -4587,7 +4523,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: R(2).sin() 0.909297426825681695396019865912? """ - cdef RealIntervalFieldElement x x = self._new() sig_on() mpfi_sin(x.value, self.value) @@ -4607,32 +4542,12 @@ cdef class RealIntervalFieldElement(RingElement): sage: q.tan() 0.577350269189626? """ - cdef RealIntervalFieldElement x x = self._new() sig_on() mpfi_tan(x.value, self.value) sig_off() return x -# MPFI does not have sincos -# def sincos(self): -# """ -# Returns a pair consisting of the sine and cosine. - -# EXAMPLES: -# sage: R = RealIntervalField() -# sage: t = R.pi()/6 -# sage: t.sincos() -# (0.499999999999999, 0.866025403784438) -# """ -# cdef RealIntervalFieldElement x,y -# x = self._new() -# y = self._new() -# sig_on() -# mpfi_sin_cos(x.value, y.value, self.value) -# sig_off() -# return x,y - def arccos(self): """ Return the inverse cosine of ``self``. @@ -4656,7 +4571,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: q in q2 True """ - cdef RealIntervalFieldElement x x = self._new() sig_on() mpfi_acos(x.value, self.value) @@ -4686,7 +4600,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: q in q2 True """ - cdef RealIntervalFieldElement x x = self._new() sig_on() mpfi_asin(x.value, self.value) @@ -4716,7 +4629,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: q in q2 True """ - cdef RealIntervalFieldElement x x = self._new() sig_on() mpfi_atan(x.value, self.value) @@ -4733,7 +4645,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: q.cosh() 1.034465640095511? """ - cdef RealIntervalFieldElement x x = self._new() sig_on() mpfi_cosh(x.value, self.value) @@ -4750,7 +4661,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: q.sinh() 0.2648002276022707? """ - cdef RealIntervalFieldElement x x = self._new() sig_on() mpfi_sinh(x.value, self.value) @@ -4767,7 +4677,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: q.tanh() 0.2780794292958503? """ - cdef RealIntervalFieldElement x x = self._new() sig_on() mpfi_tanh(x.value, self.value) @@ -4784,7 +4693,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: i = q.arccosh() ; i 1.023227478547551? """ - cdef RealIntervalFieldElement x x = self._new() sig_on() mpfi_acosh(x.value, self.value) @@ -4803,7 +4711,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: i.arcsinh() - q 0.?e-15 """ - cdef RealIntervalFieldElement x x = self._new() sig_on() mpfi_asinh(x.value, self.value) @@ -4822,7 +4729,6 @@ cdef class RealIntervalFieldElement(RingElement): sage: i.arctanh() - q 0.?e-15 """ - cdef RealIntervalFieldElement x x = self._new() sig_on() mpfi_atanh(x.value, self.value) @@ -5069,7 +4975,7 @@ cdef class RealIntervalFieldElement(RingElement): sage: gamma(RIF(-3/2,-1/2)) [-infinity .. +infinity] """ - cdef RealIntervalFieldElement x = self._new() + x = self._new() if self > 1.462: # increasing mpfr_gamma(&x.value.left, &self.value.left, MPFR_RNDD) @@ -5114,65 +5020,6 @@ cdef class RealIntervalFieldElement(RingElement): from sage.rings.real_arb import RealBallField return RealBallField(self.precision())(self).psi()._real_mpfi_(self._parent) -# MPFI does not have: agm, erf, gamma, zeta -# def agm(self, other): -# """ -# Return the arithmetic-geometric mean of self and other. The -# arithmetic-geometric mean is the common limit of the sequences -# $u_n$ and $v_n$, where $u_0$ is self, $v_0$ is other, -# $u_{n+1}$ is the arithmetic mean of $u_n$ and $v_n$, and -# $v_{n+1}$ is the geometric mean of u_n and v_n. If any operand -# is negative, the return value is \code{NaN}. -# """ -# cdef RealIntervalFieldElement x, _other -# if not isinstance(other, RealIntervalFieldElement) or other.parent() != self._parent: -# _other = self._parent(other) -# else: -# _other = other -# x = self._new() -# sig_on() -# mpfi_agm(x.value, self.value, _other.value) -# sig_off() -# return x - - -# def erf(self): -# """ -# Return the value of the error function on ``self``. - -# EXAMPLES:: -# -# sage: R = RealIntervalField() -# sage: R(6).erf() -# 0.999999999999999 -# """ -# cdef RealIntervalFieldElement x -# x = self._new() -# sig_on() -# mpfi_erf(x.value, self.value) -# sig_off() -# return x - - -# def gamma(self): -# """ -# The Euler gamma function. Return gamma of self. - -# EXAMPLES:: -# -# sage: R = RealIntervalField() -# sage: R(6).gamma() -# 120.000000000000 -# sage: R(1.5).gamma() -# 0.886226925452757 -# """ -# cdef RealIntervalFieldElement x -# x = self._new() -# sig_on() -# mpfi_gamma(x.value, self.value) -# sig_off() -# return x - def zeta(self, a=None): """ Return the image of this interval by the Hurwitz zeta function. @@ -5401,4 +5248,4 @@ def __create__RealIntervalFieldElement_version1(parent, lower, upper): sage: sage.rings.real_mpfi.__create__RealIntervalFieldElement_version1(RIF, 2.225, 2.227) 2.226? """ - return RealIntervalFieldElement(parent, (lower, upper)) + return parent([lower, upper]) From c7f4d35736d89b5b7dfb60a7c091211c2a6e8ce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Tue, 13 Feb 2018 11:54:54 +0200 Subject: [PATCH 706/740] Add a note. --- src/sage/combinat/posets/posets.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 3bb9ddd5afb..411e837f2a5 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -2845,6 +2845,12 @@ def is_series_parallel(self): series-parallel contains a subposet isomorphic to the 4-element N-shaped poset where `a > c, d` and `b > d`. + .. NOTE:: + + Some papers use the term N-free for posets having no + N-shaped poset as a *cover-preserving subposet*. This definition + is not used here. + See :wikipedia:`Series-parallel partial order`. EXAMPLES:: From ede1402a8ec9f4a2012584b3017d54e1c925e692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 13 Feb 2018 11:49:19 +0100 Subject: [PATCH 707/740] trac 24286 detail --- src/sage/repl/load.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/repl/load.py b/src/sage/repl/load.py index f1591e0d64a..f2abb04e358 100644 --- a/src/sage/repl/load.py +++ b/src/sage/repl/load.py @@ -172,7 +172,7 @@ def load(filename, globals, attach=False): sage: load_attach_path() ['.'] sage: t_dir = tmp_dir() - sage: fname = tmp_filename(ext='.py') + sage: fname = 'test.py' sage: fullpath = os.path.join(t_dir, fname) sage: with open(fullpath, 'w') as f: ....: _ = f.write("print(37 * 3)") From c5a10dc8445d0e07b060f168918ac19c64e601f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 13 Feb 2018 11:54:14 +0100 Subject: [PATCH 708/740] trac 24507 do not use integer_types --- src/sage/libs/pynac/pynac.pyx | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/sage/libs/pynac/pynac.pyx b/src/sage/libs/pynac/pynac.pyx index 69672a34514..5426c68854a 100644 --- a/src/sage/libs/pynac/pynac.pyx +++ b/src/sage/libs/pynac/pynac.pyx @@ -13,11 +13,8 @@ Pynac interface # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** - from __future__ import absolute_import, division, print_function -from six import integer_types - from cpython cimport * from libc cimport math @@ -986,7 +983,7 @@ cdef py_real(x): sage: py_real(complex(2,2)) 2.0 """ - if type(x) is float or type(x) in integer_types: + if type(x) is float or type(x) in [int, long]: return x elif type(x) is complex: return x.real @@ -1083,7 +1080,7 @@ cdef py_conjugate(x): cdef bint py_is_rational(x): return (type(x) is Rational or type(x) is Integer or - isinstance(x, integer_types)) + isinstance(x, (int, long))) cdef bint py_is_equal(x, y): """ @@ -1117,7 +1114,7 @@ cdef bint py_is_integer(x): sage: py_is_integer(3.0r) False """ - return (isinstance(x, integer_types + (Integer,)) or + return (isinstance(x, (int, long, Integer,)) or (isinstance(x, Element) and ((x)._parent.is_exact() or (x)._parent == ring.SR) and (x in ZZ))) @@ -1177,7 +1174,7 @@ def py_is_crational_for_doctest(x): return py_is_crational(x) cdef bint py_is_real(a): - if (type(a) in integer_types or isinstance(a, Integer) or + if (type(a) in [int, long] or isinstance(a, Integer) or type(a) is float): return True try: @@ -1203,7 +1200,7 @@ cdef bint py_is_prime(n): cdef bint py_is_exact(x): - return (isinstance(x, integer_types + (Integer,)) or + return (isinstance(x, (int, long, Integer,)) or (isinstance(x, Element) and ((x)._parent.is_exact() or (x)._parent == ring.SR))) @@ -1235,7 +1232,7 @@ cdef py_numer(n): sage: py_numer(no_numer()) 42 """ - if isinstance(n, integer_types + (Integer,)): + if isinstance(n, (int, long, Integer,)): return n try: return n.numerator() @@ -1273,7 +1270,7 @@ cdef py_denom(n): sage: py_denom(2/3*i) 3 """ - if isinstance(n, integer_types + (Integer,)): + if isinstance(n, (int, long, Integer,)): return 1 try: return n.denominator() @@ -1394,7 +1391,7 @@ cdef py_tgamma(x): sage: py_tgamma(1/2) 1.77245385090552 """ - if type(x) in integer_types: + if type(x) in [int, long]: x = float(x) if type(x) is float: return math.tgamma(PyFloat_AS_DOUBLE(x)) @@ -1715,7 +1712,7 @@ cdef py_log(x): """ cdef gsl_complex res cdef double real, imag - if type(x) in integer_types: + if type(x) in [int, long]: x = float(x) if type(x) is float: real = PyFloat_AS_DOUBLE(x) From a29fb5826049e271ddaed1f1c6de0e52a4cd4192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 16 Oct 2017 11:50:06 +0200 Subject: [PATCH 709/740] adding some decode to subprocess in interfaces --- src/sage/interfaces/jmoldata.py | 11 ++++++----- src/sage/interfaces/tachyon.py | 3 ++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/sage/interfaces/jmoldata.py b/src/sage/interfaces/jmoldata.py index 10277cc8739..be613b40c8b 100644 --- a/src/sage/interfaces/jmoldata.py +++ b/src/sage/interfaces/jmoldata.py @@ -24,6 +24,7 @@ from sage.env import SAGE_LOCAL from sage.misc.temporary_file import tmp_filename +from sage.cpython.string import bytes_to_str import subprocess import os @@ -60,7 +61,7 @@ def is_jvm_available(self): <... 'bool'> """ try: - version = subprocess.check_output(['java', '-version'], stderr=subprocess.STDOUT) + version = bytes_to_str(subprocess.check_output(['java', '-version'], stderr=subprocess.STDOUT)) except (subprocess.CalledProcessError, OSError): return False @@ -140,7 +141,7 @@ def export_image(self, sage: if sys.platform == 'cygwin': ....: from subprocess import check_output, STDOUT ....: archive_native = check_output(['cygpath', '-w', archive_native], - ....: stderr=STDOUT).rstrip() + ....: stderr=STDOUT).decode('utf-8').rstrip() sage: script = 'set defaultdirectory "{0}"\n script SCRIPT\n'.format(archive_native) sage: testfile = os.path.join(SAGE_TMP, "testimage.png") sage: JData.export_image(targetfile=testfile, datafile=script, image_type="PNG") # optional -- java @@ -153,12 +154,12 @@ def export_image(self, import sys if sys.platform == 'cygwin': jmolpath = subprocess.check_output(['cygpath', '-w', jmolpath], - stderr=subprocess.STDOUT).rstrip() + stderr=subprocess.STDOUT).decode('utf-8').rstrip() target_native = subprocess.check_output(['cygpath', '-w', target_native], - stderr=subprocess.STDOUT).rstrip() + stderr=subprocess.STDOUT).decode('utf-8').rstrip() if (datafile_cmd != 'script'): datafile = subprocess.check_output(['cygpath', '-w', datafile], - stderr=subprocess.STDOUT).rstrip() + stderr=subprocess.STDOUT).decode('utf-8').rstrip() launchscript = "" if (datafile_cmd!='script'): launchscript = "load " diff --git a/src/sage/interfaces/tachyon.py b/src/sage/interfaces/tachyon.py index a3ce76bb54e..ce8bc94c9d6 100644 --- a/src/sage/interfaces/tachyon.py +++ b/src/sage/interfaces/tachyon.py @@ -21,6 +21,7 @@ from sage.misc.pager import pager from sage.misc.temporary_file import tmp_filename from sage.structure.sage_object import SageObject +from sage.cpython.string import bytes_to_str class TachyonRT(SageObject): @@ -153,7 +154,7 @@ def __call__(self, model, outfile='sage.png', verbose=1, extra_opts=''): if verbose: print(' '.join(cmd)) import subprocess - out = subprocess.check_output(cmd) + out = bytes_to_str(subprocess.check_output(cmd)) if verbose >= 1: print(out) if out.rstrip().endswith('Aborting render.'): From cd61fefc2a81c279e4521f65231477541fcfe242 Mon Sep 17 00:00:00 2001 From: David Coudert Date: Tue, 13 Feb 2018 14:28:32 +0100 Subject: [PATCH 710/740] trac #24683: check input parameter algorithm --- src/sage/graphs/generic_graph.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index ac91a47999c..5d3794f7f0e 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -7297,7 +7297,20 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, 3 sage: print(G.hamiltonian_path(s=0, t=1, use_edge_labels=True, maximize=True)[0]) 5 + + Parameter ``algorithm`` must be either ``'backtrack'`` or ``'MILP'``:: + + sage: Graph().hamiltonian_path(algorithm='noname') + Traceback (most recent call last): + ... + ValueError: algorithm must be either 'backtrack' or 'MILP' """ + if use_edge_labels or algorithm is None: + # We force the algorithm to 'MILP' + algorithm = 'MILP' + elif not algorithm in ['MILP', 'backtrack']: + raise ValueError("algorithm must be either 'backtrack' or 'MILP'") + if self.order() < 2: raise ValueError('the Hamiltonian path problem is not well ' + 'defined for empty and one-element (di)graphs') From f47ed48f4716e44fb18e46f52b4c5c67da9ad3a2 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 13 Feb 2018 10:47:36 +0100 Subject: [PATCH 711/740] Cleanup in sage.misc.cython --- src/sage/misc/cython.py | 178 +++++++++++++++++----------------------- 1 file changed, 75 insertions(+), 103 deletions(-) diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index 6a3b051c7a7..87efed1e8fd 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -7,13 +7,17 @@ - William Stein (2007-07-28): update from sagex to cython - Martin Albrecht & William Stein (2011-08): cfile & cargs """ + #***************************************************************************** # Copyright (C) 2006 William Stein # -# Distributed under the terms of the GNU General Public License (GPL) -# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** + from __future__ import print_function, absolute_import from six.moves import builtins from six import iteritems @@ -32,6 +36,7 @@ from .temporary_file import tmp_filename from sage.misc.superseded import deprecated_function_alias from sage.repl.user_globals import get_globals +from sage.misc.sage_ostools import restore_cwd # CBLAS can be one of multiple implementations @@ -298,13 +303,6 @@ def _pyx_preparse(s): TESTS:: - sage: module = sage.misc.cython.import_test("trac11680") # long time (7s on sage.math, 2012) - sage: R. = QQ[] - sage: module.evaluate_at_power_of_gen(x^3 + x - 7, 5) # long time - x^15 + x^5 - 7 - - :: - sage: from sage.misc.cython import pyx_preparse sage: _ = pyx_preparse("") doctest:...: DeprecationWarning: pyx_preparse is deprecated. Please use sage.misc.cython._pyx_preparse instead. @@ -393,6 +391,10 @@ def cython(filename, verbose=0, compile_message=False, - ``create_local_so_file`` (bool, default False) -- if True, save a copy of the compiled .so file in the current directory. + OUTPUT: a tuple ``(name, dir)`` where ``name`` is the name + of the compiled module and ``dir`` is the directory containing + the generated files. + TESTS: Before :trac:`12975`, it would have been needed to write ``#clang c++``, @@ -460,9 +462,9 @@ def cython(filename, verbose=0, compile_message=False, # the same session). if create_local_so_file: base, ext = os.path.splitext(os.path.basename(filename)) - base = sanitize(base) else: - base = sanitize(os.path.abspath(filename)) + base = os.path.abspath(filename) + base = sanitize(base) # This is the *temporary* directory where we store the pyx file. # This is deleted when Sage exits, which means pyx files must be @@ -509,6 +511,7 @@ def cython(filename, verbose=0, compile_message=False, if compile_message: print("Compiling {}...".format(filename), file=sys.stderr) + sys.stderr.flush() with open(filename) as f: (preparsed, libs, includes, language, additional_source_files, @@ -547,15 +550,14 @@ def cython(filename, verbose=0, compile_message=False, extra_compile_args=extra_args, language=language) - orig_cwd = os.getcwd() try: # Change directories to target_dir so that Cython produces the correct # relative path; https://trac.sagemath.org/ticket/24097 - os.chdir(target_dir) - ext, = cythonize([ext], - aliases=cython_aliases(), - include_path=includes, - quiet=not verbose) + with restore_cwd(target_dir): + ext, = cythonize([ext], + aliases=cython_aliases(), + include_path=includes, + quiet=not verbose) except CompileError: # Check for names in old_pxi_names note = '' @@ -570,8 +572,6 @@ def cython(filename, verbose=0, compile_message=False, """.format(pxd, name)) raise RuntimeError("Error converting {} to C".format(filename) + note) - finally: - os.chdir(orig_cwd) if create_local_c_file: shutil.copy(os.path.join(target_dir, ext.sources[0]), @@ -631,10 +631,7 @@ def subtract_from_line_numbers(s, n): ################################################################ # COMPILE ################################################################ -def cython_lambda(vars, expr, - verbose=False, - compile_message=False, - use_cache=False): +def cython_lambda(vars, expr, verbose=0, **kwds): """ Create a compiled function which evaluates ``expr`` assuming machine values for ``vars``. @@ -706,16 +703,14 @@ def __getattr__(self, name): def f(%s): return %s """%(v, expr) - if verbose: + if verbose > 0: print(s) - tmpfile = tmp_filename(ext=".spyx") - open(tmpfile,'w').write(s) + tmpfile = tmp_filename(ext=".pyx") + with open(tmpfile,'w') as f: + f.write(s) d = {} - cython_import_all(tmpfile, d, - verbose=verbose, compile_message=compile_message, - use_cache=use_cache, - create_local_c_file=False) + cython_import_all(tmpfile, d, verbose=verbose, **kwds) return d['f'] @@ -751,6 +746,8 @@ def cython_create_local_so(filename): sage: _ = f.write(s) sage: f.close() sage: cython_create_local_so('hello.spyx') + doctest:...: DeprecationWarning: cython_create_local_so is deprecated, call cython() with the create_local_so_file=True keyword + See http://trac.sagemath.org/24722 for details. Compiling hello.spyx... sage: sys.path.append('.') sage: import hello @@ -762,14 +759,15 @@ def cython_create_local_so(filename): - David Fu (2008-04-09): initial version """ + from sage.misc.superseded import deprecation + deprecation(24722, "cython_create_local_so is deprecated, call cython() with the create_local_so_file=True keyword") cython(filename, compile_message=True, use_cache=False, create_local_so_file=True) ################################################################ # IMPORT ################################################################ -def cython_import(filename, verbose=False, compile_message=False, - use_cache=False, create_local_c_file=True, **kwds): +def cython_import(filename, **kwds): """ Compile a file containing Cython code, then import and return the module. Raises an ``ImportError`` if anything goes wrong. @@ -786,17 +784,17 @@ def cython_import(filename, verbose=False, compile_message=False, - the module that contains the compiled Cython code. """ - name, build_dir = cython(filename, verbose=verbose, - compile_message=compile_message, - use_cache=use_cache, - create_local_c_file=create_local_c_file, - **kwds) - sys.path.append(build_dir) - return builtins.__import__(name) + name, build_dir = cython(filename, **kwds) + + oldpath = sys.path + try: + sys.path.append(build_dir) + return builtins.__import__(name) + finally: + sys.path = oldpath -def cython_import_all(filename, globals, verbose=False, compile_message=False, - use_cache=False, create_local_c_file=True): +def cython_import_all(filename, globals, **kwds): """ Imports all non-private (i.e., not beginning with an underscore) attributes of the specified Cython module into the given context. @@ -811,9 +809,7 @@ def cython_import_all(filename, globals, verbose=False, compile_message=False, - ``filename`` - a string; name of a file that contains Cython code """ - m = cython_import(filename, verbose=verbose, compile_message=compile_message, - use_cache=use_cache, - create_local_c_file=create_local_c_file) + m = cython_import(filename, **kwds) for k, x in iteritems(m.__dict__): if k[0] != '_': globals[k] = x @@ -848,7 +844,7 @@ def sanitize(f): return s -def compile_and_load(code): +def compile_and_load(code, **kwds): r""" INPUT: @@ -860,18 +856,40 @@ def compile_and_load(code): EXAMPLES:: - sage: module = sage.misc.cython.compile_and_load("def f(int n):\n return n*n") + sage: from sage.misc.cython import compile_and_load + sage: module = compile_and_load("def f(int n):\n return n*n") sage: module.f(10) 100 + + TESTS:: + + sage: code = ''' + ....: from sage.rings.rational cimport Rational + ....: from sage.rings.polynomial.polynomial_rational_flint cimport Polynomial_rational_flint + ....: from sage.libs.flint.fmpq_poly cimport fmpq_poly_length, fmpq_poly_get_coeff_mpq, fmpq_poly_set_coeff_mpq + ....: + ....: def evaluate_at_power_of_gen(Polynomial_rational_flint f, unsigned long n): + ....: assert n >= 1 + ....: cdef Polynomial_rational_flint res = f._new() + ....: cdef unsigned long k + ....: cdef Rational z = Rational(0) + ....: for k in range(fmpq_poly_length(f.__poly)): + ....: fmpq_poly_get_coeff_mpq(z.value, f.__poly, k) + ....: fmpq_poly_set_coeff_mpq(res.__poly, n*k, z.value) + ....: return res + ....: ''' + sage: module = compile_and_load(code) # long time + sage: R. = QQ[] + sage: module.evaluate_at_power_of_gen(x^3 + x - 7, 5) # long time + x^15 + x^5 - 7 """ - file = tmp_filename(ext=".pyx") - open(file,'w').write(code) - return cython_import(file, create_local_c_file=False) + tmpfile = tmp_filename(ext=".pyx") + with open(tmpfile, 'w') as f: + f.write(code) + return cython_import(tmpfile, **kwds) -def cython_compile(code, - verbose=False, compile_message=False, - make_c_file_nice=False, use_cache=False): +def cython_compile(code, **kwds): """ Given a block of Cython code (as a text string), this function compiles it using a C compiler, and includes it into the global @@ -879,7 +897,7 @@ def cython_compile(code, AUTHOR: William Stein, 2006-10-31 - .. warn: + .. WARNING:: Only use this from Python code, not from extension code, since from extension code you would change the global scope (i.e., @@ -893,58 +911,12 @@ def cython_compile(code, library would greatly slow down startup time, since currently there is no caching. - .. todo: + .. TODO:: Need to create a clever caching system so code only gets compiled once. """ tmpfile = tmp_filename(ext=".pyx") - open(tmpfile,'w').write(code) - cython_import_all(tmpfile, get_globals(), - verbose=verbose, compile_message=compile_message, - use_cache=use_cache, - create_local_c_file=False) - - -TESTS = { -'trac11680':""" -from sage.rings.rational cimport Rational -from sage.rings.polynomial.polynomial_rational_flint cimport Polynomial_rational_flint -from sage.libs.flint.fmpq_poly cimport fmpq_poly_length, fmpq_poly_get_coeff_mpq, fmpq_poly_set_coeff_mpq - -def evaluate_at_power_of_gen(Polynomial_rational_flint f, unsigned long n): - assert n >= 1 - cdef Polynomial_rational_flint res = f._new() - cdef unsigned long k - cdef Rational z = Rational(0) - for k in range(fmpq_poly_length(f.__poly)): - fmpq_poly_get_coeff_mpq(z.value, f.__poly, k) - fmpq_poly_set_coeff_mpq(res.__poly, n*k, z.value) - return res -""", - -'trac11680b':""" -def f(int a, int b, int c): - return a+b+c -""" -} - -def import_test(name): - """ - This is used by the testing infrastructure to test building - Cython programs. - - INPUT: - - - ``name`` -- string; name of a key to the TESTS dictionary above - - OUTPUT: a module, which results from compiling the given code and importing it - - EXAMPLES:: - - sage: module = sage.misc.cython.import_test("trac11680b") - sage: module.f(2,3,4) - 9 - """ - return compile_and_load(TESTS[name]) - + with open(tmpfile,'w') as f: + f.write(code) + return cython_import_all(tmpfile, get_globals(), **kwds) From 3d8b37de22947d50c8a1ba1a64ca2d1f3f56a051 Mon Sep 17 00:00:00 2001 From: David Coudert Date: Tue, 13 Feb 2018 15:15:34 +0100 Subject: [PATCH 712/740] trac #24683: document the transformation from hamiltonian path to hamiltonian cycle --- src/sage/graphs/generic_graph.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 5d3794f7f0e..f085d2447f7 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -7386,17 +7386,24 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, path = g.longest_path(s=new_s, t=new_t, algorithm="backtrack") return path if path.order() == g.order() else None - - # We modify the graph to search for a Hamiltonian Cycle starting from - # new_s (if determined) and ending at new_t (if determined) + # + # We modify the graph to turn the Hamiltonian Path problem into a + # Hamiltonian Cycle problem. The modification ensures that the computed + # Hamiltonian Cycle will visit new_s (if determined) right after new_t + # (if determined). Hence the extraction of the Hamiltonian Path is easy. + # extra_vertices = [] if new_s is None: + # If the source is not specified or determined, we add a virtual + # source and an edge from it to each vertex of the (di)graph. new_s = g.add_vertex() extra_vertices.append(new_s) for u in self: # in original set of vertices g.add_edge(new_s, u, 0) if new_t is None: + # If the source is not specified or determined, we add a virtual + # destination and an edge from each vertex of the (di)graph to it. new_t = g.add_vertex() extra_vertices.append(new_t) for u in self: From 235e38b4091001da1289ca51365a4bd6d3b3f66a Mon Sep 17 00:00:00 2001 From: David Coudert Date: Tue, 13 Feb 2018 15:19:44 +0100 Subject: [PATCH 713/740] trac #24683: fix typo paramter -> parameter --- src/sage/graphs/generic_graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index f085d2447f7..f0e3f426255 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -7460,7 +7460,7 @@ def traveling_salesman_problem(self, use_edge_labels=False, maximize=False, - ``maximize`` -- boolean (default: ``False``). When set to ``True`` search for a Hamiltonian cycle (res. circuit) of maximum weight - instead of minimum weight. This paramter is used only when + instead of minimum weight. This parameter is used only when ``use_edge_labels`` is ``True``. - ``solver`` -- (default: ``None``) Specify a Linear Program (LP) From f13924eea90ea5dc0d247cace07ee21353bdabca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 13 Feb 2018 15:45:14 +0100 Subject: [PATCH 714/740] more bytes_to_str in jmoldata --- src/sage/interfaces/jmoldata.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/interfaces/jmoldata.py b/src/sage/interfaces/jmoldata.py index be613b40c8b..7757d034283 100644 --- a/src/sage/interfaces/jmoldata.py +++ b/src/sage/interfaces/jmoldata.py @@ -132,9 +132,9 @@ def export_image(self, sage: from sage.interfaces.jmoldata import JmolData sage: JData = JmolData() - sage: D=dodecahedron() + sage: D = dodecahedron() sage: from sage.misc.misc import SAGE_TMP - sage: archive_name=os.path.join(SAGE_TMP, "archive.jmol.zip") + sage: archive_name = os.path.join(SAGE_TMP, "archive.jmol.zip") sage: D.export_jmol(archive_name) #not scaled properly...need some more steps. sage: archive_native = archive_name sage: import sys @@ -153,13 +153,13 @@ def export_image(self, target_native = targetfile import sys if sys.platform == 'cygwin': - jmolpath = subprocess.check_output(['cygpath', '-w', jmolpath], - stderr=subprocess.STDOUT).decode('utf-8').rstrip() - target_native = subprocess.check_output(['cygpath', '-w', target_native], - stderr=subprocess.STDOUT).decode('utf-8').rstrip() + jmolpath = bytes_to_str(subprocess.check_output(['cygpath', '-w', jmolpath], + stderr=subprocess.STDOUT)).rstrip() + target_native = bytes_to_str(subprocess.check_output(['cygpath', '-w', target_native], + stderr=subprocess.STDOUT)).rstrip() if (datafile_cmd != 'script'): - datafile = subprocess.check_output(['cygpath', '-w', datafile], - stderr=subprocess.STDOUT).decode('utf-8').rstrip() + datafile = bytes_to_str(subprocess.check_output(['cygpath', '-w', datafile], + stderr=subprocess.STDOUT)).rstrip() launchscript = "" if (datafile_cmd!='script'): launchscript = "load " From 6d0bd62e5e71c3b635e3838eea94d480ad1bfb99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Tue, 13 Feb 2018 17:21:53 +0200 Subject: [PATCH 715/740] Return type of PowerPoset(). --- src/sage/combinat/posets/poset_examples.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index 88c2b622d5c..9390febc91b 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -578,18 +578,18 @@ def PowerPoset(n): EXAMPLES:: sage: P3 = posets.PowerPoset(3); P3 - Finite poset containing 19 elements + Finite meet-semilattice containing 19 elements sage: all(P.is_chain() for P in P3.maximal_elements()) True TESTS:: sage: P0 = posets.PowerPoset(0); P0 - Finite poset containing 1 elements + Finite meet-semilattice containing 1 elements sage: P0[0] Finite poset containing 0 elements sage: P1 = posets.PowerPoset(1); P1 - Finite poset containing 1 elements + Finite meet-semilattice containing 1 elements sage: P1[0] Finite poset containing 1 elements sage: P1[0][0] @@ -610,9 +610,9 @@ def PowerPoset(n): for r in Permutations(P): all_pos_n.add(P.relabel(list(r))) - return Poset((all_pos_n, - lambda A, B: all(B.is_lequal(x, y) for x,y in A.cover_relations_iterator()) - )) + return MeetSemilattice((all_pos_n, + lambda A, B: all(B.is_lequal(x, y) for x,y in A.cover_relations_iterator()) + )) @staticmethod def RandomPoset(n, p): From 64837df2c32641a257082b63ed3a3f1b9f80007d Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 18 Jan 2018 11:54:55 +0100 Subject: [PATCH 716/740] Deprecate sage.structure.element.generic_power --- src/sage/all.py | 1 + src/sage/combinat/diagram_algebras.py | 2 +- src/sage/groups/generic.py | 3 ++ src/sage/misc/dev_tools.py | 2 +- .../rings/number_field/number_field_ideal.py | 29 +++++++------------ src/sage/rings/polynomial/polydict.pyx | 20 +++---------- .../rings/polynomial/polynomial_element.pyx | 14 ++++----- .../polynomial/polynomial_modn_dense_ntl.pyx | 2 +- src/sage/structure/element.pyx | 6 ++-- src/sage/structure/factorization.py | 7 +++-- 10 files changed, 38 insertions(+), 48 deletions(-) diff --git a/src/sage/all.py b/src/sage/all.py index 19bc8ecba69..aeb881f1b00 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -108,6 +108,7 @@ from sage.schemes.all import * from sage.graphs.all import * from sage.groups.all import * +from sage.arith.power import generic_power as power from sage.databases.all import * from sage.categories.all import * from sage.sets.all import * diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index ea65a2cee0d..aa92cdb86a9 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -25,7 +25,7 @@ from sage.categories.algebras import Algebras from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets -from sage.structure.element import generic_power +from sage.arith.power import generic_power from sage.combinat.free_module import CombinatorialFreeModule from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation diff --git a/src/sage/groups/generic.py b/src/sage/groups/generic.py index f31756e785a..4bfda6eca43 100644 --- a/src/sage/groups/generic.py +++ b/src/sage/groups/generic.py @@ -121,8 +121,11 @@ multiplication_names = ( 'multiplication', 'times', 'product', '*') addition_names = ( 'addition', 'plus', 'sum', '+') + +# deprecation(24256) from sage.structure.element import generic_power as power + def multiple(a, n, operation='*', identity=None, inverse=None, op=None): r""" Returns either `na` or `a^n`, where `n` is any integer and `a` is diff --git a/src/sage/misc/dev_tools.py b/src/sage/misc/dev_tools.py index 5392ac8c72c..4f2257e9420 100644 --- a/src/sage/misc/dev_tools.py +++ b/src/sage/misc/dev_tools.py @@ -425,7 +425,7 @@ def import_statements(*objects, **kwds): from sage.modular.arithgroup.farey_symbol import Farey as FareySymbol sage: import_statements('power') - from sage.structure.element import generic_power as power + from sage.arith.power import generic_power as power In order to be able to detect functions that belong to a non-loaded module, you might call the helper :func:`load_submodules` as in the following:: diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index ae9ea3d5a98..d422ffc5ad9 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -57,7 +57,7 @@ from sage.misc.all import prod from sage.misc.mrange import xmrange_iter from sage.misc.cachefunc import cached_method -from sage.structure.element import generic_power, MultiplicativeGroupElement +from sage.structure.element import MultiplicativeGroupElement from sage.structure.factorization import Factorization from sage.structure.sequence import Sequence from sage.structure.proof.proof import get_flag @@ -1703,8 +1703,17 @@ def is_NumberFieldIdeal(x): class NumberFieldFractionalIdeal(MultiplicativeGroupElement, NumberFieldIdeal): r""" A fractional ideal in a number field. - """ + EXAMPLES:: + + sage: R. = PolynomialRing(QQ) + sage: K. = NumberField(x^3 - 2) + sage: I = K.ideal(2/(5+a)) + sage: J = I^2 + sage: Jinv = I^(-2) + sage: J*Jinv + Fractional ideal (1) + """ def __init__(self, field, gens, coerce=True): """ INPUT: @@ -1871,22 +1880,6 @@ def __invert__(self): I.__pari_hnf = hnf return I - def __pow__(self, r): - """ - Return self to the power of r. - - EXAMPLES:: - - sage: R. = PolynomialRing(QQ) - sage: K. = NumberField(x^3 - 2) - sage: I = K.ideal(2/(5+a)) - sage: J = I^2 - sage: Jinv = I^(-2) - sage: J*Jinv - Fractional ideal (1) - """ - return generic_power(self, r) - def is_maximal(self): """ Return True if this ideal is maximal. This is equivalent to diff --git a/src/sage/rings/polynomial/polydict.pyx b/src/sage/rings/polynomial/polydict.pyx index b045e6bc5e5..be5a96ef192 100644 --- a/src/sage/rings/polynomial/polydict.pyx +++ b/src/sage/rings/polynomial/polydict.pyx @@ -46,7 +46,7 @@ from cysignals.memory cimport sig_malloc, sig_free import copy from functools import reduce -from sage.structure.element import generic_power +from sage.arith.power import generic_power from sage.misc.misc import cputime from sage.misc.latex import latex from sage.misc.superseded import deprecation @@ -640,20 +640,6 @@ cdef class PolyDict: sage: g = PolyDict({(1,5):-3, (2,3):-2, (1,1):3}) sage: f*g PolyDict with representation {(2, 3): 9, (3, 2): 12, (2, 7): -9, (3, 5): -6, (4, 6): -4, (3, 4): 6, (3, 6): -12, (4, 4): -8, (3, 8): -6} - - Next we multiply two polynomials with fractional exponents in 3 variables:: - - # I've removed fractional exponent support in ETuple when moving to a sparse C integer array - #sage: f = PolyDict({(2/3,3,5):2, (1,2,1):3, (2,1,1):4}, force_int_exponents=False) - #sage: g = PolyDict({(2/3,3,5):3}, force_int_exponents=False) - #sage: f*g - #PolyDict with representation {(8/3, 4, 6): 12, (5/3, 5, 6): 9, (4/3, 6, 10): 6} - - Finally we print the result in a nice format. :: - - # I've removed fractional exponent support in ETuple when moving to a sparse C integer array - #sage: (f*g).poly_repr(['a', 'b', 'c'], atomic_exponents = False) - #'12*a^(8/3)*b^(4)*c^(6) + 9*a^(5/3)*b^(5)*c^(6) + 6*a^(4/3)*b^(6)*c^(10)' """ cdef PyObject *cc newpoly = {} @@ -760,7 +746,9 @@ cdef class PolyDict: sage: (f-f)**0 PolyDict with representation {0: 1} """ - return generic_power(self, n, self.__one()) + if not n: + return self.__one() + return generic_power(self, n) def lcmt(PolyDict self, greater_etuple): """ diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 3286fc27db2..15946bdd5dc 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -71,6 +71,7 @@ import sage.rings.infinity as infinity from sage.misc.sage_eval import sage_eval from sage.misc.abstract_method import abstract_method from sage.misc.latex import latex +from sage.arith.power cimport generic_power from sage.arith.long cimport pyobject_to_long from sage.structure.factorization import Factorization from sage.structure.richcmp cimport (richcmp, richcmp_item, @@ -88,7 +89,7 @@ from sage.rings.real_double import is_RealDoubleField, RDF from sage.rings.complex_double import is_ComplexDoubleField, CDF from sage.rings.real_mpfi import is_RealIntervalField -from sage.structure.element import generic_power, coerce_binop +from sage.structure.element import coerce_binop from sage.structure.element cimport (parent, have_same_parent, Element, RingElement, coercion_model) @@ -2249,7 +2250,7 @@ cdef class Polynomial(CommutativeAlgebraElement): Check that the algorithm used is indeed correct:: - sage: from sage.structure.element import generic_power + sage: from sage.arith.power import generic_power sage: R1 = PolynomialRing(GF(8,'a'), 'x') sage: R2 = PolynomialRing(GF(9,'b'), 'x', sparse=True) sage: R3 = PolynomialRing(R2, 'y') @@ -2257,7 +2258,7 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: for d in range(20,40): # long time ....: for R in [R1, R2, R3, R3]: ....: a = R.random_element() - ....: assert a^d == generic_power(a,d) + ....: assert a^d == generic_power(a, d) Test the powering modulo ``x^n`` (calling :meth:`power_trunc`):: @@ -2317,8 +2318,7 @@ cdef class Polynomial(CommutativeAlgebraElement): p = -1 if 0 < p <= right and (self.base_ring() in sage.categories.integral_domains.IntegralDomains() or p.is_prime()): x = self.parent().gen() - one = self.parent().one() - ret = one + ret = self.parent().one() e = 1 q = right sparse = self.parent().is_sparse() @@ -2336,11 +2336,11 @@ cdef class Polynomial(CommutativeAlgebraElement): for i in range(len(c)): tmp[e*i] = c[i]**e tmp = self.parent()(tmp) - ret *= generic_power(tmp, r, one=one) + ret *= generic_power(tmp, r) e *= p return ret - return generic_power(self,right) + return generic_power(self, right) def power_trunc(self, n, prec): r""" diff --git a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx index 62ed7fb6da7..c08edf50b83 100644 --- a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx @@ -48,7 +48,7 @@ from sage.rings.infinity import infinity from . import polynomial_singular_interface from sage.interfaces.all import singular as singular_default -from sage.structure.element import generic_power, canonical_coercion, bin_op, coerce_binop +from sage.structure.element import coerce_binop from sage.libs.ntl.types cimport NTL_SP_BOUND from sage.libs.ntl.ZZ_p cimport * diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index 2d6c17eb281..db72aa62023 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -4283,6 +4283,8 @@ def generic_power(a, n, one=None): sage: from sage.structure.element import generic_power sage: generic_power(int(12),int(0)) + doctest:...: DeprecationWarning: import 'generic_power' from sage.arith.power instead + See http://trac.sagemath.org/24256 for details. 1 sage: generic_power(int(0),int(100)) 0 @@ -4304,8 +4306,8 @@ def generic_power(a, n, one=None): sage: generic_power(int(5), 0) 1 """ - # from sage.misc.superseded import deprecation - # deprecation(24256, "import 'generic_power' from sage.arith.power instead") + from sage.misc.superseded import deprecation + deprecation(24256, "import 'generic_power' from sage.arith.power instead") if one is not None: # Special cases not handled by sage.arith.power if not n: diff --git a/src/sage/structure/factorization.py b/src/sage/structure/factorization.py index b3a5daeef11..e30d82dcc78 100644 --- a/src/sage/structure/factorization.py +++ b/src/sage/structure/factorization.py @@ -1125,8 +1125,11 @@ def __pow__(self, n): return Factorization([]) if self.is_commutative(): return Factorization([(p, n*e) for p, e in self], unit=self.unit()**n, cr=self.__cr, sort=False, simplify=False) - from sage.groups.generic import power - return power(self, n, Factorization([])) + if n < 0: + self = ~self + n = -n + from sage.arith.power import generic_power + return generic_power(self, n) def __invert__(self): r""" From af2c010783730449891f86576de4f8bc32471531 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 13 Feb 2018 22:19:32 +0100 Subject: [PATCH 717/740] Flush after showing doctest warning --- src/sage/doctest/forker.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index b88e6282fd6..b9f90127ef1 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -185,6 +185,7 @@ def showwarning_with_traceback(message, category, filename, lineno, file=sys.std lines.extend(traceback.format_exception_only(category, message)) try: file.writelines(lines) + file.flush() except IOError: pass # the file is invalid From cd25d3eaeb474e308c0839925b8c5b53d5834d24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Wed, 14 Feb 2018 08:34:54 +0200 Subject: [PATCH 718/740] Add two test for parameter value. --- src/sage/graphs/generic_graph.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index dc5cdd6018d..ad961731191 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -13016,7 +13016,7 @@ def random_subgraph(self, p, inplace=False): vertices.append(v) return self.subgraph(vertices=vertices, inplace=inplace) - def is_chordal(self, certificate = False, algorithm = "B"): + def is_chordal(self, certificate=False, algorithm="B"): r""" Tests whether the given graph is chordal. @@ -13160,6 +13160,9 @@ def is_chordal(self, certificate = False, algorithm = "B"): Pacific J. Math 1965 Vol. 15, number 3, pages 835--855 """ + if algorithm not in ['A', 'B']: + raise ValueError('unknown algorithm "{}"'.format(algorithm)) + self._scream_if_not_simple() # If the graph is not connected, we are computing the result on each component @@ -22364,7 +22367,7 @@ class by some canonization function `c`. If `G` and `H` are graphs, deprecation(19517, "Verbosity-parameter is removed.") # Check parameter combinations - if algorithm and algorithm not in ['sage', 'bliss']: + if algorithm not in [None, 'sage', 'bliss']: raise ValueError("'algorithm' must be equal to 'bliss', 'sage', or None") if algorithm != 'bliss' and not return_graph: raise ValueError("return_graph=False can only be used with algorithm='bliss'") From f855f208243324d4038306cb8d0f4a56eae2738e Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 14 Feb 2018 09:51:51 +0100 Subject: [PATCH 719/740] Further fixes to generic_power use --- src/sage/libs/ntl/ntl_ZZX.pyx | 23 +++++++++--- src/sage/libs/ntl/ntl_ZZ_pEX.pyx | 32 ++++++++++------ src/sage/libs/ntl/ntl_lzz_p.pyx | 56 ++++++++++++++++------------ src/sage/schemes/generic/morphism.py | 17 +++------ 4 files changed, 75 insertions(+), 53 deletions(-) diff --git a/src/sage/libs/ntl/ntl_ZZX.pyx b/src/sage/libs/ntl/ntl_ZZX.pyx index 836d5cd4223..03297686b2e 100644 --- a/src/sage/libs/ntl/ntl_ZZX.pyx +++ b/src/sage/libs/ntl/ntl_ZZX.pyx @@ -28,6 +28,7 @@ from sage.rings.integer import Integer from sage.rings.integer_ring import IntegerRing from sage.rings.integer cimport Integer from sage.rings.integer_ring cimport IntegerRing_class +from sage.arith.power cimport generic_power_pos ZZ = IntegerRing() @@ -414,16 +415,26 @@ cdef class ntl_ZZX(object): """ Return the n-th nonnegative power of self. - EXAMPLES: + EXAMPLES:: + sage: g = ntl.ZZX([-1,0,1]) - sage: g**10 + sage: g ^ 10 [1 0 -10 0 45 0 -120 0 210 0 -252 0 210 0 -120 0 45 0 -10 0 1] + sage: g ^ 0 + [1] + sage: g ^ 1 + [-1 0 1] + sage: g ^ (-1) + Traceback (most recent call last): + ... + ArithmeticError """ + if n == 0: + from copy import copy + return copy(one_ZZX) if n < 0: - raise NotImplementedError - import sage.groups.generic as generic - from copy import copy - return generic.power(self, n, copy(one_ZZX)) + raise ArithmeticError + return generic_power_pos(self, n) def __richcmp__(ntl_ZZX self, other, int op): """ diff --git a/src/sage/libs/ntl/ntl_ZZ_pEX.pyx b/src/sage/libs/ntl/ntl_ZZ_pEX.pyx index 9842b46b28e..7bee79e6fb1 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pEX.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pEX.pyx @@ -36,8 +36,8 @@ from sage.libs.ntl.ntl_ZZ_pX cimport ntl_ZZ_pX from sage.libs.ntl.ntl_ZZ_pEContext cimport ntl_ZZ_pEContext_class from sage.libs.ntl.ntl_ZZ_pEContext import ntl_ZZ_pEContext from sage.libs.ntl.ntl_ZZ_pContext cimport ntl_ZZ_pContext_class - from sage.libs.ntl.ntl_ZZ import unpickle_class_args +from sage.arith.power cimport generic_power_pos ############################################################################## # @@ -453,19 +453,29 @@ cdef class ntl_ZZ_pEX(object): """ Return the n-th nonnegative power of self. - EXAMPLES: - sage: c=ntl.ZZ_pEContext(ntl.ZZ_pX([1,1,1], 7)) - sage: a = ntl.ZZ_pE([3,2], c) - sage: b = ntl.ZZ_pE([1,2], c) - sage: f = ntl.ZZ_pEX([a, b, b]) - sage: f^5 - [[5 1] [2 6] [4 5] [5 1] [] [6 2] [2 3] [0 1] [1 4] [3 6] [2 4]] + EXAMPLES:: + + sage: c = ntl.ZZ_pEContext(ntl.ZZ_pX([1,1,1], 7)) + sage: a = ntl.ZZ_pE([3,2], c) + sage: b = ntl.ZZ_pE([1,2], c) + sage: f = ntl.ZZ_pEX([a, b, b]) + sage: f ^ 5 + [[5 1] [2 6] [4 5] [5 1] [] [6 2] [2 3] [0 1] [1 4] [3 6] [2 4]] + sage: f ^ 0 + [[1]] + sage: f ^ 1 + [[3 2] [1 2] [1 2]] + sage: f ^ (-1) + Traceback (most recent call last): + ... + ArithmeticError """ self.c.restore_c() + if n == 0: + return ntl_ZZ_pEX([[1]], self.c) if n < 0: - raise NotImplementedError - import sage.groups.generic as generic - return generic.power(self, n, ntl_ZZ_pEX([[1]],self.c)) + raise ArithmeticError + return generic_power_pos(self, n) def __richcmp__(ntl_ZZ_pEX self, other, int op): """ diff --git a/src/sage/libs/ntl/ntl_lzz_p.pyx b/src/sage/libs/ntl/ntl_lzz_p.pyx index 1c957a10fe3..36850937558 100644 --- a/src/sage/libs/ntl/ntl_lzz_p.pyx +++ b/src/sage/libs/ntl/ntl_lzz_p.pyx @@ -43,6 +43,7 @@ from sage.rings.finite_rings.integer_mod cimport IntegerMod_gmp, IntegerMod_int, from sage.libs.ntl.ntl_lzz_pContext import ntl_zz_pContext from sage.libs.ntl.ntl_lzz_pContext cimport ntl_zz_pContext_class +from sage.arith.power cimport generic_power_pos ZZ_sage = IntegerRing() @@ -253,37 +254,44 @@ cdef class ntl_zz_p(object): """ Return the n-th nonnegative power of self. - EXAMPLES: - sage: g = ntl.zz_p(5,13) - sage: g**10 + EXAMPLES:: + + sage: g = ntl.zz_p(5, 13) + sage: g ^ 10 12 - sage: g**(-1) + sage: g ^ (-1) 8 - sage: g**(-5) + sage: g ^ (-5) 8 + sage: g ^ 0 + 1 + sage: z = ntl.zz_p(0, 13) + sage: z ^ 0 + 1 + sage: z ^ 1 + 0 + sage: z ^ (-1) + Traceback (most recent call last): + ... + ZeroDivisionError: inverse does not exist """ - cdef ntl_zz_p y + self.c.restore_c() + if n == 0: + return ntl_zz_p(1, self.c) + if self.is_zero(): - if n == 0: - raise ArithmeticError("0^0 is undefined.") - elif n < 0: - raise ZeroDivisionError - else: + if n > 0: return self - else: - from sage.groups.generic import power + raise ZeroDivisionError("inverse does not exist") - self.c.restore_c() - - if n == 0: - return self - elif n < 0: - y = ntl_zz_p.__new__(ntl_zz_p) - y.c = self.c - zz_p_inv(y.x, self.x) - return power(y, -n, ntl_zz_p(1,self.c)) - else: - return power(self, n, ntl_zz_p(1,self.c)) + cdef ntl_zz_p y + if n > 0: + return generic_power_pos(self, n) + else: + y = ntl_zz_p.__new__(ntl_zz_p) + y.c = self.c + zz_p_inv(y.x, self.x) + return generic_power_pos(y, -n) def __neg__(self): """ diff --git a/src/sage/schemes/generic/morphism.py b/src/sage/schemes/generic/morphism.py index b8734e94620..359e527b5d7 100644 --- a/src/sage/schemes/generic/morphism.py +++ b/src/sage/schemes/generic/morphism.py @@ -62,10 +62,6 @@ that have been introduced in :trac:`14711`. """ -# Historical note: in trac #11599, V.B. renamed -# * _point_morphism_class -> _morphism -# * _homset_class -> _point_homset - #***************************************************************************** # Copyright (C) 2013 Simon King # Copyright (C) 2011 Volker Braun @@ -82,7 +78,8 @@ import operator from sage.structure.element import (AdditiveGroupElement, RingElement, - Element, generic_power, parent, coercion_model) + Element, parent, coercion_model) +from sage.arith.power import generic_power from sage.structure.richcmp import richcmp from sage.structure.sequence import Sequence from sage.categories.homset import Homset, Hom, End @@ -142,13 +139,9 @@ class SchemeMorphism(Element): .. TODO:: - Currently, :class:`SchemeMorphism` copies code from - :class:`~sage.categories.map.Map` rather than inheriting from it. This - is to work around a bug in Cython: We want to create a common - sub-class of :class:`~sage.structure.element.ModuleElement` and - :class:`SchemeMorphism`, but Cython would currently confuse cpdef - attributes of the two base classes. Proper inheritance should be used - as soon as this bug is fixed. See :trac:`14711`. + For historical reasons, :class:`SchemeMorphism` copies code from + :class:`~sage.categories.map.Map` rather than inheriting from it. + Proper inheritance should be used instead. See :trac:`14711`. EXAMPLES:: From aedad7fe3ce3903a7e5a01367a5f303832f3c9ae Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 21 Dec 2017 11:04:00 +0000 Subject: [PATCH 720/740] Various Python 3 fixes for sage.libs.singular * Mostly string handling fixes. * Some updates to the the reference counting code in sage.libs.singular.ring to use a defaultdict instead of a plain dict, which simplifies the code a bit. --- src/sage/libs/singular/function.pyx | 40 +++++++------ src/sage/libs/singular/polynomial.pyx | 11 ++-- src/sage/libs/singular/ring.pyx | 85 ++++++++++++++++++--------- src/sage/libs/singular/singular.pyx | 11 ++-- 4 files changed, 92 insertions(+), 55 deletions(-) diff --git a/src/sage/libs/singular/function.pyx b/src/sage/libs/singular/function.pyx index 72a1c185e0c..09e246b3130 100644 --- a/src/sage/libs/singular/function.pyx +++ b/src/sage/libs/singular/function.pyx @@ -79,6 +79,8 @@ from __future__ import absolute_import from libc.string cimport memcpy from cysignals.signals cimport sig_on, sig_off +from sage.cpython.string cimport str_to_bytes, char_to_str + from sage.structure.sage_object cimport SageObject from sage.structure.richcmp cimport richcmp @@ -111,6 +113,7 @@ from sage.misc.misc import get_verbose from sage.structure.sequence import Sequence, Sequence_generic from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence, PolynomialSequence_generic + cdef poly* sage_vector_to_poly(v, ring *r) except -1: """ Convert a vector or list of multivariate polynomials to a @@ -187,7 +190,7 @@ cdef class RingWrap: sage: ring(l, ring=P).var_names() ['x', 'y', 'z'] """ - return [self._ring.names[i] for i in range(self.ngens())] + return [char_to_str(self._ring.names[i]) for i in range(self.ngens())] def npars(self): """ @@ -219,9 +222,7 @@ cdef class RingWrap: sage: ring(l, ring=P).ordering_string() 'dp(3),C' """ - return rOrderingString(self._ring) - - + return char_to_str(rOrderingString(self._ring)) def par_names(self): """ @@ -237,7 +238,8 @@ cdef class RingWrap: sage: ring(l, ring=P).par_names() [] """ - return [n_ParameterNames(self._ring.cf)[i] for i in range(self.npars())] + return [char_to_str(n_ParameterNames(self._ring.cf)[i]) + for i in range(self.npars())] def characteristic(self): """ @@ -533,7 +535,7 @@ cdef class Converter(SageObject): elif isinstance(a, int) or isinstance(a, long): v = self.append_int(a) - elif isinstance(a, basestring): + elif isinstance(a, str): v = self.append_str(a) elif isinstance(a, Matrix_mpolynomial_dense): @@ -918,6 +920,7 @@ cdef class Converter(SageObject): """ Append the string ``n`` to the list. """ + n = str_to_bytes(n) cdef char *_n = n return self._append(omStrDup(_n), STRING_CMD) @@ -973,7 +976,10 @@ cdef class Converter(SageObject): return si2sa_intvec(to_convert.data) elif rtyp == STRING_CMD: - ret = to_convert.data + # TODO: Need to determine what kind of data can be returned by a + # STRING_CMD--is it just ASCII strings or can it be an arbitrary + # binary? + ret = char_to_str(to_convert.data) return ret elif rtyp == VECTOR_CMD: result = self.to_sage_vector_destructive( @@ -1311,7 +1317,7 @@ cdef class SingularFunction(SageObject): [[e + d + c + b + a, ...]] """ global dummy_ring - + if ring is None: ring = self.common_ring(args, ring) if ring is None: @@ -1520,7 +1526,7 @@ cdef inline call_function(SingularFunction self, tuple args, object R, bint sign if errorreported: errorreported = 0 - raise RuntimeError("error in Singular function call %r:\n%s"% + raise RuntimeError("error in Singular function call %r:\n%s" % (self._name, "\n".join(error_messages))) res = argument_list.to_python(_res) @@ -1560,7 +1566,7 @@ cdef class SingularLibraryFunction(SingularFunction): self.call_handler = self.get_call_handler() cdef BaseCallHandler get_call_handler(self): - cdef idhdl* singular_idhdl = ggetid(self._name) + cdef idhdl* singular_idhdl = ggetid(str_to_bytes(self._name)) if singular_idhdl==NULL: raise NameError("Singular library function {!r} is not defined".format(self._name)) if singular_idhdl.typ!=PROC_CMD: @@ -1571,7 +1577,7 @@ cdef class SingularLibraryFunction(SingularFunction): return res cdef bint function_exists(self): - cdef idhdl* singular_idhdl = ggetid(self._name) + cdef idhdl* singular_idhdl = ggetid(str_to_bytes(self._name)) return singular_idhdl!=NULL cdef class SingularKernelFunction(SingularFunction): @@ -1607,7 +1613,7 @@ cdef class SingularKernelFunction(SingularFunction): cdef BaseCallHandler get_call_handler(self): cdef int cmd_n = 0 - arity = IsCmd(self._name, cmd_n) # call by reverence for CMD_n + arity = IsCmd(str_to_bytes(self._name), cmd_n) # call by reverence for CMD_n if not cmd_n: raise NameError("Singular kernel function {!r} is not defined".format(self._name)) @@ -1615,7 +1621,7 @@ cdef class SingularKernelFunction(SingularFunction): cdef bint function_exists(self): cdef int cmd_n = -1 - arity = IsCmd(self._name, cmd_n) # call by reverence for CMD_n + arity = IsCmd(str_to_bytes(self._name), cmd_n) # call by reverence for CMD_n return cmd_n != -1 @@ -1831,7 +1837,7 @@ def lib(name): si_opt_2 &= ~Sy_bit(V_LOAD_LIB) si_opt_2 &= ~Sy_bit(V_REDEFINE) - cdef char* cname = omStrDup(name) + cdef char* cname = omStrDup(str_to_bytes(name)) sig_on() cdef bint failure = iiLibCmd(cname, 1, 1, 1) sig_off() @@ -1860,13 +1866,13 @@ def list_of_functions(packages=False): cdef idhdl *ph = NULL while h!=NULL: if PROC_CMD == IDTYP(h): - l.append(h.id) - if PACKAGE_CMD == IDTYP(h): + l.append(char_to_str(h.id)) + if PACKAGE_CMD == IDTYP(h): if packages: ph = IDPACKAGE(h).idroot while ph != NULL: if PROC_CMD == IDTYP(ph): - l.append(ph.id) + l.append(char_to_str(ph.id)) ph = IDNEXT(ph) h = IDNEXT(h) return l diff --git a/src/sage/libs/singular/polynomial.pyx b/src/sage/libs/singular/polynomial.pyx index 6ab52a58929..360b806e8fa 100644 --- a/src/sage/libs/singular/polynomial.pyx +++ b/src/sage/libs/singular/polynomial.pyx @@ -25,6 +25,8 @@ cdef extern from *: # hack to get at cython macro import re plusminus_pattern = re.compile("([^\(^])([\+\-])") +from sage.cpython.string cimport bytes_to_str, str_to_bytes + from sage.libs.singular.decl cimport number, ideal from sage.libs.singular.decl cimport currRing, rChangeCurrRing from sage.libs.singular.decl cimport p_Copy, p_Add_q, p_Neg, pp_Mult_nn, p_GetCoeff, p_IsConstant, p_Cmp, pNext @@ -123,7 +125,7 @@ cdef int singular_polynomial_rmul(poly **ret, poly *p, RingElement n, ring *r): 0 """ if(r != currRing): rChangeCurrRing(r) - cdef number *_n = sa2si(n,r) + cdef number *_n = sa2si(n, r) ret[0] = pp_Mult_nn(p, _n, r) n_Delete(&_n, r) return 0 @@ -428,8 +430,8 @@ cdef object singular_polynomial_str(poly *p, ring *r): """ if(r!=currRing): rChangeCurrRing(r) - s = p_String(p, r, r) - s = re.sub(plusminus_pattern, "\\1 \\2 ", s) + s = bytes_to_str(p_String(p, r, r)) + s = plusminus_pattern.sub("\\1 \\2 ", s) return s @@ -507,7 +509,6 @@ cdef object singular_polynomial_latex(poly *p, ring *r, object base, object late cdef object singular_polynomial_str_with_changed_varnames(poly *p, ring *r, object varnames): cdef char **_names cdef char **_orig_names - cdef char *_name cdef int i if len(varnames) != r.N: @@ -515,7 +516,7 @@ cdef object singular_polynomial_str_with_changed_varnames(poly *p, ring *r, obje _names = omAlloc0(sizeof(char*)*r.N) for i from 0 <= i < r.N: - _name = varnames[i] + _name = str_to_bytes(varnames[i]) _names[i] = omStrDup(_name) _orig_names = r.names diff --git a/src/sage/libs/singular/ring.pyx b/src/sage/libs/singular/ring.pyx index de6ec294abf..323fb1bb043 100644 --- a/src/sage/libs/singular/ring.pyx +++ b/src/sage/libs/singular/ring.pyx @@ -15,6 +15,8 @@ AUTHORS: #***************************************************************************** from __future__ import print_function +from sage.cpython.string cimport str_to_bytes + from sage.libs.gmp.types cimport __mpz_struct from sage.libs.gmp.mpz cimport mpz_init_set_ui, mpz_init_set @@ -39,6 +41,10 @@ from sage.rings.polynomial.term_order import TermOrder from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, MPolynomialRing_libsingular from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from cpython.object cimport Py_EQ, Py_NE + +from collections import defaultdict + # mapping str --> SINGULAR representation order_dict = { @@ -118,7 +124,6 @@ cdef ring *singular_ring_new(base_ring, n, names, term_order) except NULL: cdef ring* _ring cdef char **_names cdef char **_ext_names - cdef char *_name cdef int i,j cdef int nblcks cdef int offset @@ -147,10 +152,9 @@ cdef ring *singular_ring_new(base_ring, n, names, term_order) except NULL: nblcks = nbaseblcks + order.singular_moreblocks() offset = 0 - _names = omAlloc0(sizeof(char*)*(len(names))) for i from 0 <= i < n: - _name = names[i] + _name = str_to_bytes(names[i]) _names[i] = omStrDup(_name) # from the SINGULAR source code documentation for the rInit function @@ -233,7 +237,7 @@ cdef ring *singular_ring_new(base_ring, n, names, term_order) except NULL: _ext_names = omAlloc0(sizeof(char*)) extname = k.gen() - _name = k._names[0] + _name = str_to_bytes(k._names[0]) _ext_names[0] = omStrDup(_name) _cfr = rDefault( 0, 1, _ext_names ) @@ -284,7 +288,7 @@ cdef ring *singular_ring_new(base_ring, n, names, term_order) except NULL: cexponent = F[0][1] _ext_names = omAlloc0(sizeof(char*)) - _name = k._names[0] + _name = str_to_bytes(k._names[0]) _ext_names[0] = omStrDup(_name) _cfr = rDefault( modbase, 1, _ext_names ) @@ -328,6 +332,8 @@ cdef ring *singular_ring_new(base_ring, n, names, term_order) except NULL: elif ringtype == n_Z2m: _cf = nInitChar( n_Z2m, cexponent ) + else: + raise NotImplementedError(f"polynomials over {base_ring} are not supported in Singular") elif not isprime and ch.is_prime_power() and ch < ZZ(2)**160: @@ -372,7 +378,7 @@ cdef ring *singular_ring_new(base_ring, n, names, term_order) except NULL: rComplete(_ring, 1) _ring.ShortOut = 0 - + if order.is_local(): assert(_ring.OrdSgn == -1) if order.is_global(): @@ -382,7 +388,7 @@ cdef ring *singular_ring_new(base_ring, n, names, term_order) except NULL: ############################################################################# -ring_refcount_dict = {} +ring_refcount_dict = defaultdict(int) cdef class ring_wrapper_Py(object): @@ -412,7 +418,11 @@ cdef class ring_wrapper_Py(object): sage: from sage.libs.singular.ring import ring_wrapper_Py sage: t = ring_wrapper_Py(); t The ring pointer 0x0 - sage: TestSuite(t).run() + + These are just wrappers around a pointer, so it isn't really meaningful + to pickle them:: + + sage: TestSuite(t).run(skip='_test_pickling') """ self._ring = NULL @@ -452,10 +462,10 @@ cdef class ring_wrapper_Py(object): """ return 'The ring pointer '+hex(self.__hash__()) - def __cmp__(ring_wrapper_Py left, ring_wrapper_Py right): + def __richcmp__(self, other, op): """ - Compare ``left`` and ``right`` so that instances can be used - as dictionary keys. + Equality comparison between two ``ring_wrapper_Py`` instances, + for use when hashing. INPUT: @@ -463,21 +473,39 @@ cdef class ring_wrapper_Py(object): OUTPUT: - -1, 0, or +1 depending on whether ``left`` and ``right`` are - less than, equal, or greather than. + True if both ``ring_wrapper_Py`` wrap the same pointer. EXAMPLES:: - sage: from sage.libs.singular.ring import ring_wrapper_Py + sage: from sage.libs.singular.ring import (ring_wrapper_Py, + ....: currRing_wrapper) sage: t = ring_wrapper_Py() sage: t == t True + sage: P. = QQ[] + sage: t2 = currRing_wrapper() + sage: t3 = currRing_wrapper() + sage: t == t2 + False + sage: t2 == t3 + True + sage: t2 != t3 + False """ - if left._ring < right._ring: - return -1 - if left._ring > right._ring: - return +1 - return 0 + + cdef ring_wrapper_Py l, r + + if not (op == Py_EQ or op == Py_NE): + return NotImplemented + + if type(other) is not ring_wrapper_Py: + return op != Py_EQ + + l = self + r = other + + return ((l._ring == r._ring and op == Py_EQ) or + (l._ring != r._ring and op == Py_NE)) cdef wrap_ring(ring* R): @@ -521,9 +549,9 @@ cdef ring *singular_ring_reference(ring *existing_ring) except NULL: sage: from sage.libs.singular.groebner_strategy import GroebnerStrategy sage: from sage.libs.singular.ring import ring_refcount_dict sage: n = len(ring_refcount_dict) - sage: prev_rings = set(ring_refcount_dict.keys()) + sage: prev_rings = set(ring_refcount_dict) sage: P = MPolynomialRing_libsingular(GF(541), 2, ('x', 'y'), TermOrder('degrevlex', 2)) - sage: ring_ptr = set(ring_refcount_dict.keys()).difference(prev_rings).pop() + sage: ring_ptr = set(ring_refcount_dict).difference(prev_rings).pop() sage: ring_ptr # random output The ring pointer 0x7f78a646b8d0 sage: ring_refcount_dict[ring_ptr] @@ -543,10 +571,11 @@ cdef ring *singular_ring_reference(ring *existing_ring) except NULL: sage: ring_ptr in ring_refcount_dict True """ - if existing_ring==NULL: + if existing_ring == NULL: raise ValueError('singular_ring_reference(ring*) called with NULL pointer.') + cdef object r = wrap_ring(existing_ring) - ring_refcount_dict[r] = ring_refcount_dict.get(r,0)+1 + ring_refcount_dict[r] += 1 return existing_ring @@ -586,11 +615,12 @@ cdef void singular_ring_delete(ring *doomed): return cdef ring_wrapper_Py r = wrap_ring(doomed) - refcount = ring_refcount_dict.pop(r) - if refcount > 1: - ring_refcount_dict[r] = refcount-1 + ring_refcount_dict[r] -= 1 + if ring_refcount_dict[r] > 0: return + del ring_refcount_dict[r] + global currRing cdef ring *oldRing = currRing if currRing == doomed: @@ -602,8 +632,6 @@ cdef void singular_ring_delete(ring *doomed): rChangeCurrRing(oldRing) - - ############################################################################# # helpers for debugging @@ -658,6 +686,7 @@ cpdef print_currRing(): cdef size_t addr = currRing print("DEBUG: currRing == " + str(hex(addr))) + def currRing_wrapper(): """ Returns a wrapper for the current ring, for use in debugging ring_refcount_dict. diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index d73b55ed44b..152dab2c006 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -37,8 +37,9 @@ from sage.rings.finite_rings.finite_field_givaro import FiniteField_givaro from sage.rings.finite_rings.finite_field_ntl_gf2e import FiniteField_ntl_gf2e from sage.libs.pari.all import pari from sage.libs.gmp.all cimport * + from sage.cpython.string import FS_ENCODING -from sage.cpython.string cimport str_to_bytes +from sage.cpython.string cimport str_to_bytes, char_to_str from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular @@ -510,7 +511,7 @@ cdef number *sa2si_NF(object elem, ring *_ring): # so we hace to get/create one : # # todo: reuse qqr/ get an existing Singular polynomial ring over Q. - varname = "a" + varname = b"a" _name = omStrDup(varname) cdef char **_ext_names _ext_names = omAlloc0(sizeof(char*)) @@ -518,7 +519,7 @@ cdef number *sa2si_NF(object elem, ring *_ring): qqr = rDefault( 0, 1, _ext_names); rComplete(qqr,1) qqr.ShortOut = 0 - + nMapFuncPtr = naSetMap( qqr.cf , _ring.cf ) # choose correct mapping function cdef poly *_p @@ -607,7 +608,7 @@ cdef inline number *sa2si_ZZmod(IntegerMod_abstract d, ring *_ring): cdef int64_t _d cdef char *_name cdef char **_ext_names - varname = "a" + varname = b"a" cdef nMapFunc nMapFuncPtr = NULL; @@ -807,5 +808,5 @@ cdef init_libsingular(): init_libsingular() cdef void libsingular_error_callback(const_char_ptr s): - _s = s + _s = char_to_str(s) error_messages.append(_s) From 7b3a118f6f3ade74c0ce6ba85d28a4d80233230f Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 14 Feb 2018 10:40:17 +0100 Subject: [PATCH 721/740] Reviewer fixes to libsingular interface * Fix all compiler warnings * Simplify ring_wrapper_Py.__richcmp__ * Assorted minor fixes --- src/sage/libs/singular/function.pyx | 11 +++---- src/sage/libs/singular/polynomial.pyx | 8 ++--- src/sage/libs/singular/ring.pyx | 45 ++++++++++++--------------- src/sage/libs/singular/singular.pyx | 6 ++-- src/sage/rings/polynomial/plural.pyx | 6 ++-- 5 files changed, 33 insertions(+), 43 deletions(-) diff --git a/src/sage/libs/singular/function.pyx b/src/sage/libs/singular/function.pyx index 09e246b3130..87342e8a639 100644 --- a/src/sage/libs/singular/function.pyx +++ b/src/sage/libs/singular/function.pyx @@ -546,7 +546,7 @@ cdef class Converter(SageObject): elif isinstance(a, Matrix_generic_dense) and\ is_sage_wrapper_for_singular_ring(a.parent().base_ring()): - self.append_matrix(a) + v = self.append_matrix(a) elif isinstance(a, Resolution): v = self.append_resolution(a) @@ -594,8 +594,8 @@ cdef class Converter(SageObject): if attributes and a in attributes: for attrib in attributes[a]: if attrib == "isSB" : - val = int(attributes[a][attrib]) - atSet(v, omStrDup("isSB"), val, INT_CMD) + val = (attributes[a][attrib]) + atSet(v, omStrDup("isSB"), val, INT_CMD) setFlag(v, FLAG_STD) else: raise NotImplementedError("Support for attribute '%s' not implemented yet."%attrib) @@ -920,9 +920,8 @@ cdef class Converter(SageObject): """ Append the string ``n`` to the list. """ - n = str_to_bytes(n) - cdef char *_n = n - return self._append(omStrDup(_n), STRING_CMD) + b = str_to_bytes(n) + return self._append(omStrDup(b), STRING_CMD) cdef to_python(self, leftv* to_convert): """ diff --git a/src/sage/libs/singular/polynomial.pyx b/src/sage/libs/singular/polynomial.pyx index 360b806e8fa..0b501ed9b1e 100644 --- a/src/sage/libs/singular/polynomial.pyx +++ b/src/sage/libs/singular/polynomial.pyx @@ -462,14 +462,14 @@ cdef object singular_polynomial_latex(poly *p, ring *r, object base, object late \left(z + 1\right) v w - z w^{2} + z v + \left(-z - 1\right) w + z + 1 """ poly = "" - cdef unsigned long e,j - cdef int n = r.N + cdef unsigned long e + cdef int n = r.N, j cdef int atomic_repr = base._repr_option('element_is_atomic') while p: # First determine the multinomial: multi = "" - for j in range(1,n+1): + for j in range(1, n+1): e = p_GetExp(p, j, r) if e > 0: multi += " "+latex_gens[j-1] @@ -530,7 +530,6 @@ cdef object singular_polynomial_str_with_changed_varnames(poly *p, ring *r, obje return s cdef long singular_polynomial_deg(poly *p, poly *x, ring *r): - cdef int i cdef long _deg, deg deg = -1 @@ -547,6 +546,7 @@ cdef long singular_polynomial_deg(poly *p, poly *x, ring *r): p = pNext(p) return deg + cdef int i = 0 for i in range(1,r.N+1): if p_GetExp(x, i, r): break diff --git a/src/sage/libs/singular/ring.pyx b/src/sage/libs/singular/ring.pyx index 323fb1bb043..5dd992fbb75 100644 --- a/src/sage/libs/singular/ring.pyx +++ b/src/sage/libs/singular/ring.pyx @@ -315,26 +315,23 @@ cdef ring *singular_ring_new(base_ring, n, names, term_order) except NULL: exponent = ch.nbits() -1 cexponent = exponent - if exponent <= 30: ringtype = n_Z2m - else: ringtype = n_Znm + if exponent <= 30: + ringtype = n_Z2m + else: + ringtype = n_Znm if ringtype == n_Znm: + F = ch.factor() - F = ch.factor() - - modbase = F[0][0] - cexponent = F[0][1] - - _info.base = <__mpz_struct*>omAlloc(sizeof(__mpz_struct)) - mpz_init_set_ui(_info.base, modbase) - _info.exp = cexponent - _cf = nInitChar( n_Znm, &_info ) - - elif ringtype == n_Z2m: - _cf = nInitChar( n_Z2m, cexponent ) - else: - raise NotImplementedError(f"polynomials over {base_ring} are not supported in Singular") + modbase = F[0][0] + cexponent = F[0][1] + _info.base = <__mpz_struct*>omAlloc(sizeof(__mpz_struct)) + mpz_init_set_ui(_info.base, modbase) + _info.exp = cexponent + _cf = nInitChar(ringtype, &_info) + else: # ringtype == n_Z2m + _cf = nInitChar(ringtype, cexponent) elif not isprime and ch.is_prime_power() and ch < ZZ(2)**160: F = ch.factor() @@ -462,7 +459,9 @@ cdef class ring_wrapper_Py(object): """ return 'The ring pointer '+hex(self.__hash__()) - def __richcmp__(self, other, op): + # This could be written using __eq__ but that does not work + # due to https://github.com/cython/cython/issues/2019 + def __richcmp__(ring_wrapper_Py self, other, int op): """ Equality comparison between two ``ring_wrapper_Py`` instances, for use when hashing. @@ -491,21 +490,17 @@ cdef class ring_wrapper_Py(object): True sage: t2 != t3 False + sage: t2 == None + False """ - - cdef ring_wrapper_Py l, r - if not (op == Py_EQ or op == Py_NE): return NotImplemented if type(other) is not ring_wrapper_Py: return op != Py_EQ - l = self r = other - - return ((l._ring == r._ring and op == Py_EQ) or - (l._ring != r._ring and op == Py_NE)) + return (self._ring == r._ring) == (op == Py_EQ) cdef wrap_ring(ring* R): @@ -571,7 +566,7 @@ cdef ring *singular_ring_reference(ring *existing_ring) except NULL: sage: ring_ptr in ring_refcount_dict True """ - if existing_ring == NULL: + if existing_ring is NULL: raise ValueError('singular_ring_reference(ring*) called with NULL pointer.') cdef object r = wrap_ring(existing_ring) diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index 152dab2c006..16f76e960fd 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -511,8 +511,7 @@ cdef number *sa2si_NF(object elem, ring *_ring): # so we hace to get/create one : # # todo: reuse qqr/ get an existing Singular polynomial ring over Q. - varname = b"a" - _name = omStrDup(varname) + _name = omStrDup("a") cdef char **_ext_names _ext_names = omAlloc0(sizeof(char*)) _ext_names[0] = omStrDup(_name) @@ -608,7 +607,6 @@ cdef inline number *sa2si_ZZmod(IntegerMod_abstract d, ring *_ring): cdef int64_t _d cdef char *_name cdef char **_ext_names - varname = b"a" cdef nMapFunc nMapFuncPtr = NULL; @@ -623,7 +621,7 @@ cdef inline number *sa2si_ZZmod(IntegerMod_abstract d, ring *_ring): # create ZZr, a plain polynomial ring over ZZ with one variable. # # todo (later): reuse ZZr - _name = omStrDup(varname) + _name = omStrDup("a") _ext_names = omAlloc0(sizeof(char*)) _ext_names[0] = omStrDup(_name) _cf = nInitChar( n_Z, NULL) # integer coefficient ring diff --git a/src/sage/rings/polynomial/plural.pyx b/src/sage/rings/polynomial/plural.pyx index 3b45a38419a..29f332c0e1b 100644 --- a/src/sage/rings/polynomial/plural.pyx +++ b/src/sage/rings/polynomial/plural.pyx @@ -2724,8 +2724,7 @@ cpdef MPolynomialRing_libsingular new_CRing(RingWrap rw, base_ring): self._ring = rw._ring wrapped_ring = wrap_ring(self._ring) - cur_refcnt = sage.libs.singular.ring.ring_refcount_dict.get(wrapped_ring, 0) - sage.libs.singular.ring.ring_refcount_dict[wrapped_ring] = cur_refcnt + 1 + sage.libs.singular.ring.ring_refcount_dict[wrapped_ring] += 1 self._ring.ShortOut = 0 @@ -2795,8 +2794,7 @@ cpdef NCPolynomialRing_plural new_NRing(RingWrap rw, base_ring): self._ring = rw._ring wrapped_ring = wrap_ring(self._ring) - cur_refcnt = sage.libs.singular.ring.ring_refcount_dict.get(wrapped_ring, 0) - sage.libs.singular.ring.ring_refcount_dict[wrapped_ring] = cur_refcnt + 1 + sage.libs.singular.ring.ring_refcount_dict[wrapped_ring] += 1 self._ring.ShortOut = 0 From 27cf62b66d5e95a88e0c2f83e0abd0f5c86d2380 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 14 Feb 2018 12:12:31 +0100 Subject: [PATCH 722/740] Clean up pynac type checks --- src/sage/libs/pynac/pynac.pyx | 44 +++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/sage/libs/pynac/pynac.pyx b/src/sage/libs/pynac/pynac.pyx index 5426c68854a..9e9d8f664bb 100644 --- a/src/sage/libs/pynac/pynac.pyx +++ b/src/sage/libs/pynac/pynac.pyx @@ -342,7 +342,7 @@ cdef stdstring* py_repr(o, int level): t = s # Python complexes are always printed with parentheses # we try to avoid double parentheses - if type(o) is not complex and \ + if not isinstance(o, complex) and \ (' ' in t or '/' in t or '+' in t or '-' in t or '*' in t \ or '^' in t): s = '(%s)'%s @@ -983,9 +983,9 @@ cdef py_real(x): sage: py_real(complex(2,2)) 2.0 """ - if type(x) is float or type(x) in [int, long]: + if isinstance(x, (float, int, long)): return x - elif type(x) is complex: + elif isinstance(x, complex): return x.real try: @@ -1039,9 +1039,9 @@ cdef py_imag(x): sage: py_imag(complex(2,2)) 2.0 """ - if type(x) is float: - return float(0) - if type(x) is complex: + if isinstance(x, float): + return 0.0 + if isinstance(x, complex): return x.imag try: return x.imag() @@ -1114,10 +1114,13 @@ cdef bint py_is_integer(x): sage: py_is_integer(3.0r) False """ - return (isinstance(x, (int, long, Integer,)) or - (isinstance(x, Element) and - ((x)._parent.is_exact() or - (x)._parent == ring.SR) and (x in ZZ))) + if isinstance(x, (int, long, Integer)): + return True + if not isinstance(x, Element): + return False + P = (x)._parent + return (P is ring.SR or P.is_exact()) and x in ZZ + def py_is_integer_for_doctests(x): """ @@ -1174,8 +1177,7 @@ def py_is_crational_for_doctest(x): return py_is_crational(x) cdef bint py_is_real(a): - if (type(a) in [int, long] or isinstance(a, Integer) or - type(a) is float): + if isinstance(a, (int, long, Integer, float)): return True try: P = parent(a) @@ -1200,10 +1202,12 @@ cdef bint py_is_prime(n): cdef bint py_is_exact(x): - return (isinstance(x, (int, long, Integer,)) or - (isinstance(x, Element) and - ((x)._parent.is_exact() or - (x)._parent == ring.SR))) + if isinstance(x, (int, long, Integer)): + return True + if not isinstance(x, Element): + return False + P = (x)._parent + return P is ring.SR or P.is_exact() cdef py_numer(n): @@ -1232,7 +1236,7 @@ cdef py_numer(n): sage: py_numer(no_numer()) 42 """ - if isinstance(n, (int, long, Integer,)): + if isinstance(n, (int, long, Integer)): return n try: return n.numerator() @@ -1270,7 +1274,7 @@ cdef py_denom(n): sage: py_denom(2/3*i) 3 """ - if isinstance(n, (int, long, Integer,)): + if isinstance(n, (int, long, Integer)): return 1 try: return n.denominator() @@ -1391,7 +1395,7 @@ cdef py_tgamma(x): sage: py_tgamma(1/2) 1.77245385090552 """ - if type(x) in [int, long]: + if isinstance(x, (int, long)): x = float(x) if type(x) is float: return math.tgamma(PyFloat_AS_DOUBLE(x)) @@ -1712,7 +1716,7 @@ cdef py_log(x): """ cdef gsl_complex res cdef double real, imag - if type(x) in [int, long]: + if isinstance(x, (int, long)): x = float(x) if type(x) is float: real = PyFloat_AS_DOUBLE(x) From e64c034e0ae46dc08ff444bb8a6b85430d2b1c26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 15 Feb 2018 13:27:26 +1300 Subject: [PATCH 723/740] Fix doctest in cpython/debug.pyx --- src/sage/cpython/debug.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/cpython/debug.pyx b/src/sage/cpython/debug.pyx index a227bbf1c04..d46300fe39b 100644 --- a/src/sage/cpython/debug.pyx +++ b/src/sage/cpython/debug.pyx @@ -97,7 +97,7 @@ def getattr_debug(obj, name, default=_no_default): returning log () sage: from ipywidgets import IntSlider sage: _ = getattr_debug(IntSlider(), "value") - getattr_debug(obj=IntSlider(value=0, min=0, max=100, step=1), name='value'): + getattr_debug(obj=IntSlider(value=0), name='value'): type(obj) = object has __dict__ slot () found 'value' in dict of From d85d8128baed9ac8169ac415b6cb34eec3ee0d96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 15 Feb 2018 09:08:28 +0100 Subject: [PATCH 724/740] use https://trac in deprecation links (after #24700) --- src/sage/misc/superseded.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/sage/misc/superseded.py b/src/sage/misc/superseded.py index 2628bde18be..773a552492b 100644 --- a/src/sage/misc/superseded.py +++ b/src/sage/misc/superseded.py @@ -66,9 +66,10 @@ def _check_trac_number(trac_number): try: trac_number = trac_number.__index__() except Exception: - raise TypeError('%r is not a valid trac issue number'%trac_number) + raise TypeError('%r is not a valid trac issue number' % trac_number) if trac_number <= 0: - raise ValueError('%r is not a valid trac issue number'%trac_number) + raise ValueError('%r is not a valid trac issue number' % trac_number) + def deprecation(trac_number, message, stacklevel=4): r""" @@ -100,6 +101,7 @@ def deprecation(trac_number, message, stacklevel=4): """ warning(trac_number, message, DeprecationWarning, stacklevel) + def warning(trac_number, message, warning_class=Warning, stacklevel=3): r""" Issue a warning. @@ -126,7 +128,7 @@ def warning(trac_number, message, warning_class=Warning, stacklevel=3): ....: FutureWarning) sage: foo() doctest:...: FutureWarning: The syntax will change in future. - See http://trac.sagemath.org/99999 for details. + See https://trac.sagemath.org/99999 for details. .. SEEALSO:: @@ -136,11 +138,16 @@ def warning(trac_number, message, warning_class=Warning, stacklevel=3): """ _check_trac_number(trac_number) message += '\n' - message += 'See http://trac.sagemath.org/'+ str(trac_number) + ' for details.' + if trac_number < 24700: # to avoid changing all previous doctests + message += 'See http://trac.sagemath.org/'+ str(trac_number) + ' for details.' + else: + message += 'See https://trac.sagemath.org/'+ str(trac_number) + ' for details.' + # Stack level 3 to get the line number of the code which called # the deprecated function which called this function. warn(message, warning_class, stacklevel) + def experimental_warning(trac_number, message, stacklevel=4): r""" Issue a warning that the functionality or class is experimental @@ -165,7 +172,7 @@ def experimental_warning(trac_number, message, stacklevel=4): sage: foo() doctest:...: FutureWarning: This function is experimental and might change in future. - See http://trac.sagemath.org/66666 for details. + See https://trac.sagemath.org/66666 for details. .. SEEALSO:: @@ -199,7 +206,7 @@ def __init__(self, trac_number, stacklevel=4): doctest:...: FutureWarning: This class/method/function is marked as experimental. It, its functionality or its interface might change without a formal deprecation. - See http://trac.sagemath.org/79997 for details. + See https://trac.sagemath.org/79997 for details. (7,) {'what': 'Hello'} :: @@ -212,7 +219,7 @@ def __init__(self, trac_number, stacklevel=4): doctest:...: FutureWarning: This class/method/function is marked as experimental. It, its functionality or its interface might change without a formal deprecation. - See http://trac.sagemath.org/99999 for details. + See https://trac.sagemath.org/99999 for details. piep (99,) {} TESTS: @@ -227,7 +234,7 @@ def __init__(self, trac_number, stacklevel=4): doctest:...: FutureWarning: This class/method/function is marked as experimental. It, its functionality or its interface might change without a formal deprecation. - See http://trac.sagemath.org/88888 for details. + See https://trac.sagemath.org/88888 for details. I'm A .. SEEALSO:: @@ -261,7 +268,7 @@ def __call__(self, func): doctest:...: FutureWarning: This class/method/function is marked as experimental. It, its functionality or its interface might change without a formal deprecation. - See http://trac.sagemath.org/99399 for details. + See https://trac.sagemath.org/99399 for details. (3,) {'what': 'Hello'} """ from sage.misc.decorators import sage_wraps @@ -420,7 +427,7 @@ def __call__(self, *args, **kwds): else: return self.func(self.instance, *args, **kwds) - def __get__(self, inst, cls = None): + def __get__(self, inst, cls=None): """ TESTS:: From 0f854423130c5d5918744ba08b3a80ed65a27ba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 15 Feb 2018 10:15:25 +0100 Subject: [PATCH 725/740] use https in standard licence header --- src/doc/en/developer/coding_basics.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index 423da9d7f43..15c45531abe 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -19,7 +19,7 @@ definitions, etc., are informed by how the corresponding objects are used in everyday mathematics. .. [1] - See http://www.sagemath.org/links-components.html for a full list + See https://www.sagemath.org/links-components.html for a full list of packages shipped with every copy of Sage To meet the goal of making Sage easy to read, maintain, and improve, @@ -35,8 +35,8 @@ Python Code Style Follow the standard Python formatting rules when writing code for Sage, as explained at the following URLs: -* http://www.python.org/dev/peps/pep-0008 -* http://www.python.org/dev/peps/pep-0257 +* https://www.python.org/dev/peps/pep-0008 +* https://www.python.org/dev/peps/pep-0257 In particular, @@ -206,7 +206,7 @@ The top of each Sage code file should follow this format:: # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. - # http://www.gnu.org/licenses/ + # https://www.gnu.org/licenses/ # **************************************************************************** As an example, see ``SAGE_ROOT/src/sage/rings/integer.pyx``, which contains the From 78be0c66bce4aa98ea03ea1cef5b002b928910bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 15 Feb 2018 10:20:12 +0100 Subject: [PATCH 726/740] using pep roles --- src/doc/en/developer/coding_basics.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index 15c45531abe..036d52c3b4f 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -35,8 +35,8 @@ Python Code Style Follow the standard Python formatting rules when writing code for Sage, as explained at the following URLs: -* https://www.python.org/dev/peps/pep-0008 -* https://www.python.org/dev/peps/pep-0257 +* :pep:`0008` +* :pep:`0257` In particular, From d7d73cc2361a688486ed293119f92cbd58ff858f Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 15 Feb 2018 10:50:56 +0100 Subject: [PATCH 727/740] Make Graph.is_asteroidal_triple_free usable as method --- src/sage/graphs/asteroidal_triples.pyx | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/sage/graphs/asteroidal_triples.pyx b/src/sage/graphs/asteroidal_triples.pyx index 38e87a2c6fe..46a4c9810c8 100644 --- a/src/sage/graphs/asteroidal_triples.pyx +++ b/src/sage/graphs/asteroidal_triples.pyx @@ -1,3 +1,4 @@ +# cython: binding=True r""" Asteroidal triples @@ -105,26 +106,25 @@ def is_asteroidal_triple_free(G, certificate=False): The complete graph is AT-free, as well as its line graph:: - sage: from sage.graphs.asteroidal_triples import * sage: G = graphs.CompleteGraph(5) - sage: is_asteroidal_triple_free(G) + sage: G.is_asteroidal_triple_free() True - sage: is_asteroidal_triple_free(G, certificate=True) + sage: G.is_asteroidal_triple_free(certificate=True) (True, []) sage: LG = G.line_graph() - sage: is_asteroidal_triple_free(LG) + sage: LG.is_asteroidal_triple_free() True sage: LLG = LG.line_graph() - sage: is_asteroidal_triple_free(LLG) + sage: LLG.is_asteroidal_triple_free() False The PetersenGraph is not AT-free:: sage: from sage.graphs.asteroidal_triples import * sage: G = graphs.PetersenGraph() - sage: is_asteroidal_triple_free(G) + sage: G.is_asteroidal_triple_free() False - sage: is_asteroidal_triple_free(G, certificate=True) + sage: G.is_asteroidal_triple_free(certificate=True) (False, [0, 2, 6]) TESTS: @@ -136,7 +136,6 @@ def is_asteroidal_triple_free(G, certificate=False): Traceback (most recent call last): ... ValueError: The first parameter must be a Graph. - """ from sage.graphs.graph import Graph if not isinstance(G, Graph): From 103dba1c7101957a8188ede5d702adee9d4a7391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 15 Feb 2018 11:47:46 +0100 Subject: [PATCH 728/740] use future ticket number --- src/sage/misc/superseded.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/misc/superseded.py b/src/sage/misc/superseded.py index 773a552492b..8dc60fa8da4 100644 --- a/src/sage/misc/superseded.py +++ b/src/sage/misc/superseded.py @@ -138,7 +138,7 @@ def warning(trac_number, message, warning_class=Warning, stacklevel=3): """ _check_trac_number(trac_number) message += '\n' - if trac_number < 24700: # to avoid changing all previous doctests + if trac_number < 24800: # to avoid changing all previous doctests message += 'See http://trac.sagemath.org/'+ str(trac_number) + ' for details.' else: message += 'See https://trac.sagemath.org/'+ str(trac_number) + ' for details.' From fd742401fa9ccae4d121f5a1c71b18d2d0ea7041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 15 Feb 2018 11:58:14 +0100 Subject: [PATCH 729/740] use the pep role in other places --- src/doc/en/faq/faq-contribute.rst | 4 +--- src/sage/misc/lazy_string.pyx | 2 +- .../modules/with_basis/indexed_element.pyx | 9 ++++----- src/sage/repl/preparse.py | 18 ++++++++---------- 4 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/doc/en/faq/faq-contribute.rst b/src/doc/en/faq/faq-contribute.rst index 75e934b1357..b9cc732799f 100644 --- a/src/doc/en/faq/faq-contribute.rst +++ b/src/doc/en/faq/faq-contribute.rst @@ -181,9 +181,7 @@ Are there any coding conventions I need to follow? """""""""""""""""""""""""""""""""""""""""""""""""" You should follow the standard Python conventions as documented at -`PEP 0008 `_ -and -`PEP 0257 `_. +:pep:`0008` and :pep:`0257`. Also consult the Sage Developer's Guide, especially the chapter `Conventions for Coding in Sage `_. diff --git a/src/sage/misc/lazy_string.pyx b/src/sage/misc/lazy_string.pyx index cd849d89e8e..dad0fcd2cd4 100644 --- a/src/sage/misc/lazy_string.pyx +++ b/src/sage/misc/lazy_string.pyx @@ -330,7 +330,7 @@ cdef class _LazyString(object): ``self`` is a path. This is for Python 3 compatibility: see :trac:`24046`, and also - https://www.python.org/dev/peps/pep-0519/ and + :pep:`0519` and https://docs.python.org/3/library/os.html#os.fspath Test :trac:`24046`:: diff --git a/src/sage/modules/with_basis/indexed_element.pyx b/src/sage/modules/with_basis/indexed_element.pyx index b33b69d845c..3b36365e366 100644 --- a/src/sage/modules/with_basis/indexed_element.pyx +++ b/src/sage/modules/with_basis/indexed_element.pyx @@ -117,11 +117,10 @@ cdef class IndexedFreeModuleElement(ModuleElement): 6920829894162680369 # 64-bit -528971215 # 32-bit - This uses the recipe that was proposed for frozendicts in `PEP - 0416 `_ (and adds - the hash of the parent). This recipe relies on the hash - function for frozensets which uses tricks to mix the hash - values of the items in case they are similar. + This uses the recipe that was proposed for frozendicts in + :pep:`0416` (and adds the hash of the parent). This recipe + relies on the hash function for frozensets which uses tricks + to mix the hash values of the items in case they are similar. .. TODO:: diff --git a/src/sage/repl/preparse.py b/src/sage/repl/preparse.py index fef2ceb9952..b5789536068 100644 --- a/src/sage/repl/preparse.py +++ b/src/sage/repl/preparse.py @@ -1520,19 +1520,17 @@ def handle_encoding_declaration(contents, out): '#!/usr/local/bin/python\nimport os, sys' - NOTES: + .. NOTES:: - - PEP 263: http://www.python.org/dev/peps/pep-0263/ + - PEP 263 (:pep:`0263`) says that Python will interpret a UTF-8 + byte order mark as a declaration of UTF-8 encoding, but I don't + think we do that; this function only sees a Python string so it + can't account for a BOM. - - PEP 263 says that Python will interpret a UTF-8 byte order mark - as a declaration of UTF-8 encoding, but I don't think we do - that; this function only sees a Python string so it can't - account for a BOM. + - We default to UTF-8 encoding even though PEP 263 says that + Python files should default to ASCII. - - We default to UTF-8 encoding even though PEP 263 says that - Python files should default to ASCII. - - - Also see http://docs.python.org/ref/encodings.html. + - Also see https://docs.python.org/ref/encodings.html. AUTHORS: From 3885ba55680efa03699c7388ec4602e645f1e92b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 15 Feb 2018 10:26:30 +1300 Subject: [PATCH 730/740] Remove "autoconf" as part of the version and make it part of checksum --- build/pkgs/sqlite/checksums.ini | 2 +- build/pkgs/sqlite/package-version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/sqlite/checksums.ini b/build/pkgs/sqlite/checksums.ini index c81f76dd9e8..1095b6310f6 100644 --- a/build/pkgs/sqlite/checksums.ini +++ b/build/pkgs/sqlite/checksums.ini @@ -1,4 +1,4 @@ -tarball=sqlite-VERSION.tar.gz +tarball=sqlite-autoconf-VERSION.tar.gz sha1=2fb24ec12001926d5209d2da90d252b9825366ac md5=96b5648d542e8afa6ab7ffb8db8ddc3d cksum=1556846983 diff --git a/build/pkgs/sqlite/package-version.txt b/build/pkgs/sqlite/package-version.txt index b3793d498a2..a5e894e3794 100644 --- a/build/pkgs/sqlite/package-version.txt +++ b/build/pkgs/sqlite/package-version.txt @@ -1 +1 @@ -autoconf-3220000.p0 +3220000 From 46ff2b1579b6434ed9f05b06c25973c4b48dd410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 15 Feb 2018 13:29:38 +0100 Subject: [PATCH 731/740] no leading zero in :pep: --- src/doc/en/faq/faq-contribute.rst | 2 +- src/sage/misc/lazy_string.pyx | 2 +- src/sage/modules/with_basis/indexed_element.pyx | 2 +- src/sage/repl/preparse.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/en/faq/faq-contribute.rst b/src/doc/en/faq/faq-contribute.rst index b9cc732799f..8e1df816803 100644 --- a/src/doc/en/faq/faq-contribute.rst +++ b/src/doc/en/faq/faq-contribute.rst @@ -181,7 +181,7 @@ Are there any coding conventions I need to follow? """""""""""""""""""""""""""""""""""""""""""""""""" You should follow the standard Python conventions as documented at -:pep:`0008` and :pep:`0257`. +:pep:`8` and :pep:`257`. Also consult the Sage Developer's Guide, especially the chapter `Conventions for Coding in Sage `_. diff --git a/src/sage/misc/lazy_string.pyx b/src/sage/misc/lazy_string.pyx index dad0fcd2cd4..cfe21bd6fe2 100644 --- a/src/sage/misc/lazy_string.pyx +++ b/src/sage/misc/lazy_string.pyx @@ -330,7 +330,7 @@ cdef class _LazyString(object): ``self`` is a path. This is for Python 3 compatibility: see :trac:`24046`, and also - :pep:`0519` and + :pep:`519` and https://docs.python.org/3/library/os.html#os.fspath Test :trac:`24046`:: diff --git a/src/sage/modules/with_basis/indexed_element.pyx b/src/sage/modules/with_basis/indexed_element.pyx index 3b36365e366..1712036f39a 100644 --- a/src/sage/modules/with_basis/indexed_element.pyx +++ b/src/sage/modules/with_basis/indexed_element.pyx @@ -118,7 +118,7 @@ cdef class IndexedFreeModuleElement(ModuleElement): -528971215 # 32-bit This uses the recipe that was proposed for frozendicts in - :pep:`0416` (and adds the hash of the parent). This recipe + :pep:`416` (and adds the hash of the parent). This recipe relies on the hash function for frozensets which uses tricks to mix the hash values of the items in case they are similar. diff --git a/src/sage/repl/preparse.py b/src/sage/repl/preparse.py index b5789536068..c1893194068 100644 --- a/src/sage/repl/preparse.py +++ b/src/sage/repl/preparse.py @@ -1522,7 +1522,7 @@ def handle_encoding_declaration(contents, out): .. NOTES:: - - PEP 263 (:pep:`0263`) says that Python will interpret a UTF-8 + - PEP 263 (:pep:`263`) says that Python will interpret a UTF-8 byte order mark as a declaration of UTF-8 encoding, but I don't think we do that; this function only sees a Python string so it can't account for a BOM. From 5c709e6962658a6faa2ebfe73dfc424577f05ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 15 Feb 2018 13:30:56 +0100 Subject: [PATCH 732/740] more details about :pep: --- src/sage/repl/preparse.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/repl/preparse.py b/src/sage/repl/preparse.py index c1893194068..d085a079731 100644 --- a/src/sage/repl/preparse.py +++ b/src/sage/repl/preparse.py @@ -1520,9 +1520,9 @@ def handle_encoding_declaration(contents, out): '#!/usr/local/bin/python\nimport os, sys' - .. NOTES:: + .. NOTE:: - - PEP 263 (:pep:`263`) says that Python will interpret a UTF-8 + - :pep:`263` says that Python will interpret a UTF-8 byte order mark as a declaration of UTF-8 encoding, but I don't think we do that; this function only sees a Python string so it can't account for a BOM. From 9985ee8a8d6d096d9c8507ca750dabc5f92e347f Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Fri, 19 Jan 2018 13:37:12 +0000 Subject: [PATCH 733/740] =?UTF-8?q?Deprecate=20RealNumber.=5F=5Fhex=5F=5F?= =?UTF-8?q?=20and=20replace=20it=20(especially=20for=20Python=203)=20with?= =?UTF-8?q?=20RealNumber.hex=20=C3=A0=20la=20#24514?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sage/rings/real_mpfr.pyx | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 9b5da88a595..0375310dfd7 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -92,7 +92,7 @@ TESTS:: sage: -1e30 -1.00000000000000e30 - sage: hex(-1. + 2^-52) + sage: (-1. + 2^-52).hex() '-0xf.ffffffffffffp-4' Make sure we don't have a new field for every new literal:: @@ -131,6 +131,7 @@ from sage.libs.gmp.mpz cimport * from sage.libs.mpfr cimport * from sage.misc.randstate cimport randstate, current_randstate from sage.cpython.string cimport char_to_str +from sage.misc.superseded import deprecation from sage.structure.element cimport RingElement, Element, ModuleElement from sage.structure.richcmp cimport rich_to_bool_sgn @@ -2040,23 +2041,23 @@ cdef class RealNumber(sage.structure.element.RingElement): return z - def __hex__(self): + def hex(self): """ Return a hexadecimal floating-point representation of ``self``, in the style of C99 hexadecimal floating-point constants. EXAMPLES:: - sage: hex(RR(-1/3)) + sage: RR(-1/3).hex() '-0x5.5555555555554p-4' - sage: hex(Reals(100)(123.456e789)) + sage: Reals(100)(123.456e789).hex() '0xf.721008e90630c8da88f44dd2p+2624' - sage: hex((-0.)) + sage: (-0.).hex() '-0x0p+0' :: - sage: [(hex(a), float(a).hex()) for a in [.5, 1., 2., 16.]] + sage: [(a.hex(), float(a).hex()) for a in [.5, 1., 2., 16.]] [('0x8p-4', '0x1.0000000000000p-1'), ('0x1p+0', '0x1.0000000000000p+0'), ('0x2p+0', '0x1.0000000000000p+1'), @@ -2064,7 +2065,7 @@ cdef class RealNumber(sage.structure.element.RingElement): Special values:: - sage: [hex(RR(s)) for s in ['+inf', '-inf', 'nan']] + sage: [RR(s).hex() for s in ['+inf', '-inf', 'nan']] ['inf', '-inf', 'nan'] """ cdef char *s @@ -2074,10 +2075,23 @@ cdef class RealNumber(sage.structure.element.RingElement): sig_off() if r < 0: # MPFR free()s its buffer itself in this case raise RuntimeError("unable to convert an mpfr number to a string") - t = str(s) + t = char_to_str(s) mpfr_free_str(s) return t + def __hex__(self): + """ + TESTS:: + + sage: hex(RR(-1/3)) # py2 + doctest:...: + DeprecationWarning: use the method .hex instead + See http://trac.sagemath.org/24568 for details. + '-0x5.5555555555554p-4' + """ + deprecation(24568, 'use the method .hex instead') + return self.hex() + def __copy__(self): """ Return copy of ``self`` - since ``self`` is immutable, we just return From 41cddc057c50f8dbf1949c92f39d2bf7a524c1cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 15 Feb 2018 14:12:42 +0100 Subject: [PATCH 734/740] adding some # py2 tags in the documentation --- src/doc/de/tutorial/programming.rst | 2 +- src/doc/en/prep/Programming.rst | 3 ++- .../tutorial-programming-python.rst | 12 ++++++------ src/doc/en/tutorial/programming.rst | 2 +- src/doc/fr/tutorial/programming.rst | 2 +- src/doc/ja/tutorial/programming.rst | 2 +- src/doc/pt/tutorial/programming.rst | 2 +- src/doc/ru/tutorial/programming.rst | 2 +- 8 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/doc/de/tutorial/programming.rst b/src/doc/de/tutorial/programming.rst index 51c31d4b9c4..e9aeb746e84 100644 --- a/src/doc/de/tutorial/programming.rst +++ b/src/doc/de/tutorial/programming.rst @@ -328,7 +328,7 @@ Sage-Integers): :: - sage: range(1, 15) + sage: range(1, 15) # py2 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] Dies ist nützlich wenn man List-Comprehensions verwendet um Listen zu diff --git a/src/doc/en/prep/Programming.rst b/src/doc/en/prep/Programming.rst index cafca74b2d5..22a613a081b 100644 --- a/src/doc/en/prep/Programming.rst +++ b/src/doc/en/prep/Programming.rst @@ -303,8 +303,9 @@ Below, we show that one can get step sizes other than one as well. :: - sage: range(3, 23, 2); [3,5..21] + sage: range(3, 23, 2) # py2 [3, 5, 7, 9, 11, 13, 15, 17, 19, 21] + sage: [3,5..21] [3, 5, 7, 9, 11, 13, 15, 17, 19, 21] .. note:: diff --git a/src/doc/en/thematic_tutorials/tutorial-programming-python.rst b/src/doc/en/thematic_tutorials/tutorial-programming-python.rst index f8faf5a5c4e..81a2c0e905a 100644 --- a/src/doc/en/thematic_tutorials/tutorial-programming-python.rst +++ b/src/doc/en/thematic_tutorials/tutorial-programming-python.rst @@ -274,7 +274,7 @@ Creating Lists III: list comprehensions **Example** We already know how to create the list `[1, 2, \dots, 16]`:: - sage: range(1,17) + sage: range(1,17) # py2 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] Using a *list comprehension*, we can now create the list @@ -535,7 +535,7 @@ return a sublist of ``L``. **Exercise:** Below are some examples of slicing lists. Try to guess what the output will be before evaluating the cell:: - sage: L = range(20) + sage: L = list(range(20)) sage: L [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] @@ -1011,22 +1011,22 @@ included: :: - sage: range(4) + sage: range(4) # py2 [0, 1, 2, 3] :: - sage: range(1, 5) + sage: range(1, 5) # py2 [1, 2, 3, 4] :: - sage: range(1, 11, 2) + sage: range(1, 11, 2) # py2 [1, 3, 5, 7, 9] :: - sage: range(10, 0, -1) + sage: range(10, 0, -1) # py2 [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] :: diff --git a/src/doc/en/tutorial/programming.rst b/src/doc/en/tutorial/programming.rst index febe20200f1..4d821f636dd 100644 --- a/src/doc/en/tutorial/programming.rst +++ b/src/doc/en/tutorial/programming.rst @@ -313,7 +313,7 @@ Integers): :: - sage: range(1, 15) + sage: range(1, 15) # py2 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] This is useful when using list comprehensions to construct lists: diff --git a/src/doc/fr/tutorial/programming.rst b/src/doc/fr/tutorial/programming.rst index f58b0f60213..4eaa3a9cf64 100644 --- a/src/doc/fr/tutorial/programming.rst +++ b/src/doc/fr/tutorial/programming.rst @@ -325,7 +325,7 @@ Sage) : :: - sage: range(1, 15) + sage: range(1, 15) # py2 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] Cela est utile pour construire des listes par compréhension : diff --git a/src/doc/ja/tutorial/programming.rst b/src/doc/ja/tutorial/programming.rst index f390e5744d9..83eb61f2efa 100644 --- a/src/doc/ja/tutorial/programming.rst +++ b/src/doc/ja/tutorial/programming.rst @@ -289,7 +289,7 @@ SageのIntegerクラスが使えるのは言うまでもない(Rationalクラス :: - sage: range(1, 15) + sage: range(1, 15) # py2 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] この ``range`` が便利なのは,リスト内包表記を使ってリストを生成する場合だ: diff --git a/src/doc/pt/tutorial/programming.rst b/src/doc/pt/tutorial/programming.rst index 82ba2c538f8..9dd806e1d9e 100644 --- a/src/doc/pt/tutorial/programming.rst +++ b/src/doc/pt/tutorial/programming.rst @@ -341,7 +341,7 @@ Sage): :: - sage: range(1, 15) + sage: range(1, 15) # py2 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] Isso é útil quando se usa "list comprehensions" para construir listas: diff --git a/src/doc/ru/tutorial/programming.rst b/src/doc/ru/tutorial/programming.rst index 72ec9030bfb..8580a3ca913 100644 --- a/src/doc/ru/tutorial/programming.rst +++ b/src/doc/ru/tutorial/programming.rst @@ -302,7 +302,7 @@ Python, сработает нормально. :: - sage: range(1, 15) + sage: range(1, 15) # py2 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] Это удобно, когда для создания списков используется вид списка: From 739b625953b9c07d57d603ab50f93649a46655a8 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 15 Feb 2018 15:12:07 +0100 Subject: [PATCH 735/740] Rename _coerce_c_impl -> _convert --- src/sage/rings/polynomial/pbori.pxd | 2 ++ src/sage/rings/polynomial/pbori.pyx | 12 +++--------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/polynomial/pbori.pxd b/src/sage/rings/polynomial/pbori.pxd index 95b1b6189f7..6a5a1adbcb3 100644 --- a/src/sage/rings/polynomial/pbori.pxd +++ b/src/sage/rings/polynomial/pbori.pxd @@ -18,6 +18,8 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): # it is very important to keep this cached, since otherwise the magma interface will break cdef public object __cover_ring + cdef _convert(self, rhs) + cdef class BooleanPolynomial(MPolynomial): cdef PBPoly _pbpoly cpdef _add_(self, other) diff --git a/src/sage/rings/polynomial/pbori.pyx b/src/sage/rings/polynomial/pbori.pyx index 89bf06a18d8..1c3550470f1 100644 --- a/src/sage/rings/polynomial/pbori.pyx +++ b/src/sage/rings/polynomial/pbori.pyx @@ -674,16 +674,11 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): return False return self._base.has_coerce_map_from(S.base()) - cdef _coerce_c_impl(self, rhs): + cdef _convert(self, other): r""" Canonical conversion of elements from other domains to this boolean polynomial ring. - NOTE: - - Inspite of its name, we use this method for conversion, - not coercion. - EXAMPLES: Convert elements of ``self``. @@ -806,7 +801,6 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): cdef BooleanPolynomial p # we check for other PolyBoRi types first since this conversion # is used by the PolyBoRi python code often - other = rhs if isinstance(other, BooleSet): other = new_BP_from_PBSet(other.ring(), (other)._pbset) @@ -937,7 +931,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): cdef int i try: - return self._coerce_c_impl(other) + return self._convert(other) except TypeError: pass @@ -1008,7 +1002,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): i = int(other) except Exception: try: # last chance: try Sage's conversions over GF(2), Trac #13284 - return self._coerce_c_impl(self.cover_ring()(other)) + return self._convert(self.cover_ring()(other)) except Exception: raise TypeError("cannot convert %s to BooleanPolynomial" % (type(other))) From 154e1118092b682ed9462f1f5aabc28225261ac4 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 15 Feb 2018 15:28:45 +0100 Subject: [PATCH 736/740] Open lazy_import cache file in binary mode --- src/sage/misc/lazy_import.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/misc/lazy_import.pyx b/src/sage/misc/lazy_import.pyx index bb2d670f678..7574696e1da 100644 --- a/src/sage/misc/lazy_import.pyx +++ b/src/sage/misc/lazy_import.pyx @@ -1110,7 +1110,7 @@ def save_cache_file(): cache_dir = os.path.dirname(cache_file) sage_makedirs(cache_dir) - with atomic_write(cache_file) as f: + with atomic_write(cache_file, binary=True) as f: pickle.dump(star_imports, f) def get_star_imports(module_name): @@ -1145,7 +1145,7 @@ def get_star_imports(module_name): if star_imports is None: star_imports = {} try: - with open(get_cache_file()) as cache_file: + with open(get_cache_file(), "rb") as cache_file: star_imports = pickle.load(cache_file) except IOError: # file does not exist pass From f292c1c7aa0efaed9f8268a416b1d8ac77cebc5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 15 Feb 2018 18:20:51 +0100 Subject: [PATCH 737/740] more use of https for wikipedia links --- src/sage/categories/category_singleton.pyx | 2 +- src/sage/databases/oeis.py | 4 ++-- src/sage/interacts/library.py | 2 +- src/sage/interacts/test_jupyter.rst | 2 +- src/sage/numerical/mip.pyx | 14 ++++++------ src/sage/numerical/sdp.pyx | 4 ++-- src/sage/plot/plot3d/parametric_plot3d.py | 25 +++++++++++----------- 7 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/sage/categories/category_singleton.pyx b/src/sage/categories/category_singleton.pyx index 51b0e197147..6ba06632c7c 100644 --- a/src/sage/categories/category_singleton.pyx +++ b/src/sage/categories/category_singleton.pyx @@ -89,7 +89,7 @@ class Category_singleton(Category): A *singleton* category is a category whose class takes no parameters like ``Fields()`` or ``Rings()``. See also the - `Singleton design pattern `_. + `Singleton design pattern `_. This is a subclass of :class:`Category`, with a couple optimizations for singleton categories. diff --git a/src/sage/databases/oeis.py b/src/sage/databases/oeis.py index 9b3fefc7db7..63edd421363 100644 --- a/src/sage/databases/oeis.py +++ b/src/sage/databases/oeis.py @@ -554,7 +554,7 @@ def _imaginary_entry(self, keywords=''): '%D A999999 Lewis Carroll, Alice\'s Adventures in Wonderland.\n' '%D A999999 Lewis Carroll, The Hunting of the Snark.\n' '%D A999999 Deep Thought, The Answer to the Ultimate Question of Life, The Universe, and Everything.\n' - '%H A999999 Wikipedia, 42 (number)\n' + '%H A999999 Wikipedia, 42 (number)\n' '%H A999999 See. also trac ticket #42\n' '%H A999999 Do not confuse with the sequence A000042 or the sequence A000024\n' '%H A999999 The string http://42.com is not a link.\n' @@ -1465,7 +1465,7 @@ def links(self, browse=None, format='guess'): 'http://oeis.org/A000024' sage: HTML = s.links(format="html"); HTML - 0: Wikipedia, 42 (number) + 0: Wikipedia, 42 (number) 1: See. also trac ticket #42 ... sage: type(HTML) diff --git a/src/sage/interacts/library.py b/src/sage/interacts/library.py index 8ab52592718..35ba7ea10f6 100644 --- a/src/sage/interacts/library.py +++ b/src/sage/interacts/library.py @@ -290,7 +290,7 @@ def difference_quotient( """ html('

Difference Quotient

') html('' ) diff --git a/src/sage/interacts/test_jupyter.rst b/src/sage/interacts/test_jupyter.rst index 36f8abeddc6..c4cdd37eaf0 100644 --- a/src/sage/interacts/test_jupyter.rst +++ b/src/sage/interacts/test_jupyter.rst @@ -86,7 +86,7 @@ Test all interacts from the Sage interact library:: a: IntSlider(value=5, min=0, max=10, step=1, description=u'$a$') x0: IntSlider(value=2, min=0, max=10, step=1, description=u'$x_0$ (start point)')

Difference Quotient

- +

Difference Quotient



diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 6dd988f6a1b..e9613906c6d 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -2,9 +2,9 @@ r""" Mixed Integer Linear Programming This module implements classes and methods for the efficient solving of Linear -Programs (`LP `_) and Mixed +Programs (`LP `_) and Mixed Integer Linear Programs (`MILP -`_). +`_). *Do you want to understand how the simplex method works?* See the :mod:`~sage.numerical.interactive_simplex_method` module (educational purposes @@ -13,8 +13,8 @@ only) Definition ---------- -A linear program (`LP `_) -is an `optimization problem `_ +A linear program (`LP `_) +is an `optimization problem `_ in the following form .. MATH:: @@ -24,7 +24,7 @@ with given `A \in \mathbb{R}^{m,n}`, `b \in \mathbb{R}^m`, `c \in \mathbb{R}^n` and unknown `x \in \mathbb{R}^{n}`. If some or all variables in the vector `x` are restricted over the integers `\mathbb{Z}`, the problem is called mixed integer -linear program (`MILP `_). +linear program (`MILP `_). A wide variety of problems in optimization can be formulated in this standard form. Then, solvers are able to calculate a solution. @@ -1393,8 +1393,8 @@ cdef class MixedIntegerLinearProgram(SageObject): Writing problem data to ... 17 records were written - For information about the MPS file format : - http://en.wikipedia.org/wiki/MPS_%28format%29 + For information about the MPS file format, see + :wikipedia:`MPS_(format)` """ self._backend.write_mps(filename, modern) diff --git a/src/sage/numerical/sdp.pyx b/src/sage/numerical/sdp.pyx index 30b40d3081a..85ae351799e 100644 --- a/src/sage/numerical/sdp.pyx +++ b/src/sage/numerical/sdp.pyx @@ -1,8 +1,8 @@ r""" SemiDefinite Programming -A semidefinite program (`SDP `_) -is an `optimization problem `_ +A semidefinite program (`SDP `_) +is an `optimization problem `_ of the following form .. MATH:: diff --git a/src/sage/plot/plot3d/parametric_plot3d.py b/src/sage/plot/plot3d/parametric_plot3d.py index 04875aed3ec..835f8f8f8cb 100644 --- a/src/sage/plot/plot3d/parametric_plot3d.py +++ b/src/sage/plot/plot3d/parametric_plot3d.py @@ -380,7 +380,7 @@ def g(x,y): return x, y+sin(y), x**2 + y**2 f_z = v sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-1,1), frame=False, color="red")) - A Trefoil knot https://en.wikipedia.org/wiki/Trefoil_knot:: + A Trefoil knot (:wikipedia:`Trefoil_knot`):: sage: u, v = var('u,v') sage: f_x = (4*(1+0.25*sin(3*v))+cos(u))*cos(2*v) @@ -414,7 +414,7 @@ def g(x,y): return x, y+sin(y), x**2 + y**2 f_z = cos(u) / (1 + sqrt(2)) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-pi,pi), frame=False, color="green")) - Boy's surface http://en.wikipedia.org/wiki/Boy's_surface and http://mathcurve.com/surfaces/boy/boy.shtml:: + Boy's surface (:wikipedia:`Boy's_surface` and https://mathcurve.com/surfaces/boy/boy.shtml):: sage: u, v = var('u,v') sage: K = cos(u) / (sqrt(2) - cos(2*u)*sin(3*v)) @@ -436,7 +436,7 @@ def g(x,y): return x, y+sin(y), x**2 + y**2 plot_points=[90,90], frame=False, color="orange") # long time -- about 30 seconds sphinx_plot(P) - Maeder's Owl also known as Bour's minimal surface https://en.wikipedia.org/wiki/Bour%27s_minimal_surface:: + Maeder's Owl also known as Bour's minimal surface (:wikipedia:`Bour%27s_minimal_surface`):: sage: u, v = var('u,v') sage: f_x = v*cos(u) - 0.5*v^2*cos(2*u) @@ -527,8 +527,7 @@ def g(x,y): return x, y+sin(y), x**2 + y**2 f_z = sin(v) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="green")) - Yellow Whitney's umbrella - http://en.wikipedia.org/wiki/Whitney_umbrella:: + Yellow Whitney's umbrella (:wikipedia:`Whitney_umbrella`):: sage: u, v = var('u,v') sage: f_x = u*v @@ -545,7 +544,7 @@ def g(x,y): return x, y+sin(y), x**2 + y**2 f_z = v**2 sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-1,1), (v,-1,1), frame=False, color="yellow")) - Cross cap http://en.wikipedia.org/wiki/Cross-cap:: + Cross cap (:wikipedia:`Cross-cap`):: sage: u, v = var('u,v') sage: f_x = (1+cos(v)) * cos(u) @@ -597,8 +596,8 @@ def g(x,y): return x, y+sin(y), x**2 + y**2 sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,4*pi), (v,0,2*pi), frame=False, color="red", opacity=0.7)) Steiner surface/Roman's surface (see - http://en.wikipedia.org/wiki/Roman_surface and - http://en.wikipedia.org/wiki/Steiner_surface):: + :wikipedia:`Roman_surface` and + :wikipedia:`Steiner_surface`):: sage: u, v = var('u,v') sage: f_x = (sin(2*u) * cos(v) * cos(v)) @@ -615,7 +614,7 @@ def g(x,y): return x, y+sin(y), x**2 + y**2 f_z = (cos(u) * sin(2*v)) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi/2,pi/2), (v,-pi/2,pi/2), frame=False, color="red")) - Klein bottle? (see http://en.wikipedia.org/wiki/Klein_bottle):: + Klein bottle? (see :wikipedia:`Klein_bottle`):: sage: u, v = var('u,v') sage: f_x = (3*(1+sin(v)) + 2*(1-cos(v)/2)*cos(u)) * cos(v) @@ -633,7 +632,7 @@ def g(x,y): return x, y+sin(y), x**2 + y**2 sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="green")) A Figure 8 embedding of the Klein bottle (see - http://en.wikipedia.org/wiki/Klein_bottle):: + :wikipedia:`Klein_bottle`):: sage: u, v = var('u,v') sage: f_x = (2+cos(v/2)*sin(u)-sin(v/2)*sin(2*u)) * cos(v) @@ -651,7 +650,7 @@ def g(x,y): return x, y+sin(y), x**2 + y**2 sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="red")) Enneper's surface (see - http://en.wikipedia.org/wiki/Enneper_surface):: + :wikipedia:`Enneper_surface`):: sage: u, v = var('u,v') sage: f_x = u - u^3/3 + u*v^2 @@ -861,7 +860,7 @@ def g(x,y): return x, y+sin(y), x**2 + y**2 sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-pi,pi), plot_points=[50,50], frame=False, color="red")) A Helicoid (lines through a helix, - http://en.wikipedia.org/wiki/Helix):: + :wikipedia:`Helix`):: sage: u, v = var('u,v') sage: f_x = sinh(v) * sin(u) @@ -932,7 +931,7 @@ def g(x,y): return x, y+sin(y), x**2 + y**2 sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-25,25), (v,-25,25), plot_points=[50,50], frame=False, color="green")) The breather surface - (http://en.wikipedia.org/wiki/Breather_surface):: + (:wikipedia:`Breather_surface`):: sage: K = sqrt(0.84) sage: G = (0.4*((K*cosh(0.4*u))^2 + (0.4*sin(K*v))^2)) From 59543036db06279477eb31510db72d2abfb18960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 16 Feb 2018 08:32:36 +0100 Subject: [PATCH 738/740] more wikipedia links --- src/sage/numerical/mip.pyx | 12 ++++++------ src/sage/numerical/sdp.pyx | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index e9613906c6d..dd5e4235ca9 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -2,9 +2,9 @@ r""" Mixed Integer Linear Programming This module implements classes and methods for the efficient solving of Linear -Programs (`LP `_) and Mixed -Integer Linear Programs (`MILP -`_). +Programs (:wikipedia:`LP `) and Mixed +Integer Linear Programs (:wikipedia:`MILP +`). *Do you want to understand how the simplex method works?* See the :mod:`~sage.numerical.interactive_simplex_method` module (educational purposes @@ -13,8 +13,8 @@ only) Definition ---------- -A linear program (`LP `_) -is an `optimization problem `_ +A linear program (:wikipedia:`LP `) +is an optimization problem (:wikipedia:`Optimization_(mathematics)`) in the following form .. MATH:: @@ -24,7 +24,7 @@ with given `A \in \mathbb{R}^{m,n}`, `b \in \mathbb{R}^m`, `c \in \mathbb{R}^n` and unknown `x \in \mathbb{R}^{n}`. If some or all variables in the vector `x` are restricted over the integers `\mathbb{Z}`, the problem is called mixed integer -linear program (`MILP `_). +linear program (:wikipedia:`MILP `). A wide variety of problems in optimization can be formulated in this standard form. Then, solvers are able to calculate a solution. diff --git a/src/sage/numerical/sdp.pyx b/src/sage/numerical/sdp.pyx index 85ae351799e..17733a301d7 100644 --- a/src/sage/numerical/sdp.pyx +++ b/src/sage/numerical/sdp.pyx @@ -1,8 +1,8 @@ r""" SemiDefinite Programming -A semidefinite program (`SDP `_) -is an `optimization problem `_ +A semidefinite program (:wikipedia:`SDP `) +is an optimization problem (:wikipedia:`Optimization_(mathematics)>`) of the following form .. MATH:: From a42d5893a26bab529152311478a8c081e1612f49 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Fri, 16 Feb 2018 15:26:10 +0100 Subject: [PATCH 739/740] 24745: pynac patch: fix memleaks in in-place PyObject numerics --- build/pkgs/pynac/package-version.txt | 2 +- build/pkgs/pynac/patches/memleak.patch | 68 ++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/pynac/patches/memleak.patch diff --git a/build/pkgs/pynac/package-version.txt b/build/pkgs/pynac/package-version.txt index bf7b715d275..1da1a62642d 100644 --- a/build/pkgs/pynac/package-version.txt +++ b/build/pkgs/pynac/package-version.txt @@ -1 +1 @@ -0.7.16 +0.7.16.p0 diff --git a/build/pkgs/pynac/patches/memleak.patch b/build/pkgs/pynac/patches/memleak.patch new file mode 100644 index 00000000000..a02ce84c658 --- /dev/null +++ b/build/pkgs/pynac/patches/memleak.patch @@ -0,0 +1,68 @@ +diff --git a/ginac/numeric.cpp b/ginac/numeric.cpp +index c92dffa..0e1352c 100644 +--- a/ginac/numeric.cpp ++++ b/ginac/numeric.cpp +@@ -1969,7 +1969,6 @@ numeric & operator+=(numeric & lh, const numeric & rh) + } + lh.hash = (long)PyObject_Hash(lh.v._pyobject); + Py_DECREF(p); +- Py_INCREF(lh.v._pyobject); + return lh; + } + default: +@@ -2049,7 +2048,6 @@ numeric & operator-=(numeric & lh, const numeric & rh) + } + lh.hash = (long)PyObject_Hash(lh.v._pyobject); + Py_DECREF(p); +- Py_INCREF(lh.v._pyobject); + return lh; + } + default: +@@ -2131,7 +2129,6 @@ numeric & operator*=(numeric & lh, const numeric & rh) + } + lh.hash = (long)PyObject_Hash(lh.v._pyobject); + Py_DECREF(p); +- Py_INCREF(lh.v._pyobject); + return lh; + } + default: +@@ -2265,7 +2262,6 @@ numeric & operator/=(numeric & lh, const numeric & rh) + } + lh.hash = (long)PyObject_Hash(lh.v._pyobject); + Py_DECREF(p); +- Py_INCREF(lh.v._pyobject); + return lh; + } else if (PyLong_Check(rh.v._pyobject)) { + PyObject* d = py_funcs.py_integer_from_python_obj(rh.v._pyobject); +@@ -2277,7 +2273,6 @@ numeric & operator/=(numeric & lh, const numeric & rh) + lh.hash = (long)PyObject_Hash(lh.v._pyobject); + Py_DECREF(d); + Py_DECREF(p); +- Py_INCREF(lh.v._pyobject); + return lh; + } + } else if (PyLong_Check(p)) { +@@ -2290,7 +2285,6 @@ numeric & operator/=(numeric & lh, const numeric & rh) + lh.hash = (long)PyObject_Hash(lh.v._pyobject); + Py_DECREF(n); + Py_DECREF(p); +- Py_INCREF(lh.v._pyobject); + return lh; + } + } +@@ -2306,7 +2300,6 @@ numeric & operator/=(numeric & lh, const numeric & rh) + lh.hash = (long)PyObject_Hash(lh.v._pyobject); + Py_DECREF(n); + Py_DECREF(p); +- Py_INCREF(lh.v._pyobject); + return lh; + } + } +@@ -2318,7 +2311,6 @@ numeric & operator/=(numeric & lh, const numeric & rh) + } + lh.hash = (long)PyObject_Hash(lh.v._pyobject); + Py_DECREF(p); +- Py_INCREF(lh.v._pyobject); + return lh; + } + default: From 93020a4eb1213bfe866aba47ccaa60f8ee06a1d7 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sun, 18 Feb 2018 20:43:34 +0100 Subject: [PATCH 740/740] Updated SageMath version to 8.2.beta6 --- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- src/bin/sage-banner | 2 +- src/bin/sage-version.sh | 4 ++-- src/sage/version.py | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index 3ad408445a9..ed9a5636623 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 8.2.beta5, Release Date: 2018-02-09 +SageMath version 8.2.beta6, Release Date: 2018-02-18 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index cd2c5db52ae..49c1fbab972 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=ade416eb97804aa5dbc97afe9e55f63de0986270 -md5=a8b02b91f6650ecaf26ef7b2287a5e7f -cksum=334528818 +sha1=5c564595c8dab9d93e86dcc43141b956086d9e16 +md5=9125aedb54d788ec1acb8f3112c5a04e +cksum=251192083 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 63fe24a5cdd..f1aaa90592d 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -253 +254 diff --git a/src/bin/sage-banner b/src/bin/sage-banner index 720359c08dd..b49c16f2c8c 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ SageMath version 8.2.beta5, Release Date: 2018-02-09 │ +│ SageMath version 8.2.beta6, Release Date: 2018-02-18 │ │ Type "notebook()" for the browser-based notebook interface. │ │ Type "help()" for help. │ └────────────────────────────────────────────────────────────────────┘ diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index b892de5bdcc..8fb8ca30f3b 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,4 +1,4 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='8.2.beta5' -SAGE_RELEASE_DATE='2018-02-09' +SAGE_VERSION='8.2.beta6' +SAGE_RELEASE_DATE='2018-02-18' diff --git a/src/sage/version.py b/src/sage/version.py index c018102e652..9af21e10e7a 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,4 +1,4 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '8.2.beta5' -date = '2018-02-09' +version = '8.2.beta6' +date = '2018-02-18'