MuPAD - Dynamic Modules [1]

Using the NAGC Library in MuPAD

Andreas Sorgatz (andi@uni-paderborn.de) - 30. Nov. 1996

The NAG Library is a widely known package for numerical calculations. First versions were only available for FORTRAN, but newer ones are also available as C libraries (NAGC). With this, NAG can be integrated into MuPAD by using dynamic modules. Now one can combine the power of the NAGC library with the strength of the symbolic computer algebra system MuPAD (symbolic computations) to solve mathematical problems. MuPAD provides a flexible and comfortable interface to your NAG algorithms.
[About] -- [Demo1] -- [Demo2] -- [mnag.h] -- [Availability]

About mnag.h

mnag.h is a small header file, which makes it easy to use the NAGC library in a MuPAD module. It provides conversion routines (MuPAD <-> NAGC) for the basic data types of NAGC and supports a MuPAD-like error handling. This interface was tested on the following plattforms. Further ports are planned.

SunSPARC (Solaris 2.5).

A Demonstration Of The NAGC Interface

NAGdemo1: The Random Number Generator

Figure 1 shows the source code of the dynamic module NAGdemo1, which performs an interface to the NAGC random number generator (g05cbc, g05cac). The function "new" of this module gets a first Integer to initialize the generator and a second one to specify the number of random values the function shall return in a list.

Fig. 1: NAGdemo1: The Source Code

/******************************************************************************/
/* FILE   : NAGdemo1.C                                                        */
/* CONTENT: The function "new" returns a list of five random values.          */
/* AUTHOR : Andreas Sorgatz (andi@uni-paderborn.de)                           */
/* CREATED: 30/11/96                                                          */
/* CHANGED: 30/11/96                                                          */
/* RELEASE: MuPAD 1.3, NAGC Mark 3                                            */
/* SYSTEMS: Solaris 2.5                                                       */
/*                                                                            */
/* This simple NAGdemo1 demonstrates how NAG functions can be used in a MuPAD */
/* dynamic module. For conversions of basic NAG data types (Boolean, Complex, */
/* double and Integer) between  NAG <--> MuPAD,  the functions MNboolean, ... */
/* can be used.                                                               */
/******************************************************************************/

////////////////////////////////////////////////////////////////////////////////
// Module Generator Options: ///////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Define include and library path as well as libraries to be linked. //////////
//
MMG( coption = "-I/usr/local/MATH/NAGC/include" )
MMG( loption = "-L/usr/local/MATH/NAGC -lnagc -lm /usr/lib/libc.so.1" );

// Tell MuPAD not to take the default linker call, but this one. ///////////////
//
MMG( linker  = "ld -G" )

// Include this information string, which will be printed by 'info()'. /////////
//
MMG( info    = "Using the NAG-Library in MuPAD, Demo-1, random numbers" )

// Include the MuPAD/NAG interface /////////////////////////////////////////////
//
#include "mnag.h"


////////////////////////////////////////////////////////////////////////////////
// Include NAG header files according to the functions that are used. NOTE: ////
// The  'extern "C" { ... }'  statement is used because the NAG Library is  ////
// an ANSI-C library but not a C++ library!                                 ////
////////////////////////////////////////////////////////////////////////////////
//
extern "C" {
#include <Nag/nagg05.h>
}

// The module function /////////////////////////////////////////////////////////
//
MFUNC( new, MCnop )                 // NAGdemo1( init_value, num_of_value )
{
    Integer i, num;                 // NAG data type
  
    MFnargsCheck(2);                // exactly two arguments are expected
    MFargCheck(1,DOM_INT);          // range := 0..MFarg(1)
    MFargCheck(2,DOM_INT);          // number:= MFarg(2) random values 

    num = MNinteger( MFarg(2) );    // converts DOM_INT to Integer

    g05cbc( MNinteger(MFarg(1)) );  // initializes the random number generator

    MTcell list = MFnewList(num);   // creates a MuPAD list

    for( i=0; i < num; i++ )        // fills the list with random MuPAD numbers
    {
        MFsetList( &list, i, MNdouble(g05cac()) );
    }
    MFsig(list);                    // calculates the signature of the list
    MFreturn(list);                 // returns the MuPAD list
} MFEND


MFUNC( initmod, MCnop )             // An introduction for the user.
{
    MFprintf( "Usage: NAGdemo1( init_value, num_of_value );           \n" );
    MFreturn( MFcopy(MVnull) );
} MFEND

Figure 2 shows how the dynamic module is created and used in a MuPAD session.

Fig. 2: NAGdemo1: Creation And Usage

andi> mmg -v NAGdemo1.C

MMG -- MuPAD-Module-Generator -- V-1.3.0  Oct.96
Mesg.: Scanning source file ... 
Mesg.: 2 function(s) and 4 option(s) found in 91 lines
Mesg.: Module contains a user defined init function
Mesg.: Creating  extended module code ... 
Mesg.: Compiling extended module code ... 

CC -pic -G -DSOLARIS -c MMGNAGdemo1.C -o NAGdemo1.o 
   -DPARI_C_PLUSPLUS -DLONG_IS_32BIT -DPOINTER_IS_32BIT 
   -I/wiwianka/user/andi/MuPAD/SOURCE/DEV/share/mmg/include/kernel 
   -I/wiwianka/user/andi/MuPAD/SOURCE/DEV/share/mmg/include/pari 
   -I/wiwianka/user/andi/MuPAD/SOURCE/DEV/share/mmg/include/mmt 
   -I/usr/local/MATH/NAGC/include

Mesg.: Linking dynamic module ... 

ld -G -o NAGdemo1.mdm NAGdemo1.o 
   -L/wiwianka/user/andi/MuPAD/SOURCE/DEV/solaris/lib 
   -L/usr/local/MATH/NAGC -lnagc -lm /usr/lib/libc.so.1

Mesg.: Ok


>> module(NAGdemo1): Usage: NAGdemo1( init_value, num_of_value ); >> NAGdemo1(42,4); [0.585542118, 0.1859650411, 0.55882297, 0.1279809965] >> NAGdemo1(17,2); [0.8293408721, 0.9001032522]

NAGdemo2: The Roots Of A Complex Polynomial

Figure 3 shows the source code of the dynamic module NAGdemo2, which performs an interface to the NAGC function c02afc. The function "new" of this module gets a complex polynomial and returns a list of approximations for all of its roots.

Fig. 3: NAGdemo2: The Source Code

/******************************************************************************/
/* FILE   : NAGdemo2.C                                                        */
/* CONTENT: The function "roots" finds all the roots of a complex polynomial  */
/* AUTHOR : Andreas Sorgatz (andi@uni-paderborn.de)                           */
/* CREATED: 30/11/96                                                          */
/* CHANGED: 30/11/96                                                          */
/* RELEASE: MuPAD 1.3, NAGC Mark 3                                            */
/* SYSTEMS: Solaris 2.5                                                       */
/******************************************************************************/

////////////////////////////////////////////////////////////////////////////////
// Module Generator Options: ///////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

// Define include and library path as well as libraries to be linked. //////////
//
MMG( coption = "-I/usr/local/MATH/NAGC/include" )
MMG( loption = "-L/usr/local/MATH/NAGC -lnagc -lm /usr/lib/libc.so.1" );

// Tell MuPAD not to take the default linker call, but this one. ///////////////
//
MMG( linker  = "ld -G" )

// Include this information string, which will be printed by 'info()'. /////////
//
MMG( info    = "Using the NAG-Library in MuPAD, Demo-2, roots of a polynomial" )

// Include the MuPAD/NAG interface /////////////////////////////////////////////
//
#include "mnag.h"


////////////////////////////////////////////////////////////////////////////////
// Include NAG header files according to the functions that are used. NOTE: ////
// The  'extern "C" { ... }'  statement is used because the NAG Library is  ////
// an ANSI-C library but not a C++ library!                                 ////
////////////////////////////////////////////////////////////////////////////////
extern "C" {
#include <Nag/nagc02.h>
}

// The module function /////////////////////////////////////////////////////////
//
MFUNC( new, MCnop )                 // NAGdemo2( polynomial )
{
    MFnargsCheck(1);                
    MFargCheck(1,DOM_POLY);         

    // Check if the polynomial is univariate.
    //
    if( MFnops(MFop(MFarg(1),1)) != 1 )
        MFerror( "Univariate polynomial expected" );

    // Convert the MuPAD polynomial into a dense list representation. 
    // This makes further conversions easier. 
    //
    MTcell  polylist = MFcall( "poly2list", 1, MFcopy(MFarg(1)) );
    long    polyleng = MFnops( polylist );

    if( polylist == 0 ) 
        MFerror( "Polynomial must not be zero" );

    // Get the degree of the polynomial.
    // 
    Integer degree = MNinteger( MFop(MFop(polylist,0),1) );

    if( degree == 0 ) {
        MFfree( polylist );
        MFerror( "Degree of the polynomial must greater than zero" );
    }

    // Create a NAG (sparse) complex polynomial.
    //
    Complex* poly = (Complex*) MFcmalloc( (degree+1) * sizeof(Complex) );

    if( poly == NULL ) {
        MFfree( polylist );
        MFerror( "Not enough memory" );
    }

    MTcell   Mmonom, Mcoeff;
    Integer  copos = degree, expo;
    Complex* coeff = poly;

    for( long i = 0; i < polyleng; i++ ) {
        Mmonom = MFop( polylist, i );
        Mcoeff = MFcall( "float", 1, MFcopy(MFop(Mmonom,0)) );
        expo   = MNinteger( MFop(Mmonom,1) );

        while( copos > expo ) { 
            copos--; coeff++; coeff->re = 0.0; coeff->im = 0.0; 
        }
        if( MFisFloat(Mcoeff) ) {
            coeff->re = MNdouble( Mcoeff );    coeff->im = 0.0;
        } else 
        if( MFisComplex(Mcoeff) ) {
            *coeff = MNcomplex( Mcoeff );
        } else {
            MFerror( "Numeric element expected [MNcomplexList]" );
        }
        MFfree( Mcoeff );
    }
    while( --copos >= 0 ) {
        coeff++; coeff->re = 0.0; coeff->im = 0.0; 
    }
    MFfree( polylist );

    // Find all the complex roots of the given polynomial
    //
    Complex* root = (Complex*) MFcmalloc( (degree  ) * sizeof(Complex) );
    if( root == NULL ) {
        MFcfree( poly );
        MFerror( "Not enough memory" );
    }
    c02afc( degree, poly, TRUE, root, MNfail() );
    MFcfree( poly );

    // Return the result as a MuPAD list.
    //
    MTcell result = MNcomplexList( root, degree );
    MFcfree( root );
    
    MFreturn( result );
} MFEND


MFUNC( initmod, MCnop )             // An introduction for the user.
{
    MFprintf( "Usage: NAGdemo2( complex_polynomial ); \n" );
    MFreturn( MFcopy(MVnull) );
} MFEND

Figure 4 shows how the dynamic module is created and used in a MuPAD session.

Fig. 4: NAGdemo2: Creation And Usage

andi> mmg -v NAGdemo2.C

MMG -- MuPAD-Module-Generator -- V-1.3.0  Oct.96
Mesg.: Scanning source file ... 
Mesg.: 2 function(s) and 4 option(s) found in 141 lines
Mesg.: Module contains a user defined init function
Mesg.: Creating  extended module code ... 
Mesg.: Compiling extended module code ... 

CC -pic -G -DSOLARIS -c MMGNAGdemo2.C -o NAGdemo2.o 
   -DPARI_C_PLUSPLUS -DLONG_IS_32BIT -DPOINTER_IS_32BIT 
   -I/wiwianka/user/andi/MuPAD/SOURCE/DEV/share/mmg/include/kernel 
   -I/wiwianka/user/andi/MuPAD/SOURCE/DEV/share/mmg/include/pari 
   -I/wiwianka/user/andi/MuPAD/SOURCE/DEV/share/mmg/include/mmt 
   -I/usr/local/MATH/NAGC/include

Mesg.: Linking dynamic module ... 

ld -G -o NAGdemo2.mdm NAGdemo2.o 
   -L/wiwianka/user/andi/MuPAD/SOURCE/DEV/solaris/lib 
   -L/usr/local/MATH/NAGC -lnagc -lm /usr/lib/libc.so.1

Mesg.: Ok


>> module(NAGdemo2): Usage: NAGdemo2( complex_polynomial ); >> NAGdemo2( poly(2*x^3+5*x^2+3) ); [- 2.705001171 + 5.551115123e-17 I, 0.1025005859 - 0.7375784977 I, 0.1025005859 + 0.7375784977 I]

The Header File mnag.h

This interface still has an experimental state and it is incomplete. Further functions for conversions of NAG/MuPAD objects like lists, polynomials and arrays are planned ... when I have a bit more time for this.

Fig. 5: The Header File mnag.h

/******************************************************************************/
/* FILE   : mnag.h                                                            */
/* CONTENT: The MuPAD dynamic module interface to the NAG C Library           */
/* AUTHOR : Andreas Sorgatz (andi@uni-paderborn.de)                           */
/* CREATED: 29/11/96                                                          */
/* CHANGED: 30/11/96                                                          */
/* RELEASE: MuPAD 1.3, NAGC Mark 3                                            */
/* SYSTEMS: Solaris 2.5                                                       */
/*                                                                            */
/******************************************************************************/

// Undefines some conflicting MuPAD/PARI definements. //////////////////////////
//
#undef zero                    // Internal PARI definement
#undef lmax                    // Internal PARI definement
#undef debug                   // Internal MDM  definement


// NAG header files: On UNIX systems in general the prefix 'Nag/' must be used.
// To suppress this prefix, the option -DNOPREFIX can be used.
//
extern "C" {
#if (defined MACINTOSH) || (defined MSDOS) || (defined NOPREFIX)
#  include 
#  include 
#  include 
#  include 
#else
#  include 
#  include 
#  include 
#  include 
#endif
}


// Splits a NAG complex into its real and imagine part. ////////////////////////
//
#define MNcplx(CPLX)          CPLX.re, CPLX.im


////////////////////////////////////////////////////////////////////////////////
// Definitions for NAG and MuPAD error handling. ///////////////////////////////
////////////////////////////////////////////////////////////////////////////////

void MNerror( char *str, int code, char *name ) 
{ 
    static char buf[NAG_ERROR_BUF_LEN+512];

    if( code != 0 ) {
       sprintf( buf, "In NAG function \"%s\", reason: %d=%s", name, code, str );
       MFerror( buf ); 
    }
}

NagError* MNfail()
{
    static NagError fail; 

    SET_FAIL(fail);
    fail.print   = 0;
    fail.handler = MNerror;
    return( &fail );
}


////////////////////////////////////////////////////////////////////////////////
// Converts basic data types between the NAG and MuPAD representation. /////////
////////////////////////////////////////////////////////////////////////////////

inline Integer  MNinteger( MTcell  Mint ) { return( MFlong(Mint) ); }
inline MTcell   MNinteger( Integer Nint ) { return( MFlong(Nint) ); }

inline double   MNdouble ( MTcell  Mdbl ) { return( MFdouble(Mdbl) ); }
inline MTcell   MNdouble ( double  Ndbl ) { return( MFdouble(Ndbl) ); }

inline Boolean MNboolean( MTcell Mboo ) 
{ 
    if( MFisTrue (Mboo) ) return( 1 );
    if( MFisFalse(Mboo) ) return( 0 );
    MFerror( "Conversion failed: TRUE or FALSE expected" ); 
}
inline MTcell MNboolean( Boolean Nboo ) 
{ 
    if( Nboo ) return( MFcopy(MVtrue)  );
    else       return( MFcopy(MVfalse) ); 
}

inline Complex MNcomplex( MTcell Mcpx ) 
{
    Complex  Ncpx;

    Ncpx.re = MNdouble( MFcomplexRe(Mcpx) ); 
    Ncpx.im = MNdouble( MFcomplexIm(Mcpx) ); 
    return( Ncpx );
}       
inline MTcell MNcomplex( Complex Ncpx ) 
{
    return( MFcomplex(MNdouble(Ncpx.re),MNdouble(Ncpx.im)) );
}


////////////////////////////////////////////////////////////////////////////////
// Converts vectors between the NAG and MuPAD representation. //////////////////
////////////////////////////////////////////////////////////////////////////////

inline MTcell MNcomplexList( Complex* clist, Integer length )
{
    MTcell mlist = MFnewList( length );

    for( Integer i = 0; i < length; i++ ) 
        MFsetList( &mlist, i, MNcomplex(*clist++) );
    MFsig( mlist );
    return( mlist );
}
inline Complex* MNcomplexList( MTcell mlist )
{
    Integer  length = MFnops(mlist);
    Complex* clist  = (Complex*) MFcmalloc( length*sizeof(Complex) );

    if( clist == NULL )
        MFerror( "Not enough memory" );

    MTcell flist = MFcall( "float", 1, MFcopy(mlist) );
    MTcell elem;

    for( Integer i = 0; i < length; i++ ) {
        if( MFisFloat(elem=MFop(flist,i)) ) {
            clist[i].re = MNdouble(elem);
            clist[i].im = 0.0;
        } else 
        if( MFisComplex(elem) ) {
            clist[i] = MNcomplex(elem);
        } else {
            MFerror( "Numeric element expected [MNcomplexList]" );
        }
    }
    MFfree( flist );
    return( clist );
}

Appendix

The header file mnag.h will be available for the MuPAD release 1.3.0. Please refer to the contrib directory of any official MuPAD ftp site.

Bibliography

[1] Dynamische Module, A. Sorgatz, MuPAD Reports, Teubner Verlag, Stuttgart, Oktober 1996. http://math-www.uni-paderborn.de/MuPAD/BIB/sorgatz96.html
[2] Integration of Magnum in MuPAD
[3] How to create MuPAD scanners with (f)lex


Author: Andreas Sorgatz (andi@uni-paderborn.de)
Last update: 16. Dec. 1996