Using Singular functions from SAGE is not much different conceptually than using GAP functions from SAGE. This can range from very easy to hard, depending on how much of the data structure of the output of the Singular function is already present in SAGE.
First, some terminology. For us, a curve
over a finite field
is a
equation of the form
, where
is a polynomial. It may or
may not be singular. A place of degree
is a Galois orbit of
points in
, where
is degree
. For example, a place of degree
is also a place of degree
, but a place of degree
is not since no degree
extension of
contains a degree
extension. Places of degree
are
also called
-rational points.
As an example of the SAGE-Singular interface, we will explain how to wrap
Singular's NSplaces
, which computes places on a curve over a finite
field. (The command closed_points
also does this in some cases.) This is
``easy'' since no new Python classes are needed in SAGE to carry this out.
Here's an example of how to use this command in Singular:
A Computer Algebra System for Polynomial Computations / version 3-0-0 0< by: G.-M. Greuel, G. Pfister, H. Schoenemann \ May 2005 FB Mathematik der Universitaet, D-67653 Kaiserslautern \ > LIB "brnoeth.lib"; [...] > ring s=5,(x,y),lp; > poly f=y^2-x^9-x; > list X1=Adj_div(f); Computing affine singular points ... Computing all points at infinity ... Computing affine singular places ... Computing singular places at infinity ... Computing non-singular places at infinity ... Adjunction divisor computed successfully The genus of the curve is 4 > list X2=NSplaces(1,X1); Computing non-singular affine places of degree 1 ... > list X3=extcurve(1,X2); Total number of rational places : 6 > def R=X3[1][5]; > setring R; > POINTS; [1]: [1]: 0 [2]: 1 [3]: 0 [2]: [1]: -2 [2]: 1 [3]: 1 [3]: [1]: -2 [2]: 1 [3]: 1 [4]: [1]: -2 [2]: -1 [3]: 1 [5]: [1]: 2 [2]: -2 [3]: 1 [6]: [1]: 0 [2]: 0 [3]: 1
Here's one way of doing this same calculation in the SAGE interface to Singular:
sage: singular.LIB("brnoeth.lib") sage: singular.ring(5,'(x,y)','lp') // characteristic : 5 // number of vars : 2 // block 1 : ordering lp // : names x y // block 2 : ordering C sage: f = singular('y^2-x^9-x') sage: print singular.eval("list X1=Adj_div(%s);"%f.name()) Computing affine singular points ... Computing all points at infinity ... Computing affine singular places ... Computing singular places at infinity ... Computing non-singular places at infinity ... Adjunction divisor computed successfully <BLANKLINE> The genus of the curve is 4 sage: print singular.eval("list X2=NSplaces(1,X1);") Computing non-singular affine places of degree 1 ... sage: print singular.eval("list X3=extcurve(1,X2);") <BLANKLINE> Total number of rational places : 6 <BLANKLINE> sage: singular.eval("def R=X3[1][5];") '' sage: singular.eval("setring R;") '' sage: L = singular.eval("POINTS;")
sage: print L [1]: [1]: 0 [2]: 1 [3]: 0 [2]: [1]: 0 [2]: 0 [3]: 1 [3]: [1]: -2 [2]: 1 [3]: 1 [4]: [1]: 2 [2]: -2 [3]: 1 [5]: [1]: 2 [2]: 2 [3]: 1 [6]: [1]: -2 [2]: -1 [3]: 1
>From looking at the output, notice that our wrapper program will need to
parse the string represented by
above.
Let us write a separate program to do just that. This requires figuring out how to determine where the coordinates of the points are placed in the string L. Python has some very useful string manipulation commands to do just that.
def points_parser(string_points,F): """ This program will parse a string of points of X over a finite field F returned by Singular's NSplaces command into a Python list of points with entries from F. EXAMPLE sage: F = GF(5) sage: points_parser(L,F) ((0, 1, 0), (3, 4, 1), (0, 0, 1), (2, 3, 1), (3, 1, 1), (2, 2, 1)) """ Pts=[] n=len(L) #print n #start block to compute a pt L1=L while len(L1)>32: idx=L1.index(" ") pt=[] ## start block1 for compute pt idx=L1.index(" ") idx2=L1[idx:].index("\n") L2=L1[idx:idx+idx2] #print L2 pt.append(F(eval(L2))) # end block1 to compute pt L1=L1[idx+8:] # repeat block 2 more times #print len(L1) ## start block2 for compute pt idx=L1.index(" ") idx2=L1[idx:].index("\n") L2=L1[idx:idx+idx2] pt.append(F(eval(L2))) # end block2 to compute pt L1=L1[idx+8:] # repeat block 1 more time ## start block3 for compute pt idx=L1.index(" ") if "\n" in L1[idx:]: idx2=L1[idx:].index("\n") else: idx2=len(L1[idx:]) L2=L1[idx:idx+idx2] pt.append(F(eval(L2))) #print pt # end block3 to compute pt #end block to compute a pt Pts.append(tuple(pt)) # repeat until no more pts L1=L1[idx+8:] # repeat block 2 more times return tuple(Pts)
Now it is an easy matter to put these ingredients together into a SAGE function
which takes as input a triple
: finite field
of prime order,
a polynomial
in
defining
(note the variables
must be used), and the degree
. The output is the number of points of places
in
of degree
over
. At the moment, their is no ``translation''
between elements of
in Singular and SAGE unless
. So, for this
reason, we restrict ourselves to points of degree one.
def places_on_curve(f,F): """ INPUT: f -- element of F[x,y], defining X: f(x,y)=0 F -- a finite field of *prime order* OUTPUT: integer -- the number of places in X of degree d=1 over F EXAMPLES: sage: F=GF(5) sage: R=MPolynomialRing(F,2,names=["x","y"]) sage: x,y=R.gens() sage: f=y^2-x^9-x sage: places_on_curve(f,F) ((0, 1, 0), (3, 4, 1), (0, 0, 1), (2, 3, 1), (3, 1, 1), (2, 2, 1)) """ d = 1 p = F.characteristic() singular.eval('LIB "brnoeth.lib";') singular.eval("ring s="+str(p)+",(x,y),lp;") singular.eval("poly f="+str(f)) singular.eval("list X1=Adj_div(f);") singular.eval("list X2=NSplaces("+str(d)+",X1);") singular.eval("list X3=extcurve("+str(d)+",X2);") singular.eval("def R=X3[1][5];") singular.eval("setring R;") L = singular.eval("POINTS;") return points_parser(L,F)
Note also that the ordering returned by this SAGE function is exactly the same
as the ordering in the Singular variable POINTS
.
Some examples:
sage: F = GF(5) sage: R = MPolynomialRing(F,2,names = ["x","y"]) sage: x,y = R.gens() sage: f = y^2-x^9-x sage: places_on_curve(f,F) ((0, 1, 0), (3, 4, 1), (0, 0, 1), (2, 3, 1), (3, 1, 1), (2, 2, 1)) sage: F = GF(2) sage: R = MPolynomialRing(F,2,names = ["x","y"]) sage: x,y = R.gens() sage: f = x^3*y+y^3+x sage: places_on_curve(f,F) ((0, 1, 0), (1, 0, 0), (0, 0, 1))
sage: singular.lib('brnoeth.lib') sage: R = singular.ring(5, '(x,y)', 'lp') sage: f = singular.new('y^2 - x^9 - x') sage: X1 = f.Adj_div() sage: X2 = singular.NSplaces(1, X1) sage: X3 = singular.extcurve(1, X2) sage: R = X3[1][5] sage: singular.set_ring(R) sage: L = singular.new('POINTS')
sage: [(L[i][1], L[i][2], L[i][3]) for i in range(1,7)] [(0, 1, 0), (-2, 1, 1), (0, 0, 1), (2, 2, 1), (-2, -1, 1), (2, -2, 1)]
name
method of a Singular object returns the name
of that object in the Singular interpreter, so that it can
be used as input to a Singular function.
Next we implement the general function (we omit the docstring, which
is the same as above). Note that the point_parser
function
is not required.
def places_on_curve(f,F): p = F.characteristic() if F.degree() > 1: raise NotImplementedError singular.lib('brnoeth.lib') R = singular.ring(5, '(x,y)', 'lp') f = singular.new('y^2 - x^9 - x') X1 = f.Adj_div() X2 = singular.NSplaces(1, X1) X3 = singular.extcurve(1, X2) R = X3[1][5] singular.setring(R) L = singular.new('POINTS') return [(int(L[i][1]), int(L[i][2]), int(L[i][3])) \ for i in range(1,int(L.size())+1)]
This code is much shorter, nice, and more readable. However, it depends
on certain functions, e.g., singular.setring
having been implemented
in the SAGE/Singular interface, whereas the code in the previous section
used only the barest minimum of that interface.
a
of
BrillNoether
function to compute bases of Riemann-Roch spaces.
This would not require writing some new Python objects.
groebner
function to compute Groebner bases.
This would not require writing some new Python objects.
See About this document... for information on suggesting changes.