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: 23/09/2025
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: Most of the intrinsic functions (cos, sin, exp, ...) and operations (+, -, *, /, **) are possible to use with dnS_t.
Remarks: X, Y, Z, f, df, gradf are of dnS_t type and gradf is a table of dnS_t.
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 )-
=: from another dnS_t, a real(kind=Rkind), an integer (default kind) -
+,-,*,/: from one dnS_t and another dnS_t, a real(kind=Rkind), an integer (default kind) -
**: from one dnS_t (left argument) and a real(kind=Rkind), an integer (default kind) (right argument) -
>,>=,<,<=,==,/=, (.GT.,.GE.,.LT.,.LE.,.EQ.,.NEQ.) : between two dnS_t (it uses only the d0 components) -
sqrt,exp,log,log10,abs -
sin,asin,cos,acos,tan,atan -
sinh,asinh,cosh,acosh,tanh,atanh -
mod,modulo: only with the d0 components -
atan2: with two dnS_t arguments -
dot_product: from two vectors of dnS_t or on vector of dnS_t and another real vector -
sum: from a vector or a matrix of dnS_t -
transpose: from a matrix of dnS_t -
matmul: from two of dnS_t or on matrix of dnS_t and another real one -
cross_product: from two vectors (size=3) of dnS_t or on vector of dnS_t and another real vector -
dnMonomial(x,i): polynomials:x**i -
dnBox(x,i,[ReNorm]): sine or particle-in-a-box between$]0,\pi[$ . If the optional argumentReNorm=.TRUE.or ifReNormis not present, the function is renormalized. -
dnFourier(x,i,[ReNorm]): Fourier series (order:cos(0*x),sin(1*x),cos(1*x),sin(2*x),cos(2*x)...) between$]-\pi,\pi[$ . If the optional argumentReNorm=.TRUE.or ifReNormis not present, the function is renormalized. -
dnFourier2(x,i,[ReNorm]): Fourier series (order: ...,sin(2*x),sin(1*x),cos(0*x),cos(1*x), ,cos(2*x)...) between$]-\pi,\pi[$ . If the optional argumentReNorm=.TRUE.or ifReNormis not present, the function is renormalized. -
dnLegendre0(x,i,[ReNorm]): Legendre polynomials [-1,1]. If the optional argumentReNorm=.TRUE.or ifReNormis not present, the function is renormalized. -
dnLegendre(x,l,m,[ReNorm]): Associated Legendre polynomials ]-1,1[ (l >= 0and-l <= m <= l). If the optional argumentReNorm=.TRUE.or ifReNormis not present, the function is renormalized. -
dnJocobi(x,n,alpha,beta,[ReNorm]): Jacobi polynomials ]-1,1[ (n,alpha,betaare integers). If the optional argumentReNorm=.TRUE.or ifReNormis not present, the function is renormalized. -
dnHermite(x,l,[ReNorm]): Physicist Hermite polynomials between$]-\infty,+\infty[$ . If the optional argumentReNorm=.TRUE.or ifReNormis not present, the function is renormalized. -
dnExpHermite(x,l,[ReNorm]): Hermite functions or Hermite-Gaussian functions between$]-\infty,+\infty[$ . If the optional argumentReNorm=.TRUE.or ifReNormis not present, the function is renormalized. -
RSphericalHarmonics(th,phi,l,lm,ReNorm): Real Spherical Harmonics$Y_{l}^{lm}(th,phi)$ . It uses thednFourierorder. -
RSphericalHarmonics2(th,phi,l,lm,ReNorm): Real Spherical Harmonics$Y_{l}^{lm}(th,phi)$ . It uses thednFourier2order.
-
Subroutine
Write_dnS(S,nio,info,all_type,FOR_test,Rfmt,nderiv)writeS(dnS_t) in a file of unitnioand SubroutineWrite_dnS(S,string,info,all_type,FOR_test,Rfmt,nderiv)writeS(dnS_t) in a string of characters (string). For both subroutines,infois an optional string which is printed.all_typeorFOR_testare some optional flags when one is present and is .TRUE., all components of thednS_tis printed without format (different writing). When those flags are absent or set to .FALSE., some derivatives are printed (all or up tonderivif present) with the formate12.3orRfmtif present. -
Subroutine
set_dnS(S,d0,d1,d2,d3)to set the corresponding derivatives ofS(dnS_t).d0is real (scalar),d1is a vector of real,d2is a matrix of real andd3has a rank 3.d0,d1,d2andd3are optional.nderivis defined accordingly to the presence ofd0,d1,d2ord3. -
Subroutine
set_d0S(S,d0)to set the componentd0of ofS(dnS_t) without changingnderiv. -
Functions
get_d0,get_d1,get_d2andget_d3to get, respectively, the corresponding derivatives,dnS%d0,dnS%d1(:),dnS%d2(:,:)anddnS%d3(:,:,:). -
Subroutine
sub_get_dn: gets thed0,d1,d2ord3components according to their presence. -
Function
get_Flatten(S,i_der)to get allS(dnS_t) derivatives (dnS%d0,dnS%d1(:)...) in a single real vector. Ifideris present only theider-derivatives (all components) are returned (it is equivalent the reshape of after aget_d1,get_d2orget_d3). -
Functions,
get_nderivandget_nVar, get, respectively,nderivandnVar. -
Function,
TO_dnSReducedDer(S,list)returns adnS_tfromS(ofdnS_t) with a subset of derivatives defined by thelistof integer. -
Function,
FROM_dnSReducedDer(nVar,list,S)returns adnS_tfromS(ofdnS_t). The derivativeiofSare set inlist(i)of the result.nVaris the number of variables (derivatives) of the results. It must be larger or equal than the size oflist. -
Function
Deriv(S,ider)returnsdS(dnS_t) as theider-derivative ofS(dnS_t).nderivis reduced by 1. -
Function
grad(S)returns the gradient of ofS(dnS_t) as a vector ofdnS_t.nderivis reduced by 1. -
Function
get_Jacobian(Vec)returns the Jacobian matrix as a real matrix.Vecis a vector ofdnS_t. -
Function
dnf_OF_dnS(f,S)returnsfS(dnS_t) as the functionf(dnS_twithnVar=1i.e. 1D) ofS(dnS_t). -
Function
dnf_OF_dnS(f,S(:))returnsfS(dnS_t) as the functionf(dnS_twithnVar>=1) ofS(:)(dnS_t) withnVaroffequal the size ofS(:).
fpm buildmake allIt will make the library, the executable tests and example. You can change the compiler, Lapack flag, the OpenMP flag, the compiler optimization flag, the default integer, the real kind either in the makefile or when calling make:
make all FC=ifort OMP=0 OPT=0 LAPACK=0 INT=4 RKIND=real64
# 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
# RKIND=real64 or real32 or real128: change the real kindThe library, libAD_dnSVM_XXX_oppY_ompZ_lapackW_intV_realA.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)
- A is 32 or 64 or 128 (real32, real64, real128)
If needed, the .mod files are in the OBJ/obj_XXX_oppY_ompZ_lapackW_intV_realA directory.
Remark: the external library(ies) are stored in the Ext_Lib directory. However, it can be modified with the makefile variable ExtLibDIR. For instance, to compile with the external library directory stored in ../../LIB, use:
make lib OPT=0 ExtLibDIR=../../LIBIt has been tested with:
- gfortran (11, 12, 13, 14, 15 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
fpm test dnMator in the Tests directory, run the scripts
./run_test_dnS
./run_test_dnPolyor in the Tests directory (with several combinations of OMP, OPT ...), run the script
./run_tests.shWith fpm
fpm run Exa_dnSor in the main directory
./Exa_dnS.x