def number_field_to_AA(a): r""" It is a mess to convert an element of a number field to the algebraic field ``AA``. This is a temporary fix. """ try: return AA(a) except TypeError: return AA.polynomial_root(a.minpoly(), RIF(a))
def from_mathematica(a): try: return QQ(a.sage()) except Exception: pass try: return AA(a.sage()) except Exception: coefficients = mathematica.CoefficientList( mathematica.MinimalPolynomial(a, 'x'), 'x').sage() x = polygen(QQ) minpoly = x.parent()(coefficients) interval = mathematica.IsolatingInterval(a).sage() rif_interval = RIF(interval) return AA.polynomial_root(minpoly, rif_interval)
def flipper_nf_to_sage(K, name='a'): r""" Convert a flipper number field into a Sage number field .. NOTE:: Currently, the code is not careful at all with root isolation. EXAMPLES:: sage: import flipper # optional - flipper sage: from flatsurf.geometry.similarity_surface_generators import flipper_nf_to_sage sage: p = flipper.kernel.Polynomial([-2r] + [0r]*5 + [1r]) # optional - flipper sage: r1,r2 = p.real_roots() # optional - flipper sage: K = flipper.kernel.NumberField(r1) # optional - flipper sage: K_sage = flipper_nf_to_sage(K) # optional - flipper sage: K_sage # optional - flipper Number Field in a with defining polynomial x^6 - 2 sage: AA(K_sage.gen()) # optional - flipper -1.122462048309373? """ from sage.rings.number_field.number_field import NumberField from sage.rings.all import QQ,RIF,AA r = K.lmbda.interval_approximation() l = r.lower * ZZ(10)**(-r.precision) u = r.upper * ZZ(10)**(-r.precision) p = QQ['x'](K.polynomial.coefficients) s = AA.polynomial_root(p, RIF(l,u)) return NumberField(p, name, embedding=s)
def number_field_elements_from_algebraics(elts, name='a'): r""" The native Sage function ``number_field_elements_from_algebraics`` currently returns number field *without* embedding. This function return field with embedding! EXAMPLES:: sage: from flatsurf.geometry.subfield import number_field_elements_from_algebraics sage: z = QQbar.zeta(5) sage: c = z.real() sage: s = z.imag() sage: number_field_elements_from_algebraics((c,s)) (Number Field in a with defining polynomial y^4 - 5*y^2 + 5 with a = 1.902113032590308?, [1/2*a^2 - 3/2, 1/2*a]) sage: number_field_elements_from_algebraics([AA(1), AA(2/3)]) (Rational Field, [1, 2/3]) """ # case when all elements are rationals if all(x in QQ for x in elts): return QQ, [QQ(x) for x in elts] # general case from sage.rings.qqbar import number_field_elements_from_algebraics field, elts, phi = number_field_elements_from_algebraics(elts, minimal=True) polys = [x.polynomial() for x in elts] K = NumberField(field.polynomial(), name, embedding=AA(phi(field.gen()))) gen = K.gen() return K, [x.polynomial()(gen) for x in elts]
def rho(self): r""" Return the vertex ``rho`` of the basic hyperbolic triangle which describes ``self``. ``rho`` has absolute value 1 and angle ``pi/n``. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: HeckeTriangleGroup(3).rho() == 1/2 + sqrt(3)/2*i True sage: HeckeTriangleGroup(4).rho() == sqrt(2)/2*(1 + i) True sage: HeckeTriangleGroup(6).rho() == sqrt(3)/2 + 1/2*i True sage: HeckeTriangleGroup(10).rho() 0.95105651629515...? + 0.30901699437494...?*I sage: HeckeTriangleGroup(infinity).rho() 1 """ # TODO: maybe rho should be replaced by -rhobar if (self._n == infinity): return AA(1) else: return AlgebraicField()(exp(pi/self._n*i))
def flipper_nf_to_sage(K, name='a'): r""" Convert a flipper number field into a Sage number field .. NOTE:: Currently, the code is not careful at all with root isolation. EXAMPLES:: sage: import flipper # optional - flipper sage: import realalg # optional - flipper sage: from flatsurf.geometry.similarity_surface_generators import flipper_nf_to_sage sage: K = realalg.RealNumberField([-2r] + [0r]*5 + [1r]) # optional - flipper sage: K_sage = flipper_nf_to_sage(K) # optional - flipper sage: K_sage # optional - flipper Number Field in a with defining polynomial x^6 - 2 with a = 1.122462048309373? sage: AA(K_sage.gen()) # optional - flipper 1.122462048309373? """ r = K.lmbda.interval() l = r.lower * ZZ(10)**(-r.precision) u = r.upper * ZZ(10)**(-r.precision) p = QQ['x'](K.coefficients) s = AA.polynomial_root(p, RIF(l, u)) return NumberField(p, name, embedding=s)
def is_elliptic(self): r""" Return whether ``self`` is an elliptic matrix. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(n=7) sage: [ G.V(k).is_elliptic() for k in range(1,8) ] [False, False, False, False, False, False, True] sage: G.U().is_elliptic() True """ return AA(self.discriminant()) < 0
def __init__(self, lambda_squared=None, field=None): if lambda_squared == None: from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing R = PolynomialRing(ZZ, 'x') x = R.gen() field = NumberField(x**3 - ZZ(5) * x**2 + ZZ(4) * x - ZZ(1), 'r', embedding=AA(ZZ(4))) self._l = field.gen() else: if field is None: self._l = lambda_squared field = lambda_squared.parent() else: self._l = field(lambda_squared) Surface.__init__(self, field, ZZ.zero(), finite=False)
def lam_minpoly(self): r""" Return the minimal polynomial of the corresponding lambda parameter of ``self``. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: HeckeTriangleGroup(10).lam_minpoly() x^4 - 5*x^2 + 5 sage: HeckeTriangleGroup(17).lam_minpoly() x^8 - x^7 - 7*x^6 + 6*x^5 + 15*x^4 - 10*x^3 - 10*x^2 + 4*x + 1 sage: HeckeTriangleGroup(infinity).lam_minpoly() x - 2 """ # TODO: Write an explicit (faster) implementation lam_symbolic = 2*cos(pi/self._n) return AA(lam_symbolic).minpoly()
def __init__(self, n): r""" Hecke triangle group (2, n, infinity). Namely the von Dyck group corresponding to the triangle group with angles (pi/2, pi/n, 0). INPUT: - ``n`` - ``infinity`` or an integer greater or equal to ``3``. OUTPUT: The Hecke triangle group for the given parameter ``n``. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(12) sage: G Hecke triangle group for n = 12 sage: G.category() Category of groups """ self._n = n if n in [3, infinity]: self._base_ring = ZZ self._lam = ZZ(1) if n==3 else ZZ(2) else: lam_symbolic = 2*cos(pi/n) K = NumberField(self.lam_minpoly(), 'lam', embedding = AA(lam_symbolic)) #self._base_ring = K.order(K.gens()) self._base_ring = K.maximal_order() self._lam = self._base_ring.gen(1) T = matrix(self._base_ring, [[1,self._lam],[0,1]]) S = matrix(self._base_ring, [[0,-1],[1,0]]) FinitelyGeneratedMatrixGroup_generic.__init__(self, ZZ(2), self._base_ring, [S, T])
def homothety_rotation_decomposition(m): r""" Return a couple composed of the homothety and a rotation matrix. The coefficients of the returned pair are either in the ground field of ``m`` or in the algebraic field ``AA``. EXAMPLES:: sage: from flatsurf.geometry.matrix_2x2 import homothety_rotation_decomposition sage: R.<x> = PolynomialRing(QQ) sage: K.<sqrt2> = NumberField(x^2 - 2, embedding=1.4142) sage: m = matrix([[sqrt2, -sqrt2],[sqrt2,sqrt2]]) sage: a,rot = homothety_rotation_decomposition(m) sage: a 2 sage: rot [ 1/2*sqrt2 -1/2*sqrt2] [ 1/2*sqrt2 1/2*sqrt2] """ if not is_similarity(m): raise ValueError("the matrix must be a similarity") det = m.det() if not det.is_square(): if not AA.has_coerce_map_from(m.base_ring()): l = map(number_field_to_AA, m.list()) M = MatrixSpace(AA, 2) m = M(l) else: m = m.change_ring(AA) sqrt_det = det.sqrt() return sqrt_det, m / sqrt_det
def homothety_rotation_decomposition(m): r""" Return a couple composed of the homothety and a rotation matrix. The coefficients of the returned pair are either in the ground field of ``m`` or in the algebraic field ``AA``. EXAMPLES:: sage: from flatsurf.geometry.matrix_2x2 import homothety_rotation_decomposition sage: R.<x> = PolynomialRing(QQ) sage: K.<sqrt2> = NumberField(x^2 - 2, embedding=1.4142) sage: m = matrix([[sqrt2, -sqrt2],[sqrt2,sqrt2]]) sage: a,rot = homothety_rotation_decomposition(m) sage: a 2 sage: rot [ 1/2*sqrt2 -1/2*sqrt2] [ 1/2*sqrt2 1/2*sqrt2] """ if not is_similarity(m): raise ValueError("the matrix must be a similarity") det = m.det() if not det.is_square(): if not AA.has_coerce_map_from(m.base_ring()): l = map(number_field_to_AA,m.list()) M = MatrixSpace(AA,2) m = M(l) else: m = m.change_ring(AA) sqrt_det = det.sqrt() return sqrt_det, m / sqrt_det
def mcmullen_genus2_prototype(w, h, t, e, rel=0): r""" McMullen prototypes in the stratum H(2). These prototype appear at least in McMullen "Teichmüller curves in genus two: Discriminant and spin" (2004). The notation from that paper are quadruple ``(a, b, c, e)`` which translates in our notation as ``w = b``, ``h = c``, ``t = a`` (and ``e = e``). The associated discriminant is `D = e^2 + 4 wh`. If ``rel`` is a positive parameter (less than w-lambda) the surface belongs to the eigenform locus in H(1,1). EXAMPLES:: sage: from flatsurf import translation_surfaces sage: from surface_dynamics import AbelianStratum sage: prototypes = { ....: 5: [(1,1,0,-1)], ....: 8: [(1,1,0,-2), (2,1,0,0)], ....: 9: [(2,1,0,-1)], ....: 12: [(1,2,0,-2), (2,1,0,-2), (3,1,0,0)], ....: 13: [(1,1,0,-3), (3,1,0,-1), (3,1,0,1)], ....: 16: [(3,1,0,-2), (4,1,0,0)], ....: 17: [(1,2,0,-3), (2,1,0,-3), (2,2,0,-1), (2,2,1,-1), (4,1,0,-1), (4,1,0,1)], ....: 20: [(1,1,0,-4), (2,2,1,-2), (4,1,0,-2), (4,1,0,2)], ....: 21: [(1,3,0,-3), (3,1,0,-3)], ....: 24: [(1,2,0,-4), (2,1,0,-4), (3,2,0,0)], ....: 25: [(2,2,0,-3), (2,2,1,-3), (3,2,0,-1), (4,1,0,-3)]} sage: for D in sorted(prototypes): ....: for w,h,t,e in prototypes[D]: ....: T = translation_surfaces.mcmullen_genus2_prototype(w,h,t,e) ....: assert T.stratum() == AbelianStratum(2) ....: assert (D.is_square() and T.base_ring() is QQ) or (T.base_ring().polynomial().discriminant() == D) An example with some relative homology:: sage: U8 = translation_surfaces.mcmullen_genus2_prototype(2,1,0,0,1/4) # discriminant 8 sage: U12 = translation_surfaces.mcmullen_genus2_prototype(3,1,0,0,3/10) # discriminant 12 sage: U8.stratum() H_2(1^2) sage: U8.base_ring().polynomial().discriminant() 8 sage: U8.j_invariant() ( [4 0] (0), (0), [0 2] ) sage: U12.stratum() H_2(1^2) sage: U12.base_ring().polynomial().discriminant() 12 sage: U12.j_invariant() ( [6 0] (0), (0), [0 2] ) """ w = ZZ(w) h = ZZ(h) t = ZZ(t) e = ZZ(e) g = w.gcd(h) gg = g.gcd(t).gcd(e) if w <= 0 or h <= 0 or t < 0 or t >= g or not g.gcd(t).gcd( e).is_one() or e + h >= w: raise ValueError("invalid parameters") x = polygen(QQ) poly = x**2 - e * x - w * h if poly.is_irreducible(): emb = AA.polynomial_root(poly, RIF(0, w)) K = NumberField(poly, 'l', embedding=emb) l = K.gen() else: K = QQ D = e**2 + 4 * w * h d = D.sqrt() l = (e + d) / 2 rel = K(rel) # (lambda,lambda) square on top # twisted (w,0), (t,h) s = Surface_list(base_ring=K) if rel: if rel < 0 or rel > w - l: raise ValueError("invalid rel argument") s.add_polygon( polygons(vertices=[(0, 0), (l, 0), (l + rel, l), (rel, l)], ring=K)) s.add_polygon( polygons(vertices=[(0, 0), (rel, 0), (rel + l, 0), (w, 0), (w + t, h), (l + rel + t, h), (t + l, h), (t, h)], ring=K)) s.set_edge_pairing(0, 1, 0, 3) s.set_edge_pairing(0, 0, 1, 6) s.set_edge_pairing(0, 2, 1, 1) s.set_edge_pairing(1, 2, 1, 4) s.set_edge_pairing(1, 3, 1, 7) s.set_edge_pairing(1, 0, 1, 5) else: s.add_polygon( polygons(vertices=[(0, 0), (l, 0), (l, l), (0, l)], ring=K)) s.add_polygon( polygons(vertices=[(0, 0), (l, 0), (w, 0), (w + t, h), (l + t, h), (t, h)], ring=K)) s.set_edge_pairing(0, 1, 0, 3) s.set_edge_pairing(0, 0, 1, 4) s.set_edge_pairing(0, 2, 1, 0) s.set_edge_pairing(1, 1, 1, 3) s.set_edge_pairing(1, 2, 1, 5) s.set_immutable() return TranslationSurface(s)
def fixed_points(self, embedded=False, order="default"): r""" Return a pair of (mutually conjugate) fixed points of ``self`` in a possible quadratic extension of the base field. INPUT: - ``embedded`` -- If ``True`` the fixed points are embedded into ``AlgebraicRealField`` resp. ``AlgebraicField``. Default: ``False``. - ``order`` -- If ``order="none"`` the fixed points are choosen and ordered according to a fixed formula. If ``order="sign"`` the fixed points are always ordered according to the sign in front of the square root. If ``order="default"`` (default) then in case the fixed points are hyperbolic they are ordered according to the sign of the trace of ``self`` instead, such that the attracting fixed point comes first. If ``order="trace"`` the fixed points are always ordered according to the sign of the trace of ``self``. If the trace is zero they are ordered by the sign in front of the square root. In particular the fixed_points in this case remain the same for ``-self``. OUTPUT: If ``embedded=True`` an element of either ``AlgebraicRealField`` or ``AlgebraicField`` is returned. Otherwise an element of a relative field extension over the base field of (the parent of) ``self`` is returned. Warning: Relative field extensions don't support default embeddings. So the correct embedding (which is the positive resp. imaginary positive one) has to be choosen. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(n=infinity) sage: (-G.T(-4)).fixed_points() [+Infinity, +Infinity] sage: (-G.S()).fixed_points() [1/2*e, -1/2*e] sage: p = (-G.S()).fixed_points(embedded=True)[0] sage: p 1*I sage: (-G.S()).acton(p) == p True sage: (-G.V(2)).fixed_points() [1/2*e, -1/2*e] sage: (-G.V(2)).fixed_points() == G.V(2).fixed_points() True sage: p = (-G.V(2)).fixed_points(embedded=True)[1] sage: p -1.732050807568878? sage: (-G.V(2)).acton(p) == p True sage: G = HeckeTriangleGroup(n=7) sage: (-G.S()).fixed_points() [1/2*e, -1/2*e] sage: p = (-G.S()).fixed_points(embedded=True)[1] sage: p -1*I sage: (-G.S()).acton(p) == p True sage: (G.U()^4).fixed_points() [(1/2*lam^2 - 1/2*lam - 1/2)*e + 1/2*lam, (-1/2*lam^2 + 1/2*lam + 1/2)*e + 1/2*lam] sage: pts = (G.U()^4).fixed_points(order="trace") sage: (G.U()^4).fixed_points() == [pts[1], pts[0]] True sage: (G.U()^4).fixed_points(order="trace") == (-G.U()^4).fixed_points(order="trace") True sage: (G.U()^4).fixed_points() == (G.U()^4).fixed_points(order="none") True sage: (-G.U()^4).fixed_points() == (G.U()^4).fixed_points() True sage: (-G.U()^4).fixed_points(order="none") == pts True sage: p = (G.U()^4).fixed_points(embedded=True)[1] sage: p 0.9009688679024191? - 0.4338837391175581?*I sage: (G.U()^4).acton(p) == p True sage: (-G.V(5)).fixed_points() [(1/2*lam^2 - 1/2*lam - 1/2)*e, (-1/2*lam^2 + 1/2*lam + 1/2)*e] sage: (-G.V(5)).fixed_points() == G.V(5).fixed_points() True sage: p = (-G.V(5)).fixed_points(embedded=True)[0] sage: p 0.6671145837954892? sage: (-G.V(5)).acton(p) == p True """ if self.c() == 0: return [infinity, infinity] else: D = self.discriminant() if D.is_square(): e = D.sqrt() else: e = self.root_extension_field().gen() a = self.a() d = self.d() c = self.c() if order == "none": sgn = ZZ(1) elif order == "sign": sgn = AA(c).sign() elif order == "default": if self.is_elliptic() or self.trace() == 0: sgn = AA(c).sign() else: sgn = AA(self.trace()).sign() elif order == "trace": if self.trace() == 0: sgn = AA(c).sign() else: sgn = AA(self.trace()).sign() else: raise NotImplementedError if embedded: e = AA(D).sqrt() a = AA(a) d = AA(d) c = AA(c) root1 = (a - d) / (2 * c) + sgn * e / (2 * c) root2 = (a - d) / (2 * c) - sgn * e / (2 * c) return [root1, root2]
def decompose_basic(self): r""" Decompose ``self`` into a product of the generators ``S`` and ``T`` of its parent, The function returns a tuple ``L`` consisting of either ``parent.S()`` or non-trivial integer powers of ``parent.T()``. Additionally ``L`` starts with +- the identity. It satisfies the property: ``prod(L) == self``. If this decomposition is not possible an ``ValueError`` is raised. In particular this function can be used to check the membership in ``parent`` of an arbitrary matrix over the base ring. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(n=17) sage: L = (-G.V(2)).decompose_basic() sage: L ( [ 1 lam] [ 0 -1] [ 1 lam] [-1 0] [ 0 1], [ 1 0], [ 0 1], [ 0 -1] ) sage: prod(L) == -G.V(2) True sage: L = G.U().decompose_basic() sage: L ( [ 1 lam] [ 0 -1] [1 0] [ 0 1], [ 1 0], [0 1] ) sage: prod(L) == G.U() True """ def mshift(A): a = A[0][0] b = A[0][1] c = A[1][0] d = A[1][1] return (4 * a * c + b * d) / (4 * c * c + d * d) def mabs(A): a = A[0][0] b = A[0][1] c = A[1][0] d = A[1][1] return (4 * a * a + b * b) / (4 * c * c + d * d) res = [] T = self.parent().T() S = self.parent().S() M = self lam = self.parent().lam() while True: m = (AA(mshift(M) / lam) + ZZ(1) / ZZ(2)).floor() M = T**(-m) * M if (m != 0): res.append(T**m) abs_t = mabs(M) if AA(abs_t) < 1: M = (-S) * M res.append(S) elif M.is_identity(): res.append(M) return tuple(res) else: raise ValueError( "The matrix is not an element of {}, up to equivalence it identifies two nonequivalent points." .format(self.parent()))
def root_extension_embedding(self, K=AlgebraicField()): r""" Return the correct embedding from the root extension field to ``K`` (default: ``AlgebraicField()``). EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(n=infinity) sage: fp = (-G.S()).fixed_points()[0] sage: alg_fp = (-G.S()).root_extension_embedding()(fp) sage: alg_fp 1*I sage: alg_fp == (-G.S()).fixed_points(embedded=True)[0] True sage: fp = (-G.V(2)).fixed_points()[1] sage: alg_fp = (-G.V(2)).root_extension_embedding(AA)(fp) sage: alg_fp -1.732050807568...? sage: alg_fp == (-G.V(2)).fixed_points(embedded=True)[1] True sage: fp = (-G.V(2)).fixed_points()[0] sage: alg_fp = (-G.V(2)).root_extension_embedding(AA)(fp) sage: alg_fp 1.732050807568...? sage: alg_fp == (-G.V(2)).fixed_points(embedded=True)[0] True sage: G = HeckeTriangleGroup(n=7) sage: fp = (-G.S()).fixed_points()[1] sage: alg_fp = (-G.S()).root_extension_embedding()(fp) sage: alg_fp 0.?... - 1.000000000000...?*I sage: alg_fp == (-G.S()).fixed_points(embedded=True)[1] True sage: fp = (-G.U()^4).fixed_points()[0] sage: alg_fp = (-G.U()^4).root_extension_embedding()(fp) sage: alg_fp 0.9009688679024...? + 0.4338837391175...?*I sage: alg_fp == (-G.U()^4).fixed_points(embedded=True)[0] True sage: (-G.U()^4).root_extension_embedding(CC)(fp) 0.900968867902... + 0.433883739117...*I sage: (-G.U()^4).root_extension_embedding(CC)(fp).parent() Complex Field with 53 bits of precision sage: fp = (-G.V(5)).fixed_points()[1] sage: alg_fp = (-G.V(5)).root_extension_embedding(AA)(fp) sage: alg_fp -0.6671145837954...? sage: alg_fp == (-G.V(5)).fixed_points(embedded=True)[1] True """ G = self.parent() D = self.discriminant() F = self.root_extension_field() n = G.n() if D.is_square(): e = D.sqrt() lam = G.lam() elif n in [3, infinity]: e = F.gen(0) lam = G.lam() else: (e, lam) = F.gens() emb_lam = K(G.lam()) if self.is_elliptic(): emb_e = K(AlgebraicField()(D).sqrt()) else: emb_e = K(AA(D).sqrt()) guess = ZZ(0) min_value = infinity index = ZZ(0) for (index, emb) in enumerate(self.root_extension_field().embeddings(K)): if K.is_exact(): if emb(lam) == emb_lam and emb(e) == emb_e: return emb else: value = (emb(lam) - emb_lam).n( K.prec()).abs() + (emb(e) - emb_e).n(K.prec()).abs() if (value < min_value): guess = index min_value = value if K.is_exact() or min_value == infinity: raise ValueError( "No suitable embedding is available for K = {}!".format(K)) else: return self.root_extension_field().embeddings(K)[guess]
def arnoux_yoccoz(genus): r""" Construct the Arnoux-Yoccoz surface of genus 3 or greater. This presentation of the surface follows Section 2.3 of Joshua P. Bowman's paper "The Complete Family of Arnoux-Yoccoz Surfaces." EXAMPLES:: sage: from flatsurf import * sage: s = translation_surfaces.arnoux_yoccoz(4) sage: TestSuite(s).run() sage: s.is_delaunay_decomposed() True sage: s = s.canonicalize() sage: field=s.base_ring() sage: a = field.gen() sage: from sage.matrix.constructor import Matrix sage: m = Matrix([[a,0],[0,~a]]) sage: ss = m*s sage: ss = ss.canonicalize() sage: s.cmp_translation_surface(ss)==0 True The Arnoux-Yoccoz pseudo-Anosov are known to have (minimal) invariant foliations with SAF=0:: sage: S3 = translation_surfaces.arnoux_yoccoz(3) sage: Jxx, Jyy, Jxy = S3.j_invariant() sage: Jxx.is_zero() and Jyy.is_zero() True sage: Jxy [ 0 2 0] [ 2 -2 0] [ 0 0 2] sage: S4 = translation_surfaces.arnoux_yoccoz(4) sage: Jxx, Jyy, Jxy = S4.j_invariant() sage: Jxx.is_zero() and Jyy.is_zero() True sage: Jxy [ 0 2 0 0] [ 2 -2 0 0] [ 0 0 2 2] [ 0 0 2 0] """ g=ZZ(genus) assert g>=3 from sage.rings.polynomial.polynomial_ring import polygen x = polygen(AA) p=sum([x**i for i in xrange(1,g+1)])-1 cp = AA.common_polynomial(p) alpha_AA = AA.polynomial_root(cp, RIF(1/2, 1)) field=NumberField(alpha_AA.minpoly(),'alpha',embedding=alpha_AA) a=field.gen() from sage.modules.free_module import VectorSpace V=VectorSpace(field,2) p=[None for i in xrange(g+1)] q=[None for i in xrange(g+1)] p[0]=V(( (1-a**g)/2, a**2/(1-a) )) q[0]=V(( -a**g/2, a )) p[1]=V(( -(a**(g-1)+a**g)/2, (a-a**2+a**3)/(1-a) )) p[g]=V(( 1+(a-a**g)/2, (3*a-1-a**2)/(1-a) )) for i in xrange(2,g): p[i]=V(( (a-a**i)/(1-a) , a/(1-a) )) for i in xrange(1,g+1): q[i]=V(( (2*a-a**i-a**(i+1))/(2*(1-a)), (a-a**(g-i+2))/(1-a) )) from flatsurf.geometry.polygon import Polygons P=Polygons(field) s = Surface_list(field) T = [None] * (2*g+1) Tp = [None] * (2*g+1) from sage.matrix.constructor import Matrix m=Matrix([[1,0],[0,-1]]) for i in xrange(1,g+1): # T_i is (P_0,Q_i,Q_{i-1}) T[i]=s.add_polygon(P(edges=[ q[i]-p[0], q[i-1]-q[i], p[0]-q[i-1] ])) # T_{g+i} is (P_i,Q_{i-1},Q_{i}) T[g+i]=s.add_polygon(P(edges=[ q[i-1]-p[i], q[i]-q[i-1], p[i]-q[i] ])) # T'_i is (P'_0,Q'_{i-1},Q'_i) Tp[i]=s.add_polygon(m*s.polygon(T[i])) # T'_{g+i} is (P'_i,Q'_i, Q'_{i-1}) Tp[g+i]=s.add_polygon(m*s.polygon(T[g+i])) for i in xrange(1,g): s.change_edge_gluing(T[i],0,T[i+1],2) s.change_edge_gluing(Tp[i],2,Tp[i+1],0) for i in xrange(1,g+1): s.change_edge_gluing(T[i],1,T[g+i],1) s.change_edge_gluing(Tp[i],1,Tp[g+i],1) #P 0 Q 0 is paired with P' 0 Q' 0, ... s.change_edge_gluing(T[1],2,Tp[g],2) s.change_edge_gluing(Tp[1],0,T[g],0) # P1Q1 is paired with P'_g Q_{g-1} s.change_edge_gluing(T[g+1],2,Tp[2*g],2) s.change_edge_gluing(Tp[g+1],0,T[2*g],0) # P1Q0 is paired with P_{g-1} Q_{g-1} s.change_edge_gluing(T[g+1],0,T[2*g-1],2) s.change_edge_gluing(Tp[g+1],2,Tp[2*g-1],0) # PgQg is paired with Q1P2 s.change_edge_gluing(T[2*g],2,T[g+2],0) s.change_edge_gluing(Tp[2*g],0,Tp[g+2],2) for i in xrange(2,g-1): # PiQi is paired with Q'_i P'_{i+1} s.change_edge_gluing(T[g+i],2,Tp[g+i+1],2) s.change_edge_gluing(Tp[g+i],0,T[g+i+1],0) s.set_immutable() return TranslationSurface(s)
def _coerce_map_from_(self, P): r""" Return whether ``P`` coerces into this symbolic subring. INPUT: - ``P`` -- a parent. OUTPUT: A boolean or ``None``. TESTS:: sage: from sage.symbolic.subring import GenericSymbolicSubring sage: GenericSymbolicSubring(vars=tuple()).has_coerce_map_from(SR) # indirect doctest # not tested see #19231 False :: sage: from sage.symbolic.subring import SymbolicSubring sage: C = SymbolicSubring(no_variables=True) sage: C.has_coerce_map_from(ZZ) # indirect doctest True sage: C.has_coerce_map_from(QQ) # indirect doctest True sage: C.has_coerce_map_from(RR) # indirect doctest True sage: C.has_coerce_map_from(RIF) # indirect doctest True sage: C.has_coerce_map_from(CC) # indirect doctest True sage: C.has_coerce_map_from(CIF) # indirect doctest True sage: C.has_coerce_map_from(AA) # indirect doctest True sage: C.has_coerce_map_from(QQbar) # indirect doctest True sage: C.has_coerce_map_from(SR) # indirect doctest False """ if P == SR: # Workaround; can be deleted once #19231 is fixed return False from sage.rings.real_mpfr import mpfr_prec_min from sage.rings.all import (ComplexField, RLF, CLF, AA, QQbar, InfinityRing) from sage.rings.real_mpfi import is_RealIntervalField from sage.rings.complex_interval_field import is_ComplexIntervalField if isinstance(P, type): return SR._coerce_map_from_(P) elif RLF.has_coerce_map_from(P) or \ CLF.has_coerce_map_from(P) or \ AA.has_coerce_map_from(P) or \ QQbar.has_coerce_map_from(P): return True elif (P is InfinityRing or is_RealIntervalField(P) or is_ComplexIntervalField(P)): return True elif ComplexField(mpfr_prec_min()).has_coerce_map_from(P): return P not in (RLF, CLF, AA, QQbar)
def angle(u, v, numerical=False, assume_rational=False): r""" Return the angle between the vectors ``u`` and ``v`` divided by `2 \pi`. INPUT: - ``u``, ``v`` - vectors - ``numerical`` - boolean, whether to return floating point numbers - ``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 The numerical version (working over floating point numbers):: sage: import math sage: u = (1, 0) sage: for n in xsrange(1,20): ....: for k in xsrange(1,n): ....: a = 2 * k * math.pi / n ....: v = (math.cos(a), math.sin(a)) ....: assert abs(angle(u,v,numerical=True) * 2 * math.pi - a) < 1.e-10 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 # abs tol 1e-14 0.14102355421224375 sage: exp(2*pi.n()*CC(0,1)*a) 0.632455532033676 + 0.774596669241483*I sage: v / v.norm() (0.6324555320336758?, 0.774596669241484?) """ if not assume_rational and not numerical: 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 = vector(AA, u) vv = (AA(sqnorm_u) / AA(sqnorm_v)).sqrt() * vector(AA, v) 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) elif assume_rational: is_rational = True import math u0 = float(u[0]) u1 = float(u[1]) v0 = float(v[0]) v1 = float(v[1]) cos_uv = (u0 * v0 + u1 * v1) / math.sqrt( (u0 * u0 + u1 * u1) * (v0 * v0 + v1 * v1)) if cos_uv < -1.0: assert cos_uv > -1.0000001 cos_uv = -1.0 elif cos_uv > 1.0: assert cos_uv < 1.0000001 cos_uv = 1.0 angle = math.acos(cos_uv) / (2 * math.pi) # rat number between 0 and 1/2 if numerical or not is_rational: return 1.0 - angle if u0 * v1 - u1 * v0 < 0 else angle else: # fast and dirty way using floating point approximation # (see below for a slow but exact method) angle_rat = RR(angle).nearby_rational(0.00000001) if angle_rat.denominator() > 100: raise NotImplementedError( "the numerical method used is not smart enough!") return 1 - angle_rat if u0 * v1 - u1 * v0 < 0 else angle_rat
- 251568270025211201955389171860338579171675714868104052152789880989805712726614197574887246767925108246607970007160940134370673529705101398997875679835167365081669374673539993436160000*t**3 - 141772430429024326881509616736177148212059744813752208473666088996700368688933789363560771135161864201721524861419389136852260702560567771393655430337183040528733205980885277265100800000*t**2 - 25817606346086956476758470208850883766420383787607030339022713274189365873405154695767797745935894060883806800961601773820707673481632102376182116930107064769426021006200320503617211596800*t + 34918206405098505823938790072675572231488655998026261577866691497637535623661745400444768148106948876570405151317417742685700849593772165309178580940805695949542187927076864000)*Dt) quadric_slice_pol = ( 4980990673427087034113103774848375913397675011396681161337606780457883155824640000000000*t**12 - 16313074573215242896867677175985719375664055250377801991087546344967331905536000000000*t**9 - 14852779293587242300314544658084523021409425155052443959294262319432698489552764928000000*t**8 + 18694126910889886952945780127491545129704079293214429569400282861674612412907520000*t**6 + 32429224374768702788524801575483580065598417846595577296275963028007688596147404800000*t**5 + 14763130935033327878568955564665179022508855828282305094488782847988800598441515915673600*t**4 - 7447056930374930458107131157447569387299331973073657492405996702806537404416000*t**3 - 18581243794708202636835504417848386599346688512251081679746508518773002589362454528*t**2 - 16116744082275656666424675660780874575937043631040306492377025123023286892432343685120*t - 4891341219838850087826096307272910719484535278552470341569283855964428449539674077056375) quadric_slice_crit = AA.polynomial_root(quadric_slice_pol, RIF(-0.999,-0.998)) aa = AA.polynomial_root(AA.common_polynomial(t**2 - t - 6256320), RIF(-RR(2500.7637305969961), -RR(2500.7637305969956))) K, a = NumberField(t**2 - t - 6256320, 'a', embedding=aa).objgen() DiffOps_x, x, Dx = DifferentialOperators(K, 'x') iint_quadratic_alg = IVP( dop = ( (8680468749131953125000000000000000000000*x**13 + (34722222218750000000000000000000*a - 8680555572048611109375000000000000000000)*x**12 - 43419899820094632213834375000000000000000*x**11 + ( -173681336093739466250000000000000*a + 43420334110275534609369733125000000000000)*x**10 + 86874920665761352031076792873375000000000*x**9 + (347503157694622354347850650000000*a
def arnoux_yoccoz(genus): r""" Construct the Arnoux-Yoccoz surface of genus 3 or greater. This presentation of the surface follows Section 2.3 of Joshua P. Bowman's paper "The Complete Family of Arnoux-Yoccoz Surfaces." EXAMPLES:: sage: from flatsurf import * sage: s = translation_surfaces.arnoux_yoccoz(4) sage: TestSuite(s).run() sage: s.is_delaunay_decomposed() True sage: s = s.canonicalize() sage: field=s.base_ring() sage: a = field.gen() sage: from sage.matrix.constructor import Matrix sage: m = Matrix([[a,0],[0,~a]]) sage: ss = m*s sage: ss = ss.canonicalize() sage: s.cmp(ss) == 0 True The Arnoux-Yoccoz pseudo-Anosov are known to have (minimal) invariant foliations with SAF=0:: sage: S3 = translation_surfaces.arnoux_yoccoz(3) sage: Jxx, Jyy, Jxy = S3.j_invariant() sage: Jxx.is_zero() and Jyy.is_zero() True sage: Jxy [ 0 2 0] [ 2 -2 0] [ 0 0 2] sage: S4 = translation_surfaces.arnoux_yoccoz(4) sage: Jxx, Jyy, Jxy = S4.j_invariant() sage: Jxx.is_zero() and Jyy.is_zero() True sage: Jxy [ 0 2 0 0] [ 2 -2 0 0] [ 0 0 2 2] [ 0 0 2 0] """ g = ZZ(genus) assert g >= 3 x = polygen(AA) p = sum([x**i for i in range(1, g + 1)]) - 1 cp = AA.common_polynomial(p) alpha_AA = AA.polynomial_root(cp, RIF(1 / 2, 1)) field = NumberField(alpha_AA.minpoly(), 'alpha', embedding=alpha_AA) a = field.gen() V = VectorSpace(field, 2) p = [None for i in range(g + 1)] q = [None for i in range(g + 1)] p[0] = V(((1 - a**g) / 2, a**2 / (1 - a))) q[0] = V((-a**g / 2, a)) p[1] = V((-(a**(g - 1) + a**g) / 2, (a - a**2 + a**3) / (1 - a))) p[g] = V((1 + (a - a**g) / 2, (3 * a - 1 - a**2) / (1 - a))) for i in range(2, g): p[i] = V(((a - a**i) / (1 - a), a / (1 - a))) for i in range(1, g + 1): q[i] = V(((2 * a - a**i - a**(i + 1)) / (2 * (1 - a)), (a - a**(g - i + 2)) / (1 - a))) P = ConvexPolygons(field) s = Surface_list(field) T = [None] * (2 * g + 1) Tp = [None] * (2 * g + 1) from sage.matrix.constructor import Matrix m = Matrix([[1, 0], [0, -1]]) for i in range(1, g + 1): # T_i is (P_0,Q_i,Q_{i-1}) T[i] = s.add_polygon( P(edges=[q[i] - p[0], q[i - 1] - q[i], p[0] - q[i - 1]])) # T_{g+i} is (P_i,Q_{i-1},Q_{i}) T[g + i] = s.add_polygon( P(edges=[q[i - 1] - p[i], q[i] - q[i - 1], p[i] - q[i]])) # T'_i is (P'_0,Q'_{i-1},Q'_i) Tp[i] = s.add_polygon(m * s.polygon(T[i])) # T'_{g+i} is (P'_i,Q'_i, Q'_{i-1}) Tp[g + i] = s.add_polygon(m * s.polygon(T[g + i])) for i in range(1, g): s.change_edge_gluing(T[i], 0, T[i + 1], 2) s.change_edge_gluing(Tp[i], 2, Tp[i + 1], 0) for i in range(1, g + 1): s.change_edge_gluing(T[i], 1, T[g + i], 1) s.change_edge_gluing(Tp[i], 1, Tp[g + i], 1) #P 0 Q 0 is paired with P' 0 Q' 0, ... s.change_edge_gluing(T[1], 2, Tp[g], 2) s.change_edge_gluing(Tp[1], 0, T[g], 0) # P1Q1 is paired with P'_g Q_{g-1} s.change_edge_gluing(T[g + 1], 2, Tp[2 * g], 2) s.change_edge_gluing(Tp[g + 1], 0, T[2 * g], 0) # P1Q0 is paired with P_{g-1} Q_{g-1} s.change_edge_gluing(T[g + 1], 0, T[2 * g - 1], 2) s.change_edge_gluing(Tp[g + 1], 2, Tp[2 * g - 1], 0) # PgQg is paired with Q1P2 s.change_edge_gluing(T[2 * g], 2, T[g + 2], 0) s.change_edge_gluing(Tp[2 * g], 0, Tp[g + 2], 2) for i in range(2, g - 1): # PiQi is paired with Q'_i P'_{i+1} s.change_edge_gluing(T[g + i], 2, Tp[g + i + 1], 2) s.change_edge_gluing(Tp[g + i], 0, T[g + i + 1], 0) s.set_immutable() return TranslationSurface(s)