(The first version of this chapter was written by David Joyner.)
Using Singular functions from Sage is not much different conceptually than using GAP functions from Sage. As with GAP, this can range from 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 an
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 function will need to
parse the string represented by
above, so
let us write a separate function 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 function 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
:
a polynomial
in
defining
(note that the variables
must be used),
a finite field
of prime order,
and the degree
. The output is the number of places
in
of degree
over
. At the moment, there 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)
POINTS.
One more example (in addition to the one in the docstring):
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))
See About this document... for information on suggesting changes.