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 __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 __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 is_cm_j_invariant(j, method='new'): """ Return whether or not this is a CM `j`-invariant. INPUT: - ``j`` -- an element of a number field `K` OUTPUT: A pair (bool, (d,f)) which is either (False, None) if `j` is not a CM j-invariant or (True, (d,f)) if `j` is the `j`-invariant of the imaginary quadratic order of discriminant `D=df^2` where `d` is the associated fundamental discriminant and `f` the index. .. note:: The current implementation makes use of the classification of all orders of class number up to 100, and hence will raise an error if `j` is an algebraic integer of degree greater than this. It would be possible to implement a more general version, using the fact that `d` must be supported on the primes dividing the discriminant of the minimal polynomial of `j`. EXAMPLES:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: is_cm_j_invariant(0) (True, (-3, 1)) sage: is_cm_j_invariant(8000) (True, (-8, 1)) sage: K.<a> = QuadraticField(5) sage: is_cm_j_invariant(282880*a + 632000) (True, (-20, 1)) sage: K.<a> = NumberField(x^3 - 2) sage: is_cm_j_invariant(31710790944000*a^2 + 39953093016000*a + 50337742902000) (True, (-3, 6)) TESTS:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: all([is_cm_j_invariant(j) == (True, (d,f)) for d,f,j in cm_j_invariants_and_orders(QQ)]) True """ # First we check that j is an algebraic number: from sage.rings.all import NumberFieldElement, NumberField if not isinstance(j, NumberFieldElement) and not j in QQ: raise NotImplementedError( "is_cm_j_invariant() is only implemented for number field elements" ) # for j in ZZ we have a lookup-table: if j in ZZ: j = ZZ(j) table = dict([(jj, (d, f)) for d, f, jj in cm_j_invariants_and_orders(QQ)]) if j in table: return True, table[j] return False, None # Otherwise if j is in Q then it is not integral so is not CM: if j in QQ: return False, None # Now j has degree at least 2. If it is not integral so is not CM: if not j.is_integral(): return False, None # Next we find its minimal polynomial and degree h, and if h is # less than the degree of j.parent() we recreate j as an element # of Q(j): jpol = PolynomialRing(QQ, 'x')([-j, 1 ]) if j in QQ else j.absolute_minpoly() h = jpol.degree() # This will be used as a fall-back if we cannot determine the # result using local data. For this to be necessary there would # have to be very few primes of degree 1 and norm under 1000, # since we only need to find one prime of degree 1, good # reduction for which a_P is nonzero. if method == 'old': if h > 100: raise NotImplementedError( "CM data only available for class numbers up to 100") for d, f in cm_orders(h): if jpol == hilbert_class_polynomial(d * f**2): return True, (d, f) return False, None # replace j by a clone whose parent is Q(j), if necessary: K = j.parent() if h < K.absolute_degree(): K = NumberField(jpol, 'j') j = K.gen() # Construct an elliptic curve with j-invariant j, with # integral model: from sage.schemes.elliptic_curves.all import EllipticCurve E = EllipticCurve(j=j).integral_model() D = E.discriminant() prime_bound = 1000 # test primes of degree 1 up to this norm max_primes = 20 # test at most this many primes num_prime = 0 cmd = 0 cmf = 0 # Test primes of good reduction. If E has CM then for half the # primes P we will have a_P=0, and for all other prime P the CM # field is Q(sqrt(a_P^2-4N(P))). Hence if these fields are # different for two primes then E does not have CM. If they are # all equal for the primes tested, then we have a candidate CM # field. Moreover the discriminant of the endomorphism ring # divides all the values a_P^2-4N(P), since that is the # discriminant of the order containing the Frobenius at P. So we # end up with a finite number (usually one) of candidate # discriminants to test. Each is tested by checking that its class # number is h, and if so then that j is a root of its Hilbert # class polynomial. In practice non CM curves will be eliminated # by the local test at a small number of primes (probably just 2). for P in K.primes_of_degree_one_iter(prime_bound): if num_prime > max_primes: if cmd: # we have a candidate CM field already break else: # we need to try more primes max_primes *= 2 if D.valuation(P) > 0: # skip bad primes continue aP = E.reduction(P).trace_of_frobenius() if aP == 0: # skip supersingular primes continue num_prime += 1 DP = aP**2 - 4 * P.norm() dP = DP.squarefree_part() fP = ZZ(DP // dP).isqrt() if cmd == 0: # first one, so store d and f cmd = dP cmf = fP elif cmd != dP: # inconsistent with previous return False, None else: # consistent d, so update f cmf = cmf.gcd(fP) if cmd == 0: # no conclusion, we found no degree 1 primes, revert to old method return is_cm_j_invariant(j, method='old') # it looks like cm by disc cmd * f**2 where f divides cmf if cmd % 4 != 1: cmd = cmd * 4 cmf = cmf // 2 # Now we must check if h(cmd*f**2)==h for f|cmf; if so we check # whether j is a root of the associated Hilbert class polynomial. for f in cmf.divisors(): # only positive divisors d = cmd * f**2 if h != d.class_number(): continue pol = hilbert_class_polynomial(d) if pol(j) == 0: return True, (cmd, f) return False, None
from sage.rings.all import NumberField, QQ R = QQ['x'] x = R.gen() F = NumberField(x * x - x - 1, 'a') a = F.gen()
def is_cm_j_invariant(j, method='new'): """ Return whether or not this is a CM `j`-invariant. INPUT: - ``j`` -- an element of a number field `K` OUTPUT: A pair (bool, (d,f)) which is either (False, None) if `j` is not a CM j-invariant or (True, (d,f)) if `j` is the `j`-invariant of the imaginary quadratic order of discriminant `D=df^2` where `d` is the associated fundamental discriminant and `f` the index. .. note:: The current implementation makes use of the classification of all orders of class number up to 100, and hence will raise an error if `j` is an algebraic integer of degree greater than this. It would be possible to implement a more general version, using the fact that `d` must be supported on the primes dividing the discriminant of the minimal polynomial of `j`. EXAMPLES:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: is_cm_j_invariant(0) (True, (-3, 1)) sage: is_cm_j_invariant(8000) (True, (-8, 1)) sage: K.<a> = QuadraticField(5) sage: is_cm_j_invariant(282880*a + 632000) (True, (-20, 1)) sage: K.<a> = NumberField(x^3 - 2) sage: is_cm_j_invariant(31710790944000*a^2 + 39953093016000*a + 50337742902000) (True, (-3, 6)) TESTS:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: all([is_cm_j_invariant(j) == (True, (d,f)) for d,f,j in cm_j_invariants_and_orders(QQ)]) True """ # First we check that j is an algebraic number: from sage.rings.all import NumberFieldElement, NumberField if not isinstance(j, NumberFieldElement) and not j in QQ: raise NotImplementedError("is_cm_j_invariant() is only implemented for number field elements") # for j in ZZ we have a lookup-table: if j in ZZ: j = ZZ(j) table = dict([(jj,(d,f)) for d,f,jj in cm_j_invariants_and_orders(QQ)]) if j in table: return True, table[j] return False, None # Otherwise if j is in Q then it is not integral so is not CM: if j in QQ: return False, None # Now j has degree at least 2. If it is not integral so is not CM: if not j.is_integral(): return False, None # Next we find its minimal polynomial and degree h, and if h is # less than the degree of j.parent() we recreate j as an element # of Q(j): jpol = PolynomialRing(QQ,'x')([-j,1]) if j in QQ else j.absolute_minpoly() h = jpol.degree() # This will be used as a fall-back if we cannot determine the # result using local data. For this to be necessary there would # have to be very few primes of degree 1 and norm under 1000, # since we only need to find one prime of degree 1, good # reduction for which a_P is nonzero. if method=='old': if h>100: raise NotImplementedError("CM data only available for class numbers up to 100") for d,f in cm_orders(h): if jpol == hilbert_class_polynomial(d*f**2): return True, (d,f) return False, None # replace j by a clone whose parent is Q(j), if necessary: K = j.parent() if h < K.absolute_degree(): K = NumberField(jpol, 'j') j = K.gen() # Construct an elliptic curve with j-invariant j, with # integral model: from sage.schemes.elliptic_curves.all import EllipticCurve E = EllipticCurve(j=j).integral_model() D = E.discriminant() prime_bound = 1000 # test primes of degree 1 up to this norm max_primes = 20 # test at most this many primes num_prime = 0 cmd = 0 cmf = 0 # Test primes of good reduction. If E has CM then for half the # primes P we will have a_P=0, and for all other prime P the CM # field is Q(sqrt(a_P^2-4N(P))). Hence if these fields are # different for two primes then E does not have CM. If they are # all equal for the primes tested, then we have a candidate CM # field. Moreover the discriminant of the endomorphism ring # divides all the values a_P^2-4N(P), since that is the # discriminant of the order containing the Frobenius at P. So we # end up with a finite number (usually one) of candidate # discriminats to test. Each is tested by checking that its class # number is h, and if so then that j is a root of its Hilbert # class polynomial. In practice non CM curves will be eliminated # by the local test at a small number of primes (probably just 2). for P in K.primes_of_degree_one_iter(prime_bound): if num_prime > max_primes: if cmd: # we have a candidate CM field already break else: # we need to try more primes max_primes *=2 if D.valuation(P)>0: # skip bad primes continue aP = E.reduction(P).trace_of_frobenius() if aP == 0: # skip supersingular primes continue num_prime += 1 DP = aP**2 - 4*P.norm() dP = DP.squarefree_part() fP = ZZ(DP//dP).isqrt() if cmd==0: # first one, so store d and f cmd = dP cmf = fP elif cmd != dP: # inconsistent with previous return False, None else: # consistent d, so update f cmf = cmf.gcd(fP) if cmd==0: # no conclusion, we found no degree 1 primes, revert to old method return is_cm_j_invariant(j, method='old') # it looks like cm by disc cmd * f**2 where f divides cmf if cmd%4!=1: cmd = cmd*4 cmf = cmf//2 # Now we must check if h(cmd*f**2)==h for f|cmf; if so we check # whether j is a root of the associated Hilbert class polynomial. for f in cmf.divisors(): # only positive divisors d = cmd*f**2 if h != d.class_number(): continue pol = hilbert_class_polynomial(d) if pol(j)==0: return True, (cmd,f) return False, None
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)
from sage.rings.all import NumberField, QQ R = QQ['x'] x = R.gen() F = NumberField(x*x - x- 1, 'a') a = F.gen()
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 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)