Beispiel #1
0
    def parity_check_matrix(self):
        r"""
        Return a parity check matrix of ``self``.

        The construction of the parity check matrix in case ``self``
        is not a binary code is not really well documented.
        Regarding the choice of projective geometry, one might check:

        - the note over section 2.3 in [Rot2006]_, pages 47-48
        - the dedicated paragraph in [HP2003]_, page 30

        EXAMPLES::

            sage: C = codes.HammingCode(GF(3), 3)
            sage: C.parity_check_matrix()
            [1 0 1 1 0 1 0 1 1 1 0 1 1]
            [0 1 1 2 0 0 1 1 2 0 1 1 2]
            [0 0 0 0 1 1 1 1 1 2 2 2 2]

        """
        n = self.length()
        F = self.base_field()
        m = n - self.dimension()
        MS = MatrixSpace(F, n, m)
        X = ProjectiveSpace(m - 1, F)
        PFn = [list(p) for p in X.point_set(F).points()]

        H = MS(PFn).transpose()
        H = H[::-1, :]
        H.set_immutable()
        return H
Beispiel #2
0
 def __init__(self, J):
     """
     """
     R = J.base_ring()
     PP = ProjectiveSpace(3, R, ["X0", "X1", "X2", "X3"])
     X0, X1, X2, X3 = PP.gens()
     C = J.curve()
     f, h = C.hyperelliptic_polynomials()
     a12 = f[0]
     a10 = f[1]
     a8 = f[2]
     a6 = f[3]
     a4 = f[4]
     a2 = f[5]
     a0 = f[6]
     if h != 0:
         c6 = h[0]
         c4 = h[1]
         c2 = h[2]
         c0 = h[3]
         a12, a10, a8, a6, a4, a2, a0 = \
              (4*a12 + c6**2,
               4*a10 + 2*c4*c6,
               4*a8 + 2*c2*c6 + c4**2,
               4*a6 + 2*c0*c6 + 2*c2*c4,
               4*a4 + 2*c0*c4 + c2**2,
               4*a2 + 2*c0*c2,
               4*a0 + c0**2)
     F = \
       (-4*a8*a12 + a10**2)*X0**4 + \
       -4*a6*a12*X0**3*X1 + \
       -2*a6*a10*X0**3*X2 + \
       -4*a12*X0**3*X3 + \
       -4*a4*a12*X0**2*X1**2 + \
       (4*a2*a12 - 4*a4*a10)*X0**2*X1*X2 + \
       -2*a10*X0**2*X1*X3 + \
       (-4*a0*a12 + 2*a2*a10 - 4*a4*a8 + a6**2)*X0**2*X2**2 + \
       -4*a8*X0**2*X2*X3 + \
       -4*a2*a12*X0*X1**3 + \
       (8*a0*a12 - 4*a2*a10)*X0*X1**2*X2 + \
       (4*a0*a10 - 4*a2*a8)*X0*X1*X2**2 + \
       -2*a6*X0*X1*X2*X3 + \
       -2*a2*a6*X0*X2**3 + \
       -4*a4*X0*X2**2*X3 + \
       -4*X0*X2*X3**2 + \
       -4*a0*a12*X1**4 + \
       -4*a0*a10*X1**3*X2 + \
       -4*a0*a8*X1**2*X2**2 + \
       X1**2*X3**2 + \
       -4*a0*a6*X1*X2**3 + \
       -2*a2*X1*X2**2*X3 + \
       (-4*a0*a4 + a2**2)*X2**4 + \
       -4*a0*X2**3*X3
     AlgebraicScheme_subscheme_projective.__init__(self, PP, F)
     X, Y, Z = C.ambient_space().gens()
     if a0 == 0:
         a0 = a2
     phi = Hom(C, self)([0, Z**2, X * Z, a0 * X**2], Schemes())
     C._kummer_morphism = phi
     J._kummer_surface = self
Beispiel #3
0
def HammingCode(r,F):
    r"""
    Implements the Hamming codes.

    The `r^{th}` Hamming code over `F=GF(q)` is an
    `[n,k,d]` code with length `n=(q^r-1)/(q-1)`,
    dimension `k=(q^r-1)/(q-1) - r` and minimum distance
    `d=3`. The parity check matrix of a Hamming code has rows
    consisting of all nonzero vectors of length r in its columns,
    modulo a scalar factor so no parallel columns arise. A Hamming code
    is a single error-correcting code.

    INPUT:


    -  ``r`` - an integer 2

    -  ``F`` - a finite field.


    OUTPUT: Returns the r-th q-ary Hamming code.

    EXAMPLES::

        sage: codes.HammingCode(3,GF(2))
        Linear code of length 7, dimension 4 over Finite Field of size 2
        sage: C = codes.HammingCode(3,GF(3)); C
        Linear code of length 13, dimension 10 over Finite Field of size 3
        sage: C.minimum_distance()
        3
        sage: C.minimum_distance(algorithm='gap') # long time, check d=3
        3
        sage: C = codes.HammingCode(3,GF(4,'a')); C
        Linear code of length 21, dimension 18 over Finite Field in a of size 2^2

    While the ``codes`` object now gathers all code constructors,
    ``HammingCode`` is still available in the global namespace::

        sage: HammingCode(3,GF(2))
        doctest:...: DeprecationWarning: This method soon will not be available in that way anymore. To use it, you can now call it by typing codes.HammingCode
        See http://trac.sagemath.org/15445 for details.
        Linear code of length 7, dimension 4 over Finite Field of size 2

    """
    q = F.order()
    n =  (q**r-1)/(q-1)
    k = n-r
    MS = MatrixSpace(F,n,r)
    X = ProjectiveSpace(r-1,F)
    PFn = [list(p) for p in X.point_set(F).points(F)]
    H = MS(PFn).transpose()
    Cd = LinearCode(H)
    # Hamming code always has distance 3, so we provide the distance.
    return LinearCode(Cd.dual_code().gen_mat(), d=3)
Beispiel #4
0
def HammingCode(r,F):
    r"""
    Implements the Hamming codes.

    The `r^{th}` Hamming code over `F=GF(q)` is an
    `[n,k,d]` code with length `n=(q^r-1)/(q-1)`,
    dimension `k=(q^r-1)/(q-1) - r` and minimum distance
    `d=3`. The parity check matrix of a Hamming code has rows
    consisting of all nonzero vectors of length r in its columns,
    modulo a scalar factor so no parallel columns arise. A Hamming code
    is a single error-correcting code.

    INPUT:


    -  ``r`` - an integer 2

    -  ``F`` - a finite field.


    OUTPUT: Returns the r-th q-ary Hamming code.

    EXAMPLES::

        sage: codes.HammingCode(3,GF(2))
        Linear code of length 7, dimension 4 over Finite Field of size 2
        sage: C = codes.HammingCode(3,GF(3)); C
        Linear code of length 13, dimension 10 over Finite Field of size 3
        sage: C.minimum_distance()
        3
        sage: C.minimum_distance(algorithm='gap') # long time, check d=3
        3
        sage: C = codes.HammingCode(3,GF(4,'a')); C
        Linear code of length 21, dimension 18 over Finite Field in a of size 2^2

    While the ``codes`` object now gathers all code constructors,
    ``HammingCode`` is still available in the global namespace::

        sage: HammingCode(3,GF(2))
        doctest:1: DeprecationWarning: This method soon will not be available in that way anymore. To use it, you can now call it by typing codes.HammingCode
        See http://trac.sagemath.org/15445 for details.
        Linear code of length 7, dimension 4 over Finite Field of size 2

    """
    q = F.order()
    n =  (q**r-1)/(q-1)
    k = n-r
    MS = MatrixSpace(F,n,r)
    X = ProjectiveSpace(r-1,F)
    PFn = [list(p) for p in X.point_set(F).points(F)]
    H = MS(PFn).transpose()
    Cd = LinearCode(H)
    # Hamming code always has distance 3, so we provide the distance.
    return LinearCode(Cd.dual_code().gen_mat(), d=3)
Beispiel #5
0
 def __init__(self,J):
     """
     """
     R = J.base_ring()
     PP = ProjectiveSpace(3,R,["X0","X1","X2","X3"])
     X0, X1, X2, X3 = PP.gens()
     C = J.curve()
     f, h = C.hyperelliptic_polynomials()
     a12 = f[0]; a10 = f[1]; a8 = f[2];
     a6 = f[3]; a4 = f[4]; a2 = f[5]; a0 = f[6]
     if h != 0:
         c6 = h[0]; c4 = h[1]; c2 = h[2]; c0 = h[3]
         a12, a10, a8, a6, a4, a2, a0 = \
              (4*a12 + c6**2,
               4*a10 + 2*c4*c6,
               4*a8 + 2*c2*c6 + c4**2,
               4*a6 + 2*c0*c6 + 2*c2*c4,
               4*a4 + 2*c0*c4 + c2**2,
               4*a2 + 2*c0*c2,
               4*a0 + c0**2)
     F = \
       (-4*a8*a12 + a10**2)*X0**4 + \
       -4*a6*a12*X0**3*X1 + \
       -2*a6*a10*X0**3*X2 + \
       -4*a12*X0**3*X3 + \
       -4*a4*a12*X0**2*X1**2 + \
       (4*a2*a12 - 4*a4*a10)*X0**2*X1*X2 + \
       -2*a10*X0**2*X1*X3 + \
       (-4*a0*a12 + 2*a2*a10 - 4*a4*a8 + a6**2)*X0**2*X2**2 + \
       -4*a8*X0**2*X2*X3 + \
       -4*a2*a12*X0*X1**3 + \
       (8*a0*a12 - 4*a2*a10)*X0*X1**2*X2 + \
       (4*a0*a10 - 4*a2*a8)*X0*X1*X2**2 + \
       -2*a6*X0*X1*X2*X3 + \
       -2*a2*a6*X0*X2**3 + \
       -4*a4*X0*X2**2*X3 + \
       -4*X0*X2*X3**2 + \
       -4*a0*a12*X1**4 + \
       -4*a0*a10*X1**3*X2 + \
       -4*a0*a8*X1**2*X2**2 + \
       X1**2*X3**2 + \
       -4*a0*a6*X1*X2**3 + \
       -2*a2*X1*X2**2*X3 + \
       (-4*a0*a4 + a2**2)*X2**4 + \
       -4*a0*X2**3*X3
     AlgebraicScheme_subscheme_projective.__init__(self, PP, F)
     X, Y, Z = C.ambient_space().gens()
     if a0 ==0:
         a0 = a2
     phi = Hom(C,self)([0,Z**2,X*Z,a0*X**2],Schemes())
     C._kummer_morphism = phi
     J._kummer_surface = self
def QuarticCurve(F, PP=None, check=False):
    """
    Returns the quartic curve defined by the polynomial F.

    INPUT:

    - F -- a polynomial in three variables, homogeneous of degree 4

    - PP -- a projective plane (default:None)

    - check -- whether to check for smoothness or not (default:False)

    EXAMPLES::

        sage: x,y,z=PolynomialRing(QQ,['x','y','z']).gens()
        sage: QuarticCurve(x**4+y**4+z**4)
        Quartic Curve over Rational Field defined by x^4 + y^4 + z^4

    TESTS::

        sage: QuarticCurve(x**3+y**3)
        Traceback (most recent call last):
        ...
        ValueError: Argument F (=x^3 + y^3) must be a homogeneous polynomial of degree 4

        sage: QuarticCurve(x**4+y**4+z**3)
        Traceback (most recent call last):
        ...
        ValueError: Argument F (=x^4 + y^4 + z^3) must be a homogeneous polynomial of degree 4

        sage: x,y=PolynomialRing(QQ,['x','y']).gens()
        sage: QuarticCurve(x**4+y**4)
        Traceback (most recent call last):
        ...
        ValueError: Argument F (=x^4 + y^4) must be a polynomial in 3 variables

    """
    if not is_MPolynomial(F):
        raise ValueError("Argument F (=%s) must be a multivariate polynomial" %
                         F)
    P = F.parent()
    if not P.ngens() == 3:
        raise ValueError(
            "Argument F (=%s) must be a polynomial in 3 variables" % F)
    if not (F.is_homogeneous() and F.degree() == 4):
        raise ValueError(
            "Argument F (=%s) must be a homogeneous polynomial of degree 4" %
            F)

    if PP is not None:
        if not is_ProjectiveSpace(PP) and PP.dimension == 2:
            raise ValueError(f"Argument PP (={PP}) must be a projective plane")
    else:
        PP = ProjectiveSpace(P)

    if check:
        raise NotImplementedError(
            "Argument checking (for nonsingularity) is not implemented.")

    return QuarticCurve_generic(PP, F)
Beispiel #7
0
def HammingCode(r, F):
    r"""
    Implements the Hamming codes.

    The `r^{th}` Hamming code over `F=GF(q)` is an
    `[n,k,d]` code with length `n=(q^r-1)/(q-1)`,
    dimension `k=(q^r-1)/(q-1) - r` and minimum distance
    `d=3`. The parity check matrix of a Hamming code has rows
    consisting of all nonzero vectors of length r in its columns,
    modulo a scalar factor so no parallel columns arise. A Hamming code
    is a single error-correcting code.

    INPUT:


    -  ``r`` - an integer 2

    -  ``F`` - a finite field.


    OUTPUT: Returns the r-th q-ary Hamming code.

    EXAMPLES::

        sage: codes.HammingCode(3,GF(2))
        Linear code of length 7, dimension 4 over Finite Field of size 2
        sage: C = codes.HammingCode(3,GF(3)); C
        Linear code of length 13, dimension 10 over Finite Field of size 3
        sage: C.minimum_distance()
        3
        sage: C.minimum_distance(algorithm='gap') # long time, check d=3
        3
        sage: C = codes.HammingCode(3,GF(4,'a')); C
        Linear code of length 21, dimension 18 over Finite Field in a of size 2^2
    """
    q = F.order()
    n = (q**r - 1) / (q - 1)
    k = n - r
    MS = MatrixSpace(F, n, r)
    X = ProjectiveSpace(r - 1, F)
    PFn = [list(p) for p in X.point_set(F).points(F)]
    H = MS(PFn).transpose()
    Cd = LinearCode(H)
    # Hamming code always has distance 3, so we provide the distance.
    return LinearCode(Cd.dual_code().generator_matrix(), d=3)
def HammingCode(r,F):
    r"""
    Implements the Hamming codes.

    The `r^{th}` Hamming code over `F=GF(q)` is an
    `[n,k,d]` code with length `n=(q^r-1)/(q-1)`,
    dimension `k=(q^r-1)/(q-1) - r` and minimum distance
    `d=3`. The parity check matrix of a Hamming code has rows
    consisting of all nonzero vectors of length r in its columns,
    modulo a scalar factor so no parallel columns arise. A Hamming code
    is a single error-correcting code.

    INPUT:


    -  ``r`` - an integer 2

    -  ``F`` - a finite field.


    OUTPUT: Returns the r-th q-ary Hamming code.

    EXAMPLES::

        sage: codes.HammingCode(3,GF(2))
        Linear code of length 7, dimension 4 over Finite Field of size 2
        sage: C = codes.HammingCode(3,GF(3)); C
        Linear code of length 13, dimension 10 over Finite Field of size 3
        sage: C.minimum_distance()
        3
        sage: C.minimum_distance(algorithm='gap') # long time, check d=3
        3
        sage: C = codes.HammingCode(3,GF(4,'a')); C
        Linear code of length 21, dimension 18 over Finite Field in a of size 2^2
    """
    q = F.order()
    n =  (q**r-1)/(q-1)
    k = n-r
    MS = MatrixSpace(F,n,r)
    X = ProjectiveSpace(r-1,F)
    PFn = [list(p) for p in X.point_set(F).points(F)]
    H = MS(PFn).transpose()
    Cd = LinearCode(H)
    # Hamming code always has distance 3, so we provide the distance.
    return LinearCode(Cd.dual_code().generator_matrix(), d=3)
Beispiel #9
0
    def _number_field_from_algebraics(self):
        r"""
        Given a projective point defined over ``QQbar``, return the same point, but defined
        over a number field.

        This is only implemented for points of projective space.

        OUTPUT: scheme point

        EXAMPLES::

            sage: R.<x> = PolynomialRing(QQ)
            sage: P.<x,y> = ProjectiveSpace(QQbar,1)
            sage: Q = P([-1/2*QQbar(sqrt(2)) + QQbar(I), 1])
            sage: S = Q._number_field_from_algebraics(); S
            (1/2*a^3 + a^2 - 1/2*a : 1)
            sage: S.codomain()
            Projective Space of dimension 1 over Number Field in a with defining polynomial y^4 + 1 with a = 0.7071067811865475? + 0.7071067811865475?*I

        The following was fixed in :trac:`23808`::

            sage: R.<x> = PolynomialRing(QQ)
            sage: P.<x,y> = ProjectiveSpace(QQbar,1)
            sage: Q = P([-1/2*QQbar(sqrt(2)) + QQbar(I), 1]);Q
            (-0.7071067811865475? + 1*I : 1)
            sage: S = Q._number_field_from_algebraics(); S
            (1/2*a^3 + a^2 - 1/2*a : 1)
            sage: T = S.change_ring(QQbar) # Used to fail
            sage: T
            (-0.7071067811865475? + 1.000000000000000?*I : 1)
            sage: Q[0] == T[0]
            True
        """
        from sage.schemes.projective.projective_space import is_ProjectiveSpace
        if not is_ProjectiveSpace(self.codomain()):
            raise NotImplementedError("not implemented for subschemes")

        # Trac #23808: Keep the embedding info associated with the number field K
        # used below, instead of in the separate embedding map phi which is
        # forgotten.
        K_pre, P, phi = number_field_elements_from_algebraics(list(self))
        if K_pre is QQ:
            K = QQ
        else:
            from sage.rings.number_field.number_field import NumberField
            K = NumberField(K_pre.polynomial(),
                            embedding=phi(K_pre.gen()),
                            name='a')
            psi = K_pre.hom([K.gen()], K)  # Identification of K_pre with K
            P = [psi(p) for p in P]  # The elements of P were elements of K_pre
        from sage.schemes.projective.projective_space import ProjectiveSpace
        PS = ProjectiveSpace(K, self.codomain().dimension_relative(), 'z')
        return PS(P)
Beispiel #10
0
    def __init__(self, poly, ambient=None):
        """
        Return the projective hypersurface in the space ambient
        defined by the polynomial poly.

        If ambient is not given, it will be constructed based on
        poly.

        EXAMPLES::

            sage: P.<x, y, z> = ProjectiveSpace(ZZ, 2)
            sage: ProjectiveHypersurface(x-y, P)
            Projective hypersurface defined by x - y in Projective Space of dimension 2 over Integer Ring

        ::

            sage: R.<x, y, z> = QQ[]
            sage: ProjectiveHypersurface(x-y)
            Projective hypersurface defined by x - y in Projective Space of dimension 2 over Rational Field

        TESTS::

            sage: H = ProjectiveHypersurface(x-y)
            sage: H == loads(dumps(H))
            True
        """
        if not is_MPolynomial(poly):
            raise TypeError(
                "Defining polynomial (=%s) must be a multivariate polynomial."
                % poly)
        if not poly.is_homogeneous():
            raise TypeError("Defining polynomial (=%s) must be homogeneous." %
                            poly)
        if ambient is None:
            R = poly.parent()
            from sage.schemes.projective.projective_space import ProjectiveSpace
            ambient = ProjectiveSpace(R.base_ring(), R.ngens() - 1)
            ambient._coordinate_ring = R
        AlgebraicScheme_subscheme_projective.__init__(self, ambient, [poly])
Beispiel #11
0
    def __init__(self, N, R=QQ, names=None):
        r"""
        The Python constructor.

        INPUT:

        - ``N`` - a list or tuple of positive integers.

        - ``R`` - a ring.

        - ``names`` - a tuple or list of strings. This must either be a single variable name
                    or the complete list of variables.

        EXAMPLES::

            sage: T.<x,y,z,u,v,w> = ProductProjectiveSpaces([2, 2], QQ)
            sage: T
            Product of projective spaces P^2 x P^2 over Rational Field
            sage: T.coordinate_ring()
            Multivariate Polynomial Ring in x, y, z, u, v, w over Rational Field
            sage: T[1].coordinate_ring()
            Multivariate Polynomial Ring in u, v, w over Rational Field

        ::

            sage: ProductProjectiveSpaces([1,1,1],ZZ, ['x', 'y', 'z', 'u', 'v', 'w'])
            Product of projective spaces P^1 x P^1 x P^1 over Integer Ring

        ::

            sage: T = ProductProjectiveSpaces([1, 1], QQ, 'z')
            sage: T.coordinate_ring()
            Multivariate Polynomial Ring in z0, z1, z2, z3 over Rational Field
        """
        assert isinstance(N, (tuple, list))
        N = [Integer(n) for n in N]
        assert isinstance(R, CommutativeRing)
        if len(N) < 2:
            raise ValueError("must be at least two components for a product")
        AmbientSpace.__init__(self, sum(N), R)
        self._dims = N
        start = 0
        self._components = []
        for i in range(len(N)):
            self._components.append(
                ProjectiveSpace(N[i], R, names[start:start + N[i] + 1]))
            start += N[i] + 1
        #Note that the coordinate ring should really be the tensor product of the component
        #coordinate rings. But we just deal with them as multihomogeneous polynomial rings
        self._coordinate_ring = PolynomialRing(R, sum(N) + len(N), names)
        self._assign_names(names)
Beispiel #12
0
    def __init__(self, poly, ambient=None):
        """
        Return the projective hypersurface in the space ambient
        defined by the polynomial poly.

        If ambient is not given, it will be constructed based on
        poly.

        EXAMPLES::

            sage: P.<x, y, z> = ProjectiveSpace(ZZ, 2)
            sage: ProjectiveHypersurface(x-y, P)
            Projective hypersurface defined by x - y in Projective Space of dimension 2 over Integer Ring

        ::

            sage: R.<x, y, z> = QQ[]
            sage: ProjectiveHypersurface(x-y)
            Projective hypersurface defined by x - y in Projective Space of dimension 2 over Rational Field

        TESTS::

            sage: H = ProjectiveHypersurface(x-y)
            sage: H == loads(dumps(H))
            True
        """
        if not is_MPolynomial(poly):
            raise TypeError, \
                  "Defining polynomial (=%s) must be a multivariate polynomial."%poly
        if not poly.is_homogeneous():
            raise TypeError, "Defining polynomial (=%s) must be homogeneous."%poly
        if ambient == None:
            R = poly.parent()
            from sage.schemes.projective.projective_space import ProjectiveSpace
            ambient = ProjectiveSpace(R.base_ring(), R.ngens()-1)
            ambient._coordinate_ring = R
        AlgebraicScheme_subscheme_projective.__init__(self, ambient, [poly])
Beispiel #13
0
    def projective_embedding(self, i=None, PP=None):
        """
        Returns a morphism from this space into an ambient projective space
        of the same dimension.

        INPUT:


        -  ``i`` - integer (default: dimension of self = last
           coordinate) determines which projective embedding to compute. The
           embedding is that which has a 1 in the i-th coordinate, numbered
           from 0.

        -  ``PP`` - (default: None) ambient projective space, i.e.,
           codomain of morphism; this is constructed if it is not
           given.

        EXAMPLES::

            sage: AA = AffineSpace(2, QQ, 'x')
            sage: pi = AA.projective_embedding(0); pi
            Scheme morphism:
              From: Affine Space of dimension 2 over Rational Field
              To:   Projective Space of dimension 2 over Rational Field
              Defn: Defined on coordinates by sending (x0, x1) to
                    (1 : x0 : x1)
            sage: z = AA(3,4)
            sage: pi(z)
            (1/4 : 3/4 : 1)
            sage: pi(AA(0,2))
            (1/2 : 0 : 1)
            sage: pi = AA.projective_embedding(1); pi
            Scheme morphism:
              From: Affine Space of dimension 2 over Rational Field
              To:   Projective Space of dimension 2 over Rational Field
              Defn: Defined on coordinates by sending (x0, x1) to
                    (x0 : 1 : x1)
            sage: pi(z)
            (3/4 : 1/4 : 1)
            sage: pi = AA.projective_embedding(2)
            sage: pi(z)
            (3 : 4 : 1)
        """
        n = self.dimension_relative()
        if i is None:
            try:
                i = self._default_embedding_index
            except AttributeError:
                i = int(n)
        else:
            i = int(i)
        try:
            return self.__projective_embedding[i]
        except AttributeError:
            self.__projective_embedding = {}
        except KeyError:
            pass
        if PP is None:
            from sage.schemes.projective.projective_space import ProjectiveSpace
            PP = ProjectiveSpace(n, self.base_ring())
        R = self.coordinate_ring()
        v = list(R.gens())
        if n < 0 or n >self.dimension_relative():
            raise ValueError("Argument i (=%s) must be between 0 and %s, inclusive"%(i,n))
        v.insert(i, R(1))
        phi = self.hom(v, PP)
        self.__projective_embedding[i] = phi
        return phi
Beispiel #14
0
def TaylorTwographDescendantSRG(q, clique_partition=None):
    r"""
    constructing the descendant graph of the Taylor's two-graph for `U_3(q)`, `q` odd

    This is a strongly regular graph with parameters
    `(v,k,\lambda,\mu)=(q^3, (q^2+1)(q-1)/2, (q-1)^3/4-1, (q^2+1)(q-1)/4)`
    obtained as a two-graph descendant of the
    :func:`Taylor's two-graph <sage.combinat.designs.twographs.taylor_twograph>` `T`.
    This graph admits a partition into cliques of size `q`, which are useful in
    :func:`TaylorTwographSRG <sage.graphs.generators.classical_geometries.TaylorTwographSRG>`,
    a strongly regular graph on `q^3+1` vertices in the
    Seidel switching class of `T`, for which we need `(q^2+1)/2` cliques.
    The cliques are the `q^2` lines on `v_0` of the projective plane containing the unital
    for `U_3(q)`, and intersecting the unital (i.e. the vertices of the graph and the point
    we remove) in `q+1` points. This is all taken from §7E of [BvL84]_.

    INPUT:

    - ``q`` -- a power of an odd prime number

    - ``clique_partition`` -- if ``True``, return `q^2-1` cliques of size `q`
      with empty pairwise intersection. (Removing all of them leaves a clique, too),
      and the point removed from the unital.

    EXAMPLES::

        sage: g=graphs.TaylorTwographDescendantSRG(3); g
        Taylor two-graph descendant SRG: Graph on 27 vertices
        sage: g.is_strongly_regular(parameters=True)
        (27, 10, 1, 5)
        sage: from sage.combinat.designs.twographs import taylor_twograph
        sage: T = taylor_twograph(3)                           # long time
        sage: g.is_isomorphic(T.descendant(T.ground_set()[1])) # long time
        True
        sage: g=graphs.TaylorTwographDescendantSRG(5)    # not tested (long time)
        sage: g.is_strongly_regular(parameters=True)  # not tested (long time)
        (125, 52, 15, 26)

    TESTS::

        sage: g,l,_=graphs.TaylorTwographDescendantSRG(3,clique_partition=True)
        sage: all(map(lambda x: g.is_clique(x), l))
        True
        sage: graphs.TaylorTwographDescendantSRG(4)
        Traceback (most recent call last):
        ...
        ValueError: q must be an odd prime power
        sage: graphs.TaylorTwographDescendantSRG(6)
        Traceback (most recent call last):
        ...
        ValueError: q must be an odd prime power
    """
    from sage.rings.arith import is_prime_power
    p, k = is_prime_power(q, get_data=True)
    if k == 0 or p == 2:
        raise ValueError('q must be an odd prime power')
    from sage.schemes.projective.projective_space import ProjectiveSpace
    from sage.rings.finite_rings.constructor import FiniteField
    from sage.modules.free_module_element import free_module_element as vector
    from sage.rings.finite_rings.integer_mod import mod
    from __builtin__ import sum
    Fq = FiniteField(q**2, 'a')
    PG = map(tuple, ProjectiveSpace(2, Fq))

    def S(x, y):
        return sum(map(lambda j: x[j] * y[2 - j]**q, xrange(3)))

    V = filter(lambda x: S(x, x) == 0, PG)  # the points of the unital
    v0 = V[0]
    V.remove(v0)
    if mod(q, 4) == 1:
        G = Graph(
            [V, lambda y, z: not (S(v0, y) * S(y, z) * S(z, v0)).is_square()],
            loops=False)
    else:
        G = Graph(
            [V, lambda y, z: (S(v0, y) * S(y, z) * S(z, v0)).is_square()],
            loops=False)
    G.name("Taylor two-graph descendant SRG")
    if clique_partition:
        lines = map(lambda x: filter(lambda t: t[0] + x * t[1] == 0, V),
                    filter(lambda z: z != 0, Fq))
        return (G, lines, v0)
    else:
        return G
Beispiel #15
0
    def segre_embedding(self, PP=None, var='u'):
        r"""
        Return the Segre embedding of this space into the appropriate
        projective space.

        INPUT:

        -  ``PP`` -- (default: ``None``) ambient image projective space;
            this is constructed if it is not given.

        - ``var`` -- string, variable name of the image projective space, default `u` (optional).

        OUTPUT:

        Hom -- from this space to the appropriate subscheme of projective space.

        .. TODO::

            Cartesian products with more than two components.

        EXAMPLES::

            sage: X.<y0,y1,y2,y3,y4,y5> = ProductProjectiveSpaces(ZZ, [2, 2])
            sage: phi = X.segre_embedding(); phi
            Scheme morphism:
              From: Product of projective spaces P^2 x P^2 over Integer Ring
              To:   Closed subscheme of Projective Space of dimension 8 over Integer Ring defined by:
              -u5*u7 + u4*u8,
              -u5*u6 + u3*u8,
              -u4*u6 + u3*u7,
              -u2*u7 + u1*u8,
              -u2*u4 + u1*u5,
              -u2*u6 + u0*u8,
              -u1*u6 + u0*u7,
              -u2*u3 + u0*u5,
              -u1*u3 + u0*u4
              Defn: Defined by sending (y0 : y1 : y2 , y3 : y4 : y5) to
                    (y0*y3 : y0*y4 : y0*y5 : y1*y3 : y1*y4 : y1*y5 : y2*y3 : y2*y4 : y2*y5).

            ::

            sage: T = ProductProjectiveSpaces([1, 2], CC, 'z')
            sage: T.segre_embedding()
            Scheme morphism:
              From: Product of projective spaces P^1 x P^2 over Complex Field with 53 bits of precision
              To:   Closed subscheme of Projective Space of dimension 5 over Complex Field with 53 bits of precision defined by:
              -u2*u4 + u1*u5,
              -u2*u3 + u0*u5,
              -u1*u3 + u0*u4
              Defn: Defined by sending (z0 : z1 , z2 : z3 : z4) to
                    (z0*z2 : z0*z3 : z0*z4 : z1*z2 : z1*z3 : z1*z4).

            ::

            sage: T = ProductProjectiveSpaces([1, 2, 1], QQ, 'z')
            sage: T.segre_embedding()
            Scheme morphism:
              From: Product of projective spaces P^1 x P^2 x P^1 over Rational Field
              To:   Closed subscheme of Projective Space of dimension 11 over
            Rational Field defined by:
              -u9*u10 + u8*u11,
              -u7*u10 + u6*u11,
              -u7*u8 + u6*u9,
              -u5*u10 + u4*u11,
              -u5*u8 + u4*u9,
              -u5*u6 + u4*u7,
              -u5*u9 + u3*u11,
              -u5*u8 + u3*u10,
              -u5*u8 + u2*u11,
              -u4*u8 + u2*u10,
              -u3*u8 + u2*u9,
              -u3*u6 + u2*u7,
              -u3*u4 + u2*u5,
              -u5*u7 + u1*u11,
              -u5*u6 + u1*u10,
              -u3*u7 + u1*u9,
              -u3*u6 + u1*u8,
              -u5*u6 + u0*u11,
              -u4*u6 + u0*u10,
              -u3*u6 + u0*u9,
              -u2*u6 + u0*u8,
              -u1*u6 + u0*u7,
              -u1*u4 + u0*u5,
              -u1*u2 + u0*u3
              Defn: Defined by sending (z0 : z1 , z2 : z3 : z4 , z5 : z6) to
                    (z0*z2*z5 : z0*z2*z6 : z0*z3*z5 : z0*z3*z6 : z0*z4*z5 : z0*z4*z6
            : z1*z2*z5 : z1*z2*z6 : z1*z3*z5 : z1*z3*z6 : z1*z4*z5 : z1*z4*z6).
        """
        N = self._dims
        M = prod([n+1 for n in N]) - 1
        CR = self.coordinate_ring()

        vars = list(self.coordinate_ring().variable_names()) + [var + str(i) for i in range(M+1)]
        R = PolynomialRing(self.base_ring(), self.ngens()+M+1, vars, order='lex')

        #set-up the elimination for the segre embedding
        mapping = []
        k = self.ngens()
        index = self.num_components()*[0]
        for count in range(M + 1):
            mapping.append(R.gen(k+count)-prod([CR(self[i].gen(index[i])) for i in range(len(index))]))
            for i in range(len(index)-1, -1, -1):
                if index[i] == N[i]:
                    index[i] = 0
                else:
                    index[i] += 1
                    break #only increment once

        #change the defining ideal of the subscheme into the variables
        I = R.ideal(list(self.defining_polynomials()) + mapping)
        J = I.groebner_basis()
        s = set(R.gens()[:self.ngens()])
        n = len(J)-1
        L = []
        while s.isdisjoint(J[n].variables()):
            L.append(J[n])
            n = n-1

        #create new subscheme
        if PP is None:
            PS = ProjectiveSpace(self.base_ring(), M, R.variable_names()[self.ngens():])
            Y = PS.subscheme(L)
        else:
            if PP.dimension_relative() != M:
                raise ValueError("projective Space %s must be dimension %s")%(PP, M)
            S = PP.coordinate_ring()
            psi = R.hom([0]*k + list(S.gens()), S)
            L = [psi(l) for l in L]
            Y = PP.subscheme(L)

        #create embedding for points
        mapping = []
        index = self.num_components()*[0]
        for count in range(M + 1):
            mapping.append(prod([CR(self[i].gen(index[i])) for i in range(len(index))]))
            for i in range(len(index)-1, -1, -1):
                if index[i] == N[i]:
                    index[i] = 0
                else:
                    index[i] += 1
                    break #only increment once
        phi = self.hom(mapping, Y)

        return phi
Beispiel #16
0
    def __init__(self, J):
        """
        EXAMPLES::

            sage: R.<x> = QQ[]
            sage: f = x^5 + x + 1
            sage: X = HyperellipticCurve(f)
            sage: J = Jacobian(X)
            sage: K = KummerSurface(J); K
            Closed subscheme of Projective Space of dimension 3 over Rational Field defined by:
            X0^4 - 4*X0*X1^3 + 4*X0^2*X1*X2 - 4*X0*X1^2*X2 + 2*X0^2*X2^2 + X2^4 - 4*X0^3*X3 - 2*X0^2*X1*X3 - 2*X1*X2^2*X3 + X1^2*X3^2 - 4*X0*X2*X3^2
        """
        R = J.base_ring()
        PP = ProjectiveSpace(3, R, ["X0", "X1", "X2", "X3"])
        X0, X1, X2, X3 = PP.gens()
        C = J.curve()
        f, h = C.hyperelliptic_polynomials()
        a12 = f[0]
        a10 = f[1]
        a8 = f[2]
        a6 = f[3]
        a4 = f[4]
        a2 = f[5]
        a0 = f[6]
        if h != 0:
            c6 = h[0]
            c4 = h[1]
            c2 = h[2]
            c0 = h[3]
            a12, a10, a8, a6, a4, a2, a0 = \
                 (4*a12 + c6**2,
                  4*a10 + 2*c4*c6,
                  4*a8 + 2*c2*c6 + c4**2,
                  4*a6 + 2*c0*c6 + 2*c2*c4,
                  4*a4 + 2*c0*c4 + c2**2,
                  4*a2 + 2*c0*c2,
                  4*a0 + c0**2)
        F = \
          (-4*a8*a12 + a10**2)*X0**4 + \
          -4*a6*a12*X0**3*X1 + \
          -2*a6*a10*X0**3*X2 + \
          -4*a12*X0**3*X3 + \
          -4*a4*a12*X0**2*X1**2 + \
          (4*a2*a12 - 4*a4*a10)*X0**2*X1*X2 + \
          -2*a10*X0**2*X1*X3 + \
          (-4*a0*a12 + 2*a2*a10 - 4*a4*a8 + a6**2)*X0**2*X2**2 + \
          -4*a8*X0**2*X2*X3 + \
          -4*a2*a12*X0*X1**3 + \
          (8*a0*a12 - 4*a2*a10)*X0*X1**2*X2 + \
          (4*a0*a10 - 4*a2*a8)*X0*X1*X2**2 + \
          -2*a6*X0*X1*X2*X3 + \
          -2*a2*a6*X0*X2**3 + \
          -4*a4*X0*X2**2*X3 + \
          -4*X0*X2*X3**2 + \
          -4*a0*a12*X1**4 + \
          -4*a0*a10*X1**3*X2 + \
          -4*a0*a8*X1**2*X2**2 + \
          X1**2*X3**2 + \
          -4*a0*a6*X1*X2**3 + \
          -2*a2*X1*X2**2*X3 + \
          (-4*a0*a4 + a2**2)*X2**4 + \
          -4*a0*X2**3*X3
        AlgebraicScheme_subscheme_projective.__init__(self, PP, F)
        X, Y, Z = C.ambient_space().gens()
        if a0 == 0:
            a0 = a2
        phi = Hom(C, self)([X.parent().zero(), Z**2, X*Z, a0*X**2], Schemes())
        C._kummer_morphism = phi
        J._kummer_surface = self
Beispiel #17
0
    def segre_embedding(self, PP=None):
        r"""
        Return the Segre embedding of this subscheme into the appropriate projective
        space.

        INPUT:

        - ``PP`` -- (default: ``None``) ambient image projective space;
          this is constructed if it is not given.

        OUTPUT:

        Hom from this subscheme to the appropriate subscheme of projective space

        EXAMPLES::

            sage: X.<x,y,z,w,u,v> = ProductProjectiveSpaces([2,2], QQ)
            sage: P = ProjectiveSpace(QQ,8,'t')
            sage: L = (-w - v)*x + (-w*y - u*z)
            sage: Q = (-u*w - v^2)*x^2 + ((-w^2 - u*w + (-u*v - u^2))*y + (-w^2 - u*v)*z)*x + \
            ((-w^2 - u*w - u^2)*y^2 + (-u*w - v^2)*z*y + (-w^2 + (-v - u)*w)*z^2)
            sage: W = X.subscheme([L,Q])
            sage: phi = W.segre_embedding(P)
            sage: phi.codomain().ambient_space() == P
            True

        ::

            sage: PP.<x,y,u,v,s,t> = ProductProjectiveSpaces([1,1,1], CC)
            sage: PP.subscheme([]).segre_embedding()
            Scheme morphism:
              From: Closed subscheme of Product of projective spaces P^1 x P^1 x P^1
            over Complex Field with 53 bits of precision defined by:
              (no polynomials)
              To:   Closed subscheme of Projective Space of dimension 7 over Complex
            Field with 53 bits of precision defined by:
              -u5*u6 + u4*u7,
              -u3*u6 + u2*u7,
              -u3*u4 + u2*u5,
              -u3*u5 + u1*u7,
              -u3*u4 + u1*u6,
              -u3*u4 + u0*u7,
              -u2*u4 + u0*u6,
              -u1*u4 + u0*u5,
              -u1*u2 + u0*u3
              Defn: Defined by sending (x : y , u : v , s : t) to
                    (x*u*s : x*u*t : x*v*s : x*v*t : y*u*s : y*u*t : y*v*s : y*v*t).

        ::

            sage: PP.<x,y,z,u,v,s,t> = ProductProjectiveSpaces([2,1,1], ZZ)
            sage: PP.subscheme([x^3, u-v, s^2-t^2]).segre_embedding()
            Scheme morphism:
              From: Closed subscheme of Product of projective spaces P^2 x P^1 x P^1
            over Integer Ring defined by:
              x^3,
              u - v,
              s^2 - t^2
              To:   Closed subscheme of Projective Space of dimension 11 over
            Integer Ring defined by:
              u10^2 - u11^2,
              u9 - u11,
              u8 - u10,
              -u7*u10 + u6*u11,
              u6*u10 - u7*u11,
              u6^2 - u7^2,
              u5 - u7,
              u4 - u6,
              u3^3,
              -u3*u10 + u2*u11,
              u2*u10 - u3*u11,
              -u3*u6 + u2*u7,
              u2*u6 - u3*u7,
              u2*u3^2,
              u2^2 - u3^2,
              u1 - u3,
              u0 - u2
              Defn: Defined by sending (x : y : z , u : v , s : t) to
                    (x*u*s : x*u*t : x*v*s : x*v*t : y*u*s : y*u*t : y*v*s : y*v*t :
            z*u*s : z*u*t : z*v*s : z*v*t).
        """
        AS = self.ambient_space()
        CR = AS.coordinate_ring()
        N = AS.dimension_relative_components()
        M = prod([n + 1 for n in N]) - 1

        vars = list(AS.coordinate_ring().variable_names()) + [
            'u' + str(i) for i in range(M + 1)
        ]
        R = PolynomialRing(AS.base_ring(),
                           AS.ngens() + M + 1,
                           vars,
                           order='lex')

        #set-up the elimination for the segre embedding
        mapping = []
        k = AS.ngens()
        index = AS.num_components() * [0]
        for count in range(M + 1):
            mapping.append(
                R.gen(k + count) -
                prod([CR(AS[i].gen(index[i])) for i in range(len(index))]))
            for i in range(len(index) - 1, -1, -1):
                if index[i] == N[i]:
                    index[i] = 0
                else:
                    index[i] += 1
                    break  #only increment once

        #change the defining ideal of the subscheme into the variables
        I = R.ideal(list(self.defining_polynomials()) + mapping)
        J = I.groebner_basis()
        s = set(R.gens()[:AS.ngens()])
        n = len(J) - 1
        L = []
        while s.isdisjoint(J[n].variables()):
            L.append(J[n])
            n = n - 1

        #create new subscheme
        if PP is None:
            PS = ProjectiveSpace(self.base_ring(), M, R.gens()[AS.ngens():])
            Y = PS.subscheme(L)
        else:
            if PP.dimension_relative() != M:
                raise ValueError(
                    "projective space %s must be dimension %s") % (PP, M)
            S = PP.coordinate_ring()
            psi = R.hom([0] * k + list(S.gens()), S)
            L = [psi(l) for l in L]
            Y = PP.subscheme(L)

        #create embedding for points
        mapping = []
        index = AS.num_components() * [0]
        for count in range(M + 1):
            mapping.append(
                prod([CR(AS[i].gen(index[i])) for i in range(len(index))]))
            for i in range(len(index) - 1, -1, -1):
                if index[i] == N[i]:
                    index[i] = 0
                else:
                    index[i] += 1
                    break  #only increment once
        phi = self.hom(mapping, Y)

        return phi
Beispiel #18
0
    def homogenize(self,n,newvar='h'):
        r"""
        Return the homogenization of ``self``. If ``self.domain()`` is a subscheme, the domain of
        the homogenized map is the projective embedding of ``self.domain()``. The domain and codomain
        can be homogenized at different coordinates: ``n[0]`` for the domain and ``n[1]`` for the codomain.

        INPUT:

        - ``newvar`` -- the name of the homogenization variable (only used when ``self.domain()`` is affine space)

        - ``n`` -- a tuple of nonnegative integers. If ``n`` is an integer, then the two values of
            the tuple are assumed to be the same.

        OUTPUT:

        - :class:`SchemMorphism_polynomial_projective_space`

        EXAMPLES::

            sage: A.<x,y>=AffineSpace(ZZ,2)
            sage: H=Hom(A,A)
            sage: f=H([(x^2-2)/x^5,y^2])
            sage: f.homogenize(2,'z')
            Scheme endomorphism of Projective Space of dimension 2 over Integer Ring
              Defn: Defined on coordinates by sending (x : y : z) to
                    (x^2*z^5 - 2*z^7 : x^5*y^2 : x^5*z^2)

        ::

            sage: A.<x,y>=AffineSpace(CC,2)
            sage: H=Hom(A,A)
            sage: f=H([(x^2-2)/(x*y),y^2-x])
            sage: f.homogenize((2,0),'z')
            Scheme endomorphism of Projective Space of dimension 2 over Complex
            Field with 53 bits of precision
              Defn: Defined on coordinates by sending (x : y : z) to
                    (x*y*z^2 : x^2*z^2 + (-2.00000000000000)*z^4 : x*y^3 - x^2*y*z)

        ::

            sage: A.<x,y>=AffineSpace(ZZ,2)
            sage: X=A.subscheme([x-y^2])
            sage: H=Hom(X,X)
            sage: f=H([9*y^2,3*y])
            sage: f.homogenize(2)
            Scheme endomorphism of Closed subscheme of Projective Space of dimension 2 over Integer Ring defined by:
              -x1^2 + x0*x2
              Defn: Defined on coordinates by sending (x0 : x1 : x2) to
                    (9*x0*x2 : 3*x1*x2 : x2^2)

        ::

            sage: R.<t>=PolynomialRing(ZZ)
            sage: A.<x,y>=AffineSpace(R,2)
            sage: H=Hom(A,A)
            sage: f=H([(x^2-2)/y,y^2-x])
            sage: f.homogenize((2,0),'z')
            Scheme endomorphism of Projective Space of dimension 2 over Univariate
            Polynomial Ring in t over Integer Ring
              Defn: Defined on coordinates by sending (x : y : z) to
                    (y*z^2 : x^2*z + (-2)*z^3 : y^3 - x*y*z)

        ::

            sage: A.<x>=AffineSpace(QQ,1)
            sage: H=End(A)
            sage: f=H([x^2-1])
            sage: f.homogenize((1,0),'y')
            Scheme endomorphism of Projective Space of dimension 1 over Rational
            Field
              Defn: Defined on coordinates by sending (x : y) to
                    (y^2 : x^2 - y^2)
        """
        A=self.domain()
        B=self.codomain()
        N=A.ambient_space().dimension_relative()
        NB=B.ambient_space().dimension_relative()

        #it is possible to homogenize the domain and codomain at different coordinates
        if isinstance(n,(tuple,list)):
            ind=tuple(n)
        else:
            ind=(n,n)

        #homogenize the domain
        Vars=list(A.ambient_space().variable_names())
        Vars.insert(ind[0],newvar)
        S=PolynomialRing(A.base_ring(),Vars)

        #find the denominators if a rational function
        try:
            l=lcm([self[i].denominator() for i in range(N)])
        except Exception:  #no lcm
            l=prod([self[i].denominator() for i in range(N)])

        from sage.rings.polynomial.polynomial_ring import PolynomialRing_general
        from sage.rings.polynomial.multi_polynomial_ring_generic import MPolynomialRing_generic
        if self.domain().base_ring()==RealField() or self.domain().base_ring()==ComplexField():
            F=[S(((self[i]*l).numerator())._maxima_().divide(self[i].denominator())[0].sage()) for i in range(N)]
        elif isinstance(self.domain().base_ring(),(PolynomialRing_general,MPolynomialRing_generic)):
            F=[S(((self[i]*l).numerator())._maxima_().divide(self[i].denominator())[0].sage()) for i in range(N)]
        else:
            F=[S(self[i]*l) for i in range(N)]

        #homogenize the codomain
        F.insert(ind[1],S(l))
        d=max([F[i].degree() for i in range(N+1)])
        F=[F[i].homogenize(newvar)*S.gen(N)**(d-F[i].degree()) for i in range(N+1)]
        from sage.schemes.affine.affine_space import is_AffineSpace
        if is_AffineSpace(A)==True:
            from sage.schemes.projective.projective_space import ProjectiveSpace
            X=ProjectiveSpace(A.base_ring(),NB,Vars)
        else:
            X=A.projective_embedding(ind[1]).codomain()
            phi=S.hom(X.ambient_space().gens(),X.ambient_space().coordinate_ring())
            F=[phi(f) for f in F]
        H=Hom(X,X)
        return(H(F))
Beispiel #19
0
def Conic(base_field, F=None, names=None, unique=True):
    r"""
    Return the plane projective conic curve defined by ``F``
    over ``base_field``.

    The input form ``Conic(F, names=None)`` is also accepted,
    in which case the fraction field of the base ring of ``F``
    is used as base field.

    INPUT:

    - ``base_field`` -- The base field of the conic.

    - ``names`` -- a list, tuple, or comma separated string
      of three variable names specifying the names
      of the coordinate functions of the ambient
      space `\Bold{P}^3`. If not specified or read
      off from ``F``, then this defaults to ``'x,y,z'``.

    - ``F`` -- a polynomial, list, matrix, ternary quadratic form,
      or list or tuple of 5 points in the plane.

                   If ``F`` is a polynomial or quadratic form,
                   then the output is the curve in the projective plane
                   defined by ``F = 0``.

                   If ``F`` is a polynomial, then it must be a polynomial
                   of degree at most 2 in 2 variables, or a homogeneous
                   polynomial in of degree 2 in 3 variables.

                   If ``F`` is a matrix, then the output is the zero locus
                   of `(x,y,z) F (x,y,z)^t`.

                   If ``F`` is a list of coefficients, then it has
                   length 3 or 6 and gives the coefficients of
                   the monomials `x^2, y^2, z^2` or all 6 monomials
                   `x^2, xy, xz, y^2, yz, z^2` in lexicographic order.

                   If ``F`` is a list of 5 points in the plane, then the output
                   is a conic through those points.

    - ``unique`` -- Used only if ``F`` is a list of points in the plane.
      If the conic through the points is not unique, then
      raise ``ValueError`` if and only if ``unique`` is True

    OUTPUT:

    A plane projective conic curve defined by ``F`` over a field.

    EXAMPLES:

    Conic curves given by polynomials ::

        sage: X,Y,Z = QQ['X,Y,Z'].gens()
        sage: Conic(X^2 - X*Y + Y^2 - Z^2)
        Projective Conic Curve over Rational Field defined by X^2 - X*Y + Y^2 - Z^2
        sage: x,y = GF(7)['x,y'].gens()
        sage: Conic(x^2 - x + 2*y^2 - 3, 'U,V,W')
        Projective Conic Curve over Finite Field of size 7 defined by U^2 + 2*V^2 - U*W - 3*W^2

    Conic curves given by matrices ::

        sage: Conic(matrix(QQ, [[1, 2, 0], [4, 0, 0], [7, 0, 9]]), 'x,y,z')
        Projective Conic Curve over Rational Field defined by x^2 + 6*x*y + 7*x*z + 9*z^2

        sage: x,y,z = GF(11)['x,y,z'].gens()
        sage: C = Conic(x^2+y^2-2*z^2); C
        Projective Conic Curve over Finite Field of size 11 defined by x^2 + y^2 - 2*z^2
        sage: Conic(C.symmetric_matrix(), 'x,y,z')
        Projective Conic Curve over Finite Field of size 11 defined by x^2 + y^2 - 2*z^2

    Conics given by coefficients ::

        sage: Conic(QQ, [1,2,3])
        Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + 3*z^2
        sage: Conic(GF(7), [1,2,3,4,5,6], 'X')
        Projective Conic Curve over Finite Field of size 7 defined by X0^2 + 2*X0*X1 - 3*X1^2 + 3*X0*X2 - 2*X1*X2 - X2^2

    The conic through a set of points ::

        sage: C = Conic(QQ, [[10,2],[3,4],[-7,6],[7,8],[9,10]]); C
        Projective Conic Curve over Rational Field defined by x^2 + 13/4*x*y - 17/4*y^2 - 35/2*x*z + 91/4*y*z - 37/2*z^2
        sage: C.rational_point()
        (10 : 2 : 1)
        sage: C.point([3,4])
        (3 : 4 : 1)

        sage: a=AffineSpace(GF(13),2)
        sage: Conic([a([x,x^2]) for x in range(5)])
        Projective Conic Curve over Finite Field of size 13 defined by x^2 - y*z
    """
    if not (base_field is None or isinstance(base_field, IntegralDomain)):
        if names is None:
            names = F
        F = base_field
        base_field = None
    if isinstance(F, (list, tuple)):
        if len(F) == 1:
            return Conic(base_field, F[0], names)
        if names is None:
            names = 'x,y,z'
        if len(F) == 5:
            L = []
            for f in F:
                if isinstance(f, SchemeMorphism_point_affine):
                    C = Sequence(f, universe=base_field)
                    if len(C) != 2:
                        raise TypeError("points in F (=%s) must be planar" % F)
                    C.append(1)
                elif isinstance(f, SchemeMorphism_point_projective_field):
                    C = Sequence(f, universe=base_field)
                elif isinstance(f, (list, tuple)):
                    C = Sequence(f, universe=base_field)
                    if len(C) == 2:
                        C.append(1)
                else:
                    raise TypeError("F (=%s) must be a sequence of planar " \
                                      "points" % F)
                if len(C) != 3:
                    raise TypeError("points in F (=%s) must be planar" % F)
                P = C.universe()
                if not isinstance(P, IntegralDomain):
                    raise TypeError("coordinates of points in F (=%s) must " \
                                     "be in an integral domain" % F)
                L.append(
                    Sequence([
                        C[0]**2, C[0] * C[1], C[0] * C[2], C[1]**2,
                        C[1] * C[2], C[2]**2
                    ], P.fraction_field()))
            M = Matrix(L)
            if unique and M.rank() != 5:
                raise ValueError("points in F (=%s) do not define a unique " \
                                   "conic" % F)
            con = Conic(base_field, Sequence(M.right_kernel().gen()), names)
            con.point(F[0])
            return con
        F = Sequence(F, universe=base_field)
        base_field = F.universe().fraction_field()
        temp_ring = PolynomialRing(base_field, 3, names)
        (x, y, z) = temp_ring.gens()
        if len(F) == 3:
            return Conic(F[0] * x**2 + F[1] * y**2 + F[2] * z**2)
        if len(F) == 6:
            return Conic(F[0]*x**2 + F[1]*x*y + F[2]*x*z + F[3]*y**2 + \
                         F[4]*y*z + F[5]*z**2)
        raise TypeError("F (=%s) must be a sequence of 3 or 6" \
                         "coefficients" % F)
    if is_QuadraticForm(F):
        F = F.matrix()
    if is_Matrix(F) and F.is_square() and F.ncols() == 3:
        if names is None:
            names = 'x,y,z'
        temp_ring = PolynomialRing(F.base_ring(), 3, names)
        F = vector(temp_ring.gens()) * F * vector(temp_ring.gens())

    if not is_MPolynomial(F):
        raise TypeError("F (=%s) must be a three-variable polynomial or " \
                         "a sequence of points or coefficients" % F)

    if F.total_degree() != 2:
        raise TypeError("F (=%s) must have degree 2" % F)

    if base_field is None:
        base_field = F.base_ring()
    if not isinstance(base_field, IntegralDomain):
        raise ValueError("Base field (=%s) must be a field" % base_field)
    base_field = base_field.fraction_field()
    if names is None:
        names = F.parent().variable_names()
    pol_ring = PolynomialRing(base_field, 3, names)

    if F.parent().ngens() == 2:
        (x, y, z) = pol_ring.gens()
        F = pol_ring(F(x / z, y / z) * z**2)

    if F == 0:
        raise ValueError("F must be nonzero over base field %s" % base_field)

    if F.total_degree() != 2:
        raise TypeError("F (=%s) must have degree 2 over base field %s" % \
                          (F, base_field))

    if F.parent().ngens() == 3:
        P2 = ProjectiveSpace(2, base_field, names)
        if is_PrimeFiniteField(base_field):
            return ProjectiveConic_prime_finite_field(P2, F)
        if is_FiniteField(base_field):
            return ProjectiveConic_finite_field(P2, F)
        if is_RationalField(base_field):
            return ProjectiveConic_rational_field(P2, F)
        if is_NumberField(base_field):
            return ProjectiveConic_number_field(P2, F)
        if is_FractionField(base_field) and (is_PolynomialRing(
                base_field.ring()) or is_MPolynomialRing(base_field.ring())):
            return ProjectiveConic_rational_function_field(P2, F)

        return ProjectiveConic_field(P2, F)

    raise TypeError("Number of variables of F (=%s) must be 2 or 3" % F)
Beispiel #20
0
    def parametrization(self, point=None, morphism=True):
        r"""
        Return a parametrization `f` of ``self`` together with the
        inverse of `f`.

        If ``point`` is specified, then that point is used
        for the parametrization. Otherwise, use ``self.rational_point()``
        to find a point.

        If ``morphism`` is True, then `f` is returned in the form
        of a Scheme morphism. Otherwise, it is a tuple of polynomials
        that gives the parametrization.

        EXAMPLES:

        An example over a finite field ::

            sage: c = Conic(GF(2), [1,1,1,1,1,0])
            sage: c.parametrization()
            (Scheme morphism:
              From: Projective Space of dimension 1 over Finite Field of size 2
              To:   Projective Conic Curve over Finite Field of size 2 defined by x^2 + x*y
            + y^2 + x*z + y*z
              Defn: Defined on coordinates by sending (x : y) to
                    (x*y + y^2 : x^2 + x*y : x^2 + x*y + y^2),
             Scheme morphism:
              From: Projective Conic Curve over Finite Field of size 2 defined by x^2 + x*y
            + y^2 + x*z + y*z
              To:   Projective Space of dimension 1 over Finite Field of size 2
              Defn: Defined on coordinates by sending (x : y : z) to
                    (y : x))

        An example with ``morphism = False`` ::

            sage: R.<x,y,z> = QQ[]
            sage: C = Curve(7*x^2 + 2*y*z + z^2)
            sage: (p, i) = C.parametrization(morphism = False); (p, i)
            ([-2*x*y, 7*x^2 + y^2, -2*y^2], [-1/2*x, -1/2*z])
            sage: C.defining_polynomial()(p)
            0
            sage: i[0](p) / i[1](p)
            x/y

        A ``ValueError`` is raised if ``self`` has no rational point ::

            sage: C = Conic(x^2 + y^2 + 7*z^2)
            sage: C.parametrization()
            Traceback (most recent call last):
            ...
            ValueError: Conic Projective Conic Curve over Rational Field defined by x^2 + y^2 + 7*z^2 has no rational points over Rational Field!

        A ``ValueError`` is raised if ``self`` is not smooth ::

            sage: C = Conic(x^2 + y^2)
            sage: C.parametrization()
            Traceback (most recent call last):
            ...
            ValueError: The conic self (=Projective Conic Curve over Rational Field defined by x^2 + y^2) is not smooth, hence does not have a parametrization.
        """
        if (not self._parametrization is None) and not point:
            par = self._parametrization
        else:
            if not self.is_smooth():
                raise ValueError("The conic self (=%s) is not smooth, hence does not have a parametrization." % self)
            if point is None:
                point = self.rational_point()
            point = Sequence(point)
            B = self.base_ring()
            Q = PolynomialRing(B, 'x,y')
            [x, y] = Q.gens()
            gens = self.ambient_space().gens()
            P = PolynomialRing(B, 4, ['X', 'Y', 'T0', 'T1'])
            [X, Y, T0, T1] = P.gens()
            c3 = [j for j in range(2,-1,-1) if point[j] != 0][0]
            c1 = [j for j in range(3) if j != c3][0]
            c2 = [j for j in range(3) if j != c3 and j != c1][0]
            L = [0,0,0]
            L[c1] = Y*T1*point[c1] + Y*T0
            L[c2] = Y*T1*point[c2] + X*T0
            L[c3] = Y*T1*point[c3]
            bezout = P(self.defining_polynomial()(L) / T0)
            t = [bezout([x,y,0,-1]),bezout([x,y,1,0])]
            par = (tuple([Q(p([x,y,t[0],t[1]])/y) for  p in L]),
                   tuple([gens[m]*point[c3]-gens[c3]*point[m]
                       for m in [c2,c1]]))
            if self._parametrization is None:
                self._parametrization = par
        if not morphism:
            return par
        P1 = ProjectiveSpace(self.base_ring(), 1, 'x,y')
        return P1.hom(par[0],self), self.Hom(P1)(par[1], check = False)
Beispiel #21
0
    def segre_embedding(self, PP=None, var='u'):
        r"""
        Return the Segre embedding of this space into the appropriate
        projective space.

        INPUT:

        -  ``PP`` -- (default: ``None``) ambient image projective space;
            this is constructed if it is not given.

        - ``var`` -- string, variable name of the image projective space, default `u` (optional).

        OUTPUT:

        Hom -- from this space to the appropriate subscheme of projective space.

        .. TODO::

            Cartesian products with more than two components.

        EXAMPLES::

            sage: X.<y0,y1,y2,y3,y4,y5> = ProductProjectiveSpaces(ZZ, [2, 2])
            sage: phi = X.segre_embedding(); phi
            Scheme morphism:
              From: Product of projective spaces P^2 x P^2 over Integer Ring
              To:   Closed subscheme of Projective Space of dimension 8 over Integer Ring defined by:
              -u5*u7 + u4*u8,
              -u5*u6 + u3*u8,
              -u4*u6 + u3*u7,
              -u2*u7 + u1*u8,
              -u2*u4 + u1*u5,
              -u2*u6 + u0*u8,
              -u1*u6 + u0*u7,
              -u2*u3 + u0*u5,
              -u1*u3 + u0*u4
              Defn: Defined by sending (y0 : y1 : y2 , y3 : y4 : y5) to
                    (y0*y3 : y0*y4 : y0*y5 : y1*y3 : y1*y4 : y1*y5 : y2*y3 : y2*y4 : y2*y5).

            ::

            sage: T = ProductProjectiveSpaces([1, 2], CC, 'z')
            sage: T.segre_embedding()
            Scheme morphism:
              From: Product of projective spaces P^1 x P^2 over Complex Field with 53 bits of precision
              To:   Closed subscheme of Projective Space of dimension 5 over Complex Field with 53 bits of precision defined by:
              -u2*u4 + u1*u5,
              -u2*u3 + u0*u5,
              -u1*u3 + u0*u4
              Defn: Defined by sending (z0 : z1 , z2 : z3 : z4) to
                    (z0*z2 : z0*z3 : z0*z4 : z1*z2 : z1*z3 : z1*z4).

            ::

            sage: T = ProductProjectiveSpaces([1, 2, 1], QQ, 'z')
            sage: T.segre_embedding()
            Scheme morphism:
              From: Product of projective spaces P^1 x P^2 x P^1 over Rational Field
              To:   Closed subscheme of Projective Space of dimension 11 over
            Rational Field defined by:
              -u9*u10 + u8*u11,
              -u7*u10 + u6*u11,
              -u7*u8 + u6*u9,
              -u5*u10 + u4*u11,
              -u5*u8 + u4*u9,
              -u5*u6 + u4*u7,
              -u5*u9 + u3*u11,
              -u5*u8 + u3*u10,
              -u5*u8 + u2*u11,
              -u4*u8 + u2*u10,
              -u3*u8 + u2*u9,
              -u3*u6 + u2*u7,
              -u3*u4 + u2*u5,
              -u5*u7 + u1*u11,
              -u5*u6 + u1*u10,
              -u3*u7 + u1*u9,
              -u3*u6 + u1*u8,
              -u5*u6 + u0*u11,
              -u4*u6 + u0*u10,
              -u3*u6 + u0*u9,
              -u2*u6 + u0*u8,
              -u1*u6 + u0*u7,
              -u1*u4 + u0*u5,
              -u1*u2 + u0*u3
              Defn: Defined by sending (z0 : z1 , z2 : z3 : z4 , z5 : z6) to
                    (z0*z2*z5 : z0*z2*z6 : z0*z3*z5 : z0*z3*z6 : z0*z4*z5 : z0*z4*z6
            : z1*z2*z5 : z1*z2*z6 : z1*z3*z5 : z1*z3*z6 : z1*z4*z5 : z1*z4*z6).
        """
        N = self._dims
        M = prod([n + 1 for n in N]) - 1
        CR = self.coordinate_ring()

        vars = list(self.coordinate_ring().variable_names()) + [
            var + str(i) for i in range(M + 1)
        ]
        R = PolynomialRing(self.base_ring(),
                           self.ngens() + M + 1,
                           vars,
                           order='lex')

        #set-up the elimination for the segre embedding
        mapping = []
        k = self.ngens()
        index = self.num_components() * [0]
        for count in range(M + 1):
            mapping.append(
                R.gen(k + count) -
                prod([CR(self[i].gen(index[i])) for i in range(len(index))]))
            for i in range(len(index) - 1, -1, -1):
                if index[i] == N[i]:
                    index[i] = 0
                else:
                    index[i] += 1
                    break  #only increment once

        #change the defining ideal of the subscheme into the variables
        I = R.ideal(list(self.defining_polynomials()) + mapping)
        J = I.groebner_basis()
        s = set(R.gens()[:self.ngens()])
        n = len(J) - 1
        L = []
        while s.isdisjoint(J[n].variables()):
            L.append(J[n])
            n = n - 1

        #create new subscheme
        if PP is None:
            PS = ProjectiveSpace(self.base_ring(), M,
                                 R.variable_names()[self.ngens():])
            Y = PS.subscheme(L)
        else:
            if PP.dimension_relative() != M:
                raise ValueError(
                    "projective Space %s must be dimension %s") % (PP, M)
            S = PP.coordinate_ring()
            psi = R.hom([0] * k + list(S.gens()), S)
            L = [psi(l) for l in L]
            Y = PP.subscheme(L)

        #create embedding for points
        mapping = []
        index = self.num_components() * [0]
        for count in range(M + 1):
            mapping.append(
                prod([CR(self[i].gen(index[i])) for i in range(len(index))]))
            for i in range(len(index) - 1, -1, -1):
                if index[i] == N[i]:
                    index[i] = 0
                else:
                    index[i] += 1
                    break  #only increment once
        phi = self.hom(mapping, Y)

        return phi
Beispiel #22
0
def AffineSpace(n, R=None, names='x', ambient_projective_space=None,
                default_embedding_index=None):
    r"""
    Return affine space of dimension ``n`` over the ring ``R``.

    EXAMPLES:

    The dimension and ring can be given in either order::

        sage: AffineSpace(3, QQ, 'x')
        Affine Space of dimension 3 over Rational Field
        sage: AffineSpace(5, QQ, 'x')
        Affine Space of dimension 5 over Rational Field
        sage: A = AffineSpace(2, QQ, names='XY'); A
        Affine Space of dimension 2 over Rational Field
        sage: A.coordinate_ring()
        Multivariate Polynomial Ring in X, Y over Rational Field

    Use the divide operator for base extension::

        sage: AffineSpace(5, names='x')/GF(17)
        Affine Space of dimension 5 over Finite Field of size 17

    The default base ring is `\ZZ`::

        sage: AffineSpace(5, names='x')
        Affine Space of dimension 5 over Integer Ring

    There is also an affine space associated to each polynomial ring::

        sage: R = GF(7)['x, y, z']
        sage: A = AffineSpace(R); A
        Affine Space of dimension 3 over Finite Field of size 7
        sage: A.coordinate_ring() is R
        True
    """
    if (is_MPolynomialRing(n) or is_PolynomialRing(n)) and R is None:
        R = n
        A = AffineSpace(R.ngens(), R.base_ring(), R.variable_names())
        A._coordinate_ring = R
        return A
    if isinstance(R, integer_types + (Integer,)):
        n, R = R, n
    if R is None:
        R = ZZ  # default is the integers
    if names is None:
        if n == 0:
            names = ''
        else:
            raise TypeError("you must specify the variables names of the coordinate ring")
    names = normalize_names(n, names)
    if default_embedding_index is not None and ambient_projective_space is None:
        from sage.schemes.projective.projective_space import ProjectiveSpace
        ambient_projective_space = ProjectiveSpace(n, R)
    if R in _Fields:
        if is_FiniteField(R):
            return AffineSpace_finite_field(n, R, names,
                                            ambient_projective_space, default_embedding_index)
        else:
            return AffineSpace_field(n, R, names,
                                     ambient_projective_space, default_embedding_index)
    return AffineSpace_generic(n, R, names, ambient_projective_space, default_embedding_index)
Beispiel #23
0
def HyperellipticCurve(f, h=0, names=None, PP=None, check_squarefree=True):
    r"""
    Returns the hyperelliptic curve `y^2 + h y = f`, for
    univariate polynomials `h` and `f`. If `h`
    is not given, then it defaults to 0.

    INPUT:

    -  ``f`` - univariate polynomial

    -  ``h`` - optional univariate polynomial

    -  ``names``  (default: ``["x","y"]``) - names for the
       coordinate functions

    -  ``check_squarefree`` (default: ``True``) - test if
       the input defines a hyperelliptic curve when f is
       homogenized to degree `2g+2` and h to degree
       `g+1` for some g.

    .. WARNING::

        When setting ``check_squarefree=False`` or using a base ring that is
        not a field, the output curves are not to be trusted. For example, the
        output of ``is_singular`` is always ``False``, without this being
        properly tested in that case.

    .. NOTE::

        The words "hyperelliptic curve" are normally only used for curves of
        genus at least two, but this class allows more general smooth double
        covers of the projective line (conics and elliptic curves), even though
        the class is not meant for those and some outputs may be incorrect.

    EXAMPLES:

    Basic examples::

        sage: R.<x> = QQ[]
        sage: HyperellipticCurve(x^5 + x + 1)
        Hyperelliptic Curve over Rational Field defined by y^2 = x^5 + x + 1
        sage: HyperellipticCurve(x^19 + x + 1, x-2)
        Hyperelliptic Curve over Rational Field defined by y^2 + (x - 2)*y = x^19 + x + 1

        sage: k.<a> = GF(9); R.<x> = k[]
        sage: HyperellipticCurve(x^3 + x - 1, x+a)
        Hyperelliptic Curve over Finite Field in a of size 3^2 defined by y^2 + (x + a)*y = x^3 + x + 2

    Characteristic two::

        sage: P.<x> = GF(8,'a')[]
        sage: HyperellipticCurve(x^7+1, x)
        Hyperelliptic Curve over Finite Field in a of size 2^3 defined by y^2 + x*y = x^7 + 1
        sage: HyperellipticCurve(x^8+x^7+1, x^4+1)
        Hyperelliptic Curve over Finite Field in a of size 2^3 defined by y^2 + (x^4 + 1)*y = x^8 + x^7 + 1

        sage: HyperellipticCurve(x^8+1, x)
        Traceback (most recent call last):
        ...
        ValueError: Not a hyperelliptic curve: highly singular at infinity.

        sage: HyperellipticCurve(x^8+x^7+1, x^4)
        Traceback (most recent call last):
        ...
        ValueError: Not a hyperelliptic curve: singularity in the provided affine patch.

        sage: F.<t> = PowerSeriesRing(FiniteField(2))
        sage: P.<x> = PolynomialRing(FractionField(F))
        sage: HyperellipticCurve(x^5+t, x)
        Hyperelliptic Curve over Laurent Series Ring in t over Finite Field of size 2 defined by y^2 + x*y = x^5 + t

    We can change the names of the variables in the output::

        sage: k.<a> = GF(9); R.<x> = k[]
        sage: HyperellipticCurve(x^3 + x - 1, x+a, names=['X','Y'])
        Hyperelliptic Curve over Finite Field in a of size 3^2 defined by Y^2 + (X + a)*Y = X^3 + X + 2

    This class also allows curves of genus zero or one, which are strictly
    speaking not hyperelliptic::

        sage: P.<x> = QQ[]
        sage: HyperellipticCurve(x^2+1)
        Hyperelliptic Curve over Rational Field defined by y^2 = x^2 + 1
        sage: HyperellipticCurve(x^4-1)
        Hyperelliptic Curve over Rational Field defined by y^2 = x^4 - 1
        sage: HyperellipticCurve(x^3+2*x+2)
        Hyperelliptic Curve over Rational Field defined by y^2 = x^3 + 2*x + 2

    Double roots::

        sage: P.<x> = GF(7)[]
        sage: HyperellipticCurve((x^3-x+2)^2*(x^6-1))
        Traceback (most recent call last):
        ...
        ValueError: Not a hyperelliptic curve: singularity in the provided affine patch.

        sage: HyperellipticCurve((x^3-x+2)^2*(x^6-1), check_squarefree=False)
        Hyperelliptic Curve over Finite Field of size 7 defined by y^2 = x^12 + 5*x^10 + 4*x^9 + x^8 + 3*x^7 + 3*x^6 + 2*x^4 + 3*x^3 + 6*x^2 + 4*x + 3

    The input for a (smooth) hyperelliptic curve of genus `g` should not
    contain polynomials of degree greater than `2g+2`. In the following
    example, the hyperelliptic curve has genus 2 and there exists a model
    `y^2 = F` of degree 6, so the model `y^2 + yh = f` of degree 200 is not
    allowed.::

        sage: P.<x> = QQ[]
        sage: h = x^100
        sage: F = x^6+1
        sage: f = F-h^2/4
        sage: HyperellipticCurve(f, h)
        Traceback (most recent call last):
        ...
        ValueError: Not a hyperelliptic curve: highly singular at infinity.

        sage: HyperellipticCurve(F)
        Hyperelliptic Curve over Rational Field defined by y^2 = x^6 + 1

    An example with a singularity over an inseparable extension of the
    base field::

        sage: F.<t> = GF(5)[]
        sage: P.<x> = F[]
        sage: HyperellipticCurve(x^5+t)
        Traceback (most recent call last):
        ...
        ValueError: Not a hyperelliptic curve: singularity in the provided affine patch.

    Input with integer coefficients creates objects with the integers
    as base ring, but only checks smoothness over `\QQ`, not over Spec(`\ZZ`).
    In other words, it is checked that the discriminant is non-zero, but it is
    not checked whether the discriminant is a unit in `\ZZ^*`.::

        sage: P.<x> = ZZ[]
        sage: HyperellipticCurve(3*x^7+6*x+6)
        Hyperelliptic Curve over Integer Ring defined by y^2 = 3*x^7 + 6*x + 6

    TESTS:

    Check that `f` can be a constant (see :trac:`15516`)::

        sage: R.<u> = PolynomialRing(Rationals())
        sage: HyperellipticCurve(-12, u^4 + 7)
        Hyperelliptic Curve over Rational Field defined by y^2 + (x^4 + 7)*y = -12

    Check that two curves with the same class name have the same class type::

        sage: R.<t> = PolynomialRing(GF(next_prime(10^9)))
        sage: C = HyperellipticCurve(t^5 + t + 1)
        sage: C2 = HyperellipticCurve(t^5 + 3*t + 1)
        sage: type(C2) == type(C)
        True

    Check that the inheritance is correct::

        sage: R.<t> = PolynomialRing(GF(next_prime(10^9)))
        sage: C = HyperellipticCurve(t^5 + t + 1)
        sage: type(C).mro()
        [<class 'sage.schemes.hyperelliptic_curves.constructor.HyperellipticCurve_g2_FiniteField_with_category'>,
         <class 'sage.schemes.hyperelliptic_curves.constructor.HyperellipticCurve_g2_FiniteField'>,
         <class 'sage.schemes.hyperelliptic_curves.hyperelliptic_g2.HyperellipticCurve_g2'>,
         <class 'sage.schemes.hyperelliptic_curves.hyperelliptic_finite_field.HyperellipticCurve_finite_field'>,
         <class 'sage.schemes.hyperelliptic_curves.hyperelliptic_generic.HyperellipticCurve_generic'>,
        ...]
    """
    # F is the discriminant; use this for the type check
    # rather than f and h, one of which might be constant.
    F = h**2 + 4 * f
    if not is_Polynomial(F):
        raise TypeError("Arguments f (= %s) and h (= %s) must be polynomials" %
                        (f, h))
    P = F.parent()
    f = P(f)
    h = P(h)
    df = f.degree()
    dh_2 = 2 * h.degree()
    if dh_2 < df:
        g = (df - 1) // 2
    else:
        g = (dh_2 - 1) // 2
    if check_squarefree:
        # Assuming we are working over a field, this checks that after
        # resolving the singularity at infinity, we get a smooth double cover
        # of P^1.
        if P(2) == 0:
            # characteristic 2
            if h == 0:
                raise ValueError(
                    "In characteristic 2, argument h (= %s) must be non-zero."
                    % h)
            if h[g + 1] == 0 and f[2 * g + 1]**2 == f[2 * g + 2] * h[g]**2:
                raise ValueError("Not a hyperelliptic curve: " \
                                  "highly singular at infinity.")
            should_be_coprime = [h, f * h.derivative()**2 + f.derivative()**2]
        else:
            # characteristic not 2
            if not F.degree() in [2 * g + 1, 2 * g + 2]:
                raise ValueError("Not a hyperelliptic curve: " \
                                  "highly singular at infinity.")
            should_be_coprime = [F, F.derivative()]
        try:
            smooth = should_be_coprime[0].gcd(
                should_be_coprime[1]).degree() == 0
        except (AttributeError, NotImplementedError, TypeError):
            try:
                smooth = should_be_coprime[0].resultant(
                    should_be_coprime[1]) != 0
            except (AttributeError, NotImplementedError, TypeError):
                raise NotImplementedError("Cannot determine whether " \
                      "polynomials %s have a common root. Use " \
                      "check_squarefree=False to skip this check." % \
                      should_be_coprime)
        if not smooth:
            raise ValueError("Not a hyperelliptic curve: " \
                              "singularity in the provided affine patch.")
    R = P.base_ring()
    PP = ProjectiveSpace(2, R)
    if names is None:
        names = ["x", "y"]

    superclass = []
    cls_name = ["HyperellipticCurve"]

    genus_classes = {2: HyperellipticCurve_g2}

    fields = [("FiniteField", is_FiniteField, HyperellipticCurve_finite_field),
              ("RationalField", is_RationalField,
               HyperellipticCurve_rational_field),
              ("pAdicField", is_pAdicField, HyperellipticCurve_padic_field)]

    if g in genus_classes:
        superclass.append(genus_classes[g])
        cls_name.append("g%s" % g)

    for name, test, cls in fields:
        if test(R):
            superclass.append(cls)
            cls_name.append(name)
            break

    class_name = "_".join(cls_name)
    cls = dynamic_class(class_name,
                        tuple(superclass),
                        HyperellipticCurve_generic,
                        doccls=HyperellipticCurve)
    return cls(PP, f, h, names=names, genus=g)
Beispiel #24
0
def HyperellipticCurve(f, h=None, names=None, PP=None, check_squarefree=True):
    r"""
    Returns the hyperelliptic curve `y^2 + h y = f`, for
    univariate polynomials `h` and `f`. If `h`
    is not given, then it defaults to 0.

    INPUT:

    -  ``f`` - univariate polynomial

    -  ``h`` - optional univariate polynomial

    -  ``names``  (default: ``["x","y"]``) - names for the
       coordinate functions

    -  ``check_squarefree`` (default: ``True``) - test if
       the input defines a hyperelliptic curve when f is
       homogenized to degree `2g+2` and h to degree
       `g+1` for some g.

    .. WARNING::

        When setting ``check_squarefree=False`` or using a base ring that is
        not a field, the output curves are not to be trusted. For example, the
        output of ``is_singular`` is always ``False``, without this being
        properly tested in that case.

    .. NOTE::

        The words "hyperelliptic curve" are normally only used for curves of
        genus at least two, but this class allows more general smooth double
        covers of the projective line (conics and elliptic curves), even though
        the class is not meant for those and some outputs may be incorrect.

    EXAMPLES:

    Basic examples::

        sage: R.<x> = QQ[]
        sage: HyperellipticCurve(x^5 + x + 1)
        Hyperelliptic Curve over Rational Field defined by y^2 = x^5 + x + 1
        sage: HyperellipticCurve(x^19 + x + 1, x-2)
        Hyperelliptic Curve over Rational Field defined by y^2 + (x - 2)*y = x^19 + x + 1

        sage: k.<a> = GF(9); R.<x> = k[]
        sage: HyperellipticCurve(x^3 + x - 1, x+a)
        Hyperelliptic Curve over Finite Field in a of size 3^2 defined by y^2 + (x + a)*y = x^3 + x + 2

    Characteristic two::

        sage: P.<x> = GF(8,'a')[]
        sage: HyperellipticCurve(x^7+1, x)
        Hyperelliptic Curve over Finite Field in a of size 2^3 defined by y^2 + x*y = x^7 + 1
        sage: HyperellipticCurve(x^8+x^7+1, x^4+1)
        Hyperelliptic Curve over Finite Field in a of size 2^3 defined by y^2 + (x^4 + 1)*y = x^8 + x^7 + 1

        sage: HyperellipticCurve(x^8+1, x)
        Traceback (most recent call last):
        ...
        ValueError: Not a hyperelliptic curve: highly singular at infinity.

        sage: HyperellipticCurve(x^8+x^7+1, x^4)
        Traceback (most recent call last):
        ...
        ValueError: Not a hyperelliptic curve: singularity in the provided affine patch.

        sage: F.<t> = PowerSeriesRing(FiniteField(2))
        sage: P.<x> = PolynomialRing(FractionField(F))
        sage: HyperellipticCurve(x^5+t, x)
        Hyperelliptic Curve over Laurent Series Ring in t over Finite Field of size 2 defined by y^2 + x*y = x^5 + t

    We can change the names of the variables in the output::

        sage: k.<a> = GF(9); R.<x> = k[]
        sage: HyperellipticCurve(x^3 + x - 1, x+a, names=['X','Y'])
        Hyperelliptic Curve over Finite Field in a of size 3^2 defined by Y^2 + (X + a)*Y = X^3 + X + 2

    This class also allows curves of genus zero or one, which are strictly
    speaking not hyperelliptic::

        sage: P.<x> = QQ[]
        sage: HyperellipticCurve(x^2+1)
        Hyperelliptic Curve over Rational Field defined by y^2 = x^2 + 1
        sage: HyperellipticCurve(x^4-1)
        Hyperelliptic Curve over Rational Field defined by y^2 = x^4 - 1
        sage: HyperellipticCurve(x^3+2*x+2)
        Hyperelliptic Curve over Rational Field defined by y^2 = x^3 + 2*x + 2

    Double roots::

        sage: P.<x> = GF(7)[]
        sage: HyperellipticCurve((x^3-x+2)^2*(x^6-1))
        Traceback (most recent call last):
        ...
        ValueError: Not a hyperelliptic curve: singularity in the provided affine patch.

        sage: HyperellipticCurve((x^3-x+2)^2*(x^6-1), check_squarefree=False)
        Hyperelliptic Curve over Finite Field of size 7 defined by y^2 = x^12 + 5*x^10 + 4*x^9 + x^8 + 3*x^7 + 3*x^6 + 2*x^4 + 3*x^3 + 6*x^2 + 4*x + 3

    The input for a (smooth) hyperelliptic curve of genus `g` should not
    contain polynomials of degree greater than `2g+2`. In the following
    example, the hyperelliptic curve has genus 2 and there exists a model
    `y^2 = F` of degree 6, so the model `y^2 + yh = f` of degree 200 is not
    allowed.::

        sage: P.<x> = QQ[]
        sage: h = x^100
        sage: F = x^6+1
        sage: f = F-h^2/4
        sage: HyperellipticCurve(f, h)
        Traceback (most recent call last):
        ...
        ValueError: Not a hyperelliptic curve: highly singular at infinity.

        sage: HyperellipticCurve(F)
        Hyperelliptic Curve over Rational Field defined by y^2 = x^6 + 1

    An example with a singularity over an inseparable extension of the
    base field::

        sage: F.<t> = GF(5)[]
        sage: P.<x> = F[]
        sage: HyperellipticCurve(x^5+t)
        Traceback (most recent call last):
        ...
        ValueError: Not a hyperelliptic curve: singularity in the provided affine patch.

    Input with integer coefficients creates objects with the integers
    as base ring, but only checks smoothness over `\QQ`, not over Spec(`\ZZ`).
    In other words, it is checked that the discriminant is non-zero, but it is
    not checked whether the discriminant is a unit in `\ZZ^*`.::

        sage: P.<x> = ZZ[]
        sage: HyperellipticCurve(3*x^7+6*x+6)
        Hyperelliptic Curve over Integer Ring defined by y^2 = 3*x^7 + 6*x + 6
    """
    if (not is_Polynomial(f)) or f == 0:
        raise TypeError, "Arguments f (=%s) and h (= %s) must be polynomials " \
                         "and f must be non-zero" % (f,h)
    P = f.parent()
    if h is None:
        h = P(0)
    try:
        h = P(h)
    except TypeError:
        raise TypeError, \
              "Arguments f (=%s) and h (= %s) must be polynomials in the same ring"%(f,h)
    df = f.degree()
    dh_2 = 2*h.degree()
    if dh_2 < df:
        g = (df-1)//2
    else:
        g = (dh_2-1)//2
    if check_squarefree:
        # Assuming we are working over a field, this checks that after
        # resolving the singularity at infinity, we get a smooth double cover
        # of P^1.
        if P(2) == 0:
            # characteristic 2
            if h == 0:
                raise ValueError, \
                   "In characteristic 2, argument h (= %s) must be non-zero."%h
            if h[g+1] == 0 and f[2*g+1]**2 == f[2*g+2]*h[g]**2:
                raise ValueError, "Not a hyperelliptic curve: " \
                                  "highly singular at infinity."
            should_be_coprime = [h, f*h.derivative()**2+f.derivative()**2]
        else:
            # characteristic not 2
            F = f + h**2/4
            if not F.degree() in [2*g+1, 2*g+2]:
                raise ValueError, "Not a hyperelliptic curve: " \
                                  "highly singular at infinity."
            should_be_coprime = [F, F.derivative()]
        try:
            smooth = should_be_coprime[0].gcd(should_be_coprime[1]).degree()==0
        except (AttributeError, NotImplementedError, TypeError):
            try:
                smooth = should_be_coprime[0].resultant(should_be_coprime[1])!=0
            except (AttributeError, NotImplementedError, TypeError):
                raise NotImplementedError, "Cannot determine whether " \
                      "polynomials %s have a common root. Use " \
                      "check_squarefree=False to skip this check." % \
                      should_be_coprime
        if not smooth:
            raise ValueError, "Not a hyperelliptic curve: " \
                              "singularity in the provided affine patch."
    R = P.base_ring()
    PP = ProjectiveSpace(2, R)
    if names is None:
        names = ["x","y"]
    if is_FiniteField(R):
        if g == 2:
            return HyperellipticCurve_g2_finite_field(PP, f, h, names=names, genus=g)
        else:
            return HyperellipticCurve_finite_field(PP, f, h, names=names, genus=g)
    elif is_RationalField(R):
        if g == 2:
            return HyperellipticCurve_g2_rational_field(PP, f, h, names=names, genus=g)
        else:
            return HyperellipticCurve_rational_field(PP, f, h, names=names, genus=g)
    elif is_pAdicField(R):
        if g == 2:
            return HyperellipticCurve_g2_padic_field(PP, f, h, names=names, genus=g)
        else:
            return HyperellipticCurve_padic_field(PP, f, h, names=names, genus=g)
    else:
        if g == 2:
            return HyperellipticCurve_g2_generic(PP, f, h, names=names, genus=g)
        else:
            return HyperellipticCurve_generic(PP, f, h, names=names, genus=g)
Beispiel #25
0
def julia_plot(c=-1, **kwds):
    r"""
    Plots the Julia set of a given complex `c` value. Users can specify whether
    they would like to display the Mandelbrot side by side with the Julia set.

    The Julia set of a given `c` value is the set of complex numbers for which
    the function `Q_c(z)=z^2+c` is bounded under iteration. The Julia set can
    be visualized by plotting each point in the set in the complex plane.
    Julia sets are examples of fractals when plotted in the complex plane.

    ALGORITHM:

    Define the map `Q_c(z) = z^2 + c` for some `c \in \mathbb{C}`. For every
    `p \in \mathbb{C}`, if `|Q_{c}^{k}(p)| > 2` for some `k \geq 0`,
    then `Q_{c}^{n}(p) \to \infty`. Let `N` be the maximum number of iterations.
    Compute the first `N` points on the orbit of `p` under `Q_c`. If for
    any `k < N`, `|Q_{c}^{k}(p)| > 2`, we stop the iteration and assign a color
    to the point `p` based on how quickly `p` escaped to infinity under
    iteration of `Q_c`. If `|Q_{c}^{i}(p)| \leq 2` for all `i \leq N`, we assume
    `p` is in the Julia set and assign the point `p` the color black.

    INPUT:

    - ``c`` -- complex (optional - default: ``-1``), complex point `c` that
      determines the Julia set.

    kwds:

    - ``period`` -- list (optional - default: ``None``), returns the Julia set
      for a random `c` value with the given (formal) cycle structure.

    - ``mandelbrot`` -- boolean (optional - default: ``True``), when set to
      ``True``, an image of the Mandelbrot set is appended to the right of the
      Julia set.

    - ``point_color`` -- RGB color (optional - default: ``[255, 0, 0]``),
      color of the point `c` in the Mandelbrot set.

    - ``x_center`` -- double (optional - default: ``-1.0``), Real part
      of center point.

    - ``y_center`` -- double (optional - default: ``0.0``), Imaginary part
      of center point.

    - ``image_width`` -- double (optional - default: ``4.0``), width of image
      in the complex plane.

    - ``max_iteration`` -- long (optional - default: ``500``), maximum number
      of iterations the map `Q_c(z)`.

    - ``pixel_count`` -- long (optional - default: ``500``), side length of
      image in number of pixels.

    - ``base_color`` -- RGB color (optional - default: ``[40, 40, 40]``), color
      used to determine the coloring of set.

    - ``iteration_level`` -- long (optional - default: 1), number of iterations
      between each color level.

    - ``number_of_colors`` -- long (optional - default: 30), number of colors
      used to plot image.

    - ``interact`` -- boolean (optional - default: ``False``), controls whether
      plot will have interactive functionality.

    OUTPUT:

    24-bit RGB image of the Julia set in the complex plane.

    EXAMPLES::

        sage: julia_plot()
        1001x500px 24-bit RGB image

    To display only the Julia set, set ``mandelbrot`` to ``False``::

        sage: julia_plot(mandelbrot=False)
        500x500px 24-bit RGB image

    To display an interactive plot of the Julia set in the Notebook,
    set ``interact`` to ``True``::

        sage: julia_plot(interact=True)
        <html>...</html>

    To return the Julia set of a random `c` value with (formal) cycle structure
    `(2,3)`, set ``period = [2,3]``::

        sage: julia_plot(period=[2,3])
        1001x500px 24-bit RGB image

    To return all of the Julia sets of `c` values with (formal) cycle structure
    `(2,3)`::

        sage: period = [2,3] # not tested
        ....: R.<c> = QQ[]
        ....: P.<x,y> = ProjectiveSpace(R,1)
        ....: f = DynamicalSystem([x^2+c*y^2, y^2])
        ....: L = f.dynatomic_polynomial(period).subs({x:0,y:1}).roots(ring=CC)
        ....: c_values = [k[0] for k in L]
        ....: for c in c_values:
        ....:     julia_plot(c)
    """

    x_center = kwds.pop("x_center", 0.0)
    y_center = kwds.pop("y_center", 0.0)
    image_width = kwds.pop("image_width", 4.0)
    max_iteration = kwds.pop("max_iteration", 500)
    pixel_count = kwds.pop("pixel_count", 500)
    base_color = kwds.pop("base_color", [50, 50, 50])
    iteration_level = kwds.pop("iteration_level", 1)
    number_of_colors = kwds.pop("number_of_colors", 50)
    point_color = kwds.pop("point_color", [255, 0, 0])
    interacts = kwds.pop("interact", False)
    mandelbrot = kwds.pop("mandelbrot", True)
    period = kwds.pop("period", None)

    if not period is None:
        R = PolynomialRing(QQ, 'c')
        c = R.gen()
        P = ProjectiveSpace(R, 1, 'x,y')
        x,y = P.gens()
        f = DynamicalSystem([x**2+c*y**2, y**2])
        L = f.dynatomic_polynomial(period).subs({x:0,y:1}).roots(ring=CC)
        c = L[randint(0,len(L)-1)][0]

    c_real = CC(c).real()
    c_imag = CC(c).imag()

    if interacts:
        @interact(layout={'bottom':[['real_center'], ['im_center'], ['width']],
         'top':[['iterations'], ['level_sep'], ['color_num'], ['mandel'],
         ['cx'], ['cy']], 'right':[['image_color'], ['pt_color']]})
        def _(cx = input_box(c_real, '$Re(c)$'),
            cy = input_box(c_imag, '$Im(c)$'),
            real_center=input_box(x_center, 'Real Center'),
            im_center=input_box(y_center, 'Imaginary Center'),
            width=input_box(image_width, 'Width of Image'),
            iterations=input_box(max_iteration, 'Max Number of Iterations'),
            level_sep=input_box(iteration_level, 'Iterations between Colors'),
            color_num=input_box(number_of_colors, 'Number of Colors'),
            image_color=color_selector(default=Color([j/255 for j in base_color]),
             label="Image Color", hide_box=True),
            pt_color=color_selector(default=Color([j/255 for j in point_color]),
             label="Point Color", hide_box=True),
            mandel=checkbox(mandelbrot, label='Mandelbrot set')):

            if mandel:
                return julia_helper(cx, cy, real_center, im_center,
                 width, iterations, pixel_count, level_sep, color_num,
                 image_color, pt_color).show()

            else:
                return fast_julia_plot(cx, cy, real_center, im_center,
                 width, iterations, pixel_count, level_sep, color_num,
                 image_color).show()

    else:
        if mandelbrot:
            return julia_helper(c_real, c_imag, x_center, y_center,
             image_width, max_iteration, pixel_count, iteration_level,
             number_of_colors, base_color, point_color)

        else:
            return fast_julia_plot(c_real, c_imag, x_center, y_center,
             image_width, max_iteration, pixel_count, iteration_level,
             number_of_colors, base_color)
Beispiel #26
0
def p_saturation(Plist, p, sieve=True, lin_combs=dict(), verbose=False):
    r"""
    Checks whether the list of points is `p`-saturated.

    INPUT:

    - ``Plist`` (list) - a list of independent points on one elliptic curve.

    - ``p`` (integer) - a prime number.

    - ``sieve`` (boolean) - if True, use a sieve (when there are at
      least 2 points); otherwise test all combinations.

    - ``lin_combs`` (dict) - a dict, possibly empty, with keys
      coefficient tuples and values the corresponding linear
      combinations of the points in ``Plist``.

    .. note::

       The sieve is much more efficient when the points are saturated
       and the number of points or the prime are large.

    OUTPUT:

    Either (``True``, ``lin_combs``) if the points are `p`-saturated,
    or (``False``, ``i``, ``newP``) if they are not `p`-saturated, in
    which case after replacing the i'th point with ``newP``, the
    subgroup generated contains that generated by ``Plist`` with
    index `p`.  Note that while proving the points `p`-saturated, the
    ``lin_combs`` dict may have been enlarged, so is returned.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.saturation import p_saturation
        sage: E = EllipticCurve('389a')
        sage: K.<i> = QuadraticField(-1)
        sage: EK = E.change_ring(K)
        sage: P = EK(1+i,-1-2*i)
        sage: p_saturation([P],2)
        (True, {})
        sage: p_saturation([2*P],2)
        (False, 0, (i + 1 : -2*i - 1 : 1))


        sage: Q = EK(0,0)
        sage: R = EK(-1,1)
        sage: p_saturation([P,Q,R],3)
        (True, {})

    Here we see an example where 19-saturation is proved, with the
    verbose flag set to True so that we can see what is going on::

        sage: p_saturation([P,Q,R],19, verbose=True)
        Using sieve method to saturate...
        There is 19-torsion modulo Fractional ideal (i + 14), projecting points
         --> [(184 : 27 : 1), (0 : 0 : 1), (196 : 1 : 1)]
         --rank is now 1
        There is 19-torsion modulo Fractional ideal (i - 14), projecting points
         --> [(15 : 168 : 1), (0 : 0 : 1), (196 : 1 : 1)]
         --rank is now 2
        There is 19-torsion modulo Fractional ideal (-2*i + 17), projecting points
         --> [(156 : 275 : 1), (0 : 0 : 1), (292 : 1 : 1)]
         --rank is now 3
        Reached full rank: points were 19-saturated
        (True, {})

    An example where the points are not 11-saturated::

        sage: res = p_saturation([P+5*Q,P-6*Q,R],11); res
        (False,
        0,
        (-5783311/14600041*i + 1396143/14600041 : 37679338314/55786756661*i + 3813624227/55786756661 : 1))

    That means that the 0'th point may be replaced by the displayed
    point to achieve an index gain of 11::

        sage: p_saturation([res[2],P-6*Q,R],11)
        (True, {})

    TESTS:

    See :trac:`27387`::

        sage: K.<a> = NumberField(x^2-x-26)
        sage: E = EllipticCurve([a,1-a,0,93-16*a, 3150-560*a])
        sage: P = E([65-35*a/3, (959*a-5377)/9])
        sage: T = E.torsion_points()[0]
        sage: from sage.schemes.elliptic_curves.saturation import p_saturation
        sage: p_saturation([P,T],2,True, {}, True)
        Using sieve method to saturate...
        ...
        -- points were not 2-saturated, gaining index 2
        (False, 0, (-1/4*a + 3/4 : 59/8*a - 317/8 : 1))
        sage: p_saturation([P,T],2,False, {})
        (False, 0, (-1/4*a + 3/4 : 59/8*a - 317/8 : 1))

    """
    # This code does a lot of residue field construction and elliptic curve
    # group structure computations, and would benefit immensely if those
    # were sped up.
    n = len(Plist)
    if n == 0:
        return (True, lin_combs)

    if n == 1:
        try:
            return (False, 0, Plist[0].division_points(p)[0])
        except IndexError:
            return (True, lin_combs)

    E = Plist[0].curve()

    if not sieve:
        from sage.groups.generic import multiples
        from sage.schemes.projective.projective_space import ProjectiveSpace

        mults = [list(multiples(P, p))
                 for P in Plist[:-1]] + [list(multiples(Plist[-1], 2))]
        E0 = E(0)

        for v in ProjectiveSpace(GF(p), n - 1):  # an iterator
            w = tuple(int(x) for x in v)
            try:
                P = lin_combs[w]
            except KeyError:
                P = sum([m[c] for m, c in zip(mults, w)], E0)
                lin_combs[w] = P
            divisors = P.division_points(p)
            if divisors:
                if verbose:
                    print("  points not saturated at %s, increasing index" % p)
                # w will certainly have a coordinate equal to 1
                return (False, w.index(1), divisors[0])
        # we only get here if no linear combination is divisible by p,
        # so the points are p-saturated:
        return (True, lin_combs)

    # Now we use the more sophisticated sieve to either prove
    # p-saturation, or compute a much smaller list of possible points
    # to test for p-divisibility:

    E = Plist[0].curve()
    K = E.base_ring()

    def projections(Q, p):
        r"""
        Project points onto (E mod Q)(K mod Q) \otimes \F_p.

        Returns a list of 0, 1 or 2 vectors in \F_p^n
        """
        # NB we do not do E.reduction(Q) since that may change the
        # model which will cause problems when we reduce points.
        # see trac #27387
        Ek = E.change_ring(K.residue_field(Q))
        G = Ek.abelian_group()  # cached!

        if not p.divides(G.order()):
            return []

        if verbose:
            print("There is %s-torsion modulo %s, projecting points" % (p, Q))

        def proj1(pt):
            try:
                return Ek(pt)
            except ZeroDivisionError:
                return Ek(0)

        projPlist = [proj1(pt) for pt in Plist]

        if verbose:
            print(" --> %s" % projPlist)

        gens = G.gens()
        orders = G.generator_orders()
        from sage.groups.generic import discrete_log_lambda
        from sage.modules.all import vector

        if len(gens) == 1:  # cyclic case
            n = orders[0]
            m = n.prime_to_m_part(p)  # prime-to-p part of order
            p2 = n // m  # p-primary order
            g = (m * gens[0]).element()  # generator of p-primary part
            assert g.order() == p2
            # if verbose:
            #     print("   cyclic, %s-primary part generated by %s of order %s" % (p,g,p2))

            # multiplying any point by m takes it into the p-primary
            # part -- this way we do discrete logs in a cyclic group
            # of order p2 (which will often just be p) and not order n

            v = [
                discrete_log_lambda(m * pt, g, (0, p2), '+')
                for pt in projPlist
            ]
            # entries of v are well-defined mod p2, hence mod p
            return [vector(GF(p), v)]

        # Now the reduction is not cyclic, but we still handle
        # separately the case where the p-primary part is cyclic,
        # where a similar discrete log computation suffices; in the
        # remaining case (p-rank 2) we use the Weil pairing instead.

        # Note the code makes no assumption about which generator
        # order divides the other, since conventions differ!

        mm = [ni.prime_to_m_part(p) for ni in orders]
        pp = [ni // mi for ni, mi in zip(orders, mm)]  # p-powers
        gg = [(mi * gi).element()
              for mi, gi in zip(mm, gens)]  # p-power order gens
        m = max(mm)  # = lcm(mm), the prime-to-p exponent of G;
        # multiply by this to map onto the p-primary part.
        p2 = max(pp)  # p-exponent of G
        p1 = min(pp)  # == 1 iff p-primary part is cyclic

        if p1 == 1:  # p-primary part is cyclic of order p2
            g = gg[pp.index(p2)]  # g generates the p-primary part
            # and we do discrete logs there:

            assert g.order() == p2
            # if verbose:
            #     print("   non-cyclic but %s-primary part cyclic generated by %s of order %s" % (p,g,p2))
            v = [
                discrete_log_lambda(m * pt, g, (0, p2), '+')
                for pt in projPlist
            ]
            # entries of v are well-defined mod p2, hence mod p
            return [vector(GF(p), v)]

        # Now the p-primary part is non-cyclic of exponent p2; we use
        # Weil pairings of this order, whose values are p1'th roots of unity.

        # if verbose:
        #     print("   %s-primary part non-cyclic generated by %s of orders %s" % (p,gg,pp))
        zeta = gg[0].weil_pairing(gg[1], p2)  # a primitive p1'th root of unity
        if zeta.multiplicative_order() != p1:
            if verbose:
                print("Ek = ", Ek)
                print("(p1,p2) = (%s,%s)" % (p1, p2))
                print("gg = (%s,%s)" % (gg[0], gg[1]))
            raise RuntimeError("Weil pairing error during saturation.")

        # these are the homomorphisms from E to F_p (for g in gg):
        def a(pt, g):
            """Return the zeta-based log of the Weil pairing of ``m*pt`` with ``g``.
            """
            w = (m * pt).weil_pairing(g, p2)  # result is a p1'th root of unity
            return discrete_log_lambda(w, zeta, (0, p1), '*')

        return [vector(GF(p), [a(pt, gen) for pt in projPlist]) for gen in gg]

    if verbose:
        print("Using sieve method to saturate...")
    from sage.matrix.all import matrix
    A = matrix(GF(p), 0, n)
    rankA = 0
    count = 0
    from sage.sets.primes import Primes
    Edisc = E.discriminant()
    for q in Primes():
        for Q in K.primes_above(q, degree=1):
            if not E.is_local_integral_model(Q) or Edisc.valuation(Q) != 0:
                continue
            vecs = projections(Q, p)
            if len(vecs) == 0:
                continue
            for v in vecs:
                A = matrix(A.rows() + [v])
            newrank = A.rank()
            if verbose:
                print(" --rank is now %s" % newrank)
            if newrank == n:
                if verbose:
                    print("Reached full rank: points were %s-saturated" % p)
                return (True, lin_combs)
            if newrank == rankA:
                count += 1
                if count == 10:
                    if verbose:
                        print("! rank same for 10 steps, checking kernel...")
                    # no increase in rank for the last 10 primes Q
                    # find the points in the kernel and call the no-sieve version
                    vecs = A.right_kernel().basis()
                    if verbose:
                        print("kernel vectors: %s" % vecs)
                    Rlist = [
                        sum([int(vi) * Pi for vi, Pi in zip(v, Plist)], E(0))
                        for v in vecs
                    ]
                    if verbose:
                        print("points generating kernel: %s" % Rlist)

                    res = p_saturation(Rlist,
                                       p,
                                       sieve=False,
                                       lin_combs=dict(),
                                       verbose=verbose)
                    if res[0]:  # points really were saturated
                        if verbose:
                            print("-- points were %s-saturated" % p)
                        return (True, lin_combs)
                    else:  # they were not, and Rlist[res[1]] can be
                        # replaced by res[2] to enlarge the span; we
                        # need to find a point in Plist which can be
                        # replaced: any one with a nonzero coordinate in
                        # the appropriate kernel vector will do.
                        if verbose:
                            print(
                                "-- points were not %s-saturated, gaining index %s"
                                % (p, p))
                        j = next(i for i, x in enumerate(vecs[res[1]]) if x)
                        return (False, j, res[2])
                else:  # rank stayed same; carry on using more Qs
                    pass
            else:  # rank went up but is <n; carry on using more Qs
                rankA = newrank
                count = 0
    # We reach here only if using all good primes of norm<1000 the
    # rank never stuck for 10 consecutive Qs but is still < n.  That
    # means that n is rather large, or perhaps that E has a large
    # number of bad primes.  So we fall back to the naive method,
    # still making use of any partial information about the kernel we
    # have acquired.

    vecs = A.right_kernel().basis()
    Rlist = [
        sum([int(vi) * Pi for vi, Pi in zip(v, Plist)], E(0)) for v in vecs
    ]
    res = p_saturation(Rlist,
                       p,
                       sieve=False,
                       lin_combs=dict(),
                       verbose=verbose)
    if res[0]:  # points really were saturated
        return (True, lin_combs)
    else:
        j = next(i for i, x in enumerate(vecs[res[1]]) if x)
        return (False, j, res[2])
Beispiel #27
0
    def parametrization(self, point=None, morphism=True):
        r"""
        Return a parametrization `f` of ``self`` together with the
        inverse of `f`.

        If ``point`` is specified, then that point is used
        for the parametrization. Otherwise, use ``self.rational_point()``
        to find a point.

        If ``morphism`` is True, then `f` is returned in the form
        of a Scheme morphism. Otherwise, it is a tuple of polynomials
        that gives the parametrization.

        EXAMPLES:

        An example over a finite field ::

            sage: c = Conic(GF(2), [1,1,1,1,1,0])
            sage: c.parametrization()
            (Scheme morphism:
              From: Projective Space of dimension 1 over Finite Field of size 2
              To:   Projective Conic Curve over Finite Field of size 2 defined by x^2 + x*y
            + y^2 + x*z + y*z
              Defn: Defined on coordinates by sending (x : y) to
                    (x*y + y^2 : x^2 + x*y : x^2 + x*y + y^2),
             Scheme morphism:
              From: Projective Conic Curve over Finite Field of size 2 defined by x^2 + x*y
            + y^2 + x*z + y*z
              To:   Projective Space of dimension 1 over Finite Field of size 2
              Defn: Defined on coordinates by sending (x : y : z) to
                    (y : x))

        An example with ``morphism = False`` ::

            sage: R.<x,y,z> = QQ[]
            sage: C = Curve(7*x^2 + 2*y*z + z^2)
            sage: (p, i) = C.parametrization(morphism = False); (p, i)
            ([-2*x*y, x^2 + 7*y^2, -2*x^2], [-1/2*x, 1/7*y + 1/14*z])
            sage: C.defining_polynomial()(p)
            0
            sage: i[0](p) / i[1](p)
            x/y

        A ``ValueError`` is raised if ``self`` has no rational point ::

            sage: C = Conic(x^2 + y^2 + 7*z^2)
            sage: C.parametrization()
            Traceback (most recent call last):
            ...
            ValueError: Conic Projective Conic Curve over Rational Field defined by x^2 + y^2 + 7*z^2 has no rational points over Rational Field!

        A ``ValueError`` is raised if ``self`` is not smooth ::

            sage: C = Conic(x^2 + y^2)
            sage: C.parametrization()
            Traceback (most recent call last):
            ...
            ValueError: The conic self (=Projective Conic Curve over Rational Field defined by x^2 + y^2) is not smooth, hence does not have a parametrization.
        """
        if (not self._parametrization is None) and not point:
            par = self._parametrization
        else:
            if not self.is_smooth():
                raise ValueError("The conic self (=%s) is not smooth, hence does not have a parametrization." % self)
            if point is None:
                point = self.rational_point()
            point = Sequence(point)
            B = self.base_ring()
            Q = PolynomialRing(B, 'x,y')
            [x, y] = Q.gens()
            gens = self.ambient_space().gens()
            P = PolynomialRing(B, 4, ['X', 'Y', 'T0', 'T1'])
            [X, Y, T0, T1] = P.gens()
            c3 = [j for j in range(2,-1,-1) if point[j] != 0][0]
            c1 = [j for j in range(3) if j != c3][0]
            c2 = [j for j in range(3) if j != c3 and j != c1][0]
            L = [0,0,0]
            L[c1] = Y*T1*point[c1] + Y*T0
            L[c2] = Y*T1*point[c2] + X*T0
            L[c3] = Y*T1*point[c3]
            bezout = P(self.defining_polynomial()(L) / T0)
            t = [bezout([x,y,0,-1]),bezout([x,y,1,0])]
            par = (tuple([Q(p([x,y,t[0],t[1]])/y) for  p in L]),
                   tuple([gens[m]*point[c3]-gens[c3]*point[m]
                       for m in [c2,c1]]))
            if self._parametrization is None:
                self._parametrization = par
        if not morphism:
            return par
        P1 = ProjectiveSpace(self.base_ring(), 1, 'x,y')
        return P1.hom(par[0],self), self.Hom(P1)(par[1], check = False)
    def dual(self):
        r"""
        Return the projective dual of the given subscheme of projective space.

        INPUT:

        - ``X`` -- A subscheme of projective space. At present, ``X`` is
          required to be an irreducible and reduced hypersurface defined
          over `\QQ` or a finite field.

        OUTPUT:

        - The dual of ``X`` as a subscheme of the dual projective space.

        EXAMPLES:

        The dual of a smooth conic in the plane is also a smooth conic::

            sage: R.<x, y, z> = QQ[]
            sage: P.<x, y, z> = ProjectiveSpace(2, QQ)
            sage: I = R.ideal(x^2 + y^2 + z^2)
            sage: X = P.subscheme(I)
            sage: X.dual()
            Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
              y0^2 + y1^2 + y2^2

        The dual of the twisted cubic curve in projective 3-space is a singular
        quartic surface. In the following example, we compute the dual of this
        surface, which by double duality is equal to the twisted cubic itself.
        The output is the twisted cubic as an intersection of three quadrics::

            sage: R.<x, y, z, w> = QQ[]
            sage: P.<x, y, z, w> = ProjectiveSpace(3, QQ)
            sage: I = R.ideal(y^2*z^2 - 4*x*z^3 - 4*y^3*w + 18*x*y*z*w - 27*x^2*w^2)
            sage: X = P.subscheme(I)
            sage: X.dual()
            Closed subscheme of Projective Space of dimension 3 over
            Rational Field defined by:
              y2^2 - y1*y3,
              y1*y2 - y0*y3,
              y1^2 - y0*y2

        The singular locus of the quartic surface in the last example
        is itself supported on a twisted cubic::

            sage: X.Jacobian().radical()
            Ideal (z^2 - 3*y*w, y*z - 9*x*w, y^2 - 3*x*z) of Multivariate
            Polynomial Ring in x, y, z, w over Rational Field

        An example over a finite field::

            sage: R = PolynomialRing(GF(61), 'a,b,c')
            sage: P.<a, b, c> = ProjectiveSpace(2, R.base_ring())
            sage: X = P.subscheme(R.ideal(a*a+2*b*b+3*c*c))
            sage: X.dual()
            Closed subscheme of Projective Space of dimension 2 over
            Finite Field of size 61 defined by:
            y0^2 - 30*y1^2 - 20*y2^2

        TESTS::

            sage: R = PolynomialRing(Qp(3), 'a,b,c')
            sage: P.<a, b, c> = ProjectiveSpace(2, R.base_ring())
            sage: X = P.subscheme(R.ideal(a*a+2*b*b+3*c*c))
            sage: X.dual()
            Traceback (most recent call last):
            ...
            NotImplementedError: base ring must be QQ or a finite field
        """
        from sage.libs.singular.function_factory import ff

        K = self.base_ring()
        if not(is_RationalField(K) or is_FiniteField(K)):
            raise NotImplementedError("base ring must be QQ or a finite field")
        I = self.defining_ideal()
        m = I.ngens()
        n = I.ring().ngens() - 1
        if (m != 1 or (n < 1) or I.is_zero()
            or I.is_trivial() or not I.is_prime()):
            raise NotImplementedError("At the present, the method is only"
                                      " implemented for irreducible and"
                                      " reduced hypersurfaces and the given"
                                      " list of generators for the ideal must"
                                      " have exactly one element.")
        R = PolynomialRing(K, 'x', n + 1)
        from sage.schemes.projective.projective_space import ProjectiveSpace
        Pd = ProjectiveSpace(n, K, 'y')
        Rd = Pd.coordinate_ring()
        x = R.variable_names()
        y = Rd.variable_names()
        S = PolynomialRing(K, x + y + ('t',))
        if S.has_coerce_map_from(I.ring()):
            T = PolynomialRing(K, 'w', n + 1)
            I_S = (I.change_ring(T)).change_ring(S)
        else:
            I_S = I.change_ring(S)
        f_S = I_S.gens()[0]
        z = S.gens()
        J = I_S
        for i in range(n + 1):
            J = J + S.ideal(z[-1] * f_S.derivative(z[i]) - z[i + n + 1])

        sat = ff.elim__lib.sat

        max_ideal = S.ideal(z[n + 1: 2 * n + 2])
        J_sat_gens = sat(J, max_ideal)[0]
        J_sat = S.ideal(J_sat_gens)
        L = J_sat.elimination_ideal(z[0: n + 1] + (z[-1],))
        return Pd.subscheme(L.change_ring(Rd))
Beispiel #29
0
    def parametrization(self, point=None, morphism=True):
        r"""
        Return a parametrization `f` of ``self`` together with the
        inverse of `f`.

        If ``point`` is specified, then that point is used
        for the parametrization. Otherwise, use ``self.rational_point()``
        to find a point.

        If ``morphism`` is True, then `f` is returned in the form
        of a Scheme morphism. Otherwise, it is a tuple of polynomials
        that gives the parametrization.

        ALGORITHM:

        Uses the PARI/GP function ``qfparam``.

        EXAMPLES ::

            sage: c = Conic([1,1,-1])
            sage: c.parametrization()
            (Scheme morphism:
              From: Projective Space of dimension 1 over Rational Field
              To:   Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2
              Defn: Defined on coordinates by sending (x : y) to
                    (2*x*y : x^2 - y^2 : x^2 + y^2),
             Scheme morphism:
              From: Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2
              To:   Projective Space of dimension 1 over Rational Field
              Defn: Defined on coordinates by sending (x : y : z) to
                    (1/2*x : -1/2*y + 1/2*z))

        An example with ``morphism = False`` ::

            sage: R.<x,y,z> = QQ[]
            sage: C = Curve(7*x^2 + 2*y*z + z^2)
            sage: (p, i) = C.parametrization(morphism = False); (p, i)
            ([-2*x*y, x^2 + 7*y^2, -2*x^2], [-1/2*x, 1/7*y + 1/14*z])
            sage: C.defining_polynomial()(p)
            0
            sage: i[0](p) / i[1](p)
            x/y

        A ``ValueError`` is raised if ``self`` has no rational point ::

            sage: C = Conic(x^2 + 2*y^2 + z^2)
            sage: C.parametrization()
            Traceback (most recent call last):
            ...
            ValueError: Conic Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + z^2 has no rational points over Rational Field!

        A ``ValueError`` is raised if ``self`` is not smooth ::

            sage: C = Conic(x^2 + y^2)
            sage: C.parametrization()
            Traceback (most recent call last):
            ...
            ValueError: The conic self (=Projective Conic Curve over Rational Field defined by x^2 + y^2) is not smooth, hence does not have a parametrization.
        """
        if (not self._parametrization is None) and not point:
            par = self._parametrization
        else:
            if not self.is_smooth():
                raise ValueError(
                    "The conic self (=%s) is not smooth, hence does not have a parametrization."
                    % self)
            if point is None:
                point = self.rational_point()
            point = Sequence(point)
            Q = PolynomialRing(QQ, 'x,y')
            [x, y] = Q.gens()
            gens = self.ambient_space().gens()
            M = self.symmetric_matrix()
            M *= lcm([t.denominator() for t in M.list()])
            par1 = qfparam(M, point)
            B = Matrix([[par1[i][j] for j in range(3)] for i in range(3)])
            # self is in the image of B and does not lie on a line,
            # hence B is invertible
            A = B.inverse()
            par2 = [sum([A[i, j] * gens[j] for j in range(3)]) for i in [1, 0]]
            par = ([Q(pol(x / y) * y**2) for pol in par1], par2)
            if self._parametrization is None:
                self._parametrization = par
        if not morphism:
            return par
        P1 = ProjectiveSpace(self.base_ring(), 1, 'x,y')
        return P1.hom(par[0], self), self.Hom(P1)(par[1], check=False)
Beispiel #30
0
    def parametrization(self, point=None, morphism=True):
        r"""
        Return a parametrization `f` of ``self`` together with the
        inverse of `f`.

        If ``point`` is specified, then that point is used
        for the parametrization. Otherwise, use ``self.rational_point()``
        to find a point.

        If ``morphism`` is True, then `f` is returned in the form
        of a Scheme morphism. Otherwise, it is a tuple of polynomials
        that gives the parametrization.

        ALGORITHM:

        Uses the PARI/GP function ``qfparam``.

        EXAMPLES ::

            sage: c = Conic([1,1,-1])
            sage: c.parametrization()
            (Scheme morphism:
              From: Projective Space of dimension 1 over Rational Field
              To:   Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2
              Defn: Defined on coordinates by sending (x : y) to
                    (2*x*y : x^2 - y^2 : x^2 + y^2),
             Scheme morphism:
              From: Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2
              To:   Projective Space of dimension 1 over Rational Field
              Defn: Defined on coordinates by sending (x : y : z) to
                    (1/2*x : -1/2*y + 1/2*z))

        An example with ``morphism = False`` ::

            sage: R.<x,y,z> = QQ[]
            sage: C = Curve(7*x^2 + 2*y*z + z^2)
            sage: (p, i) = C.parametrization(morphism = False); (p, i)
            ([-2*x*y, x^2 + 7*y^2, -2*x^2], [-1/2*x, 1/7*y + 1/14*z])
            sage: C.defining_polynomial()(p)
            0
            sage: i[0](p) / i[1](p)
            x/y

        A ``ValueError`` is raised if ``self`` has no rational point ::

            sage: C = Conic(x^2 + 2*y^2 + z^2)
            sage: C.parametrization()
            Traceback (most recent call last):
            ...
            ValueError: Conic Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + z^2 has no rational points over Rational Field!

        A ``ValueError`` is raised if ``self`` is not smooth ::

            sage: C = Conic(x^2 + y^2)
            sage: C.parametrization()
            Traceback (most recent call last):
            ...
            ValueError: The conic self (=Projective Conic Curve over Rational Field defined by x^2 + y^2) is not smooth, hence does not have a parametrization.
        """
        if (not self._parametrization is None) and not point:
            par = self._parametrization
        else:
            if not self.is_smooth():
                raise ValueError("The conic self (=%s) is not smooth, hence does not have a parametrization." % self)
            if point is None:
                point = self.rational_point()
            point = Sequence(point)
            Q = PolynomialRing(QQ, 'x,y')
            [x, y] = Q.gens()
            gens = self.ambient_space().gens()
            M = self.symmetric_matrix()
            M *= lcm([ t.denominator() for t in M.list() ])
            par1 = qfparam(M, point)
            B = Matrix([[par1[i][j] for j in range(3)] for i in range(3)])
            # self is in the image of B and does not lie on a line,
            # hence B is invertible
            A = B.inverse()
            par2 = [sum([A[i,j]*gens[j] for j in range(3)]) for i in [1,0]]
            par = ([Q(pol(x/y)*y**2) for pol in par1], par2)
            if self._parametrization is None:
                self._parametrization = par
        if not morphism:
            return par
        P1 = ProjectiveSpace(self.base_ring(), 1, 'x,y')
        return P1.hom(par[0],self), self.Hom(P1)(par[1], check = False)
Beispiel #31
0
    def projective_embedding(self, i=None, PP=None):
        """
        Returns a morphism from this space into an ambient projective space
        of the same dimension.

        INPUT:


        -  ``i`` -- integer (default: dimension of self = last
           coordinate) determines which projective embedding to compute. The
           embedding is that which has a 1 in the i-th coordinate, numbered
           from 0.

        -  ``PP`` -- (default: None) ambient projective space, i.e.,
           codomain of morphism; this is constructed if it is not
           given.

        EXAMPLES::

            sage: AA = AffineSpace(2, QQ, 'x')
            sage: pi = AA.projective_embedding(0); pi
            Scheme morphism:
              From: Affine Space of dimension 2 over Rational Field
              To:   Projective Space of dimension 2 over Rational Field
              Defn: Defined on coordinates by sending (x0, x1) to
                    (1 : x0 : x1)
            sage: z = AA(3,4)
            sage: pi(z)
            (1/4 : 3/4 : 1)
            sage: pi(AA(0,2))
            (1/2 : 0 : 1)
            sage: pi = AA.projective_embedding(1); pi
            Scheme morphism:
              From: Affine Space of dimension 2 over Rational Field
              To:   Projective Space of dimension 2 over Rational Field
              Defn: Defined on coordinates by sending (x0, x1) to
                    (x0 : 1 : x1)
            sage: pi(z)
            (3/4 : 1/4 : 1)
            sage: pi = AA.projective_embedding(2)
            sage: pi(z)
            (3 : 4 : 1)

        ::

            sage: A.<x,y> = AffineSpace(ZZ,2)
            sage: A.projective_embedding(2).codomain().affine_patch(2) == A
            True
        """
        n = self.dimension_relative()
        if i is None:
            try:
                i = self._default_embedding_index
            except AttributeError:
                i = int(n)
        else:
            i = int(i)

        try:
            phi = self.__projective_embedding[i]
            #assume that if you've passed in a new codomain you want to override
            #the existing embedding
            if PP is None or phi.codomain() == PP:
                return (phi)
        except AttributeError:
            self.__projective_embedding = {}
        except KeyError:
            pass

        #if no ith embedding exists, we may still be here with PP==None
        if PP is None:
            from sage.schemes.projective.projective_space import ProjectiveSpace
            PP = ProjectiveSpace(n, self.base_ring())
        elif PP.dimension_relative() != n:
            raise ValueError("Projective Space must be of dimension %s" % (n))

        R = self.coordinate_ring()
        v = list(R.gens())
        if n < 0 or n > self.dimension_relative():
            raise ValueError(
                "Argument i (=%s) must be between 0 and %s, inclusive" %
                (i, n))
        v.insert(i, R(1))
        phi = self.hom(v, PP)
        self.__projective_embedding[i] = phi
        #make affine patch and projective embedding match
        PP.affine_patch(i, self)
        return phi
Beispiel #32
0
def AffineSpace(n, R=None, names=None, ambient_projective_space=None,
                default_embedding_index=None):
    r"""
    Return affine space of dimension ``n`` over the ring ``R``.

    EXAMPLES:

    The dimension and ring can be given in either order::

        sage: AffineSpace(3, QQ, 'x')
        Affine Space of dimension 3 over Rational Field
        sage: AffineSpace(5, QQ, 'x')
        Affine Space of dimension 5 over Rational Field
        sage: A = AffineSpace(2, QQ, names='XY'); A
        Affine Space of dimension 2 over Rational Field
        sage: A.coordinate_ring()
        Multivariate Polynomial Ring in X, Y over Rational Field

    Use the divide operator for base extension::

        sage: AffineSpace(5, names='x')/GF(17)
        Affine Space of dimension 5 over Finite Field of size 17

    The default base ring is `\ZZ`::

        sage: AffineSpace(5, names='x')
        Affine Space of dimension 5 over Integer Ring

    There is also an affine space associated to each polynomial ring::

        sage: R = GF(7)['x, y, z']
        sage: A = AffineSpace(R); A
        Affine Space of dimension 3 over Finite Field of size 7
        sage: A.coordinate_ring() is R
        True

    TESTS::

            sage: R.<w> = QQ[]
            sage: A.<w> = AffineSpace(R)
            sage: A.gens() == R.gens()
            True

        ::

            sage: R.<x> = QQ[]
            sage: A.<z> = AffineSpace(R)
            Traceback (most recent call last):
            ...
            NameError: variable names passed to AffineSpace conflict with names in ring
    """
    if (is_MPolynomialRing(n) or is_PolynomialRing(n)) and R is None:
        R = n
        if names is not None:
            # Check for the case that the user provided a variable name
            # That does not match what we wanted to use from R
            names = normalize_names(R.ngens(), names)
            if n.variable_names() != names:
                # The provided name doesn't match the name of R's variables
                raise NameError("variable names passed to AffineSpace conflict with names in ring")
        A = AffineSpace(R.ngens(), R.base_ring(), R.variable_names())
        A._coordinate_ring = R
        return A
    if names is None:
        if n == 0:
            names = ''
        else:
            names = 'x'
    if isinstance(R, integer_types + (Integer,)):
        n, R = R, n
    if R is None:
        R = ZZ  # default is the integers
    names = normalize_names(n, names)
    if default_embedding_index is not None and ambient_projective_space is None:
        from sage.schemes.projective.projective_space import ProjectiveSpace
        ambient_projective_space = ProjectiveSpace(n, R)
    if R in _Fields:
        if is_FiniteField(R):
            return AffineSpace_finite_field(n, R, names,
                                            ambient_projective_space, default_embedding_index)
        else:
            return AffineSpace_field(n, R, names,
                                     ambient_projective_space, default_embedding_index)
    return AffineSpace_generic(n, R, names, ambient_projective_space, default_embedding_index)
Beispiel #33
0
    def projective_embedding(self, i=None, PP=None):
        """
        Returns a morphism from this space into an ambient projective space
        of the same dimension.

        INPUT:


        -  ``i`` -- integer (default: dimension of self = last
           coordinate) determines which projective embedding to compute. The
           embedding is that which has a 1 in the i-th coordinate, numbered
           from 0.

        -  ``PP`` -- (default: None) ambient projective space, i.e.,
           codomain of morphism; this is constructed if it is not
           given.

        EXAMPLES::

            sage: AA = AffineSpace(2, QQ, 'x')
            sage: pi = AA.projective_embedding(0); pi
            Scheme morphism:
              From: Affine Space of dimension 2 over Rational Field
              To:   Projective Space of dimension 2 over Rational Field
              Defn: Defined on coordinates by sending (x0, x1) to
                    (1 : x0 : x1)
            sage: z = AA(3, 4)
            sage: pi(z)
            (1/4 : 3/4 : 1)
            sage: pi(AA(0,2))
            (1/2 : 0 : 1)
            sage: pi = AA.projective_embedding(1); pi
            Scheme morphism:
              From: Affine Space of dimension 2 over Rational Field
              To:   Projective Space of dimension 2 over Rational Field
              Defn: Defined on coordinates by sending (x0, x1) to
                    (x0 : 1 : x1)
            sage: pi(z)
            (3/4 : 1/4 : 1)
            sage: pi = AA.projective_embedding(2)
            sage: pi(z)
            (3 : 4 : 1)

        ::

            sage: A.<x,y> = AffineSpace(ZZ, 2)
            sage: A.projective_embedding(2).codomain().affine_patch(2) == A
            True
        """
        n = self.dimension_relative()
        if i is None:
            try:
                i = self._default_embedding_index
            except AttributeError:
                i = int(n)
        else:
            i = int(i)

        try:
            phi = self.__projective_embedding[i]
            #assume that if you've passed in a new codomain you want to override
            #the existing embedding
            if PP is None or phi.codomain() == PP:
                return(phi)
        except AttributeError:
            self.__projective_embedding = {}
        except KeyError:
            pass

        #if no i-th embedding exists, we may still be here with PP==None
        if PP is None:
            from sage.schemes.projective.projective_space import ProjectiveSpace
            PP = ProjectiveSpace(n, self.base_ring())
        elif PP.dimension_relative() != n:
            raise ValueError("projective Space must be of dimension %s"%(n))

        R = self.coordinate_ring()
        v = list(R.gens())
        if n < 0 or n >self.dimension_relative():
            raise ValueError("argument i (=%s) must be between 0 and %s, inclusive"%(i,n))
        v.insert(i, R(1))
        phi = self.hom(v, PP)
        self.__projective_embedding[i] = phi
        #make affine patch and projective embedding match
        PP.affine_patch(i,self)
        return phi
Beispiel #34
0
def SymplecticPolarGraph(d, q, algorithm=None):
    r"""
    Returns the Symplectic Polar Graph `Sp(d,q)`.

    The Symplectic Polar Graph `Sp(d,q)` is built from a projective space of dimension
    `d-1` over a field `F_q`, and a symplectic form `f`. Two vertices `u,v` are
    made adjacent if `f(u,v)=0`.

    See the page `on symplectic graphs on Andries Brouwer's website
    <http://www.win.tue.nl/~aeb/graphs/Sp.html>`_.

    INPUT:

    - ``d,q`` (integers) -- note that only even values of `d` are accepted by
      the function.

    - ``algorithm`` -- if set to 'gap' then the computation is carried via GAP
      library interface, computing totally singular subspaces, which is faster for `q>3`.
      Otherwise it is done directly.

    EXAMPLES:

    Computation of the spectrum of `Sp(6,2)`::

        sage: g = graphs.SymplecticGraph(6,2)
        doctest:...: DeprecationWarning: SymplecticGraph is deprecated. Please use sage.graphs.generators.classical_geometries.SymplecticPolarGraph instead.
        See http://trac.sagemath.org/19136 for details.
        sage: g.is_strongly_regular(parameters=True)
        (63, 30, 13, 15)
        sage: set(g.spectrum()) == {-5, 3, 30}
        True

    The parameters of `Sp(4,q)` are the same as of `O(5,q)`, but they are
    not isomorphic if `q` is odd::

        sage: G = graphs.SymplecticPolarGraph(4,3)
        sage: G.is_strongly_regular(parameters=True)
        (40, 12, 2, 4)
        sage: O=graphs.OrthogonalPolarGraph(5,3)
        sage: O.is_strongly_regular(parameters=True)
        (40, 12, 2, 4)
        sage: O.is_isomorphic(G)
        False
        sage: graphs.SymplecticPolarGraph(6,4,algorithm="gap").is_strongly_regular(parameters=True) # not tested (long time)
        (1365, 340, 83, 85)

    TESTS::

        sage: graphs.SymplecticPolarGraph(4,4,algorithm="gap").is_strongly_regular(parameters=True)
        (85, 20, 3, 5)
        sage: graphs.SymplecticPolarGraph(4,4).is_strongly_regular(parameters=True)
        (85, 20, 3, 5)
        sage: graphs.SymplecticPolarGraph(4,4,algorithm="blah")
        Traceback (most recent call last):
        ...
        ValueError: unknown algorithm!
    """
    if d < 1 or d % 2 != 0:
        raise ValueError("d must be even and greater than 2")

    if algorithm == "gap":  # faster for larger (q>3)  fields
        from sage.libs.gap.libgap import libgap
        G = _polar_graph(d, q, libgap.SymplecticGroup(d, q))

    elif algorithm == None:  # faster for small (q<4) fields
        from sage.rings.finite_rings.constructor import FiniteField
        from sage.modules.free_module import VectorSpace
        from sage.schemes.projective.projective_space import ProjectiveSpace
        from sage.matrix.constructor import identity_matrix, block_matrix, zero_matrix

        F = FiniteField(q, "x")
        M = block_matrix(F, 2, 2, [
            zero_matrix(F, d / 2),
            identity_matrix(F, d / 2), -identity_matrix(F, d / 2),
            zero_matrix(F, d / 2)
        ])

        V = VectorSpace(F, d)
        PV = list(ProjectiveSpace(d - 1, F))
        G = Graph([[tuple(_) for _ in PV], lambda x, y: V(x) *
                   (M * V(y)) == 0],
                  loops=False)

    else:
        raise ValueError("unknown algorithm!")

    G.name("Symplectic Polar Graph Sp(" + str(d) + "," + str(q) + ")")
    G.relabel()
    return G
Beispiel #35
0
def EllipticCurve_from_cubic(F, P, morphism=True):
    r"""
    Construct an elliptic curve from a ternary cubic with a rational point.

    If you just want the Weierstrass form and are not interested in
    the morphism then it is easier to use
    :func:`~sage.schemes.elliptic_curves.jacobian.Jacobian`
    instead. This will construct the same elliptic curve but you don't
    have to supply the point ``P``.

    INPUT:

    - ``F`` -- a homogeneous cubic in three variables with rational
      coefficients, as a polynomial ring element, defining a smooth
      plane cubic curve.

    - ``P`` -- a 3-tuple `(x,y,z)` defining a projective point on the
      curve `F=0`. Need not be a flex, but see caveat on output.

    - ``morphism`` -- boolean (default: ``True``). Whether to return
      the morphism or just the elliptic curve.

    OUTPUT:

    An elliptic curve in long Weierstrass form isomorphic to the curve
    `F=0`.

    If ``morphism=True`` is passed, then a birational equivalence
    between F and the Weierstrass curve is returned. If the point
    happens to be a flex, then this is an isomorphism.

    EXAMPLES:

    First we find that the Fermat cubic is isomorphic to the curve
    with Cremona label 27a1::

        sage: R.<x,y,z> = QQ[]
        sage: cubic = x^3+y^3+z^3
        sage: P = [1,-1,0]
        sage: E = EllipticCurve_from_cubic(cubic, P, morphism=False); E
        Elliptic Curve defined by y^2 + 2*x*y + 1/3*y = x^3 - x^2 - 1/3*x - 1/27 over Rational Field
        sage: E.cremona_label()
        '27a1'
        sage: EllipticCurve_from_cubic(cubic, [0,1,-1], morphism=False).cremona_label()
        '27a1'
        sage: EllipticCurve_from_cubic(cubic, [1,0,-1], morphism=False).cremona_label()
        '27a1'

    Next we find the minimal model and conductor of the Jacobian of the
    Selmer curve::

        sage: R.<a,b,c> = QQ[]
        sage: cubic = a^3+b^3+60*c^3
        sage: P = [1,-1,0]
        sage: E = EllipticCurve_from_cubic(cubic, P, morphism=False);  E
        Elliptic Curve defined by y^2 + 2*x*y + 20*y = x^3 - x^2 - 20*x - 400/3 over Rational Field
        sage: E.minimal_model()
        Elliptic Curve defined by y^2 = x^3 - 24300 over Rational Field
        sage: E.conductor()
        24300

    We can also get the birational equivalence to and from the
    Weierstrass form. We start with an example where ``P`` is a flex
    and the equivalence is an isomorphism::

        sage: f = EllipticCurve_from_cubic(cubic, P, morphism=True)
        sage: f
        Scheme morphism:
          From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
                a^3 + b^3 + 60*c^3
          To:   Elliptic Curve defined by y^2 + 2*x*y + 20*y = x^3 - x^2 - 20*x - 400/3
                over Rational Field
          Defn: Defined on coordinates by sending (a : b : c) to
                (-c : -b + c : 1/20*a + 1/20*b)

        sage: finv = f.inverse();  finv
        Scheme morphism:
          From: Elliptic Curve defined by y^2 + 2*x*y + 20*y = x^3 - x^2 - 20*x - 400/3
                over Rational Field
          To:   Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
          a^3 + b^3 + 60*c^3
          Defn: Defined on coordinates by sending (x : y : z) to
                (x + y + 20*z : -x - y : -x)

    We verify that `f` maps the chosen point `P=(1,-1,0)` on the cubic
    to the origin of the elliptic curve::

        sage: f([1,-1,0])
        (0 : 1 : 0)
        sage: finv([0,1,0])
        (-1 : 1 : 0)

    To verify the output, we plug in the polynomials to check that
    this indeed transforms the cubic into Weierstrass form::

        sage: cubic(finv.defining_polynomials()) * finv.post_rescaling()
        -x^3 + x^2*z + 2*x*y*z + y^2*z + 20*x*z^2 + 20*y*z^2 + 400/3*z^3

        sage: E.defining_polynomial()(f.defining_polynomials()) * f.post_rescaling()
        a^3 + b^3 + 60*c^3

    If the point is not a flex then the cubic can not be transformed
    to a Weierstrass equation by a linear transformation. The general
    birational transformation is quadratic::

        sage: cubic =  a^3+7*b^3+64*c^3
        sage: P = [2,2,-1]
        sage: f = EllipticCurve_from_cubic(cubic, P, morphism=True)
        sage: E = f.codomain();  E
        Elliptic Curve defined by y^2 - 722*x*y - 21870000*y = x^3
        + 23579*x^2 over Rational Field
        sage: E.minimal_model()
        Elliptic Curve defined by y^2 + y = x^3 - 331 over Rational Field

        sage: f
        Scheme morphism:
          From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
                a^3 + 7*b^3 + 64*c^3
          To:   Elliptic Curve defined by y^2 - 722*x*y - 21870000*y =
                x^3 + 23579*x^2 over Rational Field
          Defn: Defined on coordinates by sending (a : b : c) to
                (-5/112896*a^2 - 17/40320*a*b - 1/1280*b^2 - 29/35280*a*c
                 - 13/5040*b*c - 4/2205*c^2 :
                 -4055/112896*a^2 - 4787/40320*a*b - 91/1280*b^2 - 7769/35280*a*c
                 - 1993/5040*b*c - 724/2205*c^2 :
                 1/4572288000*a^2 + 1/326592000*a*b + 1/93312000*b^2 + 1/142884000*a*c
                 + 1/20412000*b*c + 1/17860500*c^2)

        sage: finv = f.inverse();  finv
        Scheme morphism:
          From: Elliptic Curve defined by y^2 - 722*x*y - 21870000*y =
                x^3 + 23579*x^2 over Rational Field
          To:   Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
                a^3 + 7*b^3 + 64*c^3
          Defn: Defined on coordinates by sending (x : y : z) to
                (2*x^2 + 227700*x*z - 900*y*z :
                 2*x^2 - 32940*x*z + 540*y*z :
                 -x^2 - 56520*x*z - 180*y*z)

        sage: cubic(finv.defining_polynomials()) * finv.post_rescaling()
        -x^3 - 23579*x^2*z - 722*x*y*z + y^2*z - 21870000*y*z^2

        sage: E.defining_polynomial()(f.defining_polynomials()) * f.post_rescaling()
        a^3 + 7*b^3 + 64*c^3

    TESTS::

        sage: R.<x,y,z> = QQ[]
        sage: cubic = x^2*y + 4*x*y^2 + x^2*z + 8*x*y*z + 4*y^2*z + 9*x*z^2 + 9*y*z^2
        sage: EllipticCurve_from_cubic(cubic, [1,-1,1], morphism=False)
        Elliptic Curve defined by y^2 - 882*x*y - 2560000*y = x^3 - 127281*x^2 over Rational Field
    """
    import sage.matrix.all as matrix

    # check the input
    R = F.parent()
    if not is_MPolynomialRing(R):
        raise TypeError('equation must be a polynomial')
    if R.ngens() != 3:
        raise TypeError('equation must be a polynomial in three variables')
    if not F.is_homogeneous():
        raise TypeError('equation must be a homogeneous polynomial')
    K = F.parent().base_ring()
    try:
        P = [K(c) for c in P]
    except TypeError:
        raise TypeError('cannot convert %s into %s'%(P,K))
    if F(P) != 0:
        raise ValueError('%s is not a point on %s'%(P,F))
    if len(P) != 3:
        raise TypeError('%s is not a projective point'%P)
    x, y, z = R.gens()

    # First case: if P = P2 then P is a flex
    P2 = chord_and_tangent(F, P)
    if are_projectively_equivalent(P, P2, base_ring=K):
        # find the tangent to F in P
        dx = K(F.derivative(x)(P))
        dy = K(F.derivative(y)(P))
        dz = K(F.derivative(z)(P))
        # find a second point Q on the tangent line but not on the cubic
        for tangent in [[dy, -dx, K.zero()], [dz, K.zero(), -dx], [K.zero(), -dz, dx]]:
            tangent = projective_point(tangent)
            Q = [tangent[0]+P[0], tangent[1]+P[1], tangent[2]+P[2]]
            F_Q = F(Q)
            if F_Q != 0:  # At most one further point may accidentally be on the cubic
                break
        assert F_Q != 0
        # pick linearly independent third point
        for third_point in [(1,0,0), (0,1,0), (0,0,1)]:
            M = matrix.matrix(K, [Q, P, third_point]).transpose()
            if M.is_invertible():
                break
        F2 = R(M.act_on_polynomial(F))
        # scale and dehomogenise
        a = K(F2.coefficient(x**3))
        F3 = F2/a
        b = K(F3.coefficient(y*y*z))
        S = rings.PolynomialRing(K, 'x,y,z')
        # elliptic curve coordinates
        X, Y, Z = S.gen(0), S.gen(1), S(-1/b)*S.gen(2)
        F4 = F3(X, Y, Z)
        E = EllipticCurve(F4.subs(z=1))
        if not morphism:
            return E
        inv_defining_poly = [ M[i,0]*X + M[i,1]*Y + M[i,2]*Z for i in range(3) ]
        inv_post = -1/a
        M = M.inverse()
        trans_x, trans_y, trans_z = [ M[i,0]*x + M[i,1]*y + M[i,2]*z for i in range(3) ]
        fwd_defining_poly = [trans_x, trans_y, -b*trans_z]
        fwd_post = -a

    # Second case: P is not a flex, then P, P2, P3 are different
    else:
        P3 = chord_and_tangent(F, P2)
        # send P, P2, P3 to (1:0:0), (0:1:0), (0:0:1) respectively
        M = matrix.matrix(K, [P, P2, P3]).transpose()
        F2 = M.act_on_polynomial(F)
        # substitute x = U^2, y = V*W, z = U*W, and rename (x,y,z)=(U,V,W)
        F3 = F2.substitute({x:x**2, y:y*z, z:x*z}) // (x**2*z)
        # scale and dehomogenise
        a = K(F3.coefficient(x**3))
        F4 = F3/a
        b = K(F4.coefficient(y*y*z))
        # change to a polynomial in only two variables
        S = rings.PolynomialRing(K, 'x,y,z')
        # elliptic curve coordinates
        X, Y, Z = S.gen(0), S.gen(1), S(-1/b)*S.gen(2)
        F5 = F4(X, Y, Z)
        E = EllipticCurve(F5.subs(z=1))
        if not morphism:
            return E
        inv_defining_poly = [ M[i,0]*X*X + M[i,1]*Y*Z + M[i,2]*X*Z for i in range(3) ]
        inv_post = -1/a/(X**2)/Z
        M = M.inverse()
        trans_x, trans_y, trans_z = [
            (M[i,0]*x + M[i,1]*y + M[i,2]*z) for i in range(3) ]
        fwd_defining_poly = [ trans_x*trans_z, trans_x*trans_y, -b*trans_z*trans_z ]
        fwd_post = -a/(trans_x*trans_z*trans_z)

    # Construct the morphism
    from sage.schemes.projective.projective_space import ProjectiveSpace
    P2 = ProjectiveSpace(2, K, names=map(str, R.gens()))
    cubic = P2.subscheme(F)
    from sage.schemes.elliptic_curves.weierstrass_transform import \
        WeierstrassTransformationWithInverse
    return WeierstrassTransformationWithInverse(
        cubic, E, fwd_defining_poly, fwd_post, inv_defining_poly, inv_post)
Beispiel #36
0
def UnitaryPolarGraph(m, q, algorithm="gap"):
    r"""
    Returns the Unitary Polar Graph `U(m,q)`.

    For more information on Unitary Polar graphs, see the `page of
    Andries Brouwer's website <http://www.win.tue.nl/~aeb/graphs/srghub.html>`_.

    INPUT:

    - ``m,q`` (integers) -- `q` must be a prime power.

    - ``algorithm`` -- if set to 'gap' then the computation is carried via GAP
      library interface, computing totally singular subspaces, which is faster for
      large examples (especially with `q>2`). Otherwise it is done directly.

    EXAMPLES::

        sage: G = graphs.UnitaryPolarGraph(4,2); G
        Unitary Polar Graph U(4, 2); GQ(4, 2): Graph on 45 vertices
        sage: G.is_strongly_regular(parameters=True)
        (45, 12, 3, 3)
        sage: graphs.UnitaryPolarGraph(5,2).is_strongly_regular(parameters=True)
        (165, 36, 3, 9)
        sage: graphs.UnitaryPolarGraph(6,2)    # not tested (long time)
        Unitary Polar Graph U(6, 2): Graph on 693 vertices

    TESTS::

        sage: graphs.UnitaryPolarGraph(4,3, algorithm="gap").is_strongly_regular(parameters=True)
        (280, 36, 8, 4)
        sage: graphs.UnitaryPolarGraph(4,3).is_strongly_regular(parameters=True)
        (280, 36, 8, 4)
        sage: graphs.UnitaryPolarGraph(4,3, algorithm="foo")
        Traceback (most recent call last):
        ...
        ValueError: unknown algorithm!
    """
    if algorithm == "gap":
        from sage.libs.gap.libgap import libgap
        G = _polar_graph(m, q**2, libgap.GeneralUnitaryGroup(m, q))

    elif algorithm == None:  # slow on large examples
        from sage.schemes.projective.projective_space import ProjectiveSpace
        from sage.rings.finite_rings.constructor import FiniteField
        from sage.modules.free_module_element import free_module_element as vector
        from __builtin__ import sum as psum
        Fq = FiniteField(q**2, 'a')
        PG = map(vector, ProjectiveSpace(m - 1, Fq))
        map(lambda x: x.set_immutable(), PG)

        def P(x, y):
            return psum(map(lambda j: x[j] * y[m - 1 - j]**q, xrange(m))) == 0

        V = filter(lambda x: P(x, x), PG)
        G = Graph(
            [
                V,
                lambda x, y:  # bottleneck is here, of course:
                P(x, y)
            ],
            loops=False)
    else:
        raise ValueError("unknown algorithm!")

    G.relabel()
    G.name("Unitary Polar Graph U" + str((m, q)))
    if m == 4:
        G.name(G.name() + '; GQ' + str((q**2, q)))
    if m == 5:
        G.name(G.name() + '; GQ' + str((q**2, q**3)))
    return G
Beispiel #37
0
    def segre_embedding(self, PP=None, var='u'):
        r"""
        Return the Segre embedding of ``self`` into the appropriate
        projective space.

        INPUT:

        -  ``PP`` -- (default: ``None``) ambient image projective space;
            this is constructed if it is not given.

        - ``var`` -- string, variable name of the image projective space, default `u` (optional)

        OUTPUT:

        Hom -- from ``self`` to the appropriate subscheme of projective space

        .. TODO::

            Cartesian products with more than two components

        EXAMPLES::

            sage: X.<y0,y1,y2,y3,y4,y5> = ProductProjectiveSpaces(ZZ,[2,2])
            sage: phi = X.segre_embedding(); phi
            Scheme morphism:
              From: Product of projective spaces P^2 x P^2 over Integer Ring
              To:   Closed subscheme of Projective Space of dimension 8 over Integer Ring defined by:
              -u5*u7 + u4*u8,
              -u5*u6 + u3*u8,
              -u4*u6 + u3*u7,
              -u2*u7 + u1*u8,
              -u2*u4 + u1*u5,
              -u2*u6 + u0*u8,
              -u1*u6 + u0*u7,
              -u2*u3 + u0*u5,
              -u1*u3 + u0*u4
              Defn: Defined by sending (y0 : y1 : y2 , y3 : y4 : y5) to
                    (y0*y3 : y0*y4 : y0*y5 : y1*y3 : y1*y4 : y1*y5 : y2*y3 : y2*y4 : y2*y5).

            ::

            sage: T = ProductProjectiveSpaces([1,2],CC,'z')
            sage: T.segre_embedding()
            Scheme morphism:
              From: Product of projective spaces P^1 x P^2 over Complex Field with 53 bits of precision
              To:   Closed subscheme of Projective Space of dimension 5 over Complex Field with 53 bits of precision defined by:
              -u2*u4 + u1*u5,
              -u2*u3 + u0*u5,
              -u1*u3 + u0*u4
              Defn: Defined by sending (z0 : z1 , z2 : z3 : z4) to
                    (z0*z2 : z0*z3 : z0*z4 : z1*z2 : z1*z3 : z1*z4).
        """
        N = self._dims
        if len(N) > 2:
            raise NotImplementedError("Cannot have more than two components.")
        M = (N[0]+1)*(N[1]+1)-1

        vars = list(self.coordinate_ring().variable_names()) + [var + str(i) for i in range(M+1)]
        R = PolynomialRing(self.base_ring(),self.ngens()+M+1, vars, order='lex')

        #set-up the elimination for the segre embedding
        mapping = []
        k = self.ngens()
        for i in range(N[0]+1):
            for j in range(N[0]+1,N[0]+N[1]+2):
                mapping.append(R.gen(k)-R(self.gen(i)*self.gen(j)))
                k+=1

        #change the defining ideal of the subscheme into the variables
        I = R.ideal(list(self.defining_polynomials()) + mapping)
        J = I.groebner_basis()
        s = set(R.gens()[:self.ngens()])
        n = len(J)-1
        L = []
        while s.isdisjoint(J[n].variables()):
            L.append(J[n])
            n = n-1

        #create new subscheme
        if PP is None:
            PS = ProjectiveSpace(self.base_ring(),M,R.gens()[self.ngens():])
            Y = PS.subscheme(L)
        else:
            if PP.dimension_relative()!= M:
                raise ValueError("Projective Space %s must be dimension %s")%(PP, M)
            S = PP.coordinate_ring()
            psi = R.hom([0]*(N[0]+N[1]+2) + list(S.gens()),S)
            L = [psi(l) for l in L]
            Y = PP.subscheme(L)

        #create embedding for points
        mapping = []
        for i in range(N[0]+1):
            for j in range(N[0]+1,N[0]+N[1]+2):
                mapping.append(self.gen(i)*self.gen(j))
        phi = self.hom(mapping,Y)

        return phi
Beispiel #38
0
def julia_plot(f=None, **kwds):
    r"""
    Plots the Julia set of a given polynomial ``f``. Users can specify whether
    they would like to display the Mandelbrot side by side with the Julia set
    with the ``mandelbrot`` argument. If ``f`` is not specified, this method
    defaults to `f(z) = z^2-1`.

    The Julia set of a polynomial ``f`` is the set of complex numbers `z` for
    which the function `f(z)` is bounded under iteration. The Julia set can
    be visualized by plotting each point in the set in the complex plane.
    Julia sets are examples of fractals when plotted in the complex plane.

    ALGORITHM:

    Let `R_c = \bigl(1 + \sqrt{1 + 4|c|}\bigr)/2` if the polynomial is of the
    form `f(z) = z^2 + c`; otherwise, let `R_c = 2`.
    For every `p \in \mathbb{C}`, if `|f^{k}(p)| > R_c` for some `k \geq 0`,
    then `f^{n}(p) \to \infty`.  Let `N` be the maximum number of iterations.
    Compute the first `N` points on the orbit of `p` under `f`. If for
    any `k < N`, `|f^{k}(p)| > R_c`, we stop the iteration and assign a color
    to the point `p` based on how quickly `p` escaped to infinity under
    iteration of `f`. If `|f^{i}(p)| \leq R_c` for all `i \leq N`, we assume
    `p` is in the Julia set and assign the point `p` the color black.

    INPUT:

    - ``f`` -- input polynomial (optional - default: ``z^2 - 1``).

    - ``period`` -- list (optional - default: ``None``), returns the Julia set
      for a random `c` value with the given (formal) cycle structure.

    - ``mandelbrot`` -- boolean (optional - default: ``True``), when set to
      ``True``, an image of the Mandelbrot set is appended to the right of the
      Julia set.

    - ``point_color`` -- RGB color (optional - default: ``'tomato'``),
      color of the point `c` in the Mandelbrot set (any valid input for Color).

    - ``x_center`` -- double (optional - default: ``-1.0``), Real part
      of center point.

    - ``y_center`` -- double (optional - default: ``0.0``), Imaginary part
      of center point.

    - ``image_width`` -- double (optional - default: ``4.0``), width of image
      in the complex plane.

    - ``max_iteration`` -- long (optional - default: ``500``), maximum number
      of iterations the map `f(z)`.

    - ``pixel_count`` -- long (optional - default: ``500``), side length of
      image in number of pixels.

    - ``base_color`` -- hex color (optional - default: ``'steelblue'``), color
      used to determine the coloring of set (any valid input for Color).

    - ``level_sep`` -- long (optional - default: 1), number of iterations
      between each color level.

    - ``number_of_colors`` -- long (optional - default: 30), number of colors
      used to plot image.

    - ``interact`` -- boolean (optional - default: ``False``), controls whether
      plot will have interactive functionality.

    OUTPUT:

    24-bit RGB image of the Julia set in the complex plane.

    .. TODO::

        Implement the side-by-side Mandelbrot-Julia plots for general one-parameter families
        of polynomials.

    EXAMPLES:

    The default ``f`` is `z^2 - 1`::

        sage: julia_plot()
        1001x500px 24-bit RGB image

    To display only the Julia set, set ``mandelbrot`` to ``False``::

        sage: julia_plot(mandelbrot=False)
        500x500px 24-bit RGB image

    ::

        sage: R.<z> = CC[]
        sage: f = z^3 - z + 1
        sage: julia_plot(f)
        500x500px 24-bit RGB image

    To display an interactive plot of the Julia set in the Notebook,
    set ``interact`` to ``True``. (This is only implemented for polynomials of
    the form ``f = z^2 + c``)::

        sage: julia_plot(interact=True)
        interactive(children=(FloatSlider(value=-1.0, description=u'Real c'...

        ::

        sage: R.<z> = CC[]
        sage: f = z^2 + 1/2
        sage: julia_plot(f,interact=True)
        interactive(children=(FloatSlider(value=0.5, description=u'Real c'...

    To return the Julia set of a random `c` value with (formal) cycle structure
    `(2,3)`, set ``period = [2,3]``::

        sage: julia_plot(period=[2,3])
        1001x500px 24-bit RGB image

    To return all of the Julia sets of `c` values with (formal) cycle structure
    `(2,3)`::

        sage: period = [2,3] # not tested
        ....: R.<c> = QQ[]
        ....: P.<x,y> = ProjectiveSpace(R,1)
        ....: f = DynamicalSystem([x^2+c*y^2, y^2])
        ....: L = f.dynatomic_polynomial(period).subs({x:0,y:1}).roots(ring=CC)
        ....: c_values = [k[0] for k in L]
        ....: for c in c_values:
        ....:     julia_plot(c)

    Polynomial maps can be defined over a polynomial ring or a fraction field,
    so long as ``f`` is polynomial::

        sage: R.<z> = CC[]
        sage: f = z^2 - 1
        sage: julia_plot(f)
        1001x500px 24-bit RGB image

    ::

        sage: R.<z> = CC[]
        sage: K = R.fraction_field(); z = K.gen()
        sage: f = z^2-1
        sage: julia_plot(f)
        1001x500px 24-bit RGB image

    Interact functionality is not implemented if the polynomial is not of the
    form `f = z^2 + c`::

        sage: R.<z> = CC[]
        sage: f = z^3 + 1
        sage: julia_plot(f, interact=True)
        Traceback (most recent call last):
        ...
        NotImplementedError: The interactive plot is only implemented for ...
    """

    # extract keyword arguments
    period = kwds.pop("period", None)
    mandelbrot = kwds.pop("mandelbrot", True)
    point_color = kwds.pop("point_color", 'tomato')
    x_center = kwds.pop("x_center", 0.0)
    y_center = kwds.pop("y_center", 0.0)
    image_width = kwds.pop("image_width", 4.0)
    max_iteration = kwds.pop("max_iteration", 500)
    pixel_count = kwds.pop("pixel_count", 500)
    base_color = kwds.pop("base_color", 'steelblue')
    level_sep = kwds.pop("level_sep", 1)
    number_of_colors = kwds.pop("number_of_colors", 30)
    interacts = kwds.pop("interact", False)

    f_is_default_after_all = None

    if period:  # pick a random c with the specified period
        R = PolynomialRing(CC, 'c')
        c = R.gen()
        x, y = ProjectiveSpace(R, 1, 'x,y').gens()
        F = DynamicalSystem([x**2 + c * y**2, y**2])
        L = F.dynatomic_polynomial(period).subs({x: 0, y: 1}).roots(ring=CC)
        c = L[randint(0, len(L) - 1)][0]

    base_color = Color(base_color)
    point_color = Color(point_color)

    EPS = 0.00001

    if f is not None and period is None:  # f user-specified and no period given

        # try to coerce f to live in a polynomial ring
        S = PolynomialRing(CC, names='z')
        z = S.gen()
        try:
            f_poly = S(f)
        except TypeError:
            R = f.parent()
            if not (R.is_integral_domain() and
                    (CC.is_subring(R) or CDF.is_subring(R))):
                raise ValueError('Given `f` must be a complex polynomial.')
            else:
                raise NotImplementedError(
                    'Julia sets not implemented for rational functions.')

        if (f_poly - z * z) in CC:  # f is specified and of the form z^2 + c.
            f_is_default_after_all = True
            c = f_poly - z * z
        else:  # f is specified and not of the form z^2 + c
            if interacts:
                raise NotImplementedError(
                    "The interactive plot is only implemented for "
                    "polynomials of the form f = z^2 + c.")
            else:
                return general_julia(f_poly, x_center, y_center, image_width,
                                     max_iteration, pixel_count, level_sep,
                                     number_of_colors, base_color)

    # otherwise we can use fast_julia_plot for z^2 + c
    if f_is_default_after_all or f is None or period is not None:

        # specify default c = -1 value if f and period were not specified
        if not f_is_default_after_all and period is None:
            c = -1

        c = CC(c)
        c_real = c.real()
        c_imag = c.imag()

        if interacts:  # set widgets
            from ipywidgets.widgets import FloatSlider, IntSlider, \
                                           ColorPicker, interact
            widgets = dict(
                c_real=FloatSlider(min=-2.0,
                                   max=2.0,
                                   step=EPS,
                                   value=c_real,
                                   description="Real c"),
                c_imag=FloatSlider(min=-2.0,
                                   max=2.0,
                                   step=EPS,
                                   value=c_imag,
                                   description="Imag c"),
                x_center=FloatSlider(min=-1.0,
                                     max=1.0,
                                     step=EPS,
                                     value=x_center,
                                     description="Real center"),
                y_center=FloatSlider(min=-1.0,
                                     max=1.0,
                                     step=EPS,
                                     value=y_center,
                                     description="Imag center"),
                image_width=FloatSlider(min=EPS,
                                        max=4.0,
                                        step=EPS,
                                        value=image_width,
                                        description="Width"),
                max_iteration=IntSlider(min=0,
                                        max=1000,
                                        value=max_iteration,
                                        description="Iterations"),
                pixel_count=IntSlider(min=10,
                                      max=1000,
                                      value=pixel_count,
                                      description="Pixels"),
                level_sep=IntSlider(min=1,
                                    max=20,
                                    value=level_sep,
                                    description="Color sep"),
                color_num=IntSlider(min=1,
                                    max=100,
                                    value=number_of_colors,
                                    description="# Colors"),
                base_color=ColorPicker(value=base_color.html_color(),
                                       description="Base color"),
            )
            if mandelbrot:
                widgets["point_color"] = ColorPicker(
                    value=point_color.html_color(), description="Point color")
                return interact(**widgets).widget(julia_helper)
            else:
                return interact(**widgets).widget(fast_julia_plot)
        elif mandelbrot:  # non-interactive with mandelbrot
            return julia_helper(c_real, c_imag, x_center, y_center,
                                image_width, max_iteration, pixel_count,
                                level_sep, number_of_colors, base_color,
                                point_color)
        else:  # non-interactive without mandelbrot
            return fast_julia_plot(c_real, c_imag, x_center, y_center,
                                   image_width, max_iteration, pixel_count,
                                   level_sep, number_of_colors, base_color)
Beispiel #39
0
def EllipticCurve_from_cubic(F, P, morphism=True):
    r"""
    Construct an elliptic curve from a ternary cubic with a rational point.

    If you just want the Weierstrass form and are not interested in
    the morphism then it is easier to use
    :func:`~sage.schemes.elliptic_curves.jacobian.Jacobian`
    instead. This will construct the same elliptic curve but you don't
    have to supply the point ``P``.

    INPUT:

    - ``F`` -- a homogeneous cubic in three variables with rational
      coefficients, as a polynomial ring element, defining a smooth
      plane cubic curve.

    - ``P`` -- a 3-tuple `(x,y,z)` defining a projective point on the
      curve `F=0`. Need not be a flex, but see caveat on output.

    - ``morphism`` -- boolean (default: ``True``). Whether to return
      the morphism or just the elliptic curve.

    OUTPUT:

    An elliptic curve in long Weierstrass form isomorphic to the curve
    `F=0`.

    If ``morphism=True`` is passed, then a birational equivalence
    between F and the Weierstrass curve is returned. If the point
    happens to be a flex, then this is an isomorphism.

    EXAMPLES:

    First we find that the Fermat cubic is isomorphic to the curve
    with Cremona label 27a1::

        sage: R.<x,y,z> = QQ[]
        sage: cubic = x^3+y^3+z^3
        sage: P = [1,-1,0]
        sage: E = EllipticCurve_from_cubic(cubic, P, morphism=False); E
        Elliptic Curve defined by y^2 + 2*x*y + 1/3*y = x^3 - x^2 - 1/3*x - 1/27 over Rational Field
        sage: E.cremona_label()
        '27a1'
        sage: EllipticCurve_from_cubic(cubic, [0,1,-1], morphism=False).cremona_label()
        '27a1'
        sage: EllipticCurve_from_cubic(cubic, [1,0,-1], morphism=False).cremona_label()
        '27a1'

    Next we find the minimal model and conductor of the Jacobian of the
    Selmer curve::

        sage: R.<a,b,c> = QQ[]
        sage: cubic = a^3+b^3+60*c^3
        sage: P = [1,-1,0]
        sage: E = EllipticCurve_from_cubic(cubic, P, morphism=False);  E
        Elliptic Curve defined by y^2 + 2*x*y + 20*y = x^3 - x^2 - 20*x - 400/3 over Rational Field
        sage: E.minimal_model()
        Elliptic Curve defined by y^2 = x^3 - 24300 over Rational Field
        sage: E.conductor()
        24300

    We can also get the birational equivalence to and from the
    Weierstrass form. We start with an example where ``P`` is a flex
    and the equivalence is an isomorphism::

        sage: f = EllipticCurve_from_cubic(cubic, P, morphism=True)
        sage: f
        Scheme morphism:
          From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
                a^3 + b^3 + 60*c^3
          To:   Elliptic Curve defined by y^2 + 2*x*y + 20*y = x^3 - x^2 - 20*x - 400/3
                over Rational Field
          Defn: Defined on coordinates by sending (a : b : c) to
                (-c : -b + c : 1/20*a + 1/20*b)

        sage: finv = f.inverse();  finv
        Scheme morphism:
          From: Elliptic Curve defined by y^2 + 2*x*y + 20*y = x^3 - x^2 - 20*x - 400/3
                over Rational Field
          To:   Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
          a^3 + b^3 + 60*c^3
          Defn: Defined on coordinates by sending (x : y : z) to
                (x + y + 20*z : -x - y : -x)

    We verify that `f` maps the chosen point `P=(1,-1,0)` on the cubic
    to the origin of the elliptic curve::

        sage: f([1,-1,0])
        (0 : 1 : 0)
        sage: finv([0,1,0])
        (-1 : 1 : 0)

    To verify the output, we plug in the polynomials to check that
    this indeed transforms the cubic into Weierstrass form::

        sage: cubic(finv.defining_polynomials()) * finv.post_rescaling()
        -x^3 + x^2*z + 2*x*y*z + y^2*z + 20*x*z^2 + 20*y*z^2 + 400/3*z^3

        sage: E.defining_polynomial()(f.defining_polynomials()) * f.post_rescaling()
        a^3 + b^3 + 60*c^3

    If the point is not a flex then the cubic can not be transformed
    to a Weierstrass equation by a linear transformation. The general
    birational transformation is quadratic::

        sage: cubic =  a^3+7*b^3+64*c^3
        sage: P = [2,2,-1]
        sage: f = EllipticCurve_from_cubic(cubic, P, morphism=True)
        sage: E = f.codomain();  E
        Elliptic Curve defined by y^2 - 722*x*y - 21870000*y = x^3
        + 23579*x^2 over Rational Field
        sage: E.minimal_model()
        Elliptic Curve defined by y^2 + y = x^3 - 331 over Rational Field

        sage: f
        Scheme morphism:
          From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
                a^3 + 7*b^3 + 64*c^3
          To:   Elliptic Curve defined by y^2 - 722*x*y - 21870000*y =
                x^3 + 23579*x^2 over Rational Field
          Defn: Defined on coordinates by sending (a : b : c) to
                (-5/112896*a^2 - 17/40320*a*b - 1/1280*b^2 - 29/35280*a*c
                 - 13/5040*b*c - 4/2205*c^2 :
                 -4055/112896*a^2 - 4787/40320*a*b - 91/1280*b^2 - 7769/35280*a*c
                 - 1993/5040*b*c - 724/2205*c^2 :
                 1/4572288000*a^2 + 1/326592000*a*b + 1/93312000*b^2 + 1/142884000*a*c
                 + 1/20412000*b*c + 1/17860500*c^2)

        sage: finv = f.inverse();  finv
        Scheme morphism:
          From: Elliptic Curve defined by y^2 - 722*x*y - 21870000*y =
                x^3 + 23579*x^2 over Rational Field
          To:   Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
                a^3 + 7*b^3 + 64*c^3
          Defn: Defined on coordinates by sending (x : y : z) to
                (2*x^2 + 227700*x*z - 900*y*z :
                 2*x^2 - 32940*x*z + 540*y*z :
                 -x^2 - 56520*x*z - 180*y*z)

        sage: cubic(finv.defining_polynomials()) * finv.post_rescaling()
        -x^3 - 23579*x^2*z - 722*x*y*z + y^2*z - 21870000*y*z^2

        sage: E.defining_polynomial()(f.defining_polynomials()) * f.post_rescaling()
        a^3 + 7*b^3 + 64*c^3

    TESTS::

        sage: R.<x,y,z> = QQ[]
        sage: cubic = x^2*y + 4*x*y^2 + x^2*z + 8*x*y*z + 4*y^2*z + 9*x*z^2 + 9*y*z^2
        sage: EllipticCurve_from_cubic(cubic, [1,-1,1], morphism=False)
        Elliptic Curve defined by y^2 - 882*x*y - 2560000*y = x^3 - 127281*x^2 over Rational Field
    """
    import sage.matrix.all as matrix

    # check the input
    R = F.parent()
    if not is_MPolynomialRing(R):
        raise TypeError('equation must be a polynomial')
    if R.ngens() != 3:
        raise TypeError('equation must be a polynomial in three variables')
    if not F.is_homogeneous():
        raise TypeError('equation must be a homogeneous polynomial')
    K = F.parent().base_ring()
    try:
        P = [K(c) for c in P]
    except TypeError:
        raise TypeError('cannot convert %s into %s' % (P, K))
    if F(P) != 0:
        raise ValueError('%s is not a point on %s' % (P, F))
    if len(P) != 3:
        raise TypeError('%s is not a projective point' % P)
    x, y, z = R.gens()

    # First case: if P = P2 then P is a flex
    P2 = chord_and_tangent(F, P)
    if are_projectively_equivalent(P, P2, base_ring=K):
        # find the tangent to F in P
        dx = K(F.derivative(x)(P))
        dy = K(F.derivative(y)(P))
        dz = K(F.derivative(z)(P))
        # find a second point Q on the tangent line but not on the cubic
        for tangent in [[dy, -dx, K.zero()], [dz, K.zero(), -dx],
                        [K.zero(), -dz, dx]]:
            tangent = projective_point(tangent)
            Q = [tangent[0] + P[0], tangent[1] + P[1], tangent[2] + P[2]]
            F_Q = F(Q)
            if F_Q != 0:  # At most one further point may accidentally be on the cubic
                break
        assert F_Q != 0
        # pick linearly independent third point
        for third_point in [(1, 0, 0), (0, 1, 0), (0, 0, 1)]:
            M = matrix.matrix(K, [Q, P, third_point]).transpose()
            if M.is_invertible():
                break
        F2 = R(M.act_on_polynomial(F))
        # scale and dehomogenise
        a = K(F2.coefficient(x**3))
        F3 = F2 / a
        b = K(F3.coefficient(y * y * z))
        S = rings.PolynomialRing(K, 'x,y,z')
        # elliptic curve coordinates
        X, Y, Z = S.gen(0), S.gen(1), S(-1 / b) * S.gen(2)
        F4 = F3(X, Y, Z)
        E = EllipticCurve(F4.subs(z=1))
        if not morphism:
            return E
        inv_defining_poly = [
            M[i, 0] * X + M[i, 1] * Y + M[i, 2] * Z for i in range(3)
        ]
        inv_post = -1 / a
        M = M.inverse()
        trans_x, trans_y, trans_z = [
            M[i, 0] * x + M[i, 1] * y + M[i, 2] * z for i in range(3)
        ]
        fwd_defining_poly = [trans_x, trans_y, -b * trans_z]
        fwd_post = -a

    # Second case: P is not a flex, then P, P2, P3 are different
    else:
        P3 = chord_and_tangent(F, P2)
        # send P, P2, P3 to (1:0:0), (0:1:0), (0:0:1) respectively
        M = matrix.matrix(K, [P, P2, P3]).transpose()
        F2 = M.act_on_polynomial(F)
        # substitute x = U^2, y = V*W, z = U*W, and rename (x,y,z)=(U,V,W)
        F3 = F2.substitute({x: x**2, y: y * z, z: x * z}) // (x**2 * z)
        # scale and dehomogenise
        a = K(F3.coefficient(x**3))
        F4 = F3 / a
        b = K(F4.coefficient(y * y * z))
        # change to a polynomial in only two variables
        S = rings.PolynomialRing(K, 'x,y,z')
        # elliptic curve coordinates
        X, Y, Z = S.gen(0), S.gen(1), S(-1 / b) * S.gen(2)
        F5 = F4(X, Y, Z)
        E = EllipticCurve(F5.subs(z=1))
        if not morphism:
            return E
        inv_defining_poly = [
            M[i, 0] * X * X + M[i, 1] * Y * Z + M[i, 2] * X * Z
            for i in range(3)
        ]
        inv_post = -1 / a / (X**2) / Z
        M = M.inverse()
        trans_x, trans_y, trans_z = [(M[i, 0] * x + M[i, 1] * y + M[i, 2] * z)
                                     for i in range(3)]
        fwd_defining_poly = [
            trans_x * trans_z, trans_x * trans_y, -b * trans_z * trans_z
        ]
        fwd_post = -a / (trans_x * trans_z * trans_z)

    # Construct the morphism
    from sage.schemes.projective.projective_space import ProjectiveSpace
    P2 = ProjectiveSpace(2, K, names=[str(_) for _ in R.gens()])
    cubic = P2.subscheme(F)
    from sage.schemes.elliptic_curves.weierstrass_transform import \
        WeierstrassTransformationWithInverse
    return WeierstrassTransformationWithInverse(cubic, E, fwd_defining_poly,
                                                fwd_post, inv_defining_poly,
                                                inv_post)
Beispiel #40
0
    def homogenize(self,n,newvar='h'):
        r"""
        Return the homogenization of ``self``. If ``self.domain()`` is a subscheme, the domain of
        the homogenized map is the projective embedding of ``self.domain()``

        INPUT:

        - ``newvar`` -- the name of the homogenization variable (only used when ``self.domain()`` is affine space)

        - ``n`` -- the n-th projective embedding into projective space

        OUTPUT:

        - :class:`SchemMorphism_polynomial_projective_space`

        EXAMPLES::

            sage: A.<x,y>=AffineSpace(ZZ,2)
            sage: H=Hom(A,A)
            sage: f=H([(x^2-2)/x^5,y^2])
            sage: f.homogenize(2,'z')
            Scheme endomorphism of Projective Space of dimension 2 over Integer Ring
              Defn: Defined on coordinates by sending (x : y : z) to
                    (x^2*z^5 - 2*z^7 : x^5*y^2 : x^5*z^2)

        ::

            sage: A.<x,y>=AffineSpace(CC,2)
            sage: H=Hom(A,A)
            sage: f=H([(x^2-2)/(x*y),y^2-x])
            sage: f.homogenize(0,'z')
            Scheme endomorphism of Projective Space of dimension 2 over Complex
            Field with 53 bits of precision
              Defn: Defined on coordinates by sending (x : y : z) to
                    (x*y*z^2 : x^2*z^2 + (-2.00000000000000)*z^4 : x*y^3 - x^2*y*z)

        ::

            sage: A.<x,y>=AffineSpace(ZZ,2)
            sage: X=A.subscheme([x-y^2])
            sage: H=Hom(X,X)
            sage: f=H([9*y^2,3*y])
            sage: f.homogenize(2)
            Scheme endomorphism of Closed subscheme of Projective Space of dimension 2 over Integer Ring defined by:
              -x1^2 + x0*x2
              Defn: Defined on coordinates by sending (x0 : x1 : x2) to
                    (9*x0*x2 : 3*x1*x2 : x2^2)

        ::

            sage: R.<t>=PolynomialRing(ZZ)
            sage: A.<x,y>=AffineSpace(R,2)
            sage: H=Hom(A,A)
            sage: f=H([(x^2-2)/y,y^2-x])
            sage: f.homogenize(0,'z')
            Scheme endomorphism of Projective Space of dimension 2 over Univariate
            Polynomial Ring in t over Integer Ring
              Defn: Defined on coordinates by sending (x : y : z) to
                    (y*z^2 : x^2*z + (-2)*z^3 : y^3 - x*y*z)
        """
        A=self.domain()
        B=self.codomain()
        N=A.ambient_space().dimension_relative()
        NB=B.ambient_space().dimension_relative()
        Vars=list(A.ambient_space().variable_names())+[newvar]
        S=PolynomialRing(A.base_ring(),Vars)
        try:
            l=lcm([self[i].denominator() for i in range(N)])
        except Exception:  #no lcm
            l=prod([self[i].denominator() for i in range(N)])

        from sage.rings.polynomial.polynomial_ring import PolynomialRing_general
        from sage.rings.polynomial.multi_polynomial_ring_generic import MPolynomialRing_generic
        if self.domain().base_ring()==RealField() or self.domain().base_ring()==ComplexField():
            F=[S(((self[i]*l).numerator())._maxima_().divide(self[i].denominator())[0].sage()) for i in range(N)]
        elif isinstance(self.domain().base_ring(),(PolynomialRing_general,MPolynomialRing_generic)):
            F=[S(((self[i]*l).numerator())._maxima_().divide(self[i].denominator())[0].sage()) for i in range(N)]
        else:
            F=[S(self[i]*l) for i in range(N)]
        F.insert(n,S(l))
        d=max([F[i].degree() for i in range(N+1)])
        F=[F[i].homogenize(newvar)*S.gen(N)**(d-F[i].degree()) for i in range(N+1)]
        from sage.schemes.affine.affine_space import is_AffineSpace
        if is_AffineSpace(A)==True:
            from sage.schemes.projective.projective_space import ProjectiveSpace
            X=ProjectiveSpace(A.base_ring(),NB,Vars)
        else:
            X=A.projective_embedding(n).codomain()
            phi=S.hom(X.ambient_space().gens(),X.ambient_space().coordinate_ring())
            F=[phi(f) for f in F]
        H=Hom(X,X)
        return(H(F))
    def dual(self):
        r"""
        Return the projective dual of the given subscheme of projective space.

        INPUT:

        - ``X`` -- A subscheme of projective space. At present, ``X`` is
          required to be an irreducible and reduced hypersurface defined
          over `\QQ` or a finite field.

        OUTPUT:

        - The dual of ``X`` as a subscheme of the dual projective space.

        EXAMPLES:

        The dual of a smooth conic in the plane is also a smooth conic::

            sage: R.<x, y, z> = QQ[]
            sage: P.<x, y, z> = ProjectiveSpace(2, QQ)
            sage: I = R.ideal(x^2 + y^2 + z^2)
            sage: X = P.subscheme(I)
            sage: X.dual()
            Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
              y0^2 + y1^2 + y2^2

        The dual of the twisted cubic curve in projective 3-space is a singular
        quartic surface. In the following example, we compute the dual of this
        surface, which by double duality is equal to the twisted cubic itself.
        The output is the twisted cubic as an intersection of three quadrics::

            sage: R.<x, y, z, w> = QQ[]
            sage: P.<x, y, z, w> = ProjectiveSpace(3, QQ)
            sage: I = R.ideal(y^2*z^2 - 4*x*z^3 - 4*y^3*w + 18*x*y*z*w - 27*x^2*w^2)
            sage: X = P.subscheme(I)
            sage: X.dual()
            Closed subscheme of Projective Space of dimension 3 over
            Rational Field defined by:
              y2^2 - y1*y3,
              y1*y2 - y0*y3,
              y1^2 - y0*y2

        The singular locus of the quartic surface in the last example
        is itself supported on a twisted cubic::

            sage: X.Jacobian().radical()
            Ideal (z^2 - 3*y*w, y*z - 9*x*w, y^2 - 3*x*z) of Multivariate
            Polynomial Ring in x, y, z, w over Rational Field

        An example over a finite field::

            sage: R = PolynomialRing(GF(61), 'a,b,c')
            sage: P.<a, b, c> = ProjectiveSpace(2, R.base_ring())
            sage: X = P.subscheme(R.ideal(a*a+2*b*b+3*c*c))
            sage: X.dual()
            Closed subscheme of Projective Space of dimension 2 over
            Finite Field of size 61 defined by:
            y0^2 - 30*y1^2 - 20*y2^2

        TESTS::

            sage: R = PolynomialRing(Qp(3), 'a,b,c')
            sage: P.<a, b, c> = ProjectiveSpace(2, R.base_ring())
            sage: X = P.subscheme(R.ideal(a*a+2*b*b+3*c*c))
            sage: X.dual()
            Traceback (most recent call last):
            ...
            NotImplementedError: base ring must be QQ or a finite field
        """
        from sage.libs.singular.function_factory import ff

        K = self.base_ring()
        if not (is_RationalField(K) or is_FiniteField(K)):
            raise NotImplementedError("base ring must be QQ or a finite field")
        I = self.defining_ideal()
        m = I.ngens()
        n = I.ring().ngens() - 1
        if (m != 1 or (n < 1) or I.is_zero() or I.is_trivial()
                or not I.is_prime()):
            raise NotImplementedError("At the present, the method is only"
                                      " implemented for irreducible and"
                                      " reduced hypersurfaces and the given"
                                      " list of generators for the ideal must"
                                      " have exactly one element.")
        R = PolynomialRing(K, 'x', n + 1)
        from sage.schemes.projective.projective_space import ProjectiveSpace
        Pd = ProjectiveSpace(n, K, 'y')
        Rd = Pd.coordinate_ring()
        x = R.variable_names()
        y = Rd.variable_names()
        S = PolynomialRing(K, x + y + ('t', ))
        if S.has_coerce_map_from(I.ring()):
            T = PolynomialRing(K, 'w', n + 1)
            I_S = (I.change_ring(T)).change_ring(S)
        else:
            I_S = I.change_ring(S)
        f_S = I_S.gens()[0]
        z = S.gens()
        J = I_S
        for i in range(n + 1):
            J = J + S.ideal(z[-1] * f_S.derivative(z[i]) - z[i + n + 1])

        sat = ff.elim__lib.sat

        max_ideal = S.ideal(z[n + 1:2 * n + 2])
        J_sat_gens = sat(J, max_ideal)[0]
        J_sat = S.ideal(J_sat_gens)
        L = J_sat.elimination_ideal(z[0:n + 1] + (z[-1], ))
        return Pd.subscheme(L.change_ring(Rd))
Beispiel #42
0
def _orthogonal_polar_graph(m, q, sign="+", point_type=[0]):
    r"""
    A helper function to build ``OrthogonalPolarGraph`` and ``NO2,3,5`` graphs.

    See see the `page of
    Andries Brouwer's website <http://www.win.tue.nl/~aeb/graphs/srghub.html>`_.

    INPUT:

    - ``m,q`` (integers) -- `q` must be a prime power.

    - ``sign`` -- ``"+"`` or ``"-"`` if `m` is even, ``"+"`` (default)
      otherwise.

    - ``point_type`` -- a list of elements from `F_q`

    EXAMPLES:
    
    Petersen graph::
`
        sage: from sage.graphs.generators.classical_geometries import _orthogonal_polar_graph
        sage: g=_orthogonal_polar_graph(3,5,point_type=[2,3])
        sage: g.is_strongly_regular(parameters=True)
        (10, 3, 0, 1)

    A locally Petersen graph (a.k.a. Doro graph, a.k.a. Hall graph)::

        sage: g=_orthogonal_polar_graph(4,5,'-',point_type=[2,3])
        sage: g.is_distance_regular(parameters=True)
        ([10, 6, 4, None], [None, 1, 2, 5])

    Various big and slow to build graphs:

    `NO^+(7,3)`::

        sage: g=_orthogonal_polar_graph(7,3,point_type=[1])  # not tested (long time)
        sage: g.is_strongly_regular(parameters=True)       # not tested (long time)
        (378, 117, 36, 36)

    `NO^-(7,3)`::

        sage: g=_orthogonal_polar_graph(7,3,point_type=[-1]) # not tested (long time)
        sage: g.is_strongly_regular(parameters=True)       # not tested (long time)
        (351, 126, 45, 45)

    `NO^+(6,3)`::

        sage: g=_orthogonal_polar_graph(6,3,point_type=[1])
        sage: g.is_strongly_regular(parameters=True)
        (117, 36, 15, 9)

    `NO^-(6,3)`::

        sage: g=_orthogonal_polar_graph(6,3,'-',point_type=[1])
        sage: g.is_strongly_regular(parameters=True)
        (126, 45, 12, 18)

    `NO^{-,\perp}(5,5)`::

        sage: g=_orthogonal_polar_graph(5,5,point_type=[2,3]) # long time
        sage: g.is_strongly_regular(parameters=True)          # long time
        (300, 65, 10, 15)

    `NO^{+,\perp}(5,5)`::

        sage: g=_orthogonal_polar_graph(5,5,point_type=[1,-1]) # not tested (long time)
        sage: g.is_strongly_regular(parameters=True) # not tested (long time)
        (325, 60, 15, 10)

    TESTS::

        sage: g=_orthogonal_polar_graph(5,3,point_type=[-1])
        sage: g.is_strongly_regular(parameters=True)
        (45, 12, 3, 3)
        sage: g=_orthogonal_polar_graph(5,3,point_type=[1])
        sage: g.is_strongly_regular(parameters=True)
        (36, 15, 6, 6)

    """
    from sage.schemes.projective.projective_space import ProjectiveSpace
    from sage.rings.finite_rings.constructor import FiniteField
    from sage.modules.free_module_element import free_module_element as vector
    from sage.matrix.constructor import Matrix
    from sage.libs.gap.libgap import libgap
    from itertools import combinations

    if m % 2 == 0:
        if sign != "+" and sign != "-":
            raise ValueError("sign must be equal to either '-' or '+' when "
                             "m is even")
    else:
        if sign != "" and sign != "+":
            raise ValueError("sign must be equal to either '' or '+' when "
                             "m is odd")
        sign = ""

    e = {'+': 1, '-': -1, '': 0}[sign]

    M = Matrix(
        libgap.InvariantQuadraticForm(libgap.GeneralOrthogonalGroup(
            e, m, q))['matrix'])
    Fq = libgap.GF(q).sage()
    PG = map(vector, ProjectiveSpace(m - 1, Fq))
    map(lambda x: x.set_immutable(), PG)

    def F(x):
        return x * M * x

    if q % 2 == 0:

        def P(x, y):
            return F(x - y)
    else:

        def P(x, y):
            return x * M * y + y * M * x

    V = [x for x in PG if F(x) in point_type]

    G = Graph([V, lambda x, y: P(x, y) == 0], loops=False)

    G.relabel()
    return G