Home Online Manual
Top
Back: Basic programming
Forward: Rings associated to monomial orderings
FastBack: Examples
FastForward: Computing Groebner and Standard Bases
Up: Programming
Top: Singular Manual
Contents: Table of Contents
Index: Index
About: About this document

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.