Пример #1
0
 def _sollya_annotate(self, center, rad, polys):
     import sagesollya as sollya
     logger = logging.getLogger(__name__ + ".sollya")
     logger.info("calling annotatefunction() on %s derivatives", len(polys))
     center = QQ(center)
     sollya_fun = self._sollya_object
     # sollya keeps all annotations, let's not bother with selecting
     # the best one
     for ord, pol0 in enumerate(polys):
         pol = ZZ(ord).factorial() * pol0
         sollya_pol = sum(
             [c.center() * sollya.x**k for k, c in enumerate(pol)])
         dom = RIF(center - rad,
                   center + rad)  # XXX: dangerous when inexact
         err_pol = pol.map_coefficients(lambda c: c - c.squash())
         err = RIF(err_pol(RBF.zero().add_error(rad)))
         with sollya.settings(display=sollya.dyadic):
             logger = logging.getLogger(__name__ + ".sollya.annotate")
             logger.debug("annotatefunction(%s, %s, %s, %s, %s);",
                          sollya_fun, sollya_pol, sollya.SollyaObject(dom),
                          sollya.SollyaObject(err),
                          sollya.SollyaObject(center))
         sollya.annotatefunction(sollya_fun, sollya_pol, dom, err, center)
         sollya_fun = sollya.diff(sollya_fun)
     logger.info("...done")
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)
Пример #3
0
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))
Пример #4
0
    def __init__(self,
                 dop,
                 ini,
                 name="dfinitefun",
                 max_prec=256,
                 max_rad=RBF('inf')):
        self.dop = dop = DifferentialOperator(dop)
        if not isinstance(ini, dict):
            ini = {0: ini}
        if len(ini) != 1:
            # In the future, we should support specifying several vectors of
            # initial values.
            raise NotImplementedError
        self.ini = ini
        self.name = name

        # Global maximum width for the approximation intervals. In the case of
        # equations with no finite singular point, we try to avoid cancellation
        # and interval blowup issues by taking polynomial approximations on
        # intervals on which the general term of the series doesn't grow too
        # large. The expected order of magnitude of the “top of the hump” is
        # about exp(κ·|αx|^(1/κ)) and doesn't depend on the base point. We also
        # let the user impose a maximum width, even in other cases.
        self.max_rad = RBF(max_rad)
        if dop.leading_coefficient().is_constant():
            kappa, alpha = _growth_parameters(dop)
            self.max_rad = self.max_rad.min(1 / (alpha * RBF(kappa)**kappa))
        self.max_prec = max_prec

        self._inivecs = {}
        self._polys = {}

        self._sollya_object = None
        self._sollya_domain = RIF('-inf', 'inf')
        self._max_derivatives = dop.order()
        self._update_approx_hook = (lambda *args: None)
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)
Пример #6
0
 def wrapper(pt, ord, prec):
     if RBF(pt.diameter()) >= self.max_rad / 4:
         return self._known_bound(RBF(pt), post_transform=Dx**ord)
     try:
         val = self.approx(pt, prec, post_transform=Dx**ord)
     except Exception:
         logger.info("pt=%s, ord=%s, prec=%s, error",
                     pt,
                     ord,
                     prec,
                     exc_info=(pt.absolute_diameter() < .5))
         return RIF('nan')
     logger.debug("pt=%s, ord=%s, prec=%s, val=%s", pt, ord, prec, val)
     if not pt.overlaps(self._sollya_domain):
         backtrace = sollya.getbacktrace()
         logger.debug("%s not in %s", pt.str(style='brackets'),
                      self._sollya_domain.str(style='brackets'))
         logger.debug("sollya backtrace: %s", [
             sollya.objectname(t.struct.called_proc) for t in backtrace
         ])
     return val
Пример #7
0
    def bound_kolyvagin(self, D=0, regulator=None,
                           ignore_nonsurj_hypothesis=False):
        r"""
        Given a fundamental discriminant `D \neq -3,-4` that satisfies the
        Heegner hypothesis for `E`, return a list of primes so that
        Kolyvagin's theorem (as in Gross's paper) implies that any
        prime divisor of `Sha` is in this list.

        INPUT:

        - ``D`` - (optional) a fundamental discriminant < -4 that satisfies
          the Heegner hypothesis for `E`; if not given, use the first such `D`
        - ``regulator`` -- (optional) regulator of `E(K)`; if not given, will
          be computed (which could take a long time)
        - ``ignore_nonsurj_hypothesis`` (optional: default ``False``) --
          If ``True``, then gives the bound coming from Heegner point
          index, but without any hypothesis on surjectivity
          of the mod-`p` representation.

        OUTPUT:

        - list - a list of primes such that if `p` divides `Sha(E/K)`, then
          `p` is in this list, unless `E/K` has complex multiplication or
          analytic rank greater than 2 (in which case we return 0).

        - index - the odd part of the index of the Heegner point in the full
          group of `K`-rational points on E.  (If `E` has CM, returns 0.)

        REMARKS:

        1)      We do not have to assume that the Manin constant is 1
                (or a power of 2).  If the Manin constant were
                divisible by a prime, that prime would get included in
                the list of bad primes.

        2)      We assume the Gross-Zagier theorem is true under the
                hypothesis that `gcd(N,D) = 1`, instead of the stronger
                hypothesis `gcd(2\cdot N,D)=1` that is in the original
                Gross-Zagier paper.  That Gross-Zagier is true when
                `gcd(N,D)=1` is "well-known" to the experts, but does not
                seem to written up well in the literature.

        3)      Correctness of the computation is guaranteed using
                interval arithmetic, under the assumption that the
                regulator, square root, and period lattice are
                computed to precision at least `10^{-10}`, i.e., they are
                correct up to addition or a real number with absolute
                value less than `10^{-10}`.

        EXAMPLES::

            sage: E = EllipticCurve('37a')
            sage: E.sha().bound_kolyvagin()
            ([2], 1)
            sage: E = EllipticCurve('141a')
            sage: E.sha().an()
            1
            sage: E.sha().bound_kolyvagin()
            ([2, 7], 49)

        We get no information when the curve has rank 2.::

            sage: E = EllipticCurve('389a')
            sage: E.sha().bound_kolyvagin()
            (0, 0)
            sage: E = EllipticCurve('681b')
            sage: E.sha().an()
            9
            sage: E.sha().bound_kolyvagin()
            ([2, 3], 9)
        """
        E = self.Emin
        if E.has_cm():
            return 0, 0

        if D == 0:
            D = -5
            while not E.satisfies_heegner_hypothesis(D):
                D -= 1

        if not E.satisfies_heegner_hypothesis(D):
            raise ArithmeticError("Discriminant (=%s) must be a fundamental discriminant that satisfies the Heegner hypothesis."%D)
        if D == -3 or D == -4:
            raise ArithmeticError("Discriminant (=%s) must not be -3 or -4."%D)
        eps = E.root_number()
        L1_vanishes = E.lseries().L1_vanishes()
        if eps == 1 and L1_vanishes:
            return 0, 0        # rank even hence >= 2, so Kolyvagin gives nothing.
        alpha = sqrt(abs(D))/(2*E.period_lattice().complex_area())
        F = E.quadratic_twist(D)
        k_E = 2*sqrt(E.conductor()) + 10
        k_F = 2*sqrt(F.conductor()) + 10
        #k_E = 2
        #k_F = 2

        MIN_ERR = 1e-10   # we assume that regulator and
                          # discriminant, etc., computed to this accuracy.
        tries = 0
        while True:
            tries += 1
            if tries >= 6:
                raise RuntimeError("Too many precision increases in bound_kolyvagin")
            if eps == 1:   # E has even rank
                verbose("Conductor of twist = %s"%F.conductor())
                LF1, err_F = F.lseries().deriv_at1(k_F)
                LE1, err_E = E.lseries().at1(k_E)
                err_F = max(err_F, MIN_ERR)
                err_E = max(err_E, MIN_ERR)
                if regulator is not None:
                    hZ = regulator/2
                else:
                    hZ = F.regulator(use_database=True)/2
                #print  alpha * LE1 * LF1 / hZ
                I = RIF(alpha) * RIF(LE1-err_E,LE1+err_E) * RIF(LF1-err_F,LF1+err_F) / hZ
                #print I

            else:          # E has odd rank

                if regulator is not None:
                    hZ = regulator/2
                else:
                    hZ = E.regulator(use_database=True)/2
                LE1, err_E = E.lseries().deriv_at1(k_E)
                LF1, err_F = F.lseries().at1(k_F)
                err_F = max(err_F, MIN_ERR)
                err_E = max(err_E, MIN_ERR)
                #I = alpha * LE1 * LF1 / hZ

                I = RIF(alpha) * RIF(LE1-err_E,LE1+err_E) * RIF(LF1-err_F,LF1+err_F) / hZ

            verbose('interval = %s'%I)
            t, n = I.is_int()
            if t:
                break
            elif I.absolute_diameter() < 1:
                raise RuntimeError("Problem in bound_kolyvagin; square of index is not an integer -- D=%s, I=%s."%(D,I))
            verbose("Doubling bounds")
            k_E *= 2
            k_F *= 2
        # end while

        # We include 2 since Kolyvagin (in Gross) says nothing there
        if n == 0:  return 0, 0  # no bound
        F = factor(n)
        B = [2]
        for p, e in factor(n):
            if p > 2:
                if e%2 != 0:
                    raise RuntimeError("Problem in bound_kolyvagin; square of index is not a perfect square!  D=%s, I=%s, n=%s, e=%s."%(D,I,n,e))
                B.append(p)
            else:
                n /= 2**e  # replace n by its odd part
        if not ignore_nonsurj_hypothesis:
            for p in E.galois_representation().non_surjective():
                B.append(p)
        B = sorted(set([int(x) for x in B]))
        return B, n
Пример #8
0
    def bound_kolyvagin(self, D=0, regulator=None,
                           ignore_nonsurj_hypothesis=False):
        r"""
        Given a fundamental discriminant `D \neq -3,-4` that satisfies the
        Heegner hypothesis for `E`, return a list of primes so that
        Kolyvagin's theorem (as in Gross's paper) implies that any
        prime divisor of `Sha` is in this list.

        INPUT:

        - ``D`` - (optional) a fundamental discriminant < -4 that satisfies the
          Heegner hypothesis for `E`; if not given, use the first such `D`
        - ``regulator`` -- (optional) regulator of `E(K)`; if not given, will
          be computed (which could take a long time)
        - ``ignore_nonsurj_hypothesis`` (optional: default ``False``) --
          If ``True``, then gives the bound coming from Heegner point
          index, but without any hypothesis on surjectivity
          of the mod-`p` representation.

        OUTPUT:

        - list - a list of primes such that if `p` divides `Sha(E/K)`, then
          `p` is in this list, unless `E/K` has complex multiplication or
          analytic rank greater than 2 (in which case we return 0).

        - index - the odd part of the index of the Heegner point in the full
          group of `K`-rational points on E.  (If `E` has CM, returns 0.)

        REMARKS:

        1)      We do not have to assume that the Manin constant is 1
                (or a power of 2).  If the Manin constant were
                divisible by a prime, that prime would get included in
                the list of bad primes.

        2)      We assume the Gross-Zagier theorem is true under the
                hypothesis that `gcd(N,D) = 1`, instead of the stronger
                hypothesis `gcd(2\cdot N,D)=1` that is in the original
                Gross-Zagier paper.  That Gross-Zagier is true when
                `gcd(N,D)=1` is "well-known" to the experts, but does not
                seem to written up well in the literature.

        3)      Correctness of the computation is guaranteed using
                interval arithmetic, under the assumption that the
                regulator, square root, and period lattice are
                computed to precision at least `10^{-10}`, i.e., they are
                correct up to addition or a real number with absolute
                value less than `10^{-10}`.

        EXAMPLES::

            sage: E = EllipticCurve('37a')
            sage: E.sha().bound_kolyvagin()
            ([2], 1)
            sage: E = EllipticCurve('141a')
            sage: E.sha().an()
            1
            sage: E.sha().bound_kolyvagin()
            ([2, 7], 49)

        We get no information when the curve has rank 2.::

            sage: E = EllipticCurve('389a')
            sage: E.sha().bound_kolyvagin()
            (0, 0)
            sage: E = EllipticCurve('681b')
            sage: E.sha().an()
            9
            sage: E.sha().bound_kolyvagin()
            ([2, 3], 9)
        """
        E = self.Emin
        if E.has_cm():
            return 0, 0

        if D == 0:
            D = -5
            while not E.satisfies_heegner_hypothesis(D):
                D -= 1

        if not E.satisfies_heegner_hypothesis(D):
            raise ArithmeticError("Discriminant (=%s) must be a fundamental discriminant that satisfies the Heegner hypothesis."%D)
        if D == -3 or D == -4:
            raise ArithmeticError("Discriminant (=%s) must not be -3 or -4."%D)
        eps = E.root_number()
        L1_vanishes = E.lseries().L1_vanishes()
        if eps == 1 and L1_vanishes:
            return 0, 0        # rank even hence >= 2, so Kolyvagin gives nothing.
        alpha = sqrt(abs(D))/(2*E.period_lattice().complex_area())
        F = E.quadratic_twist(D)
        k_E = 2*sqrt(E.conductor()) + 10
        k_F = 2*sqrt(F.conductor()) + 10
        #k_E = 2
        #k_F = 2

        MIN_ERR = 1e-10   # we assume that regulator and
                          # discriminant, etc., computed to this accuracy.
        tries = 0
        while True:
            tries += 1
            if tries >= 6:
                raise RuntimeError("Too many precision increases in bound_kolyvagin")
            if eps == 1:   # E has even rank
                verbose("Conductor of twist = %s"%F.conductor())
                LF1, err_F = F.lseries().deriv_at1(k_F)
                LE1, err_E = E.lseries().at1(k_E)
                err_F = max(err_F, MIN_ERR)
                err_E = max(err_E, MIN_ERR)
                if regulator != None:
                    hZ = regulator/2
                else:
                    hZ = F.regulator(use_database=True)/2
                #print  alpha * LE1 * LF1 / hZ
                I = RIF(alpha) * RIF(LE1-err_E,LE1+err_E) * RIF(LF1-err_F,LF1+err_F) / hZ
                #print I

            else:          # E has odd rank

                if regulator != None:
                    hZ = regulator/2
                else:
                    hZ = E.regulator(use_database=True)/2
                LE1, err_E = E.lseries().deriv_at1(k_E)
                LF1, err_F = F.lseries().at1(k_F)
                err_F = max(err_F, MIN_ERR)
                err_E = max(err_E, MIN_ERR)
                #I = alpha * LE1 * LF1 / hZ

                I = RIF(alpha) * RIF(LE1-err_E,LE1+err_E) * RIF(LF1-err_F,LF1+err_F) / hZ

            verbose('interval = %s'%I)
            t, n = I.is_int()
            if t:
                break
            elif I.absolute_diameter() < 1:
                raise RuntimeError("Problem in bound_kolyvagin; square of index is not an integer -- D=%s, I=%s."%(D,I))
            verbose("Doubling bounds")
            k_E *= 2
            k_F *= 2
        # end while

        # We include 2 since Kolyvagin (in Gross) says nothing there
        if n == 0:  return 0, 0  # no bound
        F = factor(n)
        B = [2]
        for p, e in factor(n):
            if p > 2:
                if e%2 != 0:
                    raise RuntimeError("Problem in bound_kolyvagin; square of index is not a perfect square!  D=%s, I=%s, n=%s, e=%s."%(D,I,n,e))
                B.append(p)
            else:
                n /= 2**e  # replace n by its odd part
        if not ignore_nonsurj_hypothesis:
            for p in E.galois_representation().non_surjective():
                B.append(p)
        B = list(set([int(x) for x in B]))
        B.sort()
        return B, n
Пример #9
0
    - 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 
Пример #10
0
    * 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 -
           86875789597407167434273839673925325000000) * x**8 -
Пример #11
0
class DFiniteFunction(object):
    r"""
    At the moment, this class just provides a simple caching mechanism for
    repeated evaluations of a D-Finite function on the real line. It may
    evolve to support evaluations on the complex plane, branch cuts, ring
    operations on D-Finite functions, and more. Do not expect any API stability.

    TESTS::

        sage: from ore_algebra import *
        sage: from ore_algebra.analytic.function import DFiniteFunction
        sage: from ore_algebra.analytic.path import Point
        sage: from sage.rings.infinity import AnInfinity

        sage: Dops, x, Dx = DifferentialOperators()

        sage: f = DFiniteFunction(Dx - 1, [1], max_rad=7/8)
        sage: f._disk(Point(pi, dop=Dx-1))
        (7/2, 0.5000000000000000)

        sage: f = DFiniteFunction(Dx^2 - x,
        ....:         [1/(gamma(2/3)*3^(2/3)), -1/(gamma(1/3)*3^(1/3))],
        ....:         name='my_Ai')
        sage: f.plot((-5,5))
        Graphics object consisting of 1 graphics primitive
        sage: f._known_bound(RBF(RIF(1/3, 2/3)), post_transform=Dops.one())
        [0.2...]
        sage: f._known_bound(RBF(RIF(-1, 5.99)), post_transform=Dops.one())
        [+/- ...]
        sage: f._known_bound(RBF(RIF(-1, 6.01)), Dops.one())
        [+/- inf]

        sage: f = DFiniteFunction((x^2 + 1)*Dx^2 + 2*x*Dx, [0, 1])
        sage: f.plot((0, 4))
        Graphics object consisting of 1 graphics primitive
        sage: f._known_bound(RBF(RIF(1,4)), Dops.one())
        [1e+0 +/- 0.3...]
        sage: f.approx(1, post_transform=Dx), f.approx(2, post_transform=Dx)
        ([0.5000000000000...], [0.2000000000000...])
        sage: f._known_bound(RBF(RIF(1.1, 2.9)), post_transform=Dx)
        [+/- 0.4...]
        sage: f._known_bound(RBF(AnInfinity()), Dops.one())
        [+/- inf]

        sage: f = DFiniteFunction((x + 1)*Dx^2 + 2*x*Dx, [0, 1])
        sage: f._known_bound(RBF(RIF(1,10)),Dops.one())
        nan
        sage: f._known_bound(RBF(AnInfinity()), Dops.one())
        nan

        sage: f = DFiniteFunction(Dx^2 + 2*x*Dx, [1, -2/sqrt(pi)], name='my_erfc')
        sage: f._known_bound(RBF(RIF(-1/2,1/2)), post_transform=Dx^2)
        [+/- inf]
        sage: _ = f.approx(1/2, post_transform=Dx^2)
        sage: _ = f.approx(-1/2, post_transform=Dx^2)
        sage: f._known_bound(RBF(RIF(-1/2,1/2)), post_transform=Dx^2)
        [+/- 1.5...]
    """

    # Stupid, but simple and deterministic caching strategy:
    #
    # - To any center c = m·2^k with k ∈ ℤ and m *odd*, we associate the disk of
    #   radius 2^k. Any two disks with the same k have at most one point in
    #   common.
    #
    # - Thus, in the case of an equation with at least one finite singular
    #   point, there is a unique largest disk of the previous collection that
    #   contains any given ordinary x ∈ ℝ \ ℤ·2^(-∞) while staying “far enough”
    #   from the singularities.
    #
    # - When asked to evaluate f at x, we actually compute and store a
    #   polynomial approximation on (the real trace of) the corresponding disk
    #   and/or a vector of initial conditions at its center, which can then be
    #   reused for subsequent evaluations.
    #
    # - We may additionally want to allow disks (of any radius?) centered at
    #   real regular singular points, and perhaps, as a special case, at 0.
    #   These would be used when possible, and one would revert to the other
    #   family otherwise.

    def __init__(self,
                 dop,
                 ini,
                 name="dfinitefun",
                 max_prec=256,
                 max_rad=RBF('inf')):
        self.dop = dop = DifferentialOperator(dop)
        if not isinstance(ini, dict):
            ini = {0: ini}
        if len(ini) != 1:
            # In the future, we should support specifying several vectors of
            # initial values.
            raise NotImplementedError
        self.ini = ini
        self.name = name

        # Global maximum width for the approximation intervals. In the case of
        # equations with no finite singular point, we try to avoid cancellation
        # and interval blowup issues by taking polynomial approximations on
        # intervals on which the general term of the series doesn't grow too
        # large. The expected order of magnitude of the “top of the hump” is
        # about exp(κ·|αx|^(1/κ)) and doesn't depend on the base point. We also
        # let the user impose a maximum width, even in other cases.
        self.max_rad = RBF(max_rad)
        if dop.leading_coefficient().is_constant():
            kappa, alpha = _growth_parameters(dop)
            self.max_rad = self.max_rad.min(1 / (alpha * RBF(kappa)**kappa))
        self.max_prec = max_prec

        self._inivecs = {}
        self._polys = {}

        self._sollya_object = None
        self._sollya_domain = RIF('-inf', 'inf')
        self._max_derivatives = dop.order()
        self._update_approx_hook = (lambda *args: None)

    def __repr__(self):
        return self.name

    @cached_method
    def _is_everywhere_defined(self):
        return not any(
            rt.imag().contains_zero()
            for rt, mult in self.dop.leading_coefficient().roots(CIF))

    def _disk(self, pt):
        assert pt.is_real()
        # Since approximation disks satisfy 2·rad ≤ dist(center, sing), any
        # approximation disk containing pt must have rad ≤ dist(pt, sing)
        max_rad = pt.dist_to_sing().min(self.max_rad)
        # What we want is the largest such disk containing pt
        expo = ZZ(max_rad.log(2).upper().ceil()) - 1  # rad = 2^expo
        logger.log(logging.DEBUG - 2, "max_rad = %s, expo = %s", max_rad, expo)
        while True:
            approx_pt = pt.approx_abs_real(-expo)
            mantissa = (approx_pt.squash() >> expo).floor()
            if ZZ(mantissa) % 2 == 0:
                mantissa += 1
            center = mantissa << expo
            dist = Point(center, pt.dop).dist_to_sing()
            rad = RBF.one() << expo
            logger.log(
                logging.DEBUG - 2,
                "candidate disk: approx_pt = %s, mantissa = %s, "
                "center = %s, dist = %s, rad = %s", approx_pt, mantissa,
                center, dist, rad)
            if safe_ge(dist >> 1, rad):
                break
            expo -= 1
        logger.debug("disk for %s: center=%s, rad=%s", pt, center, rad)
        # pt may be a ball with nonzero radius: check that it is contained in
        # our candidate disk
        log = RBF.zero() if 0 in approx_pt else approx_pt.abs().log(2)
        F = RealBallField(ZZ((expo - log).max(0).upper().ceil()) + 10)
        dist_to_center = (F(approx_pt) - F(center)).abs()
        if not safe_le(dist_to_center, rad):
            assert not safe_gt((approx_pt.squash() - center).squash(), rad)
            logger.info("check that |%s - %s| < %s failed", approx_pt, center,
                        rad)
            return None, None
        # exactify center so that subsequent computations are not limited by the
        # precision of its parent
        center = QQ(center)
        return center, rad

    def _rad(self, center):
        return RBF.one() << QQ(center).valuation(2)

    def _path_to(self, dest, prec=None):
        r"""
        Find a path from a point with known "initial" values to pt
        """
        # TODO:
        # - attempt to start as close as possible to the destination
        #   [and perhaps add logic to change for a starting point with exact
        #   initial values if loosing too much precision]
        # - return a path passing through "interesting" points (and cache the
        #   associated initial vectors)
        start, ini = self.ini.items()[0]
        return ini, [start, dest]

    # Having the update (rather than the full test-and-update) logic in a
    # separate method is convenient to override it in subclasses.
    def _update_approx(self, center, rad, prec, derivatives):
        ini, path = self._path_to(center, prec)
        eps = RBF.one() >> prec
        # keep="all" won't do anything until _path_to returns better paths
        ctx = ancont.Context(self.dop, path, eps, keep="all")
        pairs = ancont.analytic_continuation(ctx, ini=ini)
        for (vert, val) in pairs:
            known = self._inivecs.get(vert)
            if known is None or known[0].accuracy() < val[0][0].accuracy():
                self._inivecs[vert] = [c[0] for c in val]
        logger.info(
            "computing new polynomial approximations: "
            "ini=%s, path=%s, rad=%s, eps=%s, ord=%s", ini, path, rad, eps,
            derivatives)
        polys = polapprox.doit(self.dop,
                               ini=ini,
                               path=path,
                               rad=rad,
                               eps=eps,
                               derivatives=derivatives,
                               x_is_real=True,
                               economization=polapprox.chebyshev_economization)
        logger.info("...done")
        approx = self._polys.get(center, [])
        new_approx = []
        for ord, pol in enumerate(polys):
            if ord >= len(approx) or approx[ord].prec < prec:
                new_approx.append(RealPolApprox(pol, prec))
            else:
                new_approx.append(approx[ord])
        self._update_approx_hook(center, rad, polys)
        self._polys[center] = new_approx
        return polys

    def _sollya_annotate(self, center, rad, polys):
        import sagesollya as sollya
        logger = logging.getLogger(__name__ + ".sollya")
        logger.info("calling annotatefunction() on %s derivatives", len(polys))
        center = QQ(center)
        sollya_fun = self._sollya_object
        # sollya keeps all annotations, let's not bother with selecting
        # the best one
        for ord, pol0 in enumerate(polys):
            pol = ZZ(ord).factorial() * pol0
            sollya_pol = sum(
                [c.center() * sollya.x**k for k, c in enumerate(pol)])
            dom = RIF(center - rad,
                      center + rad)  # XXX: dangerous when inexact
            err_pol = pol.map_coefficients(lambda c: c - c.squash())
            err = RIF(err_pol(RBF.zero().add_error(rad)))
            with sollya.settings(display=sollya.dyadic):
                logger = logging.getLogger(__name__ + ".sollya.annotate")
                logger.debug("annotatefunction(%s, %s, %s, %s, %s);",
                             sollya_fun, sollya_pol, sollya.SollyaObject(dom),
                             sollya.SollyaObject(err),
                             sollya.SollyaObject(center))
            sollya.annotatefunction(sollya_fun, sollya_pol, dom, err, center)
            sollya_fun = sollya.diff(sollya_fun)
        logger.info("...done")

    def _known_bound(self, iv, post_transform):
        post_transform = normalize_post_transform(self.dop, post_transform)
        Balls = iv.parent()
        Ivs = RealIntervalField(Balls.precision())
        mid = [
            c for c in self._polys.keys()
            if Balls(c).add_error(self._rad(c)).overlaps(iv)
        ]
        mid.sort()
        rad = [self._rad(c) for c in mid]
        crude_bound = (Balls(AnInfinity())
                       if self._is_everywhere_defined() else Balls('nan'))
        if len(mid) < 1 or not mid[0] - rad[0] <= iv <= mid[-1] + rad[-1]:
            return crude_bound
        if not all(
                safe_eq(mid[i] + rad[i], mid[i + 1] - rad[i + 1])
                for i in range(len(mid) - 1)):
            return crude_bound
        bound = None
        for c, r in zip(mid, rad):
            if len(self._polys[c]) <= post_transform.order():
                return crude_bound
            polys = [a.pol for a in self._polys[c]]
            dom = Balls(Ivs(Balls(c).add_error(r)).intersection(Ivs(iv)))
            reduced_dom = dom - c
            img = sum(
                ZZ(j).factorial() * coeff(dom) * polys[j](reduced_dom)
                for j, coeff in enumerate(post_transform))
            bound = img if bound is None else bound.union(img)
        return bound

    def approx(self, pt, prec=None, post_transform=None):
        r"""
        TESTS::

            sage: from ore_algebra import *
            sage: from ore_algebra.analytic.function import DFiniteFunction
            sage: DiffOps, x, Dx = DifferentialOperators()

            sage: h = DFiniteFunction(Dx^3-1, [0, 0, 1])
            sage: h.approx(0, post_transform=Dx^2)
            [2.0000000000000...]

            sage: f = DFiniteFunction((x^2 + 1)*Dx^2 + 2*x*Dx, [0, 1], max_prec=20)
            sage: f.approx(1/3, prec=10)
            [0.32...]
            sage: f.approx(1/3, prec=40)
            [0.321750554396...]
            sage: f.approx(1/3, prec=10, post_transform=Dx)
            [0.9...]
            sage: f.approx(1/3, prec=40, post_transform=Dx)
            [0.900000000000...]
            sage: f.approx(1/3, prec=10, post_transform=Dx^2)
            [-0.54...]
            sage: f.approx(1/3, prec=40, post_transform=Dx^2)
            [-0.540000000000...]

        """
        pt = Point(pt, self.dop)
        if prec is None:
            prec = _guess_prec(pt)
        if post_transform is None:
            post_transform = self.dop.parent().one()
        derivatives = min(post_transform.order() + 1, self._max_derivatives)
        post_transform = normalize_post_transform(self.dop, post_transform)
        if prec >= self.max_prec or not pt.is_real():
            logger.info(
                "performing high-prec evaluation "
                "(pt=%s, prec=%s, post_transform=%s)", pt, prec,
                post_transform)
            ini, path = self._path_to(pt)
            eps = RBF.one() >> prec
            return self.dop.numerical_solution(ini,
                                               path,
                                               eps,
                                               post_transform=post_transform)
        center, rad = self._disk(pt)
        if center is None:
            # raise NotImplementedError
            logger.info("falling back on generic evaluator")
            ini, path = self._path_to(pt)
            eps = RBF.one() >> prec
            return self.dop.numerical_solution(ini,
                                               path,
                                               eps,
                                               post_transform=post_transform)
        approx = self._polys.get(center, [])
        Balls = RealBallField(prec)
        # due to the way the polynomials are recomputed, the precisions attached
        # to the successive derivatives are nonincreasing
        if (len(approx) < derivatives or approx[derivatives - 1].prec < prec):
            polys = self._update_approx(center, rad, prec, derivatives)
        else:
            polys = [a.pol for a in approx]
        bpt = Balls(pt.value)
        reduced_pt = bpt - Balls(center)
        val = sum(
            ZZ(j).factorial() * coeff(bpt) * polys[j](reduced_pt)
            for j, coeff in enumerate(post_transform))
        return val

    def __call__(self, x, prec=None):
        return self.approx(x, prec=prec)

    def plot(self, xrange, **options):
        r"""
        Plot this function.

        The plot is intended to give an idea of the accuracy of the evaluations
        that led to it. However, it may not be a rigorous enclosure of the graph
        of the function.

        EXAMPLES::

            sage: from ore_algebra import *
            sage: from ore_algebra.analytic.function import DFiniteFunction
            sage: DiffOps, x, Dx = DifferentialOperators()

            sage: f = DFiniteFunction(Dx^2 - x,
            ....:         [1/(gamma(2/3)*3^(2/3)), -1/(gamma(1/3)*3^(1/3))])
            sage: plot(f, (-10, 5), color='black')
            Graphics object consisting of 1 graphics primitive
        """
        mids = generate_plot_points(lambda x: self.approx(x, 20).mid(),
                                    xrange,
                                    plot_points=200)
        ivs = [(x, self.approx(x, 20)) for x, _ in mids]
        bounds = [(x, y.upper()) for x, y in ivs]
        bounds += [(x, y.lower()) for x, y in reversed(ivs)]
        options.setdefault('aspect_ratio', 'automatic')
        g = plot.polygon(bounds, thickness=1, **options)
        return g

    def plot_known(self):
        r"""
        TESTS::

            sage: from ore_algebra import *
            sage: from ore_algebra.analytic.function import DFiniteFunction
            sage: DiffOps, x, Dx = DifferentialOperators()

            sage: f = DFiniteFunction((x^2 + 1)*Dx^2 + 2*x*Dx, [0, 1])
            sage: f(-10, 100) # long time
            [-1.4711276743037345918528755717...]
            sage: f.approx(5, post_transform=Dx) # long time
            [0.038461538461538...]
            sage: f.plot_known() # long time
            Graphics object consisting of ... graphics primitives
        """
        g = plot.Graphics()
        for center, polys in self._polys.iteritems():
            center, rad = self._disk(Point(center, self.dop))
            xrange = (center - rad).mid(), (center + rad).mid()
            for i, a in enumerate(polys):
                # color palette copied from sage.plot.plot.plot
                color = plot.Color((0.66666 + i * 0.61803) % 1,
                                   1,
                                   0.4,
                                   space='hsl')
                Balls = a.pol.base_ring()
                g += plot.plot(lambda x: a.pol(Balls(x)).mid(),
                               xrange,
                               color=color)
                g += plot.text(str(a.prec), (center, a.pol(center).mid()),
                               color=color)
        for point, ini in self._inivecs.iteritems():
            g += plot.point2d((point, 0), size=50)
        return g

    def _sollya_(self):
        r"""
        EXAMPLES::

            sage: import logging; logging.basicConfig(level=logging.INFO)
            sage: logger = logging.getLogger("ore_algebra.analytic.function.sollya")
            sage: logger.setLevel(logging.DEBUG)

            sage: import ore_algebra
            sage: DiffOps, x, Dx = ore_algebra.DifferentialOperators()
            sage: from ore_algebra.analytic.function import DFiniteFunction
            sage: f = DFiniteFunction(Dx - 1, [1])

            sage: import sagesollya as sollya # optional - sollya
            sage: sollya.plot(f, sollya.Interval(0, 1)) # not tested
            ...

        """
        if self._sollya_object is not None:
            return self._sollya_object
        import sagesollya as sollya
        logger = logging.getLogger(__name__ + ".sollya.eval")
        Dx = self.dop.parent().gen()

        def wrapper(pt, ord, prec):
            if RBF(pt.diameter()) >= self.max_rad / 4:
                return self._known_bound(RBF(pt), post_transform=Dx**ord)
            try:
                val = self.approx(pt, prec, post_transform=Dx**ord)
            except Exception:
                logger.info("pt=%s, ord=%s, prec=%s, error",
                            pt,
                            ord,
                            prec,
                            exc_info=(pt.absolute_diameter() < .5))
                return RIF('nan')
            logger.debug("pt=%s, ord=%s, prec=%s, val=%s", pt, ord, prec, val)
            if not pt.overlaps(self._sollya_domain):
                backtrace = sollya.getbacktrace()
                logger.debug("%s not in %s", pt.str(style='brackets'),
                             self._sollya_domain.str(style='brackets'))
                logger.debug("sollya backtrace: %s", [
                    sollya.objectname(t.struct.called_proc) for t in backtrace
                ])
            return val

        wrapper.__name__ = self.name
        self._sollya_object = sollya.sagefunction(wrapper)
        for pt, ini in self.ini.iteritems():
            pt = RIF(pt)
            if pt.is_exact():
                fun = self._sollya_object
                for ord, val in enumerate(ini):
                    try:
                        sollya.annotatefunction(fun, val, pt, RIF.zero())
                    except TypeError:
                        logger.info("annotation failed: D^%s(%s)(%s) = %s",
                                    ord, self.name, pt, val)
                    fun = sollya.diff(fun)
        self._update_approx_hook = self._sollya_annotate
        return self._sollya_object
Пример #12
0
    def _sollya_(self):
        r"""
        EXAMPLES::

            sage: import logging; logging.basicConfig(level=logging.INFO)
            sage: logger = logging.getLogger("ore_algebra.analytic.function.sollya")
            sage: logger.setLevel(logging.DEBUG)

            sage: import ore_algebra
            sage: DiffOps, x, Dx = ore_algebra.DifferentialOperators()
            sage: from ore_algebra.analytic.function import DFiniteFunction
            sage: f = DFiniteFunction(Dx - 1, [1])

            sage: import sagesollya as sollya # optional - sollya
            sage: sollya.plot(f, sollya.Interval(0, 1)) # not tested
            ...

        """
        if self._sollya_object is not None:
            return self._sollya_object
        import sagesollya as sollya
        logger = logging.getLogger(__name__ + ".sollya.eval")
        Dx = self.dop.parent().gen()

        def wrapper(pt, ord, prec):
            if RBF(pt.diameter()) >= self.max_rad / 4:
                return self._known_bound(RBF(pt), post_transform=Dx**ord)
            try:
                val = self.approx(pt, prec, post_transform=Dx**ord)
            except Exception:
                logger.info("pt=%s, ord=%s, prec=%s, error",
                            pt,
                            ord,
                            prec,
                            exc_info=(pt.absolute_diameter() < .5))
                return RIF('nan')
            logger.debug("pt=%s, ord=%s, prec=%s, val=%s", pt, ord, prec, val)
            if not pt.overlaps(self._sollya_domain):
                backtrace = sollya.getbacktrace()
                logger.debug("%s not in %s", pt.str(style='brackets'),
                             self._sollya_domain.str(style='brackets'))
                logger.debug("sollya backtrace: %s", [
                    sollya.objectname(t.struct.called_proc) for t in backtrace
                ])
            return val

        wrapper.__name__ = self.name
        self._sollya_object = sollya.sagefunction(wrapper)
        for pt, ini in self.ini.iteritems():
            pt = RIF(pt)
            if pt.is_exact():
                fun = self._sollya_object
                for ord, val in enumerate(ini):
                    try:
                        sollya.annotatefunction(fun, val, pt, RIF.zero())
                    except TypeError:
                        logger.info("annotation failed: D^%s(%s)(%s) = %s",
                                    ord, self.name, pt, val)
                    fun = sollya.diff(fun)
        self._update_approx_hook = self._sollya_annotate
        return self._sollya_object
    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)