AD_dnSVM is a Fortran Automatic Differentiation library using forward mode for scalars, vectors and matrices. Its features are:
- no limit in terms of the number of independent variables (this number is defined at runtime)
- up to third derivatives
It is similar to Auto_Deriv fortran module: http://www.autodiff.org/?module=Tools&tool=AUTO_DERIV
date: 20/08/2024
Copyright 2021 David Lauvergnat [1]
Originally, it has been developed for Quantum Model Lib (QML): https://github.com/lauvergn/QuantumModelLib
This library enables to get the derivatives (up to third order) of a function of several variables (type(dnS_t)). See the APP/Example_dnS.f90 file.
Variables need to be initialized. For instance the variables, X, Y, Z
USE ADdnSVM_m
IMPLICIT NONE
!....
type(dnS_t) :: X,Y,Z
type(dnS_t) :: f
X = Variable( Val=HALF, nVar=3, iVar=1, nderiv=1 )
Y = Variable( Val=ONE, nVar=3, iVar=2, nderiv=1 )
Z = Variable( Val=TWO, nVar=3, iVar=3, nderiv=1 )
X, Y and Z are, respectively, the first, the second and the third variables (iVar) among 3 (nVar). Their derivatives are defined up to the 1st order (nderiv=1). The variable values are set with Val.
Remark: for Y (the second variable), its value is ONE, the three 1st derivatives are [ZERO,ONE,ZERO] and all 2d derivatives are null.
Operations with X, Y and Z: Remarks: X, Y, Z, f, df, gradf are of dnS_t type and gradf is a table.
f = ONE + cos(X)**2 + sin(Y*Z)
f value : 1. + cos(0.5)**2 + sin(2.) = 2.679448580
f gradient: [-2. * sin(0.5)*cos(0.5), 2. * cos(2.), cos(2.)] =
[-0.8414709848, -0.8322936731, -0.4161468365]
CALL Write_dnS(f,info='f=1.0 + cos(X)**2 + sin(Y*Z), value: 2.67945')
gives:
f=1.0 + cos(X)**2 + sin(Y*Z), value: 2.67945
0 derivative +0.268E+01
1st derivative 1 -0.841E+00
1st derivative 2 -0.832E+00
1st derivative 3 -0.416E+00
One can define a new function from the first derivative with respect to a variable (defined by the ider index) of a function:
df = deriv(f,ider=1)
Here, df constains df/dX. WARNING: the nderiv of df is reduced by one with respect to nderiv of f.
The gradient is also possible, the result is in a table of dnS. It has the same limitation with nderiv (reduction of nderiv)/
gradf = grad(f)
Instead of several variable initializations, one can initialize a vector:
USE ADdnSVM_m
IMPLICIT NONE
!....
type(dnS_t), allocatable :: VecOld(:),VecNew(:)
r = TWO
th = Pi/3
VecOld = Variable([r,th], nderiv=2 ) ! VecOld(1) : r, VecOld(2) : th
VecNew = VecOld(1)*[cos(VecOld(2)),sin(VecOld(2))] ! [r*cos(th), r*sin(th)]
JacNewOld = get_Jacobian( VecNew ) ! JacNewOld(inew,iold)=[ dQinew/dQiold ]
The vector intialization has optional arguments:
nVar
: the total number of the independent variables. It must be larger or equal to the vector size.iVar(:)
: a table with the indices of the independent variables. It size must be equal to the vector size.
VecOld = Variable([r,th], nVar=3, iVar=[1,2], nderiv=2 )
-
Subroutine
set_dnS(S,d0,d1,d2,d3)
to set the corresponding derivatives ofS
(dnS_t
).d0
is real (scalar),d1
is a vector of real,d2
is a matrix of real andd3
has a rank 3.d0
,d1
,d2
andd3
are optional.nderiv
is defined accordingly to the presence ofd0
,d1
,d2
ord3
. -
Functions
get_d0
,get_d1
,get_d2
andget_d3
to get, respectively, the corresponding derivatives,dnS%d0
,dnS%d1(:)
,dnS%d2(:,:)
anddnS%d3(:,:,:)
. -
Function
get_Flatten
to get alldnS
derivatives (dnS%d0
,dnS%d1(:)
...) in a single real vector.
fpm build
make all
It will make the library, the executable tests and example. You can change the compiler, Lapack flag, the OpenMP flag, the compiler optimization flag and the default integer either in the makefile or when calling make:
make all FC=ifort OMP=0 OPT=0 LAPACK=0 INT=4
# FC=ifort to change the compiller to ifort
# OMP=0/1 to turn off/on the OpenMP fortran flag.
# OPT=0/1 to turn off/on the fortran optimization.
# LAPACK=0/1 to turn off/on the lapack use
# INT=4/8 to change the default integer
The library, libAD_dnSVM_XXX_oppY_ompZ_lapackW_intV.a is created in the main directory and the libAD_dnSVM.a library is linked to it. Remarks :
- XXX is the compiller (gfortran, ifort ...)
- Y is 0 or 1 (opt0 / opt1: compiler optimization)
- Z is 0 or 1 (omp0 / omp1: whitout/with OpenMP)
- W is 0 or 1 (lapack0 / lapack1: whitout/with lapack)
- V is 4 or 8 (int4 / int8)
If needed, the .mod files are in the OBJ/obj_XXX_oppY_ompZ_lapackW_intV directory.
It has been tested with:
- gfortran (14.1.0_2 on macos, 12 on linux)
- ifx/ifort (2023 on linux)
- nagor (7.1 on linux)
With fpm
fpm test dnPoly
fpm test dnS
fpm test dnVec
or in the Tests directory, run the scripts
./run_test_dnS
./run_test_dnPoly
or in the Tests directory (with several combinations of OMP, OPT ...), run the script
./run_tests
With fpm
fpm run Exa_dnS
or in the main directory
./Exa_dnS.x