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() 1.9997253418 A probability space can be defined on any list of elements. EXAMPLES:: 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() 1.5 """ 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 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 not done_flag: ## 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)
def green_function(self, G,v, **kwds): r""" Evaluates the local Green's function at the place ``v`` for ``self`` with ``N`` terms of the series or, in dimension 1, to within the specified error bound. Defaults to ``N=10`` if no kwds provided Use ``v=0`` for the archimedean place. Must be over `\ZZ` or `\QQ`. ALGORITHM: See Exercise 5.29 and Figure 5.6 of ``The Arithmetic of Dynamics Systems``, Joseph H. Silverman, Springer, GTM 241, 2007. INPUT: - ``G`` - an endomorphism of self.codomain() - ``v`` - non-negative integer. a place, use v=0 for the archimedean place kwds: - ``N`` - positive integer. number of terms of the series to use - ``prec`` - positive integer, float point or p-adic precision, default: 100 - ``error_bound`` - a positive real number OUTPUT: - a real number Examples:: sage: P.<x,y>=ProjectiveSpace(QQ,1) sage: H=Hom(P,P) sage: f=H([x^2+y^2,x*y]); sage: Q=P(5,1) sage: f.green_function(Q,0,N=30) 1.6460930159932946233759277576 :: sage: P.<x,y>=ProjectiveSpace(QQ,1) sage: H=Hom(P,P) sage: f=H([x^2+y^2,x*y]); sage: Q=P(5,1) sage: Q.green_function(f,0,N=200,prec=200) 1.6460930160038721802875250367738355497198064992657997569827 .. TODO:: error bounds for dimension > 1 """ N = kwds.get('N', None) #Get number of iterates (if entered) err = kwds.get('error_bound', None) #Get error bound (if entered) prec = kwds.get('prec', 100) #Get precision (if entered) R=RealField(prec) if not (v == 0 or is_prime(v)): raise ValueError("Invalid valuation (=%s) entered."%v) if v == 0: K = R else: K = Qp(v, prec) #Coerce all polynomials in F into polynomials with coefficients in K F=G.change_ring(K,False) d = F.degree() D=F.codomain().ambient_space().dimension_relative() if err is not None: if D!=1: raise NotImplementedError("error bounds only for dimension 1") err = R(err) if not err>0: raise ValueError, "Error bound (=%s) must be positive."%err #if doing error estimates, compute needed number of iterates res = F.resultant() #compute maximum coefficient of polynomials of F C = R(G.global_height(prec)) if v == 0: log_fact = R(0) for i in range(2*d+1): log_fact += R(i+1).log() B = max((R(res.abs()) - R(2*d).log() - (2*d-1)*C - log_fact).log().abs(), (C + R(d+1).log()).abs()) else: B = max(R(res.abs()).log() - ((2*d-1)*C).abs(), C.abs()) N = R(B/(err*(d-1))).log(d).abs().ceil() elif N is None: N=10 #default is to do 10 iterations #Coerce the coordinates into Q_v self.normalize_coordinates() if self.codomain().base_ring()==QQ: self.clear_denominators() P=self.change_ring(K,False) #START GREEN FUNCTION CALCULATION g = R(0) for i in range(N+1): m = -1 #compute the maximum absolute value of entries of a, and where it occurs for n in range(D+1): a_v = R(P[n].abs()) if a_v > m: j = n m = a_v #add to Greens function g += (1/R(d))**(i)*R(m).log() #normalize coordinates and evaluate P.scale_by(1/P[j]) P = F(P,False) return g
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 dynatomic_polynomial(self,period): r""" For a map `f:\mathbb{P}^1 \to \mathbb{P}^1` this function computes the dynatomic polynomial. The dynatomic polynomial is the analog of the cyclotomic polynomial and its roots are the points of formal period `n`. ALGORITHM: For a positive integer `n`, let `[F_n,G_n]` be the coordinates of the `nth` iterate of `f`. Then construct .. MATH:: \Phi^{\ast}_n(f)(x,y) = \sum_{d \mid n} (yF_d(x,y) - xG_d(x,y))^{\mu(n/d)} where `\mu` is the Moebius function. For a pair `[m,n]`, let `f^m = [F_m,G_m]`. Compute .. MATH:: \Phi^{\ast}_{m,n}(f)(x,y) = \Phi^{\ast}_n(f)(F_m,G_m)/\Phi^{\ast}_n(f)(F_{m-1},G_{m-1}) REFERENCES: .. - B. Hutz. Efficient determination of rational preperiodic points for endomorphisms of projective space. arxiv:1210.6246, 2012. - P. Morton and P. Patel. The Galois theory of periodic points of polynomial maps. Proc. London Math. Soc., 68 (1994), 225-263. INPUT: - ``period`` -- a positive integer or a list/tuple `[m,n]` where `m` is the preperiod and `n` is the period OUTPUT: - If possible, a two variable polynomial in the coordinate ring of ``self``. Otherwise a fraction field element of the coordinate ring of ``self`` .. TODO:: Do the division when the base ring is p-adic or a function field so that the output is a polynomial. EXAMPLES:: sage: P.<x,y>=ProjectiveSpace(QQ,1) sage: H=Hom(P,P) sage: f=H([x^2+y^2,y^2]) sage: f.dynatomic_polynomial(2) x^2 + x*y + 2*y^2 :: sage: P.<x,y>=ProjectiveSpace(ZZ,1) sage: H=Hom(P,P) sage: f=H([x^2+y^2,x*y]) sage: f.dynatomic_polynomial(4) 2*x^12 + 18*x^10*y^2 + 57*x^8*y^4 + 79*x^6*y^6 + 48*x^4*y^8 + 12*x^2*y^10 + y^12 :: sage: P.<x,y>=ProjectiveSpace(CC,1) sage: H=Hom(P,P) sage: f=H([x^2+y^2,3*x*y]) sage: f.dynatomic_polynomial(3) 13.0000000000000*x^6 + 117.000000000000*x^4*y^2 + 78.0000000000000*x^2*y^4 + y^6 :: sage: P.<x,y>=ProjectiveSpace(QQ,1) sage: H=Hom(P,P) sage: f=H([x^2-10/9*y^2,y^2]) sage: f.dynatomic_polynomial([2,1]) x^4*y^2 - 11/9*x^2*y^4 - 80/81*y^6 :: sage: P.<x,y>=ProjectiveSpace(QQ,1) sage: H=Hom(P,P) sage: f=H([x^2-29/16*y^2,y^2]) sage: f.dynatomic_polynomial([2,3]) x^12 - 95/8*x^10*y^2 + 13799/256*x^8*y^4 - 119953/1024*x^6*y^6 + 8198847/65536*x^4*y^8 - 31492431/524288*x^2*y^10 + 172692729/16777216*y^12 :: sage: P.<x,y>=ProjectiveSpace(ZZ,1) sage: H=Hom(P,P) sage: f=H([x^2-y^2,y^2]) sage: f.dynatomic_polynomial([1,2]) x^2 - x*y :: sage: P.<x,y>=ProjectiveSpace(QQ,1) sage: H=Hom(P,P) sage: f=H([x^3-y^3,3*x*y^2]) sage: f.dynatomic_polynomial([0,4])==f.dynatomic_polynomial(4) True :: sage: P.<x,y,z>=ProjectiveSpace(QQ,2) sage: H=Hom(P,P) sage: f=H([x^2+y^2,x*y,z^2]) sage: f.dynatomic_polynomial(2) Traceback (most recent call last): ... TypeError: Does not make sense in dimension >1 :: #TODO: it would be nice to get this to actually be a polynomial sage: P.<x,y>=ProjectiveSpace(Qp(5),1) sage: H=Hom(P,P) sage: f=H([x^2+y^2,y^2]) sage: f.dynatomic_polynomial(2) (x^4*y + (2 + O(5^20))*x^2*y^3 - x*y^4 + (2 + O(5^20))*y^5)/(x^2*y - x*y^2 + y^3) :: sage: L.<t>=PolynomialRing(QQ) sage: P.<x,y>=ProjectiveSpace(L,1) sage: H=Hom(P,P) sage: f=H([x^2+t*y^2,y^2]) sage: f.dynatomic_polynomial(2) x^2 + x*y + (t + 1)*y^2 :: sage: K.<c>=PolynomialRing(ZZ) sage: P.<x,y>=ProjectiveSpace(K,1) sage: H=Hom(P,P) sage: f=H([x^2+ c*y^2,y^2]) sage: f.dynatomic_polynomial([1,2]) x^2 - x*y + (c + 1)*y^2 """ if self.domain() != self.codomain(): raise TypeError("Must have same domain and codomain to iterate") from sage.schemes.projective.projective_space import is_ProjectiveSpace if is_ProjectiveSpace(self.domain())==False: raise NotImplementedError("Not implemented for subschemes") if self.domain().dimension_relative()>1: raise TypeError("Does not make sense in dimension >1") if (isinstance(period,(list,tuple))==False): period=[0,period] try: period[0]=ZZ(period[0]) period[1]=ZZ(period[1]) except TypeError: raise TypeError("Period and preperiod must be integers") if period[1]<=0: raise AttributeError("Period must be at least 1") if period[0]!=0: m=period[0] fm=self.nth_iterate_map(m) fm1=self.nth_iterate_map(m-1) n=period[1] PHI=1; x=self.domain().gen(0) y=self.domain().gen(1) F=self._polys f=F for d in range(1,n+1): if n%d ==0: PHI=PHI*((y*F[0]-x*F[1])**moebius(n/d)) if d !=n: #avoid extra iteration F=[f[0](F[0],F[1]),f[1](F[0],F[1])] if m!=0: PHI=PHI(fm._polys)/PHI(fm1._polys) else: PHI=1; x=self.domain().gen(0) y=self.domain().gen(1) F=self._polys f=F for d in range(1,period[1]+1): if period[1]%d ==0: PHI=PHI*((y*F[0]-x*F[1])**moebius(period[1]/d)) if d !=period[1]: #avoid extra iteration F=[f[0](F[0],F[1]),f[1](F[0],F[1])] from sage.rings.polynomial.polynomial_ring import PolynomialRing_general from sage.rings.polynomial.multi_polynomial_ring_generic import MPolynomialRing_generic if self.domain().base_ring()==RealField() or self.domain().base_ring()==ComplexField(): PHI=PHI.numerator()._maxima_().divide(PHI.denominator())[0].sage() elif isinstance(self.domain().base_ring(),(PolynomialRing_general,MPolynomialRing_generic)): from sage.rings.padics.generic_nodes import is_pAdicField, is_pAdicRing from sage.rings.function_field.function_field import is_FunctionField BR=self.domain().base_ring().base_ring() if is_pAdicField(BR) or is_pAdicRing(BR) or is_FunctionField(BR): raise NotImplementedError("Not implemented") PHI=PHI.numerator()._maxima_().divide(PHI.denominator())[0].sage() #do it again to divide out by denominators of coefficients PHI=PHI.numerator()._maxima_().divide(PHI.denominator())[0].sage() if PHI.denominator()==1: PHI=self.coordinate_ring()(PHI) return(PHI)
def homogenize(self, n, newvar='h'): r""" Return the homogenization of ``self``. If ``self.domain()`` is a subscheme, the domain of the homogenized map is the projective embedding of ``self.domain()`` INPUT: - ``newvar`` -- the name of the homogenization variable (only used when ``self.domain()`` is affine space) - ``n`` -- the n-th projective embedding into projective space OUTPUT: - :class:`SchemMorphism_polynomial_projective_space` EXAMPLES:: sage: A.<x,y>=AffineSpace(ZZ,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/x^5,y^2]) sage: f.homogenize(2,'z') Scheme endomorphism of Projective Space of dimension 2 over Integer Ring Defn: Defined on coordinates by sending (x : y : z) to (x^2*z^5 - 2*z^7 : x^5*y^2 : x^5*z^2) :: sage: A.<x,y>=AffineSpace(CC,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/(x*y),y^2-x]) sage: f.homogenize(0,'z') Scheme endomorphism of Projective Space of dimension 2 over Complex Field with 53 bits of precision Defn: Defined on coordinates by sending (x : y : z) to (x*y*z^2 : x^2*z^2 + (-2.00000000000000)*z^4 : x*y^3 - x^2*y*z) :: sage: A.<x,y>=AffineSpace(ZZ,2) sage: X=A.subscheme([x-y^2]) sage: H=Hom(X,X) sage: f=H([9*y^2,3*y]) sage: f.homogenize(2) Scheme endomorphism of Closed subscheme of Projective Space of dimension 2 over Integer Ring defined by: -x1^2 + x0*x2 Defn: Defined on coordinates by sending (x0 : x1 : x2) to (9*x0*x2 : 3*x1*x2 : x2^2) :: sage: R.<t>=PolynomialRing(ZZ) sage: A.<x,y>=AffineSpace(R,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/y,y^2-x]) sage: f.homogenize(0,'z') Scheme endomorphism of Projective Space of dimension 2 over Univariate Polynomial Ring in t over Integer Ring Defn: Defined on coordinates by sending (x : y : z) to (y*z^2 : x^2*z + (-2)*z^3 : y^3 - x*y*z) """ A = self.domain() B = self.codomain() N = A.ambient_space().dimension_relative() NB = B.ambient_space().dimension_relative() Vars = list(A.ambient_space().variable_names()) + [newvar] S = PolynomialRing(A.base_ring(), Vars) try: l = lcm([self[i].denominator() for i in range(N)]) except Exception: #no lcm l = prod([self[i].denominator() for i in range(N)]) from sage.rings.polynomial.polynomial_ring import PolynomialRing_general from sage.rings.polynomial.multi_polynomial_ring_generic import MPolynomialRing_generic if self.domain().base_ring() == RealField() or self.domain().base_ring( ) == ComplexField(): F = [ S(((self[i] * l).numerator())._maxima_().divide( self[i].denominator())[0].sage()) for i in range(N) ] elif isinstance(self.domain().base_ring(), (PolynomialRing_general, MPolynomialRing_generic)): F = [ S(((self[i] * l).numerator())._maxima_().divide( self[i].denominator())[0].sage()) for i in range(N) ] else: F = [S(self[i] * l) for i in range(N)] F.insert(n, S(l)) d = max([F[i].degree() for i in range(N + 1)]) F = [ F[i].homogenize(newvar) * S.gen(N)**(d - F[i].degree()) for i in range(N + 1) ] from sage.schemes.affine.affine_space import is_AffineSpace if is_AffineSpace(A) == True: from sage.schemes.projective.projective_space import ProjectiveSpace X = ProjectiveSpace(A.base_ring(), NB, Vars) else: X = A.projective_embedding(n).codomain() phi = S.hom(X.ambient_space().gens(), X.ambient_space().coordinate_ring()) F = [phi(f) for f in F] H = Hom(X, X) return (H(F))
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 [TIDES]_. 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_J(nu, z, algorithm="pari", prec=53): r""" Return value of the "J-Bessel function", or "Bessel function, 1st kind", with index (or "order") nu and argument z. :: Defn: Maxima: inf ==== - nu - 2 k nu + 2 k \ (-1)^k 2 z > ------------------------- / k! Gamma(nu + k + 1) ==== k = 0 PARI: inf ==== - 2k 2k \ (-1)^k 2 z Gamma(nu + 1) > ---------------------------- / k! Gamma(nu + k + 1) ==== k = 0 Sometimes bessel_J(nu,z) is denoted J_nu(z) in the literature. .. warning:: Inaccurate for small values of z. EXAMPLES:: sage: bessel_J(2,1.1) 0.136564153956658 sage: bessel_J(0,1.1) 0.719622018527511 sage: bessel_J(0,1) 0.765197686557967 sage: bessel_J(0,0) 1.00000000000000 sage: bessel_J(0.1,0.1) 0.777264368097005 We check consistency of PARI and Maxima:: sage: n(bessel_J(3,10,"maxima")) 0.0583793793051... sage: n(bessel_J(3,10,"pari")) 0.0583793793051868 sage: bessel_J(3,10,"scipy") 0.0583793793052... Check whether the return value is real whenever the argument is real (#10251):: sage: bessel_J(5, 1.5, algorithm='scipy') in RR True """ if 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 if nu == 0: nu = ZZ(0) K = z.parent() return K(pari(nu).besselj(z, precision=prec)) elif algorithm == "scipy": if prec != 53: raise ValueError, "for the scipy algorithm the precision must be 53" import scipy.special ans = str(scipy.special.jv(float(nu), complex(real(z), imag(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 == "maxima": if prec != 53: raise ValueError, "for the maxima algorithm the precision must be 53" return maxima_function("bessel_j")(nu, z) else: raise ValueError, "unknown algorithm '%s'" % algorithm
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 bessel_I(nu, z, algorithm="pari", prec=53): r""" Implements the "I-Bessel function", or "modified Bessel function, 1st kind", with index (or "order") nu and argument z. INPUT: - ``nu`` - a real (or complex, for pari) number - ``z`` - a real (positive) algorithm - "pari" or "maxima" or "scipy" prec - real precision (for PARI only) DEFINITION:: Maxima: inf ==== - nu - 2 k nu + 2 k \ 2 z > ------------------- / k! Gamma(nu + k + 1) ==== k = 0 PARI: inf ==== - 2 k 2 k \ 2 z Gamma(nu + 1) > ----------------------- / k! Gamma(nu + k + 1) ==== k = 0 Sometimes ``bessel_I(nu,z)`` is denoted ``I_nu(z)`` in the literature. .. warning:: In Maxima (the manual says) i0 is deprecated but ``bessel_i(0,*)`` is broken. (Was fixed in recent CVS patch though.) EXAMPLES:: sage: bessel_I(1,1,"pari",500) 0.565159103992485027207696027609863307328899621621092009480294489479255640964371134092664997766814410064677886055526302676857637684917179812041131208121 sage: bessel_I(1,1) 0.565159103992485 sage: bessel_I(2,1.1,"maxima") 0.16708949925104... sage: bessel_I(0,1.1,"maxima") 1.32616018371265... sage: bessel_I(0,1,"maxima") 1.2660658777520... sage: bessel_I(1,1,"scipy") 0.565159103992... Check whether the return value is real whenever the argument is real (#10251):: sage: bessel_I(5, 1.5, algorithm='scipy') in RR True """ if 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).besseli(z, precision=prec)) elif algorithm == "scipy": if prec != 53: raise ValueError, "for the scipy algorithm the precision must be 53" import scipy.special ans = str(scipy.special.iv(float(nu), complex(real(z), imag(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 # Return real value when arg is real elif algorithm == "maxima": if prec != 53: raise ValueError, "for the maxima algorithm the precision must be 53" return sage_eval(maxima.eval("bessel_i(%s,%s)" % (float(nu), float(z)))) else: raise ValueError, "unknown algorithm '%s'" % algorithm
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 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) 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) if F.base_ring() != C: if emb is None: compF = F.change_ring(C) else: compF = F.change_ring(emb) else: compF = F 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 x = f.parent().gen() 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 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 bdd_height(K, height_bound, precision=53, LLL=False): r""" Computes all elements in the number number field `K` which have relative multiplicative height at most ``height_bound``. The algorithm requires arithmetic with floating point numbers; ``precision`` gives the user the option to set the precision for such computations. It might be helpful to work with an LLL-reduced system of fundamental units, so the user has the option to perform an LLL reduction for the fundamental units by setting ``LLL`` to True. Certain computations may be faster assuming GRH, which may be done globally by using the number_field(True/False) switch. The function will only be called for number fields `K` with positive unit rank. An error will occur if `K` is `QQ` or an imaginary quadratic field. ALGORITHM: This is an implementation of the main algorithm (Algorithm 3) in [Doyle-Krumm]. INPUT: - ``height_bound`` - real number - ``precision`` - (default: 53) positive integer - ``LLL`` - (default: False) boolean value OUTPUT: - an iterator of number field elements .. WARNING:: In the current implementation, the output of the algorithm cannot be guaranteed to be correct due to the necessity of floating point computations. In some cases, the default 53-bit precision is considerably lower than would be required for the algorithm to generate correct output. .. TODO:: Should implement a version of the algorithm that guarantees correct output. See Algorithm 4 in [Doyle-Krumm] for details of an implementation that takes precision issues into account. 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^3 - 197*x + 39) sage: len(list(bdd_height(K, 200))) # long time (5 s) 451 :: 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,precision=100))) # 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,LLL=true))) 99 """ B = height_bound r1, r2 = K.signature() r = r1 + r2 - 1 if B < 1: return yield K(0) roots_of_unity = K.roots_of_unity() if B == 1: for zeta in roots_of_unity: yield zeta return RF = RealField(precision) embeddings = K.places(prec=precision) logB = RF(B).log() def log_map(number): r""" Computes the image of an element of `K` under the logarithmic map. """ x = number x_logs = [] for i in xrange(r1): sigma = embeddings[i] x_logs.append(abs(sigma(x)).log()) for i in xrange(r1, r + 1): tau = embeddings[i] x_logs.append(2 * abs(tau(x)).log()) return vector(x_logs) def log_height_for_generators(n, i, j): r""" Computes the logarithmic height of elements of the form `g_i/g_j`. """ gen_logs = generator_logs[n] Log_gi = gen_logs[i] Log_gj = gen_logs[j] arch_sum = sum([max(Log_gi[k], Log_gj[k]) for k in range(r + 1)]) return (arch_sum - class_group_rep_norm_logs[n]) def packet_height(n, pair, u): r""" Computes the height of the element of `K` encoded by a given packet. """ gen_logs = generator_logs[n] i = pair[0] j = pair[1] Log_gi = gen_logs[i] Log_gj = gen_logs[j] Log_u_gi = Log_gi + unit_log_dictionary[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_logs[n]) class_group_reps = [] class_group_rep_norms = [] class_group_rep_norm_logs = [] for c in K.class_group(): a = c.ideal() a_norm = a.norm() class_group_reps.append(a) class_group_rep_norms.append(a_norm) class_group_rep_norm_logs.append(RF(a_norm).log()) class_number = len(class_group_reps) # Get fundamental units and their images under the log map fund_units = UnitGroup(K).fundamental_units() fund_unit_logs = [log_map(fund_units[i]) for i in range(r)] unit_prec_test = fund_unit_logs try: [l.change_ring(QQ) for l in unit_prec_test] except ValueError: raise ValueError( 'Precision too low.') # QQ(log(0)) may occur if precision too low # If LLL is set to True, find an LLL-reduced system of fundamental units if LLL: cut_fund_unit_logs = column_matrix(fund_unit_logs).delete_rows([r]) lll_fund_units = [] for c in pari(cut_fund_unit_logs).qflll().python().columns(): new_unit = 1 for i in xrange(r): new_unit *= fund_units[i]**c[i] lll_fund_units.append(new_unit) fund_units = lll_fund_units fund_unit_logs = [log_map(_) for _ in fund_units] unit_prec_test = fund_unit_logs try: [l.change_ring(QQ) for l in unit_prec_test] except ValueError: raise ValueError('Precision too low.' ) # QQ(log(0)) may occur if precision too low # Find generators for principal ideals of bounded norm possible_norm_set = set([]) for n in xrange(class_number): for m in xrange(1, B + 1): possible_norm_set.add(m * class_group_rep_norms[n]) bdd_ideals = bdd_norm_pr_ideal_gens(K, possible_norm_set) # Distribute the principal ideal generators generator_lists = [] generator_logs = [] for n in xrange(class_number): this_ideal = class_group_reps[n] this_ideal_norm = class_group_rep_norms[n] gens = [] gen_logs = [] for i in xrange(1, B + 1): for g in bdd_ideals[i * this_ideal_norm]: if g in this_ideal: gens.append(g) gen_logs.append(log_map(g)) generator_lists.append(gens) generator_logs.append(gen_logs) # Compute the lists of relevant pairs and corresponding heights gen_height_dictionary = dict() relevant_pair_lists = [] for n in xrange(class_number): relevant_pairs = [] gens = generator_lists[n] s = len(gens) for i in xrange(s): for j in xrange(i + 1, s): if K.ideal(gens[i], gens[j]) == class_group_reps[n]: relevant_pairs.append([i, j]) gen_height_dictionary[(n, i, j)] = log_height_for_generators( n, i, j) relevant_pair_lists.append(relevant_pairs) # Find the bound for units to be computed gen_height_list = [ gen_height_dictionary[x] for x in gen_height_dictionary.keys() ] if len(gen_height_list) == 0: d = logB else: d = logB + max(gen_height_list) # Create the matrix whose columns are the logs of the fundamental units S = column_matrix(fund_unit_logs).delete_rows([r]) try: T = S.inverse() except ZeroDivisionError: raise ValueError('Precision too low.') # Find all integer lattice points in the unit polytope U = integer_points_in_polytope(T, ceil(d)) U0 = [] L0 = [] # Compute unit heights unit_height_dictionary = dict() unit_log_dictionary = dict() Ucopy = copy(U) for u in U: u_log = sum([u[j] * fund_unit_logs[j] for j in range(r)]) unit_log_dictionary[u] = u_log u_height = sum([max(u_log[k], 0) for k in range(r + 1)]) unit_height_dictionary[u] = u_height if u_height <= logB: U0.append(u) if u_height > d: Ucopy.remove(u) U = Ucopy # Sort U by height U = sorted(U, key=lambda u: unit_height_dictionary[u]) U_length = len(U) all_unit_tuples = set(copy(U0)) # Check candidate heights for n in xrange(class_number): relevant_pairs = relevant_pair_lists[n] for pair in relevant_pairs: i = pair[0] j = pair[1] gen_height = gen_height_dictionary[(n, i, j)] u_height_bound = logB + gen_height for k in xrange(U_length): u = U[k] u_height = unit_height_dictionary[u] if u_height <= u_height_bound: candidate_height = packet_height(n, pair, u) if candidate_height <= logB: L0.append([n, pair, u]) all_unit_tuples.add(u) else: break # Use previous data to build all necessary units units_dictionary = dict() for u in all_unit_tuples: unit = K(1) for j in xrange(r): unit *= (fund_units[j])**(u[j]) units_dictionary[u] = unit # Build all the output numbers for u in U0: unit = units_dictionary[u] for zeta in roots_of_unity: yield zeta * unit for packet in L0: n = packet[0] pair = packet[1] u = packet[2] i = pair[0] j = pair[1] relevant_pairs = relevant_pair_lists[n] gens = generator_lists[n] unit = units_dictionary[u] c = unit * gens[i] / gens[j] for zeta in roots_of_unity: yield zeta * c yield zeta / c
def cholesky_decomposition(self, bit_prec=53): """ 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 #print "After 1: Q is \n" + str(Q) ## 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 _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 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 precision 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 ([TIDES]_). 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 homogenize(self,n,newvar='h'): r""" Return the homogenization of ``self``. If ``self.domain()`` is a subscheme, the domain of the homogenized map is the projective embedding of ``self.domain()``. The domain and codomain can be homogenized at different coordinates: ``n[0]`` for the domain and ``n[1]`` for the codomain. INPUT: - ``newvar`` -- the name of the homogenization variable (only used when ``self.domain()`` is affine space) - ``n`` -- a tuple of nonnegative integers. If ``n`` is an integer, then the two values of the tuple are assumed to be the same. OUTPUT: - :class:`SchemMorphism_polynomial_projective_space` EXAMPLES:: sage: A.<x,y>=AffineSpace(ZZ,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/x^5,y^2]) sage: f.homogenize(2,'z') Scheme endomorphism of Projective Space of dimension 2 over Integer Ring Defn: Defined on coordinates by sending (x : y : z) to (x^2*z^5 - 2*z^7 : x^5*y^2 : x^5*z^2) :: sage: A.<x,y>=AffineSpace(CC,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/(x*y),y^2-x]) sage: f.homogenize((2,0),'z') Scheme endomorphism of Projective Space of dimension 2 over Complex Field with 53 bits of precision Defn: Defined on coordinates by sending (x : y : z) to (x*y*z^2 : x^2*z^2 + (-2.00000000000000)*z^4 : x*y^3 - x^2*y*z) :: sage: A.<x,y>=AffineSpace(ZZ,2) sage: X=A.subscheme([x-y^2]) sage: H=Hom(X,X) sage: f=H([9*y^2,3*y]) sage: f.homogenize(2) Scheme endomorphism of Closed subscheme of Projective Space of dimension 2 over Integer Ring defined by: -x1^2 + x0*x2 Defn: Defined on coordinates by sending (x0 : x1 : x2) to (9*x0*x2 : 3*x1*x2 : x2^2) :: sage: R.<t>=PolynomialRing(ZZ) sage: A.<x,y>=AffineSpace(R,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/y,y^2-x]) sage: f.homogenize((2,0),'z') Scheme endomorphism of Projective Space of dimension 2 over Univariate Polynomial Ring in t over Integer Ring Defn: Defined on coordinates by sending (x : y : z) to (y*z^2 : x^2*z + (-2)*z^3 : y^3 - x*y*z) :: sage: A.<x>=AffineSpace(QQ,1) sage: H=End(A) sage: f=H([x^2-1]) sage: f.homogenize((1,0),'y') Scheme endomorphism of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (y^2 : x^2 - y^2) """ A=self.domain() B=self.codomain() N=A.ambient_space().dimension_relative() NB=B.ambient_space().dimension_relative() #it is possible to homogenize the domain and codomain at different coordinates if isinstance(n,(tuple,list)): ind=tuple(n) else: ind=(n,n) #homogenize the domain Vars=list(A.ambient_space().variable_names()) Vars.insert(ind[0],newvar) S=PolynomialRing(A.base_ring(),Vars) #find the denominators if a rational function try: l=lcm([self[i].denominator() for i in range(N)]) except Exception: #no lcm l=prod([self[i].denominator() for i in range(N)]) from sage.rings.polynomial.polynomial_ring import PolynomialRing_general from sage.rings.polynomial.multi_polynomial_ring_generic import MPolynomialRing_generic if self.domain().base_ring()==RealField() or self.domain().base_ring()==ComplexField(): F=[S(((self[i]*l).numerator())._maxima_().divide(self[i].denominator())[0].sage()) for i in range(N)] elif isinstance(self.domain().base_ring(),(PolynomialRing_general,MPolynomialRing_generic)): F=[S(((self[i]*l).numerator())._maxima_().divide(self[i].denominator())[0].sage()) for i in range(N)] else: F=[S(self[i]*l) for i in range(N)] #homogenize the codomain F.insert(ind[1],S(l)) d=max([F[i].degree() for i in range(N+1)]) F=[F[i].homogenize(newvar)*S.gen(N)**(d-F[i].degree()) for i in range(N+1)] from sage.schemes.affine.affine_space import is_AffineSpace if is_AffineSpace(A)==True: from sage.schemes.projective.projective_space import ProjectiveSpace X=ProjectiveSpace(A.base_ring(),NB,Vars) else: X=A.projective_embedding(ind[1]).codomain() phi=S.hom(X.ambient_space().gens(),X.ambient_space().coordinate_ring()) F=[phi(f) for f in F] H=Hom(X,X) return(H(F))
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 _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.all import SR return SR(result) except Exception: raise NotImplementedError("Unable to parse Maple output: %s" % result)