def global_height(self, prec=None): r""" Returns the logarithmic height of the point. INPUT: - ``prec`` -- desired floating point precision (default: default RealField precision). OUTPUT: - a real number EXAMPLES:: sage: P.<x,y>=AffineSpace(QQ,2) sage: Q=P(41,1/12) sage: Q.global_height() 3.71357206670431 :: sage: P=AffineSpace(ZZ,4,'x') sage: Q=P(3,17,-51,5) sage: Q.global_height() 3.93182563272433 :: sage: R.<x>=PolynomialRing(QQ) sage: k.<w>=NumberField(x^2+5) sage: A=AffineSpace(k,2,'z') sage: A([3,5*w+1]).global_height(prec=100) 2.4181409534757389986565376694 .. TODO:: p-adic heights add heights to integer.pyx and remove special case """ if self.domain().base_ring() == ZZ: if prec is None: R = RealField() else: R = RealField(prec) H = max([ self[i].abs() for i in range( self.codomain().ambient_space().dimension_relative()) ]) return (R(max(H, 1)).log()) if self.domain().base_ring() in _NumberFields or is_NumberFieldOrder( self.domain().base_ring()): return (max([ self[i].global_height(prec) for i in range( self.codomain().ambient_space().dimension_relative()) ])) else: raise NotImplementedError( "Must be over a Numberfield or a Numberfield Order")
def _compute_power_series(self, n, abs_prec, cache_ring=None): """ Compute the power series giving Dickman's function on [n, n+1], by recursion in n. For internal use; self.power_series() is a wrapper around this intended for the user. INPUT: - ``n`` - the lower endpoint of the interval for which this power series holds - ``abs_prec`` - the absolute precision of the resulting power series - ``cache_ring`` - for internal use, caches the power series at this precision. EXAMPLES:: sage: f = dickman_rho.power_series(2, 20); f -9.9376e-8*x^11 + 3.7722e-7*x^10 - 1.4684e-6*x^9 + 5.8783e-6*x^8 - 0.000024259*x^7 + 0.00010341*x^6 - 0.00045583*x^5 + 0.0020773*x^4 - 0.0097336*x^3 + 0.045224*x^2 - 0.11891*x + 0.13032 """ if n <= 1: if n <= -1: return PolynomialRealDense(RealField(abs_prec)['x']) if n == 0: return PolynomialRealDense(RealField(abs_prec)['x'], [1]) elif n == 1: nterms = (RDF(abs_prec) * RDF(2).log() / RDF(3).log()).ceil() R = RealField(abs_prec) neg_three = ZZ(-3) coeffs = [1 - R(1.5).log() ] + [neg_three**-k / k for k in range(1, nterms)] f = PolynomialRealDense(R['x'], coeffs) if cache_ring is not None: self._f[n] = f.truncate_abs(f[0] >> ( cache_ring.prec() + 1)).change_ring(cache_ring) return f else: f = self._compute_power_series(n - 1, abs_prec, cache_ring) # integrand = f / (2n+1 + x) # We calculate this way because the most significant term is the constant term, # and so we want to push the error accumulation and remainder out to the least # significant terms. integrand = f.reverse().quo_rem( PolynomialRealDense(f.parent(), [1, 2 * n + 1]))[0].reverse() integrand = integrand.truncate_abs(RR(2)**-abs_prec) iintegrand = integrand.integral() ff = PolynomialRealDense(f.parent(), [f(1) + iintegrand(-1)]) - iintegrand i = 0 while abs(f[i]) < abs(f[i + 1]): i += 1 rel_prec = int(abs_prec + abs(RR(f[i])).log2()) if cache_ring is not None: self._f[n] = ff.truncate_abs( ff[0] >> (cache_ring.prec() + 1)).change_ring(cache_ring) return ff.change_ring(RealField(rel_prec))
def global_height(self, prec=None): r""" Returns the maximum of the heights of the coefficients in any of the coordinate functions of ``self``. INPUT: - ``prec`` -- desired floating point precision (default: default RealField precision). OUTPUT: - a real number EXAMPLES:: sage: A.<x>=AffineSpace(QQ,1) sage: H=Hom(A,A) sage: f=H([1/1331*x^2+4000]); sage: f.global_height() 8.29404964010203 :: sage: R.<x>=PolynomialRing(QQ) sage: k.<w>=NumberField(x^2+5) sage: A.<x,y>=AffineSpace(k,2) sage: H=Hom(A,A) sage: f=H([13*w*x^2+4*y, 1/w*y^2]); sage: f.global_height(prec=100) 3.3696683136785869233538671082 .. TODO:: add heights to integer.pyx and remove special case """ if self.domain().base_ring() == ZZ: if prec is None: R = RealField() else: R = RealField(prec) H = R(0) for i in range(self.domain().ambient_space().dimension_relative()): C = self[i].coefficients() h = max([c.abs() for c in C]) H = max(H, R(h).log()) return (H) H = 0 for i in range(self.domain().ambient_space().dimension_relative()): C = self[i].coefficients() if C == []: #to deal with the case self[i]=0 h = 0 else: h = max([c.global_height(prec) for c in C]) H = max(H, h) return (H)
def global_height(self, prec=None): r""" Returns the logarithmic height of the points. Must be over `\ZZ` or `\QQ`. INPUT: - ``prec`` -- desired floating point precision (default: default RealField precision). OUTPUT: - a real number EXAMPLES:: sage: P.<x,y,z>=ProjectiveSpace(QQ,2) sage: Q=P.point([4,4,1/30]) sage: Q.global_height() 4.78749174278205 :: sage: P.<x,y,z>=ProjectiveSpace(ZZ,2) sage: Q=P([4,1,30]) sage: Q.global_height() 3.40119738166216 :: sage: R.<x>=PolynomialRing(QQ) sage: k.<w>=NumberField(x^2+5) sage: A=ProjectiveSpace(k,2,'z') sage: A([3,5*w+1,1]).global_height(prec=100) 2.4181409534757389986565376694 .. TODO:: p-adic heights add heights to integer.pyx and remove special case """ if self.domain().base_ring() == ZZ: if prec is None: R = RealField() else: R = RealField(prec) H=R(0) return(R(max([self[i].abs() for i in range(self.codomain().ambient_space().dimension_relative()+1)])).log()) if self.domain().base_ring() in _NumberFields or is_NumberFieldOrder(self.domain().base_ring()): return(max([self[i].global_height(prec) for i in range(self.codomain().ambient_space().dimension_relative()+1)])) else: raise NotImplementedError("Must be over a Numberfield or a Numberfield Order")
def numeric_converter(value, cur=None): """ Used for converting numeric values from Postgres to Python. INPUT: - ``value`` -- a string representing a decimal number. - ``cur`` -- a cursor, unused OUTPUT: - either a sage integer (if there is no decimal point) or a real number whose precision depends on the number of digits in value. """ if value is None: return None if "." in value: if SAGE_MODE: # The following is a good guess for the bit-precision, # but we use LmfdbRealLiterals to ensure that our number # prints the same as we got it. prec = ceil(len(value) * 3.322) return LmfdbRealLiteral(RealField(prec), value) else: # Sage isn't installed, so we fall back on Python floats return float(value) else: return Integer(value)
def _test_fun_approx(pol, ref, disk_rad=None, interval_rad=None, prec=53, test_count=100): r""" EXAMPLES:: sage: from ore_algebra.analytic.polynomial_approximation import _test_fun_approx sage: _test_fun_approx(lambda x: x.exp(), lambda x: x.exp() + x/1000, ....: interval_rad=1) Traceback (most recent call last): ... AssertionError: z = ..., ref(z) = ... not in pol(z) = ... """ from sage.rings.real_mpfr import RealField from sage.rings.real_arb import RealBallField from sage.rings.complex_arb import ComplexBallField my_RR = RealField(prec) my_RBF = RealBallField(prec) my_CBF = ComplexBallField(prec) if bool(disk_rad) == bool(interval_rad): raise ValueError rad = disk_rad or interval_rad for _ in range(test_count): rho = my_RBF(my_RR.random_element(-rad, rad)) if disk_rad: exp_i_theta = my_CBF(my_RR.random_element(0, 1)).exppii() z = rho*exp_i_theta elif interval_rad: z = rho ref_z = ref(z) pol_z = pol(z) if not ref_z.overlaps(pol_z): fmt = "z = {}, ref(z) = {} not in pol(z) = {}" raise AssertionError(fmt.format(z, ref_z, pol_z))
def __init__(self, v, omega, mu=0, prec=None): r""" EXAMPLES:: sage: from slabbe import DiscreteHyperplane sage: p = DiscreteHyperplane([1,pi,7], 1+pi+7, mu=0) sage: p Set of points x in ZZ^3 satisfying: 0 <= (1, pi, 7) . x + 0 < pi + 8 :: sage: p = DiscreteHyperplane([1,pi,7], 1+pi+7, mu=20) sage: vector((0,0,0)) in p False sage: p = DiscreteHyperplane([1,pi,7], 1+pi+7, mu=0) sage: vector((0,0,0)) in p True """ if prec is None: self._v = vector(v) self._omega = omega self._mu = mu else: RF = RealField(prec=prec) self._v = vector(RF, v) self._omega = RF(omega) self._mu = RF(mu) def contain(p): #print "est-ce proche : ", self._v.dot_product(p) + self._mu return 0 <= self._v.dot_product(p) + self._mu < self._omega DiscreteSubset.__init__(self, dimension=len(self._v), predicate=contain)
def column_Log(SUK, iota, U, prec=106): r""" Return the log vector of ``iota``; i.e., the logs of all the valuations INPUT: - ``SUK`` -- a group of `S`-units - ``iota`` -- an element of ``K`` - ``U`` -- a list of places (finite or infinite) of ``K`` - ``prec`` -- (default: 106) the precision of the real field OUTPUT: The log vector as a list of real numbers EXAMPLES:: sage: from sage.rings.number_field.S_unit_solver import column_Log sage: K.<xi> = NumberField(x^3-3) sage: S = tuple(K.primes_above(3)) sage: SUK = UnitGroup(K, S=S) sage: phi_complex = K.places()[1] sage: v_fin = S[0] sage: U = [phi_complex, v_fin] sage: column_Log(SUK, xi^2, U) # abs tol 1e-29 [1.464816384890812968648768625966, -2.197224577336219382790490473845] REFERENCES: - [Sma1995]_ p. 823 """ R = RealField(prec) return [ R(SUK.number_field().abs_val(v, iota, prec)).log() for v in U]
def corrected_voronoi_diagram(points): r""" Compute a Voronoi diagram of a set of points with rational coordinates, such that the given points are granted to lie one in each bounded region. INPUT: - ``points`` -- a list of complex numbers OUTPUT: A VoronoiDiagram constructed from rational approximations of the points, with the guarantee that each bounded region contains exactly one of the input points. EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import corrected_voronoi_diagram sage: points = (2, I, 0.000001, 0, 0.000001*I) sage: V = corrected_voronoi_diagram(points) sage: V The Voronoi diagram of 9 points of dimension 2 in the Rational Field sage: V.regions() {P(-7, 0): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices and 2 rays, P(0, -7): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices and 2 rays, P(0, 0): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices, P(0, 1): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 5 vertices, P(0, 1/1000000): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices, P(0, 7): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 2 rays, P(1/1000000, 0): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 5 vertices, P(2, 0): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 5 vertices, P(7, 0): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 2 vertices and 2 rays} """ prec = 53 point_coordinates = [(p.real(), p.imag()) for p in points] while True: RF = RealField(prec) apprpoints = {(QQ(RF(p[0])), QQ(RF(p[1]))): p for p in point_coordinates} added_points = 3 * max(map(abs, flatten(apprpoints))) + 1 configuration = list(apprpoints.keys()) + [(added_points, 0), (-added_points, 0), (0, added_points), (0, -added_points)] V = VoronoiDiagram(configuration) valid = True for r in V.regions().items(): if not r[1].rays() and not r[1].interior_contains( apprpoints[r[0].affine()]): prec += 53 valid = False break if valid: break return V
def extract(cls, obj): """ Takes an object extracted by the json parser and decodes the special-formating dictionaries used to store special types. """ if isinstance(obj, dict) and 'data' in obj: if len(obj) == 2 and '__ComplexList__' in obj: return [complex(*v) for v in obj['data']] elif len(obj) == 2 and '__QQList__' in obj: return [QQ(tuple(v)) for v in obj['data']] elif len(obj) == 3 and '__NFList__' in obj and 'base' in obj: base = cls.extract(obj['base']) return [cls._extract(base, c) for c in obj['data']] elif len(obj) == 2 and '__IntDict__' in obj: return {Integer(k): cls.extract(v) for k,v in obj['data']} elif len(obj) == 3 and '__Vector__' in obj and 'base' in obj: base = cls.extract(obj['base']) return vector([cls._extract(base, v) for v in obj['data']]) elif len(obj) == 2 and '__Rational__' in obj: return Rational(*obj['data']) elif len(obj) == 3 and '__RealLiteral__' in obj and 'prec' in obj: return LmfdbRealLiteral(RealField(obj['prec']), obj['data']) elif len(obj) == 2 and '__complex__' in obj: return complex(*obj['data']) elif len(obj) == 3 and '__Complex__' in obj and 'prec' in obj: return ComplexNumber(ComplexField(obj['prec']), *obj['data']) elif len(obj) == 3 and '__NFElt__' in obj and 'parent' in obj: return cls._extract(cls.extract(obj['parent']), obj['data']) elif len(obj) == 3 and ('__NFRelative__' in obj or '__NFAbsolute__' in obj) and 'vname' in obj: poly = cls.extract(obj['data']) return NumberField(poly, name=obj['vname']) elif len(obj) == 2 and '__NFCyclotomic__' in obj: return CyclotomicField(obj['data']) elif len(obj) == 2 and '__IntegerRing__' in obj: return ZZ elif len(obj) == 2 and '__RationalField__' in obj: return QQ elif len(obj) == 3 and '__RationalPoly__' in obj and 'vname' in obj: return QQ[obj['vname']]([QQ(tuple(v)) for v in obj['data']]) elif len(obj) == 4 and '__Poly__' in obj and 'vname' in obj and 'base' in obj: base = cls.extract(obj['base']) return base[obj['vname']]([cls._extract(base, c) for c in obj['data']]) elif len(obj) == 5 and '__PowerSeries__' in obj and 'vname' in obj and 'base' in obj and 'prec' in obj: base = cls.extract(obj['base']) prec = infinity if obj['prec'] == 'inf' else int(obj['prec']) return base[[obj['vname']]]([cls._extract(base, c) for c in obj['data']], prec=prec) elif len(obj) == 2 and '__date__' in obj: return datetime.datetime.strptime(obj['data'], "%Y-%m-%d").date() elif len(obj) == 2 and '__time__' in obj: return datetime.datetime.strptime(obj['data'], "%H:%M:%S.%f").time() elif len(obj) == 2 and '__datetime__' in obj: return datetime.datetime.strptime(obj['data'], "%Y-%m-%d %H:%M:%S.%f") return obj
def __init__(self, X, P, codomain=None, check=False): r""" Create the discrete probability space with probabilities on the space X given by the dictionary P with values in the field real_field. EXAMPLES:: sage: S = [ i for i in range(16) ] sage: P = {} sage: for i in range(15): P[i] = 2^(-i-1) sage: P[15] = 2^-16 sage: X = DiscreteProbabilitySpace(S,P) sage: X.domain() (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) sage: X.set() {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} sage: X.entropy().n() 1.99972534179688 A probability space can be defined on any list of elements:: sage: AZ = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' sage: S = [ AZ[i] for i in range(26) ] sage: P = { 'A':1/2, 'B':1/4, 'C':1/4 } sage: X = DiscreteProbabilitySpace(S,P) sage: X Discrete probability space defined by {'A': 1/2, 'C': 1/4, 'B': 1/4} sage: X.entropy().n() 1.50000000000000 """ if codomain is None: codomain = RealField() if not is_RealField(codomain) and not is_RationalField(codomain): raise TypeError( "Argument codomain (= %s) must be the reals or rationals" % codomain) if check: one = sum([P[x] for x in P.keys()]) if is_RationalField(codomain): if not one == 1: raise TypeError( "Argument P (= %s) does not define a probability function" ) else: if not Abs(one - 1) < 2 ^ (-codomain.precision() + 1): raise TypeError( "Argument P (= %s) does not define a probability function" ) ProbabilitySpace_generic.__init__(self, X, codomain) DiscreteRandomVariable.__init__(self, self, P, codomain, check)
def hypergeometric_U(alpha, beta, x, algorithm="pari", prec=53): r""" Default is a wrap of PARI's hyperu(alpha,beta,x) function. Optionally, algorithm = "scipy" can be used. The confluent hypergeometric function `y = U(a,b,x)` is defined to be the solution to Kummer's differential equation .. math:: xy'' + (b-x)y' - ay = 0. This satisfies `U(a,b,x) \sim x^{-a}`, as `x\rightarrow \infty`, and is sometimes denoted ``x^{-a}2_F_0(a,1+a-b,-1/x)``. This is not the same as Kummer's `M`-hypergeometric function, denoted sometimes as ``_1F_1(alpha,beta,x)``, though it satisfies the same DE that `U` does. .. warning:: In the literature, both are called "Kummer confluent hypergeometric" functions. EXAMPLES:: sage: hypergeometric_U(1,1,1,"scipy") 0.596347362323... sage: hypergeometric_U(1,1,1) 0.59634736232319... sage: hypergeometric_U(1,1,1,"pari",70) 0.59634736232319407434... """ if algorithm == "scipy": if prec != 53: raise ValueError( "for the scipy algorithm the precision must be 53") import scipy.special ans = str(scipy.special.hyperu(float(alpha), float(beta), float(x))) ans = ans.replace("(", "") ans = ans.replace(")", "") ans = ans.replace("j", "*I") return sage_eval(ans) elif algorithm == 'pari': from sage.libs.pari.all import pari R = RealField(prec) return R(pari(R(alpha)).hyperu(R(beta), R(x), precision=prec)) else: raise ValueError("unknown algorithm '%s'" % algorithm)
def c3_func(SUK, prec=106): r""" Return the constant `c_3` from Smart's 1995 TCDF paper, [Sma1995]_ INPUT: - ``SUK`` -- a group of `S`-units - ``prec`` -- (default: 106) the precision of the real field OUTPUT: The constant ``c3``, as a real number EXAMPLES:: sage: from sage.rings.number_field.S_unit_solver import c3_func sage: K.<xi> = NumberField(x^3-3) sage: SUK = UnitGroup(K, S=tuple(K.primes_above(3))) sage: c3_func(SUK) # abs tol 1e-29 0.4257859134798034746197327286726 .. NOTE:: The numerator should be as close to 1 as possible, especially as the rank of the `S`-units grows large REFERENCES: - [Sma1995]_ p. 823 """ R = RealField(prec) all_places = list(SUK.primes()) + SUK.number_field().places(prec) Possible_U = Combinations(all_places, SUK.rank()) c1 = R(0) for U in Possible_U: # first, build the matrix C_{i,U} columns_of_C = [] for unit in SUK.fundamental_units(): columns_of_C.append(column_Log(SUK, unit, U, prec)) C = Matrix(SUK.rank(), SUK.rank(), columns_of_C) # Is it invertible? if abs(C.determinant()) > 10**(-10): poss_c1 = C.inverse().apply_map(abs).norm(Infinity) c1 = R(max(poss_c1, c1)) return R(0.9999999) / (c1*SUK.rank())
def quadratic_L_function__numerical(n, d, num_terms=1000): """ Evaluate the Dirichlet L-function (for quadratic character) numerically (in a very naive way). EXAMPLES: First, let us test several values for a given character:: sage: RR = RealField(100) sage: for i in range(5): ....: print("L({}, (-4/.)): {}".format(1+2*i, RR(quadratic_L_function__exact(1+2*i, -4)) - quadratic_L_function__numerical(RR(1+2*i),-4, 10000))) L(1, (-4/.)): 0.000049999999500000024999996962707 L(3, (-4/.)): 4.99999970000003...e-13 L(5, (-4/.)): 4.99999922759382...e-21 L(7, (-4/.)): ...e-29 L(9, (-4/.)): ...e-29 This procedure fails for negative special values, as the Dirichlet series does not converge here:: sage: quadratic_L_function__numerical(-3,-4, 10000) Traceback (most recent call last): ... ValueError: the Dirichlet series does not converge here Test for several characters that the result agrees with the exact value, to a given accuracy :: sage: for d in range(-20,0): # long time (2s on sage.math 2014) ....: if abs(RR(quadratic_L_function__numerical(1, d, 10000) - quadratic_L_function__exact(1, d))) > 0.001: ....: print("Oops! We have a problem at d = {}: exact = {}, numerical = {}".format(d, RR(quadratic_L_function__exact(1, d)), RR(quadratic_L_function__numerical(1, d)))) """ # Set the correct precision if it is given (for n). if isinstance(n.parent(), sage.rings.abc.RealField): R = n.parent() else: from sage.rings.real_mpfr import RealField R = RealField() if n < 0: raise ValueError('the Dirichlet series does not converge here') d1 = fundamental_discriminant(d) ans = R.zero() for i in range(1,num_terms): ans += R(kronecker_symbol(d1,i) / R(i)**n) return ans
def K0_func(SUK, A, prec=106): r""" Return the constant `K_0` from Smart's TCDF paper, [Sma1995]_ INPUT: - ``SUK`` -- a group of `S`-units - ``A`` -- the set of the products of the coefficients of the `S`-unit equation with each root of unity of ``K`` - ``prec`` -- (default: 106) the precision of the real field OUTPUT: The constant ``K0``, a real number EXAMPLES:: sage: from sage.rings.number_field.S_unit_solver import K0_func sage: K.<xi> = NumberField(x^3-3) sage: SUK = UnitGroup(K, S=tuple(K.primes_above(3))) sage: A = K.roots_of_unity() sage: K0_func(SUK, A) # abs tol 1e-29 9.475576673109275443280257946929e17 REFERENCES: - [Sma1995]_ p. 824 """ R = RealField(prec) def c5_func(SUK, v, R): return c3_func(SUK, R.precision()) / (v.residue_class_degree()*R(v.smallest_integer()).log()*v.ramification_index()) def c6_func(SUK, v, A, R): return c4_func(SUK, v, A, R.precision()).log() / (v.residue_class_degree()*R(v.smallest_integer()).log()*v.ramification_index()) def c7_func(SUK, v, A, R): return (c4_func(SUK, v, A, R.precision())).log() / c3_func(SUK, R.precision()) def c10_func(SUK, v, A, R): # [Sma1995]_ p. 824 e_h = v.ramification_index() c_8, c_9 = c8_c9_func(SUK, v, A, R.precision()) return (2/(e_h*c5_func(SUK, v, R))) * (e_h*c6_func(SUK, v, A, R)+c_9+c_8*(c_8/(e_h*c5_func(SUK, v, R))).log()) return R(max([c10_func(SUK,v, A, R) for v in SUK.primes()] + [c7_func(SUK,v,A,R) for v in SUK.primes()]))
def __init__(self, X, f, codomain = None, check = False): r""" Create free binary string monoid on `n` generators. INPUT: x: A probability space f: A dictionary such that X[x] = value for x in X is the discrete function on X """ if not is_DiscreteProbabilitySpace(X): raise TypeError("Argument X (= %s) must be a discrete probability space" % X) if check: raise NotImplementedError("Not implemented") if codomain is None: RR = RealField() else: RR = codomain RandomVariable_generic.__init__(self, X, RR) self._function = f
def c11_func(SUK, v, A, prec=106): r""" Return the constant `c_{11}` from Smart's TCDF paper, [Sma1995]_ INPUT: - ``SUK`` -- a group of `S`-units - ``v`` -- a place of ``K``, finite (a fractional ideal) or infinite (element of ``SUK.number_field().places(prec)``) - ``A`` -- the set of the product of the coefficients of the `S`-unit equation with each root of unity of ``K`` - ``prec`` -- (default: 106) the precision of the real field OUTPUT: The constant ``c11``, a real number EXAMPLES:: sage: from sage.rings.number_field.S_unit_solver import c11_func sage: K.<xi> = NumberField(x^3-3) sage: SUK = UnitGroup(K, S=tuple(K.primes_above(3))) sage: phi_real = K.places()[0] sage: phi_complex = K.places()[1] sage: A = K.roots_of_unity() sage: c11_func(SUK, phi_real, A) # abs tol 1e-29 3.255848343572896153455615423662 sage: c11_func(SUK, phi_complex, A) # abs tol 1e-29 6.511696687145792306911230847323 REFERENCES: - [Sma1995]_ p. 825 """ R = RealField(prec) if is_real_place(v): return R(4*c4_func(SUK, v, A, prec)).log() / c3_func(SUK, prec) else: return 2*R(4*(c4_func(SUK, v, A, prec)).sqrt()).log() / c3_func(SUK, prec)
def removeConstants(list1, list2, pars): ''' Remove constant expresions from list1 and list2. If the constant expresion does not contain any parameter, it is just removed otherwise it is moved into constList1 and constList2 ''' i = 0 RR = RealField() # define Real field constList1 = [] constList2 = [] while i < len(list1): if (list1[i] in RR ): # checks if it is a Real Number of SAGE (no parameters at all) list1.pop(i) list2.pop(i) elif expresionIsConstant(list1[i], pars): # checks if there are only parameters constList1.append(list1.pop(i)) constList2.append(list2.pop(i)) else: i += 1 return constList1, constList2
def remove_constants(l1,l2): """ Given two lists, remove the entries in the first that are real constants, and also the corresponding elements in the second one. sage: from sage.interfaces.tides import subexpressions_list, remove_constants sage: f(a)=[1+cos(7)*a] sage: l1, l2 = subexpressions_list(f) sage: l1, l2 ([sin(7), cos(7), a*cos(7), a*cos(7) + 1], [('sin', 7), ('cos', 7), ('mul', cos(7), a), ('add', 1, a*cos(7))]) sage: remove_constants(l1,l2) sage: l1, l2 ([a*cos(7), a*cos(7) + 1], [('mul', cos(7), a), ('add', 1, a*cos(7))]) """ i=0 while i < len(l1): if l1[i] in RealField(): l1.pop(i) l2.pop(i) else: i+=1
def _coerce_map_from_(self, R): r""" There is a coercion from anything that has a coercion into the reals. The way Sage works is that everything that should be comparable with infinity can be coerced into the infinity ring, so if you ever compare with infinity the comparison is done there. If you don't have a coercion then you will get undesirable answers from the fallback comparison (likely memory location). EXAMPLES:: sage: InfinityRing.has_coerce_map_from(int) # indirect doctest True sage: InfinityRing.has_coerce_map_from(AA) True sage: InfinityRing.has_coerce_map_from(RDF) True sage: InfinityRing.has_coerce_map_from(RIF) True As explained above, comparison works by coercing to the infinity ring:: sage: cm = get_coercion_model() sage: cm.explain(AA(3), oo, operator.lt) Coercion on left operand via Coercion map: From: Algebraic Real Field To: The Infinity Ring Arithmetic performed after coercions. Result lives in The Infinity Ring The Infinity Ring The symbolic ring does not coerce to the infinity ring, so symbolic comparisons with infinities all happen in the symbolic ring:: sage: SR.has_coerce_map_from(InfinityRing) True sage: InfinityRing.has_coerce_map_from(SR) False Complex numbers do not coerce into the infinity ring (what would `i \infty` coerce to?). This is fine since they can not be compared, so we do not have to enforce consistency when comparing with infinity either:: sage: InfinityRing.has_coerce_map_from(CDF) False sage: InfinityRing.has_coerce_map_from(CC) False sage: CC(0, oo) < CC(1) # does not coerce to infinity ring True """ from sage.rings.real_mpfr import mpfr_prec_min, RealField if RealField(mpfr_prec_min()).has_coerce_map_from(R): return True from sage.rings.real_mpfi import RealIntervalField_class if isinstance(R, RealIntervalField_class): return True try: from sage.rings.real_arb import RealBallField if isinstance(R, RealBallField): return True except ImportError: pass return False
def epsinv(F, target, prec=53, target_tol=0.001, z=None, emb=None): """ Compute a bound on the hyperbolic distance. The true minimum will be within the computed bound. It is computed as the inverse of epsilon_F from [HS2018]_. INPUT: - ``F`` -- binary form of degree at least 3 with no multiple roots - ``target`` -- positive real number. The value we want to attain, i.e., the value we are taking the inverse of - ``prec``-- positive integer. precision to use in CC - ``target_tol`` -- positive real number. The tolerance with which we attain the target value. - ``z`` -- complex number. ``z_0`` covariant for F. - ``emb`` -- embedding into CC OUTPUT: a real number delta satisfying target + target_tol > eps_F(delta) > target. EXAMPLES:: sage: from sage.rings.polynomial.binary_form_reduce import epsinv sage: R.<x,y> = QQ[] sage: epsinv(-2*x^3 + 2*x^2*y + 3*x*y^2 + 127*y^3, 31.5022020249597) # tol 1e-12 4.02520895942207 """ def coshdelta(z): #The cosh of the hyperbolic distance from z = t+uj to j return (z.norm() + 1) / (2 * z.imag()) def RQ(delta): # this is the quotient R(F_0,z)/R(F_0,z(F)) for a generic z # at distance delta from j. See Lemma 4.2 in [HS2018]. cd = cosh(delta).n(prec=prec) sd = sinh(delta).n(prec=prec) return prod( [cd + (cost * phi[0] + sint * phi[1]) * sd for phi in phis]) def epsF(delta): pol = RQ(delta) #get R quotient in terms of z S = PolynomialRing(C, 'v') g = S([(i - d) * pol[i - d] for i in range(2 * d + 1)]) # take derivative drts = [ e for e in g.roots(ring=C, multiplicities=False) if (e.norm() - 1).abs() < 0.1 ] # find min return min([pol(r / r.abs()).real() for r in drts]) C = ComplexField(prec=prec) R = F.parent() d = F.degree() if z is None: z, th = covariant_z0(F, prec=prec, emb=emb) else: #need to do our own input checking if R.ngens() != 2 or any(sum(t) != d for t in F.exponents()): raise TypeError('must be a binary form') if d < 3: raise ValueError('must be at least degree 3') f = F.subs({R.gen(1): 1}).univariate_polynomial() #now we have a single variable polynomial if (max([ex for p,ex in f.roots(ring=C)]) >= QQ(d)/2)\ or (f.degree() < QQ(d)/2): raise ValueError('cannot have root with multiplicity >= deg(F)/2') R = RealField(prec=prec) PR = PolynomialRing(R, 't') t = PR.gen(0) # compute phi_1, ..., phi_k # first find F_0 and its roots # this change of variables on f moves z(f) to j, i.e. produces F_0 rts = f(z.imag() * t + z.real()).roots(ring=C) phis = [] # stereographic projection of roots for r, e in rts: phis.extend( [[2 * r.real() / (r.norm() + 1), (r.norm() - 1) / (r.norm() + 1)]]) if d != f.degree(): # include roots at infinity phis.extend([(d - f.degree()) * [0, 1]]) # for writing RQ in terms of generic z to minimize LC = LaurentSeriesRing(C, 'u', default_prec=2 * d + 2) u = LC.gen(0) cost = (u + u**(-1)) / 2 sint = (u - u**(-1)) / (2 * C.gen(0)) # first find an interval containing the desired value # then use regula falsi on log eps_F # d -> delta value in interval [0,1] # v in value in interval [1,epsF(1)] dl = R(0.0) vl = R(1.0) du = R(1.0) vu = epsF(du) while vu < target: # compute the next value of epsF for delta = 2*delta dl = du vl = vu du *= 2 vu = epsF(du) # now dl < delta <= du logt = target.log() l2 = (vu.log() - logt).n(prec=prec) l1 = (vl.log() - logt).n(prec=prec) dn = (dl * l2 - du * l1) / (l2 - l1) vn = epsF(dn) dl = du vl = vu du = dn vu = vn while (du - dl).abs() >= target_tol or max(vl, vu) < target: l2 = (vu.log() - logt).n(prec=prec) l1 = (vl.log() - logt).n(prec=prec) dn = (dl * l2 - du * l1) / (l2 - l1) vn = epsF(dn) dl = du vl = vu du = dn vu = vn return max(dl, du)
def genfiles_mpfr(integrator, driver, f, ics, initial, final, delta, parameters = None , parameter_values = None, dig = 20, tolrel=1e-16, tolabs=1e-16, output = ''): r""" Generate the needed files for the mpfr module of the tides library. INPUT: - ``integrator`` -- the name of the integrator file. - ``driver`` -- the name of the driver file. - ``f`` -- the function that determines the differential equation. - ``ics`` -- a list or tuple with the initial conditions. - ``initial`` -- the initial time for the integration. - ``final`` -- the final time for the integration. - ``delta`` -- the step of the output. - ``parameters`` -- the variables inside the function that should be treated as parameters. - ``parameter_values`` -- the values of the parameters for the particular initial value problem. - ``dig`` -- the number of digits of precission that will be used in the integration - ``tolrel`` -- the relative tolerance. - ``tolabs`` -- the absolute tolerance. - ``output`` -- the name of the file that the compiled integrator will write to This function creates two files, integrator and driver, that can be used later with the tides library ([TI]_). TESTS:: sage: from tempfile import mkdtemp sage: from sage.interfaces.tides import genfiles_mpfr sage: import os sage: import shutil sage: from sage.misc.temporary_file import tmp_dir sage: tempdir = tmp_dir() sage: intfile = os.path.join(tempdir, 'integrator.c') sage: drfile = os.path.join(tempdir ,'driver.c') sage: var('t,x,y,X,Y') (t, x, y, X, Y) sage: f(t,x,y,X,Y)=[X, Y, -x/(x^2+y^2)^(3/2), -y/(x^2+y^2)^(3/2)] sage: genfiles_mpfr(intfile, drfile, f, [1,0, 0, 0.2], 0, 10, 0.1, output = 'out', dig = 50) sage: fileint = open(intfile) sage: l = fileint.readlines() sage: fileint.close() sage: l[5] ' #include "mp_tides.h"\n' sage: l[15] '\tstatic int PARAMETERS = 0;\n' sage: l[25] '\t\tmpfrts_var_t(itd, link[5], var[3], i);\n' sage: l[30] '\t\tmpfrts_pow_t_c(itd, link[2], "-1.500000000000000000000000000000000000000000000000000", link[3], i);\n' sage: l[35] '\n' sage: l[36] ' }\n' sage: l[37] ' write_mp_solution();\n' sage: filedr = open(drfile) sage: l = filedr.readlines() sage: filedr.close() sage: l[6] ' #include "mpfr.h"\n' sage: l[16] ' int nfun = 0;\n' sage: l[26] '\tmpfr_set_str(v[2], "0.0000000000000000000000000000000000000000000000000000", 10, TIDES_RND);\n' sage: l[30] '\tmpfr_init2(tolabs, TIDES_PREC); \n' sage: l[34] '\tmpfr_init2(tini, TIDES_PREC); \n' sage: l[40] '\tmp_tides_delta(function_iteration, NULL, nvar, npar, nfun, v, p, tini, dt, nipt, tolrel, tolabs, NULL, fd);\n' sage: shutil.rmtree(tempdir) Check that ticket :trac:`17179` is fixed (handle expressions like `\\pi`):: sage: from sage.interfaces.tides import genfiles_mpfr sage: import os sage: import shutil sage: from sage.misc.temporary_file import tmp_dir sage: tempdir = tmp_dir() sage: intfile = os.path.join(tempdir, 'integrator.c') sage: drfile = os.path.join(tempdir ,'driver.c') sage: var('t,x,y,X,Y') (t, x, y, X, Y) sage: f(t,x,y,X,Y)=[X, Y, -x/(x^2+y^2)^(3/2), -y/(x^2+y^2)^(3/2)] sage: genfiles_mpfr(intfile, drfile, f, [pi, 0, 0, 0.2], 0, 10, 0.1, output = 'out', dig = 50) sage: fileint = open(intfile) sage: l = fileint.readlines() sage: fileint.close() sage: l[30] '\t\tmpfrts_pow_t_c(itd, link[2], "-1.500000000000000000000000000000000000000000000000000", link[3], i);\n' sage: filedr = open(drfile) sage: l = filedr.readlines() sage: filedr.close() sage: l[24] '\tmpfr_set_str(v[0], "3.141592653589793238462643383279502884197169399375101", 10, TIDES_RND);\n' sage: shutil.rmtree(tempdir) """ if parameters == None: parameters = [] if parameter_values == None: parameter_values = [] RR = RealField(ceil(dig * 3.3219)) l1, l2 = subexpressions_list(f, parameters) remove_repeated(l1, l2) remove_constants(l1, l2) l3=[] var = f[0].arguments() l0 = map(str, l1) lv = map(str, var) lp = map(str, parameters) for i in l2: oper = i[0] if oper in ["log", "exp", "sin", "cos", "atan", "asin", "acos"]: a = i[1] if str(a) in lv: l3.append((oper, 'var[{}]'.format(lv.index(str(a))))) elif str(a) in lp: l3.append((oper, 'par[{}]'.format(lp.index(str(a))))) else: l3.append((oper, 'link[{}]'.format(l0.index(str(a))))) else: a=i[1] b=i[2] sa = str(a) sb = str(b) consta=False constb=False if sa in lv: aa = 'var[{}]'.format(lv.index(sa)) elif sa in l0: aa = 'link[{}]'.format(l0.index(sa)) elif sa in lp: aa = 'par[{}]'.format(lp.index(sa)) else: consta=True aa = RR(a).str(truncate=False) if sb in lv: bb = 'var[{}]'.format(lv.index(sb)) elif sb in l0: bb = 'link[{}]'.format(l0.index(sb)) elif sb in lp: bb = 'par[{}]'.format(lp.index(sb)) else: constb=True bb = RR(b).str(truncate=False) if consta: oper += '_c' if not oper=='div': bb, aa = aa,bb elif constb: oper += '_c' l3.append((oper, aa, bb)) n = len(var) code = [] l0 = lv + l0 indices = [l0.index(str(i(*var)))+n for i in f] for i in range (1, n): aux = indices[i-1]-n if aux < n: code.append('mpfrts_var_t(itd, var[{}], var[{}], i);'.format(aux, i)) else: code.append('mpfrts_var_t(itd, link[{}], var[{}], i);'.format(aux-n, i)) for i in range(len(l3)): el = l3[i] string = "mpfrts_" if el[0] == 'add': string += 'add_t(itd, ' + el[1] + ', ' + el[2] + ', link[{}], i);'.format(i) elif el[0] == 'add_c': string += 'add_t_c(itd, "' + el[2] + '", ' + el[1] + ', link[{}], i);'.format(i) elif el[0] == 'mul': string += 'mul_t(itd, ' + el[1] + ', ' + el[2] + ', link[{}], i);'.format(i) elif el[0] == 'mul_c': string += 'mul_t_c(itd, "' + el[2] + '", ' + el[1] + ', link[{}], i);'.format(i) elif el[0] == 'pow_c': string += 'pow_t_c(itd, ' + el[1] + ', "' + el[2] + '", link[{}], i);'.format(i) elif el[0] == 'div': string += 'div_t(itd, ' + el[2] + ', ' + el[1] + ', link[{}], i);'.format(i) elif el[0] == 'div_c': string += 'div_t_cv(itd, "' + el[2] + '", ' + el[1] + ', link[{}], i);'.format(i) elif el[0] == 'log': string += 'log_t(itd, ' + el[1] + ', link[{}], i);'.format(i) elif el[0] == 'exp': string += 'exp_t(itd, ' + el[1] + ', link[{}], i);'.format(i) elif el[0] == 'sin': string += 'sin_t(itd, ' + el[1] + ', link[{}], link[{}], i);'.format(i+1, i) elif el[0] == 'cos': string += 'cos_t(itd, ' + el[1] + ', link[{}], link[{}], i);'.format(i-1, i) elif el[0] == 'atan': indarg = l0.index(str(1+l2[i][1]**2))-n string += 'atan_t(itd, ' + el[1] + ', link[{}], link[{}], i);'.format(indarg, i) elif el[0] == 'asin': indarg = l0.index(str(sqrt(1-l2[i][1]**2)))-n string += 'asin_t(itd, ' + el[1] + ', link[{}], link[{}], i);'.format(indarg, i) elif el[0] == 'acos': indarg = l0.index(str(-sqrt(1-l2[i][1]**2)))-n string += 'acos_t(itd, ' + el[1] + ', link[{}], link[{}], i);'.format(indarg, i) code.append(string) VAR = n-1 PAR = len(parameters) TT = len(code)+1-VAR outfile = open(integrator, 'a') auxstring = """ /**************************************************************************** This file has been created by Sage for its use with TIDES *****************************************************************************/ #include "mp_tides.h" long function_iteration(iteration_data *itd, mpfr_t t, mpfr_t v[], mpfr_t p[], int ORDER, mpfr_t *cvfd) { int i; int NCONST = 0; mpfr_t ct[0]; """ outfile.write(auxstring) outfile.write("\n\tstatic int VARIABLES = {};\n".format(VAR)) outfile.write("\tstatic int PARAMETERS = {};\n".format(PAR)) outfile.write("\tstatic int LINKS = {};\n".format(TT)) outfile.write('\tstatic int FUNCTIONS = 0;\n') outfile.write('\tstatic int POS_FUNCTIONS[1] = {0};\n') outfile.write('\n\tinitialize_mp_case();\n') outfile.write('\n\tfor(i=0; i<=ORDER; i++) {\n') for i in code: outfile.write('\t\t'+i+'\n') auxstring = """ } write_mp_solution(); clear_vpl(); clear_cts(); return NUM_COLUMNS; } """ outfile.write(auxstring) outfile.close() npar = len(parameter_values) outfile = open(driver, 'a') auxstring = """ /**************************************************************************** Driver file of the mp_tides program This file has been created automatically by Sage *****************************************************************************/ #include "mpfr.h" #include "mp_tides.h" long function_iteration(iteration_data *itd, mpfr_t t, mpfr_t v[], mpfr_t p[], int ORDER, mpfr_t *cvfd); int main() { int i; int nfun = 0; """ outfile.write(auxstring) outfile.write('\tset_precision_digits({});'.format(dig)) outfile.write('\n\tint npar = {};\n'.format(npar)) outfile.write('\tmpfr_t p[npar];\n') outfile.write('\tfor(i=0; i<npar; i++) mpfr_init2(p[i], TIDES_PREC);\n') for i in range(npar): outfile.write('\tmpfr_set_str(p[{}], "{}", 10, TIDES_RND);\n'.format(i,RR(parameter_values[i]).str(truncate=False))) outfile.write('\tint nvar = {};\n\tmpfr_t v[nvar];\n'.format(VAR)) outfile.write('\tfor(i=0; i<nvar; i++) mpfr_init2(v[i], TIDES_PREC);\n') for i in range(len(ics)): outfile.write('\tmpfr_set_str(v[{}], "{}", 10, TIDES_RND);\n'.format(i,RR(ics[i]).str(truncate=False))) outfile.write('\tmpfr_t tolrel, tolabs;\n') outfile.write('\tmpfr_init2(tolrel, TIDES_PREC); \n') outfile.write('\tmpfr_init2(tolabs, TIDES_PREC); \n') outfile.write('\tmpfr_set_str(tolrel, "{}", 10, TIDES_RND);\n'.format(RR(tolrel).str(truncate=False))) outfile.write('\tmpfr_set_str(tolabs, "{}", 10, TIDES_RND);\n'.format(RR(tolabs).str(truncate=False))) outfile.write('\tmpfr_t tini, dt; \n') outfile.write('\tmpfr_init2(tini, TIDES_PREC); \n') outfile.write('\tmpfr_init2(dt, TIDES_PREC); \n') outfile.write('\tmpfr_set_str(tini, "{}", 10, TIDES_RND);;\n'.format(RR(initial).str(truncate=False))) outfile.write('\tmpfr_set_str(dt, "{}", 10, TIDES_RND);\n'.format(RR(delta).str(truncate=False))) outfile.write('\tint nipt = {};\n'.format(floor((final-initial)/delta))) outfile.write('\tFILE* fd = fopen("' + output + '", "w");\n') outfile.write('\tmp_tides_delta(function_iteration, NULL, nvar, npar, nfun, v, p, tini, dt, nipt, tolrel, tolabs, NULL, fd);\n') outfile.write('\tfclose(fd);\n\treturn 0;\n}') outfile.close()
def genfiles_mintides(integrator, driver, f, ics, initial, final, delta, tolrel=1e-16, tolabs=1e-16, output = ''): r""" Generate the needed files for the min_tides library. INPUT: - ``integrator`` -- the name of the integrator file. - ``driver`` -- the name of the driver file. - ``f`` -- the function that determines the differential equation. - ``ics`` -- a list or tuple with the initial conditions. - ``initial`` -- the initial time for the integration. - ``final`` -- the final time for the integration. - ``delta`` -- the step of the output. - ``tolrel`` -- the relative tolerance. - ``tolabs`` -- the absolute tolerance. - ``output`` -- the name of the file that the compiled integrator will write to This function creates two files, integrator and driver, that can be used later with the min_tides library [TI]_. TESTS:: sage: from sage.interfaces.tides import genfiles_mintides sage: import os sage: import shutil sage: from sage.misc.temporary_file import tmp_dir sage: tempdir = tmp_dir() sage: intfile = os.path.join(tempdir, 'integrator.c') sage: drfile = os.path.join(tempdir ,'driver.c') sage: var('t,x,y,X,Y') (t, x, y, X, Y) sage: f(t,x,y,X,Y)=[X, Y, -x/(x^2+y^2)^(3/2), -y/(x^2+y^2)^(3/2)] sage: genfiles_mintides(intfile, drfile, f, [1,0, 0, 0.2], 0, 10, 0.1, output = 'out') sage: fileint = open(intfile) sage: l = fileint.readlines() sage: fileint.close() sage: l[5] ' #include "minc_tides.h"\n' sage: l[15] ' double XX[TT+1][MO+1];\n' sage: l[25] '\n' sage: l[35] '\t\tXX[1][i+1] = XX[3][i] / (i+1.0);\n' sage: filedr = open(drfile) sage: l = filedr.readlines() sage: filedr.close() sage: l[6] ' #include "minc_tides.h"\n' sage: l[15] ' double tolrel, tolabs, tini, tend, dt;\n' sage: l[25] '\ttolrel = 9.9999999999999998e-17 ;\n' sage: shutil.rmtree(tempdir) Check that ticket :trac:`17179` is fixed (handle expressions like `\\pi`):: sage: from sage.interfaces.tides import genfiles_mintides sage: import os sage: import shutil sage: from sage.misc.temporary_file import tmp_dir sage: tempdir = tmp_dir() sage: intfile = os.path.join(tempdir, 'integrator.c') sage: drfile = os.path.join(tempdir ,'driver.c') sage: var('t,x,y,X,Y') (t, x, y, X, Y) sage: f(t,x,y,X,Y)=[X, Y, -x/(x^2+y^2)^(3/2), -y/(x^2+y^2)^(3/2)] sage: genfiles_mintides(intfile, drfile, f, [pi, 0, 0, 0.2], 0, 10, 0.1, output = 'out') sage: fileint = open(intfile) sage: l = fileint.readlines() sage: fileint.close() sage: l[30] '\t\tXX[8][i] = pow_mc_c(XX[7],-1.5000000000000000,XX[8], i);\n' sage: filedr = open(drfile) sage: l = filedr.readlines() sage: filedr.close() sage: l[18] ' \tv[0] = 3.1415926535897931 ; \n' sage: shutil.rmtree(tempdir) """ RR = RealField() l1, l2 = subexpressions_list(f) remove_repeated(l1, l2) remove_constants(l1, l2) l0 = map(str, l1) #generate the corresponding c lines l3=[] var = f[0].arguments() lv = map(str, var) for i in l2: oper = i[0] if oper in ["log", "exp", "sin", "cos"]: a = i[1] if a in var: l3.append((oper, 'XX[{}]'.format(lv.index(str(a))))) elif a in l1: l3.append((oper, 'XX[{}]'.format(l0.index(str(a))+len(var)))) else: a=i[1] b=i[2] consta=False constb=False if str(a) in lv: aa = 'XX[{}]'.format(lv.index(str(a))) elif str(a) in l0: aa = 'XX[{}]'.format(l0.index(str(a))+len(var)) else: consta=True aa = RR(a).str(truncate=False) if str(b) in lv: bb = 'XX[{}]'.format(lv.index(str(b))) elif str(b) in l0: bb = 'XX[{}]'.format(l0.index(str(b))+len(var)) else: constb = True bb = RR(b).str(truncate=False) if consta: oper += '_c' if not oper=='div': bb, aa = aa, bb elif constb: oper += '_c' l3.append((oper, aa, bb)) n = len(var) res = [] for i in range(len(l3)): el = l3[i] string = "XX[{}][i] = ".format(i + n) if el[0] == 'add': string += el[1] + "[i] + " + el[2] +"[i];" elif el[0] == 'add_c': string += "(i==0)? {}+".format(el[2]) + el[1] + "[0] : "+ el[1]+ "[i];" elif el[0] == 'mul': string += "mul_mc("+el[1]+","+el[2]+",i);" elif el[0] == 'mul_c': string += el[2] + "*"+ el[1] + "[i];" elif el[0] == 'pow_c': string += "pow_mc_c("+el[1]+","+el[2]+",XX[{}], i);".format(i+n) elif el[0] == 'div': string += "div_mc("+el[2]+","+el[1]+",XX[{}], i);".format(i+n) elif el[0] == 'div_c': string += "inv_mc("+el[2]+","+el[1]+",XX[{}], i);".format(i+n) elif el[0] == 'log': string += "log_mc("+el[1]+",XX[{}], i);".format(i+n) elif el[0] == 'exp': string += "exp_mc("+el[1]+",XX[{}], i);".format(i+n) elif el[0] == 'sin': string += "sin_mc("+el[1]+",XX[{}], i);".format(i+n+1) elif el[0] == 'cos': string += "cos_mc("+el[1]+",XX[{}], i);".format(i+n-1) res.append(string) l0 = lv + l0 indices = [l0.index(str(i(*var))) + n for i in f] for i in range (1, n): res.append("XX[{}][i+1] = XX[{}][i] / (i+1.0);".format(i,indices[i-1]-n)) code = res outfile = open(integrator, 'a') auxstring = """ /**************************************************************************** This file has been created by Sage for its use with TIDES *****************************************************************************/ #include "minc_tides.h" void mincseries(double t,double *v, double *p, double **XVAR,int ORDER, int MO) { int VAR,PAR,TT,i,j, inext; """ outfile.write(auxstring) outfile.write("\tVAR = {};\n".format(n)) outfile.write("\tPAR = {};\n".format(0)) outfile.write("\tTT = {};\n".format(len(res))) auxstring = """ double XX[TT+1][MO+1]; for(j=0; j<=TT; j++) for(i=0; i<=ORDER; i++) XX[j][i] = 0.e0; XX[0][0] = t; XX[0][1] = 1.e0; for(i=1;i<=VAR;i++) { XX[i][0] = v[i-1]; } for(i=0;i<ORDER;i++) { """ outfile.write(auxstring) outfile.writelines(["\t\t"+i+"\n" for i in code]) outfile.write('\t}\n') outfile.write('\n') outfile.write('\tfor(j=0; j<=VAR; j++)\n') outfile.write('\t\tfor(i=0; i<=ORDER; i++)\n') outfile.write('\t\t\tXVAR[i][j] = XX[j][i];\n') outfile.write('}\n') outfile.write('\n') outfile = open(driver, 'a') auxstring = """ /**************************************************************************** Driver file of the minc_tides program This file has been automatically created by Sage *****************************************************************************/ #include "minc_tides.h" int main() { int i, VARS, PARS; VARS = %s ; PARS = 1; double tolrel, tolabs, tini, tend, dt; double v[VARS], p[PARS]; """%(n-1) outfile.write(auxstring) for i in range(len(ics)): outfile.write('\tv[{}] = {} ; \n'.format(i, RR(ics[i]).str(truncate=False))) outfile.write('\ttini = {} ;\n'.format(RR(initial).str(truncate=False))) outfile.write('\ttend = {} ;\n'.format(RR(final).str(truncate=False))) outfile.write('\tdt = {} ;\n'.format(RR(delta).str(truncate=False))) outfile.write('\ttolrel = {} ;\n'.format(RR(tolrel).str(truncate=False))) outfile.write('\ttolabs = {} ;\n'.format(RR(tolabs).str(truncate=False))) outfile.write('\textern char ofname[500];') outfile.write('\tstrcpy(ofname, "'+ output +'");\n') outfile.write('\tminc_tides(v,VARS,p,PARS,tini,tend,dt,tolrel,tolabs);\n') outfile.write('\treturn 0; \n }') outfile.close()
def bessel_K(nu, z, algorithm="pari", prec=53): r""" Implements the "K-Bessel function", or "modified Bessel function, 2nd kind", with index (or "order") nu and argument z. Defn:: pi*(bessel_I(-nu, z) - bessel_I(nu, z)) ---------------------------------------- 2*sin(pi*nu) if nu is not an integer and by taking a limit otherwise. Sometimes bessel_K(nu,z) is denoted K_nu(z) in the literature. In PARI, nu can be complex and z must be real and positive. EXAMPLES:: sage: bessel_K(3,2,"scipy") 0.64738539094... sage: bessel_K(3,2) 0.64738539094... sage: bessel_K(1,1) 0.60190723019... sage: bessel_K(1,1,"pari",10) 0.60 sage: bessel_K(1,1,"pari",100) 0.60190723019723457473754000154 TESTS:: sage: bessel_K(2,1.1, algorithm="maxima") Traceback (most recent call last): ... NotImplementedError: The K-Bessel function is only implemented for the pari and scipy algorithms Check whether the return value is real whenever the argument is real (#10251):: sage: bessel_K(5, 1.5, algorithm='scipy') in RR True """ if algorithm == "scipy": if prec != 53: raise ValueError, "for the scipy algorithm the precision must be 53" import scipy.special ans = str(scipy.special.kv(float(nu), float(z))) ans = ans.replace("(", "") ans = ans.replace(")", "") ans = ans.replace("j", "*I") ans = sage_eval(ans) return real(ans) if z in RR else ans elif algorithm == 'pari': from sage.libs.pari.all import pari try: R = RealField(prec) nu = R(nu) z = R(z) except TypeError: C = ComplexField(prec) nu = C(nu) z = C(z) K = C K = z.parent() return K(pari(nu).besselk(z, precision=prec)) elif algorithm == 'maxima': raise NotImplementedError, "The K-Bessel function is only implemented for the pari and scipy algorithms" else: raise ValueError, "unknown algorithm '%s'" % algorithm
def _sage_(self): r""" Convert a maple expression back to a Sage expression. This currently does not implement a parser for the Maple output language, therefore only very simple expressions will convert successfully. REFERENCE: https://www.asc.tuwien.ac.at/compmath/download/Monagan_Maple_Programming.pdf EXAMPLES:: sage: m = maple('x^2 + 5*y') # optional - maple sage: m.sage() # optional - maple x^2 + 5*y sage: m._sage_() # optional - maple x^2 + 5*y sage: m = maple('sin(sqrt(1-x^2)) * (1 - cos(1/x))^2') # optional - maple sage: m.sage() # optional - maple (cos(1/x) - 1)^2*sin(sqrt(-x^2 + 1)) Some matrices can be converted back:: sage: m = matrix(2, 2, [1, 2, x, 3]) # optional - maple sage: mm = maple(m) # optional - maple sage: mm.sage() == m # optional - maple True Some vectors can be converted back:: sage: m = vector([1, x, 2, 3]) # optional - maple sage: mm = maple(m) # optional - maple sage: mm.sage() == m # optional - maple True Integers and rationals are converted as such:: sage: maple(33).sage().parent() # optional - maple Integer Ring sage: maple(191/5).sage().parent() # optional - maple Rational Field Sets, lists, sequences:: sage: maple("[4,5,6]").sage() # optional - maple [4, 5, 6] sage: maple({14,33,6}).sage() # optional - maple {6, 14, 33} sage: maple("seq(i**2,i=1..5)").sage() # optional - maple (1, 4, 9, 16, 25) Strings:: sage: maple('"banane"').sage() # optional - maple '"banane"' Floats:: sage: Z3 = maple('evalf(Zeta(3))') # optional - maple sage: Z3.sage().parent() # optional - maple Real Field with 53 bits of precision sage: sq5 = maple('evalf(sqrt(5),100)') # optional - maple sage: sq5 = sq5.sage(); sq5 # optional - maple 2.23606797749978969640... sage: sq5.parent() # optional - maple Real Field with 332 bits of precision Functions are not yet converted back correctly:: sage: maple(hypergeometric([3,4],[5],x)) # optional - maple hypergeom([3, 4],[5],x) sage: _.sage() # known bug # optional - maple hypergeometric((3, 4), (5,), x) """ from sage.matrix.constructor import matrix from sage.modules.free_module_element import vector from sage.rings.integer_ring import ZZ # The next few lines are a very crude excuse for a maple "parser" maple_type = repr(self.whattype()) result = repr(self) result = result.replace("Pi", "pi") if maple_type == 'symbol': # pi pass # left to symbolic ring elif maple_type == 'string': # "banane" return result elif maple_type == 'exprseq': # 2, 2 n = self.parent()(f"[{self._name}]").nops()._sage_() return tuple(self[i] for i in range(1, n + 1)) elif maple_type == 'set': # {1, 2} n = self.nops()._sage_() return set(self.op(i)._sage_() for i in range(1, n + 1)) elif maple_type == 'list': # [1, 2] n = self.nops()._sage_() return [self.op(i)._sage_() for i in range(1, n + 1)] elif maple_type == "Matrix": # Matrix(2, 2, [[1,2],[3,4]]) mn = self.op(1) m = mn[1]._sage_() n = mn[2]._sage_() coeffs = [self[i + 1, j + 1]._sage_() for i in range(m) for j in range(n)] return matrix(m, n, coeffs) elif maple_type[:6] == "Vector": # Vector[row](3, [4,5,6]) n = self.op(1)._sage_() return vector([self[i + 1]._sage_() for i in range(n)]) elif maple_type == 'integer': return ZZ(result) elif maple_type == 'fraction': return self.op(1)._sage_() / self.op(2)._sage_() elif maple_type == "function": pass # TODO : here one should translate back function names elif maple_type == "float": from sage.rings.real_mpfr import RealField mantissa = len(repr(self.op(1))) prec = max(53, (mantissa * 13301) // 4004) R = RealField(prec) return R(result) elif maple_type == '`=`': # (1, 1) = 2 return (self.op(1)._sage_() == self.op(2)._sage()) try: from sage.symbolic.ring import SR return SR(result) except Exception: raise NotImplementedError("Unable to parse Maple output: %s" % result)
def exponential_integral_1(x, n=0): r""" Returns the exponential integral `E_1(x)`. If the optional argument `n` is given, computes list of the first `n` values of the exponential integral `E_1(x m)`. The exponential integral `E_1(x)` is .. math:: E_1(x) = \int_{x}^{\infty} e^{-t}/t dt INPUT: - ``x`` -- a positive real number - ``n`` -- (default: 0) a nonnegative integer; if nonzero, then return a list of values ``E_1(x*m)`` for m = 1,2,3,...,n. This is useful, e.g., when computing derivatives of L-functions. OUTPUT: A real number if n is 0 (the default) or a list of reals if n > 0. The precision is the same as the input, with a default of 53 bits in case the input is exact. EXAMPLES:: sage: exponential_integral_1(2) 0.0489005107080611 sage: exponential_integral_1(2,4) # abs tol 1e-18 [0.0489005107080611, 0.00377935240984891, 0.000360082452162659, 0.0000376656228439245] sage: exponential_integral_1(40,5) [1.03677326145166e-19, 2.22854325868847e-37, 6.33732515501151e-55, 2.02336191509997e-72, 6.88522610630764e-90] sage: exponential_integral_1(0) +Infinity sage: r = exponential_integral_1(RealField(150)(1)) sage: r 0.21938393439552027367716377546012164903104729 sage: parent(r) Real Field with 150 bits of precision sage: exponential_integral_1(RealField(150)(100)) 3.6835977616820321802351926205081189876552201e-46 TESTS: The relative error for a single value should be less than 1 ulp:: sage: for prec in [20..1000]: # long time (22s on sage.math, 2013) ....: R = RealField(prec) ....: S = RealField(prec+64) ....: for t in range(8): # Try 8 values for each precision ....: a = R.random_element(-15,10).exp() ....: x = exponential_integral_1(a) ....: y = exponential_integral_1(S(a)) ....: e = float(abs(S(x) - y)/x.ulp()) ....: if e >= 1.0: ....: print "exponential_integral_1(%s) with precision %s has error of %s ulp"%(a, prec, e) The absolute error for a vector should be less than `c 2^{-p}`, where `p` is the precision in bits of `x` and `c = 2 max(1, exponential_integral_1(x))`:: sage: for prec in [20..128]: # long time (15s on sage.math, 2013) ....: R = RealField(prec) ....: S = RealField(prec+64) ....: a = R.random_element(-15,10).exp() ....: n = 2^ZZ.random_element(14) ....: x = exponential_integral_1(a, n) ....: y = exponential_integral_1(S(a), n) ....: c = RDF(2 * max(1.0, y[0])) ....: for i in range(n): ....: e = float(abs(S(x[i]) - y[i]) << prec) ....: if e >= c: ....: print "exponential_integral_1(%s, %s)[%s] with precision %s has error of %s >= %s"%(a, n, i, prec, e, c) ALGORITHM: use the PARI C-library function ``eint1``. REFERENCE: - See Proposition 5.6.12 of Cohen's book "A Course in Computational Algebraic Number Theory". """ if isinstance(x, Expression): if x.is_trivial_zero(): from sage.rings.infinity import Infinity return Infinity else: raise NotImplementedError( "Use the symbolic exponential integral " + "function: exp_integral_e1.") elif not is_inexact(x): # x is exact and not an expression if not x: # test if exact x == 0 quickly from sage.rings.infinity import Infinity return Infinity # else x is not an exact 0 from sage.libs.pari.all import pari # Figure out output precision try: prec = parent(x).precision() except AttributeError: prec = 53 R = RealField(prec) if n <= 0: # Add extra bits to the input. # (experimentally verified -- Jeroen Demeyer) inprec = prec + math.ceil(math.log(2 * prec)) x = RealField(inprec)(x)._pari_() return R(x.eint1()) else: # PARI's algorithm is less precise as n grows larger: # add extra bits. # (experimentally verified -- Jeroen Demeyer) inprec = prec + 1 + math.ceil(1.4427 * math.log(n)) x = RealField(inprec)(x)._pari_() return [R(z) for z in x.eint1(n)]
def bdd_height(K, height_bound, tolerance=1e-2, precision=53): r""" Compute all elements in the number field `K` which have relative multiplicative height at most ``height_bound``. The function can only be called for number fields `K` with positive unit rank. An error will occur if `K` is `QQ` or an imaginary quadratic field. This algorithm computes 2 lists: L containing elements x in `K` such that H_k(x) <= B, and a list L' containing elements x in `K` that, due to floating point issues, may be slightly larger then the bound. This can be controlled by lowering the tolerance. In current implementation both lists (L,L') are merged and returned in form of iterator. ALGORITHM: This is an implementation of the revised algorithm (Algorithm 4) in [DK2013]_. INPUT: - ``height_bound`` -- real number - ``tolerance`` -- (default: 0.01) a rational number in (0,1] - ``precision`` -- (default: 53) positive integer OUTPUT: - an iterator of number field elements EXAMPLES: There are no elements of negative height:: sage: from sage.rings.number_field.bdd_height import bdd_height sage: K.<g> = NumberField(x^5 - x + 7) sage: list(bdd_height(K,-3)) [] The only nonzero elements of height 1 are the roots of unity:: sage: from sage.rings.number_field.bdd_height import bdd_height sage: K.<g> = QuadraticField(3) sage: list(bdd_height(K,1)) [0, -1, 1] :: sage: from sage.rings.number_field.bdd_height import bdd_height sage: K.<g> = QuadraticField(36865) sage: len(list(bdd_height(K,101))) # long time (4 s) 131 :: sage: from sage.rings.number_field.bdd_height import bdd_height sage: K.<g> = NumberField(x^6 + 2) sage: len(list(bdd_height(K,60))) # long time (5 s) 1899 :: sage: from sage.rings.number_field.bdd_height import bdd_height sage: K.<g> = NumberField(x^4 - x^3 - 3*x^2 + x + 1) sage: len(list(bdd_height(K,10))) 99 TESTS: Check that :trac:`22771` is fixed:: sage: from sage.rings.number_field.bdd_height import bdd_height sage: K.<v> = NumberField(x^3 + x + 1) sage: len(list(bdd_height(K,3))) 23 """ # global values, used in internal function B = height_bound theta = tolerance if B < 1: return embeddings = K.places(prec=precision) O_K = K.ring_of_integers() r1, r2 = K.signature() r = r1 + r2 - 1 RF = RealField(precision) lambda_gens_approx = {} class_group_rep_norm_log_approx = [] unit_log_dict = {} def rational_in(x, y): r""" Compute a rational number q, such that x<q<y using Archimedes' axiom """ z = y - x if z == 0: n = 1 else: n = RR(1/z).ceil() + 1 if RR(n*y).ceil() is n*y: # WHAT !? m = n*y - 1 else: m = RR(n*y).floor() return m / n def delta_approximation(x, delta): r""" Compute a rational number in range (x-delta, x+delta) """ return rational_in(x - delta, x + delta) def vector_delta_approximation(v, delta): r""" Compute a rational vector w=(w1, ..., wn) such that |vi-wi|<delta for all i in [1, n] """ return [delta_approximation(vi, delta) for vi in v] def log_map(number): r""" Compute the image of an element of `K` under the logarithmic map. """ x = number x_logs = [] for i in range(r1): sigma = embeddings[i] # real embeddings x_logs.append(sigma(x).abs().log()) for i in range(r1, r + 1): tau = embeddings[i] # Complex embeddings x_logs.append(2 * tau(x).abs().log()) return vector(x_logs) def log_height_for_generators_approx(alpha, beta, Lambda): r""" Compute the rational approximation of logarithmic height function. Return a lambda approximation h_K(alpha/beta) """ delta = Lambda / (r + 2) norm_log = delta_approximation(RR(O_K.ideal(alpha, beta).norm()).log(), delta) log_ga = vector_delta_approximation(log_map(alpha), delta) log_gb = vector_delta_approximation(log_map(beta), delta) arch_sum = sum([max(log_ga[k], log_gb[k]) for k in range(r + 1)]) return (arch_sum - norm_log) def packet_height(n, pair, u): r""" Compute the height of the element of `K` encoded by a given packet. """ gens = generator_lists[n] i = pair[0] j = pair[1] Log_gi = lambda_gens_approx[gens[i]] Log_gj = lambda_gens_approx[gens[j]] Log_u_gi = vector(Log_gi) + unit_log_dict[u] arch_sum = sum([max(Log_u_gi[k], Log_gj[k]) for k in range(r + 1)]) return (arch_sum - class_group_rep_norm_log_approx[n]) # Step 1 # Computes ideal class representative and their rational approx norm t = theta / (3*B) delta_1 = t / (6*r+12) class_group_reps = [] class_group_rep_norms = [] for c in K.class_group(): a = c.ideal() a_norm = a.norm() log_norm = RF(a_norm).log() log_norm_approx = delta_approximation(log_norm, delta_1) class_group_reps.append(a) class_group_rep_norms.append(a_norm) class_group_rep_norm_log_approx.append(log_norm_approx) class_number = len(class_group_reps) # Step 2 # Find generators for principal ideals of bounded norm possible_norm_set = set([]) for n in range(class_number): for m in range(1, (B + 1).ceil()): possible_norm_set.add(m * class_group_rep_norms[n]) bdd_ideals = bdd_norm_pr_ideal_gens(K, possible_norm_set) # Stores it in form of an dictionary and gives lambda(g)_approx for key g for norm in possible_norm_set: gens = bdd_ideals[norm] for g in gens: lambda_g_approx = vector_delta_approximation(log_map(g), delta_1) lambda_gens_approx[g] = lambda_g_approx # Step 3 # Find a list of all generators corresponding to each ideal a_l generator_lists = [] for l in range(class_number): this_ideal = class_group_reps[l] this_ideal_norm = class_group_rep_norms[l] gens = [] for i in range(1, (B + 1).ceil()): for g in bdd_ideals[i * this_ideal_norm]: if g in this_ideal: gens.append(g) generator_lists.append(gens) # Step 4 # Finds all relevant pair and their height gen_height_approx_dictionary = {} relevant_pair_lists = [] for n in range(class_number): relevant_pairs = [] gens = generator_lists[n] l = len(gens) for i in range(l): for j in range(i+1, l): if K.ideal(gens[i], gens[j]) == class_group_reps[n]: relevant_pairs.append([i, j]) gen_height_approx_dictionary[(n, i, j)] = log_height_for_generators_approx(gens[i], gens[j], t/6) relevant_pair_lists.append(relevant_pairs) # Step 5 b = rational_in(t/12 + RR(B).log(), t/4 + RR(B).log()) maximum = 0 for n in range(class_number): for p in relevant_pair_lists[n]: maximum = max(maximum, gen_height_approx_dictionary[(n, p[0], p[1])]) d_tilde = b + t/6 + maximum # Step 6 # computes fundamental units and their value under log map fund_units = UnitGroup(K).fundamental_units() fund_unit_logs = [log_map(fund_units[i]) for i in range(r)] S = column_matrix(fund_unit_logs).delete_rows([r]) S_inverse = S.inverse() S_norm = S.norm(Infinity) S_inverse_norm = S_inverse.norm(Infinity) upper_bound = (r**2) * max(S_norm, S_inverse_norm) m = RR(upper_bound).ceil() + 1 # Step 7 # Variables needed for rational approximation lambda_tilde = (t/12) / (d_tilde*r*(1+m)) delta_tilde = min(lambda_tilde/((r**2)*((m**2)+m*lambda_tilde)), 1/(r**2)) M = d_tilde * (upper_bound+lambda_tilde*RR(r).sqrt()) M = RR(M).ceil() d_tilde = RR(d_tilde) delta_2 = min(delta_tilde, (t/6)/(r*(r+1)*M)) # Step 8, 9 # Computes relevant points in polytope fund_unit_log_approx = [vector_delta_approximation(fund_unit_logs[i], delta_2) for i in range(r)] S_tilde = column_matrix(fund_unit_log_approx).delete_rows([r]) S_tilde_inverse = S_tilde.inverse() U = integer_points_in_polytope(S_tilde_inverse, d_tilde) # Step 10 # tilde suffixed list are used for computing second list (L_primed) yield K(0) U0 = [] U0_tilde = [] L0 = [] L0_tilde = [] # Step 11 # Computes unit height unit_height_dict = {} U_copy = copy(U) inter_bound = b - (5*t)/12 for u in U: u_log = sum([u[j]*vector(fund_unit_log_approx[j]) for j in range(r)]) unit_log_dict[u] = u_log u_height = sum([max(u_log[k], 0) for k in range(r + 1)]) unit_height_dict[u] = u_height if u_height < inter_bound: U0.append(u) if inter_bound <= u_height and u_height < b - (t/12): U0_tilde.append(u) if u_height > t/12 + d_tilde: U_copy.remove(u) U = U_copy relevant_tuples = set(U0 + U0_tilde) # Step 12 # check for relevant packets for n in range(class_number): for pair in relevant_pair_lists[n]: i = pair[0] j = pair[1] u_height_bound = b + gen_height_approx_dictionary[(n, i, j)] + t/4 for u in U: if unit_height_dict[u] < u_height_bound: candidate_height = packet_height(n, pair, u) if candidate_height <= b - 7*t/12: L0.append([n, pair, u]) relevant_tuples.add(u) elif candidate_height < b + t/4: L0_tilde.append([n, pair, u]) relevant_tuples.add(u) # Step 13 # forms a dictionary of all_unit_tuples and their value tuple_to_unit_dict = {} for u in relevant_tuples: unit = K.one() for k in range(r): unit *= fund_units[k]**u[k] tuple_to_unit_dict[u] = unit # Step 14 # Build all output numbers roots_of_unity = K.roots_of_unity() for u in U0 + U0_tilde: for zeta in roots_of_unity: yield zeta * tuple_to_unit_dict[u] # Step 15 for p in L0 + L0_tilde: gens = generator_lists[p[0]] i = p[1][0] j = p[1][1] u = p[2] c_p = tuple_to_unit_dict[u] * (gens[i] / gens[j]) for zeta in roots_of_unity: yield zeta * c_p yield zeta / c_p
def covariant_z0(F, z0_cov=False, prec=53, emb=None, error_limit=0.000001): r""" Return the covariant and Julia invariant from Cremona-Stoll [CS2003]_. In [CS2003]_ and [HS2018]_ the Julia invariant is denoted as `\Theta(F)` or `R(F, z(F))`. Note that you may get faster convergence if you first move `z_0(F)` to the fundamental domain before computing the true covariant INPUT: - ``F`` -- binary form of degree at least 3 with no multiple roots - ``z0_cov`` -- boolean, compute only the `z_0` invariant. Otherwise, solve the minimization problem - ``prec``-- positive integer. precision to use in CC - ``emb`` -- embedding into CC - ``error_limit`` -- sets the error tolerance (default:0.000001) OUTPUT: a complex number, a real number EXAMPLES:: sage: from sage.rings.polynomial.binary_form_reduce import covariant_z0 sage: R.<x,y> = QQ[] sage: F = 19*x^8 - 262*x^7*y + 1507*x^6*y^2 - 4784*x^5*y^3 + 9202*x^4*y^4\ ....: - 10962*x^3*y^5 + 7844*x^2*y^6 - 3040*x*y^7 + 475*y^8 sage: covariant_z0(F, prec=80, z0_cov=True) (1.3832330115323681438175 + 0.31233552177413614978744*I, 3358.4074848663492819259) sage: F = -x^8 + 6*x^7*y - 7*x^6*y^2 - 12*x^5*y^3 + 27*x^4*y^4\ ....: - 4*x^3*y^5 - 19*x^2*y^6 + 10*x*y^7 - 5*y^8 sage: covariant_z0(F, prec=80) (0.64189877107807122203366 + 1.1852516565091601348355*I, 3134.5148284344627168276) :: sage: R.<x,y> = QQ[] sage: covariant_z0(x^3 + 2*x^2*y - 3*x*y^2, z0_cov=True)[0] 0.230769230769231 + 0.799408065031789*I sage: -1/covariant_z0(-y^3 + 2*y^2*x + 3*y*x^2, z0_cov=True)[0] 0.230769230769231 + 0.799408065031789*I :: sage: R.<x,y> = QQ[] sage: covariant_z0(2*x^2*y - 3*x*y^2, z0_cov=True)[0] 0.750000000000000 + 1.29903810567666*I sage: -1/covariant_z0(-x^3 - x^2*y + 2*x*y^2, z0_cov=True)[0] + 1 0.750000000000000 + 1.29903810567666*I :: sage: R.<x,y> = QQ[] sage: covariant_z0(x^2*y - x*y^2, prec=100) (0.50000000000000000000000000003 + 0.86602540378443864676372317076*I, 1.5396007178390020386910634147) TESTS:: sage: R.<x,y>=QQ[] sage: covariant_z0(x^2 + 24*x*y + y^2) Traceback (most recent call last): ... ValueError: must be at least degree 3 sage: covariant_z0((x+y)^3, z0_cov=True) Traceback (most recent call last): ... ValueError: cannot have multiple roots for z0 invariant sage: covariant_z0(x^3 + 3*x*y + y) Traceback (most recent call last): ... TypeError: must be a binary form sage: covariant_z0(-2*x^2*y^3 + 3*x*y^4 + 127*y^5) Traceback (most recent call last): ... ValueError: cannot have a root with multiplicity >= 5/2 sage: covariant_z0((x^2+2*y^2)^2) Traceback (most recent call last): ... ValueError: must have at least 3 distinct roots """ R = F.parent() d = ZZ(F.degree()) if R.ngens() != 2 or any([sum(t) != d for t in F.exponents()]): raise TypeError('must be a binary form') if d < 3: raise ValueError('must be at least degree 3') f = F.subs({R.gen(1):1}).univariate_polynomial() if f.degree() < d: # we have a root at infinity if f.constant_coefficient() != 0: # invert so we find all roots! mat = matrix(ZZ,2,2,[0,-1,1,0]) else: t = 0 poly_ring = f.parent() while f(t) == 0: t += 1 mat = matrix(ZZ,2,2,[t,-1,1,0]) else: mat = matrix(ZZ,2,2,[1,0,0,1]) f = F(list(mat * vector(R.gens()))).subs({R.gen(1):1}).univariate_polynomial() # now we have a single variable polynomial with all the roots of F K = ComplexField(prec=prec) if f.base_ring() != K: if emb == None: f = f.change_ring(K) else: f = f.change_ring(emb) roots = f.roots() if (max([ex for p,ex in roots]) > 1)\ or (f.degree() < d-1): if z0_cov: raise ValueError('cannot have multiple roots for z0 invariant') else: # just need a starting point for Newton's method f = f.lc()*prod([p for p,ex in f.factor()])# removes multiple roots if f.degree() < 3: raise ValueError('must have at least 3 distinct roots') roots = f.roots() roots = [p for p,ex in roots] # finding quadratic Q_0, gives us our covariant, z_0 dF = f.derivative() n = ZZ(f.degree()) PR = PolynomialRing(K,'x,y') x,y = PR.gens() # finds Stoll and Cremona's Q_0 q = sum([(1/(dF(r).abs()**(2/(n-2)))) * ((x-(r*y)) * (x-(r.conjugate()*y)))\ for r in roots]) # this is Q_0 , always positive def as long as F has distinct roots A = q.monomial_coefficient(x**2) B = q.monomial_coefficient(x*y) C = q.monomial_coefficient(y**2) # need positive root try: z = ((-B + ((B**2)-(4*A*C)).sqrt())/(2*A)) except ValueError: raise ValueError("not enough precision") if z.imag() < 0: z = (-B - ((B**2)-(4*A*C)).sqrt())/(2*A) if z0_cov: FM = f # for Julia's invariant else: # solve the minimization problem for 'true' covariant CF = ComplexIntervalField(prec=prec) # keeps trac of our precision error RF = RealField(prec=prec) z = CF(z) FM = F(list(mat * vector(R.gens()))).subs({R.gen(1):1}).univariate_polynomial() from sage.rings.polynomial.complex_roots import complex_roots L1 = complex_roots(FM, min_prec=prec) L = [] err = z.diameter() # making sure multiplicity isn't too large using convergence conditions in paper for p,e in L1: if e >= d/2: raise ValueError('cannot have a root with multiplicity >= %s/2'%d) for _ in range(e): L.append(p) RCF = PolynomialRing(CF, 'u,t') a = RCF(0) c = RCF(0) u,t = RCF.gens() for l in L: a += u**2/((t-l) * (t-l.conjugate()) + u**2) c += (t-l.real())/((t-l) * (t-l.conjugate()) + u**2) # Newton's Method, to find solutions. Error bound is less than diameter of our z err = z.diameter() zz = z.diameter() g1 = a.numerator() - d/2*a.denominator() g2 = c.numerator() G = vector([g1, g2]) J = jacobian(G, [u, t]) v0 = vector([z.imag(), z.real()]) # z0 as starting point # finds our correct z while err <= zz: NJ = J.subs({u:v0[0], t:v0[1]}) NJinv = NJ.inverse() # inverse for CIF matrix seems to return fractions not CIF elements, fix them if NJinv.base_ring() != CF: NJinv = matrix(CF, 2, 2, [CF(zw.numerator()/zw.denominator()) for zw in NJinv.list()]) w = z v0 = v0 - NJinv*G.subs({u:v0[0], t:v0[1]}) z = v0[1].constant_coefficient() + v0[0].constant_coefficient()*CF.gen(0) err = z.diameter() # precision zz = (w - z).abs() # difference in w and z else: if err > error_limit or err.is_NaN(): raise ValueError("accuracy of Newton's root not within tolerance(%s > %s), increase precision"%(err, error_limit)) if z.imag() <= z.diameter(): raise ArithmeticError("Newton's method converged to z not in the upper half plane") z = z.center() # Julia's invariant if FM.base_ring() != ComplexField(prec=prec): FM = FM.change_ring(ComplexField(prec=prec)) tF = z.real() uF = z.imag() th = FM.lc().abs()**2 for r,ex in FM.roots(): for _ in range(ex): th = th * ((((r-tF).abs())**2 + uF**2)/uF) # undo shift and invert (if needed) # since F \cdot m ~ m^(-1)\cdot z # we apply m to z to undo m acting on F l = mat*vector([z,1]) return l[0]/l[1], th
def cholesky_decomposition(self, bit_prec=53): r""" Give the Cholesky decomposition of this quadratic form `Q` as a real matrix of precision ``bit_prec``. RESTRICTIONS: Q must be given as a QuadraticForm defined over `\ZZ`, `\QQ`, or some real field. If it is over some real field, then an error is raised if the precision given is not less than the defined precision of the real field defining the quadratic form! REFERENCE: From Cohen's "A Course in Computational Algebraic Number Theory" book, p 103. INPUT: ``bit_prec`` -- a natural number (default 53). OUTPUT: an upper triangular real matrix of precision ``bit_prec``. TO DO: If we only care about working over the real double field (RDF), then we can use the ``cholesky()`` method present for square matrices over that. .. note:: There is a note in the original code reading :: ##///////////////////////////////////////////////////////////////////////////////////////////////// ##/// Finds the Cholesky decomposition of a quadratic form -- as an upper-triangular matrix! ##/// (It's assumed to be global, hence twice the form it refers to.) <-- Python revision asks: Is this true?!? =| ##///////////////////////////////////////////////////////////////////////////////////////////////// EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1]) sage: Q.cholesky_decomposition() [ 1.00000000000000 0.000000000000000 0.000000000000000] [0.000000000000000 1.00000000000000 0.000000000000000] [0.000000000000000 0.000000000000000 1.00000000000000] :: sage: Q = QuadraticForm(QQ, 3, range(1,7)); Q Quadratic form in 3 variables over Rational Field with coefficients: [ 1 2 3 ] [ * 4 5 ] [ * * 6 ] sage: Q.cholesky_decomposition() [ 1.00000000000000 1.00000000000000 1.50000000000000] [0.000000000000000 3.00000000000000 0.333333333333333] [0.000000000000000 0.000000000000000 3.41666666666667] """ ## Check that the precision passed is allowed. if isinstance(self.base_ring(), RealField_class) and (self.base_ring().prec() < bit_prec): raise RuntimeError( "Oops! The precision requested is greater than that of the given quadratic form!" ) ## 1. Initialization n = self.dim() R = RealField(bit_prec) MS = MatrixSpace(R, n, n) Q = MS(R(0.5)) * MS( self.matrix() ) ## Initialize the real symmetric matrix A with the matrix for Q(x) = x^t * A * x ## DIAGNOSTIC ## 2. Loop on i for i in range(n): for j in range(i + 1, n): Q[j, i] = Q[i, j] ## Is this line redundant? Q[i, j] = Q[i, j] / Q[i, i] ## 3. Main Loop for k in range(i + 1, n): for l in range(k, n): Q[k, l] = Q[k, l] - Q[k, i] * Q[i, l] ## 4. Zero out the strictly lower-triangular entries for i in range(n): for j in range(i): Q[i, j] = 0 return Q
def theta_by_cholesky(self, q_prec): r""" Uses the real Cholesky decomposition to compute (the `q`-expansion of) the theta function of the quadratic form as a power series in `q` with terms correct up to the power `q^{\text{q\_prec}}`. (So its error is `O(q^ {\text{q\_prec} + 1})`.) REFERENCE: From Cohen's "A Course in Computational Algebraic Number Theory" book, p 102. EXAMPLES:: ## Check the sum of 4 squares form against Jacobi's formula sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1]) sage: Theta = Q.theta_by_cholesky(10) sage: Theta 1 + 8*q + 24*q^2 + 32*q^3 + 24*q^4 + 48*q^5 + 96*q^6 + 64*q^7 + 24*q^8 + 104*q^9 + 144*q^10 sage: Expected = [1] + [8*sum([d for d in divisors(n) if d%4 != 0]) for n in range(1,11)] sage: Expected [1, 8, 24, 32, 24, 48, 96, 64, 24, 104, 144] sage: Theta.list() == Expected True :: ## Check the form x^2 + 3y^2 + 5z^2 + 7w^2 represents everything except 2 and 22. sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Theta = Q.theta_by_cholesky(50) sage: Theta_list = Theta.list() sage: [m for m in range(len(Theta_list)) if Theta_list[m] == 0] [2, 22] """ ## RAISE AN ERROR -- This routine is deprecated! #raise NotImplementedError, "This routine is deprecated. Try theta_series(), which uses theta_by_pari()." n = self.dim() theta = [0 for i in range(q_prec + 1)] PS = PowerSeriesRing(ZZ, 'q') bit_prec = 53 ## TO DO: Set this precision to reflect the appropriate roundoff Cholesky = self.cholesky_decomposition( bit_prec ) ## error estimate, to be confident through our desired q-precision. Q = Cholesky ## <---- REDUNDANT!!! R = RealField(bit_prec) half = R(0.5) ## 1. Initialize i = n - 1 T = [R(0) for j in range(n)] U = [R(0) for j in range(n)] T[i] = R(q_prec) U[i] = 0 L = [0 for j in range(n)] x = [0 for j in range(n)] ## 2. Compute bounds #Z = sqrt(T[i] / Q[i,i]) ## IMPORTANT NOTE: sqrt preserves the precision of the real number it's given... which is not so good... =| #L[i] = floor(Z - U[i]) ## Note: This is a Sage Integer #x[i] = ceil(-Z - U[i]) - 1 ## Note: This is a Sage Integer too done_flag = False from_step4_flag = False from_step3_flag = True ## We start by pretending this, since then we get to run through 2 and 3a once. =) #double Q_val_double; #unsigned long Q_val; // WARNING: Still need a good way of checking overflow for this value... ## Big loop which runs through all vectors while (done_flag == False): ## Loop through until we get to i=1 (so we defined a vector x) while from_step3_flag or from_step4_flag: ## IMPORTANT WARNING: This replaces a do...while loop, so it may have to be adjusted! ## Go to directly to step 3 if we're coming from step 4, otherwise perform step 2. if from_step4_flag: from_step4_flag = False else: ## 2. Compute bounds from_step3_flag = False Z = sqrt(T[i] / Q[i, i]) L[i] = floor(Z - U[i]) x[i] = ceil(-Z - U[i]) - 1 ## 3a. Main loop ## DIAGNOSTIC #print #print " L = ", L #print " x = ", x x[i] += 1 while (x[i] > L[i]): ## DIAGNOSTIC #print " x = ", x i += 1 x[i] += 1 ## 3b. Main loop if (i > 0): from_step3_flag = True ## DIAGNOSTIC #print " i = " + str(i) #print " T[i] = " + str(T[i]) #print " Q[i,i] = " + str(Q[i,i]) #print " x[i] = " + str(x[i]) #print " U[i] = " + str(U[i]) #print " x[i] + U[i] = " + str(x[i] + U[i]) #print " T[i-1] = " + str(T[i-1]) T[i - 1] = T[i] - Q[i, i] * (x[i] + U[i]) * (x[i] + U[i]) # DIAGNOSTIC #print " T[i-1] = " + str(T[i-1]) #print i += -1 U[i] = 0 for j in range(i + 1, n): U[i] += Q[i, j] * x[j] ## 4. Solution found (This happens when i=0) from_step4_flag = True Q_val_double = q_prec - T[0] + Q[0, 0] * (x[0] + U[0]) * (x[0] + U[0]) Q_val = floor( Q_val_double + half ) ## Note: This rounds the value up, since the "round" function returns a float, but floor returns integer. ## DIAGNOSTIC #print " Q_val_double = ", Q_val_double #print " Q_val = ", Q_val #raise RuntimeError ## OPTIONAL SAFETY CHECK: eps = 0.000000001 if (abs(Q_val_double - Q_val) > eps): raise RuntimeError, "Oh No! We have a problem with the floating point precision... \n" \ + " Q_val_double = " + str(Q_val_double) + "\n" \ + " Q_val = " + str(Q_val) + "\n" \ + " x = " + str(x) + "\n" ## DIAGNOSTIC #print " The float value is " + str(Q_val_double) #print " The associated long value is " + str(Q_val) #print if (Q_val <= q_prec): theta[Q_val] += 2 ## 5. Check if x = 0, for exit condition. =) done_flag = True for j in range(n): if (x[j] != 0): done_flag = False ## Set the value: theta[0] = 1 theta[0] = 1 ## DIAGNOSTIC #print "Leaving ComputeTheta \n" ## Return the series, truncated to the desired q-precision return PS(theta)