Beispiel #1
0
    def quadratic_twist(self, D=None):
        """
        Return the quadratic twist of this curve by ``D``.

        INPUT:

        - ``D`` (default None) the twisting parameter (see below).

        In characteristics other than 2, `D` must be nonzero, and the
        twist is isomorphic to self after adjoining `\sqrt(D)` to the
        base.

        In characteristic 2, `D` is arbitrary, and the twist is
        isomorphic to self after adjoining a root of `x^2+x+D` to the
        base.

        In characteristic 2 when `j=0`, this is not implemented.

        If the base field `F` is finite, `D` need not be specified,
        and the curve returned is the unique curve (up to isomorphism)
        defined over `F` isomorphic to the original curve over the
        quadratic extension of `F` but not over `F` itself.  Over
        infinite fields, an error is raised if `D` is not given.

        EXAMPLES::

            sage: E = EllipticCurve([GF(1103)(1), 0, 0, 107, 340]); E
            Elliptic Curve defined by y^2 + x*y  = x^3 + 107*x + 340 over Finite Field of size 1103
            sage: F=E.quadratic_twist(-1); F
            Elliptic Curve defined by y^2  = x^3 + 1102*x^2 + 609*x + 300 over Finite Field of size 1103
            sage: E.is_isomorphic(F)
            False
            sage: E.is_isomorphic(F,GF(1103^2,'a'))
            True

        A characteristic 2 example::

            sage: E=EllipticCurve(GF(2),[1,0,1,1,1])
            sage: E1=E.quadratic_twist(1)
            sage: E.is_isomorphic(E1)
            False
            sage: E.is_isomorphic(E1,GF(4,'a'))
            True

        Over finite fields, the twisting parameter may be omitted::

            sage: k.<a> = GF(2^10)
            sage: E = EllipticCurve(k,[a^2,a,1,a+1,1])
            sage: Et = E.quadratic_twist()
            sage: Et # random (only determined up to isomorphism)
            Elliptic Curve defined by y^2 + x*y  = x^3 + (a^7+a^4+a^3+a^2+a+1)*x^2 + (a^8+a^6+a^4+1) over Finite Field in a of size 2^10
            sage: E.is_isomorphic(Et)
            False
            sage: E.j_invariant()==Et.j_invariant()
            True

            sage: p=next_prime(10^10)
            sage: k = GF(p)
            sage: E = EllipticCurve(k,[1,2,3,4,5])
            sage: Et = E.quadratic_twist()
            sage: Et # random (only determined up to isomorphism)
            Elliptic Curve defined by y^2  = x^3 + 7860088097*x^2 + 9495240877*x + 3048660957 over Finite Field of size 10000000019
            sage: E.is_isomorphic(Et)
            False
            sage: k2 = GF(p^2,'a')
            sage: E.change_ring(k2).is_isomorphic(Et.change_ring(k2))
            True
        """
        K=self.base_ring()
        char=K.characteristic()

        if D is None:
            if K.is_finite():
                x = rings.polygen(K)
                if char==2:
                    # We find D such that x^2+x+D is irreducible. If the
                    # degree is odd we can take D=1; otherwise it suffices to
                    # consider odd powers of a generator.
                    D = K(1)
                    if K.degree()%2==0:
                        D = K.gen()
                        a = D**2
                        while len((x**2+x+D).roots())>0:
                            D *= a
                else:
                    # We could take a multiplicative generator but
                    # that might be expensive to compute; otherwise
                    # half the elements will do
                    D = K.random_element()
                    while len((x**2-D).roots())>0:
                        D = K.random_element()
            else:
                raise ValueError("twisting parameter D must be specified over infinite fields.")
        else:
            try:
                D=K(D)
            except ValueError:
                raise ValueError("twisting parameter D must be in the base field.")

            if char!=2 and D.is_zero():
                raise ValueError("twisting parameter D must be nonzero when characteristic is not 2")

        if char!=2:
            b2,b4,b6,b8=self.b_invariants()
            # E is isomorphic to  [0,b2,0,8*b4,16*b6]
            return EllipticCurve(K,[0,b2*D,0,8*b4*D**2,16*b6*D**3])

        # now char==2
        if self.j_invariant() !=0: # iff a1!=0
            a1,a2,a3,a4,a6=self.ainvs()
            E0=self.change_weierstrass_model(a1,a3/a1,0,(a1**2*a4+a3**2)/a1**3)
            # which has the form = [1,A2,0,0,A6]
            assert E0.a1()==K(1)
            assert E0.a3()==K(0)
            assert E0.a4()==K(0)
            return EllipticCurve(K,[1,E0.a2()+D,0,0,E0.a6()])
        else:
            raise ValueError("Quadratic twist not implemented in char 2 when j=0")
Beispiel #2
0
    def characters(self):
        r"""
        Return the two conjugate characters of `K^\times`, where `K` is some
        quadratic extension of `\QQ_p`, defining this representation. This is
        fully implemented only in the case where the power of `p` dividing the
        level of the form is even, in which case `K` is the unique unramified
        quadratic extension of `\QQ_p`.

        EXAMPLES:

        The first example from _[LW11]::

            sage: f = Newform('50a')
            sage: Pi = LocalComponent(f, 5)
            sage: chars = Pi.characters(); chars
            [
            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> d, 5 |--> 1,
            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -d - 1, 5 |--> 1
            ]
            sage: chars[0].base_ring()
            Number Field in d with defining polynomial x^2 + x + 1

        These characters are interchanged by the Frobenius automorphism of `\mathbb{F}_{25}`::

            sage: chars[0] == chars[1]**5
            True

        A more complicated example (higher weight and nontrivial central character)::

            sage: f = Newforms(GammaH(25, [6]), 3, names='j')[0]; f
            q + j0*q^2 + 1/3*j0^3*q^3 - 1/3*j0^2*q^4 + O(q^6)
            sage: Pi = LocalComponent(f, 5)
            sage: Pi.characters()
            [
            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> d, 5 |--> 5,
            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -d - 1/3*j0^3, 5 |--> 5
            ]
            sage: Pi.characters()[0].base_ring()
            Number Field in d with defining polynomial x^2 + 1/3*j0^3*x - 1/3*j0^2 over its base field

        .. warning::

            The above output isn't actually the same as in Example 2 of
            _[LW11], due to an error in the published paper (correction
            pending) -- the published paper has the inverses of the above
            characters.

        A higher level example::

            sage: f = Newform('81a', names='j'); f
            q + j0*q^2 + q^4 - j0*q^5 + O(q^6)
            sage: LocalComponent(f, 3).characters()  # long time (12s on sage.math, 2012)
            [
            Character of unramified extension Q_3(s)* (s^2 + 2*s + 2 = 0), of level 2, mapping -2*s |--> -2*d - j0, 4 |--> 1, 3*s + 1 |--> -j0*d - 2, 3 |--> 1,
            Character of unramified extension Q_3(s)* (s^2 + 2*s + 2 = 0), of level 2, mapping -2*s |--> 2*d + j0, 4 |--> 1, 3*s + 1 |--> j0*d + 1, 3 |--> 1
            ]

        In the ramified case, it's not fully implemented, and just returns a
        string indicating which ramified extension is being considered::

            sage: Pi = LocalComponent(Newform('27a'), 3)
            sage: Pi.characters()
            'Character of Q_3(sqrt(-3))'
            sage: Pi = LocalComponent(Newform('54a'), 3)
            sage: Pi.characters()
            'Character of Q_3(sqrt(3))'
        """
        T = self.type_space()
        if self.conductor() % 2 == 0:

            G = SmoothCharacterGroupUnramifiedQuadratic(
                self.prime(), self.coefficient_field())
            n = self.conductor() // 2
            g = G.quotient_gen(n)
            m = g.matrix().change_ring(ZZ).list()
            tr = (~T.rho(m)).trace()

            # The inverse is needed here because T is the *homological* type space,
            # which is dual to the cohomological one that defines the local component.

            X = polygen(self.coefficient_field())
            theta_poly = X**2 - (-1)**n * tr * X + self.central_character()(
                g.norm())
            if theta_poly.is_irreducible():
                F = self.coefficient_field().extension(theta_poly, "d")
                G = G.base_extend(F)
            chi1, chi2 = [
                G.extend_character(n, self.central_character(), x[0])
                for x in theta_poly.roots(G.base_ring())
            ]

            # Consistency checks
            assert chi1.restrict_to_Qp() == chi2.restrict_to_Qp(
            ) == self.central_character()
            assert chi1 * chi2 == chi1.parent().compose_with_norm(
                self.central_character())

            return Sequence([chi1, chi2], check=False, cr=True)

        else:
            # The ramified case.

            p = self.prime()

            if p == 2:
                # The ramified 2-adic representations aren't classified by admissible pairs. Die.
                raise NotImplementedError(
                    "Computation with ramified 2-adic representations not implemented"
                )

            if p % 4 == 3:
                a = ZZ(-1)
            else:
                a = ZZ(Zmod(self.prime()).quadratic_nonresidue())

            tr1 = (~T.rho([0, 1, a * p, 0])).trace()
            tr2 = (~T.rho([0, 1, p, 0])).trace()

            if tr1 == tr2 == 0:
                # This *can* happen. E.g. if the central character satisfies
                # chi(-1) = -1, then we have theta(pi) + theta(-pi) = theta(pi)
                # * (1 + -1) = 0. In this case, one can presumably identify
                # the character and the extension by some more subtle argument
                # but I don't know of a good way to automate the process.
                raise NotImplementedError(
                    "Can't identify ramified quadratic extension -- both traces zero"
                )
            elif tr1 == 0:
                return "Character of Q_%s(sqrt(%s))" % (p, p)

            elif tr2 == 0:
                return "Character of Q_%s(sqrt(%s))" % (p, a * p)

            else:
                # At least one of the traces is *always* 0, since the type
                # space has to be isomorphic to its twist by the (ramified
                # quadratic) character corresponding to the quadratic
                # extension.
                raise RuntimeError("Can't get here!")
Beispiel #3
0
    def is_quadratic_twist(self, other):
        r"""
        Determine whether this curve is a quadratic twist of another.

        INPUT:

        - ``other`` -- an elliptic curves with the same base field as self.

        OUTPUT:

        Either 0, if the curves are not quadratic twists, or `D` if
        ``other`` is ``self.quadratic_twist(D)`` (up to isomorphism).
        If ``self`` and ``other`` are isomorphic, returns 1.

        If the curves are defined over `\mathbb{Q}`, the output `D` is
        a squarefree integer.

        .. note::

           Not fully implemented in characteristic 2, or in
           characteristic 3 when both `j`-invariants are 0.

        EXAMPLES::

            sage: E = EllipticCurve('11a1')
            sage: Et = E.quadratic_twist(-24)
            sage: E.is_quadratic_twist(Et)
            -6

            sage: E1=EllipticCurve([0,0,1,0,0])
            sage: E1.j_invariant()
            0
            sage: E2=EllipticCurve([0,0,0,0,2])
            sage: E1.is_quadratic_twist(E2)
            2
            sage: E1.is_quadratic_twist(E1)
            1
            sage: type(E1.is_quadratic_twist(E1)) == type(E1.is_quadratic_twist(E2))   #trac 6574
            True

        ::

            sage: E1=EllipticCurve([0,0,0,1,0])
            sage: E1.j_invariant()
            1728
            sage: E2=EllipticCurve([0,0,0,2,0])
            sage: E1.is_quadratic_twist(E2)
            0
            sage: E2=EllipticCurve([0,0,0,25,0])
            sage: E1.is_quadratic_twist(E2)
            5

        ::

            sage: F = GF(101)
            sage: E1 = EllipticCurve(F,[4,7])
            sage: E2 = E1.quadratic_twist()
            sage: D = E1.is_quadratic_twist(E2); D!=0
            True
            sage: F = GF(101)
            sage: E1 = EllipticCurve(F,[4,7])
            sage: E2 = E1.quadratic_twist()
            sage: D = E1.is_quadratic_twist(E2)
            sage: E1.quadratic_twist(D).is_isomorphic(E2)
            True
            sage: E1.is_isomorphic(E2)
            False
            sage: F2 = GF(101^2,'a')
            sage: E1.change_ring(F2).is_isomorphic(E2.change_ring(F2))
            True

        A characteristic 3 example::

            sage: F = GF(3^5,'a')
            sage: E1 = EllipticCurve_from_j(F(1))
            sage: E2 = E1.quadratic_twist(-1)
            sage: D = E1.is_quadratic_twist(E2); D!=0
            True
            sage: E1.quadratic_twist(D).is_isomorphic(E2)
            True

        ::

            sage: E1 = EllipticCurve_from_j(F(0))
            sage: E2 = E1.quadratic_twist()
            sage: D = E1.is_quadratic_twist(E2); D
            1
            sage: E1.is_isomorphic(E2)
            True

        """
        from sage.schemes.elliptic_curves.ell_generic import is_EllipticCurve
        E = self
        F = other
        if not is_EllipticCurve(E) or not is_EllipticCurve(F):
            raise ValueError("arguments are not elliptic curves")
        K = E.base_ring()
        zero = K.zero_element()
        if not K == F.base_ring():
            return zero
        j=E.j_invariant()
        if  j != F.j_invariant():
            return zero

        if E.is_isomorphic(F):
            if K is rings.QQ:
                return rings.ZZ(1)
            return K.one_element()

        char=K.characteristic()

        if char==2:
            raise NotImplementedError("not implemented in characteristic 2")
        elif char==3:
            if j==0:
                raise NotImplementedError("not implemented in characteristic 3 for curves of j-invariant 0")
            D = E.b2()/F.b2()

        else:
            # now char!=2,3:
            c4E,c6E = E.c_invariants()
            c4F,c6F = F.c_invariants()

            if j==0:
                um = c6E/c6F
                x=rings.polygen(K)
                ulist=(x**3-um).roots(multiplicities=False)
                if len(ulist)==0:
                    D = zero
                else:
                    D = ulist[0]
            elif j==1728:
                um=c4E/c4F
                x=rings.polygen(K)
                ulist=(x**2-um).roots(multiplicities=False)
                if len(ulist)==0:
                    D = zero
                else:
                    D = ulist[0]
            else:
                D = (c6E*c4F)/(c6F*c4E)

        # Normalization of output:

        if D.is_zero():
            return D

        if K is rings.QQ:
            D = D.squarefree_part()

        assert E.quadratic_twist(D).is_isomorphic(F)

        return D
Beispiel #4
0
    def quadratic_twist(self, D=None):
        """
        Return the quadratic twist of this curve by ``D``.

        INPUT:

        - ``D`` (default None) the twisting parameter (see below).

        In characteristics other than 2, `D` must be nonzero, and the
        twist is isomorphic to self after adjoining `\sqrt(D)` to the
        base.

        In characteristic 2, `D` is arbitrary, and the twist is
        isomorphic to self after adjoining a root of `x^2+x+D` to the
        base.

        In characteristic 2 when `j=0`, this is not implemented.

        If the base field `F` is finite, `D` need not be specified,
        and the curve returned is the unique curve (up to isomorphism)
        defined over `F` isomorphic to the original curve over the
        quadratic extension of `F` but not over `F` itself.  Over
        infinite fields, an error is raised if `D` is not given.

        EXAMPLES::

            sage: E = EllipticCurve([GF(1103)(1), 0, 0, 107, 340]); E
            Elliptic Curve defined by y^2 + x*y  = x^3 + 107*x + 340 over Finite Field of size 1103
            sage: F=E.quadratic_twist(-1); F
            Elliptic Curve defined by y^2  = x^3 + 1102*x^2 + 609*x + 300 over Finite Field of size 1103
            sage: E.is_isomorphic(F)
            False
            sage: E.is_isomorphic(F,GF(1103^2,'a'))
            True

        A characteristic 2 example::

            sage: E=EllipticCurve(GF(2),[1,0,1,1,1])
            sage: E1=E.quadratic_twist(1)
            sage: E.is_isomorphic(E1)
            False
            sage: E.is_isomorphic(E1,GF(4,'a'))
            True

        Over finite fields, the twisting parameter may be omitted::

            sage: k.<a> = GF(2^10)
            sage: E = EllipticCurve(k,[a^2,a,1,a+1,1])
            sage: Et = E.quadratic_twist()
            sage: Et # random (only determined up to isomorphism)
            Elliptic Curve defined by y^2 + x*y  = x^3 + (a^7+a^4+a^3+a^2+a+1)*x^2 + (a^8+a^6+a^4+1) over Finite Field in a of size 2^10
            sage: E.is_isomorphic(Et)
            False
            sage: E.j_invariant()==Et.j_invariant()
            True

            sage: p=next_prime(10^10)
            sage: k = GF(p)
            sage: E = EllipticCurve(k,[1,2,3,4,5])
            sage: Et = E.quadratic_twist()
            sage: Et # random (only determined up to isomorphism)
            Elliptic Curve defined by y^2  = x^3 + 7860088097*x^2 + 9495240877*x + 3048660957 over Finite Field of size 10000000019
            sage: E.is_isomorphic(Et)
            False
            sage: k2 = GF(p^2,'a')
            sage: E.change_ring(k2).is_isomorphic(Et.change_ring(k2))
            True
        """
        K = self.base_ring()
        char = K.characteristic()

        if D is None:
            if K.is_finite():
                x = rings.polygen(K)
                if char == 2:
                    # We find D such that x^2+x+D is irreducible. If the
                    # degree is odd we can take D=1; otherwise it suffices to
                    # consider odd powers of a generator.
                    D = K(1)
                    if K.degree() % 2 == 0:
                        D = K.gen()
                        a = D**2
                        while len((x**2 + x + D).roots()) > 0:
                            D *= a
                else:
                    # We could take a multiplicative generator but
                    # that might be expensive to compute; otherwise
                    # half the elements will do
                    D = K.random_element()
                    while len((x**2 - D).roots()) > 0:
                        D = K.random_element()
            else:
                raise ValueError(
                    "twisting parameter D must be specified over infinite fields."
                )
        else:
            try:
                D = K(D)
            except ValueError:
                raise ValueError(
                    "twisting parameter D must be in the base field.")

            if char != 2 and D.is_zero():
                raise ValueError(
                    "twisting parameter D must be nonzero when characteristic is not 2"
                )

        if char != 2:
            b2, b4, b6, b8 = self.b_invariants()
            # E is isomorphic to  [0,b2,0,8*b4,16*b6]
            return EllipticCurve(K,
                                 [0, b2 * D, 0, 8 * b4 * D**2, 16 * b6 * D**3])

        # now char==2
        if self.j_invariant() != 0:  # iff a1!=0
            a1, a2, a3, a4, a6 = self.ainvs()
            E0 = self.change_weierstrass_model(a1, a3 / a1, 0,
                                               (a1**2 * a4 + a3**2) / a1**3)
            # which has the form = [1,A2,0,0,A6]
            assert E0.a1() == K(1)
            assert E0.a3() == K(0)
            assert E0.a4() == K(0)
            return EllipticCurve(K, [1, E0.a2() + D, 0, 0, E0.a6()])
        else:
            raise ValueError(
                "Quadratic twist not implemented in char 2 when j=0")
Beispiel #5
0
    def characters(self):
        r"""
        Return the two conjugate characters of `K^\times`, where `K` is some
        quadratic extension of `\QQ_p`, defining this representation. This is
        fully implemented only in the case where the power of `p` dividing the
        level of the form is even, in which case `K` is the unique unramified
        quadratic extension of `\QQ_p`.

        EXAMPLES:

        The first example from _[LW11]::

            sage: f = Newform('50a')
            sage: Pi = LocalComponent(f, 5)
            sage: chars = Pi.characters(); chars
            [
            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> d, 5 |--> 1,
            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -d - 1, 5 |--> 1
            ]
            sage: chars[0].base_ring()
            Number Field in d with defining polynomial x^2 + x + 1
        
        These characters are interchanged by the Frobenius automorphism of `\mathbb{F}_{25}`::

            sage: chars[0] == chars[1]**5
            True

        A more complicated example (higher weight and nontrivial central character)::
            
            sage: f = Newforms(GammaH(25, [6]), 3, names='j')[0]; f
            q + j0*q^2 + 1/3*j0^3*q^3 - 1/3*j0^2*q^4 + O(q^6)
            sage: Pi = LocalComponent(f, 5)
            sage: Pi.characters()
            [
            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> d, 5 |--> 5,
            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -d - 1/3*j0^3, 5 |--> 5
            ]
            sage: Pi.characters()[0].base_ring()
            Number Field in d with defining polynomial x^2 + 1/3*j0^3*x - 1/3*j0^2 over its base field

        .. warning::
            
            The above output isn't actually the same as in Example 2 of
            _[LW11], due to an error in the published paper (correction
            pending) -- the published paper has the inverses of the above
            characters. 

        A higher level example::

            sage: f = Newform('81a', names='j'); f
            q + j0*q^2 + q^4 - j0*q^5 + O(q^6)
            sage: LocalComponent(f, 3).characters()
            [
            Character of unramified extension Q_3(s)* (s^2 + 2*s + 2 = 0), of level 2, mapping -2*s |--> -2*d - j0, 4 |--> 1, 3*s + 1 |--> -j0*d - 2, 3 |--> 1,
            Character of unramified extension Q_3(s)* (s^2 + 2*s + 2 = 0), of level 2, mapping -2*s |--> 2*d + j0, 4 |--> 1, 3*s + 1 |--> j0*d + 1, 3 |--> 1
            ]

        In the ramified case, it's not fully implemented, and just returns a
        string indicating which ramified extension is being considered::

            sage: Pi = LocalComponent(Newform('27a'), 3)
            sage: Pi.characters()
            'Character of Q_3(sqrt(-3))'
            sage: Pi = LocalComponent(Newform('54a'), 3)
            sage: Pi.characters()
            'Character of Q_3(sqrt(3))'
        """
        T = self.type_space()
        if self.conductor() % 2 == 0:

            G = SmoothCharacterGroupUnramifiedQuadratic(self.prime(), self.coefficient_field())
            n = self.conductor() // 2
            g = G.quotient_gen(n)
            m = g.matrix().change_ring(ZZ).list()
            tr = (~T.rho(m)).trace()
            
            # The inverse is needed here because T is the *homological* type space,
            # which is dual to the cohomological one that defines the local component.

            X = polygen(self.coefficient_field())
            theta_poly = X**2 - (-1)**n*tr*X + self.central_character()(g.norm())
            if theta_poly.is_irreducible():
                F = self.coefficient_field().extension(theta_poly, "d")
                G = G.base_extend(F)
            chi1, chi2 = [G.extend_character(n, self.central_character(), x[0]) for x in theta_poly.roots(G.base_ring())]

            # Consistency checks
            assert chi1.restrict_to_Qp() == chi2.restrict_to_Qp() == self.central_character()
            assert chi1*chi2 == chi1.parent().compose_with_norm(self.central_character())

            return Sequence([chi1, chi2], check=False, cr=True)

        else:
            # The ramified case.
            
            p = self.prime()
            
            if p == 2: 
                # The ramified 2-adic representations aren't classified by admissible pairs. Die.
                raise NotImplementedError( "Computation with ramified 2-adic representations not implemented" )

            if p % 4 == 3: 
                a = ZZ(-1)
            else: 
                a = ZZ(Zmod(self.prime()).quadratic_nonresidue())

            tr1 = (~T.rho([0,1,a*p, 0])).trace()
            tr2 = (~T.rho([0,1,p,0])).trace()

            if tr1 == tr2 == 0: 
                # This *can* happen. E.g. if the central character satisfies
                # chi(-1) = -1, then we have theta(pi) + theta(-pi) = theta(pi)
                # * (1 + -1) = 0. In this case, one can presumably identify
                # the character and the extension by some more subtle argument
                # but I don't know of a good way to automate the process.
                raise NotImplementedError( "Can't identify ramified quadratic extension -- both traces zero" )
            elif tr1 == 0:
                return "Character of Q_%s(sqrt(%s))" % (p, p)

            elif tr2 == 0:
                return "Character of Q_%s(sqrt(%s))" % (p, a*p)

            else:
                # At least one of the traces is *always* 0, since the type
                # space has to be isomorphic to its twist by the (ramified
                # quadratic) character corresponding to the quadratic
                # extension. 
                raise RuntimeError( "Can't get here!" )
Beispiel #6
0
    def is_quadratic_twist(self, other):
        r"""
        Determine whether this curve is a quadratic twist of another.

        INPUT:

        - ``other`` -- an elliptic curves with the same base field as self.

        OUTPUT:

        Either 0, if the curves are not quadratic twists, or `D` if
        ``other`` is ``self.quadratic_twist(D)`` (up to isomorphism).
        If ``self`` and ``other`` are isomorphic, returns 1.

        If the curves are defined over `\mathbb{Q}`, the output `D` is
        a squarefree integer.

        .. note::

           Not fully implemented in characteristic 2, or in
           characteristic 3 when both `j`-invariants are 0.

        EXAMPLES::

            sage: E = EllipticCurve('11a1')
            sage: Et = E.quadratic_twist(-24)
            sage: E.is_quadratic_twist(Et)
            -6

            sage: E1=EllipticCurve([0,0,1,0,0])
            sage: E1.j_invariant()
            0
            sage: E2=EllipticCurve([0,0,0,0,2])
            sage: E1.is_quadratic_twist(E2)
            2
            sage: E1.is_quadratic_twist(E1)
            1
            sage: type(E1.is_quadratic_twist(E1)) == type(E1.is_quadratic_twist(E2))   #trac 6574
            True

        ::

            sage: E1=EllipticCurve([0,0,0,1,0])
            sage: E1.j_invariant()
            1728
            sage: E2=EllipticCurve([0,0,0,2,0])
            sage: E1.is_quadratic_twist(E2)
            0
            sage: E2=EllipticCurve([0,0,0,25,0])
            sage: E1.is_quadratic_twist(E2)
            5

        ::

            sage: F = GF(101)
            sage: E1 = EllipticCurve(F,[4,7])
            sage: E2 = E1.quadratic_twist()
            sage: D = E1.is_quadratic_twist(E2); D!=0
            True
            sage: F = GF(101)
            sage: E1 = EllipticCurve(F,[4,7])
            sage: E2 = E1.quadratic_twist()
            sage: D = E1.is_quadratic_twist(E2)
            sage: E1.quadratic_twist(D).is_isomorphic(E2)
            True
            sage: E1.is_isomorphic(E2)
            False
            sage: F2 = GF(101^2,'a')
            sage: E1.change_ring(F2).is_isomorphic(E2.change_ring(F2))
            True

        A characteristic 3 example::

            sage: F = GF(3^5,'a')
            sage: E1 = EllipticCurve_from_j(F(1))
            sage: E2 = E1.quadratic_twist(-1)
            sage: D = E1.is_quadratic_twist(E2); D!=0
            True
            sage: E1.quadratic_twist(D).is_isomorphic(E2)
            True

        ::

            sage: E1 = EllipticCurve_from_j(F(0))
            sage: E2 = E1.quadratic_twist()
            sage: D = E1.is_quadratic_twist(E2); D
            1
            sage: E1.is_isomorphic(E2)
            True

        """
        from sage.schemes.elliptic_curves.ell_generic import is_EllipticCurve
        E = self
        F = other
        if not is_EllipticCurve(E) or not is_EllipticCurve(F):
            raise ValueError("arguments are not elliptic curves")
        K = E.base_ring()
        zero = K.zero()
        if not K == F.base_ring():
            return zero
        j = E.j_invariant()
        if j != F.j_invariant():
            return zero

        if E.is_isomorphic(F):
            if K is rings.QQ:
                return rings.ZZ(1)
            return K.one()

        char = K.characteristic()

        if char == 2:
            raise NotImplementedError("not implemented in characteristic 2")
        elif char == 3:
            if j == 0:
                raise NotImplementedError(
                    "not implemented in characteristic 3 for curves of j-invariant 0"
                )
            D = E.b2() / F.b2()

        else:
            # now char!=2,3:
            c4E, c6E = E.c_invariants()
            c4F, c6F = F.c_invariants()

            if j == 0:
                um = c6E / c6F
                x = rings.polygen(K)
                ulist = (x**3 - um).roots(multiplicities=False)
                if len(ulist) == 0:
                    D = zero
                else:
                    D = ulist[0]
            elif j == 1728:
                um = c4E / c4F
                x = rings.polygen(K)
                ulist = (x**2 - um).roots(multiplicities=False)
                if len(ulist) == 0:
                    D = zero
                else:
                    D = ulist[0]
            else:
                D = (c6E * c4F) / (c6F * c4E)

        # Normalization of output:

        if D.is_zero():
            return D

        if K is rings.QQ:
            D = D.squarefree_part()

        assert E.quadratic_twist(D).is_isomorphic(F)

        return D
Beispiel #7
0
    def characters(self):
        r"""
        Return the two conjugate characters of `K^\times`, where `K` is some
        quadratic extension of `\QQ_p`, defining this representation. An error
        will be raised in some 2-adic cases, since not all 2-adic supercuspidal
        representations arise in this way.

        EXAMPLES:

        The first example from [LW2012]_::

            sage: f = Newform('50a')
            sage: Pi = LocalComponent(f, 5)
            sage: chars = Pi.characters(); chars
            [
            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -d - 1, 5 |--> 1,
            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> d, 5 |--> 1
            ]
            sage: chars[0].base_ring()
            Number Field in d with defining polynomial x^2 + x + 1

        These characters are interchanged by the Frobenius automorphism of `\GF{25}`::

            sage: chars[0] == chars[1]**5
            True

        A more complicated example (higher weight and nontrivial central character)::

            sage: f = Newforms(GammaH(25, [6]), 3, names='j')[0]; f
            q + j0*q^2 + 1/3*j0^3*q^3 - 1/3*j0^2*q^4 + O(q^6)
            sage: Pi = LocalComponent(f, 5)
            sage: Pi.characters()
            [
            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> 1/3*j0^2*d - 1/3*j0^3, 5 |--> 5,
            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -1/3*j0^2*d, 5 |--> 5
            ]
            sage: Pi.characters()[0].base_ring()
            Number Field in d with defining polynomial x^2 - j0*x + 1/3*j0^2 over its base field

        .. warning::

            The above output isn't actually the same as in Example 2 of
            [LW2012]_, due to an error in the published paper (correction
            pending) -- the published paper has the inverses of the above
            characters.

        A higher level example::

            sage: f = Newform('81a', names='j'); f
            q + j0*q^2 + q^4 - j0*q^5 + O(q^6)
            sage: LocalComponent(f, 3).characters()  # long time (12s on sage.math, 2012)
            [
            Character of unramified extension Q_3(s)* (s^2 + 2*s + 2 = 0), of level 2, mapping -2*s |--> -2*d + j0, 4 |--> 1, 3*s + 1 |--> -j0*d + 1, 3 |--> 1,
            Character of unramified extension Q_3(s)* (s^2 + 2*s + 2 = 0), of level 2, mapping -2*s |--> 2*d - j0, 4 |--> 1, 3*s + 1 |--> j0*d - 2, 3 |--> 1
            ]

        Some ramified examples::

            sage: Newform('27a').local_component(3).characters()
            [
            Character of ramified extension Q_3(s)* (s^2 - 6 = 0), of level 2, mapping 2 |--> 1, s + 1 |--> -d, s |--> -1,
            Character of ramified extension Q_3(s)* (s^2 - 6 = 0), of level 2, mapping 2 |--> 1, s + 1 |--> d - 1, s |--> -1
            ]
            sage: LocalComponent(Newform('54a'), 3, twist_factor=4).characters()
            [
            Character of ramified extension Q_3(s)* (s^2 - 3 = 0), of level 2, mapping 2 |--> 1, s + 1 |--> -1/9*d, s |--> -9,
            Character of ramified extension Q_3(s)* (s^2 - 3 = 0), of level 2, mapping 2 |--> 1, s + 1 |--> 1/9*d - 1, s |--> -9
            ]

        A 2-adic non-example::

            sage: Newform('24a').local_component(2).characters()
            Traceback (most recent call last):
            ...
            ValueError: Totally ramified 2-adic representations are not classified by characters

        Examples where `K^\times / \QQ_p^\times` is not topologically cyclic
        (which complicates the computations greatly)::

            sage: Newforms(DirichletGroup(64, QQ).1, 2, names='a')[0].local_component(2).characters() # long time, random
            [
            Character of unramified extension Q_2(s)* (s^2 + s + 1 = 0), of level 3, mapping s |--> 1, 2*s + 1 |--> 1/2*a0, 4*s + 1 |--> 1, -1 |--> 1, 2 |--> 1,
            Character of unramified extension Q_2(s)* (s^2 + s + 1 = 0), of level 3, mapping s |--> 1, 2*s + 1 |--> 1/2*a0, 4*s + 1 |--> -1, -1 |--> 1, 2 |--> 1
            ]
            sage: Newform('243a',names='a').local_component(3).characters() # long time
            [
            Character of ramified extension Q_3(s)* (s^2 - 6 = 0), of level 4, mapping -2*s - 1 |--> -d - 1, 4 |--> 1, 3*s + 1 |--> -d - 1, s |--> 1,
            Character of ramified extension Q_3(s)* (s^2 - 6 = 0), of level 4, mapping -2*s - 1 |--> d, 4 |--> 1, 3*s + 1 |--> d, s |--> 1
            ]
        """
        T = self.type_space()
        p = self.prime()
        if self.conductor() % 2 == 0:

            G = SmoothCharacterGroupUnramifiedQuadratic(
                self.prime(), self.coefficient_field())
            n = self.conductor() // 2

            gs = G.quotient_gens(n)
            g = gs[-1]

            assert g.valuation(G.ideal(1)) == 0
            m = g.matrix().change_ring(ZZ).list()
            tr = (~T.rho(m)).trace()

            # The inverse is needed here because T is the *homological* type space,
            # which is dual to the cohomological one that defines the local component.

            X = polygen(self.coefficient_field())
            theta_poly = X**2 - (-1)**n * tr * X + self.central_character()(
                g.norm())
            verbose("theta_poly for %s is %s" % (g, theta_poly), level=1)
            if theta_poly.is_irreducible():
                F = self.coefficient_field().extension(theta_poly, "d")
                G = G.base_extend(F)

            # roots with repetitions allowed
            gvals = flatten([[y[0]] * y[1]
                             for y in theta_poly.roots(G.base_ring())])

            if len(gs) == 1:
                # This is always the case if p != 2
                chi1, chi2 = [
                    G.extend_character(n, self.central_character(), [x])
                    for x in gvals
                ]
            else:
                # 2-adic cases, conductor >= 64. Here life is complicated
                # because the quotient (O_K* / p^n)^* / (image of Z_2^*) is not
                # cyclic.
                g0 = gs[0]
                try:
                    G._reduce_Qp(1, g0)
                    raise ArithmeticError("Bad generators returned")
                except ValueError:
                    pass

                tr = (~T.rho(g0.matrix().list())).trace()
                X = polygen(G.base_ring())
                theta0_poly = X**2 - (
                    -1)**n * tr * X + self.central_character()(g0.norm())
                verbose("theta_poly for %s is %s" % (g0, theta_poly), level=1)
                if theta0_poly.is_irreducible():
                    F = theta0_poly.base_ring().extension(theta_poly, "e")
                    G = G.base_extend(F)
                g0vals = flatten([[y[0]] * y[1]
                                  for y in theta0_poly.roots(G.base_ring())])

                pairA = [[g0vals[0], gvals[0]], [g0vals[1], gvals[1]]]
                pairB = [[g0vals[0], gvals[1]], [g0vals[1], gvals[0]]]

                A_fail = 0
                B_fail = 0
                try:
                    chisA = [
                        G.extend_character(n, self.central_character(), [y, x])
                        for (y, x) in pairA
                    ]
                except ValueError:
                    A_fail = 1
                try:
                    chisB = [
                        G.extend_character(n, self.central_character(), [y, x])
                        for (y, x) in pairB
                    ]
                except ValueError:
                    B_fail = 1

                if chisA == chisB or chisA == reversed(chisB):
                    # repeated roots -- break symmetry arbitrarily
                    B_fail = 1

                # check the character relation from LW12
                if (not A_fail and not B_fail):
                    for x in G.ideal(n).invertible_residues():
                        try:
                            # test if G mod p is in Fp
                            flag = G._reduce_Qp(1, x)
                        except ValueError:
                            flag = None
                        if flag is not None:
                            verbose("skipping x=%s as congruent to %s mod p" %
                                    (x, flag))
                            continue

                        verbose("testing x = %s" % x, level=1)
                        ti = (-1)**n * (~T.rho(x.matrix().list())).trace()
                        verbose("  trace of matrix is %s" % ti, level=1)
                        if ti != chisA[0](x) + chisA[1](x):
                            verbose("  chisA FAILED", level=1)
                            A_fail = 1
                            break
                        if ti != chisB[0](x) + chisB[1](x):
                            verbose("  chisB FAILED", level=1)
                            B_fail = 1
                            break
                        else:
                            verbose("  Trace identity check works for both",
                                    level=1)

                if B_fail and not A_fail:
                    chi1, chi2 = chisA
                elif A_fail and not B_fail:
                    chi1, chi2 = chisB
                else:
                    raise ValueError(
                        "Something went wrong: can't identify the characters")

            # Consistency checks
            assert chi1.restrict_to_Qp() == chi2.restrict_to_Qp(
            ) == self.central_character()
            assert chi1 * chi2 == chi1.parent().compose_with_norm(
                self.central_character())

            return Sequence([chi1, chi2], check=False, cr=True)

        else:
            # The ramified case.

            n = self.conductor() - 1
            if p == 2:
                # The ramified 2-adic representations aren't classified by admissible pairs. Die.
                raise ValueError(
                    "Totally ramified 2-adic representations are not classified by characters"
                )

            G0 = SmoothCharacterGroupRamifiedQuadratic(
                p, 0, self.coefficient_field())
            G1 = SmoothCharacterGroupRamifiedQuadratic(
                p, 1, self.coefficient_field())
            q0 = G0.quotient_gens(n)
            assert all(x.valuation(G0.ideal(1)) == 1 for x in q0)
            q1 = G1.quotient_gens(n)
            assert all(x.valuation(G1.ideal(1)) == 1 for x in q1)

            t0 = [(~T.rho(q.matrix().list())).trace() for q in q0]
            t1 = [(~T.rho(q.matrix().list())).trace() for q in q1]

            if all(x == 0 for x in t0 + t1):
                # Can't happen?
                raise NotImplementedError(
                    "Can't identify ramified quadratic extension -- all traces zero"
                )
            elif all(x == 0 for x in t1):
                G, qs, ts = G0, q0, t0
            elif all(x == 0 for x in t0):
                G, qs, ts = G1, q1, t1
            else:
                # At least one of the traces is *always* 0, since the type
                # space has to be isomorphic to its twist by the (ramified
                # quadratic) character corresponding to the quadratic
                # extension.
                raise RuntimeError("Can't get here!")

            q = qs[0]
            t = ts[0]
            k = self.newform().weight()
            t *= p**ZZ((k - 2 + self.twist_factor()) / 2)

            X = polygen(self.coefficient_field())
            theta_poly = X**2 - X * t + self.central_character()(q.norm())
            verbose("theta_poly is %s" % theta_poly, level=1)
            if theta_poly.is_irreducible():
                F = self.coefficient_field().extension(theta_poly, "d")
                G = G.base_extend(F)
            c1q, c2q = flatten([[x] * e
                                for x, e in theta_poly.roots(G.base_ring())])

            if len(qs) == 1:
                chi1, chi2 = [
                    G.extend_character(n, self.central_character(), [x])
                    for x in [c1q, c2q]
                ]

            else:
                assert p == 3
                q = qs[1]
                t = ts[1]
                t *= p**ZZ((k - 2 + self.twist_factor()) / 2)

                X = polygen(G.base_ring())
                theta_poly = X**2 - X * t + self.central_character()(q.norm())
                verbose("theta_poly is %s" % theta_poly, level=1)
                if theta_poly.is_irreducible():
                    F = G.base_ring().extension(theta_poly, "e")
                    G = G.base_extend(F)
                c1q2, c2q2 = flatten(
                    [[x] * e for x, e in theta_poly.roots(G.base_ring())])

                pairA = [[c1q, c1q2], [c2q, c2q2]]
                pairB = [[c1q, c2q2], [c2q, c1q2]]

                A_fail = 0
                B_fail = 0
                try:
                    chisA = [
                        G.extend_character(n, self.central_character(), [x, y])
                        for (x, y) in pairA
                    ]
                except ValueError:
                    verbose('A failed to create', level=1)
                    A_fail = 1
                try:
                    chisB = [
                        G.extend_character(n, self.central_character(), [x, y])
                        for (x, y) in pairB
                    ]
                except ValueError:
                    verbose('A failed to create', level=1)
                    B_fail = 1

                if c1q == c2q or c1q2 == c2q2:
                    B_fail = 1

                for u in G.ideal(n).invertible_residues():
                    if A_fail or B_fail:
                        break
                    x = q * u
                    verbose("testing x = %s" % x, level=1)
                    ti = (~T.rho(x.matrix().list())).trace() * p**ZZ(
                        (k - 2 + self.twist_factor()) / 2)
                    verbose("trace of matrix is %s" % ti, level=1)
                    if chisA[0](x) + chisA[1](x) != ti:
                        A_fail = 1
                    if chisB[0](x) + chisB[1](x) != ti:
                        B_fail = 1

                if B_fail and not A_fail:
                    chi1, chi2 = chisA
                elif A_fail and not B_fail:
                    chi1, chi2 = chisB
                else:
                    raise ValueError(
                        "Something went wrong: can't identify the characters")

            # Consistency checks
            assert chi1.restrict_to_Qp() == chi2.restrict_to_Qp(
            ) == self.central_character()
            assert chi1 * chi2 == chi1.parent().compose_with_norm(
                self.central_character())

            return Sequence([chi1, chi2], check=False, cr=True)
    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)
Beispiel #10
0
def cos_minpoly(n, var='x'):
    r"""
    Return the minimal polynomial of 2 cos pi/n

    Done via [KoRoTr2015]_ Algorithm 3.

    EXAMPLES::

        sage: from flatsurf.geometry.subfield import cos_minpoly

        sage: cos_minpoly(1)
        x + 2
        sage: cos_minpoly(2)
        x
        sage: cos_minpoly(3)
        x - 1
        sage: cos_minpoly(4)
        x^2 - 2
        sage: cos_minpoly(5)
        x^2 - x - 1
        sage: cos_minpoly(6)
        x^2 - 3
        sage: cos_minpoly(8)
        x^4 - 4*x^2 + 2
        sage: cos_minpoly(9)
        x^3 - 3*x - 1
        sage: cos_minpoly(10)
        x^4 - 5*x^2 + 5

        sage: cos_minpoly(90)
        x^24 - 24*x^22 + 252*x^20 - 1519*x^18 + 5796*x^16 - 14553*x^14 + 24206*x^12 - 26169*x^10 + 17523*x^8 - 6623*x^6 + 1182*x^4 - 72*x^2 + 1
        sage: cos_minpoly(148)
        x^72 - 72*x^70 + 2483*x^68 - 54604*x^66 + 860081*x^64 ... - 3511656*x^6 + 77691*x^4 - 684*x^2 + 1
    """
    n = ZZ(n)
    facs = list(n.factor())
    if isinstance(var, str):
        var = polygen(ZZ, var)

    if not facs:
        # 2 cos (pi) = -2
        return var + 2
    if facs[0][0] == 2:  # n is even
        k = facs[0][1]
        facs.pop(0)
    else:
        k = 0

    if not facs:
        # 0. n = 2^k
        # ([KoRoTr2015] Lemma 12)
        return chebyshev_T(2**(k-1), var)

    # 1. Compute M_{n0} = M_{p1 ... ps}
    # ([KoRoTr2015] Lemma 14 and Lemma 15)
    M = cos_minpoly_odd_prime(facs[0][0], var)
    for i in range(1, len(facs)):
        p = facs[i][0]
        M, r = M(chebyshev_T(p, var)).quo_rem(M)
        assert r.is_zero()

    # 2. Compute M_{2^k p1^{a1} ... ps^{as}}
    # ([KoRoTr2015] Lemma 12)
    nn = 2**k * prod(p**(a-1) for p,a in facs)
    if nn != 1:
        M = M(chebyshev_T(nn, var))

    return M