def _hyperbolic_arc(self, z0, z3, first=False): """ Function to construct Bezier path as an approximation to the hyperbolic arc between the complex numbers z0 and z3 in the hyperbolic plane. """ if (z0 - z3).real() == 0: self.path.append([(z0.real(), z0.imag()), (z3.real(), z3.imag())]) return z0, z3 = (CC(z0), CC(z3)) if z0.imag() == 0 and z3.imag() == 0: p = (z0.real() + z3.real()) / 2 r = abs(z0 - p) zm = CC(p, r) self._hyperbolic_arc(z0, zm, first) self._hyperbolic_arc(zm, z3) return else: p = (abs(z0) * abs(z0) - abs(z3) * abs(z3)) / (z0 - z3).real() / 2 r = abs(z0 - p) zm = ((z0 + z3) / 2 - p) / abs((z0 + z3) / 2 - p) * r + p t = (8 * zm - 4 * (z0 + z3)).imag() / 3 / (z3 - z0).real() z1 = z0 + t * CC(z0.imag(), (p - z0.real())) z2 = z3 - t * CC(z3.imag(), (p - z3.real())) if first: self.path.append([(z0.real(), z0.imag()), (z1.real(), z1.imag()), (z2.real(), z2.imag()), (z3.real(), z3.imag())]) first = False else: self.path.append([(z1.real(), z1.imag()), (z2.real(), z2.imag()), (z3.real(), z3.imag())])
def braid_in_segment(f, x0, x1): """ Return the braid formed by the `y` roots of ``f`` when `x` moves from ``x0`` to ``x1``. INPUT: - ``f`` -- a polynomial in two variables - ``x0`` -- a complex number - ``x1`` -- a complex number OUTPUT: A braid. EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import braid_in_segment # optional - sirocco sage: R.<x,y> = QQ[] sage: f = x^2 + y^3 sage: x0 = CC(1,0) sage: x1 = CC(1, 0.5) sage: braid_in_segment(f, x0, x1) # optional - sirocco s1 """ CC = ComplexField(64) (x, y) = f.variables() I = QQbar.gen() X0 = QQ(x0.real()) + I*QQ(x0.imag()) X1 = QQ(x1.real()) + I*QQ(x1.imag()) F0 = QQbar[y](f(X0, y)) y0s = F0.roots(multiplicities=False) strands = [followstrand(f, x0, x1, CC(a)) for a in y0s] complexstrands = [[(a[0], CC(a[1], a[2])) for a in b] for b in strands] centralbraid = braid_from_piecewise(complexstrands) initialstrands = [] y0aps = [c[0][1] for c in complexstrands] used = [] for y0ap in y0aps: distances = [((y0ap - y0).norm(), y0) for y0 in y0s] y0 = sorted(distances)[0][1] if y0 in used: raise ValueError("different roots are too close") used.append(y0) initialstrands.append([(0, CC(y0)), (1, y0ap)]) initialbraid = braid_from_piecewise(initialstrands) F1 = QQbar[y](f(X1,y)) y1s = F1.roots(multiplicities=False) finalstrands = [] y1aps = [c[-1][1] for c in complexstrands] used = [] for y1ap in y1aps: distances = [((y1ap - y1).norm(), y1) for y1 in y1s] y1 = sorted(distances)[0][1] if y1 in used: raise ValueError("different roots are too close") used.append(y1) finalstrands.append([(0, y1ap), (1, CC(y1))]) finallbraid = braid_from_piecewise(finalstrands) return initialbraid * centralbraid * finallbraid
def __init__(self, sides, i_angle, center, options): """ Initialize HyperbolicRegularPolygon. EXAMPLES:: sage: from sage.plot.hyperbolic_regular_polygon import HyperbolicRegularPolygon sage: print(HyperbolicRegularPolygon(5,pi/2,I, {})) Hyperbolic regular polygon (sides=5, i_angle=1/2*pi, center=1.00000000000000*I) """ self.center = CC(center) if self.center.imag() <= 0 : raise ValueError("center: %s is not a valid point in the upper half plane model of the hyperbolic plane"%(self.center)) if sides < 3 : raise ValueError("degenerated polygons (sides<=2) are not supported") if i_angle <=0 or i_angle >= pi: raise ValueError("interior angle %s must be in (0, pi) interval"%(i_angle)) if pi*(sides-2) - sides*i_angle <= 0 : raise ValueError("there exists no hyperbolic regular compact polygon, for sides=%s the interior angle must be less than %s"%(sides, pi * (sides-2) / sides)) self.sides = sides self.i_angle = i_angle beta = 2 * pi / self.sides # compute the rotation angle to be used ahead alpha = self.i_angle / Integer(2) I = CC(0, 1) # compute using cosine theorem the radius of the circumscribed circle # using the triangle formed by the radius and the three known angles r = arccosh(cot(alpha) * (1 + cos(beta)) / sin(beta)) # The first point will be always on the imaginary axis limited # to 8 digits for efficiency in the subsequent calculations. z_0 = [I*(e**r).n(digits=8)] # Compute the dilation isometry used to move the center # from I to the imaginary part of the given center. scale = self.center.imag() # Compute the parabolic isometry to move the center to the # real part of the given center. h_disp = self.center.real() d_z_k = [z_0[0]*scale + h_disp] #d_k has the points for the polygon in the given center z_k = z_0 #z_k has the Re(z)>0 vertices for the I centered polygon r_z_k = [] #r_z_k has the Re(z)<0 vertices if is_odd(self.sides): vert = (self.sides - 1) / 2 else: vert = self.sides / 2 - 1 for k in range(0, vert): # Compute with 8 digits to accelerate calculations new_z_k = self._i_rotation(z_k[-1], beta).n(digits=8) z_k = z_k + [new_z_k] d_z_k = d_z_k + [new_z_k * scale + h_disp] r_z_k=[-(new_z_k).conjugate() * scale + h_disp] + r_z_k if is_odd(self.sides): HyperbolicPolygon.__init__(self, d_z_k + r_z_k, options) else: z_opo = [I * (e**(-r)).n(digits=8) * scale + h_disp] HyperbolicPolygon.__init__(self, d_z_k + z_opo + r_z_k, options)
def __init__(self, A, B, options): A, B = (CC(A), CC(B)) if A.imag() < 0: raise ValueError("%s is not a valid point in the UHP model" % (A)) if B.imag() < 0: raise ValueError("%s is not a valid point in the UHP model" % (B)) self.path = [] self._hyperbolic_arc(A, B, True) BezierPath.__init__(self, self.path, options) self.A, self.B = (A, B)
def segments(points): """ Return the bounded segments of the Voronoi diagram of the given points. INPUT: - ``points`` -- a list of complex points OUTPUT: A list of pairs ``(p1, p2)``, where ``p1`` and ``p2`` are the endpoints of the segments in the Voronoi diagram. EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import discrim, segments # optional - sirocco sage: R.<x,y> = QQ[] sage: f = y^3 + x^3 - 1 sage: disc = discrim(f) # optional - sirocco sage: segments(disc) # optional - sirocco # abs tol 1e-15 [(-2.84740787203333 - 2.84740787203333*I, -2.14285714285714 + 1.11022302462516e-16*I), (-2.84740787203333 + 2.84740787203333*I, -2.14285714285714 + 1.11022302462516e-16*I), (2.50000000000000 + 2.50000000000000*I, 1.26513881334184 + 2.19128470333546*I), (2.50000000000000 + 2.50000000000000*I, 2.50000000000000 - 2.50000000000000*I), (1.26513881334184 + 2.19128470333546*I, 0.000000000000000), (0.000000000000000, 1.26513881334184 - 2.19128470333546*I), (2.50000000000000 - 2.50000000000000*I, 1.26513881334184 - 2.19128470333546*I), (-2.84740787203333 + 2.84740787203333*I, 1.26513881334184 + 2.19128470333546*I), (-2.14285714285714 + 1.11022302462516e-16*I, 0.000000000000000), (-2.84740787203333 - 2.84740787203333*I, 1.26513881334184 - 2.19128470333546*I)] """ from numpy import array, vstack from scipy.spatial import Voronoi discpoints = array([(CC(a).real(), CC(a).imag()) for a in points]) added_points = 3 * abs(discpoints).max() + 1.0 configuration = vstack([ discpoints, array([[added_points, 0], [-added_points, 0], [0, added_points], [0, -added_points]]) ]) V = Voronoi(configuration) res = [] for rv in V.ridge_vertices: if not -1 in rv: p1 = CC(list(V.vertices[rv[0]])) p2 = CC(list(V.vertices[rv[1]])) res.append((p1, p2)) return res
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 segments(points): """ Return the bounded segments of the Voronoi diagram of the given points. INPUT: - A list of complex points. OUTPUT: A list of pairs (p1, p2) where p1 and p2 are the endpoints of the segments in the Voronoi diagram EXAMPLES:: sage: R.<x,y> = QQ[] sage: f = y^3 + x^3 -1 sage: disc = discrim(f) sage: segments(disc) [(-2.84740787203333 - 2.84740787203333*I, -2.14285714285714 + 1.11022302462516e-16*I), (-2.84740787203333 + 2.84740787203333*I, -2.14285714285714 + 1.11022302462516e-16*I), (2.50000000000000 + 2.50000000000000*I, 1.26513881334184 + 2.19128470333546*I), (2.50000000000000 + 2.50000000000000*I, 2.50000000000000 - 2.50000000000000*I), (1.26513881334184 + 2.19128470333546*I, 0.000000000000000), (0.000000000000000, 1.26513881334184 - 2.19128470333546*I), (2.50000000000000 - 2.50000000000000*I, 1.26513881334184 - 2.19128470333546*I), (-2.84740787203333 + 2.84740787203333*I, 1.26513881334184 + 2.19128470333546*I), (-2.14285714285714 + 1.11022302462516e-16*I, 0.000000000000000), (-2.84740787203333 - 2.84740787203333*I, 1.26513881334184 - 2.19128470333546*I)] """ discpoints = array([(CC(a).real(), CC(a).imag()) for a in points]) added_points = 3 * abs(discpoints).max() + 1.0 configuration = vstack([ discpoints, array([[added_points, 0], [-added_points, 0], [0, added_points], [0, -added_points]]) ]) V = Voronoi(configuration) res = [] for rv in V.ridge_vertices: if not -1 in rv: p1 = CC(list(V.vertices[rv[0]])) p2 = CC(list(V.vertices[rv[1]])) res.append((p1, p2)) return res
def point_in_model(self, p): r""" Check whether a complex number lies in the open upper half plane. EXAMPLES:: sage: UHP = HyperbolicPlane().UHP() sage: UHP.point_in_model(1 + I) True sage: UHP.point_in_model(infinity) False sage: UHP.point_in_model(CC(infinity)) False sage: UHP.point_in_model(RR(infinity)) False sage: UHP.point_in_model(1) False sage: UHP.point_in_model(12) False sage: UHP.point_in_model(1 - I) False sage: UHP.point_in_model(-2*I) False sage: UHP.point_in_model(I) True sage: UHP.point_in_model(0) # Not interior point False """ if isinstance(p, HyperbolicPoint): return p.is_boundary() return bool(imag(CC(p)) > 0)
def boundary_point_in_model(self, p): r""" Check whether a complex number is a real number or ``\infty``. In the ``UHP.model_name_name``, this is the ideal boundary of hyperbolic space. EXAMPLES:: sage: UHP = HyperbolicPlane().UHP() sage: UHP.boundary_point_in_model(1 + I) False sage: UHP.boundary_point_in_model(infinity) True sage: UHP.boundary_point_in_model(CC(infinity)) True sage: UHP.boundary_point_in_model(RR(infinity)) True sage: UHP.boundary_point_in_model(1) True sage: UHP.boundary_point_in_model(12) True sage: UHP.boundary_point_in_model(1 - I) False sage: UHP.boundary_point_in_model(-2*I) False sage: UHP.boundary_point_in_model(0) True sage: UHP.boundary_point_in_model(I) False """ if isinstance(p, HyperbolicPoint): return p.is_boundary() im = abs(imag(CC(p)).n()) return (im < EPSILON) or bool(p == infinity)
def get_classified_solution_dicts(output_file_contents, input_ring, get_failures=True): """ Returns a dictionary of lists of dictionaries of variable:value (key:value) pairs. Only used internally; see the classified_solution_dict function in the PHC_Object class definition for details. INPUT: - output_file_contents -- phc solution output as a string - input_ring -- a PolynomialRing that variable names can be coerced into OUTPUT: - a dictionary of lists if dictionaries of solutions, classifies by type EXAMPLES:: sage: from sage.interfaces.phc import * sage: R2.<x1,x2> = PolynomialRing(QQ,2) sage: test_sys = [(x1-2)^5-x2, (x2-1)^5-1] sage: sol = phc.blackbox(test_sys, R2) # optional -- phc sage: sol_classes = get_classified_solution_dicts(sol.output_file_contents,R2) # optional -- phc sage: len(sol_classes['real']) # optional -- phc 1 """ output_list = output_file_contents.splitlines() test = 'False' solution_dicts = {} solution_types = ['complex', 'real', 'failure'] for sol_type in solution_types: solution_dicts[sol_type] = [] for solution_line in range(len(output_list) - 1, -1, -1): if output_list[solution_line].find('THE SOLUTIONS') == 0: break var_number = int(output_list[solution_line + 2].split(' ')[1]) sol_number = int(output_list[solution_line + 2].split(' ')[0]) for i in range(solution_line + 1, len(output_list)): if output_list[i].count('the solution for t') == 1: phc_type = output_list[i + var_number + 1].split(' = ')[-1] if phc_type.find('complex') != -1: phc_type = 'complex' elif phc_type.find('real') != -1: phc_type = 'real' else: phc_type = 'failure' temp_dict = {} for j in range(1, var_number + 1): rawsplit = output_list[i + j].split(': ')[1].split(' ') for extras in range(rawsplit.count('')): rawsplit.remove('') temp_var = output_list[i + j].split(': ')[0].replace(' ', '') if phc_type == 'real': temp_dict[input_ring(temp_var)] = RR(rawsplit[0]) else: temp_dict[input_ring(temp_var)] = CC( rawsplit[0], rawsplit[1]) solution_dicts[phc_type].append(temp_dict) return solution_dicts
def fft(self): """ Wraps the gsl ``FastFourierTransform.forward()`` in :mod:`~sage.gsl.fft`. If the length is a power of 2 then this automatically uses the radix2 method. If the number of sample points in the input is a power of 2 then the wrapper for the GSL function ``gsl_fft_complex_radix2_forward()`` is automatically called. Otherwise, ``gsl_fft_complex_forward()`` is used. EXAMPLES:: sage: J = range(5) sage: A = [RR(1) for i in J] sage: s = IndexedSequence(A,J) sage: t = s.fft(); t Indexed sequence: [5.00000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000] indexed by [0, 1, 2, 3, 4] """ from sage.rings.all import CC I = CC.gen() # elements must be coercible into RR J = self.index_object() ## must be = range(N) N = len(J) S = self.list() a = FastFourierTransform(N) for i in range(N): a[i] = S[i] a.forward_transform() return IndexedSequence([a[j][0]+I*a[j][1] for j in J],J)
def __init__(self, A, B, C, options): """ Initialize HyperbolicTriangle: Examples:: sage: from sage.plot.hyperbolic_triangle import HyperbolicTriangle sage: print HyperbolicTriangle(0, 1/2, I, {}) Hyperbolic triangle (0.000000000000000, 0.500000000000000, 1.00000000000000*I) """ A, B, C = (CC(A), CC(B), CC(C)) self.path = [] self._hyperbolic_arc(A, B, True) self._hyperbolic_arc(B, C) self._hyperbolic_arc(C, A) BezierPath.__init__(self, self.path, options) self.A, self.B, self.C = (A, B, C)
def show(self, boundary=True, **options): r""" Plot ``self``. EXAMPLES:: sage: UHP = HyperbolicPlane().UHP() sage: UHP.get_geodesic(0, 1).show() Graphics object consisting of 2 graphics primitives sage: UHP.get_geodesic(I, 3+4*I).show(linestyle="dashed", color="red") 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()] if (abs(real(end_1) - real(end_2)) < EPSILON) \ or CC(infinity) in [end_1, end_2]: # on same vertical line # If one of the endpoints is infinity, we replace it with a # large finite point if end_1 == CC(infinity): end_1 = (real(end_2), (imag(end_2) + 10)) end_2 = (real(end_2), imag(end_2)) elif end_2 == CC(infinity): end_2 = (real(end_1), (imag(end_1) + 10)) end_1 = (real(end_1), imag(end_1)) pic = line((end_1, end_2), **opts) if boundary: cent = min(bd_1, bd_2) bd_dict = {'bd_min': cent - 3, 'bd_max': cent + 3} bd_pic = self._model.get_background_graphic(**bd_dict) pic = bd_pic + pic return pic else: center = (bd_1 + bd_2) / 2 # Circle center radius = abs(bd_1 - bd_2) / 2 theta1 = CC(end_1 - center).arg() theta2 = CC(end_2 - center).arg() if abs(theta1 - theta2) < EPSILON: theta2 += pi pic = arc((real(center), imag(center)), radius, sector=(theta1, theta2), **opts) if boundary: # We want to draw a segment of the real line. The # computations below compute the projection of the # geodesic to the real line, and then draw a little # to the left and right of the projection. shadow_1, shadow_2 = [real(k) for k in [end_1, end_2]] midpoint = (shadow_1 + shadow_2) / 2 length = abs(shadow_1 - shadow_2) bd_dict = { 'bd_min': midpoint - length, 'bd_max': midpoint + length } bd_pic = self._model.get_background_graphic(**bd_dict) pic = bd_pic + pic return pic
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: 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 _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 _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 _element_constructor_(self, x): r""" Return the element of the algebra ``self`` corresponding to ``x``. EXAMPLES:: sage: S = SiegelModularFormsAlgebra(coeff_ring=QQ) sage: B = SiegelModularFormsAlgebra(coeff_ring=ZZ).1 sage: S(B) Igusa_6 sage: S(1/5) 1/5 sage: S(1/5).parent() is S True sage: S._element_constructor_(2.67) Traceback (most recent call last): ... TypeError: Unable to construct an element of Algebra of Siegel modular forms of degree 2 and even weights on Sp(4,Z) over Rational Field corresponding to 2.67000000000000 sage: S.base_extend(RR)._element_constructor_(2.67) 2.67000000000000 """ if isinstance(x, six.integer_types): x = ZZ(x) if isinstance(x, float): from sage.rings.all import RR x = RR(x) if isinstance(x, complex): from sage.rings.all import CC x = CC(x) if isinstance(x.parent(), SiegelModularFormsAlgebra_class): d = dict((f, self.coeff_ring()(x[f])) for f in x.coeffs()) return self.element_class(parent=self, weight=x.weight(), coeffs=d, prec=x.prec(), name=x.name()) R = self.base_ring() if R.has_coerce_map_from(x.parent()): d = {(0, 0, 0): R(x)} from sage.rings.all import infinity return self.element_class(parent=self, weight=0, coeffs=d, prec=infinity, name=str(x)) else: raise TypeError( "Unable to construct an element of {0} corresponding to {1}". format(self, x))
def get_solution_dicts(output_file_contents, input_ring, get_failures=True): """ Returns a list of dictionaries of variable:value (key:value) pairs. Only used internally; see the solution_dict function in the PHC_Object class definition for details. INPUT: - output_file_contents -- phc solution output as a string - input_ring -- a PolynomialRing that variable names can be coerced into OUTPUT: a list of dictionaries of solutions EXAMPLES:: sage: from sage.interfaces.phc import * sage: R2.<x1,x2> = PolynomialRing(QQ,2) sage: test_sys = [(x1-1)^5-x2, (x2-1)^5-1] sage: sol = phc.blackbox(test_sys, R2) # optional -- phc sage: test = get_solution_dicts(sol.output_file_contents,R2) # optional -- phc sage: str(sum([q[x1].real() for q in test]))[0:4] # optional -- phc '25.0' """ output_list = output_file_contents.splitlines() test = 'False' solution_dicts = [] for solution_line in range(len(output_list) - 1, -1, -1): if output_list[solution_line].find('THE SOLUTIONS') == 0: break try: var_number = int(output_list[solution_line + 2].split(' ')[1]) sol_number = int(output_list[solution_line + 2].split(' ')[0]) except IndexError: var_number = int(output_list[solution_line + 1].split(' ')[1]) sol_number = int(output_list[solution_line + 1].split(' ')[0]) for i in range(solution_line + 1, len(output_list)): if output_list[i].count('the solution for t') == 1: if output_list[i - 3].count('success') > 0 or get_failures == True: temp_dict = {} for j in range(1, var_number + 1): rawsplit = output_list[i + j].split(': ')[1].split(' ') for extras in range(rawsplit.count('')): rawsplit.remove('') temp_var = output_list[i + j].split(': ')[0].replace( ' ', '') temp_dict[input_ring(temp_var)] = CC( rawsplit[0], rawsplit[1]) solution_dicts.append(temp_dict) return solution_dicts
def boundary_point_in_model(self, p): r""" Check whether a complex number lies in the open unit disk. EXAMPLES:: sage: PD = HyperbolicPlane().PD() sage: PD.boundary_point_in_model(1.00) True sage: PD.boundary_point_in_model(1/2 + I/2) False sage: PD.boundary_point_in_model(1 + .2*I) False """ if isinstance(p, HyperbolicPoint): return p.is_boundary() return bool(abs(abs(CC(p)) - 1) < EPSILON)
def __init__(self, pts, options): """ Initialize HyperbolicPolygon. EXAMPLES:: sage: from sage.plot.hyperbolic_polygon import HyperbolicPolygon sage: print HyperbolicPolygon([0, 1/2, I], {}) Hyperbolic polygon (0.000000000000000, 0.500000000000000, 1.00000000000000*I) """ pts = [CC(_) for _ in pts] self.path = [] self._hyperbolic_arc(pts[0], pts[1], True) for i in range(1, len(pts) - 1): self._hyperbolic_arc(pts[i], pts[i + 1]) self._hyperbolic_arc(pts[-1], pts[0]) BezierPath.__init__(self, self.path, options) self._pts = pts
def show(self, boundary=True, **options): r""" Plot ``self``. EXAMPLES:: sage: HyperbolicPlane().PD().get_point(0).show() Graphics object consisting of 2 graphics primitives sage: HyperbolicPlane().KM().get_point((0,0)).show() Graphics object consisting of 2 graphics primitives sage: HyperbolicPlane().HM().get_point((0,0,1)).show() Graphics3d Object """ p = self.coordinates() if p == infinity: raise NotImplementedError("can't draw the point infinity") opts = {'axes': False, 'aspect_ratio': 1} opts.update(self.graphics_options()) opts.update(options) from sage.plot.point import point from sage.misc.functional import numerical_approx if self._bdry: # It is a boundary point p = numerical_approx(p) pic = point((p, 0), **opts) if boundary: bd_pic = self._model.get_background_graphic(bd_min=p - 1, bd_max=p + 1) pic = bd_pic + pic else: # It is an interior point if p in RR: p = CC(p) elif hasattr(p, 'iteritems') or hasattr(p, '__iter__'): p = [numerical_approx(k) for k in p] else: p = numerical_approx(p) pic = point(p, **opts) if boundary: bd_pic = self.parent().get_background_graphic() pic = bd_pic + pic return pic
def _evalf_(self, n, x, **kwds): """ Evaluate :class:`chebyshev_U` numerically with mpmath. EXAMPLES:: sage: chebyshev_U(5,-4+3.*I) 98280.0000000000 - 11310.0000000000*I sage: chebyshev_U(10,3).n(75) 4.661117900000000000000e7 sage: chebyshev_U._evalf_(1.5, Mod(8,9)) Traceback (most recent call last): ... TypeError: cannot evaluate chebyshev_U with parent Ring of integers modulo 9 """ try: real_parent = kwds['parent'] except KeyError: real_parent = parent(x) if not is_RealField(real_parent) and not is_ComplexField( real_parent): # parent is not a real or complex field: figure out a good parent if x in RR: x = RR(x) real_parent = RR elif x in CC: x = CC(x) real_parent = CC if not is_RealField(real_parent) and not is_ComplexField(real_parent): raise TypeError( "cannot evaluate chebyshev_U with parent {}".format( real_parent)) from sage.libs.mpmath.all import call as mpcall from sage.libs.mpmath.all import chebyu as mpchebyu return mpcall(mpchebyu, n, x, parent=real_parent)
def perron_right_eigenvector(M): r""" EXAMPLES:: sage: from slabbe.matrix_cocycle import perron_right_eigenvector sage: m = matrix(2,[-11,14,-26,29]) sage: perron_right_eigenvector(m) # tolerance 0.00001 (15.0000000000000, (0.35, 0.6499999999999999)) """ from sage.modules.free_module_element import vector from sage.rings.real_mpfr import RR from sage.rings.all import CC import numpy eig, vec = numpy.linalg.eig(M) index = abs(eig).argmax() rightv = vec.transpose()[index] if eig[index].imag == 0: eig_sage = RR(eig[index].real) vec_sage = vector(a.real for a in rightv) else: eig_sage = CC(eig[index]) vec_sage = vector(CC, rightv) return eig_sage, vec_sage/sum(vec_sage)
def contradicts(self, soln): """ Return ``True`` if this assumption is violated by the given variable assignment(s). INPUT: - ``soln`` -- Either a dictionary with variables as keys or a symbolic relation with a variable on the left hand side. EXAMPLES:: sage: from sage.symbolic.assumptions import GenericDeclaration sage: GenericDeclaration(x, 'integer').contradicts(x==4) False sage: GenericDeclaration(x, 'integer').contradicts(x==4.0) False sage: GenericDeclaration(x, 'integer').contradicts(x==4.5) True sage: GenericDeclaration(x, 'integer').contradicts(x==sqrt(17)) True sage: GenericDeclaration(x, 'noninteger').contradicts(x==sqrt(17)) False sage: GenericDeclaration(x, 'noninteger').contradicts(x==17) True sage: GenericDeclaration(x, 'even').contradicts(x==3) True sage: GenericDeclaration(x, 'complex').contradicts(x==3) False sage: GenericDeclaration(x, 'imaginary').contradicts(x==3) True sage: GenericDeclaration(x, 'imaginary').contradicts(x==I) False sage: var('y,z') (y, z) sage: GenericDeclaration(x, 'imaginary').contradicts(x==y+z) False sage: GenericDeclaration(x, 'rational').contradicts(y==pi) False sage: GenericDeclaration(x, 'rational').contradicts(x==pi) True sage: GenericDeclaration(x, 'irrational').contradicts(x!=pi) False sage: GenericDeclaration(x, 'rational').contradicts({x: pi, y: pi}) True sage: GenericDeclaration(x, 'rational').contradicts({z: pi, y: pi}) False """ if isinstance(soln, dict): value = soln.get(self._var) if value is None: return False elif soln.lhs() == self._var: value = soln.rhs() else: return False try: CC(value) except TypeError: return False if self._assumption == 'integer': return value not in ZZ elif self._assumption == 'noninteger': return value in ZZ elif self._assumption == 'even': return value not in ZZ or ZZ(value) % 2 != 0 elif self._assumption == 'odd': return value not in ZZ or ZZ(value) % 2 != 1 elif self._assumption == 'rational': return value not in QQ elif self._assumption == 'irrational': return value in QQ elif self._assumption == 'real': return value not in RR elif self._assumption == 'imaginary': return value not in CC or CC(value).real() != 0 elif self._assumption == 'complex': return value not in CC
def __init__(self, A, B, options): A, B = (CC(A), CC(B)) self.path = [] self._hyperbolic_arc(A, B, True) BezierPath.__init__(self, self.path, options) self.A, self.B = (A, B)
def _parse_path_file(self, input_filename, verbose=False): """ Takes a phpack output file containing path tracking information and parses it into a list of lists of dictionaries - i.e. a list of solutions paths, where each solution path is a list of dictionaries of variable and homotopy parameter values. INPUT: input_filename -- file must have path-tracking information OUTPUT: a list of lists of dictionaries, described above EXAMPLES:: sage: from sage.interfaces.phc import * sage: R2.<x,y> = PolynomialRing(QQ,2) sage: start_sys = [x^5-y^2,y^5-1] sage: sol = phc.blackbox(start_sys, R2) # optional -- phc sage: start_save = sol.save_as_start() # optional -- phc sage: end_sys = [x^5-2,y^5-x^2] # optional -- phc sage: path_track_filename = phc._path_track_file(start_save, end_sys, R2, c_skew = .001) # optional -- phc sage: sol_paths = phc._parse_path_file(path_track_filename) # optional -- phc sage: len(sol_paths) # optional -- phc 25 """ if not os.path.exists(input_filename): raise RuntimeError("The file containing output from phc (" + input_filename + ") cannot be found") fh = open(input_filename) line_idx = 0 begin = 0 count = 0 solutions_dicts = [] steps_dicts = [] # regular expressions for matching certain output types var_cnt_regex = re.compile('^ +([0-9]+)') output_regex = re.compile('^OUTPUT INFORMATION DURING') t_regex = re.compile( '(^t +: +(-{0,1}[0-9]+\.[0-9]+E[-+][0-9]+) +(-{0,1}[0-9]+\.[0-9]+E[-+][0-9]+)$)', re.IGNORECASE) sols_regex = re.compile( '(^ *(([a-z]|[0-9])+) +: +(-?[0-9]+\.[0-9]+E[-+][0-9]+) +(-?[0-9]+\.[0-9]+E[-+][0-9]+)$)', re.IGNORECASE) complete_regex = re.compile('^TIMING INFORMATION') breakfast = False a_line = fh.readline() end_test = '' while a_line: # processing.... a_line = a_line.replace("\n", '') if line_idx == 0: m = var_cnt_regex.match(a_line) if m: count = Integer(m.group(1)) if count > 0: m = output_regex.match(a_line) if m: begin = 1 if begin: m = t_regex.match(a_line) if m: # put the t-values into a dict # m.group(2) contains the real val # m.group(3) contains the imaginary val # fh_w.write( "T=> G1(" + m.group(2) + '),G2(' + m.group(3) + ")\n") # read off two lines - this should be 'm' and 'the solution for t :' a_line = fh.readline() end_test = a_line # store this to check for end of solution a_line = fh.readline() t_val = CC(m.group(2), m.group(3)) temp_dict = {} temp_dict["t"] = t_val for i in range(0, count): a_line = fh.readline() m = sols_regex.match(a_line) if m: # m.group(2) contains our var name # m.group(4) contains our real val # m.group(5) contains our imaginary val temp_dict[m.group(2)] = CC( m.group(4), m.group(5)) steps_dicts.append(temp_dict) # check if its the end of a solution if end_test.find('Length of path') != -1: if verbose: print "recording sol" if steps_dicts != []: solutions_dicts.append(steps_dicts) steps_dicts = [] m = complete_regex.match(a_line) if m: breakfast = True if breakfast: break line_idx += 1 a_line = fh.readline() fh.close() return solutions_dicts
def followstrand(f, x0, x1, y0a, prec=53): r""" Return a piecewise linear aproximation of the homotopy continuation of the root ``y0a`` from ``x0`` to ``x1``. INPUT: - ``f`` -- a polynomial in two variables - ``x0`` -- a complex value, where the homotopy starts - ``x1`` -- a complex value, where the homotopy ends - ``y0a`` -- an approximate solution of the polynomial `F(y) = f(x_0, y)` - ``prec`` -- the precision to use OUTPUT: A list of values `(t, y_{tr}, y_{ti})` such that: - ``t`` is a real number between zero and one - `f(t \cdot x_1 + (1-t) \cdot x_0, y_{tr} + I \cdot y_{ti})` is zero (or a good enough aproximation) - the piecewise linear path determined by the points has a tubular neighborhood where the actual homotopy continuation path lies, and no other root intersects it. EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import followstrand # optional - sirocco sage: R.<x,y> = QQ[] sage: f = x^2 + y^3 sage: x0 = CC(1, 0) sage: x1 = CC(1, 0.5) sage: followstrand(f, x0, x1, -1.0) # optional - sirocco # abs tol 1e-15 [(0.0, -1.0, 0.0), (0.7500000000000001, -1.015090921153253, -0.24752813818386948), (1.0, -1.026166099551513, -0.32768940253604323)] """ CIF = ComplexIntervalField(prec) CC = ComplexField(prec) G = f.change_ring(QQbar).change_ring(CIF) (x, y) = G.variables() g = G.subs({x: (1-x)*CIF(x0) + x*CIF(x1)}) coefs = [] deg = g.total_degree() for d in range(deg + 1): for i in range(d + 1): c = CIF(g.coefficient({x: d-i, y: i})) cr = c.real() ci = c.imag() coefs += list(cr.endpoints()) coefs += list(ci.endpoints()) yr = CC(y0a).real() yi = CC(y0a).imag() from sage.libs.sirocco import contpath, contpath_mp try: if prec == 53: points = contpath(deg, coefs, yr, yi) else: points = contpath_mp(deg, coefs, yr, yi, prec) return points except Exception: return followstrand(f, x0, x1, y0a, 2*prec)
def smallest_dynamical(f, dynatomic=True, start_n=1, prec=53, emb=None, algorithm='HS', check_minimal=True): r""" Determine the poly with smallest coefficients in `SL(2,\ZZ)` orbit of ``F`` Smallest is in the sense of global height. The method is the algorithm in Hutz-Stoll [HS2018]_. A binary form defining the periodic points is associated to ``f``. From this polynomial a bound on the search space can be determined. ``f`` should already be a minimal model or finding the orbit representatives may give wrong results. INPUT: - ``f`` -- a dynamical system on `P^1` - ``dyantomic`` -- boolean. whether ``F`` is the periodic points or the formal periodic points of period ``m`` for ``f`` - ``start_n`` - positive integer. the period used to start trying to create associate binary form ``F`` - ``prec``-- positive integer. precision to use in CC - ``emb`` -- embedding into CC - ``algorithm`` -- (optional) string; either ``'BM'`` for the Bruin-Molnar algorithm or ``'HS'`` for the Hutz-Stoll algorithm. If not specified, properties of the map are utilized to choose how to compute minimal orbit representatives - ``check_minimal`` -- (default: True), boolean, whether to check if this map is a minimal model OUTPUT: pair [dynamical system, matrix] EXAMPLES:: sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import smallest_dynamical sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([50*x^2 + 795*x*y + 2120*y^2, 265*x^2 + 106*y^2]) sage: smallest_dynamical(f) #long time [ Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (-480*x^2 - 1125*x*y + 1578*y^2 : 265*x^2 + 1060*x*y + 1166*y^2), <BLANKLINE> [1 2] [0 1] ] """ def insert_item(pts, item, index): # binary insertion to maintain list of points left to consider N = len(pts) if N == 0: return [item] elif N == 1: if item[index] > pts[0][index]: pts.insert(0,item) else: pts.append(item) return pts else: # binary insertion left = 1 right = N mid = (left + right) // 2 # these are ints so this is .floor() if item[index] > pts[mid][index]: # item goes into first half return insert_item(pts[:mid], item, index) + pts[mid:N] else: # item goes into second half return pts[:mid] + insert_item(pts[mid:N], item, index) def coshdelta(z): # The cosh of the hyperbolic distance from z = t+uj to j return (z.norm() + 1)/(2*z.imag()) # can't be smaller if height 0 f.normalize_coordinates() if f.global_height(prec=prec) == 0: return [f, matrix(ZZ,2,2,[1,0,0,1])] all_min = f.all_minimal_models(return_transformation=True, algorithm=algorithm, check_minimal=check_minimal) current_min = None current_size = None # search for minimum over all orbits for g,M in all_min: PS = g.domain() CR = PS.coordinate_ring() x,y = CR.gens() n = start_n # sometimes you get a problem later with 0,infty as roots if dynatomic: pts_poly = g.dynatomic_polynomial(n) else: gn = g.nth_iterate_map(n) pts_poly = y*gn[0] - x*gn[1] d = ZZ(pts_poly.degree()) max_mult = max([ex for p,ex in pts_poly.factor()]) while ((d < 3) or (max_mult >= d/2) and (n < 5)): n = n+1 if dynatomic: pts_poly = g.dynatomic_polynomial(n) else: gn = g.nth_iterate_map(n) pts_poly = y*gn[0] - x*gn[1] d = ZZ(pts_poly.degree()) max_mult = max([ex for p,ex in pts_poly.factor()]) assert(n<=4), "n > 4, failed to find usable poly" R = get_bound_dynamical(pts_poly, g, m=n, dynatomic=dynatomic, prec=prec, emb=emb) # search starts in fundamental domain G,MG = pts_poly.reduced_form(prec=prec, emb=emb, smallest_coeffs=False) red_g = f.conjugate(M*MG) if G != pts_poly: R2 = get_bound_dynamical(G, red_g, m=n, dynatomic=dynatomic, prec=prec, emb=emb) if R2 < R: # use the better bound R = R2 red_g.normalize_coordinates() if red_g.global_height(prec=prec) == 0: return [red_g, M*MG] # height if current_size is None: current_size = e**red_g.global_height(prec=prec) v0, th = covariant_z0(G, prec=prec, emb=emb) rep = 2*CC.gen(0) from math import isnan if isnan(v0.abs()): raise ValueError("invalid covariant: %s"%v0) # get orbit S = matrix(ZZ,2,2,[0,-1,1,0]) T = matrix(ZZ,2,2,[1,1,0,1]) TI = matrix(ZZ,2,2,[1,-1,0,1]) count = 0 pts = [[G, red_g, v0, rep, M*MG, coshdelta(v0), 0]] # label - 0:None, 1:S, 2:T, 3:T^(-1) if current_min is None: current_min = [G, red_g, v0, rep, M*MG, coshdelta(v0)] while pts != []: G, g, v, rep, M, D, label = pts.pop() # apply ST and keep z, Sz if D > R: break #all remaining pts are too far away # check if it is smaller. If so, we can improve the bound count += 1 new_size = e**g.global_height(prec=prec) if new_size < current_size: current_min = [G ,g, v, rep, M, coshdelta(v)] current_size = new_size if new_size == 1: # early exit return [current_min[1], current_min[4]] new_R = get_bound_dynamical(G, g, m=n, dynatomic=dynatomic, prec=prec, emb=emb) if new_R < R: R = new_R # add new points to check if label != 1 and min((rep+1).norm(), (rep-1).norm()) >= 1: # don't undo S # the 2nd condition is equivalent to |\Re(-1/rep)| <= 1/2 # this means that rep can have resulted from an inversion step in # the shift-and-invert procedure, so don't invert # do inversion z = -1/v new_pt = [G.subs({x:-y, y:x}), g.conjugate(S), z, -1/rep, M*S, coshdelta(z), 1] pts = insert_item(pts, new_pt, 5) if label != 3: # don't undo T on g # do right shift z = v-1 new_pt = [G.subs({x:x+y}), g.conjugate(TI), z, rep-1, M*TI, coshdelta(z), 2] pts = insert_item(pts, new_pt, 5) if label != 2: # don't undo TI on g # do left shift z = v+1 new_pt = [G.subs({x:x-y}), g.conjugate(T), z, rep+1, M*T, coshdelta(z), 3] pts = insert_item(pts, new_pt, 5) return [current_min[1], current_min[4]]
def smallest_dynamical(f, dynatomic=True, start_n=1, prec=53, emb=None, algorithm='HS', check_minimal=True): r""" Determine the poly with smallest coefficients in `SL(2,\ZZ)` orbit of ``F`` Smallest is in the sense of global height. The method is the algorithm in Hutz-Stoll [HS2018]_. A binary form defining the periodic points is associated to ``f``. From this polynomial a bound on the search space can be determined. ``f`` should already be a minimal model or finding the orbit representatives may give wrong results. INPUT: - ``f`` -- a dynamical system on `P^1` - ``dynatomic`` -- boolean. whether ``F`` is the periodic points or the formal periodic points of period ``m`` for ``f`` - ``start_n`` - positive integer. the period used to start trying to create associate binary form ``F`` - ``prec``-- positive integer. precision to use in CC - ``emb`` -- embedding into CC - ``algorithm`` -- (optional) string; either ``'BM'`` for the Bruin-Molnar algorithm or ``'HS'`` for the Hutz-Stoll algorithm. If not specified, properties of the map are utilized to choose how to compute minimal orbit representatives - ``check_minimal`` -- (default: True), boolean, whether to check if this map is a minimal model OUTPUT: pair [dynamical system, matrix] EXAMPLES:: sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import smallest_dynamical sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([50*x^2 + 795*x*y + 2120*y^2, 265*x^2 + 106*y^2]) sage: smallest_dynamical(f) #long time [ Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (-480*x^2 - 1125*x*y + 1578*y^2 : 265*x^2 + 1060*x*y + 1166*y^2), <BLANKLINE> [1 2] [0 1] ] """ def insert_item(pts, item, index): # binary insertion to maintain list of points left to consider N = len(pts) if N == 0: return [item] elif N == 1: if item[index] > pts[0][index]: pts.insert(0, item) else: pts.append(item) return pts else: # binary insertion left = 1 right = N mid = (left + right) // 2 # these are ints so this is .floor() if item[index] > pts[mid][index]: # item goes into first half return insert_item(pts[:mid], item, index) + pts[mid:N] else: # item goes into second half return pts[:mid] + insert_item(pts[mid:N], item, index) def coshdelta(z): # The cosh of the hyperbolic distance from z = t+uj to j return (z.norm() + 1) / (2 * z.imag()) # can't be smaller if height 0 f.normalize_coordinates() if f.global_height(prec=prec) == 0: return [f, matrix(ZZ, 2, 2, [1, 0, 0, 1])] all_min = f.all_minimal_models(return_transformation=True, algorithm=algorithm, check_minimal=check_minimal) current_min = None current_size = None # search for minimum over all orbits for g, M in all_min: PS = g.domain() CR = PS.coordinate_ring() x, y = CR.gens() n = start_n # sometimes you get a problem later with 0,infty as roots if dynatomic: pts_poly = g.dynatomic_polynomial(n) else: gn = g.nth_iterate_map(n) pts_poly = y * gn[0] - x * gn[1] d = ZZ(pts_poly.degree()) max_mult = max([ex for p, ex in pts_poly.factor()]) while ((d < 3) or (max_mult >= d / 2) and (n < 5)): n = n + 1 if dynatomic: pts_poly = g.dynatomic_polynomial(n) else: gn = g.nth_iterate_map(n) pts_poly = y * gn[0] - x * gn[1] d = ZZ(pts_poly.degree()) max_mult = max([ex for p, ex in pts_poly.factor()]) assert (n <= 4), "n > 4, failed to find usable poly" R = get_bound_dynamical(pts_poly, g, m=n, dynatomic=dynatomic, prec=prec, emb=emb) # search starts in fundamental domain G, MG = pts_poly.reduced_form(prec=prec, emb=emb, smallest_coeffs=False) red_g = f.conjugate(M * MG) if G != pts_poly: R2 = get_bound_dynamical(G, red_g, m=n, dynatomic=dynatomic, prec=prec, emb=emb) if R2 < R: # use the better bound R = R2 red_g.normalize_coordinates() if red_g.global_height(prec=prec) == 0: return [red_g, M * MG] # height if current_size is None: current_size = e**red_g.global_height(prec=prec) v0, th = covariant_z0(G, prec=prec, emb=emb) rep = 2 * CC.gen(0) from math import isnan if isnan(v0.abs()): raise ValueError("invalid covariant: %s" % v0) # get orbit S = matrix(ZZ, 2, 2, [0, -1, 1, 0]) T = matrix(ZZ, 2, 2, [1, 1, 0, 1]) TI = matrix(ZZ, 2, 2, [1, -1, 0, 1]) count = 0 pts = [[G, red_g, v0, rep, M * MG, coshdelta(v0), 0]] # label - 0:None, 1:S, 2:T, 3:T^(-1) if current_min is None: current_min = [G, red_g, v0, rep, M * MG, coshdelta(v0)] while pts != []: G, g, v, rep, M, D, label = pts.pop() # apply ST and keep z, Sz if D > R: break #all remaining pts are too far away # check if it is smaller. If so, we can improve the bound count += 1 new_size = e**g.global_height(prec=prec) if new_size < current_size: current_min = [G, g, v, rep, M, coshdelta(v)] current_size = new_size if new_size == 1: # early exit return [current_min[1], current_min[4]] new_R = get_bound_dynamical(G, g, m=n, dynatomic=dynatomic, prec=prec, emb=emb) if new_R < R: R = new_R # add new points to check if label != 1 and min((rep + 1).norm(), (rep - 1).norm()) >= 1: # don't undo S # the 2nd condition is equivalent to |\Re(-1/rep)| <= 1/2 # this means that rep can have resulted from an inversion step in # the shift-and-invert procedure, so don't invert # do inversion z = -1 / v new_pt = [ G.subs({ x: -y, y: x }), g.conjugate(S), z, -1 / rep, M * S, coshdelta(z), 1 ] pts = insert_item(pts, new_pt, 5) if label != 3: # don't undo T on g # do right shift z = v - 1 new_pt = [ G.subs({x: x + y}), g.conjugate(TI), z, rep - 1, M * TI, coshdelta(z), 2 ] pts = insert_item(pts, new_pt, 5) if label != 2: # don't undo TI on g # do left shift z = v + 1 new_pt = [ G.subs({x: x - y}), g.conjugate(T), z, rep + 1, M * T, coshdelta(z), 3 ] pts = insert_item(pts, new_pt, 5) return [current_min[1], current_min[4]]
def julia_plot(c=-1, x_center=0.0, y_center=0.0, image_width=4.0, max_iteration=500, pixel_count=500, base_color='steelblue', iteration_level=1, number_of_colors=50, point_color='yellow', interact=False, mandelbrot=True, period=None): r""" Plots the Julia set of a given complex `c` value. Users can specify whether they would like to display the Mandelbrot side by side with the Julia set. The Julia set of a given `c` value is the set of complex numbers for which the function `Q_c(z)=z^2+c` is bounded under iteration. The Julia set can be visualized by plotting each point in the set in the complex plane. Julia sets are examples of fractals when plotted in the complex plane. ALGORITHM: Define the map `Q_c(z) = z^2 + c` for some `c \in \mathbb{C}`. For every `p \in \mathbb{C}`, if `|Q_{c}^{k}(p)| > 2` for some `k \geq 0`, then `Q_{c}^{n}(p) \to \infty`. Let `N` be the maximum number of iterations. Compute the first `N` points on the orbit of `p` under `Q_c`. If for any `k < N`, `|Q_{c}^{k}(p)| > 2`, we stop the iteration and assign a color to the point `p` based on how quickly `p` escaped to infinity under iteration of `Q_c`. If `|Q_{c}^{i}(p)| \leq 2` for all `i \leq N`, we assume `p` is in the Julia set and assign the point `p` the color black. INPUT: - ``c`` -- complex (optional - default: ``-1``), complex point `c` that determines the Julia set. - ``period`` -- list (optional - default: ``None``), returns the Julia set for a random `c` value with the given (formal) cycle structure. - ``mandelbrot`` -- boolean (optional - default: ``True``), when set to ``True``, an image of the Mandelbrot set is appended to the right of the Julia set. - ``point_color`` -- RGB color (optional - default: ``'tomato'``), color of the point `c` in the Mandelbrot set (any valid input for Color). - ``x_center`` -- double (optional - default: ``-1.0``), Real part of center point. - ``y_center`` -- double (optional - default: ``0.0``), Imaginary part of center point. - ``image_width`` -- double (optional - default: ``4.0``), width of image in the complex plane. - ``max_iteration`` -- long (optional - default: ``500``), maximum number of iterations the map `Q_c(z)`. - ``pixel_count`` -- long (optional - default: ``500``), side length of image in number of pixels. - ``base_color`` -- RGB color (optional - default: ``'steelblue'``), color used to determine the coloring of set (any valid input for Color). - ``iteration_level`` -- long (optional - default: 1), number of iterations between each color level. - ``number_of_colors`` -- long (optional - default: 30), number of colors used to plot image. - ``interact`` -- boolean (optional - default: ``False``), controls whether plot will have interactive functionality. OUTPUT: 24-bit RGB image of the Julia set in the complex plane. EXAMPLES:: sage: julia_plot() 1001x500px 24-bit RGB image To display only the Julia set, set ``mandelbrot`` to ``False``:: sage: julia_plot(mandelbrot=False) 500x500px 24-bit RGB image To display an interactive plot of the Julia set in the Notebook, set ``interact`` to ``True``:: sage: julia_plot(interact=True) interactive(children=(FloatSlider(value=-1.0, description=u'Real c'... To return the Julia set of a random `c` value with (formal) cycle structure `(2,3)`, set ``period = [2,3]``:: sage: julia_plot(period=[2,3]) 1001x500px 24-bit RGB image To return all of the Julia sets of `c` values with (formal) cycle structure `(2,3)`:: sage: period = [2,3] # not tested ....: R.<c> = QQ[] ....: P.<x,y> = ProjectiveSpace(R,1) ....: f = DynamicalSystem([x^2+c*y^2, y^2]) ....: L = f.dynatomic_polynomial(period).subs({x:0,y:1}).roots(ring=CC) ....: c_values = [k[0] for k in L] ....: for c in c_values: ....: julia_plot(c) """ if period is not None: R = PolynomialRing(QQ, 'c') c = R.gen() x, y = ProjectiveSpace(R, 1, 'x,y').gens() f = DynamicalSystem([x**2 + c * y**2, y**2]) L = f.dynatomic_polynomial(period).subs({x: 0, y: 1}).roots(ring=CC) c = L[randint(0, len(L) - 1)][0] c = CC(c) c_real = c.real() c_imag = c.imag() base_color = Color(base_color) point_color = Color(point_color) if interact: from ipywidgets.widgets import FloatSlider, IntSlider, ColorPicker, interact widgets = dict( c_real = FloatSlider(min=-2.0, max=2.0, step=EPS, value=c_real, description="Real c"), c_imag = FloatSlider(min=-2.0, max=2.0, step=EPS, value=c_imag, description="Imag c"), x_center = FloatSlider(min=-1.0, max=1.0, step=EPS, value=x_center, description="Real center"), y_center = FloatSlider(min=-1.0, max=1.0, step=EPS, value=y_center, description="Imag center"), image_width = FloatSlider(min=EPS, max=4.0, step=EPS, value=image_width, description="Image width"), max_iteration = IntSlider(min=0, max=600, value=max_iteration, description="Iterations"), pixel_count = IntSlider(min=10, max=600, value=pixel_count, description="Pixels"), level_sep = IntSlider(min=1, max=20, value=iteration_level, description="Color sep"), color_num = IntSlider(min=1, max=100, value=number_of_colors, description="# Colors"), base_color = ColorPicker(value=base_color.html_color(), description="Base color"), ) if mandelbrot: widgets["point_color"] = ColorPicker(value=point_color.html_color(), description="Point color") return interact(**widgets).widget(julia_helper) else: return interact(**widgets).widget(fast_julia_plot) if mandelbrot: return julia_helper(c_real, c_imag, x_center, y_center, image_width, max_iteration, pixel_count, iteration_level, number_of_colors, base_color, point_color) else: return fast_julia_plot(c_real, c_imag, x_center, y_center, image_width, max_iteration, pixel_count, iteration_level, number_of_colors, base_color)
def _closest_unsafe(lst, x): x = CC(x.value) return min(enumerate(lst), key=lambda (i, y): abs(CC(y.value) - x))
class HyperbolicRegularPolygon(HyperbolicPolygon): r""" Primitive class for regular hyberbolic polygon type. See ``hyperbolic_regular_polygon?`` for information about plotting a hyperbolic regular polygon in the upper complex halfplane. INPUT: - ``sides`` -- number of sides of the polygon - ``i_angle`` -- interior angle of the polygon - ``center``-- center point as a complex number of the polygon EXAMPLES: Note that constructions should use :func:`hyperbolic_regular_polygon`:: sage: from sage.plot.hyperbolic_regular_polygon import HyperbolicRegularPolygon sage: print(HyperbolicRegularPolygon(5,pi/2,I, {})) Hyperbolic regular polygon (sides=5, i_angle=1/2*pi, center=1.00000000000000*I) The code verifies is there exists a compact hyperbolic regular polygon with the given data, checking .. MATH:: A(\mathcal{P}) = \pi(s-2) - s \cdot \alpha > 0, where `s` is ``sides`` and `\alpha` is ``i_angle`. This raises an error if the ``i_angle`` is less than the minimum to generate a compact polygon:: sage: from sage.plot.hyperbolic_regular_polygon import HyperbolicRegularPolygon sage: P = HyperbolicRegularPolygon(4, pi/2, I, {}) Traceback (most recent call last): ... ValueError: there exists no hyperbolic regular compact polygon, for sides=4 the interior angle must be less than 1/2*pi It is an error to give a center outside the upper half plane in this model :: sage: from sage.plot.hyperbolic_regular_polygon import HyperbolicRegularPolygon sage: P = HyperbolicRegularPolygon(4, pi/4, 1-I, {}) Traceback (most recent call last): ... ValueError: center: 1.00000000000000 - 1.00000000000000*I is not a valid point in the upper half plane model of the hyperbolic plane TESTS:: sage: from sage.plot.hyperbolic_regular_polygon import HyperbolicRegularPolygon sage: P = HyperbolicRegularPolygon(4, -pi/4, I, {}) Traceback (most recent call last): ... ValueError: interior angle -1/4*pi must be in (0, pi) interval sage: from sage.plot.hyperbolic_regular_polygon import HyperbolicRegularPolygon sage: P=HyperbolicRegularPolygon(16, 3*pi/2, I, {}) Traceback (most recent call last): ... ValueError: interior angle 3/2*pi must be in (0, pi) interval sage: from sage.plot.hyperbolic_regular_polygon import HyperbolicRegularPolygon sage: P = HyperbolicRegularPolygon(2, pi/10, I, {}) Traceback (most recent call last): ... ValueError: degenerated polygons (sides<=2) are not supported """ def __init__(self, sides, i_angle, center, options): """ Initialize HyperbolicRegularPolygon. EXAMPLES:: sage: from sage.plot.hyperbolic_regular_polygon import HyperbolicRegularPolygon sage: print(HyperbolicRegularPolygon(5,pi/2,I, {})) Hyperbolic regular polygon (sides=5, i_angle=1/2*pi, center=1.00000000000000*I) """ self.center = CC(center) if self.center.imag() <= 0 : raise ValueError("center: %s is not a valid point in the upper half plane model of the hyperbolic plane"%(self.center)) if sides < 3 : raise ValueError("degenerated polygons (sides<=2) are not supported") if i_angle <=0 or i_angle >= pi: raise ValueError("interior angle %s must be in (0, pi) interval"%(i_angle)) if pi*(sides-2) - sides*i_angle <= 0 : raise ValueError("there exists no hyperbolic regular compact polygon, for sides=%s the interior angle must be less than %s"%(sides, pi * (sides-2) / sides)) self.sides = sides self.i_angle = i_angle beta = 2 * pi / self.sides # compute the rotation angle to be used ahead alpha = self.i_angle / Integer(2) I = CC(0, 1) # compute using cosine theorem the radius of the circumscribed circle # using the triangle formed by the radius and the three known angles r = arccosh(cot(alpha) * (1 + cos(beta)) / sin(beta)) # The first point will be always on the imaginary axis limited # to 8 digits for efficiency in the subsequent calculations. z_0 = [I*(e**r).n(digits=8)] # Compute the dilation isometry used to move the center # from I to the imaginary part of the given center. scale = self.center.imag() # Compute the parabolic isometry to move the center to the # real part of the given center. h_disp = self.center.real() d_z_k = [z_0[0]*scale + h_disp] #d_k has the points for the polygon in the given center z_k = z_0 #z_k has the Re(z)>0 vertices for the I centered polygon r_z_k = [] #r_z_k has the Re(z)<0 vertices if is_odd(self.sides): vert = (self.sides - 1) / 2 else: vert = self.sides / 2 - 1 for k in range(0, vert): # Compute with 8 digits to accelerate calculations new_z_k = self._i_rotation(z_k[-1], beta).n(digits=8) z_k = z_k + [new_z_k] d_z_k = d_z_k + [new_z_k * scale + h_disp] r_z_k=[-(new_z_k).conjugate() * scale + h_disp] + r_z_k if is_odd(self.sides): HyperbolicPolygon.__init__(self, d_z_k + r_z_k, options) else: z_opo = [I * (e**(-r)).n(digits=8) * scale + h_disp] HyperbolicPolygon.__init__(self, d_z_k + z_opo + r_z_k, options) def _repr_(self): """ String representation of HyperbolicRegularPolygon. TESTS:: sage: from sage.plot.hyperbolic_regular_polygon import HyperbolicRegularPolygon sage: HyperbolicRegularPolygon(5,pi/2,I, {})._repr_() 'Hyperbolic regular polygon (sides=5, i_angle=1/2*pi, center=1.00000000000000*I)' """ return ("Hyperbolic regular polygon (sides=%s, i_angle=%s, center=%s)" % (self.sides, self.i_angle, self.center)) def _i_rotation(self, z, alpha): r""" Return the resulting point after applying a hyperbolic rotation centered at `0 + i` and angle ``alpha`` to ``z``. INPUT: - ``z``-- point in the upper complex halfplane to which apply the isometry - ``alpha``-- angle of rotation (radians,counterwise) OUTPUT: - rotated point in the upper complex halfplane TESTS:: sage: from sage.plot.hyperbolic_regular_polygon import HyperbolicRegularPolygon sage: P = HyperbolicRegularPolygon(4, pi/4, 1+I, {}) sage: P._i_rotation(2+I, pi/2) I - 2 """ _a = alpha / 2 _c = cos(_a) _s = sin(_a) G = matrix([[_c, _s], [-_s, _c]]) return (G[0][0] * z + G[0][1]) / (G[1][0] * z + G[1][1])