This is a brief description of these extensions and their usage. It is not intended to explain the specific implementation details of the extensions.
In an extra section we will give some hints for the adaptation of the BIAS IEEE version to non-supported architectures. For this section some experience with C and good knowledge about the architecture (that is how to switch the rounding mode of the floating point unit) is required.
To install the extensions, change to the PROFIL directory and copy the package file into this directory. The package file is called profext.tar.Z under Unix systems and profext.tgz under DOS or OS/2. To unpack the compressed package archive, type
zcat profext.tar.Z | tar xf -
under Unix or
gzip -cd profext.tgz | tar xf -
under DOS or OS/2. After that the compressed package (profext.tar.Z or profext.tgz) may be deleted.
Type cd Tools to change to the directory containing the extensions. To build all libraries, type
dmake architecture
where architecture is the same value used for building PROFIL. After the dmake finishes, the following libraries have been built:
ProfilLIN
ProfilNLI
ProfilMISC
If _test is appended to the above build command, additionally a set of test routines is also built. These test routines perform a simple check of some of the new functions. Under Unix, they are executed automatically, but under DOS or OS/2 you have to start them explicitly. If the test routines terminate silently, the test succeeded. Otherwise, some information is given.
Due to limitations in file name length on some systems, some of the
file names mentioned below may be different on your system. If any differences
occur, those can be identified in the file names where the first name
in each line corresponds to the name mentioned in this documentation and the second name
is the file name on your system. On such systems for example the library
ProfilMISC
is called prmis
instead (i.e. the file name
is libprmis.a).
REAL Rand01 ()
REAL Random ()
UNSIGNED Rand ()
PROFIL_RAND_MAX
.
void Randomize ()
INT Binom (INT k, INT p)
INT Legendre (INT k, INT p)
To get a quick overview concerning the matrix properties, each of the test matrices has a four character type field in the first line of the header comments. The values used for each of the four characters are:
S
PD
M
The symbol * in any position denotes the absence of a certain property.
Except symmetry, any mentioned properties of the matrices below (e.g. being positive definite or a given range for the eigenvalues) are not guaranteed due to the finite precision of the floating point arithmetic used to calculate the matrices.
The following test matrices are provided:
MATRIX Lietzke (INT n)
MATRIX Pascal (INT n [, REAL k])
MATRIX Hilbert (INT n)
MATRIX ExactHilbert (INT n)
Only matrices which are exactly representable on the computer are returned. Beyond a machine-dependent matrix dimension, the subroutine aborts with an error message.
MATRIX InverseHilbert (INT n)
MATRIX Lotkin (INT n)
MATRIX Westlake (INT n)
MATRIX Newman (INT n)
MATRIX Frank (INT n)
MATRIX BoothroydMax (INT n)
MATRIX Hadamard (INT n)
MATRIX Binomial (INT n)
MATRIX Aergerter (INT n)
MATRIX Todd (INT n)
MATRIX Milnes (INT n [, VECTOR u])
MATRIX Combinatorial (INT n [, REAL x, REAL y])
MATRIX Cauchy (INT n [, REAL x, REAL y])
MATRIX Vandermonde (INT n, VECTOR x)
MATRIX Boothroyd (INT n)
MATRIX InverseBoothroyd (INT n)
MATRIX RandomM_Matrix (INT n)
MATRIX RandomPD (INT n)
MATRIX RandomSPD (INT n [, REAL lower bound, REAL upper bound])
MATRIX RandomTeoplitz (INT n)
MATRIX RandomMatrix (INT n [, INT m)
MATRIX RandomSymmetric (INT n)
MATRIX RandomPerSymmetric (INT n)
REAL
result without using derivatives are contained in the package.
The first method is the downhill simplex method
due to Nelder and Mead. It is a robust but rather slow method
in terms of required function evaluations. The implementation has been taken
from the Numerical Receipes in C with some slight modifications.
The file NelderMead.h contains the definitions needed to use this method.
The second method is due to Brent and defined in Brent.h. Both methods are available as easy-to-use and as expert version. The latter having more parameters allowing more freedom in customization.
REAL NelderMead (VECTOR & x, VECTOR & diam, REAL tol, REAL abstol, INT & ok, INT niter, REAL (*pfunc)(VECTOR &))
VOID NelderMead_Expert (MATRIX & p, VECTOR & y, REAL tol, REAL abstol, INT & ok, INT niter, PREAL parms, REAL (*pfunc)(VECTOR &))
parms[0]
parms[1]
parms[2]
Unchanged on output.
REAL BrentMinimize (VECTOR & p, VECTOR & diam, REAL rtol, REAL atol, REAL (*pfunc)(VECTOR &))
REAL BrentMinimize_Expert (VECTOR & p, REAL h, REAL rtol, REAL atol, REAL scbd, INT illc, INT ktm, REAL (*pfunc)(VECTOR &))
The creation of the new list data types is done by defining some macros with the names of the data types to be used and including the generic files mentioned above.
The following macros control the generation of the linear lists:
LIST_ELEMENT
LIST
LISTOBJECT
After defining these macros, the file LinearList.hgen can be included to generate all definitions of the new list data type. By including the file LinearList.Cgen (the file LinearList.hgen must have been included before), all necessary routines for the list implementation are generated.
VECTOR
).
The new list data type should be named VECTORLIST
. With VECTORLISTOBJECT
we want to denote the list node data type.
To create the definitions (headers) and implementations (subroutines) of the new list type, use the following code fragment:
#define LIST_ELEMENT VECTOR #define LIST VECTORLIST #define LISTOBJECT VECTORLISTOBJECT#include "LinearList.hgen"
#include "LinearList.Cgen"
#undef LISTOBJECT #undef LIST #undef LIST_ELEMENT
The #undef commands may be necessary, if more than one list type is defined.
After the generation step, you can define a new list li
of vectors by
VECTORLIST li;
The comparison function used for the sorted insert must have the following prototype
INT SortCompare (LIST_ELEMENT & e1, LIST_ELEMENT & e2);
where the name of the function can be chosen arbitrarily. The return value of the
comparison function must be 1, if the element given by e1 should precede
the element given by e2 in the list. Otherwise, a value of 0 must be returned.
To create the list considered in the above example as sorted list with the
comparison function called SortComp
, the list is defined as:
VECTORLIST li(SortComp);
The elements are inserted into this list by using the <<= operator (see below).
LIST_ELEMENT First (LIST & li)
LIST_ELEMENT Next (LIST & li)
LIST_ELEMENT Last (LIST & li)
LIST_ELEMENT Current (LIST & li)
VOID RemoveCurrent (LIST & li)
BOOL Finished (LIST & li)
TRUE
if the current element is undefined, i.e. if the end of the
list is reached. Otherwise, FALSE
is returned.
BOOL IsEmpty (LIST & li)
TRUE
if the list is empty. Otherwise, FALSE
is returned.
INT Length (LIST & li)
INT MaxLength (LIST & li)
ResetLength
or the definition of the list variable.
VOID ResetLength (LIST & li)
To append the element e at the end of a list li, use
li += e
To prepend the element e at the beginning of a list li, use
li *= e
The element e is inserted into the sorted list li by
li <<= e
and the first element of a list li can be removed by li--.
A complete list can be written to output by using the <<
operator, e.g.
cout << li;
REAL
or INTERVAL
result,
an automatic computation of the gradient and optionally the Hessian value can be
performed during the evaluation of the functional expression in parallel to the
computation of the function value.
If only the first derivative (the gradient) has to be computed, the necessary header files are (depending whether you have a real or interval valued function) Gradient.h or IntervalGradient.h.
If the Hessian is also needed, the header files are AutoDiff.h or IntervalAutoDiff.h.
With automatic differentiation, new data types which contain the current function value as well as the current derivative values, do exist. Their names depend on the header file used
GRADIENT
INTERVAL_GRADIENT
AUTODIFF
INTERVAL_AUTODIFF
In the following, we will use always AUTODIFF
as new type name.
To use automatic differentiation, you first have to define your
independent variable by creating a new variable of the type AUTODIFF
containing the values of your independent variable. After that, you
write your functional expression as you would do in the real case, using
variables of the type AUTODIFF
instead of REAL
or INTERVAL
for your intermediate results.
Last, you can assign your result (either the function value, the gradient
value, or the Hessian value).
Components of a variable of the type AUTODIFF
can be accessed by the
following functions:
REAL FunctionValue (AUTODIFF a)
INTERVAL
, instead.
VECTOR GradientValue (AUTODIFF a)
INTERVAL_VECTOR
, instead.
MATRIX HessianValue (AUTODIFF a)
INTERVAL_MATRIX
, instead.
In most cases, the above functions are not needed, because the conversion is performed automatically. For example:
VECTOR x(2), y(2);x(1) = 1.0; x(2) = 2.0;
GRADIENT xg(x); // x resp. xg is the independent variable y = 2.0 * xg(1) * xg(2); // automatic call of GradientValue(...)
Caution: This automatic conversion will fail if the expression does not depend on the independent variable. In this case, the compiler will generate an error message about incompatible types. Using an intermediate result of an automatic differentiation type or the explicit assignment of zero to the derivatives will be a work-around. This is not a bug of the automatic differentiation package but due to the limited capabilities of C++.
The file AutoDiffExample.C contains a simple example of how to use the automatic differentiation package. If you want to use other than the predefined set of functions with automatic differentiation, you have to modify the files AutoDiff.hgen and AutoDiff.Cgen. These files contain the generic parts for all automatic differentiation files.
The optional computation of the Hessian is controlled by two variables:
BOOL AUTODIFF::ComputeHessian
TRUE
, the evaluation of the Hessian is performed
for AUTODIFF
objects. Otherwise, the Hessian is not computed.
BOOL INTERVAL_AUTODIFF::ComputeHessian
TRUE
, the evaluation of the Hessian is performed
for INTERVAL_AUTODIFF
objects. Otherwise, the Hessian is not computed.
All sample programs can simply be build by appending _example to the
architecture of the dmake
command discussed in the installation
section. For example,
dmake xlC_examples
builds all libraries (if not already done) and all sample programs.
The adaptation is performed in 4 steps:
For the correct automatic compilation of BIAS, your system architecture must
be identified at compilation time. For this purpose, most compilers support
pre-defined macros which can be used for that purpose. For example, the
xlc
on RS/6000 architectures running under AIX defines the macro _AIX.
To identify your architecture, you have to modify the file Portab.h. The necessary insertions should be behind the comment starting with
Insert settings for new compilers / machines here!
The most important setting is whether you have a big or little endian architecture on your machine. Big endian architectures are e.g. Sparc, RS/6000. Little endian architectures are e.g. PCs with 80x86. As a help, the following C program may be used to identify your machine:
#includeunion { struct { char a; char b; } bytes; short word; } un;
main() { un.bytes.a = 1; un.bytes.b = 2; if (un.word == 258) printf ("big endian machine\n"); else if (un.word == 513) printf ("little endian machine\n"); else printf ("unknown machine\n"); }
Define the macro __HIGHBYTEFIRST__
for big endian and
__LOWBYTEFIRST__
for little endian machines.
Additionally, you have to define __32BIT__
or __16BIT__
if your
system word length is either 32 or 16 bit. Define __ANSI__
if your
compiler supports the ANSI keyword volatile
. If your compiler supports
the library function memmove
with overlapping memory fields and does
not define the macro __STDC__
, you should define it yourself.
As an example, consider the xlc compiler on RS/6000 architectures running AIX. For this case, the settings to be inserted into Portab.h should be:
#if defined (_AIX) #ifndef __ANSI__ #define __ANSI__ #endif /* __STDC__ predefined */ #define __32BIT__ #define __HIGHBYTEFIRST__ #endif
After that, you have to define the subroutines for switching the rounding mode. These subroutines should be called
BiasRoundUp() BiasRoundDown() BiasRoundNear()
for the different rounding mode settings. Then the assemblation of the rounding mode switching routines must be inserted into the Makefile of BIAS. For this purpose, you need the name of your compiler, the necessary command line flags and the name of the assembler file, where the rounding mode setting routines are contained. If you use C routines instead of assembler subroutines, you have to change in the Makefile in the line
$(ROUNDOBJ) : $(ROUND).s
the Extension .s into .c.
The BIAS Makefile is prepared for one new architecture/compiler to be simply added. Search for the line
.ELIF $(YOURCOMP)
and modify the following lines for your purposes, if you consider that the compiler name
is defined by the macro CC
, the command line flags by the macro CFLAGS
and the file with the new rounding mode subroutines is defined by ROUND
.
The compilation then can be started by
dmake yourcomp
or
dmake yourcomp_test
If you can use assembler inline sequences to switch the rounding mode, more
modifications which are beyond the scope of this report are necessary.
Take a look at the files BiasInt.h and Bias0.c. Look for the
keyword __I386__
together with __GNUC__
for a machine which only
uses inline assembler sequences. If you provide subroutines as well as inline
sequences, look for the keyword _AIX
or sparc
together with __GNUC__
as example.
If you have adapted the BIAS IEEE version to any non-supported architecture
and you think that your adaptation is of general interest, you may send it with all
modified and/or new files via email with the subject field PROFIL/BIAS
to the author. Please include a declaration that your adaptation may be distributed
for non-commercial use free of charge.