Pnl is a scientific library written in C and distributed under the Gnu Lesser General Public Licence (LGPL). This manual is divided into four parts.
Mathematical functions: complex numbers, special functions, standard financial functions for the Black & Scholes model.
Linear algebra : vectors, matrices (dense and sparse), hypermatrices, tridiagonal matrices, band matrices and the corresponding routines to manipulate them and solve linear systems.
Probabilistic functions: random number generators and cumulative distribution functions.
Deterministic toolbox : FFT, Laplace inversion, numerical integration, zero searching, multivariate polynomial regression, …
All header file names are prefixed by pnl_ and are surrounded by the preprocessor conditionals
#ifndef _PNL_MATRIX_H #define _PNL_MATRIX_H ... #endif /* _PNL_MATRIX_H
All the header files are protected by an extern "C" declaration for possible use with a C++ compiler. The header files must be include using
#include "pnl/pnl_xxx.h"
All function names are prefixed by pnl_ except those implementing complex number
arithmetic which are named following the C99 complex library but using a capitalised first
letter C.
For example, the addition of two complex numbers is performed by the function
Cadd.
Function containing _create in their names always return a pointer to an object created by one or several calls to dynamic allocation. Once these objects are not used, they must be freed by calling the same function but ending in _free. A function pnl_foo_create_yyy returns a PnlFoo * object (note the “*”) and a function pnl_foo_bar_create_yyy returns a PnlFooBar * object (note the “*”). These objects must be freed by calling respectively pnl_foo_free or pnl_foo_bar_free.
Functions ending in _clone take two arguments src and dest and modify dest to make it identical to src, ie. they have the same size and data. Note that no new object is allocated, dest must exist before calling this function.
Functions ending in _copy create a new object identical (ie. with the same size and content) as its argument but independent (ie. modifying one of them does not alter the other). Calling A = pnl_xxx_copy(B) is equivalent to first calling A = pnl_xxx_new() function and then pnl_xxx_clone(A, B).
Every object must implement a pnl_xxx_new function which returns a pointer to an empty object with all its elements properly set to 0. This means that the objects returned by the pnl_xxx_new functions can be used as output arguments for functions ending in _inplace for instance. They are suitable for being resized.
Functions containing _wrap_ in their names always return an object, not a pointer to an object, and do not make any use of dynamic allocation. The returned object must not be freed. For instance, a function pnl_foo_wrap_xxx returns an object PnlFoo and a function pnl_foo_bar_wrap_xxx returns an object PnlFooBar
PnlVectComplex *v1; PnlVectComplex v2; v1 = pnl_vect_complex_create_from_scalar (5, Complex(0., 1.)); v2 = pnl_vect_complex_wrap_subvect (v1, 1, 2); ... pnl_vect_complex_free (&v1);
The vector v1 is of size 5 and contains the pure imaginary number i. The vector v2 only provides a view to v1(1:1+2), which means that modifying v2 will also modify v1 and vice-versa because v1 shares part of its data with v2. Note that only v1 must be freed and not v2.
Functions ending in _init do not create any object but only perform some internal initialisation.
Hypermatrices, matrices and vectors are stored using a flat block of memory obtained by concatenating the matrix rows and C-style pointer-to-pointer arrays. Matrices are stored in row-major order, which means that the column index moves continuously. Note that this convention is not Blas & Lapack compliant since Fortran expects 2-dimensional arrays to be stored in a column-major order.
Type names always begin with Pnl, they do not contain underscores but instead we use
capital letters to separate units in type names.
Examples : PnlMat, PnlMatComplex.
Object and function names are intimately linked : an object PnlFoo is manipulated by functions starting in pnl_foo, an object PnlFooBar is manipulated by functions starting in pnl_foo_bar. In table 1, we summarise the types and their corresponding prefixes.
Pnl types | Pnl prefix |
PnlVect | pnl_vect |
PnlVectComplex | pnl_vect_complex |
PnlVectInt | pnl_vect_int |
PnlMat | pnl_mat |
PnlMatComplex | pnl_mat_complex |
PnlMatInt | pnl_mat_int |
PnlSpMat | pnl_sp_mat |
PnlSpMatComplex | pnl_sp_mat_complex |
PnlSpMatInt | pnl_sp_mat_int |
PnlHmat | pnl_hmat |
PnlHmatComplex | pnl_hmat_complex |
PnlHmatInt | pnl_hmat_int |
PnlTridiagMat | pnl_tridiag_mat |
PnlBandMat | pnl_band_mat |
PnlList | pnl_list |
PnlBasis | pnl_basis |
PnlCgSolver | pnl_cg_solver |
PnlBicgSolver | pnl_bicg_solver |
PnlGmresSolver | pnl_gmres_solver |
All macro names begin with PNL_ and are capitalised.
Differences between copy and clone methods. The copy methods take a single argument and return a pointer to an object of the same type which is an independent copy of its argument. Example:
PnlVect *v1, *v2; v1 = pnl_vect_create_from_scalar (5, 2.5); v2 = pnl_vect_copy (v1);
v1 and v2 are two vectors of size 5 with all their elements equal to 2.5. Note that v2 must not have been created by a call to pnl_vect_create_xxx because otherwise it will cause a memory leak. v1 and v2 are independent in the sense that a modification to one of them does not affect the other.
The clone methods take two arguments and fill the first one with the second one. Example:
PnlVect *v1, *v2; v1 = pnl_vect_create_from_scalar (5, 2.5); v2 = pnl_vect_new (); pnl_vect_clone (v2, v1);
v1 and v2 are two vectors of size 5 with all their elements equal to 2.5. Note that v2 must have been created by a call to pnl_vect_new because otherwise the function pnl_vect_clone will crash. v1 and v2 are independent in the sense that a modification to one of them does not modify the other.
All objects are measured using integers int and not size_t. Hence, iterations over vectors, matrices, …should use an index of type int.
In fonctions ending in inplace, the output parameter must be different from any of the input parameters.
In this section, we assume that the library is installed in the directory $HOME/pnl-xxx.
Once installed, the library can be found in the $HOME/pnl-xxx/lib directory and the header files in the $HOME/pnl-xxx/include directory.
The header files of the library are installed in a root pnl directory and should always be included with this pnl/ prefix. So, for instance to use random number generators you should include
#include <pnl/pnl_random.h>
Compiling and linking by hand. If gcc or llvm is used, you should pass the following options
-I$HOME/pnl-xxx/include for compiling
-L$HOME/pnl-xxx/lib -lpnl for linking
This does not work straight away on all OS especially if the library is not installed in a standard directory namely /usr/ or /usr/local/ for which you need a privileged writing access. On some systems, you may need to add to the linker flags the dependencies of the library, which can become very tedious. Therefore, we provide a second automatic mechanism which takes care of the dependencies on its own.
Compiling and linking using an automatic Makefile. This mechanism only works under Unix (it has been tested under various Linux distributions and Mac OS X).
First, you need to create a new directory wherever you want, put in all your code and create a Makefile as below
To define your target just add the executable name, say my-exec, to the BINS list and create an entry my_exec_SRC carrying the list of source files needed to create your executable. Note that if dashes ’-’ may appear in an executable name, the name of the associated variable holding the list of source files is obtained by replacing dashes with underscores ’_’ and adding the _SRC suffix.
Assume you want to create two binaries : my-exec based on mixed C and C++ code (file1.c and file2.cpp) and mybinary based on poo1.cxx and poo2.cpp. You can use the following Makefile.
## Flags passed to the linker LDFLAGS= ## Flags passed to the compiler CFLAGS= ## list of executables to create BINS=my-exec mybinary my_exec_SRC=file1.c file2.cpp # optional flags for compiling and linking my_exec_CFLAGS= my_exec_CXXFLAGS= my_exec_LDFLAGS= mybinary_SRC=poo1.cxx poo2.cpp # optional flags for compiling and linking mybinary_CFLAGS= mybinary_CXXFLAGS= mybinary_LDFLAGS= ## This line must be the last one include full_path_to_pnl_build/CMakeuser.incl
Let us comment a little the different variables
CFLAGS: global flags used for creating objects based on C code
CXXFLAGS: global flags used for creating objects based on C++ code
LDFLAGS: gobal linker flags.
binaryname_CFLAGS: flags used when creating the objects based on C code and required by binaryname
binaryname_CXXFLAGS: flags used when creating the objects based on C++ code and required by binaryname
binaryname_LDFLAGS: flags used when linking objects for creating binaryname
An example of such a Makefile can be found in pnl-xxx/perso.
Warning: if a file appears in the source list of several binairies, the flags used to compile this file are determined by the ones of the first binary involving this file. In the following example main.cpp will always be compiled with the flag -O3 even for generating bin2
BINS=bin1 bin2 bin1_SRC=main.cpp poo1.c my_exec_CXXFLAGS=-O3 bin2_SRC=main.cpp poo2.c mybinary_CXXFLAGS=-g -O0 ## This line must be the last one include full_path_to_pnl_build/CMakeuser.incl
Compiling and linking using CMake. If you already use CMake for your new project, just add the following to your toplevel CMakeLists.txt
find_package(Pnl REQUIRED) set(LIBS ${LIBS} ${PNL_LIBRARIES}) include_directories(${PNL_INCLUDE_DIRS}) # Deactivate PNL debugging stuff on Release builds if(${CMAKE_BUILD_TYPE} STREQUAL "Release") add_definitions(-DPNL_RANGE_CHECK_OFF) endif()
Then, call cmake with the following extra flag
-DCMAKE_PREFIX_PATH=path/to/build-dir
or add the variable CMAKE_BUILD_TYPE to the GUI.
Just in case, we give an example of a complete although elementary CMakeLists.txt
If it is supported by your compiler, getter and setter functions are declared as inline functions. This is automatically detected when running CMake. By default, setter and getter functions check that the required access is valid, basically it boils down to checking whether the index of the access is within an acceptable range. These extra tests can become very expensive when getter and setter functions are intensively called.
Thus, it is possible to alter this default behaviour by defining the macro PNL_RANGE_CHECK_OFF. This macro is automatically defined when the library is compiled in Release mode, ie. with -DCMAKE_BUILD_TYPE=Release passed to CMake.