|
A.1.2 Writing procedures and libraries
The user may add their own commands to the commands already available in
SINGULAR by writing SINGULAR procedures. There are basically
two kinds of procedures:
- procedures written in the SINGULAR programming language (which are
usually collected in SINGULAR libraries).
- procedures written in C/C++ (collected in dynamic modules).
At this point, we restrict ourselves to describing the first kind of
(library) procedures, which are sufficient for most applications. The
syntax and general structure of a library (procedure) is described in
Procedures, and Libraries.
The probably most efficient way of
writing a new library is to use one of the official SINGULAR
libraries, say ring.lib as a sample. On a Unix-like operating
system, type LIB "ring.lib"; to get information on where the
libraries are stored on your disk.
SINGULAR provides several commands and tools, which may be useful
when writing a procedure, for instance, to have a look at intermediate
results (see Debugging tools).
If such a library should be contributed to SINGULAR some formal
requirements are needed:
-
the library header must explain the purpose of the library and
(for non-trivial algorithm) a pointer to the algorithm (text book, article, etc.)
-
all global procedures must have a help string and an example which shows its usage.
-
it is strongly recommend also to provide test scripts which test the functionality: one should test the essential functionality of the
library/command in a relatively short time (say, in no more than 30s),
other tests should check the functionality of the
library/command in detail so that, if possible, all relevant
cases/results are tested. Nevertheless, such a test should not run
longer than, say, 10 minutes.
We give short examples of procedures to demonstrate the following:
-
Write procedures which return an integer (ring independent), see also
Milnor and Tjurina number. (Here we restrict ourselves to the main
body of the procedures).
-
The procedure
milnorNumber must be called with one parameter, a
polynomial.
The name g is local to the procedure and is killed automatically when
leaving the procedure.
milnorNumber returns the Milnor number (and displays a comment).
-
The procedure
tjurinaNumber has no specified number of
parameters. Here, the parameters are referred to by #[1] for
the 1st, #[2] for the 2nd parameter, etc.
tjurinaNumber returns the Tjurina number (and displays a comment).
-
the procedure
milnor_tjurina which returns a list consisting of two
integers, the Milnor and the Tjurina number.
-
Write a procedure which creates a new ring and returns data dependent on
this new ring (two numbers) and an int. In this example, we also show
how to write a help text for the procedure (which is optional, but
recommended).
| proc milnorNumber (poly g)
{
"Milnor number:";
return(vdim(std(jacob(g))));
}
proc tjurinaNumber
{
"Tjurina number:";
return(vdim(std(jacob(#[1])+#[1])));
}
proc milnor_tjurina (poly f)
{
ideal j=jacob(f);
list L=vdim(std(j)),vdim(std(j+f));
return(L);
}
proc real_sols (number b, number c)
"USAGE: real_sols (b,c); b,c number
ASSUME: active basering has characteristic 0
RETURN: list: first entry is an integer (the number of different real
solutions). If this number is non-negative, the list has as second
entry a ring in which the list SOL of real solutions of x^2+bx+c=0
is stored (as floating point number, precision 30 digits).
NOTE: This procedure calls laguerre_solve from solve.lib.
"
{
def oldring = basering; // assign name to the ring active when
// calling the procedure
number disc = b^2-4*c;
if (disc>0) { int n_of_sols = 2; }
if (disc==0) { int n_of_sols = 1; }
string s = nameof(var(1)); // name of first ring variable
if (disc>=0) {
execute("ring rinC =(complex,30),("+s+"),lp;");
if (not(defined(laguerre_solve))) { LIB "solve.lib"; }
poly f = x2+imap(oldring,b)*x+imap(oldring,c);
// f is a local ring-dependent variable
list SOL = laguerre_solve(f,30);
export SOL; // make SOL a global ring-dependent variable
// such variables are still accessible when the
// ring is among the return values of the proc
setring oldring;
return(list(n_of_sols,rinC));
}
else {
return(list(0));
}
}
//
// We now apply the procedures which are defined by the
// lines of code above:
//
ring r = 0,(x,y),ds;
poly f = x7+y7+(x-y)^2*x2y2;
milnorNumber(f);
==> Milnor number:
==> 28
tjurinaNumber(f);
==> Tjurina number:
==> 24
milnor_tjurina(f); // a list containing Milnor and Tjurina number
==> [1]:
==> 28
==> [2]:
==> 24
def L=real_sols(2,1);
L[1]; // number of real solutions of x^2+2x+1
==> 1
def R1=L[2];
setring R1;
listvar(R1); // only global ring-dependent objects are still alive
==> // R1 [0] *ring
==> // SOL [0] list, size: 2
SOL; // the real solutions
==> [1]:
==> -1
==> [2]:
==> -1
setring r;
L=real_sols(1,1);
L[1]; // number of reals solutions of x^2+x+1
==> 0
setring r;
L=real_sols(1,-5);
L[1]; // number of reals solutions of x^2+x-5
==> 2
def R3=L[2];
setring R3; SOL; // the real solutions
==> [1]:
==> -2.79128784747792000329402359686
==> [2]:
==> 1.79128784747792000329402359686
|
Writing a dynamic module is not as simple as writing a library
procedure, since it does not only require some knowledge of C/C++, but
also about the way the SINGULAR kernel works.
See also Dynamic modules.
|