Path: blob/master/sage/schemes/plane_curves/projective_curve.py
4130 views
"""1Projective plane curves over a general ring23AUTHORS:45- William Stein (2005-11-13)67- David Joyner (2005-11-13)89- David Kohel (2006-01)1011- Moritz Minzlaff (2010-11)12"""1314#*****************************************************************************15# Copyright (C) 2005 William Stein <[email protected]>16#17# Distributed under the terms of the GNU General Public License (GPL)18#19# The full text of the GPL is available at:20#21# http://www.gnu.org/licenses/22#*****************************************************************************2324from sage.interfaces.all import singular25from sage.misc.all import add, sage_eval26from sage.rings.all import degree_lowest_rational_function2728from sage.schemes.generic.projective_space import is_ProjectiveSpace2930from curve import Curve_generic_projective3132class ProjectiveSpaceCurve_generic(Curve_generic_projective):33def _repr_type(self):34return "Projective Space"3536def __init__(self, A, X):37if not is_ProjectiveSpace(A):38raise TypeError, "A (=%s) must be a projective space"%A39Curve_generic_projective.__init__(self, A, X)40d = self.dimension()41if d != 1:42raise ValueError, "defining equations (=%s) define a scheme of dimension %s != 1"%(X,d)4344class ProjectiveCurve_generic(Curve_generic_projective):45def __init__(self, A, f):46if not (is_ProjectiveSpace(A) and A.dimension != 2):47raise TypeError, "Argument A (= %s) must be a projective plane."%A48Curve_generic_projective.__init__(self, A, [f])4950def _repr_type(self):51return "Projective"5253def arithmetic_genus(self):54r"""55Return the arithmetic genus of this curve.5657This is the arithmetic genus `g_a(C)` as defined in58Hartshorne. If the curve has degree `d` then this is simply59`(d-1)(d-2)/2`. It need *not* equal the geometric genus60(the genus of the normalization of the curve).6162EXAMPLE::6364sage: x,y,z = PolynomialRing(GF(5), 3, 'xyz').gens()65sage: C = Curve(y^2*z^7 - x^9 - x*z^8); C66Projective Curve over Finite Field of size 5 defined by -x^9 + y^2*z^7 - x*z^867sage: C.arithmetic_genus()682869sage: C.genus()70471"""72d = self.defining_polynomial().total_degree()73return int((d-1)*(d-2)/2)7475def divisor_of_function(self, r):76"""77Return the divisor of a function on a curve.7879INPUT: r is a rational function on X8081OUTPUT:828384- ``list`` - The divisor of r represented as a list of85coefficients and points. (TODO: This will change to a more86structural output in the future.)878889EXAMPLES::9091sage: FF = FiniteField(5)92sage: P2 = ProjectiveSpace(2, FF, names = ['x','y','z'])93sage: R = P2.coordinate_ring()94sage: x, y, z = R.gens()95sage: f = y^2*z^7 - x^9 - x*z^896sage: C = Curve(f)97sage: K = FractionField(R)98sage: r = 1/x99sage: C.divisor_of_function(r) # todo: not implemented !!!!100[[-1, (0, 0, 1)]]101sage: r = 1/x^3102sage: C.divisor_of_function(r) # todo: not implemented !!!!103[[-3, (0, 0, 1)]]104"""105F = self.base_ring()106f = self.defining_polynomial()107x, y, z = f.parent().gens()108pnts = self.rational_points()109divf = []110for P in pnts:111if P[2] != F(0):112# What is the '5' in this line and the 'r()' in the next???113lcs = self.local_coordinates(P,5)114ldg = degree_lowest_rational_function(r(lcs[0],lcs[1]),z)115if ldg[0] != 0:116divf.append([ldg[0],P])117return divf118119120def local_coordinates(self, pt, n):121r"""122Return local coordinates to precision n at the given point.123124Behaviour is flaky - some choices of `n` are worst that125others.126127128INPUT:129130131- ``pt`` - an F-rational point on X which is not a132point of ramification for the projection (x,y) - x.133134- ``n`` - the number of terms desired135136137OUTPUT: x = x0 + t y = y0 + power series in t138139EXAMPLES::140141sage: FF = FiniteField(5)142sage: P2 = ProjectiveSpace(2, FF, names = ['x','y','z'])143sage: x, y, z = P2.coordinate_ring().gens()144sage: C = Curve(y^2*z^7-x^9-x*z^8)145sage: pt = C([2,3,1])146sage: C.local_coordinates(pt,9) # todo: not implemented !!!!147[2 + t, 3 + 3*t^2 + t^3 + 3*t^4 + 3*t^6 + 3*t^7 + t^8 + 2*t^9 + 3*t^11 + 3*t^12]148"""149150f = self.defining_polynomial()151R = f.parent()152F = self.base_ring()153p = F.characteristic()154x0 = F(pt[0])155y0 = F(pt[1])156astr = ["a"+str(i) for i in range(1,2*n)]157x,y = R.gens()158R0 = PolynomialRing(F,2*n+2,names = [str(x),str(y),"t"]+astr)159vars0 = R0.gens()160t = vars0[2]161yt = y0*t**0 + add([vars0[i]*t**(i-2) for i in range(3,2*n+2)])162xt = x0+t163ft = f(xt,yt)164S = singular165S.eval('ring s = '+str(p)+','+str(R0.gens())+',lp;')166S.eval('poly f = '+str(ft))167cmd = 'matrix c = coeffs ('+str(ft)+',t)'168S.eval(cmd)169N = int(S.eval('size(c)'))170b = ["c["+str(i)+",1]," for i in range(2,N/2-4)]171b = ''.join(b)172b = b[:len(b)-1] #to cut off the trailing comma173cmd = 'ideal I = '+b174S.eval(cmd)175c = S.eval('slimgb(I)')176d = c.split("=")177d = d[1:]178d[len(d)-1] += "\n"179e = [x[:x.index("\n")] for x in d]180vals = []181for x in e:182for y in vars0:183if str(y) in x:184if len(x.replace(str(y),"")) != 0:185i = x.find("-")186if i>0:187vals.append([eval(x[1:i]),x[:i],F(eval(x[i+1:]))])188i = x.find("+")189if i>0:190vals.append([eval(x[1:i]),x[:i],-F(eval(x[i+1:]))])191else:192vals.append([eval(str(y)[1:]),str(y),F(0)])193vals.sort()194k = len(vals)195v = [x0+t,y0+add([vals[i][2]*t**(i+1) for i in range(k)])]196return v197198def plot(self, *args, **kwds):199"""200Plot the real points of an affine patch of this projective201plane curve.202203204INPUT:205206207- ``self`` - an affine plane curve208209- ``patch`` - (optional) the affine patch to be plotted; if not210specified, the patch corresponding to the last projective211coordinate being nonzero212213- ``*args`` - optional tuples (variable, minimum, maximum) for214plotting dimensions215216- ``**kwds`` - optional keyword arguments passed on to217``implicit_plot``218219220EXAMPLES:221222A cuspidal curve::223224sage: R.<x, y, z> = QQ[]225sage: C = Curve(x^3 - y^2*z)226sage: C.plot()227228The other affine patches of the same curve::229230sage: C.plot(patch=0)231sage: C.plot(patch=1)232233An elliptic curve::234235sage: E = EllipticCurve('101a')236sage: C = Curve(E)237sage: C.plot()238sage: C.plot(patch=0)239sage: C.plot(patch=1)240241A hyperelliptic curve::242243sage: P.<x> = QQ[]244sage: f = 4*x^5 - 30*x^3 + 45*x - 22245sage: C = HyperellipticCurve(f)246sage: C.plot()247sage: C.plot(patch=0)248sage: C.plot(patch=1)249"""250# if user hasn't specified a favourite affine patch, take the251# one avoiding "infinity", i.e. the one corresponding to the252# last projective coordinate being nonzero253patch = kwds.pop('patch', self.ngens() - 1)254from constructor import Curve255C = Curve(self.affine_patch(patch))256return C.plot(*args, **kwds)257258def is_singular(C):259r"""260Returns whether the curve is singular or not.261262EXAMPLES:263264Over `\QQ`::265266sage: F = QQ267sage: P2.<X,Y,Z> = ProjectiveSpace(F,2)268sage: C = Curve(X^3-Y^2*Z)269sage: C.is_singular()270True271272Over a finite field::273274sage: F = GF(19)275sage: P2.<X,Y,Z> = ProjectiveSpace(F,2)276sage: C = Curve(X^3+Y^3+Z^3)277sage: C.is_singular()278False279sage: D = Curve(X^4-X*Z^3)280sage: D.is_singular()281True282sage: E = Curve(X^5+19*Y^5+Z^5)283sage: E.is_singular()284True285sage: E = Curve(X^5+9*Y^5+Z^5)286sage: E.is_singular()287False288289Over `\CC`::290291sage: F = CC292sage: P2.<X,Y,Z> = ProjectiveSpace(F,2)293sage: C = Curve(X)294sage: C.is_singular()295False296sage: D = Curve(Y^2*Z-X^3)297sage: D.is_singular()298True299sage: E = Curve(Y^2*Z-X^3+Z^3)300sage: E.is_singular()301False302303Showing that ticket #12187 is fixed::304305sage: F.<X,Y,Z> = GF(2)[]306sage: G = Curve(X^2+Y*Z)307sage: G.is_singular()308False309"""310poly = C.defining_polynomial()311return poly.parent().ideal(poly.gradient()+[poly]).dimension()> 0312313314class ProjectiveCurve_finite_field(ProjectiveCurve_generic):315def rational_points_iterator(self):316r"""317Return a generator object for the rational points on this curve.318319INPUT:320321- ``self`` -- a projective curve322323OUTPUT:324325A generator of all the rational points on the curve defined over its base field.326327EXAMPLE::328329sage: F = GF(37)330sage: P2.<X,Y,Z> = ProjectiveSpace(F,2)331sage: C = Curve(X^7+Y*X*Z^5*55+Y^7*12)332sage: len(list(C.rational_points_iterator()))33337334335::336337sage: F = GF(2)338sage: P2.<X,Y,Z> = ProjectiveSpace(F,2)339sage: C = Curve(X*Y*Z)340sage: a = C.rational_points_iterator()341sage: a.next()342(1 : 0 : 0)343sage: a.next()344(0 : 1 : 0)345sage: a.next()346(1 : 1 : 0)347sage: a.next()348(0 : 0 : 1)349sage: a.next()350(1 : 0 : 1)351sage: a.next()352(0 : 1 : 1)353sage: a.next()354Traceback (most recent call last):355...356StopIteration357358::359360sage: F = GF(3^2,'a')361sage: P2.<X,Y,Z> = ProjectiveSpace(F,2)362sage: C = Curve(X^3+5*Y^2*Z-33*X*Y*X)363sage: b = C.rational_points_iterator()364sage: b.next()365(0 : 1 : 0)366sage: b.next()367(0 : 0 : 1)368sage: b.next()369(2*a + 2 : 2*a : 1)370sage: b.next()371(2 : a + 1 : 1)372sage: b.next()373(a + 1 : a + 2 : 1)374sage: b.next()375(1 : 2 : 1)376sage: b.next()377(2*a + 2 : a : 1)378sage: b.next()379(2 : 2*a + 2 : 1)380sage: b.next()381(a + 1 : 2*a + 1 : 1)382sage: b.next()383(1 : 1 : 1)384sage: b.next()385Traceback (most recent call last):386...387StopIteration388389"""390g = self.defining_polynomial()391K = g.parent().base_ring()392from sage.rings.polynomial.all import PolynomialRing393R = PolynomialRing(K,'X')394X = R.gen()395one = K.one_element()396zero = K.zero_element()397398# the point with Z = 0 = Y399try:400t = self.point([one,zero,zero])401yield(t)402except TypeError:403pass404405# points with Z = 0, Y = 1406g10 = R(g(X,one,zero))407if g10.is_zero():408for x in K:409yield(self.point([x,one,zero]))410else:411for x in g10.roots(multiplicities=False):412yield(self.point([x,one,zero]))413414# points with Z = 1415for y in K:416gy1 = R(g(X,y,one))417if gy1.is_zero():418for x in K:419yield(self.point([x,y,one]))420else:421for x in gy1.roots(multiplicities=False):422yield(self.point([x,y,one]))423424def rational_points(self, algorithm="enum", sort=True):425r"""426Return the rational points on this curve computed via enumeration.427428429INPUT:430431- ``algorithm`` (string, default: 'enum') -- the algorithm to432use. Currently this is ignored.433434- ``sort`` (boolean, default ``True``) -- whether the output435points should be sorted. If False, the order of the output436is non-deterministic.437438OUTPUT:439440A list of all the rational points on the curve defined over441its base field, possibly sorted.442443.. note::444445This is a slow Python-level implementation.446447448EXAMPLES::449450sage: F = GF(7)451sage: P2.<X,Y,Z> = ProjectiveSpace(F,2)452sage: C = Curve(X^3+Y^3-Z^3)453sage: C.rational_points()454[(0 : 1 : 1), (0 : 2 : 1), (0 : 4 : 1), (1 : 0 : 1), (2 : 0 : 1), (3 : 1 : 0), (4 : 0 : 1), (5 : 1 : 0), (6 : 1 : 0)]455456457::458459sage: F = GF(1237)460sage: P2.<X,Y,Z> = ProjectiveSpace(F,2)461sage: C = Curve(X^7+7*Y^6*Z+Z^4*X^2*Y*89)462sage: len(C.rational_points())4631237464465::466467sage: F = GF(2^6,'a')468sage: P2.<X,Y,Z> = ProjectiveSpace(F,2)469sage: C = Curve(X^5+11*X*Y*Z^3 + X^2*Y^3 - 13*Y^2*Z^3)470sage: len(C.rational_points())471104472473::474475sage: R.<x,y,z> = GF(2)[]476sage: f = x^3*y + y^3*z + x*z^3477sage: C = Curve(f); pts = C.rational_points()478sage: pts479[(0 : 0 : 1), (0 : 1 : 0), (1 : 0 : 0)]480481"""482points = list(self.rational_points_iterator())483if sort:484points.sort()485return points486487class ProjectiveCurve_prime_finite_field(ProjectiveCurve_finite_field):488def _points_via_singular(self, sort=True):489r"""490Return all rational points on this curve, computed using Singular's491Brill-Noether implementation.492493INPUT:494495496- ``sort`` - bool (default: True), if True return the497point list sorted. If False, returns the points in the order498computed by Singular.499500501EXAMPLE::502503sage: x, y, z = PolynomialRing(GF(5), 3, 'xyz').gens()504sage: f = y^2*z^7 - x^9 - x*z^8505sage: C = Curve(f); C506Projective Curve over Finite Field of size 5 defined by507-x^9 + y^2*z^7 - x*z^8508sage: C._points_via_singular()509[(0 : 0 : 1), (0 : 1 : 0), (2 : 2 : 1), (2 : 3 : 1),510(3 : 1 : 1), (3 : 4 : 1)]511sage: C._points_via_singular(sort=False) #random512[(0 : 1 : 0), (3 : 1 : 1), (3 : 4 : 1), (2 : 2 : 1),513(0 : 0 : 1), (2 : 3 : 1)]514515516.. note::517518The Brill-Noether package does not always work (i.e., the519'bn' algorithm. When it fails a RuntimeError exception is520raised.521"""522f = self.defining_polynomial()._singular_()523singular = f.parent()524singular.lib('brnoeth')525try:526X1 = f.Adj_div()527except (TypeError, RuntimeError), s:528raise RuntimeError, str(s) + "\n\n ** Unable to use the\529Brill-Noether Singular package to\530compute all points (see above)."531532X2 = singular.NSplaces(1, X1)533R = X2[5][1][1]534singular.set_ring(R)535536# We use sage_flattened_str_list since iterating through537# the entire list through the sage/singular interface directly538# would involve hundreds of calls to singular, and timing issues with539# the expect interface could crop up. Also, this is vastly540# faster (and more robust).541v = singular('POINTS').sage_flattened_str_list()542pnts = [self(int(v[3*i]), int(v[3*i+1]), int(v[3*i+2]))543for i in range(len(v)/3)]544# singular always dehomogenizes with respect to the last variable545# so if this variable divides the curve equation, we need to add546# points at infinity547F = self.defining_polynomial()548z = F.parent().gens()[-1]549if z.divides(F):550pnts += [self(1,a,0) for a in self.base_ring()]551pnts += [self(0,1,0)]552# remove multiple points553pnts = list(set(pnts))554if sort:555pnts.sort()556return pnts557558def riemann_roch_basis(self, D):559r"""560Return a basis for the Riemann-Roch space corresponding to561`D`.562563This uses Singular's Brill-Noether implementation.564565INPUT:566567- ``D`` - a divisor568569OUTPUT:570571A list of function field elements that form a basis of the Riemann-Roch space572573EXAMPLE::574575sage: R.<x,y,z> = GF(2)[]576sage: f = x^3*y + y^3*z + x*z^3577sage: C = Curve(f); pts = C.rational_points()578sage: D = C.divisor([ (4, pts[0]), (4, pts[2]) ])579sage: C.riemann_roch_basis(D)580[x/y, 1, z/y, z^2/y^2, z/x, z^2/(x*y)]581582::583584sage: R.<x,y,z> = GF(5)[]585sage: f = x^7 + y^7 + z^7586sage: C = Curve(f); pts = C.rational_points()587sage: D = C.divisor([ (3, pts[0]), (-1,pts[1]), (10, pts[5]) ])588sage: C.riemann_roch_basis(D)589[(-2*x + y)/(x + y), (-x + z)/(x + y)]590591592.. NOTE::593Currently this only works over prime field and divisors supported on rational points.594"""595f = self.defining_polynomial()._singular_()596singular = f.parent()597singular.lib('brnoeth')598try:599X1 = f.Adj_div()600except (TypeError, RuntimeError), s:601raise RuntimeError, str(s) + "\n\n ** Unable to use the Brill-Noether Singular package to compute all points (see above)."602X2 = singular.NSplaces(1, X1)603# retrieve list of all computed closed points (possibly of degree >1)604v = X2[3].sage_flattened_str_list() # We use sage_flattened_str_list since iterating through605# the entire list through the sage/singular interface directly606# would involve hundreds of calls to singular, and timing issues with607# the expect interface could crop up. Also, this is vastly608# faster (and more robust).609v = [ v[i].partition(',') for i in range(len(v)) ]610pnts = [ ( int(v[i][0]), int(v[i][2])-1 ) for i in range(len(v))]611# retrieve coordinates of rational points612R = X2[5][1][1]613singular.set_ring(R)614v = singular('POINTS').sage_flattened_str_list()615coords = [self(int(v[3*i]), int(v[3*i+1]), int(v[3*i+2])) for i in range(len(v)/3)]616# build correct representation of D for singular617Dsupport = D.support()618Dcoeffs = []619for x in pnts:620if x[0] == 1:621Dcoeffs.append(D.coefficient(coords[x[1]]))622else:623Dcoeffs.append(0)624Dstr = str(tuple(Dcoeffs))625G = singular(','.join([str(x) for x in Dcoeffs]), type='intvec')626# call singular's brill noether routine and return627T = X2[1][2]628T.set_ring()629LG = G.BrillNoether(X2)630LG = [X.split(',\n') for X in LG.sage_structured_str_list()]631x,y,z = self.ambient_space().coordinate_ring().gens()632vars = {'x':x, 'y':y, 'z':z}633V = [(sage_eval(a, vars)/sage_eval(b, vars)) for a, b in LG]634return V635636def rational_points(self, algorithm="enum", sort=True):637r"""638INPUT:639640641- ``algorithm`` - string:642643- ``'enum'`` - straightforward enumeration644645- ``'bn'`` - via Singular's brnoeth package.646647648EXAMPLE::649650sage: x, y, z = PolynomialRing(GF(5), 3, 'xyz').gens()651sage: f = y^2*z^7 - x^9 - x*z^8652sage: C = Curve(f); C653Projective Curve over Finite Field of size 5 defined by654-x^9 + y^2*z^7 - x*z^8655sage: C.rational_points()656[(0 : 0 : 1), (0 : 1 : 0), (2 : 2 : 1), (2 : 3 : 1),657(3 : 1 : 1), (3 : 4 : 1)]658sage: C = Curve(x - y + z)659sage: C.rational_points()660[(0 : 1 : 1), (1 : 1 : 0), (1 : 2 : 1), (2 : 3 : 1),661(3 : 4 : 1), (4 : 0 : 1)]662sage: C = Curve(x*z+z^2)663sage: C.rational_points('all')664[(0 : 1 : 0), (1 : 0 : 0), (1 : 1 : 0), (2 : 1 : 0),665(3 : 1 : 0), (4 : 0 : 1), (4 : 1 : 0), (4 : 1 : 1),666(4 : 2 : 1), (4 : 3 : 1), (4 : 4 : 1)]667668.. note::669670The Brill-Noether package does not always work (i.e., the671'bn' algorithm. When it fails a RuntimeError exception is672raised.673"""674if algorithm == "enum":675676return ProjectiveCurve_finite_field.rational_points(self,677algorithm="enum",678sort=sort)679680elif algorithm == "bn":681682return self._points_via_singular(sort=sort)683684elif algorithm == "all":685686S_enum = self.rational_points(algorithm = "enum")687S_bn = self.rational_points(algorithm = "bn")688if S_enum != S_bn:689raise RuntimeError, "Bug in rational_points -- different\690algorithms give different answers for\691curve %s!"%self692return S_enum693694else:695696raise ValueError, "No algorithm '%s' known"%algorithm697698def Hasse_bounds(q, genus=1):699r"""700Return the Hasse-Weil bounds for the cardinality of a nonsingular701curve defined over `\GF{q}` of given ``genus``.702703INPUT:704705- ``q`` (int) -- a prime power706707- ``genus`` (int, default 1) -- a non-negative integer,708709OUTPUT:710711(tuple) The Hasse bounds (lb,ub) for the cardinality of a curve of712genus ``genus`` defined over `\GF{q}`.713714EXAMPLES::715716sage: Hasse_bounds(2)717(1, 5)718sage: Hasse_bounds(next_prime(10^30))719(999999999999998000000000000058, 1000000000000002000000000000058)720"""721if genus==1:722rq = (4*q).isqrt()723else:724rq = (4*(genus**2)*q).isqrt()725return (q+1-rq,q+1+rq)726727728729