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))
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)
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)
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
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
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
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
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))