def plotkin_upper_bound(n, q, d, algorithm=None): r""" Returns Plotkin upper bound for number of elements in the largest code of minimum distance d in `\GF{q}^n`. The algorithm="gap" option wraps Guava's UpperBoundPlotkin. EXAMPLES:: sage: codes.bounds.plotkin_upper_bound(10,2,3) 192 sage: codes.bounds.plotkin_upper_bound(10,2,3,algorithm="gap") # optional - gap_packages (Guava package) 192 """ if algorithm == "gap": gap.load_package("guava") ans = gap.eval("UpperBoundPlotkin(%s,%s,%s)" % (n, d, q)) return QQ(ans) else: t = 1 - 1 / q if (q == 2) and (n == 2 * d) and (d % 2 == 0): return 4 * d elif (q == 2) and (n == 2 * d + 1) and (d % 2 == 1): return 4 * d + 4 elif d > t * n: return int(d / (d - t * n)) elif d < t * n + 1: fact = (d - 1) / t if RR(fact) == RR(int(fact)): fact = int(fact) + 1 return int(d / (d - t * fact)) * q**(n - fact)
def random_isometry(self, preserve_orientation=True, **kwargs): r""" Return a random isometry in the Upper Half Plane model. INPUT: - ``preserve_orientation`` -- if ``True`` return an orientation-preserving isometry OUTPUT: - a hyperbolic isometry EXAMPLES:: sage: A = HyperbolicPlane().UHP().random_isometry() sage: B = HyperbolicPlane().UHP().random_isometry(preserve_orientation=False) sage: B.preserves_orientation() False """ [a,b,c,d] = [RR.random_element() for k in range(4)] while abs(a*d - b*c) < EPSILON: [a,b,c,d] = [RR.random_element() for k in range(4)] M = matrix(RDF, 2,[a,b,c,d]) M = M / (M.det()).abs().sqrt() if M.det() > 0: if not preserve_orientation: M = M * matrix(2,[0,1,1,0]) elif preserve_orientation: M = M * matrix(2,[0,1,1,0]) return self._Isometry(self, M, check=False)
def random_isometry(self, preserve_orientation=True, **kwargs): r""" Return a random isometry in the Upper Half Plane model. INPUT: - ``preserve_orientation`` -- if ``True`` return an orientation-preserving isometry OUTPUT: - a hyperbolic isometry EXAMPLES:: sage: A = HyperbolicPlane().UHP().random_isometry() sage: B = HyperbolicPlane().UHP().random_isometry(preserve_orientation=False) sage: B.preserves_orientation() False """ [a, b, c, d] = [RR.random_element() for k in range(4)] while abs(a * d - b * c) < EPSILON: [a, b, c, d] = [RR.random_element() for k in range(4)] M = matrix(RDF, 2, [a, b, c, d]) M = M / (M.det()).abs().sqrt() if M.det() > 0: if not preserve_orientation: M = M * matrix(2, [0, 1, 1, 0]) elif preserve_orientation: M = M * matrix(2, [0, 1, 1, 0]) return self._Isometry(self, M, check=False)
def plotkin_upper_bound(n, q, d, algorithm=None): r""" Return the Plotkin upper bound. Return the Plotkin upper bound for the number of elements in a largest code of minimum distance `d` in `\GF{q}^n`. More precisely this is a generalization of Plotkin's result for `q=2` to bigger `q` due to Berlekamp. The ``algorithm="gap"`` option wraps Guava's ``UpperBoundPlotkin``. EXAMPLES:: sage: codes.bounds.plotkin_upper_bound(10,2,3) 192 sage: codes.bounds.plotkin_upper_bound(10,2,3,algorithm="gap") # optional - gap_packages (Guava package) 192 """ _check_n_q_d(n, q, d, field_based=False) if algorithm == "gap": libgap.load_package("guava") return QQ(libgap.UpperBoundPlotkin(n, d, q)) else: t = 1 - 1 / q if (q == 2) and (n == 2 * d) and (d % 2 == 0): return 4 * d elif (q == 2) and (n == 2 * d + 1) and (d % 2 == 1): return 4 * d + 4 elif d > t * n: return int(d / (d - t * n)) elif d < t * n + 1: fact = (d - 1) / t if RR(fact) == RR(int(fact)): fact = int(fact) + 1 return int(d / (d - t * fact)) * q**(n - fact)
def _eval_(self, x): """ EXAMPLES:: sage: [dickman_rho(n) for n in [1..10]] [1.00000000000000, 0.306852819440055, 0.0486083882911316, 0.00491092564776083, 0.000354724700456040, 0.0000196496963539553, 8.74566995329392e-7, 3.23206930422610e-8, 1.01624828273784e-9, 2.77017183772596e-11] sage: dickman_rho(0) 1.00000000000000 """ if not is_RealNumber(x): try: x = RR(x) except (TypeError, ValueError): return None # PrimitiveFunction.__call__(self, SR(x)) if x < 0: return x.parent()(0) elif x <= 1: return x.parent()(1) elif x <= 2: return 1 - x.log() n = x.floor() if self._cur_prec < x.parent().prec() or n not in self._f: self._cur_prec = rel_prec = x.parent().prec() # Go a bit beyond so we're not constantly re-computing. max = x.parent()(1.1) * x + 10 abs_prec = (-self.approximate(max).log2() + rel_prec + 2 * max.log2()).ceil() self._f = {} if sys.getrecursionlimit() < max + 10: sys.setrecursionlimit(int(max) + 10) self._compute_power_series(max.floor(), abs_prec, cache_ring=x.parent()) return self._f[n](2 * (x - n - x.parent()(0.5)))
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 newton_sqrt(self,f,x0, prec): r""" Takes the square root of the power series `f` by Newton's method NOTE: this function should eventually be moved to `p`-adic power series ring INPUT: - f power series wtih coefficients in `\QQ_p` or an extension - x0 seeds the Newton iteration - prec precision OUTPUT: the square root of `f` EXAMPLES:: sage: R.<x> = QQ['x'] sage: H = HyperellipticCurve(x^5-23*x^3+18*x^2+40*x) sage: Q = H(0,0) sage: u,v = H.local_coord(Q,prec=100) sage: K = Qp(11,5) sage: HK = H.change_ring(K) sage: L.<a> = K.extension(x^20-11) sage: HL = H.change_ring(L) sage: S = HL(u(a),v(a)) sage: f = H.hyperelliptic_polynomials()[0] sage: y = HK.newton_sqrt( f(u(a)^11), a^11,5) sage: y^2 - f(u(a)^11) O(a^122) AUTHOR: - Jennifer Balakrishnan """ z = x0 try: x = f.parent().variable_name() if x!='a' : #this is to distinguish between extensions of Qp that are finite vs. not S = f.base_ring()[[x]] x = S.gen() except ValueError: pass z = x0 loop_prec = (log(RR(prec))/log(RR(2))).ceil() for i in range(loop_prec): z = (z+f/z)/2 try: return z + O(x**prec) except (NameError,ArithmeticError,TypeError): return z
def show(self, boundary=True, **options): r""" Plot ``self``. EXAMPLES: First some lines:: sage: PD = HyperbolicPlane().PD() sage: PD.get_geodesic(0, 1).show() Graphics object consisting of 2 graphics primitives sage: PD.get_geodesic(0, 0.3+0.8*I).show() Graphics object consisting of 2 graphics primitives Then some generic geodesics:: sage: PD.get_geodesic(-0.5, 0.3+0.4*I).show() Graphics object consisting of 2 graphics primitives sage: PD.get_geodesic(-1, exp(3*I*pi/7)).show(linestyle="dashed", color="red") Graphics object consisting of 2 graphics primitives sage: PD.get_geodesic(exp(2*I*pi/11), exp(1*I*pi/11)).show(thickness=6, color="orange") Graphics object consisting of 2 graphics primitives """ opts = {'axes': False, 'aspect_ratio': 1} opts.update(self.graphics_options()) opts.update(options) end_1, end_2 = [CC(k.coordinates()) for k in self.endpoints()] bd_1, bd_2 = [CC(k.coordinates()) for k in self.ideal_endpoints()] # Check to see if it's a line if abs(bd_1 + bd_2) < EPSILON: pic = line([end_1, end_2], **opts) else: # If we are here, we know it's not a line # So we compute the center and radius of the circle invdet = RR.one() / (real(bd_1) * imag(bd_2) - real(bd_2) * imag(bd_1)) centerx = (imag(bd_2) - imag(bd_1)) * invdet centery = (real(bd_1) - real(bd_2)) * invdet center = centerx + I * centery radius = RR(abs(bd_1 - center)) # Now we calculate the angles for the arc theta1 = CC(end_1 - center).arg() theta2 = CC(end_2 - center).arg() theta1, theta2 = sorted([theta1, theta2]) # Make sure the sector is inside the disk if theta2 - theta1 > pi: theta1 += 2 * pi pic = arc((centerx, centery), radius, sector=(theta1, theta2), **opts) if boundary: pic += self._model.get_background_graphic() return pic
def rotation_matrix_angle(r, check=False): r""" Return the angle of the rotation matrix ``r`` divided by ``2 pi``. EXAMPLES:: sage: from flatsurf.geometry.matrix_2x2 import rotation_matrix_angle sage: def rot_matrix(p, q): ....: z = QQbar.zeta(q) ** p ....: c = z.real() ....: s = z.imag() ....: return matrix(AA, 2, [c,-s,s,c]) sage: [rotation_matrix_angle(rot_matrix(i, 5)) for i in range(1,5)] [1/5, 2/5, 3/5, 4/5] sage: [rotation_matrix_angle(rot_matrix(i, 5)) for i in range(1,5)] [1/5, 2/5, 3/5, 4/5] sage: [rotation_matrix_angle(rot_matrix(i,7)) for i in range(1,7)] [1/7, 2/7, 3/7, 4/7, 5/7, 6/7] Some random tests:: sage: for _ in range(100): ....: r = QQ.random_element(x=0,y=500) ....: r -= r.floor() ....: m = rot_matrix(r.numerator(), r.denominator()) ....: assert rotation_matrix_angle(m) == r .. NOTE:: This is using floating point arithmetic and might be wrong. """ e0, e1 = r.change_ring(CDF).eigenvalues() m0 = (e0.log() / 2 / CDF.pi()).imag() m1 = (e1.log() / 2 / CDF.pi()).imag() r0 = RR(m0).nearby_rational(max_denominator=10000) r1 = RR(m1).nearby_rational(max_denominator=10000) if r0 != -r1: raise RuntimeError r0 = r0.abs() if r[0][1] > 0: return QQ.one() - r0 else: return r0 if check: e = r.change_ring(AA).eigenvalues()[0] if e.minpoly() != ZZ['x'].cyclotomic_polynomial()(r.denominator()): raise RuntimeError z = QQbar.zeta(r.denominator()) if z**r.numerator() != e: raise RuntimeError return r
def good_primes(B): r""" Given the bound returns the prime whose product is greater than ``B`` and which would take least amount of time to run main sieve algorithm Complexity of finding points modulo primes is assumed to be N^2 * P_max^{N}. Complexity of lifting points and LLL() function is assumed to be close to N^5 * (alpha^dim_scheme / P_max). where alpha is product of all primes, and P_max is largest prime in list. """ M = dict() # stores optimal list of primes, corresponding to list size small_primes = sufficient_primes(B) max_length = len(small_primes) M[max_length] = small_primes current_count = max_length - 1 while current_count > 1: current_list = [] # stores prime which are bigger than least updated_list = [] best_list = [] least = (RR(B)**(1.00 / current_count)).floor() for i in range(current_count): current_list.append(next_prime(least)) least = current_list[-1] # improving list of primes by taking prime less than least # this part of algorithm is used to centralize primes around `least` prod_prime = prod(current_list) least = current_list[0] while least != 2 and prod_prime > B and len( updated_list) < current_count: best_list = updated_list + current_list[:current_count - len(updated_list)] updated_list.append(previous_prime(least)) least = updated_list[-1] removed_prime = current_list[current_count - len(updated_list)] prod_prime = (prod_prime * least) / removed_prime M[current_count] = sorted(best_list) current_count = current_count - 1 best_size = 2 best_time = (N**2) * M[2][-1]**(N) + ( N**5 * RR(prod(M[2])**dim_scheme / M[2][-1])) for i in range(2, max_length + 1): current_time = (N**2) * M[i][-1]**(N) + ( N**5 * RR(prod(M[i])**dim_scheme / M[i][-1])) if current_time < best_time: best_size = i best_time = current_time return M[best_size]
def rational_in(x, y): r""" Computes a rational number q, such that x<q<y using Archimedes' axiom """ z = y - x n = RR(1 / z).ceil() + 1 if RR(n * y).ceil() is n * y: m = n * y - 1 else: m = RR(n * y).floor() return m / n
def bkz_runtime_k_sieve(k, n): """ Runtime estimation given ‘k‘ and assuming sieving is used to realise the SVP oracle. For small ‘k‘ we use estimates based on experiments. For ‘k ě 90‘ we use the asymptotics. """ repeat = _sage_const_3 * log(n, _sage_const_2) - _sage_const_2 * log( k, _sage_const_2) + log(log(n, _sage_const_2), _sage_const_2) if k < _sage_const_90: return RR(_sage_const_0p45 * k + _sage_const_12p31) + repeat else: # we simply pick the same additive constant 12.31 as above return RR(_sage_const_0p3366 * k + _sage_const_12p31) + repeat
def rotation_matrix_angle(r, check=False): r""" Return the angle of the rotation matrix ``r`` divided by ``2 pi``. EXAMPLES:: sage: from flatsurf.geometry.matrix_2x2 import rotation_matrix_angle sage: def rot_matrix(p, q): ....: z = QQbar.zeta(q) ** p ....: c = z.real() ....: s = z.imag() ....: return matrix(AA, 2, [c,-s,s,c]) sage: [rotation_matrix_angle(rot_matrix(i, 5)) for i in range(1,5)] [1/5, 2/5, 3/5, 4/5] sage: [rotation_matrix_angle(rot_matrix(i,7)) for i in range(1,7)] [1/7, 2/7, 3/7, 4/7, 5/7, 6/7] Some random tests:: sage: for _ in range(100): ....: r = QQ.random_element(x=0,y=500) ....: r -= r.floor() ....: m = rot_matrix(r.numerator(), r.denominator()) ....: assert rotation_matrix_angle(m) == r .. NOTE:: This is using floating point arithmetic and might be wrong. """ e0,e1 = r.change_ring(CDF).eigenvalues() m0 = (e0.log() / 2 / CDF.pi()).imag() m1 = (e1.log() / 2 / CDF.pi()).imag() r0 = RR(m0).nearby_rational(max_denominator=10000) r1 = RR(m1).nearby_rational(max_denominator=10000) if r0 != -r1: raise RuntimeError r0 = r0.abs() if r[0][1] > 0: return QQ.one() - r0 else: return r0 if check: e = r.change_ring(AA).eigenvalues()[0] if e.minpoly() != ZZ['x'].cyclotomic_polynomial()(r.denominator()): raise RuntimeError z = QQbar.zeta(r.denominator()) if z**r.numerator() != e: raise RuntimeError return r
def circle_plot(self): pts = [] pi = RR.pi() for angle in self.angles: angle = RR(angle)*pi c = angle.cos() s = angle.sin() if abs(s) < 0.00000001: pts.append((c,s)) else: pts.extend([(c,s),(c,-s)]) P = points(pts,size=100) + circle((0,0),1,color='black') P.axes(False) P.set_aspect_ratio(1) return encode_plot(P)
def circle_plot(self): pts = [] pi = RR.pi() for angle in self.angles: angle = RR(angle) * pi c = angle.cos() s = angle.sin() if abs(s) < 0.00000001: pts.append((c, s)) else: pts.extend([(c, s), (c, -s)]) P = points(pts, size=100) + circle((0, 0), 1, color='black') P.axes(False) P.set_aspect_ratio(1) return encode_plot(P)
def __init__(self, P, n, sigma): r""" Construct a sampler for univariate polynomials of degree ``n-1`` where coefficients are drawn independently with standard deviation ``sigma``. INPUT: - ``P`` - a univariate polynomial ring over the Integers - ``n`` - number of coefficients to be sampled - ``sigma`` - coefficients `x` are accepted with probability proportional to `\exp(-x²/(2σ²))`. If an object of type :class:`sage.stats.distributions.discrete_gaussian_integer.DiscreteGaussianDistributionIntegerSampler` is passed, then this sampler is used to sample coefficients. EXAMPLES:: sage: from sage.stats.distributions.discrete_gaussian_polynomial import DiscreteGaussianDistributionPolynomialSampler sage: DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], 8, 3.0)() 3*x^7 + 3*x^6 - 3*x^5 - x^4 - 5*x^2 + 3 sage: gs = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], 8, 3.0) sage: [gs() for _ in range(3)] [4*x^7 + 4*x^6 - 4*x^5 + 2*x^4 + x^3 - 4*x + 7, -5*x^6 + 4*x^5 - 3*x^3 + 4*x^2 + x, 2*x^7 + 2*x^6 + 2*x^5 - x^4 - 2*x^2 + 3*x + 1] """ if isinstance(sigma, DiscreteGaussianDistributionIntegerSampler): self.D = sigma else: self.D = DiscreteGaussianDistributionIntegerSampler(RR(sigma)) self.n = ZZ(n) self.P = P
def __float__(self): r""" Generate a floating-point infinity. The printing of floating-point infinity varies across platforms. EXAMPLES:: sage: RDF(infinity) +infinity sage: float(infinity) # random +infinity sage: CDF(infinity) +infinity sage: infinity.__float__() # random +infinity sage: RDF(-infinity) -infinity sage: float(-infinity) # random -inf sage: CDF(-infinity) -infinity sage: (-infinity).__float__() # random -inf """ # Evidently there is no standard way to generate an infinity # in Python (before Python 2.6). from sage.rings.all import RR return float(RR(self))
def take_power(x,n): r''' TESTS:: sage: from functions import * sage: A = Matrix(Zmod(3,10),5,5,range(10,10+25)) sage: take_power(A,10) == A ** 10 True ''' if n == 1: return x R = x.parent().base_ring() y = x.parent()(1) if n == 0: return y while n > 1: verbose("log_2(n) = %s"%RR(n).log(2)) if n % 2 == 0: n = n // 2 else: y = multiply_and_reduce(x,y) n = (n - 1) // 2 x = multiply_and_reduce(x,x) return multiply_and_reduce(x, y)
def bkz_runtime_k_bkz2(k, n): """ Runtime estimation given ‘k‘ and assuming [CheNgu12]_ estimates are correct. The constants in this function were derived as follows based on Table 4 in [CheNgu12]_:: sage: dim = [100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250] sage: nodes = [39.0, 44.0, 49.0, 54.0, 60.0, 66.0, 72.0, 78.0, 84.0, 96.0, 99.0, 105.0, 111.0, 120.0, 127.0, 134.0] sage: times = [c + log(200,2).n() for c in nodes] sage: T = zip(dim, nodes) sage: var("a,b,c,k") sage: f = a*k*log(k, 2.0) + b*k + c sage: f = f.function(k) sage: f.subs(find_fit(T, f, solution_dict=True)) k |--> 0.270188776350190*k*log(k) - 1.0192050451318417*k + 16.10253135200765 .. [CheNgu12] Yuanmi Chen and Phong Q. Nguyen. BKZ 2.0: Better lattice security estimates ( Full Version). 2012. http://www.di.ens.fr/~ychen/research/Full_BKZ.pdf """ repeat = _sage_const_3 * log(n, _sage_const_2) - _sage_const_2 * log( k, _sage_const_2) + log(log(n, _sage_const_2), _sage_const_2) return RR(_sage_const_0p270188776350190 * k * log(k) - _sage_const_1p0192050451318417 * k + _sage_const_16p10253135200765 + repeat)
def error_fcn(t): r""" The complementary error function `\frac{2}{\sqrt{\pi}}\int_t^\infty e^{-x^2} dx` (t belongs to RR). This function is currently always evaluated immediately. EXAMPLES:: sage: error_fcn(6) 2.15197367124989e-17 sage: error_fcn(RealField(100)(1/2)) 0.47950012218695346231725334611 Note this is literally equal to `1 - erf(t)`:: sage: 1 - error_fcn(0.5) 0.520499877813047 sage: erf(0.5) 0.520499877813047 """ try: return t.erfc() except AttributeError: try: return RR(t).erfc() except Exception: raise NotImplementedError
def approximate(self, x, parent=None): r""" Approximate using de Bruijn's formula .. MATH:: \rho(x) \sim \frac{exp(-x \xi + Ei(\xi))}{\sqrt{2\pi x}\xi} which is asymptotically equal to Dickman's function, and is much faster to compute. REFERENCES: - N. De Bruijn, "The Asymptotic behavior of a function occurring in the theory of primes." J. Indian Math Soc. v 15. (1951) EXAMPLES:: sage: dickman_rho.approximate(10) 2.41739196365564e-11 sage: dickman_rho(10) 2.77017183772596e-11 sage: dickman_rho.approximate(1000) 4.32938809066403e-3464 """ log, exp, sqrt, pi = math.log, math.exp, math.sqrt, math.pi x = float(x) xi = log(x) y = (exp(xi) - 1.0) / xi - x while abs(y) > 1e-12: dydxi = (exp(xi) * (xi - 1.0) + 1.0) / (xi * xi) xi -= y / dydxi y = (exp(xi) - 1.0) / xi - x return (-x * xi + RR(xi).eint()).exp() / (sqrt(2 * pi * x) * xi)
def first_prime_in_class(c, norm_bound=1000): Cl = c.parent() # the class group K = Cl.number_field() for P in K.primes_of_bounded_norm_iter(RR(norm_bound)): if Cl(P)==c: return P raise RuntimeError("No prime of norm less than %s found in class %s" % (norm_bound, c))
def local_coordinates_at_infinity(self, prec=20, name='t'): """ For the genus `g` hyperelliptic curve `y^2 = f(x)`, return `(x(t), y(t))` such that `(y(t))^2 = f(x(t))`, where `t = x^g/y` is the local parameter at infinity INPUT: - ``prec`` -- desired precision of the local coordinates - ``name`` -- generator of the power series ring (default: ``t``) OUTPUT: `(x(t),y(t))` such that `y(t)^2 = f(x(t))` and `t = x^g/y` is the local parameter at infinity EXAMPLES:: sage: R.<x> = QQ['x'] sage: H = HyperellipticCurve(x^5-5*x^2+1) sage: x,y = H.local_coordinates_at_infinity(10) sage: x t^-2 + 5*t^4 - t^8 - 50*t^10 + O(t^12) sage: y t^-5 + 10*t - 2*t^5 - 75*t^7 + 50*t^11 + O(t^12) :: sage: R.<x> = QQ['x'] sage: H = HyperellipticCurve(x^3-x+1) sage: x,y = H.local_coordinates_at_infinity(10) sage: x t^-2 + t^2 - t^4 - t^6 + 3*t^8 + O(t^12) sage: y t^-3 + t - t^3 - t^5 + 3*t^7 - 10*t^11 + O(t^12) Note: if even degree model, just returns local coordinate above one point AUTHOR: - Jennifer Balakrishnan (2007-12) """ g = self.genus() pol = self.hyperelliptic_polynomials()[0] K = LaurentSeriesRing(self.base_ring(), name) t = K.gen() K.set_default_prec(prec + 2) L = PolynomialRing(K, 'x') x = L.gen() i = 0 w = (x**g / t)**2 - pol wprime = w.derivative(x) if pol.degree() == 2 * g + 1: x = t**-2 else: x = t**-1 for i in range((RR(log(prec + 2) / log(2))).ceil()): x = x - w(x) / wprime(x) y = x**g / t return x + O(t**(prec + 2)), y + O(t**(prec + 2))
def circle_plot(self): pts = [] pi = RR.pi() for angle in self.angles: angle = RR(angle) * pi c = angle.cos() s = angle.sin() if abs(s) < 0.00000001: pts.append((c, s)) else: pts.extend([(c, s), (c, -s)]) P = circle((0, 0), 1, color="black", thickness=2.5) P[0].set_zorder(-1) P += points(pts, size=300, rgbcolor="darkblue") P.axes(False) P.set_aspect_ratio(1) return encode_plot(P, pad=0, pad_inches=None, transparent=True, axes_pad=0.04)
def icosidodecahedron(self, exact=True): """ Return the Icosidodecahedron The Icosidodecahedron is a polyhedron with twenty triangular faces and twelve pentagonal faces. For more information see the :wikipedia:`Icosidodecahedron`. INPUT: - ``exact`` -- (boolean, default ``True``) If ``False`` use an approximate ring for the coordinates. EXAMPLES:: sage: gr = polytopes.icosidodecahedron() sage: gr.f_vector() (1, 30, 60, 32, 1) TESTS:: sage: polytopes.icosidodecahedron(exact=False) A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 30 vertices """ from sage.rings.number_field.number_field import QuadraticField from itertools import product K = QuadraticField(5, 'sqrt5') one = K.one() phi = (one + K.gen()) / 2 gens = [((-1)**a * one / 2, (-1)**b * phi / 2, (-1)**c * (one + phi) / 2) for a, b, c in product([0, 1], repeat=3)] gens.extend([(0, 0, phi), (0, 0, -phi)]) verts = [] for p in AlternatingGroup(3): verts.extend(p(x) for x in gens) if exact: return Polyhedron(vertices=verts, base_ring=K) else: verts = [(RR(x), RR(y), RR(z)) for x, y, z in verts] return Polyhedron(vertices=verts)
def random_point(self, **kwargs): r""" Return a random point in the upper half plane. The points are uniformly distributed over the rectangle `[-10, 10] \times [0, 10i]`. EXAMPLES:: sage: p = HyperbolicPlane().UHP().random_point().coordinates() sage: bool((p.imag()) > 0) True """ # TODO: use **kwargs to allow these to be set real_min = -10 real_max = 10 imag_min = 0 imag_max = 10 p = RR.random_element(min=real_min, max=real_max) \ + I * RR.random_element(min=imag_min, max=imag_max) return self.get_point(p)
def _evalf_(self, x, **kwargs): """ EXAMPLES:: sage: airy_bi_prime(0.0) 0.448288357353826 We can use several methods for numerical evaluation:: sage: airy_bi_prime(4).n(algorithm='mpmath') 161.926683504613 sage: airy_bi_prime(4).n(algorithm='mpmath', prec=100) 161.92668350461340184309492429 sage: airy_bi_prime(4).n(algorithm='scipy') # rel tol 1e-10 161.92668350461398 sage: airy_bi_prime(I).n(algorithm='scipy') # rel tol 1e-10 0.135026646710819 - 0.1288373867812549*I TESTS:: sage: parent(airy_bi_prime(3).n(algorithm='scipy')) Real Field with 53 bits of precision sage: airy_bi_prime(3).n(algorithm='scipy', prec=200) Traceback (most recent call last): ... NotImplementedError: airy_bi_prime not implemented for precision > 53 """ algorithm = kwargs.get('algorithm', 'mpmath') or 'mpmath' parent = kwargs.get('parent', None) if algorithm == 'scipy': if hasattr(parent, 'prec') and parent.prec() > 53: raise NotImplementedError( "%s not implemented for precision > 53" % self.name()) from sage.rings.all import RR, CC from sage.functions.other import real, imag from scipy.special import airy as airy if x in RR: y = airy(real(x))[3] if parent is None: return RR(y) else: y = airy(complex(real(x), imag(x)))[3] if parent is None: return CC(y) return parent(y) elif algorithm == 'mpmath': import mpmath from sage.libs.mpmath import utils as mpmath_utils return mpmath_utils.call(mpmath.airybi, x, derivative=1, parent=parent) else: raise ValueError("unknown algorithm '%s'" % algorithm)
def local_coordinates_at_nonweierstrass(self, P, prec=20, name='t'): """ For a non-Weierstrass point `P = (a,b)` on the hyperelliptic curve `y^2 = f(x)`, return `(x(t), y(t))` such that `(y(t))^2 = f(x(t))`, where `t = x - a` is the local parameter. INPUT: - ``P = (a, b)`` -- a non-Weierstrass point on self - ``prec`` -- desired precision of the local coordinates - ``name`` -- gen of the power series ring (default: ``t``) OUTPUT: `(x(t),y(t))` such that `y(t)^2 = f(x(t))` and `t = x - a` is the local parameter at `P` EXAMPLES:: sage: R.<x> = QQ['x'] sage: H = HyperellipticCurve(x^5-23*x^3+18*x^2+40*x) sage: P = H(1,6) sage: x,y = H.local_coordinates_at_nonweierstrass(P,prec=5) sage: x 1 + t + O(t^5) sage: y 6 + t - 7/2*t^2 - 1/2*t^3 - 25/48*t^4 + O(t^5) sage: Q = H(-2,12) sage: x,y = H.local_coordinates_at_nonweierstrass(Q,prec=5) sage: x -2 + t + O(t^5) sage: y 12 - 19/2*t - 19/32*t^2 + 61/256*t^3 - 5965/24576*t^4 + O(t^5) AUTHOR: - Jennifer Balakrishnan (2007-12) """ d = P[1] if d == 0: raise TypeError( "P = %s is a Weierstrass point. Use local_coordinates_at_weierstrass instead!" % P) pol = self.hyperelliptic_polynomials()[0] L = PowerSeriesRing(self.base_ring(), name) t = L.gen() L.set_default_prec(prec) K = PowerSeriesRing(L, 'x') pol = K(pol) x = K.gen() b = P[0] f = pol(t + b) for i in range((RR(log(prec) / log(2))).ceil()): d = (d + f / d) / 2 return t + b + O(t**(prec)), d + O(t**(prec))
def bessel_Y(nu, z, algorithm="maxima", prec=53): r""" Implements the "Y-Bessel function", or "Bessel function of the 2nd kind", with index (or "order") nu and argument z. .. note:: Currently only prec=53 is supported. Defn:: cos(pi n)*bessel_J(nu, z) - bessel_J(-nu, z) ------------------------------------------------- sin(nu*pi) if nu is not an integer and by taking a limit otherwise. Sometimes bessel_Y(n,z) is denoted Y_n(z) in the literature. This is computed using Maxima by default. EXAMPLES:: sage: bessel_Y(2,1.1,"scipy") -1.4314714939... sage: bessel_Y(2,1.1) -1.4314714939590... sage: bessel_Y(3.001,2.1) -1.0299574976424... TESTS:: sage: bessel_Y(2,1.1, algorithm="pari") Traceback (most recent call last): ... NotImplementedError: The Y-Bessel function is only implemented for the maxima and scipy algorithms """ if algorithm == "scipy": if prec != 53: raise ValueError, "for the scipy algorithm the precision must be 53" import scipy.special ans = str(scipy.special.yv(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 RR(maxima.eval("bessel_y(%s,%s)" % (float(nu), float(z)))) elif algorithm == "pari": raise NotImplementedError, "The Y-Bessel function is only implemented for the maxima and scipy algorithms" else: raise ValueError, "unknown algorithm '%s'" % algorithm
def _evalf_(self, x, **kwargs): """ EXAMPLES:: sage: airy_ai_prime(0.0) -0.258819403792807 We can use several methods for numerical evaluation:: sage: airy_ai_prime(4).n(algorithm='mpmath') -0.00195864095020418 sage: airy_ai_prime(4).n(algorithm='mpmath', prec=100) -0.0019586409502041789001381409184 sage: airy_ai_prime(4).n(algorithm='scipy') # rel tol 1e-10 -0.00195864095020418 sage: airy_ai_prime(I).n(algorithm='scipy') # rel tol 1e-10 -0.43249265984180707 + 0.09804785622924324*I TESTS:: sage: parent(airy_ai_prime(3).n(algorithm='scipy')) Real Field with 53 bits of precision sage: airy_ai_prime(3).n(algorithm='scipy', prec=200) Traceback (most recent call last): ... NotImplementedError: airy_ai_prime not implemented for precision > 53 """ algorithm = kwargs.get('algorithm', 'mpmath') or 'mpmath' parent = kwargs.get('parent', None) if algorithm == 'scipy': if hasattr(parent, 'prec') and parent.prec() > 53: raise NotImplementedError( "%s not implemented for precision > 53" % self.name()) from sage.rings.all import RR, CC from sage.functions.other import real, imag from scipy.special import airy as airy if x in RR: y = airy(real(x))[1] if parent is None: return RR(y) else: y = airy(complex(real(x), imag(x)))[1] if parent is None: return CC(y) return parent(y) elif algorithm == 'mpmath': import mpmath from sage.libs.mpmath import utils as mpmath_utils return mpmath_utils.call(mpmath.airyai, x, derivative=1, parent=parent) else: raise ValueError("unknown algorithm '%s'" % algorithm)
def _evalf_(self, x, **kwargs): """ EXAMPLES:: sage: from sage.functions.airy import airy_ai_simple sage: airy_ai_simple(0.0) 0.355028053887817 sage: airy_ai_simple(1.0 * I) 0.331493305432141 - 0.317449858968444*I We can use several methods for numerical evaluation:: sage: airy_ai_simple(3).n(algorithm='mpmath') 0.00659113935746072 sage: airy_ai_simple(3).n(algorithm='mpmath', prec=100) 0.0065911393574607191442574484080 sage: airy_ai_simple(3).n(algorithm='scipy') # rel tol 1e-10 0.006591139357460719 sage: airy_ai_simple(I).n(algorithm='scipy') # rel tol 1e-10 0.33149330543214117 - 0.3174498589684438*I TESTS:: sage: parent(airy_ai_simple(3).n(algorithm='scipy')) Real Field with 53 bits of precision sage: airy_ai_simple(3).n(algorithm='scipy', prec=200) Traceback (most recent call last): ... NotImplementedError: airy_ai not implemented for precision > 53 """ algorithm = kwargs.get('algorithm', 'mpmath') or 'mpmath' parent = kwargs.get('parent') if algorithm == 'scipy': if hasattr(parent, 'prec') and parent.prec() > 53: raise NotImplementedError( "%s not implemented for precision > 53" % self.name()) from sage.rings.all import RR, CC from sage.functions.other import real, imag from scipy.special import airy as airy if x in RR: y = airy(real(x))[0] if parent is None: return RR(y) else: y = airy(complex(real(x), imag(x)))[0] if parent is None: return CC(y) return parent(y) elif algorithm == 'mpmath': import mpmath from sage.libs.mpmath import utils as mpmath_utils return mpmath_utils.call(mpmath.airyai, x, parent=parent) else: raise ValueError("unknown algorithm '%s'" % algorithm)
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 _evalf_(self, x, **kwargs): """ EXAMPLES:: sage: from sage.functions.airy import airy_bi_simple sage: airy_bi_simple(0.0) 0.614926627446001 sage: airy_bi_simple(1.0 * I) 0.648858208330395 + 0.344958634768048*I We can use several methods for numerical evaluation:: sage: airy_bi_simple(3).n(algorithm='mpmath') 14.0373289637302 sage: airy_bi_simple(3).n(algorithm='mpmath', prec=100) 14.037328963730232031740267314 sage: airy_bi_simple(3).n(algorithm='scipy') # rel tol 1e-10 14.037328963730136 sage: airy_bi_simple(I).n(algorithm='scipy') # rel tol 1e-10 0.648858208330395 + 0.34495863476804844*I TESTS:: sage: parent(airy_bi_simple(3).n(algorithm='scipy')) Real Field with 53 bits of precision sage: airy_bi_simple(3).n(algorithm='scipy', prec=200) Traceback (most recent call last): ... NotImplementedError: airy_bi not implemented for precision > 53 """ algorithm = kwargs.get('algorithm', 'mpmath') or 'mpmath' parent = kwargs.get('parent', None) if algorithm == 'scipy': if hasattr(parent, 'prec') and parent.prec() > 53: raise NotImplementedError( "%s not implemented for precision > 53" % self.name()) from sage.rings.all import RR, CC from sage.functions.other import real, imag from scipy.special import airy as airy if x in RR: y = airy(real(x))[2] if parent is None: return RR(y) else: y = airy(complex(real(x), imag(x)))[2] if parent is None: return CC(y) return parent(y) elif algorithm == 'mpmath': import mpmath from sage.libs.mpmath import utils as mpmath_utils return mpmath_utils.call(mpmath.airybi, x, parent=parent) else: raise ValueError("unknown algorithm '%s'" % algorithm)
def newton_sqrt(self, f, x0, prec): r""" Takes the square root of the power series `f` by Newton's method NOTE: this function should eventually be moved to `p`-adic power series ring INPUT: - ``f`` -- power series with coefficients in `\QQ_p` or an extension - ``x0`` -- seeds the Newton iteration - ``prec`` -- precision OUTPUT: the square root of `f` EXAMPLES:: sage: R.<x> = QQ['x'] sage: H = HyperellipticCurve(x^5-23*x^3+18*x^2+40*x) sage: Q = H(0,0) sage: u,v = H.local_coord(Q,prec=100) sage: K = Qp(11,5) sage: HK = H.change_ring(K) sage: L.<a> = K.extension(x^20-11) sage: HL = H.change_ring(L) sage: S = HL(u(a),v(a)) sage: f = H.hyperelliptic_polynomials()[0] sage: y = HK.newton_sqrt( f(u(a)^11), a^11,5) sage: y^2 - f(u(a)^11) O(a^122) AUTHOR: - Jennifer Balakrishnan """ z = x0 loop_prec = (log(RR(prec)) / log(RR(2))).ceil() for i in range(loop_prec): z = (z + f / z) / 2 return z
def show(self, boundary=True, **options): r""" Plot ``self``. EXAMPLES: First some lines:: sage: PD = HyperbolicPlane().PD() sage: PD.get_geodesic(0, 1).show() Graphics object consisting of 2 graphics primitives sage: PD.get_geodesic(0, 0.3+0.8*I).show() Graphics object consisting of 2 graphics primitives Then some generic geodesics:: sage: PD.get_geodesic(-0.5, 0.3+0.4*I).show() Graphics object consisting of 2 graphics primitives sage: PD.get_geodesic(-1, exp(3*I*pi/7)).show(linestyle="dashed", color="red") Graphics object consisting of 2 graphics primitives sage: PD.get_geodesic(exp(2*I*pi/11), exp(1*I*pi/11)).show(thickness=6, color="orange") Graphics object consisting of 2 graphics primitives """ opts = {'axes': False, 'aspect_ratio': 1} opts.update(self.graphics_options()) opts.update(options) end_1, end_2 = [CC(k.coordinates()) for k in self.endpoints()] bd_1, bd_2 = [CC(k.coordinates()) for k in self.ideal_endpoints()] # Check to see if it's a line if abs(bd_1 + bd_2) < EPSILON: pic = line([end_1, end_2], **opts) else: # If we are here, we know it's not a line # So we compute the center and radius of the circle invdet = RR.one() / (real(bd_1)*imag(bd_2) - real(bd_2)*imag(bd_1)) centerx = (imag(bd_2) - imag(bd_1)) * invdet centery = (real(bd_1) - real(bd_2)) * invdet center = centerx + I * centery radius = RR(abs(bd_1 - center)) # Now we calculate the angles for the arc theta1 = CC(end_1 - center).arg() theta2 = CC(end_2 - center).arg() theta1, theta2 = sorted([theta1, theta2]) # Make sure the sector is inside the disk if theta2 - theta1 > pi: theta1 += 2 * pi pic = arc((centerx, centery), radius, sector=(theta1, theta2), **opts) if boundary: pic += self._model.get_background_graphic() return pic
def regular_polygon(self, n, base_ring=QQ): """ Return a regular polygon with `n` vertices. Over the rational field the vertices may not be exact. INPUT: - ``n`` -- a positive integer, the number of vertices. - ``base_ring`` -- a ring in which the coordinates will lie. EXAMPLES:: sage: octagon = polytopes.regular_polygon(8) sage: len(octagon.vertices()) 8 sage: polytopes.regular_polygon(3).vertices() (A vertex at (-125283617/144665060, -500399958596723/1000799917193445), A vertex at (0, 1), A vertex at (94875313/109552575, -1000799917193444/2001599834386889)) sage: polytopes.regular_polygon(3, base_ring=RealField(100)).vertices() (A vertex at (0.00000000000000000000000000000, 1.0000000000000000000000000000), A vertex at (0.86602540378443864676372317075, -0.50000000000000000000000000000), A vertex at (-0.86602540378443864676372317076, -0.50000000000000000000000000000)) sage: polytopes.regular_polygon(3, base_ring=RealField(10)).vertices() (A vertex at (0.00, 1.0), A vertex at (0.87, -0.50), A vertex at (-0.86, -0.50)) """ try: omega = 2*base_ring.pi()/n except AttributeError: omega = 2*RR.pi()/n verts = [] for i in range(n): t = omega*i verts.append([base_ring(t.sin()), base_ring(t.cos())]) return Polyhedron(vertices=verts, base_ring=base_ring)
def angle(u, v, assume_rational=False): r""" Return the angle between the vectors ``u`` and ``v`` divided by `2 \pi`. INPUT: - ``u``, ``v`` - vectors - ``assume_rational`` - whether we assume that the angle is a multiple rational of ``pi``. By default it is ``False`` but if it is known in advance that the result is rational then setting it to ``True`` might be much faster. EXAMPLES:: sage: from flatsurf.geometry.matrix_2x2 import angle As the implementation is dirty, we at least check that it works for all denominator up to 20:: sage: u = vector((AA(1),AA(0))) sage: for n in xsrange(1,20): # long time (10 sec) ....: for k in xsrange(1,n): ....: v = vector((AA(cos(2*k*pi/n)), AA(sin(2*k*pi/n)))) ....: assert angle(u,v) == k/n And we test up to 50 when setting ``assume_rational`` to ``True``:: sage: for n in xsrange(1,20): # long time ....: for k in xsrange(1,n): ....: v = vector((AA(cos(2*k*pi/n)), AA(sin(2*k*pi/n)))) ....: assert angle(u,v,assume_rational=True) == k/n If the angle is not rational, then the method returns an element in the real lazy field:: sage: v = vector((AA(sqrt(2)), AA(sqrt(3)))) sage: a = angle(u,v) sage: a 0.1410235542122437? sage: exp(2*pi.n()*CC(0,1)*a.n()) 0.632455532033676 + 0.774596669241483*I sage: v / v.norm() (0.6324555320336758?, 0.774596669241484?) """ if not assume_rational: sqnorm_u = u[0] * u[0] + u[1] * u[1] sqnorm_v = v[0] * v[0] + v[1] * v[1] if sqnorm_u != sqnorm_v: uu = u.change_ring(AA) vv = (AA(sqnorm_u) / AA(sqnorm_v)).sqrt() * v.change_ring(AA) else: uu = u vv= v cos_uv = (uu[0]*vv[0] + uu[1]*vv[1]) / sqnorm_u sin_uv = (uu[0]*vv[1] - uu[1]*vv[0]) / sqnorm_u is_rational = is_cosine_sine_of_rational(cos_uv, sin_uv) else: is_rational = True if is_rational: # fast and dirty way using floating point approximation # (see below for a slow but exact method) from math import acos,asin,sqrt u0 = float(u[0]); u1 = float(u[1]) v0 = float(v[0]); v1 = float(v[1]) cos_uv = (u0*v0 + u1*v1) / sqrt((u0*u0 + u1*u1)*(v0*v0 + v1*v1)) angle = acos(float(cos_uv)) / (2*pi_float) # rat number between 0 and 1/2 angle_rat = RR(angle).nearby_rational(0.00000001) if angle_rat.denominator() > 100: raise NotImplementedError("the numerical method used is not smart enough!") if u0*v1 - u1*v0 < 0: return 1 - angle_rat return angle_rat else: from sage.functions.trig import acos from sage.rings.real_lazy import RLF from sage.symbolic.constants import pi if sin_uv > 0: return acos(RLF(cos_uv)) / RLF(2*pi) else: return -acos(RLF(cos_uv)) / RLF(2*pi)