diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 367e62d5257..0a7b7dff59b 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -34,8 +34,10 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.categories.fields import Fields from sage.categories.homset import Hom from sage.interfaces.all import singular +import sage.libs.singular from sage.misc.all import add @@ -142,6 +144,73 @@ def projective_closure(self, i=0, PP=None): from constructor import Curve return Curve(AlgebraicScheme_subscheme_affine.projective_closure(self, i, PP)) + def multiplicity(self, P): + r""" + Return the multiplicity of this affine curve at the point ``P``. + + This is computed as the multiplicity of the local ring of self corresponding to ``P``. This + curve must be defined over a field. An error is raised if ``P`` is not a point on this curve. + + INPUT: + + - ``P`` -- a point in the ambient space of this curve. + + OUTPUT: + + An integer. + + EXAMPLES:: + + sage: A. = AffineSpace(CC, 3) + sage: C = A.curve([y - x^2, z - x^3]) + sage: Q = A([1,1,1]) + sage: C.multiplicity(Q) + 1 + + :: + + sage: A. = AffineSpace(QQ, 4) + sage: C = A.curve([y^9 - x^5, z^10 - w - y^4, z - y]) + sage: C.multiplicity(A([0,0,0,0])) + 5 + + :: + + sage: A. = AffineSpace(GF(23), 5) + sage: C = A.curve([x^8 - y, y^7 - z, z^3 - 1, w^5 - v^3]) + sage: Q = A([22,1,1,0,0]) + sage: C.multiplicity(Q) + 3 + + :: + + sage: A. = AffineSpace(QQ, 3) + sage: C = A.curve([y^2 - x^3, x^2 - z^2]) + sage: Q = A([1,1,0]) + sage: C.multiplicity(Q) + Traceback (most recent call last): + ... + TypeError: (=(1, 1, 0)) is not a point on (=Affine Curve over Rational + Field defined by -x^3 + y^2, x^2 - z^2) + """ + if not self.base_ring() in Fields(): + raise TypeError("curve must be defined over a field") + + # Check whether P is a point on this curve + try: + P = self(P) + except TypeError: + raise TypeError("(=%s) is not a point on (=%s)"%(P,self)) + + # Apply a linear change of coordinates to self so that P is sent to the origin + # and then compute the multiplicity of the local ring of the translated curve + # corresponding to the point (0,...,0) + AA = self.ambient_space() + chng_coords = [AA.gens()[i] + P[i] for i in range(AA.dimension_relative())] + R = AA.coordinate_ring().change_ring(order='negdegrevlex') + I = R.ideal([f(chng_coords) for f in self.defining_polynomials()]) + return singular.mult(singular.std(I)).sage() + def intersection_multiplicity(self, C, P): r""" Return the intersection multiplicity of this curve and the curve ``C`` at the point ``P``. @@ -448,6 +517,184 @@ def is_transverse(self, C, P): # there is only one tangent at a nonsingular point of a plane curve return not self.tangents(P)[0] == C.tangents(P)[0] + def multiplicity(self, P): + r""" + Return the multiplicity of this affine plane curve at the point ``P``. + + In the special case of affine plane curves, the multiplicity of an affine + plane curve at the point (0,0) can be computed as the minimum of the degrees + of the homogeneous components of its defining polynomial. To compute the + multiplicity of a different point, a linear change of coordinates is used. + + This curve must be defined over a field. An error if raised if ``P`` is + not a point on this curve. + + INPUT: + + - ``P`` -- a point in the ambient space of this curve. + + OUTPUT: + + An integer. + + EXAMPLES:: + + sage: A. = AffineSpace(QQ, 2) + sage: C = Curve([y^2 - x^3], A) + sage: Q1 = A([1,1]) + sage: C.multiplicity(Q1) + 1 + sage: Q2 = A([0,0]) + sage: C.multiplicity(Q2) + 2 + + :: + + sage: A. = AffineSpace(QQbar,2) + sage: C = Curve([-x^7 + (-7)*x^6 + y^6 + (-21)*x^5 + 12*y^5 + (-35)*x^4 + 60*y^4 +\ + (-35)*x^3 + 160*y^3 + (-21)*x^2 + 240*y^2 + (-7)*x + 192*y + 63], A) + sage: Q = A([-1,-2]) + sage: C.multiplicity(Q) + 6 + + :: + + sage: A. = AffineSpace(QQ, 2) + sage: C = A.curve([y^3 - x^3 + x^6]) + sage: Q = A([1,1]) + sage: C.multiplicity(Q) + Traceback (most recent call last): + ... + TypeError: (=(1, 1)) is not a point on (=Affine Plane Curve over + Rational Field defined by x^6 - x^3 + y^3) + """ + if not self.base_ring() in Fields(): + raise TypeError("curve must be defined over a field") + + # Check whether P is a point on this curve + try: + P = self(P) + except TypeError: + raise TypeError("(=%s) is not a point on (=%s)"%(P,self)) + + # Apply a linear change of coordinates to self so that P becomes (0,0) + AA = self.ambient_space() + f = self.defining_polynomials()[0](AA.gens()[0] + P[0], AA.gens()[1] + P[1]) + + # Compute the multiplicity of the new curve at (0,0), which is the minimum of the degrees of its + # nonzero terms + return min([g.degree() for g in f.monomials()]) + + def tangents(self, P): + r""" + Return the tangents of this affine plane curve at the point ``P``. + + The point ``P`` must be a point on this curve. + + INPUT: + + - ``P`` -- a point on this curve. + + OUTPUT: + + - a list of polynomials in the coordinate ring of the ambient space of this curve. + + EXAMPLES:: + + sage: R. = QQ[] + sage: K. = NumberField(a^2 - 3) + sage: A. = AffineSpace(K, 2) + sage: C = Curve([(x^2 + y^2 - 2*x)^2 - x^2 - y^2], A) + sage: Q = A([0,0]) + sage: C.tangents(Q) + [x + (-1/3*b)*y, x + (1/3*b)*y] + + :: + + sage: A. = AffineSpace(QQ, 2) + sage: C = A.curve([y^2 - x^3 - x^2]) + sage: Q = A([0,0]) + sage: C.tangents(Q) + [x - y, x + y] + + :: + + sage: A. = AffineSpace(QQ, 2) + sage: C = A.curve([y*x - x^4 + 2*x^2]) + sage: Q = A([1,1]) + sage: C.tangents(Q) + Traceback (most recent call last): + ... + TypeError: (=(1, 1)) is not a point on (=Affine Plane Curve over + Rational Field defined by -x^4 + 2*x^2 + x*y) + """ + r = self.multiplicity(P) + f = self.defining_polynomials()[0] + vars = self.ambient_space().gens() + deriv = [f.derivative(vars[0],i).derivative(vars[1],r-i)(list(P)) for i in range(r+1)] + from sage.arith.misc import binomial + T = sum([binomial(r,i)*deriv[i]*(vars[0] - P[0])**i*(vars[1] - P[1])**(r-i) for i in range(r+1)]) + fact = T.factor() + return [l[0] for l in fact] + + def is_ordinary_singularity(self, P): + r""" + Return whether the singular point ``P`` of this affine plane curve is an ordinary singularity. + + The point ``P`` is an ordinary singularity of this curve if it is a singular point, and + if the tangents of this curve at ``P`` are distinct. + + INPUT: + + - ``P`` -- a point on this curve. + + OUTPUT: + + - Boolean. True or False depending on whether ``P`` is or is not an ordinary singularity of this + curve, respectively. An error is raised if ``P`` is not a singular point of this curve. + + EXAMPLES:: + + sage: A. = AffineSpace(QQ, 2) + sage: C = Curve([y^2 - x^3], A) + sage: Q = A([0,0]) + sage: C.is_ordinary_singularity(Q) + False + + :: + + sage: R. = QQ[] + sage: K. = NumberField(a^2 - 3) + sage: A. = AffineSpace(K, 2) + sage: C = Curve([(x^2 + y^2 - 2*x)^2 - x^2 - y^2], A) + sage: Q = A([0,0]) + sage: C.is_ordinary_singularity(Q) + True + + :: + + sage: A. = AffineSpace(QQ, 2) + sage: C = A.curve([x^2*y - y^2*x + y^2 + x^3]) + sage: Q = A([-1,-1]) + sage: C.is_ordinary_singularity(Q) + Traceback (most recent call last): + ... + TypeError: (=(-1, -1)) is not a singular point of (=Affine Plane Curve + over Rational Field defined by x^3 + x^2*y - x*y^2 + y^2) + """ + r = self.multiplicity(P) + if r < 2: + raise TypeError("(=%s) is not a singular point of (=%s)"%(P,self)) + + T = self.tangents(P) + + # when there is a tangent of higher multiplicity + if len(T) < r: + return False + + # otherwise they are distinct + return True + class AffinePlaneCurve_finite_field(AffinePlaneCurve): def rational_points(self, algorithm="enum"): r""" diff --git a/src/sage/schemes/curves/curve.py b/src/sage/schemes/curves/curve.py index 79fd5e699d7..876676dfd3f 100644 --- a/src/sage/schemes/curves/curve.py +++ b/src/sage/schemes/curves/curve.py @@ -3,7 +3,7 @@ """ from sage.categories.finite_fields import FiniteFields - +from sage.categories.fields import Fields from sage.misc.all import latex from sage.schemes.generic.algebraic_scheme import AlgebraicScheme_subscheme @@ -197,6 +197,119 @@ def union(self, other): __add__ = union + def singular_subscheme(self): + r""" + Return the subscheme of singular points of this curve. + + OUTPUT: + + - a subscheme in the ambient space of this curve. + + EXAMPLES:: + + sage: A. = AffineSpace(CC, 2) + sage: C = Curve([y^4 - 2*x^5 - x^2*y], A) + sage: C.singular_subscheme() + Closed subscheme of Affine Space of dimension 2 over Complex Field with + 53 bits of precision defined by: + (-2.00000000000000)*x^5 + y^4 - x^2*y, + (-10.0000000000000)*x^4 + (-2.00000000000000)*x*y, + 4.00000000000000*y^3 - x^2 + + :: + + sage: P. = ProjectiveSpace(QQ, 3) + sage: C = Curve([y^8 - x^2*z*w^5, w^2 - 2*y^2 - x*z], P) + sage: C.singular_subscheme() + Closed subscheme of Projective Space of dimension 3 over Rational Field + defined by: + y^8 - x^2*z*w^5, + -2*y^2 - x*z + w^2, + -x^3*y*z^4 + 3*x^2*y*z^3*w^2 - 3*x*y*z^2*w^4 + 8*x*y*z*w^5 + y*z*w^6, + x^2*z*w^5, + -5*x^2*z^2*w^4 - 4*x*z*w^6, + x^4*y*z^3 - 3*x^3*y*z^2*w^2 + 3*x^2*y*z*w^4 - 4*x^2*y*w^5 - x*y*w^6, + -2*x^3*y*z^3*w + 6*x^2*y*z^2*w^3 - 20*x^2*y*z*w^4 - 6*x*y*z*w^5 + + 2*y*w^7, + -5*x^3*z*w^4 - 2*x^2*w^6 + """ + return self.ambient_space().subscheme(self.Jacobian()) + + def singular_points(self, F=None): + r""" + Return the set of singular points of this curve. + + INPUT: + + - ``F`` -- (default: None) field over which to find the singular points. If not given, + the base ring of this curve is used. + + OUTPUT: + + - a list of points in the ambient space of this curve. + + EXAMPLES:: + + sage: A. = AffineSpace(QQ, 3) + sage: C = Curve([y^2 - x^5, x - z], A) + sage: C.singular_points() + [(0, 0, 0)] + + :: + + sage: R. = QQ[] + sage: K. = NumberField(a^8 - a^4 + 1) + sage: P. = ProjectiveSpace(QQ, 2) + sage: C = Curve([359/12*x*y^2*z^2 + 2*y*z^4 + 187/12*y^3*z^2 + x*z^4\ + + 67/3*x^2*y*z^2 + 117/4*y^5 + 9*x^5 + 6*x^3*z^2 + 393/4*x*y^4\ + + 145*x^2*y^3 + 115*x^3*y^2 + 49*x^4*y], P) + sage: C.singular_points(K) + [(1/2*b^5 + 1/2*b^3 - 1/2*b - 1 : 1 : 0), (-2/3*b^4 + 1/3 : 0 : 1), + (2/3*b^4 - 1/3 : 0 : 1), (b^6 : -b^6 : 1), (-b^6 : b^6 : 1), + (-1/2*b^5 - 1/2*b^3 + 1/2*b - 1 : 1 : 0)] + """ + if F is None: + if not self.base_ring() in Fields(): + raise TypeError("curve must be defined over a field") + elif not F in Fields(): + raise TypeError("(=%s) must be a field"%F) + X = self.singular_subscheme() + return X.rational_points(F=F) + + def is_singular(self, P=None): + r""" + Return whether ``P`` is a singular point of this curve, or if no point is passed, + whether this curve is singular or not. + + This just uses the is_smooth function for algebraic subschemes. + + INPUT: + + - ``P`` -- (default: None) a point on this curve. + + OUTPUT: + + - Boolean. If a point ``P`` is provided, and if ``P`` lies on this curve, returns True + if ``P`` is a singular point of this curve, and False otherwise. If no point is provided, + returns True or False depending on whether this curve is or is not singular, respectively. + + EXAMPLES:: + + sage: P. = ProjectiveSpace(QQ, 3) + sage: C = P.curve([y^2 - x^2 - z^2, z - w]) + sage: C.is_singular() + False + + :: + + sage: A. = AffineSpace(GF(11), 3) + sage: C = A.curve([y^3 - z^5, x^5 - y + 1]) + sage: Q = A([7,0,0]) + sage: C.is_singular(Q) + True + """ + return not self.is_smooth(P) + def intersects_at(self, C, P): r""" Return whether the point ``P`` is or is not in the intersection of this curve with the curve ``C``. diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 005a93e0559..a43b9d9102e 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -36,6 +36,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.categories.fields import Fields from sage.categories.homset import Hom from sage.interfaces.all import singular from sage.misc.all import add, sage_eval @@ -128,6 +129,82 @@ def affine_patch(self, i, AA=None): from constructor import Curve return Curve(AlgebraicScheme_subscheme_projective.affine_patch(self, i, AA)) + def multiplicity(self, P): + r""" + Return the multiplicity of this projective curve at the point ``P``. + + This is computed as the corresponding multiplicity of an affine patch of this curve that + contains the point. This curve must be defined over a field. An error is returned if ``P`` + not a point on this curve. + + INPUT: + + - ``P`` -- a point in the ambient space of this curve. + + OUTPUT: + + An integer. + + EXAMPLES:: + + sage: P. = ProjectiveSpace(QQ, 2) + sage: C = Curve([y^4 - x^3*z - x^2*z^2], P) + sage: Q = P([0,0,1]) + sage: C.multiplicity(Q) + 2 + + :: + + sage: P. = ProjectiveSpace(RR, 3) + sage: C = Curve([y^8 - x^2*z*w^5, w^2 - 2*y^2 - x*z], P) + sage: Q1 = P([-1,-1,1,1]) + sage: C.multiplicity(Q1) + 1 + sage: Q2 = P([1,0,0,0]) + sage: C.multiplicity(Q2) + 7 + sage: Q3 = P([0,0,1,0]) + sage: C.multiplicity(Q3) + 8 + + :: + + sage: P. = ProjectiveSpace(GF(29), 3) + sage: C = Curve([y^17 - x^5*w^4*z^8, x*y - z^2], P) + sage: Q = P([3,0,0,1]) + sage: C.multiplicity(Q) + 8 + + :: + + sage: P. = ProjectiveSpace(QQ, 2) + sage: C = P.curve([y^2*z^5 - x^7]) + sage: Q = P([-1,-1,1]) + sage: C.multiplicity(Q) + Traceback (most recent call last): + ... + TypeError: (=(-1 : -1 : 1)) is not a point on (=Projective Plane Curve + over Rational Field defined by -x^7 + y^2*z^5) + """ + if not self.base_ring() in Fields(): + raise TypeError("curve must be defined over a field") + + # Check whether P is a point on this curve + try: + P = self(P) + except TypeError: + raise TypeError("(=%s) is not a point on (=%s)"%(P,self)) + + # Find an affine chart of the ambient space of self that contains P + i = 0 + while(P[i] == 0): + i = i + 1 + C = self.affine_patch(i) + Q = list(P) + t = Q.pop(i) + Q = [1/t*Q[j] for j in range(self.ambient_space().dimension_relative())] + return C.multiplicity(C.ambient_space()(Q)) + def is_complete_intersection(self): r""" Return whether this projective curve is or is not a complete intersection. @@ -457,9 +534,20 @@ def plot(self, *args, **kwds): C = Curve(self.affine_patch(patch)) return C.plot(*args, **kwds) - def is_singular(C): + def is_singular(self, P=None): r""" - Returns whether the curve is singular or not. + Return whether this curve is singular or not, or if a point ``P`` is provided, + whether ``P`` is a singular point of this curve. + + INPUT: + + - ``P`` -- (default: None) a point on this curve. + + OUTPUT: + + - Boolean. If no point ``P`` is provided, returns True of False depending on whether + this curve is singular or not. If a point ``P`` is provided, returns True or False + depending on whether ``P`` is or is not a singular point of this curve. EXAMPLES: @@ -508,33 +596,137 @@ def is_singular(C): sage: G = Curve(X^2+Y*Z) sage: G.is_singular() False - """ - poly = C.defining_polynomial() - return poly.parent().ideal(poly.gradient()+[poly]).dimension()> 0 - def is_transverse(self, C, P): + :: + + sage: P. = ProjectiveSpace(CC, 2) + sage: C = Curve([y^4 - x^3*z], P) + sage: Q = P([0,0,1]) + sage: C.is_singular() + True + """ + if P is None: + poly = self.defining_polynomial() + return poly.parent().ideal(poly.gradient()+[poly]).dimension() > 0 + else: + return not self.is_smooth(P) + + def tangents(self, P): + r""" + Return the tangents of this projective plane curve at the point ``P``. + + These are found by homogenizing the tangents of an affine patch of this curve + containing ``P``. The point ``P`` must be a point on this curve. + + INPUT: + + - ``P`` -- a point on this curve. + + OUTPUT: + + - a list of polynomials in the coordinate ring of the ambient space of this curve. + + EXAMPLES:: + + sage: P. = ProjectiveSpace(QQ,2) + sage: C = P.curve([x^2*y^3*z^4 - y^6*z^3 - 4*x^2*y^4*z^3 - 4*x^4*y^2*z^3 + 3*y^7*z^2 +\ + 10*x^2*y^5*z^2 + 9*x^4*y^3*z^2 + 5*x^6*y*z^2 - 3*y^8*z - 9*x^2*y^6*z - 11*x^4*y^4*z -\ + 7*x^6*y^2*z - 2*x^8*z + y^9 + 2*x^2*y^7 + 3*x^4*y^5 + 4*x^6*y^3 + 2*x^8*y]) + sage: Q = P([0,1,1]) + sage: C.tangents(Q) + [-y + z, -3*x^2 + y^2 - 2*y*z + z^2] + + :: + + sage: P. = ProjectiveSpace(QQ, 2) + sage: C = P.curve([z^3*x + y^4 - x^2*z^2]) + sage: Q = P([1,1,1]) + sage: C.tangents(Q) + Traceback (most recent call last): + ... + TypeError: (=(1 : 1 : 1)) is not a point on (=Projective Plane Curve + over Rational Field defined by y^4 - x^2*z^2 + x*z^3) + """ + # Check whether P is a point on this curve + try: + P = self(P) + except TypeError: + raise TypeError("(=%s) is not a point on (=%s)"%(P,self)) + + # Find an affine chart of the ambient space of self that contains P + i = 0 + while(P[i] == 0): + i = i + 1 + C = self.affine_patch(i) + Q = list(P) + t = Q.pop(i) + L = C.tangents(C.ambient_space()([1/t*Q[j] for j in range(self.ambient_space().dimension_relative())])) + R = self.ambient_space().coordinate_ring() + H = Hom(C.ambient_space().coordinate_ring(), R) + G = list(R.gens()) + x = G.pop(i) + phi = H(G) + return [phi(g).homogenize(x) for g in L] + + def is_ordinary_singularity(self, P): r""" - Return whether the intersection of this curve with the curve ``C`` at the point ``P`` is transverse. + Return whether the singular point ``P`` of this projective plane curve is an ordinary singularity. + + The point ``P`` is an ordinary singularity of this curve if it is a singular point, and + if the tangents of this curve at ``P`` are distinct. INPUT: - - ``C`` -- a curve in the ambient space of this curve. + - ``P`` -- a point on this curve. - - ``P`` -- a point in the intersection of both curves that is not a singular point of either curve. + OUTPUT: - OUPUT: Boolean. + - Boolean. True or False depending on whether ``P`` is or is not an ordinary singularity of this + curve, respectively. An error is raised if ``P`` is not a singular point of this curve. EXAMPLES:: - + sage: P. = ProjectiveSpace(QQ, 2) + sage: C = Curve([y^2*z^3 - x^5], P) + sage: Q = P([0,0,1]) + sage: C.is_ordinary_singularity(Q) + False + + :: + + sage: R. = QQ[] + sage: K. = NumberField(a^2 - 3) + sage: P. = ProjectiveSpace(K, 2) + sage: C = P.curve([x^2*y^3*z^4 - y^6*z^3 - 4*x^2*y^4*z^3 - 4*x^4*y^2*z^3 + 3*y^7*z^2 + 10*x^2*y^5*z^2\ + + 9*x^4*y^3*z^2 + 5*x^6*y*z^2 - 3*y^8*z - 9*x^2*y^6*z - 11*x^4*y^4*z - 7*x^6*y^2*z - 2*x^8*z + y^9 +\ + 2*x^2*y^7 + 3*x^4*y^5 + 4*x^6*y^3 + 2*x^8*y]) + sage: Q = P([0,1,1]) + sage: C.is_ordinary_singularity(Q) + True + + :: + + sage: P. = ProjectiveSpace(QQ, 2) + sage: C = P.curve([z^5 - y^5 + x^5 + x*y^2*z^2]) + sage: Q = P([0,1,1]) + sage: C.is_ordinary_singularity(Q) + Traceback (most recent call last): + ... + TypeError: (=(0 : 1 : 1)) is not a singular point of (=Projective Plane + Curve over Rational Field defined by x^5 - y^5 + x*y^2*z^2 + z^5) """ - if not self.intersects_at(C, P): - raise TypeError("(=%s) must be a point in the intersection of (=%s) and this curve"%(P,C)) - if self.is_singular(P) or C.is_singular(P): - raise TypeError("(=%s) must be a nonsingular point of both (=%s) and this curve"%(P,C)) + r = self.multiplicity(P) + if r < 2: + raise TypeError("(=%s) is not a singular point of (=%s)"%(P,self)) + + T = self.tangents(P) + + # when there is a tangent of higher multiplicity + if len(T) < r: + return False - # there is only one tangent at a nonsingular point of a plane curve - return not self.tangents(P)[0] == C.tangents(P)[0] + # otherwise they are distinct + return True class ProjectivePlaneCurve_finite_field(ProjectivePlaneCurve): def rational_points_iterator(self): diff --git a/src/sage/schemes/projective/projective_homset.py b/src/sage/schemes/projective/projective_homset.py index a94fccaba98..75fdbf0888b 100644 --- a/src/sage/schemes/projective/projective_homset.py +++ b/src/sage/schemes/projective/projective_homset.py @@ -169,6 +169,9 @@ def points(self, B=0, prec=53): #each dictionary entry P.update({R.gen(varindex):-pol.constant_coefficient() / pol.monomial_coefficient(r)}) new_points.append(copy(P)) + else: + new_points.append(P) + good = 1 if good: points = new_points #the dictionary entries now have values for all coordinates